harfbuzz_setup.lua
dofile 'package_path_searcher.lua'
require 'ot_font_loader'
require 'custom_callbacks'
local serpent = require 'serpent'
texio.write_nl(serpent.block(callback.list()))
local harfbuzz = require 'harfbuzz'
local lt_to_hb_dir = { TLT = "ltr", TRT = "rtl" }
local function upem_to_sp(v,font)
return math.floor(v / font.units_per_em * font.size)
end
local function show_nodes (head, raw)
local nodes = ''
for item in node.traverse(head) do
local i = item.id
if i == node.id("glyph") then
if raw then i = string.format('<glyph %d>', item.char) else i = unicode.utf8.char(item.char) end
else
i = string.format('<%s%s>', node.type(i), ( item.subtype and ("(".. item.subtype .. ")") or ''))
end
nodes = nodes .. i .. ' '
end
texio.write_nl(nodes)
return true
end
local function process_nodes(head)
local head_slider = head
assert(head_slider.id == node.id("local_par"), "local_par expected")
local dir = head_slider.dir
texio.write_nl("direction is: "..dir)
head_slider = head_slider.next
assert(head_slider.id == node.id("hlist") and head_slider.subtype == 3, "parindent hlist expected")
local fontid = head_slider.next.font
texio.write_nl("fontid is "..fontid)
local font = font.getfont(fontid)
if not font.harfbuzz then return head end
texio.write_nl("paragraph can be shaped by Harfbuzz")
local new_head = node.copy_list(head, head_slider.next)
assert(node.length(new_head) == 2, "expected two nodes in new_head")
local new_head_slider = node.slide(new_head)
local codepoints = { }
while head_slider.next.id ~= node.id("penalty") do
head_slider = head_slider.next
if head_slider.id == node.id("glyph") then
table.insert(codepoints, head_slider.char)
elseif head_slider.id == node.id("glue") and head_slider.subtype == 13 then
table.insert(codepoints, 0x20)
else
error(string.format("Cant handle node of type %s, subtype %s", node.type(head_slider.id), tostring(head_slider.subtype)))
end
end
local new_tail = head_slider.next
head_slider = head_slider.next.next
assert(head_slider.id == node.id("glue") and head_slider.subtype == 15, "\\parfillskip expected")
assert(not head_slider.next, "Expected this to be the last node.")
local buf = harfbuzz.Buffer.new()
buf:set_cluster_level(harfbuzz.Buffer.HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
buf:add_codepoints(codepoints)
harfbuzz.shape(font.harfbuzz.font,buf, { direction = lt_to_hb_dir[dir] })
if dir == 'TRT' then buf:reverse() end
local glyphs = buf:get_glyph_infos_and_positions()
for _, v in ipairs(glyphs) do
local n,k local char = font.backmap[v.codepoint]
if codepoints[v.cluster+1] == 0x20 then
assert(char == 0x20 or char == 0xa0, "Expected char to be 0x20 or 0xa0")
n = node.new("glue")
n.subtype = 0
n.width = font.parameters.space
n.stretch = font.parameters.space_stretch
n.shrink = font.parameters.space_shrink
new_head_slider.next = n
else
n = node.new("glyph")
n.font = fontid
n.char = char
n.subtype = 1
n.yoffset = upem_to_sp(v.y_offset, font)
n.xoffset = upem_to_sp(v.x_offset, font)
if dir == 'TRT' then n.xoffset = n.xoffset * -1 end
local x_advance = upem_to_sp(v.x_advance, font)
if math.abs(x_advance - n.width) > 1 then k = node.new("kern")
k.kern = (x_advance - n.width)
end
if k then
if dir == 'TRT' then k.next = n
new_head_slider.next = k
else n.next = k
new_head_slider.next = n
end
else new_head_slider.next = n
end
end
new_head_slider = node.slide(new_head_slider)
end
new_head_slider.next = new_tail
texio.write_nl("No. of nodes after shaping: "..node.length(new_head))
show_nodes(new_head, true)
return new_head
end
local function show_and_process_nodes(head)
texio.write_nl("No. of nodes: "..node.length(head))
show_nodes(head)
return process_nodes(head)
end
callback.register("pre_linebreak_filter", show_and_process_nodes)