diff options
Diffstat (limited to 'Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua')
-rw-r--r-- | Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua | 2108 |
1 files changed, 1645 insertions, 463 deletions
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua index 13095f7e699..1fe4289b2f7 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua @@ -48,21 +48,36 @@ if not modules then modules = { } end modules ['font-dsp'] = { -- of node lists is not noticeable faster for latin texts, but for arabic we gain some 10% -- (and could probably gain a bit more). +-- All this packing in the otf format is somewhat obsessive as nowadays 4K resolution +-- multi-gig videos pass through our networks and storage and memory is abundant. + +-- Although we use a few table readers there i sno real gain in there (apart from having +-- less code. After all there are often not that many demanding features. + local next, type = next, type -local bittest = bit32.btest +local band = bit32.band +local extract = bit32.extract +local bor = bit32.bor +local lshift = bit32.lshift local rshift = bit32.rshift -local concat = table.concat +local gsub = string.gsub local lower = string.lower -local copy = table.copy local sub = string.sub local strip = string.strip local tohash = table.tohash +local concat = table.concat +local copy = table.copy local reversed = table.reversed +local sort = table.sort +local insert = table.insert +local round = math.round +local settings_to_hash = utilities.parsers.settings_to_hash_colon_too local setmetatableindex = table.setmetatableindex local formatters = string.formatters local sortedkeys = table.sortedkeys local sortedhash = table.sortedhash +local sequenced = table.sequenced local report = logs.reporter("otf reader") @@ -71,18 +86,62 @@ local streamreader = readers.streamreader local setposition = streamreader.setposition local getposition = streamreader.getposition -local skipshort = streamreader.skipshort local readushort = streamreader.readcardinal2 -- 16-bit unsigned integer local readulong = streamreader.readcardinal4 -- 24-bit unsigned integer +local readinteger = streamreader.readinteger1 local readshort = streamreader.readinteger2 -- 16-bit signed integer -local readfword = readshort local readstring = streamreader.readstring local readtag = streamreader.readtag local readbytes = streamreader.readbytes +local readfixed = streamreader.readfixed4 +local read2dot14 = streamreader.read2dot14 +local skipshort = streamreader.skipshort +local skipbytes = streamreader.skip +local readbytetable = streamreader.readbytetable +local readbyte = streamreader.readbyte +local readcardinaltable = streamreader.readcardinaltable +local readintegertable = streamreader.readintegertable +local readfword = readshort + +local short = 2 +local ushort = 2 +local ulong = 4 + +directives.register("fonts.streamreader",function() + + streamreader = utilities.streams + + setposition = streamreader.setposition + getposition = streamreader.getposition + readushort = streamreader.readcardinal2 + readulong = streamreader.readcardinal4 + readinteger = streamreader.readinteger1 + readshort = streamreader.readinteger2 + readstring = streamreader.readstring + readtag = streamreader.readtag + readbytes = streamreader.readbytes + readfixed = streamreader.readfixed4 + read2dot14 = streamreader.read2dot14 + skipshort = streamreader.skipshort + skipbytes = streamreader.skip + readbytetable = streamreader.readbytetable + readbyte = streamreader.readbyte + readcardinaltable = streamreader.readcardinaltable + readintegertable = streamreader.readintegertable + readfword = readshort + +end) local gsubhandlers = { } local gposhandlers = { } +readers.gsubhandlers = gsubhandlers +readers.gposhandlers = gposhandlers + +local helpers = readers.helpers +local gotodatatable = helpers.gotodatatable +local setvariabledata = helpers.setvariabledata + local lookupidoffset = -1 -- will become 1 when we migrate (only -1 for comparign with old) local classes = { @@ -121,6 +180,57 @@ local chaindirections = { reversechainedcontextsingle = -1, } +local function setmetrics(data,where,tag,d) + local w = data[where] + if w then + local v = w[tag] + if v then + -- it looks like some fonts set the value and not the delta + -- report("adding %s to %s.%s value %s",d,where,tag,v) + w[tag] = v + d + end + end +end + +local variabletags = { + hasc = function(data,d) setmetrics(data,"windowsmetrics","typoascender",d) end, + hdsc = function(data,d) setmetrics(data,"windowsmetrics","typodescender",d) end, + hlgp = function(data,d) setmetrics(data,"windowsmetrics","typolinegap",d) end, + hcla = function(data,d) setmetrics(data,"windowsmetrics","winascent",d) end, + hcld = function(data,d) setmetrics(data,"windowsmetrics","windescent",d) end, + vasc = function(data,d) setmetrics(data,"vhea not done","ascent",d) end, + vdsc = function(data,d) setmetrics(data,"vhea not done","descent",d) end, + vlgp = function(data,d) setmetrics(data,"vhea not done","linegap",d) end, + xhgt = function(data,d) setmetrics(data,"windowsmetrics","xheight",d) end, + cpht = function(data,d) setmetrics(data,"windowsmetrics","capheight",d) end, + sbxs = function(data,d) setmetrics(data,"windowsmetrics","subscriptxsize",d) end, + sbys = function(data,d) setmetrics(data,"windowsmetrics","subscriptysize",d) end, + sbxo = function(data,d) setmetrics(data,"windowsmetrics","subscriptxoffset",d) end, + sbyo = function(data,d) setmetrics(data,"windowsmetrics","subscriptyoffset",d) end, + spxs = function(data,d) setmetrics(data,"windowsmetrics","superscriptxsize",d) end, + spys = function(data,d) setmetrics(data,"windowsmetrics","superscriptysize",d) end, + spxo = function(data,d) setmetrics(data,"windowsmetrics","superscriptxoffset",d) end, + spyo = function(data,d) setmetrics(data,"windowsmetrics","superscriptyoffset",d) end, + strs = function(data,d) setmetrics(data,"windowsmetrics","strikeoutsize",d) end, + stro = function(data,d) setmetrics(data,"windowsmetrics","strikeoutpos",d) end, + unds = function(data,d) setmetrics(data,"postscript","underlineposition",d) end, + undo = function(data,d) setmetrics(data,"postscript","underlinethickness",d) end, +} + +local read_cardinal = { + streamreader.readcardinal1, + streamreader.readcardinal2, + streamreader.readcardinal3, + streamreader.readcardinal4, +} + +local read_integer = { + streamreader.readinteger1, + streamreader.readinteger2, + streamreader.readinteger3, + streamreader.readinteger4, +} + -- Traditionally we use these unique names (so that we can flatten the lookup list -- (we create subsets runtime) but I will adapt the old code to newer names. @@ -164,7 +274,7 @@ local lookupnames = { -- local lookupstate = setmetatableindex(function(t,k) -- local v = { } -- for kk, vv in next, lookupbits do --- if bittest(k,kk) then +-- if band(k,kk) ~= 0 then -- v[vv] = true -- end -- end @@ -174,36 +284,286 @@ local lookupnames = { local lookupflags = setmetatableindex(function(t,k) local v = { - bittest(k,0x0008) and true or false, -- ignoremarks - bittest(k,0x0004) and true or false, -- ignoreligatures - bittest(k,0x0002) and true or false, -- ignorebaseglyphs - bittest(k,0x0001) and true or false, -- r2l + band(k,0x0008) ~= 0 and true or false, -- ignoremarks + band(k,0x0004) ~= 0 and true or false, -- ignoreligatures + band(k,0x0002) ~= 0 and true or false, -- ignorebaseglyphs + band(k,0x0001) ~= 0 and true or false, -- r2l } t[k] = v return v end) +-- Variation stores: it's not entirely clear if the regions are a shared +-- resource (it looks like they are). Anyway, we play safe and use a +-- share. + +-- values can be anything the min/max permits so we can either think of +-- real values of a fraction along the axis (probably easier) + +-- wght:400,wdth:100,ital:1 + +local function axistofactors(str) + local t = settings_to_hash(str) + for k, v in next, t do + t[k] = tonumber(v) or v -- this also normalizes numbers itself + end + return t +end + +local hash = table.setmetatableindex(function(t,k) + local v = sequenced(axistofactors(k),",") + t[k] = v + return v +end) + +helpers.normalizedaxishash = hash + +local cleanname = fonts.names and fonts.names.cleanname or function(name) + return name and (gsub(lower(name),"[^%a%d]","")) or nil +end + +helpers.cleanname = cleanname + +function helpers.normalizedaxis(str) + return hash[str] or str +end + +-- contradicting spec ... (signs) so i'll check it and fix it once we have +-- proper fonts + +local function getaxisscale(segments,minimum,default,maximum,user) + -- + -- returns the right values cf example in standard + -- + if not minimum or not default or not maximum then + return false + end + if user < minimum then + user = minimum + elseif user > maximum then + user = maximum + end + if user < default then + default = - (default - user) / (default - minimum) + elseif user > default then + default = (user - default) / (maximum - default) + else + default = 0 + end + if not segments then + return default + end + local e + for i=1,#segments do + local s = segments[i] + if type(s) ~= "number" then + report("using default axis scale") + return default + elseif s[1] >= default then + if s[2] == default then + return default + else + e = i + break + end + end + end + if e then + local b = segments[e-1] + local e = segments[e] + return b[2] + (e[2] - b[2]) * (default - b[1]) / (e[1] - b[1]) + else + return false + end +end + +local function getfactors(data,instancespec) + if instancespec == true then + -- take default + elseif type(instancespec) ~= "string" or instancespec == "" then + return + end + local variabledata = data.variabledata + if not variabledata then + return + end + local instances = variabledata.instances + local axis = variabledata.axis + local segments = variabledata.segments + if instances and axis then + local values + if instancespec == true then + -- first instance: + -- values = instances[1].values + -- axis defaults: + values = { } + for i=1,#axis do + values[i] = { + -- axis = axis[i].tag, + value = axis[i].default, + } + end + + else + for i=1,#instances do + local instance = instances[i] + if cleanname(instance.subfamily) == instancespec then + values = instance.values + break + end + end + end + if values then + local factors = { } + for i=1,#axis do + local a = axis[i] + factors[i] = getaxisscale(segments,a.minimum,a.default,a.maximum,values[i].value) + end + return factors + end + local values = axistofactors(hash[instancespec] or instancespec) + if values then + local factors = { } + for i=1,#axis do + local a = axis[i] + local d = a.default + factors[i] = getaxisscale(segments,a.minimum,d,a.maximum,values[a.name or a.tag] or d) + end + return factors + end + end +end + +local function getscales(regions,factors) + local scales = { } + for i=1,#regions do + local region = regions[i] + local s = 1 + for j=1,#region do + local axis = region[j] + local f = factors[j] + local start = axis.start + local peak = axis.peak + local stop = axis.stop + -- get rid of these tests, false flag + if start > peak or peak > stop then + -- * 1 + elseif start < 0 and stop > 0 and peak ~= 0 then + -- * 1 + elseif peak == 0 then + -- * 1 + elseif f < start or f > stop then + -- * 0 + s = 0 + break + elseif f < peak then + -- s = - s * (f - start) / (peak - start) + s = s * (f - start) / (peak - start) + elseif f > peak then + s = s * (stop - f) / (stop - peak) + else + -- * 1 + end + end + scales[i] = s + end + return scales +end + +helpers.getaxisscale = getaxisscale +helpers.getfactors = getfactors +helpers.getscales = getscales +helpers.axistofactors = axistofactors + +local function readvariationdata(f,storeoffset,factors) -- store + local position = getposition(f) + setposition(f,storeoffset) + -- header + local format = readushort(f) + local regionoffset = storeoffset + readulong(f) + local nofdeltadata = readushort(f) + local deltadata = readcardinaltable(f,nofdeltadata,ulong) + -- regions + setposition(f,regionoffset) + local nofaxis = readushort(f) + local nofregions = readushort(f) + local regions = { } + for i=1,nofregions do -- 0 + local t = { } + for i=1,nofaxis do + t[i] = { -- maybe no keys, just 1..3 + start = read2dot14(f), + peak = read2dot14(f), + stop = read2dot14(f), + } + end + regions[i] = t + end + -- deltas + if factors then + for i=1,nofdeltadata do + setposition(f,storeoffset+deltadata[i]) + local nofdeltasets = readushort(f) + local nofshorts = readushort(f) + local nofregions = readushort(f) + local usedregions = { } + local deltas = { } + for i=1,nofregions do + usedregions[i] = regions[readushort(f)+1] + end + -- we could test before and save a for + for i=1,nofdeltasets do + local t = readintegertable(f,nofshorts,short) + for i=nofshorts+1,nofregions do + t[i] = readinteger(f) + end + deltas[i] = t + end + deltadata[i] = { + regions = usedregions, + deltas = deltas, + scales = factors and getscales(usedregions,factors) or nil, + } + end + end + setposition(f,position) + return regions, deltadata +end + +helpers.readvariationdata = readvariationdata + -- Beware: only use the simple variant if we don't set keys/values (otherwise too many entries). We -- could also have a variant that applies a function but there is no real benefit in this. local function readcoverage(f,offset,simple) setposition(f,offset) local coverageformat = readushort(f) - local coverage = { } if coverageformat == 1 then local nofcoverage = readushort(f) if simple then - for i=1,nofcoverage do - coverage[i] = readushort(f) + -- often 1 or 2 + if nofcoverage == 1 then + return { readushort(f) } + elseif nofcoverage == 2 then + return { readushort(f), readushort(f) } + else + return readcardinaltable(f,nofcoverage,ushort) end + elseif nofcoverage == 1 then + return { [readushort(f)] = 0 } + elseif nofcoverage == 2 then + return { [readushort(f)] = 0, [readushort(f)] = 1 } else + local coverage = { } for i=0,nofcoverage-1 do coverage[readushort(f)] = i -- index in record end + return coverage end elseif coverageformat == 2 then local nofranges = readushort(f) - local n = simple and 1 or 0 -- needs checking + local coverage = { } + local n = simple and 1 or 0 -- needs checking for i=1,nofranges do local firstindex = readushort(f) local lastindex = readushort(f) @@ -220,10 +580,11 @@ local function readcoverage(f,offset,simple) end end end + return coverage else report("unknown coverage format %a ",coverageformat) + return { } end - return coverage end local function readclassdef(f,offset,preset) @@ -283,36 +644,168 @@ end -- extra readers -local function readposition(f,format) +local skips = { [0] = + 0, -- ---- + 1, -- ---x + 1, -- --y- + 2, -- --yx + 1, -- -h-- + 2, -- -h-x + 2, -- -hy- + 3, -- -hyx + 2, -- v--x + 2, -- v-y- + 3, -- v-yx + 2, -- vh-- + 3, -- vh-x + 3, -- vhy- + 4, -- vhyx +} + +-- We can assume that 0 is nothing and in fact we can start at 1 as +-- usual in Lua to make sure of that. + +local function readvariation(f,offset) + local p = getposition(f) + setposition(f,offset) + local outer = readushort(f) + local inner = readushort(f) + local format = readushort(f) + setposition(f,p) + if format == 0x8000 then + return outer, inner + end +end + +local function readposition(f,format,mainoffset,getdelta) if format == 0 then - return nil + return false end - -- maybe fast test on 0x0001 + 0x0002 + 0x0004 + 0x0008 (profile first) - local x = bittest(format,0x0001) and readshort(f) or 0 -- placement - local y = bittest(format,0x0002) and readshort(f) or 0 -- placement - local h = bittest(format,0x0004) and readshort(f) or 0 -- advance - local v = bittest(format,0x0008) and readshort(f) or 0 -- advance - if x == 0 and y == 0 and h == 0 and v == 0 then - return nil + -- a few happen often + if format == 0x04 then + local h = readshort(f) + if h == 0 then + return true -- all zero + else + return { 0, 0, h, 0 } + end + end + if format == 0x05 then + local x = readshort(f) + local h = readshort(f) + if x == 0 and h == 0 then + return true -- all zero + else + return { x, 0, h, 0 } + end + end + if format == 0x44 then + local h = readshort(f) + if getdelta then + local d = readshort(f) -- short or ushort + if d > 0 then + local outer, inner = readvariation(f,mainoffset+d) + if outer then + h = h + getdelta(outer,inner) + end + end + else + skipshort(f,1) + end + if h == 0 then + return true -- all zero + else + return { 0, 0, h, 0 } + end + end + -- + -- todo: + -- + -- if format == 0x55 then + -- local x = readshort(f) + -- local h = readshort(f) + -- .... + -- end + -- + local x = band(format,0x1) ~= 0 and readshort(f) or 0 -- x placement + local y = band(format,0x2) ~= 0 and readshort(f) or 0 -- y placement + local h = band(format,0x4) ~= 0 and readshort(f) or 0 -- h advance + local v = band(format,0x8) ~= 0 and readshort(f) or 0 -- v advance + if format >= 0x10 then + local X = band(format,0x10) ~= 0 and skipshort(f) or 0 + local Y = band(format,0x20) ~= 0 and skipshort(f) or 0 + local H = band(format,0x40) ~= 0 and skipshort(f) or 0 + local V = band(format,0x80) ~= 0 and skipshort(f) or 0 + local s = skips[extract(format,4,4)] + if s > 0 then + skipshort(f,s) + end + if getdelta then + if X > 0 then + local outer, inner = readvariation(f,mainoffset+X) + if outer then + x = x + getdelta(outer,inner) + end + end + if Y > 0 then + local outer, inner = readvariation(f,mainoffset+Y) + if outer then + y = y + getdelta(outer,inner) + end + end + if H > 0 then + local outer, inner = readvariation(f,mainoffset+H) + if outer then + h = h + getdelta(outer,inner) + end + end + if V > 0 then + local outer, inner = readvariation(f,mainoffset+V) + if outer then + v = v + getdelta(outer,inner) + end + end + end + return { x, y, h, v } + elseif x == 0 and y == 0 and h == 0 and v == 0 then + return true -- all zero else return { x, y, h, v } end end -local function readanchor(f,offset) +local function readanchor(f,offset,getdelta) -- maybe also ignore 0's as in pos if not offset or offset == 0 then return nil -- false end setposition(f,offset) - local format = readshort(f) - if format == 0 then - report("invalid anchor format %i @ position %i",format,offset) - return false - elseif format > 3 then - report("unsupported anchor format %i @ position %i",format,offset) - return false + -- no need to skip as we position each + local format = readshort(f) -- 1: x y 2: x y index 3 x y X Y + local x = readshort(f) + local y = readshort(f) + if format == 3 then + if getdelta then + local X = readshort(f) + local Y = readshort(f) + if X > 0 then + local outer, inner = readvariation(f,offset+X) + if outer then + x = x + getdelta(outer,inner) + end + end + if Y > 0 then + local outer, inner = readvariation(f,offset+Y) + if outer then + y = y + getdelta(outer,inner) + end + end + else + skipshort(f,2) + end + return { x, y } -- , { xindex, yindex } + else + return { x, y } end - return { readshort(f), readshort(f) } end -- common handlers: inlining can be faster but we cache anyway @@ -325,23 +818,17 @@ local function readfirst(f,offset) return { readushort(f) } end -local function readarray(f,offset,first) +-- quite often 0, 1, 2 + +function readarray(f,offset) if offset then setposition(f,offset) end local n = readushort(f) - if first then - local t = { first } - for i=2,n do - t[i] = readushort(f) - end - return t, n + if n == 1 then + return { readushort(f) }, 1 elseif n > 0 then - local t = { } - for i=1,n do - t[i] = readushort(f) - end - return t, n + return readcardinaltable(f,n,ushort), n end end @@ -390,7 +877,13 @@ local function readlookuparray(f,noflookups,nofcurrent) if index > length then length = index end - lookups[index] = readushort(f) + 1 + local lookup = readushort(f) + 1 + local list = lookups[index] + if list then + list[#list+1] = lookup + else + lookups[index] = { lookup } + end end for index=1,length do if not lookups[index] then @@ -736,10 +1229,7 @@ function gsubhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofg elseif subtype == 2 then -- in streamreader a seek and fetch is faster than a temp table local coverage = readushort(f) local nofreplacements = readushort(f) - local replacements = { } - for i=1,nofreplacements do - replacements[i] = readushort(f) - end + local replacements = readcardinaltable(f,nofreplacements,ushort) local coverage = readcoverage(f,tableoffset + coverage) -- not simple as we need to set key/value anyway for index, newindex in next, coverage do newindex = newindex + 1 @@ -767,18 +1257,10 @@ local function sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp if subtype == 1 then local coverage = readushort(f) local nofsequence = readushort(f) - local sequences = { } - for i=1,nofsequence do - sequences[i] = readushort(f) - end + local sequences = readcardinaltable(f,nofsequence,ushort) for i=1,nofsequence do setposition(f,tableoffset + sequences[i]) - local n = readushort(f) - local s = { } - for i=1,n do - s[i] = readushort(f) - end - sequences[i] = s + sequences[i] = readcardinaltable(f,readushort(f),ushort) end local coverage = readcoverage(f,tableoffset + coverage) for index, newindex in next, coverage do @@ -813,19 +1295,20 @@ function gsubhandlers.ligature(f,fontdata,lookupid,lookupoffset,offset,glyphs,no if subtype == 1 then local coverage = readushort(f) local nofsets = readushort(f) - local ligatures = { } - for i=1,nofsets do - ligatures[i] = readushort(f) - end + local ligatures = readcardinaltable(f,nofsets,ushort) for i=1,nofsets do local offset = lookupoffset + offset + ligatures[i] setposition(f,offset) local n = readushort(f) - local l = { } - for i=1,n do - l[i] = offset + readushort(f) + if n == 1 then + ligatures[i] = { offset + readushort(f) } + else + local l = { } + for i=1,n do + l[i] = offset + readushort(f) + end + ligatures[i] = l end - ligatures[i] = l end local coverage = readcoverage(f,tableoffset + coverage) for index, newindex in next, coverage do @@ -883,12 +1366,14 @@ function gsubhandlers.reversechainedcontextsingle(f,fontdata,lookupid,lookupoffs before = readcoveragearray(f,tableoffset,before,true) after = readcoveragearray(f,tableoffset,after,true) return { - coverage = { - format = "reversecoverage", -- reversesub - before = before, - current = current, - after = after, - replacements = replacements, + format = "reversecoverage", -- reversesub + rules = { + { + before = before, + current = current, + after = after, + replacements = replacements, + } } }, "reversechainedcontextsingle" else @@ -898,20 +1383,21 @@ end -- gpos handlers -local function readpairsets(f,tableoffset,sets,format1,format2) +local function readpairsets(f,tableoffset,sets,format1,format2,mainoffset,getdelta) local done = { } for i=1,#sets do local offset = sets[i] local reused = done[offset] if not reused then - setposition(f,tableoffset + offset) + offset = tableoffset + offset + setposition(f,offset) local n = readushort(f) reused = { } for i=1,n do reused[i] = { readushort(f), -- second glyph id - readposition(f,format1), - readposition(f,format2) + readposition(f,format1,offset,getdelta), + readposition(f,format2,offset,getdelta), } end done[offset] = reused @@ -921,14 +1407,14 @@ local function readpairsets(f,tableoffset,sets,format1,format2) return sets end -local function readpairclasssets(f,nofclasses1,nofclasses2,format1,format2) +local function readpairclasssets(f,nofclasses1,nofclasses2,format1,format2,mainoffset,getdelta) local classlist1 = { } for i=1,nofclasses1 do local classlist2 = { } classlist1[i] = classlist2 for j=1,nofclasses2 do - local one = readposition(f,format1) - local two = readposition(f,format2) + local one = readposition(f,format1,mainoffset,getdelta) + local two = readposition(f,format2,mainoffset,getdelta) if one or two then classlist2[j] = { one, two } else @@ -944,34 +1430,35 @@ end function gposhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs) local tableoffset = lookupoffset + offset setposition(f,tableoffset) - local subtype = readushort(f) + local subtype = readushort(f) + local getdelta = fontdata.temporary.getdelta if subtype == 1 then local coverage = readushort(f) local format = readushort(f) - local value = readposition(f,format) + local value = readposition(f,format,tableoffset,getdelta) local coverage = readcoverage(f,tableoffset+coverage) for index, newindex in next, coverage do - coverage[index] = value + coverage[index] = value -- will be packed and shared anyway end return { - format = "pair", - coverage = coverage + format = "single", + coverage = coverage, } elseif subtype == 2 then local coverage = readushort(f) local format = readushort(f) - local values = { } local nofvalues = readushort(f) + local values = { } for i=1,nofvalues do - values[i] = readposition(f,format) + values[i] = readposition(f,format,tableoffset,getdelta) end local coverage = readcoverage(f,tableoffset+coverage) for index, newindex in next, coverage do coverage[index] = values[newindex+1] end return { - format = "pair", - coverage = coverage + format = "single", + coverage = coverage, } else report("unsupported subtype %a in %a positioning",subtype,"single") @@ -983,18 +1470,17 @@ end -- ValueFormat1 applies to the ValueRecord of the first glyph in each pair. ValueRecords for all first glyphs must use ValueFormat1. If ValueFormat1 is set to zero (0), the corresponding glyph has no ValueRecord and, therefore, should not be repositioned. -- ValueFormat2 applies to the ValueRecord of the second glyph in each pair. ValueRecords for all second glyphs must use ValueFormat2. If ValueFormat2 is set to null, then the second glyph of the pair is the “next” glyph for which a lookup should be performed. --- !!!!! this needs checking: when both false, we have no hit so then we might need to fall through - function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs) local tableoffset = lookupoffset + offset setposition(f,tableoffset) - local subtype = readushort(f) + local subtype = readushort(f) + local getdelta = fontdata.temporary.getdelta if subtype == 1 then local coverage = readushort(f) local format1 = readushort(f) local format2 = readushort(f) local sets = readarray(f) - sets = readpairsets(f,tableoffset,sets,format1,format2) + sets = readpairsets(f,tableoffset,sets,format1,format2,mainoffset,getdelta) coverage = readcoverage(f,tableoffset + coverage) for index, newindex in next, coverage do local set = sets[newindex+1] @@ -1006,9 +1492,9 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly local first = value[2] local second = value[3] if first or second then - hash[other] = { first, second } -- needs checking + hash[other] = { first, second or nil } -- needs checking else - hash[other] = nil + hash[other] = nil -- what if set, maybe warning end end end @@ -1016,7 +1502,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly end return { format = "pair", - coverage = coverage + coverage = coverage, } elseif subtype == 2 then local coverage = readushort(f) @@ -1026,7 +1512,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly local classdef2 = readushort(f) local nofclasses1 = readushort(f) -- incl class 0 local nofclasses2 = readushort(f) -- incl class 0 - local classlist = readpairclasssets(f,nofclasses1,nofclasses2,format1,format2) + local classlist = readpairclasssets(f,nofclasses1,nofclasses2,format1,format2,tableoffset,getdelta) coverage = readcoverage(f,tableoffset+coverage) classdef1 = readclassdef(f,tableoffset+classdef1,coverage) classdef2 = readclassdef(f,tableoffset+classdef2,nofglyphs) @@ -1042,7 +1528,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly local first = offsets[1] local second = offsets[2] if first or second then - hash[paired] = { first, second } + hash[paired] = { first, second or nil } else -- upto the next lookup for this combination end @@ -1054,7 +1540,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly end return { format = "pair", - coverage = usedcoverage + coverage = usedcoverage, } elseif subtype == 3 then report("yet unsupported subtype %a in %a positioning",subtype,"pair") @@ -1066,7 +1552,8 @@ end function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs) local tableoffset = lookupoffset + offset setposition(f,tableoffset) - local subtype = readushort(f) + local subtype = readushort(f) + local getdelta = fontdata.temporary.getdelta if subtype == 1 then local coverage = tableoffset + readushort(f) local nofrecords = readushort(f) @@ -1075,24 +1562,34 @@ function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nof local entry = readushort(f) local exit = readushort(f) records[i] = { - entry = entry ~= 0 and (tableoffset + entry) or false, - exit = exit ~= 0 and (tableoffset + exit ) or false, + -- entry = entry ~= 0 and (tableoffset + entry) or false, + -- exit = exit ~= 0 and (tableoffset + exit ) or nil, + entry ~= 0 and (tableoffset + entry) or false, + exit ~= 0 and (tableoffset + exit ) or nil, } end + -- slot 1 will become hash after loading and it must be unique because we + -- pack the tables (packed we turn the cc-* into a zero) + local cc = (fontdata.temporary.cursivecount or 0) + 1 + fontdata.temporary.cursivecount = cc + cc = "cc-" .. cc coverage = readcoverage(f,coverage) for i=1,nofrecords do local r = records[i] records[i] = { - 1, -- will become hash after loading (must be unique per lookup when packed) - readanchor(f,r.entry) or nil, - readanchor(f,r.exit ) or nil, + -- 1, + cc, + -- readanchor(f,r.entry,getdelta) or false, + -- readanchor(f,r.exit, getdelta) or nil, + readanchor(f,r[1],getdelta) or false, + readanchor(f,r[2],getdelta) or nil, } end for index, newindex in next, coverage do coverage[index] = records[newindex+1] end return { - coverage = coverage + coverage = coverage, } else report("unsupported subtype %a in %a positioning",subtype,"cursive") @@ -1102,7 +1599,8 @@ end local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,ligature) local tableoffset = lookupoffset + offset setposition(f,tableoffset) - local subtype = readushort(f) + local subtype = readushort(f) + local getdelta = fontdata.temporary.getdelta if subtype == 1 then -- we are one based, not zero local markcoverage = tableoffset + readushort(f) @@ -1121,17 +1619,12 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp local lastanchor = fontdata.lastanchor or 0 local usedanchors = { } -- --- local placeholder = (fontdata.markcount or 0) + 1 --- fontdata.markcount = placeholder --- placeholder = "m" .. placeholder - -- for i=1,nofmarkclasses do local class = readushort(f) + 1 local offset = readushort(f) if offset == 0 then markclasses[i] = false else --- markclasses[i] = { placeholder, class, markoffset + offset } markclasses[i] = { class, markoffset + offset } end usedanchors[class] = true @@ -1139,8 +1632,7 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp for i=1,nofmarkclasses do local mc = markclasses[i] if mc then --- mc[3] = readanchor(f,mc[3]) - mc[2] = readanchor(f,mc[2]) + mc[2] = readanchor(f,mc[2],getdelta) end end -- @@ -1194,7 +1686,7 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp local classes = components[c] if classes then for i=1,nofclasses do - local anchor = readanchor(f,classes[i]) + local anchor = readanchor(f,classes[i],getdelta) local bclass = baseclasses[i] local bentry = bclass[b] if bentry then @@ -1204,7 +1696,6 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp end end end --- components[i] = classes end end end @@ -1237,7 +1728,7 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp local r = baserecords[i] local b = basecoverage[i] for j=1,nofclasses do - baseclasses[j][b] = readanchor(f,r[j]) + baseclasses[j][b] = readanchor(f,r[j],getdelta) end end for index, newindex in next, markcoverage do @@ -1295,10 +1786,10 @@ do setposition(f,offset) local designsize = readushort(f) if designsize > 0 then -- we could also have a threshold - local fontstyle = readushort(f) - local guimenuid = readushort(f) - local minsize = readushort(f) - local maxsize = readushort(f) + local fontstyleid = readushort(f) + local guimenuid = readushort(f) + local minsize = readushort(f) + local maxsize = readushort(f) if minsize == 0 and maxsize == 0 and fontstyleid == 0 and guimenuid == 0 then minsize = designsize maxsize = designsize @@ -1328,6 +1819,10 @@ do end end + -- function plugins.rvrn(f,fontdata,tableoffset,feature) + -- -- todo, at least a message + -- end + -- feature order needs checking ... as we loop over a hash ... however, in the file -- they are sorted so order is not that relevant @@ -1461,10 +1956,15 @@ do local parameters = readushort(f) -- feature.parameters local noflookups = readushort(f) if noflookups > 0 then - local lookups = { } +-- local lookups = { } +-- feature.lookups = lookups +-- for j=1,noflookups do +-- lookups[j] = readushort(f) + 1 +-- end + local lookups = readcardinaltable(f,noflookups,ushort) feature.lookups = lookups for j=1,noflookups do - lookups[j] = readushort(f) + 1 + lookups[j] = lookups[j] + 1 end end if parameters > 0 then @@ -1480,14 +1980,11 @@ do local function readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder) setposition(f,lookupoffset) - local lookups = { } local noflookups = readushort(f) - for i=1,noflookups do - lookups[i] = readushort(f) - end + local lookups = readcardinaltable(f,noflookups,ushort) for lookupid=1,noflookups do - local index = lookups[lookupid] - setposition(f,lookupoffset+index) + local offset = lookups[lookupid] + setposition(f,lookupoffset+offset) local subtables = { } local typebits = readushort(f) local flagbits = readushort(f) @@ -1495,11 +1992,10 @@ do local lookupflags = lookupflags[flagbits] local nofsubtables = readushort(f) for j=1,nofsubtables do - local offset = readushort(f) - subtables[j] = offset + index -- we can probably put lookupoffset here + subtables[j] = offset + readushort(f) -- we can probably put lookupoffset here end -- which one wins? - local markclass = bittest(flagbits,0x0010) -- usemarkfilteringset + local markclass = band(flagbits,0x0010) ~= 0 -- usemarkfilteringset if markclass then markclass = readushort(f) -- + 1 end @@ -1521,26 +2017,12 @@ do return lookups end - local function readscriptoffsets(f,fontdata,tableoffset) - if not tableoffset then - return - end - setposition(f,tableoffset) - local version = readulong(f) - if version ~= 0x00010000 then - report("table version %a of %a is not supported (yet), maybe font %s is bad",version,what,fontdata.filename) - return - end - -- - return tableoffset + readushort(f), tableoffset + readushort(f), tableoffset + readushort(f) - end - local f_lookupname = formatters["%s_%s_%s"] - local function resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what) + local function resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what,tableoffset) - local sequences = fontdata.sequences or { } - local sublookuplist = fontdata.sublookups or { } + local sequences = fontdata.sequences or { } + local sublookuplist = fontdata.sublookups or { } fontdata.sequences = sequences fontdata.sublookups = sublookuplist local nofsublookups = #sublookuplist @@ -1555,6 +2037,8 @@ do local noflookups = #lookups local lookupprefix = sub(what,2,2) -- g[s|p][ub|os] -- + local usedlookups = false -- setmetatableindex("number") + -- for lookupid=1,noflookups do local lookup = lookups[lookupid] local lookuptype = lookup.type @@ -1565,7 +2049,7 @@ do local nofsubtables = #subtables local order = lookup.order local flags = lookup.flags - -- this is expected in th efont handler (faster checking) + -- this is expected in the font handler (faster checking) if flags[1] then flags[1] = "mark" end if flags[2] then flags[2] = "ligature" end if flags[3] then flags[3] = "base" end @@ -1593,10 +2077,11 @@ do local rules = step.rules if rules then for i=1,#rules do - local rule = rules[i] - local before = rule.before - local current = rule.current - local after = rule.after + local rule = rules[i] + local before = rule.before + local current = rule.current + local after = rule.after + local replacements = rule.replacements if before then for i=1,#before do before[i] = tohash(before[i]) @@ -1605,15 +2090,44 @@ do rule.before = reversed(before) end if current then - for i=1,#current do - current[i] = tohash(current[i]) + if replacements then + -- We have a reverse lookup and therefore only one current entry. We might need + -- to reverse the order in the before and after lists so that needs checking. + local first = current[1] + local hash = { } + local repl = { } + for i=1,#first do + local c = first[i] + hash[c] = true + repl[c] = replacements[i] + end + rule.current = { hash } + rule.replacements = repl + else + for i=1,#current do + current[i] = tohash(current[i]) + end end + else + -- weird lookup end if after then for i=1,#after do after[i] = tohash(after[i]) end end + if usedlookups then + local lookups = rule.lookups + if lookups then + for k, v in next, lookups do + if v then + for k, v in next, v do + usedlookups[v] = usedlookups[v] + 1 + end + end + end + end + end end end end @@ -1665,6 +2179,10 @@ do end end + if usedlookups then + report("used %s lookups: % t",what,sortedkeys(usedlookups)) + end + -- When we have a context, we have sublookups that resolve into lookups for which we need to -- know the type. We split the main lookuptable in two parts: sequences (the main lookups) -- and subtable lookups (simple specs with no features). We could keep them merged and might do @@ -1698,56 +2216,63 @@ do report_issue(i,what,sequence,"empty") rule.lookups = nil else - -- we can have holes in rlookups - -- for index, lookupid in sortedhash(rlookups) do + -- we can have holes in rlookups flagged false and we can have multiple lookups + -- applied (first time seen in seguemj) local length = #rlookups --- for index in next, rlookups do --- if index > length then --- length = index --- end --- end for index=1,length do - local lookupid = rlookups[index] - if lookupid then - local h = sublookuphash[lookupid] - if not h then - -- here we have a lookup that is used independent as well - -- as in another one - local lookup = lookups[lookupid] - if lookup then - local d = lookup.done - if d then - nofsublookups = nofsublookups + 1 - -- report("registering %i as sublookup %i",lookupid,nofsublookups) - h = { - index = nofsublookups, -- handy for tracing - name = f_lookupname(lookupprefix,"d",lookupid+lookupidoffset), - derived = true, -- handy for tracing - steps = d.steps, - nofsteps = d.nofsteps, - type = d.lookuptype, - markclass = d.markclass or nil, - flags = d.flags, - -- chain = d.chain, - } - sublookuplist[nofsublookups] = copy(h) -- we repack later - sublookuphash[lookupid] = nofsublookups - sublookupcheck[lookupid] = 1 - h = nofsublookups --- patch by Hans + local lookuplist = rlookups[index] + if lookuplist then + local length = #lookuplist + local found = { } + local noffound = 0 + for index=1,length do + local lookupid = lookuplist[index] + if lookupid then + local h = sublookuphash[lookupid] + if not h then + -- here we have a lookup that is used independent as well + -- as in another one + local lookup = lookups[lookupid] + if lookup then + local d = lookup.done + if d then + nofsublookups = nofsublookups + 1 + -- report("registering %i as sublookup %i",lookupid,nofsublookups) + local l = { + index = nofsublookups, -- handy for tracing + name = f_lookupname(lookupprefix,"d",lookupid+lookupidoffset), + derived = true, -- handy for tracing + steps = d.steps, + nofsteps = d.nofsteps, + type = d.lookuptype or "gsub_single", -- todo: check type + markclass = d.markclass or nil, + flags = d.flags, + -- chain = d.chain, + } + sublookuplist[nofsublookups] = copy(l) -- we repack later + sublookuphash[lookupid] = nofsublookups + sublookupcheck[lookupid] = 1 + h = nofsublookups + else + report_issue(i,what,sequence,"missing") + rule.lookups = nil + break + end + else + report_issue(i,what,sequence,"bad") + rule.lookups = nil + break + end else - report_issue(i,what,sequence,"missing") - rule.lookups = nil - break + sublookupcheck[lookupid] = sublookupcheck[lookupid] + 1 + end + if h then + noffound = noffound + 1 + found[noffound] = h end - else - report_issue(i,what,sequence,"bad") - rule.lookups = nil - break end - else - sublookupcheck[lookupid] = sublookupcheck[lookupid] + 1 end - rlookups[index] = h or false + rlookups[index] = noffound > 0 and found or false else rlookups[index] = false end @@ -1764,45 +2289,122 @@ do if n == 0 and t ~= "extension" then local d = l.done report("%s lookup %s of type %a is not used",what,d and d.name or l.name,t) - -- inspect(l) end end end - local function readscripts(f,fontdata,what,lookuptypes,lookuphandlers,lookupstoo) - local datatable = fontdata.tables[what] - if not datatable then - return - end - local tableoffset = datatable.offset - if not tableoffset then - return + local function loadvariations(f,fontdata,variationsoffset,lookuptypes,featurehash,featureorder) + setposition(f,variationsoffset) + local version = readulong(f) + local nofrecords = readulong(f) + local records = { } + for i=1,nofrecords do + records[i] = { + conditions = readulong(f), + substitutions = readulong(f), + } end - local scriptoffset, featureoffset, lookupoffset = readscriptoffsets(f,fontdata,tableoffset) - if not scriptoffset then - return + for i=1,nofrecords do + local record = records[i] + local offset = record.conditions + if offset == 0 then + record.condition = nil + record.matchtype = "always" + else + local offset = variationsoffset+offset + setposition(f,offset) + local nofconditions = readushort(f) + local conditions = { } + for i=1,nofconditions do + conditions[i] = offset + readulong(f) + end + record.conditions = conditions + record.matchtype = "condition" + end end - -- - local scripts = readscriplan(f,fontdata,scriptoffset) - local features = readfeatures(f,fontdata,featureoffset) - -- - local scriptlangs, featurehash, featureorder = reorderfeatures(fontdata,scripts,features) - -- - if fontdata.features then - fontdata.features[what] = scriptlangs - else - fontdata.features = { [what] = scriptlangs } + for i=1,nofrecords do + local record = records[i] + if record.matchtype == "condition" then + local conditions = record.conditions + for i=1,#conditions do + setposition(f,conditions[i]) + conditions[i] = { + format = readushort(f), + axis = readushort(f), + minvalue = read2dot14(f), + maxvalue = read2dot14(f), + } + end + end end - -- - if not lookupstoo then - return + + for i=1,nofrecords do + local record = records[i] + local offset = record.substitutions + if offset == 0 then + record.substitutions = { } + else + setposition(f,variationsoffset + offset) + local version = readulong(f) + local nofsubstitutions = readushort(f) + local substitutions = { } + for i=1,nofsubstitutions do + substitutions[readushort(f)] = readulong(f) + end + for index, alternates in sortedhash(substitutions) do + if index == 0 then + record.substitutions = false + else + local tableoffset = variationsoffset + offset + alternates + setposition(f,tableoffset) + local parameters = readulong(f) -- feature parameters + local noflookups = readushort(f) + local lookups = readcardinaltable(f,noflookups,ushort) -- not sure what to do with these + -- todo : resolve to proper lookups + record.substitutions = lookups + end + end + end end - -- - local lookups = readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder) - -- - if lookups then - resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what) + setvariabledata(fontdata,"features",records) + end + + local function readscripts(f,fontdata,what,lookuptypes,lookuphandlers,lookupstoo) + local tableoffset = gotodatatable(f,fontdata,what,true) + if tableoffset then + local version = readulong(f) + local scriptoffset = tableoffset + readushort(f) + local featureoffset = tableoffset + readushort(f) + local lookupoffset = tableoffset + readushort(f) + local variationsoffset = version > 0x00010000 and (tableoffset + readulong(f)) or 0 + if not scriptoffset then + return + end + local scripts = readscriplan(f,fontdata,scriptoffset) + local features = readfeatures(f,fontdata,featureoffset) + -- + local scriptlangs, featurehash, featureorder = reorderfeatures(fontdata,scripts,features) + -- + if fontdata.features then + fontdata.features[what] = scriptlangs + else + fontdata.features = { [what] = scriptlangs } + end + -- + if not lookupstoo then + return + end + -- + local lookups = readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder) + -- + if lookups then + resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what,tableoffset) + end + -- + if variationsoffset > 0 then + loadvariations(f,fontdata,variationsoffset,lookuptypes,featurehash,featureorder) + end end end @@ -1822,61 +2424,64 @@ do report("ignoring global kern table using gpos kern feature") return end - report("adding global kern table as gpos feature %a",name) setposition(f,datatable.offset) local version = readushort(f) local noftables = readushort(f) - local kerns = setmetatableindex("table") - for i=1,noftables do - local version = readushort(f) - local length = readushort(f) - local coverage = readushort(f) - -- bit 8-15 of coverage: format 0 or 2 - local format = bit32.rshift(coverage,8) -- is this ok? - if format == 0 then - local nofpairs = readushort(f) - local searchrange = readushort(f) - local entryselector = readushort(f) - local rangeshift = readushort(f) - for i=1,nofpairs do - kerns[readushort(f)][readushort(f)] = readfword(f) + if noftables > 1 then + report("adding global kern table as gpos feature %a",name) + local kerns = setmetatableindex("table") + for i=1,noftables do + local version = readushort(f) + local length = readushort(f) + local coverage = readushort(f) + -- bit 8-15 of coverage: format 0 or 2 + local format = rshift(coverage,8) -- is this ok + if format == 0 then + local nofpairs = readushort(f) + local searchrange = readushort(f) + local entryselector = readushort(f) + local rangeshift = readushort(f) + for i=1,nofpairs do + kerns[readushort(f)][readushort(f)] = readfword(f) + end + elseif format == 2 then + -- apple specific so let's ignore it + else + -- not supported by ms end - elseif format == 2 then - -- apple specific so let's ignore it - else - -- not supported by ms end - end - local feature = { dflt = { dflt = true } } - if not features then - fontdata.features = { gpos = { [name] = feature } } - elseif not gposfeatures then - fontdata.features.gpos = { [name] = feature } - else - gposfeatures[name] = feature - end - local sequences = fontdata.sequences - if not sequences then - sequences = { } - fontdata.sequences = sequences - end - local nofsequences = #sequences + 1 - sequences[nofsequences] = { - index = nofsequences, - name = name, - steps = { - { - coverage = kerns, - format = "kern", + local feature = { dflt = { dflt = true } } + if not features then + fontdata.features = { gpos = { [name] = feature } } + elseif not gposfeatures then + fontdata.features.gpos = { [name] = feature } + else + gposfeatures[name] = feature + end + local sequences = fontdata.sequences + if not sequences then + sequences = { } + fontdata.sequences = sequences + end + local nofsequences = #sequences + 1 + sequences[nofsequences] = { + index = nofsequences, + name = name, + steps = { + { + coverage = kerns, + format = "kern", + }, }, - }, - nofsteps = 1, - type = "gpos_pair", - -- type = "gpos_single", -- maybe better - flags = { false, false, false, false }, - order = { name }, - features = { [name] = feature }, - } + nofsteps = 1, + type = "gpos_pair", + flags = { false, false, false, false }, + order = { name }, + features = { [name] = feature }, + } + else + report("ignoring empty kern table of feature %a",name) + end end function readers.gsub(f,fontdata,specification) @@ -1897,26 +2502,30 @@ do end function readers.gdef(f,fontdata,specification) - if specification.glyphs then - local datatable = fontdata.tables.gdef - if datatable then - local tableoffset = datatable.offset - setposition(f,tableoffset) - local version = readulong(f) - local classoffset = tableoffset + readushort(f) - local attachmentoffset = tableoffset + readushort(f) -- used for bitmaps - local ligaturecarets = tableoffset + readushort(f) -- used in editors (maybe nice for tracing) - local markclassoffset = tableoffset + readushort(f) - local marksetsoffset = version == 0x00010002 and (tableoffset + readushort(f)) - local glyphs = fontdata.glyphs - local marks = { } - local markclasses = setmetatableindex("table") - local marksets = setmetatableindex("table") - fontdata.marks = marks - fontdata.markclasses = markclasses - fontdata.marksets = marksets - -- class definitions - setposition(f,classoffset) + if not specification.glyphs then + return + end + local datatable = fontdata.tables.gdef + if datatable then + local tableoffset = datatable.offset + setposition(f,tableoffset) + local version = readulong(f) + local classoffset = readushort(f) + local attachmentoffset = readushort(f) -- used for bitmaps + local ligaturecarets = readushort(f) -- used in editors (maybe nice for tracing) + local markclassoffset = readushort(f) + local marksetsoffset = version >= 0x00010002 and readushort(f) or 0 + local varsetsoffset = version >= 0x00010003 and readulong(f) or 0 + local glyphs = fontdata.glyphs + local marks = { } + local markclasses = setmetatableindex("table") + local marksets = setmetatableindex("table") + fontdata.marks = marks + fontdata.markclasses = markclasses + fontdata.marksets = marksets + -- class definitions + if classoffset ~= 0 then + setposition(f,tableoffset + classoffset) local classformat = readushort(f) if classformat == 1 then local firstindex = readushort(f) @@ -1944,8 +2553,10 @@ function readers.gdef(f,fontdata,specification) end end end - -- mark classes - setposition(f,markclassoffset) + end + -- mark classes + if markclassoffset ~= 0 then + setposition(f,tableoffset + markclassoffset) local classformat = readushort(f) if classformat == 1 then local firstindex = readushort(f) @@ -1964,24 +2575,55 @@ function readers.gdef(f,fontdata,specification) end end end - -- mark sets : todo: just make the same as class sets above - if marksetsoffset and marksetsoffset > tableoffset then -- zero offset means no table - setposition(f,marksetsoffset) - local format = readushort(f) - if format == 1 then - local nofsets = readushort(f) - local sets = { } - for i=1,nofsets do - sets[i] = readulong(f) + end + -- mark sets : todo: just make the same as class sets above + if marksetsoffset ~= 0 then + marksetsoffset = tableoffset + marksetsoffset + setposition(f,marksetsoffset) + local format = readushort(f) + if format == 1 then + local nofsets = readushort(f) + local sets = readcardinaltable(f,nofsets,ulong) + for i=1,nofsets do + local offset = sets[i] + if offset ~= 0 then + marksets[i] = readcoverage(f,marksetsoffset+offset) end - for i=1,nofsets do - local offset = sets[i] - if offset ~= 0 then - marksets[i] = readcoverage(f,marksetsoffset+offset) + end + end + end + + local factors = specification.factors + + if (specification.variable or factors) and varsetsoffset ~= 0 then + + local regions, deltas = readvariationdata(f,tableoffset+varsetsoffset,factors) + + -- setvariabledata(fontdata,"gregions",regions) + + if factors then + fontdata.temporary.getdelta = function(outer,inner) + local delta = deltas[outer+1] + if delta then + local d = delta.deltas[inner+1] + if d then + local scales = delta.scales + local dd = 0 + for i=1,#scales do + local di = d[i] + if di then + dd = dd + scales[i] * di + else + break + end + end + return round(dd) end end + return 0 end end + end end end @@ -2178,14 +2820,8 @@ local function readmathvariants(f,fontdata,offset) local hcoverage = readushort(f) local vnofglyphs = readushort(f) local hnofglyphs = readushort(f) - local vconstruction = { } - local hconstruction = { } - for i=1,vnofglyphs do - vconstruction[i] = readushort(f) - end - for i=1,hnofglyphs do - hconstruction[i] = readushort(f) - end + local vconstruction = readcardinaltable(f,vnofglyphs,ushort) + local hconstruction = readcardinaltable(f,hnofglyphs,ushort) fontdata.mathconstants.MinConnectorOverlap = minoverlap @@ -2243,7 +2879,7 @@ local function readmathvariants(f,fontdata,offset) advance = readushort(f), } local flags = readushort(f) - if bittest(flags,0x0001) then + if band(flags,0x0001) ~= 0 then p.extender = 1 -- true end parts[i] = p @@ -2270,173 +2906,719 @@ local function readmathvariants(f,fontdata,offset) end function readers.math(f,fontdata,specification) - if specification.glyphs then - local datatable = fontdata.tables.math - if datatable then - local tableoffset = datatable.offset - setposition(f,tableoffset) - local version = readulong(f) - if version ~= 0x00010000 then - report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"math",fontdata.filename) - return - end - local constants = readushort(f) - local glyphinfo = readushort(f) - local variants = readushort(f) - if constants == 0 then - report("the math table of %a has no constants",fontdata.filename) - else - readmathconstants(f,fontdata,tableoffset+constants) + local tableoffset = gotodatatable(f,fontdata,"math",specification.glyphs) + if tableoffset then + local version = readulong(f) + -- if version ~= 0x00010000 then + -- report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"math",fontdata.filename) + -- return + -- end + local constants = readushort(f) + local glyphinfo = readushort(f) + local variants = readushort(f) + if constants == 0 then + report("the math table of %a has no constants",fontdata.filename) + else + readmathconstants(f,fontdata,tableoffset+constants) + end + if glyphinfo ~= 0 then + readmathglyphinfo(f,fontdata,tableoffset+glyphinfo) + end + if variants ~= 0 then + readmathvariants(f,fontdata,tableoffset+variants) + end + end +end + +function readers.colr(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"colr",specification.glyphs) + if tableoffset then + local version = readushort(f) + if version ~= 0 then + report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"colr",fontdata.filename) + return + end + if not fontdata.tables.cpal then + report("color table %a in font %a has no mandate %a table","colr",fontdata.filename,"cpal") + fontdata.colorpalettes = { } + end + local glyphs = fontdata.glyphs + local nofglyphs = readushort(f) + local baseoffset = readulong(f) + local layeroffset = readulong(f) + local noflayers = readushort(f) + local layerrecords = { } + local maxclass = 0 + -- The special value 0xFFFF is foreground (but we index from 1). It + -- more looks like indices into a palette so 'class' is a better name + -- than 'palette'. + setposition(f,tableoffset + layeroffset) + for i=1,noflayers do + local slot = readushort(f) + local class = readushort(f) + if class < 0xFFFF then + class = class + 1 + if class > maxclass then + maxclass = class + end end - if glyphinfo ~= 0 then - readmathglyphinfo(f,fontdata,tableoffset+glyphinfo) + layerrecords[i] = { + slot = slot, + class = class, + } + end + fontdata.maxcolorclass = maxclass + setposition(f,tableoffset + baseoffset) + for i=0,nofglyphs-1 do + local glyphindex = readushort(f) + local firstlayer = readushort(f) + local noflayers = readushort(f) + local t = { } + for i=1,noflayers do + t[i] = layerrecords[firstlayer+i] end - if variants ~= 0 then - readmathvariants(f,fontdata,tableoffset+variants) + glyphs[glyphindex].colors = t + end + end + fontdata.hascolor = true +end + +function readers.cpal(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"cpal",specification.glyphs) + if tableoffset then + local version = readushort(f) + -- if version > 1 then + -- report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"cpal",fontdata.filename) + -- return + -- end + local nofpaletteentries = readushort(f) + local nofpalettes = readushort(f) + local nofcolorrecords = readushort(f) + local firstcoloroffset = readulong(f) + local colorrecords = { } + local palettes = readcardinaltable(f,nofpalettes,ushort) + if version == 1 then + -- used for guis + local palettettypesoffset = readulong(f) + local palettelabelsoffset = readulong(f) + local paletteentryoffset = readulong(f) + end + setposition(f,tableoffset+firstcoloroffset) + for i=1,nofcolorrecords do + local b, g, r, a = readbytes(f,4) + colorrecords[i] = { + r, g, b, a ~= 255 and a or nil, + } + end + for i=1,nofpalettes do + local p = { } + local o = palettes[i] + for j=1,nofpaletteentries do + p[j] = colorrecords[o+j] end + palettes[i] = p end + fontdata.colorpalettes = palettes end end -function readers.colr(f,fontdata,specification) - local datatable = fontdata.tables.colr - if datatable then - if specification.glyphs then - local tableoffset = datatable.offset - setposition(f,tableoffset) - local version = readushort(f) - if version ~= 0 then - report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"colr",fontdata.filename) - return +function readers.svg(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"svg",specification.glyphs) + if tableoffset then + local version = readushort(f) + -- if version ~= 0 then + -- report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"svg",fontdata.filename) + -- return + -- end + local glyphs = fontdata.glyphs + local indexoffset = tableoffset + readulong(f) + local reserved = readulong(f) + setposition(f,indexoffset) + local nofentries = readushort(f) + local entries = { } + for i=1,nofentries do + entries[i] = { + first = readushort(f), + last = readushort(f), + offset = indexoffset + readulong(f), + length = readulong(f), + } + end + for i=1,nofentries do + local entry = entries[i] + setposition(f,entry.offset) + entries[i] = { + first = entry.first, + last = entry.last, + data = readstring(f,entry.length) + } + end + fontdata.svgshapes = entries + end + fontdata.hascolor = true +end + +function readers.sbix(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"sbix",specification.glyphs) + if tableoffset then + local version = readushort(f) + local flags = readushort(f) + local nofstrikes = readulong(f) + local strikes = { } + local nofglyphs = fontdata.nofglyphs + for i=1,nofstrikes do + strikes[i] = readulong(f) + end + -- if true then + local shapes = { } + local done = 0 + for i=1,nofstrikes do + local strikeoffset = strikes[i] + tableoffset + setposition(f,strikeoffset) + strikes[i] = { + ppem = readushort(f), + ppi = readushort(f), + offset = strikeoffset + } end - if not fontdata.tables.cpal then - report("color table %a in font %a has no mandate %a table","colr",fontdata.filename,"cpal") - fontdata.colorpalettes = { } - end - local glyphs = fontdata.glyphs - local nofglyphs = readushort(f) - local baseoffset = readulong(f) - local layeroffset = readulong(f) - local noflayers = readushort(f) - local layerrecords = { } - local maxclass = 0 - -- The special value 0xFFFF is foreground (but we index from 1). It - -- more looks like indices into a palette so 'class' is a better name - -- than 'palette'. - setposition(f,tableoffset + layeroffset) - for i=1,noflayers do - local slot = readushort(f) - local class = readushort(f) - if class < 0xFFFF then - class = class + 1 - if class > maxclass then - maxclass = class + -- highest first + sort(strikes,function(a,b) + if b.ppem == a.ppem then + return b.ppi < a.ppi + else + return b.ppem < a.ppem + end + end) + local glyphs = { } + for i=1,nofstrikes do + local strike = strikes[i] + local strikeppem = strike.ppem + local strikeppi = strike.ppi + local strikeoffset = strike.offset + setposition(f,strikeoffset) + for i=0,nofglyphs do + glyphs[i] = readulong(f) + end + local glyphoffset = glyphs[0] + for i=0,nofglyphs-1 do + local nextoffset = glyphs[i+1] + if not shapes[i] then + local datasize = nextoffset - glyphoffset + if datasize > 0 then + setposition(f,strikeoffset + glyphoffset) + shapes[i] = { + x = readshort(f), + y = readshort(f), + tag = readtag(f), -- maybe for tracing + data = readstring(f,datasize-8), + ppem = strikeppem, -- not used, for tracing + ppi = strikeppi, -- not used, for tracing + } + done = done + 1 + if done == nofglyphs then + break + end + end end + glyphoffset = nextoffset end - layerrecords[i] = { - slot = slot, - class = class, + end + fontdata.sbixshapes = shapes + -- else + -- for i=1,nofstrikes do + -- local strikeoffset = strikes[i] + tableoffset + -- setposition(f,strikeoffset) + -- local glyphs = { } + -- strikes[i] = { + -- ppem = readushort(f), + -- ppi = readushort(f), + -- glyphs = glyphs, + -- } + -- for i=0,nofglyphs do + -- glyphs[i] = readulong(f) + -- end + -- local glyphoffset = glyphs[0] + -- for i=0,nofglyphs-1 do + -- local nextoffset = glyphs[i+1] + -- local datasize = nextoffset - glyphoffset + -- if datasize > 0 then + -- setposition(f,strikeoffset + glyphoffset) + -- glyphs[i] = { + -- x = readshort(f), + -- y = readshort(f), + -- tag = readtag(f), + -- data = readstring(f,datasize-8) + -- } + -- glyphoffset = nextoffset + -- end + -- end + -- end + -- fontdata.sbixshapes = strikes + -- end + end +end + +-- function readers.cblc(f,fontdata,specification) +-- local tableoffset = gotodatatable(f,fontdata,"cblc",specification.glyphs) +-- if tableoffset then +-- end +-- end +-- +-- function readers.cbdt(f,fontdata,specification) +-- local tableoffset = gotodatatable(f,fontdata,"ctdt",specification.glyphs) +-- if tableoffset then +-- +-- local function getmetrics(f) +-- return { +-- ascender = readinteger(f), +-- descender = readinteger(f), +-- widthmax = readcardinal(f), +-- caretslopedumerator = readinteger(f), +-- caretslopedenominator = readinteger(f), +-- caretoffset = readinteger(f), +-- minorigin = readinteger(f), +-- minadvance = readinteger(f), +-- maxbefore = readinteger(f), +-- minafter = readinteger(f), +-- pad1 = readinteger(f), +-- pad2 = readinteger(f), +-- } +-- end +-- +-- local majorversion = readushort(f) +-- local minorversion = readushort(f) +-- local nofsizetables = readulong(f) +-- local sizetable = { } +-- for i=1,nofsizetables do +-- sizetable[i] = { +-- subtables = readulong(f), +-- indexsize = readulong(f), +-- nofsubtables = readulong(f), +-- colorref = readulong(f), +-- hormetrics = getmetrics(f), +-- vermetrics = getmetrics(f), +-- firstindex = readushort(f), +-- lastindex = readushort(f), +-- ppemx = readbyte(f), +-- ppemy = readbyte(f), +-- bitdepth = readbyte(f), +-- flags = readbyte(f), +-- } +-- end +-- +-- sort(sizetable,function(a,b) +-- if b.ppemx == a.ppemx then +-- return b.bitdepth < a.bitdepth +-- else +-- return b.ppemx < a.ppemx +-- end +-- end) +-- +-- local shapes = { } +-- +-- for i=1,nofsizetables do +-- local s = sizetables[i] +-- for j=firstindex,lastindex do +-- if not shapes[j] then +-- shapes[j] = { +-- i +-- } +-- end +-- end +-- end +-- +-- inspect(shapes) +-- +-- end +-- end + +-- function readers.ebdt(f,fontdata,specification) +-- if specification.glyphs then +-- end +-- end + +-- function readers.ebsc(f,fontdata,specification) +-- if specification.glyphs then +-- end +-- end + +-- function readers.eblc(f,fontdata,specification) +-- if specification.glyphs then +-- end +-- end + +-- + AVAR : optional +-- + CFF2 : otf outlines +-- - CVAR : ttf hinting, not needed +-- + FVAR : the variations +-- + GVAR : ttf outline changes +-- + HVAR : horizontal changes +-- + MVAR : metric changes +-- + STAT : relations within fonts +-- * VVAR : vertical changes +-- +-- * BASE : extra baseline adjustments +-- - GASP : not needed +-- + GDEF : not needed (carets) +-- + GPOS : adapted device tables (needed?) +-- + GSUB : new table +-- + NAME : 25 added + +function readers.stat(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"stat",true) -- specification.variable + if tableoffset then + local extras = fontdata.extras + local version = readulong(f) -- 0x00010000 + local axissize = readushort(f) + local nofaxis = readushort(f) + local axisoffset = readulong(f) + local nofvalues = readushort(f) + local valuesoffset = readulong(f) + local fallbackname = extras[readushort(f)] -- beta fonts mess up + local axis = { } + local values = { } + setposition(f,tableoffset+axisoffset) + for i=1,nofaxis do + local tag = readtag(f) + axis[i] = { + tag = tag, + name = lower(extras[readushort(f)] or tag), + ordering = readushort(f), -- maybe gaps + variants = { } + } + end + -- flags: + -- + -- 0x0001 : OlderSiblingFontAttribute + -- 0x0002 : ElidableAxisValueName + -- 0xFFFC : reservedFlags + -- + setposition(f,tableoffset+valuesoffset) + for i=1,nofvalues do + values[i] = readushort(f) + end + for i=1,nofvalues do + setposition(f,tableoffset + valuesoffset + values[i]) + local format = readushort(f) + local index = readushort(f) + 1 + local flags = readushort(f) + local name = lower(extras[readushort(f)] or "no name") + local value = readfixed(f) + local variant + if format == 1 then + variant = { + flags = flags, + name = name, + value = value, + } + elseif format == 2 then + variant = { + flags = flags, + name = name, + value = value, + minimum = readfixed(f), + maximum = readfixed(f), + } + elseif format == 3 then + variant = { + flags = flags, + name = name, + value = value, + link = readfixed(f), } end - fontdata.maxcolorclass = maxclass - setposition(f,tableoffset + baseoffset) - for i=0,nofglyphs-1 do - local glyphindex = readushort(f) - local firstlayer = readushort(f) - local noflayers = readushort(f) - local t = { } - for i=1,noflayers do - t[i] = layerrecords[firstlayer+i] + insert(axis[index].variants,variant) + end + sort(axis,function(a,b) + return a.ordering < b.ordering + end) + for i=1,#axis do + local a = axis[i] + sort(a.variants,function(a,b) + return a.name < b.name + end) + a.ordering = nil + end + setvariabledata(fontdata,"designaxis",axis) + setvariabledata(fontdata,"fallbackname",fallbackname) + end +end + +-- The avar table is optional and used in combination with fvar. Given the +-- detailed explanation about bad values we expect the worst and do some +-- checking. + +function readers.avar(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"avar",true) -- specification.variable + if tableoffset then + + local function collect() + local nofvalues = readushort(f) + local values = { } + local lastfrom = false + local lastto = false + for i=1,nofvalues do + local f, t = read2dot14(f), read2dot14(f) + if lastfrom and f <= lastfrom then + -- ignore + elseif lastto and t >= lastto then + -- ignore + else + values[#values+1] = { f, t } + lastfrom, lastto = f, t + end + end + nofvalues = #values + if nofvalues > 2 then + local some = values[1] + if some[1] == -1 and some[2] == -1 then + some = values[nofvalues] + if some[1] == 1 and some[2] == 1 then + for i=2,nofvalues-1 do + some = values[i] + if some[1] == 0 and some[2] == 0 then + return values + end + end + end end - glyphs[glyphindex].colors = t end + return false end - fontdata.hascolor = true + + local majorversion = readushort(f) -- 1 + local minorversion = readushort(f) -- 0 + local reserved = readushort(f) + local nofaxis = readushort(f) + local segments = { } + for i=1,nofaxis do + segments[i] = collect() + end + setvariabledata(fontdata,"segments",segments) end end -function readers.cpal(f,fontdata,specification) - if specification.glyphs then - local datatable = fontdata.tables.cpal - if datatable then - local tableoffset = datatable.offset - setposition(f,tableoffset) - local version = readushort(f) - if version > 1 then - report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"cpal",fontdata.filename) - return +function readers.fvar(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"fvar",true) -- specification.variable or specification.instancenames + if tableoffset then + local version = readulong(f) -- 1.0 + local offsettoaxis = tableoffset + readushort(f) + local reserved = skipshort(f) + -- pair 1 + local nofaxis = readushort(f) + local sizeofaxis = readushort(f) + -- pair 2 + local nofinstances = readushort(f) + local sizeofinstances = readushort(f) + -- + local extras = fontdata.extras + local axis = { } + local instances = { } + -- + setposition(f,offsettoaxis) + -- + for i=1,nofaxis do + axis[i] = { + tag = readtag(f), -- ital opsz slnt wdth wght + minimum = readfixed(f), + default = readfixed(f), + maximum = readfixed(f), + flags = readushort(f), + name = lower(extras[readushort(f)] or "bad name"), + } + local n = sizeofaxis - 20 + if n > 0 then + skipbytes(f,n) + elseif n < 0 then + -- error end - local nofpaletteentries = readushort(f) - local nofpalettes = readushort(f) - local nofcolorrecords = readushort(f) - local firstcoloroffset = readulong(f) - local colorrecords = { } - local palettes = { } - for i=1,nofpalettes do - palettes[i] = readushort(f) - end - if version == 1 then - -- used for guis - local palettettypesoffset = readulong(f) - local palettelabelsoffset = readulong(f) - local paletteentryoffset = readulong(f) - end - setposition(f,tableoffset+firstcoloroffset) - for i=1,nofcolorrecords do - local b, g, r, a = readbytes(f,4) - colorrecords[i] = { - r, g, b, a ~= 255 and a or nil, + end + -- + local nofbytes = 2 + 2 + 2 + nofaxis * 4 + local readpsname = nofbytes <= sizeofinstances + local skippable = sizeofinstances - nofbytes + for i=1,nofinstances do + local subfamid = readushort(f) + local flags = readushort(f) -- 0, not used yet + local values = { } + for i=1,nofaxis do + values[i] = { + axis = axis[i].tag, + value = readfixed(f), } end - for i=1,nofpalettes do - local p = { } - local o = palettes[i] - for j=1,nofpaletteentries do - p[j] = colorrecords[o+j] - end - palettes[i] = p + local psnameid = readpsname and readushort(f) or 0xFFFF + if subfamid == 2 or subfamid == 17 then + -- okay + elseif subfamid == 0xFFFF then + subfamid = nil + elseif subfamid <= 256 or subfamid >= 32768 then + subfamid = nil -- actually an error + end + if psnameid == 6 then + -- okay + elseif psnameid == 0xFFFF then + psnameid = nil + elseif psnameid <= 256 or psnameid >= 32768 then + psnameid = nil -- actually an error + end + instances[i] = { + -- flags = flags, + subfamily = extras[subfamid], + psname = psnameid and extras[psnameid] or nil, + values = values, + } + if skippable > 0 then + skipbytes(f,skippable) end - fontdata.colorpalettes = palettes end + setvariabledata(fontdata,"axis",axis) + setvariabledata(fontdata,"instances",instances) end end -function readers.svg(f,fontdata,specification) - local datatable = fontdata.tables.svg - if datatable then - if specification.glyphs then - local tableoffset = datatable.offset - setposition(f,tableoffset) - local version = readushort(f) - if version ~= 0 then - report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"svg",fontdata.filename) - return - end - local glyphs = fontdata.glyphs - local indexoffset = tableoffset + readulong(f) - local reserved = readulong(f) - setposition(f,indexoffset) - local nofentries = readushort(f) - local entries = { } - for i=1,nofentries do - entries[i] = { - first = readushort(f), - last = readushort(f), - offset = indexoffset + readulong(f), - length = readulong(f), - } +function readers.hvar(f,fontdata,specification) + local factors = specification.factors + if not factors then + return + end + local tableoffset = gotodatatable(f,fontdata,"hvar",specification.variable) + if not tableoffset then + return + end + + local version = readulong(f) -- 1.0 + local variationoffset = tableoffset + readulong(f) -- the store + local advanceoffset = tableoffset + readulong(f) + local lsboffset = tableoffset + readulong(f) + local rsboffset = tableoffset + readulong(f) + + local regions = { } + local variations = { } + local innerindex = { } -- size is mapcount + local outerindex = { } -- size is mapcount + + if variationoffset > 0 then + regions, deltas = readvariationdata(f,variationoffset,factors) + end + + if not regions then + -- for now .. what to do ? + return + end + + if advanceoffset > 0 then + -- + -- innerIndexBitCountMask = 0x000F + -- mapEntrySizeMask = 0x0030 + -- reservedFlags = 0xFFC0 + -- + -- outerIndex = entry >> ((entryFormat & innerIndexBitCountMask) + 1) + -- innerIndex = entry & ((1 << ((entryFormat & innerIndexBitCountMask) + 1)) - 1) + -- + setposition(f,advanceoffset) + local format = readushort(f) -- todo: check + local mapcount = readushort(f) + local entrysize = rshift(band(format,0x0030),4) + 1 + local nofinnerbits = band(format,0x000F) + 1 -- n of inner bits + local innermask = lshift(1,nofinnerbits) - 1 + local readcardinal = read_cardinal[entrysize] -- 1 upto 4 bytes + for i=0,mapcount-1 do + local mapdata = readcardinal(f) + outerindex[i] = rshift(mapdata,nofinnerbits) + innerindex[i] = band(mapdata,innermask) + end + -- use last entry when no match i + setvariabledata(fontdata,"hvarwidths",true) + local glyphs = fontdata.glyphs + for i=0,fontdata.nofglyphs-1 do + local glyph = glyphs[i] + local width = glyph.width + if width then + local outer = outerindex[i] or 0 + local inner = innerindex[i] or i + if outer and inner then -- not needed + local delta = deltas[outer+1] + if delta then + local d = delta.deltas[inner+1] + if d then + local scales = delta.scales + local deltaw = 0 + for i=1,#scales do + local di = d[i] + if di then + deltaw = deltaw + scales[i] * di + else + break -- can't happen + end + end +-- report("index: %i, outer: %i, inner: %i, deltas: %|t, scales: %|t, width: %i, delta %i", +-- i,outer,inner,d,scales,width,round(deltaw)) + glyph.width = width + round(deltaw) + end + end + end end - for i=1,nofentries do - local entry = entries[i] - setposition(f,entry.offset) - entries[i] = { - first = entry.first, - last = entry.last, - data = readstring(f,entry.length) - } + end + + end + + -- if lsboffset > 0 then + -- -- we don't use left side bearings + -- end + + -- if rsboffset > 0 then + -- -- we don't use right side bearings + -- end + + -- setvariabledata(fontdata,"hregions",regions) + +end + +function readers.vvar(f,fontdata,specification) + if not specification.variable then + return + end +end + +function readers.mvar(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"mvar",specification.variable) + if tableoffset then + local version = readulong(f) -- 1.0 + local reserved = skipshort(f,1) + local recordsize = readushort(f) + local nofrecords = readushort(f) + local offsettostore = tableoffset + readushort(f) + local dimensions = { } + local factors = specification.factors + if factors then + local regions, deltas = readvariationdata(f,offsettostore,factors) + for i=1,nofrecords do + local tag = readtag(f) + local var = variabletags[tag] + if var then + local outer = readushort(f) + local inner = readushort(f) + local delta = deltas[outer+1] + if delta then + local d = delta.deltas[inner+1] + if d then + local scales = delta.scales + local dd = 0 + for i=1,#scales do + dd = dd + scales[i] * d[i] + end + var(fontdata,round(dd)) + end + end + else + skipshort(f,2) + end + if recordsize > 8 then -- 4 + 2 + 2 + skipbytes(recordsize-8) + end end - fontdata.svgshapes = entries end - fontdata.hascolor = true + -- setvariabledata(fontdata,"mregions",regions) end end |