% \iffalse meta-comment % %% File: l3backend-basics.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-basics} module\\ Backend basics^^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-basics} implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % Whilst there is a reasonable amount of code overlap between backends, % it is much clearer to have the blocks more-or-less separated than run % in together and DocStripped out in parts. As such, most of the following % is set up on a per-backend basis, though there is some common code (again % given in blocks not interspersed with other material). % % All the file identifiers are up-front so that they come out in the right % place in the files. % \begin{macrocode} \ProvidesExplFile %<*dvipdfmx> {l3backend-dvipdfmx.def}{2024-03-14}{} {L3 backend support: dvipdfmx} % %<*dvips> {l3backend-dvips.def}{2024-03-14}{} {L3 backend support: dvips} % %<*dvisvgm> {l3backend-dvisvgm.def}{2024-03-14}{} {L3 backend support: dvisvgm} % %<*luatex> {l3backend-luatex.def}{2024-03-14}{} {L3 backend support: PDF output (LuaTeX)} % %<*pdftex> {l3backend-pdftex.def}{2024-03-14}{} {L3 backend support: PDF output (pdfTeX)} % %<*xetex> {l3backend-xetex.def}{2024-03-14}{} {L3 backend support: XeTeX} % % \end{macrocode} % % Check if the loaded kernel is at least enough to load this file. % The kernel date has to be at least equal to \cs{ExplBackendFileDate} % or later. If \cs{__kernel_dependency_version_check:Nn} doesn't % exist we're loading in an older kernel, so it's an error anyway. % With time, this test should vanish and only the dependency check % should remain. % \begin{macrocode} \cs_if_exist:NTF \__kernel_dependency_version_check:nn { \__kernel_dependency_version_check:nn {2023-10-10} % {l3backend-dvipdfmx.def} % {l3backend-dvips.def} % {l3backend-dvisvgm.def} % {l3backend-luatex.def} % {l3backend-pdftex.def} % {l3backend-xetex.def} } { \cs_if_exist_use:cF { @latex@error } { \errmessage } { Mismatched~LaTeX~support~files~detected. \MessageBreak Loading~aborted! } { \use:c { @ehd } } \tex_endinput:D } % \end{macrocode} % % The order of the backend code here is such that we get somewhat logical % outcomes in terms of code sharing whilst keeping things readable. (Trying to % mix all of the code by concept is almost unmanageable.) The key parts which % are shared are % \begin{itemize} % \item Color support is either \texttt{dvips}-like or \LuaTeX{}/pdfTeX{}-like. % \item \LuaTeX{}/pdfTeX{} and \texttt{dvipdfmx}/\XeTeX{} share drawing routines. % \item \XeTeX{} is the same as \texttt{dvipdfmx} other than image size % extraction so takes most of the same code. % \end{itemize} % % \begin{macro} % { % \__kernel_backend_literal:e, % \__kernel_backend_literal:n % } % The one shared function for all backends is access to the basic % \tn{special} primitive: it has slightly odd expansion behaviour % so a wrapper is provided. % \begin{macrocode} \cs_new_eq:NN \__kernel_backend_literal:e \tex_special:D \cs_new_protected:Npn \__kernel_backend_literal:n #1 { \__kernel_backend_literal:e { \exp_not:n {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\__kernel_backend_first_shipout:n} % We need to write at first shipout in a few places. As we want to use the % most up-to-date method, % \begin{macrocode} \cs_if_exist:NTF \@ifl@t@r { \@ifl@t@r \fmtversion { 2020-10-01 } { \cs_new_protected:Npn \__kernel_backend_first_shipout:n #1 { \hook_gput_code:nnn { shipout / firstpage } { l3backend } {#1} } } { \cs_new_eq:NN \__kernel_backend_first_shipout:n \AtBeginDvi } } { \cs_new_eq:NN \__kernel_backend_first_shipout:n \use:n } % \end{macrocode} % \end{macro} % % \subsection{\texttt{dvips} backend} % % \begin{macrocode} %<*dvips> % \end{macrocode} % % \begin{macro} % {\__kernel_backend_literal_postscript:n, \__kernel_backend_literal_postscript:e} % Literal PostScript can be included using a few low-level formats. Here, % we use the form with no positioning: this is overall more convenient as % a wrapper. Note that this does require that where position is important, % an appropriate wrapper is included. % \begin{macrocode} \cs_new_protected:Npn \__kernel_backend_literal_postscript:n #1 { \__kernel_backend_literal:n { ps:: #1 } } \cs_generate_variant:Nn \__kernel_backend_literal_postscript:n { e } % \end{macrocode} % \end{macro} % % \begin{macro}{\__kernel_backend_postscript:n, \__kernel_backend_postscript:e} % PostScript data that does have positioning, and also applying % a shift to |SDict| (which is not done automatically by % |ps:| or |ps::|, in contrast to |!| or |"|). % \begin{macrocode} \cs_new_protected:Npn \__kernel_backend_postscript:n #1 { \__kernel_backend_literal:n { ps: SDict ~ begin ~ #1 ~ end } } \cs_generate_variant:Nn \__kernel_backend_postscript:n { e } % \end{macrocode} % \end{macro} % % PostScript for the header: a small saving but makes the code clearer. % This is held until the start of shipout such that a document with no % actual output does not write anything. % \begin{macrocode} \bool_if:NT \g__kernel_backend_header_bool { \__kernel_backend_first_shipout:n { \__kernel_backend_literal:n { header = l3backend-dvips.pro } } } % \end{macrocode} % % \begin{macro} % { % \__kernel_backend_align_begin:, % \__kernel_backend_align_end: % } % In \texttt{dvips} there is no built-in saving of the current % position, and so some additional PostScript is required to set up the % transformation matrix and also to restore it afterwards. Notice the use % of the stack to save the current position \enquote{up front} and to % move back to it at the end of the process. Notice that the |[begin]|/^^A % |[end]| pair here mean that we can use a run of PostScript statements % in separate lines: not \emph{required} but does make the code and % output more clear. % \begin{macrocode} \cs_new_protected:Npn \__kernel_backend_align_begin: { \__kernel_backend_literal:n { ps::[begin] } \__kernel_backend_literal_postscript:n { currentpoint } \__kernel_backend_literal_postscript:n { currentpoint~translate } } \cs_new_protected:Npn \__kernel_backend_align_end: { \__kernel_backend_literal_postscript:n { neg~exch~neg~exch~translate } \__kernel_backend_literal:n { ps::[end] } } % \end{macrocode} % \end{macro} % % \begin{macro}{\__kernel_backend_scope_begin:, \__kernel_backend_scope_end:} % Saving/restoring scope for general operations needs to be done with % \texttt{dvips} positioning (try without to see this!). Thus we need the % |ps:| version of the special here. As only the graphics state is ever % altered within this pairing, we use the lower-cost |g|-versions. % \begin{macrocode} \cs_new_protected:Npn \__kernel_backend_scope_begin: { \__kernel_backend_literal:n { ps:gsave } } \cs_new_protected:Npn \__kernel_backend_scope_end: { \__kernel_backend_literal:n { ps:grestore } } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \subsection{\LuaTeX{} and \pdfTeX{} backends} % % \begin{macrocode} %<*luatex|pdftex> % \end{macrocode} % % Both \LuaTeX{} and \pdfTeX{} write PDFs directly rather than via an % intermediate file. Although there are similarities, the move of \LuaTeX{} % to have more code in Lua means we create two independent files using % shared DocStrip code. % % \begin{macro}{\__kernel_backend_literal_pdf:n, \__kernel_backend_literal_pdf:e} % This is equivalent to \verb|\special{pdf:}| but the engine can % track it. Without the \texttt{direct} keyword everything is kept in % sync: the transformation matrix is set to the current point automatically. % Note that this is still inside the text (\texttt{BT} \dots \texttt{ET} % block). % \begin{macrocode} \cs_new_protected:Npn \__kernel_backend_literal_pdf:n #1 { %<*luatex> \tex_pdfextension:D literal % %<*pdftex> \tex_pdfliteral:D % { \exp_not:n {#1} } } \cs_generate_variant:Nn \__kernel_backend_literal_pdf:n { e } % \end{macrocode} % \end{macro} % % \begin{macro}{\__kernel_backend_literal_page:n, \__kernel_backend_literal_page:e} % Page literals are pretty simple. To avoid an expansion, we write out % by hand. % \begin{macrocode} \cs_new_protected:Npn \__kernel_backend_literal_page:n #1 { %<*luatex> \tex_pdfextension:D literal ~ % %<*pdftex> \tex_pdfliteral:D % page { \exp_not:n {#1} } } \cs_new_protected:Npn \__kernel_backend_literal_page:e #1 { %<*luatex> \tex_pdfextension:D literal ~ % %<*pdftex> \tex_pdfliteral:D % page {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{\__kernel_backend_scope_begin:, \__kernel_backend_scope_end:} % Higher-level interfaces for saving and restoring the graphic state. % \begin{macrocode} \cs_new_protected:Npn \__kernel_backend_scope_begin: { %<*luatex> \tex_pdfextension:D save \scan_stop: % %<*pdftex> \tex_pdfsave:D % } \cs_new_protected:Npn \__kernel_backend_scope_end: { %<*luatex> \tex_pdfextension:D restore \scan_stop: % %<*pdftex> \tex_pdfrestore:D % } % \end{macrocode} % \end{macro} % % \begin{macro}{\__kernel_backend_matrix:n, \__kernel_backend_matrix:e} % Here the appropriate function is set up to insert an affine matrix % into the PDF. With \pdfTeX{} and \LuaTeX{} in direct PDF output mode there % is a primitive for this, which only needs the rotation/scaling/skew part. % \begin{macrocode} \cs_new_protected:Npn \__kernel_backend_matrix:n #1 { %<*luatex> \tex_pdfextension:D setmatrix % %<*pdftex> \tex_pdfsetmatrix:D % { \exp_not:n {#1} } } \cs_generate_variant:Nn \__kernel_backend_matrix:n { e } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \subsection{\texttt{dvipdfmx} backend} % % \begin{macrocode} %<*dvipdfmx|xetex> % \end{macrocode} % % The \texttt{dvipdfmx} shares code with the PDF mode one (using the common % section to this file) but also with \XeTeX{}. The latter is close % to identical to \texttt{dvipdfmx} and so all of the code here is extracted % for both backends, with some \texttt{clean up} for \XeTeX{} as % required. % % \begin{macro}{\__kernel_backend_literal_pdf:n, \__kernel_backend_literal_pdf:e} % Undocumented but equivalent to \pdfTeX{}'s |literal| keyword. It's similar to % be not the same as the documented |contents| keyword as that adds a |q|/|Q| % pair. % \begin{macrocode} \cs_new_protected:Npn \__kernel_backend_literal_pdf:n #1 { \__kernel_backend_literal:n { pdf:literal~ #1 } } \cs_generate_variant:Nn \__kernel_backend_literal_pdf:n { e } % \end{macrocode} % \end{macro} % % \begin{macro}{\__kernel_backend_literal_page:n} % Whilst the manual says this is like |literal direct| in \pdfTeX{}, % it closes the |BT| block! % \begin{macrocode} \cs_new_protected:Npn \__kernel_backend_literal_page:n #1 { \__kernel_backend_literal:n { pdf:literal~direct~ #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\__kernel_backend_scope_begin:, \__kernel_backend_scope_end:} % Scoping is done using the backend-specific specials. We use the versions % originally from \texttt{xdvidfpmx} (\texttt{x:}) as these are well-tested % \enquote{in the wild}. % \begin{macrocode} \cs_new_protected:Npn \__kernel_backend_scope_begin: { \__kernel_backend_literal:n { x:gsave } } \cs_new_protected:Npn \__kernel_backend_scope_end: { \__kernel_backend_literal:n { x:grestore } } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \subsection{\texttt{dvisvgm} backend} % % \begin{macrocode} %<*dvisvgm> % \end{macrocode} % % \begin{macro}{\__kernel_backend_literal_svg:n, \__kernel_backend_literal_svg:e} % Unlike the other backends, the requirements for making SVG files mean % that we can't conveniently transform all operations to the current point. % That makes life a bit more tricky later as that needs to be accounted for. % A new line is added after each call to help to keep the output readable % for debugging. % \begin{macrocode} \cs_new_protected:Npn \__kernel_backend_literal_svg:n #1 { \__kernel_backend_literal:n { dvisvgm:raw~ #1 { ?nl } } } \cs_generate_variant:Nn \__kernel_backend_literal_svg:n { e } % \end{macrocode} % \end{macro} % % \begin{variable}{\g__kernel_backend_scope_int, \l__kernel_backend_scope_int} % In SVG, we need to track scope nesting as properties attach to scopes; that % requires a pair of \texttt{int} registers. % \begin{macrocode} \int_new:N \g__kernel_backend_scope_int \int_new:N \l__kernel_backend_scope_int % \end{macrocode} % \end{variable} % % \begin{macro}{\__kernel_backend_scope_begin:, \__kernel_backend_scope_end:} % \begin{macro}{\__kernel_backend_scope_begin:n, \__kernel_backend_scope_begin:e} % \begin{macro}{\__kernel_backend_scope:n, \__kernel_backend_scope:e} % In SVG, the need to attach concepts to a scope means we need to be sure we % will close all of the open scopes. That is easiest done if we only need % an outer \enquote{wrapper} \texttt{begin}/\texttt{end} pair, and within % that we apply operations as a simple scoped statements. To keep down the % non-productive groups, we also have a \texttt{begin} version that does take % an argument. % \begin{macrocode} \cs_new_protected:Npn \__kernel_backend_scope_begin: { \__kernel_backend_literal_svg:n { } \int_set_eq:NN \l__kernel_backend_scope_int \g__kernel_backend_scope_int \group_begin: \int_gset:Nn \g__kernel_backend_scope_int { 1 } } \cs_new_protected:Npn \__kernel_backend_scope_end: { \prg_replicate:nn { \g__kernel_backend_scope_int } { \__kernel_backend_literal_svg:n { } } \group_end: \int_gset_eq:NN \g__kernel_backend_scope_int \l__kernel_backend_scope_int } \cs_new_protected:Npn \__kernel_backend_scope_begin:n #1 { \__kernel_backend_literal_svg:n { } \int_set_eq:NN \l__kernel_backend_scope_int \g__kernel_backend_scope_int \group_begin: \int_gset:Nn \g__kernel_backend_scope_int { 1 } } \cs_generate_variant:Nn \__kernel_backend_scope_begin:n { e } \cs_new_protected:Npn \__kernel_backend_scope:n #1 { \__kernel_backend_literal_svg:n { } \int_gincr:N \g__kernel_backend_scope_int } \cs_generate_variant:Nn \__kernel_backend_scope:n { e } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex