% \iffalse meta-comment % %% File: l3backend-pdf.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 % % https://www.latex-project.org/lppl.txt % % This file is part of the "l3backend 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,kernel]{l3doc} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{l3backend-pdf} module\\ Backend PDF features^^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-03-14} % % \maketitle % % \begin{documentation} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3backend-pdf} implementation} % % \begin{macrocode} %<*package> %<@@=pdf> % \end{macrocode} % % Setting up PDF resources is a complex area with only limited documentation % in the engine manuals. The following code builds heavily on existing ideas % from \pkg{hyperref} work by Sebastian Rahtz and Heiko Oberdiek, and % significant contributions by Alexander Grahn, in addition to the specific % code referenced a various points. % % \subsection{Shared code} % % A very small number of items that belong at the backend level but which % are common to most backends. % % \begin{macrocode} %<*!dvisvgm> % \end{macrocode} % % \begin{variable}{\l_@@_internal_box} % \begin{macrocode} \box_new:N \l_@@_internal_box % \end{macrocode} % \end{variable} % % \begin{macrocode} % % \end{macrocode} % % \subsection{\texttt{dvips} backend} % % \begin{macrocode} %<*dvips> % \end{macrocode} % % \begin{macro}{\@@_backend_pdfmark:n, \@@_backend_pdfmark:e} % Used often enough it should be a separate function. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_pdfmark:n #1 { \__kernel_backend_postscript:n { mark #1 ~ pdfmark } } \cs_generate_variant:Nn \@@_backend_pdfmark:n { e } % \end{macrocode} % \end{macro} % % \subsubsection{Catalogue entries} % % \begin{macro}{\@@_backend_catalog_gput:nn, \@@_backend_info_gput:nn} % \begin{macrocode} \cs_new_protected:Npn \@@_backend_catalog_gput:nn #1#2 { \@@_backend_pdfmark:n { { Catalog } << /#1 ~ #2 >> /PUT } } \cs_new_protected:Npn \@@_backend_info_gput:nn #1#2 { \@@_backend_pdfmark:n { /#1 ~ #2 /DOCINFO } } % \end{macrocode} % \end{macro} % % \subsubsection{Objects} % % \begin{variable}{\g_@@_backend_object_int} % For tracking objects. % \begin{macrocode} \int_new:N \g_@@_backend_object_int % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_backend_object_new:n} % \begin{macro}[EXP]{\@@_backend_object_ref:n} % \begin{macrocode} \cs_new_protected:Npn \@@_backend_object_new:n #1 { \int_gincr:N \g_@@_backend_object_int \int_const:cn { c_@@_object_ \tl_to_str:n {#1} _int } { \g_@@_backend_object_int } } \cs_new:Npn \@@_backend_object_ref:n #1 { { pdf.obj \int_use:c { c_@@_object_ \tl_to_str:n {#1} _int } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro} % { % \@@_backend_object_write:nnn, \@@_backend_object_write:nne, % \@@_backend_object_write_aux:nnn % } % \begin{macro} % { % \@@_backend_object_write_array:nn , % \@@_backend_object_write_dict:nn , % \@@_backend_object_write_fstream:nn , % \@@_backend_object_write_stream:nn % } % \begin{macro}{\@@_backend_object_write_stream:nnn} % This is where we choose the actual type: some work to get things % right. To allow code sharing with the anonymous version, we use an % auxiliary. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_object_write:nnn #1#2#3 { \@@_backend_object_write_aux:nnn { \@@_backend_object_ref:n {#1} } {#2} {#3} } \cs_generate_variant:Nn \@@_backend_object_write:nnn { nne } \cs_new_protected:Npn \@@_backend_object_write_aux:nnn #1#2#3 { \@@_backend_pdfmark:e { /_objdef ~ #1 /type \str_case:nn {#2} { { array } { /array } { dict } { /dict } { fstream } { /stream } { stream } { /stream } } /OBJ } \use:c { @@_backend_object_write_ #2 :nn } {#1} {#3} } \cs_new_protected:Npn \@@_backend_object_write_array:nn #1#2 { \@@_backend_pdfmark:e { #1 ~0~ [ ~ \exp_not:n {#2} ~ ] ~ /PUTINTERVAL } } \cs_new_protected:Npn \@@_backend_object_write_dict:nn #1#2 { \@@_backend_pdfmark:e { #1 << \exp_not:n {#2} >> /PUT } } \cs_new_protected:Npn \@@_backend_object_write_fstream:nn #1#2 { \exp_args:Ne \@@_backend_object_write_fstream:nnn {#1} #2 } \cs_new_protected:Npn \@@_backend_object_write_fstream:nnn #1#2#3 { \__kernel_backend_postscript:n { SDict ~ begin ~ mark ~ #1 ~ << #2 >> /PUT ~ pdfmark ~ mark ~ #1 ~ ( #3 )~ ( r )~ file ~ /PUT ~ pdfmark ~ end } } \cs_new_protected:Npn \@@_backend_object_write_stream:nn #1#2 { \exp_args:Ne \@@_backend_object_write_stream:nnn {#1} #2 } \cs_new_protected:Npn \@@_backend_object_write_stream:nnn #1#2#3 { \__kernel_backend_postscript:n { mark ~ #1 ~ ( #3 ) /PUT ~ pdfmark ~ mark ~ #1 ~ << #2 >> /PUT ~ pdfmark } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_backend_object_now:nn, \@@_backend_object_now:ne} % No anonymous objects, so things are done manually. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_object_now:nn #1#2 { \int_gincr:N \g_@@_backend_object_int \@@_backend_object_write_aux:nnn { { pdf.obj \int_use:N \g_@@_backend_object_int } } {#1} {#2} } \cs_generate_variant:Nn \@@_backend_object_now:nn { ne } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_backend_object_last:} % Much like the annotation version. % \begin{macrocode} \cs_new:Npn \@@_backend_object_last: { { pdf.obj \int_use:N \g_@@_backend_object_int } } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_backend_pageobject_ref:n} % Page references are easy in \texttt{dvips}. % \begin{macrocode} \cs_new:Npn \@@_backend_pageobject_ref:n #1 { { Page #1 } } % \end{macrocode} % \end{macro} % % \subsubsection{Annotations} % % In \texttt{dvips}, annotations have to be constructed manually. As such, % we need the object code above for some definitions. % % \begin{variable}{\l_@@_backend_content_box} % The content of an annotation. % \begin{macrocode} \box_new:N \l_@@_backend_content_box % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_backend_model_box} % For creating model sizing for links. % \begin{macrocode} \box_new:N \l_@@_backend_model_box % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_backend_annotation_int} % Needed as objects which are not annotations could be created. % \begin{macrocode} \int_new:N \g_@@_backend_annotation_int % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_backend_annotation:nnnn} % Annotations are objects, but we track them separately. Notably, they are % not in the object data lists. Here, to get the co-ordinates of the % annotation, we need to have the data collected at the PostScript level. % That requires a bit of box trickery (effectively a \LaTeXe{} |picture| % of zero size). Once the data is collected, use it to set up the annotation % border. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_annotation:nnnn #1#2#3#4 { \exp_args:Nf \@@_backend_annotation_aux:nnnn { \dim_eval:n {#1} } {#2} {#3} {#4} } \cs_new_protected:Npn \@@_backend_annotation_aux:nnnn #1#2#3#4 { \box_move_down:nn {#3} { \hbox:n { \__kernel_backend_postscript:n { pdf.save.ll } } } \box_move_up:nn {#2} { \hbox:n { \__kernel_kern:n {#1} \__kernel_backend_postscript:n { pdf.save.ur } \__kernel_kern:n { -#1 } } } \int_gincr:N \g_@@_backend_object_int \int_gset_eq:NN \g_@@_backend_annotation_int \g_@@_backend_object_int \@@_backend_pdfmark:e { /_objdef { pdf.obj \int_use:N \g_@@_backend_object_int } pdf.rect #4 ~ /ANN } } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_backend_annotation_last:} % Provide the last annotation we created: could get tricky of course if % other packages are loaded. % \begin{macrocode} \cs_new:Npn \@@_backend_annotation_last: { { pdf.obj \int_use:N \g_@@_backend_annotation_int } } % \end{macrocode} % \end{macro} % % \begin{variable}{\g_@@_backend_link_int} % To track annotations which are links. % \begin{macrocode} \int_new:N \g_@@_backend_link_int % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_backend_link_dict_tl} % To pass information to the end-of-link function. % \begin{macrocode} \tl_new:N \g_@@_backend_link_dict_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_backend_link_sf_int} % Needed to save/restore space factor, which is needed to deal with the face % we need a box. % \begin{macrocode} \int_new:N \g_@@_backend_link_sf_int % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_backend_link_math_bool} % Needed to save/restore math mode. % \begin{macrocode} \bool_new:N \g_@@_backend_link_math_bool % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_backend_link_bool} % Track link formation: we cannot nest at all. % \begin{macrocode} \bool_new:N \g_@@_backend_link_bool % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_breaklink_pdfmark_tl} % Swappable content for link breaking. % \begin{macrocode} \tl_new:N \l_@@_breaklink_pdfmark_tl \tl_set:Nn \l_@@_breaklink_pdfmark_tl { pdfmark } % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_breaklink_postscript:n} % To allow dropping material unless link breaking is active. % \begin{macrocode} \cs_new_protected:Npn \@@_breaklink_postscript:n #1 { } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_breaklink_usebox:N} % Swappable box unpacking or use. % \begin{macrocode} \cs_new_eq:NN \@@_breaklink_usebox:N \box_use:N % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_backend_link_begin_goto:nnw, \@@_backend_link_begin_user:nnw} % \begin{macro}{\@@_backend_link:nw, \@@_backend_link_aux:nw} % \begin{macro}{\@@_backend_link_end:, \@@_backend_link_end_aux:} % \begin{macro}{\@@_backend_link_minima:} % \begin{macro}{\@@_backend_link_outerbox:n} % \begin{macro}{\@@_backend_link_sf_save:, \@@_backend_link_sf_restore:} % Links are crated like annotations but with dedicated code to allow for % adjusting the size of the rectangle. In contrast to \pkg{hyperref}, we % grab the link content as a box which can then unbox: this allows the same % interface as for \pdfTeX{}. % % Notice that the link setup here uses |/Action| not |/A|. That is because % Distiller \emph{requires} this trigger word, rather than a \enquote{raw} % PDF dictionary key (Ghostscript can handle either form). % % Taking the idea of |evenboxes| from \pkg{hypdvips}, we implement a minimum % box height and depth for link placement. This means that \enquote{underlining} % with a hyperlink will generally give an even appearance. However, to ensure % that the full content is always above the link border, we do not allow % this to be negative (contrast \pkg{hypdvips} approach). The result should % be similar to \pdfTeX{} in the vast majority of foreseeable cases. % % The object number for a link is saved separately from the rest of the % dictionary as this allows us to insert it just once, at either an % unbroken link or only in the first line of a broken one. That makes the % code clearer but also avoids a low-level PostScript error with the code % as taken from \pkg{hypdvips}. % % Getting the outer dimensions of the text area may be better using a two-pass % approach and |\tex_savepos:D|. That plus generic mode are still to re-examine. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_link_begin_goto:nnw #1#2 { \@@_backend_link_begin:nw { #1 /Subtype /Link /Action << /S /GoTo /D ( #2 ) >> } } \cs_new_protected:Npn \@@_backend_link_begin_user:nnw #1#2 { \@@_backend_link_begin:nw {#1#2} } \cs_new_protected:Npn \@@_backend_link_begin:nw #1 { \bool_if:NF \g_@@_backend_link_bool { \@@_backend_link_begin_aux:nw {#1} } } % \end{macrocode} % The definition of |pdf.link.dict| here is needed as there is code in the % PostScript headers for breaking links, and that can only work with this % available. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_link_begin_aux:nw #1 { \bool_gset_true:N \g_@@_backend_link_bool \__kernel_backend_postscript:n { /pdf.link.dict ( #1 ) def } \tl_gset:Nn \g_@@_backend_link_dict_tl {#1} \@@_backend_link_sf_save: \mode_if_math:TF { \bool_gset_true:N \g_@@_backend_link_math_bool } { \bool_gset_false:N \g_@@_backend_link_math_bool } \hbox_set:Nw \l_@@_backend_content_box \@@_backend_link_sf_restore: \bool_if:NT \g_@@_backend_link_math_bool { \c_math_toggle_token } } \cs_new_protected:Npn \@@_backend_link_end: { \bool_if:NT \g_@@_backend_link_bool { \@@_backend_link_end_aux: } } \cs_new_protected:Npn \@@_backend_link_end_aux: { \bool_if:NT \g_@@_backend_link_math_bool { \c_math_toggle_token } \@@_backend_link_sf_save: \hbox_set_end: \@@_backend_link_minima: \hbox_set:Nn \l_@@_backend_model_box { Gg } \exp_args:Ne \@@_backend_link_outerbox:n { \int_if_odd:nTF { \value { page } } { \oddsidemargin } { \evensidemargin } } \box_move_down:nn { \box_dp:N \l_@@_backend_content_box } { \hbox:n { \__kernel_backend_postscript:n { pdf.save.linkll } } } \@@_breaklink_postscript:n { pdf.bordertracking.begin } \@@_breaklink_usebox:N \l_@@_backend_content_box \@@_breaklink_postscript:n { pdf.bordertracking.end } \box_move_up:nn { \box_ht:N \l_@@_backend_content_box } { \hbox:n { \__kernel_backend_postscript:n { pdf.save.linkur } } } \int_gincr:N \g_@@_backend_object_int \int_gset_eq:NN \g_@@_backend_link_int \g_@@_backend_object_int \__kernel_backend_postscript:e { mark /_objdef { pdf.obj \int_use:N \g_@@_backend_link_int } \g_@@_backend_link_dict_tl \c_space_tl pdf.rect /ANN ~ \l_@@_breaklink_pdfmark_tl } \@@_backend_link_sf_restore: \bool_gset_false:N \g_@@_backend_link_bool } \cs_new_protected:Npn \@@_backend_link_minima: { \hbox_set:Nn \l_@@_backend_model_box { Gg } \__kernel_backend_postscript:e { /pdf.linkdp.pad ~ \dim_to_decimal:n { \dim_max:nn { \box_dp:N \l_@@_backend_model_box - \box_dp:N \l_@@_backend_content_box } { 0pt } } ~ pdf.pt.dvi ~ def /pdf.linkht.pad ~ \dim_to_decimal:n { \dim_max:nn { \box_ht:N \l_@@_backend_model_box - \box_ht:N \l_@@_backend_content_box } { 0pt } } ~ pdf.pt.dvi ~ def } } \cs_new_protected:Npn \@@_backend_link_outerbox:n #1 { \__kernel_backend_postscript:e { /pdf.outerbox [ \dim_to_decimal:n {#1} ~ \dim_to_decimal:n { -\box_dp:N \l_@@_backend_model_box } ~ \dim_to_decimal:n { #1 + \textwidth } ~ \dim_to_decimal:n { \box_ht:N \l_@@_backend_model_box } ] [ exch { pdf.pt.dvi } forall ] def /pdf.baselineskip ~ \dim_to_decimal:n { \tex_baselineskip:D } ~ dup ~ 0 ~ gt { pdf.pt.dvi ~ def } { pop ~ pop } ifelse } } \cs_new_protected:Npn \@@_backend_link_sf_save: { \int_gset:Nn \g_@@_backend_link_sf_int { \mode_if_horizontal:TF { \tex_spacefactor:D } { 0 } } } \cs_new_protected:Npn \@@_backend_link_sf_restore: { \mode_if_horizontal:T { \int_compare:nNnT \g_@@_backend_link_sf_int > { 0 } { \int_set_eq:NN \tex_spacefactor:D \g_@@_backend_link_sf_int } } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % Hooks to allow link breaking: something will be needed in format mode % at some stage. At present this code is disabled as there is an open % question about the name of the hook: to be resolved at the \LaTeXe{} % end. % \begin{macrocode} \use_none:n { \cs_if_exist:NT \@makecol@hook { \tl_put_right:Nn \@makecol@hook { \box_if_empty:NF \l_shipout_box { \vbox_set:Nn \l_shipout_box { \__kernel_backend_postscript:n { pdf.globaldict /pdf.brokenlink.rect ~ known { pdf.bordertracking.continue } if } \vbox_unpack_drop:N \l_shipout_box \__kernel_backend_postscript:n { pdf.bordertracking.endpage } } } } \tl_set:Nn \l_@@_breaklink_pdfmark_tl { pdf.pdfmark } \cs_set_eq:NN \@@_breaklink_postscript:n \__kernel_backend_postscript:n \cs_set_eq:NN \@@_breaklink_usebox:N \hbox_unpack:N } } % \end{macrocode} % % \begin{macro}{\@@_backend_link_last:} % The same as annotations, but with a custom integer. % \begin{macrocode} \cs_new:Npn \@@_backend_link_last: { { pdf.obj \int_use:N \g_@@_backend_link_int } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_backend_link_margin:n} % Convert to big points and pass to PostScript. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_link_margin:n #1 { \__kernel_backend_postscript:e { /pdf.linkmargin { \dim_to_decimal:n {#1} ~ pdf.pt.dvi } def } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_backend_destination:nn} % \begin{macro}{\@@_backend_destination:nnnn, \@@_backend_destination_aux:nnnn} % Here, we need to turn the zoom into a scale. We also need to know where % the current anchor point actually is: worked out in PostScript. For the % rectangle version, we have a bit more PostScript: we need two points. % fitr without rule spec doesn't work, so it falls back to \texttt{/Fit} here. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_destination:nn #1#2 { \__kernel_backend_postscript:n { pdf.dest.anchor } \@@_backend_pdfmark:e { /View [ \str_case:nnF {#2} { { xyz } { /XYZ ~ pdf.dest.point ~ null } { fit } { /Fit } { fitb } { /FitB } { fitbh } { /FitBH ~ pdf.dest.y } { fitbv } { /FitBV ~ pdf.dest.x } { fith } { /FitH ~ pdf.dest.y } { fitv } { /FitV ~ pdf.dest.x } { fitr } { /Fit } } { /XYZ ~ pdf.dest.point ~ \fp_eval:n { (#2) / 100 } } ] /Dest ( \exp_not:n {#1} ) cvn /DEST } } \cs_new_protected:Npn \@@_backend_destination:nnnn #1#2#3#4 { \exp_args:Ne \@@_backend_destination_aux:nnnn { \dim_eval:n {#2} } {#1} {#3} {#4} } \cs_new_protected:Npn \@@_backend_destination_aux:nnnn #1#2#3#4 { \vbox_to_zero:n { \__kernel_kern:n {#4} \hbox:n { \__kernel_backend_postscript:n { pdf.save.ll } } \tex_vss:D } \__kernel_kern:n {#1} \vbox_to_zero:n { \__kernel_kern:n { -#3 } \hbox:n { \__kernel_backend_postscript:n { pdf.save.ur } } \tex_vss:D } \__kernel_kern:n { -#1 } \@@_backend_pdfmark:n { /View [ /FitR ~ pdf.llx ~ pdf.lly ~ pdf.dest2device ~ pdf.urx ~ pdf.ury ~ pdf.dest2device ] /Dest ( #2 ) cvn /DEST } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsubsection{Structure} % % \begin{macro}{\@@_backend_compresslevel:n} % \begin{macro}{\@@_backend_compress_objects:n} % Doable for the usual \texttt{ps2pdf} method. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_compresslevel:n #1 { \int_compare:nNnT {#1} = 0 { \__kernel_backend_literal_postscript:n { /setdistillerparams ~ where { pop << /CompressPages ~ false >> setdistillerparams } if } } } \cs_new_protected:Npn \@@_backend_compress_objects:n #1 { \bool_if:nF {#1} { \__kernel_backend_literal_postscript:n { /setdistillerparams ~ where { pop << /CompressStreams ~ false >> setdistillerparams } if } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro} % {\@@_backend_version_major_gset:n, \@@_backend_version_minor_gset:n} % \begin{macrocode} \cs_new_protected:Npn \@@_backend_version_major_gset:n #1 { \cs_gset:Npe \@@_backend_version_major: { \int_eval:n {#1} } } \cs_new_protected:Npn \@@_backend_version_minor_gset:n #1 { \cs_gset:Npe \@@_backend_version_minor: { \int_eval:n {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_backend_version_major:, \@@_backend_version_minor:} % Data not available! % \begin{macrocode} \cs_new:Npn \@@_backend_version_major: { -1 } \cs_new:Npn \@@_backend_version_minor: { -1 } % \end{macrocode} % \end{macro} % % \subsubsection{Marked content} % % \begin{macro}{\@@_backend_bdc:nn} % \begin{macro}{\@@_backend_emc:} % Simple wrappers. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_bdc:nn #1#2 { \@@_backend_pdfmark:n { /#1 ~ #2 /BDC } } \cs_new_protected:Npn \@@_backend_emc: { \@@_backend_pdfmark:n { /EMC } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \subsection{\LuaTeX{} and \pdfTeX{} backend} % % \begin{macrocode} %<*luatex|pdftex> % \end{macrocode} % % \subsubsection{Annotations} % % \begin{macro}{\@@_backend_annotation:nnnn} % Simply pass the raw data through, just dealing with evaluation of dimensions. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_annotation:nnnn #1#2#3#4 { %<*luatex> \tex_pdfextension:D annot ~ % %<*pdftex> \tex_pdfannot:D % width ~ \dim_eval:n {#1} ~ height ~ \dim_eval:n {#2} ~ depth ~ \dim_eval:n {#3} ~ {#4} } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_backend_annotation_last:} % A tiny amount of extra data gets added here; we use \texttt{x}-type % expansion to get the space in the right place and form. The \enquote{extra} % space in the \LuaTeX{} version is \emph{required} as it is consumed in % finding the end of the keyword. % \begin{macrocode} \cs_new:Npe \@@_backend_annotation_last: { \exp_not:N \int_value:w %<*luatex> \exp_not:N \tex_pdffeedback:D lastannot ~ % %<*pdftex> \exp_not:N \tex_pdflastannot:D % \c_space_tl 0 ~ R } % \end{macrocode} % \end{macro} % % \begin{macro} % {\@@_backend_link_begin_goto:nnw, \@@_backend_link_begin_user:nnw} % \begin{macro}{\@@_backend_link_begin:nnnw} % \begin{macro}{\@@_backend_link_end:} % Links are all created using the same internals. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_link_begin_goto:nnw #1#2 { \@@_backend_link_begin:nnnw {#1} { goto~name } {#2} } \cs_new_protected:Npn \@@_backend_link_begin_user:nnw #1#2 { \@@_backend_link_begin:nnnw {#1} { user } {#2} } \cs_new_protected:Npn \@@_backend_link_begin:nnnw #1#2#3 { %<*luatex> \tex_pdfextension:D startlink ~ % %<*pdftex> \tex_pdfstartlink:D % attr {#1} #2 {#3} } \cs_new_protected:Npn \@@_backend_link_end: { %<*luatex> \tex_pdfextension:D endlink \scan_stop: % %<*pdftex> \tex_pdfendlink:D % } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_backend_link_last:} % Formatted for direct use. % \begin{macrocode} \cs_new:Npe \@@_backend_link_last: { \exp_not:N \int_value:w %<*luatex> \exp_not:N \tex_pdffeedback:D lastlink ~ % %<*pdftex> \exp_not:N \tex_pdflastlink:D % \c_space_tl 0 ~ R } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_backend_link_margin:n} % A simple task: pass the data to the primitive. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_link_margin:n #1 { %<*luatex> \tex_pdfvariable:D linkmargin % %<*pdftex> \tex_pdflinkmargin:D % \dim_eval:n {#1} \scan_stop: } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_backend_destination:nn} % \begin{macro}{\@@_backend_destination:nnnn} % A simple task: pass the data to the primitive. The |\scan_stop:| deals % with the danger of an unterminated keyword. The zoom given here is a % percentage, but we need to pass it as \emph{per mille}. The rectangle % version is also easy as everything is build in. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_destination:nn #1#2 { %<*luatex> \tex_pdfextension:D dest ~ % %<*pdftex> \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: } \cs_new_protected:Npn \@@_backend_destination:nnnn #1#2#3#4 { %<*luatex> \tex_pdfextension:D dest ~ % %<*pdftex> \tex_pdfdest:D % name {#1} fitr ~ width \dim_eval:n {#2} ~ height \dim_eval:n {#3} ~ depth \dim_eval:n {#4} \scan_stop: } % \end{macrocode} % \end{macro} % \end{macro} % % \subsubsection{Catalogue entries} % % \begin{macro}{\@@_backend_catalog_gput:nn, \@@_backend_info_gput:nn} % \begin{macrocode} \cs_new_protected:Npn \@@_backend_catalog_gput:nn #1#2 { %<*luatex> \tex_pdfextension:D catalog % %<*pdftex> \tex_pdfcatalog:D % { / #1 ~ #2 } } \cs_new_protected:Npn \@@_backend_info_gput:nn #1#2 { %<*luatex> \tex_pdfextension:D info % %<*pdftex> \tex_pdfinfo:D % { / #1 ~ #2 } } % \end{macrocode} % \end{macro} % % \subsubsection{Objects} % % \begin{variable}{\g_@@_backend_object_prop} % For tracking objects to allow finalisation. % \begin{macrocode} \prop_new:N \g_@@_backend_object_prop % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_backend_object_new:n} % \begin{macro}[EXP]{\@@_backend_object_ref:n} % Declaring objects means reserving at the PDF level plus starting % tracking. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_object_new:n #1 { %<*luatex> \tex_pdfextension:D obj ~ % %<*pdftex> \tex_pdfobj:D % reserveobjnum ~ \int_const:cn { c_@@_object_ \tl_to_str:n {#1} _int } %<*luatex> { \tex_pdffeedback:D lastobj } % %<*pdftex> { \tex_pdflastobj:D } % } \cs_new:Npn \@@_backend_object_ref:n #1 { \int_use:c { c_@@_object_ \tl_to_str:n {#1} _int } ~ 0 ~ R } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_backend_object_write:nnn, \@@_backend_object_write:nne} % \begin{macro}[EXP]{\@@_backend_object_write:nn} % \begin{macro}[EXP]{\@@_exp_not_i:nn, \@@_exp_not_ii:nn} % Writing the data needs a little information about the structure of the % object. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_object_write:nnn #1#2#3 { %<*luatex> \tex_immediate:D \tex_pdfextension:D obj ~ % %<*pdftex> \tex_immediate:D \tex_pdfobj:D % useobjnum ~ \int_use:c { c_@@_object_ \tl_to_str:n {#1} _int } \@@_backend_object_write:nn {#2} {#3} } \cs_new:Npn \@@_backend_object_write:nn #1#2 { \str_case:nn {#1} { { array } { { [ ~ \exp_not:n {#2} ~ ] } } { dict } { { << ~ \exp_not:n {#2} ~ >> } } { fstream } { stream ~ attr ~ { \@@_exp_not_i:nn #2 } ~ file ~ { \@@_exp_not_ii:nn #2 } } { stream } { stream ~ attr ~ { \@@_exp_not_i:nn #2 } ~ { \@@_exp_not_ii:nn #2 } } } } \cs_generate_variant:Nn \@@_backend_object_write:nnn { nne } \cs_new:Npn \@@_exp_not_i:nn #1#2 { \exp_not:n {#1} } \cs_new:Npn \@@_exp_not_ii:nn #1#2 { \exp_not:n {#2} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_backend_object_now:nn, \@@_backend_object_now:ne} % Much like writing, but direct creation. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_object_now:nn #1#2 { %<*luatex> \tex_immediate:D \tex_pdfextension:D obj ~ % %<*pdftex> \tex_immediate:D \tex_pdfobj:D % \@@_backend_object_write:nn {#1} {#2} } \cs_generate_variant:Nn \@@_backend_object_now:nn { ne } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_backend_object_last:} % Much like annotation. % \begin{macrocode} \cs_new:Npe \@@_backend_object_last: { \exp_not:N \int_value:w %<*luatex> \exp_not:N \tex_pdffeedback:D lastobj ~ % %<*pdftex> \exp_not:N \tex_pdflastobj:D % \c_space_tl 0 ~ R } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_backend_pageobject_ref:n} % The usual wrapper situation; the three spaces here are essential. % \begin{macrocode} \cs_new:Npe \@@_backend_pageobject_ref:n #1 { \exp_not:N \int_value:w %<*luatex> \exp_not:N \tex_pdffeedback:D pageref % %<*pdftex> \exp_not:N \tex_pdfpageref:D % \c_space_tl #1 \c_space_tl \c_space_tl \c_space_tl 0 ~ R } % \end{macrocode} % \end{macro} % % \subsubsection{Structure} % % \begin{macro}{\@@_backend_compresslevel:n} % \begin{macro}{\@@_backend_compress_objects:n} % \begin{macro}{\@@_backend_objcompresslevel:n} % Simply pass data to the engine. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_compresslevel:n #1 { \tex_global:D %<*luatex> \tex_pdfvariable:D compresslevel % %<*pdftex> \tex_pdfcompresslevel:D % \int_value:w \int_eval:n {#1} \scan_stop: } \cs_new_protected:Npn \@@_backend_compress_objects:n #1 { \bool_if:nTF {#1} { \@@_backend_objcompresslevel:n { 2 } } { \@@_backend_objcompresslevel:n { 0 } } } \cs_new_protected:Npn \@@_backend_objcompresslevel:n #1 { \tex_global:D %<*luatex> \tex_pdfvariable:D objcompresslevel % %<*pdftex> \tex_pdfobjcompresslevel:D % #1 \scan_stop: } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro} % {\@@_backend_version_major_gset:n, \@@_backend_version_minor_gset:n} % The availability of the primitive is not universal, so we have to test % at load time. % \begin{macrocode} \cs_new_protected:Npe \@@_backend_version_major_gset:n #1 { %<*luatex> \int_compare:nNnT \tex_luatexversion:D > { 106 } { \exp_not:N \tex_global:D \tex_pdfvariable:D majorversion \exp_not:N \int_eval:n {#1} \scan_stop: } % %<*pdftex> \cs_if_exist:NT \tex_pdfmajorversion:D { \exp_not:N \tex_global:D \tex_pdfmajorversion:D \exp_not:N \int_eval:n {#1} \scan_stop: } % } \cs_new_protected:Npn \@@_backend_version_minor_gset:n #1 { \tex_global:D %<*luatex> \tex_pdfvariable:D minorversion % %<*pdftex> \tex_pdfminorversion:D % \int_eval:n {#1} \scan_stop: } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_backend_version_major:, \@@_backend_version_minor:} % As above. % \begin{macrocode} \cs_new:Npe \@@_backend_version_major: { %<*luatex> \int_compare:nNnTF \tex_luatexversion:D > { 106 } { \exp_not:N \tex_the:D \tex_pdfvariable:D majorversion } { 1 } % %<*pdftex> \cs_if_exist:NTF \tex_pdfmajorversion:D { \exp_not:N \tex_the:D \tex_pdfmajorversion:D } { 1 } % } \cs_new:Npn \@@_backend_version_minor: { \tex_the:D %<*luatex> \tex_pdfvariable:D minorversion % %<*pdftex> \tex_pdfminorversion:D % } % \end{macrocode} % \end{macro} % % \subsubsection{Marked content} % % \begin{macro}{\@@_backend_bdc:nn} % \begin{macro}{\@@_backend_emc:} % Simple wrappers. May need refinement: see % \url{https://chat.stackexchange.com/transcript/message/49970158#49970158}. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_bdc:nn #1#2 { \__kernel_backend_literal_page:n { /#1 ~ #2 ~ BDC } } \cs_new_protected:Npn \@@_backend_emc: { \__kernel_backend_literal_page:n { EMC } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \subsection{\texttt{dvipdfmx} backend} % % \begin{macrocode} %<*dvipdfmx|xetex> % \end{macrocode} % % \begin{macro}{\@@_backend:n, \@@_backend:e} % A generic function for the backend PDF specials: used where we can. % \begin{macrocode} \cs_new_protected:Npe \@@_backend:n #1 { \__kernel_backend_literal:n { pdf: #1 } } \cs_generate_variant:Nn \@@_backend:n { e } % \end{macrocode} % \end{macro} % % \subsubsection{Catalogue entries} % % \begin{macro}{\@@_backend_catalog_gput:nn, \@@_backend_info_gput:nn} % \begin{macrocode} \cs_new_protected:Npn \@@_backend_catalog_gput:nn #1#2 { \@@_backend:n { put ~ @catalog << /#1 ~ #2 >> } } \cs_new_protected:Npn \@@_backend_info_gput:nn #1#2 { \@@_backend:n { docinfo << /#1 ~ #2 >> } } % \end{macrocode} % \end{macro} % % \subsubsection{Objects} % % \begin{variable}{\g_@@_backend_object_int, \g_@@_backend_object_prop} % For tracking objects to allow finalisation. % \begin{macrocode} \int_new:N \g_@@_backend_object_int \prop_new:N \g_@@_backend_object_prop % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_backend_object_new:n} % \begin{macro}[EXP]{\@@_backend_object_ref:n} % Objects are tracked at the macro level, but we don't have to do anything % at this stage. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_object_new:n #1 { \int_gincr:N \g_@@_backend_object_int \int_const:cn { c_@@_object_ \tl_to_str:n {#1} _int } { \g_@@_backend_object_int } } \cs_new:Npn \@@_backend_object_ref:n #1 { @pdf.obj \int_use:c { c_@@_object_ \tl_to_str:n {#1} _int } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_backend_object_write:nnn, \@@_backend_object_write:nne} % \begin{macro} % { % \@@_backend_object_write_array:nn , % \@@_backend_object_write_dict:nn , % \@@_backend_object_write_fstream:nn , % \@@_backend_object_write_stream:nn % } % \begin{macro}{\@@_backend_object_write_stream:nnnn} % This is where we choose the actual type. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_object_write:nnn #1#2#3 { \use:c { @@_backend_object_write_ #2 :nn } { \@@_backend_object_ref:n {#1} } {#3} } \cs_generate_variant:Nn \@@_backend_object_write:nnn { nne } \cs_new_protected:Npn \@@_backend_object_write_array:nn #1#2 { \@@_backend:e { obj ~ #1 ~ [ ~ \exp_not:n {#2} ~ ] } } \cs_new_protected:Npn \@@_backend_object_write_dict:nn #1#2 { \@@_backend:e { obj ~ #1 ~ << ~ \exp_not:n {#2} ~ >> } } \cs_new_protected:Npn \@@_backend_object_write_fstream:nn #1#2 { \@@_backend_object_write_stream:nnnn { f } {#1} #2 } \cs_new_protected:Npn \@@_backend_object_write_stream:nn #1#2 { \@@_backend_object_write_stream:nnnn { } {#1} #2 } \cs_new_protected:Npn \@@_backend_object_write_stream:nnnn #1#2#3#4 { \@@_backend:e { #1 stream ~ #2 ~ ( \exp_not:n {#4} ) ~ << \exp_not:n {#3} >> } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_backend_object_now:nn, \@@_backend_object_now:ne} % No anonymous objects with \texttt{dvipdfmx} so we have to give an % object name. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_object_now:nn #1#2 { \int_gincr:N \g_@@_backend_object_int \exp_args:Nne \use:c { @@_backend_object_write_ #1 :nn } { @pdf.obj \int_use:N \g_@@_backend_object_int } {#2} } \cs_generate_variant:Nn \@@_backend_object_now:nn { ne } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_backend_object_last:} % \begin{macrocode} \cs_new:Npn \@@_backend_object_last: { @pdf.obj \int_use:N \g_@@_backend_object_int } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_backend_pageobject_ref:n} % Page references are easy in \texttt{dvipdfmx}/\XeTeX{}. % \begin{macrocode} \cs_new:Npn \@@_backend_pageobject_ref:n #1 { @page #1 } % \end{macrocode} % \end{macro} % % \subsubsection{Annotations} % % \begin{variable}{\g_@@_backend_annotation_int} % Needed as objects which are not annotations could be created. % \begin{macrocode} \int_new:N \g_@@_backend_annotation_int % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_backend_annotation:nnnn} % Simply pass the raw data through, just dealing with evaluation of dimensions. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_annotation:nnnn #1#2#3#4 { \int_gincr:N \g_@@_backend_object_int \int_gset_eq:NN \g_@@_backend_annotation_int \g_@@_backend_object_int \@@_backend:e { ann ~ @pdf.obj \int_use:N \g_@@_backend_object_int \c_space_tl width ~ \dim_eval:n {#1} ~ height ~ \dim_eval:n {#2} ~ depth ~ \dim_eval:n {#3} ~ << /Type /Annot #4 >> } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_backend_annotation_last:} % \begin{macrocode} \cs_new:Npn \@@_backend_annotation_last: { @pdf.obj \int_use:N \g_@@_backend_annotation_int } % \end{macrocode} % \end{macro} % % \begin{variable}{\g_@@_backend_link_int} % To track annotations which are links. % \begin{macrocode} \int_new:N \g_@@_backend_link_int % \end{macrocode} % \end{variable} % % \begin{macro} % {\@@_backend_link_begin_goto:nnw, \@@_backend_link_begin_user:nnw} % \begin{macro}{\@@_backend_link_begin:n} % \begin{macro}{\@@_backend_link_end:} % All created using the same internals. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_link_begin_goto:nnw #1#2 { \@@_backend_link_begin:n { #1 /Subtype /Link /A << /S /GoTo /D ( #2 ) >> } } \cs_new_protected:Npn \@@_backend_link_begin_user:nnw #1#2 { \@@_backend_link_begin:n {#1#2} } \cs_new_protected:Npe \@@_backend_link_begin:n #1 { \exp_not:N \int_gincr:N \exp_not:N \g_@@_backend_link_int \@@_backend:e { bann ~ @pdf.lnk \exp_not:N \int_use:N \exp_not:N \g_@@_backend_link_int \c_space_tl << /Type /Annot #1 >> } } \cs_new_protected:Npn \@@_backend_link_end: { \@@_backend:n { eann } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_backend_link_last:} % Available using the backend mechanism with a suitably-recent % version. % \begin{macrocode} \cs_new:Npn \@@_backend_link_last: { @pdf.lnk \int_use:N \g_@@_backend_link_int } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_backend_link_margin:n} % Pass to \texttt{dvipdfmx}. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_link_margin:n #1 { \__kernel_backend_literal:e { dvipdfmx:config~g~ \dim_eval:n {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_backend_destination:nn} % \begin{macro}{\@@_backend_destination:nnnn,\@@_backend_destination_aux:nnnn} % Here, we need to turn the zoom into a scale. The method for \texttt{FitR} % is from Alexander Grahn: the idea is to avoid needing to do any calculations % in \TeX{} by using the backend data for \texttt{@xpos} and \texttt{@ypos}. % \texttt{/FitR} without rule spec doesn't work, so it falls back to % \texttt{/Fit} here. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_destination:nn #1#2 { \@@_backend:e { 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 } } ] } } \cs_new_protected:Npn \@@_backend_destination:nnnn #1#2#3#4 { \exp_args:Ne \@@_backend_destination_aux:nnnn { \dim_eval:n {#2} } {#1} {#3} {#4} } \cs_new_protected:Npn \@@_backend_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 ] } } \tex_vss:D } \__kernel_kern:n { -#1 } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsubsection{Structure} % % \begin{macro}{\@@_backend_compresslevel:n} % \begin{macro}{\@@_backend_compress_objects:n} % Pass data to the backend: these are a one-shot. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_compresslevel:n #1 { \__kernel_backend_literal:e { dvipdfmx:config~z~ \int_eval:n {#1} } } \cs_new_protected:Npn \@@_backend_compress_objects:n #1 { \bool_if:nF {#1} { \__kernel_backend_literal:n { dvipdfmx:config~C~0x40 } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro} % {\@@_backend_version_major_gset:n, \@@_backend_version_minor_gset:n} % We start with the assumption that the default is active. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_version_major_gset:n #1 { \cs_gset:Npe \@@_backend_version_major: { \int_eval:n {#1} } \__kernel_backend_literal:e { pdf:majorversion~ \@@_backend_version_major: } } \cs_new_protected:Npn \@@_backend_version_minor_gset:n #1 { \cs_gset:Npe \@@_backend_version_minor: { \int_eval:n {#1} } \__kernel_backend_literal:e { pdf:minorversion~ \@@_backend_version_minor: } } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_backend_version_major:, \@@_backend_version_minor:} % We start with the assumption that the default is active. % \begin{macrocode} \cs_new:Npn \@@_backend_version_major: { 1 } \cs_new:Npn \@@_backend_version_minor: { 5 } % \end{macrocode} % \end{macro} % % \subsubsection{Marked content} % % \begin{macro}{\@@_backend_bdc:nn} % \begin{macro}{\@@_backend_emc:} % Simple wrappers. May need refinement: see % \url{https://chat.stackexchange.com/transcript/message/49970158#49970158}. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_bdc:nn #1#2 { \__kernel_backend_literal_page:n { /#1 ~ #2 ~ BDC } } \cs_new_protected:Npn \@@_backend_emc: { \__kernel_backend_literal_page:n { EMC } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \subsection{\texttt{dvisvgm} backend} % % \begin{macrocode} %<*dvisvgm> % \end{macrocode} % % \subsubsection{Annotations} % % \begin{macro}{\@@_backend_annotation:nnnn} % \begin{macrocode} \cs_new_protected:Npn \@@_backend_annotation:nnnn #1#2#3#4 { } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_backend_annotation_last:} % \begin{macrocode} \cs_new:Npn \@@_backend_annotation_last: { } % \end{macrocode} % \end{macro} % % \begin{macro} % {\@@_backend_link_begin_goto:nnw, \@@_backend_link_begin_user:nnw} % \begin{macro}{\@@_backend_link_begin:nnnw} % \begin{macro}{\@@_backend_link_end:} % \begin{macrocode} \cs_new_protected:Npn \@@_backend_link_begin_goto:nnw #1#2 { } \cs_new_protected:Npn \@@_backend_link_begin_user:nnw #1#2 { } \cs_new_protected:Npn \@@_backend_link_begin:nnnw #1#2#3 { } \cs_new_protected:Npn \@@_backend_link_end: { } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_backend_link_last:} % \begin{macrocode} \cs_new:Npe \@@_backend_link_last: { } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_backend_link_margin:n} % A simple task: pass the data to the primitive. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_link_margin:n #1 { } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_backend_destination:nn} % \begin{macro}{\@@_backend_destination:nnnn} % \begin{macrocode} \cs_new_protected:Npn \@@_backend_destination:nn #1#2 { } \cs_new_protected:Npn \@@_backend_destination:nnnn #1#2#3#4 { } % \end{macrocode} % \end{macro} % \end{macro} % % \subsubsection{Catalogue entries} % % \begin{macro}{\@@_backend_catalog_gput:nn, \@@_backend_info_gput:nn} % No-op. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_catalog_gput:nn #1#2 { } \cs_new_protected:Npn \@@_backend_info_gput:nn #1#2 { } % \end{macrocode} % \end{macro} % % \subsubsection{Objects} % % \begin{macro}{\@@_backend_object_new:n} % \begin{macro}[EXP]{\@@_backend_object_ref:n} % \begin{macro}{\@@_backend_object_write:nnn, \@@_backend_object_write:ne} % \begin{macro}{\@@_backend_object_now:nn, , \@@_backend_object_now:ne} % \begin{macro}{\@@_backend_object_last:} % \begin{macro}[EXP]{\@@_backend_pageobject_ref:n} % All no-ops here. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_object_new:nn #1 { } \cs_new:Npn \@@_backend_object_ref:n #1 { } \cs_new_protected:Npn \@@_backend_object_write:nnn #1#2#3 { } \cs_new_protected:Npn \@@_backend_object_write:nne #1#2#3 { } \cs_new_protected:Npn \@@_backend_object_now:nn #1#2 { } \cs_new_protected:Npn \@@_backend_object_now:ne #1#2 { } \cs_new:Npn \@@_backend_object_last: { } \cs_new:Npn \@@_backend_pageobject_ref:n #1 { } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsubsection{Structure} % % \begin{macro}{\@@_backend_compresslevel:n} % \begin{macro}{\@@_backend_compress_objects:n} % These are all no-ops. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_compresslevel:n #1 { } \cs_new_protected:Npn \@@_backend_compress_objects:n #1 { } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro} % {\@@_backend_version_major_gset:n, \@@_backend_version_minor_gset:n} % Data not available! % \begin{macrocode} \cs_new_protected:Npn \@@_backend_version_major_gset:n #1 { } \cs_new_protected:Npn \@@_backend_version_minor_gset:n #1 { } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_backend_version_major:, \@@_backend_version_minor:} % Data not available! % \begin{macrocode} \cs_new:Npn \@@_backend_version_major: { -1 } \cs_new:Npn \@@_backend_version_minor: { -1 } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_backend_bdc:nn} % \begin{macro}{\@@_backend_emc:} % More no-ops. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_bdc:nn #1#2 { } \cs_new_protected:Npn \@@_backend_emc: { } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \subsection{PDF Page size (media box)} % % For setting the media box, the split between backends is somewhat different % to other areas, thus we approach this separately. The code here assumes a % recent \LaTeXe{}: that is ensured at the level above. % % \begin{macrocode} %<*dvipdfmx|dvips> % \end{macrocode} % % \begin{macro}{\@@_backend_pagesize_gset:nn} % This is done as a backend literal, so we deal with it using the shipout % hook. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_pagesize_gset:nn #1#2 { \__kernel_backend_first_shipout:n { \__kernel_backend_literal:e { %<*dvipdfmx> pdf:pagesize ~ width ~ \dim_eval:n {#1} ~ height ~ \dim_eval:n {#2} % %<*dvips> papersize = \dim_eval:n {#1} , \dim_eval:n {#2} % } } } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \begin{macrocode} %<*luatex|pdftex|xetex> % \end{macrocode} % % \begin{macro}{\@@_backend_pagesize_gset:nn} % Pass to the primitives. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_pagesize_gset:nn #1#2 { \dim_gset:Nn \tex_pagewidth:D {#1} \dim_gset:Nn \tex_pageheight:D {#2} } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \begin{macrocode} %<*dvisvgm> % \end{macrocode} % % \begin{macro}{\@@_backend_pagesize_gset:nn} % A no-op. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_pagesize_gset:nn #1#2 { } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex