% \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} % % \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.95h, released 2021-07-31} % % \maketitle % % % \begin{implementation} % % \section{\pkg{l3backend-testphase} Implementation} % \begin{macrocode} %\ProvidesExplFile %<*dvipdfmx> {l3backend-testphase-dvipdfmx.def}{2021-07-31}{} {LaTeX~PDF~management~testphase~bundle~backend~support: dvipdfmx} % %<*dvips> {l3backend-testphase-dvips.def}{2021-07-31}{} {LaTeX~PDF~management~testphase~bundle~backend~support: dvips} % %<*dvisvgm> {l3backend-testphase-dvisvgm.def}{2021-07-31}{} {LaTeX~PDF~management~testphase~bundle~backend~support: dvisvgm} % %<*luatex> {l3backend-testphase-luatex.def}{2021-07-31}{} {LaTeX~PDF~management~testphase~bundle~backend~support: PDF output (LuaTeX)} % %<*pdftex> {l3backend-testphase-pdftex.def}{2021-07-31}{} {LaTeX~PDF~management~testphase~bundle~backend~support: PDF output (pdfTeX)} % %<*xdvipdfmx> {l3backend-testphase-xetex.def}{2021-07-31}{} {LaTeX~PDF~management~testphase~bundle~backend~support: XeTeX} % % \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} % % \end{macrocode} % avoid that destinations names are optimized with xelatex/dvipdfmx % see https://tug.org/pipermail/dvipdfmx/2019-May/000002.html % \begin{macrocode} %<*dvipdfmx|xdvipdfmx> \__kernel_backend_literal:x { dvipdfmx:config~C~ 0x0010 } % % \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 \box_new:N \l_@@_backend_tmpb_box % % \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 % % \end{macrocode} % \end{variable} % \subsection{luacode} % Load the lua code. % \begin{macrocode} %<*luatex> \directlua { require("l3backend-testphase.lua") } % % \end{macrocode} % \subsection{Converting unicode strings to a pdfname} % dvips needs a special function here, so we add this as backend function. % \begin{macrocode} %<*pdftex|luatex|dvipdfmx|xdvipdfmx|dvisvgm> \cs_new:Npn \__kernel_pdf_name_from_unicode_e:n #1 { / \str_convert_pdfname:e { \text_expand:n { #1 } } } % %<*dvips> \cs_new:Npn \__kernel_pdf_name_from_unicode_e:n #1 { ~ ( \text_expand:n { #1 } ) ~ cvn } % % \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 } % %<*dvipdfmx|xdvipdfmx> % put in \@kernel@after@shipout@lastpage \tl_gput_right:Nn \@kernel@after@shipout@lastpage { \g__kernel_pdfmanagement_end_run_code_tl } % %<*dvips> % put in \@kernel@after@shipout@lastpage \tl_gput_right:Nn\@kernel@after@shipout@lastpage { \g__kernel_pdfmanagement_end_run_code_tl } % % \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 } } { \hook_gput_code:nnn{shipout/background}{pdf} { \g__kernel_pdfmanagement_thispage_shipout_code_tl } } % % \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 <>}+ % 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 } } % %<*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 } ) } } } % %<*dvips> \cs_new_protected:Npx \@@_backend_Pages_primitive:n #1 { \tex_special:D{ps:~[#1~/PAGES~pdfmark} %] } % %<*dvipdfmx|xdvipdfmx> \cs_new_protected:Npn \@@_backend_Pages_primitive:n #1 { \@@_backend:n{put~@pages~<<#1>>} } % %<*dvisvgm> \cs_new_protected:Npn \@@_backend_Pages_primitive:n #1 {} % % \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}<> /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 } } % %<*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"]) } } % %<*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} } } % %<*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} } } % %<*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 {} % % \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}{\c_@@_backend_PageResources_clist} % The names are quite often needed % a similar list is now in l3pdfmanagement. Perhaps it should be merged. % \begin{macrocode} %<*drivers> \clist_const:Nn \c_@@_backend_PageResources_clist { ExtGState, ColorSpace, Pattern, Shading, } % % \end{macrocode} % \end{macro} % % Now the backend commands the command to fill the register % and to push the values. % % \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 should be a no-op with xdvipdfmx and dvips % as it currently issued in the end-of-run hook! % create the backend objects: % \begin{macrocode} %<*pdftex|luatex> \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}" } } } % % \end{macrocode} % values are only stored in a prop and will be output at end document. % luatex must also trigger the lua side % \begin{macrocode} %<*luatex> \cs_new_protected:Npn \@@_backend_PageResources_gput:nnn #1 #2 #3 { \pdfdict_gput:nnn {g_@@_Core/Page/Resources/#1} { #2 }{ #3 } \tex_latelua:D{ltx.@@.Page.Resources.#1=true} \tex_latelua:D { ltx.pdf.Page_Resources_gpush(tex.count["g_shipout_readonly_int"]) } } % %<*pdftex> \cs_new_protected:Npn \@@_backend_PageResources_gput:nnn #1 #2 #3 { \pdfdict_gput:nnn {g_@@_Core/Page/Resources/#1} { #2 }{ #3 } } % % \end{macrocode} % code for end of document code % \begin{macrocode} %<*pdftex|luatex> \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} } } } } % % \end{macrocode} % 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! % 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. % The push command should not be used as it is % in the wrong end document hook. If needed a new command must be added. % \begin{macrocode} %<*dvipdfmx|xdvipdfmx> %\hook_gset_rule:nnnn{shipout/firstpage}{l3backend-xetex}{after}{pdf} %\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}<> } } \cs_new_protected:Npn \@@_backend_PageResources_obj_gpush: {} % % \end{macrocode} % dvips unneeded, or no-op. The push command should not be used as it is % in the wrong end document hook. If needed a new command must be added. % \begin{macrocode} %<*dvips> \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: {} % % \end{macrocode} % dvipsvgm unneeded, or no-op % \begin{macrocode} %<*dvisvgm> \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: {} % % \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 % %<*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 { \@@_backend_pdfmark:x{/#1~<<#2>>~/BDC} } \cs_set_protected:Npn \@@_backend_bdcobject:nn #1 #2 % #1 eg. Span, #2: object name { \@@_backend_pdfmark:x{/#1~\@@_backend_object_ref:n{#2}~/BDC} } \cs_set_protected:Npn \@@_backend_bdcobject:n #1 % #1 eg. Span, { \@@_backend_pdfmark:x{/#1~\@@_backend_object_last:~/BDC} } \cs_set_protected:Npn \@@_backend_emc: { \@@_backend_pdfmark:n{/EMC} % } \cs_set_protected:Npn \@@_backend_bmc:n #1 { \@@_backend_pdfmark:n{/#1~/BMC} % } \cs_new_protected:Npn \@@_backend_PageResources_gpush:n #1 {} % %<*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 {} % % 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~ <> } } } } % % 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 {} % %<*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 } } % % \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 % dvips wants code for every file and then creates the Name tree automatically. % Other name trees are ignored. % TODO: Currently the code for EmbeddedFiles is still a bit % different but this should be merged, all name trees should be handled with the % same code. % \begin{macrocode} % pdflatex %<*pdftex> \cs_new_protected:Npn \@@_backend_Names_gpush:nn #1 #2 %#1 name of name tree, #2 array content { \pdf_object_unnamed_write:nn {dict} {/Names [#2] } \tex_pdfnames:D {/#1~\pdf_object_ref_last:} } % %<*luatex> \cs_new_protected:Npn \@@_backend_Names_gpush:nn #1 #2 %#1 name of name tree, #2 array content { \pdf_object_unnamed_write:nn {dict} {/Names [#2] } \tex_pdfextension:D~names~ {/#1~\pdf_object_ref_last:} } % %<*dvipdfmx|xdvipdfmx> \cs_new_protected:Npn \@@_backend_Names_gpush:nn #1 #2 %#1 name of name tree, #2 array content { \pdf_object_unnamed_write:nn {dict} {/Names [#2] } \@@_backend:x {put~@names~<>} } % %dvips: noop %<*dvips> \cs_new_protected:Npn \@@_backend_Names_gpush:nn #1 #2 {} % %dvisvgm: noop %<*dvisvgm> \cs_new_protected:Npn \@@_backend_Names_gpush:nn #1 #2 {} % % \end{macrocode} % EmbeddedFiles is a bit special. % For once we need backend commands for dvips. % But we want also an option to create the name on the fly. % \begin{macro}{\@@_backend_NamesEmbeddedFiles_add:nn} % dvips need special backend code to create the name tree. % With the other engines it does nothing. % \begin{macrocode} %<*pdftex|luatex|dvipdfmx|xdvipdfmx> \cs_new_protected:Npn \@@_backend_NamesEmbeddedFiles_add:nn #1 #2 {} % %<*dvips> \cs_new_protected:Npn \@@_backend_NamesEmbeddedFiles_add:nn #1 #2 { \@@_backend_pdfmark:x { /Name~#1~ /FS~#2~ /EMBED } } % %<*dvisvgm> %no op. Or is there any sensible use for it? \cs_new_protected:Npn \@@_backend_NamesEmbeddedFiles_add:nn #1 #2 {} % % \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 } % %<*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 } % %<*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 } % %<*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 {} % % \end{macrocode} % The xform code for dvips is based on code from the attachfile2 package % (in atfi-dvips), along with some ideas from pdfbase and has been corrected % with the help of Alexander Grahn. % Details like clipping and landscape will probably be corrected in the future. % \begin{macrocode} %<*dvips> \cs_new_protected:Npn\@@_backend_xform_new:nnnn #1 #2 #3 #4 % #1 name, #2 attribute, #4 content { \int_gincr:N \g_@@_backend_object_int \int_const:cn { c_@@_backend_xform_ \tl_to_str:n {#1} _int } { \g_@@_backend_object_int } \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 } %mirror the box \box_scale:Nnn \l_@@_backend_tmpa_box {1} {-1} \hbox_set:Nn\l_@@_backend_tmpb_box { \__kernel_backend_postscript:x { gsave~currentpoint~translate~ mark~ /_objdef~{ pdf.obj \int_use:N\g_@@_backend_object_int }\c_space_tl~ /BBox[ currentpoint~\dim_to_decimal:n{\box_dp:N \l_@@_backend_tmpa_box}~72.27~div~VResolution~mul~add~ currentpoint~ exch~\dim_to_decimal:n{\box_wd:N \l_@@_backend_tmpa_box}~72.27~div~Resolution~mul~add~ exch~\dim_to_decimal:n{\box_ht:N \l_@@_backend_tmpa_box}~72.27~div~VResolution~mul~sub~ ] \str_if_eq:eeF{#1}{} { product~(Distiller)~search~{pop~pop~pop~#2}{pop}ifelse~ } /BP~pdfmark~grestore~ } \box_use_drop:N\l_@@_backend_tmpa_box \__kernel_backend_postscript:n { mark ~ /EP~pdfmark } \str_if_eq:eeF{#1}{} { \__kernel_backend_postscript:x { product~(Ghostscript)~search~ { pop~pop~pop~ mark~ { pdf.obj \int_use:c{c_@@_backend_xform_ \tl_to_str:n {#1} _int} } ~<<#2>>~/PUT~pdfmark }{pop}ifelse } } } \box_set_dp:Nn \l_@@_backend_tmpb_box { \c_zero_dim } \box_set_ht:Nn \l_@@_backend_tmpb_box { \c_zero_dim } \box_set_wd:Nn \l_@@_backend_tmpb_box { \c_zero_dim } \hook_gput_code:nnn {begindocument/end}{pdfxform} { \mode_leave_vertical: \box_use:N\l_@@_backend_tmpb_box } } \cs_new_protected:Npn \@@_backend_xform_use:n #1 { \hbox_set:Nn \l_@@_backend_tmpa_box { \__kernel_backend_postscript:x { gsave~currentpoint~translate~1~-1~scale~ mark~{ pdf.obj \int_use:c{c__pdf_backend_xform_ \tl_to_str:n {#1} _int }}~ /SP~pdfmark ~ grestore } } \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 } \cs_new:Npn \@@_backend_xform_ref:n #1 { { pdf.obj \int_use:c{c_@@_backend_xform_ \tl_to_str:n {#1} _int} } } % %<*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 } % % \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 % % \end{macrocode} % \end{implementation} % % \PrintIndex