% \iffalse meta-comment % %% File: tagpdf-user.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-user module (tagpdf)} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % \title{^^A % The \pkg{tagpdf-user} module\\ % Code related to \LaTeX2e user commands and document commands ^^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.99i, released 2024-11-19} % \maketitle % \begin{documentation} % \section{Setup commands} % \begin{function}{\tagpdfsetup} % \begin{syntax} % \cs{tagpdfsetup}\Arg{key val list} % \end{syntax} % This is the main setup command to adapt the behaviour of tagpdf. % It can be used in the preamble and in the document (but not all keys make sense there). % \end{function} % % \begin{function}{activate (setup-key)} % And additional setup key which combine the other activate keys % |activate/mc|, |activate/tree|, |activate/struct| and additionally % adds a document structure. % \end{function} % % \begin{function}{\tag_tool:n,\tagtool} % \begin{syntax} % \cs{tag_tool:n}\Arg{key val} % \end{syntax} % The tagging of basic document elements will require a variety of small commands % to configure and adapt the tagging. This command will collect them under a command % interface. The argument is \emph{one} key-value like string. This is work in progress % and both syntax, known arguments and implementation can change! % \end{function} % % \section{Commands related to mc-chunks} % \begin{function}{\tagmcbegin, \tagmcend,\tagmcuse} % \begin{syntax} % \cs{tagmcbegin} \Arg{key-val}\\ % \cs{tagmcend}\\ % \cs{tagmcuse}\Arg{label} % \end{syntax} % These are wrappers around |\tag_mc_begin:n|, |\tag_mc_end:| and |\tag_mc_use:n|. % The commands and their argument are documentated in the \pkg{tagpdf-mc} module. % In difference to the expl3 commands, \cs{tagmcbegin} issues also an \cs{ignorespaces}, % and \cs{tagmcend} will issue in horizontal mode an \cs{unskip}. % \end{function} % % \begin{function}{\tagmcifinTF} % \begin{syntax} % \cs{tagmcifin} \Arg{true code}\Arg{false code} % \end{syntax} % This is a wrapper around |\tag_mc_if_in:TF|. % and tests if an mc is open or not. It is mostly of % importance for pdflatex as lualatex doesn't mind much if a mc tag is not % correctly closed. Unlike the expl3 command it is not expandable. % % The command is probably not of much use and will perhaps disappear in future % versions. It normally makes more sense to push/pop an mc-chunk. % \end{function} % % \section{Commands related to structures} % \begin{function}{\tagstructbegin, \tagstructend,\tagstructuse} % \begin{syntax} % \cs{tagstructbegin} \Arg{key-val}\\ % \cs{tagstructend}\\ % \cs{tagstructuse}\Arg{label} % \end{syntax} % These are direct wrappers around |\tag_struct_begin:n|, % |\tag_struct_end:| and |\tag_struct_use:n|. % The commands and their argument are documented in the \pkg{tagpdf-struct} module. % \end{function} % \section{Debugging} % \begin{function}{\ShowTagging} % \begin{syntax} % \cs{ShowTagging} \Arg{key-val} % \end{syntax} % This is a generic function to output various debugging helps. It not % necessarily stops the compilation. The keys and their function are described below. % \end{function} % % \begin{function}{mc-data (show-key)} % \begin{syntax} % |mc-data| = \meta{number} % \end{syntax} % This key is (currently?) relevant for lua mode only. % It shows the data of all mc-chunks created so far. % It is accurate only after shipout (and perhaps a second compilation), % so typically should be issued after a % newpage. The value is a positive integer and sets the first mc-shown. If no value % is given, 1 is used and so all mc-chunks created so far are shown. % \end{function} % \begin{function}{mc-current (show-key)} % \begin{syntax} % |mc-current| % \end{syntax} % This key shows the number and the tag of the currently open mc-chunk. If no % chunk is open it shows only the state of the abs count. It works in all mode, % but the output in luamode looks different. % \end{function} % \begin{function}{mc-marks (show-key)} % \begin{syntax} % |mc-marks| = |show|\verb+|+|use| % \end{syntax} % This key helps to debug the page marks. It should only be used at shipout in % header or footer. % \end{function} % \begin{function}{struct-stack (show-key)} % \begin{syntax} % |struct-stack| = |log|\verb+|+|show| % \end{syntax} % This key shows the current structure stack. With |log| the info is only % written to the log-file, |show| stops the compilation and shows on the terminal. % If no value is used, then the default is |show|. % \end{function} % % \begin{function}{debug/structures (show-key)} % \begin{syntax} % |debug/structures| = \meta{structure number} % \end{syntax} % This key is available only if the tagpdf-debug package is loaded % and shows all structures starting with the one with the number % given by the key. % \end{function} % \section{Extension commands} % The following commands and code parts are not core commands of tagpdf. % They either provide work-arounds for missing functionality elsewhere, % or do a first step to apply tagpdf commands to document commands. % % The commands and keys should be view as experimental! % % This part will be regularly revisited to check if the code should go to a % better place or can be improved and so can change easily. % % \subsection{Fake space} % \begin{function}{\pdffakespace} % (lua-only) This provides a lua-version of the |\pdffakespace| primitive of % pdftex. % \end{function} % % \subsection{Tagging of paragraphs} % % This makes use of the paragraph hooks in LaTeX to % automate the tagging of paragraph. It requires sane paragraph nesting, % faulty code, e.g. a missing |\par| at the end of a low-level vbox can highly % confuse the tagging. The tags should be carefully checked if this is used. % % \begin{function}{para/tagging (setup-key),paratagging-show (deprecated),paratagging (deprecated)} % \begin{syntax} % |para/tagging| = |true|\verb+|+|false|\\ % |debug/show=para|\\ % |debug/show=paraOff| % \end{syntax} % The |para/tagging| key can be used in |\tagpdfsetup| and enable/disables tagging % of paragraphics. % |debug/show=para| puts small colored numbers at the begin and end of a paragraph. % This is meant as a debugging help. The number are boxes and have a (tiny) height, % so they can affect typesetting. % \end{function} % % \begin{function}{\tagpdfparaOn,\tagpdfparaOff} % These commands allow to enable/disable para tagging too and are a bit % faster then |\tagpdfsetup|. But I'm not sure if the names are good. % \end{function} % % \begin{function}{\tagpdfsuppressmarks} % This command allows to suppress the creation of the marks. It takes an argument % which should normally be one of the mc-commands, puts a group around it and % suppress the marks creation in this group. This command should be used if % the begin and end command are at different boxing levels. E.g. % \begin{verbatim} % \@hangfrom % { % \tagstructbegin{tag=H1}% % \tagmcbegin {tag=H1}% % #2 % } % {#3\tagpdfsuppressmarks{\tagmcend}\tagstructend}% % \end{verbatim} % \end{function} % % \subsection{Header and footer} % Header and footer are automatically tagged as artifact: % They are surrounded by an artifact-mc and inside tagging is stopped. % If some real content is in the header and % footer, tagging must be restarted there explicitly. % The behaviour can be changed with the following key. % The key accepts the values |true| (the default), % |false| which disables the header tagging code. This can be useful % if the page style is empty (it then avoids empty mc-chunks) or % if the head and foot should be tagged in some special way. % The last value, |pagination|, is like |true| but % additionally adds an artifact structure with an pagination attribute. % % \begin{function}{page/exclude-header-footer (setup-key)} % \begin{syntax} % |page/exclude-header-footer| = |true|\verb+|+|false|\verb+|+pagination % \end{syntax}% % \end{function} % \subsection{Link tagging} % Links need a special structure and cross reference system. % This is added through hooks of the l3pdfannot module and will work % automatically if tagging is activated. % % Links should (probably) have an alternative text in the Contents % key. It is unclear which text this should be and % how to get it. Currently the code simply adds the fix texts |url| and |ref|. % Another text can be added by changing the dictionary value: % \begin{verbatim} % \pdfannot_dict_put:nnn % { link/GoTo } % { Contents } % { (ref) } % \end{verbatim} % % \section{Socket support} % % \begin{function}{\tag_socket_use:n,\tag_socket_use:nnn,\UseTaggingSocket} % \begin{syntax} % \cs{tag_socket_use:n} \Arg{socket name}\\ % \cs{tag_socket_use:nn} \Arg{socket name} \Arg{socket argument}\\ % \cs{tag_socket_use:nnn} \Arg{socket name} \Arg{socket argument} \Arg{socket argument}\\ % \cs{tag_socket_use_expandable:n} \Arg{socket name}\\ % \cs{UseTaggingSocket} \Arg{socket name} \\ % \cs{UseTaggingSocket} \Arg{socket name} \Arg{socket argument}\\ % \cs{UseTaggingSocket} \Arg{socket name} \Arg{socket argument}\Arg{socket argument}\\ % \end{syntax} % \end{function} % % Given that we sometimes have to suspend tagging, it would be fairly % inefficient to put different plugs into these sockets whenever that % happens. We therefore offer \cs{UseTaggingSocket} which is like % \cs{UseSocket} except that is expects a socket starting with % \texttt{tagsupport/} but the socket name is specified without % this prefix, i.e., % \begin{quote} % \verb=\UseTaggingSocket{foo}= $\to$ % \verb=\UseSocket{tagsupport/foo}= % \end{quote}. % % Beside being slightly shorter, the big advantage is that this way % we can change \cs{UseTaggingSocket} to do nothing by switching a boolean % instead of changing the plugs of the tagging support sockets back and forth. % % Usually, these sockets have (beside the default plug defined for every socket) % one additional plug defined and directly assigned. This plug is used when % tagging is active. % There may be more plugs, e.g., tagging with special debugging or special behaviour % depending on the class or PDF version etc., but right now it is usually just on or off. % % When tagging is suspended they all have the same predefined behaviour: % The sockets with zero arguments do nothing. The sockets with one argument % gobble their argument. The sockets with two arguments % will drop their first argument and pass the second unchanged. % % It is possible to use the tagging support sockets with % \cs{UseSocket} directly, but in this case the socket remains active % if e.g. \cs{SuspendTagging} is in force. There may be reasons for doing % that but in general we expect to always use \cs{UseTaggingSocket}. % % For special cases like in some \cs{halign} contexts we need a fully expandable % version of the commend. For these cases, \cs{UseExpandableTaggingSocket} can be % used. To allow being expandable, it does not output any debugging information % if \cs{DebugSocketsOn} is in effect and therefore should be avoided whenever possible. % % The L3 programming layer versions \cs{tag_socket_use_expandable:n}, \cs{tag_socket_use:n}, and % \cs{tag_socket_use:nn}, \cs{tag_socket_use:nnn} % are slightly more efficient than % \cs{UseTaggingSocket} because they do not have to determine how % many arguments the socket takes when disabling it. % \end{documentation} % % \begin{implementation} % \section{User commands and extensions of document commands} % \begin{macrocode} %<@@=tag> %<*header> \ProvidesExplPackage {tagpdf-user} {2024-11-19} {0.99i} {tagpdf - user commands} % % \end{macrocode} % \section{Setup and preamble commands} % \begin{macro}{\tagpdfsetup} % \begin{macrocode} %\NewDocumentCommand \tagpdfsetup { m }{} %<*package> \RenewDocumentCommand \tagpdfsetup { m } { \keys_set:nn { @@ / setup } { #1 } } % % \end{macrocode} % \end{macro} % % \begin{macro}{\tag_tool:n,\tagtool} % This is a first definition of the tool command. % Currently it uses key-val, but this should be probably % be flattened to speed it up. % \begin{macrocode} %\cs_new_protected:Npn\tag_tool:n #1 {} %\cs_set_eq:NN\tagtool\tag_tool:n %<*package> \cs_set_protected:Npn\tag_tool:n #1 { \tag_if_active:T { \keys_set:nn {tag / tool}{#1} } } \cs_set_eq:NN\tagtool\tag_tool:n % % \end{macrocode} % \end{macro} % \section{Commands for the mc-chunks} % \begin{macro}{\tagmcbegin,\tagmcend,\tagmcuse } % \begin{macrocode} %<*base> \NewDocumentCommand \tagmcbegin { m } { \tag_mc_begin:n {#1} } \NewDocumentCommand \tagmcend { } { \tag_mc_end: } \NewDocumentCommand \tagmcuse { m } { \tag_mc_use:n {#1} } % % \end{macrocode} % \end{macro} % \begin{macro}{\tagmcifinTF} % This is a wrapper around |\tag_mc_if_in:| and tests if an mc is open or not. It is mostly of % importance for pdflatex as lualatex doesn't mind much if a mc tag is not % correctly closed. Unlike the expl3 command it is not expandable. % \begin{macrocode} %<*package> \NewDocumentCommand \tagmcifinTF { m m } { \tag_mc_if_in:TF { #1 } { #2 } } % % \end{macrocode} % \end{macro} % % \section{Commands for the structure} % \begin{macro}{\tagstructbegin,\tagstructend,\tagstructuse } % % These are structure related user commands. There are direct wrapper around the % expl3 variants. % \begin{macrocode} %<*base> \NewDocumentCommand \tagstructbegin { m } { \tag_struct_begin:n {#1} } \NewDocumentCommand \tagstructend { } { \tag_struct_end: } \NewDocumentCommand \tagstructuse { m } { \tag_struct_use:n {#1} } % % \end{macrocode} % \end{macro} % % \section{Socket support} % Until we can be sure that the kernel defines the commands we provide them before % redefining them: % The expandable version will only work correctly after the 2024-11-01 release. % \begin{macrocode} %<*base> \providecommand\tag_socket_use:n[1]{} \providecommand\tag_socket_use:nn[2]{} \providecommand\tag_socket_use:nnn[3]{} \providecommand\tag_socket_use_expandable:n[1]{} \providecommand\socket_use_expandable:nw [1] { \use:c { __socket_#1_plug_ \str_use:c { l__socket_#1_plug_str } :w } } \providecommand\UseTaggingSocket[1]{} \providecommand\UseExpandableTaggingSocket[1]{} % % \end{macrocode} % % \begin{macro}{\tag_socket_use:n,\tag_socket_use:nn,\tag_socket_use:nnn,\UseTaggingSocket, % \tag_socket_use_expandable:n,\UseExpandableTaggingSocket} % \begin{macrocode} %<*package> \cs_set_protected:Npn \tag_socket_use:n #1 { \bool_if:NT \l_@@_active_socket_bool { \socket_use:n {tagsupport/#1} } } % \end{macrocode} % % \begin{macrocode} \cs_set_protected:Npn \tag_socket_use:nn #1#2 { \bool_if:NT \l_@@_active_socket_bool { \socket_use:nn {tagsupport/#1} {#2} } } % \end{macrocode} % % \begin{macrocode} \cs_set_protected:Npn \tag_socket_use:nnn #1#2#3 { \bool_if:NTF \l_@@_active_socket_bool { \socket_use:nnn {tagsupport/#1} {#2} {#3} } { #3 } } % \end{macrocode} % \begin{macrocode} \cs_set:Npn \tag_socket_use_expandable:n #1 { \bool_if:NT \l_@@_active_socket_bool { \socket_use_expandable:n {tagsupport/#1} } } % \end{macrocode} % \begin{macrocode} \cs_set_protected:Npn \UseTaggingSocket #1 { \bool_if:NTF \l_@@_active_socket_bool { \socket_use:nw {tagsupport/#1} } { \int_case:nnF { \int_use:c { c__socket_tagsupport/#1_args_int } } { 0 \prg_do_nothing: 1 \use_none:n 2 \use_ii:nn % \end{macrocode} % We do not expect tagging sockets with more than one or two % arguments, so for now we only provide those. % \begin{macrocode} } \ERRORusetaggingsocket } } % \end{macrocode} % \begin{macrocode} \cs_set:Npn \UseExpandableTaggingSocket #1 { \bool_if:NTF \l_@@_active_socket_bool { \socket_use_expandable:nw {tagsupport/#1} } { \int_case:nnF { \int_use:c { c__socket_tagsupport/#1_args_int } } { 0 \prg_do_nothing: 1 \use_none:n 2 \use_ii:nn % \end{macrocode} % We do not expect tagging sockets with more than one or two % arguments, so for now we only provide those. % \begin{macrocode} } \ERRORusetaggingsocket } } % % \end{macrocode} % \end{macro} % % \section{Debugging} % \begin{macro}{\ShowTagging} % This is a generic command for various show commands. % It takes a keyval list, the various keys are implemented below. % \begin{macrocode} %<*package> \NewDocumentCommand\ShowTagging { m } { \keys_set:nn { @@ / show }{ #1} } % \end{macrocode} % \end{macro} % % \begin{macro}{mc-data (show-key)} % This key is (currently?) relevant for lua mode only. % It shows the data of all mc-chunks created so far. % It is accurate only after shipout, so typically should be issued after a % newpage. With the optional argument the minimal number can be set. % \begin{macrocode} \keys_define:nn { @@ / show } { mc-data .code:n = { \sys_if_engine_luatex:T { \lua_now:e{ltx.@@.trace.show_all_mc_data(#1,\@@_get_mc_abs_cnt:,0)} } } ,mc-data .default:n = 1 } % \end{macrocode} % \end{macro} % \begin{macro}{mc-current (show-key)} % This shows some info about the current mc-chunk. It works in generic and lua-mode. % \begin{macrocode} \keys_define:nn { @@ / show } { mc-current .code:n = { \bool_if:NTF \g_@@_mode_lua_bool { \sys_if_engine_luatex:T { \int_compare:nNnTF { -2147483647 } = { \lua_now:e { tex.print (tex.getattribute (luatexbase.attributes.g_@@_mc_cnt_attr)) } } { \lua_now:e { ltx.@@.trace.log ( "mc-current:~no~MC~open,~current~abscnt =\@@_get_mc_abs_cnt:" ,0 ) texio.write_nl("") } } { \lua_now:e { ltx.@@.trace.log ( "mc-current:~abscnt=\@@_get_mc_abs_cnt:==" .. tex.getattribute(luatexbase.attributes.g_@@_mc_cnt_attr) .. "~=>tag=" .. tostring (ltx.@@.func.get_tag_from (tex.getattribute (luatexbase.attributes.g_@@_mc_type_attr))) .. "=" .. tex.getattribute (luatexbase.attributes.g_@@_mc_type_attr) ,0 ) texio.write_nl("") } } } } { \msg_note:nn{ tag }{ mc-current } } } } % \end{macrocode} % \end{macro} % \begin{macro}{mc-marks (show-key)} % It maps the mc-marks into the sequences and then shows them. % This allows to inspect the first and last mc-Mark on a page. % It should only be used in the shipout (header/footer). % \begin{macrocode} \keys_define:nn { @@ / show } { mc-marks .choice: , mc-marks / show .code:n = { \@@_mc_get_marks: \@@_check_if_mc_in_galley:TF { \iow_term:n {Marks~from~this~page:~} } { \iow_term:n {Marks~from~a~previous~page:~} } \seq_show:N \l_@@_mc_firstmarks_seq \seq_show:N \l_@@_mc_botmarks_seq \@@_check_if_mc_tmb_missing:T { \iow_term:n {BDC~missing~on~this~page!} } \@@_check_if_mc_tme_missing:T { \iow_term:n {EMC~missing~on~this~page!} } }, mc-marks / use .code:n = { \@@_mc_get_marks: \@@_check_if_mc_in_galley:TF { Marks~from~this~page:~} { Marks~from~a~previous~page:~} \seq_use:Nn \l_@@_mc_firstmarks_seq {,~}\quad \seq_use:Nn \l_@@_mc_botmarks_seq {,~}\quad \@@_check_if_mc_tmb_missing:T { BDC~missing~ } \@@_check_if_mc_tme_missing:T { EMC~missing } }, mc-marks .default:n = show } % \end{macrocode} % \end{macro} % \begin{macro}{struct-stack (show-key)} % \begin{macrocode} \keys_define:nn { @@ / show } { struct-stack .choice: ,struct-stack / log .code:n = \seq_log:N \g_@@_struct_tag_stack_seq ,struct-stack / show .code:n = \seq_show:N \g_@@_struct_tag_stack_seq ,struct-stack .default:n = show } % % \end{macrocode} % \end{macro} % \begin{macro}{debug/structures (show-key)} % The following key is available only if the tagpdf-debug package is loaded % and shows all structures starting with the one with the number % given by the key. % \begin{macrocode} %<*debug> \keys_define:nn { @@ / show } { ,debug/structures .code:n = { \int_step_inline:nnn{#1}{\c@g_@@_struct_abs_int} { \msg_term:nneeee { tag/debug } { show-struct } { ##1 } { \prop_map_function:cN {g_@@_struct_debug_##1_prop} \msg_show_item_unbraced:nn } { } { } \msg_term:nneeee { tag/debug } { show-kids } { ##1 } { \seq_map_function:cN {g_@@_struct_debug_kids_##1_seq} \msg_show_item_unbraced:n } { } { } } } ,debug/structures .default:n = 1 } % % \end{macrocode} % \end{macro} % % \section{Commands to extend document commands} % The following commands and code parts are not core commands of tagpdf. % They either provide work-arounds for missing functionality elsewhere, % or do a first step to apply tagpdf commands to document commands. % This part should be regularly revisited to check if the code should go to a % better place or can be improved. % % \begin{macrocode} %<*package> % \end{macrocode} % \subsection{Document structure} % \begin{macro}{\g_@@_root_default_tl,activate (setup-key),activate/socket (setup-key)} % \begin{macrocode} \tl_new:N\g_@@_root_default_tl \tl_gset:Nn\g_@@_root_default_tl {Document} \hook_gput_code:nnn{begindocument}{tagpdf}{\tagstructbegin{tag=\g_@@_root_default_tl}} \hook_gput_code:nnn{tagpdf/finish/before}{tagpdf}{\tagstructend} \keys_define:nn { @@ / setup} { activate/socket .bool_set:N = \l_@@_active_socket_bool, activate .code:n = { \keys_set:nn { @@ / setup } { activate/mc,activate/tree,activate/struct,activate/socket } \tl_gset:Nn\g_@@_root_default_tl {#1} }, activate .default:n = Document } % \end{macrocode} % \end{macro} % % \subsection{Structure destinations} % Since TeXlive 2022 pdftex and luatex offer support for structure destinations % and the pdfmanagement has backend support for. We activate them if % structures are actually created. % Structure destinations are actually PDF 2.0 only but they don't harm in % older PDF and can improve html export. % \begin{macrocode} \AddToHook{begindocument/before} { \bool_lazy_and:nnT { \g_@@_active_struct_dest_bool } { \g_@@_active_struct_bool } { \tl_set:Nn \l_pdf_current_structure_destination_tl { {@@/struct}{\g_@@_struct_stack_current_tl }} \pdf_activate_indexed_structure_destination: } } % \end{macrocode} % \subsection{Fake space} % \begin{macro}{\pdffakespace} % We need a luatex variant for |\pdffakespace|. This should probably go into % the kernel at some time. We also provide a no-op version for dvi mode % \begin{macrocode} \sys_if_engine_luatex:T { \NewDocumentCommand\pdffakespace { } { \@@_fakespace: } } \providecommand\pdffakespace{} % \end{macrocode} % \end{macro} % % \subsection{Paratagging} % The following are some simple commands to enable/disable paratagging. % Probably one should add some checks if we are already in a paragraph. % % % \begin{macro}{ % \l_@@_para_bool, % \l_@@_para_flattened_bool, % \l_@@_para_show_bool, % \g_@@_para_begin_int, % \g_@@_para_end_int, % \g_@@_para_main_begin_int, % \g_@@_para_main_end_int, % \g_@@_para_main_struct_tl, % \l_@@_para_tag_default_tl, % \l_@@_para_tag_tl, % \l_@@_para_main_tag_tl, % \l_@@_para_attr_class_tl, % \l_@@_para_main_attr_class_tl, % } % At first some variables. % \begin{macrocode} % %\bool_new:N \l_@@_para_flattened_bool %\bool_new:N \l_@@_para_bool %<*package> \int_new:N \g_@@_para_begin_int \int_new:N \g_@@_para_end_int \int_new:N \g_@@_para_main_begin_int \int_new:N \g_@@_para_main_end_int % \end{macrocode} % this will hold the structure number of the current text-unit. % \begin{macrocode} \tl_new:N \g_@@_para_main_struct_tl \tl_gset:Nn \g_@@_para_main_struct_tl {1} \tl_new:N \l_@@_para_tag_default_tl \tl_set:Nn \l_@@_para_tag_default_tl { text } \tl_new:N \l_@@_para_tag_tl \tl_set:Nn \l_@@_para_tag_tl { \l_@@_para_tag_default_tl } \tl_new:N \l_@@_para_main_tag_tl \tl_set:Nn \l_@@_para_main_tag_tl {text-unit} % \end{macrocode} % this is perhaps already defined by the block code % \begin{macrocode} \tl_if_exist:NF \l_@@_para_attr_class_tl {\tl_new:N \l_@@_para_attr_class_tl } \tl_new:N \l_@@_para_main_attr_class_tl % \end{macrocode} % \end{macro} % \begin{macro} % { % \@@_gincr_para_main_begin_int:, % \@@_gincr_para_main_end_int:, % \@@_gincr_para_begin_int:, % \@@_gincr_para_end_int: % } % The global para counter should be set through commands so % that \cs{tag_stop:} can stop them. % \begin{macrocode} \cs_new_protected:Npn \@@_gincr_para_main_begin_int: { \int_gincr:N \g_@@_para_main_begin_int } \cs_new_protected:Npn \@@_gincr_para_begin_int: { \int_gincr:N \g_@@_para_begin_int } \cs_new_protected:Npn \@@_gincr_para_main_end_int: { \int_gincr:N \g_@@_para_main_end_int } \cs_new_protected:Npn \@@_gincr_para_end_int: { \int_gincr:N \g_@@_para_end_int } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_start_para_ints:,\@@_stop_para_ints:} % \begin{macrocode} \cs_new_protected:Npn \@@_start_para_ints: { \cs_set_protected:Npn \@@_gincr_para_main_begin_int: { \int_gincr:N \g_@@_para_main_begin_int } \cs_set_protected:Npn \@@_gincr_para_begin_int: { \int_gincr:N \g_@@_para_begin_int } \cs_set_protected:Npn \@@_gincr_para_main_end_int: { \int_gincr:N \g_@@_para_main_end_int } \cs_set_protected:Npn \@@_gincr_para_end_int: { \int_gincr:N \g_@@_para_end_int } } \cs_new_protected:Npn \@@_stop_para_ints: { \cs_set_eq:NN \@@_gincr_para_main_begin_int:\prg_do_nothing: \cs_set_eq:NN \@@_gincr_para_begin_int: \prg_do_nothing: \cs_set_eq:NN \@@_gincr_para_main_end_int: \prg_do_nothing: \cs_set_eq:NN \@@_gincr_para_end_int: \prg_do_nothing: } % \end{macrocode} % \end{macro} % % We want to be able to inspect the current para main structure, so % we need a command to store its structure number % \begin{macro}{\@@_para_main_store_struct:} % \begin{macrocode} \cs_new:Npn \@@_para_main_store_struct: { \tl_gset:Ne \g_@@_para_main_struct_tl {\int_use:N \c@g_@@_struct_abs_int } } % \end{macrocode} % \end{macro} % TEMPORARY FIX (2023-11-17). Until latex-lab is updated we must adapt a sec command: % \begin{macrocode} \AddToHook{package/latex-lab-testphase-sec/after} { \cs_set_protected:Npn \@kernel@tag@hangfrom #1 { \tagstructbegin{tag=\l_@@_para_tag_tl} \@@_gincr_para_begin_int: \tagstructbegin{tag=Lbl} \setbox\@tempboxa \hbox { \bool_lazy_and:nnT {\tag_if_active_p:} {\g_@@_mode_lua_bool} {\tagmcbegin{tag=Lbl}} {#1} } \tag_suspend:n{hangfrom} \hangindent \wd\@tempboxa\noindent \tag_resume:n{hangfrom} \tagmcbegin{}\box\@tempboxa\tagmcend\tagstructend\tagmcbegin{} } } % \end{macrocode} % and one temporary adaptions for the block module: % \begin{macrocode} \AddToHook{package/latex-lab-testphase-block/after} { \tl_if_exist:NT \l_tag_para_attr_class_tl { \tl_set:Nn \l_@@_para_attr_class_tl { \l_tag_para_attr_class_tl } } } % \end{macrocode} %\begin{macro} % { % para/tagging (setup-key), % para/tag (setup-key), % para/maintag (setup-key), % para/tagging (tool-key), % para/tag (tool-key), % para/maintag (tool-key), % para/flattened (tool-key), % unittag (deprecated), % para-flattened (deprecated), % paratagging (deprecated), % paratagging-show (deprecated), % paratag (deprecated), % } % These keys enable/disable locally paratagging. % Paragraphs are typically tagged with two structure: A main structure % around the whole paragraph, and inner structures around the various chunks. % Debugging can be % activated locally with |debug/show=para|, this can affect the typesetting % as the small numbers % are boxes and they have a (small) height. Debugging can be deactivated % with |debug/show=paraOff| % The |para/tag| key sets the tag used by % the inner structure, |para/maintag| the tag of the outer structure, % both can also be changed with |\tag_tool:n| % \begin{macrocode} \keys_define:nn { @@ / setup } { para/tagging .bool_set:N = \l_@@_para_bool, debug/show/para .code:n = {\bool_set_true:N \l_@@_para_show_bool}, debug/show/paraOff .code:n = {\bool_set_false:N \l_@@_para_show_bool}, para/tag .tl_set:N = \l_@@_para_tag_tl, para/maintag .tl_set:N = \l_@@_para_main_tag_tl, para/flattened .bool_set:N = \l_@@_para_flattened_bool } \keys_define:nn { tag / tool} { para/tagging .bool_set:N = \l_@@_para_bool, para/tag .tl_set:N = \l_@@_para_tag_tl, para/maintag .tl_set:N = \l_@@_para_main_tag_tl, para/flattened .bool_set:N = \l_@@_para_flattened_bool } % \end{macrocode} % the deprecated names % \begin{macrocode} \keys_define:nn { @@ / setup } { paratagging .bool_set:N = \l_@@_para_bool, paratagging-show .bool_set:N = \l_@@_para_show_bool, paratag .tl_set:N = \l_@@_para_tag_tl } \keys_define:nn { tag / tool} { para .bool_set:N = \l_@@_para_bool, paratag .tl_set:N = \l_@@_para_tag_tl, unittag .tl_set:N = \l_@@_para_main_tag_tl, para-flattened .bool_set:N = \l_@@_para_flattened_bool } % \end{macrocode} % \end{macro} % Helper command for debugging: % \begin{macrocode} \cs_new_protected:Npn \@@_check_para_begin_show:nn #1 #2 %#1 color, #2 prefix { \bool_if:NT \l_@@_para_show_bool { \tag_mc_begin:n{artifact} \llap{\color_select:n{#1}\tiny#2\int_use:N\g_@@_para_begin_int\ } \tag_mc_end: } } \cs_new_protected:Npn \@@_check_para_end_show:nn #1 #2 %#1 color, #2 prefix { \bool_if:NT \l_@@_para_show_bool { \tag_mc_begin:n{artifact} \rlap{\color_select:n{#1}\tiny\ #2\int_use:N\g_@@_para_end_int} \tag_mc_end: } } % \end{macrocode} % % The para/begin and para/end code. % We have two variants here: a simpler one, which must be used % if the block code is not used (and so probably will disappear % at some time) and a more sophisticated one that must % be used if the block code is used. % It is possible that we will need more variants, so % we setup a socket so that the code can be easily switched. % This code should move into lttagging, so we add a test for % the transition. % \begin{macrocode} \str_if_exist:cF { l__socket_tagsupport/para/begin_plug_str } { \socket_new:nn {tagsupport/para/begin}{0} \socket_new:nn {tagsupport/para/end}{0} \socket_new_plug:nnn{tagsupport/para/begin}{plain} { \bool_if:NT \l_@@_para_bool { \bool_if:NF \l_@@_para_flattened_bool { \@@_gincr_para_main_begin_int: \tag_struct_begin:n { tag=\l_@@_para_main_tag_tl, } \@@_para_main_store_struct: } \@@_gincr_para_begin_int: \tag_struct_begin:n {tag=\l_@@_para_tag_tl} \@@_check_para_begin_show:nn {green}{} \tag_mc_begin:n {} } } \socket_new_plug:nnn{tagsupport/para/begin}{block} { \bool_if:NT \l_@@_para_bool { \legacy_if:nF { @inlabel } { \@@_check_typeout_v:n {==>~ @endpe = \legacy_if:nTF { @endpe }{true}{false} \on@line } \legacy_if:nF { @endpe } { \bool_if:NF \l_@@_para_flattened_bool { \@@_gincr_para_main_begin_int: \tag_struct_begin:n { tag=\l_@@_para_main_tag_tl, attribute-class=\l_@@_para_main_attr_class_tl, } \@@_para_main_store_struct: } } \@@_gincr_para_begin_int: \@@_check_typeout_v:n {==>~increment~ P \on@line } \tag_struct_begin:n { tag=\l_@@_para_tag_tl ,attribute-class=\l_@@_para_attr_class_tl } \@@_check_para_begin_show:nn {green}{\PARALABEL} \tag_mc_begin:n {} } } } % \end{macrocode} % there was no real difference between the original and % in the block variant, only a debug message. We therefore % define only a plain variant. % \begin{macrocode} \socket_new_plug:nnn{tagsupport/para/end}{plain} { \bool_if:NT \l_@@_para_bool { \@@_gincr_para_end_int: \@@_check_typeout_v:n {==>~increment~ /P \on@line } \tag_mc_end: \@@_check_para_end_show:nn {red}{} \tag_struct_end: \bool_if:NF \l_@@_para_flattened_bool { \@@_gincr_para_main_end_int: \tag_struct_end: } } } } % \end{macrocode} % By default we assign the plain plug: % \begin{macrocode} \socket_assign_plug:nn { tagsupport/para/begin}{plain} \socket_assign_plug:nn { tagsupport/para/end}{plain} % \end{macrocode} % And use the sockets in the hooks. Once tagging sockets exist, this % can be adapted. % \begin{macrocode} \AddToHook{para/begin}{ \socket_use:n { tagsupport/para/begin } } \AddToHook{para/end} { \socket_use:n { tagsupport/para/end } } % \end{macrocode} % % If the block code is loaded we must ensure that it doesn't overwrite % the hook again. And we must reassign the para/begin plug. % This can go once the block code no longer tries to adapt the hooks. % \begin{macrocode} \AddToHook{package/latex-lab-testphase-block/after} { \RemoveFromHook{para/begin}[tagpdf] \RemoveFromHook{para/end}[latex-lab-testphase-block] \AddToHook{para/begin}[tagpdf] { \socket_use:n { tagsupport/para/begin } } \AddToHook{para/end}[tagpdf] { \socket_use:n { tagsupport/para/end } } \socket_assign_plug:nn { tagsupport/para/begin}{block} } % \end{macrocode} % % We check the para count at the end. If tagging is not active it is not a error, % but we issue a warning as it perhaps indicates that the testphase code didn't guard % everything correctly. % \begin{macrocode} \AddToHook{enddocument/info} { \tag_if_active:F { \msg_redirect_name:nnn { tag } { para-hook-count-wrong } { warning } } \int_compare:nNnF {\g_@@_para_main_begin_int}={\g_@@_para_main_end_int} { \msg_error:nneee {tag} {para-hook-count-wrong} {\int_use:N\g_@@_para_main_begin_int} {\int_use:N\g_@@_para_main_end_int} {text-unit} } \int_compare:nNnF {\g_@@_para_begin_int}={\g_@@_para_end_int} { \msg_error:nneee {tag} {para-hook-count-wrong} {\int_use:N\g_@@_para_begin_int} {\int_use:N\g_@@_para_end_int} {text} } } % \end{macrocode} % We need at least the new-or-1 code. % In generic mode we also must insert the code to finish the MC-chunks % \begin{macrocode} \@ifpackageloaded{footmisc} {\PackageWarning{tagpdf}{tagpdf~has~been~loaded~too~late!}} % {\RequirePackage{latex-lab-testphase-new-or-1}} \AddToHook{begindocument/before} { \providecommand\@kernel@tagsupport@@@@makecol{} \providecommand\@kernel@before@cclv{} \bool_if:NF \g_@@_mode_lua_bool { \cs_if_exist:NT \@kernel@before@footins { \tl_put_right:Nn \@kernel@before@footins { \tag_mc_add_missing_to_stream:Nn \footins {footnote} } \tl_put_right:Nn \@kernel@before@cclv { \@@_check_typeout_v:n {====>~In~\token_to_str:N \@makecol\c_space_tl\the\c@page} \tag_mc_add_missing_to_stream:Nn \@cclv {main} } \tl_put_right:Nn \@kernel@tagsupport@@@@makecol { \@@_check_typeout_v:n {====>~In~\token_to_str:N \@makecol\c_space_tl\the\c@page} \tag_mc_add_missing_to_stream:Nn \@outputbox {main} } \tl_if_exist:NT \@mult@ptagging@hook { \tl_put_right:Nn \@mult@ptagging@hook { \@@_check_typeout_v:n {====>~In~\string\page@sofar} \process@cols\mult@firstbox { \tag_mc_add_missing_to_stream:Nn \count@ {multicol} } \tag_mc_add_missing_to_stream:Nn \mult@rightbox {multicol} } } } } } % % \end{macrocode} % \begin{macro} % { % \tagpdfparaOn, % \tagpdfparaOff % } % This two command switch para mode on and off. |\tagpdfsetup| could be used % too but is longer. An alternative is |\tag_tool:n{para/tagging=false}| % \begin{macrocode} %\newcommand\tagpdfparaOn {} %\newcommand\tagpdfparaOff{} %<*package> \renewcommand\tagpdfparaOn {\bool_set_true:N \l_@@_para_bool} \renewcommand\tagpdfparaOff{\bool_set_false:N \l_@@_para_bool} % \end{macrocode} % \end{macro} % % \begin{macro} % { % \tagpdfsuppressmarks % } % This command allows to suppress the creation of the marks. It takes an argument % which should normally be one of the mc-commands, puts a group around it and % suppress the marks creation in this group. This command should be used if % the begin and end command are at different boxing levels. E.g. % \begin{verbatim} % \@hangfrom % { % \tagstructbegin{tag=H1}% % \tagmcbegin {tag=H1}% % #2 % } % {#3\tagpdfsuppressmarks{\tagmcend}\tagstructend}% % \end{verbatim} % \begin{macrocode} \NewDocumentCommand\tagpdfsuppressmarks{m} {{\use:c{@@_mc_disable_marks:} #1}} % \end{macrocode} % \end{macro} % \subsection{Language support} % % With the following key the lang variable is set. All structures in the current group will % then set this lang variable. % \begin{macro}{test/lang (setup-key)} % \begin{macrocode} \keys_define:nn { @@ / setup } { text / lang .tl_set:N = \l_@@_struct_lang_tl } % \end{macrocode} % \end{macro} % \subsection{Header and footer} % Header and footer should normally be tagged as artifacts. The following code % requires the new hooks. % For now we allow to disable this function, but probably the code should always there at % the end. % TODO check if Pagination should be changeable. % \begin{macrocode} \cs_new_protected:Npn\@@_hook_kernel_before_head:{} \cs_new_protected:Npn\@@_hook_kernel_after_head:{} \cs_new_protected:Npn\@@_hook_kernel_before_foot:{} \cs_new_protected:Npn\@@_hook_kernel_after_foot:{} \AddToHook{begindocument} { \cs_if_exist:NT \@kernel@before@head { \tl_put_right:Nn \@kernel@before@head {\@@_hook_kernel_before_head:} \tl_put_left:Nn \@kernel@after@head {\@@_hook_kernel_after_head:} \tl_put_right:Nn \@kernel@before@foot {\@@_hook_kernel_before_foot:} \tl_put_left:Nn \@kernel@after@foot {\@@_hook_kernel_after_foot:} } } \bool_new:N \g_@@_saved_in_mc_bool \cs_new_protected:Npn \@@_exclude_headfoot_begin: { \bool_set_false:N \l_@@_para_bool \bool_if:NTF \g_@@_mode_lua_bool { \tag_mc_end_push: } { \bool_gset_eq:NN \g_@@_saved_in_mc_bool \g_@@_in_mc_bool \bool_gset_false:N \g_@@_in_mc_bool } \tag_mc_begin:n {artifact} \tag_suspend:n{headfoot} } \cs_new_protected:Npn \@@_exclude_headfoot_end: { \tag_resume:n{headfoot} \tag_mc_end: \bool_if:NTF \g_@@_mode_lua_bool { \tag_mc_begin_pop:n{} } { \bool_gset_eq:NN \g_@@_in_mc_bool\g_@@_saved_in_mc_bool } } % \end{macrocode} % This version allows to use an Artifact structure % \begin{macrocode} \@@_attr_new_entry:nn {@@/attr/pagination}{/O/Artifact/Type/Pagination} \cs_new_protected:Npn \@@_exclude_struct_headfoot_begin:n #1 { \bool_set_false:N \l_@@_para_bool \bool_if:NTF \g_@@_mode_lua_bool { \tag_mc_end_push: } { \bool_gset_eq:NN \g_@@_saved_in_mc_bool \g_@@_in_mc_bool \bool_gset_false:N \g_@@_in_mc_bool } \tag_struct_begin:n{tag=Artifact,attribute-class=@@/attr/#1} \tag_mc_begin:n {artifact=#1} \tag_suspend:n{headfoot} } \cs_new_protected:Npn \@@_exclude_struct_headfoot_end: { \tag_resume:n{headfoot} \tag_mc_end: \tag_struct_end: \bool_if:NTF \g_@@_mode_lua_bool { \tag_mc_begin_pop:n{} } { \bool_gset_eq:NN \g_@@_in_mc_bool\g_@@_saved_in_mc_bool } } % \end{macrocode} % And now the keys % \begin{macro}{page/exclude-header-footer (setup-key),exclude-header-footer (deprecated)} % \begin{macrocode} \keys_define:nn { @@ / setup } { page/exclude-header-footer .choice:, page/exclude-header-footer / true .code:n = { \cs_set_eq:NN \@@_hook_kernel_before_head: \@@_exclude_headfoot_begin: \cs_set_eq:NN \@@_hook_kernel_before_foot: \@@_exclude_headfoot_begin: \cs_set_eq:NN \@@_hook_kernel_after_head: \@@_exclude_headfoot_end: \cs_set_eq:NN \@@_hook_kernel_after_foot: \@@_exclude_headfoot_end: }, page/exclude-header-footer / pagination .code:n = { \cs_set:Nn \@@_hook_kernel_before_head: { \@@_exclude_struct_headfoot_begin:n {pagination} } \cs_set:Nn \@@_hook_kernel_before_foot: { \@@_exclude_struct_headfoot_begin:n {pagination} } \cs_set_eq:NN \@@_hook_kernel_after_head: \@@_exclude_struct_headfoot_end: \cs_set_eq:NN \@@_hook_kernel_after_foot: \@@_exclude_struct_headfoot_end: }, page/exclude-header-footer / false .code:n = { \cs_set_eq:NN \@@_hook_kernel_before_head: \prg_do_nothing: \cs_set_eq:NN \@@_hook_kernel_before_foot: \prg_do_nothing: \cs_set_eq:NN \@@_hook_kernel_after_head: \prg_do_nothing: \cs_set_eq:NN \@@_hook_kernel_after_foot: \prg_do_nothing: }, page/exclude-header-footer .default:n = true, page/exclude-header-footer .initial:n = true, % \end{macrocode} % deprecated name % \begin{macrocode} exclude-header-footer .meta:n = { page/exclude-header-footer = {#1} } } % \end{macrocode} % \end{macro} % \subsection{Links} % We need to close and reopen mc-chunks around links. % Currently we handle URI and GoTo (internal) links. % Links should have an alternative text in the Contents % key. It is unclear which text this should be and % how to get it. % \begin{macrocode} \hook_gput_code:nnn {pdfannot/link/URI/before} {tagpdf} { \tag_mc_end_push: \tag_struct_begin:n { tag=Link } \tag_mc_begin:n { tag=Link } \pdfannot_dict_put:nne { link/URI } { StructParent } { \tag_struct_parent_int: } } \hook_gput_code:nnn {pdfannot/link/URI/after} {tagpdf} { \tag_struct_insert_annot:ee {\pdfannot_link_ref_last:}{\tag_struct_parent_int:} \tag_mc_end: \tag_struct_end: \tag_mc_begin_pop:n{} } \hook_gput_code:nnn {pdfannot/link/GoTo/before} {tagpdf} { \tag_mc_end_push: \tag_struct_begin:n{tag=Link} \tag_mc_begin:n{tag=Link} \pdfannot_dict_put:nne { link/GoTo } { StructParent } { \tag_struct_parent_int: } } \hook_gput_code:nnn {pdfannot/link/GoTo/after} {tagpdf} { \tag_struct_insert_annot:ee {\pdfannot_link_ref_last:}{\tag_struct_parent_int:} \tag_mc_end: \tag_struct_end: \tag_mc_begin_pop:n{} } % "alternative descriptions " for PAX3. How to get better text here?? \pdfannot_dict_put:nnn { link/URI } { Contents } { (url) } \pdfannot_dict_put:nnn { link/GoTo } { Contents } { (ref) } % \end{macrocode} % % \end{implementation} % \PrintIndex