% \iffalse meta-comment
%
%% File: tagpdf-checks.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-checks module (tagpdf)}
\begin{document}
\DocInput{\jobname.dtx}
\end{document}
%
% \fi
% \title{^^A
% The \pkg{tagpdf-checks} module\\ Messages and check code ^^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.99h, released 2024-11-18}
% \maketitle
% \begin{documentation}
% \section{Commands}
% \begin{function}[pTF,EXP]{\tag_if_active:}
% This command tests if tagging is active. It only gives true if all tagging has
% been activated, \emph{and} if tagging hasn't been stopped locally.
% \end{function}
% \begin{function}[EXP]{\tag_get:n}
% \begin{syntax}
% \cs{tag_get:n}\Arg{keyword}
% \end{syntax}
% This is a generic command to retrieve data for the current structure or
% mc-chunk. Currently
% the only sensible values for the argument \meta{keyword}
% are |mc_tag|, |struct_tag|, |struct_id| and |struct_num|.
% \end{function}
%
% \begin{function}[pTF,EXP]{\tag_if_box_tagged:N}
% \begin{syntax}
% \cs{tag_if_box_tagged:N}\Arg{box}
% \end{syntax}
% This tests if a box contains tagging commands.
% It relies currently on that the code, that saved the box, correctly sets
% the command \verb+\l_tag_box_\int_use:N #1_tl+ to a positive value.
% The LaTeX commands will do that automatically
% at some time but it is in the responsibility of the user to
% ensure that when using low-level code.
% If the internal command doesn't exist the box is assumed to be untagged.
% \end{function}
% \section{Description of log messages}
% \subsection{\cs{ShowTagging} command}
% \begin{tabular}{lll}
% Argument & type & note\\
% \cs{ShowTagging}{mc-data = num} & log+term & lua-only\\
% \cs{ShowTagging}{mc-current} & log+term & \\
% \cs{ShowTagging}{struck-stack= [log\verb+|+show]} & log or term+stop \\
% \cs{ShowTagging}{debug/structures = num} & log+termn & debug mode only
% \end{tabular}
%
% \subsection{Messages in checks and commands}
% \scriptsize
% \begin{tabular}{llll}
% command & message & action & remark\\
% |\@@_check_structure_has_tag:n|
% & |struct-missing-tag|
% & error
% \\
% |\@@_check_structure_tag:N|
% & |role-unknown-tag|
% & warning
% \\
% |\@@_check_info_closing_struct:n|
% & |struct-show-closing|
% & info
% & log-level>0
% \\
% |\@@_check_no_open_struct:|
% & struct-faulty-nesting
% & error
% & TODO: error only with 1?
% \\
% |\@@_check_struct_used:n|
% & |struct-used-twice|
% & warning
% \\
% |\@@_check_add_tag_role:nn|
% & |role-missing|, |role-tag|, |role-unknown|
% & warning, info (>0), warning
% \\
% |\@@_check_mc_if_nested:|,
% & |mc-nested|
% & warning
% \\
% |\@@_check_mc_if_open:|
% & |mc-not-open|
% & warning
% & only generic (?)
% \\
% |\@@_check_mc_pushed_popped:nn|
% & |mc-pushed|, |mc-popped|
% & info (2), info+|seq_log| (>2)
% \\
% |\@@_check_mc_tag:N|
% & |mc-tag-missing|, |role-unknown-tag|
% & error (missing), warning (unknown).
% \\
% |\@@_check_mc_used:n|
% & |mc-used-twice|
% & warning
% & TODO: review the sense of this test!
% \\
% |\@@_check_show_MCID_by_page:|
% &
% &
% & currently unused
% \\
% |\tag_mc_use:n|
% & |mc-label-unknown|, |mc-used-twice|
% & warning
% & in mc-shared
% \\
% |\role_add_tag:nn|
% & |new-tag|
% & info (>0)
% & in roles
% \\
% & |sys-no-interwordspace|
% & warning
% & space module, only xetex/dvi
% \\
% |\@@_struct_write_obj:n|
% & |struct-no-objnum|
% & error
% & in struct module
% \\
% |\@@_struct_write_obj:n|
% & |struct-orphan|
% & warning
% & in struct module
% \\
% |\tag_struct_begin:n|
% & |struct-faulty-nesting|
% & error
% \\
% |\@@_struct_insert_annot:nn|
% & |struct-faulty-nesting|
% & error
% \\
% |tag_struct_use:n|
% & |struct-label-unknown|
% & warning
% \\
% |attribute-class|, |attribute|
% & |attr-unknown|
% & error
% \\
% |\@@_tree_fill_parenttree:|
% & |tree-mcid-index-wrong|
% & warning
% TODO: should trigger a standard rerun message.
% \\
% in |enddocument/info|-hook
% & |para-hook-count-wrong|
% & error (warning?)
% \end{tabular}
%
% \normalsize
% \subsection{Messages from the ptagging code}
% A few messages are issued in generic
% mode from the code which reinserts missing TMB/TME.
% This is currently done if log-level is larger than zero.
% TODO: reconsider log-level and messages when this code
% settles down.
%
%
% \subsection{Warning messages from the lua-code}
% The messages are triggered if the log-level is at least equal to the number.
%
% \begin{tabular}[t]{>{\ttfamily}lll}
% message & log-level & remark \\\hline
% WARN TAG-NOT-TAGGED: &1 \\
% WARN TAG-OPEN-MC: &1 \\
% WARN SHIPOUT-MC-OPEN: &1 \\
% WARN SHIPOUT-UPS: &0 & shouldn't happen \\
% WARN TEX-MC-INSERT-MISSING:&0 & shouldn't happen \\
% WARN TEX-MC-INSERT-NO-KIDS:&2 & e.g. from empty hbox
% \end{tabular}
%
% \subsection{Info messages from the lua-code}
% The messages are triggered if the log-level is at least equal to the number.
% |TAG| messages are from the traversing function, |TEX| from code used in the tagpdf-mc module.
% |PARENTREE| is the code building the parenttree.
%
% \begin{longtable}{>{\ttfamily}lll}
% message & log-level & remark \\\hline\endhead
% INFO SHIPOUT-INSERT-LAST-EMC & 3 & finish of shipout code \\
% INFO SPACE-FUNCTION-FONT & 3 & interwordspace code \\
% INFO TAG-ABSPAGE & 3 \\
% INFO TAG-ARGS & 4 \\
% INFO TAG-ENDHEAD & 4 \\
% INFO TAG-ENDHEAD & 4 \\
% INFO TAG-HEAD & 3 \\
% INFO TAG-INSERT-ARTIFACT & 3 \\
% INFO TAG-INSERT-BDC & 3 \\
% INFO TAG-INSERT-EMC & 3 \\
% INFO TAG-INSERT-TAG & 3 \\
% INFO TAG-KERN-SUBTYPE & 4\\
% INFO TAG-MATH-SUBTYPE & 4 \\
% INFO TAG-MC-COMPARE & 4 \\
% INFO TAG-MC-INTO-PAGE & 3 \\
% INFO TAG-NEW-MC-NODE & 4 \\
% INFO TAG-NODE & 3 \\
% INFO TAG-NO-HEAD & 3 \\
% INFO TAG-NOT-TAGGED & 2 & replaced by artifact\\
% INFO TAG-QUITTING-BOX & 4 \\
% INFO TAG-STORE-MC-KID & 4 \\
% INFO TAG-TRAVERSING-BOX 3 \\
% INFO TAG-USE-ACTUALTEXT & 3 \\
% INFO TAG-USE-ALT & 3 \\
% INFO TAG-USE-RAW & 3 \\
% INFO TEX-MC-INSERT-KID & 3 \\
% INFO TEX-MC-INSERT-KID-TEST & 4 \\
% INFO TEX-MC-INTO-STRUCT & 3 \\
% INFO TEX-STORE-MC-DATA & 3 \\
% INFO TEX-STORE-MC-KID & 3 \\
% INFO PARENTTREE-CHUNKS & 3 \\
% INFO PARENTTREE-NO-DATA & 3\\
% INFO PARENTTREE-NUM & 3 \\
% INFO PARENTTREE-NUMENTRY & 3 \\
% INFO PARENTTREE-STRUCT-OBJREF & 4
% \end{longtable}
%
% \subsection{Debug mode messages and code}
% If the package tagpdf-debug is loaded a number of commands are redefined and
% enhanced with additional commands which can be used to output debug messages or
% collect statistics. The commands are present but do nothing if the log-level is zero.
% \begin{tabular}{llll}
% command & name & action & remark\\\hline
% |\tag_mc_begin:n| & mc-begin-insert & msg \\
% & mc-begin-ignore & msg & if inactive \\
% \end{tabular}
%
% \subsection{Messages}
% \begin{function}{
% mc-nested,
% mc-tag-missing,
% mc-label-unknown,
% mc-used-twice,
% mc-used-twice,
% mc-not-open,
% mc-pushed,
% mc-popped,
% mc-current}
% Various messages related to mc-chunks. TODO document their meaning.
% \end{function}
% \begin{function}{struct-unknown,struct-no-objnum,struct-orphan,struct-faulty-nesting,
% struct-missing-tag,struct-used-twice,struct-label-unknown,struct-show-closing}
% Various messages related to structure. Check the definition in the code for their
% meaning and the arguments they take.
% \end{function}
%
% \begin{function}{tree-struct-still-open}
% Message issued at the end of the compilation
% if there are (beside Root) other open structures on the stack.
% \end{function}
%
% \begin{function}{tree-statistic}
% Message issued at the end of the compilation
% showing the number of objects to write
% \end{function}
%
% \begin{function}{show-struct,show-kids}
% These two messages are used in debug mode
% to show the current structures in the log and terminal.
% \end{function}
%
% \begin{function}{attr-unknown}
% Message if an attribute i sunknown.
% \end{function}
%
% \begin{function}{role-missing,role-unknown,role-unknown-tag,role-unknown-NS,role-tag,new-tag,
% role-parent-child,role-remapping}
% Messages related to role mapping.
% \end{function}
%
% \begin{function}{tree-mcid-index-wrong}
% Used in the tree code, typically indicates the document must be rerun.
% \end{function}
%
% \begin{function}{sys-no-interwordspace}
% Message if an engine doesn't support inter word spaces
% \end{function}
%
% \begin{function}{para-hook-count-wrong}
% Message if the number of begin paragraph and end paragraph differ. This normally means faulty structure.
% \end{function}
% \end{documentation}
% \begin{implementation}
% \begin{macrocode}
%<@@=tag>
%<*header>
\ProvidesExplPackage {tagpdf-checks-code} {2024-11-18} {0.99h}
{part of tagpdf - code related to checks, conditionals, debugging and messages}
%
% \end{macrocode}
% \section{Messages}
%
% \subsection{Messages related to mc-chunks}
% \begin{macro}{mc-nested}
% This message is issue is a mc is opened before the previous has been closed.
% This is not relevant for luamode, as the attributes don't care about this.
% It is used in the |\@@_check_mc_if_nested:| test.
% \begin{macrocode}
%<*package>
\msg_new:nnn { tag } {mc-nested} { nested~marked~content~found~-~mcid~#1 }
% \end{macrocode}
% \end{macro}
% \begin{macro}{mc-tag-missing}
% If the tag is missing
% \begin{macrocode}
\msg_new:nnn { tag } {mc-tag-missing} { required~tag~missing~-~mcid~#1 }
% \end{macrocode}
% \end{macro}
% \begin{macro}{mc-label-unknown}
% If the label of a mc that is used in another place is not known (yet)
% or has been undefined as the mc was already used.
% \begin{macrocode}
\msg_new:nnn { tag } {mc-label-unknown}
{ label~#1~unknown~or~has~been~already~used.\\
Either~rerun~or~remove~one~of~the~uses. }
% \end{macrocode}
% \end{macro}
% \begin{macro}{mc-used-twice}
% An mc-chunk can be inserted only in one structure. This indicates wrong coding
% and so should at least give a warning.
% \begin{macrocode}
\msg_new:nnn { tag } {mc-used-twice} { mc~#1~has~been~already~used }
% \end{macrocode}
% \end{macro}
% \begin{macro}{mc-not-open}
% This is issued if a |\tag_mc_end:| is issued wrongly, wrong coding.
% \begin{macrocode}
\msg_new:nnn { tag } {mc-not-open} { there~is~no~mc~to~end~at~#1 }
% \end{macrocode}
% \end{macro}
% \begin{macro}{mc-pushed,mc-popped}
% Informational messages about mc-pushing.
% \begin{macrocode}
\msg_new:nnn { tag } {mc-pushed} { #1~has~been~pushed~to~the~mc~stack}
\msg_new:nnn { tag } {mc-popped} { #1~has~been~removed~from~the~mc~stack }
% \end{macrocode}
% \end{macro}
% \begin{macro}{mc-current}
% Informational messages about current mc state.
% \begin{macrocode}
\msg_new:nnn { tag } {mc-current}
{ current~MC:~
\bool_if:NTF\g_@@_in_mc_bool
{abscnt=\@@_get_mc_abs_cnt:,~tag=\g_@@_mc_key_tag_tl}
{no~MC~open,~current~abscnt=\@@_get_mc_abs_cnt:"}
}
% \end{macrocode}
% \end{macro}
% \subsection{Messages related to structures}
% \begin{macro}{struct-unknown}
% if for example a parent key value points to structure that doesn't exist (yet)
% \begin{macrocode}
\msg_new:nnn { tag } {struct-unknown}
{ structure~with~number~#1~doesn't~exist\\ #2 }
% \end{macrocode}
% \end{macro}
% \begin{macro}{struct-no-objnum}
% Should not happen \ldots
% \begin{macrocode}
\msg_new:nnn { tag } {struct-no-objnum} { objnum~missing~for~structure~#1 }
% \end{macrocode}
% \end{macro}
% \begin{macro}{struct-orphan}
% This indicates that there is a structure which has kids but no parent.
% This can happen if a structure is stashed but then not used.
% \begin{macrocode}
\msg_new:nnn { tag } {struct-orphan}
{
Structure~#1~has~#2~kids~but~no~parent.\\
It~is~turned~into~an~artifact.\\
Did~you~stashed~a~structure~and~then~didn't~use~it?
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{struct-faulty-nesting}
% This indicates that there is somewhere one |\tag_struct_end:| too much.
% This should be normally an error.
% \begin{macrocode}
\msg_new:nnn { tag }
{struct-faulty-nesting}
{ there~is~no~open~structure~on~the~stack }
% \end{macrocode}
% \end{macro}
% \begin{macro}{struct-missing-tag}
% A structure must have a tag.
% \begin{macrocode}
\msg_new:nnn { tag } {struct-missing-tag} { a~structure~must~have~a~tag! }
% \end{macrocode}
% \end{macro}
% \begin{macro}{struct-used-twice}
% \begin{macrocode}
\msg_new:nnn { tag } {struct-used-twice}
{ structure~with~label~#1~has~already~been~used}
% \end{macrocode}
% \end{macro}
% \begin{macro}{struct-label-unknown}
% label is unknown, typically needs a rerun.
% \begin{macrocode}
\msg_new:nnn { tag } {struct-label-unknown}
{ structure~with~label~#1~is~unknown~rerun}
% \end{macrocode}
% \end{macro}
% \begin{macro}{struct-show-closing}
% Informational message shown if log-mode is high enough
% \begin{macrocode}
\msg_new:nnn { tag } {struct-show-closing}
{ closing~structure~#1~tagged~\use:e{\prop_item:cn{g_@@_struct_#1_prop}{S}} }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{struct-Ref-unknown}
% This message is issued at the end, when the Ref keys are updated.
% TODO: in debug mode it should report more info about the structure.
% \begin{macrocode}
\msg_new:nnn { tag } {struct-Ref-unknown}
{
#1~has~no~related~structure.\\
/Ref~not~updated.
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{tree-struct-still-open}
% Message issued at the end if there are beside Root other
% open structures on the stack.
% \begin{macrocode}
\msg_new:nnn { tag } {tree-struct-still-open}
{
There~are~still~open~structures~on~the~stack!\\
The~stack~contains~\seq_use:Nn\g_@@_struct_tag_stack_seq{,}.\\
The~structures~are~automatically~closed,\\
but~their~nesting~can~be~wrong.
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{tree-statistic}
% Message issued at the end showing the estimated number of
% structures and MC-childs
% \begin{macrocode}
\msg_new:nnn { tag } {tree-statistic}
{
Finalizing~the~tagging~structure:\\
Writing~out~\c_tilde_str
\int_use:N\c@g_@@_struct_abs_int\c_space_tl~structure~objects\\
with~\c_tilde_str
\int_use:N\c@g_@@_MCID_abs_int\c_space_tl'MC'~leaf~nodes.\\
Be~patient~if~there~are~lots~of~objects!
}
%
% \end{macrocode}
% \end{macro}
% The following messages are only needed in debug mode.
% \begin{macro}{show-struct,show-kids}
% This two messages are used to show the current structures in the log and terminal.
% \begin{macrocode}
%<*debug>
\msg_new:nnn { tag/debug } { show-struct }
{
=========================\\
The~structure~#1~
\tl_if_empty:nTF {#2}
{ is~empty \\>~ . }
{ contains: #2 }
\\
}
\msg_new:nnn { tag/debug } { show-kids }
{
The~structure~has~the~following~kids:
\tl_if_empty:nTF {#2}
{ \\>~ NONE }
{ #2 }
\\
=========================
}
%
% \end{macrocode}
% \end{macro}
% \subsection{Attributes}
% Not much yet, as attributes aren't used so much.
% \begin{macro}{attr-unknown}
% \begin{macrocode}
%<*package>
\msg_new:nnn { tag } {attr-unknown} { attribute~#1~is~unknown}
% \end{macrocode}
% \end{macro}
%
% \subsection{Roles}
% \begin{macro}{role-missing,role-unknown,role-unknown-tag,role-unknown-NS}
% Warning message if either the tag or the role is missing
% \begin{macrocode}
\msg_new:nnn { tag } {role-missing} { tag~#1~has~no~role~assigned }
\msg_new:nnn { tag } {role-unknown} { role~#1~is~not~known }
\msg_new:nnn { tag } {role-unknown-tag} { tag~#1~is~not~known }
\msg_new:nnn { tag } {role-unknown-NS} { \tl_if_empty:nTF{#1}{Empty~NS}{NS~#1~is~not~known} }
% \end{macrocode}
% \end{macro}
% \begin{macro}{role-parent-child}
% This is info and warning message about the containment rules between child and
% parent tags.
% \begin{macrocode}
\msg_new:nnn { tag } {role-parent-child}
{ Parent-Child~'#1'~-->~'#2'.\\Relation~is~#3~\msg_line_context:}
% \end{macrocode}
% \end{macro}
% \begin{macro}{role-remapping}
% This is info and warning message about role-remapping
% \begin{macrocode}
\msg_new:nnn { tag } {role-remapping}
{ remapping~tag~to~#1 }
% \end{macrocode}
% \end{macro}
% \begin{macro}{role-tag,new-tag}
% Info messages.
% \begin{macrocode}
\msg_new:nnn { tag } {role-tag} { mapping~tag~#1~to~role~#2 }
\msg_new:nnn { tag } {new-tag} { adding~new~tag~#1 }
\msg_new:nnn { tag } {read-namespace} { reading~namespace~definitions~tagpdf-ns-#1.def }
\msg_new:nnn { tag } {namespace-missing}{ namespace~definitions~tagpdf-ns-#1.def~not~found }
\msg_new:nnn { tag } {namespace-unknown}{ namespace~#1~is~not~declared }
% \end{macrocode}
% \end{macro}
%
% \subsection{Miscellaneous}
% \begin{macro}{tree-mcid-index-wrong}
% Used in the tree code, typically indicates the document must be rerun.
% \begin{macrocode}
\msg_new:nnn { tag } {tree-mcid-index-wrong}
{something~is~wrong~with~the~mcid--rerun}
% \end{macrocode}
% \end{macro}
% \begin{macro}{sys-no-interwordspace}
% Currently only pdflatex and lualatex have some support for real spaces.
% \begin{macrocode}
\msg_new:nnn { tag } {sys-no-interwordspace}
{engine/output~mode~#1~doesn't~support~the~interword~spaces}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_check_typeout_v:n}
% A simple logging function. By default is gobbles its argument, but
% the log-keys sets it to typeout.
% \begin{macrocode}
\cs_set_eq:NN \@@_check_typeout_v:n \use_none:n
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{para-hook-count-wrong}
% At the end of the document we check if the count of para-begin and para-end is
% identical. If not we issue a warning: this is normally a coding error and
% and breaks the structure.
% \begin{macrocode}
\msg_new:nnnn { tag } {para-hook-count-wrong}
{The~number~of~automatic~begin~(#1)~and~end~(#2)~#3~para~hooks~differ!}
{This~quite~probably~a~coding~error~and~the~structure~will~be~wrong!}
%
% \end{macrocode}
% \end{macro}
% \section{Retrieving data}
% \begin{macro}[EXP]{\tag_get:n}
% This retrieves some data.
% This is a generic command to retrieve data. Currently
% the only sensible values for the argument are |mc_tag|, |struct_tag|
% and |struct_num|.
% \begin{macrocode}
%\cs_new:Npn \tag_get:n #1 { \use:c {@@_get_data_#1: } }
% \end{macrocode}
% \end{macro}
%
% \section{User conditionals}
% \begin{macro}[pTF]{\tag_if_active:}
% This tests if tagging is active. This allows packages
% to add conditional code.
% The test is true if all booleans, the global and the two local one are true.
%
% \begin{macrocode}
%<*base>
\prg_new_conditional:Npnn \tag_if_active: { p , T , TF, F }
{ \prg_return_false: }
%
%<*package>
\prg_set_conditional:Npnn \tag_if_active: { p , T , TF, F }
{
\bool_lazy_all:nTF
{
{\g_@@_active_struct_bool}
{\g_@@_active_mc_bool}
{\g_@@_active_tree_bool}
{\l_@@_active_struct_bool}
{\l_@@_active_mc_bool}
}
{
\prg_return_true:
}
{
\prg_return_false:
}
}
%
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF]{\tag_if_box_tagged:N}
% This tests if a box contains tagging commands.
% It relies on that the code that saved the box correctly set
% \verb+\l_tag_box__tl+ to a positive value.
% The LaTeX commands will do that automatically
% at some time but it is in the responsibility of the user to
% ensure that when using low-level code.
% If the internal command doesn't exist the box is assumed to be untagged.
% \begin{macrocode}
%<*base>
\prg_new_conditional:Npnn \tag_if_box_tagged:N #1 {p,T,F,TF}
{
\tl_if_exist:cTF {l_tag_box_\int_use:N #1_tl}
{
\int_compare:nNnTF {0\tl_use:c{l_tag_box_\int_use:N #1_tl}}>{0}
{ \prg_return_true: }
{ \prg_return_false: }
}
{
\prg_return_false:
% warning??
}
}
%
% \end{macrocode}
% \end{macro}
%
% \section{Internal checks}
% These are checks used in various places in the code.
%
% \subsection{checks for active tagging}
% \begin{macro}[TF]{\@@_check_if_active_mc:,\@@_check_if_active_struct:}
% This checks if mc are active.
% \begin{macrocode}
%<*package>
\prg_new_conditional:Npnn \@@_check_if_active_mc: {T,F,TF}
{
\bool_lazy_and:nnTF { \g_@@_active_mc_bool } { \l_@@_active_mc_bool }
{
\prg_return_true:
}
{
\prg_return_false:
}
}
\prg_new_conditional:Npnn \@@_check_if_active_struct: {T,F,TF}
{
\bool_lazy_and:nnTF { \g_@@_active_struct_bool } { \l_@@_active_struct_bool }
{
\prg_return_true:
}
{
\prg_return_false:
}
}
% \end{macrocode}
% \end{macro}
% \subsection{Checks related to structures}
% \begin{macro}{\@@_check_structure_has_tag:n}
% Structures must have a tag, so we check if the S entry is in the property.
% It is an error if this is missing. The argument is a number.
% The tests for existence and type is split in structures,
% as the tags are stored differently to the mc case.
% \begin{macrocode}
\cs_new_protected:Npn \@@_check_structure_has_tag:n #1 %#1 struct num
{
\prop_if_in:cnF { g_@@_struct_#1_prop }
{S}
{
\msg_error:nn { tag } {struct-missing-tag}
}
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_check_structure_tag:N}
% This checks if the name of the tag is known,
% either because it is a standard type or has been rolemapped.
% \begin{macrocode}
\cs_new_protected:Npn \@@_check_structure_tag:N #1
{
\prop_if_in:NoF \g_@@_role_tags_NS_prop {#1}
{
\msg_warning:nne { tag } {role-unknown-tag} {#1}
}
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_check_info_closing_struct:n}
% This info message is issued at a closing structure, the use should be guarded by
% log-level.
% \begin{macrocode}
\cs_new_protected:Npn \@@_check_info_closing_struct:n #1 %#1 struct num
{
\int_compare:nNnT {\l_@@_loglevel_int} > { 0 }
{
\msg_info:nnn { tag } {struct-show-closing} {#1}
}
}
\cs_generate_variant:Nn \@@_check_info_closing_struct:n {o,e}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_check_no_open_struct:}
% This checks if there is an open structure. It should be used when trying to
% close a structure. It errors if false.%
% \begin{macrocode}
\cs_new_protected:Npn \@@_check_no_open_struct:
{
\msg_error:nn { tag } {struct-faulty-nesting}
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_check_struct_used:n}
% This checks if a stashed structure has already been used.
% \begin{macrocode}
\cs_new_protected:Npn \@@_check_struct_used:n #1 %#1 label
{
\prop_get:cnNT
{g_@@_struct_\property_ref:enn{tagpdfstruct-#1}{tagstruct}{unknown}_prop}
{P}
\l_@@_tmpa_tl
{
\msg_warning:nnn { tag } {struct-used-twice} {#1}
}
}
% \end{macrocode}
% \end{macro}
%
% \subsection{Checks related to roles}
% \begin{macro}{\@@_check_add_tag_role:nn}
% This check is used when defining a new role mapping.
% \begin{macrocode}
\cs_new_protected:Npn \@@_check_add_tag_role:nn #1 #2 %#1 tag, #2 role
{
\tl_if_empty:nTF {#2}
{
\msg_error:nnn { tag } {role-missing} {#1}
}
{
\prop_get:NnNTF \g_@@_role_tags_NS_prop {#2} \l_@@_tmpa_tl
{
\int_compare:nNnT {\l_@@_loglevel_int} > { 0 }
{
\msg_info:nnnn { tag } {role-tag} {#1} {#2}
}
}
{
\msg_error:nnn { tag } {role-unknown} {#2}
}
}
}
% \end{macrocode}
% Similar with a namespace
% \begin{macrocode}
\cs_new_protected:Npn \@@_check_add_tag_role:nnn #1 #2 #3 %#1 tag/NS, #2 role #3 namespace
{
\tl_if_empty:nTF {#2}
{
\msg_error:nnn { tag } {role-missing} {#1}
}
{
\prop_get:cnNTF { g_@@_role_NS_#3_prop } {#2} \l_@@_tmpa_tl
{
\int_compare:nNnT {\l_@@_loglevel_int} > { 0 }
{
\msg_info:nnnn { tag } {role-tag} {#1} {#2/#3}
}
}
{
\msg_error:nnn { tag } {role-unknown} {#2/#3}
}
}
}
% \end{macrocode}
% \end{macro}
% \subsection{Check related to mc-chunks}
%
% \begin{macro}{\@@_check_mc_if_nested:,\@@_check_mc_if_open:}
% Two tests if a mc is currently open. One for the true (for begin
% code), one for the false part (for end code).
% \begin{macrocode}
\cs_new_protected:Npn \@@_check_mc_if_nested:
{
\@@_mc_if_in:T
{
\msg_warning:nne { tag } {mc-nested} { \@@_get_mc_abs_cnt: }
}
}
\cs_new_protected:Npn \@@_check_mc_if_open:
{
\@@_mc_if_in:F
{
\msg_warning:nne { tag } {mc-not-open} { \@@_get_mc_abs_cnt: }
}
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_check_mc_pushed_popped:nn}
% This creates an information message if mc's are pushed or popped.
% The first argument is a word (pushed or popped), the second the tag name.
% With larger log-level the stack is shown too.
% \begin{macrocode}
\cs_new_protected:Npn \@@_check_mc_pushed_popped:nn #1 #2
{
\int_compare:nNnT
{ \l_@@_loglevel_int } ={ 2 }
{ \msg_info:nne {tag}{mc-#1}{#2} }
\int_compare:nNnT
{ \l_@@_loglevel_int } > { 2 }
{
\msg_info:nne {tag}{mc-#1}{#2}
\seq_log:N \g_@@_mc_stack_seq
}
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_check_mc_tag:N}
% This checks if the mc has a (known) tag.
% \begin{macrocode}
\cs_new_protected:Npn \@@_check_mc_tag:N #1 %#1 is var with a tag name in it
{
\tl_if_empty:NT #1
{
\msg_error:nne { tag } {mc-tag-missing} { \@@_get_mc_abs_cnt: }
}
\prop_if_in:NoF \g_@@_role_tags_NS_prop {#1}
{
\msg_warning:nne { tag } {role-unknown-tag} {#1}
}
}
% \end{macrocode}
% \end{macro}
%
% \begin{variable}{\g_@@_check_mc_used_intarray, \@@_check_init_mc_used:}
% This variable holds the list of used mc numbers.
% Everytime we store a mc-number we will add one the relevant array index
% If everything is right at the end there should be only 1 until the max count
% of the mcid. 2 indicates that one mcid was used twice, 0 that we lost one.
% In engines other than luatex the total number of all intarray entries
% are restricted so we use only a rather small value of 65536, and we initialize
% the array only at first used, guarded by the log-level.
% This check is probably only needed for debugging.
% TODO does this really make sense to check? When can it happen??
% \begin{macrocode}
\cs_new_protected:Npn \@@_check_init_mc_used:
{
\intarray_new:Nn \g_@@_check_mc_used_intarray { 65536 }
\cs_gset_eq:NN \@@_check_init_mc_used: \prg_do_nothing:
}
% \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_check_mc_used:n}
% This checks if a mc is used twice.
% \begin{macrocode}
\cs_new_protected:Npn \@@_check_mc_used:n #1 %#1 mcid abscnt
{
\int_compare:nNnT {\l_@@_loglevel_int} > { 2 }
{
\@@_check_init_mc_used:
\intarray_gset:Nnn \g_@@_check_mc_used_intarray
{#1}
{ \intarray_item:Nn \g_@@_check_mc_used_intarray {#1} + 1 }
\int_compare:nNnT
{
\intarray_item:Nn \g_@@_check_mc_used_intarray {#1}
}
>
{ 1 }
{
\msg_warning:nnn { tag } {mc-used-twice} {#1}
}
}
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_check_show_MCID_by_page:}
% This allows to show the mc on a page. Currently unused.
% \begin{macrocode}
\cs_new_protected:Npn \@@_check_show_MCID_by_page:
{
\tl_set:Ne \l_@@_tmpa_tl
{
\@@_property_ref_lastpage:nn
{abspage}
{-1}
}
\int_step_inline:nnnn {1}{1}
{
\l_@@_tmpa_tl
}
{
\seq_clear:N \l_@@_tmpa_seq
\int_step_inline:nnnn
{1}
{1}
{
\@@_property_ref_lastpage:nn
{tagmcabs}
{-1}
}
{
\int_compare:nT
{
\property_ref:enn
{mcid-####1}
{tagabspage}
{-1}
=
##1
}
{
\seq_gput_right:Ne \l_@@_tmpa_seq
{
Page##1-####1-
\property_ref:enn
{mcid-####1}
{tagmcid}
{-1}
}
}
}
\seq_show:N \l_@@_tmpa_seq
}
}
% \end{macrocode}
% \end{macro}
%
% \subsection{Checks related to the state of MC on a page or in a split stream}
% The following checks are currently only usable in generic mode as they
% rely on the marks defined in the mc-generic module. They are used to detect
% if a mc-chunk has been split by a page break or similar and additional
% end/begin commands are needed.
%
% \begin{macro}[pTF]{\@@_check_mc_in_galley:}
% At first we need a test to decide if |\tag_mc_begin:n| (tmb) and |\tag_mc_end:|
% (tme) has been used at all on the current galley. As each command issues
% two slightly different marks we can do it by comparing firstmarks and botmarks.
% The test assumes that the marks have been already mapped into the sequence with
% |\@@_mc_get_marks:|. As |\seq_if_eq:NNTF| doesn't exist we use the tl-test.
% \begin{macrocode}
\prg_new_conditional:Npnn \@@_check_if_mc_in_galley: { T,F,TF }
{
\tl_if_eq:NNTF \l_@@_mc_firstmarks_seq \l_@@_mc_botmarks_seq
{ \prg_return_false: }
{ \prg_return_true: }
}
% \end{macrocode}
% \end{macro}
% \begin{macro}[pTF]{\@@_check_if_mc_tmb_missing:}
% This checks if a extra top mark (\enquote{extra-tmb}) is needed.
% According to the analysis this the case if the firstmarks start with
% |e-| or |b+|.
% Like above we assume that the marks content is already in the seq's.
% \begin{macrocode}
\prg_new_conditional:Npnn \@@_check_if_mc_tmb_missing: { T,F,TF }
{
\bool_if:nTF
{
\str_if_eq_p:ee {\seq_item:Nn \l_@@_mc_firstmarks_seq {1}}{e-}
||
\str_if_eq_p:ee {\seq_item:Nn \l_@@_mc_firstmarks_seq {1}}{b+}
}
{ \prg_return_true: }
{ \prg_return_false: }
}
% \end{macrocode}
% \end{macro}
% \begin{macro}[pTF]{\@@_check_if_mc_tme_missing:}
% This checks if a extra bottom mark (\enquote{extra-tme}) is needed.
% According to the analysis this the case if the botmarks starts with
% |b+|.
% Like above we assume that the marks content is already in the seq's.
% \begin{macrocode}
\prg_new_conditional:Npnn \@@_check_if_mc_tme_missing: { T,F,TF }
{
\str_if_eq:eeTF {\seq_item:Nn \l_@@_mc_botmarks_seq {1}}{b+}
{ \prg_return_true: }
{ \prg_return_false: }
}
% \end{macrocode}
% \end{macro}
% \begin{macrocode}
%
% \end{macrocode}
% \begin{macrocode}
%<*debug>
% \end{macrocode}
% Code for tagpdf-debug. This will probably change over time.
% At first something for the mc commands.
% \begin{macrocode}
\msg_new:nnn { tag / debug } {mc-begin} { MC~begin~#1~with~options:~\tl_to_str:n{#2}~[\msg_line_context:] }
\msg_new:nnn { tag / debug } {mc-end} { MC~end~#1~[\msg_line_context:] }
\cs_new_protected:Npn \@@_debug_mc_begin_insert:n #1
{
\int_compare:nNnT { \l_@@_loglevel_int } > {0}
{
\msg_note:nnnn { tag / debug } {mc-begin} {inserted} { #1 }
}
}
\cs_new_protected:Npn \@@_debug_mc_begin_ignore:n #1
{
\int_compare:nNnT { \l_@@_loglevel_int } > {0}
{
\msg_note:nnnn { tag / debug } {mc-begin } {ignored} { #1 }
}
}
\cs_new_protected:Npn \@@_debug_mc_end_insert:
{
\int_compare:nNnT { \l_@@_loglevel_int } > {0}
{
\msg_note:nnn { tag / debug } {mc-end} {inserted}
}
}
\cs_new_protected:Npn \@@_debug_mc_end_ignore:
{
\int_compare:nNnT { \l_@@_loglevel_int } > {0}
{
\msg_note:nnn { tag / debug } {mc-end } {ignored}
}
}
% \end{macrocode}
% And now something for the structures
% \begin{macrocode}
\msg_new:nnn { tag / debug } {struct-begin}
{
Struct~\tag_get:n{struct_num}~begin~#1~with~options:~\tl_to_str:n{#2}~\\[\msg_line_context:]
}
\msg_new:nnn { tag / debug } {struct-end}
{
Struct~end~#1~[\msg_line_context:]
}
\msg_new:nnn { tag / debug } {struct-end-wrong}
{
Struct~end~'#1'~doesn't~fit~start~'#2'~[\msg_line_context:]
}
\cs_new_protected:Npn \@@_debug_struct_begin_insert:n #1
{
\int_compare:nNnT { \l_@@_loglevel_int } > {0}
{
\msg_note:nnnn { tag / debug } {struct-begin} {inserted} { #1 }
\seq_log:N \g_@@_struct_tag_stack_seq
}
}
\cs_new_protected:Npn \@@_debug_struct_begin_ignore:n #1
{
\int_compare:nNnT { \l_@@_loglevel_int } > {0}
{
\msg_note:nnnn { tag / debug } {struct-begin } {ignored} { #1 }
}
}
\cs_new_protected:Npn \@@_debug_struct_end_insert:
{
\int_compare:nNnT { \l_@@_loglevel_int } > {0}
{
\msg_note:nnn { tag / debug } {struct-end} {inserted}
\seq_log:N \g_@@_struct_tag_stack_seq
}
}
\cs_new_protected:Npn \@@_debug_struct_end_ignore:
{
\int_compare:nNnT { \l_@@_loglevel_int } > {0}
{
\msg_note:nnn { tag / debug } {struct-end } {ignored}
}
}
\cs_new_protected:Npn \@@_debug_struct_end_check:n #1
{
\int_compare:nNnT { \l_@@_loglevel_int } > {0}
{
\seq_get:NNT \g_@@_struct_tag_stack_seq \l_@@_tmpa_tl
{
\str_if_eq:eeF
{#1}
{\exp_last_unbraced:NV\use_i:nn \l_@@_tmpa_tl}
{
\msg_warning:nnee { tag/debug }{ struct-end-wrong }
{#1}
{\exp_last_unbraced:NV\use_i:nn \l_@@_tmpa_tl}
}
}
}
}
% \end{macrocode}
% This tracks tag suspend and resume.
% The tag-suspend message should go before the int is increased.
% The tag-resume message after the int is decreased.
% \begin{macrocode}
\msg_new:nnn { tag / debug } {tag-suspend}
{
\int_if_zero:nTF
{#1}
{Tagging~suspended}
{Tagging~(not)~suspended~(already~inactive)}\\
level:~#1~==>~\int_eval:n{#1+1}\tl_if_empty:nF{#2}{,~label:~#2}~[\msg_line_context:]
}
\msg_new:nnn { tag / debug } {tag-resume}
{
\int_if_zero:nTF
{#1}
{Tagging~resumed}
{Tagging~(not)~resumed}\\
level:~\int_eval:n{#1+1}~==>~#1\tl_if_empty:nF{#2}{,~label:~#2}~[\msg_line_context:]
}
% \end{macrocode}
% \begin{macrocode}
%
% \end{macrocode}
%
% \subsection{Benchmarks}
% It doesn't make much sense to do benchmarks in debug mode or in combination
% with a log-level as this would slow down the compilation.
% So we add simple commands that can be activated if l3benchmark has been loaded.
% TODO: is a warning needed?
% \begin{macrocode}
%<*package>
\cs_new_protected:Npn \@@_check_benchmark_tic:{}
\cs_new_protected:Npn \@@_check_benchmark_toc:{}
\cs_new_protected:Npn \tag_check_benchmark_on:
{
\cs_if_exist:NT \benchmark_tic:
{
\cs_set_eq:NN \@@_check_benchmark_tic: \benchmark_tic:
\cs_set_eq:NN \@@_check_benchmark_toc: \benchmark_toc:
}
}
%
% \end{macrocode}
% \end{implementation}
% \PrintIndex