Some backend stuff

This commit is contained in:
Marcel Krüger 2020-05-31 09:30:49 +02:00
parent c0954fe7f6
commit 4c0794032c
6 changed files with 228 additions and 34 deletions

View File

@ -11,10 +11,14 @@ local colorstacks = {{
page_stack = {"0 g 0 G"}, page_stack = {"0 g 0 G"},
}} }}
token.scan_list = token.scan_box -- They are equal if no parameter is present token.scan_list = token.scan_box -- They are equal if no parameter is present
token.luacmd("shipout", function() local function get_pfile()
if not pfile then if not pfile then
pfile = newpdf.open(tex.jobname .. '.pdf') pfile = newpdf.open(tex.jobname .. '.pdf')
end end
return pfile
end
token.luacmd("shipout", function()
local pfile = get_pfile()
local voff = node.new'kern' local voff = node.new'kern'
voff.kern = tex.voffset + pdf.variable.vorigin voff.kern = tex.voffset + pdf.variable.vorigin
voff.next = token.scan_list() voff.next = token.scan_list()
@ -30,7 +34,14 @@ token.luacmd("shipout", function()
token.scan_token() token.scan_token()
end, 'force', 'protected') end, 'force', 'protected')
local infodir = "" local infodir = ""
local catalogdir = ""
local creationdate = os.date("D:%Y%m%d%H%M%S%z"):gsub("+0000$", "Z"):gsub("%d%d$", "'%0") local creationdate = os.date("D:%Y%m%d%H%M%S%z"):gsub("+0000$", "Z"):gsub("%d%d$", "'%0")
-- TODO: write_catalogdir is never called yet
local function write_catalogdir(p)
local additional = ""
error[[Not implemented]]
return p:indirect(nil, string.format("<<%s%s>>", catalogdir, additional))
end
local function write_infodir(p) local function write_infodir(p)
local additional = "" local additional = ""
if not string.find(infodir, "/CreationDate", 1, false) then if not string.find(infodir, "/CreationDate", 1, false) then
@ -87,6 +98,9 @@ token.luacmd("pdfvariable", function()
end) end)
local whatsit_id = node.id'whatsit' local whatsit_id = node.id'whatsit'
local whatsits = node.whatsits() local whatsits = node.whatsits()
local lastobj = -1
function pdf.newcolorstack(default, mode, page) function pdf.newcolorstack(default, mode, page)
local idx = #colorstacks local idx = #colorstacks
colorstacks[idx + 1] = { colorstacks[idx + 1] = {
@ -97,6 +111,9 @@ function pdf.newcolorstack(default, mode, page)
} }
return idx return idx
end end
local function do_refobj(prop, p, n, x, y)
pfile:reference(prop.obj)
end
local function do_literal(prop, p, n, x, y) local function do_literal(prop, p, n, x, y)
pdf.write(prop.mode, prop.data, x, y, p) pdf.write(prop.mode, prop.data, x, y, p)
end end
@ -165,13 +182,15 @@ token.luacmd("pdffeedback", function()
tex.sprint(tostring(pdf.newcolorstack(default, mode, page))) tex.sprint(tostring(pdf.newcolorstack(default, mode, page)))
elseif token.scan_keyword"creationdate" then elseif token.scan_keyword"creationdate" then
tex.sprint(creationdate) tex.sprint(creationdate)
elseif token.scan_keyword"lastobj" then
tex.sprint(tostring(lastobj))
else else
-- The following error message gobbles the next word as a side effect. -- The following error message gobbles the next word as a side effect.
-- This is intentional to make error-recovery easier. -- This is intentional to make error-recovery easier.
error(string.format("Unknown PDF feedback %s", token.scan_word())) error(string.format("Unknown PDF feedback %s", token.scan_word()))
end end
end) end)
token.luacmd("pdfextension", function() token.luacmd("pdfextension", function(_, imm)
if token.scan_keyword"colorstack" then if token.scan_keyword"colorstack" then
write_colorstack() write_colorstack()
elseif token.scan_keyword"literal" then elseif token.scan_keyword"literal" then
@ -186,6 +205,40 @@ token.luacmd("pdfextension", function()
node.write(whatsit) node.write(whatsit)
elseif token.scan_keyword"info" then elseif token.scan_keyword"info" then
infodir = infodir .. token.scan_string() infodir = infodir .. token.scan_string()
elseif token.scan_keyword"catalog" then
catalogdir = catalogdir .. token.scan_string()
elseif token.scan_keyword"obj" then
local pfile = get_pfile()
if token.scan_keyword"reserveobjnum" then
lastobj = pfile:getobj()
else
local num = token.scan_keyword'useobjnum' and token.scan_int() or pfile:getobj()
lastobj = num
local attr = token.scan_keyword'stream' and (token.scan_keyword'attr' and token.scan_string() or '')
local isfile = token.scan_keyword'file'
local content = token.scan_string()
if immediate then
if attr then
pfile:stream(num, attr, content, isfile)
else
pfile:indirect(num, attr, content, isfile)
end
else
if attr then
pfile:delayedstream(num, attr, content, isfile)
else
pfile:delayed(num, attr, content, isfile)
end
end
end
elseif token.scan_keyword"refobj" then
local num = token.scan_int()
local whatsit = node.new(whatsit_id, whatsits.pdf_refobj)
node.setproperty(whatsit, {
obj = num,
handle = do_refobj,
})
node.write(whatsit)
else else
-- The following error message gobbles the next word as a side effect. -- The following error message gobbles the next word as a side effect.
-- This is intentional to make error-recovery easier. -- This is intentional to make error-recovery easier.

View File

@ -9,6 +9,7 @@
end end
local dimen_cmd = token.command_id'assign_dimen' local dimen_cmd = token.command_id'assign_dimen'
local count_cmd = token.command_id'assign_int' local count_cmd = token.command_id'assign_int'
local toks_cmd = token.command_id'assign_toks'
local tex_params = {} local tex_params = {}
local texmeta = getmetatable(tex) local texmeta = getmetatable(tex)
local texmetaoldindex = texmeta.__index local texmetaoldindex = texmeta.__index
@ -20,6 +21,8 @@
return tex.count[v.index] return tex.count[v.index]
elseif v.command == dimen_cmd then elseif v.command == dimen_cmd then
return tex.dimen[v.index] return tex.dimen[v.index]
elseif v.command == toks_cmd then
return tex.toks[v.index]
end end
else else
return texmetaoldindex(t, k) return texmetaoldindex(t, k)
@ -32,6 +35,8 @@
tex.count[p.index] = v tex.count[p.index] = v
elseif p.command == dimen_cmd then elseif p.command == dimen_cmd then
tex.dimen[p.index] = v tex.dimen[p.index] = v
elseif p.command == toks_cmd then
tex.toks[p.index] = v
end end
else else
return texmetaoldnewindex(t, k, v) return texmetaoldnewindex(t, k, v)
@ -47,6 +52,8 @@
return tex.count[v.index] return tex.count[v.index]
elseif v.command == dimen_cmd then elseif v.command == dimen_cmd then
return tex.dimen[v.index] return tex.dimen[v.index]
elseif v.command == toks_cmd then
return tex.toks[v.index]
end end
else else
return texmetaoldindex(t, k) return texmetaoldindex(t, k)
@ -59,6 +66,8 @@
tex.count[p.index] = v tex.count[p.index] = v
elseif p.command == dimen_cmd then elseif p.command == dimen_cmd then
tex.dimen[p.index] = v tex.dimen[p.index] = v
elseif p.command == toks_cmd then
tex.toks[p.index] = v
end end
else else
return texmetaoldnewindex(t, k, v) return texmetaoldnewindex(t, k, v)
@ -87,10 +96,12 @@
\texAlloc{dimen}{pagewidth}{210mm} \texAlloc{dimen}{pagewidth}{210mm}
\pdfAlloc{dimen}{horigin}{1in} \pdfAlloc{dimen}{horigin}{1in}
\pdfAlloc{dimen}{vorigin}{1in} \pdfAlloc{dimen}{vorigin}{1in}
\pdfAlloc{dimen}{linkmargin}{0pt}
\pdfAlloc{count}{majorversion}{1} \pdfAlloc{count}{majorversion}{1}
\pdfAlloc{count}{minorversion}{7} \pdfAlloc{count}{minorversion}{7}
\pdfAlloc{count}{compresslevel}{0} % 0 is actually the only supported value right now, so this is basically ignored \pdfAlloc{count}{compresslevel}{0} % 0 is actually the only supported value right now, so this is basically ignored
\pdfAlloc{count}{objcompresslevel}{0} % see above \pdfAlloc{count}{objcompresslevel}{0} % see above
\pdfAlloc{toks}{pageresources}{{}}
\texAlloc{count}{bodydirection}{0} \texAlloc{count}{bodydirection}{0}
\texAlloc{count}{pagedirection}{0} \texAlloc{count}{pagedirection}{0}

View File

@ -51,6 +51,7 @@ function token.luacmd(name, func, ...)
return idx return idx
end end
local properties = node.direct.get_properties_table() local properties = node.direct.get_properties_table()
node.direct.properties = properties
-- setmetatable(node.direct.get_properties_table(), { -- setmetatable(node.direct.get_properties_table(), {
-- __index = function(t, id) -- __index = function(t, id)
-- local new = {} -- local new = {}
@ -218,3 +219,25 @@ else
-- end -- end
end end
require'luametalatex-back-pdf' require'luametalatex-back-pdf'
require'luametalatex-node-luaotfload'
local integer_code do
local value_values = token.values'value'
for i=0,#value_values do
if value_values[i] == "integer" then
integer_code = i
break
end
end
end
token.luacmd("Umathcodenum", function(_, scanning)
if scanning then
local class, family, char = tex.getmathcodes (token.scan_int())
return integer_code, char | (class | family << 3) << 21
else
local char = token.scan_int()
local mathcode = token.scan_int()
tex.setmathcodes(char, (mathcode >> 21) & 7, mathcode >> 24, mathcode & 0x1FFFFF)
end
end, "force", "global", "value")

View File

@ -74,15 +74,36 @@ callback.register('open_data_file', function(name)
}) })
end) end)
callback.register('find_format_file', function(name) return kpse.find_file(name, 'fmt', true) end) callback.register('find_format_file', function(name) return kpse.find_file(name, 'fmt', true) end)
callback.register('show_warning_message', function() callback.register('handle_error_hook', function()
texio.write_nl('WARNING Tag: ' .. status.lastwarningtag) repeat
texio.write_nl(status.lastwarningstring) texio.write_nl'? '
end) local line = io.read()
callback.register('show_error_message', function() if not line then
if status.lasterrorcontext then error[[TODO: Handle EOL]]
texio.write_nl('ERROR Context: ' .. status.lasterrorcontext)
end end
texio.write_nl(status.lasterrorstring) if line == "" then return 3 end
local first = line:sub(1,1):upper()
if first == 'H' then
texio.write(tex.gethelptext())
elseif first == 'I' then
line = line:sub(2)
tex.runtoks(function()
tex.sprint(token.scan_token(), line)
end)
return 3
elseif first == 'Q' then texio.write'OK, entering \\batchmode...\n' return 0
elseif first == 'R' then texio.write'OK, entering \\nonstopmode...\n' return 1
elseif first == 'S' then texio.write'OK, entering \\scrollmode...\n' return 2
elseif first == 'X' then return -1
else
texio.write'Type <return> to proceed, S to scroll future error messages,\
\z R to run without stopping, Q to run quietly,\
\z I to insert something,\
\z H for help, X to quit.'
end
until false
-- print('handle')
return 3
end) end)
callback.register('pre_dump', function() callback.register('pre_dump', function()
lua.prepared_code[1] = string.format("fixupluafunctions(%i)", fixupluafunctions()) lua.prepared_code[1] = string.format("fixupluafunctions(%i)", fixupluafunctions())

View File

@ -2,12 +2,13 @@ local format = string.format
local concat = table.concat local concat = table.concat
local write = texio.write_nl local write = texio.write_nl
local direct = node.direct local direct = node.direct
local properties = direct.get_properties_table() local properties = direct.properties
local tonode = direct.tonode local tonode = direct.tonode
local todirect = direct.todirect local todirect = direct.todirect
local getid = direct.getid local getid = direct.getid
local traverse = direct.traverse local traverse = direct.traverse
local getsubtype = direct.getsubtype local getsubtype = direct.getsubtype
local getdirection = direct.getdirection
local setsubtype = direct.setsubtype local setsubtype = direct.setsubtype
local getdepth = direct.getdepth local getdepth = direct.getdepth
local getheight = direct.getheight local getheight = direct.getheight
@ -27,6 +28,10 @@ local getnext = direct.getnext
local getexpansion = direct.getexpansion local getexpansion = direct.getexpansion
local getchar = direct.getchar local getchar = direct.getchar
local rangedimensions = direct.rangedimensions local rangedimensions = direct.rangedimensions
local traverse_id = direct.traverse_id
local dir_id = node.id'dir'
local function doublekeyed(t, id2name, name2id, index) local function doublekeyed(t, id2name, name2id, index)
return setmetatable(t, { return setmetatable(t, {
__index = index, __index = index,
@ -42,7 +47,8 @@ local function doublekeyed(t, id2name, name2id, index)
end end
local nodehandler = (function() local nodehandler = (function()
local function unknown_handler(_, n, x, y) local function unknown_handler(_, n, x, y)
write(format("Sorry, but the PDF backend does not support %q (id = %i) nodes right now. The supplied node will be dropped at coordinates (%i, %i).", node.type(getid(n)), getid(n), x, y)) print(node.type(10))
write(format("Sorry, but the PDF backend does not support %q (id = %i) nodes right now. The supplied node will be dropped at coordinates (%i, %i).", node.type(getid(n)), getid(n), x//1, y//1))
end end
return doublekeyed({}, node.type, node.id, function() return doublekeyed({}, node.type, node.id, function()
return unknown_handler return unknown_handler
@ -55,7 +61,7 @@ local whatsithandler = (function()
if prop and prop.handle then if prop and prop.handle then
prop:handle(p, n, x, y, ...) prop:handle(p, n, x, y, ...)
else else
write(format("Sorry, but the PDF backend does not support %q (id = %i) whatsits right now. The supplied node will be dropped at coordinates (%i, %i).", whatsits[getsubtype(n)], getsubtype(n), x, y)) write(format("Sorry, but the PDF backend does not support %q (id = %i) whatsits right now. The supplied node will be dropped at coordinates (%i, %i).", whatsits[getsubtype(n)], getsubtype(n), x//1, y//1))
end end
end end
return doublekeyed({}, function(n)return whatsits[n]end, function(n)return whatsits[n]end, function() return doublekeyed({}, function(n)return whatsits[n]end, function(n)return whatsits[n]end, function()
@ -185,9 +191,10 @@ local function write_link(p, link)
for i=1,#quads do quads[i] = nil end for i=1,#quads do quads[i] = nil end
link.objnum = nil link.objnum = nil
end end
local function addlinkpoint(p, link, x, y, list, final) local function addlinkpoint(p, link, x, y, list, kind)
local quads = link.quads local quads = link.quads
print'addlink' local off = pdf.variable.linkmargin
x = kind == 'start' and x-off or x+off
if link.annots and link.annots ~= p.annots then -- We started on another page, let's finish that before starting the new page if link.annots and link.annots ~= p.annots then -- We started on another page, let's finish that before starting the new page
write_link(p, link) write_link(p, link)
link.annots = nil link.annots = nil
@ -198,12 +205,12 @@ local function addlinkpoint(p, link, x, y, list, final)
p.annots[#p.annots+1] = link.objnum .. " 0 R" p.annots[#p.annots+1] = link.objnum .. " 0 R"
end end
local m = p.matrix local m = p.matrix
local lx, ly = projected_point(m, x, y-(link.depth or getdepth(list))) local lx, ly = projected_point(m, x, y-off-(link.depth or getdepth(list)))
local ux, uy = projected_point(m, x, y+(link.height or getheight(list))) local ux, uy = projected_point(m, x, y+off+(link.height or getheight(list)))
local n = #quads local n = #quads
quads[n+1], quads[n+2], quads[n+3], quads[n+4] = lx, ly, ux, uy quads[n+1], quads[n+2], quads[n+3], quads[n+4] = lx, ly, ux, uy
if final or (link.force_separate and (n+4)%8 == 0) then if kind == 'final' or (link.force_separate and (n+4)%8 == 0) then
print(final, n, link.force_separate) print(kind, n, link.force_separate)
write_link(p, link) write_link(p, link)
link.annots = nil link.annots = nil
end end
@ -216,18 +223,56 @@ function nodehandler.hlist(p, list, x0, y, outerlist, origin, level)
x0 = x0 + getshift(list) x0 = x0 + getshift(list)
end end
end end
local direction = getdirection(list)
if direction == 1 then
x0 = x0 + getwidth(list)
end
local dirstack = {}
local dirnodes = {}
for n, sub in traverse_id(dir_id, getlist(list)) do
if sub == 0 then
dirstack[#dirstack + 1] = n
else
local m = dirstack[#dirstack]
dirnodes[m] = n
dirstack[#dirstack] = nil
end
end
for i=1,#dirstack do
dirnodes[dirstack[i]] = rangedimensions(list, dirstack[i])
end
local x = x0 local x = x0
for _,l in ipairs(p.linkcontext) do if l.level == level+1 then for _,l in ipairs(p.linkcontext) do if l.level == level+1 then
addlinkpoint(p, l, x, y, list) addlinkpoint(p, l, x, y, list, 'start')
end end end end
for n in traverse(getlist(list)) do for n, id, sub in traverse(getlist(list)) do
if id == dir_id then
if sub == 0 then
local newdir = getdirection(n)
if newdir ~= direction then
local close = dirnodes[n]
local dim = rangedimensions(list, n, close)
if close then dirnodes[close] = dim end
x = x + (2*newdir-1) * dim
direction = newdir
end
else
local dim = dirnodes[n]
if dim then
direction = 1-direction
x = x + (2*newdir-1) * dim
end
end
else
local next = getnext(n) local next = getnext(n)
local w = next and rangedimensions(list, n, next) or rangedimensions(list, n) local w = next and rangedimensions(list, n, next) or rangedimensions(list, n)
nodehandler[getid(n)](p, n, x, y, list, x0, level+1) if direction == 1 then x = x - w end
x = w + x nodehandler[id](p, n, x, y, list, x0, level+1)
if direction == 0 then x = w + x end
end
end end
for _,l in ipairs(p.linkcontext) do if l.level == level+1 then for _,l in ipairs(p.linkcontext) do if l.level == level+1 then
addlinkpoint(p, l, x, y, list) addlinkpoint(p, l, x, y, list, 'end')
end end end end
end end
function nodehandler.vlist(p, list, x, y0, outerlist, origin, level) function nodehandler.vlist(p, list, x, y0, outerlist, origin, level)
@ -484,14 +529,14 @@ function whatsithandler.pdf_start_link(p, n, x, y, outer, _, level)
local links = p.linkcontext local links = p.linkcontext
local link = {quads = {}, attr = n.link_attr, action = n.action, level = level, force_separate = false} -- force_separate should become an option local link = {quads = {}, attr = n.link_attr, action = n.action, level = level, force_separate = false} -- force_separate should become an option
links[#links+1] = link links[#links+1] = link
addlinkpoint(p, link, x, y, outer) addlinkpoint(p, link, x, y, outer, 'start')
end end
function whatsithandler.pdf_end_link(p, n, x, y, outer, _, level) function whatsithandler.pdf_end_link(p, n, x, y, outer, _, level)
local links = p.linkcontext local links = p.linkcontext
local link = links[#links] local link = links[#links]
links[#links] = nil links[#links] = nil
if link.level ~= level then error"Wrong link level" end if link.level ~= level then error"Wrong link level" end
addlinkpoint(p, link, x, y, outer, true) addlinkpoint(p, link, x, y, outer, 'final')
end end
]] ]]
local global_p, global_x, global_y local global_p, global_x, global_y

View File

@ -6,28 +6,69 @@ local error = error
local pairs = pairs local pairs = pairs
local setmetatable = setmetatable local setmetatable = setmetatable
local assigned = {} local assigned = {}
local function stream(pdf, num, dict, content) local delayed = {}
local function stream(pdf, num, dict, content, isfile)
if not num then num = pdf:getobj() end if not num then num = pdf:getobj() end
if pdf[num] ~= assigned then if pdf[num] ~= assigned then
error[[Invalid object]] error[[Invalid object]]
end end
pdf[num] = {offset = pdf.file:seek()} pdf[num] = {offset = pdf.file:seek()}
if isfile then
local f = io.open(content)
content = f:read'a'
f:close()
end
pdf.file:write(format('%i 0 obj\n<<%s/Length %i>>stream\n', num, dict, #content)) pdf.file:write(format('%i 0 obj\n<<%s/Length %i>>stream\n', num, dict, #content))
pdf.file:write(content) pdf.file:write(content)
pdf.file:write'\nendstream\nendobj\n' pdf.file:write'\nendstream\nendobj\n'
return num return num
end end
local function indirect(pdf, num, content) local function delayedstream(pdf, num, dict, content, isfile)
if not num then num = pdf:getobj() end
if pdf[num] ~= assigned then
error[[Invalid object]]
end
pdf[num] = delayed
pdf[-num] = {stream, dict, content, isfile}
return num
end
local function indirect(pdf, num, content, isfile)
if not num then num = pdf:getobj() end if not num then num = pdf:getobj() end
if pdf[num] ~= assigned then if pdf[num] ~= assigned then
error[[Invalid object]] error[[Invalid object]]
end end
pdf[num] = {offset = pdf.file:seek()} pdf[num] = {offset = pdf.file:seek()}
pdf.file:write(format('%i 0 obj\n', num)) pdf.file:write(format('%i 0 obj\n', num))
if isfile then
local f = io.open(content)
content = f:read'a'
f:close()
end
pdf.file:write(content) pdf.file:write(content)
pdf.file:write'\nendobj\n' pdf.file:write'\nendobj\n'
return num return num
end end
local function delayed(pdf, num, content, isfile)
if not num then num = pdf:getobj() end
if pdf[num] ~= assigned then
error[[Invalid object]]
end
pdf[num] = delayed
pdf[-num] = {indirect, content, isfile}
return num
end
local function reference(pdf, num)
local status = pdf[num]
if status == delayed then
local saved = pdf[-num]
pdf[-num] = nil
pdf[num] = assigned
return saved[1](pdf, num, table.unpack(saved, 2))
elseif status == assigned or not status then
error[[Invalid object]]
-- else -- Already written
end
end
local function getid(pdf) local function getid(pdf)
local id = pdf[0] + 1 local id = pdf[0] + 1
pdf[0] = id pdf[0] = id
@ -73,9 +114,9 @@ local pdfmeta = {
stream = stream, stream = stream,
newpage = pagetree.newpage, newpage = pagetree.newpage,
writepages = pagetree.write, writepages = pagetree.write,
-- delayed = delayed, delayed = delayed,
-- delayedstream = delayedstream, delayedstream = delayedstream,
-- reference reference = reference,
} }
pdfmeta.__index = pdfmeta pdfmeta.__index = pdfmeta
local function open(filename) local function open(filename)