diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/font-otn.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/font-otn.lua | 733 |
1 files changed, 398 insertions, 335 deletions
diff --git a/Master/texmf-dist/tex/context/base/font-otn.lua b/Master/texmf-dist/tex/context/base/font-otn.lua index d97ef7363b7..39a3f77dda0 100644 --- a/Master/texmf-dist/tex/context/base/font-otn.lua +++ b/Master/texmf-dist/tex/context/base/font-otn.lua @@ -3,9 +3,11 @@ if not modules then modules = { } end modules ['font-otn'] = { comment = "companion to font-ini.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" + license = "see context related readme files", } +-- preprocessors = { "nodes" } + -- this is still somewhat preliminary and it will get better in due time; -- much functionality could only be implemented thanks to the husayni font -- of Idris Samawi Hamid to who we dedicate this module. @@ -28,6 +30,8 @@ if not modules then modules = { } end modules ['font-otn'] = { -- handle positions (we need example fonts) -- handle gpos_single (we might want an extra width field in glyph nodes because adding kerns might interfere) -- mark (to mark) code is still not what it should be (too messy but we need some more extreem husayni tests) +-- remove some optimizations (when I have a faster machine) + --[[ldx-- <p>This module is a bit more split up that I'd like but since we also want to test @@ -116,11 +120,14 @@ results in different tables.</p> -- we now use only one hash. If needed we can have multiple again but in that -- case I will probably prefix (i.e. rename) the lookups in the cached font file. +-- Todo: make plugin feature that operates on char/glyphnode arrays + local concat, insert, remove = table.concat, table.insert, table.remove -local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip +local gmatch, gsub, find, match, lower, strip = string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip local type, next, tonumber, tostring = type, next, tonumber, tostring local lpegmatch = lpeg.match local random = math.random +local formatters = string.formatters local logs, trackers, nodes, attributes = logs, trackers, nodes, attributes @@ -151,6 +158,7 @@ local report_subchain = logs.reporter("fonts","otf subchain") local report_chain = logs.reporter("fonts","otf chain") local report_process = logs.reporter("fonts","otf process") local report_prepare = logs.reporter("fonts","otf prepare") +local report_warning = logs.reporter("fonts","otf warning") registertracker("otf.verbose_chain", function(v) otf.setcontextchain(v and "verbose") end) registertracker("otf.normal_chain", function(v) otf.setcontextchain(v and "normal") end) @@ -166,9 +174,8 @@ local insert_node_after = node.insert_after local delete_node = nodes.delete local copy_node = node.copy local find_node_tail = node.tail or node.slide -local set_attribute = node.set_attribute -local has_attribute = node.has_attribute local flush_node_list = node.flush_list +local end_of_math = node.end_of_math local setmetatableindex = table.setmetatableindex @@ -185,6 +192,7 @@ local glyph_code = nodecodes.glyph local glue_code = nodecodes.glue local disc_code = nodecodes.disc local whatsit_code = nodecodes.whatsit +local math_code = nodecodes.math local dir_code = whatcodes.dir local localpar_code = whatcodes.localpar @@ -199,15 +207,15 @@ local privateattribute = attributes.private -- more complex than an average font) but I need proper examples of all cases, not -- of only some. -local state = privateattribute('state') -local markbase = privateattribute('markbase') -local markmark = privateattribute('markmark') -local markdone = privateattribute('markdone') -- assigned at the injection end -local cursbase = privateattribute('cursbase') -local curscurs = privateattribute('curscurs') -local cursdone = privateattribute('cursdone') -local kernpair = privateattribute('kernpair') -local ligacomp = privateattribute('ligacomp') -- assigned here (ideally it should be combined) +local a_state = privateattribute('state') +local a_markbase = privateattribute('markbase') +local a_markmark = privateattribute('markmark') +local a_markdone = privateattribute('markdone') -- assigned at the injection end +local a_cursbase = privateattribute('cursbase') +local a_curscurs = privateattribute('curscurs') +local a_cursdone = privateattribute('cursdone') +local a_kernpair = privateattribute('kernpair') +local a_ligacomp = privateattribute('ligacomp') -- assigned here (ideally it should be combined) local injections = nodes.injections local setmark = injections.setmark @@ -229,8 +237,7 @@ local onetimemessage = fonts.loggers.onetimemessage otf.defaultnodealternate = "none" -- first last --- we share some vars here, after all, we have no nested lookups and --- less code +-- we share some vars here, after all, we have no nested lookups and less code local tfmdata = false local characters = false @@ -245,9 +252,6 @@ local handlers = { } local rlmode = 0 local featurevalue = false --- we cannot optimize with "start = first_glyph(head)" because then we don't --- know which rlmode we're in which messes up cursive handling later on --- -- head is always a whatsit so we can safely assume that head is not changed -- we use this for special testing and documentation @@ -267,203 +271,238 @@ local function logwarning(...) report_direct(...) end -local function gref(n) +local f_unicode = formatters["%U"] +local f_uniname = formatters["%U (%s)"] +local f_unilist = formatters["% t (% t)"] + +local function gref(n) -- currently the same as in font-otb if type(n) == "number" then local description = descriptions[n] local name = description and description.name if name then - return format("U+%05X (%s)",n,name) + return f_uniname(n,name) else - return format("U+%05X",n) + return f_unicode(n) end - elseif not n then - return "<error in tracing>" - else + elseif n then local num, nam = { }, { } for i=1,#n do local ni = n[i] if tonumber(ni) then -- later we will start at 2 local di = descriptions[ni] - num[i] = format("U+%05X",ni) - nam[i] = di and di.name or "?" + num[i] = f_unicode(ni) + nam[i] = di and di.name or "-" end end - return format("%s (%s)",concat(num," "), concat(nam," ")) + return f_unilist(num,nam) + else + return "<error in node mode tracing>" end end -local function cref(kind,chainname,chainlookupname,lookupname,index) +local function cref(kind,chainname,chainlookupname,lookupname,index) -- not in the mood to alias f_ if index then - return format("feature %s, chain %s, sub %s, lookup %s, index %s",kind,chainname,chainlookupname,lookupname,index) + return formatters["feature %a, chain %a, sub %a, lookup %a, index %a"](kind,chainname,chainlookupname,lookupname,index) elseif lookupname then - return format("feature %s, chain %s, sub %s, lookup %s",kind,chainname or "?",chainlookupname or "?",lookupname) + return formatters["feature %a, chain %a, sub %a, lookup %a"](kind,chainname,chainlookupname,lookupname) elseif chainlookupname then - return format("feature %s, chain %s, sub %s",kind,chainname or "?",chainlookupname) + return formatters["feature %a, chain %a, sub %a"](kind,chainname,chainlookupname) elseif chainname then - return format("feature %s, chain %s",kind,chainname) + return formatters["feature %a, chain %a"](kind,chainname) else - return format("feature %s",kind) + return formatters["feature %a"](kind) end end local function pref(kind,lookupname) - return format("feature %s, lookup %s",kind,lookupname) + return formatters["feature %a, lookup %a"](kind,lookupname) end --- we can assume that languages that use marks are not hyphenated --- we can also assume that at most one discretionary is present - -local function markstoligature(kind,lookupname,start,stop,char) - local n = copy_node(start) - local keep = start - local current - current, start = insert_node_after(start,start,n) - local snext = stop.next - current.next = snext - if snext then - snext.prev = current +-- We can assume that languages that use marks are not hyphenated. We can also assume +-- that at most one discretionary is present. + +-- We do need components in funny kerning mode but maybe I can better reconstruct then +-- as we do have the font components info available; removing components makes the +-- previous code much simpler. Also, later on copying and freeing becomes easier. +-- However, for arabic we need to keep them around for the sake of mark placement +-- and indices. + +local function copy_glyph(g) -- next and prev are untouched ! + local components = g.components + if components then + g.components = nil + local n = copy_node(g) + g.components = components + return n + else + return copy_node(g) end - start.prev, stop.next = nil, nil - current.char, current.subtype, current.components = char, ligature_code, start - return keep end -local function toligature(kind,lookupname,start,stop,char,markflag,discfound) -- brr head - if start == stop then - start.char = char - return start - elseif discfound then - -- print("start->stop",nodes.tosequence(start,stop)) - local components = start.components - if components then - flush_node_list(components) - start.components = nil - end - local lignode = copy_node(start) - lignode.font = start.font - lignode.char = char - lignode.subtype = ligature_code - local next = stop.next +-- start is a mark and we need to keep that one + +local function markstoligature(kind,lookupname,head,start,stop,char) + if start == stop and start.char == char then + return head, start + else local prev = start.prev - stop.next = nil + local next = stop.next start.prev = nil - lignode.components = start - -- print("lignode",nodes.tosequence(lignode)) - -- print("components",nodes.tosequence(lignode.components)) - prev.next = lignode + stop.next = nil + local base = copy_glyph(start) + if head == start then + head = base + end + base.char = char + base.subtype = ligature_code + base.components = start + if prev then + prev.next = base + end if next then - next.prev = lignode + next.prev = base end - lignode.next = next - lignode.prev = prev - -- print("start->end",nodes.tosequence(start)) - return lignode + base.next = next + base.prev = prev + return head, base + end +end + +-- The next code is somewhat complicated by the fact that some fonts can have ligatures made +-- from ligatures that themselves have marks. This was identified by Kai in for instance +-- arabtype: KAF LAM SHADDA ALEF FATHA (0x0643 0x0644 0x0651 0x0627 0x064E). This becomes +-- KAF LAM-ALEF with a SHADDA on the first and a FATHA op de second component. In a next +-- iteration this becomes a KAF-LAM-ALEF with a SHADDA on the second and a FATHA on the +-- third component. + +local function getcomponentindex(start) + if start.id ~= glyph_code then + return 0 + elseif start.subtype == ligature_code then + local i = 0 + local components = start.components + while components do + i = i + getcomponentindex(components) + components = components.next + end + return i + elseif not marks[start.char] then + return 1 else - -- start is the ligature + return 0 + end +end + +local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) -- brr head + if start == stop and start.char == char then + start.char = char + return head, start + end + local prev = start.prev + local next = stop.next + start.prev = nil + stop.next = nil + local base = copy_glyph(start) + if start == head then + head = base + end + base.char = char + base.subtype = ligature_code + base.components = start -- start can have components + if prev then + prev.next = base + end + if next then + next.prev = base + end + base.next = next + base.prev = prev + if not discfound then local deletemarks = markflag ~= "mark" - local n = copy_node(start) - local current - current, start = insert_node_after(start,start,n) - local snext = stop.next - current.next = snext - if snext then - snext.prev = current - end - start.prev = nil - stop.next = nil - current.char = char - current.subtype = ligature_code - current.components = start - local head = current - -- this is messy ... we should get rid of the components eventually - local i = 0 -- is index of base + local components = start + local baseindex = 0 + local componentindex = 0 + local head = base + local current = base while start do - if not marks[start.char] then - i = i + 1 + local char = start.char + if not marks[char] then + baseindex = baseindex + componentindex + componentindex = getcomponentindex(start) elseif not deletemarks then -- quite fishy - set_attribute(start,ligacomp,i) + start[a_ligacomp] = baseindex + (start[a_ligacomp] or componentindex) if trace_marks then - logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) + logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) end - head, current = insert_node_after(head,current,copy_node(start)) + head, current = insert_node_after(head,current,copy_node(start)) -- unlikely that mark has components end start = start.next end - start = current.next - while start and start.id == glyph_code do - if marks[start.char] then - set_attribute(start,ligacomp,i) + local start = components + while start and start.id == glyph_code do -- hm, is id test needed ? + local char = start.char + if marks[char] then + start[a_ligacomp] = baseindex + (start[a_ligacomp] or componentindex) if trace_marks then - logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) + logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) end else break end start = start.next end - -- - -- we do need components in funny kerning mode but maybe I can better reconstruct then - -- as we do have the font components info available; removing components makes the - -- previous code much simpler - -- - -- flush_node_list(head.components) - return head end + return head, base end -function handlers.gsub_single(start,kind,lookupname,replacement) +function handlers.gsub_single(head,start,kind,lookupname,replacement) if trace_singles then logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement)) end start.char = replacement - return start, true + return head, start, true end -local function get_alternative_glyph(start,alternatives,value) - -- needs checking: (global value, brrr) - local choice = nil - local n = #alternatives - local char = start.char - -- +local function get_alternative_glyph(start,alternatives,value,trace_alternatives) + local n = #alternatives if value == "random" then local r = random(1,n) - value, choice = format("random, choice %s",r), alternatives[r] + return alternatives[r], trace_alternatives and formatters["value %a, taking %a"](value,r) elseif value == "first" then - value, choice = format("first, choice %s",1), alternatives[1] + return alternatives[1], trace_alternatives and formatters["value %a, taking %a"](value,1) elseif value == "last" then - value, choice = format("last, choice %s",n), alternatives[n] + return alternatives[n], trace_alternatives and formatters["value %a, taking %a"](value,n) else value = tonumber(value) if type(value) ~= "number" then - value, choice = "default, choice 1", alternatives[1] + return alternatives[1], trace_alternatives and formatters["invalid value %s, taking %a"](value,1) elseif value > n then local defaultalt = otf.defaultnodealternate if defaultalt == "first" then - value, choice = format("no %s variants, taking %s",value,n), alternatives[n] + return alternatives[n], trace_alternatives and formatters["invalid value %s, taking %a"](value,1) elseif defaultalt == "last" then - value, choice = format("no %s variants, taking %s",value,1), alternatives[1] + return alternatives[1], trace_alternatives and formatters["invalid value %s, taking %a"](value,n) else - value, choice = format("no %s variants, ignoring",value), false + return false, trace_alternatives and formatters["invalid value %a, %s"](value,"out of range") end elseif value == 0 then - value, choice = format("choice %s (no change)",value), char + return start.char, trace_alternatives and formatters["invalid value %a, %s"](value,"no change") elseif value < 1 then - value, choice = format("no %s variants, taking %s",value,1), alternatives[1] + return alternatives[1], trace_alternatives and formatters["invalid value %a, taking %a"](value,1) else - value, choice = format("choice %s",value), alternatives[value] + return alternatives[value], trace_alternatives and formatters["value %a, taking %a"](value,value) end end - return choice end -local function multiple_glyphs(start,multiple) -- marks ? +local function multiple_glyphs(head,start,multiple) -- marks ? local nofmultiples = #multiple if nofmultiples > 0 then start.char = multiple[1] if nofmultiples > 1 then local sn = start.next for k=2,nofmultiples do -- todo: use insert_node - local n = copy_node(start) + local n = copy_node(start) -- ignore components n.char = multiple[k] n.next = sn n.prev = start @@ -474,45 +513,45 @@ local function multiple_glyphs(start,multiple) -- marks ? start = n end end - return start, true + return head, start, true else if trace_multiples then logprocess("no multiple for %s",gref(start.char)) end - return start, false + return head, start, false end end -function handlers.gsub_alternate(start,kind,lookupname,alternative,sequence) +function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence) local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue - local choice = get_alternative_glyph(start,alternative,value) + local choice, comment = get_alternative_glyph(start,alternative,value,trace_alternatives) if choice then if trace_alternatives then - logprocess("%s: replacing %s by alternative %s (%s)",pref(kind,lookupname),gref(char),gref(choice),choice) + logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(start.char),choice,gref(choice),comment) end start.char = choice else if trace_alternatives then - logwarning("%s: no variant %s for %s",pref(kind,lookupname),tostring(value),gref(char)) + logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(start.char),comment) end end - return start, true + return head, start, true end -function handlers.gsub_multiple(start,kind,lookupname,multiple) +function handlers.gsub_multiple(head,start,kind,lookupname,multiple) if trace_multiples then logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) end - return multiple_glyphs(start,multiple) + return multiple_glyphs(head,start,multiple) end -function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) +function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) local s, stop, discfound = start.next, nil, false local startchar = start.char if marks[startchar] then while s do local id = s.id - if id == glyph_code and s.subtype<256 and s.font == currentfont then + if id == glyph_code and s.font == currentfont and s.subtype<256 then local lg = ligature[s.char] if lg then stop = s @@ -530,12 +569,12 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) if lig then if trace_ligatures then local stopchar = stop.char - start = markstoligature(kind,lookupname,start,stop,lig) - logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) + head, start = markstoligature(kind,lookupname,head,start,stop,lig) + logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) else - start = markstoligature(kind,lookupname,start,stop,lig) + head, start = markstoligature(kind,lookupname,head,start,stop,lig) end - return start, true + return head, start, true else -- ok, goto next lookup end @@ -574,18 +613,18 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) if lig then if trace_ligatures then local stopchar = stop.char - start = toligature(kind,lookupname,start,stop,lig,skipmark,discfound) - logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) + head, start = toligature(kind,lookupname,head, start,stop,lig,skipmark,discfound) + logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) else - start = toligature(kind,lookupname,start,stop,lig,skipmark,discfound) + head, start = toligature(kind,lookupname,head, start,stop,lig,skipmark,discfound) end - return start, true + return head, start, true else -- ok, goto next lookup end end end - return start, false + return head, start, false end --[[ldx-- @@ -593,16 +632,16 @@ end we need to explicitly test for basechar, baselig and basemark entries.</p> --ldx]]-- -function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) +function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence) local markchar = start.char if marks[markchar] then local base = start.prev -- [glyph] [start=mark] - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then local basechar = base.char if marks[basechar] then while true do base = base.prev - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then basechar = base.char if not marks[basechar] then break @@ -611,7 +650,7 @@ function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) if trace_bugs then logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) end - return start, false + return head, start, false end end end @@ -629,10 +668,10 @@ function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) if ma then local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) end - return start, true + return head, start, true end end end @@ -650,20 +689,20 @@ function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) elseif trace_bugs then logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) end - return start, false + return head, start, false end -function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) +function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence) -- check chainpos variant local markchar = start.char if marks[markchar] then local base = start.prev -- [glyph] [optional marks] [start=mark] - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then local basechar = base.char if marks[basechar] then while true do base = base.prev - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then basechar = base.char if not marks[basechar] then break @@ -672,11 +711,11 @@ function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) if trace_bugs then logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) end - return start, false + return head, start, false end end end - local index = has_attribute(start,ligacomp) + local index = start[a_ligacomp] local baseanchors = descriptions[basechar] if baseanchors then baseanchors = baseanchors.anchors @@ -692,10 +731,10 @@ function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) if ba then local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -- index if trace_marks then - logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", + logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) end - return start, true + return head, start, true end end end @@ -715,20 +754,17 @@ function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) elseif trace_bugs then logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) end - return start, false + return head, start, false end -function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) +function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence) local markchar = start.char if marks[markchar] then local base = start.prev -- [glyph] [basemark] [start=mark] - -- while base and has_attribute(base,ligacomp) and has_attribute(base,ligacomp) ~= has_attribute(start,ligacomp) do - -- base = base.prev -- KE: prevents mkmk for marks on different components of a ligature - -- end - local slc = has_attribute(start,ligacomp) + local slc = start[a_ligacomp] if slc then -- a rather messy loop ... needs checking with husayni while base do - local blc = has_attribute(base,ligacomp) + local blc = base[a_ligacomp] if blc and blc ~= slc then base = base.prev else @@ -736,7 +772,7 @@ function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) end end end - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -- subtype test can go + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -- subtype test can go local basechar = base.char local baseanchors = descriptions[basechar] if baseanchors then @@ -751,10 +787,10 @@ function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) if ma then local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) end - return start,true + return head, start, true end end end @@ -773,11 +809,11 @@ function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) elseif trace_bugs then logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) end - return start,false + return head, start, false end -function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to be checked - local alreadydone = cursonce and has_attribute(start,cursbase) +function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) -- to be checked + local alreadydone = cursonce and start[a_cursbase] if not alreadydone then local done = false local startchar = start.char @@ -787,7 +823,7 @@ function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to end else local nxt = start.next - while not done and nxt and nxt.id == glyph_code and nxt.subtype<256 and nxt.font == currentfont do + while not done and nxt and nxt.id == glyph_code and nxt.font == currentfont and nxt.subtype<256 do local nextchar = nxt.char if marks[nextchar] then -- should not happen (maybe warning) @@ -806,7 +842,7 @@ function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to if exit then local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) if trace_cursive then - logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) + logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) end done = true break @@ -823,35 +859,35 @@ function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to end end end - return start, done + return head, start, done else if trace_cursive and trace_details then logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) end - return start, false + return head, start, false end end -function handlers.gpos_single(start,kind,lookupname,kerns,sequence) +function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) local startchar = start.char local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) if trace_kerns then - logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) + logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) end - return start, false + return head, start, false end -function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) +function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too -- todo: kerns in components of ligatures local snext = start.next if not snext then - return start, false + return head, start, false else local prev, done = start, false local factor = tfmdata.parameters.factor local lookuptype = lookuptypes[lookupname] - while snext and snext.id == glyph_code and snext.subtype<256 and snext.font == currentfont do + while snext and snext.id == glyph_code and snext.font == currentfont and snext.subtype<256 do local nextchar = snext.char local krn = kerns[nextchar] if not krn and marks[nextchar] then @@ -868,14 +904,14 @@ function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) local startchar = start.char local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) if trace_kerns then - logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) + logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) end end if b and #b > 0 then local startchar = start.char local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) if trace_kerns then - logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) + logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) end end else -- wrong ... position has different entries @@ -902,7 +938,7 @@ function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) break end end - return start, done + return head, start, done end end @@ -935,21 +971,21 @@ local logwarning = report_chain -- We could share functions but that would lead to extra function calls with many -- arguments, redundant tests and confusing messages. -function chainprocs.chainsub(start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname) +function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname) logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) - return start, false + return head, start, false end -function chainmores.chainsub(start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) +function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) - return start, false + return head, start, false end -- The reversesub is a special case, which is why we need to store the replacements -- in a bit weird way. There is no lookup and the replacement comes from the lookup -- itself. It is meant mostly for dealing with Urdu. -function chainprocs.reversesub(start,stop,kind,chainname,currentcontext,lookuphash,replacements) +function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements) local char = start.char local replacement = replacements[char] if replacement then @@ -957,9 +993,9 @@ function chainprocs.reversesub(start,stop,kind,chainname,currentcontext,lookupha logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement)) end start.char = replacement - return start, true + return head, start, true else - return start, false + return head, start, false end end @@ -987,6 +1023,10 @@ local function delete_till_stop(start,stop,ignoremarks) -- keeps start repeat -- start x x m x x stop => start m local next = start.next if not marks[next.char] then + local components = next.components + if components then -- probably not needed + flush_node_list(components) + end delete_node(start,next) end n = n + 1 @@ -994,6 +1034,10 @@ local function delete_till_stop(start,stop,ignoremarks) -- keeps start else -- start x x x stop => start repeat local next = start.next + local components = next.components + if components then -- probably not needed + flush_node_list(components) + end delete_node(start,next) n = n + 1 until next == stop @@ -1006,7 +1050,7 @@ end match.</p> --ldx]]-- -function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) +function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) -- todo: marks ? local current = start local subtables = currentlookup.subtables @@ -1024,7 +1068,7 @@ function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,lookuph end else replacement = replacement[currentchar] - if not replacement then + if not replacement or replacement == "" then if trace_bugs then logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar)) end @@ -1035,14 +1079,14 @@ function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,lookuph current.char = replacement end end - return start, true + return head, start, true elseif current == stop then break else current = current.next end end - return start, false + return head, start, false end chainmores.gsub_single = chainprocs.gsub_single @@ -1052,7 +1096,7 @@ chainmores.gsub_single = chainprocs.gsub_single the match.</p> --ldx]]-- -function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) +function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) delete_till_stop(start,stop) -- we could pass ignoremarks as #3 .. local startchar = start.char local subtables = currentlookup.subtables @@ -1064,7 +1108,7 @@ function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,looku end else replacements = replacements[startchar] - if not replacements then + if not replacements or replacement == "" then if trace_bugs then logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar)) end @@ -1072,17 +1116,12 @@ function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,looku if trace_multiples then logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements)) end - return multiple_glyphs(start,replacements) + return multiple_glyphs(head,start,replacements) end end - return start, false + return head, start, false end --- function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) --- logprocess("%s: gsub_multiple not yet supported",cref(kind,chainname,chainlookupname)) --- return start, false --- end - chainmores.gsub_multiple = chainprocs.gsub_multiple --[[ldx-- @@ -1097,7 +1136,7 @@ chainmores.gsub_multiple = chainprocs.gsub_multiple -- marks come last anyway -- are there cases where we need to delete the mark -function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) +function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) local current = start local subtables = currentlookup.subtables local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue @@ -1113,36 +1152,31 @@ function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,look else alternatives = alternatives[currentchar] if alternatives then - local choice = get_alternative_glyph(current,alternatives,value) + local choice, comment = get_alternative_glyph(current,alternatives,value,trace_alternatives) if choice then if trace_alternatives then - logprocess("%s: replacing %s by alternative %s (%s)",cref(kind,chainname,chainlookupname,lookupname),gref(char),gref(choice),choice) + logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment) end start.char = choice else if trace_alternatives then - logwarning("%s: no variant %s for %s",cref(kind,chainname,chainlookupname,lookupname),tostring(value),gref(char)) + logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment) end end elseif trace_bugs then - logwarning("%s: no alternative for %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar)) + logwarning("%s: no alternative for %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar),comment) end end - return start, true + return head, start, true elseif current == stop then break else current = current.next end end - return start, false + return head, start, false end --- function chainmores.gsub_alternate(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) --- logprocess("%s: gsub_alternate not yet supported",cref(kind,chainname,chainlookupname)) --- return start, false --- end - chainmores.gsub_alternate = chainprocs.gsub_alternate --[[ldx-- @@ -1151,7 +1185,7 @@ this function (move code inline and handle the marks by a separate function). We assume rather stupid ligatures (no complex disc nodes).</p> --ldx]]-- -function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) +function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) local startchar = start.char local subtables = currentlookup.subtables local lookupname = subtables[1] @@ -1203,13 +1237,13 @@ function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,looku end if trace_ligatures then if start == stop then - logprocess("%s: replacing character %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) + logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) else - logprocess("%s: replacing character %s upto %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) + logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) end end - start = toligature(kind,lookupname,start,stop,l2,currentlookup.flags[1],discfound) - return start, true, nofreplacements + head, start = toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound) + return head, start, true, nofreplacements elseif trace_bugs then if start == stop then logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) @@ -1219,12 +1253,12 @@ function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,looku end end end - return start, false, 0 + return head, start, false, 0 end chainmores.gsub_ligature = chainprocs.gsub_ligature -function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) +function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) local markchar = start.char if marks[markchar] then local subtables = currentlookup.subtables @@ -1235,12 +1269,12 @@ function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,look end if markanchors then local base = start.prev -- [glyph] [start=mark] - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then local basechar = base.char if marks[basechar] then while true do base = base.prev - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then basechar = base.char if not marks[basechar] then break @@ -1249,7 +1283,7 @@ function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,look if trace_bugs then logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) end - return start, false + return head, start, false end end end @@ -1264,10 +1298,10 @@ function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,look if ma then local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) end - return start, true + return head, start, true end end end @@ -1285,10 +1319,10 @@ function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,look elseif trace_bugs then logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) end - return start, false + return head, start, false end -function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) +function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) local markchar = start.char if marks[markchar] then local subtables = currentlookup.subtables @@ -1299,12 +1333,12 @@ function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext, end if markanchors then local base = start.prev -- [glyph] [optional marks] [start=mark] - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then local basechar = base.char if marks[basechar] then while true do base = base.prev - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then basechar = base.char if not marks[basechar] then break @@ -1313,12 +1347,12 @@ function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext, if trace_bugs then logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar) end - return start, false + return head, start, false end end end -- todo: like marks a ligatures hash - local index = has_attribute(start,ligacomp) + local index = start[a_ligacomp] local baseanchors = descriptions[basechar].anchors if baseanchors then local baseanchors = baseanchors['baselig'] @@ -1332,10 +1366,10 @@ function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext, if ba then local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -- index if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", + logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) end - return start, true + return head, start, true end end end @@ -1354,13 +1388,13 @@ function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext, elseif trace_bugs then logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) end - return start, false + return head, start, false end -function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) +function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) local markchar = start.char if marks[markchar] then ---~ local alreadydone = markonce and has_attribute(start,markmark) +--~ local alreadydone = markonce and start[a_markmark] --~ if not alreadydone then -- local markanchors = descriptions[markchar].anchors markanchors = markanchors and markanchors.mark local subtables = currentlookup.subtables @@ -1371,13 +1405,10 @@ function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,look end if markanchors then local base = start.prev -- [glyph] [basemark] [start=mark] - -- while (base and has_attribute(base,ligacomp) and has_attribute(base,ligacomp) ~= has_attribute(start,ligacomp)) do - -- base = base.prev -- KE: prevents mkmk for marks on different components of a ligature - -- end - local slc = has_attribute(start,ligacomp) + local slc = start[a_ligacomp] if slc then -- a rather messy loop ... needs checking with husayni while base do - local blc = has_attribute(base,ligacomp) + local blc = base[a_ligacomp] if blc and blc ~= slc then base = base.prev else @@ -1385,7 +1416,7 @@ function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,look end end end - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -- subtype test can go + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -- subtype test can go local basechar = base.char local baseanchors = descriptions[basechar].anchors if baseanchors then @@ -1398,10 +1429,10 @@ function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,look if ma then local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) end - return start, true + return head, start, true end end end @@ -1422,13 +1453,11 @@ function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,look elseif trace_bugs then logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) end - return start, false + return head, start, false end --- ! ! ! untested ! ! ! - -function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local alreadydone = cursonce and has_attribute(start,cursbase) +function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local alreadydone = cursonce and start[a_cursbase] if not alreadydone then local startchar = start.char local subtables = currentlookup.subtables @@ -1445,7 +1474,7 @@ function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,lookup end else local nxt = start.next - while not done and nxt and nxt.id == glyph_code and nxt.subtype<256 and nxt.font == currentfont do + while not done and nxt and nxt.id == glyph_code and nxt.font == currentfont and nxt.subtype<256 do local nextchar = nxt.char if marks[nextchar] then -- should not happen (maybe warning) @@ -1464,7 +1493,7 @@ function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,lookup if exit then local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) if trace_cursive then - logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) + logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) end done = true break @@ -1481,18 +1510,18 @@ function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,lookup end end end - return start, done + return head, start, done else if trace_cursive and trace_details then logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) end - return start, false + return head, start, false end end - return start, false + return head, start, false end -function chainprocs.gpos_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) +function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) -- untested .. needs checking for the new model local startchar = start.char local subtables = currentlookup.subtables @@ -1503,16 +1532,16 @@ function chainprocs.gpos_single(start,stop,kind,chainname,currentcontext,lookuph if kerns then local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) if trace_kerns then - logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) + logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) end end end - return start, false + return head, start, false end -- when machines become faster i will make a shared function -function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) +function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) -- logwarning("%s: gpos_pair not yet supported",cref(kind,chainname,chainlookupname)) local snext = start.next if snext then @@ -1526,7 +1555,7 @@ function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,lookuphas local lookuptype = lookuptypes[lookupname] local prev, done = start, false local factor = tfmdata.parameters.factor - while snext and snext.id == glyph_code and snext.subtype<256 and snext.font == currentfont do + while snext and snext.id == glyph_code and snext.font == currentfont and snext.subtype<256 do local nextchar = snext.char local krn = kerns[nextchar] if not krn and marks[nextchar] then @@ -1542,14 +1571,14 @@ function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,lookuphas local startchar = start.char local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) if trace_kerns then - logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) + logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) end end if b and #b > 0 then local startchar = start.char local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) if trace_kerns then - logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) + logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) end end else @@ -1576,11 +1605,11 @@ function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,lookuphas break end end - return start, done + return head, start, done end end end - return start, false + return head, start, false end -- what pointer to return, spec says stop @@ -1593,13 +1622,13 @@ end local function show_skip(kind,chainname,char,ck,class) if ck[9] then - logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s (%s=>%s)",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) + logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) else - logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s",cref(kind,chainname),gref(char),class,ck[1],ck[2]) + logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) end end -local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,lookuphash) +local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash) -- local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] local flags = sequence.flags local done = false @@ -1619,7 +1648,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence -- f..l = mid string if s == 1 then -- never happens - match = current.id == glyph_code and current.subtype<256 and current.font == currentfont and seq[1][current.char] + match = current.id == glyph_code and current.font == currentfont and current.subtype<256 and seq[1][current.char] else -- maybe we need a better space check (maybe check for glue or category or combination) -- we cannot optimize for n=2 because there can be disc nodes @@ -1639,7 +1668,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence if last then local id = last.id if id == glyph_code then - if last.subtype<256 and last.font == currentfont then + if last.font == currentfont and last.subtype<256 then local char = last.char local ccd = descriptions[char] if ccd then @@ -1689,7 +1718,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence if prev then local id = prev.id if id == glyph_code then - if prev.subtype<256 and prev.font == currentfont then -- normal char + if prev.font == currentfont and prev.subtype<256 then -- normal char local char = prev.char local ccd = descriptions[char] if ccd then @@ -1722,7 +1751,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence break end prev = prev.prev - elseif seq[n][32] then -- somehat special, as zapfino can have many preceding spaces + elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces n = n -1 else match = false @@ -1750,7 +1779,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence if current then local id = current.id if id == glyph_code then - if current.subtype<256 and current.font == currentfont then -- normal char + if current.font == currentfont and current.subtype<256 then -- normal char local char = current.char local ccd = descriptions[char] if ccd then @@ -1808,10 +1837,10 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence local rule, lookuptype, f, l = ck[1], ck[2], ck[4], ck[5] local char = start.char if ck[9] then - logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s (%s=>%s)", + logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a", cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) else - logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s", + logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a", cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype) end end @@ -1825,7 +1854,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence if chainlookup then local cp = chainprocs[chainlookup.type] if cp then - start, done = cp(start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) + head, start, done = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) else logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) end @@ -1856,7 +1885,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence local cp = chainlookup and chainmores[chainlookup.type] if cp then local ok, n - start, ok, n = cp(start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) + head, start, ok, n = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) -- messy since last can be changed ! if ok then done = true @@ -1870,13 +1899,17 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence -- logprocess("%s: multiple subchains for %s are not yet supported",cref(kind,chainname,chainlookupname),chainlookup and chainlookup.type or "?") i = i + 1 end - start = start.next + if start then + start = start.next + else + -- weird + end until i > nofchainlookups end else local replacements = ck[7] if replacements then - start, done = chainprocs.reversesub(start,last,kind,chainname,ck,lookuphash,replacements) -- sequence + head, start, done = chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements) -- sequence else done = true -- can be meant to be skipped if trace_contexts then @@ -1886,7 +1919,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence end end end - return start, done + return head, start, done end -- Because we want to keep this elsewhere (an because speed is less an issue) we @@ -1899,7 +1932,7 @@ local verbose_handle_contextchain = function(font,...) end otf.chainhandlers = { - normal = normal_handle_contextchain, + normal = normal_handle_contextchain, verbose = verbose_handle_contextchain, } @@ -1910,7 +1943,7 @@ function otf.setcontextchain(method) end handlers.contextchain = normal_handle_contextchain else - logwarning("installing contextchain handler '%s'",method) + logwarning("installing contextchain handler %a",method) local handler = otf.chainhandlers[method] handlers.contextchain = function(...) return handler(currentfont,...) -- hm, get rid of ... @@ -1941,7 +1974,7 @@ local function report_missing_cache(typ,lookup) local t = f[typ] if not t then t = { } f[typ] = t end if not t[lookup] then t[lookup] = true - logwarning("missing cache for lookup %s of type %s in font %s (%s)",lookup,typ,currentfont,tfmdata.properties.fullname) + logwarning("missing cache for lookup %a, type %a, font %a, name %a",lookup,typ,currentfont,tfmdata.properties.fullname) end end @@ -1962,12 +1995,7 @@ end) -- fonts.hashes.lookups = lookuphashes -local special_attributes = { - init = 1, - medi = 2, - fina = 3, - isol = 4 -} +local autofeatures = fonts.analyzers.features -- was: constants local function initialize(sequence,script,language,enabled) local features = sequence.features @@ -1977,7 +2005,7 @@ local function initialize(sequence,script,language,enabled) if valid then local languages = scripts[script] or scripts[wildcard] if languages and (languages[language] or languages[wildcard]) then - return { valid, special_attributes[kind] or false, sequence.chain or 0, kind, sequence } + return { valid, autofeatures[kind] or false, sequence.chain or 0, kind, sequence } end end end @@ -1985,7 +2013,7 @@ local function initialize(sequence,script,language,enabled) return false end -function otf.dataset(tfmdata,sequences,font) -- generic variant, overloaded in context +function otf.dataset(tfmdata,font) -- generic variant, overloaded in context local shared = tfmdata.shared local properties = tfmdata.properties local language = properties.language or "dflt" @@ -2003,13 +2031,24 @@ function otf.dataset(tfmdata,sequences,font) -- generic variant, overloaded in c end local rl = rs[language] if not rl then - rl = { } + rl = { + -- indexed but we can also add specific data by key + } rs[language] = rl - setmetatableindex(rl, function(t,k) - local v = enabled and initialize(sequences[k],script,language,enabled) - t[k] = v - return v - end) + local sequences = tfmdata.resources.sequences +-- setmetatableindex(rl, function(t,k) +-- if type(k) == "number" then +-- local v = enabled and initialize(sequences[k],script,language,enabled) +-- t[k] = v +-- return v +-- end +-- end) +for s=1,#sequences do + local v = enabled and initialize(sequences[s],script,language,enabled) + if v then + rl[#rl+1] = v + end +end end return rl end @@ -2030,6 +2069,8 @@ end -- start = start.next -- end +-- there will be a new direction parser (pre-parsed etc) + local function featuresprocessor(head,font,attr) local lookuphash = lookuphashes[font] -- we can also check sequences here @@ -2057,7 +2098,7 @@ local function featuresprocessor(head,font,attr) local sequences = resources.sequences local done = false - local datasets = otf.dataset(tfmdata,sequences,font,attr) + local datasets = otf.dataset(tfmdata,font,attr) local dirstack = { } -- could move outside function @@ -2066,12 +2107,20 @@ local function featuresprocessor(head,font,attr) -- to keep track of directions anyway. Also at some point I want to play with -- font interactions and then we do need the full sweeps. - for s=1,#sequences do - local dataset = datasets[s] - if dataset then - featurevalue = dataset[1] -- todo: pass to function instead of using a global - if featurevalue then - local sequence = sequences[s] -- also dataset[5] + -- Keeping track of the headnode is needed for devanagari (I generalized it a bit + -- so that multiple cases are also covered.) + +-- for s=1,#sequences do +-- local dataset = datasets[s] +-- if dataset then +-- featurevalue = dataset[1] -- todo: pass to function instead of using a global +-- if featurevalue then -- never false + +for s=1,#datasets do + local dataset = datasets[s] + featurevalue = dataset[1] -- todo: pass to function instead of using a global + + local sequence = dataset[5] -- sequences[s] -- also dataset[5] local rlparmode = 0 local topstack = 0 local success = false @@ -2087,8 +2136,8 @@ local function featuresprocessor(head,font,attr) while start do local id = start.id if id == glyph_code then - if start.subtype<256 and start.font == font then - local a = has_attribute(start,0) + if start.font == font and start.subtype<256 then + local a = start[0] if a then a = a == attr else @@ -2101,7 +2150,7 @@ local function featuresprocessor(head,font,attr) if lookupcache then local lookupmatch = lookupcache[start.char] if lookupmatch then - start, success = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + head, start, success = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) if success then break end @@ -2135,19 +2184,19 @@ local function featuresprocessor(head,font,attr) while start do local id = start.id if id == glyph_code then - if start.subtype<256 and start.font == font then - local a = has_attribute(start,0) + if start.font == font and start.subtype<256 then + local a = start[0] if a then - a = (a == attr) and (not attribute or has_attribute(start,state,attribute)) + a = (a == attr) and (not attribute or start[a_state] == attribute) else - a = not attribute or has_attribute(start,state,attribute) + a = not attribute or start[a_state] == attribute end if a then local lookupmatch = lookupcache[start.char] if lookupmatch then -- sequence kan weg local ok - start, ok = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) + head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) if ok then success = true end @@ -2156,6 +2205,8 @@ local function featuresprocessor(head,font,attr) else start = start.next end + elseif id == math_code then + start = end_of_math(start).next else start = start.next end @@ -2178,7 +2229,7 @@ local function featuresprocessor(head,font,attr) rlmode = rlparmode end if trace_directions then - report_process("directions after txtdir %s: txtdir=%s:%s, parmode=%s, txtmode=%s",dir,topstack,newdir or "unset",rlparmode,rlmode) + report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) end elseif subtype == localpar_code then local dir = start.dir @@ -2191,10 +2242,12 @@ local function featuresprocessor(head,font,attr) end rlmode = rlparmode if trace_directions then - report_process("directions after pardir %s: parmode=%s, txtmode=%s",dir,rlparmode,rlmode) + report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) end end start = start.next + elseif id == math_code then + start = end_of_math(start).next else start = start.next end @@ -2204,12 +2257,12 @@ local function featuresprocessor(head,font,attr) while start do local id = start.id if id == glyph_code then - if start.subtype<256 and start.font == font then - local a = has_attribute(start,0) + if start.font == font and start.subtype<256 then + local a = start[0] if a then - a = (a == attr) and (not attribute or has_attribute(start,state,attribute)) + a = (a == attr) and (not attribute or start[a_state] == attribute) else - a = not attribute or has_attribute(start,state,attribute) + a = not attribute or start[a_state] == attribute end if a then for i=1,ns do @@ -2220,7 +2273,7 @@ local function featuresprocessor(head,font,attr) if lookupmatch then -- we could move all code inline but that makes things even more unreadable local ok - start, ok = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) if ok then success = true break @@ -2256,7 +2309,7 @@ local function featuresprocessor(head,font,attr) rlmode = rlparmode end if trace_directions then - report_process("directions after txtdir %s: txtdir=%s:%s, parmode=%s, txtmode=%s",dir,topstack,newdir or "unset",rlparmode,rlmode) + report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) end elseif subtype == localpar_code then local dir = start.dir @@ -2269,10 +2322,12 @@ local function featuresprocessor(head,font,attr) end rlmode = rlparmode if trace_directions then - report_process("directions after pardir %s: parmode=%s, txtmode=%s",dir,rlparmode,rlmode) + report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) end end start = start.next + elseif id == math_code then + start = end_of_math(start).next else start = start.next end @@ -2285,8 +2340,12 @@ local function featuresprocessor(head,font,attr) if trace_steps then -- ? registerstep(head) end - end - end + +-- end +-- else +-- -- report_process("warning, no dataset %a",s) +-- end + end return head, done end @@ -2450,10 +2509,10 @@ local function prepare_contextchains(tfmdata) local format = lookupdata.format local validformat = valid[format] if not validformat then - report_prepare("unsupported format %s",format) + report_prepare("unsupported format %a",format) elseif not validformat[lookuptype] then -- todo: dejavu-serif has one (but i need to see what use it has) - report_prepare("unsupported %s %s for %s",format,lookuptype,lookupname) + report_prepare("unsupported format %a, lookuptype %a, lookupname %a",format,lookuptype,lookupname) else local contexts = lookuphash[lookupname] if not contexts then @@ -2469,7 +2528,7 @@ local function prepare_contextchains(tfmdata) local replacements = rule.replacements local sequence = { } local nofsequences = 0 - -- Wventually we can store start, stop and sequence in the cached file + -- Eventually we can store start, stop and sequence in the cached file -- but then less sharing takes place so best not do that without a lot -- of profiling so let's forget about it. if before then @@ -2509,7 +2568,7 @@ local function prepare_contextchains(tfmdata) -- no rules end else - report_prepare("missing lookuptype for %s",lookupname) + report_prepare("missing lookuptype for lookupname %a",lookupname) end end end @@ -2530,7 +2589,7 @@ local function featuresinitializer(tfmdata,value) prepare_lookups(tfmdata) properties.initialized = true if trace_preparing then - report_prepare("preparation time is %0.3f seconds for %s",os.clock()-starttime,tfmdata.properties.fullname or "?") + report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,tfmdata.properties.fullname) end end end @@ -2548,3 +2607,7 @@ registerotffeature { node = featuresprocessor, } } + +-- This can be used for extra handlers, but should be used with care! + +otf.handlers = handlers |