@sofereditor/import-docx 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +157 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +157 -4
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -271,6 +271,24 @@ function collectRun(run, ctx, wrappingMarks, out) {
|
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
|
+
var DOCX_HIGHLIGHT_COLORS = {
|
|
275
|
+
yellow: "#ffff00",
|
|
276
|
+
green: "#00ff00",
|
|
277
|
+
cyan: "#00ffff",
|
|
278
|
+
magenta: "#ff00ff",
|
|
279
|
+
blue: "#0000ff",
|
|
280
|
+
red: "#ff0000",
|
|
281
|
+
darkblue: "#000080",
|
|
282
|
+
darkcyan: "#008080",
|
|
283
|
+
darkgreen: "#008000",
|
|
284
|
+
darkmagenta: "#800080",
|
|
285
|
+
darkred: "#800000",
|
|
286
|
+
darkyellow: "#808000",
|
|
287
|
+
darkgray: "#808080",
|
|
288
|
+
lightgray: "#c0c0c0",
|
|
289
|
+
black: "#000000",
|
|
290
|
+
white: "#ffffff"
|
|
291
|
+
};
|
|
274
292
|
function runMarks(rPr) {
|
|
275
293
|
const m = {};
|
|
276
294
|
if (!rPr) return m;
|
|
@@ -298,6 +316,19 @@ function runMarks(rPr) {
|
|
|
298
316
|
if (css) m.color = css;
|
|
299
317
|
break;
|
|
300
318
|
}
|
|
319
|
+
case "w:shd": {
|
|
320
|
+
const fill = attr(child, "w:fill");
|
|
321
|
+
if (fill && fill.toLowerCase() !== "auto") {
|
|
322
|
+
const css = docxHexToCssColor(fill);
|
|
323
|
+
if (css) m.highlight = css;
|
|
324
|
+
}
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
case "w:highlight": {
|
|
328
|
+
const css = DOCX_HIGHLIGHT_COLORS[(attr(child, "w:val") ?? "").toLowerCase()];
|
|
329
|
+
if (css) m.highlight = css;
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
301
332
|
// w:rFonts intentionally ignored: school standard is Arial for all
|
|
302
333
|
// exams. Any font declared in the .docx is dropped on import so the
|
|
303
334
|
// imported doc inherits Arial from `.ed-root` / PDF base stylesheet.
|
|
@@ -378,6 +409,10 @@ function paragraphToBlock(p, ctx, numbering) {
|
|
|
378
409
|
listKind: resolved?.listKind ?? "bullet",
|
|
379
410
|
listLevel: resolved?.listLevel ?? 0
|
|
380
411
|
};
|
|
412
|
+
if (resolved?.listKind === "ordered") {
|
|
413
|
+
if (resolved.ordinal != null) attrs2.listStart = resolved.ordinal;
|
|
414
|
+
if (resolved.listStyle) attrs2.listStyle = resolved.listStyle;
|
|
415
|
+
}
|
|
381
416
|
if (align) attrs2.align = align;
|
|
382
417
|
if (isRtl) attrs2.dir = "rtl";
|
|
383
418
|
return { type: "listItem", text, delta, attrs: attrs2 };
|
|
@@ -404,8 +439,35 @@ function paragraphToBlock(p, ctx, numbering) {
|
|
|
404
439
|
const attrs = {};
|
|
405
440
|
if (align) attrs.align = align;
|
|
406
441
|
if (isRtl) attrs.dir = "rtl";
|
|
442
|
+
Object.assign(attrs, readAnswerLine(pPr, text.length > 0));
|
|
407
443
|
return { type: "paragraph", text, delta, attrs };
|
|
408
444
|
}
|
|
445
|
+
function readAnswerLine(pPr, hasText) {
|
|
446
|
+
if (!pPr || hasText) return {};
|
|
447
|
+
const pBdr = findChild(pPr, "w:pBdr");
|
|
448
|
+
if (!pBdr) return {};
|
|
449
|
+
const bottom = findChild(pBdr, "w:bottom");
|
|
450
|
+
if (!bottom) return {};
|
|
451
|
+
const val = (attr(bottom, "w:val") ?? "").toLowerCase();
|
|
452
|
+
if (val === "" || val === "none" || val === "nil") return {};
|
|
453
|
+
return { answerLine: true, answerLineSpacing: readAnswerLineSpacing(pPr) };
|
|
454
|
+
}
|
|
455
|
+
function readAnswerLineSpacing(pPr) {
|
|
456
|
+
const sp = findChild(pPr, "w:spacing");
|
|
457
|
+
const line = sp ? Number.parseFloat(attr(sp, "w:line") ?? "") : NaN;
|
|
458
|
+
if (!Number.isFinite(line)) return 1;
|
|
459
|
+
const buckets = [1, 1.5, 2];
|
|
460
|
+
let best = 1;
|
|
461
|
+
let bestDist = Infinity;
|
|
462
|
+
for (const b of buckets) {
|
|
463
|
+
const d = Math.abs(line - 240 * b);
|
|
464
|
+
if (d < bestDist) {
|
|
465
|
+
bestDist = d;
|
|
466
|
+
best = b;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return best;
|
|
470
|
+
}
|
|
409
471
|
function collectText(delta) {
|
|
410
472
|
let out = "";
|
|
411
473
|
for (const op of delta) {
|
|
@@ -497,8 +559,33 @@ function tableToBlock(tbl, ctx) {
|
|
|
497
559
|
}
|
|
498
560
|
const cells = slots.map((s) => s ?? emptyCell());
|
|
499
561
|
const attrs = { rows, cols };
|
|
562
|
+
const preset = readBorderPreset(tbl);
|
|
563
|
+
if (preset) attrs.borderPreset = preset;
|
|
500
564
|
return { type: "table", text: "", delta: [], attrs, cells };
|
|
501
565
|
}
|
|
566
|
+
function readBorderPreset(tbl) {
|
|
567
|
+
const tblPr = findChild(tbl, "w:tblPr");
|
|
568
|
+
const b = tblPr ? findChild(tblPr, "w:tblBorders") : void 0;
|
|
569
|
+
if (!b) return void 0;
|
|
570
|
+
const ligado = (...nomes) => nomes.some((nome) => {
|
|
571
|
+
const n = findChild(b, nome);
|
|
572
|
+
if (!n) return false;
|
|
573
|
+
const val = (attr(n, "w:val") ?? "").toLowerCase();
|
|
574
|
+
return val !== "" && val !== "none" && val !== "nil";
|
|
575
|
+
});
|
|
576
|
+
const top = ligado("w:top");
|
|
577
|
+
const bottom = ligado("w:bottom");
|
|
578
|
+
const left = ligado("w:left", "w:start");
|
|
579
|
+
const right = ligado("w:right", "w:end");
|
|
580
|
+
const iH = ligado("w:insideH");
|
|
581
|
+
const iV = ligado("w:insideV");
|
|
582
|
+
if (top && bottom && left && right && iH && iV) return "all";
|
|
583
|
+
if (top && bottom && left && right && !iH && !iV) return "outer";
|
|
584
|
+
if (top && bottom && !left && !right && iH && !iV) return "horizontal";
|
|
585
|
+
if (!top && !bottom && left && right && !iH && iV) return "vertical";
|
|
586
|
+
if (!top && !bottom && !left && !right && !iH && !iV) return "none";
|
|
587
|
+
return "all";
|
|
588
|
+
}
|
|
502
589
|
function gridSpanOf(tc) {
|
|
503
590
|
const tcPr = findChild(tc, "w:tcPr");
|
|
504
591
|
if (!tcPr) return 1;
|
|
@@ -533,9 +620,19 @@ function emptyCell() {
|
|
|
533
620
|
}
|
|
534
621
|
|
|
535
622
|
// src/numbering.ts
|
|
623
|
+
var FMT_TO_LIST_STYLE = {
|
|
624
|
+
decimal: "decimal",
|
|
625
|
+
lowerLetter: "lower-alpha",
|
|
626
|
+
upperLetter: "upper-alpha",
|
|
627
|
+
lowerRoman: "lower-roman",
|
|
628
|
+
upperRoman: "upper-roman"
|
|
629
|
+
};
|
|
536
630
|
var NumberingResolver = class {
|
|
537
631
|
numIdToAbstract = /* @__PURE__ */ new Map();
|
|
538
632
|
abstractLevelFmt = /* @__PURE__ */ new Map();
|
|
633
|
+
abstractLevelStart = /* @__PURE__ */ new Map();
|
|
634
|
+
/** Contadores vivos por instância de lista (numId), indexados por nível. */
|
|
635
|
+
counters = /* @__PURE__ */ new Map();
|
|
539
636
|
constructor(numberingXml) {
|
|
540
637
|
if (!numberingXml) return;
|
|
541
638
|
for (const top of numberingXml) {
|
|
@@ -546,12 +643,16 @@ var NumberingResolver = class {
|
|
|
546
643
|
const abstractId = attr(child, "w:abstractNumId");
|
|
547
644
|
if (!abstractId) continue;
|
|
548
645
|
const levels = /* @__PURE__ */ new Map();
|
|
646
|
+
const starts = /* @__PURE__ */ new Map();
|
|
549
647
|
for (const lvl of findChildren(child, "w:lvl")) {
|
|
550
648
|
const ilvl = parseIntAttr(attr(lvl, "w:ilvl"), 0);
|
|
551
649
|
const fmt = attr(findChild(lvl, "w:numFmt") ?? {}, "w:val");
|
|
552
650
|
if (fmt) levels.set(ilvl, fmt);
|
|
651
|
+
const start = findChild(lvl, "w:start");
|
|
652
|
+
if (start) starts.set(ilvl, parseIntAttr(attr(start, "w:val"), 1));
|
|
553
653
|
}
|
|
554
654
|
this.abstractLevelFmt.set(abstractId, levels);
|
|
655
|
+
this.abstractLevelStart.set(abstractId, starts);
|
|
555
656
|
} else if (tag === "w:num") {
|
|
556
657
|
const numId = attr(child, "w:numId");
|
|
557
658
|
const abstractRef = findChild(child, "w:abstractNumId");
|
|
@@ -561,7 +662,12 @@ var NumberingResolver = class {
|
|
|
561
662
|
}
|
|
562
663
|
}
|
|
563
664
|
}
|
|
564
|
-
/**
|
|
665
|
+
/**
|
|
666
|
+
* Resolves a paragraph's `<w:numPr>` into `{listKind, listLevel, ordinal?,
|
|
667
|
+
* listStyle?}`, or null. STATEFUL for ordered lists: each call advances the
|
|
668
|
+
* per-`numId` counter (Word instance semantics) — call exactly once per
|
|
669
|
+
* numbered paragraph, in document order.
|
|
670
|
+
*/
|
|
565
671
|
resolve(numPr) {
|
|
566
672
|
if (!numPr) return null;
|
|
567
673
|
const ilvlRef = findChild(numPr, "w:ilvl");
|
|
@@ -575,9 +681,21 @@ var NumberingResolver = class {
|
|
|
575
681
|
return { listKind: "bullet", listLevel };
|
|
576
682
|
}
|
|
577
683
|
const levels = this.abstractLevelFmt.get(abstractId);
|
|
578
|
-
const fmt =
|
|
579
|
-
const
|
|
580
|
-
|
|
684
|
+
const fmt = levels?.get(listLevel) ?? "bullet";
|
|
685
|
+
const fmtLower = fmt.toLowerCase();
|
|
686
|
+
const listKind = fmtLower === "bullet" || fmtLower === "none" ? "bullet" : "ordered";
|
|
687
|
+
if (listKind === "bullet") return { listKind, listLevel };
|
|
688
|
+
const start = this.abstractLevelStart.get(abstractId)?.get(listLevel) ?? 1;
|
|
689
|
+
const c = this.counters.get(numId) ?? [];
|
|
690
|
+
c[listLevel] = c[listLevel] == null ? start : c[listLevel] + 1;
|
|
691
|
+
c.length = listLevel + 1;
|
|
692
|
+
this.counters.set(numId, c);
|
|
693
|
+
return {
|
|
694
|
+
listKind,
|
|
695
|
+
listLevel,
|
|
696
|
+
ordinal: c[listLevel],
|
|
697
|
+
listStyle: FMT_TO_LIST_STYLE[fmt] ?? "decimal"
|
|
698
|
+
};
|
|
581
699
|
}
|
|
582
700
|
};
|
|
583
701
|
function clampLevel(n) {
|
|
@@ -641,9 +759,44 @@ async function docxBlobToDocument(input) {
|
|
|
641
759
|
if (blocks.length === 0) {
|
|
642
760
|
blocks.push({ type: "paragraph", text: "", delta: [], attrs: {} });
|
|
643
761
|
}
|
|
762
|
+
pruneNonLeaderListStarts(blocks);
|
|
644
763
|
const pageSettings = sectPrToPageSettings(findChild(body, "w:sectPr"));
|
|
645
764
|
return pageSettings ? { blocks, pageSettings } : { blocks };
|
|
646
765
|
}
|
|
766
|
+
function pruneNonLeaderListStarts(blocks) {
|
|
767
|
+
let grupoAtivo = false;
|
|
768
|
+
let grupoStyle;
|
|
769
|
+
let ordinais = [];
|
|
770
|
+
const resetGrupo = () => {
|
|
771
|
+
grupoAtivo = false;
|
|
772
|
+
grupoStyle = void 0;
|
|
773
|
+
ordinais = [];
|
|
774
|
+
};
|
|
775
|
+
for (const block of blocks) {
|
|
776
|
+
if (block.type !== "listItem") {
|
|
777
|
+
resetGrupo();
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
if ((block.attrs.listKind ?? "bullet") !== "ordered") {
|
|
781
|
+
resetGrupo();
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
const level = block.attrs.listLevel ?? 0;
|
|
785
|
+
const ordinal = typeof block.attrs.listStart === "number" ? block.attrs.listStart : 1;
|
|
786
|
+
const mesmoNivelSequencial = ordinais[level] != null && ordinal === ordinais[level] + 1;
|
|
787
|
+
const entrandoEmNivelNovo = ordinais[level] == null && level > 0 && ordinal === 1;
|
|
788
|
+
const continuaGrupo = grupoAtivo && block.attrs.listStyle === grupoStyle && (mesmoNivelSequencial || entrandoEmNivelNovo);
|
|
789
|
+
if (continuaGrupo) {
|
|
790
|
+
delete block.attrs.listStart;
|
|
791
|
+
} else {
|
|
792
|
+
grupoAtivo = true;
|
|
793
|
+
grupoStyle = block.attrs.listStyle;
|
|
794
|
+
ordinais = [];
|
|
795
|
+
}
|
|
796
|
+
ordinais[level] = ordinal;
|
|
797
|
+
ordinais.length = level + 1;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
647
800
|
async function docxBlobToEditorDocument(input) {
|
|
648
801
|
const serialized = await docxBlobToDocument(input);
|
|
649
802
|
return import_core2.EditorDocument.fromJSON(serialized);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/docx.ts","../src/parse-xml.ts","../src/units.ts","../src/images.ts","../src/runs.ts","../src/paragraphs.ts","../src/tables.ts","../src/numbering.ts","../src/page-settings.ts"],"sourcesContent":["export { docxBlobToDocument, docxBlobToEditorDocument } from \"./docx\";\n","import { EditorDocument, type SerializedBlock, type SerializedDocument } from \"@sofereditor/core\";\nimport { readDocx, childrenOf, tagOf, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { paragraphToBlock } from \"./paragraphs\";\nimport { tableToBlock } from \"./tables\";\nimport { NumberingResolver } from \"./numbering\";\nimport { sectPrToPageSettings } from \"./page-settings\";\nimport type { RunContext } from \"./runs\";\n\n/**\n * Read a `.docx` (OOXML) Blob/ArrayBuffer/Uint8Array and produce a\n * `SerializedDocument` ready to feed `EditorDocument.fromJSON` (or\n * `loadFromJSON` on an existing instance).\n *\n * The mapping is the inverse of `@sofereditor/export-docx`. Round-trip is exact for\n * documents the editor produced; documents authored externally (Word, Google\n * Docs export) load with marks/lists/tables/images recognized and unsupported\n * features dropped silently.\n */\nexport async function docxBlobToDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<SerializedDocument> {\n const file = await readDocx(input);\n const numbering = new NumberingResolver(file.numberingXml);\n const ctx: RunContext = {\n relationships: file.relationships,\n mediaBytes: file.mediaBytes,\n };\n\n const body = findBody(file.documentXml);\n if (!body) return { blocks: [{ type: \"paragraph\", text: \"\", delta: [], attrs: {} }] };\n\n const blocks: SerializedBlock[] = [];\n for (const child of childrenOf(body)) {\n const tag = tagOf(child);\n if (tag === \"w:p\") {\n blocks.push(paragraphToBlock(child, ctx, numbering));\n } else if (tag === \"w:tbl\") {\n blocks.push(tableToBlock(child, ctx));\n }\n // w:sectPr is parsed below (page settings); other body-level metadata ignored.\n }\n\n if (blocks.length === 0) {\n blocks.push({ type: \"paragraph\", text: \"\", delta: [], attrs: {} });\n }\n\n const pageSettings = sectPrToPageSettings(findChild(body, \"w:sectPr\"));\n return pageSettings ? { blocks, pageSettings } : { blocks };\n}\n\n/** Convenience: parse → instantiate a fresh `EditorDocument`. */\nexport async function docxBlobToEditorDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<EditorDocument> {\n const serialized = await docxBlobToDocument(input);\n return EditorDocument.fromJSON(serialized);\n}\n\nfunction findBody(documentXml: OoxmlNode[]): OoxmlNode | undefined {\n for (const top of documentXml) {\n if (tagOf(top) === \"w:document\") {\n const body = findChild(top, \"w:body\");\n if (body) return body;\n }\n }\n return undefined;\n}\n","import JSZip from \"jszip\";\nimport { XMLParser } from \"fast-xml-parser\";\n\n/**\n * fast-xml-parser with `preserveOrder: true` produces a list of single-key\n * objects per element. Each element is either:\n *\n * - A tag node: `{ \"<tagName>\": <children-array>, \":@\"?: <attrs> }`\n * - A text node: `{ \"#text\": <string> }`\n *\n * Attributes are prefixed with `@_`. The `:@` key holds attributes for the\n * surrounding tag. Children preserve original document order, which matters\n * for paragraph runs (bold word followed by italic word).\n */\nexport type OoxmlNode = Record<string, unknown>;\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n preserveOrder: true,\n trimValues: false, // keep whitespace inside <w:t>\n parseAttributeValue: false,\n parseTagValue: false,\n});\n\nexport interface DocxFile {\n documentXml: OoxmlNode[];\n numberingXml?: OoxmlNode[];\n /** rId → target path (inside the zip), e.g. \"media/image1.png\". */\n relationships: Map<string, string>;\n /** zip path (without leading \"word/\") → bytes for embedded media. */\n mediaBytes: Map<string, Uint8Array>;\n}\n\nexport async function readDocx(input: Blob | ArrayBuffer | Uint8Array): Promise<DocxFile> {\n const data =\n input instanceof Uint8Array\n ? input\n : input instanceof ArrayBuffer\n ? new Uint8Array(input)\n : new Uint8Array(await (input as Blob).arrayBuffer());\n\n const zip = await JSZip.loadAsync(data);\n const documentXml = await readXml(zip, \"word/document.xml\");\n if (!documentXml) throw new Error(\"Invalid .docx: missing word/document.xml\");\n\n const numberingXml = await readXml(zip, \"word/numbering.xml\");\n const relsXml = await readXml(zip, \"word/_rels/document.xml.rels\");\n const relationships = parseRelationships(relsXml);\n\n const mediaBytes = new Map<string, Uint8Array>();\n const mediaFolder = zip.folder(\"word/media\");\n if (mediaFolder) {\n const promises: Promise<void>[] = [];\n mediaFolder.forEach((relativePath, file) => {\n if (file.dir) return;\n promises.push(\n file.async(\"uint8array\").then((bytes) => {\n mediaBytes.set(`media/${relativePath}`, bytes);\n }),\n );\n });\n await Promise.all(promises);\n }\n\n return { documentXml, numberingXml, relationships, mediaBytes };\n}\n\nasync function readXml(zip: JSZip, path: string): Promise<OoxmlNode[] | undefined> {\n const entry = zip.file(path);\n if (!entry) return undefined;\n const text = await entry.async(\"string\");\n return parser.parse(text) as OoxmlNode[];\n}\n\nfunction parseRelationships(nodes: OoxmlNode[] | undefined): Map<string, string> {\n const out = new Map<string, string>();\n if (!nodes) return out;\n // <Relationships><Relationship Id=\"rId1\" Type=\"...\" Target=\"media/image1.png\"/>...\n for (const top of nodes) {\n const children = top[\"Relationships\"];\n if (!Array.isArray(children)) continue;\n for (const child of children as OoxmlNode[]) {\n if (!(\"Relationship\" in child)) continue;\n const attrs = (child[\":@\"] ?? {}) as Record<string, unknown>;\n const id = attrs[\"@_Id\"];\n const target = attrs[\"@_Target\"];\n if (typeof id === \"string\" && typeof target === \"string\") {\n out.set(id, target);\n }\n }\n }\n return out;\n}\n\n// ---------- generic walkers over preserveOrder shape ----------\n\n/** Returns the tag name (e.g. \"w:p\") of a preserveOrder element, or null for text. */\nexport function tagOf(node: OoxmlNode): string | null {\n for (const key of Object.keys(node)) {\n if (key === \":@\") continue;\n return key;\n }\n return null;\n}\n\n/** Returns the children array of an element, or [] when none. */\nexport function childrenOf(node: OoxmlNode): OoxmlNode[] {\n const tag = tagOf(node);\n if (!tag) return [];\n const v = node[tag];\n return Array.isArray(v) ? (v as OoxmlNode[]) : [];\n}\n\n/** Returns the attribute bag (already with `@_` prefix) for an element. */\nexport function attrsOf(node: OoxmlNode): Record<string, unknown> {\n return ((node[\":@\"] ?? {}) as Record<string, unknown>) ?? {};\n}\n\n/** Read a single `@_key` attribute as a string. */\nexport function attr(node: OoxmlNode, key: string): string | undefined {\n const v = attrsOf(node)[`@_${key}`];\n return typeof v === \"string\" ? v : undefined;\n}\n\n/** Find the first direct child with the given tag name. */\nexport function findChild(node: OoxmlNode, tag: string): OoxmlNode | undefined {\n for (const child of childrenOf(node)) {\n if (tagOf(child) === tag) return child;\n }\n return undefined;\n}\n\n/** Find all direct children with the given tag name. */\nexport function findChildren(node: OoxmlNode, tag: string): OoxmlNode[] {\n const out: OoxmlNode[] = [];\n for (const child of childrenOf(node)) {\n if (tagOf(child) === tag) out.push(child);\n }\n return out;\n}\n\n/** Recursively collect text nodes' concatenated content. */\nexport function textOf(node: OoxmlNode): string {\n let out = \"\";\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) {\n const t = child[\"#text\"];\n if (typeof t === \"string\") out += t;\n } else {\n out += textOf(child);\n }\n }\n return out;\n}\n","/**\n * Unit conversions between OOXML and the editor's serialized format.\n * These are the inverses of the helpers in `@sofereditor/export-docx/src/docx.ts`.\n */\n\n/** docx half-points → CSS pt string. 24 (half-pt) → \"12pt\". */\nexport function halfPointsToCssPt(halfPoints: number): string | undefined {\n if (!Number.isFinite(halfPoints) || halfPoints <= 0) return undefined;\n const pt = halfPoints / 2;\n return `${stripTrailingZeros(pt)}pt`;\n}\n\n/** OOXML RRGGBB (no '#') → CSS hex with leading '#'. */\nexport function docxHexToCssColor(hex: string | undefined): string | undefined {\n if (!hex) return undefined;\n const m = /^[0-9a-f]{6}$/i.exec(hex.trim());\n if (!m) return undefined;\n return `#${hex.toLowerCase()}`;\n}\n\n/** OOXML alignment value → editor AlignValue. */\nexport function parseAlign(val: string | undefined): \"left\" | \"center\" | \"right\" | \"justify\" | undefined {\n if (!val) return undefined;\n switch (val.toLowerCase()) {\n case \"left\":\n case \"start\":\n return \"left\";\n case \"center\":\n return \"center\";\n case \"right\":\n case \"end\":\n return \"right\";\n case \"both\":\n case \"distribute\":\n case \"justify\":\n return \"justify\";\n default:\n return undefined;\n }\n}\n\n/** Parse XML attribute that should be a positive integer. */\nexport function parseIntAttr(val: unknown, fallback = 0): number {\n if (typeof val === \"number\" && Number.isFinite(val)) return Math.trunc(val);\n if (typeof val !== \"string\") return fallback;\n const n = Number.parseInt(val, 10);\n return Number.isFinite(n) ? n : fallback;\n}\n\nfunction stripTrailingZeros(n: number): string {\n // 12 → \"12\", 12.5 → \"12.5\"\n return Number.isInteger(n) ? n.toString() : n.toFixed(2).replace(/\\.?0+$/, \"\");\n}\n","import type { ImageEmbed } from \"@sofereditor/core\";\nimport { attr, childrenOf, tagOf, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/** 1 inch = 914400 EMU = 96 px → 1 px = 9525 EMU. */\nconst EMU_PER_PX = 9525;\n\nconst MIME_BY_EXT: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n bmp: \"image/bmp\",\n svg: \"image/svg+xml\",\n};\n\nexport interface ImageContext {\n relationships: Map<string, string>;\n mediaBytes: Map<string, Uint8Array>;\n}\n\n/**\n * Walk a `<w:drawing>` node and return an `ImageEmbed` if it contains a\n * recognized inline picture. Returns null for unsupported drawings (shapes,\n * charts, etc.).\n */\nexport function extractImageFromDrawing(\n drawing: OoxmlNode,\n ctx: ImageContext,\n): ImageEmbed | null {\n // The interesting bits live deep inside: a:blip carries `r:embed`, and\n // wp:extent carries cx/cy. We do a generic walk because the wrapping nodes\n // differ between inline (`wp:inline`) and anchored (`wp:anchor`) drawings.\n let embedId: string | undefined;\n let cx: number | undefined;\n let cy: number | undefined;\n\n walk(drawing, (n) => {\n const tag = tagOf(n);\n if (!tag) return;\n if (tag === \"a:blip\") {\n const id = attr(n, \"r:embed\") ?? attr(n, \"r:link\");\n if (id) embedId = id;\n } else if (tag === \"wp:extent\") {\n cx = parseIntAttr(attr(n, \"cx\"));\n cy = parseIntAttr(attr(n, \"cy\"));\n }\n });\n\n if (!embedId) return null;\n const target = ctx.relationships.get(embedId);\n if (!target) return null;\n // Relationship targets are typically \"media/image1.png\"; the zip lookup uses\n // the same path (we stored bytes keyed under \"media/<file>\").\n const bytes = ctx.mediaBytes.get(target);\n if (!bytes) return null;\n\n const ext = extensionOf(target);\n const mime = MIME_BY_EXT[ext] ?? \"image/png\";\n const src = `data:${mime};base64,${bytesToBase64(bytes)}`;\n const widthPx = cx != null ? Math.max(1, Math.round(cx / EMU_PER_PX)) : 100;\n const heightPx = cy != null ? Math.max(1, Math.round(cy / EMU_PER_PX)) : 100;\n\n return { type: \"image\", src, width: widthPx, height: heightPx };\n}\n\nfunction walk(node: OoxmlNode, visit: (n: OoxmlNode) => void): void {\n visit(node);\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) continue;\n walk(child, visit);\n }\n}\n\nfunction extensionOf(path: string): string {\n const m = /\\.([a-z0-9]+)$/i.exec(path);\n return m ? m[1].toLowerCase() : \"\";\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n // Browser path.\n if (typeof btoa === \"function\") {\n let bin = \"\";\n const CHUNK = 0x8000;\n for (let i = 0; i < bytes.length; i += CHUNK) {\n bin += String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + CHUNK)));\n }\n return btoa(bin);\n }\n // Node fallback.\n return Buffer.from(bytes).toString(\"base64\");\n}\n","import type { DeltaOp, MarkAttrs } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { docxHexToCssColor, halfPointsToCssPt } from \"./units\";\nimport { extractImageFromDrawing, type ImageContext } from \"./images\";\n\nexport interface RunContext extends ImageContext {}\n\n/**\n * Walk a paragraph's children (post `<w:pPr>`) and emit a `DeltaOp[]`. Combines\n * adjacent runs that share the same mark set into a single op for parity with\n * the export — Y.Text.applyDelta would merge them anyway, but pre-merging keeps\n * round-trip output stable.\n */\nexport function paragraphChildrenToDelta(children: OoxmlNode[], ctx: RunContext): DeltaOp[] {\n const ops: DeltaOp[] = [];\n for (const child of children) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:pPr\") continue;\n if (tag === \"w:r\") {\n collectRun(child, ctx, undefined, ops);\n } else if (tag === \"w:hyperlink\") {\n // hyperlinks carry an r:id that maps via relationships to the href.\n const rId = attr(child, \"r:id\");\n const href = rId ? ctx.relationships.get(rId) : undefined;\n const linkMark: MarkAttrs | undefined = href ? { link: { href } } : undefined;\n for (const sub of childrenOf(child)) {\n if (tagOf(sub) === \"w:r\") collectRun(sub, ctx, linkMark, ops);\n }\n }\n }\n return mergeAdjacent(ops);\n}\n\nfunction collectRun(\n run: OoxmlNode,\n ctx: RunContext,\n wrappingMarks: MarkAttrs | undefined,\n out: DeltaOp[],\n): void {\n const marks = mergeMarks(wrappingMarks, runMarks(findChild(run, \"w:rPr\")));\n for (const child of childrenOf(run)) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:t\") {\n const text = textContent(child);\n if (text.length > 0) push(out, text, marks);\n } else if (tag === \"w:tab\") {\n push(out, \"\\t\", marks);\n } else if (tag === \"w:br\") {\n // Treat w:br type=\"page\" as also a soft break for the model — we have no\n // explicit page-break node in the schema; pagination is computed live.\n push(out, \"\\n\", marks);\n } else if (tag === \"w:drawing\") {\n const embed = extractImageFromDrawing(child, ctx);\n if (embed) out.push({ insert: embed });\n }\n // Other run children (w:sym, w:fldChar, etc.) are silently dropped.\n }\n}\n\nfunction runMarks(rPr: OoxmlNode | undefined): MarkAttrs {\n const m: MarkAttrs = {};\n if (!rPr) return m;\n for (const child of childrenOf(rPr)) {\n const tag = tagOf(child);\n if (!tag) continue;\n switch (tag) {\n case \"w:b\":\n if (!isFalse(attr(child, \"w:val\"))) m.bold = true;\n break;\n case \"w:i\":\n if (!isFalse(attr(child, \"w:val\"))) m.italic = true;\n break;\n case \"w:u\": {\n const val = attr(child, \"w:val\");\n if (val && val.toLowerCase() !== \"none\") m.underline = true;\n break;\n }\n case \"w:strike\":\n if (!isFalse(attr(child, \"w:val\"))) m.strike = true;\n break;\n case \"w:color\": {\n const hex = attr(child, \"w:val\");\n const css = docxHexToCssColor(hex);\n if (css) m.color = css;\n break;\n }\n // w:rFonts intentionally ignored: school standard is Arial for all\n // exams. Any font declared in the .docx is dropped on import so the\n // imported doc inherits Arial from `.ed-root` / PDF base stylesheet.\n case \"w:sz\": {\n const halfPts = Number.parseFloat(attr(child, \"w:val\") ?? \"\");\n const css = halfPointsToCssPt(halfPts);\n if (css) m.fontSize = css;\n break;\n }\n }\n }\n return m;\n}\n\nfunction mergeMarks(a: MarkAttrs | undefined, b: MarkAttrs): MarkAttrs {\n if (!a) return b;\n return { ...a, ...b };\n}\n\nfunction textContent(node: OoxmlNode): string {\n let out = \"\";\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) {\n const t = child[\"#text\"];\n if (typeof t === \"string\") out += t;\n }\n }\n return out;\n}\n\nfunction isFalse(val: string | undefined): boolean {\n if (val == null) return false;\n const v = val.toLowerCase();\n return v === \"0\" || v === \"false\" || v === \"none\";\n}\n\nfunction push(out: DeltaOp[], text: string, marks: MarkAttrs): void {\n const op: DeltaOp =\n Object.keys(marks).length === 0 ? { insert: text } : { insert: text, attributes: { ...marks } };\n out.push(op);\n}\n\nfunction mergeAdjacent(ops: DeltaOp[]): DeltaOp[] {\n const out: DeltaOp[] = [];\n for (const op of ops) {\n const prev = out[out.length - 1];\n if (\n prev &&\n typeof prev.insert === \"string\" &&\n typeof op.insert === \"string\" &&\n sameMarks(prev.attributes, op.attributes)\n ) {\n prev.insert = prev.insert + op.insert;\n continue;\n }\n out.push(op);\n }\n return out;\n}\n\nfunction sameMarks(a: MarkAttrs | undefined, b: MarkAttrs | undefined): boolean {\n const ak = a ? Object.keys(a).sort() : [];\n const bk = b ? Object.keys(b).sort() : [];\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i++) {\n if (ak[i] !== bk[i]) return false;\n const av = (a as Record<string, unknown>)[ak[i]];\n const bv = (b as Record<string, unknown>)[bk[i]];\n if (typeof av === \"object\" && av !== null && typeof bv === \"object\" && bv !== null) {\n if (JSON.stringify(av) !== JSON.stringify(bv)) return false;\n } else if (av !== bv) {\n return false;\n }\n }\n return true;\n}\n","import type { AlignValue, BlockAttrs, BlockType, DeltaOp, SerializedBlock } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { NumberingResolver } from \"./numbering\";\nimport { parseAlign } from \"./units\";\n\n/**\n * Map a `<w:p>` element to a SerializedBlock. Decides between\n * `paragraph / heading / blockquote / codeBlock / listItem` based on the\n * paragraph style + `w:pPr` properties — mirrors the heuristics encoded in\n * `@sofereditor/export-docx/src/docx.ts`.\n */\nexport function paragraphToBlock(\n p: OoxmlNode,\n ctx: RunContext,\n numbering: NumberingResolver,\n): SerializedBlock {\n const pPr = findChild(p, \"w:pPr\");\n const numPr = pPr ? findChild(pPr, \"w:numPr\") : undefined;\n const styleId = pPr ? attr(findChild(pPr, \"w:pStyle\") ?? {}, \"w:val\") : undefined;\n\n const delta = paragraphChildrenToDelta(childrenOf(p), ctx);\n const text = collectText(delta);\n const align = parseAlign(pPr ? attr(findChild(pPr, \"w:jc\") ?? {}, \"w:val\") : undefined);\n const isRtl = pPr ? !!findChild(pPr, \"w:bidi\") : false;\n\n // List? — numPr is the strongest signal.\n if (numPr) {\n const resolved = numbering.resolve(numPr);\n const attrs: BlockAttrs = {\n listKind: resolved?.listKind ?? \"bullet\",\n listLevel: resolved?.listLevel ?? 0,\n };\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"listItem\", text, delta, attrs };\n }\n\n // Heading?\n const headingLevel = styleId ? headingFromStyleId(styleId) : null;\n if (headingLevel != null) {\n const attrs: BlockAttrs = { level: headingLevel };\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"heading\", text, delta, attrs };\n }\n\n // Blockquote (export uses left border + 480 twip indent).\n if (pPr && hasLeftBorder(pPr)) {\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"blockquote\", text, delta, attrs };\n }\n\n // Code block (export uses Consolas + light-gray shading).\n if (pPr && hasCodeBlockShading(pPr)) {\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"codeBlock\", text, delta, attrs };\n }\n\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"paragraph\", text, delta, attrs };\n}\n\nfunction collectText(delta: DeltaOp[]): string {\n let out = \"\";\n for (const op of delta) {\n if (typeof op.insert === \"string\") out += op.insert;\n }\n return out;\n}\n\nfunction headingFromStyleId(id: string): 1 | 2 | 3 | 4 | 5 | 6 | null {\n const m = /^Heading\\s*([1-6])$/i.exec(id);\n if (!m) return null;\n const n = Number.parseInt(m[1], 10);\n return (n as 1 | 2 | 3 | 4 | 5 | 6) ?? null;\n}\n\nfunction hasLeftBorder(pPr: OoxmlNode): boolean {\n const pBdr = findChild(pPr, \"w:pBdr\");\n if (!pBdr) return false;\n return !!findChild(pBdr, \"w:left\");\n}\n\nfunction hasCodeBlockShading(pPr: OoxmlNode): boolean {\n const shd = findChild(pPr, \"w:shd\");\n if (!shd) return false;\n const fill = attr(shd, \"w:fill\");\n if (!fill) return false;\n return fill.toLowerCase() === \"f1f5f9\";\n}\n\n/** Whitelist the block types this module produces (used by callers for typing). */\nexport type ParagraphBlockType = Exclude<BlockType, \"table\">;\n/** Whitelist the align values this module produces (used by callers for typing). */\nexport type ParagraphAlign = AlignValue;\n","import type { BlockAttrs, CellAttrs, DeltaOp, SerializedBlock, SerializedCell } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * Map a `<w:tbl>` element to a SerializedBlock(table). The output `cells` array\n * is row-major with `rows * cols` entries — covered slots are filled with\n * `{attrs: {covered: true}}` placeholders so it matches the invariant required\n * by `@sofereditor/core`'s `EditorDocument`.\n */\nexport function tableToBlock(tbl: OoxmlNode, ctx: RunContext): SerializedBlock {\n const tblGrid = findChild(tbl, \"w:tblGrid\");\n const gridCols = tblGrid ? findChildren(tblGrid, \"w:gridCol\") : [];\n const trs = findChildren(tbl, \"w:tr\");\n\n // The docx writer (and Word itself) may emit fewer `<w:gridCol>` entries than\n // the table's logical column count when a row has merged cells. Take the max\n // of (declared gridCols, widest row's total gridSpan) to stay robust.\n let cols = gridCols.length;\n for (const tr of trs) {\n const tcs = findChildren(tr, \"w:tc\");\n let count = 0;\n for (const tc of tcs) count += gridSpanOf(tc);\n if (count > cols) cols = count;\n }\n if (cols < 1) cols = 1;\n const rows = trs.length;\n\n // Build a row-major grid of slots. Each slot is either a real cell or marked\n // as covered. We track which (row, col) is the anchor for vMerge groups.\n const slots: Array<SerializedCell | null> = new Array(rows * cols).fill(null);\n // Anchor of the active vMerge per column index; resets when a non-merge cell\n // is placed in the same column.\n const vMergeAnchorByCol = new Map<number, { row: number; col: number }>();\n\n for (let r = 0; r < rows; r++) {\n const tr = trs[r];\n let cursor = 0;\n for (const tc of findChildren(tr, \"w:tc\")) {\n // Advance the cursor past already-occupied (covered-from-above) slots.\n while (cursor < cols && slots[r * cols + cursor] !== null) cursor++;\n if (cursor >= cols) break;\n\n const tcPr = findChild(tc, \"w:tcPr\");\n const gridSpan = gridSpanOf(tc);\n const vMergeNode = tcPr ? findChild(tcPr, \"w:vMerge\") : undefined;\n const vMergeVal = vMergeNode ? (attr(vMergeNode, \"w:val\") ?? \"continue\") : null;\n\n if (vMergeNode && vMergeVal !== \"restart\") {\n // Continuation of a vertical merge. Cover this row, and bump the anchor's\n // rowspan if we can find it.\n const anchor = vMergeAnchorByCol.get(cursor);\n if (anchor) {\n const anchorIdx = anchor.row * cols + anchor.col;\n const a = slots[anchorIdx];\n if (a) {\n a.attrs = { ...(a.attrs ?? {}), rowspan: (a.attrs?.rowspan ?? 1) + 1 };\n }\n }\n for (let c = 0; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n cursor += gridSpan;\n continue;\n }\n\n // Real cell: build its delta + attrs.\n const delta = cellChildrenToDelta(tc, ctx);\n const text = textOfDelta(delta);\n const cellAttrs: CellAttrs = {};\n if (gridSpan > 1) cellAttrs.colspan = gridSpan;\n // rowspan starts at 1 — vMerge continuations bump it.\n\n const realCell: SerializedCell = { text, delta, attrs: cellAttrs };\n slots[r * cols + cursor] = realCell;\n\n // Mark horizontally-covered slots from gridSpan.\n for (let c = 1; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n\n if (vMergeVal === \"restart\") {\n vMergeAnchorByCol.set(cursor, { row: r, col: cursor });\n } else {\n vMergeAnchorByCol.delete(cursor);\n }\n cursor += gridSpan;\n }\n\n // Fill any trailing un-touched slots in this row with empty placeholders so\n // the row-major invariant holds (length = rows * cols).\n for (let c = 0; c < cols; c++) {\n if (slots[r * cols + c] === null) {\n slots[r * cols + c] = emptyCell();\n }\n }\n }\n\n const cells: SerializedCell[] = slots.map((s) => s ?? emptyCell());\n\n const attrs: BlockAttrs = { rows, cols };\n return { type: \"table\", text: \"\", delta: [], attrs, cells };\n}\n\nfunction gridSpanOf(tc: OoxmlNode): number {\n const tcPr = findChild(tc, \"w:tcPr\");\n if (!tcPr) return 1;\n const gs = findChild(tcPr, \"w:gridSpan\");\n if (!gs) return 1;\n const n = parseIntAttr(attr(gs, \"w:val\"), 1);\n return Math.max(1, n);\n}\n\nfunction cellChildrenToDelta(tc: OoxmlNode, ctx: RunContext): DeltaOp[] {\n // A cell can contain multiple paragraphs; we concat their deltas with a\n // newline between adjacent ones to preserve visual separation. The cell\n // model in @sofereditor/core is a single Y.Text, so we flatten.\n const out: DeltaOp[] = [];\n let first = true;\n for (const child of childrenOf(tc)) {\n if (tagOf(child) !== \"w:p\") continue;\n if (!first) out.push({ insert: \"\\n\" });\n first = false;\n out.push(...paragraphChildrenToDelta(childrenOf(child), ctx));\n }\n return out;\n}\n\nfunction textOfDelta(delta: DeltaOp[]): string {\n let out = \"\";\n for (const op of delta) {\n if (typeof op.insert === \"string\") out += op.insert;\n }\n return out;\n}\n\nfunction coveredCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: { covered: true } };\n}\n\nfunction emptyCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: {} };\n}\n","import type { ListKind } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * Resolves per-paragraph `<w:numPr>` into the editor's `{listKind, listLevel}`.\n *\n * docx numbering is two-step:\n * 1. paragraphs reference a `numId` and an `ilvl`\n * 2. each `<w:num numId=\"...\">` points to a `<w:abstractNum>` via `<w:abstractNumId>`\n * 3. each `<w:lvl ilvl=\"...\">` declares a `<w:numFmt val=\"bullet|decimal|...\">`\n *\n * The export side uses two hardcoded reference names (\"ed-bullet\" / \"ed-ordered\"),\n * but Word-authored documents use arbitrary `numId`s — we resolve by reading the\n * `numFmt` of the target level.\n */\nexport class NumberingResolver {\n private numIdToAbstract = new Map<string, string>();\n private abstractLevelFmt = new Map<string, Map<number, string>>();\n\n constructor(numberingXml: OoxmlNode[] | undefined) {\n if (!numberingXml) return;\n for (const top of numberingXml) {\n if (tagOf(top) !== \"w:numbering\") continue;\n for (const child of childrenOf(top)) {\n const tag = tagOf(child);\n if (tag === \"w:abstractNum\") {\n const abstractId = attr(child, \"w:abstractNumId\");\n if (!abstractId) continue;\n const levels = new Map<number, string>();\n for (const lvl of findChildren(child, \"w:lvl\")) {\n const ilvl = parseIntAttr(attr(lvl, \"w:ilvl\"), 0);\n const fmt = attr(findChild(lvl, \"w:numFmt\") ?? {}, \"w:val\");\n if (fmt) levels.set(ilvl, fmt);\n }\n this.abstractLevelFmt.set(abstractId, levels);\n } else if (tag === \"w:num\") {\n const numId = attr(child, \"w:numId\");\n const abstractRef = findChild(child, \"w:abstractNumId\");\n const abstractId = abstractRef ? attr(abstractRef, \"w:val\") : undefined;\n if (numId && abstractId) this.numIdToAbstract.set(numId, abstractId);\n }\n }\n }\n }\n\n /** Returns `{listKind, listLevel}` for a given paragraph's `<w:numPr>` node, or null. */\n resolve(numPr: OoxmlNode | undefined): { listKind: ListKind; listLevel: number } | null {\n if (!numPr) return null;\n const ilvlRef = findChild(numPr, \"w:ilvl\");\n const numIdRef = findChild(numPr, \"w:numId\");\n if (!numIdRef) return null;\n const numId = attr(numIdRef, \"w:val\");\n if (!numId) return null;\n const listLevel = clampLevel(parseIntAttr(ilvlRef ? attr(ilvlRef, \"w:val\") : undefined, 0));\n const abstractId = this.numIdToAbstract.get(numId);\n if (abstractId == null) {\n // Numbering refs the export side defines without a numbering.xml entry visible\n // to us (the export-docx pipeline generates them dynamically via the docx lib).\n // Fall back by inspecting the numId itself when it is a known sentinel.\n return { listKind: \"bullet\", listLevel };\n }\n const levels = this.abstractLevelFmt.get(abstractId);\n const fmt = (levels?.get(listLevel) ?? \"bullet\").toLowerCase();\n const listKind: ListKind = fmt === \"bullet\" || fmt === \"none\" ? \"bullet\" : \"ordered\";\n return { listKind, listLevel };\n }\n}\n\nfunction clampLevel(n: number): number {\n if (!Number.isFinite(n)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(n)));\n}\n","import { detectPreset, mmToPx, type PageSettings } from \"@sofereditor/core\";\nimport { attr, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * 1 inch = 1440 twips = 25.4 mm. So 1 twip = 25.4/1440 mm.\n *\n * Page size lives in `<w:sectPr><w:pgSz w:w=\"...\" w:h=\"...\"/></w:sectPr>` and\n * margins in `<w:pgMar w:top=\"...\" w:bottom=\"...\" w:left=\"...\" w:right=\"...\"/>`,\n * all in twips. This is the inverse of `sectionPropertiesFor` in\n * `@sofereditor/export-docx`.\n */\nconst MM_PER_TWIP = 25.4 / 1440;\n\nfunction twipsToPx(twips: number): number {\n return mmToPx(twips * MM_PER_TWIP);\n}\n\nexport function sectPrToPageSettings(\n sectPr: OoxmlNode | undefined,\n): PageSettings | undefined {\n if (!sectPr) return undefined;\n const pgSz = findChild(sectPr, \"w:pgSz\");\n const pgMar = findChild(sectPr, \"w:pgMar\");\n if (!pgSz && !pgMar) return undefined;\n\n const widthTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:w\"), 0) : 0;\n const heightTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:h\"), 0) : 0;\n const topTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:top\"), 0) : 0;\n const bottomTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:bottom\"), 0) : 0;\n const leftTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:left\"), 0) : 0;\n const rightTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:right\"), 0) : 0;\n\n if (widthTwips <= 0 && heightTwips <= 0 && topTwips <= 0 && leftTwips <= 0) {\n return undefined;\n }\n\n const width = widthTwips > 0 ? twipsToPx(widthTwips) : 794;\n const height = heightTwips > 0 ? twipsToPx(heightTwips) : 1123;\n const settings: PageSettings = {\n width,\n height,\n marginTop: twipsToPx(topTwips || 0),\n marginBottom: twipsToPx(bottomTwips || 0),\n marginLeft: twipsToPx(leftTwips || 0),\n marginRight: twipsToPx(rightTwips || 0),\n };\n settings.preset = detectPreset(settings.width, settings.height);\n return settings;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,eAA8E;;;ACA9E,mBAAkB;AAClB,6BAA0B;AAe1B,IAAM,SAAS,IAAI,iCAAU;AAAA,EAC3B,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,YAAY;AAAA;AAAA,EACZ,qBAAqB;AAAA,EACrB,eAAe;AACjB,CAAC;AAWD,eAAsB,SAAS,OAA2D;AACxF,QAAM,OACJ,iBAAiB,aACb,QACA,iBAAiB,cACf,IAAI,WAAW,KAAK,IACpB,IAAI,WAAW,MAAO,MAAe,YAAY,CAAC;AAE1D,QAAM,MAAM,MAAM,aAAAC,QAAM,UAAU,IAAI;AACtC,QAAM,cAAc,MAAM,QAAQ,KAAK,mBAAmB;AAC1D,MAAI,CAAC,YAAa,OAAM,IAAI,MAAM,0CAA0C;AAE5E,QAAM,eAAe,MAAM,QAAQ,KAAK,oBAAoB;AAC5D,QAAM,UAAU,MAAM,QAAQ,KAAK,8BAA8B;AACjE,QAAM,gBAAgB,mBAAmB,OAAO;AAEhD,QAAM,aAAa,oBAAI,IAAwB;AAC/C,QAAM,cAAc,IAAI,OAAO,YAAY;AAC3C,MAAI,aAAa;AACf,UAAM,WAA4B,CAAC;AACnC,gBAAY,QAAQ,CAAC,cAAc,SAAS;AAC1C,UAAI,KAAK,IAAK;AACd,eAAS;AAAA,QACP,KAAK,MAAM,YAAY,EAAE,KAAK,CAAC,UAAU;AACvC,qBAAW,IAAI,SAAS,YAAY,IAAI,KAAK;AAAA,QAC/C,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AACD,UAAM,QAAQ,IAAI,QAAQ;AAAA,EAC5B;AAEA,SAAO,EAAE,aAAa,cAAc,eAAe,WAAW;AAChE;AAEA,eAAe,QAAQ,KAAY,MAAgD;AACjF,QAAM,QAAQ,IAAI,KAAK,IAAI;AAC3B,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,MAAM,MAAM,MAAM,QAAQ;AACvC,SAAO,OAAO,MAAM,IAAI;AAC1B;AAEA,SAAS,mBAAmB,OAAqD;AAC/E,QAAM,MAAM,oBAAI,IAAoB;AACpC,MAAI,CAAC,MAAO,QAAO;AAEnB,aAAW,OAAO,OAAO;AACvB,UAAM,WAAW,IAAI,eAAe;AACpC,QAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG;AAC9B,eAAW,SAAS,UAAyB;AAC3C,UAAI,EAAE,kBAAkB,OAAQ;AAChC,YAAM,QAAS,MAAM,IAAI,KAAK,CAAC;AAC/B,YAAM,KAAK,MAAM,MAAM;AACvB,YAAM,SAAS,MAAM,UAAU;AAC/B,UAAI,OAAO,OAAO,YAAY,OAAO,WAAW,UAAU;AACxD,YAAI,IAAI,IAAI,MAAM;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAKO,SAAS,MAAM,MAAgC;AACpD,aAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AACnC,QAAI,QAAQ,KAAM;AAClB,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGO,SAAS,WAAW,MAA8B;AACvD,QAAM,MAAM,MAAM,IAAI;AACtB,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,QAAM,IAAI,KAAK,GAAG;AAClB,SAAO,MAAM,QAAQ,CAAC,IAAK,IAAoB,CAAC;AAClD;AAGO,SAAS,QAAQ,MAA0C;AAChE,SAAS,KAAK,IAAI,KAAK,CAAC,KAAkC,CAAC;AAC7D;AAGO,SAAS,KAAK,MAAiB,KAAiC;AACrE,QAAM,IAAI,QAAQ,IAAI,EAAE,KAAK,GAAG,EAAE;AAClC,SAAO,OAAO,MAAM,WAAW,IAAI;AACrC;AAGO,SAAS,UAAU,MAAiB,KAAoC;AAC7E,aAAW,SAAS,WAAW,IAAI,GAAG;AACpC,QAAI,MAAM,KAAK,MAAM,IAAK,QAAO;AAAA,EACnC;AACA,SAAO;AACT;AAGO,SAAS,aAAa,MAAiB,KAA0B;AACtE,QAAM,MAAmB,CAAC;AAC1B,aAAW,SAAS,WAAW,IAAI,GAAG;AACpC,QAAI,MAAM,KAAK,MAAM,IAAK,KAAI,KAAK,KAAK;AAAA,EAC1C;AACA,SAAO;AACT;;;ACtIO,SAAS,kBAAkB,YAAwC;AACxE,MAAI,CAAC,OAAO,SAAS,UAAU,KAAK,cAAc,EAAG,QAAO;AAC5D,QAAM,KAAK,aAAa;AACxB,SAAO,GAAG,mBAAmB,EAAE,CAAC;AAClC;AAGO,SAAS,kBAAkB,KAA6C;AAC7E,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,IAAI,iBAAiB,KAAK,IAAI,KAAK,CAAC;AAC1C,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,IAAI,IAAI,YAAY,CAAC;AAC9B;AAGO,SAAS,WAAW,KAA8E;AACvG,MAAI,CAAC,IAAK,QAAO;AACjB,UAAQ,IAAI,YAAY,GAAG;AAAA,IACzB,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAGO,SAAS,aAAa,KAAc,WAAW,GAAW;AAC/D,MAAI,OAAO,QAAQ,YAAY,OAAO,SAAS,GAAG,EAAG,QAAO,KAAK,MAAM,GAAG;AAC1E,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,IAAI,OAAO,SAAS,KAAK,EAAE;AACjC,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAEA,SAAS,mBAAmB,GAAmB;AAE7C,SAAO,OAAO,UAAU,CAAC,IAAI,EAAE,SAAS,IAAI,EAAE,QAAQ,CAAC,EAAE,QAAQ,UAAU,EAAE;AAC/E;;;AC/CA,IAAM,aAAa;AAEnB,IAAM,cAAsC;AAAA,EAC1C,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAYO,SAAS,wBACd,SACA,KACmB;AAInB,MAAI;AACJ,MAAI;AACJ,MAAI;AAEJ,OAAK,SAAS,CAAC,MAAM;AACnB,UAAM,MAAM,MAAM,CAAC;AACnB,QAAI,CAAC,IAAK;AACV,QAAI,QAAQ,UAAU;AACpB,YAAM,KAAK,KAAK,GAAG,SAAS,KAAK,KAAK,GAAG,QAAQ;AACjD,UAAI,GAAI,WAAU;AAAA,IACpB,WAAW,QAAQ,aAAa;AAC9B,WAAK,aAAa,KAAK,GAAG,IAAI,CAAC;AAC/B,WAAK,aAAa,KAAK,GAAG,IAAI,CAAC;AAAA,IACjC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,SAAS,IAAI,cAAc,IAAI,OAAO;AAC5C,MAAI,CAAC,OAAQ,QAAO;AAGpB,QAAM,QAAQ,IAAI,WAAW,IAAI,MAAM;AACvC,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,MAAM,YAAY,MAAM;AAC9B,QAAM,OAAO,YAAY,GAAG,KAAK;AACjC,QAAM,MAAM,QAAQ,IAAI,WAAW,cAAc,KAAK,CAAC;AACvD,QAAM,UAAU,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,UAAU,CAAC,IAAI;AACxE,QAAM,WAAW,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,UAAU,CAAC,IAAI;AAEzE,SAAO,EAAE,MAAM,SAAS,KAAK,OAAO,SAAS,QAAQ,SAAS;AAChE;AAEA,SAAS,KAAK,MAAiB,OAAqC;AAClE,QAAM,IAAI;AACV,aAAW,SAAS,WAAW,IAAI,GAAG;AACpC,QAAI,WAAW,MAAO;AACtB,SAAK,OAAO,KAAK;AAAA,EACnB;AACF;AAEA,SAAS,YAAY,MAAsB;AACzC,QAAM,IAAI,kBAAkB,KAAK,IAAI;AACrC,SAAO,IAAI,EAAE,CAAC,EAAE,YAAY,IAAI;AAClC;AAEA,SAAS,cAAc,OAA2B;AAEhD,MAAI,OAAO,SAAS,YAAY;AAC9B,QAAI,MAAM;AACV,UAAM,QAAQ;AACd,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,OAAO;AAC5C,aAAO,OAAO,aAAa,MAAM,MAAM,MAAM,KAAK,MAAM,SAAS,GAAG,IAAI,KAAK,CAAC,CAAC;AAAA,IACjF;AACA,WAAO,KAAK,GAAG;AAAA,EACjB;AAEA,SAAO,OAAO,KAAK,KAAK,EAAE,SAAS,QAAQ;AAC7C;;;ACxEO,SAAS,yBAAyB,UAAuB,KAA4B;AAC1F,QAAM,MAAiB,CAAC;AACxB,aAAW,SAAS,UAAU;AAC5B,UAAM,MAAM,MAAM,KAAK;AACvB,QAAI,CAAC,IAAK;AACV,QAAI,QAAQ,QAAS;AACrB,QAAI,QAAQ,OAAO;AACjB,iBAAW,OAAO,KAAK,QAAW,GAAG;AAAA,IACvC,WAAW,QAAQ,eAAe;AAEhC,YAAM,MAAM,KAAK,OAAO,MAAM;AAC9B,YAAM,OAAO,MAAM,IAAI,cAAc,IAAI,GAAG,IAAI;AAChD,YAAM,WAAkC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI;AACpE,iBAAW,OAAO,WAAW,KAAK,GAAG;AACnC,YAAI,MAAM,GAAG,MAAM,MAAO,YAAW,KAAK,KAAK,UAAU,GAAG;AAAA,MAC9D;AAAA,IACF;AAAA,EACF;AACA,SAAO,cAAc,GAAG;AAC1B;AAEA,SAAS,WACP,KACA,KACA,eACA,KACM;AACN,QAAM,QAAQ,WAAW,eAAe,SAAS,UAAU,KAAK,OAAO,CAAC,CAAC;AACzE,aAAW,SAAS,WAAW,GAAG,GAAG;AACnC,UAAM,MAAM,MAAM,KAAK;AACvB,QAAI,CAAC,IAAK;AACV,QAAI,QAAQ,OAAO;AACjB,YAAM,OAAO,YAAY,KAAK;AAC9B,UAAI,KAAK,SAAS,EAAG,MAAK,KAAK,MAAM,KAAK;AAAA,IAC5C,WAAW,QAAQ,SAAS;AAC1B,WAAK,KAAK,KAAM,KAAK;AAAA,IACvB,WAAW,QAAQ,QAAQ;AAGzB,WAAK,KAAK,MAAM,KAAK;AAAA,IACvB,WAAW,QAAQ,aAAa;AAC9B,YAAM,QAAQ,wBAAwB,OAAO,GAAG;AAChD,UAAI,MAAO,KAAI,KAAK,EAAE,QAAQ,MAAM,CAAC;AAAA,IACvC;AAAA,EAEF;AACF;AAEA,SAAS,SAAS,KAAuC;AACvD,QAAM,IAAe,CAAC;AACtB,MAAI,CAAC,IAAK,QAAO;AACjB,aAAW,SAAS,WAAW,GAAG,GAAG;AACnC,UAAM,MAAM,MAAM,KAAK;AACvB,QAAI,CAAC,IAAK;AACV,YAAQ,KAAK;AAAA,MACX,KAAK;AACH,YAAI,CAAC,QAAQ,KAAK,OAAO,OAAO,CAAC,EAAG,GAAE,OAAO;AAC7C;AAAA,MACF,KAAK;AACH,YAAI,CAAC,QAAQ,KAAK,OAAO,OAAO,CAAC,EAAG,GAAE,SAAS;AAC/C;AAAA,MACF,KAAK,OAAO;AACV,cAAM,MAAM,KAAK,OAAO,OAAO;AAC/B,YAAI,OAAO,IAAI,YAAY,MAAM,OAAQ,GAAE,YAAY;AACvD;AAAA,MACF;AAAA,MACA,KAAK;AACH,YAAI,CAAC,QAAQ,KAAK,OAAO,OAAO,CAAC,EAAG,GAAE,SAAS;AAC/C;AAAA,MACF,KAAK,WAAW;AACd,cAAM,MAAM,KAAK,OAAO,OAAO;AAC/B,cAAM,MAAM,kBAAkB,GAAG;AACjC,YAAI,IAAK,GAAE,QAAQ;AACnB;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAIA,KAAK,QAAQ;AACX,cAAM,UAAU,OAAO,WAAW,KAAK,OAAO,OAAO,KAAK,EAAE;AAC5D,cAAM,MAAM,kBAAkB,OAAO;AACrC,YAAI,IAAK,GAAE,WAAW;AACtB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,WAAW,GAA0B,GAAyB;AACrE,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,EAAE,GAAG,GAAG,GAAG,EAAE;AACtB;AAEA,SAAS,YAAY,MAAyB;AAC5C,MAAI,MAAM;AACV,aAAW,SAAS,WAAW,IAAI,GAAG;AACpC,QAAI,WAAW,OAAO;AACpB,YAAM,IAAI,MAAM,OAAO;AACvB,UAAI,OAAO,MAAM,SAAU,QAAO;AAAA,IACpC;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,QAAQ,KAAkC;AACjD,MAAI,OAAO,KAAM,QAAO;AACxB,QAAM,IAAI,IAAI,YAAY;AAC1B,SAAO,MAAM,OAAO,MAAM,WAAW,MAAM;AAC7C;AAEA,SAAS,KAAK,KAAgB,MAAc,OAAwB;AAClE,QAAM,KACJ,OAAO,KAAK,KAAK,EAAE,WAAW,IAAI,EAAE,QAAQ,KAAK,IAAI,EAAE,QAAQ,MAAM,YAAY,EAAE,GAAG,MAAM,EAAE;AAChG,MAAI,KAAK,EAAE;AACb;AAEA,SAAS,cAAc,KAA2B;AAChD,QAAM,MAAiB,CAAC;AACxB,aAAW,MAAM,KAAK;AACpB,UAAM,OAAO,IAAI,IAAI,SAAS,CAAC;AAC/B,QACE,QACA,OAAO,KAAK,WAAW,YACvB,OAAO,GAAG,WAAW,YACrB,UAAU,KAAK,YAAY,GAAG,UAAU,GACxC;AACA,WAAK,SAAS,KAAK,SAAS,GAAG;AAC/B;AAAA,IACF;AACA,QAAI,KAAK,EAAE;AAAA,EACb;AACA,SAAO;AACT;AAEA,SAAS,UAAU,GAA0B,GAAmC;AAC9E,QAAM,KAAK,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AACxC,QAAM,KAAK,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AACxC,MAAI,GAAG,WAAW,GAAG,OAAQ,QAAO;AACpC,WAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,KAAK;AAClC,QAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAG,QAAO;AAC5B,UAAM,KAAM,EAA8B,GAAG,CAAC,CAAC;AAC/C,UAAM,KAAM,EAA8B,GAAG,CAAC,CAAC;AAC/C,QAAI,OAAO,OAAO,YAAY,OAAO,QAAQ,OAAO,OAAO,YAAY,OAAO,MAAM;AAClF,UAAI,KAAK,UAAU,EAAE,MAAM,KAAK,UAAU,EAAE,EAAG,QAAO;AAAA,IACxD,WAAW,OAAO,IAAI;AACpB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;ACvJO,SAAS,iBACd,GACA,KACA,WACiB;AACjB,QAAM,MAAM,UAAU,GAAG,OAAO;AAChC,QAAM,QAAQ,MAAM,UAAU,KAAK,SAAS,IAAI;AAChD,QAAM,UAAU,MAAM,KAAK,UAAU,KAAK,UAAU,KAAK,CAAC,GAAG,OAAO,IAAI;AAExE,QAAM,QAAQ,yBAAyB,WAAW,CAAC,GAAG,GAAG;AACzD,QAAM,OAAO,YAAY,KAAK;AAC9B,QAAM,QAAQ,WAAW,MAAM,KAAK,UAAU,KAAK,MAAM,KAAK,CAAC,GAAG,OAAO,IAAI,MAAS;AACtF,QAAM,QAAQ,MAAM,CAAC,CAAC,UAAU,KAAK,QAAQ,IAAI;AAGjD,MAAI,OAAO;AACT,UAAM,WAAW,UAAU,QAAQ,KAAK;AACxC,UAAMC,SAAoB;AAAA,MACxB,UAAU,UAAU,YAAY;AAAA,MAChC,WAAW,UAAU,aAAa;AAAA,IACpC;AACA,QAAI,MAAO,CAAAA,OAAM,QAAQ;AACzB,QAAI,MAAO,CAAAA,OAAM,MAAM;AACvB,WAAO,EAAE,MAAM,YAAY,MAAM,OAAO,OAAAA,OAAM;AAAA,EAChD;AAGA,QAAM,eAAe,UAAU,mBAAmB,OAAO,IAAI;AAC7D,MAAI,gBAAgB,MAAM;AACxB,UAAMA,SAAoB,EAAE,OAAO,aAAa;AAChD,QAAI,MAAO,CAAAA,OAAM,QAAQ;AACzB,QAAI,MAAO,CAAAA,OAAM,MAAM;AACvB,WAAO,EAAE,MAAM,WAAW,MAAM,OAAO,OAAAA,OAAM;AAAA,EAC/C;AAGA,MAAI,OAAO,cAAc,GAAG,GAAG;AAC7B,UAAMA,SAAoB,CAAC;AAC3B,QAAI,MAAO,CAAAA,OAAM,QAAQ;AACzB,QAAI,MAAO,CAAAA,OAAM,MAAM;AACvB,WAAO,EAAE,MAAM,cAAc,MAAM,OAAO,OAAAA,OAAM;AAAA,EAClD;AAGA,MAAI,OAAO,oBAAoB,GAAG,GAAG;AACnC,UAAMA,SAAoB,CAAC;AAC3B,QAAI,MAAO,CAAAA,OAAM,QAAQ;AACzB,QAAI,MAAO,CAAAA,OAAM,MAAM;AACvB,WAAO,EAAE,MAAM,aAAa,MAAM,OAAO,OAAAA,OAAM;AAAA,EACjD;AAEA,QAAM,QAAoB,CAAC;AAC3B,MAAI,MAAO,OAAM,QAAQ;AACzB,MAAI,MAAO,OAAM,MAAM;AACvB,SAAO,EAAE,MAAM,aAAa,MAAM,OAAO,MAAM;AACjD;AAEA,SAAS,YAAY,OAA0B;AAC7C,MAAI,MAAM;AACV,aAAW,MAAM,OAAO;AACtB,QAAI,OAAO,GAAG,WAAW,SAAU,QAAO,GAAG;AAAA,EAC/C;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,IAA0C;AACpE,QAAM,IAAI,uBAAuB,KAAK,EAAE;AACxC,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,IAAI,OAAO,SAAS,EAAE,CAAC,GAAG,EAAE;AAClC,SAAQ,KAA+B;AACzC;AAEA,SAAS,cAAc,KAAyB;AAC9C,QAAM,OAAO,UAAU,KAAK,QAAQ;AACpC,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,CAAC,CAAC,UAAU,MAAM,QAAQ;AACnC;AAEA,SAAS,oBAAoB,KAAyB;AACpD,QAAM,MAAM,UAAU,KAAK,OAAO;AAClC,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,OAAO,KAAK,KAAK,QAAQ;AAC/B,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK,YAAY,MAAM;AAChC;;;ACpFO,SAAS,aAAa,KAAgB,KAAkC;AAC7E,QAAM,UAAU,UAAU,KAAK,WAAW;AAC1C,QAAM,WAAW,UAAU,aAAa,SAAS,WAAW,IAAI,CAAC;AACjE,QAAM,MAAM,aAAa,KAAK,MAAM;AAKpC,MAAI,OAAO,SAAS;AACpB,aAAW,MAAM,KAAK;AACpB,UAAM,MAAM,aAAa,IAAI,MAAM;AACnC,QAAI,QAAQ;AACZ,eAAW,MAAM,IAAK,UAAS,WAAW,EAAE;AAC5C,QAAI,QAAQ,KAAM,QAAO;AAAA,EAC3B;AACA,MAAI,OAAO,EAAG,QAAO;AACrB,QAAM,OAAO,IAAI;AAIjB,QAAM,QAAsC,IAAI,MAAM,OAAO,IAAI,EAAE,KAAK,IAAI;AAG5E,QAAM,oBAAoB,oBAAI,IAA0C;AAExE,WAAS,IAAI,GAAG,IAAI,MAAM,KAAK;AAC7B,UAAM,KAAK,IAAI,CAAC;AAChB,QAAI,SAAS;AACb,eAAW,MAAM,aAAa,IAAI,MAAM,GAAG;AAEzC,aAAO,SAAS,QAAQ,MAAM,IAAI,OAAO,MAAM,MAAM,KAAM;AAC3D,UAAI,UAAU,KAAM;AAEpB,YAAM,OAAO,UAAU,IAAI,QAAQ;AACnC,YAAM,WAAW,WAAW,EAAE;AAC9B,YAAM,aAAa,OAAO,UAAU,MAAM,UAAU,IAAI;AACxD,YAAM,YAAY,aAAc,KAAK,YAAY,OAAO,KAAK,aAAc;AAE3E,UAAI,cAAc,cAAc,WAAW;AAGzC,cAAM,SAAS,kBAAkB,IAAI,MAAM;AAC3C,YAAI,QAAQ;AACV,gBAAM,YAAY,OAAO,MAAM,OAAO,OAAO;AAC7C,gBAAM,IAAI,MAAM,SAAS;AACzB,cAAI,GAAG;AACL,cAAE,QAAQ,EAAE,GAAI,EAAE,SAAS,CAAC,GAAI,UAAU,EAAE,OAAO,WAAW,KAAK,EAAE;AAAA,UACvE;AAAA,QACF;AACA,iBAAS,IAAI,GAAG,IAAI,YAAY,SAAS,IAAI,MAAM,KAAK;AACtD,gBAAM,IAAI,OAAO,SAAS,CAAC,IAAI,YAAY;AAAA,QAC7C;AACA,kBAAU;AACV;AAAA,MACF;AAGA,YAAM,QAAQ,oBAAoB,IAAI,GAAG;AACzC,YAAM,OAAO,YAAY,KAAK;AAC9B,YAAM,YAAuB,CAAC;AAC9B,UAAI,WAAW,EAAG,WAAU,UAAU;AAGtC,YAAM,WAA2B,EAAE,MAAM,OAAO,OAAO,UAAU;AACjE,YAAM,IAAI,OAAO,MAAM,IAAI;AAG3B,eAAS,IAAI,GAAG,IAAI,YAAY,SAAS,IAAI,MAAM,KAAK;AACtD,cAAM,IAAI,OAAO,SAAS,CAAC,IAAI,YAAY;AAAA,MAC7C;AAEA,UAAI,cAAc,WAAW;AAC3B,0BAAkB,IAAI,QAAQ,EAAE,KAAK,GAAG,KAAK,OAAO,CAAC;AAAA,MACvD,OAAO;AACL,0BAAkB,OAAO,MAAM;AAAA,MACjC;AACA,gBAAU;AAAA,IACZ;AAIA,aAAS,IAAI,GAAG,IAAI,MAAM,KAAK;AAC7B,UAAI,MAAM,IAAI,OAAO,CAAC,MAAM,MAAM;AAChC,cAAM,IAAI,OAAO,CAAC,IAAI,UAAU;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAA0B,MAAM,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC;AAEjE,QAAM,QAAoB,EAAE,MAAM,KAAK;AACvC,SAAO,EAAE,MAAM,SAAS,MAAM,IAAI,OAAO,CAAC,GAAG,OAAO,MAAM;AAC5D;AAEA,SAAS,WAAW,IAAuB;AACzC,QAAM,OAAO,UAAU,IAAI,QAAQ;AACnC,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,KAAK,UAAU,MAAM,YAAY;AACvC,MAAI,CAAC,GAAI,QAAO;AAChB,QAAM,IAAI,aAAa,KAAK,IAAI,OAAO,GAAG,CAAC;AAC3C,SAAO,KAAK,IAAI,GAAG,CAAC;AACtB;AAEA,SAAS,oBAAoB,IAAe,KAA4B;AAItE,QAAM,MAAiB,CAAC;AACxB,MAAI,QAAQ;AACZ,aAAW,SAAS,WAAW,EAAE,GAAG;AAClC,QAAI,MAAM,KAAK,MAAM,MAAO;AAC5B,QAAI,CAAC,MAAO,KAAI,KAAK,EAAE,QAAQ,KAAK,CAAC;AACrC,YAAQ;AACR,QAAI,KAAK,GAAG,yBAAyB,WAAW,KAAK,GAAG,GAAG,CAAC;AAAA,EAC9D;AACA,SAAO;AACT;AAEA,SAAS,YAAY,OAA0B;AAC7C,MAAI,MAAM;AACV,aAAW,MAAM,OAAO;AACtB,QAAI,OAAO,GAAG,WAAW,SAAU,QAAO,GAAG;AAAA,EAC/C;AACA,SAAO;AACT;AAEA,SAAS,cAA8B;AACrC,SAAO,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,OAAO,EAAE,SAAS,KAAK,EAAE;AACzD;AAEA,SAAS,YAA4B;AACnC,SAAO,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE;AAC1C;;;AC/HO,IAAM,oBAAN,MAAwB;AAAA,EACrB,kBAAkB,oBAAI,IAAoB;AAAA,EAC1C,mBAAmB,oBAAI,IAAiC;AAAA,EAEhE,YAAY,cAAuC;AACjD,QAAI,CAAC,aAAc;AACnB,eAAW,OAAO,cAAc;AAC9B,UAAI,MAAM,GAAG,MAAM,cAAe;AAClC,iBAAW,SAAS,WAAW,GAAG,GAAG;AACnC,cAAM,MAAM,MAAM,KAAK;AACvB,YAAI,QAAQ,iBAAiB;AAC3B,gBAAM,aAAa,KAAK,OAAO,iBAAiB;AAChD,cAAI,CAAC,WAAY;AACjB,gBAAM,SAAS,oBAAI,IAAoB;AACvC,qBAAW,OAAO,aAAa,OAAO,OAAO,GAAG;AAC9C,kBAAM,OAAO,aAAa,KAAK,KAAK,QAAQ,GAAG,CAAC;AAChD,kBAAM,MAAM,KAAK,UAAU,KAAK,UAAU,KAAK,CAAC,GAAG,OAAO;AAC1D,gBAAI,IAAK,QAAO,IAAI,MAAM,GAAG;AAAA,UAC/B;AACA,eAAK,iBAAiB,IAAI,YAAY,MAAM;AAAA,QAC9C,WAAW,QAAQ,SAAS;AAC1B,gBAAM,QAAQ,KAAK,OAAO,SAAS;AACnC,gBAAM,cAAc,UAAU,OAAO,iBAAiB;AACtD,gBAAM,aAAa,cAAc,KAAK,aAAa,OAAO,IAAI;AAC9D,cAAI,SAAS,WAAY,MAAK,gBAAgB,IAAI,OAAO,UAAU;AAAA,QACrE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,QAAQ,OAAgF;AACtF,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,UAAU,UAAU,OAAO,QAAQ;AACzC,UAAM,WAAW,UAAU,OAAO,SAAS;AAC3C,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,QAAQ,KAAK,UAAU,OAAO;AACpC,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,YAAY,WAAW,aAAa,UAAU,KAAK,SAAS,OAAO,IAAI,QAAW,CAAC,CAAC;AAC1F,UAAM,aAAa,KAAK,gBAAgB,IAAI,KAAK;AACjD,QAAI,cAAc,MAAM;AAItB,aAAO,EAAE,UAAU,UAAU,UAAU;AAAA,IACzC;AACA,UAAM,SAAS,KAAK,iBAAiB,IAAI,UAAU;AACnD,UAAM,OAAO,QAAQ,IAAI,SAAS,KAAK,UAAU,YAAY;AAC7D,UAAM,WAAqB,QAAQ,YAAY,QAAQ,SAAS,WAAW;AAC3E,WAAO,EAAE,UAAU,UAAU;AAAA,EAC/B;AACF;AAEA,SAAS,WAAW,GAAmB;AACrC,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC;AAC/C;;;AC/EA,kBAAwD;AAYxD,IAAM,cAAc,OAAO;AAE3B,SAAS,UAAU,OAAuB;AACxC,aAAO,oBAAO,QAAQ,WAAW;AACnC;AAEO,SAAS,qBACd,QAC0B;AAC1B,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,OAAO,UAAU,QAAQ,QAAQ;AACvC,QAAM,QAAQ,UAAU,QAAQ,SAAS;AACzC,MAAI,CAAC,QAAQ,CAAC,MAAO,QAAO;AAE5B,QAAM,aAAa,OAAO,aAAa,KAAK,MAAM,KAAK,GAAG,CAAC,IAAI;AAC/D,QAAM,cAAc,OAAO,aAAa,KAAK,MAAM,KAAK,GAAG,CAAC,IAAI;AAChE,QAAM,WAAW,QAAQ,aAAa,KAAK,OAAO,OAAO,GAAG,CAAC,IAAI;AACjE,QAAM,cAAc,QAAQ,aAAa,KAAK,OAAO,UAAU,GAAG,CAAC,IAAI;AACvE,QAAM,YAAY,QAAQ,aAAa,KAAK,OAAO,QAAQ,GAAG,CAAC,IAAI;AACnE,QAAM,aAAa,QAAQ,aAAa,KAAK,OAAO,SAAS,GAAG,CAAC,IAAI;AAErE,MAAI,cAAc,KAAK,eAAe,KAAK,YAAY,KAAK,aAAa,GAAG;AAC1E,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,aAAa,IAAI,UAAU,UAAU,IAAI;AACvD,QAAM,SAAS,cAAc,IAAI,UAAU,WAAW,IAAI;AAC1D,QAAM,WAAyB;AAAA,IAC7B;AAAA,IACA;AAAA,IACA,WAAW,UAAU,YAAY,CAAC;AAAA,IAClC,cAAc,UAAU,eAAe,CAAC;AAAA,IACxC,YAAY,UAAU,aAAa,CAAC;AAAA,IACpC,aAAa,UAAU,cAAc,CAAC;AAAA,EACxC;AACA,WAAS,aAAS,0BAAa,SAAS,OAAO,SAAS,MAAM;AAC9D,SAAO;AACT;;;AR/BA,eAAsB,mBACpB,OAC6B;AAC7B,QAAM,OAAO,MAAM,SAAS,KAAK;AACjC,QAAM,YAAY,IAAI,kBAAkB,KAAK,YAAY;AACzD,QAAM,MAAkB;AAAA,IACtB,eAAe,KAAK;AAAA,IACpB,YAAY,KAAK;AAAA,EACnB;AAEA,QAAM,OAAO,SAAS,KAAK,WAAW;AACtC,MAAI,CAAC,KAAM,QAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,aAAa,MAAM,IAAI,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE,CAAC,EAAE;AAEpF,QAAM,SAA4B,CAAC;AACnC,aAAW,SAAS,WAAW,IAAI,GAAG;AACpC,UAAM,MAAM,MAAM,KAAK;AACvB,QAAI,QAAQ,OAAO;AACjB,aAAO,KAAK,iBAAiB,OAAO,KAAK,SAAS,CAAC;AAAA,IACrD,WAAW,QAAQ,SAAS;AAC1B,aAAO,KAAK,aAAa,OAAO,GAAG,CAAC;AAAA,IACtC;AAAA,EAEF;AAEA,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,KAAK,EAAE,MAAM,aAAa,MAAM,IAAI,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE,CAAC;AAAA,EACnE;AAEA,QAAM,eAAe,qBAAqB,UAAU,MAAM,UAAU,CAAC;AACrE,SAAO,eAAe,EAAE,QAAQ,aAAa,IAAI,EAAE,OAAO;AAC5D;AAGA,eAAsB,yBACpB,OACyB;AACzB,QAAM,aAAa,MAAM,mBAAmB,KAAK;AACjD,SAAO,4BAAe,SAAS,UAAU;AAC3C;AAEA,SAAS,SAAS,aAAiD;AACjE,aAAW,OAAO,aAAa;AAC7B,QAAI,MAAM,GAAG,MAAM,cAAc;AAC/B,YAAM,OAAO,UAAU,KAAK,QAAQ;AACpC,UAAI,KAAM,QAAO;AAAA,IACnB;AAAA,EACF;AACA,SAAO;AACT;","names":["import_core","JSZip","attrs"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/docx.ts","../src/parse-xml.ts","../src/units.ts","../src/images.ts","../src/runs.ts","../src/paragraphs.ts","../src/tables.ts","../src/numbering.ts","../src/page-settings.ts"],"sourcesContent":["export { docxBlobToDocument, docxBlobToEditorDocument } from \"./docx\";\n","import { EditorDocument, type SerializedBlock, type SerializedDocument } from \"@sofereditor/core\";\nimport { readDocx, childrenOf, tagOf, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { paragraphToBlock } from \"./paragraphs\";\nimport { tableToBlock } from \"./tables\";\nimport { NumberingResolver } from \"./numbering\";\nimport { sectPrToPageSettings } from \"./page-settings\";\nimport type { RunContext } from \"./runs\";\n\n/**\n * Read a `.docx` (OOXML) Blob/ArrayBuffer/Uint8Array and produce a\n * `SerializedDocument` ready to feed `EditorDocument.fromJSON` (or\n * `loadFromJSON` on an existing instance).\n *\n * The mapping is the inverse of `@sofereditor/export-docx`. Round-trip is exact for\n * documents the editor produced; documents authored externally (Word, Google\n * Docs export) load with marks/lists/tables/images recognized and unsupported\n * features dropped silently.\n */\nexport async function docxBlobToDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<SerializedDocument> {\n const file = await readDocx(input);\n const numbering = new NumberingResolver(file.numberingXml);\n const ctx: RunContext = {\n relationships: file.relationships,\n mediaBytes: file.mediaBytes,\n };\n\n const body = findBody(file.documentXml);\n if (!body) return { blocks: [{ type: \"paragraph\", text: \"\", delta: [], attrs: {} }] };\n\n const blocks: SerializedBlock[] = [];\n for (const child of childrenOf(body)) {\n const tag = tagOf(child);\n if (tag === \"w:p\") {\n blocks.push(paragraphToBlock(child, ctx, numbering));\n } else if (tag === \"w:tbl\") {\n blocks.push(tableToBlock(child, ctx));\n }\n // w:sectPr is parsed below (page settings); other body-level metadata ignored.\n }\n\n if (blocks.length === 0) {\n blocks.push({ type: \"paragraph\", text: \"\", delta: [], attrs: {} });\n }\n\n pruneNonLeaderListStarts(blocks);\n\n const pageSettings = sectPrToPageSettings(findChild(body, \"w:sectPr\"));\n return pageSettings ? { blocks, pageSettings } : { blocks };\n}\n\n/**\n * O Word numera por INSTÂNCIA (`numId`): parágrafos comuns entre itens não\n * resetam a contagem. O editor numera por ADJACÊNCIA: o renderer agrupa\n * `listItem`s consecutivos do mesmo `listKind`/`listStyle`, e um `listStart`\n * em item de CONTINUAÇÃO quebra o grupo ali. O parse gravou o ordinal Word em\n * `listStart` de TODOS os itens ordenados; este pós-passe o mantém apenas nos\n * LÍDERES de grupo (primeiro item, item após bloco comum, ou após item de\n * kind/estilo diferente) — assim o editor renderiza exatamente os números do\n * Word (\"3.\" depois de uma linha de resposta, \"b.\" em subitens) sem fragmentar\n * os grupos.\n */\nexport function pruneNonLeaderListStarts(blocks: SerializedBlock[]): void {\n // Estado do grupo de adjacência corrente: estilo do líder e último ordinal\n // visto por nível (para exigir sequência contígua — duas listas Word\n // independentes coladas, \"1,2,3\" + \"1,2\", NÃO podem fundir num 1..5).\n let grupoAtivo = false;\n let grupoStyle: unknown;\n let ordinais: number[] = [];\n\n const resetGrupo = () => {\n grupoAtivo = false;\n grupoStyle = undefined;\n ordinais = [];\n };\n\n for (const block of blocks) {\n if (block.type !== \"listItem\") {\n resetGrupo();\n continue;\n }\n if ((block.attrs.listKind ?? \"bullet\") !== \"ordered\") {\n // Bullet entre ordenados: mantém o grupo do renderer? Não — kind\n // diferente quebra o grupo lá também.\n resetGrupo();\n continue;\n }\n const level = block.attrs.listLevel ?? 0;\n const ordinal = typeof block.attrs.listStart === \"number\" ? block.attrs.listStart : 1;\n\n const mesmoNivelSequencial = ordinais[level] != null && ordinal === ordinais[level] + 1;\n const entrandoEmNivelNovo = ordinais[level] == null && level > 0 && ordinal === 1;\n const continuaGrupo =\n grupoAtivo &&\n block.attrs.listStyle === grupoStyle &&\n (mesmoNivelSequencial || entrandoEmNivelNovo);\n\n if (continuaGrupo) {\n delete block.attrs.listStart;\n } else {\n // Este item lidera um grupo novo no renderer (listStart preservado).\n grupoAtivo = true;\n grupoStyle = block.attrs.listStyle;\n ordinais = [];\n }\n ordinais[level] = ordinal;\n ordinais.length = level + 1;\n }\n}\n\n/** Convenience: parse → instantiate a fresh `EditorDocument`. */\nexport async function docxBlobToEditorDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<EditorDocument> {\n const serialized = await docxBlobToDocument(input);\n return EditorDocument.fromJSON(serialized);\n}\n\nfunction findBody(documentXml: OoxmlNode[]): OoxmlNode | undefined {\n for (const top of documentXml) {\n if (tagOf(top) === \"w:document\") {\n const body = findChild(top, \"w:body\");\n if (body) return body;\n }\n }\n return undefined;\n}\n","import JSZip from \"jszip\";\nimport { XMLParser } from \"fast-xml-parser\";\n\n/**\n * fast-xml-parser with `preserveOrder: true` produces a list of single-key\n * objects per element. Each element is either:\n *\n * - A tag node: `{ \"<tagName>\": <children-array>, \":@\"?: <attrs> }`\n * - A text node: `{ \"#text\": <string> }`\n *\n * Attributes are prefixed with `@_`. The `:@` key holds attributes for the\n * surrounding tag. Children preserve original document order, which matters\n * for paragraph runs (bold word followed by italic word).\n */\nexport type OoxmlNode = Record<string, unknown>;\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n preserveOrder: true,\n trimValues: false, // keep whitespace inside <w:t>\n parseAttributeValue: false,\n parseTagValue: false,\n});\n\nexport interface DocxFile {\n documentXml: OoxmlNode[];\n numberingXml?: OoxmlNode[];\n /** rId → target path (inside the zip), e.g. \"media/image1.png\". */\n relationships: Map<string, string>;\n /** zip path (without leading \"word/\") → bytes for embedded media. */\n mediaBytes: Map<string, Uint8Array>;\n}\n\nexport async function readDocx(input: Blob | ArrayBuffer | Uint8Array): Promise<DocxFile> {\n const data =\n input instanceof Uint8Array\n ? input\n : input instanceof ArrayBuffer\n ? new Uint8Array(input)\n : new Uint8Array(await (input as Blob).arrayBuffer());\n\n const zip = await JSZip.loadAsync(data);\n const documentXml = await readXml(zip, \"word/document.xml\");\n if (!documentXml) throw new Error(\"Invalid .docx: missing word/document.xml\");\n\n const numberingXml = await readXml(zip, \"word/numbering.xml\");\n const relsXml = await readXml(zip, \"word/_rels/document.xml.rels\");\n const relationships = parseRelationships(relsXml);\n\n const mediaBytes = new Map<string, Uint8Array>();\n const mediaFolder = zip.folder(\"word/media\");\n if (mediaFolder) {\n const promises: Promise<void>[] = [];\n mediaFolder.forEach((relativePath, file) => {\n if (file.dir) return;\n promises.push(\n file.async(\"uint8array\").then((bytes) => {\n mediaBytes.set(`media/${relativePath}`, bytes);\n }),\n );\n });\n await Promise.all(promises);\n }\n\n return { documentXml, numberingXml, relationships, mediaBytes };\n}\n\nasync function readXml(zip: JSZip, path: string): Promise<OoxmlNode[] | undefined> {\n const entry = zip.file(path);\n if (!entry) return undefined;\n const text = await entry.async(\"string\");\n return parser.parse(text) as OoxmlNode[];\n}\n\nfunction parseRelationships(nodes: OoxmlNode[] | undefined): Map<string, string> {\n const out = new Map<string, string>();\n if (!nodes) return out;\n // <Relationships><Relationship Id=\"rId1\" Type=\"...\" Target=\"media/image1.png\"/>...\n for (const top of nodes) {\n const children = top[\"Relationships\"];\n if (!Array.isArray(children)) continue;\n for (const child of children as OoxmlNode[]) {\n if (!(\"Relationship\" in child)) continue;\n const attrs = (child[\":@\"] ?? {}) as Record<string, unknown>;\n const id = attrs[\"@_Id\"];\n const target = attrs[\"@_Target\"];\n if (typeof id === \"string\" && typeof target === \"string\") {\n out.set(id, target);\n }\n }\n }\n return out;\n}\n\n// ---------- generic walkers over preserveOrder shape ----------\n\n/** Returns the tag name (e.g. \"w:p\") of a preserveOrder element, or null for text. */\nexport function tagOf(node: OoxmlNode): string | null {\n for (const key of Object.keys(node)) {\n if (key === \":@\") continue;\n return key;\n }\n return null;\n}\n\n/** Returns the children array of an element, or [] when none. */\nexport function childrenOf(node: OoxmlNode): OoxmlNode[] {\n const tag = tagOf(node);\n if (!tag) return [];\n const v = node[tag];\n return Array.isArray(v) ? (v as OoxmlNode[]) : [];\n}\n\n/** Returns the attribute bag (already with `@_` prefix) for an element. */\nexport function attrsOf(node: OoxmlNode): Record<string, unknown> {\n return ((node[\":@\"] ?? {}) as Record<string, unknown>) ?? {};\n}\n\n/** Read a single `@_key` attribute as a string. */\nexport function attr(node: OoxmlNode, key: string): string | undefined {\n const v = attrsOf(node)[`@_${key}`];\n return typeof v === \"string\" ? v : undefined;\n}\n\n/** Find the first direct child with the given tag name. */\nexport function findChild(node: OoxmlNode, tag: string): OoxmlNode | undefined {\n for (const child of childrenOf(node)) {\n if (tagOf(child) === tag) return child;\n }\n return undefined;\n}\n\n/** Find all direct children with the given tag name. */\nexport function findChildren(node: OoxmlNode, tag: string): OoxmlNode[] {\n const out: OoxmlNode[] = [];\n for (const child of childrenOf(node)) {\n if (tagOf(child) === tag) out.push(child);\n }\n return out;\n}\n\n/** Recursively collect text nodes' concatenated content. */\nexport function textOf(node: OoxmlNode): string {\n let out = \"\";\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) {\n const t = child[\"#text\"];\n if (typeof t === \"string\") out += t;\n } else {\n out += textOf(child);\n }\n }\n return out;\n}\n","/**\n * Unit conversions between OOXML and the editor's serialized format.\n * These are the inverses of the helpers in `@sofereditor/export-docx/src/docx.ts`.\n */\n\n/** docx half-points → CSS pt string. 24 (half-pt) → \"12pt\". */\nexport function halfPointsToCssPt(halfPoints: number): string | undefined {\n if (!Number.isFinite(halfPoints) || halfPoints <= 0) return undefined;\n const pt = halfPoints / 2;\n return `${stripTrailingZeros(pt)}pt`;\n}\n\n/** OOXML RRGGBB (no '#') → CSS hex with leading '#'. */\nexport function docxHexToCssColor(hex: string | undefined): string | undefined {\n if (!hex) return undefined;\n const m = /^[0-9a-f]{6}$/i.exec(hex.trim());\n if (!m) return undefined;\n return `#${hex.toLowerCase()}`;\n}\n\n/** OOXML alignment value → editor AlignValue. */\nexport function parseAlign(val: string | undefined): \"left\" | \"center\" | \"right\" | \"justify\" | undefined {\n if (!val) return undefined;\n switch (val.toLowerCase()) {\n case \"left\":\n case \"start\":\n return \"left\";\n case \"center\":\n return \"center\";\n case \"right\":\n case \"end\":\n return \"right\";\n case \"both\":\n case \"distribute\":\n case \"justify\":\n return \"justify\";\n default:\n return undefined;\n }\n}\n\n/** Parse XML attribute that should be a positive integer. */\nexport function parseIntAttr(val: unknown, fallback = 0): number {\n if (typeof val === \"number\" && Number.isFinite(val)) return Math.trunc(val);\n if (typeof val !== \"string\") return fallback;\n const n = Number.parseInt(val, 10);\n return Number.isFinite(n) ? n : fallback;\n}\n\nfunction stripTrailingZeros(n: number): string {\n // 12 → \"12\", 12.5 → \"12.5\"\n return Number.isInteger(n) ? n.toString() : n.toFixed(2).replace(/\\.?0+$/, \"\");\n}\n","import type { ImageEmbed } from \"@sofereditor/core\";\nimport { attr, childrenOf, tagOf, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/** 1 inch = 914400 EMU = 96 px → 1 px = 9525 EMU. */\nconst EMU_PER_PX = 9525;\n\nconst MIME_BY_EXT: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n bmp: \"image/bmp\",\n svg: \"image/svg+xml\",\n};\n\nexport interface ImageContext {\n relationships: Map<string, string>;\n mediaBytes: Map<string, Uint8Array>;\n}\n\n/**\n * Walk a `<w:drawing>` node and return an `ImageEmbed` if it contains a\n * recognized inline picture. Returns null for unsupported drawings (shapes,\n * charts, etc.).\n */\nexport function extractImageFromDrawing(\n drawing: OoxmlNode,\n ctx: ImageContext,\n): ImageEmbed | null {\n // The interesting bits live deep inside: a:blip carries `r:embed`, and\n // wp:extent carries cx/cy. We do a generic walk because the wrapping nodes\n // differ between inline (`wp:inline`) and anchored (`wp:anchor`) drawings.\n let embedId: string | undefined;\n let cx: number | undefined;\n let cy: number | undefined;\n\n walk(drawing, (n) => {\n const tag = tagOf(n);\n if (!tag) return;\n if (tag === \"a:blip\") {\n const id = attr(n, \"r:embed\") ?? attr(n, \"r:link\");\n if (id) embedId = id;\n } else if (tag === \"wp:extent\") {\n cx = parseIntAttr(attr(n, \"cx\"));\n cy = parseIntAttr(attr(n, \"cy\"));\n }\n });\n\n if (!embedId) return null;\n const target = ctx.relationships.get(embedId);\n if (!target) return null;\n // Relationship targets are typically \"media/image1.png\"; the zip lookup uses\n // the same path (we stored bytes keyed under \"media/<file>\").\n const bytes = ctx.mediaBytes.get(target);\n if (!bytes) return null;\n\n const ext = extensionOf(target);\n const mime = MIME_BY_EXT[ext] ?? \"image/png\";\n const src = `data:${mime};base64,${bytesToBase64(bytes)}`;\n const widthPx = cx != null ? Math.max(1, Math.round(cx / EMU_PER_PX)) : 100;\n const heightPx = cy != null ? Math.max(1, Math.round(cy / EMU_PER_PX)) : 100;\n\n return { type: \"image\", src, width: widthPx, height: heightPx };\n}\n\nfunction walk(node: OoxmlNode, visit: (n: OoxmlNode) => void): void {\n visit(node);\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) continue;\n walk(child, visit);\n }\n}\n\nfunction extensionOf(path: string): string {\n const m = /\\.([a-z0-9]+)$/i.exec(path);\n return m ? m[1].toLowerCase() : \"\";\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n // Browser path.\n if (typeof btoa === \"function\") {\n let bin = \"\";\n const CHUNK = 0x8000;\n for (let i = 0; i < bytes.length; i += CHUNK) {\n bin += String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + CHUNK)));\n }\n return btoa(bin);\n }\n // Node fallback.\n return Buffer.from(bytes).toString(\"base64\");\n}\n","import type { DeltaOp, MarkAttrs } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { docxHexToCssColor, halfPointsToCssPt } from \"./units\";\nimport { extractImageFromDrawing, type ImageContext } from \"./images\";\n\nexport interface RunContext extends ImageContext {}\n\n/**\n * Walk a paragraph's children (post `<w:pPr>`) and emit a `DeltaOp[]`. Combines\n * adjacent runs that share the same mark set into a single op for parity with\n * the export — Y.Text.applyDelta would merge them anyway, but pre-merging keeps\n * round-trip output stable.\n */\nexport function paragraphChildrenToDelta(children: OoxmlNode[], ctx: RunContext): DeltaOp[] {\n const ops: DeltaOp[] = [];\n for (const child of children) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:pPr\") continue;\n if (tag === \"w:r\") {\n collectRun(child, ctx, undefined, ops);\n } else if (tag === \"w:hyperlink\") {\n // hyperlinks carry an r:id that maps via relationships to the href.\n const rId = attr(child, \"r:id\");\n const href = rId ? ctx.relationships.get(rId) : undefined;\n const linkMark: MarkAttrs | undefined = href ? { link: { href } } : undefined;\n for (const sub of childrenOf(child)) {\n if (tagOf(sub) === \"w:r\") collectRun(sub, ctx, linkMark, ops);\n }\n }\n }\n return mergeAdjacent(ops);\n}\n\nfunction collectRun(\n run: OoxmlNode,\n ctx: RunContext,\n wrappingMarks: MarkAttrs | undefined,\n out: DeltaOp[],\n): void {\n const marks = mergeMarks(wrappingMarks, runMarks(findChild(run, \"w:rPr\")));\n for (const child of childrenOf(run)) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:t\") {\n const text = textContent(child);\n if (text.length > 0) push(out, text, marks);\n } else if (tag === \"w:tab\") {\n push(out, \"\\t\", marks);\n } else if (tag === \"w:br\") {\n // Treat w:br type=\"page\" as also a soft break for the model — we have no\n // explicit page-break node in the schema; pagination is computed live.\n push(out, \"\\n\", marks);\n } else if (tag === \"w:drawing\") {\n const embed = extractImageFromDrawing(child, ctx);\n if (embed) out.push({ insert: embed });\n }\n // Other run children (w:sym, w:fldChar, etc.) are silently dropped.\n }\n}\n\n/**\n * Os valores nomeados de `w:highlight` do OOXML. O Word só aceita esta lista\n * fechada — documentos com cor arbitrária usam `w:shd`, que é o que o nosso\n * export emite. `none` fica de fora de propósito: ausência = sem marca.\n */\nconst DOCX_HIGHLIGHT_COLORS: Record<string, string> = {\n yellow: \"#ffff00\",\n green: \"#00ff00\",\n cyan: \"#00ffff\",\n magenta: \"#ff00ff\",\n blue: \"#0000ff\",\n red: \"#ff0000\",\n darkblue: \"#000080\",\n darkcyan: \"#008080\",\n darkgreen: \"#008000\",\n darkmagenta: \"#800080\",\n darkred: \"#800000\",\n darkyellow: \"#808000\",\n darkgray: \"#808080\",\n lightgray: \"#c0c0c0\",\n black: \"#000000\",\n white: \"#ffffff\",\n};\n\nfunction runMarks(rPr: OoxmlNode | undefined): MarkAttrs {\n const m: MarkAttrs = {};\n if (!rPr) return m;\n for (const child of childrenOf(rPr)) {\n const tag = tagOf(child);\n if (!tag) continue;\n switch (tag) {\n case \"w:b\":\n if (!isFalse(attr(child, \"w:val\"))) m.bold = true;\n break;\n case \"w:i\":\n if (!isFalse(attr(child, \"w:val\"))) m.italic = true;\n break;\n case \"w:u\": {\n const val = attr(child, \"w:val\");\n if (val && val.toLowerCase() !== \"none\") m.underline = true;\n break;\n }\n case \"w:strike\":\n if (!isFalse(attr(child, \"w:val\"))) m.strike = true;\n break;\n case \"w:color\": {\n const hex = attr(child, \"w:val\");\n const css = docxHexToCssColor(hex);\n if (css) m.color = css;\n break;\n }\n case \"w:shd\": {\n // Sombreamento de RUN = marca-texto. `auto` significa \"sem cor\".\n const fill = attr(child, \"w:fill\");\n if (fill && fill.toLowerCase() !== \"auto\") {\n const css = docxHexToCssColor(fill);\n if (css) m.highlight = css;\n }\n break;\n }\n case \"w:highlight\": {\n const css = DOCX_HIGHLIGHT_COLORS[(attr(child, \"w:val\") ?? \"\").toLowerCase()];\n if (css) m.highlight = css;\n break;\n }\n // w:rFonts intentionally ignored: school standard is Arial for all\n // exams. Any font declared in the .docx is dropped on import so the\n // imported doc inherits Arial from `.ed-root` / PDF base stylesheet.\n case \"w:sz\": {\n const halfPts = Number.parseFloat(attr(child, \"w:val\") ?? \"\");\n const css = halfPointsToCssPt(halfPts);\n if (css) m.fontSize = css;\n break;\n }\n }\n }\n return m;\n}\n\nfunction mergeMarks(a: MarkAttrs | undefined, b: MarkAttrs): MarkAttrs {\n if (!a) return b;\n return { ...a, ...b };\n}\n\nfunction textContent(node: OoxmlNode): string {\n let out = \"\";\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) {\n const t = child[\"#text\"];\n if (typeof t === \"string\") out += t;\n }\n }\n return out;\n}\n\nfunction isFalse(val: string | undefined): boolean {\n if (val == null) return false;\n const v = val.toLowerCase();\n return v === \"0\" || v === \"false\" || v === \"none\";\n}\n\nfunction push(out: DeltaOp[], text: string, marks: MarkAttrs): void {\n const op: DeltaOp =\n Object.keys(marks).length === 0 ? { insert: text } : { insert: text, attributes: { ...marks } };\n out.push(op);\n}\n\nfunction mergeAdjacent(ops: DeltaOp[]): DeltaOp[] {\n const out: DeltaOp[] = [];\n for (const op of ops) {\n const prev = out[out.length - 1];\n if (\n prev &&\n typeof prev.insert === \"string\" &&\n typeof op.insert === \"string\" &&\n sameMarks(prev.attributes, op.attributes)\n ) {\n prev.insert = prev.insert + op.insert;\n continue;\n }\n out.push(op);\n }\n return out;\n}\n\nfunction sameMarks(a: MarkAttrs | undefined, b: MarkAttrs | undefined): boolean {\n const ak = a ? Object.keys(a).sort() : [];\n const bk = b ? Object.keys(b).sort() : [];\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i++) {\n if (ak[i] !== bk[i]) return false;\n const av = (a as Record<string, unknown>)[ak[i]];\n const bv = (b as Record<string, unknown>)[bk[i]];\n if (typeof av === \"object\" && av !== null && typeof bv === \"object\" && bv !== null) {\n if (JSON.stringify(av) !== JSON.stringify(bv)) return false;\n } else if (av !== bv) {\n return false;\n }\n }\n return true;\n}\n","import type {\n AlignValue,\n AnswerLineSpacing,\n BlockAttrs,\n BlockType,\n DeltaOp,\n SerializedBlock,\n} from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { NumberingResolver } from \"./numbering\";\nimport { parseAlign } from \"./units\";\n\n/**\n * Map a `<w:p>` element to a SerializedBlock. Decides between\n * `paragraph / heading / blockquote / codeBlock / listItem` based on the\n * paragraph style + `w:pPr` properties — mirrors the heuristics encoded in\n * `@sofereditor/export-docx/src/docx.ts`.\n */\nexport function paragraphToBlock(\n p: OoxmlNode,\n ctx: RunContext,\n numbering: NumberingResolver,\n): SerializedBlock {\n const pPr = findChild(p, \"w:pPr\");\n const numPr = pPr ? findChild(pPr, \"w:numPr\") : undefined;\n const styleId = pPr ? attr(findChild(pPr, \"w:pStyle\") ?? {}, \"w:val\") : undefined;\n\n const delta = paragraphChildrenToDelta(childrenOf(p), ctx);\n const text = collectText(delta);\n const align = parseAlign(pPr ? attr(findChild(pPr, \"w:jc\") ?? {}, \"w:val\") : undefined);\n const isRtl = pPr ? !!findChild(pPr, \"w:bidi\") : false;\n\n // List? — numPr is the strongest signal.\n if (numPr) {\n const resolved = numbering.resolve(numPr);\n const attrs: BlockAttrs = {\n listKind: resolved?.listKind ?? \"bullet\",\n listLevel: resolved?.listLevel ?? 0,\n };\n if (resolved?.listKind === \"ordered\") {\n // O ordinal Word vai TEMPORARIAMENTE em listStart de todos os itens; o\n // pós-passe em docx.ts remove dos não-líderes (um listStart em item de\n // continuação quebraria o grupo no renderer). listStyle fica em todos —\n // itens do mesmo estilo agrupam; estilo diferente (ex.: a/b/c entre\n // questões decimais) quebra o grupo exatamente como no Word.\n if (resolved.ordinal != null) attrs.listStart = resolved.ordinal;\n if (resolved.listStyle) attrs.listStyle = resolved.listStyle;\n }\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"listItem\", text, delta, attrs };\n }\n\n // Heading?\n const headingLevel = styleId ? headingFromStyleId(styleId) : null;\n if (headingLevel != null) {\n const attrs: BlockAttrs = { level: headingLevel };\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"heading\", text, delta, attrs };\n }\n\n // Blockquote (export uses left border + 480 twip indent).\n if (pPr && hasLeftBorder(pPr)) {\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"blockquote\", text, delta, attrs };\n }\n\n // Code block (export uses Consolas + light-gray shading).\n if (pPr && hasCodeBlockShading(pPr)) {\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"codeBlock\", text, delta, attrs };\n }\n\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n Object.assign(attrs, readAnswerLine(pPr, text.length > 0));\n return { type: \"paragraph\", text, delta, attrs };\n}\n\n/**\n * Parágrafo SEM texto e com borda INFERIOR = linha de resposta.\n *\n * A exigência de estar vazio é essencial: um parágrafo com texto e régua é um\n * título sublinhado, e convertê-lo em linha de resposta destruiria a semântica\n * do conteúdo. A borda ESQUERDA já é tratada antes como blockquote.\n */\nfunction readAnswerLine(pPr: OoxmlNode | undefined, hasText: boolean): Partial<BlockAttrs> {\n if (!pPr || hasText) return {};\n const pBdr = findChild(pPr, \"w:pBdr\");\n if (!pBdr) return {};\n const bottom = findChild(pBdr, \"w:bottom\");\n if (!bottom) return {};\n const val = (attr(bottom, \"w:val\") ?? \"\").toLowerCase();\n if (val === \"\" || val === \"none\" || val === \"nil\") return {};\n return { answerLine: true, answerLineSpacing: readAnswerLineSpacing(pPr) };\n}\n\n/** 240 twips = 1 linha. Arredonda para o bucket suportado mais próximo. */\nfunction readAnswerLineSpacing(pPr: OoxmlNode): AnswerLineSpacing {\n const sp = findChild(pPr, \"w:spacing\");\n const line = sp ? Number.parseFloat(attr(sp, \"w:line\") ?? \"\") : NaN;\n if (!Number.isFinite(line)) return 1;\n const buckets: AnswerLineSpacing[] = [1, 1.5, 2];\n let best: AnswerLineSpacing = 1;\n let bestDist = Infinity;\n for (const b of buckets) {\n const d = Math.abs(line - 240 * b);\n if (d < bestDist) {\n bestDist = d;\n best = b;\n }\n }\n return best;\n}\n\nfunction collectText(delta: DeltaOp[]): string {\n let out = \"\";\n for (const op of delta) {\n if (typeof op.insert === \"string\") out += op.insert;\n }\n return out;\n}\n\nfunction headingFromStyleId(id: string): 1 | 2 | 3 | 4 | 5 | 6 | null {\n const m = /^Heading\\s*([1-6])$/i.exec(id);\n if (!m) return null;\n const n = Number.parseInt(m[1], 10);\n return (n as 1 | 2 | 3 | 4 | 5 | 6) ?? null;\n}\n\nfunction hasLeftBorder(pPr: OoxmlNode): boolean {\n const pBdr = findChild(pPr, \"w:pBdr\");\n if (!pBdr) return false;\n return !!findChild(pBdr, \"w:left\");\n}\n\nfunction hasCodeBlockShading(pPr: OoxmlNode): boolean {\n const shd = findChild(pPr, \"w:shd\");\n if (!shd) return false;\n const fill = attr(shd, \"w:fill\");\n if (!fill) return false;\n return fill.toLowerCase() === \"f1f5f9\";\n}\n\n/** Whitelist the block types this module produces (used by callers for typing). */\nexport type ParagraphBlockType = Exclude<BlockType, \"table\">;\n/** Whitelist the align values this module produces (used by callers for typing). */\nexport type ParagraphAlign = AlignValue;\n","import type {\n BlockAttrs,\n CellAttrs,\n DeltaOp,\n SerializedBlock,\n SerializedCell,\n TableBorderPreset,\n} from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * Map a `<w:tbl>` element to a SerializedBlock(table). The output `cells` array\n * is row-major with `rows * cols` entries — covered slots are filled with\n * `{attrs: {covered: true}}` placeholders so it matches the invariant required\n * by `@sofereditor/core`'s `EditorDocument`.\n */\nexport function tableToBlock(tbl: OoxmlNode, ctx: RunContext): SerializedBlock {\n const tblGrid = findChild(tbl, \"w:tblGrid\");\n const gridCols = tblGrid ? findChildren(tblGrid, \"w:gridCol\") : [];\n const trs = findChildren(tbl, \"w:tr\");\n\n // The docx writer (and Word itself) may emit fewer `<w:gridCol>` entries than\n // the table's logical column count when a row has merged cells. Take the max\n // of (declared gridCols, widest row's total gridSpan) to stay robust.\n let cols = gridCols.length;\n for (const tr of trs) {\n const tcs = findChildren(tr, \"w:tc\");\n let count = 0;\n for (const tc of tcs) count += gridSpanOf(tc);\n if (count > cols) cols = count;\n }\n if (cols < 1) cols = 1;\n const rows = trs.length;\n\n // Build a row-major grid of slots. Each slot is either a real cell or marked\n // as covered. We track which (row, col) is the anchor for vMerge groups.\n const slots: Array<SerializedCell | null> = new Array(rows * cols).fill(null);\n // Anchor of the active vMerge per column index; resets when a non-merge cell\n // is placed in the same column.\n const vMergeAnchorByCol = new Map<number, { row: number; col: number }>();\n\n for (let r = 0; r < rows; r++) {\n const tr = trs[r];\n let cursor = 0;\n for (const tc of findChildren(tr, \"w:tc\")) {\n // Advance the cursor past already-occupied (covered-from-above) slots.\n while (cursor < cols && slots[r * cols + cursor] !== null) cursor++;\n if (cursor >= cols) break;\n\n const tcPr = findChild(tc, \"w:tcPr\");\n const gridSpan = gridSpanOf(tc);\n const vMergeNode = tcPr ? findChild(tcPr, \"w:vMerge\") : undefined;\n const vMergeVal = vMergeNode ? (attr(vMergeNode, \"w:val\") ?? \"continue\") : null;\n\n if (vMergeNode && vMergeVal !== \"restart\") {\n // Continuation of a vertical merge. Cover this row, and bump the anchor's\n // rowspan if we can find it.\n const anchor = vMergeAnchorByCol.get(cursor);\n if (anchor) {\n const anchorIdx = anchor.row * cols + anchor.col;\n const a = slots[anchorIdx];\n if (a) {\n a.attrs = { ...(a.attrs ?? {}), rowspan: (a.attrs?.rowspan ?? 1) + 1 };\n }\n }\n for (let c = 0; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n cursor += gridSpan;\n continue;\n }\n\n // Real cell: build its delta + attrs.\n const delta = cellChildrenToDelta(tc, ctx);\n const text = textOfDelta(delta);\n const cellAttrs: CellAttrs = {};\n if (gridSpan > 1) cellAttrs.colspan = gridSpan;\n // rowspan starts at 1 — vMerge continuations bump it.\n\n const realCell: SerializedCell = { text, delta, attrs: cellAttrs };\n slots[r * cols + cursor] = realCell;\n\n // Mark horizontally-covered slots from gridSpan.\n for (let c = 1; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n\n if (vMergeVal === \"restart\") {\n vMergeAnchorByCol.set(cursor, { row: r, col: cursor });\n } else {\n vMergeAnchorByCol.delete(cursor);\n }\n cursor += gridSpan;\n }\n\n // Fill any trailing un-touched slots in this row with empty placeholders so\n // the row-major invariant holds (length = rows * cols).\n for (let c = 0; c < cols; c++) {\n if (slots[r * cols + c] === null) {\n slots[r * cols + c] = emptyCell();\n }\n }\n }\n\n const cells: SerializedCell[] = slots.map((s) => s ?? emptyCell());\n\n const attrs: BlockAttrs = { rows, cols };\n const preset = readBorderPreset(tbl);\n if (preset) attrs.borderPreset = preset;\n return { type: \"table\", text: \"\", delta: [], attrs, cells };\n}\n\n/**\n * Reduz `w:tblBorders` ao preset mais próximo.\n *\n * O modelo não tem borda por célula, então `w:tcBorders` continua ignorado —\n * deliberadamente. Combinação não mapeável cai em `all`, que é o padrão visual\n * e o comportamento menos surpreendente para quem importa um documento externo.\n *\n * Retorna `undefined` quando a tabela não declara `tblBorders`, para não gravar\n * a chave à toa.\n */\nfunction readBorderPreset(tbl: OoxmlNode): TableBorderPreset | undefined {\n const tblPr = findChild(tbl, \"w:tblPr\");\n const b = tblPr ? findChild(tblPr, \"w:tblBorders\") : undefined;\n if (!b) return undefined;\n\n // OOXML estrito usa start/end no lugar de left/right.\n const ligado = (...nomes: string[]): boolean =>\n nomes.some((nome) => {\n const n = findChild(b, nome);\n if (!n) return false;\n const val = (attr(n, \"w:val\") ?? \"\").toLowerCase();\n return val !== \"\" && val !== \"none\" && val !== \"nil\";\n });\n\n const top = ligado(\"w:top\");\n const bottom = ligado(\"w:bottom\");\n const left = ligado(\"w:left\", \"w:start\");\n const right = ligado(\"w:right\", \"w:end\");\n const iH = ligado(\"w:insideH\");\n const iV = ligado(\"w:insideV\");\n\n if (top && bottom && left && right && iH && iV) return \"all\";\n if (top && bottom && left && right && !iH && !iV) return \"outer\";\n if (top && bottom && !left && !right && iH && !iV) return \"horizontal\";\n if (!top && !bottom && left && right && !iH && iV) return \"vertical\";\n if (!top && !bottom && !left && !right && !iH && !iV) return \"none\";\n return \"all\";\n}\n\nfunction gridSpanOf(tc: OoxmlNode): number {\n const tcPr = findChild(tc, \"w:tcPr\");\n if (!tcPr) return 1;\n const gs = findChild(tcPr, \"w:gridSpan\");\n if (!gs) return 1;\n const n = parseIntAttr(attr(gs, \"w:val\"), 1);\n return Math.max(1, n);\n}\n\nfunction cellChildrenToDelta(tc: OoxmlNode, ctx: RunContext): DeltaOp[] {\n // A cell can contain multiple paragraphs; we concat their deltas with a\n // newline between adjacent ones to preserve visual separation. The cell\n // model in @sofereditor/core is a single Y.Text, so we flatten.\n const out: DeltaOp[] = [];\n let first = true;\n for (const child of childrenOf(tc)) {\n if (tagOf(child) !== \"w:p\") continue;\n if (!first) out.push({ insert: \"\\n\" });\n first = false;\n out.push(...paragraphChildrenToDelta(childrenOf(child), ctx));\n }\n return out;\n}\n\nfunction textOfDelta(delta: DeltaOp[]): string {\n let out = \"\";\n for (const op of delta) {\n if (typeof op.insert === \"string\") out += op.insert;\n }\n return out;\n}\n\nfunction coveredCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: { covered: true } };\n}\n\nfunction emptyCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: {} };\n}\n","import type { ListKind, ListStyleType } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * Resolves per-paragraph `<w:numPr>` into the editor's `{listKind, listLevel}`.\n *\n * docx numbering is two-step:\n * 1. paragraphs reference a `numId` and an `ilvl`\n * 2. each `<w:num numId=\"...\">` points to a `<w:abstractNum>` via `<w:abstractNumId>`\n * 3. each `<w:lvl ilvl=\"...\">` declares a `<w:numFmt val=\"bullet|decimal|...\">`\n *\n * The export side uses two hardcoded reference names (\"ed-bullet\" / \"ed-ordered\"),\n * but Word-authored documents use arbitrary `numId`s — we resolve by reading the\n * `numFmt` of the target level.\n */\n/** `w:numFmt` → CSS `list-style-type` do editor. Não listados caem em \"decimal\". */\nconst FMT_TO_LIST_STYLE: Record<string, ListStyleType> = {\n decimal: \"decimal\",\n lowerLetter: \"lower-alpha\",\n upperLetter: \"upper-alpha\",\n lowerRoman: \"lower-roman\",\n upperRoman: \"upper-roman\",\n};\n\nexport interface ResolvedNumbering {\n listKind: ListKind;\n listLevel: number;\n /**\n * Número Word desta entrada (contador por instância `numId`+nível, honrando\n * `w:start` e resetando níveis mais profundos). Só para `ordered`. O Word\n * numera por INSTÂNCIA — parágrafos comuns entre itens não resetam a\n * contagem — enquanto o editor numera por adjacência; o importador usa este\n * ordinal para semear `listStart` nos líderes de grupo (ver docx.ts).\n */\n ordinal?: number;\n /** Estilo do nível (`w:numFmt` mapeado). Só para `ordered`. */\n listStyle?: ListStyleType;\n}\n\nexport class NumberingResolver {\n private numIdToAbstract = new Map<string, string>();\n private abstractLevelFmt = new Map<string, Map<number, string>>();\n private abstractLevelStart = new Map<string, Map<number, number>>();\n /** Contadores vivos por instância de lista (numId), indexados por nível. */\n private counters = new Map<string, number[]>();\n\n constructor(numberingXml: OoxmlNode[] | undefined) {\n if (!numberingXml) return;\n for (const top of numberingXml) {\n if (tagOf(top) !== \"w:numbering\") continue;\n for (const child of childrenOf(top)) {\n const tag = tagOf(child);\n if (tag === \"w:abstractNum\") {\n const abstractId = attr(child, \"w:abstractNumId\");\n if (!abstractId) continue;\n const levels = new Map<number, string>();\n const starts = new Map<number, number>();\n for (const lvl of findChildren(child, \"w:lvl\")) {\n const ilvl = parseIntAttr(attr(lvl, \"w:ilvl\"), 0);\n const fmt = attr(findChild(lvl, \"w:numFmt\") ?? {}, \"w:val\");\n if (fmt) levels.set(ilvl, fmt);\n const start = findChild(lvl, \"w:start\");\n if (start) starts.set(ilvl, parseIntAttr(attr(start, \"w:val\"), 1));\n }\n this.abstractLevelFmt.set(abstractId, levels);\n this.abstractLevelStart.set(abstractId, starts);\n } else if (tag === \"w:num\") {\n const numId = attr(child, \"w:numId\");\n const abstractRef = findChild(child, \"w:abstractNumId\");\n const abstractId = abstractRef ? attr(abstractRef, \"w:val\") : undefined;\n if (numId && abstractId) this.numIdToAbstract.set(numId, abstractId);\n }\n }\n }\n }\n\n /**\n * Resolves a paragraph's `<w:numPr>` into `{listKind, listLevel, ordinal?,\n * listStyle?}`, or null. STATEFUL for ordered lists: each call advances the\n * per-`numId` counter (Word instance semantics) — call exactly once per\n * numbered paragraph, in document order.\n */\n resolve(numPr: OoxmlNode | undefined): ResolvedNumbering | null {\n if (!numPr) return null;\n const ilvlRef = findChild(numPr, \"w:ilvl\");\n const numIdRef = findChild(numPr, \"w:numId\");\n if (!numIdRef) return null;\n const numId = attr(numIdRef, \"w:val\");\n if (!numId) return null;\n const listLevel = clampLevel(parseIntAttr(ilvlRef ? attr(ilvlRef, \"w:val\") : undefined, 0));\n const abstractId = this.numIdToAbstract.get(numId);\n if (abstractId == null) {\n // Numbering refs the export side defines without a numbering.xml entry visible\n // to us (the export-docx pipeline generates them dynamically via the docx lib).\n // Fall back by inspecting the numId itself when it is a known sentinel.\n return { listKind: \"bullet\", listLevel };\n }\n const levels = this.abstractLevelFmt.get(abstractId);\n const fmt = levels?.get(listLevel) ?? \"bullet\";\n const fmtLower = fmt.toLowerCase();\n const listKind: ListKind = fmtLower === \"bullet\" || fmtLower === \"none\" ? \"bullet\" : \"ordered\";\n if (listKind === \"bullet\") return { listKind, listLevel };\n\n // Ordinal Word: contador por numId; nível avança, níveis mais profundos resetam.\n const start = this.abstractLevelStart.get(abstractId)?.get(listLevel) ?? 1;\n const c = this.counters.get(numId) ?? [];\n c[listLevel] = c[listLevel] == null ? start : c[listLevel] + 1;\n c.length = listLevel + 1;\n this.counters.set(numId, c);\n\n return {\n listKind,\n listLevel,\n ordinal: c[listLevel],\n listStyle: FMT_TO_LIST_STYLE[fmt] ?? \"decimal\",\n };\n }\n}\n\nfunction clampLevel(n: number): number {\n if (!Number.isFinite(n)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(n)));\n}\n","import { detectPreset, mmToPx, type PageSettings } from \"@sofereditor/core\";\nimport { attr, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * 1 inch = 1440 twips = 25.4 mm. So 1 twip = 25.4/1440 mm.\n *\n * Page size lives in `<w:sectPr><w:pgSz w:w=\"...\" w:h=\"...\"/></w:sectPr>` and\n * margins in `<w:pgMar w:top=\"...\" w:bottom=\"...\" w:left=\"...\" w:right=\"...\"/>`,\n * all in twips. This is the inverse of `sectionPropertiesFor` in\n * `@sofereditor/export-docx`.\n */\nconst MM_PER_TWIP = 25.4 / 1440;\n\nfunction twipsToPx(twips: number): number {\n return mmToPx(twips * MM_PER_TWIP);\n}\n\nexport function sectPrToPageSettings(\n sectPr: OoxmlNode | undefined,\n): PageSettings | undefined {\n if (!sectPr) return undefined;\n const pgSz = findChild(sectPr, \"w:pgSz\");\n const pgMar = findChild(sectPr, \"w:pgMar\");\n if (!pgSz && !pgMar) return undefined;\n\n const widthTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:w\"), 0) : 0;\n const heightTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:h\"), 0) : 0;\n const topTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:top\"), 0) : 0;\n const bottomTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:bottom\"), 0) : 0;\n const leftTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:left\"), 0) : 0;\n const rightTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:right\"), 0) : 0;\n\n if (widthTwips <= 0 && heightTwips <= 0 && topTwips <= 0 && leftTwips <= 0) {\n return undefined;\n }\n\n const width = widthTwips > 0 ? twipsToPx(widthTwips) : 794;\n const height = heightTwips > 0 ? twipsToPx(heightTwips) : 1123;\n const settings: PageSettings = {\n width,\n height,\n marginTop: twipsToPx(topTwips || 0),\n marginBottom: twipsToPx(bottomTwips || 0),\n marginLeft: twipsToPx(leftTwips || 0),\n marginRight: twipsToPx(rightTwips || 0),\n };\n settings.preset = detectPreset(settings.width, settings.height);\n return settings;\n}\n"],"mappings":"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","names":["import_core","JSZip","attrs"]}
|
package/dist/index.js
CHANGED
|
@@ -234,6 +234,24 @@ function collectRun(run, ctx, wrappingMarks, out) {
|
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
|
+
var DOCX_HIGHLIGHT_COLORS = {
|
|
238
|
+
yellow: "#ffff00",
|
|
239
|
+
green: "#00ff00",
|
|
240
|
+
cyan: "#00ffff",
|
|
241
|
+
magenta: "#ff00ff",
|
|
242
|
+
blue: "#0000ff",
|
|
243
|
+
red: "#ff0000",
|
|
244
|
+
darkblue: "#000080",
|
|
245
|
+
darkcyan: "#008080",
|
|
246
|
+
darkgreen: "#008000",
|
|
247
|
+
darkmagenta: "#800080",
|
|
248
|
+
darkred: "#800000",
|
|
249
|
+
darkyellow: "#808000",
|
|
250
|
+
darkgray: "#808080",
|
|
251
|
+
lightgray: "#c0c0c0",
|
|
252
|
+
black: "#000000",
|
|
253
|
+
white: "#ffffff"
|
|
254
|
+
};
|
|
237
255
|
function runMarks(rPr) {
|
|
238
256
|
const m = {};
|
|
239
257
|
if (!rPr) return m;
|
|
@@ -261,6 +279,19 @@ function runMarks(rPr) {
|
|
|
261
279
|
if (css) m.color = css;
|
|
262
280
|
break;
|
|
263
281
|
}
|
|
282
|
+
case "w:shd": {
|
|
283
|
+
const fill = attr(child, "w:fill");
|
|
284
|
+
if (fill && fill.toLowerCase() !== "auto") {
|
|
285
|
+
const css = docxHexToCssColor(fill);
|
|
286
|
+
if (css) m.highlight = css;
|
|
287
|
+
}
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
case "w:highlight": {
|
|
291
|
+
const css = DOCX_HIGHLIGHT_COLORS[(attr(child, "w:val") ?? "").toLowerCase()];
|
|
292
|
+
if (css) m.highlight = css;
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
264
295
|
// w:rFonts intentionally ignored: school standard is Arial for all
|
|
265
296
|
// exams. Any font declared in the .docx is dropped on import so the
|
|
266
297
|
// imported doc inherits Arial from `.ed-root` / PDF base stylesheet.
|
|
@@ -341,6 +372,10 @@ function paragraphToBlock(p, ctx, numbering) {
|
|
|
341
372
|
listKind: resolved?.listKind ?? "bullet",
|
|
342
373
|
listLevel: resolved?.listLevel ?? 0
|
|
343
374
|
};
|
|
375
|
+
if (resolved?.listKind === "ordered") {
|
|
376
|
+
if (resolved.ordinal != null) attrs2.listStart = resolved.ordinal;
|
|
377
|
+
if (resolved.listStyle) attrs2.listStyle = resolved.listStyle;
|
|
378
|
+
}
|
|
344
379
|
if (align) attrs2.align = align;
|
|
345
380
|
if (isRtl) attrs2.dir = "rtl";
|
|
346
381
|
return { type: "listItem", text, delta, attrs: attrs2 };
|
|
@@ -367,8 +402,35 @@ function paragraphToBlock(p, ctx, numbering) {
|
|
|
367
402
|
const attrs = {};
|
|
368
403
|
if (align) attrs.align = align;
|
|
369
404
|
if (isRtl) attrs.dir = "rtl";
|
|
405
|
+
Object.assign(attrs, readAnswerLine(pPr, text.length > 0));
|
|
370
406
|
return { type: "paragraph", text, delta, attrs };
|
|
371
407
|
}
|
|
408
|
+
function readAnswerLine(pPr, hasText) {
|
|
409
|
+
if (!pPr || hasText) return {};
|
|
410
|
+
const pBdr = findChild(pPr, "w:pBdr");
|
|
411
|
+
if (!pBdr) return {};
|
|
412
|
+
const bottom = findChild(pBdr, "w:bottom");
|
|
413
|
+
if (!bottom) return {};
|
|
414
|
+
const val = (attr(bottom, "w:val") ?? "").toLowerCase();
|
|
415
|
+
if (val === "" || val === "none" || val === "nil") return {};
|
|
416
|
+
return { answerLine: true, answerLineSpacing: readAnswerLineSpacing(pPr) };
|
|
417
|
+
}
|
|
418
|
+
function readAnswerLineSpacing(pPr) {
|
|
419
|
+
const sp = findChild(pPr, "w:spacing");
|
|
420
|
+
const line = sp ? Number.parseFloat(attr(sp, "w:line") ?? "") : NaN;
|
|
421
|
+
if (!Number.isFinite(line)) return 1;
|
|
422
|
+
const buckets = [1, 1.5, 2];
|
|
423
|
+
let best = 1;
|
|
424
|
+
let bestDist = Infinity;
|
|
425
|
+
for (const b of buckets) {
|
|
426
|
+
const d = Math.abs(line - 240 * b);
|
|
427
|
+
if (d < bestDist) {
|
|
428
|
+
bestDist = d;
|
|
429
|
+
best = b;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return best;
|
|
433
|
+
}
|
|
372
434
|
function collectText(delta) {
|
|
373
435
|
let out = "";
|
|
374
436
|
for (const op of delta) {
|
|
@@ -460,8 +522,33 @@ function tableToBlock(tbl, ctx) {
|
|
|
460
522
|
}
|
|
461
523
|
const cells = slots.map((s) => s ?? emptyCell());
|
|
462
524
|
const attrs = { rows, cols };
|
|
525
|
+
const preset = readBorderPreset(tbl);
|
|
526
|
+
if (preset) attrs.borderPreset = preset;
|
|
463
527
|
return { type: "table", text: "", delta: [], attrs, cells };
|
|
464
528
|
}
|
|
529
|
+
function readBorderPreset(tbl) {
|
|
530
|
+
const tblPr = findChild(tbl, "w:tblPr");
|
|
531
|
+
const b = tblPr ? findChild(tblPr, "w:tblBorders") : void 0;
|
|
532
|
+
if (!b) return void 0;
|
|
533
|
+
const ligado = (...nomes) => nomes.some((nome) => {
|
|
534
|
+
const n = findChild(b, nome);
|
|
535
|
+
if (!n) return false;
|
|
536
|
+
const val = (attr(n, "w:val") ?? "").toLowerCase();
|
|
537
|
+
return val !== "" && val !== "none" && val !== "nil";
|
|
538
|
+
});
|
|
539
|
+
const top = ligado("w:top");
|
|
540
|
+
const bottom = ligado("w:bottom");
|
|
541
|
+
const left = ligado("w:left", "w:start");
|
|
542
|
+
const right = ligado("w:right", "w:end");
|
|
543
|
+
const iH = ligado("w:insideH");
|
|
544
|
+
const iV = ligado("w:insideV");
|
|
545
|
+
if (top && bottom && left && right && iH && iV) return "all";
|
|
546
|
+
if (top && bottom && left && right && !iH && !iV) return "outer";
|
|
547
|
+
if (top && bottom && !left && !right && iH && !iV) return "horizontal";
|
|
548
|
+
if (!top && !bottom && left && right && !iH && iV) return "vertical";
|
|
549
|
+
if (!top && !bottom && !left && !right && !iH && !iV) return "none";
|
|
550
|
+
return "all";
|
|
551
|
+
}
|
|
465
552
|
function gridSpanOf(tc) {
|
|
466
553
|
const tcPr = findChild(tc, "w:tcPr");
|
|
467
554
|
if (!tcPr) return 1;
|
|
@@ -496,9 +583,19 @@ function emptyCell() {
|
|
|
496
583
|
}
|
|
497
584
|
|
|
498
585
|
// src/numbering.ts
|
|
586
|
+
var FMT_TO_LIST_STYLE = {
|
|
587
|
+
decimal: "decimal",
|
|
588
|
+
lowerLetter: "lower-alpha",
|
|
589
|
+
upperLetter: "upper-alpha",
|
|
590
|
+
lowerRoman: "lower-roman",
|
|
591
|
+
upperRoman: "upper-roman"
|
|
592
|
+
};
|
|
499
593
|
var NumberingResolver = class {
|
|
500
594
|
numIdToAbstract = /* @__PURE__ */ new Map();
|
|
501
595
|
abstractLevelFmt = /* @__PURE__ */ new Map();
|
|
596
|
+
abstractLevelStart = /* @__PURE__ */ new Map();
|
|
597
|
+
/** Contadores vivos por instância de lista (numId), indexados por nível. */
|
|
598
|
+
counters = /* @__PURE__ */ new Map();
|
|
502
599
|
constructor(numberingXml) {
|
|
503
600
|
if (!numberingXml) return;
|
|
504
601
|
for (const top of numberingXml) {
|
|
@@ -509,12 +606,16 @@ var NumberingResolver = class {
|
|
|
509
606
|
const abstractId = attr(child, "w:abstractNumId");
|
|
510
607
|
if (!abstractId) continue;
|
|
511
608
|
const levels = /* @__PURE__ */ new Map();
|
|
609
|
+
const starts = /* @__PURE__ */ new Map();
|
|
512
610
|
for (const lvl of findChildren(child, "w:lvl")) {
|
|
513
611
|
const ilvl = parseIntAttr(attr(lvl, "w:ilvl"), 0);
|
|
514
612
|
const fmt = attr(findChild(lvl, "w:numFmt") ?? {}, "w:val");
|
|
515
613
|
if (fmt) levels.set(ilvl, fmt);
|
|
614
|
+
const start = findChild(lvl, "w:start");
|
|
615
|
+
if (start) starts.set(ilvl, parseIntAttr(attr(start, "w:val"), 1));
|
|
516
616
|
}
|
|
517
617
|
this.abstractLevelFmt.set(abstractId, levels);
|
|
618
|
+
this.abstractLevelStart.set(abstractId, starts);
|
|
518
619
|
} else if (tag === "w:num") {
|
|
519
620
|
const numId = attr(child, "w:numId");
|
|
520
621
|
const abstractRef = findChild(child, "w:abstractNumId");
|
|
@@ -524,7 +625,12 @@ var NumberingResolver = class {
|
|
|
524
625
|
}
|
|
525
626
|
}
|
|
526
627
|
}
|
|
527
|
-
/**
|
|
628
|
+
/**
|
|
629
|
+
* Resolves a paragraph's `<w:numPr>` into `{listKind, listLevel, ordinal?,
|
|
630
|
+
* listStyle?}`, or null. STATEFUL for ordered lists: each call advances the
|
|
631
|
+
* per-`numId` counter (Word instance semantics) — call exactly once per
|
|
632
|
+
* numbered paragraph, in document order.
|
|
633
|
+
*/
|
|
528
634
|
resolve(numPr) {
|
|
529
635
|
if (!numPr) return null;
|
|
530
636
|
const ilvlRef = findChild(numPr, "w:ilvl");
|
|
@@ -538,9 +644,21 @@ var NumberingResolver = class {
|
|
|
538
644
|
return { listKind: "bullet", listLevel };
|
|
539
645
|
}
|
|
540
646
|
const levels = this.abstractLevelFmt.get(abstractId);
|
|
541
|
-
const fmt =
|
|
542
|
-
const
|
|
543
|
-
|
|
647
|
+
const fmt = levels?.get(listLevel) ?? "bullet";
|
|
648
|
+
const fmtLower = fmt.toLowerCase();
|
|
649
|
+
const listKind = fmtLower === "bullet" || fmtLower === "none" ? "bullet" : "ordered";
|
|
650
|
+
if (listKind === "bullet") return { listKind, listLevel };
|
|
651
|
+
const start = this.abstractLevelStart.get(abstractId)?.get(listLevel) ?? 1;
|
|
652
|
+
const c = this.counters.get(numId) ?? [];
|
|
653
|
+
c[listLevel] = c[listLevel] == null ? start : c[listLevel] + 1;
|
|
654
|
+
c.length = listLevel + 1;
|
|
655
|
+
this.counters.set(numId, c);
|
|
656
|
+
return {
|
|
657
|
+
listKind,
|
|
658
|
+
listLevel,
|
|
659
|
+
ordinal: c[listLevel],
|
|
660
|
+
listStyle: FMT_TO_LIST_STYLE[fmt] ?? "decimal"
|
|
661
|
+
};
|
|
544
662
|
}
|
|
545
663
|
};
|
|
546
664
|
function clampLevel(n) {
|
|
@@ -604,9 +722,44 @@ async function docxBlobToDocument(input) {
|
|
|
604
722
|
if (blocks.length === 0) {
|
|
605
723
|
blocks.push({ type: "paragraph", text: "", delta: [], attrs: {} });
|
|
606
724
|
}
|
|
725
|
+
pruneNonLeaderListStarts(blocks);
|
|
607
726
|
const pageSettings = sectPrToPageSettings(findChild(body, "w:sectPr"));
|
|
608
727
|
return pageSettings ? { blocks, pageSettings } : { blocks };
|
|
609
728
|
}
|
|
729
|
+
function pruneNonLeaderListStarts(blocks) {
|
|
730
|
+
let grupoAtivo = false;
|
|
731
|
+
let grupoStyle;
|
|
732
|
+
let ordinais = [];
|
|
733
|
+
const resetGrupo = () => {
|
|
734
|
+
grupoAtivo = false;
|
|
735
|
+
grupoStyle = void 0;
|
|
736
|
+
ordinais = [];
|
|
737
|
+
};
|
|
738
|
+
for (const block of blocks) {
|
|
739
|
+
if (block.type !== "listItem") {
|
|
740
|
+
resetGrupo();
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
if ((block.attrs.listKind ?? "bullet") !== "ordered") {
|
|
744
|
+
resetGrupo();
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
const level = block.attrs.listLevel ?? 0;
|
|
748
|
+
const ordinal = typeof block.attrs.listStart === "number" ? block.attrs.listStart : 1;
|
|
749
|
+
const mesmoNivelSequencial = ordinais[level] != null && ordinal === ordinais[level] + 1;
|
|
750
|
+
const entrandoEmNivelNovo = ordinais[level] == null && level > 0 && ordinal === 1;
|
|
751
|
+
const continuaGrupo = grupoAtivo && block.attrs.listStyle === grupoStyle && (mesmoNivelSequencial || entrandoEmNivelNovo);
|
|
752
|
+
if (continuaGrupo) {
|
|
753
|
+
delete block.attrs.listStart;
|
|
754
|
+
} else {
|
|
755
|
+
grupoAtivo = true;
|
|
756
|
+
grupoStyle = block.attrs.listStyle;
|
|
757
|
+
ordinais = [];
|
|
758
|
+
}
|
|
759
|
+
ordinais[level] = ordinal;
|
|
760
|
+
ordinais.length = level + 1;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
610
763
|
async function docxBlobToEditorDocument(input) {
|
|
611
764
|
const serialized = await docxBlobToDocument(input);
|
|
612
765
|
return EditorDocument.fromJSON(serialized);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/docx.ts","../src/parse-xml.ts","../src/units.ts","../src/images.ts","../src/runs.ts","../src/paragraphs.ts","../src/tables.ts","../src/numbering.ts","../src/page-settings.ts"],"sourcesContent":["import { EditorDocument, type SerializedBlock, type SerializedDocument } from \"@sofereditor/core\";\nimport { readDocx, childrenOf, tagOf, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { paragraphToBlock } from \"./paragraphs\";\nimport { tableToBlock } from \"./tables\";\nimport { NumberingResolver } from \"./numbering\";\nimport { sectPrToPageSettings } from \"./page-settings\";\nimport type { RunContext } from \"./runs\";\n\n/**\n * Read a `.docx` (OOXML) Blob/ArrayBuffer/Uint8Array and produce a\n * `SerializedDocument` ready to feed `EditorDocument.fromJSON` (or\n * `loadFromJSON` on an existing instance).\n *\n * The mapping is the inverse of `@sofereditor/export-docx`. Round-trip is exact for\n * documents the editor produced; documents authored externally (Word, Google\n * Docs export) load with marks/lists/tables/images recognized and unsupported\n * features dropped silently.\n */\nexport async function docxBlobToDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<SerializedDocument> {\n const file = await readDocx(input);\n const numbering = new NumberingResolver(file.numberingXml);\n const ctx: RunContext = {\n relationships: file.relationships,\n mediaBytes: file.mediaBytes,\n };\n\n const body = findBody(file.documentXml);\n if (!body) return { blocks: [{ type: \"paragraph\", text: \"\", delta: [], attrs: {} }] };\n\n const blocks: SerializedBlock[] = [];\n for (const child of childrenOf(body)) {\n const tag = tagOf(child);\n if (tag === \"w:p\") {\n blocks.push(paragraphToBlock(child, ctx, numbering));\n } else if (tag === \"w:tbl\") {\n blocks.push(tableToBlock(child, ctx));\n }\n // w:sectPr is parsed below (page settings); other body-level metadata ignored.\n }\n\n if (blocks.length === 0) {\n blocks.push({ type: \"paragraph\", text: \"\", delta: [], attrs: {} });\n }\n\n const pageSettings = sectPrToPageSettings(findChild(body, \"w:sectPr\"));\n return pageSettings ? { blocks, pageSettings } : { blocks };\n}\n\n/** Convenience: parse → instantiate a fresh `EditorDocument`. */\nexport async function docxBlobToEditorDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<EditorDocument> {\n const serialized = await docxBlobToDocument(input);\n return EditorDocument.fromJSON(serialized);\n}\n\nfunction findBody(documentXml: OoxmlNode[]): OoxmlNode | undefined {\n for (const top of documentXml) {\n if (tagOf(top) === \"w:document\") {\n const body = findChild(top, \"w:body\");\n if (body) return body;\n }\n }\n return undefined;\n}\n","import JSZip from \"jszip\";\nimport { XMLParser } from \"fast-xml-parser\";\n\n/**\n * fast-xml-parser with `preserveOrder: true` produces a list of single-key\n * objects per element. Each element is either:\n *\n * - A tag node: `{ \"<tagName>\": <children-array>, \":@\"?: <attrs> }`\n * - A text node: `{ \"#text\": <string> }`\n *\n * Attributes are prefixed with `@_`. The `:@` key holds attributes for the\n * surrounding tag. Children preserve original document order, which matters\n * for paragraph runs (bold word followed by italic word).\n */\nexport type OoxmlNode = Record<string, unknown>;\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n preserveOrder: true,\n trimValues: false, // keep whitespace inside <w:t>\n parseAttributeValue: false,\n parseTagValue: false,\n});\n\nexport interface DocxFile {\n documentXml: OoxmlNode[];\n numberingXml?: OoxmlNode[];\n /** rId → target path (inside the zip), e.g. \"media/image1.png\". */\n relationships: Map<string, string>;\n /** zip path (without leading \"word/\") → bytes for embedded media. */\n mediaBytes: Map<string, Uint8Array>;\n}\n\nexport async function readDocx(input: Blob | ArrayBuffer | Uint8Array): Promise<DocxFile> {\n const data =\n input instanceof Uint8Array\n ? input\n : input instanceof ArrayBuffer\n ? new Uint8Array(input)\n : new Uint8Array(await (input as Blob).arrayBuffer());\n\n const zip = await JSZip.loadAsync(data);\n const documentXml = await readXml(zip, \"word/document.xml\");\n if (!documentXml) throw new Error(\"Invalid .docx: missing word/document.xml\");\n\n const numberingXml = await readXml(zip, \"word/numbering.xml\");\n const relsXml = await readXml(zip, \"word/_rels/document.xml.rels\");\n const relationships = parseRelationships(relsXml);\n\n const mediaBytes = new Map<string, Uint8Array>();\n const mediaFolder = zip.folder(\"word/media\");\n if (mediaFolder) {\n const promises: Promise<void>[] = [];\n mediaFolder.forEach((relativePath, file) => {\n if (file.dir) return;\n promises.push(\n file.async(\"uint8array\").then((bytes) => {\n mediaBytes.set(`media/${relativePath}`, bytes);\n }),\n );\n });\n await Promise.all(promises);\n }\n\n return { documentXml, numberingXml, relationships, mediaBytes };\n}\n\nasync function readXml(zip: JSZip, path: string): Promise<OoxmlNode[] | undefined> {\n const entry = zip.file(path);\n if (!entry) return undefined;\n const text = await entry.async(\"string\");\n return parser.parse(text) as OoxmlNode[];\n}\n\nfunction parseRelationships(nodes: OoxmlNode[] | undefined): Map<string, string> {\n const out = new Map<string, string>();\n if (!nodes) return out;\n // <Relationships><Relationship Id=\"rId1\" Type=\"...\" Target=\"media/image1.png\"/>...\n for (const top of nodes) {\n const children = top[\"Relationships\"];\n if (!Array.isArray(children)) continue;\n for (const child of children as OoxmlNode[]) {\n if (!(\"Relationship\" in child)) continue;\n const attrs = (child[\":@\"] ?? {}) as Record<string, unknown>;\n const id = attrs[\"@_Id\"];\n const target = attrs[\"@_Target\"];\n if (typeof id === \"string\" && typeof target === \"string\") {\n out.set(id, target);\n }\n }\n }\n return out;\n}\n\n// ---------- generic walkers over preserveOrder shape ----------\n\n/** Returns the tag name (e.g. \"w:p\") of a preserveOrder element, or null for text. */\nexport function tagOf(node: OoxmlNode): string | null {\n for (const key of Object.keys(node)) {\n if (key === \":@\") continue;\n return key;\n }\n return null;\n}\n\n/** Returns the children array of an element, or [] when none. */\nexport function childrenOf(node: OoxmlNode): OoxmlNode[] {\n const tag = tagOf(node);\n if (!tag) return [];\n const v = node[tag];\n return Array.isArray(v) ? (v as OoxmlNode[]) : [];\n}\n\n/** Returns the attribute bag (already with `@_` prefix) for an element. */\nexport function attrsOf(node: OoxmlNode): Record<string, unknown> {\n return ((node[\":@\"] ?? {}) as Record<string, unknown>) ?? {};\n}\n\n/** Read a single `@_key` attribute as a string. */\nexport function attr(node: OoxmlNode, key: string): string | undefined {\n const v = attrsOf(node)[`@_${key}`];\n return typeof v === \"string\" ? v : undefined;\n}\n\n/** Find the first direct child with the given tag name. */\nexport function findChild(node: OoxmlNode, tag: string): OoxmlNode | undefined {\n for (const child of childrenOf(node)) {\n if (tagOf(child) === tag) return child;\n }\n return undefined;\n}\n\n/** Find all direct children with the given tag name. */\nexport function findChildren(node: OoxmlNode, tag: string): OoxmlNode[] {\n const out: OoxmlNode[] = [];\n for (const child of childrenOf(node)) {\n if (tagOf(child) === tag) out.push(child);\n }\n return out;\n}\n\n/** Recursively collect text nodes' concatenated content. */\nexport function textOf(node: OoxmlNode): string {\n let out = \"\";\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) {\n const t = child[\"#text\"];\n if (typeof t === \"string\") out += t;\n } else {\n out += textOf(child);\n }\n }\n return out;\n}\n","/**\n * Unit conversions between OOXML and the editor's serialized format.\n * These are the inverses of the helpers in `@sofereditor/export-docx/src/docx.ts`.\n */\n\n/** docx half-points → CSS pt string. 24 (half-pt) → \"12pt\". */\nexport function halfPointsToCssPt(halfPoints: number): string | undefined {\n if (!Number.isFinite(halfPoints) || halfPoints <= 0) return undefined;\n const pt = halfPoints / 2;\n return `${stripTrailingZeros(pt)}pt`;\n}\n\n/** OOXML RRGGBB (no '#') → CSS hex with leading '#'. */\nexport function docxHexToCssColor(hex: string | undefined): string | undefined {\n if (!hex) return undefined;\n const m = /^[0-9a-f]{6}$/i.exec(hex.trim());\n if (!m) return undefined;\n return `#${hex.toLowerCase()}`;\n}\n\n/** OOXML alignment value → editor AlignValue. */\nexport function parseAlign(val: string | undefined): \"left\" | \"center\" | \"right\" | \"justify\" | undefined {\n if (!val) return undefined;\n switch (val.toLowerCase()) {\n case \"left\":\n case \"start\":\n return \"left\";\n case \"center\":\n return \"center\";\n case \"right\":\n case \"end\":\n return \"right\";\n case \"both\":\n case \"distribute\":\n case \"justify\":\n return \"justify\";\n default:\n return undefined;\n }\n}\n\n/** Parse XML attribute that should be a positive integer. */\nexport function parseIntAttr(val: unknown, fallback = 0): number {\n if (typeof val === \"number\" && Number.isFinite(val)) return Math.trunc(val);\n if (typeof val !== \"string\") return fallback;\n const n = Number.parseInt(val, 10);\n return Number.isFinite(n) ? n : fallback;\n}\n\nfunction stripTrailingZeros(n: number): string {\n // 12 → \"12\", 12.5 → \"12.5\"\n return Number.isInteger(n) ? n.toString() : n.toFixed(2).replace(/\\.?0+$/, \"\");\n}\n","import type { ImageEmbed } from \"@sofereditor/core\";\nimport { attr, childrenOf, tagOf, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/** 1 inch = 914400 EMU = 96 px → 1 px = 9525 EMU. */\nconst EMU_PER_PX = 9525;\n\nconst MIME_BY_EXT: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n bmp: \"image/bmp\",\n svg: \"image/svg+xml\",\n};\n\nexport interface ImageContext {\n relationships: Map<string, string>;\n mediaBytes: Map<string, Uint8Array>;\n}\n\n/**\n * Walk a `<w:drawing>` node and return an `ImageEmbed` if it contains a\n * recognized inline picture. Returns null for unsupported drawings (shapes,\n * charts, etc.).\n */\nexport function extractImageFromDrawing(\n drawing: OoxmlNode,\n ctx: ImageContext,\n): ImageEmbed | null {\n // The interesting bits live deep inside: a:blip carries `r:embed`, and\n // wp:extent carries cx/cy. We do a generic walk because the wrapping nodes\n // differ between inline (`wp:inline`) and anchored (`wp:anchor`) drawings.\n let embedId: string | undefined;\n let cx: number | undefined;\n let cy: number | undefined;\n\n walk(drawing, (n) => {\n const tag = tagOf(n);\n if (!tag) return;\n if (tag === \"a:blip\") {\n const id = attr(n, \"r:embed\") ?? attr(n, \"r:link\");\n if (id) embedId = id;\n } else if (tag === \"wp:extent\") {\n cx = parseIntAttr(attr(n, \"cx\"));\n cy = parseIntAttr(attr(n, \"cy\"));\n }\n });\n\n if (!embedId) return null;\n const target = ctx.relationships.get(embedId);\n if (!target) return null;\n // Relationship targets are typically \"media/image1.png\"; the zip lookup uses\n // the same path (we stored bytes keyed under \"media/<file>\").\n const bytes = ctx.mediaBytes.get(target);\n if (!bytes) return null;\n\n const ext = extensionOf(target);\n const mime = MIME_BY_EXT[ext] ?? \"image/png\";\n const src = `data:${mime};base64,${bytesToBase64(bytes)}`;\n const widthPx = cx != null ? Math.max(1, Math.round(cx / EMU_PER_PX)) : 100;\n const heightPx = cy != null ? Math.max(1, Math.round(cy / EMU_PER_PX)) : 100;\n\n return { type: \"image\", src, width: widthPx, height: heightPx };\n}\n\nfunction walk(node: OoxmlNode, visit: (n: OoxmlNode) => void): void {\n visit(node);\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) continue;\n walk(child, visit);\n }\n}\n\nfunction extensionOf(path: string): string {\n const m = /\\.([a-z0-9]+)$/i.exec(path);\n return m ? m[1].toLowerCase() : \"\";\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n // Browser path.\n if (typeof btoa === \"function\") {\n let bin = \"\";\n const CHUNK = 0x8000;\n for (let i = 0; i < bytes.length; i += CHUNK) {\n bin += String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + CHUNK)));\n }\n return btoa(bin);\n }\n // Node fallback.\n return Buffer.from(bytes).toString(\"base64\");\n}\n","import type { DeltaOp, MarkAttrs } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { docxHexToCssColor, halfPointsToCssPt } from \"./units\";\nimport { extractImageFromDrawing, type ImageContext } from \"./images\";\n\nexport interface RunContext extends ImageContext {}\n\n/**\n * Walk a paragraph's children (post `<w:pPr>`) and emit a `DeltaOp[]`. Combines\n * adjacent runs that share the same mark set into a single op for parity with\n * the export — Y.Text.applyDelta would merge them anyway, but pre-merging keeps\n * round-trip output stable.\n */\nexport function paragraphChildrenToDelta(children: OoxmlNode[], ctx: RunContext): DeltaOp[] {\n const ops: DeltaOp[] = [];\n for (const child of children) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:pPr\") continue;\n if (tag === \"w:r\") {\n collectRun(child, ctx, undefined, ops);\n } else if (tag === \"w:hyperlink\") {\n // hyperlinks carry an r:id that maps via relationships to the href.\n const rId = attr(child, \"r:id\");\n const href = rId ? ctx.relationships.get(rId) : undefined;\n const linkMark: MarkAttrs | undefined = href ? { link: { href } } : undefined;\n for (const sub of childrenOf(child)) {\n if (tagOf(sub) === \"w:r\") collectRun(sub, ctx, linkMark, ops);\n }\n }\n }\n return mergeAdjacent(ops);\n}\n\nfunction collectRun(\n run: OoxmlNode,\n ctx: RunContext,\n wrappingMarks: MarkAttrs | undefined,\n out: DeltaOp[],\n): void {\n const marks = mergeMarks(wrappingMarks, runMarks(findChild(run, \"w:rPr\")));\n for (const child of childrenOf(run)) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:t\") {\n const text = textContent(child);\n if (text.length > 0) push(out, text, marks);\n } else if (tag === \"w:tab\") {\n push(out, \"\\t\", marks);\n } else if (tag === \"w:br\") {\n // Treat w:br type=\"page\" as also a soft break for the model — we have no\n // explicit page-break node in the schema; pagination is computed live.\n push(out, \"\\n\", marks);\n } else if (tag === \"w:drawing\") {\n const embed = extractImageFromDrawing(child, ctx);\n if (embed) out.push({ insert: embed });\n }\n // Other run children (w:sym, w:fldChar, etc.) are silently dropped.\n }\n}\n\nfunction runMarks(rPr: OoxmlNode | undefined): MarkAttrs {\n const m: MarkAttrs = {};\n if (!rPr) return m;\n for (const child of childrenOf(rPr)) {\n const tag = tagOf(child);\n if (!tag) continue;\n switch (tag) {\n case \"w:b\":\n if (!isFalse(attr(child, \"w:val\"))) m.bold = true;\n break;\n case \"w:i\":\n if (!isFalse(attr(child, \"w:val\"))) m.italic = true;\n break;\n case \"w:u\": {\n const val = attr(child, \"w:val\");\n if (val && val.toLowerCase() !== \"none\") m.underline = true;\n break;\n }\n case \"w:strike\":\n if (!isFalse(attr(child, \"w:val\"))) m.strike = true;\n break;\n case \"w:color\": {\n const hex = attr(child, \"w:val\");\n const css = docxHexToCssColor(hex);\n if (css) m.color = css;\n break;\n }\n // w:rFonts intentionally ignored: school standard is Arial for all\n // exams. Any font declared in the .docx is dropped on import so the\n // imported doc inherits Arial from `.ed-root` / PDF base stylesheet.\n case \"w:sz\": {\n const halfPts = Number.parseFloat(attr(child, \"w:val\") ?? \"\");\n const css = halfPointsToCssPt(halfPts);\n if (css) m.fontSize = css;\n break;\n }\n }\n }\n return m;\n}\n\nfunction mergeMarks(a: MarkAttrs | undefined, b: MarkAttrs): MarkAttrs {\n if (!a) return b;\n return { ...a, ...b };\n}\n\nfunction textContent(node: OoxmlNode): string {\n let out = \"\";\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) {\n const t = child[\"#text\"];\n if (typeof t === \"string\") out += t;\n }\n }\n return out;\n}\n\nfunction isFalse(val: string | undefined): boolean {\n if (val == null) return false;\n const v = val.toLowerCase();\n return v === \"0\" || v === \"false\" || v === \"none\";\n}\n\nfunction push(out: DeltaOp[], text: string, marks: MarkAttrs): void {\n const op: DeltaOp =\n Object.keys(marks).length === 0 ? { insert: text } : { insert: text, attributes: { ...marks } };\n out.push(op);\n}\n\nfunction mergeAdjacent(ops: DeltaOp[]): DeltaOp[] {\n const out: DeltaOp[] = [];\n for (const op of ops) {\n const prev = out[out.length - 1];\n if (\n prev &&\n typeof prev.insert === \"string\" &&\n typeof op.insert === \"string\" &&\n sameMarks(prev.attributes, op.attributes)\n ) {\n prev.insert = prev.insert + op.insert;\n continue;\n }\n out.push(op);\n }\n return out;\n}\n\nfunction sameMarks(a: MarkAttrs | undefined, b: MarkAttrs | undefined): boolean {\n const ak = a ? Object.keys(a).sort() : [];\n const bk = b ? Object.keys(b).sort() : [];\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i++) {\n if (ak[i] !== bk[i]) return false;\n const av = (a as Record<string, unknown>)[ak[i]];\n const bv = (b as Record<string, unknown>)[bk[i]];\n if (typeof av === \"object\" && av !== null && typeof bv === \"object\" && bv !== null) {\n if (JSON.stringify(av) !== JSON.stringify(bv)) return false;\n } else if (av !== bv) {\n return false;\n }\n }\n return true;\n}\n","import type { AlignValue, BlockAttrs, BlockType, DeltaOp, SerializedBlock } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { NumberingResolver } from \"./numbering\";\nimport { parseAlign } from \"./units\";\n\n/**\n * Map a `<w:p>` element to a SerializedBlock. Decides between\n * `paragraph / heading / blockquote / codeBlock / listItem` based on the\n * paragraph style + `w:pPr` properties — mirrors the heuristics encoded in\n * `@sofereditor/export-docx/src/docx.ts`.\n */\nexport function paragraphToBlock(\n p: OoxmlNode,\n ctx: RunContext,\n numbering: NumberingResolver,\n): SerializedBlock {\n const pPr = findChild(p, \"w:pPr\");\n const numPr = pPr ? findChild(pPr, \"w:numPr\") : undefined;\n const styleId = pPr ? attr(findChild(pPr, \"w:pStyle\") ?? {}, \"w:val\") : undefined;\n\n const delta = paragraphChildrenToDelta(childrenOf(p), ctx);\n const text = collectText(delta);\n const align = parseAlign(pPr ? attr(findChild(pPr, \"w:jc\") ?? {}, \"w:val\") : undefined);\n const isRtl = pPr ? !!findChild(pPr, \"w:bidi\") : false;\n\n // List? — numPr is the strongest signal.\n if (numPr) {\n const resolved = numbering.resolve(numPr);\n const attrs: BlockAttrs = {\n listKind: resolved?.listKind ?? \"bullet\",\n listLevel: resolved?.listLevel ?? 0,\n };\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"listItem\", text, delta, attrs };\n }\n\n // Heading?\n const headingLevel = styleId ? headingFromStyleId(styleId) : null;\n if (headingLevel != null) {\n const attrs: BlockAttrs = { level: headingLevel };\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"heading\", text, delta, attrs };\n }\n\n // Blockquote (export uses left border + 480 twip indent).\n if (pPr && hasLeftBorder(pPr)) {\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"blockquote\", text, delta, attrs };\n }\n\n // Code block (export uses Consolas + light-gray shading).\n if (pPr && hasCodeBlockShading(pPr)) {\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"codeBlock\", text, delta, attrs };\n }\n\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"paragraph\", text, delta, attrs };\n}\n\nfunction collectText(delta: DeltaOp[]): string {\n let out = \"\";\n for (const op of delta) {\n if (typeof op.insert === \"string\") out += op.insert;\n }\n return out;\n}\n\nfunction headingFromStyleId(id: string): 1 | 2 | 3 | 4 | 5 | 6 | null {\n const m = /^Heading\\s*([1-6])$/i.exec(id);\n if (!m) return null;\n const n = Number.parseInt(m[1], 10);\n return (n as 1 | 2 | 3 | 4 | 5 | 6) ?? null;\n}\n\nfunction hasLeftBorder(pPr: OoxmlNode): boolean {\n const pBdr = findChild(pPr, \"w:pBdr\");\n if (!pBdr) return false;\n return !!findChild(pBdr, \"w:left\");\n}\n\nfunction hasCodeBlockShading(pPr: OoxmlNode): boolean {\n const shd = findChild(pPr, \"w:shd\");\n if (!shd) return false;\n const fill = attr(shd, \"w:fill\");\n if (!fill) return false;\n return fill.toLowerCase() === \"f1f5f9\";\n}\n\n/** Whitelist the block types this module produces (used by callers for typing). */\nexport type ParagraphBlockType = Exclude<BlockType, \"table\">;\n/** Whitelist the align values this module produces (used by callers for typing). */\nexport type ParagraphAlign = AlignValue;\n","import type { BlockAttrs, CellAttrs, DeltaOp, SerializedBlock, SerializedCell } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * Map a `<w:tbl>` element to a SerializedBlock(table). The output `cells` array\n * is row-major with `rows * cols` entries — covered slots are filled with\n * `{attrs: {covered: true}}` placeholders so it matches the invariant required\n * by `@sofereditor/core`'s `EditorDocument`.\n */\nexport function tableToBlock(tbl: OoxmlNode, ctx: RunContext): SerializedBlock {\n const tblGrid = findChild(tbl, \"w:tblGrid\");\n const gridCols = tblGrid ? findChildren(tblGrid, \"w:gridCol\") : [];\n const trs = findChildren(tbl, \"w:tr\");\n\n // The docx writer (and Word itself) may emit fewer `<w:gridCol>` entries than\n // the table's logical column count when a row has merged cells. Take the max\n // of (declared gridCols, widest row's total gridSpan) to stay robust.\n let cols = gridCols.length;\n for (const tr of trs) {\n const tcs = findChildren(tr, \"w:tc\");\n let count = 0;\n for (const tc of tcs) count += gridSpanOf(tc);\n if (count > cols) cols = count;\n }\n if (cols < 1) cols = 1;\n const rows = trs.length;\n\n // Build a row-major grid of slots. Each slot is either a real cell or marked\n // as covered. We track which (row, col) is the anchor for vMerge groups.\n const slots: Array<SerializedCell | null> = new Array(rows * cols).fill(null);\n // Anchor of the active vMerge per column index; resets when a non-merge cell\n // is placed in the same column.\n const vMergeAnchorByCol = new Map<number, { row: number; col: number }>();\n\n for (let r = 0; r < rows; r++) {\n const tr = trs[r];\n let cursor = 0;\n for (const tc of findChildren(tr, \"w:tc\")) {\n // Advance the cursor past already-occupied (covered-from-above) slots.\n while (cursor < cols && slots[r * cols + cursor] !== null) cursor++;\n if (cursor >= cols) break;\n\n const tcPr = findChild(tc, \"w:tcPr\");\n const gridSpan = gridSpanOf(tc);\n const vMergeNode = tcPr ? findChild(tcPr, \"w:vMerge\") : undefined;\n const vMergeVal = vMergeNode ? (attr(vMergeNode, \"w:val\") ?? \"continue\") : null;\n\n if (vMergeNode && vMergeVal !== \"restart\") {\n // Continuation of a vertical merge. Cover this row, and bump the anchor's\n // rowspan if we can find it.\n const anchor = vMergeAnchorByCol.get(cursor);\n if (anchor) {\n const anchorIdx = anchor.row * cols + anchor.col;\n const a = slots[anchorIdx];\n if (a) {\n a.attrs = { ...(a.attrs ?? {}), rowspan: (a.attrs?.rowspan ?? 1) + 1 };\n }\n }\n for (let c = 0; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n cursor += gridSpan;\n continue;\n }\n\n // Real cell: build its delta + attrs.\n const delta = cellChildrenToDelta(tc, ctx);\n const text = textOfDelta(delta);\n const cellAttrs: CellAttrs = {};\n if (gridSpan > 1) cellAttrs.colspan = gridSpan;\n // rowspan starts at 1 — vMerge continuations bump it.\n\n const realCell: SerializedCell = { text, delta, attrs: cellAttrs };\n slots[r * cols + cursor] = realCell;\n\n // Mark horizontally-covered slots from gridSpan.\n for (let c = 1; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n\n if (vMergeVal === \"restart\") {\n vMergeAnchorByCol.set(cursor, { row: r, col: cursor });\n } else {\n vMergeAnchorByCol.delete(cursor);\n }\n cursor += gridSpan;\n }\n\n // Fill any trailing un-touched slots in this row with empty placeholders so\n // the row-major invariant holds (length = rows * cols).\n for (let c = 0; c < cols; c++) {\n if (slots[r * cols + c] === null) {\n slots[r * cols + c] = emptyCell();\n }\n }\n }\n\n const cells: SerializedCell[] = slots.map((s) => s ?? emptyCell());\n\n const attrs: BlockAttrs = { rows, cols };\n return { type: \"table\", text: \"\", delta: [], attrs, cells };\n}\n\nfunction gridSpanOf(tc: OoxmlNode): number {\n const tcPr = findChild(tc, \"w:tcPr\");\n if (!tcPr) return 1;\n const gs = findChild(tcPr, \"w:gridSpan\");\n if (!gs) return 1;\n const n = parseIntAttr(attr(gs, \"w:val\"), 1);\n return Math.max(1, n);\n}\n\nfunction cellChildrenToDelta(tc: OoxmlNode, ctx: RunContext): DeltaOp[] {\n // A cell can contain multiple paragraphs; we concat their deltas with a\n // newline between adjacent ones to preserve visual separation. The cell\n // model in @sofereditor/core is a single Y.Text, so we flatten.\n const out: DeltaOp[] = [];\n let first = true;\n for (const child of childrenOf(tc)) {\n if (tagOf(child) !== \"w:p\") continue;\n if (!first) out.push({ insert: \"\\n\" });\n first = false;\n out.push(...paragraphChildrenToDelta(childrenOf(child), ctx));\n }\n return out;\n}\n\nfunction textOfDelta(delta: DeltaOp[]): string {\n let out = \"\";\n for (const op of delta) {\n if (typeof op.insert === \"string\") out += op.insert;\n }\n return out;\n}\n\nfunction coveredCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: { covered: true } };\n}\n\nfunction emptyCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: {} };\n}\n","import type { ListKind } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * Resolves per-paragraph `<w:numPr>` into the editor's `{listKind, listLevel}`.\n *\n * docx numbering is two-step:\n * 1. paragraphs reference a `numId` and an `ilvl`\n * 2. each `<w:num numId=\"...\">` points to a `<w:abstractNum>` via `<w:abstractNumId>`\n * 3. each `<w:lvl ilvl=\"...\">` declares a `<w:numFmt val=\"bullet|decimal|...\">`\n *\n * The export side uses two hardcoded reference names (\"ed-bullet\" / \"ed-ordered\"),\n * but Word-authored documents use arbitrary `numId`s — we resolve by reading the\n * `numFmt` of the target level.\n */\nexport class NumberingResolver {\n private numIdToAbstract = new Map<string, string>();\n private abstractLevelFmt = new Map<string, Map<number, string>>();\n\n constructor(numberingXml: OoxmlNode[] | undefined) {\n if (!numberingXml) return;\n for (const top of numberingXml) {\n if (tagOf(top) !== \"w:numbering\") continue;\n for (const child of childrenOf(top)) {\n const tag = tagOf(child);\n if (tag === \"w:abstractNum\") {\n const abstractId = attr(child, \"w:abstractNumId\");\n if (!abstractId) continue;\n const levels = new Map<number, string>();\n for (const lvl of findChildren(child, \"w:lvl\")) {\n const ilvl = parseIntAttr(attr(lvl, \"w:ilvl\"), 0);\n const fmt = attr(findChild(lvl, \"w:numFmt\") ?? {}, \"w:val\");\n if (fmt) levels.set(ilvl, fmt);\n }\n this.abstractLevelFmt.set(abstractId, levels);\n } else if (tag === \"w:num\") {\n const numId = attr(child, \"w:numId\");\n const abstractRef = findChild(child, \"w:abstractNumId\");\n const abstractId = abstractRef ? attr(abstractRef, \"w:val\") : undefined;\n if (numId && abstractId) this.numIdToAbstract.set(numId, abstractId);\n }\n }\n }\n }\n\n /** Returns `{listKind, listLevel}` for a given paragraph's `<w:numPr>` node, or null. */\n resolve(numPr: OoxmlNode | undefined): { listKind: ListKind; listLevel: number } | null {\n if (!numPr) return null;\n const ilvlRef = findChild(numPr, \"w:ilvl\");\n const numIdRef = findChild(numPr, \"w:numId\");\n if (!numIdRef) return null;\n const numId = attr(numIdRef, \"w:val\");\n if (!numId) return null;\n const listLevel = clampLevel(parseIntAttr(ilvlRef ? attr(ilvlRef, \"w:val\") : undefined, 0));\n const abstractId = this.numIdToAbstract.get(numId);\n if (abstractId == null) {\n // Numbering refs the export side defines without a numbering.xml entry visible\n // to us (the export-docx pipeline generates them dynamically via the docx lib).\n // Fall back by inspecting the numId itself when it is a known sentinel.\n return { listKind: \"bullet\", listLevel };\n }\n const levels = this.abstractLevelFmt.get(abstractId);\n const fmt = (levels?.get(listLevel) ?? \"bullet\").toLowerCase();\n const listKind: ListKind = fmt === \"bullet\" || fmt === \"none\" ? \"bullet\" : \"ordered\";\n return { listKind, listLevel };\n }\n}\n\nfunction clampLevel(n: number): number {\n if (!Number.isFinite(n)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(n)));\n}\n","import { detectPreset, mmToPx, type PageSettings } from \"@sofereditor/core\";\nimport { attr, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * 1 inch = 1440 twips = 25.4 mm. So 1 twip = 25.4/1440 mm.\n *\n * Page size lives in `<w:sectPr><w:pgSz w:w=\"...\" w:h=\"...\"/></w:sectPr>` and\n * margins in `<w:pgMar w:top=\"...\" w:bottom=\"...\" w:left=\"...\" w:right=\"...\"/>`,\n * all in twips. This is the inverse of `sectionPropertiesFor` in\n * `@sofereditor/export-docx`.\n */\nconst MM_PER_TWIP = 25.4 / 1440;\n\nfunction twipsToPx(twips: number): number {\n return mmToPx(twips * MM_PER_TWIP);\n}\n\nexport function sectPrToPageSettings(\n sectPr: OoxmlNode | undefined,\n): PageSettings | undefined {\n if (!sectPr) return undefined;\n const pgSz = findChild(sectPr, \"w:pgSz\");\n const pgMar = findChild(sectPr, \"w:pgMar\");\n if (!pgSz && !pgMar) return undefined;\n\n const widthTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:w\"), 0) : 0;\n const heightTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:h\"), 0) : 0;\n const topTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:top\"), 0) : 0;\n const bottomTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:bottom\"), 0) : 0;\n const leftTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:left\"), 0) : 0;\n const rightTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:right\"), 0) : 0;\n\n if (widthTwips <= 0 && heightTwips <= 0 && topTwips <= 0 && leftTwips <= 0) {\n return undefined;\n }\n\n const width = widthTwips > 0 ? twipsToPx(widthTwips) : 794;\n const height = heightTwips > 0 ? twipsToPx(heightTwips) : 1123;\n const settings: PageSettings = {\n width,\n height,\n marginTop: twipsToPx(topTwips || 0),\n marginBottom: twipsToPx(bottomTwips || 0),\n marginLeft: twipsToPx(leftTwips || 0),\n marginRight: twipsToPx(rightTwips || 0),\n };\n settings.preset = detectPreset(settings.width, settings.height);\n return settings;\n}\n"],"mappings":"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","names":["attrs"]}
|
|
1
|
+
{"version":3,"sources":["../src/docx.ts","../src/parse-xml.ts","../src/units.ts","../src/images.ts","../src/runs.ts","../src/paragraphs.ts","../src/tables.ts","../src/numbering.ts","../src/page-settings.ts"],"sourcesContent":["import { EditorDocument, type SerializedBlock, type SerializedDocument } from \"@sofereditor/core\";\nimport { readDocx, childrenOf, tagOf, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { paragraphToBlock } from \"./paragraphs\";\nimport { tableToBlock } from \"./tables\";\nimport { NumberingResolver } from \"./numbering\";\nimport { sectPrToPageSettings } from \"./page-settings\";\nimport type { RunContext } from \"./runs\";\n\n/**\n * Read a `.docx` (OOXML) Blob/ArrayBuffer/Uint8Array and produce a\n * `SerializedDocument` ready to feed `EditorDocument.fromJSON` (or\n * `loadFromJSON` on an existing instance).\n *\n * The mapping is the inverse of `@sofereditor/export-docx`. Round-trip is exact for\n * documents the editor produced; documents authored externally (Word, Google\n * Docs export) load with marks/lists/tables/images recognized and unsupported\n * features dropped silently.\n */\nexport async function docxBlobToDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<SerializedDocument> {\n const file = await readDocx(input);\n const numbering = new NumberingResolver(file.numberingXml);\n const ctx: RunContext = {\n relationships: file.relationships,\n mediaBytes: file.mediaBytes,\n };\n\n const body = findBody(file.documentXml);\n if (!body) return { blocks: [{ type: \"paragraph\", text: \"\", delta: [], attrs: {} }] };\n\n const blocks: SerializedBlock[] = [];\n for (const child of childrenOf(body)) {\n const tag = tagOf(child);\n if (tag === \"w:p\") {\n blocks.push(paragraphToBlock(child, ctx, numbering));\n } else if (tag === \"w:tbl\") {\n blocks.push(tableToBlock(child, ctx));\n }\n // w:sectPr is parsed below (page settings); other body-level metadata ignored.\n }\n\n if (blocks.length === 0) {\n blocks.push({ type: \"paragraph\", text: \"\", delta: [], attrs: {} });\n }\n\n pruneNonLeaderListStarts(blocks);\n\n const pageSettings = sectPrToPageSettings(findChild(body, \"w:sectPr\"));\n return pageSettings ? { blocks, pageSettings } : { blocks };\n}\n\n/**\n * O Word numera por INSTÂNCIA (`numId`): parágrafos comuns entre itens não\n * resetam a contagem. O editor numera por ADJACÊNCIA: o renderer agrupa\n * `listItem`s consecutivos do mesmo `listKind`/`listStyle`, e um `listStart`\n * em item de CONTINUAÇÃO quebra o grupo ali. O parse gravou o ordinal Word em\n * `listStart` de TODOS os itens ordenados; este pós-passe o mantém apenas nos\n * LÍDERES de grupo (primeiro item, item após bloco comum, ou após item de\n * kind/estilo diferente) — assim o editor renderiza exatamente os números do\n * Word (\"3.\" depois de uma linha de resposta, \"b.\" em subitens) sem fragmentar\n * os grupos.\n */\nexport function pruneNonLeaderListStarts(blocks: SerializedBlock[]): void {\n // Estado do grupo de adjacência corrente: estilo do líder e último ordinal\n // visto por nível (para exigir sequência contígua — duas listas Word\n // independentes coladas, \"1,2,3\" + \"1,2\", NÃO podem fundir num 1..5).\n let grupoAtivo = false;\n let grupoStyle: unknown;\n let ordinais: number[] = [];\n\n const resetGrupo = () => {\n grupoAtivo = false;\n grupoStyle = undefined;\n ordinais = [];\n };\n\n for (const block of blocks) {\n if (block.type !== \"listItem\") {\n resetGrupo();\n continue;\n }\n if ((block.attrs.listKind ?? \"bullet\") !== \"ordered\") {\n // Bullet entre ordenados: mantém o grupo do renderer? Não — kind\n // diferente quebra o grupo lá também.\n resetGrupo();\n continue;\n }\n const level = block.attrs.listLevel ?? 0;\n const ordinal = typeof block.attrs.listStart === \"number\" ? block.attrs.listStart : 1;\n\n const mesmoNivelSequencial = ordinais[level] != null && ordinal === ordinais[level] + 1;\n const entrandoEmNivelNovo = ordinais[level] == null && level > 0 && ordinal === 1;\n const continuaGrupo =\n grupoAtivo &&\n block.attrs.listStyle === grupoStyle &&\n (mesmoNivelSequencial || entrandoEmNivelNovo);\n\n if (continuaGrupo) {\n delete block.attrs.listStart;\n } else {\n // Este item lidera um grupo novo no renderer (listStart preservado).\n grupoAtivo = true;\n grupoStyle = block.attrs.listStyle;\n ordinais = [];\n }\n ordinais[level] = ordinal;\n ordinais.length = level + 1;\n }\n}\n\n/** Convenience: parse → instantiate a fresh `EditorDocument`. */\nexport async function docxBlobToEditorDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<EditorDocument> {\n const serialized = await docxBlobToDocument(input);\n return EditorDocument.fromJSON(serialized);\n}\n\nfunction findBody(documentXml: OoxmlNode[]): OoxmlNode | undefined {\n for (const top of documentXml) {\n if (tagOf(top) === \"w:document\") {\n const body = findChild(top, \"w:body\");\n if (body) return body;\n }\n }\n return undefined;\n}\n","import JSZip from \"jszip\";\nimport { XMLParser } from \"fast-xml-parser\";\n\n/**\n * fast-xml-parser with `preserveOrder: true` produces a list of single-key\n * objects per element. Each element is either:\n *\n * - A tag node: `{ \"<tagName>\": <children-array>, \":@\"?: <attrs> }`\n * - A text node: `{ \"#text\": <string> }`\n *\n * Attributes are prefixed with `@_`. The `:@` key holds attributes for the\n * surrounding tag. Children preserve original document order, which matters\n * for paragraph runs (bold word followed by italic word).\n */\nexport type OoxmlNode = Record<string, unknown>;\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n preserveOrder: true,\n trimValues: false, // keep whitespace inside <w:t>\n parseAttributeValue: false,\n parseTagValue: false,\n});\n\nexport interface DocxFile {\n documentXml: OoxmlNode[];\n numberingXml?: OoxmlNode[];\n /** rId → target path (inside the zip), e.g. \"media/image1.png\". */\n relationships: Map<string, string>;\n /** zip path (without leading \"word/\") → bytes for embedded media. */\n mediaBytes: Map<string, Uint8Array>;\n}\n\nexport async function readDocx(input: Blob | ArrayBuffer | Uint8Array): Promise<DocxFile> {\n const data =\n input instanceof Uint8Array\n ? input\n : input instanceof ArrayBuffer\n ? new Uint8Array(input)\n : new Uint8Array(await (input as Blob).arrayBuffer());\n\n const zip = await JSZip.loadAsync(data);\n const documentXml = await readXml(zip, \"word/document.xml\");\n if (!documentXml) throw new Error(\"Invalid .docx: missing word/document.xml\");\n\n const numberingXml = await readXml(zip, \"word/numbering.xml\");\n const relsXml = await readXml(zip, \"word/_rels/document.xml.rels\");\n const relationships = parseRelationships(relsXml);\n\n const mediaBytes = new Map<string, Uint8Array>();\n const mediaFolder = zip.folder(\"word/media\");\n if (mediaFolder) {\n const promises: Promise<void>[] = [];\n mediaFolder.forEach((relativePath, file) => {\n if (file.dir) return;\n promises.push(\n file.async(\"uint8array\").then((bytes) => {\n mediaBytes.set(`media/${relativePath}`, bytes);\n }),\n );\n });\n await Promise.all(promises);\n }\n\n return { documentXml, numberingXml, relationships, mediaBytes };\n}\n\nasync function readXml(zip: JSZip, path: string): Promise<OoxmlNode[] | undefined> {\n const entry = zip.file(path);\n if (!entry) return undefined;\n const text = await entry.async(\"string\");\n return parser.parse(text) as OoxmlNode[];\n}\n\nfunction parseRelationships(nodes: OoxmlNode[] | undefined): Map<string, string> {\n const out = new Map<string, string>();\n if (!nodes) return out;\n // <Relationships><Relationship Id=\"rId1\" Type=\"...\" Target=\"media/image1.png\"/>...\n for (const top of nodes) {\n const children = top[\"Relationships\"];\n if (!Array.isArray(children)) continue;\n for (const child of children as OoxmlNode[]) {\n if (!(\"Relationship\" in child)) continue;\n const attrs = (child[\":@\"] ?? {}) as Record<string, unknown>;\n const id = attrs[\"@_Id\"];\n const target = attrs[\"@_Target\"];\n if (typeof id === \"string\" && typeof target === \"string\") {\n out.set(id, target);\n }\n }\n }\n return out;\n}\n\n// ---------- generic walkers over preserveOrder shape ----------\n\n/** Returns the tag name (e.g. \"w:p\") of a preserveOrder element, or null for text. */\nexport function tagOf(node: OoxmlNode): string | null {\n for (const key of Object.keys(node)) {\n if (key === \":@\") continue;\n return key;\n }\n return null;\n}\n\n/** Returns the children array of an element, or [] when none. */\nexport function childrenOf(node: OoxmlNode): OoxmlNode[] {\n const tag = tagOf(node);\n if (!tag) return [];\n const v = node[tag];\n return Array.isArray(v) ? (v as OoxmlNode[]) : [];\n}\n\n/** Returns the attribute bag (already with `@_` prefix) for an element. */\nexport function attrsOf(node: OoxmlNode): Record<string, unknown> {\n return ((node[\":@\"] ?? {}) as Record<string, unknown>) ?? {};\n}\n\n/** Read a single `@_key` attribute as a string. */\nexport function attr(node: OoxmlNode, key: string): string | undefined {\n const v = attrsOf(node)[`@_${key}`];\n return typeof v === \"string\" ? v : undefined;\n}\n\n/** Find the first direct child with the given tag name. */\nexport function findChild(node: OoxmlNode, tag: string): OoxmlNode | undefined {\n for (const child of childrenOf(node)) {\n if (tagOf(child) === tag) return child;\n }\n return undefined;\n}\n\n/** Find all direct children with the given tag name. */\nexport function findChildren(node: OoxmlNode, tag: string): OoxmlNode[] {\n const out: OoxmlNode[] = [];\n for (const child of childrenOf(node)) {\n if (tagOf(child) === tag) out.push(child);\n }\n return out;\n}\n\n/** Recursively collect text nodes' concatenated content. */\nexport function textOf(node: OoxmlNode): string {\n let out = \"\";\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) {\n const t = child[\"#text\"];\n if (typeof t === \"string\") out += t;\n } else {\n out += textOf(child);\n }\n }\n return out;\n}\n","/**\n * Unit conversions between OOXML and the editor's serialized format.\n * These are the inverses of the helpers in `@sofereditor/export-docx/src/docx.ts`.\n */\n\n/** docx half-points → CSS pt string. 24 (half-pt) → \"12pt\". */\nexport function halfPointsToCssPt(halfPoints: number): string | undefined {\n if (!Number.isFinite(halfPoints) || halfPoints <= 0) return undefined;\n const pt = halfPoints / 2;\n return `${stripTrailingZeros(pt)}pt`;\n}\n\n/** OOXML RRGGBB (no '#') → CSS hex with leading '#'. */\nexport function docxHexToCssColor(hex: string | undefined): string | undefined {\n if (!hex) return undefined;\n const m = /^[0-9a-f]{6}$/i.exec(hex.trim());\n if (!m) return undefined;\n return `#${hex.toLowerCase()}`;\n}\n\n/** OOXML alignment value → editor AlignValue. */\nexport function parseAlign(val: string | undefined): \"left\" | \"center\" | \"right\" | \"justify\" | undefined {\n if (!val) return undefined;\n switch (val.toLowerCase()) {\n case \"left\":\n case \"start\":\n return \"left\";\n case \"center\":\n return \"center\";\n case \"right\":\n case \"end\":\n return \"right\";\n case \"both\":\n case \"distribute\":\n case \"justify\":\n return \"justify\";\n default:\n return undefined;\n }\n}\n\n/** Parse XML attribute that should be a positive integer. */\nexport function parseIntAttr(val: unknown, fallback = 0): number {\n if (typeof val === \"number\" && Number.isFinite(val)) return Math.trunc(val);\n if (typeof val !== \"string\") return fallback;\n const n = Number.parseInt(val, 10);\n return Number.isFinite(n) ? n : fallback;\n}\n\nfunction stripTrailingZeros(n: number): string {\n // 12 → \"12\", 12.5 → \"12.5\"\n return Number.isInteger(n) ? n.toString() : n.toFixed(2).replace(/\\.?0+$/, \"\");\n}\n","import type { ImageEmbed } from \"@sofereditor/core\";\nimport { attr, childrenOf, tagOf, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/** 1 inch = 914400 EMU = 96 px → 1 px = 9525 EMU. */\nconst EMU_PER_PX = 9525;\n\nconst MIME_BY_EXT: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n bmp: \"image/bmp\",\n svg: \"image/svg+xml\",\n};\n\nexport interface ImageContext {\n relationships: Map<string, string>;\n mediaBytes: Map<string, Uint8Array>;\n}\n\n/**\n * Walk a `<w:drawing>` node and return an `ImageEmbed` if it contains a\n * recognized inline picture. Returns null for unsupported drawings (shapes,\n * charts, etc.).\n */\nexport function extractImageFromDrawing(\n drawing: OoxmlNode,\n ctx: ImageContext,\n): ImageEmbed | null {\n // The interesting bits live deep inside: a:blip carries `r:embed`, and\n // wp:extent carries cx/cy. We do a generic walk because the wrapping nodes\n // differ between inline (`wp:inline`) and anchored (`wp:anchor`) drawings.\n let embedId: string | undefined;\n let cx: number | undefined;\n let cy: number | undefined;\n\n walk(drawing, (n) => {\n const tag = tagOf(n);\n if (!tag) return;\n if (tag === \"a:blip\") {\n const id = attr(n, \"r:embed\") ?? attr(n, \"r:link\");\n if (id) embedId = id;\n } else if (tag === \"wp:extent\") {\n cx = parseIntAttr(attr(n, \"cx\"));\n cy = parseIntAttr(attr(n, \"cy\"));\n }\n });\n\n if (!embedId) return null;\n const target = ctx.relationships.get(embedId);\n if (!target) return null;\n // Relationship targets are typically \"media/image1.png\"; the zip lookup uses\n // the same path (we stored bytes keyed under \"media/<file>\").\n const bytes = ctx.mediaBytes.get(target);\n if (!bytes) return null;\n\n const ext = extensionOf(target);\n const mime = MIME_BY_EXT[ext] ?? \"image/png\";\n const src = `data:${mime};base64,${bytesToBase64(bytes)}`;\n const widthPx = cx != null ? Math.max(1, Math.round(cx / EMU_PER_PX)) : 100;\n const heightPx = cy != null ? Math.max(1, Math.round(cy / EMU_PER_PX)) : 100;\n\n return { type: \"image\", src, width: widthPx, height: heightPx };\n}\n\nfunction walk(node: OoxmlNode, visit: (n: OoxmlNode) => void): void {\n visit(node);\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) continue;\n walk(child, visit);\n }\n}\n\nfunction extensionOf(path: string): string {\n const m = /\\.([a-z0-9]+)$/i.exec(path);\n return m ? m[1].toLowerCase() : \"\";\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n // Browser path.\n if (typeof btoa === \"function\") {\n let bin = \"\";\n const CHUNK = 0x8000;\n for (let i = 0; i < bytes.length; i += CHUNK) {\n bin += String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + CHUNK)));\n }\n return btoa(bin);\n }\n // Node fallback.\n return Buffer.from(bytes).toString(\"base64\");\n}\n","import type { DeltaOp, MarkAttrs } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { docxHexToCssColor, halfPointsToCssPt } from \"./units\";\nimport { extractImageFromDrawing, type ImageContext } from \"./images\";\n\nexport interface RunContext extends ImageContext {}\n\n/**\n * Walk a paragraph's children (post `<w:pPr>`) and emit a `DeltaOp[]`. Combines\n * adjacent runs that share the same mark set into a single op for parity with\n * the export — Y.Text.applyDelta would merge them anyway, but pre-merging keeps\n * round-trip output stable.\n */\nexport function paragraphChildrenToDelta(children: OoxmlNode[], ctx: RunContext): DeltaOp[] {\n const ops: DeltaOp[] = [];\n for (const child of children) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:pPr\") continue;\n if (tag === \"w:r\") {\n collectRun(child, ctx, undefined, ops);\n } else if (tag === \"w:hyperlink\") {\n // hyperlinks carry an r:id that maps via relationships to the href.\n const rId = attr(child, \"r:id\");\n const href = rId ? ctx.relationships.get(rId) : undefined;\n const linkMark: MarkAttrs | undefined = href ? { link: { href } } : undefined;\n for (const sub of childrenOf(child)) {\n if (tagOf(sub) === \"w:r\") collectRun(sub, ctx, linkMark, ops);\n }\n }\n }\n return mergeAdjacent(ops);\n}\n\nfunction collectRun(\n run: OoxmlNode,\n ctx: RunContext,\n wrappingMarks: MarkAttrs | undefined,\n out: DeltaOp[],\n): void {\n const marks = mergeMarks(wrappingMarks, runMarks(findChild(run, \"w:rPr\")));\n for (const child of childrenOf(run)) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:t\") {\n const text = textContent(child);\n if (text.length > 0) push(out, text, marks);\n } else if (tag === \"w:tab\") {\n push(out, \"\\t\", marks);\n } else if (tag === \"w:br\") {\n // Treat w:br type=\"page\" as also a soft break for the model — we have no\n // explicit page-break node in the schema; pagination is computed live.\n push(out, \"\\n\", marks);\n } else if (tag === \"w:drawing\") {\n const embed = extractImageFromDrawing(child, ctx);\n if (embed) out.push({ insert: embed });\n }\n // Other run children (w:sym, w:fldChar, etc.) are silently dropped.\n }\n}\n\n/**\n * Os valores nomeados de `w:highlight` do OOXML. O Word só aceita esta lista\n * fechada — documentos com cor arbitrária usam `w:shd`, que é o que o nosso\n * export emite. `none` fica de fora de propósito: ausência = sem marca.\n */\nconst DOCX_HIGHLIGHT_COLORS: Record<string, string> = {\n yellow: \"#ffff00\",\n green: \"#00ff00\",\n cyan: \"#00ffff\",\n magenta: \"#ff00ff\",\n blue: \"#0000ff\",\n red: \"#ff0000\",\n darkblue: \"#000080\",\n darkcyan: \"#008080\",\n darkgreen: \"#008000\",\n darkmagenta: \"#800080\",\n darkred: \"#800000\",\n darkyellow: \"#808000\",\n darkgray: \"#808080\",\n lightgray: \"#c0c0c0\",\n black: \"#000000\",\n white: \"#ffffff\",\n};\n\nfunction runMarks(rPr: OoxmlNode | undefined): MarkAttrs {\n const m: MarkAttrs = {};\n if (!rPr) return m;\n for (const child of childrenOf(rPr)) {\n const tag = tagOf(child);\n if (!tag) continue;\n switch (tag) {\n case \"w:b\":\n if (!isFalse(attr(child, \"w:val\"))) m.bold = true;\n break;\n case \"w:i\":\n if (!isFalse(attr(child, \"w:val\"))) m.italic = true;\n break;\n case \"w:u\": {\n const val = attr(child, \"w:val\");\n if (val && val.toLowerCase() !== \"none\") m.underline = true;\n break;\n }\n case \"w:strike\":\n if (!isFalse(attr(child, \"w:val\"))) m.strike = true;\n break;\n case \"w:color\": {\n const hex = attr(child, \"w:val\");\n const css = docxHexToCssColor(hex);\n if (css) m.color = css;\n break;\n }\n case \"w:shd\": {\n // Sombreamento de RUN = marca-texto. `auto` significa \"sem cor\".\n const fill = attr(child, \"w:fill\");\n if (fill && fill.toLowerCase() !== \"auto\") {\n const css = docxHexToCssColor(fill);\n if (css) m.highlight = css;\n }\n break;\n }\n case \"w:highlight\": {\n const css = DOCX_HIGHLIGHT_COLORS[(attr(child, \"w:val\") ?? \"\").toLowerCase()];\n if (css) m.highlight = css;\n break;\n }\n // w:rFonts intentionally ignored: school standard is Arial for all\n // exams. Any font declared in the .docx is dropped on import so the\n // imported doc inherits Arial from `.ed-root` / PDF base stylesheet.\n case \"w:sz\": {\n const halfPts = Number.parseFloat(attr(child, \"w:val\") ?? \"\");\n const css = halfPointsToCssPt(halfPts);\n if (css) m.fontSize = css;\n break;\n }\n }\n }\n return m;\n}\n\nfunction mergeMarks(a: MarkAttrs | undefined, b: MarkAttrs): MarkAttrs {\n if (!a) return b;\n return { ...a, ...b };\n}\n\nfunction textContent(node: OoxmlNode): string {\n let out = \"\";\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) {\n const t = child[\"#text\"];\n if (typeof t === \"string\") out += t;\n }\n }\n return out;\n}\n\nfunction isFalse(val: string | undefined): boolean {\n if (val == null) return false;\n const v = val.toLowerCase();\n return v === \"0\" || v === \"false\" || v === \"none\";\n}\n\nfunction push(out: DeltaOp[], text: string, marks: MarkAttrs): void {\n const op: DeltaOp =\n Object.keys(marks).length === 0 ? { insert: text } : { insert: text, attributes: { ...marks } };\n out.push(op);\n}\n\nfunction mergeAdjacent(ops: DeltaOp[]): DeltaOp[] {\n const out: DeltaOp[] = [];\n for (const op of ops) {\n const prev = out[out.length - 1];\n if (\n prev &&\n typeof prev.insert === \"string\" &&\n typeof op.insert === \"string\" &&\n sameMarks(prev.attributes, op.attributes)\n ) {\n prev.insert = prev.insert + op.insert;\n continue;\n }\n out.push(op);\n }\n return out;\n}\n\nfunction sameMarks(a: MarkAttrs | undefined, b: MarkAttrs | undefined): boolean {\n const ak = a ? Object.keys(a).sort() : [];\n const bk = b ? Object.keys(b).sort() : [];\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i++) {\n if (ak[i] !== bk[i]) return false;\n const av = (a as Record<string, unknown>)[ak[i]];\n const bv = (b as Record<string, unknown>)[bk[i]];\n if (typeof av === \"object\" && av !== null && typeof bv === \"object\" && bv !== null) {\n if (JSON.stringify(av) !== JSON.stringify(bv)) return false;\n } else if (av !== bv) {\n return false;\n }\n }\n return true;\n}\n","import type {\n AlignValue,\n AnswerLineSpacing,\n BlockAttrs,\n BlockType,\n DeltaOp,\n SerializedBlock,\n} from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { NumberingResolver } from \"./numbering\";\nimport { parseAlign } from \"./units\";\n\n/**\n * Map a `<w:p>` element to a SerializedBlock. Decides between\n * `paragraph / heading / blockquote / codeBlock / listItem` based on the\n * paragraph style + `w:pPr` properties — mirrors the heuristics encoded in\n * `@sofereditor/export-docx/src/docx.ts`.\n */\nexport function paragraphToBlock(\n p: OoxmlNode,\n ctx: RunContext,\n numbering: NumberingResolver,\n): SerializedBlock {\n const pPr = findChild(p, \"w:pPr\");\n const numPr = pPr ? findChild(pPr, \"w:numPr\") : undefined;\n const styleId = pPr ? attr(findChild(pPr, \"w:pStyle\") ?? {}, \"w:val\") : undefined;\n\n const delta = paragraphChildrenToDelta(childrenOf(p), ctx);\n const text = collectText(delta);\n const align = parseAlign(pPr ? attr(findChild(pPr, \"w:jc\") ?? {}, \"w:val\") : undefined);\n const isRtl = pPr ? !!findChild(pPr, \"w:bidi\") : false;\n\n // List? — numPr is the strongest signal.\n if (numPr) {\n const resolved = numbering.resolve(numPr);\n const attrs: BlockAttrs = {\n listKind: resolved?.listKind ?? \"bullet\",\n listLevel: resolved?.listLevel ?? 0,\n };\n if (resolved?.listKind === \"ordered\") {\n // O ordinal Word vai TEMPORARIAMENTE em listStart de todos os itens; o\n // pós-passe em docx.ts remove dos não-líderes (um listStart em item de\n // continuação quebraria o grupo no renderer). listStyle fica em todos —\n // itens do mesmo estilo agrupam; estilo diferente (ex.: a/b/c entre\n // questões decimais) quebra o grupo exatamente como no Word.\n if (resolved.ordinal != null) attrs.listStart = resolved.ordinal;\n if (resolved.listStyle) attrs.listStyle = resolved.listStyle;\n }\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"listItem\", text, delta, attrs };\n }\n\n // Heading?\n const headingLevel = styleId ? headingFromStyleId(styleId) : null;\n if (headingLevel != null) {\n const attrs: BlockAttrs = { level: headingLevel };\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"heading\", text, delta, attrs };\n }\n\n // Blockquote (export uses left border + 480 twip indent).\n if (pPr && hasLeftBorder(pPr)) {\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"blockquote\", text, delta, attrs };\n }\n\n // Code block (export uses Consolas + light-gray shading).\n if (pPr && hasCodeBlockShading(pPr)) {\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"codeBlock\", text, delta, attrs };\n }\n\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n Object.assign(attrs, readAnswerLine(pPr, text.length > 0));\n return { type: \"paragraph\", text, delta, attrs };\n}\n\n/**\n * Parágrafo SEM texto e com borda INFERIOR = linha de resposta.\n *\n * A exigência de estar vazio é essencial: um parágrafo com texto e régua é um\n * título sublinhado, e convertê-lo em linha de resposta destruiria a semântica\n * do conteúdo. A borda ESQUERDA já é tratada antes como blockquote.\n */\nfunction readAnswerLine(pPr: OoxmlNode | undefined, hasText: boolean): Partial<BlockAttrs> {\n if (!pPr || hasText) return {};\n const pBdr = findChild(pPr, \"w:pBdr\");\n if (!pBdr) return {};\n const bottom = findChild(pBdr, \"w:bottom\");\n if (!bottom) return {};\n const val = (attr(bottom, \"w:val\") ?? \"\").toLowerCase();\n if (val === \"\" || val === \"none\" || val === \"nil\") return {};\n return { answerLine: true, answerLineSpacing: readAnswerLineSpacing(pPr) };\n}\n\n/** 240 twips = 1 linha. Arredonda para o bucket suportado mais próximo. */\nfunction readAnswerLineSpacing(pPr: OoxmlNode): AnswerLineSpacing {\n const sp = findChild(pPr, \"w:spacing\");\n const line = sp ? Number.parseFloat(attr(sp, \"w:line\") ?? \"\") : NaN;\n if (!Number.isFinite(line)) return 1;\n const buckets: AnswerLineSpacing[] = [1, 1.5, 2];\n let best: AnswerLineSpacing = 1;\n let bestDist = Infinity;\n for (const b of buckets) {\n const d = Math.abs(line - 240 * b);\n if (d < bestDist) {\n bestDist = d;\n best = b;\n }\n }\n return best;\n}\n\nfunction collectText(delta: DeltaOp[]): string {\n let out = \"\";\n for (const op of delta) {\n if (typeof op.insert === \"string\") out += op.insert;\n }\n return out;\n}\n\nfunction headingFromStyleId(id: string): 1 | 2 | 3 | 4 | 5 | 6 | null {\n const m = /^Heading\\s*([1-6])$/i.exec(id);\n if (!m) return null;\n const n = Number.parseInt(m[1], 10);\n return (n as 1 | 2 | 3 | 4 | 5 | 6) ?? null;\n}\n\nfunction hasLeftBorder(pPr: OoxmlNode): boolean {\n const pBdr = findChild(pPr, \"w:pBdr\");\n if (!pBdr) return false;\n return !!findChild(pBdr, \"w:left\");\n}\n\nfunction hasCodeBlockShading(pPr: OoxmlNode): boolean {\n const shd = findChild(pPr, \"w:shd\");\n if (!shd) return false;\n const fill = attr(shd, \"w:fill\");\n if (!fill) return false;\n return fill.toLowerCase() === \"f1f5f9\";\n}\n\n/** Whitelist the block types this module produces (used by callers for typing). */\nexport type ParagraphBlockType = Exclude<BlockType, \"table\">;\n/** Whitelist the align values this module produces (used by callers for typing). */\nexport type ParagraphAlign = AlignValue;\n","import type {\n BlockAttrs,\n CellAttrs,\n DeltaOp,\n SerializedBlock,\n SerializedCell,\n TableBorderPreset,\n} from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * Map a `<w:tbl>` element to a SerializedBlock(table). The output `cells` array\n * is row-major with `rows * cols` entries — covered slots are filled with\n * `{attrs: {covered: true}}` placeholders so it matches the invariant required\n * by `@sofereditor/core`'s `EditorDocument`.\n */\nexport function tableToBlock(tbl: OoxmlNode, ctx: RunContext): SerializedBlock {\n const tblGrid = findChild(tbl, \"w:tblGrid\");\n const gridCols = tblGrid ? findChildren(tblGrid, \"w:gridCol\") : [];\n const trs = findChildren(tbl, \"w:tr\");\n\n // The docx writer (and Word itself) may emit fewer `<w:gridCol>` entries than\n // the table's logical column count when a row has merged cells. Take the max\n // of (declared gridCols, widest row's total gridSpan) to stay robust.\n let cols = gridCols.length;\n for (const tr of trs) {\n const tcs = findChildren(tr, \"w:tc\");\n let count = 0;\n for (const tc of tcs) count += gridSpanOf(tc);\n if (count > cols) cols = count;\n }\n if (cols < 1) cols = 1;\n const rows = trs.length;\n\n // Build a row-major grid of slots. Each slot is either a real cell or marked\n // as covered. We track which (row, col) is the anchor for vMerge groups.\n const slots: Array<SerializedCell | null> = new Array(rows * cols).fill(null);\n // Anchor of the active vMerge per column index; resets when a non-merge cell\n // is placed in the same column.\n const vMergeAnchorByCol = new Map<number, { row: number; col: number }>();\n\n for (let r = 0; r < rows; r++) {\n const tr = trs[r];\n let cursor = 0;\n for (const tc of findChildren(tr, \"w:tc\")) {\n // Advance the cursor past already-occupied (covered-from-above) slots.\n while (cursor < cols && slots[r * cols + cursor] !== null) cursor++;\n if (cursor >= cols) break;\n\n const tcPr = findChild(tc, \"w:tcPr\");\n const gridSpan = gridSpanOf(tc);\n const vMergeNode = tcPr ? findChild(tcPr, \"w:vMerge\") : undefined;\n const vMergeVal = vMergeNode ? (attr(vMergeNode, \"w:val\") ?? \"continue\") : null;\n\n if (vMergeNode && vMergeVal !== \"restart\") {\n // Continuation of a vertical merge. Cover this row, and bump the anchor's\n // rowspan if we can find it.\n const anchor = vMergeAnchorByCol.get(cursor);\n if (anchor) {\n const anchorIdx = anchor.row * cols + anchor.col;\n const a = slots[anchorIdx];\n if (a) {\n a.attrs = { ...(a.attrs ?? {}), rowspan: (a.attrs?.rowspan ?? 1) + 1 };\n }\n }\n for (let c = 0; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n cursor += gridSpan;\n continue;\n }\n\n // Real cell: build its delta + attrs.\n const delta = cellChildrenToDelta(tc, ctx);\n const text = textOfDelta(delta);\n const cellAttrs: CellAttrs = {};\n if (gridSpan > 1) cellAttrs.colspan = gridSpan;\n // rowspan starts at 1 — vMerge continuations bump it.\n\n const realCell: SerializedCell = { text, delta, attrs: cellAttrs };\n slots[r * cols + cursor] = realCell;\n\n // Mark horizontally-covered slots from gridSpan.\n for (let c = 1; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n\n if (vMergeVal === \"restart\") {\n vMergeAnchorByCol.set(cursor, { row: r, col: cursor });\n } else {\n vMergeAnchorByCol.delete(cursor);\n }\n cursor += gridSpan;\n }\n\n // Fill any trailing un-touched slots in this row with empty placeholders so\n // the row-major invariant holds (length = rows * cols).\n for (let c = 0; c < cols; c++) {\n if (slots[r * cols + c] === null) {\n slots[r * cols + c] = emptyCell();\n }\n }\n }\n\n const cells: SerializedCell[] = slots.map((s) => s ?? emptyCell());\n\n const attrs: BlockAttrs = { rows, cols };\n const preset = readBorderPreset(tbl);\n if (preset) attrs.borderPreset = preset;\n return { type: \"table\", text: \"\", delta: [], attrs, cells };\n}\n\n/**\n * Reduz `w:tblBorders` ao preset mais próximo.\n *\n * O modelo não tem borda por célula, então `w:tcBorders` continua ignorado —\n * deliberadamente. Combinação não mapeável cai em `all`, que é o padrão visual\n * e o comportamento menos surpreendente para quem importa um documento externo.\n *\n * Retorna `undefined` quando a tabela não declara `tblBorders`, para não gravar\n * a chave à toa.\n */\nfunction readBorderPreset(tbl: OoxmlNode): TableBorderPreset | undefined {\n const tblPr = findChild(tbl, \"w:tblPr\");\n const b = tblPr ? findChild(tblPr, \"w:tblBorders\") : undefined;\n if (!b) return undefined;\n\n // OOXML estrito usa start/end no lugar de left/right.\n const ligado = (...nomes: string[]): boolean =>\n nomes.some((nome) => {\n const n = findChild(b, nome);\n if (!n) return false;\n const val = (attr(n, \"w:val\") ?? \"\").toLowerCase();\n return val !== \"\" && val !== \"none\" && val !== \"nil\";\n });\n\n const top = ligado(\"w:top\");\n const bottom = ligado(\"w:bottom\");\n const left = ligado(\"w:left\", \"w:start\");\n const right = ligado(\"w:right\", \"w:end\");\n const iH = ligado(\"w:insideH\");\n const iV = ligado(\"w:insideV\");\n\n if (top && bottom && left && right && iH && iV) return \"all\";\n if (top && bottom && left && right && !iH && !iV) return \"outer\";\n if (top && bottom && !left && !right && iH && !iV) return \"horizontal\";\n if (!top && !bottom && left && right && !iH && iV) return \"vertical\";\n if (!top && !bottom && !left && !right && !iH && !iV) return \"none\";\n return \"all\";\n}\n\nfunction gridSpanOf(tc: OoxmlNode): number {\n const tcPr = findChild(tc, \"w:tcPr\");\n if (!tcPr) return 1;\n const gs = findChild(tcPr, \"w:gridSpan\");\n if (!gs) return 1;\n const n = parseIntAttr(attr(gs, \"w:val\"), 1);\n return Math.max(1, n);\n}\n\nfunction cellChildrenToDelta(tc: OoxmlNode, ctx: RunContext): DeltaOp[] {\n // A cell can contain multiple paragraphs; we concat their deltas with a\n // newline between adjacent ones to preserve visual separation. The cell\n // model in @sofereditor/core is a single Y.Text, so we flatten.\n const out: DeltaOp[] = [];\n let first = true;\n for (const child of childrenOf(tc)) {\n if (tagOf(child) !== \"w:p\") continue;\n if (!first) out.push({ insert: \"\\n\" });\n first = false;\n out.push(...paragraphChildrenToDelta(childrenOf(child), ctx));\n }\n return out;\n}\n\nfunction textOfDelta(delta: DeltaOp[]): string {\n let out = \"\";\n for (const op of delta) {\n if (typeof op.insert === \"string\") out += op.insert;\n }\n return out;\n}\n\nfunction coveredCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: { covered: true } };\n}\n\nfunction emptyCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: {} };\n}\n","import type { ListKind, ListStyleType } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * Resolves per-paragraph `<w:numPr>` into the editor's `{listKind, listLevel}`.\n *\n * docx numbering is two-step:\n * 1. paragraphs reference a `numId` and an `ilvl`\n * 2. each `<w:num numId=\"...\">` points to a `<w:abstractNum>` via `<w:abstractNumId>`\n * 3. each `<w:lvl ilvl=\"...\">` declares a `<w:numFmt val=\"bullet|decimal|...\">`\n *\n * The export side uses two hardcoded reference names (\"ed-bullet\" / \"ed-ordered\"),\n * but Word-authored documents use arbitrary `numId`s — we resolve by reading the\n * `numFmt` of the target level.\n */\n/** `w:numFmt` → CSS `list-style-type` do editor. Não listados caem em \"decimal\". */\nconst FMT_TO_LIST_STYLE: Record<string, ListStyleType> = {\n decimal: \"decimal\",\n lowerLetter: \"lower-alpha\",\n upperLetter: \"upper-alpha\",\n lowerRoman: \"lower-roman\",\n upperRoman: \"upper-roman\",\n};\n\nexport interface ResolvedNumbering {\n listKind: ListKind;\n listLevel: number;\n /**\n * Número Word desta entrada (contador por instância `numId`+nível, honrando\n * `w:start` e resetando níveis mais profundos). Só para `ordered`. O Word\n * numera por INSTÂNCIA — parágrafos comuns entre itens não resetam a\n * contagem — enquanto o editor numera por adjacência; o importador usa este\n * ordinal para semear `listStart` nos líderes de grupo (ver docx.ts).\n */\n ordinal?: number;\n /** Estilo do nível (`w:numFmt` mapeado). Só para `ordered`. */\n listStyle?: ListStyleType;\n}\n\nexport class NumberingResolver {\n private numIdToAbstract = new Map<string, string>();\n private abstractLevelFmt = new Map<string, Map<number, string>>();\n private abstractLevelStart = new Map<string, Map<number, number>>();\n /** Contadores vivos por instância de lista (numId), indexados por nível. */\n private counters = new Map<string, number[]>();\n\n constructor(numberingXml: OoxmlNode[] | undefined) {\n if (!numberingXml) return;\n for (const top of numberingXml) {\n if (tagOf(top) !== \"w:numbering\") continue;\n for (const child of childrenOf(top)) {\n const tag = tagOf(child);\n if (tag === \"w:abstractNum\") {\n const abstractId = attr(child, \"w:abstractNumId\");\n if (!abstractId) continue;\n const levels = new Map<number, string>();\n const starts = new Map<number, number>();\n for (const lvl of findChildren(child, \"w:lvl\")) {\n const ilvl = parseIntAttr(attr(lvl, \"w:ilvl\"), 0);\n const fmt = attr(findChild(lvl, \"w:numFmt\") ?? {}, \"w:val\");\n if (fmt) levels.set(ilvl, fmt);\n const start = findChild(lvl, \"w:start\");\n if (start) starts.set(ilvl, parseIntAttr(attr(start, \"w:val\"), 1));\n }\n this.abstractLevelFmt.set(abstractId, levels);\n this.abstractLevelStart.set(abstractId, starts);\n } else if (tag === \"w:num\") {\n const numId = attr(child, \"w:numId\");\n const abstractRef = findChild(child, \"w:abstractNumId\");\n const abstractId = abstractRef ? attr(abstractRef, \"w:val\") : undefined;\n if (numId && abstractId) this.numIdToAbstract.set(numId, abstractId);\n }\n }\n }\n }\n\n /**\n * Resolves a paragraph's `<w:numPr>` into `{listKind, listLevel, ordinal?,\n * listStyle?}`, or null. STATEFUL for ordered lists: each call advances the\n * per-`numId` counter (Word instance semantics) — call exactly once per\n * numbered paragraph, in document order.\n */\n resolve(numPr: OoxmlNode | undefined): ResolvedNumbering | null {\n if (!numPr) return null;\n const ilvlRef = findChild(numPr, \"w:ilvl\");\n const numIdRef = findChild(numPr, \"w:numId\");\n if (!numIdRef) return null;\n const numId = attr(numIdRef, \"w:val\");\n if (!numId) return null;\n const listLevel = clampLevel(parseIntAttr(ilvlRef ? attr(ilvlRef, \"w:val\") : undefined, 0));\n const abstractId = this.numIdToAbstract.get(numId);\n if (abstractId == null) {\n // Numbering refs the export side defines without a numbering.xml entry visible\n // to us (the export-docx pipeline generates them dynamically via the docx lib).\n // Fall back by inspecting the numId itself when it is a known sentinel.\n return { listKind: \"bullet\", listLevel };\n }\n const levels = this.abstractLevelFmt.get(abstractId);\n const fmt = levels?.get(listLevel) ?? \"bullet\";\n const fmtLower = fmt.toLowerCase();\n const listKind: ListKind = fmtLower === \"bullet\" || fmtLower === \"none\" ? \"bullet\" : \"ordered\";\n if (listKind === \"bullet\") return { listKind, listLevel };\n\n // Ordinal Word: contador por numId; nível avança, níveis mais profundos resetam.\n const start = this.abstractLevelStart.get(abstractId)?.get(listLevel) ?? 1;\n const c = this.counters.get(numId) ?? [];\n c[listLevel] = c[listLevel] == null ? start : c[listLevel] + 1;\n c.length = listLevel + 1;\n this.counters.set(numId, c);\n\n return {\n listKind,\n listLevel,\n ordinal: c[listLevel],\n listStyle: FMT_TO_LIST_STYLE[fmt] ?? \"decimal\",\n };\n }\n}\n\nfunction clampLevel(n: number): number {\n if (!Number.isFinite(n)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(n)));\n}\n","import { detectPreset, mmToPx, type PageSettings } from \"@sofereditor/core\";\nimport { attr, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * 1 inch = 1440 twips = 25.4 mm. So 1 twip = 25.4/1440 mm.\n *\n * Page size lives in `<w:sectPr><w:pgSz w:w=\"...\" w:h=\"...\"/></w:sectPr>` and\n * margins in `<w:pgMar w:top=\"...\" w:bottom=\"...\" w:left=\"...\" w:right=\"...\"/>`,\n * all in twips. This is the inverse of `sectionPropertiesFor` in\n * `@sofereditor/export-docx`.\n */\nconst MM_PER_TWIP = 25.4 / 1440;\n\nfunction twipsToPx(twips: number): number {\n return mmToPx(twips * MM_PER_TWIP);\n}\n\nexport function sectPrToPageSettings(\n sectPr: OoxmlNode | undefined,\n): PageSettings | undefined {\n if (!sectPr) return undefined;\n const pgSz = findChild(sectPr, \"w:pgSz\");\n const pgMar = findChild(sectPr, \"w:pgMar\");\n if (!pgSz && !pgMar) return undefined;\n\n const widthTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:w\"), 0) : 0;\n const heightTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:h\"), 0) : 0;\n const topTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:top\"), 0) : 0;\n const bottomTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:bottom\"), 0) : 0;\n const leftTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:left\"), 0) : 0;\n const rightTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:right\"), 0) : 0;\n\n if (widthTwips <= 0 && heightTwips <= 0 && topTwips <= 0 && leftTwips <= 0) {\n return undefined;\n }\n\n const width = widthTwips > 0 ? twipsToPx(widthTwips) : 794;\n const height = heightTwips > 0 ? twipsToPx(heightTwips) : 1123;\n const settings: PageSettings = {\n width,\n height,\n marginTop: twipsToPx(topTwips || 0),\n marginBottom: twipsToPx(bottomTwips || 0),\n marginLeft: twipsToPx(leftTwips || 0),\n marginRight: twipsToPx(rightTwips || 0),\n };\n settings.preset = detectPreset(settings.width, settings.height);\n return settings;\n}\n"],"mappings":"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","names":["attrs"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sofereditor/import-docx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "@sofereditor/import-docx — Parse .docx (OOXML) in browser or Node into a SerializedDocument loadable by the Sofer editor.",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"author": "Rafael Marreca",
|
|
@@ -54,12 +54,12 @@
|
|
|
54
54
|
"test": "vitest run"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@sofereditor/core": "^0.
|
|
57
|
+
"@sofereditor/core": "^0.4.0",
|
|
58
58
|
"fast-xml-parser": "^4.5.0",
|
|
59
59
|
"jszip": "^3.10.1"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@sofereditor/export-docx": "^0.
|
|
62
|
+
"@sofereditor/export-docx": "^0.4.0",
|
|
63
63
|
"tsup": "^8.3.5",
|
|
64
64
|
"typescript": "^5.7.2",
|
|
65
65
|
"vitest": "^2.1.8"
|