diff options
Diffstat (limited to 'tex/context/base/back-exp.lua')
-rw-r--r-- | tex/context/base/back-exp.lua | 798 |
1 files changed, 536 insertions, 262 deletions
diff --git a/tex/context/base/back-exp.lua b/tex/context/base/back-exp.lua index b9cfcefd4..8450b36f1 100644 --- a/tex/context/base/back-exp.lua +++ b/tex/context/base/back-exp.lua @@ -15,6 +15,9 @@ if not modules then modules = { } end modules ['back-exp'] = { -- We can consider replacing attributes by the hash entry ... slower in resolving but it's still -- quite okay. +-- todo: less attributes e.g. internal only first node +-- todo: build xml tree in mem (handy for cleaning) + local nodecodes = nodes.nodecodes local traverse_nodes = node.traverse local hlist_code = nodecodes.hlist @@ -39,9 +42,9 @@ end nodes.locate = locate local next, type = next, type -local format, match, concat, rep = string.format, string.match, table.concat, string.rep +local format, match, concat, rep, sub, gsub = string.format, string.match, table.concat, string.rep, string.sub, string.gsub local lpegmatch = lpeg.match -local utfchar = utf.char +local utfchar, utfsub = utf.char, utf.sub local insert, remove = table.insert, table.remove local trace_export = false trackers.register ("structures.export", function(v) trace_export = v end) @@ -50,82 +53,91 @@ local trace_tree = false trackers.register ("structures.export.showtree", local less_state = false directives.register("structures.export.lessstate", function(v) less_state = v end) local page_breaks = false directives.register("structures.export.pagebreaks", function(v) page_breaks = v end) -local report_export = logs.reporter("backend","export") - -local nodes = nodes -local attributes = attributes -local variables = interfaces.variables - -local tasks = nodes.tasks -local fontchar = fonts.hashes.characters -local languagenames = languages.numbers - -local nodecodes = nodes.nodecodes -local skipcodes = nodes.skipcodes -local whatsitcodes = nodes.whatsitcodes -local listcodes = nodes.listcodes - -local hlist_code = nodecodes.hlist -local vlist_code = nodecodes.vlist -local glyph_code = nodecodes.glyph -local glue_code = nodecodes.glue -local kern_code = nodecodes.kern -local disc_code = nodecodes.disc -local insert_code = nodecodes.insert -local whatsit_code = nodecodes.whatsit -local refximage_code = whatsitcodes.pdfrefximage - -local userskip_code = skipcodes.userskip -local rightskip_code = skipcodes.rightskip -local parfillskip_code= skipcodes.parfillskip - -local line_code = listcodes.line - -local a_tagged = attributes.private('tagged') -local a_image = attributes.private('image') - -local a_taggedalign = attributes.private("taggedalign") -local a_taggedcolumns = attributes.private("taggedcolumns") -local a_taggedrows = attributes.private("taggedrows") -local a_taggedpar = attributes.private("taggedpar") -local a_taggedpacked = attributes.private("taggedpacked") -local a_taggedsymbol = attributes.private("taggedsymbol") -local a_taggedinsert = attributes.private("taggedinsert") - -local a_reference = attributes.private('reference') - -local has_attribute = node.has_attribute -local traverse_nodes = node.traverse -local slide_nodelist = node.slide -local texattribute = tex.attribute -local unsetvalue = attributes.unsetvalue -local locate_node = nodes.locate - -local references = structures.references -local structurestags = structures.tags -local taglist = structurestags.taglist -local properties = structurestags.properties -local userdata = structurestags.userdata -- might be combines with taglist - -local version = "0.10" -local result = nil +local report_export = logs.reporter("backend","export") + +local nodes = nodes +local attributes = attributes +local variables = interfaces.variables + +local setmetatableindex = table.setmetatableindex +local tasks = nodes.tasks +local fontchar = fonts.hashes.characters +local languagenames = languages.numbers + +local nodecodes = nodes.nodecodes +local skipcodes = nodes.skipcodes +local whatsitcodes = nodes.whatsitcodes +local listcodes = nodes.listcodes + +local hlist_code = nodecodes.hlist +local vlist_code = nodecodes.vlist +local glyph_code = nodecodes.glyph +local glue_code = nodecodes.glue +local kern_code = nodecodes.kern +local disc_code = nodecodes.disc +local insert_code = nodecodes.insert +local whatsit_code = nodecodes.whatsit +local refximage_code = whatsitcodes.pdfrefximage + +local userskip_code = skipcodes.userskip +local rightskip_code = skipcodes.rightskip +local parfillskip_code = skipcodes.parfillskip + +local line_code = listcodes.line + +local a_tagged = attributes.private('tagged') +local a_image = attributes.private('image') + +local a_taggedalign = attributes.private("taggedalign") +local a_taggedcolumns = attributes.private("taggedcolumns") +local a_taggedrows = attributes.private("taggedrows") +local a_taggedpar = attributes.private("taggedpar") +local a_taggedpacked = attributes.private("taggedpacked") +local a_taggedsymbol = attributes.private("taggedsymbol") +local a_taggedinsert = attributes.private("taggedinsert") +local a_taggedtag = attributes.private("taggedtag") + +local a_reference = attributes.private('reference') + +local has_attribute = node.has_attribute +local traverse_nodes = node.traverse +local slide_nodelist = node.slide +local texattribute = tex.attribute +local unsetvalue = attributes.unsetvalue +local locate_node = nodes.locate + +local references = structures.references +local structurestags = structures.tags +local taglist = structurestags.taglist +local properties = structurestags.properties +local userdata = structurestags.userdata -- might be combines with taglist +local tagdata = structurestags.data + +local starttiming = statistics.starttiming +local stoptiming = statistics.stoptiming + +-- todo: more locals (and optimize) + +local version = "0.20" +local result = nil -- todo: nofresult local entry = nil local attributehash = { } -local handle = nil -local hyphen = utfchar(0xAD) +local hyphen = utfchar(0xAD) -- todo: also emdash etc local colonsplitter = lpeg.splitat(":") local dashsplitter = lpeg.splitat("-") local threshold = 65536 local indexing = false local linedone = false local inlinedepth = 0 -local collapse = true local tree = { data = { }, depth = 0 } -- root local treestack = { } local treehash = { } local extras = { } local nofbreaks = 0 -local listhash = { } +local used = { } +local exporting = false + +setmetatableindex(used, function(t,k) if k then local v = { } t[k] = v return v end end) local last = nil local lastpar = nil @@ -136,7 +148,7 @@ local joiner_3 = " " local joiner_4 = " " local joiner_5 = " " local joiner_6 = " " -local joiner_7 = " " +local joiner_7 = "\n" local joiner_8 = " " local joiner_9 = " " local joiner_0 = " " @@ -150,9 +162,22 @@ local joiner_0 = " " -- local tagsplitter = C(precolon) * colon * C(predash) * dash * C(rest) + -- C(predash) * dash * Cc(nil) * C(rest) +local listdata = { } + +local function hashlistdata() + local c = structures.lists.collected + for i=1,#c do + local ci = c[i] + local tag = ci.references.tag + if tag then + listdata[ci.metadata.kind .. ":" .. ci.metadata.name .. "-" .. tag] = ci + end + end +end + local spaces = { } -- watch how we also moved the -1 in depth-1 to the creator -setmetatable(spaces, { __index = function(t,k) t[k] = rep(" ",k-1) return t[k] end } ) +setmetatableindex(spaces, function(t,k) t[k] = rep(" ",k-1) return t[k] end) properties.vspace = { export = "break", nature = "display" } properties.pbreak = { export = "pagebreak", nature = "display" } @@ -184,13 +209,23 @@ local function makebreaknode(node) } end -function extras.document(element,detail,n,fulltag,hash) - handle:write(" language='",languagenames[tex.count.mainlanguagenumber],"'") +local fields = { "title", "subtitle", "author", "keywords" } + +function extras.document(handle,element,detail,n,fulltag,hash) + handle:write(format(" language=%q",languagenames[tex.count.mainlanguagenumber])) if not less_state then - handle:write(" file='",tex.jobname,"'") - handle:write(" date='",os.date(),"'") - handle:write(" context='",environment.version,"'") - handle:write(" version='",version,"'") + handle:write(format(" file=%q",tex.jobname)) + handle:write(format(" date=%q",os.date())) + handle:write(format(" context=%q",environment.version)) + handle:write(format(" version=%q",version)) + local identity = interactions.general.getidentity() + for i=1,#fields do + local key = fields[i] + local value = identity[key] + if value and value ~= "" then + handle:write(format(" %s=%q",key,value)) + end + end end end @@ -206,10 +241,34 @@ function structurestags.setitemgroup(packed,symbol,di) texattribute[a_taggedsymbol] = s end +-- todo: per class + +local synonymnames, synonymnumbers = { }, { } -- can be one hash + +function structurestags.setsynonym(class,tag) + local s = synonymnumbers[tag] + if not s then + s = #synonymnames + 1 + synonymnames[s], synonymnumbers[tag] = tag, s + end + texattribute[a_taggedtag] = s +end + +local sortingnames, sortingnumbers = { }, { } -- can be one hash + +function structurestags.setsorting(class,tag) + local s = sortingnumbers[tag] + if not s then + s = #sortingnames + 1 + sortingnames[s], sortingnumbers[tag] = tag, s + end + texattribute[a_taggedtag] = s +end + local insertids = { } function structurestags.setdescriptionid(tag,n) - local nd = structures.notes.get(tag,n) + local nd = structures.notes.get(tag,n) -- todo: use listdata instead if nd then local r = nd.references texattribute[a_taggedinsert] = r.internal or unsetvalue @@ -218,7 +277,7 @@ function structurestags.setdescriptionid(tag,n) end end -function extras.descriptiontag(element,detail,n,fulltag,di) +function extras.descriptiontag(handle,element,detail,n,fulltag,di) local hash = attributehash[fulltag] if hash then local v = hash.insert @@ -229,7 +288,7 @@ function extras.descriptiontag(element,detail,n,fulltag,di) end end -function extras.descriptionsymbol(element,detail,n,fulltag,di) +function extras.descriptionsymbol(handle,element,detail,n,fulltag,di) local hash = attributehash[fulltag] if hash then local v = hash.insert @@ -240,7 +299,29 @@ function extras.descriptionsymbol(element,detail,n,fulltag,di) end end -function extras.image(element,detail,n,fulltag,di) +function extras.synonym(handle,element,detail,n,fulltag,di) + local hash = attributehash[fulltag] + if hash then + local v = hash.tag + v = v and synonymnames[v] + if v then + handle:write(" tag='",v,"'") + end + end +end + +function extras.sorting(handle,element,detail,n,fulltag,di) + local hash = attributehash[fulltag] + if hash then + local v = hash.tag + v = v and sortingnames[v] + if v then + handle:write(" tag='",v,"'") + end + end +end + +function extras.image(handle,element,detail,n,fulltag,di) local hash = attributehash[fulltag] if hash then local v = hash.imageindex @@ -270,14 +351,14 @@ end local evaluators = { } local specials = { } -evaluators.inner = function(var) +evaluators.inner = function(handle,var) local inner = var.inner if var.inner then handle:write(" location='",inner,"'") end end -evaluators.outer = function(var) +evaluators.outer = function(handle,var) local file, url = references.checkedfileorurl(var.outer,var.outer) if url then handle:write(" url='",file,"'") @@ -286,7 +367,7 @@ evaluators.outer = function(var) end end -evaluators["outer with inner"] = function(var) +evaluators["outer with inner"] = function(handle,var) local file = references.checkedfile(var.f) if file then handle:write(" file='",file,"'") @@ -297,10 +378,10 @@ evaluators["outer with inner"] = function(var) end end -evaluators.special = function(var) +evaluators.special = function(handle,var) local handler = specials[var.special] if handler then - handler(var) + handler(handle,var) end end @@ -308,21 +389,21 @@ evaluators["special outer with operation"] = evaluators.special evaluators["special operation"] = evaluators.special evaluators["special operation with arguments"] = evaluators.special -function specials.url(var) +function specials.url(handle,var) local url = references.checkedurl(var.operation) if url then handle:write(" url='",url,"'") end end -function specials.file(var) +function specials.file(handle,var) local file = references.checkedfile(var.operation) if file then handle:write(" file='",file,"'") end end -function specials.fileorurl(var) +function specials.fileorurl(handle,var) local file, url = references.checkedfileorurl(var.operation,var.operation) if url then handle:write(" url='",file,"'") @@ -331,7 +412,14 @@ function specials.fileorurl(var) end end -local function addreference(references) -- todo: specials -> exporters and then concat +function specials.internal(handle,var) + local internal = references.checkedurl(var.operation) + if internal then + handle:write(" location='aut:",internal,"'") + end +end + +local function adddestination(handle,references) -- todo: specials -> exporters and then concat if references then local reference = references.reference if reference and reference ~= "" then @@ -339,44 +427,97 @@ local function addreference(references) -- todo: specials -> exporters and then if prefix and prefix ~= "" then handle:write(" prefix='",prefix,"'") end - handle:write(" reference='",reference,"'") + handle:write(" destination='",reference,"'") for i=1,#references do local r = references[i] local e = evaluators[r.kind] if e then - e(r) + e(handle,r) end end end end end --- end of references related code -- +local function addreference(handle,references) + if references then + local reference = references.reference + if reference and reference ~= "" then + local prefix = references.prefix + if prefix and prefix ~= "" then + handle:write(" prefix='",prefix,"'") + end + handle:write(" reference='",reference,"'") + end + local internal = references.internal + if internal and internal ~= "" then + handle:write(" location='aut:",internal,"'") + end + end +end -function extras.link(element,detail,n,fulltag,di) - -- why so often +function extras.link(handle,element,detail,n,fulltag,di) + -- for instance in lists a link has nested elements and no own text local hash = attributehash[fulltag] if hash then local references = hash.reference if references then - addreference(structures.references.get(references)) + adddestination(handle,structures.references.get(references)) + end + return true + else + local data = di.data + if data then + for i=1,#data do + local di = data[i] + if di and extras.link(handle,element,detail,n,di.fulltag,di) then + return true + end + end end end end -function extras.section(element,detail,n,fulltag,di) - local hash = listhash[element] - hash = hash and hash[n] - addreference(hash and hash.references) +function extras.section(handle,element,detail,n,fulltag,di) + local data = listdata[fulltag] + if data then + addreference(handle,data.references) + return true + else + local data = di.data + if data then + for i=1,#data do + local di = data[i] + if di then + local ft = di.fulltag + if ft and extras.section(handle,element,detail,n,ft,di) then + return true + end + end + end + end + end end -function extras.float(element,detail,n,fulltag,di) - local hash = listhash[element] - hash = hash and hash[n] - addreference(hash and hash.references) +function extras.float(handle,element,detail,n,fulltag,di) + local data = listdata[fulltag] + if data then + addreference(handle,data.references) + return true + else + local data = di.data + if data then + for i=1,#data do + local di = data[i] + if di and extras.section(handle,element,detail,n,di.fulltag,di) then + return true + end + end + end + end end -function extras.itemgroup(element,detail,n,fulltag,di) +function extras.itemgroup(handle,element,detail,n,fulltag,di) local data = di.data for i=1,#data do local di = data[i] @@ -388,8 +529,6 @@ function extras.itemgroup(element,detail,n,fulltag,di) local tg = ddi.tg if tg == "itemtag" or tg == "itemcontent" then local hash = attributehash[ddi.fulltag] ---~ table.print(attributehash) ---~ print(ddi.fulltag,hash) if hash then local v = hash.packed if v and v == 1 then @@ -408,47 +547,51 @@ function extras.itemgroup(element,detail,n,fulltag,di) end end -function extras.tablecell(element,detail,n,fulltag,di) +function extras.tablecell(handle,element,detail,n,fulltag,di) local hash = attributehash[fulltag] - local v = hash.align - if not v or v == 0 then - -- normal - elseif v == 1 then - handle:write(" align='flushright'") - elseif v == 2 then - handle:write(" align='middle'") - elseif v == 3 then - handle:write(" align='flushleft'") - end - local v = hash.columns - if v and v > 1 then - handle:write(" columns='",v,"'") - end - local v = hash.rows - if v and v > 1 then - handle:write(" rows='",v,"'") + if hash then + local v = hash.align + if not v or v == 0 then + -- normal + elseif v == 1 then + handle:write(" align='flushright'") + elseif v == 2 then + handle:write(" align='middle'") + elseif v == 3 then + handle:write(" align='flushleft'") + end + local v = hash.columns + if v and v > 1 then + handle:write(" columns='",v,"'") + end + local v = hash.rows + if v and v > 1 then + handle:write(" rows='",v,"'") + end end end -function extras.tabulatecell(element,detail,n,fulltag,di) +function extras.tabulatecell(handle,element,detail,n,fulltag,di) local hash = attributehash[fulltag] - local v = hash.align - if not v or v == 0 then - -- normal - elseif v == 1 then - handle:write(" align='flushright'") - elseif v == 2 then - handle:write(" align='middle'") - elseif v == 3 then - handle:write(" align='flushleft'") + if hash then + local v = hash.align + if not v or v == 0 then + -- normal + elseif v == 1 then + handle:write(" align='flushright'") + elseif v == 2 then + handle:write(" align='middle'") + elseif v == 3 then + handle:write(" align='flushleft'") + end end end -local function emptytag(element,nature,depth) +local function emptytag(handle,element,nature,depth) handle:write("\n",spaces[depth],"<",element,"/>\n") end -local function begintag(element,nature,depth,di,empty) +local function begintag(handle,element,nature,depth,di,empty) local detail, n, fulltag = di.detail, di.n, di.fulltag if nature == "inline" then linedone = false @@ -480,7 +623,7 @@ local function begintag(element,nature,depth,di,empty) end local extra = extras[element] if extra then - extra(element,detail,n,fulltag,di) + extra(handle,element,detail,n,fulltag,di) end local u = userdata[fulltag] if u then @@ -496,9 +639,10 @@ local function begintag(element,nature,depth,di,empty) linedone = true end end + used[element][detail or ""] = nature end -local function endtag(element,nature,depth,empty) +local function endtag(handle,element,nature,depth,empty) if nature == "display" then if inlinedepth == 0 then if empty then @@ -556,7 +700,7 @@ local function push(fulltag,depth,entry) data = { }, attribute = attribute, parnumber = parnumber, - node = entry[5], + -- node = entry[5], -- will go } local treedata = tree.data treedata[#treedata+1] = t @@ -574,37 +718,29 @@ local function pop() tree = remove(treestack) end -local function flush(current,content) - if content then - if collapse then - tree.data[#tree.data+1] = content - else - handle:write(content) - end - end -end - local function flushresult(entry) local current, content = entry[1], entry[2] if not content then - -- skip + -- skip, normally this cannot happen else local newdepth, olddepth, content = #current, #treestack, concat(content) if trace_export then - report_export("%3i => %3i : handling: ",olddepth,newdepth,current[newdepth]) + report_export("%s => %s : handling: %s",olddepth,newdepth,current[newdepth]) end if olddepth <= 0 then for i=1,newdepth do if trace_export then - report_export("[1] push :",current[i]) + report_export("[1] push : %s",current[i]) end push(current[i],i,entry) end - flush(current,content) + if content then + tree.data[#tree.data+1] = content + end elseif newdepth < olddepth then for i=newdepth,olddepth-1 do if trace_export then - report_export("[2a] pop :",current[i]) + report_export("[2a] pop : %s",current[i]) end pop() end @@ -613,7 +749,7 @@ local function flushresult(entry) for i=newdepth,1,-1 do if current[i] ~= treestack[i].fulltag then -- needs checking if trace_export then - report_export("[2b] pop :",current[i]) + report_export("[2b] pop : %s",current[i]) end pop() else @@ -623,16 +759,18 @@ local function flushresult(entry) olddepth = #treestack for i=olddepth+1,newdepth do if trace_export then - report_export("[2] push :",current[i]) + report_export("[2] push : %s",current[i]) end push(current[i],i,entry) end - flush(current,content) + if content then + tree.data[#tree.data+1] = content + end elseif newdepth > olddepth then for i=olddepth,1,-1 do if current[i] ~= treestack[i].fulltag then if trace_export then - report_export("[3] pop :",current[i]) + report_export("[3] pop : %s",current[i]) end pop() else @@ -642,19 +780,23 @@ local function flushresult(entry) olddepth = #treestack for i=olddepth+1,newdepth do if trace_export then - report_export("[3] push :",current[i]) + report_export("[3] push : %s",current[i]) end push(current[i],i,entry) end - flush(current,content) + if content then + tree.data[#tree.data+1] = content + end elseif current[newdepth] == treestack[olddepth] then --move up ? -- continuation - flush(current,content) + if content then + tree.data[#tree.data+1] = content + end else for i=olddepth,1,-1 do if current[i] ~= treestack[i].fulltag then if trace_export then - report_export("[4] pop :",current[i]) + report_export("[4] pop : %s",current[i]) end pop() else @@ -664,11 +806,13 @@ local function flushresult(entry) olddepth = #treestack for i=olddepth+1,newdepth do if trace_export then - report_export("[4] push :",current[i]) + report_export("[4] push : %s",current[i]) end push(current[i],i,entry) end - flush(current,content) + if content then + tree.data[#tree.data+1] = content + end end end end @@ -692,47 +836,39 @@ local function checkinserts(data) end end -local function checkreferences(data) - local c = structures.lists.collected - for i=1,#c do -- todo: make hash from name -> n - local ci = c[i] - local name = ci.metadata.kind - local hash = listhash[name] - if not hash then - hash = { } - listhash[name] = hash - end - local tag = ci.references.tag - if tag then - hash[tag] = ci - end - end -end - -local function flushtree(data) - for i=1,#data do +local function flushtree(handle,data,nature) + local nofdata = #data + for i=1,nofdata do local di = data[i] if not di then -- collapsed elseif type(di) == "string" then +if i == nofdata and sub(di,-1) == joiner_7 then + if nature == "inline" or nature == "mixed" then + handle:write(sub(di,1,-2)) + else + handle:write(sub(di,1,-2)," ") + end +else handle:write(di) +end linedone = false elseif not di.collapsed then local element = di.element if element == "break" or element == "pagebreak" then - emptytag(element,nature,di.depth) + emptytag(handle,element,nature,di.depth) else local nature, depth = di.nature, di.depth local did = di.data local nid = #did if nid == 0 or (nid == 1 and did[1] == "") then - begintag(element,nature,depth,di,true) + begintag(handle,element,nature,depth,di,true) -- no content - endtag(element,nature,depth,true) + endtag(handle,element,nature,depth,true) else - begintag(element,nature,depth,di) - flushtree(did) - endtag(element,nature,depth) + begintag(handle,element,nature,depth,di) + flushtree(handle,did,nature) + endtag(handle,element,nature,depth) end end end @@ -750,9 +886,11 @@ local function collapsetree() local lpn = v[i-1].parnumber if lpn and lpn == 0 then lpn = nil end if type(d[1]) ~= "string" then lpn = nil end -- no need anyway so no further testing needed +local justdone = false for j=1,#vd do local vdj = vd[j] if type(vdj) == "string" then +--~ print(vdj) -- experiment, should be improved -- can be simplified ... lpn instead of done if done then @@ -779,6 +917,8 @@ local function collapsetree() end end else +--~ nd = nd + 1 +--~ d[nd] = joiner_3 -- lpn = nil end if vdj ~= "" then @@ -815,7 +955,7 @@ local function prunetree(tree) end end -function finishexport() +local function finishexport() if entry then local result = entry[2] if result and result[#result] == " " then @@ -828,27 +968,25 @@ function finishexport() end end -local function stopexport() - if handle then - report_export("finalizing") - finishexport() - if collapse then - collapsetree() - if trace_tree then - prunetree(tree) - report_export(table.serialize(tree,"root")) - end - end - checkinserts(tree.data) - checkreferences(tree.data) - flushtree(tree.data) - handle = false - end -end +local displaymapping = { + inline = "inline", + display = "block", + mixed = "inline", +} + +local e_template = [[ +%s { + display: %s +}]] + +local d_template = [[ +%s[detail=%s] { + display: %s +}]] -- encoding='utf-8' -local preamble = [[ +local xmlpreamble = [[ <?xml version='1.0' standalone='yes' ?> <!-- input filename : %- 17s --> @@ -857,23 +995,112 @@ local preamble = [[ <!-- exporter version : %- 17s --> ]] -local done = false +local csspreamble = [[ -local function startexport(v) - if not done then - local filename = tex.jobname - if type(v) == "string" and v ~= variables.yes and v ~= "" then - filename = v +<?xml-stylesheet type="text/css" href="%s"?> +]] + +local cssfile, xhtmlfile = nil, nil + +directives.register("backend.export.css", function(v) cssfile = v end) +directives.register("backend.export.xhtml",function(v) xhtmlfile = v end) + +local function stopexport(v) + starttiming(treehash) + report_export("finalizing") + finishexport() + collapsetree() + if trace_tree then + prunetree(tree) + report_export(table.serialize(tree,"root")) + end + checkinserts(tree.data) + hashlistdata() + if type(v) ~= "string" or v == variables.yes or v == "" then + v = tex.jobname + end + local xmlfile = file.addsuffix(v,"export") + local handle = io.open(xmlfile,"wb") + if handle then + report_export("saving xml data in '%s",xmlfile) + handle:write(format(xmlpreamble,tex.jobname,os.date(),environment.version,version)) + if cssfile then + if type(v) ~= "string" or cssfile == variables.yes or cssfile == "" or cssfile == xmlfile then + cssfile = file.replacesuffix(xmlfile,"css") + else + cssfile = file.addsuffix(cssfile,"css") + end + report_export("adding css reference '%s",cssfile) + handle:write(format(csspreamble,cssfile)) end - local filename = file.addsuffix(filename,"export") -- todo: v - handle = io.open(filename,"wb") - if handle then - nodes.tasks.appendaction("shipouts", "normalizers", "nodes.handlers.export") - report_export("saving xml in '%s",filename) - handle:write(format(preamble,tex.jobname,os.date(),environment.version,version)) - luatex.registerstopactions(stopexport) + flushtree(handle,tree.data) + handle:close() + -- css template file + local cssfile = file.replacesuffix(xmlfile,"template") + report_export("saving css template in '%s",cssfile) + local templates = { format("/* template for file %s */",xmlfile) } + for element, details in table.sortedhash(used) do + templates[#templates+1] = format("/* category: %s */",element) + for detail, nature in table.sortedhash(details) do + local d = displaymapping[nature or "display"] or "block" + if detail == "" then + templates[#templates+1] = format(e_template,element,d) + else + templates[#templates+1] = format(d_template,element,detail,d) + end + end + end + io.savedata(cssfile,concat(templates,"\n\n")) + -- xhtml references + if xhtmlfile then + if type(v) ~= "string" or xhtmlfile == variables.yes or xhtmlfile == "" or xhtmlfile == xmlfile then + xhtmlfile = file.replacesuffix(xmlfile,"xhtml") + else + xhtmlfile = file.addsuffix(xhtmlfile,"xhtml") + end + report_export("saving xhtml variant in '%s",xhtmlfile) + local xmltree = xml.load(xmlfile) + if xmltree then + local xmlwrap = xml.wrap + for e in xml.collected(xmltree,"/document") do + e.at["xmlns:xhtml"] = "http://www.w3.org/1999/xhtml" + break + end + local wrapper = { tg = "a", ns = "xhtml", at = { href = "unknown" } } + for e in xml.collected(xmltree,"link") do + local location = e.at.location + if location then + wrapper.at.href = "#" .. gsub(location,":","_") + xmlwrap(e,wrapper) + end + end + local wrapper = { tg = "a", ns = "xhtml", at = { name = "unknown" } } + for e in xml.collected(xmltree,"!link[@location]") do + local location = e.at.location + if location then + wrapper.at.name = gsub(location,":","_") + xmlwrap(e,wrapper) + end + end + xml.save(xmltree,xhtmlfile) + end + end + else + report_export("unable to saving xml in '%s",xmlfile) + end + stoptiming(treehash) +end + +local function startexport(v) + if v and not exporting then + nodes.tasks.appendaction("shipouts", "normalizers", "nodes.handlers.export") + report_export("enabling export to xml") + luatex.registerstopactions(function() stopexport(v) end) + if trace_spaces then + joiner_1 = "<S1/>" joiner_2 = "<S2/>" joiner_3 = "<S3/>" joiner_4 = "<S4/>" joiner_5 = "<S5/>" + joiner_6 = "<S6/>" joiner_7 = "<S7/>" joiner_8 = "<S8/>" joiner_9 = "<S9/>" joiner_0 = "<S0/>" end - done = true + exporting = true end end @@ -883,23 +1110,32 @@ local function injectbreak() flushresult(entry) flushresult(makebreak(entry)) result = { } - entry = { entry[1], result, last, lastpar } + entry = { entry[1], result, last, lastpar } -- entry[1] ? +end + +local function injectspace(a,joiner) + flushresult(entry) + result = { joiner } + local tl = taglist[a] + entry = { tl , result, a, lastpar, n } end local function collectresults(head,list,p) - local preceding = p or false -- nasty hack + local preceding = p or false 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 - report_export("skipping character: 0x%05X %s (no attribute)",n.char,utfchar(n.char)) + -- we need to tag the pagebody stuff as being valid skippable + -- + -- report_export("skipping character: 0x%05X %s (no attribute)",n.char,utfchar(n.char)) else - -- we could add tonunicodes for ligatures + -- we could add tonunicodes for ligatures (todo) local components = n.components - if components then - collectresults(components,nil) - preceding = false + if components then -- we loose data + collectresults(components,nil,preceding) +--~ preceding = true else if last ~= at then local tl = taglist[at] @@ -913,16 +1149,20 @@ local function collectresults(head,list,p) result = { } end lastpar = has_attribute(n,a_taggedpar) - entry = { tl , result, at, lastpar, n } - attributehash[tl[#tl]] = { -- this includes detail ! - align = has_attribute(n,a_taggedalign ), - columns = has_attribute(n,a_taggedcolumns), - rows = has_attribute(n,a_taggedrows ), - packed = has_attribute(n,a_taggedpacked ), - symbol = has_attribute(n,a_taggedsymbol ), - insert = has_attribute(n,a_taggedinsert ), - reference = has_attribute(n,a_reference ), + entry = { tl, result, at, lastpar, n } + local ah = { -- this includes detail ! + align = has_attribute(n,a_taggedalign ), + columns = has_attribute(n,a_taggedcolumns), + rows = has_attribute(n,a_taggedrows ), + packed = has_attribute(n,a_taggedpacked ), + symbol = has_attribute(n,a_taggedsymbol ), + insert = has_attribute(n,a_taggedinsert ), + reference = has_attribute(n,a_reference ), + tag = has_attribute(n,a_taggedtag ), -- used for synonyms } + if next(ah) then + attributehash[tl[#tl]] = ah + end last = at elseif last then local at = has_attribute(n,a_taggedpar) @@ -980,44 +1220,74 @@ local function collectresults(head,list,p) last = nil lastpar = nil else - -- maybe check for lines: n.subtype = line_code +--~ if result and #result > 0 then -- and n.subtype == line_code then +--~ local r = result[#result] +--~ if type(r) == "string" and r ~= " " then +--~ local s = utfsub(r,-1) +--~ if s == hyphen then +--~ result[#result] = utfsub(r,1,-2) +--~ elseif s ~= joiner_7 then +--~ result[#result] = r .. joiner_7 +--~ --~ preceding = true +--~ end +--~ end +--~ preceding = false +--~ end + -- we need to determine an end-of-line preceding = collectresults(n.list,n,preceding) preceding = false end - elseif id == disc_code then + elseif id == disc_code then -- probably too late collectresults(n.replace,nil) preceding = false elseif id == glue_code then + -- we need to distinguish between hskips and vskips local subtype = n.subtype - if subtype == userskip_code then + if subtype == userskip_code then -- todo space_code if n.spec.width > threshold then - preceding = true - if result then - if last and #result > 0 and result[#result] ~= " " then - if has_attribute(n,a_tagged) == last then - result[#result+1] = joiner_6 - preceding = false - end +--~ preceding = true + if result and last and #result > 0 and result[#result] ~= " " then + local a = has_attribute(n,a_tagged) + if a == last then + result[#result+1] = joiner_6 + preceding = false + elseif a then + -- e.g LOGO<space>LOGO + preceding = false + last = a + injectspace(last,joiner_6) end end end ---~ elseif subtype == rightskip_code or subtype == parfillskip_code then ---~ if result and last and #result > 0 and result[#result] ~= " " then ---~ result[#result+1] = joiner_7 ---~ end + elseif subtype == rightskip_code or subtype == parfillskip_code then +if result and #result > 0 then -- and n.subtype == line_code then + local r = result[#result] + if type(r) == "string" and r ~= " " then + local s = utfsub(r,-1) + if s == hyphen then + result[#result] = utfsub(r,1,-2) + elseif s ~= joiner_7 then + result[#result] = r .. joiner_7 +--~ preceding = true + end + end + preceding = false +end end elseif id == kern_code then if n.kern > threshold then - preceding = true - if result then - if last and #result > 0 and result[#result] ~= " " then - if has_attribute(n,a_tagged) == last then - result[#result+1] = joiner_8 - preceding = false - end +--~ preceding = true + if result and last and #result > 0 and result[#result] ~= " " then + local a = has_attribute(n,a_tagged) + if a == last then + result[#result+1] = joiner_8 + preceding = false + elseif a then + -- e.g LOGO<space>LOGO + preceding = false + last = a + injectspace(last,joiner_8) end - elseif not preceding then - preceding = true end end end @@ -1026,10 +1296,6 @@ local function collectresults(head,list,p) end function nodes.handlers.export(head) - if trace_spaces then - joiner_1 = "<S1/>" joiner_2 = "<S2/>" joiner_3 = "<S3/>" joiner_4 = "<S4/>" joiner_5 = "<S5/>" - joiner_6 = "<S6/>" joiner_7 = "<S7/>" joiner_8 = "<S8/>" joiner_9 = "<S9/>" joiner_0 = "<S0/>" - end if result then -- maybe we need a better test for what is in result so far if page_breaks then @@ -1037,7 +1303,15 @@ function nodes.handlers.export(head) end result[#result+1] = joiner_0 end + starttiming(treehash) collectresults(head) -- no flush here, pending page stuff + stoptiming(treehash) return head, true end + +statistics.register("xml exporting time", function() + if exporting then + return format("%s seconds", statistics.elapsedtime(treehash)) + end +end) |