diff options
author | Karl Berry <karl@freefriends.org> | 2021-02-23 22:24:07 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-02-23 22:24:07 +0000 |
commit | 9fc4fc7a3e0c2a085bf32447278e741e6667b206 (patch) | |
tree | dc6883ed69554b06a17e43fc26c77db73e0eaa52 /Master/texmf-dist/source/latex | |
parent | d2957bb0609af40f0c61a7be4b0476c02de5205a (diff) |
tagpdf (23feb21)
git-svn-id: svn://tug.org/texlive/trunk@57857 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex')
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-backend.dtx | 803 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-checks.dtx | 232 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-mc.dtx | 779 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-roles.dtx | 156 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-space.dtx | 89 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx | 789 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf-tree.dtx | 294 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf.dtx | 498 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/tagpdf/tagpdf.ins | 100 |
9 files changed, 3740 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-backend.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-backend.dtx new file mode 100644 index 00000000000..d80d56778bc --- /dev/null +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-backend.dtx @@ -0,0 +1,803 @@ +% \iffalse meta-comment +% +%% File: tagpdf-backend.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> +%<*luatex> +\ProvidesExplFile {tagpdf-luatex.def} {2021/02/23} {0.80} + {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: +{ + \fontencoding{TU}\fontfamily{lmr}\fontseries{m}\fontshape{n}\fontsize{10pt}{10pt}\selectfont +} +\directlua { tagpdf=require('tagpdf.lua') } + +%%%% driver (lualatex) commands +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +% 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 +% To access such a table with a dynamical name create a string and then use +% 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:Nn \@@_luatex_get_table_name:Nn + { + \tl_set_rescan:Nnn #1 { \char_set_catcode_ignore:N \\ } { #2 } + } + + +\cs_new:Nn \@@_prop_new:N + { + \prop_new:N #1 + \@@_luatex_get_table_name:Nn \l_tmpa_tl { #1 } + \directlua { ltx.@@.tables.\l_tmpa_tl = {} } + } + + +\cs_new:Nn \@@_seq_new:N + { + \seq_new:N #1 + \@@_luatex_get_table_name:Nn \l_tmpa_tl { #1 } + \directlua { ltx.@@.tables.\l_tmpa_tl = {} } + } + + +\cs_new:Nn \@@_prop_gput:Nnn + { + \prop_gput:Nnn #1 { #2 } { #3 } + \@@_luatex_get_table_name:Nn \l_tmpa_tl { #1 } + \directlua { ltx.@@.tables.\l_tmpa_tl["#2"] = "#3" } + } + + +\cs_new:Nn \@@_seq_gput_right:Nn + { + \seq_gput_right:Nn #1 { #2 } + \@@_luatex_get_table_name:Nn \l_tmpa_tl { #1 } + \directlua { table.insert(ltx.@@.tables.\l_tmpa_tl, "#2") } + } + +%Hm not quite sure about the naming + +\cs_new:Npn \@@_seq_item:cn #1 #2 + { + \directlua { tex.print(ltx.@@.tables.#1[#2]) } + } + +\cs_new:Npn \@@_prop_item:cn #1 #2 + { + \directlua { tex.print(ltx.@@.tables.#1["#2"]) } + } + +%for debugging commands that show both the seq/prop and the lua tables +\cs_new:Nn \@@_seq_show:N + { + \seq_show:N #1 + \@@_luatex_get_table_name:Nn \l_tmpa_tl { #1 } + \directlua { ltx.@@.trace.log ("lua~sequence~array~\l_tmpa_tl",1) } + \directlua { ltx.@@.trace.show_seq (ltx.@@.tables.\l_tmpa_tl) } + } + +\cs_new:Nn \@@_prop_show:N + { + \prop_show:N #1 + \@@_luatex_get_table_name:Nn \l_tmpa_tl { #1 } + \directlua {ltx.@@.trace.log ("lua~property~table~\l_tmpa_tl",1) } + \directlua {ltx.@@.trace.show_prop (ltx.@@.tables.\l_tmpa_tl) } + } +%</luatex> +% \end{macrocode} +% +% \begin{macrocode} +%<*pdftex> +\ProvidesExplFile {tagpdf-pdftex.def} {2021/02/23} {0.80} + {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> +% \end{macrocode} +% \begin{macrocode} +%<*lua> +-- tagpdf.lua +-- Ulrike Fischer + +local ProvidesLuaModule = { + name = "tagpdf", + version = "0.80", --TAGVERSION + date = "2021-02-23", --TAGDATE + description = "tagpdf lua code", + license = "The LATEX Project Public License 1.3c" +} + +if luatexbase and luatexbase.provides_module then + luatexbase.provides_module (ProvidesLuaModule) +end + +--[[ +The code has quite probably a number of problems + - more variables should be local instead of global + - the naming is not always consistent due to the development of the code + - the traversing of the shipout box must be tested with more complicated setups + - it should probably handle more node types + - +--]] + + + +--[[ +the main table is named ltx.@@. It contains the functions and also the data +collected during the compilation. + +ltx.@@.mc will contain mc connected data. +ltx.@@.struct will contain structure related data. +ltx.@@.page will contain page data +ltx.@@.tables contains also data from mc and struct (from older code). This needs cleaning up. + There are certainly dublettes, but I don't dare yet ... +ltx.@@.func will contain (public) functions. +ltx.@@.trace will contain tracing/loging functions. +local funktions starts with __ +functions meant for users will be in ltx.tag + +functions + ltx.@@.func.get_num_from (tag): takes a tag (string) and returns the id number + ltx.@@.func.output_num_from (tag): takes a tag (string) and prints (to tex) the id number + ltx.@@.func.get_tag_from (num): takes a num and returns the tag + ltx.@@.func.output_tag_from (num): takes a num and prints (to tex) the tag + ltx.@@.func.store_mc_data (num,key,data): stores key=data in ltx.@@.mc[num] + ltx.@@.func.store_mc_label (label,num): stores label=num in ltx.@@.mc.labels + ltx.@@.func.store_mc_kid (mcnum,kid,page): stores the mc-kids of mcnum on page page + ltx.@@.func.store_mc_in_page(mcnum,mcpagecnt,page): stores in the page table the number of mcnum on this page + ltx.@@.func.store_struct_mcabs (structnum,mcnum): stores relations structnum<->mcnum (abs) + ltx.@@.func.mc_insert_kids (mcnum): inserts the /K entries for mcnum by wandering throught the [kids] table + ltx.@@.func.mark_page_elements(box,mcpagecnt,mccntprev,mcopen,name,mctypeprev) : the main function + 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.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_seq: shows a sequence (array) + ltx.@@.trace.show_struct_data (num): shows data of structure num + ltx.@@.trace.show_prop: shows a prop + ltx.@@.trace.log + 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") +local tagunmarkedbool= token.create("g_@@_tagunmarked_bool") +local truebool = token.create("c_true_bool") + +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 +local nodesetattribute = node.set_attribute +local nodenew = node.new +local nodetail = node.tail +local nodeslide = node.slide +local noderemove = node.remove +local nodetraverseid = node.traverse_id +local nodetraverse = node.traverse +local nodeinsertafter = node.insert_after +local nodeinsertbefore = node.insert_before +local pdfpageref = pdf.pageref + +local HLIST = node.id("hlist") +local VLIST = node.id("vlist") +local RULE = node.id("rule") +local DISC = node.id("disc") +local GLUE = node.id("glue") +local GLYPH = node.id("glyph") +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 + + + +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 + +local @@_log = + function (message,loglevel) + if (loglevel or 3) <= tex.count["l_@@_loglevel_int"] then + texio.write_nl("tagpdf: ".. message) + end + end + +ltx.@@.trace.log = @@_log + + +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 + + +local function @@_insert_emc_node (head,current) + local emcnode = nodenew("whatsit","pdf_literal") + emcnode.data = "EMC" + emcnode.mode=1 + head = node.insert_before(head,current,emcnode) + return head +end + + +local function @@_insert_bmc_node (head,current,tag) + local bmcnode = nodenew("whatsit","pdf_literal") + bmcnode.data = "/"..tag.." BMC" + bmcnode.mode=1 + head = node.insert_before(head,current,bmcnode) + return head +end + +local function @@_insert_bdc_node (head,current,tag,dict) + local bdcnode = nodenew("whatsit","pdf_literal") + bdcnode.data = "/"..tag.."<<"..dict..">> BDC" + bdcnode.mode=1 + head = node.insert_before(head,current,bdcnode) + return head +end + +-- this is for debugging the space chars +local function @@_show_spacemark (head,current,color,height) + local markcolor = color or "1 0 0" + local markheight = height or 10 + local pdfstring = node.new("whatsit","pdf_literal") + pdfstring.data = + string.format("q "..markcolor.." RG "..markcolor.." rg 0.4 w 0 %g m 0 %g l S Q",-3,markheight) + head = node.insert_after(head,current,pdfstring) + return head +end + +--[[ a function to mark up places where real space chars should be inserted + it only sets an attribute. +--]] + +local function @@_mark_spaces (head) + local inside_math = false + for n in nodetraverse(head) do + local id = n.id + if id == GLYPH then + local glyph = n + if glyph.next and (glyph.next.id == GLUE) + and not inside_math and (glyph.next.width >0) + then + nodesetattribute(glyph.next,iwspaceattributeid,1) + nodesetattribute(glyph.next,iwfontattributeid,glyph.font) + -- for debugging + if ltx.@@.trace.showspaces then + @@_show_spacemark (head,glyph) + end + elseif glyph.next and (glyph.next.id==KERN) and not inside_math then + local kern = glyph.next + if kern.next and (kern.next.id== GLUE) and (kern.next.width >0) + then + nodesetattribute(kern.next,iwspaceattributeid,1) + nodesetattribute(kern.next,iwfontattributeid,glyph.font) + end + end + elseif id == PENALTY then + local glyph = n + -- ltx.@@.trace.log ("PENALTY ".. n.subtype.."VALUE"..n.penalty,3) + if glyph.next and (glyph.next.id == GLUE) + and not inside_math and (glyph.next.width >0) and n.subtype==0 + then + nodesetattribute(glyph.next,iwspaceattributeid,1) + -- nodesetattribute(glyph.next,iwfontattributeid,glyph.font) + -- for debugging + if ltx.@@.trace.showspaces then + @@_show_spacemark (head,glyph) + end + end + elseif id == MATH then + inside_math = (n.subtype == 0) + end + end + return head +end + +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") + luatexbase.add_to_callback("hpack_filter",@@_mark_spaces,"markspaces") + end +end + +ltx.@@.func.markspaceon=@@_activate_mark_space + +local function @@_deactivate_mark_space () + if luatexbase.in_callback ("pre_linebreak_filter","markspaces") then + luatexbase.remove_from_callback("pre_linebreak_filter","markspaces") + luatexbase.remove_from_callback("hpack_filter","markspaces") + end +end +-- +ltx.@@.func.markspaceoff=@@_deactivate_mark_space + +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 + +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 +end + +--[[ + Now follows the core function + It wades through the shipout box and checks the attributes + ARGUMENTS + box: is a box, + mcpagecnt: num, the current page cnt of mc (should start at -1 in shipout box), needed for recursion + mccntprev: num, the attribute cnt of the previous node/whatever - if different we have a chunk border + mcopen: num, records if some bdc/emc is open + These arguments are only needed for log messages, if not present are replaces by fix strings: + name: string to describe the box + mctypeprev: num, the type attribute of the previous node/whatever + + there are lots of logging messages currently. Should be cleaned up in due course. + One should also find ways to make the function shorter. +--]] + +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 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) + else + ltx.@@.trace.log ("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) + if n.id == HLIST + then -- enter the hlist + mcopen,mcpagecnt,mccntprev,mctypeprev= + ltx.@@.func.mark_page_elements (n,mcpagecnt,mccntprev,mcopen,"INTERNAL HLIST",mctypeprev) + 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 == 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) + 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) + end + -- endmath + ltx.@@.trace.log("CURRENT "..mccnt.." PREV "..mccntprev,3) + if mccnt~=mccntprev then -- a new mc chunk + ltx.@@.trace.log ("NODE ".. node.type(node.getid(n)).." MC"..tostring(mccnt).." <=> PREVIOUS "..tostring(mccntprev),3) + 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) + if mcopen ~=0 then + ltx.@@.trace.log ("!WARNING! open mc" .. " MCOPEN = " .. 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) + 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) + mcpagecnt = mcpagecnt +1 + ltx.@@.trace.log ("INSERT BDC "..mcpagecnt,2) + local dict= "/MCID "..mcpagecnt + if ltx.@@.mc[mccnt]["raw"] then + ltx.@@.trace.log("RAW CONTENT"..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) + dict= dict .. " " .. ltx.@@.mc[mccnt]["alt"] + end + if ltx.@@.mc[mccnt]["actualtext"] then + ltx.@@.trace.log("RAW CONTENT"..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) + 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 + box.list = @@_insert_bmc_node (box.list,n,"Artifact") + mcopen = mcopen + 1 + end + end + mccntprev = mccnt + end + end -- end if + 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) + else + ltx.@@.trace.log ("ENDHEAD is ".. tostring(head),3) + end + ltx.@@.trace.log ("QUITTING TRAVERSING BOX ".. tostring(name).." TYPE ".. node.type(node.getid(box)),3) + 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) + 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 + emcnode.data = "EMC" + emcnode.mode=1 + 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) + else + ltx.__tag.trace.log ("UPS ",1) + end + if mcopen ~=0 then + ltx.__tag.trace.log ("!WARNING! open mc" .. " MCOPEN = " .. 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 + +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! + local numsentry ="" + 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) + for i=0,mcchunks do + ltx.@@.trace.log("PAGETREE CHUNKS "..ltx.@@.page[page][i],3) + 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" + texio.write_nl("=====>"..tostring(objref)) + numsentry = pdfpage .. " [".. objref .. "]" + ltx.@@.trace.log("PAGETREE 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" + numsentry = numsentry .. " ".. objref + end + numsentry = numsentry .. "] " + ltx.@@.trace.log("PAGETREE PAGE" .. page.. " NUM ENTRY = ".. numsentry,3) + end + else + ltx.@@.trace.log ("PAGETREE: NO DATA FOR PAGE "..page,3) + end + return numsentry +end + +function ltx.@@.func.output_parenttree (abspage) + for i=1,abspage do + line = ltx.@@.func.fill_parent_tree_line (i) .. "^^J" + tex.sprint(catlatex,line) + end +end + + +%</lua> +% \end{macrocode} diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-checks.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-checks.dtx new file mode 100644 index 00000000000..ad3866ed76c --- /dev/null +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-checks.dtx @@ -0,0 +1,232 @@ +% \iffalse meta-comment +% +%% File: tagpdf-checks.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> +%<*checks> +\ProvidesExplPackage {tagpdf-checks-code} {2021/02/23} {0.80} + {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 } + +% 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 +\msg_new:nnn { tag } {attr-unknown} { attribute~#1~is~unknown} + + +%Roles +\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 } +\msg_new:nnn { tag } {role-new-tag} { adding~new~tag~#1~mapped~to~role~#2 } + + +% 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? + { + \prop_if_in:cnF { g_@@_struct_#1_prop } + {S} + { + \msg_error:nn { tag } {struct-missing-tag} + } + } + +\cs_new:Nn \@@_check_structure_tag:N %_protected? + { + \prop_if_in:NoF \g_@@_role_tags_prop {#1} + { + \msg_warning:nnx { tag } {role-unknown-tag} {#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: + { + \msg_error:nn { tag } {struct-faulty-nesting} + } + +\cs_new_protected:Nn \@@_check_struct_used:n %#1 label + { + \prop_get:cnNT + {g_@@_struct_\@@_ref_value:enn{tagpdfstruct-#1}{tagstruct}{unknown}_prop} + {P} + \l_tmpa_tl + { + \msg_warning:nnn { tag } {struct-used-twice} {#1} + } + } + +%roles + +\cs_new_protected:Nn \@@_check_add_tag_role:nn %#1 tag, #2 role + { + \tl_if_empty:nTF {#2} + { + \msg_warning:nnn { tag } {role-missing} {#1} + } + { + \prop_get:NnNTF \g_@@_role_tags_prop {#2} \l_tmpa_tl + { + \msg_info:nnnn { tag } {role-new-tag} {#1} {#2} + } + { + \msg_warning:nnn { tag } {role-unknown} {#2} + } + } + } + +%mc +\cs_new_protected:Nn \@@_check_mc_if_nested: + { + \@@_mc_if_in:T + { + \msg_warning:nnx { tag } {mc-nested} { \@@_get_mc_abs_cnt: } + } + } + +\cs_new_protected:Nn \@@_check_mc_if_open: + { + \@@_mc_if_in:F + { + \msg_warning:nnx { tag } {mc-not-open} { \@@_get_mc_abs_cnt: } + } + } + +\cs_new:Nn \@@_check_mc_tag:N %protected?? + { + \tl_if_empty:NT #1 + { + \msg_error:nnx { tag } {mc-tag-missing} { \@@_get_mc_abs_cnt: } + } + \prop_if_in:NoF \g_@@_role_tags_prop {#1} + { + \msg_warning:nnx { tag } {role-unknown-tag} {#1} + } + } + +\seq_new:N \g_@@_check_mc_used_seq +\cs_new_protected:Nn \@@_check_mc_used:n + { + \seq_if_in:NnTF \g_@@_check_mc_used_seq {#1} + { + \msg_warning:nnn { tag } {mc-used-twice} {#1} + } + { + \seq_gput_right:Nx \g_@@_check_mc_used_seq {#1} + } + } + + + +\cs_new_protected:Nn \@@_check_show_MCID_by_page: + { + \tl_set:Nx \l_@@_tmpa_tl + { + \@@_ref_value_lastpage:nn + {abspage} + {-1} + } + \int_step_inline:nnnn {1}{1} + { + \l_@@_tmpa_tl + } + { + \seq_clear:N \l_tmpa_seq + \int_step_inline:nnnn + {1} + {1} + { + \@@_ref_value_lastpage:nn + {tagmcabs} + {-1} + } + { + \int_compare:nT + { + \@@_ref_value:enn + {mcid-####1} + {tagabspage} + {-1} + = + ##1 + } + { + \seq_gput_right:Nx \l_tmpa_seq + { + Page##1-####1- + \@@_ref_value:enn + {mcid-####1} + {tagmcid} + {-1} + } + } + } + \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} diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc.dtx new file mode 100644 index 00000000000..a2cc7f349dc --- /dev/null +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-mc.dtx @@ -0,0 +1,779 @@ +% \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/02/23} {0.80} + {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 + +% 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 + +%</shared> +% \end{macrocode} +% \begin{macrocode} +%<*generic> +\ProvidesExplPackage {tagpdf-mc-code-generic} {2021/02/23} {0.80} + {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} + +%deprecated +\prg_new_eq_conditional:NNn \uftag_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: + } + +%deprecated +\cs_set_eq:NN \uftag_mc_begin:n \tag_mc_begin:n + +\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_set_eq:NN \uftag_mc_end: \tag_mc_end: + +\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} {} + } + } + } + +%deprecated +\cs_set_eq:NN \uftag_mc_use:n \tag_mc_use:n + +\cs_new:Nn \@@_get_data_mc_tag: { \g_@@_mc_key_tag_tl } + +%</generic> +% \end{macrocode} +% \begin{macrocode} +%<*luamode> +\ProvidesExplPackage {tagpdf-mc-code-lua} {2021/02/23} {0.80} + {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} + +%deprecated +\prg_new_eq_conditional:NNn \uftag_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: + } + +%deprecated +\cs_set_eq:NN \uftag_mc_begin:n \tag_mc_begin:n + +\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_set_eq:NN \uftag_mc_end: \tag_mc_end: + +\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_set_eq:NN \uftag_mc_use:n \tag_mc_use:n + +\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 new file mode 100644 index 00000000000..920c6345ca8 --- /dev/null +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-roles.dtx @@ -0,0 +1,156 @@ +% \iffalse meta-comment +% +%% File: tagpdf-roles.dtx +% +% Copyright (C) 2019 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> +%<*roles> +\ProvidesExplPackage {tagpdf-roles-code} {2021/02/23} {0.80} + {part of tagpdf - code related to roles and structure names} + +\@@_seq_new:N \g_@@_role_tags_seq %to get names from numbers +\@@_prop_new:N \g_@@_role_tags_prop %to get numbers from names + +%The list of standard adobe tags. +\clist_const:Nn \c_@@_role_sttags_clist + {%possible root elements + 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. + Art, %A relatively self-contained body of text constituting a single narrative or exposition + %subelements + Sect, %A container for grouping related content elements. + Div, %A generic block-level element or group of elements + BlockQuote, %A portion of text consisting of one or more paragraphs attributed to someone other + %than the author of the surrounding text. + Caption, %A brief portion of text describing a table or figure. + 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, + NonStruct, %probably not needed + H, + H1, + H2, + H3, + H4, + H5, + H6, + P, + L, %list + LI, %list item (around label and list item body) + Lbl, %list label + LBody, %list item body + Table, + TR, %table row + TH, %table header cell + TD, %table data cell + THead, %table header (n rows) + TBody, %table rows + TFoot, %table footer + Span, %generic inline marker + Quote, %inline quote + Note, % footnote, endnote. Lbl can be child + Reference, % A citation to content elsewhere in the document. + BibEntry, %bibentry + Code, % + Link, % + Annot, + Figure, + Formula, + Form, + Artifact + } + +% get tag name from number: \seq_item:Nn \g_@@_role_tags_seq { n } +%\seq_gset_from_clist:NN \g_@@_role_tags_seq \c_@@_role_tags_clist + +\clist_map_inline:Nn \c_@@_role_sttags_clist + { + \@@_seq_gput_right:Nn \g_@@_role_tags_seq { #1 } + } + + +% get tag number from name: \prop_item:Nn \g_@@_role_tags_prop { name } +\int_step_inline:nnnn { 1 }{ 1 }{ \seq_count:N \g_@@_role_tags_seq } + { + \@@_prop_gput:Nxn \g_@@_role_tags_prop + { + \seq_item:Nn \g_@@_role_tags_seq { #1 } + } + { #1 } + } + +\cs_new:Nn \@@_role_get_tag_from_index:nn + { + \@@_seq_item:cn { #1_seq } { #2 } + } + +\cs_new:Nn \@@_role_get_index_from_tag:nn + { + \@@_prop_item:cn { #1_prop } { #2 } + } + +% new tags and the rolemap + +\@@_prop_new:N \g_@@_role_rolemap_prop + +\cs_new_protected:Nn \@@_role_add_tag:nn %new name, reference to old + { + \@@_seq_gput_right:Nn \g_@@_role_tags_seq { #1 } + \@@_prop_gput:Nnx \g_@@_role_tags_prop { #1 } + { + \seq_count:N \g_@@_role_tags_seq + } + \@@_check_add_tag_role:nn {#1}{#2} + \tl_if_empty:nF { #2 } + { + \@@_prop_gput:Nnn \g_@@_role_rolemap_prop + { #1 } { #2 } + } + } + +\cs_generate_variant:Nn \@@_role_add_tag:nn {xx} + +\keys_define:nn { @@ / setup } + { + add-new-tag .code:n = + { + \seq_set_split:Nnn \l_tmpa_seq { / } {#1/} + \@@_role_add_tag:xx + { + \seq_item:Nn \l_tmpa_seq {1} + } + { + \seq_item:Nn \l_tmpa_seq {2} + } + } + } + + +%</roles> +% \end{macrocode} diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-space.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-space.dtx new file mode 100644 index 00000000000..edc5e445c70 --- /dev/null +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-space.dtx @@ -0,0 +1,89 @@ +% \iffalse meta-comment +% +%% File: tagpdf-space.dtx +% +% Copyright (C) 2019 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> +%<*space> +\ProvidesExplPackage {tagpdf-space-code} {2021/02/23} {0.80} + {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 + +\sys_if_engine_pdftex:T + { + \pdfglyphtounicode{space}{0020} + \keys_define:nn { @@ / setup } + { + interwordspace .choices:nn = { true, on } { \pdfinterwordspaceon }, + interwordspace .choices:nn = { false, off }{ \pdfinterwordspaceon }, + show-spaces .bool_set:N = \l_@@_showspaces_bool + } + } + + +\sys_if_engine_luatex:T + { + \keys_define:nn { @@ / setup } + { + interwordspace .choices:nn = + { true, on } + { \directlua{ltx.@@.func.markspaceon()} }, + interwordspace .choices:nn = + { false, off } + {\directlua{ltx.@@.func.markspaceoff()} }, + show-spaces .choice:, + show-spaces / true .code:n = + {\directlua{ltx.@@.trace.showspaces=true}}, + show-spaces / false .code:n = + {\directlua{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} }, + show-spaces .bool_set:N = \l_@@_showspaces_bool + } + } + +%</space> +% \end{macrocode} diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx new file mode 100644 index 00000000000..09ea3511882 --- /dev/null +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx @@ -0,0 +1,789 @@ +% \iffalse meta-comment +% +%% File: tagpdf-struct.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> +%<*struct> +\ProvidesExplPackage {tagpdf-struct-code} {2021/02/23} {0.80} + {part of tagpdf - code related to storing structure} + +% I will use a latex counter for the structure count +% to have a chance to avoid double structures in align etc + +\newcounter { g_@@_struct_abs_int } +\int_gzero:N \c@g_@@_struct_abs_int + +% ref code is in tagpdf.dtx + +% a sequence stores structnum -> the obj numbers +% to allow easy mapping over the structures + +\@@_seq_new:N \g_@@_struct_objR_seq + +% a sequence for the structure stack. When a sequence is opened it's number is put on the stack. +\seq_new:N \g_@@_struct_stack_seq +\seq_gpush:Nn \g_@@_struct_stack_seq {0} + +%this variables will hold the top entry and the parent on the stack +\tl_new:N \l_@@_struct_stack_parent_tmp_tl +\tl_new:N \g_@@_struct_stack_current_tl + +% I 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) +% 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) + + +\seq_const_from_clist:Nn \c_@@_struct_StructTreeRoot_entries_seq + {%p. 857/858 + Type, % always /StructTreeRoot + K, % kid, dictionary or array of dictionaries + IDTree, % currently unused + ParentTree, % required,obj ref to the parent tree + ParentTreeNextKey, %optional + RoleMap, + ClassMap + } + +\seq_const_from_clist:Nn \c_@@_struct_StructElem_entries_seq + {%p 858 f + Type, %always /StructElem + S, %tag/type + P, %parent + ID, %optional + Ref, %optional, pdf 2.0 Use? + Pg, %obj num of starting page, optional + K, %kids + A, %attributes, probably unused + C, %class "" + %R, + T, %title, value in () or <> + Lang, %language + Alt, % value in () or <> + 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 + { + \prop_if_in:cnT + { g_@@_struct_#1_prop } + { #2 } + { + \c_space_tl/#2~ \prop_item:cn{ g_@@_struct_#1_prop } { #2 } + } + } + +\cs_new:Nn \@@_new_output_prop_handler:n + { + \cs_new:cn { @@_struct_output_prop_#1:n } + { + \@@_struct_output_prop_aux:nn {#1}{##1} + } + } + + +% the first one, the StructTreeRoot is special, so +% created manually: +\@@_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 } + +% deprecated +%\@@_prop_gput:cno +% { g_@@_struct_0_prop } +% { objnum} +% { \c_@@_tree_obj_structtreeroot_tl } + +% new +\@@_prop_gput:cno + { g_@@_struct_0_prop } + { objref} + { \pdf_object_ref:n { c_@@_struct_0_obj } } + +\@@_prop_gput:cno + { 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 { c_@@_tree_parenttree_obj } } + +\@@_prop_gput:cnx + { g_@@_struct_0_prop } + { RoleMap } + { \pdf_object_ref:n { c_@@_tree_rolemap_obj } } + +\@@_prop_gput:cno + { g_@@_struct_0_prop } + { entries } + { StructTreeRoot } + +\@@_prop_gput:cno + { g_@@_struct_0_prop } + { num } + { 0 } + +% 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% + { + %\@@_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 + \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 + { + \@@_seq_gput_right:cx + { g_@@_struct_kids_#1_seq } + { + \prop_item:cn + { g_@@_struct_#2_prop } + { objref } + } + } + +\cs_new:Nn\@@_struct_kid_link_gput_right:nn %#1 num of parent struct, #2 obj reference + { + \pdf_object_unnamed_write:nx + { dict } + { + /Type \c_space_tl /OBJR \c_space_tl + /Obj~#2 + } + \@@_seq_gput_right:cx + { g_@@_struct_kids_#1_seq } + { + \pdf_object_ref_last: + } + } + +\cs_generate_variant:Nn\@@_struct_kid_link_gput_right:nn { nx } + +\cs_new:Nn\@@_struct_exchange_kid_command:N %N= seq + { + \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 + } + +\cs_generate_variant:Nn\@@_struct_exchange_kid_command:N { c } + +\cs_new:Nn \@@_struct_fill_kid_key:n %#1 is the struct num + { + \int_case:nnF + { + \seq_count:c + { + g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_seq + } + } + { + { 0 } + { } %no kids, do nothing + { 1 } % 1 kid, insert + { + % in this case we need a special command in + %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} + } + \@@_prop_gput:cnx { g_@@_struct_#1_prop } {K} + { + \seq_item:cn + { + g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_seq + } + {1} + } + } % + } + { %many kids, use an array + \@@_prop_gput:cnx { g_@@_struct_#1_prop } {K} + { + [ + \seq_use:cn + { + g_@@_struct_kids_\prop_item:cn{ g_@@_struct_#1_prop }{num}_seq + } + { + \c_space_tl + } + ] + } + } + } + +% 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 + { + %\tl_set:Nn \l_@@_struct_dict_content_tl {<<} + \tl_clear:N \l_@@_struct_dict_content_tl + \seq_map_inline:cn + { + c_@@_struct_\prop_item:cn{ g_@@_struct_#1_prop }{entries}_entries_seq + } + { + \tl_put_right:Nx + \l_@@_struct_dict_content_tl + { + \prop_if_in:cnT + { g_@@_struct_#1_prop } + { ##1 } + { + \c_space_tl/##1~\prop_item:cn{ g_@@_struct_#1_prop } { ##1 } + } + } + } + %\tl_put_right:Nn \l_@@_struct_dict_content_tl { >> } + } + + +% #1 is the struct num +\cs_new:Nn \@@_struct_write_obj:n + { + \prop_if_in:cnTF + { g_@@_struct_#1_prop } + { objref } + { + \@@_struct_fill_kid_key:n { #1 } + %\prop_show:c { g_@@_struct_#1_prop } + \@@_struct_get_dict_content:n { #1 } + \exp_args:Nx + \pdf_object_write:nx + { c_@@_struct_#1_obj } + { + \l_@@_struct_dict_content_tl + } + } + { + \msg_error:nnn { tag } { struct-no-objnum } { #1} + } + } + +% keys for the user commands +% should I pass the values (e.g. the tag) through an escape command? +\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?? + \tl_set:Nx \l_@@_tmpa_tl { #1 } + \bool_if:NT \g_@@_check_tags_bool + { + \@@_check_structure_tag:N \l_@@_tmpa_tl + } + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { S } + { /\exp_not:V\l_@@_tmpa_tl } + }, + title .code:n = % T property + { + \str_set_convert:Nnon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { T } + { <\l_@@_tmpa_str> } + }, + title-o .code:n = % T property + { + \str_set_convert:Nnon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { T } + { <\l_@@_tmpa_str> } + }, + alttext .code:n = % Alt property + { + \str_set_convert:Nnon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { Alt } + { <\l_@@_tmpa_str> } + }, + alttext-o .code:n = % Alt property + { + \str_set_convert:Noon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { Alt } + { <\l_@@_tmpa_str> } + }, + actualtext .code:n = % ActualText property + { + \str_set_convert:Nnon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { ActualText } + { <\l_@@_tmpa_str>} + }, + actualtext-o .code:n = % ActualText property + { + \str_set_convert:Noon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { ActualText } + { <\l_@@_tmpa_str>} + }, + lang .code:n = % Lang property + { + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { Lang } + { (#1) } + }, + ref .code:n = % Lang property + { + \tl_clear:N\l_@@_tmpa_tl + \clist_map_inline:nn {#1} + { + \tl_put_right:Nx \l_@@_tmpa_tl + {~\ref_value:nn{tagpdfstruct-##1}{tagstructobj} } + } + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { Ref } + { [\l_@@_tmpa_tl] } + }, + E .code:n = % E property + { + \str_set_convert:Nnon + \l_@@_tmpa_str + { #1 } + { default } + { utf16/hex } + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { E } + { <\l_@@_tmpa_str> } + }, + } + +% keys for AF keys, they rely on l3pdffile! +% The stream variants use txt as extension to get the mimetype. +\keys_define:nn { @@ / struct } + { + AF .code:n = % T property + { + \pdf_object_if_exist:nTF {#1} + { + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { AF } + { \pdf_object_ref:n {#1} } + } + { + + } + }, + ,AFinline .code:n = + { + \group_begin: + \exp_args:Ne + \pdf_object_if_exist:nF {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} + { + \pdffile_embed_stream:nxx + {#1} + {tag-AFfile\int_use:N\c@g_@@_struct_abs_int.txt} + {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} + } + \@@_prop_gput:cnx + { g_@@_struct_\int_use:N\c@g_@@_struct_abs_int _prop } + { AF } + { \pdf_object_ref:e {@@/fileobj\int_use:N\c@g_@@_struct_abs_int } } + \group_end: + } + ,AFinline-o .code:n = + { + \group_begin: + \exp_args:Ne + \pdf_object_if_exist:nF {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} + { + \pdffile_embed_stream:oxx + {#1} + {tag-AFfile\int_use:N\c@g_@@_struct_abs_int.txt} + {@@/fileobj\int_use:N\c@g_@@_struct_abs_int} + } + \@@_prop_gput:cnx + { g_@@_struct_\int_use:N\c@g_@@_struct_abs_int _prop } + { AF } + { \pdf_object_ref:e {@@/fileobj\int_use:N\c@g_@@_struct_abs_int } } + \group_end: + } + } + +\cs_new_protected:Nn \tag_struct_begin:n + { + \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 + { c_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_obj } + { dict } + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_prop } + { objref} + { + \exp_args:Ne + \pdf_object_ref:n + {c_@@_struct_\int_eval:n { \c@g_@@_struct_abs_int }_obj} + } +% \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 + \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 } + { + \prop_item:cn + { g_@@_struct_\l_@@_struct_stack_parent_tmp_tl _prop} + { objref } + } + %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: + } + +%deprecated +\cs_set_eq:NN \uftag_struct_begin:n \tag_struct_begin:n + +\cs_new_protected:Nn \tag_struct_end: + { %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:NNTF \g_@@_struct_stack_seq \l_tmpa_tl + { + \int_compare:nNnT {\l_@@_loglevel_int} > { 0 } + { + \@@_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: + } + } + +%deprecated +\cs_set_eq:NN \uftag_struct_end: \tag_struct_end: + +\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 } + { + \prop_item:cn + { g_@@_struct_\g_@@_struct_stack_current_tl _prop} + { objref } + } + } + { + \msg_warning:nnn{ tag }{struct-label-unknown}{#1} + } + } + +\cs_set_eq:NN \uftag_struct_use:n \tag_struct_use:n + + +%%%% Code to tag links +%%%% this works for url, see exp-link.pdf. +%%%% it must be checked for other links + +\cs_new_protected:Nn \@@_struct_finish_link: + { + \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 } + } + %put the obj number of link annot in the kid entry: + \@@_struct_kid_link_gput_right:nx + { + \l_@@_struct_stack_parent_tmp_tl + } + { + \pdfannot_link_ref_last: + } + % add the parent obj number to the parent tree: + \@@_parenttree_add_objr:nn + { + \int_use:N\c@g_@@_parenttree_obj_int + } + { + \prop_item:cn + { g_@@_struct_\l_@@_struct_stack_parent_tmp_tl _prop } + { objref } + } + % increase the int: + \stepcounter{ g_@@_parenttree_obj_int } + } + } + +%</struct> + +% \end{macrocode} +% \begin{macrocode} +%<*attr> +\ProvidesExplPackage {tagpdf-attr-code} {2021/02/23} {0.80} + {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 + +\prop_new:N \g_@@_attr_entries_prop +\tl_new:N \g_@@_attr_class_content_tl +\tl_new:N \l_@@_attr_objtmp_tl +\tl_new:N \l_@@_attr_value_tl + + +\cs_new_protected:Nn \@@_attr_new_entry:nn %#1:name, #2: content + { + \prop_gput:Nnn \g_@@_attr_entries_prop + {#1}{#2} + } + +\keys_define:nn { @@ / setup } + { + newattribute .code:n = + { + \@@_attr_new_entry:nn #1 + } + } + + +% the key for the structure: +\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 + { + \prop_if_in:NnF \g_@@_attr_entries_prop {##1} + { + \msg_error:nnn { tag } { attr-unknown } { ##1 } + } + \seq_gput_left:Nn\g_@@_attr_class_used_seq { ##1} + } + \seq_set_map:NNn \l_tmpb_seq \l_tmpa_seq + { + /##1 + } + \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 > 0 } + { + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { C } + { \l_tmpa_tl } + %\prop_show:c { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + } + } + } + +\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 + \tl_set:Nx \l_@@_attr_value_tl + { + \int_compare:nT { \seq_count:N \l_tmpa_seq > 1 }{[}%] + } + \seq_map_inline:Nn \l_tmpa_seq + { + \prop_if_in:NnF \g_@@_attr_entries_prop {##1} + { + \msg_error:nnn { tag } { attr-unknown } { ##1 } + } + \prop_if_in:NnF \g_@@_attr_objref_prop {##1} + {%\prop_show:N \g_@@_attr_entries_prop + \pdf_object_unnamed_write:nx + { dict } + { + \prop_item:Nn\g_@@_attr_entries_prop {##1} + } + \prop_gput:Nnx \g_@@_attr_objref_prop {##1} {\pdf_object_ref_last:} + } + \tl_put_right:Nx \l_@@_attr_value_tl + { + \c_space_tl + \prop_item:Nn \g_@@_attr_objref_prop {##1} + } + % \tl_show:N \l_@@_attr_value_tl + } + \tl_put_right:Nx \l_@@_attr_value_tl + { %[ + \int_compare:nT { \seq_count:N \l_tmpa_seq > 1 }{]}% + } + % \tl_show:N \l_@@_attr_value_tl + \@@_prop_gput:cnx + { g_@@_struct_\int_eval:n {\c@g_@@_struct_abs_int}_prop } + { A } + { \l_@@_attr_value_tl } + }, + } +%</attr> +% \end{macrocode} diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf-tree.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf-tree.dtx new file mode 100644 index 00000000000..35f622ca1c9 --- /dev/null +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf-tree.dtx @@ -0,0 +1,294 @@ +% \iffalse meta-comment +% +%% File: tagpdf-tree.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> +%<*tree> +\ProvidesExplPackage {tagpdf-tree-code} {2021/02/23} {0.80} + {part of tagpdf - code related to writing trees and dictionaries to the pdf} + +%this does the actual finishing: +%Is \AfterEndDocument the best places?? +\hook_gput_code:nnn{begindocument}{tagpdf} + { + \bool_if:NT \g_@@_active_tree_bool + { + \AfterEndDocument { \tag_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 +\pdf_object_new:nn { c_@@_struct_0_obj }{ dict } + + %need to think about the best place ... +\hook_gput_code:nnn{begindocument}{tagpdf} + { + \bool_if:NT \g_@@_active_struct_bool + { + \pdfmanagement_add:nnx + { Catalog } + { StructTreeRoot } + { \pdf_object_ref:n { c_@@_struct_0_obj } } + } + } +\cs_new_protected:Nn \@@_tree_write_structtreeroot: + { + \@@_struct_write_obj:n { 0 } + } + +\cs_new_protected:Nn \@@_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 + } + } + + +%the ParentTree +% deprecated +%\@@_pdfreserveobjnum:N \l_tmpa_tl +%\tl_const:Nx \c_@@_tree_obj_parenttree_tl { \l_tmpa_tl } + +%new +\pdf_object_new:nn { c_@@_tree_parenttree_obj }{ 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. +\newcounter { g_@@_parenttree_obj_int } +\hook_gput_code:nnn{begindocument}{tagpdf} + { + \int_gset:Nn + \c@g_@@_parenttree_obj_int + { \@@_ref_value_lastpage:nn{abspage}{100} } + } + +% we need to record the obj references: + +\tl_new:N \g_@@_parenttree_objr_tl + +\cs_new_protected:Nn \@@_parenttree_add_objr:nn %#1 Structparent number, #2 objref + { + \tl_gput_right:Nx \g_@@_parenttree_objr_tl + { + #1 \c_space_tl #2 ^^J + } + } + +\tl_new:N \l_@@_parenttree_content_tl + +\cs_new_protected:Nn \@@_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 + \int_step_inline:nnnn{1}{1}{\@@_ref_value_lastpage:nn{tagmcabs}{-1}} + { + %mcid####1 + \int_compare:nT%F + {\@@_ref_value:enn{mcid-####1}{tagabspage}{-1}=##1} %mcid is on current page + {% yes\par + \prop_put:Nxx + \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 + [\c_space_tl %] + } + \int_step_inline:nnnn + {0} + {1} + { \prop_map_function:NN \l_tmpa_prop\@@_prop_count:nn -1 } + { + \prop_get:NnNTF \l_tmpa_prop {####1} \l_tmpb_tl + {% page#1:mcid##1:\l_tmpb_tl :content + \tl_put_right:Nx \l_@@_parenttree_content_tl + { + \prop_item:cn { g_@@_struct_\l_tmpb_tl _prop } {objref} + \c_space_tl + } + %\tl_show:N \l_@@_parenttree_content_tl + } + { + \msg_warning:nn { tag } {tree-mcid-index-wrong} + } + } + \tl_put_right:Nn + \l_@@_parenttree_content_tl + {%[ + ]^^J + } + } + } + +%lua mode must/can do it differently +\cs_new_protected:Nn \@@_tree_lua_fill_parenttree: + { + \tl_set:Nn \l_@@_parenttree_content_tl + { + \directlua + { + ltx.@@.func.output_parenttree + ( + \int_use:N\g_shipout_readonly_int + ) + } + } + } + + + +\cs_new_protected:Nn \@@_tree_write_parenttree: + { + \bool_if:NTF \g_@@_mode_lua_bool + { + \@@_tree_lua_fill_parenttree: + } + { + \@@_tree_fill_parenttree: + } + \tl_put_right:NV \l_@@_parenttree_content_tl\g_@@_parenttree_objr_tl + \pdf_object_write:nx { c_@@_tree_parenttree_obj } + { + /Nums\c_space_tl [\l_@@_parenttree_content_tl] + } + } + +%the Rolemap tree +%deprecated +%\@@_pdfreserveobjnum:N \l_tmpa_tl +%\tl_const:Nx \c_@@_tree_obj_rolemap_tl { \l_tmpa_tl } + +%new +\pdf_object_new:nn { c_@@_tree_rolemap_obj }{ dict } + +\tl_new:N \l_@@_rolemap_content_tl + +\cs_new_protected:Nn \@@_tree_fill_rolemap: + { + \prop_map_inline:Nn \g_@@_role_rolemap_prop + { + \tl_put_right:Nx \l_@@_rolemap_content_tl + { + /##1\c_space_tl/##2^^J + } + } + } + +\cs_new_protected:Nn \@@_tree_write_rolemap: + { + \@@_tree_fill_rolemap: + \pdf_object_write:nx { c_@@_tree_rolemap_obj } + { + \l_@@_rolemap_content_tl + } + } + +%classmap, should only be written, if values has been used + +\cs_new_protected:Nn \@@_tree_write_classmap: + { + \tl_gclear:N \g_@@_attr_class_content_tl + \seq_gremove_duplicates:N \g_@@_attr_class_used_seq + \seq_set_map:NNn \l_tmpa_seq \g_@@_attr_class_used_seq + { + /##1\c_space_tl + << + \prop_item:Nn + \g_@@_attr_entries_prop + {##1} + >> + } + \tl_gset:Nx \g_@@_attr_class_content_tl + { + \seq_use:Nn + \l_tmpa_seq + { \iow_newline: } + } + \tl_if_empty:NF + \g_@@_attr_class_content_tl + { + \pdf_object_new:nn { c_@@_tree_classmap_obj }{ dict } + % \@@_pdfreserveobjnum:N \l_tmpa_tl + % \tl_const:Nx \c_@@_tree_obj_classmap_tl { \l_tmpa_tl } + \pdf_object_write:nx + { c_@@_tree_classmap_obj } + { \g_@@_attr_class_content_tl } + \@@_prop_gput:cnx + { g_@@_struct_0_prop } + { ClassMap } + { \pdf_object_ref:n { c_@@_tree_classmap_obj } } + } + } + + +\cs_new_protected:Nn \tag_finish_structure: + { + \@@_tree_write_parenttree: + \@@_tree_write_rolemap: + \@@_tree_write_classmap: + \@@_tree_write_structelements: %this is rather slow!! + \@@_tree_write_structtreeroot: + } + +%deprecated +\cs_set_eq:NN \uftag_finish_structure: \tag_finish_structure: + +%StructParents !!! we need a better hook for \g__pdf_BACKEND_thispage_shipout_tl!! +% No actually +\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}} + } + } + } + +%</tree> +% \end{macrocode} diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf.dtx b/Master/texmf-dist/source/latex/tagpdf/tagpdf.dtx new file mode 100644 index 00000000000..145b1509712 --- /dev/null +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf.dtx @@ -0,0 +1,498 @@ +% \iffalse meta-comment +% +%% File: tagpdf.dtx +% +% Copyright (C) 2019-2020 Ulrike Fischer +% +% It may be distributed and/or modified under the conditions of the +% LaTeX Project Public License (LPPL), either version 1.3c of this +% license or (at your option) any later version. The latest version +% of this license is in the file +% +% https://www.latex-project.org/lppl.txt +% +% This file is part of the "tagpdf bundle" (The Work in LPPL) +% and all files in that bundle must be distributed together. +% +% ----------------------------------------------------------------------- +% +% The development version of the bundle can be found at +% +% https://github.com/u-fischer/tagpdf +% +% for those people who are interested. +% +% \fi +% +% \begin{macrocode} +%<@@=tag> +%<*package> +\ProvidesExplPackage {tagpdf} {2021/02/23} {0.80} + { A package to experiment with pdf tagging } + +\bool_if:nF + { + \bool_lazy_and_p:nn + {\cs_if_exist_p:N \pdfmanagement_if_active_p:} + { \pdfmanagement_if_active_p: } + } + { %error for now, perhaps warning later. + \PackageError{tagpdf} + { + PDF~resource~management~is~no~active!\MessageBreak + tagpdf~will~no~work. + } + { + Activate~it~with \MessageBreak + \string\RequirePackage{pdfmanagement-testphase}\MessageBreak + \string\DeclareDocumentMetadata{<options>}\MessageBreak + before~\string\documentclass + } + } + +%\debug_on:n{check-declarations,check-expressions,deprecation} + +%map internal tag to package name +\prop_if_exist:NT \g_msg_module_name_prop + { + \prop_gput:Nnn \g_msg_module_name_prop { tag }{ tagpdf } + } + +% storing internal names to my name space: +\cs_set_eq:NN \@@_tex_global:D \tex_global:D + +%%% package options +\bool_new:N\g_@@_mode_lua_bool + +\DeclareOption {luamode} { \sys_if_engine_luatex:T { \bool_gset_true:N \g_@@_mode_lua_bool } } +\DeclareOption {genericmode}{ \bool_gset_false:N\g_@@_mode_lua_bool } +\ExecuteOptions{luamode} +\ProcessOptions + +%%% some packages +%references use l3ref-tmp +\RequirePackage{l3ref-tmp} + %================ + % temporary code + % =============== + % faking the LastPage label: + \cs_new_protected:Npn \@@_lastpagelabel: + { + \legacy_if:nT { @filesw } + { + \exp_args:NNnx \exp_args:NNx\iow_now:Nn \@auxout + { + \token_to_str:N \newlabeldata + {@@_LastPage} + { + {abspage} { \int_use:N \g_shipout_readonly_int} + {tagmcabs}{ \int_use:N \c@g_@@_MCID_abs_int } + } + } + } + } + + \AddToHook{enddocument/afterlastpage} + {\@@_lastpagelabel:} + + % a command to override the general default. See issue in ref + \cs_if_exist:NF \ref_value:nnn + { + \cs_new:Npn \ref_value:nnn #1#2#3 + { + \exp_args:Nee + \__ref_value:nnn + { \tl_to_str:n {#1} } { \tl_to_str:n {#2} } {#3} + } + \cs_new:Npn \__ref_value:nnn #1#2#3 + { + \tl_if_exist:cTF { g__ref_label_ #1 _ #2 _tl } + { \tl_use:c { g__ref_label_ #1 _ #2 _tl } } + { + % test if attribute exist at all? + #3 + } + } + } + + %============= + % attributes + %============= +\ref_attribute_gset:nnnn {tagstruct} {0}{now} { \int_use:N \c@g_@@_struct_abs_int } +\ref_attribute_gset:nnnn {tagstructobj} {}{now} + { + \exp_args:Ne + \pdf_object_if_exist:nT {c__tag_struct_ \int_use:N \c@g__tag_struct_abs_int _obj} + { + \pdf_object_ref:e{c__tag_struct_ \int_use:N \c@g_@@_struct_abs_int _obj} + } + } + % replace by abspage?? +\ref_attribute_gset:nnnn {tagabspage}{0}{shipout}{ \int_use:N \g_shipout_readonly_int } +\ref_attribute_gset:nnnn {tagmcabs } {0}{now} { \int_use:N \c@g_@@_MCID_abs_int } +\ref_attribute_gset:nnnn {tagmcid } {0}{now} { \int_use:N \g_@@_MCID_tmp_bypage_int } +%============ +% lists +%============ +\clist_const:Nn \c_@@_refmc_clist {tagabspage,tagmcabs,tagmcid} %mc is new!! +\clist_const:Nn \c_@@_refstruct_clist {tagstruct,tagstructobj} +%================= +% command to set a label +%================ +\cs_generate_variant:Nn \ref_label:nn { nv } +\cs_new_protected:Npn \@@_ref_label:nn #1 #2 %#1 label, #2 name of list mc or struct + { + \@bsphack + \ref_label:nv {#1}{c_@@_ref#2_clist} + \@esphack + } +%============== +% command to retrieve the value +%=============== +\cs_new:Npn \@@_ref_value:nnn #1 #2 #3 %#1 label, #2 attribute, #3 default + { + \ref_value:nnn {#1}{#2}{#3} + } +%=============== +% the LastPage label has a different name, so we need a special command for now +% ============= +\cs_new:Npn \@@_ref_value_lastpage:nn #1 #2 + { + \ref_value:nnn {@@_LastPage}{#1}{#2} + } + +% as ref doesn't expand label names we need variants: +\cs_generate_variant:Nn \@@_ref_label:nn {en} +\cs_generate_variant:Nn \@@_ref_value:nnn {enn} + +\RequirePackage{etoolbox} +%\RequirePackage{l3pdf} +%\RequirePackage{pdfescape} %check if needed +%\RequirePackage{pdftexcmds}%check if needed (expandable commands!) + +%\RequirePackage{l3str-convert} +\cs_generate_variant:Nn \str_set_convert:Nnnn {Nonn, Noon, Nnon } + + + + +%%% tagpdfsetup, +%%% TODO: checks need to be improved +\int_new:N \l_@@_loglevel_int +\tl_new:N \l_@@_tree_tabs_order_tl + +\keys_define:nn { @@ / setup } + { + activate-mc .bool_gset:N = \g_@@_active_mc_bool, + activate-tree .bool_gset:N = \g_@@_active_tree_bool, + activate-struct .bool_gset:N = \g_@@_active_struct_bool, + activate-all .meta:n ={activate-mc,activate-tree,activate-struct}, + check-tags .bool_gset:N = \g_@@_check_tags_bool, + check-tags .initial:n = true, + log .choice:, + log / none .code:n = {\int_set:Nn \l_@@_loglevel_int { 0 }}, + log / v .code:n = {\int_set:Nn \l_@@_loglevel_int { 1 }}, + log / vv .code:n = {\int_set:Nn \l_@@_loglevel_int { 2 }}, + log / vvv .code:n = {\int_set:Nn \l_@@_loglevel_int { 3 }}, + log / all .code:n = {\int_set:Nn \l_@@_loglevel_int { 10 }}, + tagunmarked .bool_gset:N = \g_@@_tagunmarked_bool, + tagunmarked .initial:n = true, + tabsorder .choice:, + tabsorder / row .code:n = %{\tl_set:Nn \l_@@_tree_tabs_order_tl {/Tabs/R}}, + \pdfmanagement_add:nnn { Page } {Tabs}{/R}, + tabsorder / column .code:n = %{\tl_set:Nn \l_@@_tree_tabs_order_tl {/Tabs/C}}, + \pdfmanagement_add:nnn { Page } {Tabs}{/C}, + tabsorder / structure .code:n = %{\tl_set:Nn \l_@@_tree_tabs_order_tl {/Tabs/S}}, + \pdfmanagement_add:nnn { Page } {Tabs}{/S}, + tabsorder / none .code:n = %{\tl_set:Nn \l_@@_tree_tabs_order_tl {}}, + \pdfmanagement_remove:nn {Page} {Tabs}, + tabsorder .initial:n = structure, + uncompress .code:n = {\pdf_uncompress: }, + } +%need to think about the right place ... +%\AtBeginDocument +\hook_gput_code:nnn{begindocument}{tagpdf} + { + \bool_if:NT \g_@@_active_struct_bool + { + \pdfmanagement_add:nnn { Catalog / MarkInfo } { Marked } { true } + } + } +% escape commands are a bit engine specific so have been moved to the backend files. +% commands to escape strings so that they can be safely used in pdf +% currently not much used. But will be needed later, when alt and actualtext are added. +% we probably need the equivalent \pdfescapestring, \pdfescapename \pdfescapehex +% the commands of pdfescape adds an additional layout to allow for babel shorthands +%\cs_set_eq:NN \@@_pdf_escape_string:Nn \EdefEscapeString +%\cs_set_eq:NN \@@_pdf_escape_name:Nn \EdefEscapeName +%\cs_set_eq:NN \@@_pdf_escape_hex:Nn \EdefEscapeHex + +%\cs_set_eq:NN \@@_pdf_escape_string:n \EdefEscapeString +%\cs_set_eq:NN \@@_pdf_escape_name:Nn \EdefEscapeName + +%escape command. They are backend specific as \pdf@escapehex would ignore most chars +%outside the "pdflatex" range + + + + + +% a hook for later code and an absolute page counter +% should be executed before counters are resetted. +% is it used? +\cs_new:Nn \@@_finish_page_hook: { } + +%\AtBeginShipout + +\hook_gput_code:nnn + { shipout/before } + { tagpdf/cnt } + { + \@@_finish_page_hook: + %\int_gincr:N \g_@@_abspage_int + } + + +%testing the engines and loading the backend files +\sys_if_engine_xetex:T + { %lets try with xelatex ... + %\PackageError { tagpdf } { xelatex~is~not~supported~-~aborting } {} + %\tex_endinput:D + \file_input:n {tagpdf-pdftex.def} + } + +\sys_if_engine_luatex:T + { + \file_input:n {tagpdf-luatex.def} + } + +\sys_if_engine_pdftex:T + { + \file_input:n {tagpdf-pdftex.def} + } + +\sys_if_output_dvi:T + { + %\PackageError { tagpdf } { dvi~output~is~not~supported~-~aborting }{} + %\tex_endinput:D + } + +\cs_generate_variant:Nn \@@_prop_gput:Nnn { Nxn , Nxx, Nnx , cnn, cxn, cnx, cno} +\cs_generate_variant:Nn \@@_seq_gput_right:Nn { Nx , No, cn, cx } +\cs_generate_variant:Nn \@@_prop_new:N { c } +\cs_generate_variant:Nn \@@_seq_new:N { c } +\cs_generate_variant:Nn \@@_seq_show:N { c } +\cs_generate_variant:Nn \@@_prop_show:N { c } +\cs_generate_variant:Nn \prop_gput:Nnn {Nxx} +\cs_generate_variant:Nn \prop_put:Nnn {Nxx} + +\cs_generate_variant:Nn \pdffile_embed_stream:nnn {nxx,oxx} +\cs_generate_variant:Nn \pdf_object_ref:n {e} + + +% few temp tl +\tl_new:N \g_@@_tmpa_tl +\tl_new:N \l_@@_tmpa_tl +\tl_new:N \l_@@_tmpb_tl +\tl_new:N \l_@@_tmpc_tl +\tl_new:N \l_@@_tmpd_tl +\tl_new:N \l_@@_tmpe_tl +\str_new:N \l_@@_tmpa_str + +% helper function to get the propcount. +% use as \prop_map_function:NN PROP { \@@_prop_count:nn -1 } +\cs_new:Nn\@@_prop_count:nn { + 1 } + +%% Loading the tagpdf sub packages +\RequirePackage { tagpdf-checks-code } +\RequirePackage { tagpdf-user } +\RequirePackage { tagpdf-tree-code } +\RequirePackage { tagpdf-roles-code } +\RequirePackage { tagpdf-attr-code } +% mc-code is split: +\RequirePackage { tagpdf-mc-code-shared } +\bool_if:NTF \g_@@_mode_lua_bool + { + \RequirePackage {tagpdf-mc-code-lua} + } + { + \RequirePackage { tagpdf-mc-code-generic } % + } + +\RequirePackage { tagpdf-struct-code } +\RequirePackage { tagpdf-space-code } +%</package> +% \end{macrocode} +% +% \begin{macrocode} +%<*user> +\ProvidesExplPackage {tagpdf-user} {2021/02/23} {0.80} + {tagpdf - user commands} + +\NewDocumentCommand \tagpdfsetup { m } + { + \keys_set:nn { @@ / setup } { #1 } + } + +\cs_set_eq:NN\tagpdfifluatexTF \sys_if_engine_luatex:TF +\cs_set_eq:NN\tagpdfifluatexT \sys_if_engine_luatex:T +\cs_set_eq:NN\tagpdfifpdftexT \sys_if_engine_pdftex:T + +%%% stop tagging +\bool_new:N \l_@@_stop_bool + +\prg_new_conditional:Nnn \@@_if_stop: {p,T,F,TF} + { + \bool_if:NTF \l_@@_stop_bool + { \prg_return_true: } + { \prg_return_false: } + } + +\prg_new_eq_conditional:NNn \tag_if_stop: \@@_if_stop: {p,T,F,TF} + +\cs_new_protected:Npn \@@_stop: + { + \bool_set_true:N \l_@@_stop_bool + \cs_set_eq:NN \tag_mc_begin:n \use_none:n + \cs_set_eq:NN \tag_mc_end: \prg_do_nothing: + \cs_set_eq:NN \tag_mc_use:n \use_none:n + \cs_set_eq:NN \tag_struct_begin:n \use_none:n + \cs_set_eq:NN \tag_struct_end: \prg_do_nothing: + \cs_set_eq:NN \tag_struct_use:n \use_none:n + %deprecated + \cs_set_eq:NN \uftag_mc_begin:n \tag_mc_begin:n + \cs_set_eq:NN \uftag_mc_end: \tag_mc_end: + \cs_set_eq:NN \uftag_mc_use:n \tag_mc_use:n + \cs_set_eq:NN \uftag_struct_begin:n \tag_struct_begin:n + \cs_set_eq:NN \uftag_struct_end: \tag_struct_end: + \cs_set_eq:NN \uftag_struct_use:n \tag_struct_use:n + } + +\cs_new_protected:Npn \tag_stop_group_begin: + { + \group_begin: + \@@_stop: + } + +\cs_set_eq:NN \tag_stop_group_end: \group_end: + + +%%% a generic command to retrieve data + +\cs_new:Npn \tagpdfget #1 { \use:c {@@_get_data_#1: } } +\cs_new:Npn \tag_get:n #1 { \use:c {@@_get_data_#1: } } +%deprecated: +\cs_new:Npn \uftag_get:n #1 { \use:c {@@_get_data_#1: } } + + +%%%% mc related user commands +\NewDocumentCommand \tagmcifinTF { m m } + { + \@@_mc_if_in:TF { #1 } { #2 } + } + + +\NewDocumentEnvironment{tagmcartifact}{m} + { + \tag_mc_artifact_group_begin:n { #1}\ignorespaces + } + { + \tag_mc_artifact_group_end: + } + +\NewDocumentCommand \tagmcbegin { m } + { + \tag_mc_begin:n {#1}\ignorespaces + } + + +\NewDocumentCommand \tagmcend { } + { + \unskip % this unskip appears to mess up some spacing; can we do this in another way? + \tag_mc_end: + } + +\NewDocumentCommand \tagmcuse { m } + { + \tag_mc_use:n {#1} + } + + +%%%% structure related commands + +\NewDocumentCommand \tagstructbegin { m } + { + \tag_struct_begin:n {#1} + } + +\NewDocumentCommand \tagstructend { } + { + \tag_struct_end: + } + +\NewDocumentCommand \tagstructuse { m } + { + \tag_struct_use:n {#1} + } + + + +%%%% debug/show commands +\NewDocumentCommand\showtagpdfmcdata { O {\@@_tag_get_mc_abs_cnt:} } + { + \bool_if:NT \g_@@_mode_lua_bool + { + \sys_if_engine_luatex:T + { + \directlua{ltx.@@.trace.show_all_mc_data(#1)} + } + } + } + +\NewDocumentCommand\showtagpdfattributes { } + { + \bool_if:NT \g_@@_mode_lua_bool + { + \sys_if_engine_luatex:T + { + \directlua + { + ltx.@@.trace.log + ( + "showtagpdfattributes: + MC=>abscnt=\@@_get_mc_abs_cnt: + =>attr=\the\l_@@_mc_cnt_attr=>tag=" + .. + tostring(ltx.@@.func.get_tag_from (\the\l_@@_mc_type_attr)) + .. + "=\the\l_@@_mc_type_attr",0 + ) + } + \ignorespaces + } + } + } + +\sys_if_engine_luatex:T + { + \NewDocumentCommand\pdffakespace { } + { + \@@_fakespace: + } + } + +%</user> +% \end{macrocode} +% +% \begin{macrocode} +%<*deprecated> +% \uftag_get:n +% \uftag_mc_if_in: +% \uftag_mc_begin:n +% \uftag_mc_end: +% \uftag_mc_use:n +% \uftag_struct_begin:n +% \uftag_struct_end: +% \uftag_finish_structure: +%</deprecated> +% \end{macrocode} diff --git a/Master/texmf-dist/source/latex/tagpdf/tagpdf.ins b/Master/texmf-dist/source/latex/tagpdf/tagpdf.ins new file mode 100644 index 00000000000..c94a1808ded --- /dev/null +++ b/Master/texmf-dist/source/latex/tagpdf/tagpdf.ins @@ -0,0 +1,100 @@ +\iffalse meta-comment + +File: tagpdf.ins + +Copyright (C) 2019 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 released version of this bundle is available from CTAN. + +----------------------------------------------------------------------- + +The development version of the bundle can be found at + + https://github.com/u-fischer/tagpdf + +for those people who are interested. + +----------------------------------------------------------------------- + +\fi + +\input l3docstrip.tex +\askforoverwritefalse + +\let\MetaPrefix\relax +\preamble + +Copyright (C) 2019 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. + +\endpreamble +\let\MetaPrefix\DoubleperCent +% stop docstrip adding \endinput +\postamble +\endpostamble + +\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}}} + +\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}}} + +\generate + {\file{tagpdf-checks-code.sty}{\from{tagpdf-checks.dtx}{checks}}} + +\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% + \MetaPrefix\space End of File `\outFileName'.% +} +\def\currentpostamble{\defaultpostamble}% +\generate{\file{tagpdf.lua} {\from{tagpdf-backend.dtx}{lua}}} +\endbatchfile |