diff options
author | Karl Berry <karl@freefriends.org> | 2021-02-23 22:38:24 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-02-23 22:38:24 +0000 |
commit | 96be5ed7ba5ce4406ca538c4b0d18edeec9d7aab (patch) | |
tree | f903bef02645eac80be0107658487813b3ebc6a7 /Master/texmf-dist/source/latex/pdfmanagement-testphase | |
parent | 7389bb9fb813a0f5e8654431dacfcb81d1f116e4 (diff) |
pdfmanagement-testphase (23feb21)
git-svn-id: svn://tug.org/texlive/trunk@57862 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/pdfmanagement-testphase')
13 files changed, 12872 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/pdfmanagement-testphase/hyperref-generic.dtx b/Master/texmf-dist/source/latex/pdfmanagement-testphase/hyperref-generic.dtx new file mode 100644 index 00000000000..c279519c3b5 --- /dev/null +++ b/Master/texmf-dist/source/latex/pdfmanagement-testphase/hyperref-generic.dtx @@ -0,0 +1,5118 @@ +% \iffalse meta-comment +% +%% File: hyperref-generic.dtx +% +% Copyright (C) 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 released version of this bundle is available from CTAN. +% +% ----------------------------------------------------------------------- +% +% 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{l3doc} +\usepackage{tabularx,array,booktabs,hyperxmp} +\hypersetup{pdfauthor=The LaTeX Project, + pdftitle=generic hyperref driver (LaTeX PDF management testphase bundle)} +\premulticols=4cm +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% \NewDocElement[ +% idxgroup=hypersetup keys\actualchar\cs{hypersetup} keys, +% idxtype = {hypersetup key}, +% printtype= \textit{setup key} +% ]{Hypkey}{xhypkey} +% \ExplSyntaxOn +% \NewDocumentEnvironment{hypkey}{m} +% { +% \clist_map_inline:nn {#1}{\begin{xhypkey}{##1}} +% } +% { +% \clist_map_inline:nn {#1}{\end{xhypkey}} +% } +% \ExplSyntaxOff +% \NewDocElement[ +% idxgroup=color names, +% idxtype = {color name}, +% printtype= \textit{color name} +% ]{HypColor}{xhypcolor} +% \ExplSyntaxOn +% \NewDocumentEnvironment{hypcolor}{m} +% { +% \clist_map_inline:nn {#1}{\begin{xhypcolor}{##1}} +% } +% { +% \clist_map_inline:nn {#1}{\end{xhypcolor}} +% } +% \ExplSyntaxOff +% \NewDocElement[ +% idxgroup=Hooks, +% printtype= \textit{hook} +% ]{Hook}{hook} +% +% \title{^^A +% The \pkg{hyperref-generic} module \\ A generic driver for hyperref ^^A +% } +% +% \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} +% This module generates a generic driver for \pkg{hyperref} meant to be used +% with the new \LaTeX{} PDF management code. It is loaded automatically +% if the PDF management code is active. The name of the driver will change after +% the testphase. +% +% The generic driver can be used with pdflatex, lualatex, xelatex, latex with +% dvipdfmx, latex with dvips+ps2pdf. latex with dvips+distiller could work too +% but is untested. (x)dvipdfmx will probably soon support dvilualatex, then this +% combination should work too. +% +% The driver \emph{requires} the new PDF management code, so documents wanting to use it +% should start like this +% \begin{verbatim} +% \RequirePackage{pdfmanagement-testphase} %loads the code +% \DeclareDocumentMetadata % activates it +% { +% %% options +% %% e.g. pdf version, backend: +% % pdfversion=1.7, +% % backend = dvipdfmx +% } +% \end{verbatim} +% +% The new driver tries to be compatible with the standard \pkg{hyperref} drivers +% but there are nevertheless differences. Some of them due to the still experimental +% status of the driver, others are design decisions: one part of the project is +% to clean up and modernize the code. The following sections try to describe +% the differences but also to document some of the rationales of the changes, +% and to add some details and comments about the existing options and so to +% extend the \pkg{hyperref} manual. +% +% \section{Avoiding transition problems} +% Some code will only work properly after other packages have been adapted to +% the new PDF management code and the changes in this driver. +% This will take some time. Until then it is recommended to follow +% the following rules +% \begin{itemize} +% \item Package options are processed at the end of the driver, +% Class options are ignored. But not every option already works as package options, +% in some cases \pkg{hyperref} interferes. So +% it is recommended for most options ---with the exception of a few mentioned below in +% section~\ref{sec:pkg-options}---to set them in \cs{hypersetup}, +% not as package option. +% \item This driver uses \pkg{l3color} for the colors. All colors +% defined with \cs{color_set:nn} or \cs{color_set:nnn} will work. +% Colors defined with \pkg{xcolor} will work if they don't use one of +% the special color models not supported by \pkg{l3color} as +% \pkg{pdfmanagement-firstaid} contains a patch for xcolor. +% If the package \pkg{color} is used it is currently recommended to define +% colors after \pkg{hyperref}. This driver uses \pkg{l3color} for the colors, +% some special color models of \pkg{xcolor} won't work. +% \item Load a color package or graphicx to get the right page sizes. +% \item Report problems! Only known problem can be resolved. +% \end{itemize} +% +% +% \section{Bookmarks / outlines} +% The new driver doesn't contain code to handle bookmarks/outlines. +% Instead it forces the loading of the \pkg{bookmark} package +% unless the package option |bookmarks=false| +% has been used. Currently \pkg{bookmark} is loaded at the end of the preamble +% so if commands from \pkg{bookmark} are needed in the preamble the document +% should load it manually. This is subject to change at some time in the future. +% +% \section{\enquote{Metadata}} +% +% \enquote{Metadata}, informations about the document, are stored in a PDF in two +% places: The |/Info| dictionary and the XMP-metadata. \pkg{hyperref} only handles the +% |/Info| dictionary. The XMP-metadata can be added with like \pkg{pdfx} and +% \pkg{hyperxmp}. +% +% The |/Info| dictionary can be filled with arbitrary keys, but the PDF viewer typically +% care only about a few, like |/Author|, |/Title| and |/Keywords|. +% A number of |/Info| keys, like dates and the producer, are added automatically +% by the engines and backends. +% Some of them can only be removed with special commands, some not at all. +% But---with the exception of |/Producer| when using the dvips backend---they +% can be overwritten. +% +% The current handling of the metadata is problematic: +% \begin{itemize} +% \item External package like \pkg{hyperxmp} wants to access them too and for +% this had to patch an number of internal hyperref commands---which is a problem +% if the internal commands change (as happens with this new driver) +% \item \pkg {hyperref} (and also \pkg{hyperxmp}) +% tries to deduce some datas from document commands like +% \cs{title} or \cs{author}---something that worked reasonably well when only +% some standard classes with well-known definitions of these command existed, +% but gets problematic with classes and packages which define +% more powerful commands knowing a variety of optional arguments to set authors and +% affiliations and title information. +% \end{itemize} +% +% To resolve some of this problem the driver will +% \begin{itemize} +% \item \emph{Not} try deduce author and title from documents. They have to be set in +% \cs{hypersetup} with |pdfauthor| and |pdftitle|. It is recommended to separate +% more than one author by commas, and to hide commas inside braces if needed: +% +% \begin{verbatim} +% pdfauthor = {Bär, Peter Anteater, {Riley, the sloth}} +% \end{verbatim} +% \item All values of relevant keys (including keys from the hyperxmp package) +% will be stored in a Metadata container, and can be retrieved with +% \cs{GetDocumentProperties}. +% +% \begin{verbatim} +% \edef\my@pdfauthor{\GetDocumentProperties{hyperref/pdfauthor}} +% \end{verbatim} +% If the key hasn't be set, the result is empty. This gives external +% packages a public and reliable access to the data. +% +% \item |pdflang| is deprecated. Instead \cs{DeclareDocumentMetadata} +% should be used: +% \begin{verbatim} +% \DeclareDocumentMetadata{lang=de-DE} +% \end{verbatim} +% +% The value can be retrieved as |document/lang|. +% +% \end{itemize} +% \section{Dates} +% \pkg{hyperref has a few keys to set dates}. They typically expect the date +% in \enquote{PDF} format: |D:YYYYMMDDhhmmss+01'00'|. +% +% One should be aware that \pkg{hyperxmp} will sometimes +% overwrites such settings. +% ^^A XMP: yyyy-mm-ddThh:mm:ss+tt:tt, PDF: D:YYYYMMDDhhmmss+01'00' +% +% \section{PDF page size (mediabox)} +% The standard \pkg{hyperref} driver contain code to set the PDF page size. +% There is no real justification why this is done by \pkg{hyperref} apart from the +% fact that \LaTeX{} itself doesn't do it and that the needed special code could +% be added to the backend drivers. +% +% In the new driver this code is gone. The reason is not that it is +% difficult to set the MediaBox, actually it could be done with one line of code: +% \begin{verbatim} +% \pdfmanagement_add:nnn{Page}{MediaBox} +% {[0~0~\dim_to_decimal_in_bp:n{\paperwidth}~ +% \dim_to_decimal_in_bp:n{\paperheight}]} +% \end{verbatim} +% +% The problem is to know which value to use (with the memoir class e.g.\cs{stockwidth} +% should be used instead of \cs{paperwidth}), +% and detecting this not a \pkg{hyperref} task. Instead the packages which change +% these values should also set the PDF page size. Also there are +% too many actors here: color/graphicx, geometry,the KOMA-classes, memoir, +% \ldots\ all try to set this. +% +% So if the PDF page size is wrong: load one of the other packages setting it +% e.g. the \pkg{color} or the \pkg{graphicx} package. +% +% +% \section{Link decorations: border, color, OCG-color, \ldots} +% Some main changes are +% \begin{itemize} +% \item The default colors have been changed. +% \item |citecolor| is no longer set with |allcolors| and it is not part of the +% color scheme, it is only supported for compability. +% \item a number of color schemes have been predefined. +% \end{itemize} +% +% \subsection{Background information} +% With the standard drivers \pkg{hyperref} allows either to color the link text, +% or to use a border around it. +% There is also a (rather unknown) option |frenchlinks| to use small caps +% for some links instead of colors. +% +% The \emph{link border} is a setting in the PDF annotation directory. +% It can be colored and styled (with the |<xxx>bordercolor|, |pdfborderstyle| +% and |pdfhighlight| keys), but the exact look depends on the PDF viewer. +% Such decorations are normally not printed. +% +% The \emph{link text} is colored with the standard color commands for text. +% Such a color is also printed, which is often not wanted. The printing +% can be avoided in PDF with so-called OCG-layers: They allow to add +% variants of a text along with instructions which variant should be used for +% viewing and which for printing. \pkg{hyperref} implements a rather simple version +% for links: The link text is put in a box and printed twice with different colors +% on different OCG layers. As boxes are used such links can't be broken. The +% package \pkg{ocgx2} implements a more sophisticated version which allows to +% use it for links broken over lines and pages. +% +% \pkg{hyperref} has keys to set the color and border for |link|, |url|, |file|, +% |menu| and |run| types. They correspond to the PDF annotation types +% |GoTo|, |URI|, |GoToR|, |Named| and |Launch|. +% Beside this there is a |anchorcolor| which isn't used at all, and |citecolor| +% which is a semantical category and doesn't fit to the other types. +% +% In the standard drivers the decoration options are more or less exclusive and global: +% One of the options (|colorlinks|, |ocgcolorlinks|, or borders) has to be +% chosen in the preamble and is then used for the whole document +% and all link types. +% Only colors and eventually the border style can be adjusted locally. +% But there is no technical reason for these restrictions: +% It is quite possible to change all these attributes +% at any time both by link type and locally. The restrictions of the current +% implementation can only be explained by the age of the code: \pkg{hyperref} +% has been created at a time when memory was small and the main drivers were html +% and postscript based. +% +% While link colors have been traditionally more or less +% under the control of \pkg{hyperref}, +% the situation with other format options, like the font, is more complicated. +% The font in \cs{url} is for example determined by \cs{Urlfont}, +% a command from the \pkg{url} package. +% In the case of internal (GoTo) references packages like \pkg{cleveref} or +% \pkg{biblatex} or \pkg{glossaries} offer formatting options too. +% Formatting here is often connected to semantics: +% an acronym should use a different font than a citation. +% While \pkg{hyperref} could offer options here, it would probably only clash +% with package formatting. It is more sensible not to interfere here. For this +% reason the |frenchlinks| option has been dropped. +% +% \subsection{New Keys} +% Some of the existing keys have been extended to allow individual setting for +% the link types |link|, |url|, |file| |menu| and |run|: +% +% \begin{itemize} +% \item Beside |pdfborder| there are also |linkborder|, |urlborder| etc +% \item Beside |pdfhighlight| there are also |linkhighlight|, |urlhighlight| etc +% \item Beside |pdfborderstyle| there are also |linkborderstyle|, |urlborderstyle| etc +% \item Beside |colorlinks| there are also |colorlink|, |colorurl| etc +% \item Beside |ocgcolorlinks| there are also |ocgcolorlink|, |ocgcolorurl|, etc %TODO +% \item Beside |hidelinks| there are also |hidelink|, |hideurl|, etc +% \item |bordercolormodel| allows to set the model used in annotations, +% the allowed values are |rgb| or |cmyk|. |rgb| is the default. +% It does \emph{not} change the model of text colors. Be aware +% that while the PDF format allows cmyk (4 numbers) in the |/C| key of an annotation, +% this is often ignored by pdf viewers and the colors can be wrong. +% \item The boolean keys |url|, |link|, |run|, |menu|, |file| allow to deactivate +% locally the link types. +% \end{itemize} +% +% \begin{hypkey}{colorscheme} +% +% The new key |colorscheme| allows to switch the colors (both for text and borders) +% with a key word. It takes one of the values +% |orginal| (the colors as hyperref uses normally), |phelype|, |daleif|, +% |szabolcsA|, |szabolcsB|, |tivv|, |julian|, |henryford|. +% +% The names refer to the authors in answers and comments in +% \url{https://tex.stackexchange.com/questions/525261/better-default-colors-for-hyperref-links}. +% +% The default is |phelype|. +% \end{hypkey} +% +% \subsection{Public interfaces} +% The |colorlinks| and |ocgcolorlinks| and related keys are +% using these booleans: +% \begin{verbatim} +% \l_hyp_annot_colorlink_bool, +% \l_hyp_annot_colorurl_bool, +% \l_hyp_annot_colorfile_bool, +% \l_hyp_annot_colorrun_bool, +% \l_hyp_annot_colormenu_bool, +% \l_hyp_annot_ocgcolorlink_bool, +% \l_hyp_annot_ocgcolorurl_bool, +% \l_hyp_annot_ocgcolorfile_bool, +% \l_hyp_annot_ocgcolorrun_bool, +% \l_hyp_annot_ocgcolormenu_bool, +% \end{verbatim} +% +% They are both inserting hook code in the |pdfannot/link/|\meta{type}|/begin| +% and |pdfannot/link/|\meta{type}|/end| hooks. \meta{type} is one of +% |GoTo|, |URI|, |GoToR|, |Named| or |Launch|. +% +% |colorlinks| uses the label |hyp/color|, and |ocgcolorlinks| the label |hyp/ocg|. +% +% They both use the same color names: |hyp/color/link|, |hyp/color/url|, +% |hyp/color/file|, |hyp/color/run|, |hyp/color/menu|. +% +% The cite colors uses the names |hyp/color/cite| and |hyp/color/citeborder|. +% +% The border colors aren't saved in color names currently, but if the need +% would arise it would possible to change this. +% \subsection{Changed behaviour} +% \begin{description} +% \item[colorlinks] |colorlinks| will as before disable the |pdfborder|, but it is possible +% to use the key in the document at any time, or to reenable the border if wanted. +% Internally |colorlinks| \& friends will no longer define/undefine +% |\Hy@colorlink|, but instead use the hooks provided by the \pkg{l3pdfannot} package. +% \item Color keys accept the following input syntax: +% +% \begin{tabular}{ll} +% model based & |urlbordercolor = [rgb]{1,1,0}| \\ +% color expression & |urlbordercolor = red!50!blue| \\ +% command & |urlbordercolor = \mycolor| +% \end{tabular} +% +% where |\mycolor| should expand to one of the other two syntax variants. +% +% \item[frenchlinks] The option |frenchlinks| does nothing at all. +% \item[cite colors] As mentioned above the support for |citecolor| +% and |citebordercolor| has been reduced. A package like \pkg{hyperref} +% can't keep track of such semantic +% contexts like cite, acronym, glossaries and special references and maintain keys for +% them. The keys are not completly dropped as this would affect packages like +% \pkg{natbib}, but they have been separated and are no longer affected by +% group keys like |allcolors| but must be set individually instead. +% +% \item[link margin] The driver sets a default link margin---this is identical +% to pdftex and luatex driver, but a change for the xetex and dvips driver. +% The (undocumentated) command \cs{setpdflinkmargin} does nothing. +% Use either the key |pdflinkmargin| or \cs{pdfannot_link_margin:n} to change the margin. +% See also the description in section~\ref{sec:keydesc} and in the hyperref manual. +% \end{description} +% +% \section{PDF strings} +% +% \pkg{hyperref} uses a command called \cs{pdfstringdef} to convert text input into +% something that makes sense and is valid in a PDF string, e.g. in the bookmarks +% or in the info dictionary or as form field values. +% +% As the handling of the outlines are delegated to the \pkg{bookmark} package, they +% will for now still use \cs{pdfstringdef}, but all other strings produced by +% this driver will use a new method based on the expl3 commands +% \cs{text_purify:n} and \cs{str_set_convert:Nnnn}. For normal text +% it shouldn't matter, but a variety of commands and math are handled differently. +% Like with \cs{pdfstringdef} they are a number of ways to adjust the outcome of +% \cs{text_purify:n}. These are described in the expl3 documentation interface3.pdf. +% +% \emph{The new method is under heavy development!} +% +% Important differences here are +% \begin{itemize} +% \item \emph{This new method requires that files are utf8-encoded} +% (at least if non-ascii chars are used in for PDF strings). +% \item \emph{All} robust commands are currently removed, +% unless an equivalent has been declared. +% \item Currently the new method is much more silent: it doesn't warn like +% \pkg{hyperref} if it removes commands. +% \end{itemize} +% +% \section{Package options from hyperref}\label{sec:pkg-options} +% The driver will process the package options at the end. +% But normally options should better be set with \cs{hypersetup} after +% the package has been loaded. This is also the case for options which normally +% don't work in \cs{hypersetup}. +% One option that currently doesn't work correctly as package option is +% |ocgcolorlinks| +% +% +% Options that still must be set as package options are +% \begin{itemize} +% \item |backref| +% \item |CJKbookmarks| this key should not be used anymore. At some time +% it will be removed. +% \item |destlabels| (destination names are taken from \cs{label} if possible) +% \item |encap| +% \item |hyperfigures| (according to the \pkg{hyperref} manual +% it makes figures hyper links, but actually is a no-op for most drivers, and it +% does nothing with this driver either.) +% \item |hyperfootnotes| +% \item |hyperindex| +% \item |implicit| (redefine \LaTeX\ internals) +% \item |nesting| unneeded key, see comment below in \ref{sec:keydesc}. At some time +% it will be either removed or extended (if some use can be found). +% \item |pagebackref| +% \item |pdfpagelabels| (set PDF page labels) +% \item |psdextra| this loads some extra definitions used by \cs{pdfstringdef}. +% The new driver uses \cs{pdfstringdef} only for the bookmarks, for other strings +% it is not relevant. +% \end{itemize} +% +% Options that can be without problems set as package options are +% \begin{itemize} +% \item |debug|, |verbose| (a boolean) +% \item |bookmarks| (a boolean) +% \item |plainpages| +% \item |draft|, |final| +% \item |hypertexnames| +% \item |naturalnames| +% \item |pageanchor| +% \end{itemize} +% +% Ignored options: +% \begin{itemize} +% \item All driver options like |pdftex|, |dvipdfmx|, \ldots +% \item |raiselinks| (only used in the dviwind, textures and tex4ht driver anyway) +% \item |frenchlinks| +% \item |setpagesize| +% \item |addtopdfcreator| +% \end{itemize} +% +% \section{Disabling links} +% +% \pkg{hyperref} knows like many packages the options |draft| and |final|. +% With \pkg{hyperref} they can be used as package options or in the preamble +% in \cs{hypersetup} and disable links and anchors completely. +% The new driver passes the options also to the \pkg{bookmark} package if +% \pkg{bookmark} hasn't been loaded yet as bookmarks can't work properly if +% the anchors from hyperref are missing. +% +% \DescribeHypkey{link}% +% \DescribeHypkey{url}% +% \DescribeHypkey{file}% +% \DescribeHypkey{run}% +% \DescribeHypkey{menu}% +% The |draft| option is a global option that can't be undone (at least not easily). +% So the new driver offers also boolean keys |link|, |url|, |file|, |run| and |menu| +% which allow to locally disable a link type. So e.g. +% |\hypersetup{link=false}\ref{abc}| will give a reference without link (this +% is naturally also possible with |\ref*{abc}|). This disables also all hooks of +% the link type, so the link is for example no longer colored. It also removes the +% implicit grouping of the content. +% +% +% \section{Draftmode} +% pdftex and other engines knows a +% draftmode which can be set with |\pdfdraftmode=1| +% and \pkg{hyperref} honors this in some places. The new +% driver ignores it, for example |pagelabels| are created in any case. +% With today's computer power there is not much to gain and it only complicates +% the code. +% +% This should not be confused with the |draft| and |final| package options! They +% are still honored. +% +% \section{Dropped options} +% A number of options are ignored by this driver +% \begin{description} +% \item[pdfversion] The pdfversion should be set in \cs{DeclareDocumentMetadata} +% \item[setpagesize] The key is ignored and the PDF page size is not +% set. Load \pkg{color} or \pkg{graphicx} or use a class which sets the PDF page size. +% \item[breaklinks] The option does nothing sensible anyway (apart from triggering +% a warning). Currently with latex+dvips links can't be broken. But there is work +% in progress to change this. +% \item[unicode] This is always true. +% \item[pdfa] If this option is set to true \pkg{hyperref} normally checks and sets +% a small number of requirements for the PDF standard PDF/A. +% The key is ignored with this driver. Instead the wanted standard should be declared +% in \cs{DeclareDocumentMetadata}: +% \begin{verbatim} +% \DeclareDocumentMetadata{standard=A-2b} +% \end{verbatim} +% Currently |A-1b|, |A-2b|, |A-3b| can be set. +% The support for various requirements is still incomplete, but the parts that +% \pkg{hyperref} checked are implemented: +% \begin{itemize} +% \item The |/F| key is added to links and |Print| is activated, |Hidden|, |Invisible|, +% |NoView| are deactivated. +% \item |/NeedAppearances| is suppressed +% \item Pushbuttons, which use the action |/S/JavaScript| are suppressed. +% \item Resetbuttons, which use the action |/S/ResetForm| are suppressed. +% \item In widget annotations, the /AA dictionary is suppressed. +% \end{itemize} +% \end{description} +% \section{Destinations} +% Destinations (sometimes call anchors in the \pkg{hyperref} documentation) +% are the places a link jumped too. Unlike the name may suggest they don't described +% an exact location in the PDF. Instead a destination contains a reference to +% a page along with an instruction how to display this page. +% The normally used \enquote{XYZ \textit{top left zoom}} for example instructs +% the viewer to show the page with the given \textit{zoom} and +% the top left corner at the \textit{top left} coordinates---which then gives +% the impression that there is an anchor at this position. +% +% From these instructions two (|Fit| and |FitB|) don't take an argument. +% All others take one (|FitH|, |FitV|, |FitBH|, |FitBV|) +% or more (|XYZ|, |FitR|) arguments. These arguments are normally +% coordinates, |XYZ| takes also a zoom factor. The coordinates are +% absolute coordinates in |bp| relative to the lower left corner +% of the PDF. +% +% With the primitive command \cs{pdfdest} of pdftex almost all instructions are created +% with a keyword only: The needed coordinate is calculated automatically from the location +% the \cs{pdfdest} command is issued. So to get a specific coordinate one has to +% move the command to the right place. E.g. +% \begin{verbatim} +% \AddToHookNext{shipout/background} +% {\put(0,-\pdfpageheight+100bp){\pdfdest name{destA} FitH\relax}} +% \end{verbatim} +% +% Exceptions are the |XYZ| instruction, where pdftex accepts a keyword +% |zoom| followed by a zoom factor, and the |FitR| instruction +% which understands the keywords |width|, |height| and |depth| +% followed by a dimension, which is then used to calculate a rectangle relative to the +% current location. If no keywords are given the dimensions are taken from the surrounding +% box---which can also lead to zero sized areas. +% +% The manual of \pkg{hyperref} gives a bit the impression as if this +% coordinates can be set manually by the user but as described above this is +% mostly wrong: It is for normal destination only possible with a dvi-backend like +% dvips which make use of |pdfmark.def|. pdftex and luatex can use manual coordinates +% only for |pdfstartview| and |pdfremotestartview|. +% As dvips was the first driver of \pkg{hyperref} the option |pdfview| was at first +% developed for it and then adapted to pdftex. But this had the effect that the handling +% of the option |pdfview| is inconsequent across the backend and engines: +% For example with |pdfview=FitH 100| pdftex ignores the +% number and calculates its own, while dvips sets the coordinate to the absolute +% 100. The zoom factor of |XYZ| is not supported by the pdftex driver at all, and +% |FitR| only partially. +% +% The generic driver consolidate this but tries to stay compatible with the other +% drivers as far as possible. +% It also takes into account that |pdfview| and |pdfstartview| and |pdfremotestartview| +% have different requirements: While for the first relative coordinates are fine, +% for the two others absolute coordinates are more sensible. +% +% \DescribeHypkey{pdfview} +% \DescribeHypkey{pdfstartview} +% \DescribeHypkey{pdfremotestartview} +% So with this driver the options +% |pdfview|, |pdfstartview| and |pdfremotestartview| take +% the following options: +% \begin{itemize} +% \item |Fit|, |FitB|, |FitH|, |FitV|, +% |FitBH|, |FitBV| which can be followed by a positive integer (separated by a space) or the +% keyword |null|. +% The number can be gives as a \meta{dimension expression} surrounded with +% \cs{hypercalcbp}. The driver redefines this command to use +% \cs{dim_to_decimal_in_bp:n}. +% \begin{itemize} +% \item |pdfview| will ignore the integer and any other arguments and calculate +% the expected coordinates as described above for pdftex with all +% supported engines and backends. +% \item |pdfstartview| and |pdfremotestartview| will pass the optional +% number or keyword after expansion as absolute coordinate. Missing numbers will +% be filled up with |null|. +% \end{itemize} +% +% \item |XYZ|. This can be followed (separated by spaces) by up to three +% positive integers or keywords |null| which are then taken as \textit{top left zoom} +% in this order. \textit{zoom} is a factor, so e.g. 0.5 will give a scaling of 50\%. +% \begin{itemize} +% \item |pdfview| will use the last value as \textit{zoom}, ignore all other values +% and calculate the expected coordinates as described above for pdftex with all +% supported engines and backends (this means it is possible to use |XYZ 2| to +% set a zoom of 200\%, it is not necessary to fill in dummy values.) +% \item |pdfstartview| and |pdfremotestartview| will pass the optional +% numbers or keyword after expansion as absolute coordinates and zoom. +% Missing numbers will be filled up with |null|. +% \end{itemize} +% This new behaviour is in part incompatible with previous handling with the dvips driver. +% +% +% \item |FitR|. +% If no argument (separated by spaces) follows then +% |pdfview| will use with pdftex and luatex +% the automatic calculation of the coordinates from the encompassing box. With +% dvips and (x)dvipdfmx it will fall back to |Fit|. +% |pdfstartview| and |pdfremotestartview| will fallback to |Fit| too. +% +% If arguments (separated by spaces) follow they should be +% four numbers representing \texttt{left bottom right top}. +% \begin{itemize}\item +% |pdfview| will use the values to calculate coordinates relative to the current +% location. So |0 -100 200 400| will give a \enquote{box} of width 200bp, +% height 400bp and depth 100dp that the destination should encompass. +% Missing numbers will be set to 0. But one should be aware that is it is quite +% unpredicable how viewers which support |FitR| handles zero sizes. +% +% \item |pdfstartview| and |pdfremotestartview| will pass +% the values as absolute coordinates. +% \end{itemize} +% \end{itemize} +% +% +%\section{Assorted key descriptions}\label{sec:keydesc} +%The following gives a few details to some keys that are perhaps not +%completly described in the manual, or are a bit different in this driver. +%The list is alphabetic. +% +% \DescribeHypkey{bookmarkstype} This key takes as value the extension of a list +% like |toc| or |lof|. If this list uses \cs{addcontentsline} the content will +% be added to the bookmarks. The key can be use in \cs{hypersetup} and also in +% the middle of the document to switch the list. +% +% \DescribeHypkey{bordercolormodel} With |bordercolormodel| the colormodel used in the |/C| +% key of the annotation array and in similar keys is set. It does not affect +% the text and graphics colors in the page stream. +% Possible choices are |rgb| (three numbers in the array) and |cmyk| (four numbers). +% While the PDF reference allows four numbers, PDF readers don't necessarly handle +% this correctly, so the value can be wrong. +% +% \DescribeHypkey{destlabel} This is a boolean key. Currently it must be set +% as package option. If set to true, the name +% of a destination is taken from a following \cs{label}, if there is one before +% the next destination command. This requires two compilations to get +% the correct coordinates in the destination. In the first compilation +% the alias name is recorded in the aux-file: +% \begin{verbatim} +% \hyper@newdestlabel{section.1.2}{sec:sec2} +% \end{verbatim} +% The next compilation can then make use of it. +% The two-pass could be avoided in the future with a better labeling system, +% where the name if set earlier. +% +% \DescribeHypkey{extension} This key sets an variable that has two purposes: +% It is used if file name has not extension, and it decides if the annotation +% is a URI or GoToR annotation. So +% \begin{verbatim} +% \hypersetup{extension=dvi} +% \href{mwe1.pdf}{pdf} +% \href{mwe2.dvi}{dvi} +% \href{mwe3}{no ext} +% \end{verbatim} +% will create +% \begin{verbatim} +% /Subtype/Link/A<</S/URI /URI(mwe1.pdf)>> +% /Subtype/Link/A<</S/GoToR /F (mwe2.dvi)>> +% /Subtype/Link/A<</S/GoToR /F (mwe3.dvi)>> +% \end{verbatim} +% Typically PDF viewer can handle only GoToR annotions pointing to a PDF. +% So normally the default value |pdf| of this key should not be changed. +% \DescribeHypkey{nesting}\label{key:nesting}% +% This key is useless in PDF context. The boolean is only used in +% the code for anchors/destination where nesting doesn't make sense. +% It should not be changed. +% +% \DescribeHypkey{pdfborder}\DescribeHypkey{linkborder}\DescribeHypkey{urlborder} +% \DescribeHypkey{runborder}\DescribeHypkey{menuborder} +% This key set accept as value three numbers or three numbers and an array describing +% a dash pattern, examples are |0 0 1| or |0 0 1 [3 2]|. +% The first two numbers should according to the reference set round +% corners, but PDF viewer seem to ignore it. The third number is the line width +% of the border. Settings done with |pdfborderstyle| should take precedence. +% +% \DescribeHypkey{pdfborderstyle}\DescribeHypkey{linkborderstyle} +% \DescribeHypkey{urlborderstyle}\DescribeHypkey{fileborderstyle} +% \DescribeHypkey{runborderstyle}\DescribeHypkey{menuborderstyle} +% The value of this key is the content of the |BS| dictionary. +% As an example |/Type/Border /W 1 /S/U /D[3 2]| +% +% \begin{tabular}{>{\ttfamily} lll} +% Key & Values & comment / example\\\hline +% /Type & |/Border| & optional \\ +% /W & \meta{number} & Width of border line \\ +% /S & |/S| & solid (default)\\ +% & |/D| &dash pattern can be set with /D\\ +% & |/B| & beveled\\ +% & |/I| & inset\\ +% & |/U| & underline\\ +% /D & \meta{array of numbers} & dash pattern (lines/gaps) (default [3]) +% \end{tabular} +% +% \DescribeHypkey{pdfcreationdate}\DescribeHypkey{pdfmoddate} +% Setting these keys is normally not needed. If they are used the values +% are stored directly in the Info dictionary for |/Creationdate| and |/ModDate|. +% The values are converted to strings but not processed further, so they should +% have the correct PDF format without the enclosing parentheses, +% e.g. |D:20200202111111+01'00'|. +% +% \DescribeHypkey{pdflinkmargin}As described in the \pkg{hyperref} manual +% the behaviour differs between the backends: with dvips it is possible to +% change links locally, pdflatex and lualatex work by page, with dvipdfmx +% the setting is global (and has to be done in the preamble). +% +% \DescribeHypkey{pdflang} The key will work, but it is recommended to the set +% the language in \cs{DeclareDocumentMetadata} instead. +%^^A %% This is an adapted version of hluatex.def +%^^A %% meant to test the use of the commands +%^^A %% from pdfmanagement.sty +%^^A %% +%^^A %% drivers are loaded in line 4745 in hyperref.sty in a \Hy@AtEndOfPackage command. +%^^A %%%%%%%%%%%%%%%%%%%%%%%%%%%% +%^^A %% list of new internal commands +%^^A %% __hyp_link_goto_begin:nw : start command for links to internal destination +%^^A %% replaces \find@pdflink +%^^A %% __hyp_link_goto_end: +%^^A %% __hyp_destination:n : sets an anchor, replaces \new@pdflink +%^^A %% \__hyp_PageLabels_gpush: : puts pagelabels in the catalog, used on every storing +%^^A %% PDF String (text) +%^^A %% *\@@_text_pdfstring:nnN : replaces Hy@pstringdef, converts #1 to pdfstring in encoding #2 +%^^A %% and stores in N +%^^A %% Build with: +%^^A %% *\@@_text_purify:nN +%^^A %% *\@@_text_cleanup:N +%^^A %% *\@@_text_convert:nN +%^^A %% +%^^A %% Destinations (dest) +%^^A %% \l_@@_dest_box +%^^A %% +%^^A %% References (ref) +%^^A %% *\@@_ref_label:nn +%^^A %% *\@@_ref_if_exist:nn +%^^A %% *\@@_ref_check:nn +%^^A %% % helps to display key list messages +%^^A %% \@@_clist_display:n +%^^A %% +%^^A %% \__hyp_info_generate_addons: what did this do?? +%^^A %% +%^^A %% \g__hyp_AcroForm_CoFields_prop +%^^A %% \g__hyp_AcroForm_Fields_prop +%^^A %% +%^^A %% \g_@@_dest_pdfstartpage_tl , +%^^A %% \g_@@_dest_pdfstartview_tl , +%^^A %% \l_@@_dest_pdfremotestartview_tl , +%^^A +%^^A %% Constants +%^^A %% *\c_@@_map_annot_hyp_prop +%^^A %% *\c_@@_dest_undefined_tl +%^^A %% Temp variables +%^^A %% \l_@@_tmpa_seq +%^^A %% \l_@@_tmpa_box +%^^A %% \l_@@_tmpa_tl +%^^A %% \l_@@_tmpa_str +%^^A %% \l_@@_tmpa_int +%^^A %% +%^^A %% \l_@@_dest_name_tmpa_tl +%^^A %% \l_@@_uri_tmpa_tl +%^^A %% \l_@@_filename_tmpa_tl +%^^A %% \l_@@_para_tmpa_tl +%^^A %% \l_@@_text_tmpa_str +%^^A %% \g_@@_text_tmpa_str +%^^A %% \l_@@_CheckmarkYes_tl +%^^A %% \l_@@_CheckmarkOff_tl +%^^A %% \l_@@_RadioYes_tl +%^^A %% \g_@@_bordercolormodel_tl +% +%^^A %% \l_@@_dest_pdfview_tl +%^^A %% list of commands which probably will have to change +% \end{documentation} +% \begin{implementation} +% \part{\pkg{hyperref-generic} driver implementation} +% \begin{macrocode} +%<*package> +%<@@=hyp> +% \end{macrocode} +% \begin{macrocode} +\ProvidesFile{hgeneric-testphase.def} + [2021/02/22 v0.95a % + generic Hyperref driver for the LaTeX PDF management testphase bundle] + +\RequirePackage{etoolbox} %why? +% \end{macrocode} +% \begin{macrocode} +\ExplSyntaxOn +\file_input:n {hyperref-colorschemes.def} +\ExplSyntaxOff +% \end{macrocode} +% +% +% \section{messages} +% Redirect the message name: +% \begin{macrocode} +\ExplSyntaxOn +\prop_gput:Nnn \g_msg_module_name_prop { hyp }{ hyperref } +% \end{macrocode} +% At first a message for the testing of the resource management +% \begin{macrocode} +\msg_new:nnnn + { hyp } + { missing-resource-management } + { The~PDF~resource~management~is~required~for~this~hyperref~driver! } + { + Activate~it~with \\ + \tl_to_str:n{\RequirePackage{pdfmanagement-testphase}}\\ + \tl_to_str:n{\DeclareDocumentMetadata{<options>}}\\ + before~\tl_to_str:n{\documentclass} + } +% \end{macrocode} +% The pdfversion should be set in \cs{DeclareDocumentMetadata} +% \begin{macrocode} +\msg_new:nnnn + { hyp } + { pdfversion-disabled } + { + This~hyperref~driver~ignores~the~pdfversion~key!\\ + Set~the~pdfversion~in~\token_to_str:N \DeclareDocumentMetadata + } + { + For example:\\ + \tl_to_str:n + { + \DeclareDocumentMetadata { pdfversion=1.7 } + } + } +% \end{macrocode} +% A generic message for ignored keys. +% \begin{macrocode} +\msg_new:nnn + { hyp } + { key-dropped } + { + This~hyperref~driver~ignores~the~key~#1!\\ + Please~check~the~documentation. + } +% \end{macrocode} +% pdf/A messages for fields, this will probably be moved to an external package +% \begin{macrocode} +\msg_new:nnn + { hyp } + { pdfa-no-push-button } + { PDF/A:~Push~button~with~JavaScript~is~prohibited } + +\msg_new:nnn + { hyp } + { pdfa-no-reset-button } + { PDF/A:~Reset~action~is~prohibited } +% \end{macrocode} +% pdf/A message for not allowed Named actions +% \begin{macrocode} +\msg_new:nnn + { hyp } + { pdfa-no-named-action } + { PDF/A:~Named~action~#1~is~prohibited } +% \end{macrocode} +% A message if the destination name is empty. +% \begin{macrocode} +\msg_new:nnn + { hyp } + { empty-destination-name } + { + Empty~destination~name,\\ + using~`#1' + } +% \end{macrocode} +% A message if the destination check fails +% \begin{macrocode} +\msg_new:nnn + { hyp } + { invalid-destination-value } + { + Invalid~value~`#1'~of~`#2' \\ + is~replaced~by~`Fit'~\msg_line_context:. + } +% \end{macrocode} +% Some options or values should not be used in older pdf versions +% \begin{macrocode} +\msg_new:nnn + { hyp } + { ignore-deprecated-or-unknown-option-in-pdf-version } + { + Option~`#1'~is~unknown~or~deprecated~in\\ + pdf~version~#2.~Ignored. + } +\msg_new:nnn + { hyp } + { ignore-deprecated-or-unknown-value-in-pdf-version } + { + Value~`#1'~is~unknown~or~deprecated~in\\ + pdf~version~#2.~Ignored. + } +\msg_new:nnn + { hyp } + { replace-deprecated-or-unknown-value-in-pdf-version } + { + Value~`#1'~is~unknown~or~deprecated~in\\ + pdf~version~#2. Value~`#3'~is used instead. + } +% \end{macrocode} +% During development not all standard hyperref keys are known and the +% Hyp-handler needs to process some new keys unknown to him. +% This issues warnings for now: +% \begin{macrocode} +\msg_new:nnn + { hyp } + { unknown-key } + { + unknown~key~#2~of~module~’#1’~set~to~’#3’. + } +\msg_new:nnn + { hyp } + { unknown-key-to-Hyp } + { + ignored~in~family~Hyp~unknown~key~#1. + } +% \end{macrocode} +% There are a lot choice keys. This defines messages which shows the valid +% choices if a faulty one has been used: +% \begin{macrocode} +\cs_new:Npn \@@_clist_display:n #1 {*~#1\\} +\msg_new:nnn + { hyp } + { unknown-choice } + { + Value~'#3'~is~invalid~for~key~'#1'.\\ + The~key~accepts~only~the~choices\\ + \clist_map_function:nN { #2 }\@@_clist_display:n + } + +\msg_new:nnn + { hyp } + { unknown-choice+empty } + { + Value~'#3'~is~invalid~for~key~'#1'.\\ + The~key~accepts~only~the~choices\\ + \clist_map_function:nN { #2 }\@@_clist_display:n + An~empty~value~removes~the~setting. + } + +\msg_new:nnn + { hyp } + { no-bool } + { + Value~'#2'~is~invalid~for~key~'#1'.\\ + The~key~accepts~only~the~choices\\ + *~true\\ + *~false \\ + *~and~an~empty~value~which~removes~the~setting.\\ + No~value~is~equivalent~to~using~`true`. + } +% \end{macrocode} +% \section{Variants} +% \begin{macrocode} +\cs_generate_variant:Nn\pdf_destination:nn {nf} +% \end{macrocode} +% \section{Overwriting/providing commands from hyperref} +% hyperref checks driver version, we need to suppress this during the development +% \begin{macrocode} +\chardef\Hy@VersionChecked=1 %don't check the version! +% \end{macrocode} +% \begin{macrocode} +%\cs_set_protected:Npn \PDF@SetupDoc{} +%\\PDF@FinishDoc{}% dummy needed for hyperref ... +% \end{macrocode} +% \begin{macro}[EXP]{\hypercalcbp} +% We define a better (expandable) version of \cs{hypercalcbp} +% \begin{macrocode} +\cs_set_eq:NN \hypercalcbp \dim_to_decimal_in_bp:n +% \end{macrocode} +% \end{macro} +% +% This command must be provided for now, but they are unused by the driver: +% \begin{macrocode} +\providecommand\@pdfborder{} +\providecommand\@pdfborderstyle{} +\newcommand\OBJ@OCG@view {} % needed in hyperref +\def\Hy@numberline#1{#1\c_space_tl} %needed by bookmark +% \end{macrocode} +% +% We force unicode as option. As bookmarks are still using \cs{pdfstringdef} +% we also need to load puenc.def. +% \begin{macrocode} +\HyPsd@LoadUnicode +\Hy@unicodetrue +\let\HyPsd@pdfencoding\HyPsd@pdfencoding@unicode +\Hy@DisableOption{unicode} +% \end{macrocode} +% The pdfversion should be set in \cs{DeclareDocumentMetadata} but we must +% copy it to the \pkg{hyperref} command: +% \begin{macrocode} +\cs_set_eq:NN \Hy@pdfminorversion \pdf_version_minor: +\cs_set_eq:NN \Hy@pdfmajorversion \pdf_version_major: +% \end{macrocode} +% \begin{macrocode} +\legacy_if:nT { Hy@setpdfversion } + { + \msg_warning:nn { hyp }{ pdfversion-disabled } + } +\Hy@DisableOption{pdfversion} +% \end{macrocode} +% \cs{Acrobatmenu} should use the new internal link command +% \begin{macrocode} +\RenewDocumentCommand \Acrobatmenu { m m } + { + \hyper@linknamed {#1} {#2} + } +% \end{macrocode} +% +% \cs{hypersetup} should set the new keys. +% We can't also execute |\kvsetkeys{Hyp}| as this errors for example with colors. +% This means the driver has to provide new code for every key! +% +% \begin{macrocode} +% TODO should go at some time ... +% \kv@set@family@handler{Hyp} +% { \msg_warning:nnx {hyp}{unknown-key-to-Hyp}{#1} } +\cs_set_protected:Npn \hypersetup #1 + { + %\kvsetkeys{Hyp} {#1} + \keys_set:nn { hyp / setup }{ #1 } + } +% TODO for now unknown keys should only give warnings. +\keys_define:nn { hyp / setup } + { + unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-key } + { hyp~/~setup }{ \l_keys_key_str } { #1 } + } + } +% \end{macrocode} +% +% \section{Compability commands} +% \subsection{Metadata} +% A number of values should be accessible from other packages. Until now +% packages like \pkg{hyperxmp} used variables like \cs{@pdfauthor}. As they are +% gone we need to provide some other access. +% \begin{macrocode} +\cs_new_protected:Npn \@@_store_metadata:nn #1 #2 %#1 key, #2 value. + { + %\tl_set:cn {@#1}{#2} + \AddToDocumentProperties[hyperref]{#1}{#2} + } +\cs_generate_variant:Nn \@@_store_metadata:nn {xn,nx,xx} +% \end{macrocode} +% \subsection{citecolor} +% cite is a link context. So we define a hook, and the keys in terms of this hook. +% +% \begin{macrocode} +\hook_new:n{hyp/link/cite} +\color_set:nnn {hyp/color/cite}{HTML}{2E7E2A} +\color_set:nn {hyp/color/citeborder}{hyp/color/cite!60!white} +\keys_define:nn { hyp / setup } + { + ,citecolor .code:n = {\@@_color_set:nn {hyp/color/cite}{#1}} + ,citebordercolor + .code:n = {\@@_color_set:nn {hyp/color/citeborder}{#1}} + } +\hook_gput_code:nnn { hyp/link/cite }{hyp/cite} + { + \keys_set:nn { hyp / setup } + { + ,linkbordercolor= hyp/color/citeborder + ,linkcolor = hyp/color/cite + } + } +% \end{macrocode} +% \section{Checks} +% The driver can not work properly if the pdfmanagement is not active, +% as keys need to write to the catalog and to info. But annotations +% and outlines should work. +% So should this be a fatal error? +% Should there be a difference between missing and inactive management? +% TODO +% \begin{macrocode} +\bool_lazy_and:nnF + { \cs_if_exist_p:N \pdfmanagement_if_active_p: }{ \pdfmanagement_if_active_p: } + { \msg_error:nn { hyp}{ missing-resource-management } } +% \end{macrocode} +% Outlines/bookmarks require the bookmark package. +% TODO check pdfpagemode if booksmarks are suppressed. +% \begin{macrocode} +\legacy_if:nT { Hy@bookmarks } + { + \AddToHook{begindocument/before}[hyperref/bookmark]{\RequirePackage{bookmark}} + } +\legacy_if:nT {Hy@draft} + { + \PassOptionsToPackage{draft}{bookmark} + } +% \end{macrocode} +% +% \section{Reference and label commands} +% The code uses the l3ref-tmp package which +% must have been loaded as pdfmanagement is a requirement. +% The commands use after the module prefix always |_ref|. +% +% At first a label command which add the space commands from LaTeX: +% \begin{macro} +% { +% \@@_ref_label:nn, +% \@@_ref_if_exist:nn, +% \@@_ref_check:nn, +% \@@_ref_value:nn +% } +% \begin{macrocode} +% +\cs_new_protected:Npn \@@_ref_label:nn #1 #2 %label/attributes + { + \@bsphack + \ref_label:nn{#1}{#2} + \@esphack + } +\cs_generate_variant:Nn \@@_ref_label:nn {en} +% \end{macrocode} +% This provides a condition which tests if a label/attribute combination is known +% \begin{macrocode} +\prg_new_eq_conditional:NNn \@@_ref_if_exist:nn \ref_if_exist:nn { p , T , F, TF } +\prg_generate_conditional_variant:Nnn \@@_ref_if_exist:nn {en} { p , T , F, TF } +% \end{macrocode} +% This checks if the label/attribute is known and issues a warning if not. +% It then also triggers the standard rerun message. +% \begin{macrocode} +\cs_new_protected:Npn \@@_ref_check:nn #1 #2 %label/attribute + { + \@@_ref_if_exist:nnF {#1}{#2} + { + \protect\G@refundefinedtrue + \@latex@warning + { + Reference~`\tl_to_str:n {#1}'~with~attribute~`\tl_to_str:n {#2}'~ + on~page~\thepage~\space undefined + } + } + } +\cs_generate_variant:Nn \@@_ref_check:nn {en} +% \end{macrocode} +% This retrieves a value, it is a simple wrapper around the \cs{ref_value:nn} +% \begin{macrocode} +\cs_new:Npn \@@_ref_value:nn #1 #2 + { + \ref_value:nn{#1}{#2} + } +\cs_generate_variant:Nn \@@_ref_value:nn {en} +% \end{macrocode} +% \end{macro} +% \section{Variables} +% \subsection{Private temporary variables} +% At first a few generic tmp variables +% \begin{variable} +% { +% \l_@@_tmpa_tl, +% \l_@@_tmpa_seq, +% \l_@@_tmpa_int, +% \l_@@_tmpa_box, +% \l_@@_tmpa_str, +% } +% \begin{macrocode} +\box_new:N \l_@@_tmpa_box +\tl_new:N \l_@@_tmpa_tl +\seq_new:N \l_@@_tmpa_seq +\int_new:N \l_@@_tmpa_int +\str_new:N \l_@@_tmpa_str +% \end{macrocode} +% \end{variable} +% +% A number of more specific tmp variables. These will perhaps disappear or change. +% \begin{variable} +% { +% \l_@@_dest_name_tmpa_tl, +% \l_@@_uri_tmpa_tl, +% \l_@@_filename_tmpa_tl, +% \l_@@_para_tmpa_tl +% \l_@@_text_tmpa_str +% \g_@@_text_tmpa_str +% } +% TODO: document and check use! +% \begin{macrocode} +\tl_new:N \l_@@_dest_name_tmpa_tl +\tl_new:N \l_@@_uri_tmpa_tl +\tl_new:N \l_@@_filename_tmpa_tl +\tl_new:N \l_@@_para_tmpa_tl +\str_new:N \l_@@_text_tmpa_str +\str_new:N \g_@@_text_tmpa_str +% \end{macrocode} +% \end{variable} +% +% \subsection{Constants} +% \begin{variable} +% { \c_@@_dest_undefined_tl } +% This variable is used if a destination name is empty. +% \begin{macrocode} +\tl_const:Nn \c_@@_dest_undefined_tl {UNDEFINED} +% \end{macrocode} +% \end{variable} +% \begin{variable} +% { +% \c_@@_annot_types_seq, +% \c_@@_map_annot_hyp_prop, +% \c_@@_map_hyp_annot_prop, +% } +% This constants holds the link types managed by hyperref +% along with a mapping from annot names to hyperref names and back. +% \begin{macrocode} +\seq_const_from_clist:Nn \c_@@_annot_types_seq + {url,link,file,menu,run} +\prop_const_from_keyval:Nn \c_@@_map_annot_hyp_prop + { + URI = url, + GoTo = link, + GoToR = file, + Named = menu, + Launch= run + } +\prop_const_from_keyval:Nn \c_@@_map_hyp_annot_prop + { + url = URI, + link = GoTo, + file = GoToR, + menu = Named, + run = Launch + } + +% \end{macrocode} +% \end{variable} +% \subsection{Variables} +% \begin{variable} +% { +% \g_@@_dest_pdfstartpage_tl , +% \g_@@_dest_pdfstartview_tl , +% \l_@@_dest_pdfremotestartview_tl , +% } +% The first holds the (absolute) start page number, +% the other the startview instruction for the current and remote files. +% The instruction is in \enquote{PDF format} but without the leading slash! +% \begin{macrocode} +\tl_new:N \g_@@_dest_pdfstartpage_tl +\tl_new:N \g_@@_dest_pdfstartview_tl +\tl_new:N \l_@@_dest_pdfremotestartview_tl +% \end{macrocode} +% \end{variable} +% +% It is still unclear which str convert option is the best in the various +% places, so we use a variable to allow tests and perhaps external configuration. +% The \enquote{print} type should always have the delimiters. +% \begin{variable} +% { +% \l_@@_text_enc_uri_print_tl, +% \l_@@_text_enc_info_print_tl, +% \l_@@_text_enc_dest_tl, +% \l_@@_text_enc_dest_print_tl, +% \l_@@_text_enc_file_print_tl, +% \l_@@_text_enc_para_print_tl +% } +% \begin{macrocode} +\tl_new:N \l_@@_text_enc_uri_print_tl +\tl_new:N \l_@@_text_enc_info_print_tl +\tl_new:N \l_@@_text_enc_dest_tl +\tl_new:N \l_@@_text_enc_dest_print_tl +\tl_new:N \l_@@_text_enc_file_print_tl +\tl_new:N \l_@@_text_enc_para_print_tl + +\tl_set:Nn \l_@@_text_enc_uri_print_tl {utf8/URI} +\tl_set:Nn \l_@@_text_enc_info_print_tl {utf16/hex} +\tl_set:Nn \l_@@_text_enc_dest_tl {utf8/string-raw} +\tl_set:Nn \l_@@_text_enc_dest_print_tl {utf8/string} +\tl_set:Nn \l_@@_text_enc_file_print_tl {utf8/string} +\tl_set:Nn \l_@@_text_enc_para_print_tl {utf8/string} +% \end{macrocode} +% \end{variable} +% \begin{variable}{\l_@@_dest_pdfview_tl} +% This hold the destination instructions in a format suitable for +% \cs{pdf_destination:nn}. The special value |fitrbox| indicates a boxed destination. +% \begin{macrocode} +\tl_new:N \l_@@_dest_pdfview_tl +% \end{macrocode} +% \end{variable} +% \begin{hypcolor} +% { +% hyp/annot/link, +% hyp/annot/url, +% hyp/annot/file, +% hyp/annot/run, +% hyp/annot/menu, +% } +% These color names are used for the annotations (colorlinks). They are initialized +% at the end when the color scheme is used +% \end{hypcolor} +% \begin{variable}{\g_@@_bordercolormodel_str} +% This holds the export model for border color etc. +% It is currently either |space-sep-cmyk| or |space-sep-rgb|. +% The default is the second. It can be change by the key |bordercolormodel| +% \begin{macrocode} +\str_new:N \g_@@_bordercolormodel_str +% \end{macrocode} +% \end{variable} +% \subsection{Booleans} +% \begin{variable} +% { +% \l_hyp_annot_colorlink_bool, +% \l_hyp_annot_colorurl_bool, +% \l_hyp_annot_colorfile_bool, +% \l_hyp_annot_colorrun_bool, +% \l_hyp_annot_colormenu_bool, +% } +% These booleans are needed to control the colors. +% They are public so that other packages can query the state too. +% \begin{macrocode} +\seq_map_inline:Nn \c_@@_annot_types_seq + { + \bool_new:c {l_hyp_annot_color#1_bool} + } +% \end{macrocode} +% \end{variable} +% \begin{variable} +% { +% \l_hyp_annot_ocgcolorlink_bool, +% \l_hyp_annot_ocgcolorurl_bool, +% \l_hyp_annot_ocgcolorfile_bool, +% \l_hyp_annot_ocgcolorrun_bool, +% \l_hyp_annot_ocgcolormenu_bool, +% } +% These booleans are needed to control the ocgcolors. +% They are public so that other packages can query the state too. +% \begin{macrocode} +\seq_map_inline:Nn \c_@@_annot_types_seq + { + \bool_new:c {l_hyp_annot_ocgcolor#1_bool} + } +% \end{macrocode} +% \end{variable} +% \begin{variable} +% { +% \l_@@_annot_GoTo_bool +% \l_@@_annot_URI_bool +% \l_@@_annot_GoToR_bool +% \l_@@_annot_Named_bool +% \l_@@_annot_Launch_bool +% } +% This booleans are used to disable some link types +% while keeping others. +% \begin{macrocode} +\seq_map_inline:Nn \c_pdfannot_link_types_seq + { + \bool_new:c {l_@@_annot_#1_bool} + \bool_set_true:c {l_@@_annot_#1_bool} + } +% \end{macrocode} +% \end{variable} +% +% \subsection{Boxes} +% \begin{variable}{\l_@@_dest_box} +% This holds an (empty) box which is used to get the width for FitR destinations. +% \begin{macrocode} +\box_new:N \l_@@_dest_box +% \end{macrocode} +% \end{variable} + +% \subsection{Regex} +% \begin{variable}{\c_@@_dest_startview_regex} +% This regex is used to extract the right arguments +% pdfstartview and pdfremotestartview. Their values is filled up with |null| +% and then the start extracted. +% \begin{macrocode} +\regex_const:Nn \c_@@_dest_startview_regex + { + \A\ * + (?: + (?:XYZ (?:\ +(?:(?:\d+|\d*\.\d+)|null)){3}\ ) + | + (?:Fit\b|FitB\b) + | + (?:(?:FitH|FitV|FitBH|FitBV)(?:\ +(?:\d+|\d*\.\d+)|\ +null){1}) + | + (?:FitR (?:\ +\d+|\ +\d*\.\d+){4}\ ) + ) + } +% \end{macrocode} +% \end{variable} +% +% \subsection{PDF dictionaries} +% \begin{variable}{l_@@_page/Trans} +% This dictionary is used for page transitions. +% \begin{macrocode} +\pdfdict_new:n {l_@@_page/Trans} +\pdfdict_put:nnn {l_@@_page/Trans}{Type}{/Trans} +% \end{macrocode} +% \end{variable} + +% \section{PDF string conversion} +% +% This defines a command which is used to replace +% \cs{pdfstringdef}. This is probably temporary and will be adjusted or +% replaced if some more generic PDF string command/module exists. +% All commands here use the \enquote{submodule} name \texttt{text}. +% At first a hook for user additions: +% \begin{macro}{hyp/text/pdfstring} +% \begin{macrocode} +\hook_new:n {hyp/text/pdfstring} +% \end{macrocode} +% \end{macro} +% The first step to convert input in a PDF string is to purify it, that means +% to remove/expand commands. As the whole process is not expandable anyway we +% can use a protected command. The \enquote{output} is a string: +% \begin{macro}{\@@_text_purify:nN} +% \begin{macrocode} +\cs_new_protected:Npn \@@_text_purify:nN #1 #2 %#1 input, #2 str command + { + \str_set:Nx #2 {\text_purify:n { #1 } } + } +% \end{macrocode} +% \end{macro} +% The second step is to cleanup the output of the first step. This is a dummy +% currently. The argument should be a string variable. +% \begin{macro}{\@@_text_cleanup:N} +% \begin{macrocode} +\cs_new_protected:Npn \@@_text_cleanup:N #1 + { + + } +% \end{macrocode} +% \end{macro} +% The last step converts the string to a PDF encoding. As we have at least two +% targets (hex and literal) there is an argument. The conversion assumes +% utf8 input, it is based on cs{pdf_string_from_unicode:nnN} in l3pdftools. +% +% \#2 is str variable, +% \#1 should be one of +% +% \begin{tabular}{ll} +% utf8/string & \texttt{(lit)} (utf8/string)\\ +% utf8/string-raw & \texttt{lit} (utf8/string)\\ +% utf8/URI & \texttt{(percent encoded url)}\\ +% utf8/URI-raw & \texttt{percent encoded url}\\ +% utf16/hex & \texttt{<HEX>} (utf16/hex)\\ +% utf16/hex-raw & \texttt{HEX} (utf16/hex)\\ +% utf16/string & \texttt{(lit)} (utf16/string)\\ +% utf16/string-raw & \texttt{lit} (utf16/string) +% \end{tabular} +% \begin{macro}{ \@@_text_string_from_unicode:nN } +% \begin{macrocode} +\cs_new_protected:Npn \@@_text_string_from_unicode:nN #1 #2 + { + \pdf_string_from_unicode:nVN { #1 } #2 #2 + } +% \end{macrocode} +% \end{macro} +% This command combines everything. +% |#1|=input, |#2|= handler shortcut |#3|= output str variable +% The commands uses a group to locally set \cs{Hy@pdfstringtrue} +% so that \cs{texorpdfstring} works and other local settings can be done. +% +% \begin{macro}{ \@@_text_pdfstring:nnN } +% \begin{macrocode} +\cs_new_protected:Npn \@@_text_pdfstring:nnN #1 #2 #3 + { + \group_begin: + \Hy@pdfstringtrue + \hook_use:n {hyp/text/pdfstring} + \@@_text_purify:nN { #1 } \l_@@_text_tmpa_str + \@@_text_cleanup:N \l_@@_text_tmpa_str + \@@_text_string_from_unicode:nN { #2 } \l_@@_text_tmpa_str + \str_gset_eq:NN \g_@@_text_tmpa_str\l_@@_text_tmpa_str + \group_end: + \str_set_eq:NN #3 \g_@@_text_tmpa_str + } +\cs_generate_variant:Nn \@@_text_pdfstring:nnN {xnN,onN,xoN,ooN,noN} +% \end{macrocode} +% \end{macro} +% !!! temporary until all instances are gone +%^^A TODO check if the redefinition of |~| is needed (probably not) +%^^A \edef~{\string~}% +%^^A \char_set_catcode_other:N \~ +%^^A \char_set_active_eq:NN \~ \c_tilde_str +%^^A \char_set_catcode_active:N \~ +% \begin{macrocode} +\cs_new_protected:Npn\Hy@pstringdef #1 #2 + { \@@_text_pdfstring:xnN {#2} {utf8/string-raw}#1 } +% \end{macrocode} +% +% This is a special version for info keys: +% \begin{macro}{ \@@_text_pdfstring_info:nN } +% \begin{macrocode} +\cs_new_protected:Npn \@@_text_pdfstring_info:nN #1 #2 + { + \@@_text_pdfstring:noN { #1 }{ \l_@@_text_enc_info_print_tl } #2 + } +% \end{macrocode} +% \end{macro} +% +% \section{Pagelabels} +% Page labels are representations of the page numbers in the PDF viewer. If the hyperref +% options |pdfpagelabels| is true (the default) roman numbers are e.g. shown as +% \enquote{ii (2/58)}. To do this the page ranges must be collected, if possible a prefix +% and the numbering of the counter must be identified and then written +% to the catalog. +% +% The current implementation in hyperref/hyperref drivers: +% \begin{description} +% \item[xetex:] hxetex.def, line 80-110\\ +% |\HyPL@StorePageLabel| writes to the aux-file +% at begin document (after reading the aux) +% |\HyPL@SetPageLabels| is called (defined in hyperref.sty after the driver +% loading) +% which calls |\Hy@PutCatalog{/PageLabels<</Nums[\HyPL@Labels]>>}| +% \item[dvips:] identical to xetex, line 60 to 90 in pdfmark.def +% \item[dvipdfm:] identical to xetex +% \item[pdftex:] |\HyPL@StorePageLabel| stores in |\HyPL@Labels| in the first compilation +% In |\AtVeryEndDocument| |\HyPL@SetPageLabels| is called. +% \item[luatex] identical to pdftex +% \end{description} +% +% The code in \pkg{hyperref} inspects |\thepage| and tries to figure out +% the numbering system and the prefix. E.g. A-\arabic{page} is correctly split. +% If the counter can not be identified \pkg{hyperref} generates only /P entries with the +% whole content. +% +% The new implementation makes use of the pdf management: The relevant entry in the +% catalog is continuously updated and pushed out at the end of the document. +% This works (hopefully \ldots) with all drivers. +% +% We do not try to avoid the (in hyperref's wording) +% \enquote{useless} pagelabel entry +% |/PageLabels <</Nums[0<</S/D>>]>>| +% (but it would be possible), we also don't test for empty |\thepage|, +% \pkg{hyperref} seems to handle this fine and the pdf is valid. +% +% The code has to define |\Hy@PutCatalog| as we can't yet +% change code in hyperref. The switch for draftmode has been removed. +% +% \begin{macro} +% { +% \@@_PageLabels_gpush:, +% \Hy@PutCatalog, +% \HyPL@StorePageLabel +% } +% \begin{macrocode} +\cs_new_protected:Npn\@@_PageLabels_gpush: + { + \pdfmanagement_add:nnx {Catalog} {PageLabels}{<</Nums[\HyPL@Labels]>>} + } + +\def\Hy@PutCatalog #1 {} + + +\legacy_if:nT { Hy@pdfpagelabels } + { + \cs_set_protected:Npn \HyPL@StorePageLabel #1 + { + \tl_gput_right:Nx \HyPL@Labels { \the\Hy@abspage<<#1>> } + \@@_PageLabels_gpush: + } + } +% \end{macrocode} +% \end{macro} +% \section {Core Hyperref Commands} +% Every hyperref has to define eight core command: +% \begin{verbatim} +% \hyper@anchor +% \hyper@anchorstart +% \hyper@anchorend +% \hyper@link %GoTo +% \hyper@linkstart %GoTo +% \hyper@linkend %GoTo +% \hyper@linkfile %GoToR +% \hyper@linkurl %URI +% \end{verbatim} +% +% This driver defines for consistency also +% |\hyper@linklaunch| for Launch and |\hyper@linknamed| for Named. +% +% \subsection{ Anchors / destinations} +% The first three commands are needed for \enquote{anchors}. At first +% the internal commands to create a destination. It uses +% |\Hy@WrapperDef| to make it babel safe, it is not clear if this is +% still needed, but we leave if for now. +% \begin{function} { \@@_destination:nn } +% \begin{syntax} +% \cs{@@_destination:nn} \Arg{destination name} \Arg{location} +% \end{syntax} +% The \meta{destination name} is encoded with the method stored in +% in \cs{l_@@_text_enc_dest_tl}. The location should be one of +% |fit|, |fith|, |fitv|, |fitbv|, |fitbh|, |fitr|, |xyz|, |fitrbx|. +% The last will make use of \cs{l_@@_dest_box} +% \end{function} +% \begin{macro}{ \@@_destination:nn } +% \begin{macrocode} +\Hy@WrapperDef \@@_destination:nn #1 #2 + { + \mode_if_horizontal:T { \@savsf\spacefactor } + \Hy@SaveLastskip %defined in hyperref + \Hy@VerboseAnchor{#1} %defined in hyperref, for debugging + \@@_text_pdfstring:xoN + { \HyperDestNameFilter{#1} } + { \l_@@_text_enc_dest_tl } + \l_@@_tmpa_tl + \str_if_eq:nnTF {#2} {fitrbox} + { + \exp_args:NV + \pdf_destination:nnnn \l_@@_tmpa_tl + { \box_wd:N \l_@@_dest_box } + { \box_ht:N \l_@@_dest_box } + { \box_dp:N \l_@@_dest_box } + } + { + \exp_args:NV + \pdf_destination:nf + { \l_@@_tmpa_tl } + { #2 } + } + \Hy@RestoreLastskip %defined in hyperref + \mode_if_horizontal:T { \spacefactor\@savsf } + } +% \end{macrocode} +% \end{macro} +% This are the three destinations commands. They are modelled along the +% xetex version. It is not quite clear if really all three +% are needed for the backends supported by this driver, but changing the hyperref +% code would be difficult. +% \begin{macro} +% { +% \hyper@anchor, +% \hyper@anchorstart, +% \hyper@anchorend +% } +% \begin{macrocode} +\cs_new_protected:Npn \hyper@anchor #1 + { + \exp_args:NnV + \@@_destination:nn {#1} \l_@@_dest_pdfview_tl + } + +\cs_new_protected:Npn \hyper@anchorstart #1 + { + \Hy@activeanchortrue + \exp_args:NnV + \@@_destination:nn {#1} \l_@@_dest_pdfview_tl + } + +\cs_new_protected:Npn \hyper@anchorend + { + \Hy@activeanchorfalse + } +% \end{macrocode} +% \end{macro} +% +% \subsection{GoTo Links} +% The next three commands are for links inside the document, +% to destinations (GoTo links). +% The definition in \pkg{hyperref} have a first argument which +% can be used to pass a semantical context. Currently this argument is +% only used for \cs{cite} and only to change the color. The new +% implementation uses it for a real hook. +% +% At first the internal link commands: +% \begin{macrocode} +\cs_new_protected:Npn \@@_link_goto_begin:nw #1 + { + \mode_leave_vertical: + \protected@edef \l_@@_dest_name_tmpa_tl { #1 } + \tl_if_empty:NTF \l_@@_dest_name_tmpa_tl + { + \msg_warning:nnx + { hyp } + { empty-destination-name } + { \c_@@_dest_undefined_tl } + \tl_set_eq:NN \l_@@_dest_name_tmpa_tl \c_@@_dest_undefined_tl + } + { + \@@_text_pdfstring:xoN + { \exp_args:No \HyperDestNameFilter { \l_@@_dest_name_tmpa_tl } } + { \l_@@_text_enc_dest_tl } + \l_@@_dest_name_tmpa_tl + } + \exp_args:No + \pdfannot_link_goto_begin:nw { \l_@@_dest_name_tmpa_tl } + } + +\cs_new_protected:Npn \@@_link_goto_end: + { + \pdfannot_link_goto_end: + } +% \end{macrocode} +% +% Now the three hyperref commands. +% The splitted commands \cs{hyper@linkstart} and \cs{hyper@linkend} are used for +% footnotemarks, toc and natbib-cites. +% \begin{function}{\hyper@link} +% \begin{syntax} +% \cs{hyper@link}\Arg{context}\Arg{destination name}\Arg{link text} +% \end{syntax} +% This creates a complete GoTo link around the \meta{link text} +% pointing to \meta{destination name}. +% The hook |hyp/link/|\meta{context} is executed at the begin if it exists. +% +% The only \meta{context} for which a hook is predefined is |cite|. +% Packages which want to use another \meta{context} should initialize the hook like +% this: +% \begin{verbatim} +% \IfHookExistsTF{hyp/link/context}{} +% {\NewHook{hyp/link/context}} +% \end{verbatim} +% +% The hook code is executed in a group but before all the pdfannot hooks. +% \end{function} +% \begin{function}{\hyper@linkstart,\hyper@linkend} +% \begin{syntax} +% \cs{hyper@linkstart}\Arg{context}\Arg{destination name}\\ +% \cs{hyper@linkend} +% \end{syntax} +% This creates the start and end commands for a GoTo link around the text +% between both pointing to \meta{destination name}. +% The hook |hyp/link/|\meta{context} is executed at the begin if it exists +% as with \cs{hyper@link} +% +% The commands open and close a group, so should be placed carefully. . +% \end{function} + +% \pkg{hyperref} adds a group with \cs{Hy@colorlink}, we move this outside the link +% so that it groups the context hook too. +% \begin{macrocode} + +\cs_new_protected:Npn \hyper@link #1 #2 #3 %#1 context, #2=destination name, #3 content + { + \bool_if:NTF \l_@@_annot_GoTo_bool + { + \Hy@VerboseLinkStart{#1}{#2} + \group_begin: + \hook_use:n {hyp/link/#1} + \@@_link_goto_begin:nw {#2}#3\Hy@xspace@end + \__hyp_link_goto_end: + \group_end: + \Hy@VerboseLinkStop + }{#3} + } + +\cs_new_protected:Npn \hyper@linkstart #1 #2 %#1 context, #2=destination name + { + \bool_if:NT \l_@@_annot_GoTo_bool + { + \Hy@VerboseLinkStart{#1}{#2}% only for debug + \group_begin: + \hook_use:n {hyp/link/#1} + \@@_link_goto_begin:nw {#2} + } + } + +\cs_new_protected:Npn \hyper@linkend + { + \bool_if:NT \l_@@_annot_GoTo_bool + { + \@@_link_goto_end: + \group_end: + \Hy@VerboseLinkStop + } + } +% \end{macrocode} +% +% \subsection{URI links} +% We define a dictionary for the action dictionary. For now it is public. +% \begin{macrocode} +\pdfdict_new:n {l_hyp/annot/A/URI} +\pdfdict_put:nnn {l_hyp/annot/A/URI}{Type}{/Action} +\pdfdict_put:nnn {l_hyp/annot/A/URI}{S}{/URI} + +\cs_new_protected:Npn \hyper@linkurl #1 #2 %#1:link text #2: URI, + { + \bool_if:NTF \l_@@_annot_URI_bool + { + \group_begin: + \@@_text_pdfstring:xoN + { #2} + { \l_@@_text_enc_uri_print_tl } + \l_@@_uri_tmpa_tl + \pdfdict_put:nno{l_hyp/annot/A/URI}{URI}{\l_@@_uri_tmpa_tl} + \ifHy@href@ismap + \pdfdict_put:nnn{l_hyp/annot/A/URI}{IsMap}{true} + \fi + \tl_if_empty:NF \Hy@href@nextactionraw + { + \str_remove_once:Nn \Hy@href@nextactionraw {/Next} + \pdfdict_put:nno{l_hyp/annot/A/Next}{\Hy@href@nextactionraw} + } + \cs_set_eq:NN \# \c_hash_str + \cs_set_eq:NN \% \c_percent_str + \Hy@safe@activestrue + \mode_leave_vertical: + \pdfannot_link:nxn { URI } + { + /A + << + \pdfdict_use:n {l_hyp/annot/A/URI} + >> + } + { + #1 + \Hy@xspace@end + \Hy@VerboseLinkStop %where is the start?? + } + \group_end: + } + {#1} + } + +% \end{macrocode} +% \subsection{GoToR Links {files}} +% \begin{macrocode} +\pdfdict_new:n {l_hyp/annot/A/GoToR} +\pdfdict_put:nnn {l_hyp/annot/A/GoToR}{Type}{/Action} +\pdfdict_put:nnn {l_hyp/annot/A/GoToR}{S}{/GoToR} + +\cs_new_protected:Npn \hyper@linkfile #1 #2 #3 % link text, filename, destname + { + \bool_if:NTF \l_@@_annot_GoToR_bool + { + \group_begin: + \tl_set:Nn \l_@@_filename_tmpa_tl { #2 } + \Hy@CleanupFile \l_@@_filename_tmpa_tl + \@@_text_pdfstring:ooN + { \l_@@_filename_tmpa_tl } + { \l_@@_text_enc_file_print_tl } + \l_@@_filename_tmpa_tl + \pdfdict_put:nno {l_hyp/annot/A/GoToR}{F}{\l_@@_filename_tmpa_tl} + \@@_text_pdfstring:nnN + { #3 } + { \l_@@_text_enc_dest_print_tl } + \l_@@_dest_name_tmpa_tl + \Hy@MakeRemoteAction + \tl_if_blank:eTF {#3} + { + \pdfdict_put:nnx {l_hyp/annot/A/GoToR}{D} + {[\Hy@href@page/\l_@@_dest_pdfremotestartview_tl]} + } + { + \pdfdict_put:nno {l_hyp/annot/A/GoToR}{D}{\l_@@_dest_name_tmpa_tl} + } + \tl_if_empty:NF \Hy@href@nextactionraw + { + \str_remove_once:Nn \Hy@href@nextactionraw {/Next} + \pdfdict_put:nno{l_hyp/annot/A/Next}{Next}{\Hy@href@nextactionraw} + } + \mode_leave_vertical: + \pdfannot_link:nxn %expansion?? + { GoToR } + { + /A<< + \pdfdict_use:n {l_hyp/annot/A/GoToR} + >> + } + { + #1\Hy@xspace@end + \Hy@VerboseLinkStop %where is the start?? + } + \group_end: + } + {#1} + } +% \end{macrocode} +% +% \subsection{Launch links} +% +% We define \cs{hyper@linklaunch} for naming consistency +% \begin{macrocode} +\pdfdict_new:n {l_hyp/annot/A/Launch} +\pdfdict_put:nnn {l_hyp/annot/A/Launch}{Type}{/Action} +\pdfdict_put:nnn {l_hyp/annot/A/Launch}{S}{/Launch} + +\cs_new_protected:Npn \hyper@linklaunch #1 #2 #3 % filename, link text, Parameters + { + \bool_if:NTF \l_@@_annot_Launch_bool + { + \group_begin: + \@@_text_pdfstring:nnN + { #1 } + { \l_@@_text_enc_file_print_tl } + \l_@@_filename_tmpa_tl + \pdfdict_put:nno {l_hyp/annot/A/Launch}{F}{\l_@@_filename_tmpa_tl} + \@@_text_pdfstring:noN + { #3 } + { \l_@@_text_enc_para_print_tl } + \l_@@_para_tmpa_tl + \bool_if:nTF + { + \str_if_eq_p:Vn \l_@@_para_tmpa_tl {()} + || + \pdf_version_compare_p:Nn > {1.9} + } + { + \pdfdict_remove:nn {l_hyp/annot/A/Launch}{Win} + } + { + \pdfdict_put:nnx {l_hyp/annot/A/Launch}{Win} + {<</P \l_@@_para_tmpa_tl /F \l_@@_filename_tmpa_tl >>} + } + \tl_if_empty:NF \Hy@href@nextactionraw + { + \str_remove_once:Nn \Hy@href@nextactionraw {/Next} + \pdfdict_put:nno{l_hyp/annot/A/Next}{Next}{\Hy@href@nextactionraw} + } + \mode_leave_vertical: + \pdfannot_link:nxn + { Launch } + { + /A + << + \pdfdict_use:n {l_hyp/annot/A/Launch} + >> + } + { + #2\Hy@xspace@end + \Hy@VerboseLinkStop %where is the start?? + } + \group_end: + } + {#2} + } +% \end{macrocode} +% The actually command used by hyperref is \cs{@hyper@launch} which uses a delimited +% argument, because of the color the definition is a bit convoluted. +% \begin{macrocode} +\use:x + { % filename, anchor text, linkname + \cs_set_protected:Npn \exp_not:N \@hyper@launch run \c_colon_str ##1 \exp_not:N \\ ##2 ##3 + } + { + \hyper@linklaunch {#1}{#2}{#3} + } +% \end{macrocode} +% +% \subsection{Named links (menu)} +% We also define \cs{hyper@linknamed} for consistency. +% \begin{macrocode} +\pdfdict_new:n {l_hyp/annot/A/Named} +\pdfdict_put:nnn {l_hyp/annot/A/Named}{Type}{/Action} +\pdfdict_put:nnn {l_hyp/annot/A/Named}{S}{/Named} + +\cs_new_protected:Npn \hyper@linknamed #1 #2 %#1 action, #2 link text + { + \bool_if:NTF \l_@@_annot_Named_bool + { + \group_begin: + \pdfmeta_standard_verify:nnTF {named_actions}{#1} + { + \mode_leave_vertical: + \pdfdict_put:nnx {l_hyp/annot/A/Named}{N} + {\pdf_name_from_unicode_e:n{#1}} + \tl_if_empty:NF \Hy@href@nextactionraw + { + \str_remove_once:Nn \Hy@href@nextactionraw {/Next} + \pdfdict_put:nno{l_hyp/annot/A/Next}{Next}{\Hy@href@nextactionraw} + } + \pdfannot_link:nxn { Named } + { + /A + << + \pdfdict_use:n { l_hyp/annot/A/Named } + >> + } + { + #2 + \Hy@xspace@end + \Hy@VerboseLinkStop + } + } + { + \msg_warning:nnn { hyp } { pdfa-no-named-action }{#1} + #2 + } + \group_end: + } + {#2} + } + +% \end{macrocode} +% +% \section{Link decorations} +% \subsection{Functions to export and select colors} +% We support two input syntax: color expressions and model with values. +% \begin{function}{\@@_color_export:nnN,\@@_colormodel_export:nnnN} +% \begin{syntax} +% \cs{@@_color_export:nnN} \Arg{color} \Arg{export format} \meta{tlvar}\\ +% \cs{@@_colormodel_export:nnnN} \Arg{color model}\Arg{value} \Arg{export format} \meta{tlvar} +% \end{syntax} +% This exports a color as space separated numbers as needed in in |/C|. +% \Arg{color} should have either the format |[model]{value}| or be a color expression. +% For examples: |[rgb]{1,0,.5}| or |red!50!blue|. The export format is either +% |space-sep-cmyk| or |space-sep-rgb|. +% \end{function} +% \begin{macro}{\@@_color_export:nnN,\@@_colormodel_export:nnnN} +% Color keys need to parse color expressions. Two input types are supported: +% |color=[rgb]{1,0,.5}| and |color=red!50!blue|. The colors are exported +% to PDF syntax (space separated numbers). +% \begin{macrocode} +\cs_new_protected:Npn \@@_color_export:nnN #1 #2 #3 + { + \tl_if_head_eq_charcode:nNTF {#1}[ %] + { + \@@_colormodel_export:wnnN #1 {#2} #3 + } + { + \color_export:nnN {#1} {#2} #3 + } + } + +\cs_new_protected:Npn \@@_colormodel_export:wnnN [#1] #2 #3 #4 + { + \color_export:nnnN {#1}{#2}{#3}#4 + } + +\cs_generate_variant:Nn \@@_color_export:nnN {xVN} +% \end{macrocode} +% \end{macro} +% +% \begin{function}{\@@_color_select:n} +% \begin{syntax} +% \cs{@@_color_select:nN} \Arg{color} \\ +% \end{syntax} +% These commands select a (text) color. +% \Arg{color} should have either the format |[model]{value}| or be a color expression. +% For examples: |[rgb]{1,0,.5}| or |red!50!blue| +% \end{function} +% \begin{macro}{\@@_color_select:n,\@@_colormodel_select_aux:wn} +% Color keys need to parse color expressions. Two input types are supported: +% |color=[rgb]{1,0,.5}| and |color=red!50!blue|. +% \begin{macrocode} +\cs_new_protected:Npn \@@_color_select:n #1 + { + \tl_if_head_eq_charcode:nNTF {#1}[ %] + { + \@@_colormodel_select_aux:wn #1 + } + { + \color_select:n {#1} + } + } + +\cs_new_protected:Npn \@@_colormodel_select_aux:wn [#1] #2 + { + \color_select:nn {#1}{#2} + } + +\cs_generate_variant:Nn \@@_color_select:n {e} +% \end{macrocode} +% \end{macro} +% +% \begin{function}{\@@_color_set:nn} +% \begin{syntax} +% \cs{@@_color_set:nN} \Arg{ name } \Arg{color} \\ +% \end{syntax} +% These commands store the color in \Arg{name}. +% \Arg{color} should have either the format |[model]{value}| or be a color expression. +% For examples: |[rgb]{1,0,.5}| or |red!50!blue| +% \end{function} +% \begin{macro}{\@@_color_set:nn,\@@_colormodel_set_aux:nwn} +% Color keys need to parse color expressions. Two input types are supported: +% |color=[rgb]{1,0,.5}| and |color=red!50!blue|. +% \begin{macrocode} +\cs_new_protected:Npn \@@_color_set:nn #1 #2 + { + \tl_if_head_eq_charcode:nNTF {#2}[ %] + { + \@@_colormodel_set_aux:nwn { #1 } #2 + } + { + \color_set:nn {#1} {#2} + } + } + +\cs_new_protected:Npn \@@_colormodel_set_aux:nwn #1 [#2] #3 + { + \color_set:nnn {#1}{#2}{#3} + } + +\cs_generate_variant:Nn \@@_color_set:nn {ne} +% \end{macrocode} +% \end{macro} +% \subsection{Textcolor of links} +% colors are added in the hooks. This means that they can also be removed if needed. +% They add a group---this isn't needed with hyperref code, but could be relevant +% with low-level annotations. +% \begin{macrocode} +\prop_map_inline:Nn \c_@@_map_hyp_annot_prop + { + \hook_gput_code:nnn + {pdfannot/link/#2/begin} + {hyp/color} + { + \bool_if:cT { l_hyp_annot_color#1_bool } + { + \group_begin: + \color_select:n { hyp/color/#1} + } + } + \hook_gput_code:nnn + {pdfannot/link/#2/end} + {hyp/color} + { + \bool_if:cT { l_hyp_annot_color#1_bool } + { + \group_end: + } + } + } +% \end{macrocode} +% +% \begin{hypkey}{colorlinks} +% This key also resets the border and borderstyle. +% \begin{macrocode} +\keys_define:nn { hyp / setup } + { + ,colorlinks .meta:n = + { + ,pdfborder={0~0~0} + ,pdfborderstyle= + ,colorurl =#1 + ,colorlink =#1 + ,colorrun =#1 + ,colormenu =#1 + ,colorfile =#1 + } + ,colorlinks .default:n = {true} + } +% \end{macrocode} +% \end{hypkey} +% \begin{hypkey}{colorurl,colorlink,colorrun,colormenu,colorfile} +% \begin{hypkey}{urlcolor,linkcolor,runcolor,menucolor,filecolor} +% \begin{hypkey}{allcolor} +% \begin{macrocode} +\seq_map_inline:Nn \c_@@_annot_types_seq + { + \keys_define:nn { hyp / setup } + { + ,color#1 .bool_set:c = { l_hyp_annot_color#1_bool } + ,#1color .code:n = { \@@_color_set:ne {hyp/color/#1}{##1} } + } + } + +\keys_define:nn { hyp / setup } + { + ,allcolors .meta:n = + { + ,urlcolor=#1 + ,linkcolor=#1 + ,runcolor=#1 + ,filecolor=#1 + ,menucolor=#1 + } + ,allcolors .value_required:n = true + } +% \end{macrocode} +% \end{hypkey} +% \end{hypkey} +% \end{hypkey} +% \subsection{Style and color of borders} +% \subsubsection{Border color} +% The border color is set by link type. The color can be set as rgb (default) +% or cmyk (unusual). This can be set with the |bordercolormodel| key: +%\begin{hypkey}{bordercolormodel} +% \begin{macrocode} +\keys_define:nn { hyp / setup } + { + ,bordercolormodel .choices:nn = + {rgb,cmyk} + { \str_gset:Nn \g_@@_bordercolormodel_str {space-sep-#1}} + ,bordercolormodel .initial:n ={rgb} + } +% \end{macrocode} +% \end{hypkey} +% \begin{macrocode} +\prop_map_inline:Nn \c_@@_map_hyp_annot_prop + { + \keys_define:nn { hyp / setup } + { + #1bordercolor .code:n = + { + \tl_if_empty:nTF { ##1 } + { + \pdfannot_dict_remove:nn + {link/#2} + { C } + } + { + \@@_color_export:xVN {##1}\g_@@_bordercolormodel_str \l_@@_tmpa_tl + \pdfannot_dict_put:nnx + {link/#2} + { C } + { [\l_@@_tmpa_tl] } + } + } + } + } + +\keys_define:nn { hyp / setup } + { + ,allbordercolors .meta:n = + { + ,linkbordercolor=#1 + ,urlbordercolor =#1 + ,filebordercolor=#1 + ,menubordercolor=#1 + ,runbordercolor =#1 + } + ,allbordercolors .value_required:n = true + } + +% \end{macrocode} +% +% \subsubsection{Borderwidth and -arc} +% +% \begin{macrocode} +\prop_map_inline:Nn \c_@@_map_hyp_annot_prop + { + \keys_define:nn { hyp / setup } + { + #1border .code:n = + { + \tl_if_empty:nTF { ##1 } + { + \pdfannot_dict_remove:nn + {link/#2} + { Border } + } + { + \pdfannot_dict_put:nnn + {link/#2} + { Border } + { [##1] } + } + } + } + } +\keys_define:nn { hyp / setup } + { + ,pdfborder .code:n = + { + \tl_if_empty:nTF { #1 } + { + \prop_map_inline:Nn \c_@@_map_hyp_annot_prop + { + \pdfannot_dict_remove:nn + {link/##2} + { Border } + } + } + { + \prop_map_inline:Nn \c_@@_map_hyp_annot_prop + { + \pdfannot_dict_put:nnn + {link/##2} + { Border } + { [#1] } + } + } + } + ,pdfborder .initial:n = {0~0~1}, + } +% \end{macrocode} +% \subsubsection{Borderstyle} +% This keys fill the extended /BS entry (a dictionary). +% \begin{hypkey}{pdfborderstyle,urlborderstyle,linkborderstyle, +% runborderstyle,fileborderstyle, +% menuborderstyle} +% \begin{macrocode} +\prop_map_inline:Nn \c_@@_map_hyp_annot_prop + { + \keys_define:nn { hyp / setup } + { + #1borderstyle .code:n = + { + \tl_if_empty:nTF { ##1 } + { + \pdfannot_dict_remove:nn + {link/#2} + { BS } + } + { + \pdfannot_dict_put:nnn + {link/#2} + { BS } + { <<##1>> } + } + } + } + } +\keys_define:nn { hyp / setup } + { + ,pdfborderstyle .code:n = + { + \tl_if_empty:nTF { #1 } + { + \prop_map_inline:Nn \c_@@_map_hyp_annot_prop + { + \pdfannot_dict_remove:nn + {link/##2} + { BS } + } + } + { + \prop_map_inline:Nn \c_@@_map_hyp_annot_prop + { + \pdfannot_dict_put:nnn + {link/##2} + { BS } + { <<#1>> } + } + } + } + ,pdfborderstyle .initial:n = {}, + } +% \end{macrocode} +% \end{hypkey} +% \subsection{ocgcolorlinks} +% OCG colorlinks need objects and an entry in the catalog. +% Perhaps the objects need public names to avoid that ocgx2 has to create +% duplicates? +% TODO +% \begin{macro}{\@@_ocg_init:} +% This commands write the objects as needed if ocg links are used. +% The initialization should happens only once. +% \begin{macrocode} +\cs_new_protected:Npn \@@_ocg_init: + { + \pdf_object_new:nn { l_@@_ocg_view_dict_obj } { dict } + \pdf_object_new:nn { l_@@_ocg_print_dict_obj } { dict } + \pdf_object_new:nn { l_@@_ocg_config_dict_obj } { dict } + \pdf_object_new:nn { l_@@_ocg_ref_array_obj } { array } + \pdf_object_write:nx { l_@@_ocg_ref_array_obj } + { + \pdf_object_ref:n { l_@@_ocg_view_dict_obj } + \c_space_tl + \pdf_object_ref:n { l_@@_ocg_print_dict_obj } + } + \pdf_object_write:nn { l_@@_ocg_view_dict_obj } + { + /Type/OCG + /Name(View) + /Usage + << + /Print <</PrintState/OFF>>~ + /View <</ViewState/ON >>~ + >> + } + \pdf_object_write:nn { l_@@_ocg_print_dict_obj } + { + /Type/OCG + /Name(Print) + /Usage + << + /Print <</PrintState/ON>>~ + /View <</ViewState/OFF>>~ + >> + } + \pdfmanagement_add:nnx { Catalog / OCProperties }{OCGs }{ \pdf_object_ref:n {l_@@_ocg_view_dict_obj} } + \pdfmanagement_add:nnx { Catalog / OCProperties }{OCGs }{ \pdf_object_ref:n {l_@@_ocg_print_dict_obj} } + \pdf_object_write:nx { l_@@_ocg_config_dict_obj } + { + /OFF[\pdf_object_ref:n { l_@@_ocg_print_dict_obj }] + /AS[ + << + /Event/View + /OCGs\c_space_tl \pdf_object_ref:n { l_@@_ocg_ref_array_obj } + /Category[/View] + >> + << + /Event/Print + /OCGs\c_space_tl \pdf_object_ref:n { l_@@_ocg_ref_array_obj } + /Category[/Print] + >> + << + /Event/Export + /OCGs\c_space_tl \pdf_object_ref:n { l_@@_ocg_ref_array_obj } + /Category[/Print] + >> + ] + } + \pdfmanagement_add:nnx { Catalog / OCProperties }{ D }{ \pdf_object_ref:n { l_@@_ocg_config_dict_obj} } + \cs_gset:Npn \@@_ocg_init: {} + } +% \end{macrocode} +% \end{macro} +% We use like with colors a hook, this allows ocgx to replace it. +% The implementation is rather simple and uses a box. +% \begin{macrocode} +\prop_map_inline:Nn \c_@@_map_hyp_annot_prop + { + \hook_gput_code:nnn + {pdfannot/link/#2/begin} + {hyp/ocg} + { + \bool_if:cT { l_hyp_annot_ocgcolor#1_bool } + { + \@@_ocg_init: + \group_begin: + \hbox_set:Nw \l_@@_tmpa_box + } + } + \hook_gput_code:nnn + {pdfannot/link/#2/end} + {hyp/ocg} + { + \bool_if:cT { l_hyp_annot_ocgcolor#1_bool } + { + \hbox_set_end: + \mbox + { + \pdf_bdcobject:nn {OC}{l_@@_ocg_print_dict_obj} + \hbox_overlap_right:n { \box_use:N \l_@@_tmpa_box } + \pdf_emc: + \pdf_bdcobject:nn {OC}{l_@@_ocg_view_dict_obj} + \group_begin: + \color_select:n { hyp/color/#1 } + \box_use_drop:N \l_@@_tmpa_box + \group_end: + \pdf_emc: + } + \group_end: + } + } + } +% \end{macrocode} +% +% \begin{hypkey} +% { +% ocgcolorlinks, +% ocgcolorlink, +% ocgcolorurl, +% ocgcolorfile, +% ocgcolormenu, +% ocgcolorrun +% } +% These are the keys for ocgcolors. We try to disable it +% for pdf version below 1.5 +% \begin{macrocode} +\pdf_version_compare:NnTF < {1.5} + { + \keys_define:nn { hyp / setup } + { + ,_ocgcolorlinks .code:n = + { + \msg_warning:nnxx + { hyp } + { ignore-deprecated-or-unknown-option-in-pdf-version } + { ocgcolorlinks } { \pdf_version_major:.\pdf_version_minor: } + } + } + } + { + \keys_define:nn { hyp / setup } + { + ,_ocgcolorlinks .meta:n = + { + ocgcolorlink=#1, + ocgcolorurl=#1, + ocgcolorfile=#1, + ocgcolorrun=#1, + ocgcolormenu=#1 + } + ,_ocgcolorlinks .default:n = true + } + } + +\keys_define:nn { hyp / setup } + { + ,ocgcolorlinks .choice: + ,ocgcolorlinks / true .meta:n = + { + pdfborder ={0~0~0}, + pdfborderstyle ={}, + colorlinks = false, + _ocgcolorlinks = true + } + ,ocgcolorlinks / false .meta:n = + { + _ocgcolorlinks = false + } + ,ocgcolorlinks .default:n = {true} + } + +\seq_map_inline:Nn \c_@@_annot_types_seq + { + \pdf_version_compare:NnTF < {1.5} + { + \keys_define:nn { hyp / setup } + { + ,ocgcolor#1 .code:n= + { + \msg_warning:nnxx + { hyp } + { ignore-deprecated-or-unknown-option-in-pdf-version } + { ocgcolor#1 } + { \pdf_version_major:.\pdf_version_minor: } + } + } + } + { + \keys_define:nn { hyp / setup } + { + ,ocgcolor#1 .bool_set:c = { l_hyp_annot_ocgcolor#1_bool } + } + } + } +% \end{macrocode} +% \end{hypkey} +% \subsection{Highlighting} +% This keys set what happens if you click on a link +% \begin{macrocode} +\prop_map_inline:Nn \c_@@_map_hyp_annot_prop + { + \keys_define:nn { hyp / setup } + { + ,#1highlight .choices:nn = + { /I, /N, /O, /P} + { + \pdfannot_dict_put:nnn + {link/#2} + { H } + { ##1 } + + } + ,#1highlight / .code:n = + { + \pdfannot_dict_remove:nn + {link/#2} + { H } + + } + ,#1highlight / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice+empty } + { pdfhighlight } + { /I~(inverse), /N~(no effect), /O~(outline), /P~(inset) } + { \exp_not:n {##1} } + } + } + } + + +\keys_define:nn { hyp / setup } + { + ,pdfhighlight .choices:nn = + { /I, /N, /O, /P} + { + \prop_map_inline:Nn \c_@@_map_hyp_annot_prop + { + \pdfannot_dict_put:nnn + {link/####2} + { H } + { #1 } + } + } + ,pdfhighlight / .code:n = + { + \prop_map_inline:Nn \c_@@_map_hyp_annot_prop + { + \pdfannot_dict_remove:nn + {link/##2} + { H } + } + } + ,pdfhighlight .initial:n = {/I}, + ,pdfhighlight / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice+empty } + { pdfhighlight } + { /I~(inverse), /N~(no effect), /O~(outline), /P~(inset) } + { \exp_not:n {#1} } + } + } +% \end{macrocode} +% +% \subsection{Hiding links} +% This key disable all appearance keys. The link themselves are still there. +% \begin{hypkey}{hidelinks,hidelink,hideurl,hidefile,hiderun,hidemenu} +% \begin{macrocode} +\keys_define:nn { hyp / setup } + { + hidelinks .meta:n = + { + ,colorlinks = false + ,ocgcolorlinks = false + ,pdfborder = { 0~0~0 } + ,pdfborderstyle= + } + } + +\seq_map_inline:Nn \c_@@_annot_types_seq + { + \keys_define:nn { hyp / setup } + { + hide#1 .meta:n = + { + ,color#1 = false + ,ocgcolor#1 = false + ,#1border = { 0~0~0 } + ,#1borderstyle = + } + } + } +% \end{macrocode} +% \end{hypkey} +% \subsection{color schemes and settings} +% This define the key for the color schemes and sets the default colors. +% \begin{hypkey}{colorscheme} +% \begin{macrocode} +\keys_define:nn { hyp / setup } + { + colorscheme .code:n = + { + \prop_map_inline:cn { c_@@_colorscheme_#1_prop } + { + \keys_set:nn { hyp /setup } + { + ##1 = ##2 + } + } + } + } +\keys_set:nn { hyp / setup } {colorscheme=phelype} +% \end{macrocode} +% \end{hypkey} +% +% \section{Keys} +% +% \subsection{Ignored keys} +% The following are ignored (with or without warnings) +% \begin{hypkey}{unicode,pdfencoding,pdfversion} +% \begin{macrocode} +\keys_define:nn { hyp / setup } + { + ,unicode .code:n = {} + ,pdfencoding .code:n = {} + ,pdfversion .code:n = + { + \msg_warning:nn { hyp }{ pdfversion-disabled } + } + } +% +% \end{macrocode} +% \end{hypkey} +% +% \subsection{Various keys for the pdf and linking behaviour} +% This keys are typically set only once. +% +% \begin{hypkey}{verbose,debug,draft,final} +% \begin{macrocode} +\keys_define:nn { hyp / setup } + { + ,verbose .choice: + ,verbose / true .code:n = { \Hy@verbosetrue} + ,verbose / false .code:n = { \Hy@verbosefalse} + ,verbose .default:n = {true} + ,debug .meta:n = {verbose=#1} + ,debug .default:n = {true} + } +\keys_define:nn { hyp / setup } + { + ,draft .code:n = + { + \Hy@drafttrue + \PassOptionsToPackage{draft}{bookmark} + } + ,final .code:n = + { + \Hy@finaltrue + \PassOptionsToPackage{final}{bookmark} + } + } +% \end{macrocode} +% \end{hypkey} +% \begin{hypkey}{extension,hypertexnames,naturalnames, +% pageanchor,linktoc,linktocpage,plainpages,localanchorname, +% linkfileprefix} +% \begin{macrocode} +\keys_define:nn { hyp / setup } + { + ,extension .tl_set:N = \XR@ext + ,extension .initial:n= pdf + ,hypertexnames .choice: + ,hypertexnames / true .code:n = { \Hy@hypertexnamestrue} + ,hypertexnames / false .code:n = { \Hy@hypertexnamesfalse} + ,hypertexnames .default:n = {true} + ,linkfileprefix .tl_set:N = \Hy@linkfileprefix + ,localanchorname .choice: + ,localanchorname / true .code:n = { \Hy@localanchornametrue } + ,localanchorname / false .code:n = { \Hy@localanchornamefalse } + ,localanchorname .default:n = {true} + ,naturalnames .choice: + ,naturalnames / true .code:n = { \Hy@naturalnamestrue} + ,naturalnames / false .code:n = { \Hy@naturalnamesfalse} + ,naturalnames .default:n = {true} + ,pageanchor .choice: + ,pageanchor / true .code:n = { \Hy@pageanchortrue} + ,pageanchor / false .code:n = { \Hy@pageanchorfalse} + ,pageanchor .default:n = {true} + ,plainpages .choice: + ,plainpages / true .code:n = { \Hy@plainpagestrue} + ,plainpages / false .code:n = { \Hy@plainpagesfalse} + ,plainpages .default:n = {true} + } + +\keys_define:nn { hyp / setup } + { + ,linktoc .choices:nn = { none, section, all, page } + { + \cs_set_eq:Nc \Hy@linktoc { Hy@linktoc@#1 } + } + ,linktoc / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice } + { linktoc } + { none, section, all, page } + { \exp_not:n {#1} } + } + ,linktocpage .choice: + ,linktocpage / true .meta:n = {linktoc=page} + ,linktocpage / false .meta:n = {linktoc=section} + ,linktocpage .default:n = true + } + +% \end{macrocode} +% \end{hypkey} +% \begin{hypkey}{link,url,file,menu,run} +% This booleans allow to disable the link types. +% \begin{macrocode} +\prop_map_inline:Nn \c_@@_map_hyp_annot_prop + { + \keys_define:nn { hyp / setup } + { + ,#1 .bool_set:c = {l_@@_annot_#2_bool} + } + } +% \end{macrocode} +% \end{hypkey} +% +% \begin{macrocode} +\keys_define:nn { hyp / setup } + { + ,baseurl .code:n = + { + \@@_text_pdfstring:ooN { #1 } {\l_@@_text_enc_uri_print_tl} \l_@@_tmpa_tl + \tl_if_empty:NTF \l_@@_tmpa_tl + { + \pdfmanagement_remove:nn {Catalog} { URI } + } + { + \pdfmanagement_add:nnx {Catalog} { URI }{ <</Base \l_@@_tmpa_tl>> } + } + } + %only false does something ... + ,bookmarks .choice: + ,bookmarks / false .code:n = {\RemoveFromHook {begindocument/before}[hyperref/bookmark]} + ,bookmarks / true .code:n = {} + ,bookmarks .default:n = {true} + ,bookmarksnumbered .choice: + ,bookmarksnumbered / false .code:n = { \Hy@bookmarksnumberedfalse } + ,bookmarksnumbered / true .code:n = { \Hy@bookmarksnumberedtrue } + ,bookmarksnumbered .default:n = {true} + ,bookmarksopen .choice: + ,bookmarksopen / false .code:n = { \Hy@bookmarksopenfalse } + ,bookmarksopen / true .code:n = { \Hy@bookmarksopentrue } + ,bookmarksopen .default:n = {true} + ,bookmarksopenlevel .tl_set:N = \@bookmarksopenlevel + ,bookmarkstype .tl_set:N = \Hy@bookmarkstype + ,pdfcenterwindow .choice: + ,pdfcenterwindow / false .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences }{ CenterWindow } + } + ,pdfcenterwindow / true .code:n = + { + \pdfmanagement_add:nnn {Catalog / ViewerPreferences } { CenterWindow }{ true } + } + ,pdfcenterwindow / .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences }{ CenterWindow } + } + ,pdfcenterwindow / unknown .code:n = + { + \msg_warning:nnxx { hyp } { no-bool } + { pdfcenterwindow } + { \exp_not:n {#1} } + } + ,pdfcenterwindow .default:n = true + ,pdfdirection .choice: + ,pdfdirection / L2R .code:n = + { + \pdfmanagement_add:nnn {Catalog / ViewerPreferences } { Direction }{ /L2R } + } + ,pdfdirection / R2L .code:n = + { + \pdfmanagement_add:nnn {Catalog / ViewerPreferences } { Direction }{ /R2L } + } + ,pdfdirection / .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { Direction } + } + ,pdfdirection / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice+empty } + { pdfdirection } + { L2R , R2L } + { \exp_not:n {#1} } + } + ,pdfdisplaydoctitle .choice: + ,pdfdisplaydoctitle / false .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { DisplayDocTitle } + } + ,pdfdisplaydoctitle / true .code:n = + { + \pdfmanagement_add:nnn {Catalog / ViewerPreferences } { DisplayDocTitle } { true } + } + ,pdfdisplaydoctitle .default:n = true + ,pdfduplex .choices:nn = + {Simplex, DuplexFlipShortEdge, DuplexFlipLongEdge} + { + \pdf_version_compare:NnTF > {1.6} + { + \pdfmanagement_add:nnn {Catalog / ViewerPreferences } + { PrintDuplex } { /#1 } + } + { + \msg_warning:nnxx + {hyp} + {ignore-deprecated-or-unknown-option-in-pdf-version} + {pdfduplex} + {\pdf_version:} + } + }% + ,pdfduplex / .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { PrintDuplex } + } + ,pdfduplex / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice+empty } + { pdfduplex } + { Simplex, DuplexFlipShortEdge, DuplexFlipLongEdge } + { \exp_not:n {#1} } + } + ,pdffitwindow .choice: + ,pdffitwindow / false .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { FitWindow } + } + ,pdffitwindow / true .code:n = + { + \pdfmanagement_add:nnn {Catalog / ViewerPreferences } { FitWindow } { true } + } + ,pdffitwindow / .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { FitWindow } + } + ,pdffitwindow .default:n = true + ,pdffitwindow / unknown .code:n = + { + \msg_warning:nnxx { hyp } { no-bool } + { pdffitwindow } + { \exp_not:n {#1} } + } + ,pdflinkmargin .code:n = { \pdfannot_link_margin:n { #1 } } + ,pdflinkmargin .initial:n = {1pt} + ,pdfmenubar .choice: + ,pdfmenubar / true .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { HideMenubar } + } + ,pdfmenubar / false .code:n = + { + \pdfmanagement_add:nn {Catalog / ViewerPreferences } + { HideMenubar } { true } + } + ,pdfmenubar / .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { HideMenubar } + } + ,pdfmenubar .default:n = true + ,pdfmenubar / unknown .code:n = + { + \msg_warning:nnxx { hyp } { no-bool } + { pdfmenubar } + { \exp_not:n {#1} } + } + ,pdfnewwindow .choice: + ,pdfnewwindow / true .code:n = + { + \pdfdict_put:nnn {l_hyp/annot/A/GoToR}{/NewWindow}{true} + \pdfdict_put:nnn {l_hyp/annot/A/Launch}{/NewWindow}{true} + } + ,pdfnewwindow / false .code:n = + { + \pdfdict_put:nnn {l_hyp/annot/A/GoToR}{/NewWindow}{false} + \pdfdict_put:nnn {l_hyp/annot/A/Launch}{/NewWindow}{false} + } + ,pdfnewwindow / .code:n = + { + \pdfdict_remove:nn {l_hyp/annot/A/GoToR}{/NewWindow} + \pdfdict_remove:nn {l_hyp/annot/A/Launch}{/NewWindow} + } + ,pdfnonfullscreenpagemode .choices:nn = + { UseNone, UseOutlines, UseThumbs, FullScreen, UseOC } %pdf 1.5 + { + \pdfmanagement_add:nnx {Catalog / ViewerPreferences } + { NonFullScreenPageMode} {/#1} + } + ,pdfnonfullscreenpagemode / UseAttachments .code:n = + { + \pdf_version_compare:NnTF < {1.6} + { + %message + } + { + \pdfmanagement_add:nnx {Catalog / ViewerPreferences } + {NonFullScreenPageMode}{/UseAttachments} + } + } + ,pdfnonfullscreenpagemode / .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { NonFullScreenPageMode } + } + ,pdfnonfullscreenpagemode / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice+empty } + { pdfnonfullscreenpagemode } + { UseNone, UseOutlines, UseThumbs, FullScreen, UseOC, UseAttachments (PDF 1.6) } + { \exp_not:n {#1} } + } + ,pdfnumcopies .code:n = + { + \pdf_version_compare:NnTF > {1.6} + { + \tl_if_empty:nTF {#1} + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { NumCopies } + } + { + \pdfmanagement_add:nnx {Catalog / ViewerPreferences } + {NumCopies}{#1} + } + } + { + \msg_warning:nnxx + {hyp} + {ignore-deprecated-or-unknown-option-in-pdf-version} + {pdfnumcopies} + {\pdf_version:} + } + } + ,pdfpagelayout .choices:nn = + { SinglePage, OneColumn, TwoColumnLeft, TwoColumnRight, TwoPageLeft, TwoPageRight} + { \pdfmanagement_add:nnx {Catalog} { PageLayout }{ /#1 } } + ,pdfpagelayout / .code:n = + { \pdfmanagement_remove:nn {Catalog} { PageLayout } } + ,pdfpagelayout / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice+empty } + { pdfpagelayout } + { SinglePage, OneColumn, TwoColumnLeft, TwoColumnRight, TwoPageLeft, TwoPageRight } + { \exp_not:n {#1} } + } + ,pdfpagemode .choices:nn = + { UseNone, UseOutlines, UseThumbs, FullScreen, UseOC } %pdf 1.5 + { \pdfmanagement_add:nnx {Catalog} { PageMode }{ /#1 } } + ,pdfpagemode / UseAttachments .code:n = + { + \pdf_version_compare:NnTF > {1.5} + { + \pdfmanagement_add:nnx {Catalog} { PageMode }{ /UseAttachments } + } + { + \msg_warning:nnxx + {hyp} + {ignore-deprecated-or-unknown-value-in-pdf-version} + {UseAttachments} + {\pdf_version:} + } + } + ,pdfpagemode .initial:n = { UseOutlines } %for now ... + ,pdfpagemode / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice+empty } + { pdfpagemode } + { UseNone, UseOutlines, UseThumbs, FullScreen, UseOC, UseAttachments (PDF 1.6) } + { \exp_not:n {#1} } + } + ,pdfpagescrop .code:n = + { + \tl_if_empty:nTF %or blank? + { + \pdfmanagement_remove:nn {Pages} { CropBox } + } + { + \pdfmanagement_add:nnx {Pages} { CropBox } { [#1] } + } + } + ,pdfpicktraybypdfsize .choice: + ,pdfpicktraybypdfsize / true .code:n = + { + \pdf_version_compare:NnTF > {1.6} + { + \pdfmanagement_add:nnn {Catalog / ViewerPreferences } + { PickTrayByPDFSize } { true } + } + { + \msg_warning:nnxx + {hyp} + {ignore-deprecated-or-unknown-option-in-pdf-version} + {pdfpicktraybypdfsize} + {\pdf_version:} + } + } + ,pdfpicktraybypdfsize / false .code:n = + { + \pdf_version_compare:NnTF > {1.6} + { + \pdfmanagement_add:nnn {Catalog / ViewerPreferences } + { PickTrayByPDFSize } { false } + } + { + \msg_warning:nnxx + {hyp} + {ignore-deprecated-or-unknown-option-in-pdf-version} + {pdfpicktraybypdfsize} + {\pdf_version:} + } + } + ,pdfpicktraybypdfsize / .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { PickTrayByPDFSize } + } + ,pdfpicktraybypdfsize / unknown .code:n = + { + \msg_warning:nnxx { hyp } { no-bool } + { picktraybypdfsize } + { \exp_not:n {#1} } + } + ,pdfprintarea .choices:nn = + { MediaBox, CropBox, BleedBox, TrimBox, ArtBox } + { + \pdf_version_compare:NnTF < {2.0} + { + \pdfmanagement_add:nnx {Catalog / ViewerPreferences } + { PrintArea } { /#1 } + } + { + \msg_warning:nnxx + {hyp} + {ignore-deprecated-or-unknown-option-in-pdf-version} + {pdfprintarea} + {\pdf_version:} + } + }% + ,pdfprintarea / .code:n = + { \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { PrintArea } } + ,pdfprintarea / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice+empty } + { pdfprintarea } + { MediaBox, CropBox, BleedBox, TrimBox, ArtBox } + { \exp_not:n {#1} } + } + ,pdfprintclip .choices:nn = + { MediaBox, CropBox, BleedBox, TrimBox, ArtBox } + { + \pdf_version_compare:NnTF < {2.0} + { + \pdfmanagement_add:nnx {Catalog / ViewerPreferences } + { PrintClip } { /#1 } + } + { + \msg_warning:nnxx + {hyp} + {ignore-deprecated-or-unknown-option-in-pdf-version} + {pdfprintclip} + {\pdf_version:} + } + }% + ,pdfprintclip / .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { PrintClip } + } + ,pdfprintclip / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice+empty } + { pdfprintclip } + { MediaBox, CropBox, BleedBox, TrimBox, ArtBox } + { \exp_not:n {#1} } + } + ,pdfprintpagerange .code:n = + { + \pdf_version_compare:NnTF > {1.6} + { + \tl_if_empty:nTF { #1} + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } + { PrintPageRange } + } + { + \pdfmanagement_add:nnx {Catalog / ViewerPreferences } + {PrintPageRange}{[#1]} + } + } + { + \msg_warning:nnxx + {hyp} + {ignore-deprecated-or-unknown-option-in-pdf-version} + {pdfprintpagerange} + {\pdf_version:} + } + } + ,pdfprintscaling .choices:nn = + { None, AppDefault } + { + \pdf_version_compare:NnTF > {1.5} + { + \pdfmanagement_add:nnx {Catalog / ViewerPreferences } + { PrintScaling } { /#1 } + } + { + \msg_warning:nnxx + {hyp} + {ignore-deprecated-or-unknown-option-in-pdf-version} + {pdfprintscaling} + {\pdf_version:} + } + }% + ,pdfprintscaling / .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } {PrintScaling } + } + ,pdfprintscaling / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice+empty } + { pdfprintarea } + { None, AppDefault } + { \exp_not:n {#1} } + } + ,pdfremotestartview .code:n = + { + \tl_set:Nx \l_@@_tmpa_tl {#1~null~null~null~} + \exp_args:NNV + \regex_extract_once:NnNTF \c_@@_dest_startview_regex \l_@@_tmpa_tl \l_@@_tmpa_seq + { + \tl_set:Nx \l_@@_dest_pdfremotestartview_tl {\seq_item:Nn \l_@@_tmpa_seq {1}} + } + { + \msg_warning:nnnn {hyp}{invalid-destination-value}{#1}{pdfremotestartview} + \tl_set:Nn \l_@@_dest_pdfremotestartview_tl {Fit} + } + } + ,pdfremotestartview .initial:n = {Fit} + % pdfstartpage is special as it shares code with pdfstartview + ,pdfstartpage .code:n = + { + \tl_gset:Nx \g_@@_dest_pdfstartpage_tl { #1 } + \bool_if:nTF + { \tl_if_empty_p:N \g_@@_dest_pdfstartpage_tl || \tl_if_empty_p:N \g_@@_dest_pdfstartview_tl } + { + \pdfmanagement_remove:nn {Catalog} { OpenAction } + } + { + \pdfmanagement_add:nnx {Catalog} { OpenAction } + { + [\pdf_pageobject_ref:n {\g_@@_dest_pdfstartpage_tl}~/\g_@@_dest_pdfstartview_tl] + } + } + } + ,pdfstartpage .initial:n =1 + ,pdfstartview .code:n = + { + \tl_set:Nx \l_@@_tmpa_tl {#1~null~null~null~} + \exp_args:NNV + \regex_extract_once:NnNTF \c_@@_dest_startview_regex \l_@@_tmpa_tl \l_@@_tmpa_seq + { + \tl_gset:Nx \g_@@_dest_pdfstartview_tl {\seq_item:Nn \l_@@_tmpa_seq {1}} + } + { + \msg_warning:nnnn {hyp}{invalid-destination-value}{#1}{pdfstartview} + \tl_gset:Nn \g_@@_dest_pdfstartview_tl {Fit} + } + \bool_if:nTF + { \tl_if_empty_p:N \g_@@_dest_pdfstartpage_tl || \tl_if_empty_p:N \g_@@_dest_pdfstartview_tl } + { + \pdfmanagement_remove:nn {Catalog} { OpenAction } + } + { + \pdfmanagement_add:nnx {Catalog} { OpenAction } + { + [\pdf_pageobject_ref:n {\g_@@_dest_pdfstartpage_tl}~/\g_@@_dest_pdfstartview_tl] + } + } + } + ,pdfstartview .initial:n = Fit + ,pdftoolbar .choice: + ,pdftoolbar / true .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { HideToolbar } + } + ,pdftoolbar / false .code:n = + { + \pdfmanagement_add:nnn {Catalog / ViewerPreferences } + { HideToolbar } { true } + } + ,pdftoolbar / true .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { HideToolbar } + } + ,pdftoolbar .default:n = true + ,pdftoolbar / unknown .code:n = + { + \msg_warning:nnxx { hyp } { no-bool } + { pdftoolbar } + { \exp_not:n {#1} } + } + % pdfview see below. + ,pdfviewarea .choices:nn = + { MediaBox, CropBox, BleedBox, TrimBox, ArtBox } + { + \pdf_version_compare:NnTF < {2.0} + { + \pdfmanagement_add:nnn {Catalog / ViewerPreferences } + { ViewArea } { /#1 } + } + { + \msg_warning:nnxx + {hyp} + {ignore-deprecated-or-unknown-option-in-pdf-version} + {pdfviewarea} + {\pdf_version:} + } + }% + ,pdfviewarea / .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { ViewArea } + } + ,pdfviewarea / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice+empty } + { pdfviewarea } + { MediaBox, CropBox, BleedBox, TrimBox, ArtBox } + { \exp_not:n {#1} } + } + ,pdfviewclip .choices:nn = + { MediaBox, CropBox, BleedBox, TrimBox, ArtBox } + { + \pdf_version_compare:NnTF < {2.0} + { + \pdfmanagement_add:nnn {Catalog / ViewerPreferences } + { ViewClip } { /#1 } + } + { + \msg_warning:nnxx + {hyp} + {ignore-deprecated-or-unknown-option-in-pdf-version} + {pdfviewclip} + {\pdf_version:} + } + }% + ,pdfviewclip / .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { ViewClip } + } + ,pdfviewclip / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice+empty } + { pdfviewclip } + { MediaBox, CropBox, BleedBox, TrimBox, ArtBox } + { \exp_not:n {#1} } + } + ,pdfwindowui .choice: + ,pdfwindowui / true .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } { HideWindowUI } + } + ,pdfwindowui / false .code:n = + { + \pdfmanagement_add:nnn {Catalog / ViewerPreferences } + { HideWindowUI } { true } + } + ,pdfwindowui / .code:n = + { + \pdfmanagement_remove:nn {Catalog / ViewerPreferences } {HideWindowUI } + } + ,pdfwindowui / unknown .code:n = + { + \msg_warning:nnxx { hyp } { no-bool } + { pdfwindowui } + { \exp_not:n {#1} } + } + ,pdfwindowui .default:n = true + } +% \end{macrocode} +% +% \begin{hypkey}{pdfview} +% Destination keys. pdfview is a bit more complicated so extra. +% \begin{macrocode} +\keys_define:nn { hyp / setup } + { + ,pdfview .code:n = + { + \seq_set_split:Nnn \l_@@_tmpa_seq {~}{#1} + \str_case_e:nnF { \str_lowercase:f{ \seq_item:Nn \l_@@_tmpa_seq {1} } } + { + { xyz } + { + \int_compare:nNnTF {\seq_count:N \l_@@_tmpa_seq } > { 1 } + { + \seq_get_right:NN \l_@@_tmpa_seq \l_@@_tmpa_tl + \tl_if_eq:NnTF \l_@@_tmpa_tl {null} + { + \tl_set:Nn \l_@@_dest_pdfview_tl {xyz} + } + { + \tl_set:Nx \l_@@_dest_pdfview_tl + { + \fp_eval:n { \l_@@_tmpa_tl * 100 } + } + } + } + { + \tl_set:Nn \l_@@_dest_pdfview_tl {xyz} + } + } + { fit } { \tl_set:Nn \l_@@_dest_pdfview_tl {fit} } + { fitb } { \tl_set:Nn \l_@@_dest_pdfview_tl {fitb} } + { fitbh } { \tl_set:Nn \l_@@_dest_pdfview_tl {fitbh}} + { fitbv } { \tl_set:Nn \l_@@_dest_pdfview_tl {fitbv}} + { fith } { \tl_set:Nn \l_@@_dest_pdfview_tl {fith} } + { fitv } { \tl_set:Nn \l_@@_dest_pdfview_tl {fitv} } + { fitr } + { + \int_compare:nNnTF {\seq_count:N \l_@@_tmpa_seq } = {1} + { + \tl_set:Nn \l_@@_dest_pdfview_tl {fitr} + } + { + %ensure 4 values ... + \tl_set:Nn \l_@@_dest_pdfview_tl {fitrbox} + \seq_put_right:Nn \l_@@_tmpa_seq {0} + \seq_put_right:Nn \l_@@_tmpa_seq {0} + \seq_put_right:Nn \l_@@_tmpa_seq {0} + \hbox_set_to_wd:Nnn \l_@@_dest_box + { + \fp_eval:n + { + round + ( + abs + ( + \seq_item:Nn\l_@@_tmpa_seq{4} + - + (\seq_item:Nn\l_@@_tmpa_seq{2}) + ), + 3 + ) + }bp + }{} + \box_set_dp:Nn \l_@@_dest_box + { + \fp_eval:n + { + round(0 - (\seq_item:Nn\l_@@_tmpa_seq{3}),3) + }bp + } + \box_set_ht:Nn \l_@@_dest_box + { + \seq_item:Nn\l_@@_tmpa_seq{5}bp + } + } + } + } + { + \msg_warning:nnnn {hyp}{invalid-destination-value}{#1}{pdfview} + \tl_set:Nn \l_@@_dest_pdfview_tl {fit} + } + } + ,pdfview .initial:n = {xyz} + } +% \end{macrocode} +% \end{hypkey} +%\subsection{\enquote{MetaData keys}} +% The following keys are relevant for the metadata: the info dictionary and +% the xmp-metadata. +% \begin{hypkey}{pdflang} +% |pdflang| should be deprecated. +% \begin{macrocode} +\keys_define:nn { hyp / setup } + { + ,pdflang .code:n = + { + \tl_if_empty:nTF { #1 } + { + \pdfmanagement_remove:nn {Catalog} { Lang } + } + { + \pdfmanagement_add:nnx {Catalog} { Lang } { (#1) } + } + \@@_store_metadata:nn {pdflang}{#1} + } + } +% \end{macrocode} +% \end{hypkey} +% +% \subsubsection{\enquote{info keys}} +% \begin{hypkey}{pdfauthor,pdftitle,pdfcreator,pdfsubject,pdfproducer,pdfkeywords} +% The keys store their value also in the metadate container, so that hyperxmp can +% use them. +% \begin{macrocode} +\cs_new_protected:Npn \@@_setup_info_key:nn #1 #2 + { + \keys_define:nn { hyp / setup } + { + pdf#1 .code:n = + { + \tl_if_blank:nTF {##1} + { + \pdfmanagement_remove:nn {Info}{#2} + } + { + \@@_text_pdfstring_info:nN {##1}\l_@@_tmpa_str + \str_if_eq:VnF\l_@@_tmpa_str{<FEFF>} + { + \pdfmanagement_add:nnx {Info}{#2}{\l_@@_tmpa_str} + } + } + \@@_store_metadata:nn {pdf#1}{##1} + } + } + \keys_define:nn { hyp / info } + { + #2 .code:n = + { + \tl_if_blank:nTF {##1} + { + \pdfmanagement_remove:nn {Info}{#2} + } + { + \@@_text_pdfstring_info:nN {##1}\l_@@_tmpa_str + \str_if_eq:VnF\l_@@_tmpa_str{<FEFF>} + { + \pdfmanagement_add:nnx {Info}{#2}{\l_@@_tmpa_str} + } + } + \exp_args:Nx \@@_store_metadata:nn {pdf\str_lowercase:n{#1}}{##1} + } + ,unknown .code:n = + { + \@@_text_pdfstring_info:nN {##1}\l_@@_tmpa_str + \str_if_eq:VnF\l_@@_tmpa_str{<FEFF>} + { + \exp_args:Nno + \pdfmanagement_add:nnx {Info} + { \l_keys_key_str } {\l_@@_tmpa_str} + } + } + } + } +\@@_setup_info_key:nn {author} {Author} +\@@_setup_info_key:nn {title} {Title} +\@@_setup_info_key:nn {producer} {Producer} +\@@_setup_info_key:nn {creator} {Creator} +% ignored key: addtopdfcreator +\@@_setup_info_key:nn {subject} {Subject} +\@@_setup_info_key:nn {keywords} {Keywords} +% \end{macrocode} +% \end{hypkey} +% \begin{hypkey}{pdfcreationdate,pdfmoddate} +% These keys are not really needed. We store them too in the container. +% CreationDate and ModDate should not use the hex encoding. +% \begin{macrocode} +\cs_new_protected:Npn \@@_setup_info_date_key:nn #1 #2 + { + \keys_define:nn { hyp / setup } + { + pdf#1 .code:n = + { + \tl_if_blank:nTF {##1} + { + \pdfmanagement_remove:nn {Info}{#2} + } + { + \pdfmanagement_add:nnx {Info}{#2}{(\tl_to_str:n {##1})} + } + \@@_store_metadata:nn {pdf#1}{##1} + } + } + \keys_define:nn { hyp / info } + { + #2 .code:n = + { + \tl_if_blank:nTF {##1} + { + \pdfmanagement_remove:nn {Info}{#2} + } + { + \pdfmanagement_add:nnx {Info}{#2}{(\tl_to_str:n {##1})} + } + \exp_args:Nx \@@_store_metadata:nn {pdf\str_lowercase:n{#1}}{##1} + } + } + } + +\@@_setup_info_date_key:nn {creationdate} {CreationDate} +\@@_setup_info_date_key:nn {moddate} {ModDate} +% \end{macrocode} +% \end{hypkey} +% \begin{hypkey}{pdftrapped} +% Trapped is a bit curious, it has an value "unknown", and one can't suppress it ... +% \begin{macrocode} +\keys_define:nn { hyp / setup } + { + ,pdftrapped .code:n = + { + \exp_args:Nne + \keys_set:nn { hyp / setup } { _pdftrapped = \str_uppercase:n { #1 } } + } + ,_pdftrapped .choices:nn = {TRUE,FALSE,UNKNOWN} + { + \pdfmanagement_add:nnx {Info}{Trapped} + {/ + \str_uppercase:f { \str_head:n { #1 } } + \str_lowercase:f { \str_tail:n { #1 } } + } + \@@_store_metadata:nx {pdftrapped} + { + \str_uppercase:f { \str_head:n { #1 } } + \str_lowercase:f { \str_tail:n { #1 } } + } + } + ,_pdftrapped / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice } + { pdftrapped } + { true~(case~insensitive), false~(case~insensitive), unknown~(case~insensitive) } + { \exp_not:n {#1} } + } + } +% \end{macrocode} +% \end{hypkey} +% \begin{hypkey}{pdfinfo} +% pdfinfo allows to set the info keys with keyval ... +% \begin{macrocode} +\keys_define:nn { hyp / setup } + { + pdfinfo .code:n = + { + \keys_set:nn { hyp / info } { #1 } + } + } +% \end{macrocode} +% \end{hypkey} +% Now we set some default values +% \begin{macrocode} +\keys_set:nn { hyp / setup} {pdfcreator = LaTeX~with~hyperref} +\keys_set:nn { hyp / setup} {pdfauthor = } +\keys_set:nn { hyp / setup} {pdftitle = } +\keys_set:nn { hyp / setup} {pdfsubject = } +% \end{macrocode} +% +% \subsection{hyperxmp keys} +% hyperxmp defines lots of keys for \cs{hypersetup}. +% They now longer work with this driver. So we provide them, +% but they are only stored as metadata: +% \begin{macrocode} +\clist_map_inline:nn + { + ,pdfcopyright + ,pdftype + ,pdflicenseurl + ,pdfauthortitle + ,pdfcaptionwriter + ,pdfmetalang + ,pdfapart + ,pdfaconformance + ,pdfuapart + ,pdfxstandard + ,pdfsource + ,pdfdocumentid + ,pdfinstanceid + ,pdfversionid + ,pdfrendition + ,pdfpublication + ,pdfpubtype + ,pdfbytes + ,pdfnumpages + ,pdfissn + ,pdfeissn + ,pdfisbn + ,pdfbookedition + ,pdfpublisher + ,pdfvolumenum + ,pdfissuenum + ,pdfpagerange + ,pdfdoi + ,pdfurl + ,pdfidentifier + ,pdfsubtitle + ,pdfpubstatus + ,pdfcontactaddress + ,pdfcontactcity + ,pdfcontactregion + ,pdfcontactpostcode + ,pdfcontactcountry + ,pdfcontactphone + ,pdfcontactemail + ,pdfcontacturl + ,pdfdate + } + { + \keys_define:nn { hyp / setup } + { + #1 .code:n= { \@@_store_metadata:nn {#1}{##1}} + } + } +% \end{macrocode} +% +% \subsection{Transitions} +% pdfpageduration sets the duration a page is shown in full screen mode. +% \begin{macrocode} +\keys_define:nn { hyp / setup } + { + pdfpageduration .code:n = + { + \tl_if_blank:nTF { #1 } + { + \pdfmanagement_remove:nn {Page}{Dur} + } + { + \pdfmanagement_add:nnn {Page}{Dur}{#1} + } + } + } +% \end{macrocode} +% Transition settings are used by (some) pdf viewers when presenting a +% pdf in full screen mode. They are added to the page settings and describe the +% transition from the previous page to current page. Transition setting can be +% set in the preamble for all pages or in the document for the current and the +% following pages. Due to the asynchronous page breaking one has to be careful +% to set it on the right page, e.g. only after a |\newpage|. +% The generic driver uses a different syntax than the other hyperref drivers: +% various transition options can be set by a keyval syntax in the value of +% |pdfpagetransition|. A typical setting looks e.g. like this\\ +% |\hypersetup{pdfpagetransition={style=Fly,duration=2,direction=90,opaque=false}}| +% +% The keys allowed in the argument of |pdfpagetransition| are +% \begin{tabular}{l>{\raggedright\arraybackslash}p{6cm}} +% style & one of Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade\\ +% duration & a number, describes the duration of the transition\\ +% direction& \begin{tabular}[t]{l} +% H~(horizontal,~only~Split,~Blinds)\\ +% V~(vertical,~only~Split,~Blinds)\\ +% 0~(left~to~right,~only~Wipe,~Glitter,~Fly,~Cover,~Uncover,~Push)\\ +% 90~(bottom~to~top,~only~Wipe)\\ +% 180~(right~to~left,~only~Wipe)\\ +% 270~(top~to~bottom,~only~Wipe,~Glitter,~Fly,~Cover,~Uncover,~Push)\\ +% 315~(top~left~to~bottom,~only~Glitter)\\ +% None~(only~Fly) +% \end{tabular}\\ +% motion & one of I, O, only relevant for Split, Box and Fly\\ +% scale & a number, only relevant for Fly style \\ +% opaque & true or false, only relevant for Fly style +% \end{tabular} +% \begin{macrocode} +\keys_define:nn { hyp / setup } + { + pdfpagetransition .code:n = + { + \tl_if_blank:nTF {#1} + { + \pdfmanagement_remove:nn {Page}{Trans} + } + { + \group_begin: + \keys_set:nn { hyp / trans }{style=R,#1} + \pdf_object_unnamed_write:nx { dict } + { + \pdfdict_use:n {l_@@_page/Trans} + } + \pdfmanagement_add:nnx {Page}{Trans}{\pdf_object_ref_last:} + \group_end: + } + } + } +\keys_define:nn { hyp / trans } + { + ,style .choices:nn = + {Split,Blinds,Box,Wipe,Dissolve,Glitter,R,Fly,Push,Cover,Uncover,Fade} + { \pdfdict_put:nnn {l_@@_page/Trans}{ S }{/#1} } + ,style / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice } + { trans / style } + { Split,Blinds,Box,Wipe,Dissolve,Glitter,R,Fly,Push,Cover,Uncover,Fade } + { \exp_not:n {#1} } + } + ,duration .code:n = + { + \pdfdict_put:nnn {l_@@_page/Trans}{ D }{#1} + } + ,direction .choices:nn = + {H,V} + { \pdfdict_put:nnn {l_@@_page/Trans}{ S }{/#1} } + ,direction .choices:nn = + {0,90,180,270,315} + { \pdfdict_put:nnn {l_@@_page/Trans}{ DI }{ #1 } } + ,direction / None .code:n = + { \pdfdict_put:nnn {l_@@_page/Trans}{ DI }{ /None } } + ,direction / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice } + { trans / direction } + { + H~(horizontal,~only~Split,~Blinds), + V~(vertical,~only~Split,~Blinds), + 0~(left~to~right,~only~Wipe,~Glitter,~Fly,~Cover,~Uncover,~Push), + 90~(bottom~to~top,~only~Wipe), + 180~(right~to~left,~only~Wipe), + 270~(top~to~bottom,~only~Wipe,~Glitter,~Fly,~Cover,~Uncover,~Push), + 315~(top~left~to~bottom,~only~Glitter), + None~(only~Fly) + } + { \exp_not:n {#1} } + } + ,motion .choices:nn = + {I,O} + { \pdfdict_put:nnn {l_@@_page/Trans}{ M }{/#1} } + ,motion / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice } + { trans / motion } + { I~(inwards) , O~(outwards) } + { \exp_not:n {#1} } + } + ,scale .code:n = + { \pdfdict_put:nnn { l_@@_page/Trans }{ SS }{ #1 } } + ,opaque .choices:nn = {true,false} + { \pdfdict_put:nnn { l_@@_page/Trans }{ B } { #1} } + ,opaque / unknown .code:n = + { + \msg_warning:nnxxx { hyp } { unknown-choice } + { trans / B } + { true~(opaque~back,~only~Fly), false~(opaque~back,~only~Fly) } + { \exp_not:n {#1} } + } + % try to set unknown keys as style + ,unknown .code:n = + { + % warning ... + \exp_args:Nnx\keys_set:nn {hyp/trans}{ style=\l_keys_key_str } + } + } +% \end{macrocode} +% +% Finally we process the package option list, to get most keys working +% \begin{macrocode} +\keys_set_known:nv{hyp/setup}{opt@hyperref.sty} +% \end{macrocode} +% +% Unfinished +%% Form field code +% \begin{macrocode} +\NewDocumentCommand \MakeFieldObject { m m } + { + \pdfxform_new:nnn { #2 }{} { #1 } + } + + +\prop_new:N \g_@@_AcroForm_CoFields_prop +\prop_new:N \g_@@_AcroForm_Fields_prop + +\let\HyField@afields\ltx@empty +\let\HyField@cofields\ltx@empty +%% UF test for old pdftex removed +%\let\HyField@AuxAddToFields\ltx@gobble +%\let\HyField@AuxAddToCoFields\ltx@gobbletwo +\def\HyField@AfterAuxOpen{\Hy@AtBeginDocument}% + +% the value doesn't matter, but with a prop we avoid duplicates and it is +% clearly faster than removing them from a sequence +\def\HyField@AuxAddToFields#1 + { + \prop_gput:Nnn \g__hyp_AcroForm_Fields_prop {#1}{F} + }% + +%fields with empty key get a value too -- lets hope that +%this give the expected behaviour +\def\HyField@AuxAddToCoFields #1 #2 + { + \prop_gput:Nnn \g__hyp_AcroForm_CoFields_prop {a#1}{#2} + } + +\Hy@AtBeginDocument + { + \if@filesw + \immediate\write\@mainaux{% + \string\providecommand\string\HyField@AuxAddToFields[1]{}% + }% + \immediate\write\@mainaux{% + \string\providecommand\string\HyField@AuxAddToCoFields[2]{}% + }% + \fi + \let\HyField@AfterAuxOpen\@firstofone + }% + +\def\HyField@AddToFields + { + \exp_args:Nx\HyField@@AddToFields + { + \pdfannot_box_ref_last: + } + \ifx\Fld@calculate@code\ltx@empty + \else + \begingroup + \Hy@safe@activestrue + \edef\Hy@temp{% + \endgroup + \if@filesw + \write\@mainaux + { + \string\HyField@AuxAddToCoFields + { + \Fld@calculate@sortkey + } + { + \pdfannot_box_ref_last: + } + } + \fi + }% + \Hy@temp + \fi + }% + +\def\HyField@@AddToFields#1{ + \HyField@AfterAuxOpen{% + \if@filesw + \write\@mainaux{% + \string\HyField@AuxAddToFields{#1}% + }% + \fi + }% + }% + +\ExplSyntaxOff +\ExplSyntaxOn +\tl_new:N \l_@@_CheckmarkYes_tl +\tl_set:Nn \l_@@_CheckmarkYes_tl { __hyp_xform_CheckMarkYes } +\tl_new:N \l_@@_CheckmarkOff_tl +\tl_set:Nn \l_@@_CheckmarkOff_tl { __hyp_xform_CheckMarkOff } + +\def\@Form[#1] + { + \@ifundefined{textcolor}{\let\textcolor\@gobble}{} + \kvsetkeys{Form}{#1} + \pdf@ifdraftmode{} + { + \Hy@FormObjects + \prop_map_inline:Nn \g__hyp_AcroForm_Fields_prop + { + \pdfmanagement_add:nnx { Catalog / AcroForm } { Fields }{##1} + %\pdfmanagement_show:n { Catalog / AcroForm } + } + \prop_if_empty:NF \g__hyp_AcroForm_CoFields_prop + { + \prop_map_inline:Nn \g__hyp_AcroForm_CoFields_prop + { + \seq_put_right:Nn \l_@@_tmpa_seq {##1} + } + \seq_sort:Nn \l_@@_tmpa_seq + { + \int_compare:nNnTF { \pdf@strcmp{##1}{##2} } > { 0 } + { \sort_return_swapped: } + { \sort_return_same: } + } + \seq_map_inline:Nn \l_@@_tmpa_seq + { + \pdfmanagement_add:nnx { Catalog / AcroForm } + { CO } + { + \prop_item:Nn \g__hyp_AcroForm_CoFields_prop {##1} + } + } + } + \pdfmanagement_add:nnx {Catalog / AcroForm/DR/Font } + {ZaDb} {\pdf_object_ref:n {l_@@_font_zapfdingbats_obj} } + \pdfmanagement_add:nnx {Catalog / AcroForm/DR/Font } + {Helv} {\pdf_object_ref:n {l_@@_font_helvetica_obj} } + \pdfmanagement_add:nnx {Catalog /AcroForm} + {DA}{(/Helv~10~Tf~0~g)} + \pdfmeta_standard_verify:nTF {form_no_NeedAppearance} + { + \legacy_if:nT { HyField@NeedAppearances } + { + \pdfmanagement_add:nnn {Catalog / AcroForm }{NeedAppearances}{true} + } + } + { + \pdfmanagement_remove:nn {Catalog / AcroForm }{NeedAppearances} + } + } + \MakeFieldObject + { + \group_begin: + \fontfamily{pzd} + \fontencoding{U} + \fontseries{m} + \fontshape{n} + \selectfont + \char123 + \group_end: + } + {__hyp_xform_Ding} + \MakeFieldObject + { + \group_begin: + \fontfamily{pzd} + \fontencoding{U} + \fontseries{m} + \fontshape{n} + \selectfont + \phantom{\char123} + \group_end: + } + {__hyp_xform_DingOff} + \MakeFieldObject + { + \group_begin: + \fontfamily{pzd} + \fontencoding{U} + \fontseries{m} + \fontshape{n} + \selectfont + \char51 + \group_end: + } + {__hyp_xform_CheckMarkYes} + \MakeFieldObject + { + \group_begin: + \fontfamily{pzd} + \fontencoding{U} + \fontseries{m} + \fontshape{n} + \selectfont + \phantom{\char51} %perhaps xetex needs some small glyph .. + \group_end: + } + {__hyp_xform_CheckMarkOff} + \MakeFieldObject + { + \fbox{\textcolor{yellow}{\textsf{Submit}}} %color? + } + {__hyp_xform_Submit} + \MakeFieldObject + { + \fbox{\textcolor{yellow}{\textsf{SubmitP}}} %color? + } + {__hyp_xform_SubmitP} + } +\ExplSyntaxOff +\let\@endForm\ltx@empty +\let\HyAnn@AbsPageLabel\ltx@empty +\let\Fld@pageobjref\ltx@empty + +\ExplSyntaxOn +\newcount\HyAnn@Count +\HyAnn@Count=\ltx@zero +\def\HyAnn@AbsPageLabel + { + \global\advance\HyAnn@Count by\ltx@one + %\zref@labelbyprops{HyAnn@\the\HyAnn@Count}{abspage}% + %\zref@labelbylist {HyAnn@\the\HyAnn@Count} {l3pdf} + %\zref@refused{HyAnn@\the\HyAnn@Count}% + \__hyp_ref_label:en {HyAnn@\the\HyAnn@Count}{abspage} + \__hyp_ref_check:en {HyAnn@\the\HyAnn@Count}{abspage} + }% +\def\Fld@pageobjref + { + \__hyp_ref_if_exist:enT {HyAnn@\the\HyAnn@Count}{abspage} + { + /P~\pdf_pageobject_ref:n + { + \__hyp_ref_value:en{HyAnn@\the\HyAnn@Count}{abspage} + } + } + } +\ExplSyntaxOff +\ExplSyntaxOn +%% check if the attr should be set through +%% hooks. +%% check if options are missing. +\def\@TextField[#1]#2{% parameters, label + \def\Fld@name{#2}% + \let\Fld@default\ltx@empty + \let\Fld@value\@empty + \def\Fld@width{\DefaultWidthofText}% + \def\Fld@height{% + \ifFld@multiline + \DefaultHeightofTextMultiline + \else + \DefaultHeightofText + \fi + }% + \begingroup + \expandafter\HyField@SetKeys\expandafter{% + \DefaultOptionsofText,#1% + }% + \PDFForm@Name + \HyField@FlagsText + \ifFld@hidden\def\Fld@width{1sp}\fi + \ifx\Fld@value\@empty\def\Fld@value{\Fld@default}\fi + \LayoutTextField{#2}{% + \leavevmode + \HyAnn@AbsPageLabel + \Hy@escapeform\PDFForm@Text + \pdfannot_box:nnnn + {\Fld@width} + {\Fld@height} + {0pt} %is this correct? + {\PDFForm@Text} + \MakeTextField{\Fld@width}{\Fld@height} + \HyField@AddToFields + }% + \endgroup +} +\providecommand\@curropt{} +\def\@ChoiceMenu[#1]#2#3{% parameters, label, choices + \def\Fld@name{#2} + \let\Fld@default\relax + \let\Fld@value\relax + \def\Fld@width{\DefaultWidthofChoiceMenu} + \def\Fld@height{\DefaultHeightofChoiceMenu} + \begingroup + \Fld@menulength=0 % + \@tempdima\z@ + \clist_map_variable:nNn { #3 } \@curropt + %\@for\@curropt:=#3\do + {% + \expandafter\Fld@checkequals\@curropt==\\% + \Hy@StepCount\Fld@menulength + \settowidth{\@tempdimb}{\@currDisplay}% + \ifdim\@tempdimb>\@tempdima\@tempdima\@tempdimb\fi + }% + \advance\@tempdima by~15\p@ + \begingroup + \HyField@SetKeys{#1} + \edef\x{\endgroup + \noexpand\expandafter + \noexpand\HyField@SetKeys + \noexpand\expandafter{% + \expandafter\noexpand\csname DefaultOptionsof% + \ifFld@radio + Radio% + \else + \ifFld@combo + \ifFld@popdown + PopdownBox% + \else + ComboBox% + \fi + \else + ListBox% + \fi + \fi + \endcsname + }% + }\x + \HyField@SetKeys{#1}% + \PDFForm@Name + \ifFld@hidden\def\Fld@width{1sp}\fi + \ifx\Fld@value\relax + \let\Fld@value\Fld@default + \fi + \LayoutChoiceField{#2}{% + \ifFld@radio + \HyField@FlagsRadioButton + \@@Radio{#3}% + \else + \begingroup + \HyField@FlagsChoice + \ifdim\Fld@width<\@tempdima + \ifdim\@tempdima<1cm\@tempdima1cm\fi + \edef\Fld@width{\the\@tempdima}% + \fi + \ifFld@combo + \else + \@tempdima=\the\Fld@menulength\Fld@charsize + \advance\@tempdima by~\Fld@borderwidth bp % + \advance\@tempdima by~\Fld@borderwidth bp % + \edef\Fld@height{\the\@tempdima}% + \fi + \@@Listbox{#3}% + \endgroup + \fi + }% + \endgroup +} +\tl_new:N \l_@@_RadioYes_tl +\tl_set:Nn \l_@@_RadioYes_tl { __hyp_xform_Ding } +\def\@@Radio#1{% + \Fld@listcount=0~% + %\show\Fld@default + \EdefEscapeName\Fld@default{\Fld@default}% + \clist_map_variable:nNn { #1 } \@curropt + %\@for\@curropt:=#1\do + {% + \expandafter\Fld@checkequals\@curropt==\\% + \EdefEscapeName\@currValue{\@currValue}% + \Hy@StepCount\Fld@listcount + \@currDisplay\space + \leavevmode + \HyAnn@AbsPageLabel + \Hy@escapeform\PDFForm@Radio + \pdfannot_box:nnnn + {\Fld@width} + {\Fld@height} + {0pt} %is this correct? + { + \PDFForm@Radio + /AP + << + /N + << + /\@currValue\c_space_tl \pdfxform_ref:o {__hyp_xform_Ding} + %/Off \c_space_tl \pdfxform_ref:n {__hyp_xform_DingOff} %hm + >> + >> + } + {\fbox{ \MakeRadioField{\Fld@width}{\Fld@height}} } + \int_compare:nNnT { \Fld@listcount} = { 1 } + { \HyField@AddToFields } + \c_space_tl % deliberate space between radio buttons + % to do: --> should be configurable + }% +} + +\newcount\Fld@listcount +\def\@@Listbox#1 + { + \HyField@PDFChoices{#1} + \mode_leave_vertical: + \HyAnn@AbsPageLabel + \Hy@escapeform\PDFForm@List + \pdf_link_user:nnn + {widget} %perhaps we need more types?? + {\PDFForm@List} + {\MakeChoiceField{\Fld@width}{\Fld@height}} + \HyField@AddToFields + } + + +\def\@PushButton[#1]#2{% parameters, label + \def\Fld@name{#2}% + \group_begin: + \exp_args:No\HyField@SetKeys + { + \DefaultOptionsofPushButton,#1 + } + \PDFForm@Name + \pdfmeta_standard_verify:nnTF {annot_action_A}{JavaScript} + { + \HyField@FlagsPushButton + \legacy_if:nT {Fld@hidden} + { + \def\Fld@width{1sp} + } + \LayoutPushButtonField + { + \mode_leave_vertical: + \HyAnn@AbsPageLabel + \Hy@escapeform\PDFForm@Push + \hbox_set:Nn \l_tmpa_box { \MakeButtonField {#2}} + \pdfannot_box:nnnn + {\box_wd:N\l_tmpa_box} + {\box_ht:N\l_tmpa_box} + {\box_dp:N\l_tmpa_box} %is this correct? + {\PDFForm@Push} + {\box_use:N\l_tmpa_box} + \HyField@AddToFields + } + } + { + \msg_error:nn { hyp }{ pdfa-no-push-button } + \LayoutPushButtonField + { + \mode_leave_vertical: + \MakeButtonField{#2} + } + } + \group_end: +} + +\def\@Submit[#1]#2 + { + \def\Fld@width {\DefaultWidthofSubmit} + \def\Fld@height{\DefaultHeightofSubmit} + \group_begin: + \exp_args:No\HyField@SetKeys + { + \DefaultOptionsofSubmit,#1 + } + \HyField@FlagsPushButton + \HyField@FlagsSubmit + \legacy_if:nT { Fld@hidden } + { + \def\Fld@width{1sp} + } + \mode_leave_vertical: + \HyAnn@AbsPageLabel + \Hy@escapeform\PDFForm@Submit + \hbox_set:Nn \l_tmpa_box { \MakeButtonField {#2}} + \pdfannot_box:nnnn + {\box_wd:N\l_tmpa_box} + {\box_ht:N\l_tmpa_box} + {\box_dp:N\l_tmpa_box} %is this correct? + { + \PDFForm@Submit + /AP<< + /N~\pdfxform_ref:n {__hyp_xform_Submit}~ + /D~\pdfxform_ref:n {__hyp_xform_SubmitP} + >> + } + \HyField@AddToFields + \box_use:N\l_tmpa_box + + \group_end: + } + +\def\@Reset[#1]#2 + { + \def\Fld@width {\DefaultWidthofReset} + \def\Fld@height{\DefaultHeightofReset} + \group_begin: + \exp_args:No\HyField@SetKeys + { + \DefaultOptionsofReset,#1 + } + \mode_leave_vertical: + \pdfmeta_standard_verify:nnTF {annot_action_A}{ResetForm} + { + \HyField@FlagsPushButton + \legacy_if:nT { Fld@hidden } + { \def\Fld@width{1sp} } + \HyAnn@AbsPageLabel + \Hy@escapeform\PDFForm@Reset + \hbox_set:Nn \l_tmpa_box { \MakeButtonField {#2}} + \pdfannot_box:nnnn + {\box_wd:N\l_tmpa_box} + {\box_ht:N\l_tmpa_box} + {\box_dp:N\l_tmpa_box} %is this correct? + { \PDFForm@Reset } + \HyField@AddToFields + \box_use:N \l_tmpa_box + } + { + \msg_error:nn { hyp }{ pdfa-no-reset-button } + \MakeButtonField{#2} + } + \group_end: + } + +\def\@CheckBox[#1]#2 + {% parameters, label + \def\Fld@name{#2} + \def\Fld@default{0} + \group_begin: + \def\Fld@width {\DefaultWidthofCheckBox} + \def\Fld@height{\DefaultHeightofCheckBox} + \exp_args:No\HyField@SetKeys + { + \DefaultOptionsofCheckBox,#1 + } + \PDFForm@Name + \HyField@FlagsCheckBox + \legacy_if:nT { Fld@hidden } + { + \def\Fld@width{1sp} + } + \LayoutCheckField{#2} + { + \mode_leave_vertical: + \HyAnn@AbsPageLabel + \Hy@escapeform\PDFForm@Check + \pdfannot_box:nnnn + {\Fld@width} + {\Fld@height} + {0pt} %is this correct? + {\PDFForm@Check} + \HyField@AddToFields %check if this works with xelatex ... + } + \group_end: + } +\ExplSyntaxOff + +%hm. Should a luatex driver use type1 fonts in fields???? +\ExplSyntaxOn +\def\Hy@FormObjects + { + \pdf_object_new:nn {l_@@_encoding_pdfdoc_obj } { dict } + \pdf_object_new:nn {l_@@_font_zapfdingbats_obj } { dict } + \pdf_object_new:nn {l_@@_font_helvetica_obj } { dict } + \pdf_object_write:nx {l_@@_encoding_pdfdoc_obj } + { + /Type/Encoding + /Differences[ + 24/breve/caron/circumflex/dotaccent/hungarumlaut/ogonek + /ring/tilde + \c_space_tl + 39/quotesingle + \c_space_tl + 96/grave % + \iow_newline: + 128/bullet/dagger/daggerdbl/ellipsis/emdash/endash/florin + /fraction/guilsinglleft/guilsinglright/minus/perthousand + /quotedblbase/quotedblleft/quotedblright/quoteleft + /quoteright/quotesinglbase/trademark/fi/fl/Lslash/OE + /Scaron/Ydieresis/Zcaron/dotlessi/lslash/oe/scaron/zcaron + \iow_newline: + 164/currency + \c_space_tl + 166/brokenbar + \c_space_tl + 168/dieresis/copyright/ordfeminine + \c_space_tl + 172/logicalnot/.notdef/registered/macron/degree/plusminus + /twosuperior/threesuperior/acute/mu + \c_space_tl + 183/periodcentered/cedilla/onesuperior/ordmasculine + \c_space_tl + 188/onequarter/onehalf/threequarters + \iow_newline: + 192/Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE + /Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave + /Iacute/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute + /Ocircumflex/Otilde/Odieresis/multiply/Oslash/Ugrave + /Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls + /agrave/aacute/acircumflex/atilde/adieresis/aring/ae + /ccedilla/egrave/eacute/ecircumflex/edieresis/igrave + /iacute/icircumflex/idieresis/eth/ntilde/ograve/oacute + /ocircumflex/otilde/odieresis/divide/oslash/ugrave + /uacute/ucircumflex/udieresis/yacute/thorn/ydieresis + ] + } + \pdf_object_write:nn {l_@@_font_zapfdingbats_obj } + { + /Type/Font + /Subtype/Type1 + /Name/ZaDb + /BaseFont/ZapfDingbats + } + \pdf_object_write:nx {l_@@_font_helvetica_obj } + { + /Type/Font + /Subtype/Type1 + /Name/Helv + /BaseFont/Helvetica + /Encoding~\pdf_object_ref:n { l_@@_encoding_pdfdoc_obj } + } + \global\let\Hy@FormObjects\relax + } +\ExplSyntaxOff +\providecommand*{\Fld@pageobjref}{} +\ifcsname pdf@escapestring\endcsname + \def\Hy@escapeform#1{% + \ifHy@pdfescapeform + \let\Hy@escapestring\pdfescapestring + \else + \let\Hy@escapestring\@firstofone + \fi + }% + \Hy@escapeform{}% +\else + \let\Hy@escapestring\@firstofone + \def\Hy@escapeform#1{% + \ifHy@pdfescapeform + \def\Hy@escapestring##1{% + \noexpand\Hy@escapestring{\noexpand##1}% + }% + \edef\Hy@temp{#1}% + \expandafter\Hy@@escapeform\Hy@temp\Hy@escapestring{}\@nil + \def\Hy@escapestring##1{% + \@ifundefined{Hy@esc@\string##1}{% + ##1% + \ThisShouldNotHappen + }{% + \csname Hy@esc@\string##1\endcsname + }% + }% + \else + \let\Hy@escapestring\@firstofone + \fi + }% + \def\Hy@@escapeform#1\Hy@escapestring#2#3\@nil{% + \ifx\\#3\\% + \else + \expandafter + \Hy@pstringdef\csname Hy@esc@\string#2\endcsname{#2}% probably string-hex + \ltx@ReturnAfterFi{% + \Hy@@escapeform#3\@nil + }% + \fi + }% +\fi +\def\PDFForm@Name{% + \PDFForm@@Name\Fld@name + \ifx\Fld@altname\relax + \else + \PDFForm@@Name\Fld@altname + \fi + \ifx\Fld@mappingname\relax + \else + \PDFForm@@Name\Fld@mappingname + \fi +} +\def\PDFForm@@Name#1{% + \begingroup + \ifnum\Hy@pdfversion<5 % implementation note 117, PDF spec 1.7 + \ifHy@unicode + \Hy@unicodefalse + \fi + \fi + \pdfstringdef\Hy@gtemp#1% + \endgroup + \let#1\Hy@gtemp +} +\def\Fld@X@additionalactions{% + \ifx\Fld@keystroke@code\@empty + \else + /K<</S/JavaScript/JS(\Hy@escapestring{\Fld@keystroke@code})>>% + \fi + \ifx\Fld@format@code\@empty + \else + /F<</S/JavaScript/JS(\Hy@escapestring{\Fld@format@code})>>% + \fi + \ifx\Fld@validate@code\@empty + \else + /V<</S/JavaScript/JS(\Hy@escapestring{\Fld@validate@code})>>% + \fi + \ifx\Fld@calculate@code\@empty + \else + /C<</S/JavaScript/JS(\Hy@escapestring{\Fld@calculate@code})>>% + \fi + \ifx\Fld@onfocus@code\@empty + \else + /Fo<</S/JavaScript/JS(\Hy@escapestring{\Fld@onfocus@code})>>% + \fi + \ifx\Fld@onblur@code\@empty + \else + /Bl<</S/JavaScript/JS(\Hy@escapestring{\Fld@onblur@code})>>% + \fi + \ifx\Fld@onmousedown@code\@empty + \else + /D<</S/JavaScript/JS(\Hy@escapestring{\Fld@onmousedown@code})>>% + \fi + \ifx\Fld@onmouseup@code\@empty + \else + /U<</S/JavaScript/JS(\Hy@escapestring{\Fld@onmouseup@code})>>% + \fi + \ifx\Fld@onenter@code\@empty + \else + /E<</S/JavaScript/JS(\Hy@escapestring{\Fld@onenter@code})>>% + \fi + \ifx\Fld@onexit@code\@empty + \else + /X<</S/JavaScript/JS(\Hy@escapestring{\Fld@onexit@code})>>% + \fi +} +\ExplSyntaxOn +\def\Fld@additionalactions + {% + \exp_args:Ne\str_if_eq:nnF {\Fld@X@additionalactions}{} + { + \pdfmeta_standard_verify:nT {annot_widget_no_AA} + {/AA<<\Fld@X@additionalactions>>} + } + } +\ExplSyntaxOff +\def\Fld@annotnames{% + /T(\Fld@name)% + \ifx\Fld@altname\relax + \else + /TU(\Fld@altname)% + \fi + \ifx\Fld@mappingname\relax + \else + /TM(\Fld@mappingname)% + \fi +} +\ExplSyntaxOn +\def\PDFForm@Check + { + /Subtype/Widget + ~\Fld@annotflags + ~\Fld@pageobjref + ~\Fld@annotnames + /FT/Btn + \Fld@flags + /Q~\Fld@align + /BS<</W~\Fld@borderwidth /S/\Fld@borderstyle>> + /AP + << + /N + << + /Yes~\pdfxform_ref:o{\l_@@_CheckmarkYes_tl} + /Off~\pdfxform_ref:o{\l_@@_CheckmarkOff_tl} + >> + >> + /MK<< + \int_compare:nNnF {\Fld@rotation}={0} + { + /R~\Fld@rotation + } + \tl_if_empty:NF\Fld@bordercolor + { + /BC[\Fld@bordercolor] + } + \tl_if_empty:NF\Fld@bcolor + { + /BG[\Fld@bcolor] + } + /CA(\Hy@escapestring{\Fld@cbsymbol})% + >> + /DA + ( + /ZaDb~\strip@pt\Fld@charsize\c_space_tl Tf + \tl_if_empty:NF \Fld@color + { + \c_space_tl \Fld@color + } + ) + /H/P + \legacy_if:nTF {Fld@checked} + { + /V/Yes /AS/Yes + } + { + /V/Off /AS/Off + } + \Fld@additionalactions +} +\ExplSyntaxOff +\ExplSyntaxOn + \def\PDFForm@Push + { + /Subtype/Widget + ~\Fld@annotflags + ~\Fld@pageobjref + ~\Fld@annotnames + /FT/Btn + ~\Fld@flags + /H/P + /BS<</W~\Fld@borderwidth/S/\Fld@borderstyle>> + \bool_if:nT + { + !\int_compare_p:nNn {\Fld@rotation} = {0} + || + \tl_if_exist_p:N \Fld@bordercolor + } + { + /MK + << + \int_compare:nNnF {\Fld@rotation} = {0} + { + /R~\Fld@rotation + } + \tl_if_exist:NT \Fld@bordercolor + { + /BC[\Fld@bordercolor] + } + >> + } + /A<</S/JavaScript/JS(\Hy@escapestring{\Fld@onclick@code})>> + \Fld@additionalactions + } + +\ExplSyntaxOff +\def\PDFForm@List{% + /Subtype/Widget% + \Fld@annotflags + \Fld@pageobjref + \Fld@annotnames + /FT/Ch% + \Fld@flags + /Q \Fld@align + /BS<</W \Fld@borderwidth/S/\Fld@borderstyle>>% + \ifcase0\ifnum\Fld@rotation=\z@ \else 1\fi + \ifx\Fld@bordercolor\relax\else 1\fi + \ifx\fld@bcolor\relax \else 1\fi + \space + \else + /MK<<% + \ifnum\Fld@rotation=\z@ + \else + /R \Fld@rotation + \fi + \ifx\Fld@bordercolor\relax + \else + /BC[\Fld@bordercolor]% + \fi + \ifx\Fld@bcolor\relax + \else + /BG[\Fld@bcolor]% + \fi + >>% + \fi + /DA(/Helv \strip@pt\Fld@charsize\space Tf% + \ifx\Fld@color\@empty\else\space\Fld@color\fi)% + \Fld@choices + \Fld@additionalactions +} +\ExplSyntaxOn +\def\PDFForm@Radio + { + /Subtype/Widget + ~\Fld@annotflags + ~\Fld@pageobjref + ~\Fld@annotnames + /FT/Btn + \Fld@flags + /H/P + /BS<</W~\Fld@borderwidth/S/\Fld@borderstyle>> + /MK<< + \ifnum\Fld@rotation=\z@ + \else + /R~\Fld@rotation + \fi + \ifx\Fld@bordercolor\relax + \else + /BC[\Fld@bordercolor]% + \fi + \ifx\Fld@bcolor\relax + \else + /BG[\Fld@bcolor]% + \fi + /CA(\Hy@escapestring{\Fld@radiosymbol})% + >> + /DA(/ZaDb~\strip@pt\Fld@charsize\space Tf% + \ifx\Fld@color\@empty\else\space\Fld@color\fi)% + \ifx\Fld@default\@empty + /V/Off% + /DV/Off% + \else + /V/\Fld@default + /DV/\Fld@default + \fi + \Fld@additionalactions + } +\ExplSyntaxOff +\ExplSyntaxOn +% Does an appeareance dict make sense here? +\def\PDFForm@Text + { + /Subtype/Widget + ~\Fld@annotflags + ~\Fld@pageobjref + ~\Fld@annotnames + /FT/Tx + ~\Fld@flags + /Q~\Fld@align + /BS<</W~\Fld@borderwidth\c_space_tl /S /\Fld@borderstyle>> + \bool_if:nT + { + !\int_compare_p:nNn {\Fld@rotation} = {0} + || + \tl_if_exist_p:N \Fld@bordercolor + || + \tl_if_exist_p:N \Fld@bcolor + } + { + /MK + << + \int_compare:nNnF {\Fld@rotation} = {0} + { + /R~\Fld@rotation + } + \tl_if_exist:NT \Fld@bordercolor + { + /BC[\Fld@bordercolor] + } + \tl_if_exist:NT \Fld@bcolor + { + /BG[\Fld@bcolor] + } + >> + } + /DA + ( + /Helv~\strip@pt\Fld@charsize\c_space_tl Tf + \tl_if_empty:NF {\c_space_tl\Fld@color} + ) + /DV(\Hy@escapestring{\Fld@default}) + /V(\Hy@escapestring{\Fld@value}) + ~\Fld@additionalactions + \int_compare:nNnT { \Fld@maxlen}>{0} + { + /MaxLen~\Fld@maxlen + } + } +\ExplSyntaxOff + +\def\PDFForm@Submit{% + /Subtype/Widget% + \Fld@annotflags + \Fld@pageobjref + \Fld@annotnames + /FT/Btn% + \Fld@flags + /H/P% + /BS<</W \Fld@borderwidth/S/\Fld@borderstyle>>% + \ifcase0\ifnum\Fld@rotation=\z@ \else 1\fi + \ifx\Fld@bordercolor\relax\else 1\fi + \space + \else + /MK<<% + \ifnum\Fld@rotation=\z@ + \else + /R \Fld@rotation + \fi + \ifx\Fld@bordercolor\relax + \else + /BC[\Fld@bordercolor]% + \fi + >>% + \fi + /A<<% + /S/SubmitForm% + /F<<% + /FS/URL% + /F(\Hy@escapestring{\Form@action})% + >>% + \Fld@submitflags + >>% + \Fld@additionalactions +} +\ExplSyntaxOn + \def\PDFForm@Reset{% + /Subtype/Widget% + \Fld@annotflags + \Fld@pageobjref + \Fld@annotnames + /FT/Btn% + \Fld@flags + /H/P% + /DA(/Helv~\strip@pt\Fld@charsize\space Tf~0~0~1~rg)% + \ifcase0\ifnum\Fld@rotation=\z@ \else 1\fi + \ifx\Fld@bordercolor\relax\else 1\fi + \space + \else + /MK<<% + \ifnum\Fld@rotation=\z@ + \else + /R~\Fld@rotation + \fi + \ifx\Fld@bordercolor\relax + \else + /BC[\Fld@bordercolor]% + \fi + >>% + \fi + /BS<</W \Fld@borderwidth/S/\Fld@borderstyle>>% + /A<</S/ResetForm>>% + \Fld@additionalactions + }% + + + %these patterns are used in hyperref checks. +%it is unclear if they are really useful and if a backend support is +%needed. +\str_case:VnF \c_sys_backend_str + { + { pdfmode } + { + \def\HyPat@ObjRef + { + [0-9]*[1-9][0-9]*~0~R + } + } + { dvipdfmx } + { + \def\HyPat@ObjRef + { + @[^~]+ + } + } + { xdvipdfmx } + { + \def\HyPat@ObjRef + { + @[^~]+ + } + } + } + { %also set in hyperref sty, so probably not needed. + \def\HyPat@ObjRef/{.+} + } + + +\ExplSyntaxOff +% UF: removed Hy@writebookmark +% \Hy@currentbookmarklevel{0} +% \Hy@numberline +% \@@writetorep +% counter{bookmark@seq@number} +% removed \HyPsd@SanitizeForOutFile, not needed +% removed \currentpdfbookmark, defined by bookmark, +% should use \newcommand there +% removed \subpdfbookmark, defined by bookmark, +% should use \newcommand there +% removed \belowpdfbookmark, defined by bookmark, +% should use \newcommand there +% removed \pdfbookmark, defined by bookmark, +% \BOOKMARK +% \@BOOKMARK +%% \RequirePackage{rerunfilecheck}[2009/12/10] +%% removed \Hy@OutlineRerunCheck, unneeded with bookmark +%% removed \ReadBookmarks / unneeded with bookmark. +%% removed \Hy@OutlineName +%% removed \check@bm@number +%% removed \calc@bm@number + +\ifHy@implicit +\else + \expandafter\endinput +\fi +\newlength\Hy@SectionHShift +\def\Hy@SectionAnchorHref#1{% + \ifx\protect\@typeset@protect + \Hy@@SectionAnchor{#1}% + \fi +} +\DeclareRobustCommand*{\Hy@@SectionAnchor}[1]{% + \leavevmode + \hbox to 0pt{% + \kern-\Hy@SectionHShift + \Hy@raisedlink{% + \hyper@anchorstart{#1}\hyper@anchorend + }% + \hss + }% +} +\let\H@old@ssect\@ssect +\def\@ssect#1#2#3#4#5{% + \Hy@MakeCurrentHrefAuto{section*}% + \setlength{\Hy@SectionHShift}{#1}% + \begingroup + \toks@{\H@old@ssect{#1}{#2}{#3}{#4}}% + \toks\tw@\expandafter{% + \expandafter\Hy@SectionAnchorHref\expandafter{\@currentHref}% + #5% + }% + \edef\x{\endgroup + \the\toks@{\the\toks\tw@}% + }\x +} +\let\H@old@schapter\@schapter +\def\@schapter#1{% + \begingroup + \let\@mkboth\@gobbletwo + \Hy@MakeCurrentHrefAuto{\Hy@chapapp*}% + \Hy@raisedlink{% + \hyper@anchorstart{\@currentHref}\hyper@anchorend + }% + \endgroup + \H@old@schapter{#1}% +} +\ltx@IfUndefined{@chapter}{}{% + \let\Hy@org@chapter\@chapter + \def\@chapter{% + \def\Hy@next{% + \Hy@MakeCurrentHrefAuto{\Hy@chapapp*}% + \Hy@raisedlink{% + \hyper@anchorstart{\@currentHref}\hyper@anchorend + }% + }% + \ifnum\c@secnumdepth>\m@ne + \ltx@IfUndefined{if@mainmatter}% + \iftrue{\csname if@mainmatter\endcsname}% + \let\Hy@next\relax + \fi + \fi + \Hy@next + \Hy@org@chapter + }% +} +\let\H@old@part\@part +\begingroup\expandafter\expandafter\expandafter\endgroup +\expandafter\ifx\csname chapter\endcsname\relax + \let\Hy@secnum@part\z@ +\else + \let\Hy@secnum@part\m@ne +\fi +\def\@part{% + \ifnum\Hy@secnum@part>\c@secnumdepth + \phantomsection + \fi + \H@old@part +} +\let\H@old@spart\@spart +\def\@spart#1{% + \Hy@MakeCurrentHrefAuto{part*}% + \Hy@raisedlink{% + \hyper@anchorstart{\@currentHref}\hyper@anchorend + }% + \H@old@spart{#1}% +} +\let\H@old@sect\@sect +\def\@sect#1#2#3#4#5#6[#7]#8{% + \ifnum #2>\c@secnumdepth + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi + {% + \Hy@MakeCurrentHrefAuto{section*}% + \setlength{\Hy@SectionHShift}{#3}% + \begingroup + \toks@{\H@old@sect{#1}{#2}{#3}{#4}{#5}{#6}[{#7}]}% + \toks\tw@\expandafter{% + \expandafter\Hy@SectionAnchorHref\expandafter{\@currentHref}% + #8% + }% + \edef\x{\endgroup + \the\toks@{\the\toks\tw@}% + }\x + }{% + \H@old@sect{#1}{#2}{#3}{#4}{#5}{#6}[{#7}]{#8}% + }% +} +\expandafter\def\csname Parent-4\endcsname{} +\expandafter\def\csname Parent-3\endcsname{} +\expandafter\def\csname Parent-2\endcsname{} +\expandafter\def\csname Parent-1\endcsname{} +\expandafter\def\csname Parent0\endcsname{} +\expandafter\def\csname Parent1\endcsname{} +\expandafter\def\csname Parent2\endcsname{} +\expandafter\def\csname Parent3\endcsname{} +\expandafter\def\csname Parent4\endcsname{} +%% +%% End of file `hgeneric-testphase.def'. +%</package> +% \end{macrocode} +% \begin{macrocode} +%<*colorscheme> +% collected from https://tex.stackexchange.com/questions/525261/better-default-colors-for-hyperref-links +% cite color ignored, as it doesn't fit ... should be done by cite packages ? +% linkcolor= +%,filecolor= +%,urlcolor= +%,menucolor= +%,runcolor= +%,linkbordercolor= +%,filebordercolor= +%,urlbordercolor= +%,menubordercolor= +%,runbordercolor= + +\prop_const_from_keyval:Nn \c_@@_colorscheme_original_prop + { + linkcolor = [rgb]{1,0,0}, %red + filecolor = [rgb]{0,1,1}, %cyan + urlcolor = [rgb]{1,0,1}, %magenta + menucolor = [rgb]{1, 0, 0}, %red + runcolor = [rgb]{0,1,1}, %cyan + %------------- + linkbordercolor = [rgb]{1, 0 ,0 }, + filebordercolor = [rgb]{0, .5, .5}, + urlbordercolor = [rgb]{0, 1, 1}, + menubordercolor = [rgb]{1, 0, 0}, + runbordercolor = [rgb]{0, .7, .7} + } + +\prop_const_from_keyval:Nn \c_@@_colorscheme_daleif_prop + { + linkcolor = [rgb]{0,0.2,0.6}, + filecolor = [rgb]{0.8,0,0.8}, + urlcolor = [rgb]{0.8,0,0.8}, + menucolor = [rgb]{0,0.2,0.6}, + runcolor = [rgb]{0.8,0,0.8}, + %------------- %-------- + linkbordercolor = [rgb]{0,0.2,0.6}, + filebordercolor = [rgb]{0.8,0,0.8}, + urlbordercolor = [rgb]{0.8,0,0.8}, + menubordercolor = [rgb]{0,0.2,0.6}, + runbordercolor = [rgb]{0.8,0,0.8} + } + +\prop_const_from_keyval:Nn \c_@@_colorscheme_julian_prop + { %two colors: intern/extern + linkcolor = [rgb]{0.79216, 0, 0.12549}, + filecolor = [rgb]{0.01961, 0.44314, 0.6902}, + urlcolor = [rgb]{0.01961, 0.44314, 0.6902}, + menucolor = [rgb]{0.79216, 0, 0.12549 }, + runcolor = [rgb]{0.01961, 0.44314, 0.6902 }, + %------------- %-------- + linkbordercolor = [rgb]{0.79216, 0, 0.12549}, + filebordercolor = [rgb]{0.01961, 0.44314, 0.6902}, + urlbordercolor = [rgb]{0.01961, 0.44314, 0.6902}, + menubordercolor = [rgb]{0.79216, 0, 0.12549 }, + runbordercolor = [rgb]{0.01961, 0.44314, 0.6902 } + } + +\prop_const_from_keyval:Nn \c_@@_colorscheme_tivv_prop + { %all darkgray + linkcolor = [rgb]{0.4 ,0.4 ,0.4 }, + filecolor = [rgb]{0.4 ,0.4 ,0.4 }, + urlcolor = [rgb]{0.4 ,0.4 ,0.4 }, + menucolor = [rgb]{0.4 ,0.4 ,0.4 }, + runcolor = [rgb]{0.4 ,0.4 ,0.4 }, + %------------- %-------- + linkbordercolor = [rgb]{0.4 ,0.4 ,0.4 }, + filebordercolor = [rgb]{0.4 ,0.4 ,0.4 }, + urlbordercolor = [rgb]{0.4 ,0.4 ,0.4 }, + menubordercolor = [rgb]{0.4 ,0.4 ,0.4 }, + runbordercolor = [rgb]{0.4 ,0.4 ,0.4 } + } + +\prop_const_from_keyval:Nn \c_@@_colorscheme_szabolcsA_prop + { %dvipsnam.def + linkcolor = [rgb]{0.06, 0.46, 1}, %NavyBlue + filecolor = [rgb]{1, 0, 0}, %Red + urlcolor = [rgb]{0.06, 0.46, 1}, %NavyBlue + menucolor = [rgb]{1, 0, 0}, %Red + runcolor = [rgb]{1, 0, 0}, %Red + %------------- %------------------ + linkbordercolor = [rgb]{0.06, 0.46, 1}, %NavyBlue + filebordercolor = [rgb]{1, 0, 0}, %Red + urlbordercolor = [rgb]{0.06, 0.46, 1}, %NavyBlue + menubordercolor = [rgb]{1, 0, 0}, %Red + runbordercolor = [rgb]{1, 0, 0} %Red + } + +\prop_const_from_keyval:Nn \c_@@_colorscheme_szabolcsB_prop + { %dvipsnam.def + linkcolor = [rgb]{0.72, 0, 0}, %BrickRed + filecolor = [rgb]{0, 1, 0}, %Green + urlcolor = [rgb]{0.64, 0.08, 0.98}, %Mulberry + menucolor = [rgb]{0.06, 0.46, 1}, %NavyBlue + runcolor = [rgb]{0.64, 0.08, 0.98}, %Mulberry + %------------- %------------------ + linkbordercolor = [rgb]{0.72, 0, 0}, %BrickRed + filebordercolor = [rgb]{0, 1, 0}, %Green + urlbordercolor = [rgb]{0.64, 0.08, 0.98}, %Mulberry + menubordercolor = [rgb]{0.06, 0.46, 1}, %NavyBlue + runbordercolor = [rgb]{0.64, 0.08, 0.98} %Mulberry + } + + +\prop_const_from_keyval:Nn \c_@@_colorscheme_phelype_prop + { + linkcolor = [rgb]{0.50196, 0, 0.02353}, + filecolor = [rgb]{0.07451, 0.09412, 0.46667}, + urlcolor = [rgb]{0.54118, 0, 0.52941}, + menucolor = [rgb]{0.44706, 0.45882, 0}, + runcolor = [rgb]{0.07451, 0.46667, 0.46275}, + %------------- %------------- + linkbordercolor = [rgb]{0.701176, 0.4, 0.414118}, + filebordercolor = [rgb]{0.444706, 0.456472, 0.680002}, + urlbordercolor = [rgb]{0.724708, 0.4, 0.717646}, + menubordercolor = [rgb]{0.668236, 0.675292, 0.4}, + runbordercolor = [rgb]{0.444706, 0.680002, 0.67765} + } + +\prop_const_from_keyval:Nn \c_@@_colorscheme_henryford_prop + { + linkcolor = [rgb]{0,0,0}, + filecolor = [rgb]{0,0,0}, + urlcolor = [rgb]{0,0,0}, + menucolor = [rgb]{0,0,0}, + runcolor = [rgb]{0,0,0}, + %------------- %-------- + linkbordercolor = [rgb]{0,0,0}, + filebordercolor = [rgb]{0,0,0}, + urlbordercolor = [rgb]{0,0,0}, + menubordercolor = [rgb]{0,0,0}, + runbordercolor = [rgb]{0,0,0} + } +%</colorscheme> +% \end{macrocode} +% \end{implementation} +% \newpage +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3backend-testphase.dtx b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3backend-testphase.dtx new file mode 100644 index 00000000000..ffa7fd0b633 --- /dev/null +++ b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3backend-testphase.dtx @@ -0,0 +1,1598 @@ +% \iffalse meta-comment +% +%% File: l3backend-testphase.dtx +% +% Copyright (C) 2019-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 +% +% https://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> +\documentclass[full,kernel]{l3doc} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% \title{^^A +% The \textsf{l3backend-testphase} package\\ Additional backend PDF features^^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{implementation} +% +% \section{\pkg{l3backend-testphase} Implementation} +% \begin{macrocode} +%<drivers>\ProvidesExplFile +%<*dvipdfmx> + {l3backend-testphase-dvipdfmx.def}{2021-02-22}{} + {LaTeX~PDF~management~testphase~bundle~backend~support: dvipdfmx} +%</dvipdfmx> +%<*dvips> + {l3backend-testphase-dvips.def}{2021-02-22}{} + {LaTeX~PDF~management~testphase~bundle~backend~support: dvips} +%</dvips> +%<*dvisvgm> + {l3backend-testphase-dvisvgm.def}{2021-02-22}{} + {LaTeX~PDF~management~testphase~bundle~backend~support: dvisvgm} +%</dvisvgm> +%<*luatex> + {l3backend-testphase-luatex.def}{2021-02-22}{} + {LaTeX~PDF~management~testphase~bundle~backend~support: PDF output (LuaTeX)} +%</luatex> +%<*pdftex> + {l3backend-testphase-pdftex.def}{2021-02-22}{} + {LaTeX~PDF~management~testphase~bundle~backend~support: PDF output (pdfTeX)} +%</pdftex> +%<*xdvipdfmx> + {l3backend-testphase-xetex.def}{2021-02-22}{} + {LaTeX~PDF~management~testphase~bundle~backend~support: XeTeX} +%</xdvipdfmx> +% \end{macrocode} +% \subsection{Crossreferences} +% This uses the temporary l3ref-tmp.sty. It will +% will be replaced by kernel code later. +% It is only need to get a reference for the absolute page counter. +% This uses the counter from the +% new lthooks/ltshipout package. +% \begin{macrocode} +%<@@=pdf> +%<*drivers> +\RequirePackage{l3ref-tmp} +\cs_generate_variant:Nn \ref_label:nn {en} +\cs_generate_variant:Nn \ref_value:nn {en} +\cs_new_protected:Npn \@@_backend_ref_label:nn #1 #2 + { + \@bsphack + \ref_label:nn{#1}{abspage} + \@esphack + } +\cs_new:Npn \@@_backend_ref_value:nn #1 #2 + { + \ref_value:nn{#1}{#2} + } +\cs_generate_variant:Nn \@@_backend_ref_label:nn {en} +\cs_generate_variant:Nn \@@_backend_ref_value:nn {en} +%</drivers> +% \end{macrocode} +% \begin{macrocode} +%<*dvipdfmx|xdvipdfmx> +% avoid that destinations names are optimized. +% is this still needed?? +% see https://tug.org/pipermail/dvipdfmx/2019-May/000002.html + \__kernel_backend_literal:x { dvipdfmx:config~C~ 0x0010 } +%</dvipdfmx|xdvipdfmx> +% \end{macrocode} +% \begin{variable}{\g_@@_tmpa_prop, \l_@@_tmpa_tl, \l_@@_backend_tmpa_box } +% Some scratch variables +% \begin{macrocode} +%<*drivers> +\prop_new:N \g_@@_tmpa_prop +\tl_new:N \l_@@_tmpa_tl +\box_new:N \l_@@_backend_tmpa_box +%</drivers> +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% {\g_@@_backend_resourceid_int, \g_@@_backend_name_int, \g_@@_backend_page_int} +% a counter to create labels for the resources, a counter +% to number properties in bdc marks, a counter for the \cs{pdfpageref} implementation. +% \begin{macrocode} +%<*drivers> +\int_new:N \g_@@_backend_resourceid_int +\int_new:N \g_@@_backend_name_int +\int_new:N \g_@@_backend_page_int +%</drivers> +% \end{macrocode} +% \end{variable} +% \subsection{luacode} +% Load the lua code. +% \begin{macrocode} +%<*luatex> + \directlua { require("l3backend-testphase.lua") } +%</luatex> +% \end{macrocode} +% \subsection{Hooks} +% \subsubsection{Add the \enquote{end run} hooks} +% Here we add the end run hook to suitable +% end hooks. +% \begin{macrocode} +%<*pdftex|luatex> +% put in \@kernel@after@enddocument@afterlastpage +\tl_gput_right:Nn \@kernel@after@enddocument@afterlastpage + { + \g__kernel_pdfmanagement_end_run_code_tl + } +%</pdftex|luatex> +%<*dvipdfmx|xdvipdfmx> +% put in \@kernel@after@shipout@lastpage +\tl_gput_right:Nn \@kernel@after@shipout@lastpage + { + \g__kernel_pdfmanagement_end_run_code_tl + } +%</dvipdfmx|xdvipdfmx> +%<*dvips> +% put in \@kernel@after@shipout@lastpage +\tl_gput_right:Nn\@kernel@after@shipout@lastpage + { + \g__kernel_pdfmanagement_end_run_code_tl + } +%</dvips> +% \end{macrocode} +% \subsubsection{Add the \enquote{shipout} hooks} +% Now we add to the shipout hooks the relevant token lists. +% We also push the page resources in shipout/firstpage (AtBeginDvi) +% as the backend code sets color stack there. The xetex driver needs a rule here. +% If it clashes on the first page, we will need a test ... +% \begin{macrocode} +%<*drivers> +\tl_if_exist:NTF \@kernel@after@shipout@background + { + \g@addto@macro \@kernel@before@shipout@background{\relax} + \g@addto@macro \@kernel@after@shipout@background + { + \g__kernel_pdfmanagement_thispage_shipout_code_tl + } + \tl_gput_left:Nn\@kernel@after@shipout@lastpage + { + \g__kernel_pdfmanagement_lastpage_shipout_code_tl + } + } + { + \hook_gput_code:nnn{shipout/background}{pdf} + { + \g__kernel_pdfmanagement_thispage_shipout_code_tl + } + \hook_gput_code:nnn {shipout/lastpage} {pdf} + { + \g__kernel_pdfmanagement_lastpage_shipout_code_tl + } + } + +%</drivers> +% \end{macrocode} +% \subsection{ The /Pages dictionary (pdfpagesattr) } +% \begin{NOTE}{UF} +% path: Pages +% pdfpagesattr is a single token register which is used at the end of the compilation. +% dvips syntax: \verb+\special{ps: [/ABC /CDE /EFG /FGH /Rotate 90 /PAGES pdfmark}+ +% dvipdfmx syntax: \verb+\special{pdf:put @pages <</ABC /WEZ /EFG /XYZ /Rotate 0>>}+ +% both remove duplicate entries automatically, so there is no need to be careful. +% \end{NOTE} +% \begin{macro}{\@@_backend_Pages_primitive:n} +% This is the primitive command to add something to the /Pages dictionary. +% It works differently for the backends: pdftex and luatex overwrite existing +% content, dvips and dvipdfmx are additive. luatex sets it in lua. +% The higher level code has to take this into account. +% \begin{macrocode} +%<*pdftex> +\cs_new_protected:Npn \@@_backend_Pages_primitive:n #1 + { + \tex_global:D \tex_pdfpagesattr:D { #1 } + } +%</pdftex> +%<*luatex> +%luatex: does it in lua +\sys_if_engine_luatex:T + { + \cs_new_protected:Npn \@@_backend_Pages_primitive:n #1 + { + \tex_directlua:D + { + pdf.setpagesattributes( \@@_backend_luastring:n { #1 } ) + } + } + } +%</luatex> +%<*dvips> +\cs_new_protected:Npx \@@_backend_Pages_primitive:n #1 + { + \tex_special:D{ps:~[#1~/PAGES~pdfmark} %] + } +%</dvips> +%<*dvipdfmx|xdvipdfmx> +\cs_new_protected:Npn \@@_backend_Pages_primitive:n #1 + { + \@@_backend:n{put~@pages~<<#1>>} + } +%</dvipdfmx|xdvipdfmx> +%<*dvisvgm> +\cs_new_protected:Npn \@@_backend_Pages_primitive:n #1 + {} +%</dvisvgm> +% \end{macrocode} +% \end{macro} +% +% \subsection{\enquote{Page} and \enquote{ThisPage} attributes (pdfpageattr)} +% \begin{NOTE}{UF} +% path: Page +% !!!!!!!!!!!!!!!!!!!!!! +% This part of the code depends on zref/xref as it sets labels. +% It also depends on code in l3pdfmanagement as the code uses the Core-dictionaries +% !!!!!!!!!!!!!!!!!!!!!! +% +% The engines differ a lot here: pdflatex and lualatex uses a register while with +% dvips/dvipdfmx a one-shot-special is used. So for pdflatex and lualatex code +% to assemble the content of the register is needed. Specials are used at shipout, +% the registers is set directly. With lualatex one can use +% \cs{latelua} to delay the setting, with pdflatex one has to use a shipout hook. +% To get the code on the correct page one has to use the aux with pdflatex. +% In sum this means that quite a lot backend commands are needed to handle +% this differences. Simply variants of \cs{pdfpageattr} are not enough ...% +% dvips syntax: \special{ps: [{ThisPage}<</Rotate 90>> /PUT pdfmark}% +% There seem to be an in-built management code: multiple uses don't lead to +% multiple entries (/Rotate is special: there is always a /Rotate 0 in the dict, +% but seems not to do harm). +% dvipdfmx syntax: \special{pdf: put @thispage << /Rotate 90 >>}, +% like dvips the backend has an in-built management code. +% Both change only the current page, so to get the pdftex behavior (which sets +% also the following pages) one need to repeat it on every shipout. +% \end{NOTE} +% \begin{macro} +% { +% \@@_backend_Page_primitive:n, +% \@@_backend_Page_gput:nn, +% \@@_backend_Page_gremove:n, +% \@@_backend_ThisPage_gput:nn, +% \@@_backend_ThisPage_gpush:n +% } +% \cs{@@_backend_Page_primitive:n} is the primitive command to add +% something to the /Page dictionary. +% It works differently for the backends: pdftex and luatex overwrite existing +% content, dvips and dvipdfmx are additive. luatex sets it in lua. +% The higher level code has to take this into account. +% \cs{@@_backend_Page_gput:nn} stores default values. +% \cs{@@_backend_Page_gremove:n} allows to remove a value. +% \cs{@@_backend_ThisPage_gput:nn} adds a value to the current page. +% \cs{@@_backend_ThisPage_gpush:n} merges the default and the current page values +% and add them to the dictionary of the current page in +% \cs{g_@@_backend_thispage_shipout_tl}. +% \begin{macrocode} +% backend commands +%<*pdftex> + %the primitive + \cs_new_protected:Npn \@@_backend_Page_primitive:n #1 + { + \tex_global:D \tex_pdfpageattr:D { #1 } + } +% the command to store default values. +% Uses a prop with pdflatex + dvi, +% sets a lua table with lualatex + \cs_new_protected:Npn \@@_backend_Page_gput:nn #1 #2 %key,value + { + \pdfdict_gput:nnn {g_@@_Core/Page}{ #1 }{ #2 } + } +% the command to remove a default value. +% Uses a prop with pdflatex + dvi, +% changes a lua table with lualatex +\cs_new_protected:Npn \@@_backend_Page_gremove:n #1 + { + \pdfdict_gremove:nn {g_@@_Core/Page}{ #1 } + } +% the command used in the document. +% direct call of the primitive special with dvips/dvipdfmx +% \latelua: fill a page related table with lualatex, merge it with the page +% table and push it directly +% write to aux and store in prop with pdflatex +\cs_new_protected:Npn \@@_backend_ThisPage_gput:nn #1 #2 + { + %we need to know the page the resource should be added too. + \int_gincr:N\g_@@_backend_resourceid_int + %\zref@labelbylist {l3pdf\int_use:N\g_@@_backend_resourceid_int} {l3pdf} + %\ref_label:en{l3pdf\int_use:N\g_@@_backend_resourceid_int}{abspage} + \@@_backend_ref_label:en { l3pdf\int_use:N\g_@@_backend_resourceid_int }{abspage} + \tl_set:Nx \l_@@_tmpa_tl + { + %\zref@extractdefault +% {l3pdf\int_use:N\g_@@_backend_resourceid_int} +% {pdf@abspage} +% {0} +% \ref_value:en{l3pdf\int_use:N\g_@@_backend_resourceid_int}{abspage} + \@@_backend_ref_value:en {l3pdf\int_use:N\g_@@_backend_resourceid_int}{abspage} + } + \pdfdict_if_exist:nF { g_@@_Core/backend_Page\l_@@_tmpa_tl} + { + \pdfdict_new:n { g_@@_Core/backend_Page\l_@@_tmpa_tl} + } + %backend_Page has no handler. + \pdfdict_gput:nnn {g_@@_Core/backend_Page\l_@@_tmpa_tl}{ #1 }{ #2 } + } +%the code to push the values, used in shipout +%merges the two props and then fills the register in pdflatex +%merges the two tables and then fills (in lua) in luatex +%issues the values stored in the global prop with dvi +\cs_new_protected:Npn \@@_backend_ThisPage_gpush:n #1 + { + \prop_gset_eq:Nc \g_@@_tmpa_prop { \__kernel_pdfdict_name:n { g_@@_Core/Page } } + \prop_if_exist:cT { \__kernel_pdfdict_name:n { g_@@_Core/backend_Page#1 } } + { + \prop_map_inline:cn { \__kernel_pdfdict_name:n { g_@@_Core/backend_Page#1 } } + { + \prop_gput:Nnn \g_@@_tmpa_prop { ##1 }{ ##2 } + } + } + \exp_args:Nx \@@_backend_Page_primitive:n + { + \prop_map_function:NN \g_@@_tmpa_prop \pdfdict_item:ne + } + } +%</pdftex> +%<*luatex> +% do we need to use some escaping for the values????? +\cs_new:Npn \@@_backend_luastring:n #1 + { + "\tex_luaescapestring:D { \tex_unexpanded:D { #1 } }" + } + %not used, only there for consistency +\cs_new_protected:Npn \@@_backend_Page_primitive:n #1 + { + \tex_latelua:D + { + pdf.setpageattributes(\@@_backend_luastring:n { #1 }) + } + } + % the command to store default values. + % Uses a prop with pdflatex + dvi, + % sets a lua table with lualatex +\cs_new_protected:Npn \@@_backend_Page_gput:nn #1 #2 + { + \tex_directlua:D + { + ltx.@@.backend_Page_gput + ( + \@@_backend_luastring:n { #1 }, + \@@_backend_luastring:n { #2 } + ) + } + } + % the command to remove a default value. + % Uses a prop with pdflatex + dvi, + % changes a lua table with lualatex +\cs_new_protected:Npn \@@_backend_Page_gremove:n #1 + { + \tex_directlua:D + { + ltx.@@.backend_Page_gremove (\@@_backend_luastring:n { #1 }) + } + } + % the command used in the document. + % direct call of the primitive special with dvips/dvipdfmx + % \latelua: fill a page related table with lualatex, merge it with the page + % table and push it directly + % write to aux and store in prop with pdflatex +\cs_new_protected:Npn \@@_backend_ThisPage_gput:nn #1 #2 + { + \tex_latelua:D + { + ltx.@@.backend_ThisPage_gput + ( + tex.count["g_shipout_readonly_int"], + \@@_backend_luastring:n { #1 }, + \@@_backend_luastring:n { #2 } + ) + ltx.@@.backend_ThisPage_gpush (tex.count["g_shipout_readonly_int"]) + } + } + %the code to push the values, used in shipout + %merges the two props and then fills the register in pdflatex + %merges the two tables (the one is probably still empty) and then fills (in lua) in luatex + %issues the values stored in the global prop with dvi +\cs_new_protected:Npn \@@_backend_ThisPage_gpush:n #1 + { + \tex_latelua:D + { + ltx.@@.backend_ThisPage_gpush (tex.count["g_shipout_readonly_int"]) + } + } + +%</luatex> +%<*dvipdfmx|xdvipdfmx> + %the primitive +\cs_new_protected:Npn \@@_backend_Page_primitive:n #1 + { + \tex_special:D{pdf:~put~@thispage~<<#1>>} + } + % the command to store default values. + % Uses a prop with pdflatex + dvi, + % sets a lua table with lualatex +\cs_new_protected:Npn \@@_backend_Page_gput:nn #1 #2 + { + \pdfdict_gput:nnn {g_@@_Core/Page}{ #1 }{ #2 } + } + % the command to remove a default value. + % Uses a prop with pdflatex + dvi, + % changes a lua table with lualatex +\cs_new_protected:Npn \@@_backend_Page_gremove:n #1 + { + \pdfdict_gremove:nn {g_@@_Core/Page}{ #1 } + } + % the command used in the document. + % direct call of the primitive special with dvips/dvipdfmx + % \latelua: fill a page related table with lualatex, merge it with the page + % table and push it directly + % write to aux and store in prop with pdflatex +\cs_new_protected:Npn \@@_backend_ThisPage_gput:nn #1 #2 + { + \@@_backend_Page_primitive:n { /#1~#2 } + } + %the code to push the values, used in shipout + %merges the two props and then fills the register in pdflatex + %merges the two tables (the one is probably still empty) + % and then fills (in lua) in luatex + %issues the values stored in the global prop with dvi +\cs_new_protected:Npn \@@_backend_ThisPage_gpush:n #1 + { + \exp_args:Nx \@@_backend_Page_primitive:n + { \pdfdict_use:n { g_@@_Core/Page} } + } +%</dvipdfmx|xdvipdfmx> +%<*dvips> +\cs_new_protected:Npn \@@_backend_Page_primitive:n #1 + { + \tex_special:D{ps:~[{ThisPage}<<#1>>~/PUT~pdfmark} %] + } + % the command to store default values. + % Uses a prop with pdflatex + dvi, + % sets a lua table with lualatex +\cs_new_protected:Npn \@@_backend_Page_gput:nn #1 #2 + { + \pdfdict_gput:nnn {g_@@_Core/Page}{ #1 }{ #2 } + } + % the command to remove a default value. + % Uses a prop with pdflatex + dvi, + % changes a lua table with lualatex +\cs_new_protected:Npn \@@_backend_Page_gremove:n #1 + { + \pdfdict_gremove:nn {g_@@_Core/Page}{ #1 } + } + % the command used in the document. + % direct call of the primitive special with dvips/dvipdfmx + % \latelua: fill a page related table with lualatex, merge it with the page + % table and push it directly + % write to aux and store in prop with pdflatex +\cs_new_protected:Npn \@@_backend_ThisPage_gput:nn #1 #2 + { + \@@_backend_Page_primitive:n { /#1~#2 } + } + %the code to push the values, used in shipout + %merges the two props and then fills the register in pdflatex + %merges the two tables (the one is probably still empty) + %and then fills (in lua) in luatex + %issues the values stored in the global prop with dvi +\cs_new_protected:Npn \@@_backend_ThisPage_gpush:n #1 + { + \exp_args:Nx \@@_backend_Page_primitive:n + { \pdfdict_use:n { g_@@_Core/Page} } + } +%</dvips> +%<*dvisvgm> +% mostly only dummies ... +\cs_new_protected:Npn \@@_backend_Page_primitive:n #1 + {} + % Uses a prop with pdflatex + dvi, +\cs_new_protected:Npn \@@_backend_Page_gput:nn #1 #2 + { + \pdfdict_gput:nnn {g_@@_Core/Page}{ #1 }{ #2 } + } + % the command to remove a default value. + % Uses a prop with pdflatex + dvi, +\cs_new_protected:Npn \@@_backend_Page_gremove:n #1 + { + \pdfdict_gremove:nn {g_@@_Core/Page}{ #1 } + } + % the command used in the document. +\cs_new_protected:Npn \@@_backend_ThisPage_gput:nn #1 #2 + {} + %the code to push the values, used in shipout +\cs_new_protected:Npn \@@_backend_ThisPage_gpush:n #1 + {} +%</dvisvgm> +% \end{macrocode} +% \end{macro} +% +% \subsection{\enquote{Page/Resources}: ExtGState, ColorSpace, Shading, Pattern} +% Path: Page/Resources/ExtGState etc. The actual output of the resources is handled +% together with the bdc/Properties. Here is only special code. +% \begin{macro}{\@@_backend_PageResources_gput:nnn} +% stores values for the page resources. +% \begin{arguments} +% \item name of the resource (ExtGState, ColorSpace, Shading, Pattern) +% \item a pdf name without slash +% \item value +% \end{arguments} +% \begin{macro}{\@@_backend_PageResources_obj_gpush:} +% This pushes out the objects. It is a no-op with xdvipdfmx and dvips. +% \begin{macrocode} +% backend commands the command to fill the register +% and to push the values. +% +% The names are quite often needed +% a similar list is now in l3pdfmanagement. Perhaps it should be merged. +%<*drivers> +\clist_const:Nn \c_@@_backend_PageResources_clist + { + ExtGState, + ColorSpace, + Pattern, + Shading, + } +%</drivers> +% pdftex and luatex +%<*pdftex|luatex> + %create the backend objects: +\clist_map_inline:Nn \c_@@_backend_PageResources_clist + { + \@@_backend_object_new:nn {Page/Resources/#1} {dict} + \cs_if_exist:NT \tex_directlua:D + { + \tex_directlua:D + { + ltx.@@.object["Page/Resources/#1"] + = + "\@@_backend_object_ref:n{Page/Resources/#1}" + } + } + } +%</pdftex|luatex> +%<*luatex> +%values are only stored in a prop and will be output at end document. +\cs_new_protected:Npn \@@_backend_PageResources_gput:nnn #1 #2 #3 + { + \pdfdict_gput:nnn {g_@@_Core/Page/Resources/#1} { #2 }{ #3 } + % luatex must also trigger the lua side + \tex_latelua:D{ltx.@@.Page.Resources.#1=true} + \tex_latelua:D + { + ltx.pdf.Page_Resources_gpush(tex.count["g_shipout_readonly_int"]) + } + } +%</luatex> +%<*pdftex> + \cs_new_protected:Npn \@@_backend_PageResources_gput:nnn #1 #2 #3 + { + \pdfdict_gput:nnn {g_@@_Core/Page/Resources/#1} { #2 }{ #3 } + } +%</pdftex> +%<*pdftex|luatex> +%code for end of document code +\cs_new_protected:Npn \@@_backend_PageResources_obj_gpush: + { + \clist_map_inline:Nn \c_@@_backend_PageResources_clist + { + \prop_if_empty:cF + { \__kernel_pdfdict_name:n { g_@@_Core/Page/Resources/##1} } + { + \@@_backend_object_write:nx + { Page/Resources/##1 } + { \pdfdict_use:n { g_@@_Core/Page/Resources/##1} } + } + } + } +%</pdftex|luatex> +% xdvipdfmx +% \special{pdf:pageresources<<#1>>} doesn't work correctly with object names ... +% https://tug.org/pipermail/dvipdfmx/2019-August/000021.html, +% so we use \special{pdf:put @resources} +% this must be issued on every page! +%<*dvipdfmx|xdvipdfmx> +%objects should not only be created but also "initialized" +% initialization should be done before anyone tries to write +% so we add rules for the backend. +%<xdvipdfmx>\hook_gset_rule:nnnn{shipout/firstpage}{l3backend-xetex}{after}{pdf} +%<dvipdfmx>\hook_gset_rule:nnnn{shipout/firstpage}{l3backend-dvipdfmx}{after}{pdf} +% +\clist_map_inline:Nn \c_@@_backend_PageResources_clist + { + \@@_backend_object_new:nn { Page/Resources/#1 } { dict } + \hook_gput_code:nnn{shipout/firstpage}{pdf}{\@@_backend_object_write:nn { Page/Resources/#1 } {}} + } +\cs_new_protected:Npn \@@_backend_PageResources:n #1 + { + \@@_backend:n {put~@resources~<<#1>>} + } +\cs_new_protected:Npn \@@_backend_PageResources_gput:nnn #1 #2 #3 + { + % this is not used for output, but there is a test if the resource is empty + \exp_args:Nnx + \prop_gput:cnn { \__kernel_pdfdict_name:n { g_@@_Core/Page/Resources/#1} } + { \str_convert_pdfname:n {#2} }{ #3 } + %objects are not filled with \pdf_object_write as this is not additive! + \@@_backend:x + { + put~\@@_backend_object_ref:n {Page/Resources/#1}<</#2~#3>> + } + } + +\cs_new_protected:Npn \@@_backend_PageResources_obj_gpush: {} +%</dvipdfmx|xdvipdfmx> +%<*dvips> +% dvips unneeded, or no-op +\cs_new_protected:Npn \@@_backend_PageResources:n #1 {} +\cs_new_protected:Npn \@@_backend_PageResources_gput:nnn #1 #2 #3 + { %only for the show command TEST!! + \pdfdict_gput:nnn {g_@@_Core/Page/Resources/#1} { #2 }{ #3 } + } +\cs_new_protected:Npn \@@_backend_PageResources_obj_gpush: {} +%</dvips> +%<*dvisvgm> +% dvips unneeded, or no-op +\cs_new_protected:Npn \@@_backend_PageResources:n #1 {} +\cs_new_protected:Npn \@@_backend_PageResources_gput:nnn #1 #2 #3 + { %only for the show command TEST!! + \pdfdict_gput:nnn {g_@@_Core/Page/Resources/#1} { #2 }{ #3 } + } +\cs_new_protected:Npn \@@_backend_PageResources_obj_gpush: {} +%</dvisvgm> +% \end{macrocode} +% \end{macro} +% \end{macro} +% \subsubsection{Page resources /Properties + BDC operators} +% \begin{macro} +% { +% \@@_backend_bdc:nn, +% \@@_backend_bdcobject:nn, +% \@@_backend_bdcobject:n, +% \@@_backend_bmc:n, +% \@@_backend_emc:, +% \@@_backend_PageResources_gpush:n +% } +% \cs{@@_backend_bdc:nn}, \cs{@@_backend_bdcobject:nn}, \cs{@@_backend_bdcobject:n}, +% \cs{@@_backend_bmc:n} and \cs{@@_backend_emc:} +% are the backend command that +% create the bdc/emc marker and store the properties. +% \cs{@@_backend_PageResources_gpush:n} outputs the /Properties and/or the other +% resources for the current page. +% \begin{macrocode} +% pdftex and luatex (and perhaps dvips ...) need to know if there are in a +% xform stream ... +%<*drivers> +\bool_new:N \l_@@_backend_xform_bool +%</drivers> +%<*dvips> +% dvips is easy: create an object, and reference it in the bdc +% ghostscript will then automatically replace it by a name +% and add the name to the /Properties dict +% special variant von accsupp +% https://chat.stackexchange.com/transcript/message/50831812#50831812 +% +\cs_set_protected:Npn \@@_backend_bdc:nn #1 #2 % #1 eg. Span, #2: dict_content + { + \__pdf_backend_pdfmark:x{/#1~<<#2>>~/BDC} + } +\cs_set_protected:Npn \@@_backend_bdcobject:nn #1 #2 % #1 eg. Span, #2: object name + { + \__pdf_backend_pdfmark:x{/#1~\@@_backend_object_ref:n{#2}~/BDC} + } +\cs_set_protected:Npn \@@_backend_bdcobject:n #1 % #1 eg. Span, + { + \__pdf_backend_pdfmark:x{/#1~\@@_backend_object_last:~/BDC} + } +\cs_set_protected:Npn \@@_backend_emc: + { + \__pdf_backend_pdfmark:n{/EMC} % + } +\cs_set_protected:Npn \@@_backend_bmc:n #1 + { + \__pdf_backend_pdfmark:n{/#1~/BMC} % + } +\cs_new_protected:Npn \@@_backend_PageResources_gpush:n #1 {} + +%</dvips> +%<*dvisvgm> +% dvisvgm should do nothing +% +\cs_set_protected:Npn \@@_backend_bdc:nn #1 #2 % #1 eg. Span, #2: dict_content + {} +\cs_set_protected:Npn \@@_backend_bdcobject:nn #1 #2 % #1 eg. Span, #2: object name + {} +\cs_set_protected:Npn \@@_backend_bdcobject:n #1 % #1 eg. Span, + {} +\cs_set_protected:Npn \@@_backend_emc: + {} +\cs_set_protected:Npn \@@_backend_bmc:n #1 + {} +\cs_new_protected:Npn \@@_backend_PageResources_gpush:n #1 {} + +%</dvisvgm> + +% xetex has to create the entries in the /Properties manually +% (like the other backends) +% use pdfbase special +% https://chat.stackexchange.com/transcript/message/50832016#50832016 +% the property is added to xform resources automatically, +% no need to worry about it. +%<*dvipdfmx|xdvipdfmx> + \cs_set_protected:Npn \@@_backend_bdcobject:nn #1 #2 % #1 eg. Span, #2: object name + { + \int_gincr:N \g_@@_backend_name_int + \__kernel_backend_literal:x + { + pdf:code~/#1/l3pdf\int_use:N\g_@@_backend_name_int\c_space_tl BDC + } + \__kernel_backend_literal:x + { + pdf:put~@resources~ + << + /Properties~ + << + /l3pdf\int_use:N\g_@@_backend_name_int\c_space_tl + \@@_backend_object_ref:n { #2 } + >> + >> + } + } + \cs_set_protected:Npn \@@_backend_bdcobject:n #1 % #1 eg. Span + { + \int_gincr:N \g_@@_backend_name_int + \__kernel_backend_literal:x + { + pdf:code~/#1/l3pdf\int_use:N\g_@@_backend_name_int\c_space_tl BDC + } + \__kernel_backend_literal:x + { + pdf:put~@resources~ + << + /Properties~ + << + /l3pdf\int_use:N\g_@@_backend_name_int\c_space_tl + \@@_backend_object_last: + >> + >> + } + } +\cs_set_protected:Npn \@@_backend_bmc:n #1 + { + \__kernel_backend_literal:n {pdf:code~/#1~BMC} %pdfbase + } + +%this require management +\cs_set_protected:Npn \@@_backend_bdc_contobj:nn #1 #2 + { + \pdf_object_unnamed_write:nn { dict }{ #2 } + \@@_backend_bdcobject:n { #1 } + } + +\cs_set_protected:Npn \@@_backend_bdc_contstream:nn #1 #2 + { + \__kernel_backend_literal:n {pdf:code~ /#1~<<#2>>~BDC } + } + +\cs_set_protected:Npn \@@_backend_bdc:nn #1 #2 + { + \bool_if:NTF \g__pdfmanagement_active_bool + {\cs_gset_eq:NN \@@_backend_bdc:nn \@@_backend_bdc_contobj:nn} + {\cs_gset_eq:NN \@@_backend_bdc:nn \@@_backend_bdc_contstream:nn} + \@@_backend_bdc:nn {#1}{#2} + } +\cs_set_protected:Npn \@@_backend_emc: + { + \__kernel_backend_literal:n {pdf:code~EMC} %pdfbase + } + % properties are handled automatically, but the other resources should be added + % at shipout +\cs_new_protected:Npn \@@_backend_PageResources_gpush:n #1 + { + \clist_map_inline:Nn \c_@@_backend_PageResources_clist + { + \prop_if_empty:cF { \__kernel_pdfdict_name:n { g_@@_Core/Page/Resources/##1} } + { + \__kernel_backend_literal:x + { + pdf:put~@resources~ + <</##1~\@@_backend_object_ref:n {Page/Resources/##1}>> + } + } + } + } +%</dvipdfmx|xdvipdfmx> +% luatex + pdftex +%<*luatex> +\cs_set_protected:Npn \@@_backend_bdcobject:nn #1 #2 % #1 eg. Span, #2: object name + { + \int_gincr:N \g_@@_backend_name_int + \exp_args:Nx\__kernel_backend_literal_page:n + { /#1 ~ /l3pdf\int_use:N\g_@@_backend_name_int\c_space_tl BDC } + \bool_if:NTF \l_@@_backend_xform_bool + { + \exp_args:Nnx\pdfdict_gput:nnn + { g_@@_Core/Xform/Resources/Properties } + { l3pdf\int_use:N\g_@@_backend_name_int } + { \@@_backend_object_ref:n { #2 } } + } + { + \exp_args:Nx \tex_latelua:D + { + ltx.pdf.Page_Resources_Properties_gput + ( + tex.count["g_shipout_readonly_int"], + "l3pdf\int_use:N\g_@@_backend_name_int", + "\@@_backend_object_ref:n { #2 }" + ) + } + } + } +\cs_set_protected:Npn \@@_backend_bdcobject:n #1% #1 eg. Span + { + \int_gincr:N \g_@@_backend_name_int + \exp_args:Nx\__kernel_backend_literal_page:n + { /#1 ~ /l3pdf\int_use:N\g_@@_backend_name_int\c_space_tl BDC } + \bool_if:NTF \l_@@_backend_xform_bool + { + \exp_args:Nnx\pdfdict_gput:nnn %no handler needed + { g_@@_Core/Xform/Resources/Properties } + { l3pdf\int_use:N\g_@@_backend_name_int } + { \@@_backend_object_last: } + } + { + \exp_args:Nx \tex_latelua:D + { + ltx.pdf.Page_Resources_Properties_gput + ( + tex.count["g_shipout_readonly_int"], + "l3pdf\int_use:N\g_@@_backend_name_int", + "\@@_backend_object_last:" + ) + } + } + } +\cs_set_protected:Npn \@@_backend_bmc:n #1 + { + \__kernel_backend_literal_page:n { /#1~BMC } + } +\cs_set_protected:Npn \@@_backend_bdc_contobj:nn #1 #2 + { + \pdf_object_unnamed_write:nn { dict } { #2 } + \@@_backend_bdcobject:n { #1 } + } +\cs_set_protected:Npn \@@_backend_bdc_contstream:nn #1 #2 + { + \__kernel_backend_literal_page:n { /#1~<<#2>>~BDC } + } +\cs_set_protected:Npn \@@_backend_bdc:nn #1 #2 + { + \bool_if:NTF \g__pdfmanagement_active_bool + {\cs_gset_eq:NN \@@_backend_bdc:nn \@@_backend_bdc_contobj:nn} + {\cs_gset_eq:NN \@@_backend_bdc:nn \@@_backend_bdc_contstream:nn} + \@@_backend_bdc:nn {#1}{#2} + } +\cs_set_protected:Npn \@@_backend_emc: + { + \__kernel_backend_literal_page:n { EMC } + } + +\cs_new_protected:Npn \@@_backend_PageResources_gpush:n #1 {} +%</luatex> +%<*pdftex> +% pdflatex is the most complicated as it has to go through the aux ... +% the push command is extended to take other resources too +\cs_set_protected:Npn \@@_backend_bdcobject:nn #1 #2 % #1 eg. Span, #2: object name + { + \int_gincr:N \g_@@_backend_name_int + \exp_args:Nx\__kernel_backend_literal_page:n + { /#1 ~ /l3pdf\int_use:N\g_@@_backend_name_int\c_space_tl BDC } + % code to set the property .... + \int_gincr:N\g_@@_backend_resourceid_int + \bool_if:NTF \l_@@_backend_xform_bool + { + \exp_args:Nnxx\pdfdict_gput:nnn %no handler needed + { g_@@_Core/Xform/Resources/Properties } + { l3pdf\int_use:N\g_@@_backend_resourceid_int } + { \@@_backend_object_ref:n { #2 } } + } + { + %\zref@labelbylist +% { l3pdf\int_use:N\g_@@_backend_resourceid_int } +% { l3pdf } +% \ref_label:en{l3pdf\int_use:N\g_@@_backend_resourceid_int}{abspage} + \@@_backend_ref_label:en{l3pdf\int_use:N\g_@@_backend_resourceid_int}{abspage} + \tl_set:Nx \l_@@_tmpa_tl + { + %\zref@extractdefault +% { l3pdf\int_use:N\g_@@_backend_resourceid_int } +% {pdf@abspage} +% {0} + %\ref_value:en{l3pdf\int_use:N\g_@@_backend_resourceid_int}{abspage} + \@@_backend_ref_value:en{l3pdf\int_use:N\g_@@_backend_resourceid_int}{abspage} + } + \pdfdict_if_exist:nF { g_@@_Core/backend_Page\l_@@_tmpa_tl/Resources/Properties } + { + \pdfdict_new:n { g_@@_Core/backend_Page\l_@@_tmpa_tl/Resources/Properties } + } + \exp_args:Nnxx\pdfdict_gput:nnn + { g_@@_Core/backend_Page\l_@@_tmpa_tl/Resources/Properties } + { l3pdf\int_use:N\g_@@_backend_resourceid_int } + { \@@_backend_object_ref:n{#2} } + } + } +\cs_set_protected:Npn \@@_backend_bdcobject:n #1% #1 eg. Span + { + \int_gincr:N \g_@@_backend_name_int + \exp_args:Nx\__kernel_backend_literal_page:n + { /#1 ~ /l3pdf\int_use:N\g_@@_backend_name_int\c_space_tl BDC } + % code to set the property .... + \int_gincr:N\g_@@_backend_resourceid_int + \bool_if:NTF \l_@@_backend_xform_bool + { + \exp_args:Nnxx\pdfdict_gput:nnn + { g_@@_Core/Xform/Resources/Properties } + { l3pdf\int_use:N\g_@@_backend_resourceid_int } + { \@@_backend_object_last: } + } + { + %\zref@labelbylist +% { l3pdf\int_use:N\g_@@_backend_resourceid_int } +% { l3pdf } + %\ref_label:en{l3pdf\int_use:N\g_@@_backend_resourceid_int}{abspage} + \@@_backend_ref_label:en{l3pdf\int_use:N\g_@@_backend_resourceid_int}{abspage} + \tl_set:Nx \l_@@_tmpa_tl + { + %\zref@extractdefault +% { l3pdf\int_use:N\g_@@_backend_resourceid_int } +% {pdf@abspage} +% {0} + % \ref_value:en{l3pdf\int_use:N\g_@@_backend_resourceid_int}{abspage} + \@@_backend_ref_value:en{l3pdf\int_use:N\g_@@_backend_resourceid_int}{abspage} + } + \pdfdict_if_exist:nF { g_@@_Core/backend_Page\l_@@_tmpa_tl/Resources/Properties } + { + \pdfdict_new:n { g_@@_Core/backend_Page\l_@@_tmpa_tl/Resources/Properties } + } + \exp_args:Nnxx\pdfdict_gput:nnn + { g_@@_Core/backend_Page\l_@@_tmpa_tl/Resources/Properties } + { l3pdf\int_use:N\g_@@_backend_resourceid_int } + { \@@_backend_object_last: } + %\pdfdict_show:n { g_backend_Page\l_@@_tmpa_tl/Resources/Properties } + } + } +\cs_set_protected:Npn \@@_backend_bmc:n #1 + { + \__kernel_backend_literal_page:n { /#1~BMC } + } +\cs_set_protected:Npn \@@_backend_bdc_contobj:nn #1 #2 + { + \pdf_object_unnamed_write:nn { dict } { #2 } + \@@_backend_bdcobject:n { #1 } + } +\cs_set_protected:Npn \@@_backend_bdc_contstream:nn #1 #2 + { + \__kernel_backend_literal_page:n { /#1~<<#2>>~BDC } + } +\cs_set_protected:Npn \@@_backend_bdc:nn #1 #2 + { + \bool_if:NTF \g__pdfmanagement_active_bool + {\cs_gset_eq:NN \@@_backend_bdc:nn \@@_backend_bdc_contobj:nn} + {\cs_gset_eq:NN \@@_backend_bdc:nn \@@_backend_bdc_contstream:nn} + \@@_backend_bdc:nn {#1}{#2} + } +\cs_set_protected:Npn \@@_backend_emc: + { + \__kernel_backend_literal_page:n { EMC } + } + +\cs_new:Npn \@@_backend_PageResources_gpush_aux:n #1 %#1 ExtGState etc + { + \prop_if_empty:cF + { \__kernel_pdfdict_name:n { g_@@_Core/Page/Resources/#1} } + { + \pdfdict_item:ne { #1 }{ \pdf_object_ref:n {Page/Resources/#1}} + } + } + +\cs_new_protected:Npn \@@_backend_PageResources_gpush:n #1 + { + \exp_args:NNx \tex_global:D \tex_pdfpageresources:D + { + \prop_if_exist:cT + { \__kernel_pdfdict_name:n { g_@@_Core/backend_Page#1/Resources/Properties } } + { + /Properties~ + << + \prop_map_function:cN + { \__kernel_pdfdict_name:n { g_@@_Core/backend_Page#1/Resources/Properties } } + \pdfdict_item:ne + >> + } + %% add ExtGState etc + \clist_map_function:NN + \c_@@_backend_PageResources_clist + \@@_backend_PageResources_gpush_aux:n + } + } + +%</pdftex> +% \end{macrocode} +% \end{macro} +% \subsection{\enquote{Catalog} \& subdirectories (pdfcatalog) } +% The backend command is already in the driver: +% \cs{@@_backend_catalog_gput:nn} +% \subsubsection { Special case: the /Names/EmbeddedFiles dictionary } +% Entries to /Names are handled differently, in part (/Desc) it is automatic, for +% other special commands like \cs{pdfnames} must be used. For EmbeddedFiles +% we need some code to push the tree if files have been added. dvips wants code +% for every file and then creates the Name tree automatically. +% \begin{macrocode} +% pdflatex +%<*pdftex> +\cs_new_protected:Npn \@@_backend_NamesEmbeddedFiles_gpush:n #1 %array content + { + \pdf_object_unnamed_write:nn {dict} {/Names [#1] } + \tex_pdfnames:D {/EmbeddedFiles~\pdf_object_ref_last:} + } +%</pdftex> +%<*luatex> +\cs_new_protected:Npn \@@_backend_NamesEmbeddedFiles_gpush:n #1 %array content + { + \pdf_object_unnamed_write:nn {dict} {/Names [#1] } + \tex_pdfextension:D~names~{/EmbeddedFiles~\pdf_object_ref_last: } + } +%</luatex> +%<*dvipdfmx|xdvipdfmx> +\cs_new_protected:Npn \@@_backend_NamesEmbeddedFiles_gpush:n #1 %array content + { + \pdf_object_unnamed_write:nn {dict} { /Names [#1] } + %n or x? + \__pdf_backend:x {put~@names~<</EmbeddedFiles~\pdf_object_ref_last: >>} + } +%</dvipdfmx|xdvipdfmx> + +%dvips: noop +%<*dvips> +\cs_new_protected:Npn \@@_backend_NamesEmbeddedFiles_gpush:n #1 {} +%</dvips> +%dvisvgm: noop +%<*dvisvgm> +\cs_new_protected:Npn \@@_backend_NamesEmbeddedFiles_gpush:n #1 {} +%</dvisvgm> + +% \end{macrocode} +% Names in the EmbeddedFiles name tree must sorted alphabetically, +% so we need commands to create this names. And we need a sequence to store +% the names and the objects. We use the prefix l3ef, and we assume that at +% most 9999 files will be used. +% \begin{variable}{\g_@@_backend_EmbeddedFiles_int} +% \end{variable} +% \begin{macro}{\@@_backend_EmbeddedFiles_name:} +% \begin{macrocode} +%<*drivers> +\int_new:N \g_@@_backend_EmbeddedFiles_int +\cs_new:Npn \@@_backend_EmbeddedFiles_name: + { + ( + l3ef + \int_compare:nNnT {\g_@@_backend_EmbeddedFiles_int} < {10} + {0} + \int_compare:nNnT {\g_@@_backend_EmbeddedFiles_int} < {100} + {0} + \int_compare:nNnT {\g_@@_backend_EmbeddedFiles_int} < {1000} + {0} + \int_use:N \g_@@_backend_EmbeddedFiles_int + ) + } +%</drivers> +% \end{macrocode} +% \end{macro} +% \begin{variable}{\g_@@_backend_EmbeddedFiles_seq,\g_@@_backend_EmbeddedFiles_named_prop} +% The sequence will hold the content of the array that is pushed out at then +% end (not with dvips), the prop holds the obj names-names relation. +% \end{variable} +% \begin{macrocode} +%<*drivers> +\seq_new:N \g_@@_backend_EmbeddedFiles_seq +\prop_new:N \g_@@_backend_EmbeddedFiles_named_prop +%</drivers> +% \end{macrocode} +% \begin{macro}{\@@_backend_NamesEmbeddedFiles_add:n} +% This command saves an object reference of a filespec dictionary in the +% EmbeddedFiles name tree. We define a prop to store the relation between +% object name and name in the name tree. +% \begin{macrocode} +%<*pdftex|luatex|dvipdfmx|xdvipdfmx> +\cs_new_protected:Npn \@@_backend_NamesEmbeddedFiles_add:n #1 + %#1 object ref + { + \int_gincr:N \g_@@_backend_EmbeddedFiles_int + \prop_gput:Nnx \g_@@_backend_EmbeddedFiles_named_prop + { #1 } + { \@@_backend_EmbeddedFiles_name: } + \seq_gput_right:Nx \g_@@_backend_EmbeddedFiles_seq + { \@@_backend_EmbeddedFiles_name: \c_space_tl #1 } + } + +%</pdftex|luatex|dvipdfmx|xdvipdfmx> +%<*dvips> +\cs_new_protected:Npn \@@_backend_NamesEmbeddedFiles_add:n #1 + { + \int_gincr:N \g_@@_backend_EmbeddedFiles_int + \prop_gput:Nnx \g_@@_backend_EmbeddedFiles_named_prop + { #1 } + { \@@_backend_EmbeddedFiles_name: } + \@@_backend_pdfmark:x + { + /Name~\@@_backend_EmbeddedFiles_name:~ + /FS~#1~ + /EMBED + } + } +%</dvips> +%<*dvisvgm> +%no op. Or is there any sensible use for it? +\cs_new_protected:Npn \@@_backend_NamesEmbeddedFiles_add:n #1 + {} +%</dvisvgm> +% \end{macrocode} +% \end{macro} +% \subsubsection{Form XObject / backend } +% \begin{macro}{ \@@_backend_xform_new:nnnn } +% \begin{arguments} +% \item name +% \item attributes +% \item resources %needed?? or are all resources autogenerated? +% \item content, this doesn't need to be a box! +% \end{arguments} +% \begin{macro}{ \@@_backend_xform_use:n, \@@_backend_xform_ref:n } +% \begin{macrocode} +%<*pdftex> +\cs_new_protected:Npn \@@_backend_xform_new:nnnn #1 #2 #3 #4 +% #1 name +% #2 attributes +% #3 resources +% #4 content, not necessarily a box! + { + \hbox_set:Nn \l_@@_backend_tmpa_box + { + \bool_set_true:N \l_@@_backend_xform_bool + \prop_gclear:c {\__kernel_pdfdict_name:n { g_@@_Core/Xform/Resources/Properties }} + #4 + } + %store the dimensions + \tl_const:cx + { c_@@_backend_xform_wd_ \tl_to_str:n {#1} _tl } + { \tex_the:D \box_wd:N \l_@@_backend_tmpa_box } + \tl_const:cx + { c_@@_backend_xform_ht_ \tl_to_str:n {#1} _tl } + { \tex_the:D \box_ht:N \l_@@_backend_tmpa_box } + \tl_const:cx + { c_@@_backend_xform_dp_ \tl_to_str:n {#1} _tl } + { \tex_the:D \box_dp:N \l_@@_backend_tmpa_box } + %% do we need to test if #2 and #3 are empty?? + \tex_immediate:D \tex_pdfxform:D + ~ attr ~ { #2 } + %% which other resources should be default? Is an argument actually needed? + ~ resources ~ + { + #3 + \int_compare:nNnT + { \prop_count:c { \__kernel_pdfdict_name:n { g_@@_Core/Xform/Resources/Properties } } } + > + { 0 } + { + /Properties~ + << + \pdfdict_use:n { g_@@_Core/Xform/Resources/Properties } + >> + } + + \prop_if_empty:cF + { \__kernel_pdfdict_name:n { g_@@_Core/Page/Resources/ExtGState } } + { + /ExtGState~ \pdf_object_ref:n { Page/Resources/ExtGState } + } + \prop_if_empty:cF + { \__kernel_pdfdict_name:n { g_@@_Core/Page/Resources/Pattern } } + { + /Pattern~ \pdf_object_ref:n { Page/Resources/Pattern } + } + \prop_if_empty:cF + { \__kernel_pdfdict_name:n { g_@@_Core/Page/Resources/Shading } } + { + /Shading~ \pdf_object_ref:n { Page/Resources/Shading } + } + \prop_if_empty:cF + { \__kernel_pdfdict_name:n { g_@@_Core/Page/Resources/ColorSpace } } + { + /ColorSpace~ \pdf_object_ref:n { Page/Resources/ColorSpace } + } + } + \l_@@_backend_tmpa_box + \int_const:cn + { c_@@_backend_xform_ \tl_to_str:n {#1} _int } + { \tex_pdflastxform:D } + } + +\cs_new_protected:Npn \@@_backend_xform_use:n #1 + { + \tex_pdfrefxform:D + \int_use:c { c_@@_backend_xform_ \tl_to_str:n {#1} _int } + \scan_stop: + } + +\cs_new:Npn \@@_backend_xform_ref:n #1 + { + \int_use:c { c_@@_backend_xform_ \tl_to_str:n {#1} _int } ~ 0 ~ R + } +%</pdftex> +%<*luatex> +%luatex +%nearly identical but not completely ... +\cs_new_protected:Npn \@@_backend_xform_new:nnnn #1 #2 #3 #4 +% #1 name +% #2 attributes +% #3 resources +% #4 content, not necessarily a box! + { + \hbox_set:Nn \l_@@_backend_tmpa_box + { + \bool_set_true:N \l_@@_backend_xform_bool + \prop_gclear:c { \__kernel_pdfdict_name:n { g_@@_Core/Xform/Resources/Properties } } + #4 + } + \tl_const:cx + { c_@@_backend_xform_wd_ \tl_to_str:n {#1} _tl } + { \tex_the:D \box_wd:N \l_@@_backend_tmpa_box } + \tl_const:cx + { c_@@_backend_xform_ht_ \tl_to_str:n {#1} _tl } + { \tex_the:D \box_ht:N \l_@@_backend_tmpa_box } + \tl_const:cx + { c_@@_backend_xform_dp_ \tl_to_str:n {#1} _tl } + { \tex_the:D \box_dp:N \l_@@_backend_tmpa_box } + %% do we need to test if #2 and #3 are empty?? + \tex_immediate:D \tex_pdfxform:D + ~ attr ~ { #2 } + %% which resources should be default? Is an argument actually needed? + ~ resources ~ + { + #3 + \int_compare:nNnT + {\prop_count:c { \__kernel_pdfdict_name:n { g_@@_Core/Xform/Resources/Properties } }} + > + { 0 } + { + /Properties~ + << + \pdfdict_use:n { g_@@_Core/Xform/Resources/Properties } + >> + } + \prop_if_empty:cF + { \__kernel_pdfdict_name:n { g_@@_Core/Page/Resources/ExtGState } } + { + /ExtGState~ \pdf_object_ref:n { Page/Resources/ExtGState } + } + \prop_if_empty:cF + { \__kernel_pdfdict_name:n { g_@@_Core/Page/Resources/Pattern } } + { + /Pattern~ \pdf_object_ref:n { Page/Resources/Pattern } + } + \prop_if_empty:cF + { \__kernel_pdfdict_name:n { g_@@_Core/Page/Resources/Shading } } + { + /Shading~ \pdf_object_ref:n { Page/Resources/Shading } + } + \prop_if_empty:cF + { \__kernel_pdfdict_name:n { g_@@_Core/Page/Resources/ColorSpace } } + { + /ColorSpace~ \pdf_object_ref:n { Page/Resources/ColorSpace } + } + } + \l_@@_backend_tmpa_box + \int_const:cn + { c_@@_backend_xform_ \tl_to_str:n {#1} _int } + { \tex_pdflastxform:D } + } + +\cs_new_protected:Npn \@@_backend_xform_use:n #1 %protected as with xelatex + { + \tex_pdfrefxform:D \int_use:c + { + c_@@_backend_xform_ \tl_to_str:n {#1} _int + } + \scan_stop: + } + +\cs_new:Npn \@@_backend_xform_ref:n #1 + { \int_use:c { c_@@_backend_xform_ \tl_to_str:n {#1} _int } ~ 0 ~ R } + +%</luatex> +%<*dvipdfmx|xdvipdfmx> +% xetex + % it needs a bit testing if it really works to set the box to 0 before the special ... + % does it disturb viewing the xobject? + % what happens with the resources (bdc)? (should work as they are specials too) + % xetex requires that the special is in horizontal mode. This means it affects + % typesetting. But we can no delay the whole form code to shipout + % as the object reference and the size is often wanted on the current page. + % so we need to allocate a box - but probably they won't be thousands xform + % in a document so it shouldn't matter. + \cs_new_protected:Npn \@@_backend_xform_new:nnnn #1 #2 #3 #4 + % #1 name + % #2 attributes + % #3 resources + % #4 content, not necessarily a box! + { + \int_gincr:N \g_@@_backend_object_int + \int_const:cn + { c_@@_backend_xform_ \tl_to_str:n {#1} _int } + { \g_@@_backend_object_int } + \box_new:c { g_@@_backend_xform_#1_box } + \hbox_gset:cn { g_@@_backend_xform_#1_box } + { + \bool_set_true:N \l_@@_backend_xform_bool + #4 + } + \tl_const:cx + { c_@@_backend_xform_wd_ \tl_to_str:n {#1} _tl } + { \tex_the:D \box_wd:c { g_@@_backend_xform_#1_box } } + \tl_const:cx + { c_@@_backend_xform_ht_ \tl_to_str:n {#1} _tl } + { \tex_the:D \box_ht:c { g_@@_backend_xform_#1_box } } + \tl_const:cx + { c_@@_backend_xform_dp_ \tl_to_str:n {#1} _tl } + { \tex_the:D \box_dp:c { g_@@_backend_xform_#1_box } } + \box_set_dp:cn { g_@@_backend_xform_#1_box } { \c_zero_dim } + \box_set_ht:cn { g_@@_backend_xform_#1_box } { \c_zero_dim } + \box_set_wd:cn { g_@@_backend_xform_#1_box } { \c_zero_dim } + \hook_gput_next_code:nn {shipout/background} + { + \mode_leave_vertical: %needed, the xform disappears without it. + \@@_backend:x + { + bxobj ~ \@@_backend_xform_ref:n { #1 } + \c_space_tl width ~ \pdfxform_wd:n { #1 } + \c_space_tl height ~ \pdfxform_ht:n { #1 } + \c_space_tl depth ~ \pdfxform_dp:n { #1 } + } + \box_use_drop:c { g_@@_backend_xform_#1_box } + \@@_backend:x {put ~ @resources ~<<#3>> } + \@@_backend:x + { + put~ @resources ~ + << + /ExtGState~ \pdf_object_ref:n { Page/Resources/ExtGState } + >> + } + \@@_backend:x + { + put~ @resources ~ + << + /Pattern~ \pdf_object_ref:n { Page/Resources/Pattern } + >> + } + \@@_backend:x + { + put~ @resources ~ + << + /Shading~ \pdf_object_ref:n { Page/Resources/Shading } + >> + } + \@@_backend:x + { + put~ @resources ~ + << + /ColorSpace~ + \pdf_object_ref:n { Page/Resources/ColorSpace } + >> + } + \exp_args:Nx + \@@_backend:x {exobj ~<<#2>>} + } + } + + + + \cs_new:Npn \@@_backend_xform_ref:n #1 + { + @pdf.xform \int_use:c { c_@@_backend_xform_ \tl_to_str:n {#1} _int } + } + + \cs_new_protected:Npn \@@_backend_xform_use:n #1 + { + \hbox_set:Nn \l_@@_backend_tmpa_box + { + \@@_backend:x + { + uxobj~ \@@_backend_xform_ref:n { #1 } + } + } + \box_set_wd:Nn \l_@@_backend_tmpa_box { \pdfxform_wd:n { #1 } } + \box_set_ht:Nn \l_@@_backend_tmpa_box { \pdfxform_ht:n { #1 } } + \box_set_dp:Nn \l_@@_backend_tmpa_box { \pdfxform_dp:n { #1 } } + \box_use_drop:N \l_@@_backend_tmpa_box + } +%</dvipdfmx|xdvipdfmx> +%<*dvisvgm> +% unclear what it should do!! +\cs_new_protected:Npn \@@_backend_xform_new:nnnn #1 #2 #3 #4 {} +\cs_new_protected:Npn \@@_backend_xform_use:n #1 {} +\cs_new:Npn \@@_backend_xform_ref:n {} +%</dvisvgm> +%<*drivers> +%% all +\prg_new_conditional:Npnn \@@_backend_xform_if_exist:n #1 { p , T , F , TF } + { + \int_if_exist:cTF { c_@@_backend_xform_ \tl_to_str:n {#1} _int } + { \prg_return_true: } + { \prg_return_false:} + } +\prg_new_eq_conditional:NNn \pdfxform_if_exist:n\@@_backend_xform_if_exist:n + { TF , T , F , p } +%</drivers> +% \end{macrocode} +% \end{macro} +% \end{macro} +% \subsection{lua code for lualatex} +% \begin{macrocode} +%<*lua> +ltx= ltx or {} +ltx.@@ = ltx.@@ or {} +ltx.@@.Page = ltx.@@.Page or {} +ltx.@@.Page.dflt = ltx.@@.Page.dflt or {} +ltx.@@.Page.Resources = ltx.@@.Resources or {} +ltx.@@.Page.Resources.Properties = ltx.@@.Page.Resources.Properties or {} +ltx.@@.Page.Resources.List={"ExtGState","ColorSpace","Pattern","Shading"} +ltx.@@.object = ltx.@@.object or {} + +ltx.pdf= ltx.pdf or {} -- for "public" functions + +local @@ = ltx.@@ +local pdf = pdf + +local function @@_backend_Page_gput (name,value) + @@.Page.dflt[name]=value +end + +local function @@_backend_Page_gremove (name) + @@.Page.dflt[name]=nil +end + +local function @@_backend_Page_gclear () + @@.Page.dflt={} +end + +local function @@_backend_ThisPage_gput (page,name,value) + @@.Page[page] = @@.Page[page] or {} + @@.Page[page][name]=value +end + +local function @@_backend_ThisPage_gpush (page) + local token="" + local t = {} + local tkeys= {} + for name,value in pairs(@@.Page.dflt) do + t[name]=value + end + if @@.Page[page] then + for name,value in pairs(@@.Page[page]) do + t[name] = value + end + end + -- sort the table to get reliable test files. + for name,value in pairs(t) do + table.insert(tkeys,name) + end + table.sort(tkeys) + for _,name in ipairs(tkeys) do + token = token .. "/"..name.." "..t[name] + end + return token +end + +function ltx.@@.backend_ThisPage_gput (page,name,value) -- tex.count["g_shipout_readonly_int"] + @@_backend_ThisPage_gput (page,name,value) +end + +function ltx.@@.backend_ThisPage_gpush (page) + pdf.setpageattributes(@@_backend_ThisPage_gpush (page)) +end + +function ltx.@@.backend_Page_gput (name,value) + @@_backend_Page_gput (name,value) +end + +function ltx.@@.backend_Page_gremove (name) + @@_backend_Page_gremove (name) +end + +function ltx.@@.backend_Page_gclear () + @@_backend_Page_gclear () +end + + +local Properties = ltx.@@.Page.Resources.Properties +local ResourceList= ltx.@@.Page.Resources.List +local function @@_backend_PageResources_gpush (page) + local token="" + if Properties[page] then +-- we sort the table, so that the pdf test works + local t = {} + for name,value in pairs (Properties[page]) do + table.insert (t,name) + end + table.sort (t) + for _,name in ipairs(t) do + token = token .. "/"..name.." ".. Properties[page][name] + end + token = "/Properties <<"..token..">>" + end + for i,name in ipairs(ResourceList) do + if ltx.@@.Page.Resources[name] then + token = token .. "/"..name.." "..ltx.pdf.object_ref("Page/Resources/"..name) + end + end + return token +end + +-- the function is public, as I probably need it in tagpdf too ... +function ltx.pdf.Page_Resources_Properties_gput (page,name,value) -- tex.count["g_shipout_readonly_int"] + Properties[page] = Properties[page] or {} + Properties[page][name]=value + pdf.setpageresources(@@_backend_PageResources_gpush (page)) +end + +function ltx.pdf.Page_Resources_gpush(page) + pdf.setpageresources(@@_backend_PageResources_gpush (page)) +end + +function ltx.pdf.object_ref (objname) + if ltx.@@.object[objname] then + local ref= ltx.@@.object[objname] + return ref + else + return "false" + end +end +%</lua> +% \end{macrocode} +% \end{implementation} +% +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfannot.dtx b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfannot.dtx new file mode 100644 index 00000000000..94b7a89af87 --- /dev/null +++ b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfannot.dtx @@ -0,0 +1,583 @@ +% \iffalse meta-comment +% +%% File: l3pdfannot.dtx +% +% Copyright (C) 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=l3pdfannot (LaTeX PDF management testphase bundle)} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% \providecommand\hook[1]{\texttt{#1}} +% \title{^^A +% The \pkg{l3pdfannot} module\\ Commands for PDF annotations ^^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{l3pdfannot} documentation} +% This module contains a number of commands to create PDF annotations. +% The commands are \emph{not} always simple wrappers around primitive commands. +% To allow external packages to configure links and other annotations, +% some of the commands have hooks and use shared attribute dictionaries. +% For these commands the hooks and dictionaries are selected depending on the \meta{type} +% of the annotation. Currently the module only supports some general commands +% and link annotations. Commands for other annotations like widgets will be added +% later. +% +% \subsection{General annotation commands} +% +% \begin{function}[added = 2019-09-05, updated = 2020-04-14] +% { \pdfannot_box:nnnn } +% \begin{syntax} +% \cs{pdfannot_box:nnnn} \Arg{width} \Arg{height} \Arg{depth} \Arg{annot spec} +% \end{syntax} +% This creates an \texttt{/Type/Annot} object with the given dimensions. +% It doesn't use hooks or dictionaries. +% \end{function} +% \begin{function}[added = 2020-03-30] +% { \pdfannot_box:nnnnn } +% \begin{syntax} +% \cs{pdfannot_box:nnnnn} \Arg{type} \Arg{width} \Arg{height} \Arg{depth} \Arg{annot spec} +% \end{syntax} +% This creates an \texttt{/Type/Annot} object with the given dimensions. +% \meta{type} should be currently one of |link/URI|, |link/GoToR|, +% |link/Launch|, |link/GoTo| or |link/Named| or +% |widget|, it will then insert the attribute dictionary of this type +% additionally to the manually given \meta{annot spec}. +% The attribute dictionaries can be filled with commands described below. +% Hooks are not used. +% \end{function} +% \begin{function}[added = 2019-09-05] +% { \pdfannot_box_ref_last: } +% \begin{syntax} +% \cs{pdfannot_box_ref_last:} +% \end{syntax} +% This retrieves the object reference of the last box annotation created. +% \end{function} +% \subsection{Link annotations} +% Link annotations are special cases of annotations. In the PDF they are identified +% by an |/Subtype/Link| entry in the dictionary. +% Link annotations are quite important as many documents contain links, +% both internal and external. They need a set of special commands for two reasons: +% +% At first the content of links are not only boxes. Links can contain line +% and page breaks (this is normally implemented by the primitive command by +% creating a set of annotations). +% +% At second link annotations are objects that need some +% \enquote{management} as more than one +% package wants to configure their look and behaviour. +% For example \pkg{hyperref}, \pkg{ocgx2} and the code for tagged PDF (currently +% in \pkg{tagpdf}) all want to add keys and values to the dictionaries of +% link annotation and code around links. +% So commands to create link annotations should offer suitable hooks. +% There are three standard places in a link where such hooks are needed: +% At the begin (for example for a structure command or color), +% in the \emph{attr spec} dictionary of the link (for example for the border), and +% at the end of the link (to close a structure or the color group). +% For the begin and end hooks of the LaTeX hook management are predefined and used. +% To add and remove values from the \emph{attr spec} dictionary special +% commands described below are provided. The link commands switch to horizontal mode +% as the commands of pdftex and luatex can't be used in vertical mode. +% +% \begin{variable}{\c_pdfannot_link_types_seq} +% There are currently five link types, \texttt{URI}, \texttt{GoToR}, +% \texttt{Launch}, \texttt{GoTo} or \texttt{Named}, and there are store in this +% constant. +% \end{variable} +% \begin{variable} +% { +% pdfannot/link/TYPE/before, +% pdfannot/link/TYPE/begin, +% pdfannot/link/TYPE/end, +% pdfannot/link/TYPE/after +% } +% These are the hooks used by the following commands. TYPE can be one of +% \texttt{URI}, \texttt{GoToR}, +% \texttt{Launch}, \texttt{GoTo} or \texttt{Named} +% \end{variable} +% \begin{variable} +% { +% link/TYPE +% } +% These is the name of the dictionary used by the following commands. TYPE can be one of +% \texttt{URI}, \texttt{GoToR}, +% \texttt{Launch}, \texttt{GoTo} or \texttt{Named}. The dictionary can be changed +% by the commands \cs{pdfannot_dict_put:nnn} and friends described below. +% \end{variable} +% \begin{function}[added = 2020-03-12, updated = 2020-12-06]{ \pdfannot_link:nnn } +% \begin{syntax} +% \cs{pdfannot_link:nnn} \Arg{type} \Arg{user action spec} \Arg{link text} +% \end{syntax} +% This creates a link around the \meta{link text} with the specified +% \meta{user action spec}\footnote{The wording follows the pdftex documentation}. +% \texttt{/Subtype/Link} is added automatically. +% \meta{type} should be one of \texttt{URI}, \texttt{GoToR}, +% \texttt{Launch}, \texttt{GoTo} or \texttt{Named}. The |GoTo| variant does +% \emph{not} complain if the destination name is not known like +% \cs{pdfannot_link_goto_begin:nw}. +% The attributes stored in the local dictionary +% \texttt{link/}\meta{type} are inserted as +% \emph{attr spec} and the code in the begin and end hook +% \texttt{pdfannot/link/\meta{type}/before} +% and \texttt{pdfannot/link/\meta{type}/after} +% is executed before and after the link (outside the link command) +% while \texttt{pdfannot/link/\meta{type}/begin} +% and \texttt{pdfannot/link/\meta{type}/end} are directly around the link +% text. None of the hooks introduce a group. +% \meta{type} should normally be identical to the value of the |/S| key +% in the action dictionary. +% As example +% \begin{verbatim} +% \pdfannot_dict_put:nnn +% {link/URI} { C } {[1~0~0]} %red border +% \pdfannot_link:nnn { URI } +% { +% /A +% << +% /Type/Action +% /S/URI +% /URI(https://www.latex-project.org) +% >> +% } +% { link text } +% \end{verbatim} +% +% +% \end{function} +% \begin{function}[updated = 2020-12-06]{ \pdfannot_link_begin:nnw, \pdfannot_link_end:n } +% \begin{syntax} +% \cs{pdfannot_link_begin:nnw} \Arg{type} \Arg{user action spec} \meta{content} +% \cs{pdfannot_link_end:n} \Arg{type} +% \end{syntax} +% This creates a link around the \meta{content} with the specified +% \meta{user action spec} (e.g. an /A dictionary with an URI) or +% \meta{destination} (a name as defined with the +% first argument of \cs{pdf_destination:nn}). +% \texttt{/Subtype/Link} is added automatically. +% In contrast to \cs{pdfannot_link:nnn} this function +% does not absorb the argument when finding the \meta{content}, and so can +% be used in circumstances where the \meta{content} may not be a simple +% argument. But beside this, it works similar and use the same hooks. +% As example +% \begin{verbatim} +% \pdfannot_link_begin:nnw { URI } +% { +% /A<< +% /Type/Action +% /S/URI +% /URI(https://www.latex-project.org) +% >> +% } +% link text +% \pdfannot_link_end:n { URI } +% \end{verbatim} +% \end{function} +% \begin{function}[updated = 2020-12-06]{ \pdfannot_link_goto_begin:nw, \pdfannot_link_goto_end: } +% \begin{syntax} +% \cs{pdfannot_link_goto_begin:nw} \Arg{destination} \meta{content} +% \cs{pdfannot_link_goto_end:} +% \end{syntax} +% This is a special, shorter version for links to internal destinations. It always +% uses the hooks and dictionary of the |GoTo| link type. \meta{destination} is a +% destination name. In difference to |\pdfannot_link:nnn { GoTo }| it will complain if +% \meta{destination} is an unknown destination and give the message +% +% |name{ZZZZ} has been referenced but does not exist, replaced by a fixed one| +% +% \end{function} +% \begin{function}[added = 2021-02-14]{ \pdfannot_link_ref_last: } +% This retrieves the object reference a link created previously with the commands +% above. This doesn't work currently with xelatex but a feature request has +% been made. see https://tug.org/pipermail/dvipdfmx/2020-December/000134.html +% \end{function} +% \begin{function}[added = 2021-02-14]{ \pdfannot_ref_last: } +% This retrieves the object reference a previously annotation +% created either with a link or a general box command. When the last was a link +% it won't work with xelatex. +% see https://tug.org/pipermail/dvipdfmx/2020-December/000134.html +% \end{function} +% \begin{NOTE}{UF} +% only annot link or also annot? +% \end{NOTE} +% \begin{function}[added = 2020-03-12]{ \pdfannot_link_margin:n } +% \begin{syntax} +% \cs{pdfannot_link_margin:n} \Arg{dimen} +% \end{syntax} +% This sets the dimension of the link margin. +% \end{function} +% \begin{function}[added = 2020-12-04]{ \pdfannot_dict_put:nnn } +% \begin{syntax} +% \cs{pdfannot_dict_put:nnn} \Arg{dictionary name} \Arg{key} \Arg{value} +% \end{syntax} +% This adds (locally) a key-value to the internal annot dictionaries used +% by the link commands above. +% \meta{dictionary name} should be currently one of \texttt{link/URI}, +% \texttt{link/URI},\texttt{link/GoToR}, \texttt{link/Launch}, +% \texttt{link/GoTo}, \texttt{link/Named}. +% \end{function} +% \begin{function}[added = 2020-12-04]{ \pdfannot_dict_remove:nn } +% \begin{syntax} +% \cs{pdfannot_dict_remove:nn} \Arg{dictionary name} \Arg{key} +% \end{syntax} +% This removes a key-value from the internal annot dictionary +% \meta{dictionary name} should be currently one of +% \texttt{link/URI}, \texttt{link/GoToR}, \texttt{link/Launch}, +% \texttt{link/GoTo}, \texttt{link/Named}. +% \end{function} +% \begin{function}[added = 2020-12-04]{ \pdfannot_dict_show:n } +% \begin{syntax} +% \cs{pdfannot_dict_show:n} \Arg{dictionary name} +% \end{syntax} +% This shows the content of the internal annot dictionary. +% \meta{dictionary name} should be currently one of \texttt{link/URI}, +% \texttt{link/URI}, \texttt{link/GoToR}, \texttt{link/Launch}, +% \texttt{link/GoTo}, \texttt{link/Named}. +% \end{function} +% +% \begin{variable}[added = 2020-12-28]{\l_pdfannot_F_bitset} +% This is a bitset variable, with the named index names suitable for the +% /F flag in an annotation. +% It can be used for example like this: +% \begin{verbatim} +% \pdfannot_dict_put:nnn {link/URI} {F} +% { \bitset_to_arabic:N \l_pdfannot_F_bitset } +% \bitset_set_true:Nn \l_pdfannot_F_bitset {Print} +% \end{verbatim} +% The known keys for the bitset are |Invisible|, |Hidden|, +% |Print|, |NoZoom|, |NoRotate|, |NoView|, |ReadOnly|, |Locked|, |ToggleNoView|, +% |LockedContents| which correspond to the names used in the PDF references. +% \end{variable} +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{l3pdfannot} implementation} +% +% \begin{macrocode} +%<@@=pdfannot> +%<*header> +\ProvidesExplPackage {l3pdfannot} {2021-02-22} {0.95a} + {PDF-annotations} +\RequirePackage{l3pdfdict} +%</header> +% \end{macrocode} +% Annotations have a /F flag, we provide a public +% bitset for it. +% \begin{macrocode} +%<*package> +\RequirePackage{l3bitset} +\bitset_new:Nn \l_pdfannot_F_bitset + { + Invisible = 1, + Hidden = 2, + Print = 3, + NoZoom = 4, + NoRotate = 5, + NoView = 6, + ReadOnly = 7, + Locked = 8, + ToggleNoView = 9, + LockedContents = 10 + } +% \end{macrocode} +% \begin{NOTE}{UF} +% The code/naming tries to unify general annotations and the special type of +% link under a common name. +% regarding naming and relation of annotation commands see +% https://github.com/FrankMittelbach/AccessiblePDF/issues/73 +% \subsection{Annotations / backend} +% The backend commands are in l3backend: +% \cs{__pdf_backend_annotation:nnnn} and \cs{__pdf_backend_annotation_last:} +% \cs{__pdf_backend_link_begin_user:nnw}, etc +% \end{NOTE} +% +% \subsection{ General Annotations } +% \begin{variable} +% { \g_@@_use_lastlink_bool } +% The pdf engines have two different primitive commands to refer to the last created +% annotation: one for links, one for boxed annotation. We use a boolean to decide +% which one should be used, so that only one user command is needed. +% \begin{macrocode} +\bool_new:N \g_@@_use_lastlink_bool +% \end{macrocode} +% \end{variable} +% \begin{NOTE}{UF} +% type or not type? Syntax for type? +% should there be a version without type? +% \end{NOTE} + +% \begin{macrocode} +\cs_new_protected:Npn \pdfannot_box:nnnn #1 #2 #3 #4 + { + \__pdf_backend_annotation:nnnn {#1}{#2}{#3}{#4} + \bool_gset_false:N\g_@@_use_lastlink_bool + } + +\cs_new:Npn \pdfannot_box_ref_last: + { + \__pdf_backend_annotation_last: + } + +\cs_new_protected:Npn \pdfannot_box:nnnnn #1 #2 #3 #4 #5 + { + \exp_args:Nx + \__pdf_backend_annotation:nnnn {#2}{#3}{#4} + { + \pdfdict_if_exist:nT { l_@@/#1 } + { + \pdfdict_use:n { l_@@/#1} + } + #5 + } + \bool_gset_false:N\g_@@_use_lastlink_bool + } +% \end{macrocode} +% \subsection{Annotations, subtype Widget}\label{pdf:annot:widget} +% Currently no code is provided here. +% The local dictionary \texttt{l_@@/Widget} is a skeleton +% dictionary for this subtype. It currently contains as only entry +% the subtype setting (the /Type is added by the backend). +% \begin{macrocode} + \pdfdict_new:n { l_@@/widget } + \pdfdict_put:nnn { l_@@/widget }{ Subtype }{ /Widget } +% \end{macrocode} +% +% \subsection{Annotations, subtype Link}\label{sec:links} +% The code assumes that there will be different link types +% (currently URI, GoToR, Launch, GoTo, Named, hyperref uses the names +% url,file,run,link,menu) and that links of the same type share +% the \emph{attr spec} and also the same begin/end +% code. The list of link types need to stay restricted and well documented so that +% all packages know which types they have to handle. It is stored in a constant +% seq. +% \begin{NOTE}{UF} +% Perhaps a |cite| type will be useful at some time. -- Thinking more about it, +% a |cite| type is not sensible. hyperref supports it, but it doesn't fit in. +% Commands like cite, gls, acro, footnote and so on should locally change +% linkcolor and linkbordercolor. +% Probably we will need some commands to add an attribute to all link types +% at once. +% hyperref commands for the various type: +% url |\hyper@linkurl|, +% file |\hyper@linkfile|, +% run |\@hyper@launch run|, +% link |\hyper@link|, |\find@pdflink| +% menu |\Acrobatmenu| +% \end{NOTE} +% \begin{variable}[added = 2020-03-12]{ \c_pdfannot_link_types_seq } +% This constant sequence contains the list of currently supported link types +% for which hooks and dictionaries exist. +% \end{variable} +% +% \begin{variable} +% { +% link/TYPE, +% pdfannot/link/TYPE/before, +% pdfannot/link/TYPE/begin, +% pdfannot/link/TYPE/end, +% pdfannot/link/TYPE/after +% } +% These setup the dictionary and the hook pairs. +% \begin{macrocode} +\seq_const_from_clist:Nn \c_pdfannot_link_types_seq { URI , GoToR , Launch , GoTo, Named } +\seq_map_inline:Nn \c_pdfannot_link_types_seq + { + \pdfdict_new:n { l_@@/link/#1 } + \hook_new_pair:nn + {pdfannot/link/#1/before} + {pdfannot/link/#1/after} + \hook_new_pair:nn + {pdfannot/link/#1/begin} + {pdfannot/link/#1/end} + } +% \end{macrocode} +% \end{variable} +% \subsubsection{Annotations, subtype Link /management} +% +% \begin{macro}{\pdfannot_link:nnn,\pdfannot_link:nxn} +% \begin{macrocode} +\cs_new_protected:Nn \pdfannot_link:nnn %#1 type (URI, GoTo etc), + %#2 action spec, #3 link text + { + \hook_use:n { pdfannot/link/#1/before} + \mode_leave_vertical: + \exp_args:Nxx %xetex needs expansion + \__pdf_backend_link_begin_user:nnw + { + \pdfdict_if_exist:nT { l_@@/link/#1 } + { + \pdfdict_use:n { l_@@/link/#1} + } + } + { + /Subtype/Link + #2 %exp_not? + } + \bool_gset_true:N \g_@@_use_lastlink_bool + \hook_use:n { pdfannot/link/#1/begin} + #3 + \hook_use:n { pdfannot/link/#1/end} + \__pdf_backend_link_end: + \bool_gset_true:N \g_@@_use_lastlink_bool + \hook_use:n { pdfannot/link/#1/after} + } +\cs_generate_variant:Nn \pdfannot_link:nnn {nxn} +% \end{macrocode} +% \end{macro} +% \begin{macro}{ +% \pdfannot_link_begin:nnw, +% \pdfannot_link_begin:nxw, +% \pdfannot_link_end:n } +% \begin{macrocode} +\cs_new_protected:Npn \pdfannot_link_begin:nnw #1 #2 %#1 type, #2 action spec + { + \hook_use:n { pdfannot/link/#1/before} + \mode_leave_vertical: + \exp_args:Nxx %xetex needs expansion + \__pdf_backend_link_begin_user:nnw + { + \pdfdict_if_exist:nT { l_@@/link/#1 } + { + \pdfdict_use:n { l_@@/link/#1} + } + } + { #2 } + \bool_gset_true:N \g_@@_use_lastlink_bool + \hook_use:n { pdfannot/link/#1/begin} + } + +\cs_new_protected:Nn \pdfannot_link_end:n %#1 type, e.g. url + { + \hook_use:n { pdfannot/link/#1/end} + \__pdf_backend_link_end: + \bool_gset_true:N \g_@@_use_lastlink_bool + \hook_use:n { pdfannot/link/#1/after} + } +\cs_generate_variant:Nn \pdfannot_link_begin:nnw {nxw} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\pdfannot_link_goto_begin:nw, \pdfannot_link_goto_end:} +% \begin{macrocode} +\cs_new_protected:Npn \pdfannot_link_goto_begin:nw #1 %#1 destination + { + \hook_use:n { pdfannot/link/GoTo/before} + \mode_leave_vertical: + \exp_args:Nxx %xetex needs expansion + \__pdf_backend_link_begin_goto:nnw + { + \pdfdict_use:n { l_@@/link/GoTo} + } + { #1 } + \bool_gset_true:N \g_@@_use_lastlink_bool + \hook_use:n { pdfannot/link/GoTo/begin} + } + +\cs_new_protected:Nn \pdfannot_link_goto_end: + { + \hook_use:n { pdfannot/link/GoTo/end} + \__pdf_backend_link_end: + \bool_gset_true:N \g_@@_use_lastlink_bool + \hook_use:n { pdfannot/link/GoTo/after} + } +% \end{macrocode} +% \end{macro} +% \begin{macro}[EXP]{\pdfannot_link_ref_last:, \pdfannot_ref_last:} +% \begin{macrocode} +\cs_new:Nn \pdfannot_link_ref_last: { \__pdf_backend_link_last: } +% \end{macrocode} +% \begin{macrocode} +\cs_new:Npn \pdfannot_ref_last: + { + \bool_if:NTF \g_@@_use_lastlink_bool + { + \__pdf_backend_link_last: + } + { + \__pdf_backend_annotation_last: + } + } +% \end{macrocode} +% \end{macro} +% \begin{macro}{ \pdfannot_link_margin:n} +% \begin{macrocode} +\cs_new_protected:Npn \pdfannot_link_margin:n #1 + { + \__pdf_backend_link_margin:n { #1 } + } +% \end{macrocode} +% \end{macro} +% \begin{macro} +% { +% \pdfannot_dict_put:nnn, +% \pdfannot_dict_put:nnx, +% \pdfannot_dict_remove:nn, +% \pdfannot_dict_show:n +% } +% \begin{macrocode} +\cs_new_protected:Npn \pdfannot_dict_put:nnn #1 #2 #3 + { + \pdfdict_put:nnn { l_@@/#1 } { #2 }{ #3 } + } +\cs_generate_variant:Nn \pdfannot_dict_put:nnn {nnx} +% \end{macrocode} +% \begin{macrocode} +\cs_new_protected:Npn \pdfannot_dict_remove:nn #1 #2 + { + \pdfdict_remove:nn { l_@@/#1 } { #2 } + } +% \end{macrocode} +% \begin{macrocode} +\cs_new_protected:Npn \pdfannot_dict_show:n #1 + { + \pdfdict_show:n { l_@@/#1 } + } +%</package> +% \end{macrocode} +% \end{macro} +% \end{implementation} +% +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfdict.dtx b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfdict.dtx new file mode 100644 index 00000000000..558b721ca57 --- /dev/null +++ b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfdict.dtx @@ -0,0 +1,591 @@ +% \iffalse meta-comment +% +%% File: l3pdfdict.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=l3pdfdict (LaTeX PDF management testphase bundle)} + +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% \title{^^A +% The \pkg{l3pdfdict} module---tools for PDF dictionaries ^^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{l3pdfdict} documentation} +% Many PDF objects are or contain dictionaries---structures +% containing a number of \mbox{(pdf-)}name/value pairs. +% Examples are attributes of links, +% filespec dictionaries, xform dictionaries, the catalog, the info dictionary. +% The commands in this module offer an number of +% tools to handle such dictionaries. The module setups a name space for the +% dictionary names and offers some commands to output dictionaries. +% +% The dictionaries work in many respects +% like property lists with a few PDF specific changes: +% \begin{itemize} +% \item The keys are always converted with \cs{str_convert_pdfname:n} +% to get a correct PDF name; +% \item a key with a empty value can not be added, it will be ignored; +% \item there is a dedicated function to output the property as space +% separated list with keys with slash: \texttt{/key1 value1 /key2 value2}. +% \end{itemize} +% Local and global dictionaries can be created. +% +% \subsection{User Commands} +% \begin{function}[updated = 2020-12-03] +% {\pdfdict_new:n} +% \begin{syntax} +% \cs{pdfdict_new:n} \Arg{dictionary name} +% \end{syntax} +% This function create a new local or global dictionary. Which one depends on +% \meta{dictionary name}: If it begins with the standard |g| the dictionary is global, +% with |l| the dictionary is local, other starting chars will give an error. +% It is recommended to begin the name in the standard expl3 naming scheme +% with one or two underscores and a module name, +% so |g_module_XXXX| or |g__module_XXXX|. +% \end{function} +% +% \begin{function}[added = 2020-06-16,updated = 2020-12-03] +% {\pdfdict_set_eq:nn,\pdfdict_gset_eq:nn} +% \begin{syntax} +% \cs{pdfdict_set_eq:nn} \Arg{local dictionary name_1} \Arg{dictionary name_2}\\ +% \cs{pdfdict_gset_eq:nn} \Arg{global dictionary name_1}\Arg{dictionary name_2} +% \end{syntax} +% This functions copy \meta{dictionary name_2} into +% \meta{local/global dictionary name_1} locally or globally. If the +% dictionary \meta{local/global dictionary name_1} doesn't exist yet, it will be created. +% If \meta{dictionary name_2} doesn't exist yet, an error will be raised. +% \end{function} +% +% \begin{function}[added = 2020-04-06] +% {\pdfdict_put:nnn, \pdfdict_gput:nnn} +% \begin{syntax} +% \cs{pdfdict_put:nnn} \Arg{local dictionary} \Arg{name} \Arg{value} \\ +% \cs{pdfdict_gput:nnn} \Arg{global dictionary} \Arg{name} \Arg{value} +% \end{syntax} +% This function puts key \meta{name} and value \meta{value} locally or globally in the +% \meta{dictionary} created with \cs{pdfdict_new:n}. +% \Arg{name} should be a PDF Name without the starting slash. It will be stored +% with \cs{str_convert_pdfname:n}, so will be automatically correctly escaped in case +% it contains slashes, spaces or other chars not allowed in a PDF name. +% \meta{value} should be a valid PDF value for this name in the +% target dictionary. The value is \emph{neither} converted \emph{nor} escaped automatically. +% If the value is blank nothing is added to the dictionary. +% +% When adding a value keep in mind that the expansion behaviour +% of the backends differ. Some backends expand a +% value always fully when writing to the PDF, with other backends commands +% could end as strings in the PDF. This makes controlling the +% expansion quite tricky. It is better to not rely on +% \meta{value} to be expanded nor not expanded by the backend commands. +% \end{function} +% +% \begin{function}[EXP,added = 2020-12-04] +% { \pdfdict_item:nn, \pdfdict_item:ne } +% \begin{syntax} +% \cs{pdfdict_item:nn} \Arg{key} \Arg{value} +% \end{syntax} +% A simple command to output key-value as |/key value|. This is +% needed to output dictionaries in mapping commands. The command doesn't +% do any escaping, it expects that the name has been escaped when the value +% has been stored into the dictionary. +% If the value is blank nothing is output. +% The command is expandable if the content is it. +% \end{function} +% +% \begin{function}[EXP,updated = 2020-12-03] +% { \pdfdict_use:n } +% \begin{syntax} +% \cs{pdfdict_use:n} \Arg{dictionary} +% \end{syntax} +% This outputs the property list of the dictionary as a list of +% |/key value| pairs. +% This can be used e.g. when writing a dictionary object with +% \cs{pdf_object_write:nx} +% \end{function} +% +% \begin{function}[updated = 2020-12-03] +% {\pdfdict_show:n } +% \begin{syntax} +% \cs{pdfdict_show:n} \Arg{dictionary} +% \end{syntax} +% This shows the content of \meta{dictionary} in the log and on the terminal. +% \end{function} +%\begin{function}[EXP, pTF,updated = 2020-12-03] +% { \pdfdict_if_exist:n } +% \begin{syntax} +% \cs{pdfdict_if_exist:n} \Arg{dictionary} +% \end{syntax} +% This tests if the dictionary exists. +% \end{function} +% \begin{function}[EXP, pTF,updated = 2020-12-03] +% { \pdfdict_if_empty:n } +% \begin{syntax} +% \cs{pdfdict_if_empty:n} \Arg{dictionary} +% \end{syntax} +% This tests if the dictionary is empty. The result is false if the +% dictionary doesn't exist. +% \end{function} +% \begin{function}[added = 2020-07-06] +% {\pdfdict_get:nnN } +% \begin{syntax} +% \cs{pdfdict_get:nnN} \Arg{dictionary} \Arg{name} \meta{tl var} +% \end{syntax} +% Recovers the \meta{value} stored by \cs{pdfdict_put:nnn} or +% \cs{pdfdict_gput:nnn} +% for \meta{name} and places this in the \meta{token list +% variable}. If \meta{name} is not found +% then the \meta{token list variable} is set +% to the special marker \cs{q_no_value}. \meta{name} is first converted +% with \cs{str_convert_pdfname:n}. The \meta{token list +% variable} is set within the current \TeX{} group. +% \end{function} +% +% \begin{function}[updated = 2020-12-03] +% { +% \pdfdict_remove:nn, \pdfdict_gremove:nn +% } +% \begin{syntax} +% \cs{pdfdict_remove:nn} \Arg{local dictionary} \Arg{name}\\ +% \cs{pdfdict_gremove:nn} \Arg{global dictionary} \Arg{name} +% \end{syntax} +% Removes \meta{name} and its associated \meta{value} from +% the \Arg{dictionary} +% The removal is local from local dictionaries +% and global from global dictionaries. +% 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. \meta{name} is first converted +% with \cs{str_convert_pdfname:n}. +% \end{function} +% \end{documentation} +% +% \begin{implementation} +% \section{\pkg{l3pdfdict} implementation} +% \begin{macrocode} +%<@@=pdfdict> +%<*header> +\ProvidesExplPackage {l3pdfdict} {2021-02-22} {0.95a} + {Tools for PDF dictionaries (LaTeX PDF management testphase bundle)} +%</header> +% \end{macrocode} +% \subsection{messages} +% \begin{macrocode} +%<*package> +\cs_new:Npn \@@_get_type:n #1 + { + \str_case_e:nn { \str_head:n{#1} } + { + {g}{global} + {l}{local} + } + } +\msg_new:nnn { pdfdict } { show-dict } + { %#1: name of the dictionary + %#2: expanded content + %#3: type + The~#3~dictionary~'#1'~ + \tl_if_empty:nTF {#2} + { is~empty \\>~ . } + { contains~the~pairs~(without~outer~braces): #2 . } + } +\msg_new:nnn { pdfdict } { unknown-dict } + { + The~dictionary~'#1'~is~unknown. + } +\msg_new:nnn { pdfdict } { dict-already-defined } + { + The~#2~dictionary~'#1'~is~already~defined. + } +\msg_new:nnn { pdfdict } { empty-value } + { The~value~#1~for~#2~is~blank~and~will~be~ignored } + +\msg_new:nnn { pdfdict } { invalid-name } + { Name~'#1'~is~not~valid\\ + Names~of~dictionaries~should~start~with~'g_'~or~'l_' } + +% \end{macrocode} +% + +% \subsection{Creating dictionaries} +% \begin{variable} +% {\g_@@_names_seq,\g_@@_gnames_seq} +% Two seq to store the used names for diagnostics. +% \begin{macrocode} +\seq_new:N \g_@@_lnames_seq +\seq_new:N \g_@@_gnames_seq +% \end{macrocode} +% \end{variable} +% +% \begin{macro} +% { +% \@@_name:n, \__kernel_pdfdict_name:n, +% \@@_new:n, \pdfdict_new:n, +% } +% +% This are the commands to create new dictionaries and to access their internal +% name. All internal names start with |g__pdfdict_/| or |l__pdfdict_/|. +% +% For the other modules we also need a kernel command to access the internal +% name to speed up the code and allow the use standard commands +% of the \texttt{prop} module to deal with the dictionaries. For example\\ +% |\prop_clear:c { \__kernel_pdfdict_name:n { name }}| +% +% \begin{macrocode} +\cs_new:Npn \@@_name:n #1 % #1 dictionary name + { + \str_head:n{#1}_@@_/#1_prop + } +\cs_set_eq:NN \__kernel_pdfdict_name:n \@@_name:n + +\cs_new_protected:Npn \@@_new:n #1 + { + \@@_if_exist:nTF { #1 } + { + \msg_error:nnxx + { pdfdict } + { dict-already-defined } + { \tl_to_str:n {#1} } + { \@@_get_type:n{#1} } + } + { + \str_case_e:nnF { \str_head:n{#1} } + { + {g} + { + \prop_new:c { \@@_name:n { #1 } } + \seq_gput_right:cn {g_@@_gnames_seq} { #1 } + } + {l} + { + \prop_new:c { \@@_name:n { #1 } } + \seq_gput_right:cn {g_@@_lnames_seq} { #1 } + } + } + { + \msg_error:nnx{pdfdict}{invalid-name}{\tl_to_str:n{#1}} + } + } + } + +\cs_set_eq:NN \pdfdict_new:n \@@_new:n +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \@@_set_eq:nn,\pdfdict_set_eq:nn, +% \@@_gset_eq:nn,\pdfdict_gset_eq:nn +% } +% \begin{macrocode} +\cs_new_protected:Npn \@@_set_eq:nn #1 #2 + { + \@@_if_exist:nTF { #2 } + { + \@@_if_exist:nF { #1 } + { + \@@_new:n { #1 } + } + \prop_set_eq:cc { \@@_name:n {#1} }{ \@@_name:n {#2} } + } + { + \msg_error:nnn { pdfdict } { unknown-dict } { #1 } + } + } + +\cs_set_eq:NN \pdfdict_set_eq:nn \@@_set_eq:nn + +\cs_new_protected:Npn \@@_gset_eq:nn #1 #2 + { + \@@_if_exist:nTF { #2 } + { + \@@_if_exist:nF { #1 } + { + \@@_new:n { #1 } + } + \prop_gset_eq:cc { \@@_name:n {#1} }{ \@@_name:n {#2} } + } + { + \msg_error:nnn { pdfdict } { unknown-dict } { #1 } + } + } + +\cs_set_eq:NN \pdfdict_gset_eq:nn \@@_gset_eq:nn +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro} +% { +% \@@_if_exist:n, \pdfdict_if_exist:n, +% } +% +% Existence tests. +% \begin{macrocode} +%local +\prg_new_conditional:Npnn \@@_if_exist:n #1 { p , T , F , TF } + { + \prop_if_exist:cTF + { \@@_name:n { #1 } } + { \prg_return_true: } + { \prg_return_false: } + } +\prg_set_eq_conditional:NNn + \pdfdict_if_exist:n \@@_if_exist:n { p , T , F , TF } +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro} +% { +% \@@_if_empty:n, \pdfdict_if_empty:n, +% } +% +% Tests for emptiness. +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_empty:n #1 { p , T , F , TF } + { + \prop_if_empty:cTF + { \@@_name:n { #1 } } + { \prg_return_true: } + { \prg_return_false: } + } + +\prg_set_eq_conditional:NNn + \pdfdict_if_empty:n \@@_if_empty:n { p , T , F , TF } +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro} +% { +% \@@_put:nnn, \pdfdict_put:nnn, +% \@@_gput:nnn,\pdfdict_gput:nnn +% } +% These are the commands to store values into the dictionaries. +% The main difference to adding values to a normal property list is, +% that the keys are converted with \cs{str_convert_pdfname:n} +% and that empty values are ignored. +% \begin{macrocode} +\cs_new_protected:Npn \@@_put:nnn #1 #2 #3 %#1 (local) dict, #2 name, #3 value + { + \tl_if_blank:nTF { #3 } + { + \msg_warning:nnnn { pdfdict }{ empty-value }{ #2 } { #1 } + } + { + \@@_if_exist:nTF { #1 } + { + \exp_args:Nnx \prop_put:cnn + { \@@_name:n { #1 } }{ \str_convert_pdfname:n { #2 } } { #3 } + } + { + \msg_error:nnn { pdfdict } { unknown-dict } { #1 } + } + } + } + +\cs_set_eq:NN \pdfdict_put:nnn \@@_put:nnn +\cs_generate_variant:Nn \pdfdict_put:nnn {nnx,nno} + +\cs_new_protected:Npn \@@_gput:nnn #1 #2 #3 %#1 global dict, #2 name, #3 value + { + \tl_if_empty:nTF { #3 } + { + \msg_warning:nnnn { pdfdict }{ empty-value }{ #2 } { #1 } + } + { + \@@_if_exist:nTF { #1 } + { + \exp_args:Nnx \prop_gput:cnn + { \@@_name:n { #1 } }{ \str_convert_pdfname:n { #2 } } { #3 } + } + { + \msg_error:nnn { pdfdict } { unknown-dict } { #1 } + } + } + } + +\cs_set_eq:NN \pdfdict_gput:nnn \@@_gput:nnn +\cs_generate_variant:Nn \pdfdict_gput:nnn {nnx,nno} +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \@@_get:nnN, \pdfdict_get:nnN, +% } +% Recover the values. The name must be first escaped to match the stored name. +% \begin{macrocode} +\cs_new_protected:Npn \@@_get:nnN #1 #2 #3 %dict,key,macro + { + \@@_if_exist:nTF { #1 } + { + \exp_args:Nnx \prop_get:cnN + { \@@_name:n { #1 } } + { \str_convert_pdfname:n { #2 } } #3 + } + { + \msg_error:nnn { pdfdict } { unknown-dict } { #1 } + } + } + +\cs_set_eq:NN \pdfdict_get:nnN \@@_get:nnN +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \@@_remove:nn, \pdfdict_remove:nn, +% \@@_gremove:nn,\pdfdict_gremove:nn +% } +% This removes a name/value pair from a dictionary. +% The name has to be passed through the escaping. +% \begin{macrocode} +\cs_new_protected:Npn \@@_remove:nn #1 #2 %dict,name + { + \@@_if_exist:nTF { #1 } + { + \exp_args:Nnx \prop_remove:cn + { \@@_name:n { #1 } }{ \str_convert_pdfname:n { #2 } } + } + { + \msg_error:nnn { pdfdict } { unknown-dict } { #1 } + } + } +\cs_set_eq:NN \pdfdict_remove:nn \@@_remove:nn + +\cs_new_protected:Npn \@@_gremove:nn #1 #2 %dict,name + { + \@@_if_exist:nTF { #1 } + { + \exp_args:Nnx \prop_gremove:cn + { \@@_name:n { #1 } }{ \str_convert_pdfname:n { #2 } } + } + { + \msg_error:nnn { pdfdict } { unknown-dict } { #1 } + } + } + +\cs_set_eq:NN \pdfdict_gremove:nn \@@_gremove:nn +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { \@@_show:Nn, \pdfdict_show:n } +% This allows to show the content of dictionaries. It also displays if a +% dictionary is local or global. If both exists both are shown. +% \begin{macrocode} +\cs_new_protected:Npn \@@_show:Nn #1#2 %#1 message command, #2 dict + { + \prop_if_exist:cTF { \@@_name:n { #2 } } + { + #1 + { pdfdict } + { show-dict } + { \tl_to_str:n {#2} } + { \prop_map_function:cN {\@@_name:n { #2 }} \msg_show_item:nn } + { \@@_get_type:n{#2} } + { } + } + { + #1 { pdfdict } { unknown-dict } { #2 } {}{}{} + } + } +\cs_new_protected:Npn \pdfdict_show:n #1 + { + \@@_show:Nn \msg_show:nnxxxx {#1} + } +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\@@_item:nn, \@@_item:ne} +% \begin{macro}{\pdfdict_item:nn, \pdfdict_item:ne} +% \begin{macrocode} +\cs_new:Npn \@@_item:nn #1 #2 %#1 name, #2 value + { + \tl_if_blank:nF {#2} { /#1~#2~ } + } +\cs_generate_variant:Nn \@@_item:nn {ne} +\cs_set_eq:NN \pdfdict_item:nn \@@_item:nn +\cs_generate_variant:Nn \pdfdict_item:nn {ne} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% \begin{macro} +% { +% \@@_use:n,\pdfdict_use:n +% } +% \cs{@@_use:n} outputs a prop as needed in a dictionary: +% as a list of /\meta{key} \meta{value} pairs. +% \begin{NOTE}{UF} +% !! is e-expansion the right thing? +% \end{NOTE} +% \begin{macrocode} +\cs_new:Npn \@@_use:n #1 %#1 dict + { + \prop_map_function:cN { \@@_name:n { #1 } } \@@_item:ne + } + +\cs_set_eq:NN \pdfdict_use:n \@@_use:n +% \end{macrocode} +% \end{macro} +% +% +% \begin{macrocode} +%</package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdffile.dtx b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdffile.dtx new file mode 100644 index 00000000000..c1a2a25591d --- /dev/null +++ b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdffile.dtx @@ -0,0 +1,746 @@ +% \iffalse meta-comment +% +%% File: l3pdffile.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 package 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=l3pdffile (LaTeX PDF management testphase bundle)} + +\providecommand\potentialclash{\noindent\llap{\dbend\ }} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% \title{^^A +% The \pkg{l3pdffile} module\\ Embedding and referencing files in a PDF ^^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{l3pdffile} documentation} +% \subsection{Introduction} +% \subsubsection{Background} +% External files can be referenced from a PDF in three ways: +% \begin{enumerate} +% \item through an annotation of type Link, +% \item by referencing a local file in the file system, +% \item by embedding the file directly into the PDF +% \end{enumerate} +% Case 1 (Links) are created with the \cs{pdfannot} commands. +% This module handles the two other cases. Actually from the view +% of the PDF format they are quite similar: Case 2 is case 3 without the stream +% object and without the /EF entry in the /FileSpec dictionary (this points to the +% stream object of the file). Not embedding the file makes the PDF smaller. But it is +% also less portable: the files can only be found if they are in the right location +% relative to the PDF. The normal case is to embed the file. +% +% The tasks to embed and reference such a file are +% \begin{enumerate} +% \item Embed the file in a stream. +% \item Create a FileSpec dictionary which references the stream object in the /EF +% dictionary: +% \begin{verbatim} +% << +% /Type /Filespec +% /F (l3pdffile.dtx) +% /UF (l3pdffile.dtx) +% /AFRelationship /Source +% /EF <</F 21 0 R /UF 21 0 R>> %case 3, embedded file +% >> +% \end{verbatim} +% The file names in the /UF and /F value don't need to be identical to the +% name of file on the disc. It is quite possible to embed a \texttt{zzz.tex} +% and name it \texttt{blub.tex}. The second name is then what the user will see +% in the attachment list or in the properties of an annotation. +% +%\item Reference the FileSpec dictionary so that the user can access the file. +% This can be done in various way: +% \begin{enumerate} +% \item With an annotation (/Subtype/FileAttachment). This is done by +% \pkg{attachfile}, \pkg{attachfile2} and \pkg{intopdf}. +% Typical entries of such an annotation are: +% +% \medskip +% \begin{tabular}{lll} +% key & value type & notes\\\hline +% /FS & object reference &(FileSpec dictionary)\\ +% /Name & name & /Graph, /PushPin, /Paperclip, /Tag\\ +% /Contents & text string & optional but recommended\\ +% /F & integer & Flags\\ +% /AP & dictionary & Appearance (required if rectangle >0) \\ +% /AS & name\\ +% \end{tabular} +% +% The |/AP| takes precedence over Border and similar keys. +% \item Through an entry in the |/EmbeddedFiles| name tree. +% This is what \pkg{embedfiles} does. +% \begin{verbatim} +% 20 0 obj %Document Name tree +% <</EmbeddedFiles 21 0 R>> +% endobj +% 21 0 obj %Embedded Files Name dictionary +% <</Names [(AcmeCustomCrypto Protected PDF.pdf) 17 0 R]>> +% endobj +% \end{verbatim} +% The strings (keys) in the |/Names| dictionary must be sorted lexically. +% But they don't have to be the file name or anything related to +% the file name. The resource management code uses l3emb0001, l3emb0002~\ldots, +% which allows up to 9999 files. +% The key can be needed to identify the start file in a collection, +% so their relation to the files are stored in a property list. +% +% \item Through the |/AF| key in various objects (pdf 2.0). +% The value is normally an array of object +% references, but it can also be a name which is mapped to an array in /Properties: +% \begin{verbatim} +% /AF /NamedAF BDC +% /Properties <</NamedAF [12 0 R] +% \end{verbatim} +% The related |/FileSpec| dictionary should contain an +% |/AFRelationship| key in this case (but it doesn't harm to add it by +% default anyway). The values of this key is describe in table~\ref{tab:AFrel}. +% +% \begin{table} +% \caption{Values of the \texttt{/AFRelationship} key\label{tab:AFrel}} +% \begin{tabular}{lp{8cm}} +% Source & shall be used if this file specification is the original +% source material for the associated content.\\ +% Data & shall be used if this file specification represents +% information used to +% derive a visual presentation – such as for a table or a graph.\\ +% Alternative & shall be used if this file specification is an +% alternative representation of content, for example audio.\\ +% Supplement & shall be used if this file specification represents +% a supplemental representation of the original source or data +% that may be more easily consumable +% (e.g., A MathML version of an equation).\\ +% EncryptedPayload & shall be used if this file specification +% is an encrypted payload document that should be displayed to the user +% if the PDF processor has the cryptographic filter +% needed to decrypt the document.\\ +% FormData & shall be used if this file specification +% is the data associated with the AcroForm +% (see 12.7.3, \enquote{Interactive form dictionary}) of this PDF.\\ +% Schema & shall be used if this file specification is a schema +% definition for the associated object +% (e.g. an XML schema associated with a metadata stream).\\ +% Unspecified &(default value) shall be used when the +% relationship is not known +% or cannot be described using one of the other values.\\ +% Other names & Second-class names (see Annex E, +% \enquote{(normative) PDF Name Registry}) should be used to +% represent other types of relationships. +% \end{tabular} +% \end{table} +% \end{enumerate} +% \end{enumerate} +% +% \subsubsection{Task 1: Embedding a file} +% Embedding an existing file is in most cases quite straightforward. This module +% offers commands, but it can also be done with the basic commands +% from the |l3pdf| module \cs{pdf_object_unnamed_write:nn} or +% \cs{pdf_object_new:nn}/\cs{pdf_object_write:nn} or primitive commands +% to create objects. +% The object number should be stored for the reference +% in the |/FileSpec| dictionary. +% +% \begin{verbatim} +% \pdf_object_unnamed_write:nx {fstream} +% { +% { +% /Type /EmbeddedFile +% /Subtype /application\c_hash_str2Fpostscript +% /Params +% << +% /ModDate ~ (\file_timestamp:n{example-image.eps}) +% /Size ~ \file_size:n {example-image.eps} +% /CheckSum ~ (\file_mdfive_hash:n {example-image.eps}) +% >> +% } +% {example-image.eps} +% } +% \tl_set:Nx \l_my_fileobj_tl {\pdf_object_ref_last:} +% \end{verbatim} +% +% \begin{itemize} +% \item The |/Params| dictionary is not always required, but the commands of +% these module will prefill them as shown in the examples. A |/CreationDate| entry +% has to be added explicitly as there is no sensible way +% to retrieve this automatically. +% \item The mimetype (in the |/Subtype|) should be properly escaped. +% This module contains a property list with maps a number of file extensions +% to mimetypes and the commands try to detect and fill the mimetype automatically. +% \item The dictionary can contain additional keys (|/Filter|, |/DecodeParms|), +% see the pdf reference. +% \end{itemize} +% +% \subsubsection{Task 2: Creating the \texttt{/FileSpec} dictionary} +% The |/FileSpec| dictionary is a simple dictionary object, and can also +% be created in various ways. If it refers to an embedded file it should +% reference it in the |/EF| key. +% +% \subsubsection{Task 3: Referencing the \texttt{/FileSpec} dictionary} +% +% Using the dictionary reference in annotations and |/AF| keys is unproblematic. +% +% \potentialclash +% But to add it to the |/EmbeddedFiles| name tree so that it appears in the +% attachment panel requires special care: +% This name tree is a global resource and uncoordinated access can lead to +% clashes and files that are not visible or inaccessible. +% The access here is managed by the \pkg{l3pdfmanagement} module:\\[\smallskipamount]% +% |\pdfmanagement_add:nnx{Catalog/Names}{EmbeddedFiles}{|\meta{objref}|}| +% +% \subsection{Commands and tools of these module} +% \begin{function}{file, file/Params, file/streamParams,file/FileSpec} +% The module predefines and uses a number of local dictionaries for the +% components of the stream and the |/FileSpec| object. These dictionaries are +% then used by the \cs{pdffile_embed_XX}. +% The content of these dictionaries can be changed by users with the commands +% from the \pkg{l3pdfdict} module, but it should be done only locally +% to avoid side effects on uses by other packages/commands. + +% The preset values are of these dictionaries are shown in table~\ref{tab:filedict}. +% \end{function} +% \begin{table} +% \caption{Preset values in the file dictionaries\label{tab:filedict}} +% \begin{tabular}{lll} +% dictionary & key & value \\\hline +% l\_pdffile & Type & /EmbeddedFile\\ +% l\_pdffile/Params& Size & |\file_size:n{\l_pdffile_source_name_str}|\\ +% l\_pdffile/Params& ModDate & |(\file_timestamp:n {\l_pdffile_source_name_str})|\\ +% l\_pdffile/Params& CheckSum & |(\file_mdfive_hash:n{\l_pdffile_source_name_str})|\\ +% l\_pdffile/streamParams& & a /ModDate entry with year/month/date (used with \cs{pdffile_embed_stream:nnn})\\ +% l\_pdffile/FileSpec & Type & /FileSpec\\ +% l\_pdffile/FileSpec & AFRelationship &Unspecified +% \end{tabular} +% \end{table} +% \begin{function}{\pdffile_embed_file:nnn} +% \begin{syntax} +% \cs{pdffile_embed_file:nnn} \Arg{source filename} \Arg{target filename} \Arg{object name } +% \end{syntax} +% This commands embeds the file \meta{source filename} in the PDF, +% and creates a |/FileSpec| dictionary object named \meta{object name}. +% The object name must be unique. +% The command uses the content of the local +% dictionaries |l_pdffile|, |l_pdffile/Params| and |l_pdffile/FileSpec| +% to setup the dictionary entries of the stream object and the +% |/FileSpec| dictionary. The |/F| and |/UF| entry are filled +% with \meta{target filename}. +% +% It is an error if both \meta{target filename} and \meta{source filename} +% are empty. +% +% If \meta{target filename} is empty \meta{source filename} is used instead. +% +% If \meta{source filename} is empty, only a |/FileSpec| dictionary is +% created. +% +% If the |l_pdffile| dictionary doesn't contain a +% Subtype entry with the mimetype, the command tries to guess it +% from the file extension of \meta{source filename}. Unknown file extensions can be +% added (or known extension be changed) by adding to or changing the value in +% the property \cs{g_pdffile_mimetypes_prop}, see below. +% +% When using \texttt{dvips} and \texttt{pstopdf} the actual embedding is +% done by \texttt{pstopdf}. \texttt{pstopdf} will embed files +% only if used with the option \texttt{-dNOSAFER} and will not be able +% to use files which are found with \texttt{kpathsea}. +% +% \meta{target filename} doesn't need to be a file name with an extension, +% but it is recommended as security settings in the pdf +% viewer can restrict access to known file types. +% \end{function} +% +% \begin{NOTE}{UF} +% we should perhaps also consider the chunk method see pdfbase and +% https://chat.stackexchange.com/transcript/message/54181193#54181193 +% \end{NOTE} +% +% \begin{function}{\pdffile_embed_stream:nnn} +% \begin{syntax} +% \cs{pdffile_embed_stream:nnn} \Arg{content} \Arg{target filename} \Arg{object name } +% \end{syntax} +% This commands embeds the \meta{content} in the PDF in a stream objects and +% creates a |/FileSpec| dictionary object named \meta{object name}. +% \meta{content} is wrapped in a \cs{exp_not:n}. +% The object name must be unique. The command uses the content of the local +% dictionaries |l_pdffile|, |l_pdffile/streamParams| and |l_pdffile/FileSpec| +% to setup the dictionary entries of the stream object and the /FileSpec dictionary. +% The /F and /UF entry are filled with \meta{target filename}. +% If \meta{target filename} is empty the fix name \texttt{stream.txt} +% is used instead. +% +% If the |l_pdffile| dictionary doesn't contain a +% Subtype entry with the mimetype, the command tries to guess it +% from the file extension of \meta{target filename}. +% +% \meta{target filename} doesn't need to be a file name with an extension, +% but it is recommended as security settings in the pdf +% viewer can restrict access to known file types. +% +% The stream should not be too long, at least PS imposes a size limit for strings. +% \end{function} +% +% \begin{variable}{\g_pdffile_mimetypes_prop} +% This property contains a list of extensions and their mimetypes. +% Values can be added or changed with the standard commands: +% +% |\prop_gput:Nnn \g_pdffile_mimetypes_prop {.abc}{text/plain}| +% +% The extension should start with a period, the mimetype should be given +% as plain text (it will be escaped internally). Extensions with two periods +% are not supported. +% \end{variable} +% +% \begin{variable}{\l_pdffile_source_name_str} +% This variable is set at the begin of \cs{pdffile_embed_file:nnn}. It can be +% (and is) used in the file dictionaries, see table~\ref{tab:filedict} for examples. +% \end{variable} +% +% \begin{variable} {\g_pdffile_embed_prop} +% This property holds a list of embedded files. It is used by the following +% show command. The keys are the object names, the argument holds a key word, +% the source file name and the target file name. +% \end{variable} +% +% \begin{function}{\pdffile_embed_show:} +% This shows the embedded files with their source and target name. +% \end{function} +% +% \subsection{Example} +% \begin{verbatim} +% \group_begin: +% %set the relationship: +% \pdfdict_put:nnn {l_pdffile/FileSpec} {AFRelationship}{/Source} +% %set the description key. The text must first be converted: +% \pdf_string_from_unicode:nnN {utf16/string} +% {this~is~an~odd~description~with~öäü} +% \l_tmpa_str +% \pdfdict_put:nnx {l_pdffile/FileSpec} {Desc}{\l_tmpa_str} +% %embeds testinput.txt and calls it grüße.txt +% \pdffile_embed_file:nnn {testinput.txt}{grüße.txt}{file:example1} +% %reference it in the panel +% \pdfmanagement_add:nnx +% {Catalog/Names} +% {EmbeddedFiles} +% {\pdf_object_ref:n{file:example1}} +% \group_end: +% \end{verbatim} +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{l3pdffile} implementation} +% +% \begin{macrocode} +%<*header> +\ProvidesExplPackage {l3pdffile} {2021-02-22} {0.95a} + {embedding and referencing files in PDF---LaTeX PDF management testphase bundle} +\RequirePackage{l3pdftools} %temporarly!! +%</header> +% \end{macrocode} +% +% \begin{macrocode} +%<*package> +%<@@=pdffile> +\cs_new_protected:Npn \@@_filename_convert_to_print:nN #1 #2 + {\pdf_string_from_unicode:nnN {utf16/hex}{#1}{#2}} +% \end{macrocode} +% \subsection{Messages} +% \begin{macrocode} +\msg_new:nnn { pdffile } { file-not-found } + { + File~'\tl_to_str:n{#1}'~not~found + } + +\msg_new:nnn { pdffile } { mimetype-missing } + { + Mime~type~not~set~for~file~'\tl_to_str:n{#1}' + } + +\msg_new:nnn { pdffile } { target-name-missing } + { + a~target~name~for~the~/FileSpec~dictionary~is~missing. + } + +\msg_new:nnn { pdffile } { object-exists } + { + object~name~'#1'~is~already~used. + } + +\msg_new:nnn { pdffile } { show-files } + { + The~following~files~have~been~embedded\\ + #1 + } +% \end{macrocode} +% \begin{variable} +% { +% \l_@@_tmpa_tl, +% \l_@@_tmpb_tl, +% \l_@@_tmpa_str, +% \l_@@_tmpb_str, +% \l_@@_ext_str, +% \l_@@_automimetype_tl, +% \l_@@_embed_ref_tl +% } +% temporary variables: generic, for extension, subtype, to store the ref. +% \end{variable} +% \begin{macrocode} +\tl_new:N \l_@@_tmpa_tl +\tl_new:N \l_@@_tmpb_tl +\str_new:N \l_@@_tmpa_str +\str_new:N \l_@@_tmpb_str +\str_new:N \l_@@_ext_str +\tl_new:N \l_@@_automimetype_tl +\tl_new:N \l_@@_embed_ref_tl +% \end{macrocode} +% \begin{variable} {\g_pdffile_mimetypes_prop} +% This variable holds common mimetypes. The key is an extension with (one) period, +% the value the description, e.g. \texttt{text/csv}. +% \end{variable} +% \begin{macrocode} +\prop_new:N \g_pdffile_mimetypes_prop +\prop_set_from_keyval:Nn \g_pdffile_mimetypes_prop + { + ,.csv = text/csv + ,.html= text/html + ,.dtx = text/plain %or application/x-tex, not in iana.org list + ,.eps = application/postscript + ,.jpg = image/jpeg + ,.mp4 = video/mp4 + ,.pdf = application/pdf + ,.png = image/png + ,.tex = text/plain %or application/x-tex, not in iana.org list + ,.txt = text/plain + ,.sty = text/plain + } +% \end{macrocode} +% \begin{variable} +% { +% \l_pdffile_source_name_str +% } +% \cs{l_pdffile_source_name_str} will be set at the begin of the command and +% contains the full file name and can be used e.g. with \cs{file_timestamp:n}. +% \end{variable} +% \begin{macrocode} +\str_new:N \l_pdffile_source_name_str +% \end{macrocode} +% Here we define and setup the local dictionaries. +% We add a ModDate to ensure that there is an entry if +% associated files are used. +% \begin{macrocode} +\pdfdict_new:n { l_pdffile } +\pdfdict_put:nnn { l_pdffile }{Type}{/EmbeddedFile} +\pdfdict_new:n { l_pdffile/Params } +\pdfdict_put:nnn { l_pdffile/Params } + {ModDate} { (\file_timestamp:n { \l_pdffile_source_name_str }) } +\pdfdict_put:nnn { l_pdffile/Params } + {Size} { \file_size:n { \l_pdffile_source_name_str } } +\pdfdict_put:nnn { l_pdffile/Params } + {CheckSum} { (\file_mdfive_hash:n { \l_pdffile_source_name_str }) } +\pdfdict_new:n { l_pdffile/streamParams } +\pdfdict_put:nnn { l_pdffile/streamParams } + {ModDate} { + ( + D:\int_use:N\c_sys_year_int + \int_compare:nNnT{\c_sys_month_int}<{10}{0} + \int_use:N\c_sys_month_int + \int_compare:nNnT{\c_sys_day_int}<{10}{0} + \int_use:N\c_sys_day_int + ) + } +\pdfdict_new:n { l_pdffile/FileSpec } +\pdfdict_put:nnn { l_pdffile/FileSpec } + {Type} { /FileSpec } +\pdfdict_put:nnn { l_pdffile/FileSpec } + {AFRelationship} { /Unspecified } + +% \end{macrocode} +% \begin{variable}{\g_pdffile_embed_prop} +% we record here the relation\\% +% \meta{object name} $\Rightarrow$ +% \Arg{file/stream or empty}\Arg{sourcename}\Arg{targetname} +% +% \begin{macrocode} +\prop_new:N \g_pdffile_embed_prop +% \end{macrocode} +% \end{variable} +% \begin{macro}{\pdffile_embed_show:} +% \begin{macrocode} +\cs_new_protected:Npn \pdffile_embed_show: + { + \msg_show:nnx + {pdffile}{show-files} + { + \prop_map_function:NN {\g_pdffile_embed_prop} \msg_show_item:nn + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\pdffile_embed_file:nnn, \pdffile_embed_stream:nnn} +% At first a command to set the mimetype. It either uses the current value +% in the file dictionary, or tries to guess it from the extension. +% \begin{macro}{\@@_mimetype_set:nN,\@@_mimetype_set:VN} +% \begin{macro}{\@@_fstream_write:nN, \@@_fstream_write:VN} +% \begin{macro}{\@@_stream_write:nN, \@@_stream_write:VN} +% \begin{macrocode} +%#1 file name, +%#2 tl to return the (printed) value for the guessed mimetype +\cs_new_protected:Npn \@@_mimetype_set:nN #1 #2 + { + \file_parse_full_name:nNNN + {#1} + \l_@@_tmpa_str %unused + \l_@@_tmpb_str %unused + \l_@@_ext_str + %check if Subtype has been set + \pdfdict_get:nnN { l_pdffile}{Subtype}\l_@@_tmpa_tl + %if not look up in the prop: + \quark_if_no_value:NT \l_@@_tmpa_tl + { + \prop_get:NVNTF + \g_pdffile_mimetypes_prop + \l_@@_ext_str + \l_@@_tmpb_tl + { + \tl_set:Nx #2 {/Subtype~\pdf_name_from_unicode_e:V \l_@@_tmpb_tl} + } + { + \msg_warning:nnx { pdffile }{ mimetype-missing} {#1} + \tl_clear:N #2 + } + } + } + +\cs_generate_variant:Nn \@@_mimetype_set:nN {VN} + +%#1 file name, +%#2 tl, should be empty or contain /Subtype /mimetype +% e.g. result from \@@_mimetype_set:NN +\cs_new_protected:Npn \@@_fstream_write:nN #1 #2 + { + \pdf_object_unnamed_write:nx { fstream } + { + { + #2 + \pdfdict_use:n { l_pdffile} + \pdfdict_if_empty:nF { l_pdffile/Params} + { + /Params + << + \pdfdict_use:n { l_pdffile/Params} + >> + } + } + { #1 } + } + \tl_clear:N \l_@@_automimetype_tl + } + +\cs_generate_variant:Nn \@@_fstream_write:nN {VN} + +%#1 file content +%#2 tl, should be empty or contain /Subtype /mimtype +% e.g. result from \@@_mimetype_set:NN +\cs_new_protected:Npn \@@_stream_write:nN #1 #2 + { + \pdf_object_unnamed_write:nx { stream } + { + { + #2 + \pdfdict_use:n { l_pdffile} + \pdfdict_if_empty:nF { l_pdffile/streamParams} + { + /Params + << + \pdfdict_use:n { l_pdffile/streamParams} + >> + } + } + { \exp_not:n { #1 } } + } + \tl_clear:N \l_@@_automimetype_tl + } + +\cs_generate_variant:Nn \@@_stream_write:nN {VN} + +%#1 symbolic name of dict object +%#2 target file name, +%#3 object ref of the file stream. +\cs_new_protected:Npn \@@_filespec_write:nnn #1 #2 #3 + { + \tl_if_blank:nT { #2 } + { + \msg_error:nn {pdffile}{target-name-missing} + } + { + \pdf_object_new:nn { #1 } {dict} + \group_begin: + \@@_filename_convert_to_print:nN { #2 } \l_@@_tmpa_str + \pdfdict_put:nnx {l_pdffile/FileSpec}{F} { \l_@@_tmpa_str } + \pdfdict_put:nnx {l_pdffile/FileSpec}{UF}{ \l_@@_tmpa_str } + \pdf_object_write:nx { #1 } + { + \pdfdict_use:n { l_pdffile/FileSpec} + \tl_if_empty:nF { #3 } + { + /EF <</F~#3 /UF~#3>> + } + } + \group_end: + } + } + + +%#1 {source filename} +%#2 {target filename} +%#3 { filespec object name } (will internally get a prefix!) +\cs_new_protected:Npn \pdffile_embed_file:nnn #1 #2 #3 + { % if #1 empty => only filespec + % if #2 empty => = #1 + \pdf_object_if_exist:nTF { #3 } + { + \msg_error:nnn { pdffile }{ object-exists } { #3 } + } + { + \tl_if_blank:nTF { #1 } + { + \tl_set:Nn \l_@@_embed_ref_tl {} + } + { + \file_get_full_name:nNTF {#1} \l_pdffile_source_name_str + { + \@@_mimetype_set:VN + \l_pdffile_source_name_str + \l_@@_automimetype_tl + \@@_fstream_write:VN + \l_pdffile_source_name_str + \l_@@_automimetype_tl + \tl_set:Nx \l_@@_embed_ref_tl { \pdf_object_ref_last: } + } + { + \msg_error:nnn { pdffile }{ file-not-found }{ #1 } + } + + } + \prop_gput:Nnx + \g_pdffile_embed_prop + { #3 } + { + { \tl_if_blank:nTF { #1 } {filespec}{file} } + {\l_pdffile_source_name_str} + { + \tl_if_blank:nTF { #2 } + { \l_pdffile_source_name_str } + { \tl_to_str:n{#2}} + } + } + \tl_if_blank:nTF { #2 } + { + \exp_args:Nnnx + \@@_filespec_write:nnn + %#1 dict, #2 target file name, #3 object ref + { #3 } + { #1 } + {\l_@@_embed_ref_tl} + } + { + \exp_args:Nnnx + \@@_filespec_write:nnn + %#1 dict, #2 target file name, #3 object ref + { #3 } + { #2 } + {\l_@@_embed_ref_tl} + } + } + } + + +%#1{stream content} +%#2{target filename} +%#3{file object name } +\cs_new_protected:Npn \pdffile_embed_stream:nnn #1 #2 #3 + { + % if #2 empty => error + \pdf_object_if_exist:nTF { #3 } + { + \msg_error:nnn { pdffile }{ object-exists } { #3 } + } + { + \prop_gput:Nnx + \g_pdffile_embed_prop + { #3 } + {{stream}{}{\tl_if_blank:nTF {#2}{stream.txt}{\exp_not:n{#2}}}} + \tl_if_blank:nTF {#2} + { \@@_mimetype_set:nN {stream.txt}\l_@@_automimetype_tl} + { \@@_mimetype_set:nN { #2 } \l_@@_automimetype_tl } + \@@_stream_write:nN + { #1 } + \l_@@_automimetype_tl + \tl_set:Nx \l_@@_embed_ref_tl { \pdf_object_ref_last: } + \exp_args:Nnxx + \@@_filespec_write:nnn + %#1 dict, #2 target file name, #3 object ref + { #3 } + { \tl_if_blank:nTF {#2}{stream.txt}{\exp_not:n{#2}} } + {\l_@@_embed_ref_tl} + } + } + + +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \begin{macrocode} +%</package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex 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 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 diff --git a/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdftools.dtx b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdftools.dtx new file mode 100644 index 00000000000..481ddce8724 --- /dev/null +++ b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdftools.dtx @@ -0,0 +1,427 @@ +% \iffalse meta-comment +% +%% File: l3pdftools.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=l3pdftools (LaTeX PDF management testphase bundle)} + +\providecommand\potentialclash{\noindent\llap{\dbend\ }} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% \title{^^A +% The \pkg{l3pdftools} module\\ temporary collection of pdf related commands ^^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{l3pdftools} documentation} +% +% This module collects a number of candidate commands for the l3pdf module +% +% \begin{function}[EXP,added=2021-02-14] +% {\pdf_name_from_unicode_e:n} +% \begin{syntax} +% \cs{pdf_name_from_unicode_e:n} \Arg{content} +% \end{syntax} +% This converts \meta{content} to a format suitable for a PDF Name. +% It will first expand the content with \cs{text_expand:n} and +% then escape it in the way needed in a PDF Name with +% \cs{str_convert_pdfname:e}, and at last prepend a slash before. +% Typically such names use only ascii, +% but non-ascii is supported, but should be utf8 encoded. For example\\ +% |\pdf_name_from_unicode_e:n {A~B\c_percent_str C\c_hash_str D€}}|\\ +% will output |/A#20B#25C#23D#E2#82#AC|. +% \end{function} +% \begin{function}[added=2020-07-04] +% {\pdf_string_from_unicode:nnN} +% \begin{syntax} +% \cs{pdf_string_from_unicode:nnN} \Arg{format} \Arg{content} \Arg{tlvar} +% \end{syntax} +% This converts \meta{content} following the rules defined by \meta{format} and stores +% the result in \meta{tlvar}. The assignment is done locally. +% Non-ascii input should be utf8 encoded. +% Currently the following formats exist: +% \begin{description} +% \item[utf8/string-raw] +% this converts with \cs{str_set_convert:Nnnn} into utf8/string. +% \item[utf8/string] +% this converts into utf8/string and adds parentheses around the result.% +% \item[utf8/URI-raw] +% this converts with \cs{str_set_convert:Nnnn} into utf8/url and +% then replaces reserved and digits back from the percent encoding. Parentheses +% are escaped. +% \item[utf8/URI] +% this converts into utf8/URI and adds parentheses around the result.% +% \item[utf16/string-raw] +% this converts with \cs{str_set_convert:Nnnn} into utf16/string. +% \item[utf16/string] +% this converts into utf16/string and adds parentheses around the result. +% \item[utf16/hex-raw] +% this converts into utf16/hex +% \item[utf16/hex] +% this converts into utf16/hex and adds bracket around the result. +% \end{description} +% \end{function} +% +% \subsection{BDC operator / Properties resource} +% \begin{NOTE}{UF} +% we need a switch for the case that the resource should be added to +% xform resource instead of a page resources, see pdfbase.sty +% - xdvipdfmx: looks fine, the resource is added to the xform resource automatically +% - pdftex should now work okay too +% \end{NOTE} +% Entries to the /Properties dictionary in the page resources can +% be added with dvips only through side-effects: if a BDC-mark is created +% dvips/ghostscript will automatically create the necessary objects and names. +% To get a sensible abstraction the code does the same for the other backends if the +% core management code has been activated. This means that the behaviour +% of the command is different then. The \cs{pdf_bdcobject:..} should only be used +% if the management is active. +% \begin{function}[updated = 2020-07-03] +% { +% \pdf_bdc:nn +% } +% \begin{syntax} +% \cs{pdf_bdc:nn} \Arg{tag} \Arg{dictionary content} +% \end{syntax} +% This command adds a BDC marked content operator to the current page stream. +% \meta{tag} is the tag of this operator (without the leading slash), +% \meta{dictionary content} is the content of the second argument. +% If the PDF resource management is active an dictionary object with the +% content is created and referenced with a name in the BDC operator. +% Without the resource management the content is used directly. It then +% depends on the backend how it is handled: with dvips a name is used +% while the pdfmode engines and dvipdfmx write the content into the stream. +% +% \end{function} +% \begin{function}[added = 2020-07-03] +% { +% \pdf_bdcobject:nn +% } +% \begin{syntax} +% \cs{pdf_bdcobject:nn} \Arg{tag} \Arg{object name} +% \end{syntax} +% This command adds a BDC marked content operator to the current page stream. +% \meta{tag} is the tag of this operator (without the leading slash), +% \meta{object name} is a the name of an dictionary object reserved with +% \cs{pdf_object_new:nn} and filled with \cs{pdf_object_write:n} with +% the properties of the BDC. Reusing a predefined object can save space +% but the command works correctly +% only if the resources management has been activated and should be used only +% if this can be ensured. +% \end{function} +% \begin{function}[updated = 2020-07-03] +% { +% \pdf_bdcobject:n +% } +% \begin{syntax} +% \cs{pdf_bdcobject:n} \Arg{tag} +% \end{syntax} +% This command adds a BDC marked content operator to the current page stream. +% \meta{tag} is the tag of this operator (without the leading slash). +% As object this commands uses the last anonymous dictionary object created with +% \cs{pdf_object_now:nn}. It lies in the responsibility of the user that the last +% object is the wanted one. Like with \cs{pdf_bdcobject:nn} the command works correctly +% only if the resources management has been activated and should be used only +% if this can be ensured. +% \end{function} +% \begin{function}[added = 2019-10-17] +% { +% \pdf_bmc:n +% } +% \begin{syntax} +% \cs{pdf_bmc:n} \Arg{tag} +% \end{syntax} +% This command created a BMC marked content operator. The argument is the +% tag without the leading slash. It can be e.g. used for simple artifact +% markers. +% \end{function} +% \begin{function}[added = 2019-06-30] +% { +% \pdf_emc: +% } +% \begin{syntax} +% \cs{pdf_emc:} +% \end{syntax} +% This command closes the BDC marked content operator opened with \cs{pdf_bdc:nn}. +% It should be on the same page as the bdc-command. +% +% \begin{verbatim} +% \pdf_object_new:nn {objA}{dict} +% \pdf_object_write:nn {objA}{/Type/Artifact} +% \pdf_bdc:nn {Span}{objA} +% text +% \pdf_emc: +% \end{verbatim} +% \end{function} +% +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{l3pdftools} implementation} +% +% \begin{macrocode} +%<*header> +\ProvidesExplPackage {l3pdftools} {2021-02-22} {0.95a} + {candidate commands for l3pdf---LaTeX PDF management testphase bundle} +%</header> +% \end{macrocode} +% +% \begin{macrocode} +%<@@=pdf> +%<*package> +% \end{macrocode} +% \subsection{Conversions and export functions} +% \begin{macro}{\pdf_name_from_unicode_e:n,\pdf_name_from_unicode_e:V} +% \begin{macrocode} +\cs_generate_variant:Nn \str_convert_pdfname:n { e } + +\cs_new:Npn \pdf_name_from_unicode_e:n #1 + { + / \str_convert_pdfname:e { \text_expand:n { #1 } } + } + +\cs_generate_variant:Nn \pdf_name_from_unicode_e:n {V} +% \end{macrocode} +% \end{macro} +% +% The convert command must use a different value the source encoding +% depending on the engines. Until the PR in str-convert is active we add the alias here +% too +% \begin{macrocode} +\bool_lazy_any:nTF + { + \sys_if_engine_luatex_p: + \sys_if_engine_xetex_p: + } + { + \prop_gput:Nnn \g__str_alias_prop { default } { } + } + { + \prop_gput:Nnn \g__str_alias_prop { default } { utf8 } + } +% \end{macrocode} +% \begin{macro}{\pdf_string_from_unicode:nnN} +% \begin{macrocode} +\cs_new:Npn \pdf_string_from_unicode:nnN #1 #2 #3 + { + \cs_if_exist_use:cF { @@_string_from_unicode_#1:nN } + { + \__kernel_msg_error:nnn { pdf } { unknown-convert } {#1} + \use_none:nn + } + { #2 } #3 + } + +\cs_generate_variant:Nn \pdf_string_from_unicode:nnN {nVN} +% \end{macrocode} +% \end{macro} +% Most converter are simply wrapper around the str-convert commands and so +% use the same names, with the addition raw if no delimiters are added. +% The exception is the one for url's: it reverts most of the percent encodings +% and escapes the parentheses. +% That's why its name is URI instead of url. The current code is probably quite +% slow and will need a replacement. +% \begin{macro}{ @@_string_from_unicode_utf8/string-raw:nN } +% \begin{macro}{ @@_string_from_unicode_utf8/string:nN } +% \begin{macro}{ @@_string_from_unicode_utf8/URI-raw:nN } +% \begin{macro}{ @@_string_from_unicode_utf8/URI:nN } +% \begin{macro}{ @@_string_from_unicode_utf16/string-raw:nN } +% \begin{macro}{ @@_string_from_unicode_utf16/string:nN } +% \begin{macro}{ @@_string_from_unicode_utf16/hex-raw:nN } +% \begin{macro}{ @@_string_from_unicode_utf16/hex:nN } +% \begin{macrocode} +%% TODO Names need a review when it is clear which converters +%% are actually needed +%% string conversions and printing +%% we assume here that the text purify step has been done. The input is +%% a list of (utf8) chars. +%% str convert, not expandable. +% filespec (attachment view) tests: +% utf8: gr\303\274\303\237e.txt +% %doesn't work, umlaut wrong, +% utf8 with BOM \357\273\277gr\303\274\303\237e.txt +% %doesn't work, umlaut wrong, bom visible +% utf16 with BE: (FEFF) +% \376\377\000g\000r\000\374\000\337\000e\000.\000t\000x\000t %works +% xetex converts to <feff0067007200fc00df0065002e007400780074> +% utf16 with BE / HEX: <FEFF0067007200FC00DF0065002E007400780074> works + +% bookmarks: as pdfoutline uses () currently only utf16 with BE is usable. +% check if one can use HEX too when directly writing the object +% ========== +% uri: utf16BE/string seems not to work, hex neither +% utf8/string works but not on macos, +% so a specfic utf8/url variant is needed +% ========== +% "input" is utf8 for pdftex, empty (native) for unicode engine +% commands to output literal strings (...) + +\cs_new_protected:cpn { @@_string_from_unicode_utf8/string-raw:nN } #1 #2 + { + \str_set_convert:Nnnn #2 + { #1 } + { default } + {utf8/string} + } + +\cs_new_protected:cpn { @@_string_from_unicode_utf8/string:nN } #1 #2 + { + \use:c { @@_string_from_unicode_utf8/string-raw:nN } { #1 } #2 + \str_put_left:Nn #2 {(} + \str_put_right:Nn #2 {)} + } +% special url command: +\cs_new_protected:cpx { @@_string_from_unicode_utf8/URI-raw:nN } #1 #2 + { + \exp_not:N \str_set_convert:Nnnn #2 + { #1 } + { default } + {utf8/url} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 3A} {:} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 2F} {/} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 23} {\c_hash_str} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 5B} {[} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 5D} {]} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 40} {\c_atsign_str} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 21} {!} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 24} {\c_dollar_str} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 26} {\c_ampersand_str} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 27} {'} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 2A} {*} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 2B} {+} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 2C} {,} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 3B} {;} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 3D} {=} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 30} {0} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 31} {1} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 32} {2} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 33} {3} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 34} {4} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 35} {5} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 36} {6} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 37} {7} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 38} {8} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 39} {9} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 28} {\c_backslash_str(} + \exp_not:N \str_replace_all:Nnn #2 {\c_percent_str 29} {\c_backslash_str)} + } + +\cs_new_protected:cpn { @@_string_from_unicode_utf8/URI:nN } #1 #2 + { + \use:c { @@_string_from_unicode_utf8/URI-raw:nN } {#1} #2 + \str_put_left:Nn #2 {(} + \str_put_right:Nn #2 {)} + } +% with utf16 with BE marker +\cs_new_protected:cpn { @@_string_from_unicode_utf16/string-raw:nN } #1 #2 + { + \str_set_convert:Nnnn #2 + { #1 } + { default } + {utf16/string} + } + +\cs_new_protected:cpn { @@_string_from_unicode_utf16/string:nN } #1 #2 + { + \use:c { @@_string_from_unicode_utf16/string-raw:nN } {#1} #2 + \str_put_left:Nn #2 {(} + \str_put_right:Nn #2 {)} + } + +\cs_new_protected:cpn { @@_string_from_unicode_utf16/hex-raw:nN } #1 #2 + { + \str_set_convert:Nnnn #2 + { #1 } + { default } + {utf16/hex} + } + +\cs_new_protected:cpn { @@_string_from_unicode_utf16/hex:nN } #1 #2 + { + \use:c { @@_string_from_unicode_utf16/hex-raw:nN } {#1} #2 + \str_put_left:Nn #2 {<} + \str_put_right:Nn #2 {>} + } + +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsubsection{BDC operator commands} +% \begin{macro}{\pdf_bdc:nn} +% \begin{macro}{\pdf_bdcobject:nn} +% \begin{macro}{\pdf_bdcobject:n} +% \begin{macro}{\pdf_bmc:n} +% \begin{macro}{\pdf_emc:} +% \begin{macrocode} +\cs_new_protected:Npn \pdf_bdc:nn #1 #2 { \@@_backend_bdc:nn { #1 }{ #2 } } +\cs_new_protected:Npn \pdf_bdcobject:nn #1 #2 { \@@_backend_bdcobject:nn { #1 }{ #2 } } +\cs_new_protected:Npn \pdf_bdcobject:n #1 { \@@_backend_bdcobject:n { #1 } } +\cs_new_protected:Npn \pdf_bmc:n #1 { \@@_backend_bmc:n { #1 } } +\cs_new_protected:Npn \pdf_emc: { \@@_backend_emc: } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \begin{macrocode} +%</package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfxform.dtx b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfxform.dtx new file mode 100644 index 00000000000..ec9afe865db --- /dev/null +++ b/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3pdfxform.dtx @@ -0,0 +1,204 @@ +% \iffalse meta-comment +% +%% File: l3pdfxform.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=l3pdfxform (LaTeX PDF management testphase bundle)} + +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% \title{^^A +% The \pkg{l3pdfxform} module\\ Commands for form XObjects ^^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{l3pdfxform} documentation} +% This module contains a number of commands to create \enquote{form XObjects}, +% for which pdf\TeX{} introduced the short name \enquote{xforms}. +% +% The status is still beta, so the style itself is currently named +% \pkg{l3pdfxform-beta}. Missing is for example code for the dvips route. +% Probably some of the function will also need to be enhanced by hooks. +% +% \begin{function}[added = 2019-08-05,updated=2021-02-14] +% { +% \pdfxform_new:nnn +% } +% \begin{syntax} +% \cs{pdfxform_new:nnn} \Arg{name} \Arg{attributes} \Arg{content} +% \end{syntax} +% This command create a new form XObject that can be used as appearance or +% directly later. +% If the \meta{content} contains BDC-marks it should \emph{not} be given as a +% previously typeset box, but directly so that the names of the +% BDC-marks can be added to the resources of the xform. The xform will automatically +% include the resources of the current page. +% The content will be typeset in a hbox. With pdflatex and luatex +% the surrounding color is \emph{not} stored in the form XObject +% but should be if wanted added e.g. with |\color_select:n{.}|. This keeps +% the option of color injection open. +% \end{function} +% \begin{function}[added = 2019-08-05] +% { +% \pdfxform_use:n +% } +% \begin{syntax} +% \cs{pdfxform_use:n} \Arg{name} +% \end{syntax} +% This command uses (typesets) a previously created form XObject. +% If the surrounding color is different, it is injected in the form XObject with the +% engines pdftex or luatex. +% \end{function} +% \begin{function}[EXP,added = 2019-08-05] +% { +% \pdfxform_ref:n +% } +% \begin{syntax} +% \cs{pdfxform_ref:n} \Arg{name} +% \end{syntax} +% Inserts the appropriate information to reference the xform \meta{name} +% in for example appearance dictionaries. +% \end{function} +% \begin{function}[EXP,added = 2019-08-05] +% { +% \pdfxform_wd:n, \pdfxform_ht:n, \pdfxform_dp:n +% } +% \begin{syntax} +% \cs{pdfxform_wd:n} \Arg{name} +% \end{syntax} +% These command give back the sizes of the form XObject. The values are stored in +% tl-variables with the unit pt and not in dimensions! +% \end{function} +% \begin{function}[EXP,pTF,added = 2020-04-29] +% { +% \pdfxform_if_exist:n +% } +% \begin{syntax} +% \cs{pdfxform_if_exist_p:n} \Arg{name} +% \cs{pdfxform_if_exist:NTF} \meta{name} \Arg{true code} \Arg{false code} +% \end{syntax} +% These command tests if an xform with name \Arg{name} has been already defined. +% \end{function}% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{l3pdfxform} implementation} +% +% \begin{macrocode} +%<@@=pdf> +%<*header> +\ProvidesExplPackage {l3pdfxform-beta} {2021-02-22} {0.95a} + {command to create xforms (beta)---LaTeX PDF management testphase bundle} +%</header> +% \end{macrocode} +% \subsection{Form XObject (pdfxform)} +% \begin{NOTE}{UF} +% - As in dvi mode the xform is immediate, this is done for pdftex/luatex too. +% If needed a delayed version can be added later. +% - the argument for attributes is needed to add e.g. /StructParents +% - it is not clear if an argument for additional resources is needed, probably they +% should / need to be added automatically. +% - code for adding ExtGState etc to the local resource is missing, will be +% added when the object name is clear. +% - should the size be stored in dim or tl? +% - dvips implementation is missing for ideas: pdfbase, atfi-dvips.def, +% \end{NOTE} +% \begin{macro}{\pdfxform_new:nnn} +% \begin{macrocode} +%<*package> +\cs_new_protected:Npn \pdfxform_new:nnn #1 #2 #3 + { + \@@_backend_xform_new:nnnn { #1 } { #2 } { } { #3 } + } +% \end{macrocode} +% \end{macro} +% \begin{macro}{\pdfxform_use:n} +% \begin{macrocode} +\cs_new_protected:Npn \pdfxform_use:n #1 + { + \@@_backend_xform_use:n { #1 } + } +% \end{macrocode} +% \end{macro} +% \begin{macro}{\pdfxform_ref:n,\pdfxform_ref:o} +% \begin{macrocode} +% expansion? +\cs_new:Npn \pdfxform_ref:n #1 + { + \@@_backend_xform_ref:n { #1 } + } + +\cs_generate_variant:Nn \pdfxform_ref:n {o} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\pdfxform_wd:n,\pdfxform_ht:n,\pdfxform_dp:n,} +% \begin{macrocode} +\cs_new:Npn \pdfxform_wd:n #1 + { + \tl_use:c { c_@@_backend_xform_wd_ \tl_to_str:n { #1 } _tl } + } + +\cs_new:Npn \pdfxform_ht:n #1 + { + \tl_use:c { c_@@_backend_xform_ht_ \tl_to_str:n { #1 } _tl } + } + +\cs_new:Npn \pdfxform_dp:n #1 + { + \tl_use:c { c_@@_backend_xform_dp_ \tl_to_str:n { #1 } _tl } + } +% \end{macrocode} +% \end{macro} +% \begin{macrocode} +%</package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/pdfmanagement-testphase/ltdocinit.dtx b/Master/texmf-dist/source/latex/pdfmanagement-testphase/ltdocinit.dtx new file mode 100644 index 00000000000..f37514d936c --- /dev/null +++ b/Master/texmf-dist/source/latex/pdfmanagement-testphase/ltdocinit.dtx @@ -0,0 +1,354 @@ +% \iffalse meta-comment +% +%% File: ltdocinit.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=ltdocinit (LaTeX PDF management testphase bundle)} + +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% \title{^^A +% The \pkg{ltdocinit} module +% } +% +% \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{ltdocinit} documentation} +% This small module defines \cs{DeclareDocumentMetadata} and the related keys. +% It also defines commands to store document properties in a global container. +% +% \subsection{\cs{DeclareDocumentMetadata}} +% +% \begin{function}{\DeclareDocumentMetadata} +% \begin{syntax} +% \cs{DeclareDocumentMetadata}\Arg{key-value list} +% \end{syntax} +% \end{function} +% +% Currently there is no dedicated location to declare settings concerning +% a document as a whole. Settings are placed somewhere in the preamble or +% with the class options or even with some package options. +% For some settings this can be too late, +% for example the pdf version can no longer be changed if a +% package has used code which already opened the PDF. +% +% \cs{DeclareDocumentMetadata} as a new command +% should unify such settings in one place. +% It should be used before \cs{documentclass}, directly after +% loading \pkg{pdfmanagement-testphase}. \cs{DeclareDocumentMetadata} +% can be used more than once. +% +% The keys defined for \cs{DeclareDocumentMetadata} +% currently allows to set the PDF version, to set the PDF \texttt{/Lang}, +% to uncompress a pdf, to set the language and to declare a few PDF standards +% and some colorprofiles. +% +% \cs{DeclareDocumentMetadata} is also used to +% activate the new PDF management code and it loads +% a number of required files for the PDF management code. +% As this forces the loading of the backend files, a backend +% which can't be detected automatically like |dvipdfmx|, +% must be set in the first \cs{DeclareDocumentMetadata}. +% +% Currently the following keys are implemented +% +% \begin{description} +% \item[\texttt{backend}] passes the backend name to expl3. +% This will probably be extended to pass the value also to packages. +% \item[\texttt{pdfversion}] e.g. \texttt{pdfversion=1.7} +% \item[\texttt{uncompress}] no value. Forces an uncompressed pdf. +% \item[\texttt{lang}] to set the Lang entry in the Catalog. +% E.g. \texttt{lang=de-DE}. The initial value is |en-US| +% \item[\texttt{pdfstandard}] Choice key to set the pdf standard. +% Currently |A-1b|, |A-2b| and |A-3b| are accepted as +% values. The underlying code to ensure the requirements (as far as they +% can be ensured) is incomplete, but a color profile is included and the +% /OutputIntent is set. More information can be found in the documentation +% of \pkg{l3pdfmeta}. +% \item[\texttt{colorprofiles}] This allows to load icc-colorprofiles. Details +% are described in the documentation of \pkg{l3pdfmeta}. +% \item[\texttt{pdfmanagement}] Boolean. This activates/deactivates +% the core management code. By default the value is true. +% \item[firstaidoff] This accepts a comma lists of keysword and disable the patches +% related to them. More information can be found in the documentation of +% \pkg{pdfmanagement-firstaid}. +% \end{description} +% +% \subsection{Container for document properties} +% +% The module provides a container where classes, packages and users can store +% properties of the document which are perhaps of interest +% or use for other packages or the author. +% +% The properties are stored with a key |label/property|. The values can be +% retrieved expandably. +% +% \begin{function}{\AddToDocumentProperties} +% \begin{syntax} +% \cs{AddToDocumentProperties}\oarg{label}\Arg{property}\Arg{value} +% \end{syntax} +% This stores \meta{value} under the key \meta{label}/\meta{property}. +% By default \meta{label} is the current package name |\@currname|. If another +% label is choosen, it should be one which avoids clashes with other packages +% using the container. The label |document| is reserved. +% \end{function} +% +% \begin{function}{\GetDocumentProperties} +% \begin{syntax} +% \cs{AddToDocumentProperties}\Arg{label/property} +% \end{syntax} +% Expands to the \meta{value} corresponding to \meta{label/property} +% in the container. If \meta{label/property} is missing, +% this has an empty expansion. +% The result is returned within \cs{exp_not:n}, which means +% that the \meta{value} does not expand further +% when appearing in an x-type argument expansion. +% \end{function} +% +% \begin{function}{\ShowDocumentProperties} +% \begin{syntax} +% \cs{ShowDocumentProperties} +% \end{syntax} +% This show the current content of the container. +% \end{function} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{ltdocinit} implementation} +% \subsection{\cs{DeclareDocumentMetadata}} +% \begin{macrocode} +%<@@=pdfmanagement> +%<*header> +\ProvidesExplPackage {ltdocinit} {2021-02-22} {0.95a} + {Initialize document metadata} +%</header> +% \end{macrocode} +% \cs{DeclareDocumentMetadata} should for now not be used after +% \cs{documentclass} so we error in this case. It can be used more than once +% but follow-up calls should not do the initialization code. +% \begin{macrocode} +%<*package> +\msg_new:nnn { document } { setup-after-documentclass } + { + \token_to_str:N \DeclareDocumentMetadata \c_space_tl + should~be~used~only~before~\token_to_str:N\documentclass + } +% \end{macrocode} +% \begin{macro}{\DeclareDocumentMetadata} +% \begin{macrocode} +\NewDocumentCommand\DeclareDocumentMetadata { m } + { + \cs_if_eq:NNTF \documentclass \@twoclasseserror + { \msg_error:nn { document }{ setup-after-documentclass } } + { +% \end{macrocode} +% The wanted backend must be detected first, we read the init key and then +% force the loading by either loading l3pdf (+ expl3) or only expl3. +% \begin{macrocode} + \keys_set_groups:nnn { document / metadata} {init}{ #1 } + %should be loaded after the backend is set, and only if not in the kernel + \cs_if_free:NTF \pdf_uncompress: + {\RequirePackage{l3pdf}}{\RequirePackage{expl3}} +% \end{macrocode} +% Now we load the extra backend code and set the boolean to true +% \begin{macrocode} + \ExplSyntaxOn\makeatletter + \file_input:n {l3backend-testphase-\c_sys_backend_str.def} + \ExplSyntaxOff\makeatother + \bool_gset_true:N \g_@@_active_bool +% \end{macrocode} +% set the default language, process the rest of the keys, +% and setup the generic driver +% \begin{macrocode} + \keys_set_filter:nnn { document / metadata } { init } { lang=en-US, #1 } + \bool_if:NT \g_@@_active_bool + { + \PassOptionsToPackage{customdriver=hgeneric-testphase}{hyperref} + } +% \end{macrocode} +% \cs{pdfmanagement_add:nnn} has collected values in this hook. +% \begin{macrocode} + \hook_use_once:n {pdfmanagement/add} +% \end{macrocode} +% Redefine \cs{DeclareDocumentMetadata} so that it only process the keys. +% We need to update the hyperref option if the active status changes. +% \begin{macrocode} + \RenewDocumentCommand\DeclareDocumentMetadata { m } + { + \keys_set_filter:nnn { document / metadata } { init } { ##1 } + \bool_if:NTF \g_@@_active_bool + { + \str_remove_all:cn {opt@hyperref.sty}{customdriver=hgeneric-testphase} + \PassOptionsToPackage{customdriver=hgeneric-testphase}{hyperref} + } + { + \str_remove_all:cn {opt@hyperref.sty}{customdriver=hgeneric-testphase} + } + } +% \end{macrocode} +% Load more modules and the firstaid code. +% \begin{macrocode} + \RequirePackage{pdfmanagement-firstaid} + } + } +% \end{macrocode} +% \end{macro} +% \subsection{Container for document Properties} +% The container for the document properties is a prop +% \begin{variable}{\g_@@_documentproperties_prop} +% \begin{macrocode} +\prop_new:N \g_@@_documentproperties_prop % +% \end{macrocode} +% \end{variable} +% \begin{macro}{\AddToDocumentProperties} +% \begin{macrocode} +\NewDocumentCommand\AddToDocumentProperties{O{\@currname}mm} + { + \exp_args:NNx + \prop_gput:Nnn \g_@@_documentproperties_prop + { + \tl_if_blank:eTF {#1}{top-level/}{#1/} #2 + } + { #3} + } +% \end{macrocode} +% \end{macro} +% \begin{macro}{\GetDocumentProperties} +% \begin{macrocode} +\NewExpandableDocumentCommand\GetDocumentProperties{m} + { + \prop_item:Nn \g_@@_documentproperties_prop {#1} + } +% \end{macrocode} +% \end{macro} +% \begin{macro}{\ShowDocumentProperties} +% \begin{macrocode} +\msg_new:nnn { pdfmanagement } { show-properties } + { + The~following~document~properties~have~been~stored: + #1 + } +\NewDocumentCommand\ShowDocumentProperties {} + { + \msg_show:nnx {pdfmanagement}{show-properties} + { + \prop_map_function:NN \g_@@_documentproperties_prop \msg_show_item:nn + } + } +% \end{macrocode} +% \end{macro} +% \subsection{The keys for \cs{DeclareDocumentMetadata}} +% As they use \cs{AddToDocumentProperties} they have to come after +% it has been defined. +% +% \begin{variable}{\g_@@_firstaidoff_clist} +% A list to store the firstaid code which should be disabled +% \begin{macrocode} +\clist_new:N \g_@@_firstaidoff_clist +% \end{macrocode} +% \end{variable} +% \begin{macrocode} +\keys_define:nn { document / metadata } + { + backend .code:n = + { + \PassOptionsToPackage { driver=#1 } {expl3} + \AddToDocumentProperties[document]{backend}{#1} + }, + backend .groups:n = { init } , + } + +\keys_define:nn { document / metadata } + { + ,pdfversion .code:n = + { + \pdf_version_gset:n { #1 } + \AddToDocumentProperties[document]{pdfversion}{#1} + } + ,uncompress .code:n = + { + \pdf_uncompress: + } + ,uncompress .value_forbidden:n = true + ,lang .code:n = + { + \pdfmanagement_add:nnn {Catalog} {Lang}{(#1)} + \AddToDocumentProperties[document]{lang}{#1} + } + %,xmpmeta .bool_gset:N = \g_pdfmeta_xmp_bool %see pdfmeta unused and undefined for now! + % this uses internal command from pdfmeta, it should probably move there ... + ,pdfstandard .code:n = + { + \exp_args:Nnx + \keys_set:nn {document / metadata} {_pdfstandard=\str_uppercase:n{#1}} + } + ,_pdfstandard .choices:nn = + {A-1B,A-2B,A-3B} + { + \prop_if_exist:cT { g__pdfmeta_standard_pdf/#1_prop } + { + \prop_gset_eq:Nc \g__pdfmeta_standard_prop { g__pdfmeta_standard_pdf/#1 _prop } + } + \AddToDocumentProperties [document]{pdfstandard}{#1} + } + ,_pdfstandard / unknown .code:n = + { + \msg_warning:nnn{pdf}{unknown-standard}{#1} + } + ,pdfmanagement .bool_gset:N = \g_@@_active_bool + ,pdfmanagement .initial:n = {true} + ,firstaidoff .clist_gset:N = \g_@@_firstaidoff_clist + } +% \end{macrocode} +% \begin{macrocode} +%</package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/pdfmanagement-testphase/pdfmanagement-firstaid.dtx b/Master/texmf-dist/source/latex/pdfmanagement-testphase/pdfmanagement-firstaid.dtx new file mode 100644 index 00000000000..08fbf856db1 --- /dev/null +++ b/Master/texmf-dist/source/latex/pdfmanagement-testphase/pdfmanagement-firstaid.dtx @@ -0,0 +1,319 @@ +% \iffalse meta-comment +% +%% File: pdfmanagement-firstaid.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=pdfmanagement-firstaid + (LaTeX PDF management testphase bundle)} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% \title{^^A +% The \pkg{pdfmanagement-firstaid} package -- temporary patches and package replacements ^^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{pdfmanagement-firstaid} documentation} +% This code is temporary! It tries to patch commands of other packages or even +% replace package which are incompatible with the pdfmanagement, +% to remove clashes and test if everything works as expected. +% This code should disappear when packages adapt to the central interfaces. +% +% The package contains an number of sections for various packages. Every +% section can be disabled in (the first) \cs{DeclareDocumentMetadata} with +% |firstaidoff={name1,name2,...}|. +% \begin{macrocode} +%<*package> +\ProvidesExplPackage {pdfmanagement-firstaid} {2021-02-22} {0.95a} + {LaTeX PDF management testphase bundle / firstaid-patches} + +%<@@=pdfmanagement> +\clist_map_inline:nn {pgf,transparent,hyperxmp,pdflscape,xcolor,color} + { + \bool_new:c { g_@@_firstaid_#1_bool } + \bool_gset_true:c { g_@@_firstaid_#1_bool } + } +\clist_map_inline:Nn \g_@@_firstaidoff_clist + { + \bool_if_exist:cT { g_@@_firstaid_#1_bool } + { + \bool_gset_false:c { g_@@_firstaid_#1_bool } + } + } +\msg_new:nnn { pdfmanagement } { firstaid } + { loading~pdfmanagement~firstaid~code~for~#1 } +% \end{macrocode} +% \subsection{\pkg{color}} +% +% \pkg{color} is not incompatible, but the new \pkg{hyperref} driver makes use of +% \pkg{l3color} to set the colors. It is therefore necessary to patch some +% internal \pkg{color} commands, so +% that colors defined with its \cs{definecolor} command are known to \pkg{l3color} and +% so \pkg{hyperref}. This only supports the color models from l3color (which covers +% all standard model of the \pkg{color} package). The |named| model is mapped to +% \cs{color_set:nn}. +% +% This patch serves also as test to check if this change can be safely +% added to \pkg{color} later. +% \begin{macrocode} +\@ifundefined{color_set:nn}{ +\RequirePackage{l3color}}{} +\bool_if:NT \g_@@_firstaid_color_bool + { + \declare@file@substitution{color.sty}{color-ltx.sty} + } +% \end{macrocode} +% +% \subsection{\pkg{xcolor}} +% +% \pkg{xcolor} is not incompatible, but the new \pkg{hyperref} driver makes use of +% \pkg{l3color} to set the colors. It is therefore necessary to patch \pkg{xcolor}, so +% that colors defined with its \cs{definecolor} command are known to \pkg{l3color} and +% so \pkg{hyperref}. This only supports the color model from l3color. Colors defined +% with the models |cmy| and |tHsb| are silently ignored. +% +% The |named| model is mapped to \cs{color_set:nn}. +% +% \begin{macrocode} +\@ifundefined{color_set:nn}{ +\RequirePackage{l3color}}{} +\bool_if:NT \g_@@_firstaid_xcolor_bool + { + \AddToHook{package/after/xcolor} + {\RequirePackage{xcolor-patches-tmp-ltx}\XC@@@@names} + } +% \end{macrocode} +% \subsection{\pkg{pgf}} +% +% In \pkg{pgf}, resource management is set up in the file |pgfutil-common.tex|. +% This then provides three functions for adding to the resources, all of which +% are objects: +% \begin{itemize} +% \item \cs{pgfutil@addpdfresource@extgs}: Extended graphics state +% \item \cs{pgfutil@addpdfresource@colorspaces}: Color spaces +% \item \cs{pgfutil@addpdfresource@patterns}: Patterns +% \end{itemize} +% +% These resource dictionaries are used by adding entries in a cumulative sense; +% the macro layer deals with ensuring that each entry is only given once. Note +% that the objects themselves must be given only once for each page. +% +% To support these functions, there are a series of set-up macros which install +% these resources. That has to take place for every page: the exact route +% therefore depends on the driver. +% +% For the pdfmanagement project we need to avoid that pgf interferes in ExtGState, +% ColorSpace and Pattern (Shadings are added to the xform resources and so probably +% unproblematic for now). +% The actual patch is in a file hook guarded by the boolean, +% the rest of the code is always defined. +% \begin{macrocode} + +\bool_if:NT \g_@@_firstaid_pgf_bool + { + \msg_info:nnn{pdfmanagement }{firstaid}{pgf} + \AddToHook{file/after/pgfrcs.sty} + { + \cs_set_eq:NN + \@@_pgfori_pgfutil@setuppdfresources + \pgfutil@setuppdfresources + \def\pgfutil@setuppdfresources + { + \pdfmanagement_if_active:TF + { + \@@_pgf_sys_setuppdfresources_plain: + } + { + \@@_pgfori_pgfutil@setuppdfresources + } + } + } + } +%\def\pgfutil@addpdfresource@extgs#1{\pgf@sys@addpdfresource@extgs@plain{#1}} +%\def\pgfutil@addpdfresource@colorspaces#1{\pgf@sys@addpdfresource@colorspaces@plain{#1}} +%\def\pgfutil@addpdfresource@patterns#1{\pgf@sys@addpdfresource@patterns@plain{#1}} +%\def\pgfutil@setuppdfresources{\pgf@sys@setuppdfresources@plain} +% \pgf@sys@pdf@possible@resources %used in xform +%Trying to patch pgf .. +\cs_new_protected:Npn \@@_pgf_sys_setuppdfresources_plain: + { + %objects are already created ... + \def\pgf@sys@pdf@possible@resources + { + /ColorSpace~\pdf_object_ref:n {Page/Resources/ColorSpace} + /Pattern ~\pdf_object_ref:n {Page/Resources/Pattern} + /ExtGState ~\pdf_object_ref:n {Page/Resources/ExtGState} + } + \let\pgf@sys@pdf@check@resources=\relax% + %not sure if needed, but perhaps the lists are used somewhere else ... + \let\pgf@sys@pgf@resource@list@extgs=\pgfutil@empty% + \let\pgf@sys@pgf@resource@list@patterns=\pgfutil@empty% + \let\pgf@sys@pgf@resource@list@colorspaces=\pgfutil@empty% + % the commands to add page resources + \def\pgf@sys@addpdfresource@extgs@plain##1 + { + \exp_after:wN %for transparent which passes a command + \@@_patch_pgfextgs:w ##1\q_stop + } + \def\pgf@sys@addpdfresource@patterns@plain##1 + { + \@@_patch_pgfpatterns:w ##1\q_stop + } + \def\pgf@sys@addpdfresource@colorspaces@plain##1 + { + \@@_patch_pgfcolorspaces:w ##1\q_stop + } + } + +%\AtEndPreamble{\pgfutil@setuppdfresources} +% helper commands as pgf doesn't pass resources as two arguments +% code to add to the resources existing stuff in the format "/name value": +\cs_new:Npn \@@_split_dict_entry_aux:NNw #1 #2 /#3~#4\q_stop + { + \tl_set:Nn #1 {#3} + \tl_set:Nn #2 {#4} + } + +\cs_new:Npn \@@_patch_pgfextgs:w #1/#2<<#3>>#4\q_stop + { + \exp_args:Nne + \__pdf_backend_PageResources_gput:nnn + {ExtGState}{\tl_trim_spaces:n{#2}}{<<#3>>} + } +\cs_new:Npn \@@_patch_pgfpatterns:w #1/#2\space#3\q_stop + { + \exp_args:Nnxx + \__pdf_backend_PageResources_gput:nnn + {Pattern}{\tl_trim_spaces:n{#2}}{#3} + } +\cs_new:Npn \@@_patch_pgfcolorspaces:w #1/#2[#3]#4\q_stop + { + \exp_args:Nne + \__pdf_backend_PageResources_gput:nnn + {ColorSpace}{\tl_trim_spaces:n{#2}}{[#3]} + } + +% \end{macrocode} +% \subsection{\pkg{transparent}} +% We simply replace by the new version. +% \begin{macrocode} +\bool_if:NT \g_@@_firstaid_transparent_bool + { + \declare@file@substitution{transparent.sty}{transparent-ltx.sty} + } +% \end{macrocode} +% \subsection{\pkg{pdflscape}} +% We simply replace by the new version. +% \begin{macrocode} +\bool_if:NT \g_@@_firstaid_pdflscape_bool + { + \declare@file@substitution{pdflscape.sty}{pdflscape-ltx.sty} + } +% \end{macrocode} +% \subsection{hyperxmp} +% We add some code at the end of hyperxmp.sty. +% \begin{macrocode} +\bool_if:NT \g_@@_firstaid_hyperxmp_bool + { + \AddToHook + {file/after/hyperxmp.sty} + {\RequirePackage{hyperxmp-patches-tmp-ltx}} + } +%</package> +% \end{macrocode} +% \subsection{\pkg{colorspace}} +% This is rather difficult as no real places to inject patches +% at first a try to avoid that its ExtGState is missing: +% it can not be avoided to recreate the objects (and so to get duplicates) +% as colorspace uses temporary macros whose contents is lost. +% \begin{macrocode} +%<*package> +%<@@=pdf> +% this must be earlier, to avoid problems with luatex which has two pageresources +% lua/tex +\hook_gput_code:nnn {begindocument} {pdf} + { + \tl_if_exist:NT \spc@op + { + \def\spc@Pageresources#1{} + } + + } + +\hook_gput_code:nnn {begindocument/end} {pdf} + { + \tl_if_exist:NT \spc@op + { + \@@_backend_object_new:nn {__spc_extgstate_op_false}{dict} + \@@_backend_object_write:nn + {__spc_extgstate_op_false} + {/Type /ExtGState~/op~false~/OP~false} + \pdfmanagement_add:nnn + {Page/Resources/ExtGState} + {SPCko} + {\@@_backend_object_ref:n {__spc_extgstate_op_false}} + \@@_backend_object_new:nn {__spc_extgstate_op_true0}{dict} + \@@_backend_object_write:nn + {__spc_extgstate_op_true0} + {/Type /ExtGState~/op~true~/OP~true~/OPM~0}% + \pdfmanagement_add:nnn + {Page/Resources/ExtGState} + {SPCmz} + {\@@_backend_object_ref:n {__spc_extgstate_op_true0}} + \@@_backend_object_new:nn {__spc_extgstate_op_true1}{dict} + \@@_backend_object_write:nn + {__spc_extgstate_op_true1} + {/Type /ExtGState~/op~true~/OP~true~/OPM~1}% + \pdfmanagement_add:nnn + {Page/Resources/ExtGState} + {SPCop} + {\@@_backend_object_ref:n {__spc_extgstate_op_true1}} + } + } +%</package> +% \end{macrocode}% +% \end{documentation} +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/pdfmanagement-testphase/pdfmanagement-testphase.dtx b/Master/texmf-dist/source/latex/pdfmanagement-testphase/pdfmanagement-testphase.dtx new file mode 100644 index 00000000000..4b63544d179 --- /dev/null +++ b/Master/texmf-dist/source/latex/pdfmanagement-testphase/pdfmanagement-testphase.dtx @@ -0,0 +1,429 @@ +% \iffalse meta-comment +% +%% File: pdfmanagement-testphase.dtx +% +% Copyright (C) 2019-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 +% +% https://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{l3doc} +\usepackage{tabularx,array,booktabs,hyperxmp} +\hypersetup{pdfauthor=The LaTeX Project,pdftitle=LaTeX PDF management testphase bundle} +\newcommand\potentialclash{\noindent\llap{\dbend\ }} +\raggedbottom +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% +% \title{The \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*{Abstract} +% {\em This is a temporary package created to allow the manual +% loading of the new \LaTeX{} PDF management code during a test phase. +% It will disappear when the code is integrated into the \LaTeX{} format.} +% +% \medskip +% \noindent During the test phase the package should be used like this: +% +% \begin{verbatim} +% \RequirePackage{pdfmanagement-testphase} % load the package +% +% \DeclareDocumentMetadata % activate the PDF management (with options) +% { +% % options +% } +% +% \documentclass {...} +% \end{verbatim} +% Note that the activation has to happen before the \cs{documentclass} declaration. +% Because of this, the package needs loading with \cs{RequirePackage}. +% +% +% +% \section*{Feedback wanted!} +% +% Bug reports and feedback are welcome. Please open an issue at +% \url{https://github.com/latex3/pdfresources}. +% +% While the code targets PDF as output format, feedback about the +% effect on other formats is needed too. +% +% \section{Introduction} +% The \LaTeX{} format currently contains nearly no code specific to the now quite +% central output format, PDF. It also offers nearly no interfaces to important PDF related +% primitive commands for package writers. +% +% Important tasks like supporting PDF standards, +% creating links, adding special colors, managing the content of +% central PDF-directories or even simple tasks like setting the PDF version +% are delegated to external packages which have to recourse to +% the primitive low-level commands in their code. +% +% This is problematic for three reasons: +% \begin{itemize} +%\item At first using primitives directly can lead to clashes and duplicate +% settings with conflicting values---nothing prevent packages to add for example +% the \texttt{/Title} twice to the Info dictionary, the \texttt{/Lang} entry +% twice to the Catalog, or to add two \texttt{/ExtGState} +% resources to a page. The PDF normally doesn't break in such +% cases---the format is quite robust---but it will ignore one of the duplicates and +% the output can be wrong. +% +% \item At second the primitives differ between the various engines and backends with +% which \LaTeX{} is used. To support the engines and backend +% packages have to write and maintain +% \enquote{driver} files which they did to a varying degree. This makes it +% difficult for users to assess if a package will work with their work-flow and +% is a strain for package writers as they have to keep track of engine and backend changes. +% +% \item And at last generic hooks and configuration points to various +% PDF related structures are missing and difficult to add. +% \end{itemize} +% +% Despite the potential problems, until now the number of conflicts were +% small and could be resolved in an ad-hoc fashion. But the future plans for +% \LaTeX{} regarding support for tagged PDF and +% PDF standards mean that much more PDF specific code will have to be +% written by the kernel directly and this can not be done without proper, +% well-defined and well-behaving interfaces and hooks. +% +% Some first steps for better support of PDF related commands have been already done +% with the \pkg{l3pdf} package which has now been integrated into \pkg{l3kernel}. +% It offers backend independent commands to create +% PDF objects and destination, to set the compress level and the PDF version. +% +% The PDF management bundle extends this to more PDF related areas +% and provides interfaces to them in a backend independent way. +% +% The new PDF management has three main objectives +% connected with the problems identified above: +% \begin{itemize} +% \item For commands with \enquote{clash potential} it implements commands to +% replace the primitives and so to resolve potential conflicts. +% +% \item It implements commands for a variety of PDF related tasks +% and supports a well-defined set of backends. +% +% \item If sensible this commands are enhanced by hooks from the new +% \LaTeX{} hook system. This has been e.g.\ done for annotations in the +% \pkg{l3pdfannot} bundle. +% \end{itemize} +% +% \section{\enquote{Change Strategy}: The integration into \LaTeX\label{sec:change}} +% +% The central module of this bundle, \pkg{l3pdfmanagement}, defines an interface +% for the (pdf\TeX) primitives \cs{pdfcatalog}, \cs{pdfinfo}, +% \cs{pdfpagesattr}, \cs{pdfpagesattr} and \cs{pdfpageresources} and +% the analog commands from the other engines and backends. +% +% All these commands have a \enquote{clash potential}, this means that the new +% interface is incompatible with a parallel use of the primitive commands +% which it targets to replace and supersede. +% This doesn't affect many packages, but the list of package using such primitives +% contains central and important packages like \pkg{hyperref}, \pkg{tikz}, +% \pkg{pdfx} and more. +% +% So while the goal is to integrate the code into the \LaTeX{} format directly, +% this can not be done immediately without conflicts with existing documents and packages. +% +% As an intermediary step the package \pkg{pdfmanagement-testphase} has been +% created which loads the code manually. +% With it package authors and users can test the new code, give feedback +% and packages can be adapted. +% +% Loading the package will only \emph{load} the modules, +% to \emph{activate} the core PDF management the trigger command +% \cs{DeclareDocumentMetadata} has to be used too. +% The loading and activation has to be done +% \emph{before} the \cs{documentclass} command. +% +% +% We hope that this setup will allow packages writers and users to test the +% PDF management code and adapt packages and documents safely. +% +% +% \section{Backend support} +% The supported backends are pdflatex, lualatex, (x)dvipdfmx (latex, xelatex, +% dvilualatex (in texlive 2021)) +% and dvips with ps2pdf (not completely yet). dvips with distiller could work too +% but is untested. +% +% That the interfaces and commands 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 compilation runs. +% Some backends manage some of the resources through side-effects, +% some manage them automatically. +% All this mean that package writers will still have to keep an eye on +% backend requirements and run tests for all variants. Also backend specific code +% will still be needed in some cases. +% +% \section{Use}\label{sec:use} +% The package should be loaded before \cs{documentclass}. To activate +% the resource management it should be followed by +% \cs{DeclareDocumentMetadata}\marg{key-val}. +% The options of \cs{DeclareDocumentMetadata} are described in the documentation of +% \pkg{ltdocinit}. +% +% \begin{verbatim} +% \RequirePackage{pdfmanagement-testphase} % load the package +% \DeclareDocumentMetadata % activates the PDF management interface +% { +% %options +% } +% \documentclass {...} +% \end{verbatim} +% +% The PDF management can be deactivated either setting the key +% \texttt{pdfmanagement} to \texttt{false} or by commenting out +% the whole \cs{DeclareDocumentMetadata} declaration. +% +% To test if the PDF management is active the predicate +% \cs{pdfmanagement_if_active:TF} can be used, see the documentation of \pkg{l3pdfmanagement}. +% +% \section{Requirements} +% The new PDF management requires a \LaTeX{} format from 2020/10/01 or later +% and an L3 programming layer of 2021-02-18 or later. +% It currently depends on the experimental packages \pkg{l3ref-tmp} and \pkg{l3bitset}. +% In some places, e.g. when writing strings to the pdf it assumes that +% the file is utf8 encoded -- ascii will naturally work too, but legacy 8bit encodings are +% not supported. +% +% \section{Modules} +% The bundle contains a number of modules. The majority of the modules don't +% have a stand alone \texttt{sty}, their code is combined +% in one file and loaded by the main package. The organization and naming is bound +% to change over time: For almost all modules the goal is to +% integrate them into the format and the individual files to disappear. +% +% The description items give the name of the documentation +% of the modules. There doesn't exist in all cases a related |.sty|. +% +% \begin{description} +% \item[l3pdfdict] This module provides commands for PDF dictionaries. Its main +% purpose is to create name spaces. The code used e.g. by \pkg{l3pdfmanagement} and +% \pkg{l3pdfannot}. +% +% \item[l3pdfannot] This module provides commands for annotations. Currently mainly +% link annotations, widget annotations will be added later. It doesn't require +% the PDF management to be active. +% +% \item[l3pdfmanagement] This is the core code of the PDF management. +% +% \item[ltdocinit] This module provides the \cs{DeclareDocumentMetadata} command. +% +% \item[hyperref-generic] This module provides a new generic hyperref driver. +% The driver will +% be loaded automatically by hyperref if the PDF management code is active. +% +% \item[l3backend-testphase] This module contains backend code needed by the +% PDF management. It will in due time be integrated into l3backend. +% +% \item[l3pdfmeta] This module contains code to handle PDF standards. +% Currently it handles pdf/A and colorprofiles/outputintents. +% +% \item[l3pdfxform] Commands for form XObjects (xforms). +% +% \item[l3pdf\/tool] A number of commands like text conversion commands and +% bcd/emc. The commands will at some time be moved into the \pkg{l3pdf} +% module of l3kernel. +% +% \item[l3pdf\/file] This module provides commands for to embed files. +% +% \item[pdfmanagement-firstaid] This module provides a number of patches +% for external incompatible packages. This patches will disappear as soon as +% the packages are natively compatible. It is loaded automatically. +% +% \end{description} +% \section{Incompabilities} +% +% As described in section~\ref{sec:change}, if activated +% the new PDF management takes over the management of core PDF dictionaries. +% All packages +% that bypass the PDF management and access these dictionaries with primitives like +% \cs{pdfcatalog}, \cs{pdfinfo}, \cs{pdfpageresources}, \cs{pdfpagesattr} +% and \cs{pdfpageattr} or similar commands from other engines and backends are +% basically incompatible: values can get lost or will be wrong. +% +% The following describes known incompatible packages along with some suggestions +% how this should or will be handled in future. The list is not exhaustive. +% +% \subsection{hyperref} +% A generic driver that can +% be used as replacement has been developed and is provided by this bundle. +% It will be loaded automatically if the pdf management is active. +% +% The generic driver differs in some points from other \pkg{hyperref} drivers: +% \begin{itemize} +% \item The code for bookmarks has been removed from this driver, instead +% the \pkg{bookmarks} is loaded and used. +% \item The driver isn't yet fully integrated into hyperref. This means that +% it doesn't react to a number of package options. Instead \cs{hypersetup} should +% be used. +% \item Incomplete is the support for form fields. Quite probably form fields will +% be extracted in a dedicated package. +% \item The driver uses for the color handling the l3color package. While normally +% it should be able to use colors defined with color and xcolor, there could be +% edge cases where it fails. +% \item The colors have been changed (this counts probably as an improvement \ldots). +% \end{itemize} +% +% More details can be found in the documentation \pkg{hyperref-generic.pdf}. +% +% \subsection{pdfx} +% \pkg{pdfx} is not compatible. It uses the commands \cs{pdfpagesattr}, \cs{pdfpageattr}, +% \cs{pdfinfo} and \cs{pdfcatalog}. The needed changes are not many, but can +% not be done by external patches. +% +% It is also one goal of the pdfmanagement project to +% offer support for standards natively. The code is under development, +% see the documentation of \pkg{l3pdfmeta}. +% +% \subsection{hyperxmp} +% \pkg{hyperxmp} uses \cs{pdfcatalog} to insert the \texttt{/MetaData} reference. This +% makes it incompatible, but adjusting this is even possible with external +% patches. \pkg{hyperxmp} also relies on some \pkg{hyperref} internals, so +% changes in \pkg{hyperref} must be coordinated. +% +% Some patch code is provided by the bundle and loaded automatically, +% but it is not complete currently. Failures are e.g. possible with complicated +% author or title settings. The handling of dates isn't correct either yet. +% The patch code can be disabled by using |firstaidoff=hyperxmp| in \cs{DeclareDocumentMetadata} +% +% \subsection{tikz/pgf} +% \pkg{pgf} writes to the page resources too and so is incompatible. The needed +% changes are rather small and will be done in coordination with the maintainer. +% Until this works, \pkg{pagemanagement} will load the patches automatically. +% This can be disabled by using |firstaidoff=pgf| in \cs{DeclareDocumentMetadata} +% +% \subsection{transparent} +% The package \pkg{transparent} is incompatible. A replacement has been written +% (\pkg{transparent-ltx}) and is loaded automatically. It requires a very recent +% L3 programming layer! +% This can be disabled by using |firstaidoff=transparent| in \cs{DeclareDocumentMetadata} +% \subsection{pdflscape} +% The package \pkg{pdflscape} is incompatible. A replacement has been written +% (\pkg{pdflscape-ltx}) and is loaded automatically. +% This can be disabled by using |firstaidoff=pdflscape| in \cs{DeclareDocumentMetadata} +% +% \subsection{colorspace} +% The package is incompatible. Some patches +% have been added to \pkg{pdfmanagement-firstaid}. +% Alternative code for spot colors is +% in the \pkg{l3color} package which has now been add to \pkg{l3kernel}. +% +% \subsection{embedfile, attachfile, attachfile2} +% Tools needed to be able to write a replacement +% to replace these packages have been developed in the \pkg{l3pdffile} package. +% Full replacements for the packages don't exist yet. +% +% \subsection{tagpdf} +% The development code is compatible and will be uploaded in time. +% +% \subsection{ocgx2, animate, media9} +% These package all make use of low-level PDF command and will +% have to be reviewed. +% +% \subsection{acrotex} +% The \pkg{acrotex} makes heavy use of PDF commands and so must be reviewed and +% adapted, including the currently untested route dvips + distiller. +% +% \subsection{fancytooltips} +% This package uses \cs{pdfpageattr} and \pkg{acrotex} and so must be reviewed. +% \end{documentation} +% +% +% \begin{implementation} +% +% \section{Implementation} +% +% \begin{macrocode} +%<@@=pdf> +%<*package> +\ProvidesExplPackage {pdfmanagement-testphase} {2021-02-22} {0.95a} + {LaTeX PDF management testphase bundle} +\providecommand\IfFormatAtLeastTF{\@ifl@t@r\fmtversion} +\IfFormatAtLeastTF{2020-10-01}{}{ + \PackageWarning{pdfmanagement-testphase} + {This~package~needs~LaTeX~2020-10-01~or~newer. + \MessageBreak Loading~is~aborted.}{} + \DeclareOption { debug }{} + \newcommand\DeclareDocumentMetadata[1]{} + \ProcessOptions\relax + } +\IfFormatAtLeastTF{2020-10-01}{}{\endinput} + +\DeclareOption { debug } + { + \msg_redirect_module:nnn { pdf } { none } { warning } + } + +\ProcessOptions\relax +%</package> +% \end{macrocode} +% +% \subsection{Loading the core files.} +% This loads the core files. The backend should not be loaded +% to allow to set it in the document. +% \begin{macrocode} +%<*header> +\ProvidesExplFile + {pdfmanagement-testphase.ltx}{2021-02-22}{0.95a} + {PDF~management~code~(testphase)} +%</header> +%<*package> +%\RequirePackage{l3pdfdict} % needed by l3pdfmanagement +%\RequirePackage{l3pdfmanagement} % loads the core code with the boolean +%\RequirePackage{ltdocinit} % DeclareDocumentMetadata, +%% can perhaps be combined or made optional ... +%\RequirePackage{l3pdfannot} +%\RequirePackage{l3pdfxform-beta} +%\RequirePackage{l3pdfmeta} % +%\RequirePackage{l3pdftools} +%\RequirePackage{l3pdffile} +\input{pdfmanagement-testphase.ltx} +%</package> +% \end{macrocode} +% \end{implementation} +% \newpage +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/pdfmanagement-testphase/pdfmanagement-testphase.ins b/Master/texmf-dist/source/latex/pdfmanagement-testphase/pdfmanagement-testphase.ins new file mode 100644 index 00000000000..6452b24c68d --- /dev/null +++ b/Master/texmf-dist/source/latex/pdfmanagement-testphase/pdfmanagement-testphase.ins @@ -0,0 +1,211 @@ +\iffalse meta-comment + +File: pdfmanagement-testphase.ins + +Copyright (C) 2019-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 + + https://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 released version of this bundle is available from CTAN. + +----------------------------------------------------------------------- + +The development version of the bundle can be found at + + https://github.com/latex3/pdfresources + +for those people who are interested. + +----------------------------------------------------------------------- + +\fi + +\input l3docstrip.tex +\askforoverwritefalse + +\let\MetaPrefix\relax +\preamble + +Copyright (C) 2019-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: + + https://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. + +\endpreamble +\let\MetaPrefix\DoubleperCent +% stop docstrip adding \endinput +\postamble +\endpostamble + +\keepsilent + +\generate + {% + \file{pdfmanagement-testphase.sty} + {% + \from{pdfmanagement-testphase.dtx}{package} + }% + } + +%\generate{\file{ltdocinit.sty} +% { +% \from{ltdocinit.dtx} {header,package} +% } +%} +%\generate{\file{l3pdfdict.sty} +% { +% \from{l3pdfdict.dtx} {header,package} +% } +%} +% +%\generate{\file{l3pdfmanagement.sty} +% { +% \from{l3pdfmanagement.dtx} {header,package} +% } +%} +% +%\generate{\file{l3pdfmeta.sty} +% { +% \from{l3pdfmeta.dtx} {header,package} +% } +%} +% +%\generate{\file{l3pdftools.sty} +% { +% \from{l3pdftools.dtx} {header,package} +% } +%} + +\generate + {% + \file{pdfmanagement-firstaid.sty} + {% + \from{pdfmanagement-firstaid.dtx}{package} + }% + } + +%\generate +% {% +% \file{l3pdfannot.sty} +% {% +% \from{l3pdfannot.dtx}{header,package} +% }% +% } +% +%\generate +% {% +% \file{l3pdfxform-beta.sty} +% {% +% \from{l3pdfxform.dtx}{header,package} +% }% +% } +% +%\generate +% {% +% \file{l3pdffile.sty} +% {% +% \from{l3pdffile.dtx}{header,package} +% }% +% } + +\generate + { + \file{l3backend-testphase-dvipdfmx.def} + { + \from{l3backend-testphase.dtx}{drivers,dvipdfmx} + } + } + +\generate + { + \file{l3backend-testphase-dvips.def} + { + \from{l3backend-testphase.dtx}{drivers,dvips} + } + } + +\generate + { + \file{l3backend-testphase-dvisvgm.def} + { + \from{l3backend-testphase.dtx}{drivers,dvisvgm} + } + } + +\generate + { + \file{l3backend-testphase-luatex.def} + { + \from{l3backend-testphase.dtx}{drivers,luatex} + } + } + +\generate + { + \file{l3backend-testphase-pdftex.def} + { + \from{l3backend-testphase.dtx}{drivers,pdftex} + } + } +\generate + { + \file {l3backend-testphase-xetex.def} + { + \from{l3backend-testphase.dtx}{drivers,xdvipdfmx} + } + } + +\generate + { + \file {hgeneric-testphase.def} + { + \from{hyperref-generic.dtx}{package} + } + } +\generate + { + \file {hyperref-colorschemes.def} + { + \from{hyperref-generic.dtx}{colorscheme} + } + } + +\generate + { + \file {pdfmanagement-testphase.ltx} + { + \from{pdfmanagement-testphase.dtx}{header} + \from{l3pdfdict.dtx}{package} + \from{l3pdfmanagement.dtx}{package} + \from{ltdocinit.dtx}{package} + \from{l3pdfannot.dtx}{package} + \from{l3pdfxform.dtx}{package} + \from{l3pdfmeta.dtx}{package} + \from{l3pdftools.dtx}{package} + \from{l3pdffile.dtx}{package} + } + } + +\def\MetaPrefix{-- } +\def\defaultpostamble{% + \MetaPrefix^^J% + \MetaPrefix\space End of File `\outFileName'.% +} +\def\currentpostamble{\defaultpostamble}% +\generate{\file{l3backend-testphase.lua} {\from{l3backend-testphase.dtx}{lua}}} +\endbatchfile |