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