-
-
Notifications
You must be signed in to change notification settings - Fork 102
/
Copy pathplain.lua
467 lines (405 loc) · 18.3 KB
/
plain.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
--- plain document class.
-- @use classes.plain
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)
-- TODO: set a default direction here?
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")
-- Despite their name, in older versions, \framebreak and \pagebreak worked badly in horizontal
-- mode. The former was a linebreak, and the latter did nothing. That was surely not intended.
-- There are many ways, though to assume what's wrong or what the user's intent ought to be.
-- We now warn, and terminate the paragraph, but to all extents this might be a wrong approach to
-- reconsider at some point.
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"
)
-- THIS SEEMS BROKEN BUT THE COMMAND NOT MENTIONED IN THE SILE MANUAL
-- In TeX, "\filbreak" compensates the vertical fill if no break actually occurs
-- (\def\filbreak{\par\vfil\penalty-200\vfilneg)
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")
-- NOTE: TeX's "\goodbreak" does a \par first, so always switches to vertical mode.
-- SILE differs here, allowing it both within a paragraph (line breaking) and between
-- paragraphs (page breaking).
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)
-- IMPLEMENTATION NOTE:
-- The \code command came from the url package, though used in plenty of
-- places. It was referring to the verbatim:font from the verbatim
-- package, which _should_ be sort of unrelated.
-- Trying to untangle the things here, by introducing the
-- definition from the former, but it's of sub-quality...
-- The ugly -3 size is a HACK of sorts.
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()
-- Keep the fixed part of the margins for nesting but remove the stretchability.
SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width.length))
SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width.length))
-- Reset parfillskip to its default value, in case the surrounding context
-- is ragged and cancelled it.
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)
-- Fairly dubious command for compatibility
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()
-- We keep the stretcheability of the lskip and rskip: honoring text alignment
-- from the parent context.
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.
]])
-- Ugly shim:
-- One day we ought to do SILE.typesetter:pushHlist(hlist) here, so as to push
-- back the migrating contents from within the hbox'ed content.
-- However, old Lua code assumed the hbox to be returned, and sometimes removed it
-- from the typesetter queue (for measuring, etc.), assuming it was the last
-- element in the queue...
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