1if not modules then modules = { } end modules ['lpdf-fmt'] = {
2 version = 1.001,
3 comment = "companion to lpdf-ini.mkiv",
4 author = "Peter Rolf and Hans Hagen",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files",
7}
8
9
10
11
12
13local tonumber = tonumber
14local lower, gmatch, format, find, gsub = string.lower, string.gmatch, string.format, string.find, string.gsub
15local concat, serialize, sortedhash = table.concat, table.serialize, table.sortedhash
16
17local trace_format = false trackers.register("backend.format", function(v) trace_format = v end)
18local trace_variables = false trackers.register("backend.variables", function(v) trace_variables = v end)
19
20local report_backend = logs.reporter("backend","profiles")
21
22local pdfbackend = backends.registered.pdf
23
24local codeinjections = pdfbackend.codeinjections
25
26local variables = interfaces.variables
27local viewerlayers = attributes.viewerlayers
28local colors = attributes.colors
29local transparencies = attributes.transparencies
30
31local lpdf = lpdf
32local pdfdictionary = lpdf.dictionary
33local pdfarray = lpdf.array
34local pdfconstant = lpdf.constant
35local pdfreference = lpdf.reference
36local pdfflushobject = lpdf.flushobject
37local pdfstring = lpdf.string
38local pdfverbose = lpdf.verbose
39local pdfflushstreamfileobject = lpdf.flushstreamfileobject
40
41local addtoinfo = lpdf.addtoinfo
42local injectxmpinfo = lpdf.injectxmpinfo
43local insertxmpinfo = lpdf.insertxmpinfo
44local replacexmpinfo = lpdf.replacexmpinfo
45
46local settings_to_array = utilities.parsers.settings_to_array
47local settings_to_hash = utilities.parsers.settings_to_hash
48
49
71
72local channels = {
73 gray = 1,
74 grey = 1,
75 rgb = 3,
76 cmyk = 4,
77}
78
79local prefixes = {
80 gray = "DefaultGray",
81 grey = "DefaultGray",
82 rgb = "DefaultRGB",
83 cmyk = "DefaultCMYK",
84}
85
86local formatspecification = nil
87local formatname = nil
88
89
90
91
92
93local pdfformats = {
94
95 ["x1a2001"] = {
96 pdf_version = 1.3,
97 format_name = "PDF/X-1a:2001",
98 xmp_file = "lpdf-pdx.xml",
99 gts_flag = "GTS_PDFX",
100 gray_scale = true,
101 cmyk_colors = true,
102 spot_colors = true,
103 internal_icc_profiles = true,
104 include_cidsets = true,
105 include_charsets = true,
106 include_procsets = true,
107 attachments = false,
108 inject_metadata = function()
109 addtoinfo("GTS_PDFXVersion","PDF/X-1a:2001")
110 replacexmpinfo(
111 "xml://rdf:RDF/pdfaid-placeholder",
112[[
113<rdf:Description rdf:about='' xmlns:pdfxid='http://www.npes.org/pdfx/ns/id/'>
114 <pdfxid:GTS_PDFXVersion>PDF/X-1a:2001</pdfxid:GTS_PDFXVersion>
115 </rdf:Description>
116]]
117 )
118 end
119 },
120
121 ["x1a2003"] = {
122 pdf_version = 1.4,
123 format_name = "PDF/X-1a:2003",
124 xmp_file = "lpdf-pdx.xml",
125 gts_flag = "GTS_PDFX",
126 gray_scale = true,
127 cmyk_colors = true,
128 spot_colors = true,
129 internal_icc_profiles = true,
130 include_cidsets = true,
131 include_charsets = true,
132 include_procsets = true,
133 attachments = false,
134 inject_metadata = function()
135 addtoinfo("GTS_PDFXVersion","PDF/X-1a:2003")
136 replacexmpinfo(
137 "xml://rdf:RDF/pdfaid-placeholder",
138[[
139<rdf:Description rdf:about='' xmlns:pdfxid='http://www.npes.org/pdfx/ns/id/'>
140 <pdfxid:GTS_PDFXVersion>PDF/X-1a:2003</pdfxid:GTS_PDFXVersion>
141 </rdf:Description>
142]]
143 )
144 end
145 },
146
147 ["x32002"] = {
148 pdf_version = 1.3,
149 format_name = "PDF/X-3:2002",
150 xmp_file = "lpdf-pdx.xml",
151 gts_flag = "GTS_PDFX",
152 gray_scale = true,
153 cmyk_colors = true,
154 rgb_colors = true,
155 calibrated_rgb_colors = true,
156 spot_colors = true,
157 cielab_colors = true,
158 internal_icc_profiles = true,
159 include_intents = true,
160 include_cidsets = true,
161 include_charsets = true,
162 include_procsets = true,
163 attachments = false,
164 inject_metadata = function()
165 addtoinfo("GTS_PDFXVersion","PDF/X-3:2002")
166 end
167 },
168
169 ["x32003"] = {
170 pdf_version = 1.4,
171 format_name = "PDF/X-3:2003",
172 xmp_file = "lpdf-pdx.xml",
173 gts_flag = "GTS_PDFX",
174 gray_scale = true,
175 cmyk_colors = true,
176 rgb_colors = true,
177 calibrated_rgb_colors = true,
178 spot_colors = true,
179 cielab_colors = true,
180 internal_icc_profiles = true,
181 include_intents = true,
182 jbig2_compression = true,
183 include_cidsets = true,
184 include_charsets = true,
185 include_procsets = true,
186 attachments = false,
187 inject_metadata = function()
188 addtoinfo("GTS_PDFXVersion","PDF/X-3:2003")
189 end
190 },
191
192 ["x4"] = {
193 pdf_version = 1.6,
194 format_name = "PDF/X-4",
195 xmp_file = "lpdf-pdx.xml",
196 gts_flag = "GTS_PDFX",
197 gray_scale = true,
198 cmyk_colors = true,
199 rgb_colors = true,
200 calibrated_rgb_colors = true,
201 spot_colors = true,
202 cielab_colors = true,
203 internal_icc_profiles = true,
204 include_intents = true,
205 optional_content = true,
206 transparency = true,
207 jbig2_compression = true,
208 jpeg2000_compression = true,
209 object_compression = true,
210 include_cidsets = true,
211 include_charsets = true,
212 include_procsets = true,
213 attachments = false,
214 inject_metadata = function()
215 replacexmpinfo(
216 "xml://rdf:RDF/pdfaid-placeholder",
217[[
218<rdf:Description rdf:about='' xmlns:pdfxid='http://www.npes.org/pdfx/ns/id/'>
219 <pdfxid:GTS_PDFXVersion>PDF/X-4</pdfxid:GTS_PDFXVersion>
220 </rdf:Description>
221]]
222 )
223 insertxmpinfo(
224 "xml://rdf:Description/xmpMM:InstanceID",
225 [[<xmpMM:VersionID>1</xmpMM:VersionID>]],
226 false
227 )
228 insertxmpinfo(
229 "xml://rdf:Description/xmpMM:InstanceID",
230 [[<xmpMM:RenditionClass>default</xmpMM:RenditionClass>]],
231 false
232 )
233 end
234 },
235
236 ["x4p"] = {
237 pdf_version = 1.6,
238 format_name = "PDF/X-4p",
239 xmp_file = "lpdf-pdx.xml",
240 gts_flag = "GTS_PDFX",
241 gray_scale = true,
242 cmyk_colors = true,
243 rgb_colors = true,
244 calibrated_rgb_colors = true,
245 spot_colors = true,
246 cielab_colors = true,
247 internal_icc_profiles = true,
248 external_icc_profiles = true,
249 include_intents = true,
250 optional_content = true,
251 transparency = true,
252 jbig2_compression = true,
253 jpeg2000_compression = true,
254 object_compression = true,
255 include_cidsets = true,
256 include_charsets = true,
257 include_procsets = true,
258 attachments = false,
259 inject_metadata = function()
260 replacexmpinfo(
261 "xml://rdf:RDF/pdfaid-placeholder",
262[[
263<rdf:Description rdf:about='' xmlns:pdfxid='http://www.npes.org/pdfx/ns/id/'>
264 <pdfxid:GTS_PDFXVersion>PDF/X-4p</pdfxid:GTS_PDFXVersion>
265 </rdf:Description>
266]]
267 )
268 insertxmpinfo(
269 "xml://rdf:Description/xmpMM:InstanceID",
270 [[<xmpMM:VersionID>1</xmpMM:VersionID>]],
271 false
272 )
273 insertxmpinfo(
274 "xml://rdf:Description/xmpMM:InstanceID",
275 [[<xmpMM:RenditionClass>default</xmpMM:RenditionClass>]],
276 false
277 )
278 end
279 },
280
281 ["x5g"] = {
282 pdf_version = 1.6,
283 format_name = "PDF/X-5g",
284 xmp_file = "lpdf-pdx.xml",
285 gts_flag = "GTS_PDFX",
286 gray_scale = true,
287 cmyk_colors = true,
288 rgb_colors = true,
289 calibrated_rgb_colors = true,
290 spot_colors = true,
291 cielab_colors = true,
292 internal_icc_profiles = true,
293 include_intents = true,
294 open_prepress_interface = true,
295 optional_content = true,
296 transparency = true,
297 jbig2_compression = true,
298 jpeg2000_compression = true,
299 object_compression = true,
300 include_cidsets = true,
301 include_charsets = true,
302 include_procsets = true,
303 attachments = false,
304 inject_metadata = function()
305
306 end
307 },
308
309 ["x5pg"] = {
310 pdf_version = 1.6,
311 format_name = "PDF/X-5pg",
312 xmp_file = "lpdf-pdx.xml",
313 gts_flag = "GTS_PDFX",
314 gray_scale = true,
315 cmyk_colors = true,
316 rgb_colors = true,
317 calibrated_rgb_colors = true,
318 spot_colors = true,
319 cielab_colors = true,
320 internal_icc_profiles = true,
321 external_icc_profiles = true,
322 include_intents = true,
323 open_prepress_interface = true,
324 optional_content = true,
325 transparency = true,
326 jbig2_compression = true,
327 jpeg2000_compression = true,
328 object_compression = true,
329 include_cidsets = true,
330 include_charsets = true,
331 include_procsets = true,
332 attachments = false,
333 inject_metadata = function()
334
335 end
336 },
337
338 ["x5n"] = {
339 pdf_version = 1.6,
340 format_name = "PDF/X-5n",
341 xmp_file = "lpdf-pdx.xml",
342 gts_flag = "GTS_PDFX",
343 gray_scale = true,
344 cmyk_colors = true,
345 rgb_colors = true,
346 calibrated_rgb_colors = true,
347 spot_colors = true,
348 cielab_colors = true,
349 internal_icc_profiles = true,
350 include_intents = true,
351 optional_content = true,
352 transparency = true,
353 jbig2_compression = true,
354 jpeg2000_compression = true,
355 nchannel_colorspace = true,
356 object_compression = true,
357 include_cidsets = true,
358 include_charsets = true,
359 include_procsets = true,
360 attachments = false,
361 inject_metadata = function()
362
363 end
364 },
365
366 ["a1a2005"] = {
367 pdf_version = 1.4,
368 format_name = "pdf/a-1a:2005",
369 xmp_file = "lpdf-pda.xml",
370 gts_flag = "GTS_PDFA1",
371 gray_scale = true,
372 cmyk_colors = true,
373 rgb_colors = true,
374 spot_colors = true,
375 calibrated_rgb_colors = true,
376 cielab_colors = true,
377 include_intents = true,
378 forms = true,
379 tagging = true,
380 internal_icc_profiles = true,
381 include_cidsets = true,
382 include_charsets = true,
383 include_procsets = true,
384 attachments = false,
385 inject_metadata = function()
386 replacexmpinfo(
387 "xml://rdf:RDF/pdfaid-placeholder",
388[[
389<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'>
390 <pdfaid:part>1</pdfaid:part>
391 <pdfaid:conformance>A</pdfaid:conformance>
392 </rdf:Description>
393]]
394 )
395 end
396 },
397
398 ["a1b2005"] = {
399 pdf_version = 1.4,
400 format_name = "pdf/a-1b:2005",
401 xmp_file = "lpdf-pda.xml",
402 gts_flag = "GTS_PDFA1",
403 gray_scale = true,
404 cmyk_colors = true,
405 rgb_colors = true,
406 spot_colors = true,
407 calibrated_rgb_colors = true,
408 cielab_colors = true,
409 include_intents = true,
410 forms = true,
411 internal_icc_profiles = true,
412 include_cidsets = true,
413 include_charsets = true,
414 include_procsets = true,
415 attachments = false,
416 inject_metadata = function()
417 replacexmpinfo(
418 "xml://rdf:RDF/pdfaid-placeholder",
419[[
420<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'>
421 <pdfaid:part>1</pdfaid:part>
422 <pdfaid:conformance>B</pdfaid:conformance>
423 </rdf:Description>
424]]
425 )
426 end
427 },
428
429
430
431 ["a2a"] = {
432 pdf_version = 1.7,
433 format_name = "pdf/a-2a",
434 xmp_file = "lpdf-pda.xml",
435 gts_flag = "GTS_PDFA1",
436 gray_scale = true,
437 cmyk_colors = true,
438 rgb_colors = true,
439 spot_colors = true,
440 calibrated_rgb_colors = true,
441 cielab_colors = true,
442 include_intents = true,
443 forms = true,
444 tagging = true,
445 internal_icc_profiles = true,
446 transparency = true,
447 jbig2_compression = true,
448 jpeg2000_compression = true,
449 object_compression = true,
450
451
452
453 attachments = true,
454 inject_metadata = function()
455 replacexmpinfo(
456 "xml://rdf:RDF/pdfaid-placeholder",
457[[
458<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'>
459 <pdfaid:part>2</pdfaid:part>
460 <pdfaid:conformance>A</pdfaid:conformance>
461 </rdf:Description>
462]]
463 )
464 end
465 },
466
467 ["a2b"] = {
468 pdf_version = 1.7,
469 format_name = "pdf/a-2b",
470 xmp_file = "lpdf-pda.xml",
471 gts_flag = "GTS_PDFA1",
472 gray_scale = true,
473 cmyk_colors = true,
474 rgb_colors = true,
475 spot_colors = true,
476 calibrated_rgb_colors = true,
477 cielab_colors = true,
478 include_intents = true,
479 forms = true,
480 tagging = false,
481 internal_icc_profiles = true,
482 transparency = true,
483 jbig2_compression = true,
484 jpeg2000_compression = true,
485 object_compression = true,
486
487
488
489 attachments = true,
490 inject_metadata = function()
491 replacexmpinfo(
492 "xml://rdf:RDF/pdfaid-placeholder",
493[[
494<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'>
495 <pdfaid:part>2</pdfaid:part>
496 <pdfaid:conformance>B</pdfaid:conformance>
497 </rdf:Description>
498]]
499 )
500 end
501 },
502
503
504
505 ["a2u"] = {
506 pdf_version = 1.7,
507 format_name = "pdf/a-2u",
508 xmp_file = "lpdf-pda.xml",
509 gts_flag = "GTS_PDFA1",
510 gray_scale = true,
511 cmyk_colors = true,
512 rgb_colors = true,
513 spot_colors = true,
514 calibrated_rgb_colors = true,
515 cielab_colors = true,
516 include_intents = true,
517 forms = true,
518
519 internal_icc_profiles = true,
520 transparency = true,
521 jbig2_compression = true,
522 jpeg2000_compression = true,
523 object_compression = true,
524
525
526
527 attachments = true,
528 inject_metadata = function()
529 replacexmpinfo(
530 "xml://rdf:RDF/pdfaid-placeholder",
531[[
532<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'>
533 <pdfaid:part>2</pdfaid:part>
534 <pdfaid:conformance>U</pdfaid:conformance>
535 </rdf:Description>
536]]
537 )
538 end
539 },
540
541
542
543 ["a3a"] = {
544 pdf_version = 1.7,
545 format_name = "pdf/a-3a",
546 xmp_file = "lpdf-pda.xml",
547 gts_flag = "GTS_PDFA1",
548 gray_scale = true,
549 cmyk_colors = true,
550 rgb_colors = true,
551 spot_colors = true,
552 calibrated_rgb_colors = true,
553 cielab_colors = true,
554 include_intents = true,
555 forms = true,
556 tagging = true,
557 internal_icc_profiles = true,
558 transparency = true,
559 jbig2_compression = true,
560 jpeg2000_compression = true,
561 object_compression = true,
562
563
564
565 attachments = true,
566 inject_metadata = function()
567 replacexmpinfo(
568 "xml://rdf:RDF/pdfaid-placeholder",
569[[
570<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'>
571 <pdfaid:part>3</pdfaid:part>
572 <pdfaid:conformance>A</pdfaid:conformance>
573 </rdf:Description>
574]]
575 )
576 end
577 },
578
579 ["a3b"] = {
580 pdf_version = 1.7,
581 format_name = "pdf/a-3b",
582 xmp_file = "lpdf-pda.xml",
583 gts_flag = "GTS_PDFA1",
584 gray_scale = true,
585 cmyk_colors = true,
586 rgb_colors = true,
587 spot_colors = true,
588 calibrated_rgb_colors = true,
589 cielab_colors = true,
590 include_intents = true,
591 forms = true,
592
593 internal_icc_profiles = true,
594 transparency = true,
595 jbig2_compression = true,
596 jpeg2000_compression = true,
597 object_compression = true,
598
599
600
601 attachments = true,
602 inject_metadata = function()
603 replacexmpinfo(
604 "xml://rdf:RDF/pdfaid-placeholder",
605[[
606<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'>
607 <pdfaid:part>3</pdfaid:part>
608 <pdfaid:conformance>B</pdfaid:conformance>
609 </rdf:Description>
610]]
611 )
612 end
613 },
614
615 ["a3u"] = {
616 pdf_version = 1.7,
617 format_name = "pdf/a-3u",
618 xmp_file = "lpdf-pda.xml",
619 gts_flag = "GTS_PDFA1",
620 gray_scale = true,
621 cmyk_colors = true,
622 rgb_colors = true,
623 spot_colors = true,
624 calibrated_rgb_colors = true,
625 cielab_colors = true,
626 include_intents = true,
627 forms = true,
628 tagging = false,
629 internal_icc_profiles = true,
630 transparency = true,
631 jbig2_compression = true,
632 jpeg2000_compression = true,
633 object_compression = true,
634
635
636
637 attachments = true,
638 inject_metadata = function()
639 replacexmpinfo(
640 "xml://rdf:RDF/pdfaid-placeholder",
641[[
642<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'>
643 <pdfaid:part>3</pdfaid:part>
644 <pdfaid:conformance>U</pdfaid:conformance>
645 </rdf:Description>
646]]
647 )
648 end
649 },
650
651 ["ua1"] = {
652 pdf_version = 1.7,
653 format_name = "pdf/ua-1",
654 xmp_file = "lpdf-pua.xml",
655 gray_scale = true,
656 cmyk_colors = true,
657 rgb_colors = true,
658 spot_colors = true,
659 calibrated_rgb_colors = true,
660 cielab_colors = true,
661 include_intents = true,
662 forms = true,
663 tagging = true,
664 internal_icc_profiles = true,
665 transparency = true,
666 jbig2_compression = true,
667 jpeg2000_compression = true,
668 object_compression = true,
669
670
671
672 attachments = true,
673 inject_metadata = function()
674 replacexmpinfo(
675 "xml://rdf:RDF/pdfaid-placeholder",
676[[
677<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'>
678 <pdfaid:part>3</pdfaid:part>
679 <pdfaid:conformance>A</pdfaid:conformance>
680 </rdf:Description>
681]]
682 )
683 replacexmpinfo(
684 "xml://rdf:RDF/pdfuaid-placeholder",
685[[
686<rdf:Description rdf:about='' xmlns:pdfuaid='http://www.aiim.org/pdfua/ns/id/'>
687 <pdfuaid:part>1</pdfuaid:part>
688 <pdfuaid:rev>2024</pdfuaid:rev>
689 </rdf:Description>
690]]
691 )
692 end
693 },
694
695 ["ua2"] = {
696 pdf_version = 2.0,
697 format_name = "pdf/ua-2",
698 xmp_file = "lpdf-pua.xml",
699 gray_scale = true,
700 cmyk_colors = true,
701 rgb_colors = true,
702 spot_colors = true,
703 calibrated_rgb_colors = true,
704 cielab_colors = true,
705 include_intents = true,
706 forms = true,
707 tagging = true,
708 internal_icc_profiles = true,
709 transparency = true,
710 jbig2_compression = true,
711 jpeg2000_compression = true,
712 object_compression = true,
713
714
715
716 attachments = true,
717 inject_metadata = function()
718 replacexmpinfo(
719 "xml://rdf:RDF/pdfaid-placeholder",
720[[
721<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'>
722 <pdfaid:part>3</pdfaid:part>
723 <pdfaid:conformance>A</pdfaid:conformance>
724 </rdf:Description>
725]]
726 )
727 replacexmpinfo(
728 "xml://rdf:RDF/pdfuaid-placeholder",
729[[
730<rdf:Description rdf:about='' xmlns:pdfuaid='http://www.aiim.org/pdfua/ns/id/'>
731 <pdfuaid:part>2</pdfuaid:part>
732 <pdfuaid:rev>2024</pdfuaid:rev>
733 </rdf:Description>
734]]
735 )
736 end,
737 inject_conformance = function(indeed)
738 return replacexmpinfo(
739 "xml://rdf:RDF/wtpdf-placeholder",
740indeed and [[
741<rdf:Description rdf:about='' xmlns:pdfd='http://pdfa.org/declarations/'>
742 <pdfd:declarations>
743 <rdf:Bag>
744 <rdf:li rdf:parseType='Resource'>
745 <pdfd:conformsTo>http://pdfa.org/declarations/wtpdf#accessibility1.0</pdfd:conformsTo>
746 </rdf:li>
747 <rdf:li rdf:parseType='Resource'>
748 <pdfd:conformsTo>http://pdfa.org/declarations/wtpdf#reuse1.0</pdfd:conformsTo>
749 </rdf:li>
750 </rdf:Bag>
751 </pdfd:declarations>
752 </rdf:Description>
753]] or ""
754 )
755 end
756 },
757
758}
759
760pdfformats["2"] = pdfformats["ua2"]
761pdfformats["2.0"] = pdfformats["ua2"]
762
763local formats = utilities.storage.allocate {
764 version = {
765 external_icc_profiles = 1.4,
766 jbig2_compression = 1.4,
767 jpeg2000_compression = 1.5,
768 nchannel_colorspace = 1.6,
769 open_prepress_interface = 1.3,
770 optional_content = 1.5,
771 transparency = 1.4,
772 object_compression = 1.5,
773 attachments = 1.7,
774 },
775 default = {
776 pdf_version = 1.7,
777 format_name = "default",
778 xmp_file = "lpdf-pdx.xml",
779 gray_scale = true,
780 cmyk_colors = true,
781 rgb_colors = true,
782 spot_colors = true,
783 calibrated_rgb_colors = true,
784 cielab_colors = true,
785 nchannel_colorspace = true,
786 internal_icc_profiles = true,
787 external_icc_profiles = true,
788 include_intents = true,
789 open_prepress_interface = true,
790 optional_content = true,
791 transparency = true,
792 jbig2_compression = true,
793 jpeg2000_compression = true,
794
795
796
797 attachments = true,
798 inject_metadata = function()
799
800 end
801 },
802 data = pdfformats,
803}
804
805lpdf.formats = formats
806
807local filenames = {
808 "colorprofiles.xml",
809 "colorprofiles.lua",
810}
811
812local function locatefile(filename)
813 local fullname = resolvers.findfile(filename,"icc",1,true)
814 if not fullname or fullname == "" then
815 fullname = resolvers.finders.byscheme("loc",filename)
816 end
817 return fullname or ""
818end
819
820local function loadprofile(name,filename)
821 local profile = false
822 local databases = filename and filename ~= "" and settings_to_array(filename) or filenames
823 for i=1,#databases do
824 local filename = locatefile(databases[i])
825 if filename and filename ~= "" then
826 local suffix = file.suffix(filename)
827 local lname = lower(name)
828 if suffix == "xml" then
829 local xmldata = xml.load(filename)
830 if xmldata then
831 profile = xml.filter(xmldata,format('xml://profiles/profile/(info|filename)[lower(text())=="%s"]/../table()',lname))
832 end
833 elseif suffix == "lua" then
834 local luadata = loadfile(filename)
835 luadata = ludata and luadata()
836 if luadata then
837 profile = luadata[name] or luadata[lname]
838 if not profile then
839 for i=1,#luadata do
840 local li = luadata[i]
841 if lower(li.info) == lname then
842 profile = li
843 break
844 end
845 end
846 end
847 end
848 end
849 if profile then
850 if next(profile) then
851 report_backend("profile specification %a loaded from %a",name,filename)
852 return profile
853 elseif trace_format then
854 report_backend("profile specification %a loaded from %a but empty",name,filename)
855 end
856 return false
857 end
858 end
859 end
860 report_backend("profile specification %a not found in %a",name,concat(filenames, ", "))
861end
862
863local function urls(url)
864 if not url or url == "" then
865 return nil
866 else
867 local u = pdfarray()
868 for url in gmatch(url,"([^, ]+)") do
869 if find(url,"^http") then
870 u[#u+1] = pdfdictionary {
871 FS = pdfconstant("URL"),
872 F = pdfstring(url),
873 }
874 end
875 end
876 return u
877 end
878end
879
880local function profilename(filename)
881 return lower(file.basename(filename))
882end
883
884local internalprofiles = { }
885local defaultprofiles = { }
886
887local function handleinternalprofile(s,include)
888 local filename, colorspace = s.filename or "", s.colorspace or ""
889 if filename == "" or colorspace == "" then
890 report_backend("error in internal profile specification: %s",serialize(s,false))
891 else
892 local tag = profilename(filename)
893 local profile = internalprofiles[tag]
894 if not profile then
895 local colorspace = lower(colorspace)
896 if include then
897
898 local fullname = locatefile(filename)
899 if fullname == "" then
900 fullname = locatefile("colo-imp-" .. filename)
901 end
902 local channel = channels[colorspace] or nil
903 if fullname == "" then
904 report_backend("error, couldn't locate profile %a",filename)
905 elseif not channel then
906 report_backend("error, couldn't resolve channel entry for colorspace %a",colorspace)
907 else
908 profile = pdfflushstreamfileobject(fullname,pdfdictionary{ N = channel },false)
909 internalprofiles[tag] = profile
910 defaultprofiles[channel] = profile
911 if trace_format then
912 report_backend("including %a color profile from %a",colorspace,fullname)
913 end
914 end
915 else
916 internalprofiles[tag] = true
917 if trace_format then
918 report_backend("not including %a color profile %a",colorspace,filename)
919 end
920 end
921 end
922 return profile
923 end
924end
925
926function codeinjections.defaultprofile(channel)
927
928 return defaultprofiles[channel]
929end
930
931local externalprofiles = { }
932
933local function handleexternalprofile(s,include)
934 local name, url, filename, checksum, version, colorspace =
935 s.info or s.filename or "", s.url or "", s.filename or "", s.checksum or "", s.version or "", s.colorspace or ""
936 if false then
937 local iccprofile = colors.iccprofile(filename)
938 if iccprofile then
939 name = name ~= "" and name or iccprofile.tags.desc.cleaned or ""
940 url = url ~= "" and url or iccprofile.tags.dmnd.cleaned or ""
941 checksum = checksum ~= "" and checksum or file.checksum(iccprofile.fullname) or ""
942 version = version ~= "" and version or iccprofile.header.version or ""
943 colorspace = colorspace ~= "" and colorspace or iccprofile.header.colorspace or ""
944 end
945
946 end
947 if name == "" or url == "" or checksum == "" or version == "" or colorspace == "" or filename == "" then
948 local profile = handleinternalprofile(s)
949 if profile then
950 report_backend("incomplete external profile specification, falling back to internal")
951 else
952 report_backend("error in external profile specification: %s",serialize(s,false))
953 end
954 else
955 local tag = profilename(filename)
956 local profile = externalprofiles[tag]
957 if not profile then
958 local d = pdfdictionary {
959 ProfileName = name,
960 ProfileCS = colorspace,
961 URLs = urls(url),
962 CheckSum = pdfverbose { "<", lower(checksum), ">" },
963 ICCVersion = pdfverbose { "<", version, ">" },
964 }
965 profile = pdfflushobject(d)
966 externalprofiles[tag] = profile
967 end
968 return profile
969 end
970end
971
972local loadeddefaults = { }
973
974local function handledefaultprofile(s,spec)
975 local filename, colorspace = s.filename or "", lower(s.colorspace or "")
976 if filename == "" or colorspace == "" then
977 report_backend("error in default profile specification: %s",serialize(s,false))
978 elseif not loadeddefaults[colorspace] then
979 local tag = profilename(filename)
980 local n = internalprofiles[tag]
981 if n == true then
982 report_backend("no default profile %a for colorspace %a",filename,colorspace)
983 elseif n then
984 local a = pdfarray {
985 pdfconstant("ICCBased"),
986 pdfreference(n),
987 }
988
989 lpdf.adddocumentcolorspace(prefixes[colorspace],pdfreference(pdfflushobject(a)))
990 loadeddefaults[colorspace] = true
991 report_backend("setting %a as default %a color space",filename,colorspace)
992 else
993 report_backend("no default profile %a for colorspace %a",filename,colorspace)
994 end
995 elseif trace_format then
996 report_backend("a default %a colorspace is already in use",colorspace)
997 end
998end
999
1000local loadedintents = { }
1001local intents = pdfarray()
1002
1003local function handleoutputintent(s,spec)
1004 local url = s.url or ""
1005 local filename = s.filename or ""
1006 local name = s.info or filename
1007 local id = s.id or ""
1008 local outputcondition = s.outputcondition or ""
1009 local info = s.info or ""
1010 if name == "" or id == "" then
1011 report_backend("error in output intent specification: %s",serialize(s,false))
1012 elseif not loadedintents[name] then
1013 local tag = profilename(filename)
1014 local internal, external = internalprofiles[tag], externalprofiles[tag]
1015 if internal or external then
1016 local d = {
1017 Type = pdfconstant("OutputIntent"),
1018 S = pdfconstant(spec.gts_flag or "GTS_PDFX"),
1019 OutputConditionIdentifier = id,
1020 RegistryName = url,
1021 OutputCondition = outputcondition,
1022 Info = info,
1023 }
1024 if internal and internal ~= true then
1025 d.DestOutputProfile = pdfreference(internal)
1026 elseif external and external ~= true then
1027 d.DestOutputProfileRef = pdfreference(external)
1028 else
1029 report_backend("omitting reference to profile for intent %a",name)
1030 end
1031 intents[#intents+1] = pdfreference(pdfflushobject(pdfdictionary(d)))
1032 if trace_format then
1033 report_backend("setting output intent to %a with id %a for entry %a",name,id,#intents)
1034 end
1035 else
1036 report_backend("invalid output intent %a",name)
1037 end
1038 loadedintents[name] = true
1039 elseif trace_format then
1040 report_backend("an output intent with name %a is already in use",name)
1041 end
1042end
1043
1044local function handleiccprofile(message,spec,name,filename,how,options,alwaysinclude,gts_flag)
1045 if name and name ~= "" then
1046 local list = settings_to_array(name)
1047 for i=1,#list do
1048 local name = list[i]
1049 local profile = loadprofile(name,filename)
1050 if trace_format then
1051 report_backend("handling %s %a",message,name)
1052 end
1053 if profile then
1054 if formatspecification.cmyk_colors then
1055 profile.colorspace = profile.colorspace or "CMYK"
1056 else
1057 profile.colorspace = profile.colorspace or "RGB"
1058 end
1059 local external = formatspecification.external_icc_profiles
1060 local internal = formatspecification.internal_icc_profiles
1061 local include = formatspecification.include_intents
1062 local always, never = options[variables.always], options[variables.never]
1063 if always or alwaysinclude then
1064 if trace_format then
1065 report_backend("forcing internal profiles")
1066 end
1067
1068 internal, external = not never, false
1069 elseif never then
1070 if trace_format then
1071 report_backend("forcing external profiles")
1072 end
1073 internal, external = false, true
1074 end
1075 if external then
1076 if trace_format then
1077 report_backend("handling external profiles cf. %a",name)
1078 end
1079 handleexternalprofile(profile,false)
1080 else
1081 if trace_format then
1082 report_backend("handling internal profiles cf. %a",name)
1083 end
1084 if internal then
1085 handleinternalprofile(profile,always or include)
1086 else
1087 report_backend("no profile inclusion for %a",formatname)
1088 end
1089 end
1090 how(profile,spec)
1091 elseif trace_format then
1092 report_backend("unknown profile %a",name)
1093 end
1094 end
1095 end
1096end
1097
1098local function flushoutputintents()
1099 if #intents > 0 then
1100 lpdf.addtocatalog("OutputIntents",pdfreference(pdfflushobject(intents)))
1101 end
1102end
1103
1104lpdf.registerdocumentfinalizer(flushoutputintents,2,"output intents")
1105
1106function codeinjections.setformat(s)
1107 local format = s.format or ""
1108 local level = tonumber(s.level)
1109 local intent = s.intent or ""
1110 local profile = s.profile or ""
1111 local option = s.option or ""
1112 local filename = s.file or ""
1113 if format ~= "" then
1114 local form = lower(format)
1115 form = gsub(form,"pdf/","")
1116 form = gsub(form,"[^a-z0-9]","")
1117 local spec = pdfformats[form]
1118 if spec then
1119 formatspecification = spec
1120 formatname = spec.format_name
1121 report_backend("setting format %a to %a",format,formatname)
1122 local xmp_file = formatspecification.xmp_file or ""
1123 if xmp_file == "" then
1124
1125 else
1126 codeinjections.setxmpfile(xmp_file)
1127 end
1128 if not level then
1129 level = 3
1130 end
1131 local pdf_version = spec.pdf_version * 10
1132 local inject_metadata = spec.inject_metadata
1133 local inject_conformance = spec.inject_conformance
1134 local majorversion = math.floor(math.div(pdf_version,10))
1135 local minorversion = math.floor(math.mod(pdf_version,10))
1136 local objectcompression = spec.object_compression and pdf_version >= 15
1137 local compresslevel = level or lpdf.compresslevel()
1138 local objectcompresslevel = (objectcompression and (level or lpdf.objectcompresslevel())) or 0
1139 lpdf.setcompression(compresslevel,objectcompresslevel)
1140 lpdf.setversion(majorversion,minorversion)
1141 if objectcompression then
1142 report_backend("forcing pdf version %s.%s, compression level %s, object compression level %s",
1143 majorversion,minorversion,compresslevel,objectcompresslevel)
1144 elseif compresslevel > 0 then
1145 report_backend("forcing pdf version %s.%s, compression level %s, object compression disabled",
1146 majorversion,minorversion,compresslevel)
1147 else
1148 report_backend("forcing pdf version %s.%s, compression disabled",
1149 majorversion,minorversion)
1150 end
1151
1152
1153
1154 codeinjections.settaggingsupport(formatspecification.tagging)
1155 codeinjections.setattachmentsupport(formatspecification.attachments)
1156
1157
1158
1159
1160
1161
1162 local rgb = spec.rgb_colors and variables.yes or variables.no
1163 local cmy = spec.cmyk_colors and variables.yes or variables.no
1164 report_backend("permitted colorspaces: rgb %a, cmyk %a",rgb,cmy)
1165
1166
1167 colors.forcesupport(
1168 spec.gray_scale or false,
1169 spec.rgb_colors or false,
1170 spec.cmyk_colors or false,
1171 spec.spot_colors or false,
1172 spec.nchannel_colorspace or false
1173 )
1174 transparencies.forcesupport(
1175 spec.transparency or false
1176 )
1177 viewerlayers.forcesupport(
1178 spec.optional_content or false
1179 )
1180 viewerlayers.setfeatures(
1181 spec.has_order or false
1182 )
1183
1184
1185
1186
1187 if type(inject_metadata) == "function" then
1188 inject_metadata()
1189 end
1190 if type(inject_conformance) == "function" then
1191 inject_conformance(environment.arguments.wtpdf)
1192 end
1193 local options = settings_to_hash(option)
1194 handleiccprofile("color profile",spec,profile,filename,handledefaultprofile,options,true)
1195 handleiccprofile("output intent",spec,intent,filename,handleoutputintent,options,false)
1196 if trace_variables then
1197 for k, v in sortedhash(formats.default) do
1198 local v = formatspecification[k]
1199 if type(v) ~= "function" then
1200 report_backend("%a = %a",k,v or false)
1201 end
1202 end
1203 end
1204 function codeinjections.setformat(noname)
1205 if trace_format then
1206 report_backend("error, format is already set to %a, ignoring %a",formatname,noname.format)
1207 end
1208 end
1209
1210 lpdf.setincludecidset(spec.include_cidsets)
1211 lpdf.setincludeprocset(spec.include_procsets)
1212 else
1213 report_backend("error, format %a is not supported, valid formats: % | t",
1214 format,table.sortedkeys(formats.data))
1215 end
1216 elseif level then
1217 lpdf.setcompression(level,level)
1218 else
1219
1220 end
1221end
1222
1223directives.register("backend.format", function(v)
1224 local tv = type(v)
1225 if tv == "table" then
1226 codeinjections.setformat(v)
1227 elseif tv == "string" then
1228 codeinjections.setformat { format = v }
1229 end
1230end)
1231
1232interfaces.implement {
1233 name = "setformat",
1234 actions = codeinjections.setformat,
1235 arguments = { { "*" } }
1236}
1237
1238function codeinjections.getformatoption(key)
1239 return formatspecification and formatspecification[key]
1240end
1241
1242
1243
1244
1245
1246function codeinjections.supportedformats()
1247 local t = { }
1248 for k, v in sortedhash(formats.data) do
1249 t[#t+1] = k
1250 end
1251 return t
1252end
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273 |