• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sile-typesetter / sile / 9322841962

31 May 2024 06:41PM UTC coverage: 47.763% (-8.9%) from 56.674%
9322841962

push

github

web-flow
Merge 71aa6f2a1 into bb89a7e79

8081 of 16919 relevant lines covered (47.76%)

246.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.07
/classes/plain.lua
1
--- plain document class.
2
-- @use classes.plain
3

4
local base = require("classes.base")
184✔
5

6
local class = pl.class(base)
184✔
7
class._name = "plain"
184✔
8

9
class.defaultFrameset = {
184✔
10
   content = {
184✔
11
      left = "5%pw",
12
      right = "95%pw",
13
      top = "5%ph",
14
      bottom = "top(footnotes)",
15
   },
184✔
16
   folio = {
184✔
17
      left = "left(content)",
18
      right = "right(content)",
19
      top = "bottom(footnotes)+2%ph",
20
      bottom = "97%ph",
21
   },
184✔
22
   footnotes = {
184✔
23
      left = "left(content)",
24
      right = "right(content)",
25
      height = "0",
26
      bottom = "90%ph",
27
   },
184✔
28
}
184✔
29
class.firstContentFrame = "content"
184✔
30

31
local skips = {
184✔
32
   small = "3pt plus 1pt minus 1pt",
33
   med = "6pt plus 2pt minus 2pt",
34
   big = "12pt plus 4pt minus 4pt",
35
}
36

37
function class:_init (options)
184✔
38
   base._init(self, options)
184✔
39
   self:loadPackage("bidi")
184✔
40
   self:loadPackage("folio")
184✔
41
end
42

43
function class:declareOptions ()
184✔
44
   base.declareOptions(self)
184✔
45
   self:declareOption("direction", function (_, value)
368✔
46
      if value then
6✔
47
         SILE.documentState.direction = value
6✔
48
         SILE.settings:set("font.direction", value, true)
6✔
49
         for _, frame in pairs(self.defaultFrameset) do
24✔
50
            if not frame.direction then
18✔
51
               frame.direction = value
18✔
52
            end
53
         end
54
      end
55
      return SILE.documentState.direction
6✔
56
   end)
57
end
58

59
function class:setOptions (options)
184✔
60
   -- TODO: set a default direction here?
61
   base.setOptions(self, options)
184✔
62
end
63

64
function class:declareSettings ()
184✔
65
   base.declareSettings(self)
184✔
66
   for k, v in pairs(skips) do
736✔
67
      SILE.settings:declare({
1,104✔
68
         parameter = "plain." .. k .. "skipamount",
552✔
69
         type = "vglue",
70
         default = SILE.types.node.vglue(v),
1,104✔
71
         help = "The amount of a \\" .. k .. "skip",
552✔
72
      })
73
   end
74
end
75

76
function class:registerCommands ()
184✔
77
   SILE.classes.base.registerCommands(self)
184✔
78

79
   self:registerCommand("noindent", function (_, content)
368✔
80
      if #SILE.typesetter.state.nodes ~= 0 then
110✔
81
         SU.warn(
2✔
82
            "\\noindent called after nodes already received in a paragraph, the setting will have no effect because the parindent (if any) has already been output"
83
         )
1✔
84
      end
85
      SILE.settings:set("current.parindent", SILE.types.node.glue())
220✔
86
      SILE.process(content)
110✔
87
   end, "Do not add an indent to the start of this paragraph")
294✔
88

89
   self:registerCommand("neverindent", function (_, content)
368✔
90
      SILE.settings:set("current.parindent", SILE.types.node.glue())
126✔
91
      SILE.settings:set("document.parindent", SILE.types.node.glue())
126✔
92
      SILE.process(content)
63✔
93
   end, "Turn off all indentation")
247✔
94

95
   self:registerCommand("indent", function (_, content)
368✔
96
      SILE.settings:set("current.parindent", SILE.settings:get("document.parindent"))
8✔
97
      SILE.process(content)
4✔
98
   end, "Do add an indent to the start of this paragraph, even if previously told otherwise")
5✔
99

100
   for k, _ in pairs(skips) do
20✔
101
      self:registerCommand(k .. "skip", function (_, _)
30✔
102
         SILE.typesetter:leaveHmode()
×
103
         SILE.typesetter:pushExplicitVglue(SILE.settings:get("plain." .. k .. "skipamount"))
×
104
      end, "Skip vertically by a " .. k .. " amount")
30✔
105
   end
106

107
   self:registerCommand("hfill", function (_, _)
10✔
108
      SILE.typesetter:pushExplicitGlue(SILE.types.node.hfillglue())
×
109
   end, "Add a huge horizontal glue")
5✔
110

111
   self:registerCommand("vfill", function (_, _)
10✔
112
      SILE.typesetter:leaveHmode()
10✔
113
      SILE.typesetter:pushExplicitVglue(SILE.types.node.vfillglue())
20✔
114
   end, "Add huge vertical glue")
15✔
115

116
   self:registerCommand("hss", function (_, _)
10✔
117
      SILE.typesetter:initline()
×
118
      SILE.typesetter:pushGlue(SILE.types.node.hssglue())
×
119
      table.insert(SILE.typesetter.state.nodes, SILE.types.node.zerohbox())
×
120
   end, "Add glue which stretches and shrinks horizontally (good for centering)")
5✔
121

122
   self:registerCommand("vss", function (_, _)
10✔
123
      SILE.typesetter:pushExplicitVglue(SILE.types.node.vssglue())
×
124
   end, "Add glue which stretches and shrinks vertically")
5✔
125

126
   local _thinspacewidth = SILE.types.measurement(0.16667, "em")
5✔
127

128
   self:registerCommand("thinspace", function (_, _)
10✔
129
      SILE.call("glue", { width = _thinspacewidth })
×
130
   end)
131

132
   self:registerCommand("negthinspace", function (_, _)
10✔
133
      SILE.call("glue", { width = -_thinspacewidth })
×
134
   end)
135

136
   self:registerCommand("enspace", function (_, _)
10✔
137
      SILE.call("glue", { width = SILE.types.measurement(1, "en") })
×
138
   end)
139

140
   self:registerCommand("relax", function (_, _) end)
5✔
141

142
   self:registerCommand("enskip", function (_, _)
10✔
143
      SILE.call("enspace")
×
144
   end)
145

146
   local _quadwidth = SILE.types.measurement(1, "em")
5✔
147

148
   self:registerCommand("quad", function (_, _)
10✔
149
      SILE.call("glue", { width = _quadwidth })
×
150
   end)
151

152
   self:registerCommand("qquad", function (_, _)
10✔
153
      SILE.call("glue", { width = _quadwidth * 2 })
×
154
   end)
155

156
   self:registerCommand("slash", function (_, _)
10✔
157
      SILE.typesetter:typeset("/")
×
158
      SILE.call("penalty", { penalty = 50 })
×
159
   end)
160

161
   self:registerCommand("break", function (_, _)
10✔
162
      SILE.call("penalty", { penalty = -10000 })
×
163
   end, "Requests a frame break (if in vertical mode) or a line break (if in horizontal mode)")
5✔
164

165
   self:registerCommand("cr", function (_, _)
10✔
166
      SILE.call("hfill")
×
167
      SILE.call("break")
×
168
   end, "Fills a line with a stretchable glue and then requests a line break")
5✔
169

170
   -- Despite their name, in older versions, \framebreak and \pagebreak worked badly in horizontal
171
   -- mode. The former was a linebreak, and the latter did nothing. That was surely not intended.
172
   -- There are many ways, though to assume what's wrong or what the user's intent ought to be.
173
   -- We now warn, and terminate the paragraph, but to all extents this might be a wrong approach to
174
   -- reconsider at some point.
175

176
   self:registerCommand("framebreak", function (_, _)
10✔
177
      if not SILE.typesetter:vmode() then
×
178
         SU.warn("framebreak was not intended to work in horizontal mode. Behaviour may change in future versions")
×
179
      end
180
      SILE.call("penalty", { penalty = -10000, vertical = true })
×
181
   end, "Requests a frame break (switching to vertical mode if needed)")
5✔
182

183
   self:registerCommand("pagebreak", function (_, _)
10✔
184
      if not SILE.typesetter:vmode() then
×
185
         SU.warn("pagebreak was not intended to work in horizontal mode. Behaviour may change in future versions")
×
186
      end
187
      SILE.call("penalty", { penalty = -20000, vertical = true })
×
188
   end, "Requests a non-negotiable page break (switching to vertical mode if needed)")
5✔
189

190
   self:registerCommand("nobreak", function (_, _)
10✔
191
      SILE.call("penalty", { penalty = 10000 })
×
192
   end, "Inhibits a frame break (if in vertical mode) or a line break (if in horizontal mode)")
5✔
193

194
   self:registerCommand("novbreak", function (_, _)
10✔
195
      SILE.call("penalty", { penalty = 10000, vertical = true })
×
196
   end, "Inhibits a frame break (switching to vertical mode if needed)")
5✔
197

198
   self:registerCommand(
10✔
199
      "allowbreak",
5✔
200
      function (_, _)
201
         SILE.call("penalty", { penalty = 0 })
×
202
      end,
203
      "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"
204
   )
5✔
205

206
   -- THIS SEEMS BROKEN BUT THE COMMAND NOT MENTIONED IN THE SILE MANUAL
207
   -- In TeX, "\filbreak" compensates the vertical fill if no break actually occurs
208
   -- (\def\filbreak{\par\vfil\penalty-200\vfilneg)
209
   self:registerCommand("filbreak", function (_, _)
10✔
210
      SILE.call("vfill")
×
211
      SILE.call("penalty", { penalty = -200 })
×
212
   end, "I HAVE THE SAME NAME AS A TEX COMMAND BUT DON'T SEEM TO BE THE SAME")
5✔
213

214
   -- NOTE: TeX's "\goodbreak" does a \par first, so always switches to vertical mode.
215
   -- SILE differs here, allowing it both within a paragraph (line breaking) and between
216
   -- paragraphs (page breaking).
217
   self:registerCommand("goodbreak", function (_, _)
10✔
218
      SILE.call("penalty", { penalty = -500 })
×
219
   end, "Indicates a good potential point to break a frame (if in vertical mode) or a line (if in horizontal mode")
5✔
220

221
   self:registerCommand("eject", function (_, _)
10✔
222
      SILE.call("vfill")
×
223
      SILE.call("break")
×
224
   end, "Fills the page with stretchable vglue and then request a page break")
5✔
225

226
   self:registerCommand("supereject", function (_, _)
10✔
227
      SILE.call("vfill")
5✔
228
      SILE.call("penalty", { penalty = -20000 })
5✔
229
   end, "Fills the page with stretchable vglue and then requests a non-negotiable page break")
10✔
230

231
   self:registerCommand("em", function (_, content)
10✔
232
      local style = SILE.settings:get("font.style")
5✔
233
      local toggle = (style and style:lower() == "italic") and "Regular" or "Italic"
10✔
234
      SILE.call("font", { style = toggle }, content)
5✔
235
   end, "Emphasizes its contents by switching the font style to italic (or back to regular if already italic)")
10✔
236

237
   self:registerCommand("strong", function (_, content)
10✔
238
      SILE.call("font", { weight = 700 }, content)
×
239
   end, "Sets the font weight to bold (700)")
5✔
240

241
   self:registerCommand("code", function (options, content)
10✔
242
      -- IMPLEMENTATION NOTE:
243
      -- The \code command came from the url package, though used in plenty of
244
      -- places. It was referring to the verbatim:font from the verbatim
245
      -- package, which _should_ be sort of unrelated.
246
      -- Trying to untangle the things here, by introducing the
247
      -- definition from the former, but it's of sub-quality...
248
      -- The ugly -3 size is a HACK of sorts.
249
      options.family = options.family or "Hack"
×
250
      options.size = options.size or SILE.settings:get("font.size") - 3
×
251
      SILE.call("font", options, content)
×
252
   end)
253

254
   self:registerCommand("nohyphenation", function (_, content)
10✔
255
      SILE.call("font", { language = "und" }, content)
×
256
   end)
257

258
   self:registerCommand("center", function (_, content)
10✔
259
      if #SILE.typesetter.state.nodes ~= 0 then
3✔
260
         SU.warn("\\center environment started after other nodes in a paragraph, may not center as expected")
×
261
      end
262
      SILE.settings:temporarily(function ()
6✔
263
         local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
6✔
264
         local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
6✔
265
         SILE.settings:set("document.parindent", SILE.types.node.glue())
6✔
266
         SILE.settings:set("current.parindent", SILE.types.node.glue())
6✔
267
         SILE.settings:set("document.lskip", SILE.types.node.hfillglue(lskip.width.length))
6✔
268
         SILE.settings:set("document.rskip", SILE.types.node.hfillglue(rskip.width.length))
6✔
269
         SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
6✔
270
         SILE.settings:set("document.spaceskip", SILE.types.length("1spc", 0, 0))
6✔
271
         SILE.process(content)
3✔
272
         SILE.call("par")
3✔
273
      end)
274
   end, "Typeset its contents in a centered block (keeping margins).")
8✔
275

276
   self:registerCommand("raggedright", function (_, content)
10✔
277
      SILE.settings:temporarily(function ()
2✔
278
         local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
2✔
279
         local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
2✔
280
         SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width.length))
2✔
281
         SILE.settings:set("document.rskip", SILE.types.node.hfillglue(rskip.width.length))
2✔
282
         SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
2✔
283
         SILE.settings:set("document.spaceskip", SILE.types.length("1spc", 0, 0))
2✔
284
         SILE.process(content)
1✔
285
         SILE.call("par")
1✔
286
      end)
287
   end, "Typeset its contents in a left aligned block (keeping margins).")
6✔
288

289
   self:registerCommand("raggedleft", function (_, content)
10✔
290
      SILE.settings:temporarily(function ()
4✔
291
         local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
4✔
292
         local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
4✔
293
         SILE.settings:set("document.lskip", SILE.types.node.hfillglue(lskip.width.length))
4✔
294
         SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width.length))
4✔
295
         SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
4✔
296
         SILE.settings:set("document.spaceskip", SILE.types.length("1spc", 0, 0))
4✔
297
         SILE.process(content)
2✔
298
         SILE.call("par")
2✔
299
      end)
300
   end, "Typeset its contents in a right aligned block (keeping margins).")
7✔
301

302
   self:registerCommand("justified", function (_, content)
10✔
303
      SILE.settings:temporarily(function ()
2✔
304
         local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
2✔
305
         local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
2✔
306
         -- Keep the fixed part of the margins for nesting but remove the stretchability.
307
         SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width.length))
2✔
308
         SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width.length))
2✔
309
         -- Reset parfillskip to its default value, in case the surrounding context
310
         -- is ragged and cancelled it.
311
         SILE.settings:set("typesetter.parfillskip", nil, false, true)
1✔
312
         SILE.settings:set("document.spaceskip", nil)
1✔
313
         SILE.process(content)
1✔
314
         SILE.call("par")
1✔
315
      end)
316
   end, "Typeset its contents in a justified block (keeping margins).")
6✔
317

318
   self:registerCommand("ragged", function (options, content)
10✔
319
      -- Fairly dubious command for compatibility
320
      local l = SU.boolean(options.left, false)
×
321
      local r = SU.boolean(options.right, false)
×
322
      if l and r then
×
323
         SILE.call("center", {}, content)
×
324
      elseif r then
×
325
         SILE.call("raggedleft", {}, content)
×
326
      elseif l then
×
327
         SILE.call("raggedright", {}, content)
×
328
      else
329
         SILE.call("justified", {}, content)
×
330
      end
331
   end)
332

333
   self:registerCommand("rightalign", function (_, content)
10✔
334
      SU.deprecated("\\rightalign", "\\raggedleft", "0.15.0", "0.17.0")
1✔
335
      SILE.call("raggedleft", {}, content)
1✔
336
   end)
337

338
   self:registerCommand("blockquote", function (_, content)
10✔
339
      SILE.call("smallskip")
×
340
      SILE.typesetter:leaveHmode()
×
341
      SILE.settings:temporarily(function ()
×
342
         local indent = SILE.types.measurement("2em"):absolute()
×
343
         local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
×
344
         local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
×
345
         -- We keep the stretcheability of the lskip and rskip: honoring text alignment
346
         -- from the parent context.
347
         SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width + indent))
×
348
         SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width + indent))
×
349
         SILE.settings:set("font.size", SILE.settings:get("font.size") * 0.95)
×
350
         SILE.process(content)
×
351
         SILE.typesetter:leaveHmode()
×
352
      end)
353
      SILE.call("smallskip")
×
354
   end, "A blockquote environment")
5✔
355

356
   self:registerCommand("quote", function (_, content)
10✔
357
      SU.deprecated(
×
358
         "\\quote",
359
         "\\pullquote or \\blockquote",
360
         "0.14.5",
361
         "0.16.0",
362
         [[
×
363
  The \quote command has *such* bad output it is being completely
364
  deprecated as unsuitable for general purpose use.
365
  The pullquote package (\use[module=packages.pullquote]) provides one
366
  alternative, and the blockquote environment provides another.
367
  But you can also copy and adapt the original source from the plain
368
  class if you need to maintain exact output past SILE v0.16.0.]]
×
369
      )
370
      SILE.call("smallskip")
×
371
      SILE.call("par")
×
372
      local margin = SILE.types.measurement(2.5, "em")
×
373
      SILE.settings:set("document.lskip", margin)
×
374
      SILE.settings:set("document.lskip", margin)
×
375
      SILE.call("font", { size = SILE.types.measurement(0.8, "em") }, function ()
×
376
         SILE.call("noindent")
×
377
         SILE.process(content)
×
378
      end)
379
      SILE.call("par")
×
380
      SILE.settings:set("document.lskip", nil)
×
381
      SILE.settings:set("document.rskip", nil)
×
382
      SILE.call("smallskip")
×
383
   end)
384

385
   self:registerCommand("listitem", function (_, content)
10✔
386
      SU.deprecated(
×
387
         "\\listitem",
388
         "\\item",
389
         "0.14.6",
390
         "0.16.0",
391
         [[
×
392
  The new list package (\use[module=packages.lists) has much better
393
  typography for lists. If you want to maintain the exact output of listitem
394
  past SILE v0.16.0 copy the source of \listitem from the plain class into
395
  your project.]]
×
396
      )
397
      SILE.call("medskip")
×
398
      SILE.typesetter:typeset("• ")
×
399
      SILE.process(content)
×
400
      SILE.call("medskip")
×
401
   end)
402

403
   self:registerCommand("sloppy", function (_, _)
10✔
404
      SILE.settings:set("linebreak.tolerance", 9999)
×
405
   end)
406

407
   self:registerCommand("awful", function (_, _)
10✔
408
      SILE.settings:set("linebreak.tolerance", 10000)
×
409
   end)
410

411
   self:registerCommand("hbox", function (_, content)
10✔
412
      local hbox, hlist = SILE.typesetter:makeHbox(content)
×
413
      SILE.typesetter:pushHbox(hbox)
×
414
      if #hlist > 0 then
×
415
         SU.warn("Hbox has migrating content (ignored for now, but likely to break in future versions)")
×
416
         -- Ugly shim:
417
         -- One day we ought to do SILE.typesetter:pushHlist(hlist) here, so as to push
418
         -- back the migrating contents from within the hbox'ed content.
419
         -- However, old Lua code assumed the hbox to be returned, and sometimes removed it
420
         -- from the typesetter queue (for measuring, etc.), assuming it was the last
421
         -- element in the queue...
422
      end
423
      return hbox
×
424
   end, "Compiles all the enclosed horizontal-mode material into a single hbox")
5✔
425

426
   self:registerCommand("vbox", function (options, content)
10✔
427
      local vbox
428
      SILE.settings:temporarily(function ()
×
429
         if options.width then
×
430
            SILE.settings:set("typesetter.breakwidth", SILE.types.length(options.width))
×
431
         end
432
         SILE.typesetter:pushState()
×
433
         SILE.process(content)
×
434
         SILE.typesetter:leaveHmode(1)
×
435
         vbox = SILE.pagebuilder:collateVboxes(SILE.typesetter.state.outputQueue)
×
436
         SILE.typesetter:popState()
×
437
      end)
438
      return vbox
×
439
   end, "Compiles all the enclosed material into a single vbox")
5✔
440
end
441

442
return class
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc