summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/pdfmanagement-testphase/l3pdfmeta.dtx
blob: 73163f820a265f738b8927c421d6713e1885af0f (plain)
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
% \iffalse meta-comment
%
%% File: l3pdfmeta.dtx
%
% Copyright (C) 2018-2021 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>
\RequirePackage{pdfmanagement-testphase}
\DeclareDocumentMetadata{pdfstandard=A-2b}
\makeatletter
\declare@file@substitution{doc.sty}{doc-v3beta.sty}
\makeatother
\documentclass[full]{l3doc}
\usepackage{array,booktabs,hyperxmp}
\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:latex-team@latex-project.org}
%          {latex-team@latex-project.org}^^A
%    }^^A
% }
%
% \date{Version 0.95f, released 2021-06-29}
%
% \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.
%
% In future is will probably also contain
% code to setup XMP-metadata.
% Until then XMP-metadata can be added by one of two mutual incompatible packages:
% \pkg{hyperxmp} and \pkg{pdfx}. Both
% packages aren't yet compatible with the new PDF management,
% but for \pkg{hyperxmp} some patches are provided, so the basic functions works.
%
% \begin{NOTE}{UF}
% This module should not replace both packages.
% Regarding XMP-metadata its goal
% is to create a skeleton metadata stream, add some core default values
% and to define interfaces that allows other packages
% to add data to this metadata and so to extend them.
% The problems to solve here are
% \begin{itemize}
% \item which tree structure is sensible
% \item how to escape if needed the input (or which tools are needed to allow
% the users to correctly escape their input)
% \item how interface to input data should look
% \end{itemize}
% \end{NOTE}
%
% \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{DeclareDocumentMetadata} 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[|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.
% \end{description}
%
% \subsubsection{Tests with values and special handlers}
%
% \begin{description}
%
% \item[|min_pdf_version|]  stores the minimal PDF version.
%  It should be checked against the current PDF version (\cs{pdf_version:}).
%  A failure means that the version should be changed.
%  This check is done by \pkg{l3pdfmeta} when the version is set with
%  \cs{DeclareDocumentMetadata} so 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}
%  \DeclareDocumentMetadata
%    {
%      %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}
%  \DeclareDocumentMetadata
%    {
%      %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.
%
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3pdfmeta} implementation}
%
%    \begin{macrocode}
%<@@=pdfmeta>
%<*header>
\ProvidesExplPackage{l3pdfmeta}{2021-06-29}{0.95f}
  {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}
% \begin{variable}{\l_@@_tmpa_tl,\l_@@_tmpb_tl,\l_@@_tmpa_str}
%    \begin{macrocode}
\tl_new:N\l_@@_tmpa_tl
\tl_new:N\l_@@_tmpb_tl
\str_new:N \l_@@_tmpa_str
%    \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:Nnnx
            \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 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: }
  }
%    \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-2B_prop ,
%   \g_@@_standard_pdf/A-2U_prop ,
%   \g_@@_standard_pdf/A-3B_prop ,
%   \g_@@_standard_pdf/A-3U_prop ,
%   }
%     \begin{macrocode}
\prop_new:c { g_@@_standard_pdf/A-1B_prop }
\prop_set_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
    ,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.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}

%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-3b ==============
\prop_new:c { g_@@_standard_pdf/A-3B_prop }
\prop_gset_eq:cc
  { g_@@_standard_pdf/A-3B_prop }
  { g_@@_standard_pdf/A-2B_prop }
\prop_gput:cnn
  { g_@@_standard_pdf/A-3B_prop }{name}{pdf/A-3B}
\prop_gput:cnn
  { g_@@_standard_pdf/A-3B_prop }{year}{2012}
\prop_gput:cnn
  { g_@@_standard_pdf/A-3B_prop }{level}{3}
% embedding files is allowed (with restrictions)
\prop_gremove:cn
  { g_@@_standard_pdf/A-3B_prop }
  { embed_content}

%A-3u ==============
\prop_new:c { g_@@_standard_pdf/A-3U_prop }
\prop_gset_eq:cc
  { g_@@_standard_pdf/A-3U_prop }
  { g_@@_standard_pdf/A-3B_prop }
\prop_gput:cnn
  { g_@@_standard_pdf/A-3U_prop }{name}{pdf/A-3U}
\prop_gput:cnn
  { g_@@_standard_pdf/A-3U_prop }{conformance}{U}
\prop_gput:cnn
  { g_@@_standard_pdf/A-3U_prop }{unicode}{}
%    \end{macrocode}
% \end{variable}
%
% \subsubsection{Colorprofiles and Outputintents}
% The following provides a minimum of interface to add a color profile
% and an outputintent need for PDF/A for now. There will be need to extend it later,
% so we try for enough generality.
%
% Adding a profile and an intent is technically easy:
% \begin{enumerate}
% \item Embed the profile as stream with
% \begin{verbatim}
%  \pdf_object_unnamed_write:nn{fstream} {{/N~4}{XXX.icc}}
% \end{verbatim}
% \item Write a |/OutputIntent| dictionary for this
% \begin{verbatim}
% \pdf_object_unnamed_write:nx {dict}
%  {
%    /Type /OutputIntent
%    /S /GTS_PDFA1  % or GTS_PDFX or ISO_PDFE1 or ...
%    /DestOutputProfile \pdf_object_ref_last: % ref the color profile
%    /OutputConditionIdentifier ...
%    ... %more info
%  }
% \end{verbatim}
% \item Reference the dictionary in the catalog:
% \begin{verbatim}
% \pdfmanagement_add:nnx {Catalog}{OutputIntents}{\pdf_object_ref_last:}
% \end{verbatim}
% \end{enumerate}
% But we need to do a bit more work, to get the interface right.
% The object for the profile should be named, to allow l3color to reuse it
% if needed. And we need container to store the profiles, to handle the
% standard requirements.
%
% \begin{variable}{\g_@@_outputintents_prop}
% This variable will hold the profiles for the subtypes. We assume
% that every subtype has only only color profile.
%    \begin{macrocode}
\prop_new:N \g_@@_outputintents_prop
%    \end{macrocode}
% \end{variable}
% Some keys to fill the property.
%    \begin{macrocode}
\keys_define:nn { document / metadata }
  {
    colorprofiles .code:n =
     {
       \keys_set:nn { document / metadata / colorprofiles }{#1}
     }
  }
\keys_define:nn { document / metadata / colorprofiles }
 {
   ,A .code:n =
      {
        \tl_if_blank:nF {#1}
          {
            \prop_gput:Nnn \g_@@_outputintents_prop
             { GTS_PDFA1  } {#1}
          }
      }
   ,a .code:n =
      {
        \tl_if_blank:nF {#1}
          {
            \prop_gput:Nnn \g_@@_outputintents_prop
              { GTS_PDFA1  } {#1}
          }
      }
   ,X .code:n =
      {
        \tl_if_blank:nF {#1}
          {
             \prop_gput:Nnn \g_@@_outputintents_prop
              { GTS_PDFX  } {#1}
          }
      }
   ,x .code:n =
      {
        \tl_if_blank:nF {#1}
          {
            \prop_gput:Nnn \g_@@_outputintents_prop
              { GTS_PDFX  } {#1}
          }
      }
   ,unknown .code:n =
     {
       \tl_if_blank:nF {#1}
          {
           \exp_args:NNo
            \prop_gput:Nnn \g_@@_outputintents_prop
              { \l_keys_key_str  } {#1}
          }
     }
 }
%    \end{macrocode}
% At first we setup our two default profiles. This is internal as
% the public interface is still undecided.
%    \begin{macrocode}
\pdfdict_new:n   {l_pdfmeta/outputintent}
\pdfdict_put:nnn {l_pdfmeta/outputintent}
  {Type}{/OutputIntent}
\prop_const_from_keyval:cn { c_@@_colorprofile_sRGB.icc}
  {
    ,OutputConditionIdentifier=IEC~sRGB
    ,Info=IEC~61966-2.1~Default~RGB~colour~space~-~sRGB
    ,RegistryName=http://www.iec.ch
    ,N = 3
  }
\prop_const_from_keyval:cn { c_@@_colorprofile_FOGRA39L_coated.icc}
  {
    ,OutputConditionIdentifier=FOGRA39L~Coated
    ,Info={Offset~printing,~according~to~ISO~12647-2:2004/Amd~1,~OFCOM,~ %
           paper~type~1~or~2~=~coated~art,~115~g/m2,~tone~value~increase~
           curves~A~(CMY)~and~B~(K)}
    ,RegistryName=http://www.fogra.org
    ,N = 4
  }
%    \end{macrocode}
% \begin{macro}{\@@_embed_colorprofile:n,\@@_write_outputintent:nn}
% The commands embed the profile, and write the dictionary and add it to
% the catalog. The first command should perhaps be moved to l3color
% as it needs such profiles too. We used named objects so that we can
% check if the profile is already there. This is not full proof if pathes are
% used.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_embed_colorprofile:n #1%#1 file name
  {
    \pdf_object_if_exist:nF { __color_icc_ #1 }
      {
        \pdf_object_new:nn  { __color_icc_ #1 }{fstream}
        \pdf_object_write:nx { __color_icc_ #1 }
         {
           {/N\c_space_tl
             \prop_item:cn{c_@@_colorprofile_#1}{N}
           }
           {#1}
         }
      }
  }

\cs_new_protected:Npn \@@_write_outputintent:nn #1 #2 %#1 file name, #2 subtype
  {
    \group_begin:
     \pdfdict_put:nnx {l_pdfmeta/outputintent}{S}{/\str_convert_pdfname:n{#2}}
     \pdfdict_put:nnx {l_pdfmeta/outputintent}
       {DestOutputProfile}
       {\pdf_object_ref:n{ __color_icc_ #1 }}
     \clist_map_inline:nn { OutputConditionIdentifier, Info, RegistryName }
       {
         \prop_get:cnNT
          { c_@@_colorprofile_#1}
          { ##1 }
          \l_@@_tmpa_tl
          {
            \pdf_string_from_unicode:nVN {utf8/string}\l_@@_tmpa_tl\l_@@_tmpa_str
            \pdfdict_put:nnx
              {l_pdfmeta/outputintent}{##1}{\l_@@_tmpa_str}
          }
       }
     \pdf_object_unnamed_write:nx {dict}{\pdfdict_use:n {l_pdfmeta/outputintent} }
     \pdfmanagement_add:nnx {Catalog}{OutputIntents}{\pdf_object_ref_last:}
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
% Now the verifying code.
% If no requirement is set we simply loop over the property
%    \begin{macrocode}

\AddToHook{begindocument/end}
  {
    \pdfmeta_standard_verify:nTF {outputintent_A}
      {
         \prop_map_inline:Nn \g_@@_outputintents_prop
           {
             \@@_embed_colorprofile:n
               {#2}
             \@@_write_outputintent:nn
               {#2}
               {#1}
           }
      }
%    \end{macrocode}
% If an output intent is required for pdf/A we need to ensure, that the key of
% default subtype has a value, as default we take sRGB.icc.
% Then we loop but take always the same profile.
%    \begin{macrocode}
      {
         \exp_args:NNx
         \prop_if_in:NnF
           \g_@@_outputintents_prop
           { \pdfmeta_standard_item:n { outputintent_A } }
           {
             \exp_args:NNx
             \prop_gput:Nnn
               \g_@@_outputintents_prop
               { \pdfmeta_standard_item:n { outputintent_A } }
               { sRGB.icc }
           }
         \exp_args:NNx
         \prop_get:NnN
           \g_@@_outputintents_prop
           { \pdfmeta_standard_item:n { outputintent_A } }
           \l_@@_tmpb_tl
         \exp_args:NV \@@_embed_colorprofile:n \l_@@_tmpb_tl
         \prop_map_inline:Nn \g_@@_outputintents_prop
           {
             \exp_args:NV
             \@@_write_outputintent:nn
               \l_@@_tmpb_tl
               { #1 }
           }
       }
   }
%    \end{macrocode}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex