From 66f6e60b6dc60cc08482030b92df223b6a51b15f Mon Sep 17 00:00:00 2001 From: Norbert Preining 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} % % \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