% \iffalse meta-comment
%
%% File: tagpdf-mc-generic.dtx
%
% Copyright (C) 2019-2026 Ulrike Fischer
%
% 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 "tagpdf 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/tagpdf
%
% for those people who are interested.
%<*driver>
\DocumentMetadata{tagging=on}
\documentclass{l3in2edoc}
\usepackage{array,booktabs,caption}
\hypersetup{pdfauthor=Ulrike Fischer,
 pdftitle=tagpdf-mc module (tagpdf)}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
% \title{^^A
%   The \pkg{tagpdf-mc-generic} module\\ Code related to Marked Content (mc-chunks), generic mode  ^^A
% }
%
% \author{^^A
%  Ulrike Fischer\thanks
%    {^^A
%      E-mail:
%        \href{mailto:fischer@troubleshooting-tex.de}
%          {fischer@troubleshooting-tex.de}^^A
%    }^^A
% }
%
% \date{Version 1.0d, released 2026-07-25}
% \maketitle
% \begin{documentation}
% \end{documentation}
% \begin{implementation}
% \section{Marked content code -- generic mode}
%    \begin{macrocode}
%<@@=tag>
%<*generic>
\ProvidesExplPackage {tagpdf-mc-code-generic} {2026-07-25} {1.0d}
 {part of tagpdf - code related to marking chunks - generic mode}
%</generic>
%<*debug>
\ProvidesExplPackage {tagpdf-debug-generic} {2026-07-25} {1.0d}
 {part of tagpdf - debugging code related to marking chunks - generic mode}
%</debug>
%    \end{macrocode}
%
%\subsection{Variables}
%
%    \begin{macrocode}
%<*generic>
%    \end{macrocode}
%
% \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{macro}{\tag_mc_new_stream:n}
%    \begin{macrocode}
\cs_new_protected:Npn  \tag_mc_new_stream:n #1
  {
    \seq_new:c { g_@@_mc_multicol_#1_seq }
  }
%    \end{macrocode}
% \end{macro}
% \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:NNe
   \seq_set_from_clist:Nn \l_@@_mc_firstmarks_seq
     { \tex_firstmarks:D \g_@@_mc_marks }
   \exp_args:NNe
   \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:Nne \g_@@_struct_cont_mc_prop {#1}{ \l_@@_tmpa_tl \@@_struct_mcid_dict:n {#2}}
      }
      {
        \prop_gput:Nne \g_@@_struct_cont_mc_prop {#1}{ \@@_struct_mcid_dict:n {#2}}
      }
    \prop_gput:Nee \g_@@_mc_parenttree_prop
      {#2}
      {#3}
  }
\cs_generate_variant:Nn \@@_mc_store:nnn {eee}
%    \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_@@_mc_firstmarks_seq {,~}}
     \@@_check_typeout_v:n {=>~ bot~ \seq_use:Nn \l_@@_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:Ne \l_@@_tmpa_tl { \seq_item:cn { g_@@_mc_#1_marks_seq } {4} }
              \@@_mc_handle_artifact:N \l_@@_tmpa_tl
           }
           {
              \exp_args:Ne
              \@@_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:eee
                    {
                      \seq_item:cn { g_@@_mc_#1_marks_seq } {2}
                    }
                    { \int_eval:n{\c@g_@@_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 and 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_protected: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}{\tag_mc_add_missing_to_stream:Nn,\@@_add_missing_mcs_to_stream:Nn}
%    This is the main command to add mc to the stream. It is therefore
%    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
%    artificially 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:NNe
    \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_@@_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_@@_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:NNe
          \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
%%
  }
}
\cs_set_eq:NN \tag_mc_add_missing_to_stream:Nn \@@_add_missing_mcs_to_stream:Nn
%    \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}
  {
    \bool_if:NTF \g_@@_in_mc_bool
      { \prg_return_true:  }
      { \prg_return_false: }
  }

\prg_new_eq_conditional:NNn \tag_mc_if_in: \@@_mc_if_in: {p,T,F,TF}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{ \@@_mc_bmc:n,\@@_mc_emc:,\@@_mc_bdc:nn}
% These are the low-level commands. There are now equal to the
% pdfmanagement commands generic mode, but we use an indirection
% in case luamode need something else.
% change 04.08.2018: the commands do not check the validity of the arguments or try
% to escape them, this should be done before using them.
% change 2023-08-18: we are delaying the writing to the shipout.
%    \begin{macrocode}
% #1 tag, #2 properties
\cs_set_eq:NN \@@_mc_bmc:n  \pdf_bmc:n
\cs_set_eq:NN \@@_mc_emc:   \pdf_emc:
\cs_set_eq:NN \@@_mc_bdc:nn \pdf_bdc:nn
\cs_set_eq:NN \@@_mc_bdc_shipout:ee \pdf_bdc_shipout:ee
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}
%   {
%     \@@_mc_bdc_mcid:nn,\@@_mc_bdc_mcid:n,
%     \@@_mc_handle_mcid:nn,\@@_mc_handle_mcid:oo
%   }
%
% This create a BDC mark with an |/MCID| key. Most of the work here is to get
% the current number value for the MCID: they must be numbered by page
% starting with 0 and then successively.
% The first argument is the tag, e.g. |P| or |Span|, the second is used to pass
% more properties.
% Starting with texlive 2023 this is much simpler and faster as we can use
% delay the numbering to the shipout.
% We also define a wrapper around the low-level command as luamode will need
% something different.
%    \begin{macrocode}
\hook_gput_code:nnn {shipout/before}{tagpdf}{ \flag_clear:n { @@/mcid } }
\cs_set_protected:Npn \@@_mc_bdc_mcid:nn #1 #2
  {
    \int_gincr:N \c@g_@@_MCID_abs_int
    \@@_property_record:eo
     {
       mcid-\int_use:N \c@g_@@_MCID_abs_int
     }
     { \c_@@_property_mc_clist }
    \@@_mc_bdc_shipout:ee
      {#1}
      {
        /MCID~\flag_height:n { @@/mcid }
        \flag_raise:n { @@/mcid }~  #2
      }
  }
 \cs_new_protected:Npn \@@_mc_bdc_mcid:n #1
  {
    \@@_mc_bdc_mcid:nn {#1} {}
  }

\cs_new_protected:Npn \@@_mc_handle_mcid:nn #1 #2 %#1 tag, #2 properties
  {
    \@@_mc_bdc_mcid:nn {#1} {#2}
  }

\cs_generate_variant:Nn \@@_mc_handle_mcid:nn {oo}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\@@_mc_handle_stash:n,\@@_mc_handle_stash:e}
% This is the handler which puts a mc into the
% the current structure. The argument is the number of the mc.
% Beside storing the mc into the structure, it also has to record the
% structure for the parent tree.
% The name is a bit confusing, it does \emph{not} handle mc with the stash key
% \ldots.
% TODO: why does luamode use it for begin + use, but generic mode only for begin?
%    \begin{macrocode}
\cs_new_protected:Npn \@@_mc_handle_stash:n #1 %1 mcidnum
  {
    \@@_check_mc_used:n {#1}
    \@@_struct_kid_mc_gput_right:nn
      { \g_@@_struct_stack_current_tl }
      {#1}
   \prop_gput:Nee \g_@@_mc_parenttree_prop
     {#1}
     { \g_@@_struct_stack_current_tl }
  }
\cs_generate_variant:Nn \@@_mc_handle_stash:n { e }
%    \end{macrocode}
% \end{macro}

% \begin{macro}
%  {
%    \@@_mc_bmc_artifact:,
%    \@@_mc_bmc_artifact:n,
%    \@@_mc_handle_artifact:N
%   }
% Two commands to create artifacts, one without type, and one with.
% We define also a wrapper handler as luamode will need a different definition.
% TODO: perhaps later: more properties for artifacts
%    \begin{macrocode}
\cs_new_protected:Npn  \@@_mc_bmc_artifact:
  {
    \@@_mc_bmc:n {Artifact}
  }
\cs_new_protected:Npn \@@_mc_bmc_artifact:n #1
  {
    \@@_mc_bdc:nn {Artifact}{/Type/#1}
  }
\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:No\@@_mc_bmc_artifact:n {#1} }
  }
%    \end{macrocode}
% \end{macro}

% \begin{macro}{ \@@_get_data_mc_tag: }
% This allows to retrieve the active mc-tag.
% It is use by the get command.
%    \begin{macrocode}
\cs_new:Nn \@@_get_data_mc_tag: { \g_@@_mc_key_tag_tl }
%</generic>
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\tag_mc_begin:n,\tag_mc_end:}
% These are the core public commands to open and close an mc.
% They don't need to be in the same group or grouping level,
% but the code expect that they are issued linearly. The tag and
% the state is passed to the end command through a global var and
% a global boolean.
%    \begin{macrocode}
%<base>\cs_new_protected:Npn \tag_mc_begin:n #1 { \@@_whatsits: \int_gincr:N \c@g_@@_MCID_abs_int }
%<base>\cs_new_protected:Nn \tag_mc_end:{ \@@_whatsits: }
%<*generic|debug>
%<*generic>
\cs_set_protected:Npn \tag_mc_begin:n #1 %#1 keyval
  {
    \@@_check_if_active_mc:T
      {
%</generic>
%<*debug>
\cs_set_protected:Npn \tag_mc_begin:n #1 %#1 keyval
  {
    \@@_check_if_active_mc:TF
      {
        \@@_debug_mc_begin_insert:n { #1 }
%</debug>
        \group_begin: %hm
        \@@_check_mc_if_nested:
        \bool_gset_true:N \g_@@_in_mc_bool
%    \end{macrocode}
% set default MC tags to structure:
%    \begin{macrocode}
        \tl_set_eq:NN \l_@@_mc_key_tag_tl \g_@@_struct_tag_tl
        \tl_gset_eq:NN\g_@@_mc_key_tag_tl \g_@@_struct_tag_tl
        \tl_if_empty:NTF\l_@@_mc_lang_tl
          {
            \keys_set:nn { @@ / mc }{ #1 }
          }
          {
            \keys_set:nn { @@ / mc }{ lang=\l_@@_mc_lang_tl, #1 }
          }
        \bool_if:NTF \l_@@_mc_artifact_bool
          { %handle artifact
            \@@_mc_handle_artifact:N \l_@@_mc_artifact_type_tl
            \exp_args:No
            \@@_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:oo
              { \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
              {
                \@@_mc_handle_mc_label:e { \l_@@_mc_key_label_tl }
              }
%    \end{macrocode}
% check if the MC can be used here. This is guarded by the stash boolean.
%    \begin{macrocode}
            \bool_if:NF \l_@@_mc_key_stash_bool
              {
                \socket_use:nn{tag/check/parent-child}
                  {
                   \@@_mc_check_parent_child:o
                    { \g_@@_struct_stack_current_tl }
                  }
                \@@_mc_handle_stash:e { \int_use:N \c@g_@@_MCID_abs_int }

              }
          }
        \group_end:
      }
%<*debug>
      {
        \@@_debug_mc_begin_ignore:n { #1 }
      }
%</debug>
  }
%<*generic>
\cs_set_protected:Nn \tag_mc_end:
  {
    \@@_check_if_active_mc:T
      {
%</generic>
%<*debug>
\cs_set_protected:Nn \tag_mc_end:
  {
    \@@_check_if_active_mc:TF
      {
        \@@_debug_mc_end_insert:
%</debug>
        \@@_check_mc_if_open:
        \bool_gset_false:N \g_@@_in_mc_bool
        \tl_gset:Nn  \g_@@_mc_key_tag_tl { }
        \@@_mc_emc:
        \@@_mc_end_marks:
      }
%<*debug>
      {
        \@@_debug_mc_end_ignore:
      }
%</debug>
  }
%</generic|debug>
%    \end{macrocode}
% \end{macro}

%
% \subsection{Keys}
% Definitions are different in luamode.
% |tag| and |raw| are expanded as |\lua_now:e| in lua does it too and
% we assume that their values are safe.
% \begin{macro}
%  {
%   tag (mc-key),
%   raw (mc-key),
%   alt (mc-key),
%   actualtext (mc-key),
%   label (mc-key),
%   artifact (mc-key)
%  }
%    \begin{macrocode}
%<*generic>
\keys_define:nn { @@ / mc }
  {
    tag .code:n = % the name (H,P,Span) etc
      {
        \tl_set:Ne   \l_@@_mc_key_tag_tl { #1 }
        \tl_gset:Ne  \g_@@_mc_key_tag_tl { #1 }
      },
    raw  .code:n =
      {
        \tl_put_right:Ne \l_@@_mc_key_properties_tl { #1 }
      },
    alt .code:n      = % Alt property
      {
        \str_set_convert:Noon
          \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 .meta:n = {alt=#1},
%    \end{macrocode}
% lang is not according to the spec, but it works in acrobat \ldots.
% We assume that this are simple strings that do not need escaping.
%    \begin{macrocode}
    lang .code:n      = % Lang property
      {
        \tl_put_right:Ne \l_@@_mc_key_properties_tl { /Lang~(#1) }
      },
    actualtext .code:n      = % ActualText property
      {
        \tl_if_empty:oF{#1}
         {
           \str_set_convert:Noon
             \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>~ }
         }
      },
    label .tl_set:N        = \l_@@_mc_key_label_tl,
    artifact .code:n       =
      {
        \exp_args:Nne
          \keys_set:nn
            { @@ / mc }
            { __artifact-bool, __artifact-type=#1 }
      },
    artifact .default:n    = {notype}
  }
%</generic>
%    \end{macrocode}
% \end{macro}
% \end{implementation}
% \PrintIndex
