[use/save]boxresource, the Lua part

This commit is contained in:
Marcel Krüger 2020-06-29 19:04:09 +02:00
parent f7a76b69d8
commit efedcba3e1
3 changed files with 148 additions and 21 deletions

View File

@ -47,11 +47,29 @@ token.luacmd("shipout", function()
local out, resources, annots = writer(pfile, list, fontdirs, usedglyphs, colorstacks)
cur_page = nil
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>>]], parent, content, -math.ceil(list.depth/65781.76), math.ceil(list.width/65781.76), math.ceil(list.height/65781.76), resources, annots))
pfile:indirect(page, string.format([[<</Type/Page/Parent %i 0 R/Contents %i 0 R/MediaBox[0 %i %i %i]/Resources<<%s>>%s>>]], parent, content, -math.ceil(list.depth/65781.76), math.ceil(list.width/65781.76), math.ceil(list.height/65781.76), resources, annots))
node.flush_list(list)
token.put_next(token.create'immediateassignment', token.create'global', token.create'deadcycles', token.create(0x30), token.create'relax')
token.scan_token()
end, 'force', 'protected')
local savedbox = require'luametalatex-pdf-savedbox'
local savedbox_save = savedbox.save
function tex.saveboxresource(n, attr, resources, immediate, type, margin, pfile)
if not node.type(n) then
n = tonumber(n)
if not n then
error[[Invalid argument to saveboxresource]]
end
token.put_next(token.create'box', token.new(n, token.command_id'char_given'))
n = token.scan_list()
end
margin = margin or tex.sp'1bp' -- FIXME: default margin variable
return savedbox_save(pfile or get_pfile(), n, attr, resources, immediate, type, margin, fontdirs, usedglyphs)
end
tex.useboxresource = savedbox.use
-- TODO: savedbox TeX interface
local infodir = ""
local namesdir = ""
local catalogdir = ""
@ -257,12 +275,16 @@ local function addlinkpoint(p, link, x, y, list, kind)
end
end
local function linkcontext_set(linkcontext, p, x, y, list, level, kind)
if not p.is_page then return end
for _,l in ipairs(linkcontext) do if l.level == level then
addlinkpoint(p, l, x, y, list, level, kind)
end end
end
function do_start_link(prop, p, n, x, y, outer, _, level)
if not p.is_page then
error[[No link allowed here]]
end
local links = p.linkcontext
if not links then
links = {set = linkcontext_set}
@ -273,10 +295,14 @@ function do_start_link(prop, p, n, x, y, outer, _, level)
addlinkpoint(p, link, x, y, outer, 'start')
end
function do_end_link(prop, p, n, x, y, outer, _, level)
if not p.is_page then
error[[No link allowed here]]
end
local links = p.linkcontext
if not links then error"No link here to end" end
local link = links[#links]
links[#links] = nil
if not links[1] then p.linkcontext = nil end
if link.level ~= level then error"Wrong link level" end
addlinkpoint(p, link, x, y, outer, 'final')
end
@ -367,9 +393,10 @@ local function do_colorstack(prop, p, n, x, y)
local stack
if p.is_page then
stack = colorstack.page_stack
elseif prop.last_form == resources then
elseif colorstack.last_form == p.resources then
stack = colorstack.form_stack
else
colorstack.last_form = p.resources
stack = {prop.default}
colorstack.form_stack = stack
end

View File

@ -77,6 +77,7 @@ end
local function sp2bp(sp)
return sp/65781.76
end
local fontnames = setmetatable({}, {__index = function(t, k) local res = format("F%i", k) t[k] = res return res end})
local topage
local function totext(p, fid)
local last = p.mode
@ -91,8 +92,12 @@ local function totext(p, fid)
if last == text and p.font.fid == fid then return end
local f = font.getfont(fid) or font.fonts[fid]
if last ~= text then p.strings[#p.strings+1] = "BT" p.pos.lx, p.pos.ly, p.pos.x, p.pos.y, p.font.exfactor, p.font.extend, p.font.squeeze, p.font.slant = 0, 0, 0, 0, 0, 1, 1, 0 end
p:fontprovider(f, fid)
-- p.strings[#p.strings+1] = format("/F%i %f Tf 0 Tr", f.parent, sp2bp(f.size)) -- TODO: Setting the mode, expansion, etc.
if not f.parent then f.parent = pdf.getfontname(fid) end
p.resources.Font[fontnames[f.parent]] = p.fontdirs[f.parent]
p.strings[#p.strings+1] = format("/F%i %f Tf 0 Tr", f.parent, sp2bp(f.size)) -- TODO: Setting the mode, width, etc.
p.font.usedglyphs = p.usedglyphs[f.parent]
p.font.fid = fid
p.font.font = f
local need_tm = false
@ -257,6 +262,7 @@ local user_rule = rulesubtypes.user
local empty_rule = rulesubtypes.empty
local outline_rule = rulesubtypes.outline
local ship_img = require'luametalatex-pdf-image'.ship
local ship_box = require'luametalatex-pdf-savedbox'.ship
-- print(require'inspect'(node.subtypes('glue')))
-- print(require'inspect'(node.fields('glue')))
-- print(require'inspect'(node.fields('rule')))
@ -266,10 +272,10 @@ function nodehandler.rule(p, n, x, y, outer)
if getheight(n) == -1073741824 then setheight(n, getheight(outer)) end
if getdepth(n) == -1073741824 then setdepth(n, getdepth(outer)) end
local sub = getsubtype(n)
if getwidth(n) <= 0 or getdepth(n) + getheight(n) <= 0 then return end
if sub == box_rule then
error[[We can't handle boxes yet]]
ship_box(getdata(n), p, n, x, y)
elseif sub == image_rule then
if getwidth(n) <= 0 or getdepth(n) + getheight(n) <= 0 then return end
ship_img(getdata(n), p, n, x, y)
elseif sub == empty_rule then
elseif sub == user_rule then
@ -277,7 +283,6 @@ function nodehandler.rule(p, n, x, y, outer)
elseif sub == outline_rule then
error[[We can't handle outline rules yet]]
else
if getwidth(n) <= 0 or getdepth(n) + getheight(n) <= 0 then return end
topage(p)
p.strings[#p.strings+1] = gsub(format("%f %f %f %f re f", sp2bp(x), sp2bp(y - getdepth(n)), sp2bp(getwidth(n)), sp2bp(getdepth(n) + getheight(n))), '%.?0+ ', ' ')
end
@ -453,13 +458,13 @@ function nodehandler.glyph(p, n, x, y, ...)
else
p.pending[#p.pending+1] = pdf_escape(string.pack('>H', index))
end
if not p.usedglyphs[index] then
p.usedglyphs[index] = {index, math.floor(c.width * 1000 / f.size / p.font.extend + .5), c.tounicode}
if not p.font.usedglyphs[index] then
p.font.usedglyphs[index] = {index, math.floor(c.width * 1000 / f.size / p.font.extend + .5), c.tounicode}
end
else
p.pending[#p.pending+1] = pdf_escape(string.char(getchar(n)))
if not p.usedglyphs[getchar(n)] then
p.usedglyphs[getchar(n)] = {getchar(n), math.floor(c.width * 1000 / f.size / p.font.extend + .5), c.tounicode}
if not p.font.usedglyphs[getchar(n)] then
p.font.usedglyphs[getchar(n)] = {getchar(n), math.floor(c.width * 1000 / f.size / p.font.extend + .5), c.tounicode}
end
end
p.pos.x = p.pos.x + math.floor(getwidth(n)*(1+getexpansion(n)/1000000)+.5)
@ -521,7 +526,7 @@ local ondemandmeta = {
}
local function writeresources(p)
local resources = p.resources
local result = {"<<"}
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
@ -530,11 +535,9 @@ local function writeresources(p)
end
result[#result+1] = ">>"
end end
result[#result+1] = ">>"
return concat(result)
end
local fontnames = setmetatable({}, {__index = function(t, k) local res = format("F%i", k) t[k] = res return res end})
return function(file, n, fontdirs, usedglyphs, colorstacks)
local function nodewriter(file, n, fontdirs, usedglyphs, colorstacks)
n = todirect(n)
setmetatable(usedglyphs, ondemandmeta)
local p = {
@ -544,12 +547,6 @@ return function(file, n, fontdirs, usedglyphs, colorstacks)
strings = {},
pending = {},
pos = {},
fontprovider = function(p, f, fid)
if not f.parent then f.parent = pdf.getfontname(fid) end
p.resources.Font[fontnames[f.parent]] = fontdirs[f.parent]
p.strings[#p.strings+1] = format("/F%i %f Tf 0 Tr", f.parent, sp2bp(f.size)) -- TODO: Setting the mode, expansion, etc.
p.usedglyphs = usedglyphs[f.parent]
end,
font = {},
vfont = {},
matrix = {1, 0, 0, 1, 0, 0},
@ -557,6 +554,8 @@ return function(file, n, fontdirs, usedglyphs, colorstacks)
resources = setmetatable({}, ondemandmeta),
annots = {},
linkcontext = file.linkcontext,
fontdirs = fontdirs,
usedglyphs = usedglyphs,
}
if colorstacks then
for i=1, #colorstacks do
@ -574,3 +573,5 @@ return function(file, n, fontdirs, usedglyphs, colorstacks)
topage(p)
return concat(p.strings, '\n'), writeresources(p), (p.annots[1] and string.format("/Annots[%s]", table.concat(p.annots, ' ')) or "")
end
require'luametalatex-pdf-savedbox':init_nodewriter(nodewriter)
return nodewriter

View File

@ -0,0 +1,99 @@
local writer -- = require'luametalatex-nodewriter' -- This would introduce some cyclic dependency
-- XForms currently have the form {width, height, depth, objnum, attributes, list, margin}
local xforms = {}
local function to_bp(sp) return sp/65781.76 end
local function shipout(pfile, xform, fontdirs, usedglyphs)
local list, margin = xform.list, xform.margin
if not list then return xform.objnum end -- Already shipped out
local last_page = cur_page cur_page = nil
local out, resources, annots = writer(pfile, list, fontdirs, usedglyphs)
cur_page = last_page
assert(annots == '')
local dict = string.format('/Subtype/Form/BBox[%f %f %f %f]/Resources<<%s%s>>%s', -to_bp(margin), -to_bp(list.depth+margin), to_bp(list.width+margin), to_bp(list.height+margin), resources, xform.resources or '', xform.attributes or '')
node.flush_list(list)
xform.list = nil
local objnum = pfile:stream(xform.objnum, dict, out)
xform.objnum = objnum
return objnum
end
local function save(pfile, n, attr, resources, immediate, type, margin, fontdirs, usedglyphs)
local index = #xforms+1
local xform = {
list = assert(n, 'List required for saveboxresource'),
width = n.width,
height = n.height,
depth = n.depth,
attributes = attr,
resources = resources,
margin = margin,
-- type = type, -- TODO: Not yet used. Do we need this at all?
}
xforms[index] = xform
if immediate then
shipout(pfile, xform, fontdirs, usedglyphs)
end
return index
end
local function adjust_sizes(width, height, depth, real_width, real_height, real_depth)
if not depth then
if height then
local scale = height/real_height
depth = (real_depth*scale + .5)//1
width = width or (real_width*scale + .5)//1
elseif width then
local scale = width/real_width
depth = (real_depth*scale + .5)//1
height = (real_height*scale + .5)//1
else
width, height, depth = real_width, real_height, real_depth
end
elseif height then
width = width or (real_width*(height+depth)/(real_height+real_depth) + .5)//1
else
width = width or real_width
local scale = width/real_width
height = ((real_depth+real_height)*scale + .5)//1 - depth
end
return width, height, depth
end
local ruleid = node.id'rule'
local ruletypes = node.subtypes'rule'
local boxrule
for n, name in next, ruletypes do
if name == 'box' then boxrule = n break end
end
local function use(index, width, height, depth)
local xform = xforms[index]
if not xform then return nil, nil, nil, nil end
width, height, depth = adjust_sizes(width, height, depth, xform.width, xform.height, xform.depth)
local n = node.direct.new(ruleid, boxrule)
node.direct.setdata(n, index)
node.direct.setwhd(n, width, height, depth)
return node.direct.tonode(n), width, height, depth
end
local function do_box(data, p, n, x, y)
local xform = assert(xforms[data], 'Invalid XForm')
local objnum = shipout(p.file, xform, p.fontdirs, p.usedglyphs)
local width, height, depth = node.direct.getwhd(n)
local xscale, yscale = width / xform.width, (height+depth) / (xform.height+xform.depth)
p.resources.XObject['Fm' .. tostring(data)] = objnum
pdf.write('page', string.format('q %f 0 0 %f %f %f cm /Fm%i Do Q',
xscale, yscale,
to_bp(x), to_bp(y-depth+yscale*xform.depth),
data), nil, nil, p)
end
return {
save = save,
use = use,
ship = do_box,
init_nodewriter = function(t, nodewriter) writer, t.init_nodewriter = nodewriter, nil end,
}