summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Hagen <pragma@wxs.nl>2017-03-20 18:51:53 +0100
committerContext Git Mirror Bot <phg42.2a@gmail.com>2017-03-20 18:51:53 +0100
commit8e0089484acf80066e7393b1245d59dda211be09 (patch)
treea444a174ea8de55816cb4a9c3def00fb4521e7d8
parent8f5c555274eb48fcaaa3d7f340ee77710846fb7e (diff)
downloadcontext-8e0089484acf80066e7393b1245d59dda211be09.tar.gz
2017-03-20 17:38:00
-rw-r--r--doc/context/documents/general/manuals/luatex.pdfbin1096445 -> 1098418 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-cs.pdfbin799404 -> 799421 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-de.pdfbin801721 -> 801322 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-en.pdfbin804052 -> 804058 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-fr.pdfbin799223 -> 799227 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-it.pdfbin799567 -> 799575 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-nl.pdfbin796492 -> 796501 bytes
-rw-r--r--doc/context/documents/general/qrcs/setup-ro.pdfbin796431 -> 796445 bytes
-rw-r--r--scripts/context/lua/mtx-fonts.lua7
-rw-r--r--scripts/context/lua/mtxrun.lua49
-rw-r--r--scripts/context/stubs/mswin/mtxrun.lua49
-rw-r--r--scripts/context/stubs/unix/mtxrun49
-rw-r--r--scripts/context/stubs/win64/mtxrun.lua49
-rw-r--r--tex/context/base/mkii/cont-new.mkii2
-rw-r--r--tex/context/base/mkii/context.mkii2
-rw-r--r--tex/context/base/mkiv/cldf-int.lua4
-rw-r--r--tex/context/base/mkiv/cont-new.mkiv2
-rw-r--r--tex/context/base/mkiv/context.mkiv2
-rw-r--r--tex/context/base/mkiv/font-cff.lua894
-rw-r--r--tex/context/base/mkiv/font-con.lua38
-rw-r--r--tex/context/base/mkiv/font-ctx.lua269
-rw-r--r--tex/context/base/mkiv/font-def.lua22
-rw-r--r--tex/context/base/mkiv/font-dsp.lua1591
-rw-r--r--tex/context/base/mkiv/font-fil.mkvi4
-rw-r--r--tex/context/base/mkiv/font-lib.mkvi8
-rw-r--r--tex/context/base/mkiv/font-mps.lua8
-rw-r--r--tex/context/base/mkiv/font-onr.lua2
-rw-r--r--tex/context/base/mkiv/font-otc.lua27
-rw-r--r--tex/context/base/mkiv/font-oti.lua100
-rw-r--r--tex/context/base/mkiv/font-otl.lua80
-rw-r--r--tex/context/base/mkiv/font-otr.lua747
-rw-r--r--tex/context/base/mkiv/font-ott.lua7
-rw-r--r--tex/context/base/mkiv/font-oup.lua132
-rw-r--r--tex/context/base/mkiv/font-shp.lua232
-rw-r--r--tex/context/base/mkiv/font-syn.lua42
-rw-r--r--tex/context/base/mkiv/font-ttf.lua1060
-rw-r--r--tex/context/base/mkiv/l-lpeg.lua8
-rw-r--r--tex/context/base/mkiv/l-md5.lua3
-rw-r--r--tex/context/base/mkiv/l-string.lua5
-rw-r--r--tex/context/base/mkiv/lpdf-ini.lua28
-rw-r--r--tex/context/base/mkiv/luat-cbk.lua8
-rw-r--r--tex/context/base/mkiv/lxml-tab.lua7
-rw-r--r--tex/context/base/mkiv/m-fonts-plugins.mkiv406
-rw-r--r--tex/context/base/mkiv/mlib-pdf.lua2
-rw-r--r--tex/context/base/mkiv/node-tra.lua10
-rw-r--r--tex/context/base/mkiv/status-files.pdfbin25672 -> 25633 bytes
-rw-r--r--tex/context/base/mkiv/status-lua.pdfbin420238 -> 422419 bytes
-rw-r--r--tex/context/base/mkiv/util-fil.lua50
-rw-r--r--tex/context/interface/mkiv/i-context.pdfbin804052 -> 804058 bytes
-rw-r--r--tex/context/interface/mkiv/i-readme.pdfbin60772 -> 60772 bytes
-rw-r--r--tex/context/modules/mkiv/m-ipsum.mkiv1
-rw-r--r--tex/context/modules/mkiv/s-fonts-shapes.lua2
-rw-r--r--tex/context/modules/mkiv/s-fonts-shapes.mkiv2
-rw-r--r--tex/context/modules/mkiv/s-fonts-variable.lua264
-rw-r--r--tex/context/modules/mkiv/s-fonts-variable.mkiv46
-rw-r--r--tex/generic/context/luatex/luatex-core.lua91
-rw-r--r--tex/generic/context/luatex/luatex-fonts-merged.lua3757
57 files changed, 7670 insertions, 2498 deletions
diff --git a/doc/context/documents/general/manuals/luatex.pdf b/doc/context/documents/general/manuals/luatex.pdf
index c682888d2..934341614 100644
--- a/doc/context/documents/general/manuals/luatex.pdf
+++ b/doc/context/documents/general/manuals/luatex.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-cs.pdf b/doc/context/documents/general/qrcs/setup-cs.pdf
index 7683fb9e6..dfd26b22c 100644
--- a/doc/context/documents/general/qrcs/setup-cs.pdf
+++ b/doc/context/documents/general/qrcs/setup-cs.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-de.pdf b/doc/context/documents/general/qrcs/setup-de.pdf
index b50c29075..9b138a715 100644
--- a/doc/context/documents/general/qrcs/setup-de.pdf
+++ b/doc/context/documents/general/qrcs/setup-de.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-en.pdf b/doc/context/documents/general/qrcs/setup-en.pdf
index ef5687741..496d93c5f 100644
--- a/doc/context/documents/general/qrcs/setup-en.pdf
+++ b/doc/context/documents/general/qrcs/setup-en.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-fr.pdf b/doc/context/documents/general/qrcs/setup-fr.pdf
index 4a07f3a30..ed0cb493a 100644
--- a/doc/context/documents/general/qrcs/setup-fr.pdf
+++ b/doc/context/documents/general/qrcs/setup-fr.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-it.pdf b/doc/context/documents/general/qrcs/setup-it.pdf
index f57e4b4e9..52419a337 100644
--- a/doc/context/documents/general/qrcs/setup-it.pdf
+++ b/doc/context/documents/general/qrcs/setup-it.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-nl.pdf b/doc/context/documents/general/qrcs/setup-nl.pdf
index 2b66731db..122590485 100644
--- a/doc/context/documents/general/qrcs/setup-nl.pdf
+++ b/doc/context/documents/general/qrcs/setup-nl.pdf
Binary files differ
diff --git a/doc/context/documents/general/qrcs/setup-ro.pdf b/doc/context/documents/general/qrcs/setup-ro.pdf
index 84ef00ede..ce6fe75a1 100644
--- a/doc/context/documents/general/qrcs/setup-ro.pdf
+++ b/doc/context/documents/general/qrcs/setup-ro.pdf
Binary files differ
diff --git a/scripts/context/lua/mtx-fonts.lua b/scripts/context/lua/mtx-fonts.lua
index ee15bc31d..aedaf1dbf 100644
--- a/scripts/context/lua/mtx-fonts.lua
+++ b/scripts/context/lua/mtx-fonts.lua
@@ -15,7 +15,7 @@ local nameonly, basename, joinpath, collapsepath = file.nameonly, file.basename,
local lower = string.lower
local otfversion = 2.826
-local otlversion = 3.027
+local otlversion = 3.028
local helpinfo = [[
<?xml version="1.0"?>
@@ -265,6 +265,11 @@ local function showfeatures(tag,specification)
indeed("subfont : %s",subfont(specification.subfont))
indeed("fweight : %s",fontweight(specification.fontweight))
-- maybe more
+ local instancenames = specification.instancenames
+ if instancenames then
+ report()
+ indeed("instances : % t",instancenames)
+ end
local features = fonts.helpers.getfeatures(specification.filename,not getargument("nosave"))
if features then
for what, v in table.sortedhash(features) do
diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua
index 3cd84e865..ba5970a51 100644
--- a/scripts/context/lua/mtxrun.lua
+++ b/scripts/context/lua/mtxrun.lua
@@ -737,7 +737,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["l-lpeg"] = package.loaded["l-lpeg"] or true
--- original size: 37644, stripped down to: 20029
+-- original size: 37748, stripped down to: 20111
if not modules then modules={} end modules ['l-lpeg']={
version=1.001,
@@ -829,6 +829,7 @@ patterns.nonwhitespace=nonwhitespace
local stripper=spacer^0*C((spacer^0*nonspacer^1)^0)
local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0)
local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0))
+local nospacer=Cs((whitespace^1/""+nonwhitespace^1)^0)
local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0)
local e_collapser=Cs((whitespace^1*P(-1)/""+nonwhitespace^1+whitespace^1/" ")^0)
local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0)
@@ -838,6 +839,7 @@ local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0)
patterns.stripper=stripper
patterns.fullstripper=fullstripper
patterns.collapser=collapser
+patterns.nospacer=nospacer
patterns.b_collapser=b_collapser
patterns.m_collapser=m_collapser
patterns.e_collapser=e_collapser
@@ -1543,7 +1545,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["l-string"] = package.loaded["l-string"] or true
--- original size: 6296, stripped down to: 3225
+-- original size: 6419, stripped down to: 3339
if not modules then modules={} end modules ['l-string']={
version=1.001,
@@ -1581,6 +1583,7 @@ end
local stripper=patterns.stripper
local fullstripper=patterns.fullstripper
local collapser=patterns.collapser
+local nospacer=patterns.nospacer
local longtostring=patterns.longtostring
function string.strip(str)
return str and lpegmatch(stripper,str) or ""
@@ -1591,6 +1594,9 @@ end
function string.collapsespaces(str)
return str and lpegmatch(collapser,str) or ""
end
+function string.nospaces(str)
+ return str and lpegmatch(nospacer,str) or ""
+end
function string.longtostring(str)
return str and lpegmatch(longtostring,str) or ""
end
@@ -4111,7 +4117,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["l-md5"] = package.loaded["l-md5"] or true
--- original size: 3248, stripped down to: 2266
+-- original size: 3309, stripped down to: 2314
if not modules then modules={} end modules ['l-md5']={
version=1.001,
@@ -4141,6 +4147,8 @@ do
if not md5.HEX then function md5.HEX(str) if str then return lpegmatch(bytestoHEX,md5sum(str)) end end end
if not md5.hex then function md5.hex(str) if str then return lpegmatch(bytestohex,md5sum(str)) end end end
if not md5.dec then function md5.dec(str) if str then return lpegmatch(bytestodec,md5sum(str)) end end end
+ md5.sumhexa=md5.hex
+ md5.sumHEXA=md5.HEX
end
end
function file.needsupdating(oldname,newname,threshold)
@@ -7021,7 +7029,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["util-fil"] = package.loaded["util-fil"] or true
--- original size: 7427, stripped down to: 5702
+-- original size: 7038, stripped down to: 5659
if not modules then modules={} end modules ['util-fil']={
version=1.001,
@@ -7122,21 +7130,12 @@ function files.readcardinal2le(f)
end
function files.readinteger2(f)
local a,b=byte(f:read(2),1,2)
- local n=0x100*a+b
- if n>=0x8000 then
- return n-0x10000
+ if a>=0x80 then
+ return 0x100*a+b-0x10000
else
- return n
+ return 0x100*a+b
end
end
- function files.readinteger2(f)
- local a,b=byte(f:read(2),1,2)
- if a>=0x80 then
- return 0x100*a+b-0x10000
- else
- return 0x100*a+b
- end
- end
function files.readinteger2le(f)
local b,a=byte(f:read(2),1,2)
local n=0x100*a+b
@@ -7218,8 +7217,13 @@ if extract then
local band=bit32.band
function files.read2dot14(f)
local a,b=byte(f:read(2),1,2)
- local n=0x100*a+b
- return extract(n,14,2)+(band(n,0x3FFF)/16384.0)
+ if a>=0x80 then
+ local n=-(0x100*a+b)
+ return-(extract(n,14,2)+(band(n,0x3FFF)/16384.0))
+ else
+ local n=0x100*a+b
+ return (extract(n,14,2)+(band(n,0x3FFF)/16384.0))
+ end
end
end
function files.skipshort(f,n)
@@ -11248,7 +11252,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["lxml-tab"] = package.loaded["lxml-tab"] or true
--- original size: 56187, stripped down to: 35523
+-- original size: 56300, stripped down to: 35539
if not modules then modules={} end modules ['lxml-tab']={
version=1.001,
@@ -11390,7 +11394,7 @@ local function add_empty(spacing,namespace,tag)
tg=tag,
at=at,
dt={},
- ni=nil,
+ ni=nt,
__p__=top
}
dt[nt]=t
@@ -11440,6 +11444,7 @@ local function add_end(spacing,namespace,tag)
dt=top.dt
nt=#dt+1
dt[nt]=toclose
+ toclose.ni=nt
if toclose.at.xmlns then
remove(xmlns)
end
@@ -20228,8 +20233,8 @@ end -- of closure
-- used libraries : l-lua.lua l-sandbox.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-tpl.lua util-sbx.lua util-mrg.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua
-- skipped libraries : -
--- original bytes : 836086
--- stripped bytes : 304336
+-- original bytes : 836098
+-- stripped bytes : 304131
-- end library merge
diff --git a/scripts/context/stubs/mswin/mtxrun.lua b/scripts/context/stubs/mswin/mtxrun.lua
index 3cd84e865..ba5970a51 100644
--- a/scripts/context/stubs/mswin/mtxrun.lua
+++ b/scripts/context/stubs/mswin/mtxrun.lua
@@ -737,7 +737,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["l-lpeg"] = package.loaded["l-lpeg"] or true
--- original size: 37644, stripped down to: 20029
+-- original size: 37748, stripped down to: 20111
if not modules then modules={} end modules ['l-lpeg']={
version=1.001,
@@ -829,6 +829,7 @@ patterns.nonwhitespace=nonwhitespace
local stripper=spacer^0*C((spacer^0*nonspacer^1)^0)
local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0)
local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0))
+local nospacer=Cs((whitespace^1/""+nonwhitespace^1)^0)
local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0)
local e_collapser=Cs((whitespace^1*P(-1)/""+nonwhitespace^1+whitespace^1/" ")^0)
local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0)
@@ -838,6 +839,7 @@ local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0)
patterns.stripper=stripper
patterns.fullstripper=fullstripper
patterns.collapser=collapser
+patterns.nospacer=nospacer
patterns.b_collapser=b_collapser
patterns.m_collapser=m_collapser
patterns.e_collapser=e_collapser
@@ -1543,7 +1545,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["l-string"] = package.loaded["l-string"] or true
--- original size: 6296, stripped down to: 3225
+-- original size: 6419, stripped down to: 3339
if not modules then modules={} end modules ['l-string']={
version=1.001,
@@ -1581,6 +1583,7 @@ end
local stripper=patterns.stripper
local fullstripper=patterns.fullstripper
local collapser=patterns.collapser
+local nospacer=patterns.nospacer
local longtostring=patterns.longtostring
function string.strip(str)
return str and lpegmatch(stripper,str) or ""
@@ -1591,6 +1594,9 @@ end
function string.collapsespaces(str)
return str and lpegmatch(collapser,str) or ""
end
+function string.nospaces(str)
+ return str and lpegmatch(nospacer,str) or ""
+end
function string.longtostring(str)
return str and lpegmatch(longtostring,str) or ""
end
@@ -4111,7 +4117,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["l-md5"] = package.loaded["l-md5"] or true
--- original size: 3248, stripped down to: 2266
+-- original size: 3309, stripped down to: 2314
if not modules then modules={} end modules ['l-md5']={
version=1.001,
@@ -4141,6 +4147,8 @@ do
if not md5.HEX then function md5.HEX(str) if str then return lpegmatch(bytestoHEX,md5sum(str)) end end end
if not md5.hex then function md5.hex(str) if str then return lpegmatch(bytestohex,md5sum(str)) end end end
if not md5.dec then function md5.dec(str) if str then return lpegmatch(bytestodec,md5sum(str)) end end end
+ md5.sumhexa=md5.hex
+ md5.sumHEXA=md5.HEX
end
end
function file.needsupdating(oldname,newname,threshold)
@@ -7021,7 +7029,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["util-fil"] = package.loaded["util-fil"] or true
--- original size: 7427, stripped down to: 5702
+-- original size: 7038, stripped down to: 5659
if not modules then modules={} end modules ['util-fil']={
version=1.001,
@@ -7122,21 +7130,12 @@ function files.readcardinal2le(f)
end
function files.readinteger2(f)
local a,b=byte(f:read(2),1,2)
- local n=0x100*a+b
- if n>=0x8000 then
- return n-0x10000
+ if a>=0x80 then
+ return 0x100*a+b-0x10000
else
- return n
+ return 0x100*a+b
end
end
- function files.readinteger2(f)
- local a,b=byte(f:read(2),1,2)
- if a>=0x80 then
- return 0x100*a+b-0x10000
- else
- return 0x100*a+b
- end
- end
function files.readinteger2le(f)
local b,a=byte(f:read(2),1,2)
local n=0x100*a+b
@@ -7218,8 +7217,13 @@ if extract then
local band=bit32.band
function files.read2dot14(f)
local a,b=byte(f:read(2),1,2)
- local n=0x100*a+b
- return extract(n,14,2)+(band(n,0x3FFF)/16384.0)
+ if a>=0x80 then
+ local n=-(0x100*a+b)
+ return-(extract(n,14,2)+(band(n,0x3FFF)/16384.0))
+ else
+ local n=0x100*a+b
+ return (extract(n,14,2)+(band(n,0x3FFF)/16384.0))
+ end
end
end
function files.skipshort(f,n)
@@ -11248,7 +11252,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["lxml-tab"] = package.loaded["lxml-tab"] or true
--- original size: 56187, stripped down to: 35523
+-- original size: 56300, stripped down to: 35539
if not modules then modules={} end modules ['lxml-tab']={
version=1.001,
@@ -11390,7 +11394,7 @@ local function add_empty(spacing,namespace,tag)
tg=tag,
at=at,
dt={},
- ni=nil,
+ ni=nt,
__p__=top
}
dt[nt]=t
@@ -11440,6 +11444,7 @@ local function add_end(spacing,namespace,tag)
dt=top.dt
nt=#dt+1
dt[nt]=toclose
+ toclose.ni=nt
if toclose.at.xmlns then
remove(xmlns)
end
@@ -20228,8 +20233,8 @@ end -- of closure
-- used libraries : l-lua.lua l-sandbox.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-tpl.lua util-sbx.lua util-mrg.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua
-- skipped libraries : -
--- original bytes : 836086
--- stripped bytes : 304336
+-- original bytes : 836098
+-- stripped bytes : 304131
-- end library merge
diff --git a/scripts/context/stubs/unix/mtxrun b/scripts/context/stubs/unix/mtxrun
index 3cd84e865..ba5970a51 100644
--- a/scripts/context/stubs/unix/mtxrun
+++ b/scripts/context/stubs/unix/mtxrun
@@ -737,7 +737,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["l-lpeg"] = package.loaded["l-lpeg"] or true
--- original size: 37644, stripped down to: 20029
+-- original size: 37748, stripped down to: 20111
if not modules then modules={} end modules ['l-lpeg']={
version=1.001,
@@ -829,6 +829,7 @@ patterns.nonwhitespace=nonwhitespace
local stripper=spacer^0*C((spacer^0*nonspacer^1)^0)
local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0)
local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0))
+local nospacer=Cs((whitespace^1/""+nonwhitespace^1)^0)
local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0)
local e_collapser=Cs((whitespace^1*P(-1)/""+nonwhitespace^1+whitespace^1/" ")^0)
local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0)
@@ -838,6 +839,7 @@ local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0)
patterns.stripper=stripper
patterns.fullstripper=fullstripper
patterns.collapser=collapser
+patterns.nospacer=nospacer
patterns.b_collapser=b_collapser
patterns.m_collapser=m_collapser
patterns.e_collapser=e_collapser
@@ -1543,7 +1545,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["l-string"] = package.loaded["l-string"] or true
--- original size: 6296, stripped down to: 3225
+-- original size: 6419, stripped down to: 3339
if not modules then modules={} end modules ['l-string']={
version=1.001,
@@ -1581,6 +1583,7 @@ end
local stripper=patterns.stripper
local fullstripper=patterns.fullstripper
local collapser=patterns.collapser
+local nospacer=patterns.nospacer
local longtostring=patterns.longtostring
function string.strip(str)
return str and lpegmatch(stripper,str) or ""
@@ -1591,6 +1594,9 @@ end
function string.collapsespaces(str)
return str and lpegmatch(collapser,str) or ""
end
+function string.nospaces(str)
+ return str and lpegmatch(nospacer,str) or ""
+end
function string.longtostring(str)
return str and lpegmatch(longtostring,str) or ""
end
@@ -4111,7 +4117,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["l-md5"] = package.loaded["l-md5"] or true
--- original size: 3248, stripped down to: 2266
+-- original size: 3309, stripped down to: 2314
if not modules then modules={} end modules ['l-md5']={
version=1.001,
@@ -4141,6 +4147,8 @@ do
if not md5.HEX then function md5.HEX(str) if str then return lpegmatch(bytestoHEX,md5sum(str)) end end end
if not md5.hex then function md5.hex(str) if str then return lpegmatch(bytestohex,md5sum(str)) end end end
if not md5.dec then function md5.dec(str) if str then return lpegmatch(bytestodec,md5sum(str)) end end end
+ md5.sumhexa=md5.hex
+ md5.sumHEXA=md5.HEX
end
end
function file.needsupdating(oldname,newname,threshold)
@@ -7021,7 +7029,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["util-fil"] = package.loaded["util-fil"] or true
--- original size: 7427, stripped down to: 5702
+-- original size: 7038, stripped down to: 5659
if not modules then modules={} end modules ['util-fil']={
version=1.001,
@@ -7122,21 +7130,12 @@ function files.readcardinal2le(f)
end
function files.readinteger2(f)
local a,b=byte(f:read(2),1,2)
- local n=0x100*a+b
- if n>=0x8000 then
- return n-0x10000
+ if a>=0x80 then
+ return 0x100*a+b-0x10000
else
- return n
+ return 0x100*a+b
end
end
- function files.readinteger2(f)
- local a,b=byte(f:read(2),1,2)
- if a>=0x80 then
- return 0x100*a+b-0x10000
- else
- return 0x100*a+b
- end
- end
function files.readinteger2le(f)
local b,a=byte(f:read(2),1,2)
local n=0x100*a+b
@@ -7218,8 +7217,13 @@ if extract then
local band=bit32.band
function files.read2dot14(f)
local a,b=byte(f:read(2),1,2)
- local n=0x100*a+b
- return extract(n,14,2)+(band(n,0x3FFF)/16384.0)
+ if a>=0x80 then
+ local n=-(0x100*a+b)
+ return-(extract(n,14,2)+(band(n,0x3FFF)/16384.0))
+ else
+ local n=0x100*a+b
+ return (extract(n,14,2)+(band(n,0x3FFF)/16384.0))
+ end
end
end
function files.skipshort(f,n)
@@ -11248,7 +11252,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["lxml-tab"] = package.loaded["lxml-tab"] or true
--- original size: 56187, stripped down to: 35523
+-- original size: 56300, stripped down to: 35539
if not modules then modules={} end modules ['lxml-tab']={
version=1.001,
@@ -11390,7 +11394,7 @@ local function add_empty(spacing,namespace,tag)
tg=tag,
at=at,
dt={},
- ni=nil,
+ ni=nt,
__p__=top
}
dt[nt]=t
@@ -11440,6 +11444,7 @@ local function add_end(spacing,namespace,tag)
dt=top.dt
nt=#dt+1
dt[nt]=toclose
+ toclose.ni=nt
if toclose.at.xmlns then
remove(xmlns)
end
@@ -20228,8 +20233,8 @@ end -- of closure
-- used libraries : l-lua.lua l-sandbox.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-tpl.lua util-sbx.lua util-mrg.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua
-- skipped libraries : -
--- original bytes : 836086
--- stripped bytes : 304336
+-- original bytes : 836098
+-- stripped bytes : 304131
-- end library merge
diff --git a/scripts/context/stubs/win64/mtxrun.lua b/scripts/context/stubs/win64/mtxrun.lua
index 3cd84e865..ba5970a51 100644
--- a/scripts/context/stubs/win64/mtxrun.lua
+++ b/scripts/context/stubs/win64/mtxrun.lua
@@ -737,7 +737,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["l-lpeg"] = package.loaded["l-lpeg"] or true
--- original size: 37644, stripped down to: 20029
+-- original size: 37748, stripped down to: 20111
if not modules then modules={} end modules ['l-lpeg']={
version=1.001,
@@ -829,6 +829,7 @@ patterns.nonwhitespace=nonwhitespace
local stripper=spacer^0*C((spacer^0*nonspacer^1)^0)
local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0)
local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0))
+local nospacer=Cs((whitespace^1/""+nonwhitespace^1)^0)
local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0)
local e_collapser=Cs((whitespace^1*P(-1)/""+nonwhitespace^1+whitespace^1/" ")^0)
local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0)
@@ -838,6 +839,7 @@ local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0)
patterns.stripper=stripper
patterns.fullstripper=fullstripper
patterns.collapser=collapser
+patterns.nospacer=nospacer
patterns.b_collapser=b_collapser
patterns.m_collapser=m_collapser
patterns.e_collapser=e_collapser
@@ -1543,7 +1545,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["l-string"] = package.loaded["l-string"] or true
--- original size: 6296, stripped down to: 3225
+-- original size: 6419, stripped down to: 3339
if not modules then modules={} end modules ['l-string']={
version=1.001,
@@ -1581,6 +1583,7 @@ end
local stripper=patterns.stripper
local fullstripper=patterns.fullstripper
local collapser=patterns.collapser
+local nospacer=patterns.nospacer
local longtostring=patterns.longtostring
function string.strip(str)
return str and lpegmatch(stripper,str) or ""
@@ -1591,6 +1594,9 @@ end
function string.collapsespaces(str)
return str and lpegmatch(collapser,str) or ""
end
+function string.nospaces(str)
+ return str and lpegmatch(nospacer,str) or ""
+end
function string.longtostring(str)
return str and lpegmatch(longtostring,str) or ""
end
@@ -4111,7 +4117,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["l-md5"] = package.loaded["l-md5"] or true
--- original size: 3248, stripped down to: 2266
+-- original size: 3309, stripped down to: 2314
if not modules then modules={} end modules ['l-md5']={
version=1.001,
@@ -4141,6 +4147,8 @@ do
if not md5.HEX then function md5.HEX(str) if str then return lpegmatch(bytestoHEX,md5sum(str)) end end end
if not md5.hex then function md5.hex(str) if str then return lpegmatch(bytestohex,md5sum(str)) end end end
if not md5.dec then function md5.dec(str) if str then return lpegmatch(bytestodec,md5sum(str)) end end end
+ md5.sumhexa=md5.hex
+ md5.sumHEXA=md5.HEX
end
end
function file.needsupdating(oldname,newname,threshold)
@@ -7021,7 +7029,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["util-fil"] = package.loaded["util-fil"] or true
--- original size: 7427, stripped down to: 5702
+-- original size: 7038, stripped down to: 5659
if not modules then modules={} end modules ['util-fil']={
version=1.001,
@@ -7122,21 +7130,12 @@ function files.readcardinal2le(f)
end
function files.readinteger2(f)
local a,b=byte(f:read(2),1,2)
- local n=0x100*a+b
- if n>=0x8000 then
- return n-0x10000
+ if a>=0x80 then
+ return 0x100*a+b-0x10000
else
- return n
+ return 0x100*a+b
end
end
- function files.readinteger2(f)
- local a,b=byte(f:read(2),1,2)
- if a>=0x80 then
- return 0x100*a+b-0x10000
- else
- return 0x100*a+b
- end
- end
function files.readinteger2le(f)
local b,a=byte(f:read(2),1,2)
local n=0x100*a+b
@@ -7218,8 +7217,13 @@ if extract then
local band=bit32.band
function files.read2dot14(f)
local a,b=byte(f:read(2),1,2)
- local n=0x100*a+b
- return extract(n,14,2)+(band(n,0x3FFF)/16384.0)
+ if a>=0x80 then
+ local n=-(0x100*a+b)
+ return-(extract(n,14,2)+(band(n,0x3FFF)/16384.0))
+ else
+ local n=0x100*a+b
+ return (extract(n,14,2)+(band(n,0x3FFF)/16384.0))
+ end
end
end
function files.skipshort(f,n)
@@ -11248,7 +11252,7 @@ do -- create closure to overcome 200 locals limit
package.loaded["lxml-tab"] = package.loaded["lxml-tab"] or true
--- original size: 56187, stripped down to: 35523
+-- original size: 56300, stripped down to: 35539
if not modules then modules={} end modules ['lxml-tab']={
version=1.001,
@@ -11390,7 +11394,7 @@ local function add_empty(spacing,namespace,tag)
tg=tag,
at=at,
dt={},
- ni=nil,
+ ni=nt,
__p__=top
}
dt[nt]=t
@@ -11440,6 +11444,7 @@ local function add_end(spacing,namespace,tag)
dt=top.dt
nt=#dt+1
dt[nt]=toclose
+ toclose.ni=nt
if toclose.at.xmlns then
remove(xmlns)
end
@@ -20228,8 +20233,8 @@ end -- of closure
-- used libraries : l-lua.lua l-sandbox.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-tpl.lua util-sbx.lua util-mrg.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua
-- skipped libraries : -
--- original bytes : 836086
--- stripped bytes : 304336
+-- original bytes : 836098
+-- stripped bytes : 304131
-- end library merge
diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii
index eed45203f..547fdc99d 100644
--- a/tex/context/base/mkii/cont-new.mkii
+++ b/tex/context/base/mkii/cont-new.mkii
@@ -11,7 +11,7 @@
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.
-\newcontextversion{2017.03.02 22:23}
+\newcontextversion{2017.03.20 17:33}
%D This file is loaded at runtime, thereby providing an
%D excellent place for hacks, patches, extensions and new
diff --git a/tex/context/base/mkii/context.mkii b/tex/context/base/mkii/context.mkii
index 6a5f25120..334f20b95 100644
--- a/tex/context/base/mkii/context.mkii
+++ b/tex/context/base/mkii/context.mkii
@@ -20,7 +20,7 @@
%D your styles an modules.
\edef\contextformat {\jobname}
-\edef\contextversion{2017.03.02 22:23}
+\edef\contextversion{2017.03.20 17:33}
%D For those who want to use this:
diff --git a/tex/context/base/mkiv/cldf-int.lua b/tex/context/base/mkiv/cldf-int.lua
index cd4db2e90..a97eadf35 100644
--- a/tex/context/base/mkiv/cldf-int.lua
+++ b/tex/context/base/mkiv/cldf-int.lua
@@ -26,7 +26,7 @@ local trace_define = false trackers.register("context.define", function(v) tr
interfaces = interfaces or { }
-_clmh_ = utilities.parsers.settings_to_array
+_clmh_ = utilities.parsers.settings_to_hash
_clma_ = utilities.parsers.settings_to_array
local starters, stoppers, macros, stack = { }, { }, { }, { }
@@ -66,6 +66,8 @@ _clmn_ = tonumber
local estart = interfaces.elements.start
local estop = interfaces.elements.stop
+-- this is a bit old definition ... needs to be modernized
+
function interfaces.definecommand(name,specification) -- name is optional
if type(name) == "table" then
specification = name
diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv
index c603af8ba..ec63ac14b 100644
--- a/tex/context/base/mkiv/cont-new.mkiv
+++ b/tex/context/base/mkiv/cont-new.mkiv
@@ -11,7 +11,7 @@
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.
-\newcontextversion{2017.03.02 22:23}
+\newcontextversion{2017.03.20 17:33}
%D This file is loaded at runtime, thereby providing an excellent place for
%D hacks, patches, extensions and new features.
diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv
index d12ba09be..0a70af437 100644
--- a/tex/context/base/mkiv/context.mkiv
+++ b/tex/context/base/mkiv/context.mkiv
@@ -39,7 +39,7 @@
%D up and the dependencies are more consistent.
\edef\contextformat {\jobname}
-\edef\contextversion{2017.03.02 22:23}
+\edef\contextversion{2017.03.20 17:33}
\edef\contextkind {beta}
%D For those who want to use this:
diff --git a/tex/context/base/mkiv/font-cff.lua b/tex/context/base/mkiv/font-cff.lua
index 37436fbcf..eb0a2c1be 100644
--- a/tex/context/base/mkiv/font-cff.lua
+++ b/tex/context/base/mkiv/font-cff.lua
@@ -13,15 +13,21 @@ if not modules then modules = { } end modules ['font-cff'] = {
-- This is a heavy one as it is a rather packed format. We don't need al the information
-- now but we might need it later (who know what magic we can do with metapost). So at
-- some point this might become a module. We just follow Adobe Technical Notes #5176 and
--- #5177. In case of doubt I looked in the fontforge code that comes with LuaTeX.
+-- #5177. In case of doubt I looked in the fontforge code that comes with LuaTeX but
+-- it's not the easiest source to read (and doesn't cover cff2).
-- For now we save the segments in a list of segments with the operator last in an entry
-- because that reflects the original. But it might make more sense to use a single array
-- per segment. For pdf a simple concat works ok, but for other purposes a operator first
-- flush is nicer.
+--
+-- In retrospect I could have looked into the backend code of LuaTeX but it never
+-- occurred to me that parsing charstrings was needed there (which has to to
+-- with merging subroutines and flattening, not so much with calculations.) On
+-- the other hand, we can now feed back cff2 stuff.
local next, type, tonumber = next, type, tonumber
-local byte, gmatch = string.byte, string.gmatch
+local byte, char, gmatch = string.byte, string.char, string.gmatch
local concat, remove = table.concat, table.remove
local floor, abs, round, ceil, min, max = math.floor, math.abs, math.round, math.ceil, math.min, math.max
local P, C, R, S, C, Cs, Ct = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Ct
@@ -51,6 +57,8 @@ local parsecharstring
local parsecharstrings
local resetcharstrings
local parseprivates
+local startparsing
+local stopparsing
local defaultstrings = { [0] = -- taken from ff
".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent",
@@ -131,13 +139,21 @@ local cffreaders = {
local function readheader(f)
local offset = getposition(f)
+ local major = readbyte(f)
local header = {
offset = offset,
- major = readbyte(f),
+ major = major,
minor = readbyte(f),
size = readbyte(f), -- headersize
- osize = readbyte(f), -- for offsets to start
}
+ if major == 1 then
+ header.dsize = readbyte(f) -- list of dict offsets
+ elseif major == 2 then
+ header.dsize = readushort(f) -- topdict size
+ else
+ -- I'm probably no longer around by then and we use AI's to
+ -- handle this kind of stuff, if we typeset documents at all.
+ end
setposition(f,offset+header.size)
return header
end
@@ -145,8 +161,8 @@ end
-- The indexes all look the same, so we share a loader. We could pass a handler
-- and run over the array but why bother, we only have a few uses.
-local function readlengths(f)
- local count = readushort(f)
+local function readlengths(f,longcount)
+ local count = longcount and readulong(f) or readushort(f)
if count == 0 then
return { }
end
@@ -160,7 +176,12 @@ local function readlengths(f)
local previous = read(f)
for i=1,count do
local offset = read(f)
- lengths[i] = offset - previous
+ local length = offset - previous
+ if length < 0 then
+ report("bad offset: %i",length)
+ length = 0
+ end
+ lengths[i] = length
previous = offset
end
return lengths
@@ -218,7 +239,8 @@ end
do
- -- We use a closure so that we don't need to pass too much around.
+ -- We use a closure so that we don't need to pass too much around. For cff2 we can
+ -- at some point use a simple version as there is less.
local stack = { }
local top = 0
@@ -272,7 +294,7 @@ do
result.encoding = stack[top]
top = 0
end
- + P("\17") / function()
+ + P("\17") / function() -- valid cff2
result.charstrings = stack[top]
top = 0
end
@@ -285,19 +307,32 @@ do
end
+ P("\19") / function()
result.subroutines = stack[top]
+ top = 0 -- new, forgotten ?
end
+ P("\20") / function()
result.defaultwidthx = stack[top]
+ top = 0 -- new, forgotten ?
end
+ P("\21") / function()
result.nominalwidthx = stack[top]
+ top = 0 -- new, forgotten ?
+ end
+ -- + P("\22") / function() -- reserved
+ -- end
+ -- + P("\23") / function() -- reserved
+ -- end
+ + P("\24") / function() -- new in cff2
+ result.vstore = stack[top]
+ top = 0
+ end
+ + P("\25") / function() -- new in cff2
+ result.maxstack = stack[top]
+ top = 0
end
- -- + P("\22") / function() end -- reserved
- -- + P("\23") / function() end -- reserved
- -- + P("\24") / function() end -- reserved
- -- + P("\25") / function() end -- reserved
- -- + P("\26") / function() end -- reserved
- -- + P("\27") / function() end -- reserved
+ -- + P("\26") / function() -- reserved
+ -- end
+ -- + P("\27") / function() -- reserved
+ -- end
local p_double = P("\12") * (
P("\00") / function()
@@ -328,7 +363,7 @@ do
result.charstringtype = stack[top]
top = 0
end
- + P("\07") / function()
+ + P("\07") / function() -- valid cff2
result.fontmatrix = { unpack(stack,1,6) }
top = 0
end
@@ -378,11 +413,11 @@ do
result.cid.uidbase = stack[top]
top = 0
end
- + P("\36") / function()
+ + P("\36") / function() -- valid cff2
result.cid.fdarray = stack[top]
top = 0
end
- + P("\37") / function()
+ + P("\37") / function() -- valid cff2
result.cid.fdselect = stack[top]
top = 0
end
@@ -503,12 +538,12 @@ do
+ p_unsupported
)^1
- parsedictionaries = function(data,dictionaries)
+ parsedictionaries = function(data,dictionaries,what)
stack = { }
strings = data.strings
for i=1,#dictionaries do
top = 0
- result = {
+ result = what == "cff" and {
monospaced = false,
italicangle = 0,
underlineposition = -100,
@@ -526,6 +561,13 @@ do
fonttype = 0,
count = 8720,
}
+ } or {
+ charstringtype = 2,
+ charset = 0,
+ vstore = 0,
+ cid = {
+ -- nothing yet
+ },
}
lpegmatch(p_dictionary,dictionaries[i])
dictionaries[i] = result
@@ -580,23 +622,32 @@ do
-- because there quite some variants are done in one helper with a lot of
-- testing for states.
- local x = 0
- local y = 0
- local width = false
- local r = 0
- local stems = 0
- local globalbias = 0
- local localbias = 0
- local globals = false
- local locals = false
- local depth = 1
- local xmin = 0
- local xmax = 0
- local ymin = 0
- local ymax = 0
- local checked = false
- local keepcurve = false
- local version = 2
+ local x = 0
+ local y = 0
+ local width = false
+ local r = 0
+ local stems = 0
+ local globalbias = 0
+ local localbias = 0
+ local nominalwidth = 0
+ local defaultwidth = 0
+ local charset = false
+ local globals = false
+ local locals = false
+ local depth = 1
+ local xmin = 0
+ local xmax = 0
+ local ymin = 0
+ local ymax = 0
+ local checked = false
+ local keepcurve = false
+ local version = 2
+ local regions = false
+ local nofregions = 0
+ local region = false
+ local factors = false
+ local axis = false
+ local vsindex = 0
local function showstate(where)
report("%w%-10s : [%s] n=%i",depth*2,where,concat(stack," ",1,top),top)
@@ -615,7 +666,7 @@ do
-- some back. I inlined some of then and a bit speed can be gained by more
-- inlining but not that much.
- local function moveto()
+ local function xymoveto()
if keepcurve then
r = r + 1
result[r] = { x, y, "m" }
@@ -668,7 +719,15 @@ do
end
end
- local function lineto() -- we could inline
+ local function moveto()
+ if trace_charstrings then
+ showstate("moveto")
+ end
+ top = 0 -- forgotten
+ xymoveto()
+ end
+
+ local function xylineto() -- we could inline, no blend
if keepcurve then
r = r + 1
result[r] = { x, y, "l" }
@@ -721,7 +780,10 @@ do
end
end
- local function curveto(x1,y1,x2,y2,x3,y3)
+ local function xycurveto(x1,y1,x2,y2,x3,y3) -- called local so no blend here
+ if trace_charstrings then
+ showstate("curveto")
+ end
if keepcurve then
r = r + 1
result[r] = { x1, y1, x2, y2, x3, y3, "c" }
@@ -747,7 +809,7 @@ do
if top > 2 then
width = stack[1]
if trace_charstrings then
- showvalue("width",width)
+ showvalue("backtrack width",width)
end
else
width = true
@@ -759,7 +821,7 @@ do
x = x + stack[top-1] -- dx1
y = y + stack[top] -- dy1
top = 0
- moveto()
+ xymoveto()
end
local function hmoveto()
@@ -767,7 +829,7 @@ do
if top > 1 then
width = stack[1]
if trace_charstrings then
- showvalue("width",width)
+ showvalue("backtrack width",width)
end
else
width = true
@@ -786,7 +848,7 @@ do
if top > 1 then
width = stack[1]
if trace_charstrings then
- showvalue("width",width)
+ showvalue("backtrack width",width)
end
else
width = true
@@ -807,7 +869,7 @@ do
for i=1,top,2 do
x = x + stack[i] -- dxa
y = y + stack[i+1] -- dya
- lineto()
+ xylineto()
end
top = 0
end
@@ -871,7 +933,7 @@ do
local by = ay + stack[i+3] -- dyb
x = bx + stack[i+4] -- dxc
y = by + stack[i+5] -- dyc
- curveto(ax,ay,bx,by,x,y)
+ xycurveto(ax,ay,bx,by,x,y)
end
top = 0
end
@@ -892,7 +954,7 @@ do
local by = ay + stack[i+2] -- dyb
x = bx + stack[i+3] -- dxc
y = by
- curveto(ax,ay,bx,by,x,y)
+ xycurveto(ax,ay,bx,by,x,y)
end
top = 0
end
@@ -914,7 +976,7 @@ do
local by = ay + stack[i+2] -- dyb
x = bx
y = by + stack[i+3] -- dyc
- curveto(ax,ay,bx,by,x,y)
+ xycurveto(ax,ay,bx,by,x,y)
d = 0
end
top = 0
@@ -952,7 +1014,7 @@ do
end
swap = true
end
- curveto(ax,ay,bx,by,x,y)
+ xycurveto(ax,ay,bx,by,x,y)
end
top = 0
end
@@ -982,11 +1044,11 @@ do
local by = ay + stack[i+3] -- dyb
x = bx + stack[i+4] -- dxc
y = by + stack[i+5] -- dyc
- curveto(ax,ay,bx,by,x,y)
+ xycurveto(ax,ay,bx,by,x,y)
end
x = x + stack[top-1] -- dxc
y = y + stack[top] -- dyc
- lineto()
+ xylineto()
top = 0
end
@@ -998,7 +1060,7 @@ do
for i=1,top-6,2 do
x = x + stack[i]
y = y + stack[i+1]
- lineto()
+ xylineto()
end
end
local ax = x + stack[top-5]
@@ -1007,7 +1069,7 @@ do
local by = ay + stack[top-2]
x = bx + stack[top-1]
y = by + stack[top]
- curveto(ax,ay,bx,by,x,y)
+ xycurveto(ax,ay,bx,by,x,y)
top = 0
end
@@ -1023,14 +1085,14 @@ do
local by = ay + stack[4] -- dy2
local cx = bx + stack[5] -- dx3
local cy = by + stack[6] -- dy3
- curveto(ax,ay,bx,by,cx,cy)
+ xycurveto(ax,ay,bx,by,cx,cy)
local dx = cx + stack[7] -- dx4
local dy = cy + stack[8] -- dy4
local ex = dx + stack[9] -- dx5
local ey = dy + stack[10] -- dy5
x = ex + stack[11] -- dx6
y = ey + stack[12] -- dy6
- curveto(dx,dy,ex,ey,x,y)
+ xycurveto(dx,dy,ex,ey,x,y)
top = 0
end
@@ -1044,13 +1106,13 @@ do
local by = ay + stack[3] -- dy2
local cx = bx + stack[4] -- dx3
local cy = by
- curveto(ax,ay,bx,by,cx,cy)
+ xycurveto(ax,ay,bx,by,cx,cy)
local dx = cx + stack[5] -- dx4
local dy = by
local ex = dx + stack[6] -- dx5
local ey = y
x = ex + stack[7] -- dx6
- curveto(dx,dy,ex,ey,x,y)
+ xycurveto(dx,dy,ex,ey,x,y)
top = 0
end
@@ -1064,13 +1126,13 @@ do
local by = ay + stack[4] -- dy2
local cx = bx + stack[5] -- dx3
local cy = by
- curveto(ax,ay,bx,by,cx,cy)
+ xycurveto(ax,ay,bx,by,cx,cy)
local dx = cx + stack[6] -- dx4
local dy = by
local ex = dx + stack[7] -- dx5
local ey = dy + stack[8] -- dy5
x = ex + stack[9] -- dx6
- curveto(dx,dy,ex,ey,x,y)
+ xycurveto(dx,dy,ex,ey,x,y)
top = 0
end
@@ -1084,7 +1146,7 @@ do
local by = ay + stack[4] --dy2
local cx = bx + stack[5] --dx3
local cy = by + stack[6] --dy3
- curveto(ax,ay,bx,by,cx,cy)
+ xycurveto(ax,ay,bx,by,cx,cy)
local dx = cx + stack[7] --dx4
local dy = cy + stack[8] --dy4
local ex = dx + stack[9] --dx5
@@ -1094,7 +1156,7 @@ do
else
y = ey + stack[11]
end
- curveto(dx,dy,ex,ey,x,y)
+ xycurveto(dx,dy,ex,ey,x,y)
top = 0
end
@@ -1253,7 +1315,109 @@ do
top = 0
end
- -- so far for unsupported postscript
+ -- So far for unsupported postscript. Now some cff2 magic. As I still need
+ -- to wrap my head around the rather complex variable font specification
+ -- with regions and axis, the following approach kind of works but is more
+ -- some trial and error trick. It's still not clear how much of the complex
+ -- truetype description applies to cff.
+
+ local reginit = false
+
+ local function updateregions(n) -- n + 1
+ if regions then
+ local current = regions[n] or regions[1]
+ nofregions = #current
+ if axis and n ~= reginit then
+ factors = { }
+ for i=1,nofregions do
+ local region = current[i]
+ local s = 1
+ for j=1,#axis do
+ local f = axis[j]
+ local r = region[j]
+ local start = r.start
+ local peak = r.peak
+ local stop = r.stop
+ 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)
+ elseif f > peak then
+ s = s * (stop - f) / (stop - peak)
+ else
+ -- * 1
+ end
+ end
+ factors[i] = s
+ end
+ end
+ end
+ reginit = n
+ end
+
+ local function setvsindex()
+ local vsindex = stack[top]
+ if trace_charstrings then
+ showstate(formatters["vsindex %i"](vsindex))
+ end
+ updateregions(vsindex)
+ top = top - 1
+ end
+
+ local function blend()
+ local n = stack[top]
+ top = top - 1
+ if axis then
+ -- x (r1x,r2x,r3x)
+ -- (x,y) (r1x,r2x,r3x) (r1y,r2y,r3y)
+ if trace_charstrings then
+ local t = top - nofregions * n
+ local m = t - n
+ for i=1,n do
+ local k = m + i
+ local d = m + n + (i-1)*nofregions
+ local old = stack[k]
+ local new = old
+ for r=1,nofregions do
+ new = new + stack[d+r] * factors[r]
+ end
+ stack[k] = new
+ showstate(formatters["blend %i of %i: %s -> %s"](i,n,old,new))
+ end
+ top = t
+ elseif n == 1 then
+ top = top - nofregions
+ local v = stack[top]
+ for r=1,nofregions do
+ v = v + stack[top+r] * factors[r]
+ end
+ stack[top] = v
+ else
+ top = top - nofregions * n
+ local d = top
+ local k = top - n
+ for i=1,n do
+ k = k + 1
+ local v = stack[k]
+ for r=1,nofregions do
+ v = v + stack[d+r] * factors[r]
+ end
+ stack[k] = v
+ d = d + nofregions
+ end
+ end
+ else
+ -- error
+ end
+ end
-- Bah, we cannot use a fast lpeg because a hint has an unknown size and a
-- runtime capture cannot handle that well.
@@ -1274,8 +1438,8 @@ do
unsupported, -- 12 -- elsewhere
hsbw, -- 13 -- hsbw (type 1 cff)
unsupported, -- 14 -- endchar,
- unsupported, -- 15
- unsupported, -- 16
+ setvsindex, -- 15 -- cff2
+ blend, -- 16 -- cff2
unsupported, -- 17
getstem, -- 18 -- hstemhm
getmask, -- 19 -- hintmask
@@ -1311,6 +1475,92 @@ do
[037] = flex1,
}
+ local c_endchar = char(14)
+
+ local passon do
+
+ -- todo: round in blend
+ -- todo: delay this hash
+
+ local rshift = bit32.rshift
+ local band = bit32.band
+ local round = math.round
+
+ local encode = table.setmetatableindex(function(t,i)
+ for i=-2048,-1130 do
+ t[i] = char(28,band(rshift(i,8),0xFF),band(i,0xFF))
+ end
+ for i=-1131,-108 do
+ local v = 0xFB00 - i - 108
+ t[i] = char(band(rshift(v,8),0xFF),band(v,0xFF))
+ end
+ for i=-107,107 do
+ t[i] = char(i + 139)
+ end
+ for i=108,1131 do
+ local v = 0xF700 + i - 108
+ t[i] = char(band(rshift(v,8),0xFF),band(v,0xFF))
+ end
+ for i=1132,2048 do
+ t[i] = char(28,band(rshift(i,8),0xFF),band(i,0xFF))
+ end
+ return t[i]
+ end)
+
+ local function setvsindex()
+ local vsindex = stack[top]
+ updateregions(vsindex)
+ top = top - 1
+ end
+
+ local function blend()
+ local n = stack[top]
+ top = top - 1
+ if not axis then
+ -- fatal error
+ elseif n == 1 then
+ top = top - nofregions
+ local v = stack[top]
+ for r=1,nofregions do
+ v = v + stack[top+r] * factors[r]
+ end
+ stack[top] = round(v)
+ else
+ top = top - nofregions * n
+ local d = top
+ local k = top - n
+ for i=1,n do
+ k = k + 1
+ local v = stack[k]
+ for r=1,nofregions do
+ v = v + stack[d+r] * factors[r]
+ end
+ stack[k] = round(v)
+ d = d + nofregions
+ end
+ end
+ end
+
+ passon = function(operation)
+ if operation == 15 then
+ setvsindex()
+ elseif operation == 16 then
+ blend()
+ else
+ for i=1,top do
+ r = r + 1
+ result[r] = encode[stack[i]]
+ end
+ r = r + 1
+ result[r] = char(operation) -- maybe use a hash
+ top = 0
+ end
+ end
+
+ end
+
+ -- end of experiment
+
local process
local function call(scope,list,bias) -- ,process)
@@ -1337,6 +1587,8 @@ do
-- precompiling and reuse is much slower than redoing the calls
+ local justpass = false
+
process = function(tab)
local i = 1
local n = #tab
@@ -1384,7 +1636,7 @@ do
stack[top] = n
end
i = i + 3
- elseif t == 11 then
+ elseif t == 11 then -- not in cff2
if trace_charstrings then
showstate("return")
end
@@ -1392,7 +1644,7 @@ do
elseif t == 10 then
call("local",locals,localbias) -- ,process)
i = i + 1
- elseif t == 14 then -- endchar
+ elseif t == 14 then -- not in cff2
if width then
-- okay
elseif top > 0 then
@@ -1423,6 +1675,9 @@ do
top = 0
end
i = i + 1
+ elseif justpass then
+ passon(t)
+ i = i + 1
else
local a = actions[t]
if a then
@@ -1492,143 +1747,58 @@ do
end
end
- parsecharstrings = function(data,glyphs,doshapes,tversion)
- -- for all charstrings
- local dictionary = data.dictionaries[1]
- local charstrings = dictionary.charstrings
- local charset = dictionary.charset
- local private = dictionary.private or { data = { } }
-
- keepcurve = doshapes
- version = tversion
- stack = { }
- glyphs = glyphs or { }
- strings = data.strings
- globals = data.routines or { }
- locals = dictionary.subroutines or { }
-
- globalbias, localbias = setbias(globals,locals)
-
- local nominalwidth = private.data.nominalwidthx or 0
- local defaultwidth = private.data.defaultwidthx or 0
+ local function processshape(tab,index)
- for i=1,#charstrings do
- local tab = bytetable(charstrings[i])
- local index = i - 1
- x = 0
- y = 0
- width = false
- r = 0
- top = 0
- stems = 0
- result = { } -- we could reuse it when only boundingbox calculations are needed
- --
- xmin = 0
- xmax = 0
- ymin = 0
- ymax = 0
- checked = false
- --
- if trace_charstrings then
- report("glyph: %i",index)
- report("data: % t",tab)
- end
- --
- process(tab)
- --
- local boundingbox = { round(xmin), round(ymin), round(xmax), round(ymax) }
- --
- if width == true or width == false then
- width = defaultwidth
- else
- width = nominalwidth + width
- end
- --
- -- trace_charstrings = index == 3078 -- todo: make tracker
- local glyph = glyphs[index] -- can be autodefined in otr
- if glyph then
- glyph.segments = doshapes ~= false and result or nil
- glyph.boundingbox = boundingbox
- if not glyph.width then
- glyph.width = width
- end
- if charset and not glyph.name then
- glyph.name = charset[index]
- end
- -- glyph.sidebearing = 0 -- todo
- elseif doshapes then
- glyphs[index] = {
- segments = result,
- boundingbox = boundingbox,
- width = width,
- name = charset[index],
- -- sidebearing = 0,
- }
- else
- glyphs[index] = {
- boundingbox = boundingbox,
- width = width,
- name = charset[index],
- }
- end
- if trace_charstrings then
- report("width: %s",tostring(width))
- report("boundingbox: % t",boundingbox)
- end
- charstrings[i] = nil -- free memory (what if used more often?)
- end
- return glyphs
- end
+ tab = bytetable(tab)
- parsecharstring = function(data,dictionary,tab,glyphs,index,doshapes,tversion)
- local private = dictionary.private
- keepcurve = doshapes
- version = tversion
- strings = data.strings -- or in dict?
- locals = dictionary.subroutines or { }
- globals = data.routines or { }
+ x = 0
+ y = 0
+ width = false
+ r = 0
+ top = 0
+ stems = 0
+ result = { } -- we could reuse it when only boundingbox calculations are needed
- globalbias, localbias = setbias(globals,locals)
+ xmin = 0
+ xmax = 0
+ ymin = 0
+ ymax = 0
+ checked = false
- local nominalwidth = private and private.data.nominalwidthx or 0
- local defaultwidth = private and private.data.defaultwidthx or 0
- --
- tab = bytetable(tab)
- --
- x = 0
- y = 0
- width = false
- r = 0
- top = 0
- stems = 0
- result = { }
- --
- xmin = 0
- xmax = 0
- ymin = 0
- ymax = 0
- checked = false
- --
if trace_charstrings then
report("glyph: %i",index)
- report("data: % t",tab)
+ report("data : % t",tab)
end
- --
+
+ updateregions(vsindex)
+
process(tab)
- --
- local boundingbox = { xmin, ymin, xmax, ymax }
- --
+
+ local boundingbox = {
+ round(xmin),
+ round(ymin),
+ round(xmax),
+ round(ymax),
+ }
+
if width == true or width == false then
width = defaultwidth
else
width = nominalwidth + width
end
- --
- index = index - 1
- --
+
local glyph = glyphs[index] -- can be autodefined in otr
- if glyph then
- glyph.segments = doshapes ~= false and result or nil
+ if justpass then
+ r = r + 1
+ result[r] = c_endchar
+ local stream = concat(result)
+ if glyph then
+ glyph.stream = stream
+ else
+ glyphs[index] = { stream = stream }
+ end
+ elseif glyph then
+ glyph.segments = keepcurve ~= false and result or nil
glyph.boundingbox = boundingbox
if not glyph.width then
glyph.width = width
@@ -1637,32 +1807,107 @@ do
glyph.name = charset[index]
end
-- glyph.sidebearing = 0 -- todo
- elseif doshapes then
+ elseif keepcurve then
glyphs[index] = {
segments = result,
boundingbox = boundingbox,
width = width,
- name = charset[index],
+ name = charset and charset[index] or nil,
-- sidebearing = 0,
}
else
glyphs[index] = {
boundingbox = boundingbox,
width = width,
- name = charset[index],
+ name = charset and charset[index] or nil,
}
end
- --
+
if trace_charstrings then
- report("width: %s",tostring(width))
+ report("width : %s",tostring(width))
report("boundingbox: % t",boundingbox)
end
+
end
- resetcharstrings = function()
- result = { }
- top = 0
- stack = { }
+ startparsing = function(fontdata,data,streams)
+ reginit = false
+ axis = false
+ regions = data.regions
+ justpass = streams == true
+ if regions then
+ regions = { regions } -- needs checking
+ axis = data.factors or false
+ end
+ end
+
+ stopparsing = function(fontdata,data)
+ stack = { }
+ glyphs = false
+ result = { }
+ top = 0
+ locals = false
+ globals = false
+ strings = false
+ end
+
+ local function setwidths(private)
+ if not private then
+ return 0, 0
+ end
+ local privatedata = private.data
+ if not privatedata then
+ return 0, 0
+ end
+ return privatedata.nominalwidthx or 0, privatedata.defaultwidthx or 0
+ end
+
+ parsecharstrings = function(fontdata,data,glphs,doshapes,tversion,streams)
+
+ local dictionary = data.dictionaries[1]
+ local charstrings = dictionary.charstrings
+
+ keepcurve = doshapes
+ version = tversion
+ strings = data.strings
+ globals = data.routines or { }
+ locals = dictionary.subroutines or { }
+ charset = dictionary.charset
+ vsindex = dictionary.vsindex or 0
+ glyphs = glphs or { }
+
+ globalbias, localbias = setbias(globals,locals)
+ nominalwidth, defaultwidth = setwidths(dictionary.private)
+
+ startparsing(fontdata,data,streams)
+
+ for index=1,#charstrings do
+ processshape(charstrings[index],index-1)
+ charstrings[index] = nil -- free memory (what if used more often?)
+ end
+
+ stopparsing(fontdata,data)
+
+ return glyphs
+ end
+
+ parsecharstring = function(fontdata,data,dictionary,tab,glphs,index,doshapes,tversion)
+
+ keepcurve = doshapes
+ version = tversion
+ strings = data.strings
+ globals = data.routines or { }
+ locals = dictionary.subroutines or { }
+ charset = false
+ vsindex = dictionary.vsindex or 0
+ glyphs = glphs or { }
+
+ globalbias, localbias = setbias(globals,locals)
+ nominalwidth, defaultwidth = setwidths(dictionary.private)
+
+ processshape(tab,index-1)
+
+ -- return glyphs[index]
end
end
@@ -1684,8 +1929,7 @@ local function readcharsets(f,data,dictionary)
local strings = data.strings
local nofglyphs = data.nofglyphs
local charsetoffset = dictionary.charset
-
- if charsetoffset ~= 0 then
+ if charsetoffset and charsetoffset ~= 0 then
setposition(f,header.offset+charsetoffset)
local format = readbyte(f)
local charset = { [0] = ".notdef" }
@@ -1711,6 +1955,9 @@ local function readcharsets(f,data,dictionary)
else
report("cff parser: unsupported charset format %a",format)
end
+ else
+ dictionary.nocharset = true
+ dictionary.charset = nil
end
end
@@ -1748,16 +1995,18 @@ end
-- These charstrings are little programs and described in: Technical Note #5177. A truetype
-- font has only one dictionary.
-local function readcharstrings(f,data)
+local function readcharstrings(f,data,what)
local header = data.header
local dictionaries = data.dictionaries
local dictionary = dictionaries[1]
- local type = dictionary.charstringtype
+ local stringtype = dictionary.charstringtype
local offset = dictionary.charstrings
- if type == 2 then
+ if type(offset) ~= "number" then
+ -- weird
+ elseif stringtype == 2 then
setposition(f,header.offset+offset)
-- could be a metatable .. delayed loading
- local charstrings = readlengths(f)
+ local charstrings = readlengths(f,what=="cff2")
local nofglyphs = #charstrings
for i=1,nofglyphs do
charstrings[i] = readstring(f,charstrings[i])
@@ -1765,7 +2014,7 @@ local function readcharstrings(f,data)
data.nofglyphs = nofglyphs
dictionary.charstrings = charstrings
else
- report("unsupported charstr type %i",type)
+ report("unsupported charstr type %i",stringtype)
data.nofglyphs = 0
dictionary.charstrings = { }
end
@@ -1787,31 +2036,36 @@ local function readcidprivates(f,data)
parseprivates(data,dictionaries)
end
-local function readnoselect(f,data,glyphs,doshapes,version)
+readers.parsecharstrings = parsecharstrings -- used in font-onr.lua (type 1)
+
+local function readnoselect(f,fontdata,data,glyphs,doshapes,version,streams)
local dictionaries = data.dictionaries
local dictionary = dictionaries[1]
readglobals(f,data)
- readcharstrings(f,data)
- readencodings(f,data)
- readcharsets(f,data,dictionary)
+ readcharstrings(f,data,version)
+ if version ~= "cff2" then
+ readencodings(f,data)
+ readcharsets(f,data,dictionary)
+ end
readprivates(f,data)
parseprivates(data,data.dictionaries)
readlocals(f,data,dictionary)
- parsecharstrings(data,glyphs,doshapes,version)
- resetcharstrings()
+ startparsing(fontdata,data,streams)
+ parsecharstrings(fontdata,data,glyphs,doshapes,version,streams)
+ stopparsing(fontdata,data)
end
-readers.parsecharstrings = parsecharstrings
-
-local function readfdselect(f,data,glyphs,doshapes,version)
+local function readfdselect(f,fontdata,data,glyphs,doshapes,version,streams)
local header = data.header
local dictionaries = data.dictionaries
local dictionary = dictionaries[1]
local cid = dictionary.cid
local cidselect = cid and cid.fdselect
readglobals(f,data)
- readcharstrings(f,data)
- readencodings(f,data)
+ readcharstrings(f,data,version)
+ if version ~= "cff2" then
+ readencodings(f,data)
+ end
local charstrings = dictionary.charstrings
local fdindex = { }
local nofglyphs = data.nofglyphs
@@ -1848,6 +2102,7 @@ local function readfdselect(f,data,glyphs,doshapes,version)
else
-- unsupported format
end
+ -- hm, always
if maxindex >= 0 then
local cidarray = cid.fdarray
setposition(f,header.offset+cidarray)
@@ -1861,86 +2116,151 @@ local function readfdselect(f,data,glyphs,doshapes,version)
for i=1,#dictionaries do
readlocals(f,data,dictionaries[i])
end
+ startparsing(fontdata,data,streams)
for i=1,#charstrings do
- parsecharstring(data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version)
+ parsecharstring(fontdata,data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version)
charstrings[i] = nil
end
- resetcharstrings()
+ stopparsing(fontdata,data)
end
end
+local gotodatatable = readers.helpers.gotodatatable
+
+local function cleanup(data,dictionaries)
+ -- for i=1,#dictionaries do
+ -- local d = dictionaries[i]
+ -- d.subroutines = nil
+ -- end
+ -- data.strings = nil
+ -- if data then
+ -- data.charstrings = nil
+ -- data.routines = nil
+ -- end
+end
+
function readers.cff(f,fontdata,specification)
- -- if specification.glyphs then
- if specification.details then
- local datatable = fontdata.tables.cff
- if datatable then
- local offset = datatable.offset
- local glyphs = fontdata.glyphs
- if not f then
- report("invalid filehandle")
- return
- end
- if offset then
- setposition(f,offset)
- end
- local header = readheader(f)
- if header.major > 1 then
- report("version mismatch")
- return
- end
- local names = readfontnames(f)
- local dictionaries = readtopdictionaries(f)
- local strings = readstrings(f)
- local data = {
- header = header,
- names = names,
- dictionaries = dictionaries,
- strings = strings,
- nofglyphs = fontdata.nofglyphs,
- }
- --
- parsedictionaries(data,data.dictionaries)
- --
- local d = dictionaries[1]
- local c = d.cid
- fontdata.cffinfo = {
- familynamename = d.familyname,
- fullname = d.fullname,
- boundingbox = d.boundingbox,
- weight = d.weight,
- italicangle = d.italicangle,
- underlineposition = d.underlineposition,
- underlinethickness = d.underlinethickness,
- monospaced = d.monospaced,
- }
- fontdata.cidinfo = c and {
- registry = c.registry,
- ordering = c.ordering,
- supplement = c.supplement,
- }
- --
- if not specification.glyphs then
- -- we only want some metadata
+ local tableoffset = gotodatatable(f,fontdata,"cff",specification.details)
+ if tableoffset then
+ local header = readheader(f)
+ if header.major ~= 1 then
+ report("only version %s is supported for table %a",1,"cff")
+ return
+ end
+ local glyphs = fontdata.glyphs
+ local names = readfontnames(f)
+ local dictionaries = readtopdictionaries(f)
+ local strings = readstrings(f)
+ local data = {
+ header = header,
+ names = names,
+ dictionaries = dictionaries,
+ strings = strings,
+ nofglyphs = fontdata.nofglyphs,
+ }
+ --
+ parsedictionaries(data,dictionaries,"cff")
+ --
+ local dic = dictionaries[1]
+ local cid = dic.cid
+ fontdata.cffinfo = {
+ familynamename = dic.familyname,
+ fullname = dic.fullname,
+ boundingbox = dic.boundingbox,
+ weight = dic.weight,
+ italicangle = dic.italicangle,
+ underlineposition = dic.underlineposition,
+ underlinethickness = dic.underlinethickness,
+ monospaced = dic.monospaced,
+ }
+ fontdata.cidinfo = cid and {
+ registry = cid.registry,
+ ordering = cid.ordering,
+ supplement = cid.supplement,
+ }
+ --
+ if specification.glyphs then
+ local all = specification.shapes or false
+ if cid and cid.fdselect then
+ readfdselect(f,fontdata,data,glyphs,all,"cff")
else
- local cid = d.cid
- if cid and cid.fdselect then
- readfdselect(f,data,glyphs,specification.shapes or false)
- else
- readnoselect(f,data,glyphs,specification.shapes or false)
- end
+ readnoselect(f,fontdata,data,glyphs,all,"cff")
end
- --
- -- cleanup (probably more can go)
- --
- -- for i=1,#dictionaries do
- -- local d = dictionaries[i]
- -- d.subroutines = nil
- -- end
- -- data.strings = nil
- -- if data then
- -- data.charstrings = nil
- -- data.routines = nil
- -- end
end
+ cleanup(data,dictionaries)
+ end
+end
+
+function readers.cff2(f,fontdata,specification)
+ local tableoffset = gotodatatable(f,fontdata,"cff2",specification.glyphs)
+ if tableoffset then
+ local header = readheader(f)
+ if header.major ~= 2 then
+ report("only version %s is supported for table %a",2,"cff2")
+ return
+ end
+ local glyphs = fontdata.glyphs
+ local dictionaries = { readstring(f,header.dsize) }
+ local data = {
+ header = header,
+ dictionaries = dictionaries,
+ nofglyphs = fontdata.nofglyphs,
+ }
+ --
+ parsedictionaries(data,dictionaries,"cff2")
+ --
+ local storeoffset = dictionaries[1].vstore + data.header.offset + 2 -- cff has a preceding size field
+ local regions, deltas = readers.helpers.readvariationdata(f,storeoffset,factors)
+ --
+ data.regions = regions
+ data.deltas = deltas
+ data.factors = specification.factors
+ --
+ local cid = data.dictionaries[1].cid
+ local all = specification.shapes or false
+ if cid and cid.fdselect then
+ readfdselect(f,fontdata,data,glyphs,all,"cff2",specification.streams)
+ else
+ readnoselect(f,fontdata,data,glyphs,all,"cff2",specification.streams)
+ end
+ cleanup(data,dictionaries)
+ end
+end
+
+-- temporary helper needed for checking backend patches
+
+function readers.cffcheck(filename)
+ local f = io.open(filename,"rb")
+ if f then
+ local fontdata = {
+ glyphs = { },
+ }
+ local header = readheader(f)
+ if header.major ~= 1 then
+ report("only version %s is supported for table %a",1,"cff")
+ return
+ end
+ local names = readfontnames(f)
+ local dictionaries = readtopdictionaries(f)
+ local strings = readstrings(f)
+ local glyphs = { }
+ local data = {
+ header = header,
+ names = names,
+ dictionaries = dictionaries,
+ strings = strings,
+ glyphs = glyphs,
+ nofglyphs = 4,
+ }
+ --
+ parsedictionaries(data,dictionaries,"cff")
+ --
+ local cid = data.dictionaries[1].cid
+ if cid and cid.fdselect then
+ readfdselect(f,fontdata,data,glyphs,false)
+ else
+ readnoselect(f,fontdata,data,glyphs,false)
+ end
+ return data
end
end
diff --git a/tex/context/base/mkiv/font-con.lua b/tex/context/base/mkiv/font-con.lua
index 18f221710..85ac33a10 100644
--- a/tex/context/base/mkiv/font-con.lua
+++ b/tex/context/base/mkiv/font-con.lua
@@ -10,7 +10,8 @@ if not modules then modules = { } end modules ['font-con'] = {
local next, tostring, rawget = next, tostring, rawget
local format, match, lower, gsub, find = string.format, string.match, string.lower, string.gsub, string.find
-local sort, insert, concat, sortedkeys, serialize, fastcopy = table.sort, table.insert, table.concat, table.sortedkeys, table.serialize, table.fastcopy
+local sort, insert, concat = table.sort, table.insert, table.concat
+local sortedkeys, sortedhash, serialize, fastcopy = table.sortedkeys, table.sortedhash, table.serialize, table.fastcopy
local derivetable = table.derive
local ioflush = io.flush
@@ -963,49 +964,28 @@ constructors.hashmethods = hashmethods
function constructors.hashfeatures(specification) -- will be overloaded
local features = specification.features
if features then
- local t, tn = { }, 0
- for category, list in next, features do
+ local t, n = { }, 0
+-- inspect(features)
+-- for category, list in next, features do
+ for category, list in sortedhash(features) do
if next(list) then
local hasher = hashmethods[category]
if hasher then
local hash = hasher(list)
if hash then
- tn = tn + 1
- t[tn] = category .. ":" .. hash
+ n = n + 1
+ t[n] = category .. ":" .. hash
end
end
end
end
- if tn > 0 then
+ if n > 0 then
return concat(t," & ")
end
end
return "unknown"
end
--- hashmethods.normal = function(list)
--- local s = { }
--- local n = 0
--- for k, v in next, list do
--- if not k then
--- -- no need to add to hash
--- elseif k == "number" or k == "features" then
--- -- no need to add to hash (maybe we need a skip list)
--- else
--- n = n + 1
--- s[n] = k
--- end
--- end
--- if n > 0 then
--- sort(s)
--- for i=1,n do
--- local k = s[i]
--- s[i] = k .. '=' .. tostring(list[k])
--- end
--- return concat(s,"+")
--- end
--- end
-
hashmethods.normal = function(list)
local s = { }
local n = 0
diff --git a/tex/context/base/mkiv/font-ctx.lua b/tex/context/base/mkiv/font-ctx.lua
index f53a4e643..0e64511e2 100644
--- a/tex/context/base/mkiv/font-ctx.lua
+++ b/tex/context/base/mkiv/font-ctx.lua
@@ -64,6 +64,8 @@ local hashes = fonts.hashes
local currentfont = font.current
local definefont = font.define
+local cleanname = names.cleanname
+
local encodings = fonts.encodings
----- aglunicodes = encodings.agl.unicodes
local aglunicodes = nil -- delayed loading
@@ -181,13 +183,52 @@ do
local shares = { }
local hashes = { }
+local nofinstances = 0
+local instances = table.setmetatableindex(function(t,k)
+ nofinstances = nofinstances + 1
+ t[k] = nofinstances
+ return nofinstances
+end)
+
function constructors.trytosharefont(target,tfmdata)
constructors.noffontsloaded = constructors.noffontsloaded + 1
if constructors.sharefonts then
- local fonthash = target.specification.hash
+ local fonthash = target.specification.hash
if fonthash then
local properties = target.properties
local fullname = target.fullname
+ local fontname = target.fontname
+ local psname = target.psname
+ -- for the moment here:
+ local instance = properties.instance
+ if instance then
+ local format = tfmdata.properties.format
+ if format == "opentype" then
+ target.streamprovider = 1
+ elseif format == "truetype" then
+ target.streamprovider = 2
+ else
+ target.streamprovider = 0
+ end
+ if target.streamprovider > 0 then
+ if fullname then
+ fullname = fullname .. ":" .. instances[instance]
+ target.fullname = fullname
+ end
+ if fontname then
+ fontname = fontname .. ":" .. instances[instance]
+ target.fontname = fontname
+ end
+ if psname then
+ -- this one is used for the funny prefix in font names in pdf
+ -- so it has ot be kind of unique in order to avoid subset prefix
+ -- clashes being reported
+ psname = psname .. ":" .. instances[instance]
+ target.psname = psname
+ end
+ end
+ end
+ --
local sharedname = hashes[fonthash]
if sharedname then
-- this is ok for context as we know that only features can mess with font definitions
@@ -201,10 +242,18 @@ do
constructors.nofsharedhashes = constructors.nofsharedhashes + 1
else
-- the one takes more time (in the worst case of many cjk fonts) but it also saves
- -- embedding time
+ -- embedding time .. haha, this is interesting: when i got a clash on subset tag
+ -- collision i saw in the source that these tags are also using a hash like below
+ -- so maybe we should have an option to pass it from lua
local characters = target.characters
local n = 1
local t = { target.psname }
+ -- for the moment here:
+ if instance then
+ n = n + 1
+ t[n] = instance
+ end
+ --
local u = sortedkeys(characters)
for i=1,#u do
local k = u[i]
@@ -1441,14 +1490,19 @@ end
local designsizes = constructors.designsizes
+-- called quite often when in mp labels
+-- otf.normalizedaxis
+
function constructors.hashinstance(specification,force)
- local hash, size, fallbacks = specification.hash, specification.size, specification.fallbacks
+ local hash = specification.hash
+ local size = specification.size
+ local fallbacks = specification.fallbacks
if force or not hash then
hash = constructors.hashfeatures(specification)
specification.hash = hash
end
if size < 1000 and designsizes[hash] then
- size = math.round(constructors.scaled(size,designsizes[hash]))
+ size = round(constructors.scaled(size,designsizes[hash]))
specification.size = size
end
if fallbacks then
@@ -1512,9 +1566,6 @@ function definers.resolve(specification) -- overload function in font-con.lua
return specification
end
-
-
-
-- soon to be obsolete:
local mappings = fonts.mappings
@@ -2302,100 +2353,106 @@ end
-- make a closure (200 limit):
-local trace_analyzing = false trackers.register("otf.analyzing", function(v) trace_analyzing = v end)
+do
-local analyzers = fonts.analyzers
-local methods = analyzers.methods
+ local trace_analyzing = false trackers.register("otf.analyzing", function(v) trace_analyzing = v end)
-local unsetvalue = attributes.unsetvalue
+ local analyzers = fonts.analyzers
+ local methods = analyzers.methods
-local traverse_id = nuts.traverse_id
+ local unsetvalue = attributes.unsetvalue
-local a_color = attributes.private('color')
-local a_colormodel = attributes.private('colormodel')
-local a_state = attributes.private('state')
-local m_color = attributes.list[a_color] or { }
+ local traverse_id = nuts.traverse_id
-local glyph_code = nodes.nodecodes.glyph
+ local a_color = attributes.private('color')
+ local a_colormodel = attributes.private('colormodel')
+ local a_state = attributes.private('state')
+ local m_color = attributes.list[a_color] or { }
-local states = analyzers.states
+ local glyph_code = nodes.nodecodes.glyph
-local colornames = {
- [states.init] = "font:1",
- [states.medi] = "font:2",
- [states.fina] = "font:3",
- [states.isol] = "font:4",
- [states.mark] = "font:5",
- [states.rest] = "font:6",
- [states.rphf] = "font:1",
- [states.half] = "font:2",
- [states.pref] = "font:3",
- [states.blwf] = "font:4",
- [states.pstf] = "font:5",
-}
+ local states = analyzers.states
+
+ local colornames = {
+ [states.init] = "font:1",
+ [states.medi] = "font:2",
+ [states.fina] = "font:3",
+ [states.isol] = "font:4",
+ [states.mark] = "font:5",
+ [states.rest] = "font:6",
+ [states.rphf] = "font:1",
+ [states.half] = "font:2",
+ [states.pref] = "font:3",
+ [states.blwf] = "font:4",
+ [states.pstf] = "font:5",
+ }
-local function markstates(head)
- if head then
- head = tonut(head)
- local model = getattr(head,a_colormodel) or 1
- for glyph in traverse_id(glyph_code,head) do
- local a = getprop(glyph,a_state)
- if a then
- local name = colornames[a]
- if name then
- local color = m_color[name]
- if color then
- setattr(glyph,a_colormodel,model)
- setattr(glyph,a_color,color)
+ local function markstates(head)
+ if head then
+ head = tonut(head)
+ local model = getattr(head,a_colormodel) or 1
+ for glyph in traverse_id(glyph_code,head) do
+ local a = getprop(glyph,a_state)
+ if a then
+ local name = colornames[a]
+ if name then
+ local color = m_color[name]
+ if color then
+ setattr(glyph,a_colormodel,model)
+ setattr(glyph,a_color,color)
+ end
end
end
end
end
end
-end
-local function analyzeprocessor(head,font,attr)
- local tfmdata = fontdata[font]
- local script, language = otf.scriptandlanguage(tfmdata,attr)
- local action = methods[script]
- if not action then
- return head, false
- end
- if type(action) == "function" then
- local head, done = action(head,font,attr)
- if done and trace_analyzing then
- markstates(head)
+ local function analyzeprocessor(head,font,attr)
+ local tfmdata = fontdata[font]
+ local script, language = otf.scriptandlanguage(tfmdata,attr)
+ local action = methods[script]
+ if not action then
+ return head, false
end
- return head, done
- end
- action = action[language]
- if action then
- local head, done = action(head,font,attr)
- if done and trace_analyzing then
- markstates(head)
+ if type(action) == "function" then
+ local head, done = action(head,font,attr)
+ if done and trace_analyzing then
+ markstates(head)
+ end
+ return head, done
+ end
+ action = action[language]
+ if action then
+ local head, done = action(head,font,attr)
+ if done and trace_analyzing then
+ markstates(head)
+ end
+ return head, done
+ else
+ return head, false
end
- return head, done
- else
- return head, false
end
-end
-registerotffeature { -- adapts
- name = "analyze",
- processors = {
- node = analyzeprocessor,
+ registerotffeature { -- adapts
+ name = "analyze",
+ processors = {
+ node = analyzeprocessor,
+ }
}
-}
-function methods.nocolor(head,font,attr)
- for n in traverse_id(glyph_code,head) do
- if not font or getfont(n) == font then
- setattr(n,a_color,unsetvalue)
+
+ function methods.nocolor(head,font,attr)
+ for n in traverse_id(glyph_code,head) do
+ if not font or getfont(n) == font then
+ setattr(n,a_color,unsetvalue)
+ end
end
+ return head, true
end
- return head, true
+
end
+
local function purefontname(name)
if type(name) == "number" then
name = getfontname(name)
@@ -2412,6 +2469,7 @@ implement {
}
local list = storage.shared.bodyfontsizes or { }
+
storage.shared.bodyfontsizes = list
implement {
@@ -2448,7 +2506,7 @@ implement {
implement {
name = "cleanfontname",
- actions = { names.cleanname, context },
+ actions = { cleanname, context },
arguments = "string"
}
@@ -2654,3 +2712,58 @@ do
}
end
+
+do
+
+ local function getinstancespec(id)
+ local data = fontdata[id or true]
+ local shared = data.shared
+ local resources = shared and shared.rawdata.resources
+ if resources then
+ local instancespec = data.properties.instance
+ if instancespec then
+ local variabledata = resources.variabledata
+ if variabledata then
+ local instances = variabledata.instances
+ if instances then
+ for i=1,#instances do
+ local instance = instances[i]
+ if cleanname(instance.subfamily)== instancespec then
+ local values = table.copy(instance.values)
+ local axis = variabledata.axis
+ for i=1,#values do
+ for j=1,#axis do
+ if values[i].axis == axis[j].tag then
+ values[i].name = axis[j].name
+ break
+ end
+ end
+ end
+ return values
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ helpers.getinstancespec = getinstancespec
+
+ implement {
+ name = "currentfontinstancespec",
+ actions = function()
+ local t = getinstancespec() -- current font
+ if t then
+ for i=1,#t do
+ if i > 1 then
+ context.space()
+ end
+ local ti = t[i]
+ context("%s=%s",ti.name,ti.value)
+ end
+ end
+ end
+ }
+
+end
diff --git a/tex/context/base/mkiv/font-def.lua b/tex/context/base/mkiv/font-def.lua
index 88d614566..6765be9d3 100644
--- a/tex/context/base/mkiv/font-def.lua
+++ b/tex/context/base/mkiv/font-def.lua
@@ -184,11 +184,30 @@ end
function resolvers.name(specification)
local resolve = fonts.names.resolve
if resolve then
- local resolved, sub, subindex = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
+ local resolved, sub, subindex, instance = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
if resolved then
specification.resolved = resolved
specification.sub = sub
specification.subindex = subindex
+ -- new, needed for experiments
+ if instance then
+ specification.instance = instance
+ local features = specification.features
+ if not features then
+ features = { }
+ specification.features = features
+ end
+ local normal = features.normal
+ if not normal then
+ normal = { }
+ features.normal = normal
+ end
+ normal.instance = instance
+if not callbacks.supported.glyph_stream_provider then
+ normal.variableshapes = true -- for the moment
+end
+ end
+ --
local suffix = lower(suffixonly(resolved))
if fonts.formats[suffix] then
specification.forced = suffix
@@ -296,6 +315,7 @@ end
function definers.loadfont(specification)
local hash = constructors.hashinstance(specification)
+ -- todo: also hash by instance / factors
local tfmdata = loadedfonts[hash] -- hashes by size !
if not tfmdata then
local forced = specification.forced or ""
diff --git a/tex/context/base/mkiv/font-dsp.lua b/tex/context/base/mkiv/font-dsp.lua
index 09c6aea3d..fc56df3f2 100644
--- a/tex/context/base/mkiv/font-dsp.lua
+++ b/tex/context/base/mkiv/font-dsp.lua
@@ -48,19 +48,28 @@ 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.
+
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 lpegmatch = lpeg.match
local setmetatableindex = table.setmetatableindex
local formatters = string.formatters
@@ -92,6 +101,13 @@ local readbyte = streamreader.readbyte
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 = {
@@ -130,6 +146,90 @@ 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,
+}
+
+-- using helpers doesn't make much sense, subtle differences
+--
+-- local function readushortarray(f,n)
+-- local t = { }
+-- for i=1,n do
+-- t[i] = readushort(f)
+-- end
+-- return t
+-- end
+--
+-- local function readulongarray(f,n)
+-- local t = { }
+-- for i=1,n do
+-- t[i] = readulong(f)
+-- end
+-- return t
+-- end
+--
+-- local function readushortarray(f,target,first,size)
+-- if not size then
+-- for i=1,size do
+-- target[i] = readushort(f)
+-- end
+-- else
+-- for i=1,size do
+-- target[first+i] = readushort(f)
+-- end
+-- end
+-- return target
+-- end
+--
+-- so we get some half helper - half non helper mix then
+
-- 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.
@@ -192,6 +292,264 @@ local lookupflags = setmetatableindex(function(t,k)
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 names = table.setmetatableindex ( {
+-- weight = "wght",
+-- width = "wdth",
+-- italic = "ital",
+-- }, "self")
+
+local pattern = lpeg.Cf (
+ lpeg.Ct("") *
+ lpeg.Cg (
+ --(lpeg.R("az")^1/names) * lpeg.S(" :") *
+ lpeg.C(lpeg.R("az")^1) * lpeg.S(" :=") *
+ (lpeg.patterns.number/tonumber) * lpeg.S(" ,")^0
+ )^1, rawset
+)
+
+local hash = table.setmetatableindex(function(t,k)
+ local v = lpegmatch(pattern,k)
+ local t = { }
+ for k, v in sortedhash(v) do
+ t[#t+1] = k .. "=" .. v
+ end
+ v = concat(t,",")
+ 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
+
+local function axistofactors(str)
+ return lpegmatch(pattern,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 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 = { }
+ for i=1,nofdeltadata do
+ deltadata[i] = readulong(f)
+ end
+ -- 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 = { } -- newtable
+ for i=1,nofshorts do
+ t[i] = readshort(f)
+ end
+ 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.
@@ -292,36 +650,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
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
+ 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
+ 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
+ else
+ return { 0, 0, h, 0 }
+ end
+ end
+ --
+ -- todo:
+ --
+ -- if format == 0x55 then
+ -- local x = readshort(f)
+ -- local h = readshort(f)
+ -- ....
+ -- end
+ --
+ local x = bittest(format,0x01) and readshort(f) or 0 -- x placement
+ local y = bittest(format,0x02) and readshort(f) or 0 -- y placement
+ local h = bittest(format,0x04) and readshort(f) or 0 -- h advance
+ local v = bittest(format,0x08) and readshort(f) or 0 -- v advance
+ if format >= 0x10 then
+ local X = bittest(format,0x10) and skipshort(f) or 0
+ local Y = bittest(format,0x20) and skipshort(f) or 0
+ local H = bittest(format,0x40) and skipshort(f) or 0
+ local V = bittest(format,0x80) 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
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
@@ -907,20 +1397,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
@@ -930,14 +1421,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
@@ -953,26 +1444,27 @@ 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
end
return {
format = "pair",
- coverage = coverage
+ 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
@@ -980,7 +1472,7 @@ function gposhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofg
end
return {
format = "pair",
- coverage = coverage
+ coverage = coverage,
}
else
report("unsupported subtype %a in %a positioning",subtype,"single")
@@ -997,13 +1489,14 @@ end
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]
@@ -1025,7 +1518,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)
@@ -1035,7 +1528,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)
@@ -1063,7 +1556,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")
@@ -1075,7 +1568,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)
@@ -1091,17 +1585,18 @@ function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nof
coverage = readcoverage(f,coverage)
for i=1,nofrecords do
local r = records[i]
+ -- slot 1 will become hash after loading (must be unique per lookup when packed)
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,
+ readanchor(f,r.entry,getdelta) or nil,
+ readanchor(f,r.exit, 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")
@@ -1111,7 +1606,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)
@@ -1130,17 +1626,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
@@ -1148,8 +1639,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
--
@@ -1203,7 +1693,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
@@ -1213,7 +1703,6 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp
end
end
end
--- components[i] = classes
end
end
end
@@ -1246,7 +1735,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
@@ -1304,10 +1793,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
@@ -1337,6 +1826,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
@@ -1495,8 +1988,8 @@ do
lookups[i] = readushort(f)
end
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)
@@ -1504,8 +1997,7 @@ 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
@@ -1530,23 +2022,9 @@ 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 { }
@@ -1767,45 +2245,124 @@ 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
+ setposition(f,variationsoffset+offset)
+ local nofconditions = readushort(f)
+ local conditions = { }
+ for i=1,nofconditions do
+ conditions[i] = variationsoffset+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 = { }
+ for i=1,noflookups do
+ lookups[i] = readushort(f) -- not sure what to do with these
+ end
+ -- 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
@@ -1836,7 +2393,7 @@ do
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?
+ local format = bit32.rshift(coverage,8) -- is this ok
if format == 0 then
local nofpairs = readushort(f)
local searchrange = readushort(f)
@@ -1904,91 +2461,126 @@ 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)
- local classformat = readushort(f)
- if classformat == 1 then
- local firstindex = readushort(f)
- local lastindex = firstindex + readushort(f) - 1
- for index=firstindex,lastindex do
- local class = classes[readushort(f)]
- if class == "mark" then
- marks[index] = true
- end
- glyphs[index].class = class
+ 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 = 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 varsetsoffset = version >= 0x00010003 and (tableoffset + readulong(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)
+ local classformat = readushort(f)
+ if classformat == 1 then
+ local firstindex = readushort(f)
+ local lastindex = firstindex + readushort(f) - 1
+ for index=firstindex,lastindex do
+ local class = classes[readushort(f)]
+ if class == "mark" then
+ marks[index] = true
end
- elseif classformat == 2 then
- local nofranges = readushort(f)
- for i=1,nofranges do
- local firstindex = readushort(f)
- local lastindex = readushort(f)
- local class = classes[readushort(f)]
- if class then
- for index=firstindex,lastindex do
- glyphs[index].class = class
- if class == "mark" then
- marks[index] = true
- end
+ glyphs[index].class = class
+ end
+ elseif classformat == 2 then
+ local nofranges = readushort(f)
+ for i=1,nofranges do
+ local firstindex = readushort(f)
+ local lastindex = readushort(f)
+ local class = classes[readushort(f)]
+ if class then
+ for index=firstindex,lastindex do
+ glyphs[index].class = class
+ if class == "mark" then
+ marks[index] = true
end
end
end
end
- -- mark classes
- setposition(f,markclassoffset)
- local classformat = readushort(f)
- if classformat == 1 then
+ end
+ -- mark classes
+ setposition(f,markclassoffset)
+ local classformat = readushort(f)
+ if classformat == 1 then
+ local firstindex = readushort(f)
+ local lastindex = firstindex + readushort(f) - 1
+ for index=firstindex,lastindex do
+ markclasses[readushort(f)][index] = true
+ end
+ elseif classformat == 2 then
+ local nofranges = readushort(f)
+ for i=1,nofranges do
local firstindex = readushort(f)
- local lastindex = firstindex + readushort(f) - 1
+ local lastindex = readushort(f)
+ local class = markclasses[readushort(f)]
for index=firstindex,lastindex do
- markclasses[readushort(f)][index] = true
+ class[index] = true
end
- elseif classformat == 2 then
- local nofranges = readushort(f)
- for i=1,nofranges do
- local firstindex = readushort(f)
- local lastindex = readushort(f)
- local class = markclasses[readushort(f)]
- for index=firstindex,lastindex do
- class[index] = true
+ 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
+ for i=1,nofsets do
+ local offset = sets[i]
+ if offset ~= 0 then
+ marksets[i] = readcoverage(f,marksetsoffset+offset)
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
- for i=1,nofsets do
- local offset = sets[i]
- if offset ~= 0 then
- marksets[i] = readcoverage(f,marksetsoffset+offset)
+ end
+
+ local factors = specification.factors
+
+ if (specification.variable or factors) and varsetsoffset and varsetsoffset > tableoffset then
+
+ local regions, deltas = readvariationdata(f,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
@@ -2277,173 +2869,534 @@ 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)
- end
- if glyphinfo ~= 0 then
- readmathglyphinfo(f,fontdata,tableoffset+glyphinfo)
- end
- if variants ~= 0 then
- readmathvariants(f,fontdata,tableoffset+variants)
- end
+ 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 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
- 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
+ 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
- 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
- glyphs[glyphindex].colors = t
+ 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
+ glyphs[glyphindex].colors = t
end
- fontdata.hascolor = true
end
+ fontdata.hascolor = true
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
+ 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 = { }
+ 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
+ for i=1,nofpalettes do
+ local p = { }
+ local o = palettes[i]
+ for j=1,nofpaletteentries do
+ p[j] = colorrecords[o+j]
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,
+ palettes[i] = p
+ end
+ fontdata.colorpalettes = palettes
+ end
+end
+
+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
+
+-- + 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
+ axis[i] = {
+ tag = readtag(f),
+ name = lower(extras[readushort(f)]),
+ 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)])
+ 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
- for i=1,nofpalettes do
- local p = { }
- local o = palettes[i]
- for j=1,nofpaletteentries do
- p[j] = colorrecords[o+j]
+ 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 valeus 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 = readulong(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 }
+ lasfrom, lastto = f, t
end
- palettes[i] = p
end
- fontdata.colorpalettes = palettes
+ 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,size-1 do
+ some = values[i]
+ if some[1] == 0 and some[2] == 0 then
+ return values
+ end
+ end
+ end
+ end
+ end
+ return false
+ end
+
+ local version = readulong(f) -- 1.0
+ local reserved = readulong(f)
+ local nofaxis = readulong(f)
+ local segments = { }
+ for i=1,nofaxis do
+ segments[i] = collect()
end
+ setvariabledata(fontdata,"segments",segments)
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
+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), -- we get weird values from a test font ... to be checked
+ default = readfixed(f), -- idem
+ maximum = readfixed(f), -- idem
+ flags = readushort(f),
+ name = lower(extras[readushort(f)]),
+ }
+ local n = sizeofaxis - 20
+ if n > 0 then
+ skipbytes(f,n)
+ elseif n < 0 then
+ -- error
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
+ --
+ 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
+ -- depends on what we want to see:
+ --
+ -- values[axis[i].tag] = readfixed(f)
+ --
+ values[i] = {
+ axis = axis[i].tag,
+ value = readfixed(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)
- }
+ 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
+ end
+ setvariabledata(fontdata,"axis",axis)
+ setvariabledata(fontdata,"instances",instances)
+ end
+end
+
+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
+ 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
+ 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
diff --git a/tex/context/base/mkiv/font-fil.mkvi b/tex/context/base/mkiv/font-fil.mkvi
index fbe4b8442..ba9d5e2c6 100644
--- a/tex/context/base/mkiv/font-fil.mkvi
+++ b/tex/context/base/mkiv/font-fil.mkvi
@@ -469,4 +469,8 @@
\setxvalue{\??fontclass\fontclass#style\s!designsize}{#designsize}%
\setxvalue{\??fontclass\fontclass#style\s!direction }{#direction}}
+% bonus
+
+\let\currentfontinstancespec\clf_currentfontinstancespec % expandable
+
\protect \endinput
diff --git a/tex/context/base/mkiv/font-lib.mkvi b/tex/context/base/mkiv/font-lib.mkvi
index fa8797394..e37da2545 100644
--- a/tex/context/base/mkiv/font-lib.mkvi
+++ b/tex/context/base/mkiv/font-lib.mkvi
@@ -61,10 +61,6 @@
%registerctxluafile{font-afm}{1.001}
\registerctxluafile{font-afk}{1.001}
-% shapes
-
-\registerctxluafile{font-shp}{1.001}
-
% tfm
\registerctxluafile{font-tfm}{1.001}
@@ -74,6 +70,10 @@
\registerctxluafile{font-syn}{1.001}
\registerctxluafile{font-trt}{1.001}
+% shapes
+
+\registerctxluafile{font-shp}{1.001}
+
% so far
\registerctxluafile{font-pat}{1.001} % patchers
diff --git a/tex/context/base/mkiv/font-mps.lua b/tex/context/base/mkiv/font-mps.lua
index 6c441699b..69b2af68c 100644
--- a/tex/context/base/mkiv/font-mps.lua
+++ b/tex/context/base/mkiv/font-mps.lua
@@ -307,8 +307,12 @@ function metapost.output(kind,font,char,advance,shift,ex)
xfactor = xfactor * wfactor
end
local paths = topaths(glyf,xfactor,yfactor)
- local code = f_code(kind,#paths,advance,shift,paths)
- return code, character.width * fc * wfactor
+ if paths then
+ local code = f_code(kind,#paths,advance,shift,paths)
+ return code, character.width * fc * wfactor
+ else
+ return "", 0
+ end
end
end
end
diff --git a/tex/context/base/mkiv/font-onr.lua b/tex/context/base/mkiv/font-onr.lua
index d986a0ddc..7fa51bf05 100644
--- a/tex/context/base/mkiv/font-onr.lua
+++ b/tex/context/base/mkiv/font-onr.lua
@@ -209,7 +209,7 @@ do
}
},
}
- fonts.handlers.otf.readers.parsecharstrings(data,glyphs,true,true)
+ fonts.handlers.otf.readers.parsecharstrings(false,data,glyphs,true,true)
else
lpegmatch(p_filternames,binary)
end
diff --git a/tex/context/base/mkiv/font-otc.lua b/tex/context/base/mkiv/font-otc.lua
index a99d3db9f..dc855a74d 100644
--- a/tex/context/base/mkiv/font-otc.lua
+++ b/tex/context/base/mkiv/font-otc.lua
@@ -318,9 +318,18 @@ local function addfeature(data,feature,specifications)
return coverage
end
+ local function resetspacekerns()
+ -- a bit of a hack, this nil setting but it forces a
+ -- rehash of the resources needed .. the feature itself
+ -- should be a kern (at least for now)
+ data.properties.hasspacekerns = true
+ data.resources .spacekerns = nil
+ end
+
local function prepare_kern(list,featuretype)
local coverage = { }
local cover = coveractions[featuretype]
+ local isspace = false
for code, replacement in next, list do
local unicode = tounicode(code)
local description = descriptions[unicode]
@@ -330,11 +339,17 @@ local function addfeature(data,feature,specifications)
local u = tounicode(k)
if u then
r[u] = v
+ if u == 32 then
+ isspace = true
+ end
end
end
if next(r) then
cover(coverage,unicode,r)
done = done + 1
+ if unicode == 32 then
+ isspace = true
+ end
else
skip = skip + 1
end
@@ -342,6 +357,9 @@ local function addfeature(data,feature,specifications)
skip = skip + 1
end
end
+ if isspace then
+ resetspacekerns()
+ end
return coverage
end
@@ -358,11 +376,17 @@ local function addfeature(data,feature,specifications)
local u = tounicode(k)
if u then
r[u] = v
+ if u == 32 then
+ isspace = true
+ end
end
end
if next(r) then
cover(coverage,unicode,r)
done = done + 1
+ if unicode == 32 then
+ isspace = true
+ end
else
skip = skip + 1
end
@@ -370,6 +394,9 @@ local function addfeature(data,feature,specifications)
skip = skip + 1
end
end
+ if isspace then
+ resetspacekerns()
+ end
else
report_otf("unknown cover type %a",featuretype)
end
diff --git a/tex/context/base/mkiv/font-oti.lua b/tex/context/base/mkiv/font-oti.lua
index e10a261cd..4c6053be0 100644
--- a/tex/context/base/mkiv/font-oti.lua
+++ b/tex/context/base/mkiv/font-oti.lua
@@ -160,3 +160,103 @@ function otffeatures.checkeddefaultlanguage(featuretype,autolanguage,languages)
end
end
end
+
+-- the following might become available generic in due time but for now
+-- this is some context playground (development code)
+
+-- if not context then
+-- return
+-- end
+
+-- local helpers = otf.readers.helpers
+-- local axistofactors = helpers.axistofactors
+-- local normalizedaxis = helpers.normalizedaxis
+-- local getaxisscale = helpers.getaxisscale
+-- local cleanname = containers.cleanname
+
+-- local function validvariable(tfmdata)
+-- if tfmdata.properties.factors then
+-- return
+-- end
+-- local resources = tfmdata.resources
+-- local variabledata = resources and resources.variabledata
+-- if not variabledata then
+-- return
+-- end
+-- local instances = variabledata.instances
+-- local axis = variabledata.axis
+-- local segments = variabledata.segments
+-- if instances and axis then
+-- return instances, axis, segments
+-- end
+-- end
+
+-- local function initializeinstance(tfmdata,value)
+-- if type(value) == "string" then
+-- local instances, axis, segments = validvariable(tfmdata)
+-- if instances then
+-- local values
+-- for i=1,#instances do
+-- local instance = instances[i]
+-- if cleanname(instance.subfamily) == value then
+-- values = instance.values
+-- break
+-- 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
+-- tfmdata.properties.instance = {
+-- hash = instance,
+-- factors = factors,
+-- }
+-- end
+-- else
+-- report("incomplete variable data")
+-- end
+-- end
+-- end
+
+-- local function initializeaxis(tfmdata,value)
+-- if type(value) == "string" then
+-- local instances, axis, segments = validvariable(tfmdata)
+-- if instances then
+-- local values = axistofactors(value)
+-- 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
+-- tfmdata.properties.instance = {
+-- hash = cleanname(value),
+-- factors = factors,
+-- }
+-- end
+-- else
+-- report("incomplete variable data")
+-- end
+-- end
+-- end
+
+-- registerotffeature {
+-- name = "instance",
+-- description = "variation instance",
+-- initializers = {
+-- node = initializeinstance,
+-- base = initializeinstance,
+-- }
+-- }
+
+-- registerotffeature {
+-- name = "axis",
+-- description = "variation axis",
+-- initializers = {
+-- node = initializeaxis,
+-- base = initializeaxis,
+-- }
+-- }
diff --git a/tex/context/base/mkiv/font-otl.lua b/tex/context/base/mkiv/font-otl.lua
index 4b97a90a2..9e4e255e3 100644
--- a/tex/context/base/mkiv/font-otl.lua
+++ b/tex/context/base/mkiv/font-otl.lua
@@ -52,7 +52,7 @@ local report_otf = logs.reporter("fonts","otf loading")
local fonts = fonts
local otf = fonts.handlers.otf
-otf.version = 3.027 -- beware: also sync font-mis.lua and in mtx-fonts
+otf.version = 3.028 -- beware: also sync font-mis.lua and in mtx-fonts
otf.cache = containers.define("fonts", "otl", otf.version, true)
otf.svgcache = containers.define("fonts", "svg", otf.version, true)
otf.pdfcache = containers.define("fonts", "pdf", otf.version, true)
@@ -93,31 +93,16 @@ registerdirective("fonts.otf.loader.force", function(v) forceload =
registerdirective("fonts.otf.loader.syncspace", function(v) syncspace = v end)
registerdirective("fonts.otf.loader.forcenotdef", function(v) forcenotdef = v end)
--- local function load_featurefile(raw,featurefile)
--- if featurefile and featurefile ~= "" then
--- if trace_loading then
--- report_otf("using featurefile %a", featurefile)
--- end
--- -- TODO: apply_featurefile(raw, featurefile)
--- end
--- end
-
-- otfenhancers.patch("before","migrate metadata","cambria",function() end)
registerotfenhancer("check extra features", function() end) -- placeholder
-function otf.load(filename,sub,featurefile) -- second argument (format) is gone !
- --
- local featurefile = nil -- not supported (yet)
- --
+function otf.load(filename,sub,instance)
local base = file.basename(file.removesuffix(filename))
- local name = file.removesuffix(base)
+ local name = file.removesuffix(base) -- already no suffix
local attr = lfs.attributes(filename)
local size = attr and attr.size or 0
local time = attr and attr.modification or 0
- if featurefile then
- name = name .. "@" .. file.removesuffix(file.basename(featurefile))
- end
-- sub can be number of string
if sub == "" then
sub = false
@@ -126,69 +111,22 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
if sub then
hash = hash .. "-" .. sub
end
+ if instance then
+ hash = hash .. "-" .. instance
+ end
hash = containers.cleanname(hash)
- -- local featurefiles
- -- if featurefile then
- -- featurefiles = { }
- -- for s in gmatch(featurefile,"[^,]+") do
- -- local name = resolvers.findfile(file.addsuffix(s,'fea'),'fea') or ""
- -- if name == "" then
- -- report_otf("loading error, no featurefile %a",s)
- -- else
- -- local attr = lfs.attributes(name)
- -- featurefiles[#featurefiles+1] = {
- -- name = name,
- -- size = attr and attr.size or 0,
- -- time = attr and attr.modification or 0,
- -- }
- -- end
- -- end
- -- if #featurefiles == 0 then
- -- featurefiles = nil
- -- end
- -- end
local data = containers.read(otf.cache,hash)
local reload = not data or data.size ~= size or data.time ~= time or data.tableversion ~= otfreaders.tableversion
if forceload then
report_otf("forced reload of %a due to hard coded flag",filename)
reload = true
end
- -- if not reload then
- -- local featuredata = data.featuredata
- -- if featurefiles then
- -- if not featuredata or #featuredata ~= #featurefiles then
- -- reload = true
- -- else
- -- for i=1,#featurefiles do
- -- local fi, fd = featurefiles[i], featuredata[i]
- -- if fi.name ~= fd.name or fi.size ~= fd.size or fi.time ~= fd.time then
- -- reload = true
- -- break
- -- end
- -- end
- -- end
- -- elseif featuredata then
- -- reload = true
- -- end
- -- if reload then
- -- report_otf("loading: forced reload due to changed featurefile specification %a",featurefile)
- -- end
- -- end
if reload then
report_otf("loading %a, hash %a",filename,hash)
--
starttiming(otfreaders)
- data = otfreaders.loadfont(filename,sub or 1) -- we can pass the number instead (if it comes from a name search)
- --
- -- if featurefiles then
- -- for i=1,#featurefiles do
- -- load_featurefile(data,featurefiles[i].name)
- -- end
- -- end
- --
- --
+ data = otfreaders.loadfont(filename,sub or 1,instance) -- we can pass the number instead (if it comes from a name search)
if data then
- --
local resources = data.resources
local svgshapes = resources.svgshapes
if svgshapes then
@@ -206,7 +144,6 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
}
end
end
- --
otfreaders.compact(data)
otfreaders.rehash(data,"unicodes")
otfreaders.addunicodetable(data)
@@ -550,7 +487,8 @@ local function otftotfm(specification)
local subindex = specification.subindex
local filename = specification.filename
local features = specification.features.normal
- local rawdata = otf.load(filename,sub,features and features.featurefile)
+ local instance = specification.instance or (features and features.axis)
+ local rawdata = otf.load(filename,sub,instance)
if rawdata and next(rawdata) then
local descriptions = rawdata.descriptions
rawdata.lookuphash = { } -- to be done
diff --git a/tex/context/base/mkiv/font-otr.lua b/tex/context/base/mkiv/font-otr.lua
index 43a059bb9..eab33f8b6 100644
--- a/tex/context/base/mkiv/font-otr.lua
+++ b/tex/context/base/mkiv/font-otr.lua
@@ -75,12 +75,13 @@ local setmetatableindex = table.setmetatableindex
local formatters = string.formatters
local sortedkeys = table.sortedkeys
local sortedhash = table.sortedhash
-local stripstring = string.strip
+local stripstring = string.nospaces
local utf16_to_utf8_be = utf.utf16_to_utf8_be
local report = logs.reporter("otf reader")
local trace_cmap = false -- only for checking issues
+local trace_cmap_detail = false -- only for checking issues
fonts = fonts or { }
local handlers = fonts.handlers or { }
@@ -703,6 +704,34 @@ local panosewidths = {
-- We implement a reader per table.
+-- helper
+
+local helpers = { }
+readers.helpers = helpers
+
+local function gotodatatable(f,fontdata,tag,criterium)
+ if criterium and f then
+ local datatable = fontdata.tables[tag]
+ if datatable then
+ local tableoffset = datatable.offset
+ setposition(f,tableoffset)
+ return tableoffset
+ end
+ end
+end
+
+local function setvariabledata(fontdata,tag,data)
+ local variabledata = fontdata.variabledata
+ if variabledata then
+ variabledata[tag] = data
+ else
+ fontdata.variabledata = { [tag] = data }
+ end
+end
+
+helpers.gotodatatable = gotodatatable
+helpers.setvariabledata = setvariabledata
+
-- The name table is probably the first one to load. After all this one provides
-- useful information about what we deal with. The complication is that we need
-- to filter the best one available.
@@ -718,14 +747,13 @@ local platformnames = {
}
function readers.name(f,fontdata,specification)
- local datatable = fontdata.tables.name
- if datatable then
- setposition(f,datatable.offset)
+ local tableoffset = gotodatatable(f,fontdata,"name",true)
+ if tableoffset then
local format = readushort(f)
local nofnames = readushort(f)
local offset = readushort(f)
-- we can also provide a raw list as extra, todo as option
- local start = datatable.offset + offset
+ local start = tableoffset + offset
local namelists = {
unicode = { },
windows = { },
@@ -748,25 +776,15 @@ function readers.name(f,fontdata,specification)
if encoding and language then
local index = readushort(f)
local name = reservednames[index]
- if name then
- namelist[#namelist+1] = {
- platform = platform,
- encoding = encoding,
- language = language,
- name = name,
- length = readushort(f),
- offset = start + readushort(f),
- }
- else
-namelist[#namelist+1] = {
- platform = platform,
- encoding = encoding,
- language = language,
- length = readushort(f),
- offset = start + readushort(f),
-}
--- skipshort(f,2)
- end
+ namelist[#namelist+1] = {
+ platform = platform,
+ encoding = encoding,
+ language = language,
+ name = name,
+ index = index,
+ length = readushort(f),
+ offset = start + readushort(f),
+ }
else
skipshort(f,3)
end
@@ -803,6 +821,7 @@ namelist[#namelist+1] = {
for i=1,#namelist do
local name = namelist[i]
local nametag = name.name
+ local index = name.index
if not done[nametag or i] then
local encoding = name.encoding
local language = name.language
@@ -824,7 +843,7 @@ namelist[#namelist+1] = {
language = language,
}
end
- extras[i-1] = content
+ extras[index] = content
done[nametag or i] = true
end
end
@@ -896,9 +915,8 @@ end
-- properties table afterwards.
readers["os/2"] = function(f,fontdata)
- local datatable = fontdata.tables["os/2"]
- if datatable then
- setposition(f,datatable.offset)
+ local tableoffset = gotodatatable(f,fontdata,"os/2",true)
+ if tableoffset then
local version = readushort(f)
local windowsmetrics = {
version = version,
@@ -957,9 +975,8 @@ readers["os/2"] = function(f,fontdata)
end
readers.head = function(f,fontdata)
- local datatable = fontdata.tables.head
- if datatable then
- setposition(f,datatable.offset)
+ local tableoffset = gotodatatable(f,fontdata,"head",true)
+ if tableoffset then
local fontheader = {
version = readfixed(f),
revision = readfixed(f),
@@ -990,68 +1007,60 @@ end
-- variables are not used but nofmetrics is quite important.
readers.hhea = function(f,fontdata,specification)
- if specification.details then
- local datatable = fontdata.tables.hhea
- if datatable then
- setposition(f,datatable.offset)
- fontdata.horizontalheader = {
- version = readfixed(f), -- two ushorts: major minor
- ascender = readfword(f),
- descender = readfword(f),
- linegap = readfword(f),
- maxadvancewidth = readufword(f),
- minleftsidebearing = readfword(f),
- minrightsidebearing = readfword(f),
- maxextent = readfword(f),
- caretsloperise = readshort(f),
- caretsloperun = readshort(f),
- caretoffset = readshort(f),
- reserved_1 = readshort(f),
- reserved_2 = readshort(f),
- reserved_3 = readshort(f),
- reserved_4 = readshort(f),
- metricdataformat = readshort(f),
- nofmetrics = readushort(f),
- }
- else
- fontdata.horizontalheader = {
- nofmetrics = 0,
- }
- end
+ local tableoffset = gotodatatable(f,fontdata,"hhea",specification.details)
+ if tableoffset then
+ fontdata.horizontalheader = {
+ version = readfixed(f), -- two ushorts: major minor
+ ascender = readfword(f),
+ descender = readfword(f),
+ linegap = readfword(f),
+ maxadvancewidth = readufword(f),
+ minleftsidebearing = readfword(f),
+ minrightsidebearing = readfword(f),
+ maxextent = readfword(f),
+ caretsloperise = readshort(f),
+ caretsloperun = readshort(f),
+ caretoffset = readshort(f),
+ reserved_1 = readshort(f),
+ reserved_2 = readshort(f),
+ reserved_3 = readshort(f),
+ reserved_4 = readshort(f),
+ metricdataformat = readshort(f),
+ nofmetrics = readushort(f),
+ }
+ else
+ fontdata.horizontalheader = {
+ nofmetrics = 0,
+ }
end
end
readers.vhea = function(f,fontdata,specification)
- if specification.details then
- local datatable = fontdata.tables.vhea
- if datatable then
- setposition(f,datatable.offset)
- local version = readfixed(f)
- fontdata.verticalheader = {
- version = version,
- ascender = readfword(f),
- descender = readfword(f),
- linegap = readfword(f),
- maxadvanceheight = readufword(f),
- mintopsidebearing = readfword(f),
- minbottomsidebearing = readfword(f),
- maxextent = readfword(f),
- caretsloperise = readshort(f),
- caretsloperun = readshort(f),
- caretoffset = readshort(f),
- reserved_1 = readshort(f),
- reserved_2 = readshort(f),
- reserved_3 = readshort(f),
- reserved_4 = readshort(f),
- metricdataformat = readshort(f),
- nofmetrics = readushort(f),
- }
--- inspect(fontdata.verticalheader)
- else
- fontdata.verticalheader = {
- nofmetrics = 0,
- }
- end
+ local tableoffset = gotodatatable(f,fontdata,"vhea",specification.details)
+ if tableoffset then
+ fontdata.verticalheader = {
+ version = readfixed(f),
+ ascender = readfword(f),
+ descender = readfword(f),
+ linegap = readfword(f),
+ maxadvanceheight = readufword(f),
+ mintopsidebearing = readfword(f),
+ minbottomsidebearing = readfword(f),
+ maxextent = readfword(f),
+ caretsloperise = readshort(f),
+ caretsloperun = readshort(f),
+ caretoffset = readshort(f),
+ reserved_1 = readshort(f),
+ reserved_2 = readshort(f),
+ reserved_3 = readshort(f),
+ reserved_4 = readshort(f),
+ metricdataformat = readshort(f),
+ nofmetrics = readushort(f),
+ }
+ else
+ fontdata.verticalheader = {
+ nofmetrics = 0,
+ }
end
end
@@ -1061,44 +1070,40 @@ end
-- fontdata.maximumprofile can be bad
readers.maxp = function(f,fontdata,specification)
- if specification.details then
- local datatable = fontdata.tables.maxp
- if datatable then
- setposition(f,datatable.offset)
- local version = readfixed(f)
- local nofglyphs = readushort(f)
- fontdata.nofglyphs = nofglyphs
- if version == 0.5 then
- fontdata.maximumprofile = {
- version = version,
- nofglyphs = nofglyphs,
- }
- return
- elseif version == 1.0 then
- fontdata.maximumprofile = {
- version = version,
- nofglyphs = nofglyphs,
- points = readushort(f),
- contours = readushort(f),
- compositepoints = readushort(f),
- compositecontours = readushort(f),
- zones = readushort(f),
- twilightpoints = readushort(f),
- storage = readushort(f),
- functiondefs = readushort(f),
- instructiondefs = readushort(f),
- stackelements = readushort(f),
- sizeofinstructions = readushort(f),
- componentelements = readushort(f),
- componentdepth = readushort(f),
- }
- return
- end
+ local tableoffset = gotodatatable(f,fontdata,"maxp",specification.details)
+ if tableoffset then
+ local version = readfixed(f)
+ local nofglyphs = readushort(f)
+ fontdata.nofglyphs = nofglyphs
+ if version == 0.5 then
+ fontdata.maximumprofile = {
+ version = version,
+ nofglyphs = nofglyphs,
+ }
+ elseif version == 1.0 then
+ fontdata.maximumprofile = {
+ version = version,
+ nofglyphs = nofglyphs,
+ points = readushort(f),
+ contours = readushort(f),
+ compositepoints = readushort(f),
+ compositecontours = readushort(f),
+ zones = readushort(f),
+ twilightpoints = readushort(f),
+ storage = readushort(f),
+ functiondefs = readushort(f),
+ instructiondefs = readushort(f),
+ stackelements = readushort(f),
+ sizeofinstructions = readushort(f),
+ componentelements = readushort(f),
+ componentdepth = readushort(f),
+ }
+ else
+ fontdata.maximumprofile = {
+ version = version,
+ nofglyphs = 0,
+ }
end
- fontdata.maximumprofile = {
- version = version,
- nofglyphs = 0,
- }
end
end
@@ -1106,86 +1111,78 @@ end
-- course).
readers.hmtx = function(f,fontdata,specification)
- if specification.glyphs then
- local datatable = fontdata.tables.hmtx
- if datatable then
- setposition(f,datatable.offset)
- local horizontalheader = fontdata.horizontalheader
- local nofmetrics = horizontalheader.nofmetrics
- local glyphs = fontdata.glyphs
- local nofglyphs = fontdata.nofglyphs
- local width = 0 -- advance
- local leftsidebearing = 0
- for i=0,nofmetrics-1 do
- local glyph = glyphs[i]
- width = readshort(f)
- leftsidebearing = readshort(f)
- if width ~= 0 then
- glyph.width = width
- end
- -- if leftsidebearing ~= 0 then
- -- glyph.lsb = leftsidebearing
- -- end
+ local tableoffset = gotodatatable(f,fontdata,"hmtx",specification.glyphs)
+ if tableoffset then
+ local horizontalheader = fontdata.horizontalheader
+ local nofmetrics = horizontalheader.nofmetrics
+ local glyphs = fontdata.glyphs
+ local nofglyphs = fontdata.nofglyphs
+ local width = 0 -- advance
+ local leftsidebearing = 0
+ for i=0,nofmetrics-1 do
+ local glyph = glyphs[i]
+ width = readshort(f)
+ leftsidebearing = readshort(f)
+ if width ~= 0 then
+ glyph.width = width
end
- -- The next can happen in for instance a monospace font or in a cjk font
- -- with fixed widths.
- for i=nofmetrics,nofglyphs-1 do
- local glyph = glyphs[i]
- if width ~= 0 then
- glyph.width = width
- end
- -- if leftsidebearing ~= 0 then
- -- glyph.lsb = leftsidebearing
- -- end
+ -- if leftsidebearing ~= 0 then
+ -- glyph.lsb = leftsidebearing
+ -- end
+ end
+ -- The next can happen in for instance a monospace font or in a cjk font
+ -- with fixed widths.
+ for i=nofmetrics,nofglyphs-1 do
+ local glyph = glyphs[i]
+ if width ~= 0 then
+ glyph.width = width
end
+ -- if leftsidebearing ~= 0 then
+ -- glyph.lsb = leftsidebearing
+ -- end
end
+ -- hm, there can be a lsb here
end
end
readers.vmtx = function(f,fontdata,specification)
- if specification.glyphs then
- local datatable = fontdata.tables.vmtx
- if datatable then
- setposition(f,datatable.offset)
- local verticalheader = fontdata.verticalheader
- local nofmetrics = verticalheader.nofmetrics
- local glyphs = fontdata.glyphs
- local nofglyphs = fontdata.nofglyphs
- local vheight = 0
- local vdefault = verticalheader.ascender + verticalheader.descender
- local topsidebearing = 0
- for i=0,nofmetrics-1 do
- local glyph = glyphs[i]
- vheight = readshort(f)
- topsidebearing = readshort(f)
- if vheight ~= 0 and vheight ~= vdefault then
- glyph.vheight = vheight
- end
- -- if topsidebearing ~= 0 then
- -- glyph.tsb = topsidebearing
- -- end
+ local tableoffset = gotodatatable(f,fontdata,"vmtx",specification.glyphs)
+ if tableoffset then
+ local verticalheader = fontdata.verticalheader
+ local nofmetrics = verticalheader.nofmetrics
+ local glyphs = fontdata.glyphs
+ local nofglyphs = fontdata.nofglyphs
+ local vheight = 0
+ local vdefault = verticalheader.ascender + verticalheader.descender
+ local topsidebearing = 0
+ for i=0,nofmetrics-1 do
+ local glyph = glyphs[i]
+ vheight = readshort(f)
+ topsidebearing = readshort(f)
+ if vheight ~= 0 and vheight ~= vdefault then
+ glyph.vheight = vheight
end
- -- The next can happen in for instance a monospace font or in a cjk font
- -- with fixed heights.
- for i=nofmetrics,nofglyphs-1 do
- local glyph = glyphs[i]
- if vheight ~= 0 and vheight ~= vdefault then
- glyph.vheight = vheight
- end
- -- if topsidebearing ~= 0 then
- -- glyph.tsb = topsidebearing
- -- end
+ -- if topsidebearing ~= 0 then
+ -- glyph.tsb = topsidebearing
+ -- end
+ end
+ -- The next can happen in for instance a monospace font or in a cjk font
+ -- with fixed heights.
+ for i=nofmetrics,nofglyphs-1 do
+ local glyph = glyphs[i]
+ if vheight ~= 0 and vheight ~= vdefault then
+ glyph.vheight = vheight
end
+ -- if topsidebearing ~= 0 then
+ -- glyph.tsb = topsidebearing
+ -- end
end
end
end
readers.vorg = function(f,fontdata,specification)
if specification.glyphs then
- local datatable = fontdata.tables.vorg
- if datatable then
- report("todo: %s","vorg")
- end
+ -- reportskippedtable("vorg")
end
end
@@ -1194,9 +1191,8 @@ end
-- description is somewhat fuzzy but it is a hybrid with overloads.
readers.post = function(f,fontdata,specification)
- local datatable = fontdata.tables.post
- if datatable then
- setposition(f,datatable.offset)
+ local tableoffset = gotodatatable(f,fontdata,"post",true)
+ if tableoffset then
local version = readfixed(f)
fontdata.postscript = {
version = version,
@@ -1225,7 +1221,7 @@ readers.post = function(f,fontdata,specification)
for i=0,nofglyphs-1 do
local nameindex = readushort(f)
if nameindex >= 258 then
- maxnames = maxnames + 1
+ maxnames = maxnames + 1
nameindex = nameindex - 257
indices[nameindex] = i
else
@@ -1238,7 +1234,7 @@ readers.post = function(f,fontdata,specification)
report("quit post name fetching at %a of %a: %s",i,maxnames,"no index")
break
else
- local length = readbyte(f)
+ local length = readbyte(f)
if length > 0 then
glyphs[mapping].name = readstring(f,length)
else
@@ -1365,7 +1361,7 @@ formatreaders[4] = function(f,fontdata,offset)
elseif offset == 0xFFFF then
-- bad encoding
elseif offset == 0 then
- if trace_cmap then
+ if trace_cmap_detail then
report("format 4.%i segment %2i from %C upto %C at index %H",1,segment,startchar,endchar,(startchar + delta) % 65536)
end
for unicode=startchar,endchar do
@@ -1398,7 +1394,7 @@ formatreaders[4] = function(f,fontdata,offset)
end
else
local shift = (segment-nofsegments+offset/2) - startchar
- if trace_cmap then
+ if trace_cmap_detail then
report("format 4.%i segment %2i from %C upto %C at index %H",0,segment,startchar,endchar,(startchar + delta) % 65536)
end
for unicode=startchar,endchar do
@@ -1448,7 +1444,7 @@ formatreaders[6] = function(f,fontdata,offset)
local count = readushort(f)
local stop = start+count-1
local nofdone = 0
- if trace_cmap then
+ if trace_cmap_detail then
report("format 6 from %C to %C",2,start,stop)
end
for unicode=start,stop do
@@ -1485,7 +1481,7 @@ formatreaders[12] = function(f,fontdata,offset)
local first = readulong(f)
local last = readulong(f)
local index = readulong(f)
- if trace_cmap then
+ if trace_cmap_detail then
report("format 12 from %C to %C starts at index %i",first,last,index)
end
for unicode=first,last do
@@ -1529,7 +1525,7 @@ formatreaders[13] = function(f,fontdata,offset)
local last = readulong(f)
local index = readulong(f)
if first < privateoffset then
- if trace_cmap then
+ if trace_cmap_detail then
report("format 13 from %C to %C get index %i",first,last,index)
end
local glyph = glyphs[index]
@@ -1635,76 +1631,82 @@ local function checkcmap(f,fontdata,records,platform,encoding,format)
local p = platforms[platform]
local e = encodings[p]
local n = reader(f,fontdata,data) or 0
- report("cmap checked: platform %i (%s), encoding %i (%s), format %i, new unicodes %i",platform,p,encoding,e and e[encoding] or "?",format,n)
+ if trace_cmap then
+ report("cmap checked: platform %i (%s), encoding %i (%s), format %i, new unicodes %i",platform,p,encoding,e and e[encoding] or "?",format,n)
+ end
return n
end
function readers.cmap(f,fontdata,specification)
- if specification.glyphs then
- local datatable = fontdata.tables.cmap
- if datatable then
- local tableoffset = datatable.offset
- setposition(f,tableoffset)
- local version = readushort(f)
- local noftables = readushort(f)
- local records = { }
- local unicodecid = false
- local variantcid = false
- local variants = { }
- local duplicates = fontdata.duplicates or { }
- fontdata.duplicates = duplicates
- for i=1,noftables do
- local platform = readushort(f)
- local encoding = readushort(f)
- local offset = readulong(f)
- local record = records[platform]
- if not record then
- records[platform] = {
- [encoding] = {
- offsets = { offset },
- formats = { },
- }
+ local tableoffset = gotodatatable(f,fontdata,"cmap",specification.glyphs)
+ if tableoffset then
+ local version = readushort(f)
+ local noftables = readushort(f)
+ local records = { }
+ local unicodecid = false
+ local variantcid = false
+ local variants = { }
+ local duplicates = fontdata.duplicates or { }
+ fontdata.duplicates = duplicates
+ for i=1,noftables do
+ local platform = readushort(f)
+ local encoding = readushort(f)
+ local offset = readulong(f)
+ local record = records[platform]
+ if not record then
+ records[platform] = {
+ [encoding] = {
+ offsets = { offset },
+ formats = { },
+ }
+ }
+ else
+ local subtables = record[encoding]
+ if not subtables then
+ record[encoding] = {
+ offsets = { offset },
+ formats = { },
}
else
- local subtables = record[encoding]
- if not subtables then
- record[encoding] = {
- offsets = { offset },
- formats = { },
- }
- else
- local offsets = subtables.offsets
- offsets[#offsets+1] = offset
- end
+ local offsets = subtables.offsets
+ offsets[#offsets+1] = offset
end
end
+ end
+ if trace_cmap then
report("found cmaps:")
- for platform, record in sortedhash(records) do
- local p = platforms[platform]
- local e = encodings[p]
- local sp = supported[platform]
- local ps = p or "?"
+ end
+ for platform, record in sortedhash(records) do
+ local p = platforms[platform]
+ local e = encodings[p]
+ local sp = supported[platform]
+ local ps = p or "?"
+ if trace_cmap then
if sp then
report(" platform %i: %s",platform,ps)
else
report(" platform %i: %s (unsupported)",platform,ps)
end
- for encoding, subtables in sortedhash(record) do
- local se = sp and sp[encoding]
- local es = e and e[encoding] or "?"
+ end
+ for encoding, subtables in sortedhash(record) do
+ local se = sp and sp[encoding]
+ local es = e and e[encoding] or "?"
+ if trace_cmap then
if se then
report(" encoding %i: %s",encoding,es)
else
report(" encoding %i: %s (unsupported)",encoding,es)
end
- local offsets = subtables.offsets
- local formats = subtables.formats
- for i=1,#offsets do
- local offset = tableoffset + offsets[i]
- setposition(f,offset)
- formats[readushort(f)] = offset
- end
- record[encoding] = formats
+ end
+ local offsets = subtables.offsets
+ local formats = subtables.formats
+ for i=1,#offsets do
+ local offset = tableoffset + offsets[i]
+ setposition(f,offset)
+ formats[readushort(f)] = offset
+ end
+ record[encoding] = formats
+ if trace_cmap then
local list = sortedkeys(formats)
for i=1,#list do
if not (se and se[list[i]]) then
@@ -1714,27 +1716,27 @@ function readers.cmap(f,fontdata,specification)
report(" formats: % t",list)
end
end
- --
- local ok = false
- for i=1,#sequence do
- local si = sequence[i]
- local sp, se, sf = si[1], si[2], si[3]
- if checkcmap(f,fontdata,records,sp,se,sf) > 0 then
- ok = true
- end
- end
- if not ok then
- report("no useable unicode cmap found")
+ end
+ --
+ local ok = false
+ for i=1,#sequence do
+ local si = sequence[i]
+ local sp, se, sf = si[1], si[2], si[3]
+ if checkcmap(f,fontdata,records,sp,se,sf) > 0 then
+ ok = true
end
- --
- fontdata.cidmaps = {
- version = version,
- noftables = noftables,
- records = records,
- }
- else
- fontdata.cidmaps = { }
end
+ if not ok then
+ report("no useable unicode cmap found")
+ end
+ --
+ fontdata.cidmaps = {
+ version = version,
+ noftables = noftables,
+ records = records,
+ }
+ else
+ fontdata.cidmaps = { }
end
end
@@ -1778,42 +1780,39 @@ end
-- can also be available. Todo: we need a 'fake' lookup for this (analogue to ff).
function readers.kern(f,fontdata,specification)
- if specification.kerns then
- local datatable = fontdata.tables.kern
- if datatable then
- setposition(f,datatable.offset)
- local version = readushort(f)
- local noftables = readushort(f)
- 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)
- local kerns = { }
- local glyphs = fontdata.glyphs
- for i=1,nofpairs do
- local left = readushort(f)
- local right = readushort(f)
- local kern = readfword(f)
- local glyph = glyphs[left]
- local kerns = glyph.kerns
- if kerns then
- kerns[right] = kern
- else
- glyph.kerns = { [right] = kern }
- end
+ local tableoffset = gotodatatable(f,fontdata,"kern",specification.kerns)
+ if tableoffset then
+ local version = readushort(f)
+ local noftables = readushort(f)
+ 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)
+ local kerns = { }
+ local glyphs = fontdata.glyphs
+ for i=1,nofpairs do
+ local left = readushort(f)
+ local right = readushort(f)
+ local kern = readfword(f)
+ local glyph = glyphs[left]
+ local kerns = glyph.kerns
+ if kerns then
+ kerns[right] = kern
+ else
+ glyph.kerns = { [right] = kern }
end
- elseif format == 2 then
- report("todo: kern classes")
- else
- report("todo: kerns")
end
+ elseif format == 2 then
+ report("todo: kern classes")
+ else
+ report("todo: kerns")
end
end
end
@@ -1847,7 +1846,7 @@ end
-- some properties in order to read following tables. When details is true we also
-- initialize the glyphs data.
-local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo)
+local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo,instancenames)
local fontdata = sub and maindata.subfonts and maindata.subfonts[sub] or maindata
local names = fontdata.names
local info = nil
@@ -1872,6 +1871,25 @@ local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo)
if not familyname then familyname = family end
if not subfamilyname then subfamilyname = subfamily end
end
+ if platformnames then
+ platformnames = fontdata.platformnames
+ end
+ if instancenames then
+ local variabledata = fontdata.variabledata
+ if variabledata then
+ local instances = variabledata and variabledata.instances
+ if instances then
+ instancenames = { }
+ for i=1,#instances do
+ instancenames[i] = lower(stripstring(instances[i].subfamily))
+ end
+ else
+ instancenames = nil
+ end
+ else
+ instancenames = nil
+ end
+ end
info = { -- we inherit some inconsistencies/choices from ff
subfontindex = fontdata.subfontindex or sub or 0,
-- filename = filename,
@@ -1902,7 +1920,8 @@ local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo)
capheight = metrics.capheight, -- not always present and probably crap
ascender = metrics.typoascender,
descender = metrics.typodescender,
- platformnames = platformnames and fontdata.platformnames or nil,
+ platformnames = platformnames or nil,
+ instancenames = instancenames or nil,
}
if metricstoo then
local keys = {
@@ -1960,6 +1979,7 @@ local function loadtables(f,specification,offset)
entryselector = readushort(f), -- not needed
rangeshift = readushort(f), -- not needed
tables = tables,
+ foundtables = false,
}
for i=1,fontdata.noftables do
local tag = lower(stripstring(readstring(f,4)))
@@ -1975,7 +1995,8 @@ local function loadtables(f,specification,offset)
length = length,
}
end
- if tables.cff then
+ fontdata.foundtables = sortedkeys(tables)
+ if tables.cff or tables.cff2 then
fontdata.format = "opentype"
else
fontdata.format = "truetype"
@@ -1996,23 +2017,35 @@ local function prepareglyps(fontdata)
fontdata.mapping = { }
end
-local function readtable(tag,f,fontdata,specification)
+local function readtable(tag,f,fontdata,specification,...)
local reader = readers[tag]
if reader then
-- local t = os.clock()
- reader(f,fontdata,specification)
+ reader(f,fontdata,specification,...)
-- report("reading table %a took %0.4f seconds",tag,os.clock()-t)
end
end
+local variablefonts_supported = context and true or false
+
local function readdata(f,offset,specification)
+
local fontdata = loadtables(f,specification,offset)
+
if specification.glyphs then
prepareglyps(fontdata)
end
- --
+
+ if not variablefonts_supported then
+ specification.instance = nil
+ specification.variable = nil
+ specification.factors = nil
+ end
+
+ fontdata.temporary = { }
+
readtable("name",f,fontdata,specification)
- --
+
local askedname = specification.askedname
if askedname then
local fullname = getname(fontdata,"fullname") or ""
@@ -2022,7 +2055,35 @@ local function readdata(f,offset,specification)
return -- keep searching
end
end
- --
+
+ readtable("stat",f,fontdata,specification)
+ readtable("avar",f,fontdata,specification)
+ readtable("fvar",f,fontdata,specification)
+
+ if variablefonts_supported then
+
+ if not specification.factors then
+ local instance = specification.instance
+ if type(instance) == "string" then
+ local factors = helpers.getfactors(fontdata,instance)
+ specification.factors = factors
+ fontdata.factors = factors
+ fontdata.instance = instance
+ report("user instance: %s, factors: % t",instance,factors)
+ end
+ end
+ if not fontdata.factors then
+ if fontdata.variabledata then
+ local factors = helpers.getfactors(fontdata,true)
+ specification.factors = factors
+ fontdata.factors = factors
+ fontdata.instance = instance
+ report("font instance: %s, factors: % t",instance,factors)
+ end
+ end
+
+ end
+
readtable("os/2",f,fontdata,specification)
readtable("head",f,fontdata,specification)
readtable("maxp",f,fontdata,specification)
@@ -2032,32 +2093,35 @@ local function readdata(f,offset,specification)
readtable("vmtx",f,fontdata,specification)
readtable("vorg",f,fontdata,specification)
readtable("post",f,fontdata,specification)
+
+ readtable("mvar",f,fontdata,specification)
+ readtable("hvar",f,fontdata,specification)
+ readtable("vvar",f,fontdata,specification)
+
+ readtable("gdef",f,fontdata,specification)
+
readtable("cff" ,f,fontdata,specification)
+ readtable("cff2",f,fontdata,specification)
+
readtable("cmap",f,fontdata,specification)
- readtable("loca",f,fontdata,specification)
- readtable("glyf",f,fontdata,specification)
+ readtable("loca",f,fontdata,specification) -- maybe load it in glyf
+ readtable("glyf",f,fontdata,specification) -- loads gvar
+
readtable("colr",f,fontdata,specification)
readtable("cpal",f,fontdata,specification)
readtable("svg" ,f,fontdata,specification)
+
readtable("kern",f,fontdata,specification)
- readtable("gdef",f,fontdata,specification)
readtable("gsub",f,fontdata,specification)
readtable("gpos",f,fontdata,specification)
+
readtable("math",f,fontdata,specification)
- --
- -- there are no proper fonts yet:
- --
- readtable("fvar",f,fontdata,specification) -- probably
- readtable("hvar",f,fontdata,specification)
- readtable("vvar",f,fontdata,specification)
- readtable("mvar",f,fontdata,specification) -- probably
- readtable("vorg",f,fontdata,specification)
- --
+
fontdata.locations = nil
fontdata.tables = nil
fontdata.cidmaps = nil
fontdata.dictionaries = nil
- -- fontdata.cff = nil
+ -- fontdata.cff = nil
return fontdata
end
@@ -2132,7 +2196,7 @@ local function loadfontdata(specification)
end
end
-local function loadfont(specification,n)
+local function loadfont(specification,n,instance)
if type(specification) == "string" then
specification = {
filename = specification,
@@ -2147,6 +2211,7 @@ local function loadfont(specification,n)
-- true or number:
subfont = n or true,
tounicode = false,
+ instance = instance
}
end
-- if shapes only then
@@ -2162,6 +2227,10 @@ local function loadfont(specification,n)
if specification.platformnames then
specification.platformnames = true -- not really used any more
end
+ if specification.instance or instance then
+ specification.variable = true
+ specification.instance = specification.instance or instance
+ end
local function message(str)
report("fatal error in file %a: %s\n%s",specification.filename,str,debug.traceback())
end
@@ -2173,11 +2242,14 @@ end
-- we need even less, but we can have a 'detail' variant
-function readers.loadshapes(filename,n)
+function readers.loadshapes(filename,n,instance,streams)
local fontdata = loadfont {
filename = filename,
shapes = true,
+ streams = streams,
+ variable = true,
subfont = n,
+ instance = instance,
}
if fontdata then
-- easier on luajit but still we can hit the 64 K stack constants issue
@@ -2202,7 +2274,7 @@ function readers.loadshapes(filename,n)
}
end
-function readers.loadfont(filename,n)
+function readers.loadfont(filename,n,instance)
local fontdata = loadfont {
filename = filename,
glyphs = true,
@@ -2212,9 +2284,9 @@ function readers.loadfont(filename,n)
-- kerns = true,
-- globalkerns = true, -- only for testing, e.g. cambria has different gpos and kern
subfont = n,
+ instance = instance,
}
if fontdata then
- --
return {
tableversion = tableversion,
creator = "context mkiv",
@@ -2224,11 +2296,13 @@ function readers.loadfont(filename,n)
descriptions = fontdata.descriptions,
format = fontdata.format,
goodies = { },
- metadata = getinfo(fontdata,n,false,false,true), -- no platformnames here !
+ metadata = getinfo(fontdata,n,false,false,true,true), -- no platformnames here !
properties = {
hasitalics = fontdata.hasitalics or false,
maxcolorclass = fontdata.maxcolorclass,
hascolor = fontdata.hascolor or false,
+ instance = fontdata.instance,
+ factors = fontdata.factors,
},
resources = {
-- filename = fontdata.filename,
@@ -2247,7 +2321,8 @@ function readers.loadfont(filename,n)
mathconstants = fontdata.mathconstants,
colorpalettes = fontdata.colorpalettes,
svgshapes = fontdata.svgshapes,
- variable = fontdata.variable,
+ variabledata = fontdata.variabledata,
+ foundtables = fontdata.foundtables,
},
}
end
@@ -2259,6 +2334,7 @@ function readers.getinfo(filename,specification) -- string, nil|number|table
local subfont = nil
local platformnames = false
local rawfamilynames = false
+ local instancenames = true
if type(specification) == "table" then
subfont = tonumber(specification.subfont)
platformnames = specification.platformnames
@@ -2270,20 +2346,21 @@ function readers.getinfo(filename,specification) -- string, nil|number|table
filename = filename,
details = true,
platformnames = platformnames,
+ instancenames = true,
-- rawfamilynames = rawfamilynames,
}
if fontdata then
local subfonts = fontdata.subfonts
if not subfonts then
- return getinfo(fontdata,nil,platformnames,rawfamilynames)
+ return getinfo(fontdata,nil,platformnames,rawfamilynames,false,instancenames)
elseif not subfont then
local info = { }
for i=1,#subfonts do
- info[i] = getinfo(fontdata,i,platformnames,rawfamilynames)
+ info[i] = getinfo(fontdata,i,platformnames,rawfamilynames,false,instancenames)
end
return info
elseif subfont >= 1 and subfont <= #subfonts then
- return getinfo(fontdata,subfont,platformnames,rawfamilynames)
+ return getinfo(fontdata,subfont,platformnames,rawfamilynames,false,instancenames)
else
return {
filename = filename,
diff --git a/tex/context/base/mkiv/font-ott.lua b/tex/context/base/mkiv/font-ott.lua
index 48d05d492..cba3758dc 100644
--- a/tex/context/base/mkiv/font-ott.lua
+++ b/tex/context/base/mkiv/font-ott.lua
@@ -1082,6 +1082,8 @@ table.setmetatableindex(usedfeatures, function(t,k) if k then local v = { } t[k]
storage.register("fonts/otf/usedfeatures", usedfeatures, "fonts.handlers.otf.statistics.usedfeatures" )
+local normalizedaxis = otf.readers.helpers.normalizedaxis or function(s) return s end
+
function otffeatures.normalize(features)
if features then
local h = { }
@@ -1093,6 +1095,11 @@ function otffeatures.normalize(features)
elseif k == "script" then
local v = gsub(lower(value),"[^a-z0-9]","")
h.script = rawget(verbosescripts,v) or (scripts[v] and v) or "dflt" -- auto adds
+ elseif k == "axis" then
+ h[k] = normalizedaxis(value)
+if not callbacks.supported.glyph_stream_provider then
+ h.variableshapes = true -- for the moment
+end
else
local uk = usedfeatures[key]
local uv = uk[value]
diff --git a/tex/context/base/mkiv/font-oup.lua b/tex/context/base/mkiv/font-oup.lua
index cfa90c794..bbc8436b2 100644
--- a/tex/context/base/mkiv/font-oup.lua
+++ b/tex/context/base/mkiv/font-oup.lua
@@ -1202,6 +1202,7 @@ function readers.pack(data)
local sublookups = resources.sublookups
local features = resources.features
local palettes = resources.colorpalettes
+ local variable = resources.variabledata
local chardata = characters and characters.data
local descriptions = data.descriptions or data.glyphs
@@ -1377,6 +1378,53 @@ function readers.pack(data)
end
+ if variable then
+
+ -- todo: segments
+
+ local instances = variable.instances
+ if instances then
+ for i=1,#instances do
+ local v = instances[i].values
+ for j=1,#v do
+ v[j] = pack_normal(v[j])
+ end
+ end
+ end
+
+ local function packdeltas(main)
+ if main then
+ local deltas = main.deltas
+ if deltas then
+ for i=1,#deltas do
+ local di = deltas[i]
+ local d = di.deltas
+ local r = di.regions
+ for j=1,#d do
+ d[j] = pack_indexed(d[j])
+ end
+ di.regions = pack_indexed(di.regions)
+ end
+ end
+ local regions = main.regions
+ if regions then
+ for i=1,#regions do
+ local r = regions[i]
+ for j=1,#r do
+ r[j] = pack_normal(r[j])
+ end
+ end
+ end
+ end
+ end
+
+ packdeltas(variable.global)
+ packdeltas(variable.horizontal)
+ packdeltas(variable.vertical)
+ packdeltas(variable.metrics)
+
+ end
+
if not success(1,pass) then
return
end
@@ -1453,10 +1501,23 @@ function readers.pack(data)
if sublookups then
packthem(sublookups)
end
- -- features
- if not success(2,pass) then
- -- return
+ if variable then
+ local function unpackdeltas(main)
+ if main then
+ local regions = main.regions
+ if regions then
+ main.regions = pack_normal(regions)
+ end
+ end
+ end
+ unpackdeltas(variable.global)
+ unpackdeltas(variable.horizontal)
+ unpackdeltas(variable.vertical)
+ unpackdeltas(variable.metrics)
end
+ -- if not success(2,pass) then
+ -- -- return
+ -- end
end
for pass=1,2 do
@@ -1525,6 +1586,7 @@ function readers.unpack(data)
local sublookups = resources.sublookups
local features = resources.features
local palettes = resources.colorpalettes
+ local variable = resources.variabledata
local unpacked = { }
setmetatable(unpacked,unpacked_mt)
for unicode, description in next, descriptions do
@@ -1810,6 +1872,70 @@ function readers.unpack(data)
end
end
+ if variable then
+
+ -- todo: segments
+
+ local instances = variable.instances
+ if instances then
+ for i=1,#instances do
+ local v = instances[i].values
+ for j=1,#v do
+ local tv = tables[v[j]]
+ if tv then
+ v[j] = tv
+ end
+ end
+ end
+ end
+
+ local function unpackdeltas(main)
+ if main then
+ local deltas = main.deltas
+ if deltas then
+ for i=1,#deltas do
+ local di = deltas[i]
+ local d = di.deltas
+ local r = di.regions
+ for j=1,#d do
+ local tv = tables[d[j]]
+ if tv then
+ d[j] = tv
+ end
+ end
+ local tv = di.regions
+ if tv then
+ di.regions = tv
+ end
+ end
+ end
+ local regions = main.regions
+ if regions then
+ local tv = tables[regions]
+ if tv then
+ main.regions = tv
+ regions = tv
+ end
+ for i=1,#regions do
+ local r = regions[i]
+ for j=1,#r do
+ local tv = tables[r[j]]
+ if tv then
+ r[j] = tv
+ end
+ end
+ end
+ end
+ end
+ end
+
+ unpackdeltas(variable.global)
+ unpackdeltas(variable.horizontal)
+ unpackdeltas(variable.vertical)
+ unpackdeltas(variable.metrics)
+
+ end
+
data.tables = nil
end
end
diff --git a/tex/context/base/mkiv/font-shp.lua b/tex/context/base/mkiv/font-shp.lua
index 92ff70127..9447af94c 100644
--- a/tex/context/base/mkiv/font-shp.lua
+++ b/tex/context/base/mkiv/font-shp.lua
@@ -6,17 +6,19 @@ if not modules then modules = { } end modules ['font-shp'] = {
license = "see context related readme files"
}
+local tonumber = tonumber
local concat = table.concat
-local load, tonumber = load, tonumber
+local formatters = string.formatters
-local otf = fonts.handlers.otf
-local afm = fonts.handlers.afm
+local otf = fonts.handlers.otf
+local afm = fonts.handlers.afm
-local hashes = fonts.hashes
-local identifiers = hashes.identifiers
+local hashes = fonts.hashes
+local identifiers = hashes.identifiers
-local version = 0.006
-local cache = containers.define("fonts", "shapes", version, true)
+local version = 0.007
+local shapescache = containers.define("fonts", "shapes", version, true)
+local streamscache = containers.define("fonts", "streams", version, true)
-- shapes (can be come a separate file at some point)
@@ -128,7 +130,19 @@ end
-- todo: loaders per format
-local function load(filename,sub)
+local readers = otf.readers
+local cleanname = readers.helpers.cleanname
+
+local function makehash(filename,sub,instance)
+ local name = cleanname(file.basename(filename))
+ if instance then
+ return formatters["%s-%s-%s"](name,sub or 0,cleanname(instance))
+ else
+ return formatters["%s-%s"] (name,sub or 0)
+ end
+end
+
+local function loadoutlines(cache,filename,sub,instance)
local base = file.basename(filename)
local name = file.removesuffix(base)
local kind = file.suffix(filename)
@@ -140,13 +154,10 @@ local function load(filename,sub)
-- fonts.formats
if size > 0 and (kind == "otf" or kind == "ttf" or kind == "tcc") then
- local hash = containers.cleanname(base) -- including suffix
- if sub then
- hash = hash .. "-" .. sub
- end
+ local hash = makehash(filename,sub,instance)
data = containers.read(cache,hash)
if not data or data.time ~= time or data.size ~= size then
- data = otf.readers.loadshapes(filename,sub)
+ data = readers.loadshapes(filename,sub,instance)
if data then
data.size = size
data.format = data.format or (kind == "otf" and "opentype") or "truetype"
@@ -185,9 +196,196 @@ local function load(filename,sub)
return data
end
+local function loadstreams(cache,filename,sub,instance)
+ local base = file.basename(filename)
+ local name = file.removesuffix(base)
+ local kind = file.suffix(filename)
+ local attr = lfs.attributes(filename)
+ local size = attr and attr.size or 0
+ local time = attr and attr.modification or 0
+ local sub = tonumber(sub)
+
+ -- fonts.formats
+
+ if size > 0 and (kind == "otf" or kind == "ttf" or kind == "tcc") then
+ local hash = makehash(filename,sub,instance)
+ data = containers.read(cache,hash)
+ if not data or data.time ~= time or data.size ~= size then
+ data = readers.loadshapes(filename,sub,instance,true)
+ if data then
+ local glyphs = data.glyphs
+ local streams = { }
+ if glyphs then
+ for i=0,#glyphs do
+ streams[i] = glyphs[i].stream or ""
+ end
+ end
+ data.streams = streams
+ data.glyphs = nil
+ data.size = size
+ data.format = data.format or (kind == "otf" and "opentype") or "truetype"
+ data.time = time
+ containers.write(cache,hash,data)
+ data = containers.read(cache,hash) -- frees old mem
+ end
+ end
+ else
+ data = {
+ filename = filename,
+ size = 0,
+ time = time,
+ format = "unknown",
+ glyphs = { }
+ }
+ end
+ return data
+end
+
+local loadedshapes = { }
+local loadedstreams = { }
+
+local function loadoutlinedata(fontdata,streams)
+ local properties = fontdata.properties
+ local filename = properties.filename
+ local subindex = fontdata.subindex
+ local instance = properties.instance
+ local hash = makehash(filename,subindex,instance)
+ local loaded = loadedshapes[hash]
+ if not loaded then
+ loaded = loadoutlines(shapescache,filename,subindex,instance)
+ loadedshapes[hash] = loaded
+ end
+ return loaded
+end
+
hashes.shapes = table.setmetatableindex(function(t,k)
- local d = identifiers[k]
- local v = load(d.properties.filename,d.subindex)
- t[k] = v
- return v
+ local f = identifiers[k]
+ if f then
+ return loadoutlinedata(f)
+ end
end)
+
+local function loadstreamdata(fontdata,streams)
+ local properties = fontdata.properties
+ local filename = properties.filename
+ local subindex = fontdata.subindex
+ local instance = properties.instance
+ local hash = makehash(filename,subindex,instance)
+ local loaded = loadedstreams[hash]
+ if not loaded then
+ loaded = loadstreams(streamscache,filename,subindex,instance)
+ loadedstreams[hash] = loaded
+ end
+ return loaded
+end
+
+hashes.streams = table.setmetatableindex(function(t,k)
+ local f = identifiers[k]
+ if f then
+ return loadstreamdata(f,true)
+ end
+end)
+
+otf.loadoutlinedata = loadoutlinedata -- not public
+otf.loadstreamdata = loadstreamdata -- not public
+otf.loadshapes = loadshapes
+
+-- experimental code, for me only ... unsupported
+
+local f_c = string.formatters["%F %F %F %F %F %F c"]
+local f_l = string.formatters["%F %F l"]
+local f_m = string.formatters["%F %F m"]
+
+local function segmentstopdf(segments,factor,bt,et)
+ local t = { }
+ local n = #segments
+ for i=1,n do
+ local s = segments[i]
+ local m = #s
+ local w = s[m]
+ if w == "c" then
+ t[i] = f_c(s[1]*factor,s[2]*factor,s[3]*factor,s[4]*factor,s[5]*factor,s[6]*factor)
+ elseif w == "l" then
+ t[i] = f_l(s[1]*factor,s[2]*factor)
+ elseif w == "m" then
+ t[i] = f_m(s[1]*factor,s[2]*factor)
+ else
+ t[i] = ""
+ end
+ end
+ t[n+1] = "h f" -- B*
+ if bt and et then
+ t[0] = bt
+ t[n+2] = et
+ return concat(t,"\n",0,n+2)
+ else
+ return concat(t,"\n")
+ end
+end
+
+local function addvariableshapes(tfmdata,key,value)
+ if value then
+ local shapes = otf.loadoutlinedata(tfmdata)
+ if not shapes then
+ return
+ end
+ local glyphs = shapes.glyphs
+ if not glyphs then
+ return
+ end
+ local characters = tfmdata.characters
+ local parameters = tfmdata.parameters
+ local hfactor = parameters.hfactor * (7200/7227)
+ local factor = hfactor / 65536
+ local getactualtext = otf.getactualtext
+ for unicode, char in next, characters do
+ if not char.commands then
+ local shape = glyphs[char.index]
+ if shape then
+ local segments = shape.segments
+ if segments then
+ -- we need inline in order to support color
+ local bt, et = getactualtext(char.tounicode or char.unicode or unicode)
+ char.commands = {
+ { "special", "pdf:" .. segmentstopdf(segments,factor,bt,et) }
+ }
+ end
+ end
+ end
+ end
+ end
+end
+
+otf.features.register {
+ name = "variableshapes", -- enforced for now
+ description = "variable shapes",
+ manipulators = {
+ base = addvariableshapes,
+ node = addvariableshapes,
+ }
+}
+
+-- In the end it is easier to just provide the new charstring (cff) and points (ttdf). First
+-- of all we already have the right information so there is no need to patch the already complex
+-- backend code (we only need to make sure the cff is valid). Also, I prototyped support for
+-- these fonts using (converted to) normal postscript shapes, a functionality that was already
+-- present for a while for metafun. This solution even permits us to come up with usage of such
+-- fonts in unexpected ways. It also opens the road to shapes generated with metafun includes
+-- as real cff (or ttf) shapes instead of virtual in-line shapes.
+--
+-- This is probably a prelude to writing a complete backend font inclusion plugin in lua. After
+-- all I already have most info. For this we just need to pass a list of used glyphs (or analyze
+-- them ourselves).
+
+local streams = fonts.hashes.streams
+
+callback.register("glyph_stream_provider",function(id,index,mode)
+ if id > 0 then
+ local streams = streams[id].streams
+ -- print(id,index,streams[index])
+ if streams then
+ return streams[index] or ""
+ end
+ end
+ return ""
+ end)
diff --git a/tex/context/base/mkiv/font-syn.lua b/tex/context/base/mkiv/font-syn.lua
index a383370f5..558d07fe7 100644
--- a/tex/context/base/mkiv/font-syn.lua
+++ b/tex/context/base/mkiv/font-syn.lua
@@ -16,7 +16,7 @@ if not modules then modules = { } end modules ['font-syn'] = {
local next, tonumber, type, tostring = next, tonumber, type, tostring
local sub, gsub, match, find, lower, upper = string.sub, string.gsub, string.match, string.find, string.lower, string.upper
-local concat, sort = table.concat, table.sort
+local concat, sort, fastcopy = table.concat, table.sort, table.fastcopy
local serialize, sortedhash = table.serialize, table.sortedhash
local lpegmatch = lpeg.match
local unpack = unpack or table.unpack
@@ -623,6 +623,8 @@ local function check_name(data,result,filename,modification,suffix,subfont)
local pfmwidth = result.pfmwidth or 0
local pfmweight = result.pfmweight or 0
--
+ local instancenames = result.instancenames
+ --
specifications[#specifications+1] = {
filename = filename, -- unresolved
cleanfilename = cleanfilename,
@@ -650,6 +652,7 @@ local function check_name(data,result,filename,modification,suffix,subfont)
maxsize = maxsize ~= 0 and maxsize or nil,
designsize = designsize ~= 0 and designsize or nil,
modification = modification ~= 0 and modification or nil,
+ instancenames = instancenames or nil,
}
end
@@ -806,6 +809,7 @@ local function collecthashes()
local format = specification.format
local fullname = specification.fullname
local fontname = specification.fontname
+ -- local rawname = specification.rawname
-- local compatiblename = specification.compatiblename
-- local cfffullname = specification.cfffullname
local familyname = specification.familyname or specification.family
@@ -814,6 +818,7 @@ local function collecthashes()
local weight = specification.weight
local mapping = mappings[format]
local fallback = fallbacks[format]
+ local instancenames = specification.instancenames
if fullname and not mapping[fullname] then
mapping[fullname] = index
nofmappings = nofmappings + 1
@@ -822,6 +827,14 @@ local function collecthashes()
mapping[fontname] = index
nofmappings = nofmappings + 1
end
+ if instancenames then
+ for i=1,#instancenames do
+ local instance = fullname .. instancenames[i]
+ mapping[instance] = index
+ nofmappings = nofmappings + 1
+
+ end
+ end
-- if compatiblename and not mapping[compatiblename] then
-- mapping[compatiblename] = index
-- nofmappings = nofmappings + 1
@@ -1365,6 +1378,23 @@ end
-- we could cache a lookup .. maybe some day ... (only when auto loaded!)
+local function checkinstance(found,askedname)
+ local instancenames = found.instancenames
+ if instancenames then
+ local fullname = found.fullname
+ for i=1,#instancenames do
+ local instancename = instancenames[i]
+ if fullname .. instancename == askedname then
+ local f = fastcopy(found)
+ f.instances = nil
+ f.instance = instancename
+ return f
+ end
+ end
+ end
+ return found
+end
+
local function foundname(name,sub) -- sub is not used currently
local data = names.data
local mappings = data.mappings
@@ -1382,7 +1412,7 @@ local function foundname(name,sub) -- sub is not used currently
if trace_names then
report_names("resolved via direct name match: %a",name)
end
- return found
+ return checkinstance(found,name)
end
end
for i=1,#list do
@@ -1392,7 +1422,7 @@ local function foundname(name,sub) -- sub is not used currently
if trace_names then
report_names("resolved via fuzzy name match: %a onto %a",name,fname)
end
- return found
+ return checkinstance(found,name)
end
end
for i=1,#list do
@@ -1402,7 +1432,7 @@ local function foundname(name,sub) -- sub is not used currently
if trace_names then
report_names("resolved via direct fallback match: %a",name)
end
- return found
+ return checkinstance(found,name)
end
end
for i=1,#list do
@@ -1412,7 +1442,7 @@ local function foundname(name,sub) -- sub is not used currently
if trace_names then
report_names("resolved via fuzzy fallback match: %a onto %a",name,fname)
end
- return found
+ return checkinstance(found,name)
end
end
if trace_names then
@@ -1435,7 +1465,7 @@ end
function names.resolve(askedname,sub)
local found = names.resolvedspecification(askedname,sub)
if found then
- return found.filename, found.subfont and found.rawname, found.subfont
+ return found.filename, found.subfont and found.rawname, found.subfont, found.instance
end
end
diff --git a/tex/context/base/mkiv/font-ttf.lua b/tex/context/base/mkiv/font-ttf.lua
index 6df339214..d222de4ba 100644
--- a/tex/context/base/mkiv/font-ttf.lua
+++ b/tex/context/base/mkiv/font-ttf.lua
@@ -6,42 +6,81 @@ if not modules then modules = { } end modules ['font-ttf'] = {
license = "see context related readme files"
}
+-- This version is different from previous in the sense that we no longer store
+-- contours but keep points and contours (endpoints) separate for a while
+-- because later on we need to apply deltas and that is easier on a list of
+-- points.
+
+-- The code is a bit messy. I looked at the ff code but it's messy too. It has
+-- to do with the fact that we need to look at points on the curve and control
+-- points in between. This also means that we start at point 2 and have to look
+-- at point 1 when we're at the end. We still use a ps like storage with the
+-- operator last in an entry. It's typical code that evolves stepwise till a
+-- point of no comprehension.
+
+-- For deltas we need a rather complex loop over points that can have holes and
+-- be less than nofpoints and even can have duplicates and also the x and y value
+-- lists can be shorter than etc. I need fonts in order to complete this simply
+-- because I need to visualize in order to understand (what the standard tries
+-- to explain).
+
+-- 0 point then none applied
+-- 1 points then applied to all
+-- otherwise inferred deltas using nearest
+-- if no lower point then use highest referenced point
+-- if no higher point then use lowest referenced point
+-- factor = (target-left)/(right-left)
+-- delta = (1-factor)*left + factor * right
+
local next, type, unpack = next, type, unpack
-local bittest = bit32.btest
-local sqrt = math.sqrt
+local bittest, band, rshift = bit32.btest, bit32.band, bit32.rshift
+local sqrt, round = math.sqrt, math.round
+local char = string.char
+local concat = table.concat
-local report = logs.reporter("otf reader","ttf")
+local report = logs.reporter("otf reader","ttf")
-local readers = fonts.handlers.otf.readers
-local streamreader = readers.streamreader
+local readers = fonts.handlers.otf.readers
+local streamreader = readers.streamreader
-local setposition = streamreader.setposition
-local getposition = streamreader.getposition
-local skipbytes = streamreader.skip
-local readbyte = streamreader.readcardinal1 -- 8-bit unsigned integer
-local readushort = streamreader.readcardinal2 -- 16-bit unsigned integer
-local readulong = streamreader.readcardinal4 -- 24-bit unsigned integer
-local readchar = streamreader.readinteger1 -- 8-bit signed integer
-local readshort = streamreader.readinteger2 -- 16-bit signed integer
-local read2dot14 = streamreader.read2dot14 -- 16-bit signed fixed number with the low 14 bits of fraction (2.14) (F2DOT14)
+local setposition = streamreader.setposition
+local getposition = streamreader.getposition
+local skipbytes = streamreader.skip
+local readbyte = streamreader.readcardinal1 -- 8-bit unsigned integer
+local readushort = streamreader.readcardinal2 -- 16-bit unsigned integer
+local readulong = streamreader.readcardinal4 -- 24-bit unsigned integer
+local readchar = streamreader.readinteger1 -- 8-bit signed integer
+local readshort = streamreader.readinteger2 -- 16-bit signed integer
+local read2dot14 = streamreader.read2dot14 -- 16-bit signed fixed number with the low 14 bits of fraction (2.14) (F2DOT14)
+local readinteger = streamreader.readinteger1
+
+local helpers = readers.helpers
+local gotodatatable = helpers.gotodatatable
local function mergecomposites(glyphs,shapes)
+ -- todo : deltas
+
local function merge(index,shape,components)
local contours = { }
+ local points = { }
local nofcontours = 0
+ local nofpoints = 0
+ local offset = 0
+ local deltas = shape.deltas
for i=1,#components do
local component = components[i]
local subindex = component.index
local subshape = shapes[subindex]
local subcontours = subshape.contours
+ local subpoints = subshape.points
if not subcontours then
local subcomponents = subshape.components
if subcomponents then
- subcontours = merge(subindex,subshape,subcomponents)
+ subcontours, subpoints = merge(subindex,subshape,subcomponents)
end
end
- if subcontours then
+ if subpoints then
local matrix = component.matrix
local xscale = matrix[1]
local xrotate = matrix[2]
@@ -49,36 +88,39 @@ local function mergecomposites(glyphs,shapes)
local yscale = matrix[4]
local xoffset = matrix[5]
local yoffset = matrix[6]
+ for i=1,#subpoints do
+ local p = subpoints[i]
+ local x = p[1]
+ local y = p[2]
+ nofpoints = nofpoints + 1
+ points[nofpoints] = {
+ xscale * x + xrotate * y + xoffset,
+ yscale * y + yrotate * x + yoffset,
+ p[3]
+ }
+ end
for i=1,#subcontours do
- local points = subcontours[i]
- local result = { }
- for i=1,#points do
- local p = points[i]
- local x = p[1]
- local y = p[2]
- result[i] = {
- xscale * x + xrotate * y + xoffset,
- yscale * y + yrotate * x + yoffset,
- p[3]
- }
- end
nofcontours = nofcontours + 1
- contours[nofcontours] = result
+ contours[nofcontours] = offset + subcontours[i]
end
+ offset = offset + #subpoints
else
report("missing contours composite %s, component %s of %s, glyph %s",index,i,#components,subindex)
end
end
+ shape.points = points -- todo : phantom points
shape.contours = contours
shape.components = nil
- return contours
+ return contours, points
end
for index=1,#glyphs do
- local shape = shapes[index]
- local components = shape.components
- if components then
- merge(index,shape,components)
+ local shape = shapes[index]
+ if shape then
+ local components = shape.components
+ if components then
+ merge(index,shape,components)
+ end
end
end
@@ -92,9 +134,6 @@ end
-- begin of converter
--- make paths: the ff code is quite complex but it looks like we need to deal
--- with all kind of on curve border cases
-
local function curveto(m_x,m_y,l_x,l_y,r_x,r_y) -- todo: inline this
return {
l_x + 2/3 *(m_x-l_x), l_y + 2/3 *(m_y-l_y),
@@ -105,132 +144,530 @@ end
-- We could omit the operator which saves some 10%:
--
--- #2=lineto #4=quadratic #6=cubic #3=moveto (with "m")
+-- #2=lineto #4=quadratic #6=cubic #3=moveto (with "m")
--
--- For the moment we keep the original outlines but that default might change
--- in the future. In any case, a backend should support both.
+-- This is tricky ... something to do with phantom points .. however, the hvar
+-- and vvar tables should take care of the width .. the test font doesn't have
+-- those so here we go then (we need a flag for hvar).
--
--- The code is a bit messy. I looked at the ff code but it's messy too. It has
--- to do with the fact that we need to look at points on the curve and control
--- points in between. This also means that we start at point 2 and have to look at
--- point 1 when we're at the end. We still use a ps like storage with the operator
--- last in an entry. It's typical code that evolves stepwise till a point of no
--- comprehension.
+-- h-advance left-side-bearing v-advance top-side-bearing
+--
+-- We had two loops (going backward) but can do it in one loop .. but maybe we
+-- should only accept fonts with proper hvar tables.
-local function contours2outlines(glyphs,shapes)
+local function applyaxis(glyph,shape,points,deltas)
+ if points then
+ local nofpoints = #points
+-- local h = nofpoints + 2 -- weird, the example font seems to have left first
+-- ----- l = nofpoints + 2
+-- ----- v = nofpoints + 3
+-- ----- t = nofpoints + 4
+-- local width = glyph.width
+ for i=1,#deltas do
+ local deltaset = deltas[i]
+ local xvalues = deltaset.xvalues
+ local yvalues = deltaset.yvalues
+ local dpoints = deltaset.points
+ local factor = deltaset.factor
+ if dpoints then
+ -- todo: interpolate
+ local nofdpoints = #dpoints
+ for i=1,nofdpoints do
+ local d = dpoints[i]
+ local p = points[d]
+ if p then
+ if xvalues then
+ local x = xvalues[d]
+ if x and x ~= 0 then
+ p[1] = p[1] + factor * x
+ end
+ end
+ if yvalues then
+ local y = yvalues[d]
+ if y and y ~= 0 then
+ p[2] = p[2] + factor * y
+ end
+ end
+ elseif width then
+-- weird one-off and bad values
+--
+-- if d == h then
+-- print("index",d)
+-- inspect(dpoints)
+-- inspect(xvalues)
+-- local x = xvalues[i]
+-- if x then
+-- print("phantom h advance",width,factor*x)
+-- width = width + factor * x
+-- end
+-- end
+ end
+ end
+ else
+ for i=1,nofpoints do
+ local p = points[i]
+ if xvalues then
+ local x = xvalues[i]
+ if x and x ~= 0 then
+ p[1] = p[1] + factor * x
+ end
+ end
+ if yvalues then
+ local y = yvalues[i]
+ if y and y ~= 0 then
+ p[2] = p[2] + factor * y
+ end
+ end
+ end
+-- todo : phantom point hadvance
+ end
+ end
+-- glyph.width = width
+ end
+end
+
+-- round or not ?
+
+local function contours2outlines_normal(glyphs,shapes) -- maybe accept the bbox overhead
local quadratic = true
-- local quadratic = false
for index=1,#glyphs do
- local glyph = glyphs[index]
- local shape = shapes[index]
- local contours = shape.contours
- if contours then
- local nofcontours = #contours
- local segments = { }
- local nofsegments = 0
- glyph.segments = segments
- if nofcontours > 0 then
- for i=1,nofcontours do
- local contour = contours[i]
- local nofcontour = #contour
- if nofcontour > 0 then
- local first_pt = contour[1]
- local first_on = first_pt[3]
- -- todo no new tables but reuse lineto and quadratic
- if nofcontour == 1 then
- -- this can influence the boundingbox
- first_pt[3] = "m" -- "moveto"
- nofsegments = nofsegments + 1
- segments[nofsegments] = first_pt
- else -- maybe also treat n == 2 special
- local first_on = first_pt[3]
- local last_pt = contour[nofcontour]
- local last_on = last_pt[3]
- local start = 1
- local control_pt = false
- if first_on then
- start = 2
- else
- if last_on then
- first_pt = last_pt
+ local shape = shapes[index]
+ if shape then
+ local glyph = glyphs[index]
+ local contours = shape.contours
+ local points = shape.points
+ if contours then
+ local nofcontours = #contours
+ local segments = { }
+ local nofsegments = 0
+ glyph.segments = segments
+ if nofcontours > 0 then
+ local px, py = 0, 0 -- we could use these in calculations which saves a copy
+ local first = 1
+ for i=1,nofcontours do
+ local last = contours[i]
+ if last >= first then
+ local first_pt = points[first]
+ local first_on = first_pt[3]
+ -- todo no new tables but reuse lineto and quadratic
+ if first == last then
+ first_pt[3] = "m" -- "moveto"
+ nofsegments = nofsegments + 1
+ segments[nofsegments] = first_pt
+ else -- maybe also treat n == 2 special
+ local first_on = first_pt[3]
+ local last_pt = points[last]
+ local last_on = last_pt[3]
+ local start = 1
+ local control_pt = false
+ if first_on then
+ start = 2
else
- first_pt = { (first_pt[1]+last_pt[1])/2, (first_pt[2]+last_pt[2])/2, false }
+ if last_on then
+ first_pt = last_pt
+ else
+ first_pt = { (first_pt[1]+last_pt[1])/2, (first_pt[2]+last_pt[2])/2, false }
+ end
+ control_pt = first_pt
end
- control_pt = first_pt
- end
- nofsegments = nofsegments + 1
- segments[nofsegments] = { first_pt[1], first_pt[2], "m" } -- "moveto"
- local previous_pt = first_pt
- for i=start,nofcontour do
- local current_pt = contour[i]
- local current_on = current_pt[3]
- local previous_on = previous_pt[3]
- if previous_on then
- if current_on then
- -- both normal points
+ local x, y = first_pt[1], first_pt[2]
+ if not done then
+ xmin, ymin, xmax, ymax = x, y, x, y
+ done = true
+ end
+ nofsegments = nofsegments + 1
+ segments[nofsegments] = { x, y, "m" } -- "moveto"
+ if not quadratic then
+ px, py = x, y
+ end
+ local previous_pt = first_pt
+ for i=first,last do
+ local current_pt = points[i]
+ local current_on = current_pt[3]
+ local previous_on = previous_pt[3]
+ if previous_on then
+ if current_on then
+ -- both normal points
+ local x, y = current_pt[1], current_pt[2]
+ nofsegments = nofsegments + 1
+ segments[nofsegments] = { x, y, "l" } -- "lineto"
+ if not quadratic then
+ px, py = x, y
+ end
+ else
+ control_pt = current_pt
+ end
+ elseif current_on then
+ local x1, y1 = control_pt[1], control_pt[2]
+ local x2, y2 = current_pt[1], current_pt[2]
nofsegments = nofsegments + 1
- segments[nofsegments] = { current_pt[1], current_pt[2], "l" } -- "lineto"
+ if quadratic then
+ segments[nofsegments] = { x1, y1, x2, y2, "q" } -- "quadraticto"
+ else
+ x1, x2, x2, y2, px, py = curveto(x1, x2, px, py, x2, y2)
+ segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" } -- "curveto"
+ end
+ control_pt = false
else
+ local x2, y2 = (previous_pt[1]+current_pt[1])/2, (previous_pt[2]+current_pt[2])/2
+ local x1, y1 = control_pt[1], control_pt[2]
+ nofsegments = nofsegments + 1
+ if quadratic then
+ segments[nofsegments] = { x1, y1, x2, y2, "q" } -- "quadraticto"
+ else
+ x1, x2, x2, y2, px, py = curveto(x1, x2, px, py, x2, y2)
+ segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" } -- "curveto"
+ end
control_pt = current_pt
end
- elseif current_on then
- local ps = segments[nofsegments]
+ previous_pt = current_pt
+ end
+ if first_pt == last_pt then
+ -- we're already done, probably a simple curve
+ else
nofsegments = nofsegments + 1
- if quadratic then
- segments[nofsegments] = { control_pt[1], control_pt[2], current_pt[1], current_pt[2], "q" } -- "quadraticto"
+ if not control_pt then
+ segments[nofsegments] = { first_pt[1], first_pt[2], "l" } -- "lineto"
+ elseif quadratic then
+ local x1, y1 = control_pt[1], control_pt[2]
+ -- local x2, y2 = first_pt[1], first_pt[2]
+ segments[nofsegments] = { x1, y1, first_pt[1], first_pt[2], "q" } -- "quadraticto"
+ else
+ local x1, y1 = control_pt[1], control_pt[2]
+ local x2, y2 = first_pt[1], first_pt[2]
+ x1, x2, x2, y2, px, py = curveto(x1, x2, px, py, x2, y2)
+ segments[nofsegments] = { x1, y1, y2, y2, px, py, "c" } -- "curveto"
+ -- px, py = x2, y2
+ end
+ end
+ end
+ end
+ first = last + 1
+ end
+ end
+ end
+ end
+ end
+end
+
+local function contours2outlines_shaped(glyphs,shapes,keepcurve)
+ local quadratic = true
+ -- local quadratic = false
+ for index=1,#glyphs do
+ local shape = shapes[index]
+ if shape then
+ local glyph = glyphs[index]
+ local contours = shape.contours
+ local points = shape.points
+ if contours then
+ local nofcontours = #contours
+ local segments = keepcurve and { } or nil
+ local nofsegments = 0
+ if keepcurve then
+ glyph.segments = segments
+ end
+ if nofcontours > 0 then
+ local xmin, ymin, xmax, ymax, done = 0, 0, 0, 0, false
+ local px, py = 0, 0 -- we could use these in calculations which saves a copy
+ local first = 1
+ for i=1,nofcontours do
+ local last = contours[i]
+ if last >= first then
+ local first_pt = points[first]
+ local first_on = first_pt[3]
+ -- todo no new tables but reuse lineto and quadratic
+ if first == last then
+ -- this can influence the boundingbox
+ if keepcurve then
+ first_pt[3] = "m" -- "moveto"
+ nofsegments = nofsegments + 1
+ segments[nofsegments] = first_pt
+ end
+ else -- maybe also treat n == 2 special
+ local first_on = first_pt[3]
+ local last_pt = points[last]
+ local last_on = last_pt[3]
+ local start = 1
+ local control_pt = false
+ if first_on then
+ start = 2
+ else
+ if last_on then
+ first_pt = last_pt
else
- local p = segments[nofsegments-1] local n = #p
- segments[nofsegments] = curveto(control_pt[1],control_pt[2],p[n-2],p[n-1],current_pt[1],current_pt[2])
+ first_pt = { (first_pt[1]+last_pt[1])/2, (first_pt[2]+last_pt[2])/2, false }
end
- control_pt = false
+ control_pt = first_pt
+ end
+ local x, y = first_pt[1], first_pt[2]
+ if not done then
+ xmin, ymin, xmax, ymax = x, y, x, y
+ done = true
else
+ if x < xmin then xmin = x elseif x > xmax then xmax = x end
+ if y < ymin then ymin = y elseif y > ymax then ymax = y end
+ end
+ if keepcurve then
nofsegments = nofsegments + 1
- local halfway_x = (previous_pt[1]+current_pt[1])/2
- local halfway_y = (previous_pt[2]+current_pt[2])/2
- if quadratic then
- segments[nofsegments] = { control_pt[1], control_pt[2], halfway_x, halfway_y, "q" } -- "quadraticto"
+ segments[nofsegments] = { x, y, "m" } -- "moveto"
+ end
+ if not quadratic then
+ px, py = x, y
+ end
+ local previous_pt = first_pt
+ for i=first,last do
+ local current_pt = points[i]
+ local current_on = current_pt[3]
+ local previous_on = previous_pt[3]
+ if previous_on then
+ if current_on then
+ -- both normal points
+ local x, y = current_pt[1], current_pt[2]
+ if x < xmin then xmin = x elseif x > xmax then xmax = x end
+ if y < ymin then ymin = y elseif y > ymax then ymax = y end
+ if keepcurve then
+ nofsegments = nofsegments + 1
+ segments[nofsegments] = { x, y, "l" } -- "lineto"
+ end
+ if not quadratic then
+ px, py = x, y
+ end
+ else
+ control_pt = current_pt
+ end
+ elseif current_on then
+ local x1, y1 = control_pt[1], control_pt[2]
+ local x2, y2 = current_pt[1], current_pt[2]
+ if quadratic then
+ if x1 < xmin then xmin = x1 elseif x1 > xmax then xmax = x1 end
+ if y1 < ymin then ymin = y1 elseif y1 > ymax then ymax = y1 end
+ if keepcurve then
+ nofsegments = nofsegments + 1
+ segments[nofsegments] = { x1, y1, x2, y2, "q" } -- "quadraticto"
+ end
+ else
+ x1, x2, x2, y2, px, py = curveto(x1, x2, px, py, x2, y2)
+ if x1 < xmin then xmin = x1 elseif x1 > xmax then xmax = x1 end
+ if y1 < ymin then ymin = y1 elseif y1 > ymax then ymax = y1 end
+ if x2 < xmin then xmin = x2 elseif x2 > xmax then xmax = x2 end
+ if y2 < ymin then ymin = y2 elseif y2 > ymax then ymax = y2 end
+ if px < xmin then xmin = px elseif px > xmax then xmax = px end
+ if py < ymin then ymin = py elseif py > ymax then ymax = py end
+ if keepcurve then
+ nofsegments = nofsegments + 1
+ segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" } -- "curveto"
+ end
+ end
+ control_pt = false
else
- local p = segments[nofsegments-1] local n = #p
- segments[nofsegments] = curveto(control_pt[1],control_pt[2],p[n-2],p[n-1],halfway_x,halfway_y)
+ local x2, y2 = (previous_pt[1]+current_pt[1])/2, (previous_pt[2]+current_pt[2])/2
+ local x1, y1 = control_pt[1], control_pt[2]
+ if quadratic then
+ if x1 < xmin then xmin = x1 elseif x1 > xmax then xmax = x1 end
+ if y1 < ymin then ymin = y1 elseif y1 > ymax then ymax = y1 end
+ if keepcurve then
+ nofsegments = nofsegments + 1
+ segments[nofsegments] = { x1, y1, x2, y2, "q" } -- "quadraticto"
+ end
+ else
+ x1, x2, x2, y2, px, py = curveto(x1, x2, px, py, x2, y2)
+ if x1 < xmin then xmin = x1 elseif x1 > xmax then xmax = x1 end
+ if y1 < ymin then ymin = y1 elseif y1 > ymax then ymax = y1 end
+ if x2 < xmin then xmin = x2 elseif x2 > xmax then xmax = x2 end
+ if y2 < ymin then ymin = y2 elseif y2 > ymax then ymax = y2 end
+ if px < xmin then xmin = px elseif px > xmax then xmax = px end
+ if py < ymin then ymin = py elseif py > ymax then ymax = py end
+ if keepcurve then
+ nofsegments = nofsegments + 1
+ segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" } -- "curveto"
+ end
+ end
+ control_pt = current_pt
end
- control_pt = current_pt
+ previous_pt = current_pt
end
- previous_pt = current_pt
- end
- if first_pt == last_pt then
- -- we're already done, probably a simple curve
- else
- nofsegments = nofsegments + 1
- if not control_pt then
- segments[nofsegments] = { first_pt[1], first_pt[2], "l" } -- "lineto"
+ if first_pt == last_pt then
+ -- we're already done, probably a simple curve
+ elseif not control_pt then
+ if keepcurve then
+ nofsegments = nofsegments + 1
+ segments[nofsegments] = { first_pt[1], first_pt[2], "l" } -- "lineto"
+ end
elseif quadratic then
- segments[nofsegments] = { control_pt[1], control_pt[2], first_pt[1], first_pt[2], "q" } -- "quadraticto"
+ local x1, y1 = control_pt[1], control_pt[2]
+ -- local x2, y2 = first_pt[1], first_pt[2]
+ if x1 < xmin then xmin = x1 elseif x1 > xmax then xmax = x1 end
+ if y1 < ymin then ymin = y1 elseif y1 > ymax then ymax = y1 end
+ if keepcurve then
+ nofsegments = nofsegments + 1
+ segments[nofsegments] = { x1, y1, first_pt[1], first_pt[2], "q" } -- "quadraticto"
+ end
else
- local p = last_pt local n = #p
- segments[nofsegments] = curveto(control_pt[1],control_pt[2],p[n-2],p[n-1],first_pt[1],first_pt[2])
+ local x1, y1 = control_pt[1], control_pt[2]
+ local x2, y2 = first_pt[1], first_pt[2]
+ x1, x2, x2, y2, px, py = curveto(x1, x2, px, py, x2, y2)
+ if x1 < xmin then xmin = x1 elseif x1 > xmax then xmax = x1 end
+ if y1 < ymin then ymin = y1 elseif y1 > ymax then ymax = y1 end
+ if x2 < xmin then xmin = x2 elseif x2 > xmax then xmax = x2 end
+ if y2 < ymin then ymin = y2 elseif y2 > ymax then ymax = y2 end
+ if px < xmin then xmin = px elseif px > xmax then xmax = px end
+ if py < ymin then ymin = py elseif py > ymax then ymax = py end
+ if keepcurve then
+ nofsegments = nofsegments + 1
+ segments[nofsegments] = { x1, y1, y2, y2, px, py, "c" } -- "curveto"
+ end
+ -- px, py = x2, y2
end
end
end
+ first = last + 1
+ end
+ glyph.boundingbox = { round(xmin), round(ymin), round(xmax), round(ymax) }
+ end
+ end
+ end
+ end
+end
+
+-- optimize for zero
+
+local c_zero = char(0)
+local s_zero = char(0,0)
+
+local function toushort(n)
+ return char(band(rshift(n,8),0xFF),band(n,0xFF))
+end
+
+local function toshort(n)
+ if n < 0 then
+ n = n + 0x10000
+ end
+ return char(band(rshift(n,8),0xFF),band(n,0xFF))
+end
+
+-- todo: we can reuse result, xpoints and ypoints
+
+local function repackpoints(glyphs,shapes)
+ local noboundingbox = { 0, 0, 0, 0 }
+ local result = { } -- reused
+ for index=1,#glyphs do
+ local shape = shapes[index]
+ if shape then
+ local r = 0
+ local glyph = glyphs[index]
+ if false then -- shape.type == "composite"
+ -- we merged them
+ else
+ local contours = shape.contours
+ local nofcontours = #contours
+ local boundingbox = glyph.boundingbox or noboundingbox
+ r = r + 1 result[r] = toshort(nofcontours)
+ r = r + 1 result[r] = toshort(boundingbox[1]) -- xmin
+ r = r + 1 result[r] = toshort(boundingbox[2]) -- ymin
+ r = r + 1 result[r] = toshort(boundingbox[3]) -- xmax
+ r = r + 1 result[r] = toshort(boundingbox[4]) -- ymax
+ if nofcontours > 0 then
+ for i=1,nofcontours do
+ r = r + 1 result[r] = toshort(contours[i]-1)
+ end
+ r = r + 1 result[r] = s_zero -- no instructions
+ local points = shape.points
+ local currentx = 0
+ local currenty = 0
+ local xpoints = { }
+ local ypoints = { }
+ local x = 0
+ local y = 0
+ local lastflag = nil
+ local nofflags = 0
+ for i=1,#points do
+ local pt = points[i]
+ local px = pt[1]
+ local py = pt[2]
+ local fl = pt[3] and 0x01 or 0x00
+ if px == currentx then
+ fl = fl + 0x10
+ else
+ local dx = round(px - currentx)
+ if dx < -255 or dx > 255 then
+ x = x + 1 xpoints[x] = toshort(dx)
+ elseif dx < 0 then
+ fl = fl + 0x02
+ x = x + 1 xpoints[x] = char(-dx)
+ elseif dx > 0 then
+ fl = fl + 0x12
+ x = x + 1 xpoints[x] = char(dx)
+ else
+ fl = fl + 0x02
+ x = x + 1 xpoints[x] = c_zero
+ end
+ end
+ if py == currenty then
+ fl = fl + 0x20
+ else
+ local dy = round(py - currenty)
+ if dy < -255 or dy > 255 then
+ y = y + 1 ypoints[y] = toshort(dy)
+ elseif dy < 0 then
+ fl = fl + 0x04
+ y = y + 1 ypoints[y] = char(-dy)
+ elseif dy > 0 then
+ fl = fl + 0x24
+ y = y + 1 ypoints[y] = char(dy)
+ else
+ fl = fl + 0x04
+ y = y + 1 ypoints[y] = c_zero
+ end
+ end
+ currentx = px
+ currenty = py
+ if lastflag == fl then
+ nofflags = nofflags + 1
+ else -- if > 255
+ if nofflags == 1 then
+ r = r + 1 result[r] = char(lastflag)
+ elseif nofflags == 2 then
+ r = r + 1 result[r] = char(lastflag,lastflag)
+ elseif nofflags > 2 then
+ lastflag = lastflag + 0x08
+ r = r + 1 result[r] = char(lastflag,nofflags-1)
+ end
+ nofflags = 1
+ lastflag = fl
+ end
+ end
+ if nofflags == 1 then
+ r = r + 1 result[r] = char(lastflag)
+ elseif nofflags == 2 then
+ r = r + 1 result[r] = char(lastflag,lastflag)
+ elseif nofflags > 2 then
+ lastflag = lastflag + 0x08
+ r = r + 1 result[r] = char(lastflag,nofflags-1)
end
+ r = r + 1 result[r] = concat(xpoints)
+ r = r + 1 result[r] = concat(ypoints)
end
end
+ glyph.stream = concat(result,"",1,r)
+ else
+ -- fatal
end
end
end
-- end of converter
-local function readglyph(f,nofcontours)
+local function readglyph(f,nofcontours) -- read deltas here, saves space
local points = { }
- local endpoints = { }
+ local contours = { }
local instructions = { }
local flags = { }
for i=1,nofcontours do
- endpoints[i] = readshort(f) + 1
+ contours[i] = readshort(f) + 1
end
- local nofpoints = endpoints[nofcontours]
+ local nofpoints = contours[nofcontours]
local nofinstructions = readushort(f)
--- f:seek("set",f:seek()+nofinstructions)
skipbytes(f,nofinstructions)
-- because flags can repeat we don't know the amount ... in fact this is
-- not that efficient (small files but more mem)
@@ -238,7 +675,7 @@ local function readglyph(f,nofcontours)
while i <= nofpoints do
local flag = readbyte(f)
flags[i] = flag
- if bittest(flag,0x0008) then
+ if bittest(flag,0x08) then
for j=1,readbyte(f) do
i = i + 1
flags[i] = flag
@@ -251,8 +688,8 @@ local function readglyph(f,nofcontours)
local x = 0
for i=1,nofpoints do
local flag = flags[i]
- local short = bittest(flag,0x0002)
- local same = bittest(flag,0x0010)
+ local short = bittest(flag,0x02)
+ local same = bittest(flag,0x10)
if short then
if same then
x = x + readbyte(f)
@@ -264,13 +701,13 @@ local function readglyph(f,nofcontours)
else
x = x + readshort(f)
end
- points[i] = { x, y, bittest(flag,0x0001) }
+ points[i] = { x, y, bittest(flag,0x01) }
end
local y = 0
for i=1,nofpoints do
local flag = flags[i]
- local short = bittest(flag,0x0004)
- local same = bittest(flag,0x0020)
+ local short = bittest(flag,0x04)
+ local same = bittest(flag,0x20)
if short then
if same then
y = y + readbyte(f)
@@ -284,17 +721,11 @@ local function readglyph(f,nofcontours)
end
points[i][2] = y
end
- -- we could integrate this if needed
- local first = 1
- for i=1,#endpoints do
- local last = endpoints[i]
- endpoints[i] = { unpack(points,first,last) }
- first = last + 1
- end
return {
- type = "glyph",
- -- points = points,
- contours = endpoints,
+ type = "glyph",
+ points = points,
+ contours = contours,
+ nofpoints = nofpoints,
}
end
@@ -384,8 +815,8 @@ local function readcomposite(f)
end
end
return {
- type = "composite",
- components = components,
+ type = "composite",
+ components = components,
}
end
@@ -407,15 +838,13 @@ function readers.loca(f,fontdata,specification)
local locations = { }
setposition(f,datatable.offset)
if format == 1 then
- local nofglyphs = datatable.length/4 - 1
- -1
+ local nofglyphs = datatable.length/4 - 2
for i=0,nofglyphs do
locations[i] = offset + readulong(f)
end
fontdata.nofglyphs = nofglyphs
else
- local nofglyphs = datatable.length/2 - 1
- -1
+ local nofglyphs = datatable.length/2 - 2
for i=0,nofglyphs do
locations[i] = offset + readushort(f) * 2
end
@@ -427,52 +856,309 @@ function readers.loca(f,fontdata,specification)
end
function readers.glyf(f,fontdata,specification) -- part goes to cff module
- if specification.glyphs then
- local datatable = fontdata.tables.glyf
- if datatable then
- local locations = fontdata.locations
- if locations then
- local glyphs = fontdata.glyphs
- local nofglyphs = fontdata.nofglyphs
- local filesize = fontdata.filesize
- local nothing = { 0, 0, 0, 0 }
- local shapes = { }
- local loadshapes = specification.shapes
- for index=0,nofglyphs do
- local location = locations[index]
- if location >= filesize then
- report("discarding %s glyphs due to glyph location bug",nofglyphs-index+1)
- fontdata.nofglyphs = index - 1
- fontdata.badfont = true
- break
- elseif location > 0 then
- setposition(f,location)
- local nofcontours = readshort(f)
- glyphs[index].boundingbox = {
- readshort(f), -- xmin
- readshort(f), -- ymin
- readshort(f), -- xmax
- readshort(f), -- ymax
- }
- if not loadshapes then
- -- save space
- elseif nofcontours == 0 then
- shapes[index] = readnothing(f,nofcontours)
- elseif nofcontours > 0 then
- shapes[index] = readglyph(f,nofcontours)
+ local tableoffset = gotodatatable(f,fontdata,"glyf",specification.glyphs)
+ if tableoffset then
+ local locations = fontdata.locations
+ if locations then
+ local glyphs = fontdata.glyphs
+ local nofglyphs = fontdata.nofglyphs
+ local filesize = fontdata.filesize
+ local nothing = { 0, 0, 0, 0 }
+ local shapes = { }
+ local loadshapes = specification.shapes or specification.instance
+ for index=0,nofglyphs do
+ local location = locations[index]
+ if location >= filesize then
+ report("discarding %s glyphs due to glyph location bug",nofglyphs-index+1)
+ fontdata.nofglyphs = index - 1
+ fontdata.badfont = true
+ break
+ elseif location > 0 then
+ setposition(f,location)
+ local nofcontours = readshort(f)
+ glyphs[index].boundingbox = {
+ readshort(f), -- xmin
+ readshort(f), -- ymin
+ readshort(f), -- xmax
+ readshort(f), -- ymax
+ }
+ if not loadshapes then
+ -- save space
+ elseif nofcontours == 0 then
+ shapes[index] = readnothing(f,nofcontours)
+ elseif nofcontours > 0 then
+ shapes[index] = readglyph(f,nofcontours)
+ else
+ shapes[index] = readcomposite(f,nofcontours)
+ end
+ else
+ if loadshapes then
+ shapes[index] = { }
+ end
+ glyphs[index].boundingbox = nothing
+ end
+ end
+ if loadshapes then
+ if readers.gvar then
+ readers.gvar(f,fontdata,specification,glyphs,shapes)
+ end
+ mergecomposites(glyphs,shapes)
+ if specification.instance then
+ if specification.streams then
+ repackpoints(glyphs,shapes)
+ else
+ contours2outlines_shaped(glyphs,shapes,specification.shapes)
+ end
+ elseif specification.loadshapes then
+ contours2outlines_normal(glyphs,shapes)
+ end
+ end
+ end
+ end
+end
+
+-- gvar is a bit crazy format and one can really wonder if the bit-jugling obscurity
+-- is still needed in these days .. cff is much nicer with these blends while the ttf
+-- coding variant looks quite horrible
+
+local function readtuplerecord(f,nofaxis)
+ local record = { }
+ for i=1,nofaxis do
+ record[i] = read2dot14(f)
+ end
+ return record
+end
+
+local function readpoints(f)
+ local count = readbyte(f)
+ if count == 0 then
+ -- second byte not used, deltas for all point numbers
+ return nil, 0 -- todo
+ else
+ if count < 128 then
+ -- no second byte, use count
+ else
+ count = band(count,0x80) * 256 + readbyte(f)
+ end
+ local points = { }
+ local p = 0
+ local n = 1
+ while p < count do
+ local control = readbyte(f)
+ local runreader = bittest(control,0x80) and readushort or readbyte
+ local runlength = band(control,0x7F)
+ for i=1,runlength+1 do
+ n = n + runreader(f)
+ p = p + 1
+ points[p] = n
+ end
+ end
+ return points, p
+ end
+end
+
+local function readdeltas(f,nofpoints)
+ local deltas = { }
+ local p = 0
+ local n = 0
+ local z = false
+ while nofpoints > 0 do
+ local control = readbyte(f)
+ local allzero = bittest(control,0x80)
+ local runreader = bittest(control,0x40) and readshort or readinteger
+ local runlength = band(control,0x3F) + 1
+ if allzero then
+ z = runlength
+ else
+ if z then
+ for i=1,z do
+ p = p + 1
+ deltas[p] = 0
+ end
+ z = false
+ end
+ for i=1,runlength do
+ p = p + 1
+ deltas[p] = runreader(f)
+ end
+ end
+ nofpoints = nofpoints - runlength
+ end
+ -- saves space
+ -- if z then
+ -- for i=1,z do
+ -- p = p + 1
+ -- deltas[p] = 0
+ -- end
+ -- end
+ if p > 0 then
+ -- forget about trailing zeros
+ return deltas
+ else
+ -- forget about all zeros
+ end
+end
+
+function readers.gvar(f,fontdata,specification,glyphdata,shapedata)
+ local instance = specification.instance
+ if not instance then
+ return
+ end
+ local factors = specification.factors
+ if not factors then
+ return
+ end
+ local tableoffset = gotodatatable(f,fontdata,"gvar",specification.variable or specification.shapes)
+ if tableoffset then
+ local version = readulong(f) -- 1.0
+ local nofaxis = readushort(f)
+ local noftuples = readushort(f)
+ local tupleoffset = readulong(f) -- shared
+ local nofglyphs = readushort(f)
+ local flags = readushort(f)
+ local dataoffset = tableoffset + readulong(f)
+ local data = { }
+ local tuples = { }
+ local glyphdata = fontdata.glyphs
+ -- there is one more offset (so that one can calculate the size i suppose)
+ -- so we could test for overflows but we simply assume sane font files
+ if bittest(flags,0x0001) then
+ for i=1,nofglyphs do
+ data[i] = readulong(f)
+ end
+ else
+ for i=1,nofglyphs do
+ data[i] = 2*readushort(f)
+ end
+ end
+ --
+ setposition(f,tableoffset+tupleoffset)
+ for i=1,noftuples do
+ tuples[i] = readtuplerecord(f,nofaxis) -- used ?
+ end
+ local lastoffset = false
+ for i=1,nofglyphs do -- hm one more cf spec
+ local shape = shapedata[i-1] -- todo 0
+ if shape then
+ local startoffset = dataoffset + data[i]
+ if startoffset == lastoffset then
+ -- in the font that i used for testing there were the same offsets so
+ -- we can assume that this indicates a zero entry
+ else
+ -- todo: args_are_xy_values mess .. i have to be really bored
+ -- and motivated to deal with it
+
+ lastoffset = startoffset
+ setposition(f,startoffset)
+ local flags = readushort(f)
+ local count = band(flags,0x0FFF)
+ local points = bittest(flags,0x8000)
+ local offset = startoffset + readushort(f) -- to serialized
+ local deltas = { }
+ local nofpoints = 0
+ local allpoints = (shape.nofpoints or 0) + 1
+ if points then
+ -- go to the packed stream (get them once)
+ local current = getposition(f)
+ setposition(f,offset)
+ points, nofpoints = readpoints(f)
+ offset = getposition(f)
+ setposition(f,current)
+ -- and back to the table
+ else
+ points, nofpoints = nil, 0
+ end
+ for i=1,count do
+ local currentstart = getposition(f)
+ local size = readushort(f) -- check
+ local flags = readushort(f)
+ local index = band(flags,0x0FFF)
+ local haspeak = bittest(flags,0x8000)
+ local intermediate = bittest(flags,0x4000)
+ local private = bittest(flags,0x1000)
+ local peak = nil
+ local start = nil
+ local stop = nil
+ local xvalues = nil
+ local yvalues = nil
+ local points = points -- we default to shared
+ local nofpoints = nofpoints -- we default to shared
+ local advance = 4
+ if peak then
+ peak = readtuplerecord(f,nofaxis)
+ advance = advance + 2*nofaxis
else
- shapes[index] = readcomposite(f,nofcontours)
+ if index+1 > #tuples then
+ print("error, bad index",index)
+ end
+ peak = tuples[index+1] -- hm, needs checking, only peak?
end
- else
- if loadshapes then
- shapes[index] = { }
+-- what start otherwise ?
+ if intermediate then
+ start = readtuplerecord(f,nofaxis)
+ stop = readtuplerecord(f,nofaxis)
+ advance = advance + 4*nofaxis
+ end
+ -- get the deltas
+ if size > 0 then
+ -- goto the packed stream
+ setposition(f,offset)
+ if private then
+ points, nofpoints = readpoints(f)
+ elseif nofpoints == 0 then
+ nofpoints = allpoints
+ end
+ if nofpoints > 0 then
+ xvalues = readdeltas(f,nofpoints)
+ yvalues = readdeltas(f,nofpoints)
+ end
+ offset = getposition(f)
+ -- back to the table
+ setposition(f,currentstart+advance)
+ end
+ if not xvalues and not yvalues then
+ points = nil
+ end
+ local s = 1
+ for i=1,nofaxis do
+ local f = factors[i]
+ local start = start and start[i] or 0
+ local peak = peak and peak [i] or 0
+ local stop = stop and stop [i] or 0
+ -- do we really need these tests ... can't we assume sane values
+ 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
+ if s ~= 0 then
+ deltas[#deltas+1] = {
+ factor = s,
+ points = points,
+ xvalues = xvalues,
+ yvalues = yvalues,
+ }
end
- glyphs[index].boundingbox = nothing
end
- end
- if loadshapes then
- mergecomposites(glyphs,shapes)
- contours2outlines(glyphs,shapes)
+ if shape.type == "glyph" then
+ applyaxis(glyphdata[i],shape,shape.points,deltas)
+ else
+ shape.deltas = deltas
+ end
end
end
end
diff --git a/tex/context/base/mkiv/l-lpeg.lua b/tex/context/base/mkiv/l-lpeg.lua
index 877dae644..c34ba6ad4 100644
--- a/tex/context/base/mkiv/l-lpeg.lua
+++ b/tex/context/base/mkiv/l-lpeg.lua
@@ -187,18 +187,20 @@ local fullstripper = whitespace^0 * C((whitespace^0 * nonwhitespace^1)^0)
----- collapser = Cs(spacer^0/"" * ((spacer^1 * endofstring / "") + (spacer^1/" ") + P(1))^0)
local collapser = Cs(spacer^0/"" * nonspacer^0 * ((spacer^0/" " * nonspacer^1)^0))
+local nospacer = Cs((whitespace^1/"" + nonwhitespace^1)^0)
local b_collapser = Cs( whitespace^0 /"" * (nonwhitespace^1 + whitespace^1/" ")^0)
local e_collapser = Cs((whitespace^1 * P(-1)/"" + nonwhitespace^1 + whitespace^1/" ")^0)
local m_collapser = Cs( (nonwhitespace^1 + whitespace^1/" ")^0)
-local b_stripper = Cs( spacer^0 /"" * (nonspacer^1 + spacer^1/" ")^0)
-local e_stripper = Cs((spacer^1 * P(-1)/"" + nonspacer^1 + spacer^1/" ")^0)
-local m_stripper = Cs( (nonspacer^1 + spacer^1/" ")^0)
+local b_stripper = Cs( spacer^0 /"" * (nonspacer^1 + spacer^1/" ")^0)
+local e_stripper = Cs((spacer^1 * P(-1)/"" + nonspacer^1 + spacer^1/" ")^0)
+local m_stripper = Cs( (nonspacer^1 + spacer^1/" ")^0)
patterns.stripper = stripper
patterns.fullstripper = fullstripper
patterns.collapser = collapser
+patterns.nospacer = nospacer
patterns.b_collapser = b_collapser
patterns.m_collapser = m_collapser
diff --git a/tex/context/base/mkiv/l-md5.lua b/tex/context/base/mkiv/l-md5.lua
index 00272c873..6758fa444 100644
--- a/tex/context/base/mkiv/l-md5.lua
+++ b/tex/context/base/mkiv/l-md5.lua
@@ -48,6 +48,9 @@ do
if not md5.hex then function md5.hex(str) if str then return lpegmatch(bytestohex,md5sum(str)) end end end
if not md5.dec then function md5.dec(str) if str then return lpegmatch(bytestodec,md5sum(str)) end end end
+ md5.sumhexa = md5.hex
+ md5.sumHEXA = md5.HEX
+
end
end
diff --git a/tex/context/base/mkiv/l-string.lua b/tex/context/base/mkiv/l-string.lua
index 2e8c61196..e0fb28445 100644
--- a/tex/context/base/mkiv/l-string.lua
+++ b/tex/context/base/mkiv/l-string.lua
@@ -72,6 +72,7 @@ end
local stripper = patterns.stripper
local fullstripper = patterns.fullstripper
local collapser = patterns.collapser
+local nospacer = patterns.nospacer
local longtostring = patterns.longtostring
function string.strip(str)
@@ -86,6 +87,10 @@ function string.collapsespaces(str)
return str and lpegmatch(collapser,str) or ""
end
+function string.nospaces(str)
+ return str and lpegmatch(nospacer,str) or ""
+end
+
function string.longtostring(str)
return str and lpegmatch(longtostring,str) or ""
end
diff --git a/tex/context/base/mkiv/lpdf-ini.lua b/tex/context/base/mkiv/lpdf-ini.lua
index b4c8be7b1..1b24269a6 100644
--- a/tex/context/base/mkiv/lpdf-ini.lua
+++ b/tex/context/base/mkiv/lpdf-ini.lua
@@ -1188,15 +1188,17 @@ end
do
- local f_actual_text_one = formatters["BT /Span << /ActualText <feff%04x> >> BDC %s EMC ET"]
- local f_actual_text_two = formatters["BT /Span << /ActualText <feff%04x%04x> >> BDC %s EMC ET"]
- local f_actual_text_one_b = formatters["BT /Span << /ActualText <feff%04x> >> BDC"]
- local f_actual_text_two_b = formatters["BT /Span << /ActualText <feff%04x%04x> >> BDC"]
- local f_actual_text_b = formatters["BT /Span << /ActualText <feff%s> >> BDC"]
- local s_actual_text_e = "EMC ET"
- local f_actual_text_b_not = formatters["/Span << /ActualText <feff%s> >> BDC"]
- local s_actual_text_e_not = "EMC"
- local f_actual_text = formatters["/Span <</ActualText %s >> BDC"]
+ local f_actual_text_one = formatters["BT /Span << /ActualText <feff%04x> >> BDC %s EMC ET"]
+ local f_actual_text_two = formatters["BT /Span << /ActualText <feff%04x%04x> >> BDC %s EMC ET"]
+ local f_actual_text_one_b = formatters["BT /Span << /ActualText <feff%04x> >> BDC"]
+ local f_actual_text_two_b = formatters["BT /Span << /ActualText <feff%04x%04x> >> BDC"]
+ local f_actual_text_b = formatters["BT /Span << /ActualText <feff%s> >> BDC"]
+ local s_actual_text_e = "EMC ET"
+ local f_actual_text_b_not = formatters["/Span << /ActualText <feff%s> >> BDC"]
+ local f_actual_text_one_b_not = formatters["/Span << /ActualText <feff%04x> >> BDC"]
+ local f_actual_text_two_b_not = formatters["/Span << /ActualText <feff%04x%04x> >> BDC"]
+ local s_actual_text_e_not = "EMC"
+ local f_actual_text = formatters["/Span <</ActualText %s >> BDC"]
local context = context
local pdfdirect = nodes.pool.pdfdirect
@@ -1226,7 +1228,13 @@ do
end
function codeinjections.startunicodetoactualtextdirect(unicode)
- return f_actual_text_b_not(unicode)
+ if type(unicode) == "string" then
+ return f_actual_text_b_not(unicode)
+ elseif unicode < 0x10000 then
+ return f_actual_text_one_b_not(unicode)
+ else
+ return f_actual_text_two_b_not(unicode/1024+0xD800,unicode%1024+0xDC00)
+ end
end
function codeinjections.stopunicodetoactualtextdirect()
diff --git a/tex/context/base/mkiv/luat-cbk.lua b/tex/context/base/mkiv/luat-cbk.lua
index 7b28b3be4..6fcfdc7f2 100644
--- a/tex/context/base/mkiv/luat-cbk.lua
+++ b/tex/context/base/mkiv/luat-cbk.lua
@@ -81,11 +81,15 @@ if not list then -- otherwise counters get reset
list = utilities.storage.allocate(list_callbacks())
+ local supported = { }
+
for k in next, list do
- list[k] = 0
+ list[k] = 0
+ supported[k] = true
end
- callbacks.list = list
+ callbacks.list = list
+ callbacks.supported = supported
end
diff --git a/tex/context/base/mkiv/lxml-tab.lua b/tex/context/base/mkiv/lxml-tab.lua
index f2d38a654..0c216bd3d 100644
--- a/tex/context/base/mkiv/lxml-tab.lua
+++ b/tex/context/base/mkiv/lxml-tab.lua
@@ -264,7 +264,7 @@ local function add_empty(spacing, namespace, tag)
tg = tag,
at = at,
dt = { },
- ni = nil, -- preset slot
+ ni = nt, -- set slot, needed for css filtering
__p__ = top
}
dt[nt] = t
@@ -287,7 +287,7 @@ local function add_begin(spacing, namespace, tag)
tg = tag,
at = at,
dt = { },
- ni = nil, -- preset slot
+ ni = nil, -- preset slot, needed for css filtering
__p__ = stack[level]
}
setmetatable(top, mt)
@@ -316,6 +316,7 @@ local function add_end(spacing, namespace, tag)
dt = top.dt
nt = #dt + 1
dt[nt] = toclose
+ toclose.ni = nt -- update slot, needed for css filtering
if toclose.at.xmlns then
remove(xmlns)
end
@@ -325,7 +326,7 @@ end
--
-- will be an option: dataonly
--
--- if #text == 0 or lpegmatch(spaceonly,text) then
+-- if #text == 0 or lpegmatch(spaceonly,text) then
-- return
-- end
diff --git a/tex/context/base/mkiv/m-fonts-plugins.mkiv b/tex/context/base/mkiv/m-fonts-plugins.mkiv
new file mode 100644
index 000000000..ecb311694
--- /dev/null
+++ b/tex/context/base/mkiv/m-fonts-plugins.mkiv
@@ -0,0 +1,406 @@
+%D \module
+%D [ file=m-fonts-plugins,
+%D version=2016.10.10,
+%D title=\CONTEXT\ Fonts,
+%D subtitle=Font Engine Plugins,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D See source code for comments. I wrote this a follow up on a presentation by
+%D Kai Eigner, left it for a while, and sort of finalized it the last quarter of
+%D 2016. As I don't use this module, apart from maybe testing something, it is
+%D not guaranteed to work. Also, plugins can interfere with other functionality
+%D in \CONTEXT\ so don't expect too much support. The two modules mentioned
+%D below should work in the generic loader too. It's anyhow an illustration of
+%D how \type {ffi} can work be used in a practical application.
+
+\registerctxluafile{font-txt}{1.001} % generic text handler
+\registerctxluafile{font-phb}{1.001} % harfbuzz plugin
+
+\startluacode
+
+ local function processlist(data)
+ local list = data.list
+ local timings = data.results
+ for i=1,#list do
+ local name = list[i]
+ local data = timings[name]
+ local none = data["context none"] or 0
+ local node = data["context node"] or 0
+ if node > 0.1 then
+ context.starttabulate { "|l|c|c|c|c|c|" }
+ context.NC() context.bold(name)
+ context.NC() context([[$t$]])
+ context.NC() context([[$t - t_{\hbox{\tx none}}$]])
+ context.NC() context([[$t - t_{\hbox{\tx node}}$]])
+ context.NC() context([[$t / t_{\hbox{\tx node}}$]])
+ context.NC() context([[$\frac{t - t_{\hbox{\txx none}}}{t_{\hbox{\txx node}} - t_{\hbox{\txx none}}}$]])
+ context.NC() context.NR()
+ context.TL()
+ for k, v in table.sortedhash(data) do
+ context.NC() context(k)
+ context.NC() context("%0.2f",v)
+ context.NC() context("%0.2f",v - none)
+ context.NC() context("%0.2f",v - node)
+ context.NC() context("%0.2f",v / node)
+ context.NC() if node ~= none then context("%0.2f",(v-none) / (node-none)) end
+ context.NC() context.NR()
+ end
+ context.stoptabulate()
+ end
+ end
+ end
+
+ moduledata.plugins = {
+ processlist = processlist,
+ }
+
+\stopluacode
+
+\continueifinputfile{m-fonts-plugins.mkiv}
+
+\usemodule[art-01]
+
+\starttext
+
+\edef\tufte{\cldloadfile{tufte.tex}}
+\edef\khatt{\cldloadfile{khatt-ar.tex}}
+
+\startbuffer[latin-definitions]
+\definefont[TestA][Serif*test]
+\definefont[TestB][SerifItalic*test]
+\definefont[TestC][SerifBold*test]
+\stopbuffer
+
+\startbuffer[latin-text]
+\TestA \tufte \par
+\TestB \tufte \par
+\TestC \tufte \par
+\dorecurse {10} {%
+ \TestA Fluffy Test Font A
+ \TestB Fluffy Test Font B
+ \TestC Fluffy Test Font C
+}\par
+\stopbuffer
+
+\startbuffer[arabic-definitions]
+\definedfont[Arabic*test at 14pt]
+\setupinterlinespace[line=18pt]
+\setupalign[r2l]
+\stopbuffer
+
+\startbuffer[arabic-text]
+\dorecurse {10} {
+ \khatt\space
+ \khatt\space
+ \khatt
+ \blank
+}
+\stopbuffer
+
+\startbuffer[mixed-definitions]
+\definefont[TestL][Serif*test]
+\definefont[TestA][Arabic*test at 14pt]
+\setupinterlinespace[line=18pt]
+\setupalign[r2l]
+\stopbuffer
+
+\startbuffer[mixed-text]
+\dorecurse {2} {
+ {\TestA\khatt\space\khatt\space\khatt}
+ {\TestL\lefttoright\tufte}
+ \blank
+ \dorecurse{10}{%
+ {\TestA وَ قَرْمِطْ بَيْنَ الْحُرُوفِ؛ فَإِنَّ}
+ {\TestL\lefttoright A snippet text that makes no sense.}
+ }
+}
+\stopbuffer
+
+\definefontfeature
+ [test-none]
+ [mode=none]
+
+\definefontfeature
+ [test-base]
+ [mode=base,
+ liga=yes,
+ kern=yes]
+
+\definefontfeature
+ [test-node]
+ [mode=node,
+ script=auto,
+ autoscript=position,
+ autolanguage=position,
+ ccmp=yes,
+ liga=yes,
+ % rlig=yes,
+ % hlig=yes,
+ % dlig=yes,
+ clig=yes,
+ kern=yes,
+ mark=yes,
+ mkmk=yes,
+ curs=yes]
+
+\definefontfeature
+ [test-text]
+ [mode=plug,
+ features=text]
+
+\definefontfeature
+ [test-native]
+ [mode=plug,
+ features=harfbuzz,
+ %liga=yes,
+ %kern=yes,
+ shaper=native]
+
+\definefontfeature
+ [test-uniscribe]
+ [mode=plug,
+ features=harfbuzz,
+ %liga=yes,
+ %kern=yes,
+ shaper=uniscribe]
+
+\definefontfeature
+ [test-binary]
+ [mode=plug,
+ features=harfbuzz,
+ %liga=yes,
+ %kern=yes,
+ shaper=uniscribe,
+ method=binary]
+
+\definefontfeature
+ [arabic-node]
+ [arabic]
+
+\definefontfeature
+ [arabic-native]
+ [mode=plug,
+ features=harfbuzz,
+ % method=binary,
+ script=arab,language=dflt,
+% ccmp=yes,
+% init=yes,medi=yes,fina=yes,isol=yes,
+% liga=yes,dlig=yes,rlig=yes,clig=yes,calt=yes,
+% mark=yes,mkmk=yes,kern=yes,curs=yes,
+ shaper=native]
+
+\definefontfeature
+ [arabic-uniscribe]
+ [mode=plug,
+ features=harfbuzz,
+ script=arab,language=dflt,ccmp=yes,
+ init=yes,medi=yes,fina=yes,isol=yes,
+ liga=yes,dlig=yes,rlig=yes,clig=yes,calt=yes,
+ mark=yes,mkmk=yes,kern=yes,curs=yes,
+ shaper=uniscribe]
+
+\starttexdefinition RunLatinTest #1#2#3#4#5
+ \start
+ \dontcomplain
+ \definefontfeature[test][test-#4]
+ \writestatus{warning}{#1 #3 #4 (1 initial run)}
+ \page
+ \startluacode
+ collectgarbage("collect")
+ \stopluacode
+ \title{#1 #3 #4}
+ \start
+ \getbuffer[#5-definitions]
+ \showfontkerns
+ \showmakeup[discretionary]
+ \enabletrackers[fonts.plugins.hb.colors]%
+ \testfeatureonce{1}{
+ \getbuffer[#5-text]
+ }
+ \stop
+ \page
+ \startluacode
+ collectgarbage("collect")
+ \stopluacode
+ \ifnum#2>1\relax
+ \writestatus{warning}{#1 #3 #4 (#2 timing runs)}
+ \start
+ \getbuffer[#5-definitions]
+ \testfeatureonce{#2}{
+ \setbox\scratchbox\hbox{\getbuffer[#5-text]}
+ }
+ \stop
+ \writestatus{warning}{done}
+ \fi
+ \startluacode
+ document.collected_timings.timings["#5"].results["#1"]["#3 #4"] = \elapsedtime\space
+ collectgarbage("collect")
+ \stopluacode
+ \stop
+\stoptexdefinition
+
+\starttexdefinition RunArabicTest #1#2#3#4#5
+ \start
+ \dontcomplain
+ \definefontsynonym[Arabic][#1]
+ \definefontfeature[test][arabic-#4]
+ \writestatus{warning}{#1 #3 #4 #5 (1 initial run)}
+ \page
+ \startluacode
+ collectgarbage("collect")
+ \stopluacode
+ \title{#1 #3 #4}
+ \start
+ \getbuffer[#5-definitions]
+ \enabletrackers[fonts.plugins.hb.colors]%
+ \testfeatureonce{1}{
+ \setupalign[flushleft] % easier to compare
+ \getbuffer[#5-text]
+ }
+ \par
+ \stop
+ \page
+ \ifnum#2>1\relax
+ \writestatus{warning}{#1 #3 #4 #5 (#2 timing runs)}
+ \start
+ \getbuffer[#5-definitions]
+ \testfeatureonce{#2}{
+ \setbox\scratchbox\hbox{\getbuffer[#5-text]}
+ }
+ \stop
+ \writestatus{warning}{done}
+ \fi
+ \startluacode
+ document.collected_timings.timings["#5"].results["#1"]["#3 #4"] = \elapsedtime\space
+ collectgarbage("collect")
+ \stopluacode
+ \stop
+\stoptexdefinition
+
+\startluacode
+ local processlist = moduledata.plugins.processlist
+
+ local data = {
+ timings = { },
+ engine = jit and "luajittex" or "luatex",
+ }
+
+ document.collected_timings = data
+
+ -- LATIN
+
+ local list = {
+ "modern",
+ "pagella",
+ "dejavu",
+ "cambria",
+ "ebgaramond",
+ "lucidaot"
+ }
+
+ data.timings["latin"] = {
+ list = list,
+ results = table.setmetatableindex("table"),
+ }
+
+ for i=1,#list do
+
+ local name = list[i]
+
+ context.setupbodyfont { name }
+ context.RunLatinTest (name, 100, "context", "none", "latin")
+ context.RunLatinTest (name, 100, "context", "base", "latin")
+ context.RunLatinTest (name, 100, "context", "node", "latin")
+ context.RunLatinTest (name, 100, "harfbuzz", "native", "latin")
+ -- context.RunLatinTest (name, 100, "harfbuzz", "uniscribe", "latin")
+ -- context.RunLatinTest (name, 1, "context", "text", "latin")
+ -- context.RunLatinTest (name, 1, "harfbuzz", "binary", "latin")
+
+ end
+
+ context(function()
+ context.page()
+ context.title((jit and "luajittex" or "luatex") .. " latin")
+ processlist(data.timings["latin"])
+ context.page()
+ end)
+
+ -- ARABIC
+
+ local list = {
+ "arabtype"
+ }
+
+ data.timings["arabic"] = {
+ list = list,
+ results = table.setmetatableindex("table")
+ }
+
+ for i=1,#list do
+
+ local name = list[i]
+
+ context.setupbodyfont { name }
+ context.RunArabicTest (name, 100, "context", "none", "arabic")
+ context.RunArabicTest (name, 100, "context", "base", "arabic")
+ context.RunArabicTest (name, 100, "context", "node", "arabic")
+ context.RunArabicTest (name, 100, "harfbuzz", "native", "arabic")
+ -- context.RunArabicTest (name, 100, "harfbuzz", "uniscribe", "arabic")
+ -- context.RunArabicTest (name, 1, "context", "text", "arabic")
+ -- context.RunArabicTest (name, 1, "harfbuzz", "binary", "arabic")
+
+ end
+
+ context(function()
+ context.page()
+ context.title((jit and "luajittex" or "luatex") .. " arabic")
+ processlist(data.timings["arabic"])
+ context.page()
+ end)
+
+ -- MIXED
+
+ local list = {
+ "arabtype"
+ }
+
+ data.timings["mixed"] = {
+ list = list,
+ results = table.setmetatableindex("table")
+ }
+
+ for i=1,#list do
+
+ local name = list[i]
+
+ context.setupbodyfont { name }
+ context.RunArabicTest (name, 100, "context", "none", "mixed")
+ context.RunArabicTest (name, 100, "context", "base", "mixed")
+ context.RunArabicTest (name, 100, "context", "node", "mixed")
+ context.RunArabicTest (name, 100, "harfbuzz", "native", "mixed")
+ -- context.RunArabicTest (name, 100, "harfbuzz", "uniscribe", "mixed")
+ -- context.RunArabicTest (name, 1, "context", "text", "mixed")
+ -- context.RunArabicTest (name, 1, "harfbuzz", "binary", "mixed")
+
+ end
+
+ context(function()
+ context.page()
+ context.title((jit and "luajittex" or "luatex") .. " mixed")
+ processlist(data.timings["mixed"])
+ context.page()
+ end)
+
+ context(function()
+ table.save("m-fonts-plugins-timings-" .. (jit and "luajittex" or "luatex") .. ".lua",data)
+ end)
+
+\stopluacode
+
+\stoptext
diff --git a/tex/context/base/mkiv/mlib-pdf.lua b/tex/context/base/mkiv/mlib-pdf.lua
index 4d1756c43..0c2945316 100644
--- a/tex/context/base/mkiv/mlib-pdf.lua
+++ b/tex/context/base/mkiv/mlib-pdf.lua
@@ -571,7 +571,7 @@ function metapost.flush(result,flusher,askedfig)
result[#result+1] = evenodd and "h f*" or "h f" -- f* = eo
elseif objecttype == "outline" then
if both then
- result[#result+1] = evenodd and "h B*" or "h B" -- f* = eo
+ result[#result+1] = evenodd and "h B*" or "h B" -- B* = eo
else
result[#result+1] = open and "S" or "h S"
end
diff --git a/tex/context/base/mkiv/node-tra.lua b/tex/context/base/mkiv/node-tra.lua
index 0d3192559..8c79e0ab8 100644
--- a/tex/context/base/mkiv/node-tra.lua
+++ b/tex/context/base/mkiv/node-tra.lua
@@ -437,12 +437,12 @@ dimenfactors[""] = dimenfactors.pt
local function numbertodimen(d,unit,fmt)
if not d or d == 0 then
- if not unit or unit == "pt" then
- return "0pt"
- elseif fmt then
- return formatters[fmt](0,unit)
- else
+ if fmt then
+ return formatters[fmt](0,unit or "pt")
+ elseif unit then
return 0 .. unit
+ else
+ return "0pt"
end
elseif fmt then
if not unit then
diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf
index dbcb86a1f..641067bfc 100644
--- a/tex/context/base/mkiv/status-files.pdf
+++ b/tex/context/base/mkiv/status-files.pdf
Binary files differ
diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf
index 637ce005e..6c11b6984 100644
--- a/tex/context/base/mkiv/status-lua.pdf
+++ b/tex/context/base/mkiv/status-lua.pdf
Binary files differ
diff --git a/tex/context/base/mkiv/util-fil.lua b/tex/context/base/mkiv/util-fil.lua
index d0ffe07c6..cb36db7be 100644
--- a/tex/context/base/mkiv/util-fil.lua
+++ b/tex/context/base/mkiv/util-fil.lua
@@ -116,6 +116,7 @@ function files.readcardinal2(f)
local a, b = byte(f:read(2),1,2)
return 0x100 * a + b
end
+
function files.readcardinal2le(f)
local b, a = byte(f:read(2),1,2)
return 0x100 * a + b
@@ -123,27 +124,17 @@ end
function files.readinteger2(f)
local a, b = byte(f:read(2),1,2)
- local n = 0x100 * a + b
- if n >= 0x8000 then
- -- return n - 0xFFFF - 1
- return n - 0x10000
+ if a >= 0x80 then
+ return 0x100 * a + b - 0x10000
else
- return n
+ return 0x100 * a + b
end
end
- function files.readinteger2(f)
- local a, b = byte(f:read(2),1,2)
- if a >= 0x80 then
- return 0x100 * a + b - 0x10000
- else
- return 0x100 * a + b
- end
- end
+
function files.readinteger2le(f)
local b, a = byte(f:read(2),1,2)
local n = 0x100 * a + b
if n >= 0x8000 then
- -- return n - 0xFFFF - 1
return n - 0x10000
else
return n
@@ -154,6 +145,7 @@ function files.readcardinal3(f)
local a, b, c = byte(f:read(3),1,3)
return 0x10000 * a + 0x100 * b + c
end
+
function files.readcardinal3le(f)
local c, b, a = byte(f:read(3),1,3)
return 0x10000 * a + 0x100 * b + c
@@ -163,17 +155,16 @@ function files.readinteger3(f)
local a, b, c = byte(f:read(3),1,3)
local n = 0x10000 * a + 0x100 * b + c
if n >= 0x80000 then
- -- return n - 0xFFFFFF - 1
return n - 0x1000000
else
return n
end
end
+
function files.readinteger3le(f)
local c, b, a = byte(f:read(3),1,3)
local n = 0x10000 * a + 0x100 * b + c
if n >= 0x80000 then
- -- return n - 0xFFFFFF - 1
return n - 0x1000000
else
return n
@@ -184,21 +175,12 @@ function files.readcardinal4(f)
local a, b, c, d = byte(f:read(4),1,4)
return 0x1000000 * a + 0x10000 * b + 0x100 * c + d
end
+
function files.readcardinal4le(f)
local d, c, b, a = byte(f:read(4),1,4)
return 0x1000000 * a + 0x10000 * b + 0x100 * c + d
end
--- function files.readinteger4(f)
--- local a, b, c, d = byte(f:read(4),1,4)
--- local n = 0x1000000 * a + 0x10000 * b + 0x100 * c + d
--- if n >= 0x8000000 then
--- -- return n - 0xFFFFFFFF - 1
--- return n - 0x100000000
--- else
--- return n
--- end
--- end
function files.readinteger4(f)
local a, b, c, d = byte(f:read(4),1,4)
if a >= 0x80 then
@@ -207,11 +189,11 @@ function files.readinteger4(f)
return 0x1000000 * a + 0x10000 * b + 0x100 * c + d
end
end
+
function files.readinteger4le(f)
local d, c, b, a = byte(f:read(4),1,4)
local n = 0x1000000 * a + 0x10000 * b + 0x100 * c + d
if n >= 0x8000000 then
- -- return n - 0xFFFFFFFF - 1
return n - 0x100000000
else
return n
@@ -227,6 +209,8 @@ function files.readfixed2(f)
end
end
+-- (real) (n>>16) + ((n&0xffff)/65536.0))
+
function files.readfixed4(f)
local a, b, c, d = byte(f:read(4),1,4)
if a >= 0x80 then
@@ -234,7 +218,6 @@ function files.readfixed4(f)
else
return (0x1000000 * a + 0x10000 * b + 0x100 * c + d)/65536.0
end
-
end
if extract then
@@ -242,10 +225,17 @@ if extract then
local extract = bit32.extract
local band = bit32.band
+ -- (real) ((n<<16)>>(16+14)) + ((n&0x3fff)/16384.0))
+
function files.read2dot14(f)
local a, b = byte(f:read(2),1,2)
- local n = 0x100 * a + b
- return extract(n,14,2) + (band(n,0x3FFF) / 16384.0)
+ if a >= 0x80 then
+ local n = -(0x100 * a + b)
+ return - (extract(n,14,2) + (band(n,0x3FFF) / 16384.0))
+ else
+ local n = 0x100 * a + b
+ return (extract(n,14,2) + (band(n,0x3FFF) / 16384.0))
+ end
end
end
diff --git a/tex/context/interface/mkiv/i-context.pdf b/tex/context/interface/mkiv/i-context.pdf
index ef5687741..496d93c5f 100644
--- a/tex/context/interface/mkiv/i-context.pdf
+++ b/tex/context/interface/mkiv/i-context.pdf
Binary files differ
diff --git a/tex/context/interface/mkiv/i-readme.pdf b/tex/context/interface/mkiv/i-readme.pdf
index 59eabb096..d69065fa9 100644
--- a/tex/context/interface/mkiv/i-readme.pdf
+++ b/tex/context/interface/mkiv/i-readme.pdf
Binary files differ
diff --git a/tex/context/modules/mkiv/m-ipsum.mkiv b/tex/context/modules/mkiv/m-ipsum.mkiv
index 1c5901d86..7ba78ee2e 100644
--- a/tex/context/modules/mkiv/m-ipsum.mkiv
+++ b/tex/context/modules/mkiv/m-ipsum.mkiv
@@ -127,6 +127,7 @@ end
\startsetups[handler:action:ipsum]
\useipsumstyleandcolor\c!style\c!color
+ % hm, also changes dates
\uselanguageparameter\ipsumparameter
\ctxlua{moduledata.ipsum.typeset {
alternative = "\ipsumparameter\c!alternative",
diff --git a/tex/context/modules/mkiv/s-fonts-shapes.lua b/tex/context/modules/mkiv/s-fonts-shapes.lua
index c07fad285..ebdf04c22 100644
--- a/tex/context/modules/mkiv/s-fonts-shapes.lua
+++ b/tex/context/modules/mkiv/s-fonts-shapes.lua
@@ -113,7 +113,7 @@ local function showglyphshape(specification)
local tfmdata = fontdata[id]
local charnum = tonumber(specification.character)
if not charnum then
- charnum = fonts.helpers.nametoslot(n)
+ charnum = fonts.helpers.nametoslot(specification.character)
end
local characters = tfmdata.characters
local descriptions = tfmdata.descriptions
diff --git a/tex/context/modules/mkiv/s-fonts-shapes.mkiv b/tex/context/modules/mkiv/s-fonts-shapes.mkiv
index 3e2bc26f6..4a0377ada 100644
--- a/tex/context/modules/mkiv/s-fonts-shapes.mkiv
+++ b/tex/context/modules/mkiv/s-fonts-shapes.mkiv
@@ -18,7 +18,7 @@
\registerctxluafile{s-fonts-shapes}{}
\installmodulecommandluasingle \showfontshapes {moduledata.fonts.shapes.showlist}
-\installmodulecommandluasingle \showglyphshape {moduledata.fonts.shapes.showglypshape}
+\installmodulecommandluasingle \showglyphshape {moduledata.fonts.shapes.showglyphshape}
\installmodulecommandluatwo \showlastglyphshapefield {moduledata.fonts.shapes.showlastglyphshapefield}
\installmodulecommandluasingle \showallglyphshapes {moduledata.fonts.shapes.showallglypshapes}
diff --git a/tex/context/modules/mkiv/s-fonts-variable.lua b/tex/context/modules/mkiv/s-fonts-variable.lua
new file mode 100644
index 000000000..d507141e2
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-variable.lua
@@ -0,0 +1,264 @@
+if not modules then modules = { } end modules ['s-fonts-variable'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-variable.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.variable = moduledata.fonts.variable or { }
+
+local format = string.format
+local stripstring = string.nospaces
+local lower = string.lower
+local rep = string.rep
+
+local context = context
+local NC, NR, HL = context.NC, context.NR, context.HL
+local bold, monobold, mono, formattedmono = context.bold, context.monobold, context.mono, context.formatted.mono
+
+function moduledata.fonts.variable.showvariations(specification)
+
+ specification = interfaces.checkedspecification(specification)
+
+ local fontfile = specification.font
+ local fontname = format("testfont-%s",i)
+ local fontsize = tex.dimen.bodyfontsize
+ if not fontfile then
+ return
+ end
+
+ local id, fontdata = fonts.definers.define {
+ name = fontfile,
+ size = fontsize,
+ cs = fontname,
+ }
+
+ local resources = fontdata.resources
+
+ if not resources then
+ return
+ end
+
+ local variabledata = resources.variabledata
+
+ if not variabledata then
+ return
+ end
+
+ context.starttitle { title = fontdata.shared.rawdata.metadata.fullname }
+
+ local parameters = fontdata.parameters
+
+ context.startsubject { title = "parameters" }
+ if parameters then
+ context.starttabulate { "|||" }
+ NC() monobold("ascender") NC() context("%p",parameters.ascender) NC() NR()
+ NC() monobold("descender") NC() context("%p",parameters.descender) NC() NR()
+ NC() monobold("emwidth") NC() context("%p",parameters.em) NC() NR()
+ NC() monobold("exheight") NC() context("%p",parameters.ex) NC() NR()
+ NC() monobold("size") NC() context("%p",parameters.size) NC() NR()
+ NC() monobold("slant") NC() context("%s",parameters.slant) NC() NR()
+ NC() monobold("space") NC() context("%p",parameters.space) NC() NR()
+ NC() monobold("shrink") NC() context("%p",parameters.spaceshrink) NC() NR()
+ NC() monobold("stretch") NC() context("%p",parameters.spacestretch) NC() NR()
+ NC() monobold("units") NC() context("%s",parameters.units) NC() NR()
+ context.stoptabulate()
+ else
+ context("no parameters")
+ end
+ context.stopsubject()
+
+ local features = fontdata.shared.rawdata.resources.features
+
+ context.startsubject { title = "features" }
+ if features then
+ local function f(g)
+ if g then
+ local t = table.sortedkeys(g)
+ local n = 0
+ for i=1,#t do
+ if #t[i] <= 4 then
+ n = n + 1
+ t[n] = t[i]
+ end
+ end
+ return table.concat(t," ",1,n)
+ end
+ end
+ context.starttabulate { "||p|" }
+ NC() monobold("gpos") NC() mono(f(features.gpos)) NC() NR()
+ NC() monobold("gsub") NC() mono(f(features.gsub)) NC() NR()
+ context.stoptabulate()
+ else
+ context("no features")
+ end
+ context.stopsubject()
+
+ local designaxis = variabledata.designaxis
+
+ context.startsubject { title = "design axis" }
+ if designaxis then
+ context.starttabulate { "||||c|c|c|c|c|" }
+ NC() bold("tag")
+ NC() bold("name")
+ NC() bold("variant")
+ NC() bold("flags")
+ NC() bold("value")
+ NC() bold("min")
+ NC() bold("max")
+ NC() bold("link")
+ NC() NR()
+ HL()
+ for k=1,#designaxis do
+ local axis = designaxis[k]
+ local tag = axis.tag
+ local name = axis.name
+ local variants = axis.variants
+ local haslimits = variants[1].maximum
+ local haslink = variants[1].link
+ for i=1,#variants do
+ local variant = variants[i]
+ NC() monobold(tag)
+ NC() context(name)
+ NC() context(variant.name)
+ NC() formattedmono("0x%04x",variant.flags)
+ NC() context(variant.value)
+ NC() context(variant.minimum or "-")
+ NC() context(variant.maximum or "-")
+ NC() context(variant.link or "-")
+ NC() NR()
+ tag = nil
+ name = nil
+ end
+ end
+ context.stoptabulate()
+ else
+ context("no design axis defined (no \\type{stat} table)")
+ end
+ context.stopsubject()
+
+ local axis = variabledata.axis
+ local instances = variabledata.instances
+ local list = { }
+
+ context.startsubject { title = "axis" }
+ if axis then
+ context.starttabulate { "|||c|c|c|" }
+ NC() bold("tag")
+ NC() bold("name")
+ NC() bold("min")
+ NC() bold("def")
+ NC() bold("max")
+ NC() NR()
+ HL()
+ for k=1,#axis do
+ local a = axis[k]
+ NC() monobold(a.tag)
+ NC() context(a.name)
+ NC() context(a.minimum)
+ NC() context(a.default)
+ NC() context(a.maximum)
+ NC() NR()
+ list[#list+1] = a.tag
+ end
+ context.stoptabulate()
+ else
+ context("no axis defined, incomplete \\type{fvar} table")
+ end
+ context.stopsubject()
+
+ local collected = { }
+
+ context.startsubject { title = "instances" }
+ if instances and #list > 0 then
+ context.starttabulate { "||" .. rep("c|",#list) .. "|" }
+ NC()
+ for i=1,#list do
+ NC() monobold(list[i])
+ end
+ NC()
+ local fullname = lower(stripstring(fontdata.shared.rawdata.metadata.fullname))
+ formattedmono("%s*",fullname)
+ NC() NR()
+ HL()
+ for k=1,#instances do
+ local i = instances[k]
+ NC() monobold(i.subfamily)
+ local values = i.values
+ local hash = { }
+ for k=1,#values do
+ local v = values[k]
+ hash[v.axis] = v.value
+ end
+ for i=1,#list do
+ NC() context(hash[list[i]])
+ end
+ NC()
+ local instance = lower(stripstring(i.subfamily))
+ mono(instance)
+ collected[#collected+1] = fullname .. instance
+ NC() NR()
+ end
+ context.stoptabulate()
+ else
+ context("no instances defined, incomplete \\type{fvar} table")
+ end
+ context.stopsubject()
+
+ for i=1,#collected do
+
+ local instance = collected[i]
+ context.startsubject { title = instance }
+ context.start()
+ context.definedfont { "name:" .. instance .. "*default" }
+ context.input("zapf.tex")
+ context.par()
+ context.stop()
+ context.stopsubject()
+ end
+
+ -- local function showregions(tag)
+ --
+ -- local regions = variabledata[tag]
+ --
+ -- context.startsubject { title = tag }
+ -- if regions then
+ -- context.starttabulate { "|r|c|r|r|r|" }
+ -- NC() bold("n")
+ -- NC() bold("axis")
+ -- NC() bold("start")
+ -- NC() bold("peak")
+ -- NC() bold("stop")
+ -- NC() NR()
+ -- HL()
+ -- local designaxis = designaxis or axis
+ -- for i=1,#regions do
+ -- local axis = regions[i]
+ -- for j=1,#axis do
+ -- local a = axis[j]
+ -- NC() monobold(i)
+ -- NC() monobold(designaxis[j].tag)
+ -- NC() context("%0.3f",a.start)
+ -- NC() context("%0.3f",a.peak)
+ -- NC() context("%0.3f",a.stop)
+ -- NC() NR()
+ -- i = nil
+ -- end
+ -- end
+ -- context.stoptabulate()
+ -- else
+ -- context("no %s defined",tag)
+ -- end
+ -- context.stopsubject()
+ --
+ -- end
+ --
+ -- showregions("gregions")
+ -- showregions("mregions")
+ -- showregions("hregions")
+
+ context.stoptitle()
+
+end
diff --git a/tex/context/modules/mkiv/s-fonts-variable.mkiv b/tex/context/modules/mkiv/s-fonts-variable.mkiv
new file mode 100644
index 000000000..564c40e11
--- /dev/null
+++ b/tex/context/modules/mkiv/s-fonts-variable.mkiv
@@ -0,0 +1,46 @@
+%D \module
+%D [ file=s-fonts-variable,
+%D version=2017.02.18,
+%D title=\CONTEXT\ Style File,
+%D subtitle=Show Variable Font Properties ,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% begin info
+%
+% title : variable information (experimental)
+%
+% comment : variable fonts contain extra information styles, axis, regions
+% status : experimental, used for luatex testing
+%
+% end info
+
+\startmodule[fonts-variable]
+
+\registerctxluafile{s-fonts-variable}{}
+
+\installmodulecommandluasingle \showfontvariations {moduledata.fonts.variable.showvariations}
+
+\stopmodule
+
+\continueifinputfile{s-fonts-variable.mkiv}
+
+\usemodule[art-01]
+
+\starttext
+
+ \showfontvariations
+ [font=file:adobevfprototype.otf]
+
+ \showfontvariations
+ [font=file:avenirnextvariable.ttf]
+
+% \showfontvariations
+% [font=file:kairossansvariable.ttf]
+
+\stoptext
diff --git a/tex/generic/context/luatex/luatex-core.lua b/tex/generic/context/luatex/luatex-core.lua
index ac552e70a..b32be8095 100644
--- a/tex/generic/context/luatex/luatex-core.lua
+++ b/tex/generic/context/luatex/luatex-core.lua
@@ -5,7 +5,7 @@
-- copyright = 'LuaTeX Development Team',
-- }
-LUATEXCOREVERSION = 1.001
+LUATEXCOREVERSION = 1.002
-- This file overloads some Lua functions. The readline variants provide the same
-- functionality as LuaTeX <= 1.04 and doing it this way permits us to keep the
@@ -24,7 +24,6 @@ local fio_recordfilename = fio.recordfilename
local mt = getmetatable(io.stderr)
local mt_lines = mt.lines
-
local saferoption = status.safer_option
local shellescape = status.shell_escape -- 0 (disabled) 1 (restricted) 2 (everything)
local kpseused = status.kpse_used -- 0 1
@@ -98,45 +97,68 @@ if kpseused == 1 then
io.open = luatex_io_open
io.popen = luatex_io_popen
- if saferoption then
+end
- os.execute = nil
- os.spawn = nil
- os.exec = nil
- os.setenv = nil
- os.tempdir = nil
+if saferoption == 1 then
- io.popen = nil
- io.open = nil
+ os.execute = nil
+ os.spawn = nil
+ os.exec = nil
+ os.setenv = nil
+ os.tempdir = nil
- os.rename = nil
- os.remove = nil
+ io.popen = nil
+ io.open = nil
- io.tmpfile = nil
- io.output = nil
+ os.rename = nil
+ os.remove = nil
- lfs.chdir = nil
- lfs.lock = nil
- lfs.touch = nil
- lfs.rmdir = nil
- lfs.mkdir = nil
+ io.tmpfile = nil
+ io.output = nil
- io.saved_popen = nil
- io.saved_open = luatex_io_open_readonly
+ lfs.chdir = nil
+ lfs.lock = nil
+ lfs.touch = nil
+ lfs.rmdir = nil
+ lfs.mkdir = nil
- end
+ io.saved_popen = nil
+ io.saved_open = luatex_io_open_readonly
- if saferoption or shellescape ~= 2 then
- local ffi = require('ffi')
- for k, v in next, ffi do
- if k ~= 'gc' then
- ffi[k] = nil
- end
- ffi = nil
+end
+
+if saferoption == 1 or shellescape ~= 2 then
+
+ ffi = require('ffi')
+ for k, v in next, ffi do
+ if k ~= 'gc' then
+ ffi[k] = nil
end
end
+ ffi = nil
+end
+
+-- os.[execute|os.spawn|os.exec] already are shellescape aware)
+
+
+if md5 then
+
+ local sum = md5.sum
+ local gsub = string.gsub
+ local format = string.format
+ local byte = string.byte
+
+ function md5.sumhexa(k)
+ return (gsub(sum(k), ".", function(c)
+ return format("%02x",byte(c))
+ end))
+ end
- -- os.[execute|os.spawn|os.exec] already are shellescape aware)
+ function md5.sumHEXA(k)
+ return (gsub(sum(k), ".", function(c)
+ return format("%02X",byte(c))
+ end))
+ end
end
@@ -152,10 +174,17 @@ if utilities and utilities.merger and utilities.merger.compact then
local d = gsub(data,'\r\n','\n') -- be nice for unix
local s = utilities.merger.compact(d) -- no comments and less spaces
+ t[#t+1] = '/* generated from and by luatex-core.lua */'
+ t[#t+1] = ''
-- t[#t+1] = format('/*\n\n%s\n\n*/',d)
+ -- t[#t+1] = ''
+ t[#t+1] = '#include "lua.h"'
+ t[#t+1] = '#include "lauxlib.h"'
+ t[#t+1] = ''
+ t[#t+1] = 'int load_luatex_core_lua (lua_State * L);'
+ t[#t+1] = ''
t[#t+1] = 'int load_luatex_core_lua (lua_State * L)'
t[#t+1] = '{'
- t[#t+1] = ' /* generated from and by luatex-core.lua */'
t[#t+1] = ' static unsigned char luatex_core_lua[] = {'
for c in gmatch(d,'.') do
if n == 16 then
diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua
index 2e5aa99c0..d946dedfd 100644
--- a/tex/generic/context/luatex/luatex-fonts-merged.lua
+++ b/tex/generic/context/luatex/luatex-fonts-merged.lua
@@ -1,6 +1,6 @@
-- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua
-- parent file : c:/data/develop/context/sources/luatex-fonts.lua
--- merge date : 03/02/17 22:23:29
+-- merge date : 03/20/17 17:33:01
do -- begin closure to overcome local limits and interference
@@ -213,6 +213,7 @@ patterns.nonwhitespace=nonwhitespace
local stripper=spacer^0*C((spacer^0*nonspacer^1)^0)
local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0)
local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0))
+local nospacer=Cs((whitespace^1/""+nonwhitespace^1)^0)
local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0)
local e_collapser=Cs((whitespace^1*P(-1)/""+nonwhitespace^1+whitespace^1/" ")^0)
local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0)
@@ -222,6 +223,7 @@ local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0)
patterns.stripper=stripper
patterns.fullstripper=fullstripper
patterns.collapser=collapser
+patterns.nospacer=nospacer
patterns.b_collapser=b_collapser
patterns.m_collapser=m_collapser
patterns.e_collapser=e_collapser
@@ -955,6 +957,7 @@ end
local stripper=patterns.stripper
local fullstripper=patterns.fullstripper
local collapser=patterns.collapser
+local nospacer=patterns.nospacer
local longtostring=patterns.longtostring
function string.strip(str)
return str and lpegmatch(stripper,str) or ""
@@ -965,6 +968,9 @@ end
function string.collapsespaces(str)
return str and lpegmatch(collapser,str) or ""
end
+function string.nospaces(str)
+ return str and lpegmatch(nospacer,str) or ""
+end
function string.longtostring(str)
return str and lpegmatch(longtostring,str) or ""
end
@@ -4360,21 +4366,12 @@ function files.readcardinal2le(f)
end
function files.readinteger2(f)
local a,b=byte(f:read(2),1,2)
- local n=0x100*a+b
- if n>=0x8000 then
- return n-0x10000
+ if a>=0x80 then
+ return 0x100*a+b-0x10000
else
- return n
+ return 0x100*a+b
end
end
- function files.readinteger2(f)
- local a,b=byte(f:read(2),1,2)
- if a>=0x80 then
- return 0x100*a+b-0x10000
- else
- return 0x100*a+b
- end
- end
function files.readinteger2le(f)
local b,a=byte(f:read(2),1,2)
local n=0x100*a+b
@@ -4456,8 +4453,13 @@ if extract then
local band=bit32.band
function files.read2dot14(f)
local a,b=byte(f:read(2),1,2)
- local n=0x100*a+b
- return extract(n,14,2)+(band(n,0x3FFF)/16384.0)
+ if a>=0x80 then
+ local n=-(0x100*a+b)
+ return-(extract(n,14,2)+(band(n,0x3FFF)/16384.0))
+ else
+ local n=0x100*a+b
+ return (extract(n,14,2)+(band(n,0x3FFF)/16384.0))
+ end
end
end
function files.skipshort(f,n)
@@ -6099,7 +6101,8 @@ if not modules then modules={} end modules ['font-con']={
}
local next,tostring,rawget=next,tostring,rawget
local format,match,lower,gsub,find=string.format,string.match,string.lower,string.gsub,string.find
-local sort,insert,concat,sortedkeys,serialize,fastcopy=table.sort,table.insert,table.concat,table.sortedkeys,table.serialize,table.fastcopy
+local sort,insert,concat=table.sort,table.insert,table.concat
+local sortedkeys,sortedhash,serialize,fastcopy=table.sortedkeys,table.sortedhash,table.serialize,table.fastcopy
local derivetable=table.derive
local ioflush=io.flush
local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
@@ -6844,20 +6847,20 @@ constructors.hashmethods=hashmethods
function constructors.hashfeatures(specification)
local features=specification.features
if features then
- local t,tn={},0
- for category,list in next,features do
+ local t,n={},0
+ for category,list in sortedhash(features) do
if next(list) then
local hasher=hashmethods[category]
if hasher then
local hash=hasher(list)
if hash then
- tn=tn+1
- t[tn]=category..":"..hash
+ n=n+1
+ t[n]=category..":"..hash
end
end
end
end
- if tn>0 then
+ if n>0 then
return concat(t," & ")
end
end
@@ -8130,10 +8133,11 @@ local setmetatableindex=table.setmetatableindex
local formatters=string.formatters
local sortedkeys=table.sortedkeys
local sortedhash=table.sortedhash
-local stripstring=string.strip
+local stripstring=string.nospaces
local utf16_to_utf8_be=utf.utf16_to_utf8_be
local report=logs.reporter("otf reader")
local trace_cmap=false
+local trace_cmap_detail=false
fonts=fonts or {}
local handlers=fonts.handlers or {}
fonts.handlers=handlers
@@ -8370,6 +8374,28 @@ local panosewidths={
[ 8]="verycondensed",
[ 9]="monospaced",
}
+local helpers={}
+readers.helpers=helpers
+local function gotodatatable(f,fontdata,tag,criterium)
+ if criterium and f then
+ local datatable=fontdata.tables[tag]
+ if datatable then
+ local tableoffset=datatable.offset
+ setposition(f,tableoffset)
+ return tableoffset
+ end
+ end
+end
+local function setvariabledata(fontdata,tag,data)
+ local variabledata=fontdata.variabledata
+ if variabledata then
+ variabledata[tag]=data
+ else
+ fontdata.variabledata={ [tag]=data }
+ end
+end
+helpers.gotodatatable=gotodatatable
+helpers.setvariabledata=setvariabledata
local platformnames={
postscriptname=true,
fullname=true,
@@ -8380,13 +8406,12 @@ local platformnames={
compatiblefullname=true,
}
function readers.name(f,fontdata,specification)
- local datatable=fontdata.tables.name
- if datatable then
- setposition(f,datatable.offset)
+ local tableoffset=gotodatatable(f,fontdata,"name",true)
+ if tableoffset then
local format=readushort(f)
local nofnames=readushort(f)
local offset=readushort(f)
- local start=datatable.offset+offset
+ local start=tableoffset+offset
local namelists={
unicode={},
windows={},
@@ -8407,24 +8432,15 @@ function readers.name(f,fontdata,specification)
if encoding and language then
local index=readushort(f)
local name=reservednames[index]
- if name then
- namelist[#namelist+1]={
- platform=platform,
- encoding=encoding,
- language=language,
- name=name,
- length=readushort(f),
- offset=start+readushort(f),
- }
- else
-namelist[#namelist+1]={
- platform=platform,
- encoding=encoding,
- language=language,
- length=readushort(f),
- offset=start+readushort(f),
-}
- end
+ namelist[#namelist+1]={
+ platform=platform,
+ encoding=encoding,
+ language=language,
+ name=name,
+ index=index,
+ length=readushort(f),
+ offset=start+readushort(f),
+ }
else
skipshort(f,3)
end
@@ -8446,6 +8462,7 @@ namelist[#namelist+1]={
for i=1,#namelist do
local name=namelist[i]
local nametag=name.name
+ local index=name.index
if not done[nametag or i] then
local encoding=name.encoding
local language=name.language
@@ -8467,7 +8484,7 @@ namelist[#namelist+1]={
language=language,
}
end
- extras[i-1]=content
+ extras[index]=content
done[nametag or i]=true
end
end
@@ -8527,9 +8544,8 @@ local function getname(fontdata,key)
end
end
readers["os/2"]=function(f,fontdata)
- local datatable=fontdata.tables["os/2"]
- if datatable then
- setposition(f,datatable.offset)
+ local tableoffset=gotodatatable(f,fontdata,"os/2",true)
+ if tableoffset then
local version=readushort(f)
local windowsmetrics={
version=version,
@@ -8579,9 +8595,8 @@ readers["os/2"]=function(f,fontdata)
end
end
readers.head=function(f,fontdata)
- local datatable=fontdata.tables.head
- if datatable then
- setposition(f,datatable.offset)
+ local tableoffset=gotodatatable(f,fontdata,"head",true)
+ if tableoffset then
local fontheader={
version=readfixed(f),
revision=readfixed(f),
@@ -8608,178 +8623,156 @@ readers.head=function(f,fontdata)
fontdata.nofglyphs=0
end
readers.hhea=function(f,fontdata,specification)
- if specification.details then
- local datatable=fontdata.tables.hhea
- if datatable then
- setposition(f,datatable.offset)
- fontdata.horizontalheader={
- version=readfixed(f),
- ascender=readfword(f),
- descender=readfword(f),
- linegap=readfword(f),
- maxadvancewidth=readufword(f),
- minleftsidebearing=readfword(f),
- minrightsidebearing=readfword(f),
- maxextent=readfword(f),
- caretsloperise=readshort(f),
- caretsloperun=readshort(f),
- caretoffset=readshort(f),
- reserved_1=readshort(f),
- reserved_2=readshort(f),
- reserved_3=readshort(f),
- reserved_4=readshort(f),
- metricdataformat=readshort(f),
- nofmetrics=readushort(f),
- }
- else
- fontdata.horizontalheader={
- nofmetrics=0,
- }
- end
+ local tableoffset=gotodatatable(f,fontdata,"hhea",specification.details)
+ if tableoffset then
+ fontdata.horizontalheader={
+ version=readfixed(f),
+ ascender=readfword(f),
+ descender=readfword(f),
+ linegap=readfword(f),
+ maxadvancewidth=readufword(f),
+ minleftsidebearing=readfword(f),
+ minrightsidebearing=readfword(f),
+ maxextent=readfword(f),
+ caretsloperise=readshort(f),
+ caretsloperun=readshort(f),
+ caretoffset=readshort(f),
+ reserved_1=readshort(f),
+ reserved_2=readshort(f),
+ reserved_3=readshort(f),
+ reserved_4=readshort(f),
+ metricdataformat=readshort(f),
+ nofmetrics=readushort(f),
+ }
+ else
+ fontdata.horizontalheader={
+ nofmetrics=0,
+ }
end
end
readers.vhea=function(f,fontdata,specification)
- if specification.details then
- local datatable=fontdata.tables.vhea
- if datatable then
- setposition(f,datatable.offset)
- local version=readfixed(f)
- fontdata.verticalheader={
+ local tableoffset=gotodatatable(f,fontdata,"vhea",specification.details)
+ if tableoffset then
+ fontdata.verticalheader={
+ version=readfixed(f),
+ ascender=readfword(f),
+ descender=readfword(f),
+ linegap=readfword(f),
+ maxadvanceheight=readufword(f),
+ mintopsidebearing=readfword(f),
+ minbottomsidebearing=readfword(f),
+ maxextent=readfword(f),
+ caretsloperise=readshort(f),
+ caretsloperun=readshort(f),
+ caretoffset=readshort(f),
+ reserved_1=readshort(f),
+ reserved_2=readshort(f),
+ reserved_3=readshort(f),
+ reserved_4=readshort(f),
+ metricdataformat=readshort(f),
+ nofmetrics=readushort(f),
+ }
+ else
+ fontdata.verticalheader={
+ nofmetrics=0,
+ }
+ end
+end
+readers.maxp=function(f,fontdata,specification)
+ local tableoffset=gotodatatable(f,fontdata,"maxp",specification.details)
+ if tableoffset then
+ local version=readfixed(f)
+ local nofglyphs=readushort(f)
+ fontdata.nofglyphs=nofglyphs
+ if version==0.5 then
+ fontdata.maximumprofile={
+ version=version,
+ nofglyphs=nofglyphs,
+ }
+ elseif version==1.0 then
+ fontdata.maximumprofile={
version=version,
- ascender=readfword(f),
- descender=readfword(f),
- linegap=readfword(f),
- maxadvanceheight=readufword(f),
- mintopsidebearing=readfword(f),
- minbottomsidebearing=readfword(f),
- maxextent=readfword(f),
- caretsloperise=readshort(f),
- caretsloperun=readshort(f),
- caretoffset=readshort(f),
- reserved_1=readshort(f),
- reserved_2=readshort(f),
- reserved_3=readshort(f),
- reserved_4=readshort(f),
- metricdataformat=readshort(f),
- nofmetrics=readushort(f),
+ nofglyphs=nofglyphs,
+ points=readushort(f),
+ contours=readushort(f),
+ compositepoints=readushort(f),
+ compositecontours=readushort(f),
+ zones=readushort(f),
+ twilightpoints=readushort(f),
+ storage=readushort(f),
+ functiondefs=readushort(f),
+ instructiondefs=readushort(f),
+ stackelements=readushort(f),
+ sizeofinstructions=readushort(f),
+ componentelements=readushort(f),
+ componentdepth=readushort(f),
}
else
- fontdata.verticalheader={
- nofmetrics=0,
+ fontdata.maximumprofile={
+ version=version,
+ nofglyphs=0,
}
end
end
end
-readers.maxp=function(f,fontdata,specification)
- if specification.details then
- local datatable=fontdata.tables.maxp
- if datatable then
- setposition(f,datatable.offset)
- local version=readfixed(f)
- local nofglyphs=readushort(f)
- fontdata.nofglyphs=nofglyphs
- if version==0.5 then
- fontdata.maximumprofile={
- version=version,
- nofglyphs=nofglyphs,
- }
- return
- elseif version==1.0 then
- fontdata.maximumprofile={
- version=version,
- nofglyphs=nofglyphs,
- points=readushort(f),
- contours=readushort(f),
- compositepoints=readushort(f),
- compositecontours=readushort(f),
- zones=readushort(f),
- twilightpoints=readushort(f),
- storage=readushort(f),
- functiondefs=readushort(f),
- instructiondefs=readushort(f),
- stackelements=readushort(f),
- sizeofinstructions=readushort(f),
- componentelements=readushort(f),
- componentdepth=readushort(f),
- }
- return
- end
- end
- fontdata.maximumprofile={
- version=version,
- nofglyphs=0,
- }
- end
-end
readers.hmtx=function(f,fontdata,specification)
- if specification.glyphs then
- local datatable=fontdata.tables.hmtx
- if datatable then
- setposition(f,datatable.offset)
- local horizontalheader=fontdata.horizontalheader
- local nofmetrics=horizontalheader.nofmetrics
- local glyphs=fontdata.glyphs
- local nofglyphs=fontdata.nofglyphs
- local width=0
- local leftsidebearing=0
- for i=0,nofmetrics-1 do
- local glyph=glyphs[i]
- width=readshort(f)
- leftsidebearing=readshort(f)
- if width~=0 then
- glyph.width=width
- end
+ local tableoffset=gotodatatable(f,fontdata,"hmtx",specification.glyphs)
+ if tableoffset then
+ local horizontalheader=fontdata.horizontalheader
+ local nofmetrics=horizontalheader.nofmetrics
+ local glyphs=fontdata.glyphs
+ local nofglyphs=fontdata.nofglyphs
+ local width=0
+ local leftsidebearing=0
+ for i=0,nofmetrics-1 do
+ local glyph=glyphs[i]
+ width=readshort(f)
+ leftsidebearing=readshort(f)
+ if width~=0 then
+ glyph.width=width
end
- for i=nofmetrics,nofglyphs-1 do
- local glyph=glyphs[i]
- if width~=0 then
- glyph.width=width
- end
+ end
+ for i=nofmetrics,nofglyphs-1 do
+ local glyph=glyphs[i]
+ if width~=0 then
+ glyph.width=width
end
end
end
end
readers.vmtx=function(f,fontdata,specification)
- if specification.glyphs then
- local datatable=fontdata.tables.vmtx
- if datatable then
- setposition(f,datatable.offset)
- local verticalheader=fontdata.verticalheader
- local nofmetrics=verticalheader.nofmetrics
- local glyphs=fontdata.glyphs
- local nofglyphs=fontdata.nofglyphs
- local vheight=0
- local vdefault=verticalheader.ascender+verticalheader.descender
- local topsidebearing=0
- for i=0,nofmetrics-1 do
- local glyph=glyphs[i]
- vheight=readshort(f)
- topsidebearing=readshort(f)
- if vheight~=0 and vheight~=vdefault then
- glyph.vheight=vheight
- end
+ local tableoffset=gotodatatable(f,fontdata,"vmtx",specification.glyphs)
+ if tableoffset then
+ local verticalheader=fontdata.verticalheader
+ local nofmetrics=verticalheader.nofmetrics
+ local glyphs=fontdata.glyphs
+ local nofglyphs=fontdata.nofglyphs
+ local vheight=0
+ local vdefault=verticalheader.ascender+verticalheader.descender
+ local topsidebearing=0
+ for i=0,nofmetrics-1 do
+ local glyph=glyphs[i]
+ vheight=readshort(f)
+ topsidebearing=readshort(f)
+ if vheight~=0 and vheight~=vdefault then
+ glyph.vheight=vheight
end
- for i=nofmetrics,nofglyphs-1 do
- local glyph=glyphs[i]
- if vheight~=0 and vheight~=vdefault then
- glyph.vheight=vheight
- end
+ end
+ for i=nofmetrics,nofglyphs-1 do
+ local glyph=glyphs[i]
+ if vheight~=0 and vheight~=vdefault then
+ glyph.vheight=vheight
end
end
end
end
readers.vorg=function(f,fontdata,specification)
if specification.glyphs then
- local datatable=fontdata.tables.vorg
- if datatable then
- report("todo: %s","vorg")
- end
end
end
readers.post=function(f,fontdata,specification)
- local datatable=fontdata.tables.post
- if datatable then
- setposition(f,datatable.offset)
+ local tableoffset=gotodatatable(f,fontdata,"post",true)
+ if tableoffset then
local version=readfixed(f)
fontdata.postscript={
version=version,
@@ -8909,7 +8902,7 @@ formatreaders[4]=function(f,fontdata,offset)
elseif startchar==0xFFFF and offset==0 then
elseif offset==0xFFFF then
elseif offset==0 then
- if trace_cmap then
+ if trace_cmap_detail then
report("format 4.%i segment %2i from %C upto %C at index %H",1,segment,startchar,endchar,(startchar+delta)%65536)
end
for unicode=startchar,endchar do
@@ -8941,7 +8934,7 @@ formatreaders[4]=function(f,fontdata,offset)
end
else
local shift=(segment-nofsegments+offset/2)-startchar
- if trace_cmap then
+ if trace_cmap_detail then
report("format 4.%i segment %2i from %C upto %C at index %H",0,segment,startchar,endchar,(startchar+delta)%65536)
end
for unicode=startchar,endchar do
@@ -8989,7 +8982,7 @@ formatreaders[6]=function(f,fontdata,offset)
local count=readushort(f)
local stop=start+count-1
local nofdone=0
- if trace_cmap then
+ if trace_cmap_detail then
report("format 6 from %C to %C",2,start,stop)
end
for unicode=start,stop do
@@ -9022,7 +9015,7 @@ formatreaders[12]=function(f,fontdata,offset)
local first=readulong(f)
local last=readulong(f)
local index=readulong(f)
- if trace_cmap then
+ if trace_cmap_detail then
report("format 12 from %C to %C starts at index %i",first,last,index)
end
for unicode=first,last do
@@ -9061,7 +9054,7 @@ formatreaders[13]=function(f,fontdata,offset)
local last=readulong(f)
local index=readulong(f)
if first<privateoffset then
- if trace_cmap then
+ if trace_cmap_detail then
report("format 13 from %C to %C get index %i",first,last,index)
end
local glyph=glyphs[index]
@@ -9151,75 +9144,81 @@ local function checkcmap(f,fontdata,records,platform,encoding,format)
local p=platforms[platform]
local e=encodings[p]
local n=reader(f,fontdata,data) or 0
- report("cmap checked: platform %i (%s), encoding %i (%s), format %i, new unicodes %i",platform,p,encoding,e and e[encoding] or "?",format,n)
+ if trace_cmap then
+ report("cmap checked: platform %i (%s), encoding %i (%s), format %i, new unicodes %i",platform,p,encoding,e and e[encoding] or "?",format,n)
+ end
return n
end
function readers.cmap(f,fontdata,specification)
- if specification.glyphs then
- local datatable=fontdata.tables.cmap
- if datatable then
- local tableoffset=datatable.offset
- setposition(f,tableoffset)
- local version=readushort(f)
- local noftables=readushort(f)
- local records={}
- local unicodecid=false
- local variantcid=false
- local variants={}
- local duplicates=fontdata.duplicates or {}
- fontdata.duplicates=duplicates
- for i=1,noftables do
- local platform=readushort(f)
- local encoding=readushort(f)
- local offset=readulong(f)
- local record=records[platform]
- if not record then
- records[platform]={
- [encoding]={
- offsets={ offset },
- formats={},
- }
+ local tableoffset=gotodatatable(f,fontdata,"cmap",specification.glyphs)
+ if tableoffset then
+ local version=readushort(f)
+ local noftables=readushort(f)
+ local records={}
+ local unicodecid=false
+ local variantcid=false
+ local variants={}
+ local duplicates=fontdata.duplicates or {}
+ fontdata.duplicates=duplicates
+ for i=1,noftables do
+ local platform=readushort(f)
+ local encoding=readushort(f)
+ local offset=readulong(f)
+ local record=records[platform]
+ if not record then
+ records[platform]={
+ [encoding]={
+ offsets={ offset },
+ formats={},
+ }
+ }
+ else
+ local subtables=record[encoding]
+ if not subtables then
+ record[encoding]={
+ offsets={ offset },
+ formats={},
}
else
- local subtables=record[encoding]
- if not subtables then
- record[encoding]={
- offsets={ offset },
- formats={},
- }
- else
- local offsets=subtables.offsets
- offsets[#offsets+1]=offset
- end
+ local offsets=subtables.offsets
+ offsets[#offsets+1]=offset
end
end
+ end
+ if trace_cmap then
report("found cmaps:")
- for platform,record in sortedhash(records) do
- local p=platforms[platform]
- local e=encodings[p]
- local sp=supported[platform]
- local ps=p or "?"
+ end
+ for platform,record in sortedhash(records) do
+ local p=platforms[platform]
+ local e=encodings[p]
+ local sp=supported[platform]
+ local ps=p or "?"
+ if trace_cmap then
if sp then
report(" platform %i: %s",platform,ps)
else
report(" platform %i: %s (unsupported)",platform,ps)
end
- for encoding,subtables in sortedhash(record) do
- local se=sp and sp[encoding]
- local es=e and e[encoding] or "?"
+ end
+ for encoding,subtables in sortedhash(record) do
+ local se=sp and sp[encoding]
+ local es=e and e[encoding] or "?"
+ if trace_cmap then
if se then
report(" encoding %i: %s",encoding,es)
else
report(" encoding %i: %s (unsupported)",encoding,es)
end
- local offsets=subtables.offsets
- local formats=subtables.formats
- for i=1,#offsets do
- local offset=tableoffset+offsets[i]
- setposition(f,offset)
- formats[readushort(f)]=offset
- end
- record[encoding]=formats
+ end
+ local offsets=subtables.offsets
+ local formats=subtables.formats
+ for i=1,#offsets do
+ local offset=tableoffset+offsets[i]
+ setposition(f,offset)
+ formats[readushort(f)]=offset
+ end
+ record[encoding]=formats
+ if trace_cmap then
local list=sortedkeys(formats)
for i=1,#list do
if not (se and se[list[i]]) then
@@ -9229,25 +9228,25 @@ function readers.cmap(f,fontdata,specification)
report(" formats: % t",list)
end
end
- local ok=false
- for i=1,#sequence do
- local si=sequence[i]
- local sp,se,sf=si[1],si[2],si[3]
- if checkcmap(f,fontdata,records,sp,se,sf)>0 then
- ok=true
- end
- end
- if not ok then
- report("no useable unicode cmap found")
+ end
+ local ok=false
+ for i=1,#sequence do
+ local si=sequence[i]
+ local sp,se,sf=si[1],si[2],si[3]
+ if checkcmap(f,fontdata,records,sp,se,sf)>0 then
+ ok=true
end
- fontdata.cidmaps={
- version=version,
- noftables=noftables,
- records=records,
- }
- else
- fontdata.cidmaps={}
end
+ if not ok then
+ report("no useable unicode cmap found")
+ end
+ fontdata.cidmaps={
+ version=version,
+ noftables=noftables,
+ records=records,
+ }
+ else
+ fontdata.cidmaps={}
end
end
function readers.loca(f,fontdata,specification)
@@ -9276,41 +9275,38 @@ function readers.svg(f,fontdata,specification)
end
end
function readers.kern(f,fontdata,specification)
- if specification.kerns then
- local datatable=fontdata.tables.kern
- if datatable then
- setposition(f,datatable.offset)
+ local tableoffset=gotodatatable(f,fontdata,"kern",specification.kerns)
+ if tableoffset then
+ local version=readushort(f)
+ local noftables=readushort(f)
+ for i=1,noftables do
local version=readushort(f)
- local noftables=readushort(f)
- for i=1,noftables do
- local version=readushort(f)
- local length=readushort(f)
- local coverage=readushort(f)
- local format=bit32.rshift(coverage,8)
- if format==0 then
- local nofpairs=readushort(f)
- local searchrange=readushort(f)
- local entryselector=readushort(f)
- local rangeshift=readushort(f)
- local kerns={}
- local glyphs=fontdata.glyphs
- for i=1,nofpairs do
- local left=readushort(f)
- local right=readushort(f)
- local kern=readfword(f)
- local glyph=glyphs[left]
- local kerns=glyph.kerns
- if kerns then
- kerns[right]=kern
- else
- glyph.kerns={ [right]=kern }
- end
+ local length=readushort(f)
+ local coverage=readushort(f)
+ local format=bit32.rshift(coverage,8)
+ if format==0 then
+ local nofpairs=readushort(f)
+ local searchrange=readushort(f)
+ local entryselector=readushort(f)
+ local rangeshift=readushort(f)
+ local kerns={}
+ local glyphs=fontdata.glyphs
+ for i=1,nofpairs do
+ local left=readushort(f)
+ local right=readushort(f)
+ local kern=readfword(f)
+ local glyph=glyphs[left]
+ local kerns=glyph.kerns
+ if kerns then
+ kerns[right]=kern
+ else
+ glyph.kerns={ [right]=kern }
end
- elseif format==2 then
- report("todo: kern classes")
- else
- report("todo: kerns")
end
+ elseif format==2 then
+ report("todo: kern classes")
+ else
+ report("todo: kerns")
end
end
end
@@ -9335,7 +9331,7 @@ function readers.math(f,fontdata,specification)
reportskippedtable("math")
end
end
-local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo)
+local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo,instancenames)
local fontdata=sub and maindata.subfonts and maindata.subfonts[sub] or maindata
local names=fontdata.names
local info=nil
@@ -9359,6 +9355,25 @@ local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo)
if not familyname then familyname=family end
if not subfamilyname then subfamilyname=subfamily end
end
+ if platformnames then
+ platformnames=fontdata.platformnames
+ end
+ if instancenames then
+ local variabledata=fontdata.variabledata
+ if variabledata then
+ local instances=variabledata and variabledata.instances
+ if instances then
+ instancenames={}
+ for i=1,#instances do
+ instancenames[i]=lower(stripstring(instances[i].subfamily))
+ end
+ else
+ instancenames=nil
+ end
+ else
+ instancenames=nil
+ end
+ end
info={
subfontindex=fontdata.subfontindex or sub or 0,
version=getname(fontdata,"version"),
@@ -9386,7 +9401,8 @@ local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo)
capheight=metrics.capheight,
ascender=metrics.typoascender,
descender=metrics.typodescender,
- platformnames=platformnames and fontdata.platformnames or nil,
+ platformnames=platformnames or nil,
+ instancenames=instancenames or nil,
}
if metricstoo then
local keys={
@@ -9440,6 +9456,7 @@ local function loadtables(f,specification,offset)
entryselector=readushort(f),
rangeshift=readushort(f),
tables=tables,
+ foundtables=false,
}
for i=1,fontdata.noftables do
local tag=lower(stripstring(readstring(f,4)))
@@ -9455,7 +9472,8 @@ local function loadtables(f,specification,offset)
length=length,
}
end
- if tables.cff then
+ fontdata.foundtables=sortedkeys(tables)
+ if tables.cff or tables.cff2 then
fontdata.format="opentype"
else
fontdata.format="truetype"
@@ -9473,17 +9491,24 @@ local function prepareglyps(fontdata)
fontdata.glyphs=glyphs
fontdata.mapping={}
end
-local function readtable(tag,f,fontdata,specification)
+local function readtable(tag,f,fontdata,specification,...)
local reader=readers[tag]
if reader then
- reader(f,fontdata,specification)
+ reader(f,fontdata,specification,...)
end
end
+local variablefonts_supported=context and true or false
local function readdata(f,offset,specification)
local fontdata=loadtables(f,specification,offset)
if specification.glyphs then
prepareglyps(fontdata)
end
+ if not variablefonts_supported then
+ specification.instance=nil
+ specification.variable=nil
+ specification.factors=nil
+ end
+ fontdata.temporary={}
readtable("name",f,fontdata,specification)
local askedname=specification.askedname
if askedname then
@@ -9494,6 +9519,30 @@ local function readdata(f,offset,specification)
return
end
end
+ readtable("stat",f,fontdata,specification)
+ readtable("avar",f,fontdata,specification)
+ readtable("fvar",f,fontdata,specification)
+ if variablefonts_supported then
+ if not specification.factors then
+ local instance=specification.instance
+ if type(instance)=="string" then
+ local factors=helpers.getfactors(fontdata,instance)
+ specification.factors=factors
+ fontdata.factors=factors
+ fontdata.instance=instance
+ report("user instance: %s, factors: % t",instance,factors)
+ end
+ end
+ if not fontdata.factors then
+ if fontdata.variabledata then
+ local factors=helpers.getfactors(fontdata,true)
+ specification.factors=factors
+ fontdata.factors=factors
+ fontdata.instance=instance
+ report("font instance: %s, factors: % t",instance,factors)
+ end
+ end
+ end
readtable("os/2",f,fontdata,specification)
readtable("head",f,fontdata,specification)
readtable("maxp",f,fontdata,specification)
@@ -9503,23 +9552,22 @@ local function readdata(f,offset,specification)
readtable("vmtx",f,fontdata,specification)
readtable("vorg",f,fontdata,specification)
readtable("post",f,fontdata,specification)
+ readtable("mvar",f,fontdata,specification)
+ readtable("hvar",f,fontdata,specification)
+ readtable("vvar",f,fontdata,specification)
+ readtable("gdef",f,fontdata,specification)
readtable("cff",f,fontdata,specification)
+ readtable("cff2",f,fontdata,specification)
readtable("cmap",f,fontdata,specification)
- readtable("loca",f,fontdata,specification)
- readtable("glyf",f,fontdata,specification)
+ readtable("loca",f,fontdata,specification)
+ readtable("glyf",f,fontdata,specification)
readtable("colr",f,fontdata,specification)
readtable("cpal",f,fontdata,specification)
readtable("svg",f,fontdata,specification)
readtable("kern",f,fontdata,specification)
- readtable("gdef",f,fontdata,specification)
readtable("gsub",f,fontdata,specification)
readtable("gpos",f,fontdata,specification)
readtable("math",f,fontdata,specification)
- readtable("fvar",f,fontdata,specification)
- readtable("hvar",f,fontdata,specification)
- readtable("vvar",f,fontdata,specification)
- readtable("mvar",f,fontdata,specification)
- readtable("vorg",f,fontdata,specification)
fontdata.locations=nil
fontdata.tables=nil
fontdata.cidmaps=nil
@@ -9596,7 +9644,7 @@ local function loadfontdata(specification)
return fontdata or {}
end
end
-local function loadfont(specification,n)
+local function loadfont(specification,n,instance)
if type(specification)=="string" then
specification={
filename=specification,
@@ -9610,6 +9658,7 @@ local function loadfont(specification,n)
lookups=true,
subfont=n or true,
tounicode=false,
+ instance=instance
}
end
if specification.shapes or specification.lookups or specification.kerns then
@@ -9624,6 +9673,10 @@ local function loadfont(specification,n)
if specification.platformnames then
specification.platformnames=true
end
+ if specification.instance or instance then
+ specification.variable=true
+ specification.instance=specification.instance or instance
+ end
local function message(str)
report("fatal error in file %a: %s\n%s",specification.filename,str,debug.traceback())
end
@@ -9632,11 +9685,14 @@ local function loadfont(specification,n)
return result
end
end
-function readers.loadshapes(filename,n)
+function readers.loadshapes(filename,n,instance,streams)
local fontdata=loadfont {
filename=filename,
shapes=true,
+ streams=streams,
+ variable=true,
subfont=n,
+ instance=instance,
}
if fontdata then
for k,v in next,fontdata.glyphs do
@@ -9657,7 +9713,7 @@ function readers.loadshapes(filename,n)
units=0,
}
end
-function readers.loadfont(filename,n)
+function readers.loadfont(filename,n,instance)
local fontdata=loadfont {
filename=filename,
glyphs=true,
@@ -9665,6 +9721,7 @@ function readers.loadfont(filename,n)
lookups=true,
variable=true,
subfont=n,
+ instance=instance,
}
if fontdata then
return {
@@ -9676,11 +9733,13 @@ function readers.loadfont(filename,n)
descriptions=fontdata.descriptions,
format=fontdata.format,
goodies={},
- metadata=getinfo(fontdata,n,false,false,true),
+ metadata=getinfo(fontdata,n,false,false,true,true),
properties={
hasitalics=fontdata.hasitalics or false,
maxcolorclass=fontdata.maxcolorclass,
hascolor=fontdata.hascolor or false,
+ instance=fontdata.instance,
+ factors=fontdata.factors,
},
resources={
filename=filename,
@@ -9698,7 +9757,8 @@ function readers.loadfont(filename,n)
mathconstants=fontdata.mathconstants,
colorpalettes=fontdata.colorpalettes,
svgshapes=fontdata.svgshapes,
- variable=fontdata.variable,
+ variabledata=fontdata.variabledata,
+ foundtables=fontdata.foundtables,
},
}
end
@@ -9707,6 +9767,7 @@ function readers.getinfo(filename,specification)
local subfont=nil
local platformnames=false
local rawfamilynames=false
+ local instancenames=true
if type(specification)=="table" then
subfont=tonumber(specification.subfont)
platformnames=specification.platformnames
@@ -9718,19 +9779,20 @@ function readers.getinfo(filename,specification)
filename=filename,
details=true,
platformnames=platformnames,
+ instancenames=true,
}
if fontdata then
local subfonts=fontdata.subfonts
if not subfonts then
- return getinfo(fontdata,nil,platformnames,rawfamilynames)
+ return getinfo(fontdata,nil,platformnames,rawfamilynames,false,instancenames)
elseif not subfont then
local info={}
for i=1,#subfonts do
- info[i]=getinfo(fontdata,i,platformnames,rawfamilynames)
+ info[i]=getinfo(fontdata,i,platformnames,rawfamilynames,false,instancenames)
end
return info
elseif subfont>=1 and subfont<=#subfonts then
- return getinfo(fontdata,subfont,platformnames,rawfamilynames)
+ return getinfo(fontdata,subfont,platformnames,rawfamilynames,false,instancenames)
else
return {
filename=filename,
@@ -9789,7 +9851,7 @@ if not modules then modules={} end modules ['font-cff']={
license="see context related readme files"
}
local next,type,tonumber=next,type,tonumber
-local byte,gmatch=string.byte,string.gmatch
+local byte,char,gmatch=string.byte,string.char,string.gmatch
local concat,remove=table.concat,table.remove
local floor,abs,round,ceil,min,max=math.floor,math.abs,math.round,math.ceil,math.min,math.max
local P,C,R,S,C,Cs,Ct=lpeg.P,lpeg.C,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Ct
@@ -9814,6 +9876,8 @@ local parsecharstring
local parsecharstrings
local resetcharstrings
local parseprivates
+local startparsing
+local stopparsing
local defaultstrings={ [0]=
".notdef","space","exclam","quotedbl","numbersign","dollar","percent",
"ampersand","quoteright","parenleft","parenright","asterisk","plus",
@@ -9889,18 +9953,24 @@ local cffreaders={
}
local function readheader(f)
local offset=getposition(f)
+ local major=readbyte(f)
local header={
offset=offset,
- major=readbyte(f),
+ major=major,
minor=readbyte(f),
size=readbyte(f),
- osize=readbyte(f),
}
+ if major==1 then
+ header.dsize=readbyte(f)
+ elseif major==2 then
+ header.dsize=readushort(f)
+ else
+ end
setposition(f,offset+header.size)
return header
end
-local function readlengths(f)
- local count=readushort(f)
+local function readlengths(f,longcount)
+ local count=longcount and readulong(f) or readushort(f)
if count==0 then
return {}
end
@@ -9914,7 +9984,12 @@ local function readlengths(f)
local previous=read(f)
for i=1,count do
local offset=read(f)
- lengths[i]=offset-previous
+ local length=offset-previous
+ if length<0 then
+ report("bad offset: %i",length)
+ length=0
+ end
+ lengths[i]=length
previous=offset
end
return lengths
@@ -9979,7 +10054,7 @@ do
end+P("\16")/function()
result.encoding=stack[top]
top=0
- end+P("\17")/function()
+ end+P("\17")/function()
result.charstrings=stack[top]
top=0
end+P("\18")/function()
@@ -9990,10 +10065,20 @@ do
top=0
end+P("\19")/function()
result.subroutines=stack[top]
+ top=0
end+P("\20")/function()
result.defaultwidthx=stack[top]
+ top=0
end+P("\21")/function()
result.nominalwidthx=stack[top]
+ top=0
+ end
++P("\24")/function()
+ result.vstore=stack[top]
+ top=0
+ end+P("\25")/function()
+ result.maxstack=stack[top]
+ top=0
end
local p_double=P("\12")*(
P("\00")/function()
@@ -10017,7 +10102,7 @@ do
end+P("\06")/function()
result.charstringtype=stack[top]
top=0
- end+P("\07")/function()
+ end+P("\07")/function()
result.fontmatrix={ unpack(stack,1,6) }
top=0
end+P("\08")/function()
@@ -10055,10 +10140,10 @@ do
end+P("\35")/function()
result.cid.uidbase=stack[top]
top=0
- end+P("\36")/function()
+ end+P("\36")/function()
result.cid.fdarray=stack[top]
top=0
- end+P("\37")/function()
+ end+P("\37")/function()
result.cid.fdselect=stack[top]
top=0
end+P("\38")/function()
@@ -10123,12 +10208,12 @@ do
local p_dictionary=(
p_byte+p_positive+p_negative+p_short+p_long+p_nibbles+p_single+p_double+p_unsupported
)^1
- parsedictionaries=function(data,dictionaries)
+ parsedictionaries=function(data,dictionaries,what)
stack={}
strings=data.strings
for i=1,#dictionaries do
top=0
- result={
+ result=what=="cff" and {
monospaced=false,
italicangle=0,
underlineposition=-100,
@@ -10146,6 +10231,12 @@ do
fonttype=0,
count=8720,
}
+ } or {
+ charstringtype=2,
+ charset=0,
+ vstore=0,
+ cid={
+ },
}
lpegmatch(p_dictionary,dictionaries[i])
dictionaries[i]=result
@@ -10187,6 +10278,9 @@ do
local stems=0
local globalbias=0
local localbias=0
+ local nominalwidth=0
+ local defaultwidth=0
+ local charset=false
local globals=false
local locals=false
local depth=1
@@ -10197,6 +10291,12 @@ do
local checked=false
local keepcurve=false
local version=2
+ local regions=false
+ local nofregions=0
+ local region=false
+ local factors=false
+ local axis=false
+ local vsindex=0
local function showstate(where)
report("%w%-10s : [%s] n=%i",depth*2,where,concat(stack," ",1,top),top)
end
@@ -10207,7 +10307,7 @@ do
report("%w%-10s : %s",depth*2,where,tostring(value))
end
end
- local function moveto()
+ local function xymoveto()
if keepcurve then
r=r+1
result[r]={ x,y,"m" }
@@ -10257,7 +10357,14 @@ do
ymin=y
end
end
- local function lineto()
+ local function moveto()
+ if trace_charstrings then
+ showstate("moveto")
+ end
+ top=0
+ xymoveto()
+ end
+ local function xylineto()
if keepcurve then
r=r+1
result[r]={ x,y,"l" }
@@ -10307,7 +10414,10 @@ do
ymin=y
end
end
- local function curveto(x1,y1,x2,y2,x3,y3)
+ local function xycurveto(x1,y1,x2,y2,x3,y3)
+ if trace_charstrings then
+ showstate("curveto")
+ end
if keepcurve then
r=r+1
result[r]={ x1,y1,x2,y2,x3,y3,"c" }
@@ -10332,7 +10442,7 @@ do
if top>2 then
width=stack[1]
if trace_charstrings then
- showvalue("width",width)
+ showvalue("backtrack width",width)
end
else
width=true
@@ -10344,14 +10454,14 @@ do
x=x+stack[top-1]
y=y+stack[top]
top=0
- moveto()
+ xymoveto()
end
local function hmoveto()
if not width then
if top>1 then
width=stack[1]
if trace_charstrings then
- showvalue("width",width)
+ showvalue("backtrack width",width)
end
else
width=true
@@ -10369,7 +10479,7 @@ do
if top>1 then
width=stack[1]
if trace_charstrings then
- showvalue("width",width)
+ showvalue("backtrack width",width)
end
else
width=true
@@ -10389,7 +10499,7 @@ do
for i=1,top,2 do
x=x+stack[i]
y=y+stack[i+1]
- lineto()
+ xylineto()
end
top=0
end
@@ -10450,7 +10560,7 @@ do
local by=ay+stack[i+3]
x=bx+stack[i+4]
y=by+stack[i+5]
- curveto(ax,ay,bx,by,x,y)
+ xycurveto(ax,ay,bx,by,x,y)
end
top=0
end
@@ -10470,7 +10580,7 @@ do
local by=ay+stack[i+2]
x=bx+stack[i+3]
y=by
- curveto(ax,ay,bx,by,x,y)
+ xycurveto(ax,ay,bx,by,x,y)
end
top=0
end
@@ -10491,7 +10601,7 @@ do
local by=ay+stack[i+2]
x=bx
y=by+stack[i+3]
- curveto(ax,ay,bx,by,x,y)
+ xycurveto(ax,ay,bx,by,x,y)
d=0
end
top=0
@@ -10528,7 +10638,7 @@ do
end
swap=true
end
- curveto(ax,ay,bx,by,x,y)
+ xycurveto(ax,ay,bx,by,x,y)
end
top=0
end
@@ -10555,11 +10665,11 @@ do
local by=ay+stack[i+3]
x=bx+stack[i+4]
y=by+stack[i+5]
- curveto(ax,ay,bx,by,x,y)
+ xycurveto(ax,ay,bx,by,x,y)
end
x=x+stack[top-1]
y=y+stack[top]
- lineto()
+ xylineto()
top=0
end
local function rlinecurve()
@@ -10570,7 +10680,7 @@ do
for i=1,top-6,2 do
x=x+stack[i]
y=y+stack[i+1]
- lineto()
+ xylineto()
end
end
local ax=x+stack[top-5]
@@ -10579,7 +10689,7 @@ do
local by=ay+stack[top-2]
x=bx+stack[top-1]
y=by+stack[top]
- curveto(ax,ay,bx,by,x,y)
+ xycurveto(ax,ay,bx,by,x,y)
top=0
end
local function flex()
@@ -10592,14 +10702,14 @@ do
local by=ay+stack[4]
local cx=bx+stack[5]
local cy=by+stack[6]
- curveto(ax,ay,bx,by,cx,cy)
+ xycurveto(ax,ay,bx,by,cx,cy)
local dx=cx+stack[7]
local dy=cy+stack[8]
local ex=dx+stack[9]
local ey=dy+stack[10]
x=ex+stack[11]
y=ey+stack[12]
- curveto(dx,dy,ex,ey,x,y)
+ xycurveto(dx,dy,ex,ey,x,y)
top=0
end
local function hflex()
@@ -10612,13 +10722,13 @@ do
local by=ay+stack[3]
local cx=bx+stack[4]
local cy=by
- curveto(ax,ay,bx,by,cx,cy)
+ xycurveto(ax,ay,bx,by,cx,cy)
local dx=cx+stack[5]
local dy=by
local ex=dx+stack[6]
local ey=y
x=ex+stack[7]
- curveto(dx,dy,ex,ey,x,y)
+ xycurveto(dx,dy,ex,ey,x,y)
top=0
end
local function hflex1()
@@ -10631,13 +10741,13 @@ do
local by=ay+stack[4]
local cx=bx+stack[5]
local cy=by
- curveto(ax,ay,bx,by,cx,cy)
+ xycurveto(ax,ay,bx,by,cx,cy)
local dx=cx+stack[6]
local dy=by
local ex=dx+stack[7]
local ey=dy+stack[8]
x=ex+stack[9]
- curveto(dx,dy,ex,ey,x,y)
+ xycurveto(dx,dy,ex,ey,x,y)
top=0
end
local function flex1()
@@ -10650,7 +10760,7 @@ do
local by=ay+stack[4]
local cx=bx+stack[5]
local cy=by+stack[6]
- curveto(ax,ay,bx,by,cx,cy)
+ xycurveto(ax,ay,bx,by,cx,cy)
local dx=cx+stack[7]
local dy=cy+stack[8]
local ex=dx+stack[9]
@@ -10660,7 +10770,7 @@ do
else
y=ey+stack[11]
end
- curveto(dx,dy,ex,ey,x,y)
+ xycurveto(dx,dy,ex,ey,x,y)
top=0
end
local function getstem()
@@ -10795,6 +10905,92 @@ do
end
top=0
end
+ local reginit=false
+ local function updateregions(n)
+ if regions then
+ local current=regions[n] or regions[1]
+ nofregions=#current
+ if axis and n~=reginit then
+ factors={}
+ for i=1,nofregions do
+ local region=current[i]
+ local s=1
+ for j=1,#axis do
+ local f=axis[j]
+ local r=region[j]
+ local start=r.start
+ local peak=r.peak
+ local stop=r.stop
+ if start>peak or peak>stop then
+ elseif start<0 and stop>0 and peak~=0 then
+ elseif peak==0 then
+ elseif f<start or f>stop then
+ s=0
+ break
+ elseif f<peak then
+ s=s*(f-start)/(peak-start)
+ elseif f>peak then
+ s=s*(stop-f)/(stop-peak)
+ else
+ end
+ end
+ factors[i]=s
+ end
+ end
+ end
+ reginit=n
+ end
+ local function setvsindex()
+ local vsindex=stack[top]
+ if trace_charstrings then
+ showstate(formatters["vsindex %i"](vsindex))
+ end
+ updateregions(vsindex)
+ top=top-1
+ end
+ local function blend()
+ local n=stack[top]
+ top=top-1
+ if axis then
+ if trace_charstrings then
+ local t=top-nofregions*n
+ local m=t-n
+ for i=1,n do
+ local k=m+i
+ local d=m+n+(i-1)*nofregions
+ local old=stack[k]
+ local new=old
+ for r=1,nofregions do
+ new=new+stack[d+r]*factors[r]
+ end
+ stack[k]=new
+ showstate(formatters["blend %i of %i: %s -> %s"](i,n,old,new))
+ end
+ top=t
+ elseif n==1 then
+ top=top-nofregions
+ local v=stack[top]
+ for r=1,nofregions do
+ v=v+stack[top+r]*factors[r]
+ end
+ stack[top]=v
+ else
+ top=top-nofregions*n
+ local d=top
+ local k=top-n
+ for i=1,n do
+ k=k+1
+ local v=stack[k]
+ for r=1,nofregions do
+ v=v+stack[d+r]*factors[r]
+ end
+ stack[k]=v
+ d=d+nofregions
+ end
+ end
+ else
+ end
+ end
local actions={ [0]=unsupported,
getstem,
unsupported,
@@ -10810,8 +11006,8 @@ do
unsupported,
hsbw,
unsupported,
- unsupported,
- unsupported,
+ setvsindex,
+ blend,
unsupported,
getstem,
getmask,
@@ -10843,6 +11039,78 @@ do
[036]=hflex1,
[037]=flex1,
}
+ local c_endchar=char(14)
+ local passon do
+ local rshift=bit32.rshift
+ local band=bit32.band
+ local round=math.round
+ local encode=table.setmetatableindex(function(t,i)
+ for i=-2048,-1130 do
+ t[i]=char(28,band(rshift(i,8),0xFF),band(i,0xFF))
+ end
+ for i=-1131,-108 do
+ local v=0xFB00-i-108
+ t[i]=char(band(rshift(v,8),0xFF),band(v,0xFF))
+ end
+ for i=-107,107 do
+ t[i]=char(i+139)
+ end
+ for i=108,1131 do
+ local v=0xF700+i-108
+ t[i]=char(band(rshift(v,8),0xFF),band(v,0xFF))
+ end
+ for i=1132,2048 do
+ t[i]=char(28,band(rshift(i,8),0xFF),band(i,0xFF))
+ end
+ return t[i]
+ end)
+ local function setvsindex()
+ local vsindex=stack[top]
+ updateregions(vsindex)
+ top=top-1
+ end
+ local function blend()
+ local n=stack[top]
+ top=top-1
+ if not axis then
+ elseif n==1 then
+ top=top-nofregions
+ local v=stack[top]
+ for r=1,nofregions do
+ v=v+stack[top+r]*factors[r]
+ end
+ stack[top]=round(v)
+ else
+ top=top-nofregions*n
+ local d=top
+ local k=top-n
+ for i=1,n do
+ k=k+1
+ local v=stack[k]
+ for r=1,nofregions do
+ v=v+stack[d+r]*factors[r]
+ end
+ stack[k]=round(v)
+ d=d+nofregions
+ end
+ end
+ end
+ passon=function(operation)
+ if operation==15 then
+ setvsindex()
+ elseif operation==16 then
+ blend()
+ else
+ for i=1,top do
+ r=r+1
+ result[r]=encode[stack[i]]
+ end
+ r=r+1
+ result[r]=char(operation)
+ top=0
+ end
+ end
+ end
local process
local function call(scope,list,bias)
depth=depth+1
@@ -10865,6 +11133,7 @@ do
end
depth=depth-1
end
+ local justpass=false
process=function(tab)
local i=1
local n=#tab
@@ -10902,7 +11171,7 @@ do
stack[top]=n
end
i=i+3
- elseif t==11 then
+ elseif t==11 then
if trace_charstrings then
showstate("return")
end
@@ -10940,6 +11209,9 @@ do
top=0
end
i=i+1
+ elseif justpass then
+ passon(t)
+ i=i+1
else
local a=actions[t]
if a then
@@ -10969,89 +11241,7 @@ do
((l<1240 and 107) or (l<33900 and 1131) or 32768)+1
end
end
- parsecharstrings=function(data,glyphs,doshapes,tversion)
- local dictionary=data.dictionaries[1]
- local charstrings=dictionary.charstrings
- local charset=dictionary.charset
- local private=dictionary.private or { data={} }
- keepcurve=doshapes
- version=tversion
- stack={}
- glyphs=glyphs or {}
- strings=data.strings
- globals=data.routines or {}
- locals=dictionary.subroutines or {}
- globalbias,localbias=setbias(globals,locals)
- local nominalwidth=private.data.nominalwidthx or 0
- local defaultwidth=private.data.defaultwidthx or 0
- for i=1,#charstrings do
- local tab=bytetable(charstrings[i])
- local index=i-1
- x=0
- y=0
- width=false
- r=0
- top=0
- stems=0
- result={}
- xmin=0
- xmax=0
- ymin=0
- ymax=0
- checked=false
- if trace_charstrings then
- report("glyph: %i",index)
- report("data: % t",tab)
- end
- process(tab)
- local boundingbox={ round(xmin),round(ymin),round(xmax),round(ymax) }
- if width==true or width==false then
- width=defaultwidth
- else
- width=nominalwidth+width
- end
- local glyph=glyphs[index]
- if glyph then
- glyph.segments=doshapes~=false and result or nil
- glyph.boundingbox=boundingbox
- if not glyph.width then
- glyph.width=width
- end
- if charset and not glyph.name then
- glyph.name=charset[index]
- end
- elseif doshapes then
- glyphs[index]={
- segments=result,
- boundingbox=boundingbox,
- width=width,
- name=charset[index],
- }
- else
- glyphs[index]={
- boundingbox=boundingbox,
- width=width,
- name=charset[index],
- }
- end
- if trace_charstrings then
- report("width: %s",tostring(width))
- report("boundingbox: % t",boundingbox)
- end
- charstrings[i]=nil
- end
- return glyphs
- end
- parsecharstring=function(data,dictionary,tab,glyphs,index,doshapes,tversion)
- local private=dictionary.private
- keepcurve=doshapes
- version=tversion
- strings=data.strings
- locals=dictionary.subroutines or {}
- globals=data.routines or {}
- globalbias,localbias=setbias(globals,locals)
- local nominalwidth=private and private.data.nominalwidthx or 0
- local defaultwidth=private and private.data.defaultwidthx or 0
+ local function processshape(tab,index)
tab=bytetable(tab)
x=0
y=0
@@ -11059,7 +11249,7 @@ do
r=0
top=0
stems=0
- result={}
+ result={}
xmin=0
xmax=0
ymin=0
@@ -11067,19 +11257,33 @@ do
checked=false
if trace_charstrings then
report("glyph: %i",index)
- report("data: % t",tab)
+ report("data : % t",tab)
end
+ updateregions(vsindex)
process(tab)
- local boundingbox={ xmin,ymin,xmax,ymax }
+ local boundingbox={
+ round(xmin),
+ round(ymin),
+ round(xmax),
+ round(ymax),
+ }
if width==true or width==false then
width=defaultwidth
else
width=nominalwidth+width
end
- index=index-1
local glyph=glyphs[index]
- if glyph then
- glyph.segments=doshapes~=false and result or nil
+ if justpass then
+ r=r+1
+ result[r]=c_endchar
+ local stream=concat(result)
+ if glyph then
+ glyph.stream=stream
+ else
+ glyphs[index]={ stream=stream }
+ end
+ elseif glyph then
+ glyph.segments=keepcurve~=false and result or nil
glyph.boundingbox=boundingbox
if not glyph.width then
glyph.width=width
@@ -11087,29 +11291,87 @@ do
if charset and not glyph.name then
glyph.name=charset[index]
end
- elseif doshapes then
+ elseif keepcurve then
glyphs[index]={
segments=result,
boundingbox=boundingbox,
width=width,
- name=charset[index],
+ name=charset and charset[index] or nil,
}
else
glyphs[index]={
boundingbox=boundingbox,
width=width,
- name=charset[index],
+ name=charset and charset[index] or nil,
}
end
if trace_charstrings then
- report("width: %s",tostring(width))
+ report("width : %s",tostring(width))
report("boundingbox: % t",boundingbox)
end
end
- resetcharstrings=function()
+ startparsing=function(fontdata,data,streams)
+ reginit=false
+ axis=false
+ regions=data.regions
+ justpass=streams==true
+ if regions then
+ regions={ regions }
+ axis=data.factors or false
+ end
+ end
+ stopparsing=function(fontdata,data)
+ stack={}
+ glyphs=false
result={}
top=0
- stack={}
+ locals=false
+ globals=false
+ strings=false
+ end
+ local function setwidths(private)
+ if not private then
+ return 0,0
+ end
+ local privatedata=private.data
+ if not privatedata then
+ return 0,0
+ end
+ return privatedata.nominalwidthx or 0,privatedata.defaultwidthx or 0
+ end
+ parsecharstrings=function(fontdata,data,glphs,doshapes,tversion,streams)
+ local dictionary=data.dictionaries[1]
+ local charstrings=dictionary.charstrings
+ keepcurve=doshapes
+ version=tversion
+ strings=data.strings
+ globals=data.routines or {}
+ locals=dictionary.subroutines or {}
+ charset=dictionary.charset
+ vsindex=dictionary.vsindex or 0
+ glyphs=glphs or {}
+ globalbias,localbias=setbias(globals,locals)
+ nominalwidth,defaultwidth=setwidths(dictionary.private)
+ startparsing(fontdata,data,streams)
+ for index=1,#charstrings do
+ processshape(charstrings[index],index-1)
+ charstrings[index]=nil
+ end
+ stopparsing(fontdata,data)
+ return glyphs
+ end
+ parsecharstring=function(fontdata,data,dictionary,tab,glphs,index,doshapes,tversion)
+ keepcurve=doshapes
+ version=tversion
+ strings=data.strings
+ globals=data.routines or {}
+ locals=dictionary.subroutines or {}
+ charset=false
+ vsindex=dictionary.vsindex or 0
+ glyphs=glphs or {}
+ globalbias,localbias=setbias(globals,locals)
+ nominalwidth,defaultwidth=setwidths(dictionary.private)
+ processshape(tab,index-1)
end
end
local function readglobals(f,data)
@@ -11127,7 +11389,7 @@ local function readcharsets(f,data,dictionary)
local strings=data.strings
local nofglyphs=data.nofglyphs
local charsetoffset=dictionary.charset
- if charsetoffset~=0 then
+ if charsetoffset and charsetoffset~=0 then
setposition(f,header.offset+charsetoffset)
local format=readbyte(f)
local charset={ [0]=".notdef" }
@@ -11153,6 +11415,9 @@ local function readcharsets(f,data,dictionary)
else
report("cff parser: unsupported charset format %a",format)
end
+ else
+ dictionary.nocharset=true
+ dictionary.charset=nil
end
end
local function readprivates(f,data)
@@ -11184,15 +11449,16 @@ local function readlocals(f,data,dictionary)
dictionary.subroutines={}
end
end
-local function readcharstrings(f,data)
+local function readcharstrings(f,data,what)
local header=data.header
local dictionaries=data.dictionaries
local dictionary=dictionaries[1]
- local type=dictionary.charstringtype
+ local stringtype=dictionary.charstringtype
local offset=dictionary.charstrings
- if type==2 then
+ if type(offset)~="number" then
+ elseif stringtype==2 then
setposition(f,header.offset+offset)
- local charstrings=readlengths(f)
+ local charstrings=readlengths(f,what=="cff2")
local nofglyphs=#charstrings
for i=1,nofglyphs do
charstrings[i]=readstring(f,charstrings[i])
@@ -11200,7 +11466,7 @@ local function readcharstrings(f,data)
data.nofglyphs=nofglyphs
dictionary.charstrings=charstrings
else
- report("unsupported charstr type %i",type)
+ report("unsupported charstr type %i",stringtype)
data.nofglyphs=0
dictionary.charstrings={}
end
@@ -11218,29 +11484,34 @@ local function readcidprivates(f,data)
end
parseprivates(data,dictionaries)
end
-local function readnoselect(f,data,glyphs,doshapes,version)
+readers.parsecharstrings=parsecharstrings
+local function readnoselect(f,fontdata,data,glyphs,doshapes,version,streams)
local dictionaries=data.dictionaries
local dictionary=dictionaries[1]
readglobals(f,data)
- readcharstrings(f,data)
- readencodings(f,data)
- readcharsets(f,data,dictionary)
+ readcharstrings(f,data,version)
+ if version~="cff2" then
+ readencodings(f,data)
+ readcharsets(f,data,dictionary)
+ end
readprivates(f,data)
parseprivates(data,data.dictionaries)
readlocals(f,data,dictionary)
- parsecharstrings(data,glyphs,doshapes,version)
- resetcharstrings()
+ startparsing(fontdata,data,streams)
+ parsecharstrings(fontdata,data,glyphs,doshapes,version,streams)
+ stopparsing(fontdata,data)
end
-readers.parsecharstrings=parsecharstrings
-local function readfdselect(f,data,glyphs,doshapes,version)
+local function readfdselect(f,fontdata,data,glyphs,doshapes,version,streams)
local header=data.header
local dictionaries=data.dictionaries
local dictionary=dictionaries[1]
local cid=dictionary.cid
local cidselect=cid and cid.fdselect
readglobals(f,data)
- readcharstrings(f,data)
- readencodings(f,data)
+ readcharstrings(f,data,version)
+ if version~="cff2" then
+ readencodings(f,data)
+ end
local charstrings=dictionary.charstrings
local fdindex={}
local nofglyphs=data.nofglyphs
@@ -11289,69 +11560,127 @@ local function readfdselect(f,data,glyphs,doshapes,version)
for i=1,#dictionaries do
readlocals(f,data,dictionaries[i])
end
+ startparsing(fontdata,data,streams)
for i=1,#charstrings do
- parsecharstring(data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version)
+ parsecharstring(fontdata,data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version)
charstrings[i]=nil
end
- resetcharstrings()
+ stopparsing(fontdata,data)
end
end
+local gotodatatable=readers.helpers.gotodatatable
+local function cleanup(data,dictionaries)
+end
function readers.cff(f,fontdata,specification)
- if specification.details then
- local datatable=fontdata.tables.cff
- if datatable then
- local offset=datatable.offset
- local glyphs=fontdata.glyphs
- if not f then
- report("invalid filehandle")
- return
- end
- if offset then
- setposition(f,offset)
- end
- local header=readheader(f)
- if header.major>1 then
- report("version mismatch")
- return
- end
- local names=readfontnames(f)
- local dictionaries=readtopdictionaries(f)
- local strings=readstrings(f)
- local data={
- header=header,
- names=names,
- dictionaries=dictionaries,
- strings=strings,
- nofglyphs=fontdata.nofglyphs,
- }
- parsedictionaries(data,data.dictionaries)
- local d=dictionaries[1]
- local c=d.cid
- fontdata.cffinfo={
- familynamename=d.familyname,
- fullname=d.fullname,
- boundingbox=d.boundingbox,
- weight=d.weight,
- italicangle=d.italicangle,
- underlineposition=d.underlineposition,
- underlinethickness=d.underlinethickness,
- monospaced=d.monospaced,
- }
- fontdata.cidinfo=c and {
- registry=c.registry,
- ordering=c.ordering,
- supplement=c.supplement,
- }
- if not specification.glyphs then
+ local tableoffset=gotodatatable(f,fontdata,"cff",specification.details)
+ if tableoffset then
+ local header=readheader(f)
+ if header.major~=1 then
+ report("only version %s is supported for table %a",1,"cff")
+ return
+ end
+ local glyphs=fontdata.glyphs
+ local names=readfontnames(f)
+ local dictionaries=readtopdictionaries(f)
+ local strings=readstrings(f)
+ local data={
+ header=header,
+ names=names,
+ dictionaries=dictionaries,
+ strings=strings,
+ nofglyphs=fontdata.nofglyphs,
+ }
+ parsedictionaries(data,dictionaries,"cff")
+ local dic=dictionaries[1]
+ local cid=dic.cid
+ fontdata.cffinfo={
+ familynamename=dic.familyname,
+ fullname=dic.fullname,
+ boundingbox=dic.boundingbox,
+ weight=dic.weight,
+ italicangle=dic.italicangle,
+ underlineposition=dic.underlineposition,
+ underlinethickness=dic.underlinethickness,
+ monospaced=dic.monospaced,
+ }
+ fontdata.cidinfo=cid and {
+ registry=cid.registry,
+ ordering=cid.ordering,
+ supplement=cid.supplement,
+ }
+ if specification.glyphs then
+ local all=specification.shapes or false
+ if cid and cid.fdselect then
+ readfdselect(f,fontdata,data,glyphs,all,"cff")
else
- local cid=d.cid
- if cid and cid.fdselect then
- readfdselect(f,data,glyphs,specification.shapes or false)
- else
- readnoselect(f,data,glyphs,specification.shapes or false)
- end
+ readnoselect(f,fontdata,data,glyphs,all,"cff")
end
end
+ cleanup(data,dictionaries)
+ end
+end
+function readers.cff2(f,fontdata,specification)
+ local tableoffset=gotodatatable(f,fontdata,"cff2",specification.glyphs)
+ if tableoffset then
+ local header=readheader(f)
+ if header.major~=2 then
+ report("only version %s is supported for table %a",2,"cff2")
+ return
+ end
+ local glyphs=fontdata.glyphs
+ local dictionaries={ readstring(f,header.dsize) }
+ local data={
+ header=header,
+ dictionaries=dictionaries,
+ nofglyphs=fontdata.nofglyphs,
+ }
+ parsedictionaries(data,dictionaries,"cff2")
+ local storeoffset=dictionaries[1].vstore+data.header.offset+2
+ local regions,deltas=readers.helpers.readvariationdata(f,storeoffset,factors)
+ data.regions=regions
+ data.deltas=deltas
+ data.factors=specification.factors
+ local cid=data.dictionaries[1].cid
+ local all=specification.shapes or false
+ if cid and cid.fdselect then
+ readfdselect(f,fontdata,data,glyphs,all,"cff2",specification.streams)
+ else
+ readnoselect(f,fontdata,data,glyphs,all,"cff2",specification.streams)
+ end
+ cleanup(data,dictionaries)
+ end
+end
+function readers.cffcheck(filename)
+ local f=io.open(filename,"rb")
+ if f then
+ local fontdata={
+ glyphs={},
+ }
+ local header=readheader(f)
+ if header.major~=1 then
+ report("only version %s is supported for table %a",1,"cff")
+ return
+ end
+ local names=readfontnames(f)
+ local dictionaries=readtopdictionaries(f)
+ local strings=readstrings(f)
+ local glyphs={}
+ local data={
+ header=header,
+ names=names,
+ dictionaries=dictionaries,
+ strings=strings,
+ glyphs=glyphs,
+ nofglyphs=4,
+ }
+ parsedictionaries(data,dictionaries,"cff")
+ local cid=data.dictionaries[1].cid
+ if cid and cid.fdselect then
+ readfdselect(f,fontdata,data,glyphs,false)
+ else
+ readnoselect(f,fontdata,data,glyphs,false)
+ end
+ return data
end
end
@@ -11367,8 +11696,10 @@ if not modules then modules={} end modules ['font-ttf']={
license="see context related readme files"
}
local next,type,unpack=next,type,unpack
-local bittest=bit32.btest
-local sqrt=math.sqrt
+local bittest,band,rshift=bit32.btest,bit32.band,bit32.rshift
+local sqrt,round=math.sqrt,math.round
+local char=string.char
+local concat=table.concat
local report=logs.reporter("otf reader","ttf")
local readers=fonts.handlers.otf.readers
local streamreader=readers.streamreader
@@ -11381,22 +11712,30 @@ local readulong=streamreader.readcardinal4
local readchar=streamreader.readinteger1
local readshort=streamreader.readinteger2
local read2dot14=streamreader.read2dot14
+local readinteger=streamreader.readinteger1
+local helpers=readers.helpers
+local gotodatatable=helpers.gotodatatable
local function mergecomposites(glyphs,shapes)
local function merge(index,shape,components)
local contours={}
+ local points={}
local nofcontours=0
+ local nofpoints=0
+ local offset=0
+ local deltas=shape.deltas
for i=1,#components do
local component=components[i]
local subindex=component.index
local subshape=shapes[subindex]
local subcontours=subshape.contours
+ local subpoints=subshape.points
if not subcontours then
local subcomponents=subshape.components
if subcomponents then
- subcontours=merge(subindex,subshape,subcomponents)
+ subcontours,subpoints=merge(subindex,subshape,subcomponents)
end
end
- if subcontours then
+ if subpoints then
local matrix=component.matrix
local xscale=matrix[1]
local xrotate=matrix[2]
@@ -11404,35 +11743,38 @@ local function mergecomposites(glyphs,shapes)
local yscale=matrix[4]
local xoffset=matrix[5]
local yoffset=matrix[6]
+ for i=1,#subpoints do
+ local p=subpoints[i]
+ local x=p[1]
+ local y=p[2]
+ nofpoints=nofpoints+1
+ points[nofpoints]={
+ xscale*x+xrotate*y+xoffset,
+ yscale*y+yrotate*x+yoffset,
+ p[3]
+ }
+ end
for i=1,#subcontours do
- local points=subcontours[i]
- local result={}
- for i=1,#points do
- local p=points[i]
- local x=p[1]
- local y=p[2]
- result[i]={
- xscale*x+xrotate*y+xoffset,
- yscale*y+yrotate*x+yoffset,
- p[3]
- }
- end
nofcontours=nofcontours+1
- contours[nofcontours]=result
+ contours[nofcontours]=offset+subcontours[i]
end
+ offset=offset+#subpoints
else
report("missing contours composite %s, component %s of %s, glyph %s",index,i,#components,subindex)
end
end
+ shape.points=points
shape.contours=contours
shape.components=nil
- return contours
+ return contours,points
end
for index=1,#glyphs do
local shape=shapes[index]
- local components=shape.components
- if components then
- merge(index,shape,components)
+ if shape then
+ local components=shape.components
+ if components then
+ merge(index,shape,components)
+ end
end
end
end
@@ -11448,117 +11790,475 @@ local function curveto(m_x,m_y,l_x,l_y,r_x,r_y)
r_x,r_y,"c"
}
end
-local function contours2outlines(glyphs,shapes)
+local function applyaxis(glyph,shape,points,deltas)
+ if points then
+ local nofpoints=#points
+ for i=1,#deltas do
+ local deltaset=deltas[i]
+ local xvalues=deltaset.xvalues
+ local yvalues=deltaset.yvalues
+ local dpoints=deltaset.points
+ local factor=deltaset.factor
+ if dpoints then
+ local nofdpoints=#dpoints
+ for i=1,nofdpoints do
+ local d=dpoints[i]
+ local p=points[d]
+ if p then
+ if xvalues then
+ local x=xvalues[d]
+ if x and x~=0 then
+ p[1]=p[1]+factor*x
+ end
+ end
+ if yvalues then
+ local y=yvalues[d]
+ if y and y~=0 then
+ p[2]=p[2]+factor*y
+ end
+ end
+ elseif width then
+ end
+ end
+ else
+ for i=1,nofpoints do
+ local p=points[i]
+ if xvalues then
+ local x=xvalues[i]
+ if x and x~=0 then
+ p[1]=p[1]+factor*x
+ end
+ end
+ if yvalues then
+ local y=yvalues[i]
+ if y and y~=0 then
+ p[2]=p[2]+factor*y
+ end
+ end
+ end
+ end
+ end
+ end
+end
+local function contours2outlines_normal(glyphs,shapes)
local quadratic=true
for index=1,#glyphs do
- local glyph=glyphs[index]
local shape=shapes[index]
- local contours=shape.contours
- if contours then
- local nofcontours=#contours
- local segments={}
- local nofsegments=0
- glyph.segments=segments
- if nofcontours>0 then
- for i=1,nofcontours do
- local contour=contours[i]
- local nofcontour=#contour
- if nofcontour>0 then
- local first_pt=contour[1]
- local first_on=first_pt[3]
- if nofcontour==1 then
- first_pt[3]="m"
- nofsegments=nofsegments+1
- segments[nofsegments]=first_pt
- else
+ if shape then
+ local glyph=glyphs[index]
+ local contours=shape.contours
+ local points=shape.points
+ if contours then
+ local nofcontours=#contours
+ local segments={}
+ local nofsegments=0
+ glyph.segments=segments
+ if nofcontours>0 then
+ local px,py=0,0
+ local first=1
+ for i=1,nofcontours do
+ local last=contours[i]
+ if last>=first then
+ local first_pt=points[first]
local first_on=first_pt[3]
- local last_pt=contour[nofcontour]
- local last_on=last_pt[3]
- local start=1
- local control_pt=false
- if first_on then
- start=2
- else
- if last_on then
- first_pt=last_pt
+ if first==last then
+ first_pt[3]="m"
+ nofsegments=nofsegments+1
+ segments[nofsegments]=first_pt
+ else
+ local first_on=first_pt[3]
+ local last_pt=points[last]
+ local last_on=last_pt[3]
+ local start=1
+ local control_pt=false
+ if first_on then
+ start=2
else
- first_pt={ (first_pt[1]+last_pt[1])/2,(first_pt[2]+last_pt[2])/2,false }
+ if last_on then
+ first_pt=last_pt
+ else
+ first_pt={ (first_pt[1]+last_pt[1])/2,(first_pt[2]+last_pt[2])/2,false }
+ end
+ control_pt=first_pt
end
- control_pt=first_pt
- end
- nofsegments=nofsegments+1
- segments[nofsegments]={ first_pt[1],first_pt[2],"m" }
- local previous_pt=first_pt
- for i=start,nofcontour do
- local current_pt=contour[i]
- local current_on=current_pt[3]
- local previous_on=previous_pt[3]
- if previous_on then
- if current_on then
+ local x,y=first_pt[1],first_pt[2]
+ if not done then
+ xmin,ymin,xmax,ymax=x,y,x,y
+ done=true
+ end
+ nofsegments=nofsegments+1
+ segments[nofsegments]={ x,y,"m" }
+ if not quadratic then
+ px,py=x,y
+ end
+ local previous_pt=first_pt
+ for i=first,last do
+ local current_pt=points[i]
+ local current_on=current_pt[3]
+ local previous_on=previous_pt[3]
+ if previous_on then
+ if current_on then
+ local x,y=current_pt[1],current_pt[2]
+ nofsegments=nofsegments+1
+ segments[nofsegments]={ x,y,"l" }
+ if not quadratic then
+ px,py=x,y
+ end
+ else
+ control_pt=current_pt
+ end
+ elseif current_on then
+ local x1,y1=control_pt[1],control_pt[2]
+ local x2,y2=current_pt[1],current_pt[2]
nofsegments=nofsegments+1
- segments[nofsegments]={ current_pt[1],current_pt[2],"l" }
+ if quadratic then
+ segments[nofsegments]={ x1,y1,x2,y2,"q" }
+ else
+ x1,x2,x2,y2,px,py=curveto(x1,x2,px,py,x2,y2)
+ segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" }
+ end
+ control_pt=false
else
+ local x2,y2=(previous_pt[1]+current_pt[1])/2,(previous_pt[2]+current_pt[2])/2
+ local x1,y1=control_pt[1],control_pt[2]
+ nofsegments=nofsegments+1
+ if quadratic then
+ segments[nofsegments]={ x1,y1,x2,y2,"q" }
+ else
+ x1,x2,x2,y2,px,py=curveto(x1,x2,px,py,x2,y2)
+ segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" }
+ end
control_pt=current_pt
end
- elseif current_on then
- local ps=segments[nofsegments]
+ previous_pt=current_pt
+ end
+ if first_pt==last_pt then
+ else
+ nofsegments=nofsegments+1
+ if not control_pt then
+ segments[nofsegments]={ first_pt[1],first_pt[2],"l" }
+ elseif quadratic then
+ local x1,y1=control_pt[1],control_pt[2]
+ segments[nofsegments]={ x1,y1,first_pt[1],first_pt[2],"q" }
+ else
+ local x1,y1=control_pt[1],control_pt[2]
+ local x2,y2=first_pt[1],first_pt[2]
+ x1,x2,x2,y2,px,py=curveto(x1,x2,px,py,x2,y2)
+ segments[nofsegments]={ x1,y1,y2,y2,px,py,"c" }
+ end
+ end
+ end
+ end
+ first=last+1
+ end
+ end
+ end
+ end
+ end
+end
+local function contours2outlines_shaped(glyphs,shapes,keepcurve)
+ local quadratic=true
+ for index=1,#glyphs do
+ local shape=shapes[index]
+ if shape then
+ local glyph=glyphs[index]
+ local contours=shape.contours
+ local points=shape.points
+ if contours then
+ local nofcontours=#contours
+ local segments=keepcurve and {} or nil
+ local nofsegments=0
+ if keepcurve then
+ glyph.segments=segments
+ end
+ if nofcontours>0 then
+ local xmin,ymin,xmax,ymax,done=0,0,0,0,false
+ local px,py=0,0
+ local first=1
+ for i=1,nofcontours do
+ local last=contours[i]
+ if last>=first then
+ local first_pt=points[first]
+ local first_on=first_pt[3]
+ if first==last then
+ if keepcurve then
+ first_pt[3]="m"
nofsegments=nofsegments+1
- if quadratic then
- segments[nofsegments]={ control_pt[1],control_pt[2],current_pt[1],current_pt[2],"q" }
+ segments[nofsegments]=first_pt
+ end
+ else
+ local first_on=first_pt[3]
+ local last_pt=points[last]
+ local last_on=last_pt[3]
+ local start=1
+ local control_pt=false
+ if first_on then
+ start=2
+ else
+ if last_on then
+ first_pt=last_pt
else
- local p=segments[nofsegments-1] local n=#p
- segments[nofsegments]=curveto(control_pt[1],control_pt[2],p[n-2],p[n-1],current_pt[1],current_pt[2])
+ first_pt={ (first_pt[1]+last_pt[1])/2,(first_pt[2]+last_pt[2])/2,false }
end
- control_pt=false
+ control_pt=first_pt
+ end
+ local x,y=first_pt[1],first_pt[2]
+ if not done then
+ xmin,ymin,xmax,ymax=x,y,x,y
+ done=true
else
+ if x<xmin then xmin=x elseif x>xmax then xmax=x end
+ if y<ymin then ymin=y elseif y>ymax then ymax=y end
+ end
+ if keepcurve then
nofsegments=nofsegments+1
- local halfway_x=(previous_pt[1]+current_pt[1])/2
- local halfway_y=(previous_pt[2]+current_pt[2])/2
- if quadratic then
- segments[nofsegments]={ control_pt[1],control_pt[2],halfway_x,halfway_y,"q" }
+ segments[nofsegments]={ x,y,"m" }
+ end
+ if not quadratic then
+ px,py=x,y
+ end
+ local previous_pt=first_pt
+ for i=first,last do
+ local current_pt=points[i]
+ local current_on=current_pt[3]
+ local previous_on=previous_pt[3]
+ if previous_on then
+ if current_on then
+ local x,y=current_pt[1],current_pt[2]
+ if x<xmin then xmin=x elseif x>xmax then xmax=x end
+ if y<ymin then ymin=y elseif y>ymax then ymax=y end
+ if keepcurve then
+ nofsegments=nofsegments+1
+ segments[nofsegments]={ x,y,"l" }
+ end
+ if not quadratic then
+ px,py=x,y
+ end
+ else
+ control_pt=current_pt
+ end
+ elseif current_on then
+ local x1,y1=control_pt[1],control_pt[2]
+ local x2,y2=current_pt[1],current_pt[2]
+ if quadratic then
+ if x1<xmin then xmin=x1 elseif x1>xmax then xmax=x1 end
+ if y1<ymin then ymin=y1 elseif y1>ymax then ymax=y1 end
+ if keepcurve then
+ nofsegments=nofsegments+1
+ segments[nofsegments]={ x1,y1,x2,y2,"q" }
+ end
+ else
+ x1,x2,x2,y2,px,py=curveto(x1,x2,px,py,x2,y2)
+ if x1<xmin then xmin=x1 elseif x1>xmax then xmax=x1 end
+ if y1<ymin then ymin=y1 elseif y1>ymax then ymax=y1 end
+ if x2<xmin then xmin=x2 elseif x2>xmax then xmax=x2 end
+ if y2<ymin then ymin=y2 elseif y2>ymax then ymax=y2 end
+ if px<xmin then xmin=px elseif px>xmax then xmax=px end
+ if py<ymin then ymin=py elseif py>ymax then ymax=py end
+ if keepcurve then
+ nofsegments=nofsegments+1
+ segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" }
+ end
+ end
+ control_pt=false
else
- local p=segments[nofsegments-1] local n=#p
- segments[nofsegments]=curveto(control_pt[1],control_pt[2],p[n-2],p[n-1],halfway_x,halfway_y)
+ local x2,y2=(previous_pt[1]+current_pt[1])/2,(previous_pt[2]+current_pt[2])/2
+ local x1,y1=control_pt[1],control_pt[2]
+ if quadratic then
+ if x1<xmin then xmin=x1 elseif x1>xmax then xmax=x1 end
+ if y1<ymin then ymin=y1 elseif y1>ymax then ymax=y1 end
+ if keepcurve then
+ nofsegments=nofsegments+1
+ segments[nofsegments]={ x1,y1,x2,y2,"q" }
+ end
+ else
+ x1,x2,x2,y2,px,py=curveto(x1,x2,px,py,x2,y2)
+ if x1<xmin then xmin=x1 elseif x1>xmax then xmax=x1 end
+ if y1<ymin then ymin=y1 elseif y1>ymax then ymax=y1 end
+ if x2<xmin then xmin=x2 elseif x2>xmax then xmax=x2 end
+ if y2<ymin then ymin=y2 elseif y2>ymax then ymax=y2 end
+ if px<xmin then xmin=px elseif px>xmax then xmax=px end
+ if py<ymin then ymin=py elseif py>ymax then ymax=py end
+ if keepcurve then
+ nofsegments=nofsegments+1
+ segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" }
+ end
+ end
+ control_pt=current_pt
end
- control_pt=current_pt
+ previous_pt=current_pt
end
- previous_pt=current_pt
- end
- if first_pt==last_pt then
- else
- nofsegments=nofsegments+1
- if not control_pt then
- segments[nofsegments]={ first_pt[1],first_pt[2],"l" }
+ if first_pt==last_pt then
+ elseif not control_pt then
+ if keepcurve then
+ nofsegments=nofsegments+1
+ segments[nofsegments]={ first_pt[1],first_pt[2],"l" }
+ end
elseif quadratic then
- segments[nofsegments]={ control_pt[1],control_pt[2],first_pt[1],first_pt[2],"q" }
+ local x1,y1=control_pt[1],control_pt[2]
+ if x1<xmin then xmin=x1 elseif x1>xmax then xmax=x1 end
+ if y1<ymin then ymin=y1 elseif y1>ymax then ymax=y1 end
+ if keepcurve then
+ nofsegments=nofsegments+1
+ segments[nofsegments]={ x1,y1,first_pt[1],first_pt[2],"q" }
+ end
else
- local p=last_pt local n=#p
- segments[nofsegments]=curveto(control_pt[1],control_pt[2],p[n-2],p[n-1],first_pt[1],first_pt[2])
+ local x1,y1=control_pt[1],control_pt[2]
+ local x2,y2=first_pt[1],first_pt[2]
+ x1,x2,x2,y2,px,py=curveto(x1,x2,px,py,x2,y2)
+ if x1<xmin then xmin=x1 elseif x1>xmax then xmax=x1 end
+ if y1<ymin then ymin=y1 elseif y1>ymax then ymax=y1 end
+ if x2<xmin then xmin=x2 elseif x2>xmax then xmax=x2 end
+ if y2<ymin then ymin=y2 elseif y2>ymax then ymax=y2 end
+ if px<xmin then xmin=px elseif px>xmax then xmax=px end
+ if py<ymin then ymin=py elseif py>ymax then ymax=py end
+ if keepcurve then
+ nofsegments=nofsegments+1
+ segments[nofsegments]={ x1,y1,y2,y2,px,py,"c" }
+ end
end
end
end
+ first=last+1
end
+ glyph.boundingbox={ round(xmin),round(ymin),round(xmax),round(ymax) }
end
end
end
end
end
-local function readglyph(f,nofcontours)
+local c_zero=char(0)
+local s_zero=char(0,0)
+local function toushort(n)
+ return char(band(rshift(n,8),0xFF),band(n,0xFF))
+end
+local function toshort(n)
+ if n<0 then
+ n=n+0x10000
+ end
+ return char(band(rshift(n,8),0xFF),band(n,0xFF))
+end
+local function repackpoints(glyphs,shapes)
+ local noboundingbox={ 0,0,0,0 }
+ local result={}
+ for index=1,#glyphs do
+ local shape=shapes[index]
+ if shape then
+ local r=0
+ local glyph=glyphs[index]
+ if false then
+ else
+ local contours=shape.contours
+ local nofcontours=#contours
+ local boundingbox=glyph.boundingbox or noboundingbox
+ r=r+1 result[r]=toshort(nofcontours)
+ r=r+1 result[r]=toshort(boundingbox[1])
+ r=r+1 result[r]=toshort(boundingbox[2])
+ r=r+1 result[r]=toshort(boundingbox[3])
+ r=r+1 result[r]=toshort(boundingbox[4])
+ if nofcontours>0 then
+ for i=1,nofcontours do
+ r=r+1 result[r]=toshort(contours[i]-1)
+ end
+ r=r+1 result[r]=s_zero
+ local points=shape.points
+ local currentx=0
+ local currenty=0
+ local xpoints={}
+ local ypoints={}
+ local x=0
+ local y=0
+ local lastflag=nil
+ local nofflags=0
+ for i=1,#points do
+ local pt=points[i]
+ local px=pt[1]
+ local py=pt[2]
+ local fl=pt[3] and 0x01 or 0x00
+ if px==currentx then
+ fl=fl+0x10
+ else
+ local dx=round(px-currentx)
+ if dx<-255 or dx>255 then
+ x=x+1 xpoints[x]=toshort(dx)
+ elseif dx<0 then
+ fl=fl+0x02
+ x=x+1 xpoints[x]=char(-dx)
+ elseif dx>0 then
+ fl=fl+0x12
+ x=x+1 xpoints[x]=char(dx)
+ else
+ fl=fl+0x02
+ x=x+1 xpoints[x]=c_zero
+ end
+ end
+ if py==currenty then
+ fl=fl+0x20
+ else
+ local dy=round(py-currenty)
+ if dy<-255 or dy>255 then
+ y=y+1 ypoints[y]=toshort(dy)
+ elseif dy<0 then
+ fl=fl+0x04
+ y=y+1 ypoints[y]=char(-dy)
+ elseif dy>0 then
+ fl=fl+0x24
+ y=y+1 ypoints[y]=char(dy)
+ else
+ fl=fl+0x04
+ y=y+1 ypoints[y]=c_zero
+ end
+ end
+ currentx=px
+ currenty=py
+ if lastflag==fl then
+ nofflags=nofflags+1
+ else
+ if nofflags==1 then
+ r=r+1 result[r]=char(lastflag)
+ elseif nofflags==2 then
+ r=r+1 result[r]=char(lastflag,lastflag)
+ elseif nofflags>2 then
+ lastflag=lastflag+0x08
+ r=r+1 result[r]=char(lastflag,nofflags-1)
+ end
+ nofflags=1
+ lastflag=fl
+ end
+ end
+ if nofflags==1 then
+ r=r+1 result[r]=char(lastflag)
+ elseif nofflags==2 then
+ r=r+1 result[r]=char(lastflag,lastflag)
+ elseif nofflags>2 then
+ lastflag=lastflag+0x08
+ r=r+1 result[r]=char(lastflag,nofflags-1)
+ end
+ r=r+1 result[r]=concat(xpoints)
+ r=r+1 result[r]=concat(ypoints)
+ end
+ end
+ glyph.stream=concat(result,"",1,r)
+ else
+ end
+ end
+end
+local function readglyph(f,nofcontours)
local points={}
- local endpoints={}
+ local contours={}
local instructions={}
local flags={}
for i=1,nofcontours do
- endpoints[i]=readshort(f)+1
+ contours[i]=readshort(f)+1
end
- local nofpoints=endpoints[nofcontours]
+ local nofpoints=contours[nofcontours]
local nofinstructions=readushort(f)
skipbytes(f,nofinstructions)
local i=1
while i<=nofpoints do
local flag=readbyte(f)
flags[i]=flag
- if bittest(flag,0x0008) then
+ if bittest(flag,0x08) then
for j=1,readbyte(f) do
i=i+1
flags[i]=flag
@@ -11569,8 +12269,8 @@ local function readglyph(f,nofcontours)
local x=0
for i=1,nofpoints do
local flag=flags[i]
- local short=bittest(flag,0x0002)
- local same=bittest(flag,0x0010)
+ local short=bittest(flag,0x02)
+ local same=bittest(flag,0x10)
if short then
if same then
x=x+readbyte(f)
@@ -11581,13 +12281,13 @@ local function readglyph(f,nofcontours)
else
x=x+readshort(f)
end
- points[i]={ x,y,bittest(flag,0x0001) }
+ points[i]={ x,y,bittest(flag,0x01) }
end
local y=0
for i=1,nofpoints do
local flag=flags[i]
- local short=bittest(flag,0x0004)
- local same=bittest(flag,0x0020)
+ local short=bittest(flag,0x04)
+ local same=bittest(flag,0x20)
if short then
if same then
y=y+readbyte(f)
@@ -11600,15 +12300,11 @@ local function readglyph(f,nofcontours)
end
points[i][2]=y
end
- local first=1
- for i=1,#endpoints do
- local last=endpoints[i]
- endpoints[i]={ unpack(points,first,last) }
- first=last+1
- end
return {
type="glyph",
- contours=endpoints,
+ points=points,
+ contours=contours,
+ nofpoints=nofpoints,
}
end
local function readcomposite(f)
@@ -11699,15 +12395,13 @@ function readers.loca(f,fontdata,specification)
local locations={}
setposition(f,datatable.offset)
if format==1 then
- local nofglyphs=datatable.length/4-1
- -1
+ local nofglyphs=datatable.length/4-2
for i=0,nofglyphs do
locations[i]=offset+readulong(f)
end
fontdata.nofglyphs=nofglyphs
else
- local nofglyphs=datatable.length/2-1
- -1
+ local nofglyphs=datatable.length/2-2
for i=0,nofglyphs do
locations[i]=offset+readushort(f)*2
end
@@ -11718,51 +12412,268 @@ function readers.loca(f,fontdata,specification)
end
end
function readers.glyf(f,fontdata,specification)
- if specification.glyphs then
- local datatable=fontdata.tables.glyf
- if datatable then
- local locations=fontdata.locations
- if locations then
- local glyphs=fontdata.glyphs
- local nofglyphs=fontdata.nofglyphs
- local filesize=fontdata.filesize
- local nothing={ 0,0,0,0 }
- local shapes={}
- local loadshapes=specification.shapes
- for index=0,nofglyphs do
- local location=locations[index]
- if location>=filesize then
- report("discarding %s glyphs due to glyph location bug",nofglyphs-index+1)
- fontdata.nofglyphs=index-1
- fontdata.badfont=true
- break
- elseif location>0 then
- setposition(f,location)
- local nofcontours=readshort(f)
- glyphs[index].boundingbox={
- readshort(f),
- readshort(f),
- readshort(f),
- readshort(f),
- }
- if not loadshapes then
- elseif nofcontours==0 then
- shapes[index]=readnothing(f,nofcontours)
- elseif nofcontours>0 then
- shapes[index]=readglyph(f,nofcontours)
+ local tableoffset=gotodatatable(f,fontdata,"glyf",specification.glyphs)
+ if tableoffset then
+ local locations=fontdata.locations
+ if locations then
+ local glyphs=fontdata.glyphs
+ local nofglyphs=fontdata.nofglyphs
+ local filesize=fontdata.filesize
+ local nothing={ 0,0,0,0 }
+ local shapes={}
+ local loadshapes=specification.shapes or specification.instance
+ for index=0,nofglyphs do
+ local location=locations[index]
+ if location>=filesize then
+ report("discarding %s glyphs due to glyph location bug",nofglyphs-index+1)
+ fontdata.nofglyphs=index-1
+ fontdata.badfont=true
+ break
+ elseif location>0 then
+ setposition(f,location)
+ local nofcontours=readshort(f)
+ glyphs[index].boundingbox={
+ readshort(f),
+ readshort(f),
+ readshort(f),
+ readshort(f),
+ }
+ if not loadshapes then
+ elseif nofcontours==0 then
+ shapes[index]=readnothing(f,nofcontours)
+ elseif nofcontours>0 then
+ shapes[index]=readglyph(f,nofcontours)
+ else
+ shapes[index]=readcomposite(f,nofcontours)
+ end
+ else
+ if loadshapes then
+ shapes[index]={}
+ end
+ glyphs[index].boundingbox=nothing
+ end
+ end
+ if loadshapes then
+ if readers.gvar then
+ readers.gvar(f,fontdata,specification,glyphs,shapes)
+ end
+ mergecomposites(glyphs,shapes)
+ if specification.instance then
+ if specification.streams then
+ repackpoints(glyphs,shapes)
+ else
+ contours2outlines_shaped(glyphs,shapes,specification.shapes)
+ end
+ elseif specification.loadshapes then
+ contours2outlines_normal(glyphs,shapes)
+ end
+ end
+ end
+ end
+end
+local function readtuplerecord(f,nofaxis)
+ local record={}
+ for i=1,nofaxis do
+ record[i]=read2dot14(f)
+ end
+ return record
+end
+local function readpoints(f)
+ local count=readbyte(f)
+ if count==0 then
+ return nil,0
+ else
+ if count<128 then
+ else
+ count=band(count,0x80)*256+readbyte(f)
+ end
+ local points={}
+ local p=0
+ local n=1
+ while p<count do
+ local control=readbyte(f)
+ local runreader=bittest(control,0x80) and readushort or readbyte
+ local runlength=band(control,0x7F)
+ for i=1,runlength+1 do
+ n=n+runreader(f)
+ p=p+1
+ points[p]=n
+ end
+ end
+ return points,p
+ end
+end
+local function readdeltas(f,nofpoints)
+ local deltas={}
+ local p=0
+ local n=0
+ local z=false
+ while nofpoints>0 do
+ local control=readbyte(f)
+ local allzero=bittest(control,0x80)
+ local runreader=bittest(control,0x40) and readshort or readinteger
+ local runlength=band(control,0x3F)+1
+ if allzero then
+ z=runlength
+ else
+ if z then
+ for i=1,z do
+ p=p+1
+ deltas[p]=0
+ end
+ z=false
+ end
+ for i=1,runlength do
+ p=p+1
+ deltas[p]=runreader(f)
+ end
+ end
+ nofpoints=nofpoints-runlength
+ end
+ if p>0 then
+ return deltas
+ else
+ end
+end
+function readers.gvar(f,fontdata,specification,glyphdata,shapedata)
+ local instance=specification.instance
+ if not instance then
+ return
+ end
+ local factors=specification.factors
+ if not factors then
+ return
+ end
+ local tableoffset=gotodatatable(f,fontdata,"gvar",specification.variable or specification.shapes)
+ if tableoffset then
+ local version=readulong(f)
+ local nofaxis=readushort(f)
+ local noftuples=readushort(f)
+ local tupleoffset=readulong(f)
+ local nofglyphs=readushort(f)
+ local flags=readushort(f)
+ local dataoffset=tableoffset+readulong(f)
+ local data={}
+ local tuples={}
+ local glyphdata=fontdata.glyphs
+ if bittest(flags,0x0001) then
+ for i=1,nofglyphs do
+ data[i]=readulong(f)
+ end
+ else
+ for i=1,nofglyphs do
+ data[i]=2*readushort(f)
+ end
+ end
+ setposition(f,tableoffset+tupleoffset)
+ for i=1,noftuples do
+ tuples[i]=readtuplerecord(f,nofaxis)
+ end
+ local lastoffset=false
+ for i=1,nofglyphs do
+ local shape=shapedata[i-1]
+ if shape then
+ local startoffset=dataoffset+data[i]
+ if startoffset==lastoffset then
+ else
+ lastoffset=startoffset
+ setposition(f,startoffset)
+ local flags=readushort(f)
+ local count=band(flags,0x0FFF)
+ local points=bittest(flags,0x8000)
+ local offset=startoffset+readushort(f)
+ local deltas={}
+ local nofpoints=0
+ local allpoints=(shape.nofpoints or 0)+1
+ if points then
+ local current=getposition(f)
+ setposition(f,offset)
+ points,nofpoints=readpoints(f)
+ offset=getposition(f)
+ setposition(f,current)
+ else
+ points,nofpoints=nil,0
+ end
+ for i=1,count do
+ local currentstart=getposition(f)
+ local size=readushort(f)
+ local flags=readushort(f)
+ local index=band(flags,0x0FFF)
+ local haspeak=bittest(flags,0x8000)
+ local intermediate=bittest(flags,0x4000)
+ local private=bittest(flags,0x1000)
+ local peak=nil
+ local start=nil
+ local stop=nil
+ local xvalues=nil
+ local yvalues=nil
+ local points=points
+ local nofpoints=nofpoints
+ local advance=4
+ if peak then
+ peak=readtuplerecord(f,nofaxis)
+ advance=advance+2*nofaxis
else
- shapes[index]=readcomposite(f,nofcontours)
+ if index+1>#tuples then
+ print("error, bad index",index)
+ end
+ peak=tuples[index+1]
end
- else
- if loadshapes then
- shapes[index]={}
+ if intermediate then
+ start=readtuplerecord(f,nofaxis)
+ stop=readtuplerecord(f,nofaxis)
+ advance=advance+4*nofaxis
+ end
+ if size>0 then
+ setposition(f,offset)
+ if private then
+ points,nofpoints=readpoints(f)
+ elseif nofpoints==0 then
+ nofpoints=allpoints
+ end
+ if nofpoints>0 then
+ xvalues=readdeltas(f,nofpoints)
+ yvalues=readdeltas(f,nofpoints)
+ end
+ offset=getposition(f)
+ setposition(f,currentstart+advance)
+ end
+ if not xvalues and not yvalues then
+ points=nil
+ end
+ local s=1
+ for i=1,nofaxis do
+ local f=factors[i]
+ local start=start and start[i] or 0
+ local peak=peak and peak [i] or 0
+ local stop=stop and stop [i] or 0
+ if start>peak or peak>stop then
+ elseif start<0 and stop>0 and peak~=0 then
+ elseif peak==0 then
+ elseif f<start or f>stop then
+ s=0
+ break
+ elseif f<peak then
+ s=s*(f-start)/(peak-start)
+ elseif f>peak then
+ s=s*(stop-f)/(stop-peak)
+ else
+ end
+ end
+ if s~=0 then
+ deltas[#deltas+1]={
+ factor=s,
+ points=points,
+ xvalues=xvalues,
+ yvalues=yvalues,
+ }
end
- glyphs[index].boundingbox=nothing
end
- end
- if loadshapes then
- mergecomposites(glyphs,shapes)
- contours2outlines(glyphs,shapes)
+ if shape.type=="glyph" then
+ applyaxis(glyphdata[i],shape,shape.points,deltas)
+ else
+ shape.deltas=deltas
+ end
end
end
end
@@ -11783,16 +12694,22 @@ if not modules then modules={} end modules ['font-dsp']={
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 lpegmatch=lpeg.match
local setmetatableindex=table.setmetatableindex
local formatters=string.formatters
local sortedkeys=table.sortedkeys
@@ -11818,6 +12735,11 @@ local readbytetable=streamreader.readbytetable
local readbyte=streamreader.readbyte
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
local classes={
"base",
@@ -11851,6 +12773,51 @@ local chaindirections={
chainedcontext=1,
reversechainedcontextsingle=-1,
}
+local function setmetrics(data,where,tag,d)
+ local w=data[where]
+ if w then
+ local v=w[tag]
+ if v then
+ 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,
+}
local lookupnames={
gsub={
single="gsub_single",
@@ -11882,6 +12849,210 @@ local lookupflags=setmetatableindex(function(t,k)
t[k]=v
return v
end)
+local pattern=lpeg.Cf (
+ lpeg.Ct("")*lpeg.Cg (
+ lpeg.C(lpeg.R("az")^1)*lpeg.S(" :=")*(lpeg.patterns.number/tonumber)*lpeg.S(" ,")^0
+ )^1,rawset
+)
+local hash=table.setmetatableindex(function(t,k)
+ local v=lpegmatch(pattern,k)
+ local t={}
+ for k,v in sortedhash(v) do
+ t[#t+1]=k.."="..v
+ end
+ v=concat(t,",")
+ 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
+local function axistofactors(str)
+ return lpegmatch(pattern,str)
+end
+local function getaxisscale(segments,minimum,default,maximum,user)
+ 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 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
+ 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
+ values={}
+ for i=1,#axis do
+ values[i]={
+ 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
+ if start>peak or peak>stop then
+ elseif start<0 and stop>0 and peak~=0 then
+ elseif peak==0 then
+ elseif f<start or f>stop then
+ s=0
+ break
+ elseif f<peak then
+ s=s*(f-start)/(peak-start)
+ elseif f>peak then
+ s=s*(stop-f)/(stop-peak)
+ else
+ 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)
+ local position=getposition(f)
+ setposition(f,storeoffset)
+ local format=readushort(f)
+ local regionoffset=storeoffset+readulong(f)
+ local nofdeltadata=readushort(f)
+ local deltadata={}
+ for i=1,nofdeltadata do
+ deltadata[i]=readulong(f)
+ end
+ setposition(f,regionoffset)
+ local nofaxis=readushort(f)
+ local nofregions=readushort(f)
+ local regions={}
+ for i=1,nofregions do
+ local t={}
+ for i=1,nofaxis do
+ t[i]={
+ start=read2dot14(f),
+ peak=read2dot14(f),
+ stop=read2dot14(f),
+ }
+ end
+ regions[i]=t
+ end
+ 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
+ for i=1,nofdeltasets do
+ local t={}
+ for i=1,nofshorts do
+ t[i]=readshort(f)
+ end
+ 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
local function readcoverage(f,offset,simple)
setposition(f,offset)
local coverageformat=readushort(f)
@@ -11974,34 +13145,150 @@ local function classtocoverage(defs)
return list
end
end
-local function readposition(f,format)
+local skips={ [0]=0,
+ 1,
+ 1,
+ 2,
+ 1,
+ 2,
+ 2,
+ 3,
+ 2,
+ 2,
+ 3,
+ 2,
+ 3,
+ 3,
+ 4,
+}
+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
end
- local x=bittest(format,0x0001) and readshort(f) or 0
- local y=bittest(format,0x0002) and readshort(f) or 0
- local h=bittest(format,0x0004) and readshort(f) or 0
- local v=bittest(format,0x0008) and readshort(f) or 0
- if x==0 and y==0 and h==0 and v==0 then
- return nil
+ if format==0x04 then
+ local h=readshort(f)
+ if h==0 then
+ return
+ 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
+ else
+ return { x,0,h,0 }
+ end
+ end
+ if format==0x44 then
+ local h=readshort(f)
+ if getdelta then
+ local d=readshort(f)
+ 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
+ else
+ return { 0,0,h,0 }
+ end
+ end
+ local x=bittest(format,0x01) and readshort(f) or 0
+ local y=bittest(format,0x02) and readshort(f) or 0
+ local h=bittest(format,0x04) and readshort(f) or 0
+ local v=bittest(format,0x08) and readshort(f) or 0
+ if format>=0x10 then
+ local X=bittest(format,0x10) and skipshort(f) or 0
+ local Y=bittest(format,0x20) and skipshort(f) or 0
+ local H=bittest(format,0x40) and skipshort(f) or 0
+ local V=bittest(format,0x80) 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
else
return { x,y,h,v }
end
end
-local function readanchor(f,offset)
+local function readanchor(f,offset,getdelta)
if not offset or offset==0 then
return nil
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
+ local format=readshort(f)
+ 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 }
+ else
+ return { x,y }
end
- return { readshort(f),readshort(f) }
end
local function readfirst(f,offset)
if offset then
@@ -12521,20 +13808,21 @@ function gsubhandlers.reversechainedcontextsingle(f,fontdata,lookupid,lookupoffs
report("unsupported subtype %a in %a substitution",subtype,"reversechainedcontextsingle")
end
end
-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),
- readposition(f,format1),
- readposition(f,format2)
+ readposition(f,format1,offset,getdelta),
+ readposition(f,format2,offset,getdelta),
}
end
done[offset]=reused
@@ -12543,14 +13831,14 @@ local function readpairsets(f,tableoffset,sets,format1,format2)
end
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
@@ -12564,25 +13852,26 @@ function gposhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofg
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
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
end
return {
format="pair",
- coverage=coverage
+ 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
@@ -12590,7 +13879,7 @@ function gposhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofg
end
return {
format="pair",
- coverage=coverage
+ coverage=coverage,
}
else
report("unsupported subtype %a in %a positioning",subtype,"single")
@@ -12600,12 +13889,13 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
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]
@@ -12627,7 +13917,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)
@@ -12637,7 +13927,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly
local classdef2=readushort(f)
local nofclasses1=readushort(f)
local nofclasses2=readushort(f)
- 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)
@@ -12664,7 +13954,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")
@@ -12676,6 +13966,7 @@ function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nof
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
local subtype=readushort(f)
+ local getdelta=fontdata.temporary.getdelta
if subtype==1 then
local coverage=tableoffset+readushort(f)
local nofrecords=readushort(f)
@@ -12693,15 +13984,15 @@ function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nof
local r=records[i]
records[i]={
1,
- readanchor(f,r.entry) or nil,
- readanchor(f,r.exit ) or nil,
+ readanchor(f,r.entry,getdelta) or nil,
+ readanchor(f,r.exit,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")
@@ -12711,6 +14002,7 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp
local tableoffset=lookupoffset+offset
setposition(f,tableoffset)
local subtype=readushort(f)
+ local getdelta=fontdata.temporary.getdelta
if subtype==1 then
local markcoverage=tableoffset+readushort(f)
local basecoverage=tableoffset+readushort(f)
@@ -12737,7 +14029,7 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp
for i=1,nofmarkclasses do
local mc=markclasses[i]
if mc then
- mc[2]=readanchor(f,mc[2])
+ mc[2]=readanchor(f,mc[2],getdelta)
end
end
setposition(f,baseoffset)
@@ -12785,7 +14077,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
@@ -12827,7 +14119,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
@@ -12870,7 +14162,7 @@ do
setposition(f,offset)
local designsize=readushort(f)
if designsize>0 then
- local fontstyle=readushort(f)
+ local fontstyleid=readushort(f)
local guimenuid=readushort(f)
local minsize=readushort(f)
local maxsize=readushort(f)
@@ -13046,8 +14338,8 @@ do
lookups[i]=readushort(f)
end
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)
@@ -13055,8 +14347,7 @@ do
local lookupflags=lookupflags[flagbits]
local nofsubtables=readushort(f)
for j=1,nofsubtables do
- local offset=readushort(f)
- subtables[j]=offset+index
+ subtables[j]=offset+readushort(f)
end
local markclass=bittest(flagbits,0x0010)
if markclass then
@@ -13078,20 +14369,8 @@ do
end
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 {}
fontdata.sequences=sequences
@@ -13289,33 +14568,110 @@ do
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
+ setposition(f,variationsoffset+offset)
+ local nofconditions=readushort(f)
+ local conditions={}
+ for i=1,nofconditions do
+ conditions[i]=variationsoffset+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)
+ local noflookups=readushort(f)
+ local lookups={}
+ for i=1,noflookups do
+ lookups[i]=readushort(f)
+ end
+ 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
local function checkkerns(f,fontdata,specification)
@@ -13405,86 +14761,114 @@ do
end
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)
- local ligaturecarets=tableoffset+readushort(f)
- 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
- setposition(f,classoffset)
- local classformat=readushort(f)
- if classformat==1 then
+ 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=tableoffset+readushort(f)
+ local attachmentoffset=tableoffset+readushort(f)
+ local ligaturecarets=tableoffset+readushort(f)
+ local markclassoffset=tableoffset+readushort(f)
+ local marksetsoffset=version>=0x00010002 and (tableoffset+readushort(f))
+ local varsetsoffset=version>=0x00010003 and (tableoffset+readulong(f))
+ local glyphs=fontdata.glyphs
+ local marks={}
+ local markclasses=setmetatableindex("table")
+ local marksets=setmetatableindex("table")
+ fontdata.marks=marks
+ fontdata.markclasses=markclasses
+ fontdata.marksets=marksets
+ setposition(f,classoffset)
+ local classformat=readushort(f)
+ if classformat==1 then
+ local firstindex=readushort(f)
+ local lastindex=firstindex+readushort(f)-1
+ for index=firstindex,lastindex do
+ local class=classes[readushort(f)]
+ if class=="mark" then
+ marks[index]=true
+ end
+ glyphs[index].class=class
+ end
+ elseif classformat==2 then
+ local nofranges=readushort(f)
+ for i=1,nofranges do
local firstindex=readushort(f)
- local lastindex=firstindex+readushort(f)-1
- for index=firstindex,lastindex do
- local class=classes[readushort(f)]
- if class=="mark" then
- marks[index]=true
- end
- glyphs[index].class=class
- end
- elseif classformat==2 then
- local nofranges=readushort(f)
- for i=1,nofranges do
- local firstindex=readushort(f)
- local lastindex=readushort(f)
- local class=classes[readushort(f)]
- if class then
- for index=firstindex,lastindex do
- glyphs[index].class=class
- if class=="mark" then
- marks[index]=true
- end
+ local lastindex=readushort(f)
+ local class=classes[readushort(f)]
+ if class then
+ for index=firstindex,lastindex do
+ glyphs[index].class=class
+ if class=="mark" then
+ marks[index]=true
end
end
end
end
- setposition(f,markclassoffset)
- local classformat=readushort(f)
- if classformat==1 then
+ end
+ setposition(f,markclassoffset)
+ local classformat=readushort(f)
+ if classformat==1 then
+ local firstindex=readushort(f)
+ local lastindex=firstindex+readushort(f)-1
+ for index=firstindex,lastindex do
+ markclasses[readushort(f)][index]=true
+ end
+ elseif classformat==2 then
+ local nofranges=readushort(f)
+ for i=1,nofranges do
local firstindex=readushort(f)
- local lastindex=firstindex+readushort(f)-1
+ local lastindex=readushort(f)
+ local class=markclasses[readushort(f)]
for index=firstindex,lastindex do
- markclasses[readushort(f)][index]=true
- end
- elseif classformat==2 then
- local nofranges=readushort(f)
- for i=1,nofranges do
- local firstindex=readushort(f)
- local lastindex=readushort(f)
- local class=markclasses[readushort(f)]
- for index=firstindex,lastindex do
- class[index]=true
- end
+ class[index]=true
end
end
- if marksetsoffset and marksetsoffset>tableoffset then
- 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
- for i=1,nofsets do
- local offset=sets[i]
- if offset~=0 then
- marksets[i]=readcoverage(f,marksetsoffset+offset)
+ end
+ if marksetsoffset and marksetsoffset>tableoffset then
+ 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
+ for i=1,nofsets do
+ local offset=sets[i]
+ if offset~=0 then
+ marksets[i]=readcoverage(f,marksetsoffset+offset)
+ end
+ end
+ end
+ end
+ local factors=specification.factors
+ if (specification.variable or factors) and varsetsoffset and varsetsoffset>tableoffset then
+ local regions,deltas=readvariationdata(f,varsetsoffset,factors)
+ 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
@@ -13756,167 +15140,435 @@ local function readmathvariants(f,fontdata,offset)
get(offset,hcoverage,hnofglyphs,hconstruction,"hvariants","hparts","hitalic")
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)
- end
- if glyphinfo~=0 then
- readmathglyphinfo(f,fontdata,tableoffset+glyphinfo)
- end
- if variants~=0 then
- readmathvariants(f,fontdata,tableoffset+variants)
- end
+ local tableoffset=gotodatatable(f,fontdata,"math",specification.glyphs)
+ if tableoffset then
+ local version=readulong(f)
+ 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 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
- 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 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
+ 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
+ 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 layerrecords={}
- local maxclass=0
- setposition(f,tableoffset+layeroffset)
+ local t={}
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
- 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
- glyphs[glyphindex].colors=t
+ t[i]=layerrecords[firstlayer+i]
end
+ glyphs[glyphindex].colors=t
end
- fontdata.hascolor=true
end
+ fontdata.hascolor=true
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
+ local tableoffset=gotodatatable(f,fontdata,"cpal",specification.glyphs)
+ if tableoffset then
+ local version=readushort(f)
+ 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
+ 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
- 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
- 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,
+ palettes[i]=p
+ end
+ fontdata.colorpalettes=palettes
+ end
+end
+function readers.svg(f,fontdata,specification)
+ local tableoffset=gotodatatable(f,fontdata,"svg",specification.glyphs)
+ if tableoffset then
+ local version=readushort(f)
+ 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.stat(f,fontdata,specification)
+ local tableoffset=gotodatatable(f,fontdata,"stat",true)
+ if tableoffset then
+ local extras=fontdata.extras
+ local version=readulong(f)
+ 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)]
+ local axis={}
+ local values={}
+ setposition(f,tableoffset+axisoffset)
+ for i=1,nofaxis do
+ axis[i]={
+ tag=readtag(f),
+ name=lower(extras[readushort(f)]),
+ ordering=readushort(f),
+ variants={}
+ }
+ end
+ 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)])
+ 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
- for i=1,nofpalettes do
- local p={}
- local o=palettes[i]
- for j=1,nofpaletteentries do
- p[j]=colorrecords[o+j]
+ 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
+function readers.avar(f,fontdata,specification)
+ local tableoffset=gotodatatable(f,fontdata,"avar",true)
+ if tableoffset then
+ local function collect()
+ local nofvalues=readulong(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
+ elseif lastto and t>=lastto then
+ else
+ values[#values+1]={ f,t }
+ lasfrom,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,size-1 do
+ some=values[i]
+ if some[1]==0 and some[2]==0 then
+ return values
+ end
+ end
+ end
end
- palettes[i]=p
end
- fontdata.colorpalettes=palettes
+ return false
end
+ local version=readulong(f)
+ local reserved=readulong(f)
+ local nofaxis=readulong(f)
+ local segments={}
+ for i=1,nofaxis do
+ segments[i]=collect()
+ end
+ setvariabledata(fontdata,"segments",segments)
+ end
+end
+function readers.fvar(f,fontdata,specification)
+ local tableoffset=gotodatatable(f,fontdata,"fvar",true)
+ if tableoffset then
+ local version=readulong(f)
+ local offsettoaxis=tableoffset+readushort(f)
+ local reserved=skipshort(f)
+ local nofaxis=readushort(f)
+ local sizeofaxis=readushort(f)
+ 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),
+ minimum=readfixed(f),
+ default=readfixed(f),
+ maximum=readfixed(f),
+ flags=readushort(f),
+ name=lower(extras[readushort(f)]),
+ }
+ local n=sizeofaxis-20
+ if n>0 then
+ skipbytes(f,n)
+ elseif n<0 then
+ end
+ 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)
+ local values={}
+ for i=1,nofaxis do
+ values[i]={
+ axis=axis[i].tag,
+ value=readfixed(f),
+ }
+ end
+ local psnameid=readpsname and readushort(f) or 0xFFFF
+ if subfamid==2 or subfamid==17 then
+ elseif subfamid==0xFFFF then
+ subfamid=nil
+ elseif subfamid<=256 or subfamid>=32768 then
+ subfamid=nil
+ end
+ if psnameid==6 then
+ elseif psnameid==0xFFFF then
+ psnameid=nil
+ elseif psnameid<=256 or psnameid>=32768 then
+ psnameid=nil
+ end
+ instances[i]={
+ subfamily=extras[subfamid],
+ psname=psnameid and extras[psnameid] or nil,
+ values=values,
+ }
+ if skippable>0 then
+ skipbytes(f,skippable)
+ end
+ 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)
+ local variationoffset=tableoffset+readulong(f)
+ local advanceoffset=tableoffset+readulong(f)
+ local lsboffset=tableoffset+readulong(f)
+ local rsboffset=tableoffset+readulong(f)
+ local regions={}
+ local variations={}
+ local innerindex={}
+ local outerindex={}
+ if variationoffset>0 then
+ regions,deltas=readvariationdata(f,variationoffset,factors)
+ end
+ if not regions then
+ return
+ end
+ if advanceoffset>0 then
+ setposition(f,advanceoffset)
+ local format=readushort(f)
+ local mapcount=readushort(f)
+ local entrysize=rshift(band(format,0x0030),4)+1
+ local nofinnerbits=band(format,0x000F)+1
+ local innermask=lshift(1,nofinnerbits)-1
+ local readcardinal=read_cardinal[entrysize]
+ for i=0,mapcount-1 do
+ local mapdata=readcardinal(f)
+ outerindex[i]=rshift(mapdata,nofinnerbits)
+ innerindex[i]=band(mapdata,innermask)
+ end
+ 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
+ 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
+ end
+ end
+ 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
+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)
+ 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
+ skipbytes(recordsize-8)
+ end
end
- fontdata.svgshapes=entries
end
- fontdata.hascolor=true
end
end
@@ -15015,6 +16667,7 @@ function readers.pack(data)
local sublookups=resources.sublookups
local features=resources.features
local palettes=resources.colorpalettes
+ local variable=resources.variabledata
local chardata=characters and characters.data
local descriptions=data.descriptions or data.glyphs
if not descriptions then
@@ -15166,6 +16819,46 @@ function readers.pack(data)
end
end
end
+ if variable then
+ local instances=variable.instances
+ if instances then
+ for i=1,#instances do
+ local v=instances[i].values
+ for j=1,#v do
+ v[j]=pack_normal(v[j])
+ end
+ end
+ end
+ local function packdeltas(main)
+ if main then
+ local deltas=main.deltas
+ if deltas then
+ for i=1,#deltas do
+ local di=deltas[i]
+ local d=di.deltas
+ local r=di.regions
+ for j=1,#d do
+ d[j]=pack_indexed(d[j])
+ end
+ di.regions=pack_indexed(di.regions)
+ end
+ end
+ local regions=main.regions
+ if regions then
+ for i=1,#regions do
+ local r=regions[i]
+ for j=1,#r do
+ r[j]=pack_normal(r[j])
+ end
+ end
+ end
+ end
+ end
+ packdeltas(variable.global)
+ packdeltas(variable.horizontal)
+ packdeltas(variable.vertical)
+ packdeltas(variable.metrics)
+ end
if not success(1,pass) then
return
end
@@ -15229,7 +16922,19 @@ function readers.pack(data)
if sublookups then
packthem(sublookups)
end
- if not success(2,pass) then
+ if variable then
+ local function unpackdeltas(main)
+ if main then
+ local regions=main.regions
+ if regions then
+ main.regions=pack_normal(regions)
+ end
+ end
+ end
+ unpackdeltas(variable.global)
+ unpackdeltas(variable.horizontal)
+ unpackdeltas(variable.vertical)
+ unpackdeltas(variable.metrics)
end
end
for pass=1,2 do
@@ -15287,6 +16992,7 @@ function readers.unpack(data)
local sublookups=resources.sublookups
local features=resources.features
local palettes=resources.colorpalettes
+ local variable=resources.variabledata
local unpacked={}
setmetatable(unpacked,unpacked_mt)
for unicode,description in next,descriptions do
@@ -15543,6 +17249,63 @@ function readers.unpack(data)
end
end
end
+ if variable then
+ local instances=variable.instances
+ if instances then
+ for i=1,#instances do
+ local v=instances[i].values
+ for j=1,#v do
+ local tv=tables[v[j]]
+ if tv then
+ v[j]=tv
+ end
+ end
+ end
+ end
+ local function unpackdeltas(main)
+ if main then
+ local deltas=main.deltas
+ if deltas then
+ for i=1,#deltas do
+ local di=deltas[i]
+ local d=di.deltas
+ local r=di.regions
+ for j=1,#d do
+ local tv=tables[d[j]]
+ if tv then
+ d[j]=tv
+ end
+ end
+ local tv=di.regions
+ if tv then
+ di.regions=tv
+ end
+ end
+ end
+ local regions=main.regions
+ if regions then
+ local tv=tables[regions]
+ if tv then
+ main.regions=tv
+ regions=tv
+ end
+ for i=1,#regions do
+ local r=regions[i]
+ for j=1,#r do
+ local tv=tables[r[j]]
+ if tv then
+ r[j]=tv
+ end
+ end
+ end
+ end
+ end
+ end
+ unpackdeltas(variable.global)
+ unpackdeltas(variable.horizontal)
+ unpackdeltas(variable.vertical)
+ unpackdeltas(variable.metrics)
+ end
data.tables=nil
end
end
@@ -15995,7 +17758,7 @@ local trace_defining=false registertracker("fonts.defining",function(v) trace_de
local report_otf=logs.reporter("fonts","otf loading")
local fonts=fonts
local otf=fonts.handlers.otf
-otf.version=3.027
+otf.version=3.028
otf.cache=containers.define("fonts","otl",otf.version,true)
otf.svgcache=containers.define("fonts","svg",otf.version,true)
otf.pdfcache=containers.define("fonts","pdf",otf.version,true)
@@ -16025,16 +17788,12 @@ registerdirective("fonts.otf.loader.force",function(v) forceload=v end)
registerdirective("fonts.otf.loader.syncspace",function(v) syncspace=v end)
registerdirective("fonts.otf.loader.forcenotdef",function(v) forcenotdef=v end)
registerotfenhancer("check extra features",function() end)
-function otf.load(filename,sub,featurefile)
- local featurefile=nil
+function otf.load(filename,sub,instance)
local base=file.basename(file.removesuffix(filename))
- local name=file.removesuffix(base)
+ local name=file.removesuffix(base)
local attr=lfs.attributes(filename)
local size=attr and attr.size or 0
local time=attr and attr.modification or 0
- if featurefile then
- name=name.."@"..file.removesuffix(file.basename(featurefile))
- end
if sub=="" then
sub=false
end
@@ -16042,6 +17801,9 @@ function otf.load(filename,sub,featurefile)
if sub then
hash=hash.."-"..sub
end
+ if instance then
+ hash=hash.."-"..instance
+ end
hash=containers.cleanname(hash)
local data=containers.read(otf.cache,hash)
local reload=not data or data.size~=size or data.time~=time or data.tableversion~=otfreaders.tableversion
@@ -16052,7 +17814,7 @@ function otf.load(filename,sub,featurefile)
if reload then
report_otf("loading %a, hash %a",filename,hash)
starttiming(otfreaders)
- data=otfreaders.loadfont(filename,sub or 1)
+ data=otfreaders.loadfont(filename,sub or 1,instance)
if data then
local resources=data.resources
local svgshapes=resources.svgshapes
@@ -16351,7 +18113,8 @@ local function otftotfm(specification)
local subindex=specification.subindex
local filename=specification.filename
local features=specification.features.normal
- local rawdata=otf.load(filename,sub,features and features.featurefile)
+ local instance=specification.instance or (features and features.axis)
+ local rawdata=otf.load(filename,sub,instance)
if rawdata and next(rawdata) then
local descriptions=rawdata.descriptions
rawdata.lookuphash={}
@@ -24619,9 +26382,14 @@ local function addfeature(data,feature,specifications)
end
return coverage
end
+ local function resetspacekerns()
+ data.properties.hasspacekerns=true
+ data.resources .spacekerns=nil
+ end
local function prepare_kern(list,featuretype)
local coverage={}
local cover=coveractions[featuretype]
+ local isspace=false
for code,replacement in next,list do
local unicode=tounicode(code)
local description=descriptions[unicode]
@@ -24631,11 +26399,17 @@ local function addfeature(data,feature,specifications)
local u=tounicode(k)
if u then
r[u]=v
+ if u==32 then
+ isspace=true
+ end
end
end
if next(r) then
cover(coverage,unicode,r)
done=done+1
+ if unicode==32 then
+ isspace=true
+ end
else
skip=skip+1
end
@@ -24643,6 +26417,9 @@ local function addfeature(data,feature,specifications)
skip=skip+1
end
end
+ if isspace then
+ resetspacekerns()
+ end
return coverage
end
local function prepare_pair(list,featuretype)
@@ -24658,11 +26435,17 @@ local function addfeature(data,feature,specifications)
local u=tounicode(k)
if u then
r[u]=v
+ if u==32 then
+ isspace=true
+ end
end
end
if next(r) then
cover(coverage,unicode,r)
done=done+1
+ if unicode==32 then
+ isspace=true
+ end
else
skip=skip+1
end
@@ -24670,6 +26453,9 @@ local function addfeature(data,feature,specifications)
skip=skip+1
end
end
+ if isspace then
+ resetspacekerns()
+ end
else
report_otf("unknown cover type %a",featuretype)
end
@@ -25242,7 +27028,7 @@ do
}
},
}
- fonts.handlers.otf.readers.parsecharstrings(data,glyphs,true,true)
+ fonts.handlers.otf.readers.parsecharstrings(false,data,glyphs,true,true)
else
lpegmatch(p_filternames,binary)
end
@@ -26983,11 +28769,28 @@ end
function resolvers.name(specification)
local resolve=fonts.names.resolve
if resolve then
- local resolved,sub,subindex=resolve(specification.name,specification.sub,specification)
+ local resolved,sub,subindex,instance=resolve(specification.name,specification.sub,specification)
if resolved then
specification.resolved=resolved
specification.sub=sub
specification.subindex=subindex
+ if instance then
+ specification.instance=instance
+ local features=specification.features
+ if not features then
+ features={}
+ specification.features=features
+ end
+ local normal=features.normal
+ if not normal then
+ normal={}
+ features.normal=normal
+ end
+ normal.instance=instance
+if not callbacks.supported.glyph_stream_provider then
+ normal.variableshapes=true
+end
+ end
local suffix=lower(suffixonly(resolved))
if fonts.formats[suffix] then
specification.forced=suffix