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

sile-typesetter / sile / 6876872636

15 Nov 2023 11:50AM UTC coverage: 68.751% (+6.5%) from 62.266%
6876872636

Pull #1904

github

web-flow
Merge pull request #1908 from alerque/reload-package
Pull Request #1904: Merge develop into master (commit to next release being breaking)

44 of 103 new or added lines in 14 files covered. (42.72%)

191 existing lines in 7 files now uncovered.

10699 of 15562 relevant lines covered (68.75%)

6786.21 hits per line

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

80.43
/classes/base.lua
1
local class = pl.class()
343✔
2
class.type = "class"
343✔
3
class._name = "base"
343✔
4

5
class._initialized = false
343✔
6
class.deferredLegacyInit = {}
343✔
7
class.deferredInit = {}
343✔
8
class.pageTemplate = { frames = {}, firstContentFrame = nil }
343✔
9
class.defaultFrameset = {}
343✔
10
class.firstContentFrame = "page"
343✔
11
class.options = setmetatable({}, {
686✔
12
    _opts = {},
343✔
13
    __newindex = function (self, key, value)
14
      local opts = getmetatable(self)._opts
1,055✔
15
      if type(opts[key]) == "function" then
1,055✔
16
        opts[key](class, value)
716✔
17
      elseif type(value) == "function" then
697✔
18
        opts[key] = value
696✔
19
      elseif type(key) == "number" then
1✔
20
        return nil
1✔
21
      else
22
        SU.error("Attempted to set an undeclared class option '" .. key .. "'")
×
23
      end
24
    end,
25
    __index = function (self, key)
26
      if key == "super" then return nil end
181✔
27
      if type(key) == "number" then return nil end
181✔
28
      local opt = getmetatable(self)._opts[key]
181✔
29
      if type(opt) == "function" then
181✔
30
        return opt(class)
181✔
31
      elseif opt then
×
32
        return opt
×
33
      else
34
        SU.error("Attempted to get an undeclared class option '" .. key .. "'")
×
35
      end
36
    end
37
  })
343✔
38
class.hooks = {
343✔
39
  newpage = {},
343✔
40
  endpage = {},
343✔
41
  finish = {},
343✔
42
}
343✔
43

44
class.packages = {}
343✔
45

46
function class:_init (options)
343✔
47
  SILE.scratch.half_initialized_class = self
172✔
48
  if self == options then options = {} end
172✔
49
  SILE.languageSupport.loadLanguage('und') -- preload for unlocalized fallbacks
172✔
50
  self:declareOptions()
172✔
51
  self:registerRawHandlers()
172✔
52
  self:declareSettings()
172✔
53
  self:registerCommands()
172✔
54
  self:setOptions(options)
172✔
55
  self:declareFrames(self.defaultFrameset)
172✔
56
  self:registerPostinit(function (self_)
344✔
57
      if type(self.firstContentFrame) == "string" then
172✔
58
        self_.pageTemplate.firstContentFrame = self_.pageTemplate.frames[self_.firstContentFrame]
172✔
59
      end
60
      local frame = self_:initialFrame()
172✔
61
      SILE.typesetter = SILE.typesetters.base(frame)
344✔
62
      SILE.typesetter:registerPageEndHook(function ()
344✔
63
        SU.debug("frames", function ()
444✔
64
          for _, v in pairs(SILE.frames) do SILE.outputter:debugFrame(v) end
×
65
          return "Drew debug outlines around frames"
×
66
        end)
67
      end)
68
    end)
69
end
70

71
function class:_post_init ()
343✔
72
  self._initialized = true
172✔
73
  for i, func in ipairs(self.deferredInit) do
424✔
74
    func(self)
252✔
75
    self.deferredInit[i] = nil
252✔
76
  end
77
  SILE.scratch.half_initialized_class = nil
172✔
78
end
79

80
function class:setOptions (options)
343✔
81
  options = options or {}
172✔
82
  -- Classes that add options with dependencies should explicitly handle them, then exempt them from furthur processing.
83
  -- The landscape option is handled explicitly before papersize, then the "rest" of options that are not interdependent.
84
  self.options.landscape = SU.boolean(options.landscape, false)
344✔
85
  options.landscape = nil
172✔
86
  self.options.papersize = options.papersize or "a4"
172✔
87
  options.papersize = nil
172✔
88
  for option, value in pairs(options) do
187✔
89
    self.options[option] = value
15✔
90
  end
91
end
92

93
function class:declareOption (option, setter)
343✔
94
  rawset(getmetatable(self.options)._opts, option, nil)
696✔
95
  self.options[option] = setter
696✔
96
end
97

98
function class:declareOptions ()
343✔
99
  self:declareOption("class", function (_, name)
344✔
100
    if name then
×
101
      if self._legacy then
×
102
        self._name = name
×
103
      elseif name ~= self._name then
×
104
        SU.error("Cannot change class name after instantiation, derive a new class instead.")
×
105
      end
106
    end
107
    return self._name
×
108
  end)
109
  self:declareOption("landscape", function(_, landscape)
344✔
110
    if landscape then
345✔
111
      self.landscape = landscape
1✔
112
    end
113
    return self.landscape
345✔
114
  end)
115
  self:declareOption("papersize", function (_, size)
344✔
116
    if size then
172✔
117
      self.papersize = size
172✔
118
      SILE.documentState.paperSize = SILE.papersize(size, self.options.landscape)
516✔
119
      SILE.documentState.orgPaperSize = SILE.documentState.paperSize
172✔
120
      SILE.newFrame({
344✔
121
        id = "page",
122
        left = 0,
123
        top = 0,
124
        right = SILE.documentState.paperSize[1],
172✔
125
        bottom = SILE.documentState.paperSize[2]
172✔
126
      })
127
    end
128
    return self.papersize
172✔
129
  end)
130
end
131

132
function class.declareSettings (_)
343✔
133
  SILE.settings:declare({
172✔
134
    parameter = "current.parindent",
135
    type = "glue or nil",
136
    default = nil,
137
    help = "Glue at start of paragraph"
×
138
  })
139
  SILE.settings:declare({
172✔
140
    parameter = "current.hangIndent",
141
    type = "measurement or nil",
142
    default = nil,
143
    help = "Size of hanging indent"
×
144
  })
145
  SILE.settings:declare({
172✔
146
    parameter = "current.hangAfter",
147
    type = "integer or nil",
148
    default = nil,
149
    help = "Number of lines affected by handIndent"
×
150
  })
151
end
152

153
function class:loadPackage (packname, options, reload)
343✔
154
  local pack = require(("packages.%s"):format(packname))
948✔
155
  if type(pack) == "table" and pack.type == "package" then -- new package
948✔
156
    self.packages[pack._name] = pack(options, reload)
1,896✔
157
  else -- legacy package
158
    self:initPackage(pack, options)
×
159
  end
160
end
161

162
function class:reloadPackage (packname, options)
343✔
NEW
163
  return self:loadPackage(packname, options, true)
×
164
end
165

166
function class:initPackage (pack, options)
343✔
167
  SU.deprecated("class:initPackage(options)", "package(options)", "0.14.0", "0.16.0", [[
×
168
  This package appears to be a legacy format package. It returns a table
169
  an expects SILE to guess a bit about what to do. New packages inherit
170
  from the base class and have a constructor function (_init) that
171
  automatically handles setup.]])
×
172
  if type(pack) == "table" then
×
173
    if pack.exports then pl.tablex.update(self, pack.exports) end
×
174
    if type(pack.declareSettings) == "function" then
×
175
      pack.declareSettings(self)
×
176
    end
177
    if type(pack.registerRawHandlers) == "function" then
×
178
      pack.registerRawHandlers(self)
×
179
    end
180
    if type(pack.registerCommands) == "function" then
×
181
      pack.registerCommands(self)
×
182
    end
183
    if type(pack.init) == "function" then
×
184
      self:registerPostinit(pack.init, options)
×
185
    end
186
  end
187
end
188

189
function class:registerLegacyPostinit (func, options)
343✔
190
  if self._initialized then return func(self, options) end
×
191
  table.insert(self.deferredLegacyInit, function (_)
×
192
      func(self, options)
×
193
    end)
194
end
195

196
function class:registerPostinit (func, options)
343✔
197
  if self._initialized then return func(self, options) end
274✔
198
  table.insert(self.deferredInit, function (_)
504✔
199
      func(self, options)
252✔
200
    end)
201
end
202

203
function class:registerHook (category, func)
343✔
204
  for _, func_ in ipairs(self.hooks[category]) do
752✔
205
    if func_ == func then
238✔
UNCOV
206
      return
×
207
      --[[ See https://github.com/sile-typesetter/sile/issues/1531
208
      return SU.warn("Attempted to set the same function hook twice, probably unintended, skipping.")
209
      -- If the same function signature is already set a package is probably being
210
      -- re-initialized. Ditch the first instance of the hook so that it runs in
211
      -- the order of last initialization.
212
      self.hooks[category][_] = nil
213
      ]]
214
    end
215
  end
216
  table.insert(self.hooks[category], func)
514✔
217
end
218

219
function class:runHooks (category, options)
343✔
220
  for _, func in ipairs(self.hooks[category]) do
952✔
221
    SU.debug("classhooks", "Running hook from", category, options and "with options " .. #options)
496✔
222
    func(self, options)
496✔
223
  end
224
end
225

226
function class.registerCommand (_, name, func, help, pack)
343✔
227
  SILE.Commands[name] = func
18,262✔
228
  if not pack then
18,262✔
229
    local where = debug.getinfo(2).source
18,247✔
230
    pack = where:match("(%w+).lua")
18,247✔
231
  end
232
  --if not help and not pack:match(".sil") then SU.error("Could not define command '"..name.."' (in package "..pack..") - no help text" ) end
233
  SILE.Help[name] = {
18,262✔
234
    description = help,
18,262✔
235
    where = pack
18,262✔
236
  }
18,262✔
237
end
238

239
function class.registerRawHandler (_, format, callback)
343✔
240
  SILE.rawHandlers[format] = callback
176✔
241
end
242

243
function class:registerRawHandlers ()
343✔
244

245
  self:registerRawHandler("text", function (_, content)
344✔
246
    SILE.settings:temporarily(function()
2✔
247
      SILE.settings:set("typesetter.parseppattern", "\n")
1✔
248
      SILE.settings:set("typesetter.obeyspaces", true)
1✔
249
      SILE.typesetter:typeset(content[1])
1✔
250
    end)
251
  end)
252

253
end
254

255
local function packOptions (options)
256
  local relevant = pl.tablex.copy(options)
147✔
257
  relevant.src = nil
147✔
258
  relevant.format = nil
147✔
259
  relevant.module = nil
147✔
260
  relevant.require = nil
147✔
261
  return relevant
147✔
262
end
263

264
function class:registerCommands ()
343✔
265

266
  local function replaceProcessBy(replacement, tree)
267
    if type(tree) ~= "table" then return tree end
175✔
268
    local ret = pl.tablex.deepcopy(tree)
106✔
269
    if tree.command == "process" then
106✔
270
      return replacement
7✔
271
    else
272
      for i, child in ipairs(tree) do
231✔
273
        ret[i] = replaceProcessBy(replacement, child)
264✔
274
      end
275
      return ret
99✔
276
    end
277
  end
278

279
  self:registerCommand("define", function (options, content)
344✔
280
    SU.required(options, "command", "defining command")
15✔
281
    if type(content) == "function" then
15✔
282
      -- A macro defined as a function can take no argument, so we register
283
      -- it as-is.
284
      self:registerCommand(options["command"], content)
×
285
      return
×
286
    elseif options.command == "process" then
15✔
287
      SU.warn("Did you mean to re-definine the `\\process` macro? That probably won't go well.")
×
288
    end
289
    self:registerCommand(options["command"], function (_, inner_content)
30✔
290
      SU.debug("macros", "Processing macro \\" .. options["command"])
43✔
291
      local macroArg
292
      if type(inner_content) == "function" then
43✔
293
        macroArg = inner_content
2✔
294
      elseif type(inner_content) == "table" then
41✔
295
        macroArg = pl.tablex.copy(inner_content)
78✔
296
        macroArg.command = nil
39✔
297
        macroArg.id = nil
39✔
298
      elseif inner_content == nil then
2✔
299
        macroArg = {}
2✔
300
      else
301
        SU.error("Unhandled content type " .. type(inner_content) .. " passed to macro \\" .. options["command"], true)
×
302
      end
303
      -- Replace every occurrence of \process in `content` (the macro
304
      -- body) with `macroArg`, then have SILE go through the new `content`.
305
      local newContent = replaceProcessBy(macroArg, content)
43✔
306
      SILE.process(newContent)
43✔
307
      SU.debug("macros", "Finished processing \\" .. options["command"])
43✔
308
    end, options.help, SILE.currentlyProcessingFile)
58✔
309
  end, "Define a new macro. \\define[command=example]{ ... \\process }")
187✔
310

311
  -- A utility function that allows SILE.call() to be used as a noop wrapper.
312
  self:registerCommand("noop", function (_, content)
344✔
313
    SILE.process(content)
1✔
314
  end)
315

316
  -- The document (SIL) or sile (XML) command is always the sigular leaf at the
317
  -- top level of our AST. The work you might expect to see happen here is
318
  -- actually handled by SILE.inputter:classInit() before we get here, so these
319
  -- are just pass through functions. Theoretically, this could be a useful
320
  -- point to hook into-especially for included documents.
321
  self:registerCommand("document", function (_, content)
344✔
322
    SILE.process(content)
×
323
  end)
324
  self:registerCommand("sile", function (_, content)
344✔
325
    SILE.process(content)
×
326
  end)
327

328
  self:registerCommand("comment", function (_, _)
344✔
329
  end, "Ignores any text within this command's body.")
172✔
330

331
  self:registerCommand("process", function ()
344✔
332
    SU.error("Encountered unsubstituted \\process.")
×
333
  end, "Within a macro definition, processes the contents of the macro body.")
172✔
334

335
  self:registerCommand("script", function (options, content)
344✔
336
    local packopts = packOptions(options)
37✔
337
    if SU.hasContent(content) then
74✔
338
      return SILE.processString(content[1], options.format or "lua", nil, packopts)
32✔
339
    elseif options.src then
5✔
340
      return SILE.require(options.src)
5✔
341
    else
342
      SU.error("\\script function requires inline content or a src file path")
×
343
      return SILE.processString(content[1], options.format or "lua", nil, packopts)
×
344
    end
345
  end, "Runs lua code. The code may be supplied either inline or using src=...")
172✔
346

347
  self:registerCommand("include", function (options, content)
344✔
348
    local packopts = packOptions(options)
1✔
349
    if SU.hasContent(content) then
2✔
350
      local doc = SU.contentToString(content)
×
351
      return SILE.processString(doc, options.format, nil, packopts)
×
352
    elseif options.src then
1✔
353
      return SILE.processFile(options.src, options.format, packopts)
1✔
354
    else
355
      SU.error("\\include function requires inline content or a src file path")
×
356
    end
357
  end, "Includes a content file for processing.")
172✔
358

359
  self:registerCommand("lua", function (options, content)
344✔
360
    local packopts = packOptions(options)
3✔
361
    if SU.hasContent(content) then
6✔
362
      local doc = SU.contentToString(content)
3✔
363
      return SILE.processString(doc, "lua", nil, packopts)
3✔
364
    elseif options.src then
×
365
      return SILE.processFile(options.src, "lua", packopts)
×
366
    elseif options.require then
×
367
      local module = SU.required(options, "require", "lua")
×
368
      return require(module)
×
369
    else
370
      SU.error("\\lua function requires inline content or a src file path or a require module name")
×
371
    end
372
  end, "Run Lua code. The code may be supplied either inline, using require=... for a Lua module, or using src=... for a file path")
172✔
373

374
  self:registerCommand("sil", function (options, content)
344✔
375
    local packopts = packOptions(options)
×
376
    if SU.hasContent(content) then
×
377
      local doc = SU.contentToString(content)
×
378
      return SILE.processString(doc, "sil")
×
379
    elseif options.src then
×
380
      return SILE.processFile(options.src, "sil", packopts)
×
381
    else
382
      SU.error("\\sil function requires inline content or a src file path")
×
383
    end
384
  end, "Process sil content. The content may be supplied either inline or using src=...")
172✔
385

386
  self:registerCommand("xml", function (options, content)
344✔
387
    local packopts = packOptions(options)
×
388
    if SU.hasContent(content) then
×
389
      local doc = SU.contentToString(content)
×
390
      return SILE.processString(doc, "xml", nil, packopts)
×
391
    elseif options.src then
×
392
      return SILE.processFile(options.src, "xml", packopts)
×
393
    else
394
      SU.error("\\xml function requires inline content or a src file path")
×
395
    end
396
  end, "Process xml content. The content may be supplied either inline or using src=...")
172✔
397

398
  self:registerCommand("use", function (options, content)
344✔
399
    local packopts = packOptions(options)
106✔
400
    if content[1] and string.len(content[1]) > 0 then
106✔
401
      local doc = SU.contentToString(content)
×
402
      SILE.processString(doc, "lua", nil, packopts)
×
403
    else
404
      if options.src then
106✔
405
        SU.warn("Use of 'src' with \\use is discouraged because some of it's path handling\n  will eventually be deprecated. Use 'module' instead when possible.")
×
406
        SILE.processFile(options.src, "lua", packopts)
×
407
      else
408
        local module = SU.required(options, "module", "use")
106✔
409
        SILE.use(module, packopts)
106✔
410
      end
411
    end
412
  end, "Load and initialize a SILE module (can be a package, a shaper, a typesetter, or whatever). Use module=... to specif what to load or include module code inline.")
278✔
413

414
  self:registerCommand("raw", function (options, content)
344✔
415
    local rawtype = SU.required(options, "type", "raw")
4✔
416
    local handler = SILE.rawHandlers[rawtype]
4✔
417
    if not handler then SU.error("No inline handler for '"..rawtype.."'") end
4✔
418
    handler(options, content)
4✔
419
  end, "Invoke a raw passthrough handler")
176✔
420

421
  self:registerCommand("pagetemplate", function (options, content)
344✔
422
    SILE.typesetter:pushState()
8✔
423
    SILE.documentState.thisPageTemplate = { frames = {} }
8✔
424
    SILE.process(content)
8✔
425
    SILE.documentState.thisPageTemplate.firstContentFrame = SILE.getFrame(options["first-content-frame"])
16✔
426
    SILE.typesetter:initFrame(SILE.documentState.thisPageTemplate.firstContentFrame)
8✔
427
    SILE.typesetter:popState()
8✔
428
  end, "Defines a new page template for the current page and sets the typesetter to use it.")
180✔
429

430
  self:registerCommand("frame", function (options, _)
344✔
431
    SILE.documentState.thisPageTemplate.frames[options.id] = SILE.newFrame(options)
62✔
432
  end, "Declares (or re-declares) a frame on this page.")
203✔
433

434
  self:registerCommand("penalty", function (options, _)
344✔
435
    if SU.boolean(options.vertical, false) and not SILE.typesetter:vmode() then
576✔
436
      SILE.typesetter:leaveHmode()
4✔
437
    end
438
    if SILE.typesetter:vmode() then
552✔
439
      SILE.typesetter:pushVpenalty({ penalty = tonumber(options.penalty) })
438✔
440
    else
441
      SILE.typesetter:pushPenalty({ penalty = tonumber(options.penalty) })
57✔
442
    end
443
  end, "Inserts a penalty node. Option is penalty= for the size of the penalty.")
448✔
444

445
  self:registerCommand("discretionary", function (options, _)
344✔
446
    local discretionary = SILE.nodefactory.discretionary({})
74✔
447
    if options.prebreak then
74✔
448
      local hbox = SILE.typesetter:makeHbox({ options.prebreak })
74✔
449
      discretionary.prebreak = { hbox }
74✔
450
    end
451
    if options.postbreak then
74✔
452
      local hbox = SILE.typesetter:makeHbox({ options.postbreak })
48✔
453
      discretionary.postbreak = { hbox }
48✔
454
    end
455
    if options.replacement then
74✔
456
      local hbox = SILE.typesetter:makeHbox({ options.replacement })
50✔
457
      discretionary.replacement = { hbox }
50✔
458
    end
459
    table.insert(SILE.typesetter.state.nodes, discretionary)
74✔
460
  end, "Inserts a discretionary node.")
246✔
461

462
  self:registerCommand("glue", function (options, _)
344✔
463
    local width = SU.cast("length", options.width):absolute()
106✔
464
    SILE.typesetter:pushGlue(width)
53✔
465
  end, "Inserts a glue node. The width option denotes the glue dimension.")
225✔
466

467
  self:registerCommand("kern", function (options, _)
344✔
468
    local width = SU.cast("length", options.width):absolute()
150✔
469
    SILE.typesetter:pushHorizontal(SILE.nodefactory.kern(width))
150✔
470
  end, "Inserts a glue node. The width option denotes the glue dimension.")
247✔
471

472
  self:registerCommand("skip", function (options, _)
344✔
473
    options.discardable = SU.boolean(options.discardable, false)
58✔
474
    options.height = SILE.length(options.height):absolute()
87✔
475
    SILE.typesetter:leaveHmode()
29✔
476
    if options.discardable then
29✔
477
      SILE.typesetter:pushVglue(options)
×
478
    else
479
      SILE.typesetter:pushExplicitVglue(options)
29✔
480
    end
481
  end, "Inserts vertical skip. The height options denotes the skip dimension.")
201✔
482

483
  self:registerCommand("par", function (_, _)
344✔
484
    SILE.typesetter:endline()
287✔
485
  end, "Ends the current paragraph.")
459✔
486

487
end
488

489
function class:initialFrame ()
343✔
490
  SILE.documentState.thisPageTemplate = pl.tablex.deepcopy(self.pageTemplate)
456✔
491
  SILE.frames = { page = SILE.frames.page }
228✔
492
  for k, v in pairs(SILE.documentState.thisPageTemplate.frames) do
989✔
493
    SILE.frames[k] = v
761✔
494
  end
495
  if not SILE.documentState.thisPageTemplate.firstContentFrame then
228✔
496
    SILE.documentState.thisPageTemplate.firstContentFrame = SILE.frames[self.firstContentFrame]
×
497
  end
498
  SILE.documentState.thisPageTemplate.firstContentFrame:invalidate()
228✔
499
  return SILE.documentState.thisPageTemplate.firstContentFrame
228✔
500
end
501

502
function class:declareFrame (id, spec)
343✔
503
  spec.id = id
564✔
504
  if spec.solve then
564✔
505
    self.pageTemplate.frames[id] = spec
×
506
  else
507
    self.pageTemplate.frames[id] = SILE.newFrame(spec)
1,128✔
508
  end
509
  --   next = spec.next,
510
  --   left = spec.left and fW(spec.left),
511
  --   right = spec.right and fW(spec.right),
512
  --   top = spec.top and fH(spec.top),
513
  --   bottom = spec.bottom and fH(spec.bottom),
514
  --   height = spec.height and fH(spec.height),
515
  --   width = spec.width and fH(spec.width),
516
  --   id = id
517
  -- })
518
end
519

520
function class:declareFrames (specs)
343✔
521
  if specs then
172✔
522
    for k, v in pairs(specs) do self:declareFrame(k, v) end
1,268✔
523
  end
524
end
525

526
-- WARNING: not called as class method
527
function class.newPar (typesetter)
343✔
528
  local parindent = SILE.settings:get("current.parindent") or SILE.settings:get("document.parindent")
1,646✔
529
  -- See https://github.com/sile-typesetter/sile/issues/1361
530
  -- The parindent *cannot* be pushed non-absolutized, as it may be evaluated
531
  -- outside the (possibly temporary) setting scope where it was used for line
532
  -- breaking.
533
  -- Early absolutization can be problematic sometimes, but here we do not
534
  -- really have the choice.
535
  -- As of problematic cases, consider a parindent that would be defined in a
536
  -- frame-related unit (%lw, %fw, etc.). If a frame break occurs and the next
537
  -- frame has a different width, the parindent won't be re-evaluated in that
538
  -- new frame context. However, defining a parindent in such a unit is quite
539
  -- unlikely. And anyway pushback() has plenty of other issues.
540
  typesetter:pushGlue(parindent:absolute())
1,646✔
541
  SILE.settings:set("current.parindent", nil)
823✔
542
  local hangIndent = SILE.settings:get("current.hangIndent")
823✔
543
  if hangIndent then
823✔
544
    SILE.settings:set("linebreak.hangIndent", hangIndent)
5✔
545
  end
546
  local hangAfter = SILE.settings:get("current.hangAfter")
823✔
547
  if hangAfter then
823✔
548
    SILE.settings:set("linebreak.hangAfter", hangAfter)
5✔
549
  end
550
end
551

552
-- WARNING: not called as class method
553
function class.endPar (typesetter)
343✔
554
  typesetter:pushVglue(SILE.settings:get("document.parskip"))
1,608✔
555
  if SILE.settings:get("current.hangIndent") then
1,608✔
556
    SILE.settings:set("current.hangIndent", nil)
4✔
557
    SILE.settings:set("linebreak.hangIndent", nil)
4✔
558
  end
559
  if SILE.settings:get("current.hangAfter") then
1,608✔
560
    SILE.settings:set("current.hangAfter", nil)
4✔
561
    SILE.settings:set("linebreak.hangAfter", nil)
4✔
562
  end
563
end
564

565
function class:newPage ()
343✔
566
  SILE.outputter:newPage()
56✔
567
  self:runHooks("newpage")
56✔
568
  -- Any other output-routiney things will be done here by inheritors
569
  return self:initialFrame()
56✔
570
end
571

572
function class:endPage ()
343✔
573
  SILE.typesetter.frame:leave(SILE.typesetter)
228✔
574
  self:runHooks("endpage")
228✔
575
  -- I'm trying to call up a new frame here, don't cause a page break in the current one
576
  -- SILE.typesetter:leaveHmode()
577
  -- Any other output-routiney things will be done here by inheritors
578
end
579

580
function class:finish ()
343✔
581
  SILE.inputter:postamble()
172✔
582
  SILE.call("vfill")
172✔
583
  while not SILE.typesetter:isQueueEmpty() do
688✔
584
    SILE.call("supereject")
172✔
585
    SILE.typesetter:leaveHmode(true)
172✔
586
    SILE.typesetter:buildPage()
172✔
587
    if not SILE.typesetter:isQueueEmpty() then
344✔
588
      SILE.typesetter:initNextFrame()
5✔
589
    end
590
  end
591
  SILE.typesetter:runHooks("pageend") -- normally run by the typesetter
172✔
592
  self:endPage()
172✔
593
  if SILE.typesetter and not SILE.typesetter:isQueueEmpty() then
344✔
594
    SU.error("Queues are not empty as expected after ending last page", true)
×
595
  end
596
  SILE.outputter:finish()
172✔
597
  self:runHooks("finish")
172✔
598
end
599

600
return class
343✔
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