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

sile-typesetter / sile / 11826759401

13 Nov 2024 10:16PM UTC coverage: 61.013% (-2.4%) from 63.4%
11826759401

push

github

web-flow
Merge pull request #2165 from Omikhleia/feat-bevelled-fractions

feat(math): Support MathML bevelled fractions

7 of 38 new or added lines in 2 files covered. (18.42%)

581 existing lines in 28 files now uncovered.

11155 of 18283 relevant lines covered (61.01%)

2604.98 hits per line

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

78.72
/packages/math/init.lua
1
local base = require("packages.base")
10✔
2

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

6
function package:_init ()
10✔
7
   base._init(self)
10✔
8
   local typesetter = require("packages.math.typesetter")
10✔
9
   self.ConvertMathML, self.handleMath = typesetter[1], typesetter[2]
10✔
10
   local texlike = require("packages.math.texlike")
10✔
11
   self.convertTexlike, self.compileToMathML = texlike[1], texlike[2]
10✔
12
   -- Register a new unit that is 1/18th of the current math font size
13
   SILE.registerUnit("mu", {
20✔
14
      relative = true,
15
      definition = function (value)
16
         return value * SILE.settings:get("math.font.size") / 18
1,150✔
17
      end,
18
   })
19
   self:loadPackage("counters")
10✔
20
end
21

22
function package.declareSettings (_)
10✔
23
   SILE.settings:declare({
10✔
24
      parameter = "math.font.family",
25
      type = "string",
26
      default = "Libertinus Math",
27
   })
28
   SILE.settings:declare({
10✔
29
      parameter = "math.font.style",
30
      type = "string",
31
      default = "Regular",
32
   })
33
   SILE.settings:declare({
10✔
34
      parameter = "math.font.weight",
35
      type = "integer",
36
      default = 400,
37
   })
38
   SILE.settings:declare({
10✔
39
      parameter = "math.font.filename",
40
      type = "string",
41
      default = "",
42
   })
43
   SILE.settings:declare({
10✔
44
      parameter = "math.font.size",
45
      type = "integer",
46
      default = 10,
47
   })
48
   -- Whether to show debug boxes around mboxes
49
   SILE.settings:declare({
10✔
50
      parameter = "math.debug.boxes",
51
      type = "boolean",
52
      default = false,
53
   })
54
   SILE.settings:declare({
20✔
55
      parameter = "math.displayskip",
56
      type = "VGlue",
57
      default = SILE.types.node.vglue("2ex plus 1pt"),
20✔
58
   })
59

60
   -- Penalties for breaking before and after a display math formula
61
   -- See TeX's \predisplaypenalty and \postdisplaypenalty
62
   SILE.settings:declare({
10✔
63
      parameter = "math.predisplaypenalty",
64
      type = "integer",
65
      default = 10000, -- strict no break by default as in (La)TeX
66
      help = "Penalty for breaking before a display math formula",
67
   })
68
   SILE.settings:declare({
10✔
69
      parameter = "math.postdisplaypenalty",
70
      type = "integer",
71
      -- (La)TeX's default is 0 (a normal line break penalty allowing a break
72
      -- after a display math formula)
73
      -- See https://github.com/sile-typesetter/sile/issues/2160
74
      --    And see implementation in handleMath(): we are not yet doing the right
75
      --    things with respect to paragraphing, so setting a lower value for now
76
      --    to ease breaking after a display math formula rather than before
77
      --    when the formula is in the middle of a paragraph.
78
      --    (In TeX, these penalties would apply in horizontal mode, with a display
79
      --    math formula being a horizontal full-width box, our implementation
80
      --    currently use them as vertical penalties).
81
      default = -50,
82
      help = "Penalty for breaking after a display math formula",
83
   })
84
end
85

86
function package:registerCommands ()
10✔
87
   self:registerCommand("mathml", function (options, content)
20✔
88
      local mbox
89
      xpcall(function ()
44✔
90
         mbox = self:ConvertMathML(content)
44✔
91
      end, function (err)
44✔
UNCOV
92
         print(err)
×
UNCOV
93
         print(debug.traceback())
×
94
      end)
95
      self:handleMath(mbox, options)
22✔
96
   end)
97

98
   self:registerCommand("math", function (options, content)
20✔
99
      local mbox
100
      xpcall(function ()
80✔
101
         mbox = self:ConvertMathML(self:compileToMathML({}, self:convertTexlike(content)))
160✔
102
      end, function (err)
80✔
UNCOV
103
         print(err)
×
UNCOV
104
         print(debug.traceback())
×
105
      end)
106
      self:handleMath(mbox, options)
40✔
107
   end)
108

109
   self:registerCommand("math:numberingstyle", function (options, _)
20✔
UNCOV
110
      SILE.typesetter:typeset("(")
×
UNCOV
111
      if options.counter then
×
UNCOV
112
         SILE.call("show-counter", { id = options.counter })
×
UNCOV
113
      elseif options.number then
×
UNCOV
114
         SILE.typesetter:typeset(options.number)
×
115
      end
UNCOV
116
      SILE.typesetter:typeset(")")
×
117
   end)
118
end
119

120
package.documentation = [[
121
\begin{document}
122
\use[module=packages.math]
123
\set[parameter=math.font.family, value=Libertinus Math]
124
\set[parameter=math.font.size, value=11]
125
% Default verbatim font (Hack) is missing a few math symbols
126
\use[module=packages.font-fallback]
127
\font:add-fallback[family=Symbola]
128
\define[command=paragraph]{\smallskip\em{\process.}\novbreak\par}
129
The \autodoc:package{math} package provides typesetting of formulas directly in a SILE document.
130

131
\autodoc:note{Mathematical typesetting in SILE is still in its infancy.
132
As such, it lacks some features and may contain bugs.
133
Feedback and contributions are always welcome.}
134

135
\noindent To typeset mathematics, you will need an OpenType math font installed on your system.%
136
%\footnote{A list of freely available math fonts can be found at \href[src=https://www.ctan.org/pkg/unicode-math]{https://www.ctan.org/pkg/unicode-math}}
137
By default, this package uses Libertinus Math, so it will fail if Libertinus Math can’t be found.
138
Another font may be specified via the setting \autodoc:setting{math.font.family}.
139
If required, you can set the font style and weight via \autodoc:setting{math.font.style} and \autodoc:setting{math.font.weight}.
140
The font size can be set via \autodoc:setting{math.font.size}.
141

142
\paragraph{MathML}
143
The first way to typeset math formulas is to enter them in the MathML format.
144
MathML is a standard for encoding mathematical notation for the Web and for other types of digital documents.
145
It is supported by a wide range of tools and represents the most promising format for unifying the encoding of mathematical notation, as well as improving its accessibility (e.g., to blind users).
146

147
To render an equation encoded in MathML, simply put it in a \code{mathml} command.
148
For example, the formula \mathml{\mrow{\msup{\mi{a}\mn{2}} \mo{+} \msup{\mi{b}\mn{2}} \mo{=} \msup{\mi{c}\mn{2}}}} was typeset by the following command:
149

150
\begin[type=autodoc:codeblock]{raw}
151
\mathml{
152
    \mrow{
153
        \msup{\mi{a}\mn{2}}
154
        \mo{+}
155
        \msup{\mi{b}\mn{2}}
156
        \mo{=}
157
        \msup{\mi{c}\mn{2}}
158
    }
159
}
160
\end{raw}
161

162
\noindent In an XML document, we could use the more classical XML syntax:
163

164
\begin[type=autodoc:codeblock]{raw}
165
<mathml>
166
    <mrow>
167
        <msup> <mi>a</mi> <mn>2</mn> </msup>
168
        <mo>+</mo>
169
        <msup> <mi>b</mi> <mn>2</mn> </msup>
170
        <mo>=</mo>
171
        <msup> <mi>c</mi> <mn>2</mn> </msup>
172
    </mrow>
173
</mathml>
174
\end{raw}
175

176
\noindent By default, formulas are integrated into the flow of text.
177
To typeset them on their own line, use the \autodoc:parameter{mode=display} option:
178

179
\mathml[mode=display]{
180
    \mrow{
181
        \msup{\mi{a}\mn{2}}
182
        \mo{+}
183
        \msup{\mi{b}\mn{2}}
184
        \mo{=}
185
        \msup{\mi{c}\mn{2}}
186
    }
187
}
188

189
\paragraph{TeX-like syntax}
190
As the previous examples illustrate, MathML is not really intended to be written by humans and quickly becomes very verbose.
191
That is why this package also provides a \code{math} command, which understands a syntax similar to the math syntax of TeX.
192
To typeset the above equation, one only has to type \code{\\math\{a^2 + b^2 = c^2\}}.
193

194
Here is a slightly more involved equation:
195

196
\begin[type=autodoc:codeblock]{raw}
197
\begin[mode=display]{math}
198
    \sum_{n=1}^\infty \frac{1}{n^2} = \frac{\pi^2}{6}
199
\end{math}
200
\end{raw}
201

202
\noindent This renders as:
203

204
\begin[mode=display]{math}
205
    \sum_{n=1}^\infty \frac{1}{n^2} = \frac{\pi^2}{6}
206
\end{math}
207

208
The general philosophy of the TeX-like syntax is to be a simple layer on top of MathML, and not to mimic perfectly the syntax of the LaTeX tool.
209
Its main difference from the SILE syntax is that \code{\\mycommand\{arg1\}\{arg2\}\{arg3\}} is translated into MathML as \code{<mycommand> arg1 arg2 arg3 </mycommand>} whereas in normal SILE syntax, the XML equivalent would be \code{<mycommand>arg1</mycommand> arg2 arg3}.
210

211
\code{\\sum}, \code{\\infty}, and \code{\\pi} are only shorthands for the Unicode characters \math{\sum}, \math{\infty} and \math{\pi}.
212
If it’s more convenient, you can use these Unicode characters directly.
213
The symbol shorthands are the same as in the TeX package \href[src=https://www.ctan.org/pkg/unicode-math]{\code{unicode-math}}.
214

215
\code{\{formula\}} is a shorthand for \code{\\mrow\{formula\}}.
216
Since parentheses—among other glyphs—stretch vertically to the size of their englobing \code{mrow}, this is useful to typeset parentheses of different sizes on the same line:
217

218
\begin[type=autodoc:codeblock]{raw}
219
\Gamma (\frac{\zeta}{2}) + x^2(x+1)
220
\end{raw}
221

222
\noindent renders as
223

224
\begin[mode=display]{math}
225
    \Gamma (\frac{\zeta}{2}) + x^2(x+1)
226
\end{math}
227

228
\noindent which is ugly.
229
To keep parentheses around \math{x+1} small, you should put braces around the expression:
230

231
\begin[type=autodoc:codeblock]{raw}
232
\Gamma (\frac{\zeta}{2}) + x^2{(x+1)}
233
\end{raw}
234

235
\begin[mode=display]{math}
236
    \Gamma (\frac{\zeta}{2}) + x^2{(x+1)}
237
\end{math}
238

239
\noindent To print a brace in a formula, you need to escape it with a backslash.
240

241
\paragraph{Token kinds}
242
In the \code{math} syntax, every individual letter is an identifier (MathML tag \code{mi}), every number is a… number (tag \code{mn}) and all other characters are operators (tag \code{mo}).
243
If this does not suit you, you can explicitly use the \code{\\mi}, \code{\\mn}, or \code{\\mo} tags.
244
For instance, \code{sin(x)} will be rendered as \math{sin(x)}, because SILE considers the letters s, i and n to be individual identifiers, and identifiers made of one character are italicized by default.
245
To avoid that, you can specify that \math{\mi{sin}} is an identifier by writing \code{\\mi\{sin\}(x)} and get: \math{\mi{sin}(x)}.
246
If you prefer it in “double struck” style, this is permitted by the \code{mathvariant} attribute: \code{\\mi[mathvariant=double-struck]\{sin\}(x)} renders as \math{\mi[mathvariant=double-struck]{sin}(x)}.
247

248
\paragraph{Atom types and spacing}
249
Each token automatically gets assigned an atom type from the list below:
250
\begin{itemize}
251
  \item{\code{ord}: \code{mi} and \code{mn} tokens, as well as unclassified operators}
252
  \item{\code{big}: big operators like ‘\math{\sum}’ or ‘\math{\prod}’}
253
  \item{\code{bin}: binary operators like ‘\math{+}’ or ‘\math{\%}’}
254
  \item{\code{rel}: relation operators like ‘\math{=}’ or ‘\math{<}’}
255
  \item{\code{open}: opening operators like ‘\math{(}’ or ‘\math{[}’}
256
  \item{\code{close}: closing operators like ‘\math{)}’ or ‘\math{]}’}
257
  \item{\code{punct}: punctuation operators like ‘\math{,}’}
258
  % TODO: Those are defined in the 'math' package but appear to be unused
259
  %\item{\code{inner}}
260
  %\item{\code{over}}
261
  %\item{\code{under}}
262
  %\item{\code{accent}}
263
  %\item{\code{radical}}
264
  %\item{\code{vcenter}}
265
\end{itemize}
266
\noindent The spacing between any two successive tokens is set automatically based on their atom types, and hence may not reflect the actual spacing used in the input.
267
To make an operator behave like it has a certain atom type, you can use the \code{atom} attribute. For example, \code{a \\mo[atom=bin]\{div\} b} renders as \math[mode=display]{a \mo[atom=bin]{div} b.}
268

269
Spaces in math mode are defined in “math units” (mu), which are 1/18 of an em of the current \em{math} font (and are independent of the current text font size).
270
Standard spaces inserted automatically between tokens come in three varieties: thin (3 mu), medium (4 mu) and thick (5 mu).
271
If needed, you can insert them manually with the \code{\\thinspace} (or \code{\\,}), \code{\\medspace} (or \code{\\>}), and \code{\\thickspace} (or \code{\\;}) commands.
272
Negative space counterparts are available as \code{\\negthinspace} (or \code{\\!}), \code{\\negmedspace}, and \code{\\negthickspace}.
273
The \code{\\enspace}, \code{\\quad}, and \code{\\qquad} commands from normal text mode are also available, but the spaces they insert scale relative to the text font size.
274
Finally, you can add a space of any size using the \code{\\mspace[width=<dimension>]} command.
275

276
\paragraph{Macros}
277
To save you some typing, the math syntax lets you define macros with the following syntax:
278

279
\begin[type=autodoc:codeblock]{raw}
280
\def{macro-name}{macro-body}
281
\end{raw}
282

283
\noindent where in the macro’s body \code{#1}, \code{#2}, etc. will be replaced by the macro’s arguments.
284
For instance:
285

286
\begin[type=autodoc:codeblock]{raw}
287
\begin[mode=display]{math}
288
    \def{diff}{\mfrac{\mo{d}#1}{\mo{d}#2}}
289
    \def{bi}{\mi[mathvariant=bold-italic]{#1}}
290

291
    \diff{\bi{p}}{t} = ∑_i \bi{F}_i
292
\end{math}
293
\end{raw}
294

295
\noindent results in:
296

297
\begin[mode=display]{math}
298
  \def{diff}{\mfrac{\mo{d}#1}{\mo{d}#2}}
299
  \def{bi}{\mi[mathvariant=bold-italic]{#1}}
300
  \diff{\bi{p}}{t} = ∑_i \bi{F}_i
301
\end{math}
302

303
When macros are not enough, creating new mathematical elements is quite simple: one only needs to create a new class deriving from \code{mbox} (defined in \code{packages/math/base-elements.lua}) and define the \code{shape} and \code{output} methods.
304
\code{shape} must define the \code{width}, \code{height} and \code{depth} attributes of the element, while \code{output} must draw the actual output.
305
An \code{mbox} may have one or more children (for instance, a fraction has two children—its numerator and denominator).
306
The \code{shape} and \code{output} methods of the children are called automatically.
307

308
\paragraph{Matrices, aligned equations, and other tables}
309
Tabular math can be typeset using the \code{table} command (or equivalently the \code{mtable} MathML tag).
310
For instance, to typeset a matrix:
311

312
\begin[type=autodoc:codeblock]{raw}
313
\begin[mode=display]{math}
314
    (
315
    \table{
316
        1 & 2 & 7 \\
317
        0 & 5 & 3 \\
318
        8 & 2 & 1 \\
319
    }
320
    )
321
\end{math}
322
\end{raw}
323

324
\noindent will yield:
325

326
\begin[mode=display]{math}
327
  (\table{
328
       1 & 2 & 7 \\
329
       0 & 5 & 3 \\
330
       8 & 2 & 1 \\
331
  })
332
\end{math}
333

334
\noindent Tables may also be used to control the alignment of formulas:
335

336
\begin[type=autodoc:codeblock]{raw}
337
\begin[mode=display]{math}
338
    \{
339
    \table[columnalign=right center left]{
340
        u_0 &=& 1 \\
341
        u_1 &=& 1 \\
342
        u_n &=& u_{n−1} + u_{n−2}, \forall n ⩾ 2 \\
343
    }
344
\end{math}
345
\end{raw}
346

347
\begin[mode=display]{math}
348
    \{
349
    \table[columnalign=right center left]{
350
        u_0 &=& 1 \\
351
        u_1 &=& 1 \\
352
        u_n &=& u_{n−1} + u_{n−2}, \forall n ⩾ 2 \\
353
    }
354
\end{math}
355

356
\noindent Tables currently do not support all attributes required by the MathML standard, but they do allow to control spacing using the \code{rowspacing} and \code{columnspacing} options.
357

358
Finally, here is a little secret. This notation:
359

360
\begin[type=autodoc:codeblock]{raw}
361
\table{
362
    1 & 2 & 7 \\
363
    0 & 5 & 3 \\
364
    8 & 2 & 1 \\
365
}
366
\end{raw}
367

368
\noindent is strictly equivalent to this one:
369

370
\begin[type=autodoc:codeblock]{raw}
371
\table{
372
        {1} {2} {7}
373
    }{
374
        {0} {5} {3}
375
    }{
376
        {8} {2} {1}
377
    }
378
}
379
\end{raw}
380

381
\noindent In other words, the notation using \code{&} and \code{\\\\} is only a syntactic sugar for a two-dimensional array constructed with braces.
382

383
\paragraph{Numbered equations}
384
Equations can be numbered in display mode.
385

386
When \autodoc:parameter{numbered=true}, equations are numbered using a default “equation” counter:
387
\math[mode=display, numbered=true]{e^{i\pi} = -1}
388

389
A different counter can be set by using the option \autodoc:parameter{counter=<id>}, and this setting will also enable numbering.
390

391
It is also possible to impose direct numbering using the \autodoc:parameter{number=<value>} option.
392

393
The default numbering format is \autodoc:example{(n)}, but this style may be overridden by defining a custom \autodoc:command{\math:numberingstyle} command.
394
The \code{counter} or the direct value \code{number} is passed as a parameter to this hook, as well as any other options.
395

396
\paragraph{Missing features}
397
This package still lacks support for some mathematical constructs, but hopefully we’ll get there.
398
Among unsupported constructs are: decorating symbols with so-called accents, such as arrows or hats, “over” or “under” braces, and line breaking inside a formula.
399

400
\font:remove-fallback
401
\end{document}
402
]]
10✔
403

404
return package
10✔
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