#!/usr/bin/env lpg local project_url = "https://git.janouch.name/p/pdf-simple-sign" 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, 6}} end function h3 (title) return lpg.VBox {fontsize=14., fontweight=600, lpg.Filler {-1, 8}, title, lpg.HLine {.25}, lpg.Filler {-1, 6}} end function p (...) return lpg.VBox {..., lpg.Filler {-1, 6}} end function code (...) return lpg.VBox { lpg.Filler {-1, 4}, lpg.HBox { lpg.Filler {12}, lpg.VBox {"" .. table.concat {...} .. ""}, 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 function pad (widget) return lpg.VBox { lpg.Filler {-1, 2}, lpg.HBox {lpg.Filler {4}, widget, lpg.Filler {}, lpg.Filler {4}}, lpg.Filler {-1, 2}, } end local page1 = lpg.VBox {fontfamily="sans serif", fontsize=12., h1("lpg User Manual"), p("lpg 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("lpg program.lua [args...]"), h2("API"), p("The Lua program receives lpg's and its own path joined " .. "as arg[0]. Any remaining sequential elements " .. "of this table represent the passed args."), 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”:"), code("ntoa {3141.592, precision=2,\n" .. " thousands_sep=\" \", decimal_point=\",\", " .. "grouping=\"\\003\"}")), define("lpg.escape (values...)", p("Interprets all values as strings, " .. "and escapes them to be used as literal text—" .. "all text within lpg is parsed as Pango markup, " .. "which is a subset of XML.")), h3("PDF documents"), define("lpg.Document (filename, width, height [, margin])", p("Returns a new Document object, whose pages are all " .. "the same size in 72 DPI points, as specified by width " .. "and height. The margin is used by show " .. "on all sides of pages."), p("The file is finalized when the object is garbage collected.")), define("Document.title, author, subject, keywords, " .. "creator, create_date, mod_date", p("Write-only PDF Info dictionary metadata strings.")), define("Document:show ([widget...])", p("Starts a new document page, and renders Widget 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 Widget is expected but another " .. "kind of value is received, lpg.Text widget will be invoked " .. "on that value."), p("Once a Widget is included in another Widget, " .. "the original Lua object can no longer be used, " .. "as its reference has been consumed."), p("Widgets can be indexed by strings to get or set " .. "their attributes. All Widget constructor tables " .. "also accept attributes, for convenience. Attributes can be " .. "either strings or numbers, mostly only act " .. "on specific Widget kinds, and are hereditary. " .. "Prefix their names with an underscore to set them ‘privately’."), p("Widget 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 thickness.")), define("lpg.VLine {[thickness]}", p("Returns a new widget that draws a simple vertical line " .. "of the given thickness.")), define("lpg.Text {[value...]}", p("Returns a new text widget that renders the concatenation of all " .. "passed values filtered through Lua’s tostring " .. "function. Non-strings will additionally be escaped."), define("Text.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("Frame.color", p("Text and line colour, for example 0xff0000 for red.")), define("Frame.w_override", p("Forcefully changes the child Widget’s " .. "requested width, such as to negative values.")), define("Frame.h_override", p("Forcefully changes the child Widget’s " .. "requested height, such as to negative values."))), lpg.Filler {}, } local page3 = lpg.VBox {fontfamily="sans serif", fontsize=12., define("lpg.Link {target, widget}", p("Returns a new hyperlink widget pointing to the target, " .. "which is a URL. The hyperlink applies " .. "to the entire area of the child widget. " .. "It has no special appearance.")), define("lpg.HBox {[widget...]}", p("Returns a new container widget that places children " .. "horizontally, from left to right."), p("If any space remains after satisfying the children widgets’ " .. "requisitions, it is distributed equally amongst all of them. " .. "Also see the note about negative sizes.")), define("lpg.VBox {[widget...]}", p("Returns a new container widget that places children " .. "vertically, from top to bottom.")), define("lpg.Picture {filename}", p("Returns a new picture widget, showing the given filename, " .. "which currently must be in the PNG format. " .. "Pictures are rescaled to fit, but keep their aspect ratio.")), define("lpg.QR {contents, module}", p("Returns a new QR code widget, encoding the contents " .. "string using the given module size. " .. "The QR code version is chosen automatically.")), h2("Examples"), p("See the source code of this user manual " .. "for the general structure of scripts."), h3("Size distribution and composition"), lpg.VBox { lpg.HLine {}, lpg.HBox { lpg.VLine {}, lpg.Frame {_w_override=lpg.cm(3), pad "3cm"}, lpg.VLine {}, lpg.Frame {pad "Measured"}, lpg.VLine {}, lpg.Frame {_w_override=-1, pad "-1"}, lpg.VLine {}, lpg.Frame {_w_override=-2, pad "-2"}, lpg.VLine {}, }, lpg.HLine {}, }, lpg.Filler {-1, 6}, code([[ function pad (widget) local function f (...) return lpg.Filler {...} end return lpg.VBox {f(-1, 2), lpg.HBox {f(4), w, f(), f(4)}, f(-1, 2)} end lpg.VBox {lpg.HLine {}, lpg.HBox { lpg.VLine {}, lpg.Frame {_w_override=lpg.cm(3), pad "3cm"}, lpg.VLine {}, lpg.Frame {pad "Measured"}, lpg.VLine {}, lpg.Frame {_w_override=-1, pad "-1"}, lpg.VLine {}, lpg.Frame {_w_override=-2, pad "-2"}, lpg.VLine {}, }, lpg.HLine {}}]]), h3("Clickable QR code link"), lpg.HBox { lpg.VBox { p("Go here to report bugs, request features, " .. "or submit pull requests:"), code(([[ url = "%s" lpg.Link {url, lpg.QR {url, 2.5}}]]):format(project_url)), }, lpg.Filler {}, lpg.Link {project_url, lpg.QR {project_url, 2.5}}, }, 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 (%s)"):format(project_url) pdf:show(page1) pdf:show(page2) pdf:show(page3) end