-
-
Notifications
You must be signed in to change notification settings - Fork 97
/
init.lua
145 lines (120 loc) · 5.71 KB
/
init.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
local base = require("packages.base")
local package = pl.class(base)
package._name = "leaders"
--
-- Leaders package
--
local widthToFrameEdge = function (frame)
local w
if frame:writingDirection() == "LTR" then
w = frame:right() - frame.state.cursorX
elseif frame:writingDirection() == "RTL" then
w = frame.state.cursorX - frame:left()
elseif frame:writingDirection() == "TTB" then
w = frame:bottom() - frame.state.cursorY
elseif frame:writingDirection() == "BTT" then
w = frame.state.cursorY - frame:top()
else
SU.error("Unknown writing direction")
end
return w
end
local leader = pl.class(SILE.nodefactory.glue)
function leader:outputYourself (typesetter, line)
local outputWidth = SU.rationWidth(self.width, self.width, line.ratio):tonumber()
local leaderWidth = self.value.width:tonumber()
local ox = typesetter.frame.state.cursorX
local oy = typesetter.frame.state.cursorY
-- Implementation note:
-- We want leaders on different lines to be aligned vertically in the frame,
-- from its end edge (e.g. the right edge in LTR writing direction).
-- Compute how many leaders we can fit from the current initial position.
local fitWidth = widthToFrameEdge(typesetter.frame):tonumber()
local maxRepetitions = math.floor(fitWidth / leaderWidth) -- round down!
-- Compute how many leaders we have to skip after our final position.
typesetter.frame:advanceWritingDirection(outputWidth)
local skipWidth = widthToFrameEdge(typesetter.frame):tonumber()
skipWidth = math.floor(skipWidth * 1e6) / 1e6 -- accept some rounding imprecision
local skipRepetitions = math.ceil(skipWidth / leaderWidth) -- round up!
local repetitions = maxRepetitions - skipRepetitions
local remainder = fitWidth - maxRepetitions * leaderWidth
SU.debug("leaders", "Leader repetitions:", repetitions, "skipped:", skipRepetitions, "remainder:", remainder)
-- Return back to our start position
typesetter.frame:advanceWritingDirection(-outputWidth)
-- Handle the leader element repetitions
if repetitions > 0 then
typesetter.frame:advanceWritingDirection(remainder)
for _ = 1, repetitions do
if SU.debugging("leaders") then
-- Draw some visible lines around leader repetitions.
-- N.B. This might be wrong for other directions than LTR-TTB, but heh it's debug stuff.
SILE.outputter:drawRule(typesetter.frame.state.cursorX, typesetter.frame.state.cursorY-leaderWidth, leaderWidth-0.3, 0.3)
SILE.outputter:drawRule(typesetter.frame.state.cursorX, typesetter.frame.state.cursorY-leaderWidth, 0.3, leaderWidth-0.3)
end
self.value:outputYourself(typesetter, line)
end
if SU.debugging("leaders") then
-- Draw some visible lines around skipped leader repetitions.
-- N.B. This might be wrong for other directions than LTR-TTB, but it's debug stuff again.
for _ = 0, skipRepetitions-1 do
SILE.outputter:drawRule(typesetter.frame.state.cursorX, typesetter.frame.state.cursorY, leaderWidth, 0.3)
SILE.outputter:drawRule(typesetter.frame.state.cursorX, typesetter.frame.state.cursorY-leaderWidth, 0.3, leaderWidth)
typesetter.frame:advanceWritingDirection(leaderWidth)
end
end
end
-- Return to our start (saved) position and move to the full leaders width.
-- (So we are sure to safely get the correct width, whathever we did above
-- with the remainder space and the leader repetitions).
typesetter.frame.state.cursorX = ox
typesetter.frame.state.cursorY = oy
typesetter.frame:advanceWritingDirection(outputWidth)
end
function package:registerCommands ()
self:registerCommand("leaders", function(options, content)
local width = options.width and SU.cast("glue", options.width) or SILE.nodefactory.hfillglue()
local hbox, hlist = SILE.typesetter:makeHbox(content)
if #hlist > 0 then
SU.error("Forbidden migrating content in leaders")
end
local l = leader({ width = width, value = hbox })
SILE.typesetter:pushExplicitGlue(l)
end)
self:registerCommand("dotfill", function(_, _)
-- Implementation note:
-- The "usual" space between dots in "modern days" is 0.5em (cf. "points
-- conducteurs" in Frey & Bouchez, 1857), evenly distributed around the dot,
-- though in older times it was sometimes up to 1em and could be distributed
-- differently. Anyhow, it is also the approach taken by LaTeX, with a
-- \@dotsep space of 4.5mu (where 18mu = 1em, so indeed leading to 0.25em).
SILE.call("leaders", { width = SILE.nodefactory.hfillglue() }, function()
SILE.call("kern", { width = SILE.length("0.25em") })
SILE.typesetter:typeset(".")
SILE.call("kern", {width = SILE.length("0.25em") })
end)
end)
end
package.documentation = [[
\begin{document}
The \autodoc:package{leaders} package allows you to create repeating patterns which fill a given space.
It provides the \autodoc:command{\dotfill} command, which does this:
\begin[type=autodoc:codeblock]{raw}
A\dotfill{}B
\end{raw}
\begin{examplefont}
A\dotfill{}B\par
\end{examplefont}
It also provides the \autodoc:command{\leaders[width=<dimension>]{<content>}} command which allow you to define your own leaders.
For example:
\begin[type=autodoc:codeblock]{raw}
A\leaders[width=40pt]{/\\}B
\end{raw}
\begin{examplefont}
A\leaders[width=40pt]{/\\}B\par
\end{examplefont}
If the width is omitted, the leaders extend as much as possible (as a \autodoc:command{\dotfill} or \autodoc:command{\hfill}).
Leader patterns are always vertically aligned, respectively to the end edge of the frame they appear in, for a given font.
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.
\end{document}
]]
return package