summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/tagpdf/tagpdf-mc-generic.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/tagpdf/tagpdf-mc-generic.dtx')
-rw-r--r--macros/latex/contrib/tagpdf/tagpdf-mc-generic.dtx437
1 files changed, 412 insertions, 25 deletions
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
@@ -349,16 +746,6 @@
},
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
{ #1 }