summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/font-cff.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkiv/font-cff.lua')
-rw-r--r--tex/context/base/mkiv/font-cff.lua1396
1 files changed, 1007 insertions, 389 deletions
diff --git a/tex/context/base/mkiv/font-cff.lua b/tex/context/base/mkiv/font-cff.lua
index 8c57b473e..1d4f01007 100644
--- a/tex/context/base/mkiv/font-cff.lua
+++ b/tex/context/base/mkiv/font-cff.lua
@@ -13,31 +13,39 @@ 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 = string.byte
+local byte, char, gmatch = string.byte, string.char, string.gmatch
local concat, remove = table.concat, table.remove
-local floor, abs, round, ceil = math.floor, math.abs, math.round, math.ceil
+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
local lpegmatch = lpeg.match
+local formatters = string.formatters
+local bytetable = string.bytetable
local readers = fonts.handlers.otf.readers
local streamreader = readers.streamreader
-local readbytes = streamreader.readbytes
local readstring = streamreader.readstring
local readbyte = streamreader.readcardinal1 -- 8-bit unsigned integer
local readushort = streamreader.readcardinal2 -- 16-bit unsigned integer
local readuint = streamreader.readcardinal3 -- 24-bit unsigned integer
-local readulong = streamreader.readcardinal4 -- 24-bit unsigned integer
+local readulong = streamreader.readcardinal4 -- 32-bit unsigned integer
local setposition = streamreader.setposition
local getposition = streamreader.getposition
+local readbytetable = streamreader.readbytetable
local setmetatableindex = table.setmetatableindex
@@ -49,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",
@@ -129,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
@@ -143,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
@@ -158,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
@@ -216,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
@@ -270,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
@@ -283,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()
@@ -326,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
@@ -376,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
@@ -501,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,
@@ -524,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
@@ -578,22 +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 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)
@@ -609,16 +663,17 @@ do
-- All these indirect calls make this run slower but it's cleaner this way
-- and we cache the result. As we moved the boundingbox code inline we gain
- -- some back.
+ -- some back. I inlined some of then and a bit speed can be gained by more
+ -- inlining but not that much.
- local function moveto(x,y)
+ local function xymoveto()
if keepcurve then
r = r + 1
result[r] = { x, y, "m" }
end
if checked then
- 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 x > xmax then xmax = x elseif x < xmin then xmin = x end
+ if y > ymax then ymax = y elseif y < ymin then ymin = y end
else
xmin = x
ymin = y
@@ -628,14 +683,58 @@ do
end
end
- local function lineto(x,y)
+ local function xmoveto() -- slight speedup
+ if keepcurve then
+ r = r + 1
+ result[r] = { x, y, "m" }
+ end
+ if not checked then
+ xmin = x
+ ymin = y
+ xmax = x
+ ymax = y
+ checked = true
+ elseif x > xmax then
+ xmax = x
+ elseif x < xmin then
+ xmin = x
+ end
+ end
+
+ local function ymoveto() -- slight speedup
+ if keepcurve then
+ r = r + 1
+ result[r] = { x, y, "m" }
+ end
+ if not checked then
+ xmin = x
+ ymin = y
+ xmax = x
+ ymax = y
+ checked = true
+ elseif y > ymax then
+ ymax = y
+ elseif y < ymin then
+ ymin = y
+ end
+ end
+
+ 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" }
end
if checked then
- 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 x > xmax then xmax = x elseif x < xmin then xmin = x end
+ if y > ymax then ymax = y elseif y < ymin then ymin = y end
else
xmin = x
ymin = y
@@ -645,14 +744,53 @@ do
end
end
- local function curveto(x1,y1,x2,y2,x3,y3)
+ local function xlineto() -- slight speedup
+ if keepcurve then
+ r = r + 1
+ result[r] = { x, y, "l" }
+ end
+ if not checked then
+ xmin = x
+ ymin = y
+ xmax = x
+ ymax = y
+ checked = true
+ elseif x > xmax then
+ xmax = x
+ elseif x < xmin then
+ xmin = x
+ end
+ end
+
+ local function ylineto() -- slight speedup
+ if keepcurve then
+ r = r + 1
+ result[r] = { x, y, "l" }
+ end
+ if not checked then
+ xmin = x
+ ymin = y
+ xmax = x
+ ymax = y
+ checked = true
+ elseif y > ymax then
+ ymax = y
+ elseif y < ymin then
+ ymin = y
+ end
+ end
+
+ 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" }
end
if checked 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 x1 > xmax then xmax = x1 elseif x1 < xmin then xmin = x1 end
+ if y1 > ymax then ymax = y1 elseif y1 < ymin then ymin = y1 end
else
xmin = x1
ymin = y1
@@ -660,22 +798,22 @@ do
ymax = y1
checked = true
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 x3 < xmin then xmin = x3 elseif x3 > xmax then xmax = x3 end
- if y3 < ymin then ymin = y3 elseif y3 > ymax then ymax = y3 end
+ if x2 > xmax then xmax = x2 elseif x2 < xmin then xmin = x2 end
+ if y2 > ymax then ymax = y2 elseif y2 < ymin then ymin = y2 end
+ if x3 > xmax then xmax = x3 elseif x3 < xmin then xmin = x3 end
+ if y3 > ymax then ymax = y3 elseif y3 < ymin then ymin = y3 end
end
local function rmoveto()
- if top > 2 then
- if not width then
+ if not width then
+ if top > 2 then
width = stack[1]
if trace_charstrings then
- showvalue("width",width)
+ showvalue("backtrack width",width)
end
+ else
+ width = true
end
- elseif not width then
- width = true
end
if trace_charstrings then
showstate("rmoveto")
@@ -683,45 +821,45 @@ do
x = x + stack[top-1] -- dx1
y = y + stack[top] -- dy1
top = 0
- moveto(x,y)
+ xymoveto()
end
local function hmoveto()
- if top > 1 then
- if not width then
+ if not width then
+ if top > 1 then
width = stack[1]
if trace_charstrings then
- showvalue("width",width)
+ showvalue("backtrack width",width)
end
+ else
+ width = true
end
- elseif not width then
- width = true
end
if trace_charstrings then
showstate("hmoveto")
end
x = x + stack[top] -- dx1
top = 0
- moveto(x,y)
+ xmoveto()
end
local function vmoveto()
- if top > 1 then
- if not width then
+ if not width then
+ if top > 1 then
width = stack[1]
if trace_charstrings then
- showvalue("width",width)
+ showvalue("backtrack width",width)
end
+ else
+ width = true
end
- elseif not width then
- width = true
end
if trace_charstrings then
showstate("vmoveto")
end
y = y + stack[top] -- dy1
top = 0
- moveto(x,y)
+ ymoveto()
end
local function rlineto()
@@ -731,21 +869,7 @@ do
for i=1,top,2 do
x = x + stack[i] -- dxa
y = y + stack[i+1] -- dya
- lineto(x,y)
- end
- top = 0
- end
-
- local function xlineto(swap) -- x (y,x)+ | (x,y)+
- for i=1,top do
- if swap then
- x = x + stack[i]
- swap = false
- else
- y = y + stack[i]
- swap = true
- end
- lineto(x,y)
+ xylineto()
end
top = 0
end
@@ -754,14 +878,48 @@ do
if trace_charstrings then
showstate("hlineto")
end
- xlineto(true)
+ if top == 1 then
+ x = x + stack[1]
+ xlineto()
+ else
+ local swap = true
+ for i=1,top do
+ if swap then
+ x = x + stack[i]
+ xlineto()
+ swap = false
+ else
+ y = y + stack[i]
+ ylineto()
+ swap = true
+ end
+ end
+ end
+ top = 0
end
local function vlineto() -- y (x,y)+ | (y,x)+
if trace_charstrings then
showstate("vlineto")
end
- xlineto(false)
+ if top == 1 then
+ y = y + stack[1]
+ ylineto()
+ else
+ local swap = false
+ for i=1,top do
+ if swap then
+ x = x + stack[i]
+ xlineto()
+ swap = false
+ else
+ y = y + stack[i]
+ ylineto()
+ swap = true
+ end
+ end
+ end
+ top = 0
end
local function rrcurveto()
@@ -773,9 +931,9 @@ do
local ay = y + stack[i+1] -- dya
local bx = ax + stack[i+2] -- dxb
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)
+ x = bx + stack[i+4] -- dxc
+ y = by + stack[i+5] -- dyc
+ xycurveto(ax,ay,bx,by,x,y)
end
top = 0
end
@@ -786,17 +944,17 @@ do
end
local s = 1
if top % 2 ~= 0 then
- y = y + stack[1] -- dy1
+ y = y + stack[1] -- dy1
s = 2
end
for i=s,top,4 do
- local ax = x + stack[i] -- dxa
+ local ax = x + stack[i] -- dxa
local ay = y
local bx = ax + stack[i+1] -- dxb
local by = ay + stack[i+2] -- dyb
- x = bx + stack[i+3] -- dxc
+ 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
@@ -808,28 +966,64 @@ do
local s = 1
local d = 0
if top % 2 ~= 0 then
- d = stack[1] -- dx1
+ d = stack[1] -- dx1
s = 2
end
for i=s,top,4 do
local ax = x + d
- local ay = y + stack[i] -- dya
+ local ay = y + stack[i] -- dya
local bx = ax + stack[i+1] -- dxb
local by = ay + stack[i+2] -- dyb
x = bx
- y = by + stack[i+3] -- dyc
- curveto(ax,ay,bx,by,x,y)
+ y = by + stack[i+3] -- dyc
+ xycurveto(ax,ay,bx,by,x,y)
d = 0
end
top = 0
end
+-- local function xxcurveto(swap)
+-- local last = top % 4 ~= 0 and stack[top]
+-- if last then
+-- top = top - 1
+-- end
+-- for i=1,top,4 do
+-- local ax, ay, bx, by
+-- if swap then
+-- ax = x + stack[i]
+-- ay = y
+-- bx = ax + stack[i+1]
+-- by = ay + stack[i+2]
+-- y = by + stack[i+3]
+-- if last and i+3 == top then
+-- x = bx + last
+-- else
+-- x = bx
+-- end
+-- swap = false
+-- else
+-- ax = x
+-- ay = y + stack[i]
+-- bx = ax + stack[i+1]
+-- by = ay + stack[i+2]
+-- x = bx + stack[i+3]
+-- if last and i+3 == top then
+-- y = by + last
+-- else
+-- y = by
+-- end
+-- swap = true
+-- end
+-- xycurveto(ax,ay,bx,by,x,y)
+-- end
+-- top = 0
+-- end
+
local function xxcurveto(swap)
local last = top % 4 ~= 0 and stack[top]
if last then
top = top - 1
end
- local sw = swap
for i=1,top,4 do
local ax, ay, bx, by
if swap then
@@ -857,11 +1051,12 @@ do
end
swap = true
end
- curveto(ax,ay,bx,by,x,y)
+ xycurveto(ax,ay,bx,by,x,y)
end
top = 0
end
+
local function hvcurveto()
if trace_charstrings then
showstate("hvcurveto")
@@ -887,11 +1082,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(x,y)
+ xylineto()
top = 0
end
@@ -903,7 +1098,7 @@ do
for i=1,top-6,2 do
x = x + stack[i]
y = y + stack[i+1]
- lineto(x,y)
+ xylineto()
end
end
local ax = x + stack[top-5]
@@ -912,7 +1107,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
@@ -928,14 +1123,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
@@ -943,19 +1138,19 @@ do
if trace_charstrings then
showstate("hflex")
end
- local ax = x + stack[1] -- dx1
+ local ax = x + stack[1] -- dx1
local ay = y
local bx = ax + stack[2] -- dx2
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)
+ x = ex + stack[7] -- dx6
+ xycurveto(dx,dy,ex,ey,x,y)
top = 0
end
@@ -969,13 +1164,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)
+ x = ex + stack[9] -- dx6
+ xycurveto(dx,dy,ex,ey,x,y)
top = 0
end
@@ -989,7 +1184,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
@@ -999,7 +1194,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
@@ -1052,13 +1247,216 @@ do
end
end
- local function unsupported()
+ local function unsupported(t)
+ if trace_charstrings then
+ showstate("unsupported " .. t)
+ end
+ top = 0
+ end
+
+ local function unsupportedsub(t)
+ if trace_charstrings then
+ showstate("unsupported sub " .. t)
+ end
+ top = 0
+ end
+
+ -- type 1 (not used in type 2)
+
+ local function getstem3()
if trace_charstrings then
- showstate("unsupported")
+ showstate("stem3")
+ end
+ top = 0
+ end
+
+ local function divide()
+ if version == 1 then
+ local d = stack[top]
+ top = top - 1
+ stack[top] = stack[top] / d
+ end
+ end
+
+ local function closepath()
+ if version == 1 then
+ if trace_charstrings then
+ showstate("closepath")
+ end
+ end
+ top = 0
+ end
+
+ local function hsbw()
+ if version == 1 then
+ if trace_charstrings then
+ showstate("dotsection")
+ end
+ width = stack[top]
+ end
+ top = 0
+ end
+
+ local function seac()
+ if version == 1 then
+ if trace_charstrings then
+ showstate("seac")
+ end
+ end
+ top = 0
+ end
+
+ local function sbw()
+ if version == 1 then
+ if trace_charstrings then
+ showstate("sbw")
+ end
+ width = stack[top-1]
+ end
+ top = 0
+ end
+
+ -- these are probably used for special cases i.e. call out to postscript
+
+ local function callothersubr()
+ if version == 1 then
+ -- we don't support this (ok, we could mimick these othersubs)
+ if trace_charstrings then
+ showstate("callothersubr (unsupported)")
+ end
+ end
+ top = 0
+ end
+
+ local function pop()
+ if version == 1 then
+ -- we don't support this
+ if trace_charstrings then
+ showstate("pop (unsupported)")
+ end
+ top = top + 1
+ stack[top] = 0 -- a dummy
+ else
+ top = 0
+ end
+ end
+
+ local function setcurrentpoint()
+ if version == 1 then
+ -- we don't support this
+ if trace_charstrings then
+ showstate("pop (unsupported)")
+ end
+ x = x + stack[top-1]
+ y = y + stack[top]
end
top = 0
end
+ -- 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.
@@ -1076,10 +1474,10 @@ do
unsupported, -- 10 -- calllocal,
unsupported, -- 11 -- callreturn,
unsupported, -- 12 -- elsewhere
- unsupported, -- 13 -- hsbw
+ 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
@@ -1098,82 +1496,190 @@ do
}
local subactions = {
+ -- cff 1
+ [000] = dotsection,
+ [001] = getstem3,
+ [002] = getstem3,
+ [006] = seac,
+ [007] = sbw,
+ [012] = divide,
+ [016] = callothersubr,
+ [017] = pop,
+ [033] = setcurrentpoint,
+ -- cff 2
[034] = hflex,
[035] = flex,
[036] = hflex1,
[037] = flex1,
}
- local p_bytes = Ct((P(1)/byte)^0)
+ local c_endchar = char(14)
- local function call(scope,list,bias,process)
- local index = stack[top] + bias
- top = top - 1
- if trace_charstrings then
- showvalue(scope,index,true)
+ 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
- local str = list[index]
- if str then
- if type(str) == "string" then
- str = lpegmatch(p_bytes,str)
- list[index] = str
+
+ 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
- depth = depth + 1
- process(str)
- depth = depth - 1
+ end
+
+ end
+
+ -- end of experiment
+
+ local process
+
+ local function call(scope,list,bias) -- ,process)
+ depth = depth + 1
+ if top == 0 then
+ showstate(formatters["unknown %s call"](scope))
+ top = 0
else
- report("unknown %s %i",scope,index)
+ local index = stack[top] + bias
+ top = top - 1
+ if trace_charstrings then
+ showvalue(scope,index,true)
+ end
+ local tab = list[index]
+ if tab then
+ process(tab)
+ else
+ showstate(formatters["unknown %s call %i"](scope,index))
+ top = 0
+ end
end
+ depth = depth - 1
end
- local function process(tab)
+ -- precompiling and reuse is much slower than redoing the calls
+
+ local justpass = false
+
+ process = function(tab)
local i = 1
local n = #tab
while i <= n do
local t = tab[i]
- if t >= 32 and t<=246 then
- -- -107 .. +107
+ if t >= 32 then
top = top + 1
- stack[top] = t - 139
- i = i + 1
- elseif t >= 247 and t <= 250 then
- -- +108 .. +1131
- top = top + 1
- stack[top] = (t-247)*256 + tab[i+1] + 108
- i = i + 2
- elseif t >= 251 and t <= 254 then
- -- -1131 .. -108
- top = top + 1
- stack[top] = -(t-251)*256 - tab[i+1] - 108
- i = i + 2
+ if t <= 246 then
+ -- -107 .. +107
+ stack[top] = t - 139
+ i = i + 1
+ elseif t <= 250 then
+ -- +108 .. +1131
+ -- stack[top] = (t-247)*256 + tab[i+1] + 108
+ -- stack[top] = t*256 - 247*256 + tab[i+1] + 108
+ stack[top] = t*256 - 63124 + tab[i+1]
+ i = i + 2
+ elseif t <= 254 then
+ -- -1131 .. -108
+ -- stack[top] = -(t-251)*256 - tab[i+1] - 108
+ -- stack[top] = -t*256 + 251*256 - tab[i+1] - 108
+ stack[top] = -t*256 + 64148 - tab[i+1]
+ i = i + 2
+ else
+ local n = 0x100 * tab[i+1] + tab[i+2]
+ if n >= 0x8000 then
+ -- stack[top] = n - 0xFFFF - 1 + (0x100 * tab[i+3] + tab[i+4])/0xFFFF
+ stack[top] = n - 0x10000 + (0x100 * tab[i+3] + tab[i+4])/0xFFFF
+ else
+ stack[top] = n + (0x100 * tab[i+3] + tab[i+4])/0xFFFF
+ end
+ i = i + 5
+ end
elseif t == 28 then
-- -32768 .. +32767 : b1<<8 | b2
top = top + 1
local n = 0x100 * tab[i+1] + tab[i+2]
if n >= 0x8000 then
- stack[top] = n - 0xFFFF - 1
+ -- stack[top] = n - 0xFFFF - 1
+ stack[top] = n - 0x10000
else
stack[top] = n
end
i = i + 3
- elseif t == 255 then
- local n = 0x100 * tab[i+1] + tab[i+2]
- top = top + 1
- if n >= 0x8000 then
- stack[top] = n - 0xFFFF - 1 + (0x100 * tab[i+3] + tab[i+4])/0xFFFF
- else
- stack[top] = n + (0x100 * tab[i+3] + tab[i+4])/0xFFFF
- end
- i = i + 5
- elseif t == 11 then
+ elseif t == 11 then -- not in cff2
if trace_charstrings then
showstate("return")
end
return
elseif t == 10 then
- call("local",locals,localbias,process)
+ 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
@@ -1189,14 +1695,14 @@ do
end
return
elseif t == 29 then
- call("global",globals,globalbias,process)
+ call("global",globals,globalbias) -- ,process)
i = i + 1
elseif t == 12 then
i = i + 1
local t = tab[i]
local a = subactions[t]
if a then
- a()
+ a(t)
else
if trace_charstrings then
showvalue("<subaction>",t)
@@ -1204,20 +1710,25 @@ do
top = 0
end
i = i + 1
+ elseif justpass then
+ passon(t)
+ i = i + 1
else
local a = actions[t]
if a then
- local s = a()
+ local s = a(t)
if s then
- i = i + s
+ i = i + s + 1
+ else
+ i = i + 1
end
else
if trace_charstrings then
showvalue("<action>",t)
end
top = 0
+ i = i + 1
end
- i = i + 1
end
end
end
@@ -1236,7 +1747,7 @@ do
-- if y < ymin then ymin = y end
-- if y > ymax then ymax = y end
-- -- we now have a reasonable start so we could
- -- -- simplyfy the next checks
+ -- -- simplify the next checks
-- for i=1,nofsegments do
-- local s = segments[i]
-- local x = s[1]
@@ -1260,142 +1771,76 @@ do
-- end
-- end
- parsecharstrings = function(data,glyphs,doshapes)
- -- for all charstrings
- local dictionary = data.dictionaries[1]
- local charstrings = dictionary.charstrings
- local charset = dictionary.charset
- keepcurve = doshapes
- stack = { }
- glyphs = glyphs or { }
- strings = data.strings
- locals = dictionary.subroutines
- globals = data.routines
- globalbias = #globals
- localbias = #locals
- globalbias = ((globalbias < 1240 and 107) or (globalbias < 33900 and 1131) or 32768) + 1
- localbias = ((localbias < 1240 and 107) or (localbias < 33900 and 1131) or 32768) + 1
- local nominalwidth = dictionary.private.data.nominalwidthx or 0
- local defaultwidth = dictionary.private.data.defaultwidthx or 0
-
- for i=1,#charstrings do
- local str = charstrings[i]
- local tab = lpegmatch(p_bytes,str)
- local index = i - 1
- x = 0
- y = 0
- width = false
- r = 0
- top = 0
- stems = 0
- result = { }
- --
- xmin = 0
- xmax = 0
- ymin = 0
- ymax = 0
- checked = false
- --
- if trace_charstrings then
- report("glyph: %i",index)
- report("data: % t",tab)
- end
- --
- process(tab)
- --
- local boundingbox = { round(xmin), round(ymin), round(xmax), round(ymax) }
- --
- if width == true or width == false then
- width = defaultwidth
- else
- width = nominalwidth + width
- end
- --
- -- trace_charstrings = index == 3078 -- todo: make tracker
- local glyph = glyphs[index] -- can be autodefined in otr
- if not glyph then
- glyphs[index] = {
- segments = doshapes ~= false and result or nil, -- optional
- boundingbox = boundingbox,
- width = width,
- name = charset[index],
- -- sidebearing = 0,
- }
- else
- 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
- end
- if trace_charstrings then
- report("width: %s",tostring(width))
- report("boundingbox: % t",boundingbox)
- end
- charstrings[i] = nil -- free memory
+ local function setbias(globals,locals)
+ if version == 1 then
+ return
+ false,
+ false
+ else
+ local g, l = #globals, #locals
+ return
+ ((g < 1240 and 107) or (g < 33900 and 1131) or 32768) + 1,
+ ((l < 1240 and 107) or (l < 33900 and 1131) or 32768) + 1
end
- return glyphs
end
- parsecharstring = function(data,dictionary,charstring,glyphs,index,doshapes)
- local private = dictionary.private
- keepcurve = doshapes
- strings = data.strings -- or in dict?
- locals = dictionary.subroutines or { }
- globals = data.routines or { }
- globalbias = #globals
- localbias = #locals
- globalbias = ((globalbias < 1240 and 107) or (globalbias < 33900 and 1131) or 32768) + 1
- localbias = ((localbias < 1240 and 107) or (localbias < 33900 and 1131) or 32768) + 1
- local nominalwidth = private and private.data.nominalwidthx or 0
- local defaultwidth = private and private.data.defaultwidthx or 0
- --
- local tab = lpegmatch(p_bytes,charstring)
- x = 0
- y = 0
- width = false
- r = 0
- top = 0
- stems = 0
- result = { }
- --
- xmin = 0
- xmax = 0
- ymin = 0
- ymax = 0
- checked = false
- --
+ local function processshape(tab,index)
+
+ tab = bytetable(tab)
+
+ 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)
+ report("data : % t",tab)
end
- --
+
+ if regions then
+ updateregions(vsindex)
+ end
+
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 not glyph then
- glyphs[index] = {
- segments = doshapes ~= false and result or nil, -- optional
- boundingbox = boundingbox,
- width = width,
- name = charset[index],
- -- sidebearing = 0,
- }
- else
- glyph.segments = doshapes ~= false and result or nil
+ if justpass then
+ r = r + 1
+ result[r] = c_endchar
+ local stream = concat(result)
+ -- if trace_charstrings then
+ -- report("vdata: %s",stream)
+ -- end
+ 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
@@ -1404,20 +1849,107 @@ index = index - 1
glyph.name = charset[index]
end
-- glyph.sidebearing = 0 -- todo
+ elseif keepcurve then
+ glyphs[index] = {
+ segments = result,
+ boundingbox = boundingbox,
+ width = width,
+ name = charset and charset[index] or nil,
+ -- sidebearing = 0,
+ }
+ else
+ glyphs[index] = {
+ boundingbox = boundingbox,
+ width = width,
+ 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
- --
- return charstring
+
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
@@ -1425,7 +1957,7 @@ end
local function readglobals(f,data)
local routines = readlengths(f)
for i=1,#routines do
- routines[i] = readstring(f,routines[i])
+ routines[i] = readbytetable(f,routines[i])
end
data.routines = routines
end
@@ -1439,8 +1971,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" }
@@ -1466,6 +1997,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
@@ -1488,7 +2022,7 @@ local function readlocals(f,data,dictionary)
setposition(f,header.offset+private.offset+subroutineoffset)
local subroutines = readlengths(f)
for i=1,#subroutines do
- subroutines[i] = readstring(f,subroutines[i])
+ subroutines[i] = readbytetable(f,subroutines[i])
end
dictionary.subroutines = subroutines
private.data.subroutines = nil
@@ -1503,16 +2037,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])
@@ -1520,7 +2056,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
@@ -1542,29 +2078,38 @@ local function readcidprivates(f,data)
parseprivates(data,dictionaries)
end
-local function readnoselect(f,data,glyphs,doshapes)
+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
+ dictionary.charset = nil
+ else
+ readencodings(f,data)
+ readcharsets(f,data,dictionary)
+ end
readprivates(f,data)
parseprivates(data,data.dictionaries)
readlocals(f,data,dictionary)
- parsecharstrings(data,glyphs,doshapes)
- resetcharstrings()
+ startparsing(fontdata,data,streams)
+ parsecharstrings(fontdata,data,glyphs,doshapes,version,streams)
+ stopparsing(fontdata,data)
end
-local function readfdselect(f,data,glyphs,doshapes)
+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
@@ -1601,6 +2146,7 @@ local function readfdselect(f,data,glyphs,doshapes)
else
-- unsupported format
end
+ -- hm, always
if maxindex >= 0 then
local cidarray = cid.fdarray
setposition(f,header.offset+cidarray)
@@ -1614,85 +2160,157 @@ local function readfdselect(f,data,glyphs,doshapes)
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)
+ 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
+ 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 offset = dictionaries[1].vstore
+ if offset > 0 then
+ local storeoffset = dictionaries[1].vstore + data.header.offset + 2 -- cff has a preceding size field
+ local regions, deltas = readers.helpers.readvariationdata(f,storeoffset,factors)
--
- -- 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
+ data.regions = regions
+ data.deltas = deltas
+ else
+ data.regions = { }
+ data.deltas = { }
+ end
+ 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