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

sile-typesetter / sile / 9428435077

08 Jun 2024 11:35AM UTC coverage: 64.56% (-9.9%) from 74.46%
9428435077

push

github

web-flow
Merge pull request #2047 from alerque/end-pars

23 of 46 new or added lines in 5 files covered. (50.0%)

1684 existing lines in 60 files now uncovered.

11145 of 17263 relevant lines covered (64.56%)

4562.45 hits per line

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

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

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

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

17
local function getStrikethroughParameters ()
UNCOV
18
   local ot = require("core.opentype-parser")
×
UNCOV
19
   local fontoptions = SILE.font.loadDefaults({})
×
UNCOV
20
   local face = SILE.font.cache(fontoptions, SILE.shaper.getFace)
×
UNCOV
21
   local font = ot.parseFont(face)
×
UNCOV
22
   local upem = font.head.unitsPerEm
×
UNCOV
23
   local yStrikeoutPosition = font.os2.yStrikeoutPosition / upem * fontoptions.size
×
UNCOV
24
   local yStrikeoutSize = font.os2.yStrikeoutSize / upem * fontoptions.size
×
UNCOV
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.types.node.hfillglue)
3✔
31
hrulefillglue.raise = SILE.types.measurement()
6✔
32
hrulefillglue.thickness = SILE.types.measurement("0.2pt")
6✔
33

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

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

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

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

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

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

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

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

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

UNCOV
128
      SILE.typesetter:liner("underline", content, function (box, typesetter, line)
×
UNCOV
129
         local oldX = typesetter.frame.state.cursorX
×
UNCOV
130
         local Y = typesetter.frame.state.cursorY
×
131

132
         -- Build the content.
133
         -- Cursor will be moved by the actual definitive size.
UNCOV
134
         box:outputContent(typesetter, line)
×
UNCOV
135
         local newX = typesetter.frame.state.cursorX
×
136

137
         -- Output a line.
138
         -- NOTE: According to the OpenType specs, underlinePosition is "the suggested distance of
139
         -- the top of the underline from the baseline" so it seems implied that the thickness
140
         -- should expand downwards
UNCOV
141
         SILE.outputter:drawRule(oldX, Y - underlinePosition, newX - oldX, underlineThickness)
×
142
      end)
143
   end, "Underlines some content")
3✔
144

145
   self:registerCommand("strikethrough", function (_, content)
6✔
UNCOV
146
      local yStrikeoutPosition, yStrikeoutSize = getStrikethroughParameters()
×
147

UNCOV
148
      SILE.typesetter:liner("strikethrough", content, function (box, typesetter, line)
×
UNCOV
149
         local oldX = typesetter.frame.state.cursorX
×
UNCOV
150
         local Y = typesetter.frame.state.cursorY
×
151

152
         -- Build the content.
153
         -- Cursor will be moved by the actual definitive size.
UNCOV
154
         box:outputContent(typesetter, line)
×
UNCOV
155
         local newX = typesetter.frame.state.cursorX
×
156

157
         -- Output a line.
158
         -- NOTE: The OpenType spec is not explicit regarding how the size
159
         -- (thickness) affects the position. We opt to distribute evenly
UNCOV
160
         SILE.outputter:drawRule(oldX, Y - yStrikeoutPosition - yStrikeoutSize / 2, newX - oldX, yStrikeoutSize)
×
161
      end)
162
   end, "Strikes out some content")
3✔
163

164
   self:registerCommand("boxaround", function (_, content)
6✔
165
      -- This command was not documented and lacks feature.
166
      -- Plan replacement with a better suited package.
167
      SU.deprecated("\\boxaround (undocumented)", "\\framebox (package)", "0.12.0")
×
168

169
      local hbox, hlist = SILE.typesetter:makeHbox(content)
×
170
      -- Re-wrap the hbox in another hbox responsible for boxing it at output
171
      -- time, when we will know the line contribution and can compute the scaled width
172
      -- of the box, taking into account possible stretching and shrinking.
173
      SILE.typesetter:pushHbox({
×
174
         inner = hbox,
175
         width = hbox.width,
176
         height = hbox.height,
177
         depth = hbox.depth,
178
         outputYourself = function (node, typesetter, line)
179
            local oldX = typesetter.frame.state.cursorX
×
180
            local Y = typesetter.frame.state.cursorY
×
181

182
            -- Build the original hbox.
183
            -- Cursor will be moved by the actual definitive size.
184
            node.inner:outputYourself(SILE.typesetter, line)
×
185
            local newX = typesetter.frame.state.cursorX
×
186

187
            -- Output a border
188
            -- NOTE: Drawn inside the hbox, so borders overlap with inner content.
189
            local w = newX - oldX
×
190
            local h = node.height:tonumber()
×
191
            local d = node.depth:tonumber()
×
192
            local thickness = 0.5
×
193

194
            SILE.outputter:drawRule(oldX, Y + d - thickness, w, thickness)
×
195
            SILE.outputter:drawRule(oldX, Y - h, w, thickness)
×
196
            SILE.outputter:drawRule(oldX, Y - h, thickness, h + d)
×
197
            SILE.outputter:drawRule(oldX + w - thickness, Y - h, thickness, h + d)
×
198
         end,
199
      })
200
      SILE.typesetter:pushHlist(hlist)
×
201
   end, "Draws a box around some content")
3✔
202
end
203

204
package.documentation = [[
205
\begin{document}
206
The \autodoc:package{rules} package provides several line-drawing commands.
207

208
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).
209
Such rules are horizontal boxes, placed along the baseline of a line of text and treated just like other text to be output.
210
So, they can appear in the middle of a paragraph, like this:
211
\hrule[width=20pt, height=0.5pt]
212
(That one was generated with \autodoc:command{\hrule[width=20pt, height=0.5pt]}.)
213

214
The \autodoc:command{\underline} command \underline{underlines} its content.
215

216
The \autodoc:command{\strikethrough} command \strikethrough{strikes} its content.
217

218
Both commands support paragraph content spanning multiple lines.
219

220
\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.}
221

222
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).
223
By default, it stands on the baseline and has a thickness of 0.2pt, below the baseline.
224
It supports optional parameters \autodoc:parameter{raise=<dimension>} and \autodoc:parameter{thickness=<dimension>} to adjust the position and thickness of the line, respectively.
225
The former accepts a negative measurement, to lower the line.
226
Alternatively, use the \autodoc:parameter{position} option, which can be set to \code{underline} or \code{strikethrough}.
227
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.
228
You can still set a custom thickness with the \autodoc:parameter{thickness} parameter.
229

230
For instance, \autodoc:command{\hrulefill[position=underline]} gives:
231
\hrulefill[position=underline]
232

233
Finally, \autodoc:command{\fullrule} draws a thin standalone rule across the width of a full text line.
234
Accepted parameters are \autodoc:parameter{raise} and \autodoc:parameter{thickness}, with the same meanings as above.
235
\end{document}
236
]]
3✔
237

238
return package
3✔
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