summaryrefslogtreecommitdiff
path: root/macros/latex-dev/required/l3kernel/l3pdf.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex-dev/required/l3kernel/l3pdf.dtx')
-rw-r--r--macros/latex-dev/required/l3kernel/l3pdf.dtx831
1 files changed, 831 insertions, 0 deletions
diff --git a/macros/latex-dev/required/l3kernel/l3pdf.dtx b/macros/latex-dev/required/l3kernel/l3pdf.dtx
new file mode 100644
index 0000000000..b0b17f2690
--- /dev/null
+++ b/macros/latex-dev/required/l3kernel/l3pdf.dtx
@@ -0,0 +1,831 @@
+% \iffalse meta-comment
+%
+%% File: l3pdf.dtx
+%
+% Copyright(C) 2019-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% http://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel 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/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3pdf} module\\ Core PDF support^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{Objects}
+%
+% \subsection{Named objects}
+%
+% An \meta{object} name should fully expand to tokens suitable for use in a
+% label-like context.
+%
+% \begin{function}[added = 2022-08-23]{\pdf_object_new:n}
+% \begin{syntax}
+% \cs{pdf_object_new:n} \Arg{object}
+% \end{syntax}
+% Declares \meta{object} as a PDF object. The object may be referenced
+% from this point on, and written later using \cs{pdf_object_write:nnn}.
+% \end{function}
+%
+% \begin{function}[added = 2022-08-23]
+% {\pdf_object_write:nnn, \pdf_object_write:nne}
+% \begin{syntax}
+% \cs{pdf_object_write:nnn} \Arg{object} \Arg{type} \Arg{content}
+% \end{syntax}
+% Writes the \meta{content} as content of the \meta{object}. Depending on the
+% \meta{type} declared for the object, the format required for the
+% \meta{data} will vary
+% \begin{itemize}
+% \item[\texttt{array}] A space-separated list of values
+% \item[\texttt{dict}] Key--value pairs in the form
+% \texttt{/\meta{key} \meta{value}}
+% \item[\texttt{fstream}] Two brace groups: \meta{file name} and
+% \meta{file content}
+% \item[\texttt{stream}] Two brace groups: \meta{attributes (dictionary)}
+% and \meta{stream contents}
+% \end{itemize}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2021-02-10]{\pdf_object_ref:n}
+% \begin{syntax}
+% \cs{pdf_object_ref:n} \Arg{object}
+% \end{syntax}
+% Inserts the appropriate information to reference the \meta{object}
+% in for example page resource allocation. If the \meta{object} does
+% not exist then the function expands to a reference to object zero;
+% no PDF indirect object ever has this number, so this is a marker for
+% error.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2020-05-15]{\pdf_object_if_exist:n}
+% \begin{syntax}
+% \cs{pdf_object_if_exist_p:n} \Arg{object}
+% \cs{pdf_object_if_exist:nTF} \Arg{object} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether an object with name \Arg{object} has been defined.
+% \end{function}
+%
+% \subsection{Indexed objects}
+%
+% Objects can also be created using a pair of \meta{class} and \emph{index};
+% the \meta{class} argument should expand to character tokens, whilst the
+% \meta{index} is an \meta{int expr} and starts at $1$. For large
+% families of objects, this approach is more efficient than using
+% individual names.
+%
+% \begin{function}[added = 2024-04-01]{\pdf_object_new_indexed:nn}
+% \begin{syntax}
+% \cs{pdf_object_new_indexed:nn} \Arg{class} \Arg{index}
+% \end{syntax}
+% Declares a PDF object of \meta{class} and \meta{index}. The object may be
+% referenced from this point on, and written later using
+% \cs{pdf_object_write_indexed:nnnn}.
+% \end{function}
+%
+% \begin{function}[added = 2024-04-01]
+% {\pdf_object_write_indexed:nnnn, \pdf_object_write_indexed:nnne}
+% \begin{syntax}
+% \cs{pdf_object_write_indexed:nnnn} \Arg{class} \Arg{index} \Arg{type} \Arg{content}
+% \end{syntax}
+% Writes the \meta{content} as content of the object of \meta{class} and
+% \meta{index}. Depending on the
+% \meta{type} declared for the object, the format required for the
+% \meta{content} will vary
+% \begin{itemize}
+% \item[\texttt{array}] A space-separated list of values
+% \item[\texttt{dict}] Key--value pairs in the form
+% \texttt{/\meta{key} \meta{value}}
+% \item[\texttt{fstream}] Two brace groups: \meta{file name} and
+% \meta{file content}
+% \item[\texttt{stream}] Two brace groups: \meta{attributes (dictionary)}
+% and \meta{stream contents}
+% \end{itemize}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2024-04-01]{\pdf_object_ref_indexed:nn}
+% \begin{syntax}
+% \cs{pdf_object_ref_indexed:nn} \Arg{class} \Arg{index}
+% \end{syntax}
+% Inserts the appropriate information to reference the object of \meta{class}
+% and \meta{index} in for example page resource allocation. If the
+% \meta{class}/\meta{index} combination does
+% not exist then the function expands to a reference to object zero;
+% no PDF indirect object ever has this number, so this is a marker for
+% error.
+% \end{function}
+%
+% \subsection{General functions}
+%
+% \begin{function}[added = 2021-02-10]
+% {\pdf_object_unnamed_write:nn, \pdf_object_unnamed_write:ne}
+% \begin{syntax}
+% \cs{pdf_object_unnamed_write:nn} \Arg{type} \Arg{content}
+% \end{syntax}
+% Writes the \meta{content} as content of an anonymous object. Depending on the
+% \meta{type}, the format required for the \meta{data} will vary
+% \begin{itemize}
+% \item[\texttt{array}] A space-separated list of values
+% \item[\texttt{dict}] Key--value pairs in the form
+% \texttt{/\meta{key} \meta{value}}
+% \item[\texttt{fstream}] Two brace groups: \meta{attributes (dictionary)}
+% and \meta{file name}
+% \item[\texttt{stream}] Two brace groups: \meta{attributes (dictionary)}
+% and \meta{stream contents}
+% \end{itemize}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2021-02-10]{\pdf_object_ref_last:}
+% \begin{syntax}
+% \cs{pdf_object_ref_last:}
+% \end{syntax}
+% Inserts the appropriate information to reference the last \meta{object}
+% created. This is particularly useful for anonymous objects.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2021-02-10]{\pdf_pageobject_ref:n}
+% \begin{syntax}
+% \cs{pdf_pagobject_ref:n} \Arg{pageobject}
+% \end{syntax}
+% Inserts the appropriate information to reference the \meta{pageobject}.
+% \end{function}
+%
+% \section{Version}
+%
+% \begin{function}[pTF, EXP, added = 2021-02-10]{\pdf_version_compare:Nn}
+% \begin{syntax}
+% \cs{pdf_version_compare_p:Nn} \meta{comparator} \Arg{version}
+% \cs{pdf_version_compare:NnTF} \meta{comparator} \Arg{version} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Compares the version of the PDF being created with the \meta{version}
+% string specified, using the \meta{comparator}. Either the \meta{true code}
+% or \meta{false code} will be left in the output stream.
+% \end{function}
+%
+% \begin{function}[added = 2021-02-10]
+% {\pdf_version_gset:n, \pdf_version_min_gset:n}
+% \begin{syntax}
+% \cs{pdf_version_gset:n} \Arg{version}
+% \end{syntax}
+% Sets the \meta{version} of the PDF being created. The |min| version will
+% not alter the output version unless it is currently lower than the
+% \meta{version} requested.
+%
+% This function may only be used up to the point where the PDF file is
+% initialised. With dvips it sets \cs{pdf_version_major:} and \cs{pdf_version_minor:}
+% and allows to compare the values with \cs{pdf_version_compare:Nn}, but the
+% PDF version itself still has to be set with the command line option
+% |-dCompatibilityLevel| of |ps2pdf|.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2021-02-10]
+% {\pdf_version:, \pdf_version_major:, \pdf_version_minor:}
+% \begin{syntax}
+% \cs{pdf_version:}
+% \end{syntax}
+% Expands to the currently-active PDF version.
+% \end{function}
+%
+% \section{Page (media) size}
+%
+% \begin{function}[added = 2023-01-14]{\pdf_pagesize_gset:nn}
+% \begin{syntax}
+% \cs{pdf_pagesize_gset:nn} \Arg{width} \Arg{height}
+% \end{syntax}
+% Sets the page size (mediabox) of the PDF being created to the
+% \meta{width} and \meta{height}, both of which are \meta{dimexpr}.
+% \end{function}
+%
+% \section{Compression}
+%
+% \begin{function}[added = 2021-02-10]{\pdf_uncompress:}
+% \begin{syntax}
+% \cs{pdf_uncompress:}
+% \end{syntax}
+% Disables any compression of the PDF, where possible.
+%
+% This function may only be used up to the point where the PDF file is
+% initialised.
+% \end{function}
+%
+% \section{Destinations}
+%
+% Destinations are the places a link jumped too.
+% Unlike the name may suggest they don't described
+% an exact location in the PDF. Instead a destination contains a reference to
+% a page along with an instruction how to display this page.
+% The normally used \enquote{XYZ \textit{top left zoom}} for example instructs
+% the viewer to show the page with the given \textit{zoom} and
+% the top left corner at the \textit{top left} coordinates---which then gives
+% the impression that there is an anchor at this position.
+%
+% If an instruction takes a coordinate, it is calculated by the following
+% commands relative to the location the command is issued.
+% So to get a specific coordinate one has to move the command to the right place.
+%
+% \begin{function}[added = 2021-01-03]
+% {\pdf_destination:nn}
+% \begin{syntax}
+% \cs{pdf_destination:nn} \Arg{name} \Arg{type or integer}
+% \end{syntax}
+% This creates a destination. \Arg{type or integer} can be one of |fit|, |fith|,
+% |fitv|, |fitb|, |fitbh|, |fitbv|, |fitr|, |xyz|
+% or an integer representing a scale factor in percent.
+% |fitr| here gives only a lightweight version of |/FitR|:
+% The backend code defines |fitr| so that it will with pdf\LaTeX{} and
+% Lua\LaTeX{} use the coordinates of the surrounding box,
+% with \texttt{dvips} and \texttt{dvipdfmx} it falls back to |fit|.
+% For full control use \cs{pdf_destination:nnnn}.
+%
+% The keywords match to the PDF names as described in the following tabular.
+%
+% \medskip
+% \noindent\begin{tabular}{ll>{\raggedright\arraybackslash}p{6cm}}
+% \toprule
+% Keyword & PDF & Remarks \\ \midrule
+% |fit| & |/Fit|
+% & Fits the page to the window\\
+% |fith| & |/FitH| \textit{top}
+% & Fits the width of the page to the window \\
+% |fitv| & |/FitV| \textit{left}
+% & Fits the height of the page to the window \\
+% |fitb| & |/FitB|
+% & Fits the page bounding box to the window \\
+% |fitbh|& |/FitBH| \textit{top}
+% & Fits the width of the page bounding box to the window. \\
+% |fitbv|& |/FitBV| \textit{left}
+% & Fits the height of the page bounding box to the window. \\
+% |fitr| & |/FitR| \textit{left bottom right top}
+% & Fits the rectangle specified by the four coordinates to the window
+% (see above for the restrictions)\\
+% |xyz| & |/XYZ| \textit{left top} null
+% & Sets a coordinate but doesn't change the zoom.\\
+% \Arg{integer} & |/XYZ| \textit{left top zoom}
+% & Sets a coordinate and a zoom meaning \Arg{integer}\%.
+% \\\bottomrule
+% \end{tabular}
+%
+% \end{function}
+%
+% \begin{function}[added = 2021-01-17]
+% {\pdf_destination:nnnn}
+% \begin{syntax}
+% \cs{pdf_destination:nnnn} \Arg{name} \Arg{width} \Arg{height} \Arg{depth}
+% \end{syntax}
+% This creates a destination with |/FitR| type with the given dimensions relative
+% to the current location. The destination is in a box of size zero, but it doesn't
+% switch to horizontal mode.
+% \end{function}
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3pdf} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=pdf>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*tex>
+% \end{macrocode}
+%
+% \begin{variable}{\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_init_bool}
+% A boolean so we have some chance of avoiding setting things we are not
+% allowed to. As we are potentially early in the format, we have to work
+% a bit harder than ideal.
+% \begin{macrocode}
+\bool_new:N \g_@@_init_bool
+\bool_lazy_and:nnT
+ { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
+ { \tl_if_exist_p:N \@expl@finalise@setup@@@@ }
+ {
+ \tl_gput_right:Nn \@expl@finalise@setup@@@@
+ {
+ \tl_gput_right:Nn \@kernel@after@begindocument
+ { \bool_gset_true:N \g_@@_init_bool }
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Compression}
+%
+% \begin{macro}{\pdf_uncompress:}
+% Simple to do.
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_uncompress:
+ {
+ \bool_if:NF \g_@@_init_bool
+ {
+ \@@_backend_compresslevel:n { 0 }
+ \@@_backend_compress_objects:n { \c_false_bool }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Objects}
+%
+% \begin{variable}{\g_@@_backend_object_int}
+% For returning object numbers.
+% \begin{macrocode}
+\int_new:N \g_@@_backend_object_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\pdf_object_new:n}
+% \begin{macro}
+% {\pdf_object_write:nnn, \pdf_object_write:nne, \pdf_object_write:nnx}
+% \begin{macro}{\pdf_object_ref:n}
+% \begin{macro}{\__kernel_pdf_object_id:n}
+% Simple to do: all objects create a constant |int| so it is not a
+% backend-specific name.
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_object_new:n #1
+ {
+ \@@_backend_object_new:
+ \@@_object_record:nN {#1} \g_@@_backend_object_int
+ }
+\cs_new_protected:Npn \pdf_object_write:nnn #1#2#3
+ {
+ \exp_args:Ne \@@_backend_object_write:nnn
+ { \@@_object_retrieve:n {#1} } {#2} {#3}
+ \bool_gset_true:N \g_@@_init_bool
+ }
+\cs_generate_variant:Nn \pdf_object_write:nnn { nne , nnx }
+\cs_new:Npn \pdf_object_ref:n #1
+ {
+ \exp_args:Ne \@@_backend_object_ref:n
+ { \@@_object_retrieve:n {#1} }
+ }
+\cs_new:Npn \__kernel_pdf_object_id:n #1
+ {
+ \exp_args:Ne \@@_backend_object_id:n
+ { \@@_object_retrieve:n {#1} }
+ }
+%</tex>
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}{\@@_object_record:nN}
+% \begin{macro}[EXP]{\@@_object_retrieve:n}
+% \begin{macro}{ltx.pdf.object_id}
+% Object mappings are tracked in Lua for \LuaTeX{} as this makes retrieving
+% them much easier; as a result, there is a split in approaches. In Lua we
+% store values in a table indexed by name. The Lua function here is set up
+% to deal with both named and indexed objects: fits the Lua idiom well.
+% \begin{macrocode}
+%<*lua>
+
+local scan_int = token.scan_int
+local scan_string = token.scan_string
+local cprint = tex.cprint
+
+local @@_objects_named = {}
+local @@_objects_indexed = {}
+
+luacmd('@@_object_record:nN', function()
+ local name = scan_string()
+ local n = scan_int()
+ @@_objects_named[name] = n
+end,'protected','global')
+
+local function object_id(name,index)
+ if index then
+ return @@_objects_indexed[name][index] or 0
+ else
+ return @@_objects_named[name] or 0
+ end
+end
+
+luacmd('@@_object_retrieve:n', function()
+ local name = scan_string()
+ return cprint(12,tostring(object_id(name)))
+end,'global')
+
+ltx.pdf = ltx.pdf or {}
+ltx.pdf.object_id = object_id
+
+%</lua>
+% \end{macrocode}
+% Whereas in \TeX{} we use integer constants.
+% \begin{macrocode}
+%<*tex>
+\sys_if_engine_luatex:F
+ {
+ \cs_new_protected:Npn \@@_object_record:nN #1#2
+ {
+ \int_const:cn
+ { c_@@_object_ #1 _int } {#2}
+ }
+ \cs_new:Npn \@@_object_retrieve:n #1
+ {
+ \int_if_exist:cTF { c_@@_object_ #1 _int }
+ {
+ \int_use:c
+ { c_@@_object_ #1 _int }
+ }
+ { 0 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}[pTF,EXP]{\pdf_object_if_exist:n}
+% \begin{macrocode}
+\prg_new_conditional:Npnn \pdf_object_if_exist:n #1 { p , T , F , TF }
+ {
+ \int_compare:nNnTF { \@@_object_retrieve:n {#1} } = 0
+ \prg_return_false:
+ \prg_return_true:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\pdf_object_new_indexed:nn}
+% \begin{macro}{\pdf_object_write_indexed:nnnn, \pdf_object_write_indexed:nnne}
+% \begin{macro}[EXP]{\pdf_object_ref_indexed:nn}
+% \begin{macro}[EXP]{\__kernel_pdf_object_id_indexed:nn}
+% Again we split between the common code and the macro- or Lua-based
+% implementation. To make life easier for the Lua route, all of the
+% potential expressions are expanded to braced numbers.
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_object_new_indexed:nn #1#2
+ {
+ \@@_backend_object_new:
+ \@@_object_record:neN {#1}
+ { \int_eval:n {#2} } \g_@@_backend_object_int
+ }
+\cs_new_protected:Npn \pdf_object_write_indexed:nnnn #1#2#3#4
+ {
+ \exp_args:Ne \@@_backend_object_write:nnn
+ { \@@_object_retrieve:ne {#1} { \int_eval:n {#2} } } {#3} {#4}
+ \bool_gset_true:N \g_@@_init_bool
+ }
+\cs_generate_variant:Nn \pdf_object_write_indexed:nnnn { nnne }
+\cs_new:Npn \pdf_object_ref_indexed:nn #1#2
+ {
+ \exp_args:Ne \@@_backend_object_ref:n
+ { \@@_object_retrieve:ne {#1} { \int_eval:n {#2} } }
+ }
+\cs_new:Npn \__kernel_pdf_object_id_indexed:nn #1#2
+ {
+ \exp_args:Ne \@@_backend_object_id:n
+ { \@@_object_retrieve:ne {#1} { \int_eval:n {#2} } }
+ }
+%</tex>
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}{\@@_object_record:nnN, \@@_object_record:neN}
+% \begin{macro}[EXP]{\@@_object_retrieve:nn}
+% \begin{macro}{\@@_object_record:NnN}
+% \begin{macro}[EXP]{\@@_object_retrieve:Nn}
+% Again we split for Lua: the same idea as above but with nested tables.
+% As we've arranged above that the \TeX{} code passes a braced number,
+% we can use |tonumber(scan_string())| rather than |scan_int()|
+% for the index.
+% \begin{macrocode}
+%<*lua>
+
+luacmd('@@_object_record:nnN', function()
+ local name = scan_string()
+ local index = tonumber(scan_string())
+ local n = scan_int()
+ @@_objects_indexed[name] = @@_objects_indexed[name] or {}
+ @@_objects_indexed[name][index] = n
+end,'protected','global')
+
+luacmd('@@_object_retrieve:nn', function()
+ local name = scan_string()
+ local index = tonumber(scan_string())
+ return cprint(12,tostring(object_id(name,index)))
+end,'global')
+
+%</lua>
+% \end{macrocode}
+% The non-Lua approach is to divide the range into blocks, and store in integer
+% arrays that can simulate dynamic assignment.
+% \begin{macrocode}
+%<*tex>
+\sys_if_engine_luatex:F
+ {
+ \cs_new_protected:Npn \@@_object_record:nnN #1#2#3
+ {
+ \use:e
+ {
+ \@@_object_record:NnN
+ \@@_object_index_split:nn {#1} {#2}
+ \exp_not:N #3
+ }
+ }
+ \cs_new_protected:Npn \@@_object_record:NnN #1#2#3
+ {
+ \intarray_if_exist:NF #1
+ { \intarray_new:Nn #1 \c_@@_object_block_size_int }
+ \intarray_gset:Nnn #1 {#2} #3
+ }
+ \cs_new:Npn \@@_object_retrieve:nn #1#2
+ {
+ \use:e
+ {
+ \exp_not:N \@@_object_retrieve:Nn
+ \@@_object_index_split:nn {#1} {#2}
+ }
+ }
+ \cs_new:Npn \@@_object_retrieve:Nn #1#2
+ { \intarray_item:Nn #1 {#2} }
+% \end{macrocode}
+% As we want blocks to start from one, and within the block for the top value
+% to be \enquote{in} the block, we do a little bit of manipulation. By shifting
+% down by one, we keep the values \enquote{in} the block, then we adjust the
+% block/index number to get back on track.
+% \begin{macrocode}
+ \cs_new:Npn \@@_object_index_split:nn #1#2
+ {
+ \exp_not:c
+ {
+ g_@@_object_ #1 _
+ \int_eval:n
+ {
+ \int_div_truncate:nn { #2 - 1 }
+ \c_@@_object_block_size_int + 1
+ }
+ _intarray
+ }
+ {
+ \int_eval:n
+ { \int_mod:nn { #2 - 1 } \c_@@_object_block_size_int + 1 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{variable}{\c_@@_object_block_size_int}
+% Sets the block size used for managing indexed objects.
+% \begin{macrocode}
+ \int_const:Nn \c_@@_object_block_size_int { 10000 }
+ }
+% \end{macrocode}
+% \end{variable}
+% \begin{macro}{\@@_object_record:neN}
+% \begin{macro}[EXP]{\@@_object_retrieve:ne}
+% Common variants.
+% \begin{macrocode}
+\cs_generate_variant:Nn \@@_object_record:nnN { ne }
+\cs_generate_variant:Nn \@@_object_retrieve:nn { ne }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \pdf_object_unnamed_write:nn, \pdf_object_unnamed_write:ne,
+% \pdf_object_unnamed_write:nx
+% }
+% No tracking needed here.
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_object_unnamed_write:nn #1#2
+ {
+ \exp_args:Ne \@@_backend_object_now:nn {#1} {#2}
+ \bool_gset_true:N \g_@@_init_bool
+ }
+\cs_generate_variant:Nn \pdf_object_unnamed_write:nn { ne , nx }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\pdf_object_ref_last:}
+% A one-step wrapper for consistency.
+% \begin{macrocode}
+\cs_new:Npn \pdf_object_ref_last: { \@@_backend_object_last: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\pdf_pageobject_ref:n}
+% \begin{macrocode}
+\cs_new:Npn \pdf_pageobject_ref:n #1
+ { \@@_backend_pageobject_ref:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Version}
+%
+% \begin{macro}[pTF,EXP]{\pdf_version_compare:Nn}
+% \begin{macro}
+% {
+% @@_version_compare_=:w ,
+% @@_version_compare_<:w ,
+% @@_version_compare_>:w
+% }
+% To compare version, we need to split the given value then deal with both
+% major and minor version
+% \begin{macrocode}
+\prg_new_conditional:Npnn \pdf_version_compare:Nn #1#2 { p , T , F , TF }
+ { \use:c { @@_version_compare_ #1 :w } #2 . . \s_@@_stop }
+\cs_new:cpn { @@_version_compare_=:w } #1 . #2 . #3 \s_@@_stop
+ {
+ \bool_lazy_and:nnTF
+ { \int_compare_p:nNn \@@_backend_version_major: = {#1} }
+ { \int_compare_p:nNn \@@_backend_version_minor: = {#2} }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_new:cpn { @@_version_compare_<:w } #1 . #2 . #3 \s_@@_stop
+ {
+ \bool_lazy_or:nnTF
+ { \int_compare_p:nNn \@@_backend_version_major: < {#1} }
+ {
+ \bool_lazy_and_p:nn
+ { \int_compare_p:nNn \@@_backend_version_major: = {#1} }
+ { \int_compare_p:nNn \@@_backend_version_minor: < {#2} }
+ }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_new:cpn { @@_version_compare_>:w } #1 . #2 . #3 \s_@@_stop
+ {
+ \bool_lazy_or:nnTF
+ { \int_compare_p:nNn \@@_backend_version_major: > {#1} }
+ {
+ \bool_lazy_and_p:nn
+ { \int_compare_p:nNn \@@_backend_version_major: = {#1} }
+ { \int_compare_p:nNn \@@_backend_version_minor: > {#2} }
+ }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\pdf_version_gset:n, \pdf_version_min_gset:n}
+% \begin{macro}{\@@_version_gset:w}
+% Split the version and set.
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_version_gset:n #1
+ { \@@_version_gset:w #1 . . \s_@@_stop }
+\cs_new_protected:Npn \pdf_version_min_gset:n #1
+ {
+ \pdf_version_compare:NnT < {#1}
+ { \@@_version_gset:w #1 . . \s_@@_stop }
+ }
+\cs_new_protected:Npn \@@_version_gset:w #1 . #2 . #3\s_@@_stop
+ {
+ \bool_if:NF \g_@@_init_bool
+ {
+ \@@_backend_version_major_gset:n {#1}
+ \@@_backend_version_minor_gset:n {#2}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\pdf_version:, \pdf_version_major:, \pdf_version_minor:}
+% Wrappers.
+% \begin{macrocode}
+\cs_new:Npn \pdf_version:
+ { \@@_backend_version_major: . \@@_backend_version_minor: }
+\cs_new:Npn \pdf_version_major: { \@@_backend_version_major: }
+\cs_new:Npn \pdf_version_minor: { \@@_backend_version_minor: }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Page size}
+%
+% \begin{macro}{\pdf_pagesize_gset:nn}
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_pagesize_gset:nn #1#2
+ { \@@_backend_pagesize_gset:nn {#1} {#2} }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Destinations}
+%
+% \begin{macro}{\pdf_destination:nn}
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_destination:nn #1#2
+ { \@@_backend_destination:nn {#1} {#2} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\pdf_destination:nnnn}
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_destination:nnnn #1#2#3#4
+ {
+ \hbox_to_zero:n
+ { \@@_backend_destination:nnnn {#1} {#2} {#3} {#4} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{PDF Page size (media box)}
+%
+% Everything here is delayed to the start of the document so that the
+% backend will definitely be loaded.
+% \begin{macrocode}
+\cs_if_exist:NT \@kernel@before@begindocument
+ {
+ \tl_gput_right:Nn \@kernel@before@begindocument
+ {
+ \bool_lazy_all:nT
+ {
+ { \cs_if_exist_p:N \stockheight }
+ { \cs_if_exist_p:N \stockwidth }
+ { \cs_if_exist_p:N \IfDocumentMetadataTF }
+ { \IfDocumentMetadataTF { \c_true_bool } { \c_false_bool } }
+ { \int_compare_p:nNn \tex_mag:D = { 1000 } }
+ }
+ {
+ \bool_lazy_and:nnTF
+ { \dim_compare_p:nNn \stockheight > { 0pt } }
+ { \dim_compare_p:nNn \stockwidth > { 0pt } }
+ {
+ \@@_backend_pagesize_gset:nn
+ \stockwidth \stockheight
+ }
+ {
+ \bool_lazy_or:nnF
+ { \dim_compare_p:nNn \stockheight < { 0pt } }
+ { \dim_compare_p:nNn \stockwidth < { 0pt } }
+ {
+ \bool_lazy_and:nnT
+ { \dim_compare_p:nNn \paperheight > { 0pt } }
+ { \dim_compare_p:nNn \paperwidth > { 0pt } }
+ {
+ \@@_backend_pagesize_gset:nn
+ \paperwidth \paperheight
+ }
+ }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</tex>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex