summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/pdfmanagement-testphase/l3backend-testphase.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/pdfmanagement-testphase/l3backend-testphase.dtx')
-rw-r--r--Master/texmf-dist/source/latex/pdfmanagement-testphase/l3backend-testphase.dtx1598
1 files changed, 1598 insertions, 0 deletions
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