aboutsummaryrefslogtreecommitdiff
path: root/lpg/lpg.lua
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2025-01-08 10:54:40 +0100
committerPřemysl Eric Janouch <p@janouch.name>2025-01-11 09:57:59 +0100
commit7aa1e5dd3d80e2664eac7eb0a409829e9785a521 (patch)
treec986a85bce91de498c7470a87da209700dc081ff /lpg/lpg.lua
parent9319d14566e5d1bfec617c479b8543a9ee3ad4ea (diff)
downloadpdf-simple-sign-lpg-libqr.tar.gz
pdf-simple-sign-lpg-libqr.tar.xz
pdf-simple-sign-lpg-libqr.zip
WIP: Add a Lua PDF generatorlpg-libqr
Because Lua compiled for C++ might be hard to find, provide a wrap. Only GitHub releases seem to contain onelua.c, which is a very handy file. WIP: Add documentation, actually get rid of the libqr copy.
Diffstat (limited to 'lpg/lpg.lua')
-rw-r--r--lpg/lpg.lua165
1 files changed, 165 insertions, 0 deletions
diff --git a/lpg/lpg.lua b/lpg/lpg.lua
new file mode 100644
index 0000000..07ee44a
--- /dev/null
+++ b/lpg/lpg.lua
@@ -0,0 +1,165 @@
+#!/usr/bin/env lpg
+function h1 (title)
+ return lpg.VBox {fontsize=18., fontweight=600,
+ title, lpg.HLine {2}, lpg.Filler {-1, 6}}
+end
+function h2 (title)
+ return lpg.VBox {fontsize=16., fontweight=600,
+ lpg.Filler {-1, 8}, title, lpg.HLine {1}, lpg.Filler {-1, 4}}
+end
+function h3 (title)
+ return lpg.VBox {fontsize=14., fontweight=600,
+ lpg.Filler {-1, 8}, title, lpg.HLine {.25}, lpg.Filler {-1, 4}}
+end
+function p (...)
+ return lpg.VBox {..., lpg.Filler {-1, 6}}
+end
+function pre (...)
+ return lpg.VBox {
+ lpg.Filler {-1, 4},
+ lpg.HBox {lpg.Filler {12}, lpg.VBox {...}, lpg.Filler {}},
+ lpg.Filler {-1, 6},
+ }
+end
+function define (name, ...)
+ return lpg.VBox {
+ lpg.Filler {-1, 2},
+ lpg.Text {fontweight=600, name}, lpg.Filler {-1, 2},
+ lpg.HBox {lpg.Filler {12}, lpg.VBox {...}, lpg.Filler {}},
+ lpg.Filler {-1, 2},
+ }
+end
+
+local page1 = lpg.VBox {fontfamily="sans serif", fontsize=12.,
+ h1("lpg User Manual"),
+ p("<b>lpg</b> is a Lua-based PDF document generator, exposing a trivial " ..
+ "layouting engine on top of the Cairo graphics library, " ..
+ "with manual paging."),
+ p("The author has primarily been using this system to typeset invoices."),
+
+ h2("Synopsis"),
+ p("<b>lpg</b> <i>program.lua</i> [<i>args...</i>]"),
+
+ h2("API"),
+ p("The Lua program receives <b>lpg</b>'s and its own path joined " ..
+ "as <tt>arg[0]</tt>. Any remaining sequential elements " ..
+ "of this table represent the passed <i>args</i>."),
+
+ h3("Utilities"),
+
+ define("lpg.cm (centimeters)",
+ p("Returns how many document points are needed " ..
+ "for the given physical length.")),
+
+ define("lpg.ntoa {number [, precision=…]\n" ..
+ "\t[, thousands_sep=…] [, decimal_point=…] [, grouping=…]}",
+ p("Formats a number using the C++ localization " ..
+ "and I/O libraries. " ..
+ "For example, the following call results in “3 141,59”:"),
+ pre("<tt>ntoa {3141.592, precision=2,\n" ..
+ " thousands_sep=\" \", decimal_point=\",\", " ..
+ "grouping=\"\\003\"}</tt>")),
+
+ define("lpg.escape (values...)",
+ p("Interprets all values as strings, " ..
+ "and escapes them to be used as literal text—" ..
+ "all text within <b>lpg</b> is parsed Pango markup, " ..
+ "which is a subset of XML.")),
+
+ h3("PDF documents"),
+
+ define("lpg.Document (filename, width, height [, margin])",
+ p("Returns a new <i>Document</i> object, whose pages are all " ..
+ "the same size in 72 DPI points, as specified by <b>width</b> " ..
+ "and <b>height</b>. The <b>margin</b> is used by <b>show</b> " ..
+ "on all sides of pages."),
+ p("The file is finalized when the object is garbage collected.")),
+
+ define("<i>Document</i>.title, author, subject, keywords, " ..
+ "creator, create_date, mod_date",
+ p("Write-only PDF <i>Info</i> dictionary metadata strings.")),
+
+ define("<i>Document</i>:show ([widget...])",
+ p("Starts a new document page, and renders <i>Widget</i> trees over " ..
+ "the whole print area.")),
+
+ lpg.Filler {},
+}
+
+local page2 = lpg.VBox {fontfamily="sans serif", fontsize=12.,
+ h3("Widgets"),
+ p("The layouting system makes heavy use of composition, " ..
+ "and thus stays simple."),
+ p("For convenience, anywhere a <i>Widget</i> is expected but another " ..
+ "kind of value is received, <b>lpg.Text</b> widget will be invoked " ..
+ "on that value."),
+ p("Once a <i>Widget</i> is included in another <i>Widget</i>, " ..
+ "the original Lua object can no longer be used, " ..
+ "as its reference has been consumed."),
+ p("<i>Widgets</i> can be indexed by strings to get or set " ..
+ "their <i>attributes</i>. All <i>Widget</i> constructor tables " ..
+ "also accept attributes, for convenience. Attributes can be " ..
+ "either strings or numbers, mostly only act " ..
+ "on specific <i>Widget</i> kinds, and are hereditary. " ..
+ "Prefix their names with an underscore to set them ‘privately’."),
+ p("<i>Widget</i> sizes can be set negative, which signals to their " ..
+ "container that they should take any remaining space, " ..
+ "after all their siblings’ requests have been satisfied. " ..
+ "When multiple widgets make this request, that space is distributed " ..
+ "in proportion to these negative values."),
+
+ define("lpg.Filler {[width] [, height]}",
+ p("Returns a new blank widget with the given dimensions, " ..
+ "which default to -1, -1.")),
+ define("lpg.HLine {[thickness]}",
+ p("Returns a new widget that draws a simple horizontal line " ..
+ "of the given <b>thickness</b>.")),
+ define("lpg.VLine {[thickness]}",
+ p("Returns a new widget that draws a simple vertical line " ..
+ "of the given <b>thickness</b>.")),
+ define("lpg.Text {[value...]}",
+ p("Returns a new text widget that renders the concatenation of all " ..
+ "passed values filtered through Lua’s <b>tostring</b> function."),
+ define("<i>Text</i>.fontfamily, fontsize, fontweight, lineheight",
+ p("Various font properties, similar to their CSS counterparts."))),
+ define("lpg.Frame {widget}",
+ p("Returns a special container widget that can override " ..
+ "a few interesting properties."),
+ define("<i>Frame</i>.color",
+ p("Text and line colour, for example <tt>0xff0000</tt> for red.")),
+ define("<i>Frame</i>.w_override",
+ p("Forcefully changes the child <i>Widget</i>’s " ..
+ "requested width, such as to negative values.")),
+ define("<i>Frame</i>.h_override",
+ p("Forcefully changes the child <i>Widget</i>’s " ..
+ "requested height, such as to negative values."))),
+
+ lpg.Filler {},
+}
+
+local page3 = lpg.VBox {fontfamily="sans serif", fontsize=12.,
+ -- TODO: Link
+ -- TODO: HBox
+ -- TODO: VBox
+ -- TODO: Picture
+ -- TODO: QR
+
+ lpg.Filler {},
+}
+
+if #arg < 1 then
+ io.stderr:write("Usage: " .. arg[0] .. " OUTPUT-PDF..." .. "\n")
+ os.exit(false)
+end
+local width, height, margin = lpg.cm(21), lpg.cm(29.7), lpg.cm(2.0)
+for i = 1, #arg do
+ local pdf = lpg.Document(arg[i], width, height, margin)
+ pdf.title = "lpg User Manual"
+ pdf.subject = "lpg User Manual"
+ pdf.author = "Přemysl Eric Janouch"
+ pdf.creator = "lpg"
+
+ pdf:show(page1)
+ pdf:show(page2)
+ pdf:show(page3)
+end