diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfmanagement.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfmanagement.dtx | 1468 |
1 files changed, 1468 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfmanagement.dtx b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfmanagement.dtx new file mode 100644 index 00000000000..55514bd06c0 --- /dev/null +++ b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfmanagement.dtx @@ -0,0 +1,1468 @@ +% \iffalse meta-comment +% +%% File: l3pdfmanagement.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{tabularx} +\usepackage{array,booktabs,hyperxmp} +\hypersetup{pdfauthor=The LaTeX Project,pdftitle=l3pdfmanagement (LaTeX PDF management testphase bundle)} + +\providecommand\potentialclash{\noindent\llap{\dbend\ }} + +% Fixing footnotes in functions and variables: this should be in l3doc! +\newcommand\fixfootnote[2]{\footnotemark + \AddToHookNext{env/#1/after}{\footnotetext{#2}}} +\AddToHook{env/function/begin}{\def\footnote{\fixfootnote{function}}} +\AddToHook{env/variable/begin}{\def\footnote{\fixfootnote{variable}}} + +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% \title{^^A +% The \pkg{l3pdfmanagement} module\\ Managing central PDF resources ^^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-22} +% +% \maketitle +% \begin{documentation} +% +% \section{\pkg{l3pdfmanagement} documentation} +% When creating a pdf a number of objects, dictionaries and entries to +% central \enquote{core} dictionaries must be created. +% +% The commands in this module offer interfaces to this core PDF dictionaries +% They unify a number of primitives like the pdftex +% registers and commands \cs{pdfcatalog}, \cs{pdfpageattr}, +% \cs{pdfpagesattr}, \cs{pdfinfo}, \cs{pdfpageresources} +% and similar commands of the other backends in a backend independant way. +% +% The supported backends are pdflatex, lualatex, (x)dvipdfmx (latex, xelatex +% and---starting in texlive 2021--lualatex) +% and dvips with ps2pdf (not completely yet). dvips with distiller could work too +% but is untested. +% +% That the interfaces are backend independent doesn't mean that the results and even +% the compilation behavior is identical. The backends are too different to allow +% this. Some backends expand arguments e.g. in a \cs{special} while other don't. +% Some backends can insert a resource at the first compilation, while another uses +% the aux-file and a label and so needs at least two. Some backends create and +% manage resources automatically which must be managed manually by other backends. +% +% The dictionaries and resources handled by this module are inserted only +% once in a PDF or only once per page. Examples are the Catalog dictionary, +% the Info dictionary, the page resources. For these dictionaries and resources +% management by the \LaTeX{} kernel is necessary to avoid +% that packages overwrite settings from +% other packages which would lead to clashes and incompatibilities. +% It is therefore necessary that \emph{all} packages which want to add content to these +% dictionaries and resources use the interface provided by this module. +% +% As these dictionaries and resources are so central for the PDF format values to these +% dictionaries are always added globally. Through the interface values +% can be added (and in many cases also removed) by users and packages, +% but the actually writing of the +% dictionary entries and resources to the PDF is handled by +% the kernel code. +% +% The interface uses as main name to address the resources \emph{Paths} +% which follow the names and structure described in the PDF reference. This +% should make it easy to identify the names needed to insert a specific +% PDF resources with the new interfaces. +% All \emph{Paths} have names starting with an uppercase letter. +% +% The following tabular summarize the \emph{Paths} and which pdftex primitive they +% replace: +% +% \begin{tabular}{ll} +% Info & \cs{pdfinfo} \\ +% Catalog \& various subdictionaries & \cs{pdfcatalog} \\ +% Pages & \cs{pdfpagesattr} \\ +% Page, ThisPage & \cs{pdfpageattr} \\ +% Page/Resources/ExtGState & \cs{pdfpageresources} \\ +% Page/Resources/Shading & \cs{pdfpageresources} \\ +% Page/Resources/Pattern & \cs{pdfpageresources} \\ +% Page/Resources/ColorSpace & \cs{pdfpageresources} \\% +% \end{tabular} +% +% There is no \texttt{Page/Resources/Properties} dictionary in the list, +% because this dictionary is not filled directly, but +% managed through side effects when setting BDC-marks. +% +% \subsection{User Commands} +% To avoid problems with older documents the resource management of this +% module is not activated unconditionally. The values are pushed out to the +% dictionaries only if a boolean has been set to true. The state can be tested +% with a conditional. +% \begin{function}[EXP,pTF,added=2020-07-04] +% {\pdfmanagement_if_active:} +% This conditional tests if the resource management code is active. +% \end{function} +% +% \begin{function}[added = 2020-04-06] +% {\pdfmanagement_add:nnn,\pdfmanagement_add:nnx,\pdfmanagement_add:nxx} +% \begin{syntax} +% \cs{pdfmanagement_add:nnn} \Arg{resource path} \Arg{name} \Arg{value} +% \end{syntax} +% This function puts \Arg{name} \Arg{value} in the PDF resource described by +% the symbolic name \Arg{resource path}. Technically it stores it globally in an internal +% property lists and writes it later into the right PDF dictionary\footnote{Currently all +% resources are PDF dictionaries, so resource and dictionary mean the same.} +% Which values for \Arg{resource path} exist is described in the following. +% \Arg{name} should be a PDF Name without the starting slash. Like with all +% keys used in PDF dictionaries (see the l3pdfdict module) the name is escaped +% with \cs{str_convert_pdfname:n} when stored. +% \Arg{value} should be a valid PDF value for this Name in the +% target dictionary. +% +% +% The code works with all major engines but not necessarily +% in the same way. Most importantly +% \begin{itemize} +% \item The expansion behaviour of the backends can differ. Some backends expand a +% value always fully when writing to the PDF, with other backends command names +% could end as strings in the PDF. So one should neither rely on \Arg{name} +% \Arg{value} to be expanded nor not expanded by the backend commands. +% \item The number of compilations needed can differ between the engines and +% backends. Some engines have to use labels and the aux-file to setup +% the dictionaries and so need at least two compilations to put everything +% in place. +% \end{itemize} +% \end{function} +% +% \begin{function}[added = 2020-04-08] +% {\pdfmanagement_show:n } +% \begin{syntax} +% \cs{pdfmanagement_show:n} \Arg{resource path} +% \end{syntax} +% This shows the content of the dictionary targetted by +% \Arg{resource path} in the log and on the terminal if possible. +% +% It is not reliable for page resources as these are filled at shipout. +% +% It also doesn't show necessarly all the content. +% For example most backends add automatically +% entries to the Info dictionary. +% \end{function} +% +% \begin{function}[added = 2020-04-07] +% { +% \pdfmanagement_remove:nn, +% } +% \begin{syntax} +% \cs{pdfmanagement_remove:nn} \Arg{resource path} \Arg{name} +% \end{syntax} +% Removes |/|\meta{name} and its associated \meta{value} from the +% dictionary described with \Arg{resource path} +% The removal is global. +% If \meta{name} is not found no change occurs, +% \emph{i.e}~there is no need to test for the existence of a name before +% trying to remove it. +% Values from the special Catalog entries where the values are collected in arrays +% can't be removed (but should ever a use case appear it could be added). +% \end{function} +% +% \subsection{Description of the resource pathes} +% \subsubsection{Info: The Info dictionary} +% \begin{NOTE}{UF} +% path: Info +% The info dictionary is filled by e.g. \cs{pdfinfo}. Multiple appearances of +% \cs{pdfinfo} are concatenated, so one could end with multiple /Title or /Author entries. +% It is then viewer dependent which one is showed, so it is better to avoid this. +% We therefore setup a property which is filled and written to the info +% directory in one go. According to hyperref a few odd drivers (hvtex, dvipsone, dviwind) +% don't support arbitrary keys, but this should be handle elsewhere. +% As entries with empty content +% should be omitted we add a test. The string command should perhaps escape the argument, +% but for now we are assuming that the argument is pdf safe. +% hyperref writes to the info dictionary at the shipout of the first page -- +% probably to catch the case that \cs{title} is issued after the begin of the document. +% We are outputting at the last page -- this needs a second compilation but +% this is needed anyway. +% \end{NOTE} +% \potentialclash If the primitive commands of the engines are used too there will +% be double entries in the pdf (at least with the backend pdftex and luatex). +% How pdf viewer handles this is unpredictable. +% +% \begin{function} +% { +% pdfmanagement: Info +% } +% \begin{syntax} +% \cs{pdfmanagement_add:nnn} \{Info\} \Arg{name} \Arg{value} +% \end{syntax} +% Adds |/|\meta{name} and the \meta{value} to the Info dictionary. +% \meta{name} should be a PDF name without the leading slash, +% Like with all +% keys used in PDF dictionaries (see the l3pdfdict module) the name is escaped +% with \cs{str_convert_pdfname:n} when stored. +% \meta{value} should be a valid pdf value. Any escaping or (re)encoding must be +% done explicitly. +% If a \meta{name} is used twice, only the last \meta{value} +% set will be used. The Info dictionary is written at the end of the compilation, +% so values can be set at any time. +% The Info dictionary expects utf16be in the strings, so a conversion like this is +% normally sensible: +% \begin{verbatim} +% \str_set_convert:Nnnn \l_tmpa_str { Grüße }{ default } {utf16/string} +% \pdfmanagement_add:nnx {Info} {Title}{(\l_tmpa_str)} +% \end{verbatim} +% \end{function} +% +% The entries in Info dictionary are rather special as the engines/backends adds some +% core entries, and changing or removing these entries is not always possible. +% +% The special entries are +% \begin{description} +% \item[Producer] Added by all engines and backends. Removing the entry is only possible +% with luatex with |\pdfvariable suppressoptionalinfo 128|. Changing is possible +% with \cs{pdfmanagement_add:nnn} with the exception of dvips/pstopdf where the entry is +% always something like |GPL Ghostscript 9.53.3|. +% \item[Creator] Added by all engines and backends. Removal only possible in luatex by adding +% 16 to the bitset. Changing is possible with the management command. +% \item[CreationDate] Added by all engines and backends. With the exception of |dvips/ps2pdf| +% |SOURCE_DATE_EPOCH| is honored. With pdftex it is possible to suppress it with +% |\pdfinfoomitdate = 1|, and in luatex by adding 32 to the bitset. +% Changing is possible with the management command and will overwrite an epoch setting. +% \item[ModDate] Added by all engines and backends with the exception of xdvipdfmx. +% With the exception of |dvips/ps2pdf| |SOURCE_DATE_EPOCH| is honored. +% Suppressing it is possible in pdftex with +% |\pdfinfoomitdate = 1|, and in luatex by adding 64 to the bitset. Changing is possible with +% the management command. +% \item[Trapped] Added by pdftex and luatex. Removal only possible in +% luatex by adding 256 to the bitset. Changing (and adding in the other backends) is +% possible with the management command. +% \item[PTEX.Fullbanner] Added by pdftex and luatex. Removal possible in pdftex with +% |\pdfsuppressptexinfo-1|, in luatex by adding 512 to the bitset. +% Changing is not possible. +% \item[Title] Added by dvips/ps2pdf and set to |filename.dvi|. Removal is probably +% not possible, but it can be overwritten with the management command.% +% \end{description} +% \subsubsection{Pages: The \enquote{Pages} dictionary} +% \potentialclash As the content of this dictionary is written at the end it will +% in pdftex and luatex overwrite values added with the primitive commands (e.g. +% \cs{pdfpagesattr}. +% Package authors should use the management commands instead. + +% By using this path with the pdfmanagement interface, +% values can be added to the /Pages object. +% This replaces for example \cs{pdfpagesattr}. +% +% \begin{function}{pdfmanagement: Pages} +% \begin{syntax} +% \cs{pdfmanagement_add:nnn} \{Pages\} \Arg{name} \Arg{value} +% \end{syntax} +% +% Adds |/|\meta{name} \meta{value} to the |/Pages| dictionary. +% It is always stored globally. The content is written to the pdf +% at the end of the compilation, so values can be added, changed or +% removed until then. +% \meta{name} should be a valid pdf name without the leading slash, +% \meta{value} should be a valid pdf value. Any escaping or (re)encoding must +% be done explicitly. Some backends expand the value but this should not be +% relied on. If a \meta{name} is used twice, only the last \meta{value} +% set will be used. +% +% \end{function} +% +% \subsubsection{\enquote{Page} and \enquote{ThisPage}} +% \begin{NOTE}{UF} +% Open is the question if one need a command to set attribute on a page by page number. +% Open is the setter for /AF (and perhaps /OutputIntents). +% See also https://tex.stackexchange.com/questions/479812/extension-of-rotating-package-to-set-pdf-rotation +% (should work now) +% \end{NOTE} +% \begin{function}[added = 2020-04-12] +% {pdfmanagement: Page} +% \begin{syntax} +% \cs{pdfmanagement_add:nnn} \{Page\} \Arg{name} \Arg{value} +% \end{syntax} +% Values added with the path \texttt{Page} are added to the page dictionary +% of the current page and the following pages. The current page means the page +% on which the command is \emph{executed}. \meta{name} should be a valid pdf name +% without the leading slash. Typical names used here are e.g. +% \texttt{Rotate} and \texttt{CropBox}. +% \meta{value} should be a valid pdf value. +% Any escaping or (re)encoding must be done explicitly. Some backends expand the +% value but this should not be relied on. +% To avoid problems with the asynchronous page breaking +% the command should be used after \cs{newpage} or in the header. +% It should not be used in a float, as it will then quite probably be executed +% on the wrong page. +% The value is assigned directly and is always stored globally. +% If a \meta{name} is used twice, only the last \meta{value} +% set will be used. Names set with \cs{pdfmanagement_add:nnn}|{ThisPage}| will overwrite +% names set with \cs{pdfmanagement_add:nnn}|{Page}| if there is a clash. +% Values can be removed again with \cs{pdfmanagement_remove:nn}. +% This replaces \cs{pdfpageattr}. +% \end{function} +% \begin{function}[added = 2020-04-12] +% { pdfmanagement: ThisPage} +% \begin{syntax} +% \cs{pdfmanagement_add:nnn} \{ThisPage\} \Arg{name} \Arg{value} +% \end{syntax} +% Adds |/|\meta{name} \meta{value} at \emph{shipout} to the page dictionary of the +% current page. Current page means here the \emph{shipout} page. +% It is always stored globally. +% If \Arg{name} has already a value set in the \texttt{Page} +% dictionary it will be overwritten for this page. +% \meta{name} should be a valid pdf name without the leading slash, +% \meta{value} should be a valid pdf value. Any escaping or (re)encoding must be +% done explicitly. If a \meta{name} is used twice, only the last \meta{value} +% set will be used. +% With the engine pdflatex (at least) a second compilation is needed. +% Values added to \texttt{ThisPage} can not be removed. It is not possible to +% show the content of this dictionary with \cs{pdfmanagement_show:n}. +% \end{function} +% \subsubsection{\enquote{Page/Resources}: ExtGState, ColorSpace, Shading, Pattern} +% \begin{NOTE}{UF} +% Only for pdf/luatex and xdvipdfmx backend- and pdf-code is needed to add values +% to these resources. +% With dvips the resources are added through high-level code (e.g. transparency), so the +% backend/pdf commands are no-ops. +% For every resources there is only one object. References to these objects are added to +% all pages starting from the page where the first time something has been added to the +% resource and to all XObjects. For luatex and pdftex it must be done together +% with the /Properties, see above. +% I don't see a need to set e.g. /ColorSpace page wise: preflight handles this +% fine, see experiment colorspace-resources. +% As pgf does the same, there is a need to patch it for now. Ditto for package colorspace. +% \end{NOTE} +% \begin{function}[updated = 2020-04-10] +% { +% pdfmanagement: Page/Resources/ExtGState, +% pdfmanagement: Page/Resources/ColorSpace, +% pdfmanagement: Page/Resources/Shading, +% pdfmanagement: Page/Resources/Pattern, +% } +% \begin{syntax} +% \cs{pdfmanagement_add:nnn} \{Page/Resources/\meta{resource}\} \Arg{name} \Arg{value} +% \end{syntax} +% Adds |/|\meta{name} \meta{value} to the page resource \meta{resource}. +% \meta{resource} can be |ExtGState|, |ColorSpace|, |Pattern| oder |Shading|. +% The values are always stored globally. The content is written to the pdf +% at the end of the compilation, so values can be added until then. +% \meta{name} should be a valid pdf name without the leading slash, +% \meta{value} should be a valid pdf value for the resource. +% Any escaping or (re)encoding must be done explicitly. If a \meta{name} is +% used twice, only the last \meta{value} set will be used. +% +% With the dvips backend the command does nothing: these resources are managed by +% ghostscript or the distiller if e.\,g. transparency is used. +% +% The resources are added to all pages starting with the first where something has +% been added to a resources. That means that for example +% all ExtGState resources are combined in one +% dictionary object and every page with a ExtGState resource refer to this object% +% \footnote{This is similar to how pgf handles this resources}. +% +% \potentialclash The primitive commands (e.g. \cs{pdfpageresources}) +% to set the resources should not be used +% together with this code as the calls will overwrite each other and values +% will be lost. This means that currently there are clashes with the packages tikz, +% transparent and colorspace. +% \end{function} +% \subsubsection{\enquote{Catalog} \& subdirectories} +% \begin{NOTE}{UF} +% Perhaps some tools to create the AF-file specification dictionaries is useful. +% Open for now: +% /Extensions (dict, pdf 2.0) +% /Dests ? difference to subdict in Names? +% /DSS (dict, pdf 2.0) +% /Acroform/DR/ExtGState etc probably unneeded. +% \end{NOTE} +% The catalog is a central dictionary in a PDF with a number of subdictionaries. +% Entries to the top level of the catalog can be added with\\ +% |\pdfmanagement_add:nnn {Catalog}|\Arg{Name}\Arg{Value}. +% Entries to subdictionaries by using in the first +% argument one of the pathes described later. +% The entries in the catalog have varying requirements regarding the +% PDF management. Some entries (like \texttt{/Lang}) are simple values +% where new values should +% overwrite existing values, other like for example \texttt{/OutputIntents} +% can contain a number of values and can be filled from more than one source. +% In some cases the values that needs to be added are not at the top-level +% but in some subsubdictionary or are actually part of an array. +% To handle the pdf management uses a variety of internal, special handlers. +% +% \potentialclash In some cases entries are added implicitly. +% For example entries to the name +% tree of the \texttt{/EmbeddedFiles} key in the \texttt{/Names} directory are +% added with the commands of the \texttt{l3pdffile} module. This clashes with +% e.g. the embedfile package which should not be used! +% +% +% \paragraph{Entries at the top level of the catalog} +% The Names in the following tabular are entries that are added to the +% top level of the catalog. +% +% If \meta{Name} gets assigned a value more than once the last one wins. +% There is no check that the values have the correct type and format. +% It is up to the user to ensure that the value does what is intended. +% +% The required PDF version is only mentioned if it is larger than 1.5. +% +% Example: |\pdfmanagement_add:nnn {Catalog}{PageMode}{/UseNone}| +% +% \medskip +% \noindent +% \begin{tabularx}{\linewidth}{ll>{\raggedright\arraybackslash}X} +% \bfseries Name & \bfseries Value & \bfseries Remark \\\midrule +% Collection & objref or dict & the content should be +% build by external packages (see eg embedfile) \\ +% DPartRoot & objref or dict & PDF 2.0 \\ +% Lang & string & e.g. \texttt{(de-DE)} \\ +% Legal & objref or dict \\ +% Metadata & objref or stream \\ +% NeedsRendering & boolean & PDF 1.7\\ +% OpenAction & array (dest) or dict (action) \\ +% PageLabels & objref or dict & number tree \\ +% PageLayout & name & one of /SinglePage, /OneColumn, +% /TwoColumnLeft, /TwoColumnRight, +% /TwoPageLeft, +% /TwoPageRight \\ +% PageMode & name & one of /UseNone, /UseOutlines, /UseThumbs, +% /UseOC, /UseAttachments (PDF 1.6)\\ +% Perms & objref or dict & permissions\\ +% PieceInfo & objref or dict \\ +% SpiderInfo & objref or dict \\ +% StructTreeRoot & objref or dict \\ +% Threads & objref to an array\\ +% URI & objref or dict \\ +% Version & name & eg. \texttt{/1.7} \\ +% \meta{unknown} & & an unknown \meta{name} will be +% inserted without a warning.\\ +% \end{tabularx} +% \par\medskip +% +% \paragraph{Simple entries in subdictionaries of the catalog} +% The following resource pathes have been predeclared and allow to +% add values to the respective subdictionaries of the catalog. The +% names of the dictionaries follow the naming and location of the dictionaries +% in the PDF reference. +% If \meta{Name} gets assigned two values the last one wins. +% +% Example: |\pdfmanagement_add:nnn {Catalog/MarkInfo}{Marked}{true}| +% +% \medskip +% \noindent +% \begin{tabularx}{\linewidth}{lll>{\raggedright\arraybackslash}X} +% \bfseries Path/dictionary & \bfseries Names & \bfseries Value & \bfseries Remark +% \\\midrule +% Catalog/AA &WC, WS, DS, WP,DP& all dict \\ +% Catalog/AcroForm & NeedAppearances& boolean & In pdf 2.0 +% NeedAppearances +% is deprecated, +% it is then required +% that every widget has +% an appearance streams.\\ +% & SigFlags & Integer\\ +% & DA & String \\ +% & Q & Integer\\ +% & XFA & stream or array & pdf 1.5\\ +% Catalog/AcroForm/DR & \meta{name} & & probably unneeded \\ +% Catalog/AcroForm/DR/Font & \meta{name} & dict & \\ +% Catalog/MarkInfo & Marked & boolean \\ +% & UserProperties & boolean \\ +% & Suspects & boolean \\ +% Catalog/ViewerPreferences & HideToolbar & boolean \\ +% & Direction & /R2L or /L2R \\ +% & \ldots & & many more, see the reference \\ +% \end{tabularx} +% +% +% \paragraph{Catalog entries with multiple values in arrays} +% The following entries are special: Their values are arrays and +% it must be possible to append to such arrays. This means that a new +% call to set this value doesn't replace the value but appends it. +% The value is an object reference. It is sensible to declare the object +% first. E.g. +% \begin{verbatim} +% \pdf_object_new:nn {pkg@intent}{dict} +% \pdf_object_write:nn {pkg@intent}{...} +% \pdfmanagement_add:nnx {Catalog} {OutputIntents}{\pdf_object_ref:n {pkg@intent}} +% \end{verbatim} +% +% or +% \begin{verbatim} +% \pdf_object_unnamed_write:nn {dict} { ... } +% \pdfmanagement_add:nnx {Catalog} {OutputIntents}{\pdf_object_ref_last:} +% \end{verbatim} +% +% +% \medskip +% \noindent +% \begin{tabularx}{\linewidth}{lll>{\raggedright\arraybackslash}X} +% \bfseries Path/dictionary &\bfseries Name & \bfseries Value & \bfseries Remark \\\midrule +% Catalog/AcroForm & Fields & object reference\\ +% Catalog/AcroForm & CO & object reference\\ +% Catalog & AF & object reference & PDF 2.0, associated files\\ +% Catalog/OCProperties & OCGs & object reference &if there are OCProperties, OCGs and D are required.\\ +% Catalog/OCProperties & Configs & object reference \\ +% Catalog/OCProperties & D & object reference & This is actually a single value as +% there can be only one default. +% If the value is set twice, the +% second wins, and the first is +% added to OCProperties/Configs.\\ +% Catalog & OutputIntents & object reference\\ +% Catalog & Requirements & object reference & PDF 1.7 \\ +% Catalog/Names & EmbeddedFiles & object reference & This should reference a filespec dictionary. It will +% attach the file to the file panel. +% \end{tabularx} +% +% \end{documentation} +% +% \begin{implementation} +% \section{\pkg{l3pdfmanagement} implementation} +% \begin{macrocode} +%<@@=pdfmanagement> +%<*header> +% +\ProvidesExplPackage {l3pdfmanagement} {2021-02-22} {0.95a} + {Management of core PDF dictionaries (LaTeX PDF management testphase bundle)} +%</header> +% \end{macrocode} +% \subsection{Messages} +% \begin{macrocode} +%<*package> +\msg_new:nnn { pdfmanagement } { unknown-dict } + { The~PDF~management~resource~'#1'~is~unknown. } + +\msg_new:nnn { pdfmanagement } { empty-value } + { The~value~for~#1~is~empty~and~will~be~ignored } + +\msg_new:nnn { pdfmanagement } { no-removal } + { It~is~not~possible~to~remove~values~from~'#1'.} + +\msg_new:nnn { pdfmanagement } { no-show } + { It~is~not~possible~to~show~the~content~of~'#1'.} + +\msg_new:nnn { pdfmanagement } { show-dict } + { + The~PDF~resource~'#1'~ + \tl_if_empty:nTF {#2} + { is~empty \\>~ . } + { contains~the~pairs~(without~outer~braces): #2 . } + } +\msg_new:nnn { pdfmanagement } { dict-already-defined } + { + The~path~'#1'~is~already~defined. + } +\msg_new:nnn { pdfmanagement } { inactive } + { + The~PDF~resources~management~is~not~active\\ + command~'#1'~ignored. + } +% \end{macrocode} +% \begin{variable}{\g_@@_active_bool} +% This boolean will control the activation of the management code. +% It is used in the hooks, and in some backend files. +% % \cs{DeclareDocumentMetadata} should set it to true +% \begin{macrocode} +\bool_new:N \g_@@_active_bool +% \end{macrocode} +% \end{variable} +% A user predicate to test if the management code is active +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_active: { p , T , F , TF } + { + \bool_if:NTF \g_@@_active_bool + { \prg_return_true: } + { \prg_return_false: } + } +\prg_set_eq_conditional:NNn + \pdfmanagement_if_active: \@@_if_active: { p , T , F , TF } + +% \end{macrocode} +% We use a hook, to collect value added before the backend is ready. +% \begin{macrocode} +\hook_new:n {pdfmanagement/add} +\cs_new_protected:Npn \pdfmanagement_add:nnn #1 #2 #3 + { + \@@_if_active:TF + { + \pdfdict_if_exist:nTF { g__pdf_Core/#1 } + { + \hook_gput_code:nnn + {pdfmanagement/add} + {pdfmanagement} + { + \@@_handler_gput:nnn { #1 }{ #2 }{ #3 } + } + } + { + \msg_error:nnn{pdfmanagement}{unknown-dict}{#1} + } + } + { + \msg_warning:nnx {pdfmanagement}{inactive} + {\tl_to_str:n {\pdfmanagement_add:nnn}} + } + } + +\cs_generate_variant:Nn \pdfmanagement_add:nnn {nnx,nxx} +% \end{macrocode} +% \subsection{Hooks -- shipout and end of run code} +% Code is executed in three places: At shipout of every page, +% at shipout of the last page, at the end of the document +% (after the last clearpage). Due to backend differences the code in the +% three places (and the exact timing) can be different: pdflatex/lualatex +% can execute code after the last \cs{clearpage} which the dvi-based +% drivers have to add on a shipout page. +% +% \begin{variable} +% { +% \g__kernel_pdfmanagement_thispage_shipout_code_tl +% \g__kernel_pdfmanagement_lastpage_shipout_code_tl +% \g__kernel_pdfmanagement_end_run_code_tl +% } +% This variables contain the code run in the three places. +% \begin{macrocode} +\tl_new:N \g__kernel_pdfmanagement_thispage_shipout_code_tl +\tl_new:N \g__kernel_pdfmanagement_lastpage_shipout_code_tl +\tl_new:N \g__kernel_pdfmanagement_end_run_code_tl +% \end{macrocode} +% \end{variable} +% \begin{macrocode} +\tl_gset:Nn \g__kernel_pdfmanagement_thispage_shipout_code_tl + { + \bool_if:NT \g_@@_active_bool + { + \exp_args:NV \__pdf_backend_ThisPage_gpush:n { \g_shipout_readonly_int } + \exp_args:NV \__pdf_backend_PageResources_gpush:n { \g_shipout_readonly_int } + } + } + +\tl_gset:Nn \g__kernel_pdfmanagement_lastpage_shipout_code_tl + { + \bool_if:NT \g_@@_active_bool + { + \__pdf_backend_PageResources_obj_gpush: %ExtGState etc + } + } + +\tl_gset:Nn \g__kernel_pdfmanagement_end_run_code_tl + { + \bool_if:NT \g_@@_active_bool + { + \@@_Pages_gpush: %pagesattr + \@@_Info_gpush: %pdfinfo + \@@_Catalog_gpush: + } + } +% \end{macrocode} +% \subsection{Naming convention} + +% Currently the following names are used: ^^A!!!!! check, compare with g_@@_gnames_seq +% All have internally additionally a \texttt{Core} before the slash, to +% hide the real name a bit. +% \begin{verbatim} +% /Info % (\pdfinfo) +% /Catalog % (\pdfcatalog) +% /Catalog/AA % +% /Catalog/AcroForm +% /Catalog/OCProperties +% /Catalog/OutputIntents +% /Catalog/AcroForm/DR +% /Catalog/AcroForm/DR/Font +% /Catalog/MarkInfo +% /Catalog/ViewerPreferences +% /Pages % (\pagesattr) +% /Page % (\pageattr) +% /ThisPage % (\pageattr) +% /backend_PageN/Resources/Properties % this is only internal. +% /Page/Resources/ExtGState +% /Page/Resources/ColorSpace +% /Page/Resources/Pattern +% /Page/Resources/Shading +% /Page/Resources/Properties +% /Xform/Resources/Properties +% \end{verbatim} + +% \begin{macro}{ +% \@@_handler_gput:nnn, +% \@@_get:nnN, +% \@@_gremove:nn, +% \@@_show:n +% } +% \cs{@@_handler_gput:nnn} is the main command to fill the dictionaries. +% In simple cases it directly fill the property list, but if a handler exists +% this is called. It is important to use it only in places where this make sense. +% +% \begin{macrocode} + +%global +\cs_new_protected:Npn \@@_handler_gput:nnn #1 #2 #3 %#1 dict, #2 name, #3 value + { + \tl_if_empty:nTF { #3 } + { + \msg_none:nnn { pdfmanagement }{ empty-value }{ /#1/#2 } + } + { + \pdfdict_if_exist:nTF { g__pdf_Core/#1 } + { + \cs_if_exist:cTF + { @@_handler/#1/?_gput:nn } %general, name independant handler + { \use:c {@@_handler/#1/?_gput:nn} {#2} {#3} } + { + \cs_if_exist:cTF + { @@_handler/#1/#2_gput:n } + { \use:c {@@_handler/#1/#2_gput:n} {#3} } %special handler + { + \exp_args:Nnx + \prop_gput:cnn + { \__kernel_pdfdict_name:n { g__pdf_Core/#1 } } + { \str_convert_pdfname:n { #2 } } + { #3 } + } + } + } + { + \msg_error:nnn { pdfmanagement } { unknown-dict } { #1 } + } + } + } + + +\cs_generate_variant:Nn \@@_handler_gput:nnn {nxx} + +\cs_new_protected:Npn \@@_get:nnN #1 #2 #3 %path,key,macro + { + \exp_args:Nnx + \prop_get:cnN + { \__kernel_pdfdict_name:n { g__pdf_Core/#1 } } + { \str_convert_pdfname:n {#2} } #3 + } + + +\cs_new_protected:Npn \@@_handler_gremove:nn #1 #2 %path,key + { + \pdfdict_if_exist:nTF { g__pdf_Core/#1 } + { + \cs_if_exist:cTF + { @@_handler/#1/?_gremove:n } %general, name independant handler + { \use:c {@@_handler/#1/?_gremove:n} {#2} } + { + \cs_if_exist:cTF + { @@_handler/#1/#2_gremove: } + { \use:c {@@_handler/#1/#2_gremove:} } %special handler + { + \exp_args:Nnx + \prop_gremove:cn + { \__kernel_pdfdict_name:n { g__pdf_Core/#1 } } + { \str_convert_pdfname:n {#2} } + } + } + } + { + \msg_error:nnn { pdfmanagement } { unknown-dict } { #1 } + } + } + +\cs_new_protected:Npn \@@_gremove:nn #1 #2 %path,key + { + \pdfdict_if_exist:nTF { g__pdf_Core/#1 } + { + \exp_args:Nnx + \prop_gremove:cn + { \__kernel_pdfdict_name:n { g__pdf_Core/#1 } } + { \str_convert_pdfname:n{#2} } + } + { + \msg_error:nnn { pdfmanagement } { unknown-dict } { #1 } + } + } + + +\cs_new_protected:Npn \@@_show:Nn #1#2 + { + \cs_if_exist:cTF + { @@_handler/#2/?_show: } %general, name independant handler + { \use:c {@@_handler/#2/?_show:} } + { + \prop_if_exist:cTF { \__kernel_pdfdict_name:n { g__pdf_Core/#2 } } + { + #1 + { pdfmanagement } { show-dict } + { \tl_to_str:n {#2} } + { + \prop_map_function:cN + {\__kernel_pdfdict_name:n { g__pdf_Core/#2 }} + \msg_show_item:nn + } + { } { } + } + { + #1 { pdfmanagement } { unknown-dict } {#2}{}{}{} + } + } + } + +\cs_new_protected:Npn \@@_show:n #1 %path + { + \prop_show:c { \__kernel_pdfdict_name:n { g__pdf_Core/#1 } } + } +% \end{macrocode} +% \end{macro} +% +% +% \begin{macrocode} +\cs_new_protected:Npn \pdfmanagement_show:n #1 + { + \@@_show:Nn \msg_show:nnxxxx {#1} + } +% \end{macrocode} +% \begin{macrocode} +\cs_new_protected:Npn \pdfmanagement_remove:nn #1 #2 + { + \pdfdict_if_exist:nTF { g__pdf_Core/#1 } + { + \@@_handler_gremove:nn { #1 }{ #2 } + } + { + \msg_error:nnn{pdfmanagement}{unknown-dict}{#1} + } + } +% \end{macrocode} +% \begin{macrocode} +\cs_new_protected:Npn \pdfmanagement_get:nnN #1 #2 #3 + { + \pdfdict_if_exist:nTF { g__pdf_Core/#1 } + { + \@@_get:nnN { #1 }{ #2 } #3 + } + { + \msg_error:nnn{pdfmanagement}{unknown-dict}{#1} + } + } +% \end{macrocode} +% \subsection{The Info dictionary} +% Initialization of the dictionary: +% \begin{macrocode} +\pdfdict_new:n { g__pdf_Core/Info} +% \end{macrocode} +% +% \begin{macro}{\@@_Info_gpush:} +% \cs{@@_Info_gpush:} is the command that outputs the info dictionary (currently +% in the end-of-run hooks). +% \begin{macrocode} +% push to the register command / issue the special +\cs_new_protected:Npn \@@_Info_gpush: + { + \prop_map_function:cN + { \__kernel_pdfdict_name:n { g__pdf_Core/Info} } + \__pdf_backend_info_gput:nn + \prop_gclear:c { \__kernel_pdfdict_name:n { g__pdf_Core/Info} } + } +% \end{macrocode} +% \end{macro} +% \subsection{The Pages dictionary code} +% \begin{NOTE}{UF} +% The register is normally used only a few times in a document, so it would be +% okay to update the register/add the special at every change, +% but with dvips/dvipdfmx this would disable removing entries. +% So we issue the push code only at the end of the document. +% \end{NOTE} +% At first the initialisation +% \begin{macrocode} +\pdfdict_new:n { g__pdf_Core/Pages} +% \end{macrocode} +% +% \begin{macro}{\@@_Pages_gpush:} +% This is the command that outputs the Pages dictionary. It is used +% at the end of the document in \cs{g__pdf_backend_end_run_tl} +% \begin{macrocode} +% push to the register command / issue the special +\cs_new_protected:Npn \@@_Pages_gpush: + { + \exp_args:Nx \__pdf_backend_Pages_primitive:n + { + \pdfdict_use:n { g__pdf_Core/Pages} + } + } + +% \end{macrocode} +% \end{macro} +% \subsection{The Page and ThisPage dictionary} +% At first the initialisation. +% \begin{macrocode} +\pdfdict_new:n { g__pdf_Core/Page } +\pdfdict_new:n { g__pdf_Core/ThisPage } + +%handler for pdfmanagement +\cs_new_protected:cpn { @@_handler/Page/?_gput:nn } #1 #2 + { + \__pdf_backend_Page_gput:nn { #1 }{ #2 } + } +% remove: +\cs_new_protected:cpn { @@_handler/Page/?_gremove:n } #1 + { + \__pdf_backend_Page_gremove:n { #1 } + } + +% handler for pdfmanagement +\cs_new_protected:cpn { @@_handler/ThisPage/?_gput:nn } #1 #2 + { + \prop_gput:cnn { \__kernel_pdfdict_name:n { g__pdf_Core/ThisPage } }{ #1 } { #2 } + \bool_if:NT \g_@@_active_bool + { + \__pdf_backend_ThisPage_gput:nn { #1 }{ #2 } + } + } + +\cs_new_protected:cpn { @@_handler/ThisPage/?_gremove:n } #1 + { + \msg_warning:nnn { pdfmanagement } { no-removal }{ThisPage} + } + +\cs_new_protected:cpn { @@_handler/ThisPage/?_show: } + { + \msg_warning:nnn { pdfmanagement } { no-show }{ThisPage} + } + +% \end{macrocode} +% \subsubsection{\enquote{Page/Resources}: ExtGState, ColorSpace, Shading, Pattern} +% \begin{macrocode} +\clist_const:Nn \c_@@_PageResources_clist + { + ExtGState, + ColorSpace, + Pattern, + Shading, + } + +\clist_map_inline:Nn \c_@@_PageResources_clist + { + \pdfdict_new:n { g__pdf_Core/Page/Resources/#1} + } +% +% setter: #1 is the name of the resource +\cs_new_protected:cpn { @@_handler/Page/Resources/ExtGState/?_gput:nn } #1 #2 + { + \__pdf_backend_PageResources_gput:nnn {ExtGState} { #1 }{ #2 } + } + +\cs_new_protected:cpn { @@_handler/Page/Resources/ColorSpace/?_gput:nn } #1 #2 + { + \__pdf_backend_PageResources_gput:nnn {ColorSpace} { #1 }{ #2 } + } + +\cs_new_protected:cpn { @@_handler/Page/Resources/Shading/?_gput:nn } #1 #2 + { + \__pdf_backend_PageResources_gput:nnn {Shading} { #1 }{ #2 } + } + +\cs_new_protected:cpn { @@_handler/Page/Resources/Pattern/?_gput:nn } #1 #2 + { + \__pdf_backend_PageResources_gput:nnn {Pattern} { #1 }{ #2 } + } +% \end{macrocode} +% \subsubsection{\enquote{Catalog}} +% The catalog has mixed entries: toplevel, subdictionaries, and entries +% which must build arrays. +% \begin{variable}[added=2019-08-24] +% { +% \c_@@_Catalog_toplevel_clist, +% \c_@@_Catalog_sub_clist, +% \c_@@_Catalog_seq_clist, +% } +% This variables hold the list of the various types of entries. With it +% the various \verb=_gput= commands are generated. +% \end{variable} +% \begin{macro}{ \@@_catalog_XX_gput:n } +% Various commands to handle subentries and special cases. +% \begin{macrocode} +\pdfdict_new:n { g__pdf_Core/Catalog} + +\clist_const:Nn \c_@@_Catalog_toplevel_clist + { + Collection, + DPartRoot, + Lang, + Legal, + Metadata, + NeedsRendering, + OCProperties/D, + OpenAction, + PageLabels, + PageLayout, + PageMode, + Perms, + PieceInfo, + SpiderInfo, + StructTreeRoot, + Threads, + URI, + Version + } + +\clist_const:Nn \c_@@_Catalog_sub_clist + { + AA, + AcroForm, + AcroForm/DR, + AcroForm/DR/Font, + MarkInfo, + ViewerPreferences, + OCProperties + } + +\clist_map_inline:Nn \c_@@_Catalog_sub_clist + { + \pdfdict_new:n { g__pdf_Core/Catalog/#1} + } + + +\clist_const:Nn \c_@@_Catalog_seq_clist + { + AF, + OCProperties/OCGs, + OCProperties/Configs, + OutputIntents, + Requirements, + AcroForm/Fields, + AcroForm/CO + } + + + +\clist_map_inline:Nn \c_@@_Catalog_seq_clist + { + \seq_new:c { g_@@_/Catalog/#1_seq } % new name later + \cs_new_protected:cpn { @@_handler/Catalog/#1_gput:n } ##1 + { + \seq_gput_right:cn { g_@@_/Catalog/#1_seq } { ##1 } + } + } + +\cs_new_protected:cpn { @@_handler/Catalog/OCProperties/D_gput:n } #1 + { + \seq_gput_left:cn + { g_@@_/Catalog/OCProperties/Configs_seq } + { #1 } + } +% \end{macrocode} +% \end{macro} +% % \paragraph {Building the catalog: Push order} +% \begin{macro}{\@@_Catalog_gpush:} +% \begin{macrocode} +\cs_new_protected:Npn \@@_Catalog_gpush: + { + \use:c { @@_/Catalog/AA_gpush: } + \use:c { @@_/Catalog/AcroForm_gpush: } + \use:c { @@_/Catalog/AF_gpush: } + \use:c { @@_/Catalog/MarkInfo_gpush: } + \pdfmeta_standard_verify:nT {Catalog_no_OCProperties} + { + \use:c { @@_/Catalog/OCProperties_gpush: } + } + \use:c { @@_/Catalog/OutputIntents_gpush: } + \use:c { @@_/Catalog/Requirements_gpush: } + \use:c { @@_/Catalog/ViewerPreferences_gpush: } + % output the single values: + \prop_map_function:cN + { \__kernel_pdfdict_name:n { g__pdf_Core/Catalog} } + \__pdf_backend_catalog_gput:nn + % output names tree: + \use:c { @@_/Catalog/Names/EmbeddedFiles_gpush: } + } +% \end{macrocode} +% \end{macro} +% \paragraph{Building catalog entries: AA} +% \begin{macro}{\@@_/Catalog/AA_gpush:} +% \begin{macrocode} +\cs_new_protected:cpn { @@_/Catalog/AA_gpush: } + { + \prop_if_empty:cF + { \__kernel_pdfdict_name:n { g__pdf_Core/Catalog/AA } } + { + \__pdf_backend_object_new:nn { g_@@_/Catalog/AA_obj } { dict } + \__pdf_backend_object_write:nx + { g_@@_/Catalog/AA_obj } + { \pdfdict_use:n { g__pdf_Core/Catalog/AA } } + \exp_args:Nnx + \__pdf_backend_catalog_gput:nn + {AA} + { + \__pdf_backend_object_ref:n { g_@@_/Catalog/AA_obj } + } + } + } +% \end{macrocode} +% \end{macro} +% +% \paragraph{Building catalog entries: AcroForm} +% This is the most complicated case. +% The entries is build from +% /Catalog/AcroForm/Fields (array), +% /Catalog/AcroForm/CO (array), +% /Catalog/AcroForm/DR/Font (dict), +% /Catalog/AcroForm/DR (dict), +% /Catalog/AcroForm +% +% \begin{macro}{\@@_/Catalog/AcroForm_gpush:} +% \begin{macrocode} +\cs_new_protected:cpn { @@_/Catalog/AcroForm_gpush: } + { + \seq_if_empty:cF { g_@@_/Catalog/AcroForm/Fields_seq } + { + \__pdf_backend_object_new:nn { g_@@_/Catalog/AcroForm/Fields_obj } { array } + \__pdf_backend_object_write:nx + { g_@@_/Catalog/AcroForm/Fields_obj } + { \seq_use:cn { g_@@_/Catalog/AcroForm/Fields_seq } {~} } + \exp_args:Nnnx + \prop_gput:cnn %we have to use \prop here to avoid the handler ... + { \__kernel_pdfdict_name:n { g__pdf_Core/Catalog/AcroForm } } + { Fields } + { \__pdf_backend_object_ref:n { g_@@_/Catalog/AcroForm/Fields_obj } } + } + \seq_if_empty:cF { g_@@_/Catalog/AcroForm/CO_seq } + { + \__pdf_backend_object_new:nn { g_@@_/Catalog/AcroForm/CO_obj } { array } + \exp_args:Nnx + \__pdf_backend_object_write:nn + { g_@@_/Catalog/AcroForm/CO_obj } + { \seq_use:cn { g_@@_/Catalog/AcroForm/CO_seq } {~} } + \exp_args:Nnnx + \prop_gput:cnn %we have to use \prop here to avoid the handler ... + { \__kernel_pdfdict_name:n { g__pdf_Core/Catalog/AcroForm } } + { CO } + { \__pdf_backend_object_ref:n { g_@@_/Catalog/AcroForm/CO_obj } } + } + \prop_if_empty:cF { \__kernel_pdfdict_name:n { g__pdf_Core/Catalog/AcroForm/DR/Font}} + { + \__pdf_backend_object_new:nn { g_@@_/Catalog/AcroForm/DR/Font_obj } {dict} + \exp_args:Nnx + \__pdf_backend_object_write:nn + { g_@@_/Catalog/AcroForm/DR/Font_obj } + { \pdfdict_use:n { g__pdf_Core/Catalog/AcroForm/DR/Font } } + \exp_args:Nnnx + \prop_gput:cnn %we have to use \prop here to avoid the handler ... + { \__kernel_pdfdict_name:n { g__pdf_Core/Catalog/AcroForm/DR } } + { Font } + { \__pdf_backend_object_ref:n { g_@@_/Catalog/AcroForm/DR/Font_obj } } + } + \prop_if_empty:cF { \__kernel_pdfdict_name:n { g__pdf_Core/Catalog/AcroForm/DR}} + { + \__pdf_backend_object_new:nn { g_@@_/Catalog/AcroForm/DR_obj } {dict} + \exp_args:Nnx + \__pdf_backend_object_write:nn + { g_@@_/Catalog/AcroForm/DR_obj } + { \pdfdict_use:n { g__pdf_Core/Catalog/AcroForm/DR } } + \exp_args:Nnnx + \prop_gput:cnn %we have to use \prop here to avoid the handler ... + { \__kernel_pdfdict_name:n { g__pdf_Core/Catalog/AcroForm } } + { DR } + { \__pdf_backend_object_ref:n { g_@@_/Catalog/AcroForm/DR_obj } } + } + \prop_if_empty:cF { \__kernel_pdfdict_name:n { g__pdf_Core/Catalog/AcroForm} } + { + \__pdf_backend_object_new:nn { g_@@_/Catalog/AcroForm_obj } {dict} + \exp_args:Nnx + \__pdf_backend_object_write:nn + { g_@@_/Catalog/AcroForm_obj } + { \pdfdict_use:n { g__pdf_Core/Catalog/AcroForm } } + \exp_args:Nnnx + \@@_handler_gput:nnn + { Catalog } + { AcroForm } + { \__pdf_backend_object_ref:n { g_@@_/Catalog/AcroForm_obj } } + } + } + +% \end{macrocode} +% \end{macro} +% +% \paragraph{Building catalog entries: AF} +% AF is an array. +% \begin{macro}{\@@_/Catalog/AF_gpush:} +% \begin{macrocode} +\cs_new_protected:cpn { @@_/Catalog/AF_gpush: } + { + \seq_if_empty:cF + { g_@@_/Catalog/AF_seq } + { + \__pdf_backend_object_new:nn { g_@@_/Catalog/AF_obj } { array } + \exp_args:Nnx + \__pdf_backend_object_write:nn + { g_@@_/Catalog/AF_obj } + { \seq_use:cn { g_@@_/Catalog/AF_seq } {~} } + \exp_args:Nnx + \__pdf_backend_catalog_gput:nn + {AF} + { + \__pdf_backend_object_ref:n {g_@@_/Catalog/AF_obj} + } + } + } +% \end{macrocode} +% \end{macro} +% % +% \paragraph{Building catalog entries: MarkInfo} +% \begin{macro}{\@@_/Catalog/MarkInfo_gpush:} +% \begin{macrocode} +\cs_new_protected:cpn { @@_/Catalog/MarkInfo_gpush: } + { + \prop_if_empty:cF + { \__kernel_pdfdict_name:n { g__pdf_Core/Catalog/MarkInfo } } + { + \__pdf_backend_object_new:nn { g_@@_/Catalog/MarkInfo_obj } { dict } + \exp_args:Nnx + \__pdf_backend_object_write:nn + { g_@@_/Catalog/MarkInfo_obj } + { \pdfdict_use:n { g__pdf_Core/Catalog/MarkInfo } } + \exp_args:Nnx + \__pdf_backend_catalog_gput:nn + {MarkInfo} + { + \__pdf_backend_object_ref:n {g_@@_/Catalog/MarkInfo_obj} + } + } + } +% \end{macrocode} +% \end{macro} +% +%\paragraph{Building catalog entries: OCProperties} +% This is a dictionary with three entries: +% \begin{description} +% \item[/OCGs] (required) An array of indirect references, +% access needed for more than one package. +% \item[/D] (required) a dict (given as an object name) to the default +% configuration +% \item[/Configs] (optional) an array of indirect references to more +% configurations. +% \end{description} +% The /D entry is also a config, it is the first of the seq. +% The overall structure is nested: a dict with arrays. +% \begin{macro}{\@@_/Catalog/OCProperties_gpush:} +% \begin{macrocode} +% Catalog/OCProperties: OCGs + D is required +\cs_new_protected:cpn { @@_/Catalog/OCProperties_gpush: } + { + \int_compare:nNnT + { + ( \seq_count:c { g_@@_/Catalog/OCProperties/OCGs_seq } )* + ( \seq_count:c { g_@@_/Catalog/OCProperties/Configs_seq } ) + } + > + { 0 } + { + \__pdf_backend_object_new:nn { g_@@_/Catalog/OCProperties_obj } { dict } + \seq_gpop_left:cN { g_@@_/Catalog/OCProperties/Configs_seq} \l_tmpa_tl + \exp_args:Nnx + \__pdf_backend_object_write:nn {g_@@_/Catalog/OCProperties_obj} + { + /OCGs~[ \seq_use:cn { g_@@_/Catalog/OCProperties/OCGs_seq } {~} ] + /D~\l_tmpa_tl~ + \seq_if_empty:cF { g_@@_/Catalog/OCProperties/Configs_seq } + { + /Configs~ + [ \seq_use:cn { g_@@_/Catalog/OCProperties/Configs_seq} {~} ] + } + } + \exp_args:Nnx + \__pdf_backend_catalog_gput:nn + { OCProperties } + { \__pdf_backend_object_ref:n {g_@@_/Catalog/OCProperties_obj} } + } + } +% \end{macrocode} +% \end{macro} +% +% \paragraph{Building catalog entries: OutputIntents} +% OutputIntents is an array. +% \begin{macro}{\@@_/Catalog/OutputIntents_gpush:} +% \begin{macrocode} +\cs_new_protected:cpn { @@_/Catalog/OutputIntents_gpush: } + { + \seq_if_empty:cF + { g_@@_/Catalog/OutputIntents_seq } + { + \__pdf_backend_object_new:nn { g_@@_/Catalog/OutputIntents_obj } { array } + \exp_args:Nnx + \__pdf_backend_object_write:nn + { g_@@_/Catalog/OutputIntents_obj } + { \seq_use:cn { g_@@_/Catalog/OutputIntents_seq } {~} } + \exp_args:Nnx + \__pdf_backend_catalog_gput:nn + {OutputIntents} + { + \__pdf_backend_object_ref:n {g_@@_/Catalog/OutputIntents_obj} + } + } + } +% \end{macrocode} +% \end{macro} +% +% \paragraph{Building catalog entries: Requirements} +% Requirements is an array. +% \begin{macro}{\@@_/Catalog/Requirements_gpush:} +% \begin{macrocode} +\cs_new_protected:cpn { @@_/Catalog/Requirements_gpush: } + { + \seq_if_empty:cF + { g_@@_/Catalog/Requirements_seq } + { + \__pdf_backend_object_new:nn { g_@@_/Catalog/Requirements_obj } { array } + \exp_args:Nnx + \__pdf_backend_object_write:nn + { g_@@_/Catalog/Requirements_obj } + { \seq_use:cn { g_@@_/Catalog/Requirements_seq } {~} } + \exp_args:Nnx + \__pdf_backend_catalog_gput:nn + {Requirements} + { + \__pdf_backend_object_ref:n { g_@@_/Catalog/Requirements_obj } + } + } + } +% \end{macrocode} +% \end{macro} +% +% +% \paragraph{Building catalog entries: ViewerPreferences} +% \begin{macro}{\@@_/Catalog/ViewerPreferences_gpush:} +% \begin{macrocode} +\cs_new_protected:cpn { @@_/Catalog/ViewerPreferences_gpush: } + { + \prop_if_empty:cF + { \__kernel_pdfdict_name:n { g__pdf_Core/Catalog/ViewerPreferences } } + { + \__pdf_backend_object_new:nn { g_@@_/Catalog/ViewerPreferences_obj } { dict } + \exp_args:Nnx + \__pdf_backend_object_write:nn + { g_@@_/Catalog/ViewerPreferences_obj } + { \pdfdict_use:n { g__pdf_Core/Catalog/ViewerPreferences } } + \exp_args:Nnx + \__pdf_backend_catalog_gput:nn + {ViewerPreferences} + { + \__pdf_backend_object_ref:n {g_@@_/Catalog/ViewerPreferences_obj} + } + } + } +% \end{macrocode} +% \end{macro} +% \paragraph{Building catalog entries: Names/EmbeddedFiles} +% \begin{NOTE}{UF} +% TODO access function for the name in the name tree +% \end{NOTE} +% \begin{macro}{ Handler} +% EmbeddedFiles is an array and needs a special handler to add values. +% \begin{macrocode} +\pdfdict_new:n { g__pdf_Core/Catalog/Names } + +\cs_new_protected:cpn { @@_handler/Catalog/Names/EmbeddedFiles_gput:n } #1 + { + \__pdf_backend_NamesEmbeddedFiles_add:n { #1 } + } +% \end{macrocode} +% \end{macro} +% The entry should only be added if there are actually embedded files. +% This can be tested by checking the names_seq +% \begin{macro}{\@@_/Catalog/Names/EmbeddedFiles_gpush:} +% \begin{macrocode} +% +\cs_new_protected:cpn { @@_/Catalog/Names/EmbeddedFiles_gpush: } + { + \seq_if_empty:NF \g__pdf_backend_EmbeddedFiles_seq + { + \exp_args:Nx \__pdf_backend_NamesEmbeddedFiles_gpush:n + { + \seq_use:Nn \g__pdf_backend_EmbeddedFiles_seq {~} + } + } + } +% \end{macrocode} +% \end{macro} +% \begin{macro}{@@_handler/Catalog/?_show:} +% A handler to show the catalog. +% \begin{macrocode} +\cs_new_protected:cpn {@@_handler/Catalog/?_show:} + { + \iow_term:x + { + \iow_newline: + The~Catalog~contains~in~the~top~level~the~single~value~entries + \prop_map_function:cN + {\__kernel_pdfdict_name:n { g__pdf_Core/Catalog }} + \msg_show_item:nn + } + \clist_map_inline:Nn \c_@@_Catalog_seq_clist + { + \seq_if_empty:cF { g_@@_/Catalog/##1_seq } + { + \iow_term:x + { + The~'##1'~array~contains~the~entries + \seq_map_function:cN { g_@@_/Catalog/##1_seq } \msg_show_item:n + } + } + } + \clist_map_inline:Nn \c_@@_Catalog_sub_clist + { + \prop_if_empty:cF { \__kernel_pdfdict_name:n { g__pdf_Core/Catalog/##1 } } + { + \iow_term:x + { + The~Catalog~subdirectory~'##1'~contains~the~single~value~entries + \prop_map_function:cN + {\__kernel_pdfdict_name:n { g__pdf_Core/Catalog/##1 }} + \msg_show_item:nn + } + } + } + \tl_show:x {\tl_to_str:n{\pdfmanagement_show:n{Catalog}}} + } +% \end{macrocode} +% \end{macro} +% \subsection{ xform / Properties } +% \begin{macrocode} +\pdfdict_new:n { g__pdf_Core/Xform/Resources/Properties} +% \end{macrocode} +% \begin{macrocode} +%</package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex |