epub-codec 0.0.0 → 1.0.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.
- package/LICENSE +21 -0
- package/README.md +184 -0
- package/dist/codec.cjs +28 -0
- package/dist/codec.d.cts +2308 -0
- package/dist/codec.d.ts +2308 -0
- package/dist/codec.js +25 -0
- package/dist/diagnostics-Ee4kG7mw.d.cts +60 -0
- package/dist/diagnostics-Ee4kG7mw.d.ts +60 -0
- package/dist/diagnostics.cjs +96 -0
- package/dist/diagnostics.d.cts +2 -0
- package/dist/diagnostics.d.ts +2 -0
- package/dist/diagnostics.js +85 -0
- package/dist/format.cjs +9 -0
- package/dist/format.d.cts +6 -0
- package/dist/format.d.ts +6 -0
- package/dist/format.js +6 -0
- package/dist/image/dimensions.cjs +82 -0
- package/dist/image/dimensions.d.cts +11 -0
- package/dist/image/dimensions.d.ts +11 -0
- package/dist/image/dimensions.js +79 -0
- package/dist/index.cjs +105 -0
- package/dist/index.d.cts +32 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +32 -0
- package/dist/nav/nav3.cjs +19 -0
- package/dist/nav/nav3.d.cts +4 -0
- package/dist/nav/nav3.d.ts +4 -0
- package/dist/nav/nav3.js +18 -0
- package/dist/nav/ncx.cjs +17 -0
- package/dist/nav/ncx.d.cts +4 -0
- package/dist/nav/ncx.d.ts +4 -0
- package/dist/nav/ncx.js +16 -0
- package/dist/nav/reconcile.cjs +8 -0
- package/dist/nav/reconcile.d.cts +4 -0
- package/dist/nav/reconcile.d.ts +4 -0
- package/dist/nav/reconcile.js +7 -0
- package/dist/nav/write.cjs +39 -0
- package/dist/nav/write.d.cts +10 -0
- package/dist/nav/write.d.ts +10 -0
- package/dist/nav/write.js +37 -0
- package/dist/ocf/container.cjs +20 -0
- package/dist/ocf/container.d.cts +4 -0
- package/dist/ocf/container.d.ts +4 -0
- package/dist/ocf/container.js +19 -0
- package/dist/ocf/write.cjs +13 -0
- package/dist/ocf/write.d.cts +4 -0
- package/dist/ocf/write.d.ts +4 -0
- package/dist/ocf/write.js +12 -0
- package/dist/opf/metadata.cjs +51 -0
- package/dist/opf/metadata.d.cts +7 -0
- package/dist/opf/metadata.d.ts +7 -0
- package/dist/opf/metadata.js +50 -0
- package/dist/opf/parse.cjs +47 -0
- package/dist/opf/parse.d.cts +6 -0
- package/dist/opf/parse.d.ts +6 -0
- package/dist/opf/parse.js +46 -0
- package/dist/opf/types.cjs +0 -0
- package/dist/opf/types.d.cts +20 -0
- package/dist/opf/types.d.ts +20 -0
- package/dist/opf/types.js +0 -0
- package/dist/opf/write.cjs +55 -0
- package/dist/opf/write.d.cts +12 -0
- package/dist/opf/write.d.ts +12 -0
- package/dist/opf/write.js +54 -0
- package/dist/path.cjs +22 -0
- package/dist/path.d.cts +5 -0
- package/dist/path.d.ts +5 -0
- package/dist/path.js +20 -0
- package/dist/read.cjs +166 -0
- package/dist/read.d.cts +10 -0
- package/dist/read.d.ts +10 -0
- package/dist/read.js +164 -0
- package/dist/util/base64.cjs +51 -0
- package/dist/util/base64.d.cts +5 -0
- package/dist/util/base64.d.ts +5 -0
- package/dist/util/base64.js +49 -0
- package/dist/write.cjs +136 -0
- package/dist/write.d.cts +10 -0
- package/dist/write.d.ts +10 -0
- package/dist/write.js +134 -0
- package/dist/xhtml/context.cjs +0 -0
- package/dist/xhtml/context.d.cts +20 -0
- package/dist/xhtml/context.d.ts +20 -0
- package/dist/xhtml/context.js +0 -0
- package/dist/xhtml/footnote.cjs +32 -0
- package/dist/xhtml/footnote.d.cts +7 -0
- package/dist/xhtml/footnote.d.ts +7 -0
- package/dist/xhtml/footnote.js +29 -0
- package/dist/xhtml/inline.cjs +128 -0
- package/dist/xhtml/inline.d.cts +11 -0
- package/dist/xhtml/inline.d.ts +11 -0
- package/dist/xhtml/inline.js +127 -0
- package/dist/xhtml/list-id.cjs +22 -0
- package/dist/xhtml/list-id.d.cts +13 -0
- package/dist/xhtml/list-id.d.ts +13 -0
- package/dist/xhtml/list-id.js +20 -0
- package/dist/xhtml/read.cjs +423 -0
- package/dist/xhtml/read.d.cts +20 -0
- package/dist/xhtml/read.d.ts +20 -0
- package/dist/xhtml/read.js +422 -0
- package/dist/xhtml/style-constants.cjs +13 -0
- package/dist/xhtml/style-constants.d.cts +8 -0
- package/dist/xhtml/style-constants.d.ts +8 -0
- package/dist/xhtml/style-constants.js +8 -0
- package/dist/xhtml/write.cjs +183 -0
- package/dist/xhtml/write.d.cts +12 -0
- package/dist/xhtml/write.d.ts +12 -0
- package/dist/xhtml/write.js +182 -0
- package/dist/xml/build.cjs +46 -0
- package/dist/xml/build.d.cts +5 -0
- package/dist/xml/build.d.ts +5 -0
- package/dist/xml/build.js +46 -0
- package/dist/xml/entities.cjs +20 -0
- package/dist/xml/entities.d.cts +5 -0
- package/dist/xml/entities.d.ts +5 -0
- package/dist/xml/entities.js +18 -0
- package/dist/xml/node.cjs +67 -0
- package/dist/xml/node.d.cts +83 -0
- package/dist/xml/node.d.ts +83 -0
- package/dist/xml/node.js +58 -0
- package/dist/xml/parse.cjs +88 -0
- package/dist/xml/parse.d.cts +5 -0
- package/dist/xml/parse.d.ts +5 -0
- package/dist/xml/parse.js +88 -0
- package/dist/xml/query.cjs +44 -0
- package/dist/xml/query.d.cts +11 -0
- package/dist/xml/query.d.ts +11 -0
- package/dist/xml/query.js +37 -0
- package/dist/zip.cjs +19 -0
- package/dist/zip.d.cts +9 -0
- package/dist/zip.d.ts +9 -0
- package/dist/zip.js +17 -0
- package/package.json +95 -2
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import { EpubDiagnosticCodes } from "../diagnostics.js";
|
|
2
|
+
import { attrValue, elementsWithTag, findChildElement, rootElement, textContent } from "../xml/query.js";
|
|
3
|
+
import { parseXml } from "../xml/parse.js";
|
|
4
|
+
import { decodeEntities } from "../xml/entities.js";
|
|
5
|
+
import { POINTS_PER_PIXEL, detectImageFormat, readImageDimensions } from "../image/dimensions.js";
|
|
6
|
+
import { bytesToBase64 } from "../util/base64.js";
|
|
7
|
+
import { buildXml } from "../xml/build.js";
|
|
8
|
+
import { isFootnoteAside, isFootnoteReferenceAnchor } from "./footnote.js";
|
|
9
|
+
import { HORIZONTAL_RULE_STYLE_ID, MONOSPACE_FONT_FAMILY } from "./style-constants.js";
|
|
10
|
+
import { buildInlineRuns } from "./inline.js";
|
|
11
|
+
import { mintListNumId } from "./list-id.js";
|
|
12
|
+
import { clampHeadingLevel } from "document-schema.js";
|
|
13
|
+
//#region src/xhtml/read.ts
|
|
14
|
+
function createIdMinter() {
|
|
15
|
+
let nextList = 0;
|
|
16
|
+
let nextItem = 0;
|
|
17
|
+
return {
|
|
18
|
+
mintNumId: (options) => {
|
|
19
|
+
nextList += 1;
|
|
20
|
+
return mintListNumId(nextList, options);
|
|
21
|
+
},
|
|
22
|
+
mintItemId: () => {
|
|
23
|
+
nextItem += 1;
|
|
24
|
+
return `item${String(nextItem)}`;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function withQuote(state) {
|
|
29
|
+
return {
|
|
30
|
+
...state,
|
|
31
|
+
quoteDepth: state.quoteDepth + 1
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function withListItem(state, listItem) {
|
|
35
|
+
return {
|
|
36
|
+
...state,
|
|
37
|
+
listItem,
|
|
38
|
+
list: {
|
|
39
|
+
numId: listItem.numId,
|
|
40
|
+
level: listItem.level
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function decorateParagraph(paragraph, state) {
|
|
45
|
+
let decorated = paragraph;
|
|
46
|
+
if (state.quoteDepth > 0) decorated = {
|
|
47
|
+
...decorated,
|
|
48
|
+
indentLeftPt: state.quoteDepth * 36,
|
|
49
|
+
styleId: decorated.styleId ?? "Quote"
|
|
50
|
+
};
|
|
51
|
+
if (state.listItem !== void 0) {
|
|
52
|
+
const membership = {
|
|
53
|
+
numId: state.listItem.numId,
|
|
54
|
+
level: state.listItem.level,
|
|
55
|
+
itemId: state.listItem.itemId
|
|
56
|
+
};
|
|
57
|
+
decorated = {
|
|
58
|
+
...decorated,
|
|
59
|
+
list: membership
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return decorated;
|
|
63
|
+
}
|
|
64
|
+
function buildIdElementMap(nodes) {
|
|
65
|
+
const map = /* @__PURE__ */ new Map();
|
|
66
|
+
const stack = [...nodes];
|
|
67
|
+
while (stack.length > 0) {
|
|
68
|
+
const node = stack.pop();
|
|
69
|
+
if (node?.type !== "element") continue;
|
|
70
|
+
const id = attrValue(node, "id");
|
|
71
|
+
if (id !== void 0 && !map.has(id)) map.set(id, node);
|
|
72
|
+
stack.push(...node.children);
|
|
73
|
+
}
|
|
74
|
+
return map;
|
|
75
|
+
}
|
|
76
|
+
function readStyleResidue(html, context) {
|
|
77
|
+
const head = findChildElement(html.children, "head");
|
|
78
|
+
if (head === void 0) return;
|
|
79
|
+
const styleElements = head.children.filter((node) => node.type === "element" && (node.tag === "style" || node.tag === "link" && attrValue(node, "rel") === "stylesheet"));
|
|
80
|
+
if (styleElements.length === 0) return;
|
|
81
|
+
context.sink({
|
|
82
|
+
code: EpubDiagnosticCodes.STYLE_RESIDUE,
|
|
83
|
+
severity: "info",
|
|
84
|
+
message: "the document's own <head> style declarations (CSS) are quarantined as residue rather than interpreted; the schema is content, not styling",
|
|
85
|
+
href: context.sourceHref
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
format: "epub",
|
|
89
|
+
xml: buildXml(styleElements)
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function readXhtmlBody(xml, options) {
|
|
93
|
+
const nodes = parseXml(xml);
|
|
94
|
+
const html = rootElement(nodes);
|
|
95
|
+
const body = html === void 0 ? void 0 : findChildElement(html.children, "body");
|
|
96
|
+
if (body === void 0 || html === void 0) return {
|
|
97
|
+
blocks: [],
|
|
98
|
+
source: void 0
|
|
99
|
+
};
|
|
100
|
+
const idElements = buildIdElementMap(body.children);
|
|
101
|
+
const footnoteTargetIds = /* @__PURE__ */ new Set();
|
|
102
|
+
for (const anchor of elementsWithTag(body.children, "a")) {
|
|
103
|
+
const name = isFootnoteReferenceAnchor(anchor, idElements);
|
|
104
|
+
if (name !== void 0) footnoteTargetIds.add(name);
|
|
105
|
+
}
|
|
106
|
+
const context = {
|
|
107
|
+
resolveImage: options.resolveImage,
|
|
108
|
+
sink: options.sink,
|
|
109
|
+
sourceHref: options.sourceHref,
|
|
110
|
+
idElements,
|
|
111
|
+
footnoteTargetIds,
|
|
112
|
+
quoteDepth: 0
|
|
113
|
+
};
|
|
114
|
+
const state = {
|
|
115
|
+
context,
|
|
116
|
+
minter: createIdMinter(),
|
|
117
|
+
quoteDepth: 0,
|
|
118
|
+
listItem: void 0,
|
|
119
|
+
list: void 0,
|
|
120
|
+
contentWidthPt: options.contentWidthPt
|
|
121
|
+
};
|
|
122
|
+
return {
|
|
123
|
+
blocks: readContainerChildren(body.children, state),
|
|
124
|
+
source: readStyleResidue(html, context)
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const BLOCK_LEVEL_TAGS = /* @__PURE__ */ new Set([
|
|
128
|
+
"p",
|
|
129
|
+
"h1",
|
|
130
|
+
"h2",
|
|
131
|
+
"h3",
|
|
132
|
+
"h4",
|
|
133
|
+
"h5",
|
|
134
|
+
"h6",
|
|
135
|
+
"ul",
|
|
136
|
+
"ol",
|
|
137
|
+
"dl",
|
|
138
|
+
"table",
|
|
139
|
+
"blockquote",
|
|
140
|
+
"pre",
|
|
141
|
+
"hr",
|
|
142
|
+
"figure",
|
|
143
|
+
"figcaption",
|
|
144
|
+
"div",
|
|
145
|
+
"section",
|
|
146
|
+
"article",
|
|
147
|
+
"aside",
|
|
148
|
+
"nav",
|
|
149
|
+
"img"
|
|
150
|
+
]);
|
|
151
|
+
function readContainerChildren(nodes, state) {
|
|
152
|
+
const blocks = [];
|
|
153
|
+
let segment = [];
|
|
154
|
+
const flush = () => {
|
|
155
|
+
if (segment.length === 0) return;
|
|
156
|
+
const inline = buildInlineRuns(segment, {}, state.context);
|
|
157
|
+
segment = [];
|
|
158
|
+
if (inline.runs.every((run) => run.text.trim().length === 0)) return;
|
|
159
|
+
const paragraph = {
|
|
160
|
+
kind: "paragraph",
|
|
161
|
+
runs: inline.runs,
|
|
162
|
+
...inline.constructs.length > 0 ? { constructs: inline.constructs } : {}
|
|
163
|
+
};
|
|
164
|
+
blocks.push(decorateParagraph(paragraph, state));
|
|
165
|
+
};
|
|
166
|
+
for (const node of nodes) {
|
|
167
|
+
if (node.type === "element" && BLOCK_LEVEL_TAGS.has(node.tag)) {
|
|
168
|
+
flush();
|
|
169
|
+
blocks.push(...readBlockElement(node, state));
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (node.type === "element" || node.type === "text") segment.push(node);
|
|
173
|
+
}
|
|
174
|
+
flush();
|
|
175
|
+
return blocks;
|
|
176
|
+
}
|
|
177
|
+
function headingLevelOf(tag) {
|
|
178
|
+
const match = /^h([1-6])$/u.exec(tag);
|
|
179
|
+
return match?.[1] === void 0 ? void 0 : clampHeadingLevel(Number(match[1]));
|
|
180
|
+
}
|
|
181
|
+
function readBlockElement(element, state) {
|
|
182
|
+
const id = attrValue(element, "id");
|
|
183
|
+
const isFootnoteTarget = id !== void 0 && state.context.footnoteTargetIds.has(id) && !isFootnoteAside(element);
|
|
184
|
+
const blocks = readBlockElementInner(element, state);
|
|
185
|
+
if (!isFootnoteTarget) return blocks;
|
|
186
|
+
return [
|
|
187
|
+
{
|
|
188
|
+
kind: "constructStart",
|
|
189
|
+
descriptor: {
|
|
190
|
+
kind: "anchor",
|
|
191
|
+
anchorType: "footnote",
|
|
192
|
+
name: id
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
...blocks,
|
|
196
|
+
{ kind: "constructEnd" }
|
|
197
|
+
];
|
|
198
|
+
}
|
|
199
|
+
function readBlockElementInner(element, state) {
|
|
200
|
+
const headingLevel = headingLevelOf(element.tag);
|
|
201
|
+
if (headingLevel !== void 0) {
|
|
202
|
+
const inline = buildInlineRuns(element.children, {}, state.context);
|
|
203
|
+
return [decorateParagraph({
|
|
204
|
+
kind: "paragraph",
|
|
205
|
+
headingLevel,
|
|
206
|
+
runs: inline.runs,
|
|
207
|
+
...inline.constructs.length > 0 ? { constructs: inline.constructs } : {}
|
|
208
|
+
}, state)];
|
|
209
|
+
}
|
|
210
|
+
switch (element.tag) {
|
|
211
|
+
case "p": return readContainerChildren(element.children, state);
|
|
212
|
+
case "hr": return [decorateParagraph({
|
|
213
|
+
kind: "paragraph",
|
|
214
|
+
runs: [],
|
|
215
|
+
styleId: HORIZONTAL_RULE_STYLE_ID
|
|
216
|
+
}, state)];
|
|
217
|
+
case "pre": return [readPre(element, state)];
|
|
218
|
+
case "blockquote": return readBlockquote(element, state);
|
|
219
|
+
case "ul":
|
|
220
|
+
case "ol": return readList(element, state);
|
|
221
|
+
case "dl": return readDefinitionList(element, state);
|
|
222
|
+
case "table": return [readTable(element, state)];
|
|
223
|
+
case "figure": return readContainerChildren(element.children, state);
|
|
224
|
+
case "figcaption": return [decorateParagraph({
|
|
225
|
+
kind: "paragraph",
|
|
226
|
+
runs: buildInlineRuns(element.children, {}, state.context).runs
|
|
227
|
+
}, state)];
|
|
228
|
+
case "img": {
|
|
229
|
+
const block = readImage(element, state);
|
|
230
|
+
return block === void 0 ? [] : [block];
|
|
231
|
+
}
|
|
232
|
+
case "aside": return readAside(element, state);
|
|
233
|
+
default: return readContainerChildren(element.children, state);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function readPre(element, state) {
|
|
237
|
+
const languageSource = findChildElement(element.children, "code") ?? element;
|
|
238
|
+
const codeLanguage = languageFromClass(attrValue(languageSource, "class"));
|
|
239
|
+
const text = decodeEntities(textContent(element.children));
|
|
240
|
+
return decorateParagraph({
|
|
241
|
+
kind: "paragraph",
|
|
242
|
+
runs: text.length > 0 ? [{
|
|
243
|
+
text,
|
|
244
|
+
fontFamily: MONOSPACE_FONT_FAMILY
|
|
245
|
+
}] : [],
|
|
246
|
+
...codeLanguage !== void 0 ? { codeLanguage } : {}
|
|
247
|
+
}, state);
|
|
248
|
+
}
|
|
249
|
+
function languageFromClass(className) {
|
|
250
|
+
if (className === void 0) return;
|
|
251
|
+
return /(?:^|\s)language-(\S+)/u.exec(className)?.[1];
|
|
252
|
+
}
|
|
253
|
+
function containsHeading(nodes) {
|
|
254
|
+
for (const node of nodes) {
|
|
255
|
+
if (node.type !== "element") continue;
|
|
256
|
+
if (headingLevelOf(node.tag) !== void 0) return true;
|
|
257
|
+
if (containsHeading(node.children)) return true;
|
|
258
|
+
}
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
function readBlockquote(element, state) {
|
|
262
|
+
const nestedState = withQuote(state);
|
|
263
|
+
const blocks = readContainerChildren(element.children, nestedState);
|
|
264
|
+
if (containsHeading(element.children)) return blocks;
|
|
265
|
+
return [
|
|
266
|
+
{
|
|
267
|
+
kind: "constructStart",
|
|
268
|
+
descriptor: { kind: "division" }
|
|
269
|
+
},
|
|
270
|
+
...blocks,
|
|
271
|
+
{ kind: "constructEnd" }
|
|
272
|
+
];
|
|
273
|
+
}
|
|
274
|
+
function readList(element, state) {
|
|
275
|
+
const numId = state.list?.numId ?? state.minter.mintNumId({
|
|
276
|
+
type: element.tag === "ol" ? "ordered" : "bullet",
|
|
277
|
+
start: startAttr(element)
|
|
278
|
+
});
|
|
279
|
+
const level = state.list === void 0 ? 0 : state.list.level + 1;
|
|
280
|
+
const blocks = [];
|
|
281
|
+
for (const child of element.children) {
|
|
282
|
+
if (child.type !== "element" || child.tag !== "li") continue;
|
|
283
|
+
const itemState = withListItem(state, {
|
|
284
|
+
numId,
|
|
285
|
+
level,
|
|
286
|
+
itemId: state.minter.mintItemId()
|
|
287
|
+
});
|
|
288
|
+
blocks.push(...readContainerChildren(child.children, itemState));
|
|
289
|
+
}
|
|
290
|
+
return blocks;
|
|
291
|
+
}
|
|
292
|
+
function startAttr(element) {
|
|
293
|
+
return positiveIntAttr(element, "start");
|
|
294
|
+
}
|
|
295
|
+
function readDefinitionList(element, state) {
|
|
296
|
+
const blocks = [];
|
|
297
|
+
for (const child of element.children) {
|
|
298
|
+
if (child.type !== "element") continue;
|
|
299
|
+
if (child.tag === "dt") {
|
|
300
|
+
const inline = buildInlineRuns(child.children, {}, state.context);
|
|
301
|
+
blocks.push(decorateParagraph({
|
|
302
|
+
kind: "paragraph",
|
|
303
|
+
runs: inline.runs
|
|
304
|
+
}, state));
|
|
305
|
+
} else if (child.tag === "dd") {
|
|
306
|
+
const inline = buildInlineRuns(child.children, {}, state.context);
|
|
307
|
+
blocks.push(decorateParagraph({
|
|
308
|
+
kind: "paragraph",
|
|
309
|
+
runs: inline.runs,
|
|
310
|
+
indentLeftPt: 36 + state.quoteDepth * 36
|
|
311
|
+
}, state));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return blocks;
|
|
315
|
+
}
|
|
316
|
+
function readTable(element, state) {
|
|
317
|
+
const rows = [];
|
|
318
|
+
let columnCount = 0;
|
|
319
|
+
for (const section of element.children) {
|
|
320
|
+
if (section.type !== "element") continue;
|
|
321
|
+
const rowContainers = section.tag === "tr" ? [section] : section.tag === "thead" || section.tag === "tbody" || section.tag === "tfoot" ? section.children.filter((c) => c.type === "element" && c.tag === "tr") : [];
|
|
322
|
+
for (const tr of rowContainers) {
|
|
323
|
+
const cells = [];
|
|
324
|
+
for (const cellNode of tr.children) {
|
|
325
|
+
if (cellNode.type !== "element" || cellNode.tag !== "td" && cellNode.tag !== "th") continue;
|
|
326
|
+
const cellStyle = cellNode.tag === "th" ? { bold: true } : {};
|
|
327
|
+
const paragraph = {
|
|
328
|
+
kind: "paragraph",
|
|
329
|
+
runs: buildInlineRuns(cellNode.children, cellStyle, state.context).runs
|
|
330
|
+
};
|
|
331
|
+
const colSpan = positiveIntAttr(cellNode, "colspan");
|
|
332
|
+
const rowSpan = positiveIntAttr(cellNode, "rowspan");
|
|
333
|
+
cells.push({
|
|
334
|
+
blocks: [paragraph],
|
|
335
|
+
...colSpan !== void 0 ? { colSpan } : {},
|
|
336
|
+
...rowSpan !== void 0 ? { rowSpan } : {}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
columnCount = Math.max(columnCount, cells.length);
|
|
340
|
+
rows.push({ cells });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
const width = columnCount > 0 ? state.contentWidthPt / columnCount : state.contentWidthPt;
|
|
344
|
+
return {
|
|
345
|
+
kind: "table",
|
|
346
|
+
rows,
|
|
347
|
+
columnWidthsPt: new Array(Math.max(columnCount, 1)).fill(width)
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function positiveIntAttr(element, name) {
|
|
351
|
+
const raw = attrValue(element, name);
|
|
352
|
+
if (raw === void 0) return;
|
|
353
|
+
const value = Number.parseInt(raw, 10);
|
|
354
|
+
return Number.isInteger(value) && value > 0 ? value : void 0;
|
|
355
|
+
}
|
|
356
|
+
function readImage(element, state) {
|
|
357
|
+
const src = attrValue(element, "src");
|
|
358
|
+
const alt = attrValue(element, "alt");
|
|
359
|
+
if (src === void 0) return;
|
|
360
|
+
const bytes = state.context.resolveImage(src);
|
|
361
|
+
if (bytes === void 0) {
|
|
362
|
+
state.context.sink({
|
|
363
|
+
code: EpubDiagnosticCodes.IMAGE_UNRESOLVED,
|
|
364
|
+
severity: "warning",
|
|
365
|
+
message: `<img src="${src}"> names no resolvable manifest part; degraded to its alt text`,
|
|
366
|
+
href: state.context.sourceHref
|
|
367
|
+
});
|
|
368
|
+
return alt === void 0 || alt.length === 0 ? void 0 : decorateParagraph({
|
|
369
|
+
kind: "paragraph",
|
|
370
|
+
runs: [{ text: decodeEntities(alt) }]
|
|
371
|
+
}, state);
|
|
372
|
+
}
|
|
373
|
+
const format = detectImageFormat(bytes);
|
|
374
|
+
const dimensions = readImageDimensions(bytes);
|
|
375
|
+
if (format === void 0 || dimensions === void 0) {
|
|
376
|
+
state.context.sink({
|
|
377
|
+
code: EpubDiagnosticCodes.IMAGE_FORMAT_UNSUPPORTED,
|
|
378
|
+
severity: "warning",
|
|
379
|
+
message: `<img src="${src}"> is neither a PNG nor a JPEG (document-schema.js's ContentImageBlock supports only those two); degraded to its alt text`,
|
|
380
|
+
href: state.context.sourceHref
|
|
381
|
+
});
|
|
382
|
+
return alt === void 0 || alt.length === 0 ? void 0 : decorateParagraph({
|
|
383
|
+
kind: "paragraph",
|
|
384
|
+
runs: [{ text: decodeEntities(alt) }]
|
|
385
|
+
}, state);
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
kind: "image",
|
|
389
|
+
format,
|
|
390
|
+
base64: bytesToBase64(bytes),
|
|
391
|
+
widthPt: dimensions.widthPx * POINTS_PER_PIXEL,
|
|
392
|
+
heightPt: dimensions.heightPx * POINTS_PER_PIXEL,
|
|
393
|
+
...alt !== void 0 && alt.length > 0 ? { altText: decodeEntities(alt) } : {}
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
function readAside(element, state) {
|
|
397
|
+
if (!isFootnoteAside(element)) return readContainerChildren(element.children, state);
|
|
398
|
+
const name = attrValue(element, "id");
|
|
399
|
+
if (name === void 0) {
|
|
400
|
+
state.context.sink({
|
|
401
|
+
code: EpubDiagnosticCodes.FOOTNOTE_TARGET_UNRESOLVED,
|
|
402
|
+
severity: "warning",
|
|
403
|
+
message: "a footnote <aside> carries no id and cannot be referenced; read as ordinary content",
|
|
404
|
+
href: state.context.sourceHref
|
|
405
|
+
});
|
|
406
|
+
return readContainerChildren(element.children, state);
|
|
407
|
+
}
|
|
408
|
+
return [
|
|
409
|
+
{
|
|
410
|
+
kind: "constructStart",
|
|
411
|
+
descriptor: {
|
|
412
|
+
kind: "anchor",
|
|
413
|
+
anchorType: "footnote",
|
|
414
|
+
name
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
...readContainerChildren(element.children, state),
|
|
418
|
+
{ kind: "constructEnd" }
|
|
419
|
+
];
|
|
420
|
+
}
|
|
421
|
+
//#endregion
|
|
422
|
+
export { readXhtmlBody };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/xhtml/style-constants.ts
|
|
3
|
+
const HORIZONTAL_RULE_STYLE_ID = "HorizontalRule";
|
|
4
|
+
const QUOTE_STYLE_ID = "Quote";
|
|
5
|
+
const QUOTE_INDENT_PT = 36;
|
|
6
|
+
const MONOSPACE_FONT_FAMILY = "Courier New";
|
|
7
|
+
const DEFINITION_BODY_INDENT_PT = 36;
|
|
8
|
+
//#endregion
|
|
9
|
+
exports.DEFINITION_BODY_INDENT_PT = DEFINITION_BODY_INDENT_PT;
|
|
10
|
+
exports.HORIZONTAL_RULE_STYLE_ID = HORIZONTAL_RULE_STYLE_ID;
|
|
11
|
+
exports.MONOSPACE_FONT_FAMILY = MONOSPACE_FONT_FAMILY;
|
|
12
|
+
exports.QUOTE_INDENT_PT = QUOTE_INDENT_PT;
|
|
13
|
+
exports.QUOTE_STYLE_ID = QUOTE_STYLE_ID;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/xhtml/style-constants.d.ts
|
|
2
|
+
declare const HORIZONTAL_RULE_STYLE_ID = "HorizontalRule";
|
|
3
|
+
declare const QUOTE_STYLE_ID = "Quote";
|
|
4
|
+
declare const QUOTE_INDENT_PT = 36;
|
|
5
|
+
declare const MONOSPACE_FONT_FAMILY = "Courier New";
|
|
6
|
+
declare const DEFINITION_BODY_INDENT_PT = 36;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { DEFINITION_BODY_INDENT_PT, HORIZONTAL_RULE_STYLE_ID, MONOSPACE_FONT_FAMILY, QUOTE_INDENT_PT, QUOTE_STYLE_ID };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/xhtml/style-constants.d.ts
|
|
2
|
+
declare const HORIZONTAL_RULE_STYLE_ID = "HorizontalRule";
|
|
3
|
+
declare const QUOTE_STYLE_ID = "Quote";
|
|
4
|
+
declare const QUOTE_INDENT_PT = 36;
|
|
5
|
+
declare const MONOSPACE_FONT_FAMILY = "Courier New";
|
|
6
|
+
declare const DEFINITION_BODY_INDENT_PT = 36;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { DEFINITION_BODY_INDENT_PT, HORIZONTAL_RULE_STYLE_ID, MONOSPACE_FONT_FAMILY, QUOTE_INDENT_PT, QUOTE_STYLE_ID };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/xhtml/style-constants.ts
|
|
2
|
+
const HORIZONTAL_RULE_STYLE_ID = "HorizontalRule";
|
|
3
|
+
const QUOTE_STYLE_ID = "Quote";
|
|
4
|
+
const QUOTE_INDENT_PT = 36;
|
|
5
|
+
const MONOSPACE_FONT_FAMILY = "Courier New";
|
|
6
|
+
const DEFINITION_BODY_INDENT_PT = 36;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { DEFINITION_BODY_INDENT_PT, HORIZONTAL_RULE_STYLE_ID, MONOSPACE_FONT_FAMILY, QUOTE_INDENT_PT, QUOTE_STYLE_ID };
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_diagnostics = require("../diagnostics.cjs");
|
|
3
|
+
const require_xml_entities = require("../xml/entities.cjs");
|
|
4
|
+
const require_util_base64 = require("../util/base64.cjs");
|
|
5
|
+
require("./style-constants.cjs");
|
|
6
|
+
const require_xhtml_list_id = require("./list-id.cjs");
|
|
7
|
+
let document_schema_js = require("document-schema.js");
|
|
8
|
+
let document_schema_js_decompose = require("document-schema.js/decompose");
|
|
9
|
+
//#region src/xhtml/write.ts
|
|
10
|
+
function element(tag, attrs = {}, children = []) {
|
|
11
|
+
return {
|
|
12
|
+
type: "element",
|
|
13
|
+
tag,
|
|
14
|
+
attributes: Object.entries(attrs).map(([name, value]) => ({
|
|
15
|
+
name,
|
|
16
|
+
value
|
|
17
|
+
})),
|
|
18
|
+
children
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function text(value) {
|
|
22
|
+
return {
|
|
23
|
+
type: "text",
|
|
24
|
+
value: require_xml_entities.encodeEntities(value)
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function writeXhtmlBody(blocks, context) {
|
|
28
|
+
const { children } = (0, document_schema_js_decompose.decomposeSection)({
|
|
29
|
+
pageSize: {
|
|
30
|
+
widthPt: 1,
|
|
31
|
+
heightPt: 1
|
|
32
|
+
},
|
|
33
|
+
margins: {
|
|
34
|
+
topPt: 0,
|
|
35
|
+
rightPt: 0,
|
|
36
|
+
bottomPt: 0,
|
|
37
|
+
leftPt: 0
|
|
38
|
+
},
|
|
39
|
+
blocks: [...blocks]
|
|
40
|
+
});
|
|
41
|
+
return element("body", {}, writeSectionChildren(children, context));
|
|
42
|
+
}
|
|
43
|
+
function writeSectionChildren(children, context) {
|
|
44
|
+
const out = [];
|
|
45
|
+
let index = 0;
|
|
46
|
+
while (index < children.length) {
|
|
47
|
+
const child = children[index];
|
|
48
|
+
if (child === void 0) break;
|
|
49
|
+
if ((0, document_schema_js.isListGroupNode)(child)) {
|
|
50
|
+
let end = index;
|
|
51
|
+
while (end < children.length && (0, document_schema_js.isListGroupNode)(children[end])) end += 1;
|
|
52
|
+
const items = children.slice(index, end);
|
|
53
|
+
out.push(writeList(items, context));
|
|
54
|
+
index = end;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
out.push(...writeSectionChild(child, context));
|
|
58
|
+
index += 1;
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
function writeSectionChild(child, context) {
|
|
63
|
+
if ((0, document_schema_js.isHeadingGroupNode)(child)) return [writeHeading(child), ...writeSectionChildren(child.children, context)];
|
|
64
|
+
if ((0, document_schema_js.isListGroupNode)(child)) return [writeList([child], context)];
|
|
65
|
+
if ((0, document_schema_js.isSectionConstructGroupNode)(child)) return writeSectionConstructGroup(child, context);
|
|
66
|
+
return writeLeafBlock(child, context);
|
|
67
|
+
}
|
|
68
|
+
function writeHeading(group) {
|
|
69
|
+
const level = Math.min(6, Math.max(1, group.node.headingLevel));
|
|
70
|
+
return element(`h${String(level)}`, {}, writeRunsToNodes(group.node.runs, group.node.constructs));
|
|
71
|
+
}
|
|
72
|
+
function writeList(items, context) {
|
|
73
|
+
const numId = items[0]?.node.list.numId;
|
|
74
|
+
const info = numId === void 0 ? void 0 : require_xhtml_list_id.parseListNumId(numId);
|
|
75
|
+
return element(info?.type === "ordered" ? "ol" : "ul", info?.type === "ordered" && info.start !== void 0 ? { start: String(info.start) } : {}, items.map((item) => {
|
|
76
|
+
const paragraph = item.node;
|
|
77
|
+
const anchorNodes = writeRunsToNodes(paragraph.runs, paragraph.constructs);
|
|
78
|
+
const nested = writeSectionChildren(item.children, context);
|
|
79
|
+
return element("li", {}, [...anchorNodes, ...nested]);
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
function writeSectionConstructGroup(group, context) {
|
|
83
|
+
const descriptor = group.node;
|
|
84
|
+
if (descriptor.kind === "division") return [element("blockquote", {}, writeSectionChildren(group.children, context))];
|
|
85
|
+
if (descriptor.kind === "anchor" && descriptor.anchorType === "footnote") return [element("aside", {
|
|
86
|
+
"epub:type": "footnote",
|
|
87
|
+
id: descriptor.name
|
|
88
|
+
}, writeSectionChildren(group.children, context))];
|
|
89
|
+
context.sink({
|
|
90
|
+
code: require_diagnostics.EpubDiagnosticCodes.CONSTRUCT_UNREPRESENTED,
|
|
91
|
+
severity: "info",
|
|
92
|
+
message: `a '${descriptor.kind}' construct has no XHTML spelling in this package's writer; its extent is written, the construct itself is not`,
|
|
93
|
+
href: context.sourceHref
|
|
94
|
+
});
|
|
95
|
+
return writeSectionChildren(group.children, context);
|
|
96
|
+
}
|
|
97
|
+
function writeLeafBlock(block, context) {
|
|
98
|
+
switch (block.kind) {
|
|
99
|
+
case "paragraph": return [writeParagraph(block)];
|
|
100
|
+
case "table": return [writeTable(block, context)];
|
|
101
|
+
case "image": return [writeImage(block, context)];
|
|
102
|
+
case "pageBreak": return [];
|
|
103
|
+
case "embeddedObject":
|
|
104
|
+
context.sink({
|
|
105
|
+
code: require_diagnostics.EpubDiagnosticCodes.ELEMENT_UNMAPPED,
|
|
106
|
+
severity: "warning",
|
|
107
|
+
message: "an embedded object has no XHTML representation in this package's writer and was dropped",
|
|
108
|
+
href: context.sourceHref
|
|
109
|
+
});
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function isPreBlockParagraph(paragraph) {
|
|
114
|
+
if (paragraph.codeLanguage !== void 0) return true;
|
|
115
|
+
return paragraph.runs.length === 1 && paragraph.runs[0]?.fontFamily === "Courier New" && paragraph.runs[0].text.includes("\n");
|
|
116
|
+
}
|
|
117
|
+
function writeParagraph(paragraph) {
|
|
118
|
+
if (paragraph.styleId === "HorizontalRule" && paragraph.runs.length === 0) return element("hr");
|
|
119
|
+
if (isPreBlockParagraph(paragraph)) return element("pre", {}, [element("code", paragraph.codeLanguage === void 0 ? {} : { class: `language-${paragraph.codeLanguage}` }, [text(paragraph.runs.map((run) => run.text).join(""))])]);
|
|
120
|
+
return element("p", {}, writeRunsToNodes(paragraph.runs, paragraph.constructs));
|
|
121
|
+
}
|
|
122
|
+
function isFootnoteExtent(construct) {
|
|
123
|
+
return construct.descriptor.kind === "anchor" && construct.descriptor.anchorType === "footnote";
|
|
124
|
+
}
|
|
125
|
+
function writeRunsToNodes(runs, constructs) {
|
|
126
|
+
const footnoteExtents = (constructs ?? []).filter(isFootnoteExtent);
|
|
127
|
+
const out = [];
|
|
128
|
+
let index = 0;
|
|
129
|
+
while (index < runs.length) {
|
|
130
|
+
const extent = footnoteExtents.find((e) => e.startRun === index);
|
|
131
|
+
if (extent !== void 0) {
|
|
132
|
+
const inner = runs.slice(extent.startRun, extent.endRun).flatMap((run) => writeRunNodes(run));
|
|
133
|
+
out.push(element("a", {
|
|
134
|
+
"epub:type": "noteref",
|
|
135
|
+
href: `#${extent.descriptor.name}`
|
|
136
|
+
}, inner));
|
|
137
|
+
index = Math.max(extent.endRun, index + 1);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const run = runs[index];
|
|
141
|
+
if (run !== void 0) out.push(...writeRunNodes(run));
|
|
142
|
+
index += 1;
|
|
143
|
+
}
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
146
|
+
function writeRunNodes(run) {
|
|
147
|
+
const segments = run.text.split("\n");
|
|
148
|
+
let nodes = [];
|
|
149
|
+
segments.forEach((segment, i) => {
|
|
150
|
+
if (i > 0) nodes.push(element("br"));
|
|
151
|
+
if (segment.length > 0) nodes.push(text(segment));
|
|
152
|
+
});
|
|
153
|
+
if (nodes.length === 0) nodes = [text("")];
|
|
154
|
+
if (run.fontFamily === "Courier New") nodes = [element("code", {}, nodes)];
|
|
155
|
+
if (run.bold === true) nodes = [element("strong", {}, nodes)];
|
|
156
|
+
if (run.italic === true) nodes = [element("em", {}, nodes)];
|
|
157
|
+
if (run.underline === true) nodes = [element("u", {}, nodes)];
|
|
158
|
+
if (run.strike === true) nodes = [element("s", {}, nodes)];
|
|
159
|
+
if (run.hyperlink !== void 0) nodes = [element("a", { href: run.hyperlink }, nodes)];
|
|
160
|
+
return nodes;
|
|
161
|
+
}
|
|
162
|
+
function writeTable(table, context) {
|
|
163
|
+
return element("table", {}, table.rows.map((row) => element("tr", {}, row.cells.map((cell) => {
|
|
164
|
+
const attrs = {};
|
|
165
|
+
if (cell.colSpan !== void 0) attrs.colspan = String(cell.colSpan);
|
|
166
|
+
if (cell.rowSpan !== void 0) attrs.rowspan = String(cell.rowSpan);
|
|
167
|
+
for (const block of cell.blocks) if (!(0, document_schema_js.isTreeBlockLeaf)(block)) context.sink({
|
|
168
|
+
code: require_diagnostics.EpubDiagnosticCodes.ELEMENT_UNMAPPED,
|
|
169
|
+
severity: "info",
|
|
170
|
+
message: "a construct-boundary marker inside a table cell has no XHTML representation in this package's writer and was dropped",
|
|
171
|
+
href: context.sourceHref
|
|
172
|
+
});
|
|
173
|
+
return element("td", attrs, cell.blocks.filter(document_schema_js.isTreeBlockLeaf).flatMap((block) => writeLeafBlock(block, context)));
|
|
174
|
+
}))));
|
|
175
|
+
}
|
|
176
|
+
function writeImage(image, context) {
|
|
177
|
+
const bytes = require_util_base64.base64ToBytes(image.base64);
|
|
178
|
+
const attrs = { src: context.registerImage(bytes, image.format) };
|
|
179
|
+
attrs.alt = image.altText ?? "";
|
|
180
|
+
return element("img", attrs);
|
|
181
|
+
}
|
|
182
|
+
//#endregion
|
|
183
|
+
exports.writeXhtmlBody = writeXhtmlBody;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { i as EpubDiagnosticSink } from "../diagnostics-Ee4kG7mw.cjs";
|
|
2
|
+
import { XmlElement } from "../xml/node.cjs";
|
|
3
|
+
import { ContentBlock } from "document-schema.js";
|
|
4
|
+
//#region src/xhtml/write.d.ts
|
|
5
|
+
interface XhtmlWriteContext {
|
|
6
|
+
readonly registerImage: (bytes: Uint8Array<ArrayBuffer>, format: "png" | "jpeg") => string;
|
|
7
|
+
readonly sink: EpubDiagnosticSink;
|
|
8
|
+
readonly sourceHref: string;
|
|
9
|
+
}
|
|
10
|
+
declare function writeXhtmlBody(blocks: readonly ContentBlock[], context: XhtmlWriteContext): XmlElement;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { XhtmlWriteContext, writeXhtmlBody };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { i as EpubDiagnosticSink } from "../diagnostics-Ee4kG7mw.js";
|
|
2
|
+
import { XmlElement } from "../xml/node.js";
|
|
3
|
+
import { ContentBlock } from "document-schema.js";
|
|
4
|
+
//#region src/xhtml/write.d.ts
|
|
5
|
+
interface XhtmlWriteContext {
|
|
6
|
+
readonly registerImage: (bytes: Uint8Array<ArrayBuffer>, format: "png" | "jpeg") => string;
|
|
7
|
+
readonly sink: EpubDiagnosticSink;
|
|
8
|
+
readonly sourceHref: string;
|
|
9
|
+
}
|
|
10
|
+
declare function writeXhtmlBody(blocks: readonly ContentBlock[], context: XhtmlWriteContext): XmlElement;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { XhtmlWriteContext, writeXhtmlBody };
|