diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/math-noa.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/math-noa.lua | 518 |
1 files changed, 388 insertions, 130 deletions
diff --git a/Master/texmf-dist/tex/context/base/math-noa.lua b/Master/texmf-dist/tex/context/base/math-noa.lua index 02bbe0a6299..c298a8068ae 100644 --- a/Master/texmf-dist/tex/context/base/math-noa.lua +++ b/Master/texmf-dist/tex/context/base/math-noa.lua @@ -9,126 +9,132 @@ if not modules then modules = { } end modules ['math-noa'] = { -- beware: this is experimental code and there will be a more -- generic (attribute value driven) interface too but for the -- moment this is ok +-- +-- we will also make dedicated processors (faster) +-- +-- beware: names will change as we wil make noads.xxx.handler i.e. xxx +-- subnamespaces local utf = unicode.utf8 -local set_attribute = node.set_attribute -local has_attribute = node.has_attribute -local mlist_to_hlist = node.mlist_to_hlist -local font_of_family = node.family_font -local fontdata = fonts.identifiers - -local format, rep = string.format, string.rep local utfchar, utfbyte = utf.char, utf.byte +local format, rep = string.format, string.rep +local concat = table.concat -noads = noads or { } - -local trace_remapping = false trackers.register("math.remapping", function(v) trace_remapping = v end) -local trace_processing = false trackers.register("math.processing", function(v) trace_processing = v end) -local trace_analyzing = false trackers.register("math.analyzing", function(v) trace_analyzing = v end) - -local noad_ord = 0 -local noad_op_displaylimits = 1 -local noad_op_limits = 2 -local noad_op_nolimits = 3 -local noad_bin = 4 -local noad_rel = 5 -local noad_open = 6 -local noad_close = 7 -local noad_punct = 8 -local noad_inner = 9 -local noad_under = 10 -local noad_over = 11 -local noad_vcenter = 12 - --- obsolete: --- --- math_ord = node.id("ord") -- attr nucleus sub sup --- math_op = node.id("op") -- attr nucleus sub sup subtype --- math_bin = node.id("bin") -- attr nucleus sub sup --- math_rel = node.id("rel") -- attr nucleus sub sup --- math_punct = node.id("punct") -- attr nucleus sub sup --- --- math_open = node.id("open") -- attr nucleus sub sup --- math_close = node.id("close") -- attr nucleus sub sup --- --- math_inner = node.id("inner") -- attr nucleus sub sup --- math_vcenter = node.id("vcenter") -- attr nucleus sub sup --- math_under = node.id("under") -- attr nucleus sub sup --- math_over = node.id("over") -- attr nucleus sub sup - -local math_noad = node.id("noad") -- attr nucleus sub sup - -local math_accent = node.id("accent") -- attr nucleus sub sup accent -local math_radical = node.id("radical") -- attr nucleus sub sup left degree -local math_fraction = node.id("fraction") -- attr nucleus sub sup left right - -local math_box = node.id("sub_box") -- attr list -local math_sub = node.id("sub_mlist") -- attr list -local math_char = node.id("math_char") -- attr fam char -local math_text_char = node.id("math_text_char") -- attr fam char -local math_delim = node.id("delim") -- attr small_fam small_char large_fam large_char -local math_style = node.id("style") -- attr style -local math_choice = node.id("choice") -- attr display text script scriptscript -local math_fence = node.id("fence") -- attr subtype - -local simple_noads = table.tohash { - math_noad, -} +local fonts, nodes, node, mathematics = fonts, nodes, node, mathematics -local all_noads = { - math_noad, - math_box, math_sub, - math_char, math_text_char, math_delim, math_style, - math_accent, math_radical, math_fraction, math_choice, math_fence, -} +local otf = fonts.handlers.otf +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register + +local trace_remapping = false trackers.register("math.remapping", function(v) trace_remapping = v end) +local trace_processing = false trackers.register("math.processing", function(v) trace_processing = v end) +local trace_analyzing = false trackers.register("math.analyzing", function(v) trace_analyzing = v end) +local trace_normalizing = false trackers.register("math.normalizing", function(v) trace_normalizing = v end) + +local report_processing = logs.reporter("mathematics","processing") +local report_remapping = logs.reporter("mathematics","remapping") +local report_normalizing = logs.reporter("mathematics","normalizing") -noads.processors = noads.processors or { } +local set_attribute = node.set_attribute +local has_attribute = node.has_attribute +local mlist_to_hlist = node.mlist_to_hlist +local font_of_family = node.family_font -local function process(start,what,n) +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers + +noads = noads or { } -- todo: only here +local noads = noads + +noads.processors = noads.processors or { } +local processors = noads.processors + +noads.handlers = noads.handlers or { } +local handlers = noads.handlers + +local tasks = nodes.tasks + +local nodecodes = nodes.nodecodes +local noadcodes = nodes.noadcodes + +local noad_ord = noadcodes.ord +local noad_rel = noadcodes.rel +local noad_punct = noadcodes.punct + +local math_noad = nodecodes.noad -- attr nucleus sub sup +local math_accent = nodecodes.accent -- attr nucleus sub sup accent +local math_radical = nodecodes.radical -- attr nucleus sub sup left degree +local math_fraction = nodecodes.fraction -- attr nucleus sub sup left right +local math_box = nodecodes.subbox -- attr list +local math_sub = nodecodes.submlist -- attr list +local math_char = nodecodes.mathchar -- attr fam char +local math_textchar = nodecodes.mathtextchar -- attr fam char +local math_delim = nodecodes.delim -- attr small_fam small_char large_fam large_char +local math_style = nodecodes.style -- attr style +local math_choice = nodecodes.choice -- attr display text script scriptscript +local math_fence = nodecodes.fence -- attr subtype + +local left_fence_code = 1 + +local function process(start,what,n,parent) if n then n = n + 1 else n = 0 end while start do + local id = start.id if trace_processing then - logs.report("math","%s%s",rep(" ",n or 0),tostring(start)) + local margin = rep(" ",n or 0) + local detail = tostring(start) + if id == math_noad then + report_processing("%s%s (class: %s)",margin,detail,noadcodes[start.subtype] or "?") + elseif id == math_char then + local char = start.char + local fam = start.fam + local font = font_of_family(fam) + report_processing("%s%s (family: %s, font: %s, char: %s, shape: %s)",margin,detail,fam,font,char,utfchar(char)) + else + report_processing("%s%s",margin,detail) + end end - local id = start.id local proc = what[id] if proc then - proc(start,what,n) - elseif id == math_char or id == math_text_char or id == math_delim then + local done, newstart = proc(start,what,n,parent or start.prev) + if newstart then + start = newstart + end + elseif id == math_char or id == math_textchar or id == math_delim then break elseif id == math_style then -- has a next elseif id == math_noad then - local noad = start.nucleus if noad then process(noad,what,n) end -- list - noad = start.sup if noad then process(noad,what,n) end -- list - noad = start.sub if noad then process(noad,what,n) end -- list + local noad = start.nucleus if noad then process(noad,what,n,start) end -- list + noad = start.sup if noad then process(noad,what,n,start) end -- list + noad = start.sub if noad then process(noad,what,n,start) end -- list elseif id == math_box or id == math_sub then - local noad = start.list if noad then process(noad,what,n) end -- list + local noad = start.list if noad then process(noad,what,n,start) end -- list elseif id == math_fraction then - local noad = start.num if noad then process(noad,what,n) end -- list - noad = start.denom if noad then process(noad,what,n) end -- list - noad = start.left if noad then process(noad,what,n) end -- delimiter - noad = start.right if noad then process(noad,what,n) end -- delimiter + local noad = start.num if noad then process(noad,what,n,start) end -- list + noad = start.denom if noad then process(noad,what,n,start) end -- list + noad = start.left if noad then process(noad,what,n,start) end -- delimiter + noad = start.right if noad then process(noad,what,n,start) end -- delimiter elseif id == math_choice then - local noad = start.display if noad then process(noad,what,n) end -- list - noad = start.text if noad then process(noad,what,n) end -- list - noad = start.script if noad then process(noad,what,n) end -- list - noad = start.scriptscript if noad then process(noad,what,n) end -- list + local noad = start.display if noad then process(noad,what,n,start) end -- list + noad = start.text if noad then process(noad,what,n,start) end -- list + noad = start.script if noad then process(noad,what,n,start) end -- list + noad = start.scriptscript if noad then process(noad,what,n,start) end -- list elseif id == math_fence then - local noad = start.delim if noad then process(noad,what,n) end -- delimiter + local noad = start.delim if noad then process(noad,what,n,start) end -- delimiter elseif id == math_radical then - local noad = start.nucleus if noad then process(noad,what,n) end -- list - noad = start.sup if noad then process(noad,what,n) end -- list - noad = start.sub if noad then process(noad,what,n) end -- list - noad = start.left if noad then process(noad,what,n) end -- delimiter - noad = start.degree if noad then process(noad,what,n) end -- list + local noad = start.nucleus if noad then process(noad,what,n,start) end -- list + noad = start.sup if noad then process(noad,what,n,start) end -- list + noad = start.sub if noad then process(noad,what,n,start) end -- list + noad = start.left if noad then process(noad,what,n,start) end -- delimiter + noad = start.degree if noad then process(noad,what,n,start) end -- list elseif id == math_accent then - local noad = start.nucleus if noad then process(noad,what,n) end -- list - noad = start.sup if noad then process(noad,what,n) end -- list - noad = start.sub if noad then process(noad,what,n) end -- list - noad = start.accent if noad then process(noad,what,n) end -- list - noad = start.bot_accent if noad then process(noad,what,n) end -- list + local noad = start.nucleus if noad then process(noad,what,n,start) end -- list + noad = start.sup if noad then process(noad,what,n,start) end -- list + noad = start.sub if noad then process(noad,what,n,start) end -- list + noad = start.accent if noad then process(noad,what,n,start) end -- list + noad = start.bot_accent if noad then process(noad,what,n,start) end -- list else -- glue, penalty, etc end @@ -143,16 +149,16 @@ noads.process = process local mathalphabet = attributes.private("mathalphabet") local mathgreek = attributes.private("mathgreek") -noads.processors.relocate = { } +processors.relocate = { } local function report_remap(tag,id,old,new,extra) - logs.report("math","remapping %s in font %s from U+%04X (%s) to U+%04X (%s)%s",tag,id,old,utfchar(old),new,utfchar(new),extra or "") + report_remapping("remapping %s in font %s from U+%05X (%s) to U+%05X (%s)%s",tag,id,old,utfchar(old),new,utfchar(new),extra or "") end -local remap_alphabets = mathematics.remap_alphabets -local fcs = fonts.color.set +local remapalphabets = mathematics.remapalphabets +local setnodecolor = nodes.tracers.colors.set --- we can have a global famdata == fonts.famdata and chrdata == fonts.chrdata +-- we can have a global famdata == fonts.famdata --~ This does not work out well, as there are no fallbacks. Ok, we could --~ define a poor mans simplify mechanism. @@ -171,7 +177,7 @@ local fcs = fonts.color.set --~ report_remap("fallback",id,char,newchar) --~ end --~ if trace_analyzing then ---~ fcs(pointer,"font:isol") +--~ setnodecolor(pointer,"font:isol") --~ end --~ pointer.char = newchar --~ return true @@ -179,7 +185,9 @@ local fcs = fonts.color.set --~ end --~ end -noads.processors.relocate[math_char] = function(pointer) +local current_id, current_characters + +processors.relocate[math_char] = function(pointer) local g = has_attribute(pointer,mathgreek) or 0 local a = has_attribute(pointer,mathalphabet) or 0 if a > 0 or g > 0 then @@ -190,17 +198,24 @@ noads.processors.relocate[math_char] = function(pointer) set_attribute(pointer,mathalphabet,0) end local char = pointer.char - local newchar = remap_alphabets(char,a,g) + local newchar = remapalphabets(char,a,g) if newchar then local fam = pointer.fam local id = font_of_family(fam) + -- local tfmdata = fontdata[id] - if tfmdata and tfmdata.characters[newchar] then -- we could probably speed this up + if tfmdata and tfmdata.characters[newchar] then + -- -- to be tested: + -- if id ~= current_id then + -- current_id = id + -- current_characters = fontdata[id].characters + -- end + -- if current_characters and current_characters[newchar] then if trace_remapping then report_remap("char",id,char,newchar) end if trace_analyzing then - fcs(pointer,"font:isol") + setnodecolor(pointer,"font:isol") end pointer.char = newchar return true @@ -214,24 +229,24 @@ noads.processors.relocate[math_char] = function(pointer) -- return checked(pointer) end if trace_analyzing then - fcs(pointer,"font:medi") + setnodecolor(pointer,"font:medi") end end -noads.processors.relocate[math_text_char] = function(pointer) +processors.relocate[math_textchar] = function(pointer) if trace_analyzing then - fcs(pointer,"font:init") + setnodecolor(pointer,"font:init") end end -noads.processors.relocate[math_delim] = function(pointer) +processors.relocate[math_delim] = function(pointer) if trace_analyzing then - fcs(pointer,"font:fina") + setnodecolor(pointer,"font:fina") end end -function noads.relocate_characters(head,style,penalties) - process(head,noads.processors.relocate) +function handlers.relocate(head,style,penalties) + process(head,processors.relocate) return true end @@ -247,10 +262,10 @@ end local mathsize = attributes.private("mathsize") -noads.processors.resize = { } +local resize = { } processors.resize = resize -noads.processors.resize[math_fence] = function(pointer) - if pointer.subtype == 1 then -- left +resize[math_fence] = function(pointer) + if pointer.subtype == left_fence_code then local a = has_attribute(pointer,mathsize) if a and a > 0 then set_attribute(pointer,mathsize,0) @@ -265,8 +280,8 @@ noads.processors.resize[math_fence] = function(pointer) end end -function noads.resize_characters(head,style,penalties) - process(head,noads.processors.resize) +function handlers.resize(head,style,penalties) + process(head,resize) return true end @@ -274,13 +289,13 @@ end local mathpunctuation = attributes.private("mathpunctuation") -noads.processors.respace = { } +local respace = { } processors.respace = respace local chardata = characters.data -- only [nd,ll,ul][po][nd,ll,ul] -noads.processors.respace[math_noad] = function(pointer) +respace[math_noad] = function(pointer) if pointer.subtype == noad_ord then local a = has_attribute(pointer,mathpunctuation) if a and a > 0 then @@ -327,40 +342,283 @@ noads.processors.respace[math_noad] = function(pointer) end end +function handlers.respace(head,style,penalties) + process(head,respace) + return true +end -function noads.respace_characters(head,style,penalties) - noads.process(head,noads.processors.respace) +-- The following code is dedicated to Luigi Scarso who pointed me +-- to the fact that \not= is not producing valid pdf-a code. +-- The code does not solve this for virtual characters but it does +-- a decent job on collapsing so that fonts that have the right +-- glyph will have a decent unicode point. + +local collapse = { } processors.collapse = collapse + +local mathpairs = characters.mathpairs + +collapse[math_noad] = function(pointer) + if pointer.subtype == noad_rel then + local current_nucleus = pointer.nucleus + if current_nucleus.id == math_char then + local current_char = current_nucleus.char + local mathpair = mathpairs[current_char] + if mathpair then + local next_noad = pointer.next + if next_noad and next_noad.id == math_noad and next_noad.subtype == noad_rel then + local next_nucleus = next_noad.nucleus + if next_nucleus.id == math_char then + local next_char = next_nucleus.char + local newchar = mathpair[next_char] + if newchar then + local fam = current_nucleus.fam + local id = font_of_family(fam) + local tfmdata = fontdata[id] + if tfmdata and tfmdata.characters[newchar] then + -- print("!!!!!",current_char,next_char,newchar) + current_nucleus.char = newchar + local next_next_noad = next_noad.next + if next_next_noad then + pointer.next = next_next_noad + next_next_noad.prev = pointer + else + pointer.next = nil + end + node.free(next_noad) + end + end + end + end + end + end + end +end + +function noads.handlers.collapse(head,style,penalties) + process(head,collapse) + return true +end + +-- normalize scripts + +local unscript = { } noads.processors.unscript = unscript + +local superscripts = characters.superscripts +local subscripts = characters.subscripts + +local replaced = { } + +local function replace(pointer) + local next = pointer.next + local start_super, stop_super, start_sub, stop_sub + local mode = "unset" + while next and next.id == math_noad do + local nextnucleus = next.nucleus + if nextnucleus and nextnucleus.id == math_char and not next.sub and not next.sup then + local char = nextnucleus.char + local s = superscripts[char] + if s then + if not start_super then + start_super = next + mode = "super" + elseif mode == "sub" then + break + end + stop_super = next + next = next.next + nextnucleus.char = s + replaced[char] = (replaced[char] or 0) + 1 + if trace_normalizing then + report_normalizing("superscript: U+05X (%s) => U+05X (%s)",char,utfchar(char),s,utfchar(s)) + end + else + local s = subscripts[char] + if s then + if not start_sub then + start_sub = next + mode = "sub" + elseif mode == "super" then + break + end + stop_sub = next + next = next.next + nextnucleus.char = s + replaced[char] = (replaced[char] or 0) + 1 + if trace_normalizing then + report_normalizing("subscript: U+05X (%s) => U+05X (%s)",char,utfchar(char),s,utfchar(s)) + end + else + break + end + end + else + break + end + end + if start_super then + if start_super == stop_super then + pointer.sup = start_super.nucleus + else + local list = node.new(math_sub) -- todo attr + list.head = start_super + pointer.sup = list + end + if mode == "super" then + pointer.next = stop_super.next + end + stop_super.next = nil + end + if start_sub then + if start_sub == stop_sub then + pointer.sub = start_sub.nucleus + else + local list = node.new(math_sub) -- todo attr + list.head = start_sub + pointer.sub = list + end + if mode == "sub" then + pointer.next = stop_sub.next + end + stop_sub.next = nil + end +end + + unscript[math_noad] = replace +-- unscript[math_accent] = replace +-- unscript[math_radical] = replace +-- unscript[math_fraction] = replace + +function handlers.unscript(head,style,penalties) + process(head,unscript) + return true +end + +statistics.register("math script replacements", function() + if next(replaced) then + local n, t = 0, { } + for k, v in table.sortedpairs(replaced) do + n = n + v + t[#t+1] = format("U+%05X:%s",k,utfchar(k)) + end + return format("%s (n=%s)",concat(t," "),n) + end +end) + +-- math alternates: (in xits lgf: $ABC$ $\cal ABC$ $\mathalternate{cal}\cal ABC$) + +local function initializemathalternates(tfmdata) + local goodies = tfmdata.goodies + if goodies then + local shared = tfmdata.shared + for i=1,#goodies do + -- first one counts + -- we can consider sharing the attributes ... todo (only once scan) + local mathgoodies = goodies[i].mathematics + local alternates = mathgoodies and mathgoodies.alternates + if alternates then + local lastattribute, attributes = 0, { } + for k, v in next, alternates do + lastattribute = lastattribute + 1 + v.attribute = lastattribute + attributes[lastattribute] = v + end + shared.mathalternates = alternates -- to be checked if shared is ok here + shared.mathalternatesattributes = attributes -- to be checked if shared is ok here + return + end + end + end +end + +registerotffeature { + name = "mathalternates", + description = "additional math alternative shapes", + initializers = { + base = initializemathalternates, + node = initializemathalternates, + } +} + +local getalternate = otf.getalternate + +local mathalternate = attributes.private("mathalternate") + +local alternate = { } -- processors.alternate = alternate + +function mathematics.setalternate(fam,tag) + local id = font_of_family(fam) + local tfmdata = fontdata[id] + local mathalternates = tfmdata.shared.mathalternates + if mathalternates then + local m = mathalternates[tag] + tex.attribute[mathalternate] = m and m.attribute or attributes.unsetvalue + end +end + +alternate[math_char] = function(pointer) + local a = has_attribute(pointer,mathalternate) + if a and a > 0 then + set_attribute(pointer,mathalternate,0) + local tfmdata = fontdata[font_of_family(pointer.fam)] -- we can also have a famdata + local mathalternatesattributes = tfmdata.shared.mathalternatesattributes + if mathalternatesattributes then + local what = mathalternatesattributes[a] + local alt = getalternate(tfmdata,pointer.char,what.feature,what.value) + if alt then + pointer.char = alt + end + end + end +end + +function handlers.check(head,style,penalties) + process(head,alternate) return true end -- the normal builder -function noads.mlist_to_hlist(head,style,penalties) +function builders.kernel.mlist_to_hlist(head,style,penalties) return mlist_to_hlist(head,style,penalties), true end -tasks.new ( - "math", - { +--~ function builders.kernel.mlist_to_hlist(head,style,penalties) +--~ print("!!!!!!! BEFORE",penalties) +--~ for n in node.traverse(head) do print(n) end +--~ print("!!!!!!!") +--~ head = mlist_to_hlist(head,style,penalties) +--~ print("!!!!!!! AFTER") +--~ for n in node.traverse(head) do print(n) end +--~ print("!!!!!!!") +--~ return head, true +--~ end + +tasks.new { + name = "math", + arguments = 2, + sequence = { "before", "normalizers", "builders", "after", } -) +} + +tasks.freezegroup("math", "normalizers") -- experimental +tasks.freezegroup("math", "builders") -- experimental -local actions = tasks.actions("math",2) -- head, style, penalties +local actions = tasks.actions("math") -- head, style, penalties local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming -function nodes.processors.mlist_to_hlist(head,style,penalties) +function processors.mlist_to_hlist(head,style,penalties) starttiming(noads) local head, done = actions(head,style,penalties) stoptiming(noads) return head, done end -callbacks.register('mlist_to_hlist',nodes.processors.mlist_to_hlist,"preprocessing math list") +callbacks.register('mlist_to_hlist',processors.mlist_to_hlist,"preprocessing math list") -- tracing |