summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/font-ext.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkiv/font-ext.lua')
-rw-r--r--tex/context/base/mkiv/font-ext.lua539
1 files changed, 450 insertions, 89 deletions
diff --git a/tex/context/base/mkiv/font-ext.lua b/tex/context/base/mkiv/font-ext.lua
index 965b6e6dc..f9db5e0d9 100644
--- a/tex/context/base/mkiv/font-ext.lua
+++ b/tex/context/base/mkiv/font-ext.lua
@@ -7,9 +7,9 @@ if not modules then modules = { } end modules ['font-ext'] = {
}
local next, type, tonumber = next, type, tonumber
-local formatters = string.formatters
-local byte = string.byte
+local byte, find, formatters = string.byte, string.find, string.formatters
local utfchar = utf.char
+local sortedhash, sortedkeys, sort = table.sortedhash, table.sortedkeys, table.sort
local context = context
local fonts = fonts
@@ -37,8 +37,12 @@ local registerafmfeature = handlers.afm.features.register
local fontdata = hashes.identifiers
local fontproperties = hashes.properties
+local constructors = fonts.constructors
+local getprivate = constructors.getprivate
+
local allocate = utilities.storage.allocate
local settings_to_array = utilities.parsers.settings_to_array
+local settings_to_hash = utilities.parsers.settings_to_hash
local getparameters = utilities.parsers.getparameters
local gettexdimen = tex.getdimen
local family_font = node.family_font
@@ -46,6 +50,13 @@ local family_font = node.family_font
local setmetatableindex = table.setmetatableindex
local implement = interfaces.implement
+local variables = interfaces.variables
+
+
+local v_background = variables.background
+local v_frame = variables.frame
+local v_empty = variables.empty
+local v_none = variables.none
-- -- -- -- -- --
-- shared
@@ -122,7 +133,6 @@ local function initializeexpansion(tfmdata,value)
shrink = 10 * shrink,
step = 10 * step,
factor = factor,
- auto = true,
}
local data = characters and characters.data
for i, chr in next, tfmdata.characters do
@@ -397,15 +407,6 @@ local function map_opbd_onto_protrusion(tfmdata,value,opbd)
end
end
end
- local parameters = tfmdata.parameters
- local protrusion = tfmdata.protrusion
- if not protrusion then
- parameters.protrusion = {
- auto = true
- }
- else
- protrusion.auto = true
- end
end
-- The opbd test is just there because it was discussed on the context development list. However,
@@ -435,7 +436,6 @@ local function initializeprotrusion(tfmdata,value)
factor = factor,
left = left,
right = right,
- auto = true,
}
for i, chr in next, tfmdata.characters do
local v, pl, pr = vector[i], nil, nil
@@ -645,32 +645,41 @@ local function manipulatedimensions(tfmdata,key,value)
local parameters = tfmdata.parameters
local emwidth = parameters.quad
local exheight = parameters.xheight
- local width = 0
- local height = 0
- local depth = 0
+ local newwidth = false
+ local newheight = false
+ local newdepth = false
if value == "strut" then
- height = gettexdimen("strutht")
- depth = gettexdimen("strutdp")
+ newheight = gettexdimen("strutht")
+ newdepth = gettexdimen("strutdp")
+ elseif value == "mono" then
+ newwidth = emwidth
else
local spec = settings_to_array(value)
- width = (spec[1] or 0) * emwidth
- height = (spec[2] or 0) * exheight
- depth = (spec[3] or 0) * exheight
+ newwidth = tonumber(spec[1])
+ newheight = tonumber(spec[2])
+ newdepth = tonumber(spec[3])
+ if newwidth then newwidth = newwidth * emwidth end
+ if newheight then newheight = newheight * exheight end
+ if newdepth then newdepth = newdepth * exheight end
end
- if width > 0 then
- local resources = tfmdata.resources
+ if newwidth or newheight or newdepth then
local additions = { }
- local private = resources.private
for unicode, old_c in next, characters do
- local oldwidth = old_c.width
+ local oldwidth = old_c.width
+ local oldheight = old_c.height
+ local olddepth = old_c.depth
+ local width = newwidth or oldwidth or 0
+ local height = newheight or oldheight or 0
+ local depth = newdepth or olddepth or 0
if oldwidth ~= width then
-- Defining the tables in one step is more efficient
-- than adding fields later.
- private = private + 1
+ local private = getprivate(tfmdata)
local new_c
local commands = {
{ "right", (width - oldwidth) / 2 },
{ "slot", 1, private },
+ -- { "slot", 0, private },
}
if height > 0 then
if depth > 0 then
@@ -703,13 +712,12 @@ local function manipulatedimensions(tfmdata,key,value)
end
setmetatableindex(new_c,old_c)
characters[unicode] = new_c
- additions[private] = old_c
+ additions[private] = old_c
end
end
for k, v in next, additions do
characters[k] = v
end
- resources.private = private
elseif height > 0 and depth > 0 then
for unicode, old_c in next, characters do
old_c.height = height
@@ -739,6 +747,53 @@ local dimensions_specification = {
registerotffeature(dimensions_specification)
registerafmfeature(dimensions_specification)
+--------------------------------------------------------------------------------------------------------------
+
+-- local function fakemonospace(tfmdata)
+-- local resources = tfmdata.resources
+-- local gposfeatures = resources.features.gpos
+-- local characters = tfmdata.characters
+-- local descriptions = tfmdata.descriptions
+-- local sequences = resources.sequences
+-- local coverage = { }
+-- local units = tfmdata.shared.rawdata.metadata.units
+-- for k, v in next, characters do
+-- local w = descriptions[k].width
+-- local d = units - w
+-- coverage[k] = { -d/2, 0, units, 0 }
+-- end
+-- local f = { dflt = { dflt = true } }
+-- local s = #sequences + 1
+-- local t = {
+-- features = { fakemono = f },
+-- flags = { false, false, false, false },
+-- index = s,
+-- name = "p_s_" .. s,
+-- nofsteps = 1,
+-- order = { "fakemono" },
+-- skiphash = false,
+-- type = "gpos_single",
+-- steps = {
+-- {
+-- format = "single",
+-- coverage = coverage,
+-- }
+-- }
+-- }
+-- gposfeatures["fakemono"] = f
+-- sequences[s] = t
+-- end
+--
+-- fonts.constructors.features.otf.register {
+-- name = "fakemono",
+-- description = "fake monospaced",
+-- initializers = {
+-- node = fakemonospace,
+-- },
+-- }
+
+--------------------------------------------------------------------------------------------------------------
+
-- for zhichu chen (see mailing list archive): we might add a few more variants
-- in due time
--
@@ -748,79 +803,175 @@ registerafmfeature(dimensions_specification)
--
-- \definecolor[DummyColor][s=.75,t=.5,a=1] {\DummyColor test} \nopdfcompression
--
--- local gray = { "special", "pdf: /Tr1 gs .75 g" }
--- local black = { "special", "pdf: /Tr0 gs 0 g" }
+-- local gray = { "pdf", "origin", "/Tr1 gs .75 g" }
+-- local black = { "pdf", "origin", "/Tr0 gs 0 g" }
--- sort of obsolete as we now have \showglyphs
+
+-- boundingbox={yes|background|frame|empty|<color>}
local push = { "push" }
local pop = { "pop" }
-local gray = { "special", "pdf: .75 g" }
-local black = { "special", "pdf: 0 g" }
-local downcache = { } -- handy for huge cjk fonts
-local rulecache = { } -- handy for huge cjk fonts
+----- gray = { "pdf", "origin", ".75 g .75 G" }
+----- black = { "pdf", "origin", "0 g 0 G" }
+----- gray = { "pdf", ".75 g" }
+----- black = { "pdf", "0 g" }
-setmetatableindex(downcache,function(t,d)
- local v = { "down", d }
- t[d] = v
+-- local bp = number.dimenfactors.bp
+--
+-- local downcache = setmetatableindex(function(t,d)
+-- local v = { "down", d }
+-- t[d] = v
+-- return v
+-- end)
+--
+-- local backcache = setmetatableindex(function(t,h)
+-- local h = h * bp
+-- local v = setmetatableindex(function(t,w)
+-- -- local v = { "rule", h, w }
+-- local v = { "pdf", "origin", formatters["0 0 %.6F %.6F re F"](w*bp,h) }
+-- t[w] = v
+-- return v
+-- end)
+-- t[h] = v
+-- return v
+-- end)
+--
+-- local forecache = setmetatableindex(function(t,h)
+-- local h = h * bp
+-- local v = setmetatableindex(function(t,w)
+-- local v = { "pdf", "origin", formatters["%.6F w 0 0 %.6F %.6F re S"](0.25*65536*bp,w*bp,h) }
+-- t[w] = v
+-- return v
+-- end)
+-- t[h] = v
+-- return v
+-- end)
+
+local bp = number.dimenfactors.bp
+local r = 16384 * bp -- 65536 // 4
+
+local backcache = setmetatableindex(function(t,h)
+ local h = h * bp
+ local v = setmetatableindex(function(t,d)
+ local d = d * bp
+ local v = setmetatableindex(function(t,w)
+ local v = { "pdf", "origin", formatters["%.6F w 0 %.6F %.6F %.6F re f"](r,-d,w*bp,h+d) }
+ t[w] = v
+ return v
+ end)
+ t[d] = v
+ return v
+ end)
+ t[h] = v
return v
end)
-setmetatableindex(rulecache,function(t,h)
- local v = { }
- t[h] = v
- setmetatableindex(v,function(t,w)
- local v = { "rule", h, w }
- t[w] = v
+local forecache = setmetatableindex(function(t,h)
+ local h = h * bp
+ local v = setmetatableindex(function(t,d)
+ local d = d * bp
+ local v = setmetatableindex(function(t,w)
+ -- the frame goes through the boundingbox
+ -- local v = { "pdf", "origin", formatters["[] 0 d 0 J %.6F w %.6F %.6F %.6F re S"](r,-d,w*bp,h+d) }
+ local v = { "pdf", "origin", formatters["[] 0 d 0 J %.6F w %.6F %.6F %.6F %.6F re S"](r,r/2,-d+r/2,w*bp-r,h+d-r) }
+ t[w] = v
+ return v
+ end)
+ t[d] = v
return v
end)
+ t[h] = v
return v
end)
+local startcolor = nil
+local stopcolor = nil
+
local function showboundingbox(tfmdata,key,value)
if value then
- local vfspecials = backends.pdf.tables.vfspecials
- local gray = vfspecials and (vfspecials.rulecolors[value] or vfspecials.rulecolors.palegray) or gray
+ if not backcolors then
+ local vfspecials = backends.pdf.tables.vfspecials
+ startcolor = vfspecials.startcolor
+ stopcolor = vfspecials.stopcolor
+ end
local characters = tfmdata.characters
- local resources = tfmdata.resources
local additions = { }
- local private = resources.private
+ local rulecache = backcache
+ local showchar = true
+ local color = "palegray"
+ if type(value) == "string" then
+ value = settings_to_array(value)
+ for i=1,#value do
+ local v = value[i]
+ if v == v_frame then
+ rulecache = forecache
+ elseif v == v_background then
+ rulecache = backcache
+ elseif v == v_empty then
+ showchar = false
+ elseif v == v_none then
+ color = nil
+ else
+ color = v
+ end
+ end
+ end
+ local gray = color and startcolor(color) or nil
+ local black = gray and stopcolor or nil
for unicode, old_c in next, characters do
- private = private + 1
- local width = old_c.width or 0
- local height = old_c.height or 0
- local depth = old_c.depth or 0
- local new_c
- if depth == 0 then
- new_c = {
- width = width,
- height = height,
- commands = {
- push,
- gray,
- rulecache[height][width],
- black,
- pop,
- { "slot", 1, private },
- }
- }
- else
- new_c = {
- width = width,
- height = height,
- depth = depth,
- commands = {
- push,
- downcache[depth],
- gray,
- rulecache[height+depth][width],
- black,
- pop,
- { "slot", 1, private },
- }
+ local private = getprivate(tfmdata)
+ local width = old_c.width or 0
+ local height = old_c.height or 0
+ local depth = old_c.depth or 0
+ local char = showchar and { "slot", 1, private } or nil -- { "slot", 0, private }
+ -- local new_c
+ -- if depth == 0 then
+ -- new_c = {
+ -- width = width,
+ -- height = height,
+ -- commands = {
+ -- push,
+ -- gray,
+ -- rulecache[height][width],
+ -- black,
+ -- pop,
+ -- char,
+ -- }
+ -- }
+ -- else
+ -- new_c = {
+ -- width = width,
+ -- height = height,
+ -- depth = depth,
+ -- commands = {
+ -- push,
+ -- downcache[depth],
+ -- gray,
+ -- rulecache[height+depth][width],
+ -- black,
+ -- pop,
+ -- char,
+ -- }
+ -- }
+ -- end
+ local rule = rulecache[height][depth][width]
+ local new_c = {
+ width = width,
+ height = height,
+ depth = depth,
+ commands = gray and {
+ -- push,
+ gray,
+ rule,
+ black,
+ -- pop,
+ char,
+ } or {
+ rule,
+ char,
}
- end
+ }
setmetatableindex(new_c,old_c)
characters[unicode] = new_c
additions[private] = old_c
@@ -828,7 +979,6 @@ local function showboundingbox(tfmdata,key,value)
for k, v in next, additions do
characters[k] = v
end
- resources.private = private
end
end
@@ -884,6 +1034,58 @@ registerotffeature {
--
-- end
+do
+
+ local P, lpegpatterns, lpegmatch = lpeg.P, lpeg.patterns, lpeg.match
+
+ local amount, stretch, shrink, extra
+
+ local factor = lpegpatterns.unsigned
+ local space = lpegpatterns.space
+ local pattern = (
+ (factor / function(n) amount = tonumber(n) or amount end)
+ + (P("+") + P("plus" )) * space^0 * (factor / function(n) stretch = tonumber(n) or stretch end)
+ + (P("-") + P("minus")) * space^0 * (factor / function(n) shrink = tonumber(n) or shrink end)
+ + ( P("extra")) * space^0 * (factor / function(n) extra = tonumber(n) or extra end)
+ + space^1
+ )^1
+
+ local function initialize(tfmdata,key,value)
+ local characters = tfmdata.characters
+ local parameters = tfmdata.parameters
+ if type(value) == "string" then
+ local emwidth = parameters.quad
+ amount, stretch, shrink, extra = 0, 0, 0, false
+ lpegmatch(pattern,value)
+ if not extra then
+ if shrink ~= 0 then
+ extra = shrink
+ elseif stretch ~= 0 then
+ extra = stretch
+ else
+ extra = amount
+ end
+ end
+ parameters.space = amount * emwidth
+ parameters.space_stretch = stretch * emwidth
+ parameters.space_shrink = shrink * emwidth
+ parameters.extra_space = extra * emwidth
+ end
+ end
+
+ -- 1.1 + 1.2 - 1.3 minus 1.4 plus 1.1 extra 1.4 -- last one wins
+
+ registerotffeature {
+ name = "spacing",
+ description = "space settings",
+ manipulators = {
+ base = initialize,
+ node = initialize,
+ }
+ }
+
+end
+
-- -- historic stuff, move from font-ota (handled differently, typo-rep)
--
-- local delete_node = nodes.delete
@@ -1069,7 +1271,7 @@ local function getprivatecharornode(tfmdata,name)
local font = properties.id
local slot = getprivateslot(font,name)
if slot then
- -- todo: set current attribibutes
+ -- todo: set current attributes
local char = tfmdata.characters[slot]
local tonode = char.tonode
if tonode then
@@ -1121,7 +1323,7 @@ do
end
end
- fonts.constructors.newfeatures.otf.register {
+ constructors.newfeatures.otf.register {
name = "extraprivates",
description = "extra privates",
default = true,
@@ -1257,21 +1459,21 @@ do -- another hack for a crappy font
local function additalictowidth(tfmdata,key,value)
local characters = tfmdata.characters
- local resources = tfmdata.resources
local additions = { }
- local private = resources.private
for unicode, old_c in next, characters do
-- maybe check for math
local oldwidth = old_c.width
local olditalic = old_c.italic
if olditalic and olditalic ~= 0 then
- private = private + 1
+ local private = getprivate(tfmdata)
local new_c = {
width = oldwidth + olditalic,
height = old_c.height,
depth = old_c.depth,
commands = {
- { "slot", 1, private },
+ -- { "slot", 1, private },
+ -- { "slot", 0, private },
+ { "char", private },
{ "right", olditalic },
},
}
@@ -1283,7 +1485,6 @@ do -- another hack for a crappy font
for k, v in next, additions do
characters[k] = v
end
- resources.private = private
end
registerotffeature {
@@ -1345,3 +1546,163 @@ do
}
end
+
+do
+
+ -- This is a rather special test-only feature that I added for the sake of testing
+ -- Idris's husayni. We wanted to know if uniscribe obeys the order of lookups in a
+ -- font, in spite of what the description of handling arabic suggests. And indeed,
+ -- mixed-in lookups of other features (like all these ss* in husayni) are handled
+ -- the same in context as in uniscribe. If one sets reorderlookups=arab then we sort
+ -- according to the "assumed" order so e.g. the ss* move to after the standard
+ -- features. The observed difference in rendering is an indication that uniscribe is
+ -- quite faithful to the font (while e.g. tests with the hb plugin demonstrate some
+ -- interference, apart from some hard coded init etc expectations). Anyway, it means
+ -- that we're okay with the (generic) node processor. A pitfall is that in context
+ -- we can actually control more, so we can trigger an analyze pass with e.g.
+ -- dflt/dflt while the libraries depend on the script settings for that. Uniscribe
+ -- probably also parses the string and when seeing arabic will follow a different
+ -- code path, although it seems to treat all features equal.
+
+ local trace_reorder = trackers.register("fonts.reorderlookups",function(v) trace_reorder = v end)
+ local report_reorder = logs.reporter("fonts","reorder")
+
+ local vectors = { }
+
+ vectors.arab = {
+ gsub = {
+ ccmp = 1,
+ isol = 2,
+ fina = 3,
+ medi = 4,
+ init = 5,
+ rlig = 6,
+ rclt = 7,
+ calt = 8,
+ liga = 9,
+ dlig = 10,
+ cswh = 11,
+ mset = 12,
+ },
+ gpos = {
+ curs = 1,
+ kern = 2,
+ mark = 3,
+ mkmk = 4,
+ },
+ }
+
+ function otf.reorderlookups(tfmdata,vector)
+ local order = vectors[vector]
+ if not order then
+ return
+ end
+ local oldsequences = tfmdata.resources.sequences
+ if oldsequences then
+ local sequences = { }
+ for i=1,#oldsequences do
+ sequences[i] = oldsequences[i]
+ end
+ for i=1,#sequences do
+ local s = sequences[i]
+ local features = s.features
+ local kind = s.type
+ local index = s.index
+ if features then
+ local when
+ local what
+ for feature in sortedhash(features) do
+ if not what then
+ what = find(kind,"^gsub") and "gsub" or "gpos"
+ end
+ local newwhen = order[what][feature]
+ if not newwhen then
+ -- skip
+ elseif not when then
+ when = newwhen
+ elseif newwhen < when then
+ when = newwhen
+ end
+ end
+ s.ondex = s.index
+ s.index = i
+ s.what = what == "gsub" and 1 or 2
+ s.when = when or 99
+ else
+ s.ondex = s.index
+ s.index = i
+ s.what = 1
+ s.when = 99
+ end
+ end
+ sort(sequences,function(a,b)
+ local what_a = a.what
+ local what_b = b.what
+ if what_a ~= what_b then
+ return a.index < b.index
+ end
+ local when_a = a.when
+ local when_b = b.when
+ if when_a == when_b then
+ return a.index < b.index
+ else
+ return when_a < when_b
+ end
+ end)
+ local swapped = 0
+ for i=1,#sequences do
+ local sequence = sequences[i]
+ local features = sequence.features
+ if features then
+ local index = sequence.index
+ if index ~= i then
+ swapped = swapped + 1
+ end
+ if trace_reorder then
+ if swapped == 1 then
+ report_reorder()
+ report_reorder("start swapping lookups in font %!font:name!",tfmdata)
+ report_reorder()
+ report_reorder("gsub order: % t",table.swapped(order.gsub))
+ report_reorder("gpos order: % t",table.swapped(order.gpos))
+ report_reorder()
+ end
+ report_reorder("%03i : lookup %03i, type %s, sorted %2i, moved %s, % t",
+ i,index,sequence.what == 1 and "gsub" or "gpos",sequence.when or 99,
+ (index > i and "-") or (index < i and "+") or "=",sortedkeys(features))
+ end
+ end
+ sequence.what = nil
+ sequence.when = nil
+ sequence.index = sequence.ondex
+ end
+ if swapped > 0 then
+ if trace_reorder then
+ report_reorder()
+ report_reorder("stop swapping lookups, %i lookups swapped",swapped)
+ report_reorder()
+ end
+-- tfmdata.resources.sequences = sequences
+ tfmdata.shared.reorderedsequences = sequences
+ end
+ end
+ end
+
+ -- maybe delay till ra is filled
+
+ local function reorderlookups(tfmdata,key,value)
+ if value then
+ otf.reorderlookups(tfmdata,value)
+ end
+ end
+
+ registerotffeature {
+ name = "reorderlookups",
+ description = "reorder lookups",
+ manipulators = {
+ base = reorderlookups,
+ node = reorderlookups,
+ }
+ }
+
+end