diff options
34 files changed, 2070 insertions, 1760 deletions
diff --git a/scripts/context/lua/mtx-base.lua b/scripts/context/lua/mtx-base.lua index 443f653f0..67bde5b79 100644 --- a/scripts/context/lua/mtx-base.lua +++ b/scripts/context/lua/mtx-base.lua @@ -130,7 +130,7 @@ elseif environment.arguments["configurations"] or environment.arguments["show-co resolvers.listers.configurations() elseif environment.arguments["help"] or (environment.files[1]=='help') or (#environment.files==0) then logs.help(messages.help) -elseif environment.files[1]=='texmfcnf.lua' then +elseif environment.files[1] == 'texmfcnf.lua' then resolvers.load("nofiles") resolvers.listers.configurations() else diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua index f792cd942..246d08103 100644 --- a/scripts/context/lua/mtxrun.lua +++ b/scripts/context/lua/mtxrun.lua @@ -709,20 +709,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 @@ -730,6 +732,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 = {} @@ -1146,6 +1150,7 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) serialize(root,name,flush,reduce,noquotes,hexify) end f:close() + io.flush() end end @@ -1381,6 +1386,7 @@ function io.savedata(filename,data,joiner) f:write(data or "") end f:close() + io.flush() return true else return false @@ -9406,7 +9412,7 @@ function resolvers.variableofformat(str) return formats[str] or formats[alternatives[str]] or '' end -function resolvers.vsriableofformatorsuffix(str) +function resolvers.variableofformatorsuffix(str) local v = formats[str] if v then return v diff --git a/scripts/context/stubs/mswin/mtxrun.lua b/scripts/context/stubs/mswin/mtxrun.lua index f792cd942..246d08103 100644 --- a/scripts/context/stubs/mswin/mtxrun.lua +++ b/scripts/context/stubs/mswin/mtxrun.lua @@ -709,20 +709,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 @@ -730,6 +732,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 = {} @@ -1146,6 +1150,7 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) serialize(root,name,flush,reduce,noquotes,hexify) end f:close() + io.flush() end end @@ -1381,6 +1386,7 @@ function io.savedata(filename,data,joiner) f:write(data or "") end f:close() + io.flush() return true else return false @@ -9406,7 +9412,7 @@ function resolvers.variableofformat(str) return formats[str] or formats[alternatives[str]] or '' end -function resolvers.vsriableofformatorsuffix(str) +function resolvers.variableofformatorsuffix(str) local v = formats[str] if v then return v diff --git a/scripts/context/stubs/unix/mtxrun b/scripts/context/stubs/unix/mtxrun index f792cd942..246d08103 100755 --- a/scripts/context/stubs/unix/mtxrun +++ b/scripts/context/stubs/unix/mtxrun @@ -709,20 +709,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 @@ -730,6 +732,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 = {} @@ -1146,6 +1150,7 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) serialize(root,name,flush,reduce,noquotes,hexify) end f:close() + io.flush() end end @@ -1381,6 +1386,7 @@ function io.savedata(filename,data,joiner) f:write(data or "") end f:close() + io.flush() return true else return false @@ -9406,7 +9412,7 @@ function resolvers.variableofformat(str) return formats[str] or formats[alternatives[str]] or '' end -function resolvers.vsriableofformatorsuffix(str) +function resolvers.variableofformatorsuffix(str) local v = formats[str] if v then return v diff --git a/tex/context/base/back-ini.mkiv b/tex/context/base/back-ini.mkiv index db37cf61f..12c41d169 100644 --- a/tex/context/base/back-ini.mkiv +++ b/tex/context/base/back-ini.mkiv @@ -128,22 +128,6 @@ \let \doloadmapline \gobbletwoarguments % + - = | fileline %D \macros -%D {ifusepagedestinations} -%D -%D In \PDF\ version 1.0 only page references were supported, -%D while in \DVIWINDO\ 1.N only named references were accepted. -%D Therefore \CONTEXT\ supports both methods of referencing. In -%D \PDF\ version 1.1 named destinations arrived. Lack of -%D continuous support of version 1.1 viewers for \MSDOS\ -%D therefore sometimes forces us to prefer page references. As -%D a bonus, they are faster too and have no limitations. How -%D fortunate we were having both mechanisms available when the -%D version 3.0 (\PDF\ version 1.2) viewers proved to be too -%D bugged to support named destinations. - -\newif\ifusepagedestinations % not yet interfaced in mkiv - -%D \macros %D {jobsuffix} %D %D By default, \TEX\ produces \DVI\ files which can be diff --git a/tex/context/base/cont-new.tex b/tex/context/base/cont-new.tex index 19871222b..6ef0310ec 100644 --- a/tex/context/base/cont-new.tex +++ b/tex/context/base/cont-new.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2010.09.05 13:23} +\newcontextversion{2010.09.09 23:45} %D This file is loaded at runtime, thereby providing an %D excellent place for hacks, patches, extensions and new diff --git a/tex/context/base/context.tex b/tex/context/base/context.tex index c50e3fb7b..23afbe8c8 100644 --- a/tex/context/base/context.tex +++ b/tex/context/base/context.tex @@ -20,7 +20,7 @@ %D your styles an modules. \edef\contextformat {\jobname} -\edef\contextversion{2010.09.05 13:23} +\edef\contextversion{2010.09.09 23:45} %D For those who want to use this: diff --git a/tex/context/base/data-env.lua b/tex/context/base/data-env.lua index be596f3bf..19c10fde0 100644 --- a/tex/context/base/data-env.lua +++ b/tex/context/base/data-env.lua @@ -151,7 +151,7 @@ function resolvers.variableofformat(str) return formats[str] or formats[alternatives[str]] or '' end -function resolvers.vsriableofformatorsuffix(str) +function resolvers.variableofformatorsuffix(str) local v = formats[str] if v then return v diff --git a/tex/context/base/font-ldr.lua b/tex/context/base/font-ldr.lua new file mode 100644 index 000000000..ca4e3f98a --- /dev/null +++ b/tex/context/base/font-ldr.lua @@ -0,0 +1,70 @@ +if not modules then modules = { } end modules ['font-ldr'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- This module provides a replacement for fontloader.to_table +-- and will be loaded in due time. + +local fields = fontloader.fields + +if fields then + + local glyphfields + + local function get_glyphs(r) + local t = { } + local g = r.glyphs + for i=1,r.glyphmax-1 do + local gi = g[i] + if gi then + if not glyphfields then + glyphfields = fields(gi) + end + local h = { } + for i=1,#glyphfields do + local s = glyphfields[i] + h[s] = gi[s] + end + t[i] = h + end + end + return t + end + + local function to_table(r) + local f = fields(r) + if f then + local t = { } + for i=1,#f do + local fi = f[i] + local ri = r[fi] + if not ri then + -- skip + elseif fi == "glyphs" then + t.glyphs = get_glyphs(r) + elseif fi == "subfonts" then + t[fi] = ri + ri.glyphs = get_glyphs(ri) + else + t[fi] = r[fi] + end + end + return t + end + end + + -- currently glyphs, subfont-glyphs and the main table are userdata + + function fonts.to_table(raw) + return to_table(raw) + end + +else + + fonts.to_table = fontloader.to_table + +end diff --git a/tex/context/base/font-map.lua b/tex/context/base/font-map.lua index 5fa4170d7..128e80eb9 100644 --- a/tex/context/base/font-map.lua +++ b/tex/context/base/font-map.lua @@ -233,7 +233,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 diff --git a/tex/context/base/font-mis.lua b/tex/context/base/font-mis.lua index 645278837..2ae60e635 100644 --- a/tex/context/base/font-mis.lua +++ b/tex/context/base/font-mis.lua @@ -11,8 +11,7 @@ local lower, strip = string.lower, string.strip fonts.otf = fonts.otf or { } -fonts.otf.version = fonts.otf.version or 2.653 -fonts.otf.pack = true +fonts.otf.version = fonts.otf.version or 2.702 fonts.otf.cache = containers.define("fonts", "otf", fonts.otf.version, true) function fonts.otf.loadcached(filename,format,sub) diff --git a/tex/context/base/font-otb.lua b/tex/context/base/font-otb.lua index e4d694ea9..241845f24 100644 --- a/tex/context/base/font-otb.lua +++ b/tex/context/base/font-otb.lua @@ -276,7 +276,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 diff --git a/tex/context/base/font-otc.lua b/tex/context/base/font-otc.lua index 3280d8161..f9d749123 100644 --- a/tex/context/base/font-otc.lua +++ b/tex/context/base/font-otc.lua @@ -24,6 +24,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 = { { @@ -74,116 +80,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 @@ -195,6 +205,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' diff --git a/tex/context/base/font-otf.lua b/tex/context/base/font-otf.lua index 1da295eb0..46cc34070 100644 --- a/tex/context/base/font-otf.lua +++ b/tex/context/base/font-otf.lua @@ -6,6 +6,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 @@ -14,6 +19,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 @@ -27,42 +34,7 @@ local trace_defining = false trackers.register("fonts.defining", function(v local report_otf = logs.new("load otf") ---~ trackers.enable("otf.loading") - ---[[ldx-- -<p>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.</p> - -<itemize> -<item>we loop over all lookups</item> -<item>for each lookup we do a run over the list of glyphs</item> -<item>but we only process them for features that are enabled</item> -<item>if we're dealing with a contextual lookup, we loop over all contexts</item> -<item>in that loop we quit at a match and then process the list of sublookups</item> -<item>we always continue after the match</item> -</itemize> - -<p>In <l n='context'/> we do this for each font that is used in a list, so in -practice we have quite some nested loops.</p> - -<p>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).</p> - -<p>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.</p> ---ldx]]-- +local starttiming, stoptiming, elapsedtime = statistics.starttiming, statistics.stoptiming, statistics.elapsedtime local fonts = fonts @@ -72,38 +44,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-- <p>We start with a lot of tables and related functions.</p> --ldx]]-- --- we can have more local functions - -otf.tables.global_fields = allocate( table.tohash { +local global_fields = table.tohash { + "metadata", "lookups", "glyphs", "subfonts", @@ -114,20 +162,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", @@ -139,23 +187,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", @@ -172,59 +220,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-- <p>Here we go.</p> --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) -local adddimensions, showfeatureorder -- implemented later +function enhancers.patches.register(what,where,pattern,action) + local ww = what[where] + if ww then + ww[pattern] = action + else + ww = { [pattern] = action} + end +end + +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)) @@ -261,6 +361,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 @@ -284,11 +388,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 @@ -301,23 +408,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 @@ -327,13 +438,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 @@ -341,8 +466,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 @@ -350,89 +475,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 + 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 - if force and not d.name then - d.name = ".notdef" + 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 - 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 + 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] @@ -446,53 +803,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 @@ -505,8 +824,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 @@ -519,8 +840,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 @@ -545,148 +866,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 + 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 - 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 + subtables = t 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] + 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 - 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 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 = { } @@ -700,12 +970,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 @@ -718,6 +989,8 @@ enhancers["prepare unicode"] = function(data,filename) indices[unicode] = index unicodes[name] = unicode end + else + -- message that something is wrong end end end @@ -729,7 +1002,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 } @@ -754,9 +1027,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 @@ -764,26 +1037,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 +actions["prepare lookups"] = function(data,filename,raw) + local lookups = raw.lookups + if lookups then + data.lookups = lookups 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 - 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 @@ -791,7 +1052,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 @@ -799,35 +1060,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 @@ -837,7 +1081,6 @@ enhancers["check math"] = function(data,filename) end end math.kerns = mk - glyph.mathkern = nil end if hv then math.horiz_variants = hv.variants @@ -853,7 +1096,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] @@ -870,221 +1112,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] @@ -1098,10 +1167,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 @@ -1114,21 +1183,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 @@ -1193,7 +1255,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 @@ -1202,14 +1264,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? @@ -1232,32 +1290,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 - 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 + local ttftables = metadata.ttf_tables + if ttftables then + for i=1,#ttftables do + ttftables[i].data = "deleted" + end + end + metadata.xuid = nil + data.udglyphs = nil + data.map = nil end local private_math_parameters = { @@ -1265,7 +1348,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 @@ -1280,123 +1363,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 @@ -1404,22 +1465,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 @@ -1496,25 +1547,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) @@ -1622,7 +1654,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 @@ -1643,7 +1675,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 diff --git a/tex/context/base/font-otn.lua b/tex/context/base/font-otn.lua index 4109ca2d0..608e42fb3 100644 --- a/tex/context/base/font-otn.lua +++ b/tex/context/base/font-otn.lua @@ -2471,7 +2471,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] diff --git a/tex/context/base/font-otp.lua b/tex/context/base/font-otp.lua index 8df02e135..6453268bf 100644 --- a/tex/context/base/font-otp.lua +++ b/tex/context/base/font-otp.lua @@ -39,7 +39,7 @@ local function tabstr(t) -- hashed from core-uti / experiment return concat(s,",") end -function enhancers.pack(data) +local function packdata(data) if data then local h, t, c = { }, { }, { } local hh, tt, cc = { }, { }, { } @@ -140,7 +140,7 @@ function enhancers.pack(data) end end end - local m = v.mykerns + local m = v.kerns if m then for k,v in next, m do m[k] = pack(v) @@ -267,9 +267,9 @@ function enhancers.pack(data) for pass=1,2 do local pack = (pass == 1 and pack_1) or pack_2 for k, v in next, data.glyphs do - local m = v.mykerns + local m = v.kerns if m then - v.mykerns = pack(m) + v.kerns = pack(m) end local m = v.math if m then @@ -305,7 +305,7 @@ function enhancers.pack(data) end end -function enhancers.unpack(data) +local function unpackdata(data) if data then local t = data.tables if t then @@ -344,11 +344,11 @@ function enhancers.unpack(data) end end end - local m = v.mykerns + local m = v.kerns if m then local tm = t[m] if tm then - v.mykerns = tm + v.kerns = tm if unpacked[tm] then m = false else @@ -508,3 +508,6 @@ function enhancers.unpack(data) end end end + +otf.enhancers.register( "pack", packdata) +otf.enhancers.register("unpack",unpackdata) diff --git a/tex/context/base/font-pat.lua b/tex/context/base/font-pat.lua index 9f679f663..20e68489c 100644 --- a/tex/context/base/font-pat.lua +++ b/tex/context/base/font-pat.lua @@ -17,7 +17,7 @@ local report_otf = logs.new("load otf") -- older versions of latin modern didn't have the designsize set -- so for them we get it from the name -local patches = fonts.otf.enhancers.patches +local register = enhancers.patches.register local function patch(data,filename) if data.design_size == 0 then @@ -29,6 +29,13 @@ local function patch(data,filename) data.design_size = tonumber(ds) * 10 end end +end + +register("after","migrate metadata","^lmroman", patch) +register("after","migrate metadata","^lmsans", patch) +register("after","migrate metadata","^lmtypewriter",patch) + +local function patch(data,filename) local uni_to_ind = data.map.map if not uni_to_ind[0x391] then -- beware, this is a hack, features for latin often don't apply to greek @@ -50,21 +57,11 @@ local function patch(data,filename) uni_to_ind[0x3A7] = uni_to_ind[0x58] uni_to_ind[0x396] = uni_to_ind[0x5A] end - -- better make this into a feature - -- - -- local glyphs = data.glyphs - -- for i=0x300,0x36F do - -- local c = glyphs[uni_to_ind[i]] - -- if c and c.width == 0 then - -- local boundingbox = c.boundingbox - -- c.width = boundingbox[3] - boundingbox[1] - -- end - -- end end -patches["^lmroman"] = patch -patches["^lmsans"] = patch -patches["^lmtypewriter"] = patch +register("after","prepare glyphs","^lmroman", patch) +register("after","prepare glyphs","^lmsans", patch) +register("after","prepare glyphs","^lmtypewriter",patch) -- for some reason (either it's a bug in the font, or it's -- a problem in the library) the palatino arabic fonts don't @@ -81,12 +78,13 @@ local function patch(data,filename) end v.features = { { - scripts = { - { - langs = { "ARA ", "FAR ", "URD ", "dflt" }, - script = "arab", - }, - }, +--~ scripts = { +--~ { +--~ langs = { "ARA ", "FAR ", "URD ", "dflt" }, +--~ script = "arab", +--~ }, +--~ }, + scripts = { arab = { "ARA " = true, "FAR " = true, "URD " = true, "dflt" = true } }, tag = "mkmk" } } @@ -95,10 +93,10 @@ local function patch(data,filename) end end -patches["palatino.*arabic"] = patch +register("after","rehash features","palatino.*arabic",patch) local function patch_domh(data,filename,threshold) - local m = data.math + local m = data.metadata.math if m then local d = m.DisplayOperatorMinHeight or 0 if d < threshold then @@ -108,21 +106,8 @@ local function patch_domh(data,filename,threshold) m.DisplayOperatorMinHeight = threshold end end --- if tex.luatexversion < 48 then --- for _, g in next, data.glyphs do --- local name = g.name --- if find(name,"^integral$") or find(name,"^integral%.vsize") then --- local width, italic = g.width or 0, g.italic_correction or 0 --- local newwidth = width - italic --- if trace_loading then --- report_otf("patching width of %s: %s (width) - %s (italic) = %s",name,width,italic,newwidth) --- end --- g.width = newwidth --- end --- end --- end end -patches["cambria"] = function(data,filename) patch_domh(data,filename,2800) end -patches["cambmath"] = function(data,filename) patch_domh(data,filename,2800) end -patches["asana"] = function(data,filename) patch_domh(data,filename,1350) end +register("after","check math parameters","cambria", function(data,filename) patch_domh(data,filename,2800) end) +register("after","check math parameters","cambmath",function(data,filename) patch_domh(data,filename,2800) end) +register("after","check math parameters","asana", function(data,filename) patch_domh(data,filename,1350) end) diff --git a/tex/context/base/l-io.lua b/tex/context/base/l-io.lua index fe988b107..f9a663519 100644 --- a/tex/context/base/l-io.lua +++ b/tex/context/base/l-io.lua @@ -38,6 +38,7 @@ function io.savedata(filename,data,joiner) f:write(data or "") end f:close() + io.flush() return true else return false diff --git a/tex/context/base/l-table.lua b/tex/context/base/l-table.lua index 331bd9ca0..996e36a83 100644 --- a/tex/context/base/l-table.lua +++ b/tex/context/base/l-table.lua @@ -193,20 +193,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 @@ -214,6 +216,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 = {} @@ -660,6 +664,7 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) serialize(root,name,flush,reduce,noquotes,hexify) end f:close() + io.flush() end end diff --git a/tex/context/base/lpdf-ano.lua b/tex/context/base/lpdf-ano.lua index a56c8fdeb..5a98a6e81 100644 --- a/tex/context/base/lpdf-ano.lua +++ b/tex/context/base/lpdf-ano.lua @@ -245,9 +245,81 @@ function codeinjections.prerollreference(actions) -- share can become option end end -local lln = latelua_node() if not node.has_field(lln,'string') then - - function nodeinjections.reference(width,height,depth,prerolled) -- keep this one +--~ local lln = latelua_node() if not node.has_field(lln,'string') then + +--~ function nodeinjections.reference(width,height,depth,prerolled) -- keep this one +--~ if prerolled then +--~ if trace_references then +--~ report_references("w=%s, h=%s, d=%s, a=%s",width,height,depth,prerolled) +--~ end +--~ return pdfannotation_node(width,height,depth,prerolled) +--~ end +--~ end + +--~ function codeinjections.finishreference() +--~ end + +--~ else + +--~ report_references("hashing annotations") + +--~ local delayed = { } +--~ local hashed = { } +--~ local sharing = true -- we can do this for special refs (so we need an extra argument) + +--~ local function flush() +--~ local n = 0 +--~ for k,v in next, delayed do +--~ pdfimmediateobject(k,v) +--~ n = n + 1 +--~ end +--~ if trace_references then +--~ report_references("%s annotations flushed",n) +--~ end +--~ delayed = { } +--~ end + +--~ lpdf.registerpagefinalizer (flush,3,"annotations") -- somehow this lags behind .. I need to look into that some day +--~ lpdf.registerdocumentfinalizer(flush,3,"annotations") -- so we need a final flush too + +--~ local factor = number.dimenfactors.bp + +--~ function codeinjections.finishreference(width,height,depth,prerolled) +--~ local h, v = pdf.h, pdf.v +--~ local llx, lly = h*factor, (v - depth)*factor +--~ local urx, ury = (h + width)*factor, (v + height)*factor +--~ local annot = format("<< /Type /Annot %s /Rect [%s %s %s %s] >>",prerolled,llx,lly,urx,ury) +--~ local n = sharing and hashed[annot] +--~ if not n then +--~ n = pdfreserveobject() -- todo: share +--~ delayed[n] = annot +--~ --~ n = pdf.obj(annot) +--~ --~ pdf.refobj(n) +--~ if sharing then +--~ hashed[annot] = n +--~ end +--~ end +--~ pdfregisterannot(n) +--~ end + +--~ _bpnf_ = codeinjections.finishreference + +--~ function nodeinjections.reference(width,height,depth,prerolled) +--~ if prerolled then +--~ if trace_references then +--~ report_references("w=%s, h=%s, d=%s, a=%s",width,height,depth,prerolled) +--~ end +--~ -- local luacode = format("backends.pdf.codeinjections.finishreference(%s,%s,%s,'%s')",width,height,depth,prerolled) +--~ local luacode = format("_bpnf_(%s,%s,%s,'%s')",width,height,depth,prerolled) +--~ return latelua_node(luacode) +--~ end +--~ end + +--~ end node.free(lln) + +local function use_normal_annotations() + + local function reference(width,height,depth,prerolled) -- keep this one if prerolled then if trace_references then report_references("w=%s, h=%s, d=%s, a=%s",width,height,depth,prerolled) @@ -256,35 +328,35 @@ local lln = latelua_node() if not node.has_field(lln,'string') then end end - function codeinjections.finishreference() + local function finishreference() end -else + return reference, finishreference - report_references("hashing annotations") +end - local delayed = { } - local hashed = { } - local sharing = true -- we can do this for special refs (so we need an extra argument) +local delayed, hashed, sharing = { }, { }, true -- we can do this for special refs (so we need an extra argument) - local function flush() - local n = 0 - for k,v in next, delayed do - pdfimmediateobject(k,v) - n = n + 1 - end - if trace_references then - report_references("%s annotations flushed",n) - end - delayed = { } +local function flush() + local n = 0 + for k,v in next, delayed do + pdfimmediateobject(k,v) + n = n + 1 end + if trace_references then + report_references("%s annotations flushed",n) + end + delayed = { } +end + +lpdf.registerdocumentfinalizer(flush,3,"annotations") -- so we need a final flush too +lpdf.registerpagefinalizer (flush,3,"annotations") -- somehow this lags behind .. I need to look into that some day - lpdf.registerpagefinalizer (flush,3,"annotations") -- somehow this lags behind .. I need to look into that some day - lpdf.registerdocumentfinalizer(flush,3,"annotations") -- so we need a final flush too +local function use_shared_annotations() local factor = number.dimenfactors.bp - function codeinjections.finishreference(width,height,depth,prerolled) + local function finishreference(width,height,depth,prerolled) local h, v = pdf.h, pdf.v local llx, lly = h*factor, (v - depth)*factor local urx, ury = (h + width)*factor, (v + height)*factor @@ -302,9 +374,9 @@ else pdfregisterannot(n) end - _bpnf_ = codeinjections.finishreference + _bpnf_ = finishreference - function nodeinjections.reference(width,height,depth,prerolled) + local function reference(width,height,depth,prerolled) if prerolled then if trace_references then report_references("w=%s, h=%s, d=%s, a=%s",width,height,depth,prerolled) @@ -315,8 +387,33 @@ else end end + return reference, finishreference + +end + +local lln = latelua_node() if node.has_field(lln,'string') then + + directives.register("refences.sharelinks", function(v) + if v then + backends.nodeinjections.reference, backends.codeinjections.finishreference = use_shared_annotations() + else + backends.nodeinjections.reference, backends.codeinjections.finishreference = use_normal_annotations() + end + end) + + nodeinjections.reference, codeinjections.finishreference = use_shared_annotations() + +else + + nodeinjections.reference, codeinjections.finishreference = use_normal_annotations() + end node.free(lln) +-- -- -- -- +-- -- -- -- + + + function nodeinjections.destination(width,height,depth,name,view) if trace_destinations then report_destinations("w=%s, h=%s, d=%s, n=%s, v=%s",width,height,depth,name,view or "no view") diff --git a/tex/context/base/pack-box.mkiv b/tex/context/base/pack-box.mkiv index 2c25f5c3a..09af1d633 100644 --- a/tex/context/base/pack-box.mkiv +++ b/tex/context/base/pack-box.mkiv @@ -482,7 +482,7 @@ \if!!donea \advance\scratchdimen\dimexpr \MPx\currentbgposition-\MPx\currentpageposition\relax \else\if!!doneb - \scratchdimen\dimexpr\paperwidth-\MPx\currentbgposition-\MPx\currentpageposition\relax % not checked + \scratchdimen\dimexpr\paperwidth-\MPx\currentbgposition+\MPx\currentpageposition\relax % not checked \fi\fi \fi \advance\scratchdimen\@@bghoffset @@ -490,7 +490,7 @@ \scratchdimen\@@bgheight \ifdone \if!!donec - \scratchdimen\dimexpr\paperheight-\MPy\currentbgposition-\MPy\currentpageposition\relax % not checked + \scratchdimen\dimexpr\paperheight-\MPy\currentbgposition+\MPy\currentpageposition\relax % not checked \else\if!!doned \advance\scratchdimen\dimexpr \MPy\currentbgposition-\MPy\currentpageposition\relax % not checked \fi\fi diff --git a/tex/context/base/page-ini.mkiv b/tex/context/base/page-ini.mkiv index 3cc0778f9..66ab776b9 100644 --- a/tex/context/base/page-ini.mkiv +++ b/tex/context/base/page-ini.mkiv @@ -565,8 +565,7 @@ \fi \fi#1#2% \resetselectiepagina - \incrementpagenumber - \incrementsubpagenumber + \doincrementpageboundcounters % should hook into an every \checkpagedimensions \ifnum\outputpenalty>\superpenalty \else \dosupereject diff --git a/tex/context/base/scrn-nav.mkiv b/tex/context/base/scrn-nav.mkiv index bc27cd973..7b8fbdfa7 100644 --- a/tex/context/base/scrn-nav.mkiv +++ b/tex/context/base/scrn-nav.mkiv @@ -56,11 +56,11 @@ \appendtoks \doifelse\@@iastate\v!start {\iflocation\else - \showmessage\m!interactions2{\ifusepagedestinations\space(PAGE)\fi}% + \showmessage\m!interactions2{(page mode: \@@iapage)}% \global\locationtrue \fi}% {\iflocation - \showmessage\m!interactions3{\ifusepagedestinations\space(PAGE)\fi}% + \showmessage\m!interactions3{(page mode: \@@iapage)}% \global\locationfalse \fi}% \iflocation @@ -73,9 +73,12 @@ \doifelse\@@iastrut \v!yes \settrue \setfalse \uselocationstrut \doifelse\@@iaclick \v!yes \settrue \setfalse \highlighthyperlinks \doifelse\@@iadisplay\v!new \settrue \setfalse \gotonewwindow - \doifelse\@@iapage \v!yes \settrue \setfalse \usepagedestinations + \doifnot \@@iapage \v!no \dosetpagedestinations \to \everysetupinteraction +\def\dosetpagedestinations + {\ctxlua{structures.references.setinnermethod("\@@iapage")}} + \def\synchronizebackendidentity {\ctxlua{backends.codeinjections.setupidentity{ title = \!!bs\@@iatitle\!!es, diff --git a/tex/context/base/scrp-cjk.lua b/tex/context/base/scrp-cjk.lua index 7c1310528..44212a098 100644 --- a/tex/context/base/scrp-cjk.lua +++ b/tex/context/base/scrp-cjk.lua @@ -553,7 +553,8 @@ local function process(head,first,last) if not pcjk or not ncjk or pcjk == "korean" or ncjk == "korean" or pcjk == "other" or ncjk == "other" - or pcjk == "jamo_final" or ncjk == "jamo_initial" then + -- or pcjk == "jamo_final" or ncjk == "jamo_initial" then + or pcjk == "jamo_final" or ncjk == "jamo_initial" or pcjk == "half_width_close" or ncjk == "half_width_open" then previous = "start" else -- if head ~= first then remove_node(head,first,true) diff --git a/tex/context/base/strc-des.mkiv b/tex/context/base/strc-des.mkiv index 9929cc1c5..5f25ac60d 100644 --- a/tex/context/base/strc-des.mkiv +++ b/tex/context/base/strc-des.mkiv @@ -602,7 +602,7 @@ \def\dosetupenumerationcounter#1% {\edef\currentenumerationcountername{#1}% only used in the token list - \edef\currentdiscription{#1}% + \edef\currentdescription{#1}% \the\everysetupenumerationcounter} \appendtoks diff --git a/tex/context/base/strc-num.lua b/tex/context/base/strc-num.lua index 1b26a4a7a..f42b11b32 100644 --- a/tex/context/base/strc-num.lua +++ b/tex/context/base/strc-num.lua @@ -161,9 +161,17 @@ local function savevalue(name,i) local cd = counterdata[name].data[i] local cs = tobesaved[name][i] local cc = collected[name] + if trace_counters then + report_counters("saving value %s of counter named %s",cd.number,name) + end local cr = cd.range local old = (cc and cc[i] and cc[i][cr]) or 0 - cs[cr] = cd.number + local number = cd.number + if cd.method == variables.page then + -- we can be one page ahead + number = number - 1 + end + cs[cr] = (number >= 0) and number or 0 cd.range = cr + 1 return old else @@ -171,11 +179,12 @@ local function savevalue(name,i) end end -function counters.define(name, start, counter) -- todo: step +function counters.define(name, start, counter, method) -- todo: step local d = allocate(name,1) d.start = start if counter ~= "" then d.counter = counter -- only for special purposes, cannot be false + d.method = method -- frozen at define time end end diff --git a/tex/context/base/strc-num.mkiv b/tex/context/base/strc-num.mkiv index f675e4ac3..d2ed5dad1 100644 --- a/tex/context/base/strc-num.mkiv +++ b/tex/context/base/strc-num.mkiv @@ -108,7 +108,10 @@ \def\dododefinestructurecounter[#1][#2]% {\getparameters[\??nn#1][\s!counter=,#2]% counter is for internal purposes - \ctxlua{structures.counters.define("#1",tonumber("\structurecounterparameter{#1}\c!start") or 0,"\structurecounterparameter{#1}\s!counter")}% + \ctxlua{structures.counters.define("#1", + tonumber("\structurecounterparameter{#1}\c!start") or 0, + "\structurecounterparameter{#1}\s!counter", + "\structurecounterparameter{#1}\c!method")}% \docheckstructurecountersetup{#1}} \def\donodefinestructurecounter[#1][#2]% inherit diff --git a/tex/context/base/strc-pag.mkiv b/tex/context/base/strc-pag.mkiv index 2d6a6aaf1..8d18b9e56 100644 --- a/tex/context/base/strc-pag.mkiv +++ b/tex/context/base/strc-pag.mkiv @@ -71,9 +71,9 @@ % \stopbodymatter % \stoptext -\definestructurecounter[\s!realpage][\c!prefix=\v!no,\c!start=1,\c!prefixsegments=,\s!counter=realpageno] -\definestructurecounter[\s!userpage][\c!prefix=\v!no,\c!start=1,\c!prefixsegments=,\s!counter=userpageno] -\definestructurecounter[\s!subpage] [\c!prefix=\v!no,\c!start=1,\c!prefixsegments=,\s!counter=subpageno] +\definestructurecounter[\s!realpage][\c!prefix=\v!no,\c!start=1,\c!prefixsegments=,\s!counter=realpageno,\c!method=\v!page] +\definestructurecounter[\s!userpage][\c!prefix=\v!no,\c!start=1,\c!prefixsegments=,\s!counter=userpageno,\c!method=\v!page] +\definestructurecounter[\s!subpage] [\c!prefix=\v!no,\c!start=1,\c!prefixsegments=,\s!counter=subpageno, \c!method=\v!page] \newtoks\everysetuprealpagenumber % todo: set state: none, start, stop, reset \newtoks\everysetupuserpagenumber % todo: set state: none, start, stop, reset @@ -221,11 +221,18 @@ \def\nextuserpagenumber {\directconvertedstructurecounter\s!userpage\v!next} \def\nextsubpagenumber {\directconvertedstructurecounter\s!subpage \v!next} -\appendtoks - \decrementstructurecounter[\s!realpage]% +\def\dodeincrementpageboundcounters % only at the end + {\decrementstructurecounter[\s!realpage]% \decrementstructurecounter[\s!userpage]% - \decrementstructurecounter[\s!subpage ]% -\to\everygoodbye + \decrementstructurecounter[\s!subpage ]} + +\def\doincrementpageboundcounters + {\incrementpagenumber + \incrementsubpagenumber} + +\appendtoks + \dodeincrementpageboundcounters +\to \everygoodbye % Equivalents (compatibility): % diff --git a/tex/context/base/strc-ref.lua b/tex/context/base/strc-ref.lua index 1d7a94c44..67436e0e3 100644 --- a/tex/context/base/strc-ref.lua +++ b/tex/context/base/strc-ref.lua @@ -957,7 +957,13 @@ end local innermethod = "names" function references.setinnermethod(m) - innermethod = m -- page names mixed + if m then + if m == "page" or m == "mixed" or m == "names" then + innermethod = m + elseif m == true or m == variables.yes then + innermethod = "page" + end + end function references.setinnermethod() report_references("inner method is already set and frozen to '%s'",innermethod) end @@ -967,6 +973,10 @@ function references.getinnermethod() return innermethod or "names" end +directives.register("references.linkmethod", function(v) -- page mixed names + references.setinnermethod(v) +end) + function references.setinternalreference(prefix,tag,internal,view) if innermethod == "page" then return unsetvalue diff --git a/tex/context/base/strc-tag.lua b/tex/context/base/strc-tag.lua index 7701cc57e..d6dcab81f 100644 --- a/tex/context/base/strc-tag.lua +++ b/tex/context/base/strc-tag.lua @@ -55,9 +55,9 @@ local properties = allocate { itemtag = { pdf = "Lbl", nature = "mixed" }, itemcontent = { pdf = "LBody", nature = "mixed" }, - description = { pdf = "Li", nature = "display" }, - descriptiontag = { pdf = "Lbl", nature = "mixed" }, - descriptioncontent = { pdf = "LBody", nature = "mixed" }, + description = { pdf = "Div", nature = "display" }, + descriptiontag = { pdf = "Div", nature = "mixed" }, + descriptioncontent = { pdf = "Div", nature = "mixed" }, descriptionsymbol = { pdf = "Span", nature = "inline" }, -- note reference verbatimblock = { pdf = "Code", nature = "display" }, @@ -91,7 +91,7 @@ local properties = allocate { delimited = { pdf = "Quote", nature = "inline" }, subsentence = { pdf = "Span", nature = "inline" }, - float = { pdf = "Div", nature = "display" }, + float = { pdf = "Div", nature = "display" }, -- Figure floatcaption = { pdf = "Caption", nature = "display" }, floattag = { pdf = "Span", nature = "mixed" }, floattext = { pdf = "Span", nature = "mixed" }, @@ -101,7 +101,7 @@ local properties = allocate { mpgraphic = { pdf = "P", nature = "mixed" }, formulaset = { pdf = "Div", nature = "display" }, - formula = { pdf = "Div", nature = "display" }, + formula = { pdf = "Div", nature = "display" }, -- Formula formulatag = { pdf = "Span", nature = "mixed" }, formulacontent = { pdf = "P", nature = "display" }, subformula = { pdf = "Div", nature = "display" }, diff --git a/tex/context/base/syst-ini.tex b/tex/context/base/syst-ini.tex index d4b024c30..c4b326d96 100644 --- a/tex/context/base/syst-ini.tex +++ b/tex/context/base/syst-ini.tex @@ -235,13 +235,13 @@ \let\newfam\newfamily -% watch out, for the moment we disable the check for already being defined -% later we will revert this but first all chardefs must be replaced +% Watch out, for the moment we disable the check for already being defined +% later we will revert this but first all chardefs must be replaced. \def\newconstant #1{\ifdefined#1\let#1\undefined\fi\newcount#1} \def\setnewconstant#1{\ifdefined#1\let#1\undefined\fi\newcount#1#1} % just a number -% matbe setconstant with check +% maybe setconstant with check % %D The next definitions are really needed (in \CONTEXT): diff --git a/tex/context/base/util-pck.lua b/tex/context/base/util-pck.lua index 835c70800..585123b5c 100644 --- a/tex/context/base/util-pck.lua +++ b/tex/context/base/util-pck.lua @@ -29,6 +29,18 @@ local function hashed(t) return concat(s,",") end +local function simplehashed(t) + local s = { } + for k, v in next, t do + s[#s+1] = k.."="..v + end + sort(s) + return concat(s,",") +end + +packers.hashed = hashed +packers.simplehashed = simplehashed + local function pack(t,keys,hash,index) for k,v in next, t do if type(v) == "table" then 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-- -<p>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.</p> - -<itemize> -<item>we loop over all lookups</item> -<item>for each lookup we do a run over the list of glyphs</item> -<item>but we only process them for features that are enabled</item> -<item>if we're dealing with a contextual lookup, we loop over all contexts</item> -<item>in that loop we quit at a match and then process the list of sublookups</item> -<item>we always continue after the match</item> -</itemize> - -<p>In <l n='context'/> we do this for each font that is used in a list, so in -practice we have quite some nested loops.</p> - -<p>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).</p> - -<p>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.</p> ---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-- <p>We start with a lot of tables and related functions.</p> --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-- <p>Here we go.</p> --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' diff --git a/web2c/contextcnf.lua b/web2c/contextcnf.lua index 03a7d4b51..7d2edb6fd 100644 --- a/web2c/contextcnf.lua +++ b/web2c/contextcnf.lua @@ -107,10 +107,12 @@ return { -- like ['foo.bar'] so for convenience we also support 'foo_bar'. directives = { - -- system_checkglobals = "10", - -- system.nostatistics = "yes", - system_errorcontext = "10", - mplib_texerrors = "yes", + -- system_checkglobals = "10", + -- system_nostatistics = "yes", + system_errorcontext = "10", + mplib_texerrors = "yes", + -- fonts_otf_loader_method = "table", -- table mixed sparse + -- fonts_otf_loader_cleanup = "0", -- 0 1 2 3 }, experiments = { |