Skip to content

Commit

Permalink
Merge 23ef07e into 9ef72e4
Browse files Browse the repository at this point in the history
  • Loading branch information
alerque committed Jan 6, 2024
2 parents 9ef72e4 + 23ef07e commit 9ec54b1
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 44 deletions.
30 changes: 25 additions & 5 deletions outputters/base.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,29 @@ local outputter = pl.class()
outputter.type = "outputter"
outputter._name = "base"

function outputter._init () end
function outputter:_init ()
self.hooks = {}
return self
end

function outputter:registerHook (category, func)
if not self.hooks[category] then self.hooks[category] = {} end
table.insert(self.hooks[category], func)
end

function outputter:runHooks (category, data)
if not self.hooks[category] then return nil end
for _, func in ipairs(self.hooks[category]) do
data = func(self, data)
end
return data
end

function outputter.newPage () end

function outputter.finish () end
function outputter:finish ()
self:runHooks("prefinish")
end

function outputter.getCursor () end

Expand Down Expand Up @@ -34,16 +52,18 @@ function outputter.debugFrame (_, _, _) end

function outputter.debugHbox (_, _, _) end

function outputter.linkAnchor (_, _, _) end -- Unstable API
function outputter.setLinkAnchor (_, _, _) end -- Unstable API

function outputter.enterLinkTarget (_, _, _) end -- Unstable API
function outputter.beginLink (_, _, _) end -- Unstable API

function outputter.leaveLinkTarget (_, _, _, _, _, _, _) end -- Unstable API
function outputter.endLink (_, _, _, _, _, _, _) end -- Unstable API

function outputter.setMetadata (_, _, _) end

function outputter.setBookmark (_, _, _) end

function outputter.drawRaw (_) end

function outputter:getOutputFilename ()
local fname
if SILE.outputFilename then
Expand Down
6 changes: 6 additions & 0 deletions outputters/cairo.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ outputter._name = "cairo"
outputter.extension = "pdf"

function outputter:_init ()
base._init(self)
local fname = self:getOutputFilename()
local surface = cairolgi.PdfSurface.create(fname == "-" and "/dev/stdout" or fname, SILE.documentState.paperSize[1], SILE.documentState.paperSize[2])
cr = cairolgi.Context.create(surface)
Expand Down Expand Up @@ -119,4 +120,9 @@ function outputter:debugHbox (hbox, scaledWidth)
cr:move_to(x, y)
end

-- untested
function outputter.drawRaw (_, literal)
cr:show_text(literal)
end

return outputter
25 changes: 25 additions & 0 deletions outputters/debug.lua
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ end
function outputter:finish ()
if SILE.status.unsupported then self:_writeline("UNSUPPORTED") end
self:_writeline("End page")
self:runHooks("prefinish")
self:_writeline("Finish")
outfile:close()
end
Expand Down Expand Up @@ -162,4 +163,28 @@ function outputter:drawRule (x, y, width, depth)
self:_writeline("Draw line", _round(x), _round(y), _round(width), _round(depth))
end

function outputter:setLinkAnchor (name, x, y)
self:_writeline("Setting link anchor", name, x, y)
end

function outputter:beginLink (dest, opts)
self:_writeline("Begining a link", dest, opts)
end

function outputter:endLink(dest, opts, x0, y0, x1, y1)
self:_writeline("Ending a link", dest, opts, x0, y0, x1, y1)
end

function outputter:setBookmark (dest, title, level)
self:_writeline("Setting bookmark", dest, title, level)
end

function outputter:setMetadata (key, value)
self:_writeline("Set metadata", key, value)
end

function outputter:drawRaw (literal)
self:_writeline("Draw raw", literal)
end

return outputter
18 changes: 12 additions & 6 deletions outputters/libtexpdf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ end
function outputter:finish ()
self:_ensureInit()
pdf.endpage()
self:_endHook()
self:runHooks("prefinish")
pdf.finish()
started = false
lastkey = nil
Expand Down Expand Up @@ -307,7 +307,7 @@ end

-- Unstable link APIs

function outputter:linkAnchor (x, y, name)
function outputter:setLinkAnchor (name, x, y)
x = SU.cast("number", x)
y = SU.cast("number", y)
self:_ensureInit()
Expand All @@ -328,17 +328,18 @@ local function borderStyle (style, width)
return "/Border[0 0 " .. width .. "]"
end

function outputter:enterLinkTarget (_, _) -- destination, options as argument
function outputter:startLink (_, _) -- destination, options as argument
self:_ensureInit()
-- HACK:
-- Looking at the code, pdf.begin_annotation does nothing, and Simon wrote a comment
-- about tracking boxes. Unsure what he implied with this obscure statement.
-- Sure thing is that some backends may need the destination here, e.g. an HTML backend
-- would generate a <a href="#destination">, as well as the options possibly for styling
-- on the link opening?
self:_ensureInit()
pdf.begin_annotation()
end
function outputter.leaveLinkTarget (_, x0, y0, x1, y1, dest, opts)

function outputter.endLink (_, dest, opts, x0, y0, x1, y1)
local bordercolor = borderColor(opts.bordercolor)
local borderwidth = SU.cast("integer", opts.borderwidth)
local borderstyle = borderStyle(opts.borderstyle, borderwidth)
Expand Down Expand Up @@ -375,7 +376,6 @@ function outputter:setMetadata (key, value)
end

function outputter:setBookmark (dest, title, level)
-- Added UTF8 to UTF16-BE conversion
-- For annotations and bookmarks, text strings must be encoded using
-- either PDFDocEncoding or UTF16-BE with a leading byte-order marker.
-- As PDFDocEncoding supports only limited character repertoire for
Expand All @@ -386,4 +386,10 @@ function outputter:setBookmark (dest, title, level)
pdf.bookmark(d, level)
end

-- Assumes the caller known what they want to stuff in raw PDF format
function outputter:drawRaw (literal)
self:_ensureInit()
pdf.add_content(literal)
end

return outputter
10 changes: 9 additions & 1 deletion outputters/podofo.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ local outputter = pl.class(base)
outputter._name = "podofo"
outputter.extension = "pdf"

function outputter._init (_)
function outputter:_init (_)
base._init(self)
document = pdf.PdfMemDocument()
pagesize = pdf.PdfRect()
pagesize:SetWidth(SILE.documentState.paperSize[1])
Expand All @@ -39,6 +40,7 @@ end

function outputter:finish ()
painter:FinishPage()
self:runHooks("prefinish")
local fname = self:getOutputFilename()
document:Write(fname == "-" and "/dev/stdout" or fname)
end
Expand Down Expand Up @@ -104,4 +106,10 @@ function outputter:debugHbox (hbox, scaledWidth)
--cr:move_to(x, y)
end

-- untested
function outputter:drawRaw (literal)
local x, y = self:getCursor()
painter:DrawText(x, y, literal, string.len(literal))
end

return outputter
6 changes: 6 additions & 0 deletions outputters/text.lua
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ end

function outputter:finish ()
self:_ensureInit()
self:runHooks("prefinish")
outfile:close()
end

Expand Down Expand Up @@ -81,4 +82,9 @@ function outputter:drawHbox (value, width)
end
end

function outputter:drawRaw (literal)
self:_ensureInit()
outfile:write(literal)
end

return outputter
44 changes: 15 additions & 29 deletions packages/pdf/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function package:registerCommands ()
local x, y = state.cursorX, state.cursorY
typesetter.frame:advancePageDirection(line.height)
local _y = SILE.documentState.paperSize[2] - y
SILE.outputter:linkAnchor(x, _y, name)
SILE.outputter:setLinkAnchor(name, x, _y)
end
})
end)
Expand All @@ -39,28 +39,8 @@ function package:registerCommands ()
})
end)

self:registerCommand("pdf:literal", function (_, content)
-- NOTE: This method is used by the pdfstructure package and should
-- probably be moved elsewhere, so there's no attempt here to delegate
-- the low-level libtexpdf call to te outputter.
if SILE.outputter._name ~= "libtexpdf" then
SU.error("pdf package requires libtexpdf backend")
end
local pdf = require("justenoughlibtexpdf")
if type(SILE.outputter._ensureInit) == "function" then
SILE.outputter:_ensureInit()
end
SILE.typesetter:pushHbox({
value = nil,
height = SILE.measurement(0),
width = SILE.measurement(0),
depth = SILE.measurement(0),
outputYourself = function (_, _, _)
pdf.add_content(content[1])
end
})
end)

-- TODO: Shim to pdfannotations package
-- self:registerCommand("pdf:literal", function (_, content)
self:registerCommand("pdf:link", function (options, content)
local dest = SU.required(options, "dest", "pdf:link")
local external = SU.boolean(options.external, false)
Expand All @@ -74,7 +54,6 @@ function package:registerCommands ()
borderwidth = borderwidth,
borderoffset = borderoffset
}

local x0, y0
SILE.typesetter:pushHbox({
value = nil,
Expand All @@ -84,7 +63,7 @@ function package:registerCommands ()
outputYourself = function (_, typesetter, _)
x0 = typesetter.frame.state.cursorX:tonumber()
y0 = (SILE.documentState.paperSize[2] - typesetter.frame.state.cursorY):tonumber()
SILE.outputter:enterLinkTarget(dest, opts)
SILE.outputter:beginLink(dest, opts)
end
})
local hbox, hlist = SILE.typesetter:makeHbox(content) -- hack
Expand All @@ -97,7 +76,7 @@ function package:registerCommands ()
outputYourself = function (_, typesetter, _)
local x1 = typesetter.frame.state.cursorX:tonumber()
local y1 = (SILE.documentState.paperSize[2] - typesetter.frame.state.cursorY + hbox.height):tonumber()
SILE.outputter:leaveLinkTarget(x0, y0, x1, y1, dest, opts) -- Unstable API
SILE.outputter:endLink(dest, opts, x0, y0, x1, y1) -- Unstable API
end
})
SILE.typesetter:pushHlist(hlist)
Expand All @@ -120,15 +99,22 @@ package.documentation = [[
The \autodoc:package{pdf} package enables basic support for PDF links and table-of-contents entries.
It provides the four commands \autodoc:command{\pdf:destination}, \autodoc:command{\pdf:link}, \autodoc:command{\pdf:bookmark}, and \autodoc:command{\pdf:metadata}.
The \autodoc:command{\pdf:destination} parameter creates a link target; it expects a parameter called \autodoc:parameter{name} to uniquely identify the target.
The \autodoc:command{\pdf:destination} parameter creates a link target;
it expects a parameter called \autodoc:parameter{name} to uniquely identify the target.
To create a link to that location in the document, use \autodoc:command{\pdf:link[dest=<name>]{<content>}}.
The \autodoc:command{\pdf:link} command accepts several options defining its border style: a \autodoc:parameter{borderwidth} length setting the border width (defaults to \code{0}, meaning no border), a \autodoc:parameter{borderstyle} string (can be set to \code{underline} or \code{dashed}, otherwise a solid box), a \autodoc:parameter{bordercolor} color specification for this border (defaults to \code{blue}), and finally a \autodoc:parameter{borderoffset} length for adjusting the border with some vertical space above the content and below the baseline (defaults to \code{1pt}).
The \autodoc:command{\pdf:link} command accepts several options defining its border style:
a \autodoc:parameter{borderwidth} length setting the border width (defaults to \code{0}, meaning no border),
a \autodoc:parameter{borderstyle} string (can be set to \code{underline} or \code{dashed}, otherwise a solid box),
a \autodoc:parameter{bordercolor} color specification for this border (defaults to \code{blue}),
and finally a \autodoc:parameter{borderoffset} length for adjusting the border with some vertical space above the content and below the baseline (defaults to \code{1pt}).
Note that PDF renderers may vary on how they honor these border styling features on link annotations.
It also has an \autodoc:parameter{external} option for URL links, which is not intended to be used directly—refer to the \autodoc:package{url} package for more flexibility typesetting external links.
To set arbitrary key-value metadata, use something like \autodoc:command{\pdf:metadata[key=Author, value=J. Smith]}. The PDF metadata field names are case-sensitive. Common keys include \code{Title}, \code{Author}, \code{Subject}, \code{Keywords}, \code{CreationDate}, and \code{ModDate}.
To set arbitrary key-value metadata, use something like \autodoc:command{\pdf:metadata[key=Author, value=J. Smith]}.
The PDF metadata field names are case-sensitive.
Common keys include \code{Title}, \code{Author}, \code{Subject}, \code{Keywords}, \code{CreationDate}, and \code{ModDate}.
\end{document}
]]

Expand Down
25 changes: 22 additions & 3 deletions packages/pdfstructure/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ function package:_init ()
local stRoot = stNode("Document")
stPointer = stRoot
self:loadPackage("pdf")
function SILE.outputters.libtexpdf._endHook (_)
SILE.outputter:registerHook("prefinish", function()
local catalog = pdf.get_dictionary("Catalog")
local structureTree = pdf.parse("<< /Type /StructTreeRoot >>")
pdf.add_dict(catalog, pdf.parse("/StructTreeRoot"), pdf.reference(structureTree))
Expand All @@ -88,7 +88,7 @@ function package:_init ()
pdf.add_dict(structureTree, pdf.parse("/K"), dumpTree(stRoot))
if structureNumberTree then pdf.release(structureNumberTree) end
if structureTree then pdf.release(structureTree) end
end
end)
end

function package:registerCommands ()
Expand Down Expand Up @@ -119,6 +119,24 @@ function package:registerCommands ()
stPointer = oldstPointer
end)

self:registerCommand("pdf:literal", function (_, content)
-- NOTE: This method is used by the pdfstructure package and should
-- probably be moved elsewhere, so there's no attempt here to delegate
-- the low-level libtexpdf call to te outputter.
if SILE.outputter._name ~= "libtexpdf" then
SU.error("pdf package requires libtexpdf backend")
end
SILE.typesetter:pushHbox({
value = nil,
height = SILE.measurement(0),
width = SILE.measurement(0),
depth = SILE.measurement(0),
outputYourself = function (_, _, _)
SILE.outputter:drawRaw (content[1])
end
})
end)

end

package.documentation = [[
Expand All @@ -127,7 +145,8 @@ package.documentation = [[
\pdf:structure[type=P]{%
For PDF documents to be considered accessible, they must contain a description of the PDF’s document structure.
This package allows structure trees to be created and saved to the PDF file.
Currently this provides a low-level interface to creating nodes in the tree; classes which require PDF accessibility should use the \autodoc:command{\pdf:structure} command in their sectioning implementation to declare the document structure.
Currently this provides a low-level interface to creating nodes in the tree;
classes which require PDF accessibility should use the \autodoc:command{\pdf:structure} command in their sectioning implementation to declare the document structure.
}
\pdf:structure[type=P]{%
Expand Down

0 comments on commit 9ec54b1

Please sign in to comment.