@pretextbook/pretext-html 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +37 -6
  2. package/assets/preview-html.xsl +8 -4
  3. package/assets/upstream.json +1 -1
  4. package/assets/xsl/README.md +25 -24
  5. package/assets/xsl/entities.ent +20 -0
  6. package/assets/xsl/extract-biblio-csl.xsl +1 -1
  7. package/assets/xsl/extract-pg.xsl +111 -297
  8. package/assets/xsl/extract-qrcode.xsl +11 -1
  9. package/assets/xsl/extract-sageplot.xsl +1 -1
  10. package/assets/xsl/latex/pretext-latex-texstyle.xsl +1 -2
  11. package/assets/xsl/localizations/README.md +24 -24
  12. package/assets/xsl/pretext-assembly.xsl +101 -11
  13. package/assets/xsl/pretext-beamer.xsl +43 -146
  14. package/assets/xsl/pretext-braille-preprint.xsl +457 -201
  15. package/assets/xsl/pretext-common.xsl +388 -423
  16. package/assets/xsl/pretext-epub.xsl +301 -72
  17. package/assets/xsl/pretext-fo.xsl +70 -86
  18. package/assets/xsl/pretext-html.xsl +331 -259
  19. package/assets/xsl/pretext-jupyter.xsl +534 -322
  20. package/assets/xsl/pretext-latex-classic.xsl +14 -15
  21. package/assets/xsl/pretext-latex-common.xsl +385 -2389
  22. package/assets/xsl/pretext-latex.xsl +1995 -5
  23. package/assets/xsl/pretext-markdown.xsl +919 -0
  24. package/assets/xsl/pretext-numbers.xsl +22 -4
  25. package/assets/xsl/pretext-revealjs.xsl +241 -33
  26. package/assets/xsl/pretext-runestone-fitb.xsl +20 -22
  27. package/assets/xsl/pretext-runestone.xsl +3 -3
  28. package/assets/xsl/pretext-sage-doctest.xsl +2 -2
  29. package/assets/xsl/pretext-text-utilities.xsl +69 -1
  30. package/assets/xsl/pretext-text.xsl +1077 -107
  31. package/assets/xsl/publisher-variables.xsl +396 -452
  32. package/assets/xsl/support/play-button/README.md +1 -3
  33. package/assets/xsl/tests/README.md +1 -1
  34. package/assets/xsl/utilities/README.md +6 -8
  35. package/assets/xsl/utilities/report-publisher-variables.xsl +15 -0
  36. package/assets/xsl-bundle.json +1 -1
  37. package/dist/assets.d.ts +43 -0
  38. package/dist/assets.js +76 -0
  39. package/dist/cli-Cp6C0sif.js +654 -0
  40. package/dist/cli.js +1 -1
  41. package/dist/index.browser.js +367 -307
  42. package/dist/index.d.ts +5 -0
  43. package/dist/index.js +16 -11
  44. package/dist/missing-asset.d.ts +31 -0
  45. package/dist/publication.d.ts +36 -1
  46. package/dist/renderer.d.ts +49 -0
  47. package/package.json +9 -1
  48. package/dist/cli-B2LiKunq.js +0 -588
@@ -0,0 +1,919 @@
1
+ <?xml version='1.0'?>
2
+
3
+ <!--********************************************************************
4
+ Copyright 2026 Robert A. Beezer
5
+
6
+ This file is part of PreTeXt.
7
+
8
+ PreTeXt is free software: you can redistribute it and/or modify
9
+ it under the terms of the GNU General Public License as published by
10
+ the Free Software Foundation, either version 2 or version 3 of the
11
+ License (at your option).
12
+
13
+ PreTeXt is distributed in the hope that it will be useful,
14
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ GNU General Public License for more details.
17
+
18
+ You should have received a copy of the GNU General Public License
19
+ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
20
+ *********************************************************************-->
21
+
22
+ <!-- http://pimpmyxslt.com/articles/entity-tricks-part2/ -->
23
+ <!DOCTYPE xsl:stylesheet [
24
+ <!ENTITY % entities SYSTEM "entities.ent">
25
+ %entities;
26
+ ]>
27
+
28
+ <xsl:stylesheet
29
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
30
+ xmlns:xml="http://www.w3.org/XML/1998/namespace"
31
+ xmlns:exsl="http://exslt.org/common"
32
+ xmlns:str="http://exslt.org/strings"
33
+ xmlns:pf="https://prefigure.org"
34
+ xmlns:pi="http://pretextbook.org/2020/pretext/internal"
35
+ extension-element-prefixes="exsl str"
36
+ exclude-result-prefixes="pf pi"
37
+ >
38
+
39
+ <!-- The markdown conversion IS the text conversion, plus the -->
40
+ <!-- conventions markdown makes machine-recognizable: ATX -->
41
+ <!-- headings, emphasis, code spans, links, and an escaping -->
42
+ <!-- layer so authored punctuation is never mistaken for markup. -->
43
+ <!-- The target is valid CommonMark. The library lives here so -->
44
+ <!-- other conversions producing markdown (Jupyter notebook -->
45
+ <!-- cells) can import it and share single definitions. -->
46
+ <xsl:import href="./pretext-text.xsl"/>
47
+
48
+ <xsl:output method="text"/>
49
+
50
+ <!-- if chunking, this is the extension of the files produced -->
51
+ <xsl:variable name="file-extension" select="'.md'"/>
52
+
53
+ <!-- ######## -->
54
+ <!-- Escaping -->
55
+ <!-- ######## -->
56
+
57
+ <!-- Prose flows through -common's "text()" machinery to the -->
58
+ <!-- "text-processing" hook: flatten authored line breaks (as the -->
59
+ <!-- text conversion does), then backslash-escape every character -->
60
+ <!-- that could be mistaken for markdown or HTML markup. (Math -->
61
+ <!-- and verbatim content never reach this hook.) CommonMark -->
62
+ <!-- honors a backslash before any ASCII punctuation, so -->
63
+ <!-- over-escaping is always safe, if unlovely; the set below is -->
64
+ <!-- the characters that can OPEN a construction mid-prose, plus -->
65
+ <!-- the dollar sign, which mathematics has claimed. -->
66
+ <xsl:variable name="markdown-escapes" select="'\`*_[]&lt;&amp;$|'"/>
67
+
68
+ <xsl:template name="text-processing">
69
+ <xsl:param name="text"/>
70
+ <xsl:variable name="flattened">
71
+ <xsl:call-template name="flatten-line-breaks">
72
+ <xsl:with-param name="text" select="$text"/>
73
+ </xsl:call-template>
74
+ </xsl:variable>
75
+ <xsl:call-template name="escape-markdown">
76
+ <xsl:with-param name="text" select="$flattened"/>
77
+ </xsl:call-template>
78
+ </xsl:template>
79
+
80
+ <!-- one pass per character of the escape set -->
81
+ <xsl:template name="escape-markdown">
82
+ <xsl:param name="text"/>
83
+ <xsl:param name="position" select="1"/>
84
+ <xsl:choose>
85
+ <xsl:when test="$position &gt; string-length($markdown-escapes)">
86
+ <xsl:value-of select="$text"/>
87
+ </xsl:when>
88
+ <xsl:otherwise>
89
+ <xsl:variable name="escaped">
90
+ <xsl:call-template name="escape-one-character">
91
+ <xsl:with-param name="text" select="$text"/>
92
+ <xsl:with-param name="character" select="substring($markdown-escapes, $position, 1)"/>
93
+ </xsl:call-template>
94
+ </xsl:variable>
95
+ <xsl:call-template name="escape-markdown">
96
+ <xsl:with-param name="text" select="$escaped"/>
97
+ <xsl:with-param name="position" select="$position + 1"/>
98
+ </xsl:call-template>
99
+ </xsl:otherwise>
100
+ </xsl:choose>
101
+ </xsl:template>
102
+
103
+ <xsl:template name="escape-one-character">
104
+ <xsl:param name="text"/>
105
+ <xsl:param name="character"/>
106
+ <xsl:choose>
107
+ <xsl:when test="contains($text, $character)">
108
+ <xsl:value-of select="substring-before($text, $character)"/>
109
+ <xsl:text>\</xsl:text>
110
+ <xsl:value-of select="$character"/>
111
+ <xsl:call-template name="escape-one-character">
112
+ <xsl:with-param name="text" select="substring-after($text, $character)"/>
113
+ <xsl:with-param name="character" select="$character"/>
114
+ </xsl:call-template>
115
+ </xsl:when>
116
+ <xsl:otherwise>
117
+ <xsl:value-of select="$text"/>
118
+ </xsl:otherwise>
119
+ </xsl:choose>
120
+ </xsl:template>
121
+
122
+ <!-- ######## -->
123
+ <!-- Headings -->
124
+ <!-- ######## -->
125
+
126
+ <!-- ATX headings: honest levels, one octothorpe per depth, the -->
127
+ <!-- document title at level one, divisions one deeper. Only -->
128
+ <!-- the heading lines change flavor; the structure of a -->
129
+ <!-- division (content, collected footnotes) is inherited. -->
130
+ <xsl:template match="book|article" mode="heading-lines">
131
+ <xsl:text># </xsl:text>
132
+ <xsl:apply-templates select="." mode="fragment-anchor"/>
133
+ <xsl:apply-templates select="." mode="title-full"/>
134
+ <xsl:text>&#xa;&#xa;</xsl:text>
135
+ </xsl:template>
136
+
137
+ <xsl:template match="*" mode="heading-lines">
138
+ <xsl:variable name="raw-level">
139
+ <xsl:apply-templates select="." mode="level"/>
140
+ </xsl:variable>
141
+ <xsl:variable name="level">
142
+ <xsl:choose>
143
+ <xsl:when test="$raw-level &gt; 5">
144
+ <xsl:text>5</xsl:text>
145
+ </xsl:when>
146
+ <xsl:otherwise>
147
+ <xsl:value-of select="$raw-level"/>
148
+ </xsl:otherwise>
149
+ </xsl:choose>
150
+ </xsl:variable>
151
+ <xsl:text>&#xa;</xsl:text>
152
+ <xsl:value-of select="substring('######', 1, $level + 1)"/>
153
+ <xsl:text> </xsl:text>
154
+ <xsl:apply-templates select="." mode="fragment-anchor"/>
155
+ <xsl:apply-templates select="." mode="heading-text"/>
156
+ <xsl:text>&#xa;&#xa;</xsl:text>
157
+ </xsl:template>
158
+
159
+ <!-- ##################### -->
160
+ <!-- Anchors and Fragments -->
161
+ <!-- ##################### -->
162
+
163
+ <!-- The @xml:id of every element some "xref" points at, -->
164
+ <!-- delimited for exact-match testing; the enumeration mirrors -->
165
+ <!-- the HTML conversion's knowl manufacture (which could migrate -->
166
+ <!-- to -common one day, and both would share it) -->
167
+ <xsl:variable name="xref-target-ids-rtf">
168
+ <xsl:for-each select="$document-root//xref">
169
+ <xsl:choose>
170
+ <!-- just use @first, clean-up spaces -->
171
+ <xsl:when test="@first and @last">
172
+ <xid>
173
+ <xsl:value-of select="normalize-space(@first)"/>
174
+ </xid>
175
+ <xid>
176
+ <xsl:value-of select="normalize-space(@last)"/>
177
+ </xid>
178
+ </xsl:when>
179
+ <!-- a space-separated or comma-separated list -->
180
+ <xsl:when test="@ref and (contains(normalize-space(@ref), ' ') or contains(@ref, ','))">
181
+ <xsl:call-template name="split-ref-list">
182
+ <xsl:with-param name="list" select="concat(normalize-space(translate(@ref, ',', ' ')), ' ')"/>
183
+ </xsl:call-template>
184
+ </xsl:when>
185
+ <xsl:when test="@ref">
186
+ <xid>
187
+ <xsl:value-of select="normalize-space(@ref)"/>
188
+ </xid>
189
+ </xsl:when>
190
+ <xsl:otherwise/>
191
+ </xsl:choose>
192
+ </xsl:for-each>
193
+ </xsl:variable>
194
+
195
+ <xsl:template name="split-ref-list">
196
+ <xsl:param name="list"/>
197
+ <xsl:choose>
198
+ <xsl:when test="$list = ''"/>
199
+ <xsl:otherwise>
200
+ <xid>
201
+ <xsl:value-of select="substring-before($list, ' ')"/>
202
+ </xid>
203
+ <xsl:call-template name="split-ref-list">
204
+ <xsl:with-param name="list" select="substring-after($list, ' ')"/>
205
+ </xsl:call-template>
206
+ </xsl:otherwise>
207
+ </xsl:choose>
208
+ </xsl:template>
209
+
210
+ <xsl:variable name="xref-target-list">
211
+ <xsl:text>|</xsl:text>
212
+ <xsl:for-each select="exsl:node-set($xref-target-ids-rtf)/xid">
213
+ <xsl:value-of select="."/>
214
+ <xsl:text>|</xsl:text>
215
+ </xsl:for-each>
216
+ </xsl:variable>
217
+
218
+ <!-- An invisible landing spot, as raw inline HTML (legal -->
219
+ <!-- CommonMark), riding inside a heading line so it never forms -->
220
+ <!-- an HTML block. Emitted only where a link may land: elements -->
221
+ <!-- some "xref" targets, and elements containing an "idx" (the -->
222
+ <!-- index's locators climb to them) — so a document with no -->
223
+ <!-- cross-references produces markdown with no anchors at all. -->
224
+ <xsl:template match="*" mode="fragment-anchor">
225
+ <xsl:if test="(@xml:id and contains($xref-target-list, concat('|', @xml:id, '|'))) or .//idx">
226
+ <xsl:text>&lt;a id="</xsl:text>
227
+ <xsl:apply-templates select="." mode="unique-id"/>
228
+ <xsl:text>"&gt;&lt;/a&gt;</xsl:text>
229
+ </xsl:if>
230
+ </xsl:template>
231
+
232
+ <!-- The address of an element: its file when chunked (elided -->
233
+ <!-- within that same file), and its fragment anchor. A fragment -->
234
+ <!-- without an anchor still lands on the right file. -->
235
+ <xsl:template name="markdown-url">
236
+ <xsl:param name="origin"/>
237
+ <xsl:param name="target"/>
238
+ <xsl:if test="$chunk-level &gt; 0">
239
+ <xsl:variable name="origin-file">
240
+ <xsl:apply-templates select="$origin" mode="containing-filename"/>
241
+ </xsl:variable>
242
+ <xsl:variable name="target-file">
243
+ <xsl:apply-templates select="$target" mode="containing-filename"/>
244
+ </xsl:variable>
245
+ <xsl:if test="not($origin-file = $target-file)">
246
+ <xsl:value-of select="$target-file"/>
247
+ </xsl:if>
248
+ </xsl:if>
249
+ <xsl:text>#</xsl:text>
250
+ <xsl:apply-templates select="$target" mode="unique-id"/>
251
+ </xsl:template>
252
+
253
+ <!-- A cross-reference is a genuine link to its target's address -->
254
+ <xsl:template match="xref" mode="xref-link">
255
+ <xsl:param name="target"/>
256
+ <xsl:param name="content"/>
257
+ <xsl:choose>
258
+ <!-- no target, or no text to carry a link: leave it be -->
259
+ <xsl:when test="not($target) or $content = ''">
260
+ <xsl:value-of select="$content"/>
261
+ </xsl:when>
262
+ <xsl:otherwise>
263
+ <xsl:text>[</xsl:text>
264
+ <xsl:value-of select="$content"/>
265
+ <xsl:text>](</xsl:text>
266
+ <xsl:call-template name="markdown-url">
267
+ <xsl:with-param name="origin" select="."/>
268
+ <xsl:with-param name="target" select="$target"/>
269
+ </xsl:call-template>
270
+ <xsl:text>)</xsl:text>
271
+ </xsl:otherwise>
272
+ </xsl:choose>
273
+ </xsl:template>
274
+
275
+ <!-- A bibliography entry carries its anchor, a frequent target. -->
276
+ <!-- (Not "apply-imports": XSLT 1.0 would not forward the -->
277
+ <!-- content parameter.) -->
278
+ <xsl:template match="biblio" mode="bibentry-wrapper">
279
+ <xsl:param name="content"/>
280
+ <xsl:if test="preceding-sibling::biblio">
281
+ <xsl:text>&#xa;</xsl:text>
282
+ </xsl:if>
283
+ <xsl:apply-templates select="." mode="fragment-anchor"/>
284
+ <xsl:text>[</xsl:text>
285
+ <xsl:apply-templates select="." mode="serial-number"/>
286
+ <xsl:text>] </xsl:text>
287
+ <xsl:copy-of select="$content"/>
288
+ <xsl:text>&#xa;</xsl:text>
289
+ </xsl:template>
290
+
291
+ <!-- ######## -->
292
+ <!-- Emphasis -->
293
+ <!-- ######## -->
294
+
295
+ <xsl:template match="em|foreign|articletitle|pubtitle">
296
+ <xsl:text>*</xsl:text>
297
+ <xsl:apply-templates/>
298
+ <xsl:text>*</xsl:text>
299
+ </xsl:template>
300
+
301
+ <xsl:template match="term|alert">
302
+ <xsl:text>**</xsl:text>
303
+ <xsl:apply-templates/>
304
+ <xsl:text>**</xsl:text>
305
+ </xsl:template>
306
+
307
+ <!-- fonts demanded by -common machinery (bibliographies) -->
308
+ <xsl:template match="*" mode="italic">
309
+ <xsl:text>*</xsl:text>
310
+ <xsl:apply-templates/>
311
+ <xsl:text>*</xsl:text>
312
+ </xsl:template>
313
+
314
+ <xsl:template match="*" mode="bold">
315
+ <xsl:text>**</xsl:text>
316
+ <xsl:apply-templates/>
317
+ <xsl:text>**</xsl:text>
318
+ </xsl:template>
319
+
320
+ <xsl:template match="*" mode="monospace">
321
+ <xsl:text>`</xsl:text>
322
+ <xsl:apply-templates/>
323
+ <xsl:text>`</xsl:text>
324
+ </xsl:template>
325
+
326
+ <!-- #### -->
327
+ <!-- Code -->
328
+ <!-- #### -->
329
+
330
+ <!-- backtick spans; content with a backtick gets double-backtick -->
331
+ <!-- delimiters and interior space, per CommonMark -->
332
+ <xsl:template name="code-wrapper">
333
+ <xsl:param name="content"/>
334
+ <xsl:choose>
335
+ <xsl:when test="contains($content, '`')">
336
+ <xsl:text>`` </xsl:text>
337
+ <xsl:copy-of select="$content"/>
338
+ <xsl:text> ``</xsl:text>
339
+ </xsl:when>
340
+ <xsl:otherwise>
341
+ <xsl:text>`</xsl:text>
342
+ <xsl:copy-of select="$content"/>
343
+ <xsl:text>`</xsl:text>
344
+ </xsl:otherwise>
345
+ </xsl:choose>
346
+ </xsl:template>
347
+
348
+ <!-- #### -->
349
+ <!-- URLs -->
350
+ <!-- #### -->
351
+
352
+ <!-- a labeled link; a bare address is already an autolink in -->
353
+ <!-- the inherited angle-bracket form -->
354
+ <xsl:template match="url[node()]">
355
+ <xsl:text>[</xsl:text>
356
+ <xsl:apply-templates/>
357
+ <xsl:text>](</xsl:text>
358
+ <xsl:value-of select="@href"/>
359
+ <xsl:text>)</xsl:text>
360
+ </xsl:template>
361
+
362
+ <!-- #### -->
363
+ <!-- Math -->
364
+ <!-- #### -->
365
+
366
+ <!-- Display mathematics in dollar-fenced blocks, which GitHub -->
367
+ <!-- and Jupyter renderers typeset; four-space indentation would -->
368
+ <!-- read as a code block, so rows sit flush left. Inside the -->
369
+ <!-- fence the rows ride a genuine *inner* alignment environment -->
370
+ <!-- (aligned, gathered, alignedat) chosen by -common's analysis, -->
371
+ <!-- since an ampersand (or "\amp") is only legal inside one, and -->
372
+ <!-- rows separate with the "\\" a renderer requires. -->
373
+ <xsl:template match="md[mrow]">
374
+ <xsl:variable name="raw-alignment">
375
+ <xsl:apply-templates select="." mode="displaymath-alignment">
376
+ <xsl:with-param name="b-needs-tags" select="false()"/>
377
+ </xsl:apply-templates>
378
+ </xsl:variable>
379
+ <xsl:variable name="environment">
380
+ <xsl:choose>
381
+ <xsl:when test="contains($raw-alignment, 'alignat')">
382
+ <xsl:text>alignedat</xsl:text>
383
+ </xsl:when>
384
+ <xsl:when test="contains($raw-alignment, 'align')">
385
+ <xsl:text>aligned</xsl:text>
386
+ </xsl:when>
387
+ <xsl:when test="contains($raw-alignment, 'gather')">
388
+ <xsl:text>gathered</xsl:text>
389
+ </xsl:when>
390
+ <!-- "equation": a single row, no environment needed -->
391
+ <xsl:otherwise/>
392
+ </xsl:choose>
393
+ </xsl:variable>
394
+ <xsl:text>&#xa;$$&#xa;</xsl:text>
395
+ <xsl:if test="not($environment = '')">
396
+ <xsl:text>\begin{</xsl:text>
397
+ <xsl:value-of select="$environment"/>
398
+ <xsl:text>}</xsl:text>
399
+ <xsl:apply-templates select="." mode="alignat-columns"/>
400
+ <xsl:text>&#xa;</xsl:text>
401
+ </xsl:if>
402
+ <xsl:apply-templates select="mrow|intertext"/>
403
+ <xsl:if test="not($environment = '')">
404
+ <xsl:text>\end{</xsl:text>
405
+ <xsl:value-of select="$environment"/>
406
+ <xsl:text>}&#xa;</xsl:text>
407
+ </xsl:if>
408
+ <xsl:text>$$&#xa;</xsl:text>
409
+ </xsl:template>
410
+
411
+ <xsl:template match="md[not(mrow)]">
412
+ <xsl:text>&#xa;$$&#xa;</xsl:text>
413
+ <xsl:value-of select="normalize-space(text())"/>
414
+ <xsl:apply-templates select="." mode="get-clause-punctuation-mark"/>
415
+ <xsl:text>&#xa;$$&#xa;</xsl:text>
416
+ </xsl:template>
417
+
418
+ <xsl:template match="mrow">
419
+ <xsl:value-of select="normalize-space(.)"/>
420
+ <xsl:choose>
421
+ <xsl:when test="following-sibling::mrow">
422
+ <xsl:text>\\</xsl:text>
423
+ </xsl:when>
424
+ <xsl:otherwise>
425
+ <xsl:apply-templates select="parent::md" mode="get-clause-punctuation-mark"/>
426
+ </xsl:otherwise>
427
+ </xsl:choose>
428
+ <xsl:text>&#xa;</xsl:text>
429
+ </xsl:template>
430
+
431
+ <!-- A structured line breaks with a raw HTML break: legal -->
432
+ <!-- CommonMark, and it keeps a multi-line cell on the one -->
433
+ <!-- physical line a pipe table demands -->
434
+ <xsl:template name="line-separator">
435
+ <xsl:text>&lt;br&gt;</xsl:text>
436
+ </xsl:template>
437
+
438
+ <!-- ###### -->
439
+ <!-- Tables -->
440
+ <!-- ###### -->
441
+
442
+ <!-- A tabular of simple cells becomes a pipe table: the authored -->
443
+ <!-- header row when there is one (a row of empty header cells -->
444
+ <!-- otherwise, which renderers accept), a delimiter row, then -->
445
+ <!-- the body. A tabular with structured cells (paragraphs) -->
446
+ <!-- keeps the plain conversion's row-per-line form. Cell text -->
447
+ <!-- is safe in a cell: "|" is in the escaping set. -->
448
+ <xsl:template match="tabular">
449
+ <xsl:choose>
450
+ <xsl:when test="row/cell/p">
451
+ <xsl:apply-imports/>
452
+ </xsl:when>
453
+ <xsl:otherwise>
454
+ <xsl:text>&#xa;</xsl:text>
455
+ <xsl:choose>
456
+ <xsl:when test="row[1][@header = 'yes']">
457
+ <xsl:apply-templates select="row[1]" mode="pipe-row"/>
458
+ <xsl:apply-templates select="row[1]" mode="pipe-delimiter"/>
459
+ <xsl:apply-templates select="row[position() &gt; 1]" mode="pipe-row"/>
460
+ </xsl:when>
461
+ <xsl:otherwise>
462
+ <xsl:apply-templates select="row[1]" mode="pipe-empty-header"/>
463
+ <xsl:apply-templates select="row[1]" mode="pipe-delimiter"/>
464
+ <xsl:apply-templates select="row" mode="pipe-row"/>
465
+ </xsl:otherwise>
466
+ </xsl:choose>
467
+ <xsl:text>&#xa;</xsl:text>
468
+ </xsl:otherwise>
469
+ </xsl:choose>
470
+ </xsl:template>
471
+
472
+ <xsl:template match="row" mode="pipe-row">
473
+ <xsl:text>|</xsl:text>
474
+ <xsl:for-each select="cell">
475
+ <xsl:text> </xsl:text>
476
+ <xsl:apply-templates/>
477
+ <xsl:text> |</xsl:text>
478
+ </xsl:for-each>
479
+ <xsl:text>&#xa;</xsl:text>
480
+ </xsl:template>
481
+
482
+ <xsl:template match="row" mode="pipe-delimiter">
483
+ <xsl:text>|</xsl:text>
484
+ <xsl:for-each select="cell">
485
+ <xsl:text>---|</xsl:text>
486
+ </xsl:for-each>
487
+ <xsl:text>&#xa;</xsl:text>
488
+ </xsl:template>
489
+
490
+ <xsl:template match="row" mode="pipe-empty-header">
491
+ <xsl:text>|</xsl:text>
492
+ <xsl:for-each select="cell">
493
+ <xsl:text> |</xsl:text>
494
+ </xsl:for-each>
495
+ <xsl:text>&#xa;</xsl:text>
496
+ </xsl:template>
497
+
498
+ <!-- ###### -->
499
+ <!-- Images -->
500
+ <!-- ###### -->
501
+
502
+ <!-- A filename without an extension presumes a manufactured -->
503
+ <!-- SVG, exactly as the HTML conversion does -->
504
+ <xsl:template name="presumed-svg">
505
+ <xsl:param name="filename"/>
506
+ <xsl:variable name="extension">
507
+ <xsl:call-template name="file-extension">
508
+ <xsl:with-param name="filename" select="$filename"/>
509
+ </xsl:call-template>
510
+ </xsl:variable>
511
+ <xsl:if test="$extension = ''">
512
+ <xsl:text>.svg</xsl:text>
513
+ </xsl:if>
514
+ </xsl:template>
515
+
516
+ <!-- The relative path of an image's file, matching both the HTML -->
517
+ <!-- conversion's references and the managed directories deposited -->
518
+ <!-- alongside a markdown build; empty when there is no file to -->
519
+ <!-- point at (an interactive Asymptote diagram, say) -->
520
+ <xsl:template match="image" mode="markdown-path">
521
+ <xsl:choose>
522
+ <xsl:when test="@pi:generated">
523
+ <xsl:value-of select="$generated-directory"/>
524
+ <xsl:value-of select="@pi:generated"/>
525
+ <xsl:call-template name="presumed-svg">
526
+ <xsl:with-param name="filename" select="@pi:generated"/>
527
+ </xsl:call-template>
528
+ </xsl:when>
529
+ <xsl:when test="@source">
530
+ <xsl:value-of select="$external-directory"/>
531
+ <xsl:value-of select="@source"/>
532
+ <xsl:call-template name="presumed-svg">
533
+ <xsl:with-param name="filename" select="@source"/>
534
+ </xsl:call-template>
535
+ </xsl:when>
536
+ <xsl:when test="latex-image">
537
+ <xsl:value-of select="$generated-directory"/>
538
+ <xsl:text>latex-image/</xsl:text>
539
+ <xsl:apply-templates select="latex-image" mode="image-source-basename"/>
540
+ <xsl:text>.svg</xsl:text>
541
+ </xsl:when>
542
+ <xsl:when test="sageplot[not(@variant = '3d')]">
543
+ <xsl:value-of select="$generated-directory"/>
544
+ <xsl:text>sageplot/</xsl:text>
545
+ <xsl:apply-templates select="sageplot" mode="image-source-basename"/>
546
+ <xsl:text>.svg</xsl:text>
547
+ </xsl:when>
548
+ <xsl:when test="pf:prefigure">
549
+ <xsl:value-of select="$generated-directory"/>
550
+ <xsl:text>prefigure/</xsl:text>
551
+ <xsl:apply-templates select="pf:prefigure" mode="image-source-basename"/>
552
+ <xsl:text>.svg</xsl:text>
553
+ </xsl:when>
554
+ <xsl:otherwise/>
555
+ </xsl:choose>
556
+ </xsl:template>
557
+
558
+ <!-- Composition of a "sidebyside" is inherited from the text -->
559
+ <!-- conversion: in a linear format panels simply follow one -->
560
+ <!-- another. The one place layout means anything here is an -->
561
+ <!-- image's width percentage, so the -common "layout-parameters" -->
562
+ <!-- machinery is consulted exactly there (see the "image" template -->
563
+ <!-- below). -->
564
+
565
+ <!-- An image is always a raw-HTML "img" (legal CommonMark, and -->
566
+ <!-- markdown's own syntax has no size or placement), so an -->
567
+ <!-- authored @width in percent is honored (100% is the default). -->
568
+ <!-- Centered by default, via an "align" wrapper that forge -->
569
+ <!-- sanitizers keep (they strip "style"). An *authored* margin -->
570
+ <!-- of 10% or less pins the image to that edge instead. Without -->
571
+ <!-- a file to reference, the producer's note of the text -->
572
+ <!-- conversion stands. -->
573
+ <xsl:template match="image">
574
+ <xsl:variable name="path">
575
+ <xsl:apply-templates select="." mode="markdown-path"/>
576
+ </xsl:variable>
577
+ <xsl:choose>
578
+ <xsl:when test="not($path = '')">
579
+ <xsl:variable name="rtf-layout">
580
+ <xsl:apply-templates select="." mode="layout-parameters"/>
581
+ </xsl:variable>
582
+ <xsl:variable name="layout" select="exsl:node-set($rtf-layout)"/>
583
+ <!-- A panel of a "sidebyside" is not aligned (it sits on -->
584
+ <!-- the left edge, as any panel does); otherwise the -->
585
+ <!-- layout reports margins as bare numbers, anything -->
586
+ <!-- unresolvable is NaN, both comparisons fail, and -->
587
+ <!-- centering wins -->
588
+ <xsl:variable name="alignment">
589
+ <xsl:choose>
590
+ <xsl:when test="ancestor::sidebyside"/>
591
+ <xsl:when test="@margins and (number($layout/left-margin) &lt;= 10)">
592
+ <xsl:text>left</xsl:text>
593
+ </xsl:when>
594
+ <xsl:when test="@margins and (number($layout/right-margin) &lt;= 10)">
595
+ <xsl:text>right</xsl:text>
596
+ </xsl:when>
597
+ <xsl:otherwise>
598
+ <xsl:text>center</xsl:text>
599
+ </xsl:otherwise>
600
+ </xsl:choose>
601
+ </xsl:variable>
602
+ <!-- The layouts report bare numbers; the "img" attribute -->
603
+ <!-- carries a percentage (a bare number would be pixels).-->
604
+ <!-- A panel image takes its width from the enclosing -->
605
+ <!-- "sidebyside" layout, locating its panel as -common -->
606
+ <!-- does (a "figure" or "stack" wrapper is the panel) -->
607
+ <xsl:variable name="width">
608
+ <xsl:choose>
609
+ <xsl:when test="ancestor::sidebyside">
610
+ <xsl:variable name="rtf-sbs-layout">
611
+ <xsl:apply-templates select="ancestor::sidebyside" mode="layout-parameters"/>
612
+ </xsl:variable>
613
+ <xsl:variable name="sbs-layout" select="exsl:node-set($rtf-sbs-layout)"/>
614
+ <xsl:variable name="panel-number">
615
+ <xsl:choose>
616
+ <xsl:when test="parent::figure or parent::stack">
617
+ <xsl:value-of select="count(parent::*/preceding-sibling::*) + 1"/>
618
+ </xsl:when>
619
+ <xsl:otherwise>
620
+ <xsl:value-of select="count(preceding-sibling::*) + 1"/>
621
+ </xsl:otherwise>
622
+ </xsl:choose>
623
+ </xsl:variable>
624
+ <!-- this layout's widths carry their percent signs -->
625
+ <xsl:value-of select="translate($sbs-layout/width[number($panel-number)], '%', '')"/>
626
+ <xsl:text>%</xsl:text>
627
+ </xsl:when>
628
+ <xsl:when test="not($layout/width = '')">
629
+ <xsl:value-of select="$layout/width"/>
630
+ <xsl:text>%</xsl:text>
631
+ </xsl:when>
632
+ <xsl:otherwise>
633
+ <xsl:text>100%</xsl:text>
634
+ </xsl:otherwise>
635
+ </xsl:choose>
636
+ </xsl:variable>
637
+ <xsl:variable name="alternative-text">
638
+ <xsl:choose>
639
+ <xsl:when test="shortdescription">
640
+ <xsl:apply-templates select="shortdescription"/>
641
+ </xsl:when>
642
+ <xsl:when test="description">
643
+ <xsl:value-of select="normalize-space(description)"/>
644
+ </xsl:when>
645
+ <xsl:otherwise>
646
+ <xsl:text>image</xsl:text>
647
+ </xsl:otherwise>
648
+ </xsl:choose>
649
+ </xsl:variable>
650
+ <xsl:text>&#xa;&lt;p</xsl:text>
651
+ <xsl:if test="not($alignment = '')">
652
+ <xsl:text> align="</xsl:text>
653
+ <xsl:value-of select="$alignment"/>
654
+ <xsl:text>"</xsl:text>
655
+ </xsl:if>
656
+ <xsl:text>&gt;&lt;img src="</xsl:text>
657
+ <xsl:value-of select="$path"/>
658
+ <xsl:text>" alt="</xsl:text>
659
+ <!-- a double quote in a description would end the attribute -->
660
+ <xsl:value-of select="translate($alternative-text, '&quot;', &quot;'&quot;)"/>
661
+ <xsl:text>" width="</xsl:text>
662
+ <xsl:value-of select="$width"/>
663
+ <xsl:text>"&gt;&lt;/p&gt;&#xa;</xsl:text>
664
+ </xsl:when>
665
+ <xsl:otherwise>
666
+ <xsl:apply-imports/>
667
+ </xsl:otherwise>
668
+ </xsl:choose>
669
+ </xsl:template>
670
+
671
+ <!-- ##### -->
672
+ <!-- Lists -->
673
+ <!-- ##### -->
674
+
675
+ <!-- CommonMark ordered-list markers are digits only, so the -->
676
+ <!-- authored label (letters, Roman numerals, ...) rides inside -->
677
+ <!-- a bullet item instead, in the manner of a task: hard-coded, -->
678
+ <!-- and every renderer shows exactly the label PreTeXt chose. -->
679
+ <!-- The label's dot is backslash-escaped: a digit label with a -->
680
+ <!-- bare dot would re-parse as an ordered list nested inside -->
681
+ <!-- the bullet, and renderers would renumber it. -->
682
+ <xsl:template match="ol/li" mode="item-marker">
683
+ <xsl:text>- </xsl:text>
684
+ <xsl:apply-templates select="." mode="item-number"/>
685
+ <xsl:text>\. </xsl:text>
686
+ </xsl:template>
687
+
688
+ <!-- A description-list item is a bullet with its emboldened -->
689
+ <!-- term; consecutive bare lines would otherwise merge into one -->
690
+ <!-- paragraph when rendered -->
691
+ <xsl:template match="dl/li">
692
+ <xsl:apply-templates select="." mode="item-indent"/>
693
+ <xsl:text>- **</xsl:text>
694
+ <xsl:apply-templates select="." mode="title-full"/>
695
+ <xsl:text>** </xsl:text>
696
+ <xsl:apply-templates/>
697
+ <xsl:apply-templates select="." mode="finish-item"/>
698
+ </xsl:template>
699
+
700
+ <!-- a task's serial marker rides inside a genuine list item, -->
701
+ <!-- so renderers indent nested tasks properly -->
702
+ <xsl:template match="task" mode="item-marker">
703
+ <xsl:text>- (</xsl:text>
704
+ <xsl:apply-templates select="." mode="serial-number"/>
705
+ <xsl:text>)</xsl:text>
706
+ </xsl:template>
707
+
708
+ <!-- ###### -->
709
+ <!-- Blocks -->
710
+ <!-- ###### -->
711
+
712
+ <!-- A block's heading line, emboldened, and carrying the -->
713
+ <!-- block's anchor -->
714
+ <xsl:template name="block-heading-line">
715
+ <xsl:param name="heading"/>
716
+ <xsl:apply-templates select="." mode="fragment-anchor"/>
717
+ <xsl:text>**</xsl:text>
718
+ <xsl:copy-of select="$heading"/>
719
+ <xsl:text>**&#xa;&#xa;</xsl:text>
720
+ </xsl:template>
721
+
722
+ <!-- ############ -->
723
+ <!-- Macro Support -->
724
+ <!-- ############ -->
725
+
726
+ <!-- Legacy fraction detection, as in the HTML conversion -->
727
+ <xsl:variable name="b-has-sfrac" select="boolean($document-root//m[contains(text(),'sfrac')] or $document-root//mrow[contains(text(),'sfrac')])"/>
728
+
729
+ <!-- The author's math packages, massaged to MathJax "\require" -->
730
+ <!-- form. A duplicate of the variable in the HTML conversion -->
731
+ <!-- (which this stylesheet does not import); a migration to -->
732
+ <!-- -common would leave one definition. -->
733
+ <xsl:variable name="latex-packages-mathjax">
734
+ <xsl:for-each select="$docinfo/math-package">
735
+ <!-- must be specified, but can be empty/null -->
736
+ <xsl:if test="not(normalize-space(@mathjax-name)) = ''">
737
+ <xsl:text>\require{</xsl:text>
738
+ <xsl:value-of select="@mathjax-name"/>
739
+ <xsl:apply-templates/>
740
+ <xsl:text>}</xsl:text>
741
+ </xsl:if>
742
+ </xsl:for-each>
743
+ </xsl:variable>
744
+
745
+ <!-- A page with mathematics opens with an invisible display-math -->
746
+ <!-- block: MathJax package requirements, the author's macros, -->
747
+ <!-- and PreTeXt's own (\lt, \gt, and notably \amp). MathJax -->
748
+ <!-- renders a definitions-only block as nothing at all, and the -->
749
+ <!-- definitions persist for every formula on the page, so each -->
750
+ <!-- page is self-sufficient. (The Jupyter conversion does the -->
751
+ <!-- same in a notebook's first cell. KaTeX-based renderers do -->
752
+ <!-- not persist definitions between zones; the block is still -->
753
+ <!-- invisible there, so no harm done.) -->
754
+ <xsl:template match="*" mode="latex-macros">
755
+ <xsl:if test=".//m or .//md">
756
+ <xsl:variable name="definitions">
757
+ <xsl:value-of select="$latex-packages-mathjax"/>
758
+ <!-- the AMS "CD" commutative-diagram environment lives in -->
759
+ <!-- a MathJax extension needing no author declaration; the -->
760
+ <!-- mixed-case v2 name persists as an alias in the later -->
761
+ <!-- component-based loaders -->
762
+ <xsl:if test=".//m[contains(., '\begin{CD}')] or .//md[contains(., '\begin{CD}')]">
763
+ <xsl:text>\require{AMScd}</xsl:text>
764
+ </xsl:if>
765
+ <xsl:value-of select="$latex-macros"/>
766
+ <xsl:call-template name="fillin-math"/>
767
+ <!-- legacy built-in support for "slanted|beveled|nice" fractions -->
768
+ <xsl:if test="$b-has-sfrac">
769
+ <xsl:text>\newcommand{\sfrac}[2]{{#1}/{#2}}&#xa;</xsl:text>
770
+ </xsl:if>
771
+ </xsl:variable>
772
+ <!-- one physical line: every markdown engine recognizes a -->
773
+ <!-- single-line "$$...$$", and spaces are nothing to TeX -->
774
+ <xsl:text>&#xa;$$</xsl:text>
775
+ <xsl:value-of select="normalize-space(translate($definitions, '&#xa;', ' '))"/>
776
+ <xsl:text>$$&#xa;</xsl:text>
777
+ </xsl:if>
778
+ </xsl:template>
779
+
780
+ <!-- An override of the template in pretext-common.xsl, in the -->
781
+ <!-- manner of the Jupyter conversion: the publisher's "shade" -->
782
+ <!-- style needs \definecolor and \colorbox from a MathJax -->
783
+ <!-- extension a generic renderer does not load, and one undefined -->
784
+ <!-- macro poisons the whole definitions block. So "shade" -->
785
+ <!-- degrades to "box", which needs nothing beyond \boxed. -->
786
+ <xsl:template name="fillin-math">
787
+ <xsl:choose>
788
+ <xsl:when test="$fillin-math-style = 'underline'">
789
+ <xsl:text>\newcommand{\fillinmath}[1]{\mathchoice</xsl:text>
790
+ <xsl:text>{\underline{\displaystyle \phantom{\ \,#1\ \,}}}</xsl:text>
791
+ <xsl:text>{\underline{\textstyle \phantom{\ \,#1\ \,}}}</xsl:text>
792
+ <xsl:text>{\underline{\scriptstyle \phantom{\ \,#1\ \,}}}</xsl:text>
793
+ <xsl:text>{\underline{\scriptscriptstyle\phantom{\ \,#1\ \,}}}}&#xa;</xsl:text>
794
+ </xsl:when>
795
+ <!-- "box", and "shade" degraded to "box" -->
796
+ <xsl:otherwise>
797
+ <xsl:text>\newcommand{\fillinmath}[1]{\mathchoice</xsl:text>
798
+ <xsl:text>{\boxed{\displaystyle \phantom{\,#1\,}}}</xsl:text>
799
+ <xsl:text>{\boxed{\textstyle \phantom{\,#1\,}}}</xsl:text>
800
+ <xsl:text>{\boxed{\scriptstyle \phantom{\,#1\,}}}</xsl:text>
801
+ <xsl:text>{\boxed{\scriptscriptstyle\phantom{\,#1\,}}}}&#xa;</xsl:text>
802
+ </xsl:otherwise>
803
+ </xsl:choose>
804
+ </xsl:template>
805
+
806
+ <!-- ##### -->
807
+ <!-- Index -->
808
+ <!-- ##### -->
809
+
810
+ <!-- Each entry is a list item, nested two spaces per subentry -->
811
+ <!-- level, so entries do not run together as a single paragraph -->
812
+ <xsl:template name="present-index-heading">
813
+ <xsl:param name="the-index-list"/>
814
+ <xsl:param name="heading-group"/>
815
+ <xsl:param name="b-write-locators"/>
816
+ <xsl:param name="heading-level"/>
817
+ <xsl:param name="content"/>
818
+ <xsl:value-of select="substring(' ', 1, 2 * ($heading-level - 1))"/>
819
+ <xsl:text>- </xsl:text>
820
+ <xsl:copy-of select="$content"/>
821
+ <xsl:if test="$b-write-locators">
822
+ <xsl:call-template name="locator-list">
823
+ <xsl:with-param name="the-index-list" select="$the-index-list"/>
824
+ <xsl:with-param name="heading-group" select="$heading-group"/>
825
+ <xsl:with-param name="cross-reference-separator" select="', '"/>
826
+ </xsl:call-template>
827
+ </xsl:if>
828
+ <xsl:text>&#xa;</xsl:text>
829
+ </xsl:template>
830
+
831
+ <!-- the "see"/"see also" words italicize -->
832
+ <xsl:template name="present-index-italics">
833
+ <xsl:param name="content"/>
834
+ <xsl:text>*</xsl:text>
835
+ <xsl:copy-of select="$content"/>
836
+ <xsl:text>*</xsl:text>
837
+ </xsl:template>
838
+
839
+ <!-- each locator links to its enclosure's address -->
840
+ <xsl:template match="index-list" mode="index-locator">
841
+ <xsl:param name="enclosure"/>
842
+ <xsl:param name="the-number"/>
843
+ <xsl:text>[</xsl:text>
844
+ <xsl:apply-templates select="$enclosure" mode="type-name"/>
845
+ <xsl:text> </xsl:text>
846
+ <xsl:value-of select="$the-number"/>
847
+ <xsl:text>](</xsl:text>
848
+ <xsl:call-template name="markdown-url">
849
+ <xsl:with-param name="origin" select="."/>
850
+ <xsl:with-param name="target" select="$enclosure"/>
851
+ </xsl:call-template>
852
+ <xsl:text>)</xsl:text>
853
+ </xsl:template>
854
+
855
+ <!-- ######## -->
856
+ <!-- Chunking -->
857
+ <!-- ######## -->
858
+
859
+ <!-- A summary page's line for a subsidiary file is a genuine link -->
860
+ <xsl:template match="*" mode="summary-entry">
861
+ <xsl:text>- [</xsl:text>
862
+ <xsl:apply-templates select="." mode="heading-text"/>
863
+ <xsl:text>](</xsl:text>
864
+ <xsl:apply-templates select="." mode="containing-filename"/>
865
+ <xsl:text>)&#xa;</xsl:text>
866
+ </xsl:template>
867
+
868
+ <!-- The navigation line links its labels; a lone paragraph, -->
869
+ <!-- pipe-separated like the plain-text form -->
870
+ <xsl:template match="*" mode="navigation-line">
871
+ <xsl:variable name="the-filename">
872
+ <xsl:apply-templates select="." mode="containing-filename"/>
873
+ </xsl:variable>
874
+ <xsl:variable name="entry" select="$chunk-list[@filename = $the-filename]"/>
875
+ <xsl:variable name="previous" select="$entry/preceding-sibling::chunk[1]"/>
876
+ <xsl:variable name="next" select="$entry/following-sibling::chunk[1]"/>
877
+ <xsl:variable name="up">
878
+ <xsl:apply-templates select="." mode="up-filename"/>
879
+ </xsl:variable>
880
+ <xsl:if test="$previous or $next">
881
+ <xsl:text>&#xa;[</xsl:text>
882
+ <xsl:apply-templates select="." mode="type-name">
883
+ <xsl:with-param name="string-id" select="'toc'"/>
884
+ </xsl:apply-templates>
885
+ <xsl:text>](</xsl:text>
886
+ <xsl:value-of select="$contents-filename"/>
887
+ <xsl:text>)</xsl:text>
888
+ <xsl:if test="$previous">
889
+ <xsl:text> | [</xsl:text>
890
+ <xsl:apply-templates select="." mode="type-name">
891
+ <xsl:with-param name="string-id" select="'previous'"/>
892
+ </xsl:apply-templates>
893
+ <xsl:text>](</xsl:text>
894
+ <xsl:value-of select="$previous/@filename"/>
895
+ <xsl:text>)</xsl:text>
896
+ </xsl:if>
897
+ <xsl:if test="not($up = '')">
898
+ <xsl:text> | [</xsl:text>
899
+ <xsl:apply-templates select="." mode="type-name">
900
+ <xsl:with-param name="string-id" select="'up'"/>
901
+ </xsl:apply-templates>
902
+ <xsl:text>](</xsl:text>
903
+ <xsl:value-of select="$up"/>
904
+ <xsl:text>)</xsl:text>
905
+ </xsl:if>
906
+ <xsl:if test="$next">
907
+ <xsl:text> | [</xsl:text>
908
+ <xsl:apply-templates select="." mode="type-name">
909
+ <xsl:with-param name="string-id" select="'next'"/>
910
+ </xsl:apply-templates>
911
+ <xsl:text>](</xsl:text>
912
+ <xsl:value-of select="$next/@filename"/>
913
+ <xsl:text>)</xsl:text>
914
+ </xsl:if>
915
+ <xsl:text>&#xa;</xsl:text>
916
+ </xsl:if>
917
+ </xsl:template>
918
+
919
+ </xsl:stylesheet>