% \iffalse meta-comment
%
%% File: tagpdf-mc-generic.dtx
%
% Copyright (C) 2019-2024 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{}
\documentclass{l3doc}
\usepackage{array,booktabs,caption}
\hypersetup{pdfauthor=Ulrike Fischer,
pdftitle=tagpdf-mc module (tagpdf)}
\begin{document}
\DocInput{\jobname.dtx}
\end{document}
%
% \fi
% \title{^^A
% The \pkg{tagpdf-mc-generic} module\\ Code related to Marked Content (mc-chunks), generic mode ^^A
% \\ Part of the tagpdf package
% }
%
% \author{^^A
% Ulrike Fischer\thanks
% {^^A
% E-mail:
% \href{mailto:fischer@troubleshooting-tex.de}
% {fischer@troubleshooting-tex.de}^^A
% }^^A
% }
%
% \date{Version 0.99g, released 2024-10-27}
% \maketitle
% \begin{documentation}
% \end{documentation}
% \begin{implementation}
% \section{Marked content code -- generic mode}
% \begin{macrocode}
%<@@=tag>
%<*generic>
\ProvidesExplPackage {tagpdf-mc-code-generic} {2024-10-27} {0.99g}
{part of tagpdf - code related to marking chunks - generic mode}
%
%<*debug>
\ProvidesExplPackage {tagpdf-debug-generic} {2024-10-27} {0.99g}
{part of tagpdf - debugging code related to marking chunks - generic mode}
%
% \end{macrocode}
%
%\subsection{Variables}
%
% \begin{macrocode}
%<*generic>
% \end{macrocode}
%
% \begin{variable}{\l_@@_mc_ref_abspage_tl}
% We need a ref-label system to ensure that the MCID cnt
% restarts at 0 on a new page
% This will be used to store the tagabspage attribute retrieved from
% a label.
% \begin{macrocode}
\tl_new:N \l_@@_mc_ref_abspage_tl
% \end{macrocode}
% \end{variable}
% \begin{variable}{\l_@@_mc_tmpa_tl}
% temporary variable
% \begin{macrocode}
\tl_new:N \l_@@_mc_tmpa_tl
% \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: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 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_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}{\@@_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
%%
}
}
% \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:VV
% }
%
% 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}
\bool_if:NTF\g_@@_delayed_shipout_bool
{
\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:eV
{
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
}
}
}
% \end{macrocode}
% if the engine is too old, we have to revert to earlier method.
% \begin{macrocode}
{
\msg_new:nnn { tagpdf } { old-engine }
{
The~engine~or~the~PDF management~is~too~old~or\\
delayed~shipout~has~been~disabled.\\
Fast~numbering~of~MC-chunks~not~available.\\
More~compilations~will~be~needed~in~generic~mode.
}
\msg_warning:nn { tagpdf} { old-engine }
\@@_prop_new:N \g_@@_MCID_byabspage_prop
\int_new:N \g_@@_MCID_tmp_bypage_int
\cs_generate_variant:Nn \@@_mc_bdc:nn {ne}
% \end{macrocode}
% revert the attribute:
% \begin{macrocode}
\property_gset:nnnn {tagmcid } { now }
{0} { \int_use:N \g_@@_MCID_tmp_bypage_int }
\cs_new_protected:Npn \@@_mc_bdc_mcid:nn #1 #2
{
\int_gincr:N \c@g_@@_MCID_abs_int
\tl_set:Ne \l_@@_mc_ref_abspage_tl
{
\property_ref:enn %3 args
{
mcid-\int_use:N \c@g_@@_MCID_abs_int
}
{ tagabspage }
{-1}
}
\prop_get:NoNTF
\g_@@_MCID_byabspage_prop
{
\l_@@_mc_ref_abspage_tl
}
\l_@@_mc_tmpa_tl
{
%key already present, use value for MCID and add 1 for the next
\int_gset:Nn \g_@@_MCID_tmp_bypage_int { \l_@@_mc_tmpa_tl }
\@@_prop_gput:Nee
\g_@@_MCID_byabspage_prop
{ \l_@@_mc_ref_abspage_tl }
{ \int_eval:n {\l_@@_mc_tmpa_tl +1} }
}
{
%key not present, set MCID to 0 and insert 1
\int_gzero:N \g_@@_MCID_tmp_bypage_int
\@@_prop_gput:Nee
\g_@@_MCID_byabspage_prop
{ \l_@@_mc_ref_abspage_tl }
{1}
}
\@@_property_record:eV
{
mcid-\int_use:N \c@g_@@_MCID_abs_int
}
\c_@@_property_mc_clist
\@@_mc_bdc:ne
{#1}
{ /MCID~\int_eval:n { \g_@@_MCID_tmp_bypage_int }~ \exp_not:n { #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 {VV}
% \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:NV\@@_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 }
%
% \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}
%\cs_new_protected:Npn \tag_mc_begin:n #1 { \@@_whatsits: \int_gincr:N \c@g_@@_MCID_abs_int }
%\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
{
%
%<*debug>
\cs_set_protected:Npn \tag_mc_begin:n #1 %#1 keyval
{
\@@_check_if_active_mc:TF
{
\@@_debug_mc_begin_insert:n { #1 }
%
\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
\keys_set:nn { @@ / mc } {#1}
\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
\@@_mc_handle_mc_label:e \l_@@_mc_key_label_tl
}
\bool_if:NF \l_@@_mc_key_stash_bool
{
\exp_args:NV\@@_struct_get_parentrole:nNN
\g_@@_struct_stack_current_tl
\l_@@_get_parent_tmpa_tl
\l_@@_get_parent_tmpb_tl
\@@_check_parent_child:VVnnN
\l_@@_get_parent_tmpa_tl
\l_@@_get_parent_tmpb_tl
{MC}{}
\l_@@_parent_child_check_tl
\int_compare:nNnT {\l_@@_parent_child_check_tl}<{0}
{
\prop_get:cnN
{ g_@@_struct_ \g_@@_struct_stack_current_tl _prop}
{S}
\l_@@_tmpa_tl
\msg_warning:nneee
{ tag }
{role-parent-child}
{ \l_@@_get_parent_tmpa_tl/\l_@@_get_parent_tmpb_tl }
{ MC~(real content) }
{ not~allowed~
(struct~\g_@@_struct_stack_current_tl,~\l_@@_tmpa_tl)
}
}
\@@_mc_handle_stash:e { \int_use:N \c@g_@@_MCID_abs_int }
}
}
\group_end:
}
%<*debug>
{
\@@_debug_mc_begin_ignore:n { #1 }
}
%
}
%<*generic>
\cs_set_protected:Nn \tag_mc_end:
{
\@@_check_if_active_mc:T
{
%
%<*debug>
\cs_set_protected:Nn \tag_mc_end:
{
\@@_check_if_active_mc:TF
{
\@@_debug_mc_end_insert:
%
\@@_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:
}
%
}
%
% \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},
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}
}
%
% \end{macrocode}
% \end{macro}
% \end{implementation}
% \PrintIndex