% \iffalse meta-comment
%
%% File: l3backend-testphase.dtx
%
% Copyright (C) 2019-2023 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.95x, released 2023-03-09}
%
% \maketitle
%
%
% \begin{implementation}
%
% \section{\pkg{l3backend-testphase} Implementation}
%    \begin{macrocode}
%<drivers>\ProvidesExplFile
%<*dvipdfmx>
  {l3backend-testphase-dvipdfmx.def}{2023-03-09}{}
  {LaTeX~PDF~management~testphase~bundle~backend~support: dvipdfmx}
%</dvipdfmx>
%<*dvips>
  {l3backend-testphase-dvips.def}{2023-03-09}{}
  {LaTeX~PDF~management~testphase~bundle~backend~support: dvips}
%</dvips>
%<*dvisvgm>
  {l3backend-testphase-dvisvgm.def}{2023-03-09}{}
  {LaTeX~PDF~management~testphase~bundle~backend~support: dvisvgm}
%</dvisvgm>
%<*luatex>
  {l3backend-testphase-luatex.def}{2023-03-09}{}
  {LaTeX~PDF~management~testphase~bundle~backend~support: PDF output (LuaTeX)}
%</luatex>
%<*pdftex>
  {l3backend-testphase-pdftex.def}{2023-03-09}{}
  {LaTeX~PDF~management~testphase~bundle~backend~support: PDF output (pdfTeX)}
%</pdftex>
%<*xdvipdfmx>
  {l3backend-testphase-xetex.def}{2023-03-09}{}
  {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 needed 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}
% 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 }
%</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
\box_new:N  \l_@@_backend_tmpb_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{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 } }
  }
%</pdftex|luatex|dvipdfmx|xdvipdfmx|dvisvgm>
%<*dvips>
\cs_new:Npn \__kernel_pdf_name_from_unicode_e:n #1
  {
    ~ ( \text_expand:n { #1 } ) ~ cvn
  }
%</dvips>
%    \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
      }
  }
  {
    \hook_gput_code:nnn{shipout/background}{pdf}
      {
        \g__kernel_pdfmanagement_thispage_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 l3ref-tmp 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
    \@@_backend_ref_label:en { l3pdf\int_use:N\g_@@_backend_resourceid_int }{abspage}
    \tl_set:Nx \l_@@_tmpa_tl
      {
        \@@_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}{\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,
  }
%</drivers>
%    \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
  {
    \pdf_object_new:n {@@/Page/Resources/#1}
    \cs_if_exist:NT \tex_directlua:D
      {
        \tex_directlua:D
          {
            ltx.@@.object["@@/Page/Resources/#1"]
            =
            "\@@_backend_object_ref:n{@@/Page/Resources/#1}"
          }
      }
   }
%</pdftex|luatex>
%    \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"])
      }
  }
%</luatex>
%<*pdftex>
 \cs_new_protected:Npn \@@_backend_PageResources_gput:nnn #1 #2 #3
   {
     \pdfdict_gput:nnn {g_@@_Core/Page/Resources/#1} { #2 }{ #3 }
   }
%</pdftex>
%    \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} }
          {
            \pdf_object_write:nnx
              { @@/Page/Resources/##1 } { dict }
              { \pdfdict_use:n { g_@@_Core/Page/Resources/##1} }
          }
     }
  }
%</pdftex|luatex>
%    \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>
%<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
  {
    \pdf_object_new:n   { @@/Page/Resources/#1 }
    \hook_gput_code:nnn
      {shipout/firstpage}
      {pdf}
      {\pdf_object_write:nnn { @@/Page/Resources/#1 } { dict } {}}
  }
\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>
%    \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: {}
%</dvips>
%    \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: {}
%</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
  {
    \@@_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 {}

%</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 } }
      }
      {
        \@@_backend_ref_label:en{l3pdf\int_use:N\g_@@_backend_resourceid_int}{abspage}
        \tl_set:Nx \l_@@_tmpa_tl
          {
            \@@_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: }
      }
      {
        \@@_backend_ref_label:en{l3pdf\int_use:N\g_@@_backend_resourceid_int}{abspage}
        \tl_set:Nx \l_@@_tmpa_tl
          {
            \@@_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
% 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:}
  }
%</pdftex>
%<*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:}
  }
%</luatex>
%<*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~<</#1~\pdf_object_ref_last: >>}
  }
%</dvipdfmx|xdvipdfmx>

%dvips: noop
%<*dvips>
\cs_new_protected:Npn \@@_backend_Names_gpush:nn #1 #2  {}
%</dvips>
%dvisvgm: noop
%<*dvisvgm>
\cs_new_protected:Npn \@@_backend_Names_gpush:nn #1 #2 {}
%</dvisvgm>
%    \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 {}
%</pdftex|luatex|dvipdfmx|xdvipdfmx>
%<*dvips>
\cs_new_protected:Npn  \@@_backend_NamesEmbeddedFiles_add:nn #1 #2
      {
        \@@_backend_pdfmark:x
          {
            /Name~#1~
            /FS~#2~
            /EMBED
          }
      }
%</dvips>
%<*dvisvgm>
%no op. Or is there any sensible use for it?
\cs_new_protected:Npn  \@@_backend_NamesEmbeddedFiles_add:nn #1 #2
      {}

%</dvisvgm>
%    \end{macrocode}
% \end{macro}
%  \subsubsection{Additional annotation commands}
% Starting with texlive 2021 pdftex and luatex offer commands to interrupt
% a link. That can for example be used to exclude the header and footer from
% the link. We add here backend support for this.
%    \begin{macrocode}
%<*drivers>
\cs_new_protected:Npn \@@_backend_link_off:{}
\cs_new_protected:Npn \@@_backend_link_on: {}
%</drivers>
%<*pdftex>
\cs_if_exist:NT \pdfrunninglinkoff
 {
   \cs_set_protected:Npn \@@_backend_link_off:
    {
      \pdfrunninglinkoff
    }
   \cs_set_protected:Npn \@@_backend_link_on:
    {
      \pdfrunninglinkon
    }
 }
%</pdftex>
%<*luatex>
\int_compare:nNnT {\tex_luatexversion:D } > {112}
 {
   \cs_set_protected:Npn \@@_backend_link_off:
    {
      \pdfextension linkstate 1
    }
   \cs_set_protected:Npn \@@_backend_link_on:
    {
      \pdfextension linkstate 0
    }
 }
%</luatex>
%<*dvipdfmx|xdvipdfmx>
  \cs_set_protected:Npn \@@_backend_link_off:
    {
     \@@_backend:n { nolink }
    }
   \cs_set_protected:Npn \@@_backend_link_on:
    {
      \@@_backend:n { link }
    }
%</dvipdfmx|xdvipdfmx>
%    \end{macrocode}
%
%  \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>
%    \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.
% We need some temporary variables to store dimensions
%    \begin{macrocode}
%<*dvips>
\tl_new:N \l_@@_backend_xform_tmpwd_tl
\tl_new:N \l_@@_backend_xform_tmpdp_tl
\tl_new:N \l_@@_backend_xform_tmpht_tl
%    \end{macrocode}
%
%    \begin{macrocode}
\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 }
   %store content dimensions in DPI units (Dots) (code from issue 25)
    \tl_set:Nx\l_@@_backend_xform_tmpwd_tl
      {
        \dim_to_decimal_in_sp:n{ \box_wd:N \l_@@_backend_tmpa_box }~
        65536~div~72.27~div~DVImag~mul~Resolution~mul~
      }
    \tl_set:Nx\l_@@_backend_xform_tmpht_tl
      {
        \dim_to_decimal_in_sp:n{ \box_ht:N \l_@@_backend_tmpa_box }~
        65536~div~72.27~div~DVImag~mul~VResolution~mul~
      }
    \tl_set:Nx\l_@@_backend_xform_tmpdp_tl
      {
        \dim_to_decimal_in_sp:n{ \box_dp:N \l_@@_backend_tmpa_box }~
        65536~div~72.27~div~DVImag~mul~VResolution~mul~
      }
    % 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~
            initclip~ % restore default clipping path (page device/whole page)
            clippath~pathbbox~newpath~pop~pop~
            \tl_use:N\l_@@_backend_xform_tmpdp_tl~add~translate~
            mark~
              /_objdef~{ pdf.obj \int_use:N\g_@@_backend_object_int }\c_space_tl~
              /BBox[
                0~
                \tl_use:N\l_@@_backend_xform_tmpht_tl~
                \tl_use:N\l_@@_backend_xform_tmpwd_tl~
                \tl_use:N\l_@@_backend_xform_tmpdp_tl~
                neg
              ]
              \str_if_eq:eeF{#1}{}
               {
                 product~(Distiller)~search~{pop~pop~pop~#2}{pop}ifelse~
               }
            /BP~pdfmark~1~-1~scale~neg~exch~neg~exch~translate
          }
        \box_use_drop:N\l_@@_backend_tmpa_box
        \__kernel_backend_postscript:n
          {
            mark ~ /EP~pdfmark  ~ grestore
          }
       \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} }
  }

%</dvips>
%<*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{Structure Destinations}
% Standard destinations consist of a reference to a page in the pdf and instructions
% how to display it---typically they will put a specific location in the left top corner
% of the viewer and so give the impression that a link jumped to the word in this place.
% But in reality they are not connected to the content.
%
% Starting with pdf~2.0 destinations can in a tagged PDF also point to a structure, to a /StructElem object.
% GoTo links can then additionally to the \texttt{/D} key pointing to a
% page destination also point to such a structure destination with an \texttt{/SD} key.
% Programs that e.g. convert such a PDF to html can then create better links.
% (According to the reference, PDF-viewer should prefer the structure destination
% over the page destination, but as far as it is known this isn't done yet.)
%
% Currently structure destinations and GoTo links making use of it could natively only
% be created with the dvipdfmx backend. With pdftex and lualatex it was only possible to create
% a restricted type which used only the \enquote{Fit} mode. Starting with
% \TeX{}live 2022  (earlier in miktex) both engine will knew new keywords which allow
% to create structure destination easily.
%
% The following backend code prepares the use of structure destinations. The general idea is
% that if structure destinations are used, they should be used always. So we define
% alternative commands which can be activated by mapping them to the standard backend commands.
%
% \begin{macro}{\l_pdf_current_structure_destination_tl}
%
% This commands holds the name of the structure object to use in the next command
% which creates a destination. The code which activates structure destinations
% must also ensure that it has a sensible, expandable content. \pkg{tagpdf} for example
% will define it as
% \begin{verbatim}
% \tl_set:Nn \l_pdf_current_structure_destination_tl { __tag/struct/\g__tag_struct_stack_current_tl }
% \end{verbatim}
%    \begin{macrocode}
%<*drivers>
\tl_new:N   \l_pdf_current_structure_destination_tl
%</drivers>
%    \end{macrocode}
% \end{macro}
%
% We will define alternatives for three backend commands:
% \begin{verbatim}
% \__pdf_backend_destination:nn       -> \__pdf_backend_structure_destination:nn
% \__pdf_backend_destination:nnnn -> \__pdf_backend_structure_destination:nnnn
% \__pdf_backend_link_begin_goto:nnw  -> \__pdf_backend_link_begin_structure_goto:nnw
% \end{verbatim}
%
% Activating means mapping them onto the original commands. Be aware that not
% all engines and compilation routes support structure destinations, for them
% the command will be a no-op.
%
% \begin{macro}{\pdf_activate_structure_destination:}
%    \begin{macrocode}
%<*drivers>
\cs_new_protected:Npn \pdf_activate_structure_destination:
 {
   \cs_gset_eq:NN \@@_backend_destination:nn \@@_backend_structure_destination:nn
   \cs_gset_eq:NN \@@_backend_destination:nnnn \@@_backend_structure_destination:nnnn
   \cs_gset_eq:NN \@@_backend_link_begin_goto:nnw  \@@_backend_link_begin_structure_goto:nnw
 }
%</drivers>
%    \end{macrocode}
% \end{macro}
% Now the driver dependant parts.
% By default the new commands are simply copies of the original commands.
% We adapt them then for the engines and engine version which provide support
% for structure destinations.
%
%    \begin{macrocode}
%<*drivers>
\cs_set_eq:NN \@@_backend_structure_destination:nn       \@@_backend_destination:nn
\cs_set_eq:NN \@@_backend_structure_destination:nnnn \@@_backend_destination:nnnn
\cs_set_eq:NN \@@_backend_link_begin_structure_goto:nnw  \@@_backend_link_begin_goto:nnw
%</drivers>
%    \end{macrocode}
% \begin{macro}{\@@_backend_structure_destination:nn}
% This command is the backend command to create a destination.
% It should in parallel create also a structure destination.
% At first xetex/dvipdfmx.
% The structure destination is an array, so we use obj for it
% so that we can reference it:
%    \begin{macrocode}
%<*xdvipdfmx|dvipdfmx>
\cs_set_protected:Npn \@@_backend_structure_destination:nn #1#2
  {
    \@@_backend:x
      {
        dest ~ ( \exp_not:n {#1} )
        [
          @thispage
          \str_case:nnF {#2}
            {
              { xyz }   { /XYZ ~ @xpos ~ @ypos ~ null }
              { fit }   { /Fit }
              { fitb }  { /FitB }
              { fitbh } { /FitBH }
              { fitbv } { /FitBV ~ @xpos }
              { fith }  { /FitH ~ @ypos }
              { fitv }  { /FitV ~ @xpos }
              { fitr }  { /Fit }
            }
            { /XYZ ~ @xpos ~ @ypos ~ \fp_eval:n { (#2) / 100 } }
        ]
      }
%    \end{macrocode}
% We test if the structure object exist. The object of the structure destination
% gets the name \texttt{@pdf.Sdest.\meta{destname}}, where \meta{destname} is the
% name of the standard destination so that we can reference it in the GoTo links.
%    \begin{macrocode}
    \exp_args:Ne \pdf_object_if_exist:nT { \l_pdf_current_structure_destination_tl }
      {
        \@@_backend:x
         {
          obj ~ @pdf.SDest.\exp_not:n{#1}
          [
            \exp_args:Ne \pdf_object_ref:n { \l_pdf_current_structure_destination_tl }
            \str_case:nnF {#2}
              {
                { xyz }   { /XYZ ~ @xpos ~ @ypos ~ null }
                { fit }   { /Fit }
                { fitb }  { /FitB }
                { fitbh } { /FitBH }
                { fitbv } { /FitBV ~ @xpos }
                { fith }  { /FitH ~ @ypos }
                { fitv }  { /FitV ~ @xpos }
                { fitr }  { /Fit }
              }
              { /XYZ ~ @xpos ~ @ypos ~ \fp_eval:n { (#2) / 100 } }
          ]
        }
      }
  }
%    \end{macrocode}
% The second destination command is for the boxed destination. Here we need to define
% an new auxiliary command:
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_structure_destination_aux:nnnn #1#2#3#4
  {
    \vbox_to_zero:n
      {
        \__kernel_kern:n {#4}
        \hbox:n
          {
            \@@_backend:n { obj ~ @pdf_ #2 _llx ~ @xpos }
            \@@_backend:n { obj ~ @pdf_ #2 _lly ~ @ypos }
          }
        \tex_vss:D
      }
    \__kernel_kern:n {#1}
    \vbox_to_zero:n
      {
        \__kernel_kern:n { -#3 }
        \hbox:n
          {
            \@@_backend:n
              {
                dest ~ (#2)
                [
                  @thispage
                  /FitR ~
                    @pdf_ #2 _llx ~ @pdf_ #2 _lly ~
                    @xpos ~ @ypos
                ]
              }
%    \end{macrocode}
% Here we add the structure destination to the same box
%    \begin{macrocode}
            \exp_args:Ne \pdf_object_if_exist:nT { \l_pdf_current_structure_destination_tl }
              {
                \@@_backend:x
                  {
                    obj ~ @pdf.SDest.\exp_not:n{#2}
                    [
                      \exp_args:Ne \pdf_object_ref:n { \l_pdf_current_structure_destination_tl }
                      /FitR ~
                        @pdf_ #2 _llx ~ @pdf_ #2 _lly ~
                        @xpos ~ @ypos
                    ]
                  }
              }
          }
        \tex_vss:D
      }
    \__kernel_kern:n { -#1 }
  }
%    \end{macrocode}
% And now we redefine the destination command:
%    \begin{macrocode}
\cs_set_protected:Npn \@@_backend_structure_destination:nnnn #1#2#3#4
  {
    \exp_args:Ne \@@_backend_structure_destination_aux:nnnn
      { \dim_eval:n {#2} } {#1} {#3} {#4}
  }
%    \end{macrocode}
% At last the goto link.
%    \begin{macrocode}
\cs_set_protected:Npn \@@_backend_link_begin_structure_goto:nnw #1#2
  {
    \@@_backend_link_begin:n { #1 /Subtype /Link /A << /S /GoTo /D ( #2 ) /SD~@pdf.SDest.#2  >> }
  }
%</xdvipdfmx|dvipdfmx>
%    \end{macrocode}
% \end{macro}
% Now pdftex. We only redefine for version 1.40 revision 24 or later.
%    \begin{macrocode}
%<*pdftex>
\bool_lazy_and:nnT
  { \int_compare_p:nNn {\tex_pdftexversion:D } > {139} }
  { \int_compare_p:nNn {\tex_pdftexrevision:D } > {23} }
  {
    \cs_set_protected:Npn \@@_backend_structure_destination:nn #1#2
      {
        \tex_pdfdest:D
           name {#1}
           \str_case:nnF {#2}
             {
               { xyz }   { xyz }
               { fit }   { fit }
               { fitb }  { fitb }
               { fitbh } { fitbh }
               { fitbv } { fitbv }
               { fith }  { fith }
               { fitv }  { fitv }
               { fitr }  { fitr }
             }
             { xyz ~ zoom \fp_eval:n { #2 * 10 } }
           \scan_stop:
        \exp_args:Ne \pdf_object_if_exist:nT { \l_pdf_current_structure_destination_tl }
          {
            \tex_pdfdest:D
               struct~
               \int_use:c
                 { c_@@_backend_object_ \exp_args:Ne \tl_to_str:n {\l_pdf_current_structure_destination_tl} _int }~
               name {#1}
               \str_case:nnF {#2}
                 {
                   { xyz }   { xyz }
                   { fit }   { fit }
                   { fitb }  { fitb }
                   { fitbh } { fitbh }
                   { fitbv } { fitbv }
                   { fith }  { fith }
                   { fitv }  { fitv }
                   { fitr }  { fitr }
                 }
                 { xyz ~ zoom \fp_eval:n { #2 * 10 } }
               \scan_stop:
          }
      }
    \cs_set_protected:Npn \@@_backend_destination:nnnn #1#2#3#4
      {
       \tex_pdfdest:D
        name {#1}
        fitr ~
        width  \dim_eval:n {#2} ~
        height \dim_eval:n {#3} ~
        depth  \dim_eval:n {#4} \scan_stop:
       \exp_args:Ne \pdf_object_if_exist:nT { \l_pdf_current_structure_destination_tl }
         {
           \tex_pdfdest:D
             struct~
             \int_use:c
               { c_@@_backend_object_ \exp_args:Ne \tl_to_str:n {\l_pdf_current_structure_destination_tl} _int }~
             name {#1}
             fitr ~
             width  \dim_eval:n {#2} ~
             height \dim_eval:n {#3} ~
             depth  \dim_eval:n {#4} \scan_stop:
         }
     }
    \cs_set_protected:Npn \@@_backend_link_begin_structure_goto:nnw #1#2
      {
        \@@_backend_link_begin:nnnw {#1} { goto~struct~name~{#2}~name } {#2}
      }
  }
%</pdftex>
%    \end{macrocode}
% luatex is quite similar to pdftex. Mostly the test for the version is different
%    \begin{macrocode}
%<*luatex>
 \int_compare:nNnT {\directlua{tex.print(status.list()["development_id"])} } > {7468}
  {
    \cs_set_protected:Npn \@@_backend_structure_destination:nn #1#2
      {
        \tex_pdfextension:D dest
           name {#1}
           \str_case:nnF {#2}
             {
               { xyz }   { xyz }
               { fit }   { fit }
               { fitb }  { fitb }
               { fitbh } { fitbh }
               { fitbv } { fitbv }
               { fith }  { fith }
               { fitv }  { fitv }
               { fitr }  { fitr }
             }
             { xyz ~ zoom \fp_eval:n { #2 * 10 } }
           \scan_stop:
        \exp_args:Ne \pdf_object_if_exist:nT { \l_pdf_current_structure_destination_tl }
          {
            \tex_pdfextension:D dest
               struct~
               \int_use:c
                 { c_@@_backend_object_ \exp_args:Ne \tl_to_str:n {\l_pdf_current_structure_destination_tl} _int }~
               name {#1}
               \str_case:nnF {#2}
                 {
                   { xyz }   { xyz }
                   { fit }   { fit }
                   { fitb }  { fitb }
                   { fitbh } { fitbh }
                   { fitbv } { fitbv }
                   { fith }  { fith }
                   { fitv }  { fitv }
                   { fitr }  { fitr }
                 }
                 { xyz ~ zoom \fp_eval:n { #2 * 10 } }
               \scan_stop:
          }
      }
    \cs_set_protected:Npn \@@_backend_destination:nnnn #1#2#3#4
      {
       \tex_pdfextension:D dest
        name {#1}
        fitr ~
        width  \dim_eval:n {#2} ~
        height \dim_eval:n {#3} ~
        depth  \dim_eval:n {#4} \scan_stop:
       \exp_args:Ne \pdf_object_if_exist:nT { \l_pdf_current_structure_destination_tl }
         {
           \tex_pdfextension:D dest
             struct~
             \int_use:c
               { c_@@_backend_object_ \exp_args:Ne \tl_to_str:n {\l_pdf_current_structure_destination_tl} _int }~
             name {#1}
             fitr ~
             width  \dim_eval:n {#2} ~
             height \dim_eval:n {#3} ~
             depth  \dim_eval:n {#4} \scan_stop:
         }
     }
    \cs_set_protected:Npn \@@_backend_link_begin_structure_goto:nnw #1#2
      {
        \@@_backend_link_begin:nnnw {#1} { goto~struct~name~{#2}~name } {#2}
      }
  }
%</luatex>
%    \end{macrocode}
% \subsection{Settings for regression tests}
% When doing pdf based regression tests some meta data in the pdf should have
% fixed values to get identical pdf's. We define here the backend dependant
% part. The main command is then in l3pdfmeta
%    \begin{macrocode}
%<*drivers>
\cs_new_protected:Npn \@@_backend_set_regression_data:
  {
    \sys_gset_rand_seed:n{1000}
    \pdfmanagement_add:nnn{Info}{Creator}{(TeX)}
%</drivers>
%<*dvips>
    \AddToHook{begindocument}{\pdfmanagement_add:nnn{Info}{Producer}{(pdfTeX+dvips)}}
    \__kernel_backend_literal:e{!~<</DocumentUUID~(DocumentUUID)>>~setpagedevice}
    \__kernel_backend_literal:e{!~<</InstanceUUID~(InstanceUUID)>>~setpagedevice}
%</dvips>
%<*dvipdfmx>
    \pdfmanagement_add:nnn{Info}{Producer}{(dvipdfmx)}
    \__kernel_backend_literal:e
      {pdf:trailerid [~
      <00112233445566778899aabbccddeeff>~
      <00112233445566778899aabbccddeeff>~
          ]}
%</dvipdfmx>
%<*xdvipdfmx>
    \pdfmanagement_add:nnn{Info}{Producer}{(xetex)}
    \__kernel_backend_literal:e
      {pdf:trailerid [~
      <00112233445566778899aabbccddeeff>~
      <00112233445566778899aabbccddeeff>~
          ]}
%</xdvipdfmx>
%<*pdftex>
   \pdfmanagement_add:nnn{Info}{Producer}{(pdfTeX)}
   \tex_pdfsuppressptexinfo:D 7 \scan_stop:
   \pdftrailerid{2350CAD05F8A7AF0AA4058486855344F}
%</pdftex>
%<*luatex>
   \pdfmanagement_add:nnn{Info}{Producer}{(LuaTeX)}
   \tex_pdfvariable:D suppressoptionalinfo 7\relax
   \tex_pdfvariable:D trailerid
     {[~
       <2350CAD05F8A7AF0AA4058486855344F>~
       <2350CAD05F8A7AF0AA4058486855344F>~
     ]}
%</luatex>
%<*drivers>
    \pdfmanagement_add:nnn{Info}{CreationDate}{(D:20010101205959-00'00')}
    \pdfmanagement_add:nnn{Info}{ModDate}{(D:20010101205959-00'00')}
    \AddToDocumentProperties[document]{creationdate}{D:20010101205959-00'00'}
    \AddToDocumentProperties[document]{moddate}{D:20010101205959-00'00'}
    \AddToDocumentProperties[hyperref]{pdfmetadate}{D:20010101205959-00'00'}
    \AddToDocumentProperties[hyperref]{pdfdate}{D:20010101205959-00'00'}
    \AddToDocumentProperties[hyperref]{pdfinstanceid}{uuid:0a57c455-157a-4141-8c19-6237d832fc80}
    \AddToDocumentProperties[hyperref]{pdfproducer}{\c_sys_engine_exec_str-NN.NN.NN}
   }
%</drivers>
%    \end{macrocode}
%
% \subsection{Uncompressed metadata object stream}
% The xmp metadata should be written \enquote{uncompressed} to pdf.
% It is not quite clear what exactly that means. Probably it only
% means that there should be no |/Filter| key in the stream, but
% packages like \pkg{pdfx} and \pkg{hyperref} try to suppress object
% compression too, so we add support for it too.
% With luatex this is possible by using the |uncompressed| key word.
% With pdftex one can change locally the compresslevel. (x)dvipdfmx does
% it automatically and doesn't need some special command. No solution
% is known for the dvips route. We need it only once, so we make
% it special and probably no public interface is needed. It writes
% an unnamed object so should be referenced directly with |\pdf_object_ref_last:|
%    \begin{macrocode}
%<*luatex>
\cs_new_protected:Npn \@@_backend_metadata_stream:n #1
  {
    \tex_immediate:D \tex_pdfextension:D obj ~uncompressed~
      \@@_backend_object_write:nn {stream} {{/Type~/Metadata~/Subtype~/XML}{#1}}
  }
%</luatex>
%<*pdftex>
\cs_new_protected:Npn \@@_backend_metadata_stream:n #1
  {
    \group_begin:
     \tex_pdfcompresslevel:D  0 \scan_stop:
     \tex_immediate:D \tex_pdfobj:D
     \@@_backend_object_write:nn {stream} {{/Type~/Metadata~/Subtype~/XML}{#1}}
    \group_end:
  }
%</pdftex>
%<*xdvipdfmx|dvipdfmx|dvips|dvisvgm>
\cs_new_protected:Npn \@@_backend_metadata_stream:n #1
  {
    \pdf_object_unnamed_write:nn {stream}{{/Type~/Metadata~/Subtype~/XML}{#1}}
  }
%</xdvipdfmx|dvipdfmx|dvips|dvisvgm>
%    \end{macrocode}
%
% \subsection{Suppressing deprecated PDF features}
% 
% \texttt{/ProcSet}, \texttt{/CharSet} and the \texttt{/Info} dictionary
% are deprecated in PDF 2.0. For the pdf/A-4 standard they must be suppressed.
% Not every engine is able to do this, but for pdfTeX and luatex we define suitable
% backend command. \texttt{/ProcSet} is suppressed automatically 
% for pdf version 2.0 starting with in texlive 2023. 
% \begin{macro}{\@@_backend_omit_charset:n}
% The option to omit /Charset exists already for quite some time for the two 
% engines.
%    \begin{macrocode}
%<*xdvipdfmx|dvipdfmx|dvips|dvisvgm>
\cs_new_protected:Npn \@@_backend_omit_charset:n #1 {} %#1 number
%</xdvipdfmx|dvipdfmx|dvips|dvisvgm>
%<*pdftex>
\cs_new_protected:Npn \@@_backend_omit_charset:n #1 %#1 number
  {
    \tex_pdfomitcharset:D = #1 \scan_stop:
  } 
%</pdftex>
%<*luatex>
\cs_new_protected:Npn \@@_backend_omit_charset:n #1 %#1 number
  {
    \tex_pdfvariable:D omitcharset  = #1 \scan_stop:
  } 
%</luatex>
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_backend_omit_info:n}
% The option to suppress the info dictionary will be available in 
% texlive 2023.
%    \begin{macrocode}
%<*xdvipdfmx|dvipdfmx|dvips|dvisvgm>
\cs_new_protected:Npn \@@_backend_omit_info:n #1 {} %#1 number
%</xdvipdfmx|dvipdfmx|dvips|dvisvgm>
%<*pdftex>
\bool_lazy_and:nnTF
  { \int_compare_p:nNn {\tex_pdftexversion:D } > {139} }
  { \int_compare_p:nNn {\tex_pdftexrevision:D } > {24} }
  {
    \cs_new_protected:Npn \@@_backend_omit_info:n #1 %#1 number
     {
       \pdfomitinfodict = #1 \scan_stop:
     } 
  }
  {
    \cs_new_protected:Npn \@@_backend_omit_info:n #1 {}%#1 number
     
  }
%</pdftex>
%<*luatex>
 \int_compare:nNnTF {\directlua{tex.print(status.list()["development_id"])} } > {7560}
  {
    \cs_new_protected:Npn \@@_backend_omit_info:n #1 %#1 number
     {
       \tex_pdfvariable:D omitinfodict  = #1 \scan_stop:
     }
  }
  {
    \cs_new_protected:Npn \@@_backend_omit_info:n #1 {} %#1 number   
  }    
%</luatex>
%    \end{macrocode}
% \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
