Skip to content

Commit 4c3bcbb

Browse files
authored
Merge pull request #241 from pamapa/attachment-annotations
support file attachment annotations
2 parents b235ea1 + bd61b4b commit 4c3bcbb

File tree

3 files changed

+381
-0
lines changed

3 files changed

+381
-0
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
namespace PdfSharpCore.Pdf.Advanced
2+
{
3+
/// <summary>
4+
/// Represent a file stream embedded in the PDF document
5+
/// </summary>
6+
public class PdfEmbeddedFile : PdfDictionary
7+
{
8+
private readonly PdfDictionary paramsDictionary;
9+
10+
public PdfEmbeddedFile(PdfDocument document)
11+
: base(document)
12+
{
13+
this.paramsDictionary = new PdfDictionary();
14+
15+
Elements.SetName(Keys.Type, "/EmbeddedFile");
16+
Elements.SetObject(Keys.Params, paramsDictionary);
17+
}
18+
19+
public PdfEmbeddedFile(PdfDocument document, byte[] bytes, string checksum = null)
20+
: this(document)
21+
{
22+
this.CreateStreamAndSetProperties(bytes, checksum);
23+
}
24+
25+
public void CreateStreamAndSetProperties(byte[] bytes, string checksum = null)
26+
{
27+
this.CreateStream(bytes);
28+
29+
this.paramsDictionary.Elements.SetInteger(Keys.Size, bytes.Length);
30+
31+
if (string.IsNullOrEmpty(checksum))
32+
this.paramsDictionary.Elements.Remove(Keys.CheckSum);
33+
else
34+
this.paramsDictionary.Elements.SetString(Keys.CheckSum, checksum);
35+
}
36+
37+
public string MimeType
38+
{
39+
get { return Elements.GetName(Keys.Subtype); }
40+
set { Elements.SetName(Keys.Subtype, value); }
41+
}
42+
43+
// TODO : Add properties for the subdictionnary Params and the subsubdictionnary Mac
44+
45+
/// <summary>
46+
/// Predefined keys of this embedded file.
47+
/// </summary>
48+
public class Keys : PdfDictionary.PdfStream.Keys
49+
{
50+
/// <summary>
51+
/// (Optional) The type of PDF object that this dictionary describes; if present,
52+
/// must be EmbeddedFile for an embedded file stream.
53+
/// </summary>
54+
[KeyInfo(KeyType.Name | KeyType.Optional, FixedValue = "EmbeddedFile")]
55+
public const string Type = "/Type";
56+
57+
/// <summary>
58+
/// (Optional) The subtype of the embedded file. The value of this entry must be a
59+
/// first-class name, as defined in Appendix E. Names without a registered prefix
60+
/// must conform to the MIME media type names defined in Internet RFC 2046,
61+
/// Multipurpose Internet Mail Extensions (MIME), Part Two: Media Types(see the
62+
/// Bibliography), with the provision that characters not allowed in names must
63+
/// use the 2-character hexadecimal code format described in Section 3.2.4,
64+
/// “Name Objects.”
65+
/// </summary>
66+
[KeyInfo(KeyType.Name | KeyType.Optional)]
67+
public const string Subtype = "/Subtype";
68+
69+
/// <summary>
70+
/// (Optional) An embedded file parameter dictionary containing additional,
71+
/// file-specific information (see Table 3.43).
72+
/// </summary>
73+
[KeyInfo(KeyType.Dictionary | KeyType.Optional)]
74+
public const string Params = "/Params";
75+
76+
/// <summary>
77+
/// (Optional) The size of the embedded file, in bytes.
78+
/// </summary>
79+
[KeyInfo(KeyType.Integer | KeyType.Optional)]
80+
public const string Size = "/Size";
81+
82+
/// <summary>
83+
/// (Optional) The date and time when the embedded file was created.
84+
/// </summary>
85+
[KeyInfo(KeyType.Date | KeyType.Optional)]
86+
public const string CreationDate = "/CreationDate";
87+
88+
/// <summary>
89+
/// (Optional) The date and time when the embedded file was last modified.
90+
/// </summary>
91+
[KeyInfo(KeyType.Date | KeyType.Optional)]
92+
public const string ModDate = "/ModDate";
93+
94+
/// <summary>
95+
/// (Optional) A subdictionary containing additional information specific to Mac OS files (see Table 3.44).
96+
/// </summary>
97+
[KeyInfo(KeyType.Dictionary | KeyType.Optional)]
98+
public const string Mac = "/Mac";
99+
100+
/// <summary>
101+
/// (Optional) A 16-byte string that is the checksum of the bytes of the uncompressed
102+
/// embedded file. The checksum is calculated by applying the standard MD5 message-digest
103+
/// algorithm (described in Internet RFC 1321, The MD5 Message-Digest Algorithm; see the
104+
/// Bibliography) to the bytes of the embedded file stream.
105+
/// </summary>
106+
[KeyInfo(KeyType.Dictionary | KeyType.Optional)]
107+
public const string CheckSum = "/CheckSum";
108+
109+
/// <summary>
110+
/// Gets the KeysMeta for these keys.
111+
/// </summary>
112+
internal static DictionaryMeta Meta
113+
{
114+
get
115+
{
116+
if (Keys.meta == null)
117+
Keys.meta = CreateMeta(typeof(Keys));
118+
return Keys.meta;
119+
}
120+
}
121+
static DictionaryMeta meta;
122+
}
123+
124+
/// <summary>
125+
/// Gets the KeysMeta of this dictionary type.
126+
/// </summary>
127+
internal override DictionaryMeta Meta
128+
{
129+
get { return Keys.Meta; }
130+
}
131+
}
132+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
namespace PdfSharpCore.Pdf.Advanced
2+
{
3+
/// <summary>
4+
/// Represent a file stream embedded in the PDF document
5+
/// </summary>
6+
public class PdfFileSpecification : PdfDictionary
7+
{
8+
private readonly PdfDictionary embeddedFileDictionary;
9+
10+
public PdfFileSpecification(PdfDocument document)
11+
: base(document)
12+
{
13+
this.embeddedFileDictionary = new PdfDictionary();
14+
15+
Elements.SetName(Keys.Type, "/Filespec");
16+
Elements.SetObject(Keys.EF, embeddedFileDictionary);
17+
}
18+
19+
public PdfFileSpecification(PdfDocument document, string fileName, PdfEmbeddedFile embeddedFile)
20+
: this(document)
21+
{
22+
this.FileName = fileName;
23+
this.EmbeddedFile = embeddedFile;
24+
}
25+
26+
public string FileName
27+
{
28+
get { return Elements.GetString(Keys.F); }
29+
set { Elements.SetString(Keys.F, value); }
30+
}
31+
32+
public PdfEmbeddedFile EmbeddedFile
33+
{
34+
get
35+
{
36+
var reference = embeddedFileDictionary.Elements.GetReference(Keys.F);
37+
38+
return reference?.Value as PdfEmbeddedFile;
39+
}
40+
set
41+
{
42+
if (value == null)
43+
{
44+
embeddedFileDictionary.Elements.Remove(Keys.F);
45+
}
46+
else
47+
{
48+
if (!value.IsIndirect)
49+
Owner._irefTable.Add(value);
50+
51+
embeddedFileDictionary.Elements.SetReference(Keys.F, value);
52+
}
53+
}
54+
}
55+
56+
/// <summary>
57+
/// Predefined keys of this embedded file.
58+
/// </summary>
59+
public class Keys : KeysBase
60+
{
61+
/// <summary>
62+
/// (Required if an EF or RF entry is present; recommended always)
63+
/// The type of PDF object that this dictionary describes; must be Filespec
64+
/// for a file specification dictionary (see implementation note 45 in Appendix H).
65+
/// </summary>
66+
[KeyInfo(KeyType.Name | KeyType.Optional, FixedValue = "Filespec")]
67+
public const string Type = "/Type";
68+
69+
/// <summary>
70+
/// (Required if the DOS, Mac, and Unix entries are all absent; amended with the UF
71+
/// entry for PDF 1.7) A file specification string of the form described in Section
72+
/// 3.10.1, “File Specification Strings,” or (if the file system is URL) a uniform
73+
/// resource locator, as described in Section 3.10.4, “URL Specifications.”
74+
///
75+
/// Note: It is recommended that the UF entry be used in addition to the F entry.
76+
/// The UF entry provides cross-platform and cross-language compatibility and the F
77+
/// entry provides backwards compatibility
78+
/// </summary>
79+
[KeyInfo(KeyType.Dictionary | KeyType.Optional)]
80+
public const string F = "/F";
81+
82+
/// <summary>
83+
/// (Required if RF is present; PDF 1.3; amended to include the UF key in PDF 1.7)
84+
/// A dictionary containing a subset of the keys F, UF, DOS, Mac, and Unix,
85+
/// corresponding to the entries by those names in the file specification dictionary.
86+
/// The value of each such key is an embedded file stream (see Section 3.10.3,
87+
/// “Embedded File Streams”) containing the corresponding file. If this entry is
88+
/// present, the Type entry is required and the file specification dictionary must
89+
/// be indirectly referenced. (See implementation note 46in Appendix H.)
90+
///
91+
/// Note: It is recommended that the F and UF entries be used in place of the DOS,
92+
/// Mac, or Unix entries.
93+
/// </summary>
94+
[KeyInfo(KeyType.Dictionary | KeyType.Optional)]
95+
public const string EF = "/EF";
96+
97+
/// <summary>
98+
/// Gets the KeysMeta for these keys.
99+
/// </summary>
100+
internal static DictionaryMeta Meta
101+
{
102+
get
103+
{
104+
if (Keys.meta == null)
105+
Keys.meta = CreateMeta(typeof(Keys));
106+
return Keys.meta;
107+
}
108+
}
109+
static DictionaryMeta meta;
110+
}
111+
112+
/// <summary>
113+
/// Gets the KeysMeta of this dictionary type.
114+
/// </summary>
115+
internal override DictionaryMeta Meta
116+
{
117+
get { return Keys.Meta; }
118+
}
119+
}
120+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
using PdfSharpCore.Pdf.Advanced;
2+
using System;
3+
4+
namespace PdfSharpCore.Pdf.Annotations
5+
{
6+
/// <summary>
7+
/// Represent a file that is attached to the PDF
8+
/// </summary>
9+
public class PdfFileAttachmentAnnotation : PdfAnnotation
10+
{
11+
/// <summary>
12+
/// Name of icons used in displaying the annotation.
13+
/// </summary>
14+
public enum IconType
15+
{
16+
Graph,
17+
PushPin,
18+
Paperclip,
19+
Tag
20+
}
21+
22+
/// <summary>
23+
/// Initializes a new instance of the <see cref="PdfFileAttachmentAnnotation"/> class.
24+
/// </summary>
25+
public PdfFileAttachmentAnnotation()
26+
{
27+
Elements.SetName(Keys.Subtype, "/FileAttachment");
28+
}
29+
30+
/// <summary>
31+
/// Initializes a new instance of the <see cref="PdfFileAttachmentAnnotation"/> class.
32+
/// </summary>
33+
public PdfFileAttachmentAnnotation(PdfDocument document)
34+
: base(document)
35+
{
36+
Elements.SetName(Keys.Subtype, "/FileAttachment");
37+
Flags = PdfAnnotationFlags.Locked;
38+
}
39+
40+
public IconType Icon
41+
{
42+
get
43+
{
44+
var iconName = Elements.GetName(Keys.Name);
45+
46+
if (iconName == null)
47+
return IconType.PushPin;
48+
49+
return (IconType)(Enum.Parse(typeof(IconType), iconName));
50+
}
51+
set { Elements.SetName(Keys.Name, value.ToString()); }
52+
}
53+
54+
public PdfFileSpecification File
55+
{
56+
get
57+
{
58+
var reference = Elements.GetReference(Keys.FS);
59+
60+
return reference?.Value as PdfFileSpecification;
61+
}
62+
set
63+
{
64+
if (value == null)
65+
{
66+
Elements.Remove(Keys.FS);
67+
}
68+
else
69+
{
70+
if (!value.IsIndirect)
71+
Owner._irefTable.Add(value);
72+
73+
Elements.SetReference(Keys.FS, value);
74+
}
75+
}
76+
}
77+
78+
/// <summary>
79+
/// Predefined keys of this dictionary.
80+
/// </summary>
81+
internal new class Keys : PdfAnnotation.Keys
82+
{
83+
/// <summary>
84+
/// (Required) The file associated with this annotation.
85+
/// </summary>
86+
[KeyInfo(KeyType.Dictionary | KeyType.Required)]
87+
public const string FS = "/FS";
88+
89+
/// <summary>
90+
/// (Optional) The name of an icon to be used in displaying the annotation.
91+
/// Viewer applications should provide predefined icon appearances for at least
92+
/// the following standard names:
93+
///
94+
/// Graph
95+
/// PushPin
96+
/// Paperclip
97+
/// Tag
98+
///
99+
/// Additional names may be supported as well. Default value: PushPin.
100+
/// Note: The annotation dictionary’s AP entry, if present, takes precedence over
101+
/// the Name entry; see Table 8.15 on page 606 and Section 8.4.4, “Appearance Streams.”
102+
/// </summary>
103+
[KeyInfo(KeyType.Name | KeyType.Optional)]
104+
public const string Name = "/Name";
105+
106+
/// <summary>
107+
/// Gets the KeysMeta for these keys.
108+
/// </summary>
109+
public static DictionaryMeta Meta
110+
{
111+
get
112+
{
113+
if (Keys.meta == null)
114+
Keys.meta = CreateMeta(typeof(Keys));
115+
return Keys.meta;
116+
}
117+
}
118+
static DictionaryMeta meta;
119+
}
120+
121+
/// <summary>
122+
/// Gets the KeysMeta of this dictionary type.
123+
/// </summary>
124+
internal override DictionaryMeta Meta
125+
{
126+
get { return Keys.Meta; }
127+
}
128+
}
129+
}

0 commit comments

Comments
 (0)