% \iffalse meta-comment
%
%% File: latex-lab-graphic.dtx (C) Copyright 2022-2024 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.
\def\ltlabgraphicdate{2024-09-18}
\def\ltlabgraphicversion{0.80e}
%
%<*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{v\ltlabgraphicversion\ \ltlabgraphicdate}
%
% \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 necessarily 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 completely. It is then the 
%   responsibility 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} {\ltlabgraphicdate} {\ltlabgraphicversion}
  {Code related to the tagging of graphics}
%    \end{macrocode}
% We load l3opacity for the debug code
%    \begin{macrocode}
\RequirePackage{l3opacity}
%    \end{macrocode}
%
% Needed during switch to e-type:
%    \begin{macrocode}
\cs_generate_variant:Nn \@@_prop_gput:Nnn {cne}
%    \end{macrocode}
% \begin{macro}{\@@_graphic_savepos:n}
% this is the command which stores the position. Similar to 
% zref-savepos it uses two savepos commands 
% for the case that bidi changes the processing order.
%    \begin{macrocode}
\cs_new_protected:Npn\@@_graphic_savepos:n #1
 {
  \tex_savepos:D 
  \property_record: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 everywhere) 
%    \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. Either \cs{Gin@scalex} or (if that is !) \cs{Gin@scaley}
%    \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~the~file~name~'#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:nne{tag}{alt-text-missing}{\l_@@_graphic_alt_dflt_tl}
                 \tl_set:Ne\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}
  \fi
  \Gin@tag@struct@end %new
  }
 }  
%    \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:Ne\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:Ne\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_suspend:n{Gin}}
          }
          {\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:Ne\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:Ne\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_suspend:n{picture}}
          }
          {\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
     {
       \str_if_eq:eeTF {\Gin@scalex} { ! }
        { \Gin@scaley }
        { \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:Ne\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:Ne\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:Ne\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:Ne\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:Ne #1
       { 
         \fp_eval:n 
          {
           #1
           + 
           \dim_to_decimal_in_bp:n {\seq_item:Nn \l_@@_graphic_bboxcorr_seq {1} } 
          }
       }
     \tl_set:Ne #2
       { 
         \fp_eval:n 
          {
           #2
           + 
           \dim_to_decimal_in_bp:n {\seq_item:Nn \l_@@_graphic_bboxcorr_seq {2} } 
          }
       }
     \tl_set:Ne #3
       { 
         \fp_eval:n 
          {
           #3
           + 
           \dim_to_decimal_in_bp:n {\seq_item:Nn \l_@@_graphic_bboxcorr_seq {3} } 
          }
       }
     \tl_set:Ne #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}
    }
    {
      \__tag_graphic_get_scale:
      \@@_graphic_get_trim:
      \int_gincr:N\g_@@_graphic_int
      \tl_set:Ne\l_@@_graphic_currentlabel_tl {_@@_graphic_\int_use:N \g_@@_graphic_int}
      \@@_graphic_savepos:e { \l_@@_graphic_currentlabel_tl }
      \tl_gset:Ne\g_@@_graphic_lx_tl
        {
          \dim_to_decimal_in_bp:n 
            { \property_ref:een {\l_@@_graphic_currentlabel_tl}{xpos}{0}sp }
        }  
      \tl_gset:Ne\g_@@_graphic_ly_tl
        {
          \dim_to_decimal_in_bp:n 
            { \property_ref:een {\l_@@_graphic_currentlabel_tl}{ypos}{0}sp }
        } 
      \tl_gset:Ne\g_@@_graphic_ux_tl
          {
            \fp_eval:n 
             { 
               \g_@@_graphic_lx_tl
               + 
               \dim_to_decimal_in_bp:n { \Gin@req@width }
             }  
          }         
       \tl_gset:Ne\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:Ne\g_@@_graphic_ux_tl
            {
              \fp_eval:n 
                {
                  \g_@@_graphic_ux_tl                       
                  + 
                  \l_@@_graphic_trim_ux_fp
                }  
            }
          \tl_gset:Ne\g_@@_graphic_lx_tl
            {
              \fp_eval:n 
                {
                  \g_@@_graphic_lx_tl              
                   - 
                  \l_@@_graphic_trim_lx_fp
                }  
            }  
          \tl_gset:Ne\g_@@_graphic_uy_tl
            {
              \fp_eval:n 
                {
                  \g_@@_graphic_uy_tl           
                  + 
                  \l_@@_graphic_trim_uy_fp
                }  
            }                                   
          \tl_gset:Ne\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:cne
         { 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:Ne\l_@@_graphic_currentlabel_tl {_@@_graphic_\int_use:N \g_@@_graphic_int}
      \@@_graphic_savepos:e { \l_@@_graphic_currentlabel_tl }
      \tl_gset:Ne \g_@@_graphic_lx_tl 
        { 
          \dim_to_decimal_in_bp:n 
          { \property_ref:een {\l_@@_graphic_currentlabel_tl}{xpos}{0}sp  } 
        }
      \tl_gset:Ne \g_@@_graphic_ly_tl 
        { 
          \dim_to_decimal_in_bp:n 
          { \property_ref:een {\l_@@_graphic_currentlabel_tl}{ypos}{0}sp - \dp\@picbox } 
        }  
      \tl_gset:Ne \g_@@_graphic_ux_tl 
        { 
          \dim_to_decimal_in_bp:n 
           {
             \g_@@_graphic_lx_tl bp + \wd\@picbox        
           }  
        }     
      \tl_gset:Ne \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:cne
            { 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_suspend:n{\@picture} %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}
       =
       {\property_ref:een{_@@_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}
        [\ltlabgraphicdate\space v\ltlabgraphicversion\space latex-lab wrapper graphic]
\RequirePackage{latex-lab-testphase-graphic}
%</latex-lab>
%    \end{macrocode}
% \end{implementation}
