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

sile-typesetter / sile / 6713098919

31 Oct 2023 10:21PM UTC coverage: 52.831% (-21.8%) from 74.636%
6713098919

push

github

web-flow
Merge d0a2a1ee9 into b185d4972

45 of 45 new or added lines in 3 files covered. (100.0%)

8173 of 15470 relevant lines covered (52.83%)

6562.28 hits per line

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

75.14
/core/nodefactory.lua
1
local nodefactory = {}
346✔
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)
346✔
6

7
local function _maxnode (nodes, dim)
8
  local dims = SU.map(function (node)
80,992✔
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])
99,563✔
13
  end, nodes)
40,496✔
14
  return SU.max(SILE.length(0), pl.utils.unpack(dims))
121,488✔
15
end
16

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

19
nodefactory.box = pl.class()
692✔
20
nodefactory.box.type = "special"
346✔
21

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

31
function nodefactory.box:_init (spec)
692✔
32
  if type(spec) == "string"
63,460✔
33
    or type(spec) == "number"
61,123✔
34
    or SU.type(spec) == "measurement"
117,702✔
35
    or SU.type(spec) == "length" then
117,684✔
36
    self[self._default_length] = SU.cast("length", spec)
24,966✔
37
  elseif SU.type(spec) == "table" then
101,954✔
38
    if spec._tospec then spec = spec:_tospec() end
42,063✔
39
    for k, v in pairs(spec) do
224,657✔
40
      self[k] = _dims[k] and SU.cast("length", v) or v
236,488✔
41
    end
42
  elseif type(spec) ~= "nil" and SU.type(spec) ~= self.type then
8,914✔
43
    SU.error("Unimplemented, creating " .. self.type .. " node from " .. SU.type(spec), 1)
×
44
  end
45
  for dim in pairs(_dims) do
253,840✔
46
    if not self[dim] then self[dim] = SILE.length() end
319,165✔
47
  end
48
  self["is_"..self.type] = true
63,460✔
49
  self.is_box = self.is_hbox or self.is_vbox or self.is_zerohbox or self.is_alternative or self.is_nnode
71,430✔
50
  self.is_zero = self.is_zerohbox or self.is_zerovglue
66,648✔
51
  if self.is_migrating then self.is_hbox, self.is_box = true, true end
65,054✔
52
end
53

54
-- De-init instances by shallow copying properties and removing meta table
55
function nodefactory.box:_tospec ()
692✔
56
  return pl.tablex.copy(self)
934✔
57
end
58

59
function nodefactory.box:tostring ()
692✔
60
  return  self:__tostring()
×
61
end
62

63
function nodefactory.box:__tostring ()
692✔
64
  return self.type
×
65
end
66

67
function nodefactory.box.__concat (a, b)
692✔
68
  return tostring(a) .. tostring(b)
×
69
end
70

71
function nodefactory.box:absolute ()
692✔
72
  local clone = nodefactory[self.type](self:_tospec())
1,868✔
73
  for dim in pairs(_dims) do
3,736✔
74
    clone[dim] = self[dim]:absolute()
5,604✔
75
  end
76
  if self.nodes then
934✔
77
    clone.nodes = pl.tablex.map_named_method("absolute", self.nodes)
10✔
78
  end
79
  return clone
934✔
80
end
81

82
function nodefactory.box:lineContribution ()
692✔
83
  -- Regardless of the orientations, "width" is always in the
84
  -- writingDirection, and "height" is always in the "pageDirection"
85
  return self.misfit and self.height or self.width
48,508✔
86
end
87

88
function nodefactory.box:outputYourself ()
692✔
89
  SU.error(self.type.." with no output routine")
×
90
end
91

92
function nodefactory.box:toText ()
692✔
93
  return self.type
×
94
end
95

96
function nodefactory.box:isBox ()
692✔
97
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
98
  return self.type == "hbox" or self.type == "zerohbox" or self.type == "alternative" or self.type == "nnode" or self.type == "vbox"
×
99
end
100

101
function nodefactory.box:isNnode ()
692✔
102
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
103
  return self.type=="nnode"
×
104
end
105

106
function nodefactory.box:isGlue ()
692✔
107
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
108
  return self.type == "glue"
×
109
end
110

111
function nodefactory.box:isVglue ()
692✔
112
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
113
  return self.type == "vglue"
×
114
end
115

116
function nodefactory.box:isZero ()
692✔
117
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
118
  return self.type == "zerohbox" or self.type == "zerovglue"
×
119
end
120

121
function nodefactory.box:isUnshaped ()
692✔
122
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
123
  return self.type == "unshaped"
×
124
end
125

126
function nodefactory.box:isAlternative ()
692✔
127
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
128
  return self.type == "alternative"
×
129
end
130

131
function nodefactory.box:isVbox ()
692✔
132
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
133
  return self.type == "vbox"
×
134
end
135

136
function nodefactory.box:isInsertion ()
692✔
137
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
138
  return self.type == "insertion"
×
139
end
140

141
function nodefactory.box:isMigrating ()
692✔
142
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
143
  return self.migrating
×
144
end
145

146
function nodefactory.box:isPenalty ()
692✔
147
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
148
  return self.type == "penalty"
×
149
end
150

151
function nodefactory.box:isDiscretionary ()
692✔
152
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
153
  return self.type == "discretionary"
×
154
end
155

156
function nodefactory.box:isKern ()
692✔
157
  SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
158
  return self.type == "kern"
×
159
end
160

161
nodefactory.hbox = pl.class(nodefactory.box)
692✔
162
nodefactory.hbox.type = "hbox"
346✔
163

164
function nodefactory.hbox:_init (spec)
692✔
165
  nodefactory.box._init(self, spec)
44,555✔
166
end
167

168
function nodefactory.hbox:__tostring ()
692✔
169
  return "H<" .. tostring(self.width) .. ">^" .. tostring(self.height) .. "-" .. tostring(self.depth) .. "v"
×
170
end
171

172
function nodefactory.hbox:scaledWidth (line)
692✔
173
  return SU.rationWidth(self:lineContribution(), self.width, line.ratio)
28,622✔
174
end
175

176
function nodefactory.hbox:outputYourself (typesetter, line)
692✔
177
  local outputWidth = self:scaledWidth(line)
13,722✔
178
  if not self.value.glyphString then return end
13,722✔
179
  if typesetter.frame:writingDirection() == "RTL" then
19,986✔
180
    typesetter.frame:advanceWritingDirection(outputWidth)
298✔
181
  end
182
  SILE.outputter:setCursor(typesetter.frame.state.cursorX, typesetter.frame.state.cursorY)
9,993✔
183
  SILE.outputter:setFont(self.value.options)
9,993✔
184
  SILE.outputter:drawHbox(self.value, outputWidth)
9,993✔
185
  if typesetter.frame:writingDirection() ~= "RTL" then
19,986✔
186
    typesetter.frame:advanceWritingDirection(outputWidth)
9,695✔
187
  end
188
end
189

190
nodefactory.zerohbox = pl.class(nodefactory.hbox)
692✔
191
nodefactory.zerohbox.type = "zerohbox"
346✔
192
nodefactory.zerohbox.value = { glyph = 0 }
346✔
193

194
nodefactory.nnode = pl.class(nodefactory.hbox)
692✔
195
nodefactory.nnode.type = "nnode"
346✔
196
nodefactory.nnode.language = ""
346✔
197
nodefactory.nnode.pal = nil
346✔
198
nodefactory.nnode.nodes = {}
346✔
199

200
function nodefactory.nnode:_init (spec)
692✔
201
  self:super(spec)
18,170✔
202
  if 0 == self.depth:tonumber() then self.depth = _maxnode(self.nodes, "depth")  end
54,510✔
203
  if 0 == self.height:tonumber() then self.height = _maxnode(self.nodes, "height") end
54,510✔
204
  if 0 == self.width:tonumber() then self.width = SU.sum(SU.map(function (node) return node.width end, self.nodes)) end
89,256✔
205
end
206

207
function nodefactory.nnode:__tostring ()
692✔
208
  return "N<" .. tostring(self.width) .. ">^" .. tostring(self.height) .. "-" .. tostring(self.depth) .. "v(" .. self:toText() .. ")"
×
209
end
210

211
function nodefactory.nnode:absolute ()
692✔
212
  return self
2✔
213
end
214

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

239
function nodefactory.nnode:toText ()
692✔
240
  return self.text
2✔
241
end
242

243
nodefactory.unshaped = pl.class(nodefactory.nnode)
692✔
244
nodefactory.unshaped.type = "unshaped"
346✔
245

246
function nodefactory.unshaped:_init (spec)
692✔
247
  self:super(spec)
1,594✔
248
  self.width = nil
1,594✔
249
end
250

251
function nodefactory.unshaped:__tostring ()
692✔
252
  return "U(" .. self:toText() .. ")";
×
253
end
254

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

262
function nodefactory.unshaped:shape ()
692✔
263
  local node =  SILE.shaper:createNnodes(self.text, self.options)
1,548✔
264
  for i=1, #node do
17,253✔
265
    node[i].parent = self.parent
31,410✔
266
  end
267
  return node
1,548✔
268
end
269

270
function nodefactory.unshaped.outputYourself (_)
692✔
271
  SU.error("An unshaped node made it to output", true)
×
272
end
273

274
nodefactory.discretionary = pl.class(nodefactory.hbox)
692✔
275

276
nodefactory.discretionary.type = "discretionary"
346✔
277
nodefactory.discretionary.prebreak = {}
346✔
278
nodefactory.discretionary.postbreak = {}
346✔
279
nodefactory.discretionary.replacement = {}
346✔
280
nodefactory.discretionary.used = false
346✔
281

282
function nodefactory.discretionary:__tostring ()
692✔
283
  return "D(" .. SU.concat(self.prebreak, "") .. "|" .. SU.concat(self.postbreak, "") .."|" .. SU.concat(self.replacement, "") .. ")";
×
284
end
285

286
function nodefactory.discretionary:toText ()
692✔
287
  return self.used and "-" or "_"
×
288
end
289

290
function nodefactory.discretionary:outputYourself (typesetter, line)
692✔
291
  -- See typesetter:computeLineRatio() which implements the currently rather
292
  -- messy hyphenated checks.
293
  -- Example: consider the word "out-put-ter".
294
  -- The node queue contains N(out)D(-)N(put)D(-)N(ter) all pointing to the same
295
  -- parent N(output), and here we hit D(-)
296

297
  -- Non-hyphenated parent: when N(out) was hit, we went for outputting
298
  -- the whole parent, so all other elements must now be skipped.
299
  if self.parent and not self.parent.hyphenated then return end
3,724✔
300

301
  -- It's possible not to have a parent (e.g. on a discretionary directly
302
  -- added in the queue and not coming from the hyphenator logic).
303
  -- Eiher that, or we have a hyphenate parent.
304
  if self.used then
897✔
305
    -- This is the actual hyphenation point.
306
    -- Skip margin glue and zero boxes.
307
    -- If we then reach our discretionary, it means its the first in the line,
308
    -- i.e. a postbreak. Otherwise, its a prebreak (near the end of the line,
309
    -- notwithstanding glues etc.)
310
    local i = 1
411✔
311
    while (line.nodes[i].is_glue and line.nodes[i].value == "margin")
1,284✔
312
      or line.nodes[i].type == "zerohbox" do
1,284✔
313
      i = i + 1
873✔
314
    end
315
    if (line.nodes[i] == self) then
411✔
316
      for _, node in ipairs(self.postbreak) do node:outputYourself(typesetter, line) end
213✔
317
    else
318
      for _, node in ipairs(self.prebreak) do node:outputYourself(typesetter, line) end
618✔
319
    end
320
  else
321
    -- This is not the hyphenation point (but another discretionary in the queue)
322
    -- E.g. we were in the case where we have N(out)D(-) [line break] N(out)D(-)N(ter)
323
    -- and now hit the second D(-).
324
    -- Unused discretionaries are obviously replaced.
325
    for _, node in ipairs(self.replacement) do node:outputYourself(typesetter, line) end
608✔
326
  end
327
end
328

329
function nodefactory.discretionary:prebreakWidth ()
692✔
330
  if self.prebw then return self.prebw end
8,361✔
331
  self.prebw = SILE.length()
7,236✔
332
  for _, node in ipairs(self.prebreak) do self.prebw:___add(node.width) end
10,854✔
333
  return self.prebw
3,618✔
334
end
335

336
function nodefactory.discretionary:postbreakWidth ()
692✔
337
  if self.postbw then return self.postbw end
667✔
338
  self.postbw = SILE.length()
894✔
339
  for _, node in ipairs(self.postbreak) do self.postbw:___add(node.width) end
455✔
340
  return self.postbw
447✔
341
end
342

343
function nodefactory.discretionary:replacementWidth ()
692✔
344
  if self.replacew then return self.replacew end
4,792✔
345
  self.replacew = SILE.length()
7,236✔
346
  for _, node in ipairs(self.replacement) do self.replacew:___add(node.width) end
4,058✔
347
  return self.replacew
3,618✔
348
end
349

350
function nodefactory.discretionary:prebreakHeight ()
692✔
351
  if self.prebh then return self.prebh end
214✔
352
  self.prebh = _maxnode(self.prebreak, "height")
428✔
353
  return self.prebh
214✔
354
end
355

356
function nodefactory.discretionary:postbreakHeight ()
692✔
357
  if self.postbh then return self.postbh end
214✔
358
  self.postbh = _maxnode(self.postbreak, "height")
428✔
359
  return self.postbh
214✔
360
end
361

362
function nodefactory.discretionary:replacementHeight ()
692✔
363
  if self.replaceh then return self.replaceh end
492✔
364
  self.replaceh = _maxnode(self.replacement, "height")
984✔
365
  return self.replaceh
492✔
366
end
367

368
function nodefactory.discretionary:replacementDepth ()
692✔
369
  if self.replaced then return self.replaced end
×
370
  self.replaced = _maxnode(self.replacement, "depth")
×
371
  return self.replaced
×
372
end
373

374
nodefactory.alternative = pl.class(nodefactory.hbox)
692✔
375

376
nodefactory.alternative.type = "alternative"
346✔
377
nodefactory.alternative.options = {}
346✔
378
nodefactory.alternative.selected = nil
346✔
379

380
function nodefactory.alternative:__tostring ()
692✔
381
  return "A(" .. SU.concat(self.options, " / ") .. ")"
×
382
end
383

384
function nodefactory.alternative:minWidth ()
692✔
385
  local minW = function (a, b) return SU.min(a.width, b.width) end
×
386
  return pl.tablex.reduce(minW, self.options)
×
387
end
388

389
function nodefactory.alternative:deltas ()
692✔
390
  local minWidth = self:minWidth()
×
391
  local rv = {}
×
392
  for i = 1, #self.options do rv[#rv+1] = self.options[i].width - minWidth end
×
393
  return rv
×
394
end
395

396
function nodefactory.alternative:outputYourself (typesetter, line)
692✔
397
  if self.selected then
×
398
    self.options[self.selected]:outputYourself(typesetter, line)
×
399
  end
400
end
401

402
nodefactory.glue = pl.class(nodefactory.box)
692✔
403
nodefactory.glue.type = "glue"
346✔
404
nodefactory.glue.discardable = true
346✔
405

406
function nodefactory.glue:__tostring ()
692✔
407
  return (self.explicit and "E:" or "") .. "G<" .. tostring(self.width) .. ">"
×
408
end
409

410
function nodefactory.glue.toText (_)
692✔
411
  return " "
×
412
end
413

414
function nodefactory.glue:outputYourself (typesetter, line)
692✔
415
  local outputWidth = SU.rationWidth(self.width:absolute(), self.width:absolute(), line.ratio)
35,316✔
416
  typesetter.frame:advanceWritingDirection(outputWidth)
11,772✔
417
end
418

419
-- A hfillglue is just a glue with infinite stretch.
420
-- (Convenience so callers do not have to know what infinity is.)
421
nodefactory.hfillglue = pl.class(nodefactory.glue)
692✔
422
function nodefactory.hfillglue:_init (spec)
692✔
423
  self:super(spec)
345✔
424
  self.width = SILE.length(self.width.length, infinity, self.width.shrink)
690✔
425
end
426

427
-- A hssglue is just a glue with infinite stretch and shrink.
428
-- (Convenience so callers do not have to know what infinity is.)
429
nodefactory.hssglue = pl.class(nodefactory.glue)
692✔
430
function nodefactory.hssglue:_init (spec)
692✔
431
  self:super(spec)
×
432
  self.width = SILE.length(self.width.length, infinity, infinity)
×
433
end
434

435
nodefactory.kern = pl.class(nodefactory.glue)
692✔
436
nodefactory.kern.type = "kern" -- Perhaps some smell here, see comment on vkern
346✔
437
nodefactory.kern.discardable = false
346✔
438

439
function nodefactory.kern:__tostring ()
692✔
440
  return "K<" .. tostring(self.width) .. ">"
×
441
end
442

443
nodefactory.vglue = pl.class(nodefactory.box)
692✔
444
nodefactory.vglue.type = "vglue"
346✔
445
nodefactory.vglue.discardable = true
346✔
446
nodefactory.vglue._default_length = "height"
346✔
447
nodefactory.vglue.adjustment = nil
346✔
448

449
function nodefactory.vglue:_init (spec)
692✔
450
  self.adjustment = SILE.measurement()
7,234✔
451
  self:super(spec)
3,617✔
452
end
453

454
function nodefactory.vglue:__tostring ()
692✔
455
  return (self.explicit and "E:" or "") .. "VG<" .. tostring(self.height) .. ">";
×
456
end
457

458
function nodefactory.vglue:adjustGlue (adjustment)
692✔
459
  self.adjustment = adjustment
1,984✔
460
end
461

462
function nodefactory.vglue:outputYourself (typesetter, line)
692✔
463
  typesetter.frame:advancePageDirection(line.height:absolute() + line.depth:absolute() + self.adjustment)
11,770✔
464
end
465

466
function nodefactory.vglue:unbox ()
692✔
467
  return { self }
42✔
468
end
469

470
-- A vfillglue is just a vglue with infinite stretch.
471
-- (Convenience so callers do not have to know what infinity is.)
472
nodefactory.vfillglue = pl.class(nodefactory.vglue)
692✔
473
function nodefactory.vfillglue:_init (spec)
692✔
474
  self:super(spec)
363✔
475
  self.height = SILE.length(self.width.length, infinity, self.width.shrink)
726✔
476
end
477

478
-- A vssglue is just a vglue with infinite stretch and shrink.
479
-- (Convenience so callers do not have to know what infinity is.)
480
nodefactory.vssglue = pl.class(nodefactory.vglue)
692✔
481
function nodefactory.vssglue:_init (spec)
692✔
482
  self:super(spec)
×
483
  self.height = SILE.length(self.width.length, infinity, infinity)
×
484
end
485

486
nodefactory.zerovglue = pl.class(nodefactory.vglue)
692✔
487

488
nodefactory.vkern = pl.class(nodefactory.vglue)
692✔
489
-- FIXME TODO
490
-- Here we cannot do:
491
--   nodefactory.vkern.type = "vkern"
492
-- It cannot be typed as "vkern" as the pagebuilder doesn't check is_vkern.
493
-- So it's just a vglue currrenty, marked as not discardable...
494
-- But on the other hand, nodefactory.kern is typed "kern" and is not a glue...
495
-- Frankly, the discardable/explicit flags and the types are too
496
-- entangled and point towards a more general design issue.
497
-- N.B. this vkern node is only used in the linespacing package so far.
498
nodefactory.vkern.discardable = false
346✔
499

500
function nodefactory.vkern:__tostring ()
692✔
501
  return "VK<" .. tostring(self.height) .. ">"
×
502
end
503

504
nodefactory.penalty = pl.class(nodefactory.box)
692✔
505
nodefactory.penalty.type = "penalty"
346✔
506
nodefactory.penalty.discardable = true
346✔
507
nodefactory.penalty.penalty = 0
346✔
508

509
function nodefactory.penalty:_init (spec)
692✔
510
  self:super(spec)
2,365✔
511
  if type(spec) ~= "table" then
2,365✔
512
    self.penalty = SU.cast("number", spec)
3,988✔
513
  end
514
end
515

516
function nodefactory.penalty:__tostring ()
692✔
517
  return "P(" .. tostring(self.penalty) .. ")";
×
518
end
519

520
function nodefactory.penalty.outputYourself (_)
692✔
521
end
522

523
function nodefactory.penalty.toText (_)
692✔
524
  return "(!)"
×
525
end
526

527
function nodefactory.penalty:unbox ()
692✔
528
  return { self }
×
529
end
530

531
nodefactory.vbox = pl.class(nodefactory.box)
692✔
532
nodefactory.vbox.type = "vbox"
346✔
533
nodefactory.vbox.nodes = {}
346✔
534
nodefactory.vbox._default_length = "height"
346✔
535

536
function nodefactory.vbox:_init (spec)
692✔
537
  self.nodes = {}
1,618✔
538
  self:super(spec)
1,618✔
539
  self.depth = _maxnode(self.nodes, "depth")
3,236✔
540
  self.height = _maxnode(self.nodes, "height")
3,236✔
541
end
542

543
function nodefactory.vbox:__tostring ()
692✔
544
  return "VB<" .. tostring(self.height) .. "|" .. self:toText() .. "v".. tostring(self.depth) ..")";
×
545
end
546

547
function nodefactory.vbox:toText ()
692✔
548
  return "VB[" .. SU.concat(SU.map(function (node) return node:toText() end, self.nodes), "") .. "]"
×
549
end
550

551
function nodefactory.vbox:outputYourself (typesetter, line)
692✔
552
  typesetter.frame:advancePageDirection(self.height)
1,445✔
553
  local initial = true
1,445✔
554
  for _, node in ipairs(self.nodes) do
33,437✔
555
    if not (initial and (node.is_glue or node.is_penalty)) then
31,992✔
556
      initial = false
31,992✔
557
      node:outputYourself(typesetter, line)
31,992✔
558
    end
559
  end
560
  typesetter.frame:advancePageDirection(self.depth)
1,445✔
561
  typesetter.frame:newLine()
1,445✔
562
end
563

564
function nodefactory.vbox:unbox ()
692✔
565
  for i = 1, #self.nodes do
53✔
566
    if self.nodes[i].is_vbox or self.nodes[i].is_vglue then return self.nodes end
53✔
567
  end
568
  return {self}
×
569
end
570

571
function nodefactory.vbox:append (box)
692✔
572
  local nodes = box
140✔
573
  if not box then SU.error("nil box given", true) end
140✔
574
  if nodes.type then
140✔
575
    nodes = box:unbox()
180✔
576
  end
577
  self.height = self.height:absolute()
280✔
578
  self.height:___add(self.depth)
140✔
579
  local lastdepth = SILE.length()
140✔
580
  for i = 1, #nodes do
666✔
581
    table.insert(self.nodes, nodes[i])
526✔
582
    self.height:___add(nodes[i].height)
526✔
583
    self.height:___add(nodes[i].depth:absolute())
1,052✔
584
    if nodes[i].is_vbox then lastdepth = nodes[i].depth end
526✔
585
  end
586
  self.height:___sub(lastdepth)
140✔
587
  self.ratio = 1
140✔
588
  self.depth = lastdepth
140✔
589
end
590

591
nodefactory.migrating = pl.class(nodefactory.hbox)
692✔
592
nodefactory.migrating.type = "migrating"
346✔
593
nodefactory.migrating.material = {}
346✔
594
nodefactory.migrating.value = {}
346✔
595
nodefactory.migrating.nodes = {}
346✔
596

597
function nodefactory.migrating:__tostring ()
692✔
598
  return "<M: " .. tostring(self.material) .. ">"
×
599
end
600

601
local _deprecated_nodefactory = {}
346✔
602

603
_deprecated_nodefactory.newHbox = function (spec)
604
  return nodefactory.hbox(spec)
×
605
end
606

607
_deprecated_nodefactory.newNnode = function (spec)
608
  return nodefactory.nnode(spec)
×
609
end
610

611
_deprecated_nodefactory.newUnshaped = function (spec)
612
  return nodefactory.unshaped(spec)
×
613
end
614

615
_deprecated_nodefactory.newDisc = function (spec)
616
  return nodefactory.discretionary(spec)
×
617
end
618

619
_deprecated_nodefactory.disc = function (spec)
620
  return nodefactory.discretionary(spec)
×
621
end
622

623
_deprecated_nodefactory.newAlternative = function (spec)
624
  return nodefactory.alternative(spec)
×
625
end
626

627
_deprecated_nodefactory.newGlue = function (spec)
628
  return nodefactory.glue(spec)
×
629
end
630

631
_deprecated_nodefactory.newKern = function (spec)
632
  return nodefactory.kern(spec)
×
633
end
634

635
_deprecated_nodefactory.newVglue = function (spec)
636
  return nodefactory.vglue(spec)
×
637
end
638

639
_deprecated_nodefactory.newVKern = function (spec)
640
  return nodefactory.vkern(spec)
×
641
end
642

643
_deprecated_nodefactory.newPenalty = function (spec)
644
  return nodefactory.penalty(spec)
×
645
end
646

647
_deprecated_nodefactory.newDiscretionary = function (spec)
648
  return nodefactory.discretionary(spec)
×
649
end
650

651
_deprecated_nodefactory.newVbox = function (spec)
652
  return nodefactory.vbox(spec)
×
653
end
654

655
_deprecated_nodefactory.newMigrating = function (spec)
656
  return nodefactory.migrating(spec)
×
657
end
658

659
_deprecated_nodefactory.zeroGlue = function ()
660
  return nodefactory.glue()
×
661
end
662

663
_deprecated_nodefactory.hfillGlue = function ()
664
  return nodefactory.hfillglue()
×
665
end
666

667
_deprecated_nodefactory.vfillGlue = function ()
668
  return nodefactory.vfillglue()
×
669
end
670

671
_deprecated_nodefactory.hssGlue = function ()
672
  return nodefactory.hssglue()
×
673
end
674

675
_deprecated_nodefactory.vssGlue = function ()
676
  return nodefactory.vssglue()
×
677
end
678

679
_deprecated_nodefactory.zeroHbox = function ()
680
  return nodefactory.zerohbox()
×
681
end
682

683
_deprecated_nodefactory.zeroVglue = function ()
684
  return nodefactory.zerovglue()
×
685
end
686

687
setmetatable(nodefactory, {
692✔
688
    __index = function (_, prop)
689
      if _deprecated_nodefactory[prop] then
×
690
        SU.deprecated("SILE.nodefactory." .. prop, "SILE.nodefactory." .. prop:match("n?e?w?(.*)"):lower(), "0.10.0", "0.14.0")
×
691
      elseif type(prop) == "number" then -- luacheck: ignore 542
×
692
        -- Likely at attempt to iterate (or dump) the table, sort of safe to ignore
693
      else
694
        SU.error("Attempt to access non-existent SILE.nodefactory." .. prop)
×
695
      end
696
    end
697
  })
698

699
return nodefactory
346✔
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