summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-02-23 22:24:07 +0000
committerKarl Berry <karl@freefriends.org>2021-02-23 22:24:07 +0000
commit9fc4fc7a3e0c2a085bf32447278e741e6667b206 (patch)
treedc6883ed69554b06a17e43fc26c77db73e0eaa52 /Master/texmf-dist/source
parentd2957bb0609af40f0c61a7be4b0476c02de5205a (diff)
tagpdf (23feb21)
git-svn-id: svn://tug.org/texlive/trunk@57857 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source')
-rw-r--r--Master/texmf-dist/source/latex/tagpdf/tagpdf-backend.dtx803
-rw-r--r--Master/texmf-dist/source/latex/tagpdf/tagpdf-checks.dtx232
-rw-r--r--Master/texmf-dist/source/latex/tagpdf/tagpdf-mc.dtx779
-rw-r--r--Master/texmf-dist/source/latex/tagpdf/tagpdf-roles.dtx156
-rw-r--r--Master/texmf-dist/source/latex/tagpdf/tagpdf-space.dtx89
-rw-r--r--Master/texmf-dist/source/latex/tagpdf/tagpdf-struct.dtx789
-rw-r--r--Master/texmf-dist/source/latex/tagpdf/tagpdf-tree.dtx294
-rw-r--r--Master/texmf-dist/source/latex/tagpdf/tagpdf.dtx498
-rw-r--r--Master/texmf-dist/source/latex/tagpdf/tagpdf.ins100
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