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

sile-typesetter / sile / 6915845768

18 Nov 2023 07:23PM UTC coverage: 63.161% (-5.6%) from 68.751%
6915845768

push

github

web-flow
Merge 0f5c09a66 into f64e235fa

9802 of 15519 relevant lines covered (63.16%)

2045.54 hits per line

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

33.85
/packages/rules/init.lua
1
local base = require("packages.base")
2✔
2

3
local package = pl.class(base)
2✔
4
package._name = "rules"
2✔
5

6
local function getUnderlineParameters ()
7
  local ot = require("core.opentype-parser")
×
8
  local fontoptions = SILE.font.loadDefaults({})
×
9
  local face = SILE.font.cache(fontoptions, SILE.shaper.getFace)
×
10
  local font = ot.parseFont(face)
×
11
  local upem = font.head.unitsPerEm
×
12
  local underlinePosition = font.post.underlinePosition / upem * fontoptions.size
×
13
  local underlineThickness = font.post.underlineThickness / upem * fontoptions.size
×
14
  return underlinePosition, underlineThickness
×
15
end
16

17
local function getStrikethroughParameters ()
18
  local ot = require("core.opentype-parser")
×
19
  local fontoptions = SILE.font.loadDefaults({})
×
20
  local face = SILE.font.cache(fontoptions, SILE.shaper.getFace)
×
21
  local font = ot.parseFont(face)
×
22
  local upem = font.head.unitsPerEm
×
23
  local yStrikeoutPosition = font.os2.yStrikeoutPosition / upem * fontoptions.size
×
24
  local yStrikeoutSize = font.os2.yStrikeoutSize / upem * fontoptions.size
×
25
  return yStrikeoutPosition, yStrikeoutSize
×
26
end
27

28
-- \hfill (from the "plain" class) and \leaders (from the "leaders" package) use glues,
29
-- so we behave the same for hrulefill.
30
local hrulefillglue = pl.class(SILE.nodefactory.hfillglue)
2✔
31
hrulefillglue.raise = SILE.measurement()
4✔
32
hrulefillglue.thickness = SILE.measurement("0.2pt")
4✔
33

34
function hrulefillglue:outputYourself (typesetter, line)
2✔
35
  local outputWidth = SU.rationWidth(self.width, self.width, line.ratio):tonumber()
×
36
  local oldx = typesetter.frame.state.cursorX
×
37
  typesetter.frame:advancePageDirection(-self.raise)
×
38
  typesetter.frame:advanceWritingDirection(outputWidth)
×
39
  local newx = typesetter.frame.state.cursorX
×
40
  local newy = typesetter.frame.state.cursorY
×
41
  SILE.outputter:drawRule(oldx, newy, newx - oldx, self.thickness)
×
42
  typesetter.frame:advancePageDirection(self.raise)
×
43
end
44

45
function package:_init ()
2✔
46
  base._init(self)
2✔
47
  self:loadPackage("raiselower")
2✔
48
  self:loadPackage("rebox")
2✔
49
end
50

51
function package:registerCommands ()
2✔
52

53
  self:registerCommand("hrule", function (options, _)
4✔
54
    local width = SU.cast("length", options.width)
10✔
55
    local height = SU.cast("length", options.height)
10✔
56
    local depth = SU.cast("length", options.depth)
10✔
57
    SILE.typesetter:pushHbox({
20✔
58
      width = width:absolute(),
20✔
59
      height = height:absolute(),
20✔
60
      depth = depth:absolute(),
20✔
61
      value = options.src,
10✔
62
      outputYourself = function (node, typesetter, line)
63
        local outputWidth = SU.rationWidth(node.width, node.width, line.ratio)
10✔
64
        typesetter.frame:advancePageDirection(-node.height)
20✔
65
        local oldx = typesetter.frame.state.cursorX
10✔
66
        local oldy = typesetter.frame.state.cursorY
10✔
67
        typesetter.frame:advanceWritingDirection(outputWidth)
10✔
68
        typesetter.frame:advancePageDirection(node.height + node.depth)
20✔
69
        local newx = typesetter.frame.state.cursorX
10✔
70
        local newy = typesetter.frame.state.cursorY
10✔
71
        SILE.outputter:drawRule(oldx, oldy, newx - oldx, newy - oldy)
30✔
72
        typesetter.frame:advancePageDirection(-node.depth)
20✔
73
      end
74
    })
75
  end, "Draws a blob of ink of width <width>, height <height> and depth <depth>")
12✔
76

77
  self:registerCommand("hrulefill", function (options, _)
4✔
78
    local raise
79
    local thickness
80
    if options.position and options.raise then
×
81
      SU.error("hrulefill cannot have both position and raise parameters")
×
82
    end
83
    if options.thickness then
×
84
      thickness = SU.cast("measurement", options.thickness)
×
85
    end
86
    if options.position == "underline" then
×
87
      local underlinePosition, underlineThickness = getUnderlineParameters()
×
88
      thickness = thickness or underlineThickness
×
89
      raise = underlinePosition
×
90
    elseif options.position == "strikethrough" then
×
91
      local yStrikeoutPosition, yStrikeoutSize = getStrikethroughParameters()
×
92
      thickness = thickness or yStrikeoutSize
×
93
      raise = yStrikeoutPosition + thickness / 2
×
94
    elseif options.position then
×
95
      SU.error("Unknown hrulefill position '"..options.position.."'")
×
96
    else
97
      raise = SU.cast("measurement", options.raise or "0")
×
98
    end
99

100
    SILE.typesetter:pushExplicitGlue(hrulefillglue({
×
101
      raise = raise,
102
      thickness = thickness or SILE.measurement("0.2pt"),
103
    }))
104
  end, "Add a huge horizontal hrule glue")
2✔
105

106
  self:registerCommand("fullrule", function (options, _)
4✔
107
    local thickness = SU.cast("measurement", options.thickness or "0.2pt")
×
108
    local raise = SU.cast("measurement", options.raise or "0.5em")
×
109

110
    if options.height then
×
111
      SU.deprecated("\\fullrule[…, height=…]", "\\fullrule[…, thickness=…]", "0.13.1", "0.15.0")
×
112
    end
113
    if not SILE.typesetter:vmode() then
×
114
      SU.deprecated("\\fullrule in horizontal mode", "\\hrule or \\hrulefill", "0.13.1", "0.15.0")
×
115
    end
116
    if options.width then
×
117
      SU.deprecated("\\fullrule with width", "\\hrule and \\raise", "0.13.1 ", "0.15.0")
×
118
    end
119

120
    SILE.typesetter:leaveHmode()
×
121
    SILE.call("noindent")
×
122
    SILE.call("hrulefill", { raise = raise, thickness = thickness })
×
123
    SILE.typesetter:leaveHmode()
×
124
  end, "Draw a full width hrule centered on the current line")
2✔
125

126
  self:registerCommand("underline", function (_, content)
4✔
127
    local underlinePosition, underlineThickness = getUnderlineParameters()
×
128

129
    local hbox, hlist = SILE.typesetter:makeHbox(content)
×
130
    -- Re-wrap the hbox in another hbox responsible for boxing it at output
131
    -- time, when we will know the line contribution and can compute the scaled width
132
    -- of the box, taking into account possible stretching and shrinking.
133
    SILE.typesetter:pushHbox({
×
134
      inner = hbox,
135
      width = hbox.width,
136
      height = hbox.height,
137
      depth = hbox.depth,
138
      outputYourself = function (node, typesetter, line)
139
        local oldX = typesetter.frame.state.cursorX
×
140
        local Y = typesetter.frame.state.cursorY
×
141

142
        -- Build the original hbox.
143
        -- Cursor will be moved by the actual definitive size.
144
        node.inner:outputYourself(SILE.typesetter, line)
×
145
        local newX = typesetter.frame.state.cursorX
×
146

147
        -- Output a line.
148
        -- NOTE: According to the OpenType specs, underlinePosition is "the suggested distance of
149
        -- the top of the underline from the baseline" so it seems implied that the thickness
150
        -- should expand downwards
151
        SILE.outputter:drawRule(oldX, Y - underlinePosition, newX - oldX, underlineThickness)
×
152
      end
153
    })
154
    SILE.typesetter:pushHlist(hlist)
×
155
  end, "Underlines some content")
2✔
156

157
  self:registerCommand("strikethrough", function (_, content)
4✔
158
    local yStrikeoutPosition, yStrikeoutSize = getStrikethroughParameters()
×
159

160
    local hbox, hlist = SILE.typesetter:makeHbox(content)
×
161
    -- Re-wrap the hbox in another hbox responsible for boxing it at output
162
    -- time, when we will know the line contribution and can compute the scaled width
163
    -- of the box, taking into account possible stretching and shrinking.
164
    SILE.typesetter:pushHbox({
×
165
      inner = hbox,
166
      width = hbox.width,
167
      height = hbox.height,
168
      depth = hbox.depth,
169
      outputYourself = function (node, typesetter, line)
170
        local oldX = typesetter.frame.state.cursorX
×
171
        local Y = typesetter.frame.state.cursorY
×
172
        -- Build the original hbox.
173
        -- Cursor will be moved by the actual definitive size.
174
        node.inner:outputYourself(SILE.typesetter, line)
×
175
        local newX = typesetter.frame.state.cursorX
×
176
        -- Output a line.
177
        -- NOTE: The OpenType spec is not explicit regarding how the size
178
        -- (thickness) affects the position. We opt to distribute evenly
179
        SILE.outputter:drawRule(oldX, Y - yStrikeoutPosition - yStrikeoutSize / 2, newX - oldX, yStrikeoutSize)
×
180
      end
181
    })
182
    SILE.typesetter:pushHlist(hlist)
×
183
  end, "Strikes out some content")
2✔
184

185
  self:registerCommand("boxaround", function (_, content)
4✔
186
    -- This command was not documented and lacks feature.
187
    -- Plan replacement with a better suited package.
188
    SU.deprecated("\\boxaround (undocumented)", "\\framebox (package)", "0.12.0")
×
189

190
    local hbox, hlist = SILE.typesetter:makeHbox(content)
×
191
    -- Re-wrap the hbox in another hbox responsible for boxing it at output
192
    -- time, when we will know the line contribution and can compute the scaled width
193
    -- of the box, taking into account possible stretching and shrinking.
194
    SILE.typesetter:pushHbox({
×
195
      inner = hbox,
196
      width = hbox.width,
197
      height = hbox.height,
198
      depth = hbox.depth,
199
      outputYourself = function (node, typesetter, line)
200
        local oldX = typesetter.frame.state.cursorX
×
201
        local Y = typesetter.frame.state.cursorY
×
202

203
        -- Build the original hbox.
204
        -- Cursor will be moved by the actual definitive size.
205
        node.inner:outputYourself(SILE.typesetter, line)
×
206
        local newX = typesetter.frame.state.cursorX
×
207

208
        -- Output a border
209
        -- NOTE: Drawn inside the hbox, so borders overlap with inner content.
210
        local w = newX - oldX
×
211
        local h = node.height:tonumber()
×
212
        local d = node.depth:tonumber()
×
213
        local thickness = 0.5
×
214

215
        SILE.outputter:drawRule(oldX, Y + d - thickness, w, thickness)
×
216
        SILE.outputter:drawRule(oldX, Y - h, w, thickness)
×
217
        SILE.outputter:drawRule(oldX, Y - h, thickness, h + d)
×
218
        SILE.outputter:drawRule(oldX + w - thickness, Y - h, thickness, h + d)
×
219
      end
220
    })
221
    SILE.typesetter:pushHlist(hlist)
×
222
  end, "Draws a box around some content")
2✔
223

224
end
225

226
package.documentation = [[
227
\begin{document}
228
The \autodoc:package{rules} package provides several line-drawing commands.
229

230
The \autodoc:command{\hrule} command draws a blob of ink of a given \autodoc:parameter{width} (length), \autodoc:parameter{height} (above the current baseline), and \autodoc:parameter{depth} (below the current baseline).
231
Such rules are horizontal boxes, placed along the baseline of a line of text and treated just like other text to be output.
232
So, they can appear in the middle of a paragraph, like this:
233
\hrule[width=20pt, height=0.5pt]
234
(That one was generated with \autodoc:command{\hrule[width=20pt, height=0.5pt]}.)
235

236
The \autodoc:command{\underline} command \underline{underlines} its content.
237

238
The \autodoc:command{\strikethrough} command \strikethrough{strikes} its content.
239

240
\autodoc:note{The position and thickness of the underlines and strikethroughs are based on the metrics of the current font, honoring the values defined by the type designer.}
241

242
The \autodoc:command{\hrulefill} inserts an infinite horizontal rubber, similar to an \autodoc:command{\hfill}, but—as its name implies—filled with a rule (that is, a solid line).
243
By default, it stands on the baseline and has a thickness of 0.2pt, below the baseline.
244
It supports optional parameters \autodoc:parameter{raise=<dimension>} and \autodoc:parameter{thickness=<dimension>} to adjust the position and thickness of the line, respectively.
245
The former accepts a negative measurement, to lower the line.
246
Alternatively, use the \autodoc:parameter{position} option, which can be set to \code{underline} or \code{strikethrough}.
247
In that case, it honors the current font metrics and the line is drawn at the appropriate position and, by default, with the relevant thickness.
248
You can still set a custom thickness with the \autodoc:parameter{thickness} parameter.
249

250
For instance, \autodoc:command{\hrulefill[position=underline]} gives:
251
\hrulefill[position=underline]
252

253
Finally, \autodoc:command{\fullrule} draws a thin standalone rule across the width of a full text line.
254
Accepted parameters are \autodoc:parameter{raise} and \autodoc:parameter{thickness}, with the same meanings as above.
255
\end{document}
256
]]
2✔
257

258
return package
2✔
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

© 2026 Coveralls, Inc