You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1379 lines
58 KiB
1379 lines
58 KiB
if not modules then modules = { } end modules ['font-ttf'] = {
|
|
version = 1.001,
|
|
comment = "companion to font-ini.mkiv",
|
|
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
|
|
copyright = "PRAGMA ADE / ConTeXt Development Team",
|
|
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 band, rshift = bit32.band, bit32.rshift
|
|
local sqrt, round = math.sqrt, math.round
|
|
local char, rep = string.char, string.rep
|
|
local concat = table.concat
|
|
local idiv = number.idiv
|
|
local setmetatableindex = table.setmetatableindex
|
|
|
|
local report = logs.reporter("otf reader","ttf")
|
|
|
|
local trace_deltas = false
|
|
|
|
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 readinteger = streamreader.readinteger1
|
|
local readcardinaltable = streamreader.readcardinaltable
|
|
local readintegertable = streamreader.readintegertable
|
|
|
|
directives.register("fonts.streamreader",function()
|
|
|
|
streamreader = utilities.streams
|
|
|
|
setposition = streamreader.setposition
|
|
getposition = streamreader.getposition
|
|
skipbytes = streamreader.skip
|
|
readbyte = streamreader.readcardinal1
|
|
readushort = streamreader.readcardinal2
|
|
readulong = streamreader.readcardinal4
|
|
readchar = streamreader.readinteger1
|
|
readshort = streamreader.readinteger2
|
|
read2dot14 = streamreader.read2dot14
|
|
readinteger = streamreader.readinteger1
|
|
readcardinaltable = streamreader.readcardinaltable
|
|
readintegertable = streamreader.readintegertable
|
|
|
|
end)
|
|
|
|
local short = 2
|
|
local ushort = 2
|
|
local ulong = 4
|
|
|
|
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, subpoints = merge(subindex,subshape,subcomponents)
|
|
end
|
|
end
|
|
if subpoints then
|
|
local matrix = component.matrix
|
|
local xscale = matrix[1]
|
|
local xrotate = matrix[2]
|
|
local yrotate = matrix[3]
|
|
local yscale = matrix[4]
|
|
local xoffset = matrix[5]
|
|
local yoffset = matrix[6]
|
|
local count = #subpoints
|
|
if xscale == 1 and yscale == 1 and xrotate == 0 and yrotate == 0 then
|
|
for i=1,count do
|
|
local p = subpoints[i]
|
|
nofpoints = nofpoints + 1
|
|
points[nofpoints] = {
|
|
p[1] + xoffset,
|
|
p[2] + yoffset,
|
|
p[3]
|
|
}
|
|
end
|
|
else
|
|
for i=1,count 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
|
|
end
|
|
local subcount = #subcontours
|
|
if subcount == 1 then
|
|
nofcontours = nofcontours + 1
|
|
contours[nofcontours] = offset + subcontours[1]
|
|
else
|
|
for i=1,#subcontours do
|
|
nofcontours = nofcontours + 1
|
|
contours[nofcontours] = offset + subcontours[i]
|
|
end
|
|
end
|
|
offset = offset + count
|
|
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, points
|
|
end
|
|
|
|
-- for index=1,#glyphs do
|
|
for index=0,#glyphs-1 do
|
|
local shape = shapes[index]
|
|
if shape then
|
|
local components = shape.components
|
|
if components then
|
|
merge(index,shape,components)
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
local function readnothing(f)
|
|
return {
|
|
type = "nothing",
|
|
}
|
|
end
|
|
|
|
-- begin of converter
|
|
|
|
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),
|
|
r_x + 2/3 *(m_x-r_x), r_y + 2/3 *(m_y-r_y),
|
|
r_x, r_y, "c"
|
|
end
|
|
|
|
-- We could omit the operator which saves some 10%:
|
|
--
|
|
-- #2=lineto #4=quadratic #6=cubic #3=moveto (with "m")
|
|
--
|
|
-- 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).
|
|
--
|
|
-- 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 applyaxis(glyph,shape,deltas,dowidth)
|
|
local points = shape.points
|
|
if points then
|
|
local nofpoints = #points
|
|
local h = nofpoints + 2 -- weird, the example font seems to have left first
|
|
local l = nofpoints + 1
|
|
----- v = nofpoints + 3
|
|
----- t = nofpoints + 4
|
|
local dw = 0
|
|
local dl = 0
|
|
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[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
|
|
elseif dowidth then
|
|
-- we've now ran into phantom points which is a bit fuzzy because:
|
|
-- are there gaps in there?
|
|
--
|
|
-- todo: move this outside the loop (when we can be sure of all 4 being there)
|
|
if d == h then
|
|
-- we have a phantom point hadvance
|
|
local x = xvalues[i]
|
|
if x then
|
|
dw = dw + factor * x
|
|
end
|
|
elseif d == l then
|
|
local x = xvalues[i]
|
|
if x then
|
|
dl = dl + 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
|
|
if dowidth then
|
|
local x = xvalues[h]
|
|
if x then
|
|
dw = dw + factor * x
|
|
end
|
|
local x = xvalues[l]
|
|
if x then
|
|
dl = dl + factor * x
|
|
end
|
|
end
|
|
end
|
|
end
|
|
-- for i=1,nofpoints do
|
|
-- local p = points[i]
|
|
-- p[1] = round(p[1])
|
|
-- p[2] = round(p[2])
|
|
-- end
|
|
if dowidth then
|
|
local width = glyph.width or 0
|
|
-- local lsb = glyph.lsb or 0
|
|
glyph.width = width + dw - dl
|
|
end
|
|
else
|
|
report("no points for glyph %a",glyph.name)
|
|
end
|
|
end
|
|
|
|
-- round or not ?
|
|
|
|
-- local quadratic = true -- both methods work, todo: install a directive
|
|
local quadratic = false
|
|
|
|
local function contours2outlines_normal(glyphs,shapes) -- maybe accept the bbox overhead
|
|
-- for index=1,#glyphs do
|
|
for index=0,#glyphs-1 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 = { }
|
|
local nofsegments = 0
|
|
glyph.segments = segments
|
|
if nofcontours > 0 then
|
|
local px = 0
|
|
local py = 0
|
|
local first = 1
|
|
for i=1,nofcontours do
|
|
local last = contours[i]
|
|
if last >= first then
|
|
local first_pt = points[first]
|
|
local first_on = first_pt[3]
|
|
-- 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
|
|
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
|
|
local x = first_pt[1]
|
|
local y = first_pt[2]
|
|
if not done then
|
|
xmin = x
|
|
ymin = y
|
|
xmax = x
|
|
ymax = y
|
|
done = true
|
|
end
|
|
nofsegments = nofsegments + 1
|
|
segments[nofsegments] = { x, y, "m" } -- "moveto"
|
|
if not quadratic then
|
|
px = x
|
|
py = 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 = control_pt[1]
|
|
local y1 = control_pt[2]
|
|
local x2 = current_pt[1]
|
|
local y2 = current_pt[2]
|
|
nofsegments = nofsegments + 1
|
|
if quadratic then
|
|
segments[nofsegments] = { x1, y1, x2, y2, "q" } -- "quadraticto"
|
|
else
|
|
x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2)
|
|
segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" } -- "curveto"
|
|
end
|
|
control_pt = false
|
|
else
|
|
local x2 = (previous_pt[1]+current_pt[1])/2
|
|
local y2 = (previous_pt[2]+current_pt[2])/2
|
|
local x1 = control_pt[1]
|
|
local y1 = control_pt[2]
|
|
nofsegments = nofsegments + 1
|
|
if quadratic then
|
|
segments[nofsegments] = { x1, y1, x2, y2, "q" } -- "quadraticto"
|
|
else
|
|
x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2)
|
|
segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" } -- "curveto"
|
|
end
|
|
control_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
|
|
local x2 = first_pt[1]
|
|
local y2 = first_pt[2]
|
|
if not control_pt then
|
|
segments[nofsegments] = { x2, y2, "l" } -- "lineto"
|
|
elseif quadratic then
|
|
local x1 = control_pt[1]
|
|
local y1 = control_pt[2]
|
|
segments[nofsegments] = { x1, y1, x2, y2, "q" } -- "quadraticto"
|
|
else
|
|
local x1 = control_pt[1]
|
|
local y1 = control_pt[2]
|
|
x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2)
|
|
segments[nofsegments] = { x1, y1, x2, 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)
|
|
-- for index=1,#glyphs do
|
|
for index=0,#glyphs-1 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
|
|
first_pt = { (first_pt[1]+last_pt[1])/2, (first_pt[2]+last_pt[2])/2, false }
|
|
end
|
|
control_pt = first_pt
|
|
end
|
|
local x = first_pt[1]
|
|
local y = 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
|
|
segments[nofsegments] = { x, y, "m" } -- "moveto"
|
|
end
|
|
if not quadratic then
|
|
px = x
|
|
py = 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 = current_pt[1]
|
|
local y = 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 = x
|
|
py = y
|
|
end
|
|
else
|
|
control_pt = current_pt
|
|
end
|
|
elseif current_on then
|
|
local x1 = control_pt[1]
|
|
local y1 = control_pt[2]
|
|
local x2 = current_pt[1]
|
|
local y2 = 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, y1, x2, y2, px, py = curveto(x1, y1, 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 x2 = (previous_pt[1]+current_pt[1])/2
|
|
local y2 = (previous_pt[2]+current_pt[2])/2
|
|
local x1 = control_pt[1]
|
|
local y1 = 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, y1, x2, y2, px, py = curveto(x1, y1, 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
|
|
previous_pt = current_pt
|
|
end
|
|
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
|
|
else
|
|
local x1 = control_pt[1]
|
|
local y1 = control_pt[2]
|
|
local x2 = first_pt[1]
|
|
local y2 = 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 quadratic then
|
|
if keepcurve then
|
|
nofsegments = nofsegments + 1
|
|
segments[nofsegments] = { x1, y1, x2, y2, "q" } -- "quadraticto"
|
|
end
|
|
else
|
|
x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2)
|
|
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
|
|
-- px, py = x2, y2
|
|
end
|
|
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 shorthash = setmetatableindex(function(t,k)
|
|
-- t[k] = char(band(rshift(k,8),0xFF),band(k,0xFF)) return t[k]
|
|
-- end)
|
|
|
|
local function toushort(n)
|
|
return char(band(rshift(n,8),0xFF),band(n,0xFF))
|
|
-- return shorthash[n]
|
|
end
|
|
|
|
local function toshort(n)
|
|
if n < 0 then
|
|
n = n + 0x10000
|
|
end
|
|
return char(band(rshift(n,8),0xFF),band(n,0xFF))
|
|
-- return shorthash[n]
|
|
end
|
|
|
|
-- todo: we can reuse result, xpoints and ypoints
|
|
|
|
local chars = setmetatableindex(function(t,k)
|
|
for i=0,255 do local v = char(i) t[i] = v end return t[k]
|
|
end)
|
|
|
|
local function repackpoints(glyphs,shapes)
|
|
local noboundingbox = { 0, 0, 0, 0 }
|
|
local result = { } -- reused
|
|
local xpoints = { } -- reused
|
|
local ypoints = { } -- reused
|
|
for index=0,#glyphs-1 do
|
|
local shape = shapes[index]
|
|
if shape then
|
|
local r = 0
|
|
local glyph = glyphs[index]
|
|
local contours = shape.contours
|
|
local nofcontours = contours and #contours or 0
|
|
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)
|
|
x = x + 1
|
|
if dx < -255 or dx > 255 then
|
|
xpoints[x] = toshort(dx)
|
|
elseif dx < 0 then
|
|
fl = fl + 0x02
|
|
-- xpoints[x] = char(-dx)
|
|
xpoints[x] = chars[-dx]
|
|
elseif dx > 0 then
|
|
fl = fl + 0x12
|
|
-- xpoints[x] = char(dx)
|
|
xpoints[x] = chars[dx]
|
|
else
|
|
fl = fl + 0x02
|
|
xpoints[x] = c_zero
|
|
end
|
|
end
|
|
if py == currenty then
|
|
fl = fl + 0x20
|
|
else
|
|
local dy = round(py - currenty)
|
|
y = y + 1
|
|
if dy < -255 or dy > 255 then
|
|
ypoints[y] = toshort(dy)
|
|
elseif dy < 0 then
|
|
fl = fl + 0x04
|
|
-- ypoints[y] = char(-dy)
|
|
ypoints[y] = chars[-dy]
|
|
elseif dy > 0 then
|
|
fl = fl + 0x24
|
|
-- ypoints[y] = char(dy)
|
|
ypoints[y] = chars[dy]
|
|
else
|
|
fl = fl + 0x04
|
|
ypoints[y] = c_zero
|
|
end
|
|
end
|
|
currentx = px
|
|
currenty = py
|
|
if lastflag == fl then
|
|
if nofflags == 255 then
|
|
-- This happens in koeieletters!
|
|
lastflag = lastflag + 0x08
|
|
r = r + 1 result[r] = char(lastflag,nofflags-1)
|
|
nofflags = 1
|
|
lastflag = fl
|
|
else
|
|
nofflags = nofflags + 1
|
|
end
|
|
else -- if > 255
|
|
if nofflags == 1 then
|
|
-- r = r + 1 result[r] = char(lastflag)
|
|
r = r + 1 result[r] = chars[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)
|
|
r = r + 1 result[r] = chars[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)
|
|
r = r + 1 result[r] = concat(xpoints,"",1,x)
|
|
r = r + 1 result[r] = concat(ypoints,"",1,y)
|
|
end
|
|
-- can be helper or delegated to user
|
|
local stream = concat(result,"",1,r)
|
|
local length = #stream
|
|
local padding = idiv(length+3,4) * 4 - length
|
|
if padding > 0 then
|
|
-- stream = stream .. rep("\0",padding) -- can be a repeater
|
|
if padding == 1 then
|
|
padding = "\0"
|
|
elseif padding == 2 then
|
|
padding = "\0\0"
|
|
else
|
|
padding = "\0\0\0"
|
|
end
|
|
padding = stream .. padding
|
|
end
|
|
glyph.stream = stream
|
|
end
|
|
end
|
|
end
|
|
|
|
-- end of converter
|
|
|
|
local flags = { }
|
|
|
|
local function readglyph(f,nofcontours) -- read deltas here, saves space
|
|
local points = { }
|
|
-- local instructions = { }
|
|
local contours = { } -- readintegertable(f,nofcontours,short)
|
|
for i=1,nofcontours do
|
|
contours[i] = readshort(f) + 1
|
|
end
|
|
local nofpoints = contours[nofcontours]
|
|
local nofinstructions = readushort(f)
|
|
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)
|
|
local i = 1
|
|
while i <= nofpoints do
|
|
local flag = readbyte(f)
|
|
flags[i] = flag
|
|
if band(flag,0x08) ~= 0 then
|
|
local n = readbyte(f)
|
|
if n == 1 then
|
|
i = i + 1
|
|
flags[i] = flag
|
|
else
|
|
for j=1,n do
|
|
i = i + 1
|
|
flags[i] = flag
|
|
end
|
|
end
|
|
end
|
|
i = i + 1
|
|
end
|
|
-- first come the x coordinates, and next the y coordinates and they
|
|
-- can be repeated
|
|
local x = 0
|
|
for i=1,nofpoints do
|
|
local flag = flags[i]
|
|
-- local short = band(flag,0x04) ~= 0
|
|
-- local same = band(flag,0x20) ~= 0
|
|
if band(flag,0x02) ~= 0 then
|
|
if band(flag,0x10) ~= 0 then
|
|
x = x + readbyte(f)
|
|
else
|
|
x = x - readbyte(f)
|
|
end
|
|
elseif band(flag,0x10) ~= 0 then
|
|
-- copy
|
|
else
|
|
x = x + readshort(f)
|
|
end
|
|
points[i] = { x, 0, band(flag,0x01) ~= 0 }
|
|
end
|
|
local y = 0
|
|
for i=1,nofpoints do
|
|
local flag = flags[i]
|
|
-- local short = band(flag,0x04) ~= 0
|
|
-- local same = band(flag,0x20) ~= 0
|
|
if band(flag,0x04) ~= 0 then
|
|
if band(flag,0x20) ~= 0 then
|
|
y = y + readbyte(f)
|
|
else
|
|
y = y - readbyte(f)
|
|
end
|
|
elseif band(flag,0x20) ~= 0 then
|
|
-- copy
|
|
else
|
|
y = y + readshort(f)
|
|
end
|
|
points[i][2] = y
|
|
end
|
|
return {
|
|
type = "glyph",
|
|
points = points,
|
|
contours = contours,
|
|
nofpoints = nofpoints,
|
|
}
|
|
end
|
|
|
|
local function readcomposite(f)
|
|
local components = { }
|
|
local nofcomponents = 0
|
|
local instructions = false
|
|
while true do
|
|
local flags = readushort(f)
|
|
local index = readushort(f)
|
|
----- f_words = band(flags,0x0001) ~= 0
|
|
local f_xyarg = band(flags,0x0002) ~= 0
|
|
----- f_round = band(flags,0x0006) ~= 0 -- 2 + 4
|
|
----- f_scale = band(flags,0x0008) ~= 0
|
|
----- f_reserved = band(flags,0x0010) ~= 0
|
|
----- f_more = band(flags,0x0020) ~= 0
|
|
----- f_xyscale = band(flags,0x0040) ~= 0
|
|
----- f_matrix = band(flags,0x0080) ~= 0
|
|
----- f_instruct = band(flags,0x0100) ~= 0
|
|
----- f_usemine = band(flags,0x0200) ~= 0
|
|
----- f_overlap = band(flags,0x0400) ~= 0
|
|
local f_offset = band(flags,0x0800) ~= 0
|
|
----- f_uoffset = band(flags,0x1000) ~= 0
|
|
local xscale = 1
|
|
local xrotate = 0
|
|
local yrotate = 0
|
|
local yscale = 1
|
|
local xoffset = 0
|
|
local yoffset = 0
|
|
local base = false
|
|
local reference = false
|
|
if f_xyarg then
|
|
if band(flags,0x0001) ~= 0 then -- f_words
|
|
xoffset = readshort(f)
|
|
yoffset = readshort(f)
|
|
else
|
|
xoffset = readchar(f) -- signed byte, stupid name
|
|
yoffset = readchar(f) -- signed byte, stupid name
|
|
end
|
|
else
|
|
if band(flags,0x0001) ~= 0 then -- f_words
|
|
base = readshort(f)
|
|
reference = readshort(f)
|
|
else
|
|
base = readchar(f) -- signed byte, stupid name
|
|
reference = readchar(f) -- signed byte, stupid name
|
|
end
|
|
end
|
|
if band(flags,0x0008) ~= 0 then -- f_scale
|
|
xscale = read2dot14(f)
|
|
yscale = xscale
|
|
if f_xyarg and f_offset then
|
|
xoffset = xoffset * xscale
|
|
yoffset = yoffset * yscale
|
|
end
|
|
elseif band(flags,0x0040) ~= 0 then -- f_xyscale
|
|
xscale = read2dot14(f)
|
|
yscale = read2dot14(f)
|
|
if f_xyarg and f_offset then
|
|
xoffset = xoffset * xscale
|
|
yoffset = yoffset * yscale
|
|
end
|
|
elseif band(flags,0x0080) ~= 0 then -- f_matrix
|
|
xscale = read2dot14(f)
|
|
xrotate = read2dot14(f)
|
|
yrotate = read2dot14(f)
|
|
yscale = read2dot14(f)
|
|
if f_xyarg and f_offset then
|
|
xoffset = xoffset * sqrt(xscale ^2 + xrotate^2)
|
|
yoffset = yoffset * sqrt(yrotate^2 + yscale ^2)
|
|
end
|
|
end
|
|
nofcomponents = nofcomponents + 1
|
|
components[nofcomponents] = {
|
|
index = index,
|
|
usemine = band(flags,0x0200) ~= 0, -- f_usemine
|
|
round = band(flags,0x0006) ~= 0, -- f_round,
|
|
base = base,
|
|
reference = reference,
|
|
matrix = { xscale, xrotate, yrotate, yscale, xoffset, yoffset },
|
|
}
|
|
if band(flags,0x0100) ~= 0 then
|
|
instructions = true
|
|
end
|
|
if band(flags,0x0020) == 0 then -- f_more
|
|
break
|
|
end
|
|
end
|
|
return {
|
|
type = "composite",
|
|
components = components,
|
|
}
|
|
end
|
|
|
|
-- function readers.cff(f,offset,glyphs,doshapes) -- false == no shapes (nil or true otherwise)
|
|
|
|
-- The glyf table depends on the loca table. We have one entry to much
|
|
-- in the locations table (the last one is a dummy) because we need to
|
|
-- calculate the size of a glyph blob from the delta, although we not
|
|
-- need it in our usage (yet). We can remove the locations table when
|
|
-- we're done (todo: cleanup finalizer).
|
|
|
|
function readers.loca(f,fontdata,specification)
|
|
if specification.glyphs then
|
|
local datatable = fontdata.tables.loca
|
|
if datatable then
|
|
-- locations are relative to the glypdata table (glyf)
|
|
local offset = fontdata.tables.glyf.offset
|
|
local format = fontdata.fontheader.indextolocformat
|
|
local profile = fontdata.maximumprofile
|
|
local nofglyphs = profile and profile.nofglyphs
|
|
local locations = { }
|
|
setposition(f,datatable.offset)
|
|
if format == 1 then
|
|
if not nofglyphs then
|
|
nofglyphs = idiv(datatable.length,4) - 1
|
|
end
|
|
for i=0,nofglyphs do
|
|
locations[i] = offset + readulong(f)
|
|
end
|
|
fontdata.nofglyphs = nofglyphs
|
|
else
|
|
if not nofglyphs then
|
|
nofglyphs = idiv(datatable.length,2) - 1
|
|
end
|
|
for i=0,nofglyphs do
|
|
locations[i] = offset + readushort(f) * 2
|
|
end
|
|
end
|
|
fontdata.nofglyphs = nofglyphs
|
|
fontdata.locations = locations
|
|
end
|
|
end
|
|
end
|
|
|
|
function readers.glyf(f,fontdata,specification) -- part goes to cff module
|
|
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 or specification.streams
|
|
for index=0,nofglyphs-1 do
|
|
local location = locations[index]
|
|
local length = locations[index+1] - location
|
|
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 length > 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)
|
|
elseif nofcontours > 0 then
|
|
shapes[index] = readglyph(f,nofcontours)
|
|
else
|
|
shapes[index] = readcomposite(f,nofcontours)
|
|
end
|
|
else
|
|
if loadshapes then
|
|
shapes[index] = readnothing(f)
|
|
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.shapes then
|
|
if specification.streams then
|
|
repackpoints(glyphs,shapes)
|
|
else
|
|
contours2outlines_normal(glyphs,shapes)
|
|
end
|
|
elseif specification.streams then
|
|
repackpoints(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
|
|
|
|
-- (1) the first is a real point the rest deltas
|
|
-- (2) points can be present more than once (multiple deltas then)
|
|
|
|
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
|
|
elseif band(count,0x80) ~= 0 then
|
|
count = band(count,0x7F) * 256 + readbyte(f)
|
|
else
|
|
-- bad news
|
|
end
|
|
local points = { }
|
|
local p = 0
|
|
local n = 1 -- indices
|
|
while p < count do
|
|
local control = readbyte(f)
|
|
local runreader = band(control,0x80) ~= 0 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 z = 0
|
|
while nofpoints > 0 do
|
|
local control = readbyte(f)
|
|
if not control then
|
|
break
|
|
end
|
|
local allzero = band(control,0x80) ~= 0
|
|
local runlength = band(control,0x3F) + 1
|
|
if allzero then
|
|
z = z + runlength
|
|
else
|
|
local runreader = band(control,0x40) ~= 0 and readshort or readinteger
|
|
if z > 0 then
|
|
for i=1,z do
|
|
p = p + 1
|
|
deltas[p] = 0
|
|
end
|
|
z = 0
|
|
end
|
|
for i=1,runlength do
|
|
p = p + 1
|
|
deltas[p] = runreader(f)
|
|
end
|
|
end
|
|
nofpoints = nofpoints - runlength
|
|
end
|
|
-- saves space
|
|
-- if z > 0 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
|
|
|
|
local function readdeltas(f,nofpoints)
|
|
local deltas = { }
|
|
local p = 0
|
|
while nofpoints > 0 do
|
|
local control = readbyte(f)
|
|
if control then
|
|
local allzero = band(control,0x80) ~= 0
|
|
local runlength = band(control,0x3F) + 1
|
|
if allzero then
|
|
for i=1,runlength do
|
|
p = p + 1
|
|
deltas[p] = 0
|
|
end
|
|
else
|
|
local runreader = band(control,0x40) ~= 0 and readshort or readinteger
|
|
for i=1,runlength do
|
|
p = p + 1
|
|
deltas[p] = runreader(f)
|
|
end
|
|
end
|
|
nofpoints = nofpoints - runlength
|
|
else
|
|
-- it happens
|
|
break
|
|
end
|
|
end
|
|
-- saves space
|
|
if p > 0 then
|
|
return deltas
|
|
else
|
|
-- forget about all zeros
|
|
end
|
|
end
|
|
|
|
function readers.gvar(f,fontdata,specification,glyphdata,shapedata)
|
|
-- this is one of the messiest tables
|
|
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 = tableoffset + readulong(f)
|
|
local nofglyphs = readushort(f)
|
|
local flags = readushort(f)
|
|
local dataoffset = tableoffset + readulong(f)
|
|
local data = { }
|
|
local tuples = { }
|
|
local glyphdata = fontdata.glyphs
|
|
local dowidth = not fontdata.variabledata.hvarwidths
|
|
-- 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 band(flags,0x0001) ~= 0 then
|
|
for i=1,nofglyphs+1 do
|
|
data[i] = dataoffset + readulong(f)
|
|
end
|
|
else
|
|
for i=1,nofglyphs+1 do
|
|
data[i] = dataoffset + 2*readushort(f)
|
|
end
|
|
end
|
|
--
|
|
if noftuples > 0 then
|
|
setposition(f,tupleoffset)
|
|
for i=1,noftuples do
|
|
tuples[i] = readtuplerecord(f,nofaxis)
|
|
end
|
|
end
|
|
local nextoffset = false
|
|
local startoffset = data[1]
|
|
for i=1,nofglyphs do -- hm one more cf spec
|
|
nextoffset = data[i+1]
|
|
local glyph = glyphdata[i-1]
|
|
local name = trace_deltas and glyph.name
|
|
if startoffset == nextoffset then
|
|
if name then
|
|
report("no deltas for glyph %a",name)
|
|
end
|
|
else
|
|
local shape = shapedata[i-1] -- todo 0
|
|
if not shape then
|
|
if name then
|
|
report("no shape for glyph %a",name)
|
|
end
|
|
else
|
|
lastoffset = startoffset
|
|
setposition(f,startoffset)
|
|
local flags = readushort(f)
|
|
local count = band(flags,0x0FFF)
|
|
local offset = startoffset + readushort(f) -- to serialized
|
|
local deltas = { }
|
|
local allpoints = (shape.nofpoints or 0) -- + 1
|
|
local shared = false
|
|
local nofshared = 0
|
|
if band(flags,0x8000) ~= 0 then -- has shared points
|
|
-- go to the packed stream (get them once)
|
|
local current = getposition(f)
|
|
setposition(f,offset)
|
|
shared, nofshared = readpoints(f)
|
|
offset = getposition(f)
|
|
setposition(f,current)
|
|
-- and back to the table
|
|
end
|
|
for j=1,count do
|
|
local size = readushort(f) -- check
|
|
local flags = readushort(f)
|
|
local index = band(flags,0x0FFF)
|
|
local haspeak = band(flags,0x8000) ~= 0
|
|
local intermediate = band(flags,0x4000) ~= 0
|
|
local private = band(flags,0x2000) ~= 0
|
|
local peak = nil
|
|
local start = nil
|
|
local stop = nil
|
|
local xvalues = nil
|
|
local yvalues = nil
|
|
local points = shared -- we default to shared
|
|
local nofpoints = nofshared -- we default to shared
|
|
-- local advance = 4
|
|
if haspeak then
|
|
peak = readtuplerecord(f,nofaxis)
|
|
-- advance = advance + 2*nofaxis
|
|
else
|
|
if index+1 > #tuples then
|
|
report("error, bad tuple index",index)
|
|
end
|
|
peak = tuples[index+1] -- hm, needs checking, only peak?
|
|
end
|
|
if intermediate then
|
|
start = readtuplerecord(f,nofaxis)
|
|
stop = readtuplerecord(f,nofaxis)
|
|
-- advance = advance + 4*nofaxis
|
|
end
|
|
-- get the deltas
|
|
if size > 0 then
|
|
local current = getposition(f)
|
|
-- goto the packed stream
|
|
setposition(f,offset)
|
|
if private then
|
|
points, nofpoints = readpoints(f)
|
|
end -- else
|
|
if nofpoints == 0 then
|
|
nofpoints = allpoints + 4
|
|
end
|
|
if nofpoints > 0 then
|
|
-- a nice test is to do only one
|
|
xvalues = readdeltas(f,nofpoints)
|
|
yvalues = readdeltas(f,nofpoints)
|
|
end
|
|
-- resync offset
|
|
offset = offset + size
|
|
-- back to the table
|
|
setposition(f,current)
|
|
end
|
|
if not xvalues and not yvalues then
|
|
points = nil
|
|
end
|
|
local s = 1
|
|
for i=1,nofaxis do
|
|
local f = factors[i]
|
|
local peak = peak and peak [i] or 0
|
|
-- local start = start and start[i] or 0
|
|
-- local stop = stop and stop [i] or 0
|
|
local start = start and start[i] or (peak < 0 and peak or 0)
|
|
local stop = stop and stop [i] or (peak > 0 and peak 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
|
|
if name then
|
|
report("no deltas applied for glyph %a",name)
|
|
end
|
|
else
|
|
deltas[#deltas+1] = {
|
|
factor = s,
|
|
points = points,
|
|
xvalues = xvalues,
|
|
yvalues = yvalues,
|
|
}
|
|
end
|
|
end
|
|
if shape.type == "glyph" then
|
|
applyaxis(glyph,shape,deltas,dowidth)
|
|
else
|
|
-- todo: args_are_xy_values mess .. i have to be really bored
|
|
-- and motivated to deal with it
|
|
shape.deltas = deltas
|
|
end
|
|
end
|
|
end
|
|
startoffset = nextoffset
|
|
end
|
|
end
|
|
end
|
|
|