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

sile-typesetter / sile / 9304049654

30 May 2024 02:12PM UTC coverage: 60.021% (-14.7%) from 74.707%
9304049654

push

github

web-flow
Merge 1a26b4f22 into a1fd105f8

6743 of 12900 new or added lines in 186 files covered. (52.27%)

347 existing lines in 49 files now uncovered.

10311 of 17179 relevant lines covered (60.02%)

3307.34 hits per line

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

43.53
/packages/linespacing/init.lua
1
local base = require("packages.base")
1✔
2

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

6
local metrics = require("fontmetrics")
1✔
7

8
local metricscache = {}
1✔
9

10
local getLineMetrics = function (l)
NEW
11
   local linemetrics = { ascender = 0, descender = 0, lineheight = SILE.types.length() }
×
NEW
12
   if not l or not l.nodes then
×
NEW
13
      return linemetrics
×
14
   end
NEW
15
   for i = 1, #l.nodes do
×
NEW
16
      local node = l.nodes[i]
×
NEW
17
      if node.is_nnode then
×
NEW
18
         local m = metricscache[SILE.font._key(node.options)]
×
NEW
19
         if not m then
×
NEW
20
            local face = SILE.font.cache(node.options, SILE.shaper.getFace)
×
NEW
21
            m = metrics.get_typographic_extents(face)
×
NEW
22
            m.ascender = m.ascender * node.options.size
×
NEW
23
            m.descender = m.descender * node.options.size
×
NEW
24
            metricscache[SILE.font._key(node.options)] = m
×
25
         end
NEW
26
         SILE.settings:temporarily(function ()
×
NEW
27
            SILE.call("font", node.options, {})
×
NEW
28
            m.lineheight = SU.cast("length", SILE.settings:get("linespacing.css.line-height")):absolute()
×
29
         end)
NEW
30
         if m.ascender > linemetrics.ascender then
×
NEW
31
            linemetrics.ascender = m.ascender
×
32
         end
NEW
33
         if m.descender > linemetrics.descender then
×
NEW
34
            linemetrics.descender = m.descender
×
35
         end
NEW
36
         if m.lineheight > linemetrics.lineheight then
×
NEW
37
            linemetrics.lineheight = m.lineheight
×
38
         end
39
      end
40
   end
NEW
41
   return linemetrics
×
42
end
43

44
local linespacingLeading = function (_, vbox, previous)
45
   local method = SILE.settings:get("linespacing.method")
9✔
46

47
   local firstline = SILE.settings:get("linespacing.minimumfirstlineposition"):absolute()
18✔
48
   if not previous then
9✔
49
      if firstline.length:tonumber() > 0 then
4✔
NEW
50
         local toAdd = SILE.types.length(firstline.length - vbox.height)
×
NEW
51
         return SILE.types.node.vkern(toAdd)
×
52
      else
53
         return nil
2✔
54
      end
55
   end
56

57
   if method == "tex" then
7✔
NEW
58
      return SILE.typesetters.base:leadingFor(vbox, previous)
×
59
   end
60

61
   if method == "fit-glyph" then
7✔
NEW
62
      local extra = SILE.settings:get("linespacing.fit-glyph.extra-space"):absolute()
×
NEW
63
      local toAdd = SILE.types.length(extra)
×
NEW
64
      return SILE.types.node.vglue(toAdd)
×
65
   end
66

67
   if method == "fixed" then
7✔
68
      local btob = SILE.settings:get("linespacing.fixed.baselinedistance"):absolute()
14✔
69
      local toAdd = SILE.types.length(btob.length - (vbox.height + previous.depth), btob.stretch, btob.shrink)
21✔
70
      return SILE.types.node.vglue(toAdd)
7✔
71
   end
72

73
   -- For these methods, we need to read the font metrics
NEW
74
   if not metrics then
×
NEW
75
      SU.error("'" .. method .. "' line spacing method requires font metrics module, which is not available.")
×
76
   end
77

NEW
78
   local thismetrics = getLineMetrics(vbox)
×
NEW
79
   local prevmetrics = getLineMetrics(previous)
×
NEW
80
   if method == "fit-font" then
×
81
      -- Distance to next baseline is max(descender) of fonts on previous +
82
      -- max(ascender) of fonts on next
NEW
83
      local extra = SILE.settings:get("linespacing.fit-font.extra-space"):absolute()
×
NEW
84
      local btob = prevmetrics.descender + thismetrics.ascender + extra
×
NEW
85
      local toAdd = btob - (vbox.height + (previous and previous.depth or 0))
×
NEW
86
      return SILE.types.node.vglue(toAdd)
×
87
   end
88

NEW
89
   if method == "css" then
×
NEW
90
      local lh = prevmetrics.lineheight
×
NEW
91
      local leading = (lh - (prevmetrics.ascender + prevmetrics.descender))
×
NEW
92
      if previous then
×
NEW
93
         previous.height = previous.height + leading / 2
×
NEW
94
         previous.depth = previous.depth + leading / 2
×
95
      end
NEW
96
      return SILE.types.node.vglue()
×
97
   end
98

NEW
99
   SU.error("Unknown line spacing method " .. method)
×
100
end
101

102
function package:_init ()
1✔
103
   base._init(self)
1✔
104
   self.class:registerPostinit(function (_)
2✔
105
      SILE.typesetter.leadingFor = linespacingLeading
1✔
106
   end)
107
end
108

109
function package.declareSettings (_)
1✔
110
   SILE.settings:declare({
1✔
111
      parameter = "linespacing.method",
112
      default = "tex",
113
      type = "string",
114
      help = "How to set the line spacing (tex, fixed, fit-font, fit-glyph, css)",
115
   })
116

117
   SILE.settings:declare({
2✔
118
      parameter = "linespacing.fixed.baselinedistance",
119
      default = SILE.types.length("1.2em"),
2✔
120
      type = "length",
121
      help = "Distance from baseline to baseline in the case of fixed line spacing",
122
   })
123

124
   SILE.settings:declare({
2✔
125
      parameter = "linespacing.minimumfirstlineposition",
126
      default = SILE.types.length(0),
2✔
127
      type = "length",
128
   })
129

130
   SILE.settings:declare({
2✔
131
      parameter = "linespacing.fit-glyph.extra-space",
132
      default = SILE.types.length(0),
2✔
133
      type = "length",
134
   })
135

136
   SILE.settings:declare({
2✔
137
      parameter = "linespacing.fit-font.extra-space",
138
      default = SILE.types.length(0),
2✔
139
      type = "length",
140
   })
141

142
   SILE.settings:declare({
2✔
143
      parameter = "linespacing.css.line-height",
144
      default = SILE.types.length("1.2em"),
2✔
145
      type = "length",
146
   })
147
end
148

149
function package:registerCommands ()
1✔
150
   self:registerCommand("linespacing-on", function ()
2✔
NEW
151
      SILE.typesetter.leadingFor = linespacingLeading
×
152
   end)
153

154
   self:registerCommand("linespacing-off", function ()
2✔
NEW
155
      SILE.typesetter.leadingFor = SILE.typesetters.base.leadingFor
×
156
   end)
157
end
158

159
package.documentation = [[
160
\begin{document}
161
\linespacing-on
162
SILE’s default method of inserting leading between lines should be familiar to users of TeX, but it is not the most friendly system for book designers.
163
The \autodoc:package{linespacing} package provides a better choice of leading systems.
164

165
After loading the package, you are able to choose the linespacing mode by setting the \autodoc:setting{linespacing.method} parameter.
166
The following examples have funny sized words in them so that you can see how the different methods interact.
167

168
By default, this is set to \code{tex}. The other options available are:
169

170
\medskip
171
\set[parameter=linespacing.method,value=fixed]
172
\set[parameter=linespacing.fixed.baselinedistance,value=1.5em]
173
\begin{itemize}
174
\item{\code{fixed}. This set the lines at a fixed baseline-to-baseline distance, determined by the \autodoc:setting{linespacing.fixed.baselinedistance} parameter.
175
You can specify this parameter either relative to the type size (\code{1.2em}) or as a absolute distance (\code{15pt}).
176
This paragraph is set with a fixed 1.5em baseline-to-baseline distance.}
177
\end{itemize}
178

179
\medskip
180
\set[parameter=linespacing.method,value=fit-glyph]
181
\begin{itemize}
182
\item{\code{fit-glyph}. This sets the lines solid; that is, the lowest point on line 1 (either a descender like \font[size=20pt]{q} or, if there are no descenders, the baseline) will touch the \font[size=20pt]{highest} point of line 2, as in this paragraph.
183
You generally don’t want to use this as-is.}
184
\end{itemize}
185

186
\set[parameter=linespacing.fit-glyph.extra-space,value=5pt]
187

188
What you probably want to do is insert a constant (relative or absolute) s\font[size=20pt]{p}ace between the lines by setting the \autodoc:setting{linespacing.fit-glyph.extra-space} parameter.
189
\font[size=20pt]{T}his paragraph is set with 5 points of space between the descenders and the ascenders.
190

191
\medskip
192
\set[parameter=linespacing.method,value=fit-font]
193
\begin{itemize}
194
\item{\code{fit-font}. This inspects each \code{hbox} on the line, and asks the fonts it finds for their bounding boxes—the highest ascender and the lower descender.
195
It then sets the lines solid.
196
Essentially each character is treated as if it is the same height, rather like composing a slug of metal type.
197
If there are things other than text on your line, or the text is buried inside other boxes, this may not work well.}
198
\end{itemize}
199

200
\set[parameter=linespacing.fit-font.extra-space,value=5pt]
201

202
As with \code{fit-glyph}, you can insert extra space between the lines with the \autodoc:setting{linespacing.fit-font.extra-space} parameter.
203

204
\medskip
205
\set[parameter=linespacing.method,value=css]
206
\set[parameter=linespacing.css.line-height,value=2em]
207
\begin{itemize}
208
\item{\code{css}. This is similar to the method used in browsers; the baseline distance is set with the \autodoc:setting{linespacing.css.line-height} parameter, and the excess \font[size=20pt]{space} between this parameter and the actual height of the line is distributed between the top and bottom of the line.}
209
\end{itemize}
210
\medskip
211

212
\linespacing-off
213
\end{document}
214
]]
1✔
215

216
return package
1✔
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