• 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

0.0
/packages/twoside/init.lua
1
local base = require("packages.base")
×
2

3
local package = pl.class(base)
×
4
package._name = "twoside"
×
5

6
local _odd = true
×
7

8
local mirrorMaster = function (_, existing, new)
9
  -- Frames in one master can't "see" frames in another, so we have to get creative
10
  -- XXX This knows altogether too much about the implementation of masters
11
  if not SILE.scratch.masters[new] then SILE.scratch.masters[new] = {frames={}} end
×
12
  if not SILE.scratch.masters[existing] then
×
13
    SU.error("Can't find master "..existing)
×
14
  end
15
  for name, frame in pairs(SILE.scratch.masters[existing].frames) do
×
16
    local newframe = pl.tablex.deepcopy(frame)
×
17
    if frame:isAbsoluteConstraint("right") then
×
18
      newframe.constraints.left = "100%pw-("..frame.constraints.right..")"
×
19
    end
20
    if frame:isAbsoluteConstraint("left") then
×
21
      newframe.constraints.right = "100%pw-("..frame.constraints.left..")"
×
22
    end
23
    SILE.scratch.masters[new].frames[name] = newframe
×
24
    if frame == SILE.scratch.masters[existing].firstContentFrame then
×
25
      SILE.scratch.masters[new].firstContentFrame = newframe
×
26
    end
27
  end
28
end
29

30
function package.oddPage (_)
×
31
  return _odd
×
32
end
33

34
local function switchPage (class)
35
  _odd = not class:oddPage()
×
36
  local nextmaster = _odd and class.oddPageMaster or class.evenPageMaster
×
37
  class:switchMaster(nextmaster)
×
38
end
39

40
local _deprecate  = [[
41
  Directly calling master switch handling functions is no longer necessary. All
42
  the SILE core classes and anything inheriting from them will take care of this
43
  automatically using hooks. Custom classes that override the class:newPage()
44
  function may need to handle this in other ways. By calling this hook directly
45
  you are likely causing it to run twice and duplicate entries.
46
]]
×
47

48
local spread_counter = 0
×
49
local spreadHook = function ()
50
  spread_counter = spread_counter + 1
×
51
end
52

53
function package:_init (options)
×
54
  base._init(self)
×
55
  if not SILE.scratch.masters then
×
56
    SU.error("Cannot load twoside package before masters.")
×
57
  end
58
  self:export("oddPage", self.oddPage)
×
59
  self:export("mirrorMaster", mirrorMaster)
×
60
  self:export("switchPage", function (class)
×
61
    SU.deprecated("class:switchPage", nil, "0.13.0", "0.15.0", _deprecate)
×
62
    return class:switchPage()
×
63
  end)
64
  self.class.oddPageMaster = options.oddPageMaster
×
65
  self.class.evenPageMaster = options.evenPageMaster
×
66
  self.class:registerPostinit(function (class)
×
67
    -- TODO: Refactor this to make mirroring a separate package / option
68
    if not SILE.scratch.masters[options.evenPageMaster] then
×
69
      class:mirrorMaster(options.oddPageMaster, options.evenPageMaster)
×
70
    end
71
  end)
72
  self.class:registerHook("newpage", spreadHook)
×
73
  self.class:registerHook("newpage", switchPage)
×
74
end
75

76
function package:registerCommands ()
×
77

78
  self:registerCommand("open-double-page", function()
×
79
    SILE.call("open-spread", { double = false, odd = true, blank = false })
×
80
  end)
81

82
  self:registerCommand("open-spread-eject", function()
×
83
    SILE.call("supereject")
×
84
  end)
85

86
  -- This is upstreamed from CaSILE. Similar to the original open-double-page,
87
  -- but can disable headers and folios on blank pages and allows opening the
88
  -- even side (with or without a leading blank).
89
  self:registerCommand("open-spread", function (options)
×
90
    local odd = SU.boolean(options.odd, true)
×
91
    local double = SU.boolean(options.double, true)
×
92
    local blank = SU.boolean(options.blank, true)
×
93
    local optionsMet = function ()
94
      return (not double or spread_counter > 1) and
×
95
             (odd == self.class:oddPage())
×
96
    end
97
    spread_counter = 0
×
98
    SILE.typesetter:leaveHmode()
×
99
    -- Output a box, then remove it and see where we are. Without adding
100
    -- content we can't prove on which page we would land because the page
101
    -- breaker *might* be stuffed almost full but still sitting on the last
102
    -- line happy to *maybe* accept more letter (but not a line). If this check
103
    -- gets us to the desired page nuke the vertical space so we don't leak it
104
    -- into the final content, otherwise just leave it be since we want to be
105
    -- forced to the next page anyway.
106
    SILE.call("hbox")
×
107
    SILE.typesetter:leaveHmode()
×
108
    table.remove(SILE.typesetter.state.nodes)
×
109
    if spread_counter == 1 and optionsMet() then
×
110
      SILE.typesetter.state.outputQueue = {}
×
111
      return
×
112
    end
113
    local startedattop = #SILE.typesetter.state.outputQueue == 2 and #SILE.typesetter.state.nodes == 0
×
114
    local spread_counter_at_start = spread_counter
×
115
    repeat
116
      if spread_counter > 0 then
×
117
        SILE.call("hbox")
×
118
        SILE.typesetter:leaveHmode()
×
119
        -- Note: before you think you can simplify this, make sure all the
120
        -- pages before chapter starts in the manual have headers if they have
121
        -- content and not if empty. Combined with the workaround for just
122
        -- barely full pages above it's tricky to get right.
123
        if blank and not (spread_counter == spread_counter_at_start and not startedattop) then
×
124
          SILE.scratch.headers.skipthispage = true
×
125
          SILE.call("nofoliothispage")
×
126
        end
127
      end
128
      SILE.call("open-spread-eject")
×
129
      SILE.typesetter:leaveHmode()
×
130
    until optionsMet()
×
131
  end)
132

133
end
134

135
package.documentation = [[
136
\begin{document}
137
A book-like class usually sets up left and right mirrored page masters.
138
The \autodoc:package{twoside} package is then responsible for swapping between the two left and right frames, running headers, and so on.
139
As it is normally loaded and initialized by a document class, its main function in mirroring master frames does not provide any user-serviceable parts.
140
It does supply a few user-facing commands for convenience.
141

142
The \autodoc:command{\open-double-page} ejects whatever page is currently being processed, then checks if it landed on an even page.
143
If so, it ejects another page to assure content starts on an odd page.
144

145
The \autodoc:command{\open-spread} is similar but a bit more tailored to use in book layouts.
146
By default, headers and folios will be suppressed automatically on any empty pages ejected, making them blank.
147
It can also accept three parameters.
148
The \autodoc:parameter{odd} parameter (default \code{true}) can be used to disable the opening page being odd, hence opening an even page spread.
149
The \autodoc:parameter{double} parameter (default \code{true}) can be used to always output at least one empty even page before the starting an odd page.
150
The \autodoc:parameter{blank} parameter (default \code{true}) can be used to not suppress headers and folios on otherwise empty pages.
151

152
Lastly the \autodoc:command{\open-spread-eject} command can be overridden to customize the output of blank pages.
153
By default it just runs \autodoc:command{\supereject}, but you could potentially add decorative content or other features in the otherwise empty space.
154
\end{document}
155
]]
×
156

157
return package
×
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