% \iffalse meta-comment
%
%% File: l3backend-box.dtx
%
% Copyright (C) 2019-2026 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 "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,kernel]{l3doc}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{^^A
%   The \pkg{l3backend-box} package\\ Backend box 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 2026-08-10}
%
% \maketitle
%
% \begin{documentation}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3backend-box} implementation}
%
%    \begin{macrocode}
%<*package>
%<@@=box>
%    \end{macrocode}
%
% \subsection{\texttt{dvips} backend}
%
%    \begin{macrocode}
%<*dvips>
%    \end{macrocode}
%
% \begin{macro}{\@@_backend_clip:N}
%   The \texttt{dvips} backend scales all absolute dimensions based on the
%   output resolution selected and any \TeX{} magnification. Thus for any
%   operation involving absolute lengths there is a correction to make. See
%   \texttt{normalscale} from \texttt{special.pro} for the variables, noting
%   that here everything is saved on the stack rather than as a separate
%   variable. Once all of that is done, the actual clipping is trivial.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_clip:N #1
  {
    \__kernel_backend_scope_begin:
    \__kernel_backend_align_begin:
    \__kernel_backend_literal_postscript:n { matrix~currentmatrix }
    \__kernel_backend_literal_postscript:n
      { Resolution~72~div~VResolution~72~div~scale }
    \__kernel_backend_literal_postscript:n { DVImag~dup~scale }
    \__kernel_backend_literal_postscript:e
      {
        0 ~
        \dim_to_decimal_in_bp:n { \box_dp:N #1 } ~
        \dim_to_decimal_in_bp:n { \box_wd:N #1 } ~
        \dim_to_decimal_in_bp:n { -\box_ht:N #1 - \box_dp:N #1 } ~
        rectclip
      }
    \__kernel_backend_literal_postscript:n { setmatrix }
    \__kernel_backend_align_end:
    \hbox_overlap_right:n { \box_use:N #1 }
    \__kernel_backend_scope_end:
    \skip_horizontal:n { \box_wd:N #1 }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_backend_rotate:Nn}
% \begin{macro}{\@@_backend_rotate_aux:Nn}
%   Rotating using \texttt{dvips} does not require that the box dimensions
%   are altered and has a very convenient built-in operation. Zero rotation
%   must be written as |0| not |-0| so there is a quick test.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_rotate:Nn #1#2
  { \exp_args:NNf \@@_backend_rotate_aux:Nn #1 { \fp_eval:n {#2} } }
\cs_new_protected:Npn \@@_backend_rotate_aux:Nn #1#2
  {
    \__kernel_backend_scope_begin:
    \__kernel_backend_align_begin:
    \__kernel_backend_literal_postscript:e
      {
        \fp_compare:nNnTF {#2} = \c_zero_fp
          { 0 }
          { \fp_eval:n { round ( -(#2) , 5 ) } } ~
        rotate
      }
    \__kernel_backend_align_end:
    \box_use:N #1
    \__kernel_backend_scope_end:
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_backend_scale:Nnn}
%   The \texttt{dvips} backend once again has a dedicated operation we can
%   use here.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_scale:Nnn #1#2#3
  {
    \__kernel_backend_scope_begin:
    \__kernel_backend_align_begin:
    \__kernel_backend_literal_postscript:e
      {
        \fp_eval:n { round ( #2 , 5 ) } ~
        \fp_eval:n { round ( #3 , 5 ) } ~
        scale
      }
    \__kernel_backend_align_end:
    \hbox_overlap_right:n { \box_use:N #1 }
    \__kernel_backend_scope_end:
  }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%</dvips>
%    \end{macrocode}
%
% \subsection{\LuaTeX{} and \pdfTeX{} backends}
%
%    \begin{macrocode}
%<*luatex|pdftex>
%    \end{macrocode}
%
% \begin{macro}{\@@_backend_clip:N}
%   The general method is to save the current location, define a clipping path
%   equivalent to the bounding box, then insert the content at the current
%   position and in a zero width box. The \enquote{real} width is then made up
%   using a horizontal skip before tidying up. There are other approaches that
%   can be taken (for example using XForm objects), but the logic here shares
%   as much code as possible and uses the same conversions (and so same
%   rounding errors) in all cases.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_clip:N #1
  {
    \__kernel_backend_scope_begin:
    \__kernel_backend_literal_pdf:e
      {
        0~
        \dim_to_decimal_in_bp:n { -\box_dp:N #1 } ~
        \dim_to_decimal_in_bp:n { \box_wd:N #1 } ~
        \dim_to_decimal_in_bp:n { \box_ht:N #1 + \box_dp:N #1 } ~
        re~W~n
      }
    \hbox_overlap_right:n { \box_use:N #1 }
    \__kernel_backend_scope_end:
    \skip_horizontal:n { \box_wd:N #1 }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_backend_rotate:Nn}
% \begin{macro}{\@@_backend_rotate_aux:Nn}
% \begin{variable}{\l_@@_backend_cos_fp, \l_@@_backend_sin_fp}
%   Rotations are set using an affine transformation matrix which therefore
%   requires sine/cosine values not the angle itself. We store the rounded
%   values to avoid rounding twice. There are also a couple of comparisons to
%   ensure that |-0| is not written to the output, as this avoids any issues
%   with problematic display programs.  Note that numbers are compared to~$0$
%   after rounding.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_rotate:Nn #1#2
  { \exp_args:NNf \@@_backend_rotate_aux:Nn #1 { \fp_eval:n {#2} } }
\cs_new_protected:Npn \@@_backend_rotate_aux:Nn #1#2
  {
    \__kernel_backend_scope_begin:
    \box_set_wd:Nn #1 { 0pt }
    \fp_set:Nn \l_@@_backend_cos_fp { round ( cosd ( #2 ) , 5 ) }
    \fp_compare:nNnT \l_@@_backend_cos_fp = \c_zero_fp
      { \fp_zero:N \l_@@_backend_cos_fp }
    \fp_set:Nn \l_@@_backend_sin_fp { round ( sind ( #2 ) , 5 ) }
    \__kernel_backend_matrix:e
      {
        \fp_use:N \l_@@_backend_cos_fp \c_space_tl
        \fp_compare:nNnTF \l_@@_backend_sin_fp = \c_zero_fp
          { 0~0 }
          {
            \fp_use:N \l_@@_backend_sin_fp
            \c_space_tl
            \fp_eval:n { -\l_@@_backend_sin_fp }
          }
        \c_space_tl
        \fp_use:N \l_@@_backend_cos_fp
      }
    \box_use:N #1
    \__kernel_backend_scope_end:
  }
\fp_new:N \l_@@_backend_cos_fp
\fp_new:N \l_@@_backend_sin_fp
%    \end{macrocode}
% \end{variable}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_backend_scale:Nnn}
%   The same idea as for rotation but without the complexity of signs and
%   cosines.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_scale:Nnn #1#2#3
  {
    \__kernel_backend_scope_begin:
    \__kernel_backend_matrix:e
      {
        \fp_eval:n { round ( #2 , 5 ) } ~
        0~0~
        \fp_eval:n { round ( #3 , 5 ) }
      }
    \hbox_overlap_right:n { \box_use:N #1 }
    \__kernel_backend_scope_end:
  }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%</luatex|pdftex>
%    \end{macrocode}
%
% \subsection{\texttt{dvipdfmx}/\XeTeX{} backend}
%
%    \begin{macrocode}
%<*dvipdfmx|xetex>
%    \end{macrocode}
%
% \begin{macro}{\@@_backend_clip:N}
%   The code here is identical to that for \LuaTeX{}/\pdfTeX{}: unlike rotation and
%   scaling, there is no higher-level support in the backend for clipping.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_clip:N #1
  {
    \__kernel_backend_scope_begin:
    \__kernel_backend_literal_pdf:e
      {
        0~
        \dim_to_decimal_in_bp:n { -\box_dp:N #1 } ~
        \dim_to_decimal_in_bp:n { \box_wd:N #1 } ~
        \dim_to_decimal_in_bp:n { \box_ht:N #1 + \box_dp:N #1 } ~
        re~W~n
      }
    \hbox_overlap_right:n { \box_use:N #1 }
    \__kernel_backend_scope_end:
    \skip_horizontal:n { \box_wd:N #1 }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_backend_rotate:Nn}
% \begin{macro}{\@@_backend_rotate_aux:Nn}
%   Rotating in \texttt{dvipdmfx}/\XeTeX{} can be implemented using either PDF or
%   backend-specific code. The former approach however is not \enquote{aware}
%   of the content of boxes: this means that any embedded links would not be
%   adjusted by the rotation. As such, the backend-native approach is preferred:
%   the code therefore is similar (though not identical) to the \texttt{dvips}
%   version (notice the rotation angle here is positive). As for
%   \texttt{dvips}, zero rotation is written as |0| not |-0|.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_rotate:Nn #1#2
  { \exp_args:NNf \@@_backend_rotate_aux:Nn #1 { \fp_eval:n {#2} } }
\cs_new_protected:Npn \@@_backend_rotate_aux:Nn #1#2
  {
    \__kernel_backend_scope_begin:
    \__kernel_backend_literal:e
      {
        x:rotate~
        \fp_compare:nNnTF {#2} = \c_zero_fp
          { 0 }
          { \fp_eval:n { round ( #2 , 5 ) } }
      }
    \box_use:N #1
    \__kernel_backend_scope_end:
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_backend_scale:Nnn}
%   Much the same idea for scaling: use the higher-level backend operation to allow
%   for box content.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_scale:Nnn #1#2#3
  {
    \__kernel_backend_scope_begin:
    \__kernel_backend_literal:e
      {
        x:scale~
        \fp_eval:n { round ( #2 , 5 ) } ~
        \fp_eval:n { round ( #3 , 5 ) }
      }
    \hbox_overlap_right:n { \box_use:N #1 }
    \__kernel_backend_scope_end:
  }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%</dvipdfmx|xetex>
%    \end{macrocode}
%
% \subsection{\texttt{dvisvgm} backend}
%
%    \begin{macrocode}
%<*dvisvgm>
%    \end{macrocode}
%
% \begin{macro}{\@@_backend_clip:N}
% \begin{variable}{\g__kernel_clip_path_int}
%   Clipping in SVG is more involved than with other backends. The first issue
%   is that the clipping path must be defined separately from where it is used,
%   so we need to track how many paths have applied. The naming here uses
%   \texttt{l3cp} as the namespace with a number following. Rather than use
%   a rectangular operation, we define the path manually as this allows it to
%   have a depth: easier than the alternative approach of shifting content
%   up and down using scopes to allow for the depth of the \TeX{} box and
%   keep the reference point the same!
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_clip:N #1
  {
    \int_gincr:N \g__kernel_clip_path_int
    \__kernel_backend_literal_svg:e
      { < clipPath~id = " l3cp \int_use:N \g__kernel_clip_path_int " > }
    \__kernel_backend_literal_svg:e
      {
        <
          path ~ d =
            "
              M ~ 0 ~
                  \dim_to_decimal:n { -\box_dp:N #1 } ~
              L ~ \dim_to_decimal:n { \box_wd:N #1 } ~
                  \dim_to_decimal:n { -\box_dp:N #1 } ~
              L ~ \dim_to_decimal:n { \box_wd:N #1 }  ~
                  \dim_to_decimal:n { \box_ht:N #1 + \box_dp:N #1 } ~
              L ~ 0 ~
                  \dim_to_decimal:n { \box_ht:N #1 + \box_dp:N #1 } ~
              Z
            "
        />
      }
    \__kernel_backend_literal_svg:n
      { < /clipPath > }
%    \end{macrocode}
%   In general the SVG set up does not try to transform coordinates to the
%   current point. For clipping we need to do that, so have a transformation
%   here to get us to the right place, and a matching one just before the
%   \TeX{} box is inserted to get things back on track. The clip path needs to
%   come between those two such that if lines up with the current point, as
%   does the \TeX{} box.
%    \begin{macrocode}
    \__kernel_backend_scope_begin:n
      {
        transform =
          "
            translate ( { ?x } , { ?y } ) ~
            scale ( 1 , -1 )
          "
      }
    \__kernel_backend_scope:e
      {
        clip-path =
          "url ( \c_hash_str l3cp \int_use:N \g__kernel_clip_path_int ) "
      }
    \__kernel_backend_scope:n
      {
        transform =
          "
            scale ( -1 , 1 ) ~
            translate ( { ?x } , { ?y } ) ~
            scale ( -1 , -1 )
          "
      }
    \box_use:N #1
    \__kernel_backend_scope_end:
  }
\int_new:N \g__kernel_clip_path_int
%    \end{macrocode}
% \end{variable}
% \end{macro}
%
% \begin{macro}{\@@_backend_rotate:Nn}
%   Rotation has a dedicated operation which includes a center-of-rotation
%   optional pair. That can be picked up from the backend syntax, so there is
%   no need to worry about the transformation matrix.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_rotate:Nn #1#2
  {
    \__kernel_backend_scope_begin:e
      {
        transform =
          "
            rotate
            ( \fp_eval:n { round ( -(#2) , 5 ) } , ~ { ?x } , ~ { ?y } )
          "
      }
    \box_use:N #1
    \__kernel_backend_scope_end:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_backend_scale:Nnn}
%   In contrast to rotation, we have to account for the current position in this
%   case. That is done using a couple of translations in addition to the scaling
%   (which is therefore done backward with a flip).
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_scale:Nnn #1#2#3
  {
    \__kernel_backend_scope_begin:e
      {
        transform =
          "
            translate ( { ?x } , { ?y } ) ~
            scale
              (
                \fp_eval:n { round ( -#2 , 5 ) } ,
                \fp_eval:n { round ( -#3 , 5 ) }
              ) ~
            translate ( { ?x } , { ?y } ) ~
            scale ( -1 )
          "
      }
    \hbox_overlap_right:n { \box_use:N #1 }
    \__kernel_backend_scope_end:
  }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%</dvisvgm>
%    \end{macrocode}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex
