diff options
Diffstat (limited to 'tex/context/base/mkiv/math-noa.lmt')
-rw-r--r-- | tex/context/base/mkiv/math-noa.lmt | 2399 |
1 files changed, 0 insertions, 2399 deletions
diff --git a/tex/context/base/mkiv/math-noa.lmt b/tex/context/base/mkiv/math-noa.lmt deleted file mode 100644 index 25e1823e2..000000000 --- a/tex/context/base/mkiv/math-noa.lmt +++ /dev/null @@ -1,2399 +0,0 @@ -if not modules then modules = { } end modules ['math-noa'] = { - version = 1.001, - optimize = true, - comment = "companion to math-ini.mkiv", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - --- beware: this is experimental code and there will be a more generic (attribute value --- driven) interface too but for the moment this is ok (sometime in 2015-2016 i will --- start cleaning up as by then the bigger picture is clear and code has been used for --- years; the main handlers will get some extensions) --- --- we will also make dedicated processors (faster) --- --- beware: names will change as we wil make noads.xxx.handler i.e. xxx --- subnamespaces - --- 20D6 -> 2190 --- 20D7 -> 2192 - --- todo: most is mathchar_code so we can have simple dedicated loops - --- nota bene: uunderdelimiter uoverdelimiter etc are radicals (we have 5 types) - -local next, tonumber = next, tonumber -local utfchar, utfbyte = utf.char, utf.byte -local formatters, gmatch = string.formatters, string.gmatch -local sortedhash = table.sortedhash -local insert, remove = table.insert, table.remove -local div, round = math.div, math.round -local bor, band = bit32.bor, bit32.band - -local fonts = fonts -local nodes = nodes -local node = node -local mathematics = mathematics -local context = context - -local otf = fonts.handlers.otf -local otffeatures = fonts.constructors.features.otf -local registerotffeature = otffeatures.register - -local privateattribute = attributes.private -local registertracker = trackers.register -local registerdirective = directives.register -local logreporter = logs.reporter -local setmetatableindex = table.setmetatableindex - -local colortracers = nodes.tracers.colors - -local trace_remapping = false registertracker("math.remapping", function(v) trace_remapping = v end) -local trace_processing = false registertracker("math.processing", function(v) trace_processing = v end) -local trace_analyzing = false registertracker("math.analyzing", function(v) trace_analyzing = v end) -local trace_normalizing = false registertracker("math.normalizing", function(v) trace_normalizing = v end) -local trace_collapsing = false registertracker("math.collapsing", function(v) trace_collapsing = v end) -local trace_fixing = false registertracker("math.fixing", function(v) trace_foxing = v end) -local trace_patching = false registertracker("math.patching", function(v) trace_patching = v end) -local trace_goodies = false registertracker("math.goodies", function(v) trace_goodies = v end) -local trace_variants = false registertracker("math.variants", function(v) trace_variants = v end) -local trace_alternates = false registertracker("math.alternates", function(v) trace_alternates = v end) -local trace_italics = false registertracker("math.italics", function(v) trace_italics = v end) -local trace_kernpairs = false registertracker("math.kernpairs", function(v) trace_kernpairs = v end) -local trace_domains = false registertracker("math.domains", function(v) trace_domains = v end) -local trace_families = false registertracker("math.families", function(v) trace_families = v end) -local trace_fences = false registertracker("math.fences", function(v) trace_fences = v end) -local trace_unstacking = false registertracker("math.unstack", function(v) trace_unstacking = v end) - -local check_coverage = true registerdirective("math.checkcoverage", function(v) check_coverage = v end) - -local report_processing = logreporter("mathematics","processing") -local report_remapping = logreporter("mathematics","remapping") -local report_normalizing = logreporter("mathematics","normalizing") -local report_collapsing = logreporter("mathematics","collapsing") -local report_fixing = logreporter("mathematics","fixing") -local report_patching = logreporter("mathematics","patching") -local report_goodies = logreporter("mathematics","goodies") -local report_variants = logreporter("mathematics","variants") -local report_alternates = logreporter("mathematics","alternates") -local report_italics = logreporter("mathematics","italics") -local report_kernpairs = logreporter("mathematics","kernpairs") -local report_domains = logreporter("mathematics","domains") -local report_families = logreporter("mathematics","families") -local report_fences = logreporter("mathematics","fences") -local report_unstacking = logreporter("mathematics","unstack") - -local a_mathrendering = privateattribute("mathrendering") -local a_exportstatus = privateattribute("exportstatus") - -local nuts = nodes.nuts -local nodepool = nuts.pool -local tonut = nuts.tonut -local nutstring = nuts.tostring - -local setfield = nuts.setfield -local setlink = nuts.setlink -local setlist = nuts.setlist -local setnext = nuts.setnext -local setprev = nuts.setprev -local setchar = nuts.setchar -local setfam = nuts.setfam -local setsubtype = nuts.setsubtype -local setattr = nuts.setattr -local setattrlist = nuts.setattrlist -local setwidth = nuts.setwidth -local setheight = nuts.setheight -local setdepth = nuts.setdepth - -local getfield = nuts.getfield -local getnext = nuts.getnext -local getprev = nuts.getprev -local getboth = nuts.getboth -local getid = nuts.getid -local getsubtype = nuts.getsubtype -local getchar = nuts.getchar -local getfont = nuts.getfont -local getfam = nuts.getfam -local getattr = nuts.getattr -local getattrs = nuts.getattrs -local getlist = nuts.getlist -local getwidth = nuts.getwidth -local getheight = nuts.getheight -local getdepth = nuts.getdepth - -local getnucleus = nuts.getnucleus -local getsub = nuts.getsub -local getsup = nuts.getsup -local getsubpre = nuts.getsubpre -local getsuppre = nuts.getsuppre - -local setnucleus = nuts.setnucleus -local setsub = nuts.setsub -local setsup = nuts.setsup -local setsubpre = nuts.setsubpre -local setsuppre = nuts.setsuppre - -local flush_node = nuts.flush -local copy_node = nuts.copy -local slide_nodes = nuts.slide -local set_visual = nuts.setvisual - -local mlist_to_hlist = nuts.mlist_to_hlist - -local new_kern = nodepool.kern -local new_submlist = nodepool.submlist -local new_noad = nodepool.noad -local new_delimiter = nodepool.delimiter -local new_fence = nodepool.fence - -local fonthashes = fonts.hashes -local fontdata = fonthashes.identifiers -local fontcharacters = fonthashes.characters -local fontitalics = fonthashes.italics - -local variables = interfaces.variables -local texsetattribute = tex.setattribute -local texgetattribute = tex.getattribute -local getfontoffamily = tex.getfontoffamily -local unsetvalue = attributes.unsetvalue -local implement = interfaces.implement - -local v_reset = variables.reset - -local chardata = characters.data - -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 enableaction = tasks.enableaction -local setaction = tasks.setaction - -local nodecodes = nodes.nodecodes -local noadcodes = nodes.noadcodes -local fencecodes = nodes.fencecodes - -local ordnoad_code = noadcodes.ord -local opdisplaylimitsnoad_code = noadcodes.opdisplaylimits -local oplimitsnoad_code = noadcodes.oplimits -local opnolimitsnoad_code = noadcodes.opnolimits -local binnoad_code = noadcodes.bin -local relnode_code = noadcodes.rel -local opennoad_code = noadcodes.open -local closenoad_code = noadcodes.close -local punctnoad_code = noadcodes.punct -local innernoad_code = noadcodes.inner -local undernoad_code = noadcodes.under -local overnoad_code = noadcodes.over -local vcenternoad_code = noadcodes.vcenter -local ordlimitsnoad_code = noadcodes.ordlimits or oplimitsnoad_code - -local noad_code = nodecodes.noad -- attr nucleus sub sup -local accent_code = nodecodes.accent -- attr nucleus sub sup accent -local radical_code = nodecodes.radical -- attr nucleus sub sup left degree -local fraction_code = nodecodes.fraction -- attr nucleus sub sup left right -local subbox_code = nodecodes.subbox -- attr list -local submlist_code = nodecodes.submlist -- attr list -local mathchar_code = nodecodes.mathchar -- attr fam char -local mathtextchar_code = nodecodes.mathtextchar -- attr fam char -local delimiter_code = nodecodes.delimiter -- attr small_fam small_char large_fam large_char ------ style_code = nodecodes.style -- attr style ------ parameter_code = nodecodes.parameter -- attr style -local math_choice = nodecodes.choice -- attr display text script scriptscript -local fence_code = nodecodes.fence -- attr subtype - -local leftfence_code = fencecodes.left -local middlefence_code = fencecodes.middle -local rightfence_code = fencecodes.right - --- local mathclasses = mathematics.classes --- local fenceclasses = { --- [leftfence_code] = mathclasses.open, --- [middlefence_code] = mathclasses.middle, --- [rightfence_code] = mathclasses.close, --- } - --- this initial stuff is tricky as we can have removed and new nodes with the same address --- the only way out is a free-per-page list of nodes (not bad anyway) - --- local gf = getfield local gt = setmetatableindex("number") getfield = function(n,f) gt[f] = gt[f] + 1 return gf(n,f) end mathematics.GETFIELD = gt --- local sf = setfield local st = setmetatableindex("number") setfield = function(n,f,v) st[f] = st[f] + 1 sf(n,f,v) end mathematics.SETFIELD = st - -local function process(start,what,n,parent) - - if n then - n = n + 1 - else - n = 0 - end - -- - local initial = start - -- - slide_nodes(start) -- we still miss a prev in noads -- fences test code - -- - while start do - local id = getid(start) - if trace_processing then - if id == noad_code then - report_processing("%w%S, class %a",n*2,nutstring(start),noadcodes[getsubtype(start)]) - elseif id == mathchar_code then - local char = getchar(start) - local font = getfont(start) - local fam = getfam(start) - report_processing("%w%S, family %a, font %a, char %a, shape %c",n*2,nutstring(start),fam,font,char,char) - else - report_processing("%w%S",n*2,nutstring(start)) - end - end - local proc = what[id] - if proc then - -- report_processing("start processing") - local done, newstart, newinitial = proc(start,what,n,parent) -- prev is bugged: or getprev(start) - if newinitial then - initial = newinitial -- temp hack .. we will make all return head - if newstart then - start = newstart - -- report_processing("stop processing (new start)") - else - -- report_processing("quit processing (done)") - break - end - else - if newstart then - start = newstart - -- report_processing("stop processing (new start)") - else - -- report_processing("stop processing") - end - end - elseif id == noad_code then - -- single characters are like this - local noad = getnucleus(start) if noad then process(noad,what,n,start) end -- list - noad = getsup (start) if noad then process(noad,what,n,start) end -- list - noad = getsub (start) if noad then process(noad,what,n,start) end -- list - if getsubpre then - noad = getsuppre (start) if noad then process(noad,what,n,start) end -- list - noad = getsubpre (start) if noad then process(noad,what,n,start) end -- list - end - elseif id == mathchar_code or id == mathtextchar_code or id == delimiter_code then - break - elseif id == subbox_code or id == submlist_code then - local noad = getlist(start) if noad then process(noad,what,n,start) end -- list (not getlist !) - elseif id == fraction_code then - local noad = getfield(start,"num") if noad then process(noad,what,n,start) end -- list - noad = getfield(start,"denom") if noad then process(noad,what,n,start) end -- list - noad = getfield(start,"left") if noad then process(noad,what,n,start) end -- delimiter - noad = getfield(start,"right") if noad then process(noad,what,n,start) end -- delimiter - elseif id == math_choice then - local noad = getfield(start,"display") if noad then process(noad,what,n,start) end -- list - noad = getfield(start,"text") if noad then process(noad,what,n,start) end -- list - noad = getfield(start,"script") if noad then process(noad,what,n,start) end -- list - noad = getfield(start,"scriptscript") if noad then process(noad,what,n,start) end -- list - elseif id == fence_code then - local noad = getfield(start,"delimiter") if noad then process(noad,what,n,start) end -- delimiter - elseif id == radical_code then - local noad = getnucleus(start) if noad then process(noad,what,n,start) end -- list - noad = getsup (start) if noad then process(noad,what,n,start) end -- list - noad = getsub (start) if noad then process(noad,what,n,start) end -- list - if getsubpre then - noad = getsuppre (start) if noad then process(noad,what,n,start) end -- list - noad = getsubpre (start) if noad then process(noad,what,n,start) end -- list - end - noad = getfield(start,"left") if noad then process(noad,what,n,start) end -- delimiter - noad = getfield(start,"degree") if noad then process(noad,what,n,start) end -- list - elseif id == accent_code then - local noad = getnucleus(start) if noad then process(noad,what,n,start) end -- list - noad = getsup (start) if noad then process(noad,what,n,start) end -- list - noad = getsub (start) if noad then process(noad,what,n,start) end -- list - if getsubpre then - noad = getsuppre (start) if noad then process(noad,what,n,start) end -- list - noad = getsubpre (start) if noad then process(noad,what,n,start) end -- list - end - noad = getfield(start,"accent") if noad then process(noad,what,n,start) end -- list - noad = getfield(start,"bot_accent") if noad then process(noad,what,n,start) end -- list - -- elseif id == style_code then - -- -- has a next - -- elseif id == parameter_code then - -- -- has a next - -- else - -- -- glue, penalty, etc - end - start = getnext(start) - end - if not parent then - return initial -- only first level -- for now - end -end - -local function processnested(current,what,n) - local noad = nil - local id = getid(current) - if id == noad_code then - noad = getnucleus(current) if noad then process(noad,what,n,current) end -- list - noad = getsup (current) if noad then process(noad,what,n,current) end -- list - noad = getsub (current) if noad then process(noad,what,n,current) end -- list - if getsubpre then - noad = getsuppre (current) if noad then process(noad,what,n,current) end -- list - noad = getsubpre (current) if noad then process(noad,what,n,current) end -- list - end - elseif id == subbox_code or id == submlist_code then - noad = getlist(current) if noad then process(noad,what,n,current) end -- list (not getlist !) - elseif id == fraction_code then - noad = getfield(current,"num") if noad then process(noad,what,n,current) end -- list - noad = getfield(current,"denom") if noad then process(noad,what,n,current) end -- list - noad = getfield(current,"left") if noad then process(noad,what,n,current) end -- delimiter - noad = getfield(current,"right") if noad then process(noad,what,n,current) end -- delimiter - elseif id == math_choice then - noad = getfield(current,"display") if noad then process(noad,what,n,current) end -- list - noad = getfield(current,"text") if noad then process(noad,what,n,current) end -- list - noad = getfield(current,"script") if noad then process(noad,what,n,current) end -- list - noad = getfield(current,"scriptscript") if noad then process(noad,what,n,current) end -- list - elseif id == fence_code then - noad = getfield(current,"delimiter") if noad then process(noad,what,n,current) end -- delimiter - elseif id == radical_code then - noad = getnucleus(current) if noad then process(noad,what,n,current) end -- list - noad = getsup (current) if noad then process(noad,what,n,current) end -- list - noad = getsub (current) if noad then process(noad,what,n,current) end -- list - if getsubpre then - noad = getsuppre (current) if noad then process(noad,what,n,current) end -- list - noad = getsubpre (current) if noad then process(noad,what,n,current) end -- list - end - noad = getfield(current,"left") if noad then process(noad,what,n,current) end -- delimiter - noad = getfield(current,"degree") if noad then process(noad,what,n,current) end -- list - elseif id == accent_code then - noad = getnucleus(current) if noad then process(noad,what,n,current) end -- list - noad = getsup (current) if noad then process(noad,what,n,current) end -- list - noad = getsub (current) if noad then process(noad,what,n,current) end -- list - if getsubpre then - noad = getsuppre (current) if noad then process(noad,what,n,current) end -- list - noad = getsubpre (current) if noad then process(noad,what,n,current) end -- list - end - noad = getfield(current,"accent") if noad then process(noad,what,n,current) end -- list - noad = getfield(current,"bot_accent") if noad then process(noad,what,n,current) end -- list - end -end - -local function processstep(current,process,n,id) - local noad = nil - local id = id or getid(current) - if id == noad_code then - noad = getnucleus(current) if noad then process(noad,n,current) end -- list - noad = getsup (current) if noad then process(noad,n,current) end -- list - noad = getsub (current) if noad then process(noad,n,current) end -- list - if getsubpre then - noad = getsuppre (current) if noad then process(noad,n,current) end -- list - noad = getsubpre (current) if noad then process(noad,n,current) end -- list - end - elseif id == subbox_code or id == submlist_code then - noad = getlist(current) if noad then process(noad,n,current) end -- list (not getlist !) - elseif id == fraction_code then - noad = getfield(current,"num") if noad then process(noad,n,current) end -- list - noad = getfield(current,"denom") if noad then process(noad,n,current) end -- list - noad = getfield(current,"left") if noad then process(noad,n,current) end -- delimiter - noad = getfield(current,"right") if noad then process(noad,n,current) end -- delimiter - elseif id == math_choice then - noad = getfield(current,"display") if noad then process(noad,n,current) end -- list - noad = getfield(current,"text") if noad then process(noad,n,current) end -- list - noad = getfield(current,"script") if noad then process(noad,n,current) end -- list - noad = getfield(current,"scriptscript") if noad then process(noad,n,current) end -- list - elseif id == fence_code then - noad = getfield(current,"delimiter") if noad then process(noad,n,current) end -- delimiter - elseif id == radical_code then - noad = getnucleus(current) if noad then process(noad,n,current) end -- list - noad = getsup (current) if noad then process(noad,n,current) end -- list - noad = getsub (current) if noad then process(noad,n,current) end -- list - if getsubpre then - noad = getsuppre (current) if noad then process(noad,n,current) end -- list - noad = getsubpre (current) if noad then process(noad,n,current) end -- list - end - noad = getfield(current,"left") if noad then process(noad,n,current) end -- delimiter - noad = getfield(current,"degree") if noad then process(noad,n,current) end -- list - elseif id == accent_code then - noad = getnucleus(current) if noad then process(noad,n,current) end -- list - noad = getsup (current) if noad then process(noad,n,current) end -- list - noad = getsub (current) if noad then process(noad,n,current) end -- list - if getsubpre then - noad = getsuppre (current) if noad then process(noad,n,current) end -- list - noad = getsubpre (current) if noad then process(noad,n,current) end -- list - end - noad = getfield(current,"accent") if noad then process(noad,n,current) end -- list - noad = getfield(current,"bot_accent") if noad then process(noad,n,current) end -- list - end -end - -local function processnoads(head,actions,banner) - if trace_processing then - report_processing("start %a",banner) - head = process(head,actions) - report_processing("stop %a",banner) - else - head = process(head,actions) - end - return head -end - -noads.process = processnoads -noads.processnested = processnested -noads.processouter = process - --- experiment (when not present fall back to fam 0) -- needs documentation - -local unknowns = { } -local checked = { } -- simple case -local tracked = false trackers.register("fonts.missing", function(v) tracked = v end) -local cached = setmetatableindex("table") -- complex case - -local function errorchar(font,char) - local done = unknowns[char] - if done then - unknowns[char] = done + 1 - else - unknowns[char] = 1 - end - if tracked then - -- slower as we check each font too and we always replace as math has - -- more demands than text - local fake = cached[font][char] - if fake then - return fake - else - local kind, fake = fonts.checkers.placeholder(font,char) - if not fake or kind ~= "char" then - fake = 0x3F - end - cached[font][char] = fake - return fake - end - else - -- only simple checking, report at the end so one should take - -- action anyway ... we can miss a few checks but that is ok - -- as there is at least one reported - if not checked[char] then - if trace_normalizing then - report_normalizing("character %C is not available",char) - end - checked[char] = true - end - return 0x3F - end -end - --- 0-2 regular --- 3-5 bold --- 6-8 pseudobold - --- this could best be integrated in the remapper, and if we run into problems, we --- might as well do this - -do - - local families = { } - local a_mathfamily = privateattribute("mathfamily") - local boldmap = mathematics.boldmap - - local familymap = { [0] = - "regular", - "regular", - "regular", - "bold", - "bold", - "bold", - "pseudobold", - "pseudobold", - "pseudobold", - } - - families[fraction_code] = function(pointer,what,n,parent) - local a = getattr(pointer,a_mathfamily) - if a and a >= 0 then - if a > 0 then - setattr(pointer,a_mathfamily,0) - if a > 5 then - a = a - 3 - end - end - setfam(pointer,a) - end - processnested(pointer,families,n+1) - end - - families[noad_code] = function(pointer,what,n,parent) - local a = getattr(pointer,a_mathfamily) - if a and a >= 0 then - if a > 0 then - setattr(pointer,a_mathfamily,0) - if a > 5 then - a = a - 3 - end - end - setfam(pointer,a) - end - processnested(pointer,families,n+1) - end - - families[mathchar_code] = function(pointer) - if getfam(pointer) == 0 then - local a = getattr(pointer,a_mathfamily) - if a and a > 0 then - setattr(pointer,a_mathfamily,0) - if a > 5 then - local char = getchar(pointer) - local bold = boldmap[char] - local newa = a - 3 - if not bold then - if trace_families then - report_families("no bold replacement for %C, family %s with remap %s becomes %s with remap %s",char,a,familymap[a],newa,familymap[newa]) - end - setfam(pointer,newa) - elseif not fontcharacters[getfontoffamily(newa)][bold] then - if trace_families then - report_families("no bold character for %C, family %s with remap %s becomes %s with remap %s",char,a,familymap[a],newa,familymap[newa]) - end - if newa > 3 then - setfam(pointer,newa-3) - end - else - setattr(pointer,a_exportstatus,char) - setchar(pointer,bold) - if trace_families then - report_families("replacing %C by bold %C, family %s with remap %s becomes %s with remap %s",char,bold,a,familymap[a],newa,familymap[newa]) - end - setfam(pointer,newa) - end - else - local char = getchar(pointer) - if not fontcharacters[getfontoffamily(a)][char] then - if trace_families then - report_families("no bold replacement for %C",char) - end - else - if trace_families then - report_families("family of %C becomes %s with remap %s",char,a,familymap[a]) - end - setfam(pointer,a) - end - end - end - end - end - families[delimiter_code] = function(pointer) - if getfield(pointer,"small_fam") == 0 then - local a = getattr(pointer,a_mathfamily) - if a and a > 0 then - setattr(pointer,a_mathfamily,0) - if a > 5 then - -- no bold delimiters in unicode - a = a - 3 - end - local char = getfield(pointer,"small_char") - local okay = fontcharacters[getfontoffamily(a)][char] - if okay then - setfield(pointer,"small_fam",a) - elseif a > 2 then - setfield(pointer,"small_fam",a-3) - end - local char = getfield(pointer,"large_char") - local okay = fontcharacters[getfontoffamily(a)][char] - if okay then - setfield(pointer,"large_fam",a) - elseif a > 2 then - setfield(pointer,"large_fam",a-3) - end - else - setfield(pointer,"small_fam",0) - setfield(pointer,"large_fam",0) - end - end - end - - -- will become: - - -- families[delimiter_code] = function(pointer) - -- if getfam(pointer) == 0 then - -- local a = getattr(pointer,a_mathfamily) - -- if a and a > 0 then - -- setattr(pointer,a_mathfamily,0) - -- if a > 5 then - -- -- no bold delimiters in unicode - -- a = a - 3 - -- end - -- local char = getchar(pointer) - -- local okay = fontcharacters[getfontoffamily(a)][char] - -- if okay then - -- setfam(pointer,a) - -- elseif a > 2 then - -- setfam(pointer,a-3) - -- end - -- else - -- setfam(pointer,0) - -- end - -- end - -- end - - families[mathtextchar_code] = families[mathchar_code] - - function handlers.families(head,style,penalties) - processnoads(head,families,"families") - return true -- not needed - end - -end - --- character remapping - -do - - local a_mathalphabet = privateattribute("mathalphabet") - local a_mathgreek = privateattribute("mathgreek") - - local relocate = { } - - local remapalphabets = mathematics.remapalphabets - local fallbackstyleattr = mathematics.fallbackstyleattr - local setnodecolor = colortracers.set - - local function report_remap(tag,id,old,new,extra) - report_remapping("remapping %s in font (%s,%s) from %C to %C%s", - tag,id,fontdata[id].properties.fontname or "",old,new,extra) - end - - local function checked(pointer) - local char = getchar(pointer) - local font = getfont(pointer) - local data = fontcharacters[font] - if not data[char] then - local specials = characters.data[char].specials - if specials and (specials[1] == "char" or specials[1] == "font") then - local newchar = specials[#specials] - if trace_remapping then - report_remap("fallback",font,char,newchar) - end - if trace_analyzing then - setnodecolor(pointer,"font:isol") - end - setattr(pointer,a_exportstatus,char) -- testcase: exponentiale - setchar(pointer,newchar) - return true - end - end - end - - -- We can optimize this if we really think that math is a bottleneck which it never - -- really is. - - relocate[mathchar_code] = function(pointer) - local g = getattr(pointer,a_mathgreek) or 0 - local a = getattr(pointer,a_mathalphabet) or 0 - -- local g, a = getattrs(pointer,a_mathgreek,a_mathalphabet) - -- if not a then a = 0 end - -- if not g then g = 0 end - local char = getchar(pointer) - local font = getfont(pointer) - local characters = fontcharacters[font] - if a > 0 or g > 0 then - if a > 0 then - -- not really critital but we could use properties - setattr(pointer,a_mathgreek,0) - end - if g > 0 then - -- not really critital but we could use properties - setattr(pointer,a_mathalphabet,0) - end - local newchar = remapalphabets(char,a,g) - if newchar then - local newchardata = characters[newchar] - if newchardata then - if trace_remapping then - report_remap("char",font,char,newchar,newchardata.commands and " (virtual)" or "") - end - if trace_analyzing then - setnodecolor(pointer,"font:isol") - end - setchar(pointer,newchar) - return true - else - local fallback = fallbackstyleattr(a) - if fallback then - local newchar = remapalphabets(char,fallback,g) - if newchar then - if characters[newchar] then - if trace_remapping then - report_remap("char",font,char,newchar," (fallback remapping used)") - end - if trace_analyzing then - setnodecolor(pointer,"font:isol") - end - setchar(pointer,newchar) - return true - elseif trace_remapping then - report_remap("char",font,char,newchar," fails (no fallback character)") - end - elseif trace_remapping then - report_remap("char",font,char,newchar," fails (no fallback remap character)") - end - elseif trace_remapping then - report_remap("char",font,char,newchar," fails (no fallback style)") - end - end - elseif trace_remapping then - local chardata = characters[char] - if chardata and chardata.commands then - report_remap("char",font,char,char," (virtual)") - end - end - end - if not characters[char] then - setchar(pointer,errorchar(font,char)) - end - if trace_analyzing then - setnodecolor(pointer,"font:medi") - end - if check_coverage then - return checked(pointer) - end - end - - relocate[mathtextchar_code] = function(pointer) - if trace_analyzing then - setnodecolor(pointer,"font:init") - end - end - - relocate[delimiter_code] = function(pointer) - if trace_analyzing then - setnodecolor(pointer,"font:fina") - end - end - - function handlers.relocate(head,style,penalties) - processnoads(head,relocate,"relocate") - return true -- not needed - end - -end - --- rendering (beware, not exported) - -do - - local render = { } - - local rendersets = mathematics.renderings.numbers or { } -- store - - render[mathchar_code] = function(pointer) - local attr = getattr(pointer,a_mathrendering) - if attr and attr > 0 then - local char = getchar(pointer) - local renderset = rendersets[attr] - if renderset then - local newchar = renderset[char] - if newchar then - local font = getfont(pointer) - local characters = fontcharacters[font] - if characters and characters[newchar] then - setchar(pointer,newchar) - setattr(pointer,a_exportstatus,char) - end - end - end - end - end - - function handlers.render(head,style,penalties) - processnoads(head,render,"render") - return true -- not needed - end - -end - --- some resize options (this works ok because the content is --- empty and no larger next will be forced) --- --- beware: we don't use \delcode but \Udelcode and as such have --- no large_fam; also, we need to check for subtype and/or --- small_fam not being 0 because \. sits in 0,0 by default --- --- todo: just replace the character by an ord noad --- and remove the right delimiter as well - -do - - local a_mathsize = privateattribute("mathsize") -- this might move into other fence code - local resize = { } - - resize[fence_code] = function(pointer) - local subtype = getsubtype(pointer) - if subtype == leftfence_code or subtype == rightfence_code then - local a = getattr(pointer,a_mathsize) - if a and a > 0 then - local method = div(a,100) - local size = a % 100 - setattr(pointer,a_mathsize,0) - local delimiter = getfield(pointer,"delimiter") - local chr = getchar(delimiter) - if chr > 0 then - local fam = getfam(delimiter) - local id = getfontoffamily(fam) - if id > 0 then - local data = fontdata[id] - local char = mathematics.big(data,chr,size,method) - local ht = getheight(pointer) - local dp = getdepth(pointer) - if ht == 1 or dp == 1 then -- 1 scaled point is a signal - local chardata = data.characters[char] - if ht == 1 then - setheight(pointer,chardata.height) - end - if dp == 1 then - setdepth(pointer,chardata.depth) - end - end - if trace_fences then - report_fences("replacing %C by %C using method %a and size %a",chr,char,method,size) - end - setchar(delimiter,char) - end - end - end - end - end - - function handlers.resize(head,style,penalties) - processnoads(head,resize,"resize") - return true -- not needed - end - -end - --- still not perfect: - -do - - local a_autofence = privateattribute("mathautofence") - local autofences = { } - local dummyfencechar = 0x2E - - local function makefence(what,char) - local d = new_delimiter() -- todo: attr - local f = new_fence() -- todo: attr - if char then - local sym = getnucleus(char) - local chr = getchar(sym) - local fam = getfam(sym) - if chr == dummyfencechar then - chr = 0 - end - setchar(d,chr) - setfam(d,fam) - flush_node(sym) - end - setattrlist(d,char) - setattrlist(f,char) - setsubtype(f,what) - setfield(f,"delimiter",d) - setfield(f,"class",-1) -- tex itself does this, so not fenceclasses[what] - return f - end - - local function show(where,pointer) - print("") - local i = 0 - for n in nuts.traverse(pointer) do - i = i + 1 - print(i,where,nuts.tonode(n)) - end - print("") - end - - local function makelist(middle,noad,f_o,o_next,c_prev,f_c) --- report_fences( --- "middle %s, noad %s, open %s, opennext %s, closeprev %s, close %s", --- middle or "?", --- noad or "?", --- f_o or "?", --- o_next or "?", --- c_prev or "?", --- f_c or "?" --- ) - local list = new_submlist() - setsubtype(noad,innernoad_code) - setnucleus(noad,list) - setlist(list,f_o) - setlink(f_o,o_next) -- prev of list is nil - setlink(c_prev,f_c) -- next of list is nil --- show("list",f_o) - if middle and next(middle) then - local prev = f_o - local current = o_next - while current ~= f_c do - local midl = middle[current] - local next = getnext(current) - if midl then - local fence = makefence(middlefence_code,current) - setnucleus(current) - flush_node(current) - middle[current] = nil - -- replace_node - setlink(prev,fence,next) - prev = fence - else - prev = current - end - current = next - end - end - return noad - end - - -- relinking is now somewhat overdone - - local function convert_both(open,close,middle) - local o_next = getnext(open) - if o_next == close then - return close - else - local c_prev, c_next = getboth(close) - local f_o = makefence(leftfence_code,open) - local f_c = makefence(rightfence_code,close) - makelist(middle,open,f_o,o_next,c_prev,f_c) - setnucleus(close) - flush_node(close) - -- open is now a list - setlink(open,c_next) - return open - end - end - - local function convert_open(open,last,middle) -- last is really last (final case) - local f_o = makefence(leftfence_code,open) - local f_c = makefence(rightfence_code) - local o_next = getnext(open) - makelist(middle,open,f_o,o_next,last,nil) - -- open is now a list - setlink(open,l_next) - return open - end - - local function convert_close(first,close,middle) - local f_o = makefence(leftfence_code) - local f_c = makefence(rightfence_code,close) - local c_prev = getprev(close) - local f_next = getnext(first) - makelist(middle, close, f_o,f_next,c_prev,f_c) - -- close is now a list - if c_prev ~= first then - setlink(first,close) - end - return close - end - - local stacks = setmetatableindex("table") - - -- 1=open 2=close 3=middle 4=both - - local function processfences(pointer,n,parent) - local current = pointer - local last = pointer - local start = pointer - local done = false - local initial = pointer - local stack = nil - local middle = nil -- todo: use properties - while current do --- show("before",pointer) - local id = getid(current) - if id == noad_code then - local a = getattr(current,a_autofence) - if a and a > 0 then - local stack = stacks[n] - setattr(current,a_autofence,0) -- hm, better use a property - local level = #stack - if a == 1 then - if trace_fences then - report_fences("%2i: level %i, handling %s, action %s",n,level,"open","open") - end - insert(stack,current) - elseif a == 2 then - local open = remove(stack) - if open then - if trace_fences then - report_fences("%2i: level %i, handling %s, action %s",n,level,"close","both") - end - current = convert_both(open,current,middle) - elseif current == start then - if trace_fences then - report_fences("%2i: level %i, handling %s, action %s",n,level,"close","skip") - end - else - if trace_fences then - report_fences("%2i: level %i, handling %s, action %s",n,level,"close","close") - end - current = convert_close(initial,current,middle) - if not parent then - initial = current - end - end - elseif a == 3 then - if trace_fences then - report_fences("%2i: level %i, handling %s, action %s",n,level,"middle","middle") - end - if middle then - middle[current] = last - else - middle = { [current] = last } - end - elseif a == 4 then - if not stack or #stack == 0 then - if trace_fences then - report_fences("%2i: level %i, handling %s, action %s",n,level,"both","open") - end - insert(stack,current) - else - local open = remove(stack) - if open then - if trace_fences then - report_fences("%2i: level %i, handling %s, action %s",n,level,"both","both") - end - current = convert_both(open,current,middle) - elseif current == start then - if trace_fences then - report_fences("%2i: level %i, handling %s, action %s",n,level,"both","skip") - end - else - if trace_fences then - report_fences("%2i: level %i, handling %s, action %s",n,level,"both","close") - end - current = convert_close(initial,current,middle) - if not parent then - initial = current - end - end - end - end - done = true - else - processstep(current,processfences,n+1,id) - end - else - -- next at current level - processstep(current,processfences,n,id) - end --- show("after",pointer) - last = current - current = getnext(current) - end - if done then - local stack = stacks[n] - local s = #stack - if s > 0 then - for i=1,s do - local open = remove(stack) - if trace_fences then - report_fences("%2i: level %i, handling %s, action %s",n,#stack,"flush","open") - end - last = convert_open(open,last,middle) - end --- show("done",pointer) - end - end - end - - -- we can have a first changed node .. an option is to have a leading dummy node in math - -- lists like the par node as it can save a lot of mess - - local enabled = false - - implement { - name = "enableautofences", - onlyonce = true, - actions = function() - enableaction("math","noads.handlers.autofences") - enabled = true - end - } - - function handlers.autofences(head,style,penalties) - if enabled then -- tex.modes.c_math_fences_auto - -- inspect(nodes.totree(head)) - processfences(head,1) - -- inspect(nodes.totree(head)) - end - end - -end - --- normalize scripts - -do - - local unscript = { } noads.processors.unscript = unscript - local superscripts = characters.superscripts - local subscripts = characters.subscripts - local fractions = characters.fractions - local replaced = { } - - local function replace(pointer,what,n,parent) - pointer = parent -- we're following the parent list (chars trigger this) - local next = getnext(pointer) - local start_super, stop_super, start_sub, stop_sub - local mode = "unset" - while next and getid(next) == noad_code do - local nextnucleus = getnucleus(next) - if nextnucleus and getid(nextnucleus) == mathchar_code and not getsub(next) and not getsup(next) then - local char = getchar(nextnucleus) - 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 = getnext(next) - setchar(nextnucleus,s) - replaced[char] = (replaced[char] or 0) + 1 - if trace_normalizing then - report_normalizing("superscript %C becomes %C",char,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 = getnext(next) - setchar(nextnucleus,s) - replaced[char] = (replaced[char] or 0) + 1 - if trace_normalizing then - report_normalizing("subscript %C becomes %C",char,s) - end - else - break - end - end - else - break - end - end - if start_super then - if start_super == stop_super then - setsup(pointer,getnucleus(start_super)) - else - local list = new_submlist() -- todo attr - setlist(list,start_super) - setsup(pointer,list) - end - if mode == "super" then - setnext(pointer,getnext(stop_super)) - end - setnext(stop_super) - end - if start_sub then - --- if mode == "sub" then --- local sup = getsup(pointer) --- if sup and not getsub(pointer) then --- local nxt = getnext(pointer) --- local new = new_noad(pointer) --- setnucleus(new,new_submlist()) --- setlink(pointer,new,nxt) --- pointer = new --- end --- end - - if start_sub == stop_sub then - setsub(pointer,getnucleus(start_sub)) - else - local list = new_submlist() -- todo attr - setlist(list,start_sub) - setsub(pointer,list) - end - if mode == "sub" then - setnext(pointer,getnext(stop_sub)) - end - setnext(stop_sub) - end - -- we could return stop - end - - unscript[mathchar_code] = replace -- not noads as we need to recurse - - function handlers.unscript(head,style,penalties) - processnoads(head,unscript,"unscript") - return true -- not needed - end - -end - -do - - local unstack = { } noads.processors.unstack = unstack - local enabled = false - local a_unstack = privateattribute("mathunstack") - - unstack[noad_code] = function(pointer) - if getattr(pointer,a_unstack) then - local sup = getsup(pointer) - local sub = getsub(pointer) - if sup and sub then - -- if trace_unstacking then - -- report_unstacking() -- todo ... what to show ... - -- end - local nxt = getnext(pointer) - local new = new_noad(pointer) - setnucleus(new,new_submlist()) - setsub(pointer) - setsub(new,sub) - setlink(pointer,new,nxt) - end - end - end - - function handlers.unstack(head,style,penalties) - if enabled then - processnoads(head,unstack,"unstack") - return true -- not needed - end - end - - implement { - name = "enablescriptunstacking", - onlyonce = true, - actions = function() - enableaction("math","noads.handlers.unstack") - enabled = true - end - } - -end - -do - - local function collected(list) - if list and next(list) then - local n, t = 0, { } - for k, v in sortedhash(list) do - n = n + 1 - t[n] = formatters["%C"](k) - end - return formatters["% t (n=%s)"](t,n) - end - end - - statistics.register("math script replacements", function() - return collected(replaced) - end) - - statistics.register("unknown math characters", function() - return collected(unknowns) - end) - -end - --- math alternates: (in xits lgf: $ABC$ $\cal ABC$ $\mathalternate{cal}\cal ABC$) --- math alternates: (in lucidaot lgf: $ABC \mathalternate{italic} ABC$) - --- todo: set alternate for specific symbols --- todo: no need to do this when already loaded --- todo: use a fonts.hashes.mathalternates - -do - - local last = 0 - - local known = setmetatableindex(function(t,k) - local v = bor(0,2^last) - t[k] = v - last = last + 1 - return v - end) - - local defaults = { - dotless = { feature = 'dtls', value = 1, comment = "Mathematical Dotless Forms" }, - -- zero = { feature = 'zero', value = 1, comment = "Slashed or Dotted Zero" }, -- in no math font (yet) - } - - local function initializemathalternates(tfmdata) - local goodies = tfmdata.goodies - local autolist = defaults -- table.copy(defaults) - - local function setthem(newalternates) - local resources = tfmdata.resources -- was tfmdata.shared - local mathalternates = resources.mathalternates - local alternates, attributes, registered, presets - if mathalternates then - alternates = mathalternates.alternates - attributes = mathalternates.attributes - registered = mathalternates.registered - else - alternates, attributes, registered = { }, { }, { } - mathalternates = { - attributes = attributes, - alternates = alternates, - registered = registered, - presets = { }, - resets = { }, - hashes = setmetatableindex("table") - } - resources.mathalternates = mathalternates - end - -- - for name, data in sortedhash(newalternates) do - if alternates[name] then - -- ignore - else - local attr = known[name] - attributes[attr] = data - alternates[name] = attr - registered[#registered+1] = attr - end - end - end - - if goodies then - local done = { } - 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 - if trace_goodies then - report_goodies("loading alternates for font %a",tfmdata.properties.name) - end - for k, v in next, autolist do - if not alternates[k] then - alternates[k] = v - end - end - setthem(alternates) - return - end - end - end - - if trace_goodies then - report_goodies("loading default alternates for font %a",tfmdata.properties.name) - end - setthem(autolist) - - end - - registerotffeature { - name = "mathalternates", - description = "additional math alternative shapes", - initializers = { - base = initializemathalternates, - node = initializemathalternates, - } - } - - -- local getalternate = otf.getalternate (runtime new method so ...) - - -- todo: not shared but copies ... one never knows - - local a_mathalternate = privateattribute("mathalternate") - local alternate = { } -- processors.alternate = alternate - local fontdata = fonts.hashes.identifiers - local fontresources = fonts.hashes.resources - - local function getalternate(fam,tag,current) - local resources = fontresources[getfontoffamily(fam)] - local attribute = unsetvalue - if resources then - local mathalternates = resources.mathalternates - if mathalternates then - local presets = mathalternates.presets - if presets then - local resets = mathalternates.resets - attribute = presets[tag] - if not attribute then - attribute = 0 - local alternates = mathalternates.alternates - for s in gmatch(tag,"[^, ]+") do - if s == v_reset then - resets[tag] = true - current = unsetvalue - else - local a = alternates[s] -- or known[s] - if a then - attribute = bor(attribute,a) - end - end - end - if attribute == 0 then - attribute = unsetvalue - end - presets[tag] = attribute - elseif resets[tag] then - current = unsetvalue - end - end - end - end - if attribute > 0 and current and current > 0 then - return bor(current,attribute) - else - return attribute - end - end - - local function presetalternate(fam,tag) - texsetattribute(a_mathalternate,getalternate(fam,tag)) - end - - implement { - name = "presetmathalternate", - actions = presetalternate, - arguments = { "integer", "string" } - } - - local function setalternate(fam,tag) - local a = texgetattribute(a_mathalternate) - local v = getalternate(fam,tag,a) - texsetattribute(a_mathalternate,v) - end - - implement { - name = "setmathalternate", - actions = setalternate, - arguments = { "integer", "string" } - } - - alternate[mathchar_code] = function(pointer) -- slow - local a = getattr(pointer,a_mathalternate) - if a and a > 0 then - setattr(pointer,a_mathalternate,0) - local fontid = getfont(pointer) - local resources = fontresources[fontid] - if resources then - local mathalternates = resources.mathalternates - if mathalternates then - local attributes = mathalternates.attributes - local registered = mathalternates.registered - local hashes = mathalternates.hashes - for i=1,#registered do - local r = registered[i] - if band(a,r) ~= 0 then - local char = getchar(pointer) - local alt = hashes[i][char] - if alt == nil then - local what = attributes[r] - alt = otf.getalternate(fontdata[fontid],char,what.feature,what.value) or false - if alt == char then - alt = false - end - hashes[i][char] = alt - end - if alt then - if trace_alternates then - local what = attributes[r] - report_alternates("alternate %a, value %a, replacing glyph %U by glyph %U", - tostring(what.feature),tostring(what.value),getchar(pointer),alt) - end - setchar(pointer,alt) - break - end - end - end - end - end - end - end - - function handlers.alternates(head,style,penalties) - processnoads(head,alternate,"alternate") - return true -- not needed - end - -end - --- italics: we assume that only characters matter --- --- = we check for correction first because accessing nodes is slower --- = the actual glyph is not that important (we can control it with numbers) - --- Italic correction in luatex math is (was) a mess. There are all kind of assumptions based on --- old fonts and new fonts. Eventually there should be a flag that can signal to ignore all --- those heuristics. We want to deal with it ourselves also in the perspective of mixed math --- and text. Also, for a while in context we had to deal with a mix of virtual math fonts and --- real ones. - --- in opentype the italic correction of a limop is added to the width and luatex does --- some juggling that we want to avoid but we need to do something here (in fact, we could --- better fix the width of the character) - -do - - local a_mathitalics = privateattribute("mathitalics") - - local italics = { } - local default_factor = 1/20 - - local setcolor = colortracers.set - local resetcolor = colortracers.reset - local italic_kern = new_kern - - local c_positive_d = "trace:dg" - local c_negative_d = "trace:dr" - - local function insert_kern(current,kern) - local sub = new_submlist() -- todo: attr - local noad = new_noad() -- todo: attr - setlist(sub,kern) - setnext(kern,noad) - setnucleus(noad,current) - return sub - end - - registertracker("math.italics.visualize", function(v) - if v then - italic_kern = function(k) - local n = new_kern(k) -- todo: attr - set_visual(n,"italic") - return n - end - else - italic_kern = new_kern - end - end) - - local function getcorrection(method,font,char) -- -- or character.italic -- (this one is for tex) - - local visual = chardata[char].visual - - if method == 1 then - -- check on state - local italics = fontitalics[font] - if italics then - local character = fontcharacters[font][char] - if character then - local correction = character.italic - if correction and correction ~= 0 then - return correction, visual - end - end - end - elseif method == 2 then - -- no check - local character = fontcharacters[font][char] - if character then - local correction = character.italic - if correction and correction ~= 0 then - return correction, visual - end - end - elseif method == 3 then - -- check on visual - if visual == "it" or visual == "bi" then - local character = fontcharacters[font][char] - if character then - local correction = character.italic - if correction and correction ~= 0 then - return correction, visual - end - end - end - elseif method == 4 then - -- combination of 1 and 3 - local italics = fontitalics[font] - if italics and (visual == "it" or visual == "bi") then - local character = fontcharacters[font][char] - if character then - local correction = character.italic - if correction and correction ~= 0 then - return correction, visual - end - end - end - end - - end - - italics[mathchar_code] = function(pointer,what,n,parent) - local method = getattr(pointer,a_mathitalics) - if method and method > 0 and method < 100 then - local char = getchar(pointer) - local font = getfont(pointer) - local correction, visual = getcorrection(method,font,char) - if correction and correction ~= 0 then - local next_noad = getnext(parent) - if not next_noad then - if n == 1 then - -- only at the outer level .. will become an option (always,endonly,none) - if trace_italics then - report_italics("method %a, flagging italic correction %p between %C and end math",method,correction,char) - end - if correction > 0 then - correction = correction + 100 - else - correction = correction - 100 - end - correction = round(correction) - setattr(pointer,a_mathitalics,correction) - setattr(parent,a_mathitalics,correction) - return -- so no reset later on - end - end - end - end - setattr(pointer,a_mathitalics,unsetvalue) - end - - function handlers.italics(head,style,penalties) - processnoads(head,italics,"italics") - return true -- not needed - end - - local enable = function() - enableaction("math", "noads.handlers.italics") - if trace_italics then - report_italics("enabling math italics") - end - -- we enable math (unless already enabled elsewhere) - typesetters.italics.enablemath() - enable = false - end - - -- best do this only on math mode (less overhead) - - function mathematics.setitalics(name) - if enable then - enable() - end - texsetattribute(a_mathitalics,name and name ~= v_reset and tonumber(name) or unsetvalue) -- maybe also v_none - end - - function mathematics.getitalics(name) - if enable then - enable() - end - context(name and name ~= v_reset and tonumber(name) or unsetvalue) - end - - function mathematics.resetitalics() - texsetattribute(a_mathitalics,unsetvalue) - end - - implement { - name = "initializemathitalics", - actions = enable, - onlyonce = true, - } - - implement { - name = "setmathitalics", - actions = mathematics.setitalics, - arguments = "string", - } - - implement { - name = "getmathitalics", - actions = mathematics.getitalics, - arguments = "string", - } - - implement { - name = "resetmathitalics", - actions = mathematics.resetitalics - } - -end - -do - - -- math kerns (experiment) in goodies: - -- - -- mathematics = { - -- kernpairs = { - -- [0x1D44E] = { - -- [0x1D44F] = 400, -- 𝑎𝑏 - -- } - -- }, - -- } - - local a_kernpairs = privateattribute("mathkernpairs") - local kernpairs = { } - - local function enable() - enableaction("math", "noads.handlers.kernpairs") - if trace_kernpairs then - report_kernpairs("enabling math kern pairs") - end - enable = false - end - - implement { - name = "initializemathkernpairs", - actions = enable, - onlyonce = true, - } - - local hash = setmetatableindex(function(t,font) - local g = fontdata[font].goodies - local m = g and g[1] and g[1].mathematics - local k = m and m.kernpairs - t[font] = k - return k - end) - - -- no correction after prime because that moved to a superscript - - kernpairs[mathchar_code] = function(pointer,what,n,parent) - if getattr(pointer,a_kernpairs) == 1 then - local font = getfont(pointer) - local list = hash[font] - if list then - local first = getchar(pointer) - local found = list[first] - if found then - local next = getnext(parent) - if next and getid(next) == noad_code then - pointer = getnucleus(next) - if pointer then - if getfont(pointer) == font then - local second = getchar(pointer) - local kern = found[second] - if kern then - kern = kern * fonts.hashes.parameters[font].hfactor - if trace_kernpairs then - report_kernpairs("adding %p kerning between %C and %C",kern,first,second) - end - setlink(parent,new_kern(kern),getnext(parent)) -- todo: attr - end - end - end - end - end - end - end - end - - function handlers.kernpairs(head,style,penalties) - processnoads(head,kernpairs,"kernpairs") - return true -- not needed - end - -end - --- primes and such - -do - - -- is validpair stil needed? - - local a_mathcollapsing = privateattribute("mathcollapsing") - local collapse = { } - local mathlists = characters.mathlists - local validpair = { - [ordnoad_code] = true, - [opdisplaylimitsnoad_code] = true, - [oplimitsnoad_code] = true, - [opnolimitsnoad_code] = true, - [binnoad_code] = true, -- new - [relnode_code] = true, - [opennoad_code] = true, -- new - [closenoad_code] = true, -- new - [punctnoad_code] = true, -- new - [innernoad_code] = false, - [undernoad_code] = false, - [overnoad_code] = false, - [vcenternoad_code] = false, - [ordlimitsnoad_code] = true, - } - - local reported = setmetatableindex("table") - - collapse[mathchar_code] = function(pointer,what,n,parent) - - if parent and mathlists[getchar(pointer)] then - local found, last, lucleus, lsup, lsub, category - local tree = mathlists - local current = parent - while current and validpair[getsubtype(current)] do - local nucleus = getnucleus(current) -- == pointer - local sub = getsub(current) - local sup = getsup(current) - local char = getchar(nucleus) - if char then - local match = tree[char] - if match then - local method = getattr(current,a_mathcollapsing) - if method and method > 0 and method <= 3 then - local specials = match.specials - local mathlist = match.mathlist - local ligature - if method == 1 then - ligature = specials - elseif method == 2 then - ligature = specials or mathlist - else -- 3 - ligature = mathlist or specials - end - if ligature then - category = mathlist and "mathlist" or "specials" - found = ligature - last = current - lucleus = nucleus - lsup = sup - lsub = sub - end - tree = match - if sub or sup then - break - else - current = getnext(current) - end - else - break - end - else - break - end - else - break - end - end - if found and last and lucleus then - local id = getfont(lucleus) - local characters = fontcharacters[id] - local replace = characters and characters[found] - if not replace then - if not reported[id][found] then - reported[id][found] = true - report_collapsing("%s ligature %C from %s","ignoring",found,category) - end - elseif trace_collapsing then - report_collapsing("%s ligature %C from %s","creating",found,category) - end - setchar(pointer,found) - local l = getnext(last) - local c = getnext(parent) - if lsub then - setsub(parent,lsub) - setsub(last) - end - if lsup then - setsup(parent,lsup) - setsup(last) - end - while c ~= l do - local n = getnext(c) - flush_node(c) - c = n - end - setlink(parent,l) - end - end - end - - function noads.handlers.collapse(head,style,penalties) - processnoads(head,collapse,"collapse") - return true -- not needed - end - - local enable = function() - enableaction("math", "noads.handlers.collapse") - if trace_collapsing then - report_collapsing("enabling math collapsing") - end - enable = false - end - - implement { - name = "initializemathcollapsing", - actions = enable, - onlyonce = true, - } - -end - -do - -- inner under over vcenter - - local fixscripts = { } - local movesub = { - -- primes - [0x2032] = 0xFE932, - [0x2033] = 0xFE933, - [0x2034] = 0xFE934, - [0x2057] = 0xFE957, - -- reverse primes - [0x2035] = 0xFE935, - [0x2036] = 0xFE936, - [0x2037] = 0xFE937, - } - - mathematics.virtualize(movesub) - - local options_supported = tokens.defined("Unosuperscript") - - local function fixsupscript(parent,current,current_char,new_char) - if new_char ~= current_char and new_char ~= true then - setchar(current,new_char) - if trace_fixing then - report_fixing("fixing subscript, replacing superscript %U by %U",current_char,new_char) - end - else - if trace_fixing then - report_fixing("fixing subscript, superscript %U",current_char) - end - end - if options_supported then - setfield(parent,"options",0x08+0x22) - end - end - - -- local function movesubscript(parent,current_nucleus,oldchar,newchar) - -- local prev = getprev(parent) - -- if prev and getid(prev) == noad_code then - -- local psup = getsup(prev) - -- local psub = getsub(prev) - -- if not psup and not psub then - -- fixsupscript(prev,current_nucleus,oldchar,newchar) - -- local nucleus = getnucleus(parent) - -- local sub = getsub(parent) - -- setsup(prev,nucleus) - -- setsub(prev,sub) - -- local dummy = copy_node(nucleus) - -- setchar(dummy,0) - -- setnucleus(parent,dummy) - -- setsub(parent) - -- elseif not psup then - -- fixsupscript(prev,current_nucleus,oldchar,newchar) - -- local nucleus = getnucleus(parent) - -- setsup(prev,nucleus) - -- local dummy = copy_node(nucleus) - -- setchar(dummy,0) - -- setnucleus(parent,dummy) - -- end - -- end - -- end - - local function move_none_none(parent,prev,nuc,oldchar,newchar) - fixsupscript(prev,nuc,oldchar,newchar) - local sub = getsub(parent) - setsup(prev,nuc) - setsub(prev,sub) - local dummy = copy_node(nuc) - setchar(dummy,0) - setnucleus(parent,dummy) - setsub(parent) - end - - local function move_none_psub(parent,prev,nuc,oldchar,newchar) - fixsupscript(prev,nuc,oldchar,newchar) - setsup(prev,nuc) - local dummy = copy_node(nuc) - setchar(dummy,0) - setnucleus(parent,dummy) - end - - fixscripts[mathchar_code] = function(pointer,what,n,parent,nested) -- todo: switch to turn in on and off - if parent then - local oldchar = getchar(pointer) - local newchar = movesub[oldchar] - if newchar then - local nuc = getnucleus(parent) - if pointer == nuc then - local sub = getsub(pointer) - local sup = getsup(pointer) - if sub then - if sup then - -- print("[char] sub sup") - else - -- print("[char] sub ---") - end - elseif sup then - -- print("[char] --- sup") - else - local prev = getprev(parent) - if prev and getid(prev) == noad_code then - local psub = getsub(prev) - local psup = getsup(prev) - if psub then - if psup then - -- print("sub sup [char] --- ---") - else - -- print("sub --- [char] --- ---") - move_none_psub(parent,prev,nuc,oldchar,newchar) - end - elseif psup then - -- print("--- sup [char] --- ---") - else - -- print("[char] --- ---") - move_none_none(parent,prev,nuc,oldchar,newchar) - end - else - -- print("no prev [char]") - end - end - else - -- print("[char]") - end - end - end - end - - function noads.handlers.fixscripts(head,style,penalties) - processnoads(head,fixscripts,"fixscripts") - return true -- not needed - end - -end - --- variants - -do - - local variants = { } - local validvariants = { -- fast check on valid - [0x2229] = 0xFE00, [0x222A] = 0xFE00, - [0x2268] = 0xFE00, [0x2269] = 0xFE00, - [0x2272] = 0xFE00, [0x2273] = 0xFE00, - [0x228A] = 0xFE00, [0x228B] = 0xFE00, - [0x2293] = 0xFE00, [0x2294] = 0xFE00, - [0x2295] = 0xFE00, - [0x2297] = 0xFE00, - [0x229C] = 0xFE00, - [0x22DA] = 0xFE00, [0x22DB] = 0xFE00, - [0x2A3C] = 0xFE00, [0x2A3D] = 0xFE00, - [0x2A9D] = 0xFE00, [0x2A9E] = 0xFE00, - [0x2AAC] = 0xFE00, [0x2AAD] = 0xFE00, - [0x2ACB] = 0xFE00, [0x2ACC] = 0xFE00, - } - - variants[mathchar_code] = function(pointer,what,n,parent) -- also set export value - local char = getchar(pointer) - local selector = validvariants[char] - if selector then - local next = getnext(parent) - if next and getid(next) == noad_code then - local nucleus = getnucleus(next) - if nucleus and getid(nucleus) == mathchar_code and getchar(nucleus) == selector then - local variant - local tfmdata = fontdata[getfont(pointer)] - local mathvariants = tfmdata.resources.variants -- and variantdata - if mathvariants then - mathvariants = mathvariants[selector] - if mathvariants then - variant = mathvariants[char] - end - end - if variant then - setchar(pointer,variant) - setattr(pointer,a_exportstatus,char) -- we don't export the variant as it's visual markup - if trace_variants then - report_variants("variant (%U,%U) replaced by %U",char,selector,variant) - end - else - if trace_variants then - report_variants("no variant (%U,%U)",char,selector) - end - end - setprev(next,pointer) - setnext(parent,getnext(next)) - flush_node(next) - end - end - end - end - - function handlers.variants(head,style,penalties) - processnoads(head,variants,"unicode variant") - return true -- not needed - end - -end - --- for manuals - -do - - local classes = { } - local colors = { - [relnode_code] = "trace:dr", - [ordnoad_code] = "trace:db", - [binnoad_code] = "trace:dg", - [opennoad_code] = "trace:dm", - [closenoad_code] = "trace:dm", - [punctnoad_code] = "trace:dc", - -- [opdisplaylimitsnoad_code] = "", - -- [oplimitsnoad_code] = "", - -- [opnolimitsnoad_code] = "", - -- [ordlimitsnoad_code] = "", - -- [innernoad_code = "", - -- [undernoad_code] = "", - -- [overnoad_code] = "", - -- [vcenternoad_code] = "", - } - - local setcolor = colortracers.set - local resetcolor = colortracers.reset - - classes[mathchar_code] = function(pointer,what,n,parent) - local color = colors[getsubtype(parent)] - if color then - setcolor(pointer,color) - else - resetcolor(pointer) - end - end - - function handlers.classes(head,style,penalties) - processnoads(head,classes,"classes") - return true -- not needed - end - - registertracker("math.classes",function(v) - setaction("math","noads.handlers.classes",v) - end) - -end - --- experimental - -do - - -- mathematics.registerdomain { - -- name = "foo", - -- parents = { "bar" }, - -- characters = { - -- [0x123] = { char = 0x234, class = binary }, - -- }, - -- } - - local domains = { } - local categories = { } - local numbers = { } - local a_mathdomain = privateattribute("mathdomain") - mathematics.domains = categories - local permitted = { - ordinary = ordnoad_code, - binary = binnoad_code, - relation = relnode_code, - punctuation = punctnoad_code, - inner = innernoad_code, - } - - function mathematics.registerdomain(data) - local name = data.name - if not name then - return - end - local attr = #numbers + 1 - categories[name] = data - numbers[attr] = data - data.attribute = attr - -- we delay hashing - return attr - end - - local enable - - enable = function() - enableaction("math", "noads.handlers.domains") - if trace_domains then - report_domains("enabling math domains") - end - enable = false - end - - function mathematics.setdomain(name) - if enable then - enable() - end - local data = name and name ~= v_reset and categories[name] - texsetattribute(a_mathdomain,data and data.attribute or unsetvalue) - end - - function mathematics.getdomain(name) - if enable then - enable() - end - local data = name and name ~= v_reset and categories[name] - context(data and data.attribute or unsetvalue) - end - - implement { - name = "initializemathdomain", - actions = enable, - onlyonce = true, - } - - implement { - name = "setmathdomain", - arguments = "string", - actions = mathematics.setdomain, - } - - implement { - name = "getmathdomain", - arguments = "string", - actions = mathematics.getdomain, - } - - local function makehash(data) - local hash = { } - local parents = data.parents - if parents then - local function merge(name) - if name then - local c = categories[name] - if c then - local hash = c.hash - if not hash then - hash = makehash(c) - end - for k, v in next, hash do - hash[k] = v - end - end - end - end - if type(parents) == "string" then - merge(parents) - elseif type(parents) == "table" then - for i=1,#parents do - merge(parents[i]) - end - end - end - local characters = data.characters - if characters then - for k, v in next, characters do - -- local chr = n.char - local cls = v.class - if cls then - v.code = permitted[cls] - else - -- invalid class - end - hash[k] = v - end - end - data.hash = hash - return hash - end - - domains[mathchar_code] = function(pointer,what,n,parent) - local attr = getattr(pointer,a_mathdomain) - if attr then - local domain = numbers[attr] - if domain then - local hash = domain.hash - if not hash then - hash = makehash(domain) - end - local char = getchar(pointer) - local okay = hash[char] - if okay then - local chr = okay.char - local cls = okay.code - if chr and chr ~= char then - setchar(pointer,chr) - end - if cls and cls ~= getsubtype(parent) then - setsubtype(parent,cls) - end - end - end - end - end - - function handlers.domains(head,style,penalties) - processnoads(head,domains,"domains") - return true -- not needed - end - -end - --- just for me - -function handlers.showtree(head,style,penalties) - inspect(nodes.totree(tonut(head))) -end - -registertracker("math.showtree",function(v) - setaction("math","noads.handlers.showtree",v) -end) - --- also for me - -do - - local applyvisuals = nuts.applyvisuals - local visual = false - - function handlers.makeup(head) - applyvisuals(head,visual) - end - - registertracker("math.makeup",function(v) - visual = v - setaction("math","noads.handlers.makeup",v) - end) - -end - --- the normal builder - -do - - local force_penalties = false - - -- registertracker("math.penalties",function(v) - -- force_penalties = v - -- end) - - function builders.kernel.mlist_to_hlist(head,style,penalties) - return mlist_to_hlist(head,style,force_penalties or penalties) - end - - -- function builders.kernel.mlist_to_hlist(head,style,penalties) - -- local h = mlist_to_hlist(head,style,force_penalties or penalties) - -- inspect(nodes.totree(h,true,true,true)) - -- return h - -- end - - implement { - name = "setmathpenalties", - arguments = "integer", - actions = function(p) - force_penalties = p > 0 - end, - } - -end - -local actions = tasks.actions("math") -- head, style, penalties - -local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming - -function processors.mlist_to_hlist(head,style,penalties) - starttiming(noads) - head = actions(head,style,penalties) - stoptiming(noads) - return head -end - -callbacks.register('mlist_to_hlist',processors.mlist_to_hlist,"preprocessing math list") - --- tracing - -statistics.register("math processing time", function() - return statistics.elapsedseconds(noads) -end) |