epub-codec 1.2.2 → 1.2.4
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 +3 -2
- package/dist/xhtml/read.cjs +22 -38
- package/dist/xhtml/read.js +22 -38
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -132,8 +132,9 @@ Every construct this package's XHTML mapping cannot represent losslessly is a do
|
|
|
132
132
|
- **`epub/style-residue`** — a document's own `<head>` style declarations (`<link rel="stylesheet">`, `<style>`) are quarantined verbatim as `SourceResidue` on the owning `ContentSection`, never interpreted: CSS is residue, not content. `writeEpubContent` re-emits it into the written `<head>` on a same-format write (this family's standard restorable-fidelity re-emission contract).
|
|
133
133
|
- **An empty or whitespace-only paragraph (`<p></p>`, `<p> </p>`, or bare whitespace text between two block-level siblings — the common case in any pretty-printed real EPUB) is dropped entirely on read**, matching the same "anonymous block box" rule a browser's own HTML block-formatting context already applies to inter-block whitespace, rather than becoming a bogus empty `ContentParagraph`.
|
|
134
134
|
- **List marker type (bullet vs. ordered, and an `<ol>`'s own non-default `start`) is packed into the opaque `numId`** (`epub{N}:{bullet|ordered}[@{start}]`, `src/xhtml/list-id.ts`), since `ContentListMembership` carries no field of its own for it — the identical mechanism `markdown-codec`'s own `src/shared/list-id.ts` uses for its GFM bullet/ordered distinction, hand-mirrored rather than shared. A numId outside this grammar (odf.js's bare `"list1"`, markdown-codec's own `"md1:bullet"`) is read back as an ordinary bullet list with no declared start.
|
|
135
|
-
- **A container's own direct-child `<img>`** (some producers/editors wrap every floating image in a paragraph tag rather than a `<figure>`) is split at the image by `readContainerChildren`: the phrasing content before and after becomes its own paragraph (dropped entirely when empty), the image its own block, in source order. That split fires for every container this package reads transparently through `readContainerChildren` — `<body>` itself, `<p>`, `<li>`, `<blockquote>`, `<figure>`, `<div>`, `<section>`, `<article>`, `<aside>`, and `<nav>` — never only a named handful of them; an `<img>` reached anywhere else — nested inside a `<span>`/`<a>` at any depth, or a _direct_ child of a heading
|
|
136
|
-
- **A run-level construct extent (most commonly a footnote reference) carried by inline content built directly into a paragraph is preserved on that paragraph's own `constructs` field wherever such a paragraph is built** — a heading
|
|
135
|
+
- **A container's own direct-child `<img>`** (some producers/editors wrap every floating image in a paragraph tag rather than a `<figure>`) is split at the image by `readContainerChildren`: the phrasing content before and after becomes its own paragraph (dropped entirely when empty), the image its own block, in source order. That split fires for every container this package reads transparently through `readContainerChildren` — `<body>` itself, `<p>`, `<li>`, `<blockquote>`, `<figure>`, `<div>`, `<section>`, `<article>`, `<aside>`, and `<nav>` — never only a named handful of them; an `<img>` reached anywhere else — nested inside a `<span>`/`<a>` at any depth, or a _direct_ child of a heading (the one container this package still reads via a single `buildInlineRuns` call rather than `readContainerChildren`) — is instead reached by `src/xhtml/inline.ts`'s own run-building recursion, which by that point has committed to producing a flat run sequence with no block list left to insert a sibling image block into, and degrades to its alt text (or nothing, when it carries none) with an `epub/image-inline-unsupported` diagnostic rather than silently vanishing. A `<caption>`, `<dt>`/`<dd>`, `<figcaption>`, and a table cell all route through `readContainerChildren` too ([ExaDev/documents.js#1023](https://github.com/ExaDev/documents.js/issues/1023) — see the dedicated bullet below), so a direct-child `<img>` in any of them now becomes a real `ContentImageBlock` the same way, rather than degrading. [ExaDev/documents.js#994](https://github.com/ExaDev/documents.js/issues/994) closed the four remaining silent gaps this guarantee did not originally cover: a non-empty `<caption>`, a legal direct child of `<table>`, is now read as one or more ordinary blocks immediately before the table (`epub/table-caption-unsupported`, fired once per `<caption>` element regardless of how many blocks it decomposes into, since `ContentTable` has no field of its own for a caption's distinct tag; a run-level construct the caption's own inline content carries — a footnote reference, most commonly — rides its own paragraph's `constructs` field exactly like any other paragraph's), while an empty or whitespace-only `<caption>` is dropped entirely instead, with no diagnostic, matching this package's own empty-paragraph-drop rule documented above; a `<dl>` wrapping one or more `dt`/`dd` pairs in a `<div>` (legal HTML5, used for a per-entry styling hook) is now recognised by recursing into the `<div>`, with no diagnostic at all, since the wrapper carries no properties of its own to lose (identical to every other `<div>` this package already reads transparently); and any content — a nested `<ul>`/`<ol>`, a bare `<img>`, stray text — sitting directly inside a `<ul>`/`<ol>` rather than inside an `<li>` (not valid HTML5, but a shape real-world converters do emit) is now recovered via `epub/list-content-outside-item`, through the same `readContainerChildren` dispatch an `<li>`'s real children already use: content sitting between or after real `<li>` siblings attaches to the preceding item's own nesting — a stray list shares its numId and increments its level, exactly as if it had been nested correctly — while content sitting before the very first `<li>` has no preceding item to attach to and is instead recovered inheriting whatever list membership its own enclosing context already carries (none, unless the `<ul>`/`<ol>` it sits directly inside is itself nested inside another list's `<li>`), landing in the read result immediately before the list's own real items — matching a browser's rendering order for this shape, though not necessarily its nesting depth when the enclosing list is itself nested. One part of the original finding remains a genuine, permanent structural limit rather than a recovered gap: an `<img>` inside a `<pre>`/`<code>` block still cannot become a real `ContentImageBlock` — this package always reads a `<pre>` as a single text-content paragraph, so there is no block list to insert an image block into, the same constraint `epub/image-inline-unsupported` already names elsewhere (the HTML Standard's own content model for `<pre>` is phrasing content, not plain text, which is exactly why `<a>`/`<code>`/`<span>`/`<img>`/`<br>` are legal, real markup inside one and this package's `readPreRuns`/`readPreText` have to handle each explicitly) — but it no longer vanishes silently either; its alt text (or nothing, when it carries none) is spliced into the extracted text in its place, with an `epub/image-pre-unsupported` diagnostic firing unconditionally — every image inside a `<pre>`, not only one with no alt text.
|
|
136
|
+
- **A run-level construct extent (most commonly a footnote reference) carried by inline content built directly into a paragraph is preserved on that paragraph's own `constructs` field wherever such a paragraph is built** — a heading and `readContainerChildren`'s own segment flush both share `constructsField`, the one helper `src/xhtml/read.ts` funnels every such paragraph through, so a fix applied to one cannot silently miss the other. A table caption, a table cell, a `<dt>`/`<dd>`, and a `<figcaption>` get this for free by routing through `readContainerChildren` ([ExaDev/documents.js#1023](https://github.com/ExaDev/documents.js/issues/1023)) rather than needing their own direct call to the helper. A `<pre>`/`<code>` block's own paragraph shares it too, and round-trips it in both directions, despite never routing through `buildInlineRuns` (a `<pre>`'s content model needs its text preserved verbatim, which `buildInlineRuns`'s own whitespace normalisation would break): on read, `readPreRuns`, a dedicated run-splitting walk reached only once a cheap subtree pre-check confirms the `<pre>` actually carries a recognised footnote reference somewhere inside it, brackets that reference's own text as its own run range exactly like `src/xhtml/inline.ts`'s `appendAnchor` does, rebasing any nested call's own construct indices onto the outer run sequence at the point of the merge; the common case (no footnote reference anywhere in the block) still takes the cheap, single-run `readPreText` path unchanged. Because a construct nested inside a `<pre>` can split its content into more than one run with no bearing on whether the block is itself preformatted, `readPre` also stamps every paragraph it produces with `document-schema.js`'s own `ContentParagraph.preformatted` flag, unconditionally and regardless of run count — the one signal the writer can trust, since inferring "this was a `<pre>`" from run shape (a lone monospace run, say) silently misclassifies a multi-run one as an ordinary paragraph the moment it carries a construct. On write, `isPreBlockParagraph` checks `preformatted` first (falling back to `codeLanguage` or the legacy single-monospace-run-with-a-newline heuristic for a foreign producer's document that sets neither), and a recognised `<pre>` paragraph's own runs and constructs are written through `writePreRunsToNodes` — the `<pre>` twin of the ordinary paragraph writer, sharing its extent-finding walk but writing a run's embedded newline as a literal `"\n"` character rather than splitting it into a `<br/>` the way the ordinary path does; `readPreRuns`/`readPreText` map a `<br>` read back from a `<pre>` straight to the same literal `"\n"`, so either spelling round-trips to identical text. A stray element sitting where only a narrower set of tags is expected is likewise recovered rather than dropped, following the same `readContainerChildren`-plus-diagnostic pattern `epub/list-content-outside-item` already established: a `<dl>` (or one of its `<div>` wrappers) carrying anything other than `dt`/`dd`/`<div>` — a stray `<p>`, stray text, a stray `<img>`, or a non-conformant wrapper like `<section>` used in `<div>`'s own place — is recovered via `epub/definition-list-content-outside-entry` (a non-conformant wrapper's own `dt`/`dd` children lose their distinct term/definition treatment once routed this way, degrading to plain concatenated text — a real fidelity cost, but a text-preserving one). The same treatment applies to a `<table>`: content sitting outside any row, caption, or `<colgroup>` — whether directly inside the `<table>` itself, inside one of its `<thead>`/`<tbody>`/`<tfoot>` row groups (which admit only `tr` and script-supporting children per the HTML Standard), or inside a `<colgroup>` itself (which admits only `<col>` and `<template>` children per the HTML Standard — narrower than the script-supporting category, since a `<colgroup>` does not admit a bare `<script>`) — is recovered immediately before the table via `epub/table-content-unrecognized`; a `<tr>` carrying content outside any `<td>`/`<th>` is recovered as its own cell in the row's own column sequence via `epub/table-row-content-outside-cell`; and a `<table>` carrying more than one `<caption>` (HTML5 permits at most one) has every caption beyond the first read as its own paragraph too, via `epub/table-duplicate-caption`, rather than `findChildElement`'s own first-match-only resolution silently discarding it as it previously did. `<script>`, `<template>`, `<style>`, and `<noscript>` appearing anywhere in `<body>` content are all now skipped by one shared guard (`isInertElement`, `src/xhtml/context.ts`) rather than leaking their own raw content as document prose — none of the four were inert outside `<head>`-level residue quarantine before this fix. `<script>`'s raw JS and `<template>`'s inert DOM subtree are never real content regardless of where they are found, and a body-level `<style>` is CSS, exactly like the `<head>`-level style residue this package already quarantines rather than interprets — none of the three fire a diagnostic, since none of them ever carry anything document-schema.js's vocabulary could represent. `<noscript>` is treated the same conservative way for a different reason: its own children ARE ordinary markup a scripting-disabled reading system would genuinely render, but this package cannot tell that case apart from a producer's own "please enable JavaScript" placeholder from the markup alone — so, unlike the other three, dropping a `<noscript>`'s subtree fires its own `epub/noscript-content-skipped` diagnostic (`reportInertElementSkip`, `src/xhtml/context.ts`) at every site that discards one, naming the potential loss rather than staying silent about it.
|
|
137
|
+
- **A `<caption>`, `<dt>`, `<dd>`, `<figcaption>`, `<td>`, or `<th>` now recognises real block-level content rather than flattening it** ([ExaDev/documents.js#1023](https://github.com/ExaDev/documents.js/issues/1023)) — all six are Flow content per the HTML Standard, so a `<pre>`, a nested list, or more than one paragraph inside one is real, conformant markup. These six used to build their content with one bare `buildInlineRuns` call each; a block element reached that way has no case in `buildInlineRuns`'s own dispatch, so it fell into the same treatment as a `<span>`, recursing into its children as ordinary phrasing content — losing a `<pre>`'s own block shape, `preformatted` flag, `codeLanguage`, and verbatim whitespace, and, separately, concatenating more than one paragraph-shaped child into one undelimited run with no break (or even a word boundary) between them: `<td><p>Alpha</p><p>Beta</p></td>` used to read as the single run `"AlphaBeta"`. All six now route through `readContainerChildren` instead, the same general block-container reader `<p>`/`<li>`/`<blockquote>`/`<figure>`/`<div>`/`<section>`/`<article>`/`<aside>`/`<nav>` already use, so a `<pre>` stays preformatted, a nested list stays real list structure, and sibling paragraphs stay distinct. `<dd>`'s own `DEFINITION_BODY_INDENT_PT` offset is threaded through a `BuildState.extraIndentPt` field (additive with blockquote nesting's own `quoteDepth`, mirrored via the identical `withQuote`/`withExtraIndent` pattern) so every paragraph the descent produces picks up the indent, not only a single top-level one; a `<th>`'s own implied bold is threaded through as `readContainerChildren`'s own `baseStyle` parameter, reaching every direct phrasing segment but not content nested inside a further block-level child (a `<table>`-in-a-`<th>`, say) — a narrow, documented degrade for a genuinely rare shape, not a silent one.
|
|
137
138
|
- **A CDATA section (`<![CDATA[...]]>`) is read exactly like an ordinary text node everywhere in the XHTML reading path** — `xml/node.ts`'s `isTextLikeNode` is the one shared predicate every text-bearing walk in this package now dispatches on (`buildInlineRuns`, `readContainerChildren`'s own phrasing/block split, `readPre`'s three text-extraction paths, every stray-content collector in `read.ts`, and `xml/query.ts`'s own `decodedTextContent`), rather than each walk separately checking for `"text"` and quietly excluding `"cdata"`. A CDATA section is simply the alternate XML spelling a producer reaches for when its own literal text would otherwise need escaping (a code sample, or any other content containing a raw `<`/`&`) — exactly the kind of content a DocBook-to-EPUB pipeline or similar tool emits inside a `<pre>` — never a distinct kind of content, so treating the two differently was silently dropping real, well-formed input rather than a corpus-tolerance gap. The one place CDATA is genuinely _not_ interchangeable with a text node is entity decoding: `xml/entities.ts`'s `decodeTextLikeNode` decodes a text node's raw value exactly as before but returns a CDATA node's value untouched, since CDATA content is never subject to XML entity resolution in the first place — running it back through `decodeEntities` would corrupt exactly the unescaped content CDATA exists to carry. `xml/query.ts`'s own plain `textContent` is deliberately narrower and CDATA-blind: it is this package's long-standing published export, and a caller's own `decodeEntities(textContent(x))` idiom stays correct only because that walk never mixes in undecoded-by-design CDATA content for the wrap to misapply entity resolution to — the CDATA-aware, decode-internally behaviour lands as the distinctly named `decodedTextContent` instead, used by `opf/metadata.ts`'s own four Dublin Core readers, so upgrading to CDATA support never silently double-decodes an existing caller's text nodes.
|
|
138
139
|
- **A blockquote containing a heading cannot carry its `division` construct** — a marker extent may not open or close a heading scope, and the last heading inside an extent always leaves one standing (`document-schema.js`'s own constraint) — so it degrades to indent-only structure (still `indentLeftPt`/`styleId: "Quote"`) while the heading keeps its own heading fidelity.
|
|
139
140
|
- **Image dimensions are derived from pixel size via the CSS reference-pixel ratio (1px = 1/96in)**, not read from any `<img width>`/`<img height>` attribute or CSS: an EPUB's own XHTML/CSS carries no reliable point-based sizing of its own, so the image's natural pixel size is the one dimension every manifest image reliably has.
|
package/dist/xhtml/read.cjs
CHANGED
|
@@ -34,6 +34,12 @@ function withQuote(state) {
|
|
|
34
34
|
quoteDepth: state.quoteDepth + 1
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
+
function withExtraIndent(state, pt) {
|
|
38
|
+
return {
|
|
39
|
+
...state,
|
|
40
|
+
extraIndentPt: state.extraIndentPt + pt
|
|
41
|
+
};
|
|
42
|
+
}
|
|
37
43
|
function withListItem(state, listItem) {
|
|
38
44
|
return {
|
|
39
45
|
...state,
|
|
@@ -48,9 +54,13 @@ function decorateParagraph(paragraph, state) {
|
|
|
48
54
|
let decorated = paragraph;
|
|
49
55
|
if (state.quoteDepth > 0) decorated = {
|
|
50
56
|
...decorated,
|
|
51
|
-
indentLeftPt: state.quoteDepth * 36,
|
|
57
|
+
indentLeftPt: state.quoteDepth * 36 + state.extraIndentPt,
|
|
52
58
|
styleId: decorated.styleId ?? "Quote"
|
|
53
59
|
};
|
|
60
|
+
else if (state.extraIndentPt > 0) decorated = {
|
|
61
|
+
...decorated,
|
|
62
|
+
indentLeftPt: state.extraIndentPt
|
|
63
|
+
};
|
|
54
64
|
if (state.listItem !== void 0) {
|
|
55
65
|
const membership = {
|
|
56
66
|
numId: state.listItem.numId,
|
|
@@ -132,6 +142,7 @@ function readXhtmlBody(xml, options) {
|
|
|
132
142
|
context,
|
|
133
143
|
minter: createIdMinter(),
|
|
134
144
|
quoteDepth: 0,
|
|
145
|
+
extraIndentPt: 0,
|
|
135
146
|
listItem: void 0,
|
|
136
147
|
list: void 0,
|
|
137
148
|
contentWidthPt: options.contentWidthPt
|
|
@@ -165,12 +176,12 @@ const BLOCK_LEVEL_TAGS = /* @__PURE__ */ new Set([
|
|
|
165
176
|
"nav",
|
|
166
177
|
"img"
|
|
167
178
|
]);
|
|
168
|
-
function readContainerChildren(nodes, state) {
|
|
179
|
+
function readContainerChildren(nodes, state, baseStyle = {}) {
|
|
169
180
|
const blocks = [];
|
|
170
181
|
let segment = [];
|
|
171
182
|
const flush = () => {
|
|
172
183
|
if (segment.length === 0) return;
|
|
173
|
-
const inline = require_xhtml_inline.buildInlineRuns(segment,
|
|
184
|
+
const inline = require_xhtml_inline.buildInlineRuns(segment, baseStyle, state.context);
|
|
174
185
|
segment = [];
|
|
175
186
|
if (inline.runs.every((run) => run.text.trim().length === 0) && inline.constructs.length === 0) return;
|
|
176
187
|
const paragraph = {
|
|
@@ -238,14 +249,7 @@ function readBlockElementInner(element, state) {
|
|
|
238
249
|
case "dl": return readDefinitionList(element, state);
|
|
239
250
|
case "table": return readTable(element, state);
|
|
240
251
|
case "figure": return readContainerChildren(element.children, state);
|
|
241
|
-
case "figcaption":
|
|
242
|
-
const inline = require_xhtml_inline.buildInlineRuns(element.children, {}, state.context);
|
|
243
|
-
return [decorateParagraph({
|
|
244
|
-
kind: "paragraph",
|
|
245
|
-
runs: inline.runs,
|
|
246
|
-
...constructsField(inline)
|
|
247
|
-
}, state)];
|
|
248
|
-
}
|
|
252
|
+
case "figcaption": return readContainerChildren(element.children, state);
|
|
249
253
|
case "img": {
|
|
250
254
|
const block = readImage(element, state);
|
|
251
255
|
return block === void 0 ? [] : [block];
|
|
@@ -450,23 +454,12 @@ function readDefinitionListEntries(nodes, state) {
|
|
|
450
454
|
for (const child of nodes) {
|
|
451
455
|
if (child.type === "element" && child.tag === "dt") {
|
|
452
456
|
flushStray();
|
|
453
|
-
|
|
454
|
-
blocks.push(decorateParagraph({
|
|
455
|
-
kind: "paragraph",
|
|
456
|
-
runs: inline.runs,
|
|
457
|
-
...constructsField(inline)
|
|
458
|
-
}, state));
|
|
457
|
+
blocks.push(...readContainerChildren(child.children, state));
|
|
459
458
|
continue;
|
|
460
459
|
}
|
|
461
460
|
if (child.type === "element" && child.tag === "dd") {
|
|
462
461
|
flushStray();
|
|
463
|
-
|
|
464
|
-
blocks.push(decorateParagraph({
|
|
465
|
-
kind: "paragraph",
|
|
466
|
-
runs: inline.runs,
|
|
467
|
-
...constructsField(inline),
|
|
468
|
-
indentLeftPt: 36 + state.quoteDepth * 36
|
|
469
|
-
}, state));
|
|
462
|
+
blocks.push(...readContainerChildren(child.children, withExtraIndent(state, 36)));
|
|
470
463
|
continue;
|
|
471
464
|
}
|
|
472
465
|
if (child.type === "element" && child.tag === "div") {
|
|
@@ -539,16 +532,11 @@ function readTable(element, state) {
|
|
|
539
532
|
if (cellNode.type === "element" && (cellNode.tag === "td" || cellNode.tag === "th")) {
|
|
540
533
|
flushStrayCell();
|
|
541
534
|
const cellStyle = cellNode.tag === "th" ? { bold: true } : {};
|
|
542
|
-
const
|
|
543
|
-
const paragraph = {
|
|
544
|
-
kind: "paragraph",
|
|
545
|
-
runs: inline.runs,
|
|
546
|
-
...constructsField(inline)
|
|
547
|
-
};
|
|
535
|
+
const cellBlocks = readContainerChildren(cellNode.children, state, cellStyle);
|
|
548
536
|
const colSpan = positiveIntAttr(cellNode, "colspan");
|
|
549
537
|
const rowSpan = positiveIntAttr(cellNode, "rowspan");
|
|
550
538
|
cells.push({
|
|
551
|
-
blocks:
|
|
539
|
+
blocks: cellBlocks,
|
|
552
540
|
...colSpan !== void 0 ? { colSpan } : {},
|
|
553
541
|
...rowSpan !== void 0 ? { rowSpan } : {}
|
|
554
542
|
});
|
|
@@ -611,8 +599,8 @@ function collectColgroupStrayContent(section, strayNodes, context) {
|
|
|
611
599
|
}
|
|
612
600
|
}
|
|
613
601
|
function readTableCaption(captionElement, isDuplicate, state) {
|
|
614
|
-
const
|
|
615
|
-
if (
|
|
602
|
+
const captionBlocks = readContainerChildren(captionElement.children, state);
|
|
603
|
+
if (captionBlocks.length === 0) return [];
|
|
616
604
|
if (isDuplicate) state.context.sink({
|
|
617
605
|
code: require_diagnostics.EpubDiagnosticCodes.TABLE_DUPLICATE_CAPTION,
|
|
618
606
|
severity: "info",
|
|
@@ -625,11 +613,7 @@ function readTableCaption(captionElement, isDuplicate, state) {
|
|
|
625
613
|
message: "<caption> has no document-schema.js table-caption field to carry its own distinct tag; read as an ordinary paragraph immediately before the table",
|
|
626
614
|
href: state.context.sourceHref
|
|
627
615
|
});
|
|
628
|
-
return
|
|
629
|
-
kind: "paragraph",
|
|
630
|
-
runs: captionInline.runs,
|
|
631
|
-
...constructsField(captionInline)
|
|
632
|
-
}, state)];
|
|
616
|
+
return captionBlocks;
|
|
633
617
|
}
|
|
634
618
|
function positiveIntAttr(element, name) {
|
|
635
619
|
const raw = require_xml_query.attrValue(element, name);
|
package/dist/xhtml/read.js
CHANGED
|
@@ -33,6 +33,12 @@ function withQuote(state) {
|
|
|
33
33
|
quoteDepth: state.quoteDepth + 1
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
+
function withExtraIndent(state, pt) {
|
|
37
|
+
return {
|
|
38
|
+
...state,
|
|
39
|
+
extraIndentPt: state.extraIndentPt + pt
|
|
40
|
+
};
|
|
41
|
+
}
|
|
36
42
|
function withListItem(state, listItem) {
|
|
37
43
|
return {
|
|
38
44
|
...state,
|
|
@@ -47,9 +53,13 @@ function decorateParagraph(paragraph, state) {
|
|
|
47
53
|
let decorated = paragraph;
|
|
48
54
|
if (state.quoteDepth > 0) decorated = {
|
|
49
55
|
...decorated,
|
|
50
|
-
indentLeftPt: state.quoteDepth * 36,
|
|
56
|
+
indentLeftPt: state.quoteDepth * 36 + state.extraIndentPt,
|
|
51
57
|
styleId: decorated.styleId ?? "Quote"
|
|
52
58
|
};
|
|
59
|
+
else if (state.extraIndentPt > 0) decorated = {
|
|
60
|
+
...decorated,
|
|
61
|
+
indentLeftPt: state.extraIndentPt
|
|
62
|
+
};
|
|
53
63
|
if (state.listItem !== void 0) {
|
|
54
64
|
const membership = {
|
|
55
65
|
numId: state.listItem.numId,
|
|
@@ -131,6 +141,7 @@ function readXhtmlBody(xml, options) {
|
|
|
131
141
|
context,
|
|
132
142
|
minter: createIdMinter(),
|
|
133
143
|
quoteDepth: 0,
|
|
144
|
+
extraIndentPt: 0,
|
|
134
145
|
listItem: void 0,
|
|
135
146
|
list: void 0,
|
|
136
147
|
contentWidthPt: options.contentWidthPt
|
|
@@ -164,12 +175,12 @@ const BLOCK_LEVEL_TAGS = /* @__PURE__ */ new Set([
|
|
|
164
175
|
"nav",
|
|
165
176
|
"img"
|
|
166
177
|
]);
|
|
167
|
-
function readContainerChildren(nodes, state) {
|
|
178
|
+
function readContainerChildren(nodes, state, baseStyle = {}) {
|
|
168
179
|
const blocks = [];
|
|
169
180
|
let segment = [];
|
|
170
181
|
const flush = () => {
|
|
171
182
|
if (segment.length === 0) return;
|
|
172
|
-
const inline = buildInlineRuns(segment,
|
|
183
|
+
const inline = buildInlineRuns(segment, baseStyle, state.context);
|
|
173
184
|
segment = [];
|
|
174
185
|
if (inline.runs.every((run) => run.text.trim().length === 0) && inline.constructs.length === 0) return;
|
|
175
186
|
const paragraph = {
|
|
@@ -237,14 +248,7 @@ function readBlockElementInner(element, state) {
|
|
|
237
248
|
case "dl": return readDefinitionList(element, state);
|
|
238
249
|
case "table": return readTable(element, state);
|
|
239
250
|
case "figure": return readContainerChildren(element.children, state);
|
|
240
|
-
case "figcaption":
|
|
241
|
-
const inline = buildInlineRuns(element.children, {}, state.context);
|
|
242
|
-
return [decorateParagraph({
|
|
243
|
-
kind: "paragraph",
|
|
244
|
-
runs: inline.runs,
|
|
245
|
-
...constructsField(inline)
|
|
246
|
-
}, state)];
|
|
247
|
-
}
|
|
251
|
+
case "figcaption": return readContainerChildren(element.children, state);
|
|
248
252
|
case "img": {
|
|
249
253
|
const block = readImage(element, state);
|
|
250
254
|
return block === void 0 ? [] : [block];
|
|
@@ -449,23 +453,12 @@ function readDefinitionListEntries(nodes, state) {
|
|
|
449
453
|
for (const child of nodes) {
|
|
450
454
|
if (child.type === "element" && child.tag === "dt") {
|
|
451
455
|
flushStray();
|
|
452
|
-
|
|
453
|
-
blocks.push(decorateParagraph({
|
|
454
|
-
kind: "paragraph",
|
|
455
|
-
runs: inline.runs,
|
|
456
|
-
...constructsField(inline)
|
|
457
|
-
}, state));
|
|
456
|
+
blocks.push(...readContainerChildren(child.children, state));
|
|
458
457
|
continue;
|
|
459
458
|
}
|
|
460
459
|
if (child.type === "element" && child.tag === "dd") {
|
|
461
460
|
flushStray();
|
|
462
|
-
|
|
463
|
-
blocks.push(decorateParagraph({
|
|
464
|
-
kind: "paragraph",
|
|
465
|
-
runs: inline.runs,
|
|
466
|
-
...constructsField(inline),
|
|
467
|
-
indentLeftPt: 36 + state.quoteDepth * 36
|
|
468
|
-
}, state));
|
|
461
|
+
blocks.push(...readContainerChildren(child.children, withExtraIndent(state, 36)));
|
|
469
462
|
continue;
|
|
470
463
|
}
|
|
471
464
|
if (child.type === "element" && child.tag === "div") {
|
|
@@ -538,16 +531,11 @@ function readTable(element, state) {
|
|
|
538
531
|
if (cellNode.type === "element" && (cellNode.tag === "td" || cellNode.tag === "th")) {
|
|
539
532
|
flushStrayCell();
|
|
540
533
|
const cellStyle = cellNode.tag === "th" ? { bold: true } : {};
|
|
541
|
-
const
|
|
542
|
-
const paragraph = {
|
|
543
|
-
kind: "paragraph",
|
|
544
|
-
runs: inline.runs,
|
|
545
|
-
...constructsField(inline)
|
|
546
|
-
};
|
|
534
|
+
const cellBlocks = readContainerChildren(cellNode.children, state, cellStyle);
|
|
547
535
|
const colSpan = positiveIntAttr(cellNode, "colspan");
|
|
548
536
|
const rowSpan = positiveIntAttr(cellNode, "rowspan");
|
|
549
537
|
cells.push({
|
|
550
|
-
blocks:
|
|
538
|
+
blocks: cellBlocks,
|
|
551
539
|
...colSpan !== void 0 ? { colSpan } : {},
|
|
552
540
|
...rowSpan !== void 0 ? { rowSpan } : {}
|
|
553
541
|
});
|
|
@@ -610,8 +598,8 @@ function collectColgroupStrayContent(section, strayNodes, context) {
|
|
|
610
598
|
}
|
|
611
599
|
}
|
|
612
600
|
function readTableCaption(captionElement, isDuplicate, state) {
|
|
613
|
-
const
|
|
614
|
-
if (
|
|
601
|
+
const captionBlocks = readContainerChildren(captionElement.children, state);
|
|
602
|
+
if (captionBlocks.length === 0) return [];
|
|
615
603
|
if (isDuplicate) state.context.sink({
|
|
616
604
|
code: EpubDiagnosticCodes.TABLE_DUPLICATE_CAPTION,
|
|
617
605
|
severity: "info",
|
|
@@ -624,11 +612,7 @@ function readTableCaption(captionElement, isDuplicate, state) {
|
|
|
624
612
|
message: "<caption> has no document-schema.js table-caption field to carry its own distinct tag; read as an ordinary paragraph immediately before the table",
|
|
625
613
|
href: state.context.sourceHref
|
|
626
614
|
});
|
|
627
|
-
return
|
|
628
|
-
kind: "paragraph",
|
|
629
|
-
runs: captionInline.runs,
|
|
630
|
-
...constructsField(captionInline)
|
|
631
|
-
}, state)];
|
|
615
|
+
return captionBlocks;
|
|
632
616
|
}
|
|
633
617
|
function positiveIntAttr(element, name) {
|
|
634
618
|
const raw = attrValue(element, name);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "epub-codec",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "EPUB 2/3 reading and deterministic EPUB 3 writing against the shared document-schema.js content pivot.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"license": "MIT",
|
|
79
79
|
"packageManager": "pnpm@11.6.0",
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"document-schema.js": "^6.2.
|
|
81
|
+
"document-schema.js": "^6.2.1",
|
|
82
82
|
"entities": "^8.0.0",
|
|
83
83
|
"fast-xml-parser": "^5.10.1",
|
|
84
84
|
"fflate": "^0.8.3",
|