diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/fontloader/misc/fontloader-font-afm.lua | 231 | ||||
-rw-r--r-- | src/fontloader/misc/fontloader-font-con.lua | 11 | ||||
-rw-r--r-- | src/fontloader/misc/fontloader-font-dsp.lua | 2 | ||||
-rw-r--r-- | src/fontloader/misc/fontloader-font-map.lua | 4 | ||||
-rw-r--r-- | src/fontloader/misc/fontloader-font-otj.lua | 6 | ||||
-rw-r--r-- | src/fontloader/misc/fontloader-font-otl.lua | 2 | ||||
-rw-r--r-- | src/fontloader/misc/fontloader-font-otr.lua | 80 | ||||
-rw-r--r-- | src/fontloader/misc/fontloader-font-ots.lua | 50 | ||||
-rw-r--r-- | src/fontloader/misc/fontloader-font-oup.lua | 28 | ||||
-rw-r--r-- | src/fontloader/misc/fontloader-font-tfm.lua | 2 | ||||
-rw-r--r-- | src/fontloader/runtime/fontloader-basics-gen.lua | 6 | ||||
-rw-r--r-- | src/fontloader/runtime/fontloader-reference.lua | 327 | ||||
-rw-r--r-- | src/luaotfload-auxiliary.lua | 265 | ||||
-rw-r--r-- | src/luaotfload-configuration.lua | 2 | ||||
-rw-r--r-- | src/luaotfload-database.lua | 201 | ||||
-rw-r--r-- | src/luaotfload-loaders.lua | 14 | ||||
-rw-r--r-- | src/luaotfload-main.lua | 26 | ||||
-rw-r--r-- | src/luaotfload-parsers.lua | 4 | ||||
-rw-r--r-- | src/luaotfload-resolvers.lua | 12 |
19 files changed, 931 insertions, 342 deletions
diff --git a/src/fontloader/misc/fontloader-font-afm.lua b/src/fontloader/misc/fontloader-font-afm.lua index 329639b..99b8577 100644 --- a/src/fontloader/misc/fontloader-font-afm.lua +++ b/src/fontloader/misc/fontloader-font-afm.lua @@ -28,9 +28,11 @@ don't have this issue.</p> local fonts, logs, trackers, containers, resolvers = fonts, logs, trackers, containers, resolvers local next, type, tonumber = next, type, tonumber -local format, match, gmatch, lower, gsub, strip = string.format, string.match, string.gmatch, string.lower, string.gsub, string.strip +local match, gmatch, lower, gsub, strip, find = string.match, string.gmatch, string.lower, string.gsub, string.strip, string.find +local char, byte, sub = string.char, string.byte, string.sub local abs = math.abs -local P, S, C, R, lpegmatch, patterns = lpeg.P, lpeg.S, lpeg.C, lpeg.R, lpeg.match, lpeg.patterns +local bxor, rshift = bit32.bxor, bit32.rshift +local P, S, R, Cmt, C, Ct, Cs, lpegmatch, patterns = lpeg.P, lpeg.S, lpeg.R, lpeg.Cmt, lpeg.C, lpeg.Ct, lpeg.Cs, lpeg.match, lpeg.patterns local derivetable = table.derive local trace_features = false trackers.register("afm.features", function(v) trace_features = v end) @@ -48,18 +50,13 @@ local definers = fonts.definers local readers = fonts.readers local constructors = fonts.constructors -local fontloader = fontloader -local font_to_table = fontloader.to_table -local open_font = fontloader.open -local close_font = fontloader.close - local afm = constructors.newhandler("afm") local pfb = constructors.newhandler("pfb") local afmfeatures = constructors.newfeatures("afm") local registerafmfeature = afmfeatures.register -afm.version = 1.500 -- incrementing this number one up will force a re-cache +afm.version = 1.501 -- incrementing this number one up will force a re-cache afm.cache = containers.define("fonts", "afm", afm.version, true) afm.autoprefixed = true -- this will become false some day (catches texnansi-blabla.*) @@ -171,7 +168,7 @@ end local function get_charmetrics(data,charmetrics,vector) local characters = data.characters local chr, ind = { }, 0 - for k,v in gmatch(charmetrics,"([%a]+) +(.-) *;") do + for k, v in gmatch(charmetrics,"([%a]+) +(.-) *;") do if k == 'C' then v = tonumber(v) if v < 0 then @@ -225,41 +222,193 @@ local function get_variables(data,fontmetrics) end end -local function get_indexes(data,pfbname) - data.resources.filename = resolvers.unresolve(pfbname) -- no shortcut - local pfbblob = open_font(pfbname) - if pfbblob then - local characters = data.characters - local pfbdata = font_to_table(pfbblob) - if pfbdata then - local glyphs = pfbdata.glyphs - if glyphs then - if trace_loading then - report_afm("getting index data from %a",pfbname) - end - for index, glyph in next, glyphs do - -- for index, glyph in table.sortedhash(glyphs) do - local name = glyph.name - if name then - local char = characters[name] - if char then - if trace_indexing then - report_afm("glyph %a has index %a",name,index) +local get_indexes + +do + + -- old font loader + + local fontloader = fontloader + + if fontloader then + + local font_to_table = fontloader.to_table + local open_font = fontloader.open + local close_font = fontloader.close + + local function get_indexes_old(data,pfbname) + local pfbblob = open_font(pfbname) + if pfbblob then + local characters = data.characters + local pfbdata = font_to_table(pfbblob) + if pfbdata then + local glyphs = pfbdata.glyphs + if glyphs then + if trace_loading then + report_afm("getting index data from %a",pfbname) + end + for index, glyph in next, glyphs do + local name = glyph.name + if name then + local char = characters[name] + if char then + if trace_indexing then + report_afm("glyph %a has index %a",name,index) + end + char.index = index + end end - char.index = index end + elseif trace_loading then + report_afm("no glyph data in pfb file %a",pfbname) end + elseif trace_loading then + report_afm("no data in pfb file %a",pfbname) end + close_font(pfbblob) elseif trace_loading then - report_afm("no glyph data in pfb file %a",pfbname) + report_afm("invalid pfb file %a",pfbname) end - elseif trace_loading then - report_afm("no data in pfb file %a",pfbname) end - close_font(pfbblob) - elseif trace_loading then - report_afm("invalid pfb file %a",pfbname) + end + + -- new (unfinished) font loader but i see no differences between + -- old and new (one bad vector with old) + + local n, m + + local progress = function(str,position,name,size) + local forward = position + tonumber(size) + 3 + 2 + n = n + 1 + if n >= m then + return #str, name + elseif forward < #str then + return forward, name + else + return #str, name + end + end + + local initialize = function(str,position,size) + n = 0 + m = tonumber(size) + return position + 1 + end + + local charstrings = P("/CharStrings") + local name = P("/") * C((R("az")+R("AZ")+R("09")+S("-_."))^1) + local size = C(R("09")^1) + local spaces = P(" ")^1 + + local p_filternames = Ct ( + (1-charstrings)^0 * charstrings * spaces * Cmt(size,initialize) + * (Cmt(name * P(" ")^1 * C(R("09")^1), progress) + P(1))^1 + ) + + -- if one of first 4 not 0-9A-F then binary else hex + + local decrypt + + do + + local r, c1, c2, n = 0, 0, 0, 0 + + local function step(c) + local cipher = byte(c) + local plain = bxor(cipher,rshift(r,8)) + r = ((cipher + r) * c1 + c2) % 65536 + return char(plain) + end + + decrypt = function(binary) + r, c1, c2, n = 55665, 52845, 22719, 4 + binary = gsub(binary,".",step) + return sub(binary,n+1) + end + + -- local pattern = Cs((P(1) / step)^1) + -- + -- decrypt = function(binary) + -- r, c1, c2, n = 55665, 52845, 22719, 4 + -- binary = lpegmatch(pattern,binary) + -- return sub(binary,n+1) + -- end + + end + + local function loadpfbvector(filename) + -- for the moment limited to encoding only + + local data = io.loaddata(resolvers.findfile(filename)) + + if not find(data,"!PS%-AdobeFont%-") then + print("no font",filename) + return + end + + if not data then + print("no data",filename) + return + end + + local ascii, binary = match(data,"(.*)eexec%s+......(.*)") + + if not binary then + print("no binary",filename) + return + end + + binary = decrypt(binary,4) + + local vector = lpegmatch(p_filternames,binary) + + vector[0] = table.remove(vector,1) + + if not vector then + print("no vector",filename) + return + end + + return vector + + end + + get_indexes = function(data,pfbname) + local vector = loadpfbvector(pfbname) + if vector then + local characters = data.characters + if trace_loading then + report_afm("getting index data from %a",pfbname) + end + for index=1,#vector do + local name = vector[index] + local char = characters[name] + if char then + if trace_indexing then + report_afm("glyph %a has index %a",name,index) + end + char.index = index + end + end + end + end + + if fontloader then + + afm.use_new_indexer = true + get_indexes_new = get_indexes + + get_indexes = function(data,pfbname) + if afm.use_new_indexer then + return get_indexes_new(data,pfbname) + else + return get_indexes_old(data,pfbname) + end + end + + end + end local function readafm(filename) @@ -351,8 +500,9 @@ function afm.load(filename) data = readafm(filename) if data then if pfbname ~= "" then + data.resources.filename = resolvers.unresolve(pfbname) get_indexes(data,pfbname) - elseif trace_loading then + elseif trace_loading then report_afm("no pfb file for %a",filename) -- data.resources.filename = "unset" -- better than loading the afm file end @@ -397,9 +547,10 @@ local uparser = fonts.mappings.makenameparser() unify = function(data, filename) local unicodevector = fonts.encodings.agl.unicodes -- loaded runtime in context - local unicodes, names = { }, { } - local private = constructors.privateoffset - local descriptions = data.descriptions + local unicodes = { } + local names = { } + local private = constructors.privateoffset + local descriptions = data.descriptions for name, blob in next, data.characters do local code = unicodevector[name] -- or characters.name_to_unicode[name] if not code then diff --git a/src/fontloader/misc/fontloader-font-con.lua b/src/fontloader/misc/fontloader-font-con.lua index f36f750..b118535 100644 --- a/src/fontloader/misc/fontloader-font-con.lua +++ b/src/fontloader/misc/fontloader-font-con.lua @@ -776,9 +776,14 @@ function constructors.scale(tfmdata,specification) elseif autoitalicamount then -- itlc feature local vi = description.italic if not vi then - local vi = description.boundingbox[3] - description.width + autoitalicamount - if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic - chr.italic = vi*hdelta + local bb = description.boundingbox + if bb then + local vi = bb[3] - description.width + autoitalicamount + if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic + chr.italic = vi*hdelta + end + else + -- report_defining("no boundingbox for character %C in font %a, fullname %a, filename %a",unicode,name,fullname,filename) end elseif vi ~= 0 then chr.italic = vi*hdelta diff --git a/src/fontloader/misc/fontloader-font-dsp.lua b/src/fontloader/misc/fontloader-font-dsp.lua index 14e816d..85a80bd 100644 --- a/src/fontloader/misc/fontloader-font-dsp.lua +++ b/src/fontloader/misc/fontloader-font-dsp.lua @@ -525,7 +525,7 @@ local function chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nof end end local noflookups = readushort(f) - local lookups = { } + local lookups = { } for i=1,noflookups do lookups[readushort(f)+1] = readushort(f) + 1 end diff --git a/src/fontloader/misc/fontloader-font-map.lua b/src/fontloader/misc/fontloader-font-map.lua index db501f0..509e751 100644 --- a/src/fontloader/misc/fontloader-font-map.lua +++ b/src/fontloader/misc/fontloader-font-map.lua @@ -19,6 +19,8 @@ local trace_mapping = false trackers.register("fonts.mapping", function(v) trac local report_fonts = logs.reporter("fonts","loading") -- not otf only +local force_ligatures = false directives.register("fonts.mapping.forceligatures",function(v) force_ligatures = v end) + local fonts = fonts or { } local mappings = fonts.mappings or { } fonts.mappings = mappings @@ -443,7 +445,7 @@ function mappings.addtounicode(data,filename,checklookups) local collected = false local unicoded = 0 for unicode, glyph in next, descriptions do - if not glyph.unicode and glyph.class == "ligature" then + if glyph.class == "ligature" and (force_ligatures or not glyph.unicode) then if not collected then collected = fonts.handlers.otf.readers.getcomponents(data) if not collected then diff --git a/src/fontloader/misc/fontloader-font-otj.lua b/src/fontloader/misc/fontloader-font-otj.lua index aae70d1..ebda723 100644 --- a/src/fontloader/misc/fontloader-font-otj.lua +++ b/src/fontloader/misc/fontloader-font-otj.lua @@ -49,8 +49,8 @@ local attributes, nodes, node = attributes, nodes, node fonts = fonts local hashes = fonts.hashes local fontdata = hashes.identifiers -local parameters = fonts.hashes.parameters -local resources = fonts.hashes.resources +----- parameters = fonts.hashes.parameters -- not in generic +----- resources = fonts.hashes.resources -- not in generic nodes.injections = nodes.injections or { } local injections = nodes.injections @@ -1444,7 +1444,7 @@ local function injectspaces(head) -- end leftkerns = trig.left rightkerns = trig.right - local par = parameters[font] + local par = fontdata[font].parameters -- fallback for generic factor = par.factor threshold = par.spacing.width - 1 -- get rid of rounding errors lastfont = font diff --git a/src/fontloader/misc/fontloader-font-otl.lua b/src/fontloader/misc/fontloader-font-otl.lua index 210abfe..bcea275 100644 --- a/src/fontloader/misc/fontloader-font-otl.lua +++ b/src/fontloader/misc/fontloader-font-otl.lua @@ -53,7 +53,7 @@ local report_otf = logs.reporter("fonts","otf loading") local fonts = fonts local otf = fonts.handlers.otf -otf.version = 3.017 -- beware: also sync font-mis.lua and in mtx-fonts +otf.version = 3.018 -- beware: also sync font-mis.lua and in mtx-fonts otf.cache = containers.define("fonts", "otl", otf.version, true) local otfreaders = otf.readers diff --git a/src/fontloader/misc/fontloader-font-otr.lua b/src/fontloader/misc/fontloader-font-otr.lua index 3542d18..24f6854 100644 --- a/src/fontloader/misc/fontloader-font-otr.lua +++ b/src/fontloader/misc/fontloader-font-otr.lua @@ -60,16 +60,16 @@ if not modules then modules = { } end modules ['font-otr'] = { -- faster but it might not be the real bottleneck as we still need to juggle data. It -- is probably more memory efficient as no intermediate strings are involved. -if not characters then - require("char-def") - require("char-ini") -end +-- if not characters then +-- require("char-def") +-- require("char-ini") +-- end local next, type, unpack = next, type, unpack local byte, lower, char, strip, gsub = string.byte, string.lower, string.char, string.strip, string.gsub local bittest = bit32.btest -local concat, remove, unpack = table.concat, table.remov, table.unpack -local floor, mod, abs, sqrt, round = math.floor, math.mod, math.abs, math.sqrt, math.round +local concat, remove, unpack, fastcopy = table.concat, table.remov, table.unpack, table.fastcopy +local floor, abs, sqrt, round = math.floor, math.abs, math.sqrt, math.round local P, R, S, C, Cs, Cc, Ct, Carg, Cmt = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.Carg, lpeg.Cmt local lpegmatch = lpeg.match @@ -1066,14 +1066,13 @@ readers.hmtx = function(f,fontdata,specification) local nofmetrics = fontdata.horizontalheader.nofhmetrics local glyphs = fontdata.glyphs local nofglyphs = fontdata.nofglyphs - local nofrepeated = nofglyphs - nofmetrics local width = 0 -- advance local leftsidebearing = 0 for i=0,nofmetrics-1 do local glyph = glyphs[i] width = readshort(f) leftsidebearing = readshort(f) - if advance ~= 0 then + if width ~= 0 then glyph.width = width end -- if leftsidebearing ~= 0 then @@ -1082,8 +1081,8 @@ readers.hmtx = function(f,fontdata,specification) end -- The next can happen in for instance a monospace font or in a cjk font -- with fixed widths. - for i=nofmetrics,nofrepeated do - local glyph = glyphs[i] + for i=nofmetrics,nofglyphs-1 do + local glyph = glyphs[i] if width ~= 0 then glyph.width = width end @@ -1187,6 +1186,8 @@ local sequence = { { 3, 0, 6 }, -- variants { 0, 5, 14 }, + -- last resort ranges + { 3, 10, 13 }, } -- local sequence = { @@ -1269,10 +1270,10 @@ formatreaders[4] = function(f,fontdata,offset) -- bad encoding elseif offset == 0 then if trace_cmap then - report("format 4.%i segment %2i from %C upto %C at index %H",1,segment,startchar,endchar,mod(startchar + delta,65536)) + report("format 4.%i segment %2i from %C upto %C at index %H",1,segment,startchar,endchar,(startchar + delta) % 65536) end for unicode=startchar,endchar do - local index = mod(unicode + delta,65536) + local index = (unicode + delta) % 65536 if index and index > 0 then local glyph = glyphs[index] if glyph then @@ -1302,13 +1303,13 @@ formatreaders[4] = function(f,fontdata,offset) else local shift = (segment-nofsegments+offset/2) - startchar if trace_cmap then - report("format 4.%i segment %2i from %C upto %C at index %H",0,segment,startchar,endchar,mod(startchar + delta,65536)) + report("format 4.%i segment %2i from %C upto %C at index %H",0,segment,startchar,endchar,(startchar + delta) % 65536) end for unicode=startchar,endchar do local slot = shift + unicode local index = indices[slot] if index and index > 0 then - index = mod(index + delta,65536) + index = (index + delta) % 65536 local glyph = glyphs[index] if glyph then local gu = glyph.unicode @@ -1389,7 +1390,7 @@ formatreaders[12] = function(f,fontdata,offset) local last = readulong(f) local index = readulong(f) if trace_cmap then - report("format 12 from %C to %C",first,last) + report("format 12 from %C to %C starts at index %i",first,last,index) end for unicode=first,last do local glyph = glyphs[index] @@ -1417,6 +1418,53 @@ formatreaders[12] = function(f,fontdata,offset) return nofdone end +formatreaders[13] = function(f,fontdata,offset) + -- + -- this fector is only used for simple fallback fonts + -- + setposition(f,offset+2+2+4+4) -- skip format reserved length language + local mapping = fontdata.mapping + local glyphs = fontdata.glyphs + local duplicates = fontdata.duplicates + local nofgroups = readulong(f) + local nofdone = 0 + for i=1,nofgroups do + local first = readulong(f) + local last = readulong(f) + local index = readulong(f) + if first < privateoffset then + if trace_cmap then + report("format 13 from %C to %C get index %i",first,last,index) + end + local glyph = glyphs[index] + local unicode = glyph.unicode + if not unicode then + unicode = first + glyph.unicode = unicode + first = first + 1 + end + local list = duplicates[unicode] + mapping[index] = unicode + if not list then + list = { } + duplicates[unicode] = list + end + if last >= privateoffset then + local limit = privateoffset - 1 + report("format 13 from %C to %C pruned to %C",first,last,limit) + last = limit + end + for unicode=first,last do + list[unicode] = true + end + nofdone = nofdone + last - first + 1 + else + report("format 13 from %C to %C ignored",first,last) + end + end + return nofdone +end + formatreaders[14] = function(f,fontdata,offset) if offset and offset ~= 0 then setposition(f,offset) @@ -2226,7 +2274,7 @@ function readers.extend(fontdata) end end --- +-- for now .. this will move to a context specific file if fonts.hashes then diff --git a/src/fontloader/misc/fontloader-font-ots.lua b/src/fontloader/misc/fontloader-font-ots.lua index 6911872..90fcde2 100644 --- a/src/fontloader/misc/fontloader-font-ots.lua +++ b/src/fontloader/misc/fontloader-font-ots.lua @@ -9,6 +9,9 @@ if not modules then modules = { } end modules ['font-ots'] = { -- sequences -- to be checked: discrun doesn't seem to do something useful now (except run the -- check again) so if we need it again we'll do a zwnjrun or so +-- components will go away and be replaced by a property table which simplifies +-- code (also more efficient) + -- beware, on my development machine we test a slightly a more optimized version -- assumptions: @@ -566,7 +569,22 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou local pre, post, replace, pretail, posttail, replacetail = getdisc(discfound,true) if not replace then -- todo: signal simple hyphen local prev = getprev(base) - local copied = copy_node_list(comp) +-- local copied = copy_node_list(comp) +local current = comp +local previous = nil +local copied = nil +while current do + if getid(current) == glyph_code then + local n = copy_node(current) + if copied then + setlink(previous,n) + else + copied = n + end + previous = n + end + current = getnext(current) +end setprev(discnext,nil) -- also blocks funny assignments setnext(discprev,nil) -- also blocks funny assignments if pre then @@ -2126,7 +2144,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) else local discfound = nil local n = f + 1 - last = getnext(last) + last = getnext(last) -- the second in current (first already matched) while n <= l do if not last and (sweeptype == "post" or sweeptype == "replace") then last = getnext(sweepnode) @@ -2167,7 +2185,6 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) end break end - last = getnext(last) elseif char == false then if discfound then notmatchreplace[discfound] = true @@ -2220,6 +2237,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) end match = not notmatchpre[last] end + -- maybe only if match last = getnext(last) else match = false @@ -2255,8 +2273,12 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) if trace_skips then show_skip(dataset,sequence,char,ck,class) end + prev = getprev(prev) -- moved here elseif seq[n][char] then - n = n -1 + if n > 1 then -- new test + prev = getprev(prev) -- moved here + end + n = n - 1 else if discfound then notmatchreplace[discfound] = true @@ -2275,7 +2297,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) end break end - prev = getprev(prev) + -- prev = getprev(prev) -- moved up elseif char == false then if discfound then notmatchreplace[discfound] = true @@ -2339,21 +2361,20 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) if not match then break end - else - -- skip 'm end - else - -- skip 'm end + -- maybe only if match + prev = getprev(prev) elseif seq[n][32] then n = n - 1 + prev = getprev(prev) else match = false break end - prev = getprev(prev) elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces n = n - 1 + prev = getprev(prev) -- was absent else match = false break @@ -2391,7 +2412,11 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) if trace_skips then show_skip(dataset,sequence,char,ck,class) end + current = getnext(current) -- was absent elseif seq[n][char] then + if n < s then -- new test + current = getnext(current) -- was absent + end n = n + 1 else if discfound then @@ -2467,16 +2492,17 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) else -- skip 'm end + -- maybe only if match + current = getnext(current) elseif seq[n][32] then -- brrr n = n + 1 else match = false break end - current = getnext(current) elseif seq[n][32] then n = n + 1 -current = getnext(current) + current = getnext(current) else match = false break diff --git a/src/fontloader/misc/fontloader-font-oup.lua b/src/fontloader/misc/fontloader-font-oup.lua index 7edaaf6..3b6d8ea 100644 --- a/src/fontloader/misc/fontloader-font-oup.lua +++ b/src/fontloader/misc/fontloader-font-oup.lua @@ -367,18 +367,29 @@ local function copyduplicates(fontdata) for u, d in next, duplicates do local du = descriptions[u] if du then - local t = { f_character_y(u), "@", f_index(du.index), "->" } + local t = { f_character_y(u), "@", f_index(du.index), "->" } + local n = 0 + local m = 25 for u in next, d do if descriptions[u] then - t[#t+1] = f_character_n(u) + if n < m then + t[n+4] = f_character_n(u) + end else local c = copy(du) - -- c.unicode = u -- maybe + c.unicode = u -- better this way descriptions[u] = c - t[#t+1] = f_character_y(u) + if n < m then + t[n+4] = f_character_y(u) + end end + n = n + 1 + end + if n <= m then + report("duplicates: %i : % t",n,t) + else + report("duplicates: %i : % t ...",n,t) end - report("duplicates: % t",t) else -- what a mess end @@ -577,13 +588,12 @@ local function checklookups(fontdata,missing,nofmissing) if r then local name = descriptions[i].name or f_index(i) if not ignore[name] then - done[#done+1] = name + done[name] = true end end end - if #done > 0 then - table.sort(done) - report("not unicoded: % t",done) + if next(done) then + report("not unicoded: % t",table.sortedkeys(done)) end end end diff --git a/src/fontloader/misc/fontloader-font-tfm.lua b/src/fontloader/misc/fontloader-font-tfm.lua index 83ac2f0..8e92c48 100644 --- a/src/fontloader/misc/fontloader-font-tfm.lua +++ b/src/fontloader/misc/fontloader-font-tfm.lua @@ -155,7 +155,7 @@ local function read_from_tfm(specification) end -- let's play safe: properties.haskerns = true - properties.haslogatures = true + properties.hasligatures = true resources.unicodes = { } resources.lookuptags = { } -- diff --git a/src/fontloader/runtime/fontloader-basics-gen.lua b/src/fontloader/runtime/fontloader-basics-gen.lua index c298f6d..2a68b1c 100644 --- a/src/fontloader/runtime/fontloader-basics-gen.lua +++ b/src/fontloader/runtime/fontloader-basics-gen.lua @@ -92,12 +92,10 @@ local remapper = { otf = "opentype fonts", ttf = "truetype fonts", ttc = "truetype fonts", - dfont = "truetype fonts", -- "truetype dictionary", cid = "cid maps", cidmap = "cid maps", - fea = "font feature files", - pfa = "type1 fonts", -- this is for Khaled, in ConTeXt we don't use this! - pfb = "type1 fonts", -- this is for Khaled, in ConTeXt we don't use this! + -- fea = "font feature files", -- no longer supported + pfb = "type1 fonts", -- needed for vector loading afm = "afm", } diff --git a/src/fontloader/runtime/fontloader-reference.lua b/src/fontloader/runtime/fontloader-reference.lua index e784738..5be6493 100644 --- a/src/fontloader/runtime/fontloader-reference.lua +++ b/src/fontloader/runtime/fontloader-reference.lua @@ -1,6 +1,6 @@ -- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua -- parent file : c:/data/develop/context/sources/luatex-fonts.lua --- merge date : 04/18/16 22:12:36 +-- merge date : 04/27/16 10:18:10 do -- begin closure to overcome local limits and interference @@ -3693,11 +3693,8 @@ local remapper={ otf="opentype fonts", ttf="truetype fonts", ttc="truetype fonts", - dfont="truetype fonts", cid="cid maps", cidmap="cid maps", - fea="font feature files", - pfa="type1 fonts", pfb="type1 fonts", afm="afm", } @@ -5656,9 +5653,13 @@ function constructors.scale(tfmdata,specification) elseif autoitalicamount then local vi=description.italic if not vi then - local vi=description.boundingbox[3]-description.width+autoitalicamount - if vi>0 then - chr.italic=vi*hdelta + local bb=description.boundingbox + if bb then + local vi=bb[3]-description.width+autoitalicamount + if vi>0 then + chr.italic=vi*hdelta + end + else end elseif vi~=0 then chr.italic=vi*hdelta @@ -6408,6 +6409,7 @@ local formatters=string.formatters local trace_loading=false trackers.register("fonts.loading",function(v) trace_loading=v end) local trace_mapping=false trackers.register("fonts.mapping",function(v) trace_unimapping=v end) local report_fonts=logs.reporter("fonts","loading") +local force_ligatures=false directives.register("fonts.mapping.forceligatures",function(v) force_ligatures=v end) local fonts=fonts or {} local mappings=fonts.mappings or {} fonts.mappings=mappings @@ -6676,7 +6678,7 @@ function mappings.addtounicode(data,filename,checklookups) local collected=false local unicoded=0 for unicode,glyph in next,descriptions do - if not glyph.unicode and glyph.class=="ligature" then + if glyph.class=="ligature" and (force_ligatures or not glyph.unicode) then if not collected then collected=fonts.handlers.otf.readers.getcomponents(data) if not collected then @@ -6927,7 +6929,7 @@ local function read_from_tfm(specification) end end properties.haskerns=true - properties.haslogatures=true + properties.hasligatures=true resources.unicodes={} resources.lookuptags={} depth[filename]=depth[filename]-1 @@ -6980,9 +6982,11 @@ if not modules then modules={} end modules ['font-afm']={ } local fonts,logs,trackers,containers,resolvers=fonts,logs,trackers,containers,resolvers local next,type,tonumber=next,type,tonumber -local format,match,gmatch,lower,gsub,strip=string.format,string.match,string.gmatch,string.lower,string.gsub,string.strip +local match,gmatch,lower,gsub,strip,find=string.match,string.gmatch,string.lower,string.gsub,string.strip,string.find +local char,byte,sub=string.char,string.byte,string.sub local abs=math.abs -local P,S,C,R,lpegmatch,patterns=lpeg.P,lpeg.S,lpeg.C,lpeg.R,lpeg.match,lpeg.patterns +local bxor,rshift=bit32.bxor,bit32.rshift +local P,S,R,Cmt,C,Ct,Cs,lpegmatch,patterns=lpeg.P,lpeg.S,lpeg.R,lpeg.Cmt,lpeg.C,lpeg.Ct,lpeg.Cs,lpeg.match,lpeg.patterns local derivetable=table.derive local trace_features=false trackers.register("afm.features",function(v) trace_features=v end) local trace_indexing=false trackers.register("afm.indexing",function(v) trace_indexing=v end) @@ -6994,15 +6998,11 @@ local findbinfile=resolvers.findbinfile local definers=fonts.definers local readers=fonts.readers local constructors=fonts.constructors -local fontloader=fontloader -local font_to_table=fontloader.to_table -local open_font=fontloader.open -local close_font=fontloader.close local afm=constructors.newhandler("afm") local pfb=constructors.newhandler("pfb") local afmfeatures=constructors.newfeatures("afm") local registerafmfeature=afmfeatures.register -afm.version=1.500 +afm.version=1.501 afm.cache=containers.define("fonts","afm",afm.version,true) afm.autoprefixed=true afm.helpdata={} @@ -7111,39 +7111,140 @@ local function get_variables(data,fontmetrics) end end end -local function get_indexes(data,pfbname) - data.resources.filename=resolvers.unresolve(pfbname) - local pfbblob=open_font(pfbname) - if pfbblob then - local characters=data.characters - local pfbdata=font_to_table(pfbblob) - if pfbdata then - local glyphs=pfbdata.glyphs - if glyphs then - if trace_loading then - report_afm("getting index data from %a",pfbname) - end - for index,glyph in next,glyphs do - local name=glyph.name - if name then - local char=characters[name] - if char then - if trace_indexing then - report_afm("glyph %a has index %a",name,index) +local get_indexes +do + local fontloader=fontloader + if fontloader then + local font_to_table=fontloader.to_table + local open_font=fontloader.open + local close_font=fontloader.close + local function get_indexes_old(data,pfbname) + local pfbblob=open_font(pfbname) + if pfbblob then + local characters=data.characters + local pfbdata=font_to_table(pfbblob) + if pfbdata then + local glyphs=pfbdata.glyphs + if glyphs then + if trace_loading then + report_afm("getting index data from %a",pfbname) + end + for index,glyph in next,glyphs do + local name=glyph.name + if name then + local char=characters[name] + if char then + if trace_indexing then + report_afm("glyph %a has index %a",name,index) + end + char.index=index + end end - char.index=index end + elseif trace_loading then + report_afm("no glyph data in pfb file %a",pfbname) end + elseif trace_loading then + report_afm("no data in pfb file %a",pfbname) end + close_font(pfbblob) elseif trace_loading then - report_afm("no glyph data in pfb file %a",pfbname) + report_afm("invalid pfb file %a",pfbname) + end + end + end + local n,m + local progress=function(str,position,name,size) + local forward=position+tonumber(size)+3+2 + n=n+1 + if n>=m then + return #str,name + elseif forward<#str then + return forward,name + else + return #str,name + end + end + local initialize=function(str,position,size) + n=0 + m=tonumber(size) + return position+1 + end + local charstrings=P("/CharStrings") + local name=P("/")*C((R("az")+R("AZ")+R("09")+S("-_."))^1) + local size=C(R("09")^1) + local spaces=P(" ")^1 + local p_filternames=Ct ( + (1-charstrings)^0*charstrings*spaces*Cmt(size,initialize)*(Cmt(name*P(" ")^1*C(R("09")^1),progress)+P(1))^1 + ) + local decrypt + do + local r,c1,c2,n=0,0,0,0 + local function step(c) + local cipher=byte(c) + local plain=bxor(cipher,rshift(r,8)) + r=((cipher+r)*c1+c2)%65536 + return char(plain) + end + decrypt=function(binary) + r,c1,c2,n=55665,52845,22719,4 + binary=gsub(binary,".",step) + return sub(binary,n+1) + end + end + local function loadpfbvector(filename) + local data=io.loaddata(resolvers.findfile(filename)) + if not find(data,"!PS%-AdobeFont%-") then + print("no font",filename) + return + end + if not data then + print("no data",filename) + return + end + local ascii,binary=match(data,"(.*)eexec%s+......(.*)") + if not binary then + print("no binary",filename) + return + end + binary=decrypt(binary,4) + local vector=lpegmatch(p_filternames,binary) + vector[0]=table.remove(vector,1) + if not vector then + print("no vector",filename) + return + end + return vector + end + get_indexes=function(data,pfbname) + local vector=loadpfbvector(pfbname) + if vector then + local characters=data.characters + if trace_loading then + report_afm("getting index data from %a",pfbname) + end + for index=1,#vector do + local name=vector[index] + local char=characters[name] + if char then + if trace_indexing then + report_afm("glyph %a has index %a",name,index) + end + char.index=index + end + end + end + end + if fontloader then + afm.use_new_indexer=true + get_indexes_new=get_indexes + get_indexes=function(data,pfbname) + if afm.use_new_indexer then + return get_indexes_new(data,pfbname) + else + return get_indexes_old(data,pfbname) end - elseif trace_loading then - report_afm("no data in pfb file %a",pfbname) end - close_font(pfbblob) - elseif trace_loading then - report_afm("invalid pfb file %a",pfbname) end end local function readafm(filename) @@ -7222,6 +7323,7 @@ function afm.load(filename) data=readafm(filename) if data then if pfbname~="" then + data.resources.filename=resolvers.unresolve(pfbname) get_indexes(data,pfbname) elseif trace_loading then report_afm("no pfb file for %a",filename) @@ -7265,7 +7367,8 @@ end local uparser=fonts.mappings.makenameparser() unify=function(data,filename) local unicodevector=fonts.encodings.agl.unicodes - local unicodes,names={},{} + local unicodes={} + local names={} local private=constructors.privateoffset local descriptions=data.descriptions for name,blob in next,data.characters do @@ -8142,15 +8245,11 @@ if not modules then modules={} end modules ['font-otr']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -if not characters then - require("char-def") - require("char-ini") -end local next,type,unpack=next,type,unpack local byte,lower,char,strip,gsub=string.byte,string.lower,string.char,string.strip,string.gsub local bittest=bit32.btest -local concat,remove,unpack=table.concat,table.remov,table.unpack -local floor,mod,abs,sqrt,round=math.floor,math.mod,math.abs,math.sqrt,math.round +local concat,remove,unpack,fastcopy=table.concat,table.remov,table.unpack,table.fastcopy +local floor,abs,sqrt,round=math.floor,math.abs,math.sqrt,math.round local P,R,S,C,Cs,Cc,Ct,Carg,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Ct,lpeg.Carg,lpeg.Cmt local lpegmatch=lpeg.match local setmetatableindex=table.setmetatableindex @@ -8701,18 +8800,17 @@ readers.hmtx=function(f,fontdata,specification) local nofmetrics=fontdata.horizontalheader.nofhmetrics local glyphs=fontdata.glyphs local nofglyphs=fontdata.nofglyphs - local nofrepeated=nofglyphs-nofmetrics local width=0 local leftsidebearing=0 for i=0,nofmetrics-1 do local glyph=glyphs[i] width=readshort(f) leftsidebearing=readshort(f) - if advance~=0 then + if width~=0 then glyph.width=width end end - for i=nofmetrics,nofrepeated do + for i=nofmetrics,nofglyphs-1 do local glyph=glyphs[i] if width~=0 then glyph.width=width @@ -8795,6 +8893,7 @@ local sequence={ { 0,0,6 }, { 3,0,6 }, { 0,5,14 }, + { 3,10,13 }, } local supported={} for i=1,#sequence do @@ -8853,10 +8952,10 @@ formatreaders[4]=function(f,fontdata,offset) elseif offset==0xFFFF then elseif offset==0 then if trace_cmap then - report("format 4.%i segment %2i from %C upto %C at index %H",1,segment,startchar,endchar,mod(startchar+delta,65536)) + report("format 4.%i segment %2i from %C upto %C at index %H",1,segment,startchar,endchar,(startchar+delta)%65536) end for unicode=startchar,endchar do - local index=mod(unicode+delta,65536) + local index=(unicode+delta)%65536 if index and index>0 then local glyph=glyphs[index] if glyph then @@ -8885,13 +8984,13 @@ formatreaders[4]=function(f,fontdata,offset) else local shift=(segment-nofsegments+offset/2)-startchar if trace_cmap then - report("format 4.%i segment %2i from %C upto %C at index %H",0,segment,startchar,endchar,mod(startchar+delta,65536)) + report("format 4.%i segment %2i from %C upto %C at index %H",0,segment,startchar,endchar,(startchar+delta)%65536) end for unicode=startchar,endchar do local slot=shift+unicode local index=indices[slot] if index and index>0 then - index=mod(index+delta,65536) + index=(index+delta)%65536 local glyph=glyphs[index] if glyph then local gu=glyph.unicode @@ -8966,7 +9065,7 @@ formatreaders[12]=function(f,fontdata,offset) local last=readulong(f) local index=readulong(f) if trace_cmap then - report("format 12 from %C to %C",first,last) + report("format 12 from %C to %C starts at index %i",first,last,index) end for unicode=first,last do local glyph=glyphs[index] @@ -8992,6 +9091,49 @@ formatreaders[12]=function(f,fontdata,offset) end return nofdone end +formatreaders[13]=function(f,fontdata,offset) + setposition(f,offset+2+2+4+4) + local mapping=fontdata.mapping + local glyphs=fontdata.glyphs + local duplicates=fontdata.duplicates + local nofgroups=readulong(f) + local nofdone=0 + for i=1,nofgroups do + local first=readulong(f) + local last=readulong(f) + local index=readulong(f) + if first<privateoffset then + if trace_cmap then + report("format 13 from %C to %C get index %i",first,last,index) + end + local glyph=glyphs[index] + local unicode=glyph.unicode + if not unicode then + unicode=first + glyph.unicode=unicode + first=first+1 + end + local list=duplicates[unicode] + mapping[index]=unicode + if not list then + list={} + duplicates[unicode]=list + end + if last>=privateoffset then + local limit=privateoffset-1 + report("format 13 from %C to %C pruned to %C",first,last,limit) + last=limit + end + for unicode=first,last do + list[unicode]=true + end + nofdone=nofdone+last-first+1 + else + report("format 13 from %C to %C ignored",first,last) + end + end + return nofdone +end formatreaders[14]=function(f,fontdata,offset) if offset and offset~=0 then setposition(f,offset) @@ -13831,16 +13973,28 @@ local function copyduplicates(fontdata) local du=descriptions[u] if du then local t={ f_character_y(u),"@",f_index(du.index),"->" } + local n=0 + local m=25 for u in next,d do if descriptions[u] then - t[#t+1]=f_character_n(u) + if n<m then + t[n+4]=f_character_n(u) + end else local c=copy(du) + c.unicode=u descriptions[u]=c - t[#t+1]=f_character_y(u) + if n<m then + t[n+4]=f_character_y(u) + end end + n=n+1 + end + if n<=m then + report("duplicates: %i : % t",n,t) + else + report("duplicates: %i : % t ...",n,t) end - report("duplicates: % t",t) else end end @@ -14025,13 +14179,12 @@ local function checklookups(fontdata,missing,nofmissing) if r then local name=descriptions[i].name or f_index(i) if not ignore[name] then - done[#done+1]=name + done[name]=true end end end - if #done>0 then - table.sort(done) - report("not unicoded: % t",done) + if next(done) then + report("not unicoded: % t",table.sortedkeys(done)) end end end @@ -15486,7 +15639,7 @@ local trace_defining=false registertracker("fonts.defining",function(v) trace_de local report_otf=logs.reporter("fonts","otf loading") local fonts=fonts local otf=fonts.handlers.otf -otf.version=3.017 +otf.version=3.018 otf.cache=containers.define("fonts","otl",otf.version,true) local otfreaders=otf.readers local hashes=fonts.hashes @@ -16527,8 +16680,6 @@ local attributes,nodes,node=attributes,nodes,node fonts=fonts local hashes=fonts.hashes local fontdata=hashes.identifiers -local parameters=fonts.hashes.parameters -local resources=fonts.hashes.resources nodes.injections=nodes.injections or {} local injections=nodes.injections local tracers=nodes.tracers @@ -17718,7 +17869,7 @@ local function injectspaces(head) local function updatefont(font,trig) leftkerns=trig.left rightkerns=trig.right - local par=parameters[font] + local par=fontdata[font].parameters factor=par.factor threshold=par.spacing.width-1 lastfont=font @@ -18566,7 +18717,21 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou local pre,post,replace,pretail,posttail,replacetail=getdisc(discfound,true) if not replace then local prev=getprev(base) - local copied=copy_node_list(comp) +local current=comp +local previous=nil +local copied=nil +while current do + if getid(current)==glyph_code then + local n=copy_node(current) + if copied then + setlink(previous,n) + else + copied=n + end + previous=n + end + current=getnext(current) +end setprev(discnext,nil) setnext(discprev,nil) if pre then @@ -19871,7 +20036,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) else local discfound=nil local n=f+1 - last=getnext(last) + last=getnext(last) while n<=l do if not last and (sweeptype=="post" or sweeptype=="replace") then last=getnext(sweepnode) @@ -19912,7 +20077,6 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) end break end - last=getnext(last) elseif char==false then if discfound then notmatchreplace[discfound]=true @@ -19997,8 +20161,12 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) if trace_skips then show_skip(dataset,sequence,char,ck,class) end + prev=getprev(prev) elseif seq[n][char] then - n=n -1 + if n>1 then + prev=getprev(prev) + end + n=n-1 else if discfound then notmatchreplace[discfound]=true @@ -20017,7 +20185,6 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) end break end - prev=getprev(prev) elseif char==false then if discfound then notmatchreplace[discfound]=true @@ -20079,19 +20246,19 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) if not match then break end - else end - else end + prev=getprev(prev) elseif seq[n][32] then n=n-1 + prev=getprev(prev) else match=false break end - prev=getprev(prev) elseif seq[n][32] then n=n-1 + prev=getprev(prev) else match=false break @@ -20126,7 +20293,11 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) if trace_skips then show_skip(dataset,sequence,char,ck,class) end + current=getnext(current) elseif seq[n][char] then + if n<s then + current=getnext(current) + end n=n+1 else if discfound then @@ -20200,16 +20371,16 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) end else end + current=getnext(current) elseif seq[n][32] then n=n+1 else match=false break end - current=getnext(current) elseif seq[n][32] then n=n+1 -current=getnext(current) + current=getnext(current) else match=false break diff --git a/src/luaotfload-auxiliary.lua b/src/luaotfload-auxiliary.lua index 3d300e7..e544dd7 100644 --- a/src/luaotfload-auxiliary.lua +++ b/src/luaotfload-auxiliary.lua @@ -88,13 +88,12 @@ package. --doc]]-- local set_sscale_dimens = function (fontdata) - local mathconstants = fontdata.MathConstants - local parameters = fontdata.parameters - if mathconstants then - parameters[10] = mathconstants.ScriptPercentScaleDown or 70 - parameters[11] = mathconstants.ScriptScriptPercentScaleDown or 50 - end - return fontdata + local resources = fontdata.resources if not resources then return end + local mathconstants = resources.MathConstants if not mathconstants then return end + local parameters = fontdata.parameters if not parameters then return end + --- the default values below are complete crap + parameters [10] = mathconstants.ScriptPercentScaleDown or 70 + parameters [11] = mathconstants.ScriptScriptPercentScaleDown or 50 end luaotfload_callbacks [#luaotfload_callbacks + 1] = { @@ -104,8 +103,8 @@ luaotfload_callbacks [#luaotfload_callbacks + 1] = { --- fontobj -> int local lookup_units = function (fontdata) local metadata = fontdata.shared and fontdata.shared.rawdata.metadata - if metadata and metadata.units_per_em then - return metadata.units_per_em + if metadata and metadata.units then + return metadata.units elseif fontdata.parameters and fontdata.parameters.units then return fontdata.parameters.units elseif fontdata.units then --- v1.x @@ -122,9 +121,9 @@ local patch_cambria_domh = function (fontdata) local mathconstants = fontdata.MathConstants if mathconstants and fontdata.psname == "CambriaMath" then --- my test Cambria has 2048 - local units_per_em = fontdata.units_per_em or lookup_units(fontdata) - local sz = fontdata.parameters.size or fontdata.size - local mh = 2800 / units_per_em * sz + local units = fontdata.units or lookup_units(fontdata) + local sz = fontdata.parameters.size or fontdata.size + local mh = 2800 / units * sz if mathconstants.DisplayOperatorMinHeight < mh then mathconstants.DisplayOperatorMinHeight = mh end @@ -177,40 +176,95 @@ Comment from fontspec: --doc]]-- +local capheight_reference_chars = { "X", "M", "Ж", "ξ", } +local capheight_reference_codepoints do + local utfbyte = unicode.utf8.byte + capheight_reference_codepoints = { } + for i = 1, #capheight_reference_chars do + local chr = capheight_reference_chars [i] + capheight_reference_codepoints [i] = utfbyte (chr) + end +end + +local determine_capheight = function (fontdata) + local parameters = fontdata.parameters if not parameters then return false end + local characters = fontdata.characters if not characters then return false end + --- Pretty simplistic but it does return *some* value for most fonts; + --- we could also refine the approach to return some kind of average + --- of all capital letters or a user-provided subset. + for i = 1, #capheight_reference_codepoints do + local refcp = capheight_reference_codepoints [i] + local refchar = characters [refcp] + if refchar then + logreport ("both", 4, "aux", + "picked height of character ‘%s’ (U+%d) as \\fontdimen8 \z + candidate", + capheight_reference_chars [i], refcp) + return refchar.height + end + end + return false +end + +local query_ascender = function (fontdata) + local parameters = fontdata.parameters if not parameters then return false end + local shared = fontdata.shared if not shared then return false end + local rawdata = shared.rawdata if not rawdata then return false end + local metadata = rawdata.metadata if not metadata then return false end + local ascender = parameters.ascender + or metadata.ascender if not ascender then return false end + local units = metadata.units if units == 0 then return false end + local size = parameters.size if not size then return false end + return ascender * size / units +end + +local query_capheight = function (fontdata) + local parameters = fontdata.parameters if not parameters then return false end + local shared = fontdata.shared if not shared then return false end + local rawdata = shared.rawdata if not rawdata then return false end + local metadata = rawdata.metadata if not metadata then return false end + local capheight = metadata.capheight if not capheight then return false end + local units = metadata.units if units == 0 then return false end + local size = parameters.size if not size then return false end + return capheight * size / units +end + +local query_fontdimen8 = function (fontdata) + local parameters = fontdata.parameters if not parameters then return false end + local fontdimen8 = parameters [8] + if fontdimen8 then return fontdimen8 end + return false +end + +local caphtfmt = function (ref, ht) + if not ht then return "<none>" end + if not ref then return tostring (ht) end + return stringformat ("%s(δ=%s)", ht, ht - ref) +end + local set_capheight = function (fontdata) - local shared = fontdata.shared - local parameters = fontdata.parameters - local capheight - if shared - and shared.rawdata.metadata - and shared.rawdata.metadata.pfminfo - then - local units_per_em = parameters.units - local size = parameters.size - local os2_capheight = shared.rawdata.metadata.pfminfo.os2_capheight - - if capheight and os2_capheight > 0 then - capheight = os2_capheight / units_per_em * size - else - local X8_str = stringbyte"X" - local X8_chr = fontdata.characters[X8_str] - if X8_chr then - capheight = X8_chr.height - else - capheight = parameters.ascender / units_per_em * size - end - end - else - local X8_str = stringbyte "X" - local X8_chr = fontdata.characters[X8_str] - if X8_chr then - capheight = X8_chr.height - end + if not fontdata then + logreport ("both", 0, "aux", + "error: set_capheight() received garbage") + return + end + local capheight_dimen8 = query_fontdimen8 (fontdata) + local capheight_alleged = query_capheight (fontdata) + local capheight_ascender = query_ascender (fontdata) + local capheight_measured = determine_capheight (fontdata) + logreport ("term", 4, "aux", + "capht: param[8]=%s advertised=%s ascender=%s measured=%s", + tostring (capheight_dimen8), + caphtfmt (capheight_dimen8, capheight_alleged), + caphtfmt (capheight_dimen8, capheight_ascender), + caphtfmt (capheight_dimen8, capheight_measured)) + if capheight_dimen8 then --- nothing to do + return end + + local capheight = capheight_alleged or capheight_ascender or capheight_measured if capheight then - --- is this legit? afaics there’s nothing else on the - --- array part of that table - fontdata.parameters[8] = capheight + fontdata.parameters [8] = capheight end end @@ -263,20 +317,27 @@ end --- int -> string -> bool -> (int | false) local slot_of_name = function (font_id, glyphname, unsafe) - local fontdata = identifiers[font_id] - if fontdata then - local unicode = fontdata.resources.unicodes[glyphname] - if unicode then - if type(unicode) == "number" then - return unicode - else - return unicode[1] --- for multiple components - end --- else --- --- missing + if not font_id or type (font_id) ~= "number" + or not glyphname or type (glyphname) ~= "string" + then + logreport ("both", 0, "aux", + "invalid parameters to slot_of_name (%s, %s)", + tostring (font_id), tostring (glyphname)) + return false + end + + local tfmdata = identifiers [font_id] + if not tfmdata then return raw_slot_of_name (font_id, glyphname) end + local resources = tfmdata.resources if not resources then return false end + local unicodes = resources.unicodes if not unicodes then return false end + + local unicode = unicodes [glyphname] + if unicode then + if type (unicode) == "number" then + return unicode + else + return unicode [1] --- for multiple components end - elseif unsafe == true then -- for Robert - return raw_slot_of_name(font_id, glyphname) end return false end @@ -298,11 +359,23 @@ local indices --- int -> (string | false) local name_of_slot = function (codepoint) + if not codepoint or type (codepoint) ~= "number" then + logreport ("both", 0, "aux", + "invalid parameters to name_of_slot (%s)", + tostring (codepoint)) + return false + end + if not indices then --- this will load the glyph list local unicodes = encodings.agl.unicodes - indices = table.swapped(unicodes) + if not unicodes or not next (unicodes)then + logreport ("both", 0, "aux", + "name_of_slot: failed to load the AGL.") + end + indices = table.swapped (unicodes) end - local glyphname = indices[codepoint] + + local glyphname = indices [codepoint] if glyphname then return glyphname end @@ -317,6 +390,12 @@ aux.name_of_slot = name_of_slot ----------------------------------------------------------------------- --- lots of arrowcode ahead +local get_features = function (tfmdata) + local resources = tfmdata.resources if not resources then return false end + local features = resources.features if not features then return false end + return features +end + --[[doc-- This function, modeled after “check_script()” from fontspec, returns true if in the given font, the script “asked_script” is accounted for in at @@ -325,13 +404,23 @@ least one feature. --- int -> string -> bool local provides_script = function (font_id, asked_script) + if not font_id or type (font_id) ~= "number" + or not asked_script or type (asked_script) ~= "string" + then + logreport ("both", 0, "aux", + "invalid parameters to provides_script(%s, %s)", + tostring (font_id), tostring (asked_script)) + return false + end asked_script = stringlower(asked_script) if font_id and font_id > 0 then - local tfmdata = identifiers[font_id] if not tfmdata then return false end - local shared = tfmdata.shared if not shared then return false end - local fontdata = shared.rawdata if not fontdata then return false end - local fontname = fontdata.metadata.fontname - local features = fontdata.resources.features + local tfmdata = identifiers[font_id] + if not tfmdata then return false end + local features = get_features (tfmdata) + if features == false then + logreport ("log", 1, "aux", "font no %d lacks a features table", font_id) + return false + end for method, featuredata in next, features do --- where method: "gpos" | "gsub" for feature, data in next, featuredata do @@ -362,14 +451,27 @@ feature. --- int -> string -> string -> bool local provides_language = function (font_id, asked_script, asked_language) - asked_script = stringlower(asked_script) - asked_language = stringlower(asked_language) + if not font_id or type (font_id) ~= "number" + or not asked_script or type (asked_script) ~= "string" + or not asked_language or type (asked_language) ~= "string" + then + logreport ("both", 0, "aux", + "invalid parameters to provides_language(%s, %s, %s)", + tostring (font_id), + tostring (asked_script), + tostring (asked_language)) + return false + end + asked_script = stringlower(asked_script) + asked_language = stringlower(asked_language) if font_id and font_id > 0 then - local tfmdata = identifiers[font_id] if not tfmdata then return false end - local shared = tfmdata.shared if not shared then return false end - local fontdata = shared.rawdata if not fontdata then return false end - local fontname = fontdata.metadata.fontname - local features = fontdata.resources.features + local tfmdata = identifiers[font_id] + if not tfmdata then return false end + local features = get_features (tfmdata) + if features == false then + logreport ("log", 1, "aux", "font no %d lacks a features table", font_id) + return false + end for method, featuredata in next, features do --- where method: "gpos" | "gsub" for feature, data in next, featuredata do @@ -432,16 +534,29 @@ accounted for in the script with tag “asked_script” in feature --- int -> string -> string -> string -> bool local provides_feature = function (font_id, asked_script, asked_language, asked_feature) + if not font_id or type (font_id) ~= "number" + or not asked_script or type (asked_script) ~= "string" + or not asked_language or type (asked_language) ~= "string" + or not asked_feature or type (asked_feature) ~= "string" + then + logreport ("both", 0, "aux", + "invalid parameters to provides_feature(%s, %s, %s, %s)", + tostring (font_id), tostring (asked_script), + tostring (asked_language), tostring (asked_feature)) + return false + end asked_script = stringlower(asked_script) asked_language = stringlower(asked_language) asked_feature = lpegmatch(strip_garbage, asked_feature) if font_id and font_id > 0 then - local tfmdata = identifiers[font_id] if not tfmdata then return false end - local shared = tfmdata.shared if not shared then return false end - local fontdata = shared.rawdata if not fontdata then return false end - local features = fontdata.resources.features - local fontname = fontdata.metadata.fontname + local tfmdata = identifiers[font_id] + if not tfmdata then return false end + local features = get_features (tfmdata) + if features == false then + logreport ("log", 1, "aux", "font no %d lacks a features table", font_id) + return false + end for method, featuredata in next, features do --- where method: "gpos" | "gsub" local feature = featuredata[asked_feature] diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index 8faf3d1..92de432 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -89,7 +89,7 @@ local config_paths = { } local valid_formats = tabletohash { - "otf", "ttc", "ttf", "afm", --"pfb", "pfa", + "otf", "ttc", "ttf", "afm", "pfb" } local feature_presets = { diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 135ab0e..437091f 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -208,7 +208,7 @@ end local format_precedence = { "otf", "ttc", "ttf", "afm", - --- "pfb", "pfa", + -- "pfb" --- may come back before Luatex 1.0 } local location_precedence = { @@ -359,7 +359,7 @@ This is a sketch of the luaotfload db: conflicts : { barename : int; basename : int }; // filename conflict with font at index; happens with subfonts familyname : string; // sanitized name of the font family the font belongs to, usually from the names table fontname : string; // sanitized name of the font - format : string; // "otf" | "ttf" | "afm" + format : string; // "otf" | "ttf" | "afm" (* | "pfb" *) fullname : string; // sanitized full name of the font including style modifiers fullpath : string; // path to font in filesystem index : int; // index in the mappings table @@ -478,7 +478,6 @@ end --- define locals in scope local access_font_index -local collect_families local find_closest local flush_lookup_cache local generate_filedata @@ -495,6 +494,7 @@ local lookup_fullpath local save_lookups local save_names local set_font_filter +local t1_fullinfo local update_names --- state of the database @@ -595,11 +595,11 @@ load_lookups = function ( ) end local regular_synonym = { - book = "r", - normal = "r", - plain = "r", - regular = "r", - roman = "r", + book = true, + normal = true, + plain = true, + regular = true, + roman = true, } local italic_synonym = { @@ -1034,7 +1034,7 @@ local lookup_fontname = function (specification, name, style) lastresort = face end elseif face.metafamily == name - and (regular_synonym [prefmodifiers] + and ( regular_synonym [prefmodifiers] or regular_synonym [subfamily]) then lastresort = face @@ -1409,7 +1409,6 @@ local map_english_names = function (metadata) certain. --]]-- if platformnames then - --inspect(metadata) --namesource = platformnames.macintosh or platformnames.windows namesource = platformnames.windows or platformnames.macintosh end @@ -1448,7 +1447,6 @@ local get_raw_info = function (metadata, basename) return { familyname = metadata.familyname, fontname = fontname, - fontstyle_name = metadata.fontstyle_name, fullname = fullname, italicangle = metadata.italicangle, names = metadata.names, @@ -1519,21 +1517,6 @@ local organize_namedata = function (rawinfo, }, } - -- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size - if rawinfo.fontstyle_name then - --- not present in all fonts, often differs from the preferred - --- subfamily as well as subfamily fields, e.g. with - --- LMSans10-BoldOblique: - --- subfamily: “Bold Italic” - --- prefmodifiers: “10 Bold Oblique” - --- fontstyle_name: “Bold Oblique” - for _, name in next, rawinfo.fontstyle_name do - if name.lang == 1033 then --- I hate magic numbers - fontnames.fontstyle_name = name.name - end - end - end - return { sanitized = sanitize_fontnames (fontnames), fontname = rawinfo.fontname, @@ -1617,13 +1600,71 @@ ot_fullinfo = function (filename, return res end +--[[doc-- + + Type1 font inspector. In comparison with OTF, PFB’s contain a good + deal less name fields which makes it tricky in some parts to find a + meaningful representation for the database. + + Good read: http://www.adobe.com/devnet/font/pdfs/5004.AFM_Spec.pdf + +--doc]]-- + +--- string -> int -> bool -> string -> fontentry + +t1_fullinfo = function (filename, _subfont, location, basename, format) + local sanitized + local metadata = load_font_file (filename) + local fontname = metadata.fontname + local fullname = metadata.fullname + local familyname = metadata.familyname + local italicangle = metadata.italicangle + local style = "" + local weight + + sanitized = sanitize_fontnames ({ + fontname = fontname, + psname = fullname, + metafamily = familyname, + familyname = familyname, + weight = metadata.weight, --- string identifier + prefmodifiers = style, + }) + + weight = sanitized.weight + + if weight == "bold" then + style = weight + end + + if italicangle ~= 0 then + style = style .. "italic" + end + + return { + basename = basename, + fullpath = filename, + subfont = false, + location = location or "system", + format = format, + fullname = sanitized.fullname, + fontname = sanitized.fontname, + familyname = sanitized.familyname, + plainname = fullname, + psname = sanitized.fontname, + version = metadata.version, + size = false, + prefmodifiers = style ~= "" and style or weight, + weight = metadata.pfminfo and pfminfo.weight or 400, + italicangle = italicangle, + } +end + local loaders = { otf = ot_fullinfo, ttc = ot_fullinfo, ttf = ot_fullinfo, - -----pfb = t1_fullinfo, --> may come back one day, so -----pfa = t1_fullinfo, --> we keep the indirection +--- pfb = t1_fullinfo, } --- not side-effect free! @@ -2525,6 +2566,9 @@ generate_filedata = function (mappings) return files end +local bold_spectrum_low = 501 --- 500 is medium, 900 heavy/black +local bold_weight = 700 + local pick_style local pick_fallback_style local check_regular @@ -2552,31 +2596,33 @@ do return false end - pick_style = function (fontstyle_name, - prefmodifiers, + pick_style = function (prefmodifiers, subfamily) local style - if fontstyle_name --[[ff only]] then - style = choose_exact (fontstyle_name) - end - if not style then - if prefmodifiers then - style = choose_exact (prefmodifiers) - elseif subfamily then - style = choose_exact (subfamily) - end + if prefmodifiers then + style = choose_exact (prefmodifiers) + elseif subfamily then + style = choose_exact (subfamily) end return style end pick_fallback_style = function (italicangle, weight, pfmweight) - --- more aggressive, but only to determine bold faces - if pfmweight > 500 or bold_synonym [weight] then --- bold spectrum matches + --[[-- + More aggressive, but only to determine bold faces. + Note: Before you make this test more inclusive, ensure + no fonts are matched in the bold synonym spectrum over + a literally “bold[italic]” one. In the past, heuristics + been tried but ultimately caused unwanted modifiers + polluting the lookup table. What doesn’t work is, e. g. + treating weights > 500 as bold or allowing synonyms like + “heavy”, “black”. + --]]-- + if pfmweight == bold_weight then --- bold spectrum matches if italicangle == 0 then - return tostring (weight) - else - return tostring (weight) .. "i" + return "b" end + return "bi" end return false end @@ -2584,13 +2630,12 @@ do --- we use only exact matches here since there are constructs --- like “regularitalic” (Cabin, Bodoni Old Fashion) - check_regular = function (fontstyle_name, - prefmodifiers, + check_regular = function (prefmodifiers, subfamily, italicangle, weight, pfmweight) - local plausible_weight + local plausible_weight = false --[[-- This filters out undesirable candidates that specify their prefmodifiers or subfamily as “regular” but are actually of @@ -2605,9 +2650,11 @@ do end if plausible_weight then - return fontstyle_name and regular_synonym [fontstyle_name] - or prefmodifiers and regular_synonym [prefmodifiers] - or subfamily and regular_synonym [subfamily] + if prefmodifiers and regular_synonym [prefmodifiers] + or subfamily and regular_synonym [subfamily] + then + return "r" + end end return false end @@ -2634,7 +2681,6 @@ local pull_values = function (entry) entry.fullname = english.fullname or info.fullname entry.prefmodifiers = english.prefmodifiers entry.familyname = metadata.familyname or english.preffamily or english.family - entry.fontstyle_name = sanitized.fontstyle_name entry.plainname = names.fullname entry.subfamily = english.subfamily @@ -2678,7 +2724,7 @@ local get_subtable = function (families, entry) return subtable end -collect_families = function (mappings) +local collect_families = function (mappings) logreport ("info", 2, "db", "Analyzing families.") @@ -2699,7 +2745,6 @@ collect_families = function (mappings) local subtable = get_subtable (families, entry) local familyname = entry.familyname - local fontstyle_name = entry.fontstyle_name local prefmodifiers = entry.prefmodifiers local subfamily = entry.subfamily @@ -2707,34 +2752,22 @@ collect_families = function (mappings) local pfmweight = entry.pfmweight local italicangle = entry.italicangle - local modifier = pick_style (fontstyle_name, - prefmodifiers, - subfamily) + local modifier = pick_style (prefmodifiers, subfamily) if not modifier then --- regular, exact only - modifier = check_regular (fontstyle_name, - prefmodifiers, + modifier = check_regular (prefmodifiers, subfamily, italicangle, weight, pfmweight) end - --if familyname == "garamondpremierpro" then - --print(entry.fullname, "reg?",modifier, "->",fontstyle_name, - --prefmodifiers, - --subfamily, - --italicangle, - --pfmweight, - --weight) - --end + + if not modifier then + modifier = pick_fallback_style (italicangle, weight, pfmweight) + end if modifier then add_family (familyname, subtable, modifier, entry) - elseif pfmweight > 500 then -- in bold spectrum - modifier = pick_fallback_style (italicangle, weight, pfmweight) - if modifier then - add_family (familyname, subtable, modifier, entry) - end end end @@ -2753,8 +2786,6 @@ end --doc]]-- -local bold_spectrum_low = 501 --- 500 is medium, 900 heavy/black -local bold_weight = 700 local style_categories = { "r", "b", "i", "bi" } local bold_categories = { "b", "bi" } @@ -2963,7 +2994,7 @@ local collect_statistics = function (mappings) local sum_dsnsize, n_dsnsize = 0, 0 local fullname, family, families = { }, { }, { } - local subfamily, prefmodifiers, fontstyle_name = { }, { }, { } + local subfamily, prefmodifiers = { }, { } local addtohash = function (hash, item) if item then @@ -3023,7 +3054,6 @@ local collect_statistics = function (mappings) addtohash (family, englishnames.family) addtohash (subfamily, englishnames.subfamily) addtohash (prefmodifiers, englishnames.prefmodifiers) - addtohash (fontstyle_name, names.fontstyle_name) addtoset (families, englishnames.family, englishnames.fullname) @@ -3098,10 +3128,6 @@ local collect_statistics = function (mappings) setsize (prefmodifiers)) pprint_top (prefmodifiers, 4) - logreport ("both", 0, "db", - " · %d different “fontstyle_name” kinds.", - setsize (fontstyle_name)) - pprint_top (fontstyle_name, 4) end local mean_dsnsize = 0 @@ -3118,7 +3144,6 @@ local collect_statistics = function (mappings) -- style = { -- subfamily = subfamily, -- prefmodifiers = prefmodifiers, --- fontstyle_name = fontstyle_name, -- }, } end @@ -3483,8 +3508,16 @@ local use_fontforge = function (val) close_font_file = fontloader.close get_english_names = get_english_names_from_ff else - read_font_file = otfhandler.readers.getinfo - read_font_info = read_font_file + local wrapper = function (filename, subfont) + return otfhandler.readers.getinfo (filename, + { subfont = subfont + , details = false + , platformnames = true + , rawfamilynames = true + }) + end + read_font_file = wrapper + read_font_info = wrapper close_font_file = function () end get_english_names = map_english_names end @@ -3537,7 +3570,7 @@ return { fonts.definers = fonts.definers or { resolvers = { } } names.blacklist = blacklist - names.version = 2.6 + names.version = 2.7 names.data = nil --- contains the loaded database names.lookups = nil --- contains the lookup cache diff --git a/src/luaotfload-loaders.lua b/src/luaotfload-loaders.lua index f0c1913..f6cb272 100644 --- a/src/luaotfload-loaders.lua +++ b/src/luaotfload-loaders.lua @@ -43,12 +43,20 @@ end local unsupported_reader = function (format) return function (specification) - logreport ("both", 0, "loaders", + logreport ("both", 4, "loaders", "font format “%s” unsupported; cannot load %s.", format, tostring (specification.name)) end end +local afm_compat_message = function (specification, method) + logreport ("both", 4, "loaders", + "PFB format only supported with matching \z + AFM; redirecting (“%s”, “%s”).", + tostring (specification.name), tostring (method)) + return fonts.readers.afm (specification, method) +end + local install_formats = function () local fonts = fonts if not fonts then return false end @@ -70,7 +78,7 @@ local install_formats = function () readers [which] = reader handlers [which] = { } if not seqset [which] then - logreport ("both", 0, "loaders", + logreport ("both", 3, "loaders", "Extending reader sequence for “%s”.", which) sequence [#sequence + 1] = which seqset [which] = true @@ -81,7 +89,7 @@ local install_formats = function () return aux ("evl", eval_reader) and aux ("lua", lua_reader) and aux ("pfa", unsupported_reader "pfa") - and aux ("pfb", unsupported_reader "pfb") + and aux ("pfb", afm_compat_message) --- pfb loader is incomplete and aux ("ofm", readers.tfm) and aux ("dfont", unsupported_reader "dfont") end diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index 51a0657..9e8d088 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -111,9 +111,33 @@ local make_loader = function (prefix) return function (name) local t_0 = osgettimeofday () local modname = make_loader_name (prefix, name) - local data = require (modname) + --- We don’t want the stack info from inside, so just pcall(). + local ok, data = pcall (require, modname) local t_end = osgettimeofday () timing_info.t_load [name] = t_end - t_0 + if not ok then + io.write "\n" + local msg = luaotfload.log and luaotfload.log.report or print + msg ("both", 0, "load", "FATAL ERROR") + msg ("both", 0, "load", " × Failed to load module %q.", + tostring (modname)) + local lines = string.split (data, "\n\t") + if not lines then + msg ("both", 0, "load", " × Error message: %q", data) + else + msg ("both", 0, "load", " × Error message:") + for i = 1, #lines do + msg ("both", 0, "load", " × %q.", lines [i]) + end + end + io.write "\n\n" + local debug = debug + if debug then + io.write (debug.traceback()) + io.write "\n\n" + end + os.exit(-1) + end return data end end diff --git a/src/luaotfload-parsers.lua b/src/luaotfload-parsers.lua index 83e2a54..307c35c 100644 --- a/src/luaotfload-parsers.lua +++ b/src/luaotfload-parsers.lua @@ -722,10 +722,10 @@ local blank_line = ws * eol local skip_line = comment_line + blank_line local ini_id_char = alpha + (dash / "_") local ini_id = Cs(alpha * ini_id_char^0) / stringlower -local ini_value_char = (valid_escapes + (1 - newline - backslash - comment_char)) +local ini_value_char = (valid_escapes + (1 - linebreak - backslash - comment_char)) local ini_value = (Cs (ini_value_char^0) / string.strip) * (comment_char * (1 - eol)^0)^-1 -local ini_string_char = (valid_escapes + (1 - newline - dquote - backslash)) +local ini_string_char = (valid_escapes + (1 - linebreak - dquote - backslash)) local ini_string = dquote * Cs (ini_string_char^0) * dquote diff --git a/src/luaotfload-resolvers.lua b/src/luaotfload-resolvers.lua index 05a0656..ee3b597 100644 --- a/src/luaotfload-resolvers.lua +++ b/src/luaotfload-resolvers.lua @@ -205,20 +205,18 @@ local resolve_sequence = function (seq, specification) for i = 1, #seq do local id = seq [i] local mth = resolve_methods [id] - logreport ("both", 0, "resolve", "step %d: apply method %q (%s)", i, id, mth) + logreport ("both", 3, "resolve", "step %d: apply method %q (%s)", i, id, mth) if mth (specification) == true then - logreport ("both", 0, "resolve", - "%d: method %q indicated lookup success", i, id) - logreport ("both", 0, "resolve", - "method %q resolved %q -> %s (%s)", - id, specification.specification, + logreport ("both", 3, "resolve", + "%d: method %q resolved %q -> %s (%s).", + i, id, specification.specification, specification.name, specification.forcedname) return true end end logreport ("both", 0, "resolve", - "sequence of %d lookups yielded nothing appropriate", #seq) + "sequence of %d lookups yielded nothing appropriate.", #seq) return false end |