diff options
author | Taco Hoekwater <taco@elvenkind.com> | 2011-06-01 08:54:21 +0000 |
---|---|---|
committer | Taco Hoekwater <taco@elvenkind.com> | 2011-06-01 08:54:21 +0000 |
commit | d7ccb42582f85acf30568913610ccf4d602023fb (patch) | |
tree | 7292e3545a420676878e7451b68892d360c62cb6 /Master/texmf-dist/tex/context/base/lpdf-tag.lua | |
parent | 2d62a6fe9b80def59c392268022f1f9a2d6e358f (diff) |
commit context 2011.05.18
git-svn-id: svn://tug.org/texlive/trunk@22719 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/context/base/lpdf-tag.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/lpdf-tag.lua | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/lpdf-tag.lua b/Master/texmf-dist/tex/context/base/lpdf-tag.lua new file mode 100644 index 00000000000..06f3377bceb --- /dev/null +++ b/Master/texmf-dist/tex/context/base/lpdf-tag.lua @@ -0,0 +1,308 @@ +if not modules then modules = { } end modules ['lpdf-tag'] = { + version = 1.001, + comment = "companion to lpdf-tag.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local format, match, concat = string.format, string.match, table.concat +local lpegmatch = lpeg.match +local utfchar = utf.char + +local trace_tags = false trackers.register("structures.tags", function(v) trace_tags = v end) + +local report_tags = logs.reporter("backend","tags") + +local backends, lpdf, nodes = backends, lpdf, nodes + +local nodeinjections = backends.pdf.nodeinjections +local codeinjections = backends.pdf.codeinjections + +local tasks = nodes.tasks + +local pdfdictionary = lpdf.dictionary +local pdfarray = lpdf.array +local pdfboolean = lpdf.boolean +local pdfconstant = lpdf.constant +local pdfreference = lpdf.reference +local pdfunicode = lpdf.unicode +local pdfstring = lpdf.string +local pdfflushobject = lpdf.flushobject +local pdfreserveobject = lpdf.reserveobject +local pdfpagereference = lpdf.pagereference + +local nodepool = nodes.pool + +local pdfliteral = nodepool.pdfliteral + +local nodecodes = nodes.nodecodes + +local hlist_code = nodecodes.hlist +local vlist_code = nodecodes.vlist +local glyph_code = nodecodes.glyph + +local a_tagged = attributes.private('tagged') +local a_image = attributes.private('image') + +local has_attribute, set_attribute, traverse_nodes, traverse_id = node.has_attribute, node.set_attribute, node.traverse, node.traverse_id +local tosequence = nodes.tosequence +local copy_node, slide_nodelist = node.copy, node.slide + +local structure_stack = { } +local structure_kids = pdfarray() +local structure_ref = pdfreserveobject() +local parent_ref = pdfreserveobject() +local root = { pref = pdfreference(structure_ref), kids = structure_kids } +local tree = { } +local elements = { } +local names = pdfarray() +local taglist = structures.tags.taglist +local usedlabels = structures.tags.labels +local properties = structures.tags.properties +local usedmapping = { } + +local colonsplitter = lpeg.splitat(":") +local dashsplitter = lpeg.splitat("-") + +local add_ids = false -- true + + +--~ function codeinjections.maptag(original,target,kind) +--~ mapping[original] = { target, kind or "inline" } +--~ end + +local function finishstructure() + if #structure_kids > 0 then + local nums, n = pdfarray(), 0 + for i=1,#tree do + n = n + 1 ; nums[n] = i-1 + n = n + 1 ; nums[n] = pdfreference(pdfflushobject(tree[i])) + end + local parenttree = pdfdictionary { + Nums = nums + } + -- we need to split names into smaller parts (e.g. alphabetic or so) + if add_ids then + local kids = pdfdictionary { + Limits = pdfarray { names[1], names[#names-1] }, + Names = names, + } + local idtree = pdfdictionary { + Kids = pdfarray { pdfreference(pdfflushobject(kids)) }, + } + end + -- + local rolemap = pdfdictionary() + for k, v in next, usedmapping do + k = usedlabels[k] or k + local p = properties[k] + rolemap[k] = pdfconstant(p and p.pdf or "Span") -- or "Div" + end + local structuretree = pdfdictionary { + Type = pdfconstant("StructTreeRoot"), + K = pdfreference(pdfflushobject(structure_kids)), + ParentTree = pdfreference(pdfflushobject(parent_ref,parenttree)), + IDTree = (add_ids and pdfreference(pdfflushobject(idtree))) or nil, + RoleMap = rolemap, + } + pdfflushobject(structure_ref,structuretree) + lpdf.addtocatalog("StructTreeRoot",pdfreference(structure_ref)) + -- + local markinfo = pdfdictionary { + Marked = pdfboolean(true), + -- UserProperties = pdfboolean(true), + -- Suspects = pdfboolean(true), + } + lpdf.addtocatalog("MarkInfo",pdfreference(pdfflushobject(markinfo))) + -- + for fulltag, element in next, elements do + pdfflushobject(element.knum,element.kids) + end + end +end + +lpdf.registerdocumentfinalizer(finishstructure,"document structure") + +local index, pageref, pagenum, list = 0, nil, 0, nil + +local pdf_mcr = pdfconstant("MCR") +local pdf_struct_element = pdfconstant("StructElem") + +local function initializepage() + index = 0 + pagenum = tex.count.realpageno + pageref = pdfreference(pdfpagereference(pagenum)) + list = pdfarray() + tree[pagenum] = list -- we can flush after done, todo +end + +local function finishpage() + -- flush what can be flushed + lpdf.addtopageattributes("StructParents",pagenum-1) +end + +-- here we can flush and free elements that are finished + +local function makeelement(fulltag,parent) + local tag, n = lpegmatch(dashsplitter,fulltag) + local tg, detail = lpegmatch(colonsplitter,tag) + local k, r = pdfarray(), pdfreserveobject() + usedmapping[tg] = true + tg = usedlabels[tg] or tg + local d = pdfdictionary { + Type = pdf_struct_element, + S = pdfconstant(tg), + ID = (add_ids and fulltag) or nil, + T = detail and detail or nil, + P = parent.pref, + Pg = pageref, + K = pdfreference(r), + -- Alt = " Who cares ", + -- ActualText = " Hi Hans ", + } + local s = pdfreference(pdfflushobject(d)) + if add_ids then + names[#names+1] = fulltag + names[#names+1] = s + end + local kids = parent.kids + kids[#kids+1] = s + elements[fulltag] = { tag = tag, pref = s, kids = k, knum = r, pnum = pagenum } +end + +local function makecontent(parent,start,stop,slist,id) + local tag, kids = parent.tag, parent.kids + local last = index + if id == "image" then + local d = pdfdictionary { + Type = pdf_mcr, + Pg = pageref, + MCID = last, + Alt = "image", + } + kids[#kids+1] = d + elseif pagenum == parent.pnum then + kids[#kids+1] = last + else + local d = pdfdictionary { + Type = pdf_mcr, + Pg = pageref, + MCID = last, + } + -- kids[#kids+1] = pdfreference(pdfflushobject(d)) + kids[#kids+1] = d + end + -- + local bliteral = pdfliteral(format("/%s <</MCID %s>>BDC",tag,last)) + local prev = start.prev + if prev then + prev.next, bliteral.prev = bliteral, prev + end + start.prev, bliteral.next = bliteral, start + if slist and slist.list == start then + slist.list = bliteral + elseif not prev then + report_tags("this can't happen: injection in front of nothing") + end + -- + local eliteral = pdfliteral("EMC") + local next = stop.next + if next then + next.prev, eliteral.next = eliteral, next + end + stop.next, eliteral.prev = eliteral, stop + -- + index = index + 1 + list[index] = parent.pref + return bliteral, eliteral +end + +-- -- -- + +local level, last, ranges, range = 0, nil, { }, nil + +local function collectranges(head,list) + for n in traverse_nodes(head) do + local id = n.id -- 14: image, 8: literal (mp) + if id == glyph_code then + local at = has_attribute(n,a_tagged) + if not at then + range = nil + elseif last ~= at then + range = { at, "glyph", n, n, list } -- attr id start stop list + ranges[#ranges+1] = range + last = at + elseif range then + range[4] = n -- stop + end + elseif id == hlist_code or id == vlist_code then + local at = has_attribute(n,a_image) + if at then + local at = has_attribute(n,a_tagged) + if not at then + range = nil + else + ranges[#ranges+1] = { at, "image", n, n, list } -- attr id start stop list + end + last = nil + else + local nl = n.list + slide_nodelist(nl) -- temporary hack till math gets slided (tracker item) + collectranges(nl,n) + end + end + end +end + +function nodeinjections.addtags(head) + -- no need to adapt head, as we always operate on lists + level, last, ranges, range = 0, nil, { }, nil + initializepage() + collectranges(head) + if trace_tags then + for i=1,#ranges do + local range = ranges[i] + local attr, id, start, stop = range[1], range[2], range[3], range[4] + local tags = taglist[attr] + if tags then -- not ok ... only first lines + report_tags("%s => %s : %05i %s",tosequence(start,start),tosequence(stop,stop),attr,concat(tags," ")) + end + end + end + for i=1,#ranges do + local range = ranges[i] + local attr, id, start, stop, list = range[1], range[2], range[3], range[4], range[5] + local tags = taglist[attr] + local prev = root + local noftags, tag = #tags, nil + for j=1,noftags do + local tag = tags[j] + if not elements[tag] then + makeelement(tag,prev) + end + prev = elements[tag] + end + local b, e = makecontent(prev,start,stop,list,id) + if start == head then + report_tags("this can't happen: parent list gets tagged") + head = b + end + end + finishpage() + -- can be separate feature + -- + -- injectspans(head) -- does to work yet + -- + return head, true +end + +function codeinjections.enabletags(tg,lb) + structures.tags.handler = nodeinjections.addtags + tasks.enableaction("shipouts","structures.tags.handler") + tasks.enableaction("shipouts","nodes.handlers.accessibility") + tasks.enableaction("math","noads.handlers.tags") + if trace_tags then + report_tags("enabling structure tags") + end +end |