Better errors and new whatsit handling
The whatsit handling is actually much more similar to the old whatsit handling then the previous new one, so that part could be seen as a partial revert of the new whatsit handling... Anyway, whatsits work different than in the previous commit now.
This commit is contained in:
parent
d13bbbc5eb
commit
94ba4a2557
@ -8,6 +8,7 @@ local fontdirs = setmetatable({}, {__index=function(t, k)t[k] = pfile:getobj() r
|
||||
local usedglyphs = {}
|
||||
local dests = {}
|
||||
local cur_page
|
||||
local declare_whatsit = require'luametalatex-whatsits'.new
|
||||
local whatsit_id = node.id'whatsit'
|
||||
local whatsits = node.whatsits()
|
||||
local colorstacks = {{
|
||||
@ -264,9 +265,16 @@ local function linkcontext_set(linkcontext, p, x, y, list, level, kind)
|
||||
end end
|
||||
end
|
||||
|
||||
function do_start_link(prop, p, n, x, y, outer, _, level)
|
||||
local start_link_whatsit = declare_whatsit('pdf_start_link', function(prop, p, n, x, y, outer, _, level)
|
||||
if not prop then
|
||||
tex.error('Invalid pdf_start_link whatsit', {"A pdf_start_link whatsit did not contain all necessary \z
|
||||
parameters. Maybe your code hasn't been adapted to LuaMetaLaTeX yet?"})
|
||||
return
|
||||
end
|
||||
if not p.is_page then
|
||||
error[[No link allowed here]]
|
||||
tex.error('pdf_start_link outside of page', {"PDF links are not allowed in Type3 charstrings or Form XObjects. \z
|
||||
The link will be ignored"})
|
||||
return
|
||||
end
|
||||
local links = p.linkcontext
|
||||
if not links then
|
||||
@ -276,29 +284,45 @@ function do_start_link(prop, p, n, x, y, outer, _, level)
|
||||
local link = {quads = {}, attr = prop.link_attr, action = prop.action, level = level, force_separate = false} -- force_separate should become an option
|
||||
links[#links+1] = link
|
||||
addlinkpoint(p, link, x, y, outer, 'start')
|
||||
end
|
||||
function do_end_link(prop, p, n, x, y, outer, _, level)
|
||||
end)
|
||||
local end_link_whatsit = declare_whatsit('pdf_end_link', function(prop, p, n, x, y, outer, _, level)
|
||||
if not p.is_page then
|
||||
error[[No link allowed here]]
|
||||
tex.error('pdf_start_link outside of page', {"PDF links are not allowed in Type3 charstrings or Form XObjects. \z
|
||||
The link will be ignored"})
|
||||
return
|
||||
end
|
||||
local links = p.linkcontext
|
||||
if not links then error"No link here to end" end
|
||||
if not links then
|
||||
tex.error('No link here to end', {"You asked me to end a link, but currently there is no link active. \z
|
||||
Maybe you forgot to run \\pdfextension startlink first?"})
|
||||
return
|
||||
end
|
||||
local link = links[#links]
|
||||
if link.level ~= level then
|
||||
tex.error('Inconsistent link level', {"You asked me to end a link, but the most recent link had been started at another level. \z
|
||||
I will continue with the link for now."})
|
||||
return
|
||||
end
|
||||
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
|
||||
end)
|
||||
|
||||
local do_setmatrix do
|
||||
local setmatrix_whatsit do
|
||||
local numberpattern = (lpeg.P'-'^-1 * lpeg.R'09'^0 * ('.' * lpeg.R'09'^0)^-1)/tonumber
|
||||
local matrixpattern = numberpattern * ' ' * numberpattern * ' ' * numberpattern * ' ' * numberpattern
|
||||
function do_setmatrix(prop, p, n, x, y, outer)
|
||||
setmatrix_whatsit = declare_whatsit('pdf_setmatrix', function(prop, p, n, x, y, outer)
|
||||
if not prop then
|
||||
tex.error('Invalid pdf_setmatrix whatsit', {"A pdf_setmatrix whatsit did not contain a matrix value. \z
|
||||
Maybe your code hasn't been adapted to LuaMetaLaTeX yet?"})
|
||||
return
|
||||
end
|
||||
local m = p.matrix
|
||||
local a, b, c, d = matrixpattern:match(prop.data)
|
||||
if not a then
|
||||
print(prop.data)
|
||||
error[[No valid matrix found]]
|
||||
tex.error('Invalid matrix', {"The matrix in this pdf_setmatrix whatsit does not have the expected structure and could not be parsed. \z
|
||||
Did you provide enough parameters? The matrix needs exactly four decimal entries."})
|
||||
return
|
||||
end
|
||||
local e, f = (1-a)*x-c*y, (1-d)*y-b*x -- Emulate that the origin is at x, y for this transformation
|
||||
-- (We could also first translate by (-x, -y), then apply the matrix
|
||||
@ -308,19 +332,23 @@ local do_setmatrix do
|
||||
c, d = projected(m, c, d, 0)
|
||||
e, f = projected(m, e, f, 1)
|
||||
m[1], m[2], m[3], m[4], m[5], m[6] = a, b, c, d, e, f
|
||||
end)
|
||||
end
|
||||
end
|
||||
local function do_save(prop, p, n, x, y, outer)
|
||||
local save_whatsit = declare_whatsit('pdf_save', function(prop, p, n, x, y, outer)
|
||||
pdf.write('page', 'q', x, y, p)
|
||||
local lastmatrix = p.matrix
|
||||
p.matrix = {[0] = lastmatrix, table.unpack(lastmatrix)}
|
||||
end
|
||||
local function do_restore(prop, p, n, x, y, outer)
|
||||
end)
|
||||
local restore_whatsit = declare_whatsit('pdf_restore', function(prop, p, n, x, y, outer)
|
||||
-- TODO: Check x, y
|
||||
pdf.write('page', 'Q', x, y, p)
|
||||
p.matrix = p.matrix[0]
|
||||
end)
|
||||
local dest_whatsit = declare_whatsit('pdf_dest', function(prop, p, n, x, y)
|
||||
if not prop then
|
||||
tex.error('Invalid pdf_dest whatsit', {"A pdf_dest whatsit did not contain all necessary \z
|
||||
parameters. Maybe your code hasn't been adapted to LuaMetaLaTeX yet?"})
|
||||
end
|
||||
local function do_dest(prop, p, n, x, y)
|
||||
assert(cur_page, "Destinations can not appear outside of a page")
|
||||
local id = prop.dest_id
|
||||
local dest_type = prop.dest_type
|
||||
@ -365,14 +393,29 @@ local function do_dest(prop, p, n, x, y)
|
||||
else
|
||||
dests[id] = pfile:indirect(dests[id], data)
|
||||
end
|
||||
end)
|
||||
local refobj_whatsit = declare_whatsit('pdf_refobj', function(prop, p, n, x, y)
|
||||
if not prop then
|
||||
tex.error('Invalid pdf_refobj whatsit', {"A pdf_refobj whatsit did not reference any object. \z
|
||||
Maybe your code hasn't been adapted to LuaMetaLaTeX yet?"})
|
||||
return
|
||||
end
|
||||
local function do_refobj(prop, p, n, x, y)
|
||||
pfile:reference(prop.obj)
|
||||
end)
|
||||
local literal_whatsit = declare_whatsit('pdf_literal', function(prop, p, n, x, y)
|
||||
if not prop then
|
||||
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?"})
|
||||
return
|
||||
end
|
||||
local function do_literal(prop, p, n, x, y)
|
||||
pdf.write(prop.mode, prop.data, x, y, p)
|
||||
end)
|
||||
local colorstack_whatsit = declare_whatsit('pdf_colorstack', function(prop, p, n, x, y)
|
||||
if not prop then
|
||||
tex.error('Invalid pdf_colorstack whatsit', {"A pdf_colorstack whatsit did not contain all necessary \z
|
||||
parameters. Maybe your code hasn't been adapted to LuaMetaLaTeX yet?"})
|
||||
return
|
||||
end
|
||||
local function do_colorstack(prop, p, n, x, y)
|
||||
local colorstack = prop.colorstack
|
||||
local stack
|
||||
if p.is_page then
|
||||
@ -393,28 +436,33 @@ local function do_colorstack(prop, p, n, x, y)
|
||||
stack[#stack] = prop.data
|
||||
end
|
||||
pdf.write(colorstack.mode, stack[#stack], x, y, p)
|
||||
end
|
||||
end)
|
||||
local function write_colorstack()
|
||||
local idx = token.scan_int()
|
||||
local colorstack = colorstacks[idx + 1]
|
||||
if not colorstack then
|
||||
error[[Undefined colorstack]]
|
||||
tex.error('Undefined colorstack', {"The requested colorstack is not initialized. \z
|
||||
This probably means that you forgot to run \\pdffeedback colorstackinit or \z
|
||||
that you specified the wrong index. I will continue with colorstack 0."})
|
||||
colorstack = colorstacks[1]
|
||||
end
|
||||
local action = token.scan_keyword'pop' and 'pop'
|
||||
or token.scan_keyword'set' and 'set'
|
||||
or token.scan_keyword'current' and 'current'
|
||||
or token.scan_keyword'push' and 'push'
|
||||
if not action then
|
||||
error[[Missing action specifier for colorstack command]]
|
||||
tex.error('Missing action specifier for colorstack', {
|
||||
"I don't know what you want to do with this colorstack. I would have expected pop/set/current or push here. \z
|
||||
I will ignore this command."})
|
||||
return
|
||||
end
|
||||
local text
|
||||
if action == "push" or "set" then
|
||||
text = token.scan_string()
|
||||
-- text = token.to_string(token.scan_tokenlist()) -- Attention! This should never be executed in an expand-only context
|
||||
end
|
||||
local whatsit = node.new(whatsit_id, whatsits.pdf_colorstack)
|
||||
local whatsit = node.new(whatsit_id, colorstack_whatsit)
|
||||
node.setproperty(whatsit, {
|
||||
handle = do_colorstack,
|
||||
colorstack = colorstack,
|
||||
action = action,
|
||||
data = text,
|
||||
@ -498,52 +546,40 @@ token.luacmd("pdfextension", function(_, imm)
|
||||
elseif token.scan_keyword"literal" then
|
||||
local mode = scan_literal_mode()
|
||||
local literal = token.scan_string()
|
||||
local whatsit = node.new(whatsit_id, whatsits.pdf_literal)
|
||||
local whatsit = node.new(whatsit_id, literal_whatsit)
|
||||
node.setproperty(whatsit, {
|
||||
handle = do_literal,
|
||||
mode = mode,
|
||||
data = literal,
|
||||
})
|
||||
node.write(whatsit)
|
||||
elseif token.scan_keyword"startlink" then
|
||||
local pfile = get_pfile()
|
||||
local whatsit = node.new(whatsit_id, whatsits.pdf_start_link)
|
||||
local whatsit = node.new(whatsit_id, start_link_whatsit)
|
||||
local attr = token.scan_keyword'attr' and token.scan_string() or ''
|
||||
local action = scan_action()
|
||||
local objnum = pfile:getobj()
|
||||
lastannot = num
|
||||
node.setproperty(whatsit, {
|
||||
handle = do_start_link,
|
||||
link_attr = attr,
|
||||
action = action,
|
||||
objnum = objnum,
|
||||
})
|
||||
node.write(whatsit)
|
||||
elseif token.scan_keyword"endlink" then
|
||||
local whatsit = node.new(whatsit_id, whatsits.pdf_end_link)
|
||||
node.setproperty(whatsit, {
|
||||
handle = do_end_link,
|
||||
})
|
||||
local whatsit = node.new(whatsit_id, end_link_whatsit)
|
||||
node.write(whatsit)
|
||||
elseif token.scan_keyword"save" then
|
||||
local whatsit = node.new(whatsit_id, whatsits.pdf_save)
|
||||
node.setproperty(whatsit, {
|
||||
handle = do_save,
|
||||
})
|
||||
local whatsit = node.new(whatsit_id, save_whatsit)
|
||||
node.write(whatsit)
|
||||
elseif token.scan_keyword"setmatrix" then
|
||||
local matrix = token.scan_string()
|
||||
local whatsit = node.new(whatsit_id, whatsits.pdf_setmatrix)
|
||||
local whatsit = node.new(whatsit_id, setmatrix_whatsit)
|
||||
node.setproperty(whatsit, {
|
||||
handle = do_setmatrix,
|
||||
data = matrix,
|
||||
})
|
||||
node.write(whatsit)
|
||||
elseif token.scan_keyword"restore" then
|
||||
local whatsit = node.new(whatsit_id, whatsits.pdf_restore)
|
||||
node.setproperty(whatsit, {
|
||||
handle = do_restore,
|
||||
})
|
||||
local whatsit = node.new(whatsit_id, restore_whatsit)
|
||||
node.write(whatsit)
|
||||
elseif token.scan_keyword"info" then
|
||||
infodir = infodir .. token.scan_string()
|
||||
@ -577,10 +613,9 @@ token.luacmd("pdfextension", function(_, imm)
|
||||
end
|
||||
elseif token.scan_keyword"refobj" then
|
||||
local num = token.scan_int()
|
||||
local whatsit = node.new(whatsit_id, whatsits.pdf_refobj)
|
||||
local whatsit = node.new(whatsit_id, refobj_whatsit)
|
||||
node.setproperty(whatsit, {
|
||||
obj = num,
|
||||
handle = do_refobj,
|
||||
})
|
||||
node.write(whatsit)
|
||||
elseif token.scan_keyword"outline" then
|
||||
@ -616,10 +651,9 @@ token.luacmd("pdfextension", function(_, imm)
|
||||
else
|
||||
error[[Unsupported id type]]
|
||||
end
|
||||
local whatsit = node.new(whatsit_id, whatsits.pdf_dest)
|
||||
local whatsit = node.new(whatsit_id, dest_whatsit)
|
||||
local prop = {
|
||||
dest_id = id,
|
||||
handle = do_dest,
|
||||
}
|
||||
node.setproperty(whatsit, prop)
|
||||
if token.scan_keyword'xyz' then
|
||||
|
@ -63,50 +63,8 @@ end
|
||||
-- end
|
||||
-- })
|
||||
|
||||
local new_whatsit = require'luametalatex-whatsits'.new
|
||||
local whatsit_id = node.id'whatsit'
|
||||
local whatsits = {
|
||||
[0] = "open",
|
||||
"write",
|
||||
"close",
|
||||
"special",
|
||||
nil,
|
||||
nil,
|
||||
"save_pos",
|
||||
"late_lua",
|
||||
"user_defined",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
"pdf_literal",
|
||||
"pdf_refobj",
|
||||
"pdf_annot",
|
||||
"pdf_start_link",
|
||||
"pdf_end_link",
|
||||
"pdf_dest",
|
||||
"pdf_action",
|
||||
"pdf_thread",
|
||||
"pdf_start_thread",
|
||||
"pdf_end_thread",
|
||||
"pdf_thread_data",
|
||||
"pdf_link_data",
|
||||
"pdf_colorstack",
|
||||
"pdf_setmatrix",
|
||||
"pdf_save",
|
||||
"pdf_restore",
|
||||
}
|
||||
whatsits[whatsits[0]] = 0
|
||||
for i = 0,#whatsits do
|
||||
local v = whatsits[i]
|
||||
if v then
|
||||
whatsits[v] = i
|
||||
end
|
||||
end
|
||||
function node.whatsits() return whatsits end
|
||||
function node.subtype(s) return type(s) == "string" and whatsits[s] or nil end
|
||||
local spacer_cmd, relax_cmd = token.command_id'spacer', token.command_id'relax'
|
||||
local function scan_filename()
|
||||
local name = {}
|
||||
@ -141,15 +99,16 @@ local function do_openout(p)
|
||||
end
|
||||
end
|
||||
end
|
||||
local open_whatsit = new_whatsit('open', do_openout)
|
||||
token.luacmd("openout", function(_, immediate) -- \openout
|
||||
local file = token.scan_int()
|
||||
token.scan_keyword'='
|
||||
local name = scan_filename()
|
||||
local props = {file = file, name = name, handle = do_openout}
|
||||
local props = {file = file, name = name}
|
||||
if immediate == "immediate" then
|
||||
do_openout(props)
|
||||
else
|
||||
local whatsit = node.direct.new(whatsit_id, whatsits.open)
|
||||
local whatsit = node.direct.new(whatsit_id, open_whatsit)
|
||||
properties[whatsit] = props
|
||||
node.direct.write(whatsit)
|
||||
end
|
||||
@ -160,13 +119,14 @@ local function do_closeout(p)
|
||||
ofiles[p.file] = nil
|
||||
end
|
||||
end
|
||||
local close_whatsit = new_whatsit('close', do_closeout)
|
||||
token.luacmd("closeout", function(_, immediate) -- \closeout
|
||||
local file = token.scan_int()
|
||||
local props = {file = file, handle = do_closeout}
|
||||
local props = {file = file}
|
||||
if immediate == "immediate" then
|
||||
do_closeout(props)
|
||||
else
|
||||
local whatsit = node.direct.new(whatsit_id, whatsits.close)
|
||||
local whatsit = node.direct.new(whatsit_id, close_whatsit)
|
||||
properties[whatsit] = props
|
||||
node.direct.write(whatsit)
|
||||
end
|
||||
@ -180,14 +140,15 @@ local function do_write(p)
|
||||
texio.write_nl(p.file < 0 and "log" or "term and log", content)
|
||||
end
|
||||
end
|
||||
local write_whatsit = new_whatsit('write', do_write)
|
||||
token.luacmd("write", function(_, immediate) -- \write
|
||||
local file = token.scan_int()
|
||||
local content = token.scan_tokenlist()
|
||||
local props = {file = file, data = content, handle = do_write}
|
||||
local props = {file = file, data = content}
|
||||
if immediate == "immediate" then
|
||||
do_write(props)
|
||||
else
|
||||
local whatsit = node.direct.new(whatsit_id, whatsits.write)
|
||||
local whatsit = node.direct.new(whatsit_id, write_whatsit)
|
||||
properties[whatsit] = props
|
||||
node.direct.write(whatsit)
|
||||
end
|
||||
@ -199,7 +160,7 @@ token.luacmd("immediate", function() -- \immediate
|
||||
return token.put_next(next_tok)
|
||||
end
|
||||
local function_id = next_tok.mode
|
||||
functions[function_id](function_id, 'immediate')
|
||||
return functions[function_id](function_id, 'immediate')
|
||||
end, "protected")
|
||||
-- functions[43] = function() -- \pdfvariable
|
||||
-- local name = token.scan_string()
|
||||
|
@ -98,7 +98,8 @@ callback_register('handle_error_hook', function()
|
||||
if line == "" then return 3 end
|
||||
local first = line:sub(1,1):upper()
|
||||
if first == 'H' then
|
||||
texio.write(tex.gethelptext())
|
||||
texio.write(tex.gethelptext() or "Sorry, I don't know how to help in this situation.\n\z
|
||||
Maybe you should try asking a human?")
|
||||
elseif first == 'I' then
|
||||
line = line:sub(2)
|
||||
tex.runtoks(function()
|
||||
|
@ -31,6 +31,8 @@ local rangedimensions = direct.rangedimensions
|
||||
local traverse_id = direct.traverse_id
|
||||
local getdata = direct.getdata
|
||||
|
||||
local get_whatsit_handler = require'luametalatex-whatsits'.handler
|
||||
|
||||
local dir_id = node.id'dir'
|
||||
|
||||
local function doublekeyed(t, id2name, name2id, index)
|
||||
@ -470,9 +472,9 @@ function nodehandler.glyph(p, n, x, y, ...)
|
||||
p.pos.x = p.pos.x + math.floor(getwidth(n)*(1+getexpansion(n)/1000000)+.5)
|
||||
end
|
||||
function nodehandler.whatsit(p, n, ...) -- Whatsit?
|
||||
local prop = properties[n]-- or node.getproperty(n)
|
||||
if prop and prop.handle then
|
||||
prop:handle(p, n, ...)
|
||||
local handler, prop = get_whatsit_handler(n)
|
||||
if handler then
|
||||
handler(prop, p, n, ...)
|
||||
else
|
||||
write("Invalid whatsit found (missing handler).")
|
||||
end
|
||||
|
96
luametalatex-whatsits.lua
Normal file
96
luametalatex-whatsits.lua
Normal file
@ -0,0 +1,96 @@
|
||||
local whatsit_id = node.id'whatsit'
|
||||
local whatsits = {
|
||||
[0] = "open",
|
||||
"write",
|
||||
"close",
|
||||
"special",
|
||||
nil,
|
||||
nil,
|
||||
"save_pos",
|
||||
"late_lua",
|
||||
"user_defined",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
"pdf_literal",
|
||||
"pdf_refobj",
|
||||
"pdf_annot",
|
||||
"pdf_start_link",
|
||||
"pdf_end_link",
|
||||
"pdf_dest",
|
||||
"pdf_action",
|
||||
"pdf_thread",
|
||||
"pdf_start_thread",
|
||||
"pdf_end_thread",
|
||||
"pdf_thread_data",
|
||||
"pdf_link_data",
|
||||
"pdf_colorstack",
|
||||
"pdf_setmatrix",
|
||||
"pdf_save",
|
||||
"pdf_restore",
|
||||
}
|
||||
local whatsithandler = {}
|
||||
-- for i = 0,#whatsits do -- #whatsits isn't guaranteed to work because of the nil entries
|
||||
for i = 0,31 do
|
||||
local v = whatsits[i]
|
||||
if v then
|
||||
whatsits[v] = i
|
||||
end
|
||||
end
|
||||
function node.whatsits() return whatsits end
|
||||
function node.subtype(s) return type(s) == "string" and whatsits[s] or nil end
|
||||
|
||||
local direct = node.direct
|
||||
local getsubtype = direct.getsubtype
|
||||
local properties = direct.get_properties_table()
|
||||
local tonode, todirect = direct.tonode, direct.todirect
|
||||
|
||||
local function get_handler(n, subtype)
|
||||
local props = properties[n]
|
||||
return props and props.handle or whatsithandler[subtype or getsubtype(n)], props
|
||||
end
|
||||
local function new(name, handler)
|
||||
assert(type(name) == 'string')
|
||||
local subtype = whatsits[name]
|
||||
if subtype then
|
||||
if whatsithandler[subtype] then
|
||||
texio.write_nl'WARNING: Overwriting default whatsit handler'
|
||||
end
|
||||
else
|
||||
subtype = #whatsits + 1
|
||||
whatsits[subtype] = name
|
||||
whatsits[name] = subtype
|
||||
end
|
||||
whatsithandler[subtype] = handler
|
||||
return subtype
|
||||
end
|
||||
|
||||
-- TODO: Some fields might expect different values
|
||||
local function setwhatsitfield(n, name, value)
|
||||
local props = properties[n]
|
||||
if not props then
|
||||
props = {}
|
||||
properties[n] = props
|
||||
end
|
||||
props[name] = value
|
||||
end
|
||||
direct.setwhatsitfield = setwhatsitfield
|
||||
|
||||
local function getwhatsitfield(n, name)
|
||||
local props = properties[n]
|
||||
return props and props[name]
|
||||
end
|
||||
direct.getwhatsitfield = getwhatsitfield
|
||||
|
||||
-- TODO: Some fields might be nodes and therefore have to be converted
|
||||
function node.setwhatsitfield(n, ...) return setwhatsitfield(todirect(n), ...) end
|
||||
function node.getwhatsitfield(n, ...) return getwhatsitfield(todirect(n), ...) end
|
||||
|
||||
return {
|
||||
handler = get_handler,
|
||||
new = new,
|
||||
}
|
Loading…
Reference in New Issue
Block a user