% \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