From cf3f5ed936ab0e84942af2e69862db6941c58d0f Mon Sep 17 00:00:00 2001 From: Hans Hagen Date: Thu, 9 Sep 2010 23:45:00 +0200 Subject: beta 2010.09.09 23:45 --- tex/generic/context/luatex-fonts-merged.lua | 1676 ++++++++++++++------------- 1 file changed, 865 insertions(+), 811 deletions(-) (limited to 'tex/generic') diff --git a/tex/generic/context/luatex-fonts-merged.lua b/tex/generic/context/luatex-fonts-merged.lua index 3e64855a8..25c0f2d9d 100644 --- a/tex/generic/context/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 09/05/10 13:23:12 +-- merge date : 09/09/10 23:45:11 do -- begin closure to overcome local limits and interference @@ -830,20 +830,22 @@ function table.imerged(...) return tmp end -local function fastcopy(old) -- fast one +local function fastcopy(old,metatabletoo) -- fast one if old then local new = { } for k,v in next, old do if type(v) == "table" then - new[k] = fastcopy(v) -- was just table.copy + new[k] = fastcopy(v,metatabletoo) -- was just table.copy else new[k] = v end end - -- optional second arg - local mt = getmetatable(old) - if mt then - setmetatable(new,mt) + if metatabletoo then + -- optional second arg + local mt = getmetatable(old) + if mt then + setmetatable(new,mt) + end end return new else @@ -851,6 +853,8 @@ local function fastcopy(old) -- fast one end end +-- todo : copy without metatable + local function copy(t, tables) -- taken from lua wiki, slightly adapted tables = tables or { } local tcopy = {} @@ -1297,6 +1301,7 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) serialize(root,name,flush,reduce,noquotes,hexify) end f:close() + io.flush() end end @@ -1961,6 +1966,7 @@ function io.savedata(filename,data,joiner) f:write(data or "") end f:close() + io.flush() return true else return false @@ -5283,7 +5289,11 @@ fonts.map.addtounicode = function(data,filename) t[#t+1] = u end end - if #t > 0 then -- done then + if #t == 0 then -- done then + -- nothing + elseif #t == 1 then + originals[index], tounicode[index], nl, unicode = t[1], tounicode16(t[1]), nl + 1, true + else originals[index], tounicode[index], nl, unicode = t, tounicode16sequence(t), nl + 1, true end end @@ -5434,6 +5444,11 @@ if not modules then modules = { } end modules ['font-otf'] = { license = "see context related readme files" } +-- langs -> languages enz +-- anchor_classes vs kernclasses +-- modification/creationtime in subfont is runtime dus zinloos +-- to_table -> totable + local utf = unicode.utf8 local concat, utfbyte = table.concat, utf.byte @@ -5442,6 +5457,8 @@ local type, next, tonumber, tostring = type, next, tonumber, tostring local abs = math.abs local getn = table.getn local lpegmatch = lpeg.match +local reverse = table.reverse +local ioflush = io.flush local allocate = utilities.storage.allocate @@ -5455,42 +5472,7 @@ local trace_defining = false trackers.register("fonts.defining", function(v local report_otf = logs.new("load otf") ---~ trackers.enable("otf.loading") - ---[[ldx-- -

The fontforge table has organized lookups in a certain way. A first implementation -of this code was organized featurewise: information related to features was -collected and processing boiled down to a run over the features. The current -implementation honors the order in the main feature table. Since we can reorder this -table as we want, we can eventually support several models of processing. We kept -the static as well as dynamic feature processing, because it had proved to be -rather useful. The formerly three loop variants have beem discarded but will -reapear at some time.

- - -we loop over all lookups -for each lookup we do a run over the list of glyphs -but we only process them for features that are enabled -if we're dealing with a contextual lookup, we loop over all contexts -in that loop we quit at a match and then process the list of sublookups -we always continue after the match - - -

In we do this for each font that is used in a list, so in -practice we have quite some nested loops.

- -

We process the whole list and then consult the glyph nodes. An alternative approach -is to collect strings of characters using the same font including spaces (because some -lookups involve spaces). However, we then need to reconstruct the list which is no fun. -Also, we need to carry quite some information, like attributes, so eventually we don't -gain much (if we gain something at all).

- -

Another consideration has been to operate on sublists (subhead, subtail) but again -this would complicate matters as we then neext to keep track of a changing subhead -and subtail. On the other hand, this might save some runtime. The number of changes -involved is not that large. This only makes sense when we have many fonts in a list -and don't change to frequently.

---ldx]]-- +local starttiming, stoptiming, elapsedtime = statistics.starttiming, statistics.stoptiming, statistics.elapsedtime local fonts = fonts @@ -5500,38 +5482,114 @@ local tfm = fonts.tfm local fontdata = fonts.ids ---~ otf.tables = otf.tables or { } -- defined in font-ott.lua ---~ otf.tables.features = otf.tables.features or { } -- defined in font-ott.lua ---~ otf.tables.languages = otf.tables.languages or { } -- defined in font-ott.lua ---~ otf.tables.scripts = otf.tables.scripts or { } -- defined in font-ott.lua - otf.features = otf.features or { } otf.features.list = otf.features.list or { } otf.features.default = otf.features.default or { } otf.enhancers = allocate() local enhancers = otf.enhancers +enhancers.patches = { } local definers = fonts.definers otf.glists = { "gsub", "gpos" } -otf.version = 2.653 -- beware: also sync font-mis.lua -otf.pack = true -- beware: also sync font-mis.lua -otf.syncspace = true -otf.notdef = false +otf.version = 2.702 -- beware: also sync font-mis.lua otf.cache = containers.define("fonts", "otf", otf.version, true) -local wildcard = "*" -local default = "dflt" +local loadmethod = "table" -- table, mixed, sparse +local forceload = false +local cleanup = 0 +local usemetatables = false -- .4 slower on mk but 30 M less mem so we might change the default -- will be directive +local packdata = true +local syncspace = true +local forcenotdef = false + +local wildcard = "*" +local default = "dflt" + +local fontloaderfields = fontloader.fields +local mainfields = nil +local glyphfields = nil -- not used yet + +directives.register("fonts.otf.loader.method", function(v) + if v == "sparse" and fontloaderfields then + loadmethod = "sparse" + elseif v == "mixed" then + loadmethod = "mixed" + elseif v == "table" then + loadmethod = "table" + else + loadmethod = "table" + report_otf("no loader method '%s', using '%s' instead",v,loadmethod) + end +end) + +directives.register("fonts.otf.loader.cleanup",function(v) + cleanup = tonumber(v) or (v and 1) or 0 +end) + +directives.register("fonts.otf.loader.force", function(v) forceload = v end) +directives.register("fonts.otf.loader.usemetatables", function(v) usemetatables = v end) +directives.register("fonts.otf.loader.pack", function(v) packdata = v end) +directives.register("fonts.otf.loader.syncspace", function(v) syncspace = v end) +directives.register("fonts.otf.loader.forcenotdef", function(v) forcenotdef = v end) + +local function load_featurefile(raw,featurefile) + if featurefile and featurefile ~= "" then + if trace_loading then + report_otf("featurefile: %s", featurefile) + end + fontloader.apply_featurefile(raw, featurefile) + end +end + +local function showfeatureorder(otfdata,filename) + local sequences = otfdata.luatex.sequences + if sequences and #sequences > 0 then + if trace_loading then + report_otf("font %s has %s sequences",filename,#sequences) + report_otf(" ") + end + for nos=1,#sequences do + local sequence = sequences[nos] + local typ = sequence.type or "no-type" + local name = sequence.name or "no-name" + local subtables = sequence.subtables or { "no-subtables" } + local features = sequence.features + if trace_loading then + report_otf("%3i %-15s %-20s [%s]",nos,name,typ,concat(subtables,",")) + end + if features then + for feature, scripts in next, features do + local tt = { } + for script, languages in next, scripts do + local ttt = { } + for language, _ in next, languages do + ttt[#ttt+1] = language + end + tt[#tt+1] = format("[%s: %s]",script,concat(ttt," ")) + end + if trace_loading then + report_otf(" %s: %s",feature,concat(tt," ")) + end + end + end + end + if trace_loading then + report_otf("\n") + end + elseif trace_loading then + report_otf("font %s has no sequences",filename) + end +end --[[ldx--

We start with a lot of tables and related functions.

--ldx]]-- --- we can have more local functions - -otf.tables.global_fields = allocate( table.tohash { +local global_fields = table.tohash { + "metadata", "lookups", "glyphs", "subfonts", @@ -5542,20 +5600,20 @@ otf.tables.global_fields = allocate( table.tohash { "names", "unicodes", "names", ---~ "math", + -- "math", "anchor_classes", "kern_classes", "gpos", "gsub" -} ) +} -otf.tables.valid_fields = allocate( { - "anchor_classes", +local valid_fields = table.tohash { + -- "anchor_classes", "ascent", - "cache_version", + -- "cache_version", "cidinfo", "copyright", - "creationtime", + -- "creationtime", "descent", "design_range_bottom", "design_range_top", @@ -5567,23 +5625,23 @@ otf.tables.valid_fields = allocate( { "fontstyle_id", "fontstyle_name", "fullname", - "glyphs", + -- "glyphs", "hasvmetrics", "head_optimized_for_cleartype", "horiz_base", "issans", "isserif", "italicangle", - "kerns", - "lookups", + -- "kerns", + -- "lookups", -- "luatex", "macstyle", - "modificationtime", + -- "modificationtime", "onlybitmaps", "origname", "os2_version", - "pfminfo", - "private", + -- "pfminfo", + -- "private", "serifcheck", "sfd_version", -- "size", @@ -5600,59 +5658,111 @@ otf.tables.valid_fields = allocate( { "upos", "use_typo_metrics", "uwidth", - "validation_state", + -- "validation_state", "verbose", "version", "vert_base", "weight", "weight_width_slope_only", - "xuid", -} ) + -- "xuid", +} + +local ordered_enhancers = { + "prepare tables", + "prepare glyphs", + "prepare unicodes", + "prepare lookups", + + "analyze glyphs", + "analyze math", + + "prepare tounicode", -- maybe merge with prepare + + "reorganize lookups", + "reorganize mark classes", + "reorganize anchor classes", + + "reorganize glyph kerns", + "reorganize glyph lookups", + "reorganize glyph anchors", + + "reorganize features", + "reorganize subtables", + + "check glyphs", + "check metadata", + "check math parameters", + "check extra features", -- after metadata +} --[[ldx--

Here we go.

--ldx]]-- -local function load_featurefile(ff,featurefile) - if featurefile and featurefile ~= "" then - if trace_loading then - report_otf("featurefile: %s", featurefile) +local actions = { } + +enhancers.patches.before = allocate() +enhancers.patches.after = allocate() + +local before = enhancers.patches.before +local after = enhancers.patches.after + +local function enhance(name,data,filename,raw,verbose) + local enhancer = actions[name] + if enhancer then + if verbose then + report_otf("enhance: %s (%s)",name,filename) + ioflush() end - fontloader.apply_featurefile(ff, featurefile) + enhancer(data,filename,raw) + else + report_otf("enhance: %s is undefined",name) end end -local function enhance(name,data,filename,verbose) - local enhancer = enhancers[name] - if enhancer then - if (verbose ~= nil and verbose) or trace_loading then - report_otf("enhance: %s (%s)",name,filename) +function enhancers.apply(data,filename,raw,verbose) + local basename = file.basename(lower(filename)) + report_otf("start enhancing: %s",filename) + ioflush() -- we want instant messages + for e=1,#ordered_enhancers do + local enhancer = ordered_enhancers[e] + local b = before[enhancer] + if b then + for pattern, action in next, b do + if find(basename,pattern) then + action(data,filename,raw) + end + end end - enhancer(data,filename) + enhance(enhancer,data,filename,raw,verbose) + local a = after[enhancer] + if a then + for pattern, action in next, a do + if find(basename,pattern) then + action(data,filename,raw) + end + end + end + ioflush() -- we want instant messages end + report_otf("stop enhancing") + ioflush() -- we want instant messages end -local ordered_enhancers = { -- implemented later - -- pack and unpack are handled separately; they might even be moved - -- away from the enhancers namespace - "patch bugs", - "merge cid fonts", "prepare unicode", "cleanup ttf tables", "compact glyphs", "reverse coverage", - "cleanup aat", "enrich with features", "add some missing characters", - "reorganize mark classes", - "reorganize kerns", -- moved here - "flatten glyph lookups", "flatten anchor tables", "flatten feature tables", - "simplify glyph lookups", -- some saving - "prepare luatex tables", - "analyze features", "rehash features", - "analyze anchors", "analyze marks", "analyze unicodes", "analyze subtables", - "check italic correction","check math", - "share widths", - "strip not needed data", - "migrate metadata", - "check math parameters", -} +-- enhancers.patches.register("before","migrate metadata","cambria",function() end) + +function enhancers.patches.register(what,where,pattern,action) + local ww = what[where] + if ww then + ww[pattern] = action + else + ww = { [pattern] = action} + end +end -local adddimensions, showfeatureorder -- implemented later +function enhancers.register(what,action) -- only already registered can be overloaded + actions[what] = action +end function otf.load(filename,format,sub,featurefile) local name = file.basename(file.removesuffix(filename)) @@ -5689,6 +5799,10 @@ function otf.load(filename,format,sub,featurefile) end local data = containers.read(otf.cache,hash) local reload = not data or data.verbose ~= fonts.verbose or data.size ~= size or data.time ~= time + if forceload then + report_otf("loading: forced reload due to hard coded flag") + reload = true + end if not reload then local featuredata = data.featuredata if featurefiles then @@ -5712,11 +5826,14 @@ function otf.load(filename,format,sub,featurefile) end if reload then report_otf("loading: %s (hash: %s)",filename,hash) - local ff, messages + local fontdata, messages, rawdata if sub then - ff, messages = fontloader.open(filename,sub) + fontdata, messages = fontloader.open(filename,sub) else - ff, messages = fontloader.open(filename) + fontdata, messages = fontloader.open(filename) + end + if fontdata then + mainfields = mainfields or (fontloaderfields and fontloaderfields(fontdata)) end if trace_loading and messages and #messages > 0 then if type(messages) == "string" then @@ -5729,23 +5846,27 @@ function otf.load(filename,format,sub,featurefile) else report_otf("font loaded okay") end - if ff then + if fontdata then if featurefiles then for i=1,#featurefiles do - load_featurefile(ff,featurefiles[i].name) + load_featurefile(fontdata,featurefiles[i].name) end end - data = fontloader.to_table(ff) - fontloader.close(ff) - if data then + report_otf("loading method: %s",loadmethod) + if loadmethod == "sparse" then + rawdata = fontdata + else + rawdata = fontloader.to_table(fontdata) + fontloader.close(fontdata) + end + if rawdata then + data = { } + starttiming(data) + local verboseindeed = verbose ~= nil and verbose or trace_loading report_otf("file size: %s", size) - report_otf("enhancing ...") - for e=1,#ordered_enhancers do - enhance(ordered_enhancers[e],data,filename) - io.flush() -- we want instant messages - end - if otf.pack and not fonts.verbose then - enhance("pack",data,filename) + enhancers.apply(data,filename,rawdata,verboseindeed) + if packdata and not fonts.verbose then + enhance("pack",data,filename,nil,verboseindeed) end data.size = size data.time = time @@ -5755,13 +5876,27 @@ function otf.load(filename,format,sub,featurefile) data.verbose = fonts.verbose report_otf("saving in cache: %s",filename) data = containers.write(otf.cache, hash, data) - collectgarbage("collect") + if cleanup > 0 then + collectgarbage("collect") + end + stoptiming(data) + report_otf("preprocessing and caching took %s seconds",elapsedtime(data)) data = containers.read(otf.cache, hash) -- this frees the old table and load the sparse one - collectgarbage("collect") + if cleanup > 1 then + collectgarbage("collect") + end else + data = nil report_otf("loading failed (table conversion error)") end + if loadmethod == "sparse" then + fontloader.close(fontdata) + if cleanup > 2 then + -- collectgarbage("collect") + end + end else + data = nil report_otf("loading failed (file read error)") end end @@ -5769,8 +5904,8 @@ function otf.load(filename,format,sub,featurefile) if trace_defining then report_otf("loading from cache: %s",hash) end - enhance("unpack",data,filename,false) -- no message here - adddimensions(data) + enhance("unpack",data,filename,nil,false) + enhance("add dimensions",data,filename,nil,false) if trace_sequences then showfeatureorder(data,filename) end @@ -5778,89 +5913,321 @@ function otf.load(filename,format,sub,featurefile) return data end -adddimensions = function(data) +local mt = { + __index = function(t,k) -- maybe set it + if k == "height" then + local ht = t.boundingbox[4] + return ht < 0 and 0 or ht + elseif k == "depth" then + local dp = -t.boundingbox[2] + return dp < 0 and 0 or dp + elseif k == "width" then + return 0 + elseif k == "name" then -- or maybe uni* + return forcenotdef and ".notdef" + end + end +} + +actions["add dimensions"] = function(data,filename) -- todo: forget about the width if it's the defaultwidth (saves mem) -- we could also build the marks hash here (instead of storing it) if data then - local force = otf.notdef local luatex = data.luatex local defaultwidth = luatex.defaultwidth or 0 local defaultheight = luatex.defaultheight or 0 local defaultdepth = luatex.defaultdepth or 0 - for _, d in next, data.glyphs do - local bb, wd = d.boundingbox, d.width - if not wd then - d.width = defaultwidth - elseif wd ~= 0 and d.class == "mark" then - d.width = -wd - end - if force and not d.name then - d.name = ".notdef" - end - if bb then - local ht, dp = bb[4], -bb[2] - if ht == 0 or ht < 0 then - -- no need to set it and no negative heights, nil == 0 + if usemetatables then + for _, d in next, data.glyphs do + local wd = d.width + if not wd then + d.width = defaultwidth + elseif wd ~= 0 and d.class == "mark" then + d.width = -wd + end + setmetatable(d,mt) + end + else + for _, d in next, data.glyphs do + local bb, wd = d.boundingbox, d.width + if not wd then + d.width = defaultwidth + elseif wd ~= 0 and d.class == "mark" then + d.width = -wd + end + if forcenotdef and not d.name then + d.name = ".notdef" + end + if bb then + local ht, dp = bb[4], -bb[2] + if ht == 0 or ht < 0 then + -- not set + else + d.height = ht + end + if dp == 0 or dp < 0 then + -- not set + else + d.depth = dp + end + end + end + end + end +end + +actions["prepare tables"] = function(data,filename,raw) + local luatex = { + filename = filename, + version = otf.version, + creator = "context mkiv", + } + data.luatex = luatex + data.metadata = { } +end + +local function somecopy(old) -- fast one + if old then + local new = { } + if type(old) == "table" then + for k, v in next, old do + if k == "glyphs" then + -- skip + elseif type(v) == "table" then + new[k] = somecopy(v) else - d.height = ht + new[k] = v end - if dp == 0 or dp < 0 then - -- no negative depths and no negative depths, nil == 0 + end + else + for i=1,#mainfields do + local k = mainfields[i] + local v = old[k] + if k == "glyphs" then + -- skip + elseif type(v) == "table" then + new[k] = somecopy(v) else - d.depth = dp + new[k] = v end end end + return new + else + return { } end end -local function showfeatureorder(otfdata,filename) - local sequences = otfdata.luatex.sequences - if sequences and #sequences > 0 then - if trace_loading then - report_otf("font %s has %s sequences",filename,#sequences) - report_otf(" ") +actions["prepare glyphs"] = function(data,filename,raw) + -- we can also move the names to data.luatex.names which might + -- save us some more memory (at the cost of harder tracing) + local rawglyphs = raw.glyphs + local glyphs, udglyphs + if loadmethod == "sparse" then + glyphs, udglyphs = { }, { } + elseif loadmethod == "mixed" then + glyphs, udglyphs = { }, rawglyphs + else + glyphs, udglyphs = rawglyphs, rawglyphs + end + data.glyphs, data.udglyphs = glyphs, udglyphs + local subfonts = raw.subfonts + if subfonts then + if data.glyphs and next(data.glyphs) then + report_otf("replacing existing glyph table due to subfonts") end - for nos=1,#sequences do - local sequence = sequences[nos] - local typ = sequence.type or "no-type" - local name = sequence.name or "no-name" - local subtables = sequence.subtables or { "no-subtables" } - local features = sequence.features - if trace_loading then - report_otf("%3i %-15s %-20s [%s]",nos,name,typ,concat(subtables,",")) - end - if features then - for feature, scripts in next, features do - local tt = { } - for script, languages in next, scripts do - local ttt = { } - for language, _ in next, languages do - ttt[#ttt+1] = language + local cidinfo = raw.cidinfo + if cidinfo.registry then + local cidmap, cidname = fonts.cid.getmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) + if cidmap then + cidinfo.usedname = cidmap.usedname + local uni_to_int, int_to_uni, nofnames, nofunicodes = { }, { }, 0, 0 + local unicodes, names = cidmap.unicodes, cidmap.names + for cidindex=1,#subfonts do + local subfont = subfonts[cidindex] + if loadmethod == "sparse" then + local rawglyphs = subfont.glyphs + for index=0,subfont.glyphmax - 1 do + local g = rawglyphs[index] + if g then + local unicode, name = unicodes[index], names[index] + if unicode then + uni_to_int[unicode] = index + int_to_uni[index] = unicode + nofunicodes = nofunicodes + 1 + elseif name then + nofnames = nofnames + 1 + end + udglyphs[index] = g + glyphs[index] = { + width = g.width, + italic = g.italic_correction, + boundingbox = g.boundingbox, + class = g.class, + name = g.name or name or "unknown", -- uniXXXX + cidindex = cidindex, + unicode = unicode, + } + end end - tt[#tt+1] = format("[%s: %s]",script,concat(ttt," ")) - end - if trace_loading then - report_otf(" %s: %s",feature,concat(tt," ")) + -- If we had more userdata, we would need more of this + -- and it would start working against us in terms of + -- convenience and speed. + subfont = somecopy(subfont) + subfont.glyphs = nil + subfont[cidindex] = subfont + elseif loadmethod == "mixed" then + for index, g in next, subfont.glyphs do + local unicode, name = unicodes[index], names[index] + if unicode then + uni_to_int[unicode] = index + int_to_uni[index] = unicode + nofunicodes = nofunicodes + 1 + elseif name then + nofnames = nofnames + 1 + end + udglyphs[index] = g + glyphs[index] = { + width = g.width, + italic = g.italic_correction, + boundingbox = g.boundingbox, + class = g.class, + name = g.name or name or "unknown", -- uniXXXX + cidindex = cidindex, + unicode = unicode, + } + end + subfont.glyphs = nil + else + for index, g in next, subfont.glyphs do + local unicode, name = unicodes[index], names[index] + if unicode then + uni_to_int[unicode] = index + int_to_uni[index] = unicode + nofunicodes = nofunicodes + 1 + g.unicode = unicode + elseif name then + nofnames = nofnames + 1 + end + g.cidindex = cidindex + glyphs[index] = g + end + subfont.glyphs = nil end end + if trace_loading then + report_otf("cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames) + end + data.map = data.map or { } + data.map.map = uni_to_int + data.map.backmap = int_to_uni + elseif trace_loading then + report_otf("unable to remap cid font, missing cid file for %s",filename) end + data.subfonts = subfonts + elseif trace_loading then + report_otf("font %s has no glyphs",filename) end - if trace_loading then - report_otf("\n") + else + if loadmethod == "sparse" then + -- we get fields from the userdata glyph table and create + -- a minimal entry first + for index=0,raw.glyphmax - 1 do + local g = rawglyphs[index] + if g then + udglyphs[index] = g + glyphs[index] = { + width = g.width, + italic = g.italic_correction, + boundingbox = g.boundingbox, + class = g.class, + name = g.name, + unicode = g.unicode, + } + end + end + elseif loadmethod == "mixed" then + -- we get fields from the totable glyph table and copy to the + -- final glyph table so first we create a minimal entry + for index, g in next, rawglyphs do + udglyphs[index] = g + glyphs[index] = { + width = g.width, + italic = g.italic_correction, + boundingbox = g.boundingbox, + class = g.class, + name = g.name, + unicode = g.unicode, + } + end + else + -- we use the totable glyph table directly and manipulate the + -- entries in this (also final) table end - elseif trace_loading then - report_otf("font %s has no sequences",filename) + data.map = raw.map end + data.cidinfo = raw.cidinfo -- hack end --- todo: normalize, design_size => designsize +-- watch copy of cidinfo: we can best make some more copies to data -enhancers["reorganize mark classes"] = function(data,filename) - if data.mark_classes then - local unicodes = data.luatex.unicodes +actions["analyze glyphs"] = function(data,filename,raw) -- maybe integrate this in the previous + local glyphs = data.glyphs + -- collect info + local has_italic, widths, marks = false, { }, { } + for index, glyph in next, glyphs do + local italic = glyph.italic_correction + if not italic then + -- skip + elseif italic == 0 then + glyph.italic_correction = nil + glyph.italic = nil + else + glyph.italic_correction = nil + glyph.italic = italic + has_italic = true + end + local width = glyph.width + widths[width] = (widths[width] or 0) + 1 + if glyph.class == "mark" then + marks[glyph.unicode] = true + end + local a = glyph.altuni if a then glyph.altuni = nil end + local d = glyph.dependents if d then glyph.dependents = nil end + local v = glyph.vwidth if v then glyph.vwidth = nil end + end + -- flag italic + data.metadata.has_italic = has_italic + -- flag marks + data.luatex.marks = marks + -- share most common width for cjk fonts + local wd, most = 0, 1 + for k,v in next, widths do + if v > most then + wd, most = k, v + end + end + if most > 1000 then -- maybe 500 + if trace_loading then + report_otf("most common width: %s (%s times), sharing (cjk font)",wd,most) + end + for index, glyph in next, glyphs do + if glyph.width == wd then + glyph.width = nil + end + end + data.luatex.defaultwidth = wd + end +end + +actions["reorganize mark classes"] = function(data,filename,raw) + local mark_classes = raw.mark_classes + if mark_classes then + local luatex = data.luatex + local unicodes = luatex.unicodes local reverse = { } - for name, class in next, data.mark_classes do + luatex.markclasses = reverse + for name, class in next, mark_classes do local t = { } for s in gmatch(class,"[^ ]+") do local us = unicodes[s] @@ -5874,53 +6241,15 @@ enhancers["reorganize mark classes"] = function(data,filename) end reverse[name] = t end - data.luatex.markclasses = reverse - data.mark_classes = nil - end -end - -enhancers["prepare luatex tables"] = function(data,filename) - data.luatex = data.luatex or { } - local luatex = data.luatex - luatex.filename = filename - luatex.version = otf.version - luatex.creator = "context mkiv" -end - -local function analyze_features(g, features) - if g then - local t, done = { }, { } - for k=1,#g do - local f = features or g[k].features - if f then - for k=1,#f do - -- scripts and tag - local tag = f[k].tag - if not done[tag] then - t[#t+1] = tag - done[tag] = true - end - end - end - end - if #t > 0 then - return t - end + data.mark_classes = nil -- when using table end - return nil -end - -enhancers["analyze features"] = function(data,filename) - -- local luatex = data.luatex - -- luatex.gposfeatures = analyze_features(data.gpos) - -- luatex.gsubfeatures = analyze_features(data.gsub) end -enhancers["rehash features"] = function(data,filename) +actions["reorganize features"] = function(data,filename,raw) -- combine with other local features = { } data.luatex.features = features for k, what in next, otf.glists do - local dw = data[what] + local dw = raw[what] if dw then local f = { } features[what] = f @@ -5933,8 +6262,10 @@ enhancers["rehash features"] = function(data,filename) local tag = strip(lower(df.tag)) local ft = f[tag] if not ft then ft = {} f[tag] = ft end local dscripts = df.scripts - for script, languages in next, dscripts do - script = strip(lower(script)) + for i=1,#dscripts do + local d = dscripts[i] + local languages = d.langs + local script = strip(lower(d.script)) local fts = ft[script] if not fts then fts = {} ft[script] = fts end for i=1,#languages do fts[strip(lower(languages[i]))] = true @@ -5947,8 +6278,8 @@ enhancers["rehash features"] = function(data,filename) end end -enhancers["analyze anchors"] = function(data,filename) - local classes = data.anchor_classes +actions["reorganize anchor classes"] = function(data,filename,raw) + local classes = raw.anchor_classes -- anchor classes not in final table local luatex = data.luatex local anchor_to_lookup, lookup_to_anchor = { }, { } luatex.anchor_to_lookup, luatex.lookup_to_anchor = anchor_to_lookup, lookup_to_anchor @@ -5973,148 +6304,97 @@ enhancers["analyze anchors"] = function(data,filename) end end -enhancers["analyze marks"] = function(data,filename) - local glyphs = data.glyphs - local marks = { } - data.luatex.marks = marks - for unicode, index in next, data.luatex.indices do - local glyph = glyphs[index] - if glyph.class == "mark" then - marks[unicode] = true - end - end +actions["prepare tounicode"] = function(data,filename,raw) + fonts.map.addtounicode(data,filename) end -enhancers["analyze unicodes"] = fonts.map.addtounicode - -enhancers["analyze subtables"] = function(data,filename) - data.luatex = data.luatex or { } +actions["reorganize subtables"] = function(data,filename,raw) local luatex = data.luatex - local sequences = { } - local lookups = { } - luatex.sequences = sequences - luatex.lookups = lookups - for _, g in next, { data.gsub, data.gpos } do - for k=1,#g do - local gk = g[k] - local typ = gk.type - if typ == "gsub_contextchain" or typ == "gpos_contextchain" then - gk.chain = 1 - elseif typ == "gsub_reversecontextchain" or typ == "gpos_reversecontextchain" then - gk.chain = -1 - else - gk.chain = 0 - end - local features = gk.features - if features then - sequences[#sequences+1] = gk - -- scripts, tag, ismac - local t = { } - for f=1,#features do - local feature = features[f] - local hash = { } - -- only script and langs matter - for s, languages in next, feature.scripts do - s = lower(s) - local h = hash[s] - if not h then h = { } hash[s] = h end - for l=1,#languages do - h[strip(lower(languages[l]))] = true - end - end - t[feature.tag] = hash - end - gk.features = t - else - lookups[gk.name] = gk - gk.name = nil - end - local subtables = gk.subtables - if subtables then - local t = { } - for s=1,#subtables do - local subtable = subtables[s] - local name = subtable.name - t[#t+1] = name - end - gk.subtables = t - end - local flags = gk.flags - if flags then - gk.flags = { -- forcing false packs nicer - (flags.ignorecombiningmarks and "mark") or false, - (flags.ignoreligatures and "ligature") or false, - (flags.ignorebaseglyphs and "base") or false, - flags.r2l or false, - } - if flags.mark_class then - gk.markclass = luatex.markclasses[flags.mark_class] - end - end - end - end -end - -enhancers["merge cid fonts"] = function(data,filename) - -- we can also move the names to data.luatex.names which might - -- save us some more memory (at the cost of harder tracing) - if data.subfonts then - if data.glyphs and next(data.glyphs) then - report_otf("replacing existing glyph table due to subfonts") - end - local cidinfo = data.cidinfo - local verbose = fonts.verbose - if cidinfo.registry then - local cidmap, cidname = fonts.cid.getmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) - if cidmap then - cidinfo.usedname = cidmap.usedname - local glyphs, uni_to_int, int_to_uni, nofnames, nofunicodes = { }, { }, { }, 0, 0 - local unicodes, names = cidmap.unicodes, cidmap.names - for n, subfont in next, data.subfonts do - for index, g in next, subfont.glyphs do - if not next(g) then - -- dummy entry - else - local unicode, name = unicodes[index], names[index] - g.cidindex = n - g.boundingbox = g.boundingbox -- or zerobox - g.name = g.name or name or "unknown" - if unicode then - uni_to_int[unicode] = index - int_to_uni[index] = unicode - nofunicodes = nofunicodes + 1 - g.unicode = unicode - elseif name then - nofnames = nofnames + 1 - g.unicode = -1 + local sequences, lookups = { }, { } + luatex.sequences, luatex.lookups = sequences, lookups + for _, what in next, otf.glists do + local dw = raw[what] + if dw then + for k=1,#dw do + local gk = dw[k] + local typ = gk.type + local chain = + (typ == "gsub_contextchain" or typ == "gpos_contextchain") and 1 or + (typ == "gsub_reversecontextchain" or typ == "gpos_reversecontextchain") and -1 or 0 + -- + local subtables = gk.subtables + if subtables then + local t = { } + for s=1,#subtables do + local subtable = subtables[s] + local name = subtable.name + t[#t+1] = name + end + subtables = t + end + local flags, markclass = gk.flags, nil + if flags then + local t = { -- forcing false packs nicer + (flags.ignorecombiningmarks and "mark") or false, + (flags.ignoreligatures and "ligature") or false, + (flags.ignorebaseglyphs and "base") or false, + flags.r2l or false, + } + markclass = flags.mark_class + if markclass then + markclass = luatex.markclasses[markclass] + end + flags = t + end + -- + local name = gk.name + -- + local features = gk.features + if features then + -- scripts, tag, ismac + local f = { } + for i=1,#features do + local df = features[i] + local tag = strip(lower(df.tag)) + local ft = f[tag] if not ft then ft = {} f[tag] = ft end + local dscripts = df.scripts + for i=1,#dscripts do + local d = dscripts[i] + local languages = d.langs + local script = strip(lower(d.script)) + local fts = ft[script] if not fts then fts = {} ft[script] = fts end + for i=1,#languages do + fts[strip(lower(languages[i]))] = true end - glyphs[index] = g end end - subfont.glyphs = nil - end - if trace_loading then - report_otf("cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames) + sequences[#sequences+1] = { + type = typ, + chain = chain, + flags = flags, + name = name, + subtables = subtables, + markclass = markclass, + features = f, + } + else + lookups[name] = { + type = typ, + chain = chain, + flags = flags, + subtables = subtables, + markclass = markclass, + } end - data.glyphs = glyphs - data.map = data.map or { } - data.map.map = uni_to_int - data.map.backmap = int_to_uni - elseif trace_loading then - report_otf("unable to remap cid font, missing cid file for %s",filename) end - elseif trace_loading then - report_otf("font %s has no glyphs",filename) end end end -enhancers["prepare unicode"] = function(data,filename) +actions["prepare unicodes"] = function(data,filename,raw) local luatex = data.luatex - if not luatex then luatex = { } data.luatex = luatex end local indices, unicodes, multiples, internals = { }, { }, { }, { } - local glyphs = data.glyphs - local mapmap = data.map + local mapmap = data.map or raw.map if not mapmap then report_otf("no map in %s",filename) mapmap = { } @@ -6128,12 +6408,13 @@ enhancers["prepare unicode"] = function(data,filename) end local criterium = fonts.privateoffset local private = criterium + local glyphs = data.glyphs for index, glyph in next, glyphs do if index > 0 then - local name = glyph.name + local name = glyph.name -- really needed ? if name then local unicode = glyph.unicode - if unicode == -1 or unicode >= criterium then + if not unicode or unicode == -1 or unicode >= criterium then glyph.unicode = private indices[private] = index unicodes[name] = private @@ -6146,6 +6427,8 @@ enhancers["prepare unicode"] = function(data,filename) indices[unicode] = index unicodes[name] = unicode end + else + -- message that something is wrong end end end @@ -6157,7 +6440,7 @@ enhancers["prepare unicode"] = function(data,filename) local un = unicodes[name] if not un then unicodes[name] = unicode -- or 0 - elseif type(un) == "number" then + elseif type(un) == "number" then -- tonumber(un) if un ~= unicode then multiples[#multiples+1] = name unicodes[name] = { un, unicode } @@ -6182,9 +6465,9 @@ enhancers["prepare unicode"] = function(data,filename) end if trace_loading then if #multiples > 0 then - report_otf("%s glyph are reused: %s",#multiples, concat(multiples," ")) + report_otf("%s glyphs are reused: %s",#multiples, concat(multiples," ")) else - report_otf("no glyph are reused") + report_otf("no glyphs are reused") end end luatex.indices = indices @@ -6192,26 +6475,14 @@ enhancers["prepare unicode"] = function(data,filename) luatex.private = private end -enhancers["cleanup ttf tables"] = function(data,filename) - local ttf_tables = data.ttf_tables - if ttf_tables then - for k=1,#ttf_tables do - if ttf_tables[k].data then ttf_tables[k].data = "deleted" end - end - end - data.ttf_tab_saved = nil -end - -enhancers["compact glyphs"] = function(data,filename) - table.compact(data.glyphs) -- needed? - if data.subfonts then - for _, subfont in next, data.subfonts do - table.compact(subfont.glyphs) -- needed? - end +actions["prepare lookups"] = function(data,filename,raw) + local lookups = raw.lookups + if lookups then + data.lookups = lookups end end -enhancers["reverse coverage"] = function(data,filename) +actions["reorganize lookups"] = function(data,filename,raw) -- we prefer the before lookups in a normal order if data.lookups then for _, v in next, data.lookups do @@ -6219,7 +6490,7 @@ enhancers["reverse coverage"] = function(data,filename) for _, vv in next, v.rules do local c = vv.coverage if c and c.before then - c.before = table.reverse(c.before) + c.before = reverse(c.before) end end end @@ -6227,35 +6498,18 @@ enhancers["reverse coverage"] = function(data,filename) end end -enhancers["check italic correction"] = function(data,filename) - local glyphs = data.glyphs - local ok = false - for index, glyph in next, glyphs do - local ic = glyph.italic_correction - if ic then - if ic ~= 0 then - glyph.italic = ic - end - glyph.italic_correction = nil - ok = true - end - end - -- we can use this to avoid calculations - otf.tables.valid_fields[#otf.tables.valid_fields+1] = "has_italic" - data.has_italic = true -end - -enhancers["check math"] = function(data,filename) - if data.math then +actions["analyze math"] = function(data,filename,raw) + if raw.math then -- we move the math stuff into a math subtable because we then can -- test faster in the tfm copy - local glyphs = data.glyphs + local glyphs, udglyphs = data.glyphs, data.udglyphs local unicodes = data.luatex.unicodes - for index, glyph in next, glyphs do - local mk = glyph.mathkern - local hv = glyph.horiz_variants - local vv = glyph.vert_variants + for index, udglyph in next, udglyphs do + local mk = udglyph.mathkern + local hv = udglyph.horiz_variants + local vv = udglyph.vert_variants if mk or hv or vv then + local glyph = glyphs[index] local math = { } glyph.math = math if mk then @@ -6265,7 +6519,6 @@ enhancers["check math"] = function(data,filename) end end math.kerns = mk - glyph.mathkern = nil end if hv then math.horiz_variants = hv.variants @@ -6281,7 +6534,6 @@ enhancers["check math"] = function(data,filename) if ic and ic ~= 0 then math.horiz_italic_correction = ic end - glyph.horiz_variants = nil end if vv then local uc = unicodes[index] @@ -6298,221 +6550,48 @@ enhancers["check math"] = function(data,filename) if ic and ic ~= 0 then math.vert_italic_correction = ic end - glyph.vert_variants = nil end local ic = glyph.italic_correction if ic then if ic ~= 0 then math.italic_correction = ic end - glyph.italic_correction = nil end end end end end -enhancers["share widths"] = function(data,filename) - local glyphs = data.glyphs - local widths = { } - for index, glyph in next, glyphs do - local width = glyph.width - widths[width] = (widths[width] or 0) + 1 - end - -- share width for cjk fonts - local wd, most = 0, 1 - for k,v in next, widths do - if v > most then - wd, most = k, v - end - end - if most > 1000 then - if trace_loading then - report_otf("most common width: %s (%s times), sharing (cjk font)",wd,most) - end - for k, v in next, glyphs do - if v.width == wd then - v.width = nil - end - end - data.luatex.defaultwidth = wd - end -end - --- kern: ttf has a table with kerns - --- Weird, as maxfirst and maxseconds can have holes, first seems to be indexed, but --- seconds can start at 2 .. this need to be fixed as getn as well as # are sort of --- unpredictable alternatively we could force an [1] if not set (maybe I will do that --- anyway). - ---~ enhancers["reorganize kerns"] = function(data,filename) ---~ local glyphs, mapmap, unicodes = data.glyphs, data.luatex.indices, data.luatex.unicodes ---~ local mkdone = false ---~ for index, glyph in next, glyphs do ---~ if glyph.kerns then ---~ local mykerns = { } ---~ for k,v in next, glyph.kerns do ---~ 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 not uvc then ---~ if trace_loading then ---~ report_otf("problems with unicode %s of kern %s at glyph %s",vc,k,index) ---~ end ---~ else ---~ if type(vl) ~= "table" then ---~ vl = { vl } ---~ end ---~ for l=1,#vl do ---~ local vll = vl[l] ---~ local mkl = mykerns[vll] ---~ if not mkl then ---~ mkl = { } ---~ mykerns[vll] = mkl ---~ end ---~ if type(uvc) == "table" then ---~ for u=1,#uvc do ---~ mkl[uvc[u]] = vo ---~ end ---~ else ---~ mkl[uvc] = vo ---~ end ---~ end ---~ end ---~ end ---~ end ---~ glyph.mykerns = mykerns ---~ glyph.kerns = nil -- saves space and time ---~ mkdone = true ---~ end ---~ end ---~ if trace_loading and mkdone then ---~ report_otf("replacing 'kerns' tables by 'mykerns' tables") ---~ end ---~ if data.kerns then ---~ if trace_loading then ---~ report_otf("removing global 'kern' table") ---~ end ---~ data.kerns = nil ---~ end ---~ local dgpos = data.gpos ---~ if dgpos then ---~ local separator = lpeg.P(" ") ---~ local other = ((1 - separator)^0) / unicodes ---~ local splitter = lpeg.Ct(other * (separator * other)^0) ---~ for gp=1,#dgpos do ---~ local gpos = dgpos[gp] ---~ local subtables = gpos.subtables ---~ if subtables then ---~ for s=1,#subtables do ---~ local subtable = subtables[s] ---~ local kernclass = subtable.kernclass -- name is inconsistent with anchor_classes ---~ if kernclass then -- the next one is quite slow ---~ local split = { } -- saves time ---~ for k=1,#kernclass do ---~ local kcl = kernclass[k] ---~ local firsts, seconds, offsets, lookups = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup -- singular ---~ if type(lookups) ~= "table" then ---~ lookups = { lookups } ---~ end ---~ local maxfirsts, maxseconds = getn(firsts), getn(seconds) ---~ for _, s in next, firsts do ---~ split[s] = split[s] or lpegmatch(splitter,s) ---~ end ---~ for _, s in next, seconds do ---~ split[s] = split[s] or lpegmatch(splitter,s) ---~ end ---~ for l=1,#lookups do ---~ local lookup = lookups[l] ---~ local function do_it(fk,first_unicode) ---~ local glyph = glyphs[mapmap[first_unicode]] ---~ if glyph then ---~ 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 ---~ local baseoffset = (fk-1) * maxseconds ---~ for sk=2,maxseconds do -- we can avoid this loop with a table ---~ local sv = seconds[sk] ---~ local splt = split[sv] ---~ if splt then ---~ local offset = offsets[baseoffset + sk] ---~ --~ local offset = offsets[sk] -- (fk-1) * maxseconds + sk] ---~ if offset then ---~ for i=1,#splt do ---~ local second_unicode = splt[i] ---~ if tonumber(second_unicode) then ---~ lookupkerns[second_unicode] = offset ---~ else for s=1,#second_unicode do ---~ lookupkerns[second_unicode[s]] = offset ---~ end end ---~ end ---~ end ---~ end ---~ end ---~ elseif trace_loading then ---~ report_otf("no glyph data for U+%04X", first_unicode) ---~ end ---~ end ---~ for fk=1,#firsts do ---~ local fv = firsts[fk] ---~ local splt = split[fv] ---~ if splt then ---~ for i=1,#splt do ---~ local first_unicode = splt[i] ---~ if tonumber(first_unicode) then ---~ do_it(fk,first_unicode) ---~ else for f=1,#first_unicode do ---~ do_it(fk,first_unicode[f]) ---~ end end ---~ end ---~ end ---~ end ---~ end ---~ end ---~ subtable.comment = "The kernclass table is merged into mykerns in the indexed glyph tables." ---~ subtable.kernclass = { } ---~ end ---~ end ---~ end ---~ end ---~ end ---~ end - -enhancers["reorganize kerns"] = function(data,filename) - local glyphs, mapmap, unicodes = data.glyphs, data.luatex.indices, data.luatex.unicodes +actions["reorganize glyph kerns"] = function(data,filename,raw) + local luatex = data.luatex + local udglyphs, glyphs, mapmap, unicodes = data.udglyphs, data.glyphs, luatex.indices, luatex.unicodes local mkdone = false - local function do_it(lookup,first_unicode,kerns) + local function do_it(lookup,first_unicode,extrakerns) -- can be moved inline but seldom used local glyph = glyphs[mapmap[first_unicode]] if glyph then - local mykerns = glyph.mykerns - if not mykerns then - mykerns = { } -- unicode indexed ! - glyph.mykerns = mykerns + local kerns = glyph.kerns + if not kerns then + kerns = { } -- unicode indexed ! + glyph.kerns = kerns end - local lookupkerns = mykerns[lookup] + local lookupkerns = kerns[lookup] if not lookupkerns then lookupkerns = { } - mykerns[lookup] = lookupkerns + kerns[lookup] = lookupkerns end - for second_unicode, kern in next, kerns do + for second_unicode, kern in next, extrakerns do lookupkerns[second_unicode] = kern end elseif trace_loading then report_otf("no glyph data for U+%04X", first_unicode) end end - for index, glyph in next, glyphs do - if glyph.kerns then - local mykerns = { } - for k,v in next, glyph.kerns do + for index, udglyph in next, data.udglyphs do + local kerns = udglyph.kerns + if kerns then + local glyph = glyphs[index] + local newkerns = { } + for k,v in next, kerns do 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] @@ -6526,10 +6605,10 @@ enhancers["reorganize kerns"] = function(data,filename) end for l=1,#vl do local vll = vl[l] - local mkl = mykerns[vll] + local mkl = newkerns[vll] if not mkl then mkl = { } - mykerns[vll] = mkl + newkerns[vll] = mkl end if type(uvc) == "table" then for u=1,#uvc do @@ -6542,21 +6621,14 @@ enhancers["reorganize kerns"] = function(data,filename) end end end - glyph.mykerns = mykerns - glyph.kerns = nil -- saves space and time + glyph.kerns = newkerns -- udglyph.kerns = nil when in mixed mode mkdone = true end end if trace_loading and mkdone then - report_otf("replacing 'kerns' tables by 'mykerns' tables") + report_otf("replacing 'kerns' tables by a new 'kerns' tables") end - if data.kerns then - if trace_loading then - report_otf("removing global 'kern' table") - end - data.kerns = nil - end - local dgpos = data.gpos + local dgpos = raw.gpos if dgpos then local separator = lpeg.P(" ") local other = ((1 - separator)^0) / unicodes @@ -6621,7 +6693,7 @@ enhancers["reorganize kerns"] = function(data,filename) end end end - subtable.comment = "The kernclass table is merged into mykerns in the indexed glyph tables." + subtable.comment = "The kernclass table is merged into kerns in the indexed glyph tables." subtable.kernclass = { } end end @@ -6630,14 +6702,10 @@ enhancers["reorganize kerns"] = function(data,filename) end end -enhancers["strip not needed data"] = function(data,filename) +actions["check glyphs"] = function(data,filename,raw) local verbose = fonts.verbose local int_to_uni = data.luatex.unicodes for k, v in next, data.glyphs do - local d = v.dependents - if d then v.dependents = nil end - local a = v.altuni - if a then v.altuni = nil end if verbose then local code = int_to_uni[k] -- looks like this is done twice ... bug? @@ -6660,32 +6728,57 @@ enhancers["strip not needed data"] = function(data,filename) v.index = nil end end - data.luatex.comment = "Glyph tables have their original index. When present, mykern tables are indexed by unicode." - data.map = nil - data.names = nil -- funny names for editors - data.glyphcnt = nil - data.glyphmax = nil - if true then - data.gpos = nil - data.gsub = nil - data.anchor_classes = nil - end + data.luatex.comment = "Glyph tables have their original index. When present, kern tables are indexed by unicode." end -enhancers["migrate metadata"] = function(data,filename) - local global_fields = otf.tables.global_fields - local metadata = { } - for k,v in next, data do - if not global_fields[k] then - metadata[k] = v - data[k] = nil +actions["check metadata"] = function(data,filename,raw) + local metadata = data.metadata + metadata.method = loadmethod + if loadmethod == "sparse" then + for _, k in next, mainfields do + if valid_fields[k] then + local v = raw[k] + if global_fields[k] then + if not data[k] then + data[k] = v + end + else + if not metadata[k] then + metadata[k] = v + end + end + end + end + else + for k, v in next, raw do + if valid_fields[k] then + if global_fields[k] then + if not data[k] then + data[v] = v + end + else + if not metadata[k] then + metadata[k] = v + end + end + end + end + end + local pfminfo = raw.pfminfo + if pfminfo then + data.pfminfo = pfminfo + metadata.isfixedpitch = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion == "Monospaced") + metadata.charwidth = pfminfo and pfminfo.avgwidth + end + local ttftables = metadata.ttf_tables + if ttftables then + for i=1,#ttftables do + ttftables[i].data = "deleted" end end - data.metadata = metadata - -- goodies - local pfminfo = data.pfminfo - metadata.isfixedpitch = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose["proportion"] == "Monospaced") - metadata.charwidth = pfminfo and pfminfo.avgwidth + metadata.xuid = nil + data.udglyphs = nil + data.map = nil end local private_math_parameters = { @@ -6693,7 +6786,7 @@ local private_math_parameters = { "FractionDelimiterDisplayStyleSize", } -enhancers["check math parameters"] = function(data,filename) +actions["check math parameters"] = function(data,filename,raw) local mathdata = data.metadata.math if mathdata then for m=1,#private_math_parameters do @@ -6708,123 +6801,101 @@ enhancers["check math parameters"] = function(data,filename) end end -enhancers["flatten glyph lookups"] = function(data,filename) - for k, v in next, data.glyphs do - local lookups = v.lookups + +-- kern: ttf has a table with kerns +-- +-- Weird, as maxfirst and maxseconds can have holes, first seems to be indexed, but +-- seconds can start at 2 .. this need to be fixed as getn as well as # are sort of +-- unpredictable alternatively we could force an [1] if not set (maybe I will do that +-- anyway). + +actions["reorganize glyph lookups"] = function(data,filename,raw) + local glyphs = data.glyphs + for index, udglyph in next, data.udglyphs do + local lookups = udglyph.lookups if lookups then + local glyph = glyphs[index] + local l = { } for kk, vv in next, lookups do + local aa = { } + l[kk] = aa for kkk=1,#vv do local vvv = vv[kkk] local s = vvv.specification - if s then - local t = vvv.type - if t == "ligature" then - vv[kkk] = { "ligature", s.components, s.char } - elseif t == "alternate" then - vv[kkk] = { "alternate", s.components } - elseif t == "substitution" then - vv[kkk] = { "substitution", s.variant } - elseif t == "multiple" then - vv[kkk] = { "multiple", s.components } - elseif t == "position" then - vv[kkk] = { "position", { s.x or 0, s.y or 0, s.h or 0, s.v or 0 } } - elseif t == "pair" then - local one, two, paired = s.offsets[1], s.offsets[2], s.paired or "" - if one then - if two then - vv[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0 } } - else - vv[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } } - end + local t = vvv.type + -- #aa+1 + if t == "ligature" then + aa[kkk] = { "ligature", s.components, s.char } + elseif t == "alternate" then + aa[kkk] = { "alternate", s.components } + elseif t == "substitution" then + aa[kkk] = { "substitution", s.variant } + elseif t == "multiple" then + aa[kkk] = { "multiple", s.components } + elseif t == "position" then + aa[kkk] = { "position", { s.x or 0, s.y or 0, s.h or 0, s.v or 0 } } + elseif t == "pair" then + -- maybe flatten this one + local one, two, paired = s.offsets[1], s.offsets[2], s.paired or "" + if one then + if two then + aa[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0 } } else - if two then - vv[kkk] = { "pair", paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { } - else - vv[kkk] = { "pair", paired } - end + aa[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } } end else - if trace_loading then - report_otf("flattening needed, report to context list") - end - for a, b in next, s do - if trace_loading and vvv[a] then - report_otf("flattening conflict, report to context list") - end - vvv[a] = b + if two then + aa[kkk] = { "pair", paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { } + else + aa[kkk] = { "pair", paired } end - vvv.specification = nil end end end end - end - end -end - -enhancers["simplify glyph lookups"] = function(data,filename) - for k, v in next, data.glyphs do - local lookups = v.lookups - if lookups then + -- we could combine this local slookups, mlookups - for kk, vv in next, lookups do + for kk, vv in next, l do if #vv == 1 then if not slookups then slookups = { } - v.slookups = slookups + glyph.slookups = slookups end slookups[kk] = vv[1] else if not mlookups then mlookups = { } - v.mlookups = mlookups + glyph.mlookups = mlookups end mlookups[kk] = vv end end - v.lookups = nil + glyph.lookups = nil -- when using table end end end -enhancers["flatten anchor tables"] = function(data,filename) - for k, v in next, data.glyphs do - if v.anchors then - for kk, vv in next, v.anchors do +actions["reorganize glyph anchors"] = function(data,filename,raw) + local glyphs = data.glyphs + for index, udglyph in next, data.udglyphs do + local anchors = udglyph.anchors + if anchors then + local glyph = glyphs[index] + local a = { } + glyph.anchors = a + for kk, vv in next, anchors do + local aa = { } + a[kk] = aa for kkk, vvv in next, vv do if vvv.x or vvv.y then - vv[kkk] = { vvv.x or 0, vvv.y or 0 } + aa[kkk] = { vvv.x , vvv.y } else + local aaa = { } + aa[kkk] = aaa for kkkk=1,#vvv do local vvvv = vvv[kkkk] - vvv[kkkk] = { vvvv.x or 0, vvvv.y or 0 } - end - end - end - end - end - end -end - -enhancers["flatten feature tables"] = function(data,filename) - -- is this needed? do we still use them at all? - for _, tag in next, otf.glists do - if data[tag] then - if trace_loading then - report_otf("flattening %s table", tag) - end - for k, v in next, data[tag] do - local features = v.features - if features then - for kk=1,#features do - local vv = features[kk] - local t = { } - local scripts = vv.scripts - for kkk=1,#scripts do - local vvv = scripts[kkk] - t[vvv.script] = vvv.langs + aaa[kkkk] = { vvvv.x, vvvv.y } end - vv.scripts = t end end end @@ -6832,22 +6903,12 @@ enhancers["flatten feature tables"] = function(data,filename) end end -enhancers.patches = allocate() - -enhancers["patch bugs"] = function(data,filename) - local basename = file.basename(lower(filename)) - for pattern, action in next, enhancers.patches do - if find(basename,pattern) then - action(data,filename) - end - end -end - --- tex features +--~ actions["check extra features"] = function(data,filename,raw) +--~ -- later, ctx only +--~ end -enhancers["enrich with features"] = function(data,filename) - -- later, ctx only -end +-- -- -- -- -- -- +-- -- -- -- -- -- function otf.features.register(name,default) otf.features.list[#otf.features.list+1] = name @@ -6924,25 +6985,6 @@ tfmdata.mode = mode return processes, features end ---~ { ---~ ['boundingbox']={ 95, -458, 733, 1449 }, ---~ ['class']="base", ---~ ['name']="braceleft", ---~ ['unicode']=123, ---~ ['vert_variants']={ ---~ ['italic_correction']=0, ---~ ['parts']={ ---~ { ['component']="uni23A9", ['endConnectorLength']=1000, ['fullAdvance']=2546, ['is_extender']=0, ['startConnectorLength']=0, }, -- bot ---~ { ['component']="uni23AA", ['endConnectorLength']=2500, ['fullAdvance']=2501, ['is_extender']=1, ['startConnectorLength']=2500, }, -- rep ---~ { ['component']="uni23A8", ['endConnectorLength']=1000, ['fullAdvance']=4688, ['is_extender']=0, ['startConnectorLength']=1000, }, -- mid ---~ { ['component']="uni23AA", ['endConnectorLength']=2500, ['fullAdvance']=2501, ['is_extender']=1, ['startConnectorLength']=2500, }, -- rep ---~ { ['component']="uni23A7", ['endConnectorLength']=0, ['fullAdvance']=2546, ['is_extender']=0, ['startConnectorLength']=1000, }, -- top ---~ }, ---~ ['variants']="braceleft braceleft.vsize1 braceleft.vsize2 braceleft.vsize3 braceleft.vsize4 braceleft.vsize5 braceleft.vsize6 braceleft.vsize7", ---~ }, ---~ ['width']=793, ---~ }, - -- the first version made a top/mid/not extensible table, now we just pass on the variants data -- and deal with it in the tfm scaler (there is no longer an extensible table anyway) @@ -7050,7 +7092,7 @@ local function copytotfm(data,cache_id) -- we can save a copy when we reorder th local filename = fonts.tfm.checkedfilename(luatex) local fontname = metadata.fontname local fullname = metadata.fullname or fontname - local cidinfo = data.cidinfo + local cidinfo = data.cidinfo -- or { } local units = metadata.units_per_em or 1000 -- cidinfo.registry = cidinfo and cidinfo.registry or "" -- weird here, fix upstream @@ -7071,7 +7113,7 @@ local function copytotfm(data,cache_id) -- we can save a copy when we reorder th if metadata.isfixedpitch then parameters.space_stretch = 0 parameters.space_shrink = 0 - elseif otf.syncspace then -- + elseif syncspace then -- parameters.space_stretch = spaceunits/2 parameters.space_shrink = spaceunits/3 end @@ -7697,7 +7739,7 @@ local function preparebasekerns(tfmdata,kind,value) -- todo what kind of kerns, for u, chr in next, characters do local d = descriptions[u] if d then - local dk = d.mykerns -- shared + local dk = d.kerns -- shared if dk then local s = sharedkerns[dk] if s == false then @@ -10285,7 +10327,7 @@ local function prepare_lookups(tfmdata) end end end - local list = glyph.mykerns + local list = glyph.kerns if list then for lookup, krn in next, list do local k = kerns[lookup] @@ -10831,6 +10873,12 @@ local report_otf = logs.new("load otf") -- -- we could have a tnum variant as well +-- In the userdata interface we can not longer tweak the loaded font as +-- conveniently as before. For instance, instead of pushing extra data in +-- in the table using the original structure, we now have to operate on +-- the mkiv representation. And as the fontloader interface is modelled +-- after fontforge we cannot change that one too much either. + local extra_lists = { tlig = { { @@ -10881,116 +10929,120 @@ local extra_lists = { local extra_features = { -- maybe just 1..n so that we prescribe order tlig = { { - features = { { scripts = { { script = "*", langs = { "*" }, } }, tag = "tlig", comment = "added bij mkiv" }, }, + features = { ["*"] = { ["*"] = true } }, name = "ctx_tlig_1", - subtables = { { name = "ctx_tlig_1_s" } }, + subtables = { "ctx_tlig_1_s" }, type = "gsub_ligature", flags = { }, }, }, trep = { { - features = { { scripts = { { script = "*", langs = { "*" }, } }, tag = "trep", comment = "added bij mkiv" }, }, + features = { ["*"] = { ["*"] = true } }, name = "ctx_trep_1", - subtables = { { name = "ctx_trep_1_s" } }, + subtables = { "ctx_trep_1_s" }, type = "gsub_single", flags = { }, }, }, anum = { { - features = { { scripts = { { script = "arab", langs = { "dflt", "FAR" }, } }, tag = "anum", comment = "added bij mkiv" }, }, + features = { arab = { URD = true, dflt = true } }, name = "ctx_anum_1", - subtables = { { name = "ctx_anum_1_s" } }, + subtables = { "ctx_anum_1_s" }, type = "gsub_single", flags = { }, }, { - features = { { scripts = { { script = "arab", langs = { "URD" }, } }, tag = "anum", comment = "added bij mkiv" }, }, + features = { arab = { URD = true } }, name = "ctx_anum_2", - subtables = { { name = "ctx_anum_2_s" } }, + subtables = { "ctx_anum_2_s" }, type = "gsub_single", flags = { }, }, }, } -otf.enhancers["add some missing characters"] = function(data,filename) - -- todo -end - -otf.enhancers["enrich with features"] = function(data,filename) - -- could be done elsewhere (true can be #) - local used = { } - for i=1,#otf.glists do - local g = data[otf.glists[i]] - if g then - for i=1,#g do - local f = g[i].features - if f then - for i=1,#f do - local t = f[i].tag - if t then used[t] = true end - end - end - end - end - end - -- +local function enhancedata(data,filename,raw) + local luatex = data.luatex + local lookups = luatex.lookups + local sequences = luatex.sequences local glyphs = data.glyphs - local indices = data.map.map - data.gsub = data.gsub or { } + local indices = luatex.indices + local gsubfeatures = luatex.features.gsub for kind, specifications in next, extra_features do - if not used[kind] then + if gsub and gsub[kind] then + -- already present + else local done = 0 for s=1,#specifications do local added = false local specification = specifications[s] + local features, subtables = specification.features, specification.subtables + local name, type, flags = specification.name, specification.type, specification.flags + local full = subtables[1] local list = extra_lists[kind][s] - local name = specification.name .. "_s" - if specification.type == "gsub_ligature" then + if type == "gsub_ligature" then + -- inefficient loop for unicode, index in next, indices do local glyph = glyphs[index] local ligature = list[glyph.name] if ligature then - local o = glyph.lookups or { } - -- o[name] = { "ligature", ligature, glyph.name } - o[name] = { - { - ["type"] = "ligature", - ["specification"] = { - char = glyph.name, - components = ligature, - } - } - } - glyph.lookups, done, added = o, done+1, true + if glyph.slookups then + glyph.slookups [full] = { "ligature", ligature, glyph.name } + else + glyph.slookups = { [full] = { "ligature", ligature, glyph.name } } + end + done, added = done+1, true end end - elseif specification.type == "gsub_single" then + elseif type == "gsub_single" then + -- inefficient loop for unicode, index in next, indices do local glyph = glyphs[index] local r = list[unicode] if r then local replacement = indices[r] if replacement and glyphs[replacement] then - local o = glyph.lookups or { } - -- o[name] = { { "substitution", glyphs[replacement].name } } - o[name] = { - { - ["type"] = "substitution", - ["specification"] = { - variant = glyphs[replacement].name, - } - } - } - glyph.lookups, done, added = o, done+1, true + if glyph.slookups then + glyph.slookups [full] = { "substitution", glyphs[replacement].name } + else + glyph.slookups = { [full] = { "substitution", glyphs[replacement].name } } + end + done, added = done+1, true end end end end if added then - insert(data.gsub,s,table.fastcopy(specification)) -- right order + sequences[#sequences+1] = { + chain = 0, + features = { [kind] = features }, + flags = flags, + name = name, + subtables = subtables, + type = type, + } + -- register in metadata (merge as there can be a few) + if not gsubfeatures then + gsubfeatures = { } + luatex.features.gsub = gsubfeatures + end + local k = gsubfeatures[kind] + if not k then + k = { } + gsubfeatures[kind] = k + end + for script, languages in next, features do + local kk = k[script] + if not kk then + kk = { } + k[script] = kk + end + for language, value in next, languages do + kk[language] = value + end + end end end if done > 0 then @@ -11002,6 +11054,8 @@ otf.enhancers["enrich with features"] = function(data,filename) end end +otf.enhancers.register("check extra features",enhancedata) + local features = otf.tables.features features['tlig'] = 'TeX Ligatures' -- cgit v1.2.3