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

sile-typesetter / sile / 8288578143

14 Mar 2024 09:39PM UTC coverage: 64.155% (-10.6%) from 74.718%
8288578143

Pull #1904

github

alerque
chore(core): Fixup ec6ed657 which didn't shim old pack styles properly
Pull Request #1904: Merge develop into master (commit to next release being breaking)

1648 of 2421 new or added lines in 107 files covered. (68.07%)

1843 existing lines in 77 files now uncovered.

10515 of 16390 relevant lines covered (64.15%)

3306.56 hits per line

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

79.44
/types/node.lua
1
--- SILE node type.
2
-- @types node
3

4
local nodetypes = {}
97✔
5

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

10
local function _maxnode (nodes, dim)
11
  local dims = SU.map(function (node)
18,270✔
12
    -- TODO there is a bug here because we shouldn't need to cast to lengths,
13
    -- somebody is setting a height as a number value (test in Lua 5.1)
14
    -- return node[dim]
15
    return SU.cast("length", node[dim])
23,095✔
16
  end, nodes)
9,135✔
17
  return SU.max(SILE.types.length(0), pl.utils.unpack(dims))
27,405✔
18
end
19

20
local _dims = pl.Set { "width", "height", "depth" }
194✔
21

22
nodetypes.box = pl.class()
194✔
23
nodetypes.box.type = "special"
97✔
24

25
nodetypes.box.height = nil
97✔
26
nodetypes.box.depth = nil
97✔
27
nodetypes.box.width = nil
97✔
28
nodetypes.box.misfit = false
97✔
29
nodetypes.box.explicit = false
97✔
30
nodetypes.box.discardable = false
97✔
31
nodetypes.box.value = nil
97✔
32
nodetypes.box._default_length = "width"
97✔
33

34
function nodetypes.box:_init (spec)
194✔
35
  if type(spec) == "string"
16,791✔
36
    or type(spec) == "number"
16,099✔
37
    or SU.type(spec) == "measurement"
30,980✔
38
    or SU.type(spec) == "length" then
30,876✔
39
    self[self._default_length] = SU.cast("length", spec)
6,512✔
40
  elseif SU.type(spec) == "table" then
27,070✔
41
    if spec._tospec then spec = spec:_tospec() end
9,652✔
42
    for k, v in pairs(spec) do
51,365✔
43
      self[k] = _dims[k] and SU.cast("length", v) or v
54,049✔
44
    end
45
  elseif type(spec) ~= "nil" and SU.type(spec) ~= self.type then
3,883✔
46
    SU.error("Unimplemented, creating " .. self.type .. " node from " .. SU.type(spec), 1)
×
47
  end
48
  for dim in pairs(_dims) do
67,164✔
49
    if not self[dim] then self[dim] = SILE.types.length() end
86,189✔
50
  end
51
  self["is_"..self.type] = true
16,791✔
52
  self.is_box = self.is_hbox or self.is_vbox or self.is_zerohbox or self.is_alternative or self.is_nnode
18,516✔
53
  self.is_zero = self.is_zerohbox or self.is_zerovglue
17,481✔
54
  if self.is_migrating then self.is_hbox, self.is_box = true, true end
17,136✔
55
end
56

57
-- De-init instances by shallow copying properties and removing meta table
58
function nodetypes.box:_tospec ()
194✔
59
  return pl.tablex.copy(self)
272✔
60
end
61

62
function nodetypes.box:tostring ()
194✔
63
  return  self:__tostring()
×
64
end
65

66
function nodetypes.box:__tostring ()
194✔
67
  return self.type
×
68
end
69

70
function nodetypes.box.__concat (a, b)
194✔
71
  return tostring(a) .. tostring(b)
×
72
end
73

74
function nodetypes.box:absolute ()
194✔
75
  local clone = nodetypes[self.type](self:_tospec())
544✔
76
  for dim in pairs(_dims) do
1,088✔
77
    clone[dim] = self[dim]:absolute()
1,632✔
78
  end
79
  if self.nodes then
272✔
80
    clone.nodes = pl.tablex.map_named_method("absolute", self.nodes)
×
81
  end
82
  return clone
272✔
83
end
84

85
function nodetypes.box:lineContribution ()
194✔
86
  -- Regardless of the orientations, "width" is always in the
87
  -- writingDirection, and "height" is always in the "pageDirection"
88
  return self.misfit and self.height or self.width
11,120✔
89
end
90

91
function nodetypes.box:outputYourself ()
194✔
92
  SU.error(self.type.." with no output routine")
×
93
end
94

95
function nodetypes.box:toText ()
194✔
96
  return self.type
×
97
end
98

99
function nodetypes.box:isBox ()
194✔
100
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
101
  return self.type == "hbox" or self.type == "zerohbox" or self.type == "alternative" or self.type == "nnode" or self.type == "vbox"
×
102
end
103

104
function nodetypes.box:isNnode ()
194✔
105
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
106
  return self.type=="nnode"
×
107
end
108

109
function nodetypes.box:isGlue ()
194✔
110
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
111
  return self.type == "glue"
×
112
end
113

114
function nodetypes.box:isVglue ()
194✔
115
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
116
  return self.type == "vglue"
×
117
end
118

119
function nodetypes.box:isZero ()
194✔
120
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
121
  return self.type == "zerohbox" or self.type == "zerovglue"
×
122
end
123

124
function nodetypes.box:isUnshaped ()
194✔
125
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
126
  return self.type == "unshaped"
×
127
end
128

129
function nodetypes.box:isAlternative ()
194✔
130
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
131
  return self.type == "alternative"
×
132
end
133

134
function nodetypes.box:isVbox ()
194✔
135
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
136
  return self.type == "vbox"
×
137
end
138

139
function nodetypes.box:isInsertion ()
194✔
140
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
141
  return self.type == "insertion"
×
142
end
143

144
function nodetypes.box:isMigrating ()
194✔
145
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
146
  return self.migrating
×
147
end
148

149
function nodetypes.box:isPenalty ()
194✔
150
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
151
  return self.type == "penalty"
×
152
end
153

154
function nodetypes.box:isDiscretionary ()
194✔
155
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
156
  return self.type == "discretionary"
×
157
end
158

159
function nodetypes.box:isKern ()
194✔
160
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
161
  return self.type == "kern"
×
162
end
163

164
nodetypes.hbox = pl.class(nodetypes.box)
194✔
165
nodetypes.hbox.type = "hbox"
97✔
166

167
function nodetypes.hbox:_init (spec)
194✔
168
  nodetypes.box._init(self, spec)
11,615✔
169
end
170

171
function nodetypes.hbox:__tostring ()
194✔
172
  return "H<" .. tostring(self.width) .. ">^" .. tostring(self.height) .. "-" .. tostring(self.depth) .. "v"
×
173
end
174

175
function nodetypes.hbox:scaledWidth (line)
194✔
176
  return SU.rationWidth(self:lineContribution(), self.width, line.ratio)
6,472✔
177
end
178

179
function nodetypes.hbox:outputYourself (typesetter, line)
194✔
180
  local outputWidth = self:scaledWidth(line)
3,222✔
181
  if not self.value.glyphString then return end
3,222✔
182
  if typesetter.frame:writingDirection() == "RTL" then
4,354✔
183
    typesetter.frame:advanceWritingDirection(outputWidth)
115✔
184
  end
185
  SILE.outputter:setCursor(typesetter.frame.state.cursorX, typesetter.frame.state.cursorY)
2,177✔
186
  SILE.outputter:setFont(self.value.options)
2,177✔
187
  SILE.outputter:drawHbox(self.value, outputWidth)
2,177✔
188
  if typesetter.frame:writingDirection() ~= "RTL" then
4,354✔
189
    typesetter.frame:advanceWritingDirection(outputWidth)
2,062✔
190
  end
191
end
192

193
nodetypes.zerohbox = pl.class(nodetypes.hbox)
194✔
194
nodetypes.zerohbox.type = "zerohbox"
97✔
195
nodetypes.zerohbox.value = { glyph = 0 }
97✔
196

197
nodetypes.nnode = pl.class(nodetypes.hbox)
194✔
198
nodetypes.nnode.type = "nnode"
97✔
199
nodetypes.nnode.language = ""
97✔
200
nodetypes.nnode.pal = nil
97✔
201
nodetypes.nnode.nodes = {}
97✔
202

203
function nodetypes.nnode:_init (spec)
194✔
204
  self:super(spec)
4,120✔
205
  if 0 == self.depth:tonumber() then self.depth = _maxnode(self.nodes, "depth")  end
12,360✔
206
  if 0 == self.height:tonumber() then self.height = _maxnode(self.nodes, "height") end
12,360✔
207
  if 0 == self.width:tonumber() then self.width = SU.sum(SU.map(function (node) return node.width end, self.nodes)) end
20,255✔
208
end
209

210
function nodetypes.nnode:__tostring ()
194✔
211
  return "N<" .. tostring(self.width) .. ">^" .. tostring(self.height) .. "-" .. tostring(self.depth) .. "v(" .. self:toText() .. ")"
×
212
end
213

214
function nodetypes.nnode:absolute ()
194✔
215
  return self
×
216
end
217

218
function nodetypes.nnode:outputYourself (typesetter, line)
194✔
219
  -- See typesetter:computeLineRatio() which implements the currently rather messy
220
  -- and probably slightly dubious 'hyphenated' logic.
221
  -- Example: consider the word "out-put".
222
  -- The node queue therefore contains N(out)D(-)N(put) all pointing to the same
223
  -- parent N(output).
224
  if self.parent and not self.parent.hyphenated then
2,922✔
225
    -- When we hit N(out) and are not hyphenated, we output N(output) directly
226
    -- and mark it as used, so as to skip D(-)N(put) afterwards.
227
    -- I guess this was done to ensure proper kerning (vs. outputting each of
228
    -- the nodes separately).
229
    if not self.parent.used then
745✔
230
      self.parent:outputYourself(typesetter, line)
313✔
231
    end
232
    self.parent.used = true
745✔
233
  else
234
    -- It's possible not to have a parent, e.g. N(word) without hyphenation points.
235
    -- Either that, or we have a hyphenated parent but are in the case we are
236
    -- outputting one of the elements e.g. N(out)D(-) [line break] N(put).
237
    -- (ignoring the G(margin) nodes and potentially zerohbox nodes also on either side of the line break)
238
    for _, node in ipairs(self.nodes) do node:outputYourself(typesetter, line) end
6,531✔
239
  end
240
end
241

242
function nodetypes.nnode:toText ()
194✔
243
  return self.text
2✔
244
end
245

246
nodetypes.unshaped = pl.class(nodetypes.nnode)
194✔
247
nodetypes.unshaped.type = "unshaped"
97✔
248

249
function nodetypes.unshaped:_init (spec)
194✔
250
  self:super(spec)
345✔
251
  self.width = nil
345✔
252
end
253

254
function nodetypes.unshaped:__tostring ()
194✔
255
  return "U(" .. self:toText() .. ")";
×
256
end
257

258
getmetatable(nodetypes.unshaped).__index = function (_, _)
97✔
259
  -- if k == "width" then SU.error("Can't get width of unshaped node", true) end
260
  -- TODO: No idea why porting to proper Penlight classes this ^^^^^^ started
261
  -- killing everything. Perhaps because this function started working and would
262
  -- actually need to return rawget(self, k) or something?
263
end
264

265
function nodetypes.unshaped:shape ()
194✔
266
  local node =  SILE.shaper:createNnodes(self.text, self.options)
318✔
267
  for i=1, #node do
4,091✔
268
    node[i].parent = self.parent
7,546✔
269
  end
270
  return node
318✔
271
end
272

273
function nodetypes.unshaped.outputYourself (_)
194✔
274
  SU.error("An unshaped node made it to output", true)
×
275
end
276

277
nodetypes.discretionary = pl.class(nodetypes.hbox)
194✔
278

279
nodetypes.discretionary.type = "discretionary"
97✔
280
nodetypes.discretionary.prebreak = {}
97✔
281
nodetypes.discretionary.postbreak = {}
97✔
282
nodetypes.discretionary.replacement = {}
97✔
283
nodetypes.discretionary.used = false
97✔
284

285
function nodetypes.discretionary:__tostring ()
194✔
286
  return "D(" .. SU.concat(self.prebreak, "") .. "|" .. SU.concat(self.postbreak, "") .."|" .. SU.concat(self.replacement, "") .. ")";
×
287
end
288

289
function nodetypes.discretionary:toText ()
194✔
290
  return self.used and "-" or "_"
×
291
end
292

293
function nodetypes.discretionary:markAsPrebreak ()
194✔
294
  self.used = true
28✔
295
  if self.parent then
28✔
296
    self.parent.hyphenated = true
28✔
297
  end
298
  self.is_prebreak = true
28✔
299
end
300

301
function nodetypes.discretionary:cloneAsPostbreak ()
194✔
302
  if not self.used then
28✔
NEW
303
    SU.error("Cannot clone a non-used discretionary (previously marked as prebreak)")
×
304
  end
305
  return SILE.types.node.discretionary({
28✔
306
    prebreak = self.prebreak,
28✔
307
    postbreak = self.postbreak,
28✔
308
    replacement = self.replacement,
28✔
309
    parent = self.parent,
28✔
310
    used = true,
311
    is_prebreak = false,
312
  })
28✔
313
end
314

315
function nodetypes.discretionary:outputYourself (typesetter, line)
194✔
316
  -- See typesetter:computeLineRatio() which implements the currently rather
317
  -- messy hyphenated checks.
318
  -- Example: consider the word "out-put-ter".
319
  -- The node queue contains N(out)D(-)N(put)D(-)N(ter) all pointing to the same
320
  -- parent N(output), and here we hit D(-)
321

322
  -- Non-hyphenated parent: when N(out) was hit, we went for outputting
323
  -- the whole parent, so all other elements must now be skipped.
324
  if self.parent and not self.parent.hyphenated then return end
513✔
325

326
  -- It's possible not to have a parent (e.g. on a discretionary directly
327
  -- added in the queue and not coming from the hyphenator logic).
328
  -- Eiher that, or we have a hyphenated parent.
329
  if self.used then
81✔
330
    -- This is the actual hyphenation point.
331
    if self.is_prebreak then
56✔
332
      for _, node in ipairs(self.prebreak) do node:outputYourself(typesetter, line) end
84✔
333
    else
334
      for _, node in ipairs(self.postbreak) do node:outputYourself(typesetter, line) end
28✔
335
    end
336
  else
337
    -- This is not the hyphenation point (but another discretionary in the queue)
338
    -- E.g. we were in the case where we have N(out)D(-) [line break] N(out)D(-)N(ter)
339
    -- and now hit the second D(-).
340
    -- Unused discretionaries are obviously replaced.
341
    for _, node in ipairs(self.replacement) do node:outputYourself(typesetter, line) end
27✔
342
  end
343
end
344

345
function nodetypes.discretionary:prebreakWidth ()
194✔
346
  if self.prebw then return self.prebw end
1,142✔
347
  self.prebw = SILE.types.length()
970✔
348
  for _, node in ipairs(self.prebreak) do self.prebw:___add(node.width) end
1,455✔
349
  return self.prebw
485✔
350
end
351

352
function nodetypes.discretionary:postbreakWidth ()
194✔
353
  if self.postbw then return self.postbw end
108✔
354
  self.postbw = SILE.types.length()
214✔
355
  for _, node in ipairs(self.postbreak) do self.postbw:___add(node.width) end
107✔
356
  return self.postbw
107✔
357
end
358

359
function nodetypes.discretionary:replacementWidth ()
194✔
360
  if self.replacew then return self.replacew end
622✔
361
  self.replacew = SILE.types.length()
970✔
362
  for _, node in ipairs(self.replacement) do self.replacew:___add(node.width) end
491✔
363
  return self.replacew
485✔
364
end
365

366
function nodetypes.discretionary:prebreakHeight ()
194✔
367
  if self.prebh then return self.prebh end
28✔
368
  self.prebh = _maxnode(self.prebreak, "height")
56✔
369
  return self.prebh
28✔
370
end
371

372
function nodetypes.discretionary:postbreakHeight ()
194✔
373
  if self.postbh then return self.postbh end
28✔
374
  self.postbh = _maxnode(self.postbreak, "height")
56✔
375
  return self.postbh
28✔
376
end
377

378
function nodetypes.discretionary:replacementHeight ()
194✔
379
  if self.replaceh then return self.replaceh end
25✔
380
  self.replaceh = _maxnode(self.replacement, "height")
50✔
381
  return self.replaceh
25✔
382
end
383

384
function nodetypes.discretionary:replacementDepth ()
194✔
385
  if self.replaced then return self.replaced end
×
386
  self.replaced = _maxnode(self.replacement, "depth")
×
387
  return self.replaced
×
388
end
389

390
nodetypes.alternative = pl.class(nodetypes.hbox)
192✔
391

392
nodetypes.alternative.type = "alternative"
96✔
393
nodetypes.alternative.options = {}
96✔
394
nodetypes.alternative.selected = nil
96✔
395

396
function nodetypes.alternative:__tostring ()
192✔
397
  return "A(" .. SU.concat(self.options, " / ") .. ")"
×
398
end
399

400
function nodetypes.alternative:minWidth ()
192✔
401
  local minW = function (a, b) return SU.min(a.width, b.width) end
×
402
  return pl.tablex.reduce(minW, self.options)
×
403
end
404

405
function nodetypes.alternative:deltas ()
192✔
406
  local minWidth = self:minWidth()
×
407
  local rv = {}
×
408
  for i = 1, #self.options do rv[#rv+1] = self.options[i].width - minWidth end
×
409
  return rv
×
410
end
411

412
function nodetypes.alternative:outputYourself (typesetter, line)
192✔
413
  if self.selected then
×
414
    self.options[self.selected]:outputYourself(typesetter, line)
×
415
  end
416
end
417

418
nodetypes.glue = pl.class(nodetypes.box)
192✔
419
nodetypes.glue.type = "glue"
96✔
420
nodetypes.glue.discardable = true
96✔
421

422
function nodetypes.glue:__tostring ()
192✔
423
  return (self.explicit and "E:" or "") .. "G<" .. tostring(self.width) .. ">"
×
424
end
425

426
function nodetypes.glue.toText (_)
192✔
427
  return " "
×
428
end
429

430
function nodetypes.glue:outputYourself (typesetter, line)
192✔
431
  local outputWidth = SU.rationWidth(self.width:absolute(), self.width:absolute(), line.ratio)
8,799✔
432
  typesetter.frame:advanceWritingDirection(outputWidth)
2,933✔
433
end
434

435
-- A hfillglue is just a glue with infinite stretch.
436
-- (Convenience so callers do not have to know what infinity is.)
437
nodetypes.hfillglue = pl.class(nodetypes.glue)
192✔
438
function nodetypes.hfillglue:_init (spec)
192✔
439
  self:super(spec)
164✔
440
  self.width = SILE.types.length(self.width.length, infinity, self.width.shrink)
328✔
441
end
442

443
-- A hssglue is just a glue with infinite stretch and shrink.
444
-- (Convenience so callers do not have to know what infinity is.)
445
nodetypes.hssglue = pl.class(nodetypes.glue)
192✔
446
function nodetypes.hssglue:_init (spec)
192✔
447
  self:super(spec)
×
NEW
448
  self.width = SILE.types.length(self.width.length, infinity, infinity)
×
449
end
450

451
nodetypes.kern = pl.class(nodetypes.glue)
192✔
452
nodetypes.kern.type = "kern" -- Perhaps some smell here, see comment on vkern
96✔
453
nodetypes.kern.discardable = false
96✔
454

455
function nodetypes.kern:__tostring ()
192✔
456
  return "K<" .. tostring(self.width) .. ">"
×
457
end
458

459
nodetypes.vglue = pl.class(nodetypes.box)
192✔
460
nodetypes.vglue.type = "vglue"
96✔
461
nodetypes.vglue.discardable = true
96✔
462
nodetypes.vglue._default_length = "height"
96✔
463
nodetypes.vglue.adjustment = nil
96✔
464

465
function nodetypes.vglue:_init (spec)
192✔
466
  self.adjustment = SILE.measurement()
1,980✔
467
  self:super(spec)
990✔
468
end
469

470
function nodetypes.vglue:__tostring ()
192✔
471
  return (self.explicit and "E:" or "") .. "VG<" .. tostring(self.height) .. ">";
×
472
end
473

474
function nodetypes.vglue:adjustGlue (adjustment)
192✔
475
  self.adjustment = adjustment
626✔
476
end
477

478
function nodetypes.vglue:outputYourself (typesetter, line)
192✔
479
  typesetter.frame:advancePageDirection(line.height:absolute() + line.depth:absolute() + self.adjustment)
3,715✔
480
end
481

482
function nodetypes.vglue:unbox ()
192✔
483
  return { self }
4✔
484
end
485

486
-- A vfillglue is just a vglue with infinite stretch.
487
-- (Convenience so callers do not have to know what infinity is.)
488
nodetypes.vfillglue = pl.class(nodetypes.vglue)
192✔
489
function nodetypes.vfillglue:_init (spec)
192✔
490
  self:super(spec)
111✔
491
  self.height = SILE.types.length(self.width.length, infinity, self.width.shrink)
222✔
492
end
493

494
-- A vssglue is just a vglue with infinite stretch and shrink.
495
-- (Convenience so callers do not have to know what infinity is.)
496
nodetypes.vssglue = pl.class(nodetypes.vglue)
192✔
497
function nodetypes.vssglue:_init (spec)
192✔
498
  self:super(spec)
×
NEW
499
  self.height = SILE.types.length(self.width.length, infinity, infinity)
×
500
end
501

502
nodetypes.zerovglue = pl.class(nodetypes.vglue)
192✔
503

504
nodetypes.vkern = pl.class(nodetypes.vglue)
192✔
505
-- FIXME TODO
506
-- Here we cannot do:
507
--   nodetypes.vkern.type = "vkern"
508
-- It cannot be typed as "vkern" as the pagebuilder doesn't check is_vkern.
509
-- So it's just a vglue currrenty, marked as not discardable...
510
-- But on the other hand, nodetypes.kern is typed "kern" and is not a glue...
511
-- Frankly, the discardable/explicit flags and the types are too
512
-- entangled and point towards a more general design issue.
513
-- N.B. this vkern node is only used in the linespacing package so far.
514
nodetypes.vkern.discardable = false
96✔
515

516
function nodetypes.vkern:__tostring ()
192✔
517
  return "VK<" .. tostring(self.height) .. ">"
×
518
end
519

520
nodetypes.penalty = pl.class(nodetypes.box)
192✔
521
nodetypes.penalty.type = "penalty"
96✔
522
nodetypes.penalty.discardable = true
96✔
523
nodetypes.penalty.penalty = 0
96✔
524

525
function nodetypes.penalty:_init (spec)
192✔
526
  self:super(spec)
702✔
527
  if type(spec) ~= "table" then
702✔
528
    self.penalty = SU.cast("number", spec)
1,214✔
529
  end
530
end
531

532
function nodetypes.penalty:__tostring ()
192✔
533
  return "P(" .. tostring(self.penalty) .. ")";
×
534
end
535

536
function nodetypes.penalty.outputYourself (_)
192✔
537
end
538

539
function nodetypes.penalty.toText (_)
192✔
540
  return "(!)"
×
541
end
542

543
function nodetypes.penalty:unbox ()
192✔
544
  return { self }
×
545
end
546

547
nodetypes.vbox = pl.class(nodetypes.box)
192✔
548
nodetypes.vbox.type = "vbox"
96✔
549
nodetypes.vbox.nodes = {}
96✔
550
nodetypes.vbox._default_length = "height"
96✔
551

552
function nodetypes.vbox:_init (spec)
192✔
553
  self.nodes = {}
407✔
554
  self:super(spec)
407✔
555
  self.depth = _maxnode(self.nodes, "depth")
814✔
556
  self.height = _maxnode(self.nodes, "height")
814✔
557
end
558

559
function nodetypes.vbox:__tostring ()
192✔
560
  return "VB<" .. tostring(self.height) .. "|" .. self:toText() .. "v".. tostring(self.depth) ..")";
×
561
end
562

563
function nodetypes.vbox:toText ()
192✔
564
  return "VB[" .. SU.concat(SU.map(function (node) return node:toText() end, self.nodes), "") .. "]"
×
565
end
566

567
function nodetypes.vbox:outputYourself (typesetter, line)
192✔
568
  typesetter.frame:advancePageDirection(self.height)
387✔
569
  local initial = true
387✔
570
  for _, node in ipairs(self.nodes) do
7,842✔
571
    if not (initial and (node.is_glue or node.is_penalty)) then
7,455✔
572
      initial = false
7,455✔
573
      node:outputYourself(typesetter, line)
7,455✔
574
    end
575
  end
576
  typesetter.frame:advancePageDirection(self.depth)
387✔
577
  typesetter.frame:newLine()
387✔
578
end
579

580
function nodetypes.vbox:unbox ()
192✔
581
  for i = 1, #self.nodes do
5✔
582
    if self.nodes[i].is_vbox or self.nodes[i].is_vglue then return self.nodes end
5✔
583
  end
584
  return {self}
×
585
end
586

587
function nodetypes.vbox:append (box)
192✔
588
  local nodes = box
13✔
589
  if not box then SU.error("nil box given", true) end
13✔
590
  if nodes.type then
13✔
591
    nodes = box:unbox()
16✔
592
  end
593
  self.height = self.height:absolute()
26✔
594
  self.height:___add(self.depth)
13✔
595
  local lastdepth = SILE.types.length()
13✔
596
  for i = 1, #nodes do
203✔
597
    table.insert(self.nodes, nodes[i])
190✔
598
    self.height:___add(nodes[i].height)
190✔
599
    self.height:___add(nodes[i].depth:absolute())
380✔
600
    if nodes[i].is_vbox then lastdepth = nodes[i].depth end
190✔
601
  end
602
  self.height:___sub(lastdepth)
13✔
603
  self.ratio = 1
13✔
604
  self.depth = lastdepth
13✔
605
end
606

607
nodetypes.migrating = pl.class(nodetypes.hbox)
192✔
608
nodetypes.migrating.type = "migrating"
96✔
609
nodetypes.migrating.material = {}
96✔
610
nodetypes.migrating.value = {}
96✔
611
nodetypes.migrating.nodes = {}
96✔
612

613
function nodetypes.migrating:__tostring ()
192✔
614
  return "<M: " .. tostring(self.material) .. ">"
×
615
end
616

617
local _deprecated_nodefactory = {
96✔
618
  newHbox = true,
619
  newNnode = true,
620
  newUnshaped = true,
621
  newDisc = true,
622
  disc = true,
623
  newAlternative = true,
624
  newGlue = true,
625
  newKern = true,
626
  newVglue = true,
627
  newVKern = true,
628
  newPenalty = true,
629
  newDiscretionary = true,
630
  newVbox = true,
631
  newMigrating = true,
632
  zeroGlue = true,
633
  hfillGlue = true,
634
  vfillGlue = true,
635
  hssGlue = true,
636
  vssGlue = true,
637
  zeroHbox = true,
638
  zeroVglue = true,
639
}
640

641
setmetatable(nodetypes, {
192✔
642
    __index = function (_, prop)
643
      if _deprecated_nodefactory[prop] then
×
NEW
644
        SU.deprecated("SILE.types.node." .. prop, "SILE.types.node." .. prop:match("n?e?w?(.*)"):lower(), "0.10.0", "0.14.0")
×
645
      elseif type(prop) == "number" then -- luacheck: ignore 542
×
646
        -- Likely an attempt to iterate, inspect, or dump the table, sort of safe to ignore
647
      else
NEW
648
        SU.error("Attempt to access non-existent SILE.types.node." .. prop)
×
649
      end
650
    end
651
  })
652

653
return nodetypes
96✔
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