@asciidoctor/core 3.0.4 → 4.0.0-alpha.1
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.
- package/README.md +42 -10
- package/build/browser/index.js +24154 -0
- package/build/node/index.cjs +24735 -0
- package/{dist/css/asciidoctor.css → data/asciidoctor-default.css} +54 -53
- package/package.json +53 -100
- package/src/abstract_block.js +849 -0
- package/src/abstract_node.js +954 -0
- package/src/attribute_entry.js +12 -0
- package/src/attribute_list.js +380 -0
- package/src/block.js +168 -0
- package/src/browser/asset.js +22 -0
- package/src/browser/reader.js +138 -0
- package/src/browser.js +121 -0
- package/src/callouts.js +85 -0
- package/src/compliance.js +54 -0
- package/src/constants.js +665 -0
- package/src/convert.js +370 -0
- package/src/converter/composite.js +83 -0
- package/src/converter/docbook5.js +1031 -0
- package/src/converter/html5.js +1899 -0
- package/src/converter/manpage.js +935 -0
- package/src/converter/template.js +459 -0
- package/src/converter.js +478 -0
- package/src/data/stylesheet-data.js +2 -0
- package/src/document.js +2134 -0
- package/src/extensions.js +1952 -0
- package/src/footnote.js +28 -0
- package/src/helpers.js +355 -0
- package/src/index.js +138 -0
- package/src/inline.js +158 -0
- package/src/list.js +240 -0
- package/src/load.js +276 -0
- package/src/logging.js +526 -0
- package/src/parser.js +3661 -0
- package/src/path_resolver.js +472 -0
- package/src/reader.js +1755 -0
- package/src/rx.js +829 -0
- package/src/section.js +354 -0
- package/src/stylesheets.js +30 -0
- package/src/substitutors.js +2241 -0
- package/src/syntaxHighlighter/highlightjs.js +90 -0
- package/src/syntaxHighlighter/html_pipeline.js +33 -0
- package/src/syntax_highlighter.js +304 -0
- package/src/table.js +952 -0
- package/src/timings.js +78 -0
- package/types/abstract_block.d.ts +343 -0
- package/types/abstract_node.d.ts +471 -0
- package/types/attribute_entry.d.ts +7 -0
- package/types/attribute_list.d.ts +52 -0
- package/types/block.d.ts +55 -0
- package/types/browser/asset.d.ts +7 -0
- package/types/browser/reader.d.ts +29 -0
- package/types/callouts.d.ts +36 -0
- package/types/compliance.d.ts +23 -0
- package/types/constants.d.ts +268 -0
- package/types/convert.d.ts +34 -0
- package/types/converter/composite.d.ts +20 -0
- package/types/converter/docbook5.d.ts +41 -0
- package/types/converter/html5.d.ts +51 -0
- package/types/converter/manpage.d.ts +59 -0
- package/types/converter/template.d.ts +83 -0
- package/types/converter.d.ts +150 -0
- package/types/data/stylesheet-data.d.ts +2 -0
- package/types/document.d.ts +495 -0
- package/types/extensions.d.ts +876 -0
- package/types/footnote.d.ts +18 -0
- package/types/helpers.d.ts +146 -0
- package/types/index.d.ts +73 -3731
- package/types/inline.d.ts +69 -0
- package/types/list.d.ts +114 -0
- package/types/load.d.ts +39 -0
- package/types/logging.d.ts +187 -0
- package/types/parser.d.ts +114 -0
- package/types/path_resolver.d.ts +103 -0
- package/types/reader.d.ts +184 -0
- package/types/rx.d.ts +513 -0
- package/types/section.d.ts +122 -0
- package/types/stylesheets.d.ts +10 -0
- package/types/substitutors.d.ts +208 -0
- package/types/syntaxHighlighter/highlightjs.d.ts +33 -0
- package/types/syntaxHighlighter/html_pipeline.d.ts +16 -0
- package/types/syntax_highlighter.d.ts +167 -0
- package/types/table.d.ts +231 -0
- package/types/timings.d.ts +25 -0
- package/types/tsconfig.json +9 -0
- package/LICENSE +0 -21
- package/dist/browser/asciidoctor.js +0 -47654
- package/dist/browser/asciidoctor.min.js +0 -1452
- package/dist/graalvm/asciidoctor.js +0 -47402
- package/dist/node/asciidoctor.cjs +0 -21567
- package/dist/node/asciidoctor.js +0 -23037
|
@@ -0,0 +1,1031 @@
|
|
|
1
|
+
// ESM conversion of converter/docbook5.rb
|
|
2
|
+
// Translated from the Ruby Asciidoctor::Converter::DocBook5Converter.
|
|
3
|
+
// Translation notes:
|
|
4
|
+
// - Ruby symbols (:compound) → strings ('compound')
|
|
5
|
+
// - Ruby predicate methods (title?, attr?, option?, has_role?, blocks?) → hasTitle(), hasAttr(), hasOption(), hasRole(), hasBlocks()
|
|
6
|
+
// - Ruby `node.image_uri` → `await node.imageUri()`; `node.icon_uri` → `await node.iconUri()`
|
|
7
|
+
// - common_attributes(id, role, reftext) kept as private _commonAttributes(id, role, reftext)
|
|
8
|
+
// - blockquote_tag uses a content callback instead of Ruby block
|
|
9
|
+
// - Ruby LF constant → '\n'
|
|
10
|
+
// - document.nested? → doc.isNested(); doc.noheader → doc.isNoheader(); doc.notitle → doc.isNotitle()
|
|
11
|
+
|
|
12
|
+
import { ConverterBase } from '../converter.js'
|
|
13
|
+
import { XmlSanitizeRx } from '../rx.js'
|
|
14
|
+
|
|
15
|
+
const LF = '\n'
|
|
16
|
+
|
|
17
|
+
// default represents variablelist
|
|
18
|
+
const DLIST_TAGS = {
|
|
19
|
+
qanda: {
|
|
20
|
+
list: 'qandaset',
|
|
21
|
+
entry: 'qandaentry',
|
|
22
|
+
label: 'question',
|
|
23
|
+
term: 'simpara',
|
|
24
|
+
item: 'answer',
|
|
25
|
+
},
|
|
26
|
+
glossary: {
|
|
27
|
+
list: null,
|
|
28
|
+
entry: 'glossentry',
|
|
29
|
+
term: 'glossterm',
|
|
30
|
+
item: 'glossdef',
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
const DLIST_TAGS_DEFAULT = {
|
|
34
|
+
list: 'variablelist',
|
|
35
|
+
entry: 'varlistentry',
|
|
36
|
+
term: 'term',
|
|
37
|
+
item: 'listitem',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const QUOTE_TAGS = {
|
|
41
|
+
monospaced: ['<literal>', '</literal>'],
|
|
42
|
+
emphasis: ['<emphasis>', '</emphasis>', true],
|
|
43
|
+
strong: ['<emphasis role="strong">', '</emphasis>', true],
|
|
44
|
+
double: ['<quote role="double">', '</quote>', true],
|
|
45
|
+
single: ['<quote role="single">', '</quote>', true],
|
|
46
|
+
mark: ['<emphasis role="marked">', '</emphasis>'],
|
|
47
|
+
superscript: ['<superscript>', '</superscript>'],
|
|
48
|
+
subscript: ['<subscript>', '</subscript>'],
|
|
49
|
+
}
|
|
50
|
+
const QUOTE_TAGS_DEFAULT = ['', '', true]
|
|
51
|
+
|
|
52
|
+
const MANPAGE_SECTION_TAGS = {
|
|
53
|
+
section: 'refsection',
|
|
54
|
+
synopsis: 'refsynopsisdiv',
|
|
55
|
+
}
|
|
56
|
+
const TABLE_PI_NAMES = ['dbhtml', 'dbfo', 'dblatex']
|
|
57
|
+
|
|
58
|
+
const CopyrightRx = /^(.+?)(?: ((?:\d{4}-)?\d{4}))?$/
|
|
59
|
+
|
|
60
|
+
export class DocBook5Converter extends ConverterBase {
|
|
61
|
+
constructor(backend, opts = {}) {
|
|
62
|
+
super(backend, opts)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async convert_document(node) {
|
|
66
|
+
const result = ['<?xml version="1.0" encoding="UTF-8"?>']
|
|
67
|
+
if (node.hasAttribute('toc')) {
|
|
68
|
+
result.push(
|
|
69
|
+
node.hasAttribute('toclevels')
|
|
70
|
+
? `<?asciidoc-toc maxdepth="${node.getAttribute('toclevels')}"?>`
|
|
71
|
+
: '<?asciidoc-toc?>'
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
if (node.hasAttribute('sectnums')) {
|
|
75
|
+
result.push(
|
|
76
|
+
node.hasAttribute('sectnumlevels')
|
|
77
|
+
? `<?asciidoc-numbered maxdepth="${node.getAttribute('sectnumlevels')}"?>`
|
|
78
|
+
: '<?asciidoc-numbered?>'
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
const langAttribute = node.hasAttribute('nolang')
|
|
82
|
+
? ''
|
|
83
|
+
: ` xml:lang="${node.getAttribute('lang', 'en')}"`
|
|
84
|
+
let rootTagName = node.doctype
|
|
85
|
+
let manpage = false
|
|
86
|
+
if (rootTagName === 'manpage') {
|
|
87
|
+
manpage = true
|
|
88
|
+
rootTagName = 'article'
|
|
89
|
+
}
|
|
90
|
+
const rootTagIdx = result.length
|
|
91
|
+
const id = node.id
|
|
92
|
+
const abstract = this._findRootAbstract(node)
|
|
93
|
+
if (!node.isNoheader())
|
|
94
|
+
result.push(await this._documentInfoTag(node, abstract))
|
|
95
|
+
if (manpage) {
|
|
96
|
+
result.push('<refentry>')
|
|
97
|
+
result.push('<refmeta>')
|
|
98
|
+
if (node.hasAttribute('mantitle'))
|
|
99
|
+
result.push(
|
|
100
|
+
`<refentrytitle>${await node.applyReftextSubs(node.getAttribute('mantitle'))}</refentrytitle>`
|
|
101
|
+
)
|
|
102
|
+
if (node.hasAttribute('manvolnum'))
|
|
103
|
+
result.push(`<manvolnum>${node.getAttribute('manvolnum')}</manvolnum>`)
|
|
104
|
+
result.push(
|
|
105
|
+
`<refmiscinfo class="source">${node.getAttribute('mansource', ' ')}</refmiscinfo>`
|
|
106
|
+
)
|
|
107
|
+
result.push(
|
|
108
|
+
`<refmiscinfo class="manual">${node.getAttribute('manmanual', ' ')}</refmiscinfo>`
|
|
109
|
+
)
|
|
110
|
+
result.push('</refmeta>')
|
|
111
|
+
result.push('<refnamediv>')
|
|
112
|
+
if (node.hasAttribute('mannames')) {
|
|
113
|
+
for (const n of node.getAttribute('mannames'))
|
|
114
|
+
result.push(`<refname>${n}</refname>`)
|
|
115
|
+
}
|
|
116
|
+
if (node.hasAttribute('manpurpose'))
|
|
117
|
+
result.push(
|
|
118
|
+
`<refpurpose>${node.getAttribute('manpurpose')}</refpurpose>`
|
|
119
|
+
)
|
|
120
|
+
result.push('</refnamediv>')
|
|
121
|
+
}
|
|
122
|
+
const headerDocinfo = await node.docinfo('header')
|
|
123
|
+
if (headerDocinfo) result.push(headerDocinfo)
|
|
124
|
+
const extractedAbstract = abstract
|
|
125
|
+
? this._extractAbstract(node, abstract)
|
|
126
|
+
: null
|
|
127
|
+
if (node.hasBlocks()) {
|
|
128
|
+
const blockResults = []
|
|
129
|
+
for (const b of node.blocks) blockResults.push(await b.convert())
|
|
130
|
+
result.push(blockResults.filter((s) => s != null).join(LF))
|
|
131
|
+
}
|
|
132
|
+
if (extractedAbstract) this._restoreAbstract(extractedAbstract)
|
|
133
|
+
const footerDocinfo = await node.docinfo('footer')
|
|
134
|
+
if (footerDocinfo) result.push(footerDocinfo)
|
|
135
|
+
if (manpage) result.push('</refentry>')
|
|
136
|
+
// defer adding root tag in case document ID is auto-generated on demand
|
|
137
|
+
const nodeId = id ?? node.id ?? this._rootDocId
|
|
138
|
+
result.splice(
|
|
139
|
+
rootTagIdx,
|
|
140
|
+
0,
|
|
141
|
+
`<${rootTagName} xmlns="http://docbook.org/ns/docbook" xmlns:xl="http://www.w3.org/1999/xlink" version="5.0"${langAttribute}${this._commonAttributes(nodeId)}>`
|
|
142
|
+
)
|
|
143
|
+
result.push(`</${rootTagName}>`)
|
|
144
|
+
return result.join(LF)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async convert_embedded(node) {
|
|
148
|
+
// NOTE in DocBook 5, the root abstract must be in the info tag and is thus not part of the body
|
|
149
|
+
let abstract = null
|
|
150
|
+
if (this.backend === 'docbook5') {
|
|
151
|
+
abstract = this._findRootAbstract(node)
|
|
152
|
+
if (abstract) this._extractAbstract(node, abstract)
|
|
153
|
+
}
|
|
154
|
+
const blockParts = []
|
|
155
|
+
for (const b of node.blocks) blockParts.push(await b.convert())
|
|
156
|
+
const result = blockParts.filter((s) => s != null).join(LF)
|
|
157
|
+
if (abstract) this._restoreAbstract(abstract)
|
|
158
|
+
return result
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async convert_section(node) {
|
|
162
|
+
let tagName = node.sectname
|
|
163
|
+
if (node.document.doctype === 'manpage') {
|
|
164
|
+
tagName = MANPAGE_SECTION_TAGS[tagName] ?? tagName
|
|
165
|
+
}
|
|
166
|
+
const titleEl =
|
|
167
|
+
node.special && (node.hasOption('notitle') || node.hasOption('untitled'))
|
|
168
|
+
? ''
|
|
169
|
+
: `<title>${node.title}</title>\n`
|
|
170
|
+
return `<${tagName}${this._commonAttributes(node.id, node.role, node.reftext)}>\n${titleEl}${await node.content()}\n</${tagName}>`
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async convert_admonition(node) {
|
|
174
|
+
const tagName = node.getAttribute('name')
|
|
175
|
+
return `<${tagName}${this._commonAttributes(node.id, node.role, node.reftext)}>\n${this._titleTag(node)}${await this._encloseContent(node)}\n</${tagName}>`
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async convert_audio(_node) {
|
|
179
|
+
return ''
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async convert_colist(node) {
|
|
183
|
+
const result = []
|
|
184
|
+
result.push(
|
|
185
|
+
`<calloutlist${this._commonAttributes(node.id, node.role, node.reftext)}>`
|
|
186
|
+
)
|
|
187
|
+
if (node.hasTitle()) result.push(`<title>${node.title}</title>`)
|
|
188
|
+
for (const item of node.getItems()) {
|
|
189
|
+
result.push(`<callout arearefs="${item.getAttribute('coids')}">`)
|
|
190
|
+
result.push(`<para>${item.getText()}</para>`)
|
|
191
|
+
if (item.hasBlocks()) result.push(await item.content())
|
|
192
|
+
result.push('</callout>')
|
|
193
|
+
}
|
|
194
|
+
result.push('</calloutlist>')
|
|
195
|
+
return result.join(LF)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async convert_dlist(node) {
|
|
199
|
+
const result = []
|
|
200
|
+
if (node.style === 'horizontal') {
|
|
201
|
+
const tagName = node.hasTitle() ? 'table' : 'informaltable'
|
|
202
|
+
result.push(
|
|
203
|
+
`<${tagName}${this._commonAttributes(node.id, node.role, node.reftext)} tabstyle="horizontal" frame="none" colsep="0" rowsep="0">`
|
|
204
|
+
)
|
|
205
|
+
result.push(`${this._titleTag(node)}<tgroup cols="2">`)
|
|
206
|
+
result.push(
|
|
207
|
+
`<colspec colwidth="${node.getAttribute('labelwidth', 15)}*"/>`
|
|
208
|
+
)
|
|
209
|
+
result.push(
|
|
210
|
+
`<colspec colwidth="${node.getAttribute('itemwidth', 85)}*"/>`
|
|
211
|
+
)
|
|
212
|
+
result.push('<tbody valign="top">')
|
|
213
|
+
for (const [terms, dd] of node.getItems()) {
|
|
214
|
+
result.push('<row>\n<entry>')
|
|
215
|
+
for (const dt of terms)
|
|
216
|
+
result.push(`<simpara>${dt.getText()}</simpara>`)
|
|
217
|
+
result.push('</entry>\n<entry>')
|
|
218
|
+
if (dd) {
|
|
219
|
+
if (dd.hasText()) result.push(`<simpara>${dd.getText()}</simpara>`)
|
|
220
|
+
if (dd.hasBlocks()) result.push(await dd.content())
|
|
221
|
+
}
|
|
222
|
+
result.push('</entry>\n</row>')
|
|
223
|
+
}
|
|
224
|
+
result.push(`</tbody>\n</tgroup>\n</${tagName}>`)
|
|
225
|
+
} else {
|
|
226
|
+
const tags = DLIST_TAGS[node.style] ?? DLIST_TAGS_DEFAULT
|
|
227
|
+
const {
|
|
228
|
+
list: listTag,
|
|
229
|
+
entry: entryTag,
|
|
230
|
+
label: labelTag,
|
|
231
|
+
term: termTag,
|
|
232
|
+
item: itemTag,
|
|
233
|
+
} = tags
|
|
234
|
+
if (listTag) {
|
|
235
|
+
result.push(
|
|
236
|
+
`<${listTag}${this._commonAttributes(node.id, node.role, node.reftext)}>`
|
|
237
|
+
)
|
|
238
|
+
if (node.hasTitle()) result.push(`<title>${node.title}</title>`)
|
|
239
|
+
}
|
|
240
|
+
for (const [terms, dd] of node.getItems()) {
|
|
241
|
+
result.push(`<${entryTag}>`)
|
|
242
|
+
if (labelTag) result.push(`<${labelTag}>`)
|
|
243
|
+
for (const dt of terms)
|
|
244
|
+
result.push(`<${termTag}>${dt.getText()}</${termTag}>`)
|
|
245
|
+
if (labelTag) result.push(`</${labelTag}>`)
|
|
246
|
+
result.push(`<${itemTag}>`)
|
|
247
|
+
if (dd) {
|
|
248
|
+
if (dd.hasText()) result.push(`<simpara>${dd.getText()}</simpara>`)
|
|
249
|
+
if (dd.hasBlocks()) result.push(await dd.content())
|
|
250
|
+
}
|
|
251
|
+
result.push(`</${itemTag}>`)
|
|
252
|
+
result.push(`</${entryTag}>`)
|
|
253
|
+
}
|
|
254
|
+
if (listTag) result.push(`</${listTag}>`)
|
|
255
|
+
}
|
|
256
|
+
return result.join(LF)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async convert_example(node) {
|
|
260
|
+
const commonAttrs = this._commonAttributes(node.id, node.role, node.reftext)
|
|
261
|
+
if (node.hasTitle()) {
|
|
262
|
+
return `<example${commonAttrs}>\n<title>${node.title}</title>\n${await this._encloseContent(node)}\n</example>`
|
|
263
|
+
}
|
|
264
|
+
return `<informalexample${commonAttrs}>\n${await this._encloseContent(node)}\n</informalexample>`
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async convert_floating_title(node) {
|
|
268
|
+
return `<bridgehead${this._commonAttributes(node.id, node.role, node.reftext)} renderas="sect${node.level}">${node.title}</bridgehead>`
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async convert_image(node) {
|
|
272
|
+
const alignAttribute = node.hasAttribute('align')
|
|
273
|
+
? ` align="${node.getAttribute('align')}"`
|
|
274
|
+
: ''
|
|
275
|
+
const mediaobject = `<mediaobject>\n<imageobject>\n<imagedata fileref="${await node.imageUri(node.getAttribute('target'))}"${this._imageSizeAttributes(node.attributes)}${alignAttribute}/>\n</imageobject>\n<textobject><phrase>${node.getAlt()}</phrase></textobject>\n</mediaobject>`
|
|
276
|
+
const commonAttrs = this._commonAttributes(node.id, node.role, node.reftext)
|
|
277
|
+
if (node.hasTitle()) {
|
|
278
|
+
return `<figure${commonAttrs}>\n<title>${node.title}</title>\n${mediaobject}\n</figure>`
|
|
279
|
+
}
|
|
280
|
+
return `<informalfigure${commonAttrs}>\n${mediaobject}\n</informalfigure>`
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async convert_listing(node) {
|
|
284
|
+
const informal = !node.hasTitle()
|
|
285
|
+
const commonAttrs = this._commonAttributes(node.id, node.role, node.reftext)
|
|
286
|
+
let wrappedContent
|
|
287
|
+
if (node.style === 'source') {
|
|
288
|
+
const attrs = node.attributes
|
|
289
|
+
let numberingAttrs
|
|
290
|
+
if (node.hasOption('linenums')) {
|
|
291
|
+
numberingAttrs =
|
|
292
|
+
'start' in attrs
|
|
293
|
+
? ` linenumbering="numbered" startinglinenumber="${parseInt(attrs.start, 10)}"`
|
|
294
|
+
: ' linenumbering="numbered"'
|
|
295
|
+
} else {
|
|
296
|
+
numberingAttrs = ' linenumbering="unnumbered"'
|
|
297
|
+
}
|
|
298
|
+
if ('language' in attrs) {
|
|
299
|
+
wrappedContent = `<programlisting${informal ? commonAttrs : ''} language="${attrs.language}"${numberingAttrs}>${await node.content()}</programlisting>`
|
|
300
|
+
} else {
|
|
301
|
+
wrappedContent = `<screen${informal ? commonAttrs : ''}${numberingAttrs}>${await node.content()}</screen>`
|
|
302
|
+
}
|
|
303
|
+
} else {
|
|
304
|
+
wrappedContent = `<screen${informal ? commonAttrs : ''}>${await node.content()}</screen>`
|
|
305
|
+
}
|
|
306
|
+
if (informal) return wrappedContent
|
|
307
|
+
return `<formalpara${commonAttrs}>\n<title>${node.title}</title>\n<para>\n${wrappedContent}\n</para>\n</formalpara>`
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async convert_literal(node) {
|
|
311
|
+
const commonAttrs = this._commonAttributes(node.id, node.role, node.reftext)
|
|
312
|
+
if (node.hasTitle()) {
|
|
313
|
+
return `<formalpara${commonAttrs}>\n<title>${node.title}</title>\n<para>\n<literallayout class="monospaced">${await node.content()}</literallayout>\n</para>\n</formalpara>`
|
|
314
|
+
}
|
|
315
|
+
return `<literallayout${commonAttrs} class="monospaced">${await node.content()}</literallayout>`
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async convert_pass(node) {
|
|
319
|
+
return await node.content()
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async convert_stem(node) {
|
|
323
|
+
let equation
|
|
324
|
+
const idx = node.subs ? node.subs.indexOf('specialcharacters') : -1
|
|
325
|
+
if (idx !== -1) {
|
|
326
|
+
node.subs.splice(idx, 1)
|
|
327
|
+
equation = await node.content()
|
|
328
|
+
node.subs.splice(idx, 0, 'specialcharacters')
|
|
329
|
+
} else {
|
|
330
|
+
equation = await node.content()
|
|
331
|
+
}
|
|
332
|
+
let equationData
|
|
333
|
+
if (node.style === 'asciimath') {
|
|
334
|
+
// NOTE: No AsciiMath-to-MathML conversion available in JS; use CDATA fallback
|
|
335
|
+
equationData = `<mathphrase><![CDATA[${equation}]]></mathphrase>`
|
|
336
|
+
} else {
|
|
337
|
+
// unhandled math (latexmath); pass source to alt and required mathphrase — dblatex will process alt as LaTeX math
|
|
338
|
+
equationData = `<alt><![CDATA[${equation}]]></alt>\n<mathphrase><![CDATA[${equation}]]></mathphrase>`
|
|
339
|
+
}
|
|
340
|
+
const commonAttrs = this._commonAttributes(node.id, node.role, node.reftext)
|
|
341
|
+
if (node.hasTitle()) {
|
|
342
|
+
return `<equation${commonAttrs}>\n<title>${node.title}</title>\n${equationData}\n</equation>`
|
|
343
|
+
}
|
|
344
|
+
return `<informalequation${commonAttrs}>\n${equationData}\n</informalequation>`
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async convert_olist(node) {
|
|
348
|
+
const result = []
|
|
349
|
+
const numAttribute = node.style ? ` numeration="${node.style}"` : ''
|
|
350
|
+
const startAttribute = node.hasAttribute('start')
|
|
351
|
+
? ` startingnumber="${node.getAttribute('start')}"`
|
|
352
|
+
: ''
|
|
353
|
+
result.push(
|
|
354
|
+
`<orderedlist${this._commonAttributes(node.id, node.role, node.reftext)}${numAttribute}${startAttribute}>`
|
|
355
|
+
)
|
|
356
|
+
if (node.hasTitle()) result.push(`<title>${node.title}</title>`)
|
|
357
|
+
for (const item of node.getItems()) {
|
|
358
|
+
result.push(`<listitem${this._commonAttributes(item.id, item.role)}>`)
|
|
359
|
+
result.push(`<simpara>${item.getText()}</simpara>`)
|
|
360
|
+
if (item.hasBlocks()) result.push(await item.content())
|
|
361
|
+
result.push('</listitem>')
|
|
362
|
+
}
|
|
363
|
+
result.push('</orderedlist>')
|
|
364
|
+
return result.join(LF)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async convert_open(node) {
|
|
368
|
+
const id = node.id
|
|
369
|
+
const role = node.role
|
|
370
|
+
const reftext = node.reftext
|
|
371
|
+
switch (node.style) {
|
|
372
|
+
case 'abstract': {
|
|
373
|
+
if (
|
|
374
|
+
node.getParent() === node.document &&
|
|
375
|
+
node.document.doctype === 'book'
|
|
376
|
+
) {
|
|
377
|
+
this.logger.warn(
|
|
378
|
+
'abstract block cannot be used in a document without a doctitle when doctype is book. Excluding block content.'
|
|
379
|
+
)
|
|
380
|
+
return ''
|
|
381
|
+
}
|
|
382
|
+
let res = `<abstract>\n${this._titleTag(node)}${await this._encloseContent(node)}\n</abstract>`
|
|
383
|
+
const parent = node.getParent()
|
|
384
|
+
if (
|
|
385
|
+
this.backend === 'docbook5' &&
|
|
386
|
+
!node.hasOption('root') &&
|
|
387
|
+
(parent.context === 'open'
|
|
388
|
+
? parent.style === 'partintro'
|
|
389
|
+
: parent.context === 'section' &&
|
|
390
|
+
parent.sectname === 'partintro') &&
|
|
391
|
+
parent.blocks[0] === node
|
|
392
|
+
) {
|
|
393
|
+
res = `<info>\n${res}\n</info>`
|
|
394
|
+
}
|
|
395
|
+
return res
|
|
396
|
+
}
|
|
397
|
+
case 'partintro': {
|
|
398
|
+
if (
|
|
399
|
+
node.level === 0 &&
|
|
400
|
+
node.getParent().context === 'section' &&
|
|
401
|
+
node.document.doctype === 'book'
|
|
402
|
+
) {
|
|
403
|
+
return `<partintro${this._commonAttributes(id, role, reftext)}>\n${this._titleTag(node)}${await this._encloseContent(node)}\n</partintro>`
|
|
404
|
+
}
|
|
405
|
+
this.logger.error(
|
|
406
|
+
'partintro block can only be used when doctype is book and must be a child of a book part. Excluding block content.'
|
|
407
|
+
)
|
|
408
|
+
return ''
|
|
409
|
+
}
|
|
410
|
+
default: {
|
|
411
|
+
if (node.hasTitle()) {
|
|
412
|
+
const contentSpacer = node.contentModel === 'compound' ? LF : ''
|
|
413
|
+
return `<formalpara${this._commonAttributes(id, role, reftext)}>\n<title>${node.title}</title>\n<para>${contentSpacer}${await node.content()}${contentSpacer}</para>\n</formalpara>`
|
|
414
|
+
} else if (id || role) {
|
|
415
|
+
if (node.contentModel === 'compound') {
|
|
416
|
+
return `<para${this._commonAttributes(id, role, reftext)}>\n${await node.content()}\n</para>`
|
|
417
|
+
}
|
|
418
|
+
return `<simpara${this._commonAttributes(id, role, reftext)}>${await node.content()}</simpara>`
|
|
419
|
+
}
|
|
420
|
+
return await this._encloseContent(node)
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async convert_page_break(_node) {
|
|
426
|
+
return '<simpara><?asciidoc-pagebreak?></simpara>'
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
async convert_paragraph(node) {
|
|
430
|
+
const commonAttrs = this._commonAttributes(node.id, node.role, node.reftext)
|
|
431
|
+
if (node.hasTitle()) {
|
|
432
|
+
return `<formalpara${commonAttrs}>\n<title>${node.title}</title>\n<para>${await node.content()}</para>\n</formalpara>`
|
|
433
|
+
}
|
|
434
|
+
return `<simpara${commonAttrs}>${await node.content()}</simpara>`
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
async convert_preamble(node) {
|
|
438
|
+
if (node.document.doctype === 'book') {
|
|
439
|
+
return `<preface${this._commonAttributes(node.id, node.role, node.reftext)}>\n${this._titleTag(node, false)}${await node.content()}\n</preface>`
|
|
440
|
+
}
|
|
441
|
+
return await node.content()
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
async convert_quote(node) {
|
|
445
|
+
return await this._blockquoteTag(
|
|
446
|
+
node,
|
|
447
|
+
node.hasRole('epigraph') ? 'epigraph' : null,
|
|
448
|
+
async () => await this._encloseContent(node)
|
|
449
|
+
)
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
async convert_thematic_break(_node) {
|
|
453
|
+
return '<simpara><?asciidoc-hr?></simpara>'
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async convert_sidebar(node) {
|
|
457
|
+
return `<sidebar${this._commonAttributes(node.id, node.role, node.reftext)}>\n${this._titleTag(node)}${await this._encloseContent(node)}\n</sidebar>`
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async convert_table(node) {
|
|
461
|
+
let hasBody = false
|
|
462
|
+
const result = []
|
|
463
|
+
const pgwideAttribute = node.hasOption('pgwide') ? ' pgwide="1"' : ''
|
|
464
|
+
let frame = node.getAttribute('frame', 'all', 'table-frame')
|
|
465
|
+
if (frame === 'ends') frame = 'topbot'
|
|
466
|
+
const grid = node.getAttribute('grid', null, 'table-grid')
|
|
467
|
+
const tagName = node.hasTitle() ? 'table' : 'informaltable'
|
|
468
|
+
const orientAttr = node.hasAttribute(
|
|
469
|
+
'orientation',
|
|
470
|
+
'landscape',
|
|
471
|
+
'table-orientation'
|
|
472
|
+
)
|
|
473
|
+
? ' orient="land"'
|
|
474
|
+
: ''
|
|
475
|
+
result.push(
|
|
476
|
+
`<${tagName}${this._commonAttributes(node.id, node.role, node.reftext)}${pgwideAttribute} frame="${frame}" rowsep="${['none', 'cols'].includes(grid) ? 0 : 1}" colsep="${['none', 'rows'].includes(grid) ? 0 : 1}"${orientAttr}>`
|
|
477
|
+
)
|
|
478
|
+
if (node.hasOption('unbreakable')) {
|
|
479
|
+
result.push('<?dbfo keep-together="always"?>')
|
|
480
|
+
} else if (node.hasOption('breakable')) {
|
|
481
|
+
result.push('<?dbfo keep-together="auto"?>')
|
|
482
|
+
}
|
|
483
|
+
if (tagName === 'table') result.push(`<title>${node.title}</title>`)
|
|
484
|
+
let colWidthKey
|
|
485
|
+
const width = node.hasAttribute('width') ? node.getAttribute('width') : null
|
|
486
|
+
if (width) {
|
|
487
|
+
for (const piName of TABLE_PI_NAMES)
|
|
488
|
+
result.push(`<?${piName} table-width="${width}"?>`)
|
|
489
|
+
colWidthKey = 'colabswidth'
|
|
490
|
+
} else {
|
|
491
|
+
colWidthKey = 'colpcwidth'
|
|
492
|
+
}
|
|
493
|
+
result.push(`<tgroup cols="${node.getAttribute('colcount')}">`)
|
|
494
|
+
for (const col of node.columns) {
|
|
495
|
+
result.push(
|
|
496
|
+
`<colspec colname="col_${col.getAttribute('colnumber')}" colwidth="${col.getAttribute(colWidthKey)}*"/>`
|
|
497
|
+
)
|
|
498
|
+
}
|
|
499
|
+
for (const [tsec, sectionRows] of node.rows.bySection()) {
|
|
500
|
+
if (!sectionRows || sectionRows.length === 0) continue
|
|
501
|
+
if (tsec === 'body') hasBody = true
|
|
502
|
+
result.push(`<t${tsec}>`)
|
|
503
|
+
for (const row of sectionRows) {
|
|
504
|
+
result.push('<row>')
|
|
505
|
+
for (const cell of row) {
|
|
506
|
+
const colspanAttribute = cell.colspan
|
|
507
|
+
? ` namest="col_${cell.column.getAttribute('colnumber')}" nameend="col_${cell.column.getAttribute('colnumber') + cell.colspan - 1}"`
|
|
508
|
+
: ''
|
|
509
|
+
const rowspanAttribute = cell.rowspan
|
|
510
|
+
? ` morerows="${cell.rowspan - 1}"`
|
|
511
|
+
: ''
|
|
512
|
+
const entryStart = `<entry align="${cell.getAttribute('halign')}" valign="${cell.getAttribute('valign')}"${colspanAttribute}${rowspanAttribute}>`
|
|
513
|
+
let cellContent
|
|
514
|
+
if (tsec === 'head') {
|
|
515
|
+
cellContent = cell.text
|
|
516
|
+
} else {
|
|
517
|
+
switch (cell.style) {
|
|
518
|
+
case 'asciidoc':
|
|
519
|
+
cellContent = await cell.content()
|
|
520
|
+
break
|
|
521
|
+
case 'literal':
|
|
522
|
+
cellContent = `<literallayout class="monospaced">${cell.text}</literallayout>`
|
|
523
|
+
break
|
|
524
|
+
case 'header': {
|
|
525
|
+
const parts = await cell.content()
|
|
526
|
+
cellContent =
|
|
527
|
+
parts.length === 0
|
|
528
|
+
? ''
|
|
529
|
+
: `<simpara><emphasis role="strong">${parts.join('</emphasis></simpara><simpara><emphasis role="strong">')}</emphasis></simpara>`
|
|
530
|
+
break
|
|
531
|
+
}
|
|
532
|
+
default: {
|
|
533
|
+
const parts = await cell.content()
|
|
534
|
+
cellContent =
|
|
535
|
+
parts.length === 0
|
|
536
|
+
? ''
|
|
537
|
+
: `<simpara>${parts.join('</simpara><simpara>')}</simpara>`
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
const entryEnd = node.document.hasAttribute('cellbgcolor')
|
|
542
|
+
? `<?dbfo bgcolor="${node.document.getAttribute('cellbgcolor')}"?></entry>`
|
|
543
|
+
: '</entry>'
|
|
544
|
+
result.push(`${entryStart}${cellContent}${entryEnd}`)
|
|
545
|
+
}
|
|
546
|
+
result.push('</row>')
|
|
547
|
+
}
|
|
548
|
+
result.push(`</t${tsec}>`)
|
|
549
|
+
}
|
|
550
|
+
result.push('</tgroup>')
|
|
551
|
+
result.push(`</${tagName}>`)
|
|
552
|
+
if (!hasBody) this.logger.warn('tables must have at least one body row')
|
|
553
|
+
return result.join(LF)
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
async convert_toc(_node) {
|
|
557
|
+
return ''
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
async convert_ulist(node) {
|
|
561
|
+
const result = []
|
|
562
|
+
if (node.style === 'bibliography') {
|
|
563
|
+
result.push(
|
|
564
|
+
`<bibliodiv${this._commonAttributes(node.id, node.role, node.reftext)}>`
|
|
565
|
+
)
|
|
566
|
+
if (node.hasTitle()) result.push(`<title>${node.title}</title>`)
|
|
567
|
+
for (const item of node.getItems()) {
|
|
568
|
+
result.push('<bibliomixed>')
|
|
569
|
+
result.push(`<bibliomisc>${item.getText()}</bibliomisc>`)
|
|
570
|
+
if (item.hasBlocks()) result.push(await item.content())
|
|
571
|
+
result.push('</bibliomixed>')
|
|
572
|
+
}
|
|
573
|
+
result.push('</bibliodiv>')
|
|
574
|
+
} else {
|
|
575
|
+
const checklist = node.hasOption('checklist')
|
|
576
|
+
const markType = checklist ? 'none' : node.style
|
|
577
|
+
const markAttribute = markType ? ` mark="${markType}"` : ''
|
|
578
|
+
result.push(
|
|
579
|
+
`<itemizedlist${this._commonAttributes(node.id, node.role, node.reftext)}${markAttribute}>`
|
|
580
|
+
)
|
|
581
|
+
if (node.hasTitle()) result.push(`<title>${node.title}</title>`)
|
|
582
|
+
for (const item of node.getItems()) {
|
|
583
|
+
const textMarker =
|
|
584
|
+
checklist && item.hasAttribute('checkbox')
|
|
585
|
+
? item.hasAttribute('checked')
|
|
586
|
+
? '✓ '
|
|
587
|
+
: '❏ '
|
|
588
|
+
: ''
|
|
589
|
+
result.push(`<listitem${this._commonAttributes(item.id, item.role)}>`)
|
|
590
|
+
result.push(`<simpara>${textMarker}${item.getText()}</simpara>`)
|
|
591
|
+
if (item.hasBlocks()) result.push(await item.content())
|
|
592
|
+
result.push('</listitem>')
|
|
593
|
+
}
|
|
594
|
+
result.push('</itemizedlist>')
|
|
595
|
+
}
|
|
596
|
+
return result.join(LF)
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
async convert_verse(node) {
|
|
600
|
+
return await this._blockquoteTag(
|
|
601
|
+
node,
|
|
602
|
+
node.hasRole('epigraph') ? 'epigraph' : null,
|
|
603
|
+
async () => `<literallayout>${await node.content()}</literallayout>`
|
|
604
|
+
)
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
async convert_video(_node) {
|
|
608
|
+
return ''
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
async convert_inline_anchor(node) {
|
|
612
|
+
switch (node.type) {
|
|
613
|
+
case 'ref':
|
|
614
|
+
return `<anchor${this._commonAttributes(node.id, null, node.reftext || `[${node.id}]`)}/>`
|
|
615
|
+
case 'xref': {
|
|
616
|
+
const path = node.attributes.path
|
|
617
|
+
if (path) {
|
|
618
|
+
return `<link xl:href="${node.target}">${node.text || path}</link>`
|
|
619
|
+
}
|
|
620
|
+
let linkend = node.attributes.refid
|
|
621
|
+
if (!linkend) {
|
|
622
|
+
const rootDoc = this._getRootDocument(node)
|
|
623
|
+
linkend =
|
|
624
|
+
rootDoc.id ??
|
|
625
|
+
(this._rootDocId ??= this._generateDocumentId(rootDoc))
|
|
626
|
+
}
|
|
627
|
+
return node.text
|
|
628
|
+
? `<link linkend="${linkend}">${node.text}</link>`
|
|
629
|
+
: `<xref linkend="${linkend}"/>`
|
|
630
|
+
}
|
|
631
|
+
case 'link':
|
|
632
|
+
return `<link xl:href="${node.target}">${node.text}</link>`
|
|
633
|
+
case 'bibref': {
|
|
634
|
+
const text = `[${node.reftext || node.id}]`
|
|
635
|
+
return `<anchor${this._commonAttributes(node.id, null, text)}/>${text}`
|
|
636
|
+
}
|
|
637
|
+
default:
|
|
638
|
+
this.logger.warn(`unknown anchor type: ${node.type}`)
|
|
639
|
+
return null
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
async convert_inline_break(node) {
|
|
644
|
+
return `${node.text}<?asciidoc-br?>`
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
async convert_inline_button(node) {
|
|
648
|
+
return `<guibutton>${node.text}</guibutton>`
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
async convert_inline_callout(node) {
|
|
652
|
+
return `<co${this._commonAttributes(node.id)}/>`
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
async convert_inline_footnote(node) {
|
|
656
|
+
if (node.type === 'xref') {
|
|
657
|
+
return `<footnoteref linkend="${node.target}"/>`
|
|
658
|
+
}
|
|
659
|
+
return `<footnote${this._commonAttributes(node.id)}><simpara>${node.text}</simpara></footnote>`
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
async convert_inline_image(node) {
|
|
663
|
+
const fileref =
|
|
664
|
+
node.type === 'icon'
|
|
665
|
+
? await node.iconUri(node.target)
|
|
666
|
+
: await node.imageUri(node.target)
|
|
667
|
+
const img = `<inlinemediaobject${this._commonAttributes(node.id, node.role)}>\n<imageobject>\n<imagedata fileref="${fileref}"${this._imageSizeAttributes(node.attributes)}/>\n</imageobject>\n<textobject><phrase>${node.getAlt()}</phrase></textobject>\n</inlinemediaobject>`
|
|
668
|
+
if (node.type !== 'icon' && node.hasAttribute('link')) {
|
|
669
|
+
const linkHref = node.getAttribute('link')
|
|
670
|
+
return `<link xl:href="${linkHref === 'self' ? fileref : linkHref}">${img}</link>`
|
|
671
|
+
}
|
|
672
|
+
return img
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
async convert_inline_indexterm(node) {
|
|
676
|
+
let rel = ''
|
|
677
|
+
const see = node.getAttribute('see')
|
|
678
|
+
if (see) {
|
|
679
|
+
rel = `\n<see>${see}</see>`
|
|
680
|
+
} else {
|
|
681
|
+
const seeAlsoList = node.getAttribute('see-also')
|
|
682
|
+
if (seeAlsoList) {
|
|
683
|
+
rel = seeAlsoList.map((s) => `\n<seealso>${s}</seealso>`).join('')
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
if (node.type === 'visible') {
|
|
687
|
+
return `<indexterm>\n<primary>${node.text}</primary>${rel}\n</indexterm>${node.text}`
|
|
688
|
+
}
|
|
689
|
+
const terms = node.getAttribute('terms')
|
|
690
|
+
const numterms = terms.length
|
|
691
|
+
const indexPromote = node.document.hasOption('indexterm-promotion')
|
|
692
|
+
if (numterms > 2) {
|
|
693
|
+
return `<indexterm>\n<primary>${terms[0]}</primary><secondary>${terms[1]}</secondary><tertiary>${terms[2]}</tertiary>${rel}\n</indexterm>${indexPromote ? `\n<indexterm>\n<primary>${terms[1]}</primary><secondary>${terms[2]}</secondary>\n</indexterm>\n<indexterm>\n<primary>${terms[2]}</primary>\n</indexterm>` : ''}`
|
|
694
|
+
} else if (numterms > 1) {
|
|
695
|
+
return `<indexterm>\n<primary>${terms[0]}</primary><secondary>${terms[1]}</secondary>${rel}\n</indexterm>${indexPromote ? `\n<indexterm>\n<primary>${terms[1]}</primary>\n</indexterm>` : ''}`
|
|
696
|
+
}
|
|
697
|
+
return `<indexterm>\n<primary>${terms[0]}</primary>${rel}\n</indexterm>`
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
async convert_inline_kbd(node) {
|
|
701
|
+
const keys = node.getAttribute('keys')
|
|
702
|
+
if (keys.length === 1) {
|
|
703
|
+
return `<keycap>${keys[0]}</keycap>`
|
|
704
|
+
}
|
|
705
|
+
return `<keycombo><keycap>${keys.join('</keycap><keycap>')}</keycap></keycombo>`
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
async convert_inline_menu(node) {
|
|
709
|
+
const menu = node.getAttribute('menu')
|
|
710
|
+
const submenus = node.getAttribute('submenus')
|
|
711
|
+
if (!submenus || submenus.length === 0) {
|
|
712
|
+
const menuitem = node.getAttribute('menuitem')
|
|
713
|
+
if (menuitem) {
|
|
714
|
+
return `<menuchoice><guimenu>${menu}</guimenu> <guimenuitem>${menuitem}</guimenuitem></menuchoice>`
|
|
715
|
+
}
|
|
716
|
+
return `<guimenu>${menu}</guimenu>`
|
|
717
|
+
}
|
|
718
|
+
return `<menuchoice><guimenu>${menu}</guimenu> <guisubmenu>${submenus.join('</guisubmenu> <guisubmenu>')}</guisubmenu> <guimenuitem>${node.getAttribute('menuitem')}</guimenuitem></menuchoice>`
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
async convert_inline_quoted(node) {
|
|
722
|
+
const type = node.type
|
|
723
|
+
if (type === 'asciimath' || type === 'latexmath') {
|
|
724
|
+
const equation = node.text
|
|
725
|
+
if (type === 'asciimath') {
|
|
726
|
+
return `<inlineequation><mathphrase><![CDATA[${equation}]]></mathphrase></inlineequation>`
|
|
727
|
+
}
|
|
728
|
+
return `<inlineequation><alt><![CDATA[${equation}]]></alt><mathphrase><![CDATA[${equation}]]></mathphrase></inlineequation>`
|
|
729
|
+
}
|
|
730
|
+
const [open, close, supportsPhrase] = QUOTE_TAGS[type] ?? QUOTE_TAGS_DEFAULT
|
|
731
|
+
const text = node.text
|
|
732
|
+
let quotedText
|
|
733
|
+
if (node.role) {
|
|
734
|
+
if (supportsPhrase) {
|
|
735
|
+
quotedText = `${open}<phrase role="${node.role}">${text}</phrase>${close}`
|
|
736
|
+
} else {
|
|
737
|
+
// chop the closing > from open tag to insert role attribute
|
|
738
|
+
quotedText = `${open.slice(0, -1)} role="${node.role}">${text}${close}`
|
|
739
|
+
}
|
|
740
|
+
} else {
|
|
741
|
+
quotedText = `${open}${text}${close}`
|
|
742
|
+
}
|
|
743
|
+
return node.id
|
|
744
|
+
? `<anchor${this._commonAttributes(node.id)}/>${quotedText}`
|
|
745
|
+
: quotedText
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// Private helpers
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* @internal
|
|
752
|
+
* @private
|
|
753
|
+
*/
|
|
754
|
+
_commonAttributes(id, role = null, reftext = null) {
|
|
755
|
+
let attrs = ''
|
|
756
|
+
if (id) {
|
|
757
|
+
attrs = ` xml:id="${id}"${role ? ` role="${role}"` : ''}`
|
|
758
|
+
} else if (role) {
|
|
759
|
+
attrs = ` role="${role}"`
|
|
760
|
+
}
|
|
761
|
+
if (reftext) {
|
|
762
|
+
let sanitized = reftext
|
|
763
|
+
if (sanitized.includes('<')) {
|
|
764
|
+
sanitized = sanitized.replace(XmlSanitizeRx, '')
|
|
765
|
+
if (sanitized.includes(' '))
|
|
766
|
+
sanitized = sanitized.replace(/ {2,}/g, ' ').trim()
|
|
767
|
+
}
|
|
768
|
+
if (sanitized.includes('"')) sanitized = sanitized.replace(/"/g, '"')
|
|
769
|
+
return `${attrs} xreflabel="${sanitized}"`
|
|
770
|
+
}
|
|
771
|
+
return attrs
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* @internal
|
|
776
|
+
* @private
|
|
777
|
+
*/
|
|
778
|
+
_imageSizeAttributes(attributes) {
|
|
779
|
+
if ('scaledwidth' in attributes) {
|
|
780
|
+
return ` width="${attributes.scaledwidth}"`
|
|
781
|
+
} else if ('scale' in attributes) {
|
|
782
|
+
return ` scale="${attributes.scale}"`
|
|
783
|
+
}
|
|
784
|
+
const widthAttr =
|
|
785
|
+
'width' in attributes ? ` contentwidth="${attributes.width}"` : ''
|
|
786
|
+
const depthAttr =
|
|
787
|
+
'height' in attributes ? ` contentdepth="${attributes.height}"` : ''
|
|
788
|
+
return `${widthAttr}${depthAttr}`
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* @internal
|
|
793
|
+
* @private
|
|
794
|
+
*/
|
|
795
|
+
_authorTag(doc, author) {
|
|
796
|
+
const result = ['<author>', '<personname>']
|
|
797
|
+
if (author.firstname)
|
|
798
|
+
result.push(
|
|
799
|
+
`<firstname>${doc.subReplacements(author.firstname)}</firstname>`
|
|
800
|
+
)
|
|
801
|
+
if (author.middlename)
|
|
802
|
+
result.push(
|
|
803
|
+
`<othername>${doc.subReplacements(author.middlename)}</othername>`
|
|
804
|
+
)
|
|
805
|
+
if (author.lastname)
|
|
806
|
+
result.push(`<surname>${doc.subReplacements(author.lastname)}</surname>`)
|
|
807
|
+
result.push('</personname>')
|
|
808
|
+
if (author.email) result.push(`<email>${author.email}</email>`)
|
|
809
|
+
result.push('</author>')
|
|
810
|
+
return result.join(LF)
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* @internal
|
|
815
|
+
* @private
|
|
816
|
+
*/
|
|
817
|
+
async _documentInfoTag(doc, abstract) {
|
|
818
|
+
const result = ['<info>']
|
|
819
|
+
if (!doc.isNotitle()) {
|
|
820
|
+
const title = doc.doctitle({ partition: true, use_fallback: true })
|
|
821
|
+
if (title?.subtitle) {
|
|
822
|
+
result.push(
|
|
823
|
+
`<title>${title.main}</title>\n<subtitle>${title.subtitle}</subtitle>`
|
|
824
|
+
)
|
|
825
|
+
} else if (title) {
|
|
826
|
+
result.push(`<title>${title}</title>`)
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
const date = doc.hasAttribute('revdate')
|
|
830
|
+
? doc.getAttribute('revdate')
|
|
831
|
+
: doc.hasAttribute('reproducible')
|
|
832
|
+
? null
|
|
833
|
+
: doc.getAttribute('docdate')
|
|
834
|
+
if (date) result.push(`<date>${date}</date>`)
|
|
835
|
+
if (doc.hasAttribute('copyright')) {
|
|
836
|
+
const m = CopyrightRx.exec(doc.getAttribute('copyright'))
|
|
837
|
+
if (m) {
|
|
838
|
+
result.push('<copyright>')
|
|
839
|
+
result.push(`<holder>${m[1]}</holder>`)
|
|
840
|
+
if (m[2]) result.push(`<year>${m[2]}</year>`)
|
|
841
|
+
result.push('</copyright>')
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
if (doc.hasHeader()) {
|
|
845
|
+
const authors = doc.authors()
|
|
846
|
+
if (authors.length > 0) {
|
|
847
|
+
if (authors.length > 1) {
|
|
848
|
+
result.push('<authorgroup>')
|
|
849
|
+
for (const author of authors)
|
|
850
|
+
result.push(this._authorTag(doc, author))
|
|
851
|
+
result.push('</authorgroup>')
|
|
852
|
+
} else {
|
|
853
|
+
const author = authors[0]
|
|
854
|
+
result.push(this._authorTag(doc, author))
|
|
855
|
+
if (author.initials)
|
|
856
|
+
result.push(`<authorinitials>${author.initials}</authorinitials>`)
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
if (
|
|
860
|
+
doc.hasAttribute('revdate') &&
|
|
861
|
+
(doc.hasAttribute('revnumber') || doc.hasAttribute('revremark'))
|
|
862
|
+
) {
|
|
863
|
+
result.push('<revhistory>\n<revision>')
|
|
864
|
+
if (doc.hasAttribute('revnumber'))
|
|
865
|
+
result.push(`<revnumber>${doc.getAttribute('revnumber')}</revnumber>`)
|
|
866
|
+
if (doc.hasAttribute('revdate'))
|
|
867
|
+
result.push(`<date>${doc.getAttribute('revdate')}</date>`)
|
|
868
|
+
if (doc.hasAttribute('authorinitials'))
|
|
869
|
+
result.push(
|
|
870
|
+
`<authorinitials>${doc.getAttribute('authorinitials')}</authorinitials>`
|
|
871
|
+
)
|
|
872
|
+
if (doc.hasAttribute('revremark'))
|
|
873
|
+
result.push(`<revremark>${doc.getAttribute('revremark')}</revremark>`)
|
|
874
|
+
result.push('</revision>\n</revhistory>')
|
|
875
|
+
}
|
|
876
|
+
if (
|
|
877
|
+
doc.hasAttribute('front-cover-image') ||
|
|
878
|
+
doc.hasAttribute('back-cover-image')
|
|
879
|
+
) {
|
|
880
|
+
const backCoverTag = await this._coverTag(doc, 'back')
|
|
881
|
+
if (backCoverTag) {
|
|
882
|
+
result.push(await this._coverTag(doc, 'front', true))
|
|
883
|
+
result.push(backCoverTag)
|
|
884
|
+
} else {
|
|
885
|
+
const frontCoverTag = await this._coverTag(doc, 'front')
|
|
886
|
+
if (frontCoverTag) result.push(frontCoverTag)
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
if (doc.hasAttribute('orgname'))
|
|
890
|
+
result.push(`<orgname>${doc.getAttribute('orgname')}</orgname>`)
|
|
891
|
+
const docinfo = await doc.docinfo()
|
|
892
|
+
if (docinfo) result.push(docinfo)
|
|
893
|
+
}
|
|
894
|
+
if (abstract) {
|
|
895
|
+
abstract.setAttribute('root-option', '')
|
|
896
|
+
result.push(await this.convert(abstract, abstract.nodeName))
|
|
897
|
+
abstract.removeAttribute('root-option')
|
|
898
|
+
}
|
|
899
|
+
result.push('</info>')
|
|
900
|
+
return result.join(LF)
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* @internal
|
|
905
|
+
* @private
|
|
906
|
+
*/
|
|
907
|
+
_findRootAbstract(doc) {
|
|
908
|
+
if (!doc.hasBlocks()) return null
|
|
909
|
+
let firstBlock = doc.blocks[0]
|
|
910
|
+
if (firstBlock.context === 'preamble') {
|
|
911
|
+
if (!firstBlock.hasBlocks()) return null
|
|
912
|
+
firstBlock = firstBlock.blocks[0]
|
|
913
|
+
} else if (firstBlock.context === 'section') {
|
|
914
|
+
if (firstBlock.sectname === 'abstract') return firstBlock
|
|
915
|
+
if (firstBlock.sectname !== 'preface' || !firstBlock.hasBlocks())
|
|
916
|
+
return null
|
|
917
|
+
firstBlock = firstBlock.blocks[0]
|
|
918
|
+
}
|
|
919
|
+
return firstBlock.style === 'abstract' && firstBlock.context === 'open'
|
|
920
|
+
? firstBlock
|
|
921
|
+
: null
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* @internal
|
|
926
|
+
* @private
|
|
927
|
+
*/
|
|
928
|
+
_extractAbstract(document, abstract) {
|
|
929
|
+
let parent = abstract.getParent()
|
|
930
|
+
let toDelete = abstract
|
|
931
|
+
while (parent !== document && parent.blocks.length === 1) {
|
|
932
|
+
toDelete = parent
|
|
933
|
+
parent = parent.getParent()
|
|
934
|
+
}
|
|
935
|
+
parent.blocks.splice(parent.blocks.indexOf(toDelete), 1)
|
|
936
|
+
return abstract
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* @internal
|
|
941
|
+
* @private
|
|
942
|
+
*/
|
|
943
|
+
_restoreAbstract(abstract) {
|
|
944
|
+
abstract.getParent().blocks.unshift(abstract)
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* @internal
|
|
949
|
+
* @private
|
|
950
|
+
*/
|
|
951
|
+
_getRootDocument(node) {
|
|
952
|
+
let doc = node.document
|
|
953
|
+
while (doc.isNested()) doc = doc.parentDocument
|
|
954
|
+
return doc
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* @internal
|
|
959
|
+
* @private
|
|
960
|
+
*/
|
|
961
|
+
_generateDocumentId(doc) {
|
|
962
|
+
return `__${doc.doctype}-root__`
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* @internal
|
|
967
|
+
* @private
|
|
968
|
+
*/
|
|
969
|
+
async _encloseContent(node) {
|
|
970
|
+
return node.contentModel === 'compound'
|
|
971
|
+
? await node.content()
|
|
972
|
+
: `<simpara>${await node.content()}</simpara>`
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* @internal
|
|
977
|
+
* @private
|
|
978
|
+
*/
|
|
979
|
+
_titleTag(node, optional = true) {
|
|
980
|
+
if (optional && !node.hasTitle()) return ''
|
|
981
|
+
return `<title>${node.title ?? ''}</title>\n`
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* @internal
|
|
986
|
+
* @private
|
|
987
|
+
*/
|
|
988
|
+
async _coverTag(doc, face, usePlaceholder = false) {
|
|
989
|
+
const coverImage = doc.getAttribute(`${face}-cover-image`)
|
|
990
|
+
if (coverImage) {
|
|
991
|
+
let fileref = coverImage
|
|
992
|
+
const sizeAttrs = ''
|
|
993
|
+
// Check if it's an image macro (contains ':')
|
|
994
|
+
if (coverImage.includes(':')) {
|
|
995
|
+
const m = /^image::?(\S|\S.*?\S)\[(.*?)?\]$/.exec(coverImage)
|
|
996
|
+
if (m) {
|
|
997
|
+
fileref = await doc.imageUri(m[1])
|
|
998
|
+
// size attrs parsing omitted for simplicity
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return `<cover role="${face}">\n<mediaobject>\n<imageobject>\n<imagedata fileref="${fileref}"${sizeAttrs}/>\n</imageobject>\n</mediaobject>\n</cover>`
|
|
1002
|
+
}
|
|
1003
|
+
if (usePlaceholder) return `<cover role="${face}"/>`
|
|
1004
|
+
return null
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* @internal
|
|
1009
|
+
* @private
|
|
1010
|
+
*/
|
|
1011
|
+
async _blockquoteTag(node, tagName, contentFn) {
|
|
1012
|
+
const tag = tagName || 'blockquote'
|
|
1013
|
+
const result = [
|
|
1014
|
+
`<${tag}${this._commonAttributes(node.id, node.role, node.reftext)}>`,
|
|
1015
|
+
]
|
|
1016
|
+
if (node.hasTitle()) result.push(`<title>${node.title}</title>`)
|
|
1017
|
+
if (node.hasAttribute('attribution') || node.hasAttribute('citetitle')) {
|
|
1018
|
+
result.push('<attribution>')
|
|
1019
|
+
if (node.hasAttribute('attribution'))
|
|
1020
|
+
result.push(node.getAttribute('attribution'))
|
|
1021
|
+
if (node.hasAttribute('citetitle'))
|
|
1022
|
+
result.push(`<citetitle>${node.getAttribute('citetitle')}</citetitle>`)
|
|
1023
|
+
result.push('</attribution>')
|
|
1024
|
+
}
|
|
1025
|
+
result.push(await contentFn())
|
|
1026
|
+
result.push(`</${tag}>`)
|
|
1027
|
+
return result.join(LF)
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
export default DocBook5Converter
|