summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/math-noa.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/math-noa.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/math-noa.lua518
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