From 0d64fe067e8054879a58f55f95a4980b648c1087 Mon Sep 17 00:00:00 2001 From: Omikhleia Date: Thu, 9 Jan 2025 21:22:35 +0100 Subject: [PATCH] fix(outputters): Compatibility of modified outputters with SILE 0.15.9 Code styling (3-space indentation etc.) Backport the new abort() method introduced in SILE 0.15.9 --- silex/outputters/base.lua | 51 ++-- silex/outputters/debug.lua | 207 +++++++------ silex/outputters/libtexpdf.lua | 539 ++++++++++++++++++--------------- 3 files changed, 437 insertions(+), 360 deletions(-) diff --git a/silex/outputters/base.lua b/silex/outputters/base.lua index 2d5042d..ee6cd33 100644 --- a/silex/outputters/base.lua +++ b/silex/outputters/base.lua @@ -1,3 +1,6 @@ +--- SILE outputter class. +-- @interfaces outputters + local outputter = pl.class() outputter.type = "outputter" outputter._name = "base" @@ -8,22 +11,30 @@ function outputter:_init () end function outputter:registerHook (category, func) - if not self.hooks[category] then self.hooks[category] = {} end + 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 + 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:abort () + return self:finish() -- unless otherwise defined +end + function outputter:finish () - self:runHooks("prefinish") + self:runHooks("prefinish") end function outputter.getCursor () end @@ -65,19 +76,19 @@ function outputter.setBookmark (_, _, _) end function outputter.drawRaw (_) end function outputter:getOutputFilename () - local fname - if SILE.outputFilename then - fname = SILE.outputFilename - elseif SILE.input.filenames[1] then - fname = pl.path.splitext(SILE.input.filenames[1]) - if self.extension then - fname = fname .. "." .. self.extension - end - end - if not fname then - SU.error("Cannot guess output filename without an input name") - end - return fname + local fname + if SILE.outputFilename then + fname = SILE.outputFilename + elseif SILE.input.filenames[1] then + fname = pl.path.splitext(SILE.input.filenames[1]) + if self.extension then + fname = fname .. "." .. self.extension + end + end + if not fname then + SU.error("Cannot guess output filename without an input name") + end + return fname end return outputter diff --git a/silex/outputters/debug.lua b/silex/outputters/debug.lua index fa08e71..f102da7 100644 --- a/silex/outputters/debug.lua +++ b/silex/outputters/debug.lua @@ -31,147 +31,172 @@ outputter.extension = "debug" -- function outputter:_init () end function outputter:_ensureInit () - if not started then - started = true -- keep this before self:_writeline or it will be a race condition! - local fname = self:getOutputFilename() - outfile = fname == "-" and io.stdout or io.open(fname, "w+") - if SILE.documentState.paperSize then - self:_writeline("Set paper size ", SILE.documentState.paperSize[1], SILE.documentState.paperSize[2]) - end - self:_writeline("Begin page") - end + if not started then + started = true -- keep this before self:_writeline or it will be a race condition! + local fname = self:getOutputFilename() + outfile = fname == "-" and io.stdout or io.open(fname, "w+") + if SILE.documentState.paperSize then + self:_writeline("Set paper size ", SILE.documentState.paperSize[1], SILE.documentState.paperSize[2]) + end + self:_writeline("Begin page") + end end function outputter:_writeline (...) - self:_ensureInit() - local args = pl.utils.pack(...) - for i = 1, #args do - outfile:write(args[i]) - if i < #args then outfile:write("\t") end - end - outfile:write("\n") + self:_ensureInit() + local args = pl.utils.pack(...) + for i = 1, #args do + outfile:write(args[i]) + if i < #args then + outfile:write("\t") + end + end + outfile:write("\n") end function outputter:newPage () - self:_writeline("New page") + self:_writeline("New page") +end + +function outputter:abort () + if started then + self:_writeline("Aborted") + outfile:close() + started = false + end 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() + if SILE.status.unsupported then + self:_writeline("UNSUPPORTED") + end + self:_writeline("End page") + self:runHooks("prefinish") + self:_writeline("Finish") + outfile:close() + started = false end function outputter.getCursor (_) - return cursorX, cursorY + return cursorX, cursorY end function outputter:setCursor (x, y, relative) - x = SU.cast("number", x) - y = SU.cast("number", y) - local oldx, oldy = self:getCursor() - local offset = relative and { x = cursorX, y = cursorY } or { x = 0, y = 0 } - cursorX = offset.x + x - cursorY = offset.y - y - if _round(oldx) ~= _round(cursorX) then self:_writeline("Mx ", _round(x)) end - if _round(oldy) ~= _round(cursorY) then self:_writeline("My ", _round(y)) end + x = SU.cast("number", x) + y = SU.cast("number", y) + local oldx, oldy = self:getCursor() + local offset = relative and { x = cursorX, y = cursorY } or { x = 0, y = 0 } + cursorX = offset.x + x + cursorY = offset.y - y + if _round(oldx) ~= _round(cursorX) then + self:_writeline("Mx ", _round(x)) + end + if _round(oldy) ~= _round(cursorY) then + self:_writeline("My ", _round(y)) + end end function outputter:setColor (color) - if color.r then - self:_writeline("Set color", _round(color.r), _round(color.g), _round(color.b)) - elseif color.c then - self:_writeline("Set color", _round(color.c), _round(color.m), _round(color.y), _round(color.k)) - elseif color.l then - self:_writeline("Set color", _round(color.l)) - end + if color.r then + self:_writeline("Set color", _round(color.r), _round(color.g), _round(color.b)) + elseif color.c then + self:_writeline("Set color", _round(color.c), _round(color.m), _round(color.y), _round(color.k)) + elseif color.l then + self:_writeline("Set color", _round(color.l)) + end end function outputter:pushColor (color) - if color.r then - self:_writeline("Push color", _round(color.r), _round(color.g), _round(color.b)) - elseif color.c then - self:_writeline("Push color (CMYK)", _round(color.c), _round(color.m), _round(color.y), _round(color.k)) - elseif color.l then - self:_writeline("Push color (grayscale)", _round(color.l)) - end + if color.r then + self:_writeline("Push color", _round(color.r), _round(color.g), _round(color.b)) + elseif color.c then + self:_writeline("Push color (CMYK)", _round(color.c), _round(color.m), _round(color.y), _round(color.k)) + elseif color.l then + self:_writeline("Push color (grayscale)", _round(color.l)) + end end function outputter:popColor () - self:_writeline("Pop color") + self:_writeline("Pop color") end function outputter:drawHbox (value, width) - if not value.glyphString then return end - width = SU.cast("number", width) - local buf - if value.complex then - local cluster = {} - for i = 1, #value.items do - local item = value.items[i] - cluster[#cluster+1] = item.gid - -- For the sake of terseness we're only dumping non-zero values - if item.glyphAdvance ~= 0 then cluster[#cluster+1] = "a=".._round(item.glyphAdvance) end - if item.x_offset then cluster[#cluster+1] = "x=".._round(item.x_offset) end - if item.y_offset then cluster[#cluster+1] = "y=".._round(item.y_offset) end - self:setCursor(item.width, 0, true) - end - buf = table.concat(cluster, " ") - else - buf = table.concat(value.glyphString, " ") .. " w=" .. _round(width) - end - self:_writeline("T", buf, "(" .. tostring(value.text) .. ")") + if not value.glyphString + then return + end + width = SU.cast("number", width) + local buf + if value.complex then + local cluster = {} + for i = 1, #value.items do + local item = value.items[i] + cluster[#cluster + 1] = item.gid + -- For the sake of terseness we're only dumping non-zero values + if item.glyphAdvance ~= 0 then + cluster[#cluster + 1] = "a=" .. _round(item.glyphAdvance) + end + if item.x_offset then + cluster[#cluster + 1] = "x=" .. _round(item.x_offset) + end + if item.y_offset then + cluster[#cluster + 1] = "y=" .. _round(item.y_offset) + end + self:setCursor(item.width, 0, true) + end + buf = table.concat(cluster, " ") + else + buf = table.concat(value.glyphString, " ") .. " w=" .. _round(width) + end + self:_writeline("T", buf, "(" .. tostring(value.text) .. ")") end function outputter:setFont (options) - local font = SILE.font._key(options) - if lastFont ~= font then - self:_writeline("Set font ", font) - lastFont = font - end + local font = SILE.font._key(options) + if lastFont ~= font then + self:_writeline("Set font ", font) + lastFont = font + end end function outputter:drawImage (src, x, y, width, height) - x = SU.cast("number", x) - y = SU.cast("number", y) - width = SU.cast("number", width) - height = SU.cast("number", height) - self:_writeline("Draw image", src, _round(x), _round(y), _round(width), _round(height)) + x = SU.cast("number", x) + y = SU.cast("number", y) + width = SU.cast("number", width) + height = SU.cast("number", height) + self:_writeline("Draw image", src, _round(x), _round(y), _round(width), _round(height)) end function outputter.getImageSize (_, src, pageno) - local pdf = require("justenoughlibtexpdf") - local llx, lly, urx, ury, xresol, yresol = pdf.imagebbox(src, pageno) - return (urx-llx), (ury-lly), xresol, yresol + local pdf = require("justenoughlibtexpdf") + local llx, lly, urx, ury, xresol, yresol = pdf.imagebbox(src, pageno) + return (urx - llx), (ury - lly), xresol, yresol end function outputter:drawSVG (figure, _, x, y, width, height, scalefactor) - x = SU.cast("number", x) - y = SU.cast("number", y) - width = SU.cast("number", width) - height = SU.cast("number", height) - self:_writeline("Draw SVG", _round(x), _round(y), _round(width), _round(height), figure, scalefactor) + x = SU.cast("number", x) + y = SU.cast("number", y) + width = SU.cast("number", width) + height = SU.cast("number", height) + self:_writeline("Draw SVG", _round(x), _round(y), _round(width), _round(height), figure, scalefactor) end function outputter:drawRule (x, y, width, depth) - x = SU.cast("number", x) - y = SU.cast("number", y) - width = SU.cast("number", width) - depth = SU.cast("number", depth) - self:_writeline("Draw line", _round(x), _round(y), _round(width), _round(depth)) + x = SU.cast("number", x) + y = SU.cast("number", y) + width = SU.cast("number", width) + depth = SU.cast("number", 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) + self:_writeline("Setting link anchor", name, x, y) end function outputter:beginLink (dest, opts) - self:_writeline("Begining a link", dest, opts) + self:_writeline("Beginning a link", dest, opts) end -function outputter:endLink(dest, opts, x0, y0, x1, y1) +function outputter:endLink (dest, opts, x0, y0, x1, y1) self:_writeline("Ending a link", dest, opts, x0, y0, x1, y1) end diff --git a/silex/outputters/libtexpdf.lua b/silex/outputters/libtexpdf.lua index 74fcc1c..779ab34 100644 --- a/silex/outputters/libtexpdf.lua +++ b/silex/outputters/libtexpdf.lua @@ -10,7 +10,7 @@ local lastkey = false local debugfont = SILE.font.loadDefaults({ family = "Gentium Plus", language = "en", size = 10 }) local glyph2string = function (glyph) - return string.char(math.floor(glyph % 2^32 / 2^8)) .. string.char(glyph % 0x100) + return string.char(math.floor(glyph % 2 ^ 32 / 2 ^ 8)) .. string.char(glyph % 0x100) end local _dl = 0.5 @@ -27,18 +27,18 @@ outputter.extension = "pdf" local deltaX local deltaY local function trueXCoord (x) - if not deltaX then - local sheetSize = SILE.documentState.sheetSize or SILE.documentState.paperSize - deltaX = (sheetSize[1] - SILE.documentState.paperSize[1]) / 2 - end - return x + deltaX + if not deltaX then + local sheetSize = SILE.documentState.sheetSize or SILE.documentState.paperSize + deltaX = (sheetSize[1] - SILE.documentState.paperSize[1]) / 2 + end + return x + deltaX end local function trueYCoord (y) - if not deltaY then - local sheetSize = SILE.documentState.sheetSize or SILE.documentState.paperSize - deltaY = (sheetSize[2] - SILE.documentState.paperSize[2]) / 2 - end - return y + deltaY + if not deltaY then + local sheetSize = SILE.documentState.sheetSize or SILE.documentState.paperSize + deltaY = (sheetSize[2] - SILE.documentState.paperSize[2]) / 2 + end + return y + deltaY end -- The outputter init can't actually initialize output (as logical as it might @@ -46,209 +46,236 @@ end -- function outputter:_init () end function outputter:_ensureInit () - if not started then - local sheetSize = SILE.documentState.sheetSize or SILE.documentState.paperSize - local w, h = sheetSize[1], sheetSize[2] - local fname = self:getOutputFilename() - -- Ideally we could want to set the PDF CropBox, BleedBox, TrimBox... - -- Our wrapper only manages the MediaBox at this point. - pdf.init(fname == "-" and "/dev/stdout" or fname, w, h, SILE.full_version) - pdf.beginpage() - started = true - end + if not started then + local sheetSize = SILE.documentState.sheetSize or SILE.documentState.paperSize + local w, h = sheetSize[1], sheetSize[2] + local fname = self:getOutputFilename() + -- Ideally we could want to set the PDF CropBox, BleedBox, TrimBox... + -- Our wrapper only manages the MediaBox at this point. + pdf.init(fname == "-" and "/dev/stdout" or fname, w, h, SILE.full_version) + pdf.beginpage() + started = true + end end function outputter:newPage () - self:_ensureInit() - pdf.endpage() - pdf.beginpage() + self:_ensureInit() + pdf.endpage() + pdf.beginpage() end -- pdf structure package needs a tie in here -function outputter._endHook (_) +function outputter._endHook (_) end + +function outputter.abort () + if started then + pdf.endpage() + pdf.finish() + started = false + lastkey = false + end end function outputter:finish () - self:_ensureInit() - pdf.endpage() - self:runHooks("prefinish") - pdf.finish() - started = false - lastkey = nil + -- allows generation of empty PDFs + self:_ensureInit() + pdf.endpage() + self:runHooks("prefinish") + pdf.finish() + started = false + lastkey = false end function outputter.getCursor (_) - return cursorX, cursorY + return cursorX, cursorY end function outputter.setCursor (_, x, y, relative) - x = SU.cast("number", x) - y = SU.cast("number", y) - local offset = relative and { x = cursorX, y = cursorY } or { x = 0, y = 0 } - cursorX = offset.x + x - cursorY = offset.y + (relative and 0 or SILE.documentState.paperSize[2]) - y + x = SU.cast("number", x) + y = SU.cast("number", y) + local offset = relative and { x = cursorX, y = cursorY } or { x = 0, y = 0 } + cursorX = offset.x + x + cursorY = offset.y + (relative and 0 or SILE.documentState.paperSize[2]) - y end function outputter:setColor (color) - self:_ensureInit() - if color.r then pdf.setcolor_rgb(color.r, color.g, color.b) end - if color.c then pdf.setcolor_cmyk(color.c, color.m, color.y, color.k) end - if color.l then pdf.setcolor_gray(color.l) end + self:_ensureInit() + if color.r then + pdf.setcolor_rgb(color.r, color.g, color.b) + end + if color.c then + pdf.setcolor_cmyk(color.c, color.m, color.y, color.k) + end + if color.l then + pdf.setcolor_gray(color.l) + end end function outputter:pushColor (color) - self:_ensureInit() - if color.r then pdf.colorpush_rgb(color.r, color.g, color.b) end - if color.c then pdf.colorpush_cmyk(color.c, color.m, color.y, color.k) end - if color.l then pdf.colorpush_gray(color.l) end + self:_ensureInit() + if color.r then + pdf.colorpush_rgb(color.r, color.g, color.b) + end + if color.c then + pdf.colorpush_cmyk(color.c, color.m, color.y, color.k) + end + if color.l then + pdf.colorpush_gray(color.l) + end end function outputter:popColor () - self:_ensureInit() - pdf.colorpop() + self:_ensureInit() + pdf.colorpop() end function outputter:_drawString (str, width, x_offset, y_offset) - local x, y = self:getCursor() - pdf.colorpush_rgb(0,0,0) - pdf.colorpop() - pdf.setstring(trueXCoord(x+x_offset), trueYCoord(y+y_offset), str, string.len(str), _font, width) + local x, y = self:getCursor() + pdf.colorpush_rgb(0, 0, 0) + pdf.colorpop() + pdf.setstring(trueXCoord(x + x_offset), trueYCoord(y + y_offset), str, string.len(str), _font, width) end function outputter:drawHbox (value, width) - width = SU.cast("number", width) - self:_ensureInit() - if not value.glyphString then return end - -- Nodes which require kerning or have offsets to the glyph - -- position should be output a glyph at a time. We pass the - -- glyph advance from the htmx table, so that libtexpdf knows - -- how wide each glyph is. It uses this to then compute the - -- relative position between the pen after the glyph has been - -- painted (cursorX + glyphAdvance) and the next painting - -- position (cursorX + width - remember that the box's "width" - -- is actually the shaped x_advance). - if value.complex then - for i = 1, #value.items do - local item = value.items[i] - local buf = glyph2string(item.gid) - self:_drawString(buf, item.glyphAdvance, item.x_offset or 0, item.y_offset or 0) - self:setCursor(item.width, 0, true) - end - else - local buf = {} - for i = 1, #value.glyphString do - buf[i] = glyph2string(value.glyphString[i]) - end - buf = table.concat(buf, "") - self:_drawString(buf, width, 0, 0) - end + width = SU.cast("number", width) + self:_ensureInit() + if not value.glyphString then + return + end + -- Nodes which require kerning or have offsets to the glyph + -- position should be output a glyph at a time. We pass the + -- glyph advance from the htmx table, so that libtexpdf knows + -- how wide each glyph is. It uses this to then compute the + -- relative position between the pen after the glyph has been + -- painted (cursorX + glyphAdvance) and the next painting + -- position (cursorX + width - remember that the box's "width" + -- is actually the shaped x_advance). + if value.complex then + for i = 1, #value.items do + local item = value.items[i] + local buf = glyph2string(item.gid) + self:_drawString(buf, item.glyphAdvance, item.x_offset or 0, item.y_offset or 0) + self:setCursor(item.width, 0, true) + end + else + local buf = {} + for i = 1, #value.glyphString do + buf[i] = glyph2string(value.glyphString[i]) + end + buf = table.concat(buf, "") + self:_drawString(buf, width, 0, 0) + end end function outputter:_withDebugFont (callback) - if not _debugfont then - _debugfont = self:setFont(debugfont) - end - local oldfont = _font - _font = _debugfont - callback() - _font = oldfont + if not _debugfont then + _debugfont = self:setFont(debugfont) + end + local oldfont = _font + _font = _debugfont + callback() + _font = oldfont end function outputter:setFont (options) - self:_ensureInit() - local key = SILE.font._key(options) - if lastkey and key == lastkey then return _font end - local font = SILE.font.cache(options, SILE.shaper.getFace) - if options.direction == "TTB" then - font.layout_dir = 1 - end - if SILE.typesetter.frame and SILE.typesetter.frame:writingDirection() == "TTB" then - pdf.setdirmode(1) - else - pdf.setdirmode(0) - end - _font = pdf.loadfont(font) - if _font < 0 then SU.error("Font loading error for " .. pl.pretty.write(options, "")) end - lastkey = key - return _font + self:_ensureInit() + local key = SILE.font._key(options) + if lastkey and key == lastkey then + return _font + end + local font = SILE.font.cache(options, SILE.shaper.getFace) + if options.direction == "TTB" then + font.layout_dir = 1 + end + if SILE.typesetter.frame and SILE.typesetter.frame:writingDirection() == "TTB" then + pdf.setdirmode(1) + else + pdf.setdirmode(0) + end + _font = pdf.loadfont(font) + if _font < 0 then + SU.error("Font loading error for " .. pl.pretty.write(options, "")) + end + lastkey = key + return _font end function outputter:drawImage (src, x, y, width, height, pageno) - x = SU.cast("number", x) - y = SU.cast("number", y) - width = SU.cast("number", width) - height = SU.cast("number", height) - self:_ensureInit() - pdf.drawimage(src, trueXCoord(x), trueYCoord(y), width, height, pageno or 1) + x = SU.cast("number", x) + y = SU.cast("number", y) + width = SU.cast("number", width) + height = SU.cast("number", height) + self:_ensureInit() + pdf.drawimage(src, trueXCoord(x), trueYCoord(y), width, height, pageno or 1) end function outputter:getImageSize (src, pageno) - self:_ensureInit() -- in case it's a PDF file - local llx, lly, urx, ury, xresol, yresol = pdf.imagebbox(src, pageno or 1) - return (urx-llx), (ury-lly), xresol, yresol + self:_ensureInit() -- in case it's a PDF file + local llx, lly, urx, ury, xresol, yresol = pdf.imagebbox(src, pageno or 1) + return (urx - llx), (ury - lly), xresol, yresol end function outputter:drawSVG (figure, x, y, _, height, scalefactor) - self:_ensureInit() - x = SU.cast("number", x) - y = SU.cast("number", y) - height = SU.cast("number", height) - pdf.add_content("q") - self:setCursor(x, y) - x, y = self:getCursor() - local sheetSize = SILE.documentState.sheetSize or SILE.documentState.paperSize - local newy = y - SILE.documentState.paperSize[2] / 2 + height - sheetSize[2] / 2 - pdf.add_content(table.concat({ scalefactor, 0, 0, -scalefactor, trueXCoord(x), newy, "cm" }, " ")) - pdf.add_content(figure) - pdf.add_content("Q") + self:_ensureInit() + x = SU.cast("number", x) + y = SU.cast("number", y) + height = SU.cast("number", height) + pdf.add_content("q") + self:setCursor(x, y) + x, y = self:getCursor() + local sheetSize = SILE.documentState.sheetSize or SILE.documentState.paperSize + local newy = y - SILE.documentState.paperSize[2] / 2 + height - sheetSize[2] / 2 + pdf.add_content(table.concat({ scalefactor, 0, 0, -scalefactor, trueXCoord(x), newy, "cm" }, " ")) + pdf.add_content(figure) + pdf.add_content("Q") end function outputter:drawRule (x, y, width, height) - x = SU.cast("number", x) - y = SU.cast("number", y) - width = SU.cast("number", width) - height = SU.cast("number", height) - self:_ensureInit() - local paperY = SILE.documentState.paperSize[2] - pdf.setrule(trueXCoord(x), trueYCoord(paperY - y - height), width, height) + x = SU.cast("number", x) + y = SU.cast("number", y) + width = SU.cast("number", width) + height = SU.cast("number", height) + self:_ensureInit() + local paperY = SILE.documentState.paperSize[2] + pdf.setrule(trueXCoord(x), trueYCoord(paperY - y - height), width, height) end function outputter:debugFrame (frame) - self:_ensureInit() - self:pushColor({ r = 0.8, g = 0, b = 0 }) - self:drawRule(frame:left()-_dl/2, frame:top()-_dl/2, frame:width()+_dl, _dl) - self:drawRule(frame:left()-_dl/2, frame:top()-_dl/2, _dl, frame:height()+_dl) - self:drawRule(frame:right()-_dl/2, frame:top()-_dl/2, _dl, frame:height()+_dl) - self:drawRule(frame:left()-_dl/2, frame:bottom()-_dl/2, frame:width()+_dl, _dl) - -- self:drawRule(frame:left() + frame:width()/2 - 5, (frame:top() + frame:bottom())/2+5, 10, 10) - local stuff = SILE.shaper:createNnodes(frame.id, debugfont) - stuff = stuff[1].nodes[1].value.glyphString -- Horrible hack - local buf = {} - for i = 1, #stuff do - buf[i] = glyph2string(stuff[i]) - end - buf = table.concat(buf, "") - self:_withDebugFont(function () - self:setCursor(frame:left():tonumber() - _dl/2, frame:top():tonumber() + _dl/2) - self:_drawString(buf, 0, 0, 0) - end) - self:popColor() + self:_ensureInit() + self:pushColor({ r = 0.8, g = 0, b = 0 }) + self:drawRule(frame:left() - _dl / 2, frame:top() - _dl / 2, frame:width() + _dl, _dl) + self:drawRule(frame:left() - _dl / 2, frame:top() - _dl / 2, _dl, frame:height() + _dl) + self:drawRule(frame:right() - _dl / 2, frame:top() - _dl / 2, _dl, frame:height() + _dl) + self:drawRule(frame:left() - _dl / 2, frame:bottom() - _dl / 2, frame:width() + _dl, _dl) + -- self:drawRule(frame:left() + frame:width()/2 - 5, (frame:top() + frame:bottom())/2+5, 10, 10) + local stuff = SILE.shaper:createNnodes(frame.id, debugfont) + stuff = stuff[1].nodes[1].value.glyphString -- Horrible hack + local buf = {} + for i = 1, #stuff do + buf[i] = glyph2string(stuff[i]) + end + buf = table.concat(buf, "") + self:_withDebugFont(function () + self:setCursor(frame:left():tonumber() - _dl / 2, frame:top():tonumber() + _dl / 2) + self:_drawString(buf, 0, 0, 0) + end) + self:popColor() end function outputter:debugHbox (hbox, scaledWidth) - self:_ensureInit() - self:pushColor({ r = 0.8, g = 0.3, b = 0.3 }) - local paperY = SILE.documentState.paperSize[2] - local x, y = self:getCursor() - y = paperY - y - self:drawRule(x-_dl/2, y-_dl/2-hbox.height, scaledWidth+_dl, _dl) - self:drawRule(x-_dl/2, y-hbox.height-_dl/2, _dl, hbox.height+hbox.depth+_dl) - self:drawRule(x-_dl/2, y-_dl/2, scaledWidth+_dl, _dl) - self:drawRule(x+scaledWidth-_dl/2, y-hbox.height-_dl/2, _dl, hbox.height+hbox.depth+_dl) - if hbox.depth > SILE.types.length(0) then - self:drawRule(x-_dl/2, y+hbox.depth-_dl/2, scaledWidth+_dl, _dl) - end - self:popColor() + self:_ensureInit() + self:pushColor({ r = 0.8, g = 0.3, b = 0.3 }) + local paperY = SILE.documentState.paperSize[2] + local x, y = self:getCursor() + y = paperY - y + self:drawRule(x - _dl / 2, y - _dl / 2 - hbox.height, scaledWidth + _dl, _dl) + self:drawRule(x - _dl / 2, y - hbox.height - _dl / 2, _dl, hbox.height + hbox.depth + _dl) + self:drawRule(x - _dl / 2, y - _dl / 2, scaledWidth + _dl, _dl) + self:drawRule(x + scaledWidth - _dl / 2, y - hbox.height - _dl / 2, _dl, hbox.height + hbox.depth + _dl) + if hbox.depth > SILE.types.length(0) then + self:drawRule(x - _dl / 2, y + hbox.depth - _dl / 2, scaledWidth + _dl, _dl) + end + self:popColor() end -- The methods below are only implemented on outputters supporting these features. @@ -257,133 +284,147 @@ end -- ! The API is unstable and subject to change. ! function outputter:scaleFn (xorigin, yorigin, xratio, yratio, callback) - xorigin = SU.cast("number", xorigin) - yorigin = SU.cast("number", yorigin) - local x0 = trueXCoord(xorigin) - local y0 = -trueYCoord(yorigin) - self:_ensureInit() - pdf:gsave() - pdf.setmatrix(1, 0, 0, 1, x0, y0) - pdf.setmatrix(xratio, 0, 0, yratio, 0, 0) - pdf.setmatrix(1, 0, 0, 1, -x0, -y0) - callback() - pdf:grestore() + xorigin = SU.cast("number", xorigin) + yorigin = SU.cast("number", yorigin) + local x0 = trueXCoord(xorigin) + local y0 = -trueYCoord(yorigin) + self:_ensureInit() + pdf:gsave() + pdf.setmatrix(1, 0, 0, 1, x0, y0) + pdf.setmatrix(xratio, 0, 0, yratio, 0, 0) + pdf.setmatrix(1, 0, 0, 1, -x0, -y0) + callback() + pdf:grestore() end function outputter:rotateFn (xorigin, yorigin, theta, callback) - xorigin = SU.cast("number", xorigin) - yorigin = SU.cast("number", yorigin) - local x0 = trueXCoord(xorigin) - local y0 = -trueYCoord(yorigin) - self:_ensureInit() - pdf:gsave() - pdf.setmatrix(1, 0, 0, 1, x0, y0) - pdf.setmatrix(math.cos(theta), math.sin(theta), -math.sin(theta), math.cos(theta), 0, 0) - pdf.setmatrix(1, 0, 0, 1, -x0, -y0) - callback() - pdf:grestore() + xorigin = SU.cast("number", xorigin) + yorigin = SU.cast("number", yorigin) + local x0 = trueXCoord(xorigin) + local y0 = -trueYCoord(yorigin) + self:_ensureInit() + pdf:gsave() + pdf.setmatrix(1, 0, 0, 1, x0, y0) + pdf.setmatrix(math.cos(theta), math.sin(theta), -math.sin(theta), math.cos(theta), 0, 0) + pdf.setmatrix(1, 0, 0, 1, -x0, -y0) + callback() + pdf:grestore() end -- Other rotation unstable APIs function outputter:enterFrameRotate (xa, xb, y, theta) -- Unstable API see rotate package - xa = SU.cast("number", xa) - xb = SU.cast("number", xb) - y = SU.cast("number", y) - -- Keep center point the same? - local cx0 = trueXCoord(xa) - local cx1 = trueXCoord(xb) - local cy = -trueYCoord(y) - self:_ensureInit() - pdf:gsave() - pdf.setmatrix(1, 0, 0, 1, cx1, cy) - pdf.setmatrix(math.cos(theta), math.sin(theta), -math.sin(theta), math.cos(theta), 0, 0) - pdf.setmatrix(1, 0, 0, 1, -cx0, -cy) + xa = SU.cast("number", xa) + xb = SU.cast("number", xb) + y = SU.cast("number", y) + -- Keep center point the same? + local cx0 = trueXCoord(xa) + local cx1 = trueXCoord(xb) + local cy = -trueYCoord(y) + self:_ensureInit() + pdf:gsave() + pdf.setmatrix(1, 0, 0, 1, cx1, cy) + pdf.setmatrix(math.cos(theta), math.sin(theta), -math.sin(theta), math.cos(theta), 0, 0) + pdf.setmatrix(1, 0, 0, 1, -cx0, -cy) end function outputter.leaveFrameRotate (_) - pdf:grestore() + pdf:grestore() end -- Unstable link APIs function outputter:setLinkAnchor (name, x, y) - x = SU.cast("number", x) - y = SU.cast("number", y) - self:_ensureInit() - pdf.destination(name, trueXCoord(x), trueYCoord(y)) + x = SU.cast("number", x) + y = SU.cast("number", y) + self:_ensureInit() + pdf.destination(name, trueXCoord(x), trueYCoord(y)) end local function borderColor (color) - if color then - if color.r then return "/C [" .. color.r .. " " .. color.g .. " " .. color.b .. "]" end - if color.c then return "/C [" .. color.c .. " " .. color.m .. " " .. color.y .. " " .. color.k .. "]" end - if color.l then return "/C [" .. color.l .. "]" end - end - return "" + if color then + if color.r then + return "/C [" .. color.r .. " " .. color.g .. " " .. color.b .. "]" + end + if color.c then + return "/C [" .. color.c .. " " .. color.m .. " " .. color.y .. " " .. color.k .. "]" + end + if color.l then + return "/C [" .. color.l .. "]" + end + end + return "" end local function borderStyle (style, width) - if style == "underline" then return "/BS<>" end - if style == "dashed" then return "/BS<>" end - return "/Border[0 0 " .. width .. "]" + if style == "underline" then + return "/BS<>" + end + if style == "dashed" then + return "/BS<>" + end + return "/Border[0 0 " .. width .. "]" end -function outputter:beginLink (_, _) -- 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 , as well as the options possibly for styling - -- on the link opening? - pdf.begin_annotation() +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 , as well as the options possibly for styling + -- on the link opening? + pdf.begin_annotation() end 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) - local target = opts.external and "/Type/Action/S/URI/URI" or "/S/GoTo/D" - local d = "<>>>" - pdf.end_annotation(d, - trueXCoord(x0) , trueYCoord(y0 - opts.borderoffset), - trueXCoord(x1), trueYCoord(y1 + opts.borderoffset)) + local bordercolor = borderColor(opts.bordercolor) + local borderwidth = SU.cast("integer", opts.borderwidth) + local borderstyle = borderStyle(opts.borderstyle, borderwidth) + local target = opts.external and "/Type/Action/S/URI/URI" or "/S/GoTo/D" + local d = "<>>>" + pdf.end_annotation( + d, + trueXCoord(x0), + trueYCoord(y0 - opts.borderoffset), + trueXCoord(x1), + trueYCoord(y1 + opts.borderoffset) + ) end -- Bookmarks and metadata local function validate_date (date) - return string.match(date, [[^D:%d+%s*-%s*%d%d%s*'%s*%d%d%s*'?$]]) ~= nil + return string.match(date, [[^D:%d+%s*-%s*%d%d%s*'%s*%d%d%s*'?$]]) ~= nil end function outputter:setMetadata (key, value) - if key == "Trapped" then - SU.warn("Skipping special metadata key \\Trapped") - return - end - - if key == "ModDate" or key == "CreationDate" then - if not validate_date(value) then - SU.warn("Invalid date: " .. value) + if key == "Trapped" then + SU.warn("Skipping special metadata key \\Trapped") return - end - else - -- see comment in on bookmark - value = SU.utf8_to_utf16be(value) - end - self:_ensureInit() - pdf.metadata(key, value) + end + + if key == "ModDate" or key == "CreationDate" then + if not validate_date(value) then + SU.warn("Invalid date: " .. value) + return + end + else + -- see comment in on bookmark + value = SU.utf8_to_utf16be(value) + end + self:_ensureInit() + pdf.metadata(key, value) end function outputter:setBookmark (dest, title, level) - -- 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 - -- European languages, we use UTF-16BE for internationalization. - local ustr = SU.utf8_to_utf16be_hexencoded(title) - local d = "</A<>>>" - self:_ensureInit() - pdf.bookmark(d, level) + -- 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 + -- European languages, we use UTF-16BE for internationalization. + local ustr = SU.utf8_to_utf16be_hexencoded(title) + local d = "</A<>>>" + self:_ensureInit() + pdf.bookmark(d, level) end -- Assumes the caller known what they want to stuff in raw PDF format