diff options
Diffstat (limited to 'macros/latex/contrib/tagpdf/tagpdf-tree.dtx')
-rw-r--r-- | macros/latex/contrib/tagpdf/tagpdf-tree.dtx | 343 |
1 files changed, 238 insertions, 105 deletions
diff --git a/macros/latex/contrib/tagpdf/tagpdf-tree.dtx b/macros/latex/contrib/tagpdf/tagpdf-tree.dtx index c3e5d6d71b..6a1d3e9562 100644 --- a/macros/latex/contrib/tagpdf/tagpdf-tree.dtx +++ b/macros/latex/contrib/tagpdf/tagpdf-tree.dtx @@ -22,80 +22,144 @@ % % for those people who are interested. % +%<*driver> +\RequirePackage{pdfmanagement-testphase} +\DeclareDocumentMetadata{} +\makeatletter +\declare@file@substitution{doc.sty}{doc-v3beta.sty} +\makeatother +\documentclass{l3doc} +\usepackage{array,booktabs,caption} +\hypersetup{pdfauthor=Ulrike Fischer, + pdftitle=tagpdf-tree module (tagpdf)} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> % \fi +% \title{^^A +% The \pkg{tagpdf-tree} module\\ Commands trees and main dictionaries ^^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.9, released 2021-06-29} +% \maketitle +% \begin{implementation} % \begin{macrocode} %<@@=tag> -%<*tree> -\ProvidesExplPackage {tagpdf-tree-code} {2021-06-14} {0.82} +%<*header> +\ProvidesExplPackage {tagpdf-tree-code} {2021-06-29} {0.9} {part of tagpdf - code related to writing trees and dictionaries to the pdf} - -%this does the actual finishing: -%Is \AddToHook{enddocument/end} the best places?? +%</header> +% \end{macrocode} +% \section{Trees, pdfmanagement and finalization code} +% +% The code to finish the structure is in a hook. +% This will perhaps at the end be a kernel hook. +% TODO check right place for the code +% The pdfmanagement code is the kernel hook after +% shipout/lastpage so all code affecting it should be before. +% Objects can be written later, at least in pdf mode. +% \begin{macrocode} +%<*package> \hook_gput_code:nnn{begindocument}{tagpdf} { \bool_if:NT \g_@@_active_tree_bool { \sys_if_output_pdf:TF { - \AddToHook{enddocument/end} { \tag_finish_structure: } + \AddToHook{enddocument/end} { \@@_finish_structure: } } { - \AddToHook{shipout/lastpage} { \tag_finish_structure: } + \AddToHook{shipout/lastpage} { \@@_finish_structure: } } } } - - -% the StructTreeRoot -% we better get the object number in any case: -% deprecated -%\@@_pdfreserveobjnum:N \l_tmpa_tl -%\tl_const:Nx \c_@@_tree_obj_structtreeroot_tl { \l_tmpa_tl } - -%new +% \end{macrocode} +% \subsection{Catalog: MarkInfo and StructTreeRoot} +% The StructTreeRoot and the MarkInfo entry must be added to the catalog. +% We do it late so that we can win, but before the pdfmanagement hook. +% \begin{macro}{@@/struct/0} +% This is the object for the root object, the StructTreeRoot +% \begin{macrocode} \pdf_object_new:nn { @@/struct/0 }{ dict } - - %need to think about the best place ... -\hook_gput_code:nnn{begindocument}{tagpdf} +% \end{macrocode} +% \end{macro} +% \begin{macrocode} +\hook_gput_code:nnn{shipout/lastpage}{tagpdf} { - \bool_if:NT \g_@@_active_struct_bool + \bool_if:NT \g_@@_active_tree_bool { + \pdfmanagement_add:nnn { Catalog / MarkInfo } { Marked } { true } \pdfmanagement_add:nnx { Catalog } { StructTreeRoot } { \pdf_object_ref:n { @@/struct/0 } } } } -\cs_new_protected:Nn \@@_tree_write_structtreeroot: +% \end{macrocode} +% +% \subsection{Writing structure elements} +% The following commands are needed to write out the structure. +% \begin{macro}{\@@_tree_write_structtreeroot:} +% This writes out the root object. +% \begin{macrocode} +\cs_new_protected:Npn \@@_tree_write_structtreeroot: { - \@@_struct_write_obj:n { 0 } + \@@_prop_gput:cnx + { g_@@_struct_0_prop } + { ParentTree } + { \pdf_object_ref:n { @@/tree/parenttree } } + \@@_prop_gput:cnx + { g_@@_struct_0_prop } + { RoleMap } + { \pdf_object_ref:n { @@/tree/rolemap } } + \@@_struct_write_obj:n { 0 } } - -\cs_new_protected:Nn \@@_tree_write_structelements: +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_tree_write_structelements:} +% This writes out the other struct elems, the absolute number is in the counter +% \begin{macrocode} +\cs_new_protected:Npn \@@_tree_write_structelements: { \int_step_inline:nnnn {1}{1}{\c@g_@@_struct_abs_int} { - %\prop_show:c {g_@@_struct_##1_prop} - \@@_struct_write_obj:n { ##1 } %write the object + \@@_struct_write_obj:n { ##1 } } } - - -%the ParentTree -% deprecated -%\@@_pdfreserveobjnum:N \l_tmpa_tl -%\tl_const:Nx \c_@@_tree_obj_parenttree_tl { \l_tmpa_tl } - -%new +% \end{macrocode} +% \end{macro} +% +% \subsection{ParentTree} +% \begin{macro}{@@/tree/parenttree} +% The object which will hold the parenttree +% \begin{macrocode} \pdf_object_new:nn { @@/tree/parenttree }{ dict } - -% we have two sets of entries in the parent tree: -% page streams and real objects. -% The numbers must be distinct and ordered -% So we rely on abspage and put the real objects at the end -% I use a counter to have a chance to get the correct number -% if code is process twice. +% \end{macrocode} +% \end{macro} +% The ParentTree maps numbers to objects or (if the number represents a page) +% to arrays of objects. The numbers refer to two dictinct types of entries: +% page streams and real objects like annotations. +% The numbers must be distinct and ordered. +% So we rely on abspage for the pages and put the real objects at the end. +% We use a counter to have a chance to get the correct number +% if code is processed twice. +% +% \begin{macro}{\c@g_@@_parenttree_obj_int} +% This is a counter for the real objects. It starts at the absolute last page +% value. It relies on l3ref. +% \begin{macrocode} \newcounter { g_@@_parenttree_obj_int } \hook_gput_code:nnn{begindocument}{tagpdf} { @@ -103,39 +167,59 @@ \c@g_@@_parenttree_obj_int { \@@_ref_value_lastpage:nn{abspage}{100} } } - -% we need to record the obj references: - +% \end{macrocode} +% \end{macro} +% We store the number/object references in a tl-var. If more structure is needed +% one could switch to a seq. +% \begin{variable}{ \g_@@_parenttree_objr_tl } +% \begin{macrocode} \tl_new:N \g_@@_parenttree_objr_tl - -\cs_new_protected:Nn \@@_parenttree_add_objr:nn %#1 Structparent number, #2 objref +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{ \@@_parenttree_add_objr:nn } +% This command stores a StructParent number and a objref into the tl var. +% This is only for objects like annotations, pages are handled elsewhere. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parenttree_add_objr:nn #1 #2 %#1 StructParent number, #2 objref { \tl_gput_right:Nx \g_@@_parenttree_objr_tl { #1 \c_space_tl #2 ^^J } } - +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\l_@@_parenttree_content_tl} +% A tl-var which will get the page related parenttree content. +% \begin{macrocode} \tl_new:N \l_@@_parenttree_content_tl +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\@@_tree_fill_parenttree:} +% This is the main command to assemble the page related entries of the parent tree. +% It wanders through the pages and the mcid numbers and collects all mcid of one page. +% \begin{macrocode} -\cs_new_protected:Nn \@@_tree_fill_parenttree: +\cs_new_protected:Npn \@@_tree_fill_parenttree: { \int_step_inline:nnnn{1}{1}{\@@_ref_value_lastpage:nn{abspage}{-1}} %not quite clear if labels are needed. See lua code - { %page ##1\par - \prop_clear:N \l_tmpa_prop + { %page ##1 + \prop_clear:N \l_@@_tmpa_prop \int_step_inline:nnnn{1}{1}{\@@_ref_value_lastpage:nn{tagmcabs}{-1}} { %mcid####1 - \int_compare:nT%F + \int_compare:nT {\@@_ref_value:enn{mcid-####1}{tagabspage}{-1}=##1} %mcid is on current page - {% yes\par + {% yes \prop_put:Nxx - \l_tmpa_prop + \l_@@_tmpa_prop {\@@_ref_value:enn{mcid-####1}{tagmcid}{-1}} {\prop_item:Nn \g_@@_mc_parenttree_prop {####1}} } } - %\prop_show:N \l_tmpa_prop \tl_put_right:Nx\l_@@_parenttree_content_tl { \int_eval:n {##1-1}\c_space_tl @@ -144,16 +228,18 @@ \int_step_inline:nnnn {0} {1} - { \prop_map_function:NN \l_tmpa_prop\@@_prop_count:nn -1 } + { \prop_count:N \l_@@_tmpa_prop -1 } { - \prop_get:NnNTF \l_tmpa_prop {####1} \l_tmpb_tl - {% page#1:mcid##1:\l_tmpb_tl :content + \prop_get:NnNTF \l_@@_tmpa_prop {####1} \l_@@_tmpa_tl + {% page#1:mcid##1:\l_@@_tmpa_tl :content \tl_put_right:Nx \l_@@_parenttree_content_tl { - \prop_item:cn { g_@@_struct_\l_tmpb_tl _prop } {objref} + \pdf_object_if_exist:eT { @@/struct/\l_@@_tmpa_tl } + { + \pdf_object_ref:e { @@/struct/\l_@@_tmpa_tl } + } \c_space_tl } - %\tl_show:N \l_@@_parenttree_content_tl } { \msg_warning:nn { tag } {tree-mcid-index-wrong} @@ -166,13 +252,18 @@ } } } - -%lua mode must/can do it differently -\cs_new_protected:Nn \@@_tree_lua_fill_parenttree: +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_tree_lua_fill_parenttree:} +% This is a special variant for luatex. +% lua mode must/can do it differently. +% \begin{macrocode} +\cs_new_protected:Npn \@@_tree_lua_fill_parenttree: { \tl_set:Nn \l_@@_parenttree_content_tl { - \directlua + \lua_now:e { ltx.@@.func.output_parenttree ( @@ -181,10 +272,14 @@ } } } - - - -\cs_new_protected:Nn \@@_tree_write_parenttree: +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_tree_write_parenttree:} +% This combines the two parts and writes out the object. +% TODO should the check for lua be moved into the backend code? +% \begin{macrocode} +\cs_new_protected:Npn \@@_tree_write_parenttree: { \bool_if:NTF \g_@@_mode_lua_bool { @@ -199,26 +294,44 @@ /Nums\c_space_tl [\l_@@_parenttree_content_tl] } } - -%the Rolemap tree - +% \end{macrocode} +% \end{macro} +% +% \subsection{Rolemap dictionary} +% The Rolemap dictionary describes relations between new tags and standard types. +% The main part here is handled in the role module, here we only define the +% command which writes it to the PDF. +% \begin{variable}{@@/tree/rolemap} +% At first we reserve again an object. +% \begin{macrocode} \pdf_object_new:nn { @@/tree/rolemap }{ dict } - -\cs_new_protected:Nn \@@_tree_write_rolemap: +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\@@_tree_write_rolemap:} +% This writes out the rolemap, basically it simply pushes out +% the dictionary which has been filled in the role module. +% \begin{macrocode} +\cs_new_protected:Npn \@@_tree_write_rolemap: { \pdf_object_write:nx { @@/tree/rolemap } { \pdfdict_use:n{g_@@_role/RoleMap_dict} } } - -%classmap, should only be written, if values has been used - -\cs_new_protected:Nn \@@_tree_write_classmap: +% \end{macrocode} +% \end{macro} +% +% \subsection{Classmap dictionary} +% Classmap and attributes are setup in the struct module, here is only the +% code to write it out. It should only done if values have been used. +% \begin{macro}{\@@_tree_write_classmap:} +% \begin{macrocode} +\cs_new_protected:Npn \@@_tree_write_classmap: { - \tl_gclear:N \g_@@_attr_class_content_tl + \tl_clear:N \l_@@_tmpa_tl \seq_gremove_duplicates:N \g_@@_attr_class_used_seq - \seq_set_map:NNn \l_tmpa_seq \g_@@_attr_class_used_seq + \seq_set_map:NNn \l_@@_tmpa_seq \g_@@_attr_class_used_seq { /##1\c_space_tl << @@ -227,30 +340,37 @@ {##1} >> } - \tl_gset:Nx \g_@@_attr_class_content_tl + \tl_set:Nx \l_@@_tmpa_tl { \seq_use:Nn - \l_tmpa_seq + \l_@@_tmpa_seq { \iow_newline: } } \tl_if_empty:NF - \g_@@_attr_class_content_tl + \l_@@_tmpa_tl { \pdf_object_new:nn { @@/tree/classmap }{ dict } - % \@@_pdfreserveobjnum:N \l_tmpa_tl - % \tl_const:Nx \c_@@_tree_obj_classmap_tl { \l_tmpa_tl } \pdf_object_write:nx { @@/tree/classmap } - { \g_@@_attr_class_content_tl } + { \l_@@_tmpa_tl } \@@_prop_gput:cnx { g_@@_struct_0_prop } { ClassMap } { \pdf_object_ref:n { @@/tree/classmap } } } } - -% namespaces are only relevant for pdf2.0 but we don't care +% \end{macrocode} +% \end{macro} +% \subsection{Namespaces} +% Namespaces are handle in the role module, here is the code to write them out. +% Namespaces are only relevant for pdf2.0 but we don't care, it doesn't harm. +% \begin{variable}{@@/tree/namespaces} +% \begin{macrocode} \pdf_object_new:nn{ @@/tree/namespaces }{array} +% \end{macrocode} +% \end{variable} +% \begin{macro}{\@@_tree_write_namespaces:} +% \begin{macrocode} \cs_new_protected:Npn \@@_tree_write_namespaces: { \prop_map_inline:Nn \g_@@_role_NS_prop @@ -274,34 +394,47 @@ \prop_map_tokens:Nn \g_@@_role_NS_prop{\use_ii:nn} } } - - -\cs_new_protected:Nn \tag_finish_structure: +% \end{macrocode} +% \end{macro} +% \subsection{Finishing the structure} +% This assembles the various parts. +% TODO (when tabular are done or if someone requests it): IDTree +% \begin{macro}{ \@@_finish_structure: } +% \begin{macrocode} +\cs_new_protected:Npn \@@_finish_structure: { - \@@_tree_write_parenttree: - \@@_tree_write_rolemap: - \@@_tree_write_classmap: - \@@_tree_write_namespaces: - \@@_tree_write_structelements: %this is rather slow!! - \@@_tree_write_structtreeroot: + \bool_if:NT\g_@@_active_tree_bool + { + \hook_use:n {tagpdf/finish/before} + \@@_tree_write_parenttree: + \@@_tree_write_rolemap: + \@@_tree_write_classmap: + \@@_tree_write_namespaces: + \@@_tree_write_structelements: %this is rather slow!! + \@@_tree_write_structtreeroot: + } } - - -%StructParents !!! we need a better hook for \g__pdf_BACKEND_thispage_shipout_tl!! -% No actually +% \end{macrocode} +% \end{macro} +% +% \subsection{StructParents entry for Page} +% We need to add to the Page resources the |StructParents| entry, this is simply the +% absolute page number. +% \begin{macrocode} \hook_gput_code:nnn{begindocument}{tagpdf} { \bool_if:NT\g_@@_active_tree_bool { \hook_gput_code:nnn{shipout/before} { tagpdf/structparents } { - \pdfmanagement_add:nnx - { Page } - {StructParents} - {\int_eval:n { \g_shipout_readonly_int}} + \pdfmanagement_add:nnx + { Page } + { StructParents } + { \int_eval:n { \g_shipout_readonly_int} } } } } - -%</tree> +%</package> % \end{macrocode} +% \end{implementation} +% \PrintIndex |