summaryrefslogtreecommitdiff
path: root/tex/context
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 /tex/context
parent8f5c555274eb48fcaaa3d7f340ee77710846fb7e (diff)
downloadcontext-8e0089484acf80066e7393b1245d59dda211be09.tar.gz
2017-03-20 17:38:00
Diffstat (limited to 'tex/context')
-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
42 files changed, 4716 insertions, 1401 deletions
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