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

sile-typesetter / sile / 9304060604

30 May 2024 02:07PM UTC coverage: 74.124% (-0.6%) from 74.707%
9304060604

push

github

alerque
style: Reformat Lua with stylua

8104 of 11995 new or added lines in 184 files covered. (67.56%)

15 existing lines in 11 files now uncovered.

12444 of 16788 relevant lines covered (74.12%)

7175.1 hits per line

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

71.29
/core/nodefactory.lua
1
local nodefactory = {}
181✔
2

3
-- This infinity needs to be smaller than an actual infinity but bigger than the infinite stretch
4
-- added by the typesetter. See https://github.com/sile-typesetter/sile/issues/227
5
local infinity = SILE.measurement(1e13)
181✔
6

7
local function _maxnode (nodes, dim)
8
   local dims = SU.map(function (node)
89,322✔
9
      -- TODO there is a bug here because we shouldn't need to cast to lengths,
10
      -- somebody is setting a height as a number value (test in Lua 5.1)
11
      -- return node[dim]
12
      return SU.cast("length", node[dim])
101,994✔
13
   end, nodes)
44,661✔
14
   return SU.max(SILE.length(0), pl.utils.unpack(dims))
133,983✔
15
end
16

17
local _dims = pl.Set({ "width", "height", "depth" })
362✔
18

19
nodefactory.box = pl.class()
362✔
20
nodefactory.box.type = "special"
181✔
21

22
nodefactory.box.height = nil
181✔
23
nodefactory.box.depth = nil
181✔
24
nodefactory.box.width = nil
181✔
25
nodefactory.box.misfit = false
181✔
26
nodefactory.box.explicit = false
181✔
27
nodefactory.box.discardable = false
181✔
28
nodefactory.box.value = nil
181✔
29
nodefactory.box._default_length = "width"
181✔
30

31
function nodefactory.box:_init (spec)
362✔
32
   if
33
      type(spec) == "string"
68,751✔
34
      or type(spec) == "number"
67,044✔
35
      or SU.type(spec) == "measurement"
129,382✔
36
      or SU.type(spec) == "length"
129,364✔
37
   then
38
      self[self._default_length] = SU.cast("length", spec)
23,944✔
39
   elseif SU.type(spec) == "table" then
113,558✔
40
      if spec._tospec then
47,693✔
NEW
41
         spec = spec:_tospec()
×
42
      end
43
      for k, v in pairs(spec) do
252,809✔
44
         self[k] = _dims[k] and SU.cast("length", v) or v
265,509✔
45
      end
46
   elseif type(spec) ~= "nil" and SU.type(spec) ~= self.type then
9,086✔
NEW
47
      SU.error("Unimplemented, creating " .. self.type .. " node from " .. SU.type(spec), 1)
×
48
   end
49
   for dim in pairs(_dims) do
275,004✔
50
      if not self[dim] then
211,278✔
51
         self[dim] = SILE.length()
267,776✔
52
      end
53
   end
54
   self["is_" .. self.type] = true
68,751✔
55
   self.is_box = self.is_hbox or self.is_vbox or self.is_zerohbox or self.is_alternative or self.is_nnode
77,126✔
56
   self.is_zero = self.is_zerohbox or self.is_zerovglue
72,101✔
57
   if self.is_migrating then
70,426✔
58
      self.is_hbox, self.is_box = true, true
86✔
59
   end
60
end
61

62
-- De-init instances by shallow copying properties and removing meta table
63
function nodefactory.box:_tospec ()
362✔
64
   return pl.tablex.copy(self)
966✔
65
end
66

67
function nodefactory.box:tostring ()
362✔
NEW
68
   return self:__tostring()
×
69
end
70

71
function nodefactory.box:__tostring ()
362✔
NEW
72
   return self.type
×
73
end
74

75
function nodefactory.box.__concat (a, b)
362✔
NEW
76
   return tostring(a) .. tostring(b)
×
77
end
78

79
function nodefactory.box:absolute ()
362✔
80
   local clone = nodefactory[self.type](self:_tospec())
1,932✔
81
   for dim in pairs(_dims) do
3,864✔
82
      clone[dim] = self[dim]:absolute()
5,796✔
83
   end
84
   if self.nodes then
966✔
85
      clone.nodes = pl.tablex.map_named_method("absolute", self.nodes)
10✔
86
   end
87
   return clone
966✔
88
end
89

90
function nodefactory.box:lineContribution ()
362✔
91
   -- Regardless of the orientations, "width" is always in the
92
   -- writingDirection, and "height" is always in the "pageDirection"
93
   return self.misfit and self.height or self.width
48,381✔
94
end
95

96
function nodefactory.box:outputYourself ()
362✔
NEW
97
   SU.error(self.type .. " with no output routine")
×
98
end
99

100
function nodefactory.box:toText ()
362✔
NEW
101
   return self.type
×
102
end
103

104
function nodefactory.box:isBox ()
362✔
NEW
105
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
NEW
106
   return self.type == "hbox"
×
NEW
107
      or self.type == "zerohbox"
×
NEW
108
      or self.type == "alternative"
×
NEW
109
      or self.type == "nnode"
×
NEW
110
      or self.type == "vbox"
×
111
end
112

113
function nodefactory.box:isNnode ()
362✔
NEW
114
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
NEW
115
   return self.type == "nnode"
×
116
end
117

118
function nodefactory.box:isGlue ()
362✔
NEW
119
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
NEW
120
   return self.type == "glue"
×
121
end
122

123
function nodefactory.box:isVglue ()
362✔
NEW
124
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
NEW
125
   return self.type == "vglue"
×
126
end
127

128
function nodefactory.box:isZero ()
362✔
NEW
129
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
NEW
130
   return self.type == "zerohbox" or self.type == "zerovglue"
×
131
end
132

133
function nodefactory.box:isUnshaped ()
362✔
NEW
134
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
NEW
135
   return self.type == "unshaped"
×
136
end
137

138
function nodefactory.box:isAlternative ()
362✔
NEW
139
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
NEW
140
   return self.type == "alternative"
×
141
end
142

143
function nodefactory.box:isVbox ()
362✔
NEW
144
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
NEW
145
   return self.type == "vbox"
×
146
end
147

148
function nodefactory.box:isInsertion ()
362✔
NEW
149
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
NEW
150
   return self.type == "insertion"
×
151
end
152

153
function nodefactory.box:isMigrating ()
362✔
NEW
154
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
NEW
155
   return self.migrating
×
156
end
157

158
function nodefactory.box:isPenalty ()
362✔
NEW
159
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
NEW
160
   return self.type == "penalty"
×
161
end
162

163
function nodefactory.box:isDiscretionary ()
362✔
NEW
164
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
NEW
165
   return self.type == "discretionary"
×
166
end
167

168
function nodefactory.box:isKern ()
362✔
NEW
169
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
NEW
170
   return self.type == "kern"
×
171
end
172

173
nodefactory.hbox = pl.class(nodefactory.box)
362✔
174
nodefactory.hbox.type = "hbox"
181✔
175

176
function nodefactory.hbox:_init (spec)
362✔
177
   nodefactory.box._init(self, spec)
50,162✔
178
end
179

180
function nodefactory.hbox:__tostring ()
362✔
NEW
181
   return "H<" .. tostring(self.width) .. ">^" .. tostring(self.height) .. "-" .. tostring(self.depth) .. "v"
×
182
end
183

184
function nodefactory.hbox:scaledWidth (line)
362✔
185
   return SU.rationWidth(self:lineContribution(), self.width, line.ratio)
28,538✔
186
end
187

188
function nodefactory.hbox:outputYourself (typesetter, line)
362✔
189
   local outputWidth = self:scaledWidth(line)
13,664✔
190
   if not self.value.glyphString then
13,664✔
191
      return
3,776✔
192
   end
193
   if typesetter.frame:writingDirection() == "RTL" then
19,776✔
194
      typesetter.frame:advanceWritingDirection(outputWidth)
291✔
195
   end
196
   SILE.outputter:setCursor(typesetter.frame.state.cursorX, typesetter.frame.state.cursorY)
9,888✔
197
   SILE.outputter:setFont(self.value.options)
9,888✔
198
   SILE.outputter:drawHbox(self.value, outputWidth)
9,888✔
199
   if typesetter.frame:writingDirection() ~= "RTL" then
19,776✔
200
      typesetter.frame:advanceWritingDirection(outputWidth)
9,597✔
201
   end
202
end
203

204
nodefactory.zerohbox = pl.class(nodefactory.hbox)
362✔
205
nodefactory.zerohbox.type = "zerohbox"
181✔
206
nodefactory.zerohbox.value = { glyph = 0 }
181✔
207

208
nodefactory.nnode = pl.class(nodefactory.hbox)
362✔
209
nodefactory.nnode.type = "nnode"
181✔
210
nodefactory.nnode.language = ""
181✔
211
nodefactory.nnode.pal = nil
181✔
212
nodefactory.nnode.nodes = {}
181✔
213

214
function nodefactory.nnode:_init (spec)
362✔
215
   self:super(spec)
20,341✔
216
   if 0 == self.depth:tonumber() then
40,682✔
217
      self.depth = _maxnode(self.nodes, "depth")
40,682✔
218
   end
219
   if 0 == self.height:tonumber() then
40,682✔
220
      self.height = _maxnode(self.nodes, "height")
40,682✔
221
   end
222
   if 0 == self.width:tonumber() then
40,682✔
223
      self.width = SU.sum(SU.map(function (node)
61,023✔
224
         return node.width
18,663✔
225
      end, self.nodes))
40,682✔
226
   end
227
end
228

229
function nodefactory.nnode:__tostring ()
362✔
NEW
230
   return "N<"
×
NEW
231
      .. tostring(self.width)
×
NEW
232
      .. ">^"
×
NEW
233
      .. tostring(self.height)
×
NEW
234
      .. "-"
×
NEW
235
      .. tostring(self.depth)
×
NEW
236
      .. "v("
×
NEW
237
      .. self:toText()
×
NEW
238
      .. ")"
×
239
end
240

241
function nodefactory.nnode:absolute ()
362✔
242
   return self
2✔
243
end
244

245
function nodefactory.nnode:outputYourself (typesetter, line)
362✔
246
   -- See typesetter:computeLineRatio() which implements the currently rather messy
247
   -- and probably slightly dubious 'hyphenated' logic.
248
   -- Example: consider the word "out-put".
249
   -- The node queue therefore contains N(out)D(-)N(put) all pointing to the same
250
   -- parent N(output).
251
   if self.parent and not self.parent.hyphenated then
14,042✔
252
      -- When we hit N(out) and are not hyphenated, we output N(output) directly
253
      -- and mark it as used, so as to skip D(-)N(put) afterwards.
254
      -- I guess this was done to ensure proper kerning (vs. outputting each of
255
      -- the nodes separately).
256
      if not self.parent.used then
4,154✔
257
         self.parent:outputYourself(typesetter, line)
1,620✔
258
      end
259
      self.parent.used = true
4,154✔
260
   else
261
      -- It's possible not to have a parent, e.g. N(word) without hyphenation points.
262
      -- Either that, or we have a hyphenated parent but are in the case we are
263
      -- outputting one of the elements e.g. N(out)D(-) [line break] N(put).
264
      -- (ignoring the G(margin) nodes and potentially zerohbox nodes also on either side of the line break)
265
      for _, node in ipairs(self.nodes) do
19,776✔
266
         node:outputYourself(typesetter, line)
9,888✔
267
      end
268
   end
269
end
270

271
function nodefactory.nnode:toText ()
362✔
272
   return self.text
2✔
273
end
274

275
nodefactory.unshaped = pl.class(nodefactory.nnode)
362✔
276
nodefactory.unshaped.type = "unshaped"
181✔
277

278
function nodefactory.unshaped:_init (spec)
362✔
279
   self:super(spec)
1,678✔
280
   self.width = nil
1,678✔
281
end
282

283
function nodefactory.unshaped:__tostring ()
362✔
NEW
284
   return "U(" .. self:toText() .. ")"
×
285
end
286

287
getmetatable(nodefactory.unshaped).__index = function (_, _)
181✔
288
   -- if k == "width" then SU.error("Can't get width of unshaped node", true) end
289
   -- TODO: No idea why porting to proper Penlight classes this ^^^^^^ started
290
   -- killing everything. Perhaps because this function started working and would
291
   -- actually need to return rawget(self, k) or something?
292
end
293

294
function nodefactory.unshaped:shape ()
362✔
295
   local node = SILE.shaper:createNnodes(self.text, self.options)
1,632✔
296
   for i = 1, #node do
17,501✔
297
      node[i].parent = self.parent
31,710✔
298
   end
299
   return node
1,632✔
300
end
301

302
function nodefactory.unshaped.outputYourself (_)
362✔
NEW
303
   SU.error("An unshaped node made it to output", true)
×
304
end
305

306
nodefactory.discretionary = pl.class(nodefactory.hbox)
362✔
307

308
nodefactory.discretionary.type = "discretionary"
181✔
309
nodefactory.discretionary.prebreak = {}
181✔
310
nodefactory.discretionary.postbreak = {}
181✔
311
nodefactory.discretionary.replacement = {}
181✔
312
nodefactory.discretionary.used = false
181✔
313

314
function nodefactory.discretionary:__tostring ()
362✔
NEW
315
   return "D("
×
NEW
316
      .. SU.concat(self.prebreak, "")
×
NEW
317
      .. "|"
×
NEW
318
      .. SU.concat(self.postbreak, "")
×
NEW
319
      .. "|"
×
NEW
320
      .. SU.concat(self.replacement, "")
×
NEW
321
      .. ")"
×
322
end
323

324
function nodefactory.discretionary:toText ()
362✔
NEW
325
   return self.used and "-" or "_"
×
326
end
327

328
function nodefactory.discretionary:outputYourself (typesetter, line)
362✔
329
   -- See typesetter:computeLineRatio() which implements the currently rather
330
   -- messy hyphenated checks.
331
   -- Example: consider the word "out-put-ter".
332
   -- The node queue contains N(out)D(-)N(put)D(-)N(ter) all pointing to the same
333
   -- parent N(output), and here we hit D(-)
334

335
   -- Non-hyphenated parent: when N(out) was hit, we went for outputting
336
   -- the whole parent, so all other elements must now be skipped.
337
   if self.parent and not self.parent.hyphenated then
3,147✔
338
      return
2,448✔
339
   end
340

341
   -- It's possible not to have a parent (e.g. on a discretionary directly
342
   -- added in the queue and not coming from the hyphenator logic).
343
   -- Eiher that, or we have a hyphenate parent.
344
   if self.used then
699✔
345
      -- This is the actual hyphenation point.
346
      -- Skip margin glue and zero boxes.
347
      -- If we then reach our discretionary, it means its the first in the line,
348
      -- i.e. a postbreak. Otherwise, its a prebreak (near the end of the line,
349
      -- notwithstanding glues etc.)
350
      local i = 1
365✔
351
      while (line.nodes[i].is_glue and line.nodes[i].value == "margin") or line.nodes[i].type == "zerohbox" do
1,159✔
352
         i = i + 1
794✔
353
      end
354
      if line.nodes[i] == self then
365✔
355
         for _, node in ipairs(self.postbreak) do
187✔
356
            node:outputYourself(typesetter, line)
5✔
357
         end
358
      else
359
         for _, node in ipairs(self.prebreak) do
367✔
360
            node:outputYourself(typesetter, line)
184✔
361
         end
362
      end
363
   else
364
      -- This is not the hyphenation point (but another discretionary in the queue)
365
      -- E.g. we were in the case where we have N(out)D(-) [line break] N(out)D(-)N(ter)
366
      -- and now hit the second D(-).
367
      -- Unused discretionaries are obviously replaced.
368
      for _, node in ipairs(self.replacement) do
388✔
369
         node:outputYourself(typesetter, line)
54✔
370
      end
371
   end
372
end
373

374
function nodefactory.discretionary:prebreakWidth ()
362✔
375
   if self.prebw then
7,253✔
376
      return self.prebw
4,190✔
377
   end
378
   self.prebw = SILE.length()
6,126✔
379
   for _, node in ipairs(self.prebreak) do
6,130✔
380
      self.prebw:___add(node.width)
3,067✔
381
   end
382
   return self.prebw
3,063✔
383
end
384

385
function nodefactory.discretionary:postbreakWidth ()
362✔
386
   if self.postbw then
599✔
387
      return self.postbw
199✔
388
   end
389
   self.postbw = SILE.length()
800✔
390
   for _, node in ipairs(self.postbreak) do
405✔
391
      self.postbw:___add(node.width)
5✔
392
   end
393
   return self.postbw
400✔
394
end
395

396
function nodefactory.discretionary:replacementWidth ()
362✔
397
   if self.replacew then
4,075✔
398
      return self.replacew
1,011✔
399
   end
400
   self.replacew = SILE.length()
6,128✔
401
   for _, node in ipairs(self.replacement) do
3,148✔
402
      self.replacew:___add(node.width)
84✔
403
   end
404
   return self.replacew
3,064✔
405
end
406

407
function nodefactory.discretionary:prebreakHeight ()
362✔
408
   if self.prebh then
191✔
NEW
409
      return self.prebh
×
410
   end
411
   self.prebh = _maxnode(self.prebreak, "height")
382✔
412
   return self.prebh
191✔
413
end
414

415
function nodefactory.discretionary:postbreakHeight ()
362✔
416
   if self.postbh then
191✔
NEW
417
      return self.postbh
×
418
   end
419
   self.postbh = _maxnode(self.postbreak, "height")
382✔
420
   return self.postbh
191✔
421
end
422

423
function nodefactory.discretionary:replacementHeight ()
362✔
424
   if self.replaceh then
340✔
NEW
425
      return self.replaceh
×
426
   end
427
   self.replaceh = _maxnode(self.replacement, "height")
680✔
428
   return self.replaceh
340✔
429
end
430

431
function nodefactory.discretionary:replacementDepth ()
362✔
432
   if self.replaced then
1✔
NEW
433
      return self.replaced
×
434
   end
435
   self.replaced = _maxnode(self.replacement, "depth")
2✔
436
   return self.replaced
1✔
437
end
438

439
nodefactory.alternative = pl.class(nodefactory.hbox)
362✔
440

441
nodefactory.alternative.type = "alternative"
181✔
442
nodefactory.alternative.options = {}
181✔
443
nodefactory.alternative.selected = nil
181✔
444

445
function nodefactory.alternative:__tostring ()
362✔
NEW
446
   return "A(" .. SU.concat(self.options, " / ") .. ")"
×
447
end
448

449
function nodefactory.alternative:minWidth ()
362✔
450
   local minW = function (a, b)
NEW
451
      return SU.min(a.width, b.width)
×
452
   end
NEW
453
   return pl.tablex.reduce(minW, self.options)
×
454
end
455

456
function nodefactory.alternative:deltas ()
362✔
NEW
457
   local minWidth = self:minWidth()
×
NEW
458
   local rv = {}
×
NEW
459
   for i = 1, #self.options do
×
NEW
460
      rv[#rv + 1] = self.options[i].width - minWidth
×
461
   end
NEW
462
   return rv
×
463
end
464

465
function nodefactory.alternative:outputYourself (typesetter, line)
362✔
NEW
466
   if self.selected then
×
NEW
467
      self.options[self.selected]:outputYourself(typesetter, line)
×
468
   end
469
end
470

471
nodefactory.glue = pl.class(nodefactory.box)
362✔
472
nodefactory.glue.type = "glue"
181✔
473
nodefactory.glue.discardable = true
181✔
474

475
function nodefactory.glue:__tostring ()
362✔
NEW
476
   return (self.explicit and "E:" or "") .. "G<" .. tostring(self.width) .. ">"
×
477
end
478

479
function nodefactory.glue.toText (_)
362✔
NEW
480
   return " "
×
481
end
482

483
function nodefactory.glue:outputYourself (typesetter, line)
362✔
484
   local outputWidth = SU.rationWidth(self.width:absolute(), self.width:absolute(), line.ratio)
35,640✔
485
   typesetter.frame:advanceWritingDirection(outputWidth)
11,880✔
486
end
487

488
-- A hfillglue is just a glue with infinite stretch.
489
-- (Convenience so callers do not have to know what infinity is.)
490
nodefactory.hfillglue = pl.class(nodefactory.glue)
362✔
491
function nodefactory.hfillglue:_init (spec)
362✔
492
   self:super(spec)
345✔
493
   self.width = SILE.length(self.width.length, infinity, self.width.shrink)
690✔
494
end
495

496
-- A hssglue is just a glue with infinite stretch and shrink.
497
-- (Convenience so callers do not have to know what infinity is.)
498
nodefactory.hssglue = pl.class(nodefactory.glue)
362✔
499
function nodefactory.hssglue:_init (spec)
362✔
NEW
500
   self:super(spec)
×
NEW
501
   self.width = SILE.length(self.width.length, infinity, infinity)
×
502
end
503

504
nodefactory.kern = pl.class(nodefactory.glue)
362✔
505
nodefactory.kern.type = "kern" -- Perhaps some smell here, see comment on vkern
181✔
506
nodefactory.kern.discardable = false
181✔
507

508
function nodefactory.kern:__tostring ()
362✔
NEW
509
   return "K<" .. tostring(self.width) .. ">"
×
510
end
511

512
nodefactory.vglue = pl.class(nodefactory.box)
362✔
513
nodefactory.vglue.type = "vglue"
181✔
514
nodefactory.vglue.discardable = true
181✔
515
nodefactory.vglue._default_length = "height"
181✔
516
nodefactory.vglue.adjustment = nil
181✔
517

518
function nodefactory.vglue:_init (spec)
362✔
519
   self.adjustment = SILE.measurement()
6,348✔
520
   self:super(spec)
3,174✔
521
end
522

523
function nodefactory.vglue:__tostring ()
362✔
NEW
524
   return (self.explicit and "E:" or "") .. "VG<" .. tostring(self.height) .. ">"
×
525
end
526

527
function nodefactory.vglue:adjustGlue (adjustment)
362✔
528
   self.adjustment = adjustment
2,037✔
529
end
530

531
function nodefactory.vglue:outputYourself (typesetter, line)
362✔
532
   typesetter.frame:advancePageDirection(line.height:absolute() + line.depth:absolute() + self.adjustment)
12,035✔
533
end
534

535
function nodefactory.vglue:unbox ()
362✔
536
   return { self }
42✔
537
end
538

539
-- A vfillglue is just a vglue with infinite stretch.
540
-- (Convenience so callers do not have to know what infinity is.)
541
nodefactory.vfillglue = pl.class(nodefactory.vglue)
362✔
542
function nodefactory.vfillglue:_init (spec)
362✔
543
   self:super(spec)
379✔
544
   self.height = SILE.length(self.width.length, infinity, self.width.shrink)
758✔
545
end
546

547
-- A vssglue is just a vglue with infinite stretch and shrink.
548
-- (Convenience so callers do not have to know what infinity is.)
549
nodefactory.vssglue = pl.class(nodefactory.vglue)
362✔
550
function nodefactory.vssglue:_init (spec)
362✔
NEW
551
   self:super(spec)
×
NEW
552
   self.height = SILE.length(self.width.length, infinity, infinity)
×
553
end
554

555
nodefactory.zerovglue = pl.class(nodefactory.vglue)
362✔
556

557
nodefactory.vkern = pl.class(nodefactory.vglue)
362✔
558
-- FIXME TODO
559
-- Here we cannot do:
560
--   nodefactory.vkern.type = "vkern"
561
-- It cannot be typed as "vkern" as the pagebuilder doesn't check is_vkern.
562
-- So it's just a vglue currrenty, marked as not discardable...
563
-- But on the other hand, nodefactory.kern is typed "kern" and is not a glue...
564
-- Frankly, the discardable/explicit flags and the types are too
565
-- entangled and point towards a more general design issue.
566
-- N.B. this vkern node is only used in the linespacing package so far.
567
nodefactory.vkern.discardable = false
181✔
568

569
function nodefactory.vkern:__tostring ()
362✔
NEW
570
   return "VK<" .. tostring(self.height) .. ">"
×
571
end
572

573
nodefactory.penalty = pl.class(nodefactory.box)
362✔
574
nodefactory.penalty.type = "penalty"
181✔
575
nodefactory.penalty.discardable = true
181✔
576
nodefactory.penalty.penalty = 0
181✔
577

578
function nodefactory.penalty:_init (spec)
362✔
579
   self:super(spec)
2,455✔
580
   if type(spec) ~= "table" then
2,455✔
581
      self.penalty = SU.cast("number", spec)
4,150✔
582
   end
583
end
584

585
function nodefactory.penalty:__tostring ()
362✔
NEW
586
   return "P(" .. tostring(self.penalty) .. ")"
×
587
end
588

589
function nodefactory.penalty.outputYourself (_) end
1,523✔
590

591
function nodefactory.penalty.toText (_)
362✔
NEW
592
   return "(!)"
×
593
end
594

595
function nodefactory.penalty:unbox ()
362✔
NEW
596
   return { self }
×
597
end
598

599
nodefactory.vbox = pl.class(nodefactory.box)
362✔
600
nodefactory.vbox.type = "vbox"
181✔
601
nodefactory.vbox.nodes = {}
181✔
602
nodefactory.vbox._default_length = "height"
181✔
603

604
function nodefactory.vbox:_init (spec)
362✔
605
   self.nodes = {}
1,628✔
606
   self:super(spec)
1,628✔
607
   self.depth = _maxnode(self.nodes, "depth")
3,256✔
608
   self.height = _maxnode(self.nodes, "height")
3,256✔
609
end
610

611
function nodefactory.vbox:__tostring ()
362✔
NEW
612
   return "VB<" .. tostring(self.height) .. "|" .. self:toText() .. "v" .. tostring(self.depth) .. ")"
×
613
end
614

615
function nodefactory.vbox:toText ()
362✔
NEW
616
   return "VB["
×
NEW
617
      .. SU.concat(
×
NEW
618
         SU.map(function (node)
×
NEW
619
            return node:toText()
×
NEW
620
         end, self.nodes),
×
621
         ""
622
      )
NEW
623
      .. "]"
×
624
end
625

626
function nodefactory.vbox:outputYourself (typesetter, line)
362✔
627
   typesetter.frame:advancePageDirection(self.height)
1,455✔
628
   local initial = true
1,455✔
629
   for _, node in ipairs(self.nodes) do
32,589✔
630
      if not (initial and (node.is_glue or node.is_penalty)) then
31,134✔
631
         initial = false
31,134✔
632
         node:outputYourself(typesetter, line)
31,134✔
633
      end
634
   end
635
   typesetter.frame:advancePageDirection(self.depth)
1,455✔
636
   typesetter.frame:newLine()
1,455✔
637
end
638

639
function nodefactory.vbox:unbox ()
362✔
640
   for i = 1, #self.nodes do
53✔
641
      if self.nodes[i].is_vbox or self.nodes[i].is_vglue then
53✔
642
         return self.nodes
53✔
643
      end
644
   end
NEW
645
   return { self }
×
646
end
647

648
function nodefactory.vbox:append (box)
362✔
649
   local nodes = box
140✔
650
   if not box then
140✔
NEW
651
      SU.error("nil box given", true)
×
652
   end
653
   if nodes.type then
140✔
654
      nodes = box:unbox()
180✔
655
   end
656
   self.height = self.height:absolute()
280✔
657
   self.height:___add(self.depth)
140✔
658
   local lastdepth = SILE.length()
140✔
659
   for i = 1, #nodes do
666✔
660
      table.insert(self.nodes, nodes[i])
526✔
661
      self.height:___add(nodes[i].height)
526✔
662
      self.height:___add(nodes[i].depth:absolute())
1,052✔
663
      if nodes[i].is_vbox then
526✔
664
         lastdepth = nodes[i].depth
211✔
665
      end
666
   end
667
   self.height:___sub(lastdepth)
140✔
668
   self.ratio = 1
140✔
669
   self.depth = lastdepth
140✔
670
end
671

672
nodefactory.migrating = pl.class(nodefactory.hbox)
362✔
673
nodefactory.migrating.type = "migrating"
181✔
674
nodefactory.migrating.material = {}
181✔
675
nodefactory.migrating.value = {}
181✔
676
nodefactory.migrating.nodes = {}
181✔
677

678
function nodefactory.migrating:__tostring ()
362✔
NEW
679
   return "<M: " .. tostring(self.material) .. ">"
×
680
end
681

682
local _deprecated_nodefactory = {}
181✔
683

684
_deprecated_nodefactory.newHbox = function (spec)
NEW
685
   return nodefactory.hbox(spec)
×
686
end
687

688
_deprecated_nodefactory.newNnode = function (spec)
NEW
689
   return nodefactory.nnode(spec)
×
690
end
691

692
_deprecated_nodefactory.newUnshaped = function (spec)
NEW
693
   return nodefactory.unshaped(spec)
×
694
end
695

696
_deprecated_nodefactory.newDisc = function (spec)
NEW
697
   return nodefactory.discretionary(spec)
×
698
end
699

700
_deprecated_nodefactory.disc = function (spec)
NEW
701
   return nodefactory.discretionary(spec)
×
702
end
703

704
_deprecated_nodefactory.newAlternative = function (spec)
NEW
705
   return nodefactory.alternative(spec)
×
706
end
707

708
_deprecated_nodefactory.newGlue = function (spec)
NEW
709
   return nodefactory.glue(spec)
×
710
end
711

712
_deprecated_nodefactory.newKern = function (spec)
NEW
713
   return nodefactory.kern(spec)
×
714
end
715

716
_deprecated_nodefactory.newVglue = function (spec)
NEW
717
   return nodefactory.vglue(spec)
×
718
end
719

720
_deprecated_nodefactory.newVKern = function (spec)
NEW
721
   return nodefactory.vkern(spec)
×
722
end
723

724
_deprecated_nodefactory.newPenalty = function (spec)
NEW
725
   return nodefactory.penalty(spec)
×
726
end
727

728
_deprecated_nodefactory.newDiscretionary = function (spec)
NEW
729
   return nodefactory.discretionary(spec)
×
730
end
731

732
_deprecated_nodefactory.newVbox = function (spec)
NEW
733
   return nodefactory.vbox(spec)
×
734
end
735

736
_deprecated_nodefactory.newMigrating = function (spec)
NEW
737
   return nodefactory.migrating(spec)
×
738
end
739

740
_deprecated_nodefactory.zeroGlue = function ()
NEW
741
   return nodefactory.glue()
×
742
end
743

744
_deprecated_nodefactory.hfillGlue = function ()
NEW
745
   return nodefactory.hfillglue()
×
746
end
747

748
_deprecated_nodefactory.vfillGlue = function ()
NEW
749
   return nodefactory.vfillglue()
×
750
end
751

752
_deprecated_nodefactory.hssGlue = function ()
NEW
753
   return nodefactory.hssglue()
×
754
end
755

756
_deprecated_nodefactory.vssGlue = function ()
NEW
757
   return nodefactory.vssglue()
×
758
end
759

760
_deprecated_nodefactory.zeroHbox = function ()
NEW
761
   return nodefactory.zerohbox()
×
762
end
763

764
_deprecated_nodefactory.zeroVglue = function ()
NEW
765
   return nodefactory.zerovglue()
×
766
end
767

768
setmetatable(nodefactory, {
362✔
769
   __index = function (_, prop)
770
      if _deprecated_nodefactory[prop] then
×
NEW
771
         SU.deprecated(
×
NEW
772
            "SILE.nodefactory." .. prop,
×
NEW
773
            "SILE.nodefactory." .. prop:match("n?e?w?(.*)"):lower(),
×
774
            "0.10.0",
775
            "0.14.0"
776
         )
UNCOV
777
      elseif type(prop) == "number" then -- luacheck: ignore 542
×
778
      -- Likely at attempt to iterate (or dump) the table, sort of safe to ignore
779
      else
NEW
780
         SU.error("Attempt to access non-existent SILE.nodefactory." .. prop)
×
781
      end
782
   end,
783
})
784

785
return nodefactory
181✔
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

© 2026 Coveralls, Inc