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

sile-typesetter / sile / 6934957716

20 Nov 2023 07:35PM UTC coverage: 57.468% (-3.2%) from 60.703%
6934957716

push

github

web-flow
Merge c91d9a7d4 into 34e2e5335

60 of 79 new or added lines in 1 file covered. (75.95%)

717 existing lines in 27 files now uncovered.

8957 of 15586 relevant lines covered (57.47%)

5715.38 hits per line

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

0.0
/packages/linespacing/init.lua
UNCOV
1
local base = require("packages.base")
×
2

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

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

UNCOV
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)
UNCOV
37
  local method = SILE.settings:get("linespacing.method")
×
38

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

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

UNCOV
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

UNCOV
59
  if method == "fixed" then
×
UNCOV
60
    local btob = SILE.settings:get("linespacing.fixed.baselinedistance"):absolute()
×
UNCOV
61
    local toAdd = SILE.length(btob.length - (vbox.height + previous.depth), btob.stretch, btob.shrink)
×
UNCOV
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

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

UNCOV
102
function package.declareSettings (_)
×
103

UNCOV
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

UNCOV
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

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

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

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

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

142
end
143

UNCOV
144
function package:registerCommands ()
×
145

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

UNCOV
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}
UNCOV
211
]]
×
212

UNCOV
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

© 2026 Coveralls, Inc