Some backend stuff
This commit is contained in:
parent
c0954fe7f6
commit
4c0794032c
@ -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.
|
||||
|
@ -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}
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user