Committed by
GitHub
PDF/A 3b (#1750)
* PDF/A 3b * PDF/A 3b * PDF/A, reorganized into multiple files * PDF/A, make all annotations printable as a default (for pdf/a) * PDF/A, merging /Names when attaching files * PDF/A, extended facturx-rdf * Update pdf/lib/src/pdf/obj/pdfa/README.md Co-authored-by: Colin Ihlenfeldt <clnmaier@gmail.com> --------- Co-authored-by: David PHAM-VAN <1387855+DavBfr@users.noreply.github.com> Co-authored-by: Colin Ihlenfeldt <clnmaier@gmail.com>
Showing
11 changed files
with
439 additions
and
7 deletions
@@ -37,6 +37,10 @@ export 'src/pdf/obj/outline.dart'; | @@ -37,6 +37,10 @@ export 'src/pdf/obj/outline.dart'; | ||
37 | export 'src/pdf/obj/page.dart'; | 37 | export 'src/pdf/obj/page.dart'; |
38 | export 'src/pdf/obj/page_label.dart'; | 38 | export 'src/pdf/obj/page_label.dart'; |
39 | export 'src/pdf/obj/pattern.dart'; | 39 | export 'src/pdf/obj/pattern.dart'; |
40 | +export 'src/pdf/obj/pdfa/pdfa_attached_files.dart'; | ||
41 | +export 'src/pdf/obj/pdfa/pdfa_color_profile.dart'; | ||
42 | +export 'src/pdf/obj/pdfa/pdfa_facturx_rdf.dart'; | ||
43 | +export 'src/pdf/obj/pdfa/pdfa_rdf.dart'; | ||
40 | export 'src/pdf/obj/shading.dart'; | 44 | export 'src/pdf/obj/shading.dart'; |
41 | export 'src/pdf/obj/signature.dart'; | 45 | export 'src/pdf/obj/signature.dart'; |
42 | export 'src/pdf/obj/smask.dart'; | 46 | export 'src/pdf/obj/smask.dart'; |
@@ -173,12 +173,16 @@ abstract class PdfAnnotBase { | @@ -173,12 +173,16 @@ abstract class PdfAnnotBase { | ||
173 | this.border, | 173 | this.border, |
174 | this.content, | 174 | this.content, |
175 | this.name, | 175 | this.name, |
176 | - this.flags, | 176 | + Set<PdfAnnotFlags>? flags, |
177 | this.date, | 177 | this.date, |
178 | this.color, | 178 | this.color, |
179 | this.subject, | 179 | this.subject, |
180 | this.author, | 180 | this.author, |
181 | - }); | 181 | + }) { |
182 | + this.flags = flags ?? { | ||
183 | + PdfAnnotFlags.print, | ||
184 | + }; | ||
185 | + } | ||
182 | 186 | ||
183 | /// The subtype of the outline, ie text, note, etc | 187 | /// The subtype of the outline, ie text, note, etc |
184 | final String subtype; | 188 | final String subtype; |
@@ -201,7 +205,7 @@ abstract class PdfAnnotBase { | @@ -201,7 +205,7 @@ abstract class PdfAnnotBase { | ||
201 | final String? subject; | 205 | final String? subject; |
202 | 206 | ||
203 | /// Flags specifying various characteristics of the annotation | 207 | /// Flags specifying various characteristics of the annotation |
204 | - final Set<PdfAnnotFlags>? flags; | 208 | + late final Set<PdfAnnotFlags> flags; |
205 | 209 | ||
206 | /// Last modification date | 210 | /// Last modification date |
207 | final DateTime? date; | 211 | final DateTime? date; |
@@ -214,11 +218,11 @@ abstract class PdfAnnotBase { | @@ -214,11 +218,11 @@ abstract class PdfAnnotBase { | ||
214 | PdfName? _as; | 218 | PdfName? _as; |
215 | 219 | ||
216 | int get flagValue { | 220 | int get flagValue { |
217 | - if (flags == null || flags!.isEmpty) { | 221 | + if (flags.isEmpty) { |
218 | return 0; | 222 | return 0; |
219 | } | 223 | } |
220 | 224 | ||
221 | - return flags! | 225 | + return flags |
222 | .map<int>((PdfAnnotFlags e) => 1 << e.index) | 226 | .map<int>((PdfAnnotFlags e) => 1 << e.index) |
223 | .reduce((int a, int b) => a | b); | 227 | .reduce((int a, int b) => a | b); |
224 | } | 228 | } |
@@ -296,7 +300,7 @@ abstract class PdfAnnotBase { | @@ -296,7 +300,7 @@ abstract class PdfAnnotBase { | ||
296 | params['/NM'] = PdfString.fromString(name!); | 300 | params['/NM'] = PdfString.fromString(name!); |
297 | } | 301 | } |
298 | 302 | ||
299 | - if (flags != null && flags!.isNotEmpty) { | 303 | + if (flags.isNotEmpty) { |
300 | params['/F'] = PdfNum(flagValue); | 304 | params['/F'] = PdfNum(flagValue); |
301 | } | 305 | } |
302 | 306 |
@@ -26,6 +26,8 @@ import 'object.dart'; | @@ -26,6 +26,8 @@ import 'object.dart'; | ||
26 | import 'outline.dart'; | 26 | import 'outline.dart'; |
27 | import 'page_label.dart'; | 27 | import 'page_label.dart'; |
28 | import 'page_list.dart'; | 28 | import 'page_list.dart'; |
29 | +import 'pdfa/pdfa_attached_files.dart'; | ||
30 | +import 'pdfa/pdfa_color_profile.dart'; | ||
29 | 31 | ||
30 | /// Pdf Catalog object | 32 | /// Pdf Catalog object |
31 | class PdfCatalog extends PdfObject<PdfDict> { | 33 | class PdfCatalog extends PdfObject<PdfDict> { |
@@ -54,6 +56,12 @@ class PdfCatalog extends PdfObject<PdfDict> { | @@ -54,6 +56,12 @@ class PdfCatalog extends PdfObject<PdfDict> { | ||
54 | /// The document metadata | 56 | /// The document metadata |
55 | PdfMetadata? metadata; | 57 | PdfMetadata? metadata; |
56 | 58 | ||
59 | + /// Colorprofile output intent (Pdf/A) | ||
60 | + PdfaColorProfile? colorProfile; | ||
61 | + | ||
62 | + /// Attached files (Pdf/A 3b) | ||
63 | + PdfaAttachedFiles? attached; | ||
64 | + | ||
57 | /// The initial page mode | 65 | /// The initial page mode |
58 | final PdfPageMode? pageMode; | 66 | final PdfPageMode? pageMode; |
59 | 67 | ||
@@ -89,6 +97,13 @@ class PdfCatalog extends PdfObject<PdfDict> { | @@ -89,6 +97,13 @@ class PdfCatalog extends PdfObject<PdfDict> { | ||
89 | params['/Metadata'] = metadata!.ref(); | 97 | params['/Metadata'] = metadata!.ref(); |
90 | } | 98 | } |
91 | 99 | ||
100 | + if (attached != null && attached!.isNotEmpty) { | ||
101 | + //params['/Names'] = attached!.catalogNames(); | ||
102 | + names ??= PdfNames(pdfDocument); | ||
103 | + names!.params.merge(attached!.catalogNames()); | ||
104 | + params['/AF'] = attached!.catalogAF(); | ||
105 | + } | ||
106 | + | ||
92 | // the Names object | 107 | // the Names object |
93 | if (names != null) { | 108 | if (names != null) { |
94 | params['/Names'] = names!.ref(); | 109 | params['/Names'] = names!.ref(); |
@@ -158,5 +173,9 @@ class PdfCatalog extends PdfObject<PdfDict> { | @@ -158,5 +173,9 @@ class PdfCatalog extends PdfObject<PdfDict> { | ||
158 | {'/Font': fontRefs}); | 173 | {'/Font': fontRefs}); |
159 | } | 174 | } |
160 | } | 175 | } |
176 | + | ||
177 | + if (colorProfile != null) { | ||
178 | + params['/OutputIntents'] = colorProfile!.outputIntents(); | ||
179 | + } | ||
161 | } | 180 | } |
162 | } | 181 | } |
@@ -45,7 +45,8 @@ class PdfMetadata extends PdfObject<PdfDictStream> { | @@ -45,7 +45,8 @@ class PdfMetadata extends PdfObject<PdfDictStream> { | ||
45 | @override | 45 | @override |
46 | void prepare() { | 46 | void prepare() { |
47 | super.prepare(); | 47 | super.prepare(); |
48 | - params['/SubType'] = const PdfName('/XML'); | 48 | + params['/Type'] = const PdfName('/Metadata'); |
49 | + params['/Subtype'] = const PdfName('/XML'); | ||
49 | params.data = Uint8List.fromList(utf8.encode(metadata.toString())); | 50 | params.data = Uint8List.fromList(utf8.encode(metadata.toString())); |
50 | } | 51 | } |
51 | } | 52 | } |
pdf/lib/src/pdf/obj/pdfa/README.md
0 → 100644
1 | +Here are some classes to help you creating PDF/A compliant PDFs | ||
2 | +plus embedding Facturx invoices. | ||
3 | + | ||
4 | +### Rules | ||
5 | + | ||
6 | +1. Your PDF must only use embedded Fonts, | ||
7 | +2. For now you cannot use any Annotations in your PDF | ||
8 | +3. You must include a special Meta-XML, use below "PdfaRdf" and put the reuslting XML document into your documents metadata | ||
9 | +4. You must include a Colorprofile, use the below "PdfaColorProfile" and embed the contents of "sRGB2014.icc" | ||
10 | +5. Optionally attach an InvoiceXML using "PdfaFacturxRdf" and "PdfaAttachedFiles" | ||
11 | + | ||
12 | +### Example | ||
13 | + | ||
14 | +``` | ||
15 | +pw.Document pdf = pw.Document( | ||
16 | + ... | ||
17 | + metadata: PdfaRdf( | ||
18 | + ... | ||
19 | + invoiceRdf: PdfaFacturxRdf().create() | ||
20 | + ).create(), | ||
21 | +); | ||
22 | + | ||
23 | +PdfaColorProfile( | ||
24 | + pdf.document, | ||
25 | + File('sRGB2014.icc').readAsBytesSync(), | ||
26 | +); | ||
27 | + | ||
28 | +PdfaAttachedFiles( | ||
29 | + pdf.document, | ||
30 | + { | ||
31 | + 'factur-x.xml': myInvoiceXmlDocument, | ||
32 | + }, | ||
33 | +); | ||
34 | +``` | ||
35 | + | ||
36 | +### Validating | ||
37 | + | ||
38 | +https://demo.verapdf.org | ||
39 | +https://avepdf.com/pdfa-validation | ||
40 | +https://www.mustangproject.org |
1 | +import 'dart:convert'; | ||
2 | +import 'dart:typed_data'; | ||
3 | + | ||
4 | +import '../../document.dart'; | ||
5 | +import '../../format/array.dart'; | ||
6 | +import '../../format/base.dart'; | ||
7 | +import '../../format/dict.dart'; | ||
8 | +import '../../format/dict_stream.dart'; | ||
9 | +import '../../format/indirect.dart'; | ||
10 | +import '../../format/name.dart'; | ||
11 | +import '../../format/num.dart'; | ||
12 | +import '../../format/object_base.dart'; | ||
13 | +import '../../format/stream.dart'; | ||
14 | +import '../../format/string.dart'; | ||
15 | +import '../object.dart'; | ||
16 | +import 'pdfa_date_format.dart'; | ||
17 | + | ||
18 | +class PdfaAttachedFiles { | ||
19 | + PdfaAttachedFiles( | ||
20 | + PdfDocument pdfDocument, | ||
21 | + Map<String, String> files, | ||
22 | + ) { | ||
23 | + for (var entry in files.entries) { | ||
24 | + _files.add( | ||
25 | + _AttachedFileSpec( | ||
26 | + pdfDocument, | ||
27 | + _AttachedFile( | ||
28 | + pdfDocument, | ||
29 | + entry.key, | ||
30 | + entry.value, | ||
31 | + ), | ||
32 | + ), | ||
33 | + ); | ||
34 | + } | ||
35 | + _names = _AttachedFileNames( | ||
36 | + pdfDocument, | ||
37 | + _files, | ||
38 | + ); | ||
39 | + pdfDocument.catalog.attached = this; | ||
40 | + } | ||
41 | + | ||
42 | + final List<_AttachedFileSpec> _files = []; | ||
43 | + | ||
44 | + late final _AttachedFileNames _names; | ||
45 | + | ||
46 | + bool get isNotEmpty => _files.isNotEmpty; | ||
47 | + | ||
48 | + PdfDict catalogNames() { | ||
49 | + return PdfDict({ | ||
50 | + '/EmbeddedFiles': _names.ref(), | ||
51 | + }); | ||
52 | + } | ||
53 | + | ||
54 | + PdfArray catalogAF() { | ||
55 | + final tmp = <PdfIndirect>[]; | ||
56 | + for (var spec in _files) { | ||
57 | + tmp.add(spec.ref()); | ||
58 | + } | ||
59 | + return PdfArray(tmp); | ||
60 | + } | ||
61 | +} | ||
62 | + | ||
63 | +class _AttachedFileNames extends PdfObject<PdfDict> { | ||
64 | + _AttachedFileNames( | ||
65 | + PdfDocument pdfDocument, | ||
66 | + this._files, | ||
67 | + ) : super( | ||
68 | + pdfDocument, | ||
69 | + params: PdfDict(), | ||
70 | + ); | ||
71 | + final List<_AttachedFileSpec> _files; | ||
72 | + | ||
73 | + @override | ||
74 | + void prepare() { | ||
75 | + super.prepare(); | ||
76 | + params['/Names'] = PdfArray( | ||
77 | + [ | ||
78 | + _PdfRaw(0, _files.first), | ||
79 | + ], | ||
80 | + ); | ||
81 | + } | ||
82 | +} | ||
83 | + | ||
84 | +class _AttachedFileSpec extends PdfObject<PdfDict> { | ||
85 | + _AttachedFileSpec( | ||
86 | + PdfDocument pdfDocument, | ||
87 | + this._file, | ||
88 | + ) : super( | ||
89 | + pdfDocument, | ||
90 | + params: PdfDict(), | ||
91 | + ); | ||
92 | + final _AttachedFile _file; | ||
93 | + | ||
94 | + @override | ||
95 | + void prepare() { | ||
96 | + super.prepare(); | ||
97 | + | ||
98 | + params['/Type'] = const PdfName('/Filespec'); | ||
99 | + params['/F'] = PdfString( | ||
100 | + Uint8List.fromList(_file.fileName.codeUnits), | ||
101 | + ); | ||
102 | + params['/UF'] = PdfString( | ||
103 | + Uint8List.fromList(_file.fileName.codeUnits), | ||
104 | + ); | ||
105 | + params['/EF'] = PdfDict({ | ||
106 | + '/F': _file.ref(), | ||
107 | + }); | ||
108 | + params['/AFRelationship'] = const PdfName('/Unspecified'); | ||
109 | + } | ||
110 | +} | ||
111 | + | ||
112 | +class _AttachedFile extends PdfObject<PdfDictStream> { | ||
113 | + _AttachedFile( | ||
114 | + PdfDocument pdfDocument, | ||
115 | + this.fileName, | ||
116 | + this.content, | ||
117 | + ) : super( | ||
118 | + pdfDocument, | ||
119 | + params: PdfDictStream( | ||
120 | + compress: false, | ||
121 | + encrypt: false, | ||
122 | + ), | ||
123 | + ); | ||
124 | + | ||
125 | + final String fileName; | ||
126 | + final String content; | ||
127 | + | ||
128 | + @override | ||
129 | + void prepare() { | ||
130 | + super.prepare(); | ||
131 | + | ||
132 | + final modDate = PdfaDateFormat().format(dt: DateTime.now()); | ||
133 | + params['/Type'] = const PdfName('/EmbeddedFile'); | ||
134 | + params['/Subtype'] = const PdfName('/application/octet-stream'); | ||
135 | + params['/Params'] = PdfDict({ | ||
136 | + '/Size': PdfNum(content.codeUnits.length), | ||
137 | + '/ModDate': PdfString( | ||
138 | + Uint8List.fromList('D:$modDate+00\'00\''.codeUnits), | ||
139 | + ), | ||
140 | + }); | ||
141 | + | ||
142 | + params.data = Uint8List.fromList(utf8.encode(content)); | ||
143 | + } | ||
144 | +} | ||
145 | + | ||
146 | +class _PdfRaw extends PdfDataType { | ||
147 | + const _PdfRaw( | ||
148 | + this.nr, | ||
149 | + this.spec, | ||
150 | + ); | ||
151 | + | ||
152 | + final int nr; | ||
153 | + final _AttachedFileSpec spec; | ||
154 | + | ||
155 | + @override | ||
156 | + void output( | ||
157 | + PdfObjectBase o, | ||
158 | + PdfStream s, [ | ||
159 | + int? indent, | ||
160 | + ]) { | ||
161 | + s.putString('(${nr.toString().padLeft(3, '0')}) ${spec.ref()}'); | ||
162 | + } | ||
163 | +} |
1 | +import 'dart:typed_data'; | ||
2 | + | ||
3 | +import '../../document.dart'; | ||
4 | +import '../../format/array.dart'; | ||
5 | +import '../../format/dict.dart'; | ||
6 | +import '../../format/dict_stream.dart'; | ||
7 | +import '../../format/name.dart'; | ||
8 | +import '../../format/num.dart'; | ||
9 | +import '../../format/string.dart'; | ||
10 | +import '../object.dart'; | ||
11 | + | ||
12 | +class PdfaColorProfile extends PdfObject<PdfDictStream> { | ||
13 | + PdfaColorProfile( | ||
14 | + PdfDocument pdfDocument, | ||
15 | + this.icc, | ||
16 | + ) : super( | ||
17 | + pdfDocument, | ||
18 | + params: PdfDictStream( | ||
19 | + compress: false, | ||
20 | + encrypt: false, | ||
21 | + ), | ||
22 | + ) { | ||
23 | + pdfDocument.catalog.colorProfile = this; | ||
24 | + } | ||
25 | + | ||
26 | + final Uint8List icc; | ||
27 | + | ||
28 | + @override | ||
29 | + void prepare() { | ||
30 | + super.prepare(); | ||
31 | + params['/N'] = const PdfNum(3); | ||
32 | + params.data = icc; | ||
33 | + } | ||
34 | + | ||
35 | + PdfArray outputIntents() { | ||
36 | + return PdfArray<PdfDict>([ | ||
37 | + PdfDict({ | ||
38 | + '/Type': const PdfName('/OutputIntent'), | ||
39 | + '/S': const PdfName('/GTS_PDFA1'), | ||
40 | + '/OutputConditionIdentifier': | ||
41 | + PdfString(Uint8List.fromList('sRGB2014.icc'.codeUnits)), | ||
42 | + '/Info': PdfString(Uint8List.fromList('sRGB2014.icc'.codeUnits)), | ||
43 | + '/RegistryName': | ||
44 | + PdfString(Uint8List.fromList('http://www.color.org'.codeUnits)), | ||
45 | + '/DestOutputProfile': ref(), | ||
46 | + }), | ||
47 | + ]); | ||
48 | + } | ||
49 | +} |
1 | +class PdfaDateFormat { | ||
2 | + String format({ | ||
3 | + required DateTime dt, | ||
4 | + bool asIso = false, | ||
5 | + }) { | ||
6 | + final year = dt.year.toString().padLeft(4, '0'); | ||
7 | + final month = dt.month.toString().padLeft(2, '0'); | ||
8 | + final day = dt.day.toString().padLeft(2, '0'); | ||
9 | + final hour = dt.hour.toString().padLeft(2, '0'); | ||
10 | + final minute = dt.minute.toString().padLeft(2, '0'); | ||
11 | + final second = dt.second.toString().padLeft(2, '0'); | ||
12 | + | ||
13 | + if (asIso) { | ||
14 | + // "yyyy-MM-dd'T'HH:mm:ss" | ||
15 | + return '$year-$month-${day}T$hour:$minute:$second'; | ||
16 | + } | ||
17 | + // "yyyyMMddHHmmss" | ||
18 | + return '$year$month$day$hour$minute$second'; | ||
19 | + } | ||
20 | +} |
1 | +class PdfaFacturxRdf { | ||
2 | + String create({ | ||
3 | + String filename = 'factur-x.xml', | ||
4 | + String namespace = 'urn:cen.eu:invoice:1p0:schema#', | ||
5 | + String conformanceLevel = 'BASIC', | ||
6 | + String version = '1.0', | ||
7 | + }) { | ||
8 | + return ''' | ||
9 | + | ||
10 | +<rdf:Description xmlns:fx="$namespace" rdf:about=""> | ||
11 | + <fx:DocumentType>INVOICE</fx:DocumentType> | ||
12 | + <fx:DocumentFileName>$filename</fx:DocumentFileName> | ||
13 | + <fx:Version>$version</fx:Version> | ||
14 | + <fx:ConformanceLevel>$conformanceLevel</fx:ConformanceLevel> | ||
15 | +</rdf:Description> | ||
16 | + | ||
17 | +<rdf:Description xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/" | ||
18 | + xmlns:pdfaField="http://www.aiim.org/pdfa/ns/field#" | ||
19 | + xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#" | ||
20 | + xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#" | ||
21 | + xmlns:pdfaType="http://www.aiim.org/pdfa/ns/type#" | ||
22 | + rdf:about="" | ||
23 | +> | ||
24 | + <pdfaExtension:schemas> | ||
25 | + <rdf:Bag> | ||
26 | + <rdf:li rdf:parseType="Resource"> | ||
27 | + <pdfaSchema:schema>Invoice PDFA Extension Schema</pdfaSchema:schema> | ||
28 | + <pdfaSchema:namespaceURI>$namespace</pdfaSchema:namespaceURI> | ||
29 | + <pdfaSchema:prefix>fx</pdfaSchema:prefix> | ||
30 | + <pdfaSchema:property> | ||
31 | + <rdf:Seq> | ||
32 | + <rdf:li rdf:parseType="Resource"> | ||
33 | + <pdfaProperty:name>DocumentFileName</pdfaProperty:name> | ||
34 | + <pdfaProperty:valueType>Text</pdfaProperty:valueType> | ||
35 | + <pdfaProperty:category>external</pdfaProperty:category> | ||
36 | + <pdfaProperty:description>name of the embedded XML invoice file</pdfaProperty:description> | ||
37 | + </rdf:li> | ||
38 | + <rdf:li rdf:parseType="Resource"> | ||
39 | + <pdfaProperty:name>DocumentType</pdfaProperty:name> | ||
40 | + <pdfaProperty:valueType>Text</pdfaProperty:valueType> | ||
41 | + <pdfaProperty:category>external</pdfaProperty:category> | ||
42 | + <pdfaProperty:description>INVOICE</pdfaProperty:description> | ||
43 | + </rdf:li> | ||
44 | + <rdf:li rdf:parseType="Resource"> | ||
45 | + <pdfaProperty:name>Version</pdfaProperty:name> | ||
46 | + <pdfaProperty:valueType>Text</pdfaProperty:valueType> | ||
47 | + <pdfaProperty:category>external</pdfaProperty:category> | ||
48 | + <pdfaProperty:description>The actual version of the ZUGFeRD data</pdfaProperty:description> | ||
49 | + </rdf:li> | ||
50 | + <rdf:li rdf:parseType="Resource"> | ||
51 | + <pdfaProperty:name>ConformanceLevel</pdfaProperty:name> | ||
52 | + <pdfaProperty:valueType>Text</pdfaProperty:valueType> | ||
53 | + <pdfaProperty:category>external</pdfaProperty:category> | ||
54 | + <pdfaProperty:description>The conformance level of the ZUGFeRD data</pdfaProperty:description> | ||
55 | + </rdf:li> | ||
56 | + </rdf:Seq> | ||
57 | + </pdfaSchema:property> | ||
58 | + </rdf:li> | ||
59 | + </rdf:Bag> | ||
60 | + </pdfaExtension:schemas> | ||
61 | +</rdf:Description> | ||
62 | +'''; | ||
63 | + } | ||
64 | +} |
pdf/lib/src/pdf/obj/pdfa/pdfa_rdf.dart
0 → 100644
1 | +import 'package:xml/xml.dart'; | ||
2 | + | ||
3 | +import 'pdfa_date_format.dart'; | ||
4 | + | ||
5 | +class PdfaRdf { | ||
6 | + PdfaRdf({ | ||
7 | + this.title, | ||
8 | + this.author, | ||
9 | + this.creator, | ||
10 | + this.subject, | ||
11 | + this.keywords, | ||
12 | + this.producer, | ||
13 | + DateTime? creationDate, | ||
14 | + this.invoiceRdf = '', | ||
15 | + }) { | ||
16 | + this.creationDate = creationDate ?? DateTime.now(); | ||
17 | + } | ||
18 | + | ||
19 | + final String? title; | ||
20 | + final String? author; | ||
21 | + final String? creator; | ||
22 | + final String? subject; | ||
23 | + final String? keywords; | ||
24 | + final String? producer; | ||
25 | + late final DateTime creationDate; | ||
26 | + final String invoiceRdf; | ||
27 | + | ||
28 | + XmlDocument? create() { | ||
29 | + var createDate = PdfaDateFormat().format(dt: creationDate, asIso: true); | ||
30 | + final offset = creationDate.timeZoneOffset; | ||
31 | + final hours = | ||
32 | + offset.inHours > 0 ? offset.inHours : 1; // For fixing divide by 0 | ||
33 | + if (!offset.isNegative) { | ||
34 | + createDate = | ||
35 | + "$createDate+${offset.inHours.toString().padLeft(2, '0')}:${(offset.inMinutes % (hours * 60)).toString().padLeft(2, '0')}"; | ||
36 | + } else { | ||
37 | + createDate = | ||
38 | + "$createDate-${(-offset.inHours).toString().padLeft(2, '0')}:${(offset.inMinutes % (hours * 60)).toString().padLeft(2, '0')}"; | ||
39 | + } | ||
40 | + | ||
41 | + return XmlDocument.parse(''' | ||
42 | +<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> | ||
43 | +<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> | ||
44 | + <rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/"> | ||
45 | + <pdf:Producer>$producer</pdf:Producer> | ||
46 | + <pdf:Keywords>$keywords</pdf:Keywords> | ||
47 | + </rdf:Description> | ||
48 | + <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/"> | ||
49 | + <xmp:CreateDate>$createDate</xmp:CreateDate> | ||
50 | + <xmp:CreatorTool>$creator</xmp:CreatorTool> | ||
51 | + </rdf:Description> | ||
52 | + <rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"> | ||
53 | + <dc:creator><rdf:Seq><rdf:li>$author</rdf:li></rdf:Seq></dc:creator> | ||
54 | + <dc:title><rdf:Alt><rdf:li xml:lang="x-default">$title</rdf:li></rdf:Alt></dc:title> | ||
55 | + <dc:description><rdf:Alt><rdf:li xml:lang="x-default">$subject</rdf:li></rdf:Alt></dc:description> | ||
56 | + </rdf:Description> | ||
57 | + <rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/"> | ||
58 | + <pdfaid:part>3</pdfaid:part> | ||
59 | + <pdfaid:conformance>B</pdfaid:conformance> | ||
60 | + </rdf:Description> | ||
61 | + | ||
62 | + $invoiceRdf | ||
63 | + | ||
64 | +</rdf:RDF> | ||
65 | +<?xpacket end="r"?> | ||
66 | +'''); | ||
67 | + } | ||
68 | +} |
pdf/lib/src/pdf/obj/pdfa/sRGB2014.icc
0 → 100644
No preview for this file type
-
Please register or login to post a comment