diff options
Diffstat (limited to 'tex/context/base/font-otf.lua')
-rw-r--r-- | tex/context/base/font-otf.lua | 1270 |
1 files changed, 792 insertions, 478 deletions
diff --git a/tex/context/base/font-otf.lua b/tex/context/base/font-otf.lua index 196e155dc..fffd4eeda 100644 --- a/tex/context/base/font-otf.lua +++ b/tex/context/base/font-otf.lua @@ -6,15 +6,14 @@ if not modules then modules = { } end modules ['font-otf'] = { license = "see context related readme files" } --- once we have all features working, i will redo this module .. caching lookups and such - -local format = string.format +local format, concat, getn = string.format, table.concat, table.getn +local type, pairs, ipairs, next, tonumber, tostring = type, pairs, ipairs, next, tonumber, tostring local space = lpeg.P(" ") local nospaces = (1-space)^1 local optionalspace = space^0 -local split_at_space = lpeg.Ct((lpeg.C(nospaces) * optionalspace)^0) +local split_at_space = lpeg.Ct((lpeg.C(nospaces) * optionalspace)^0) -- table ! -- we can use more lpegs when lpeg is extended with function args and so @@ -96,13 +95,13 @@ number by one when there's a fix in the <l n='fontforge'/> library or --~ is also faster). A further complication is that we support static as well as dynamic --~ features. -fonts = fonts or { } -fonts.otf = fonts.otf or { } +fonts = fonts or { } +fonts.otf = fonts.otf or { } -local otf = fonts.otf -local tfm = fonts.tfm +local otf = fonts.otf +local tfm = fonts.tfm -otf.version = 2.10 +otf.version = 2.24 otf.pack = true otf.tables = otf.tables or { } otf.meanings = otf.meanings or { } @@ -845,6 +844,9 @@ function otf.load(filename,format,sub,featurefile) hash = hash:gsub("[^%w%d]+","-") end local data = containers.read(otf.cache(), hash) + if data and data.verbose ~= fonts.verbose then + data = nil + end local size = lfs.attributes(filename,"size") or 0 if data and data.size ~= size then data = nil @@ -879,6 +881,8 @@ function otf.load(filename,format,sub,featurefile) data = fontforge.to_table(ff) fontforge.close(ff) if data then + logs.report("load otf","enhance: patch") + otf.enhance.patch(data,filename) logs.report("load otf","enhance: before") otf.enhance.before(data,filename) logs.report("load otf","enhance: enrich") @@ -889,16 +893,15 @@ function otf.load(filename,format,sub,featurefile) otf.enhance.analyze(data,filename) logs.report("load otf","enhance: after") otf.enhance.after(data,filename) - logs.report("load otf","enhance: patch") - otf.enhance.patch(data,filename) logs.report("load otf","enhance: strip") otf.enhance.strip(data,filename) - if otf.pack then + if otf.pack and not fonts.verbose then logs.report("load otf","enhance: pack") otf.enhance.pack(data) end logs.report("load otf","file size: %s", size) data.size = size + data.verbose = fonts.verbose logs.report("load otf","saving: in cache") data = containers.write(otf.cache(), hash, data) else @@ -914,13 +917,17 @@ end -- memory saver .. +local criterium, threshold = 1, 0 + function otf.enhance.pack(data) if data then - local h, t = { }, { } - local concat = table.concat + local h, t, c = { }, { }, { } + local hh, tt, cc = { }, { }, { } local function tabstr(t) for i=1,#t do - if type(t[i]) == "boolean" then + -- tricky, was if type(t[i]) == "boolean" then, but if no [1] then error + local ti = type(t[i]) + if ti ~= "string" or ti ~= "number" then local s = tostring(t[1]) for i=2,#t do s = s .. ",".. tostring(t[i]) @@ -930,67 +937,125 @@ function otf.enhance.pack(data) end return concat(t,",") end - local function pack(v) - local tag = tabstr(v,",") - if not h[tag] then - t[#t+1] = v - h[tag] = #t - end - return h[tag] - end - for k, v in pairs(data.glyphs) do - v.boundingbox = pack(v.boundingbox) - if v.lookups then - for k,v in pairs(v.lookups) do - for kk, vv in ipairs(v) do -- for i= - v[kk] = pack(vv) + for pass=1,2 do + local pack + if pass == 1 then + pack = function(v) + -- v == table + local tag = tabstr(v,",") + local ht = h[tag] + if not ht then + ht = #t+1 + t[ht] = v + h[tag] = ht + c[ht] = 1 + else + c[ht] = c[ht] + 1 end + return ht end - end - if v.anchors then - for k,v in pairs(v.anchors) do - if k == "baselig" then - for kk, vv in pairs(v) do - for kkk,vvv in ipairs(vv) do - vv[kkk] = pack(vvv) - end - end + else + pack = function(v) + -- v == number + if c[v] <= criterium then + return t[v] else - for kk, vv in pairs(v) do - v[kk] = pack(vv) + -- compact hash + local hv = hh[v] + if not hv then + hv = #tt+1 + tt[hv] = t[v] + hh[v] = hv + cc[hv] = c[v] end + return hv end end end - end - if data.lookups then - for k, v in pairs(data.lookups) do - if v.rules then - for kk, vv in pairs(v.rules) do - local l = vv.lookups - if l then - vv.lookups = pack(l) + for k, v in pairs(data.glyphs) do + v.boundingbox = pack(v.boundingbox) + if v.lookups then + for k,v in pairs(v.lookups) do + for kk=1,#v do + v[kk] = pack(v[kk]) end - local c = vv.coverage - if c then - c.before = c.before and pack(c.before ) - c.after = c.after and pack(c.after ) - c.current = c.current and pack(c.current) + end + end + local a = v.anchors + if a then + for k,v in pairs(a) do + if k == "baselig" then + for kk, vv in pairs(v) do + for kkk=1,#vv do + vv[kkk] = pack(vv[kkk]) + end + end + else + for kk, vv in pairs(v) do + v[kk] = pack(vv) + end end end end end - end - if data.luatex then - local li = data.luatex.ignore_flags - if li then - for k, v in pairs(li) do - li[k] = pack(v) + if data.lookups then + for k, v in pairs(data.lookups) do + if v.rules then + for kk, vv in pairs(v.rules) do + local l = vv.lookups + if l then + vv.lookups = pack(l) + end + local c = vv.coverage + if c then + c.before = c.before and pack(c.before ) + c.after = c.after and pack(c.after ) + c.current = c.current and pack(c.current) + end + end + end end end - end - if #t > 0 then - data.tables = t + if data.luatex then + local li = data.luatex.ignore_flags + if li then + for k, v in pairs(li) do + li[k] = pack(v) + end + end + end + if #t == 0 then + logs.report("load otf","pack quality: nothing to pack") + break + elseif #t >= threshold then + local one, two, rest = 0, 0, 0 + if pass == 1 then + for k,v in pairs(c) do + if v == 1 then + one = one + 1 + elseif v == 2 then + two = two + 1 + else + rest = rest + 1 + end + end + else + for k,v in pairs(cc) do + if v >20 then + rest = rest + 1 + elseif v >10 then + two = two + 1 + else + one = one + 1 + end + end + data.tables = tt + end + logs.report("load otf","pack quality: pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)", pass, one+two+rest, one, two, rest, criterium) + else + logs.report("load otf","pack quality: pass 1, %s packed, aborting pack (threshold: %s)", #t, threshold) + break + end end end end @@ -1000,25 +1065,27 @@ function otf.enhance.unpack(data) local t = data.tables if t then for k, v in pairs(data.glyphs) do - v.boundingbox = t[v.boundingbox] + local tv = t[v.boundingbox] if tv then v.boundingbox = tv end local l = v.lookups if l then for k,v in pairs(l) do for i=1,#v do - v[i] = t[v[i]] + local tv = t[v[i]] if tv then v[i] = tv end end end end local a = v.anchors if a then for k,v in pairs(a) do - for kk, vv in pairs(v) do - if kk == "baselig" then - for kkk,vvv in ipairs(vv) do - vv[kkk] = t[vvv] + if k == "baselig" then + for kk, vv in pairs(v) do + for kkk=1,#vv do + local tv = t[vv[kkk]] if tv then vv[kkk] = tv end end - else - v[kk] = t[vv] + end + else + for kk, vv in pairs(v) do + local tv = t[vv] if tv then v[kk] = tv end end end end @@ -1031,13 +1098,13 @@ function otf.enhance.unpack(data) for kk, vv in pairs(r) do local l = vv.lookups if l then - vv.lookups = t[l] + local tv = t[l] if tv then vv.lookups = tv end end local c = vv.coverage if c then - local cc = c.before if cc then c.before = t[cc] end - cc = c.after if cc then c.after = t[cc] end - cc = c.current if cc then c.current = t[cc] end + local cc = c.before if cc then local tv = t[cc] if tv then c.before = tv end end + cc = c.after if cc then local tv = t[cc] if tv then c.after = tv end end + cc = c.current if cc then local tv = t[cc] if tv then c.current = tv end end end end end @@ -1047,7 +1114,7 @@ function otf.enhance.unpack(data) local li = data.luatex.ignore_flags if li then for k, v in pairs(li) do - li[k] = t[v] + local tv = t[v] if tv then li[k] = tv end end end end @@ -1060,7 +1127,8 @@ end function otf.enhance.analyze(data,filename) local t = { - filename = file.basename(filename), +--~ filename = file.basename(filename), + filename = filename, version = otf.version, creator = "context mkiv", unicodes = otf.analyze_unicodes(data), @@ -1089,8 +1157,6 @@ do local unicodes, names = { }, {} - local tonumber = tonumber - local function do_one(a,b) unicodes[tonumber(a)] = tonumber(b,16) end @@ -1206,6 +1272,7 @@ function otf.enhance.before(data,filename) local private = fonts.private if data.subfonts and table.is_empty(data.glyphs) then local cidinfo = data.cidinfo + local verbose = fonts.verbose if cidinfo.registry then local cidmap = otf.cidmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) if cidmap then @@ -1221,12 +1288,12 @@ function otf.enhance.before(data,filename) g.boundingbox = g.boundingbox -- or zerobox g.name = g.name or name or "unknown" if unicode then - g.unicode = unicode +-- g.unicode = unicode uni_to_int[unicode] = index int_to_uni[index] = unicode nofunicodes = nofunicodes + 1 elseif name then - g.unicode = -1 +-- g.unicode = -1 nofnames = nofnames + 1 end glyphs[index] = g @@ -1251,15 +1318,15 @@ function otf.enhance.before(data,filename) local int_to_uni = data.map.backmap -- { [0|1] = unic, ... } for index, glyph in pairs(data.glyphs) do if glyph.name then - local unic = glyph.unicode or glyph.unicodeenc or -1 - glyph.unicodeenc = nil -- older luatex version +-- local unic = glyph.unicode or glyph.unicodeenc or -1 +local unic = int_to_uni[index] or -1 if index > 0 and (unic == -1 or unic >= 0x110000) then while uni_to_int[private] do private = private + 1 end uni_to_int[private] = index int_to_uni[index] = private - glyph.unicode = private +-- glyph.unicode = private if fonts.trace then logs.report("load otf","enhance: glyph %s at index %s is moved to private unicode slot %s",glyph.name,index,private) end @@ -1292,7 +1359,6 @@ function otf.enhance.before(data,filename) if data.ttf_tables then for _, v in ipairs(data.ttf_tables) do if v.data then v.data = "deleted" end - --~ if v.data then v.data = v.data:gsub("\026","\\026") end -- does not work out well end end table.compact(data.glyphs) @@ -1314,11 +1380,6 @@ function otf.enhance.before(data,filename) end end end ---~ for index, glyph in pairs(data.glyphs) do ---~ for k,v in pairs(glyph) do ---~ if v == 0 then glyph[k] = nil end ---~ end ---~ end end function otf.enhance.after(data,filename) -- to be split @@ -1332,16 +1393,21 @@ function otf.enhance.after(data,filename) -- to be split local vc, vo, vl = v.char, v.off, v.lookup if vc and vo and vl then -- brrr, wrong! we miss the non unicode ones local uvc = unicodes[vc] - if uvc then + if not uvc then + logs.report("load otf","problems with unicode %s of kern %s at glyph %s",vc,k,index) + else local mkl = mykerns[vl] if not mkl then - mkl = { [unicodes[vc]] = vo } + mkl = { } mykerns[v.lookup] = mkl + end + if type(uvc) == "table" then + for u=1,#uvc do + mkl[uvc[u]] = vo + end else - mkl[unicodes[vc]] = vo + mkl[uvc] = vo end - else - logs.report("load otf","problems with unicode %s of kern %s at glyph %s",vc,k,index) end end end @@ -1361,24 +1427,38 @@ function otf.enhance.after(data,filename) -- to be split if kernclass then for _, kcl in ipairs(kernclass) do local firsts, seconds, offsets, lookup = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup - local maxfirsts, maxseconds = table.getn(firsts), table.getn(seconds) + local maxfirsts, maxseconds = getn(firsts), getn(seconds) logs.report("load otf", "adding kernclass %s with %s times %s pairs)",lookup, maxfirsts, maxseconds) for fk, fv in pairs(firsts) do for first in fv:gmatch("[^ ]+") do - local glyph = glyphs[mapmap[unicodes[first]]] - local mykerns = glyph.mykerns - if not mykerns then - mykerns = { } -- unicode indexed ! - glyph.mykerns = mykerns - end - local lookupkerns = mykerns[lookup] - if not lookupkerns then - lookupkerns = { } - mykerns[lookup] = lookupkerns + local first_unicode = unicodes[first] + if type(first_unicode) == "number" then + first_unicode = { first_unicode } end - for sk, sv in pairs(seconds) do - for second in sv:gmatch("[^ ]+") do - lookupkerns[unicodes[second]] = offsets[(fk-1) * maxseconds + sk] + for f=1,#first_unicode do + local glyph = glyphs[mapmap[first_unicode[f]]] + local mykerns = glyph.mykerns + if not mykerns then + mykerns = { } -- unicode indexed ! + glyph.mykerns = mykerns + end + local lookupkerns = mykerns[lookup] + if not lookupkerns then + lookupkerns = { } + mykerns[lookup] = lookupkerns + end + for sk, sv in pairs(seconds) do + local offset = offsets[(fk-1) * maxseconds + sk] + for second in sv:gmatch("[^ ]+") do + local second_unicode = unicodes[second] + if type(second_unicode) == "number" then + lookupkerns[second_unicode] = offset + else + for s=1,#second_unicode do + lookupkerns[second_unicode[s]] = offset + end + end + end end end end @@ -1395,13 +1475,34 @@ function otf.enhance.after(data,filename) -- to be split end function otf.enhance.strip(data) + local verbose = fonts.verbose + local int_to_uni = data.map.backmap for k, v in pairs(data.glyphs) do local d = v.dependents if d then v.dependents = nil end + if verbose then + local code = int_to_uni[k] + if code then + local vu = v.unicode + if not vu then + v.unicode = code + elseif type(vu) == "table" then + vu[#bu+1] = code + else + v.unicode = { vu, code } + end + end + else + v.unicode = nil + v.index = nil + end end + data.luatex.comment = "Glyph tables have their original index. When present, mykern tables are indexed by unicode." + data.luatex.indices = data.map.map -- needed for shared glyphs data.map = nil data.names = nil - data.luatex.comment = "Glyph tables have their original index. When present, mykern tables are indexed by unicode." + data.glyphcnt = nil + data.glyphmax = nil end function otf.enhance.flatten(data,filename) -- to be split @@ -1490,7 +1591,7 @@ end otf.enhance.patches = { } function otf.enhance.patch(data,filename) - local basename = file.basename(filename) + local basename = file.basename(filename:lower()) for pattern, action in pairs(otf.enhance.patches) do if basename:find(pattern) then action(data,filename) @@ -1504,31 +1605,13 @@ function otf.enhance.enrich(data,filename) -- later end --- patching - -do -- will move to a typescript - - local function patch(data,filename) - if data.design_size == 0 then - local ds = (file.basename(filename)):match("(%d+)") - if ds then - logs.report("load otf","patching design size (%s)",ds) - data.design_size = tonumber(ds) * 10 - end - end - end - - otf.enhance.patches["^lmroman"] = patch - otf.enhance.patches["^lmsans"] = patch - otf.enhance.patches["^lmtypewriter"] = patch - -end - function otf.analyze_class(data,class) local classes = { } - for index, glyph in pairs(data.glyphs) do + local glyphs = data.glyphs + for unicode, index in pairs(data.map.map) do + local glyph = glyphs[index] if glyph.class == class then - classes[glyph.unicode] = true + classes[unicode] = true end end return classes @@ -1541,9 +1624,10 @@ function otf.analyze_subtables(data) for k,v in ipairs(g) do if v.features then local ignored = { false, false, false } - if v.flags.ignorecombiningmarks then ignored[1] = 'mark' end - if v.flags.ignorebasechars then ignored[2] = 'base' end - if v.flags.ignoreligatures then ignored[3] = 'ligature' end + local flags = v.flags + if flags.ignorecombiningmarks then ignored[1] = 'mark' end + if flags.ignorebasechars then ignored[2] = 'base' end + if flags.ignoreligatures then ignored[3] = 'ligature' end if v.subtables then local type = v.type for _, feature in ipairs(v.features) do @@ -1611,12 +1695,28 @@ end function otf.analyze_unicodes(data) local unicodes = { } - for _, blob in pairs(data.glyphs) do - if blob.name then - unicodes[blob.name] = blob.unicode or 0 + local indices = data.map.map + local glyphs = data.glyphs + local multiples = { } + for unicode, index in pairs(indices) do + local name = glyphs[index].name + if name then + local un = unicodes[name] + if not un then + unicodes[name] = unicode -- or 0 + elseif type(un) == "number" then + multiples[#multiples+1] = name + unicodes[name] = { un, unicode } + else + un[#un+1] = unicode + end end end - unicodes['space'] = unicodes['space'] or 32 -- handly later on + if #multiples > 0 then + logs.report("load otf","%s glyph are reused: %s",#multiples, concat(multiples," ")) + end + unicodes['space'] = unicodes['space'] or 32 -- handly later on + unicodes['hyphen'] = unicodes['hyphen'] or 45 -- handly later on return unicodes end @@ -1783,11 +1883,15 @@ end function otf.features.prepare_base_kerns(tfmdata,kind,value) -- todo what kind of kerns, currently all if value then local otfdata = tfmdata.shared.otfdata - local charlist = otfdata.glyphs - local unicodes = otfdata.luatex.unicodes + local glyphs = otfdata.glyphs + local unicodes = otfdata.luatex.unicodes -- names to unicodes local somevalid = otf.some_valid_feature(otfdata,kind,tfmdata.script,tfmdata.language) - for _, chr in pairs(tfmdata.characters) do - local d = charlist[chr.description.index] + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + for u, chr in pairs(characters) do + -- hm, maybe just use descriptions, and why still index? font is already in + -- unicode with private slots, so: d = glyphs[u] should work ok + local d = glyphs[descriptions[u].index] if d then local dk = d.mykerns if dk then @@ -1811,8 +1915,18 @@ function otf.features.prepare_base_kerns(tfmdata,kind,value) -- todo what kind o for _, v in pairs(dk) do if somevalid[v.lookup] then local k = unicodes[v.char] - if k > 0 then - t[k], done = v.off, true + local o = v.off + if type(k) == "number" then + if k > 0 then + t[k], done = o, true + end + else + for i=1,#k do + local ki = k[i] + if ki > 0 then + t[ki], done = o, true + end + end end end end @@ -1849,15 +1963,15 @@ end function otf.copy_to_tfm(data) -- we can save a copy when we reorder the tma to unicode if data then - local tfm = { characters = { }, parameters = { } } - local unicodes = data.luatex.unicodes - local characters = tfm.characters - local parameters = tfm.parameters + local characters, parameters, descriptions = { }, { }, { } + local tfm = { characters = characters, parameters = parameters, descriptions = descriptions } + local luatex = data.luatex + local indices = luatex.indices -- unicodes to indices local glyphs = data.glyphs - for k, d in pairs(glyphs) do - if d.name then - characters[d.unicode] = { description = d } - end + for u, i in pairs(indices) do + local d = glyphs[i] + characters[u] = { } -- not needed + descriptions[u] = d end local designsize = data.designsize or data.design_size or 100 if designsize == 0 then @@ -1884,23 +1998,23 @@ function otf.copy_to_tfm(data) -- we can save a copy when we reorder the tma to if data.pfminfo then data.charwidth = data.pfminfo.avgwidth end - local endash, emdash = unicodes['space'], unicodes['emdash'] + local endash, emdash = 0x20, 0x2014 -- unicodes['space'], unicodes['emdash'] if data.isfixedpitch then - if characters[endash] then - spaceunits, tfm.spacer = characters[endash].description.width, "space" + if descriptions[endash] then + spaceunits, tfm.spacer = descriptions[endash].width, "space" end - if not spaceunits and characters[emdash] then - spaceunits, tfm.spacer = characters[emdash].description.width, "emdash" + if not spaceunits and descriptions[emdash] then + spaceunits, tfm.spacer = descriptions[emdash].width, "emdash" end if not spaceunits and data.charwidth then spaceunits, tfm.spacer = data.charwidth, "charwidth" end else - if characters[endash] then - spaceunits, tfm.spacer = characters[endash].description.width, "space" + if descriptions[endash] then + spaceunits, tfm.spacer = descriptions[endash].width, "space" end - if not spaceunits and characters[emdash] then - spaceunits, tfm.spacer = characters[emdash].description.width/2, "emdash/2" + if not spaceunits and descriptions[emdash] then + spaceunits, tfm.spacer = descriptions[emdash].width/2, "emdash/2" end if not spaceunits and data.charwidth then spaceunits, tfm.spacer = data.charwidth, "charwidth" @@ -1933,9 +2047,12 @@ function otf.copy_to_tfm(data) -- we can save a copy when we reorder the tma to if data.pfminfo and data.pfminfo.os2_xheight and data.pfminfo.os2_xheight > 0 then parameters.x_height = data.pfminfo.os2_xheight else - local x = characters[unicodes['x']] + local x = 0x78 -- unicodes['x'] if x then - parameters.x_height = x.description.height + local x = descriptions[x] + if x then + parameters.x_height = x.height + end end end -- [6] @@ -2057,28 +2174,30 @@ otf.default_language = 'latn' otf.default_script = 'dflt' function otf.valid_feature(otfdata,kind,script,language) -- return hash is faster - if otfdata.luatex.ctx_always[kind] then + local luatex = otfdata.luatex + if luatex.ctx_always[kind] then script, language = 'dflt', 'dflt' else script = script or otf.default_script language = language or otf.default_language end script, language = script:lower(), language:lower() -- will go away, we will lowercase values - local ft = otfdata.luatex.subtables[kind] + local ft = luatex.subtables[kind] local st = ft[script] or ft.dflt local lt = st and (st[language] or st.dflt) - return false, otfdata.luatex.always_valid, lt.valid + return false, luatex.always_valid, lt.valid end function otf.some_valid_feature(otfdata,kind,script,language) - if otfdata.luatex.ctx_always[kind] then + local luatex = otfdata.luatex + if luatex.ctx_always[kind] then script, language = 'dflt', 'dflt' else script = script or otf.default_script language = language or otf.default_language script, language = script:lower(), language:lower() -- will go away, we will lowercase values end - local t = otfdata.luatex.subtables[kind] + local t = luatex.subtables[kind] if t then local ts = t[script] or t.dflt if ts then @@ -2092,7 +2211,8 @@ end function otf.features.aux.resolve_ligatures(tfmdata,ligatures,kind) local otfdata = tfmdata.shared.otfdata local unicodes = otfdata.luatex.unicodes - local chars = tfmdata.characters + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions local changed = tfmdata.changed or { } local done = { } kind = kind or "unknown" @@ -2104,30 +2224,51 @@ function otf.features.aux.resolve_ligatures(tfmdata,ligatures,kind) if not done[lig] then local ligs = split_at_space:match(lig) if #ligs == 2 then - local c, f, s = chars[v[2]], ligs[1], ligs[2] - local uf, us = unicodes[f], unicodes[s] + local uc = v[2] + local c, f, s = characters[uc], ligs[1], ligs[2] +--~ local uf, us = unicodes[f], unicodes[s] + +local uft, ust = unicodes[f], unicodes[s] +if not uft or not ust then + logs.report("define otf","%s: unicode problem with ligature (%s->%s=%s->%s+%s->%s)",kind,descriptions[uc].name or "?",uc,f,uft or "?",s,ust or "?") + -- some kind of error +else + if type(uft) == "number" then uft = { uft } end + if type(ust) == "number" then ust = { ust } end + for ufi=1,#uft do + local uf = uft[ufi] + for usi=1,#ust do + local us = ust[usi] + if changed[uf] or changed[us] then if trace then logs.report("define otf","%s: %s (%s) + %s (%s) ignored",kind,f,uf,s,us) end else - local first, second = chars[uf], us + local first, second = characters[uf], us if first and second then local t = first.ligatures if not t then t = { } first.ligatures = t end - t[second] = { - char = unicodes[c.description.name], - type = 0 - } + local uuc = unicodes[descriptions[uc].name] + if type(uuc) == "number" then + t[second] = { type = 0, char = uuc } + else + t[second] = { type = 0, char = uuc[1] } + end if trace then - logs.report("define otf","%s: %s (%s) + %s (%s) = %s (%s)",kind,f,uf,s,us,c.description.name,unicodes[c.description.name]) + logs.report("define otf","%s: %s (%s) + %s (%s) = %s (%s)",kind,f,uf,s,us,descriptions[uc].name,unicodes[descriptions[uc].name]) end end end - ok, done[lig] = true, c.description.name + + end + end +end + + ok, done[lig] = true, descriptions[uc].name end end end @@ -2152,14 +2293,15 @@ function otf.features.prepare_base_substitutions(tfmdata,kind,value) -- we can s local otfdata = tfmdata.shared.otfdata local unicodes = otfdata.luatex.unicodes local trace = otf.trace_features - local chars = tfmdata.characters + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions local somevalid = otf.some_valid_feature(otfdata,kind,tfmdata.script,tfmdata.language) if not table.is_empty(somevalid) then tfmdata.changed = tfmdata.changed or { } local changed = tfmdata.changed local glyphs = otfdata.glyphs - for k,c in pairs(chars) do - local o = glyphs[c.description.index] + for k,c in pairs(characters) do + local o = glyphs[descriptions[k].index] if o and o.lookups then for lookup,ps in pairs(o.lookups) do if somevalid[lookup] then @@ -2170,12 +2312,29 @@ function otf.features.prepare_base_substitutions(tfmdata,kind,value) -- we can s local pv = p[2] -- p.variant if pv then local upv = unicodes[pv] - if upv and chars[upv] then - if trace then - logs.report("define otf","%s: %s (%s) => %s (%s)",kind,chars[k].description.name,k,chars[upv].description.name,upv) + if upv then + if type(upv) == "number" then + if characters[upv] then + if trace then + logs.report("define otf","%s: %s (%s) => %s (%s)",kind,descriptions[k].name,k,descriptions[upv].name,upv) + end + characters[k] = characters[upv] + descriptions[k] = descriptions[upv] + changed[k] = true + end + else + for i=1,#upv do + local upv = upv[i] + if characters[upv] then + if trace then + logs.report("define otf","%s: %s (%s) => %s (%s)",kind,descriptions[k].name,k,descriptions[upv].name,upv) + end + characters[k] = characters[upv] + descriptions[k] = descriptions[upv] + changed[k] = true + end + end end - chars[k] = chars[upv] - changed[k] = true end end elseif t == 'alternate' then @@ -2184,12 +2343,29 @@ function otf.features.prepare_base_substitutions(tfmdata,kind,value) -- we can s pc = pa.components:match("([^ ]+)") -- todo: selector if pc then local upc = unicodes[pc] - if upc and chars[upc] then - if trace then - logs.report("define otf","%s: %s (%s) => %s (%s)",kind,chars[k].description.name,k,chars[upc].description.name,upc) + if upc then + if type(upc) == "number" then + if chars[upc] then + if trace then + logs.report("define otf","%s: %s (%s) => %s (%s)",kind,descriptions[k].name,k,descriptions[upc].name,upc) + end + characters[k] = characters[upc] + descriptions[k] = descriptions[upc] + changed[k] = true + end + else + for i=1,#upc do + local upc = upc[i] + if chars[upc] then + if trace then + logs.report("define otf","%s: %s (%s) => %s (%s)",kind,descriptions[k].name,k,descriptions[upc].name,upc) + end + characters[k] = characters[upc] + descriptions[k] = descriptions[upc] + changed[k] = true + end + end end - chars[k] = chars[upc] - changed[k] = true end end end @@ -2197,7 +2373,7 @@ function otf.features.prepare_base_substitutions(tfmdata,kind,value) -- we can s local pc = p[2] if pc then if trace then - logs.report("define otf","%s: %s => %s (%s)",kind,pc,chars[k].description.name,k) + logs.report("define otf","%s: %s => %s (%s)",kind,pc,descriptions[k].name,k) end ligatures[#ligatures+1] = { pc, k } end @@ -2258,8 +2434,9 @@ do shared.featurecache[kind] = shared.featurecache[kind] or false -- signal shared.lookuptable [fullkind] = lookuptable shared.processes [fullkind] = processes - local types = otfdata.luatex.name_to_type - local flags = otfdata.luatex.ignore_flags + local luatex = otfdata.luatex + local types = luatex.name_to_type + local flags = luatex.ignore_flags local preparers = otf.features.prepare local process = otf.features.process for i=1,#lookuptable do @@ -2284,56 +2461,91 @@ do -- helper: todo, we don't need to store non local ones for chains so we can pass the -- validator as parameter - local pairs = pairs - function otf.features.collect_ligatures(tfmdata,kind) -- ligs are spread all over the place local otfdata = tfmdata.shared.otfdata - local unicodes = tfmdata.shared.otfdata.luatex.unicodes -- actually the char index is ok too + local glyphs = otfdata.glyphs + local luatex = otfdata.luatex + local unicodes = luatex.unicodes -- names to unicode + local indices = luatex.indices -- unicode to index local trace = otf.trace_features local ligatures = { } - local function collect(lookup,o,ps) + local function collect(lookup,unicode,glyph,ps) for i=1,#ps do local p = ps[i] if p[1] == 'ligature' then if trace then - logs.report("define otf","feature %s lookup %s ligature %s => %s",kind,lookup,p[2],o.name) + logs.report("define otf","feature %s lookup %s ligature %s => %s",kind,lookup,p[2],glyph.name) end local t = ligatures[lookup] if not t then t = { } ligatures[lookup] = t end + -- this table is kind of special: + -- unicode -> tree of names/indices -> unicode + -- this way we can handle multiple unicode to one glyph cases local first = true for s in p[2]:gmatch("[^ ]+") do - local u = unicodes[s] if first then - if not t[u] then - t[u] = { { } } + local u = unicodes[s] + if not u then + logs.report("define otf","feature %s lookup %s ligature %s => %s ignored due to invalid unicode",kind,lookup,p[2],glyph.name) + elseif type(u) == "number" then + if not t[u] then + t[u] = { { } } + end + t = t[u] + else + local tt = t + local tu + for i=1,#u do + local u = u[i] + if i==1 then + if not t[u] then + t[u] = { { } } + end + tu = t[u] + t = tu + else + if not t[u] then + tt[u] = tu + end + end + end end - t = t[u] first = false else + -- beware, we mix unicodes and indices, we can comment these + -- lines when testing, see (*lig*) + s = unicodes[s] + if type(s) == "number" then + s = indices[s] + else + s = indices[s[1]] + end + -- maybe we will introduce a names table some day local t1 = t[1] - if not t1[u] then - t1[u] = { { } } + if not t1[s] then + t1[s] = { { } } end - t = t1[u] + t = t1[s] end end - t[2] = o.unicode + t[2] = unicode end end end local forced, always, okay = otf.valid_feature(otfdata,kind,tfmdata.script,tfmdata.language) - for _,o in pairs(otfdata.glyphs) do - local lookups = o.lookups + for unicode, index in pairs(indices) do + local glyph = glyphs[index] + local lookups = glyph.lookups if lookups then if forced then - for lookup, ps in pairs(lookups) do collect(lookup,o,ps) end + for lookup, ps in pairs(lookups) do collect(lookup,unicode,glyph,ps) end elseif okay then - for lookup, ps in pairs(lookups) do if always[lookup] or okay[lookup] then collect(lookup,o,ps) end end + for lookup, ps in pairs(lookups) do if always[lookup] or okay[lookup] then collect(lookup,unicode,glyph,ps) end end else - for lookup, ps in pairs(lookups) do if always[lookup] then collect(lookup,o,ps) end end + for lookup, ps in pairs(lookups) do if always[lookup] then collect(lookup,unicode,glyph,ps) end end end end end @@ -2359,18 +2571,25 @@ do substitutions = { } featuredata[lookupname] = substitutions local otfdata = tfmdata.shared.otfdata - local unicodes = otfdata.luatex.unicodes + local glyphs = otfdata.glyphs + local luatex = otfdata.luatex + local unicodes = luatex.unicodes -- names to unicode + local indices = luatex.indices -- unicode to index local trace = otf.trace_features - for _, o in pairs(otfdata.glyphs) do - local lookups = o.lookups + for unicode, index in pairs(indices) do + local glyph = glyphs[index] + local lookups = glyph.lookups if lookups then for lookup,ps in pairs(lookups) do if lookup == lookupname then for i=1,#ps do local p = ps[i] if p[1] == 'substitution' then - local old, new = o.unicode, unicodes[p[2]] - substitutions[old] = new + local old, new = unicode, unicodes[p[2]] + if type(new) == "table" then + new = new[1] + end + substitutions[old] = new if trace then logs.report("define otf","%s:%s substitution %s => %s",kind,lookupname,old,new) end @@ -2391,23 +2610,32 @@ do substitutions = { } featuredata[lookupname] = substitutions local otfdata = tfmdata.shared.otfdata - local unicodes = otfdata.luatex.unicodes + local glyphs = otfdata.glyphs + local luatex = otfdata.luatex + local unicodes = luatex.unicodes -- names to unicode + local indices = luatex.indices -- unicode to index local trace = otf.trace_features - for _,o in pairs(otfdata.glyphs) do - local lookups = o.lookups + for unicode, index in pairs(indices) do + local glyph = glyphs[index] + local lookups = glyph.lookups if lookups then for lookup,ps in pairs(lookups) do if lookup == lookupname then for i=1,#ps do local p = ps[i] if p[1] == 'multiple' then - local old, new = o.unicode, { } + local old, new = unicode, { } substitutions[old] = new for pc in p[2]:gmatch("[^ ]+") do - new[#new+1] = unicodes[pc] + local upc = unicodes[pc] + if type(upc) == "number" then + new[#new+1] = upc + else + new[#new+1] = upc[1] + end end if trace then - logs.report("define otf","%s:%s multiple %s => %s",kind,lookupname,old,table.concat(new," ")) + logs.report("define otf","%s:%s multiple %s => %s",kind,lookupname,old,concat(new," ")) end end end @@ -2427,24 +2655,33 @@ do featuredata[lookupname] = { } substitutions = featuredata[lookupname] local otfdata = tfmdata.shared.otfdata - local unicodes = otfdata.luatex.unicodes + local glyphs = otfdata.glyphs + local luatex = otfdata.luatex + local unicodes = luatex.unicodes -- names to unicode + local indices = luatex.indices -- unicode to index local trace = otf.trace_features - for _,o in pairs(otfdata.glyphs) do - local lookups = o.lookups + for unicode, index in pairs(indices) do + local glyph = glyphs[index] + local lookups = glyph.lookups if lookups then for lookup,ps in pairs(lookups) do if lookup == lookupname then for i=1,#ps do local p = ps[i] if p[1] == 'alternate' then - local old = o.unicode + local old = unicode local t = { } for pc in p[2]:gmatch("[^ ]+") do - t[#t+1] = unicodes[pc] + local upc = unicodes[pc] + if type(upc) == "number" then + t[#t+1] = upc + else + t[#t+1] = upc[1] + end end substitutions[old] = t if trace then - logs.report("define otf","%s:%s alternate %s => %s",kind,lookupname,old,table.concat(substitutions,"|")) + logs.report("define otf","%s:%s alternate %s => %s",kind,lookupname,old,concat(substitutions,"|")) end end end @@ -2472,16 +2709,20 @@ do local featuredata = tfmdata.shared.featuredata[kind] local contexts = featuredata[lookupname] if not contexts then - featuredata[lookupname] = { } - contexts = featuredata[lookupname] - local otfdata = tfmdata.shared.otfdata - local unicodes = otfdata.luatex.unicodes - local internals = otfdata.luatex.internals - local flags = otfdata.luatex.ignore_flags - local types = otfdata.luatex.name_to_type - otfdata.luatex.covers = otfdata.luatex.covers or { } + contexts = { } + featuredata[lookupname] = contexts local characters = tfmdata.characters - local cache = otfdata.luatex.covers + local otfdata = tfmdata.shared.otfdata + local luatex = otfdata.luatex + local unicodes = luatex.unicodes + local internals = luatex.internals + local flags = luatex.ignore_flags + local types = luatex.name_to_type + local cache = luatex.covers + if not cache then + cache = { } + luatex.covers = cache + end local function uncover(covers,result) -- lpeg hardly faster (.005 sec on mk) for n=1,#covers do @@ -2490,7 +2731,14 @@ do if not cc then local t = { } for s in c:gmatch("[^ ]+") do - t[unicodes[s]] = true + local us = unicodes[s] + if type(us) == "number" then + t[us] = true + else + for i=1,#us do + t[us[i]] = true + end + end end cache[c] = t result[#result+1] = t @@ -2570,12 +2818,14 @@ do anchors = { } featuredata[lookupname] = anchors local otfdata = tfmdata.shared.otfdata - local unicodes = otfdata.luatex.unicodes - local validanchors = { } local glyphs = otfdata.glyphs + local luatex = otfdata.luatex + local unicodes = luatex.unicodes + local indices = luatex.indices + local validanchors = { } local trace = otf.trace_features - if otfdata.anchor_classes then - local classes = otfdata.anchor_classes + local classes = otfdata.anchor_classes + if classes then for k=1,#classes do local class = classes[k] if class.lookup == lookupname then @@ -2586,8 +2836,9 @@ do end end end - for _,o in pairs(glyphs) do - local oanchor = o.anchors + for unicode, index in pairs(indices) do + local glyph = glyphs[index] + local oanchor = glyph.anchors if oanchor then local t, ok = { }, false for type, anchors in pairs(oanchor) do -- types @@ -2605,7 +2856,7 @@ do end end if ok then - anchors[o.unicode] = t + anchors[unicode] = t end end end @@ -2636,67 +2887,95 @@ do featuredata[lookupname] = { } kerns = featuredata[lookupname] local otfdata = tfmdata.shared.otfdata - local unicodes = otfdata.luatex.unicodes local glyphs = otfdata.glyphs + local luatex = otfdata.luatex + local unicodes = luatex.unicodes + local indices = luatex.indices -- ff has isolated kerns in a separate table - for k,o in pairs(glyphs) do - local list = o.mykerns + for unicode, index in pairs(indices) do + local glyph = glyphs[index] + local list = glyph.mykerns if list then local omk = list[lookupname] if omk then - local one = o.unicode - for char, off in pairs(omk) do - local two = char - local krn = kerns[one] - if krn then - krn[two] = off - else - kerns[one] = { two = off } + local krn = kerns[unicode] + for other, off in pairs(omk) do + if not krn then + krn = { } + kerns[unicode] = krn end + krn[other] = off if trace then - logs.report("define otf","feature %s kern pair %s - %s",kind,one,two) + logs.report("define otf","feature %s kern pair %s - %s",kind,unicode,other) end end end - elseif o.kerns then - local one = o.unicode - local okerns = o.kerns - for ok=1,#okerns do - local k = okerns[ok] - if k.lookup == lookupname then - local char = k.char - if char then - local two = unicodes[char] - local krn = kerns[one] - if krn then - krn[two] = k.off - else - kerns[one] = { two = k.off } - end - if trace then - logs.report("define otf","feature %s kern pair %s - %s",kind,one,two) + else + list = glyph.kerns + if list then + local krn + for ok=1,#list do + local k = list[ok] + if k.lookup == lookupname then + local char = k.char + if char then + if not krn then + krn = kerns[unicode] + if not krn then + krn = { } + kerns[unicode] = krn + end + end + local second = unicodes[char] + local off = k.off + if type(second) == "number" then + krn[second] = off + if trace then + logs.report("define otf","feature %s kern pair %s - %s",kind,unicode,second) + end + else + for i=1,#second do + local second = second[i] + krn[second] = off + if trace then + logs.report("define otf","feature %s kern pair %s - %s",kind,unicode,second) + end + end + end end end end end end - list = o.lookups + list = glyphs.lookups if list then - local one = o.unicode for lookup,ps in pairs(list) do if lookup == lookupname then + local krn for i=1,#ps do local p = ps[i] if p[1] == 'pair' then - local two = unicodes[p[2]] - local krn = kerns[one] - if krn then - krn[two] = p - else - kerns[one] = { two = p } + if not krn then + krn = kerns[unicode] + if not krn then + krn = { } + kerns[unicode] = krn + end end - if trace then - logs.report("define otf","feature %s kern pair %s - %s",kind,one,two) + local second = unicodes[p[2]] + if type(second) == "number" then + krn[second] = p + if trace then + logs.report("define otf","feature %s kern pair %s - %s",kind,unicode,second) + end + else + for i=1,#second do + local second = second[i] + krn[second] = p + if trace then + logs.report("define otf","feature %s kern pair %s - %s",kind,unicode,second) + end + end end end end @@ -2823,13 +3102,15 @@ do -- we share some vars here, after all, we have no nested lookups and -- less code - local tfmdata = false - local otfdata = false - local characters = false - local marks = false - local glyphs = false - local currentfont = false - local rlmode = 0 + local tfmdata = false + local otfdata = false + local characters = false + local descriptions = false + local marks = false + local indices = false + local glyphs = false + local currentfont = false + local rlmode = 0 -- we cheat a bit and assume that a font,attr combination are kind of ranged @@ -2847,8 +3128,11 @@ do local shared = tfmdata.shared otfdata = shared.otfdata characters = tfmdata.characters - marks = otfdata.luatex.marks + descriptions = tfmdata.descriptions glyphs = otfdata.glyphs + local luatex = otfdata.luatex + marks = luatex.marks + indices = luatex.indices currentfont = font rlmode = 0 local script, language, strategy @@ -3086,59 +3370,37 @@ do local prev = start.prev start = start.next end - else + else -- start is the ligature + -- to be checked: this marknum mess (sensitive for looping) local deletemarks = markflag ~= "mark" +--~ deletemarks = false start.components = copy_list(start,stop) - slide(start.components) - -- todo: components - start.subtype = 2 - start.char = char - local marknum = 1 - local next = start.next - while true do - if marks[next.char] then - if not deletemarks then - set_attribute(next,marknumber,marknum) - end - else - marknum = marknum + 1 - end - if next == stop then - break - else - next = next.next - end - end - next = stop.next - while next do - if next.id == glyph and next.font == currentfont and marks[next.char] then + local last = slide(start.components) + start.components.prev, last.next = nil, nil + start.char, start.subtype = char, 2 + local next, done, marknum = start.next, false, 1 + local after = stop.next + while not done do + done = next == stop + if not deletemarks and marks[next.char] then set_attribute(next,marknumber,marknum) next = next.next + --~ marknum = marknum + 1 else - break + marknum = marknum + 1 + start, next = nodes.remove(start,next,true) end end - local next = start.next - while next do - if next == stop or deletemarks or marks[next.char] then - local crap = next - local np, nn = next.prev, next.next - np.next = nn - if nn then - nn.prev = np - end - if next == stop then - stop = crap.prev - free(crap) - break - else - next = nn - free(crap) - end + while after and after.id == glyph and after.font == currentfont and marks[after.char] do + if deletemarks then + start, after = nodes.remove(start,after,true) else - next = nn + set_attribute(after,marknumber,marknum) + after = after.next + --~ marknum = marknum + 1 end end + end end return start @@ -3200,10 +3462,15 @@ do local id = s.id if id == glyph and s.subtype<256 then if s.font == currentfont then - if marks[s.char] then + local char = s.char + if marks[char] then s = s.next else - local lg = ligatures[1][s.char] + -- we use indices, which saves a lookup, but we can use + -- names when we comment the line after (*lig*) + -- local lg = ligatures[1][glyphs[indices[char]].name] + local lg = ligatures[1][indices[char]] + -- mayb esome day we introduce a more efficient method if not lg then break else @@ -3313,13 +3580,12 @@ do return start, false end + -- hm which one is the correct one? chainprocs.gpos_mark2mark ot the next; the next one + -- had more tracing so might be the best + function otf.features.process.gpos_mark2mark(start,kind,lookupname,b_anchors,m_anchors) local basemarkchar = start.char ---~ print(lookupname) if marks[basemarkchar] then ---~ print('') ---~ print('basemarkchar',basemarkchar) ---~ print('basemarkanchors', table.serialize(b_anchors)) local baseanchors = b_anchors['basemark'] if baseanchors then local component = start.next @@ -3328,16 +3594,11 @@ do if not marks[markchar] then break else ---~ print('markchar',markchar) - local basemarkattr = has_attribute(start, marknumber) or 1 + local basemarkattr = has_attribute(start,marknumber) or 1 local markattr = has_attribute(component,marknumber) or 1 ---~ print(basemarkattr,markattr) if basemarkattr == markattr then -- still needed? ---~ print('markanchors *', table.serialize(m_anchors)) - local markanchors = m_anchors[markchar] if markanchors then ---~ print('markanchors') local markanchor = markanchors['mark'] if markanchor then for anchor,ma in pairs(markanchor) do @@ -3355,8 +3616,9 @@ do end end end - component = component.next + -- weird, was here end + component = component.next end end end @@ -3384,7 +3646,7 @@ do local exit = cexit[anchor] if exit then local factor = tfmdata.factor - local dx = -(tfmdata.characters[prevchar].description.width-exit[1]) - entry[1] + local dx = -(descriptions[prevchar].width-exit[1]) - entry[1] local dy = -(entry[2]-exit[2]) start.yoffset = prev.yoffset + scale(dy, factor) -- start.xoffset = scale(tx[i], factor) @@ -3433,7 +3695,7 @@ do local exit = cexit[anchor] if exit then local dy = -exit[2] + entry[2] - local dx = -(tfmdata.characters[nextchar].description.width-entry[1]) - exit[1] -- often width == entry 1 + local dx = -(descriptions[nextchar].width-entry[1]) - exit[1] -- often width == entry 1 tx[#tx+1], ty[#ty+1] = dx, dy total_x, total_y = total_x + dx, total_y + dy stack[#stack+1] = start @@ -3465,51 +3727,55 @@ do end function otf.features.process.gpos_pair(start,kind,lookupname,basekerns,kerns) - local next, prev, done = start.next, start, false - -- to be optimized, we can consider using basemode for fonts without lookups -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too -- todo: kerns in components of ligatures - local trace = otf.trace_kerns - local factor = tfmdata.factor - while next and next.id == glyph and next.subtype<256 and next.font == currentfont do - local cn = characters[next.char] - if not cn or cn.description.class == 'mark' then - prev = next - next = next.next - else - local krn = basekerns[next.char] - if not krn then - -- skip - elseif type(krn) == "table" then - local a, b = krn[3], krn[7] - if a and a ~= 0 then - local k = nodes.kern(scale(a,factor)) + local next = start.next + if not next then + return start, false + else + local prev, done = start, false + local trace = otf.trace_kerns + local factor = tfmdata.factor + while next and next.id == glyph and next.subtype<256 and next.font == currentfont do + local cn = descriptions[next.char] + if not cn or cn.class == 'mark' then + prev = next + next = next.next + else + local krn = basekerns[next.char] + if not krn then + -- skip + elseif type(krn) == "table" then + local a, b = krn[3], krn[7] + if a and a ~= 0 then + local k = nodes.kern(scale(a,factor)) + k.next = next + k.prev = prev + prev.next = k + next.prev = k + if trace then + -- todo + end + end + if b and b ~= 0 then + report("otf process","we need to do something with the second kern xoff %s",b) + end + else + -- todo, just start, next = node.insert_before(head,next,nodes.kern(scale(kern,factor))) + if otf.trace_kerns then + report("otf process","%s: inserting kern %s between 0x%04X and 0x%04X",kind,krn,prev.char,next.char) + end + local k = nodes.kern(scale(krn,factor)) k.next = next k.prev = prev prev.next = k next.prev = k - if trace then - -- todo - end - end - if b and b ~= 0 then - report("otf process","we need to do something with the second kern xoff %s",b) - end - else - -- todo, just start, next = node.insert_before(head,next,nodes.kern(scale(kern,factor))) - if otf.trace_kerns then - report("otf process","%s: inserting kern %s between 0x%04X and 0x%04X",kind,krn,prev.char,next.char) end - local k = nodes.kern(scale(krn,factor)) - k.next = next - k.prev = prev - prev.next = k - next.prev = k + break end - break end + return start, done end - return start, done end -- -- -- temp here, needs to be tested first -- -- -- @@ -3627,14 +3893,23 @@ do local replacement = cacheslot[char] if replacement == true then if lookups then - local looks = glyphs[tfmdata.characters[char].description.index].lookups -- SLOW, USE OTFDATA + -- didn't we have the arrays available? + local looks = glyphs[descriptions[char].index].lookups -- SLOW, USE OTFDATA if looks then - local glyphlookups = otfdata.luatex.internals[lookups[lookup]].lookups - local unicodes = otfdata.luatex.unicodes + local luatex = otfdata.luatex + local glyphlookups = luatex.internals[lookups[lookup]].lookups + local unicodes = luatex.unicodes for gl=1,#glyphlookups do local lv = looks[glyphlookups[gl]] if lv then - replacement = unicodes[lv[1][2]] or char + local ulv = unicodes[lv[1][2]] + if not ulv then + replacement = char + elseif type(ulv) == "number" then + replacement = ulv + else + replacement = ulv[1] + end cacheslot[char] = replacement break end @@ -3673,16 +3948,22 @@ do local replacement = cacheslot[char] if replacement == true then if lookups then - local looks = glyphs[tfmdata.characters[char].description.index].lookups + local looks = glyphs[descriptions[char].index].lookups if looks then - local lookups = otfdata.luatex.internals[lookups[1]].lookups - local unicodes = otfdata.luatex.unicodes + local luatex = otfdata.luatex + local lookups = luatex.internals[lookups[1]].lookups + local unicodes = luatex.unicodes for l=1,#lookups do local lv = looks[lookups[l]] if lv then replacement = { } for c in lv[1][2]:gmatch("[^ ]+") do - replacement[#replacement+1] = unicodes[c] + local uc = unicodes[c] + if type(uc) == "number" then + replacement[#replacement+1] = uc + else + replacement[#replacement+1] = uc[1] + end end cacheslot[char] = replacement break @@ -3724,16 +4005,22 @@ do local replacement = cacheslot[char] if replacement == true then if lookups then - local looks = glyphs[tfmdata.characters[char].description.index].lookups + local looks = glyphs[descriptions[char].index].lookups if looks then - local lookups = otfdata.luatex.internals[lookups[1]].lookups - local unicodes = otfdata.luatex.unicodes + local luatex = otfdata.luatex + local lookups = luatex.internals[lookups[1]].lookups + local unicodes = luatex.unicodes for l=1,#lookups do local lv = looks[lookups[l]] if lv then replacement = { } for c in lv[1][2]:gmatch("[^ ]+") do - replacement[#replacement+1] = unicodes[c] + local uc = unicodes[c] + if type(uc) == "number" then + replacement[#replacement+1] = uc + else + replacement[#replacement+1] = uc[1] + end end cacheslot[char] = replacement break @@ -3757,12 +4044,16 @@ do function chainprocs.gsub_ligature(start,stop,kind,lookupname,sequence,f,l,lookups,flags) if lookups then + if start == stop then + -- print("todo: optimize") + end local featurecache = fontdata[currentfont].shared.featurecache - if not featurecache[kind] then - featurecache[kind] = otf.features.collect_ligatures(tfmdata,kind) -- double cached ? + local ligaturecache = featurecache[kind] + if not ligaturecache then + ligaturecache = otf.features.collect_ligatures(tfmdata,kind) -- double cached ? + featurecache[kind] = ligaturecache end local lookups = otfdata.luatex.internals[lookups[1]].lookups - local ligaturecache = featurecache[kind] local trace = otf.trace_ligatures for i=1,#lookups do local ligatures = ligaturecache[lookups[i]] @@ -3774,7 +4065,7 @@ do if id == disc then s = s.next discfound = true - elseif characters[s.char].description.class == 'mark' then -- marks + elseif descriptions[s.char].class == 'mark' then -- marks s = s.next else local lg = ligatures[1][s.char] @@ -3838,12 +4129,12 @@ do if marks[basechar] then component = component.prev else - local bglyph = glyphs[characters[basechar].description.index] -- startchar + local bglyph = glyphs[descriptions[basechar].index] -- startchar local baseanchors = bglyph.anchors['basechar'] if baseanchors then local ba = baseanchors[anchortag] if ba then - local mglyph = glyphs[characters[markchar].description.index] + local mglyph = glyphs[descriptions[markchar].index] local markanchors = mglyph.anchors['mark'] if markanchors then local ma = markanchors[anchortag] @@ -3897,7 +4188,7 @@ do if marks[basechar] then component = component.prev else - local bglyph = glyphs[characters[basechar].description.index] -- startchar + local bglyph = glyphs[descriptions[basechar].index] -- startchar local baseanchors = bglyph.anchors['baselig'] if baseanchors then local ba = baseanchors[anchortag] @@ -3905,7 +4196,7 @@ do local n = has_attribute(start,marknumber) ba = ba[n] -- ok ? if ba then - local mglyph = glyphs[characters[markchar].description.index] + local mglyph = glyphs[descriptions[markchar].index] local markanchors = mglyph.anchors['mark'] if markanchors then local ma = markanchors[anchortag] @@ -3931,7 +4222,7 @@ do return start, false end - -- to be checked + -- to be checked (see previous generic mark2mark) function chainprocs.gpos_mark2mark(start,stop,kind,lookupname,sequence,f,l,lookups) local component = start.next @@ -3959,15 +4250,14 @@ do local markattr = has_attribute(start, marknumber) or 1 -- i need to check this ! 1 is new ! local baseattr = has_attribute(component,marknumber) or 1 -- i need to check this ! 1 is new ! if baseattr == markattr then - local glyph = glyphs[characters[markchar].description.index] + local glyph = glyphs[descriptions[markchar].index] if glyph.anchors and glyph.anchors[anchortag] then local trace = otf.trace_anchors local done = false local baseanchors = glyph.anchors['basemark'][anchortag] - while true do + while component do local basechar = component.char - local charnext = characters[basechar] - local markanchors = glyphs[charnext.description.index].anchors['mark'][anchortag] + local markanchors = glyphs[descriptions[basechar].index].anchors['mark'][anchortag] if markanchors then for anchor,data in pairs(markanchors) do local ba = baseanchors[anchor] @@ -4025,6 +4315,8 @@ do -- what pointer to return, spec says stop + -- to be discussed ... is bidi changer a space? + function otf.features.process.contextchain(start,kind,lookupname,contextdata) local contexts, flags, done = contextdata.lookups, contextdata.flags, false local skipmark, skipligature, skipbase = unpack(flags) -- unpack slower than assignment @@ -4042,17 +4334,22 @@ do local id = last.id if id == glyph and last.subtype<256 and last.font == currentfont then local char = last.char - local chardata = characters[char] - if chardata then - local class = chardata.description.class - if class == skipmark or class == skipligature or class == skipbase then - -- skip 'm - last = last.next - elseif sequence[n][char] then - if n < l then + local cc = characters[char] + if cc then + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase then + -- skip 'm last = last.next + elseif sequence[n][char] then + if n < l then + last = last.next + end + n = n + 1 + else + match = false break end - n = n + 1 else match = false break end @@ -4078,11 +4375,21 @@ do local id = prev.id if id == glyph and prev.subtype<256 and prev.font == currentfont then -- normal char local char = prev.char - local class = characters[char].description.class - if class == skipmark or class == skipligature or class == skipbase then - -- skip 'm - elseif sequence[n][char] then - n = n -1 + local cc = characters[char] + if cc then + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase then + -- skip 'm + elseif sequence[n][char] then + n = n -1 + else + match = false break + end + else + match = false break + end else match = false break end @@ -4120,11 +4427,21 @@ do local id = next.id if id == glyph and next.subtype<256 and next.font == currentfont then -- normal char local char = next.char - local class = characters[char].description.class - if class == skipmark or class == skipligature or class == skipbase then - -- skip 'm - elseif sequence[n][char] then - n = n + 1 + local cc = characters[char] + if cc then + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase then + -- skip 'm + elseif sequence[n][char] then + n = n + 1 + else + match = false break + end + else + match = false break + end else match = false break end @@ -4208,7 +4525,7 @@ do local id = next.id if id == glyph and next.subtype<256 and next.font == currentfont then -- normal char local char = next.char - local class = characters[char].description.class + local class = descriptions[char].class if class == skipmark or class == skipligature or class == skipbase then -- skip elseif sequence[n][char] then @@ -4406,16 +4723,16 @@ do local tlig_list = { endash = "hyphen hyphen", emdash = "hyphen hyphen hyphen", ---~ quotedblleft = "quoteleft quoteleft", ---~ quotedblright = "quoteright quoteright", ---~ quotedblleft = "grave grave", ---~ quotedblright = "quotesingle quotesingle", ---~ quotedblbase = "comma comma", + --~ quotedblleft = "quoteleft quoteleft", + --~ quotedblright = "quoteright quoteright", + --~ quotedblleft = "grave grave", + --~ quotedblright = "quotesingle quotesingle", + --~ quotedblbase = "comma comma", } local trep_list = { ---~ [0x0022] = 0x201D, + --~ [0x0022] = 0x201D, [0x0027] = 0x2019, ---~ [0x0060] = 0x2018, + --~ [0x0060] = 0x2018, } local tlig_feature = { @@ -4436,19 +4753,22 @@ do } function otf.enhance.enrich(data,filename) - for index, glyph in pairs(data.glyphs) do + local glyphs = data.glyphs + local indices = data.map.map + for unicode, index in pairs(indices) do + local glyph = glyphs[index] local l = tlig_list[glyph.name] if l then local o = glyph.lookups or { } o["ctx_tlig_1"] = { { "ligature", l, glyph.name } } glyph.lookups = o end - local r = trep_list[glyph.unicode] + local r = trep_list[unicode] if r then - local replacement = data.map.map[r] + local replacement = indices[r] if replacement then local o = glyph.lookups or { } - o["ctx_trep_1"] = { { "substitution", data.glyphs[replacement].name } } --- + o["ctx_trep_1"] = { { "substitution", glyphs[replacement].name } } --- glyph.lookups = o end end @@ -4528,7 +4848,12 @@ function otf.name_to_slot(name) -- todo: afm en tfm if tfmdata and tfmdata.shared then local otfdata = tfmdata.shared.otfdata if otfdata and otfdata.luatex then - return otfdata.luatex.unicodes[name] + local unicode = otfdata.luatex.unicodes[name] + if type(unicode) == "number" then + return unicode + else + return unicode[1] + end end end return nil @@ -4543,28 +4868,7 @@ function otf.char(n) -- todo: afm en tfm end end ---~ function otf.name_to_table(name) ---~ lcoal temp, result = { } ---~ local tfmdata = tfm.id[font.current()] ---~ if tfmdata and tfmdata.shared then ---~ local otfdata = tfmdata.shared.otfdata ---~ if otfdata and otfdata.luatex then ---~ for k,v in pairs(otfdata.glyphs) do ---~ if v.name:find(name) then ---~ temp[v.name] = v.unicode ---~ end ---~ end ---~ end ---~ end ---~ for k,v in pairs(table.sortedkeys(temp)) do ---~ result[#result+1] = { v, temp[v] } ---~ end ---~ return result ---~ end - --- Here we plug in some analyzing code - --- will move to font-tfm +-- Here we plug in some analyzing code (will move to font-tfm). do @@ -4650,7 +4954,7 @@ do local isol_fina = { [0x0622] = true, [0x0623] = true, [0x0624] = true, [0x0625] = true, [0x0627] = true, [0x062F] = true, [0x0630] = true, [0x0631] = true, [0x0632] = true, - [0x0648] = true, + [0x0648] = true, [0x0698] = true, [0xFEF5] = true, [0xFEF7] = true, [0xFEF9] = true, [0xFEFB] = true, } @@ -4659,8 +4963,8 @@ do [0x0633] = true, [0x0634] = true, [0x0635] = true, [0x0636] = true, [0x0637] = true, [0x0638] = true, [0x0639] = true, [0x063A] = true, [0x0640] = true, -- tadwil [0x0641] = true, [0x0642] = true, [0x0643] = true, [0x0644] = true, [0x0645] = true, [0x0646] = true, [0x0647] = true, [0x0649] = true, [0x064A] = true, - [0x067E] = true, - [0x0686] = true, [zwj] = true, + [0x067E] = true, [0x0686] = true, [0x06AF] = true, [0x06A9] = true, [0x06CC] = true, + [zwj] = true, } local arab_warned = { } @@ -4685,7 +4989,9 @@ do otf.remove_joiners = true -- for idris who want it as option function fonts.analyzers.methods.arab(head,font,attr) -- maybe make a special version with no trace - local characters = fontdata[font].characters + local tfmdata = fontdata[font] + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions local first, last, current, done = nil, nil, head, false local trace, removejoiners = fonts.color.trace, otf.remove_joiners --~ local laststate = 0 @@ -4736,11 +5042,11 @@ do -- some day we will make a characters.marks hash -- this is also more efficient since it's shared local char = current.char - local chardata = characters[char] + local descriptions = descriptions[char] if removejoiners and char == zwj or char == zwnj then joiners[#joiners+1] = current end - if chardata and chardata.description.class == "mark" then + if descriptions and descriptions.class == "mark" then set_attribute(current,state,5) -- mark if trace then fcs(current,"font:mark") end elseif isol[char] then -- can be zwj or zwnj too @@ -4920,15 +5226,19 @@ do } local function is_han_character(char) + -- we might add such info to char-def return - (char>=0x04E00 and char<=0x09FFF) or + (char>=0x03040 and char<=0x0309F) or + (char>=0x030A0 and char<=0x030FF) or + (char>=0x031F0 and char<=0x031FF) or (char>=0x03400 and char<=0x04DFF) or - (char>=0x20000 and char<=0x2A6DF) or + (char>=0x04E00 and char<=0x09FFF) or (char>=0x0F900 and char<=0x0FAFF) or + (char>=0x0FF00 and char<=0x0FFEF) or + (char>=0x20000 and char<=0x2A6DF) or (char>=0x2F800 and char<=0x2FA1F) end - - -- mayeb an entry in the character table: hanclass + -- maybe an entry in the character table: hanclass --~ opening_parenthesis_hw / closing_parenthesis_hw --~ opening_parenthesis_fw / closing_parenthesis_fw @@ -4966,7 +5276,9 @@ do function fonts.analyzers.methods.hani(head,font,attr) -- maybe make a special version with no trace - local characters = fontdata[font].characters + local tfmdata = fontdata[font] + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions local current, done, stretch, prevclass = head, false, 0, 0 if fonts.analyzers.methods.stretch_hang then stretch = fontdata[font].parameters.quad @@ -4977,7 +5289,8 @@ do local internormalstretch = stretch * hang_data.inter_char_stretch_factor local trace = fonts.color.trace -- todo: check for first and last --- maybe it's better to look back + -- maybe it's better to look back +-- we need to backtrack a glyph (also other font) while current do if current.id == glyph and current.subtype<256 then if current.font == font then @@ -5035,6 +5348,7 @@ do head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0)) end else +-- here we might have a mixed font prevclass = 0 end elseif prevclass > 0 and current.id == glue and current.spec and current.spec.width > 0 then |