diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3drivers-box.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3drivers-box.dtx | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3drivers-box.dtx b/Master/texmf-dist/source/latex/l3kernel/l3drivers-box.dtx new file mode 100644 index 00000000000..f9bd0afad7c --- /dev/null +++ b/Master/texmf-dist/source/latex/l3kernel/l3drivers-box.dtx @@ -0,0 +1,473 @@ +% \iffalse meta-comment +% +%% File: l3drivers-box.dtx +% +% Copyright (C) 2011-2019 The LaTeX3 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 \textsf{l3drivers-box} package\\ Driver box support^^A +% } +% +% \author{^^A +% The \LaTeX3 Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \date{Released 2019-04-21} +% +% \maketitle +% +% \begin{documentation} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{l3drivers-box} Implementation} +% +% \begin{macrocode} +%<*initex|package> +%<@@=driver> +% \end{macrocode} +% +% \subsection{\texttt{dvips} driver} +% +% \begin{macrocode} +%<*dvips> +% \end{macrocode} +% +% \begin{macro}{\driver_box_use_clip:N} +% The \texttt{dvips} driver 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 \driver_box_use_clip:N #1 + { + \@@_scope_begin: + \@@_align_currentpoint_begin: + \@@_literal_postscript:n { matrix~currentmatrix } + \@@_literal_postscript:n + { Resolution~72~div~VResolution~72~div~scale } + \@@_literal_postscript:n { DVImag~dup~scale } + \@@_literal_postscript:x + { + 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 + } + \@@_literal_postscript:n { setmatrix } + \@@_align_currentpoint_end: + \hbox_overlap_right:n { \box_use:N #1 } + \@@_scope_end: + \skip_horizontal:n { \box_wd:N #1 } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\driver_box_use_rotate:Nn} +% \begin{macro}{\@@_box_use_rotate: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 \driver_box_use_rotate:Nn #1#2 + { \exp_args:NNf \@@_box_use_rotate:Nn #1 { \fp_eval:n {#2} } } +\cs_new_protected:Npn \@@_box_use_rotate:Nn #1#2 + { + \@@_scope_begin: + \@@_align_currentpoint_begin: + \@@_literal_postscript:x + { + \fp_compare:nNnTF {#2} = \c_zero_fp + { 0 } + { \fp_eval:n { round ( -(#2) , 5 ) } } ~ + rotate + } + \@@_align_currentpoint_end: + \box_use:N #1 + \@@_scope_end: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\driver_box_use_scale:Nnn} +% The \texttt{dvips} driver once again has a dedicated operation we can +% use here. +% \begin{macrocode} +\cs_new_protected:Npn \driver_box_use_scale:Nnn #1#2#3 + { + \@@_scope_begin: + \@@_align_currentpoint_begin: + \@@_literal_postscript:x + { + \fp_eval:n { round ( #2 , 5 ) } ~ + \fp_eval:n { round ( #3 , 5 ) } ~ + scale + } + \@@_align_currentpoint_end: + \hbox_overlap_right:n { \box_use:N #1 } + \@@_scope_end: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +%</dvips> +% \end{macrocode} +% +% \subsection{\texttt{pdfmode} driver} +% +% \begin{macrocode} +%<*pdfmode> +% \end{macrocode} +% +% \begin{macro}{\driver_box_use_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 \driver_box_use_clip:N #1 + { + \@@_scope_begin: + \@@_literal_pdf:x + { + 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 } + \@@_scope_end: + \skip_horizontal:n { \box_wd:N #1 } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\driver_box_use_rotate:Nn} +% \begin{macro}{\@@_box_use_rotate:Nn} +% \begin{variable}{\l_@@_cos_fp, \l_@@_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 \driver_box_use_rotate:Nn #1#2 + { \exp_args:NNf \@@_box_use_rotate:Nn #1 { \fp_eval:n {#2} } } +\cs_new_protected:Npn \@@_box_use_rotate:Nn #1#2 + { + \@@_scope_begin: + \box_set_wd:Nn #1 { 0pt } + \fp_set:Nn \l_@@_cos_fp { round ( cosd ( #2 ) , 5 ) } + \fp_compare:nNnT \l_@@_cos_fp = \c_zero_fp + { \fp_zero:N \l_@@_cos_fp } + \fp_set:Nn \l_@@_sin_fp { round ( sind ( #2 ) , 5 ) } + \@@_matrix:x + { + \fp_use:N \l_@@_cos_fp \c_space_tl + \fp_compare:nNnTF \l_@@_sin_fp = \c_zero_fp + { 0~0 } + { + \fp_use:N \l_@@_sin_fp + \c_space_tl + \fp_eval:n { -\l_@@_sin_fp } + } + \c_space_tl + \fp_use:N \l_@@_cos_fp + } + \box_use:N #1 + \@@_scope_end: + } +\fp_new:N \l_@@_cos_fp +\fp_new:N \l_@@_sin_fp +% \end{macrocode} +% \end{variable} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\driver_box_use_scale:Nnn} +% The same idea as for rotation but without the complexity of signs and +% cosines. +% \begin{macrocode} +\cs_new_protected:Npn \driver_box_use_scale:Nnn #1#2#3 + { + \@@_scope_begin: + \@@_matrix:x + { + \fp_eval:n { round ( #2 , 5 ) } ~ + 0~0~ + \fp_eval:n { round ( #3 , 5 ) } + } + \hbox_overlap_right:n { \box_use:N #1 } + \@@_scope_end: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +%</pdfmode> +% \end{macrocode} +% +% \subsection{\texttt{dvipdfmx} driver} +% +% \begin{macrocode} +%<*dvipdfmx|xdvipdfmx> +% \end{macrocode} +% +% \begin{macro}{\driver_box_use_clip:N} +% The code here is identical to that for \texttt{pdfmode}: unlike rotation and +% scaling, there is no higher-level support in the driver for clipping. +% \begin{macrocode} +\cs_new_protected:Npn \driver_box_use_clip:N #1 + { + \@@_scope_begin: + \@@_literal_pdf:x + { + 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 } + \@@_scope_end: + \skip_horizontal:n { \box_wd:N #1 } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\driver_box_use_rotate:Nn} +% \begin{macro}{\@@_box_use_rotate:Nn} +% Rotating in \texttt{(x)}dvipdmfx can be implemented using either PDF or +% driver-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 driver-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 \driver_box_use_rotate:Nn #1#2 + { \exp_args:NNf \@@_box_use_rotate:Nn #1 { \fp_eval:n {#2} } } +\cs_new_protected:Npn \@@_box_use_rotate:Nn #1#2 + { + \@@_scope_begin: + \@@_literal:x + { + x:rotate~ + \fp_compare:nNnTF {#2} = \c_zero_fp + { 0 } + { \fp_eval:n { round ( #2 , 5 ) } } + } + \box_use:N #1 + \@@_scope_end: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\driver_box_use_scale:Nnn} +% Much the same idea for scaling: use the higher-level driver operation to allow +% for box content. +% \begin{macrocode} +\cs_new_protected:Npn \driver_box_use_scale:Nnn #1#2#3 + { + \@@_scope_begin: + \@@_literal:x + { + x:scale~ + \fp_eval:n { round ( #2 , 5 ) } ~ + \fp_eval:n { round ( #3 , 5 ) } + } + \hbox_overlap_right:n { \box_use:N #1 } + \@@_scope_end: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +%</dvipdfmx|xdvipdfmx> +% \end{macrocode} +% +% \subsection{\texttt{dvisvgm} driver} +% +% \begin{macrocode} +%<*dvisvgm> +% \end{macrocode} +% +% \begin{macro}{\driver_box_use_clip:N} +% \begin{variable}{\g_@@_clip_path_int} +% Clipping in SVG is more involved than with other drivers. 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 \driver_box_use_clip:N #1 + { + \int_gincr:N \g_@@_clip_path_int + \@@_literal_svg:x + { < clipPath~id = " l3cp \int_use:N \g_@@_clip_path_int " > } + \@@_literal_svg:x + { + < + 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 + " + /> + } + \@@_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} + \@@_scope_begin:n + { + transform = + " + translate ( { ?x } , { ?y } ) ~ + scale ( 1 , -1 ) + " + } + \@@_scope_begin:x + { + clip-path = + "url ( \c_hash_str l3cp \int_use:N \g_@@_clip_path_int ) " + } + \@@_scope_begin:n + { + transform = + " + scale ( -1 , 1 ) ~ + translate ( { ?x } , { ?y } ) ~ + scale ( -1 , -1 ) + " + } + \box_use:N #1 + \@@_scope_end: + \@@_scope_end: + \@@_scope_end: +% \skip_horizontal:n { \box_wd:N #1 } + } +\int_new:N \g_@@_clip_path_int +% \end{macrocode} +% \end{variable} +% \end{macro} +% +% \begin{macro}{\driver_box_use_rotate:Nn} +% Rotation has a dedicated operation which includes a centre-of-rotation +% optional pair. That can be picked up from the driver syntax, so there is +% no need to worry about the transformation matrix. +% \begin{macrocode} +\cs_new_protected:Npn \driver_box_use_rotate:Nn #1#2 + { + \@@_scope_begin:x + { + transform = + " + rotate + ( \fp_eval:n { round ( -(#2) , 5 ) } , ~ { ?x } , ~ { ?y } ) + " + } + \box_use:N #1 + \@@_scope_end: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\driver_box_use_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 \driver_box_use_scale:Nnn #1#2#3 + { + \@@_scope_begin:x + { + 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 } + \@@_scope_end: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +%</dvisvgm> +% \end{macrocode} +% +% \begin{macrocode} +%</initex|package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex |