js.documents 14.6.4 → 14.6.5
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 +1 -1
- package/dist/layout/reconstruct-lines.cjs +41 -11
- package/dist/layout/reconstruct-lines.js +42 -12
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -859,7 +859,7 @@ The package is layered from generic primitives outward to the two conversion dir
|
|
|
859
859
|
- **`src/markdown/`** — third adapter family, via `markdown-codec`. `readMarkdownContent` passes `readMarkdownContent`'s (markdown-codec's flat reader, so named since that package's 4.0.0; the bare `readMarkdown` name is now its tree-form `DocumentTree` reader) result through the math-lowering pass (`math.ts` — markdown-codec's preserved `$$` display blocks and `\( \)` inline spans become two-layer formula blocks, with the document's symbol table seeded from its own prose). `buildMarkdownText` wraps `writeMarkdownContent`, reconstructing markdown math syntax from formula blocks carrying a presentation layer. `text.ts` is the byte↔text boundary. `MarkdownEditor` holds a mutable in-memory `ContentDocument`.
|
|
860
860
|
- **`src/csv/`** — fourth adapter family, sharing the spreadsheet variant with xlsx/ods. `records.ts` is the RFC 4180 record parser/writer (one shared `quoteCsvField`, also used by the `.odb` CSV exporter); `text.ts` is the byte↔text boundary, working the character encoding out from the bytes through [byte-codec](../byte-codec/README.md#text-decoding) rather than assuming UTF-8, so Excel's own windows-1252 export reads, and refusing bytes that are not text at all; `read.ts` turns records into a spreadsheet `ContentDocument` (first record as verbatim string header, data cells through the same cell-typing heuristic `pdfToOds` uses); `write.ts` turns one sheet of a spreadsheet `ContentDocument` back into records via each cell's `displayText`. TSV is the same format with `{ delimiter: '\t' }` on either side.
|
|
861
861
|
- **`src/svg/`** — fifth adapter family, sharing the drawing variant with odg. `text.ts` is the byte↔text boundary: unlike csv and markdown, it does not detect the encoding from the bytes, because SVG is XML and XML's own `<?xml ... encoding="..."?>` declaration is authoritative; `xml-encoding.ts` reads that declaration (or a byte order mark) from the leading bytes per XML 1.0's own external-encoding-detection algorithm, ahead of any real decode, with UTF-8 the strict default when neither is present; `read.ts` maps the six SVG shape primitives (rect/circle/ellipse/line/polyline/polygon/path) onto a one-page drawing `ContentDocument`, with transform lists composed as 2×3 affines and CSS lengths and the viewBox map resolved into page points; `write.ts` writes the six primitives back out, one shape element each; `path.ts` is the full SVG path-data grammar (M/L/H/V/C/S/Z plus Q/T/A and the relative forms — S/Q/T convert exactly, A is the one bounded approximation at ≤90° per cubic); `transform.ts` parses and composes the transform attribute and classifies the result by frame representability; `units.ts` resolves CSS length units and the viewBox; `paint.ts` resolves fill/stroke presentation attributes and dash styles; `diagnostics.ts` is the shared scope-limit vocabulary.
|
|
862
|
-
- **`src/layout/`** — the pure conversion algorithms: `engine.ts` (wordprocessing → layout: flow, line-breaking, pagination), `slides.ts` (presentation → layout: direct placement), `sheets.ts` (spreadsheet → layout: grid, print settings, the first algorithm accepting `AbortSignal`), `drawing.ts` (drawing → layout: vector primitives + shape reuse), `reconstruct.ts` (layout → content: baseline clustering for wordprocessing/presentation, near-1:1 mapping for drawing, gridline-lattice-or-text-clustering for spreadsheet; plus the PDF construct surfacing — link reconciliation to `ContentRun.hyperlink` or link constructs, hidden-layer content dropped, anchor/comment and AcroForm contentControl constructs, and tagged-structure semantics where the file states them: heading levels from owning `H1`..`H6` elements over the font-size census, lattice-free table recovery from `/Table`/`/TR`/`/TH`/`/TD` ownership, and `division` constructs around `/Part`/`/Sect`/`/Div` extents).
|
|
862
|
+
- **`src/layout/`** — the pure conversion algorithms: `engine.ts` (wordprocessing → layout: flow, line-breaking, pagination), `slides.ts` (presentation → layout: direct placement), `sheets.ts` (spreadsheet → layout: grid, print settings, the first algorithm accepting `AbortSignal`), `drawing.ts` (drawing → layout: vector primitives + shape reuse), `reconstruct.ts` (layout → content: baseline clustering for wordprocessing/presentation, with a superscript or subscript returned to the line of the run it touches and marked with `ContentRun.verticalAlign` by pdf-codec's `scriptPositionOf`, so `pdfToMarkdown` writes `m<sup>2</sup>` rather than `m` and a stray `2`; near-1:1 mapping for drawing, gridline-lattice-or-text-clustering for spreadsheet; plus the PDF construct surfacing — link reconciliation to `ContentRun.hyperlink` or link constructs, hidden-layer content dropped, anchor/comment and AcroForm contentControl constructs, and tagged-structure semantics where the file states them: heading levels from owning `H1`..`H6` elements over the font-size census, lattice-free table recovery from `/Table`/`/TR`/`/TH`/`/TD` ownership, and `division` constructs around `/Part`/`/Sect`/`/Div` extents).
|
|
863
863
|
- **`src/hsqldb/`** — `.odb` decoders, four tiers: `script.ts` (TEXT-script DDL/DML parser), `rowformat.ts`/`cache.ts` (CACHED binary row-store), `binary-script.ts` (BINARY/COMPRESSED whole-script). All import only `document-schema.js` — no odf.js knowledge.
|
|
864
864
|
- **`src/firebird/`** — Tier 3: gbak logical-backup reader. `reader.ts` (attribute framing + RLE decompression + XDR decoding), `schema.ts`/`data.ts` (table/row walking). No ratified spec — built against Firebird's own engine source.
|
|
865
865
|
- **`src/odb/`** — decoder-selection and pivot-mapping: `read.ts` routes to the right tier, `spreadsheet.ts`/`csv.ts` map to output formats. `odb/sql/` is the bounded SQL engine, `odb/formula/` is the rpt formula engine, `odb/report/` is the renderer, `odb/values.ts` is shared comparison/aggregation semantics.
|
|
@@ -28,13 +28,14 @@ function dropDuplicatePaints(items) {
|
|
|
28
28
|
return kept;
|
|
29
29
|
}
|
|
30
30
|
function dropOverlappingRepeatsWithinLine(items) {
|
|
31
|
-
const
|
|
31
|
+
const keptByText = /* @__PURE__ */ new Map();
|
|
32
32
|
const kept = [];
|
|
33
33
|
for (const item of items) {
|
|
34
34
|
const textKey = item.text.trim();
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
37
|
-
|
|
35
|
+
const earlier = keptByText.get(textKey);
|
|
36
|
+
if (earlier?.some((prior) => item.xPt < prior.xPt + (prior.widthPt ?? 0) && Math.abs(item.yPt - prior.yPt) <= DUPLICATE_PAINT_BUCKET_PT) === true) continue;
|
|
37
|
+
if (earlier === void 0) keptByText.set(textKey, [item]);
|
|
38
|
+
else earlier.push(item);
|
|
38
39
|
kept.push(item);
|
|
39
40
|
}
|
|
40
41
|
return kept;
|
|
@@ -127,12 +128,23 @@ function clusterIntoLines(items) {
|
|
|
127
128
|
});
|
|
128
129
|
else line.items.push(item);
|
|
129
130
|
}
|
|
130
|
-
|
|
131
|
+
const lines = (0, pdf_codec_text_group.attachScriptRuns)(working, (line) => line.items).filter(({ runs }) => runs.length > 0).map(({ line, runs }) => ({
|
|
132
|
+
...line,
|
|
133
|
+
items: runs
|
|
134
|
+
}));
|
|
135
|
+
for (const line of lines) {
|
|
131
136
|
line.items.sort((a, b) => a.xPt - b.xPt);
|
|
132
137
|
line.items = dropOverlappingRepeatsWithinLine(line.items);
|
|
133
138
|
}
|
|
134
|
-
|
|
135
|
-
return
|
|
139
|
+
lines.sort((a, b) => b.baselineY - a.baselineY);
|
|
140
|
+
return lines;
|
|
141
|
+
}
|
|
142
|
+
function scriptPositionOnLine(item, line) {
|
|
143
|
+
for (const base of line.items) {
|
|
144
|
+
if (base === item) continue;
|
|
145
|
+
const position = (0, pdf_codec_text_group.scriptPositionOf)(base, item);
|
|
146
|
+
if (position !== void 0) return position;
|
|
147
|
+
}
|
|
136
148
|
}
|
|
137
149
|
function textItemVerticalExtent(item) {
|
|
138
150
|
const { standardName } = (0, pdf_codec_fonts.resolveStandardFont)(item.font.family, item.font.weight === "bold", item.font.style === "italic");
|
|
@@ -192,13 +204,32 @@ function pushRunsForLine(runs, line, pageIndex) {
|
|
|
192
204
|
if (itemIndex > 0) {
|
|
193
205
|
const prevItem = line.items[itemIndex - 1];
|
|
194
206
|
if (gapExceeds(prevItem, item, 2 * item.sizePt)) runs.push({ text: " " });
|
|
195
|
-
else if (gapExceeds(prevItem, item, .5)) runs
|
|
207
|
+
else if (gapExceeds(prevItem, item, .5)) appendWordSpace(runs);
|
|
196
208
|
}
|
|
197
209
|
const run = textItemToContentRun(item);
|
|
198
210
|
require_layout_shared.stampFrame(run, pageIndex, textBoxOfItem(item, pageIndex));
|
|
199
|
-
|
|
211
|
+
const verticalAlign = scriptPositionOnLine(item, line);
|
|
212
|
+
const core = item.text.trim();
|
|
213
|
+
if (verticalAlign === void 0 || core.length === 0) {
|
|
214
|
+
runs.push(run);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const leading = item.text.slice(0, item.text.length - item.text.trimStart().length);
|
|
218
|
+
const trailing = item.text.slice(item.text.trimEnd().length);
|
|
219
|
+
if (leading.length > 0) runs.push({ text: leading });
|
|
220
|
+
runs.push({
|
|
221
|
+
...run,
|
|
222
|
+
text: core,
|
|
223
|
+
verticalAlign
|
|
224
|
+
});
|
|
225
|
+
if (trailing.length > 0) runs.push({ text: trailing });
|
|
200
226
|
});
|
|
201
227
|
}
|
|
228
|
+
function appendWordSpace(runs) {
|
|
229
|
+
const last = runs[runs.length - 1];
|
|
230
|
+
if (last.verticalAlign === void 0) last.text += " ";
|
|
231
|
+
else runs.push({ text: " " });
|
|
232
|
+
}
|
|
202
233
|
function bucketCounts(values, bucketSize) {
|
|
203
234
|
const counts = /* @__PURE__ */ new Map();
|
|
204
235
|
for (const v of values) {
|
|
@@ -281,8 +312,7 @@ function paragraphToContentParagraph(paragraph, pageIndex, headingLevel) {
|
|
|
281
312
|
paragraph.lines.forEach((line, lineIndex) => {
|
|
282
313
|
require_layout_shared.stampFrame(result, pageIndex, lineBox(line, pageIndex));
|
|
283
314
|
if (lineIndex > 0) {
|
|
284
|
-
|
|
285
|
-
if (lastRun !== void 0) lastRun.text += " ";
|
|
315
|
+
if (result.runs.length > 0) appendWordSpace(result.runs);
|
|
286
316
|
}
|
|
287
317
|
pushRunsForLine(result.runs, line, pageIndex);
|
|
288
318
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { stampFrame } from "./shared.js";
|
|
2
2
|
import { resolveStandardFont } from "pdf-codec/fonts";
|
|
3
3
|
import { STANDARD_METRICS } from "pdf-codec/afm-widths";
|
|
4
|
-
import { runGapPt, runsShareBaseline } from "pdf-codec/text-group";
|
|
4
|
+
import { attachScriptRuns, runGapPt, runsShareBaseline, scriptPositionOf } from "pdf-codec/text-group";
|
|
5
5
|
//#region src/layout/reconstruct-lines.ts
|
|
6
6
|
const LINE_BASELINE_TOLERANCE_FACTOR = .5;
|
|
7
7
|
const AFM_UNITS_PER_EM = 1e3;
|
|
@@ -27,13 +27,14 @@ function dropDuplicatePaints(items) {
|
|
|
27
27
|
return kept;
|
|
28
28
|
}
|
|
29
29
|
function dropOverlappingRepeatsWithinLine(items) {
|
|
30
|
-
const
|
|
30
|
+
const keptByText = /* @__PURE__ */ new Map();
|
|
31
31
|
const kept = [];
|
|
32
32
|
for (const item of items) {
|
|
33
33
|
const textKey = item.text.trim();
|
|
34
|
-
const
|
|
35
|
-
if (
|
|
36
|
-
|
|
34
|
+
const earlier = keptByText.get(textKey);
|
|
35
|
+
if (earlier?.some((prior) => item.xPt < prior.xPt + (prior.widthPt ?? 0) && Math.abs(item.yPt - prior.yPt) <= DUPLICATE_PAINT_BUCKET_PT) === true) continue;
|
|
36
|
+
if (earlier === void 0) keptByText.set(textKey, [item]);
|
|
37
|
+
else earlier.push(item);
|
|
37
38
|
kept.push(item);
|
|
38
39
|
}
|
|
39
40
|
return kept;
|
|
@@ -126,12 +127,23 @@ function clusterIntoLines(items) {
|
|
|
126
127
|
});
|
|
127
128
|
else line.items.push(item);
|
|
128
129
|
}
|
|
129
|
-
|
|
130
|
+
const lines = attachScriptRuns(working, (line) => line.items).filter(({ runs }) => runs.length > 0).map(({ line, runs }) => ({
|
|
131
|
+
...line,
|
|
132
|
+
items: runs
|
|
133
|
+
}));
|
|
134
|
+
for (const line of lines) {
|
|
130
135
|
line.items.sort((a, b) => a.xPt - b.xPt);
|
|
131
136
|
line.items = dropOverlappingRepeatsWithinLine(line.items);
|
|
132
137
|
}
|
|
133
|
-
|
|
134
|
-
return
|
|
138
|
+
lines.sort((a, b) => b.baselineY - a.baselineY);
|
|
139
|
+
return lines;
|
|
140
|
+
}
|
|
141
|
+
function scriptPositionOnLine(item, line) {
|
|
142
|
+
for (const base of line.items) {
|
|
143
|
+
if (base === item) continue;
|
|
144
|
+
const position = scriptPositionOf(base, item);
|
|
145
|
+
if (position !== void 0) return position;
|
|
146
|
+
}
|
|
135
147
|
}
|
|
136
148
|
function textItemVerticalExtent(item) {
|
|
137
149
|
const { standardName } = resolveStandardFont(item.font.family, item.font.weight === "bold", item.font.style === "italic");
|
|
@@ -191,13 +203,32 @@ function pushRunsForLine(runs, line, pageIndex) {
|
|
|
191
203
|
if (itemIndex > 0) {
|
|
192
204
|
const prevItem = line.items[itemIndex - 1];
|
|
193
205
|
if (gapExceeds(prevItem, item, 2 * item.sizePt)) runs.push({ text: " " });
|
|
194
|
-
else if (gapExceeds(prevItem, item, .5)) runs
|
|
206
|
+
else if (gapExceeds(prevItem, item, .5)) appendWordSpace(runs);
|
|
195
207
|
}
|
|
196
208
|
const run = textItemToContentRun(item);
|
|
197
209
|
stampFrame(run, pageIndex, textBoxOfItem(item, pageIndex));
|
|
198
|
-
|
|
210
|
+
const verticalAlign = scriptPositionOnLine(item, line);
|
|
211
|
+
const core = item.text.trim();
|
|
212
|
+
if (verticalAlign === void 0 || core.length === 0) {
|
|
213
|
+
runs.push(run);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const leading = item.text.slice(0, item.text.length - item.text.trimStart().length);
|
|
217
|
+
const trailing = item.text.slice(item.text.trimEnd().length);
|
|
218
|
+
if (leading.length > 0) runs.push({ text: leading });
|
|
219
|
+
runs.push({
|
|
220
|
+
...run,
|
|
221
|
+
text: core,
|
|
222
|
+
verticalAlign
|
|
223
|
+
});
|
|
224
|
+
if (trailing.length > 0) runs.push({ text: trailing });
|
|
199
225
|
});
|
|
200
226
|
}
|
|
227
|
+
function appendWordSpace(runs) {
|
|
228
|
+
const last = runs[runs.length - 1];
|
|
229
|
+
if (last.verticalAlign === void 0) last.text += " ";
|
|
230
|
+
else runs.push({ text: " " });
|
|
231
|
+
}
|
|
201
232
|
function bucketCounts(values, bucketSize) {
|
|
202
233
|
const counts = /* @__PURE__ */ new Map();
|
|
203
234
|
for (const v of values) {
|
|
@@ -280,8 +311,7 @@ function paragraphToContentParagraph(paragraph, pageIndex, headingLevel) {
|
|
|
280
311
|
paragraph.lines.forEach((line, lineIndex) => {
|
|
281
312
|
stampFrame(result, pageIndex, lineBox(line, pageIndex));
|
|
282
313
|
if (lineIndex > 0) {
|
|
283
|
-
|
|
284
|
-
if (lastRun !== void 0) lastRun.text += " ";
|
|
314
|
+
if (result.runs.length > 0) appendWordSpace(result.runs);
|
|
285
315
|
}
|
|
286
316
|
pushRunsForLine(result.runs, line, pageIndex);
|
|
287
317
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js.documents",
|
|
3
|
-
"version": "14.6.
|
|
3
|
+
"version": "14.6.5",
|
|
4
4
|
"description": "Converts between docx, pptx, odt, odp, ods, odg, xlsx, csv, svg, markdown, rtf, doc, xls, ppt and PDF through one shared, Zod-typed content schema (wpd read-only), plus read+write editors for OOXML and OpenDocument. Worker-isomorphic.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -83,10 +83,10 @@
|
|
|
83
83
|
"document-schema.js": "7.16.2",
|
|
84
84
|
"epub-codec": "5.1.17",
|
|
85
85
|
"fflate": "0.8.3",
|
|
86
|
-
"markdown-codec": "11.
|
|
86
|
+
"markdown-codec": "11.2.0",
|
|
87
87
|
"odf.js": "11.2.3",
|
|
88
88
|
"ooxml.js": "12.2.33",
|
|
89
|
-
"pdf-codec": "5.
|
|
89
|
+
"pdf-codec": "5.7.0",
|
|
90
90
|
"ppt-codec": "2.2.24",
|
|
91
91
|
"rtf-codec": "8.1.25",
|
|
92
92
|
"temml": "0.13.4",
|