% \iffalse meta-comment % %% File: tagpdf.dtx % % Copyright (C) 2019-2021 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/u-fischer/tagpdf % % for those people who are interested. % % \fi % % \begin{implementation} % \section{Initialization and test if pdfmanagement is active.} % \begin{macrocode} %<@@=tag> %<*package> \ProvidesExplPackage {tagpdf} {2021-08-27} {0.92} { A package to experiment with pdf tagging } \bool_if:nF { \bool_lazy_and_p:nn {\cs_if_exist_p:N \pdfmanagement_if_active_p:} { \pdfmanagement_if_active_p: } } { %error for now, perhaps warning later. \PackageError{tagpdf} { PDF~resource~management~is~no~active!\MessageBreak tagpdf~will~no~work. } { Activate~it~with \MessageBreak \string\RequirePackage{pdfmanagement-testphase}\MessageBreak \string\DeclareDocumentMetadata{}\MessageBreak before~\string\documentclass } } % \end{macrocode} % We map the internal module name \enquote{tag} to \enquote{tagpdf} in messages. % \begin{macrocode} \prop_if_exist:NT \g_msg_module_name_prop { \prop_gput:Nnn \g_msg_module_name_prop { tag }{ tagpdf } } % \end{macrocode} %\section{Package options} % There are only two options to switch for luatex between generic and luamode, % TODO try to get rid of them. % \begin{macrocode} \bool_new:N\g_@@_mode_lua_bool \DeclareOption {luamode} { \sys_if_engine_luatex:T { \bool_gset_true:N \g_@@_mode_lua_bool } } \DeclareOption {genericmode}{ \bool_gset_false:N\g_@@_mode_lua_bool } \ExecuteOptions{luamode} \ProcessOptions % \end{macrocode} % \section{Packages} % We need the temporary version of l3ref until this is in the kernel. % \begin{macrocode} \RequirePackage{l3ref-tmp} % \end{macrocode} % % \section{Temporary code} % This is code which will be removed when proper support exists in LaTeX % \subsection{a LastPage label} % See also issue \#2 in Accessible-xref % \begin{macro}{\@@_lastpagelabel:} % \begin{macrocode} \cs_new_protected:Npn \@@_lastpagelabel: { \legacy_if:nT { @filesw } { \exp_args:NNnx \exp_args:NNx\iow_now:Nn \@auxout { \token_to_str:N \newlabeldata {@@_LastPage} { {abspage} { \int_use:N \g_shipout_readonly_int} {tagmcabs}{ \int_use:N \c@g_@@_MCID_abs_int } } } } } \AddToHook{enddocument/afterlastpage} {\@@_lastpagelabel:} % \end{macrocode} % \end{macro} % % \begin{macro}{\ref_value:nnn} % This allows to locally set a default value if the label or the attribute % doesn't exist. % See issue \#4 in Accessible-xref. % \begin{syntax} % \cs{ref_value:nnn}\Arg{label}\Arg{attribute}\Arg{Fallback default} % \end{syntax} % \begin{macrocode} \cs_if_exist:NF \ref_value:nnn { \cs_new:Npn \ref_value:nnn #1#2#3 { \exp_args:Nee \__ref_value:nnn { \tl_to_str:n {#1} } { \tl_to_str:n {#2} } {#3} } \cs_new:Npn \__ref_value:nnn #1#2#3 { \tl_if_exist:cTF { g__ref_label_ #1 _ #2 _tl } { \tl_use:c { g__ref_label_ #1 _ #2 _tl } } { #3 } } } % \end{macrocode} % \end{macro} % \section{Variables} % \begin{variable} % { % \l_@@_tmpa_tl, % \l_@@_tmpa_str, % \l_@@_tmpa_prop, % \l_@@_tmpa_seq, % \l_@@_tmpb_seq, % \l_@@_tmpa_clist, % \l_@@_tmpa_int, % \l_@@_tmpa_box, % \l_@@_tmpb_box % } % A few temporary variables % \begin{macrocode} \tl_new:N \l_@@_tmpa_tl \str_new:N \l_@@_tmpa_str \prop_new:N \l_@@_tmpa_prop \seq_new:N \l_@@_tmpa_seq \seq_new:N \l_@@_tmpb_seq \clist_new:N \l_@@_tmpa_clist \int_new:N \l_@@_tmpa_int \box_new:N \l_@@_tmpa_box \box_new:N \l_@@_tmpb_box % \end{macrocode} % \end{variable} % Attribute lists for the label command. We have a list for % mc-related labels, and one for structures. % \begin{macro}{\c_@@_refmc_clist,\c_@@_refstruct_clist} % \begin{macrocode} \clist_const:Nn \c_@@_refmc_clist {tagabspage,tagmcabs,tagmcid} \clist_const:Nn \c_@@_refstruct_clist {tagstruct,tagstructobj} % \end{macrocode} % \end{macro} % % \begin{variable}{\l_@@_loglevel_int} % This integer hold the log-level and so allows to % control the messages. % TODO: a list which log-level shows what is needed. The current behaviour % is quite ad-hoc. % \begin{macrocode} \int_new:N \l_@@_loglevel_int % \end{macrocode} % \end{variable} % % \begin{variable} % { % \g_@@_active_space_bool, % \g_@@_active_mc_bool, % \g_@@_active_tree_bool, % \g_@@_active_struct_bool % } % These booleans should help to control the global behaviour of tagpdf. % Ideally it should more or less do nothing if all are false. % The space-boolean controles the interword space code, % the mc-boolean activates \cs{tag_mc_begin:n}, % the tree-boolean activates writing the finish code and the pdfmanagement related % commands, the struct-boolean activates the storing of the structure data. % In a normal document all should be active, the split is only there for % debugging purpose. % Also we assume currently that they are set only at begin document. But % if some control passing over groups are needed they could be perhaps % used in a document too. % TODO: check if they are used everywhere as needed and as wanted. % \begin{macrocode} \bool_new:N \g_@@_active_space_bool \bool_new:N \g_@@_active_mc_bool \bool_new:N \g_@@_active_tree_bool \bool_new:N \g_@@_active_struct_bool % \end{macrocode} % \end{variable} % \begin{variable} % { % \l_@@_active_mc_bool, % \l_@@_active_struct_bool % } % These booleans should help to control the \emph{local} behaviour of tagpdf. % In some cases it could e.g. be necessary to stop tagging completely. % As local booleans they respect groups. % TODO: check if they are used everywhere as needed and as wanted. % \begin{macrocode} \bool_new:N \l_@@_active_mc_bool \bool_set_true:N \l_@@_active_mc_bool \bool_new:N \l_@@_active_struct_bool \bool_set_true:N \l_@@_active_struct_bool % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_tagunmarked_bool} % This boolean controls if the code should try to automatically % tag parts not in mc-chunk. It is currently only used in luamode. % It would be possible to used it in generic mode, but this would create % quite a lot empty artifact mc-chunks. % \begin{macrocode} \bool_new:N \g_@@_tagunmarked_bool % \end{macrocode} % \end{variable} % % \section{Variants of l3 commands} % \begin{macrocode} \prg_generate_conditional_variant:Nnn \pdf_object_if_exist:n {e}{T,F} \cs_generate_variant:Nn \pdf_object_ref:n {e} \cs_generate_variant:Nn \pdfannot_dict_put:nnn {nnx} \cs_generate_variant:Nn \pdffile_embed_stream:nnn {nxx,oxx} \cs_generate_variant:Nn \prop_gput:Nnn {Nxx} \cs_generate_variant:Nn \prop_put:Nnn {Nxx} \cs_generate_variant:Nn \ref_label:nn { nv } \cs_generate_variant:Nn \seq_set_split:Nnn{Nne} \cs_generate_variant:Nn \str_set_convert:Nnnn {Nonn, Noon, Nnon } % \end{macrocode} % \section{Setup label attributes} % \begin{macro}{tagstruct,tagstructobj,tagabspage,tagmcabs,tagmcid} % This are attributes used by the label/ref system. % With structures we store the structure number |tagstruct| % and the object reference |tagstructobj|. % The second is needed to be able to reference a structure which hasn't been % created yet. The alternative would be to create the object in such cases, % but then we would have to check the object existence all the time. % % With mc-chunks we store the absolute page number |tagabspage|, % the absolute id |tagmcabc|, and the id on the page |tagmcid|. % \begin{macrocode} \ref_attribute_gset:nnnn { tagstruct } {0} { now } { \int_use:N \c@g_@@_struct_abs_int } \ref_attribute_gset:nnnn { tagstructobj } {} { now } { \pdf_object_if_exist:eT {@@/struct/\int_use:N \c@g__tag_struct_abs_int} { \pdf_object_ref:e{@@/struct/\int_use:N \c@g_@@_struct_abs_int} } } \ref_attribute_gset:nnnn { tagabspage } {0} { shipout } { \int_use:N \g_shipout_readonly_int } \ref_attribute_gset:nnnn { tagmcabs } {0} { now } { \int_use:N \c@g_@@_MCID_abs_int } \ref_attribute_gset:nnnn {tagmcid } {0} { now } { \int_use:N \g_@@_MCID_tmp_bypage_int } % \end{macrocode} % \end{macro} % % \section{Label commands} % \begin{macro}{\@@_ref_label:nn} % A version of |\ref_label:nn| to set a label % which takes a keyword |mc| or |struct| to % call the relevant lists. % TODO: check if |\@bsphack| and |\@esphack| make sense here. % \begin{macrocode} \cs_new_protected:Npn \@@_ref_label:nn #1 #2 %#1 label, #2 name of list mc or struct { \@bsphack \ref_label:nv {#1}{c_@@_ref#2_clist} \@esphack } \cs_generate_variant:Nn \@@_ref_label:nn {en} % \end{macrocode} % \end{macro} % \begin{macro}{\@@_ref_value:nnn} % A local version to retrieve the value. It is a direct wrapper, % but to keep naming consistent \ldots. It uses the variant defined temporarly % above. % \begin{macrocode} \cs_new:Npn \@@_ref_value:nnn #1 #2 #3 %#1 label, #2 attribute, #3 default { \ref_value:nnn {#1}{#2}{#3} } \cs_generate_variant:Nn \@@_ref_value:nnn {enn} % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_ref_value_lastpage:nn} % A command to retrieve the lastpage label, this will be adapted when there % is a proper, kernel lastpage label. % \begin{macrocode} \cs_new:Npn \@@_ref_value_lastpage:nn #1 #2 { \ref_value:nnn {@@_LastPage}{#1}{#2} } % \end{macrocode} %\end{macro} %\section{Commands to fill seq and prop} % With most engines these are simply copies of the expl3 commands, % but luatex will overwrite them, to store the data also in lua tables. % \begin{macro} % { % \@@_prop_new:N % ,\@@_seq_new:N % ,\@@_prop_gput:Nnn % ,\@@_seq_gput_right:Nn % ,\@@_seq_item:cn % ,\@@_prop_item:cn % ,\@@_seq_show:N % ,\@@_prop_show:N % } % \begin{macrocode} \cs_set_eq:NN \@@_prop_new:N \prop_new:N \cs_set_eq:NN \@@_seq_new:N \seq_new:N \cs_set_eq:NN \@@_prop_gput:Nnn \prop_gput:Nnn \cs_set_eq:NN \@@_seq_gput_right:Nn \seq_gput_right:Nn \cs_set_eq:NN \@@_seq_item:cn \seq_item:cn \cs_set_eq:NN \@@_prop_item:cn \prop_item:cn \cs_set_eq:NN \@@_seq_show:N \seq_show:N \cs_set_eq:NN \@@_prop_show:N \prop_show:N \cs_generate_variant:Nn \@@_prop_gput:Nnn { Nxn , Nxx, Nnx , cnn, cxn, cnx, cno} \cs_generate_variant:Nn \@@_seq_gput_right:Nn { Nx , No, cn, cx } \cs_generate_variant:Nn \@@_prop_new:N { c } \cs_generate_variant:Nn \@@_seq_new:N { c } \cs_generate_variant:Nn \@@_seq_show:N { c } \cs_generate_variant:Nn \@@_prop_show:N { c } % \end{macrocode} % \end{macro} % % \section{General tagging commands} % \begin{macro}{ \tag_stop_group_begin:, \tag_stop_group_end: } % We need a command to stop tagging in some places. % This simply switches the two local booleans. % \begin{macrocode} \cs_new_protected:Npn \tag_stop_group_begin: { \group_begin: \bool_set_false:N \l_@@_active_struct_bool \bool_set_false:N \l_@@_active_mc_bool } \cs_set_eq:NN \tag_stop_group_end: \group_end: % \end{macrocode} % \end{macro} % % \section{Keys for tagpdfsetup} % TODO: the log-levels must be sorted % % \begin{macro} % { % activate-space, % activate-mc, % activate-tree, % activate-struct, % activate-all, % } % Keys to (globally) activate tagging. % |activate-space| activates the additional parsing needed for % interword spaces. It is not documented, the parsing is currently % implicitly activated by the known key |interwordspace|, as the code will % perhaps move to some other place, now that it is better separated. % \begin{macrocode} \keys_define:nn { @@ / setup } { activate-space .bool_gset:N = \g_@@_active_space_bool, activate-mc .bool_gset:N = \g_@@_active_mc_bool, activate-tree .bool_gset:N = \g_@@_active_tree_bool, activate-struct .bool_gset:N = \g_@@_active_struct_bool, activate-all .meta:n = {activate-mc,activate-tree,activate-struct}, % \end{macrocode} % \end{macro} % \begin{macro}{log} % The |log| takes currently the values |none|, |v|, |vv|, |vvv|, |all|. % The description of the log levels is in tagpdf-checks. % \begin{macrocode} log .choice:, log / none .code:n = {\int_set:Nn \l_@@_loglevel_int { 0 }}, log / v .code:n = { \int_set:Nn \l_@@_loglevel_int { 1 } \cs_set_protected:Nn \@@_check_typeout_v:n { \iow_term:x {##1} } }, log / vv .code:n = {\int_set:Nn \l_@@_loglevel_int { 2 }}, log / vvv .code:n = {\int_set:Nn \l_@@_loglevel_int { 3 }}, log / all .code:n = {\int_set:Nn \l_@@_loglevel_int { 10 }}, % \end{macrocode} % \end{macro} % \begin{macro}{tagunmarked} % This key allows to set if (in luamode) unmarked text should be % marked up as artifact. The initial value is true. % \begin{macrocode} tagunmarked .bool_gset:N = \g_@@_tagunmarked_bool, tagunmarked .initial:n = true, % \end{macrocode} % \end{macro} % \begin{macro}{tabsorder} % This sets the tabsorder one a page. The values are |row|, |column|, |structure| (default) % or |none|. Currently this is set more or less globally. More finer controll can be % added if needed. % \begin{macrocode} tabsorder .choice:, tabsorder / row .code:n = \pdfmanagement_add:nnn { Page } {Tabs}{/R}, tabsorder / column .code:n = \pdfmanagement_add:nnn { Page } {Tabs}{/C}, tabsorder / structure .code:n = \pdfmanagement_add:nnn { Page } {Tabs}{/S}, tabsorder / none .code:n = \pdfmanagement_remove:nn {Page} {Tabs}, tabsorder .initial:n = structure, uncompress .code:n = { \pdf_uncompress: }, } % \end{macrocode} % \end{macro} % \section{loading of engine/more dependent code} % \begin{macrocode} \sys_if_engine_luatex:T { \file_input:n {tagpdf-luatex.def} } % % \end{macrocode} % \begin{macrocode} %<*mcloading> \bool_if:NTF \g_@@_mode_lua_bool { \RequirePackage {tagpdf-mc-code-lua} } { \RequirePackage {tagpdf-mc-code-generic} % } % % \end{macrocode} % \end{implementation} % \PrintIndex