summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/latex-lab/latex-lab-graphic.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/latex-lab/latex-lab-graphic.dtx')
-rw-r--r--Master/texmf-dist/source/latex/latex-lab/latex-lab-graphic.dtx1073
1 files changed, 1073 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/latex-lab/latex-lab-graphic.dtx b/Master/texmf-dist/source/latex/latex-lab/latex-lab-graphic.dtx
new file mode 100644
index 00000000000..f9457e0524f
--- /dev/null
+++ b/Master/texmf-dist/source/latex/latex-lab/latex-lab-graphic.dtx
@@ -0,0 +1,1073 @@
+% \iffalse meta-comment
+%
+%% File: latex-lab-graphic.dtx (C) Copyright 2022-2023 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
+%
+%
+% The development version of the bundle can be found below
+%
+% https://github.com/latex3/latex2e/required/latex-lab
+%
+% for those people who are interested or want to report an issue.
+%
+%<*driver>
+\documentclass{l3doc}
+\EnableCrossrefs
+\CodelineIndex
+\begin{document}
+ \DocInput{latex-lab-graphic.dtx}
+\end{document}
+%</driver>
+%
+% \fi
+%
+%
+% \title{The \textsf{latex-lab-graphic} package\\
+% Tagging of included graphics }
+% \author{\LaTeX{} Project\thanks{Initial implementation done by Ulrike Fischer}}
+% \date{v0.8 2023-04-07}
+%
+% \maketitle
+%
+% \newcommand{\xt}[1]{\textsl{\textsf{#1}}}
+% \newcommand{\TODO}[1]{\textbf{[TODO:} #1\textbf{]}}
+% \newcommand{\docclass}{document class \marginpar{\raggedright document class
+% customizations}}
+%
+% \providecommand\hook[1]{\texttt{#1}}
+% \begin{documentation}
+% \begin{abstract}
+% The following code implements a first draft for the tagging of graphics
+% included with \cs{includegraphics}.
+% \end{abstract}
+%
+%
+% \section{Introduction}
+%
+% The code here handle the tagging of pictures included with \cs{includegraphics}
+% and the picture environment. Pictures drawn with \texttt{l3draw} or \texttt{tikz}
+% or similar packages aren't handled yet.
+%
+% Tagging of graphics included with \cs{includegraphics} is at a first glance trivial:
+% They are either only decorations, in which case they should be in a \texttt{artifact}
+% MC-chunk or (in pdf 2.0) tagged as an \texttt{Artifact} structure,
+% or they are meaningful and then they should be tagged as a \texttt{Figure}.
+% Such a graphic is a simple box and no other content can interfere so adding the
+% structure commands shouldn't pose much problems.
+%
+% But things are actually not so easy.
+%
+% At first there are two ways to add a graphic
+% to a structure: similar to text as a marked content item (by surrounding
+% it with \cs{tagmcbegin} and \cs{tagmcend}) or by referencing the XObject
+% with an OBJR object (similar to a link annotation). Which method is more sensible
+% (and if it actually matters) is unknown but should be tested.
+% Currently the first method is used as the second require changes in the backend files.
+%
+%
+% At second---and this is actually a \emph{much} larger problem---a
+% \texttt{Figure} structure should have an attribute with an \texttt{BBox} entry.
+% The value of a \texttt{BBox} is an array of four numbers that gives the
+% coordinates of the left, bottom, right, and top edges
+% of the structure element’s bounding box. That is the rectangle that completely encloses
+% its \emph{visible} content so not necessarly the TeX bounding box:
+% if \texttt{viewport} or \texttt{trim} is used and the
+% graphic is not clipped, the visible content can be larger.
+%
+% Getting the \texttt{BBox} is quite straightforward for a graphic that is
+% used once as is. But graphics can be trimmed, scaled, reflected, rotated and reused
+% in various ways. This transformations typically involve a mix of \TeX{} commands
+% like shifting a box or changing the bounding box and backend commands like
+% inserting a pdfliteral with a transformation matrix and
+% and not in all cases getting the \texttt{BBox} is possible
+% without rewriting large parts of the graphics/x packages. Problematic are
+% \begin{itemize}
+% \item manipulations through external box commands
+% (\cs{rotatebox}, \cs{reflectbox}, \cs{scalebox}). The current implementation
+% in the graphics/x packages do not pass the transformation matrix in way
+% that allows to track the changes for the \texttt{BBox}
+% of an included graphic: sometimes
+% the values are set to late (after the box is already stored), and often the
+% values are not grouped and can leak out from earlier uses of the commands.
+%
+% \item some combination of keys in the optional argument of \cs{includegraphics}.
+% Examples are \texttt{origin} and multiple calls to \texttt{scale}
+% and \texttt{angle}) as they internally call the box commands.
+% Examples of failing combinations can be found in the test file
+% \texttt{graphic-faults}.
+%
+% \item graphics that are stored in a box and reused: to get the \texttt{BBox}
+% one has to set a label that stores the position with \cs{pdfsavepos},
+% and if a box is reused one gets multiply defined labels.
+% One possible solution here is to make
+% use of the new delayed \cs{pdfliteral}. It allows to change the label names
+% in the shipout, but this requires careful tracking the box usages
+% and so various kernel changes.
+% \end{itemize}
+%
+%
+%
+% \section{Restrictions and Todos}
+% Correct tagging is currently
+% implemented only for simple \cs{includegraphics} and the keys
+% \texttt{viewport}, \texttt{trim},
+% \texttt{scale} and \texttt{angle} (used at most once).
+%
+% Not supported
+% \begin{itemize}
+% \item graphics inside \cs{rotatebox}, \cs{reflectbox}, \cs{scalebox}.
+%
+% TODO: A new implementation with \texttt{l3graphics} and \texttt{l3box} is
+% probably needed here.
+%
+% \item multiple uses of the \texttt{scale} and \texttt{angle} keys
+%
+% \item multiple use of graphics stored in boxes. For such graphics automated tagging
+% should be probably deactivated when storing the content
+% and tagging should be added around the \cs{usebox}. (How to
+% proceed when content is saved in boxes needs generally more testing).
+% \end{itemize}
+%
+%
+% \section{Additional keys}
+% The code defines additional keys for \cs{includegraphics}:
+%
+% \begin{description}
+% \item[\texttt{tag}] with the values
+% \begin{description}
+% \item[\texttt{artifact}] When used the graphic will be tagged as artifact. This doesn't
+% require a \texttt{BBox} and so works also in some of the not yet supported cases described
+% above.
+% \item[\texttt{false}] When used tagging will be stopped completly. It is then the
+% responsability of the surrounding code to add appropriate tagging commands.
+% \item[\meta{name}] Other values will be used as tag names in the structure. If the tag is not
+% known as a structure tag you will get an warning from tagpdf. The default name is currently
+% \texttt{Figure}
+% \end{description}
+% \item[\texttt{actualtext}] This allows to add an /ActualText to the structure.
+% This is useful for small graphics that represent single chars or a short word like a
+% logo. If \texttt{actualtext} is used, the graphics is not enclosed in \texttt{Figure}
+% structure but in a \texttt{Span} structure and no \texttt{/BBox} attribute is added.
+% This in accordance with (the draft of) PDF/UA-2 but violates perhaps PDF/UA-1.
+% \item[\texttt{correct-BBox}] If the calculated \texttt{/BBox} values are wrong
+% they can be correct with this key. It expects four dimensions that are added to the
+% \texttt{/BBox} values.
+% \item[\texttt{debug}] The value \texttt{BBox} will show the calculated \texttt{/BBox}
+% as a half transparent red rectangle.
+% \end{description}
+%
+% The code also redefines the \texttt{alt} key to actually add its values
+% as an alternative text. If no \texttt{alt} value is given, a warning is issued
+% and the file name of the graphic is used.
+%
+% \begin{macrocode}
+%<@@=tag>
+%<*package>
+% \end{macrocode}
+% \end{documentation}
+% \begin{implementation}
+% \section{Implementation}
+% \begin{macrocode}
+\ProvidesExplPackage {latex-lab-testphase-graphic} {2023-04-07} {0.8}
+ {Code related to the tagging of graphics}
+% \end{macrocode}
+% We load l3opacity for the debug code
+% \begin{macrocode}
+\RequirePackage{l3opacity}
+% \end{macrocode}
+%
+%\subsection{Saving the position}
+% We need a replacement for zref-savepos. This uses l3ref which loaded
+% by the pdfmanagement/tagpdf.
+%
+% TODO: this is perhaps needed in other places too and should be moved to
+% l3ref!!
+% \begin{macrocode}
+\ref_attribute_gset:nnnn{xpos}{0}{shipout}{\int_use:N\tex_lastxpos:D}
+\ref_attribute_gset:nnnn{ypos}{0}{shipout}{\int_use:N\tex_lastypos:D}
+% \end{macrocode}
+
+% \begin{macro}{\@@_graphic_savepos:n}
+% this is the command which stores the position. It uses
+% two savepos in case bidi needs it (see zref-savepos).
+% \begin{macrocode}
+\cs_new_protected:Npn\@@_graphic_savepos:n #1
+ {
+ \tex_savepos:D
+ \ref_label:nn{#1}{xpos,ypos,abspage}
+ \tex_savepos:D
+ }
+\cs_generate_variant:Nn \@@_graphic_savepos:n {e}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Variables}
+% \begin{variable}{\l_@@_graphic_debug_bool}
+% A boolean for debug code
+% \begin{macrocode}
+\bool_new:N \l_@@_graphic_debug_bool
+\keys_define:nn { document / metadata }
+ {
+ debug / BBox .code:n = { \bool_set_true:N \l_@@_graphic_debug_bool }
+ }
+% \end{macrocode}
+% \end{variable}
+% \begin{variable}{\g_@@_graphic_int}
+% This is used to get unique labels in the savepos code.
+% \begin{macrocode}
+\int_new:N\g_@@_graphic_int
+% \end{macrocode}
+% \end{variable}
+
+% \begin{variable}
+% {
+% \g_@@_graphic_lx_tl,
+% \g_@@_graphic_ly_tl,
+% \g_@@_graphic_ux_tl,
+% \g_@@_graphic_uy_tl,
+% \l_@@_graphic_bboxcorr_seq
+% \l_@@_graphic_bboxcorr_bool
+% }
+% This commands will hold the calculated BBox values. Local variables
+% would probably work too, but global variables can be easier retrieved
+% in tests and debugging code ...
+% \begin{macrocode}
+\tl_new:N \g_@@_graphic_lx_tl
+\tl_new:N \g_@@_graphic_ly_tl
+\tl_new:N \g_@@_graphic_ux_tl
+\tl_new:N \g_@@_graphic_uy_tl
+\seq_new:N\l_@@_graphic_bboxcorr_seq
+\bool_new:N\l_@@_graphic_bboxcorr_bool
+% \end{macrocode}
+% \end{variable}
+% \begin{variable}{\l_@@_graphic_currentlabel_tl}
+% This holds the label name of the savepos.
+% \begin{macrocode}
+\tl_new:N \l_@@_graphic_currentlabel_tl
+% \end{macrocode}
+% \end{variable}
+
+% \begin{variable}
+% {
+% \l_@@_graphic_alt_tl,
+% \l_@@_graphic_alt_dft_tl,
+% \l_@@_graphic_actual_tl,
+% \l_@@_graphic_struct_tl,
+% \l_@@_graphic_artifact_bool,
+% \l_@@_graphic_BBox_bool
+% }
+% Variables for the alt text, the actualtext and the structure tag.
+% \begin{macrocode}
+\tl_new:N \l_@@_graphic_alt_tl
+\tl_new:N \l_@@_graphic_alt_dflt_tl
+\tl_set:Nn \l_@@_graphic_alt_dflt_tl {\Gin@base\Gin@ext}
+\tl_new:N \l_@@_graphic_actual_tl
+\tl_new:N \l_@@_graphic_struct_tl
+\tl_set:Nn\l_@@_graphic_struct_tl {Figure}
+\bool_new:N\l_@@_graphic_artifact_bool
+\bool_new:N\l_@@_graphic_BBox_bool
+\bool_set_true:N\l_@@_graphic_BBox_bool
+% \end{macrocode}
+% \end{variable}
+
+% \begin{variable}
+% {
+% \l_@@_graphic_sin_fp
+% ,\l_@@_graphic_cos_fp
+% ,\l_@@_graphic_scale_fp
+% ,\l_@@_graphic_lxly_fp
+% ,\l_@@_graphic_lxuy_fp
+% ,\l_@@_graphic_uxly_fp
+% ,\l_@@_graphic_uxuy_fp
+% ,\l_@@_graphic_ux_fp
+% ,\l_@@_graphic_ly_fp
+% ,\l_@@_graphic_lx_fp
+% ,\l_@@_graphic_uy_fp
+% ,\l_@@_graphic_trim_ux_fp
+% ,\l_@@_graphic_trim_ly_fp
+% ,\l_@@_graphic_trim_lx_fp
+% ,\l_@@_graphic_trim_uy_fp
+% }
+% A bunch of fp-variables (we don't use tl-vars,
+% to avoid to have to take care about minus signs everwhere)
+% \begin{macrocode}
+\fp_new:N\l_@@_graphic_sin_fp
+\fp_new:N\l_@@_graphic_cos_fp
+\fp_new:N\l_@@_graphic_lxly_fp
+\fp_new:N\l_@@_graphic_lxuy_fp
+\fp_new:N\l_@@_graphic_uxly_fp
+\fp_new:N\l_@@_graphic_uxuy_fp
+\fp_new:N\l_@@_graphic_ux_fp
+\fp_new:N\l_@@_graphic_ly_fp
+\fp_new:N\l_@@_graphic_lx_fp
+\fp_new:N\l_@@_graphic_uy_fp
+% \end{macrocode}
+% this holds the scale value. Currently this only the one from
+% \cs{Gin@scalex}
+% \begin{macrocode}
+\fp_new:N\l_@@_graphic_scale_fp
+% \end{macrocode}
+% the follow variables hold the four trim values (or the equivalent calculated
+% values if viewport is used.
+% \begin{macrocode}
+\fp_new:N\l_@@_graphic_trim_ux_fp
+\fp_new:N\l_@@_graphic_trim_ly_fp
+\fp_new:N\l_@@_graphic_trim_lx_fp
+\fp_new:N\l_@@_graphic_trim_uy_fp
+% \end{macrocode}
+% \end{variable}
+%
+%\subsection{Tagging commands}
+%
+% \begin{macro}{\Gin@tag@struct@begin}
+% The command to start the tagging.
+% \begin{macrocode}
+\msg_new:nnn {tag}{alt-text-missing}
+ {
+ Alternative~text~for~graphic~is~missing.\\
+ Using~'#1'~instead
+ }
+\cs_new_protected:Npn\Gin@tag@struct@begin
+ {
+ \tag_if_active:T
+ {
+ \tag_mc_end_push:
+% \end{macrocode}
+% we don't open a structure for artifacts to make it easier to use
+% graphics in saveboxes.
+% \begin{macrocode}
+ \bool_if:NTF\l_@@_graphic_artifact_bool
+ {
+ \tag_mc_begin:n{artifact}
+ }
+ {
+ \tl_if_empty:NTF\l_@@_graphic_actual_tl
+ {
+ \tl_if_empty:NT\l_@@_graphic_alt_tl
+ {
+ \msg_warning:nnx{tag}{alt-text-missing}{\l_@@_graphic_alt_dflt_tl}
+ \tl_set:Nx\l_@@_graphic_alt_tl {\l_@@_graphic_alt_dflt_tl}
+ }
+ \tag_struct_begin:n
+ {
+ tag=\l_@@_graphic_struct_tl,
+ alt=\l_@@_graphic_alt_tl,
+ }
+ }
+ {
+ \tag_struct_begin:n
+ {
+ tag=Span,
+ actualtext=\l_@@_graphic_actual_tl,
+ }
+ \bool_set_false:N\l_@@_graphic_BBox_bool
+ }
+ \tag_mc_begin:n{}
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\Gin@tag@struct@end}
+% \begin{macrocode}
+\cs_new_protected:Npn\Gin@tag@struct@end
+ {
+ \tag_if_active:T
+ {
+ \tag_mc_end:
+ \bool_if:NF\l_@@_graphic_artifact_bool
+ {
+ \tag_struct_end:
+ }
+ \tag_mc_begin_pop:n{}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Patching graphics commands}
+% All changes are currently done in \cs{Gin@setfile}.
+% \begin{macrocode}
+\AddToHook{package/graphics/after}
+ {
+ \def\Gin@setfile#1#2#3{%
+ \ifx\\#2\\\Gread@false\fi
+ \ifGin@bbox\else
+ \ifGread@
+ \csname Gread@%
+ \expandafter\ifx\csname Gread@#1\endcsname\relax
+ eps%
+ \else
+ #1%
+ \fi
+ \endcsname{\Gin@base#2}%
+ \else
+ \Gin@nosize{#3}%
+ \fi
+ \fi
+ \Gin@viewport@code
+ \Gin@nat@height\Gin@ury bp%
+ \advance\Gin@nat@height-\Gin@lly bp%
+ \Gin@nat@width\Gin@urx bp%
+ \advance\Gin@nat@width-\Gin@llx bp%
+ \Gin@req@sizes
+ \expandafter\ifx\csname Ginclude@#1\endcsname\relax
+ \Gin@drafttrue
+ \expandafter\ifx\csname Gread@#1\endcsname\relax
+ \@latex@error{Can not include graphics of type: #1}\@ehc
+ \global\expandafter\let\csname Gread@#1\endcsname\@empty
+ \fi
+ \fi
+ \leavevmode
+% \end{macrocode}
+% Here the tagging begins. We want to catch also the draft box,
+% and for luatex tagging must be started before the \cs{setbox}.
+% \begin{macrocode}
+ \Gin@tag@struct@begin %NEW
+ \ifGin@draft
+ \hb@xt@\Gin@req@width{%
+ \vrule\hss
+ \vbox to \Gin@req@height{%
+ \hrule \@width \Gin@req@width
+ \vss
+ \edef\@tempa{#3}%
+ \rlap{ \ttfamily\expandafter\strip@prefix\meaning\@tempa}%
+ \vss
+ \hrule}%
+ \hss\vrule}%
+ \else
+ \@addtofilelist{#3}%
+ \ProvidesFile{#3}[Graphic file (type #1)]%
+ \setbox\z@\hbox{\csname Ginclude@#1\endcsname{#3}}%
+ \dp\z@\z@
+ \ht\z@\Gin@req@height
+ \wd\z@\Gin@req@width
+% \end{macrocode}
+% This the main command to calculate the BBox values.
+% \begin{macrocode}
+ \Gin@tag@bbox@attribute %new
+ \box\z@
+% \end{macrocode}
+% and here the tagging stops.
+% \begin{macrocode}
+ \Gin@tag@struct@end %new
+ \fi}
+ }
+% \end{macrocode}
+%
+% \subsection{Additional keys for the graphics command}
+% TODO: this is a bit temporary and will perhaps need more refinement.
+% we also ensure that graphicx is loaded for the keyval support.
+% \begin{macrocode}
+\AddToHook{package/graphicx/after}[latex-lab]
+ {
+ \define@key{Gin}{alt} {\tl_set:Nx\l_@@_graphic_alt_tl{\text_purify:n{#1}}}
+ \define@key{Gin}{artifact}[]
+ {
+ \bool_set_true:N \l_@@_graphic_artifact_bool
+ \bool_set_false:N \l_@@_graphic_BBox_bool
+ }
+ \define@key{Gin}{actualtext}
+ {
+ \tl_set:Nx\l_@@_graphic_actual_tl{\text_purify:n{#1}}
+ \bool_set_false:N \l_@@_graphic_BBox_bool
+ }
+ \define@key{Gin}{correct-BBox}
+ {
+ \bool_set_true:N \l_@@_graphic_bboxcorr_bool
+ \seq_set_split:Nnn\l_@@_graphic_bboxcorr_seq{~}{#1~0pt~0pt~0pt~0pt}
+ }
+ \define@key{Gin}{tag}
+ {
+ \str_case:nnF {#1}
+ {
+ {artifact}
+ {
+ \bool_set_true:N \l_@@_graphic_artifact_bool
+ \bool_set_false:N \l_@@_graphic_BBox_bool
+ }
+ {false}{\tag_stop:}
+ }
+ {\tl_set:Nn\l_@@_graphic_struct_tl{#1}}
+ }
+ }
+\AddToHook{package/graphics/after}[latex-lab]
+ {\RequirePackage{graphicx}}
+% \end{macrocode}
+% For picture and other environments we need a similar set of keys.
+% TODO: redefine \cs{includegraphics} to make use of these here??
+% \begin{macrocode}
+\keys_define:nn{tag/picture}
+ {
+ ,alt .code:n =
+ {\tl_set:Nx\l_@@_graphic_alt_tl{\text_purify:n{#1}}}
+ ,artifact .code:n =
+ {
+ \bool_set_true:N \l_@@_graphic_artifact_bool
+ \bool_set_false:N \l_@@_graphic_BBox_bool
+ }
+ ,actualtext .code:n =
+ {
+ \tl_set:Nx\l_@@_graphic_actual_tl{\text_purify:n{#1}}
+ \bool_set_false:N \l_@@_graphic_BBox_bool
+ }
+ ,correct-BBox .code:n =
+ {
+ \bool_set_true:N \l_@@_graphic_bboxcorr_bool
+ \seq_set_split:Nnn\l_@@_graphic_bboxcorr_seq{~}{#1~0pt~0pt~0pt~0pt}
+ }
+ ,tag .code:n =
+ {
+ \str_case:nnF {#1}
+ {
+ {artifact}
+ {
+ \bool_set_true:N \l_@@_graphic_artifact_bool
+ \bool_set_false:N \l_@@_graphic_BBox_bool
+ }
+ {false}{\tag_stop:}
+ }
+ {\tl_set:Nn\l_@@_graphic_struct_tl{#1}}
+ }
+ }
+% \end{macrocode}
+%
+% \subsection{Calculating the BBox}
+%
+% \begin{macro}{\@@_graphic_get_trim:}
+% Graphics can be trimmed with the trim and the viewport key.
+% If the graphic is not clipped the values must be taken into
+% account when rotating.
+% If viewport is used we have to calculate the trim.
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_graphic_get_trim:
+ {
+ \legacy_if:nTF {Gin@clip}
+% \end{macrocode}
+% Setting to 0 is not strictly needed but looks cleaner.
+% \begin{macrocode}
+ {
+ \fp_zero:N\l_@@_graphic_trim_lx_fp
+ \fp_zero:N\l_@@_graphic_trim_ly_fp
+ \fp_zero:N\l_@@_graphic_trim_ux_fp
+ \fp_zero:N\l_@@_graphic_trim_uy_fp
+ }
+ {
+ \fp_set:Nn \l_@@_graphic_trim_lx_fp {\l_@@_graphic_scale_fp*\Gin@vllx}
+ \fp_set:Nn \l_@@_graphic_trim_ly_fp {\l_@@_graphic_scale_fp*\Gin@vlly}
+ \fp_set:Nn \l_@@_graphic_trim_ux_fp {\l_@@_graphic_scale_fp*\Gin@vurx}
+ \fp_set:Nn \l_@@_graphic_trim_uy_fp {\l_@@_graphic_scale_fp*\Gin@vury}
+ \cs_if_exist:NT \Gin@ollx
+ {
+ \fp_set:Nn \l_@@_graphic_trim_ux_fp {\l_@@_graphic_scale_fp* (\Gin@ourx-(\Gin@urx)) }
+ \fp_set:Nn \l_@@_graphic_trim_uy_fp {\l_@@_graphic_scale_fp* (\Gin@oury-(\Gin@ury)) }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_graphic_get_scale:}
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_graphic_get_scale:
+ {
+ \fp_set:Nn \l_@@_graphic_scale_fp { \Gin@scalex }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_graphic_applyangle:nnnn}
+% This takes the current BBox and rotates it according to the use angle.
+% This is the most laborious code, as we have to take also the trim values into
+% account. We have to compare the values after the rotation to find the right corners
+% for the BBox. Not sure, if this is the most effective code,
+% the l3draw package has similar code to calculate a rotation,
+% this can perhaps be reused ...
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_graphic_applyangle:nnnn #1#2#3#4 %lx,ly,ux,uy
+ {
+ \bool_lazy_and:nnT
+ {\cs_if_exist_p:N \Grot@angle }
+ {! \int_compare_p:nNn { \Grot@angle }={0}}
+ {
+ \fp_set:Nn \l_@@_graphic_sin_fp { sind(\Grot@angle) }
+ \fp_set:Nn \l_@@_graphic_cos_fp { cosd(\Grot@angle) }
+ \fp_set:Nn \l_@@_graphic_lx_fp {#1}
+ \fp_set:Nn \l_@@_graphic_ly_fp {#2}
+ \fp_set:Nn \l_@@_graphic_ux_fp {#3}
+ \fp_set:Nn \l_@@_graphic_uy_fp {#4}
+% \end{macrocode}
+% get the x coordinates (cos,-sin)
+% \begin{macrocode}
+ \fp_set:Nn\l_@@_graphic_lxly_fp
+ {
+ -\l_@@_graphic_trim_lx_fp * \l_@@_graphic_cos_fp
+ +\l_@@_graphic_trim_ly_fp * \l_@@_graphic_sin_fp
+ }
+ \fp_set:Nn\l_@@_graphic_lxuy_fp
+ {
+ (-\l_@@_graphic_trim_lx_fp) * \l_@@_graphic_cos_fp
+ +
+ (\l_@@_graphic_uy_fp-\l_@@_graphic_ly_fp-\l_@@_graphic_trim_ly_fp)
+ * (-\l_@@_graphic_sin_fp)
+ }
+ \fp_set:Nn\l_@@_graphic_uxly_fp
+ {
+ (\l_@@_graphic_ux_fp-\l_@@_graphic_lx_fp-\l_@@_graphic_trim_lx_fp)
+ * \l_@@_graphic_cos_fp
+ +
+ (\l_@@_graphic_trim_ly_fp) * (\l_@@_graphic_sin_fp)
+ }
+ \fp_set:Nn\l_@@_graphic_uxuy_fp
+ {
+ (\l_@@_graphic_ux_fp-\l_@@_graphic_lx_fp-\l_@@_graphic_trim_lx_fp)
+ * \l_@@_graphic_cos_fp
+ +
+ (\l_@@_graphic_uy_fp-\l_@@_graphic_ly_fp-\l_@@_graphic_trim_ly_fp)
+ * (-\l_@@_graphic_sin_fp)
+ }
+ \tl_gset:Nx\g_@@_graphic_lx_tl
+ {
+ \fp_eval:n
+ {
+ min
+ (
+ \l_@@_graphic_lxly_fp,
+ \l_@@_graphic_lxuy_fp,
+ \l_@@_graphic_uxly_fp,
+ \l_@@_graphic_uxuy_fp,
+ )
+ +\l_@@_graphic_lx_fp
+ +\l_@@_graphic_trim_lx_fp
+ }
+ }
+ \tl_gset:Nx\g_@@_graphic_ux_tl
+ {
+ \fp_eval:n
+ {
+ max
+ (
+ \l_@@_graphic_lxly_fp,
+ \l_@@_graphic_lxuy_fp,
+ \l_@@_graphic_uxly_fp,
+ \l_@@_graphic_uxuy_fp
+ )
+ +\l_@@_graphic_lx_fp
+ +\l_@@_graphic_trim_lx_fp
+ }
+ }
+% \end{macrocode}
+% get the y coordinates (sin,cos)
+% \begin{macrocode}
+ \fp_set:Nn\l_@@_graphic_lxly_fp
+ {
+ -\l_@@_graphic_trim_lx_fp * \l_@@_graphic_sin_fp
+ -\l_@@_graphic_trim_ly_fp * \l_@@_graphic_cos_fp
+ }
+ \fp_set:Nn\l_@@_graphic_lxuy_fp
+ {
+ - \l_@@_graphic_trim_lx_fp * \l_@@_graphic_sin_fp
+ +
+ (\l_@@_graphic_uy_fp-\l_@@_graphic_ly_fp-\l_@@_graphic_trim_ly_fp)
+ * \l_@@_graphic_cos_fp
+ }
+ \fp_set:Nn\l_@@_graphic_uxly_fp
+ {
+ (\l_@@_graphic_ux_fp-\l_@@_graphic_lx_fp-\l_@@_graphic_trim_lx_fp)
+ * \l_@@_graphic_sin_fp
+ - \l_@@_graphic_trim_ly_fp * \l_@@_graphic_cos_fp
+ }
+ \fp_set:Nn\l_@@_graphic_uxuy_fp
+ {
+ (\l_@@_graphic_ux_fp-\l_@@_graphic_lx_fp-\l_@@_graphic_trim_lx_fp)
+ * \l_@@_graphic_sin_fp
+ +
+ (\l_@@_graphic_uy_fp-\l_@@_graphic_ly_fp-\l_@@_graphic_trim_ly_fp)
+ * \l_@@_graphic_cos_fp
+ }
+ \tl_gset:Nx\g_@@_graphic_ly_tl
+ {
+ \fp_eval:n
+ {
+ min
+ (
+ \l_@@_graphic_lxly_fp,
+ \l_@@_graphic_lxuy_fp,
+ \l_@@_graphic_uxly_fp,
+ \l_@@_graphic_uxuy_fp
+ )
+ + \l_@@_graphic_ly_fp + \l_@@_graphic_trim_ly_fp
+ }
+ }
+ \tl_gset:Nx\g_@@_graphic_uy_tl
+ {
+ \fp_eval:n
+ {
+ max
+ (
+ \l_@@_graphic_lxly_fp,
+ \l_@@_graphic_lxuy_fp,
+ \l_@@_graphic_uxly_fp,
+ \l_@@_graphic_uxuy_fp,
+ )
+ + \l_@@_graphic_ly_fp + \l_@@_graphic_trim_ly_fp
+ }
+ }
+ }
+ }
+\cs_generate_variant:Nn\@@_graphic_applyangle:nnnn {VVVV}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\@@_graphic_applycorr:NNNN}
+% This command is used to add at the end the correction values. Quite dump ...
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_graphic_applycorr:NNNN #1 #2 #3 #4
+ {
+ \bool_if:NT\l_@@_graphic_bboxcorr_bool
+ {
+ \tl_set:Nx #1
+ {
+ \fp_eval:n
+ {
+ #1
+ +
+ \dim_to_decimal_in_bp:n {\seq_item:Nn \l_@@_graphic_bboxcorr_seq {1} }
+ }
+ }
+ \tl_set:Nx #2
+ {
+ \fp_eval:n
+ {
+ #2
+ +
+ \dim_to_decimal_in_bp:n {\seq_item:Nn \l_@@_graphic_bboxcorr_seq {2} }
+ }
+ }
+ \tl_set:Nx #3
+ {
+ \fp_eval:n
+ {
+ #3
+ +
+ \dim_to_decimal_in_bp:n {\seq_item:Nn \l_@@_graphic_bboxcorr_seq {3} }
+ }
+ }
+ \tl_set:Nx #4
+ {
+ \fp_eval:n
+ {
+ #4
+ +
+ \dim_to_decimal_in_bp:n {\seq_item:Nn \l_@@_graphic_bboxcorr_seq {4} }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\Gin@tag@bbox@attribute}
+% This is the main command to calculate and set the Bbox attribute
+% \begin{macrocode}
+\cs_new_protected:Npn \Gin@tag@bbox@attribute
+ {
+% \end{macrocode}
+% the attribute is only needed if tagging is active and there is not artifact.
+% \begin{macrocode}
+ \bool_lazy_all:nT
+ {
+ {\tag_if_active_p:}
+ {!\l_@@_graphic_artifact_bool}
+ {\l_@@_graphic_BBox_bool}
+ }
+ {
+ \fp_set:Nn \l_@@_graphic_scale_fp { \Gin@scalex }
+ \@@_graphic_get_trim:
+ \int_gincr:N\g_@@_graphic_int
+ \tl_set:Nx\l_@@_graphic_currentlabel_tl {_@@_graphic_\int_use:N \g_@@_graphic_int}
+ \@@_graphic_savepos:e { \l_@@_graphic_currentlabel_tl }
+ \tl_gset:Nx\g_@@_graphic_lx_tl
+ {
+ \dim_to_decimal_in_bp:n
+ { \@@_ref_value:enn {\l_@@_graphic_currentlabel_tl}{xpos}{0}sp }
+ }
+ \tl_gset:Nx\g_@@_graphic_ly_tl
+ {
+ \dim_to_decimal_in_bp:n
+ { \@@_ref_value:enn {\l_@@_graphic_currentlabel_tl}{ypos}{0}sp }
+ }
+ \tl_gset:Nx\g_@@_graphic_ux_tl
+ {
+ \fp_eval:n
+ {
+ \g_@@_graphic_lx_tl
+ +
+ \dim_to_decimal_in_bp:n { \Gin@req@width }
+ }
+ }
+ \tl_gset:Nx\g_@@_graphic_uy_tl
+ {
+ \fp_eval:n
+ {
+ \g_@@_graphic_ly_tl
+ +
+ \dim_to_decimal_in_bp:n { \Gin@req@height }
+ }
+ }
+% \end{macrocode}
+% If the graphics is not clipped we must add the trim values.
+% \begin{macrocode}
+ \legacy_if:nF {Gin@clip}
+ {
+ \tl_gset:Nx\g_@@_graphic_ux_tl
+ {
+ \fp_eval:n
+ {
+ \g_@@_graphic_ux_tl
+ +
+ \l_@@_graphic_trim_ux_fp
+ }
+ }
+ \tl_gset:Nx\g_@@_graphic_lx_tl
+ {
+ \fp_eval:n
+ {
+ \g_@@_graphic_lx_tl
+ -
+ \l_@@_graphic_trim_lx_fp
+ }
+ }
+ \tl_gset:Nx\g_@@_graphic_uy_tl
+ {
+ \fp_eval:n
+ {
+ \g_@@_graphic_uy_tl
+ +
+ \l_@@_graphic_trim_uy_fp
+ }
+ }
+ \tl_gset:Nx\g_@@_graphic_ly_tl
+ {
+ \fp_eval:n
+ {
+ \g_@@_graphic_ly_tl
+ -
+ \l_@@_graphic_trim_ly_fp
+ }
+ }
+ }
+% \end{macrocode}
+% If there is an angle we now rotate the values.
+% \begin{macrocode}
+ \@@_graphic_applyangle:VVVV
+ \g_@@_graphic_lx_tl
+ \g_@@_graphic_ly_tl
+ \g_@@_graphic_ux_tl
+ \g_@@_graphic_uy_tl
+% \end{macrocode}
+% At last we have to add the correction values
+% \begin{macrocode}
+ \@@_graphic_applycorr:NNNN
+ \g_@@_graphic_lx_tl
+ \g_@@_graphic_ly_tl
+ \g_@@_graphic_ux_tl
+ \g_@@_graphic_uy_tl
+% \end{macrocode}
+% \begin{macrocode}
+ \bool_if:NT\l_@@_graphic_debug_bool
+ {
+ \@@_graphic_show_bbox:VVVVne
+ \g_@@_graphic_lx_tl
+ \g_@@_graphic_ly_tl
+ \g_@@_graphic_ux_tl
+ \g_@@_graphic_uy_tl
+ {red}
+ {\int_use:N\g_@@_graphic_int}
+ }
+% \end{macrocode}
+% Now we add the attribute. We do it manually as it had to be delayed until now.
+% The structure and the mc must be open earlier, before the \cs{setbox} (at least
+% for luatex it has to). TODO: think about interface if more attributes are needed.
+% \begin{macrocode}
+ \@@_prop_gput:cnx
+ { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop }
+ { A }
+ {
+ <<
+ /O /Layout /BBox~
+ [
+ \g_@@_graphic_lx_tl\c_space_tl
+ \g_@@_graphic_ly_tl\c_space_tl
+ \g_@@_graphic_ux_tl\c_space_tl
+ \g_@@_graphic_uy_tl
+ ]
+ >>
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \subsection{Support for the picture environment}
+%
+% \begin{macro}{\picture@tag@bbox@attribute}
+% Picture needs a similar command to calculate the bbox. But here we stay simple
+% and use simply the size of the picbox.
+%
+% \begin{macrocode}
+\newcommand\picture@tag@bbox@attribute
+ {
+ \bool_lazy_all:nT
+ {
+ {\tag_if_active_p:}
+ {!\l_@@_graphic_artifact_bool}
+ {\l_@@_graphic_BBox_bool}
+ }
+ {
+ \int_gincr:N\g_@@_graphic_int
+ \tl_set:Nx\l_@@_graphic_currentlabel_tl {_@@_graphic_\int_use:N \g_@@_graphic_int}
+ \@@_graphic_savepos:e { \l_@@_graphic_currentlabel_tl }
+ \tl_gset:Nx \g_@@_graphic_lx_tl
+ {
+ \dim_to_decimal_in_bp:n
+ { \@@_ref_value:enn {\l_@@_graphic_currentlabel_tl}{xpos}{0}sp }
+ }
+ \tl_gset:Nx \g_@@_graphic_ly_tl
+ {
+ \dim_to_decimal_in_bp:n
+ { \@@_ref_value:enn {\l_@@_graphic_currentlabel_tl}{ypos}{0}sp - \dp\@picbox }
+ }
+ \tl_gset:Nx \g_@@_graphic_ux_tl
+ {
+ \dim_to_decimal_in_bp:n
+ {
+ \g_@@_graphic_lx_tl bp + \wd\@picbox
+ }
+ }
+ \tl_gset:Nx \g_@@_graphic_uy_tl
+ {
+ \dim_to_decimal_in_bp:n
+ {
+ \g_@@_graphic_ly_tl bp + \ht\@picbox + \dp\@picbox
+ }
+ }
+ \@@_graphic_applycorr:NNNN
+ \g_@@_graphic_lx_tl
+ \g_@@_graphic_ly_tl
+ \g_@@_graphic_ux_tl
+ \g_@@_graphic_uy_tl
+ \bool_if:NT\l_@@_graphic_debug_bool
+ {
+ \@@_graphic_show_bbox:VVVVne
+ \g_@@_graphic_lx_tl
+ \g_@@_graphic_ly_tl
+ \g_@@_graphic_ux_tl
+ \g_@@_graphic_uy_tl
+ {red}
+ {\int_use:N\g_@@_graphic_int}
+ }
+ \@@_prop_gput:cnx
+ { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop }
+ { A }
+ {
+ <<
+ /O /Layout /BBox~
+ [
+ \g_@@_graphic_lx_tl\c_space_tl
+ \g_@@_graphic_ly_tl\c_space_tl
+ \g_@@_graphic_ux_tl\c_space_tl
+ \g_@@_graphic_uy_tl
+ ]
+ >>
+ }
+ }
+ }
+
+% \end{macrocode}
+% \end{macro}
+% We redefine \cs{picture} to accept an optional argument and
+% change the default alt text. We also ensure that we are in
+% hmode, so that stopping tagging doesn't confuse the paratags.
+% \begin{macrocode}
+\RenewDocumentCommand\picture{O{}m}
+ {
+ \leavevmode
+ \keys_set:nn{tag/picture}{#1} %
+ \tl_set:Nn\l_@@_graphic_alt_dflt_tl {picture~environment}
+ \pictur@#2
+ }
+% \end{macrocode}
+% inside the picture box we stop tagging.
+% \begin{macrocode}
+\def\@picture(#1,#2)(#3,#4){%
+ \@defaultunitsset\@picht{#2}\unitlength
+ \@defaultunitsset\@tempdimc{#1}\unitlength
+ \Gin@tag@struct@begin
+ \setbox\@picbox\hb@xt@\@tempdimc\bgroup
+ \tag_stop: %do not tag inside the picture box
+ \@defaultunitsset\@tempdimc{#3}\unitlength
+ \hskip -\@tempdimc
+ \@defaultunitsset\@tempdimc{#4}\unitlength
+ \lower\@tempdimc\hbox\bgroup
+ \ignorespaces}
+% \end{macrocode}
+%
+% \begin{macrocode}
+\def\endpicture{%
+ \egroup\hss\egroup
+ \ht\@picbox\@picht\dp\@picbox\z@
+ \picture@tag@bbox@attribute
+ \mbox{\box\@picbox}
+ \Gin@tag@struct@end}
+% \end{macrocode}
+%
+% \subsection{Debugging code}
+%
+% \begin{macro}{\@@_graphic_show_bbox:nnnnnn}
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_graphic_show_bbox:nnnnnn #1#2#3#4#5#6%#5 color, #6 graphic
+ {
+ \iow_log:n {tag/graphic~debug:~BBox~of~graphics~#6~is~#1~#2~#3~#4}
+ \hook_gput_code:nnn
+ {shipout/foreground}
+ {tag/graphic}
+ {
+ \int_compare:nNnT
+ {\g_shipout_readonly_int}
+ =
+ {\@@_ref_value:enn{_@@_graphic_#6}{abspage}{0}}
+ {
+ \put
+ (#1 bp,\dim_eval:n{-\paperheight + \dim_eval:n{#2 bp}})
+ {
+ \opacity_select:n{0.5}\color_select:n{#5}
+ \rule
+ {\dim_eval:n {#3 bp-\dim_eval:n{#1 bp}}}
+ {\dim_eval:n {#4 bp-\dim_eval:n{#2 bp}}}
+ }
+ }
+ }
+ }
+ \cs_generate_variant:Nn \@@_graphic_show_bbox:nnnnnn {VVVVne}
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+
+% \begin{macrocode}
+%<*latex-lab>
+\ProvidesFile{graphic-latex-lab-testphase.ltx}
+ [2023-04-07 v0.8 code related to the tagging included graphics]
+\RequirePackage{latex-lab-testphase-graphic}
+%</latex-lab>
+% \end{macrocode}
+% \end{implementation}