summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua
diff options
context:
space:
mode:
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.lua2108
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