From 66f6e60b6dc60cc08482030b92df223b6a51b15f Mon Sep 17 00:00:00 2001
From: Norbert Preining <norbert@preining.info>
Date: Sun, 29 Aug 2021 03:02:07 +0000
Subject: CTAN sync 202108290302

---
 macros/latex/contrib/tagpdf/tagpdf-mc-generic.dtx | 437 ++++++++++++++++++++--
 1 file changed, 412 insertions(+), 25 deletions(-)

(limited to 'macros/latex/contrib/tagpdf/tagpdf-mc-generic.dtx')

diff --git a/macros/latex/contrib/tagpdf/tagpdf-mc-generic.dtx b/macros/latex/contrib/tagpdf/tagpdf-mc-generic.dtx
index 6c617e00ff..903b38d5a3 100644
--- a/macros/latex/contrib/tagpdf/tagpdf-mc-generic.dtx
+++ b/macros/latex/contrib/tagpdf/tagpdf-mc-generic.dtx
@@ -50,14 +50,16 @@
 %    }^^A
 % }
 %
-% \date{Version 0.91, released 2021-07-03}
+% \date{Version 0.92, released 2021-08-27}
 % \maketitle
+% \begin{documentation}
+% \end{documentation}
 % \begin{implementation}
 % \section{Marked content code -- generic mode}
 %    \begin{macrocode}
 %<@@=tag>
 %<*generic>
-\ProvidesExplPackage {tagpdf-mc-code-generic} {2021-07-03} {0.91}
+\ProvidesExplPackage {tagpdf-mc-code-generic} {2021-08-27} {0.92}
  {part of tagpdf - code related to marking chunks - generic mode}
 %</generic>
 %    \end{macrocode}
@@ -91,11 +93,411 @@
 %    \end{macrocode}
 % \end{variable}
 
+% \begin{variable}
+%   {
+%     \g_@@_mc_marks,
+%   }
+% a marks register to keep track of the mc's at page breaks and a sequence
+% to keep track of the data for the continuation
+% extra-tmb. We probably will need to track mc-marks
+% in more than one stream, so the seq contains the name of the stream.
+%
+%    \begin{macrocode}
+\newmarks  \g_@@_mc_marks
+%    \end{macrocode}
+% \end{variable}
+%
+%  \begin{variable}
+%   {
+%    \g_@@_mc_main_marks_seq,
+%    \g_@@_mc_footnote_marks_seq,
+%    \g_@@_mc_multicol_marks_seq
+%   }
+%    Each stream has an associated global seq variable holding the
+%    bottom marks from the/a previous chunk in the stream.
+%    We provide three by default: main, footnote and multicol.
+%    TODO: perhaps an interface for more streams will be needed.
+%    \begin{macrocode}
+\seq_new:N \g_@@_mc_main_marks_seq
+\seq_new:N \g_@@_mc_footnote_marks_seq
+\seq_new:N \g_@@_mc_multicol_marks_seq
+%    \end{macrocode}
+%  \end{variable}
+
+% \begin{variable}{\l_@@_mc_firstmarks_seq,\l_@@_mc_botmarks_seq}
+% The marks content contains a number of data which we will have to access and
+% compare, so we will store it locally in two sequences.
+% topmarks is unusable in LaTeX so we ignore it.
+%
+%    \begin{macrocode}
+\seq_new:N  \l_@@_mc_firstmarks_seq
+\seq_new:N  \l_@@_mc_botmarks_seq
+%    \end{macrocode}
+% \end{variable}
+%
 % \subsection{Functions}
+%
+% \begin{macro}{\@@_mc_begin_marks:nn,\@@_mc_artifact_begin_marks:n,\@@_mc_end_marks:}
+% Generic mode need to set marks for the page break and split stream handling.
+% We always set two marks to be able to detect the case when no mark is on a
+% page/galley. MC-begin commands will set (b,-,data) and (b,+,data),
+% MC-end commands will set (e,-,data) and (e,+,data).
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_mc_begin_marks:nn #1 #2 %#1 tag, #2 label
+  {
+    \tex_marks:D \g_@@_mc_marks
+      {
+        b-, %first of begin pair
+        \int_use:N\c@g_@@_MCID_abs_int, %mc-num
+        \g_@@_struct_stack_current_tl,  %structure num
+        #1, %tag
+        \bool_if:NT \l_@@_mc_key_stash_bool{stash}, % stash info
+        #2, %label
+      }
+    \tex_marks:D \g_@@_mc_marks
+      {
+        b+, % second of begin pair
+        \int_use:N\c@g_@@_MCID_abs_int, %mc-num
+        \g_@@_struct_stack_current_tl,  %structure num
+        #1, %tag
+        \bool_if:NT \l_@@_mc_key_stash_bool{stash}, % stash info
+        #2, %label
+      }
+  }
+\cs_generate_variant:Nn \@@_mc_begin_marks:nn {oo}
+\cs_new_protected:Npn \@@_mc_artifact_begin_marks:n #1 %#1 type
+  {
+    \tex_marks:D \g_@@_mc_marks
+      {
+        b-, %first of begin pair
+        \int_use:N\c@g_@@_MCID_abs_int, %mc-num
+        -1, %structure num
+        #1 %type
+      }
+    \tex_marks:D \g_@@_mc_marks
+      {
+        b+, %first of begin pair
+        \int_use:N\c@g_@@_MCID_abs_int, %mc-num
+        -1,  %structure num
+        #1  %Type
+      }
+  }
+
+\cs_new_protected:Npn \@@_mc_end_marks:
+  {
+    \tex_marks:D \g_@@_mc_marks
+      {
+        e-, %first of end pair
+        \int_use:N\c@g_@@_MCID_abs_int, %mc-num
+        \g_@@_struct_stack_current_tl,  %structure num
+      }
+    \tex_marks:D \g_@@_mc_marks
+      {
+        e+, %second of end pair
+        \int_use:N\c@g_@@_MCID_abs_int, %mc-num
+        \g_@@_struct_stack_current_tl,  %structure num
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\@@_mc_disable_marks:}
+% This disables the marks. They can't be reenabled, so it should only
+% be used in groups.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_mc_disable_marks:
+ {
+   \cs_set_eq:NN \@@_mc_begin_marks:nn \use_none:nn
+   \cs_set_eq:NN \@@_mc_artifact_begin_marks:n \use_none:n
+   \cs_set_eq:NN \@@_mc_end_marks: \prg_do_nothing:
+ }
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\@@_mc_get_marks:}
+% This stores the current content of the marks in the sequences. It naturally
+% should only be used in places where it makes sense.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_mc_get_marks:
+ {
+   \exp_args:NNx
+   \seq_set_from_clist:Nn \l_@@_mc_firstmarks_seq
+     { \tex_firstmarks:D \g_@@_mc_marks }
+   \exp_args:NNx
+   \seq_set_from_clist:Nn \l_@@_mc_botmarks_seq
+     { \tex_botmarks:D   \g_@@_mc_marks }
+ }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_mc_store:nnn}
+%  This inserts the mc-chunk \meta{mc-num} into the structure {struct-num}
+%  after the \meta{mc-prev}.
+%  The structure must already exist.
+%  The additional mcid dictionary is stored in a property. The item is retrieved
+%  when the kid entry is built. We test if there is already
+%  an addition and append if needed.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_mc_store:nnn #1 #2 #3 %#1 mc-prev, #2 mc-num #3 structure-num
+  {
+    %\prop_show:N \g_@@_struct_cont_mc_prop
+    \prop_get:NnNTF \g_@@_struct_cont_mc_prop {#1} \l_@@_tmpa_tl
+      {
+        \prop_gput:Nnx \g_@@_struct_cont_mc_prop {#1}{ \l_@@_tmpa_tl \@@_struct_mcid_dict:n {#2}}
+      }
+      {
+        \prop_gput:Nnx \g_@@_struct_cont_mc_prop {#1}{ \@@_struct_mcid_dict:n {#2}}
+      }
+    \prop_gput:Nxx \g_@@_mc_parenttree_prop
+      {#2}
+      {#3}
+  }
+\cs_generate_variant:Nn \@@_mc_store:nnn {xxx}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_mc_insert_extra_tmb:n,\@@_mc_insert_extra_tme:n}
+% These two functions should be used in the output routine at the place
+% where a mc-literal could be missing due to a page break or some other split.
+% They check (with the help of the marks) if a extra-tmb or extra-tme is needed.
+% The tmb command stores also the mc into the structure, the tme has to store
+% the data for a following extra-tmb.
+% The argument takes a stream name like main or footnote to allow different handling
+% there.
+% The content of the marks must be stored before (with |\@@_mc_get_marks:|
+% or manually)
+% into |\l_@@_mc_firstmarks_seq| and |\l_@@_mc_botmarks_seq| so that the tests
+% can use them.
+%
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_mc_insert_extra_tmb:n #1 % #1 stream: e.g. main or footnote
+  {
+     \@@_check_typeout_v:n {=>~ first~ \seq_use:Nn \l__tag_mc_firstmarks_seq {,~}}
+     \@@_check_typeout_v:n {=>~ bot~ \seq_use:Nn \l__tag_mc_botmarks_seq {,~}}
+     \@@_check_if_mc_tmb_missing:TF
+       {
+         \@@_check_typeout_v:n {=>~ TMB~ ~ missing~ --~ inserted}
+         %test if artifact
+         \int_compare:nNnTF { \seq_item:cn { g_@@_mc_#1_marks_seq } {3} } = {-1}
+           {
+              \tl_set:Nx \l_@@_tmpa_tl { \seq_item:cn { g_@@_mc_#1_marks_seq } {4} }
+              \@@_mc_handle_artifact:N \l_@@_tmpa_tl
+           }
+           {
+              \exp_args:Nx
+              \@@_mc_bdc_mcid:n
+                {
+                  \seq_item:cn { g_@@_mc_#1_marks_seq } {4}
+                }
+              \str_if_eq:eeTF
+                {
+                  \seq_item:cn { g_@@_mc_#1_marks_seq } {5}
+                }
+                {}
+                {
+                  %store
+                  \@@_mc_store:xxx
+                    {
+                      \seq_item:cn { g_@@_mc_#1_marks_seq } {2}
+                    }
+                    { \int_eval:n{\c@g__tag_MCID_abs_int} }
+                    {
+                      \seq_item:cn { g_@@_mc_#1_marks_seq } {3}
+                    }
+                }
+                {
+                   %stashed -> warning!!
+                }
+           }
+       }
+       {
+         \@@_check_typeout_v:n {=>~ TMB~ not~ missing}
+       }
+  }
 
+\cs_new_protected:Npn \@@_mc_insert_extra_tme:n #1 % #1 stream, eg. main or footnote
+ {
+   \@@_check_if_mc_tme_missing:TF
+     {
+       \@@_check_typeout_v:n {=>~ TME~ ~ missing~ --~ inserted}
+       \@@_mc_emc:
+       \seq_gset_eq:cN
+         {  g_@@_mc_#1_marks_seq }
+         \l_@@_mc_botmarks_seq
+     }
+     {
+       \@@_check_typeout_v:n {=>~ TME~ not~ missing}
+     }
+ }
+%    \end{macrocode}
+% \end{macro}
+%
+%  \subsection{Looking at MC marks in boxes}
+%  \begin{macro}{\@@_add_missing_mcs:Nn}
+%    Assumptions:
+%    \begin{itemize}
+%    \item
+%       test for tagging active outside;
+%    \item
+%       mark retrieval also outside.
+%    \end{itemize}
+%
+%    This takes a box register as its first argument (or the register
+%    number in a count register, as used by \pkg{multicol}). It adds
+%    an extra tmb at the top of the box if necessary and similarly
+%    an extra tme at the end. This is done by adding hboxes in a way
+%    that the positioning and the baseline of the given box is not
+%    altered.  The result is written back to the box.
+%
+%    The second argument is the stream this box belongs to und is
+%    currently either \texttt{main} for the main galley,
+%    \texttt{footnote} for footnote note text, or \texttt{multicol}
+%    for boxes produced for columns in that environment. Other streams
+%    may follow over time.
+%    \begin{macrocode}
+\cs_new:Npn\@@_add_missing_mcs:Nn #1 #2 {
+  \vbadness \@M
+  \vfuzz    \c_max_dim
+  \vbox_set_to_ht:Nnn #1 { \box_ht:N #1 } {
+    \hbox_set:Nn \l_@@_tmpa_box { \@@_mc_insert_extra_tmb:n {#2} }
+    \hbox_set:Nn \l_@@_tmpb_box { \@@_mc_insert_extra_tme:n {#2} }
+    \int_compare:nNnT {\l_@@_loglevel_int} > { 0 }
+         {
+           \seq_log:c { g_@@_mc_#2_marks_seq}
+         }
+%    \end{macrocode}
+%    The box placed on the top gets zero size and thus will not affect
+%    the box dimensions of the box we are modifying.
+%    \begin{macrocode}
+    \box_set_ht:Nn \l_@@_tmpa_box \c_zero_dim
+    \box_set_dp:Nn \l_@@_tmpa_box \c_zero_dim
+%    \end{macrocode}
+%    The box added at the bottom will get the depth of the original
+%    box. This way we can arrange that from the outside everything
+%    looks as before.
+%    \begin{macrocode}
+    \box_set_ht:Nn \l_@@_tmpb_box \c_zero_dim
+    \box_set_dp:Nn \l_@@_tmpb_box { \box_dp:N #1 }
+%    \end{macrocode}
+%    We need to set \cs{boxmaxdepth} in case the original box has an
+%    unusually large depth, otherwise that depth is not preserved when
+%    we string things together.
+%    \begin{macrocode}
+    \boxmaxdepth \@maxdepth
+    \box_use_drop:N        \l_@@_tmpa_box
+    \vbox_unpack_drop:N     #1
+%    \end{macrocode}
+%    Back up by the depth of the box as we add that later again.
+%    \begin{macrocode}
+    \tex_kern:D -\box_dp:N \l_@@_tmpb_box
+%    \end{macrocode}
+%    And we don't want any glue added when we add the box.
+%    \begin{macrocode}
+    \nointerlineskip
+    \box_use_drop:N \l_@@_tmpb_box
+  }
+}
+%    \end{macrocode}
+%  \end{macro}
+
+%  \begin{macro}{\@@_add_missing_mcs_to_stream:Nn}
+%    This is the main command to add mc to the stream. It is therefor
+%    guarded by the mc-boolean.
+%
+%    If we aren't in the main stream then processing is a bit more
+%    complicated because to get at the marks in the box we need to
+%    artifically split it and then look at the split marks.
+%
+%    First argument is the box to update and the second is the \enquote{stream}.
+%    In lua mode the command is a no-op.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_add_missing_mcs_to_stream:Nn #1#2
+  {
+    \@@_check_if_active_mc:T {
+%    \end{macrocode}
+%    First set up a temp box for trial splitting.
+%    \begin{macrocode}
+    \vbadness\maxdimen
+    \box_set_eq:NN \l_@@_tmpa_box #1
+%    \end{macrocode}
+%    Split the box to the largest size available. This should give us
+%    all content (but to be sure that there is no issue we could test
+%    out test box is empty now (not done).
+%    \begin{macrocode}
+    \vbox_set_split_to_ht:NNn \l_@@_tmpa_box \l_@@_tmpa_box \c_max_dim
+%    \end{macrocode}
+%    As a side effect of this split we should now have the first and
+%    bottom split marks set up. We use this to set up
+%    \cs{l_@@_mc_firstmarks_seq}
+%    \begin{macrocode}
+    \exp_args:NNx
+    \seq_set_from_clist:Nn \l_@@_mc_firstmarks_seq
+        { \tex_splitfirstmarks:D \g_@@_mc_marks }
+%    \end{macrocode}
+%    Some debugging info:
+%    \begin{macrocode}
+%    \iow_term:n { First~ mark~ from~ this~ box: }
+%    \seq_log:N \l__tag_mc_firstmarks_seq
+%    \end{macrocode}
+%    If this mark was empty then clearly the bottom mark will too be
+%    empty. Thus in this case we make use of the saved bot mark from
+%    the previous chunk. Note that if this is the first chunk in the
+%    stream the global seq would contain a random value, but then we
+%    can't end in this branch because the basis assumption is that
+%    streams are properly marked up so the first chunk would always
+%    have a mark at the beginning!
+%    \begin{macrocode}
+    \seq_if_empty:NTF \l_@@_mc_firstmarks_seq
+        {
+          \@@_check_typeout_v:n
+            {
+              No~ marks~ so~ use~ saved~ bot~ mark:~
+              \seq_use:cn {g__tag_mc_#2_marks_seq} {,~} \iow_newline:
+            }
+           \seq_set_eq:Nc \l_@@_mc_firstmarks_seq {g_@@_mc_#2_marks_seq}
+%    \end{macrocode}
+%    We also update the bot mark to the same value so that we can
+%    later apply \cs{@@_add_missing_mcs:Nn} with the data
+%    structures in place (see assumptions made there).
+%    \begin{macrocode}
+          \seq_set_eq:NN \l_@@_mc_botmarks_seq \l_@@_mc_firstmarks_seq
+        }
+%    \end{macrocode}
+%    If there was a first mark then there is also a bot mark (and it
+%    can't be the same as our marks always come in pairs).
+%    So if that branch is chosen we update
+%    \cs{l_@@_mc_botmarks_seq} from the bot mark.
+%    \begin{macrocode}
+        {
+          \@@_check_typeout_v:n
+            {
+              Pick~ up~ new~ bot~ mark!
+            }
+          \exp_args:NNx
+          \seq_set_from_clist:Nn \l_@@_mc_botmarks_seq
+              { \tex_splitbotmarks:D   \g_@@_mc_marks }
+        }
+%    \end{macrocode}
+%    Finally we call \cs{@@_add_missing_mcs:Nn} to add any missing
+%    tmb/tme as needed,
+%    \begin{macrocode}
+    \@@_add_missing_mcs:Nn #1 {#2}
+%%
+    \seq_gset_eq:cN  {g_@@_mc_#2_marks_seq} \l_@@_mc_botmarks_seq
+%%
+  }
+}
+%    \end{macrocode}
+%  \end{macro}
+%
 % \begin{macro}[pTF]{\@@_mc_if_in:,\tag_mc_if_in:}
 % This is a test if a mc is open or not. It depends simply on a global boolean:
 % mc-chunks are added linearly so nesting should not be relevant.
+%
+% One exception are header and footer (perhaps they are more, but for now
+% it doesn't seem so, so there are no dedicated code to handle this situation):
+% When they are built and added to the page we could be both inside or outside a mc-chunk.
+% But header and footer should ignore this and not push/pop or warn about nested mc.
+% It is therefore important there to set and reset the boolean manually.
+% See the tagpddocu-patches.sty for an example.
 %    \begin{macrocode}
 \prg_new_conditional:Nnn \@@_mc_if_in: {p,T,F,TF}
   {
@@ -238,6 +640,7 @@
 \cs_new_protected:Npn \@@_mc_handle_artifact:N #1
    % #1 is a var containing the artifact type
   {
+    \int_gincr:N \c@g_@@_MCID_abs_int
     \tl_if_empty:NTF #1
       { \@@_mc_bmc_artifact: }
       { \exp_args:NV\@@_mc_bmc_artifact:n #1 }
@@ -271,12 +674,15 @@
         \bool_if:NTF \l_@@_mc_artifact_bool
           { %handle artifact
             \@@_mc_handle_artifact:N \l_@@_mc_artifact_type_tl
+            \exp_args:NV
+            \@@_mc_artifact_begin_marks:n \l_@@_mc_artifact_type_tl
           }
           { %handle mcid type
             \@@_check_mc_tag:N  \l_@@_mc_key_tag_tl
             \@@_mc_handle_mcid:VV
                \l_@@_mc_key_tag_tl
                \l_@@_mc_key_properties_tl
+            \@@_mc_begin_marks:oo{\l_@@_mc_key_tag_tl}{\l_@@_mc_key_label_tl}
             \tl_if_empty:NF {\l_@@_mc_key_label_tl}
               {
                 \exp_args:NV
@@ -298,6 +704,7 @@
         \bool_gset_false:N \g_@@_in_mc_bool
         \tl_gset:Nn  \g_@@_mc_key_tag_tl { }
         \@@_mc_emc:
+        \@@_mc_end_marks:
       }
   }
 %    \end{macrocode}
@@ -311,8 +718,8 @@
 % \begin{macro}
 %  {
 %   tag,raw,
-%   alttext,alttext-o,
-%   actualtext,actualtext-o,
+%   alttext,
+%   actualtext,
 %   label,artifact
 %  }
 %    \begin{macrocode}
@@ -327,17 +734,7 @@
       {
         \tl_put_right:Nx \l_@@_mc_key_properties_tl { #1 }
       },
-    alttext .code:n = % Alt property
-      {
-        \str_set_convert:Nnon
-          \l_@@_tmpa_str
-          { #1 }
-          { default }
-          { utf16/hex }
-        \tl_put_right:Nn \l_@@_mc_key_properties_tl { /Alt~< }
-        \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ }
-      },
-    alttext-o .code:n      = % Alt property
+    alttext .code:n      = % Alt property
       {
         \str_set_convert:Noon
           \l_@@_tmpa_str
@@ -348,16 +745,6 @@
         \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ }
       },
     actualtext .code:n      = % ActualText property
-      {
-        \str_set_convert:Nnon
-          \l_@@_tmpa_str
-          { #1 }
-          { default }
-          { utf16/hex }
-        \tl_put_right:Nn \l_@@_mc_key_properties_tl { /ActualText~< }
-        \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ }
-      },
-    actualtext-o .code:n      = % ActualText property
       {
         \str_set_convert:Noon
           \l_@@_tmpa_str
-- 
cgit v1.2.3

