-
Notifications
You must be signed in to change notification settings - Fork 8
/
l3pdfmeta.dtx
3119 lines (3081 loc) · 108 KB
/
l3pdfmeta.dtx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
% \iffalse meta-comment
%
%% File: l3pdfmeta.dtx
%
% Copyright (C) 2018-2024 The LaTeX Project
%
% It may be distributed and/or modified under the conditions of the
% LaTeX Project Public License (LPPL), either version 1.3c of this
% license or (at your option) any later version. The latest version
% of this license is in the file
%
% http://www.latex-project.org/lppl.txt
%
% This file is part of the "LaTeX PDF management testphase bundle" (The Work in LPPL)
% and all files in that bundle must be distributed together.
%
% -----------------------------------------------------------------------
%
% The development version of the bundle can be found at
%
% https://github.com/latex3/pdfresources
%
% for those people who are interested.
%
%<*driver>
\DocumentMetadata{pdfstandard=A-2b}
\documentclass[full]{l3doc}
\usepackage{array,booktabs}
\hypersetup{pdfauthor=The LaTeX Project,pdftitle=l3pdfmeta (LaTeX PDF management testphase bundle)}
\begin{document}
\DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{^^A
% The \pkg{l3pdfmeta} module\\ PDF standards ^^A
% \\
% \LaTeX{} PDF management testphase bundle
% }
%
% \author{^^A
% The \LaTeX{} Project\thanks
% {^^A
% E-mail:
% \href{mailto:[email protected]}
% {[email protected]}^^A
% }^^A
% }
%
% \date{Version 0.96n, released 2024-10-27}
%
% \maketitle
% \begin{documentation}
%
% \section{\pkg{l3pdfmeta} documentation}
% This module sets up some tools and commands needed
% for PDF standards in general.
% The goal is to collect the requirements and to provide code to check and fulfill them.
%
%
% \subsection{Verifying requirements of PDF standards}
%
% Standards like pdf/A set requirements on a PDF: Some things have be in the PDF,
% e.g. the catalog has to contain a /Lang entry and an colorprofile and
% an /OutputIntent, some other things are forbidden or restricted, e.g.
% the action dictionary of an annotation should not contain Javascript.
%
% The \pkg{l3pdfmeta} module collects a number of relevant requirements,
% tries to enforce the ones which can be enforced and offers some tools
% for package authors to test if an action is allowed in the standard or not.
%
% This is work in progress and more tests will be added. But it should be noted
% that it will probably never be possible to prevent all forbidden actions
% or enforce all required ones or even to simply check all of them.
% The commands here don't replace a check with an external validator.
%
% Verifying against a PDF-standard involves two different task:
%
% \begin{itemize}
% \item Check if you are allowed to ignore the requirement.
% \item Decide which action to take if the answer to the first question is NO.
% \end{itemize}
%
% The following conditionals address the first task. Because of the second task
% a return value |FALSE| means that the standard requires you to do some
% special action. |TRUE| means that you can ignore this
% requirement.\footnote{One could also make the logic the
% other way round---there are arguments for both---but I had to decide.}
%
% In most cases it only matters if a requirement is in the standard,
% for example |Catalog_no_OCProperties| means \enquote{don't use |/OCProperties|
% in the catalog}. For a small number of requirements it is also needed to
% test a user value against a standard value. For example, |named_actions|
% restricts the allowed named actions in an annotation of subtype |/Named|,
% in this case it is needed to check not only if the requirement is
% in the standard but also if the user value is in the allowed list.
%
% \begin{function}[EXP,pTF]{\pdfmeta_standard_verify:n}
% \begin{syntax}
% \cs{pdfmeta_standard_verify:n}\Arg{requirement}
% \end{syntax}
%
% This checks if \meta{requirement} is listed in the standard.
% |FALSE| as result means that the requirement is in the standard and
% that probably some special action is required---which one depends
% on the requirement, see the descriptions below.
% |TRUE| means that the requirement is not there and so no special
% action is needed.
% This check can be used for simple requirements where neither
% a user nor a standard value is of importance.
% \end{function}
%
% \begin{function}[TF]{\pdfmeta_standard_verify:nn}
% \begin{syntax}
% \cs{pdfmeta_standard_verify:nn}\Arg{requirement}\Arg{value}
% \end{syntax}
%
% This checks if \meta{requirement} is listed in the standard,
% if yes it tries to find a predefined test handler for
% the requirement and passes \meta{value} and the value recorded
% in the standard to it. The handler returns |FALSE| if some special
% action is needed (e.g. if \meta{value} violates the rule)
% and |TRUE| if no special action is needed. If no handler exists
% this commands works like \cs{pdfmeta_standard_verify:n}.
% \end{function}
%
% In some cases one needs to query the value in the standard,
% e.g. to correct a wrong minimal PDF version you need to know
% which version is required by |min_pdf_version|.
% For this two commands to access the value are provided:
%
% \begin{function}[EXP]{\pdfmeta_standard_item:n}
% \begin{syntax}
% \cs{pdfmeta_standard_item:n}\Arg{requirement}
% \end{syntax}
% This retrieves the value of \meta{requirement} and leaves it in the input.
% If the requirement isn't in the standard the result is empty,
% that means that requirements not in the standard and
% requirement without values can not be distinguished here.
% \end{function}
%
%
% \begin{function}{\pdfmeta_standard_get:nN}
% \begin{syntax}
% \cs{pdfmeta_standard_get:nN}\Arg{requirement} \meta{tl var}
% \end{syntax}
% This retrieves the value of \meta{requirement} and stores
% it in the \meta{token list variable}.
% If the \meta{requirement} is not found the special
% value |\q_no_value| is used.
% The \meta{token list variable} is assigned locally.
% \end{function}
%
%
% The following describe the requirements which can be currently tested.
% Requirements with a value should use \cs{pdfmeta_standard_verify:nn}
% or \cs{pdfmeta_standard_verify:nnN} to test a local value against the standard.
% The rule numbers refer to \url{https://docs.verapdf.org/validation/pdfa-part1/}
%
% \subsubsection{Simple tests without handler}
%
% \begin{description}
%
% \item[|outputintent_A|] requires to embed a color profile and
% reference it in a /Outputintent and that all output intents reference
% the same colorprofile. The value stores the subtype.
% {\em This requirement is detected and fulfilled by \pkg{l3pdfmeta} if the
% provided interface in \cs{DocumentMetadata} is used, see below}.
%
% \item[|annot_flags|] in annotations the |Print| flag should be true,
% |Hidden|, |Invisible|, |NoView| should be false.
% {\em This requirement is detected and set by \pkg{l3pdfmeta} for annotations
% created with the \pkg{l3pdfannot}.
% A new check is only needed if the flags are changed
% or if links are created by other means.}
%
% \item[|no_encryption|] don't encrypt
% \item[|no_external_content|] no |/F|, |/FFilter|, or |/FDecodeParms|
% in stream dictionaries
% \item[|no_embed_content|] no |/EF| key in filespec, no |/Type/EmbeddedFiles|.
% \emph{This will be checked in future by \pkg{l3pdffiles}
% for the files it embeds.}
% The restrictment is set for only PDF/A-1b.
% PDF/A-2b and PDF/A3-b lifted this restriction: PDF/A-2b allows
% to embed other PDF documents conforming to either PDF/A-1 or PDF/A-2,
% and PDF/A-3 allows any embedded files. I don't see a way to test the
% PDF/A-2b requirement so currently it will simply allow everything. Perhaps
% a test for at least the PDF-format will be added in future.
% \item[|Catalog_no_OCProperties|] don't add |/OCProperties| to the catalog
% {\em l3pdfmeta removes this entry at the end of the document}
% \item[|Catalog_OCProperties_no_AS|]
% do not use |/AS| optional content configuration dictionary.
% \item[|Catalog_EmbeddedFiles|] ensure that an |EmbeddedFiles| name tree is
% in the catalog. This is required for PDF/A-4f.
% \item[|annot_widget_no_AA|] (rule 6.6.2-1)
% no AA dictionary in widget annotation,
% this will e.g. be checked by the new hyperref driver.
% \item[|annot_widget_no_A_AA|] (rule 6.9-2) no A and AA dictionary in widget.
% \item[|form_no_AA|] (6.9-3) no /AA dictionary in form field
% \item[|unicode|] that is set in the U-standards, A-2u and A-3u and means that
% every text should be in unicode. This is not something that can be enforced or
% tested from TeX, but in a current LaTeX normally ToUnicode are set for all fonts.
% \item[|tagged|] that is set in A-2a and A-3a and means that the pdf must be
% tagged. This is currently neither tested not enforced somewhere.
% \item[|no_CharSet|] CharSet is deprecated is pdf 2.0 and should not
% be used in A-4. l3pdfmeta will therefore suppress it for the
% engines pdftex and luatex (the other engines have no suitable option)
% \item[|omit_CID|] This avoids with PDF/A-2 and newer a failure
% because of with missing CID identifications
% (e.g. from rule ISO 19005-2:2011, Clause: 6.2.11.4.2)
% It has only with luatex an effect.
% \item[|Trailer_no_Info|] The \texttt{Info} dictionary
% has been deprecated since quite some time. Metadata should be set with
% XMP-data instead. In PDF A-4 now the \texttt{Info} dictionary
% shall not be present in the trailer dictionary at all
% (unless there exists a PieceInfo entry in the Catalog). And if it is present
% it should only contain the \texttt{/ModDate} entry. In
% texlive 2023 the engines pdftex and luatex have primitives
% to suppress the dictionary
% and l3pdfmeta will make use of it.
% \end{description}
%
% \subsubsection{Tests with values and special handlers}
%
% \begin{description}
%
% \item[|min_pdf_version|] stores the minimal PDF version needed for
% a standard.
% It should be checked against the current PDF version (\cs{pdf_version:}).
% A failure means that the version should be changed.
% Currently there is only one hard requirement which leads to a failure in
% a validator like verapdf: The A-4 standard should use PDF 2.0.
% As PDF A-1 is based on PDF 1.4 and PDF A-2 and A-3 are based
% on PDF 1.7 \pkg{l3pdfmeta} also sets these versions also as requirements.
% These requirements are checked by \pkg{l3pdfmeta} when the version is set with
% \cs{DocumentMetadata} and a warning is issued (but the version is
% not changed). More checks are only needed if the version is changed later.
%
%
% \item[|max_pdf_version|] stores the maximal PDF version.
% It should be checked against the current PDF version (\cs{pdf_version:}).
% A failure means that the version should be changed.
% The check is currently relevant only for the A-1 to A-3 standards:
% PDF 2.0 leads to a failure in a validator like verapdf so the maximal
% version should be PDF 1.7.
% This requirement is checked by \pkg{l3pdfmeta} when the version is set with
% \cs{DocumentMetadata} and a warning is issued (but the version is
% not changed). More checks are only needed if the version is changed later.
%
% \item[|named_actions|] this requirement restricts the list of
% allowed named actions to |NextPage|, |PrevPage|, |FirstPage|, |LastPage|.
% The check should supply the named action without slash
% (e.g. |View| (failure) or |NextPage| (pass)).
%
% \item[|annot_action_A|] (rule 6.6.1-1) this requirement restricts
% the allowed subtypes of the
% |/A| dictionary of an action. The check should supply the user
% subtype without slash e.g. as |GoTo| (pass) or |Movie| (failure).
% \end{description}
%
% \subsection{Colorprofiles and OutputIntent}
%
% The pdf/A standards require that a color profile is embedded and
% referenced in the catalog in the |/OutputIntent| array.
%
% The problem is that the pdf/A standards also require, that if the PDF has more then
% one entry in the |/OutputIntent| array (which is allowed), their |/DestOutputProfile|
% should all reference the same color profile\footnote{see rule 6.2.2-2 at
% \url{https://docs.verapdf.org/validation/pdfa-part1/}}.
%
% Enforcing this fully is impossible if entries are added manually by users or
% packages with |\pdfmanagement_add:nnn {Catalog}{OutputIntents}{|\meta{object reference}|}|
% as it is difficult to inspect and remove entries from the |/OutputIntent| array.
%
% So we provide a dedicated interface to avoid the need of manual
% user settings and allow the code to handle the requirements of the standard.
% The interface doesn't handle yet all finer points for PDF/X standards, e.g.
% named profiles, it is meant as a starting point to get at least PDF/A validation
% here.
%
% \begin{NOTE}{UF}
% The interface has to handle the following points:
% \begin{itemize}
% \item We have to assume that some documents wants to add more
% than one OutputIntent with varying subtypes.
% \item While currently only |/GTS_PDFA1| and |/GTS_PDFX| seem to
% be relevant, we have to assume that the list of subtypes is open.
% \item But we can imho assume that every subtype is there at most once.
% \item The referenced color profile can be used also other means, e.g. an /ICCBased
% color space. We must avoid that it is embedded twice in this case.
% This will need coordination with l3color. It should probably provide the
% code to embed the profile.
% \item While we can predeclare some standard icc-profiles, an interface to
% setup more is needed. This is currently not handled, as it needs
% coordination with a setup in l3color too.
% \item The implementation doesn't really handle yet all finer points for pdf/X
% see \url{tn0002_color_in_pdfa-1_2008-03-141.pdf}
% \end{itemize}
% \end{NOTE}
% The interface looks like this
%
% \begin{verbatim}
% \DocumentMetadata
% {
% %other options for example pdfstandard
% colorprofiles=
% {
% A = sRGB.icc, %or a or longer GTS_PDFA1 = sRGB.icc
% X = FOGRA39L_coated.icc, % or x or longer GTS_PDFX
% ISO_PDFE1 = whatever.icc
% }
%
% }
% \end{verbatim}
%
% |sRGB.icc| and |FOGRA39L_coated.icc| (from the \pkg{colorprofiles} package
% are predefined and will work directly\footnote{The \texttt{dvips} route
% will require that \texttt{ps2pdf} is called with \texttt{-dNOSAFER},
% and that the color profiles are in the current folder as \texttt{ps2pdf} doesn't
% use \texttt{kpathsea} to find them.}. |whatever.icc| will need special setup in
% the document preamble to declare the values for the
% |OutputIntent| dictionary, but the interface hasn't be added yet. This will be
% decided later.
%
%
% If an A-standard is detected or set which requires
% that all |/DestOutputProfile| reference the same
% color profile, the setting is changed to the equivalent of
%
% \begin{verbatim}
% \DocumentMetadata
% {
% %other options
% pdfstandard=A-2b,
% colorprofiles=
% {
% A = sRGB.icc, %or longer GTS_PDFA1 = sRGB.icc
% X = sRGB.icc,
% ISO_PDFE1 = sRGB.icc
% }
%
% }
% \end{verbatim}
%
% The pdf/A standards will use |A=sRGB.icc| by default, so this doesn't
% need to be declared explicitly.
%
% \subsection{Regression tests}
% When doing regression tests one has to set various metadata to fix values.
% \begin{function}{\pdfmeta_set_regression_data:}
% \begin{syntax}
% \cs{pdfmeta_set_regression_data:}
% \end{syntax}
% This sets various metadata to values needed by the \LaTeX{}
% regression tests.
% It also sets the seed for random functions.
% If a current l3backend is used and \cs{c_sys_timestamp_str} is available,
% the command does not set dates, but
% assumes that the environment variable \verb+SOURCE_DATE_EPOCH+ is used.
% \end{function}
%
% \section{XMP-metadata}
% XMP-metadata are data in XML format embedded in a stream
% inside the PDF and referenced from the |/Catalog|.
% Such a XMP-metadata stream contains various document related data,
% is required by various PDF standards and can replace
% or extend the data in the |/Info| dictionary.
% In PDF 2.0 the /Info dictionary is actually deprecated
% and only XMP-metadata should be used for the metadata of the PDF.
%
% The content of a XMP-metadata stream is not a fix set of data.
% Typically fields like the title, the author, the language and keywords will
% be there. But standards like e.g. ZUGferd (a standard for electronic bills)
% can require to add more fields, and it is also possible
% to define and add purely local data.
%
% In some workflows (e.g. if dvips + ghostscript is used)
% a XMP-metadata stream with some standard content is added automatically by
% the backend, but normally it must be created with code.
%
% For this task the packages \pkg{hyperxmp}, \pkg{xmpincl} or \pkg{pdfx}
% (which uses \pkg{xmpincl})
% can be used, but all these packages are not compatible with the
% pdfmanagement\footnote{\pkg{hyperxmp} was partly compatible as the pdfmanagement
% contained some patches for it, but these patches have now been removed.}.
% The following code is meant as replacement for these packages.
%
% \pkg{hyperxmp} uses |\hypersetup| as user interface to enter the XMP-metadata.
% This syntax is also supported by the new code\footnote{with a number of changes which
% are discussed in more details below}, so if \pkg{hyperref}
% has been loaded, e.g. |pdftitle=xxx| can be used to set the title.
% But XMP-metadata shouldn't require to use \pkg{hyperref} and in a future
% version an interface without \pkg{hyperref} will be added.
%
% There is currently no full user interface command to extend the XMP-metadata
% with for example the code needed for ZUGferd,
% they will be added in a second step.
%
% \subsection{Debug option}
%
% The resulting XMP-packet can be written to an external file by activating a debug
% option
%
% \begin{verbatim}
% \DocumentMetadata{debug={xmp-export}}
% %or
% \DocumentMetadata{debug={xmp-export=true}}
% %or
% \DocumentMetadata{debug={xmp-export=filename}}
% \end{verbatim}
%
% By default the data are written to \verb+\jobname.xmpi+, if a \texttt{filename} is
% given, then \verb+filename.xmpi+ is used instead. \verb+xmp-export=false+ deactivates
% the export.
%
% \subsection{Encoding and escaping}
%
% XMP-metadata are stored as UTF-8 in the PDF. This mean if you open a PDF in an editor
% a content like \enquote{grüße} will be shown probably as \enquote{grüße}.
% As XMP-metadata are in XML format special chars like |<|, |>|, and |&| and
% \texttt{\textquotestraightdblbase} must be escaped.
%
% \pkg{hyperxmp} hooks into \pkg{hyperref} and passes all
% input through |\pdfstringdef|. This means a word like
% \enquote{hallo} is first converted by |\pdfstringdef| into\\
% |\376\377\000h\000a\000l\000l\000o| and then back to UTF-8 by
% \pkg{hyperxmp} and in the course of this action
% the XML-escapings are applied.
% \pkg{pdfx} uses |\pdfstringdef| together with
% a special fontencoding (similar to the PU-encoding of \pkg{hyperref})
% for a similar aim.
% The code here is based on |\text_purify:n| followed by a few replacements for the
% escaping.
%
% User data should normally be declared in the preamble (or even in the
% |\DocumentMetadata| command), and consist of rather simple text; |&| can be entered
% as |\&| (but directly |&| will normally work too),
% babel shorthands should not be used. Some data are interpreted as comma lists,
% in this cases commas which are part of the text should be protected by braces.
% In some cases a text in brackets like |[en]| is interpreted as language tag,
% if they are part of a text they should be protected by braces too.
% XMP-metadata are stored uncompressed in the PDF so if you are unsure
% if a value has
% been passed correctly, open the PDF in an editor, copy the whole block and
% pass it to a validator, e.g. \url{https://www.w3.org/RDF/Validator/}.
%
% \subsection{User interfaces and differences to \pkg{hyperxmp} }
%
% \subsubsection{PDF standards}
%
% The \pkg{hyperxmp}/\pkg{hyperref} keys |pdfapart|, |pdfaconformance|, |pdfuapart|,
% |pdfxstandard| and |pdfa| are ignored by this code. Standards must be set with the
% |pdfstandard| key of |\DocumentMetadata|. This key can be used more than once,
% e.g. \\
% |pdfstandard=A-2b,pdfstandard=X-4,pdfstandard=UA-1|.
% \\ Note that using these
% keys doesn't mean that the document actually follows the standard. \LaTeX{}
% can neither ensure nor check all requirements of a standard, and not everything
% it can do theoretically has already been implemented.
% When setting an |A| standard, the code will e.g. insert a color profile and
% warn if the PDF version doesn't fit, but |X| and |UA| currently only
% adds the relevant declarations to the XMP-metadata.
% It is up to the author to ensure and validate
% that the document actually follows the standard.
%
% \subsubsection{Declarations}
% PDF knows beside standards also a more generic method to declare conformance
% to some specification by adding a declaration,
% see \url{https://pdfa.org/wp-content/uploads/2019/09/PDF-Declarations.pdf}).
% Such declarations can be added as a simple url which identify the specification or
% with additional details regarding date and credentials. An example would be
%
% \begin{verbatim}
% \DocumentMetadata{}
% \documentclass{article}
% \ExplSyntaxOn
% \pdfmeta_xmp_add_declaration:e {https://pdfa.org/declarations\c_hash_str iso32005}
% \pdfmeta_xmp_add_declaration:ennnn
% {https://pdfa.org/declarations\c_hash_str wcag21A}{}{2023-11-20}{}{}
% \pdfmeta_xmp_add_declaration:nnnnn
% {https://github.com/TikZlings/no-duck-harmed}
% {Ulrike~Fischer}{2023-11-20}{Bär}{https://github.com/u-fischer/bearwear}
% \pdfmeta_xmp_add_declaration:nnnnn
% {https://github.com/TikZlings/no-duck-harmed}
% {Ulrike~Fischer}{2023-11-20}{Paulo}{https://github.com/cereda/sillypage}
% \ExplSyntaxOff
% \begin{document}
% text
% \end{document}
%
% \end{verbatim}
%
% \subsubsection{Dates}
% \begin{itemize}
% \item
% The dates |xmp:CreateDate|, |xmp:ModifyDate|, |xmp:MetadataDate|
% are normally set automatically to the current date/time when the compilation
% started. If they should be changed
% (e.g. for regression tests to produce reproducible documents) they can
% be set with |\hypersetup| with the keys
% |pdfcreationdate|, |pdfmoddate| and |pdfmetadate|.
%
% \begin{verbatim}
% \hypersetup{pdfcreationdate=D:20010101205959-00'00'}
% \end{verbatim}
%
% The format should be a full date/time in PDF format, so one of these (naturally
% the numbers can change):
% \begin{verbatim}
% D:20010101205959-00'00'
% D:20010101205959+00'00'
% D:20010101205959Z
% \end{verbatim}
%
% \item The date |dc:date| is an \enquote{author date} and so
% should normally be set to the same date as
% given by |\date|. This can be done with the key |pdfdate|\footnote{Extracting
% the value automatically from \texttt{\textbackslash date} is not really possible
% as authors often put formatting or additional info in this command.}.
% The value should be a date in ISO 8601 format:
%
% \begin{verbatim}
% 2022 %year
% 2022-09-04 %year-month-day
% 2022-09-04T19:20 %year-month-day hour:minutes
% 2022-09-04T19:20:30 % year-month-day hour:minutes:second
% 2022-09-04T19:20:30.45 % year-month-day hour:minutes:second with fraction
% 2022-09-04T19:20+01:00 % with time zone designator
% 2022-09-04T19:20-02:00 % time zone designator
% 2022-09-04T19:20Z % time zone designator
% \end{verbatim}
%
% It is also possible to give the date as a full date in PDF format as described
% above. If not set the current date/time is used.
%\end{itemize}
%
% \subsection{Language}
% The code assumes that a default language is always declared
% (as the pdfmanagement gives the |/Lang| entry in the catalog a default value)
% This language can be changed with the |\DocumentMetadata| key |lang| (preferred)
% but the \pkg{hyperref} key |pdflang| is also honored. Its value should be a
% simple language tag like |de| or |de-DE|.
%
% The main language is also used in a number of attributes in the XMP data,
% if wanted a different language can be set here with the
% \pkg{hyperref}/\pkg{hyperxmp} key |pdfmetalang|.
%
% A number of entries can be given a language tag. Such a language is
% given by using an \enquote{optional argument} before the text:
%
% \begin{verbatim}
% \hypersetup{pdftitle={[en]english,[de]deutsch}}
% \hypersetup{pdfsubtitle={[en]subtitle in english}}
% \end{verbatim}
%
% \subsection{Rights}
% The keys |pdfcopyright| and |pdflicenseurl| work similar as in \pkg{hyperxmp}.
% But differently to \pkg{hyperxmp} the code doesn't set the |xmpRights:Marked|
% property, as I have some doubts that one deduce its value simply
% by checking if the other keys have been used; if needed it can be added by
% using one of these settings (true means with copyright, false means public domain).
% \begin{verbatim}
% \AddToDocumentProperties[document]{copyright}{true}
% \AddToDocumentProperties[document]{copyright}{false}
% \end{verbatim}
%
% \subsection{PDF related data}
% The PDF producer is for all engines by default built from the engine
% name and the engine version and doesn't use the banners as with \pkg{hyperxmp}
% and \pkg{pdfx}, it can be set manually with the |pdfproducer| key.
%
% The key |pdftrapped| is ignored. |Trapped| is deprecated in PDF 2.0.
%
% \subsection{Document data}
%
% The authors should be given with the |pdfauthor| key, separated by commas. If an
% author contains a comma, protect/hide it by a brace.
% \subsection{User commands}
% The XMP-meta data are added automatically. This can be suppressed with the
% |\DocumentMetadata| key |xmp|.
%
% \begin{function}{\pdfmeta_xmp_add:n}
% \begin{syntax}
% \cs{pdfmeta_xmp_add:n}\Arg{XML}
% \end{syntax}
% With this command additional XML code can be added to the Metadata.
% The content is added unchanged, and not sanitized.
% \end{function}
% \begin{function}{\pdfmeta_xmp_xmlns_new:nn}
% \begin{syntax}
% \cs{pdfmeta_xmp_xmlns_new:nn}\Arg{prefix}\Arg{uri}
% \end{syntax}
% With this command a xmlns name space can be added. The \meta{uri}
% argument is expanded, a hash can be input with |\c_hash_str|.
% \end{function}
%
% With the two following commands PDF declarations can be added to the XMP metadata
% (see \url{https://pdfa.org/wp-content/uploads/2019/09/PDF-Declarations.pdf}).
% \begin{function}{\pdfmeta_xmp_add_declaration:n,\pdfmeta_xmp_add_declaration:e}
% \begin{syntax}
% \cs{pdfmeta_xmp_add_declaration:n}\Arg{uri}
% \end{syntax}
% This add a PDF declaration with the required |conformsTo| property to the XMP metadata.
% \meta{uri} should not be empty and is a URI specifying
% the standard or profile referred to by the PDF
% Declaration. If the uri contains a hash, use \cs{c_hash_str} to escape it
% and use the \texttt{e} variant to expand it.
% \end{function}
%
% \begin{function}{\pdfmeta_xmp_add_declaration:nnnnn,
% \pdfmeta_xmp_add_declaration:ennnn,
% \pdfmeta_xmp_add_declaration:eeenn}
% \begin{syntax}
% \cs{pdfmeta_xmp_add_declaration:nnnnn}\Arg{uri}\Arg{By}\Arg{Date}\Arg{Credentials}\Arg{Report}
% \end{syntax}
% This add a PDF declaration to the XMP metadata similar
% to \cs{pdfmeta_xmp_add_declaration:n}.
% With \meta{By}, \meta{Date}, \meta{Credentials}, \meta{Report} the optional
% fields |claimBy| (text), |claimDate| (iso date), |claimCredentials| (text) and
% |claimReport| (uri) of the |claimData| property can be given.
% If \cs{pdfmeta_xmp_add_declaration:nnnnn} is used twice with the same \meta{uri}
% argument the |claimData| are concatenated. There is no check if the |claimData| are identical.
% \end{function}
%
% The following two commands can be used to extend the schema declarations in
% the XMP metadata. This is for example needed to implement a standard like ZUGferd/Factur X
% for invoices. A schema declaration should be added only once but as this task
% is probably not needed frequently only light guards are there to avoid duplicated entries.
%
% \begin{function}{\pdfmeta_xmp_schema_new:nnn}
% \begin{syntax}
% \cs{pdfmeta_xmp_schema_new:nnn}\Arg{text}\Arg{prefix}\Arg{uri}
% \end{syntax}
% \meta{text} is some string describing the schema, e.g. |PDF/A~Identification~Schema|,
% \meta{prefix} is the unique prefix used by the schema. This prefix
% must be declared first with |\pdfmeta_xmp_xmlns_new:nn|. If a schema with this prefix
% has already been declared, it will currently be ignored with a warning.
% The \meta{uri} is expanded, so a
% hash can for example be given as |\c_hash_str|.
% \end{function}
% \begin{function}{\pdfmeta_xmp_property_new:nnnnn}
% \begin{syntax}
% \cs{pdfmeta_xmp_property_new:nnnnn}\Arg{schema prefix}\Arg{name}\Arg{type}\Arg{category}\Arg{description}
% \end{syntax}
% If the new property already exists in the schema
% (as identified by the combination of \meta{schema prefix}
% and \meta{name} the property is silently ignore.
% \meta{schema prefix} is the prefix declared with the previous command.
% schema, e.g. |PDF/A~Identification~Schema|,
% \meta{name} is a short string that identifies the property, e.g. |xmpMM| or |year|. It must be
% unique in the properties of a schema. \meta{type} is e.g. |URI| or |Integer| or |Text|,
% \meta{category} is e.g. |internal| or |external|, \meta{description} is a free description string.
% \end{function}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3pdfmeta} implementation}
%
% \begin{macrocode}
%<@@=pdfmeta>
%<*header>
\ProvidesExplPackage{l3pdfmeta}{2024-10-27}{0.96n}
{PDF-Standards---LaTeX PDF management testphase bundle}
%</header>
% \end{macrocode}
% Message for unknown standards
% \begin{macrocode}
%<*package>
\msg_new:nnn {pdf }{unknown-standard}{The~standard~'#1'~is~unknown~and~has~been~ignored}
% \end{macrocode}
% Message for not fitting pdf version
% \begin{macrocode}
\msg_new:nnn {pdf }{wrong-pdfversion}
{PDF~version~#1~is~too~#2~for~standard~'#3'.}
% \end{macrocode}
% \begin{variable}{\l_@@_tmpa_tl,\l_@@_tmpb_tl,\l_@@_tmpa_str,
% \g_@@tmpa_str,\l_@@_tmpa_seq,\l_@@_tmpb_seq}
% \begin{macrocode}
\tl_new:N \l_@@_tmpa_tl
\tl_new:N \l_@@_tmpb_tl
\str_new:N \l_@@_tmpa_str
\str_new:N \g_@@_tmpa_str
\seq_new:N \l_@@_tmpa_seq
\seq_new:N \l_@@_tmpb_seq
% \end{macrocode}
% \end{variable}
% \subsection{Standards (work in progress)}
% \subsubsection{Tools and tests}
% This internal property will contain for now the settings for the document.
% \begin{variable}{\g_@@_standard_prop}
% \begin{macrocode}
\prop_new:N \g_@@_standard_prop
% \end{macrocode}
% \end{variable}
% \subsubsection{Functions to check a requirement}
% At first two commands to get the standard value if needed:
% \begin{macro}[EXP]{\pdfmeta_standard_item:n}
% \begin{macrocode}
\cs_new:Npn \pdfmeta_standard_item:n #1
{
\prop_item:Nn \g_@@_standard_prop {#1}
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\pdfmeta_standard_get:nN}
% \begin{macrocode}
\cs_new_protected:Npn \pdfmeta_standard_get:nN #1 #2
{
\prop_get:NnN \g_@@_standard_prop {#1} #2
}
% \end{macrocode}
% \end{macro}
% Now two functions to check the requirement. A simple and one value/handler based.
% \begin{macro}[pTF]{\pdfmeta_standard_verify:n}
% This is a simple test is the requirement is in the prop.
% \begin{macrocode}
\prg_new_conditional:Npnn \pdfmeta_standard_verify:n #1 {T,F,TF}
{
\prop_if_in:NnTF \g_@@_standard_prop {#1}
{
\prg_return_false:
}
{
\prg_return_true:
}
}
% \end{macrocode}
% \end{macro}
% \begin{macro}[TF]{\pdfmeta_standard_verify:nn}
% This allows to test against a user value. It calls a test handler if this
% exists and passes the user and the standard value to it. The test
% handler should return true or false.
% \begin{macrocode}
\prg_new_protected_conditional:Npnn \pdfmeta_standard_verify:nn #1 #2 {T,F,TF}
{
\prop_if_in:NnTF \g_@@_standard_prop {#1}
{
\cs_if_exist:cTF {@@_standard_verify_handler_#1:nn}
{
\exp_args:Nnne
\use:c
{@@_standard_verify_handler_#1:nn}
{ #2 }
{ \prop_item:Nn \g_@@_standard_prop {#1} }
}
{
\prg_return_false:
}
}
{
\prg_return_true:
}
}
% \end{macrocode}
% \end{macro}
%
% Now we setup a number of handlers.
%
% The first actually ignores the user values and tests against the
% current pdf version. If this is smaller than the minimum we report a failure.
% |#1| is the user value, |#2| the reference value from the standard.
% \begin{macro}{\@@_standard_verify_handler_min_pdf_version:nn}
% \begin{macrocode}
%
\cs_new_protected:Npn \@@_standard_verify_handler_min_pdf_version:nn #1 #2
{
\pdf_version_compare:NnTF <
{ #2 }
{\prg_return_false:}
{\prg_return_true:}
}
% \end{macrocode}
% \end{macro}
% The next is the counter part and checks that the version is not to high
% \begin{macro}{\@@_standard_verify_handler_max_pdf_version:nn}
% \begin{macrocode}
%
\cs_new_protected:Npn \@@_standard_verify_handler_max_pdf_version:nn #1 #2
{
\pdf_version_compare:NnTF >
{ #2 }
{\prg_return_false:}
{\prg_return_true:}
}
% \end{macrocode}
% \end{macro}
% The next checks if the user value is in the list and returns a failure if not.
% \begin{macro}{\@@_standard_verify_handler_named_actions:nn}
% \begin{macrocode}
\cs_new_protected:Npn \@@_standard_verify_handler_named_actions:nn #1 #2
{
\tl_if_in:nnTF { #2 }{ #1 }
{\prg_return_true:}
{\prg_return_false:}
}
% \end{macrocode}
% \end{macro}
% The next checks if the user value is in the list and returns a failure if not.
% \begin{macro}{\@@_standard_verify_handler_annot_action_A:nn}
% \begin{macrocode}
\cs_new_protected:Npn \@@_standard_verify_handler_annot_action_A:nn #1 #2
{
\tl_if_in:nnTF { #2 }{ #1 }
{\prg_return_true:}
{\prg_return_false:}
}
% \end{macrocode}
% \end{macro}
% This check is probably not needed, but for completeness
% \begin{macro}{\@@_standard_verify_handler_outputintent_subtype:nn}
% \begin{macrocode}
\cs_new_protected:Npn \@@_standard_verify_handler_outputintent_subtype:nn #1 #2
{
\tl_if_eq:nnTF { #2 }{ #1 }
{\prg_return_true:}
{\prg_return_false:}
}
% \end{macrocode}
% \end{macro}
% \subsubsection{Enforcing requirements}
% A number of requirements can sensibly be enforced by us.
% \paragraph{Annot flags}
% pdf/A require a number of settings here, we store them in a command which
% can be added to the property of the standard:
% \begin{macrocode}
\cs_new_protected:Npn \@@_verify_pdfa_annot_flags:
{
\bitset_set_true:Nn \l_pdfannot_F_bitset {Print}
\bitset_set_false:Nn \l_pdfannot_F_bitset {Hidden}
\bitset_set_false:Nn \l_pdfannot_F_bitset {Invisible}
\bitset_set_false:Nn \l_pdfannot_F_bitset {NoView}
\pdfannot_dict_put:nnn {link/URI}{F}{ \bitset_to_arabic:N \l_pdfannot_F_bitset }
\pdfannot_dict_put:nnn {link/GoTo}{F}{ \bitset_to_arabic:N \l_pdfannot_F_bitset }
\pdfannot_dict_put:nnn {link/GoToR}{F}{ \bitset_to_arabic:N \l_pdfannot_F_bitset }
\pdfannot_dict_put:nnn {link/Launch}{F}{ \bitset_to_arabic:N \l_pdfannot_F_bitset }
\pdfannot_dict_put:nnn {link/Named}{F}{ \bitset_to_arabic:N \l_pdfannot_F_bitset }
}
% \end{macrocode}
% At begin document this should be checked:
% \begin{macrocode}
\hook_gput_code:nnn {begindocument} {pdf}
{
\pdfmeta_standard_verify:nF { annot_flags }
{ \@@_verify_pdfa_annot_flags: }
\pdfmeta_standard_verify:nF { Trailer_no_Info }
{ \__pdf_backend_omit_info:n {1} }
\pdfmeta_standard_verify:nF { no_CharSet }
{ \__pdf_backend_omit_charset:n {1} }
\pdfmeta_standard_verify:nF { omit_CID }
{ \__pdf_backend_omit_cidset:n {1} }
\pdfmeta_standard_verify:nnF { min_pdf_version }
{ \pdf_version: }
{ \msg_warning:nneee {pdf}{wrong-pdfversion}
{\pdf_version:}{low}
{
\pdfmeta_standard_item:n{type}
-
\pdfmeta_standard_item:n{level}
}
}
\pdfmeta_standard_verify:nnF { max_pdf_version }
{ \pdf_version: }
{ \msg_warning:nneee {pdf}{wrong-pdfversion}
{\pdf_version:}{high}
{
\pdfmeta_standard_item:n{type}
-
\pdfmeta_standard_item:n{level}
}
}
}
% \end{macrocode}
%
%
% \subsubsection{pdf/A}
% We use global properties so that follow up standards can be
% copied and then adjusted.
% Some note about requirements for more standard can
% be found in info/pdfstandard.tex.
% \begin{variable}{
% \g_@@_standard_pdf/A-1B_prop ,
% \g_@@_standard_pdf/A-2A_prop ,
% \g_@@_standard_pdf/A-2B_prop ,
% \g_@@_standard_pdf/A-2U_prop ,
% \g_@@_standard_pdf/A-3A_prop ,
% \g_@@_standard_pdf/A-3B_prop ,
% \g_@@_standard_pdf/A-3U_prop ,
% \g_@@_standard_pdf/A-4_prop ,
% }
% \begin{macrocode}
\prop_new:c { g_@@_standard_pdf/A-1B_prop }
\prop_gset_from_keyval:cn { g_@@_standard_pdf/A-1B_prop }
{
,name = pdf/A-1B
,type = A
,level = 1
,conformance = B
,year = 2005
,min_pdf_version = 1.4 %minimum
,max_pdf_version = 1.4 %minimum
,no_encryption =
,no_external_content = % no F, FFilter, or FDecodeParms in stream dicts
,no_embed_content = % no EF key in filespec, no /Type/EmbeddedFiles
,max_string_size = 65535
,max_array_size = 8191
,max_dict_size = 4095
,max_obj_num = 8388607
,max_nest_qQ = 28
,named_actions = {NextPage, PrevPage, FirstPage, LastPage}
,annot_flags =
%booleans. Only the existence of the key matter.
%If the entry is added it means a requirements is there
%(in most cases "don't use ...")
%
%===============
% Rule 6.1.13-1 CosDocument, isOptionalContentPresent == false
,Catalog_no_OCProperties =
% Rule 6.9-4 The AS key shall not appear in any optional content configuration dictionary.
% actually only starting with A-2 but doesn't harm here either
,Catalog_OCProperties_no_AS=
%===============
% Rule 6.6.1-1: PDAction, S == "GoTo" || S == "GoToR" || S == "Thread"
% || S == "URI" || S == "Named" || S == "SubmitForm"
% means: no /S/Launch, /S/Sound, /S/Movie, /S/ResetForm, /S/ImportData,
% /S/JavaScript, /S/Hide
,annot_action_A = {GoTo,GoToR,Thread,URI,Named,SubmitForm}
%===============
% Rule 6.6.2-1: PDAnnot, Subtype != "Widget" || AA_size == 0
% means: no AA dictionary
,annot_widget_no_AA =
%===============
% Rule 6.9-2: PDAnnot, Subtype != "Widget" || (A_size == 0 && AA_size == 0)
% (looks like a tightening of the previous rule)
,annot_widget_no_A_AA =
%===============
% Rule 6.9-1 PDAcroForm, NeedAppearances == null || NeedAppearances == false
,form_no_NeedAppearances =
%===============
%Rule 6.9-3 PDFormField, AA_size == 0
,form_no_AA =
%===============
% to be continued https://docs.verapdf.org/validation/pdfa-part1/
% - Outputintent/colorprofiles requirements
% an outputintent should be loaded and is unique.
,outputintent_A = {GTS_PDFA1}
% - no Alternates key in image dictionaries
% - no OPI, Ref, Subtype2 with PS key in xobjects
% - Interpolate = false in images
% - no TR, TR2 in ExtGstate
}
%A-2b ==============
\prop_new:c { g_@@_standard_pdf/A-2B_prop }
\prop_gset_eq:cc
{ g_@@_standard_pdf/A-2B_prop }
{ g_@@_standard_pdf/A-1B_prop }
\prop_gput:cnn
{ g_@@_standard_pdf/A-2B_prop }{name}{pdf/A-2B}
\prop_gput:cnn
{ g_@@_standard_pdf/A-2B_prop }{year}{2011}
\prop_gput:cnn
{ g_@@_standard_pdf/A-2B_prop }{level}{2}
% embedding files is allowed (with restrictions)
\prop_gremove:cn
{ g_@@_standard_pdf/A-2B_prop }
{ embed_content}
\prop_gput:cnn
{ g_@@_standard_pdf/A-2B_prop }{max_pdf_version}{1.7}
\prop_gput:cnn
{ g_@@_standard_pdf/A-2B_prop }{omit_CID}{}
% OCG layers are allowed (with restrictions)
\prop_gremove:cn
{ g_@@_standard_pdf/A-2B_prop }
{ Catalog_no_OCProperties }
%A-2u ==============
\prop_new:c { g_@@_standard_pdf/A-2U_prop }
\prop_gset_eq:cc
{ g_@@_standard_pdf/A-2U_prop }
{ g_@@_standard_pdf/A-2B_prop }
\prop_gput:cnn
{ g_@@_standard_pdf/A-2U_prop }{name}{pdf/A-2U}
\prop_gput:cnn
{ g_@@_standard_pdf/A-2U_prop }{conformance}{U}
\prop_gput:cnn
{ g_@@_standard_pdf/A-2U_prop }{unicode}{}
%A-2a ==============
\prop_new:c { g_@@_standard_pdf/A-2A_prop }
\prop_gset_eq:cc
{ g_@@_standard_pdf/A-2A_prop }
{ g_@@_standard_pdf/A-2B_prop }