diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfmeta.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfmeta.dtx | 824 |
1 files changed, 824 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfmeta.dtx b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfmeta.dtx new file mode 100644 index 00000000000..a374e39fc7f --- /dev/null +++ b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfmeta.dtx @@ -0,0 +1,824 @@ +% \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.95a, released 2021-02-21} +% +% \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 +% \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-02-22} {0.95a} + {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-3B_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 + ,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} +% embedding files is allowed (with restrictions) +\prop_gremove:cn + { g_@@_standard_pdf/A-2B_prop } + { embed_content} + +%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-2B_prop }{year}{2012} +% embedding files is allowed (with restrictions) +\prop_gremove:cn + { g_@@_standard_pdf/A-3B_prop } + { embed_content} +% \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 |