% \iffalse meta-comment % %% File: tagpdf.dtx % % Copyright (C) 2019-2020 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{macrocode} %<@@=tag> %<*package> \ProvidesExplPackage {tagpdf} {2021/02/23} {0.80} { 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 } } %\debug_on:n{check-declarations,check-expressions,deprecation} %map internal tag to package name \prop_if_exist:NT \g_msg_module_name_prop { \prop_gput:Nnn \g_msg_module_name_prop { tag }{ tagpdf } } % storing internal names to my name space: \cs_set_eq:NN \@@_tex_global:D \tex_global:D %%% package options \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 %%% some packages %references use l3ref-tmp \RequirePackage{l3ref-tmp} %================ % temporary code % =============== % faking the LastPage label: \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:} % a command to override the general default. See issue in ref \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 } } { % test if attribute exist at all? #3 } } } %============= % attributes %============= \ref_attribute_gset:nnnn {tagstruct} {0}{now} { \int_use:N \c@g_@@_struct_abs_int } \ref_attribute_gset:nnnn {tagstructobj} {}{now} { \exp_args:Ne \pdf_object_if_exist:nT {c__tag_struct_ \int_use:N \c@g__tag_struct_abs_int _obj} { \pdf_object_ref:e{c__tag_struct_ \int_use:N \c@g_@@_struct_abs_int _obj} } } % replace by abspage?? \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 } %============ % lists %============ \clist_const:Nn \c_@@_refmc_clist {tagabspage,tagmcabs,tagmcid} %mc is new!! \clist_const:Nn \c_@@_refstruct_clist {tagstruct,tagstructobj} %================= % command to set a label %================ \cs_generate_variant:Nn \ref_label:nn { nv } \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 } %============== % command to retrieve the value %=============== \cs_new:Npn \@@_ref_value:nnn #1 #2 #3 %#1 label, #2 attribute, #3 default { \ref_value:nnn {#1}{#2}{#3} } %=============== % the LastPage label has a different name, so we need a special command for now % ============= \cs_new:Npn \@@_ref_value_lastpage:nn #1 #2 { \ref_value:nnn {@@_LastPage}{#1}{#2} } % as ref doesn't expand label names we need variants: \cs_generate_variant:Nn \@@_ref_label:nn {en} \cs_generate_variant:Nn \@@_ref_value:nnn {enn} \RequirePackage{etoolbox} %\RequirePackage{l3pdf} %\RequirePackage{pdfescape} %check if needed %\RequirePackage{pdftexcmds}%check if needed (expandable commands!) %\RequirePackage{l3str-convert} \cs_generate_variant:Nn \str_set_convert:Nnnn {Nonn, Noon, Nnon } %%% tagpdfsetup, %%% TODO: checks need to be improved \int_new:N \l_@@_loglevel_int \tl_new:N \l_@@_tree_tabs_order_tl \keys_define:nn { @@ / setup } { 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}, check-tags .bool_gset:N = \g_@@_check_tags_bool, check-tags .initial:n = true, log .choice:, log / none .code:n = {\int_set:Nn \l_@@_loglevel_int { 0 }}, log / v .code:n = {\int_set:Nn \l_@@_loglevel_int { 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 }}, tagunmarked .bool_gset:N = \g_@@_tagunmarked_bool, tagunmarked .initial:n = true, tabsorder .choice:, tabsorder / row .code:n = %{\tl_set:Nn \l_@@_tree_tabs_order_tl {/Tabs/R}}, \pdfmanagement_add:nnn { Page } {Tabs}{/R}, tabsorder / column .code:n = %{\tl_set:Nn \l_@@_tree_tabs_order_tl {/Tabs/C}}, \pdfmanagement_add:nnn { Page } {Tabs}{/C}, tabsorder / structure .code:n = %{\tl_set:Nn \l_@@_tree_tabs_order_tl {/Tabs/S}}, \pdfmanagement_add:nnn { Page } {Tabs}{/S}, tabsorder / none .code:n = %{\tl_set:Nn \l_@@_tree_tabs_order_tl {}}, \pdfmanagement_remove:nn {Page} {Tabs}, tabsorder .initial:n = structure, uncompress .code:n = {\pdf_uncompress: }, } %need to think about the right place ... %\AtBeginDocument \hook_gput_code:nnn{begindocument}{tagpdf} { \bool_if:NT \g_@@_active_struct_bool { \pdfmanagement_add:nnn { Catalog / MarkInfo } { Marked } { true } } } % escape commands are a bit engine specific so have been moved to the backend files. % commands to escape strings so that they can be safely used in pdf % currently not much used. But will be needed later, when alt and actualtext are added. % we probably need the equivalent \pdfescapestring, \pdfescapename \pdfescapehex % the commands of pdfescape adds an additional layout to allow for babel shorthands %\cs_set_eq:NN \@@_pdf_escape_string:Nn \EdefEscapeString %\cs_set_eq:NN \@@_pdf_escape_name:Nn \EdefEscapeName %\cs_set_eq:NN \@@_pdf_escape_hex:Nn \EdefEscapeHex %\cs_set_eq:NN \@@_pdf_escape_string:n \EdefEscapeString %\cs_set_eq:NN \@@_pdf_escape_name:Nn \EdefEscapeName %escape command. They are backend specific as \pdf@escapehex would ignore most chars %outside the "pdflatex" range % a hook for later code and an absolute page counter % should be executed before counters are resetted. % is it used? \cs_new:Nn \@@_finish_page_hook: { } %\AtBeginShipout \hook_gput_code:nnn { shipout/before } { tagpdf/cnt } { \@@_finish_page_hook: %\int_gincr:N \g_@@_abspage_int } %testing the engines and loading the backend files \sys_if_engine_xetex:T { %lets try with xelatex ... %\PackageError { tagpdf } { xelatex~is~not~supported~-~aborting } {} %\tex_endinput:D \file_input:n {tagpdf-pdftex.def} } \sys_if_engine_luatex:T { \file_input:n {tagpdf-luatex.def} } \sys_if_engine_pdftex:T { \file_input:n {tagpdf-pdftex.def} } \sys_if_output_dvi:T { %\PackageError { tagpdf } { dvi~output~is~not~supported~-~aborting }{} %\tex_endinput:D } \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 } \cs_generate_variant:Nn \prop_gput:Nnn {Nxx} \cs_generate_variant:Nn \prop_put:Nnn {Nxx} \cs_generate_variant:Nn \pdffile_embed_stream:nnn {nxx,oxx} \cs_generate_variant:Nn \pdf_object_ref:n {e} % few temp tl \tl_new:N \g_@@_tmpa_tl \tl_new:N \l_@@_tmpa_tl \tl_new:N \l_@@_tmpb_tl \tl_new:N \l_@@_tmpc_tl \tl_new:N \l_@@_tmpd_tl \tl_new:N \l_@@_tmpe_tl \str_new:N \l_@@_tmpa_str % helper function to get the propcount. % use as \prop_map_function:NN PROP { \@@_prop_count:nn -1 } \cs_new:Nn\@@_prop_count:nn { + 1 } %% Loading the tagpdf sub packages \RequirePackage { tagpdf-checks-code } \RequirePackage { tagpdf-user } \RequirePackage { tagpdf-tree-code } \RequirePackage { tagpdf-roles-code } \RequirePackage { tagpdf-attr-code } % mc-code is split: \RequirePackage { tagpdf-mc-code-shared } \bool_if:NTF \g_@@_mode_lua_bool { \RequirePackage {tagpdf-mc-code-lua} } { \RequirePackage { tagpdf-mc-code-generic } % } \RequirePackage { tagpdf-struct-code } \RequirePackage { tagpdf-space-code } % % \end{macrocode} % % \begin{macrocode} %<*user> \ProvidesExplPackage {tagpdf-user} {2021/02/23} {0.80} {tagpdf - user commands} \NewDocumentCommand \tagpdfsetup { m } { \keys_set:nn { @@ / setup } { #1 } } \cs_set_eq:NN\tagpdfifluatexTF \sys_if_engine_luatex:TF \cs_set_eq:NN\tagpdfifluatexT \sys_if_engine_luatex:T \cs_set_eq:NN\tagpdfifpdftexT \sys_if_engine_pdftex:T %%% stop tagging \bool_new:N \l_@@_stop_bool \prg_new_conditional:Nnn \@@_if_stop: {p,T,F,TF} { \bool_if:NTF \l_@@_stop_bool { \prg_return_true: } { \prg_return_false: } } \prg_new_eq_conditional:NNn \tag_if_stop: \@@_if_stop: {p,T,F,TF} \cs_new_protected:Npn \@@_stop: { \bool_set_true:N \l_@@_stop_bool \cs_set_eq:NN \tag_mc_begin:n \use_none:n \cs_set_eq:NN \tag_mc_end: \prg_do_nothing: \cs_set_eq:NN \tag_mc_use:n \use_none:n \cs_set_eq:NN \tag_struct_begin:n \use_none:n \cs_set_eq:NN \tag_struct_end: \prg_do_nothing: \cs_set_eq:NN \tag_struct_use:n \use_none:n %deprecated \cs_set_eq:NN \uftag_mc_begin:n \tag_mc_begin:n \cs_set_eq:NN \uftag_mc_end: \tag_mc_end: \cs_set_eq:NN \uftag_mc_use:n \tag_mc_use:n \cs_set_eq:NN \uftag_struct_begin:n \tag_struct_begin:n \cs_set_eq:NN \uftag_struct_end: \tag_struct_end: \cs_set_eq:NN \uftag_struct_use:n \tag_struct_use:n } \cs_new_protected:Npn \tag_stop_group_begin: { \group_begin: \@@_stop: } \cs_set_eq:NN \tag_stop_group_end: \group_end: %%% a generic command to retrieve data \cs_new:Npn \tagpdfget #1 { \use:c {@@_get_data_#1: } } \cs_new:Npn \tag_get:n #1 { \use:c {@@_get_data_#1: } } %deprecated: \cs_new:Npn \uftag_get:n #1 { \use:c {@@_get_data_#1: } } %%%% mc related user commands \NewDocumentCommand \tagmcifinTF { m m } { \@@_mc_if_in:TF { #1 } { #2 } } \NewDocumentEnvironment{tagmcartifact}{m} { \tag_mc_artifact_group_begin:n { #1}\ignorespaces } { \tag_mc_artifact_group_end: } \NewDocumentCommand \tagmcbegin { m } { \tag_mc_begin:n {#1}\ignorespaces } \NewDocumentCommand \tagmcend { } { \unskip % this unskip appears to mess up some spacing; can we do this in another way? \tag_mc_end: } \NewDocumentCommand \tagmcuse { m } { \tag_mc_use:n {#1} } %%%% structure related commands \NewDocumentCommand \tagstructbegin { m } { \tag_struct_begin:n {#1} } \NewDocumentCommand \tagstructend { } { \tag_struct_end: } \NewDocumentCommand \tagstructuse { m } { \tag_struct_use:n {#1} } %%%% debug/show commands \NewDocumentCommand\showtagpdfmcdata { O {\@@_tag_get_mc_abs_cnt:} } { \bool_if:NT \g_@@_mode_lua_bool { \sys_if_engine_luatex:T { \directlua{ltx.@@.trace.show_all_mc_data(#1)} } } } \NewDocumentCommand\showtagpdfattributes { } { \bool_if:NT \g_@@_mode_lua_bool { \sys_if_engine_luatex:T { \directlua { ltx.@@.trace.log ( "showtagpdfattributes: MC=>abscnt=\@@_get_mc_abs_cnt: =>attr=\the\l_@@_mc_cnt_attr=>tag=" .. tostring(ltx.@@.func.get_tag_from (\the\l_@@_mc_type_attr)) .. "=\the\l_@@_mc_type_attr",0 ) } \ignorespaces } } } \sys_if_engine_luatex:T { \NewDocumentCommand\pdffakespace { } { \@@_fakespace: } } % % \end{macrocode} % % \begin{macrocode} %<*deprecated> % \uftag_get:n % \uftag_mc_if_in: % \uftag_mc_begin:n % \uftag_mc_end: % \uftag_mc_use:n % \uftag_struct_begin:n % \uftag_struct_end: % \uftag_finish_structure: % % \end{macrocode}