@sofereditor/import-docx 0.10.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -565,10 +565,11 @@ function tableToBlock(tbl, ctx, larguraUtilTwips, numbering) {
565
565
  cursor += gridSpan;
566
566
  continue;
567
567
  }
568
- const { delta, listKind } = cellChildrenToDelta(tc, ctx, numbering);
568
+ const { delta, listKind, align } = cellChildrenToDelta(tc, ctx, numbering);
569
569
  const text = textOfDelta(delta);
570
570
  const cellAttrs = {};
571
571
  if (listKind) cellAttrs.listKind = listKind;
572
+ if (align) cellAttrs.align = align;
572
573
  if (gridSpan > 1) cellAttrs.colspan = gridSpan;
573
574
  const realCell = { text, delta, attrs: cellAttrs };
574
575
  slots[r * cols + cursor] = realCell;
@@ -590,6 +591,7 @@ function tableToBlock(tbl, ctx, larguraUtilTwips, numbering) {
590
591
  }
591
592
  const cells = slots.map((s) => s ?? emptyCell());
592
593
  const attrs = { rows, cols };
594
+ if (isBidiVisual(tbl)) attrs.dir = "rtl";
593
595
  const preset = readBorderPreset(tbl);
594
596
  if (preset) attrs.borderPreset = preset;
595
597
  const borderColor = readBorderColor(tbl);
@@ -693,18 +695,28 @@ function gridSpanOf(tc) {
693
695
  function cellChildrenToDelta(tc, ctx, numbering) {
694
696
  const out = [];
695
697
  let listKind;
698
+ const aligns = /* @__PURE__ */ new Set();
696
699
  let first = true;
697
700
  for (const child of childrenOf(tc)) {
698
701
  if (tagOf(child) !== "w:p") continue;
699
702
  if (!first) out.push({ insert: "\n" });
700
703
  first = false;
701
704
  const pPr = findChild(child, "w:pPr");
705
+ aligns.add(parseAlign(pPr ? attr(findChild(pPr, "w:jc") ?? {}, "w:val") : void 0));
702
706
  const numPr = pPr ? findChild(pPr, "w:numPr") : void 0;
703
707
  const resolvido = numbering.resolve(numPr);
704
708
  if (resolvido && !listKind) listKind = resolvido.listKind;
705
709
  out.push(...paragraphChildrenToDelta(childrenOf(child), ctx));
706
710
  }
707
- return { delta: out, listKind };
711
+ const align = aligns.size === 1 ? [...aligns][0] : void 0;
712
+ return { delta: out, listKind, align };
713
+ }
714
+ function isBidiVisual(tbl) {
715
+ const tblPr = findChild(tbl, "w:tblPr");
716
+ const node = tblPr ? findChild(tblPr, "w:bidiVisual") : void 0;
717
+ if (!node) return false;
718
+ const val = attr(node, "w:val")?.toLowerCase();
719
+ return val !== "0" && val !== "false" && val !== "off";
708
720
  }
709
721
  function textOfDelta(delta) {
710
722
  let out = "";
@@ -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 { larguraUtilTwipsDe, 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 // Lido ANTES do loop de blocos: `tableToBlock` precisa da largura útil da\n // página para reconstruir `tableWidth` a partir de `w:tblW` (twips\n // absolutos só fazem sentido percentualmente contra a largura da página).\n // `w:sectPr` é sempre um único nó ao final do `body`, então lê-lo cedo não\n // muda nada do que já era verdade sobre a ordem do documento.\n const sectPr = findChild(body, \"w:sectPr\");\n const pageSettings = sectPrToPageSettings(sectPr);\n const larguraUtilTwips = larguraUtilTwipsDe(sectPr);\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, larguraUtilTwips, numbering));\n }\n // w:sectPr already read above; 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 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/**\n * Returns the children array of an element, or [] when none.\n *\n * Content controls (`w:sdt`) são desembrulhados aqui, no lugar. No OOXML eles\n * são wrappers TRANSPARENTES: o conteúdo real vive em `w:sdtContent` e o\n * `w:sdtPr` carrega só metadados (alias, trava). O Word emite `w:sdt` ao\n * BLOQUEAR um trecho, e ele pode aparecer em qualquer nível — envolvendo uma\n * tabela no body, uma `w:tc` dentro de `w:tr`, um `w:p` dentro de `w:tc`, ou um\n * `w:r` dentro de `w:p`.\n *\n * Desembrulhar aqui, e não em cada chamador, é o que garante que todos esses\n * níveis funcionem: `findChild` e `findChildren` também passam por aqui. Antes\n * disso, cada lista branca de tag ignorava o `w:sdt` e o conteúdo travado sumia\n * silenciosamente.\n */\nexport function childrenOf(node: OoxmlNode): OoxmlNode[] {\n const tag = tagOf(node);\n if (!tag) return [];\n const v = node[tag];\n if (!Array.isArray(v)) return [];\n const kids = v as OoxmlNode[];\n // Caminho rápido: sem content control, devolve o array original sem alocar.\n // `childrenOf` é chamada em todo nó do documento.\n let temSdt = false;\n for (const k of kids) {\n if (tagOf(k) === \"w:sdt\") {\n temSdt = true;\n break;\n }\n }\n if (!temSdt) return kids;\n\n const out: OoxmlNode[] = [];\n for (const k of kids) {\n if (tagOf(k) !== \"w:sdt\") {\n out.push(k);\n continue;\n }\n // Só `w:sdtContent` é conteúdo; `w:sdtPr`/`w:sdtEndPr` são metadados.\n // A recursão trata content controls aninhados.\n const content = childrenOf(k).find((c) => tagOf(c) === \"w:sdtContent\");\n if (content) out.push(...childrenOf(content));\n }\n return out;\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/**\n * Um lado de borda (`w:top`, `w:left`, `w:insideH`, …) está LIGADO?\n *\n * A mera presença do elemento não basta: o Word grava blocos de borda\n * COMPLETOS com todos os lados `w:val=\"none\"` — uma declaração no-op. Ler só a\n * presença fazia parágrafos comuns virarem blockquote (cinza e itálico) só\n * porque traziam um `w:pBdr` vazio de significado.\n *\n * Aceita nomes alternativos porque o OOXML estrito usa `w:start`/`w:end` no\n * lugar de `w:left`/`w:right`.\n */\nexport function borderSideOn(parent: OoxmlNode | undefined, ...names: string[]): boolean {\n if (!parent) return false;\n return names.some((name) => {\n const n = findChild(parent, name);\n if (!n) return false;\n const val = (attr(n, \"w:val\") ?? \"\").toLowerCase();\n return val !== \"\" && val !== \"none\" && val !== \"nil\";\n });\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/**\n * OOXML twip → millimeters. Inverse of `convertMillimetersToTwip` from the\n * `docx` package (used by `@sofereditor/export-docx`), reimplemented here\n * because `import-docx` doesn't depend on `docx` — 1 twip = 1/20 pt =\n * 1/1440 in = 25.4/1440 mm.\n */\nexport function twipToMillimeters(twip: number): number {\n return (twip * 25.4) / 1440;\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: a escola padroniza UMA fonte para todas\n // as provas (Verdana desde 08/2026; antes Liberation Sans, antes Arial) e\n // ela mora no CSS, nunca no modelo. Qualquer fonte declarada no .docx é\n // descartada; o conteúdo herda do `.ed-root`.\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 borderSideOn,\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 if (!borderSideOn(pBdr, \"w:bottom\")) 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 // `borderSideOn` (e não a mera presença do elemento): o Word grava pBdr\n // completo com todos os lados \"none\", e ler a presença convertia parágrafo\n // comum em blockquote.\n return borderSideOn(pBdr, \"w:left\", \"w:start\");\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 ListKind,\n SerializedBlock,\n SerializedCell,\n TableBorderPreset,\n} from \"@sofereditor/core\";\nimport {\n MIN_LINHA_PX,\n mmToPx,\n normalizarLarguras,\n travaLarguraTabela,\n} from \"@sofereditor/core\";\nimport {\n attr,\n borderSideOn,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport type { NumberingResolver } from \"./numbering\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { docxHexToCssColor, parseIntAttr, twipToMillimeters } 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 *\n * `larguraUtilTwips` (largura útil da PÁGINA, de `w:sectPr`, lida uma vez em\n * `docxBlobToDocument`) é o que permite reconstruir `tableWidth` a partir de\n * `w:tblW` — sem ela, uma tabela exportada com 50% de largura reimportaria\n * como 100%, porque `normalizarLarguras` só recupera a proporção ENTRE\n * colunas, nunca a largura total da tabela contra a página.\n */\nexport function tableToBlock(\n tbl: OoxmlNode,\n ctx: RunContext,\n larguraUtilTwips: number | undefined,\n numbering: NumberingResolver,\n): 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, listKind } = cellChildrenToDelta(tc, ctx, numbering);\n const text = textOfDelta(delta);\n const cellAttrs: CellAttrs = {};\n if (listKind) cellAttrs.listKind = listKind;\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 const borderColor = readBorderColor(tbl);\n if (borderColor) attrs.borderColor = borderColor;\n // `w:gridCol` chega em twips absolutos; `normalizarLarguras` converte para\n // proporção somando 100 — a mesma conta usada para normalizar documentos\n // antigos em px, então não duplicamos a lógica aqui. Quando faltam gridCols\n // (ou o número não bate com `cols`), o próprio helper cai no split igual, o\n // que é idêntico a omitir o atributo — gravá-lo de qualquer forma é\n // inofensivo.\n const twips = gridCols.map((g) => Number(attr(g, \"w:w\") ?? 0));\n attrs.colWidths = normalizarLarguras(twips, cols);\n const rowHeights = readRowHeights(trs);\n if (rowHeights) attrs.rowHeights = rowHeights;\n const tableWidth = readTableWidth(tbl, larguraUtilTwips);\n if (tableWidth !== undefined) attrs.tableWidth = tableWidth;\n return { type: \"table\", text: \"\", delta: [], attrs, cells };\n}\n\n/**\n * Lê `w:w`, que no OOXML é `ST_MeasurementOrPercent`: pode vir como número\n * puro OU com sufixo `%` (\"50%\"). `Number(\"50%\")` é `NaN`, e sem tratar isso\n * a largura era descartada em silêncio e a tabela voltava com 100%.\n */\nfunction leMedida(bruto: string | undefined): { valor: number; ehPercentSufixo: boolean } | undefined {\n if (bruto === undefined) return undefined;\n const t = bruto.trim();\n const comSufixo = t.endsWith(\"%\");\n const n = Number(comSufixo ? t.slice(0, -1) : t);\n if (!Number.isFinite(n) || n <= 0) return undefined;\n return { valor: n, ehPercentSufixo: comSufixo };\n}\n\n/**\n * `w:tblW` → percentual da largura útil da página (`tableWidth`).\n *\n * Dois tipos possíveis: `dxa` (twips absolutos — divide contra\n * `larguraUtilTwips`, a MESMA conta que `export-docx` fez ao contrário) e\n * `pct` (já percentual, só que em quinquagésimos — `5000` = 100%). Qualquer\n * outra coisa (ausente, `auto`, `larguraUtilTwips` desconhecida) devolve\n * `undefined` e o bloco fica sem `tableWidth` — que é exatamente o default\n * (100%) do modelo, então omitir é seguro.\n */\nfunction readTableWidth(tbl: OoxmlNode, larguraUtilTwips: number | undefined): number | undefined {\n const tblPr = findChild(tbl, \"w:tblPr\");\n const tblW = tblPr ? findChild(tblPr, \"w:tblW\") : undefined;\n if (!tblW) return undefined;\n const medida = leMedida(attr(tblW, \"w:w\"));\n if (!medida) return undefined;\n const type = attr(tblW, \"w:type\");\n\n // O Word aceita tabela mais larga que a página; o editor não. `travaLarguraTabela`\n // é a MESMA faixa que `setTableWidth` usa, importada do core de propósito —\n // duas travas separadas divergiriam, e foi medido o que acontece sem ela:\n // `w:tblW` de 7500 pct entrava como 150%, a tabela era cortada pelo\n // `overflow: hidden` da página e a última coluna sumia da tela E do papel.\n // Um `9639 dxa` entrava como 113% e invadia 80 dos 96px de margem, parando\n // a 17px da borda do papel — dentro da faixa que impressora não imprime.\n if (medida.ehPercentSufixo) return travaLarguraTabela(arredondaPct(medida.valor));\n if (type === \"pct\") return travaLarguraTabela(arredondaPct(medida.valor / 50));\n if (type === \"dxa\" || type === undefined) {\n if (larguraUtilTwips === undefined || larguraUtilTwips <= 0) return undefined;\n return travaLarguraTabela(arredondaPct((medida.valor / larguraUtilTwips) * 100));\n }\n return undefined;\n}\n\nfunction arredondaPct(pct: number): number {\n return Math.round(pct * 100) / 100;\n}\n\n/**\n * `w:trHeight` por linha, convertido de twips para px (o editor guarda\n * altura em px). Devolve `undefined` quando NENHUMA linha declara altura —\n * gravar um array cheio de `undefined`/0 faria uma tabela importada sem\n * altura nascer \"congelada\" no mínimo, quando ela deveria crescer livremente\n * com o conteúdo (mesmo comportamento de hoje, sem `rowHeights`).\n */\nfunction readRowHeights(trs: OoxmlNode[]): number[] | undefined {\n const heights = trs.map((tr) => {\n const trPr = findChild(tr, \"w:trPr\");\n const trHeight = trPr ? findChild(trPr, \"w:trHeight\") : undefined;\n const val = trHeight ? Number(attr(trHeight, \"w:val\") ?? \"\") : NaN;\n return Number.isFinite(val) && val > 0 ? mmToPx(twipToMillimeters(val)) : undefined;\n });\n if (heights.every((h) => h === undefined)) return undefined;\n // Ao menos uma linha declarou altura — as que não declararam ficam no\n // mínimo do modelo (MIN_LINHA_PX), não em 0: é o mesmo \"sem restrição\n // real\" que a linha já tem hoje sem `rowHeights`, só que representável\n // dentro do array sem inventar um valor arbitrário.\n return heights.map((h) => h ?? MIN_LINHA_PX);\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 */\n/**\n * Cor da grade, lida do PRIMEIRO lado LIGADO do `w:tblBorders`.\n *\n * Lados desligados são ignorados de propósito: o Word emite `w:val=\"none\"` com\n * `w:color=\"auto\"`, que não carrega informação de cor — ler dali gravaria lixo.\n * `auto` também não grava, para o documento cair no default do modelo.\n */\nfunction readBorderColor(tbl: OoxmlNode): string | undefined {\n const tblPr = findChild(tbl, \"w:tblPr\");\n const b = tblPr ? findChild(tblPr, \"w:tblBorders\") : undefined;\n if (!b) return undefined;\n const LADOS = [\n \"w:top\",\n \"w:bottom\",\n \"w:left\",\n \"w:start\",\n \"w:right\",\n \"w:end\",\n \"w:insideH\",\n \"w:insideV\",\n ];\n for (const nome of LADOS) {\n const n = findChild(b, nome);\n if (!n) continue;\n const val = (attr(n, \"w:val\") ?? \"\").toLowerCase();\n if (val === \"\" || val === \"none\" || val === \"nil\") continue;\n const hex = attr(n, \"w:color\");\n if (!hex || hex.toLowerCase() === \"auto\") continue;\n const css = docxHexToCssColor(hex);\n if (css) return css;\n }\n return undefined;\n}\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 // `borderSideOn` trata os nomes alternativos do OOXML estrito (start/end) e\n // ignora lados declarados como \"none\"/\"nil\".\n const ligado = (...nomes: string[]): boolean => borderSideOn(b, ...nomes);\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\n/**\n * Delta da célula + o tipo de lista, se os parágrafos dela vierem numerados.\n *\n * A célula no modelo é um único `Y.Text`, então achatamos os `<w:p>` com `\\n`\n * entre eles. Quando ALGUM parágrafo traz `<w:numPr>`, a célula inteira vira\n * lista: cada linha passa a ser um item. Célula mista (alguns com marcador,\n * outros sem) vira lista inteira — é o menos surpreendente, e prova real não\n * mistura.\n *\n * O `listLevel` do Word é descartado de propósito: `CellAttrs` não tem nível\n * (`Y.Text` plano não guarda atributo por linha). Aninhamento dentro de célula\n * exigiria blocos de verdade — ver o spec de 2026-08-24.\n *\n * `numbering.resolve()` MUTA o contador de ordinais para listas ordenadas\n * (`numbering.ts:113-116`). Chamamos mesmo assim, e de propósito: o Word também\n * conta os parágrafos numerados de dentro da tabela, então não chamar\n * dessincronizaria a numeração dos itens que vêm depois dela.\n */\nfunction cellChildrenToDelta(\n tc: OoxmlNode,\n ctx: RunContext,\n numbering: NumberingResolver,\n): { delta: DeltaOp[]; listKind?: ListKind } {\n const out: DeltaOp[] = [];\n let listKind: ListKind | undefined;\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 const pPr = findChild(child, \"w:pPr\");\n const numPr = pPr ? findChild(pPr, \"w:numPr\") : undefined;\n const resolvido = numbering.resolve(numPr);\n if (resolvido && !listKind) listKind = resolvido.listKind;\n out.push(...paragraphChildrenToDelta(childrenOf(child), ctx));\n }\n return { delta: out, listKind };\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\n/**\n * Largura útil da página em twips, direto do `w:sectPr` — `pgSz.w` menos as\n * margens esquerda/direita, tudo em twips, sem passar por px/mm no meio.\n *\n * Usada para reconstruir `tableWidth` (percentual) a partir de `w:tblW`, que\n * também está em twips: dividir twip-contra-twip evita compor DUAS conversões\n * com arredondamento próprio (px→mm→twip na leitura da página E na leitura\n * da tabela) — a mesma fonte de erro documentada acima em `twipsToPx`.\n *\n * `undefined` quando o documento não declara `pgSz` — sem largura de página\n * não há largura útil para calcular `tableWidth` contra, então o import\n * deixa o atributo ausente (default 100) em vez de arriscar uma conta contra\n * um denominador inventado.\n */\nexport function larguraUtilTwipsDe(sectPr: OoxmlNode | undefined): number | undefined {\n if (!sectPr) return undefined;\n const pgSz = findChild(sectPr, \"w:pgSz\");\n if (!pgSz) return undefined;\n const width = parseIntAttr(attr(pgSz, \"w:w\"), 0);\n if (width <= 0) return undefined;\n const pgMar = findChild(sectPr, \"w:pgMar\");\n const left = pgMar ? parseIntAttr(attr(pgMar, \"w:left\"), 0) : 0;\n const right = pgMar ? parseIntAttr(attr(pgMar, \"w:right\"), 0) : 0;\n const util = width - left - right;\n return util > 0 ? util : undefined;\n}\n"],"mappings":"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","names":["import_core","JSZip","attrs","import_core"]}
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 { larguraUtilTwipsDe, 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 // Lido ANTES do loop de blocos: `tableToBlock` precisa da largura útil da\n // página para reconstruir `tableWidth` a partir de `w:tblW` (twips\n // absolutos só fazem sentido percentualmente contra a largura da página).\n // `w:sectPr` é sempre um único nó ao final do `body`, então lê-lo cedo não\n // muda nada do que já era verdade sobre a ordem do documento.\n const sectPr = findChild(body, \"w:sectPr\");\n const pageSettings = sectPrToPageSettings(sectPr);\n const larguraUtilTwips = larguraUtilTwipsDe(sectPr);\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, larguraUtilTwips, numbering));\n }\n // w:sectPr already read above; 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 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/**\n * Returns the children array of an element, or [] when none.\n *\n * Content controls (`w:sdt`) são desembrulhados aqui, no lugar. No OOXML eles\n * são wrappers TRANSPARENTES: o conteúdo real vive em `w:sdtContent` e o\n * `w:sdtPr` carrega só metadados (alias, trava). O Word emite `w:sdt` ao\n * BLOQUEAR um trecho, e ele pode aparecer em qualquer nível — envolvendo uma\n * tabela no body, uma `w:tc` dentro de `w:tr`, um `w:p` dentro de `w:tc`, ou um\n * `w:r` dentro de `w:p`.\n *\n * Desembrulhar aqui, e não em cada chamador, é o que garante que todos esses\n * níveis funcionem: `findChild` e `findChildren` também passam por aqui. Antes\n * disso, cada lista branca de tag ignorava o `w:sdt` e o conteúdo travado sumia\n * silenciosamente.\n */\nexport function childrenOf(node: OoxmlNode): OoxmlNode[] {\n const tag = tagOf(node);\n if (!tag) return [];\n const v = node[tag];\n if (!Array.isArray(v)) return [];\n const kids = v as OoxmlNode[];\n // Caminho rápido: sem content control, devolve o array original sem alocar.\n // `childrenOf` é chamada em todo nó do documento.\n let temSdt = false;\n for (const k of kids) {\n if (tagOf(k) === \"w:sdt\") {\n temSdt = true;\n break;\n }\n }\n if (!temSdt) return kids;\n\n const out: OoxmlNode[] = [];\n for (const k of kids) {\n if (tagOf(k) !== \"w:sdt\") {\n out.push(k);\n continue;\n }\n // Só `w:sdtContent` é conteúdo; `w:sdtPr`/`w:sdtEndPr` são metadados.\n // A recursão trata content controls aninhados.\n const content = childrenOf(k).find((c) => tagOf(c) === \"w:sdtContent\");\n if (content) out.push(...childrenOf(content));\n }\n return out;\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/**\n * Um lado de borda (`w:top`, `w:left`, `w:insideH`, …) está LIGADO?\n *\n * A mera presença do elemento não basta: o Word grava blocos de borda\n * COMPLETOS com todos os lados `w:val=\"none\"` — uma declaração no-op. Ler só a\n * presença fazia parágrafos comuns virarem blockquote (cinza e itálico) só\n * porque traziam um `w:pBdr` vazio de significado.\n *\n * Aceita nomes alternativos porque o OOXML estrito usa `w:start`/`w:end` no\n * lugar de `w:left`/`w:right`.\n */\nexport function borderSideOn(parent: OoxmlNode | undefined, ...names: string[]): boolean {\n if (!parent) return false;\n return names.some((name) => {\n const n = findChild(parent, name);\n if (!n) return false;\n const val = (attr(n, \"w:val\") ?? \"\").toLowerCase();\n return val !== \"\" && val !== \"none\" && val !== \"nil\";\n });\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/**\n * OOXML twip → millimeters. Inverse of `convertMillimetersToTwip` from the\n * `docx` package (used by `@sofereditor/export-docx`), reimplemented here\n * because `import-docx` doesn't depend on `docx` — 1 twip = 1/20 pt =\n * 1/1440 in = 25.4/1440 mm.\n */\nexport function twipToMillimeters(twip: number): number {\n return (twip * 25.4) / 1440;\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: a escola padroniza UMA fonte para todas\n // as provas (Verdana desde 08/2026; antes Liberation Sans, antes Arial) e\n // ela mora no CSS, nunca no modelo. Qualquer fonte declarada no .docx é\n // descartada; o conteúdo herda do `.ed-root`.\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 borderSideOn,\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 if (!borderSideOn(pBdr, \"w:bottom\")) 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 // `borderSideOn` (e não a mera presença do elemento): o Word grava pBdr\n // completo com todos os lados \"none\", e ler a presença convertia parágrafo\n // comum em blockquote.\n return borderSideOn(pBdr, \"w:left\", \"w:start\");\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 ListKind,\n SerializedBlock,\n SerializedCell,\n TableBorderPreset,\n} from \"@sofereditor/core\";\nimport {\n MIN_LINHA_PX,\n mmToPx,\n normalizarLarguras,\n travaLarguraTabela,\n} from \"@sofereditor/core\";\nimport {\n attr,\n borderSideOn,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport type { NumberingResolver } from \"./numbering\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { docxHexToCssColor, parseAlign, parseIntAttr, twipToMillimeters } 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 *\n * `larguraUtilTwips` (largura útil da PÁGINA, de `w:sectPr`, lida uma vez em\n * `docxBlobToDocument`) é o que permite reconstruir `tableWidth` a partir de\n * `w:tblW` — sem ela, uma tabela exportada com 50% de largura reimportaria\n * como 100%, porque `normalizarLarguras` só recupera a proporção ENTRE\n * colunas, nunca a largura total da tabela contra a página.\n */\nexport function tableToBlock(\n tbl: OoxmlNode,\n ctx: RunContext,\n larguraUtilTwips: number | undefined,\n numbering: NumberingResolver,\n): 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, listKind, align } = cellChildrenToDelta(tc, ctx, numbering);\n const text = textOfDelta(delta);\n const cellAttrs: CellAttrs = {};\n if (listKind) cellAttrs.listKind = listKind;\n if (align) cellAttrs.align = align;\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 // Tabela RTL: `w:tc`/`w:gridCol` já vêm em ordem LÓGICA (a 1ª célula é a da\n // direita), então não se reordena nada — `dir` na tabela faz o renderer\n // espelhar as colunas e herdar a direção nas células.\n if (isBidiVisual(tbl)) attrs.dir = \"rtl\";\n const preset = readBorderPreset(tbl);\n if (preset) attrs.borderPreset = preset;\n const borderColor = readBorderColor(tbl);\n if (borderColor) attrs.borderColor = borderColor;\n // `w:gridCol` chega em twips absolutos; `normalizarLarguras` converte para\n // proporção somando 100 — a mesma conta usada para normalizar documentos\n // antigos em px, então não duplicamos a lógica aqui. Quando faltam gridCols\n // (ou o número não bate com `cols`), o próprio helper cai no split igual, o\n // que é idêntico a omitir o atributo — gravá-lo de qualquer forma é\n // inofensivo.\n const twips = gridCols.map((g) => Number(attr(g, \"w:w\") ?? 0));\n attrs.colWidths = normalizarLarguras(twips, cols);\n const rowHeights = readRowHeights(trs);\n if (rowHeights) attrs.rowHeights = rowHeights;\n const tableWidth = readTableWidth(tbl, larguraUtilTwips);\n if (tableWidth !== undefined) attrs.tableWidth = tableWidth;\n return { type: \"table\", text: \"\", delta: [], attrs, cells };\n}\n\n/**\n * Lê `w:w`, que no OOXML é `ST_MeasurementOrPercent`: pode vir como número\n * puro OU com sufixo `%` (\"50%\"). `Number(\"50%\")` é `NaN`, e sem tratar isso\n * a largura era descartada em silêncio e a tabela voltava com 100%.\n */\nfunction leMedida(bruto: string | undefined): { valor: number; ehPercentSufixo: boolean } | undefined {\n if (bruto === undefined) return undefined;\n const t = bruto.trim();\n const comSufixo = t.endsWith(\"%\");\n const n = Number(comSufixo ? t.slice(0, -1) : t);\n if (!Number.isFinite(n) || n <= 0) return undefined;\n return { valor: n, ehPercentSufixo: comSufixo };\n}\n\n/**\n * `w:tblW` → percentual da largura útil da página (`tableWidth`).\n *\n * Dois tipos possíveis: `dxa` (twips absolutos — divide contra\n * `larguraUtilTwips`, a MESMA conta que `export-docx` fez ao contrário) e\n * `pct` (já percentual, só que em quinquagésimos — `5000` = 100%). Qualquer\n * outra coisa (ausente, `auto`, `larguraUtilTwips` desconhecida) devolve\n * `undefined` e o bloco fica sem `tableWidth` — que é exatamente o default\n * (100%) do modelo, então omitir é seguro.\n */\nfunction readTableWidth(tbl: OoxmlNode, larguraUtilTwips: number | undefined): number | undefined {\n const tblPr = findChild(tbl, \"w:tblPr\");\n const tblW = tblPr ? findChild(tblPr, \"w:tblW\") : undefined;\n if (!tblW) return undefined;\n const medida = leMedida(attr(tblW, \"w:w\"));\n if (!medida) return undefined;\n const type = attr(tblW, \"w:type\");\n\n // O Word aceita tabela mais larga que a página; o editor não. `travaLarguraTabela`\n // é a MESMA faixa que `setTableWidth` usa, importada do core de propósito —\n // duas travas separadas divergiriam, e foi medido o que acontece sem ela:\n // `w:tblW` de 7500 pct entrava como 150%, a tabela era cortada pelo\n // `overflow: hidden` da página e a última coluna sumia da tela E do papel.\n // Um `9639 dxa` entrava como 113% e invadia 80 dos 96px de margem, parando\n // a 17px da borda do papel — dentro da faixa que impressora não imprime.\n if (medida.ehPercentSufixo) return travaLarguraTabela(arredondaPct(medida.valor));\n if (type === \"pct\") return travaLarguraTabela(arredondaPct(medida.valor / 50));\n if (type === \"dxa\" || type === undefined) {\n if (larguraUtilTwips === undefined || larguraUtilTwips <= 0) return undefined;\n return travaLarguraTabela(arredondaPct((medida.valor / larguraUtilTwips) * 100));\n }\n return undefined;\n}\n\nfunction arredondaPct(pct: number): number {\n return Math.round(pct * 100) / 100;\n}\n\n/**\n * `w:trHeight` por linha, convertido de twips para px (o editor guarda\n * altura em px). Devolve `undefined` quando NENHUMA linha declara altura —\n * gravar um array cheio de `undefined`/0 faria uma tabela importada sem\n * altura nascer \"congelada\" no mínimo, quando ela deveria crescer livremente\n * com o conteúdo (mesmo comportamento de hoje, sem `rowHeights`).\n */\nfunction readRowHeights(trs: OoxmlNode[]): number[] | undefined {\n const heights = trs.map((tr) => {\n const trPr = findChild(tr, \"w:trPr\");\n const trHeight = trPr ? findChild(trPr, \"w:trHeight\") : undefined;\n const val = trHeight ? Number(attr(trHeight, \"w:val\") ?? \"\") : NaN;\n return Number.isFinite(val) && val > 0 ? mmToPx(twipToMillimeters(val)) : undefined;\n });\n if (heights.every((h) => h === undefined)) return undefined;\n // Ao menos uma linha declarou altura — as que não declararam ficam no\n // mínimo do modelo (MIN_LINHA_PX), não em 0: é o mesmo \"sem restrição\n // real\" que a linha já tem hoje sem `rowHeights`, só que representável\n // dentro do array sem inventar um valor arbitrário.\n return heights.map((h) => h ?? MIN_LINHA_PX);\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 */\n/**\n * Cor da grade, lida do PRIMEIRO lado LIGADO do `w:tblBorders`.\n *\n * Lados desligados são ignorados de propósito: o Word emite `w:val=\"none\"` com\n * `w:color=\"auto\"`, que não carrega informação de cor — ler dali gravaria lixo.\n * `auto` também não grava, para o documento cair no default do modelo.\n */\nfunction readBorderColor(tbl: OoxmlNode): string | undefined {\n const tblPr = findChild(tbl, \"w:tblPr\");\n const b = tblPr ? findChild(tblPr, \"w:tblBorders\") : undefined;\n if (!b) return undefined;\n const LADOS = [\n \"w:top\",\n \"w:bottom\",\n \"w:left\",\n \"w:start\",\n \"w:right\",\n \"w:end\",\n \"w:insideH\",\n \"w:insideV\",\n ];\n for (const nome of LADOS) {\n const n = findChild(b, nome);\n if (!n) continue;\n const val = (attr(n, \"w:val\") ?? \"\").toLowerCase();\n if (val === \"\" || val === \"none\" || val === \"nil\") continue;\n const hex = attr(n, \"w:color\");\n if (!hex || hex.toLowerCase() === \"auto\") continue;\n const css = docxHexToCssColor(hex);\n if (css) return css;\n }\n return undefined;\n}\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 // `borderSideOn` trata os nomes alternativos do OOXML estrito (start/end) e\n // ignora lados declarados como \"none\"/\"nil\".\n const ligado = (...nomes: string[]): boolean => borderSideOn(b, ...nomes);\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\n/**\n * Delta da célula + o tipo de lista, se os parágrafos dela vierem numerados.\n *\n * A célula no modelo é um único `Y.Text`, então achatamos os `<w:p>` com `\\n`\n * entre eles. Quando ALGUM parágrafo traz `<w:numPr>`, a célula inteira vira\n * lista: cada linha passa a ser um item. Célula mista (alguns com marcador,\n * outros sem) vira lista inteira — é o menos surpreendente, e prova real não\n * mistura.\n *\n * O `listLevel` do Word é descartado de propósito: `CellAttrs` não tem nível\n * (`Y.Text` plano não guarda atributo por linha). Aninhamento dentro de célula\n * exigiria blocos de verdade — ver o spec de 2026-08-24.\n *\n * `numbering.resolve()` MUTA o contador de ordinais para listas ordenadas\n * (`numbering.ts:113-116`). Chamamos mesmo assim, e de propósito: o Word também\n * conta os parágrafos numerados de dentro da tabela, então não chamar\n * dessincronizaria a numeração dos itens que vêm depois dela.\n */\nfunction cellChildrenToDelta(\n tc: OoxmlNode,\n ctx: RunContext,\n numbering: NumberingResolver,\n): { delta: DeltaOp[]; listKind?: ListKind; align?: CellAttrs[\"align\"] } {\n const out: DeltaOp[] = [];\n let listKind: ListKind | undefined;\n // A célula é um texto só, com UM alinhamento. Só herda o `w:jc` quando todos\n // os parágrafos concordam — com alinhamentos mistos, escolher um seria\n // inventar formatação.\n const aligns = new Set<CellAttrs[\"align\"]>();\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 const pPr = findChild(child, \"w:pPr\");\n aligns.add(parseAlign(pPr ? attr(findChild(pPr, \"w:jc\") ?? {}, \"w:val\") : undefined));\n const numPr = pPr ? findChild(pPr, \"w:numPr\") : undefined;\n const resolvido = numbering.resolve(numPr);\n if (resolvido && !listKind) listKind = resolvido.listKind;\n out.push(...paragraphChildrenToDelta(childrenOf(child), ctx));\n }\n const align = aligns.size === 1 ? [...aligns][0] : undefined;\n return { delta: out, listKind, align };\n}\n\n/** `<w:bidiVisual/>` no `tblPr`, respeitando o `w:val` de ST_OnOff. */\nfunction isBidiVisual(tbl: OoxmlNode): boolean {\n const tblPr = findChild(tbl, \"w:tblPr\");\n const node = tblPr ? findChild(tblPr, \"w:bidiVisual\") : undefined;\n if (!node) return false;\n const val = attr(node, \"w:val\")?.toLowerCase();\n return val !== \"0\" && val !== \"false\" && val !== \"off\";\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\n/**\n * Largura útil da página em twips, direto do `w:sectPr` — `pgSz.w` menos as\n * margens esquerda/direita, tudo em twips, sem passar por px/mm no meio.\n *\n * Usada para reconstruir `tableWidth` (percentual) a partir de `w:tblW`, que\n * também está em twips: dividir twip-contra-twip evita compor DUAS conversões\n * com arredondamento próprio (px→mm→twip na leitura da página E na leitura\n * da tabela) — a mesma fonte de erro documentada acima em `twipsToPx`.\n *\n * `undefined` quando o documento não declara `pgSz` — sem largura de página\n * não há largura útil para calcular `tableWidth` contra, então o import\n * deixa o atributo ausente (default 100) em vez de arriscar uma conta contra\n * um denominador inventado.\n */\nexport function larguraUtilTwipsDe(sectPr: OoxmlNode | undefined): number | undefined {\n if (!sectPr) return undefined;\n const pgSz = findChild(sectPr, \"w:pgSz\");\n if (!pgSz) return undefined;\n const width = parseIntAttr(attr(pgSz, \"w:w\"), 0);\n if (width <= 0) return undefined;\n const pgMar = findChild(sectPr, \"w:pgMar\");\n const left = pgMar ? parseIntAttr(attr(pgMar, \"w:left\"), 0) : 0;\n const right = pgMar ? parseIntAttr(attr(pgMar, \"w:right\"), 0) : 0;\n const util = width - left - right;\n return util > 0 ? util : undefined;\n}\n"],"mappings":"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","names":["import_core","JSZip","attrs","import_core"]}
package/dist/index.js CHANGED
@@ -533,10 +533,11 @@ function tableToBlock(tbl, ctx, larguraUtilTwips, numbering) {
533
533
  cursor += gridSpan;
534
534
  continue;
535
535
  }
536
- const { delta, listKind } = cellChildrenToDelta(tc, ctx, numbering);
536
+ const { delta, listKind, align } = cellChildrenToDelta(tc, ctx, numbering);
537
537
  const text = textOfDelta(delta);
538
538
  const cellAttrs = {};
539
539
  if (listKind) cellAttrs.listKind = listKind;
540
+ if (align) cellAttrs.align = align;
540
541
  if (gridSpan > 1) cellAttrs.colspan = gridSpan;
541
542
  const realCell = { text, delta, attrs: cellAttrs };
542
543
  slots[r * cols + cursor] = realCell;
@@ -558,6 +559,7 @@ function tableToBlock(tbl, ctx, larguraUtilTwips, numbering) {
558
559
  }
559
560
  const cells = slots.map((s) => s ?? emptyCell());
560
561
  const attrs = { rows, cols };
562
+ if (isBidiVisual(tbl)) attrs.dir = "rtl";
561
563
  const preset = readBorderPreset(tbl);
562
564
  if (preset) attrs.borderPreset = preset;
563
565
  const borderColor = readBorderColor(tbl);
@@ -661,18 +663,28 @@ function gridSpanOf(tc) {
661
663
  function cellChildrenToDelta(tc, ctx, numbering) {
662
664
  const out = [];
663
665
  let listKind;
666
+ const aligns = /* @__PURE__ */ new Set();
664
667
  let first = true;
665
668
  for (const child of childrenOf(tc)) {
666
669
  if (tagOf(child) !== "w:p") continue;
667
670
  if (!first) out.push({ insert: "\n" });
668
671
  first = false;
669
672
  const pPr = findChild(child, "w:pPr");
673
+ aligns.add(parseAlign(pPr ? attr(findChild(pPr, "w:jc") ?? {}, "w:val") : void 0));
670
674
  const numPr = pPr ? findChild(pPr, "w:numPr") : void 0;
671
675
  const resolvido = numbering.resolve(numPr);
672
676
  if (resolvido && !listKind) listKind = resolvido.listKind;
673
677
  out.push(...paragraphChildrenToDelta(childrenOf(child), ctx));
674
678
  }
675
- return { delta: out, listKind };
679
+ const align = aligns.size === 1 ? [...aligns][0] : void 0;
680
+ return { delta: out, listKind, align };
681
+ }
682
+ function isBidiVisual(tbl) {
683
+ const tblPr = findChild(tbl, "w:tblPr");
684
+ const node = tblPr ? findChild(tblPr, "w:bidiVisual") : void 0;
685
+ if (!node) return false;
686
+ const val = attr(node, "w:val")?.toLowerCase();
687
+ return val !== "0" && val !== "false" && val !== "off";
676
688
  }
677
689
  function textOfDelta(delta) {
678
690
  let out = "";
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 { larguraUtilTwipsDe, 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 // Lido ANTES do loop de blocos: `tableToBlock` precisa da largura útil da\n // página para reconstruir `tableWidth` a partir de `w:tblW` (twips\n // absolutos só fazem sentido percentualmente contra a largura da página).\n // `w:sectPr` é sempre um único nó ao final do `body`, então lê-lo cedo não\n // muda nada do que já era verdade sobre a ordem do documento.\n const sectPr = findChild(body, \"w:sectPr\");\n const pageSettings = sectPrToPageSettings(sectPr);\n const larguraUtilTwips = larguraUtilTwipsDe(sectPr);\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, larguraUtilTwips, numbering));\n }\n // w:sectPr already read above; 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 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/**\n * Returns the children array of an element, or [] when none.\n *\n * Content controls (`w:sdt`) são desembrulhados aqui, no lugar. No OOXML eles\n * são wrappers TRANSPARENTES: o conteúdo real vive em `w:sdtContent` e o\n * `w:sdtPr` carrega só metadados (alias, trava). O Word emite `w:sdt` ao\n * BLOQUEAR um trecho, e ele pode aparecer em qualquer nível — envolvendo uma\n * tabela no body, uma `w:tc` dentro de `w:tr`, um `w:p` dentro de `w:tc`, ou um\n * `w:r` dentro de `w:p`.\n *\n * Desembrulhar aqui, e não em cada chamador, é o que garante que todos esses\n * níveis funcionem: `findChild` e `findChildren` também passam por aqui. Antes\n * disso, cada lista branca de tag ignorava o `w:sdt` e o conteúdo travado sumia\n * silenciosamente.\n */\nexport function childrenOf(node: OoxmlNode): OoxmlNode[] {\n const tag = tagOf(node);\n if (!tag) return [];\n const v = node[tag];\n if (!Array.isArray(v)) return [];\n const kids = v as OoxmlNode[];\n // Caminho rápido: sem content control, devolve o array original sem alocar.\n // `childrenOf` é chamada em todo nó do documento.\n let temSdt = false;\n for (const k of kids) {\n if (tagOf(k) === \"w:sdt\") {\n temSdt = true;\n break;\n }\n }\n if (!temSdt) return kids;\n\n const out: OoxmlNode[] = [];\n for (const k of kids) {\n if (tagOf(k) !== \"w:sdt\") {\n out.push(k);\n continue;\n }\n // Só `w:sdtContent` é conteúdo; `w:sdtPr`/`w:sdtEndPr` são metadados.\n // A recursão trata content controls aninhados.\n const content = childrenOf(k).find((c) => tagOf(c) === \"w:sdtContent\");\n if (content) out.push(...childrenOf(content));\n }\n return out;\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/**\n * Um lado de borda (`w:top`, `w:left`, `w:insideH`, …) está LIGADO?\n *\n * A mera presença do elemento não basta: o Word grava blocos de borda\n * COMPLETOS com todos os lados `w:val=\"none\"` — uma declaração no-op. Ler só a\n * presença fazia parágrafos comuns virarem blockquote (cinza e itálico) só\n * porque traziam um `w:pBdr` vazio de significado.\n *\n * Aceita nomes alternativos porque o OOXML estrito usa `w:start`/`w:end` no\n * lugar de `w:left`/`w:right`.\n */\nexport function borderSideOn(parent: OoxmlNode | undefined, ...names: string[]): boolean {\n if (!parent) return false;\n return names.some((name) => {\n const n = findChild(parent, name);\n if (!n) return false;\n const val = (attr(n, \"w:val\") ?? \"\").toLowerCase();\n return val !== \"\" && val !== \"none\" && val !== \"nil\";\n });\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/**\n * OOXML twip → millimeters. Inverse of `convertMillimetersToTwip` from the\n * `docx` package (used by `@sofereditor/export-docx`), reimplemented here\n * because `import-docx` doesn't depend on `docx` — 1 twip = 1/20 pt =\n * 1/1440 in = 25.4/1440 mm.\n */\nexport function twipToMillimeters(twip: number): number {\n return (twip * 25.4) / 1440;\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: a escola padroniza UMA fonte para todas\n // as provas (Verdana desde 08/2026; antes Liberation Sans, antes Arial) e\n // ela mora no CSS, nunca no modelo. Qualquer fonte declarada no .docx é\n // descartada; o conteúdo herda do `.ed-root`.\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 borderSideOn,\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 if (!borderSideOn(pBdr, \"w:bottom\")) 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 // `borderSideOn` (e não a mera presença do elemento): o Word grava pBdr\n // completo com todos os lados \"none\", e ler a presença convertia parágrafo\n // comum em blockquote.\n return borderSideOn(pBdr, \"w:left\", \"w:start\");\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 ListKind,\n SerializedBlock,\n SerializedCell,\n TableBorderPreset,\n} from \"@sofereditor/core\";\nimport {\n MIN_LINHA_PX,\n mmToPx,\n normalizarLarguras,\n travaLarguraTabela,\n} from \"@sofereditor/core\";\nimport {\n attr,\n borderSideOn,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport type { NumberingResolver } from \"./numbering\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { docxHexToCssColor, parseIntAttr, twipToMillimeters } 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 *\n * `larguraUtilTwips` (largura útil da PÁGINA, de `w:sectPr`, lida uma vez em\n * `docxBlobToDocument`) é o que permite reconstruir `tableWidth` a partir de\n * `w:tblW` — sem ela, uma tabela exportada com 50% de largura reimportaria\n * como 100%, porque `normalizarLarguras` só recupera a proporção ENTRE\n * colunas, nunca a largura total da tabela contra a página.\n */\nexport function tableToBlock(\n tbl: OoxmlNode,\n ctx: RunContext,\n larguraUtilTwips: number | undefined,\n numbering: NumberingResolver,\n): 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, listKind } = cellChildrenToDelta(tc, ctx, numbering);\n const text = textOfDelta(delta);\n const cellAttrs: CellAttrs = {};\n if (listKind) cellAttrs.listKind = listKind;\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 const borderColor = readBorderColor(tbl);\n if (borderColor) attrs.borderColor = borderColor;\n // `w:gridCol` chega em twips absolutos; `normalizarLarguras` converte para\n // proporção somando 100 — a mesma conta usada para normalizar documentos\n // antigos em px, então não duplicamos a lógica aqui. Quando faltam gridCols\n // (ou o número não bate com `cols`), o próprio helper cai no split igual, o\n // que é idêntico a omitir o atributo — gravá-lo de qualquer forma é\n // inofensivo.\n const twips = gridCols.map((g) => Number(attr(g, \"w:w\") ?? 0));\n attrs.colWidths = normalizarLarguras(twips, cols);\n const rowHeights = readRowHeights(trs);\n if (rowHeights) attrs.rowHeights = rowHeights;\n const tableWidth = readTableWidth(tbl, larguraUtilTwips);\n if (tableWidth !== undefined) attrs.tableWidth = tableWidth;\n return { type: \"table\", text: \"\", delta: [], attrs, cells };\n}\n\n/**\n * Lê `w:w`, que no OOXML é `ST_MeasurementOrPercent`: pode vir como número\n * puro OU com sufixo `%` (\"50%\"). `Number(\"50%\")` é `NaN`, e sem tratar isso\n * a largura era descartada em silêncio e a tabela voltava com 100%.\n */\nfunction leMedida(bruto: string | undefined): { valor: number; ehPercentSufixo: boolean } | undefined {\n if (bruto === undefined) return undefined;\n const t = bruto.trim();\n const comSufixo = t.endsWith(\"%\");\n const n = Number(comSufixo ? t.slice(0, -1) : t);\n if (!Number.isFinite(n) || n <= 0) return undefined;\n return { valor: n, ehPercentSufixo: comSufixo };\n}\n\n/**\n * `w:tblW` → percentual da largura útil da página (`tableWidth`).\n *\n * Dois tipos possíveis: `dxa` (twips absolutos — divide contra\n * `larguraUtilTwips`, a MESMA conta que `export-docx` fez ao contrário) e\n * `pct` (já percentual, só que em quinquagésimos — `5000` = 100%). Qualquer\n * outra coisa (ausente, `auto`, `larguraUtilTwips` desconhecida) devolve\n * `undefined` e o bloco fica sem `tableWidth` — que é exatamente o default\n * (100%) do modelo, então omitir é seguro.\n */\nfunction readTableWidth(tbl: OoxmlNode, larguraUtilTwips: number | undefined): number | undefined {\n const tblPr = findChild(tbl, \"w:tblPr\");\n const tblW = tblPr ? findChild(tblPr, \"w:tblW\") : undefined;\n if (!tblW) return undefined;\n const medida = leMedida(attr(tblW, \"w:w\"));\n if (!medida) return undefined;\n const type = attr(tblW, \"w:type\");\n\n // O Word aceita tabela mais larga que a página; o editor não. `travaLarguraTabela`\n // é a MESMA faixa que `setTableWidth` usa, importada do core de propósito —\n // duas travas separadas divergiriam, e foi medido o que acontece sem ela:\n // `w:tblW` de 7500 pct entrava como 150%, a tabela era cortada pelo\n // `overflow: hidden` da página e a última coluna sumia da tela E do papel.\n // Um `9639 dxa` entrava como 113% e invadia 80 dos 96px de margem, parando\n // a 17px da borda do papel — dentro da faixa que impressora não imprime.\n if (medida.ehPercentSufixo) return travaLarguraTabela(arredondaPct(medida.valor));\n if (type === \"pct\") return travaLarguraTabela(arredondaPct(medida.valor / 50));\n if (type === \"dxa\" || type === undefined) {\n if (larguraUtilTwips === undefined || larguraUtilTwips <= 0) return undefined;\n return travaLarguraTabela(arredondaPct((medida.valor / larguraUtilTwips) * 100));\n }\n return undefined;\n}\n\nfunction arredondaPct(pct: number): number {\n return Math.round(pct * 100) / 100;\n}\n\n/**\n * `w:trHeight` por linha, convertido de twips para px (o editor guarda\n * altura em px). Devolve `undefined` quando NENHUMA linha declara altura —\n * gravar um array cheio de `undefined`/0 faria uma tabela importada sem\n * altura nascer \"congelada\" no mínimo, quando ela deveria crescer livremente\n * com o conteúdo (mesmo comportamento de hoje, sem `rowHeights`).\n */\nfunction readRowHeights(trs: OoxmlNode[]): number[] | undefined {\n const heights = trs.map((tr) => {\n const trPr = findChild(tr, \"w:trPr\");\n const trHeight = trPr ? findChild(trPr, \"w:trHeight\") : undefined;\n const val = trHeight ? Number(attr(trHeight, \"w:val\") ?? \"\") : NaN;\n return Number.isFinite(val) && val > 0 ? mmToPx(twipToMillimeters(val)) : undefined;\n });\n if (heights.every((h) => h === undefined)) return undefined;\n // Ao menos uma linha declarou altura — as que não declararam ficam no\n // mínimo do modelo (MIN_LINHA_PX), não em 0: é o mesmo \"sem restrição\n // real\" que a linha já tem hoje sem `rowHeights`, só que representável\n // dentro do array sem inventar um valor arbitrário.\n return heights.map((h) => h ?? MIN_LINHA_PX);\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 */\n/**\n * Cor da grade, lida do PRIMEIRO lado LIGADO do `w:tblBorders`.\n *\n * Lados desligados são ignorados de propósito: o Word emite `w:val=\"none\"` com\n * `w:color=\"auto\"`, que não carrega informação de cor — ler dali gravaria lixo.\n * `auto` também não grava, para o documento cair no default do modelo.\n */\nfunction readBorderColor(tbl: OoxmlNode): string | undefined {\n const tblPr = findChild(tbl, \"w:tblPr\");\n const b = tblPr ? findChild(tblPr, \"w:tblBorders\") : undefined;\n if (!b) return undefined;\n const LADOS = [\n \"w:top\",\n \"w:bottom\",\n \"w:left\",\n \"w:start\",\n \"w:right\",\n \"w:end\",\n \"w:insideH\",\n \"w:insideV\",\n ];\n for (const nome of LADOS) {\n const n = findChild(b, nome);\n if (!n) continue;\n const val = (attr(n, \"w:val\") ?? \"\").toLowerCase();\n if (val === \"\" || val === \"none\" || val === \"nil\") continue;\n const hex = attr(n, \"w:color\");\n if (!hex || hex.toLowerCase() === \"auto\") continue;\n const css = docxHexToCssColor(hex);\n if (css) return css;\n }\n return undefined;\n}\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 // `borderSideOn` trata os nomes alternativos do OOXML estrito (start/end) e\n // ignora lados declarados como \"none\"/\"nil\".\n const ligado = (...nomes: string[]): boolean => borderSideOn(b, ...nomes);\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\n/**\n * Delta da célula + o tipo de lista, se os parágrafos dela vierem numerados.\n *\n * A célula no modelo é um único `Y.Text`, então achatamos os `<w:p>` com `\\n`\n * entre eles. Quando ALGUM parágrafo traz `<w:numPr>`, a célula inteira vira\n * lista: cada linha passa a ser um item. Célula mista (alguns com marcador,\n * outros sem) vira lista inteira — é o menos surpreendente, e prova real não\n * mistura.\n *\n * O `listLevel` do Word é descartado de propósito: `CellAttrs` não tem nível\n * (`Y.Text` plano não guarda atributo por linha). Aninhamento dentro de célula\n * exigiria blocos de verdade — ver o spec de 2026-08-24.\n *\n * `numbering.resolve()` MUTA o contador de ordinais para listas ordenadas\n * (`numbering.ts:113-116`). Chamamos mesmo assim, e de propósito: o Word também\n * conta os parágrafos numerados de dentro da tabela, então não chamar\n * dessincronizaria a numeração dos itens que vêm depois dela.\n */\nfunction cellChildrenToDelta(\n tc: OoxmlNode,\n ctx: RunContext,\n numbering: NumberingResolver,\n): { delta: DeltaOp[]; listKind?: ListKind } {\n const out: DeltaOp[] = [];\n let listKind: ListKind | undefined;\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 const pPr = findChild(child, \"w:pPr\");\n const numPr = pPr ? findChild(pPr, \"w:numPr\") : undefined;\n const resolvido = numbering.resolve(numPr);\n if (resolvido && !listKind) listKind = resolvido.listKind;\n out.push(...paragraphChildrenToDelta(childrenOf(child), ctx));\n }\n return { delta: out, listKind };\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\n/**\n * Largura útil da página em twips, direto do `w:sectPr` — `pgSz.w` menos as\n * margens esquerda/direita, tudo em twips, sem passar por px/mm no meio.\n *\n * Usada para reconstruir `tableWidth` (percentual) a partir de `w:tblW`, que\n * também está em twips: dividir twip-contra-twip evita compor DUAS conversões\n * com arredondamento próprio (px→mm→twip na leitura da página E na leitura\n * da tabela) — a mesma fonte de erro documentada acima em `twipsToPx`.\n *\n * `undefined` quando o documento não declara `pgSz` — sem largura de página\n * não há largura útil para calcular `tableWidth` contra, então o import\n * deixa o atributo ausente (default 100) em vez de arriscar uma conta contra\n * um denominador inventado.\n */\nexport function larguraUtilTwipsDe(sectPr: OoxmlNode | undefined): number | undefined {\n if (!sectPr) return undefined;\n const pgSz = findChild(sectPr, \"w:pgSz\");\n if (!pgSz) return undefined;\n const width = parseIntAttr(attr(pgSz, \"w:w\"), 0);\n if (width <= 0) return undefined;\n const pgMar = findChild(sectPr, \"w:pgMar\");\n const left = pgMar ? parseIntAttr(attr(pgMar, \"w:left\"), 0) : 0;\n const right = pgMar ? parseIntAttr(attr(pgMar, \"w:right\"), 0) : 0;\n const util = width - left - right;\n return util > 0 ? util : undefined;\n}\n"],"mappings":"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","names":["attrs","mmToPx","mmToPx"]}
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 { larguraUtilTwipsDe, 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 // Lido ANTES do loop de blocos: `tableToBlock` precisa da largura útil da\n // página para reconstruir `tableWidth` a partir de `w:tblW` (twips\n // absolutos só fazem sentido percentualmente contra a largura da página).\n // `w:sectPr` é sempre um único nó ao final do `body`, então lê-lo cedo não\n // muda nada do que já era verdade sobre a ordem do documento.\n const sectPr = findChild(body, \"w:sectPr\");\n const pageSettings = sectPrToPageSettings(sectPr);\n const larguraUtilTwips = larguraUtilTwipsDe(sectPr);\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, larguraUtilTwips, numbering));\n }\n // w:sectPr already read above; 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 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/**\n * Returns the children array of an element, or [] when none.\n *\n * Content controls (`w:sdt`) são desembrulhados aqui, no lugar. No OOXML eles\n * são wrappers TRANSPARENTES: o conteúdo real vive em `w:sdtContent` e o\n * `w:sdtPr` carrega só metadados (alias, trava). O Word emite `w:sdt` ao\n * BLOQUEAR um trecho, e ele pode aparecer em qualquer nível — envolvendo uma\n * tabela no body, uma `w:tc` dentro de `w:tr`, um `w:p` dentro de `w:tc`, ou um\n * `w:r` dentro de `w:p`.\n *\n * Desembrulhar aqui, e não em cada chamador, é o que garante que todos esses\n * níveis funcionem: `findChild` e `findChildren` também passam por aqui. Antes\n * disso, cada lista branca de tag ignorava o `w:sdt` e o conteúdo travado sumia\n * silenciosamente.\n */\nexport function childrenOf(node: OoxmlNode): OoxmlNode[] {\n const tag = tagOf(node);\n if (!tag) return [];\n const v = node[tag];\n if (!Array.isArray(v)) return [];\n const kids = v as OoxmlNode[];\n // Caminho rápido: sem content control, devolve o array original sem alocar.\n // `childrenOf` é chamada em todo nó do documento.\n let temSdt = false;\n for (const k of kids) {\n if (tagOf(k) === \"w:sdt\") {\n temSdt = true;\n break;\n }\n }\n if (!temSdt) return kids;\n\n const out: OoxmlNode[] = [];\n for (const k of kids) {\n if (tagOf(k) !== \"w:sdt\") {\n out.push(k);\n continue;\n }\n // Só `w:sdtContent` é conteúdo; `w:sdtPr`/`w:sdtEndPr` são metadados.\n // A recursão trata content controls aninhados.\n const content = childrenOf(k).find((c) => tagOf(c) === \"w:sdtContent\");\n if (content) out.push(...childrenOf(content));\n }\n return out;\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/**\n * Um lado de borda (`w:top`, `w:left`, `w:insideH`, …) está LIGADO?\n *\n * A mera presença do elemento não basta: o Word grava blocos de borda\n * COMPLETOS com todos os lados `w:val=\"none\"` — uma declaração no-op. Ler só a\n * presença fazia parágrafos comuns virarem blockquote (cinza e itálico) só\n * porque traziam um `w:pBdr` vazio de significado.\n *\n * Aceita nomes alternativos porque o OOXML estrito usa `w:start`/`w:end` no\n * lugar de `w:left`/`w:right`.\n */\nexport function borderSideOn(parent: OoxmlNode | undefined, ...names: string[]): boolean {\n if (!parent) return false;\n return names.some((name) => {\n const n = findChild(parent, name);\n if (!n) return false;\n const val = (attr(n, \"w:val\") ?? \"\").toLowerCase();\n return val !== \"\" && val !== \"none\" && val !== \"nil\";\n });\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/**\n * OOXML twip → millimeters. Inverse of `convertMillimetersToTwip` from the\n * `docx` package (used by `@sofereditor/export-docx`), reimplemented here\n * because `import-docx` doesn't depend on `docx` — 1 twip = 1/20 pt =\n * 1/1440 in = 25.4/1440 mm.\n */\nexport function twipToMillimeters(twip: number): number {\n return (twip * 25.4) / 1440;\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: a escola padroniza UMA fonte para todas\n // as provas (Verdana desde 08/2026; antes Liberation Sans, antes Arial) e\n // ela mora no CSS, nunca no modelo. Qualquer fonte declarada no .docx é\n // descartada; o conteúdo herda do `.ed-root`.\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 borderSideOn,\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 if (!borderSideOn(pBdr, \"w:bottom\")) 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 // `borderSideOn` (e não a mera presença do elemento): o Word grava pBdr\n // completo com todos os lados \"none\", e ler a presença convertia parágrafo\n // comum em blockquote.\n return borderSideOn(pBdr, \"w:left\", \"w:start\");\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 ListKind,\n SerializedBlock,\n SerializedCell,\n TableBorderPreset,\n} from \"@sofereditor/core\";\nimport {\n MIN_LINHA_PX,\n mmToPx,\n normalizarLarguras,\n travaLarguraTabela,\n} from \"@sofereditor/core\";\nimport {\n attr,\n borderSideOn,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport type { NumberingResolver } from \"./numbering\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { docxHexToCssColor, parseAlign, parseIntAttr, twipToMillimeters } 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 *\n * `larguraUtilTwips` (largura útil da PÁGINA, de `w:sectPr`, lida uma vez em\n * `docxBlobToDocument`) é o que permite reconstruir `tableWidth` a partir de\n * `w:tblW` — sem ela, uma tabela exportada com 50% de largura reimportaria\n * como 100%, porque `normalizarLarguras` só recupera a proporção ENTRE\n * colunas, nunca a largura total da tabela contra a página.\n */\nexport function tableToBlock(\n tbl: OoxmlNode,\n ctx: RunContext,\n larguraUtilTwips: number | undefined,\n numbering: NumberingResolver,\n): 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, listKind, align } = cellChildrenToDelta(tc, ctx, numbering);\n const text = textOfDelta(delta);\n const cellAttrs: CellAttrs = {};\n if (listKind) cellAttrs.listKind = listKind;\n if (align) cellAttrs.align = align;\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 // Tabela RTL: `w:tc`/`w:gridCol` já vêm em ordem LÓGICA (a 1ª célula é a da\n // direita), então não se reordena nada — `dir` na tabela faz o renderer\n // espelhar as colunas e herdar a direção nas células.\n if (isBidiVisual(tbl)) attrs.dir = \"rtl\";\n const preset = readBorderPreset(tbl);\n if (preset) attrs.borderPreset = preset;\n const borderColor = readBorderColor(tbl);\n if (borderColor) attrs.borderColor = borderColor;\n // `w:gridCol` chega em twips absolutos; `normalizarLarguras` converte para\n // proporção somando 100 — a mesma conta usada para normalizar documentos\n // antigos em px, então não duplicamos a lógica aqui. Quando faltam gridCols\n // (ou o número não bate com `cols`), o próprio helper cai no split igual, o\n // que é idêntico a omitir o atributo — gravá-lo de qualquer forma é\n // inofensivo.\n const twips = gridCols.map((g) => Number(attr(g, \"w:w\") ?? 0));\n attrs.colWidths = normalizarLarguras(twips, cols);\n const rowHeights = readRowHeights(trs);\n if (rowHeights) attrs.rowHeights = rowHeights;\n const tableWidth = readTableWidth(tbl, larguraUtilTwips);\n if (tableWidth !== undefined) attrs.tableWidth = tableWidth;\n return { type: \"table\", text: \"\", delta: [], attrs, cells };\n}\n\n/**\n * Lê `w:w`, que no OOXML é `ST_MeasurementOrPercent`: pode vir como número\n * puro OU com sufixo `%` (\"50%\"). `Number(\"50%\")` é `NaN`, e sem tratar isso\n * a largura era descartada em silêncio e a tabela voltava com 100%.\n */\nfunction leMedida(bruto: string | undefined): { valor: number; ehPercentSufixo: boolean } | undefined {\n if (bruto === undefined) return undefined;\n const t = bruto.trim();\n const comSufixo = t.endsWith(\"%\");\n const n = Number(comSufixo ? t.slice(0, -1) : t);\n if (!Number.isFinite(n) || n <= 0) return undefined;\n return { valor: n, ehPercentSufixo: comSufixo };\n}\n\n/**\n * `w:tblW` → percentual da largura útil da página (`tableWidth`).\n *\n * Dois tipos possíveis: `dxa` (twips absolutos — divide contra\n * `larguraUtilTwips`, a MESMA conta que `export-docx` fez ao contrário) e\n * `pct` (já percentual, só que em quinquagésimos — `5000` = 100%). Qualquer\n * outra coisa (ausente, `auto`, `larguraUtilTwips` desconhecida) devolve\n * `undefined` e o bloco fica sem `tableWidth` — que é exatamente o default\n * (100%) do modelo, então omitir é seguro.\n */\nfunction readTableWidth(tbl: OoxmlNode, larguraUtilTwips: number | undefined): number | undefined {\n const tblPr = findChild(tbl, \"w:tblPr\");\n const tblW = tblPr ? findChild(tblPr, \"w:tblW\") : undefined;\n if (!tblW) return undefined;\n const medida = leMedida(attr(tblW, \"w:w\"));\n if (!medida) return undefined;\n const type = attr(tblW, \"w:type\");\n\n // O Word aceita tabela mais larga que a página; o editor não. `travaLarguraTabela`\n // é a MESMA faixa que `setTableWidth` usa, importada do core de propósito —\n // duas travas separadas divergiriam, e foi medido o que acontece sem ela:\n // `w:tblW` de 7500 pct entrava como 150%, a tabela era cortada pelo\n // `overflow: hidden` da página e a última coluna sumia da tela E do papel.\n // Um `9639 dxa` entrava como 113% e invadia 80 dos 96px de margem, parando\n // a 17px da borda do papel — dentro da faixa que impressora não imprime.\n if (medida.ehPercentSufixo) return travaLarguraTabela(arredondaPct(medida.valor));\n if (type === \"pct\") return travaLarguraTabela(arredondaPct(medida.valor / 50));\n if (type === \"dxa\" || type === undefined) {\n if (larguraUtilTwips === undefined || larguraUtilTwips <= 0) return undefined;\n return travaLarguraTabela(arredondaPct((medida.valor / larguraUtilTwips) * 100));\n }\n return undefined;\n}\n\nfunction arredondaPct(pct: number): number {\n return Math.round(pct * 100) / 100;\n}\n\n/**\n * `w:trHeight` por linha, convertido de twips para px (o editor guarda\n * altura em px). Devolve `undefined` quando NENHUMA linha declara altura —\n * gravar um array cheio de `undefined`/0 faria uma tabela importada sem\n * altura nascer \"congelada\" no mínimo, quando ela deveria crescer livremente\n * com o conteúdo (mesmo comportamento de hoje, sem `rowHeights`).\n */\nfunction readRowHeights(trs: OoxmlNode[]): number[] | undefined {\n const heights = trs.map((tr) => {\n const trPr = findChild(tr, \"w:trPr\");\n const trHeight = trPr ? findChild(trPr, \"w:trHeight\") : undefined;\n const val = trHeight ? Number(attr(trHeight, \"w:val\") ?? \"\") : NaN;\n return Number.isFinite(val) && val > 0 ? mmToPx(twipToMillimeters(val)) : undefined;\n });\n if (heights.every((h) => h === undefined)) return undefined;\n // Ao menos uma linha declarou altura — as que não declararam ficam no\n // mínimo do modelo (MIN_LINHA_PX), não em 0: é o mesmo \"sem restrição\n // real\" que a linha já tem hoje sem `rowHeights`, só que representável\n // dentro do array sem inventar um valor arbitrário.\n return heights.map((h) => h ?? MIN_LINHA_PX);\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 */\n/**\n * Cor da grade, lida do PRIMEIRO lado LIGADO do `w:tblBorders`.\n *\n * Lados desligados são ignorados de propósito: o Word emite `w:val=\"none\"` com\n * `w:color=\"auto\"`, que não carrega informação de cor — ler dali gravaria lixo.\n * `auto` também não grava, para o documento cair no default do modelo.\n */\nfunction readBorderColor(tbl: OoxmlNode): string | undefined {\n const tblPr = findChild(tbl, \"w:tblPr\");\n const b = tblPr ? findChild(tblPr, \"w:tblBorders\") : undefined;\n if (!b) return undefined;\n const LADOS = [\n \"w:top\",\n \"w:bottom\",\n \"w:left\",\n \"w:start\",\n \"w:right\",\n \"w:end\",\n \"w:insideH\",\n \"w:insideV\",\n ];\n for (const nome of LADOS) {\n const n = findChild(b, nome);\n if (!n) continue;\n const val = (attr(n, \"w:val\") ?? \"\").toLowerCase();\n if (val === \"\" || val === \"none\" || val === \"nil\") continue;\n const hex = attr(n, \"w:color\");\n if (!hex || hex.toLowerCase() === \"auto\") continue;\n const css = docxHexToCssColor(hex);\n if (css) return css;\n }\n return undefined;\n}\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 // `borderSideOn` trata os nomes alternativos do OOXML estrito (start/end) e\n // ignora lados declarados como \"none\"/\"nil\".\n const ligado = (...nomes: string[]): boolean => borderSideOn(b, ...nomes);\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\n/**\n * Delta da célula + o tipo de lista, se os parágrafos dela vierem numerados.\n *\n * A célula no modelo é um único `Y.Text`, então achatamos os `<w:p>` com `\\n`\n * entre eles. Quando ALGUM parágrafo traz `<w:numPr>`, a célula inteira vira\n * lista: cada linha passa a ser um item. Célula mista (alguns com marcador,\n * outros sem) vira lista inteira — é o menos surpreendente, e prova real não\n * mistura.\n *\n * O `listLevel` do Word é descartado de propósito: `CellAttrs` não tem nível\n * (`Y.Text` plano não guarda atributo por linha). Aninhamento dentro de célula\n * exigiria blocos de verdade — ver o spec de 2026-08-24.\n *\n * `numbering.resolve()` MUTA o contador de ordinais para listas ordenadas\n * (`numbering.ts:113-116`). Chamamos mesmo assim, e de propósito: o Word também\n * conta os parágrafos numerados de dentro da tabela, então não chamar\n * dessincronizaria a numeração dos itens que vêm depois dela.\n */\nfunction cellChildrenToDelta(\n tc: OoxmlNode,\n ctx: RunContext,\n numbering: NumberingResolver,\n): { delta: DeltaOp[]; listKind?: ListKind; align?: CellAttrs[\"align\"] } {\n const out: DeltaOp[] = [];\n let listKind: ListKind | undefined;\n // A célula é um texto só, com UM alinhamento. Só herda o `w:jc` quando todos\n // os parágrafos concordam — com alinhamentos mistos, escolher um seria\n // inventar formatação.\n const aligns = new Set<CellAttrs[\"align\"]>();\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 const pPr = findChild(child, \"w:pPr\");\n aligns.add(parseAlign(pPr ? attr(findChild(pPr, \"w:jc\") ?? {}, \"w:val\") : undefined));\n const numPr = pPr ? findChild(pPr, \"w:numPr\") : undefined;\n const resolvido = numbering.resolve(numPr);\n if (resolvido && !listKind) listKind = resolvido.listKind;\n out.push(...paragraphChildrenToDelta(childrenOf(child), ctx));\n }\n const align = aligns.size === 1 ? [...aligns][0] : undefined;\n return { delta: out, listKind, align };\n}\n\n/** `<w:bidiVisual/>` no `tblPr`, respeitando o `w:val` de ST_OnOff. */\nfunction isBidiVisual(tbl: OoxmlNode): boolean {\n const tblPr = findChild(tbl, \"w:tblPr\");\n const node = tblPr ? findChild(tblPr, \"w:bidiVisual\") : undefined;\n if (!node) return false;\n const val = attr(node, \"w:val\")?.toLowerCase();\n return val !== \"0\" && val !== \"false\" && val !== \"off\";\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\n/**\n * Largura útil da página em twips, direto do `w:sectPr` — `pgSz.w` menos as\n * margens esquerda/direita, tudo em twips, sem passar por px/mm no meio.\n *\n * Usada para reconstruir `tableWidth` (percentual) a partir de `w:tblW`, que\n * também está em twips: dividir twip-contra-twip evita compor DUAS conversões\n * com arredondamento próprio (px→mm→twip na leitura da página E na leitura\n * da tabela) — a mesma fonte de erro documentada acima em `twipsToPx`.\n *\n * `undefined` quando o documento não declara `pgSz` — sem largura de página\n * não há largura útil para calcular `tableWidth` contra, então o import\n * deixa o atributo ausente (default 100) em vez de arriscar uma conta contra\n * um denominador inventado.\n */\nexport function larguraUtilTwipsDe(sectPr: OoxmlNode | undefined): number | undefined {\n if (!sectPr) return undefined;\n const pgSz = findChild(sectPr, \"w:pgSz\");\n if (!pgSz) return undefined;\n const width = parseIntAttr(attr(pgSz, \"w:w\"), 0);\n if (width <= 0) return undefined;\n const pgMar = findChild(sectPr, \"w:pgMar\");\n const left = pgMar ? parseIntAttr(attr(pgMar, \"w:left\"), 0) : 0;\n const right = pgMar ? parseIntAttr(attr(pgMar, \"w:right\"), 0) : 0;\n const util = width - left - right;\n return util > 0 ? util : undefined;\n}\n"],"mappings":"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","names":["attrs","mmToPx","mmToPx"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sofereditor/import-docx",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
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",
@@ -32,36 +32,24 @@
32
32
  "require": "./dist/index.cjs"
33
33
  }
34
34
  },
35
- "publishConfig": {
36
- "main": "./dist/index.cjs",
37
- "module": "./dist/index.js",
38
- "types": "./dist/index.d.ts",
39
- "exports": {
40
- ".": {
41
- "types": "./dist/index.d.ts",
42
- "import": "./dist/index.js",
43
- "require": "./dist/index.cjs"
44
- }
45
- }
46
- },
47
35
  "files": [
48
36
  "dist"
49
37
  ],
50
- "scripts": {
51
- "build": "tsup",
52
- "dev": "tsup --watch",
53
- "typecheck": "tsc --noEmit",
54
- "test": "vitest run"
55
- },
56
38
  "dependencies": {
57
- "@sofereditor/core": "^0.10.0",
58
39
  "fast-xml-parser": "^4.5.0",
59
- "jszip": "^3.10.1"
40
+ "jszip": "^3.10.1",
41
+ "@sofereditor/core": "0.10.1"
60
42
  },
61
43
  "devDependencies": {
62
- "@sofereditor/export-docx": "^0.10.0",
63
44
  "tsup": "^8.3.5",
64
45
  "typescript": "^5.7.2",
65
- "vitest": "^2.1.8"
46
+ "vitest": "^2.1.8",
47
+ "@sofereditor/export-docx": "0.10.1"
48
+ },
49
+ "scripts": {
50
+ "build": "tsup",
51
+ "dev": "tsup --watch",
52
+ "typecheck": "tsc --noEmit",
53
+ "test": "vitest run"
66
54
  }
67
- }
55
+ }