% \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/05/14} {0.81} {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_protected:Nn \@@_prop_new:N { \prop_new:N #1 \directlua { ltx.@@.tables.\cs_to_str:N#1 = {} } } \cs_new_protected:Nn \@@_seq_new:N { \seq_new:N #1 \directlua { ltx.@@.tables.\cs_to_str:N#1 = {} } } \cs_new_protected:Nn \@@_prop_gput:Nnn { \prop_gput:Nnn #1 { #2 } { #3 } \directlua { ltx.@@.tables.\cs_to_str:N#1 ["#2"] = "#3" } } \cs_new_protected:Nn \@@_seq_gput_right:Nn { \seq_gput_right:Nn #1 { #2 } \directlua { table.insert(ltx.@@.tables.\cs_to_str:N#1, "#2") } } %Hm not quite sure about the naming \cs_new:Npn \@@_seq_item:cn #1 #2 { \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_protected:Nn \@@_seq_show:N { \seq_show:N #1 \directlua { ltx.@@.trace.log ("lua~sequence~array~\cs_to_str:N#1",1) } \directlua { ltx.@@.trace.show_seq (ltx.@@.tables.\cs_to_str:N#1) } } \cs_new_protected:Nn \@@_prop_show:N { \prop_show:N #1 \directlua {ltx.@@.trace.log ("lua~property~table~\cs_to_str:N#1",1) } \directlua {ltx.@@.trace.show_prop (ltx.@@.tables.\cs_to_str:N#1) } } % % \end{macrocode} % % \begin{macrocode} %<*pdftex> \ProvidesExplFile {tagpdf-pdftex.def} {2021/05/14} {0.81} {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 % % \end{macrocode} % \begin{macrocode} %<*lua> -- tagpdf.lua -- Ulrike Fischer local ProvidesLuaModule = { name = "tagpdf", version = "0.81", --TAGVERSION date = "2021-05-14", --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 nodehasattribute = node.has_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 -- look also back if glyph.prev and (glyph.prev.id == GLUE) and not inside_math and (glyph.prev.width >0) and not nodehasattribute(glyph.prev,iwspaceattributeid) then nodesetattribute(glyph.prev,iwspaceattributeid,1) nodesetattribute(glyph.prev,iwfontattributeid,glyph.font) -- for debugging if ltx.__tag.trace.showspaces then __tag_show_spacemark (head,glyph) 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,"<> " ) 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" ltx.@@.trace.log("=====>"..tostring(objref),5) 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 % % \end{macrocode}