Work on links
This commit is contained in:
parent
9265fc3463
commit
581fbca92b
@ -100,6 +100,7 @@ local whatsit_id = node.id'whatsit'
|
|||||||
local whatsits = node.whatsits()
|
local whatsits = node.whatsits()
|
||||||
|
|
||||||
local lastobj = -1
|
local lastobj = -1
|
||||||
|
local lastannot = -1
|
||||||
|
|
||||||
function pdf.newcolorstack(default, mode, page)
|
function pdf.newcolorstack(default, mode, page)
|
||||||
local idx = #colorstacks
|
local idx = #colorstacks
|
||||||
@ -118,6 +119,80 @@ local function projected(m, x, y, w)
|
|||||||
w = w or 1
|
w = w or 1
|
||||||
return x*m[1] + y*m[3] + w*m[5], x*m[2] + y*m[4] + w*m[6]
|
return x*m[1] + y*m[3] + w*m[5], x*m[2] + y*m[4] + w*m[6]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- local function get_action_attr(p, action)
|
||||||
|
-- if action.action_type == 3 then
|
||||||
|
-- return action.data
|
||||||
|
-- elseif action.action_type == 2 then
|
||||||
|
local function write_link(p, link)
|
||||||
|
local quads = link.quads
|
||||||
|
local minX, maxX, minY, maxY = math.huge, -math.huge, math.huge, -math.huge
|
||||||
|
assert(link.action.action_type == 3) -- TODO: Other types
|
||||||
|
local attr = link.attr .. link.action.data
|
||||||
|
assert(#quads%8==0)
|
||||||
|
local quadStr = {}
|
||||||
|
for i=1,#quads,8 do
|
||||||
|
local x1, y1, x4, y4, x2, y2, x3, y3 = table.unpack(quads, i, i+7)
|
||||||
|
x1, y1, x2, y2, x3, y3, x4, y4 = sp2bp(x1), sp2bp(y1), sp2bp(x2), sp2bp(y2), sp2bp(x3), sp2bp(y3), sp2bp(x4), sp2bp(y4)
|
||||||
|
quadStr[i//8+1] = string.format("%f %f %f %f %f %f %f %f", x1, y1, x2, y2, x3, y3, x4, y4)
|
||||||
|
minX = math.min(minX, x1, x2, x3, x4)
|
||||||
|
minY = math.min(minY, y1, y2, y3, y4)
|
||||||
|
maxX = math.max(maxX, x1, x2, x3, x4)
|
||||||
|
maxY = math.max(maxY, y1, y2, y3, y4)
|
||||||
|
end
|
||||||
|
pfile:indirect(link.objnum, string.format("<</Rect[%f %f %f %f]/QuadPoints[%s]%s>>", minX-.2, minY-.2, maxX+.2, maxY+.2, table.concat(quadStr, ' '), attr))
|
||||||
|
for i=1,#quads do quads[i] = nil end
|
||||||
|
link.objnum = nil
|
||||||
|
end
|
||||||
|
local function linkcontext_set(linkcontext, p, x, y, list, level, kind)
|
||||||
|
for _,l in ipairs(linkcontext) do if l.level == level then
|
||||||
|
addlinkpoint(p, l, x, y, list, level, kind)
|
||||||
|
end end
|
||||||
|
end
|
||||||
|
local function addlinkpoint(p, link, x, y, list, kind)
|
||||||
|
local quads = link.quads
|
||||||
|
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
|
||||||
|
end
|
||||||
|
if not link.annots then
|
||||||
|
link.annots = p.annots -- Just a marker to indicate the page
|
||||||
|
link.objnum = link.objnum or p.file:getobj()
|
||||||
|
p.annots[#p.annots+1] = link.objnum .. " 0 R"
|
||||||
|
end
|
||||||
|
local m = p.matrix
|
||||||
|
local lx, ly = projected(m, x, y-off-(link.depth or node.direct.getdepth(list)))
|
||||||
|
local ux, uy = projected(m, x, y+off+(link.height or node.direct.getheight(list)))
|
||||||
|
local n = #quads
|
||||||
|
quads[n+1], quads[n+2], quads[n+3], quads[n+4] = lx, ly, ux, uy
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
function do_start_link(prop, p, n, x, y, outer, _, level)
|
||||||
|
local links = p.linkcontext
|
||||||
|
if not links then
|
||||||
|
links = {set = linkcontext_set}
|
||||||
|
p.linkcontext = links
|
||||||
|
end
|
||||||
|
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)
|
||||||
|
local links = p.linkcontext
|
||||||
|
if not links then error"No link here to end" end
|
||||||
|
local link = links[#links]
|
||||||
|
links[#links] = nil
|
||||||
|
if link.level ~= level then error"Wrong link level" end
|
||||||
|
addlinkpoint(p, link, x, y, outer, 'final')
|
||||||
|
end
|
||||||
|
|
||||||
local do_setmatrix do
|
local do_setmatrix do
|
||||||
local numberpattern = (lpeg.P'-'^-1 * lpeg.R'09'^0 * ('.' * lpeg.R'09'^0)^-1)/tonumber
|
local numberpattern = (lpeg.P'-'^-1 * lpeg.R'09'^0 * ('.' * lpeg.R'09'^0)^-1)/tonumber
|
||||||
local matrixpattern = numberpattern * ' ' * numberpattern * ' ' * numberpattern * ' ' * numberpattern
|
local matrixpattern = numberpattern * ' ' * numberpattern * ' ' * numberpattern * ' ' * numberpattern
|
||||||
@ -236,6 +311,42 @@ local function write_colorstack()
|
|||||||
})
|
})
|
||||||
node.write(whatsit)
|
node.write(whatsit)
|
||||||
end
|
end
|
||||||
|
local function scan_action()
|
||||||
|
local action_type
|
||||||
|
|
||||||
|
if token.scan_keyword'user' then
|
||||||
|
return {action_type = 3, data = token.scan_string()}
|
||||||
|
elseif token.scan_keyword'thread' then
|
||||||
|
error[[FIXME: Unsupported]] -- TODO
|
||||||
|
elseif token.scan_keyword'goto' then
|
||||||
|
action_type = 1
|
||||||
|
else
|
||||||
|
error[[Unsupported action]]
|
||||||
|
end
|
||||||
|
local action = {
|
||||||
|
action_type = action_type,
|
||||||
|
file = token.scan_keyword'file' and token.scan_string(),
|
||||||
|
}
|
||||||
|
if token.scan_keyword'page' then
|
||||||
|
error[[TODO]]
|
||||||
|
elseif token.scan_keyword'num' then
|
||||||
|
if action.file and action_type == 3 then
|
||||||
|
error[[num style GoTo actions must be internal]]
|
||||||
|
end
|
||||||
|
action.id = token.scan_int()
|
||||||
|
if not id > 0 then
|
||||||
|
error[[id must be positive]]
|
||||||
|
end
|
||||||
|
elseif token.scan_keyword'name' then
|
||||||
|
action.id = token.scan_string()
|
||||||
|
else
|
||||||
|
error[[Unsupported id type]]
|
||||||
|
end
|
||||||
|
action.new_window = token.scan_keyword'newwindow' and 1
|
||||||
|
or token.scan_keyword'nonewwindow' and 2
|
||||||
|
or 0
|
||||||
|
return action
|
||||||
|
end
|
||||||
local function scan_literal_mode()
|
local function scan_literal_mode()
|
||||||
return token.scan_keyword"direct" and "direct"
|
return token.scan_keyword"direct" and "direct"
|
||||||
or token.scan_keyword"page" and "page"
|
or token.scan_keyword"page" and "page"
|
||||||
@ -259,6 +370,8 @@ 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"lastannot" then
|
||||||
|
tex.sprint(tostring(lastannot))
|
||||||
elseif token.scan_keyword"lastobj" then
|
elseif token.scan_keyword"lastobj" then
|
||||||
tex.sprint(tostring(lastobj))
|
tex.sprint(tostring(lastobj))
|
||||||
else
|
else
|
||||||
@ -280,6 +393,26 @@ token.luacmd("pdfextension", function(_, imm)
|
|||||||
data = literal,
|
data = literal,
|
||||||
})
|
})
|
||||||
node.write(whatsit)
|
node.write(whatsit)
|
||||||
|
elseif token.scan_keyword"startlink" then
|
||||||
|
local pfile = get_pfile()
|
||||||
|
local whatsit = node.new(whatsit_id, whatsits.pdf_start_link)
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
node.write(whatsit)
|
||||||
elseif token.scan_keyword"save" then
|
elseif token.scan_keyword"save" then
|
||||||
local whatsit = node.new(whatsit_id, whatsits.pdf_save)
|
local whatsit = node.new(whatsit_id, whatsits.pdf_save)
|
||||||
node.setproperty(whatsit, {
|
node.setproperty(whatsit, {
|
||||||
|
@ -136,55 +136,6 @@ local function toglyph(p, fid, x, y, exfactor)
|
|||||||
p.mode = glyph
|
p.mode = glyph
|
||||||
p.pending[1] = "[("
|
p.pending[1] = "[("
|
||||||
end
|
end
|
||||||
local linkcontext = {}
|
|
||||||
-- local function get_action_attr(p, action)
|
|
||||||
-- if action.action_type == 3 then
|
|
||||||
-- return action.data
|
|
||||||
-- elseif action.action_type == 2 then
|
|
||||||
local function write_link(p, link)
|
|
||||||
local quads = link.quads
|
|
||||||
local minX, maxX, minY, maxY = math.huge, -math.huge, math.huge, -math.huge
|
|
||||||
assert(link.action.action_type == 3) -- TODO: Other types
|
|
||||||
local attr = link.attr .. link.action.data
|
|
||||||
assert(#quads%8==0)
|
|
||||||
local quadStr = {}
|
|
||||||
for i=1,#quads,8 do
|
|
||||||
local x1, y1, x4, y4, x2, y2, x3, y3 = table.unpack(quads, i, i+7)
|
|
||||||
x1, y1, x2, y2, x3, y3, x4, y4 = sp2bp(x1), sp2bp(y1), sp2bp(x2), sp2bp(y2), sp2bp(x3), sp2bp(y3), sp2bp(x4), sp2bp(y4)
|
|
||||||
quadStr[i//8+1] = string.format("%f %f %f %f %f %f %f %f", x1, y1, x2, y2, x3, y3, x4, y4)
|
|
||||||
minX = math.min(minX, x1, x2, x3, x4)
|
|
||||||
minY = math.min(minY, y1, y2, y3, y4)
|
|
||||||
maxX = math.max(maxX, x1, x2, x3, x4)
|
|
||||||
maxY = math.max(maxY, y1, y2, y3, y4)
|
|
||||||
end
|
|
||||||
p.file:indirect(link.objnum, string.format("<</Rect[%f %f %f %f]/QuadPoints[%s]%s>>", minX-.2, minY-.2, maxX+.2, maxY+.2, table.concat(quadStr, ' '), attr))
|
|
||||||
for i=1,#quads do quads[i] = nil end
|
|
||||||
link.objnum = nil
|
|
||||||
end
|
|
||||||
local function addlinkpoint(p, link, x, y, list, kind)
|
|
||||||
local quads = link.quads
|
|
||||||
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
|
|
||||||
end
|
|
||||||
if not link.annots then
|
|
||||||
link.annots = p.annots -- Just a marker to indicate the page
|
|
||||||
link.objnum = link.objnum or p.file:getobj()
|
|
||||||
p.annots[#p.annots+1] = link.objnum .. " 0 R"
|
|
||||||
end
|
|
||||||
local m = p.matrix
|
|
||||||
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 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
|
|
||||||
end
|
|
||||||
function nodehandler.hlist(p, list, x0, y, outerlist, origin, level)
|
function nodehandler.hlist(p, list, x0, y, outerlist, origin, level)
|
||||||
if outerlist then
|
if outerlist then
|
||||||
if getid(outerlist) == 0 then
|
if getid(outerlist) == 0 then
|
||||||
@ -212,9 +163,10 @@ function nodehandler.hlist(p, list, x0, y, outerlist, origin, level)
|
|||||||
dirnodes[dirstack[i]] = rangedimensions(list, dirstack[i])
|
dirnodes[dirstack[i]] = rangedimensions(list, dirstack[i])
|
||||||
end
|
end
|
||||||
local x = x0
|
local x = x0
|
||||||
for _,l in ipairs(p.linkcontext) do if l.level == level+1 then
|
local linkcontext = p.linkcontext
|
||||||
addlinkpoint(p, l, x, y, list, 'start')
|
if linkcontext then
|
||||||
end end
|
linkcontext:set(p, x, y, list, level+1, 'start')
|
||||||
|
end
|
||||||
for n, id, sub in traverse(getlist(list)) do
|
for n, id, sub in traverse(getlist(list)) do
|
||||||
if id == dir_id then
|
if id == dir_id then
|
||||||
if sub == 0 then
|
if sub == 0 then
|
||||||
@ -241,9 +193,10 @@ function nodehandler.hlist(p, list, x0, y, outerlist, origin, level)
|
|||||||
if direction == 0 then x = w + x end
|
if direction == 0 then x = w + x end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
for _,l in ipairs(p.linkcontext) do if l.level == level+1 then
|
linkcontext = p.linkcontext
|
||||||
addlinkpoint(p, l, x, y, list, 'end')
|
if linkcontext then
|
||||||
end end
|
linkcontext:set(p, x, y, list, level+1, 'end')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
function nodehandler.vlist(p, list, x, y0, outerlist, origin, level)
|
function nodehandler.vlist(p, list, x, y0, outerlist, origin, level)
|
||||||
if outerlist then
|
if outerlist then
|
||||||
@ -557,11 +510,6 @@ local fontnames = setmetatable({}, {__index = function(t, k) local res = format(
|
|||||||
return function(file, n, fontdirs, usedglyphs, colorstacks)
|
return function(file, n, fontdirs, usedglyphs, colorstacks)
|
||||||
n = todirect(n)
|
n = todirect(n)
|
||||||
setmetatable(usedglyphs, ondemandmeta)
|
setmetatable(usedglyphs, ondemandmeta)
|
||||||
local linkcontext = file.linkcontext
|
|
||||||
if not linkcontext then
|
|
||||||
linkcontext = {}
|
|
||||||
file.linkcontext = linkcontext
|
|
||||||
end
|
|
||||||
local p = {
|
local p = {
|
||||||
is_page = not not colorstacks,
|
is_page = not not colorstacks,
|
||||||
file = file,
|
file = file,
|
||||||
|
Loading…
Reference in New Issue
Block a user