summaryrefslogtreecommitdiff
path: root/tex/context/base/back-exp.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/back-exp.lua')
-rw-r--r--tex/context/base/back-exp.lua4112
1 files changed, 2678 insertions, 1434 deletions
diff --git a/tex/context/base/back-exp.lua b/tex/context/base/back-exp.lua
index 18a339247..33b6aa1e8 100644
--- a/tex/context/base/back-exp.lua
+++ b/tex/context/base/back-exp.lua
@@ -6,7 +6,14 @@ if not modules then modules = { } end modules ['back-exp'] = {
license = "see context related readme files"
}
--- beware: we run out of the 200 local limit
+-- Todo: share properties more with tagged pdf (or thge reverse)
+
+-- Because we run into the 200 local limit we quite some do .. end wrappers .. not always
+-- that nice but it has to be.
+
+-- Experiments demonstrated that mapping to <div> and classes is messy because we have to
+-- package attributes (some 30) into one set of (space seperatated but prefixed classes)
+-- which only makes things worse .. so if you want something else, use xslt to get there.
-- language -> only mainlanguage, local languages should happen through start/stoplanguage
-- tocs/registers -> maybe add a stripper (i.e. just don't flush entries in final tree)
@@ -23,21 +30,29 @@ if not modules then modules = { } end modules ['back-exp'] = {
-- todo: move critital formatters out of functions
-- todo: delay loading (apart from basic tag stuff)
-local next, type = next, type
-local format, concat, sub, gsub = string.format, table.concat, string.sub, string.gsub
+-- problem : too many local variables
+
+-- check setting __i__
+
+local next, type, tonumber = next, type, tonumber
+local concat, sub, gsub = table.concat, string.sub, string.gsub
local validstring = string.valid
local lpegmatch = lpeg.match
local utfchar, utfvalues = utf.char, utf.values
local insert, remove = table.insert, table.remove
-local fromunicode16 = fonts.mappings.fromunicode16
local sortedhash = table.sortedhash
local formatters = string.formatters
+local todimen = number.todimen
+local replacetemplate = utilities.templates.replace
local trace_export = false trackers.register ("export.trace", function(v) trace_export = v end)
local trace_spacing = false trackers.register ("export.trace.spacing", function(v) trace_spacing = v end)
+
local less_state = false directives.register("export.lessstate", function(v) less_state = v end)
local show_comment = true directives.register("export.comment", function(v) show_comment = v end)
+show_comment = false -- figure out why break comment
+
-- maybe we will also support these:
--
-- local css_hyphens = false directives.register("export.css.hyphens", function(v) css_hyphens = v end)
@@ -49,7 +64,13 @@ local report_export = logs.reporter("backend","export")
local nodes = nodes
local attributes = attributes
+
local variables = interfaces.variables
+local v_yes = variables.yes
+local v_no = variables.no
+local v_hidden = variables.hidden
+
+local implement = interfaces.implement
local settings_to_array = utilities.parsers.settings_to_array
@@ -85,36 +106,55 @@ local line_code = listcodes.line
local texgetcount = tex.getcount
-local a_characters = attributes.private('characters')
-local a_exportstatus = attributes.private('exportstatus')
+local privateattribute = attributes.private
+local a_characters = privateattribute('characters')
+local a_exportstatus = privateattribute('exportstatus')
+local a_tagged = privateattribute('tagged')
+local a_taggedpar = privateattribute("taggedpar")
+local a_image = privateattribute('image')
+local a_reference = privateattribute('reference')
+local a_textblock = privateattribute("textblock")
-local a_tagged = attributes.private('tagged')
-local a_taggedpar = attributes.private("taggedpar")
-local a_image = attributes.private('image')
-local a_reference = attributes.private('reference')
+local nuts = nodes.nuts
+local tonut = nuts.tonut
-local a_textblock = attributes.private("textblock")
+local getnext = nuts.getnext
+local getsubtype = nuts.getsubtype
+local getfont = nuts.getfont
+local getchar = nuts.getchar
+local getlist = nuts.getlist
+local getid = nuts.getid
+local getfield = nuts.getfield
+local getattr = nuts.getattr
-local traverse_id = node.traverse_id
-local traverse_nodes = node.traverse
-local slide_nodelist = node.slide
-local locate_node = nodes.locate
+local setattr = nuts.setattr
+
+local traverse_id = nuts.traverse_id
+local traverse_nodes = nuts.traverse
local references = structures.references
local structurestags = structures.tags
local taglist = structurestags.taglist
+local specifications = structurestags.specifications
local properties = structurestags.properties
-local userdata = structurestags.userdata -- might be combines with taglist
-local tagdata = structurestags.data
-local tagmetadata = structurestags.metadata
-local detailedtag = structurestags.detailedtag
+local locatedtag = structurestags.locatedtag
local starttiming = statistics.starttiming
local stoptiming = statistics.stoptiming
+local characterdata = characters.data
+local overloads = fonts.mappings.overloads
+
-- todo: more locals (and optimize)
-local exportversion = "0.30"
+local exportversion = "0.34"
+local mathmlns = "http://www.w3.org/1998/Math/MathML"
+local contextns = "http://www.contextgarden.net/context/export" -- whatever suits
+local cssnamespaceurl = "@namespace context url('%namespace%') ;"
+local cssnamespace = "context|"
+----- cssnamespacenop = "/* no namespace */"
+
+local usecssnamespace = false
local nofcurrentcontent = 0 -- so we don't free (less garbage collection)
local currentcontent = { }
@@ -125,14 +165,15 @@ local currentparagraph = nil
local noftextblocks = 0
-local attributehash = { } -- to be considered: set the values at the tex end
local hyphencode = 0xAD
local hyphen = utfchar(0xAD) -- todo: also emdash etc
-local colonsplitter = lpeg.splitat(":")
-local dashsplitter = lpeg.splitat("-")
+local tagsplitter = structurestags.patterns.splitter
+----- colonsplitter = lpeg.splitat(":")
+----- dashsplitter = lpeg.splitat("-")
local threshold = 65536
local indexing = false
local keephyphens = false
+local exportproperties = false
local finetuning = { }
@@ -140,6 +181,8 @@ local treestack = { }
local nesting = { }
local currentdepth = 0
+local wrapups = { }
+
local tree = { data = { }, fulltag == "root" } -- root
local treeroot = tree
local treehash = { }
@@ -155,20 +198,18 @@ local somespace = { [0x20] = true, [" "] = true } -- for testing
local entities = { ["&"] = "&amp;", [">"] = "&gt;", ["<"] = "&lt;" }
local attribentities = { ["&"] = "&amp;", [">"] = "&gt;", ["<"] = "&lt;", ['"'] = "quot;" }
-local entityremapper = utf.remapper(entities)
+local p_entity = lpeg.replacer(entities) -- was: entityremapper = utf.remapper(entities)
+local p_attribute = lpeg.replacer(attribentities)
+local p_stripper = lpeg.patterns.stripper
+local p_escaped = lpeg.patterns.xml.escaped
-local alignmapping = {
- flushright = "right",
- middle = "center",
- flushleft = "left",
-}
+local f_tagid = formatters["%s-%04i"]
-local numbertoallign = {
- [0] = "justify", ["0"] = "justify", [variables.normal ] = "justify",
- [1] = "right", ["1"] = "right", [variables.flushright] = "right",
- [2] = "center", ["2"] = "center", [variables.middle ] = "center",
- [3] = "left", ["3"] = "left", [variables.flushleft ] = "left",
-}
+-- local alignmapping = {
+-- flushright = "right",
+-- middle = "center",
+-- flushleft = "left",
+-- }
local defaultnature = "mixed" -- "inline"
@@ -180,10 +221,14 @@ setmetatableindex(used, function(t,k)
end
end)
+local f_entity = formatters["&#x%X;"]
+local f_attribute = formatters[" %s=%q"]
+local f_property = formatters[" %s%s=%q"]
+
setmetatableindex(specialspaces, function(t,k)
local v = utfchar(k)
t[k] = v
- entities[v] = formatters["&#x%X;"](k)
+ entities[v] = f_entity(k)
somespace[k] = true
somespace[v] = true
return v
@@ -195,30 +240,35 @@ local namespaced = {
}
local namespaces = {
- msubsup = "m",
- msub = "m",
- msup = "m",
- mn = "m",
- mi = "m",
- ms = "m",
- mo = "m",
- mtext = "m",
- mrow = "m",
- mfrac = "m",
- mroot = "m",
- msqrt = "m",
- munderover = "m",
- munder = "m",
- mover = "m",
- merror = "m",
- math = "m",
- mrow = "m",
- mtable = "m",
- mtr = "m",
- mtd = "m",
- mfenced = "m",
- maction = "m",
- mspace = "m",
+ msubsup = "m",
+ msub = "m",
+ msup = "m",
+ mn = "m",
+ mi = "m",
+ ms = "m",
+ mo = "m",
+ mtext = "m",
+ mrow = "m",
+ mfrac = "m",
+ mroot = "m",
+ msqrt = "m",
+ munderover = "m",
+ munder = "m",
+ mover = "m",
+ merror = "m",
+ math = "m",
+ mrow = "m",
+ mtable = "m",
+ mtr = "m",
+ mtd = "m",
+ mfenced = "m",
+ maction = "m",
+ mspace = "m",
+ -- only when testing
+ mstacker = "m",
+ mstackertop = "m",
+ mstackermid = "m",
+ mstackerbot = "m",
}
setmetatableindex(namespaced, function(t,k)
@@ -232,171 +282,231 @@ end)
local function attribute(key,value)
if value and value ~= "" then
- return formatters[' %s="%s"'](key,gsub(value,".",attribentities))
+ return f_attribute(key,lpegmatch(p_attribute,value))
else
return ""
end
end
--- local P, C, Cc = lpeg.P, lpeg.C, lpeg.Cc
---
--- local dash, colon = P("-"), P(":")
---
--- local precolon, predash, rest = P((1-colon)^1), P((1-dash )^1), P(1)^1
---
--- local tagsplitter = C(precolon) * colon * C(predash) * dash * C(rest) +
--- C(predash) * dash * Cc(nil) * C(rest)
+local function setattribute(di,key,value,escaped)
+ if value and value ~= "" then
+ local a = di.attributes
+ if escaped then
+ value = lpegmatch(p_escaped,value)
+ end
+ if not a then
+ di.attributes = { [key] = value }
+ else
+ a[key] = value
+ end
+ end
+end
-local listdata = { }
+local listdata = { } -- this has to be done otherwise: each element can just point back to ...
-local function hashlistdata()
+function wrapups.hashlistdata()
local c = structures.lists.collected
for i=1,#c do
local ci = c[i]
local tag = ci.references.tag
if tag then
local m = ci.metadata
- listdata[m.kind .. ":" .. m.name .. "-" .. tag] = ci
+-- listdata[m.kind .. ":" .. m.name .. "-" .. tag] = ci
+ listdata[m.kind .. ">" .. tag] = ci
end
end
end
-local spaces = utilities.strings.newrepeater(" ",-1)
-
-function structurestags.setattributehash(fulltag,key,value) -- public hash
- if type(fulltag) == "number" then
- fulltag = taglist[fulltag]
- if fulltag then
- fulltag = fulltag[#fulltag]
- end
- end
- if fulltag then
- local ah = attributehash[fulltag] -- could be metatable magic
- if not ah then
- ah = { }
- attributehash[fulltag] = ah
- end
- ah[key] = value
+function structurestags.setattributehash(attr,key,value) -- public hash
+ local specification = taglist[attr]
+ if specification then
+ specification[key] = value
+ else
+ -- some kind of error
end
end
+local usedstyles = { }
--- experiment: styles and images
---
--- officially we should convert to bp but we round anyway
+local namespacetemplate = [[
+/* %what% for file %filename% */
-local usedstyles = { }
+%cssnamespaceurl%
+]]
+
+do
+
+ -- experiment: styles and images
+ --
+ -- officially we should convert to bp but we round anyway
--- /* padding : ; */
--- /* text-justify : inter-word ; */
+ -- /* padding : ; */
+ -- /* text-justify : inter-word ; */
+ -- /* text-align : justify ; */
local documenttemplate = [[
-document {
- font-size : %s !important ;
- max-width : %s !important ;
- text-align : %s !important ;
- hyphens : %s !important ;
+document, %namespace%div.document {
+ font-size : %size% !important ;
+ max-width : %width% !important ;
+ text-width : %align% !important ;
+ hyphens : %hyphens% !important ;
}
]]
local styletemplate = [[
-%s[detail='%s'] {
- font-style : %s ;
- font-variant : %s ;
- font-weight : %s ;
- font-family : %s ;
- color : %s ;
+%element%[detail="%detail%"], %namespace%div.%element%.%detail% {
+ display : inline ;
+ font-style : %style% ;
+ font-variant : %variant% ;
+ font-weight : %weight% ;
+ font-family : %family% ;
+ color : %color% ;
}]]
-local function allusedstyles(xmlfile)
- local result = { format("/* styles for file %s */",xmlfile) }
- --
- local bodyfont = finetuning.bodyfont
- local width = finetuning.width
- local hyphen = finetuning.hyphen
- local align = finetuning.align
- --
- if not bodyfont or bodyfont == "" then
- bodyfont = "12pt"
- elseif type(bodyfont) == "number" then
- bodyfont = number.todimen(bodyfont,"pt","%ipt") or "12pt"
- end
- if not width or width == "" then
- width = "50em"
- elseif type(width) == "number" then
- width = number.todimen(width,"pt","%ipt") or "50em"
- end
- if hyphen == variables.yes then
- hyphen = "manual"
- else
- hyphen = "inherited"
- end
- if align then
- align = numbertoallign[align]
- end
- if not align then
- align = hyphens and "justify" or "inherited"
+ local numbertoallign = {
+ [0] = "justify", ["0"] = "justify", [variables.normal ] = "justify",
+ [1] = "right", ["1"] = "right", [variables.flushright] = "right",
+ [2] = "center", ["2"] = "center", [variables.middle ] = "center",
+ [3] = "left", ["3"] = "left", [variables.flushleft ] = "left",
+ }
+
+ function wrapups.allusedstyles(basename)
+ local result = { replacetemplate(namespacetemplate, {
+ what = "styles",
+ filename = basename,
+ namespace = contextns,
+ -- cssnamespaceurl = usecssnamespace and cssnamespaceurl or cssnamespacenop,
+ cssnamespaceurl = cssnamespaceurl,
+ }) }
+ --
+ local bodyfont = finetuning.bodyfont
+ local width = finetuning.width
+ local hyphen = finetuning.hyphen
+ local align = finetuning.align
+ --
+ if type(bodyfont) == "number" then
+ bodyfont = todimen(bodyfont)
+ else
+ bodyfont = "12pt"
+ end
+ if type(width) == "number" then
+ width = todimen(width) or "50em"
+ else
+ width = "50em"
+ end
+ if hyphen == v_yes then
+ hyphen = "manual"
+ else
+ hyphen = "inherited"
+ end
+ if align then
+ align = numbertoallign[align]
+ end
+ if not align then
+ align = hyphen and "justify" or "inherited"
+ end
+ --
+ result[#result+1] = replacetemplate(documenttemplate,{
+ size = bodyfont,
+ width = width,
+ align = align,
+ hyphens = hyphen
+ })
+ --
+ local colorspecification = xml.css.colorspecification
+ local fontspecification = xml.css.fontspecification
+ for element, details in sortedhash(usedstyles) do
+ for detail, data in sortedhash(details) do
+ local s = fontspecification(data.style)
+ local c = colorspecification(data.color)
+ detail = gsub(detail,"[^A-Za-z0-9]+","-")
+ result[#result+1] = replacetemplate(styletemplate,{
+ namespace = usecssnamespace and cssnamespace or "",
+ element = element,
+ detail = detail,
+ style = s.style or "inherit",
+ variant = s.variant or "inherit",
+ weight = s.weight or "inherit",
+ family = s.family or "inherit",
+ color = c or "inherit",
+ })
+ end
+ end
+ return concat(result,"\n\n")
end
- --
- result[#result+1] = format(documenttemplate,bodyfont,width,align,hyphen)
- --
- local colorspecification = xml.css.colorspecification
- local fontspecification = xml.css.fontspecification
- for element, details in sortedhash(usedstyles) do
- for detail, data in sortedhash(details) do
- local s = fontspecification(data.style)
- local c = colorspecification(data.color)
- result[#result+1] = formatters[styletemplate](element,detail,
- s.style or "inherit",
- s.variant or "inherit",
- s.weight or "inherit",
- s.family or "inherit",
- c or "inherit")
- end
- end
- return concat(result,"\n\n")
+
end
local usedimages = { }
+do
+
local imagetemplate = [[
-%s[id="%s"] {
+%element%[id="%id%"], %namespace%div.%element%[id="%id%"] {
display : block ;
- background-image : url(%s) ;
+ background-image : url('%url%') ;
background-size : 100%% auto ;
background-repeat : no-repeat ;
- width : %s ;
- height : %s ;
+ width : %width% ;
+ height : %height% ;
}]]
-local function allusedimages(xmlfile)
- local result = { format("/* images for file %s */",xmlfile) }
- for element, details in sortedhash(usedimages) do
- for detail, data in sortedhash(details) do
- local name = data.name
- if file.suffix(name) == "pdf" then
- -- temp hack .. we will have a remapper
- name = file.replacesuffix(name,"svg")
- end
- result[#result+1] = formatters[imagetemplate](element,detail,name,data.width,data.height)
- end
- end
- return concat(result,"\n\n")
-end
+ local f_svgname = formatters["%s.svg"]
+ local f_svgpage = formatters["%s-page-%s.svg"]
+ local collected = { }
-local function uniqueusedimages()
- local unique = { }
- for element, details in next, usedimages do
- for detail, data in next, details do
- local name = data.name
- if file.suffix(name) == "pdf" then
- unique[file.replacesuffix(name,"svg")] = name
+ local function usedname(name,page)
+ if file.suffix(name) == "pdf" then
+ -- temp hack .. we will have a remapper
+ if page and page > 1 then
+ name = f_svgpage(file.nameonly(name),page)
else
- unique[name] = name
+ name = f_svgname(file.nameonly(name))
end
end
+ local scheme = url.hasscheme(name)
+ if not scheme or scheme == "file" then
+ -- or can we just use the name ?
+ return file.join("../images",file.basename(url.filename(name)))
+ else
+ return name
+ end
+ end
+
+ function wrapups.allusedimages(basename)
+ local result = { replacetemplate(namespacetemplate, {
+ what = "images",
+ filename = basename,
+ namespace = contextns,
+ -- cssnamespaceurl = usecssnamespace and cssnamespaceurl or "",
+ cssnamespaceurl = cssnamespaceurl,
+ }) }
+ for element, details in sortedhash(usedimages) do
+ for detail, data in sortedhash(details) do
+ local name = data.name
+ local page = tonumber(data.page) or 1
+ local spec = {
+ element = element,
+ id = data.id,
+ name = name,
+ page = page,
+ url = usedname(name,page),
+ width = data.width,
+ height = data.height,
+ used = data.used,
+ namespace = usecssnamespace and cssnamespace or "",
+ }
+ result[#result+1] = replacetemplate(imagetemplate,spec)
+ collected[detail] = spec
+ end
+ end
+ return concat(result,"\n\n")
+ end
+
+ function wrapups.uniqueusedimages() -- todo: combine these two
+ return collected
end
- return unique
+
end
--
@@ -407,13 +517,14 @@ properties.vspace = { export = "break", nature = "display" }
local function makebreaklist(list)
nofbreaks = nofbreaks + 1
local t = { }
- if list then
+ local l = list and list.taglist
+ if l then
for i=1,#list do
- t[i] = list[i]
+ t[i] = l[i]
end
end
- t[#t+1] = "break-" .. nofbreaks -- maybe no number
- return t
+ t[#t+1] = "break>" .. nofbreaks -- maybe no number or 0
+ return { taglist = t }
end
local breakattributes = {
@@ -424,7 +535,7 @@ local function makebreaknode(attributes) -- maybe no fulltag
nofbreaks = nofbreaks + 1
return {
tg = "break",
- fulltag = "break-" .. nofbreaks,
+ fulltag = "break>" .. nofbreaks,
n = nofbreaks,
element = "break",
nature = "display",
@@ -435,1217 +546,1764 @@ local function makebreaknode(attributes) -- maybe no fulltag
}
end
-local fields = { "title", "subtitle", "author", "keywords" }
+local function ignorebreaks(di,element,n,fulltag)
+ local data = di.data
+ for i=1,#data do
+ local d = data[i]
+ if d.content == " " then
+ d.content = ""
+ end
+ end
+end
-local function checkdocument(root)
- local data = root.data
- if data then
- for i=1,#data do
- local di = data[i]
- if di.content then
- -- ok
- elseif di.tg == "ignore" then
- di.element = ""
- checkdocument(di)
- else
- -- can't happen
- end
+local function ignorespaces(di,element,n,fulltag)
+ local data = di.data
+ for i=1,#data do
+ local d = data[i]
+ local c = d.content
+ if type(c) == "string" then
+ d.content = lpegmatch(p_stripper,c)
end
end
end
-function extras.document(result,element,detail,n,fulltag,di)
- result[#result+1] = format(" language=%q",languagenames[texgetcount("mainlanguagenumber")])
- if not less_state then
- result[#result+1] = format(" file=%q",tex.jobname)
- result[#result+1] = format(" date=%q",os.date())
- result[#result+1] = format(" context=%q",environment.version)
- result[#result+1] = format(" version=%q",exportversion)
- result[#result+1] = format(" xmlns:m=%q","http://www.w3.org/1998/Math/MathML")
- local identity = interactions.general.getidentity()
- for i=1,#fields do
- local key = fields[i]
- local value = identity[key]
- if value and value ~= "" then
- result[#result+1] = formatters[" %s=%q"](key,value)
+do
+
+ local fields = { "title", "subtitle", "author", "keywords" }
+
+ local function checkdocument(root)
+ local data = root.data
+ if data then
+ for i=1,#data do
+ local di = data[i]
+ local tg = di.tg
+ if tg == "noexport" then
+ local s = specifications[di.fulltag]
+ local u = s and s.userdata
+ if u then
+ local comment = u.comment
+ if comment then
+ di.element = "comment"
+ di.data = { { content = comment } }
+ u.comment = nil
+ else
+ data[i] = false
+ end
+ else
+ data[i] = false
+ end
+ elseif di.content then
+ -- okay
+ elseif tg == "ignore" then
+ di.element = ""
+ checkdocument(di)
+ else
+ checkdocument(di) -- new, else no noexport handling
+ end
end
end
end
- checkdocument(di)
-end
-local itemgroups = { }
+ function extras.document(di,element,n,fulltag)
+ setattribute(di,"language",languagenames[texgetcount("mainlanguagenumber")])
+ if not less_state then
+ setattribute(di,"file",tex.jobname)
+ setattribute(di,"date",os.date())
+ setattribute(di,"context",environment.version)
+ setattribute(di,"version",exportversion)
+ setattribute(di,"xmlns:m",mathmlns)
+ local identity = interactions.general.getidentity()
+ for i=1,#fields do
+ local key = fields[i]
+ local value = identity[key]
+ if value and value ~= "" then
+ setattribute(di,key,value)
+ end
+ end
+ end
+ checkdocument(di)
+ end
-function structurestags.setitemgroup(current,packed,symbol)
- itemgroups[detailedtag("itemgroup",current)] = {
- packed = packed,
- symbol = symbol,
- }
end
-function extras.itemgroup(result,element,detail,n,fulltag,di)
- local hash = itemgroups[fulltag]
- if hash then
- local v = hash.packed
- if v then
- result[#result+1] = " packed='yes'"
+do
+
+ local itemgroups = { }
+
+ function structurestags.setitemgroup(packed,level,symbol)
+ itemgroups[locatedtag("itemgroup")] = {
+ packed = packed,
+ symbol = symbol,
+ level = level,
+ }
+ end
+
+ function structurestags.setitem(kind)
+ itemgroups[locatedtag("item")] = {
+ kind = kind,
+ }
+ end
+
+ function extras.itemgroup(di,element,n,fulltag)
+ local hash = itemgroups[fulltag]
+ if hash then
+ setattribute(di,"packed",hash.packed and "yes" or nil)
+ setattribute(di,"symbol",hash.symbol)
+ setattribute(di,"level",hash.level)
end
- local v = hash.symbol
- if v then
- result[#result+1] = attribute("symbol",v)
+ end
+
+ function extras.item(di,element,n,fulltag)
+ local hash = itemgroups[fulltag]
+ if hash then
+ local kind = hash.kind
+ if kind and kind ~= "" then
+ setattribute(di,"kind",kind)
+ end
end
end
+
end
-local synonyms = { }
+do
-function structurestags.setsynonym(current,tag)
- synonyms[detailedtag("synonym",current)] = tag
-end
+ local synonyms = { }
+ local sortings = { }
-function extras.synonym(result,element,detail,n,fulltag,di)
- local tag = synonyms[fulltag]
- if tag then
- result[#result+1] = formatters[" tag='%s'"](tag)
+ function structurestags.setsynonym(tag)
+ synonyms[locatedtag("synonym")] = tag
end
-end
-local sortings = { }
+ function extras.synonym(di,element,n,fulltag)
+ local tag = synonyms[fulltag]
+ if tag then
+ setattribute(di,"tag",tag)
+ end
+ end
-function structurestags.setsorting(current,tag)
- sortings[detailedtag("sorting",current)] = tag
-end
+ function structurestags.setsorting(tag)
+ sortings[locatedtag("sorting")] = tag
+ end
-function extras.sorting(result,element,detail,n,fulltag,di)
- local tag = sortings[fulltag]
- if tag then
- result[#result+1] = formatters[" tag='%s'"](tag)
+ function extras.sorting(di,element,n,fulltag)
+ local tag = sortings[fulltag]
+ if tag then
+ setattribute(di,"tag",tag)
+ end
end
+
end
-usedstyles.highlight = { }
+do
-function structurestags.sethighlight(current,style,color) -- we assume global styles
- usedstyles.highlight[current] = {
- style = style, -- xml.css.fontspecification(style),
- color = color, -- xml.css.colorspec(color),
- }
-end
+ local highlight = { }
+ usedstyles.highlight = highlight
-local descriptions = { }
-local symbols = { }
-local linked = { }
+ local strippedtag = structurestags.strip -- we assume global styles
-function structurestags.setdescription(tag,n)
- local nd = structures.notes.get(tag,n) -- todo: use listdata instead
- if nd then
- local references = nd.references
- descriptions[references and references.internal] = detailedtag("description",tag)
+ function structurestags.sethighlight(style,color)
+ highlight[strippedtag(locatedtag("highlight"))] = {
+ style = style, -- xml.css.fontspecification(style),
+ color = color, -- xml.css.colorspec(color),
+ }
end
+
end
-function structurestags.setdescriptionsymbol(tag,n)
- local nd = structures.notes.get(tag,n) -- todo: use listdata instead
- if nd then
- local references = nd.references
- symbols[references and references.internal] = detailedtag("descriptionsymbol",tag)
+do
+
+ -- is this referencing still needed?
+
+ local descriptions = { }
+ local symbols = { }
+ local linked = { }
+
+ function structurestags.setdescription(tag,n) -- needs checking (is tag needed)
+ -- we can also use the internals hash or list
+ local nd = structures.notes.get(tag,n)
+ if nd then
+ local references = nd.references
+ descriptions[references and references.internal] = locatedtag("description")
+ end
end
-end
-function finalizers.descriptions(tree)
- local n = 0
- for id, tag in next, descriptions do
- local sym = symbols[id]
- if sym then
- n = n + 1
- linked[tag] = n
- linked[sym] = n
+ function structurestags.setdescriptionsymbol(tag,n) -- needs checking (is tag needed)
+ local nd = structures.notes.get(tag,n) -- todo: use listdata instead
+ if nd then
+ local references = nd.references
+ symbols[references and references.internal] = locatedtag("descriptionsymbol")
end
end
-end
-function extras.description(result,element,detail,n,fulltag,di)
- local id = linked[fulltag]
- if id then
- result[#result+1] = formatters[" insert='%s'"](id) -- maybe just fulltag
+ function finalizers.descriptions(tree)
+ local n = 0
+ for id, tag in next, descriptions do
+ local sym = symbols[id]
+ if sym then
+ n = n + 1
+ linked[tag] = n
+ linked[sym] = n
+ end
+ end
end
-end
-function extras.descriptionsymbol(result,element,detail,n,fulltag,di)
- local id = linked[fulltag]
- if id then
- result[#result+1] = formatters[" insert='%s'"](id)
+ function extras.description(di,element,n,fulltag)
+ local id = linked[fulltag]
+ if id then
+ setattribute(di,"insert",id)
+ end
end
-end
-usedimages.image = { }
+ function extras.descriptionsymbol(di,element,n,fulltag)
+ local id = linked[fulltag]
+ if id then
+ setattribute(di,"insert",id)
+ end
+ end
-function structurestags.setfigure(name,page,width,height)
- usedimages.image[detailedtag("image")] = {
- name = name,
- page = page,
- width = number.todimen(width,"cm","%0.3fcm"),
- height = number.todimen(height,"cm","%0.3fcm"),
- }
end
-function extras.image(result,element,detail,n,fulltag,di)
- local data = usedimages.image[fulltag]
- if data then
- result[#result+1] = attribute("name",data.name)
- if tonumber(data.page) > 1 then
- result[#result+1] = formatters[" page='%s'"](data.page)
+-- -- todo: ignore breaks
+--
+-- function extras.verbatimline(di,element,n,fulltag)
+-- inspect(di)
+-- end
+
+do
+
+ local image = { }
+ usedimages.image = image
+
+ local f_id = formatters["%s-%s"]
+
+ function structurestags.setfigure(name,used,page,width,height)
+ local fulltag = locatedtag("image")
+ local spec = specifications[fulltag]
+ local page = tonumber(page)
+ image[fulltag] = {
+ id = f_id(spec.tagname,spec.tagindex),
+ name = name,
+ used = used,
+ page = page and page > 1 and page or nil,
+ width = todimen(width, "cm","%0.3F%s"),
+ height = todimen(height,"cm","%0.3F%s"),
+ }
+ end
+
+ function extras.image(di,element,n,fulltag)
+ local data = image[fulltag]
+ if data then
+ setattribute(di,"name",data.name)
+ setattribute(di,"page",data.page)
+ setattribute(di,"id",data.id)
+ setattribute(di,"width",data.width)
+ setattribute(di,"height",data.height)
end
- result[#result+1] = formatters[" id='%s' width='%s' height='%s'"](fulltag,data.width,data.height)
end
+
end
-local combinations = { }
+do
-function structurestags.setcombination(nx,ny)
- combinations[detailedtag("combination")] = {
- nx = nx,
- ny = ny,
- }
-end
+ local combinations = { }
+
+ function structurestags.setcombination(nx,ny)
+ combinations[locatedtag("combination")] = {
+ nx = nx,
+ ny = ny,
+ }
+ end
-function extras.combination(result,element,detail,n,fulltag,di)
- local data = combinations[fulltag]
- if data then
- result[#result+1] = formatters[" nx='%s' ny='%s'"](data.nx,data.ny)
+ function extras.combination(di,element,n,fulltag)
+ local data = combinations[fulltag]
+ if data then
+ setattribute(di,"nx",data.nx)
+ setattribute(di,"ny",data.ny)
+ end
end
+
end
-- quite some code deals with exporting references --
+-- links:
+--
+-- url :
+-- file :
+-- internal : automatic location
+-- location : named reference
+
+-- references:
+--
+-- implicit : automatic reference
+-- explicit : named reference
+
local evaluators = { }
local specials = { }
+local explicits = { }
-evaluators.inner = function(result,var)
+evaluators.inner = function(di,var)
local inner = var.inner
if inner then
- result[#result+1] = attribute("location",inner)
+ setattribute(di,"location",inner,true)
end
end
-evaluators.outer = function(result,var)
+evaluators.outer = function(di,var)
local file, url = references.checkedfileorurl(var.outer,var.outer)
if url then
- result[#result+1] = attribute("url",url)
+ setattribute(di,"url",url,true)
elseif file then
- result[#result+1] = attribute("file",file)
+ setattribute(di,"file",file,true)
end
end
-evaluators["outer with inner"] = function(result,var)
+evaluators["outer with inner"] = function(di,var)
local file = references.checkedfile(var.f)
if file then
- result[#result+1] = attribute("file",file)
+ setattribute(di,"file",file,true)
end
local inner = var.inner
if inner then
- result[#result+1] = attribute("location",inner)
+ setattribute(di,"inner",inner,true)
end
end
-evaluators.special = function(result,var)
+evaluators.special = function(di,var)
local handler = specials[var.special]
if handler then
- handler(result,var)
+ handler(di,var)
end
end
-evaluators["special outer with operation"] = evaluators.special
-evaluators["special operation"] = evaluators.special
-evaluators["special operation with arguments"] = evaluators.special
+local referencehash = { }
-function specials.url(result,var)
- local url = references.checkedurl(var.operation)
- if url then
- result[#result+1] = attribute("url",url)
- end
-end
+do
-function specials.file(result,var)
- local file = references.checkedfile(var.operation)
- if file then
- result[#result+1] = attribute("file",file)
+ evaluators["special outer with operation"] = evaluators.special
+ evaluators["special operation"] = evaluators.special
+ evaluators["special operation with arguments"] = evaluators.special
+
+ function specials.url(di,var)
+ local url = references.checkedurl(var.operation)
+ if url and url ~= "" then
+ setattribute(di,"url",url,true)
+ end
end
-end
-function specials.fileorurl(result,var)
- local file, url = references.checkedfileorurl(var.operation,var.operation)
- if url then
- result[#result+1] = attribute("url",url)
- elseif file then
- result[#result+1] = attribute("file",file)
+ function specials.file(di,var)
+ local file = references.checkedfile(var.operation)
+ if file and file ~= "" then
+ setattribute(di,"file",file,true)
+ end
end
-end
-function specials.internal(result,var)
- local internal = references.checkedurl(var.operation)
- if internal then
- result[#result+1] = formatters[" location='aut:%s'"](internal)
+ function specials.fileorurl(di,var)
+ local file, url = references.checkedfileorurl(var.operation,var.operation)
+ if url and url ~= "" then
+ setattribute(di,"url",url,true)
+ elseif file and file ~= "" then
+ setattribute(di,"file",file,true)
+ end
end
-end
-local referencehash = { }
+ function specials.internal(di,var)
+ local internal = references.checkedurl(var.operation)
+ if internal then
+ setattribute(di,"location",internal)
+ end
+ end
-local function adddestination(result,references) -- todo: specials -> exporters and then concat
- if references then
- local reference = references.reference
- if reference and reference ~= "" then
- local prefix = references.prefix
- if prefix and prefix ~= "" then
- result[#result+1] = formatters[" prefix='%s'"](prefix)
- end
- result[#result+1] = formatters[" destination='%s'"](reference)
- for i=1,#references do
- local r = references[i]
- local e = evaluators[r.kind]
- if e then
- e(result,r)
+ local function adddestination(di,references) -- todo: specials -> exporters and then concat
+ if references then
+ local reference = references.reference
+ if reference and reference ~= "" then
+ local prefix = references.prefix
+ if prefix and prefix ~= "" then
+ setattribute(di,"prefix",prefix,true)
+ end
+ setattribute(di,"destination",reference,true)
+ for i=1,#references do
+ local r = references[i]
+ local e = evaluators[r.kind]
+ if e then
+ e(di,r)
+ end
end
end
end
end
-end
-local function addreference(result,references)
- if references then
- local reference = references.reference
- if reference and reference ~= "" then
- local prefix = references.prefix
- if prefix and prefix ~= "" then
- result[#result+1] = formatters[" prefix='%s'"](prefix)
+ function extras.addimplicit(di,references)
+ if references then
+ local internal = references.internal
+ if internal then
+ setattribute(di,"implicit",internal)
end
- result[#result+1] = formatters[" reference='%s'"](reference)
end
- local internal = references.internal
- if internal and internal ~= "" then
- result[#result+1] = formatters[" location='aut:%s'"](internal)
+ end
+
+ function extras.addinternal(di,references)
+ if references then
+ local internal = references.internal
+ if internal then
+ setattribute(di,"internal",internal)
+ end
end
end
-end
-function extras.link(result,element,detail,n,fulltag,di)
- -- for instance in lists a link has nested elements and no own text
- local reference = referencehash[fulltag]
- if reference then
- adddestination(result,structures.references.get(reference))
- return true
- else
- local data = di.data
- if data then
- for i=1,#data do
- local di = data[i]
- if di then
- local fulltag = di.fulltag
- if fulltag and extras.link(result,element,detail,n,fulltag,di) then
- return true
+ local p_firstpart = lpeg.Cs((1-lpeg.P(","))^0)
+
+ local function addreference(di,references)
+ if references then
+ local reference = references.reference
+ if reference and reference ~= "" then
+ local prefix = references.prefix
+ if prefix and prefix ~= "" then
+ setattribute(di,"prefix",prefix)
+ end
+ setattribute(di,"reference",reference,true)
+ setattribute(di,"explicit",lpegmatch(p_firstpart,reference),true)
+ end
+ local internal = references.internal
+ if internal and internal ~= "" then
+ setattribute(di,"implicit",internal)
+ end
+ end
+ end
+
+ local function link(di,element,n,fulltag)
+ -- for instance in lists a link has nested elements and no own text
+ local reference = referencehash[fulltag]
+ if reference then
+ adddestination(di,structures.references.get(reference))
+ return true
+ else
+ local data = di.data
+ if data then
+ for i=1,#data do
+ local di = data[i]
+ if di then
+ local fulltag = di.fulltag
+ if fulltag and link(di,element,n,fulltag) then
+ return true
+ end
end
end
end
end
end
+
+ extras.adddestination = adddestination
+ extras.addreference = addreference
+
+ extras.link = link
+
end
-- no settings, as these are obscure ones
-local automathrows = true directives.register("backend.export.math.autorows", function(v) automathrows = v end)
-local automathapply = true directives.register("backend.export.math.autoapply", function(v) automathapply = v end)
-local automathnumber = true directives.register("backend.export.math.autonumber", function(v) automathnumber = v end)
-local automathstrip = true directives.register("backend.export.math.autostrip", function(v) automathstrip = v end)
-
-local functions = mathematics.categories.functions
-
-local function collapse(di,i,data,ndata,detail,element)
- local collapsing = di.data
- if data then
- di.element = element
- di.detail = nil
- i = i + 1
- while i <= ndata do
- local dn = data[i]
- if dn.detail == detail then
- collapsing[#collapsing+1] = dn.data[1]
- dn.skip = "ignore"
- i = i + 1
- else
- break
+do
+
+ local automathrows = true directives.register("export.math.autorows", function(v) automathrows = v end)
+ local automathapply = true directives.register("export.math.autoapply", function(v) automathapply = v end)
+ local automathnumber = true directives.register("export.math.autonumber", function(v) automathnumber = v end)
+ local automathstrip = true directives.register("export.math.autostrip", function(v) automathstrip = v end)
+
+ local functions = mathematics.categories.functions
+
+ local function collapse(di,i,data,ndata,detail,element)
+ local collapsing = di.data
+ if data then
+ di.element = element
+ di.detail = nil
+ i = i + 1
+ while i <= ndata do
+ local dn = data[i]
+ if dn.detail == detail then
+ collapsing[#collapsing+1] = dn.data[1]
+ dn.skip = "ignore"
+ i = i + 1
+ else
+ break
+ end
end
end
+ return i
end
- return i
-end
-local function collapse_mn(di,i,data,ndata)
- local collapsing = di.data
- if data then
- i = i + 1
- while i <= ndata do
- local dn = data[i]
- local tg = dn.tg
- if tg == "mn" then
- collapsing[#collapsing+1] = dn.data[1]
- dn.skip = "ignore"
- i = i + 1
- elseif tg == "mo" then
- local d = dn.data[1]
- if d == "." then
- collapsing[#collapsing+1] = d
+ local function collapse_mn(di,i,data,ndata)
+ -- this is tricky ... we need to make sure that we wrap in mrows if we want
+ -- to bypass this one
+ local collapsing = di.data
+ if data then
+ i = i + 1
+ while i <= ndata do
+ local dn = data[i]
+ local tg = dn.tg
+ if tg == "mn" then
+ collapsing[#collapsing+1] = dn.data[1]
dn.skip = "ignore"
i = i + 1
+ elseif tg == "mo" then
+ local d = dn.data[1]
+ if d == "." then
+ collapsing[#collapsing+1] = d
+ dn.skip = "ignore"
+ i = i + 1
+ else
+ break
+ end
else
break
end
- else
- break
end
end
+ return i
end
- return i
-end
--- maybe delay __i__ till we need it
+ -- maybe delay __i__ till we need it
-local apply_function = {
- {
- element = "mo",
- -- comment = "apply function",
- -- data = { utfchar(0x2061) },
- data = { "&#x2061;" },
- nature = "mixed",
+ local apply_function = {
+ {
+ element = "mo",
+ -- comment = "apply function",
+ -- data = { utfchar(0x2061) },
+ data = { "&#x2061;" },
+ nature = "mixed",
+ }
}
-}
-local functioncontent = { }
+ local functioncontent = { }
-setmetatableindex(functioncontent,function(t,k)
- local v = { { content = k } }
- t[k] = v
- return v
-end)
+ setmetatableindex(functioncontent,function(t,k)
+ local v = { { content = k } }
+ t[k] = v
+ return v
+ end)
+
+ local dummy_nucleus = {
+ element = "mtext",
+ data = { content = "" },
+ nature = "inline",
+ comment = "dummy nucleus",
+ fulltag = "mtext>0"
+ }
-local function checkmath(root) -- we can provide utf.toentities as an option
- local data = root.data
- if data then
- local ndata = #data
- local roottg = root.tg
- if roottg == "msubsup" then
- local nucleus, superscript, subscript
- for i=1,ndata do
- local di = data[i]
- if not di then
- -- weird
- elseif di.content then
- -- text
- elseif not nucleus then
- nucleus = i
- elseif not superscript then
- superscript = i
- elseif not subscript then
- subscript = i
- else
- -- error
+ local function accentchar(d)
+ for i=1,3 do
+ d = d.data
+ if not d then
+ return
+ end
+ d = d[1]
+ if not d then
+ return
+ end
+ local tg = d.tg
+ if tg == "mover" then
+ local s = specifications[d.fulltag]
+ local t = s.top
+ if t then
+ d = d.data[1]
+ local d1 = d.data[1]
+ d1.content = utfchar(t)
+ d.data = { d1 }
+ return d
+ end
+ elseif tg == "munder" then
+ local s = specifications[d.fulltag]
+ local b = s.bottom
+ if b then
+ d = d.data[1]
+ local d1 = d.data[1]
+ d1.content = utfchar(b)
+ d.data = { d1 }
+ return d
end
end
- if superscript and subscript then
- local sup, sub = data[superscript], data[subscript]
- data[superscript], data[subscript] = sub, sup
- -- sub.__o__, sup.__o__ = subscript, superscript
- sub.__i__, sup.__i__ = superscript, subscript
- end
- elseif roottg == "mfenced" then
- local new, n = { }, 0
- local attributes = { }
- root.attributes = attributes
- for i=1,ndata do
- local di = data[i]
- if not di then
- -- weird
- elseif di.content then
- n = n + 1
- new[n] = di
- else
- local tg = di.tg
- if tg == "mleft" then
- attributes.left = tostring(di.data[1].data[1].content)
- elseif tg == "mmiddle" then
- attributes.middle = tostring(di.data[1].data[1].content)
- elseif tg == "mright" then
- attributes.right = tostring(di.data[1].data[1].content)
+ end
+ end
+
+ local no_mrow = {
+ mrow = true,
+ mfenced = true,
+ mfrac = true,
+ mroot = true,
+ msqrt = true,
+ mi = true,
+ mo = true,
+ mn = true,
+ }
+
+ local function checkmath(root) -- we can provide utf.toentities as an option
+ local data = root.data
+ if data then
+ local ndata = #data
+ local roottg = root.tg
+ if roottg == "msubsup" then
+ local nucleus, superscript, subscript
+ for i=1,ndata do
+ local di = data[i]
+ if not di then
+ -- weird
+ elseif di.content then
+ -- text
+ elseif not nucleus then
+ nucleus = i
+ elseif not superscript then
+ superscript = i
+ elseif not subscript then
+ subscript = i
else
- n = n + 1
- di.__i__ = n
- new[n] = di
+ -- error
end
end
+ if superscript and subscript then
+ local sup, sub = data[superscript], data[subscript]
+ data[superscript], data[subscript] = sub, sup
+ -- sub.__o__, sup.__o__ = subscript, superscript
+ sub.__i__, sup.__i__ = superscript, subscript
+ end
+-- elseif roottg == "msup" or roottg == "msub" then
+-- -- m$^2$
+-- if ndata == 1 then
+-- local d = data[1]
+-- data[2] = d
+-- d.__i__ = 2
+-- data[1] = dummy_nucleus
+-- end
+ elseif roottg == "mfenced" then
+ local s = specifications[root.fulltag]
+ local l, m, r = s.left, s.middle, s.right
+ if l then
+ l = utfchar(l)
+ end
+ if m then
+ local t = { }
+ for i=1,#m do
+ t[i] = utfchar(m[i])
+ end
+ m = concat(t)
+ end
+ if r then
+ r = utfchar(r)
+ end
+ root.attributes = {
+ open = l,
+ separators = m,
+ close = r,
+ }
end
- root.data = new
- ndata = n
- end
- if ndata == 0 then
- return
- elseif ndata == 1 then
- local d = data[1]
- if not d then
+ if ndata == 0 then
return
- elseif d.content then
- return
- elseif #root.data == 1 then
- local tg = d.tg
- if automathrows and roottg == "mrow" then
- -- maybe just always ! check spec first
- if tg == "mrow" or tg == "mfenced" or tg == "mfrac" or tg == "mroot" or tg == "msqrt"then
- root.skip = "comment"
- elseif tg == "mo" then
- root.skip = "comment"
- end
- elseif roottg == "mo" then
- if tg == "mo" then
- root.skip = "comment"
+ elseif ndata == 1 then
+ local d = data[1]
+ if not d then
+ return
+ elseif d.content then
+ return
+ elseif #root.data == 1 then
+ local tg = d.tg
+ if automathrows and roottg == "mrow" then
+ -- maybe just always ! check spec first
+ if no_mrow[tg] then
+ root.skip = "comment"
+ end
+ elseif roottg == "mo" then
+ if tg == "mo" then
+ root.skip = "comment"
+ end
end
end
end
- end
- local i = 1
- while i <= ndata do -- -- -- TOO MUCH NESTED CHECKING -- -- --
- local di = data[i]
- if di and not di.content then
- local tg = di.tg
- local detail = di.detail
- if tg == "math" then
- -- di.element = "mrow" -- when properties
- di.skip = "comment"
- checkmath(di)
- i = i + 1
- elseif tg == "mover" or tg == "munder" or tg == "munderover" then
- if detail == "accent" then
- di.attributes = { accent = "true" }
- di.detail = nil
- end
- checkmath(di)
- i = i + 1
- elseif tg == "mroot" then
- if #di.data == 1 then
- -- else firefox complains
- di.element = "msqrt"
- end
- checkmath(di)
- i = i + 1
- elseif tg == "break" then
- di.skip = "comment"
- i = i + 1
- elseif tg == "mrow" and detail then
- di.detail = nil
- checkmath(di)
- di = {
- element = "maction",
- nature = "display",
- attributes = { actiontype = detail },
- data = { di },
- n = 0,
- }
- data[i] = di
- i = i + 1
- elseif detail then
- -- no checkmath(di) here
- local category = tonumber(detail) or 0
- if category == 1 then -- mo
- i = collapse(di,i,data,ndata,detail,"mo")
- elseif category == 2 then -- mi
- i = collapse(di,i,data,ndata,detail,"mi")
- elseif category == 3 then -- mn
- i = collapse(di,i,data,ndata,detail,"mn")
- elseif category == 4 then -- ms
- i = collapse(di,i,data,ndata,detail,"ms")
- elseif category >= 1000 then
- local apply = category >= 2000
- if apply then
- category = category - 1000
+ local i = 1
+ while i <= ndata do -- -- -- TOO MUCH NESTED CHECKING -- -- --
+ local di = data[i]
+ if di and not di.content then
+ local tg = di.tg
+ if tg == "math" then
+ -- di.element = "mrow" -- when properties
+ di.skip = "comment"
+ checkmath(di)
+ i = i + 1
+ elseif tg == "mover" then
+ local s = specifications[di.fulltag]
+ if s.accent then
+ local t = s.top
+ local d = di.data
+ -- todo: accent = "false" (for scripts like limits)
+ di.attributes = {
+ accent = "true",
+ }
+ -- todo: p.topfixed
+ if t then
+ -- mover
+ d[1].data[1].content = utfchar(t)
+ di.data = { d[2], d[1] }
+ end
+ else
+ -- can't happen
end
- if tg == "mi" then -- function
- if roottg == "mrow" then
- root.skip = "comment"
- root.element = "function"
+ checkmath(di)
+ i = i + 1
+ elseif tg == "munder" then
+ local s = specifications[di.fulltag]
+ if s.accent then
+ local b = s.bottom
+ local d = di.data
+ -- todo: accent = "false" (for scripts like limits)
+ di.attributes = {
+ accent = "true",
+ }
+ -- todo: p.bottomfixed
+ if b then
+ -- munder
+ d[2].data[1].content = utfchar(b)
end
- i = collapse(di,i,data,ndata,detail,"mi")
- local tag = functions[category]
- if tag then
- di.data = functioncontent[tag]
+ else
+ -- can't happen
+ end
+ checkmath(di)
+ i = i + 1
+ elseif tg == "munderover" then
+ local s = specifications[di.fulltag]
+ if s.accent then
+ local t = s.top
+ local b = s.bottom
+ local d = di.data
+ -- todo: accent = "false" (for scripts like limits)
+ -- todo: accentunder = "false" (for scripts like limits)
+ di.attributes = {
+ accent = "true",
+ accentunder = "true",
+ }
+ -- todo: p.topfixed
+ -- todo: p.bottomfixed
+ if t and b then
+ -- munderover
+ d[1].data[1].content = utfchar(t)
+ d[3].data[1].content = utfchar(b)
+ di.data = { d[2], d[3], d[1] }
+ else
+ -- can't happen
end
- if apply then
- di.after = apply_function
- elseif automathapply then -- make function
- local following
- if i <= ndata then
- -- normally not the case
- following = data[i]
+ else
+ -- can't happen
+ end
+ checkmath(di)
+ i = i + 1
+ elseif tg == "mstacker" then
+ local d = di.data
+ local d1 = d[1]
+ local d2 = d[2]
+ local d3 = d[3]
+ local t1 = d1 and d1.tg
+ local t2 = d2 and d2.tg
+ local t3 = d3 and d3.tg
+ local m = nil -- d1.data[1]
+ local t = nil
+ local b = nil
+ -- only accent when top / bot have stretch
+ -- normally we flush [base under over] which is better for tagged pdf
+ if t1 == "mstackermid" then
+ m = accentchar(d1) -- or m
+ if t2 == "mstackertop" then
+ if t3 == "mstackerbot" then
+ t = accentchar(d2)
+ b = accentchar(d3)
+ di.element = "munderover"
+ di.data = { m or d1.data[1], b or d3.data[1], t or d2.data[1] }
else
- local parent = di.__p__ -- == root
- if parent.tg == "mrow" then
- parent = parent.__p__
- end
- local index = parent.__i__
- following = parent.data[index+1]
+ t = accentchar(d2)
+ di.element = "mover"
+ di.data = { m or d1.data[1], t or d2.data[1] }
+ end
+ elseif t2 == "mstackerbot" then
+ if t3 == "mstackertop" then
+ b = accentchar(d2)
+ t = accentchar(d3)
+ di.element = "munderover"
+ di.data = { m or d1.data[1], t or d3.data[1], m, b or d2.data[1] }
+ else
+ b = accentchar(d2)
+ di.element = "munder"
+ di.data = { m or d1.data[1], b or d2.data[1] }
end
- if following then
- local tg = following.tg
- if tg == "mrow" or tg == "mfenced" then -- we need to figure out the right condition
+ else
+ -- can't happen
+ end
+ else
+ -- can't happen
+ end
+ if t or b then
+ di.attributes = {
+ accent = t and "true" or nil,
+ accentunder = b and "true" or nil,
+ }
+ di.detail = nil
+ end
+ checkmath(di)
+ i = i + 1
+ elseif tg == "mroot" then
+ local data = di.data
+ local size = #data
+ if size == 1 then
+ -- else firefox complains ... code in math-tag (for pdf tagging)
+ di.element = "msqrt"
+ elseif size == 2 then
+ data[1], data[2] = data[2], data[1]
+ end
+ checkmath(di)
+ i = i + 1
+ elseif tg == "break" then
+ di.skip = "comment"
+ i = i + 1
+ elseif tg == "mtext" then
+ -- this is only needed for unboxed mtexts ... all kind of special
+ -- tex border cases and optimizations ... trial and error
+ local data = di.data
+ if #data > 1 then
+ for i=1,#data do
+ local di = data[i]
+ local content = di.content
+ if content then
+ data[i] = {
+ element = "mtext",
+ nature = "inline",
+ data = { di },
+ n = 0,
+ }
+ elseif di.tg == "math" then
+ local di = di.data[1]
+ data[i] = di
+ checkmath(di)
+ end
+ end
+ di.element = "mrow"
+ -- di.tg = "mrow"
+ -- di.nature = "inline"
+ end
+ checkmath(di)
+ i = i + 1
+ elseif tg == "mrow" and detail then -- hm, falls through
+ di.detail = nil
+ checkmath(di)
+ di = {
+ element = "maction",
+ nature = "display",
+ attributes = { actiontype = detail },
+ data = { di },
+ n = 0,
+ }
+ data[i] = di
+ i = i + 1
+ else
+ local category = di.mathcategory
+ if category then
+ -- no checkmath(di) here
+ if category == 1 then -- mo
+ i = collapse(di,i,data,ndata,detail,"mo")
+ elseif category == 2 then -- mi
+ i = collapse(di,i,data,ndata,detail,"mi")
+ elseif category == 3 then -- mn
+ i = collapse(di,i,data,ndata,detail,"mn")
+ elseif category == 4 then -- ms
+ i = collapse(di,i,data,ndata,detail,"ms")
+ elseif category >= 1000 then
+ local apply = category >= 2000
+ if apply then
+ category = category - 1000
+ end
+ if tg == "mi" then -- function
+ if roottg == "mrow" then
+ root.skip = "comment"
+ root.element = "function"
+ end
+ i = collapse(di,i,data,ndata,detail,"mi")
+ local tag = functions[category]
+ if tag then
+ di.data = functioncontent[tag]
+ end
+ if apply then
di.after = apply_function
+ elseif automathapply then -- make function
+ local following
+ if i <= ndata then
+ -- normally not the case
+ following = data[i]
+ else
+ local parent = di.__p__ -- == root
+ if parent.tg == "mrow" then
+ parent = parent.__p__
+ end
+ local index = parent.__i__
+ following = parent.data[index+1]
+ end
+ if following then
+ local tg = following.tg
+ if tg == "mrow" or tg == "mfenced" then -- we need to figure out the right condition
+ di.after = apply_function
+ end
+ end
end
+ else -- some problem
+ checkmath(di)
+ i = i + 1
end
+ else
+ checkmath(di)
+ i = i + 1
end
- else -- some problem
+ elseif automathnumber and tg == "mn" then
+ checkmath(di)
+ i = collapse_mn(di,i,data,ndata)
+ else
checkmath(di)
i = i + 1
end
- else
- checkmath(di)
- i = i + 1
end
- elseif automathnumber and tg == "mn" then
- checkmath(di)
- i = collapse_mn(di,i,data,ndata)
- else
- checkmath(di)
+ else -- can be string or boolean
+ if parenttg ~= "mtext" and di == " " then
+ data[i] = false
+ end
i = i + 1
end
- else -- can be string or boolean
- if parenttg ~= "mtext" and di == " " then
- data[i] = false
- end
- i = i + 1
end
end
end
-end
-function stripmath(di)
- if not di then
- --
- elseif di.content then
- return di
- else
- local tg = di.tg
- if tg == "mtext" or tg == "ms" then
+ local function stripmath(di)
+ if not di then
+ --
+ elseif di.content then
return di
else
- local data = di.data
- local ndata = #data
- local n = 0
- for i=1,ndata do
- local di = data[i]
- if di and not di.content then
- di = stripmath(di)
- end
- if di then
- local content = di.content
- if not content then
- n = n + 1
- di.__i__ = n
- data[n] = di
- elseif content == " " or content == "" then
- -- skip
- else
- n = n + 1
- data[n] = di
+ local tg = di.tg
+ if tg == "mtext" or tg == "ms" then
+ return di
+ else
+ local data = di.data
+ local ndata = #data
+ local n = 0
+ for i=1,ndata do
+ local d = data[i]
+ if d and not d.content then
+ d = stripmath(d)
+ end
+ if d then
+ local content = d.content
+ if not content then
+ n = n + 1
+ d.__i__ = n
+ data[n] = d
+ elseif content == " " or content == "" then
+ if di.tg == "mspace" then
+ -- we append or prepend a space to a preceding or following mtext
+ local parent = di.__p__
+ local index = di.__i__ -- == i
+ local data = parent.data
+ if index > 1 then
+ local d = data[index-1]
+ if d.tg == "mtext" then
+ local dd = d.data
+ local dn = dd[#dd]
+ local dc = dn.content
+ if dc then
+ dn.content = dc .. content
+ end
+ end
+ elseif index < ndata then
+ local d = data[index+1]
+ if d.tg == "mtext" then
+ local dd = d.data
+ local dn = dd[1]
+ local dc = dn.content
+ if dc then
+ dn.content = content .. dc
+ end
+ end
+ end
+ end
+ else
+ n = n + 1
+ data[n] = d
+ end
end
end
+ for i=ndata,n+1,-1 do
+ data[i] = nil
+ end
+ if #data > 0 then
+ return di
+ end
+-- end
end
- for i=ndata,n+1,-1 do
- data[i] = nil
- end
- if #data > 0 then
- return di
- end
+ -- could be integrated but is messy then
+-- while roottg == "mrow" and #data == 1 do
+-- data = data[1]
+-- for k, v in next, data do
+-- root[k] = v
+-- end
+-- roottg = data.tg
+-- end
+ end
+ end
+
+ function checks.math(di)
+ local specification = specifications[di.fulltag]
+ local mode = specification and specification.mode == "display" and "block" or "inline"
+ di.attributes = {
+ ["display"] = mode,
+ ["xmlns:m"] = mathmlns,
+ }
+ -- can be option if needed:
+ if mode == "inline" then
+ -- di.nature = "mixed" -- else spacing problem (maybe inline)
+ di.nature = "inline" -- we need to catch x$X$x and x $X$ x
+ else
+ di.nature = "display"
end
+ if automathstrip then
+ stripmath(di)
+ end
+ checkmath(di)
end
-end
-
-function checks.math(di)
- local hash = attributehash[di.fulltag]
- local mode = (hash and hash.mode) == "display" and "block" or "inline"
- di.attributes = {
- display = mode
- }
- -- can be option if needed:
- if mode == "inline" then
- di.nature = "mixed" -- else spacing problem (maybe inline)
- else
- di.nature = "display"
- end
- if automathstrip then
- stripmath(di)
- end
- checkmath(di)
-end
-local a, z, A, Z = 0x61, 0x7A, 0x41, 0x5A
+ local a, z, A, Z = 0x61, 0x7A, 0x41, 0x5A
-function extras.mi(result,element,detail,n,fulltag,di) -- check with content
- local str = di.data[1].content
- if str and sub(str,1,1) ~= "&" then -- hack but good enough (maybe gsub op eerste)
- for v in utfvalues(str) do
- if (v >= a and v <= z) or (v >= A and v <= Z) then
- local a = di.attributes
- if a then
- a.mathvariant = "normal"
- else
- di.attributes = { mathvariant = "normal" }
+ function extras.mi(di,element,n,fulltag) -- check with content
+ local str = di.data[1].content
+ if str and sub(str,1,1) ~= "&" then -- hack but good enough (maybe gsub op eerste)
+ for v in utfvalues(str) do
+ if (v >= a and v <= z) or (v >= A and v <= Z) then
+ local a = di.attributes
+ if a then
+ a.mathvariant = "normal"
+ else
+ di.attributes = { mathvariant = "normal" }
+ end
end
end
end
end
-end
-function extras.section(result,element,detail,n,fulltag,di)
- local data = listdata[fulltag]
- if data then
- addreference(result,data.references)
- return true
- else
+ function extras.msub(di,element,n,fulltag)
+ -- m$^2$
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(result,element,detail,n,ft,di) then
- return true
- end
- end
- end
+ if #data == 1 then
+ local d = data[1]
+ data[2] = d
+ d.__i__ = 2
+ data[1] = dummy_nucleus
end
end
+
+ extras.msup = extras.msub
+
end
-function extras.float(result,element,detail,n,fulltag,di)
- local data = listdata[fulltag]
- if data then
- addreference(result,data.references)
- return true
- else
- local data = di.data
+do
+
+ local registered = structures.sections.registered
+
+ local function resolve(di,element,n,fulltag)
+ local data = listdata[fulltag]
if data then
- for i=1,#data do
- local di = data[i]
- if di and extras.section(result,element,detail,n,di.fulltag,di) then
- return true
+ extras.addreference(di,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 resolve(di,element,n,ft) then
+ return true
+ end
+ end
end
end
end
end
-end
-local tabledata = { }
-
-function structurestags.settablecell(rows,columns,align)
- if align > 0 or rows > 1 or columns > 1 then
- tabledata[detailedtag("tablecell")] = {
- rows = rows,
- columns = columns,
- align = align,
- }
+ function extras.section(di,element,n,fulltag)
+ local r = registered[specifications[fulltag].detail]
+ if r then
+ setattribute(di,"level",r.level)
+ end
+ resolve(di,element,n,fulltag)
end
-end
-function extras.tablecell(result,element,detail,n,fulltag,di)
- local hash = tabledata[fulltag]
- if hash then
- local v = hash.columns
- if v and v > 1 then
- result[#result+1] = formatters[" columns='%s'"](v)
- end
- local v = hash.rows
- if v and v > 1 then
- result[#result+1] = formatters[" rows='%s'"](v)
+ extras.float = resolve
+
+ -- todo: internal is already hashed
+
+ function structurestags.setlist(n)
+ local data = structures.lists.getresult(n)
+ if data then
+ referencehash[locatedtag("listitem")] = data
end
- local v = hash.align
- if not v or v == 0 then
- -- normal
- elseif v == 1 then -- use numbertoalign here
- result[#result+1] = " align='flushright'"
- elseif v == 2 then
- result[#result+1] = " align='middle'"
- elseif v == 3 then
- result[#result+1] = " align='flushleft'"
+ end
+
+ function extras.listitem(di,element,n,fulltag)
+ local data = referencehash[fulltag]
+ if data then
+ extras.addinternal(di,data.references)
+ return true
end
end
+
end
-local tabulatedata = { }
+do
-function structurestags.settabulatecell(align)
- if align > 0 then
- tabulatedata[detailedtag("tabulatecell")] = {
- align = align,
- }
- end
-end
+ -- todo: internal is already hashed
-function extras.tabulate(result,element,detail,n,fulltag,di)
- local data = di.data
- for i=1,#data do
- local di = data[i]
- if di.tg == "tabulaterow" then
- local did = di.data
- local content = false
- for i=1,#did do
- local d = did[i].data
- if d and #d > 0 and d[1].content then
- content = true
- break
- end
- end
- if not content then
- di.element = "" -- or simply remove
- end
+ function structurestags.setregister(tag,n) -- check if tag is needed
+ local data = structures.registers.get(tag,n)
+ if data then
+ referencehash[locatedtag("registerlocation")] = data
end
end
-end
-function extras.tabulatecell(result,element,detail,n,fulltag,di)
- local hash = tabulatedata[fulltag]
- if hash then
- local v = hash.align
- if not v or v == 0 then
- -- normal
- elseif v == 1 then
- result[#result+1] = " align='flushleft'"
- elseif v == 2 then
- result[#result+1] = " align='flushright'"
- elseif v == 3 then
- result[#result+1] = " align='middle'"
+ function extras.registerlocation(di,element,n,fulltag)
+ local data = referencehash[fulltag]
+ if data then
+ extras.addinternal(di,data.references)
+ return true
end
end
-end
--- flusher
+ extras.registerpages = ignorebreaks
+ extras.registerseparator = ignorespaces
-local linedone = false -- can go ... we strip newlines anyway
-local inlinedepth = 0
+end
--- todo: #result -> nofresult
+do
-local function emptytag(result,element,nature,depth,di) -- currently only break but at some point
- local a = di.attributes -- we might add detail etc
- if a then -- happens seldom
- if linedone then
- result[#result+1] = formatters["%w<%s"](depth,namespaced[element])
- else
- result[#result+1] = formatters["\n%w<%s"](depth,namespaced[element])
- end
- for k, v in next, a do
- result[#result+1] = formatters[" %s=%q"](k,v)
- end
- result[#result+1] = "/>\n"
- else
- if linedone then
- result[#result+1] = formatters["%w<%s/>\n"](depth,namespaced[element])
- else
- result[#result+1] = formatters["\n%w<%s/>\n"](depth,namespaced[element])
+ local tabledata = { }
+
+ local function hascontent(data)
+ for i=1,#data do
+ local di = data[i]
+ if not di then
+ --
+ elseif di.content then
+ return true
+ else
+ local d = di.data
+ if d and #d > 0 and hascontent(d) then
+ return true
+ end
+ end
end
end
- linedone = false
-end
-local function begintag(result,element,nature,depth,di,skip)
- -- if needed we can use a local result with xresult
- local detail = di.detail
- local n = di.n
- local fulltag = di.fulltag
- local comment = di.comment
- if nature == "inline" then
- linedone = false
- inlinedepth = inlinedepth + 1
- if show_comment and comment then
- result[#result+1] = formatters["<!-- %s -->"](comment)
- end
- elseif nature == "mixed" then
- if inlinedepth > 0 then
- if show_comment and comment then
- result[#result+1] = formatters["<!-- %s -->"](comment)
- end
- elseif linedone then
- result[#result+1] = spaces[depth]
- if show_comment and comment then
- result[#result+1] = formatters["<!-- %s -->"](comment)
- end
- else
- result[#result+1] = formatters["\n%w"](depth)
- linedone = false
- if show_comment and comment then
- result[#result+1] = formatters["<!-- %s -->\n%w"](comment,depth)
- end
+ function structurestags.settablecell(rows,columns,align)
+ if align > 0 or rows > 1 or columns > 1 then
+ tabledata[locatedtag("tablecell")] = {
+ rows = rows,
+ columns = columns,
+ align = align,
+ }
end
- inlinedepth = inlinedepth + 1
- else
- if inlinedepth > 0 then
- if show_comment and comment then
- result[#result+1] = formatters["<!-- %s -->"](comment)
+ end
+
+ function extras.tablecell(di,element,n,fulltag)
+ local hash = tabledata[fulltag]
+ if hash then
+ local columns = hash.columns
+ if columns and columns > 1 then
+ setattribute(di,"columns",columns)
end
- elseif linedone then
- result[#result+1] = spaces[depth]
- if show_comment and comment then
- result[#result+1] = formatters["<!-- %s -->"](comment)
+ local rows = hash.rows
+ if rows and rows > 1 then
+ setattribute(di,"rows",rows)
end
- else
- result[#result+1] = formatters["\n%w"](depth) -- can introduced extra line in mixed+mixed (filtered later on)
- linedone = false
- if show_comment and comment then
- result[#result+1] = formatters["<!-- %s -->\n%w"](comment,depth)
+ local align = hash.align
+ if not align or align == 0 then
+ -- normal
+ elseif align == 1 then -- use numbertoalign here
+ setattribute(di,"align","flushright")
+ elseif align == 2 then
+ setattribute(di,"align","middle")
+ elseif align == 3 then
+ setattribute(di,"align","flushleft")
end
end
end
- if skip == "comment" then
- if show_comment then
- result[#result+1] = formatters["<!-- begin %s -->"](namespaced[element])
- end
- elseif skip then
- -- ignore
- else
- result[#result+1] = formatters["<%s"](namespaced[element])
- if detail then
- result[#result+1] = formatters[" detail=%q"](detail)
- end
- if indexing and n then
- result[#result+1] = formatters[" n=%q"](n)
- end
- local extra = extras[element]
- if extra then
- extra(result,element,detail,n,fulltag,di)
+
+ local tabulatedata = { }
+
+ function structurestags.settabulatecell(align)
+ if align > 0 then
+ tabulatedata[locatedtag("tabulatecell")] = {
+ align = align,
+ }
end
- local u = userdata[fulltag]
- if u then
- for k, v in next, u do
- result[#result+1] = formatters[" %s=%q"](k,v)
+ end
+
+ function extras.tabulate(di,element,n,fulltag)
+ local data = di.data
+ for i=1,#data do
+ local di = data[i]
+ if di.tg == "tabulaterow" and not hascontent(di.data) then
+ di.element = "" -- or simply remove
end
end
- local a = di.attributes
- if a then
- for k, v in next, a do
- result[#result+1] = formatters[" %s=%q"](k,v)
+ end
+
+ function extras.tabulatecell(di,element,n,fulltag)
+ local hash = tabulatedata[fulltag]
+ if hash then
+ local align = hash.align
+ if not align or align == 0 then
+ -- normal
+ elseif align == 1 then
+ setattribute(di,"align","flushleft")
+ elseif align == 2 then
+ setattribute(di,"align","flushright")
+ elseif align == 3 then
+ setattribute(di,"align","middle")
end
end
- result[#result+1] = ">"
end
- if inlinedepth > 0 then
- elseif nature == "display" then
- result[#result+1] = "\n"
- linedone = true
- end
- used[element][detail or ""] = nature -- for template css
- local metadata = tagmetadata[fulltag]
- if metadata then
- if not linedone then
- result[#result+1] = "\n"
- linedone = true
- end
- result[#result+1] = formatters["%w<metadata>\n"](depth)
- for k, v in table.sortedpairs(metadata) do
- v = entityremapper(v)
- result[#result+1] = formatters["%w<metavariable name=%q>%s</metavariable>\n"](depth+1,k,v)
+
+end
+
+-- flusher
+
+do
+
+ local f_detail = formatters[' detail="%s"']
+ local f_chain = formatters[' chain="%s"']
+ local f_index = formatters[' n="%s"']
+ local f_spacing = formatters['<c n="%s">%s</c>']
+
+ local f_empty_inline = formatters["<%s/>"]
+ local f_empty_mixed = formatters["%w<%s/>\n"]
+ local f_empty_display = formatters["\n%w<%s/>\n"]
+ local f_empty_inline_attr = formatters["<%s%s/>"]
+ local f_empty_mixed_attr = formatters["%w<%s%s/>"]
+ local f_empty_display_attr = formatters["\n%w<%s%s/>\n"]
+
+ local f_begin_inline = formatters["<%s>"]
+ local f_begin_mixed = formatters["%w<%s>"]
+ local f_begin_display = formatters["\n%w<%s>\n"]
+ local f_begin_inline_attr = formatters["<%s%s>"]
+ local f_begin_mixed_attr = formatters["%w<%s%s>"]
+ local f_begin_display_attr = formatters["\n%w<%s%s>\n"]
+
+ local f_end_inline = formatters["</%s>"]
+ local f_end_mixed = formatters["</%s>\n"]
+ local f_end_display = formatters["%w</%s>\n"]
+
+ local f_begin_inline_comment = formatters["<!-- %s --><%s>"]
+ local f_begin_mixed_comment = formatters["%w<!-- %s --><%s>"]
+ local f_begin_display_comment = formatters["\n%w<!-- %s -->\n%w<%s>\n"]
+ local f_begin_inline_attr_comment = formatters["<!-- %s --><%s%s>"]
+ local f_begin_mixed_attr_comment = formatters["%w<!-- %s --><%s%s>"]
+ local f_begin_display_attr_comment = formatters["\n%w<!-- %s -->\n%w<%s%s>\n"]
+
+ local f_comment_begin_inline = formatters["<!-- begin %s -->"]
+ local f_comment_begin_mixed = formatters["%w<!-- begin %s -->"]
+ local f_comment_begin_display = formatters["\n%w<!-- begin %s -->\n"]
+
+ local f_comment_end_inline = formatters["<!-- end %s -->"]
+ local f_comment_end_mixed = formatters["<!-- end %s -->\n"]
+ local f_comment_end_display = formatters["%w<!-- end %s -->\n"]
+
+ local f_metadata_begin = formatters["\n%w<metadata>\n"]
+ local f_metadata = formatters["%w<metavariable name=%q>%s</metavariable>\n"]
+ local f_metadata_end = formatters["%w</metadata>\n"]
+
+ --- we could share the r tables ... but it's fast enough anyway
+
+ local function attributes(a)
+ local r = { } -- can be shared
+ local n = 0
+ for k, v in next, a do
+ n = n + 1
+ r[n] = f_attribute(k,v) -- lpegmatch(p_escaped,v)
end
- result[#result+1] = formatters["%w</metadata>\n"](depth)
+ return concat(r,"",1,n)
end
-end
-local function endtag(result,element,nature,depth,skip)
- if nature == "display" then
- if inlinedepth == 0 then
- if not linedone then
- result[#result+1] = "\n"
- end
- if skip == "comment" then
- if show_comment then
- result[#result+1] = formatters["%w<!-- end %s -->\n"](depth,namespaced[element])
- end
- elseif skip then
- -- ignore
+ local depth = 0
+ local inline = 0
+
+ local function bpar(result)
+ result[#result+1] = "\n<p>"
+ end
+ local function epar(result)
+ result[#result+1] = "</p>\n"
+ end
+
+ local function emptytag(result,embedded,element,nature,di) -- currently only break but at some point
+ local a = di.attributes -- we might add detail etc
+ if a then -- happens seldom
+ if nature == "display" then
+ result[#result+1] = f_empty_display_attr(depth,namespaced[element],attributes(a))
+ elseif nature == "mixed" then
+ result[#result+1] = f_empty_mixed_attr(depth,namespaced[element],attributes(a))
else
- result[#result+1] = formatters["%w</%s>\n"](depth,namespaced[element])
+ result[#result+1] = f_empty_inline_attr(namespaced[element],attributes(a))
end
- linedone = true
else
- if skip == "comment" then
- if show_comment then
- result[#result+1] = formatters["<!-- end %s -->"](namespaced[element])
- end
- elseif skip then
- -- ignore
+ if nature == "display" then
+ result[#result+1] = f_empty_display(depth,namespaced[element])
+ elseif nature == "mixed" then
+ result[#result+1] = f_empty_mixed(depth,namespaced[element])
else
- result[#result+1] = formatters["</%s>"](namespaced[element])
+ result[#result+1] = f_empty_inline(namespaced[element])
end
end
- else
- inlinedepth = inlinedepth - 1
+ end
+
+ local function begintag(result,embedded,element,nature,di,skip)
+ local index = di.n
+ local fulltag = di.fulltag
+ local specification = specifications[fulltag] or { } -- we can have a dummy
+ local comment = di.comment
+ local detail = specification.detail
if skip == "comment" then
if show_comment then
- result[#result+1] = formatters["<!-- end %s -->"](namespaced[element])
+ if nature == "inline" or inline > 0 then
+ result[#result+1] = f_comment_begin_inline(namespaced[element])
+ inline = inline + 1
+ elseif nature == "mixed" then
+ result[#result+1] = f_comment_begin_mixed(depth,namespaced[element])
+ depth = depth + 1
+ inline = 1
+ else
+ result[#result+1] = f_comment_begin_display(depth,namespaced[element])
+ depth = depth + 1
+ end
end
elseif skip then
-- ignore
else
- result[#result+1] = formatters["</%s>"](namespaced[element])
- end
- linedone = false
- end
-end
-local function flushtree(result,data,nature,depth)
- depth = depth + 1
- local nofdata = #data
- for i=1,nofdata do
- local di = data[i]
- if not di then -- hm, di can be string
- -- whatever
- elseif di.content then
- -- already has breaks
- local content = entityremapper(di.content)
- if i == nofdata and sub(content,-1) == "\n" then -- move check
- -- can be an end of line in par but can also be the last line
- if trace_spacing then
- result[#result+1] = formatters["<c n='%s'>%s</c>"](di.parnumber or 0,sub(content,1,-2))
+ -- if embedded then
+ -- if element == "math" then
+ -- embedded[f_tagid(element,index)] = #result+1
+ -- end
+ -- end
+
+ local n = 0
+ local r = { } -- delay this
+ if detail then
+ detail = gsub(detail,"[^A-Za-z0-9]+","-")
+ specification.detail = detail -- we use it later in for the div
+ n = n + 1
+ r[n] = f_detail(detail)
+ end
+ local parents = specification.parents
+ if parents then
+ parents = gsub(parents,"[^A-Za-z0-9 ]+","-")
+ specification.parents = parents -- we use it later in for the div
+ n = n + 1
+ r[n] = f_chain(parents)
+ end
+ if indexing and index then
+ n = n + 1
+ r[n] = f_index(index)
+ end
+ local extra = extras[element]
+ if extra then
+ extra(di,element,index,fulltag)
+ end
+ if exportproperties then
+ local p = specification.userdata
+ if not p then
+ -- skip
+ elseif exportproperties == v_yes then
+ for k, v in next, p do
+ n = n + 1
+ r[n] = f_attribute(k,v)
+ end
else
- result[#result+1] = sub(content,1,-2)
+ for k, v in next, p do
+ n = n + 1
+ r[n] = f_property(exportproperties,k,v)
+ end
end
- result[#result+1] = " "
- else
- if trace_spacing then
- result[#result+1] = formatters["<c n='%s'>%s</c>"](di.parnumber or 0,content)
- else
- result[#result+1] = content
+ end
+ local a = di.attributes
+ if a then
+ for k, v in next, a do
+ n = n + 1
+ r[n] = f_attribute(k,v)
end
end
- linedone = false
- elseif not di.collapsed then -- ignore collapsed data (is appended, reconstructed par)
- local element = di.element
- if not element then
- -- skip
- elseif element == "break" then -- or element == "pagebreak"
- emptytag(result,element,nature,depth,di)
- elseif element == "" or di.skip == "ignore" then
- -- skip
- else
- if di.before then
- flushtree(result,di.before,nature,depth)
+ if n == 0 then
+ if nature == "inline" or inline > 0 then
+ if show_comment and comment then
+ result[#result+1] = f_begin_inline_comment(comment,namespaced[element])
+ else
+ result[#result+1] = f_begin_inline(namespaced[element])
+ end
+ inline = inline + 1
+ elseif nature == "mixed" then
+ if show_comment and comment then
+ result[#result+1] = f_begin_mixed_comment(depth,comment,namespaced[element])
+ else
+ result[#result+1] = f_begin_mixed(depth,namespaced[element])
+ end
+ depth = depth + 1
+ inline = 1
+ else
+ if show_comment and comment then
+ result[#result+1] = f_begin_display_comment(depth,comment,depth,namespaced[element])
+ else
+ result[#result+1] = f_begin_display(depth,namespaced[element])
+ end
+ depth = depth + 1
end
- local natu = di.nature
- local skip = di.skip
- if di.breaknode then
- emptytag(result,"break","display",depth,di)
+ else
+ r = concat(r,"",1,n)
+ if nature == "inline" or inline > 0 then
+ if show_comment and comment then
+ result[#result+1] = f_begin_inline_attr_comment(comment,namespaced[element],r)
+ else
+ result[#result+1] = f_begin_inline_attr(namespaced[element],r)
+ end
+ inline = inline + 1
+ elseif nature == "mixed" then
+ if show_comment and comment then
+ result[#result+1] = f_begin_mixed_attr_comment(depth,comment,namespaced[element],r)
+ else
+ result[#result+1] = f_begin_mixed_attr(depth,namespaced[element],r)
+ end
+ depth = depth + 1
+ inline = 1
+ else
+ if show_comment and comment then
+ result[#result+1] = f_begin_display_attr_comment(depth,comment,depth,namespaced[element],r)
+ else
+ result[#result+1] = f_begin_display_attr(depth,namespaced[element],r)
+ end
+ depth = depth + 1
end
- begintag(result,element,natu,depth,di,skip)
- flushtree(result,di.data,natu,depth)
- -- if sub(result[#result],-1) == " " and natu ~= "inline" then
- -- result[#result] = sub(result[#result],1,-2)
- -- end
- endtag(result,element,natu,depth,skip)
- if di.after then
- flushtree(result,di.after,nature,depth)
+ end
+ end
+ used[element][detail or ""] = { nature, specification.parents } -- for template css
+ -- also in last else ?
+ local metadata = specification.metadata
+ if metadata then
+ result[#result+1] = f_metadata_begin(depth)
+ for k, v in table.sortedpairs(metadata) do
+ result[#result+1] = f_metadata(depth+1,k,lpegmatch(p_entity,v))
+ end
+ result[#result+1] = f_metadata_end(depth)
+ end
+ end
+
+ local function endtag(result,embedded,element,nature,di,skip)
+ if skip == "comment" then
+ if show_comment then
+ if nature == "display" and (inline == 0 or inline == 1) then
+ depth = depth - 1
+ result[#result+1] = f_comment_end_display(depth,namespaced[element])
+ inline = 0
+ elseif nature == "mixed" and (inline == 0 or inline == 1) then
+ depth = depth - 1
+ result[#result+1] = f_comment_end_mixed(namespaced[element])
+ inline = 0
+ else
+ inline = inline - 1
+ result[#result+1] = f_comment_end_inline(namespaced[element])
end
end
+ elseif skip then
+ -- ignore
+ else
+ if nature == "display" and (inline == 0 or inline == 1) then
+ depth = depth - 1
+ result[#result+1] = f_end_display(depth,namespaced[element])
+ inline = 0
+ elseif nature == "mixed" and (inline == 0 or inline == 1) then
+ depth = depth - 1
+ result[#result+1] = f_end_mixed(namespaced[element])
+ inline = 0
+ else
+ inline = inline - 1
+ result[#result+1] = f_end_inline(namespaced[element])
+ end
+
+ -- if embedded then
+ -- if element == "math" then
+ -- local id = f_tagid(element,di.n) -- index)
+ -- local tx = concat(result,"",embedded[id],#result)
+ -- embedded[id] = "<?xml version='1.0' standalone='yes'?>" .. "\n" .. tx
+ -- end
+ -- end
end
end
-end
-local function breaktree(tree,parent,parentelement) -- also removes double breaks
- local data = tree.data
- if data then
+ local function flushtree(result,embedded,data,nature)
local nofdata = #data
- local prevelement
- local prevnature
- local prevparnumber
- local newdata = { }
- local nofnewdata = 0
for i=1,nofdata do
local di = data[i]
- if not di then
- -- skip
- elseif di.content then
- local parnumber = di.parnumber
- if prevnature == "inline" and prevparnumber and prevparnumber ~= parnumber then
- nofnewdata = nofnewdata + 1
- if trace_spacing then
- newdata[nofnewdata] = makebreaknode { type = "a", p = prevparnumber, n = parnumber }
+ if not di then -- hm, di can be string
+ -- whatever
+ else
+ local content = di.content
+ -- also optimize for content == "" : trace that first
+ if content then
+ -- already has breaks
+ local content = lpegmatch(p_entity,content)
+ if i == nofdata and sub(content,-1) == "\n" then -- move check
+ -- can be an end of line in par but can also be the last line
+ if trace_spacing then
+ result[#result+1] = f_spacing(di.parnumber or 0,sub(content,1,-2))
+ else
+ result[#result+1] = sub(content,1,-2)
+ end
+ result[#result+1] = " "
+ else
+ if trace_spacing then
+ result[#result+1] = f_spacing(di.parnumber or 0,content)
+ else
+ result[#result+1] = content
+ end
+ end
+ elseif not di.collapsed then -- ignore collapsed data (is appended, reconstructed par)
+ local element = di.element
+ if not element then
+ -- skip
+ elseif element == "break" then -- or element == "pagebreak"
+ emptytag(result,embedded,element,nature,di)
+ elseif element == "" or di.skip == "ignore" then
+ -- skip
else
- newdata[nofnewdata] = makebreaknode()
+ if di.before then
+ flushtree(result,embedded,di.before,nature)
+ end
+ local natu = di.nature
+ local skip = di.skip
+ if di.breaknode then
+ emptytag(result,embedded,"break","display",di)
+ end
+ begintag(result,embedded,element,natu,di,skip)
+ flushtree(result,embedded,di.data,natu)
+ endtag(result,embedded,element,natu,di,skip)
+ if di.after then
+ flushtree(result,embedded,di.after,nature)
+ end
end
end
- prevelement = nil
- prevnature = "inline"
- prevparnumber = parnumber
- nofnewdata = nofnewdata + 1
- newdata[nofnewdata] = di
- elseif not di.collapsed then
- local element = di.element
- if element == "break" then -- or element == "pagebreak"
- if prevelement == "break" then
- di.element = ""
- end
- prevelement = element
- prevnature = "display"
- elseif element == "" or di.skip == "ignore" then
+ end
+ end
+ end
+
+ local function breaktree(tree,parent,parentelement) -- also removes double breaks
+ local data = tree.data
+ if data then
+ local nofdata = #data
+ local prevelement
+ local prevnature
+ local prevparnumber
+ local newdata = { }
+ local nofnewdata = 0
+ for i=1,nofdata do
+ local di = data[i]
+ if not di then
-- skip
+ elseif di.content then
+ local parnumber = di.parnumber
+ if prevnature == "inline" and prevparnumber and prevparnumber ~= parnumber then
+ nofnewdata = nofnewdata + 1
+ if trace_spacing then
+ newdata[nofnewdata] = makebreaknode { type = "a", p = prevparnumber, n = parnumber }
+ else
+ newdata[nofnewdata] = makebreaknode()
+ end
+ end
+ prevelement = nil
+ prevnature = "inline"
+ prevparnumber = parnumber
+ nofnewdata = nofnewdata + 1
+ newdata[nofnewdata] = di
+ elseif not di.collapsed then
+ local element = di.element
+ if element == "break" then -- or element == "pagebreak"
+ if prevelement == "break" then
+ di.element = ""
+ end
+ prevelement = element
+ prevnature = "display"
+ elseif element == "" or di.skip == "ignore" then
+ -- skip
+ else
+ local nature = di.nature
+ local parnumber = di.parnumber
+ if prevnature == "inline" and nature == "inline" and prevparnumber and prevparnumber ~= parnumber then
+ nofnewdata = nofnewdata + 1
+ if trace_spacing then
+ newdata[nofnewdata] = makebreaknode { type = "b", p = prevparnumber, n = parnumber }
+ else
+ newdata[nofnewdata] = makebreaknode()
+ end
+ end
+ prevnature = nature
+ prevparnumber = parnumber
+ prevelement = element
+ breaktree(di,tree,element)
+ end
+ nofnewdata = nofnewdata + 1
+ newdata[nofnewdata] = di
else
local nature = di.nature
local parnumber = di.parnumber
if prevnature == "inline" and nature == "inline" and prevparnumber and prevparnumber ~= parnumber then
nofnewdata = nofnewdata + 1
if trace_spacing then
- newdata[nofnewdata] = makebreaknode { type = "b", p = prevparnumber, n = parnumber }
+ newdata[nofnewdata] = makebreaknode { type = "c", p = prevparnumber, n = parnumber }
else
newdata[nofnewdata] = makebreaknode()
end
end
prevnature = nature
prevparnumber = parnumber
- prevelement = element
- breaktree(di,tree,element)
- end
- nofnewdata = nofnewdata + 1
- newdata[nofnewdata] = di
- else
- local nature = di.nature
- local parnumber = di.parnumber
- if prevnature == "inline" and nature == "inline" and prevparnumber and prevparnumber ~= parnumber then
nofnewdata = nofnewdata + 1
- if trace_spacing then
- newdata[nofnewdata] = makebreaknode { type = "c", p = prevparnumber, n = parnumber }
- else
- newdata[nofnewdata] = makebreaknode()
- end
+ newdata[nofnewdata] = di
end
- prevnature = nature
- prevparnumber = parnumber
- nofnewdata = nofnewdata + 1
- newdata[nofnewdata] = di
end
- end
- tree.data = newdata
- end
-end
-
--- also tabulaterow reconstruction .. maybe better as a checker
--- i.e cell attribute
-
-local function collapsetree()
- for tag, trees in next, treehash do
- local d = trees[1].data
- if d then
- local nd = #d
- if nd > 0 then
- for i=2,#trees do
- local currenttree = trees[i]
- local currentdata = currenttree.data
- local currentpar = currenttree.parnumber
- local previouspar = trees[i-1].parnumber
- currenttree.collapsed = true
- -- is the next ok?
- if previouspar == 0 or not (di and di.content) then
- previouspar = nil -- no need anyway so no further testing needed
- end
- for j=1,#currentdata do
- local cd = currentdata[j]
- if not cd or cd == "" then
- -- skip
- elseif cd.content then
- if not currentpar then
- -- add space ?
- elseif not previouspar then
- -- add space ?
- elseif currentpar ~= previouspar then
- nd = nd + 1
- if trace_spacing then
- d[nd] = makebreaknode { type = "d", p = previouspar, n = currentpar }
- else
- d[nd] = makebreaknode()
+ tree.data = newdata
+ end
+ end
+
+ -- also tabulaterow reconstruction .. maybe better as a checker
+ -- i.e cell attribute
+
+ local function collapsetree()
+ for tag, trees in next, treehash do
+ local d = trees[1].data
+ if d then
+ local nd = #d
+ if nd > 0 then
+ for i=2,#trees do
+ local currenttree = trees[i]
+ local currentdata = currenttree.data
+ local currentpar = currenttree.parnumber
+ local previouspar = trees[i-1].parnumber
+ currenttree.collapsed = true
+ -- is the next ok?
+ if previouspar == 0 or not (di and di.content) then
+ previouspar = nil -- no need anyway so no further testing needed
+ end
+ for j=1,#currentdata do
+ local cd = currentdata[j]
+ if not cd or cd == "" then
+ -- skip
+ elseif cd.content then
+ if not currentpar then
+ -- add space ?
+ elseif not previouspar then
+ -- add space ?
+ elseif currentpar ~= previouspar then
+ nd = nd + 1
+ if trace_spacing then
+ d[nd] = makebreaknode { type = "d", p = previouspar, n = currentpar }
+ else
+ d[nd] = makebreaknode()
+ end
end
+ previouspar = currentpar
+ nd = nd + 1
+ d[nd] = cd
+ else
+ nd = nd + 1
+ d[nd] = cd
end
- previouspar = currentpar
- nd = nd + 1
- d[nd] = cd
- else
- nd = nd + 1
- d[nd] = cd
+ currentdata[j] = false
end
- currentdata[j] = false
end
end
end
end
end
-end
-local function finalizetree(tree)
- for _, finalizer in next, finalizers do
- finalizer(tree)
+ local function finalizetree(tree)
+ for _, finalizer in next, finalizers do
+ finalizer(tree)
+ end
end
-end
-local function indextree(tree)
- local data = tree.data
- if data then
- local n, new = 0, { }
- for i=1,#data do
- local d = data[i]
- if not d then
- -- skip
- elseif d.content then
- n = n + 1
- new[n] = d
- elseif not d.collapsed then
- n = n + 1
- d.__i__ = n
- d.__p__ = tree
- indextree(d)
- new[n] = d
+ local function indextree(tree)
+ local data = tree.data
+ if data then
+ local n, new = 0, { }
+ for i=1,#data do
+ local d = data[i]
+ if not d then
+ -- skip
+ elseif d.content then
+ n = n + 1
+ new[n] = d
+ elseif not d.collapsed then
+ n = n + 1
+ d.__i__ = n
+ d.__p__ = tree
+ indextree(d)
+ new[n] = d
+ end
end
+ tree.data = new
end
- tree.data = new
end
-end
-local function checktree(tree)
- local data = tree.data
- if data then
- for i=1,#data do
- local d = data[i]
- if type(d) == "table" then
- local check = checks[d.tg]
- if check then
- check(d)
+ local function checktree(tree)
+ local data = tree.data
+ if data then
+ for i=1,#data do
+ local d = data[i]
+ if type(d) == "table" then
+ local check = checks[d.tg]
+ if check then
+ check(d)
+ end
+ checktree(d)
end
- checktree(d)
end
end
end
+
+ wrapups.flushtree = flushtree
+ wrapups.breaktree = breaktree
+ wrapups.collapsetree = collapsetree
+ wrapups.finalizetree = finalizetree
+ wrapups.indextree = indextree
+ wrapups.checktree = checktree
+
end
-- collector code
local function push(fulltag,depth)
- local tag, n = lpegmatch(dashsplitter,fulltag)
- local tg, detail = lpegmatch(colonsplitter,tag)
- local element, nature
- if detail then
- local pd = properties[tag]
- local pt = properties[tg]
- element = pd and pd.export or pt and pt.export or tg
- nature = pd and pd.nature or pt and pt.nature or defaultnature
+ local tg, n, detail
+ local specification = specifications[fulltag]
+ if specification then
+ tg = specification.tagname
+ n = specification.tagindex
+ detail = specification.detail
else
- local p = properties[tg]
- element = p and p.export or tg
- nature = p and p.nature or "inline"
+ -- a break (more efficient if we don't store those in specifications)
+ tg, n = lpegmatch(tagsplitter,fulltag)
+ n = tonumber(n) -- to tonumber in tagsplitter
end
+ local p = properties[tg]
+ local element = p and p.export or tg
+ local nature = p and p.nature or "inline" -- defaultnature
local treedata = tree.data
- local t = {
+ local t = { -- maybe we can use the tag table
tg = tg,
fulltag = fulltag,
detail = detail,
- n = tonumber(n), -- more efficient
+ n = n, -- already a number
element = element,
nature = nature,
data = { },
@@ -1658,9 +2316,9 @@ local function push(fulltag,depth)
treestack[currentdepth] = tree
if trace_export then
if detail and detail ~= "" then
- report_export("%w<%s trigger=%a paragraph=%a index=%a detail=%a>",currentdepth-1,fulltag,currentattribute or 0,currentparagraph or 0,#treedata,detail)
+ report_export("%w<%s trigger=%q n=%q paragraph=%q index=%q detail=%q>",currentdepth-1,tg,n,currentattribute or 0,currentparagraph or 0,#treedata,detail)
else
- report_export("%w<%s trigger=%a paragraph=%a index=%a>",currentdepth-1,fulltag,currentattribute or 0,currentparagraph or 0,#treedata)
+ report_export("%w<%s trigger=%q n=%q paragraph=%q index=%q>",currentdepth-1,tg,n,currentattribute or 0,currentparagraph or 0,#treedata)
end
end
tree = t
@@ -1677,15 +2335,19 @@ local function push(fulltag,depth)
end
local function pop()
- local top = nesting[currentdepth]
- tree = treestack[currentdepth]
- currentdepth = currentdepth - 1
- if trace_export then
- if top then
- report_export("%w</%s>",currentdepth,top)
- else
- report_export("</%s>",top)
+ if currentdepth > 0 then
+ local top = nesting[currentdepth]
+ tree = treestack[currentdepth]
+ currentdepth = currentdepth - 1
+ if trace_export then
+ if top then
+ report_export("%w</%s>",currentdepth,top)
+ else
+ report_export("</%s>",top)
+ end
end
+ else
+ report_export("%w<!-- too many pops -->",currentdepth)
end
end
@@ -1700,63 +2362,70 @@ local function continueexport()
end
local function pushentry(current)
- if current then
- if restart then
- continueexport()
- restart = false
- end
- local newdepth = #current
- local olddepth = currentdepth
- if trace_export then
- report_export("%w<!-- moving from depth %s to %s (%s) -->",currentdepth,olddepth,newdepth,current[newdepth])
+ if not current then
+ -- bad news
+ return
+ end
+ current = current.taglist
+ if not current then
+ -- even worse news
+ return
+ end
+ if restart then
+ continueexport()
+ restart = false
+ end
+ local newdepth = #current
+ local olddepth = currentdepth
+ if trace_export then
+ report_export("%w<!-- moving from depth %s to %s (%s) -->",currentdepth,olddepth,newdepth,current[newdepth])
+ end
+ if olddepth <= 0 then
+ for i=1,newdepth do
+ push(current[i],i)
end
- if olddepth <= 0 then
- for i=1,newdepth do
- push(current[i],i)
+ else
+ local difference
+ if olddepth < newdepth then
+ for i=1,olddepth do
+ if current[i] ~= nesting[i] then
+ difference = i
+ break
+ end
end
else
- local difference
- if olddepth < newdepth then
- for i=1,olddepth do
- if current[i] ~= nesting[i] then
- difference = i
- break
- end
- end
- else
- for i=1,newdepth do
- if current[i] ~= nesting[i] then
- difference = i
- break
- end
+ for i=1,newdepth do
+ if current[i] ~= nesting[i] then
+ difference = i
+ break
end
end
- if difference then
- for i=olddepth,difference,-1 do
- pop()
- end
- for i=difference,newdepth do
- push(current[i],i)
- end
- elseif newdepth > olddepth then
- for i=olddepth+1,newdepth do
- push(current[i],i)
- end
- elseif newdepth < olddepth then
- for i=olddepth,newdepth,-1 do
- pop()
- end
- elseif trace_export then
- report_export("%w<!-- staying at depth %s (%s) -->",currentdepth,newdepth,nesting[newdepth] or "?")
+ end
+ if difference then
+ for i=olddepth,difference,-1 do
+ pop()
+ end
+ for i=difference,newdepth do
+ push(current[i],i)
end
+ elseif newdepth > olddepth then
+ for i=olddepth+1,newdepth do
+ push(current[i],i)
+ end
+ elseif newdepth < olddepth then
+ for i=olddepth,newdepth,-1 do
+ pop()
+ end
+ elseif trace_export then
+ report_export("%w<!-- staying at depth %s (%s) -->",currentdepth,newdepth,nesting[newdepth] or "?")
end
- return olddepth, newdepth
end
+ return olddepth, newdepth
end
-local function pushcontent(currentparagraph,newparagraph)
+local function pushcontent(oldparagraph,newparagraph)
if nofcurrentcontent > 0 then
- if currentparagraph then
+ if oldparagraph then
if currentcontent[nofcurrentcontent] == "\n" then
if trace_export then
report_export("%w<!-- removing newline -->",currentdepth)
@@ -1766,35 +2435,37 @@ local function pushcontent(currentparagraph,newparagraph)
end
local content = concat(currentcontent,"",1,nofcurrentcontent)
if content == "" then
- -- omit; when currentparagraph we could push, remove spaces, pop
- elseif somespace[content] and currentparagraph then
- -- omit; when currentparagraph we could push, remove spaces, pop
+ -- omit; when oldparagraph we could push, remove spaces, pop
+ elseif somespace[content] and oldparagraph then
+ -- omit; when oldparagraph we could push, remove spaces, pop
else
local olddepth, newdepth
local list = taglist[currentattribute]
if list then
olddepth, newdepth = pushentry(list)
end
- local td = tree.data
- local nd = #td
- td[nd+1] = { parnumber = currentparagraph, content = content }
- if trace_export then
- report_export("%w<!-- start content with length %s -->",currentdepth,#content)
- report_export("%w%s",currentdepth,(gsub(content,"\n","\\n")))
- report_export("%w<!-- stop content -->",currentdepth)
- end
- if olddepth then
- for i=newdepth-1,olddepth,-1 do
- pop()
+ if tree then
+ local td = tree.data
+ local nd = #td
+ td[nd+1] = { parnumber = oldparagraph or currentparagraph, content = content }
+ if trace_export then
+ report_export("%w<!-- start content with length %s -->",currentdepth,#content)
+ report_export("%w%s",currentdepth,(gsub(content,"\n","\\n")))
+ report_export("%w<!-- stop content -->",currentdepth)
+ end
+ if olddepth then
+ for i=newdepth-1,olddepth,-1 do
+ pop()
+ end
end
end
end
nofcurrentcontent = 0
end
- if currentparagraph then
+ if oldparagraph then
pushentry(makebreaklist(currentnesting))
if trace_export then
- report_export("%w<!-- break added betweep paragraph %a and %a -->",currentdepth,currentparagraph,newparagraph)
+ report_export("%w<!-- break added between paragraph %a and %a -->",currentdepth,oldparagraph,newparagraph)
end
end
end
@@ -1823,28 +2494,28 @@ end
-- whatsit_code localpar_code
-local function collectresults(head,list) -- is last used (we also have currentattribute)
+local function collectresults(head,list,pat,pap) -- is last used (we also have currentattribute)
local p
for n in traverse_nodes(head) do
- local id = n.id -- 14: image, 8: literal (mp)
+ local id = getid(n) -- 14: image, 8: literal (mp)
if id == glyph_code then
- local at = n[a_tagged]
+ local at = getattr(n,a_tagged) or pat
if not at then
-- we need to tag the pagebody stuff as being valid skippable
--
-- report_export("skipping character: %C (no attribute)",n.char)
else
-- we could add tonunicodes for ligatures (todo)
- local components = n.components
- if components then -- we loose data
- collectresults(components,nil)
+ local components = getfield(n,"components")
+ local c = getchar(n)
+ if components and (not characterdata[c] or overloads[c]) then -- we loose data
+ collectresults(components,nil,at) -- this assumes that components have the same attribute as the glyph ... we should be more tolerant (see math)
else
- local c = n.char
if last ~= at then
local tl = taglist[at]
pushcontent()
currentnesting = tl
- currentparagraph = n[a_taggedpar]
+ currentparagraph = getattr(n,a_taggedpar) or pap
currentattribute = at
last = at
pushentry(currentnesting)
@@ -1853,13 +2524,17 @@ local function collectresults(head,list) -- is last used (we also have currentat
end
-- We need to intercept this here; maybe I will also move this
-- to a regular setter at the tex end.
- local r = n[a_reference]
+ local r = getattr(n,a_reference)
if r then
- referencehash[tl[#tl]] = r -- fulltag
+ local t = tl.taglist
+ referencehash[t[#t]] = r -- fulltag
end
--
elseif last then
- local ap = n[a_taggedpar]
+ -- we can consider tagging the pars (lines) in the parbuilder but then we loose some
+ -- information unless we inject a special node (but even then we can run into nesting
+ -- issues)
+ local ap = getattr(n,a_taggedpar) or pap
if ap ~= currentparagraph then
pushcontent(currentparagraph,ap)
pushentry(currentnesting)
@@ -1874,7 +2549,7 @@ local function collectresults(head,list) -- is last used (we also have currentat
report_export("%w<!-- processing glyph %C tagged %a) -->",currentdepth,c,at)
end
end
- local s = n[a_exportstatus]
+ local s = getattr(n,a_exportstatus)
if s then
c = s
end
@@ -1883,7 +2558,7 @@ local function collectresults(head,list) -- is last used (we also have currentat
report_export("%w<!-- skipping last glyph -->",currentdepth)
end
elseif c == 0x20 then
- local a = n[a_characters]
+ local a = getattr(n,a_characters)
nofcurrentcontent = nofcurrentcontent + 1
if a then
if trace_export then
@@ -1894,45 +2569,57 @@ local function collectresults(head,list) -- is last used (we also have currentat
currentcontent[nofcurrentcontent] = " "
end
else
- local fc = fontchar[n.font]
+ local fc = fontchar[getfont(n)]
if fc then
fc = fc and fc[c]
if fc then
- local u = fc.tounicode
- if u and u ~= "" then
+ local u = fc.unicode
+ if not u then
nofcurrentcontent = nofcurrentcontent + 1
- currentcontent[nofcurrentcontent] = utfchar(fromunicode16(u))
+ currentcontent[nofcurrentcontent] = utfchar(c)
+ elseif type(u) == "table" then
+ for i=1,#u do
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = utfchar(u[i])
+ end
else
nofcurrentcontent = nofcurrentcontent + 1
- currentcontent[nofcurrentcontent] = utfchar(c)
+ currentcontent[nofcurrentcontent] = utfchar(u)
end
- else -- weird, happens in hz (we really need to get rid of the pseudo fonts)
+ elseif c > 0 then
nofcurrentcontent = nofcurrentcontent + 1
currentcontent[nofcurrentcontent] = utfchar(c)
+ else
+ -- we can have -1 as side effect of an explicit hyphen (unless we expand)
end
- else
+ elseif c > 0 then
nofcurrentcontent = nofcurrentcontent + 1
currentcontent[nofcurrentcontent] = utfchar(c)
+ else
+ -- we can have -1 as side effect of an explicit hyphen (unless we expand)
end
end
end
end
elseif id == disc_code then -- probably too late
if keephyphens then
- local pre = n.pre
- if pre and not pre.next and pre.id == glyph_code and pre.char == hyphencode then
+ local pre = getfield(n,"pre")
+ if pre and not getnext(pre) and getid(pre) == glyph_code and getchar(pre) == hyphencode then
nofcurrentcontent = nofcurrentcontent + 1
currentcontent[nofcurrentcontent] = hyphen
end
end
- collectresults(n.replace,nil)
+ local replace = getfield(n,"replace")
+ if replace then
+ collectresults(replace,nil)
+ end
elseif id == glue_code then
-- we need to distinguish between hskips and vskips
- local ca = n[a_characters]
+ local ca = getattr(n,a_characters)
if ca == 0 then
-- skip this one ... already converted special character (node-acc)
elseif ca then
- local a = n[a_tagged]
+ local a = getattr(n,a_tagged) or pat
if a then
local c = specialspaces[ca]
if last ~= a then
@@ -1942,13 +2629,13 @@ local function collectresults(head,list) -- is last used (we also have currentat
end
pushcontent()
currentnesting = tl
- currentparagraph = n[a_taggedpar]
+ currentparagraph = getattr(n,a_taggedpar) or pap
currentattribute = a
last = a
pushentry(currentnesting)
-- no reference check (see above)
elseif last then
- local ap = n[a_taggedpar]
+ local ap = getattr(n,a_taggedpar) or pap
if ap ~= currentparagraph then
pushcontent(currentparagraph,ap)
pushentry(currentnesting)
@@ -1969,11 +2656,12 @@ local function collectresults(head,list) -- is last used (we also have currentat
currentcontent[nofcurrentcontent] = c
end
else
- local subtype = n.subtype
+ local subtype = getsubtype(n)
if subtype == userskip_code then
- if n.spec.width > threshold then
+ local spec = getfield(n,"spec")
+ if getfield(spec,"width") > threshold then
if last and not somespace[currentcontent[nofcurrentcontent]] then
- local a = n[a_tagged]
+ local a = getattr(n,a_tagged) or pat
if a == last then
if trace_export then
report_export("%w<!-- injecting spacing 5a -->",currentdepth)
@@ -2000,7 +2688,7 @@ local function collectresults(head,list) -- is last used (we also have currentat
end
elseif subtype == spaceskip_code or subtype == xspaceskip_code then
if not somespace[currentcontent[nofcurrentcontent]] then
- local a = n[a_tagged]
+ local a = getattr(n,a_tagged) or pat
if a == last then
if trace_export then
report_export("%w<!-- injecting spacing 7 (stay in element) -->",currentdepth)
@@ -2029,7 +2717,7 @@ local function collectresults(head,list) -- is last used (we also have currentat
nofcurrentcontent = nofcurrentcontent - 1
end
elseif not somespace[r] then
- local a = n[a_tagged]
+ local a = getattr(n,a_tagged) or pat
if a == last then
if trace_export then
report_export("%w<!-- injecting spacing 1 (end of line, stay in element) -->",currentdepth)
@@ -2057,9 +2745,9 @@ local function collectresults(head,list) -- is last used (we also have currentat
end
end
elseif id == hlist_code or id == vlist_code then
- local ai = n[a_image]
+ local ai = getattr(n,a_image)
if ai then
- local at = n[a_tagged]
+ local at = getattr(n,a_tagged) or pat
if nofcurrentcontent > 0 then
pushcontent()
pushentry(currentnesting) -- ??
@@ -2072,18 +2760,22 @@ local function collectresults(head,list) -- is last used (we also have currentat
currentparagraph = nil
else
-- we need to determine an end-of-line
- collectresults(n.list,n)
+ local list = getlist(n)
+ if list then
+ local at = getattr(n,a_tagged) or pat
+ collectresults(list,n,at)
+ end
end
elseif id == kern_code then
- local kern = n.kern
+ local kern = getfield(n,"kern")
if kern > 0 then
local limit = threshold
- if p and p.id == glyph_code then
- limit = fontquads[p.font] / 4
+ if p and getid(p) == glyph_code then
+ limit = fontquads[getfont(p)] / 4
end
if kern > limit then
if last and not somespace[currentcontent[nofcurrentcontent]] then
- local a = n[a_tagged]
+ local a = getattr(n,a_tagged) or pat
if a == last then
if not somespace[currentcontent[nofcurrentcontent]] then
if trace_export then
@@ -2123,7 +2815,20 @@ function nodes.handlers.export(head) -- hooks into the page builder
end
-- continueexport()
restart = true
- collectresults(head)
+
+-- local function f(head,depth,pat)
+-- for n in node.traverse(head) do
+-- local a = n[a_tagged] or pat
+-- local t = taglist[a]
+-- print(depth,n,a,t and table.concat(t," "))
+-- if n.id == hlist_code or n.id == vlist_code and n.list then
+-- f(n.list,depth+1,a)
+-- end
+-- end
+-- end
+-- f(head,1)
+
+ collectresults(tonut(head))
if trace_export then
report_export("%w<!-- stop flushing page -->",currentdepth)
end
@@ -2133,280 +2838,819 @@ end
function builders.paragraphs.tag(head)
noftextblocks = noftextblocks + 1
- for n in traverse_id(hlist_code,head) do
- local subtype = n.subtype
+ for n in traverse_id(hlist_code,tonut(head)) do
+ local subtype = getsubtype(n)
if subtype == line_code then
- n[a_textblock] = noftextblocks
+ setattr(n,a_textblock,noftextblocks)
elseif subtype == glue_code or subtype == kern_code then
- n[a_textblock] = 0
+ setattr(n,a_textblock,0)
end
end
return false
end
--- encoding='utf-8'
+do
local xmlpreamble = [[
-<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<?xml version="1.0" encoding="UTF-8" standalone="%standalone%" ?>
+
+<!--
-<!-- input filename : %- 17s -->
-<!-- processing date : %- 17s -->
-<!-- context version : %- 17s -->
-<!-- exporter version : %- 17s -->
+ input filename : %filename%
+ processing date : %date%
+ context version : %contextversion%
+ exporter version : %exportversion%
+
+-->
]]
-local function wholepreamble()
- return format(xmlpreamble,tex.jobname,os.date(),environment.version,exportversion)
-end
+ local flushtree = wrapups.flushtree
+
+ local function wholepreamble(standalone)
+ return replacetemplate(xmlpreamble, {
+ standalone = standalone and "yes" or "no",
+ filename = tex.jobname,
+ date = os.date(),
+ contextversion = environment.version,
+ exportversion = exportversion,
+ })
+ end
local csspreamble = [[
-<?xml-stylesheet type="text/css" href="%s"?>
+<?xml-stylesheet type="text/css" href="%filename%" ?>
]]
-local function allusedstylesheets(xmlfile,cssfiles,files)
- local result = { }
- for i=1,#cssfiles do
- local cssfile = cssfiles[i]
- if type(cssfile) ~= "string" or cssfile == variables.yes or cssfile == "" or cssfile == xmlfile then
- cssfile = file.replacesuffix(xmlfile,"css")
- else
- cssfile = file.addsuffix(cssfile,"css")
+local cssheadlink = [[
+<link type="text/css" rel="stylesheet" href="%filename%" />
+]]
+
+ local function allusedstylesheets(cssfiles,files,path)
+ local done = { }
+ local result = { }
+ local extras = { }
+ for i=1,#cssfiles do
+ local cssfile = cssfiles[i]
+ if type(cssfile) ~= "string" then
+ -- error
+ elseif cssfile == "export-example.css" then
+ -- ignore
+ elseif not done[cssfile] then
+ cssfile = file.join(path,cssfile)
+ report_export("adding css reference '%s'",cssfile)
+ files[#files+1] = cssfile
+ result[#result+1] = replacetemplate(csspreamble, { filename = cssfile })
+ extras[#extras+1] = replacetemplate(cssheadlink, { filename = cssfile })
+ done[cssfile] = true
+ end
end
- files[#files+1] = cssfile
- report_export("adding css reference '%s'",cssfile)
- result[#result+1] = format(csspreamble,cssfile)
+ return concat(result), concat(extras)
end
- return concat(result)
-end
-local e_template = [[
-%s {
- display: %s ;
+local elementtemplate = [[
+/* element="%element%" detail="%detail%" chain="%chain%" */
+
+%element%, %namespace%div.%element% {
+ display: %display% ;
}]]
-local d_template = [[
-%s[detail=%s] {
- display: %s ;
+local detailtemplate = [[
+/* element="%element%" detail="%detail%" chain="%chain%" */
+
+%element%[detail=%detail%], %namespace%div.%element%.%detail% {
+ display: %display% ;
}]]
-local displaymapping = {
- inline = "inline",
- display = "block",
- mixed = "inline",
-}
+-- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd" >
+
+local htmltemplate = [[
+%preamble%
+
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML">
+
+ <head>
+
+ <meta charset="utf-8"/>
+
+ <title>%title%</title>
+
+%style%
+
+ </head>
+ <body>
+ <div xmlns="http://www.pragma-ade.com/context/export">
+
+<div class="warning">Rendering can be suboptimal because there is no default/fallback css loaded.</div>
+
+%body%
-local function allusedelements(xmlfile)
- local result = { format("/* template for file %s */",xmlfile) }
- for element, details in sortedhash(used) do
- result[#result+1] = format("/* category: %s */",element)
- for detail, nature in sortedhash(details) do
- local d = displaymapping[nature or "display"] or "block"
- if detail == "" then
- result[#result+1] = formatters[e_template](element,d)
+ </div>
+ </body>
+</html>
+]]
+
+ local displaymapping = {
+ inline = "inline",
+ display = "block",
+ mixed = "inline",
+ }
+
+ local function allusedelements(basename)
+ local result = { replacetemplate(namespacetemplate, {
+ what = "template",
+ filename = basename,
+ namespace = contextns,
+ -- cssnamespaceurl = usecssnamespace and cssnamespaceurl or "",
+ cssnamespaceurl = cssnamespaceurl,
+ }) }
+ for element, details in sortedhash(used) do
+ if namespaces[element] then
+ -- skip math
else
- result[#result+1] = formatters[d_template](element,detail,d)
+ for detail, what in sortedhash(details) do
+ local nature = what[1] or "display"
+ local chain = what[2]
+ local display = displaymapping[nature] or "block"
+ if detail == "" then
+ result[#result+1] = replacetemplate(elementtemplate, {
+ element = element,
+ display = display,
+ chain = chain,
+ namespace = usecssnamespace and namespace or "",
+ })
+ else
+ result[#result+1] = replacetemplate(detailtemplate, {
+ element = element,
+ display = display,
+ detail = detail,
+ chain = chain,
+ namespace = usecssnamespace and cssnamespace or "",
+ })
+ end
+ end
end
end
+ return concat(result,"\n\n")
+ end
+
+ local function allcontent(tree,embed)
+ local result = { }
+ local embedded = embed and { }
+ flushtree(result,embedded,tree.data,"display") -- we need to collect images
+ result = concat(result)
+ -- no need to lpeg .. fast enough
+ result = gsub(result,"\n *\n","\n")
+ result = gsub(result,"\n +([^< ])","\n%1")
+ return result, embedded
+ end
+
+ -- local xhtmlpreamble = [[
+ -- <!DOCTYPE html PUBLIC
+ -- "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
+ -- "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd"
+ -- >
+ -- ]]
+
+ local function cleanxhtmltree(xmltree)
+ if xmltree then
+ local implicits = { }
+ local explicits = { }
+ local overloads = { }
+ for e in xml.collected(xmltree,"*") do
+ local at = e.at
+ if at then
+ local explicit = at.explicit
+ local implicit = at.implicit
+ if explicit then
+ if not explicits[explicit] then
+ explicits[explicit] = true
+ at.id = explicit
+ if implicit then
+ overloads[implicit] = explicit
+ end
+ end
+ else
+ if implicit and not implicits[implicit] then
+ implicits[implicit] = true
+ at.id = "aut:" .. implicit
+ end
+ end
+ end
+ end
+ for e in xml.collected(xmltree,"*") do
+ local at = e.at
+ if at then
+ local internal = at.internal
+ local location = at.location
+ if internal then
+ if location then
+ local explicit = overloads[location]
+ if explicit then
+ at.href = "#" .. explicit
+ else
+ at.href = "#aut:" .. internal
+ end
+ else
+ at.href = "#aut:" .. internal
+ end
+ else
+ if location then
+ at.href = "#" .. location
+ else
+ local url = at.url
+ if url then
+ at.href = url
+ else
+ local file = at.file
+ if file then
+ at.href = file
+ end
+ end
+ end
+ end
+ end
+ end
+ return xmltree
+ else
+ return xml.convert('<?xml version="1.0"?>\n<error>invalid xhtml tree</error>')
+ end
end
- return concat(result,"\n\n")
-end
-local function allcontent(tree)
- local result = { }
- flushtree(result,tree.data,"display",0) -- we need to collect images
- result = concat(result)
- result = gsub(result,"\n *\n","\n")
- result = gsub(result,"\n +([^< ])","\n%1")
- return result
-end
+ -- maybe the reverse: be explicit about what is permitted
--- local xhtmlpreamble = [[
--- <!DOCTYPE html PUBLIC
--- "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
--- "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd"
--- >
--- ]]
-
-local function cleanxhtmltree(xmltree)
- 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
- -- todo: inject xhtmlpreamble (xmlns should have be enough)
- local wrapper = { tg = "a", ns = "xhtml", at = { href = "unknown" } }
- for e in xml.collected(xmltree,"link") do
- local at = e.at
- local href
- if at.location then
- href = "#" .. gsub(at.location,":","_")
- elseif at.url then
- href = at.url
- elseif at.file then
- href = at.file
- end
- if href then
- wrapper.at.href = href
- 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
- return xmltree
- else
- return xml.convert("<?xml version='1.0'?>\n<error>invalid xhtml tree</error>")
- end
-end
+ local private = {
+ destination = true,
+ prefix = true,
+ reference = true,
+ --
+ id = true,
+ href = true,
+ --
+ implicit = true,
+ explicit = true,
+ --
+ url = true,
+ file = true,
+ internal = true,
+ location = true,
+ --
+ name = true, -- image name
+ used = true, -- image name
+ page = true, -- image name
+ width = true,
+ height = true,
+ --
+ }
-local cssfile, xhtmlfile = nil, nil
+ local addclicks = true
+ local f_onclick = formatters[ [[location.href='%s']] ]
+ local f_onclick = formatters[ [[location.href='%s']] ]
-directives.register("backend.export.css", function(v) cssfile = v end)
-directives.register("backend.export.xhtml",function(v) xhtmlfile = v end)
+ local p_cleanid = lpeg.replacer { [":"] = "-" }
+ local p_cleanhref = lpeg.Cs(lpeg.P("#") * p_cleanid)
-local function stopexport(v)
- starttiming(treehash)
- --
- finishexport()
- --
- collapsetree(tree)
- indextree(tree)
- checktree(tree)
- breaktree(tree)
- finalizetree(tree)
- --
- hashlistdata()
- --
- if type(v) ~= "string" or v == variables.yes or v == "" then
- v = tex.jobname
- end
- local basename = file.basename(v)
- local xmlfile = file.addsuffix(basename,"export")
- --
- local imagefilename = file.addsuffix(file.removesuffix(xmlfile) .. "-images","css")
- local stylefilename = file.addsuffix(file.removesuffix(xmlfile) .. "-styles","css")
- local templatefilename = file.replacesuffix(xmlfile,"template")
- local specificationfilename = file.replacesuffix(xmlfile,"specification")
- --
- if xhtml and not cssfile then
- cssfile = true
- end
- local cssfiles = { }
- if cssfile then
- if cssfile == true then
- cssfiles = { "export-example.css" }
+ local p_splitter = lpeg.Ct ( (
+ lpeg.Carg(1) * lpeg.C((1-lpeg.P(" "))^1) / function(d,s) if not d[s] then d[s] = true return s end end
+ * lpeg.P(" ")^0 )^1 )
+
+
+ local classes = table.setmetatableindex(function(t,k)
+ local v = concat(lpegmatch(p_splitter,k,1,{})," ")
+ t[k] = v
+ return v
+ end)
+
+ local function makeclass(tg,at)
+ local detail = at.detail
+ local chain = at.chain
+ local result
+ at.detail = nil
+ at.chain = nil
+ if detail and detail ~= "" then
+ if chain and chain ~= "" then
+ if chain ~= detail then
+ result = { classes[tg .. " " .. chain .. " " .. detail] } -- we need to remove duplicates
+ elseif tg ~= detail then
+ result = { tg, detail }
+ else
+ result = { tg }
+ end
+ elseif tg ~= detail then
+ result = { tg, detail }
+ else
+ result = { tg }
+ end
+ elseif chain and chain ~= "" then
+ if tg ~= chain then
+ result = { tg, chain }
+ else
+ result = { tg }
+ end
else
- cssfiles = settings_to_array(cssfile or "")
+ result = { tg }
end
- insert(cssfiles,1,imagefilename)
- insert(cssfiles,1,stylefilename)
+ for k, v in next, at do
+ if not private[k] then
+ result[#result+1] = k .. "-" .. v
+ end
+ end
+ return concat(result, " ")
end
- cssfiles = table.unique(cssfiles)
- --
- local result = allcontent(tree) -- also does some housekeeping and data collecting
- --
- local files = {
- }
- local results = concat {
- wholepreamble(),
- allusedstylesheets(xmlfile,cssfiles,files), -- ads to files
- result,
- }
- --
- files = table.unique(files)
- --
- report_export("saving xml data in %a",xmlfile)
- io.savedata(xmlfile,results)
- --
- report_export("saving css image definitions in %a",imagefilename)
- io.savedata(imagefilename,allusedimages(xmlfile))
- --
- report_export("saving css style definitions in %a",stylefilename)
- io.savedata(stylefilename,allusedstyles(xmlfile))
- --
- report_export("saving css template in %a",templatefilename)
- io.savedata(templatefilename,allusedelements(xmlfile))
- --
- if xhtmlfile then
- if type(v) ~= "string" or xhtmlfile == true or xhtmlfile == variables.yes or xhtmlfile == "" or xhtmlfile == xmlfile then
- xhtmlfile = file.replacesuffix(xmlfile,"xhtml")
- else
- xhtmlfile = file.addsuffix(xhtmlfile,"xhtml")
+
+ local function remap(specification,source,target)
+ local comment = nil -- share comments
+ for c in xml.collected(source,"*") do
+ if not c.special then
+ local tg = c.tg
+ local ns = c.ns
+ if ns == "m" then
+ if false then -- yes or no
+ c.ns = ""
+ c.at["xmlns:m"] = nil
+ end
+ -- elseif tg == "a" then
+ -- c.ns = ""
+ else
+ -- if tg == "tabulatecell" or tg == "tablecell" then
+ local dt = c.dt
+ local nt = #dt
+ if nt == 0 or (nt == 1 and dt[1] == "") then
+ if comment then
+ c.dt = comment
+ else
+ xml.setcomment(c,"empty")
+ comment = c.dt
+ end
+ end
+ -- end
+ local at = c.at
+ local class = nil
+ if tg == "document" then
+ at.href = nil
+ at.detail = nil
+ at.chain = nil
+ else
+ class = makeclass(tg,at)
+ end
+ local id = at.id
+ local href = at.href
+ if id then
+ id = lpegmatch(p_cleanid, id) or id
+ if href then
+ href = lpegmatch(p_cleanhref,href) or href
+ c.at = {
+ class = class,
+ id = id,
+ href = href,
+ onclick = addclicks and f_onclick(href) or nil,
+ }
+ else
+ c.at = {
+ class = class,
+ id = id,
+ }
+ end
+ else
+ if href then
+ href = lpegmatch(p_cleanhref,href) or href
+ c.at = {
+ class = class,
+ href = href,
+ onclick = addclicks and f_onclick(href) or nil,
+ }
+ else
+ c.at = {
+ class = class,
+ }
+ end
+ end
+ c.tg = "div"
+ end
+ end
+ end
+ end
+
+ -- local cssfile = nil directives.register("backend.export.css", function(v) cssfile = v end)
+
+ local addsuffix = file.addsuffix
+ local joinfile = file.join
+
+ local embedfile = false directives.register("export.embed",function(v) embedfile = v end)
+ local embedmath = false
+
+ local function stopexport(v)
+
+ starttiming(treehash)
+ --
+ finishexport()
+ --
+ report_export("")
+ report_export("exporting xml, xhtml and html files")
+ report_export("")
+ --
+ wrapups.collapsetree(tree)
+ wrapups.indextree(tree)
+ wrapups.checktree(tree)
+ wrapups.breaktree(tree)
+ wrapups.finalizetree(tree)
+ --
+ wrapups.hashlistdata()
+ --
+ if type(v) ~= "string" or v == v_yes or v == "" then
+ v = tex.jobname
+ end
+
+ -- we use a dedicated subpath:
+ --
+ -- ./jobname-export
+ -- ./jobname-export/images
+ -- ./jobname-export/styles
+ -- ./jobname-export/styles
+ -- ./jobname-export/jobname-export.xml
+ -- ./jobname-export/jobname-export.xhtml
+ -- ./jobname-export/jobname-export.html
+ -- ./jobname-export/jobname-specification.lua
+ -- ./jobname-export/styles/jobname-defaults.css
+ -- ./jobname-export/styles/jobname-styles.css
+ -- ./jobname-export/styles/jobname-images.css
+ -- ./jobname-export/styles/jobname-templates.css
+
+ local basename = file.basename(v)
+ local corename = file.removesuffix(basename)
+ local basepath = basename .. "-export"
+ local imagepath = joinfile(basepath,"images")
+ local stylepath = joinfile(basepath,"styles")
+
+ local function validpath(what,pathname)
+ if lfs.isdir(pathname) then
+ report_export("using exiting %s path %a",what,pathname)
+ return pathname
+ end
+ lfs.mkdir(pathname)
+ if lfs.isdir(pathname) then
+ report_export("using cretated %s path %a",what,basepath)
+ return pathname
+ else
+ report_export("unable to create %s path %a",what,basepath)
+ return false
+ end
end
- files[#files+1] = xhtmlfile
- report_export("saving xhtml variant in %a",xhtmlfile)
- local xmltree = cleanxhtmltree(xml.convert(results))
- xml.save(xmltree,xhtmlfile)
+
+ if not (validpath("export",basepath) and validpath("images",imagepath) and validpath("styles",stylepath)) then
+ return
+ end
+
+ -- we're now on the dedicated export subpath so we can't clash names
+
+ local xmlfilebase = addsuffix(basename .. "-raw","xml" )
+ local xhtmlfilebase = addsuffix(basename .. "-tag","xhtml")
+ local htmlfilebase = addsuffix(basename .. "-div","xhtml")
+ local specificationfilebase = addsuffix(basename .. "-pub","lua" )
+
+ local xmlfilename = joinfile(basepath, xmlfilebase )
+ local xhtmlfilename = joinfile(basepath, xhtmlfilebase )
+ local htmlfilename = joinfile(basepath, htmlfilebase )
+ local specificationfilename = joinfile(basepath, specificationfilebase)
+ --
+ local defaultfilebase = addsuffix(basename .. "-defaults", "css")
+ local imagefilebase = addsuffix(basename .. "-images", "css")
+ local stylefilebase = addsuffix(basename .. "-styles", "css")
+ local templatefilebase = addsuffix(basename .. "-templates","css")
+ --
+ local defaultfilename = joinfile(stylepath,defaultfilebase )
+ local imagefilename = joinfile(stylepath,imagefilebase )
+ local stylefilename = joinfile(stylepath,stylefilebase )
+ local templatefilename = joinfile(stylepath,templatefilebase)
+
+ local cssfile = finetuning.cssfile
+
+ -- we keep track of all used files
+
+ local files = {
+ }
+
+ -- we always load the defaults and optionally extra css files; we also copy the example
+ -- css file so that we always have the latest version
+
+ local cssfiles = {
+ defaultfilebase,
+ imagefilebase,
+ stylefilebase,
+ }
+
+ local examplefilename = resolvers.find_file("export-example.css")
+ if examplefilename then
+ local data = io.loaddata(examplefilename)
+ if not data or data == "" then
+ data = "/* missing css file */"
+ elseif not usecssnamespace then
+ data = gsub(data,cssnamespace,"")
+ end
+ io.savedata(defaultfilename,data)
+ end
+
+ if cssfile then
+ local list = table.unique(settings_to_array(cssfile))
+ for i=1,#list do
+ local source = addsuffix(list[i],"css")
+ local target = joinfile(stylepath,file.basename(source))
+ cssfiles[#cssfiles+1] = source
+ if not lfs.isfile(source) then
+ source = joinfile("../",source)
+ end
+ if lfs.isfile(source) then
+ report_export("copying %s",source)
+ file.copy(source,target)
+ end
+ end
+ end
+
+ local x_styles, h_styles = allusedstylesheets(cssfiles,files,"styles")
+
+ -- at this point we're ready for the content; the collector also does some
+ -- housekeeping and data collecting; at this point we still have an xml
+ -- representation that uses verbose element names and carries information in
+ -- attributes
+
+
+ local data = tree.data
+ for i=1,#data do
+ if data[i].tg ~= "document" then
+ data[i] = { }
+ end
+ end
+
+ local result, embedded = allcontent(tree,embedmath) -- embedfile is for testing
+
+ local attach = backends.nodeinjections.attachfile
+
+ if embedfile and attach then
+ -- only for testing
+ attach {
+ data = concat{ wholepreamble(true), result },
+ name = file.basename(xmlfilename),
+ registered = "export",
+ title = "raw xml export",
+ method = v_hidden,
+ mimetype = "application/mathml+xml",
+ }
+ end
+ -- if embedmath and attach then
+ -- local refs = { }
+ -- for k, v in sortedhash(embedded) do
+ -- attach {
+ -- data = v,
+ -- file = file.basename(k),
+ -- name = file.addsuffix(k,"xml"),
+ -- registered = k,
+ -- reference = k,
+ -- title = "xml export snippet: " .. k,
+ -- method = v_hidden,
+ -- mimetype = "application/mathml+xml",
+ -- }
+ -- refs[k] = 0
+ -- end
+ -- end
+
+ result = concat {
+ wholepreamble(true),
+ x_styles, -- adds to files
+ result,
+ }
+
+ cssfiles = table.unique(cssfiles)
+
+ -- we're now ready for saving the result in the xml file
+
+ report_export("saving xml data in %a",xmlfilename)
+ io.savedata(xmlfilename,result)
+
+ report_export("saving css image definitions in %a",imagefilename)
+ io.savedata(imagefilename,wrapups.allusedimages(basename))
+
+ report_export("saving css style definitions in %a",stylefilename)
+ io.savedata(stylefilename,wrapups.allusedstyles(basename))
+
+ report_export("saving css template in %a",templatefilename)
+ io.savedata(templatefilename,allusedelements(basename))
+
+ -- additionally we save an xhtml file; for that we load the file as xml tree
+
+ report_export("saving xhtml variant in %a",xhtmlfilename)
+
+ local xmltree = cleanxhtmltree(xml.convert(result))
+
+ xml.save(xmltree,xhtmlfilename)
+
+ -- now we save a specification file that can b eused for generating an epub file
+
-- looking at identity is somewhat redundant as we also inherit from interaction
-- at the tex end
+
local identity = interactions.general.getidentity()
+
local specification = {
- name = file.removesuffix(v),
- identifier = os.uuid(),
- images = uniqueusedimages(),
- root = xhtmlfile,
- files = files,
- language = languagenames[texgetcount("mainlanguagenumber")],
- title = validstring(finetuning.title) or validstring(identity.title),
- subtitle = validstring(finetuning.subtitle) or validstring(identity.subtitle),
- author = validstring(finetuning.author) or validstring(identity.author),
- firstpage = validstring(finetuning.firstpage),
- lastpage = validstring(finetuning.lastpage),
+ name = file.removesuffix(v),
+ identifier = os.uuid(),
+ images = wrapups.uniqueusedimages(),
+ imagefile = joinfile("styles",imagefilebase),
+ imagepath = "images",
+ stylepath = "styles",
+ xmlfiles = { xmlfilebase },
+ xhtmlfiles = { xhtmlfilebase },
+ htmlfiles = { htmlfilebase },
+ styles = cssfiles,
+ htmlroot = htmlfilebase,
+ language = languagenames[texgetcount("mainlanguagenumber")],
+ title = validstring(finetuning.title) or validstring(identity.title),
+ subtitle = validstring(finetuning.subtitle) or validstring(identity.subtitle),
+ author = validstring(finetuning.author) or validstring(identity.author),
+ firstpage = validstring(finetuning.firstpage),
+ lastpage = validstring(finetuning.lastpage),
}
- report_export("saving specification in %a (mtxrun --script epub --make %s)",specificationfilename,specificationfilename)
+
+ report_export("saving specification in %a",specificationfilename,specificationfilename)
+
io.savedata(specificationfilename,table.serialize(specification,true))
- end
- stoptiming(treehash)
-end
-local appendaction = nodes.tasks.appendaction
-local enableaction = nodes.tasks.enableaction
+ -- the html export for epub is different in the sense that it uses div's instead of
+ -- specific tags
-function commands.setupexport(t)
- table.merge(finetuning,t)
- keephyphens = finetuning.hyphen == variables.yes
-end
+ report_export("saving div based alternative in %a",htmlfilename)
+
+ remap(specification,xmltree)
-local function startexport(v)
- if v and not exporting then
- report_export("enabling export to xml")
- -- not yet known in task-ini
- appendaction("shipouts","normalizers", "nodes.handlers.export")
- -- enableaction("shipouts","nodes.handlers.export")
- enableaction("shipouts","nodes.handlers.accessibility")
- enableaction("math", "noads.handlers.tags")
- -- appendaction("finalizers","lists","builders.paragraphs.tag")
- -- enableaction("finalizers","builders.paragraphs.tag")
- luatex.registerstopactions(function() stopexport(v) end)
- exporting = true
+ local title = specification.title
+
+ if not title or title == "" then
+ title = "no title" -- believe it or not, but a <title/> can prevent viewing in browsers
+ end
+
+ local variables = {
+ style = h_styles,
+ body = xml.tostring(xml.first(xmltree,"/div")),
+ preamble = wholepreamble(false),
+ title = title,
+ }
+
+ io.savedata(htmlfilename,replacetemplate(htmltemplate,variables,"xml"))
+
+ -- finally we report how an epub file can be made (using the specification)
+
+ report_export("")
+ report_export('create epub with: mtxrun --script epub --make "%s" [--purge --rename --svgmath]',file.nameonly(basename))
+ report_export("")
+
+ stoptiming(treehash)
end
-end
-directives.register("backend.export",startexport) -- maybe .name
+ local appendaction = nodes.tasks.appendaction
+ local enableaction = nodes.tasks.enableaction
-statistics.register("xml exporting time", function()
- if exporting then
- return format("%s seconds, version %s", statistics.elapsedtime(treehash),exportversion)
+ function structurestags.setupexport(t)
+ table.merge(finetuning,t)
+ keephyphens = finetuning.hyphen == v_yes
+ exportproperties = finetuning.properties
+ if exportproperties == v_no then
+ exportproperties = false
+ end
end
-end)
+
+ local function startexport(v)
+ if v and not exporting then
+ report_export("enabling export to xml")
+ -- not yet known in task-ini
+ appendaction("shipouts","normalizers", "nodes.handlers.export")
+ -- enableaction("shipouts","nodes.handlers.export")
+ enableaction("shipouts","nodes.handlers.accessibility")
+ enableaction("math", "noads.handlers.tags")
+ -- appendaction("finalizers","lists","builders.paragraphs.tag")
+ -- enableaction("finalizers","builders.paragraphs.tag")
+ luatex.registerstopactions(structurestags.finishexport)
+ exporting = v
+ end
+ end
+
+ function structurestags.finishexport()
+ if exporting then
+ stopexport(exporting)
+ exporting = false
+ end
+ end
+
+ directives.register("backend.export",startexport) -- maybe .name
+
+ statistics.register("xml exporting time", function()
+ if exporting then
+ return string.format("%s seconds, version %s", statistics.elapsedtime(treehash),exportversion)
+ end
+ end)
+
+end
-- These are called at the tex end:
-commands.settagitemgroup = structurestags.setitemgroup
-commands.settagsynonym = structurestags.setsynonym
-commands.settagsorting = structurestags.setsorting
-commands.settagdescription = structurestags.setdescription
-commands.settagdescriptionsymbol = structurestags.setdescriptionsymbol
-commands.settaghighlight = structurestags.sethighlight
-commands.settagfigure = structurestags.setfigure
-commands.settagcombination = structurestags.setcombination
-commands.settagtablecell = structurestags.settablecell
-commands.settagtabulatecell = structurestags.settabulatecell
+implement {
+ name = "setupexport",
+ actions = structurestags.setupexport,
+ arguments = {
+ {
+ { "align" },
+ { "bodyfont", "dimen" },
+ { "width", "dimen" },
+ { "properties" },
+ { "hyphen" },
+ { "title" },
+ { "subtitle" },
+ { "author" },
+ { "firstpage" },
+ { "lastpage" },
+ { "svgstyle" },
+ { "cssfile" },
+ }
+ }
+}
+
+implement {
+ name = "finishexport",
+ actions = structurestags.finishexport,
+}
+
+implement {
+ name = "settagitemgroup",
+ actions = structurestags.setitemgroup,
+ arguments = { "boolean", "integer", "string" }
+}
+
+implement {
+ name = "settagitem",
+ actions = structurestags.setitem,
+ arguments = "string"
+}
+
+implement {
+ name = "settagsynonym",
+ actions = structurestags.setsynonym,
+ arguments = "string"
+}
+
+implement {
+ name = "settagsorting",
+ actions = structurestags.setsorting,
+ arguments = "string"
+}
+
+implement {
+ name = "settagdescription",
+ actions = structurestags.setdescription,
+ arguments = { "string", "integer" }
+}
+
+implement {
+ name = "settagdescriptionsymbol",
+ actions = structurestags.setdescriptionsymbol,
+ arguments = { "string", "integer" }
+}
+
+implement {
+ name = "settaghighlight",
+ actions = structurestags.sethighlight,
+ arguments = { "string", "integer" }
+}
+
+implement {
+ name = "settagfigure",
+ actions = structurestags.setfigure,
+ arguments = { "string", "string", "string", "dimen", "dimen" }
+}
+
+implement {
+ name = "settagcombination",
+ actions = structurestags.setcombination,
+ arguments = { "integer", "integer" }
+}
+
+implement {
+ name = "settagtablecell",
+ actions = structurestags.settablecell,
+ arguments = { "integer", "integer", "integer" }
+}
+
+implement {
+ name = "settagtabulatecell",
+ actions = structurestags.settabulatecell,
+ arguments = "integer"
+}
+
+implement {
+ name = "settagregister",
+ actions = structurestags.setregister,
+ arguments = { "string", "integer" }
+}
+
+implement {
+ name = "settaglist",
+ actions = structurestags.setlist,
+ arguments = "integer"
+}