From 3a5009d6feedc1a4c4e5a8d61c7e67b9c35399fa Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 22 Dec 2014 16:42:02 +0100 Subject: [fontloader] sync with Context as of 2014-12-22 --- src/fontloader/misc/fontloader-basics-nod.lua | 8 +- src/fontloader/misc/fontloader-font-otf.lua | 33 +- src/fontloader/misc/fontloader-fonts-cbk.lua | 44 +- src/fontloader/misc/fontloader-fonts-inj.lua | 1286 ++++++++++++++++--------- src/fontloader/misc/fontloader-fonts.lua | 9 +- src/fontloader/misc/fontloader-test.tex | 14 +- 6 files changed, 937 insertions(+), 457 deletions(-) (limited to 'src/fontloader/misc') diff --git a/src/fontloader/misc/fontloader-basics-nod.lua b/src/fontloader/misc/fontloader-basics-nod.lua index ea539f3..1ec2895 100644 --- a/src/fontloader/misc/fontloader-basics-nod.lua +++ b/src/fontloader/misc/fontloader-basics-nod.lua @@ -154,8 +154,8 @@ nodes.unset_attribute = node.unset_attribute nodes.protect_glyphs = node.protect_glyphs nodes.unprotect_glyphs = node.unprotect_glyphs -nodes.kerning = node.kerning -nodes.ligaturing = node.ligaturing +-----.kerning = node.kerning +-----.ligaturing = node.ligaturing nodes.mlist_to_hlist = node.mlist_to_hlist -- in generic code, at least for some time, we stay nodes, while in context @@ -194,8 +194,12 @@ nuts.insert_before = direct.insert_before nuts.insert_after = direct.insert_after nuts.delete = direct.delete nuts.copy = direct.copy +nuts.copy_list = direct.copy_list nuts.tail = direct.tail nuts.flush_list = direct.flush_list +nuts.free = direct.free +nuts.remove = direct.remove +nuts.is_node = direct.is_node nuts.end_of_math = direct.end_of_math nuts.traverse = direct.traverse nuts.traverse_id = direct.traverse_id diff --git a/src/fontloader/misc/fontloader-font-otf.lua b/src/fontloader/misc/fontloader-font-otf.lua index 302d8ea..1bb608f 100644 --- a/src/fontloader/misc/fontloader-font-otf.lua +++ b/src/fontloader/misc/fontloader-font-otf.lua @@ -2000,6 +2000,8 @@ actions["reorganize glyph lookups"] = function(data,filename,raw) end +local zero = { 0, 0 } + actions["reorganize glyph anchors"] = function(data,filename,raw) -- when we replace inplace we safe entries local descriptions = data.descriptions for unicode, description in next, descriptions do @@ -2008,14 +2010,37 @@ actions["reorganize glyph anchors"] = function(data,filename,raw) -- when we rep for class, data in next, anchors do if class == "baselig" then for tag, specification in next, data do - for i=1,#specification do - local si = specification[i] - specification[i] = { si.x or 0, si.y or 0 } + -- for i=1,#specification do + -- local si = specification[i] + -- specification[i] = { si.x or 0, si.y or 0 } + -- end + -- can be sparse so we need to fill the holes + local n = 0 + for k, v in next, specification do + if k > n then + n = k + end + local x, y = v.x, v.y + if x or y then + specification[k] = { x or 0, y or 0 } + else + specification[k] = zero + end end + local t = { } + for i=1,n do + t[i] = specification[i] or zero + end + data[tag] = t -- so # is okay (nicer for packer) end else for tag, specification in next, data do - data[tag] = { specification.x or 0, specification.y or 0 } + local x, y = specification.x, specification.y + if x or y then + data[tag] = { x or 0, y or 0 } + else + data[tag] = zero + end end end end diff --git a/src/fontloader/misc/fontloader-fonts-cbk.lua b/src/fontloader/misc/fontloader-fonts-cbk.lua index 965b968..414cafb 100644 --- a/src/fontloader/misc/fontloader-fonts-cbk.lua +++ b/src/fontloader/misc/fontloader-fonts-cbk.lua @@ -29,11 +29,28 @@ local kerning = node.kerning local basepass = true +local function l_warning() texio.write_nl("warning: node.ligaturing called directly") l_warning = nil end +local function k_warning() texio.write_nl("warning: node.kerning called directly") k_warning = nil end + +function node.ligaturing(...) + if basepass and l_warning then + l_warning() + end + return ligaturing(...) +end + +function node.kerning(...) + if basepass and k_warning then + k_warning() + end + return kerning(...) +end + function nodes.handlers.setbasepass(v) basepass = v end -function nodes.handlers.characters(head) +function nodes.handlers.nodepass(head) local fontdata = fonts.hashes.identifiers if fontdata then local usedfonts = { } @@ -115,14 +132,27 @@ function nodes.handlers.characters(head) end end -function nodes.simple_font_handler(head) - -- lang.hyphenate(head) - head = nodes.handlers.characters(head) - nodes.injections.handler(head) +function nodes.handlers.basepass(head) if not basepass then head = ligaturing(head) head = kerning(head) end - nodes.handlers.protectglyphs(head) - return head + return head, true +end + +local nodepass = nodes.handlers.nodepass +local basepass = nodes.handlers.basepass +local injectpass = nodes.injections.handler +local protectpass = nodes.handlers.protectglyphs + +function nodes.simple_font_handler(head) + if head then + head = nodepass(head) + head = injectpass(head) + head = basepass(head) + protectpass(head) + return head, true + else + return head, false + end end diff --git a/src/fontloader/misc/fontloader-fonts-inj.lua b/src/fontloader/misc/fontloader-fonts-inj.lua index 4024035..3b93382 100644 --- a/src/fontloader/misc/fontloader-fonts-inj.lua +++ b/src/fontloader/misc/fontloader-fonts-inj.lua @@ -1,23 +1,25 @@ -if not modules then modules = { } end modules ['node-inj'] = { +if not modules then modules = { } end modules ['font-inj'] = { version = 1.001, - comment = "companion to node-ini.mkiv", + comment = "companion to font-lib.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files", } --- This is very experimental (this will change when we have luatex > .50 and --- a few pending thingies are available. Also, Idris needs to make a few more --- test fonts. Some optimizations can go away when we have faster machines. +-- This property based variant is not faster but looks nicer than the attribute one. We +-- need to use rawget (which is apbout 4 times slower than a direct access but we cannot +-- get/set that one for our purpose! --- todo: ignore kerns between disc and glyph +if not nodes.properties then return end -local next = next +local next, rawget = next, rawget local utfchar = utf.char -local trace_injections = false trackers.register("nodes.injections", function(v) trace_injections = v end) +local trace_injections = false trackers.register("fonts.injections", function(v) trace_injections = v end) -local report_injections = logs.reporter("nodes","injections") +local report_injections = logs.reporter("fonts","injections") + +report_injections("using experimental injector") local attributes, nodes, node = attributes, nodes, node @@ -29,6 +31,7 @@ local injections = nodes.injections local nodecodes = nodes.nodecodes local glyph_code = nodecodes.glyph +local disc_code = nodecodes.disc local kern_code = nodecodes.kern local nuts = nodes.nuts @@ -40,207 +43,351 @@ local tonode = nuts.tonode local tonut = nuts.tonut local getfield = nuts.getfield +local setfield = nuts.setfield local getnext = nuts.getnext local getprev = nuts.getprev local getid = nuts.getid -local getattr = nuts.getattr local getfont = nuts.getfont local getsubtype = nuts.getsubtype local getchar = nuts.getchar -local setfield = nuts.setfield -local setattr = nuts.setattr - local traverse_id = nuts.traverse_id local insert_node_before = nuts.insert_before local insert_node_after = nuts.insert_after +local find_tail = nuts.tail -local a_kernpair = attributes.private('kernpair') -local a_ligacomp = attributes.private('ligacomp') -local a_markbase = attributes.private('markbase') -local a_markmark = attributes.private('markmark') -local a_markdone = attributes.private('markdone') -local a_cursbase = attributes.private('cursbase') -local a_curscurs = attributes.private('curscurs') -local a_cursdone = attributes.private('cursdone') - -local unsetvalue = attributes.unsetvalue - --- This injector has been tested by Idris Samawi Hamid (several arabic fonts as well as --- the rather demanding Husayni font), Khaled Hosny (latin and arabic) and Kaj Eigner --- (arabic, hebrew and thai) and myself (whatever font I come across). I'm pretty sure --- that this code is not 100% okay but examples are needed to figure things out. +local properties = nodes.properties.data function injections.installnewkern(nk) newkern = nk or newkern end -local cursives = { } -local marks = { } -local kerns = { } +local nofregisteredkerns = 0 +local nofregisteredpairs = 0 +local nofregisteredmarks = 0 +local nofregisteredcursives = 0 +----- markanchors = { } -- one base can have more marks +local keepregisteredcounts = false --- Currently we do gpos/kern in a bit inofficial way but when we have the extra fields in --- glyphnodes to manipulate ht/dp/wd explicitly I will provide an alternative; also, we --- can share tables. +function injections.keepcounts() + keepregisteredcounts = true +end --- For the moment we pass the r2l key ... volt/arabtype tests .. idris: this needs --- checking with husayni (volt and fontforge). +function injections.resetcounts() + nofregisteredkerns = 0 + nofregisteredpairs = 0 + nofregisteredmarks = 0 + nofregisteredcursives = 0 + keepregisteredcounts = false +end function injections.reset(n) --- if getattr(n,a_kernpair) then --- setattr(n,a_kernpair,unsetvalue) --- end --- if getattr(n,a_markdone) then --- setattr(n,a_markbase,unsetvalue) --- setattr(n,a_markmark,unsetvalue) --- setattr(n,a_markdone,unsetvalue) --- end --- if getattr(n,a_cursdone) then --- setattr(n,a_cursbase,unsetvalue) --- setattr(n,a_curscurs,unsetvalue) --- setattr(n,a_cursdone,unsetvalue) --- end --- if getattr(n,a_ligacomp) then --- setattr(n,a_ligacomp,unsetvalue) --- end + local p = rawget(properties,start) + if p and p.injections then + -- todo: decrement counters? tricky as we then need to change the nof* to not increment + -- when we change a property + p.injections = nil -- should we keep the liga index? + end end function injections.setligaindex(n,index) - setattr(n,a_ligacomp,index) + local p = rawget(properties,n) + if p then + local i = p.injections + if i then + i.ligaindex = index + else + p.injections = { + ligaindex = index + } + end + else + properties[n] = { + injections = { + ligaindex = index + } + } + end end function injections.getligaindex(n,default) - return getattr(n,a_ligacomp) or default + local p = rawget(properties,n) + if p then + p = p.injections + if p then + return p.ligaindex or default + end + end + return default end -function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) - local dx, dy = factor*(exit[1]-entry[1]), factor*(exit[2]-entry[2]) +function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) -- hm: nuts or nodes + local dx = factor*(exit[1]-entry[1]) + local dy = -factor*(exit[2]-entry[2]) local ws, wn = tfmstart.width, tfmnext.width - local bound = #cursives + 1 - setattr(start,a_cursbase,bound) - setattr(nxt,a_curscurs,bound) - cursives[bound] = { rlmode, dx, dy, ws, wn } - return dx, dy, bound + nofregisteredcursives = nofregisteredcursives + 1 + if rlmode < 0 then + dx = -(dx + wn) + else + dx = dx - ws + end + -- + local p = rawget(properties,start) + if p then + local i = p.injections + if i then + i.cursiveanchor = true + else + p.injections = { + cursiveanchor = true, + } + end + else + properties[start] = { + injections = { + cursiveanchor = true, + }, + } + end + local p = rawget(properties,nxt) + if p then + local i = p.injections + if i then + i.cursivex = dx + i.cursivey = dy + else + p.injections = { + cursivex = dx, + cursivey = dy, + } + end + else + properties[nxt] = { + injections = { + cursivex = dx, + cursivey = dy, + }, + } + end + return dx, dy, nofregisteredcursives end -function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr) +function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2lflag & tfmchr not used local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4] - -- dy = y - h - if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then - local bound = getattr(current,a_kernpair) - if bound then - local kb = kerns[bound] - -- inefficient but singles have less, but weird anyway, needs checking - kb[2], kb[3], kb[4], kb[5] = (kb[2] or 0) + x, (kb[3] or 0) + y, (kb[4] or 0)+ w, (kb[5] or 0) + h - else - bound = #kerns + 1 - setattr(current,a_kernpair,bound) - kerns[bound] = { rlmode, x, y, w, h, r2lflag, tfmchr.width } - end - return x, y, w, h, bound + if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -- okay? + local yoffset = y - h + local leftkern = x -- both kerns are set in a pair kern compared + local rightkern = w - x -- to normal kerns where we set only leftkern + if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then + nofregisteredpairs = nofregisteredpairs + 1 + if rlmode and rlmode < 0 then + leftkern, rightkern = rightkern, leftkern + end + local p = rawget(properties,current) + if p then + local i = p.injections + if i then + if leftkern ~= 0 or rightkern ~= 0 then + i.leftkern = i.leftkern or 0 + leftkern + i.rightkern = i.rightkern or 0 + rightkern + end + if yoffset ~= 0 then + i.yoffset = i.yoffset or 0 + yoffset + end + elseif leftkern ~= 0 or rightkern ~= 0 then + p.injections = { + leftkern = leftkern, + rightkern = rightkern, + yoffset = yoffset, + } + else + p.injections = { + yoffset = yoffset, + } + end + elseif leftkern ~= 0 or rightkern ~= 0 then + properties[current] = { + injections = { + leftkern = leftkern, + rightkern = rightkern, + yoffset = yoffset, + }, + } + else + properties[current] = { + injections = { + yoffset = yoffset, + }, + } + end + return x, y, w, h, nofregisteredpairs + end end return x, y, w, h -- no bound end -function injections.setkern(current,factor,rlmode,x,tfmchr) - local dx = factor*x +-- this needs checking for rl < 0 but it is unlikely that a r2l script +-- uses kernclasses between glyphs so we're probably safe (KE has a +-- problematic font where marks interfere with rl < 0 in the previous +-- case) + +function injections.setkern(current,factor,rlmode,x,injection) + local dx = factor * x if dx ~= 0 then - local bound = #kerns + 1 - setattr(current,a_kernpair,bound) - kerns[bound] = { rlmode, dx } - return dx, bound + nofregisteredkerns = nofregisteredkerns + 1 + local p = rawget(properties,current) + if not injection then + injection = "injections" + end + if p then + local i = p[injection] + if i then + i.leftkern = dx + i.leftkern or 0 + else + p[injection] = { + leftkern = dx, + } + end + else + properties[current] = { + [injection] = { + leftkern = dx, + }, + } + end + return dx, nofregisteredkerns else return 0, 0 end end -function injections.setmark(start,base,factor,rlmode,ba,ma) -- ba=baseanchor, ma=markanchor +function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=baseanchor, ma=markanchor local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) - local bound = getattr(base,a_markbase) - local index = 1 - if bound then - local mb = marks[bound] - if mb then - -- if not index then index = #mb + 1 end - index = #mb + 1 - mb[index] = { dx, dy, rlmode } - setattr(start,a_markmark,bound) - setattr(start,a_markdone,index) - return dx, dy, bound + nofregisteredmarks = nofregisteredmarks + 1 + -- markanchors[nofregisteredmarks] = base + if rlmode >= 0 then + dx = tfmbase.width - dx -- see later commented ox + end + local p = rawget(properties,start) + if p then + local i = p.injections + if i then + i.markx = dx + i.marky = dy + i.markdir = rlmode or 0 + i.markbase = nofregisteredmarks + i.markbasenode = base else - report_injections("possible problem, %U is base mark without data (id %a)",getchar(base),bound) + p.injections = { + markx = dx, + marky = dy, + markdir = rlmode or 0, + markbase = nofregisteredmarks, + markbasenode = base, + } end + else + properties[start] = { + injections = { + markx = dx, + marky = dy, + markdir = rlmode or 0, + markbase = nofregisteredmarks, + markbasenode = base, + }, + } end - index = index or 1 - bound = #marks + 1 - setattr(base,a_markbase,bound) - setattr(start,a_markmark,bound) - setattr(start,a_markdone,index) - marks[bound] = { [index] = { dx, dy, rlmode } } - return dx, dy, bound + return dx, dy, nofregisteredmarks end local function dir(n) return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset" end -local function trace(head) - report_injections("begin run") - for n in traverse_id(glyph_code,head) do - if getsubtype(n) < 256 then - local kp = getattr(n,a_kernpair) - local mb = getattr(n,a_markbase) - local mm = getattr(n,a_markmark) - local md = getattr(n,a_markdone) - local cb = getattr(n,a_cursbase) - local cc = getattr(n,a_curscurs) - local char = getchar(n) - report_injections("font %s, char %U, glyph %c",getfont(n),char,char) - if kp then - local k = kerns[kp] - if k[3] then - report_injections(" pairkern: dir %a, x %p, y %p, w %p, h %p",dir(k[1]),k[2],k[3],k[4],k[5]) - else - report_injections(" kern: dir %a, dx %p",dir(k[1]),k[2]) +local function showchar(n,nested) + local char = getchar(n) + report_injections("%wfont %s, char %U, glyph %c",nested and 2 or 0,getfont(n),char,char) +end + +local function show(n,what,nested,symbol) + if n then + local p = rawget(properties,n) + if p then + local p = p[what] + if p then + local leftkern = p.leftkern or 0 + local rightkern = p.rightkern or 0 + local yoffset = p.yoffset or 0 + local markx = p.markx or 0 + local marky = p.marky or 0 + local markdir = p.markdir or 0 + local markbase = p.markbase or 0 -- will be markbasenode + local cursivex = p.cursivex or 0 + local cursivey = p.cursivey or 0 + local ligaindex = p.ligaindex or 0 + local margin = nested and 4 or 2 + -- + if rightkern ~= 0 or yoffset ~= 0 then + report_injections("%w%s pair: lx %p, rx %p, dy %p",margin,symbol,leftkern,rightkern,yoffset) + elseif leftkern ~= 0 then + report_injections("%w%s kern: dx %p",margin,symbol,leftkern) end - end - if mb then - report_injections(" markbase: bound %a",mb) - end - if mm then - local m = marks[mm] - if mb then - local m = m[mb] - if m then - report_injections(" markmark: bound %a, index %a, dx %p, dy %p",mm,md,m[1],m[2]) - else - report_injections(" markmark: bound %a, missing index",mm) - end - else - m = m[1] - report_injections(" markmark: bound %a, dx %p, dy %p",mm,m and m[1],m and m[2]) + if markx ~= 0 or marky ~= 0 or markbase ~= 0 then + report_injections("%w%s mark: dx %p, dy %p, dir %s, base %s",margin,symbol,markx,marky,markdir,markbase ~= 0 and "yes" or "no") + end + if cursivex ~= 0 or cursivey ~= 0 then + report_injections("%w%s curs: dx %p, dy %p",margin,symbol,cursivex,cursivey) + end + if ligaindex ~= 0 then + report_injections("%w%s liga: index %i",margin,symbol,ligaindex) end end - if cb then - report_injections(" cursbase: bound %a",cb) + end + end +end + +local function showsub(n,what,where) + report_injections("begin subrun: %s",where) + for n in traverse_id(glyph_code,n) do + showchar(n,where) + show(n,what,where," ") + end + report_injections("end subrun") +end + +local function trace(head) + report_injections("begin run: %s kerns, %s pairs, %s marks and %s cursives registered", + nofregisteredkerns,nofregisteredpairs,nofregisteredmarks,nofregisteredcursives) + local n = head + while n do + local id = getid(n) + if id == glyph_code then + showchar(n) + show(n,"injections",false," ") + show(n,"preinjections",false,"<") + show(n,"postinjections",false,">") + show(n,"replaceinjections",false,"=") + elseif id == disc_code then + local pre = getfield(n,"pre") + local post = getfield(n,"post") + local replace = getfield(n,"replace") + if pre then + showsub(pre,"preinjections","pre") end - if cc then - local c = cursives[cc] - report_injections(" curscurs: bound %a, dir %a, dx %p, dy %p",cc,dir(c[1]),c[2],c[3]) + if post then + showsub(post,"postinjections","post") + end + if replace then + showsub(replace,"replaceinjections","replace") end end + n = getnext(n) end report_injections("end run") end --- todo: reuse tables (i.e. no collection), but will be extra fields anyway --- todo: check for attribute - --- We can have a fast test on a font being processed, so we can check faster for marks etc --- but I'll make a context variant anyway. - local function show_result(head) - local current = head + local current = head local skipping = false while current do local id = getid(current) @@ -259,345 +406,616 @@ local function show_result(head) end end -function injections.handler(head,where,keep) - head = tonut(head) - local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns) - if has_marks or has_cursives then - if trace_injections then - trace(head) +-- we could also check for marks here but maybe not all are registered (needs checking) + +local function collect_glyphs_1(head) + local glyphs, nofglyphs = { }, 0 + local marks, nofmarks = { }, 0 + local nf, tm = nil, nil + for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.injections + end + local f = getfont(n) + if f ~= nf then + nf = f + tm = fontdata[nf].resources.marks -- other hash in ctx + end + if tm and tm[getchar(n)] then + nofmarks = nofmarks + 1 + marks[nofmarks] = n + else + nofglyphs = nofglyphs + 1 + glyphs[nofglyphs] = n + end + -- yoffsets can influence curs steps + if pn then + local yoffset = pn.yoffset + if yoffset and yoffset ~= 0 then + setfield(n,"yoffset",yoffset) + end + end end - -- in the future variant we will not copy items but refs to tables - local done, ky, rl, valid, cx, wx, mk, nofvalid = false, { }, { }, { }, { }, { }, { }, 0 - if has_kerns then -- move outside loop - local nf, tm = nil, nil - for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts - if getsubtype(n) < 256 then - nofvalid = nofvalid + 1 - valid[nofvalid] = n - local f = getfont(n) - if f ~= nf then - nf = f - tm = fontdata[nf].resources.marks -- other hash in ctx + end + return glyphs, nofglyphs, marks, nofmarks +end + +local function collect_glyphs_2(head) + local glyphs, nofglyphs = { }, 0 + local marks, nofmarks = { }, 0 + local nf, tm = nil, nil + for n in traverse_id(glyph_code,head) do + if getsubtype(n) < 256 then + local f = getfont(n) + if f ~= nf then + nf = f + tm = fontdata[nf].resources.marks -- other hash in ctx + end + if tm and tm[getchar(n)] then + nofmarks = nofmarks + 1 + marks[nofmarks] = n + else + nofglyphs = nofglyphs + 1 + glyphs[nofglyphs] = n + end + end + end + return glyphs, nofglyphs, marks, nofmarks +end + +local function inject_marks(marks,nofmarks) + for i=1,nofmarks do + local n = marks[i] + local pn = rawget(properties,n) + if pn then + pn = pn.injections + end + if pn then + -- local markbase = pn.markbase + -- if markbase then + -- local p = markanchors[markbase] + local p = pn.markbasenode + if p then + local px = getfield(p,"xoffset") + local ox = 0 + local pp = rawget(properties,p) + local rightkern = pp and pp.rightkern + if rightkern then -- x and w ~= 0 + if pn.markdir < 0 then + -- kern(w-x) glyph(p) kern(x) mark(n) + ox = px - pn.markx - rightkern + -- report_injections("r2l case 1: %p",ox) + else + -- kern(x) glyph(p) kern(w-x) mark(n) + -- ox = px - getfield(p,"width") + pn.markx - pp.leftkern + ox = px - pn.markx - pp.leftkern + -- report_injections("l2r case 1: %p",ox) + end + else + -- we need to deal with fonts that have marks with width + -- if pn.markdir < 0 then + -- ox = px - pn.markx + -- -- report_injections("r2l case 3: %p",ox) + -- else + -- -- ox = px - getfield(p,"width") + pn.markx + ox = px - pn.markx + -- report_injections("l2r case 3: %p",ox) + -- end + local wn = getfield(n,"width") -- in arial marks have widths + if wn ~= 0 then + -- bad: we should center + -- insert_node_before(head,n,newkern(-wn/2)) + -- insert_node_after(head,n,newkern(-wn/2)) + pn.leftkern = -wn/2 + pn.rightkern = -wn/2 + -- wx[n] = { 0, -wn/2, 0, -wn } + end + -- so far end - if tm then - mk[n] = tm[getchar(n)] + setfield(n,"xoffset",ox) + -- + local py = getfield(p,"yoffset") + local oy = 0 + if marks[p] then + oy = py + pn.marky + else + oy = getfield(n,"yoffset") + py + pn.marky end - local k = getattr(n,a_kernpair) - if k then - local kk = kerns[k] - if kk then - local x, y, w, h = kk[2] or 0, kk[3] or 0, kk[4] or 0, kk[5] or 0 - local dy = y - h - if dy ~= 0 then - ky[n] = dy - end - if w ~= 0 or x ~= 0 then - wx[n] = kk - end - rl[n] = kk[1] -- could move in test + setfield(n,"yoffset",oy) + else + -- normally this can't happen (only when in trace mode which is a special case anyway) + -- report_injections("missing mark anchor %i",pn.markbase or 0) + end + -- end + end + end +end + +local function inject_cursives(glyphs,nofglyphs) + local cursiveanchor, lastanchor = nil, nil + local minc, maxc, last = 0, 0, nil + for i=1,nofglyphs do + local n = glyphs[i] + local pn = rawget(properties,n) + if pn then + pn = pn.injections + end + if pn then + local cursivex = pn.cursivex + if cursivex then + if cursiveanchor then + if cursivex ~= 0 then + pn.leftkern = pn.leftkern or 0 + cursivex + end + if lastanchor then + if maxc == 0 then + minc = lastanchor end + maxc = lastanchor + properties[cursiveanchor].cursivedy = pn.cursivey end + last = n + else + maxc = 0 + end + elseif maxc > 0 then + local ny = getfield(n,"yoffset") + for i=maxc,minc,-1 do + local ti = glyphs[i] + ny = ny + properties[ti].cursivedy + setfield(ti,"yoffset",ny) -- why not add ? end + maxc = 0 end - else - local nf, tm = nil, nil - for n in traverse_id(glyph_code,head) do - if getsubtype(n) < 256 then - nofvalid = nofvalid + 1 - valid[nofvalid] = n - local f = getfont(n) - if f ~= nf then - nf = f - tm = fontdata[nf].resources.marks -- other hash in ctx - end - if tm then - mk[n] = tm[getchar(n)] + if pn.cursiveanchor then + cursiveanchor = n + lastanchor = i + else + cursiveanchor = nil + lastanchor = nil + if maxc > 0 then + local ny = getfield(n,"yoffset") + for i=maxc,minc,-1 do + local ti = glyphs[i] + ny = ny + properties[ti].cursivedy + setfield(ti,"yoffset",ny) -- why not add ? end + maxc = 0 end end + elseif maxc > 0 then + local ny = getfield(n,"yoffset") + for i=maxc,minc,-1 do + local ti = glyphs[i] + ny = ny + properties[ti].cursivedy + setfield(ti,"yoffset",getfield(ti,"yoffset") + ny) -- ? + end + maxc = 0 + cursiveanchor = nil + lastanchor = nil end - if nofvalid > 0 then - -- we can assume done == true because we have cursives and marks - local cx = { } - if has_kerns and next(ky) then - for n, k in next, ky do - setfield(n,"yoffset",k) - end + -- if maxc > 0 and not cursiveanchor then + -- local ny = getfield(n,"yoffset") + -- for i=maxc,minc,-1 do + -- local ti = glyphs[i] + -- ny = ny + properties[ti].cursivedy + -- setfield(ti,"yoffset",ny) -- why not add ? + -- end + -- maxc = 0 + -- end + end + if last and maxc > 0 then + local ny = getfield(last,"yoffset") + for i=maxc,minc,-1 do + local ti = glyphs[i] + ny = ny + properties[ti].cursivedy + setfield(ti,"yoffset",ny) -- why not add ? + end + end +end + +local function inject_kerns(head,glyphs,nofglyphs) + -- todo: pre/post/replace + for i=1,#glyphs do + local n = glyphs[i] + local pn = rawget(properties,n) + if pn then + pn = pn.injections + end + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + insert_node_before(head,n,newkern(leftkern)) -- type 0/2 + end + local rightkern = pn.rightkern + if rightkern and rightkern ~= 0 then + insert_node_after(head,n,newkern(rightkern)) -- type 0/2 end - -- todo: reuse t and use maxt - if has_cursives then - local p_cursbase, p = nil, nil - -- since we need valid[n+1] we can also use a "while true do" - local t, d, maxt = { }, { }, 0 - for i=1,nofvalid do -- valid == glyphs - local n = valid[i] - if not mk[n] then - local n_cursbase = getattr(n,a_cursbase) - if p_cursbase then - local n_curscurs = getattr(n,a_curscurs) - if p_cursbase == n_curscurs then - local c = cursives[n_curscurs] - if c then - local rlmode, dx, dy, ws, wn = c[1], c[2], c[3], c[4], c[5] - if rlmode >= 0 then - dx = dx - ws - else - dx = dx + wn - end - if dx ~= 0 then - cx[n] = dx - rl[n] = rlmode - end - -- if rlmode and rlmode < 0 then - dy = -dy - -- end - maxt = maxt + 1 - t[maxt] = p - d[maxt] = dy - else - maxt = 0 + end + end +end + +local function inject_everything(head,where) + head = tonut(head) + if trace_injections then + trace(head) + end + local glyphs, nofglyphs, marks, nofmarks + if nofregisteredpairs > 0 then + glyphs, nofglyphs, marks, nofmarks = collect_glyphs_1(head) + else + glyphs, nofglyphs, marks, nofmarks = collect_glyphs_2(head) + end + if nofglyphs > 0 then + if nofregisteredcursives > 0 then + inject_cursives(glyphs,nofglyphs) + end + if nofregisteredmarks > 0 then + inject_marks(marks,nofmarks) + end + inject_kerns(head,glyphs,nofglyphs) + end + if keepregisteredcounts then + keepregisteredcounts = false + else + nofregisteredkerns = 0 + nofregisteredpairs = 0 + nofregisteredmarks = 0 + nofregisteredcursives = 0 + end + return tonode(head), true +end + +local function inject_kerns_only(head,where) + head = tonut(head) + if trace_injections then + trace(head) + end + local n = head + local p = nil + while n do + local id = getid(n) + if id == glyph_code then + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + if p then + local d = getfield(p,"post") + if d then + local pn = pn.postinjections + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + local t = find_tail(d) + insert_node_after(d,t,newkern(leftkern)) end end - elseif maxt > 0 then - local ny = getfield(n,"yoffset") - for i=maxt,1,-1 do - ny = ny + d[i] - local ti = t[i] - setfield(ti,"yoffset",getfield(ti,"yoffset") + ny) + end + local d = getfield(p,"replace") + if d then + local pn = pn.replaceinjections + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + local t = find_tail(d) + insert_node_after(d,t,newkern(leftkern)) + end + end + else + local pn = pn.injections + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + setfield(p,"replace",newkern(leftkern)) + end end - maxt = 0 end - if not n_cursbase and maxt > 0 then - local ny = getfield(n,"yoffset") - for i=maxt,1,-1 do - ny = ny + d[i] - local ti = t[i] - setfield(ti,"yoffset",ny) -- maybe add to current yoffset + else + local pn = pn.injections + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + head = insert_node_before(head,n,newkern(leftkern)) end - maxt = 0 end - p_cursbase, p = n_cursbase, n end end - if maxt > 0 then - local ny = getfield(n,"yoffset") -- hm, n unset ? - for i=maxt,1,-1 do - ny = ny + d[i] - local ti = t[i] - setfield(ti,"yoffset",ny) + else + break + end + p = nil + elseif id == disc_code then + local d = getfield(n,"pre") + if d then + local h = d + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.preinjections + end + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) + end + end + else + break end - maxt = 0 end - if not keep then - cursives = { } + if h ~= d then + setfield(n,"pre",h) end end - if has_marks then - for i=1,nofvalid do - local p = valid[i] - local p_markbase = getattr(p,a_markbase) - if p_markbase then - local mrks = marks[p_markbase] - local nofmarks = #mrks - for n in traverse_id(glyph_code,getnext(p)) do - local n_markmark = getattr(n,a_markmark) - if p_markbase == n_markmark then - local index = getattr(n,a_markdone) or 1 - local d = mrks[index] - if d then - local rlmode = d[3] - -- - local k = wx[p] - local px = getfield(p,"xoffset") - local ox = 0 - if k then - local x = k[2] - local w = k[4] - if w then - if rlmode and rlmode >= 0 then - -- kern(x) glyph(p) kern(w-x) mark(n) - ox = px - getfield(p,"width") + d[1] - (w-x) - -- report_injections("l2r case 1: %p",ox) - else - -- kern(w-x) glyph(p) kern(x) mark(n) - ox = px - d[1] - x - -- report_injections("r2l case 1: %p",ox) - end - else - if rlmode and rlmode >= 0 then - -- okay for husayni - ox = px - getfield(p,"width") + d[1] - -- report_injections("r2l case 2: %p",ox) - else - -- needs checking: is x ok here? - ox = px - d[1] - x - -- report_injections("r2l case 2: %p",ox) - end - end - else - -- if rlmode and rlmode >= 0 then - -- ox = px - getfield(p,"width") + d[1] - -- -- report_injections("l2r case 3: %p",ox) - -- else - -- ox = px - d[1] - -- -- report_injections("r2l case 3: %p",ox) - -- end - -- - -- we need to deal with fonts that have marks with width - -- - local wp = getfield(p,"width") - local wn = getfield(n,"width") -- in arial marks have widths - if rlmode and rlmode >= 0 then - ox = px - wp + d[1] - -- report_injections("l2r case 3: %p",ox) - else - ox = px - d[1] - -- report_injections("r2l case 3: %p",ox) - end - if wn ~= 0 then - -- bad: we should center - insert_node_before(head,n,newkern(-wn/2)) - insert_node_after(head,n,newkern(-wn/2)) - -- wx[n] = { 0, -wn/2, 0, -wn } - end - -- so far - end - setfield(n,"xoffset",ox) - -- - local py = getfield(p,"yoffset") - local oy = 0 - if mk[p] then - oy = py + d[2] - else - oy = getfield(n,"yoffset") + py + d[2] - end - setfield(n,"yoffset",oy) - -- - if nofmarks == 1 then - break - else - nofmarks = nofmarks - 1 - end - end - elseif not n_markmark then - break -- HH: added 2013-09-12: no need to deal with non marks - else - -- KE: there can be sequences in ligatures + local d = getfield(n,"post") + if d then + local h = d + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.postinjections + end + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) end end + else + break end end - if not keep then - marks = { } + if h ~= d then + setfield(n,"post",h) end end - -- todo : combine - if next(wx) then - for n, k in next, wx do - -- only w can be nil (kernclasses), can be sped up when w == nil - local x = k[2] - local w = k[4] - if w then - local rl = k[1] -- r2l = k[6] - local wx = w - x - if rl < 0 then -- KE: don't use r2l here - if wx ~= 0 then - insert_node_before(head,n,newkern(wx)) -- type 0/2 + local d = getfield(n,"replace") + if d then + local h = d + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) -- why can it be empty { } + if pn then + pn = pn.replaceinjections + end + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) end - if x ~= 0 then - insert_node_after (head,n,newkern(x)) -- type 0/2 + end + else + break + end + end + if h ~= d then + setfield(n,"replace",h) + end + end + p = n + else + p = nil + end + n = getnext(n) + end + -- + if keepregisteredcounts then + keepregisteredcounts = false + else + nofregisteredkerns = 0 + end + return tonode(head), true +end + +local function inject_pairs_only(head,where) + head = tonut(head) + if trace_injections then + trace(head) + end + -- + local n = head + local p = nil + while n do + local id = getid(n) + if id == glyph_code then + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + if p then + local d = getfield(p,"post") + if d then + local pn = pn.postinjections + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + local t = find_tail(d) + insert_node_after(d,t,newkern(leftkern)) + end + -- local rightkern = pn.rightkern + -- if rightkern and rightkern ~= 0 then + -- insert_node_after(head,n,newkern(rightkern)) + -- n = getnext(n) -- to be checked + -- end + end + end + local d = getfield(p,"replace") + if d then + local pn = pn.replaceinjections + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + local t = find_tail(d) + insert_node_after(d,t,newkern(leftkern)) + end + -- local rightkern = pn.rightkern + -- if rightkern and rightkern ~= 0 then + -- insert_node_after(head,n,newkern(rightkern)) + -- n = getnext(n) -- to be checked + -- end end else - if x ~= 0 then - insert_node_before(head,n,newkern(x)) -- type 0/2 + local pn = pn.injections + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + setfield(p,"replace",newkern(leftkern)) + end + -- local rightkern = pn.rightkern + -- if rightkern and rightkern ~= 0 then + -- insert_node_after(head,n,newkern(rightkern)) + -- n = getnext(n) -- to be checked + -- end + end + end + else + -- this is the most common case + local pn = pn.injections + if pn then + local yoffset = pn.yoffset + if yoffset and yoffset ~= 0 then + setfield(n,"yoffset",yoffset) end - if wx ~= 0 then - insert_node_after (head,n,newkern(wx)) -- type 0/2 + local leftkern = pn.leftkern + if leftkern ~= 0 then + insert_node_before(head,n,newkern(leftkern)) + end + local rightkern = pn.rightkern + if rightkern and rightkern ~= 0 then + insert_node_after(head,n,newkern(rightkern)) + n = getnext(n) -- to be checked end end - elseif x ~= 0 then - -- this needs checking for rl < 0 but it is unlikely that a r2l script - -- uses kernclasses between glyphs so we're probably safe (KE has a - -- problematic font where marks interfere with rl < 0 in the previous - -- case) - insert_node_before(head,n,newkern(x)) -- a real font kern, type 0 end end + else + break end - if next(cx) then - for n, k in next, cx do - if k ~= 0 then - local rln = rl[n] - if rln and rln < 0 then - insert_node_before(head,n,newkern(-k)) -- type 0/2 - else - insert_node_before(head,n,newkern(k)) -- type 0/2 + p = nil + elseif id == disc_code then + local d = getfield(n,"pre") + if d then + local h = d + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.preinjections end + if pn then + local yoffset = pn.yoffset + if yoffset and yoffset ~= 0 then + setfield(n,"yoffset",yoffset) + end + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) + end + local rightkern = pn.rightkern + if rightkern and rightkern ~= 0 then + insert_node_after(head,n,newkern(rightkern)) + n = getnext(n) -- to be checked + end + end + else + break end end + if h ~= d then + setfield(n,"pre",h) + end end - if not keep then - kerns = { } + local d = getfield(n,"post") + if d then + local h = d + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.postinjections + end + if pn then + local yoffset = pn.yoffset + if yoffset and yoffset ~= 0 then + setfield(n,"yoffset",yoffset) + end + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) + end + local rightkern = pn.rightkern + if rightkern and rightkern ~= 0 then + insert_node_after(head,n,newkern(rightkern)) + n = getnext(n) -- to be checked + end + end + else + break + end + end + if h ~= d then + setfield(n,"post",h) + end end - -- if trace_injections then - -- show_result(head) - -- end - return tonode(head), true - elseif not keep then - kerns, cursives, marks = { }, { }, { } - end - elseif has_kerns then - if trace_injections then - trace(head) - end - for n in traverse_id(glyph_code,head) do - if getsubtype(n) < 256 then - local k = getattr(n,a_kernpair) - if k then - local kk = kerns[k] - if kk then - local rl, x, y, w = kk[1], kk[2] or 0, kk[3], kk[4] - if y and y ~= 0 then - setfield(n,"yoffset",y) -- todo: h ? + local d = getfield(n,"replace") + if d then + local h = d + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.replaceinjections end - if w then - -- copied from above - -- local r2l = kk[6] - local wx = w - x - if rl < 0 then -- KE: don't use r2l here - if wx ~= 0 then - insert_node_before(head,n,newkern(wx)) - end - if x ~= 0 then - insert_node_after (head,n,newkern(x)) - end - else - if x ~= 0 then - insert_node_before(head,n,newkern(x)) - end - if wx ~= 0 then - insert_node_after(head,n,newkern(wx)) - end + if pn then + local yoffset = pn.yoffset + if yoffset and yoffset ~= 0 then + setfield(n,"yoffset",yoffset) end - else - -- simple (e.g. kernclass kerns) - if x ~= 0 then - insert_node_before(head,n,newkern(x)) + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) + end + local rightkern = pn.rightkern + if rightkern and rightkern ~= 0 then + insert_node_after(head,n,newkern(rightkern)) + n = getnext(n) -- to be checked end end + else + break end end + if h ~= d then + setfield(n,"replace",h) + end end + p = n + else + p = nil end - if not keep then - kerns = { } - end - -- if trace_injections then - -- show_result(head) - -- end - return tonode(head), true + n = getnext(n) + end + -- + if keepregisteredcounts then + keepregisteredcounts = false + else + nofregisteredpairs = 0 + nofregisteredkerns = 0 + end + return tonode(head), true +end + +function injections.handler(head,where) -- optimize for n=1 ? + if nofregisteredmarks > 0 or nofregisteredcursives > 0 then + return inject_everything(head,where) + elseif nofregisteredpairs > 0 then + return inject_pairs_only(head,where) + elseif nofregisteredkerns > 0 then + return inject_kerns_only(head,where) else - -- no tracing needed + return head, false end - return tonode(head), false end diff --git a/src/fontloader/misc/fontloader-fonts.lua b/src/fontloader/misc/fontloader-fonts.lua index 678a283..c81e8cd 100644 --- a/src/fontloader/misc/fontloader-fonts.lua +++ b/src/fontloader/misc/fontloader-fonts.lua @@ -29,11 +29,12 @@ if not modules then modules = { } end modules ['luatex-fonts'] = { texio.write_nl("") texio.write_nl("--------------------------------------------------------------------------------") -texio.write_nl("The font code has been brought in sync with the context version of 2014.12.01 so") +texio.write_nl("The font code has been brought in sync with the context version of 2014.12.21 so") texio.write_nl("if things don't work out as expected the interfacing needs to be checked. When") texio.write_nl("this works as expected a second upgrade will happen that gives a more complete") texio.write_nl("support and another sync with the context code (that new code is currently being") -texio.write_nl("tested. The base pass is now integrated in the main pass.") +texio.write_nl("tested. The base pass is now integrated in the main pass. The results can differ") +texio.write_nl("from those in context because there we integrate some mechanisms differently.") texio.write_nl("--------------------------------------------------------------------------------") texio.write_nl("") @@ -260,8 +261,8 @@ generic_context.callback_define_font = fonts.definers.read if not generic_context.no_callbacks_yet then - -- callback.register('ligaturing', generic_context.callback_ligaturing) - -- callback.register('kerning', generic_context.callback_kerning) + callback.register('ligaturing', generic_context.callback_ligaturing) + callback.register('kerning', generic_context.callback_kerning) callback.register('pre_linebreak_filter', generic_context.callback_pre_linebreak_filter) callback.register('hpack_filter', generic_context.callback_hpack_filter) callback.register('define_font' , generic_context.callback_define_font) diff --git a/src/fontloader/misc/fontloader-test.tex b/src/fontloader/misc/fontloader-test.tex index 169a260..6f48e0c 100644 --- a/src/fontloader/misc/fontloader-test.tex +++ b/src/fontloader/misc/fontloader-test.tex @@ -35,14 +35,16 @@ \font\gothic=msgothic(ms-gothic) {\gothic whatever} -\font\testy=file:IranNastaliq.ttf:mode=node;script=arab;language=dflt;+calt;+ccmp;+init;+isol;+medi;+fina;+liga;+rlig;+kern;+mark;+mkmk at 14pt -\testy این یک متن نمونه است با قلم ذر که درست آمده است. +\bgroup -\pdfprotrudechars2 \pdfadjustspacing2 + \pdfprotrudechars2 + \pdfadjustspacing2 -\font\testb=file:lmroman12-regular:+liga;extend=1.5 at 12pt \testb \input tufte \par -\font\testb=file:lmroman12-regular:+liga;slant=0.8 at 12pt \testb \input tufte \par -\font\testb=file:lmroman12-regular:+liga;protrusion=default at 12pt \testb \input tufte \par + \font\testb=file:lmroman12-regular:+liga;extend=1.5 at 12pt \testb \input tufte \par + \font\testb=file:lmroman12-regular:+liga;slant=0.8 at 12pt \testb \input tufte \par + \font\testb=file:lmroman12-regular:+liga;protrusion=default at 12pt \testb \input tufte \par + +\egroup \setmplibformat{plain} -- cgit v1.2.3