Fix bugs and add node fonts

This commit is contained in:
Marcel Krüger 2020-07-08 22:20:08 +02:00
parent 2eefb9a14b
commit 1195e0f201
6 changed files with 66 additions and 36 deletions

View File

@ -5,6 +5,7 @@ local writer = require'luametalatex-nodewriter'
local newpdf = require'luametalatex-pdf' local newpdf = require'luametalatex-pdf'
local nametree = require'luametalatex-pdf-nametree' local nametree = require'luametalatex-pdf-nametree'
local build_fontdir = require'luametalatex-pdf-font' local build_fontdir = require'luametalatex-pdf-font'
local prepare_node_font = require'luametalatex-pdf-font-node'.prepare
local fontmap = require'luametalatex-pdf-font-map' local fontmap = require'luametalatex-pdf-font-map'
local utils = require'luametalatex-pdf-utils' local utils = require'luametalatex-pdf-utils'
@ -13,7 +14,17 @@ local to_bp = utils.to_bp
local pdfname, pfile local pdfname, pfile
local fontdirs = setmetatable({}, {__index=function(t, k)t[k] = pfile:getobj() return t[k] end}) local fontdirs = setmetatable({}, {__index=function(t, k)t[k] = pfile:getobj() return t[k] end})
local usedglyphs = {} local nodefont_meta = {}
local usedglyphs = setmetatable({}, {__index=function(t, fid)
local v
if font.fonts[fid].format == 'type3node' then
v = setmetatable({generation = 0, next_generation = 0}, nodefont_meta)
else
v = {}
end
t[fid] = v
return v
end})
local dests = {} local dests = {}
local cur_page local cur_page
local declare_whatsit = require'luametalatex-whatsits'.new local declare_whatsit = require'luametalatex-whatsits'.new
@ -69,7 +80,7 @@ token.luacmd("shipout", function()
local out, resources, annots = writer(pfile, list, fontdirs, usedglyphs, colorstacks) local out, resources, annots = writer(pfile, list, fontdirs, usedglyphs, colorstacks)
cur_page = nil cur_page = nil
local content = pfile:stream(nil, '', out) local content = pfile:stream(nil, '', out)
pfile:indirect(page, string.format([[<</Type/Page/Parent %i 0 R/Contents %i 0 R/MediaBox[0 %i %i %i]/Resources<<%s%s>>%s%s>>]], parent, content, -math.ceil(to_bp(list.depth)), math.ceil(to_bp(list.width)), math.ceil(to_bp(list.height)), resources, pdfvariable.pageresources, annots, pdfvariable.pageattr)) pfile:indirect(page, string.format([[<</Type/Page/Parent %i 0 R/Contents %i 0 R/MediaBox[0 %i %i %i]/Resources%s%s%s>>]], parent, content, -math.ceil(to_bp(list.depth)), math.ceil(to_bp(list.width)), math.ceil(to_bp(list.height)), resources(pdfvariable.pageresources), annots, pdfvariable.pageattr))
node.flush_list(list) node.flush_list(list)
token.put_next(reset_deadcycles) token.put_next(reset_deadcycles)
token.scan_token() token.scan_token()
@ -104,14 +115,36 @@ local pdf_escape = require'luametalatex-pdf-escape'
local pdf_bytestring = pdf_escape.escape_bytes local pdf_bytestring = pdf_escape.escape_bytes
local pdf_text = pdf_escape.escape_text local pdf_text = pdf_escape.escape_text
local function nodefont_newindex(t, k, v)
t.generation = t.next_generation
return rawset(t, k, v)
end
callback.register("stop_run", function() callback.register("stop_run", function()
if not pfile then if not pfile then
return return
end end
do
nodefont_meta.__newindex = nodefont_newindex -- Start recording generations
local need_new_run = true
while need_new_run do
need_new_run = nil
for fid, glyphs in pairs(usedglyphs) do
local next_gen = glyphs.next_generation
if next_gen and next_gen == glyphs.generation then
glyphs.next_generation = next_gen+1
need_new_run = true
local f = font.getfont(fid) or font.fonts[fid]
prepare_node_font(f, glyphs, pfile, fontdirs, usedglyphs) -- Might become fid, glyphs
end
end
end
end
for fid, id in pairs(fontdirs) do for fid, id in pairs(fontdirs) do
local f = font.getfont(fid) or font.fonts[fid] local f = font.getfont(fid) or font.fonts[fid]
local psname = f.psname or f.fullname
local sorted = {} local sorted = {}
local used = usedglyphs[fid]
used.generation, used.next_generation = nil, nil
for k,v in pairs(usedglyphs[fid]) do for k,v in pairs(usedglyphs[fid]) do
sorted[#sorted+1] = v sorted[#sorted+1] = v
end end
@ -438,8 +471,8 @@ local refobj_whatsit = declare_whatsit('pdf_refobj', function(prop, p, n, x, y)
end) end)
local literal_whatsit = declare_whatsit('pdf_literal', function(prop, p, n, x, y) local literal_whatsit = declare_whatsit('pdf_literal', function(prop, p, n, x, y)
if not prop then if not prop then
tex.error('Invalid pdf_literal whatsit', {"A pdf_literal whatsit did not contain a literal to be inserted. \z tex.error('Invalid pdf_literal whatsit', "A pdf_literal whatsit did not contain a literal to be inserted. \z
Maybe your code hasn't been adapted to LuaMetaLaTeX yet?"}) Maybe your code hasn't been adapted to LuaMetaLaTeX yet?")
return return
end end
pdf.write(prop.mode, prop.data, x, y, p) pdf.write(prop.mode, prop.data, x, y, p)

View File

@ -55,7 +55,7 @@ function font.define(f)
local format = f.format or "unknown" local format = f.format or "unknown"
local encodingbytes = f.encodingbytes or (f.format:sub(5) == "type" and 2 or 1) local encodingbytes = f.encodingbytes or (f.format:sub(5) == "type" and 2 or 1)
f.encodingbytes = encodingbytes f.encodingbytes = encodingbytes
if encodingbytes == 1 and f.type ~= 'virtual' then if encodingbytes == 1 and f.type ~= 'virtual' and f.format ~= 'type3node' then
-- map file lookup -- map file lookup
local entry = fontmap[f.name] local entry = fontmap[f.name]
if entry then if entry then
@ -145,7 +145,7 @@ function font.addcharacters(fid, newdir)
local existing = fontdir[cp] local existing = fontdir[cp]
if existing ~= glyph then if existing ~= glyph then
if existing then if existing then
texio.write_nl'Overwriting existing character. Here be dragons' -- texio.write_nl'Overwriting existing character. Here be dragons'
end end
end end
if glyph.commands then if glyph.commands then

View File

@ -35,6 +35,7 @@ local utils = require'luametalatex-pdf-utils'
local strip_floats = utils.strip_floats local strip_floats = utils.strip_floats
local to_bp = utils.to_bp local to_bp = utils.to_bp
local make_resources = require'luametalatex-pdf-resources'
local pdf_font_map = require'luametalatex-pdf-font-deduplicate' local pdf_font_map = require'luametalatex-pdf-font-deduplicate'
local get_whatsit_handler = require'luametalatex-whatsits'.handler local get_whatsit_handler = require'luametalatex-whatsits'.handler
@ -382,6 +383,8 @@ local function do_commands(p, c, f, fid, x, y, outer, ...)
for _, cmd in ipairs(c.commands) do for _, cmd in ipairs(c.commands) do
if cmd[1] == "node" then if cmd[1] == "node" then
local cmd = cmd[2] local cmd = cmd[2]
assert(node.type(cmd))
cmd = todirect(cmd)
nodehandler[getid(cmd)](p, cmd, x, y, nil, ...) nodehandler[getid(cmd)](p, cmd, x, y, nil, ...)
x = x + getwidth(cmd) x = x + getwidth(cmd)
elseif cmd[1] == "font" then elseif cmd[1] == "font" then
@ -397,17 +400,17 @@ local function do_commands(p, c, f, fid, x, y, outer, ...)
current_font = assert(fonts[cmd[2]], "invalid font requested") current_font = assert(fonts[cmd[2]], "invalid font requested")
local n = direct.new'glyph' local n = direct.new'glyph'
setsubtype(n, 256) setsubtype(n, 256)
setfont(n, current_font.id, cmd[2]) setfont(n, current_font.id, cmd[3])
nodehandler.glyph(p, n, x, y, outer, ...) nodehandler.glyph(p, n, x, y, outer, ...)
direct.free(n)
x = x + getwidth(n) x = x + getwidth(n)
direct.free(n)
elseif cmd[1] == "rule" then elseif cmd[1] == "rule" then
local n = direct.new'rule' local n = direct.new'rule'
setheight(n, cmd[2]) setheight(n, cmd[2])
setwidth(n, cmd[3]) setwidth(n, cmd[3])
nodehandler.rule(p, n, x, y, outer, ...) nodehandler.rule(p, n, x, y, outer, ...)
direct.free(n)
x = x + getwidth(n) x = x + getwidth(n)
direct.free(n)
elseif cmd[1] == "left" then elseif cmd[1] == "left" then
x = x + cmd[2] x = x + cmd[2]
elseif cmd[1] == "down" then elseif cmd[1] == "down" then
@ -533,22 +536,9 @@ local ondemandmeta = {
return t[k] return t[k]
end end
} }
local function writeresources(p) local function nodewriter(file, n, fontdirs, usedglyphs, colorstacks, resources)
local resources = p.resources
local result = {}
for kind, t in pairs(resources) do if next(t) then
result[#result+1] = format("/%s<<", kind)
for name, value in pairs(t) do
result[#result+1] = format("/%s %i 0 R", name, value)
t[name] = nil
end
result[#result+1] = ">>"
end end
return concat(result)
end
local function nodewriter(file, n, fontdirs, usedglyphs, colorstacks)
n = todirect(n) n = todirect(n)
setmetatable(usedglyphs, ondemandmeta) resources = resources or make_resources()
local p = { local p = {
is_page = not not colorstacks, is_page = not not colorstacks,
file = file, file = file,
@ -560,7 +550,7 @@ local function nodewriter(file, n, fontdirs, usedglyphs, colorstacks)
vfont = {}, vfont = {},
matrix = {1, 0, 0, 1, 0, 0}, matrix = {1, 0, 0, 1, 0, 0},
pending_matrix = {}, pending_matrix = {},
resources = setmetatable({}, ondemandmeta), resources = resources,
annots = {}, annots = {},
linkcontext = file.linkcontext, linkcontext = file.linkcontext,
fontdirs = fontdirs, fontdirs = fontdirs,
@ -580,7 +570,7 @@ local function nodewriter(file, n, fontdirs, usedglyphs, colorstacks)
nodehandler[getid(n)](p, n, 0, 0, n, nil, 0) nodehandler[getid(n)](p, n, 0, 0, n, nil, 0)
-- nodehandler[getid(n)](p, n, 0, getdepth(n), n) -- nodehandler[getid(n)](p, n, 0, getdepth(n), n)
topage(p) topage(p)
return concat(p.strings, '\n'), writeresources(p), (p.annots[1] and string.format("/Annots[%s]", table.concat(p.annots, ' ')) or "") return concat(p.strings, '\n'), resources, (p.annots[1] and string.format("/Annots[%s]", table.concat(p.annots, ' ')) or "")
end end
require'luametalatex-pdf-savedbox':init_nodewriter(nodewriter) require'luametalatex-pdf-savedbox':init_nodewriter(nodewriter)
return nodewriter return nodewriter

View File

@ -17,7 +17,7 @@ local function build_sharekey(fontdir)
local encodingbytes = assert(fontdir.encodingbytes) local encodingbytes = assert(fontdir.encodingbytes)
local key = string.format("%i:%s:", fontdir.encodingbytes, fontdir.format) local key = string.format("%i:%s:", fontdir.encodingbytes, fontdir.format)
if encodingbytes == 1 then if encodingbytes == 1 then
if fontdir.format == "type3" then if fontdir.format:sub(1,5) == "type3" then
return string.format("%s%i:%s", key, fontdir.size, fontdir.name) return string.format("%s%i:%s", key, fontdir.size, fontdir.name)
end end
key = string.format("%s%s:", key, fontdir.encoding or '') key = string.format("%s%s:", key, fontdir.encoding or '')

View File

@ -250,27 +250,34 @@ local function buildfont0(pdf, fontdir, usedcids)
cidfont) cidfont)
end end
local buildfontpk = require'luametalatex-pdf-font-pk' local buildfontpk = require'luametalatex-pdf-font-pk'
local buildfontnode = require'luametalatex-pdf-font-node'.buildfont
local function buildfont3(pdf, fontdir, usedcids) local function buildfont3(pdf, fontdir, usedcids)
local buildfont = fontdir.format == 'type3' and buildfontpk
or fontdir.format == 'type3node' and buildfontnode
or error[[Unsupported Type3 based font format]]
usedcids = usedcids or allcids(fontdir) usedcids = usedcids or allcids(fontdir)
table.sort(usedcids, function(a,b) return a[1]<b[1] end) table.sort(usedcids, function(a,b) return a[1]<b[1] end)
local enc = cidmap1byte(pdf) local enc = cidmap1byte(pdf)
local bbox, matrix, widths, charprocs = buildfontpk(pdf, fontdir, usedcids) -- TOOD local bbox, matrix, widths, charprocs = buildfont(pdf, fontdir, usedcids)
local touni = pdf:stream(nil, "", tounicode[1](fontdir, usedcids)) -- Done late to allow for defaults set from the font file local touni = pdf:stream(nil, "", tounicode[1](fontdir, usedcids)) -- Done late to allow for defaults set from the font file
return strip_floats(string.format( local matrices = strip_floats(string.format(
"<</Type/Font/Subtype/Type3/FontBBox[%f %f %f %f]/FontMatrix[%f %f %f %f %f %f]/CharProcs%s/Encoding%s/FirstChar %i/LastChar %i/Widths %i 0 R/ToUnicode %i 0 R>>", "/FontBBox[%f %f %f %f]/FontMatrix[%f %f %f %f %f %f]",
-- "<</Type/Font/Subtype/Type3/FontBBox[%f %f %f %f]/FontMatrix[%f %f %f %f %f %f]/CharProcs%s/Encoding%s/FirstChar %i/LastChar %i/Widths[%s]/ToUnicode %i 0 R/FontDescriptor %i 0 R>>",
bbox[1], bbox[2], bbox[3], bbox[4], bbox[1], bbox[2], bbox[3], bbox[4],
matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6]))
return string.format(
"<</Type/Font/Subtype/Type3%s/CharProcs%s/Encoding%s/FirstChar %i/LastChar %i/Widths %i 0 R/ToUnicode %i 0 R>>",
-- "<</Type/Font/Subtype/Type3/CharProcs%s/Encoding%s/FirstChar %i/LastChar %i/Widths %i 0 R/ToUnicode %i 0 R/FontDescriptor %i 0 R>>",
matrices,
charprocs, charprocs,
encodingtype3(pdf), encodingtype3(pdf),
usedcids[1][1], usedcids[1][1],
usedcids[#usedcids][1], usedcids[#usedcids][1],
widths, widths,
touni touni
)) -- , descriptor) -- TODO ) -- , descriptor) -- TODO
end end
return function(pdf, fontdir, usedcids) return function(pdf, fontdir, usedcids)
if fontdir.format == "type3" then if fontdir.format:sub(1,5) == "type3" then
return buildfont3(pdf, fontdir, usedcids) return buildfont3(pdf, fontdir, usedcids)
else else
return buildfont0(pdf, fontdir, usedcids) return buildfont0(pdf, fontdir, usedcids)

View File

@ -19,7 +19,7 @@ local function shipout(pfile, xform, fontdirs, usedglyphs)
texio.write_nl('term and log', 'WARNING (savedboxresource shipout): Ignoring unsupported PDF variables xformattr and xformresources. Specify resources and attributes for specific XForms instead.') texio.write_nl('term and log', 'WARNING (savedboxresource shipout): Ignoring unsupported PDF variables xformattr and xformresources. Specify resources and attributes for specific XForms instead.')
end end
local bbox = strip_floats(string.format('/BBox[%f %f %f %f]', -to_bp(margin), -to_bp(list.depth+margin), to_bp(list.width+margin), to_bp(list.height+margin))) local bbox = strip_floats(string.format('/BBox[%f %f %f %f]', -to_bp(margin), -to_bp(list.depth+margin), to_bp(list.width+margin), to_bp(list.height+margin)))
local dict = string.format('/Subtype/Form%s/Resources<<%s%s>>%s', bbox, resources, xform.resources or '', xform.attributes or '') local dict = string.format('/Subtype/Form%s/Resources%s%s', bbox, resources(xform.resources), xform.attributes or '')
node.flush_list(list) node.flush_list(list)
xform.list = nil xform.list = nil
local objnum = pfile:stream(xform.objnum, dict, out) local objnum = pfile:stream(xform.objnum, dict, out)