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"},
}}
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
pfile = newpdf.open(tex.jobname .. '.pdf')
end
return pfile
end
token.luacmd("shipout", function()
local pfile = get_pfile()
local voff = node.new'kern'
voff.kern = tex.voffset + pdf.variable.vorigin
voff.next = token.scan_list()
@ -30,7 +34,14 @@ token.luacmd("shipout", function()
token.scan_token()
end, 'force', 'protected')
local infodir = ""
local catalogdir = ""
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 additional = ""
if not string.find(infodir, "/CreationDate", 1, false) then
@ -87,6 +98,9 @@ token.luacmd("pdfvariable", function()
end)
local whatsit_id = node.id'whatsit'
local whatsits = node.whatsits()
local lastobj = -1
function pdf.newcolorstack(default, mode, page)
local idx = #colorstacks
colorstacks[idx + 1] = {
@ -97,6 +111,9 @@ function pdf.newcolorstack(default, mode, page)
}
return idx
end
local function do_refobj(prop, p, n, x, y)
pfile:reference(prop.obj)
end
local function do_literal(prop, p, n, x, y)
pdf.write(prop.mode, prop.data, x, y, p)
end
@ -165,13 +182,15 @@ token.luacmd("pdffeedback", function()
tex.sprint(tostring(pdf.newcolorstack(default, mode, page)))
elseif token.scan_keyword"creationdate" then
tex.sprint(creationdate)
elseif token.scan_keyword"lastobj" then
tex.sprint(tostring(lastobj))
else
-- The following error message gobbles the next word as a side effect.
-- This is intentional to make error-recovery easier.
error(string.format("Unknown PDF feedback %s", token.scan_word()))
end
end)
token.luacmd("pdfextension", function()
token.luacmd("pdfextension", function(_, imm)
if token.scan_keyword"colorstack" then
write_colorstack()
elseif token.scan_keyword"literal" then
@ -186,6 +205,40 @@ token.luacmd("pdfextension", function()
node.write(whatsit)
elseif token.scan_keyword"info" then
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
-- The following error message gobbles the next word as a side effect.
-- This is intentional to make error-recovery easier.

View File

@ -9,6 +9,7 @@
end
local dimen_cmd = token.command_id'assign_dimen'
local count_cmd = token.command_id'assign_int'
local toks_cmd = token.command_id'assign_toks'
local tex_params = {}
local texmeta = getmetatable(tex)
local texmetaoldindex = texmeta.__index
@ -20,6 +21,8 @@
return tex.count[v.index]
elseif v.command == dimen_cmd then
return tex.dimen[v.index]
elseif v.command == toks_cmd then
return tex.toks[v.index]
end
else
return texmetaoldindex(t, k)
@ -32,6 +35,8 @@
tex.count[p.index] = v
elseif p.command == dimen_cmd then
tex.dimen[p.index] = v
elseif p.command == toks_cmd then
tex.toks[p.index] = v
end
else
return texmetaoldnewindex(t, k, v)
@ -47,6 +52,8 @@
return tex.count[v.index]
elseif v.command == dimen_cmd then
return tex.dimen[v.index]
elseif v.command == toks_cmd then
return tex.toks[v.index]
end
else
return texmetaoldindex(t, k)
@ -59,6 +66,8 @@
tex.count[p.index] = v
elseif p.command == dimen_cmd then
tex.dimen[p.index] = v
elseif p.command == toks_cmd then
tex.toks[p.index] = v
end
else
return texmetaoldnewindex(t, k, v)
@ -87,10 +96,12 @@
\texAlloc{dimen}{pagewidth}{210mm}
\pdfAlloc{dimen}{horigin}{1in}
\pdfAlloc{dimen}{vorigin}{1in}
\pdfAlloc{dimen}{linkmargin}{0pt}
\pdfAlloc{count}{majorversion}{1}
\pdfAlloc{count}{minorversion}{7}
\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{toks}{pageresources}{{}}
\texAlloc{count}{bodydirection}{0}
\texAlloc{count}{pagedirection}{0}

View File

@ -51,6 +51,7 @@ function token.luacmd(name, func, ...)
return idx
end
local properties = node.direct.get_properties_table()
node.direct.properties = properties
-- setmetatable(node.direct.get_properties_table(), {
-- __index = function(t, id)
-- local new = {}
@ -218,3 +219,25 @@ else
-- end
end
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)
callback.register('find_format_file', function(name) return kpse.find_file(name, 'fmt', true) end)
callback.register('show_warning_message', function()
texio.write_nl('WARNING Tag: ' .. status.lastwarningtag)
texio.write_nl(status.lastwarningstring)
end)
callback.register('show_error_message', function()
if status.lasterrorcontext then
texio.write_nl('ERROR Context: ' .. status.lasterrorcontext)
end
texio.write_nl(status.lasterrorstring)
callback.register('handle_error_hook', function()
repeat
texio.write_nl'? '
local line = io.read()
if not line then
error[[TODO: Handle EOL]]
end
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)
callback.register('pre_dump', function()
lua.prepared_code[1] = string.format("fixupluafunctions(%i)", fixupluafunctions())

View File

@ -2,12 +2,13 @@ local format = string.format
local concat = table.concat
local write = texio.write_nl
local direct = node.direct
local properties = direct.get_properties_table()
local properties = direct.properties
local tonode = direct.tonode
local todirect = direct.todirect
local getid = direct.getid
local traverse = direct.traverse
local getsubtype = direct.getsubtype
local getdirection = direct.getdirection
local setsubtype = direct.setsubtype
local getdepth = direct.getdepth
local getheight = direct.getheight
@ -27,6 +28,10 @@ local getnext = direct.getnext
local getexpansion = direct.getexpansion
local getchar = direct.getchar
local rangedimensions = direct.rangedimensions
local traverse_id = direct.traverse_id
local dir_id = node.id'dir'
local function doublekeyed(t, id2name, name2id, index)
return setmetatable(t, {
__index = index,
@ -42,7 +47,8 @@ local function doublekeyed(t, id2name, name2id, index)
end
local nodehandler = (function()
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
return doublekeyed({}, node.type, node.id, function()
return unknown_handler
@ -55,7 +61,7 @@ local whatsithandler = (function()
if prop and prop.handle then
prop:handle(p, n, x, y, ...)
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
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
link.objnum = nil
end
local function addlinkpoint(p, link, x, y, list, final)
local function addlinkpoint(p, link, x, y, list, kind)
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
write_link(p, link)
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"
end
local m = p.matrix
local lx, ly = projected_point(m, x, y-(link.depth or getdepth(list)))
local ux, uy = projected_point(m, x, y+(link.height or getheight(list)))
local lx, ly = projected_point(m, x, y-off-(link.depth or getdepth(list)))
local ux, uy = projected_point(m, x, y+off+(link.height or getheight(list)))
local n = #quads
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
print(final, n, link.force_separate)
if kind == 'final' or (link.force_separate and (n+4)%8 == 0) then
print(kind, n, link.force_separate)
write_link(p, link)
link.annots = nil
end
@ -216,18 +223,56 @@ function nodehandler.hlist(p, list, x0, y, outerlist, origin, level)
x0 = x0 + getshift(list)
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
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
for n in traverse(getlist(list)) do
local next = getnext(n)
local w = next and rangedimensions(list, n, next) or rangedimensions(list, n)
nodehandler[getid(n)](p, n, x, y, list, x0, level+1)
x = w + x
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 w = next and rangedimensions(list, n, next) or rangedimensions(list, n)
if direction == 1 then x = x - w end
nodehandler[id](p, n, x, y, list, x0, level+1)
if direction == 0 then x = w + x end
end
end
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
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 link = {quads = {}, attr = n.link_attr, action = n.action, level = level, force_separate = false} -- force_separate should become an option
links[#links+1] = link
addlinkpoint(p, link, x, y, outer)
addlinkpoint(p, link, x, y, outer, 'start')
end
function whatsithandler.pdf_end_link(p, n, x, y, outer, _, level)
local links = p.linkcontext
local link = links[#links]
links[#links] = nil
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
]]
local global_p, global_x, global_y

View File

@ -6,28 +6,69 @@ local error = error
local pairs = pairs
local setmetatable = setmetatable
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 pdf[num] ~= assigned then
error[[Invalid object]]
end
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(content)
pdf.file:write'\nendstream\nendobj\n'
return num
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 pdf[num] ~= assigned then
error[[Invalid object]]
end
pdf[num] = {offset = pdf.file:seek()}
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'\nendobj\n'
return num
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 id = pdf[0] + 1
pdf[0] = id
@ -73,9 +114,9 @@ local pdfmeta = {
stream = stream,
newpage = pagetree.newpage,
writepages = pagetree.write,
-- delayed = delayed,
-- delayedstream = delayedstream,
-- reference
delayed = delayed,
delayedstream = delayedstream,
reference = reference,
}
pdfmeta.__index = pdfmeta
local function open(filename)