local base = require("classes.base")
local class = pl.class(base)
class._name = "plain"
class.defaultFrameset = {
content = {
left = "5%pw",
right = "95%pw",
top = "5%ph",
bottom = "top(footnotes)",
},
folio = {
left = "left(content)",
right = "right(content)",
top = "bottom(footnotes)+2%ph",
bottom = "97%ph",
},
footnotes = {
left = "left(content)",
right = "right(content)",
height = "0",
bottom = "90%ph",
},
}
class.firstContentFrame = "content"
local skips = {
small = "3pt plus 1pt minus 1pt",
med = "6pt plus 2pt minus 2pt",
big = "12pt plus 4pt minus 4pt",
}
function class:_init (options)
base._init(self, options)
self:loadPackage("bidi")
self:loadPackage("folio")
end
function class:declareOptions ()
base.declareOptions(self)
self:declareOption("direction", function (_, value)
if value then
SILE.documentState.direction = value
SILE.settings:set("font.direction", value, true)
for _, frame in pairs(self.defaultFrameset) do
if not frame.direction then
frame.direction = value
end
end
end
return SILE.documentState.direction
end)
end
function class:setOptions (options)
base.setOptions(self, options)
end
function class:declareSettings ()
base.declareSettings(self)
for k, v in pairs(skips) do
SILE.settings:declare({
parameter = "plain." .. k .. "skipamount",
type = "vglue",
default = SILE.types.node.vglue(v),
help = "The amount of a \\" .. k .. "skip",
})
end
end
function class:registerCommands ()
SILE.classes.base.registerCommands(self)
self:registerCommand("noindent", function (_, content)
if #SILE.typesetter.state.nodes ~= 0 then
SU.warn([[
\noindent was called after paragraph content has already been processed
This will not result in avoiding the current paragraph being indented. This
function must be called before any content belonging to the paragraph is
processed. If the intent was to suppress indentation of a following paragraph,
first explicitly close the current paragraph. From an input document this is
typically done with an empty line between paragraphs, but calling the \par
command explicitly or from Lua code running SILE.call("par") will end
the current paragraph.
]])
end
SILE.settings:set("current.parindent", SILE.types.node.glue())
SILE.process(content)
end, "Do not add an indent to the start of this paragraph")
self:registerCommand("neverindent", function (_, content)
SILE.settings:set("current.parindent", SILE.types.node.glue())
SILE.settings:set("document.parindent", SILE.types.node.glue())
SILE.process(content)
end, "Turn off all indentation")
self:registerCommand("indent", function (_, content)
SILE.settings:set("current.parindent", SILE.settings:get("document.parindent"))
SILE.process(content)
end, "Do add an indent to the start of this paragraph, even if previously told otherwise")
for k, _ in pairs(skips) do
self:registerCommand(k .. "skip", function (_, _)
SILE.typesetter:leaveHmode()
SILE.typesetter:pushExplicitVglue(SILE.settings:get("plain." .. k .. "skipamount"))
end, "Skip vertically by a " .. k .. " amount")
end
self:registerCommand("hfill", function (_, _)
SILE.typesetter:pushExplicitGlue(SILE.types.node.hfillglue())
end, "Add a huge horizontal glue")
self:registerCommand("vfill", function (_, _)
SILE.typesetter:leaveHmode()
SILE.typesetter:pushExplicitVglue(SILE.types.node.vfillglue())
end, "Add huge vertical glue")
self:registerCommand("hss", function (_, _)
SILE.typesetter:pushGlue(SILE.types.node.hssglue())
table.insert(SILE.typesetter.state.nodes, SILE.types.node.zerohbox())
end, "Add glue which stretches and shrinks horizontally (good for centering)")
self:registerCommand("vss", function (_, _)
SILE.typesetter:pushExplicitVglue(SILE.types.node.vssglue())
end, "Add glue which stretches and shrinks vertically")
local _thinspacewidth = SILE.types.measurement(0.16667, "em")
self:registerCommand("thinspace", function (_, _)
SILE.call("glue", { width = _thinspacewidth })
end)
self:registerCommand("negthinspace", function (_, _)
SILE.call("glue", { width = -_thinspacewidth })
end)
self:registerCommand("enspace", function (_, _)
SILE.call("glue", { width = SILE.types.measurement(1, "en") })
end)
self:registerCommand("relax", function (_, _) end)
self:registerCommand("enskip", function (_, _)
SILE.call("enspace")
end)
local _quadwidth = SILE.types.measurement(1, "em")
self:registerCommand("quad", function (_, _)
SILE.call("glue", { width = _quadwidth })
end)
self:registerCommand("qquad", function (_, _)
SILE.call("glue", { width = _quadwidth * 2 })
end)
self:registerCommand("slash", function (_, _)
SILE.typesetter:typeset("/")
SILE.call("penalty", { penalty = 50 })
end)
self:registerCommand("break", function (_, _)
SILE.call("penalty", { penalty = -10000 })
end, "Requests a frame break (if in vertical mode) or a line break (if in horizontal mode)")
self:registerCommand("cr", function (_, _)
SILE.call("hfill")
SILE.call("break")
end, "Fills a line with a stretchable glue and then requests a line break")
self:registerCommand("framebreak", function (_, _)
if not SILE.typesetter:vmode() then
SU.warn([[
\\framebreak was not intended to work in horizontal mode
Behavior may change in future versions.
]])
end
SILE.call("penalty", { penalty = -10000, vertical = true })
end, "Requests a frame break (switching to vertical mode if needed)")
self:registerCommand("pagebreak", function (_, _)
if not SILE.typesetter:vmode() then
SU.warn([[
\\pagebreak was not intended to work in horizontal mode
Behavior may change in future versions.
]])
end
SILE.call("penalty", { penalty = -20000, vertical = true })
end, "Requests a non-negotiable page break (switching to vertical mode if needed)")
self:registerCommand("nobreak", function (_, _)
SILE.call("penalty", { penalty = 10000 })
end, "Inhibits a frame break (if in vertical mode) or a line break (if in horizontal mode)")
self:registerCommand("novbreak", function (_, _)
SILE.call("penalty", { penalty = 10000, vertical = true })
end, "Inhibits a frame break (switching to vertical mode if needed)")
self:registerCommand(
"allowbreak",
function (_, _)
SILE.call("penalty", { penalty = 0 })
end,
"Allows a page break (if in vertical mode) or a line break (if in horizontal mode) at a point would not be considered as suitable for breaking"
)
self:registerCommand("filbreak", function (_, _)
SILE.call("vfill")
SILE.call("penalty", { penalty = -200 })
end, "I HAVE THE SAME NAME AS A TEX COMMAND BUT DON'T SEEM TO BE THE SAME")
self:registerCommand("goodbreak", function (_, _)
SILE.call("penalty", { penalty = -500 })
end, "Indicates a good potential point to break a frame (if in vertical mode) or a line (if in horizontal mode")
self:registerCommand("eject", function (_, _)
SILE.call("vfill")
SILE.call("break")
end, "Fills the page with stretchable vglue and then request a page break")
self:registerCommand("supereject", function (_, _)
SILE.call("vfill")
SILE.call("penalty", { penalty = -20000 })
end, "Fills the page with stretchable vglue and then requests a non-negotiable page break")
self:registerCommand("em", function (_, content)
local style = SILE.settings:get("font.style")
local toggle = (style and style:lower() == "italic") and "Regular" or "Italic"
SILE.call("font", { style = toggle }, content)
end, "Emphasizes its contents by switching the font style to italic (or back to regular if already italic)")
self:registerCommand("strong", function (_, content)
SILE.call("font", { weight = 700 }, content)
end, "Sets the font weight to bold (700)")
self:registerCommand("code", function (options, content)
options.family = options.family or "Hack"
options.size = options.size or SILE.settings:get("font.size") - 3
SILE.call("font", options, content)
end)
self:registerCommand("nohyphenation", function (_, content)
SILE.call("font", { language = "und" }, content)
end)
self:registerCommand("center", function (_, content)
if #SILE.typesetter.state.nodes ~= 0 then
SU.warn([[
\\center environment started after other nodes in a paragraph
Content may not be centered as expected.
]])
end
SILE.settings:temporarily(function ()
local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
SILE.settings:set("document.parindent", SILE.types.node.glue())
SILE.settings:set("current.parindent", SILE.types.node.glue())
SILE.settings:set("document.lskip", SILE.types.node.hfillglue(lskip.width.length))
SILE.settings:set("document.rskip", SILE.types.node.hfillglue(rskip.width.length))
SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
SILE.settings:set("document.spaceskip", SILE.types.length("1spc", 0, 0))
SILE.process(content)
SILE.call("par")
end)
end, "Typeset its contents in a centered block (keeping margins).")
self:registerCommand("raggedright", function (_, content)
SILE.settings:temporarily(function ()
local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width.length))
SILE.settings:set("document.rskip", SILE.types.node.hfillglue(rskip.width.length))
SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
SILE.settings:set("document.spaceskip", SILE.types.length("1spc", 0, 0))
SILE.process(content)
SILE.call("par")
end)
end, "Typeset its contents in a left aligned block (keeping margins).")
self:registerCommand("raggedleft", function (_, content)
SILE.settings:temporarily(function ()
local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
SILE.settings:set("document.lskip", SILE.types.node.hfillglue(lskip.width.length))
SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width.length))
SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
SILE.settings:set("document.spaceskip", SILE.types.length("1spc", 0, 0))
SILE.process(content)
SILE.call("par")
end)
end, "Typeset its contents in a right aligned block (keeping margins).")
self:registerCommand("justified", function (_, content)
SILE.settings:temporarily(function ()
local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width.length))
SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width.length))
SILE.settings:set("typesetter.parfillskip", nil, false, true)
SILE.settings:set("document.spaceskip", nil)
SILE.process(content)
SILE.call("par")
end)
end, "Typeset its contents in a justified block (keeping margins).")
self:registerCommand("ragged", function (options, content)
local l = SU.boolean(options.left, false)
local r = SU.boolean(options.right, false)
if l and r then
SILE.call("center", {}, content)
elseif r then
SILE.call("raggedleft", {}, content)
elseif l then
SILE.call("raggedright", {}, content)
else
SILE.call("justified", {}, content)
end
end)
self:registerCommand("rightalign", function (_, content)
SU.deprecated("\\rightalign", "\\raggedleft", "0.15.0", "0.17.0")
SILE.call("raggedleft", {}, content)
end)
self:registerCommand("blockquote", function (_, content)
SILE.call("smallskip")
SILE.typesetter:leaveHmode()
SILE.settings:temporarily(function ()
local indent = SILE.types.measurement("2em"):absolute()
local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width + indent))
SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width + indent))
SILE.settings:set("font.size", SILE.settings:get("font.size") * 0.95)
SILE.process(content)
SILE.typesetter:leaveHmode()
end)
SILE.call("smallskip")
end, "A blockquote environment")
self:registerCommand("quote", function (_, content)
SU.deprecated(
"\\quote",
"\\pullquote or \\blockquote",
"0.14.5",
"0.16.0",
[[
The \quote command has *such* bad output it is being completely deprecated as
unsuitable for general purpose use. The pullquote package
(\use[module=packages.pullquote]) provides one alternative, and the blockquote
environment provides another. But you can also copy and adapt the original
source from the plain class if you need to maintain exact output past
SILE v0.16.0.
]]
)
SILE.call("smallskip")
SILE.call("par")
local margin = SILE.types.measurement(2.5, "em")
SILE.settings:set("document.lskip", margin)
SILE.settings:set("document.lskip", margin)
SILE.call("font", { size = SILE.types.measurement(0.8, "em") }, function ()
SILE.call("noindent")
SILE.process(content)
end)
SILE.call("par")
SILE.settings:set("document.lskip", nil)
SILE.settings:set("document.rskip", nil)
SILE.call("smallskip")
end)
self:registerCommand("listitem", function (_, content)
SU.deprecated(
"\\listitem",
"\\item",
"0.14.6",
"0.16.0",
[[
The new list package (\use[module=packages.lists) has much better typography
for lists. If you want to maintain the exact output of listitem past
SILE v0.16.0 copy the source of \listitem from the plain class into your
project.
]]
)
SILE.call("medskip")
SILE.typesetter:typeset("• ")
SILE.process(content)
SILE.call("medskip")
end)
self:registerCommand("sloppy", function (_, _)
SILE.settings:set("linebreak.tolerance", 9999)
end)
self:registerCommand("awful", function (_, _)
SILE.settings:set("linebreak.tolerance", 10000)
end)
self:registerCommand("hbox", function (_, content)
local hbox, hlist = SILE.typesetter:makeHbox(content)
SILE.typesetter:pushHbox(hbox)
if #hlist > 0 then
SU.warn([[
\\hbox has migrating content
Ignored for now, but likely to break in future versions.
]])
end
return hbox
end, "Compiles all the enclosed horizontal-mode material into a single hbox")
self:registerCommand("vbox", function (options, content)
local vbox
SILE.settings:temporarily(function ()
if options.width then
SILE.settings:set("typesetter.breakwidth", SILE.types.length(options.width))
end
SILE.typesetter:pushState()
SILE.process(content)
SILE.typesetter:leaveHmode(1)
vbox = SILE.pagebuilder:collateVboxes(SILE.typesetter.state.outputQueue)
SILE.typesetter:popState()
end)
return vbox
end, "Compiles all the enclosed material into a single vbox")
end
return class