• 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/leaders/init.lua
1
local base = require("packages.base")
×
2

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

6
--
7
-- Leaders package
8
--
9

10
local widthToFrameEdge = function (frame)
11
  local w
12
  if frame:writingDirection() == "LTR" then
×
13
    w = frame:right() - frame.state.cursorX
×
14
  elseif frame:writingDirection() == "RTL" then
×
15
    w = frame.state.cursorX - frame:left()
×
16
  elseif frame:writingDirection() == "TTB" then
×
17
    w = frame:bottom() - frame.state.cursorY
×
18
  elseif frame:writingDirection() == "BTT" then
×
19
    w = frame.state.cursorY - frame:top()
×
20
  else
21
    SU.error("Unknown writing direction")
×
22
  end
23
  return w
×
24
end
25

26
local leader = pl.class(SILE.nodefactory.glue)
×
27

28
function leader:outputYourself (typesetter, line)
×
29
  local outputWidth = SU.rationWidth(self.width, self.width, line.ratio):tonumber()
×
30
  local leaderWidth = self.value.width:tonumber()
×
31
  local ox = typesetter.frame.state.cursorX
×
32
  local oy = typesetter.frame.state.cursorY
×
33

34
  -- Implementation note:
35
  -- We want leaders on different lines to be aligned vertically in the frame,
36
  -- from its end edge (e.g. the right edge in LTR writing direction).
37

38
  -- Compute how many leaders we can fit from the current initial position.
39
  local fitWidth = widthToFrameEdge(typesetter.frame):tonumber()
×
40
  local maxRepetitions = math.floor(fitWidth / leaderWidth) -- round down!
×
41
  -- Compute how many leaders we have to skip after our final position.
42
  typesetter.frame:advanceWritingDirection(outputWidth)
×
43
  local skipWidth = widthToFrameEdge(typesetter.frame):tonumber()
×
44
  skipWidth = math.floor(skipWidth * 1e6) / 1e6 -- accept some rounding imprecision
×
45
  local skipRepetitions = math.ceil(skipWidth / leaderWidth) -- round up!
×
46

47
  local repetitions = maxRepetitions - skipRepetitions
×
48
  local remainder = fitWidth - maxRepetitions * leaderWidth
×
49
  SU.debug("leaders", "Leader repetitions:", repetitions, "skipped:", skipRepetitions, "remainder:", remainder)
×
50

51
  -- Return back to our start position
52
  typesetter.frame:advanceWritingDirection(-outputWidth)
×
53
  -- Handle the leader element repetitions
54
  if repetitions > 0 then
×
55
    typesetter.frame:advanceWritingDirection(remainder)
×
56
    for _ = 1, repetitions do
×
57
      if SU.debugging("leaders") then
×
58
        -- Draw some visible lines around leader repetitions.
59
        -- N.B. This might be wrong for other directions than LTR-TTB, but heh it's debug stuff.
60
        SILE.outputter:drawRule(typesetter.frame.state.cursorX, typesetter.frame.state.cursorY-leaderWidth, leaderWidth-0.3, 0.3)
×
61
        SILE.outputter:drawRule(typesetter.frame.state.cursorX, typesetter.frame.state.cursorY-leaderWidth, 0.3, leaderWidth-0.3)
×
62
      end
63

64
      self.value:outputYourself(typesetter, line)
×
65
    end
66

67
    if SU.debugging("leaders") then
×
68
      -- Draw some visible lines around skipped leader repetitions.
69
      -- N.B. This might be wrong for other directions than LTR-TTB, but it's debug stuff again.
70
      for _ = 0, skipRepetitions-1 do
×
71
        SILE.outputter:drawRule(typesetter.frame.state.cursorX, typesetter.frame.state.cursorY, leaderWidth, 0.3)
×
72
        SILE.outputter:drawRule(typesetter.frame.state.cursorX, typesetter.frame.state.cursorY-leaderWidth, 0.3, leaderWidth)
×
73
        typesetter.frame:advanceWritingDirection(leaderWidth)
×
74
      end
75
    end
76
  end
77
  -- Return to our start (saved) position and move to the full leaders width.
78
  -- (So we are sure to safely get the correct width, whatever we did above
79
  -- with the remainder space and the leader repetitions).
80
  typesetter.frame.state.cursorX = ox
×
81
  typesetter.frame.state.cursorY = oy
×
82
  typesetter.frame:advanceWritingDirection(outputWidth)
×
83

84
end
85

86
function package:registerCommands ()
×
87

88
  self:registerCommand("leaders", function(options, content)
×
89
    local width = options.width and SU.cast("glue", options.width) or SILE.nodefactory.hfillglue()
×
90
    local hbox, hlist = SILE.typesetter:makeHbox(content)
×
91
    if #hlist > 0 then
×
92
      SU.error("Forbidden migrating content in leaders")
×
93
    end
94
    local l = leader({ width = width, value = hbox })
×
95
    SILE.typesetter:pushExplicitGlue(l)
×
96
  end)
97

98
  self:registerCommand("dotfill", function(_, _)
×
99
    -- Implementation note:
100
    -- The "usual" space between dots in "modern days" is 0.5em (cf. "points
101
    -- conducteurs" in Frey & Bouchez, 1857), evenly distributed around the dot,
102
    -- though in older times it was sometimes up to 1em and could be distributed
103
    -- differently. Anyhow, it is also the approach taken by LaTeX, with a
104
    -- \@dotsep space of 4.5mu (where 18mu = 1em, so indeed leading to 0.25em).
105
    SILE.call("leaders", { width = SILE.nodefactory.hfillglue() }, function()
×
106
      SILE.call("kern", { width = SILE.length("0.25em") })
×
107
      SILE.typesetter:typeset(".")
×
108
      SILE.call("kern", {width = SILE.length("0.25em") })
×
109
    end)
110
  end)
111

112
end
113

114
package.documentation = [[
115
\begin{document}
116
The \autodoc:package{leaders} package allows you to create repeating patterns which fill a given space.
117
It provides the \autodoc:command{\dotfill} command, which does this:
118

119
\begin[type=autodoc:codeblock]{raw}
120
A\dotfill{}B
121
\end{raw}
122

123
\begin{autodoc:example}
124
A\dotfill{}B\par
125
\end{autodoc:example}
126

127
It also provides the \autodoc:command{\leaders[width=<dimension>]{<content>}} command which allow you to define your own leaders.
128
For example:
129

130
\begin[type=autodoc:codeblock]{raw}
131
A\leaders[width=40pt]{/\\}B
132
\end{raw}
133

134
\begin{autodoc:example}
135
A\leaders[width=40pt]{/\\}B\par
136
\end{autodoc:example}
137

138
If the width is omitted, the leaders extend as much as possible (as a \autodoc:command{\dotfill} or \autodoc:command{\hfill}).
139

140
Leader patterns are always vertically aligned, respectively to the end edge of the frame they appear in, for a given font.
141
It implies that the number of repeated patterns and their positions do not only depend on the available space, but also on the alignment constraint and the active font.
142
\end{document}
143
]]
×
144

145
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