diff options
author | Karl Berry <karl@freefriends.org> | 2021-06-30 21:14:14 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-06-30 21:14:14 +0000 |
commit | c604c653358384d71cfbdad75147fe6b3ef448d3 (patch) | |
tree | aaa2eb4dac429b5bb9bdde1d867e88d75612e65d /Master/texmf-dist/source | |
parent | f52cb629a5dd055df1ae60054f89cc718a5ce58e (diff) |
tagpdf (30jun21)
git-svn-id: svn://tug.org/texlive/trunk@59769 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source')
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-backend.dtx | 1059 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-checks.dtx | 643 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-mc-generic.dtx | 389 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-mc-luacode.dtx | 490 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-mc-shared.dtx | 442 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-mc.dtx | 818 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-roles.dtx | 165 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-space.dtx | 130 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx | 1024 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-tree.dtx | 343 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-user.dtx | 585 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf.dtx | 865 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf.ins | 47 |
13 files changed, 4591 insertions, 2409 deletions
diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-backend.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-backend.dtx index 1010c79b7a5..f643e3dbdea 100644 --- a/Master/texmf-dist/source/latex/tagpdf/tagpdf-backend.dtx +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-backend.dtx @@ -22,31 +22,57 @@ % % 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-mc module (tagpdf)} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> % \fi +% \title{^^A +% The \pkg{tagpdf-luatex.def} \\ Driver for luatex ^^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> %<*luatex> -\ProvidesExplFile {tagpdf-luatex.def} {2021-06-14} {0.82} +\ProvidesExplFile {tagpdf-luatex.def} {2021-06-29} {0.9} {tagpdf~driver~for~luatex} - -\newattribute \l_@@_mc_type_attr %the value represent the type -\newattribute \l_@@_mc_cnt_attr %will hold the \c@g_@@_MCID_abs_int value - -\newattribute \g_@@_interwordspace_attr -\newattribute \g_@@_interwordfont_attr - -% The lua code -% ensure that 10pt font has been loaded: +% \end{macrocode} +% \section{Loading the lua} +% The space code requires that the fall back font has been loaded and initialized, +% so we force that first. But perhaps this could be done in the kernel. +% +% \begin{macrocode} { \fontencoding{TU}\fontfamily{lmr}\fontseries{m}\fontshape{n}\fontsize{10pt}{10pt}\selectfont } -\directlua { tagpdf=require('tagpdf.lua') } - -%%%% driver (lualatex) commands -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - +\lua_now:e { tagpdf=require('tagpdf.lua') } +% \end{macrocode} +% +% The following defines wrappers around prop and seq commands to store the +% data also in lua tables. % I probably want also lua tables % I put them in the ltx.@@.tables namespaces % The tables will be named like the variables but without backslash @@ -54,94 +80,80 @@ % ltx.@@.tables[string] % Old code, I'm not quite sure if this was a good idea. Now I have mix of table in % ltx.@@.tables and ltx.@@.mc/struct. And a lot is probably not needed. - - -\cs_new_protected:Nn \@@_prop_new:N +% TODO: this should be cleaned up, but at least roles are currently using +% the table! +% +% \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_protected:Npn \@@_prop_new:N #1 { \prop_new:N #1 - \directlua { ltx.@@.tables.\cs_to_str:N#1 = {} } + \lua_now:e { ltx.@@.tables.\cs_to_str:N#1 = {} } } -\cs_new_protected:Nn \@@_seq_new:N +\cs_set_protected:Npn \@@_seq_new:N #1 { \seq_new:N #1 - \directlua { ltx.@@.tables.\cs_to_str:N#1 = {} } + \lua_now:e { ltx.@@.tables.\cs_to_str:N#1 = {} } } -\cs_new_protected:Nn \@@_prop_gput:Nnn +\cs_set_protected:Npn \@@_prop_gput:Nnn #1 #2 #3 { \prop_gput:Nnn #1 { #2 } { #3 } - \directlua { ltx.@@.tables.\cs_to_str:N#1 ["#2"] = "#3" } + \lua_now:e { ltx.@@.tables.\cs_to_str:N#1 ["#2"] = "#3" } } -\cs_new_protected:Nn \@@_seq_gput_right:Nn +\cs_set_protected:Npn \@@_seq_gput_right:Nn #1 #2 { \seq_gput_right:Nn #1 { #2 } - \directlua { table.insert(ltx.@@.tables.\cs_to_str:N#1, "#2") } + \lua_now:e { table.insert(ltx.@@.tables.\cs_to_str:N#1, "#2") } } %Hm not quite sure about the naming -\cs_new:Npn \@@_seq_item:cn #1 #2 +\cs_set:Npn \@@_seq_item:cn #1 #2 { - \directlua { tex.print(ltx.@@.tables.#1[#2]) } + \lua_now:e { tex.print(ltx.@@.tables.#1[#2]) } } -\cs_new:Npn \@@_prop_item:cn #1 #2 +\cs_set:Npn \@@_prop_item:cn #1 #2 { - \directlua { tex.print(ltx.@@.tables.#1["#2"]) } + \lua_now:e { tex.print(ltx.@@.tables.#1["#2"]) } } %for debugging commands that show both the seq/prop and the lua tables -\cs_new_protected:Nn \@@_seq_show:N +\cs_set_protected:Npn \@@_seq_show:N #1 { \seq_show:N #1 - \directlua { ltx.@@.trace.log ("lua~sequence~array~\cs_to_str:N#1",1) } - \directlua { ltx.@@.trace.show_seq (ltx.@@.tables.\cs_to_str:N#1) } + \lua_now:e { ltx.@@.trace.log ("lua~sequence~array~\cs_to_str:N#1",1) } + \lua_now:e { ltx.@@.trace.show_seq (ltx.@@.tables.\cs_to_str:N#1) } } -\cs_new_protected:Nn \@@_prop_show:N +\cs_set_protected:Npn \@@_prop_show:N #1 { \prop_show:N #1 - \directlua {ltx.@@.trace.log ("lua~property~table~\cs_to_str:N#1",1) } - \directlua {ltx.@@.trace.show_prop (ltx.@@.tables.\cs_to_str:N#1) } + \lua_now:e {ltx.@@.trace.log ("lua~property~table~\cs_to_str:N#1",1) } + \lua_now:e {ltx.@@.trace.show_prop (ltx.@@.tables.\cs_to_str:N#1) } } -%</luatex> % \end{macrocode} -% +% \end{macro} % \begin{macrocode} -%<*pdftex> -\ProvidesExplFile {tagpdf-pdftex.def} {2021-06-14} {0.82} - {tagpdf~driver~for~pdftex} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%% driver (pdflatex) commands -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -% These commands are only aliases for pdflatex but are defined differently with luatex -% to get also lua tables. -\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 - -%</pdftex> +%</luatex> % \end{macrocode} +% The module declaration % \begin{macrocode} %<*lua> -- tagpdf.lua @@ -149,8 +161,8 @@ local ProvidesLuaModule = { name = "tagpdf", - version = "0.82", --TAGVERSION - date = "2021-06-14", --TAGDATE + version = "0.9", --TAGVERSION + date = "2021-06-29", --TAGDATE description = "tagpdf lua code", license = "The LATEX Project Public License 1.3c" } @@ -168,8 +180,9 @@ The code has quite probably a number of problems - --]] - - +% \end{macrocode} +% Some comments about the lua structure. +% \begin{macrocode} --[[ the main table is named ltx.@@. It contains the functions and also the data collected during the compilation. @@ -199,9 +212,10 @@ functions ltx.@@.func.mark_shipout (): a wrapper around the core function which inserts the last EMC ltx.@@.func.fill_parent_tree_line (page): outputs the entries of the parenttree for this page ltx.@@.func.output_parenttree(): outputs the content of the parenttree + ltx.@@.func.pdf_object_ref(name): outputs the object reference for the object name ltx.@@.func.markspaceon(), ltx.@@.func.markspaceoff(): (de)activates the marking of positions for space chars - ltx.@@.trace.show_mc_data (num): shows ltx.@@.mc[num] - ltx.@@.trace.show_all_mc_data (max): shows a maximum about mc's + ltx.@@.trace.show_mc_data (num,loglevel): shows ltx.@@.mc[num] is the current log level is >= loglevel + ltx.@@.trace.show_all_mc_data (max,loglevel): shows a maximum about mc's if the current log level is >= loglevel ltx.@@.trace.show_seq: shows a sequence (array) ltx.@@.trace.show_struct_data (num): shows data of structure num ltx.@@.trace.show_prop: shows a prop @@ -209,19 +223,34 @@ functions ltx.@@.trace.showspaces : boolean --]] -local mctypeattributeid = luatexbase.registernumber ("l_@@_mc_type_attr") -local mccntattributeid = luatexbase.registernumber ("l_@@_mc_cnt_attr") -local iwspaceattributeid = luatexbase.registernumber ("g_@@_interwordspace_attr") -local iwfontattributeid = luatexbase.registernumber ("g_@@_interwordfont_attr") - - -local catlatex = luatexbase.registernumber("catcodetable@latex") +% \end{macrocode} +% This set-ups the main attribute registers. +% The mc_type attribute stores the type (P, Span etc) encoded as a num, +% The mc_cnt attribute stores the absolute number and allows so to see +% if a node belongs to the same mc-chunk. +% +% The interwordspace attr is set by the function |@@_mark_spaces|, and marks +% the place where spaces should be inserted. +% The interwordfont attr is set by the function |@@_mark_spaces| too and +% stores the font, so that we can decide which font +% to use for the real space char. +% \begin{macrocode} +local mctypeattributeid = luatexbase.new_attribute ("g_@@_mc_type_attr") +local mccntattributeid = luatexbase.new_attribute ("g_@@_mc_cnt_attr") +local iwspaceattributeid = luatexbase.new_attribute ("g_@@_interwordspace_attr") +local iwfontattributeid = luatexbase.new_attribute ("g_@@_interwordfont_attr") +% \end{macrocode} +% with this token we can query the state of the boolean +% and so detect if unmarked nodes should be marked as attributes +% \begin{macrocode} local tagunmarkedbool= token.create("g_@@_tagunmarked_bool") local truebool = token.create("c_true_bool") - +% \end{macrocode} +% Now a number of local versions from global tables. +% Not all is perhaps needed, most node variants were copied from lua-debug. +% \begin{macrocode} +local catlatex = luatexbase.registernumber("catcodetable@latex") local tableinsert = table.insert - --- not all needed, copied from lua-visual-debug. local nodeid = node.id local nodecopy = node.copy local nodegetattribute = node.get_attribute @@ -247,29 +276,29 @@ local KERN = node.id("kern") local PENALTY = node.id("penalty") local LOCAL_PAR = node.id("local_par") local MATH = node.id("math") - -local function @@_get_mathsubtype (mathnode) - if mathnode.subtype == 0 then - subtype = "beginmath" - else - subtype = "endmath" - end - return subtype -end - - - +% \end{macrocode} +% Now we setup the main table structure. ltx is used by other latex code too! +% \begin{macrocode} ltx = ltx or { } ltx.@@ = ltx.@@ or { } -ltx.@@.mc = ltx.@@.mc or { } -- mc data -ltx.@@.struct = ltx.@@.struct or { } -- struct data -ltx.@@.tables = ltx.@@.tables or { } -- tables created with new prop and new seq. - -- wasn't a so great idea ... -ltx.@@.page = ltx.@@.page or { } -- page data, currently only i->{0->mcnum,1->mcnum,...} -ltx.@@.trace = ltx.@@.trace or { } -- show commands -ltx.@@.func = ltx.@@.func or { } -- functions -ltx.@@.conf = ltx.@@.conf or { } -- configuration variables - +ltx.@@.mc = ltx.@@.mc or { } -- mc data +ltx.@@.struct = ltx.@@.struct or { } -- struct data +ltx.@@.tables = ltx.@@.tables or { } -- tables created with new prop and new seq. + -- wasn't a so great idea ... + -- g_@@_role_tags_seq used by tag<-> is in this tables! +ltx.@@.page = ltx.@@.page or { } -- page data, currently only i->{0->mcnum,1->mcnum,...} +ltx.@@.trace = ltx.@@.trace or { } -- show commands +ltx.@@.func = ltx.@@.func or { } -- functions +ltx.@@.conf = ltx.@@.conf or { } -- configuration variables +% \end{macrocode} +% \section{Logging functions} +% +% \begin{macro}{@@_log,ltx.@@.trace.log} +% This rather simple log function takes as argument a +% message (string) and a number and +% will output the message to the log/terminal if the current loglevel +% is greater or equal than num. +% \begin{macrocode} local @@_log = function (message,loglevel) if (loglevel or 3) <= tex.count["l_@@_loglevel_int"] then @@ -278,16 +307,267 @@ local @@_log = end ltx.@@.trace.log = @@_log +% \end{macrocode} +% \end{macro} +% \begin{macro}{ltx.@@.trace.show_seq} +% This shows the content of a seq as stored in the tables table. +% It is used by the |\@@_seq_show:N| function. It is not used +% in user commands, only for debugging, and so requires log level >0. +% \begin{macrocode} +function ltx.@@.trace.show_seq (seq) + if (type(seq) == "table") then + for i,v in ipairs(seq) do + @@_log ("[" .. i .. "] => " .. tostring(v),1) + end + else + @@_log ("sequence " .. tostring(seq) .. " not found",1) + end +end +% \end{macrocode} +% \end{macro} +% \begin{macro}{ @@_pairs_prop,ltx.@@.trace.show_prop} +% This shows the content of a prop as stored in the tables table. +% It is used by the |\@@_prop_show:N| function. +% \begin{macrocode} +local @@_pairs_prop = + function (prop) + local a = {} + for n in pairs(prop) do tableinsert(a, n) end + table.sort(a) + local i = 0 -- iterator variable + local iter = function () -- iterator function + i = i + 1 + if a[i] == nil then return nil + else return a[i], prop[a[i]] + end + end + return iter + end +function ltx.@@.trace.show_prop (prop) + if (type(prop) == "table") then + for i,v in @@_pairs_prop (prop) do + @@_log ("[" .. i .. "] => " .. tostring(v),1) + end + else + @@_log ("prop " .. tostring(prop) .. " not found or not a table",1) + end + end +% \end{macrocode} +% \end{macro} +% \begin{macro}{ltx.@@.trace.show_mc_data} +% This shows some data for a mc given by |num|. +% If something is shown depends on the log level. +% The function is used by the following function and then in +% |\ShowTagging| +% \begin{macrocode} +function ltx.@@.trace.show_mc_data (num,loglevel) + if ltx.@@ and ltx.@@.mc and ltx.@@.mc[num] then + for k,v in pairs(ltx.@@.mc[num]) do + @@_log ("mc"..num..": "..tostring(k).."=>"..tostring(v),loglevel) + end + if ltx.@@.mc[num]["kids"] then + @@_log ("mc" .. num .. " has " .. #ltx.@@.mc[num]["kids"] .. " kids",loglevel) + for k,v in ipairs(ltx.@@.mc[num]["kids"]) do + @@_log ("mc ".. num .. " kid "..k.." =>" .. v.kid.." on page " ..v.page,loglevel) + end + end + else + @@_log ("mc"..num.." not found",loglevel) + end +end +% \end{macrocode} +% \end{macro} +% \begin{macro}{ltx.@@.trace.show_all_mc_data} +% This shows data for the mc's between |min| and |max| (numbers). +% It is used by the |\ShowTagging| function. +% \begin{macrocode} +function ltx.@@.trace.show_all_mc_data (min,max,loglevel) + for i = min, max do + ltx.@@.trace.show_mc_data (i,loglevel) + end + texio.write_nl("") +end +% \end{macrocode} +% \end{macro} + +% \begin{macro} +% { +% ltx.@@.trace.show_struct_data +% } +% This function shows some struct data. +% Unused but kept for debugging. +% \begin{macrocode} +function ltx.@@.trace.show_struct_data (num) + if ltx.@@ and ltx.@@.struct and ltx.@@.struct[num] then + for k,v in ipairs(ltx.@@.struct[num]) do + @@_log ("struct "..num..": "..tostring(k).."=>"..tostring(v),1) + end + else + @@_log ("struct "..num.." not found ",1) + end +end +% \end{macrocode} +% \end{macro} + +% +% \section{Helper functions} +% \subsection{Retrieve data functions} +% \begin{macro}{@@_get_mc_cnt_type_tag} +% This takes a node as argument and returns the mc-cnt, the mc-type and +% and the tag (calculated from the mc-cnt. +% \begin{macrocode} local @@_get_mc_cnt_type_tag = function (n) local mccnt = nodegetattribute(n,mccntattributeid) or -1 local mctype = nodegetattribute(n,mctypeattributeid) or -1 local tag = ltx.@@.func.get_tag_from(mctype) return mccnt,mctype,tag end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{@@_get_mathsubtype} +% This function allows to detect if we are at the begin or the end of math. +% It takes as argument a mathnode. +% \begin{macrocode} +local function @@_get_mathsubtype (mathnode) + if mathnode.subtype == 0 then + subtype = "beginmath" + else + subtype = "endmath" + end + return subtype +end +% \end{macrocode} +% \end{macro} + +% \begin{macro} +% { +% @@_get_num_from, +% ltx.@@.func.get_num_from, +% ltx.@@.func.output_num_from +% } +% These functions take as argument a string |tag|, and return the number +% under which is it recorded (and so the attribute value). +% The first function outputs the number for lua, while the |output| function +% outputs to tex. +% \begin{macrocode} +local @@_get_num_from = + function (tag) + if ltx.@@.tables["g_@@_role_tags_prop"][tag] then + a= ltx.@@.tables["g_@@_role_tags_prop"][tag] + else + a= -1 + end + return a + end + +ltx.@@.func.get_num_from = @@_get_num_from + +function ltx.@@.func.output_num_from (tag) + local num = @@_get_num_from (tag) + tex.sprint(catlatex,num) + if num == -1 then + @@_log ("Unknown tag "..tag.." used") + end +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% @@_get_tag_from , +% ltx.@@.func.get_tag_from, +% ltx.@@.func.output_tag_from +% } +% These functions are the opposites to the previous function: +% they take as argument a number (the attribute value) and return the string |tag|. +% The first function outputs the number for lua, while the |output| function +% outputs to tex. +% \begin{macrocode} +local @@_get_tag_from = + function (num) + if ltx.@@.tables["g_@@_role_tags_seq"][num] then + a = ltx.@@.tables["g_@@_role_tags_seq"][num] + else + a= "UNKNOWN" + end + return a +end +ltx.@@.func.get_tag_from = @@_get_tag_from +function ltx.@@.func.output_tag_from (num) + tex.sprint(catlatex,@@_get_tag_from (num)) +end +% \end{macrocode} +% \end{macro} +% \begin{macro} +% { +% ltx.@@.func.store_mc_data +% } +% This function stores for |key|=|data| for mc-chunk |num|. It is used in the +% tagpdf-mc code, to store for example the tag string, and the raw options. +% \begin{macrocode} +function ltx.@@.func.store_mc_data (num,key,data) + ltx.@@.mc[num] = ltx.@@.mc[num] or { } + ltx.@@.mc[num][key] = data + @@_log ("INFO TEX-STORE-MC-DATA: "..num.." => "..tostring(key).." => "..tostring(data),3) +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% ltx.@@.func.store_mc_label +% } +% This function stores the |label|=|num| relationship in the |labels| subtable. +% TODO: this is probably unused and can go. +% \begin{macrocode} +function ltx.@@.func.store_mc_label (label,num) + ltx.@@.mc["labels"] = ltx.@@.mc["labels"] or { } + ltx.@@.mc.labels[label] = num +end +% \end{macrocode} +% \end{macro} +% \begin{macro} +% { +% ltx.@@.func.store_mc_kid +% } +% This function is used in the traversing code. It stores +% a sub-chunk of a mc |mcnum| into the |kids| table. +% \begin{macrocode} +function ltx.@@.func.store_mc_kid (mcnum,kid,page) + ltx.@@.trace.log("INFO TAG-STORE-MC-KID: "..mcnum.." => " .. kid.." on page " .. page,3) + ltx.@@.mc[mcnum]["kids"] = ltx.@@.mc[mcnum]["kids"] or { } + local kidtable = {kid=kid,page=page} + tableinsert(ltx.@@.mc[mcnum]["kids"], kidtable ) +end +% \end{macrocode} +% \end{macro} + +% \begin{macro} +% { +% ltx.@@.func.mc_num_of_kids +% } +% This function returns the number of kids a mc |mcnum| has. We need to account for +% the case that a mc can have no kids. +% \begin{macrocode} +function ltx.@@.func.mc_num_of_kids (mcnum) + local num = 0 + if ltx.@@.mc[mcnum] and ltx.@@.mc[mcnum]["kids"] then + num = #ltx.@@.mc[mcnum]["kids"] + end + ltx.@@.trace.log ("INFO MC-KID-NUMBERS: " .. mcnum .. "has " .. num .. "KIDS",4) + return num +end +% \end{macrocode} +% \end{macro} +% \subsection{Functions to insert the pdf literals} +% \begin{macro}{@@_insert_emc_node} +% This insert the emc node. +% \begin{macrocode} local function @@_insert_emc_node (head,current) local emcnode = nodenew("whatsit","pdf_literal") emcnode.data = "EMC" @@ -295,8 +575,11 @@ local function @@_insert_emc_node (head,current) head = node.insert_before(head,current,emcnode) return head end - - +% \end{macrocode} +% \end{macro} +% \begin{macro}{@@_insert_bmc_node} +% This inserts a simple bmc node +% \begin{macrocode} local function @@_insert_bmc_node (head,current,tag) local bmcnode = nodenew("whatsit","pdf_literal") bmcnode.data = "/"..tag.." BMC" @@ -304,7 +587,12 @@ local function @@_insert_bmc_node (head,current,tag) head = node.insert_before(head,current,bmcnode) return head end - +% \end{macrocode} +% \end{macro} +% \begin{macro}{@@_insert_bdc_node} +% This inserts a bcd node with a fix dict. +% TODO: check if this is still used, now that we create properties. +% \begin{macrocode} local function @@_insert_bdc_node (head,current,tag,dict) local bdcnode = nodenew("whatsit","pdf_literal") bdcnode.data = "/"..tag.."<<"..dict..">> BDC" @@ -312,8 +600,28 @@ local function @@_insert_bdc_node (head,current,tag,dict) head = node.insert_before(head,current,bdcnode) return head end - --- this is for debugging the space chars +% \end{macrocode} +% \end{macro} +% \begin{macro}{@@_pdf_object_ref,ltx.@@.func.pdf_object_ref} +% This allows to reference a pdf object reserved with the l3pdf command by name. +% The return value is |n 0 R|, if the object doesn't exist, n is 0. +% TODO: is uses internal l3pdf commands, this should be properly supported by l3pdf +% \begin{macrocode} +local function @@_pdf_object_ref (name) + local tokenname = 'c__pdf_backend_object_'..name..'_int' + local object = token.create(tokenname).index..' 0 R' + return object +end +ltx.@@.func.pdf_object_ref=@@_pdf_object_ref +% \end{macrocode} +% \end{macro} +% +% \section{Function for the real space chars} +% \begin{macro}{@@_show_spacemark} +% A debugging function, it is used to +% inserts red color markers in the places where space chars can go, it can have +% side effects so not always reliable, but ok. +% \begin{macrocode} local function @@_show_spacemark (head,current,color,height) local markcolor = color or "1 0 0" local markheight = height or 10 @@ -323,7 +631,24 @@ local function @@_show_spacemark (head,current,color,height) head = node.insert_after(head,current,pdfstring) return head end - +% \end{macrocode} +% \end{macro} +% \begin{macro}{@@_fakespace,ltx.@@.func.fakespace} +% This is used to define a lua version of |\pdffakespace| +% \begin{macrocode} +local function @@_fakespace() + tex.setattribute(iwspaceattributeid,1) + tex.setattribute(iwfontattributeid,font.current()) +end +ltx.@@.func.fakespace = @@_fakespace +% \end{macrocode} +% \end{macro} +% \begin{macro}{@@_mark_spaces} +% a function to mark up places where real space chars should be inserted. +% It only sets attributes, these are then be used in a later traversing +% which inserts the actual spaces. +% When space handling is activated this function is inserted in some callbacks. +% \begin{macrocode} --[[ a function to mark up places where real space chars should be inserted it only sets an attribute. --]] @@ -353,13 +678,15 @@ local function @@_mark_spaces (head) end -- look also back if glyph.prev and (glyph.prev.id == GLUE) - and not inside_math and (glyph.prev.width >0) and not nodehasattribute(glyph.prev,iwspaceattributeid) + and not inside_math + and (glyph.prev.width >0) + and not nodehasattribute(glyph.prev,iwspaceattributeid) then nodesetattribute(glyph.prev,iwspaceattributeid,1) nodesetattribute(glyph.prev,iwfontattributeid,glyph.font) -- for debugging - if ltx.__tag.trace.showspaces then - __tag_show_spacemark (head,glyph) + if ltx.@@.trace.showspaces then + @@_show_spacemark (head,glyph) end end elseif id == PENALTY then @@ -381,7 +708,18 @@ local function @@_mark_spaces (head) end return head end - +% \end{macrocode} +% \end{macro} +% \begin{macro} +% { +% @@_activate_mark_space, +% ltx.@@.func.markspaceon, +% @@_activate_mark_space, +% ltx.@@.func.markspaceoff +% } +% Theses functions add/remove the function which marks the spaces to the callbacks +% |pre_linebreak_filter| and |hpack_filter| +% \begin{macrocode} local function @@_activate_mark_space () if not luatexbase.in_callback ("pre_linebreak_filter","markspaces") then luatexbase.add_to_callback("pre_linebreak_filter",@@_mark_spaces,"markspaces") @@ -397,23 +735,150 @@ local function @@_deactivate_mark_space () luatexbase.remove_from_callback("hpack_filter","markspaces") end end --- -ltx.@@.func.markspaceoff=@@_deactivate_mark_space +ltx.@@.func.markspaceoff=@@_deactivate_mark_space +% \end{macrocode} +% \end{macro} +% \begin{macro} +% { +% default_space_char, +% default_fontid, +% } +% We need two local variable to setup a default space char. +% \begin{macrocode} local default_space_char = node.new(GLYPH) local default_fontid = font.id("TU/lmr/m/n/10") default_space_char.char = 32 default_space_char.font = default_fontid +% \end{macrocode} +% \end{macro} +% \begin{macro} +% { +% @@_space_chars_shipout, +% ltx.@@.func.space_chars_shipout, +% } +% These is the main function to insert real space chars. It inserts a +% glyph before every glue which has been marked previously. The attributes +% are copied from the glue, so if the tagging is done later, +% it will be tagged like it. +% \begin{macrocode} +local function @@_space_chars_shipout (box) + local head = box.head + if head then + for n in node.traverse(head) do + local spaceattr = nodegetattribute(n,iwspaceattributeid) or -1 + if n.id == HLIST then -- enter the hlist + @@_space_chars_shipout (n) + elseif n.id == VLIST then -- enter the vlist + @@_space_chars_shipout (n) + elseif n.id == GLUE then + if ltx.@@.trace.showspaces and spaceattr==1 then + @@_show_spacemark (head,n,"0 1 0") + end + if spaceattr==1 then + local space + local space_char = node.copy(default_space_char) + local curfont = nodegetattribute(n,iwfontattributeid) + ltx.@@.trace.log ("INFO SPACE-FUNCTION-FONT: ".. tostring(curfont),3) + if curfont and luaotfload.aux.slot_of_name(curfont,"space") then + space_char.font=curfont + end + head, space = node.insert_before(head, n, space_char) -- + n.width = n.width - space.width + space.attr = n.attr + end + end + end + end +end -local function @@_insert_space_char (head,n,fontid) - if luaotfload.aux.slot_of_name(fontid,"space") then - local space - -- head, space = node.insert_before(head, n, ) -- Set the right font - -- n.width = n.width - space.width - -- space.attr = n.attr - end +function ltx.@@.func.space_chars_shipout (box) + @@_space_chars_shipout (box) +end +% \end{macrocode} +% \end{macro} +% +% \section{Function for the tagging} +% \begin{macro} +% { +% ltx.@@.func.mc_insert_kids +% } +% This is the main function to insert the +% K entry into a StructElem object. It is used in tagpdf-mc-luacode module. +% The |single| attribute allows to handle the case that a single +% mc on the tex side can have more than one kid after the processing here, +% and so we get the correct array/non array setup. +% \begin{macrocode} +function ltx.@@.func.mc_insert_kids (mcnum,single) + if ltx.@@.mc[mcnum] then + ltx.@@.trace.log("INFO TEX-MC-INSERT-KID-TEST: " .. mcnum,4) + if ltx.@@.mc[mcnum]["kids"] then + if #ltx.@@.mc[mcnum]["kids"] > 1 and single==1 then + tex.sprint("[") + end + for i,kidstable in ipairs( ltx.@@.mc[mcnum]["kids"] ) do + local kidnum = kidstable["kid"] + local kidpage = kidstable["page"] + local kidpageobjnum = pdfpageref(kidpage) + ltx.@@.trace.log("INFO TEX-MC-INSERT-KID: " .. mcnum .. + " insert KID " ..i.. + " with num " .. kidnum .. + " on page " .. kidpage.."/"..kidpageobjnum,3) + tex.sprint(catlatex,"<</Type /MCR /Pg "..kidpageobjnum .. " 0 R /MCID "..kidnum.. ">> " ) + end + if #ltx.@@.mc[mcnum]["kids"] > 1 and single==1 then + tex.sprint("]") + end + else + -- this is typically not a problem, e.g. empty hbox in footer/header can + -- trigger this warning. + ltx.@@.trace.log("WARN TEX-MC-INSERT-NO-KIDS: "..mcnum.." has no kids",2) + if single==1 then + tex.sprint("null") + end + end + else + ltx.@@.trace.log("WARN TEX-MC-INSERT-MISSING: "..mcnum.." doesn't exist",0) + end +end +% \end{macrocode} +% \end{macro} +% \begin{macro}{ltx.@@.func.store_struct_mcabs} +% This function is used in the tagpdf-mc-luacode. It store the absolute count +% of the mc into the current structure. This must be done ordered. +% \begin{macrocode} +function ltx.@@.func.store_struct_mcabs (structnum,mcnum) + ltx.@@.struct[structnum]=ltx.@@.struct[structnum] or { } + ltx.@@.struct[structnum]["mc"]=ltx.@@.struct[structnum]["mc"] or { } + -- a structure can contain more than on mc chunk, the content should be ordered + tableinsert(ltx.@@.struct[structnum]["mc"],mcnum) + ltx.@@.trace.log("INFO TEX-MC-INTO-STRUCT: ".. + mcnum.." inserted in struct "..structnum,3) + -- but every mc can only be in one structure + ltx.@@.mc[mcnum]= ltx.@@.mc[mcnum] or { } + ltx.@@.mc[mcnum]["parent"] = structnum end +% \end{macrocode} +% \end{macro} +% \begin{macro}{ltx.@@.func.store_mc_in_page} +% This is used in the traversing code and stores the relation between +% abs count and page count. +% \begin{macrocode} +-- pay attention: lua counts arrays from 1, tex pages from one +-- mcid and arrays in pdf count from 0. +function ltx.@@.func.store_mc_in_page (mcnum,mcpagecnt,page) + ltx.@@.page[page] = ltx.@@.page[page] or {} + ltx.@@.page[page][mcpagecnt] = mcnum + ltx.@@.trace.log("INFO TAG-MC-INTO-PAGE: page " .. page .. + ": inserting MCID " .. mcpagecnt .. " => " .. mcnum,3) +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{ltx.@@.func.mark_page_elements} +% This is the main traversing function. See the lua comment for more details. +% \begin{macrocode} --[[ Now follows the core function It wades through the shipout box and checks the attributes @@ -433,22 +898,35 @@ end function ltx.@@.func.mark_page_elements (box,mcpagecnt,mccntprev,mcopen,name,mctypeprev) local name = name or ("SOMEBOX") local mctypeprev = mctypeprev or -1 - local abspage = status.total_pages + 1 -- the real counter is increased inside the box so one off - -- if the callback is not used. - ltx.@@.trace.log ("PAGE " .. abspage,3) - ltx.@@.trace.log ("FUNC ARGS: pagecnt".. mcpagecnt.." prev "..mccntprev .. " type prev "..mctypeprev,4) - ltx.@@.trace.log ("TRAVERSING BOX ".. tostring(name).." TYPE ".. node.type(node.getid(box)),3) + local abspage = status.total_pages + 1 -- the real counter is increased + -- inside the box so one off + -- if the callback is not used. (???) + ltx.@@.trace.log ("INFO TAG-ABSPAGE: " .. abspage,3) + ltx.@@.trace.log ("INFO TAG-ARGS: pagecnt".. mcpagecnt.. + " prev "..mccntprev .. + " type prev "..mctypeprev,4) + ltx.@@.trace.log ("INFO TAG-TRAVERSING-BOX: ".. tostring(name).. + " TYPE ".. node.type(node.getid(box)),3) local head = box.head -- ShipoutBox is a vlist? if head then mccnthead, mctypehead,taghead = @@_get_mc_cnt_type_tag (head) - ltx.@@.trace.log ("HEAD " .. node.type(node.getid(head)).. " MC"..tostring(mccnthead).." => TAG "..tostring(mctypehead).." => "..tostring(taghead),3) + ltx.@@.trace.log ("INFO TAG-HEAD: " .. + node.type(node.getid(head)).. + " MC"..tostring(mccnthead).. + " => TAG " .. tostring(mctypehead).. + " => ".. tostring(taghead),3) else - ltx.@@.trace.log ("HEAD is ".. tostring(head),3) + ltx.@@.trace.log ("INFO TAG-NO-HEAD: head is ".. + tostring(head),3) end for n in node.traverse(head) do local mccnt, mctype, tag = @@_get_mc_cnt_type_tag (n) local spaceattr = nodegetattribute(n,iwspaceattributeid) or -1 - ltx.@@.trace.log ("NODE ".. node.type(node.getid(n)).." MC"..tostring(mccnt).." => TAG "..tostring(mctype).." => " .. tostring(tag),3) + ltx.@@.trace.log ("INFO TAG-NODE: ".. + node.type(node.getid(n)).. + " MC".. tostring(mccnt).. + " => TAG ".. tostring(mctype).. + " => " .. tostring(tag),3) if n.id == HLIST then -- enter the hlist mcopen,mcpagecnt,mccntprev,mctypeprev= @@ -456,83 +934,81 @@ function ltx.@@.func.mark_page_elements (box,mcpagecnt,mccntprev,mcopen,name,mct elseif n.id == VLIST then -- enter the vlist mcopen,mcpagecnt,mccntprev,mctypeprev= ltx.@@.func.mark_page_elements (n,mcpagecnt,mccntprev,mcopen,"INTERNAL VLIST",mctypeprev) - elseif n.id == GLUE then -- at glue real space chars are inserted, for the rest it is ignored - -- for debugging - if ltx.@@.trace.showspaces and spaceattr==1 then - @@_show_spacemark (head,n,"0 1 0") - end - if spaceattr==1 then - local space - local space_char = node.copy(default_space_char) - local curfont = nodegetattribute(n,iwfontattributeid) - ltx.@@.trace.log ("FONT ".. tostring(curfont),3) - if curfont and luaotfload.aux.slot_of_name(curfont,"space") then - space_char.font=curfont - end - head, space = node.insert_before(head, n, space_char) -- - n.width = n.width - space.width - space.attr = n.attr - end + elseif n.id == GLUE then -- at glue real space chars are inserted, but this has + -- been done if the previous shipout wandering, so here it is ignored elseif n.id == LOCAL_PAR then -- local_par is ignored elseif n.id == PENALTY then -- penalty is ignored elseif n.id == KERN then -- kern is ignored - ltx.@@.trace.log ("SUBTYPE KERN ".. n.subtype,3) + ltx.@@.trace.log ("INFO TAG-KERN-SUBTYPE: ".. + node.type(node.getid(n)).." "..n.subtype,4) else -- math is currently only logged. -- we could mark the whole as math -- for inner processing the mlist_to_hlist callback is probably needed. if n.id == MATH then - ltx.@@.trace.log("NODE "..node.type(node.getid(n)).." "..@@_get_mathsubtype(n),3) + ltx.@@.trace.log("INFO TAG-MATH-SUBTYPE: ".. + node.type(node.getid(n)).." "..@@_get_mathsubtype(n),4) end -- endmath - ltx.@@.trace.log("CURRENT "..mccnt.." PREV "..mccntprev,3) + ltx.@@.trace.log("INFO TAG-MC-COMPARE: current ".. + mccnt.." prev "..mccntprev,4) if mccnt~=mccntprev then -- a new mc chunk - ltx.@@.trace.log ("NODE ".. node.type(node.getid(n)).." MC"..tostring(mccnt).." <=> PREVIOUS "..tostring(mccntprev),3) + ltx.@@.trace.log ("INFO TAG-NEW-MC-NODE: ".. + node.type(node.getid(n)).. + " MC"..tostring(mccnt).. + " <=> PREVIOUS "..tostring(mccntprev),4) if mcopen~=0 then -- there is a chunk open, close it (hope there is only one ... box.list=@@_insert_emc_node (box.list,n) mcopen = mcopen - 1 - ltx.@@.trace.log ("INSERT EMC " .. mcpagecnt .. " MCOPEN = " .. mcopen,2) + ltx.@@.trace.log ("INFO TAG-INSERT-EMC: " .. + mcpagecnt .. " MCOPEN = " .. mcopen,3) if mcopen ~=0 then - ltx.@@.trace.log ("!WARNING! open mc" .. " MCOPEN = " .. mcopen,1) + ltx.@@.trace.log ("WARN TAG-OPEN-MC: " .. mcopen,1) end end if ltx.@@.mc[mccnt] then if ltx.@@.mc[mccnt]["artifact"] then - ltx.@@.trace.log("THIS IS AN ARTIFACT of type "..tostring(ltx.@@.mc[mccnt]["artifact"]),3) + ltx.@@.trace.log("INFO TAG-INSERT-ARTIFACT: ".. + tostring(ltx.@@.mc[mccnt]["artifact"]),3) if ltx.@@.mc[mccnt]["artifact"] == "" then box.list = @@_insert_bmc_node (box.list,n,"Artifact") else box.list = @@_insert_bdc_node (box.list,n,"Artifact", "/Type /"..ltx.@@.mc[mccnt]["artifact"]) end else - ltx.@@.trace.log("THIS IS A TAG "..tostring(tag),3) + ltx.@@.trace.log("INFO TAG-INSERT-TAG: ".. + tostring(tag),3) mcpagecnt = mcpagecnt +1 - ltx.@@.trace.log ("INSERT BDC "..mcpagecnt,2) + ltx.@@.trace.log ("INFO TAG-INSERT-BDC: "..mcpagecnt,3) local dict= "/MCID "..mcpagecnt if ltx.@@.mc[mccnt]["raw"] then - ltx.@@.trace.log("RAW CONTENT"..tostring(ltx.@@.mc[mccnt]["raw"]),3) + ltx.@@.trace.log("INFO TAG-USE-RAW: ".. + tostring(ltx.@@.mc[mccnt]["raw"]),3) dict= dict .. " " .. ltx.@@.mc[mccnt]["raw"] end if ltx.@@.mc[mccnt]["alt"] then - ltx.@@.trace.log("RAW CONTENT"..tostring(ltx.@@.mc[mccnt]["alt"]),3) + ltx.@@.trace.log("INFO TAG-USE-ALT: ".. + tostring(ltx.@@.mc[mccnt]["alt"]),3) dict= dict .. " " .. ltx.@@.mc[mccnt]["alt"] end if ltx.@@.mc[mccnt]["actualtext"] then - ltx.@@.trace.log("RAW CONTENT"..tostring(ltx.@@.mc[mccnt]["actualtext"]),3) + ltx.@@.trace.log("INFO TAG-USE-ACTUALTEXT: ".. + tostring(ltx.@@.mc[mccnt]["actualtext"]),3) dict= dict .. " " .. ltx.@@.mc[mccnt]["actualtext"] end box.list = @@_insert_bdc_node (box.list,n,tag, dict) ltx.@@.func.store_mc_kid (mccnt,mcpagecnt,abspage) ltx.@@.func.store_mc_in_page(mccnt,mcpagecnt,abspage) - ltx.@@.trace.show_mc_data (mccnt) + ltx.@@.trace.show_mc_data (mccnt,3) end mcopen = mcopen + 1 else - ltx.@@.trace.log("THIS HAS NOT BEEN TAGGED",1) - -- perhaps code that tag a artifact can be added ... if tagunmarkedbool.mode == truebool.mode then + ltx.@@.trace.log("INFO TAG-NOT-TAGGED: this has not been tagged, using artifact",2) box.list = @@_insert_bmc_node (box.list,n,"Artifact") mcopen = mcopen + 1 + else + ltx.@@.trace.log("WARN TAG-NOT-TAGGED: this has not been tagged",1) end end mccntprev = mccnt @@ -541,18 +1017,29 @@ function ltx.@@.func.mark_page_elements (box,mcpagecnt,mccntprev,mcopen,name,mct end -- end for if head then mccnthead, mctypehead,taghead = @@_get_mc_cnt_type_tag (head) - ltx.@@.trace.log ("ENDHEAD " .. node.type(node.getid(head)).. " MC"..tostring(mccnthead).." => TAG "..tostring(mctypehead).." => "..tostring(taghead),3) + ltx.@@.trace.log ("INFO TAG-ENDHEAD: " .. + node.type(node.getid(head)).. + " MC"..tostring(mccnthead).. + " => TAG "..tostring(mctypehead).. + " => "..tostring(taghead),4) else - ltx.@@.trace.log ("ENDHEAD is ".. tostring(head),3) + ltx.@@.trace.log ("INFO TAG-ENDHEAD: ".. tostring(head),4) end - ltx.@@.trace.log ("QUITTING TRAVERSING BOX ".. tostring(name).." TYPE ".. node.type(node.getid(box)),3) + ltx.@@.trace.log ("INFO TAG-QUITTING-BOX ".. + tostring(name).. + " TYPE ".. node.type(node.getid(box)),4) return mcopen,mcpagecnt,mccntprev,mctypeprev end - - -function ltx.__tag.func.mark_shipout (box) - mcopen = ltx.__tag.func.mark_page_elements (box,-1,-100,0,"Shipout",-1) +% \end{macrocode} +% \end{macro} +% \begin{macro}{ltx.@@.func.mark_shipout} +% This is the function used in the callback. Beside calling the traversing +% function it also checks if there is an open MC-chunk from a page +% break and insert the needed EMC literal. +% \begin{macrocode} +function ltx.@@.func.mark_shipout (box) + mcopen = ltx.@@.func.mark_page_elements (box,-1,-100,0,"Shipout",-1) if mcopen~=0 then -- there is a chunk open, close it (hope there is only one ... local emcnode = nodenew("whatsit","pdf_literal") local list = box.list @@ -561,199 +1048,28 @@ function ltx.__tag.func.mark_shipout (box) if list then list = node.insert_after (list,node.tail(list),emcnode) mcopen = mcopen - 1 - ltx.__tag.trace.log ("INSERT LAST EMC, MCOPEN = " .. mcopen,2) + ltx.@@.trace.log ("INFO SHIPOUT-INSERT-LAST-EMC: MCOPEN " .. mcopen,3) else - ltx.__tag.trace.log ("UPS ",1) + ltx.@@.trace.log ("WARN SHIPOUT-UPS: this shouldn't happen",0) end if mcopen ~=0 then - ltx.__tag.trace.log ("!WARNING! open mc" .. " MCOPEN = " .. mcopen,1) + ltx.@@.trace.log ("WARN SHIPOUT-MC-OPEN: " .. mcopen,1) end end end - - -function ltx.@@.trace.show_seq (seq) - if (type(seq) == "table") then - for i,v in ipairs(seq) do - @@_log ("[" .. i .. "] => " .. tostring(v),1) - end - else - @@_log ("sequence " .. tostring(seq) .. " not found",1) - end -end - -local @@_pairs_prop = - function (prop) - local a = {} - for n in pairs(prop) do tableinsert(a, n) end - table.sort(a) - local i = 0 -- iterator variable - local iter = function () -- iterator function - i = i + 1 - if a[i] == nil then return nil - else return a[i], prop[a[i]] - end - end - return iter - end - - -function ltx.@@.trace.show_prop (prop) - if (type(prop) == "table") then - for i,v in @@_pairs_prop (prop) do - @@_log ("[" .. i .. "] => " .. tostring(v),1) - end - else - @@_log ("prop " .. tostring(prop) .. " not found or not a table",1) - end - end - - -local @@_get_num_from = - function (tag) - if ltx.@@.tables["g_@@_role_tags_prop"][tag] then - a= ltx.@@.tables["g_@@_role_tags_prop"][tag] - else - a= -1 - end - return a - end - -ltx.@@.func.get_num_from = @@_get_num_from - -function ltx.@@.func.output_num_from (tag) - local num = @@_get_num_from (tag) - tex.sprint(catlatex,num) - if num == -1 then - @@_log ("Unknown tag "..tag.." used") - end -end - -local @@_get_tag_from = - function (num) - if ltx.@@.tables["g_@@_role_tags_seq"][num] then - a = ltx.@@.tables["g_@@_role_tags_seq"][num] - else - a= "UNKNOWN" - end - return a -end - -ltx.@@.func.get_tag_from = @@_get_tag_from - -function ltx.@@.func.output_tag_from (num) - tex.sprint(catlatex,@@_get_tag_from (num)) -end - -function ltx.@@.func.store_mc_data (num,key,data) - ltx.@@.mc[num] = ltx.@@.mc[num] or { } - ltx.@@.mc[num][key] = data - @@_log ("storing mc"..num..": "..tostring(key).."=>"..tostring(data)) -end - -function ltx.@@.trace.show_mc_data (num) - if ltx.@@ and ltx.@@.mc and ltx.@@.mc[num] then - for k,v in pairs(ltx.@@.mc[num]) do - @@_log ("mc"..num..": "..tostring(k).."=>"..tostring(v),3) - end - if ltx.@@.mc[num]["kids"] then - @@_log ("mc" .. num .. " has " .. #ltx.@@.mc[num]["kids"] .. " kids",3) - for k,v in ipairs(ltx.@@.mc[num]["kids"]) do - @@_log ("mc ".. num .. " kid "..k.." =>" .. v.kid.." on page " ..v.page,3) - end - end - else - @@_log ("mc"..num.." not found",3) - end -end - -function ltx.@@.trace.show_all_mc_data (max) - for i = 1, max do - ltx.@@.trace.show_mc_data (i) - end -end - - -function ltx.@@.func.store_mc_label (label,num) - ltx.@@.mc["labels"] = ltx.@@.mc["labels"] or { } - ltx.@@.mc.labels[label] = num -end - -function ltx.@@.func.store_mc_kid (mcnum,kid,page) - ltx.@@.trace.log("MC"..mcnum.." STORING KID" .. kid.." on page " .. page,3) - ltx.@@.mc[mcnum]["kids"] = ltx.@@.mc[mcnum]["kids"] or { } - local kidtable = {kid=kid,page=page} - tableinsert(ltx.@@.mc[mcnum]["kids"], kidtable ) -end - - -function ltx.@@.func.mc_num_of_kids (mcnum) - local num = 0 - if ltx.@@.mc[mcnum] and ltx.@@.mc[mcnum]["kids"] then - num = #ltx.@@.mc[mcnum]["kids"] - end - ltx.@@.trace.log ("MC" .. mcnum .. "has " .. num .. "KIDS",4) - return num -end - -function ltx.@@.func.mc_insert_kids (mcnum,single) - if ltx.@@.mc[mcnum] then - ltx.@@.trace.log("MC-KIDS test " .. mcnum,4) - if ltx.@@.mc[mcnum]["kids"] then - if #ltx.@@.mc[mcnum]["kids"] > 1 and single==1 then - tex.sprint("[") - end - for i,kidstable in ipairs( ltx.@@.mc[mcnum]["kids"] ) do - local kidnum = kidstable["kid"] - local kidpage = kidstable["page"] - local kidpageobjnum = pdfpageref(kidpage) - ltx.@@.trace.log("MC" .. mcnum .. " insert KID " ..i.. " with num " .. kidnum .. " on page " .. kidpage.."/"..kidpageobjnum,3) - tex.sprint(catlatex,"<</Type /MCR /Pg "..kidpageobjnum .. " 0 R /MCID "..kidnum.. ">> " ) - end - if #ltx.@@.mc[mcnum]["kids"] > 1 and single==1 then - tex.sprint("]") - end - else - ltx.@@.trace.log("WARN! MC"..mcnum.." has no kids",0) - if single==1 then - tex.sprint("null") - end - end - else - ltx.@@.trace.log("WARN! MC"..mcnum.." doesn't exist",0) - end -end - - -function ltx.@@.func.store_struct_mcabs (structnum,mcnum) - ltx.@@.struct[structnum]=ltx.@@.struct[structnum] or { } - ltx.@@.struct[structnum]["mc"]=ltx.@@.struct[structnum]["mc"] or { } - -- a structure can contain more than on mc chunk, the content should be ordered - tableinsert(ltx.@@.struct[structnum]["mc"],mcnum) - ltx.@@.trace.log("MCNUM "..mcnum.." insert in struct "..structnum,3) - -- but every mc can only be in one structure - ltx.@@.mc[mcnum]= ltx.@@.mc[mcnum] or { } - ltx.@@.mc[mcnum]["parent"] = structnum -end - -function ltx.@@.trace.show_struct_data (num) - if ltx.@@ and ltx.@@.struct and ltx.@@.struct[num] then - for k,v in ipairs(ltx.@@.struct[num]) do - @@_log ("struct "..num..": "..tostring(k).."=>"..tostring(v)) - end - else - @@_log ("struct "..num.." not found ") - end -end - --- pay attention: lua counts arrays from 1, tex pages from one --- mcid and arrays in pdf count from 0. -function ltx.@@.func.store_mc_in_page (mcnum,mcpagecnt,page) - ltx.@@.page[page] = ltx.@@.page[page] or {} - ltx.@@.page[page][mcpagecnt] = mcnum - ltx.@@.trace.log("PAGE " .. page .. ": inserting MCID " .. mcpagecnt .. " => " .. mcnum,3) -end - +% \end{macrocode} +% \end{macro} + +% \section{Parenttree} +% \begin{macro} +% { +% ltx.@@.func.fill_parent_tree_line, +% ltx.@@.func.output_parenttree +% } +% These functions create the parent tree. The second, main function +% is used in the tagpdf-tree code. +% TODO check if the tree code can move into the backend code. +% \begin{macrocode} function ltx.@@.func.fill_parent_tree_line (page) -- we need to get page-> i=kid -> mcnum -> structnum -- pay attention: the kid numbers and the page number in the parent tree start with 0! @@ -761,33 +1077,41 @@ function ltx.@@.func.fill_parent_tree_line (page) local pdfpage = page-1 if ltx.@@.page[page] and ltx.@@.page[page][0] then mcchunks=#ltx.@@.page[page] - ltx.@@.trace.log("PAGETREE PAGE "..page.." has "..mcchunks.."+1 Elements ",3) + ltx.@@.trace.log("INFO PARENTTREE-NUM: page ".. + page.." has "..mcchunks.."+1 Elements ",4) for i=0,mcchunks do - ltx.@@.trace.log("PAGETREE CHUNKS "..ltx.@@.page[page][i],3) + -- what does this log?? + ltx.@@.trace.log("INFO PARENTTREE-CHUNKS: ".. + ltx.@@.page[page][i],4) end if mcchunks == 0 then -- only one chunk so no need for an array local mcnum = ltx.@@.page[page][0] local structnum = ltx.@@.mc[mcnum]["parent"] local propname = "g_@@_struct_"..structnum.."_prop" - local objref = ltx.@@.tables[propname]["objref"] or "XXXX" - ltx.@@.trace.log("=====>"..tostring(objref),5) + --local objref = ltx.@@.tables[propname]["objref"] or "XXXX" + local objref = @@_pdf_object_ref('@@/struct/'..structnum) + ltx.@@.trace.log("INFO PARENTTREE-STRUCT-OBJREF: =====>".. + tostring(objref),5) numsentry = pdfpage .. " [".. objref .. "]" - ltx.@@.trace.log("PAGETREE PAGE" .. page.. " NUM ENTRY = ".. numsentry,3) + ltx.@@.trace.log("INFO PARENTTREE-NUMENTRY: page " .. + page.. " num entry = ".. numsentry,3) else numsentry = pdfpage .. " [" for i=0,mcchunks do local mcnum = ltx.@@.page[page][i] local structnum = ltx.@@.mc[mcnum]["parent"] or 0 local propname = "g_@@_struct_"..structnum.."_prop" - local objref = ltx.@@.tables[propname]["objref"] or "XXXX" + --local objref = ltx.@@.tables[propname]["objref"] or "XXXX" + local objref = @@_pdf_object_ref('@@/struct/'..structnum) numsentry = numsentry .. " ".. objref end numsentry = numsentry .. "] " - ltx.@@.trace.log("PAGETREE PAGE" .. page.. " NUM ENTRY = ".. numsentry,3) + ltx.@@.trace.log("INFO PARENTTREE-NUMENTRY: page " .. + page.. " num entry = ".. numsentry,3) end else - ltx.@@.trace.log ("PAGETREE: NO DATA FOR PAGE "..page,3) + ltx.@@.trace.log ("INFO PARENTTREE-NO-DATA: page "..page,3) end return numsentry end @@ -798,7 +1122,10 @@ function ltx.@@.func.output_parenttree (abspage) tex.sprint(catlatex,line) end end - - +% \end{macrocode} +% \end{macro} +% \begin{macrocode} %</lua> % \end{macrocode} +% \end{implementation} +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-checks.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-checks.dtx index 87f493d0d82..e0a1c013d7f 100644 --- a/Master/texmf-dist/source/latex/tagpdf/tagpdf-checks.dtx +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-checks.dtx @@ -22,59 +22,442 @@ % % 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-checks module (tagpdf)} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> % \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.9, released 2021-06-29} +% \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. Currently +% the only sensible values for the argument \meta{keyword} +% are |mc_tag| and |struct_tag|. +% \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 \\ +% \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 +% \\ +% |\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. +% \end{tabular} % +% \normalsize +% \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} + +% \end{documentation} +% \begin{implementation} % \begin{macrocode} %<@@=tag> -%<*checks> -\ProvidesExplPackage {tagpdf-checks-code} {2021-06-14} {0.82} - {part of tagpdf - code related to checks and messages} - - -%messages - -% mc -\msg_new:nnn { tag } {mc-nested} { nested~marked~content~found~-~mcid~#1 } -\msg_new:nnn { tag } {mc-tag-missing} { required~tag~missing~-~mcid~#1 } -\msg_new:nnn { tag } {mc-label-unknown} { label~#1~unknown~-~rerun } -\msg_new:nnn { tag } {mc-used-twice} { mc~#1~has~been~already~used } -\msg_new:nnn { tag } {mc-not-open} { there~is~no~mc~to~end~at~#1 } -\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 } - -% structures -\msg_new:nnn { tag } {struct-no-objnum} { objnum~missing~for~structure~#1 } -\msg_new:nnn { tag } {struct-faulty-nesting} { there~is~no~open~structure~on~the~stack } -\msg_new:nnn { tag } {struct-missing-tag} { a~structure~must~have~a~tag! } -\msg_new:nnn { tag } {struct-show-closing} { closing~structure~#1~tagged~\prop_item:cn{g_@@_struct_#1_prop}{S} } -\msg_new:nnn { tag } {struct-used-twice} { structure~with~label~#1~has~already~been~used} -\msg_new:nnn { tag } {struct-label-unknown} { structure~with~label~#1~is~unknown~rerun} - -% attributes +%<*header> +\ProvidesExplPackage {tagpdf-checks-code} {2021-06-29} {0.9} + {part of tagpdf - code related to checks, conditionals, debugging and messages} +%</header> +% \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 mc-chunks} +% \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-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~\prop_item:cn{g_@@_struct_#1_prop}{S} } +% \end{macrocode} +% \end{macro} +% +% \subsection{Attributes} +% Not much yet, as attributes aren't used so much. +% \begin{macro}{attr-unknown} +% \begin{macrocode} \msg_new:nnn { tag } {attr-unknown} { attribute~#1~is~unknown} - - -%Roles +% \end{macrocode} +% \end{macro} +% +% \subsection{Roles} +% \begin{macro}{role-missing,role-unknown,role-unknown-tag} +% 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 } +% \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 } - - -% trees -\msg_new:nnn { tag } {tree-mcid-index-wrong} {something~is~wrong~with~the~mcid} - -% obj -\msg_new:nnn { tag } {obj-write-num} {write~obj~#1~to~pdf} - -% engine problem -\msg_new:nnn { tag } {sys-no-interwordspace} {engine~#1~doesn't~support~the~interword~spaces} - -%checks -%structures - -\cs_new:Nn \@@_check_structure_has_tag:n %#1 struct num %_protected? +% \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} +% \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| and |struct_tag|. +% \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 is a test it 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} +\prg_new_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} +% +% \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:} +% 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. +% \begin{macrocode} +\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 stuctures} +% \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} @@ -82,37 +465,50 @@ \msg_error:nn { tag } {struct-missing-tag} } } - -\cs_new:Nn \@@_check_structure_tag:N %_protected? +% \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_prop {#1} { \msg_warning:nnx { tag } {role-unknown-tag} {#1} } } - -\cs_new:Nn \@@_check_structure_tag:NN %_protected? +% \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 { - \exp_args:NNx - \prop_if_in:NnF \g_@@_role_tags_prop {#1/#2} + \int_compare:nNnT {\l_@@_loglevel_int} > { 0 } { - \msg_warning:nnx { tag } {role-unknown-tag} {#1} + \msg_info:nnn { tag } {struct-show-closing} {#1} } } -\cs_new_protected:Nn \@@_check_info_closing_struct:n %#1 struct num - { - \msg_info:nnn { tag } {struct-show-closing} {#1} - } - \cs_generate_variant:Nn \@@_check_info_closing_struct:n {o,x} - -\cs_new_protected:Nn \@@_check_no_open_struck: +% \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} } - -\cs_new_protected:Nn \@@_check_struct_used:n %#1 label +% \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_\@@_ref_value:enn{tagpdfstruct-#1}{tagstruct}{unknown}_prop} @@ -122,10 +518,14 @@ \msg_warning:nnn { tag } {struct-used-twice} {#1} } } - -%roles - -\cs_new_protected:Nn \@@_check_add_tag_role:nn %#1 tag, #2 role +% \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} { @@ -134,16 +534,26 @@ { \prop_get:NnNTF \g_@@_role_tags_prop {#2} \l_tmpa_tl { - \msg_info:nnnn { tag } {role-tag} {#1} {#2} + \int_compare:nNnT {\l_@@_loglevel_int} > { 0 } + { + \msg_info:nnnn { tag } {role-tag} {#1} {#2} + } } { \msg_warning:nnn { tag } {role-unknown} {#2} } } } - -%mc -\cs_new_protected:Nn \@@_check_mc_if_nested: +% \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 { @@ -151,15 +561,21 @@ } } -\cs_new_protected:Nn \@@_check_mc_if_open: +\cs_new_protected:Npn \@@_check_mc_if_open: { \@@_mc_if_in:F { \msg_warning:nnx { tag } {mc-not-open} { \@@_get_mc_abs_cnt: } } } - -\cs_new_protected:Nn \@@_check_mc_pushed_popped:nn +% \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 } @@ -167,13 +583,16 @@ \int_compare:nNnT { \l_@@_loglevel_int } > { 2 } { - \msg_warning:nnx {tag}{mc-#1}{#2} - \seq_log:N \g_@@_mc_stack_seq + \msg_info:nnx {tag}{mc-#1}{#2} + \seq_log:N \g_@@_mc_stack_seq } } - - -\cs_new:Nn \@@_check_mc_tag:N %protected?? +% \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 { @@ -184,22 +603,56 @@ \msg_warning:nnx { tag } {role-unknown-tag} {#1} } } - -\seq_new:N \g_@@_check_mc_used_seq -\cs_new_protected:Nn \@@_check_mc_used:n +% \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: { - \seq_if_in:NnTF \g_@@_check_mc_used_seq {#1} - { - \msg_warning:nnn { tag } {mc-used-twice} {#1} - } + \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 } { - \seq_gput_right:Nx \g_@@_check_mc_used_seq {#1} + \@@_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} + } } } - - - -\cs_new_protected:Nn \@@_check_show_MCID_by_page: +% \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:Nx \l_@@_tmpa_tl { @@ -245,14 +698,10 @@ \seq_show:N \l_tmpa_seq } } - -\cs_new:Nn \@@_check_record_pdfobj_num:n %protected? - { - \int_compare:nT { \l_@@_loglevel_int >= 3 } - { - \msg_info:nnx { tag } {obj-write-num} {#1} - } - } - -%</checks> % \end{macrocode} +% \end{macro} +% \begin{macrocode} +%</package> +% \end{macrocode} +% \end{implementation} +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc-generic.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc-generic.dtx new file mode 100644 index 00000000000..956e3e91cbf --- /dev/null +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc-generic.dtx @@ -0,0 +1,389 @@ +% \iffalse meta-comment +% +%% File: tagpdf-mc.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. +%<*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-mc module (tagpdf)} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% \title{^^A +% The \pkg{tagpdf-mc-generic} module\\ Code related to Marked Content (mc-chunks), generic mode ^^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} +% \section{Marked content code -- generic mode} +% \begin{macrocode} +%<@@=tag> +%<*generic> +\ProvidesExplPackage {tagpdf-mc-code-generic} {2021-06-29} {0.9} + {part of tagpdf - code related to marking chunks - generic mode} +%</generic> +% \end{macrocode} +% \subsection{Variables} +% \begin{variable}{\g_@@_in_mc_bool} +% This booleans records if a mc is open, to test nesting. +% \begin{macrocode} +%<*generic> +\bool_new:N \g_@@_in_mc_bool +% \end{macrocode} +% \end{variable} + +% \begin{variable}{\g_@@_MCID_byabspage_prop} +% This property will hold the current maximum on a page +% it will contain key-value of type \meta{abspagenum}=\meta{max mcid} +% +% \begin{macrocode} +\@@_prop_new:N \g_@@_MCID_byabspage_prop +% \end{macrocode} +% \end{variable} + +% \begin{variable}{\l_@@_mc_ref_abspage_tl} +% We need a ref-label system to ensure that the MCID cnt +% restarts at 0 on a new page +% This will be used to store the tagabspage attribute retrieved from +% a label. +% \begin{macrocode} +\tl_new:N \l_@@_mc_ref_abspage_tl +% \end{macrocode} +% \end{variable} + +% \begin{variable}{\l_@@_mc_tmpa_tl} +% temporary variable +% \begin{macrocode} +\tl_new:N \l_@@_mc_tmpa_tl +% \end{macrocode} +% \end{variable} + +% \subsection{Functions} + +% \begin{macro}[pTF]{\@@_mc_if_in:,\tag_mc_if_in:} +% This is a test if a mc is open or not. It depends simply on a global boolean: +% mc-chunks are added linearly so nesting should not be relevant. +% \begin{macrocode} +\prg_new_conditional:Nnn \@@_mc_if_in: {p,T,F,TF} + { + \bool_if:NTF \g_@@_in_mc_bool + { \prg_return_true: } + { \prg_return_false: } + } + +\prg_new_eq_conditional:NNn \tag_mc_if_in: \@@_mc_if_in: {p,T,F,TF} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{ \@@_mc_bmc:n,\@@_mc_emc:,\@@_mc_bdc:nn,\@@_mc_bdc:nx} +% These are the low-level commands. There are now equal to the +% pdfmanagement commands generic mode, but we use an indirection +% in case luamode need something else. +% change 04.08.2018: the commands do not check the validity of the arguments or try +% to escape them, this should be done before using them. +% \begin{macrocode} +% #1 tag, #2 properties +\cs_set_eq:NN \@@_mc_bmc:n \pdf_bmc:n +\cs_set_eq:NN \@@_mc_emc: \pdf_emc: +\cs_set_eq:NN \@@_mc_bdc:nn \pdf_bdc:nn +\cs_generate_variant:Nn \@@_mc_bdc:nn {nx} +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \@@_mc_bdc_mcid:nn,\@@_mc_bdc_mcid:n, +% \@@_mc_handle_mcid:nn,\@@_mc_handle_mcid:VV +% } +% +% This create a BDC mark with an |/MCID| key. Most of the work here is to get +% the current number value for the MCID: they must be numbered by page +% starting with 0 and then successively. +% The first argument is the tag, e.g. |P| or |Span|, the second is used to pass +% more properties. +% We also define a wrapper around the low-level command as luamode will need +% something different. +% \begin{macrocode} +\cs_new_protected:Npn \@@_mc_bdc_mcid:nn #1 #2 + { + \int_gincr:N \c@g_@@_MCID_abs_int + \tl_set:Nx \l_@@_mc_ref_abspage_tl + { + \@@_ref_value:enn %3 args + { + mcid-\int_use:N \c@g_@@_MCID_abs_int + } + { tagabspage } + {-1} + } + \prop_get:NoNTF + \g_@@_MCID_byabspage_prop + { + \l_@@_mc_ref_abspage_tl + } + \l_@@_mc_tmpa_tl + { + %key already present, use value for MCID and add 1 for the next + \int_gset:Nn \g_@@_MCID_tmp_bypage_int { \l_@@_mc_tmpa_tl } + \@@_prop_gput:Nxx + \g_@@_MCID_byabspage_prop + { \l_@@_mc_ref_abspage_tl } + { \int_eval:n {\l_@@_mc_tmpa_tl +1} } + } + { + %key not present, set MCID to 0 and insert 1 + \int_gzero:N \g_@@_MCID_tmp_bypage_int + \@@_prop_gput:Nxx + \g_@@_MCID_byabspage_prop + { \l_@@_mc_ref_abspage_tl } + {1} + } + \@@_ref_label:en + { + mcid-\int_use:N \c@g_@@_MCID_abs_int + } + { mc } + \@@_mc_bdc:nx + {#1} + { /MCID~\int_eval:n { \g_@@_MCID_tmp_bypage_int }~ \exp_not:n { #2 } } + } +\cs_new_protected:Npn \@@_mc_bdc_mcid:n #1 + { + \@@_mc_bdc_mcid:nn {#1} {} + } + +\cs_new_protected:Npn \@@_mc_handle_mcid:nn #1 #2 %#1 tag, #2 properties + { + \@@_mc_bdc_mcid:nn {#1} {#2} + } + +\cs_generate_variant:Nn \@@_mc_handle_mcid:nn {VV} +% \end{macrocode} +% \end{macro} + +% \begin{macro}{\@@_mc_handle_stash:n,\@@_mc_handle_stash:x} +% This is the handler which puts a mc into the +% the current structure. The argument is the number of the mc. +% Beside storing the mc into the structure, it also has to record the +% structure for the parent tree. +% The name is a bit confusing, it does \emph{not} handle mc with the stash key +% \ldots. +% TODO: why does luamode use it for begin + use, but generic mode only for begin? +% \begin{macrocode} +\cs_new_protected:Npn \@@_mc_handle_stash:n #1 %1 mcidnum + { + \@@_check_mc_used:n {#1} + \@@_struct_kid_mc_gput_right:nn + { \g_@@_struct_stack_current_tl } + {#1} + \prop_gput:Nxx \g_@@_mc_parenttree_prop + {#1} + { \g_@@_struct_stack_current_tl } + } +\cs_generate_variant:Nn \@@_mc_handle_stash:n { x } +% \end{macrocode} +% \end{macro} + +% \begin{macro} +% { +% \@@_mc_bmc_artifact:, +% \@@_mc_bmc_artifact:n, +% \@@_mc_handle_artifact:N +% } +% Two commands to create artifacts, one without type, and one with. +% We define also a wrapper handler as luamode will need a different definition. +% TODO: perhaps later: more properties for artifacts +% \begin{macrocode} +\cs_new_protected:Npn \@@_mc_bmc_artifact: + { + \@@_mc_bmc:n {Artifact} + } +\cs_new_protected:Npn \@@_mc_bmc_artifact:n #1 + { + \@@_mc_bdc:nn {Artifact}{/Type/#1} + } +\cs_new_protected:Npn \@@_mc_handle_artifact:N #1 + % #1 is a var containing the artifact type + { + \tl_if_empty:NTF #1 + { \@@_mc_bmc_artifact: } + { \exp_args:NV\@@_mc_bmc_artifact:n #1 } + } +% \end{macrocode} +% \end{macro} + +% \begin{macro}{ \@@_get_data_mc_tag: } +% This allows to retrieve the active mc-tag. +% It is use by the get command. +% \begin{macrocode} +\cs_new:Nn \@@_get_data_mc_tag: { \g_@@_mc_key_tag_tl } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\tag_mc_begin:n,\tag_mc_end:} +% These are the core public commands to open and close an mc. +% They don't need to be in the same group or grouping level, +% but the code expect that they are issued linearly. The tag and +% the state is passed to the end command through a global var and +% a global boolean. +% \begin{macrocode} +\cs_new_protected:Npn \tag_mc_begin:n #1 %#1 keyval + { + \@@_check_if_active_mc:T + { + \group_begin: %hm + \@@_check_mc_if_nested: + \bool_gset_true:N \g_@@_in_mc_bool + \keys_set:nn { @@ / mc } {#1} + \bool_if:NTF \l_@@_mc_artifact_bool + { %handle artifact + \@@_mc_handle_artifact:N \l_@@_mc_artifact_type_tl + } + { %handle mcid type + \@@_check_mc_tag:N \l_@@_mc_key_tag_tl + \@@_mc_handle_mcid:VV + \l_@@_mc_key_tag_tl + \l_@@_mc_key_properties_tl + \tl_if_empty:NF {\l_@@_mc_key_label_tl} + { + \exp_args:NV + \@@_mc_handle_mc_label:n \l_@@_mc_key_label_tl + } + \bool_if:NF \l_@@_mc_key_stash_bool + { + \@@_mc_handle_stash:x { \int_use:N \c@g_@@_MCID_abs_int } + } + } + \group_end: + } + } +\cs_new_protected:Nn \tag_mc_end: + { + \@@_check_if_active_mc:T + { + \@@_check_mc_if_open: + \bool_gset_false:N \g_@@_in_mc_bool + \tl_gset:Nn \g_@@_mc_key_tag_tl { } + \@@_mc_emc: + } + } +% \end{macrocode} +% \end{macro} + +% +% \subsection{Keys} +% Definitions are different in luamode. +% |tag| and |raw| are expanded as |\lua_now:e| in lua does it too and +% we assume that their values are safe. +% \begin{macro} +% { +% tag,raw, +% alttext,alttext-o, +% actualtext,actualtext-o, +% label,artifact +% } +% \begin{macrocode} +\keys_define:nn { @@ / mc } + { + tag .code:n = % the name (H,P,Span) etc + { + \tl_set:Nx \l_@@_mc_key_tag_tl { #1 } + \tl_gset:Nx \g_@@_mc_key_tag_tl { #1 } + }, + raw .code:n = + { + \tl_put_right:Nx \l_@@_mc_key_properties_tl { #1 } + }, + alttext .code:n = % Alt property + { + \str_set_convert:Nnon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \tl_put_right:Nn \l_@@_mc_key_properties_tl { /Alt~< } + \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } + }, + alttext-o .code:n = % Alt property + { + \str_set_convert:Noon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \tl_put_right:Nn \l_@@_mc_key_properties_tl { /Alt~< } + \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } + }, + actualtext .code:n = % ActualText property + { + \str_set_convert:Nnon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \tl_put_right:Nn \l_@@_mc_key_properties_tl { /ActualText~< } + \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } + }, + actualtext-o .code:n = % ActualText property + { + \str_set_convert:Noon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \tl_put_right:Nn \l_@@_mc_key_properties_tl { /ActualText~< } + \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } + }, + label .tl_set:N = \l_@@_mc_key_label_tl, + artifact .code:n = + { + \exp_args:Nnx + \keys_set:nn + { @@ / mc } + { __artifact-bool, __artifact-type=#1 } + }, + artifact .default:n = {notype} + } +%</generic> +% \end{macrocode} +% \end{macro} +% \end{implementation} +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc-luacode.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc-luacode.dtx new file mode 100644 index 00000000000..aae817c9eb9 --- /dev/null +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc-luacode.dtx @@ -0,0 +1,490 @@ +% \iffalse meta-comment +% +%% File: tagpdf-mc.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. +%<*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-mc module (tagpdf)} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% \title{^^A +% The \pkg{tagpdf-mc-luacode} module\\ Code related to Marked Content (mc-chunks), luamode-specific ^^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} +% The code is splitted into three parts: code shared by all engines, +% code specific to luamode and code not used by luamode. +% +% \section{Marked content code -- luamode code} +% luamode uses attributes to mark mc-chunks. The two attributes used are +% defined in the backend file. The backend also load the lua file, as it can contain +% functions needed elsewhere. +% The attributes for mc are global (between 0.6 and 0.81 they were local but this +% was reverted). The attributes are setup only in lua, and one should use +% the lua functions to set and get them.\\ +% |g_@@_mc_type_attr|: the value represent the type\\ +% |g_@@_mc_cnt_attr|: will hold the |\c@g_@@_MCID_abs_int value| +% +% Handling attribute needs a different system to number the page wise mcid's: +% a |\tagmcbegin ... \tagmcend| pair no longer surrounds exactly one mc chunk: +% it can be split at page breaks. +% We know the included mcid(s) only after the ship out. +% So for the |struct -> mcid| mapping we +% need to record |struct -> mc-cnt| (in |\g_@@_mc_parenttree_prop| and/or a lua table +% and at shipout |mc-cnt-> {mcid, mcid, ...}| +% and when building the trees connect both. +% +% Key definitions are overwritten for luatex to store that data in lua-tables. +% The data for the mc are in |ltx.@@.mc[absnum]|. +% The fields of the table are:\\ +% tag : the type (a string)\\ +% raw : more properties (string)\\ +% label: a string. \\ +% artifact: the presence indicates an artifact, the value (string) is the type.\\ +% kids: a array of tables\\ +% |{1={kid=num2,page=pagenum1}, 2={kid=num2,page=pagenum2},...}|,\\ +% this describes the chunks the mc has been split to by the traversing code\\ +% parent: the number of the structure it is in. Needed to build the parent tree. +% +% \begin{macrocode} +%<@@=tag> +%<*luamode> +\ProvidesExplPackage {tagpdf-mc-code-lua} {2021-06-29} {0.9} + {tagpdf - mc code only for the luamode } +%</luamode> +% \end{macrocode} +% The main function which wanders through the shipout box to inject the literals. +% if the new callback is there, it is used. +% \begin{macrocode} +%<*luamode> +\hook_gput_code:nnn{begindocument}{tagpdf/mc} + { + \bool_if:NT\g__tag_active_space_bool + { + \lua_now:e + { + if~luatexbase.callbacktypes.pre_shipout_filter~then~ + luatexbase.add_to_callback("pre_shipout_filter", function(TAGBOX)~ + ltx.@@.func.space_chars_shipout(TAGBOX)~return~true~ + end, "tagpdf")~ + end + } + \lua_now:e + { + if~luatexbase.callbacktypes.pre_shipout_filter~then~ + token.get_next()~ + end + }\@secondoftwo\@gobble + { + \hook_gput_code:nnn{shipout/before}{tagpdf/lua} + { + \lua_now:e + { ltx.@@.func.space_chars_shipout (tex.box["ShipoutBox"]) } + } + } + } + \bool_if:NT\g__tag_active_mc_bool + { + \lua_now:e + { + if~luatexbase.callbacktypes.pre_shipout_filter~then~ + luatexbase.add_to_callback("pre_shipout_filter", function(TAGBOX)~ + ltx.@@.func.mark_shipout(TAGBOX)~return~true~ + end, "tagpdf")~ + end + } + \lua_now:e + { + if~luatexbase.callbacktypes.pre_shipout_filter~then~ + token.get_next()~ + end + }\@secondoftwo\@gobble + { + \hook_gput_code:nnn{shipout/before}{tagpdf/lua} + { + \lua_now:e + { ltx.@@.func.mark_shipout (tex.box["ShipoutBox"]) } + } + } + } + } +% \end{macrocode} +% \subsection{Commands} +% \begin{macro}{\@@_mc_if_in:,\tag_mc_if_in:} +% This tests, if we are in an mc, for attributes +% this means to check against a number. +% \begin{macrocode} +\prg_new_conditional:Nnn \@@_mc_if_in: {p,T,F,TF} + { + \int_compare:nNnTF + { -2147483647 } + = + {\lua_now:e + { + tex.print(tex.getattribute(luatexbase.attributes.g__tag_mc_type_attr)) + } + } + { \prg_return_false: } + { \prg_return_true: } + } + +\prg_new_eq_conditional:NNn \tag_mc_if_in: \@@_mc_if_in: {p,T,F,TF} +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \@@_mc_lua_set_mc_type_attr:n +% ,\@@_mc_lua_set_mc_type_attr:o +% ,\@@_mc_lua_unset_mc_type_attr: +% } +% This takes a tag name, and sets the attributes to the related number. +% It is not decided yet if this will be global or local, see the global-mc option. +% \begin{macrocode} +\cs_new:Nn \@@_mc_lua_set_mc_type_attr:n % #1 is a tag name + { + %TODO ltx.@@.func.get_num_from("#1") seems not to return a suitable number?? + \tl_set:Nx\l_@@_tmpa_tl{\lua_now:e{ltx.@@.func.output_num_from ("#1")} } + \lua_now:e + { + tex.setattribute + ( + "global", + luatexbase.attributes.g_@@_mc_type_attr, + \l_@@_tmpa_tl + ) + } + \lua_now:e + { + tex.setattribute + ( + "global", + luatexbase.attributes.g_@@_mc_cnt_attr, + \@@_get_mc_abs_cnt: + ) + } + } + +\cs_generate_variant:Nn\@@_mc_lua_set_mc_type_attr:n { o } + +\cs_new:Nn \@@_mc_lua_unset_mc_type_attr: + { + \lua_now:e + { + tex.setattribute + ( + "global", + luatexbase.attributes.g_@@_mc_type_attr, + -2147483647 + ) + } + \lua_now:e + { + tex.setattribute + ( + "global", + luatexbase.attributes.g_@@_mc_cnt_attr, + -2147483647 + ) + } + } + +% \end{macrocode} +% \end{macro} +% \begin{macro} +% { +% \@@_mc_insert_mcid_kids:n +% ,\@@_mc_insert_mcid_single_kids:n +% } +% These commands will in the finish code replace the dummy for a mc by the real mcid kids +% we need a variant for the case that it is the only kid, to get the array right +% \begin{macrocode} +\cs_new:Nn \@@_mc_insert_mcid_kids:n + { + \lua_now:e { ltx.@@.func.mc_insert_kids (#1,0) } + } + +\cs_new:Nn \@@_mc_insert_mcid_single_kids:n + { + \lua_now:e {ltx.@@.func.mc_insert_kids (#1,1) } + } +% \end{macrocode} +% \end{macro} +% +%\begin{macro}{\@@_mc_handle_stash:n,\@@_mc_handle_stash:x} +% This is the lua variant for the command to +% put an mcid absolute number in the current structure. +% \begin{macrocode} +\cs_new:Nn \@@_mc_handle_stash:n %1 mcidnum + { + \@@_check_mc_used:n { #1 } + \seq_gput_right:cn % Don't fill a lua table due to the command in the item, + % so use the kernel command + { g_@@_struct_kids_\g_@@_struct_stack_current_tl _seq } + { + \@@_mc_insert_mcid_kids:n {#1}% + } + \lua_now:e + { + ltx.@@.func.store_struct_mcabs + ( + \g_@@_struct_stack_current_tl,#1 + ) + } + \prop_gput:Nxx + \g_@@_mc_parenttree_prop + { #1 } + { \g_@@_struct_stack_current_tl } + } + +\cs_generate_variant:Nn \@@_mc_handle_stash:n { x } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\tag_mc_begin:n} +% This is the lua version of the user command. +% We currently don't check if there is nesting as it doesn't matter so +% much in lua. +% \begin{macrocode} +\cs_new_protected:Nn \tag_mc_begin:n + { + \@@_check_if_active_mc:T + { + \group_begin: + %\@@_check_mc_if_nested: + \bool_gset_true:N \g_@@_in_mc_bool + \bool_set_false:N\l_@@_mc_artifact_bool + \tl_clear:N \l_@@_mc_key_properties_tl + \int_gincr:N \c@g_@@_MCID_abs_int + \keys_set:nn { @@ / mc }{ label={}, #1 } + %check that a tag or artifact has been used + \@@_check_mc_tag:N \l_@@_mc_key_tag_tl + %set the attributes: + \@@_mc_lua_set_mc_type_attr:o { \l_@@_mc_key_tag_tl } + \bool_if:NF \l_@@_mc_artifact_bool + { % store the absolute num name in a label: + \tl_if_empty:NF {\l_@@_mc_key_label_tl} + { + \exp_args:NV + \@@_mc_handle_mc_label:n \l_@@_mc_key_label_tl + } + % if not stashed record the absolute number + \bool_if:NF \l_@@_mc_key_stash_bool + { + \@@_mc_handle_stash:x { \@@_get_mc_abs_cnt: } + } + } + \group_end: + } + } +% \end{macrocode} +% \end{macro} +% \begin{macro}{\tag_mc_end:} +% +% TODO: check how the use command must be guarded. +% \begin{macrocode} +\cs_new_protected:Nn \tag_mc_end: + { + \@@_check_if_active_mc:T + { + %\@@_check_mc_if_open: + \bool_gset_false:N \g_@@_in_mc_bool + \bool_set_false:N\l_@@_mc_artifact_bool + \@@_mc_lua_unset_mc_type_attr: + \tl_set:Nn \l_@@_mc_key_tag_tl { } + \tl_gset:Nn \g_@@_mc_key_tag_tl { } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{ \@@_get_data_mc_tag: } +% The command to retrieve the current mc tag. +% TODO: Perhaps this should use the attribute instead. +% \begin{macrocode} +\cs_new:Npn \@@_get_data_mc_tag: { \g_@@_mc_key_tag_tl } +% \end{macrocode} +% \end{macro} + +% \subsection{Key definitions} +% \begin{macro} +% { +% tag,raw, +% alttext,alttext-o, +% actualtext,actualtext-o, +% label,artifact +% } +% TODO: check conversion, check if local/global setting is right. +% \begin{macrocode} +\keys_define:nn { @@ / mc } + { + tag .code:n = % + { + \tl_set:Nx \l_@@_mc_key_tag_tl { #1 } + \tl_gset:Nx \g_@@_mc_key_tag_tl { #1 } + \lua_now:e + { + ltx.@@.func.store_mc_data(\@@_get_mc_abs_cnt:,"tag","#1") + } + }, + raw .code:n = + { + \tl_put_right:Nx \l_@@_mc_key_properties_tl { #1 } + \lua_now:e + { + ltx.@@.func.store_mc_data(\@@_get_mc_abs_cnt:,"raw","#1") + } + }, + alttext .code:n = % Alt property + { + \str_set_convert:Nnon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \tl_put_right:Nn \l_@@_mc_key_properties_tl { /Alt~< } + \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } + \lua_now:e + { + ltx.@@.func.store_mc_data + ( + \@@_get_mc_abs_cnt:,"alt","/Alt~<\str_use:N \l_@@_tmpa_str>" + ) + } + }, + alttext-o .code:n = % Alt property + { + \str_set_convert:Noon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \tl_put_right:Nn \l_@@_mc_key_properties_tl { /Alt~< } + \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } + \lua_now:e + { + ltx.@@.func.store_mc_data + ( + \@@_get_mc_abs_cnt:,"alt","/Alt~<\str_use:N \l_@@_tmpa_str>" + ) + } + }, + actualtext .code:n = % Alt property + { + \str_set_convert:Nnon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \tl_put_right:Nn \l_@@_mc_key_properties_tl { /Alt~< } + \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } + \lua_now:e + { + ltx.@@.func.store_mc_data + ( + \@@_get_mc_abs_cnt:,"actualtext","/ActualText~<\str_use:N \l_@@_tmpa_str>" + ) + } + }, + actualtext-o .code:n = % Alt property + { + \str_set_convert:Noon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \tl_put_right:Nn \l_@@_mc_key_properties_tl { /Alt~< } + \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } + \lua_now:e + { + ltx.@@.func.store_mc_data + ( + \@@_get_mc_abs_cnt:, + "actualtext", + "/ActualText~<\str_use:N \l_@@_tmpa_str>" + ) + } + }, + label .code:n = + { + \tl_set:Nn\l_@@_mc_key_label_tl { #1 } + \lua_now:e + { + ltx.@@.func.store_mc_data + ( + \@@_get_mc_abs_cnt:,"label","#1" + ) + } + }, + __artifact-store .code:n = + { + \lua_now:e + { + ltx.@@.func.store_mc_data + ( + \@@_get_mc_abs_cnt:,"artifact","#1" + ) + } + }, + artifact .code:n = + { + \exp_args:Nnx + \keys_set:nn + { @@ / mc} + { __artifact-bool, __artifact-type=#1, tag=Artifact } + \exp_args:Nnx + \keys_set:nn + { @@ / mc } + { __artifact-store=\l_@@_mc_artifact_type_tl } + }, + artifact .default:n = { notype } + } + +%</luamode> +% \end{macrocode} +% \end{macro} +% +% \end{implementation} +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc-shared.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc-shared.dtx new file mode 100644 index 00000000000..12680109a05 --- /dev/null +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc-shared.dtx @@ -0,0 +1,442 @@ +% \iffalse meta-comment +% +%% File: tagpdf-mc.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. +%<*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-mc module (tagpdf)} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% \title{^^A +% The \pkg{tagpdf-mc-shared} module\\ Code related to Marked Content (mc-chunks), code shared by all modes ^^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{documentation} +% \section{Public Commands} +% \begin{function}{\tag_mc_begin:n,\tag_mc_end:} +% \begin{syntax} +% \cs{tag_mc_begin:n}\Arg{key-values}\\ +% \cs{tag_mc_end:} +% \end{syntax} +% These commands insert the end code of the marked content. +% They don't end a group and in generic mode it doesn't matter +% if they are in another group as the starting commands. +% In generic mode both commands check if they are correctly nested +% and issue a warning if not. +% \end{function} +% +% \begin{function}{\tag_mc_use:n} +% \begin{syntax} +% \cs{tag_mc_use:n}\Arg{label} +% \end{syntax} +% These command allow to record a marked content that was stashed away before +% into the current structure. A marked content can be used only once -- +% the command will issue a warning if an mc is use a second time. +% \end{function} +% +% \begin{function}[added = 2019-11-20] +% { +% \tag_mc_artifact_group_begin:n, \tag_mc_artifact_group_end: +% } +% \begin{syntax} +% \cs{tag_mc_artifact_group_begin:n} \Arg{name}\\ +% \cs{tag_mc_artifact_group_end:} +% \end{syntax} +% This command pair creates a group with an artifact marker at the begin +% and the end. Inside the group the tagging commands are disabled. +% It allows to mark a complete region as artifact without having to worry +% about user commands with tagging commands. +% \meta{name} should be a value allowed also for the |artifact| key. +% It pushes and pops mc-chunks at the begin and end. +% TODO: document is in tagpdf.tex +% \end{function} +% +% \begin{function}[added = 2021-04-22] +% { +% \tag_mc_end_push:, \tag_mc_begin_pop:n +% } +% \begin{syntax} +% \cs{tag_mc_end_push:} \\ +% \cs{tag_mc_begin_pop:n}\Arg{key-values} +% \end{syntax} +% If there is an open mc chunk, +% \cs{tag_mc_end_push:} ends it and pushes its tag of the (global) stack. +% If there is no open chunk, it puts $-1$ on the stack (for debugging) +% \cs{tag_mc_begin_pop:n} removes a value from the stack. If it is different from +% $-1$ it opens a tag with it. +% The reopened mc chunk looses info like the alttext for now. +% \end{function} +% +% \begin{function}[pTF, EXP]{\tag_mc_if_in:} +% \begin{syntax} +% \cs{tag_mc_if_in:TF} \Arg{true code} \Arg{false code} +% \end{syntax} +% Determines if a mc-chunk is open. +% \end{function} + +% +% \section{Public keys} +% The following keys can be used with |\tag_mc_begin:n|, |\tagmcbegin|, +% |\tag_mc_begin_pop:n|, +% +% \begin{function}{tag} +% This key is required, unless artifact is used. +% The value is a tag like |P| or |H1| without a slash at the begin, +% this is added by the code. +% It is possible to setup new tags. +% The value of the key is expanded, so it can be a command. +% The expansion is passed unchanged to the PDF, +% so it should with a starting slash give a valid PDF name +% (some ascii with numbers like \texttt{H4} is fine). +% \end{function} +% +% \begin{function}{artifact} +% This will setup the marked content as an artifact. The key should be used +% for content that should be ignored. +% The key can take one of the values |pagination|, +% |layout|, |page|, |background| and |notype| +% (this is the default). +% \end{function} +% +% \begin{function}{raw} +% This key allows to add more entries to the properties dictionary. +% The value must be correct, low-level PDF. E.g. +% \verb+raw=/Alt (Hello)+ will insert an alternative Text. +% \end{function} +% +% \begin{function}{alttext,alttext-o} +% This key inserts an \texttt{/Alt} value in the property dictionary of the BDC operator. +% The value is handled as verbatim string, commands are not expanded. With |alttext-o| +% the value is expanded once. +% \end{function} +% +% \begin{function}{actualtext,actualtext-o} +% This key inserts an \texttt{/ActualText} value in the property dictionary +% of the BDC operator. The value is handled as verbatim string, +% commands are not expanded.With |actualtext-o| +% the value is expanded once. +% \end{function} +% +% \begin{function}{label} +% This key sets a label by which one can call the marked content +% later in another structure +% (if it has been stashed with the |stash| key). +% Internally the label name will start with \texttt{tagpdf-}. +% \end{function} +% +% \begin{function}{stash} +% This \enquote{stashes} an mc-chunk: it is not inserted into the current structure. +% It should be normally be used along with a label to be able to use the mc-chunk +% in another place. +% \end{function} +% \end{documentation} +% \begin{implementation} +% The code is splitted into three parts: code shared by all engines, +% code specific to luamode and code not used by luamode. +% \section{Marked content code -- shared} +% \begin{macrocode} +%<@@=tag> +%<*header> +\ProvidesExplPackage {tagpdf-mc-code-shared} {2021-06-29} {0.9} + {part of tagpdf - code related to marking chunks - + code shared by generic and luamode } +%</header> +% \end{macrocode} +% +% \subsection{Variables and counters} +% MC chunks must be counted. +% I use a latex counter for the absolute count, so that it is added to +% |\cl@@ckpt| and restored e.g. in tabulars and align. +% |\int_new:N \c@g_@@_MCID_int| and +% |\tl_put_right:Nn\cl@@ckpt{\@elt{g_uf_test_int}}| +% would work too, but as the name is not expl3 then too, why bother? +% The absolute counter can be used to label and to check if the page +% counter needs a reset. +% +% \begin{variable}{g_@@_MCID_abs_int} +% \begin{macrocode} +%<*shared> +\newcounter { g_@@_MCID_abs_int } +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\_@@_get_mc_abs_cnt:} +% A (expandable) function to get the current value of the cnt. +% \begin{macrocode} +\cs_new:Npn \_@@_get_mc_abs_cnt: { \int_use:N \c@g_@@_MCID_abs_int } +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\g_@@_MCID_tmp_bypage_int} +% The following hold the temporary by page number assigned to a mc. +% It must be defined in the shared code to avoid problems with labels. +% \begin{macrocode} +\int_new:N \g_@@_MCID_tmp_bypage_int +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_@@_mc_parenttree_prop} +% For every chunk we need to know the structure it is in, to +% record this in the parent tree. We store this in a property.\\ +% key: absolute number of the mc (tagmcabs)\\ +% value: the structure number the mc is in +% \begin{macrocode} +\@@_prop_new:N \g_@@_mc_parenttree_prop +% \end{macrocode} +% \end{variable} +% +%\begin{variable}{\g_@@_mc_parenttree_prop} +% Some commands (e.g. links) want to close a previous mc and reopen it after +% they did their work. For this we create a stack: +% \begin{macrocode} +\seq_new:N \g_@@_mc_stack_seq +% \end{macrocode} +% \end{variable} +% +%\begin{variable}{\l_@@_mc_artifact_type_tl} +% Artifacts can have various types like Pagination or Layout. This stored +% in this variable. +% \begin{macrocode} +\tl_new:N \l_@@_mc_artifact_type_tl +% \end{macrocode} +% \end{variable} +% +%\begin{variable}{\l_@@_mc_key_stash_bool,\l_@@_mc_artifact_bool} +%This booleans store the stash and artifact status of the mc-chunk. +% \begin{macrocode} +\bool_new:N \l_@@_mc_key_stash_bool +\bool_new:N \l_@@_mc_artifact_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% { +% \l_@@_mc_key_tag_tl, +% \g_@@_mc_key_tag_tl, +% \l_@@_mc_key_label_tl, +% \l_@@_mc_key_properties_tl +% } +% Variables used by the keys. |\l_@@_mc_key_properties_tl| +% will collect a number of values. TODO: should this be a pdfdict now? +% \begin{macrocode} +\tl_new:N \l_@@_mc_key_tag_tl +\tl_new:N \g_@@_mc_key_tag_tl +\tl_new:N \l_@@_mc_key_label_tl +\tl_new:N \l_@@_mc_key_properties_tl +% \end{macrocode} +% \end{variable} +% +% \subsection{Functions} +% \begin{macro}{\_@@_mc_handle_mc_label:n} +% The commands labels a mc-chunk. It is used if the user explicitly +% labels the mc-chunk with the |label| key. The argument is the +% value provided by the user. It stores the attributes\\ +% |tagabspage|: the absolute page, |\g_shipout_readonly_int|,\\ +% |tagmcabs|: the absolute mc-counter |\c@g_@@_MCID_abs_int|,\\ +% |tagmcid|: the ID of the chunk on the page |\g_@@_MCID_tmp_bypage_int|, this +% typically settles down after a second compilation. +% The reference command is defined in tagpdf.dtx and is based on l3ref. +% \begin{macrocode} +\cs_new:Nn \_@@_mc_handle_mc_label:n + { + \@@_ref_label:en{tagpdf-#1}{mc} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_mc_set_label_used:n} +% Unlike with structures we can't check if a labeled mc has been used by +% looking at the P key, so we use a dedicated csname for the test +% \begin{macrocode} +\cs_new_protected:Npn \@@_mc_set_label_used:n #1 %#1 labelname + { + \tl_new:c { g_@@_mc_label_\tl_to_str:n{#1}_used_tl } + } +% \end{macrocode} +% +% \end{macro} +% \begin{macro}{\tag_mc_use:n} +% These command allow to record a marked content that was stashed away before +% into the current structure. A marked content can be used only once -- +% the command will issue a warning if an mc is use a second time. +% The argument is a label name set with the |label| key. +% +% TODO: is testing for struct the right test? +% \begin{macrocode} +\cs_new_protected:Npn \tag_mc_use:n #1 %#1: label name + { + \@@_check_if_active_struct:T + { + \tl_set:Nx \l_@@_tmpa_tl { \@@_ref_value:nnn{tagpdf-#1}{tagmcabs}{} } + \tl_if_empty:NTF\l_@@_tmpa_tl + { + \msg_warning:nnn {tag} {mc-label-unknown} {#1} + } + { + \cs_if_free:cTF { g_@@_mc_label_\tl_to_str:n{#1}_used_tl } + { + \@@_mc_handle_stash:x { \l_@@_tmpa_tl } + \@@_mc_set_label_used:n {#1} + } + { + \msg_warning:nnn {tag}{mc-used-twice}{#1} + } + } + } + } +% \end{macrocode} +% \end{macro} +% \begin{macro} +% { +% \tag_mc_artifact_group_begin:n, +% \tag_mc_artifact_group_end: +% } +% This opens an artifact of the type given in the argument, +% and then stops all tagging. It creates a group. +% It pushes and pops mc-chunks at the begin and end. +% \begin{macrocode} +\cs_new_protected:Npn \tag_mc_artifact_group_begin:n #1 + { + \tag_mc_end_push: + \tag_mc_begin:n {artifact=#1} + \tag_stop_group_begin: + } + +\cs_new_protected:Npn \tag_mc_artifact_group_end: + { + \tag_stop_group_end: + \tag_mc_end: + \tag_mc_begin_pop:n{} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\tag_mc_end_push:, \tag_mc_begin_pop:n} +% +% \begin{macrocode} +\cs_new_protected:Npn \tag_mc_end_push: + { + \@@_check_if_active_mc:T + { + \@@_mc_if_in:TF + { + \seq_gpush:Nx \g_@@_mc_stack_seq { \tag_get:n {mc_tag} } + \@@_check_mc_pushed_popped:nn + { pushed } + { \tag_get:n {mc_tag} } + \tag_mc_end: + } + { + \seq_gpush:Nn \g_@@_mc_stack_seq {-1} + \@@_check_mc_pushed_popped:nn { pushed }{-1} + } + } + } + +\cs_new_protected:Npn \tag_mc_begin_pop:n #1 + { + \@@_check_if_active_mc:T + { + \seq_gpop:NNTF \g_@@_mc_stack_seq \l_@@_tmpa_tl + { + \tl_if_eq:NnTF \l_@@_tmpa_tl {-1} + { + \@@_check_mc_pushed_popped:nn {popped}{-1} + } + { + \@@_check_mc_pushed_popped:nn {popped}{\l_@@_tmpa_tl} + \tag_mc_begin:n {tag=\l_@@_tmpa_tl,#1} + } + } + { + \@@_check_mc_pushed_popped:nn {popped}{empty~stack,~nothing} + } + } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Keys} +% This are the keys where the code can be shared between the modes. +% +% \begin{macro}{stash,__artifact-bool,__artifact-type} +% the two internal artifact keys are use to define the public |artifact|. +% \begin{macrocode} +\keys_define:nn { @@ / mc } + { + stash .bool_set:N = \l_@@_mc_key_stash_bool, + __artifact-bool .bool_set:N = \l_@@_mc_artifact_bool, + __artifact-type .choice:, + __artifact-type / pagination .code:n = + { + \tl_set:Nn \l_@@_mc_artifact_type_tl { Pagination } + }, + __artifact-type / layout .code:n = + { + \tl_set:Nn \l_@@_mc_artifact_type_tl { Layout } + }, + __artifact-type / page .code:n = + { + \tl_set:Nn \l_@@_mc_artifact_type_tl { Page } + }, + __artifact-type / background .code:n = + { + \tl_set:Nn \l_@@_mc_artifact_type_tl { Background } + }, + __artifact-type / notype .code:n = + { + \tl_set:Nn \l_@@_mc_artifact_type_tl {} + }, + __artifact-type / .code:n = + { + \tl_set:Nn \l_@@_mc_artifact_type_tl {} + }, + } +% \end{macrocode} +% \end{macro} +% \begin{macrocode} +%</shared> +% \end{macrocode} +% \end{implementation} +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc.dtx deleted file mode 100644 index 4842c8b9931..00000000000 --- a/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc.dtx +++ /dev/null @@ -1,818 +0,0 @@ -% \iffalse meta-comment -% -%% File: tagpdf-mc.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> -%<*shared> -\ProvidesExplPackage {tagpdf-mc-code-shared} {2021-06-14} {0.82} - {part of tagpdf - code related to marking chunks - - code shared by generic and luamode } -% I use a latex counter for the absolute count, so that it is added to -% \cl@@ckpt and restored e.g. in tabulars and align -% \int_new:N \c@g_@@_MCID_int and -% \tl_put_right:Nn\cl@@ckpt{\@elt{g_uf_test_int}} -% would work too, but as the name is not expl3 then too, why bother? -% the absolute counter can be used to label and to check if the page -% counter needs a reset. - -\newcounter { g_@@_MCID_abs_int } -\cs_new:Nn \_@@_get_mc_abs_cnt: { \int_use:N \c@g_@@_MCID_abs_int } - -% tagmcabs is the label name of the absolute count which is used -% to identify the chunk -% the ref code is now in tagpdf.dtx - -%stores labels of mcid. -\cs_new:Nn \_@@_mc_handle_mc_label:n - { - \@@_ref_label:en{tagpdf-#1}{mc} - } - -% will hold the structure numbers for the parenttree -% key: absolute number of the mc (tagmcabs) -% value: the structure number the mc is in -\_@@_prop_new:N \g_@@_mc_parenttree_prop - -% some commands (e.g. links) want to close a previous mc and reopen it after -% there work. For this we create a stack: -\seq_new:N \g_@@_mc_stack_seq -% shared keys -% the rest are in the splitted code -\tl_new:N \l_@@_mc_artifact_type_tl - -\keys_define:nn { @@ / mc } - { - stash .bool_set:N = \l_@@_mc_key_stash_bool, - artifact-bool .bool_set:N = \l_@@_mc_artifact_bool, - artifact-type .choice:, - artifact-type / pagination .code:n = - { - \tl_set:Nn \l_@@_mc_artifact_type_tl { Pagination } - }, - artifact-type / layout .code:n = - { - \tl_set:Nn \l_@@_mc_artifact_type_tl { Layout } - }, - artifact-type / page .code:n = - { - \tl_set:Nn \l_@@_mc_artifact_type_tl { Page } - }, - artifact-type / background .code:n = - { - \tl_set:Nn \l_@@_mc_artifact_type_tl { Background } - }, - artifact-type / notype .code:n = - { - \tl_set:Nn \l_@@_mc_artifact_type_tl {} - }, - artifact-type / .code:n = - { - \tl_set:Nn \l_@@_mc_artifact_type_tl {} - }, - } -% \end{macrocode} -% \begin{function}[added = 2019-11-20] -% { -% \tag_mc_artifact_group_begin:n, \tag_mc_artifact_group_end: -% } -% \begin{syntax} -% \cs{tag_mc_artifact_group_begin:n} \Arg{name} -% \end{syntax} -% This command pair create a group with an artifact marker at the begin -% and the end. Inside the group the tagging commands are disabled. -% \end{function} -% \begin{macrocode} - - -\cs_new_protected:Npn \tag_mc_artifact_group_begin:n #1 - { - \tag_mc_begin:n {artifact=#1} - \tag_stop_group_begin: - } - -\cs_new_protected:Npn \tag_mc_artifact_group_end: - { - \tag_stop_group_end: - \tag_mc_end: - } - -% we need to define it for both modes to avoid a problem with the label -\int_new:N \g_@@_MCID_tmp_bypage_int - -% \end{macrocode} - -% \begin{function}[added = 2021-04-22] -% { -% \tag_mc_end_push:, \tag_mc_begin_pop:n -% } -% \begin{syntax} -% \cs{tag_mc_end_push:} \\ -% \cs{tag_mc_begin_pop:n}\Arg{key-values} -% \end{syntax} -% If there is an open mc chunk, -% \cs{tag_mc_end_push:} ends it and pushes its tag of the stack. If there is no -% open chunk, it puts -1 on the stack (for debugging) -% \cs{tag_mc_begin_pop:n} removes a value from the stack. If it is different from -% -1 it opens a tag with it. -% The reopened mc chunk looses info like the alttext for now. -% -% \begin{macrocode} -\cs_new_protected:Npn \tag_mc_end_push: - { - \@@_mc_if_in:TF - { - \seq_gpush:Nx \g_@@_mc_stack_seq { \tag_get:n {mc_tag} } - \@@_check_mc_pushed_popped:nn {pushed}{\tag_get:n {mc_tag}} - \tag_mc_end: - } - { - \@@_check_mc_pushed_popped:nn {pushed}{-1} - \seq_gpush:Nn \g_@@_mc_stack_seq{ -1 } - } - } -\cs_new_protected:Npn \tag_mc_begin_pop:n #1 - { - \seq_gpop:NNTF \g_@@_mc_stack_seq \l_@@_tmpa_tl - { - \tl_if_eq:NnTF \l_@@_tmpa_tl {-1} - { - \@@_check_mc_pushed_popped:nn {popped}{-1} - } - { - \@@_check_mc_pushed_popped:nn {popped}{\l_@@_tmpa_tl} - \tag_mc_begin:n {tag=\l_@@_tmpa_tl,#1} - } - } - { \@@_check_mc_pushed_popped:nn {popped}{empty~stack,~nothing} } %message? - } - -%</shared> -% \end{macrocode} -% \begin{macrocode} -%<*generic> -\ProvidesExplPackage {tagpdf-mc-code-generic} {2021-06-14} {0.82} - {part of tagpdf - code related to marking chunks - generic mode} - -% for the label system -% tagmcid is the id which should be also in the pdf -% it will be (hopefully) reset by page - -% ref code is in tagpdf.dtx now -% -% will hold the current maximum on a page -% it will contain key-value of type "abspagenum=max mcid on this page" -\@@_prop_new:N \g_@@_MCID_byabspage_prop - -%to test nesting mc: -\bool_new:N \g_@@_in_mc_bool - -\prg_new_conditional:Nnn \@@_mc_if_in: {p,T,F,TF} - { - \bool_if:NTF \g_@@_in_mc_bool - { \prg_return_true: } - { \prg_return_false: } - } - -\prg_new_eq_conditional:NNn \tag_mc_if_in: \@@_mc_if_in: {p,T,F,TF} - - - -% this are the low level mc command. -% they insert literals and so a are specific to generic mode -% checking if the type is defined will done somewhere else -% #1 is the type/tag -% change 04.08.2018: I don't try to escape the name, but assume that it is valid. -% Checks/conversions should perhaps be done on a higher level -\cs_new_protected:Nn \@@_mc_bmc:n - { - \pdf_bmc:n {#1} -% \@@_pdfliteral_page:n -% { -% /#1\c_space_tl BMC -% } - } - -\cs_new_protected:Nn \@@_mc_emc: - { - \pdf_emc: - %\@@_pdfliteral_page:n -% { -% EMC -% } - } - -% #1 tag, #2 properties -% change 04.08.2018: I don't escape the name. Also the dictionary content -% must imho be done at a higher level -\cs_new_protected:Nn \@@_mc_bdc:nn - { - \pdf_bdc:nn { #1 } { #2 } - } - -\cs_generate_variant:Nn \@@_mc_bdc:nn {nx} -% bdc with /MCID + more properties -% we need a ref-label system to ensure that the cnt restarts at 0 on a new page - -\tl_new:N \l_@@_mc_ref_abspage_tl %will store the abspage value of label -\tl_new:N \l_@@_mc_tmp_tl - -\cs_new:Nn \@@_mc_bdc_mcid:nn - { - \int_gincr:N \c@g_@@_MCID_abs_int - \tl_set:Nx \l_@@_mc_ref_abspage_tl - { - \@@_ref_value:enn %3 args - { - mcid-\int_use:N \c@g_@@_MCID_abs_int - } - { tagabspage } - {-1} - } - \prop_get:NoNTF - \g_@@_MCID_byabspage_prop - { - \l_@@_mc_ref_abspage_tl - } - \l_@@_mc_tmp_tl - { - %key already present, use value for MCID and add 1 for the next - \int_gset:Nn \g_@@_MCID_tmp_bypage_int { \l_@@_mc_tmp_tl } - \@@_prop_gput:Nxx - \g_@@_MCID_byabspage_prop - { \l_@@_mc_ref_abspage_tl } - { \int_eval:n {\l_@@_mc_tmp_tl +1} } - } - { - %key not present, set MCID to 0 and insert 1 - \int_gzero:N \g_@@_MCID_tmp_bypage_int - \@@_prop_gput:Nxx - \g_@@_MCID_byabspage_prop - { \l_@@_mc_ref_abspage_tl } - {1} - } - \@@_ref_label:en - { - mcid-\int_use:N \c@g_@@_MCID_abs_int - } - { mc } - %\exp_args:Nnx - \@@_mc_bdc:nx - {#1} - { /MCID~\int_eval:n { \g_@@_MCID_tmp_bypage_int }~ \exp_not:n { #2 } } - } - -% only /MCID -\cs_new:Nn \@@_mc_bdc_mcid:n - { - \@@_mc_bdc_mcid:nn {#1} {} - } - -%artifact without type -\cs_new:Nn \@@_mc_bmc_artifact: - { - \@@_mc_bmc:n {Artifact} - } - -%artifact with a type: -\cs_new:Nn \@@_mc_bmc_artifact:n - { - \@@_mc_bdc:nn {Artifact}{/Type/#1} - } - -% perhaps later: more properties for artifacts - - -% keyval definitions for the user commands: - -\tl_new:N \l_@@_mc_key_tag_tl -\tl_new:N \g_@@_mc_key_tag_tl %for "outside" queries -\tl_new:N \l_@@_mc_key_properties_tl - -% Attention! definitions are different in luamode. -% tag and raw are expanded as \directlua in lua does it too. -\keys_define:nn { @@ / mc } - { - tag .code:n = % the name (H,P,Spa) etc - { - %%????????? \pdfescapename?? - \tl_set:Nx \l_@@_mc_key_tag_tl { #1 } - \tl_gset:Nx \g_@@_mc_key_tag_tl { #1 } - }, - raw .code:n = - { - \tl_put_right:Nx \l_@@_mc_key_properties_tl { #1 } - }, - alttext .code:n = % Alt property - { - \str_set_convert:Nnon - \l_@@_tmpa_str - { #1 } - { default } - { utf16/hex } - \tl_put_right:Nn \l_@@_mc_key_properties_tl { /Alt~< } - \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } - }, - alttext-o .code:n = % Alt property - { - \str_set_convert:Noon - \l_@@_tmpa_str - { #1 } - { default } - { utf16/hex } - \tl_put_right:Nn \l_@@_mc_key_properties_tl { /Alt~< } - \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } - }, - actualtext .code:n = % ActualText property - { - \str_set_convert:Nnon - \l_@@_tmpa_str - { #1 } - { default } - { utf16/hex } - \tl_put_right:Nn \l_@@_mc_key_properties_tl { /ActualText~< } - \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } - }, - actualtext-o .code:n = % ActualText property - { - \str_set_convert:Noon - \l_@@_tmpa_str - { #1 } - { default } - { utf16/hex } - \tl_put_right:Nn \l_@@_mc_key_properties_tl { /ActualText~< } - \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } - }, - label .tl_set:N = \l_@@_mc_key_label_tl, - artifact .code:n = - { - \exp_args:Nnx - \keys_set:nn - { @@ / mc } - { artifact-bool, artifact-type=#1 } - }, - artifact .default:n = {notype} - } - -\cs_new:Nn \@@_mc_handle_artifact:N %#1 contains the artifact type - { - \tl_if_empty:NTF #1 - { \@@_mc_bmc_artifact: } - { \exp_args:No\@@_mc_bmc_artifact:n {#1} } - } - -\cs_new:Nn \@@_mc_handle_mcid:nn %#1 tag, #2 properties - { - \@@_mc_bdc_mcid:nn {#1} {#2} - } - -\cs_generate_variant:Nn \@@_mc_handle_mcid:nn {VV} - -% puts the absolute number of an mcid in the current structure -\cs_new:Nn \@@_mc_handle_stash:n %1 mcidnum - { - \@@_check_mc_used:n {#1} - \@@_struct_kid_mc_gput_right:nn - { \g_@@_struct_stack_current_tl } - {#1} - \prop_gput:Nxx \g_@@_mc_parenttree_prop - {#1} - { \g_@@_struct_stack_current_tl } - } - -\cs_new_protected:Nn \tag_mc_begin:n - { - \group_begin: %hm - \@@_check_mc_if_nested: - \bool_gset_true:N \g_@@_in_mc_bool - \keys_set:nn { @@ / mc } {#1} - \bool_if:NTF \l_@@_mc_artifact_bool - { %handle artifact - \@@_mc_handle_artifact:N \l_@@_mc_artifact_type_tl - } - { %handle mcid type - \@@_check_mc_tag:N \l_@@_mc_key_tag_tl - \@@_mc_handle_mcid:VV - \l_@@_mc_key_tag_tl - \l_@@_mc_key_properties_tl - \tl_if_empty:NF {\l_@@_mc_key_label_tl} - { - \@@_mc_handle_mc_label:n { \l_@@_mc_key_label_tl } - } - \bool_if:NF \l_@@_mc_key_stash_bool - { - \@@_mc_handle_stash:n { \int_use:N \c@g_@@_MCID_abs_int } - } - } - \group_end: - } - - -\cs_new_protected:Nn \tag_mc_end: - { - \@@_check_mc_if_open: - \bool_gset_false:N \g_@@_in_mc_bool - \tl_gset:Nn \g_@@_mc_key_tag_tl { } - \@@_mc_emc: - } - - - -\cs_new_protected:Nn \tag_mc_use:n %#1: label name - { - \tl_set:Nx \l_tmpa_tl { \@@_ref_value:enn{tagpdf-#1}{tagmcabs}{} } - \tl_if_empty:NTF\l_tmpa_tl - { - \msg_warning:nnn {tag} {mc-label-unknown} {#1} - } - { - \prop_gput:Nxx - \g_@@_mc_parenttree_prop - { - \@@_ref_value:enn {tagpdf-#1} {tagmcabs} {} - } - { - \g_@@_struct_stack_current_tl - } - \@@_struct_kid_mc_gput_right:nn - { - \g_@@_struct_stack_current_tl - } - { - \@@_ref_value:enn {tagpdf-#1} {tagmcabs} {} - } - } - } - - -\cs_new:Nn \@@_get_data_mc_tag: { \g_@@_mc_key_tag_tl } -%</generic> -% \end{macrocode} -% \begin{macrocode} -%<*luamode> -\ProvidesExplPackage {tagpdf-mc-code-lua} {2021-06-14} {0.82} - {tagpdf - mc code only for the luamode } - -% the two attibutes are defined in the driver file. -% it also load the lua (as it can also contain functions needed by generic mode. -% (new) attributes for mc are local: -% \newattribute \l_@@_mc_type_attr %the value represent the type -% \newattribute \l_@@_mc_cnt_attr %will hold the \c@g_@@_MCID_abs_int value - - -% An attribute for the current structure probably doesn't make sense as mc chunks can be used later. -% \newattribute \g_@@_struct_type_attr %represent the current structure type. Not sure if needed -% \newattribute \g_@@_struct_cnt_attr %will hold \c@g_@@_struct_abs_int a cnt - -% handling attribute needs a different system to number the page wise mcid's: -% a tagmcbegin ... tagmcend pair no longer surrounds exactly one mc chunk: it can be split -% at page breaks. We know the included mcid(s) only after the ship out. So for the struct-> mcid mapping we -% need to record struct -> mc-cnt (in \g_@@_mc_parenttree_prop and/or a lua table -% and at shipout mc-cnt-> {mcid, mcid, ...} (in a table?) -% and when building the trees connect both - -% key definitions are overwritten for luatex to store that data in tables -% the data for the mc are in ltx.@@.mc[absnum] -% the fields of the table are -% tag : the type (a string) -% raw : more properties (string) -% label: a string. Do we need a way to retrieve the num from the label from lua?? -% artifact: the presence indicates an artifact, the value (string) is the type. -% kids: a array of tables {1={kid=num2,page=pagenum1}, 2={kid=num2,page=pagenum2},...}, -% this describes the chunks the mc has been split to by the traversing code -% parent: the number of the structure it is in. Needed to build the parent tree. - -% The main function which wanders through the shipout box to inject the literals. -% if the new callback is there, it is used. -\hook_gput_code:nnn{begindocument}{tagpdf/mc} - { - \bool_if:NT\g__tag_active_mc_bool - { - \directlua - { - if~luatexbase.callbacktypes.pre_shipout_filter~then~ - luatexbase.add_to_callback("pre_shipout_filter", function(TAGBOX)~ - ltx.__tag.func.mark_shipout(TAGBOX)~return~true~ - end, "tagpdf")~ - end - } - \directlua - { - if~luatexbase.callbacktypes.pre_shipout_filter~then~ - token.get_next()~ - end - }\@secondoftwo\@gobble - { - \hook_gput_code:nnn{shipout/before}{tagpdf/lua} - { - \directlua - { ltx.__tag.func.mark_shipout (tex.box["ShipoutBox"]) } - } - } - } - } - -\prg_new_conditional:Nnn \@@_mc_if_in: {p,T,F,TF} - { - \int_compare:nNnTF { -2147483647 }={ \l_@@_mc_type_attr } - { \prg_return_false: } - { \prg_return_true: } - } - -\prg_new_eq_conditional:NNn \tag_mc_if_in: \@@_mc_if_in: {p,T,F,TF} - - -% the keys -\tl_new:N \l_@@_mc_key_tag_tl -\tl_new:N \l_@@_mc_key_label_tl -\tl_new:N \l_@@_mc_key_properties_tl - -\keys_define:nn { @@ / mc } - { - tag .code:n = % - {%%????????? \pdfescapename?? - \tl_set:Nx \l_@@_mc_key_tag_tl { #1 } - \directlua - { - ltx.@@.func.store_mc_data(\@@_get_mc_abs_cnt:,"tag","#1") - } - }, - raw .code:n = - { - \tl_put_right:Nx \l_@@_mc_key_properties_tl { #1 } - \directlua - { - ltx.@@.func.store_mc_data(\@@_get_mc_abs_cnt:,"raw","#1") - } - }, - alttext .code:n = % Alt property - { - \str_set_convert:Nnon - \l_@@_tmpa_str - { #1 } - { default } - { utf16/hex } - \tl_put_right:Nn \l_@@_mc_key_properties_tl { /Alt~< } - \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } - \directlua - { - ltx.@@.func.store_mc_data - ( - \@@_get_mc_abs_cnt:,"alt","/Alt~<\str_use:N \l_@@_tmpa_str>" - ) - } - }, - alttext-o .code:n = % Alt property - { - \str_set_convert:Noon - \l_@@_tmpa_str - { #1 } - { default } - { utf16/hex } - \tl_put_right:Nn \l_@@_mc_key_properties_tl { /Alt~< } - \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } - \directlua - { - ltx.@@.func.store_mc_data - ( - \@@_get_mc_abs_cnt:,"alt","/Alt~<\str_use:N \l_@@_tmpa_str>" - ) - } - }, - actualtext .code:n = % Alt property - { - \str_set_convert:Nnon - \l_@@_tmpa_str - { #1 } - { default } - { utf16/hex } - \tl_put_right:Nn \l_@@_mc_key_properties_tl { /Alt~< } - \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } - \directlua - { - ltx.@@.func.store_mc_data - ( - \@@_get_mc_abs_cnt:,"actualtext","/ActualText~<\str_use:N \l_@@_tmpa_str>" - ) - } - }, - actualtext-o .code:n = % Alt property - { - \str_set_convert:Noon - \l_@@_tmpa_str - { #1 } - { default } - { utf16/hex } - \tl_put_right:Nn \l_@@_mc_key_properties_tl { /Alt~< } - \tl_put_right:No \l_@@_mc_key_properties_tl { \l_@@_tmpa_str>~ } - \directlua - { - ltx.@@.func.store_mc_data - ( - \@@_get_mc_abs_cnt:, - "actualtext", - "/ActualText~<\str_use:N \l_@@_tmpa_str>" - ) - } - }, - label .code:n = - { - \tl_set:Nn\l_@@_mc_key_label_tl { #1 } - \directlua - { - ltx.@@.func.store_mc_data - ( - \@@_get_mc_abs_cnt:,"label","#1" - ) - } - }, - __artifact-store .code:n = - { - \directlua - { - ltx.@@.func.store_mc_data - ( - \@@_get_mc_abs_cnt:,"artifact","#1" - ) - } - }, - artifact .code:n = - { - \exp_args:Nnx - \keys_set:nn - { @@ / mc} - { artifact-bool, artifact-type=#1, tag=Artifact } - \exp_args:Nnx - \keys_set:nn - { @@ / mc } - { __artifact-store=\l_@@_mc_artifact_type_tl } - }, - artifact .default:n = { notype } - } - - -% attributes -% set the mc from a tag name - -\cs_set_protected:Npn \@@_attribute_gset:Nn #1 #2 - { - \tex_global:D \setattribute #1 #2 - } - -\cs_set_protected:Npn \@@_attribute_set:Nn #1 #2 - { - \setattribute #1 #2 - } - -\cs_set_protected:Npn \@@_attribute_gunset:N #1 - { - \tex_global:D \unsetattribute #1 - } - -\cs_set_protected:Npn \@@_attribute_unset:N #1 - { - \unsetattribute #1 - } - - -\cs_new:Nn \@@_mc_lua_set_mc_type_attr:n % #1 is a tag name - { - \@@_attribute_set:Nn \l_@@_mc_type_attr - { - \directlua { ltx.@@.func.output_num_from ("#1") } - } - \@@_attribute_set:Nn \l_@@_mc_cnt_attr { \@@_get_mc_abs_cnt: } - } - -\cs_generate_variant:Nn\@@_mc_lua_set_mc_type_attr:n { o } - -\cs_new:Nn \@@_mc_lua_unset_mc_type_attr: - { - \@@_attribute_unset:N \l_@@_mc_type_attr - \@@_attribute_unset:N \l_@@_mc_cnt_attr - } - - - -%This command will in the finish code replace the dummy for a mc by the real mcid kids -%we need a variant for the case that it is the only kid, to get the array right -\cs_new:Nn \@@_mc_insert_mcid_kids:n - { - \directlua { ltx.@@.func.mc_insert_kids (#1,0) } - } - -\cs_new:Nn \@@_mc_insert_mcid_single_kids:n - { - \directlua {ltx.@@.func.mc_insert_kids (#1,1) } - } - - -% we need to pass the info if the kids are alone or already in an array -% so we add a second argument, which should == 1 if the kids are single - - -% puts an mcid absolute number in the current structure -\cs_new:Nn \@@_mc_handle_stash:n %1 mcidnum - { - \@@_check_mc_used:n { #1 } - \seq_gput_right:cn % Don't fill a lua table due to the command in the item, - % so use the kernel command - { g_@@_struct_kids_\g_@@_struct_stack_current_tl _seq } - { - \@@_mc_insert_mcid_kids:n {#1}% - } - \directlua - { - ltx.@@.func.store_struct_mcabs - ( - \g_@@_struct_stack_current_tl,#1 - ) - } - \prop_gput:Nxx - \g_@@_mc_parenttree_prop - { #1 } - { \g_@@_struct_stack_current_tl } - } - -\cs_generate_variant:Nn \@@_mc_handle_stash:n { o } - -\cs_new_protected:Nn \tag_mc_begin:n - { - %\group_begin: - %\@@_check_mc_if_nested: - %\bool_gset_true:N \g_@@_in_mc_bool - \bool_set_false:N\l_@@_mc_artifact_bool - \int_gincr:N \c@g_@@_MCID_abs_int - \tl_clear:N \l_@@_mc_key_properties_tl - \keys_set:nn { @@ / mc }{ label={}, #1 } - %check that a tag or artifact has been used - \@@_check_mc_tag:N \l_@@_mc_key_tag_tl - %set the attributes: - \@@_mc_lua_set_mc_type_attr:o { \l_@@_mc_key_tag_tl } - \bool_if:NF \l_@@_mc_artifact_bool - { % store the absolute num name in a label: - \tl_if_empty:NF {\l_@@_mc_key_label_tl} - { - \@@_mc_handle_mc_label:n { \l_@@_mc_key_label_tl } - } - % if not stashed record the absolute number - \bool_if:NF \l_@@_mc_key_stash_bool - { - \exp_args:Nx \@@_mc_handle_stash:n { \@@_get_mc_abs_cnt: } - } - } - %\bool_set_false:N\l_@@_mc_artifact_bool - %\group_end: - } - - -\cs_new_protected:Nn \tag_mc_end: - { - %\@@_check_mc_if_open: - %\bool_gset_false:N \g_@@_in_mc_bool - \bool_set_false:N\l_@@_mc_artifact_bool - \@@_mc_lua_unset_mc_type_attr: - \tl_set:Nn \l_@@_mc_key_tag_tl { } - } - - - -\cs_new_protected:Nn \tag_mc_use:n %#1: label name - { - \tl_set:Nx \l_tmpa_tl { \@@_ref_value:enn{tagpdf-#1}{tagmcabs}{} } - \tl_if_empty:NTF\l_tmpa_tl - { - \msg_warning:nnn {tag} {mc-label-unknown} { #1 } - } - { - \@@_mc_handle_stash:o { \l_tmpa_tl } - } - } - - - -\cs_new:Nn \@@_get_data_mc_tag: { \l_@@_mc_key_tag_tl } -%</luamode> -% \end{macrocode} diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-roles.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-roles.dtx index 7955226573f..f8a11bc4649 100644 --- a/Master/texmf-dist/source/latex/tagpdf/tagpdf-roles.dtx +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-roles.dtx @@ -21,28 +21,60 @@ % https://github.com/u-fischer/tagpdf % % 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-checks module (tagpdf)} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> % \fi +% \title{^^A +% The \pkg{tagpdf-roles} module\\ Tags, roles and namesspace 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.9, released 2021-06-29} +% \maketitle +% \begin{implementation} % \begin{macrocode} %<@@=tag> -%<*roles> -\ProvidesExplPackage {tagpdf-roles-code} {2021-06-14} {0.82} +%<*header> +\ProvidesExplPackage {tagpdf-roles-code} {2021-06-29} {0.9} {part of tagpdf - code related to roles and structure names} +%</header> % \end{macrocode} -% \section{Variables} +% \section{Code related to roles and structure names} +% \subsection{Variables} % Tags have both a name (a string) and a number (for the lua attribute). % Testing a name is easier with a prop, while accessing with a number is % better done with a seq. So both are used and must be kept in sync if a new % tag is added. The number is only relevant for the MC type, tags with the same % name from different names spaces can have the same number. -% \begin{macro}{\g_@@_role_tags_seq,\g_@@_role_tags_prop} +% \begin{variable}{\g_@@_role_tags_seq,\g_@@_role_tags_prop} % \begin{macrocode} +%<*package> \@@_seq_new:N \g_@@_role_tags_seq %to get names (type/NS) from numbers \@@_prop_new:N \g_@@_role_tags_prop %to get numbers from names (type/NS) % \end{macrocode} -% \end{macro} -% \begin{macro}{\g_@@_role_tags_NS_prop} +% \end{variable} +% \begin{variable}{\g_@@_role_tags_NS_prop} % in pdf 2.0 tags belong to a name space. For every tag we store a default % name space. % The keys are the tags, the value shorthands like pdf2, or mathml. @@ -50,8 +82,8 @@ % \begin{macrocode} \prop_new:N \g_@@_role_tags_NS_prop %to namespace info % \end{macrocode} -% \end{macro} -% \begin{macro}{\g_@@_role_NS_prop} +% \end{variable} +% \begin{variable}{\g_@@_role_NS_prop} % The standard names spaces are the following. The keys are the name % tagpdf will use, the urls are the identifier in the namespace object. % \begin{description} @@ -68,13 +100,28 @@ % \begin{macrocode} \prop_new:N \g_@@_role_NS_prop % collect namespaces % \end{macrocode} -% \end{macro} -% \section{Namesspaces} +% \end{variable} +% +% We need also a bunch of temporary variables: +% \begin{variable} +% { +% \l_@@_role_tag_tmpa_tl +% ,\l_@@_role_tag_namespace_tmpa_tl +% ,\l_@@_role_role_tmpa_tl +% ,\l_@@_role_role_namespace_tmpa_tl +% } +% \begin{macrocode} +\tl_new:N \l_@@_role_tag_tmpa_tl +\tl_new:N \l_@@_role_tag_namespace_tmpa_tl +\tl_new:N \l_@@_role_role_tmpa_tl +\tl_new:N \l_@@_role_role_namespace_tmpa_tl +% \end{macrocode} +% \end{variable} +% \subsection{Namesspaces} % The following commands setups a names space. Namespace dictionaries can % contain an optional |/Schema| and |/RoleMapNS| entry. We only reserve the % objects but delay the writing to the finish code, where we can test if the % keys and the name spaces are actually needed -% \begin{macro}{\@@_role_NS_new:nn} % This commands setups objects for the name space and its rolemap. It also % initialize a prop to collect the rolemaps if needed. % \begin{function}{\@@_role_NS_new:nnn} @@ -85,7 +132,7 @@ % \begin{macro}{\@@_role_NS_new:nnn} % \begin{macrocode} \cs_new_protected:Npn \@@_role_NS_new:nnn #1 #2 #3 - { \msg_redirect_name:nnn { pdfdict } { empty-value } { none } + { \pdf_object_new:nn {tag/NS/#1}{dict} \pdfdict_new:n {g_@@_role/Namespace_#1_dict} \pdf_object_new:nn {@@/RoleMapNS/#1}{dict} @@ -95,15 +142,20 @@ {Type} {/Namespace} \pdf_string_from_unicode:nnN{utf8/string}{#2}\l_tmpa_str - \pdfdict_gput:nnx - {g_@@_role/Namespace_#1_dict} - {NS} - {\l_tmpa_str} + \tl_if_empty:NF \l_tmpa_str + { + \pdfdict_gput:nnx + {g_@@_role/Namespace_#1_dict} + {NS} + {\l_tmpa_str} + } %RoleMapNS is added in tree - \pdfdict_gput:nnx{g_@@_role/Namespace_#1_dict} - {Schema}{#3} + \tl_if_empty:nF {#3} + { + \pdfdict_gput:nnx{g_@@_role/Namespace_#1_dict} + {Schema}{#3} + } \prop_gput:Nnx \g_@@_role_NS_prop {#1}{\pdf_object_ref:n{tag/NS/#1}~} - \msg_redirect_name:nnn { pdfdict } { empty-value } { warning } } % \end{macrocode} % \end{macro} @@ -112,6 +164,7 @@ % be fixed by setting a seed. We fake a sort of % GUID but not try to be really exact as it doesn't matter ... % +% \begin{variable}{\c_@@_role_userNS_id_str} % \begin{macrocode} \str_const:Nx \c_@@_role_userNS_id_str { data:, @@ -128,6 +181,7 @@ \int_to_Hex:n{\int_rand:n {16777215}} } % \end{macrocode} +% \end{variable} % Now we setup the standard names spaces. Currently only if we detect pdf2.0 but % this will perhaps have to change if the structure code gets to messy. % \begin{macrocode} @@ -141,14 +195,14 @@ } % \end{macrocode} % -% \section{Data} +% \subsection{Data} % In this section we setup the standard data. % At first the list of structure types. We split them in three lists, % the tags with which are both in the pdf and pdf2 namespace, % the one only in pdf and the one with the tags only in pdf2. % We also define a rolemap for the pdfII only type to pdf so that % they can always be used. -% \begin{macro} +% \begin{variable} % { % \c_@@_role_sttags_pdf_pdfII_clist, % \c_@@_role_sttags_only_pdf_clist, @@ -160,7 +214,8 @@ % \clist_const:Nn \c_@@_role_sttags_pdf_pdfII_clist { - Document, %A complete document. This is the root element of any structure tree containing + Document, %A complete document. This is the root element + %of any structure tree containing %multiple parts or multiple articles. Part, %A large-scale division of a document. Sect, %A container for grouping related content elements. @@ -205,13 +260,16 @@ \clist_const:Nn \c_@@_role_sttags_only_pdf_clist { - Art,%A relatively self-contained body of text constituting a single narrative or exposition - BlockQuote, %A portion of text consisting of one or more paragraphs attributed to someone other - %than the author of the surrounding text. - TOC, %A list made up of table of contents item entries (structure tag TOCI; see below) - %and/or other nested table of contents entries - TOCI, %An individual member of a table of contents. This entry's children can be any of - %the following structure tags: + Art, %A relatively self-contained body of text + %constituting a single narrative or exposition + BlockQuote, %A portion of text consisting of one or more paragraphs + %attributed to someone other than the author of the + %surrounding text. + TOC, %A list made up of table of contents item entries + %(structure tag TOCI; see below) and/or other + %nested table of contents entries + TOCI, %An individual member of a table of contents. + %This entry's children can be any of the following structure tags: %Lbl,Reference,NonStruct,P,TOC Index, Private, @@ -448,7 +506,7 @@ Strong= Span, } % \end{macrocode} -% \end{macro} +% \end{variable} % We fill the structure tags in to the seq. We allow all pdf1.7 and pdf2.0, % and role map if needed the 2.0 tags. % \begin{macrocode} @@ -482,7 +540,6 @@ % For luatex and the MC we need a name/number relation. % The name space is not relevant. % \begin{macrocode} - \int_step_inline:nnnn { 1 }{ 1 }{ \seq_count:N \g_@@_role_tags_seq } { \@@_prop_gput:Nxn \g_@@_role_tags_prop @@ -493,24 +550,29 @@ } % \end{macrocode} % -% \section{Adding new tags and rolemapping} +% \subsection{Adding new tags and rolemapping} % -% \subsection{pdf 1.7 and earlier} +% \subsubsection{pdf 1.7 and earlier} % With this versions only RoleMap is filled. % At first the dictionary: +% \begin{variable}{g_@@_role/RoleMap_dict} % \begin{macrocode} \pdfdict_new:n {g_@@_role/RoleMap_dict} % \end{macrocode} +% \end{variable} % \begin{macro}{\@@_role_add_tag:nn} % The pdf 1.7 version has only two arguments: new and rolemap name. % To make pdf 2.0 types usable we directly define a rolemapping for them. -% TODO: add checks +% % \begin{macrocode} \cs_new_protected:Nn \@@_role_add_tag:nn %(new) name, reference to old { \prop_if_in:NnF \g_@@_role_tags_prop {#1} { - \msg_info:nnn { tag }{new-tag}{#1} + \int_compare:nNnT {\l_@@_loglevel_int} > { 0 } + { + \msg_info:nnn { tag }{new-tag}{#1} + } \@@_seq_gput_right:Nn \g_@@_role_tags_seq { #1 } \@@_prop_gput:Nnx \g_@@_role_tags_prop { #1 } { @@ -518,7 +580,7 @@ } \prop_gput:Nnn \g_@@_role_tags_NS_prop { #1 }{ user } } - %\@@_check_add_tag_role:nn {#1}{#2} %%!TODO + \@@_check_add_tag_role:nn {#1}{#2} \tl_if_empty:nF { #2 } { \pdfdict_gput:nnx {g_@@_role/RoleMap_dict} @@ -538,22 +600,24 @@ % \end{macrocode} % \end{macro} -% +% \subsubsection{The pdf 2.0 version} % \begin{macro}{\@@_role_add_tag:nnnn} -% The pdf 2.0 version takes for arguments: +% The pdf 2.0 version takes four arguments: % tag/namespace/role/namespace -% TODO: add checks % \begin{macrocode} \cs_new_protected:Nn \@@_role_add_tag:nnnn %tag/namespace/role/namespace { - \msg_info:nnn { tag }{new-tag}{#1} + \int_compare:nNnT {\l_@@_loglevel_int} > { 0 } + { + \msg_info:nnn { tag }{new-tag}{#1} + } \@@_seq_gput_right:Nn \g_@@_role_tags_seq { #1 } \@@_prop_gput:Nnx \g_@@_role_tags_prop { #1 } { \seq_count:N \g_@@_role_tags_seq } \prop_gput:Nnn \g_@@_role_tags_NS_prop { #1 }{ #2 } - %\@@_check_add_tag_role:nn {#1}{#3} %TODO + \@@_check_add_tag_role:nn {#1}{#3} \pdfdict_gput:nnx {g_@@_role/RoleMapNS_#2_dict}{#1} { [ @@ -566,15 +630,11 @@ \cs_generate_variant:Nn \@@_role_add_tag:nnnn {VVVV} % \end{macrocode} % \end{macro} -% The user interface use the key add-new-tag, which takes either a +% \subsection{Key-val user interface} +% The user interface use the key |add-new-tag|, which takes either a % keyval list as argument, or a tag/role. +% \begin{macro}{tag,tag-namespace,role,role-namespace,add-new-tag} % \begin{macrocode} - -\tl_new:N \l_@@_role_tag_tmpa_tl -\tl_new:N \l_@@_role_tag_namespace_tmpa_tl -\tl_new:N \l_@@_role_role_tmpa_tl -\tl_new:N \l_@@_role_role_namespace_tmpa_tl - \keys_define:nn { @@ / tag-role } { ,tag .tl_set:N = \l_@@_role_tag_tmpa_tl @@ -632,7 +692,8 @@ } } } - - -%</roles> +%</package> % \end{macrocode} +% \end{macro} +% \end{implementation} +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-space.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-space.dtx index 55021a74d2b..9ccee62c6f0 100644 --- a/Master/texmf-dist/source/latex/tagpdf/tagpdf-space.dtx +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-space.dtx @@ -2,7 +2,7 @@ % %% File: tagpdf-space.dtx % -% Copyright (C) 2019 Ulrike Fischer +% 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 @@ -21,25 +21,74 @@ % https://github.com/u-fischer/tagpdf % % 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-space module (tagpdf)} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> % \fi +% \title{^^A +% The \pkg{tagpdf-space} module\\ Code related to real space chars ^^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> -%<*space> -\ProvidesExplPackage {tagpdf-space-code} {2021-06-14} {0.82} +%<*header> +\ProvidesExplPackage {tagpdf-space-code} {2021-06-29} {0.9} {part of tagpdf - code related to real space chars} -% luatex uses an attribute (declared in the driver file) and some luacode in tagpdf.lua -% perhaps I will split the code by engine in the future - +%</header> +% \end{macrocode} +% \section{Code for interword spaces} +% The code is engine/backend dependant. Basically only pdftex and luatex +% support real space chars. Most of the code for luatex which uses attributes +% is in the lua code, here are only the keys. +% +% \begin{macro}{interwordspace,show-spaces} +% \begin{macrocode} +%<*package> \sys_if_engine_pdftex:T { - \pdfglyphtounicode{space}{0020} - \keys_define:nn { @@ / setup } + \sys_if_output_pdf:TF { - interwordspace .choices:nn = { true, on } { \pdfinterwordspaceon }, - interwordspace .choices:nn = { false, off }{ \pdfinterwordspaceon }, - show-spaces .bool_set:N = \l_@@_showspaces_bool + \pdfglyphtounicode{space}{0020} + \keys_define:nn { @@ / setup } + { + interwordspace .choices:nn = { true, on } { \pdfinterwordspaceon }, + interwordspace .choices:nn = { false, off }{ \pdfinterwordspaceon }, + interwordspace .default:n = true, + show-spaces .bool_set:N = \l_@@_showspaces_bool + } + } + { + \keys_define:nn { @@ / setup } + { + interwordspace .choices:nn = { true, on, false, off } + { \msg_warning:nnn {tag}{sys-no-interwordspace}{dvi} }, + interwordspace .default:n = true, + show-spaces .bool_set:N = \l_@@_showspaces_bool + } } } @@ -50,40 +99,57 @@ { interwordspace .choices:nn = { true, on } - { \directlua{ltx.@@.func.markspaceon()} }, + { + \bool_gset_true:N \g_@@_active_space_bool + \lua_now:e{ltx.@@.func.markspaceon()} + }, interwordspace .choices:nn = { false, off } - {\directlua{ltx.@@.func.markspaceoff()} }, + { + \bool_gset_false:N \g_@@_active_space_bool + \lua_now:e{ltx.@@.func.markspaceoff()} + }, + interwordspace .default:n = true, show-spaces .choice:, show-spaces / true .code:n = - {\directlua{ltx.@@.trace.showspaces=true}}, + {\lua_now:e{ltx.@@.trace.showspaces=true}}, show-spaces / false .code:n = - {\directlua{ltx.@@.trace.showspaces=nil}}, + {\lua_now:e{ltx.@@.trace.showspaces=nil}}, show-spaces .default:n = true } - - \cs_new_protected:Nn \@@_fakespace: - { - \group_begin: - \setattribute\g_@@_interwordspace_attr{1} - \setattribute\g_@@_interwordfont_attr - { - \directlua{tex.print(\the\catcodetable@latex, font.current())} - } - \skip_horizontal:n{\c_zero_skip} - \group_end: - } } \sys_if_engine_xetex:T { \keys_define:nn { @@ / setup } { - interwordspace .choices:nn = { true, on } { \msg_warning:nnn {tag}{sys-no-interwordspace}{xetex} }, - interwordspace .choices:nn = { false, off }{ \msg_warning:nnn {tag}{sys-no-interwordspace}{xetex} }, + interwordspace .choices:nn = { true, on } + { \msg_warning:nnn {tag}{sys-no-interwordspace}{xetex} }, + interwordspace .choices:nn = { false, off } + { \msg_warning:nnn {tag}{sys-no-interwordspace}{xetex} }, + interwordspace .default:n = true, show-spaces .bool_set:N = \l_@@_showspaces_bool } } - -%</space> % \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_fakespace:} +% For luatex we need a command for the fake space as equivalent of the pdftex +% primitive. +% \begin{macrocode} +\sys_if_engine_luatex:T + { + \cs_new_protected:Nn \@@_fakespace: + { + \group_begin: + \lua_now:e{ltx.@@.func.fakespace()} + \skip_horizontal:n{\c_zero_skip} + \group_end: + } + } +%</package> +% \end{macrocode} +% \end{macro} +% \end{implementation} +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx index e385fea951d..4c342f921c9 100644 --- a/Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx @@ -21,57 +21,278 @@ % https://github.com/u-fischer/tagpdf % % 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-struct} module\\ Commands to create the structure ^^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{documentation} +% \section{Public Commands} +% \begin{function}{\tag_struct_begin:n,\tag_struct_end:} +% \begin{syntax} +% \cs{tag_struct_begin:n}\Arg{key-values}\\ +% \cs{tag_struct_end:} +% \end{syntax} +% These commands start and end a new structure. +% They don't start a group. They set all their values globally. +% \end{function} +% \begin{function}{\tag_struct_use:n} +% \begin{syntax} +% \cs{tag_struct_use:n}\Arg{label} +% \end{syntax} +% These commands insert a structure previously stashed away as kid +% into the currently active structure. +% A structure should be used only once, +% if the structure already has a parent a warning is issued. +% \end{function} +% The following two functions are used to add annotations. They must be used +% together and with care to get the same numbers. Perhaps some improvements are needed +% here. +% \begin{function}{\tag_struct_insert_annot:nn} +% \begin{syntax} +% \cs{tag_struct_insert_annot:nn}\Arg{object reference}\Arg{struct parent number} +% \end{syntax} +% This inserts an annotation in the structure. \meta{object reference} +% is there reference to the annotation. \meta{struct parent number} +% should be the same number as had been inserted with \cs{tag_struct_parent_int:} +% as |StructParent| value to the dictionary of the annotion. +% The command will increase the value of the counter +% used by \cs{tag_struct_parent_int:}. +% \end{function} +% \begin{function}{\tag_struct_parent_int:} +% \begin{syntax} +% \cs{tag_struct_parent_int:} +% \end{syntax} +% This gives back the next free /StructParent number (assuming that it is +% together with \cs{tag_struct_insert_annot:nn} which will increase the number. +% \end{function} +% \section{Public keys} +% \subsection{Keys for the structure commands} +% \begin{function}{tag} +% This is required. The value of the key is normally one of the +% standard types listed in section \ref{sec:new-tag}. +% It is possible to setup new tags/types. +% The value can also be of the form |type/NS|, where |NS| is the +% shorthand of a declared name space. +% Currently the names spaces |pdf|, |pdf2|, |mathml| and |user| are defined. +% This allows to use a different name space than +% the one connected by default to the tag. But normally this should not be needed. +% \end{function} +% \begin{function}{stash} +% Normally a new structure inserts itself as a kid +% into the currently active structure. This key prohibits this. +% The structure is nevertheless from now on +% \enquote{the current active structure} +% and parent for following marked content and structures. +% \end{function} +% \begin{function}{label} +% This key sets a label by which +% one can use the structure later in another structure. +% Internally the label name will start with \texttt{tagpdfstruct-}. +% \end{function} +% \begin{function}{title,title-o} +% This keys allows to set the dictionary entry +% \texttt{/Title} in the structure object. +% The value is handled as verbatim string and hex encoded. +% Commands are not expanded. |title-o| will expand the value once. +% \end{function} +% +% \begin{function}{alttext,alttext-o} +% This key inserts an \texttt{/Alt} value in the dictionary of structure object. +% The value is handled as verbatim string and hex encoded. +% |alttext-o| will expand the value once. +% \end{function} +% \begin{function}{actualtext,actualtext-o} +% This key inserts an \texttt{/ActualText} value in the dictionary of structure object. +% The value is handled as verbatim string and hex encoded. +% |actualtext-o| will expand the value once. +% \end{function} +% \begin{function}{lang} +% This key allows to set the language for a structure element. The value should be a bcp-identifier, +% e.g. |de-De|. +% \end{function} +% \begin{function}{ref} +% This key allows to add references to other structure elements, +% it adds the |/Ref| array to the structure. +% The value should be a comma separated list of structure labels +% set with the |label| key. e.g. |ref={label1,label2}|. +% \end{function} +% \begin{function}{E} +% This key sets the |/E| key, the expanded form of an +% abbreviation or an acronym +% (I couldn't think of a better name, so I sticked to E). +% \end{function} +% \begin{function}{AF,AFinline,AFinline-o} +% \begin{syntax} +% AF = \meta{object name}\\ +% AF-inline = \meta{text content} +% \end{syntax} +% These keys allows to reference an associated file in the structure element. +% The value \meta{object name} should be the name of an object pointing +% to the \texttt{/Filespec} dictionary as expected by +% |\pdf_object_ref:n| from a current \texttt{l3kernel}. +% +% The value |AF-inline| is some text, +% which is embedded in the PDF as a text file with mime type text/plain. +% |AF-inline-o| is like |AF-inline| but expands the value once. +% +% Future versions will perhaps extend this to more mime types, but it is +% still a research task to find out what is really needed. +% +% \end{function} +% +% \begin{function}{attribute} +% This key takes as argument a comma list of attribute names +% (use braces to protect the commas from the external key-val parser) +% and allows to add one or more attribute dictionary entries in +% the structure object. As an example +% \begin{verbatim} +% \tagstructbegin{tag=TH,attribute= TH-row} +% \end{verbatim} +% Attribute names and their content must be declared first in \cs{tagpdfsetup}. +% +% \end{function} % +% \begin{function}{attribute-class} +% This key takes as argument a comma list of attribute class names +% (use braces to protect the commas from the external key-val parser) +% and allows to add one or more attribute classes to the structure object. +% +% Attribute class names and their content +% must be declared first in \cs{tagpdfsetup}. +% \end{function} +% \subsection{Setup keys} +% \begin{function}{newattribute} +% \begin{syntax} +% newattribute = \Arg{name}\Arg{Content} +% \end{syntax} +% This key can be used in the setup command \cs{tagpdfsetup} and allow to declare a +% new attribute, which can be used as attribute or attribute class. +% The value are two brace groups, the first contains the name, the second the content. +% \begin{verbatim} +% \tagpdfsetup +% { +% newattribute = +% {TH-col}{/O /Table /Scope /Column}, +% newattribute = +% {TH-row}{/O /Table /Scope /Row}, +% } +% \end{verbatim} +% +% \end{function} +% \end{documentation} +% \begin{implementation} % \begin{macrocode} %<@@=tag> -%<*struct> -\ProvidesExplPackage {tagpdf-struct-code} {2021-06-14} {0.82} +%<*header> +\ProvidesExplPackage {tagpdf-struct-code} {2021-06-29} {0.9} {part of tagpdf - code related to storing structure} +%</header> % \end{macrocode} +% \section{Variables} +% \begin{variable}{\c@g_@@_struct_abs_int} +% Every structure will have a unique, absolute number. % I will use a latex counter for the structure count -% to have a chance to avoid double structures in align etc +% to have a chance to avoid double structures in align etc. +% % \begin{macrocode} +%<*package> \newcounter { g_@@_struct_abs_int } \int_gzero:N \c@g_@@_struct_abs_int % \end{macrocode} -% a sequence to store structnum -> the obj numbers -% to allow easy mapping over the structures +% \end{variable} +% +% \begin{variable}{\g_@@_struct_objR_seq} +% a sequence to store mapping between the +% structure number and the object number. +% We assume that structure numbers are assign +% consecutively and so the index of the seq can be used. +% A seq allows easy mapping over the structures. % \begin{macrocode} \@@_seq_new:N \g_@@_struct_objR_seq % \end{macrocode} -% a sequence for the structure stack. +% \end{variable} +% +% \begin{variable}{\g_@@_struct_stack_seq} +% A stack sequence for the structure stack. % When a sequence is opened it's number is put on the stack. % \begin{macrocode} \seq_new:N \g_@@_struct_stack_seq \seq_gpush:Nn \g_@@_struct_stack_seq {0} % \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_@@_struct_tag_stack_seq} % We will perhaps also need the tags. While it is possible to get them from the % numbered stack, lets build a tag stack too. % \begin{macrocode} \seq_new:N \g_@@_struct_tag_stack_seq \seq_gpush:Nn \g_@@_struct_tag_stack_seq {Root} % \end{macrocode} -%this variables will hold the number of top entry and the parent on the stack +% \end{variable} +% +% \begin{variable}{\g_@@_struct_stack_current_tl,\l_@@_struct_stack_parent_tmpa_tl} +% The global variable will hold the current structure number. +% The local temporary variable will hold the parent when we fetch it from the stack. % \begin{macrocode} -\tl_new:N \l_@@_struct_stack_parent_tmp_tl \tl_new:N \g_@@_struct_stack_current_tl +\tl_new:N \l_@@_struct_stack_parent_tmpa_tl % \end{macrocode} -% I need at least one structure: the StructTreeRoot +% \end{variable} +% +% I will need at least one structure: the StructTreeRoot % normally it should have only one kid, e.g. the document element. % The data of the StructTreeRoot and the StructElem are in properties: -% \g_@@_struct_0_prop for the root -% \g_@@_struct_N_prop, N >=1 -% they have all the keys -% objnum - number, -% Type - StructTreeRoot or StructElem -% num - number (identical to the num in the name, or 0 for the root) +% |\g_@@_struct_0_prop| for the root and +% |\g_@@_struct_N_prop|, $N \geq =1$ for the other. +% +% This creates quite a number of properties, so perhaps we will have to +% do this more efficiently in the future. +% +% All properties have at least the keys +% \begin{description} +% \item[Type] StructTreeRoot or StructElem +% \end{description} % and the keys from the two following lists % (the root has a special set of properties). % the values of the prop should be already escaped properly % when the entries are created (title,lange,alt,E,actualtext) +% \begin{variable} +% { +% \c_@@_struct_StructTreeRoot_entries_seq, +% \c_@@_struct_StructElem_entries_seq +% } +% These seq contain the keys we support in the two object types. +% They are currently no longer used, but are provided as documentation and +% for potential future checks. +% They should be adapted if there are changes in the PDF format. % \begin{macrocode} \seq_const_from_clist:Nn \c_@@_struct_StructTreeRoot_entries_seq {%p. 857/858 @@ -79,7 +300,7 @@ K, % kid, dictionary or array of dictionaries IDTree, % currently unused ParentTree, % required,obj ref to the parent tree - ParentTreeNextKey, %optional + ParentTreeNextKey, % optional RoleMap, ClassMap, Namespaces @@ -96,22 +317,55 @@ K, %kids A, %attributes, probably unused C, %class "" - %R, + %R, %attribute revision number, irrelevant for us as we + % don't update/change existing PDF and (probably) + % deprecated in PDF 2.0 T, %title, value in () or <> Lang, %language Alt, % value in () or <> - E, %abreviation + E, % abreviation ActualText, AF, %pdf 2.0, array of dict, associated files NS, %pdf 2.0, dict, namespace PhoneticAlphabet, %pdf 2.0 Phoneme %pdf 2.0 } - -% I need an output handler for each prop, to get expandable output -% see https://tex.stackexchange.com/questions/424208/expandable-version-of-a-expl3-command/424213#424213 - -\cs_new:Nn \@@_struct_output_prop_aux:nn %#1 num, #2 key +% \end{macrocode} +% \end{variable} +% +% \subsection{Variables used by the keys} +% \begin{variable}{\g_@@_struct_tag_tl,\g_@@_struct_tag_NS_tl} +% Use by the tag key to store the tag and the namespace. +% \begin{macrocode} +\tl_new:N \g_@@_struct_tag_tl +\tl_new:N \g_@@_struct_tag_NS_tl +% \end{macrocode} +% \end{variable} +% \begin{variable}{\l_@@_struct_key_label_tl} +% This will hold the label value. +% \begin{macrocode} +\tl_new:N \l_@@_struct_key_label_tl +% \end{macrocode} +% \end{variable} +% \begin{variable}{\l_@@_struct_elem_stash_bool} +% This will keep track of the stash status +% \begin{macrocode} +\bool_new:N \l_@@_struct_elem_stash_bool +% \end{macrocode} +% \end{variable} +% \section{Commands} +% +% The properties must be in some places handled expandably. +% So I need an output handler for each prop, to get expandable output +% see \url{https://tex.stackexchange.com/questions/424208}. +% There is probably room here for a more efficient implementation. +% TODO check if this can now be implemented with the pdfdict commands. +% The property contains currently non pdf keys, but e.g. object numbers are +% perhaps no longer needed as we have named object anyway. +% +% \begin{macro}{\@@_struct_output_prop_aux:nn,\@@_new_output_prop_handler:n} +% \begin{macrocode} +\cs_new:Npn \@@_struct_output_prop_aux:nn #1 #2 %#1 num, #2 key { \prop_if_in:cnT { g_@@_struct_#1_prop } @@ -121,99 +375,113 @@ } } -\cs_new:Nn \@@_new_output_prop_handler:n +\cs_new_protected:Npn \@@_new_output_prop_handler:n #1 { \cs_new:cn { @@_struct_output_prop_#1:n } { \@@_struct_output_prop_aux:nn {#1}{##1} } } +% \end{macrocode} +% \end{macro} +% +% \subsection{Initialization of the StructTreeRoot} +% The first structure element, the StructTreeRoot is special, so +% created manually. The underlying object is |@@/struct/0| which is currently +% created in the tree code (TODO move it here). +% The |ParentTree| and |RoleMap| entries are added at begin document +% in the tree code as they refer to object which are setup in other parts of the +% code. This avoid timing issues. +% +% \begin{macrocode} +\tl_gset:Nn \g_@@_struct_stack_current_tl {0} +% \end{macrocode} - -% the first one, the StructTreeRoot is special, so -% created manually: +% +% \begin{variable}{g_@@_struct_0_prop,g_@@_struct_kids_0_seq} +% \begin{macrocode} \@@_prop_new:c { g_@@_struct_0_prop } \@@_new_output_prop_handler:n {0} -\tl_gset:Nn \g_@@_struct_stack_current_tl {0} - \@@_seq_new:c { g_@@_struct_kids_0_seq } -% new -\@@_prop_gput:cnx - { g_@@_struct_0_prop } - { objref} - { \pdf_object_ref:n { @@/struct/0 } } - \@@_prop_gput:cnn { g_@@_struct_0_prop } { Type } { /StructTreeRoot } -% the constants are defined in the tree code. -\@@_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 } } - -\@@_prop_gput:cnn - { g_@@_struct_0_prop } - { entries } - { StructTreeRoot } - -\@@_prop_gput:cnn - { g_@@_struct_0_prop } - { num } - { 0 } -%Namespaces are pdf 2.0 but it doesn't harm to have an empty entry +% \end{macrocode} +% Namespaces are pdf 2.0 but it doesn't harm +% to have an empty entry. We could add a test, but if the code moves into +% the kernel, timing could get tricky. +% \begin{macrocode} \@@_prop_gput:cnx { g_@@_struct_0_prop } { Namespaces } { \pdf_object_ref:n { @@/tree/namespaces } } - - -% commands to store the kids -% I don't compare the page objects number yet, but always add the /Pg key, perhaps later - -\cs_new:Nn \@@_struct_kid_mc_gput_right:nn %#1 structure num, #2 MCID absnum% +% \end{macrocode} +% \end{variable} +% \subsection{Handlings kids} +% Commands to store the kids. Kids in a structure can be a reference to a mc-chunk, +% an object reference to another structure element, or a object reference to an +% annotation (through an OBJR object). +% \begin{macro}{\@@_struct_kid_mc_gput_right:nn} +% The command to store an mc-chunk, this is a dictionary of type MCR. +% It would be possible to write out the content directly as unnamed object +% and to store only the object reference, but probably this would be slower, +% and the PDF is more readable like this. +% The code doesn't try to avoid the use of the /Pg key by checking page numbers. +% That imho only slows down without much gain. +% \begin{macrocode} +\cs_new_protected:Npn \@@_struct_kid_mc_gput_right:nn #1 #2 %#1 structure num, #2 MCID absnum% { - %\@@_store_pdfpageref:Nn \l_tmpa_tl { \@@_ref_value:enn{mcid-#2}{tagabspage}{1} } \@@_seq_gput_right:cx { g_@@_struct_kids_#1_seq } { << /Type \c_space_tl /MCR \c_space_tl - /Pg %\c_space_tl \l_tmpa_tl \c_space_tl 0 \c_space_tl R \c_space_tl + /Pg \c_space_tl \pdf_pageobject_ref:n { \@@_ref_value:enn{mcid-#2}{tagabspage}{1} } /MCID \c_space_tl \@@_ref_value:enn{mcid-#2}{tagmcid}{1} >> } } - -\cs_new:Nn\@@_struct_kid_struct_gput_right:nn %#1 num of parent struct, #2 kid struct +% \end{macrocode} +% \end{macro} +% \begin{macro} +% { +% \@@_struct_kid_struct_gput_right:nn,\@@_struct_kid_struct_gput_right:xx +% } +% This commands adds a structure as kid. We only need to record the object +% reference in the sequence. +% \begin{macrocode} +\cs_new_protected:Npn\@@_struct_kid_struct_gput_right:nn #1 #2 %#1 num of parent struct, #2 kid struct { \@@_seq_gput_right:cx { g_@@_struct_kids_#1_seq } { - \prop_item:cn - { g_@@_struct_#2_prop } - { objref } + \pdf_object_ref:n { @@/struct/#2 } } } -\cs_new:Nn\@@_struct_kid_OBJR_gput_right:nn %#1 num of parent struct, #2 obj reference +\cs_generate_variant:Nn \@@_struct_kid_struct_gput_right:nn {xx} +% \end{macrocode} +% \end{macro} +% \begin{macro} +% {\@@_struct_kid_OBJR_gput_right:nn,\@@_struct_kid_OBJR_gput_right:xx} +% At last the command to add an OBJR object. This has to write an object first. +% The first argument is the number of the parent structure, the second the +% (expanded) object reference of the annotation. +% \begin{macrocode} +\cs_new_protected:Npn\@@_struct_kid_OBJR_gput_right:nn #1 #2 %#1 num of parent struct, + %#2 obj reference { - \pdf_object_unnamed_write:nx + \pdf_object_unnamed_write:nn { dict } { - /Type \c_space_tl /OBJR \c_space_tl - /Obj~#2 + /Type/OBJR/Obj~#2 } \@@_seq_gput_right:cx { g_@@_struct_kids_#1_seq } @@ -222,27 +490,43 @@ } } -\cs_generate_variant:Nn\@@_struct_kid_OBJR_gput_right:nn { nx } +\cs_generate_variant:Nn\@@_struct_kid_OBJR_gput_right:nn { xx } -\cs_new:Nn\@@_struct_exchange_kid_command:N %N= seq +% \end{macrocode} +% \end{macro} +% \begin{macro} +% {\@@_struct_exchange_kid_command:N, \@@_struct_exchange_kid_command:c} +% In luamode it can happen that a single kid in a structure is split at a page +% break into two or more mcid. In this case the lua code has to convert +% put the dictionary of the kid into an array. See issue 13 at tagpdf repo. +% We exchange the dummy command for the kids to mark this case. +% \begin{macrocode} +\cs_new_protected:Npn\@@_struct_exchange_kid_command:N #1 %#1 = seq var { - \seq_gpop_left:NN #1 \l_tmpa_tl + \seq_gpop_left:NN #1 \l_@@_tmpa_tl \regex_replace_once:nnN { \c{\@@_mc_insert_mcid_kids:n} } { \c{\@@_mc_insert_mcid_single_kids:n} } - \l_tmpa_tl - \seq_gput_left:NV #1 \l_tmpa_tl + \l_@@_tmpa_tl + \seq_gput_left:NV #1 \l_@@_tmpa_tl } \cs_generate_variant:Nn\@@_struct_exchange_kid_command:N { c } - -\cs_new:Nn \@@_struct_fill_kid_key:n %#1 is the struct num +% \end{macrocode} +% \end{macro} +% \begin{macro}{ \@@_struct_fill_kid_key:n } +% This command adds the kid info to the K entry. In lua mode the +% content contains commands which are expanded later. The argument is the structure +% number. +% +% \begin{macrocode} +\cs_new_protected:Npn \@@_struct_fill_kid_key:n #1 %#1 is the struct num { \int_case:nnF { \seq_count:c { - g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_seq + g_@@_struct_kids_#1_seq } } { @@ -251,17 +535,17 @@ { 1 } % 1 kid, insert { % in this case we need a special command in - %luamode to get the array right. See issue #13 + % luamode to get the array right. See issue #13 \bool_if:NT\g_@@_mode_lua_bool { \@@_struct_exchange_kid_command:c - {g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_seq} + {g_@@_struct_kids_#1_seq} } \@@_prop_gput:cnx { g_@@_struct_#1_prop } {K} { \seq_item:cn { - g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_seq + g_@@_struct_kids_#1_seq } {1} } @@ -273,7 +557,7 @@ [ \seq_use:cn { - g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_seq + g_@@_struct_kids_#1_seq } { \c_space_tl @@ -283,22 +567,25 @@ } } -% this command can be used for roots and structure elements -% #1 is a num - -\tl_new:N \l_@@_struct_dict_content_tl - -\cs_new:Nn \@@_struct_get_dict_content:n +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@@_struct_get_dict_content:nN} +% This maps the dictionary content of a structure into a tl-var. +% Basically it does what |\pdfdict_use:n| does. +% TODO!! this looks over-complicated. Check if it can be done with pdfdict now. +% \begin{macrocode} +\cs_new_protected:Npn \@@_struct_get_dict_content:nN #1 #2 %#1: stucture num { - %\tl_set:Nn \l_@@_struct_dict_content_tl {<<} - \tl_clear:N \l_@@_struct_dict_content_tl + \tl_clear:N #2 \seq_map_inline:cn { - c_@@_struct_\prop_item:cn{ g_@@_struct_#1_prop }{entries}_entries_seq + c_@@_struct_ + \int_compare:nNnTF{#1}={0}{StructTreeRoot}{StructElem} + _entries_seq } { \tl_put_right:Nx - \l_@@_struct_dict_content_tl + #2 { \prop_if_in:cnT { g_@@_struct_#1_prop } @@ -308,35 +595,99 @@ } } } - %\tl_put_right:Nn \l_@@_struct_dict_content_tl { >> } } - - -% #1 is the struct num -\cs_new:Nn \@@_struct_write_obj:n +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@@_struct_write_obj:n} +% This writes out the structure object. +% This is done in the finish code, in the tree module and +% guarded by the tree boolean. +% \begin{macrocode} +\cs_new_protected:Npn \@@_struct_write_obj:n #1 % #1 is the struct num { - \prop_if_in:cnTF - { g_@@_struct_#1_prop } - { objref } + \pdf_object_if_exist:nTF { @@/struct/#1 } { \@@_struct_fill_kid_key:n { #1 } - %\prop_show:c { g_@@_struct_#1_prop } - \@@_struct_get_dict_content:n { #1 } + \@@_struct_get_dict_content:nN { #1 } \l_@@_tmpa_tl \exp_args:Nx \pdf_object_write:nx { @@/struct/#1 } { - \l_@@_struct_dict_content_tl + \l_@@_tmpa_tl } } { \msg_error:nnn { tag } { struct-no-objnum } { #1} } } - -% this command allows to get the current structure tag. We will need to handle nesting -% P -\cs_new:Nn \@@_get_data_struct_tag: +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@@_struct_insert_annot:nn} +% This is the command to insert an annotation into the structure. +% It can probably be used for xform too. +% +% Annotations used as structure content must +% \begin{enumerate} +% \item add a StructParent integer to their dictionary +% \item push the object reference as OBJR object in the structure +% \item Add a Structparent/obj-nr reference to the parent tree. +% \end{enumerate} +% For a link this looks like this +% \begin{verbatim} +% \tag_struct_begin:n { tag=Link } +% \tag_mc_begin:n { tag=Link } +% (1) \pdfannot_dict_put:nnx +% { link/URI } +% { StructParent } +% { \int_use:N\c@g_@@_parenttree_obj_int } +% <start link> link text <stop link> +% (2+3) \@@_struct_insert_annot:nn {obj ref}{parent num} +% \tag_mc_end: +% \tag_struct_end: +% \end{verbatim} +% \begin{macrocode} +\cs_new_protected:Npn \@@_struct_insert_annot:nn #1 #2 %#1 object reference to the annotation/xform + %#2 structparent number + { + \bool_if:NT \g_@@_active_struct_bool + { + %get the number of the parent structure: + \seq_get:NNF + \g_@@_struct_stack_seq + \l_@@_struct_stack_parent_tmpa_tl + { + \msg_error:nn { tag } { struct-faulty-nesting } + } + %put the obj number of the annot in the kid entry, this also creates + %the OBJR object + \@@_struct_kid_OBJR_gput_right:xx + { + \l_@@_struct_stack_parent_tmpa_tl + } + { + #1 % + } + % add the parent obj number to the parent tree: + \exp_args:Nnx + \@@_parenttree_add_objr:nn + { + #2 + } + { + \pdf_object_ref:e { @@/struct/\l_@@_struct_stack_parent_tmpa_tl } + } + % increase the int: + \stepcounter{ g_@@_parenttree_obj_int } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_get_data_struct_tag:} +% this command allows \cs{tag_get:n} to get the current +% structure tag with the keyword |struct_tag|. We will need to handle nesting +% \begin{macrocode} +\cs_new:Npn \@@_get_data_struct_tag: { \exp_args:Ne \tl_tail:n @@ -344,28 +695,33 @@ \prop_item:cn {g_@@_struct_\g__tag_struct_stack_current_tl _prop}{S} } } - -% keys for the user commands -% should I pass the values (e.g. the tag) through an escape command? +% \end{macrocode} +% \end{macro} +% +% \section{Keys} +% This are the keys for the user commands. % we store the tag in a variable. But we should be careful, it is only reliable -% at the begin -\tl_new:N \g_@@_struct_tag_tl -\tl_new:N \g_@@_struct_tag_NS_tl -\cs_generate_variant:Nn \seq_set_split:Nnn{Nne} +% at the begin. +% \begin{macro} +% { +% label,stash,tag, +% title,title-o, +% alttext,alttext-o, +% actualtext,actualtext-o +% lang,ref,E +% } +% \begin{macrocode} \keys_define:nn { @@ / struct } { label .tl_set:N = \l_@@_struct_key_label_tl, stash .bool_set:N = \l_@@_struct_elem_stash_bool, tag .code:n = % S property - {%%????????? \pdfescapename?? slash/not slash is here the question ... - \seq_set_split:Nne \l_tmpa_seq { / } {#1/\prop_item:Nn\g__tag_role_tags_NS_prop{#1}} - \tl_gset:Nx \g_@@_struct_tag_tl { \seq_item:Nn\l_tmpa_seq {1} } - \tl_gset:Nx \g_@@_struct_tag_NS_tl { \seq_item:Nn\l_tmpa_seq {2} } - \bool_if:NT \g_@@_check_tags_bool - { - \@@_check_structure_tag:N \g_@@_struct_tag_tl - } - \@@_prop_gput:cnx + { + \seq_set_split:Nne \l_@@_tmpa_seq { / } {#1/\prop_item:Nn\g__tag_role_tags_NS_prop{#1}} + \tl_gset:Nx \g_@@_struct_tag_tl { \seq_item:Nn\l_@@_tmpa_seq {1} } + \tl_gset:Nx \g_@@_struct_tag_NS_tl { \seq_item:Nn\l_@@_tmpa_seq {2} } + \@@_check_structure_tag:N \g_@@_struct_tag_tl + \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { S } { \pdf_name_from_unicode_e:n{ \g_@@_struct_tag_tl} } % @@ -482,12 +838,17 @@ { <\l_@@_tmpa_str> } }, } - -% keys for AF keys, they rely on l3pdffile! +% \end{macrocode} +% \end{macro} +% \begin{macro}{AF,AFinline,AFinline-o} +% keys for the AF keys (associated files). They use commands from l3pdffile! % The stream variants use txt as extension to get the mimetype. +% TODO: check if this should be configurable. For math we will perhaps need another +% extension. +% \begin{macrocode} \keys_define:nn { @@ / struct } { - AF .code:n = % T property + AF .code:n = % AF property { \pdf_object_if_exist:nTF {#1} { @@ -503,8 +864,7 @@ ,AFinline .code:n = { \group_begin: - \exp_args:Ne - \pdf_object_if_exist:nF {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} + \pdf_object_if_exist:eF {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} { \pdffile_embed_stream:nxx {#1} @@ -520,8 +880,7 @@ ,AFinline-o .code:n = { \group_begin: - \exp_args:Ne - \pdf_object_if_exist:nF {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} + \pdf_object_if_exist:eF {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} { \pdffile_embed_stream:oxx {#1} @@ -535,81 +894,70 @@ \group_end: } } - -\cs_new_protected:Nn \tag_struct_begin:n +% \end{macrocode} +% \end{macro} +% +% \section{User commands} +% +% \begin{macro}{\tag_struct_begin:n,\tag_struct_end:} +% \begin{macrocode} +\cs_new_protected:Npn \tag_struct_begin:n #1 %#1 key-val { - \group_begin: - \int_gincr:N \c@g_@@_struct_abs_int - \@@_prop_new:c { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } - \@@_new_output_prop_handler:n {\int_eval:n { \c@g_@@_struct_abs_int }} - \@@_seq_new:c { g_@@_struct_kids_\int_eval:n { \c@g_@@_struct_abs_int }_seq} - %\@@_pdfreserveobjnum:N \l_tmpa_tl - \exp_args:Ne - \pdf_object_new:nn - { @@/struct/\int_eval:n { \c@g_@@_struct_abs_int } } - { dict } - \@@_prop_gput:cnx - { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } - { objref} + \@@_check_if_active_struct:T { + \group_begin: + \int_gincr:N \c@g_@@_struct_abs_int + \@@_prop_new:c { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } + \@@_new_output_prop_handler:n {\int_eval:n { \c@g_@@_struct_abs_int }} + \@@_seq_new:c { g_@@_struct_kids_\int_eval:n { \c@g_@@_struct_abs_int }_seq} \exp_args:Ne - \pdf_object_ref:n - {@@/struct/\int_eval:n { \c@g_@@_struct_abs_int }} - } -% \prop_show:c { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } - \@@_prop_gput:cnx - { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } - { num} - { \int_eval:n { \c@g_@@_struct_abs_int } } - \@@_prop_gput:cno - { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } - { Type } - { /StructElem } - \@@_prop_gput:cno - { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } - { entries } - { StructElem } - \keys_set:nn { @@ / struct} { #1 } - \@@_check_structure_has_tag:n { \int_eval:n {\c@g_@@_struct_abs_int} } - \tl_if_empty:NF - \l_@@_struct_key_label_tl - { - \@@_ref_label:en{tagpdfstruct-\l_@@_struct_key_label_tl}{struct} - } - %get the potential parent from the stack: - \seq_get:NNF - \g_@@_struct_stack_seq - \l_@@_struct_stack_parent_tmp_tl - { - \msg_error:nn { tag } { struct-faulty-nesting } - } - \seq_gpush:NV \g_@@_struct_stack_seq \c@g_@@_struct_abs_int - \seq_gpush:NV \g_@@_struct_tag_stack_seq \g_@@_struct_tag_tl - \tl_gset:NV \g_@@_struct_stack_current_tl \c@g_@@_struct_abs_int - %\seq_show:N \g_@@_struct_stack_seq - \bool_if:NF - \l_@@_struct_elem_stash_bool - {%set the parent - \@@_prop_gput:cnx - { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } - { P } + \pdf_object_new:nn + { @@/struct/\int_eval:n { \c@g_@@_struct_abs_int } } + { dict } + \@@_prop_gput:cno + { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } + { Type } + { /StructElem } + \keys_set:nn { @@ / struct} { #1 } + \@@_check_structure_has_tag:n { \int_eval:n {\c@g_@@_struct_abs_int} } + \tl_if_empty:NF + \l_@@_struct_key_label_tl + { + \@@_ref_label:en{tagpdfstruct-\l_@@_struct_key_label_tl}{struct} + } + %get the potential parent from the stack: + \seq_get:NNF + \g_@@_struct_stack_seq + \l_@@_struct_stack_parent_tmpa_tl { - \prop_item:cn - { g_@@_struct_\l_@@_struct_stack_parent_tmp_tl _prop} - { objref } + \msg_error:nn { tag } { struct-faulty-nesting } + } + \seq_gpush:NV \g_@@_struct_stack_seq \c@g_@@_struct_abs_int + \seq_gpush:NV \g_@@_struct_tag_stack_seq \g_@@_struct_tag_tl + \tl_gset:NV \g_@@_struct_stack_current_tl \c@g_@@_struct_abs_int + %\seq_show:N \g_@@_struct_stack_seq + \bool_if:NF + \l_@@_struct_elem_stash_bool + {%set the parent + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { P } + { + \pdf_object_ref:e { @@/struct/\l_@@_struct_stack_parent_tmpa_tl } + } + %record this structure as kid: + %\tl_show:N \g_@@_struct_stack_current_tl + %\tl_show:N \l_@@_struct_stack_parent_tmpa_tl + \@@_struct_kid_struct_gput_right:xx + { \l_@@_struct_stack_parent_tmpa_tl } + { \g_@@_struct_stack_current_tl } + %\prop_show:c { g_@@_struct_\g_@@_struct_stack_current_tl _prop } + %\seq_show:c {g_@@_struct_kids_\l_@@_struct_stack_parent_tmpa_tl _seq} } - %record this structure as kid: - %\tl_show:N \g_@@_struct_stack_current_tl - %\tl_show:N \l_@@_struct_stack_parent_tmp_tl - \@@_struct_kid_struct_gput_right:nn - { \l_@@_struct_stack_parent_tmp_tl } - { \g_@@_struct_stack_current_tl } %\prop_show:c { g_@@_struct_\g_@@_struct_stack_current_tl _prop } - %\seq_show:c {g_@@_struct_kids_\l_@@_struct_stack_parent_tmp_tl _seq} - } - %\prop_show:c { g_@@_struct_\g_@@_struct_stack_current_tl _prop } - %\seq_show:c {g_@@_struct_kids_\l_@@_struct_stack_parent_tmp_tl _seq} - \group_end: + %\seq_show:c {g_@@_struct_kids_\l_@@_struct_stack_parent_tmpa_tl _seq} + \group_end: + } } @@ -617,149 +965,145 @@ { %take the current structure num from the stack: %the objects are written later, lua mode hasn't all needed info yet %\seq_show:N \g_@@_struct_stack_seq - \seq_gpop:NN \g_@@_struct_tag_stack_seq \l_tmpa_tl - \seq_gpop:NNTF \g_@@_struct_stack_seq \l_tmpa_tl + \@@_check_if_active_struct:T { - \int_compare:nNnT {\l_@@_loglevel_int} > { 0 } + \seq_gpop:NN \g_@@_struct_tag_stack_seq \l_@@_tmpa_tl + \seq_gpop:NNTF \g_@@_struct_stack_seq \l_@@_tmpa_tl { \@@_check_info_closing_struct:o { \g_@@_struct_stack_current_tl } } - } - { \@@_check_no_open_struck: } - % get the previous one, shouldn't be empty as the root should be there - \seq_get:NNTF \g_@@_struct_stack_seq \l_tmpa_tl - { - \tl_gset:NV \g_@@_struct_stack_current_tl \l_tmpa_tl - } - { - \@@_check_no_open_struck: - } - \seq_get:NNT \g_@@_struct_tag_stack_seq \l_tmpa_tl - { - \tl_gset:NV \g_@@_struct_tag_tl \l_tmpa_tl - } - } - - - -\cs_new_protected:Nn \tag_struct_use:n %#1 is the label - { - \prop_if_exist:cTF - { g_@@_struct_\@@_ref_value:enn{tagpdfstruct-#1}{tagstruct}{unknown}_prop } %?????????? - { - \@@_check_struct_used:n {#1} - %add the label structure as kid to the current structure (can be the root) - \@@_struct_kid_struct_gput_right:nn - { \g_@@_struct_stack_current_tl } - { \@@_ref_value:enn{tagpdfstruct-#1}{tagstruct}{0} } - %add the current structure to the labeled one as parents - \@@_prop_gput:cnx - { g_@@_struct_\@@_ref_value:enn{tagpdfstruct-#1}{tagstruct}{0}_prop } - { P } + { \@@_check_no_open_struct: } + % get the previous one, shouldn't be empty as the root should be there + \seq_get:NNTF \g_@@_struct_stack_seq \l_@@_tmpa_tl { - \prop_item:cn - { g_@@_struct_\g_@@_struct_stack_current_tl _prop} - { objref } + \tl_gset:NV \g_@@_struct_stack_current_tl \l_@@_tmpa_tl } - } - { - \msg_warning:nnn{ tag }{struct-label-unknown}{#1} - } - } - - -%%%% Code to tag annotations like links. -% Annotations used as structure content must -% (1) add a StructParent integer to their dictionary -% (2) push the object reference as OBJR object in the structure -% (3) Add a Stuctparent/obj-nr reference to the parent three. -% For a link this looks like this -% \tag_struct_begin:n { tag=Link } -% \tag_mc_begin:n { tag=Link } -% (1) \pdfannot_dict_put:nnx -% { link/URI } -% { StructParent } -% { \int_use:N\c@g_@@_parenttree_obj_int } -% <start link> link text <stop link> -% (2+3) \@@_struct_insert_annot:nn {obj ref}{parent num} -% \tag_mc_end: -% \tag_struct_end: -% -%%%% this works for url, see exp-link.pdf. -%%%% it must be checked for other links - - -% this will probably be used for xform too, but then we can rename it. -\cs_new_protected:Npn \@@_struct_insert_annot:nn #1 #2 %#1 object reference to the annotation/xform - %#2 structparent number - { - \bool_if:NT \g_@@_active_struct_bool - { - %get the number of the parent link structure: - \seq_get:NNF - \g_@@_struct_stack_seq - \l_@@_struct_stack_parent_tmp_tl { - \msg_error:nn { tag } { struct-faulty-nesting } + \@@_check_no_open_struct: } - %put the obj number of link annot in the kid entry: - \@@_struct_kid_OBJR_gput_right:nx + \seq_get:NNT \g_@@_struct_tag_stack_seq \l_@@_tmpa_tl { - \l_@@_struct_stack_parent_tmp_tl + \tl_gset:NV \g_@@_struct_tag_tl \l_@@_tmpa_tl } + } + } +% \end{macrocode} +% \end{macro} +% \begin{macro}{\tag_struct_use:n} +% This command allows to use a stashed structure in another place. +% TODO: decide how it should be guarded. Probably by the struct-check. +% \begin{macrocode} +\cs_new_protected:Nn \tag_struct_use:n %#1 is the label + { + \@@_check_if_active_struct:T + { + \prop_if_exist:cTF + { g_@@_struct_\@@_ref_value:enn{tagpdfstruct-#1}{tagstruct}{unknown}_prop } % { - #1 % - } - % add the parent obj number to the parent tree: - \exp_args:Nnx - \@@_parenttree_add_objr:nn - { - #2 + \@@_check_struct_used:n {#1} + %add the label structure as kid to the current structure (can be the root) + \@@_struct_kid_struct_gput_right:xx + { \g_@@_struct_stack_current_tl } + { \@@_ref_value:enn{tagpdfstruct-#1}{tagstruct}{0} } + %add the current structure to the labeled one as parents + \@@_prop_gput:cnx + { g_@@_struct_\@@_ref_value:enn{tagpdfstruct-#1}{tagstruct}{0}_prop } + { P } + { + \pdf_object_ref:e { @@/struct/\g_@@_struct_stack_current_tl } + } } { - \prop_item:cn - { g_@@_struct_\l_@@_struct_stack_parent_tmp_tl _prop } - { objref } + \msg_warning:nnn{ tag }{struct-label-unknown}{#1} } - % increase the int: - \stepcounter{ g_@@_parenttree_obj_int } } } - - - -% Code to add an annot to the structure. -% This is for example needed for widget annotations and form field -% It reuses the command used by links +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \tag_struct_insert_annot:nn, +% \tag_struct_insert_annot:xx +% } +% \begin{macro}[EXP] +% { +% \tag_struct_parent_int: +% } +% This are the user command to insert annotations. They must be used +% together to get the numbers right. They use a counter to the +% |StructParent| and \cs{tag_struct_insert_annot:nn} increases the +% counter given back by \cs{tag_struct_parent_int:}. +% +% It must be used together with |\tag_struct_parent_int:| to insert an +% annotation. +% TODO: decide how it should be guarded if tagging is deactivated. +% \begin{macrocode} \cs_new_protected:Npn \tag_struct_insert_annot:nn #1 #2 %#1 should be an object reference %#2 struct parent num { - \@@_struct_insert_annot:nn {#1}{#2} + \@@_check_if_active_struct:T + { + \@@_struct_insert_annot:nn {#1}{#2} + } } \cs_generate_variant:Nn \tag_struct_insert_annot:nn {xx} \cs_new:Npn \tag_struct_parent_int: {\int_use:c { c@g_@@_parenttree_obj_int }} -%</struct> +%</package> % \end{macrocode} +% \end{macro} +% \end{macro} +% \section{Attributes and attribute classes} % \begin{macrocode} -%<*attr> -\ProvidesExplPackage {tagpdf-attr-code} {2021-06-14} {0.82} +%<*header> +\ProvidesExplPackage {tagpdf-attr-code} {2021-06-29} {0.9} {part of tagpdf - code related to attributes and attribute classes} - -% the obj is written in tagpdf-tree-code. - -\seq_new:N \g_@@_attr_class_used_seq -\prop_new:N \g_@@_attr_objref_prop %will contain obj num of used attributes - +%</header> +% \end{macrocode} +% \subsection{Variables} +% \begin{variable} +% { +% ,\g_@@_attr_entries_prop +% ,\g_@@_attr_class_used_seq +% ,\g_@@_attr_objref_prop +% ,\l_@@_attr_value_tl +% } +% |\g_@@_attr_entries_prop| will store attribute names and their dictionary content.\\ +% |\g_@@_attr_class_used_seq| will hold the attributes which have been used as +% class name. +% |\l_@@_attr_value_tl| is used to build the attribute array or key. +% Everytime an attribute is used for the first time, and object is created +% with its content, the name-object reference relation is stored in +% |\g_@@_attr_objref_prop| +% \begin{macrocode} +%<*package> \prop_new:N \g_@@_attr_entries_prop -\tl_new:N \g_@@_attr_class_content_tl -\tl_new:N \l_@@_attr_objtmp_tl +\seq_new:N \g_@@_attr_class_used_seq \tl_new:N \l_@@_attr_value_tl - - -\cs_new_protected:Nn \@@_attr_new_entry:nn %#1:name, #2: content +\prop_new:N \g_@@_attr_objref_prop %will contain obj num of used attributes +% \end{macrocode} +% \end{variable} +% \subsection{Commands and keys} +% \begin{macro}{\@@_attr_new_entry:nn,newattribute} +% This allows to define attributes. Defined attributes +% are stored in a global property. |newattribute| expects +% two brace group, the name and the content. The content typically +% needs an |/O| key for the owner. An example look like +% this. +% \begin{verbatim} +% \tagpdfsetup +% { +% newattribute = +% {TH-col}{/O /Table /Scope /Column}, +% newattribute = +% {TH-row}{/O /Table /Scope /Row}, +% } +% \end{verbatim} +% \begin{macrocode} +\cs_new_protected:Npn \@@_attr_new_entry:nn #1 #2 %#1:name, #2: content { \prop_gput:Nnn \g_@@_attr_entries_prop {#1}{#2} @@ -772,16 +1116,19 @@ \@@_attr_new_entry:nn #1 } } - - -% the key for the structure: +% \end{macrocode} +% \end{macro} +% \begin{macro}{attribute-class} +% attribute-class has to store the used attribute names so that +% they can be added to the ClassMap later. +% \begin{macrocode} \keys_define:nn { @@ / struct } { attribute-class .code:n = { - \clist_set:No \l_tmpa_clist { #1 } - \seq_set_from_clist:NN \l_tmpa_seq \l_tmpa_clist - \seq_map_inline:Nn \l_tmpa_seq + \clist_set:No \l_@@_tmpa_clist { #1 } + \seq_set_from_clist:NN \l_@@_tmpa_seq \l_@@_tmpa_clist + \seq_map_inline:Nn \l_@@_tmpa_seq { \prop_if_in:NnF \g_@@_attr_entries_prop {##1} { @@ -789,38 +1136,41 @@ } \seq_gput_left:Nn\g_@@_attr_class_used_seq { ##1} } - \seq_set_map:NNn \l_tmpb_seq \l_tmpa_seq + \seq_set_map:NNn \l_@@_tmpb_seq \l_@@_tmpa_seq { /##1 } - \tl_set:Nx \l_tmpa_tl + \tl_set:Nx \l_@@_tmpa_tl { - \int_compare:nT { \seq_count:N \l_tmpa_seq > 1 }{[} - \seq_use:Nn \l_tmpb_seq { \c_space_tl } - \int_compare:nT { \seq_count:N \l_tmpa_seq > 1 }{]} + \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 1 }{[} + \seq_use:Nn \l_@@_tmpb_seq { \c_space_tl } + \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 1 }{]} } - \int_compare:nT { \seq_count:N \l_tmpa_seq > 0 } + \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 0 } { \@@_prop_gput:cnx { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } { C } - { \l_tmpa_tl } + { \l_@@_tmpa_tl } %\prop_show:c { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } } - } + } } - +% \end{macrocode} +% \end{macro} +% \begin{macro}{attribute} +% \begin{macrocode} \keys_define:nn { @@ / struct } { attribute .code:n = % A property (attribute, value currently a dictionary) { - \clist_set:No \l_tmpa_clist { #1 } - \seq_set_from_clist:NN \l_tmpa_seq \l_tmpa_clist + \clist_set:No \l_@@_tmpa_clist { #1 } + \seq_set_from_clist:NN \l_@@_tmpa_seq \l_@@_tmpa_clist \tl_set:Nx \l_@@_attr_value_tl { - \int_compare:nT { \seq_count:N \l_tmpa_seq > 1 }{[}%] + \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 1 }{[}%] } - \seq_map_inline:Nn \l_tmpa_seq + \seq_map_inline:Nn \l_@@_tmpa_seq { \prop_if_in:NnF \g_@@_attr_entries_prop {##1} { @@ -844,7 +1194,7 @@ } \tl_put_right:Nx \l_@@_attr_value_tl { %[ - \int_compare:nT { \seq_count:N \l_tmpa_seq > 1 }{]}% + \int_compare:nT { \seq_count:N \l_@@_tmpa_seq > 1 }{]}% } % \tl_show:N \l_@@_attr_value_tl \@@_prop_gput:cnx @@ -853,5 +1203,7 @@ { \l_@@_attr_value_tl } }, } -%</attr> +%</package> % \end{macrocode} +% \end{macro} +% \end{implementation} diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-tree.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-tree.dtx index c3e5d6d71b2..6a1d3e95622 100644 --- a/Master/texmf-dist/source/latex/tagpdf/tagpdf-tree.dtx +++ b/Master/texmf-dist/source/latex/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 diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-user.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-user.dtx new file mode 100644 index 00000000000..cbc6b79c334 --- /dev/null +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-user.dtx @@ -0,0 +1,585 @@ +% \iffalse meta-comment +% +%% File: tagpdf-user.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. +%<*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-user module (tagpdf)} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \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.9, released 2021-06-29} +% \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} +% +% \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}{\tagmcifin} +% \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 documentated 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 +% necessarly stops the compilation. The keys and their function are described below. +% \end{function} +% +% \begin{function}{mc-data} +% \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} +% \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}{struct-stack} +% \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} +% +% \section{Extension commands} +% The following commands and code parts are not core command 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{Paratagging} +% +% This is a first try to make use of the new paragraph hooks in a current 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}{paratagging,paratagging-show} +% \begin{syntax} +% |paratagging| = |true|\verb+|+|false|\\ +% |paratagging-show| = |true|\verb+|+|false|\\ +% \end{syntax} +% This keys can be used in |\tagpdfsetup| and enable/disable paratagging. +% |paratagging-show| puts small red 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} +% +% \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} +% \end{documentation} +% +% \begin{implementation} +% \section{User commands and extensions of document commands} +% \begin{macrocode} +%<@@=tag> +%<*header> +\ProvidesExplPackage {tagpdf-user} {2021-06-29} {0.9} + {tagpdf - user commands} +%</header> +% \end{macrocode} +% \section{Setup and preamble commands} +% \begin{macro}{\tagpdfsetup} +% \begin{macrocode} +%<*package> +\NewDocumentCommand \tagpdfsetup { m } + { + \keys_set:nn { @@ / setup } { #1 } + } +% \end{macrocode} +% \end{macro} +% + +% \section{Commands for the mc-chunks} +% \begin{macro}{\tagmcbegin,\tagmcend,\tagmcuse } +% \begin{macrocode} +\NewDocumentCommand \tagmcbegin { m } + { + \tag_mc_begin:n {#1}%\ignorespaces + } + + +\NewDocumentCommand \tagmcend { } + { + %\if_mode_horizontal: \unskip \fi: % + \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} +\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} +\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} +% +% +% \begin{macro}{\tagpdfifluatexTF,\tagpdfifluatexT,\tagpdfifpdftexTF} +% I should deprecate them ... +% \begin{macrocode} +\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 +% \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} +\NewDocumentCommand\ShowTagging { m } + { + \keys_set:nn { @@ / show }{ #1} + + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{mc-data} +% 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} +% 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__tag_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}{struct-stack} +% \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} +% +% \section{Commands to extend document commands} +% The following commands and code parts are not core command of tagpdf. +% The 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. +% \subsection{Document structure} +% \begin{macro}{\@@_add_document_structure:n,activate} +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_document_structure:n #1 + { + \hook_gput_code:nnn{begindocument}{tagpdf}{\tagstructbegin{tag=#1}} + \hook_gput_code:nnn{tagpdf/finish/before}{tagpdf}{\tagstructend} + } +\keys_define:nn { @@ / setup} + { + activate .code:n = + { + \keys_set:nn { @@ / setup } + { activate-mc,activate-tree,activate-struct } + \@@_add_document_structure:n {#1} + }, + activate .default:n = Document + } +% \end{macrocode} +% \end{macro} +% \subsection{Fake space} +% \begin{macro}{\pdffakespace} +% We need a luatex variant for |\pdffakespace|. This should probably go into +% the kernel at some time. +% \begin{macrocode} +\sys_if_engine_luatex:T + { + \NewDocumentCommand\pdffakespace { } + { + \@@_fakespace: + } + } +% \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_show_bool,\g_@@_para_int} +% At first some variables. +% \begin{macrocode} +\bool_new:N \l_@@_para_bool +\bool_new:N \l_@@_para_show_bool +\int_new:N \g_@@_para_int +% \end{macrocode} +% \end{macro} +% +% +%\begin{macro} +% { +% paratagging, +% paratagging-show, +% } +% These keys enable/disable locally paratagging, and the debug modus. +% It can affect the typesetting if |paratagging-show| is used. The small numbers +% are boxes and they have a (small) height. +% \begin{macrocode} +\keys_define:nn { @@ / setup } + { + paratagging .bool_set:N = \l_@@_para_bool, + paratagging-show .bool_set:N = \l_@@_para_show_bool, + } + +% \end{macrocode} +% \end{macro} +% This fills the para hooks with the needed code. +% \begin{macrocode} +\AddToHook{para/begin} + { + \int_gincr:N \g_@@_para_int + \bool_if:NT \l__tag_para_bool + { + \tag_struct_begin:n {tag=P} + \bool_if:NT \l_@@_para_show_bool + { \tag_mc_begin:n{artifact} + \llap{\color_select:n{red}\tiny\int_use:N\g_@@_para_int\ } + \tag_mc_end: + } + \tag_mc_begin:n {tag=P} + } + } +\AddToHook{para/end} + { + \bool_if:NT \l__tag_para_bool + { + \tag_mc_end: + \bool_if:NT \l_@@_para_show_bool + { \tag_mc_begin:n{artifact} + \rlap{\color_select:n{red}\tiny\ \int_use:N\g_@@_para_int} + \tag_mc_end: + } + \tag_struct_end: + } + } +% \end{macrocode} +% \begin{macro} +% { +% \tagpdfparaOn, +% \tagpdfparaOff +% } +% This two command switch para mode on and off. |\tagpdfsetup| could be used +% too but is longer. +% \begin{macrocode} +\newcommand\tagpdfparaOn {\bool_set_true:N \l__tag_para_bool} +\newcommand\tagpdfparaOff{\bool_set_false:N \l__tag_para_bool} +% \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:nnx + { link/URI } + { StructParent } + { \tag_struct_parent_int: } + } + +\hook_gput_code:nnn + {pdfannot/link/URI/after} + {tagpdf} + { + \tag_struct_insert_annot:xx {\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:nnx + { link/GoTo } + { StructParent } + { \tag_struct_parent_int: } + } + +\hook_gput_code:nnn + {pdfannot/link/GoTo/after} + {tagpdf} + { + \tag_struct_insert_annot:xx {\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} +%</package> +% \end{implementation} +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf.dtx index 959e7c0097b..6b79f8e7aaf 100644 --- a/Master/texmf-dist/source/latex/tagpdf/tagpdf.dtx +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf.dtx @@ -2,7 +2,7 @@ % %% File: tagpdf.dtx % -% Copyright (C) 2019-2020 Ulrike Fischer +% 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 @@ -24,10 +24,12 @@ % % \fi % +% \begin{implementation} +% \section{Initialization and test if pdfmanagement is active.} % \begin{macrocode} %<@@=tag> %<*package> -\ProvidesExplPackage {tagpdf} {2021-06-14} {0.82} +\ProvidesExplPackage {tagpdf} {2021-06-29} {0.9} { A package to experiment with pdf tagging } \bool_if:nF @@ -49,33 +51,36 @@ before~\string\documentclass } } - -%\debug_on:n{check-declarations,check-expressions,deprecation} - -%map internal tag to package name +% \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 } } - -% storing internal names to my name space: -\cs_set_eq:NN \@@_tex_global:D \tex_global:D - -%%% package options +% \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 - -%%% some packages -%references use l3ref-tmp +% \end{macrocode} +% \section{Packages} +% We need the temporary version of l3ref until this is in the kernel. +% \begin{macrocode} \RequirePackage{l3ref-tmp} - %================ - % temporary code - % =============== - % faking the LastPage label: +% \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 } @@ -94,8 +99,17 @@ \AddToHook{enddocument/afterlastpage} {\@@_lastpagelabel:} - - % a command to override the general default. See issue in ref +% \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 @@ -109,182 +123,216 @@ \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 } } } +% \end{macrocode} +% \end{macro} - %============= - % 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 {@@/struct/\int_use:N \c@g__tag_struct_abs_int} - { - \pdf_object_ref:e{@@/struct/\int_use:N \c@g_@@_struct_abs_int} - } - } - % 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!! +% \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 +% } +% 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 +% \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} -%================= -% command to set a label -%================ +% \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 } -%============== -% command to retrieve the value -%=============== +\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} } -%=============== -% the LastPage label has a different name, so we need a special command for now -% ============= +\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} } - -% 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: }, - global-mc .code:n = - { - \cs_set_eq:NN\@@_attribute_set:Nn \@@_attribute_gset:Nn - \cs_set_eq:NN\@@_attribute_unset:N\@@_attribute_gunset:N - } - } -%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 } - } - \keys_define:nn { @@ / setup } - { - global-mc .code:n={} - } - } -% 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 - } +% \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 } @@ -292,439 +340,110 @@ \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 } -%</package> -% \end{macrocode} -% -% \begin{macrocode} -%<*user> -\ProvidesExplPackage {tagpdf-user} {2021-06-14} {0.82} - {tagpdf - user commands} -% \end{macrocode} - -% \begin{macro}{\tagpdfsetup} -% \begin{macrocode} -\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 % \end{macrocode} % \end{macro} -% \begin{macro}{\tag_if_active:} -% This is a test it tagging is active. This allows packages -% to add conditional code. We probably will need a better -% test in the end, but for now it should be ok. -% \begin{macrocode} -\prg_new_conditional:Npnn \tag_if_active: { p , T , TF, F } - { - \bool_if:NTF\g_@@_active_struct_bool - { - \prg_return_true: - } - { - \prg_return_false: - } - } -% \end{macrocode} -% \end{macro} -% -% We need a command to stop tagging in some places. % +% \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} -\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 - } - \cs_new_protected:Npn \tag_stop_group_begin: { \group_begin: - \@@_stop: + \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} -% We need a command to stop tagging in some places. % -% \begin{macro}{ \tagpdfget, \tag_get:n } -% This is a generic command to retrieve data. Currently -% the only sensible value for the argument is |mc_tag| -% \begin{macrocode} -\cs_new:Npn \tagpdfget #1 { \use:c {@@_get_data_#1: } } -\cs_new:Npn \tag_get:n #1 { \use:c {@@_get_data_#1: } } +% \section{Keys for tagpdfsetup} -% \end{macrocode} -% \end{macro} +% TODO: the log-levels must be sorted % -% \begin{macro}{\tagmcifinTF} -% This is a test 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. +% \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} -\NewDocumentCommand \tagmcifinTF { m m } +\keys_define:nn { @@ / setup } { - \@@_mc_if_in:TF { #1 } { #2 } - } -% \end{macrocode} + 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}{tagmcartifact} +% \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} -\NewDocumentEnvironment{tagmcartifact}{m} - { - \tag_mc_artifact_group_begin:n { #1}\ignorespaces - } - { - \tag_mc_artifact_group_end: - } - + 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 }}, % \end{macrocode} % \end{macro} -% -% \begin{macro}{\tagmcbegin,\tagmcend,\tagmcuse } +% \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} -\NewDocumentCommand \tagmcbegin { m } - { - \tag_mc_begin:n {#1}\ignorespaces - } - - -\NewDocumentCommand \tagmcend { } - { - \if_mode_horizontal: \unskip \fi: % - \tag_mc_end: - } - -\NewDocumentCommand \tagmcuse { m } - { - \tag_mc_use:n {#1} - } - + tagunmarked .bool_gset:N = \g_@@_tagunmarked_bool, + tagunmarked .initial:n = true, % \end{macrocode} % \end{macro} -% -% \begin{macro}{\tagstructbegin,\tagstructend,\tagstructuse } -% -% These are structure related user commands +% \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} -\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{Debugging} -% -% \begin{macro}{\showtagpdfmcdata,\showtagpdfattributes } -% This commands are lua only -% \begin{macrocode} -\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 - } - } - } - -\NewDocumentCommand\showtagstack {} - { - \seq_show:N \g_@@_struct_tag_stack_seq + 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} -% -% \begin{macro}{ } +% \section{loading of engine/more dependent code} % \begin{macrocode} \sys_if_engine_luatex:T { - \NewDocumentCommand\pdffakespace { } - { - \@@_fakespace: - } - } -% \end{macrocode} -% \end{macro} -% \section{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{macrocode} -\bool_new:N \l_@@_para_bool -\bool_new:N \l_@@_para_show_bool -\int_new:N \g_@@_para_int - -\keys_define:nn { @@ / setup } - { - paratagging .bool_set:N = \l_@@_para_bool, - paratagging-show .bool_set:N = \l_@@_para_show_bool, - } - -\AddToHook{para/begin} - { - \int_gincr:N \g_@@_para_int - \bool_if:NT \l__tag_para_bool - { - \tag_struct_begin:n {tag=P} - \bool_if:NT \l_@@_para_show_bool - { \tag_mc_begin:n{artifact} - \llap{\color_select:n{red}\tiny\int_use:N\g_@@_para_int\ } - \tag_mc_end: - } - \tag_mc_begin:n {tag=P} - } - } -\AddToHook{para/end} - { - \bool_if:NT \l__tag_para_bool - { - \tag_mc_end: - \bool_if:NT \l_@@_para_show_bool - { \tag_mc_begin:n{artifact} - \rlap{\color_select:n{red}\tiny\ \int_use:N\g_@@_para_int} - \tag_mc_end: - } - \tag_struct_end: - } + \file_input:n {tagpdf-luatex.def} } - - -\newcommand\tagpdfparaOn {\bool_set_true:N \l__tag_para_bool} -\newcommand\tagpdfparaOff{\bool_set_false:N \l__tag_para_bool} - +%</package> % \end{macrocode} -% \section{Links} -% We need to close and reopen mc-chunks around links. -% With luatex the push/pop command must be outside groups, -% so we add also code to the generic commands in the hyperref driver. % \begin{macrocode} -\cs_generate_variant:Nn \pdfannot_dict_put:nnn {nnx} -\hook_gput_code:nnn - {pdfannot/link/URI/before} - {tagpdf} - { - \bool_if:NT \g_@@_active_struct_bool - { - \tag_mc_end_push: - \tag_struct_begin:n { tag=Link } - \tag_mc_begin:n { tag=Link } - \pdfannot_dict_put:nnx - { link/URI } - { StructParent } - { \tag_struct_parent_int: } - } - } - -\hook_gput_code:nnn - {pdfannot/link/URI/after} - {tagpdf} +%<*mcloading> +\bool_if:NTF \g_@@_mode_lua_bool { - \bool_if:NT \g_@@_active_struct_bool - { - \tag_struct_insert_annot:xx {\pdfannot_link_ref_last:}{\tag_struct_parent_int:} - \tag_mc_end: - \tag_struct_end: - \tag_mc_begin_pop:n{} - } + \RequirePackage {tagpdf-mc-code-lua} } - -\hook_gput_code:nnn - {pdfannot/link/GoTo/before} - {tagpdf} { - \bool_if:NT \g_@@_active_struct_bool - { - \tag_mc_end_push: - \tag_struct_begin:n{tag=Link} - \tag_mc_begin:n{tag=Link} - \pdfannot_dict_put:nnx - { link/GoTo } - { StructParent } - { \tag_struct_parent_int: } - } + \RequirePackage {tagpdf-mc-code-generic} % } - -\hook_gput_code:nnn - {pdfannot/link/GoTo/after} - {tagpdf} - { - \bool_if:NT \g_@@_active_struct_bool - { - \tag_struct_insert_annot:xx {\pdfannot_link_ref_last:}{\tag_struct_parent_int:} - \tag_mc_end: - \tag_struct_end: - \tag_mc_begin_pop:n{} - } - } - -\hook_gput_code:nnn - {cmd/url/before} - {tagpdf} - {\tag_mc_end_push:} - -\hook_gput_code:nnn - {cmd/url/after} - {tagpdf} - {\tag_mc_begin_pop:n{}} - -\hook_gput_code:nnn - {cmd/href/before} - {tagpdf} - {\tag_mc_end_push:} - -\hook_gput_code:nnn - {cmd/href/after} - {tagpdf} - {\tag_mc_begin_pop:n{}} - -\hook_gput_code:nnn - {cmd/hrefurl/before} - {tagpdf} - {\tag_mc_end_push:} - -\hook_gput_code:nnn - {cmd/hrefurl/after} - {tagpdf} - {\tag_mc_begin_pop:n{}} - -%this will go when references are improved -\hook_gput_code:nnn - {cmd/T@ref/before} - {tagpdf} - {\tag_mc_end_push:} - -\hook_gput_code:nnn - {cmd/T@ref/after} - {tagpdf} - {\tag_mc_begin_pop:n{}} - -\hook_gput_code:nnn - {cmd/T@pageref/before} - {tagpdf} - {\tag_mc_end_push:} - -\hook_gput_code:nnn - {cmd/T@pageref/after} - {tagpdf} - {\tag_mc_begin_pop:n{}} - -\hook_gput_code:nnn - {cmd/T@nameref/before} - {tagpdf} - {\tag_mc_end_push:} - -\hook_gput_code:nnn - {cmd/T@nameref/after} - {tagpdf} - {\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) } - +%</mcloading> % \end{macrocode} -%</user> -% +% \end{implementation} +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf.ins b/Master/texmf-dist/source/latex/tagpdf/tagpdf.ins index c94a1808ded..583509d4a24 100644 --- a/Master/texmf-dist/source/latex/tagpdf/tagpdf.ins +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf.ins @@ -2,7 +2,7 @@ File: tagpdf.ins -Copyright (C) 2019 Ulrike Fischer +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 @@ -34,7 +34,7 @@ for those people who are interested. \let\MetaPrefix\relax \preamble -Copyright (C) 2019 Ulrike Fischer +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 @@ -55,41 +55,28 @@ and all files in that bundle must be distributed together. \keepsilent \generate - {\file{tagpdf.sty} {\from{tagpdf.dtx}{package}}} -\generate - {\file{tagpdf-user.sty} {\from{tagpdf.dtx}{user}}} -\generate - {\file{tagpdf-deprecated.def}{\from{tagpdf.dtx}{deprecated}}} + {\file{tagpdf.sty} + { + \from{tagpdf.dtx}{package} + \from{tagpdf-checks.dtx}{package} + \from{tagpdf-user.dtx}{package} + \from{tagpdf-mc-shared.dtx}{shared} + \from{tagpdf.dtx}{mcloading} + \from{tagpdf-tree.dtx}{package} + \from{tagpdf-roles.dtx}{package} + \from{tagpdf-struct.dtx}{package}%contains also the attr code. + \from{tagpdf-space.dtx}{package} + } + } \generate {\file{tagpdf-luatex.def}{\from{tagpdf-backend.dtx}{luatex}}} \generate - {\file{tagpdf-pdftex.def}{\from{tagpdf-backend.dtx}{pdftex}}} - -\generate - {\file{tagpdf-mc-code-shared.sty}{\from{tagpdf-mc.dtx}{shared}}} -\generate - {\file{tagpdf-mc-code-generic.sty}{\from{tagpdf-mc.dtx}{generic}}} -\generate - {\file{tagpdf-mc-code-lua.sty}{\from{tagpdf-mc.dtx}{luamode}}} - -\generate - {\file{tagpdf-struct-code.sty}{\from{tagpdf-struct.dtx}{struct}}} -\generate - {\file{tagpdf-attr-code.sty}{\from{tagpdf-struct.dtx}{attr}}} - + {\file{tagpdf-mc-code-generic.sty}{\from{tagpdf-mc-generic.dtx}{generic}}} \generate - {\file{tagpdf-checks-code.sty}{\from{tagpdf-checks.dtx}{checks}}} + {\file{tagpdf-mc-code-lua.sty}{\from{tagpdf-mc-luacode.dtx}{luamode}}} -\generate - {\file{tagpdf-roles-code.sty}{\from{tagpdf-roles.dtx}{roles}}} -\generate - {\file{tagpdf-space-code.sty}{\from{tagpdf-space.dtx}{space}}} - -\generate - {\file{tagpdf-tree-code.sty}{\from{tagpdf-tree.dtx}{tree}}} - \def\MetaPrefix{-- } \def\defaultpostamble{% \MetaPrefix^^J% |