[latex3-commits] [git/LaTeX3-latex3-luaotfload] harfnode-dev: Draft (623662e)
Marcel Fabian Krüger
tex at 2krueger.de
Tue Oct 1 02:18:45 CEST 2019
Repository : https://github.com/latex3/luaotfload
On branch : harfnode-dev
Link : https://github.com/latex3/luaotfload/commit/623662ec292ae0f3720c79cb4e05565324c582a3
>---------------------------------------------------------------
commit 623662ec292ae0f3720c79cb4e05565324c582a3
Author: Marcel Fabian Krüger <tex at 2krueger.de>
Date: Tue Oct 1 02:18:45 2019 +0200
Draft
Theoretically this should work, but it was done in the middle of the
night, so if you rely on it you deserve what's coming to you..
>---------------------------------------------------------------
623662ec292ae0f3720c79cb4e05565324c582a3
src/harf/harf-node.lua | 205 ++++++++++++++++++++++---------------------------
1 file changed, 91 insertions(+), 114 deletions(-)
diff --git a/src/harf/harf-node.lua b/src/harf/harf-node.lua
index c3958db..a29edd8 100644
--- a/src/harf/harf-node.lua
+++ b/src/harf/harf-node.lua
@@ -94,6 +94,7 @@ local function copytable(old)
if type(v) == "table" then v = copytable(v) end
new[k] = v
end
+ setmetatable(new, getmetatable(old))
return new
end
@@ -108,18 +109,15 @@ local function setprop(n, prop, value)
props.harf[prop] = value
end
+local function inherit(t, base, properties)
+ local n = newnode(t)
+ setattrs(n, getattrs(base))
+ setproperty(n, properties and copytable(properties))
+ return n
+end
-- New kern node of amount `v`, inheriting the properties/attributes of `n`.
local function newkern(v, n)
- local kern = newnode(kern_t)
- local props = getproperty(n)
- local attrs = getattrs(n)
- if props then
- setproperty(kern, copytable(props))
- end
- if attrs then
- setattrs(kern, attrs)
- end
-
+ local kern = inherit(kern_t, n, getproperty(n))
setkern(kern, v)
return kern
end
@@ -156,7 +154,7 @@ local function itemize(head, fontid, direction)
local options = spec and spec.features.raw
local texlig = options and options.tlig
- local runs, nodes, codes = {}, {}, {}
+ local runs, codes = {}, {}
local dirstack = {}
local currdir = direction or "TLT"
local lastdir, lastskip, lastrun
@@ -201,17 +199,14 @@ local function itemize(head, fontid, direction)
end
codes[#codes + 1] = code
- nodes[#nodes + 1] = n
if lastdir ~= currdir or lastskip ~= skip then
lastrun = {
- node = n,
start = #codes,
len = 1,
font = fontid,
dir = currdir == "TRT" and dir_rtl or dir_ltr,
skip = skip,
- nodes = nodes,
codes = codes,
}
runs[#runs + 1] = lastrun
@@ -267,10 +262,7 @@ local shape
-- Make s a sub run, used by discretionary nodes.
local function makesub(run, codes, nodelist)
- local nodes = run.nodes
local codes = run.codes
- local start = start
- local stop = stop
local subrun = {
start = 1,
len = #codes,
@@ -284,20 +276,20 @@ local function makesub(run, codes, nodelist)
for n in traverse(nodelist) do
print(tonode(n))
end
- return { glyphs = shape(subrun), run = subrun }
+ local glyphs
+ nodelist, glyphs = shape(nodelist, subrun)
+ return { glyphs = shape(subrun), run = subrun, head = nodelist }
end
-- Main shaping function that calls HarfBuzz, and does some post-processing of
-- the output.
-shape = function(run)
- -- local nodes = run.nodes
+shape = function(head, node, run)
local codes = run.codes
local offset = run.start
local len = run.len
local fontid = run.font
local dir = run.dir
local fordisc = run.fordisc
- local node = run.node
local cluster = offset - 2
local fontdata = font.getfont(fontid)
@@ -433,7 +425,7 @@ shape = function(run)
-- Find the previous glyph that is safe to break at.
local startglyph = i
while unsafetobreak(glyphs[startglyph])
- or getid(startnode) == glue_t do
+ and getid(startnode) ~= glue_t do
startglyph = startglyph - 1
end
-- Get the corresponding character index.
@@ -442,9 +434,9 @@ shape = function(run)
-- Find the next glyph that is safe to break at.
stopglyph = i + 1
local lastcluster = glyphs[i].cluster
- while unsafetobreak(glyphs[stopglyph], nodes)
- or getid(stopnode) == glue_t
- or lastcluster == glyphs[stopglyph].cluster do
+ while unsafetobreak(glyphs[stopglyph])
+ or lastcluster == glyphs[stopglyph].cluster
+ and getid(stopnode) ~= glue_t do
lastcluster = glyphs[stopglyph].cluster
stopglyph = stopglyph + 1
end
@@ -519,12 +511,12 @@ shape = function(run)
if startnode ~= disc then
setnext(getprev(disc), rep)
setprev(rep, getprev(disc))
- local before = getprev(startnode)
- if before ~= startnode then
+ if startnode == head then
+ head = disc
+ else
+ local before = getprev(startnode)
setnext(before, disc)
setprev(disc, before)
- else
- error'TODO'
end
end
if getnext(disc) ~= endnode then
@@ -536,7 +528,6 @@ shape = function(run)
setprev(endnode, disc)
end
print'5'
- glyph.disc = disc
glyph.replace = makesub(run, repcodes, rep)
glyph.pre = makesub(run, precodes, pre)
glyph.post = makesub(run, postcodes, post)
@@ -545,10 +536,10 @@ shape = function(run)
end
node = getnext(node)
end
- return glyphs
+ return head, glyphs
end
- return {}
+ return head, {}
end
local function color_to_rgba(color)
@@ -581,8 +572,8 @@ local function cachedpng(data)
end
-- Convert glyphs to nodes and collect font characters.
-local function tonodes(head, current, run, glyphs, color)
- local nodes = run.nodes
+local function tonodes(head, node, run, glyphs, color)
+ local nodeindex = run.offset
local dir = run.dir
local fontid = run.font
local fontdata = font.getfont(fontid)
@@ -593,9 +584,7 @@ local function tonodes(head, current, run, glyphs, color)
local hbfont = hbshared.font
local fontglyphs = hbshared.glyphs
local rtl = dir:is_backward()
-
- local tracinglostchars = tex.tracinglostchars
- local tracingonline = tex.tracingonline
+ local lastprops
local scale = hbdata.scale
local letterspace = hbdata.letterspace
@@ -603,44 +592,45 @@ local function tonodes(head, current, run, glyphs, color)
local haspng = hbshared.haspng
local fonttype = hbshared.fonttype
- for i, glyph in next, glyphs do
- local index = glyph.cluster + 1
+ for i, glyph in ipairs(glyphs) do
+ if glyph.cluster < nodeindex then -- Ups, we went too far
+ nodeindex = nodeindex - 1
+ local new = inherit(glyph_t, node, lastprops)
+ setfont(new, fontid)
+ head, node = insertbefore(head, new)
+ else
+ for j = nodeindex, glyph.cluster - 1 do
+ head, node = removenode(head, node)
+ end
+ lastprops = getproperty(node)
+ nodeindex = glyph.cluster
+ end
local gid = glyph.codepoint
local char = nominals[gid] or hb.CH_GID_PREFIX + gid
- local n = nodes[index]
- local id = getid(n)
+ local id = getid(node)
local nchars, nglyphs = glyph.nchars, glyph.nglyphs
- -- If this glyph is part of a complex cluster, then copy the node as
- -- more than one glyph will use it.
- if nglyphs < 1 or nglyphs > 1 then
- n = copynode(nodes[index])
- end
-
if color then
- setprop(n, color_p, color)
+ setprop(node, color_p, color)
end
- if glyph.disc then
+ if glyph.replace then
-- For discretionary the glyph itself is skipped and a discretionary node
-- is output in place of it.
- local disc = glyph.disc
local rep, pre, post = glyph.replace, glyph.pre, glyph.post
- setdisc(disc, tonodes(nil, nil, pre.run, pre.glyphs, color),
- tonodes(nil, nil, post.run, post.glyphs, color),
- tonodes(nil, nil, rep.run, rep.glyphs, color))
-
- head, current = insertafter(head, current, disc)
+ setdisc(node, tonodes(pre.head, pre.head, pre.run, pre.glyphs, color),
+ tonodes(post.head, post.head, post.run, post.glyphs, color),
+ tonodes(rep.head, rep.head, rep.run, rep.glyphs, color))
elseif not glyph.skip then
if glyph.color then
- setprop(n, color_p, color_to_rgba(glyph.color))
+ setprop(node, color_p, color_to_rgba(glyph.color))
end
if id == glyph_t then
local fontglyph = fontglyphs[gid]
- local pngblob = fontglyph.png
+ local pngblob = fontglyph.png -- FIXME: Rewrite
if haspng and not pngblob then
pngblob = hbfont:ot_color_glyph_get_png(gid)
fontglyph.png = pngblob
@@ -658,14 +648,13 @@ local function tonodes(head, current, run, glyphs, color)
height = character.height,
depth = character.depth,
}
- head, current = insertafter(head, current, todirect(image))
if fonttype then
-- Color bitmap font with glyph outlines. Insert negative kerning
-- as we will insert the glyph node below (to help with text
-- copying) and want the bitmap and the glyph to take the same
-- advance width.
- local kern = newkern(-character.width, n)
- head, current = insertkern(head, current, kern, rtl)
+ local kern = newkern(-character.width, node)
+ head, node = insertkern(head, node, kern, rtl)
end
end
if pngblob and not fonttype then
@@ -679,31 +668,20 @@ local function tonodes(head, current, run, glyphs, color)
-- a fatal error. We use `nullfont` instead. That is a hack, but I
-- think it is good enough for now.
-- We insert the glyph node and move on, no further work is needed.
- setfont(n, 0)
- head, current = insertafter(head, current, n)
+ setfont(node, 0)
+ head, current = insertafter(head, current, node)
else
- local oldcharacter = characters[getchar(n)]
+ local oldcharacter = characters[getchar(node)]
-- If the glyph index of current font character is the same as shaped
-- glyph, keep the node char unchanged. Helps with primitives that
-- take characters as input but actually work on glyphs, like
-- `\rpcode`.
if not oldcharacter or character.index ~= oldcharacter.index then
- setchar(n, char)
+ setchar(node, char)
end
local xoffset = (rtl and -glyph.x_offset or glyph.x_offset) * scale
local yoffset = glyph.y_offset * scale
- setoffsets(n, xoffset, yoffset)
- protectglyph(n)
- head, current = insertafter(head, current, n)
-
- local x_advance = glyph.x_advance + letterspace
- local width = fontglyph.width
- if width ~= x_advance then
- -- The engine always uses the glyph width from the font, so we need
- -- to insert a kern node if the x advance is different.
- local kern = newkern((x_advance - width) * scale, n)
- head, current = insertkern(head, current, kern, rtl)
- end
+ setoffsets(node, xoffset, yoffset)
fontglyph.used = true
@@ -713,7 +691,7 @@ local function tonodes(head, current, run, glyphs, color)
-- If the string is empty it means this glyph is part of a larger
-- cluster and we don’t to print anything for it as the first glyph
-- in the cluster will have the string of the whole cluster.
- setprop(n, string_p, glyph.string or "")
+ setprop(node, string_p, glyph.string or "")
-- Handle PDF text extraction:
-- * Find how many characters in this cluster and how many glyphs,
@@ -729,15 +707,23 @@ local function tonodes(head, current, run, glyphs, color)
if nglyphs == 1 and not fontglyph.tounicode then
fontglyph.tounicode = tounicode
elseif tounicode ~= fontglyph.tounicode then
- setprop(n, startactual_p, tounicode)
+ setprop(node, startactual_p, tounicode)
glyphs[i + nglyphs - 1].endactual = true
end
end
if glyph.endactual then
- setprop(n, endactual_p, true)
+ setprop(node, endactual_p, true)
+ end
+ local x_advance = glyph.x_advance + letterspace
+ local width = fontglyph.width
+ if width ~= x_advance then
+ -- The engine always uses the glyph width from the font, so we need
+ -- to insert a kern node if the x advance is different.
+ local kern = newkern((x_advance - width) * scale, node)
+ head, node = insertkern(head, node, kern, rtl)
end
end
- elseif id == glue_t and getsubtype(n) == spaceskip_t then
+ elseif id == glue_t and getsubtype(node) == spaceskip_t then
-- If the glyph advance is different from the font space, then a
-- substitution or positioning was applied to the space glyph changing
-- it from the default, so reset the glue using the new advance.
@@ -745,28 +731,23 @@ local function tonodes(head, current, run, glyphs, color)
-- spacing after the period is larger by default in TeX.
local width = (glyph.x_advance + letterspace) * scale
if fontdata.parameters.space ~= width then
- setwidth(n, width)
- setfield(n, "stretch", width / 2)
- setfield(n, "shrink", width / 3)
+ setwidth(node, width)
+ setfield(node, "stretch", width / 2)
+ setfield(node, "shrink", width / 3)
end
- head, current = insertafter(head, current, n)
- elseif id == kern_t and getsubtype(n) == italiccorr_t then
+ elseif id == kern_t and getsubtype(node) == italiccorr_t then
-- If this is an italic correction node and the previous node is a
-- glyph, update its kern value with the glyph’s italic correction.
- -- I’d have expected the engine to do this, but apparently it doesn’t.
- -- May be it is checking for the italic correction before we have had
- -- loaded the glyph?
- local prevchar, prevfontid = isglyph(current)
- if prevchar and prevchar > 0 then
- local prevfontdata = font.getfont(prevfontid)
- local prevcharacters = prevfontdata and prevfontdata.characters
- local italic = prevcharacters and prevcharacters[prevchar].italic
+ -- FIXME: This fails if the previous glyph was e.g. a png glyph
+ local prevchar, prevfontid = ischar(getprev(node))
+ if prevfontid == fontid and prevchar and prevchar > 0 then
+ local italic = characters[prevchar].italic
if italic then
- setkern(n, italic)
+ setkern(node, italic)
end
end
- head, current = insertafter(head, current, n)
elseif id == disc_t then
+ assert(false, "Should be unreachable") -- This feels like it should be unreachable
assert(nglyphs == 1)
-- The simple case of a discretionary that is not part of a complex
-- cluster. We only need to make sure kerning before the hyphenation
@@ -779,22 +760,22 @@ local function tonodes(head, current, run, glyphs, color)
if current and getid(current) == kern_t and getsubtype(current) == fontkern_t then
setprev(current, nil)
setnext(current, nil)
- setfield(n, "replace", current)
+ setfield(node, "replace", current)
head, current = removenode(head, current)
end
- local pre, post, rep = getdisc(n)
- setdisc(n, process(pre, fontid, direction),
- process(post, fontid, direction),
- process(rep, fontid, direction))
+ local pre, post, rep = getdisc(node)
+ setdisc(node, process(pre, fontid, direction),
+ process(post, fontid, direction),
+ process(rep, fontid, direction))
- head, current = insertafter(head, current, n)
- else
- head, current = insertafter(head, current, n)
+ head, current = insertafter(head, current, node)
end
end
+ node = getnext(node)
+ nodeindex = nodeindex + 1
end
- return head, current
+ return head, node
end
local hex_to_rgba do
@@ -818,19 +799,15 @@ local function shape_run(head, current, run)
local options = fontdata.specification.features.raw
local color = options and options.color and hex_to_rgba(options.color)
- local glyphs = shape(run)
- head, current = tonodes(head, current, run, glyphs, color)
+ local glyphs
+ head, glyphs = shape(head, current, run)
+ return tonodes(head, current, run, glyphs, color)
else
- -- Not shaping, insert the original node list of of this run.
- local nodes = run.nodes
- local offset = run.start
- local len = run.len
- for i = offset, offset + len - 1 do
- head, current = insertafter(head, current, nodes[i])
+ for i = 1, len do
+ current = getnext(current)
end
+ return head, current
end
-
- return head, current
end
function process(head, font, direction)
More information about the latex3-commits
mailing list