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

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

6
local metrics = require("fontmetrics")
×
7

8
local metricscache = {}
×
9

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

36
local linespacingLeading = function (_, vbox, previous)
37
  local method = SILE.settings:get("linespacing.method")
×
38

39
  local firstline = SILE.settings:get("linespacing.minimumfirstlineposition"):absolute()
×
40
  if not previous then
×
41
    if firstline.length:tonumber() > 0 then
×
42
      local toAdd = SILE.length(firstline.length - vbox.height)
×
43
      return SILE.nodefactory.vkern(toAdd)
×
44
    else
45
      return nil
×
46
    end
47
  end
48

49
  if method == "tex" then
×
50
    return SILE.typesetters.base:leadingFor(vbox, previous)
×
51
  end
52

53
  if method == "fit-glyph" then
×
54
    local extra = SILE.settings:get("linespacing.fit-glyph.extra-space"):absolute()
×
55
    local toAdd = SILE.length(extra)
×
56
    return SILE.nodefactory.vglue(toAdd)
×
57
  end
58

59
  if method == "fixed" then
×
60
    local btob = SILE.settings:get("linespacing.fixed.baselinedistance"):absolute()
×
61
    local toAdd = SILE.length(btob.length - (vbox.height + previous.depth), btob.stretch, btob.shrink)
×
62
    return SILE.nodefactory.vglue(toAdd)
×
63
  end
64

65
  -- For these methods, we need to read the font metrics
66
  if not metrics then
×
67
    SU.error("'"..method.."' line spacing method requires font metrics module, which is not available.")
×
68
  end
69

70
  local thismetrics = getLineMetrics(vbox)
×
71
  local prevmetrics = getLineMetrics(previous)
×
72
  if method == "fit-font" then
×
73
    -- Distance to next baseline is max(descender) of fonts on previous +
74
    -- max(ascender) of fonts on next
75
    local extra = SILE.settings:get("linespacing.fit-font.extra-space"):absolute()
×
76
    local btob = prevmetrics.descender + thismetrics.ascender + extra
×
77
    local toAdd = btob - (vbox.height + (previous and previous.depth or 0))
×
78
    return SILE.nodefactory.vglue(toAdd)
×
79
  end
80

81
  if method == "css" then
×
82
    local lh = prevmetrics.lineheight
×
83
    local leading = (lh - (prevmetrics.ascender + prevmetrics.descender))
×
84
    if previous then
×
85
      previous.height = previous.height + leading / 2
×
86
      previous.depth = previous.depth + leading / 2
×
87
    end
88
    return SILE.nodefactory.vglue()
×
89

90
  end
91

92
  SU.error("Unknown line spacing method "..method)
×
93
end
94

95
function package:_init ()
×
96
  base._init(self)
×
97
  self.class:registerPostinit(function(_)
×
98
    SILE.typesetter.leadingFor = linespacingLeading
×
99
  end)
100
end
101

102
function package.declareSettings (_)
×
103

104
  SILE.settings:declare({
×
105
    parameter = "linespacing.method",
106
    default = "tex",
107
    type = "string",
108
    help = "How to set the line spacing (tex, fixed, fit-font, fit-glyph, css)"
×
109
  })
110

111
  SILE.settings:declare({
×
112
    parameter = "linespacing.fixed.baselinedistance",
113
    default = SILE.length("1.2em"),
114
    type = "length",
115
    help = "Distance from baseline to baseline in the case of fixed line spacing"
×
116
  })
117

118
  SILE.settings:declare({
×
119
    parameter = "linespacing.minimumfirstlineposition",
120
    default = SILE.length(0),
121
    type = "length"
×
122
  })
123

124
  SILE.settings:declare({
×
125
    parameter = "linespacing.fit-glyph.extra-space",
126
    default = SILE.length(0),
127
    type = "length"
×
128
  })
129

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

136
  SILE.settings:declare({
×
137
    parameter = "linespacing.css.line-height",
138
    default = SILE.length("1.2em"),
139
    type = "length"
×
140
  })
141

142
end
143

144
function package:registerCommands ()
×
145

146
  self:registerCommand("linespacing-on", function ()
×
147
    SILE.typesetter.leadingFor = linespacingLeading
×
148
  end)
149

150
  self:registerCommand("linespacing-off", function ()
×
151
    SILE.typesetter.leadingFor = SILE.typesetters.base.leadingFor
×
152
  end)
153

154
end
155

156
package.documentation = [[
157
\begin{document}
158
\linespacing-on
159
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.
160
The \autodoc:package{linespacing} package provides a better choice of leading systems.
161

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

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

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

176
\medskip
177
\set[parameter=linespacing.method,value=fit-glyph]
178
\begin{itemize}
179
\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.
180
You generally don’t want to use this as-is.}
181
\end{itemize}
182

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

185
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.
186
\font[size=20pt]{T}his paragraph is set with 5 points of space between the descenders and the ascenders.
187

188
\medskip
189
\set[parameter=linespacing.method,value=fit-font]
190
\begin{itemize}
191
\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.
192
It then sets the lines solid.
193
Essentially each character is treated as if it is the same height, rather like composing a slug of metal type.
194
If there are things other than text on your line, or the text is buried inside other boxes, this may not work well.}
195
\end{itemize}
196

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

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

201
\medskip
202
\set[parameter=linespacing.method,value=css]
203
\set[parameter=linespacing.css.line-height,value=2em]
204
\begin{itemize}
205
\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.}
206
\end{itemize}
207
\medskip
208

209
\linespacing-off
210
\end{document}
211
]]
×
212

213
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