@sofereditor/import-docx 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -150,6 +150,15 @@ function findChildren(node, tag) {
150
150
  }
151
151
  return out;
152
152
  }
153
+ function borderSideOn(parent, ...names) {
154
+ if (!parent) return false;
155
+ return names.some((name) => {
156
+ const n = findChild(parent, name);
157
+ if (!n) return false;
158
+ const val = (attr(n, "w:val") ?? "").toLowerCase();
159
+ return val !== "" && val !== "none" && val !== "nil";
160
+ });
161
+ }
153
162
 
154
163
  // src/units.ts
155
164
  function halfPointsToCssPt(halfPoints) {
@@ -465,10 +474,7 @@ function readAnswerLine(pPr, hasText) {
465
474
  if (!pPr || hasText) return {};
466
475
  const pBdr = findChild(pPr, "w:pBdr");
467
476
  if (!pBdr) return {};
468
- const bottom = findChild(pBdr, "w:bottom");
469
- if (!bottom) return {};
470
- const val = (attr(bottom, "w:val") ?? "").toLowerCase();
471
- if (val === "" || val === "none" || val === "nil") return {};
477
+ if (!borderSideOn(pBdr, "w:bottom")) return {};
472
478
  return { answerLine: true, answerLineSpacing: readAnswerLineSpacing(pPr) };
473
479
  }
474
480
  function readAnswerLineSpacing(pPr) {
@@ -503,7 +509,7 @@ function headingFromStyleId(id) {
503
509
  function hasLeftBorder(pPr) {
504
510
  const pBdr = findChild(pPr, "w:pBdr");
505
511
  if (!pBdr) return false;
506
- return !!findChild(pBdr, "w:left");
512
+ return borderSideOn(pBdr, "w:left", "w:start");
507
513
  }
508
514
  function hasCodeBlockShading(pPr) {
509
515
  const shd = findChild(pPr, "w:shd");
@@ -614,12 +620,7 @@ function readBorderPreset(tbl) {
614
620
  const tblPr = findChild(tbl, "w:tblPr");
615
621
  const b = tblPr ? findChild(tblPr, "w:tblBorders") : void 0;
616
622
  if (!b) return void 0;
617
- const ligado = (...nomes) => nomes.some((nome) => {
618
- const n = findChild(b, nome);
619
- if (!n) return false;
620
- const val = (attr(n, "w:val") ?? "").toLowerCase();
621
- return val !== "" && val !== "none" && val !== "nil";
622
- });
623
+ const ligado = (...nomes) => borderSideOn(b, ...nomes);
623
624
  const top = ligado("w:top");
624
625
  const bottom = ligado("w:bottom");
625
626
  const left = ligado("w:left", "w:start");
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/docx.ts","../src/parse-xml.ts","../src/units.ts","../src/images.ts","../src/runs.ts","../src/paragraphs.ts","../src/tables.ts","../src/numbering.ts","../src/page-settings.ts"],"sourcesContent":["export { docxBlobToDocument, docxBlobToEditorDocument } from \"./docx\";\n","import { EditorDocument, type SerializedBlock, type SerializedDocument } from \"@sofereditor/core\";\nimport { readDocx, childrenOf, tagOf, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { paragraphToBlock } from \"./paragraphs\";\nimport { tableToBlock } from \"./tables\";\nimport { NumberingResolver } from \"./numbering\";\nimport { sectPrToPageSettings } from \"./page-settings\";\nimport type { RunContext } from \"./runs\";\n\n/**\n * Read a `.docx` (OOXML) Blob/ArrayBuffer/Uint8Array and produce a\n * `SerializedDocument` ready to feed `EditorDocument.fromJSON` (or\n * `loadFromJSON` on an existing instance).\n *\n * The mapping is the inverse of `@sofereditor/export-docx`. Round-trip is exact for\n * documents the editor produced; documents authored externally (Word, Google\n * Docs export) load with marks/lists/tables/images recognized and unsupported\n * features dropped silently.\n */\nexport async function docxBlobToDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<SerializedDocument> {\n const file = await readDocx(input);\n const numbering = new NumberingResolver(file.numberingXml);\n const ctx: RunContext = {\n relationships: file.relationships,\n mediaBytes: file.mediaBytes,\n };\n\n const body = findBody(file.documentXml);\n if (!body) return { blocks: [{ type: \"paragraph\", text: \"\", delta: [], attrs: {} }] };\n\n const blocks: SerializedBlock[] = [];\n for (const child of childrenOf(body)) {\n const tag = tagOf(child);\n if (tag === \"w:p\") {\n blocks.push(paragraphToBlock(child, ctx, numbering));\n } else if (tag === \"w:tbl\") {\n blocks.push(tableToBlock(child, ctx));\n }\n // w:sectPr is parsed below (page settings); other body-level metadata ignored.\n }\n\n if (blocks.length === 0) {\n blocks.push({ type: \"paragraph\", text: \"\", delta: [], attrs: {} });\n }\n\n pruneNonLeaderListStarts(blocks);\n\n const pageSettings = sectPrToPageSettings(findChild(body, \"w:sectPr\"));\n return pageSettings ? { blocks, pageSettings } : { blocks };\n}\n\n/**\n * O Word numera por INSTÂNCIA (`numId`): parágrafos comuns entre itens não\n * resetam a contagem. O editor numera por ADJACÊNCIA: o renderer agrupa\n * `listItem`s consecutivos do mesmo `listKind`/`listStyle`, e um `listStart`\n * em item de CONTINUAÇÃO quebra o grupo ali. O parse gravou o ordinal Word em\n * `listStart` de TODOS os itens ordenados; este pós-passe o mantém apenas nos\n * LÍDERES de grupo (primeiro item, item após bloco comum, ou após item de\n * kind/estilo diferente) — assim o editor renderiza exatamente os números do\n * Word (\"3.\" depois de uma linha de resposta, \"b.\" em subitens) sem fragmentar\n * os grupos.\n */\nexport function pruneNonLeaderListStarts(blocks: SerializedBlock[]): void {\n // Estado do grupo de adjacência corrente: estilo do líder e último ordinal\n // visto por nível (para exigir sequência contígua — duas listas Word\n // independentes coladas, \"1,2,3\" + \"1,2\", NÃO podem fundir num 1..5).\n let grupoAtivo = false;\n let grupoStyle: unknown;\n let ordinais: number[] = [];\n\n const resetGrupo = () => {\n grupoAtivo = false;\n grupoStyle = undefined;\n ordinais = [];\n };\n\n for (const block of blocks) {\n if (block.type !== \"listItem\") {\n resetGrupo();\n continue;\n }\n if ((block.attrs.listKind ?? \"bullet\") !== \"ordered\") {\n // Bullet entre ordenados: mantém o grupo do renderer? Não — kind\n // diferente quebra o grupo lá também.\n resetGrupo();\n continue;\n }\n const level = block.attrs.listLevel ?? 0;\n const ordinal = typeof block.attrs.listStart === \"number\" ? block.attrs.listStart : 1;\n\n const mesmoNivelSequencial = ordinais[level] != null && ordinal === ordinais[level] + 1;\n const entrandoEmNivelNovo = ordinais[level] == null && level > 0 && ordinal === 1;\n const continuaGrupo =\n grupoAtivo &&\n block.attrs.listStyle === grupoStyle &&\n (mesmoNivelSequencial || entrandoEmNivelNovo);\n\n if (continuaGrupo) {\n delete block.attrs.listStart;\n } else {\n // Este item lidera um grupo novo no renderer (listStart preservado).\n grupoAtivo = true;\n grupoStyle = block.attrs.listStyle;\n ordinais = [];\n }\n ordinais[level] = ordinal;\n ordinais.length = level + 1;\n }\n}\n\n/** Convenience: parse → instantiate a fresh `EditorDocument`. */\nexport async function docxBlobToEditorDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<EditorDocument> {\n const serialized = await docxBlobToDocument(input);\n return EditorDocument.fromJSON(serialized);\n}\n\nfunction findBody(documentXml: OoxmlNode[]): OoxmlNode | undefined {\n for (const top of documentXml) {\n if (tagOf(top) === \"w:document\") {\n const body = findChild(top, \"w:body\");\n if (body) return body;\n }\n }\n return undefined;\n}\n","import JSZip from \"jszip\";\nimport { XMLParser } from \"fast-xml-parser\";\n\n/**\n * fast-xml-parser with `preserveOrder: true` produces a list of single-key\n * objects per element. Each element is either:\n *\n * - A tag node: `{ \"<tagName>\": <children-array>, \":@\"?: <attrs> }`\n * - A text node: `{ \"#text\": <string> }`\n *\n * Attributes are prefixed with `@_`. The `:@` key holds attributes for the\n * surrounding tag. Children preserve original document order, which matters\n * for paragraph runs (bold word followed by italic word).\n */\nexport type OoxmlNode = Record<string, unknown>;\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n preserveOrder: true,\n trimValues: false, // keep whitespace inside <w:t>\n parseAttributeValue: false,\n parseTagValue: false,\n});\n\nexport interface DocxFile {\n documentXml: OoxmlNode[];\n numberingXml?: OoxmlNode[];\n /** rId → target path (inside the zip), e.g. \"media/image1.png\". */\n relationships: Map<string, string>;\n /** zip path (without leading \"word/\") → bytes for embedded media. */\n mediaBytes: Map<string, Uint8Array>;\n}\n\nexport async function readDocx(input: Blob | ArrayBuffer | Uint8Array): Promise<DocxFile> {\n const data =\n input instanceof Uint8Array\n ? input\n : input instanceof ArrayBuffer\n ? new Uint8Array(input)\n : new Uint8Array(await (input as Blob).arrayBuffer());\n\n const zip = await JSZip.loadAsync(data);\n const documentXml = await readXml(zip, \"word/document.xml\");\n if (!documentXml) throw new Error(\"Invalid .docx: missing word/document.xml\");\n\n const numberingXml = await readXml(zip, \"word/numbering.xml\");\n const relsXml = await readXml(zip, \"word/_rels/document.xml.rels\");\n const relationships = parseRelationships(relsXml);\n\n const mediaBytes = new Map<string, Uint8Array>();\n const mediaFolder = zip.folder(\"word/media\");\n if (mediaFolder) {\n const promises: Promise<void>[] = [];\n mediaFolder.forEach((relativePath, file) => {\n if (file.dir) return;\n promises.push(\n file.async(\"uint8array\").then((bytes) => {\n mediaBytes.set(`media/${relativePath}`, bytes);\n }),\n );\n });\n await Promise.all(promises);\n }\n\n return { documentXml, numberingXml, relationships, mediaBytes };\n}\n\nasync function readXml(zip: JSZip, path: string): Promise<OoxmlNode[] | undefined> {\n const entry = zip.file(path);\n if (!entry) return undefined;\n const text = await entry.async(\"string\");\n return parser.parse(text) as OoxmlNode[];\n}\n\nfunction parseRelationships(nodes: OoxmlNode[] | undefined): Map<string, string> {\n const out = new Map<string, string>();\n if (!nodes) return out;\n // <Relationships><Relationship Id=\"rId1\" Type=\"...\" Target=\"media/image1.png\"/>...\n for (const top of nodes) {\n const children = top[\"Relationships\"];\n if (!Array.isArray(children)) continue;\n for (const child of children as OoxmlNode[]) {\n if (!(\"Relationship\" in child)) continue;\n const attrs = (child[\":@\"] ?? {}) as Record<string, unknown>;\n const id = attrs[\"@_Id\"];\n const target = attrs[\"@_Target\"];\n if (typeof id === \"string\" && typeof target === \"string\") {\n out.set(id, target);\n }\n }\n }\n return out;\n}\n\n// ---------- generic walkers over preserveOrder shape ----------\n\n/** Returns the tag name (e.g. \"w:p\") of a preserveOrder element, or null for text. */\nexport function tagOf(node: OoxmlNode): string | null {\n for (const key of Object.keys(node)) {\n if (key === \":@\") continue;\n return key;\n }\n return null;\n}\n\n/**\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 * Unit conversions between OOXML and the editor's serialized format.\n * These are the inverses of the helpers in `@sofereditor/export-docx/src/docx.ts`.\n */\n\n/** docx half-points → CSS pt string. 24 (half-pt) → \"12pt\". */\nexport function halfPointsToCssPt(halfPoints: number): string | undefined {\n if (!Number.isFinite(halfPoints) || halfPoints <= 0) return undefined;\n const pt = halfPoints / 2;\n return `${stripTrailingZeros(pt)}pt`;\n}\n\n/** OOXML RRGGBB (no '#') → CSS hex with leading '#'. */\nexport function docxHexToCssColor(hex: string | undefined): string | undefined {\n if (!hex) return undefined;\n const m = /^[0-9a-f]{6}$/i.exec(hex.trim());\n if (!m) return undefined;\n return `#${hex.toLowerCase()}`;\n}\n\n/** OOXML alignment value → editor AlignValue. */\nexport function parseAlign(val: string | undefined): \"left\" | \"center\" | \"right\" | \"justify\" | undefined {\n if (!val) return undefined;\n switch (val.toLowerCase()) {\n case \"left\":\n case \"start\":\n return \"left\";\n case \"center\":\n return \"center\";\n case \"right\":\n case \"end\":\n return \"right\";\n case \"both\":\n case \"distribute\":\n case \"justify\":\n return \"justify\";\n default:\n return undefined;\n }\n}\n\n/** Parse XML attribute that should be a positive integer. */\nexport function parseIntAttr(val: unknown, fallback = 0): number {\n if (typeof val === \"number\" && Number.isFinite(val)) return Math.trunc(val);\n if (typeof val !== \"string\") return fallback;\n const n = Number.parseInt(val, 10);\n return Number.isFinite(n) ? n : fallback;\n}\n\nfunction stripTrailingZeros(n: number): string {\n // 12 → \"12\", 12.5 → \"12.5\"\n return Number.isInteger(n) ? n.toString() : n.toFixed(2).replace(/\\.?0+$/, \"\");\n}\n","import type { ImageEmbed } from \"@sofereditor/core\";\nimport { attr, childrenOf, tagOf, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/** 1 inch = 914400 EMU = 96 px → 1 px = 9525 EMU. */\nconst EMU_PER_PX = 9525;\n\nconst MIME_BY_EXT: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n bmp: \"image/bmp\",\n svg: \"image/svg+xml\",\n};\n\nexport interface ImageContext {\n relationships: Map<string, string>;\n mediaBytes: Map<string, Uint8Array>;\n}\n\n/**\n * Walk a `<w:drawing>` node and return an `ImageEmbed` if it contains a\n * recognized inline picture. Returns null for unsupported drawings (shapes,\n * charts, etc.).\n */\nexport function extractImageFromDrawing(\n drawing: OoxmlNode,\n ctx: ImageContext,\n): ImageEmbed | null {\n // The interesting bits live deep inside: a:blip carries `r:embed`, and\n // wp:extent carries cx/cy. We do a generic walk because the wrapping nodes\n // differ between inline (`wp:inline`) and anchored (`wp:anchor`) drawings.\n let embedId: string | undefined;\n let cx: number | undefined;\n let cy: number | undefined;\n\n walk(drawing, (n) => {\n const tag = tagOf(n);\n if (!tag) return;\n if (tag === \"a:blip\") {\n const id = attr(n, \"r:embed\") ?? attr(n, \"r:link\");\n if (id) embedId = id;\n } else if (tag === \"wp:extent\") {\n cx = parseIntAttr(attr(n, \"cx\"));\n cy = parseIntAttr(attr(n, \"cy\"));\n }\n });\n\n if (!embedId) return null;\n const target = ctx.relationships.get(embedId);\n if (!target) return null;\n // Relationship targets are typically \"media/image1.png\"; the zip lookup uses\n // the same path (we stored bytes keyed under \"media/<file>\").\n const bytes = ctx.mediaBytes.get(target);\n if (!bytes) return null;\n\n const ext = extensionOf(target);\n const mime = MIME_BY_EXT[ext] ?? \"image/png\";\n const src = `data:${mime};base64,${bytesToBase64(bytes)}`;\n const widthPx = cx != null ? Math.max(1, Math.round(cx / EMU_PER_PX)) : 100;\n const heightPx = cy != null ? Math.max(1, Math.round(cy / EMU_PER_PX)) : 100;\n\n return { type: \"image\", src, width: widthPx, height: heightPx };\n}\n\nfunction walk(node: OoxmlNode, visit: (n: OoxmlNode) => void): void {\n visit(node);\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) continue;\n walk(child, visit);\n }\n}\n\nfunction extensionOf(path: string): string {\n const m = /\\.([a-z0-9]+)$/i.exec(path);\n return m ? m[1].toLowerCase() : \"\";\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n // Browser path.\n if (typeof btoa === \"function\") {\n let bin = \"\";\n const CHUNK = 0x8000;\n for (let i = 0; i < bytes.length; i += CHUNK) {\n bin += String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + CHUNK)));\n }\n return btoa(bin);\n }\n // Node fallback.\n return Buffer.from(bytes).toString(\"base64\");\n}\n","import type { DeltaOp, MarkAttrs } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { docxHexToCssColor, halfPointsToCssPt } from \"./units\";\nimport { extractImageFromDrawing, type ImageContext } from \"./images\";\n\nexport interface RunContext extends ImageContext {}\n\n/**\n * Walk a paragraph's children (post `<w:pPr>`) and emit a `DeltaOp[]`. Combines\n * adjacent runs that share the same mark set into a single op for parity with\n * the export — Y.Text.applyDelta would merge them anyway, but pre-merging keeps\n * round-trip output stable.\n */\nexport function paragraphChildrenToDelta(children: OoxmlNode[], ctx: RunContext): DeltaOp[] {\n const ops: DeltaOp[] = [];\n for (const child of children) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:pPr\") continue;\n if (tag === \"w:r\") {\n collectRun(child, ctx, undefined, ops);\n } else if (tag === \"w:hyperlink\") {\n // hyperlinks carry an r:id that maps via relationships to the href.\n const rId = attr(child, \"r:id\");\n const href = rId ? ctx.relationships.get(rId) : undefined;\n const linkMark: MarkAttrs | undefined = href ? { link: { href } } : undefined;\n for (const sub of childrenOf(child)) {\n if (tagOf(sub) === \"w:r\") collectRun(sub, ctx, linkMark, ops);\n }\n }\n }\n return mergeAdjacent(ops);\n}\n\nfunction collectRun(\n run: OoxmlNode,\n ctx: RunContext,\n wrappingMarks: MarkAttrs | undefined,\n out: DeltaOp[],\n): void {\n const marks = mergeMarks(wrappingMarks, runMarks(findChild(run, \"w:rPr\")));\n for (const child of childrenOf(run)) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:t\") {\n const text = textContent(child);\n if (text.length > 0) push(out, text, marks);\n } else if (tag === \"w:tab\") {\n push(out, \"\\t\", marks);\n } else if (tag === \"w:br\") {\n // Treat w:br type=\"page\" as also a soft break for the model — we have no\n // explicit page-break node in the schema; pagination is computed live.\n push(out, \"\\n\", marks);\n } else if (tag === \"w:drawing\") {\n const embed = extractImageFromDrawing(child, ctx);\n if (embed) out.push({ insert: embed });\n }\n // Other run children (w:sym, w:fldChar, etc.) are silently dropped.\n }\n}\n\n/**\n * Os valores nomeados de `w:highlight` do OOXML. O Word só aceita esta lista\n * fechada — documentos com cor arbitrária usam `w:shd`, que é o que o nosso\n * export emite. `none` fica de fora de propósito: ausência = sem marca.\n */\nconst DOCX_HIGHLIGHT_COLORS: Record<string, string> = {\n yellow: \"#ffff00\",\n green: \"#00ff00\",\n cyan: \"#00ffff\",\n magenta: \"#ff00ff\",\n blue: \"#0000ff\",\n red: \"#ff0000\",\n darkblue: \"#000080\",\n darkcyan: \"#008080\",\n darkgreen: \"#008000\",\n darkmagenta: \"#800080\",\n darkred: \"#800000\",\n darkyellow: \"#808000\",\n darkgray: \"#808080\",\n lightgray: \"#c0c0c0\",\n black: \"#000000\",\n white: \"#ffffff\",\n};\n\nfunction runMarks(rPr: OoxmlNode | undefined): MarkAttrs {\n const m: MarkAttrs = {};\n if (!rPr) return m;\n for (const child of childrenOf(rPr)) {\n const tag = tagOf(child);\n if (!tag) continue;\n switch (tag) {\n case \"w:b\":\n if (!isFalse(attr(child, \"w:val\"))) m.bold = true;\n break;\n case \"w:i\":\n if (!isFalse(attr(child, \"w:val\"))) m.italic = true;\n break;\n case \"w:u\": {\n const val = attr(child, \"w:val\");\n if (val && val.toLowerCase() !== \"none\") m.underline = true;\n break;\n }\n case \"w:strike\":\n if (!isFalse(attr(child, \"w:val\"))) m.strike = true;\n break;\n case \"w:color\": {\n const hex = attr(child, \"w:val\");\n const css = docxHexToCssColor(hex);\n if (css) m.color = css;\n break;\n }\n case \"w:shd\": {\n // Sombreamento de RUN = marca-texto. `auto` significa \"sem cor\".\n const fill = attr(child, \"w:fill\");\n if (fill && fill.toLowerCase() !== \"auto\") {\n const css = docxHexToCssColor(fill);\n if (css) m.highlight = css;\n }\n break;\n }\n case \"w:highlight\": {\n const css = DOCX_HIGHLIGHT_COLORS[(attr(child, \"w:val\") ?? \"\").toLowerCase()];\n if (css) m.highlight = css;\n break;\n }\n // w:rFonts intentionally ignored: school standard is Arial for all\n // exams. Any font declared in the .docx is dropped on import so the\n // imported doc inherits Arial from `.ed-root` / PDF base stylesheet.\n case \"w:sz\": {\n const halfPts = Number.parseFloat(attr(child, \"w:val\") ?? \"\");\n const css = halfPointsToCssPt(halfPts);\n if (css) m.fontSize = css;\n break;\n }\n }\n }\n return m;\n}\n\nfunction mergeMarks(a: MarkAttrs | undefined, b: MarkAttrs): MarkAttrs {\n if (!a) return b;\n return { ...a, ...b };\n}\n\nfunction textContent(node: OoxmlNode): string {\n let out = \"\";\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) {\n const t = child[\"#text\"];\n if (typeof t === \"string\") out += t;\n }\n }\n return out;\n}\n\nfunction isFalse(val: string | undefined): boolean {\n if (val == null) return false;\n const v = val.toLowerCase();\n return v === \"0\" || v === \"false\" || v === \"none\";\n}\n\nfunction push(out: DeltaOp[], text: string, marks: MarkAttrs): void {\n const op: DeltaOp =\n Object.keys(marks).length === 0 ? { insert: text } : { insert: text, attributes: { ...marks } };\n out.push(op);\n}\n\nfunction mergeAdjacent(ops: DeltaOp[]): DeltaOp[] {\n const out: DeltaOp[] = [];\n for (const op of ops) {\n const prev = out[out.length - 1];\n if (\n prev &&\n typeof prev.insert === \"string\" &&\n typeof op.insert === \"string\" &&\n sameMarks(prev.attributes, op.attributes)\n ) {\n prev.insert = prev.insert + op.insert;\n continue;\n }\n out.push(op);\n }\n return out;\n}\n\nfunction sameMarks(a: MarkAttrs | undefined, b: MarkAttrs | undefined): boolean {\n const ak = a ? Object.keys(a).sort() : [];\n const bk = b ? Object.keys(b).sort() : [];\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i++) {\n if (ak[i] !== bk[i]) return false;\n const av = (a as Record<string, unknown>)[ak[i]];\n const bv = (b as Record<string, unknown>)[bk[i]];\n if (typeof av === \"object\" && av !== null && typeof bv === \"object\" && bv !== null) {\n if (JSON.stringify(av) !== JSON.stringify(bv)) return false;\n } else if (av !== bv) {\n return false;\n }\n }\n return true;\n}\n","import type {\n AlignValue,\n AnswerLineSpacing,\n BlockAttrs,\n BlockType,\n DeltaOp,\n SerializedBlock,\n} from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { NumberingResolver } from \"./numbering\";\nimport { parseAlign } from \"./units\";\n\n/**\n * Map a `<w:p>` element to a SerializedBlock. Decides between\n * `paragraph / heading / blockquote / codeBlock / listItem` based on the\n * paragraph style + `w:pPr` properties — mirrors the heuristics encoded in\n * `@sofereditor/export-docx/src/docx.ts`.\n */\nexport function paragraphToBlock(\n p: OoxmlNode,\n ctx: RunContext,\n numbering: NumberingResolver,\n): SerializedBlock {\n const pPr = findChild(p, \"w:pPr\");\n const numPr = pPr ? findChild(pPr, \"w:numPr\") : undefined;\n const styleId = pPr ? attr(findChild(pPr, \"w:pStyle\") ?? {}, \"w:val\") : undefined;\n\n const delta = paragraphChildrenToDelta(childrenOf(p), ctx);\n const text = collectText(delta);\n const align = parseAlign(pPr ? attr(findChild(pPr, \"w:jc\") ?? {}, \"w:val\") : undefined);\n const isRtl = pPr ? !!findChild(pPr, \"w:bidi\") : false;\n\n // List? — numPr is the strongest signal.\n if (numPr) {\n const resolved = numbering.resolve(numPr);\n const attrs: BlockAttrs = {\n listKind: resolved?.listKind ?? \"bullet\",\n listLevel: resolved?.listLevel ?? 0,\n };\n if (resolved?.listKind === \"ordered\") {\n // O ordinal Word vai TEMPORARIAMENTE em listStart de todos os itens; o\n // pós-passe em docx.ts remove dos não-líderes (um listStart em item de\n // continuação quebraria o grupo no renderer). listStyle fica em todos —\n // itens do mesmo estilo agrupam; estilo diferente (ex.: a/b/c entre\n // questões decimais) quebra o grupo exatamente como no Word.\n if (resolved.ordinal != null) attrs.listStart = resolved.ordinal;\n if (resolved.listStyle) attrs.listStyle = resolved.listStyle;\n }\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"listItem\", text, delta, attrs };\n }\n\n // Heading?\n const headingLevel = styleId ? headingFromStyleId(styleId) : null;\n if (headingLevel != null) {\n const attrs: BlockAttrs = { level: headingLevel };\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"heading\", text, delta, attrs };\n }\n\n // Blockquote (export uses left border + 480 twip indent).\n if (pPr && hasLeftBorder(pPr)) {\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"blockquote\", text, delta, attrs };\n }\n\n // Code block (export uses Consolas + light-gray shading).\n if (pPr && hasCodeBlockShading(pPr)) {\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"codeBlock\", text, delta, attrs };\n }\n\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n Object.assign(attrs, readAnswerLine(pPr, text.length > 0));\n return { type: \"paragraph\", text, delta, attrs };\n}\n\n/**\n * Parágrafo SEM texto e com borda INFERIOR = linha de resposta.\n *\n * A exigência de estar vazio é essencial: um parágrafo com texto e régua é um\n * título sublinhado, e convertê-lo em linha de resposta destruiria a semântica\n * do conteúdo. A borda ESQUERDA já é tratada antes como blockquote.\n */\nfunction readAnswerLine(pPr: OoxmlNode | undefined, hasText: boolean): Partial<BlockAttrs> {\n if (!pPr || hasText) return {};\n const pBdr = findChild(pPr, \"w:pBdr\");\n if (!pBdr) return {};\n const bottom = findChild(pBdr, \"w:bottom\");\n if (!bottom) return {};\n const val = (attr(bottom, \"w:val\") ?? \"\").toLowerCase();\n if (val === \"\" || val === \"none\" || val === \"nil\") return {};\n return { answerLine: true, answerLineSpacing: readAnswerLineSpacing(pPr) };\n}\n\n/** 240 twips = 1 linha. Arredonda para o bucket suportado mais próximo. */\nfunction readAnswerLineSpacing(pPr: OoxmlNode): AnswerLineSpacing {\n const sp = findChild(pPr, \"w:spacing\");\n const line = sp ? Number.parseFloat(attr(sp, \"w:line\") ?? \"\") : NaN;\n if (!Number.isFinite(line)) return 1;\n const buckets: AnswerLineSpacing[] = [1, 1.5, 2];\n let best: AnswerLineSpacing = 1;\n let bestDist = Infinity;\n for (const b of buckets) {\n const d = Math.abs(line - 240 * b);\n if (d < bestDist) {\n bestDist = d;\n best = b;\n }\n }\n return best;\n}\n\nfunction collectText(delta: DeltaOp[]): string {\n let out = \"\";\n for (const op of delta) {\n if (typeof op.insert === \"string\") out += op.insert;\n }\n return out;\n}\n\nfunction headingFromStyleId(id: string): 1 | 2 | 3 | 4 | 5 | 6 | null {\n const m = /^Heading\\s*([1-6])$/i.exec(id);\n if (!m) return null;\n const n = Number.parseInt(m[1], 10);\n return (n as 1 | 2 | 3 | 4 | 5 | 6) ?? null;\n}\n\nfunction hasLeftBorder(pPr: OoxmlNode): boolean {\n const pBdr = findChild(pPr, \"w:pBdr\");\n if (!pBdr) return false;\n return !!findChild(pBdr, \"w:left\");\n}\n\nfunction hasCodeBlockShading(pPr: OoxmlNode): boolean {\n const shd = findChild(pPr, \"w:shd\");\n if (!shd) return false;\n const fill = attr(shd, \"w:fill\");\n if (!fill) return false;\n return fill.toLowerCase() === \"f1f5f9\";\n}\n\n/** Whitelist the block types this module produces (used by callers for typing). */\nexport type ParagraphBlockType = Exclude<BlockType, \"table\">;\n/** Whitelist the align values this module produces (used by callers for typing). */\nexport type ParagraphAlign = AlignValue;\n","import type {\n BlockAttrs,\n CellAttrs,\n DeltaOp,\n SerializedBlock,\n SerializedCell,\n TableBorderPreset,\n} from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { docxHexToCssColor, parseIntAttr } from \"./units\";\n\n/**\n * Map a `<w:tbl>` element to a SerializedBlock(table). The output `cells` array\n * is row-major with `rows * cols` entries — covered slots are filled with\n * `{attrs: {covered: true}}` placeholders so it matches the invariant required\n * by `@sofereditor/core`'s `EditorDocument`.\n */\nexport function tableToBlock(tbl: OoxmlNode, ctx: RunContext): SerializedBlock {\n const tblGrid = findChild(tbl, \"w:tblGrid\");\n const gridCols = tblGrid ? findChildren(tblGrid, \"w:gridCol\") : [];\n const trs = findChildren(tbl, \"w:tr\");\n\n // The docx writer (and Word itself) may emit fewer `<w:gridCol>` entries than\n // the table's logical column count when a row has merged cells. Take the max\n // of (declared gridCols, widest row's total gridSpan) to stay robust.\n let cols = gridCols.length;\n for (const tr of trs) {\n const tcs = findChildren(tr, \"w:tc\");\n let count = 0;\n for (const tc of tcs) count += gridSpanOf(tc);\n if (count > cols) cols = count;\n }\n if (cols < 1) cols = 1;\n const rows = trs.length;\n\n // Build a row-major grid of slots. Each slot is either a real cell or marked\n // as covered. We track which (row, col) is the anchor for vMerge groups.\n const slots: Array<SerializedCell | null> = new Array(rows * cols).fill(null);\n // Anchor of the active vMerge per column index; resets when a non-merge cell\n // is placed in the same column.\n const vMergeAnchorByCol = new Map<number, { row: number; col: number }>();\n\n for (let r = 0; r < rows; r++) {\n const tr = trs[r];\n let cursor = 0;\n for (const tc of findChildren(tr, \"w:tc\")) {\n // Advance the cursor past already-occupied (covered-from-above) slots.\n while (cursor < cols && slots[r * cols + cursor] !== null) cursor++;\n if (cursor >= cols) break;\n\n const tcPr = findChild(tc, \"w:tcPr\");\n const gridSpan = gridSpanOf(tc);\n const vMergeNode = tcPr ? findChild(tcPr, \"w:vMerge\") : undefined;\n const vMergeVal = vMergeNode ? (attr(vMergeNode, \"w:val\") ?? \"continue\") : null;\n\n if (vMergeNode && vMergeVal !== \"restart\") {\n // Continuation of a vertical merge. Cover this row, and bump the anchor's\n // rowspan if we can find it.\n const anchor = vMergeAnchorByCol.get(cursor);\n if (anchor) {\n const anchorIdx = anchor.row * cols + anchor.col;\n const a = slots[anchorIdx];\n if (a) {\n a.attrs = { ...(a.attrs ?? {}), rowspan: (a.attrs?.rowspan ?? 1) + 1 };\n }\n }\n for (let c = 0; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n cursor += gridSpan;\n continue;\n }\n\n // Real cell: build its delta + attrs.\n const delta = cellChildrenToDelta(tc, ctx);\n const text = textOfDelta(delta);\n const cellAttrs: CellAttrs = {};\n if (gridSpan > 1) cellAttrs.colspan = gridSpan;\n // rowspan starts at 1 — vMerge continuations bump it.\n\n const realCell: SerializedCell = { text, delta, attrs: cellAttrs };\n slots[r * cols + cursor] = realCell;\n\n // Mark horizontally-covered slots from gridSpan.\n for (let c = 1; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n\n if (vMergeVal === \"restart\") {\n vMergeAnchorByCol.set(cursor, { row: r, col: cursor });\n } else {\n vMergeAnchorByCol.delete(cursor);\n }\n cursor += gridSpan;\n }\n\n // Fill any trailing un-touched slots in this row with empty placeholders so\n // the row-major invariant holds (length = rows * cols).\n for (let c = 0; c < cols; c++) {\n if (slots[r * cols + c] === null) {\n slots[r * cols + c] = emptyCell();\n }\n }\n }\n\n const cells: SerializedCell[] = slots.map((s) => s ?? emptyCell());\n\n const attrs: BlockAttrs = { rows, cols };\n const preset = readBorderPreset(tbl);\n if (preset) attrs.borderPreset = preset;\n const borderColor = readBorderColor(tbl);\n if (borderColor) attrs.borderColor = borderColor;\n return { type: \"table\", text: \"\", delta: [], attrs, cells };\n}\n\n/**\n * Reduz `w:tblBorders` ao preset mais próximo.\n *\n * O modelo não tem borda por célula, então `w:tcBorders` continua ignorado —\n * deliberadamente. Combinação não mapeável cai em `all`, que é o padrão visual\n * e o comportamento menos surpreendente para quem importa um documento externo.\n *\n * Retorna `undefined` quando a tabela não declara `tblBorders`, para não gravar\n * a chave à toa.\n */\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 // OOXML estrito usa start/end no lugar de left/right.\n const ligado = (...nomes: string[]): boolean =>\n nomes.some((nome) => {\n const n = findChild(b, nome);\n if (!n) return false;\n const val = (attr(n, \"w:val\") ?? \"\").toLowerCase();\n return val !== \"\" && val !== \"none\" && val !== \"nil\";\n });\n\n const top = ligado(\"w:top\");\n const bottom = ligado(\"w:bottom\");\n const left = ligado(\"w:left\", \"w:start\");\n const right = ligado(\"w:right\", \"w:end\");\n const iH = ligado(\"w:insideH\");\n const iV = ligado(\"w:insideV\");\n\n if (top && bottom && left && right && iH && iV) return \"all\";\n if (top && bottom && left && right && !iH && !iV) return \"outer\";\n if (top && bottom && !left && !right && iH && !iV) return \"horizontal\";\n if (!top && !bottom && left && right && !iH && iV) return \"vertical\";\n if (!top && !bottom && !left && !right && !iH && !iV) return \"none\";\n return \"all\";\n}\n\nfunction gridSpanOf(tc: OoxmlNode): number {\n const tcPr = findChild(tc, \"w:tcPr\");\n if (!tcPr) return 1;\n const gs = findChild(tcPr, \"w:gridSpan\");\n if (!gs) return 1;\n const n = parseIntAttr(attr(gs, \"w:val\"), 1);\n return Math.max(1, n);\n}\n\nfunction cellChildrenToDelta(tc: OoxmlNode, ctx: RunContext): DeltaOp[] {\n // A cell can contain multiple paragraphs; we concat their deltas with a\n // newline between adjacent ones to preserve visual separation. The cell\n // model in @sofereditor/core is a single Y.Text, so we flatten.\n const out: DeltaOp[] = [];\n let first = true;\n for (const child of childrenOf(tc)) {\n if (tagOf(child) !== \"w:p\") continue;\n if (!first) out.push({ insert: \"\\n\" });\n first = false;\n out.push(...paragraphChildrenToDelta(childrenOf(child), ctx));\n }\n return out;\n}\n\nfunction textOfDelta(delta: DeltaOp[]): string {\n let out = \"\";\n for (const op of delta) {\n if (typeof op.insert === \"string\") out += op.insert;\n }\n return out;\n}\n\nfunction coveredCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: { covered: true } };\n}\n\nfunction emptyCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: {} };\n}\n","import type { ListKind, ListStyleType } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * Resolves per-paragraph `<w:numPr>` into the editor's `{listKind, listLevel}`.\n *\n * docx numbering is two-step:\n * 1. paragraphs reference a `numId` and an `ilvl`\n * 2. each `<w:num numId=\"...\">` points to a `<w:abstractNum>` via `<w:abstractNumId>`\n * 3. each `<w:lvl ilvl=\"...\">` declares a `<w:numFmt val=\"bullet|decimal|...\">`\n *\n * The export side uses two hardcoded reference names (\"ed-bullet\" / \"ed-ordered\"),\n * but Word-authored documents use arbitrary `numId`s — we resolve by reading the\n * `numFmt` of the target level.\n */\n/** `w:numFmt` → CSS `list-style-type` do editor. Não listados caem em \"decimal\". */\nconst FMT_TO_LIST_STYLE: Record<string, ListStyleType> = {\n decimal: \"decimal\",\n lowerLetter: \"lower-alpha\",\n upperLetter: \"upper-alpha\",\n lowerRoman: \"lower-roman\",\n upperRoman: \"upper-roman\",\n};\n\nexport interface ResolvedNumbering {\n listKind: ListKind;\n listLevel: number;\n /**\n * Número Word desta entrada (contador por instância `numId`+nível, honrando\n * `w:start` e resetando níveis mais profundos). Só para `ordered`. O Word\n * numera por INSTÂNCIA — parágrafos comuns entre itens não resetam a\n * contagem — enquanto o editor numera por adjacência; o importador usa este\n * ordinal para semear `listStart` nos líderes de grupo (ver docx.ts).\n */\n ordinal?: number;\n /** Estilo do nível (`w:numFmt` mapeado). Só para `ordered`. */\n listStyle?: ListStyleType;\n}\n\nexport class NumberingResolver {\n private numIdToAbstract = new Map<string, string>();\n private abstractLevelFmt = new Map<string, Map<number, string>>();\n private abstractLevelStart = new Map<string, Map<number, number>>();\n /** Contadores vivos por instância de lista (numId), indexados por nível. */\n private counters = new Map<string, number[]>();\n\n constructor(numberingXml: OoxmlNode[] | undefined) {\n if (!numberingXml) return;\n for (const top of numberingXml) {\n if (tagOf(top) !== \"w:numbering\") continue;\n for (const child of childrenOf(top)) {\n const tag = tagOf(child);\n if (tag === \"w:abstractNum\") {\n const abstractId = attr(child, \"w:abstractNumId\");\n if (!abstractId) continue;\n const levels = new Map<number, string>();\n const starts = new Map<number, number>();\n for (const lvl of findChildren(child, \"w:lvl\")) {\n const ilvl = parseIntAttr(attr(lvl, \"w:ilvl\"), 0);\n const fmt = attr(findChild(lvl, \"w:numFmt\") ?? {}, \"w:val\");\n if (fmt) levels.set(ilvl, fmt);\n const start = findChild(lvl, \"w:start\");\n if (start) starts.set(ilvl, parseIntAttr(attr(start, \"w:val\"), 1));\n }\n this.abstractLevelFmt.set(abstractId, levels);\n this.abstractLevelStart.set(abstractId, starts);\n } else if (tag === \"w:num\") {\n const numId = attr(child, \"w:numId\");\n const abstractRef = findChild(child, \"w:abstractNumId\");\n const abstractId = abstractRef ? attr(abstractRef, \"w:val\") : undefined;\n if (numId && abstractId) this.numIdToAbstract.set(numId, abstractId);\n }\n }\n }\n }\n\n /**\n * Resolves a paragraph's `<w:numPr>` into `{listKind, listLevel, ordinal?,\n * listStyle?}`, or null. STATEFUL for ordered lists: each call advances the\n * per-`numId` counter (Word instance semantics) — call exactly once per\n * numbered paragraph, in document order.\n */\n resolve(numPr: OoxmlNode | undefined): ResolvedNumbering | null {\n if (!numPr) return null;\n const ilvlRef = findChild(numPr, \"w:ilvl\");\n const numIdRef = findChild(numPr, \"w:numId\");\n if (!numIdRef) return null;\n const numId = attr(numIdRef, \"w:val\");\n if (!numId) return null;\n const listLevel = clampLevel(parseIntAttr(ilvlRef ? attr(ilvlRef, \"w:val\") : undefined, 0));\n const abstractId = this.numIdToAbstract.get(numId);\n if (abstractId == null) {\n // Numbering refs the export side defines without a numbering.xml entry visible\n // to us (the export-docx pipeline generates them dynamically via the docx lib).\n // Fall back by inspecting the numId itself when it is a known sentinel.\n return { listKind: \"bullet\", listLevel };\n }\n const levels = this.abstractLevelFmt.get(abstractId);\n const fmt = levels?.get(listLevel) ?? \"bullet\";\n const fmtLower = fmt.toLowerCase();\n const listKind: ListKind = fmtLower === \"bullet\" || fmtLower === \"none\" ? \"bullet\" : \"ordered\";\n if (listKind === \"bullet\") return { listKind, listLevel };\n\n // Ordinal Word: contador por numId; nível avança, níveis mais profundos resetam.\n const start = this.abstractLevelStart.get(abstractId)?.get(listLevel) ?? 1;\n const c = this.counters.get(numId) ?? [];\n c[listLevel] = c[listLevel] == null ? start : c[listLevel] + 1;\n c.length = listLevel + 1;\n this.counters.set(numId, c);\n\n return {\n listKind,\n listLevel,\n ordinal: c[listLevel],\n listStyle: FMT_TO_LIST_STYLE[fmt] ?? \"decimal\",\n };\n }\n}\n\nfunction clampLevel(n: number): number {\n if (!Number.isFinite(n)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(n)));\n}\n","import { detectPreset, mmToPx, type PageSettings } from \"@sofereditor/core\";\nimport { attr, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * 1 inch = 1440 twips = 25.4 mm. So 1 twip = 25.4/1440 mm.\n *\n * Page size lives in `<w:sectPr><w:pgSz w:w=\"...\" w:h=\"...\"/></w:sectPr>` and\n * margins in `<w:pgMar w:top=\"...\" w:bottom=\"...\" w:left=\"...\" w:right=\"...\"/>`,\n * all in twips. This is the inverse of `sectionPropertiesFor` in\n * `@sofereditor/export-docx`.\n */\nconst MM_PER_TWIP = 25.4 / 1440;\n\nfunction twipsToPx(twips: number): number {\n return mmToPx(twips * MM_PER_TWIP);\n}\n\nexport function sectPrToPageSettings(\n sectPr: OoxmlNode | undefined,\n): PageSettings | undefined {\n if (!sectPr) return undefined;\n const pgSz = findChild(sectPr, \"w:pgSz\");\n const pgMar = findChild(sectPr, \"w:pgMar\");\n if (!pgSz && !pgMar) return undefined;\n\n const widthTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:w\"), 0) : 0;\n const heightTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:h\"), 0) : 0;\n const topTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:top\"), 0) : 0;\n const bottomTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:bottom\"), 0) : 0;\n const leftTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:left\"), 0) : 0;\n const rightTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:right\"), 0) : 0;\n\n if (widthTwips <= 0 && heightTwips <= 0 && topTwips <= 0 && leftTwips <= 0) {\n return undefined;\n }\n\n const width = widthTwips > 0 ? twipsToPx(widthTwips) : 794;\n const height = heightTwips > 0 ? twipsToPx(heightTwips) : 1123;\n const settings: PageSettings = {\n width,\n height,\n marginTop: twipsToPx(topTwips || 0),\n marginBottom: twipsToPx(bottomTwips || 0),\n marginLeft: twipsToPx(leftTwips || 0),\n marginRight: twipsToPx(rightTwips || 0),\n };\n settings.preset = detectPreset(settings.width, settings.height);\n return settings;\n}\n"],"mappings":"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","names":["import_core","JSZip","attrs"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/docx.ts","../src/parse-xml.ts","../src/units.ts","../src/images.ts","../src/runs.ts","../src/paragraphs.ts","../src/tables.ts","../src/numbering.ts","../src/page-settings.ts"],"sourcesContent":["export { docxBlobToDocument, docxBlobToEditorDocument } from \"./docx\";\n","import { EditorDocument, type SerializedBlock, type SerializedDocument } from \"@sofereditor/core\";\nimport { readDocx, childrenOf, tagOf, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { paragraphToBlock } from \"./paragraphs\";\nimport { tableToBlock } from \"./tables\";\nimport { NumberingResolver } from \"./numbering\";\nimport { sectPrToPageSettings } from \"./page-settings\";\nimport type { RunContext } from \"./runs\";\n\n/**\n * Read a `.docx` (OOXML) Blob/ArrayBuffer/Uint8Array and produce a\n * `SerializedDocument` ready to feed `EditorDocument.fromJSON` (or\n * `loadFromJSON` on an existing instance).\n *\n * The mapping is the inverse of `@sofereditor/export-docx`. Round-trip is exact for\n * documents the editor produced; documents authored externally (Word, Google\n * Docs export) load with marks/lists/tables/images recognized and unsupported\n * features dropped silently.\n */\nexport async function docxBlobToDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<SerializedDocument> {\n const file = await readDocx(input);\n const numbering = new NumberingResolver(file.numberingXml);\n const ctx: RunContext = {\n relationships: file.relationships,\n mediaBytes: file.mediaBytes,\n };\n\n const body = findBody(file.documentXml);\n if (!body) return { blocks: [{ type: \"paragraph\", text: \"\", delta: [], attrs: {} }] };\n\n const blocks: SerializedBlock[] = [];\n for (const child of childrenOf(body)) {\n const tag = tagOf(child);\n if (tag === \"w:p\") {\n blocks.push(paragraphToBlock(child, ctx, numbering));\n } else if (tag === \"w:tbl\") {\n blocks.push(tableToBlock(child, ctx));\n }\n // w:sectPr is parsed below (page settings); other body-level metadata ignored.\n }\n\n if (blocks.length === 0) {\n blocks.push({ type: \"paragraph\", text: \"\", delta: [], attrs: {} });\n }\n\n pruneNonLeaderListStarts(blocks);\n\n const pageSettings = sectPrToPageSettings(findChild(body, \"w:sectPr\"));\n return pageSettings ? { blocks, pageSettings } : { blocks };\n}\n\n/**\n * O Word numera por INSTÂNCIA (`numId`): parágrafos comuns entre itens não\n * resetam a contagem. O editor numera por ADJACÊNCIA: o renderer agrupa\n * `listItem`s consecutivos do mesmo `listKind`/`listStyle`, e um `listStart`\n * em item de CONTINUAÇÃO quebra o grupo ali. O parse gravou o ordinal Word em\n * `listStart` de TODOS os itens ordenados; este pós-passe o mantém apenas nos\n * LÍDERES de grupo (primeiro item, item após bloco comum, ou após item de\n * kind/estilo diferente) — assim o editor renderiza exatamente os números do\n * Word (\"3.\" depois de uma linha de resposta, \"b.\" em subitens) sem fragmentar\n * os grupos.\n */\nexport function pruneNonLeaderListStarts(blocks: SerializedBlock[]): void {\n // Estado do grupo de adjacência corrente: estilo do líder e último ordinal\n // visto por nível (para exigir sequência contígua — duas listas Word\n // independentes coladas, \"1,2,3\" + \"1,2\", NÃO podem fundir num 1..5).\n let grupoAtivo = false;\n let grupoStyle: unknown;\n let ordinais: number[] = [];\n\n const resetGrupo = () => {\n grupoAtivo = false;\n grupoStyle = undefined;\n ordinais = [];\n };\n\n for (const block of blocks) {\n if (block.type !== \"listItem\") {\n resetGrupo();\n continue;\n }\n if ((block.attrs.listKind ?? \"bullet\") !== \"ordered\") {\n // Bullet entre ordenados: mantém o grupo do renderer? Não — kind\n // diferente quebra o grupo lá também.\n resetGrupo();\n continue;\n }\n const level = block.attrs.listLevel ?? 0;\n const ordinal = typeof block.attrs.listStart === \"number\" ? block.attrs.listStart : 1;\n\n const mesmoNivelSequencial = ordinais[level] != null && ordinal === ordinais[level] + 1;\n const entrandoEmNivelNovo = ordinais[level] == null && level > 0 && ordinal === 1;\n const continuaGrupo =\n grupoAtivo &&\n block.attrs.listStyle === grupoStyle &&\n (mesmoNivelSequencial || entrandoEmNivelNovo);\n\n if (continuaGrupo) {\n delete block.attrs.listStart;\n } else {\n // Este item lidera um grupo novo no renderer (listStart preservado).\n grupoAtivo = true;\n grupoStyle = block.attrs.listStyle;\n ordinais = [];\n }\n ordinais[level] = ordinal;\n ordinais.length = level + 1;\n }\n}\n\n/** Convenience: parse → instantiate a fresh `EditorDocument`. */\nexport async function docxBlobToEditorDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<EditorDocument> {\n const serialized = await docxBlobToDocument(input);\n return EditorDocument.fromJSON(serialized);\n}\n\nfunction findBody(documentXml: OoxmlNode[]): OoxmlNode | undefined {\n for (const top of documentXml) {\n if (tagOf(top) === \"w:document\") {\n const body = findChild(top, \"w:body\");\n if (body) return body;\n }\n }\n return undefined;\n}\n","import JSZip from \"jszip\";\nimport { XMLParser } from \"fast-xml-parser\";\n\n/**\n * fast-xml-parser with `preserveOrder: true` produces a list of single-key\n * objects per element. Each element is either:\n *\n * - A tag node: `{ \"<tagName>\": <children-array>, \":@\"?: <attrs> }`\n * - A text node: `{ \"#text\": <string> }`\n *\n * Attributes are prefixed with `@_`. The `:@` key holds attributes for the\n * surrounding tag. Children preserve original document order, which matters\n * for paragraph runs (bold word followed by italic word).\n */\nexport type OoxmlNode = Record<string, unknown>;\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n preserveOrder: true,\n trimValues: false, // keep whitespace inside <w:t>\n parseAttributeValue: false,\n parseTagValue: false,\n});\n\nexport interface DocxFile {\n documentXml: OoxmlNode[];\n numberingXml?: OoxmlNode[];\n /** rId → target path (inside the zip), e.g. \"media/image1.png\". */\n relationships: Map<string, string>;\n /** zip path (without leading \"word/\") → bytes for embedded media. */\n mediaBytes: Map<string, Uint8Array>;\n}\n\nexport async function readDocx(input: Blob | ArrayBuffer | Uint8Array): Promise<DocxFile> {\n const data =\n input instanceof Uint8Array\n ? input\n : input instanceof ArrayBuffer\n ? new Uint8Array(input)\n : new Uint8Array(await (input as Blob).arrayBuffer());\n\n const zip = await JSZip.loadAsync(data);\n const documentXml = await readXml(zip, \"word/document.xml\");\n if (!documentXml) throw new Error(\"Invalid .docx: missing word/document.xml\");\n\n const numberingXml = await readXml(zip, \"word/numbering.xml\");\n const relsXml = await readXml(zip, \"word/_rels/document.xml.rels\");\n const relationships = parseRelationships(relsXml);\n\n const mediaBytes = new Map<string, Uint8Array>();\n const mediaFolder = zip.folder(\"word/media\");\n if (mediaFolder) {\n const promises: Promise<void>[] = [];\n mediaFolder.forEach((relativePath, file) => {\n if (file.dir) return;\n promises.push(\n file.async(\"uint8array\").then((bytes) => {\n mediaBytes.set(`media/${relativePath}`, bytes);\n }),\n );\n });\n await Promise.all(promises);\n }\n\n return { documentXml, numberingXml, relationships, mediaBytes };\n}\n\nasync function readXml(zip: JSZip, path: string): Promise<OoxmlNode[] | undefined> {\n const entry = zip.file(path);\n if (!entry) return undefined;\n const text = await entry.async(\"string\");\n return parser.parse(text) as OoxmlNode[];\n}\n\nfunction parseRelationships(nodes: OoxmlNode[] | undefined): Map<string, string> {\n const out = new Map<string, string>();\n if (!nodes) return out;\n // <Relationships><Relationship Id=\"rId1\" Type=\"...\" Target=\"media/image1.png\"/>...\n for (const top of nodes) {\n const children = top[\"Relationships\"];\n if (!Array.isArray(children)) continue;\n for (const child of children as OoxmlNode[]) {\n if (!(\"Relationship\" in child)) continue;\n const attrs = (child[\":@\"] ?? {}) as Record<string, unknown>;\n const id = attrs[\"@_Id\"];\n const target = attrs[\"@_Target\"];\n if (typeof id === \"string\" && typeof target === \"string\") {\n out.set(id, target);\n }\n }\n }\n return out;\n}\n\n// ---------- generic walkers over preserveOrder shape ----------\n\n/** Returns the tag name (e.g. \"w:p\") of a preserveOrder element, or null for text. */\nexport function tagOf(node: OoxmlNode): string | null {\n for (const key of Object.keys(node)) {\n if (key === \":@\") continue;\n return key;\n }\n return null;\n}\n\n/**\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/** docx half-points → CSS pt string. 24 (half-pt) → \"12pt\". */\nexport function halfPointsToCssPt(halfPoints: number): string | undefined {\n if (!Number.isFinite(halfPoints) || halfPoints <= 0) return undefined;\n const pt = halfPoints / 2;\n return `${stripTrailingZeros(pt)}pt`;\n}\n\n/** OOXML RRGGBB (no '#') → CSS hex with leading '#'. */\nexport function docxHexToCssColor(hex: string | undefined): string | undefined {\n if (!hex) return undefined;\n const m = /^[0-9a-f]{6}$/i.exec(hex.trim());\n if (!m) return undefined;\n return `#${hex.toLowerCase()}`;\n}\n\n/** OOXML alignment value → editor AlignValue. */\nexport function parseAlign(val: string | undefined): \"left\" | \"center\" | \"right\" | \"justify\" | undefined {\n if (!val) return undefined;\n switch (val.toLowerCase()) {\n case \"left\":\n case \"start\":\n return \"left\";\n case \"center\":\n return \"center\";\n case \"right\":\n case \"end\":\n return \"right\";\n case \"both\":\n case \"distribute\":\n case \"justify\":\n return \"justify\";\n default:\n return undefined;\n }\n}\n\n/** Parse XML attribute that should be a positive integer. */\nexport function parseIntAttr(val: unknown, fallback = 0): number {\n if (typeof val === \"number\" && Number.isFinite(val)) return Math.trunc(val);\n if (typeof val !== \"string\") return fallback;\n const n = Number.parseInt(val, 10);\n return Number.isFinite(n) ? n : fallback;\n}\n\nfunction stripTrailingZeros(n: number): string {\n // 12 → \"12\", 12.5 → \"12.5\"\n return Number.isInteger(n) ? n.toString() : n.toFixed(2).replace(/\\.?0+$/, \"\");\n}\n","import type { ImageEmbed } from \"@sofereditor/core\";\nimport { attr, childrenOf, tagOf, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/** 1 inch = 914400 EMU = 96 px → 1 px = 9525 EMU. */\nconst EMU_PER_PX = 9525;\n\nconst MIME_BY_EXT: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n bmp: \"image/bmp\",\n svg: \"image/svg+xml\",\n};\n\nexport interface ImageContext {\n relationships: Map<string, string>;\n mediaBytes: Map<string, Uint8Array>;\n}\n\n/**\n * Walk a `<w:drawing>` node and return an `ImageEmbed` if it contains a\n * recognized inline picture. Returns null for unsupported drawings (shapes,\n * charts, etc.).\n */\nexport function extractImageFromDrawing(\n drawing: OoxmlNode,\n ctx: ImageContext,\n): ImageEmbed | null {\n // The interesting bits live deep inside: a:blip carries `r:embed`, and\n // wp:extent carries cx/cy. We do a generic walk because the wrapping nodes\n // differ between inline (`wp:inline`) and anchored (`wp:anchor`) drawings.\n let embedId: string | undefined;\n let cx: number | undefined;\n let cy: number | undefined;\n\n walk(drawing, (n) => {\n const tag = tagOf(n);\n if (!tag) return;\n if (tag === \"a:blip\") {\n const id = attr(n, \"r:embed\") ?? attr(n, \"r:link\");\n if (id) embedId = id;\n } else if (tag === \"wp:extent\") {\n cx = parseIntAttr(attr(n, \"cx\"));\n cy = parseIntAttr(attr(n, \"cy\"));\n }\n });\n\n if (!embedId) return null;\n const target = ctx.relationships.get(embedId);\n if (!target) return null;\n // Relationship targets are typically \"media/image1.png\"; the zip lookup uses\n // the same path (we stored bytes keyed under \"media/<file>\").\n const bytes = ctx.mediaBytes.get(target);\n if (!bytes) return null;\n\n const ext = extensionOf(target);\n const mime = MIME_BY_EXT[ext] ?? \"image/png\";\n const src = `data:${mime};base64,${bytesToBase64(bytes)}`;\n const widthPx = cx != null ? Math.max(1, Math.round(cx / EMU_PER_PX)) : 100;\n const heightPx = cy != null ? Math.max(1, Math.round(cy / EMU_PER_PX)) : 100;\n\n return { type: \"image\", src, width: widthPx, height: heightPx };\n}\n\nfunction walk(node: OoxmlNode, visit: (n: OoxmlNode) => void): void {\n visit(node);\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) continue;\n walk(child, visit);\n }\n}\n\nfunction extensionOf(path: string): string {\n const m = /\\.([a-z0-9]+)$/i.exec(path);\n return m ? m[1].toLowerCase() : \"\";\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n // Browser path.\n if (typeof btoa === \"function\") {\n let bin = \"\";\n const CHUNK = 0x8000;\n for (let i = 0; i < bytes.length; i += CHUNK) {\n bin += String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + CHUNK)));\n }\n return btoa(bin);\n }\n // Node fallback.\n return Buffer.from(bytes).toString(\"base64\");\n}\n","import type { DeltaOp, MarkAttrs } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { docxHexToCssColor, halfPointsToCssPt } from \"./units\";\nimport { extractImageFromDrawing, type ImageContext } from \"./images\";\n\nexport interface RunContext extends ImageContext {}\n\n/**\n * Walk a paragraph's children (post `<w:pPr>`) and emit a `DeltaOp[]`. Combines\n * adjacent runs that share the same mark set into a single op for parity with\n * the export — Y.Text.applyDelta would merge them anyway, but pre-merging keeps\n * round-trip output stable.\n */\nexport function paragraphChildrenToDelta(children: OoxmlNode[], ctx: RunContext): DeltaOp[] {\n const ops: DeltaOp[] = [];\n for (const child of children) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:pPr\") continue;\n if (tag === \"w:r\") {\n collectRun(child, ctx, undefined, ops);\n } else if (tag === \"w:hyperlink\") {\n // hyperlinks carry an r:id that maps via relationships to the href.\n const rId = attr(child, \"r:id\");\n const href = rId ? ctx.relationships.get(rId) : undefined;\n const linkMark: MarkAttrs | undefined = href ? { link: { href } } : undefined;\n for (const sub of childrenOf(child)) {\n if (tagOf(sub) === \"w:r\") collectRun(sub, ctx, linkMark, ops);\n }\n }\n }\n return mergeAdjacent(ops);\n}\n\nfunction collectRun(\n run: OoxmlNode,\n ctx: RunContext,\n wrappingMarks: MarkAttrs | undefined,\n out: DeltaOp[],\n): void {\n const marks = mergeMarks(wrappingMarks, runMarks(findChild(run, \"w:rPr\")));\n for (const child of childrenOf(run)) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:t\") {\n const text = textContent(child);\n if (text.length > 0) push(out, text, marks);\n } else if (tag === \"w:tab\") {\n push(out, \"\\t\", marks);\n } else if (tag === \"w:br\") {\n // Treat w:br type=\"page\" as also a soft break for the model — we have no\n // explicit page-break node in the schema; pagination is computed live.\n push(out, \"\\n\", marks);\n } else if (tag === \"w:drawing\") {\n const embed = extractImageFromDrawing(child, ctx);\n if (embed) out.push({ insert: embed });\n }\n // Other run children (w:sym, w:fldChar, etc.) are silently dropped.\n }\n}\n\n/**\n * Os valores nomeados de `w:highlight` do OOXML. O Word só aceita esta lista\n * fechada — documentos com cor arbitrária usam `w:shd`, que é o que o nosso\n * export emite. `none` fica de fora de propósito: ausência = sem marca.\n */\nconst DOCX_HIGHLIGHT_COLORS: Record<string, string> = {\n yellow: \"#ffff00\",\n green: \"#00ff00\",\n cyan: \"#00ffff\",\n magenta: \"#ff00ff\",\n blue: \"#0000ff\",\n red: \"#ff0000\",\n darkblue: \"#000080\",\n darkcyan: \"#008080\",\n darkgreen: \"#008000\",\n darkmagenta: \"#800080\",\n darkred: \"#800000\",\n darkyellow: \"#808000\",\n darkgray: \"#808080\",\n lightgray: \"#c0c0c0\",\n black: \"#000000\",\n white: \"#ffffff\",\n};\n\nfunction runMarks(rPr: OoxmlNode | undefined): MarkAttrs {\n const m: MarkAttrs = {};\n if (!rPr) return m;\n for (const child of childrenOf(rPr)) {\n const tag = tagOf(child);\n if (!tag) continue;\n switch (tag) {\n case \"w:b\":\n if (!isFalse(attr(child, \"w:val\"))) m.bold = true;\n break;\n case \"w:i\":\n if (!isFalse(attr(child, \"w:val\"))) m.italic = true;\n break;\n case \"w:u\": {\n const val = attr(child, \"w:val\");\n if (val && val.toLowerCase() !== \"none\") m.underline = true;\n break;\n }\n case \"w:strike\":\n if (!isFalse(attr(child, \"w:val\"))) m.strike = true;\n break;\n case \"w:color\": {\n const hex = attr(child, \"w:val\");\n const css = docxHexToCssColor(hex);\n if (css) m.color = css;\n break;\n }\n case \"w:shd\": {\n // Sombreamento de RUN = marca-texto. `auto` significa \"sem cor\".\n const fill = attr(child, \"w:fill\");\n if (fill && fill.toLowerCase() !== \"auto\") {\n const css = docxHexToCssColor(fill);\n if (css) m.highlight = css;\n }\n break;\n }\n case \"w:highlight\": {\n const css = DOCX_HIGHLIGHT_COLORS[(attr(child, \"w:val\") ?? \"\").toLowerCase()];\n if (css) m.highlight = css;\n break;\n }\n // w:rFonts intentionally ignored: school standard is Arial for all\n // exams. Any font declared in the .docx is dropped on import so the\n // imported doc inherits Arial from `.ed-root` / PDF base stylesheet.\n case \"w:sz\": {\n const halfPts = Number.parseFloat(attr(child, \"w:val\") ?? \"\");\n const css = halfPointsToCssPt(halfPts);\n if (css) m.fontSize = css;\n break;\n }\n }\n }\n return m;\n}\n\nfunction mergeMarks(a: MarkAttrs | undefined, b: MarkAttrs): MarkAttrs {\n if (!a) return b;\n return { ...a, ...b };\n}\n\nfunction textContent(node: OoxmlNode): string {\n let out = \"\";\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) {\n const t = child[\"#text\"];\n if (typeof t === \"string\") out += t;\n }\n }\n return out;\n}\n\nfunction isFalse(val: string | undefined): boolean {\n if (val == null) return false;\n const v = val.toLowerCase();\n return v === \"0\" || v === \"false\" || v === \"none\";\n}\n\nfunction push(out: DeltaOp[], text: string, marks: MarkAttrs): void {\n const op: DeltaOp =\n Object.keys(marks).length === 0 ? { insert: text } : { insert: text, attributes: { ...marks } };\n out.push(op);\n}\n\nfunction mergeAdjacent(ops: DeltaOp[]): DeltaOp[] {\n const out: DeltaOp[] = [];\n for (const op of ops) {\n const prev = out[out.length - 1];\n if (\n prev &&\n typeof prev.insert === \"string\" &&\n typeof op.insert === \"string\" &&\n sameMarks(prev.attributes, op.attributes)\n ) {\n prev.insert = prev.insert + op.insert;\n continue;\n }\n out.push(op);\n }\n return out;\n}\n\nfunction sameMarks(a: MarkAttrs | undefined, b: MarkAttrs | undefined): boolean {\n const ak = a ? Object.keys(a).sort() : [];\n const bk = b ? Object.keys(b).sort() : [];\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i++) {\n if (ak[i] !== bk[i]) return false;\n const av = (a as Record<string, unknown>)[ak[i]];\n const bv = (b as Record<string, unknown>)[bk[i]];\n if (typeof av === \"object\" && av !== null && typeof bv === \"object\" && bv !== null) {\n if (JSON.stringify(av) !== JSON.stringify(bv)) return false;\n } else if (av !== bv) {\n return false;\n }\n }\n return true;\n}\n","import type {\n AlignValue,\n AnswerLineSpacing,\n BlockAttrs,\n BlockType,\n DeltaOp,\n SerializedBlock,\n} from \"@sofereditor/core\";\nimport {\n attr,\n 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 SerializedBlock,\n SerializedCell,\n TableBorderPreset,\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 { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { docxHexToCssColor, parseIntAttr } from \"./units\";\n\n/**\n * Map a `<w:tbl>` element to a SerializedBlock(table). The output `cells` array\n * is row-major with `rows * cols` entries — covered slots are filled with\n * `{attrs: {covered: true}}` placeholders so it matches the invariant required\n * by `@sofereditor/core`'s `EditorDocument`.\n */\nexport function tableToBlock(tbl: OoxmlNode, ctx: RunContext): SerializedBlock {\n const tblGrid = findChild(tbl, \"w:tblGrid\");\n const gridCols = tblGrid ? findChildren(tblGrid, \"w:gridCol\") : [];\n const trs = findChildren(tbl, \"w:tr\");\n\n // The docx writer (and Word itself) may emit fewer `<w:gridCol>` entries than\n // the table's logical column count when a row has merged cells. Take the max\n // of (declared gridCols, widest row's total gridSpan) to stay robust.\n let cols = gridCols.length;\n for (const tr of trs) {\n const tcs = findChildren(tr, \"w:tc\");\n let count = 0;\n for (const tc of tcs) count += gridSpanOf(tc);\n if (count > cols) cols = count;\n }\n if (cols < 1) cols = 1;\n const rows = trs.length;\n\n // Build a row-major grid of slots. Each slot is either a real cell or marked\n // as covered. We track which (row, col) is the anchor for vMerge groups.\n const slots: Array<SerializedCell | null> = new Array(rows * cols).fill(null);\n // Anchor of the active vMerge per column index; resets when a non-merge cell\n // is placed in the same column.\n const vMergeAnchorByCol = new Map<number, { row: number; col: number }>();\n\n for (let r = 0; r < rows; r++) {\n const tr = trs[r];\n let cursor = 0;\n for (const tc of findChildren(tr, \"w:tc\")) {\n // Advance the cursor past already-occupied (covered-from-above) slots.\n while (cursor < cols && slots[r * cols + cursor] !== null) cursor++;\n if (cursor >= cols) break;\n\n const tcPr = findChild(tc, \"w:tcPr\");\n const gridSpan = gridSpanOf(tc);\n const vMergeNode = tcPr ? findChild(tcPr, \"w:vMerge\") : undefined;\n const vMergeVal = vMergeNode ? (attr(vMergeNode, \"w:val\") ?? \"continue\") : null;\n\n if (vMergeNode && vMergeVal !== \"restart\") {\n // Continuation of a vertical merge. Cover this row, and bump the anchor's\n // rowspan if we can find it.\n const anchor = vMergeAnchorByCol.get(cursor);\n if (anchor) {\n const anchorIdx = anchor.row * cols + anchor.col;\n const a = slots[anchorIdx];\n if (a) {\n a.attrs = { ...(a.attrs ?? {}), rowspan: (a.attrs?.rowspan ?? 1) + 1 };\n }\n }\n for (let c = 0; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n cursor += gridSpan;\n continue;\n }\n\n // Real cell: build its delta + attrs.\n const delta = cellChildrenToDelta(tc, ctx);\n const text = textOfDelta(delta);\n const cellAttrs: CellAttrs = {};\n if (gridSpan > 1) cellAttrs.colspan = gridSpan;\n // rowspan starts at 1 — vMerge continuations bump it.\n\n const realCell: SerializedCell = { text, delta, attrs: cellAttrs };\n slots[r * cols + cursor] = realCell;\n\n // Mark horizontally-covered slots from gridSpan.\n for (let c = 1; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n\n if (vMergeVal === \"restart\") {\n vMergeAnchorByCol.set(cursor, { row: r, col: cursor });\n } else {\n vMergeAnchorByCol.delete(cursor);\n }\n cursor += gridSpan;\n }\n\n // Fill any trailing un-touched slots in this row with empty placeholders so\n // the row-major invariant holds (length = rows * cols).\n for (let c = 0; c < cols; c++) {\n if (slots[r * cols + c] === null) {\n slots[r * cols + c] = emptyCell();\n }\n }\n }\n\n const cells: SerializedCell[] = slots.map((s) => s ?? emptyCell());\n\n const attrs: BlockAttrs = { rows, cols };\n const preset = readBorderPreset(tbl);\n if (preset) attrs.borderPreset = preset;\n const borderColor = readBorderColor(tbl);\n if (borderColor) attrs.borderColor = borderColor;\n return { type: \"table\", text: \"\", delta: [], attrs, cells };\n}\n\n/**\n * Reduz `w:tblBorders` ao preset mais próximo.\n *\n * O modelo não tem borda por célula, então `w:tcBorders` continua ignorado —\n * deliberadamente. Combinação não mapeável cai em `all`, que é o padrão visual\n * e o comportamento menos surpreendente para quem importa um documento externo.\n *\n * Retorna `undefined` quando a tabela não declara `tblBorders`, para não gravar\n * a chave à toa.\n */\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\nfunction cellChildrenToDelta(tc: OoxmlNode, ctx: RunContext): DeltaOp[] {\n // A cell can contain multiple paragraphs; we concat their deltas with a\n // newline between adjacent ones to preserve visual separation. The cell\n // model in @sofereditor/core is a single Y.Text, so we flatten.\n const out: DeltaOp[] = [];\n let first = true;\n for (const child of childrenOf(tc)) {\n if (tagOf(child) !== \"w:p\") continue;\n if (!first) out.push({ insert: \"\\n\" });\n first = false;\n out.push(...paragraphChildrenToDelta(childrenOf(child), ctx));\n }\n return out;\n}\n\nfunction textOfDelta(delta: DeltaOp[]): string {\n let out = \"\";\n for (const op of delta) {\n if (typeof op.insert === \"string\") out += op.insert;\n }\n return out;\n}\n\nfunction coveredCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: { covered: true } };\n}\n\nfunction emptyCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: {} };\n}\n","import type { ListKind, ListStyleType } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * Resolves per-paragraph `<w:numPr>` into the editor's `{listKind, listLevel}`.\n *\n * docx numbering is two-step:\n * 1. paragraphs reference a `numId` and an `ilvl`\n * 2. each `<w:num numId=\"...\">` points to a `<w:abstractNum>` via `<w:abstractNumId>`\n * 3. each `<w:lvl ilvl=\"...\">` declares a `<w:numFmt val=\"bullet|decimal|...\">`\n *\n * The export side uses two hardcoded reference names (\"ed-bullet\" / \"ed-ordered\"),\n * but Word-authored documents use arbitrary `numId`s — we resolve by reading the\n * `numFmt` of the target level.\n */\n/** `w:numFmt` → CSS `list-style-type` do editor. Não listados caem em \"decimal\". */\nconst FMT_TO_LIST_STYLE: Record<string, ListStyleType> = {\n decimal: \"decimal\",\n lowerLetter: \"lower-alpha\",\n upperLetter: \"upper-alpha\",\n lowerRoman: \"lower-roman\",\n upperRoman: \"upper-roman\",\n};\n\nexport interface ResolvedNumbering {\n listKind: ListKind;\n listLevel: number;\n /**\n * Número Word desta entrada (contador por instância `numId`+nível, honrando\n * `w:start` e resetando níveis mais profundos). Só para `ordered`. O Word\n * numera por INSTÂNCIA — parágrafos comuns entre itens não resetam a\n * contagem — enquanto o editor numera por adjacência; o importador usa este\n * ordinal para semear `listStart` nos líderes de grupo (ver docx.ts).\n */\n ordinal?: number;\n /** Estilo do nível (`w:numFmt` mapeado). Só para `ordered`. */\n listStyle?: ListStyleType;\n}\n\nexport class NumberingResolver {\n private numIdToAbstract = new Map<string, string>();\n private abstractLevelFmt = new Map<string, Map<number, string>>();\n private abstractLevelStart = new Map<string, Map<number, number>>();\n /** Contadores vivos por instância de lista (numId), indexados por nível. */\n private counters = new Map<string, number[]>();\n\n constructor(numberingXml: OoxmlNode[] | undefined) {\n if (!numberingXml) return;\n for (const top of numberingXml) {\n if (tagOf(top) !== \"w:numbering\") continue;\n for (const child of childrenOf(top)) {\n const tag = tagOf(child);\n if (tag === \"w:abstractNum\") {\n const abstractId = attr(child, \"w:abstractNumId\");\n if (!abstractId) continue;\n const levels = new Map<number, string>();\n const starts = new Map<number, number>();\n for (const lvl of findChildren(child, \"w:lvl\")) {\n const ilvl = parseIntAttr(attr(lvl, \"w:ilvl\"), 0);\n const fmt = attr(findChild(lvl, \"w:numFmt\") ?? {}, \"w:val\");\n if (fmt) levels.set(ilvl, fmt);\n const start = findChild(lvl, \"w:start\");\n if (start) starts.set(ilvl, parseIntAttr(attr(start, \"w:val\"), 1));\n }\n this.abstractLevelFmt.set(abstractId, levels);\n this.abstractLevelStart.set(abstractId, starts);\n } else if (tag === \"w:num\") {\n const numId = attr(child, \"w:numId\");\n const abstractRef = findChild(child, \"w:abstractNumId\");\n const abstractId = abstractRef ? attr(abstractRef, \"w:val\") : undefined;\n if (numId && abstractId) this.numIdToAbstract.set(numId, abstractId);\n }\n }\n }\n }\n\n /**\n * Resolves a paragraph's `<w:numPr>` into `{listKind, listLevel, ordinal?,\n * listStyle?}`, or null. STATEFUL for ordered lists: each call advances the\n * per-`numId` counter (Word instance semantics) — call exactly once per\n * numbered paragraph, in document order.\n */\n resolve(numPr: OoxmlNode | undefined): ResolvedNumbering | null {\n if (!numPr) return null;\n const ilvlRef = findChild(numPr, \"w:ilvl\");\n const numIdRef = findChild(numPr, \"w:numId\");\n if (!numIdRef) return null;\n const numId = attr(numIdRef, \"w:val\");\n if (!numId) return null;\n const listLevel = clampLevel(parseIntAttr(ilvlRef ? attr(ilvlRef, \"w:val\") : undefined, 0));\n const abstractId = this.numIdToAbstract.get(numId);\n if (abstractId == null) {\n // Numbering refs the export side defines without a numbering.xml entry visible\n // to us (the export-docx pipeline generates them dynamically via the docx lib).\n // Fall back by inspecting the numId itself when it is a known sentinel.\n return { listKind: \"bullet\", listLevel };\n }\n const levels = this.abstractLevelFmt.get(abstractId);\n const fmt = levels?.get(listLevel) ?? \"bullet\";\n const fmtLower = fmt.toLowerCase();\n const listKind: ListKind = fmtLower === \"bullet\" || fmtLower === \"none\" ? \"bullet\" : \"ordered\";\n if (listKind === \"bullet\") return { listKind, listLevel };\n\n // Ordinal Word: contador por numId; nível avança, níveis mais profundos resetam.\n const start = this.abstractLevelStart.get(abstractId)?.get(listLevel) ?? 1;\n const c = this.counters.get(numId) ?? [];\n c[listLevel] = c[listLevel] == null ? start : c[listLevel] + 1;\n c.length = listLevel + 1;\n this.counters.set(numId, c);\n\n return {\n listKind,\n listLevel,\n ordinal: c[listLevel],\n listStyle: FMT_TO_LIST_STYLE[fmt] ?? \"decimal\",\n };\n }\n}\n\nfunction clampLevel(n: number): number {\n if (!Number.isFinite(n)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(n)));\n}\n","import { detectPreset, mmToPx, type PageSettings } from \"@sofereditor/core\";\nimport { attr, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * 1 inch = 1440 twips = 25.4 mm. So 1 twip = 25.4/1440 mm.\n *\n * Page size lives in `<w:sectPr><w:pgSz w:w=\"...\" w:h=\"...\"/></w:sectPr>` and\n * margins in `<w:pgMar w:top=\"...\" w:bottom=\"...\" w:left=\"...\" w:right=\"...\"/>`,\n * all in twips. This is the inverse of `sectionPropertiesFor` in\n * `@sofereditor/export-docx`.\n */\nconst MM_PER_TWIP = 25.4 / 1440;\n\nfunction twipsToPx(twips: number): number {\n return mmToPx(twips * MM_PER_TWIP);\n}\n\nexport function sectPrToPageSettings(\n sectPr: OoxmlNode | undefined,\n): PageSettings | undefined {\n if (!sectPr) return undefined;\n const pgSz = findChild(sectPr, \"w:pgSz\");\n const pgMar = findChild(sectPr, \"w:pgMar\");\n if (!pgSz && !pgMar) return undefined;\n\n const widthTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:w\"), 0) : 0;\n const heightTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:h\"), 0) : 0;\n const topTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:top\"), 0) : 0;\n const bottomTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:bottom\"), 0) : 0;\n const leftTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:left\"), 0) : 0;\n const rightTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:right\"), 0) : 0;\n\n if (widthTwips <= 0 && heightTwips <= 0 && topTwips <= 0 && leftTwips <= 0) {\n return undefined;\n }\n\n const width = widthTwips > 0 ? twipsToPx(widthTwips) : 794;\n const height = heightTwips > 0 ? twipsToPx(heightTwips) : 1123;\n const settings: PageSettings = {\n width,\n height,\n marginTop: twipsToPx(topTwips || 0),\n marginBottom: twipsToPx(bottomTwips || 0),\n marginLeft: twipsToPx(leftTwips || 0),\n marginRight: twipsToPx(rightTwips || 0),\n };\n settings.preset = detectPreset(settings.width, settings.height);\n return settings;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,eAA8E;;;ACA9E,mBAAkB;AAClB,6BAA0B;AAe1B,IAAM,SAAS,IAAI,iCAAU;AAAA,EAC3B,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,YAAY;AAAA;AAAA,EACZ,qBAAqB;AAAA,EACrB,eAAe;AACjB,CAAC;AAWD,eAAsB,SAAS,OAA2D;AACxF,QAAM,OACJ,iBAAiB,aACb,QACA,iBAAiB,cACf,IAAI,WAAW,KAAK,IACpB,IAAI,WAAW,MAAO,MAAe,YAAY,CAAC;AAE1D,QAAM,MAAM,MAAM,aAAAC,QAAM,UAAU,IAAI;AACtC,QAAM,cAAc,MAAM,QAAQ,KAAK,mBAAmB;AAC1D,MAAI,CAAC,YAAa,OAAM,IAAI,MAAM,0CAA0C;AAE5E,QAAM,eAAe,MAAM,QAAQ,KAAK,oBAAoB;AAC5D,QAAM,UAAU,MAAM,QAAQ,KAAK,8BAA8B;AACjE,QAAM,gBAAgB,mBAAmB,OAAO;AAEhD,QAAM,aAAa,oBAAI,IAAwB;AAC/C,QAAM,cAAc,IAAI,OAAO,YAAY;AAC3C,MAAI,aAAa;AACf,UAAM,WAA4B,CAAC;AACnC,gBAAY,QAAQ,CAAC,cAAc,SAAS;AAC1C,UAAI,KAAK,IAAK;AACd,eAAS;AAAA,QACP,KAAK,MAAM,YAAY,EAAE,KAAK,CAAC,UAAU;AACvC,qBAAW,IAAI,SAAS,YAAY,IAAI,KAAK;AAAA,QAC/C,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AACD,UAAM,QAAQ,IAAI,QAAQ;AAAA,EAC5B;AAEA,SAAO,EAAE,aAAa,cAAc,eAAe,WAAW;AAChE;AAEA,eAAe,QAAQ,KAAY,MAAgD;AACjF,QAAM,QAAQ,IAAI,KAAK,IAAI;AAC3B,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,MAAM,MAAM,MAAM,QAAQ;AACvC,SAAO,OAAO,MAAM,IAAI;AAC1B;AAEA,SAAS,mBAAmB,OAAqD;AAC/E,QAAM,MAAM,oBAAI,IAAoB;AACpC,MAAI,CAAC,MAAO,QAAO;AAEnB,aAAW,OAAO,OAAO;AACvB,UAAM,WAAW,IAAI,eAAe;AACpC,QAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG;AAC9B,eAAW,SAAS,UAAyB;AAC3C,UAAI,EAAE,kBAAkB,OAAQ;AAChC,YAAM,QAAS,MAAM,IAAI,KAAK,CAAC;AAC/B,YAAM,KAAK,MAAM,MAAM;AACvB,YAAM,SAAS,MAAM,UAAU;AAC/B,UAAI,OAAO,OAAO,YAAY,OAAO,WAAW,UAAU;AACxD,YAAI,IAAI,IAAI,MAAM;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAKO,SAAS,MAAM,MAAgC;AACpD,aAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AACnC,QAAI,QAAQ,KAAM;AAClB,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAiBO,SAAS,WAAW,MAA8B;AACvD,QAAM,MAAM,MAAM,IAAI;AACtB,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,QAAM,IAAI,KAAK,GAAG;AAClB,MAAI,CAAC,MAAM,QAAQ,CAAC,EAAG,QAAO,CAAC;AAC/B,QAAM,OAAO;AAGb,MAAI,SAAS;AACb,aAAW,KAAK,MAAM;AACpB,QAAI,MAAM,CAAC,MAAM,SAAS;AACxB,eAAS;AACT;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,MAAmB,CAAC;AAC1B,aAAW,KAAK,MAAM;AACpB,QAAI,MAAM,CAAC,MAAM,SAAS;AACxB,UAAI,KAAK,CAAC;AACV;AAAA,IACF;AAGA,UAAM,UAAU,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,MAAM,CAAC,MAAM,cAAc;AACrE,QAAI,QAAS,KAAI,KAAK,GAAG,WAAW,OAAO,CAAC;AAAA,EAC9C;AACA,SAAO;AACT;AAGO,SAAS,QAAQ,MAA0C;AAChE,SAAS,KAAK,IAAI,KAAK,CAAC,KAAkC,CAAC;AAC7D;AAGO,SAAS,KAAK,MAAiB,KAAiC;AACrE,QAAM,IAAI,QAAQ,IAAI,EAAE,KAAK,GAAG,EAAE;AAClC,SAAO,OAAO,MAAM,WAAW,IAAI;AACrC;AAGO,SAAS,UAAU,MAAiB,KAAoC;AAC7E,aAAW,SAAS,WAAW,IAAI,GAAG;AACpC,QAAI,MAAM,KAAK,MAAM,IAAK,QAAO;AAAA,EACnC;AACA,SAAO;AACT;AAGO,SAAS,aAAa,MAAiB,KAA0B;AACtE,QAAM,MAAmB,CAAC;AAC1B,aAAW,SAAS,WAAW,IAAI,GAAG;AACpC,QAAI,MAAM,KAAK,MAAM,IAAK,KAAI,KAAK,KAAK;AAAA,EAC1C;AACA,SAAO;AACT;AA2BO,SAAS,aAAa,WAAkC,OAA0B;AACvF,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,MAAM,KAAK,CAAC,SAAS;AAC1B,UAAM,IAAI,UAAU,QAAQ,IAAI;AAChC,QAAI,CAAC,EAAG,QAAO;AACf,UAAM,OAAO,KAAK,GAAG,OAAO,KAAK,IAAI,YAAY;AACjD,WAAO,QAAQ,MAAM,QAAQ,UAAU,QAAQ;AAAA,EACjD,CAAC;AACH;;;AC/MO,SAAS,kBAAkB,YAAwC;AACxE,MAAI,CAAC,OAAO,SAAS,UAAU,KAAK,cAAc,EAAG,QAAO;AAC5D,QAAM,KAAK,aAAa;AACxB,SAAO,GAAG,mBAAmB,EAAE,CAAC;AAClC;AAGO,SAAS,kBAAkB,KAA6C;AAC7E,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,IAAI,iBAAiB,KAAK,IAAI,KAAK,CAAC;AAC1C,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,IAAI,IAAI,YAAY,CAAC;AAC9B;AAGO,SAAS,WAAW,KAA8E;AACvG,MAAI,CAAC,IAAK,QAAO;AACjB,UAAQ,IAAI,YAAY,GAAG;AAAA,IACzB,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAGO,SAAS,aAAa,KAAc,WAAW,GAAW;AAC/D,MAAI,OAAO,QAAQ,YAAY,OAAO,SAAS,GAAG,EAAG,QAAO,KAAK,MAAM,GAAG;AAC1E,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,IAAI,OAAO,SAAS,KAAK,EAAE;AACjC,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAEA,SAAS,mBAAmB,GAAmB;AAE7C,SAAO,OAAO,UAAU,CAAC,IAAI,EAAE,SAAS,IAAI,EAAE,QAAQ,CAAC,EAAE,QAAQ,UAAU,EAAE;AAC/E;;;AC/CA,IAAM,aAAa;AAEnB,IAAM,cAAsC;AAAA,EAC1C,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAYO,SAAS,wBACd,SACA,KACmB;AAInB,MAAI;AACJ,MAAI;AACJ,MAAI;AAEJ,OAAK,SAAS,CAAC,MAAM;AACnB,UAAM,MAAM,MAAM,CAAC;AACnB,QAAI,CAAC,IAAK;AACV,QAAI,QAAQ,UAAU;AACpB,YAAM,KAAK,KAAK,GAAG,SAAS,KAAK,KAAK,GAAG,QAAQ;AACjD,UAAI,GAAI,WAAU;AAAA,IACpB,WAAW,QAAQ,aAAa;AAC9B,WAAK,aAAa,KAAK,GAAG,IAAI,CAAC;AAC/B,WAAK,aAAa,KAAK,GAAG,IAAI,CAAC;AAAA,IACjC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,SAAS,IAAI,cAAc,IAAI,OAAO;AAC5C,MAAI,CAAC,OAAQ,QAAO;AAGpB,QAAM,QAAQ,IAAI,WAAW,IAAI,MAAM;AACvC,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,MAAM,YAAY,MAAM;AAC9B,QAAM,OAAO,YAAY,GAAG,KAAK;AACjC,QAAM,MAAM,QAAQ,IAAI,WAAW,cAAc,KAAK,CAAC;AACvD,QAAM,UAAU,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,UAAU,CAAC,IAAI;AACxE,QAAM,WAAW,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,UAAU,CAAC,IAAI;AAEzE,SAAO,EAAE,MAAM,SAAS,KAAK,OAAO,SAAS,QAAQ,SAAS;AAChE;AAEA,SAAS,KAAK,MAAiB,OAAqC;AAClE,QAAM,IAAI;AACV,aAAW,SAAS,WAAW,IAAI,GAAG;AACpC,QAAI,WAAW,MAAO;AACtB,SAAK,OAAO,KAAK;AAAA,EACnB;AACF;AAEA,SAAS,YAAY,MAAsB;AACzC,QAAM,IAAI,kBAAkB,KAAK,IAAI;AACrC,SAAO,IAAI,EAAE,CAAC,EAAE,YAAY,IAAI;AAClC;AAEA,SAAS,cAAc,OAA2B;AAEhD,MAAI,OAAO,SAAS,YAAY;AAC9B,QAAI,MAAM;AACV,UAAM,QAAQ;AACd,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,OAAO;AAC5C,aAAO,OAAO,aAAa,MAAM,MAAM,MAAM,KAAK,MAAM,SAAS,GAAG,IAAI,KAAK,CAAC,CAAC;AAAA,IACjF;AACA,WAAO,KAAK,GAAG;AAAA,EACjB;AAEA,SAAO,OAAO,KAAK,KAAK,EAAE,SAAS,QAAQ;AAC7C;;;ACxEO,SAAS,yBAAyB,UAAuB,KAA4B;AAC1F,QAAM,MAAiB,CAAC;AACxB,aAAW,SAAS,UAAU;AAC5B,UAAM,MAAM,MAAM,KAAK;AACvB,QAAI,CAAC,IAAK;AACV,QAAI,QAAQ,QAAS;AACrB,QAAI,QAAQ,OAAO;AACjB,iBAAW,OAAO,KAAK,QAAW,GAAG;AAAA,IACvC,WAAW,QAAQ,eAAe;AAEhC,YAAM,MAAM,KAAK,OAAO,MAAM;AAC9B,YAAM,OAAO,MAAM,IAAI,cAAc,IAAI,GAAG,IAAI;AAChD,YAAM,WAAkC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI;AACpE,iBAAW,OAAO,WAAW,KAAK,GAAG;AACnC,YAAI,MAAM,GAAG,MAAM,MAAO,YAAW,KAAK,KAAK,UAAU,GAAG;AAAA,MAC9D;AAAA,IACF;AAAA,EACF;AACA,SAAO,cAAc,GAAG;AAC1B;AAEA,SAAS,WACP,KACA,KACA,eACA,KACM;AACN,QAAM,QAAQ,WAAW,eAAe,SAAS,UAAU,KAAK,OAAO,CAAC,CAAC;AACzE,aAAW,SAAS,WAAW,GAAG,GAAG;AACnC,UAAM,MAAM,MAAM,KAAK;AACvB,QAAI,CAAC,IAAK;AACV,QAAI,QAAQ,OAAO;AACjB,YAAM,OAAO,YAAY,KAAK;AAC9B,UAAI,KAAK,SAAS,EAAG,MAAK,KAAK,MAAM,KAAK;AAAA,IAC5C,WAAW,QAAQ,SAAS;AAC1B,WAAK,KAAK,KAAM,KAAK;AAAA,IACvB,WAAW,QAAQ,QAAQ;AAGzB,WAAK,KAAK,MAAM,KAAK;AAAA,IACvB,WAAW,QAAQ,aAAa;AAC9B,YAAM,QAAQ,wBAAwB,OAAO,GAAG;AAChD,UAAI,MAAO,KAAI,KAAK,EAAE,QAAQ,MAAM,CAAC;AAAA,IACvC;AAAA,EAEF;AACF;AAOA,IAAM,wBAAgD;AAAA,EACpD,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AAAA,EACN,KAAK;AAAA,EACL,UAAU;AAAA,EACV,UAAU;AAAA,EACV,WAAW;AAAA,EACX,aAAa;AAAA,EACb,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,WAAW;AAAA,EACX,OAAO;AAAA,EACP,OAAO;AACT;AAEA,SAAS,SAAS,KAAuC;AACvD,QAAM,IAAe,CAAC;AACtB,MAAI,CAAC,IAAK,QAAO;AACjB,aAAW,SAAS,WAAW,GAAG,GAAG;AACnC,UAAM,MAAM,MAAM,KAAK;AACvB,QAAI,CAAC,IAAK;AACV,YAAQ,KAAK;AAAA,MACX,KAAK;AACH,YAAI,CAAC,QAAQ,KAAK,OAAO,OAAO,CAAC,EAAG,GAAE,OAAO;AAC7C;AAAA,MACF,KAAK;AACH,YAAI,CAAC,QAAQ,KAAK,OAAO,OAAO,CAAC,EAAG,GAAE,SAAS;AAC/C;AAAA,MACF,KAAK,OAAO;AACV,cAAM,MAAM,KAAK,OAAO,OAAO;AAC/B,YAAI,OAAO,IAAI,YAAY,MAAM,OAAQ,GAAE,YAAY;AACvD;AAAA,MACF;AAAA,MACA,KAAK;AACH,YAAI,CAAC,QAAQ,KAAK,OAAO,OAAO,CAAC,EAAG,GAAE,SAAS;AAC/C;AAAA,MACF,KAAK,WAAW;AACd,cAAM,MAAM,KAAK,OAAO,OAAO;AAC/B,cAAM,MAAM,kBAAkB,GAAG;AACjC,YAAI,IAAK,GAAE,QAAQ;AACnB;AAAA,MACF;AAAA,MACA,KAAK,SAAS;AAEZ,cAAM,OAAO,KAAK,OAAO,QAAQ;AACjC,YAAI,QAAQ,KAAK,YAAY,MAAM,QAAQ;AACzC,gBAAM,MAAM,kBAAkB,IAAI;AAClC,cAAI,IAAK,GAAE,YAAY;AAAA,QACzB;AACA;AAAA,MACF;AAAA,MACA,KAAK,eAAe;AAClB,cAAM,MAAM,uBAAuB,KAAK,OAAO,OAAO,KAAK,IAAI,YAAY,CAAC;AAC5E,YAAI,IAAK,GAAE,YAAY;AACvB;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAIA,KAAK,QAAQ;AACX,cAAM,UAAU,OAAO,WAAW,KAAK,OAAO,OAAO,KAAK,EAAE;AAC5D,cAAM,MAAM,kBAAkB,OAAO;AACrC,YAAI,IAAK,GAAE,WAAW;AACtB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,WAAW,GAA0B,GAAyB;AACrE,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,EAAE,GAAG,GAAG,GAAG,EAAE;AACtB;AAEA,SAAS,YAAY,MAAyB;AAC5C,MAAI,MAAM;AACV,aAAW,SAAS,WAAW,IAAI,GAAG;AACpC,QAAI,WAAW,OAAO;AACpB,YAAM,IAAI,MAAM,OAAO;AACvB,UAAI,OAAO,MAAM,SAAU,QAAO;AAAA,IACpC;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,QAAQ,KAAkC;AACjD,MAAI,OAAO,KAAM,QAAO;AACxB,QAAM,IAAI,IAAI,YAAY;AAC1B,SAAO,MAAM,OAAO,MAAM,WAAW,MAAM;AAC7C;AAEA,SAAS,KAAK,KAAgB,MAAc,OAAwB;AAClE,QAAM,KACJ,OAAO,KAAK,KAAK,EAAE,WAAW,IAAI,EAAE,QAAQ,KAAK,IAAI,EAAE,QAAQ,MAAM,YAAY,EAAE,GAAG,MAAM,EAAE;AAChG,MAAI,KAAK,EAAE;AACb;AAEA,SAAS,cAAc,KAA2B;AAChD,QAAM,MAAiB,CAAC;AACxB,aAAW,MAAM,KAAK;AACpB,UAAM,OAAO,IAAI,IAAI,SAAS,CAAC;AAC/B,QACE,QACA,OAAO,KAAK,WAAW,YACvB,OAAO,GAAG,WAAW,YACrB,UAAU,KAAK,YAAY,GAAG,UAAU,GACxC;AACA,WAAK,SAAS,KAAK,SAAS,GAAG;AAC/B;AAAA,IACF;AACA,QAAI,KAAK,EAAE;AAAA,EACb;AACA,SAAO;AACT;AAEA,SAAS,UAAU,GAA0B,GAAmC;AAC9E,QAAM,KAAK,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AACxC,QAAM,KAAK,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AACxC,MAAI,GAAG,WAAW,GAAG,OAAQ,QAAO;AACpC,WAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,KAAK;AAClC,QAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAG,QAAO;AAC5B,UAAM,KAAM,EAA8B,GAAG,CAAC,CAAC;AAC/C,UAAM,KAAM,EAA8B,GAAG,CAAC,CAAC;AAC/C,QAAI,OAAO,OAAO,YAAY,OAAO,QAAQ,OAAO,OAAO,YAAY,OAAO,MAAM;AAClF,UAAI,KAAK,UAAU,EAAE,MAAM,KAAK,UAAU,EAAE,EAAG,QAAO;AAAA,IACxD,WAAW,OAAO,IAAI;AACpB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;ACrLO,SAAS,iBACd,GACA,KACA,WACiB;AACjB,QAAM,MAAM,UAAU,GAAG,OAAO;AAChC,QAAM,QAAQ,MAAM,UAAU,KAAK,SAAS,IAAI;AAChD,QAAM,UAAU,MAAM,KAAK,UAAU,KAAK,UAAU,KAAK,CAAC,GAAG,OAAO,IAAI;AAExE,QAAM,QAAQ,yBAAyB,WAAW,CAAC,GAAG,GAAG;AACzD,QAAM,OAAO,YAAY,KAAK;AAC9B,QAAM,QAAQ,WAAW,MAAM,KAAK,UAAU,KAAK,MAAM,KAAK,CAAC,GAAG,OAAO,IAAI,MAAS;AACtF,QAAM,QAAQ,MAAM,CAAC,CAAC,UAAU,KAAK,QAAQ,IAAI;AAGjD,MAAI,OAAO;AACT,UAAM,WAAW,UAAU,QAAQ,KAAK;AACxC,UAAMC,SAAoB;AAAA,MACxB,UAAU,UAAU,YAAY;AAAA,MAChC,WAAW,UAAU,aAAa;AAAA,IACpC;AACA,QAAI,UAAU,aAAa,WAAW;AAMpC,UAAI,SAAS,WAAW,KAAM,CAAAA,OAAM,YAAY,SAAS;AACzD,UAAI,SAAS,UAAW,CAAAA,OAAM,YAAY,SAAS;AAAA,IACrD;AACA,QAAI,MAAO,CAAAA,OAAM,QAAQ;AACzB,QAAI,MAAO,CAAAA,OAAM,MAAM;AACvB,WAAO,EAAE,MAAM,YAAY,MAAM,OAAO,OAAAA,OAAM;AAAA,EAChD;AAGA,QAAM,eAAe,UAAU,mBAAmB,OAAO,IAAI;AAC7D,MAAI,gBAAgB,MAAM;AACxB,UAAMA,SAAoB,EAAE,OAAO,aAAa;AAChD,QAAI,MAAO,CAAAA,OAAM,QAAQ;AACzB,QAAI,MAAO,CAAAA,OAAM,MAAM;AACvB,WAAO,EAAE,MAAM,WAAW,MAAM,OAAO,OAAAA,OAAM;AAAA,EAC/C;AAGA,MAAI,OAAO,cAAc,GAAG,GAAG;AAC7B,UAAMA,SAAoB,CAAC;AAC3B,QAAI,MAAO,CAAAA,OAAM,QAAQ;AACzB,QAAI,MAAO,CAAAA,OAAM,MAAM;AACvB,WAAO,EAAE,MAAM,cAAc,MAAM,OAAO,OAAAA,OAAM;AAAA,EAClD;AAGA,MAAI,OAAO,oBAAoB,GAAG,GAAG;AACnC,UAAMA,SAAoB,CAAC;AAC3B,QAAI,MAAO,CAAAA,OAAM,QAAQ;AACzB,QAAI,MAAO,CAAAA,OAAM,MAAM;AACvB,WAAO,EAAE,MAAM,aAAa,MAAM,OAAO,OAAAA,OAAM;AAAA,EACjD;AAEA,QAAM,QAAoB,CAAC;AAC3B,MAAI,MAAO,OAAM,QAAQ;AACzB,MAAI,MAAO,OAAM,MAAM;AACvB,SAAO,OAAO,OAAO,eAAe,KAAK,KAAK,SAAS,CAAC,CAAC;AACzD,SAAO,EAAE,MAAM,aAAa,MAAM,OAAO,MAAM;AACjD;AASA,SAAS,eAAe,KAA4B,SAAuC;AACzF,MAAI,CAAC,OAAO,QAAS,QAAO,CAAC;AAC7B,QAAM,OAAO,UAAU,KAAK,QAAQ;AACpC,MAAI,CAAC,KAAM,QAAO,CAAC;AACnB,MAAI,CAAC,aAAa,MAAM,UAAU,EAAG,QAAO,CAAC;AAC7C,SAAO,EAAE,YAAY,MAAM,mBAAmB,sBAAsB,GAAG,EAAE;AAC3E;AAGA,SAAS,sBAAsB,KAAmC;AAChE,QAAM,KAAK,UAAU,KAAK,WAAW;AACrC,QAAM,OAAO,KAAK,OAAO,WAAW,KAAK,IAAI,QAAQ,KAAK,EAAE,IAAI;AAChE,MAAI,CAAC,OAAO,SAAS,IAAI,EAAG,QAAO;AACnC,QAAM,UAA+B,CAAC,GAAG,KAAK,CAAC;AAC/C,MAAI,OAA0B;AAC9B,MAAI,WAAW;AACf,aAAW,KAAK,SAAS;AACvB,UAAM,IAAI,KAAK,IAAI,OAAO,MAAM,CAAC;AACjC,QAAI,IAAI,UAAU;AAChB,iBAAW;AACX,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,YAAY,OAA0B;AAC7C,MAAI,MAAM;AACV,aAAW,MAAM,OAAO;AACtB,QAAI,OAAO,GAAG,WAAW,SAAU,QAAO,GAAG;AAAA,EAC/C;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,IAA0C;AACpE,QAAM,IAAI,uBAAuB,KAAK,EAAE;AACxC,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,IAAI,OAAO,SAAS,EAAE,CAAC,GAAG,EAAE;AAClC,SAAQ,KAA+B;AACzC;AAEA,SAAS,cAAc,KAAyB;AAC9C,QAAM,OAAO,UAAU,KAAK,QAAQ;AACpC,MAAI,CAAC,KAAM,QAAO;AAIlB,SAAO,aAAa,MAAM,UAAU,SAAS;AAC/C;AAEA,SAAS,oBAAoB,KAAyB;AACpD,QAAM,MAAM,UAAU,KAAK,OAAO;AAClC,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,OAAO,KAAK,KAAK,QAAQ;AAC/B,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK,YAAY,MAAM;AAChC;;;AClIO,SAAS,aAAa,KAAgB,KAAkC;AAC7E,QAAM,UAAU,UAAU,KAAK,WAAW;AAC1C,QAAM,WAAW,UAAU,aAAa,SAAS,WAAW,IAAI,CAAC;AACjE,QAAM,MAAM,aAAa,KAAK,MAAM;AAKpC,MAAI,OAAO,SAAS;AACpB,aAAW,MAAM,KAAK;AACpB,UAAM,MAAM,aAAa,IAAI,MAAM;AACnC,QAAI,QAAQ;AACZ,eAAW,MAAM,IAAK,UAAS,WAAW,EAAE;AAC5C,QAAI,QAAQ,KAAM,QAAO;AAAA,EAC3B;AACA,MAAI,OAAO,EAAG,QAAO;AACrB,QAAM,OAAO,IAAI;AAIjB,QAAM,QAAsC,IAAI,MAAM,OAAO,IAAI,EAAE,KAAK,IAAI;AAG5E,QAAM,oBAAoB,oBAAI,IAA0C;AAExE,WAAS,IAAI,GAAG,IAAI,MAAM,KAAK;AAC7B,UAAM,KAAK,IAAI,CAAC;AAChB,QAAI,SAAS;AACb,eAAW,MAAM,aAAa,IAAI,MAAM,GAAG;AAEzC,aAAO,SAAS,QAAQ,MAAM,IAAI,OAAO,MAAM,MAAM,KAAM;AAC3D,UAAI,UAAU,KAAM;AAEpB,YAAM,OAAO,UAAU,IAAI,QAAQ;AACnC,YAAM,WAAW,WAAW,EAAE;AAC9B,YAAM,aAAa,OAAO,UAAU,MAAM,UAAU,IAAI;AACxD,YAAM,YAAY,aAAc,KAAK,YAAY,OAAO,KAAK,aAAc;AAE3E,UAAI,cAAc,cAAc,WAAW;AAGzC,cAAM,SAAS,kBAAkB,IAAI,MAAM;AAC3C,YAAI,QAAQ;AACV,gBAAM,YAAY,OAAO,MAAM,OAAO,OAAO;AAC7C,gBAAM,IAAI,MAAM,SAAS;AACzB,cAAI,GAAG;AACL,cAAE,QAAQ,EAAE,GAAI,EAAE,SAAS,CAAC,GAAI,UAAU,EAAE,OAAO,WAAW,KAAK,EAAE;AAAA,UACvE;AAAA,QACF;AACA,iBAAS,IAAI,GAAG,IAAI,YAAY,SAAS,IAAI,MAAM,KAAK;AACtD,gBAAM,IAAI,OAAO,SAAS,CAAC,IAAI,YAAY;AAAA,QAC7C;AACA,kBAAU;AACV;AAAA,MACF;AAGA,YAAM,QAAQ,oBAAoB,IAAI,GAAG;AACzC,YAAM,OAAO,YAAY,KAAK;AAC9B,YAAM,YAAuB,CAAC;AAC9B,UAAI,WAAW,EAAG,WAAU,UAAU;AAGtC,YAAM,WAA2B,EAAE,MAAM,OAAO,OAAO,UAAU;AACjE,YAAM,IAAI,OAAO,MAAM,IAAI;AAG3B,eAAS,IAAI,GAAG,IAAI,YAAY,SAAS,IAAI,MAAM,KAAK;AACtD,cAAM,IAAI,OAAO,SAAS,CAAC,IAAI,YAAY;AAAA,MAC7C;AAEA,UAAI,cAAc,WAAW;AAC3B,0BAAkB,IAAI,QAAQ,EAAE,KAAK,GAAG,KAAK,OAAO,CAAC;AAAA,MACvD,OAAO;AACL,0BAAkB,OAAO,MAAM;AAAA,MACjC;AACA,gBAAU;AAAA,IACZ;AAIA,aAAS,IAAI,GAAG,IAAI,MAAM,KAAK;AAC7B,UAAI,MAAM,IAAI,OAAO,CAAC,MAAM,MAAM;AAChC,cAAM,IAAI,OAAO,CAAC,IAAI,UAAU;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAA0B,MAAM,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC;AAEjE,QAAM,QAAoB,EAAE,MAAM,KAAK;AACvC,QAAM,SAAS,iBAAiB,GAAG;AACnC,MAAI,OAAQ,OAAM,eAAe;AACjC,QAAM,cAAc,gBAAgB,GAAG;AACvC,MAAI,YAAa,OAAM,cAAc;AACrC,SAAO,EAAE,MAAM,SAAS,MAAM,IAAI,OAAO,CAAC,GAAG,OAAO,MAAM;AAC5D;AAmBA,SAAS,gBAAgB,KAAoC;AAC3D,QAAM,QAAQ,UAAU,KAAK,SAAS;AACtC,QAAM,IAAI,QAAQ,UAAU,OAAO,cAAc,IAAI;AACrD,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,aAAW,QAAQ,OAAO;AACxB,UAAM,IAAI,UAAU,GAAG,IAAI;AAC3B,QAAI,CAAC,EAAG;AACR,UAAM,OAAO,KAAK,GAAG,OAAO,KAAK,IAAI,YAAY;AACjD,QAAI,QAAQ,MAAM,QAAQ,UAAU,QAAQ,MAAO;AACnD,UAAM,MAAM,KAAK,GAAG,SAAS;AAC7B,QAAI,CAAC,OAAO,IAAI,YAAY,MAAM,OAAQ;AAC1C,UAAM,MAAM,kBAAkB,GAAG;AACjC,QAAI,IAAK,QAAO;AAAA,EAClB;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,KAA+C;AACvE,QAAM,QAAQ,UAAU,KAAK,SAAS;AACtC,QAAM,IAAI,QAAQ,UAAU,OAAO,cAAc,IAAI;AACrD,MAAI,CAAC,EAAG,QAAO;AAIf,QAAM,SAAS,IAAI,UAA6B,aAAa,GAAG,GAAG,KAAK;AAExE,QAAM,MAAM,OAAO,OAAO;AAC1B,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,OAAO,OAAO,UAAU,SAAS;AACvC,QAAM,QAAQ,OAAO,WAAW,OAAO;AACvC,QAAM,KAAK,OAAO,WAAW;AAC7B,QAAM,KAAK,OAAO,WAAW;AAE7B,MAAI,OAAO,UAAU,QAAQ,SAAS,MAAM,GAAI,QAAO;AACvD,MAAI,OAAO,UAAU,QAAQ,SAAS,CAAC,MAAM,CAAC,GAAI,QAAO;AACzD,MAAI,OAAO,UAAU,CAAC,QAAQ,CAAC,SAAS,MAAM,CAAC,GAAI,QAAO;AAC1D,MAAI,CAAC,OAAO,CAAC,UAAU,QAAQ,SAAS,CAAC,MAAM,GAAI,QAAO;AAC1D,MAAI,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,GAAI,QAAO;AAC7D,SAAO;AACT;AAEA,SAAS,WAAW,IAAuB;AACzC,QAAM,OAAO,UAAU,IAAI,QAAQ;AACnC,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,KAAK,UAAU,MAAM,YAAY;AACvC,MAAI,CAAC,GAAI,QAAO;AAChB,QAAM,IAAI,aAAa,KAAK,IAAI,OAAO,GAAG,CAAC;AAC3C,SAAO,KAAK,IAAI,GAAG,CAAC;AACtB;AAEA,SAAS,oBAAoB,IAAe,KAA4B;AAItE,QAAM,MAAiB,CAAC;AACxB,MAAI,QAAQ;AACZ,aAAW,SAAS,WAAW,EAAE,GAAG;AAClC,QAAI,MAAM,KAAK,MAAM,MAAO;AAC5B,QAAI,CAAC,MAAO,KAAI,KAAK,EAAE,QAAQ,KAAK,CAAC;AACrC,YAAQ;AACR,QAAI,KAAK,GAAG,yBAAyB,WAAW,KAAK,GAAG,GAAG,CAAC;AAAA,EAC9D;AACA,SAAO;AACT;AAEA,SAAS,YAAY,OAA0B;AAC7C,MAAI,MAAM;AACV,aAAW,MAAM,OAAO;AACtB,QAAI,OAAO,GAAG,WAAW,SAAU,QAAO,GAAG;AAAA,EAC/C;AACA,SAAO;AACT;AAEA,SAAS,cAA8B;AACrC,SAAO,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,OAAO,EAAE,SAAS,KAAK,EAAE;AACzD;AAEA,SAAS,YAA4B;AACnC,SAAO,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE;AAC1C;;;AC9MA,IAAM,oBAAmD;AAAA,EACvD,SAAS;AAAA,EACT,aAAa;AAAA,EACb,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,YAAY;AACd;AAiBO,IAAM,oBAAN,MAAwB;AAAA,EACrB,kBAAkB,oBAAI,IAAoB;AAAA,EAC1C,mBAAmB,oBAAI,IAAiC;AAAA,EACxD,qBAAqB,oBAAI,IAAiC;AAAA;AAAA,EAE1D,WAAW,oBAAI,IAAsB;AAAA,EAE7C,YAAY,cAAuC;AACjD,QAAI,CAAC,aAAc;AACnB,eAAW,OAAO,cAAc;AAC9B,UAAI,MAAM,GAAG,MAAM,cAAe;AAClC,iBAAW,SAAS,WAAW,GAAG,GAAG;AACnC,cAAM,MAAM,MAAM,KAAK;AACvB,YAAI,QAAQ,iBAAiB;AAC3B,gBAAM,aAAa,KAAK,OAAO,iBAAiB;AAChD,cAAI,CAAC,WAAY;AACjB,gBAAM,SAAS,oBAAI,IAAoB;AACvC,gBAAM,SAAS,oBAAI,IAAoB;AACvC,qBAAW,OAAO,aAAa,OAAO,OAAO,GAAG;AAC9C,kBAAM,OAAO,aAAa,KAAK,KAAK,QAAQ,GAAG,CAAC;AAChD,kBAAM,MAAM,KAAK,UAAU,KAAK,UAAU,KAAK,CAAC,GAAG,OAAO;AAC1D,gBAAI,IAAK,QAAO,IAAI,MAAM,GAAG;AAC7B,kBAAM,QAAQ,UAAU,KAAK,SAAS;AACtC,gBAAI,MAAO,QAAO,IAAI,MAAM,aAAa,KAAK,OAAO,OAAO,GAAG,CAAC,CAAC;AAAA,UACnE;AACA,eAAK,iBAAiB,IAAI,YAAY,MAAM;AAC5C,eAAK,mBAAmB,IAAI,YAAY,MAAM;AAAA,QAChD,WAAW,QAAQ,SAAS;AAC1B,gBAAM,QAAQ,KAAK,OAAO,SAAS;AACnC,gBAAM,cAAc,UAAU,OAAO,iBAAiB;AACtD,gBAAM,aAAa,cAAc,KAAK,aAAa,OAAO,IAAI;AAC9D,cAAI,SAAS,WAAY,MAAK,gBAAgB,IAAI,OAAO,UAAU;AAAA,QACrE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,QAAQ,OAAwD;AAC9D,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,UAAU,UAAU,OAAO,QAAQ;AACzC,UAAM,WAAW,UAAU,OAAO,SAAS;AAC3C,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,QAAQ,KAAK,UAAU,OAAO;AACpC,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,YAAY,WAAW,aAAa,UAAU,KAAK,SAAS,OAAO,IAAI,QAAW,CAAC,CAAC;AAC1F,UAAM,aAAa,KAAK,gBAAgB,IAAI,KAAK;AACjD,QAAI,cAAc,MAAM;AAItB,aAAO,EAAE,UAAU,UAAU,UAAU;AAAA,IACzC;AACA,UAAM,SAAS,KAAK,iBAAiB,IAAI,UAAU;AACnD,UAAM,MAAM,QAAQ,IAAI,SAAS,KAAK;AACtC,UAAM,WAAW,IAAI,YAAY;AACjC,UAAM,WAAqB,aAAa,YAAY,aAAa,SAAS,WAAW;AACrF,QAAI,aAAa,SAAU,QAAO,EAAE,UAAU,UAAU;AAGxD,UAAM,QAAQ,KAAK,mBAAmB,IAAI,UAAU,GAAG,IAAI,SAAS,KAAK;AACzE,UAAM,IAAI,KAAK,SAAS,IAAI,KAAK,KAAK,CAAC;AACvC,MAAE,SAAS,IAAI,EAAE,SAAS,KAAK,OAAO,QAAQ,EAAE,SAAS,IAAI;AAC7D,MAAE,SAAS,YAAY;AACvB,SAAK,SAAS,IAAI,OAAO,CAAC;AAE1B,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,SAAS,EAAE,SAAS;AAAA,MACpB,WAAW,kBAAkB,GAAG,KAAK;AAAA,IACvC;AAAA,EACF;AACF;AAEA,SAAS,WAAW,GAAmB;AACrC,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC;AAC/C;;;AClIA,kBAAwD;AAYxD,IAAM,cAAc,OAAO;AAE3B,SAAS,UAAU,OAAuB;AACxC,aAAO,oBAAO,QAAQ,WAAW;AACnC;AAEO,SAAS,qBACd,QAC0B;AAC1B,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,OAAO,UAAU,QAAQ,QAAQ;AACvC,QAAM,QAAQ,UAAU,QAAQ,SAAS;AACzC,MAAI,CAAC,QAAQ,CAAC,MAAO,QAAO;AAE5B,QAAM,aAAa,OAAO,aAAa,KAAK,MAAM,KAAK,GAAG,CAAC,IAAI;AAC/D,QAAM,cAAc,OAAO,aAAa,KAAK,MAAM,KAAK,GAAG,CAAC,IAAI;AAChE,QAAM,WAAW,QAAQ,aAAa,KAAK,OAAO,OAAO,GAAG,CAAC,IAAI;AACjE,QAAM,cAAc,QAAQ,aAAa,KAAK,OAAO,UAAU,GAAG,CAAC,IAAI;AACvE,QAAM,YAAY,QAAQ,aAAa,KAAK,OAAO,QAAQ,GAAG,CAAC,IAAI;AACnE,QAAM,aAAa,QAAQ,aAAa,KAAK,OAAO,SAAS,GAAG,CAAC,IAAI;AAErE,MAAI,cAAc,KAAK,eAAe,KAAK,YAAY,KAAK,aAAa,GAAG;AAC1E,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,aAAa,IAAI,UAAU,UAAU,IAAI;AACvD,QAAM,SAAS,cAAc,IAAI,UAAU,WAAW,IAAI;AAC1D,QAAM,WAAyB;AAAA,IAC7B;AAAA,IACA;AAAA,IACA,WAAW,UAAU,YAAY,CAAC;AAAA,IAClC,cAAc,UAAU,eAAe,CAAC;AAAA,IACxC,YAAY,UAAU,aAAa,CAAC;AAAA,IACpC,aAAa,UAAU,cAAc,CAAC;AAAA,EACxC;AACA,WAAS,aAAS,0BAAa,SAAS,OAAO,SAAS,MAAM;AAC9D,SAAO;AACT;;;AR/BA,eAAsB,mBACpB,OAC6B;AAC7B,QAAM,OAAO,MAAM,SAAS,KAAK;AACjC,QAAM,YAAY,IAAI,kBAAkB,KAAK,YAAY;AACzD,QAAM,MAAkB;AAAA,IACtB,eAAe,KAAK;AAAA,IACpB,YAAY,KAAK;AAAA,EACnB;AAEA,QAAM,OAAO,SAAS,KAAK,WAAW;AACtC,MAAI,CAAC,KAAM,QAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,aAAa,MAAM,IAAI,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE,CAAC,EAAE;AAEpF,QAAM,SAA4B,CAAC;AACnC,aAAW,SAAS,WAAW,IAAI,GAAG;AACpC,UAAM,MAAM,MAAM,KAAK;AACvB,QAAI,QAAQ,OAAO;AACjB,aAAO,KAAK,iBAAiB,OAAO,KAAK,SAAS,CAAC;AAAA,IACrD,WAAW,QAAQ,SAAS;AAC1B,aAAO,KAAK,aAAa,OAAO,GAAG,CAAC;AAAA,IACtC;AAAA,EAEF;AAEA,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,KAAK,EAAE,MAAM,aAAa,MAAM,IAAI,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE,CAAC;AAAA,EACnE;AAEA,2BAAyB,MAAM;AAE/B,QAAM,eAAe,qBAAqB,UAAU,MAAM,UAAU,CAAC;AACrE,SAAO,eAAe,EAAE,QAAQ,aAAa,IAAI,EAAE,OAAO;AAC5D;AAaO,SAAS,yBAAyB,QAAiC;AAIxE,MAAI,aAAa;AACjB,MAAI;AACJ,MAAI,WAAqB,CAAC;AAE1B,QAAM,aAAa,MAAM;AACvB,iBAAa;AACb,iBAAa;AACb,eAAW,CAAC;AAAA,EACd;AAEA,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,SAAS,YAAY;AAC7B,iBAAW;AACX;AAAA,IACF;AACA,SAAK,MAAM,MAAM,YAAY,cAAc,WAAW;AAGpD,iBAAW;AACX;AAAA,IACF;AACA,UAAM,QAAQ,MAAM,MAAM,aAAa;AACvC,UAAM,UAAU,OAAO,MAAM,MAAM,cAAc,WAAW,MAAM,MAAM,YAAY;AAEpF,UAAM,uBAAuB,SAAS,KAAK,KAAK,QAAQ,YAAY,SAAS,KAAK,IAAI;AACtF,UAAM,sBAAsB,SAAS,KAAK,KAAK,QAAQ,QAAQ,KAAK,YAAY;AAChF,UAAM,gBACJ,cACA,MAAM,MAAM,cAAc,eACzB,wBAAwB;AAE3B,QAAI,eAAe;AACjB,aAAO,MAAM,MAAM;AAAA,IACrB,OAAO;AAEL,mBAAa;AACb,mBAAa,MAAM,MAAM;AACzB,iBAAW,CAAC;AAAA,IACd;AACA,aAAS,KAAK,IAAI;AAClB,aAAS,SAAS,QAAQ;AAAA,EAC5B;AACF;AAGA,eAAsB,yBACpB,OACyB;AACzB,QAAM,aAAa,MAAM,mBAAmB,KAAK;AACjD,SAAO,4BAAe,SAAS,UAAU;AAC3C;AAEA,SAAS,SAAS,aAAiD;AACjE,aAAW,OAAO,aAAa;AAC7B,QAAI,MAAM,GAAG,MAAM,cAAc;AAC/B,YAAM,OAAO,UAAU,KAAK,QAAQ;AACpC,UAAI,KAAM,QAAO;AAAA,IACnB;AAAA,EACF;AACA,SAAO;AACT;","names":["import_core","JSZip","attrs"]}
package/dist/index.js CHANGED
@@ -113,6 +113,15 @@ function findChildren(node, tag) {
113
113
  }
114
114
  return out;
115
115
  }
116
+ function borderSideOn(parent, ...names) {
117
+ if (!parent) return false;
118
+ return names.some((name) => {
119
+ const n = findChild(parent, name);
120
+ if (!n) return false;
121
+ const val = (attr(n, "w:val") ?? "").toLowerCase();
122
+ return val !== "" && val !== "none" && val !== "nil";
123
+ });
124
+ }
116
125
 
117
126
  // src/units.ts
118
127
  function halfPointsToCssPt(halfPoints) {
@@ -428,10 +437,7 @@ function readAnswerLine(pPr, hasText) {
428
437
  if (!pPr || hasText) return {};
429
438
  const pBdr = findChild(pPr, "w:pBdr");
430
439
  if (!pBdr) return {};
431
- const bottom = findChild(pBdr, "w:bottom");
432
- if (!bottom) return {};
433
- const val = (attr(bottom, "w:val") ?? "").toLowerCase();
434
- if (val === "" || val === "none" || val === "nil") return {};
440
+ if (!borderSideOn(pBdr, "w:bottom")) return {};
435
441
  return { answerLine: true, answerLineSpacing: readAnswerLineSpacing(pPr) };
436
442
  }
437
443
  function readAnswerLineSpacing(pPr) {
@@ -466,7 +472,7 @@ function headingFromStyleId(id) {
466
472
  function hasLeftBorder(pPr) {
467
473
  const pBdr = findChild(pPr, "w:pBdr");
468
474
  if (!pBdr) return false;
469
- return !!findChild(pBdr, "w:left");
475
+ return borderSideOn(pBdr, "w:left", "w:start");
470
476
  }
471
477
  function hasCodeBlockShading(pPr) {
472
478
  const shd = findChild(pPr, "w:shd");
@@ -577,12 +583,7 @@ function readBorderPreset(tbl) {
577
583
  const tblPr = findChild(tbl, "w:tblPr");
578
584
  const b = tblPr ? findChild(tblPr, "w:tblBorders") : void 0;
579
585
  if (!b) return void 0;
580
- const ligado = (...nomes) => nomes.some((nome) => {
581
- const n = findChild(b, nome);
582
- if (!n) return false;
583
- const val = (attr(n, "w:val") ?? "").toLowerCase();
584
- return val !== "" && val !== "none" && val !== "nil";
585
- });
586
+ const ligado = (...nomes) => borderSideOn(b, ...nomes);
586
587
  const top = ligado("w:top");
587
588
  const bottom = ligado("w:bottom");
588
589
  const left = ligado("w:left", "w:start");
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/docx.ts","../src/parse-xml.ts","../src/units.ts","../src/images.ts","../src/runs.ts","../src/paragraphs.ts","../src/tables.ts","../src/numbering.ts","../src/page-settings.ts"],"sourcesContent":["import { EditorDocument, type SerializedBlock, type SerializedDocument } from \"@sofereditor/core\";\nimport { readDocx, childrenOf, tagOf, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { paragraphToBlock } from \"./paragraphs\";\nimport { tableToBlock } from \"./tables\";\nimport { NumberingResolver } from \"./numbering\";\nimport { sectPrToPageSettings } from \"./page-settings\";\nimport type { RunContext } from \"./runs\";\n\n/**\n * Read a `.docx` (OOXML) Blob/ArrayBuffer/Uint8Array and produce a\n * `SerializedDocument` ready to feed `EditorDocument.fromJSON` (or\n * `loadFromJSON` on an existing instance).\n *\n * The mapping is the inverse of `@sofereditor/export-docx`. Round-trip is exact for\n * documents the editor produced; documents authored externally (Word, Google\n * Docs export) load with marks/lists/tables/images recognized and unsupported\n * features dropped silently.\n */\nexport async function docxBlobToDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<SerializedDocument> {\n const file = await readDocx(input);\n const numbering = new NumberingResolver(file.numberingXml);\n const ctx: RunContext = {\n relationships: file.relationships,\n mediaBytes: file.mediaBytes,\n };\n\n const body = findBody(file.documentXml);\n if (!body) return { blocks: [{ type: \"paragraph\", text: \"\", delta: [], attrs: {} }] };\n\n const blocks: SerializedBlock[] = [];\n for (const child of childrenOf(body)) {\n const tag = tagOf(child);\n if (tag === \"w:p\") {\n blocks.push(paragraphToBlock(child, ctx, numbering));\n } else if (tag === \"w:tbl\") {\n blocks.push(tableToBlock(child, ctx));\n }\n // w:sectPr is parsed below (page settings); other body-level metadata ignored.\n }\n\n if (blocks.length === 0) {\n blocks.push({ type: \"paragraph\", text: \"\", delta: [], attrs: {} });\n }\n\n pruneNonLeaderListStarts(blocks);\n\n const pageSettings = sectPrToPageSettings(findChild(body, \"w:sectPr\"));\n return pageSettings ? { blocks, pageSettings } : { blocks };\n}\n\n/**\n * O Word numera por INSTÂNCIA (`numId`): parágrafos comuns entre itens não\n * resetam a contagem. O editor numera por ADJACÊNCIA: o renderer agrupa\n * `listItem`s consecutivos do mesmo `listKind`/`listStyle`, e um `listStart`\n * em item de CONTINUAÇÃO quebra o grupo ali. O parse gravou o ordinal Word em\n * `listStart` de TODOS os itens ordenados; este pós-passe o mantém apenas nos\n * LÍDERES de grupo (primeiro item, item após bloco comum, ou após item de\n * kind/estilo diferente) — assim o editor renderiza exatamente os números do\n * Word (\"3.\" depois de uma linha de resposta, \"b.\" em subitens) sem fragmentar\n * os grupos.\n */\nexport function pruneNonLeaderListStarts(blocks: SerializedBlock[]): void {\n // Estado do grupo de adjacência corrente: estilo do líder e último ordinal\n // visto por nível (para exigir sequência contígua — duas listas Word\n // independentes coladas, \"1,2,3\" + \"1,2\", NÃO podem fundir num 1..5).\n let grupoAtivo = false;\n let grupoStyle: unknown;\n let ordinais: number[] = [];\n\n const resetGrupo = () => {\n grupoAtivo = false;\n grupoStyle = undefined;\n ordinais = [];\n };\n\n for (const block of blocks) {\n if (block.type !== \"listItem\") {\n resetGrupo();\n continue;\n }\n if ((block.attrs.listKind ?? \"bullet\") !== \"ordered\") {\n // Bullet entre ordenados: mantém o grupo do renderer? Não — kind\n // diferente quebra o grupo lá também.\n resetGrupo();\n continue;\n }\n const level = block.attrs.listLevel ?? 0;\n const ordinal = typeof block.attrs.listStart === \"number\" ? block.attrs.listStart : 1;\n\n const mesmoNivelSequencial = ordinais[level] != null && ordinal === ordinais[level] + 1;\n const entrandoEmNivelNovo = ordinais[level] == null && level > 0 && ordinal === 1;\n const continuaGrupo =\n grupoAtivo &&\n block.attrs.listStyle === grupoStyle &&\n (mesmoNivelSequencial || entrandoEmNivelNovo);\n\n if (continuaGrupo) {\n delete block.attrs.listStart;\n } else {\n // Este item lidera um grupo novo no renderer (listStart preservado).\n grupoAtivo = true;\n grupoStyle = block.attrs.listStyle;\n ordinais = [];\n }\n ordinais[level] = ordinal;\n ordinais.length = level + 1;\n }\n}\n\n/** Convenience: parse → instantiate a fresh `EditorDocument`. */\nexport async function docxBlobToEditorDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<EditorDocument> {\n const serialized = await docxBlobToDocument(input);\n return EditorDocument.fromJSON(serialized);\n}\n\nfunction findBody(documentXml: OoxmlNode[]): OoxmlNode | undefined {\n for (const top of documentXml) {\n if (tagOf(top) === \"w:document\") {\n const body = findChild(top, \"w:body\");\n if (body) return body;\n }\n }\n return undefined;\n}\n","import JSZip from \"jszip\";\nimport { XMLParser } from \"fast-xml-parser\";\n\n/**\n * fast-xml-parser with `preserveOrder: true` produces a list of single-key\n * objects per element. Each element is either:\n *\n * - A tag node: `{ \"<tagName>\": <children-array>, \":@\"?: <attrs> }`\n * - A text node: `{ \"#text\": <string> }`\n *\n * Attributes are prefixed with `@_`. The `:@` key holds attributes for the\n * surrounding tag. Children preserve original document order, which matters\n * for paragraph runs (bold word followed by italic word).\n */\nexport type OoxmlNode = Record<string, unknown>;\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n preserveOrder: true,\n trimValues: false, // keep whitespace inside <w:t>\n parseAttributeValue: false,\n parseTagValue: false,\n});\n\nexport interface DocxFile {\n documentXml: OoxmlNode[];\n numberingXml?: OoxmlNode[];\n /** rId → target path (inside the zip), e.g. \"media/image1.png\". */\n relationships: Map<string, string>;\n /** zip path (without leading \"word/\") → bytes for embedded media. */\n mediaBytes: Map<string, Uint8Array>;\n}\n\nexport async function readDocx(input: Blob | ArrayBuffer | Uint8Array): Promise<DocxFile> {\n const data =\n input instanceof Uint8Array\n ? input\n : input instanceof ArrayBuffer\n ? new Uint8Array(input)\n : new Uint8Array(await (input as Blob).arrayBuffer());\n\n const zip = await JSZip.loadAsync(data);\n const documentXml = await readXml(zip, \"word/document.xml\");\n if (!documentXml) throw new Error(\"Invalid .docx: missing word/document.xml\");\n\n const numberingXml = await readXml(zip, \"word/numbering.xml\");\n const relsXml = await readXml(zip, \"word/_rels/document.xml.rels\");\n const relationships = parseRelationships(relsXml);\n\n const mediaBytes = new Map<string, Uint8Array>();\n const mediaFolder = zip.folder(\"word/media\");\n if (mediaFolder) {\n const promises: Promise<void>[] = [];\n mediaFolder.forEach((relativePath, file) => {\n if (file.dir) return;\n promises.push(\n file.async(\"uint8array\").then((bytes) => {\n mediaBytes.set(`media/${relativePath}`, bytes);\n }),\n );\n });\n await Promise.all(promises);\n }\n\n return { documentXml, numberingXml, relationships, mediaBytes };\n}\n\nasync function readXml(zip: JSZip, path: string): Promise<OoxmlNode[] | undefined> {\n const entry = zip.file(path);\n if (!entry) return undefined;\n const text = await entry.async(\"string\");\n return parser.parse(text) as OoxmlNode[];\n}\n\nfunction parseRelationships(nodes: OoxmlNode[] | undefined): Map<string, string> {\n const out = new Map<string, string>();\n if (!nodes) return out;\n // <Relationships><Relationship Id=\"rId1\" Type=\"...\" Target=\"media/image1.png\"/>...\n for (const top of nodes) {\n const children = top[\"Relationships\"];\n if (!Array.isArray(children)) continue;\n for (const child of children as OoxmlNode[]) {\n if (!(\"Relationship\" in child)) continue;\n const attrs = (child[\":@\"] ?? {}) as Record<string, unknown>;\n const id = attrs[\"@_Id\"];\n const target = attrs[\"@_Target\"];\n if (typeof id === \"string\" && typeof target === \"string\") {\n out.set(id, target);\n }\n }\n }\n return out;\n}\n\n// ---------- generic walkers over preserveOrder shape ----------\n\n/** Returns the tag name (e.g. \"w:p\") of a preserveOrder element, or null for text. */\nexport function tagOf(node: OoxmlNode): string | null {\n for (const key of Object.keys(node)) {\n if (key === \":@\") continue;\n return key;\n }\n return null;\n}\n\n/**\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 * Unit conversions between OOXML and the editor's serialized format.\n * These are the inverses of the helpers in `@sofereditor/export-docx/src/docx.ts`.\n */\n\n/** docx half-points → CSS pt string. 24 (half-pt) → \"12pt\". */\nexport function halfPointsToCssPt(halfPoints: number): string | undefined {\n if (!Number.isFinite(halfPoints) || halfPoints <= 0) return undefined;\n const pt = halfPoints / 2;\n return `${stripTrailingZeros(pt)}pt`;\n}\n\n/** OOXML RRGGBB (no '#') → CSS hex with leading '#'. */\nexport function docxHexToCssColor(hex: string | undefined): string | undefined {\n if (!hex) return undefined;\n const m = /^[0-9a-f]{6}$/i.exec(hex.trim());\n if (!m) return undefined;\n return `#${hex.toLowerCase()}`;\n}\n\n/** OOXML alignment value → editor AlignValue. */\nexport function parseAlign(val: string | undefined): \"left\" | \"center\" | \"right\" | \"justify\" | undefined {\n if (!val) return undefined;\n switch (val.toLowerCase()) {\n case \"left\":\n case \"start\":\n return \"left\";\n case \"center\":\n return \"center\";\n case \"right\":\n case \"end\":\n return \"right\";\n case \"both\":\n case \"distribute\":\n case \"justify\":\n return \"justify\";\n default:\n return undefined;\n }\n}\n\n/** Parse XML attribute that should be a positive integer. */\nexport function parseIntAttr(val: unknown, fallback = 0): number {\n if (typeof val === \"number\" && Number.isFinite(val)) return Math.trunc(val);\n if (typeof val !== \"string\") return fallback;\n const n = Number.parseInt(val, 10);\n return Number.isFinite(n) ? n : fallback;\n}\n\nfunction stripTrailingZeros(n: number): string {\n // 12 → \"12\", 12.5 → \"12.5\"\n return Number.isInteger(n) ? n.toString() : n.toFixed(2).replace(/\\.?0+$/, \"\");\n}\n","import type { ImageEmbed } from \"@sofereditor/core\";\nimport { attr, childrenOf, tagOf, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/** 1 inch = 914400 EMU = 96 px → 1 px = 9525 EMU. */\nconst EMU_PER_PX = 9525;\n\nconst MIME_BY_EXT: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n bmp: \"image/bmp\",\n svg: \"image/svg+xml\",\n};\n\nexport interface ImageContext {\n relationships: Map<string, string>;\n mediaBytes: Map<string, Uint8Array>;\n}\n\n/**\n * Walk a `<w:drawing>` node and return an `ImageEmbed` if it contains a\n * recognized inline picture. Returns null for unsupported drawings (shapes,\n * charts, etc.).\n */\nexport function extractImageFromDrawing(\n drawing: OoxmlNode,\n ctx: ImageContext,\n): ImageEmbed | null {\n // The interesting bits live deep inside: a:blip carries `r:embed`, and\n // wp:extent carries cx/cy. We do a generic walk because the wrapping nodes\n // differ between inline (`wp:inline`) and anchored (`wp:anchor`) drawings.\n let embedId: string | undefined;\n let cx: number | undefined;\n let cy: number | undefined;\n\n walk(drawing, (n) => {\n const tag = tagOf(n);\n if (!tag) return;\n if (tag === \"a:blip\") {\n const id = attr(n, \"r:embed\") ?? attr(n, \"r:link\");\n if (id) embedId = id;\n } else if (tag === \"wp:extent\") {\n cx = parseIntAttr(attr(n, \"cx\"));\n cy = parseIntAttr(attr(n, \"cy\"));\n }\n });\n\n if (!embedId) return null;\n const target = ctx.relationships.get(embedId);\n if (!target) return null;\n // Relationship targets are typically \"media/image1.png\"; the zip lookup uses\n // the same path (we stored bytes keyed under \"media/<file>\").\n const bytes = ctx.mediaBytes.get(target);\n if (!bytes) return null;\n\n const ext = extensionOf(target);\n const mime = MIME_BY_EXT[ext] ?? \"image/png\";\n const src = `data:${mime};base64,${bytesToBase64(bytes)}`;\n const widthPx = cx != null ? Math.max(1, Math.round(cx / EMU_PER_PX)) : 100;\n const heightPx = cy != null ? Math.max(1, Math.round(cy / EMU_PER_PX)) : 100;\n\n return { type: \"image\", src, width: widthPx, height: heightPx };\n}\n\nfunction walk(node: OoxmlNode, visit: (n: OoxmlNode) => void): void {\n visit(node);\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) continue;\n walk(child, visit);\n }\n}\n\nfunction extensionOf(path: string): string {\n const m = /\\.([a-z0-9]+)$/i.exec(path);\n return m ? m[1].toLowerCase() : \"\";\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n // Browser path.\n if (typeof btoa === \"function\") {\n let bin = \"\";\n const CHUNK = 0x8000;\n for (let i = 0; i < bytes.length; i += CHUNK) {\n bin += String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + CHUNK)));\n }\n return btoa(bin);\n }\n // Node fallback.\n return Buffer.from(bytes).toString(\"base64\");\n}\n","import type { DeltaOp, MarkAttrs } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { docxHexToCssColor, halfPointsToCssPt } from \"./units\";\nimport { extractImageFromDrawing, type ImageContext } from \"./images\";\n\nexport interface RunContext extends ImageContext {}\n\n/**\n * Walk a paragraph's children (post `<w:pPr>`) and emit a `DeltaOp[]`. Combines\n * adjacent runs that share the same mark set into a single op for parity with\n * the export — Y.Text.applyDelta would merge them anyway, but pre-merging keeps\n * round-trip output stable.\n */\nexport function paragraphChildrenToDelta(children: OoxmlNode[], ctx: RunContext): DeltaOp[] {\n const ops: DeltaOp[] = [];\n for (const child of children) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:pPr\") continue;\n if (tag === \"w:r\") {\n collectRun(child, ctx, undefined, ops);\n } else if (tag === \"w:hyperlink\") {\n // hyperlinks carry an r:id that maps via relationships to the href.\n const rId = attr(child, \"r:id\");\n const href = rId ? ctx.relationships.get(rId) : undefined;\n const linkMark: MarkAttrs | undefined = href ? { link: { href } } : undefined;\n for (const sub of childrenOf(child)) {\n if (tagOf(sub) === \"w:r\") collectRun(sub, ctx, linkMark, ops);\n }\n }\n }\n return mergeAdjacent(ops);\n}\n\nfunction collectRun(\n run: OoxmlNode,\n ctx: RunContext,\n wrappingMarks: MarkAttrs | undefined,\n out: DeltaOp[],\n): void {\n const marks = mergeMarks(wrappingMarks, runMarks(findChild(run, \"w:rPr\")));\n for (const child of childrenOf(run)) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:t\") {\n const text = textContent(child);\n if (text.length > 0) push(out, text, marks);\n } else if (tag === \"w:tab\") {\n push(out, \"\\t\", marks);\n } else if (tag === \"w:br\") {\n // Treat w:br type=\"page\" as also a soft break for the model — we have no\n // explicit page-break node in the schema; pagination is computed live.\n push(out, \"\\n\", marks);\n } else if (tag === \"w:drawing\") {\n const embed = extractImageFromDrawing(child, ctx);\n if (embed) out.push({ insert: embed });\n }\n // Other run children (w:sym, w:fldChar, etc.) are silently dropped.\n }\n}\n\n/**\n * Os valores nomeados de `w:highlight` do OOXML. O Word só aceita esta lista\n * fechada — documentos com cor arbitrária usam `w:shd`, que é o que o nosso\n * export emite. `none` fica de fora de propósito: ausência = sem marca.\n */\nconst DOCX_HIGHLIGHT_COLORS: Record<string, string> = {\n yellow: \"#ffff00\",\n green: \"#00ff00\",\n cyan: \"#00ffff\",\n magenta: \"#ff00ff\",\n blue: \"#0000ff\",\n red: \"#ff0000\",\n darkblue: \"#000080\",\n darkcyan: \"#008080\",\n darkgreen: \"#008000\",\n darkmagenta: \"#800080\",\n darkred: \"#800000\",\n darkyellow: \"#808000\",\n darkgray: \"#808080\",\n lightgray: \"#c0c0c0\",\n black: \"#000000\",\n white: \"#ffffff\",\n};\n\nfunction runMarks(rPr: OoxmlNode | undefined): MarkAttrs {\n const m: MarkAttrs = {};\n if (!rPr) return m;\n for (const child of childrenOf(rPr)) {\n const tag = tagOf(child);\n if (!tag) continue;\n switch (tag) {\n case \"w:b\":\n if (!isFalse(attr(child, \"w:val\"))) m.bold = true;\n break;\n case \"w:i\":\n if (!isFalse(attr(child, \"w:val\"))) m.italic = true;\n break;\n case \"w:u\": {\n const val = attr(child, \"w:val\");\n if (val && val.toLowerCase() !== \"none\") m.underline = true;\n break;\n }\n case \"w:strike\":\n if (!isFalse(attr(child, \"w:val\"))) m.strike = true;\n break;\n case \"w:color\": {\n const hex = attr(child, \"w:val\");\n const css = docxHexToCssColor(hex);\n if (css) m.color = css;\n break;\n }\n case \"w:shd\": {\n // Sombreamento de RUN = marca-texto. `auto` significa \"sem cor\".\n const fill = attr(child, \"w:fill\");\n if (fill && fill.toLowerCase() !== \"auto\") {\n const css = docxHexToCssColor(fill);\n if (css) m.highlight = css;\n }\n break;\n }\n case \"w:highlight\": {\n const css = DOCX_HIGHLIGHT_COLORS[(attr(child, \"w:val\") ?? \"\").toLowerCase()];\n if (css) m.highlight = css;\n break;\n }\n // w:rFonts intentionally ignored: school standard is Arial for all\n // exams. Any font declared in the .docx is dropped on import so the\n // imported doc inherits Arial from `.ed-root` / PDF base stylesheet.\n case \"w:sz\": {\n const halfPts = Number.parseFloat(attr(child, \"w:val\") ?? \"\");\n const css = halfPointsToCssPt(halfPts);\n if (css) m.fontSize = css;\n break;\n }\n }\n }\n return m;\n}\n\nfunction mergeMarks(a: MarkAttrs | undefined, b: MarkAttrs): MarkAttrs {\n if (!a) return b;\n return { ...a, ...b };\n}\n\nfunction textContent(node: OoxmlNode): string {\n let out = \"\";\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) {\n const t = child[\"#text\"];\n if (typeof t === \"string\") out += t;\n }\n }\n return out;\n}\n\nfunction isFalse(val: string | undefined): boolean {\n if (val == null) return false;\n const v = val.toLowerCase();\n return v === \"0\" || v === \"false\" || v === \"none\";\n}\n\nfunction push(out: DeltaOp[], text: string, marks: MarkAttrs): void {\n const op: DeltaOp =\n Object.keys(marks).length === 0 ? { insert: text } : { insert: text, attributes: { ...marks } };\n out.push(op);\n}\n\nfunction mergeAdjacent(ops: DeltaOp[]): DeltaOp[] {\n const out: DeltaOp[] = [];\n for (const op of ops) {\n const prev = out[out.length - 1];\n if (\n prev &&\n typeof prev.insert === \"string\" &&\n typeof op.insert === \"string\" &&\n sameMarks(prev.attributes, op.attributes)\n ) {\n prev.insert = prev.insert + op.insert;\n continue;\n }\n out.push(op);\n }\n return out;\n}\n\nfunction sameMarks(a: MarkAttrs | undefined, b: MarkAttrs | undefined): boolean {\n const ak = a ? Object.keys(a).sort() : [];\n const bk = b ? Object.keys(b).sort() : [];\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i++) {\n if (ak[i] !== bk[i]) return false;\n const av = (a as Record<string, unknown>)[ak[i]];\n const bv = (b as Record<string, unknown>)[bk[i]];\n if (typeof av === \"object\" && av !== null && typeof bv === \"object\" && bv !== null) {\n if (JSON.stringify(av) !== JSON.stringify(bv)) return false;\n } else if (av !== bv) {\n return false;\n }\n }\n return true;\n}\n","import type {\n AlignValue,\n AnswerLineSpacing,\n BlockAttrs,\n BlockType,\n DeltaOp,\n SerializedBlock,\n} from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { NumberingResolver } from \"./numbering\";\nimport { parseAlign } from \"./units\";\n\n/**\n * Map a `<w:p>` element to a SerializedBlock. Decides between\n * `paragraph / heading / blockquote / codeBlock / listItem` based on the\n * paragraph style + `w:pPr` properties — mirrors the heuristics encoded in\n * `@sofereditor/export-docx/src/docx.ts`.\n */\nexport function paragraphToBlock(\n p: OoxmlNode,\n ctx: RunContext,\n numbering: NumberingResolver,\n): SerializedBlock {\n const pPr = findChild(p, \"w:pPr\");\n const numPr = pPr ? findChild(pPr, \"w:numPr\") : undefined;\n const styleId = pPr ? attr(findChild(pPr, \"w:pStyle\") ?? {}, \"w:val\") : undefined;\n\n const delta = paragraphChildrenToDelta(childrenOf(p), ctx);\n const text = collectText(delta);\n const align = parseAlign(pPr ? attr(findChild(pPr, \"w:jc\") ?? {}, \"w:val\") : undefined);\n const isRtl = pPr ? !!findChild(pPr, \"w:bidi\") : false;\n\n // List? — numPr is the strongest signal.\n if (numPr) {\n const resolved = numbering.resolve(numPr);\n const attrs: BlockAttrs = {\n listKind: resolved?.listKind ?? \"bullet\",\n listLevel: resolved?.listLevel ?? 0,\n };\n if (resolved?.listKind === \"ordered\") {\n // O ordinal Word vai TEMPORARIAMENTE em listStart de todos os itens; o\n // pós-passe em docx.ts remove dos não-líderes (um listStart em item de\n // continuação quebraria o grupo no renderer). listStyle fica em todos —\n // itens do mesmo estilo agrupam; estilo diferente (ex.: a/b/c entre\n // questões decimais) quebra o grupo exatamente como no Word.\n if (resolved.ordinal != null) attrs.listStart = resolved.ordinal;\n if (resolved.listStyle) attrs.listStyle = resolved.listStyle;\n }\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"listItem\", text, delta, attrs };\n }\n\n // Heading?\n const headingLevel = styleId ? headingFromStyleId(styleId) : null;\n if (headingLevel != null) {\n const attrs: BlockAttrs = { level: headingLevel };\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"heading\", text, delta, attrs };\n }\n\n // Blockquote (export uses left border + 480 twip indent).\n if (pPr && hasLeftBorder(pPr)) {\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"blockquote\", text, delta, attrs };\n }\n\n // Code block (export uses Consolas + light-gray shading).\n if (pPr && hasCodeBlockShading(pPr)) {\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n return { type: \"codeBlock\", text, delta, attrs };\n }\n\n const attrs: BlockAttrs = {};\n if (align) attrs.align = align;\n if (isRtl) attrs.dir = \"rtl\";\n Object.assign(attrs, readAnswerLine(pPr, text.length > 0));\n return { type: \"paragraph\", text, delta, attrs };\n}\n\n/**\n * Parágrafo SEM texto e com borda INFERIOR = linha de resposta.\n *\n * A exigência de estar vazio é essencial: um parágrafo com texto e régua é um\n * título sublinhado, e convertê-lo em linha de resposta destruiria a semântica\n * do conteúdo. A borda ESQUERDA já é tratada antes como blockquote.\n */\nfunction readAnswerLine(pPr: OoxmlNode | undefined, hasText: boolean): Partial<BlockAttrs> {\n if (!pPr || hasText) return {};\n const pBdr = findChild(pPr, \"w:pBdr\");\n if (!pBdr) return {};\n const bottom = findChild(pBdr, \"w:bottom\");\n if (!bottom) return {};\n const val = (attr(bottom, \"w:val\") ?? \"\").toLowerCase();\n if (val === \"\" || val === \"none\" || val === \"nil\") return {};\n return { answerLine: true, answerLineSpacing: readAnswerLineSpacing(pPr) };\n}\n\n/** 240 twips = 1 linha. Arredonda para o bucket suportado mais próximo. */\nfunction readAnswerLineSpacing(pPr: OoxmlNode): AnswerLineSpacing {\n const sp = findChild(pPr, \"w:spacing\");\n const line = sp ? Number.parseFloat(attr(sp, \"w:line\") ?? \"\") : NaN;\n if (!Number.isFinite(line)) return 1;\n const buckets: AnswerLineSpacing[] = [1, 1.5, 2];\n let best: AnswerLineSpacing = 1;\n let bestDist = Infinity;\n for (const b of buckets) {\n const d = Math.abs(line - 240 * b);\n if (d < bestDist) {\n bestDist = d;\n best = b;\n }\n }\n return best;\n}\n\nfunction collectText(delta: DeltaOp[]): string {\n let out = \"\";\n for (const op of delta) {\n if (typeof op.insert === \"string\") out += op.insert;\n }\n return out;\n}\n\nfunction headingFromStyleId(id: string): 1 | 2 | 3 | 4 | 5 | 6 | null {\n const m = /^Heading\\s*([1-6])$/i.exec(id);\n if (!m) return null;\n const n = Number.parseInt(m[1], 10);\n return (n as 1 | 2 | 3 | 4 | 5 | 6) ?? null;\n}\n\nfunction hasLeftBorder(pPr: OoxmlNode): boolean {\n const pBdr = findChild(pPr, \"w:pBdr\");\n if (!pBdr) return false;\n return !!findChild(pBdr, \"w:left\");\n}\n\nfunction hasCodeBlockShading(pPr: OoxmlNode): boolean {\n const shd = findChild(pPr, \"w:shd\");\n if (!shd) return false;\n const fill = attr(shd, \"w:fill\");\n if (!fill) return false;\n return fill.toLowerCase() === \"f1f5f9\";\n}\n\n/** Whitelist the block types this module produces (used by callers for typing). */\nexport type ParagraphBlockType = Exclude<BlockType, \"table\">;\n/** Whitelist the align values this module produces (used by callers for typing). */\nexport type ParagraphAlign = AlignValue;\n","import type {\n BlockAttrs,\n CellAttrs,\n DeltaOp,\n SerializedBlock,\n SerializedCell,\n TableBorderPreset,\n} from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { docxHexToCssColor, parseIntAttr } from \"./units\";\n\n/**\n * Map a `<w:tbl>` element to a SerializedBlock(table). The output `cells` array\n * is row-major with `rows * cols` entries — covered slots are filled with\n * `{attrs: {covered: true}}` placeholders so it matches the invariant required\n * by `@sofereditor/core`'s `EditorDocument`.\n */\nexport function tableToBlock(tbl: OoxmlNode, ctx: RunContext): SerializedBlock {\n const tblGrid = findChild(tbl, \"w:tblGrid\");\n const gridCols = tblGrid ? findChildren(tblGrid, \"w:gridCol\") : [];\n const trs = findChildren(tbl, \"w:tr\");\n\n // The docx writer (and Word itself) may emit fewer `<w:gridCol>` entries than\n // the table's logical column count when a row has merged cells. Take the max\n // of (declared gridCols, widest row's total gridSpan) to stay robust.\n let cols = gridCols.length;\n for (const tr of trs) {\n const tcs = findChildren(tr, \"w:tc\");\n let count = 0;\n for (const tc of tcs) count += gridSpanOf(tc);\n if (count > cols) cols = count;\n }\n if (cols < 1) cols = 1;\n const rows = trs.length;\n\n // Build a row-major grid of slots. Each slot is either a real cell or marked\n // as covered. We track which (row, col) is the anchor for vMerge groups.\n const slots: Array<SerializedCell | null> = new Array(rows * cols).fill(null);\n // Anchor of the active vMerge per column index; resets when a non-merge cell\n // is placed in the same column.\n const vMergeAnchorByCol = new Map<number, { row: number; col: number }>();\n\n for (let r = 0; r < rows; r++) {\n const tr = trs[r];\n let cursor = 0;\n for (const tc of findChildren(tr, \"w:tc\")) {\n // Advance the cursor past already-occupied (covered-from-above) slots.\n while (cursor < cols && slots[r * cols + cursor] !== null) cursor++;\n if (cursor >= cols) break;\n\n const tcPr = findChild(tc, \"w:tcPr\");\n const gridSpan = gridSpanOf(tc);\n const vMergeNode = tcPr ? findChild(tcPr, \"w:vMerge\") : undefined;\n const vMergeVal = vMergeNode ? (attr(vMergeNode, \"w:val\") ?? \"continue\") : null;\n\n if (vMergeNode && vMergeVal !== \"restart\") {\n // Continuation of a vertical merge. Cover this row, and bump the anchor's\n // rowspan if we can find it.\n const anchor = vMergeAnchorByCol.get(cursor);\n if (anchor) {\n const anchorIdx = anchor.row * cols + anchor.col;\n const a = slots[anchorIdx];\n if (a) {\n a.attrs = { ...(a.attrs ?? {}), rowspan: (a.attrs?.rowspan ?? 1) + 1 };\n }\n }\n for (let c = 0; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n cursor += gridSpan;\n continue;\n }\n\n // Real cell: build its delta + attrs.\n const delta = cellChildrenToDelta(tc, ctx);\n const text = textOfDelta(delta);\n const cellAttrs: CellAttrs = {};\n if (gridSpan > 1) cellAttrs.colspan = gridSpan;\n // rowspan starts at 1 — vMerge continuations bump it.\n\n const realCell: SerializedCell = { text, delta, attrs: cellAttrs };\n slots[r * cols + cursor] = realCell;\n\n // Mark horizontally-covered slots from gridSpan.\n for (let c = 1; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n\n if (vMergeVal === \"restart\") {\n vMergeAnchorByCol.set(cursor, { row: r, col: cursor });\n } else {\n vMergeAnchorByCol.delete(cursor);\n }\n cursor += gridSpan;\n }\n\n // Fill any trailing un-touched slots in this row with empty placeholders so\n // the row-major invariant holds (length = rows * cols).\n for (let c = 0; c < cols; c++) {\n if (slots[r * cols + c] === null) {\n slots[r * cols + c] = emptyCell();\n }\n }\n }\n\n const cells: SerializedCell[] = slots.map((s) => s ?? emptyCell());\n\n const attrs: BlockAttrs = { rows, cols };\n const preset = readBorderPreset(tbl);\n if (preset) attrs.borderPreset = preset;\n const borderColor = readBorderColor(tbl);\n if (borderColor) attrs.borderColor = borderColor;\n return { type: \"table\", text: \"\", delta: [], attrs, cells };\n}\n\n/**\n * Reduz `w:tblBorders` ao preset mais próximo.\n *\n * O modelo não tem borda por célula, então `w:tcBorders` continua ignorado —\n * deliberadamente. Combinação não mapeável cai em `all`, que é o padrão visual\n * e o comportamento menos surpreendente para quem importa um documento externo.\n *\n * Retorna `undefined` quando a tabela não declara `tblBorders`, para não gravar\n * a chave à toa.\n */\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 // OOXML estrito usa start/end no lugar de left/right.\n const ligado = (...nomes: string[]): boolean =>\n nomes.some((nome) => {\n const n = findChild(b, nome);\n if (!n) return false;\n const val = (attr(n, \"w:val\") ?? \"\").toLowerCase();\n return val !== \"\" && val !== \"none\" && val !== \"nil\";\n });\n\n const top = ligado(\"w:top\");\n const bottom = ligado(\"w:bottom\");\n const left = ligado(\"w:left\", \"w:start\");\n const right = ligado(\"w:right\", \"w:end\");\n const iH = ligado(\"w:insideH\");\n const iV = ligado(\"w:insideV\");\n\n if (top && bottom && left && right && iH && iV) return \"all\";\n if (top && bottom && left && right && !iH && !iV) return \"outer\";\n if (top && bottom && !left && !right && iH && !iV) return \"horizontal\";\n if (!top && !bottom && left && right && !iH && iV) return \"vertical\";\n if (!top && !bottom && !left && !right && !iH && !iV) return \"none\";\n return \"all\";\n}\n\nfunction gridSpanOf(tc: OoxmlNode): number {\n const tcPr = findChild(tc, \"w:tcPr\");\n if (!tcPr) return 1;\n const gs = findChild(tcPr, \"w:gridSpan\");\n if (!gs) return 1;\n const n = parseIntAttr(attr(gs, \"w:val\"), 1);\n return Math.max(1, n);\n}\n\nfunction cellChildrenToDelta(tc: OoxmlNode, ctx: RunContext): DeltaOp[] {\n // A cell can contain multiple paragraphs; we concat their deltas with a\n // newline between adjacent ones to preserve visual separation. The cell\n // model in @sofereditor/core is a single Y.Text, so we flatten.\n const out: DeltaOp[] = [];\n let first = true;\n for (const child of childrenOf(tc)) {\n if (tagOf(child) !== \"w:p\") continue;\n if (!first) out.push({ insert: \"\\n\" });\n first = false;\n out.push(...paragraphChildrenToDelta(childrenOf(child), ctx));\n }\n return out;\n}\n\nfunction textOfDelta(delta: DeltaOp[]): string {\n let out = \"\";\n for (const op of delta) {\n if (typeof op.insert === \"string\") out += op.insert;\n }\n return out;\n}\n\nfunction coveredCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: { covered: true } };\n}\n\nfunction emptyCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: {} };\n}\n","import type { ListKind, ListStyleType } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * Resolves per-paragraph `<w:numPr>` into the editor's `{listKind, listLevel}`.\n *\n * docx numbering is two-step:\n * 1. paragraphs reference a `numId` and an `ilvl`\n * 2. each `<w:num numId=\"...\">` points to a `<w:abstractNum>` via `<w:abstractNumId>`\n * 3. each `<w:lvl ilvl=\"...\">` declares a `<w:numFmt val=\"bullet|decimal|...\">`\n *\n * The export side uses two hardcoded reference names (\"ed-bullet\" / \"ed-ordered\"),\n * but Word-authored documents use arbitrary `numId`s — we resolve by reading the\n * `numFmt` of the target level.\n */\n/** `w:numFmt` → CSS `list-style-type` do editor. Não listados caem em \"decimal\". */\nconst FMT_TO_LIST_STYLE: Record<string, ListStyleType> = {\n decimal: \"decimal\",\n lowerLetter: \"lower-alpha\",\n upperLetter: \"upper-alpha\",\n lowerRoman: \"lower-roman\",\n upperRoman: \"upper-roman\",\n};\n\nexport interface ResolvedNumbering {\n listKind: ListKind;\n listLevel: number;\n /**\n * Número Word desta entrada (contador por instância `numId`+nível, honrando\n * `w:start` e resetando níveis mais profundos). Só para `ordered`. O Word\n * numera por INSTÂNCIA — parágrafos comuns entre itens não resetam a\n * contagem — enquanto o editor numera por adjacência; o importador usa este\n * ordinal para semear `listStart` nos líderes de grupo (ver docx.ts).\n */\n ordinal?: number;\n /** Estilo do nível (`w:numFmt` mapeado). Só para `ordered`. */\n listStyle?: ListStyleType;\n}\n\nexport class NumberingResolver {\n private numIdToAbstract = new Map<string, string>();\n private abstractLevelFmt = new Map<string, Map<number, string>>();\n private abstractLevelStart = new Map<string, Map<number, number>>();\n /** Contadores vivos por instância de lista (numId), indexados por nível. */\n private counters = new Map<string, number[]>();\n\n constructor(numberingXml: OoxmlNode[] | undefined) {\n if (!numberingXml) return;\n for (const top of numberingXml) {\n if (tagOf(top) !== \"w:numbering\") continue;\n for (const child of childrenOf(top)) {\n const tag = tagOf(child);\n if (tag === \"w:abstractNum\") {\n const abstractId = attr(child, \"w:abstractNumId\");\n if (!abstractId) continue;\n const levels = new Map<number, string>();\n const starts = new Map<number, number>();\n for (const lvl of findChildren(child, \"w:lvl\")) {\n const ilvl = parseIntAttr(attr(lvl, \"w:ilvl\"), 0);\n const fmt = attr(findChild(lvl, \"w:numFmt\") ?? {}, \"w:val\");\n if (fmt) levels.set(ilvl, fmt);\n const start = findChild(lvl, \"w:start\");\n if (start) starts.set(ilvl, parseIntAttr(attr(start, \"w:val\"), 1));\n }\n this.abstractLevelFmt.set(abstractId, levels);\n this.abstractLevelStart.set(abstractId, starts);\n } else if (tag === \"w:num\") {\n const numId = attr(child, \"w:numId\");\n const abstractRef = findChild(child, \"w:abstractNumId\");\n const abstractId = abstractRef ? attr(abstractRef, \"w:val\") : undefined;\n if (numId && abstractId) this.numIdToAbstract.set(numId, abstractId);\n }\n }\n }\n }\n\n /**\n * Resolves a paragraph's `<w:numPr>` into `{listKind, listLevel, ordinal?,\n * listStyle?}`, or null. STATEFUL for ordered lists: each call advances the\n * per-`numId` counter (Word instance semantics) — call exactly once per\n * numbered paragraph, in document order.\n */\n resolve(numPr: OoxmlNode | undefined): ResolvedNumbering | null {\n if (!numPr) return null;\n const ilvlRef = findChild(numPr, \"w:ilvl\");\n const numIdRef = findChild(numPr, \"w:numId\");\n if (!numIdRef) return null;\n const numId = attr(numIdRef, \"w:val\");\n if (!numId) return null;\n const listLevel = clampLevel(parseIntAttr(ilvlRef ? attr(ilvlRef, \"w:val\") : undefined, 0));\n const abstractId = this.numIdToAbstract.get(numId);\n if (abstractId == null) {\n // Numbering refs the export side defines without a numbering.xml entry visible\n // to us (the export-docx pipeline generates them dynamically via the docx lib).\n // Fall back by inspecting the numId itself when it is a known sentinel.\n return { listKind: \"bullet\", listLevel };\n }\n const levels = this.abstractLevelFmt.get(abstractId);\n const fmt = levels?.get(listLevel) ?? \"bullet\";\n const fmtLower = fmt.toLowerCase();\n const listKind: ListKind = fmtLower === \"bullet\" || fmtLower === \"none\" ? \"bullet\" : \"ordered\";\n if (listKind === \"bullet\") return { listKind, listLevel };\n\n // Ordinal Word: contador por numId; nível avança, níveis mais profundos resetam.\n const start = this.abstractLevelStart.get(abstractId)?.get(listLevel) ?? 1;\n const c = this.counters.get(numId) ?? [];\n c[listLevel] = c[listLevel] == null ? start : c[listLevel] + 1;\n c.length = listLevel + 1;\n this.counters.set(numId, c);\n\n return {\n listKind,\n listLevel,\n ordinal: c[listLevel],\n listStyle: FMT_TO_LIST_STYLE[fmt] ?? \"decimal\",\n };\n }\n}\n\nfunction clampLevel(n: number): number {\n if (!Number.isFinite(n)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(n)));\n}\n","import { detectPreset, mmToPx, type PageSettings } from \"@sofereditor/core\";\nimport { attr, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * 1 inch = 1440 twips = 25.4 mm. So 1 twip = 25.4/1440 mm.\n *\n * Page size lives in `<w:sectPr><w:pgSz w:w=\"...\" w:h=\"...\"/></w:sectPr>` and\n * margins in `<w:pgMar w:top=\"...\" w:bottom=\"...\" w:left=\"...\" w:right=\"...\"/>`,\n * all in twips. This is the inverse of `sectionPropertiesFor` in\n * `@sofereditor/export-docx`.\n */\nconst MM_PER_TWIP = 25.4 / 1440;\n\nfunction twipsToPx(twips: number): number {\n return mmToPx(twips * MM_PER_TWIP);\n}\n\nexport function sectPrToPageSettings(\n sectPr: OoxmlNode | undefined,\n): PageSettings | undefined {\n if (!sectPr) return undefined;\n const pgSz = findChild(sectPr, \"w:pgSz\");\n const pgMar = findChild(sectPr, \"w:pgMar\");\n if (!pgSz && !pgMar) return undefined;\n\n const widthTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:w\"), 0) : 0;\n const heightTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:h\"), 0) : 0;\n const topTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:top\"), 0) : 0;\n const bottomTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:bottom\"), 0) : 0;\n const leftTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:left\"), 0) : 0;\n const rightTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:right\"), 0) : 0;\n\n if (widthTwips <= 0 && heightTwips <= 0 && topTwips <= 0 && leftTwips <= 0) {\n return undefined;\n }\n\n const width = widthTwips > 0 ? twipsToPx(widthTwips) : 794;\n const height = heightTwips > 0 ? twipsToPx(heightTwips) : 1123;\n const settings: PageSettings = {\n width,\n height,\n marginTop: twipsToPx(topTwips || 0),\n marginBottom: twipsToPx(bottomTwips || 0),\n marginLeft: twipsToPx(leftTwips || 0),\n marginRight: twipsToPx(rightTwips || 0),\n };\n settings.preset = detectPreset(settings.width, settings.height);\n return settings;\n}\n"],"mappings":"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","names":["attrs"]}
1
+ {"version":3,"sources":["../src/docx.ts","../src/parse-xml.ts","../src/units.ts","../src/images.ts","../src/runs.ts","../src/paragraphs.ts","../src/tables.ts","../src/numbering.ts","../src/page-settings.ts"],"sourcesContent":["import { EditorDocument, type SerializedBlock, type SerializedDocument } from \"@sofereditor/core\";\nimport { readDocx, childrenOf, tagOf, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { paragraphToBlock } from \"./paragraphs\";\nimport { tableToBlock } from \"./tables\";\nimport { NumberingResolver } from \"./numbering\";\nimport { sectPrToPageSettings } from \"./page-settings\";\nimport type { RunContext } from \"./runs\";\n\n/**\n * Read a `.docx` (OOXML) Blob/ArrayBuffer/Uint8Array and produce a\n * `SerializedDocument` ready to feed `EditorDocument.fromJSON` (or\n * `loadFromJSON` on an existing instance).\n *\n * The mapping is the inverse of `@sofereditor/export-docx`. Round-trip is exact for\n * documents the editor produced; documents authored externally (Word, Google\n * Docs export) load with marks/lists/tables/images recognized and unsupported\n * features dropped silently.\n */\nexport async function docxBlobToDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<SerializedDocument> {\n const file = await readDocx(input);\n const numbering = new NumberingResolver(file.numberingXml);\n const ctx: RunContext = {\n relationships: file.relationships,\n mediaBytes: file.mediaBytes,\n };\n\n const body = findBody(file.documentXml);\n if (!body) return { blocks: [{ type: \"paragraph\", text: \"\", delta: [], attrs: {} }] };\n\n const blocks: SerializedBlock[] = [];\n for (const child of childrenOf(body)) {\n const tag = tagOf(child);\n if (tag === \"w:p\") {\n blocks.push(paragraphToBlock(child, ctx, numbering));\n } else if (tag === \"w:tbl\") {\n blocks.push(tableToBlock(child, ctx));\n }\n // w:sectPr is parsed below (page settings); other body-level metadata ignored.\n }\n\n if (blocks.length === 0) {\n blocks.push({ type: \"paragraph\", text: \"\", delta: [], attrs: {} });\n }\n\n pruneNonLeaderListStarts(blocks);\n\n const pageSettings = sectPrToPageSettings(findChild(body, \"w:sectPr\"));\n return pageSettings ? { blocks, pageSettings } : { blocks };\n}\n\n/**\n * O Word numera por INSTÂNCIA (`numId`): parágrafos comuns entre itens não\n * resetam a contagem. O editor numera por ADJACÊNCIA: o renderer agrupa\n * `listItem`s consecutivos do mesmo `listKind`/`listStyle`, e um `listStart`\n * em item de CONTINUAÇÃO quebra o grupo ali. O parse gravou o ordinal Word em\n * `listStart` de TODOS os itens ordenados; este pós-passe o mantém apenas nos\n * LÍDERES de grupo (primeiro item, item após bloco comum, ou após item de\n * kind/estilo diferente) — assim o editor renderiza exatamente os números do\n * Word (\"3.\" depois de uma linha de resposta, \"b.\" em subitens) sem fragmentar\n * os grupos.\n */\nexport function pruneNonLeaderListStarts(blocks: SerializedBlock[]): void {\n // Estado do grupo de adjacência corrente: estilo do líder e último ordinal\n // visto por nível (para exigir sequência contígua — duas listas Word\n // independentes coladas, \"1,2,3\" + \"1,2\", NÃO podem fundir num 1..5).\n let grupoAtivo = false;\n let grupoStyle: unknown;\n let ordinais: number[] = [];\n\n const resetGrupo = () => {\n grupoAtivo = false;\n grupoStyle = undefined;\n ordinais = [];\n };\n\n for (const block of blocks) {\n if (block.type !== \"listItem\") {\n resetGrupo();\n continue;\n }\n if ((block.attrs.listKind ?? \"bullet\") !== \"ordered\") {\n // Bullet entre ordenados: mantém o grupo do renderer? Não — kind\n // diferente quebra o grupo lá também.\n resetGrupo();\n continue;\n }\n const level = block.attrs.listLevel ?? 0;\n const ordinal = typeof block.attrs.listStart === \"number\" ? block.attrs.listStart : 1;\n\n const mesmoNivelSequencial = ordinais[level] != null && ordinal === ordinais[level] + 1;\n const entrandoEmNivelNovo = ordinais[level] == null && level > 0 && ordinal === 1;\n const continuaGrupo =\n grupoAtivo &&\n block.attrs.listStyle === grupoStyle &&\n (mesmoNivelSequencial || entrandoEmNivelNovo);\n\n if (continuaGrupo) {\n delete block.attrs.listStart;\n } else {\n // Este item lidera um grupo novo no renderer (listStart preservado).\n grupoAtivo = true;\n grupoStyle = block.attrs.listStyle;\n ordinais = [];\n }\n ordinais[level] = ordinal;\n ordinais.length = level + 1;\n }\n}\n\n/** Convenience: parse → instantiate a fresh `EditorDocument`. */\nexport async function docxBlobToEditorDocument(\n input: Blob | ArrayBuffer | Uint8Array,\n): Promise<EditorDocument> {\n const serialized = await docxBlobToDocument(input);\n return EditorDocument.fromJSON(serialized);\n}\n\nfunction findBody(documentXml: OoxmlNode[]): OoxmlNode | undefined {\n for (const top of documentXml) {\n if (tagOf(top) === \"w:document\") {\n const body = findChild(top, \"w:body\");\n if (body) return body;\n }\n }\n return undefined;\n}\n","import JSZip from \"jszip\";\nimport { XMLParser } from \"fast-xml-parser\";\n\n/**\n * fast-xml-parser with `preserveOrder: true` produces a list of single-key\n * objects per element. Each element is either:\n *\n * - A tag node: `{ \"<tagName>\": <children-array>, \":@\"?: <attrs> }`\n * - A text node: `{ \"#text\": <string> }`\n *\n * Attributes are prefixed with `@_`. The `:@` key holds attributes for the\n * surrounding tag. Children preserve original document order, which matters\n * for paragraph runs (bold word followed by italic word).\n */\nexport type OoxmlNode = Record<string, unknown>;\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n preserveOrder: true,\n trimValues: false, // keep whitespace inside <w:t>\n parseAttributeValue: false,\n parseTagValue: false,\n});\n\nexport interface DocxFile {\n documentXml: OoxmlNode[];\n numberingXml?: OoxmlNode[];\n /** rId → target path (inside the zip), e.g. \"media/image1.png\". */\n relationships: Map<string, string>;\n /** zip path (without leading \"word/\") → bytes for embedded media. */\n mediaBytes: Map<string, Uint8Array>;\n}\n\nexport async function readDocx(input: Blob | ArrayBuffer | Uint8Array): Promise<DocxFile> {\n const data =\n input instanceof Uint8Array\n ? input\n : input instanceof ArrayBuffer\n ? new Uint8Array(input)\n : new Uint8Array(await (input as Blob).arrayBuffer());\n\n const zip = await JSZip.loadAsync(data);\n const documentXml = await readXml(zip, \"word/document.xml\");\n if (!documentXml) throw new Error(\"Invalid .docx: missing word/document.xml\");\n\n const numberingXml = await readXml(zip, \"word/numbering.xml\");\n const relsXml = await readXml(zip, \"word/_rels/document.xml.rels\");\n const relationships = parseRelationships(relsXml);\n\n const mediaBytes = new Map<string, Uint8Array>();\n const mediaFolder = zip.folder(\"word/media\");\n if (mediaFolder) {\n const promises: Promise<void>[] = [];\n mediaFolder.forEach((relativePath, file) => {\n if (file.dir) return;\n promises.push(\n file.async(\"uint8array\").then((bytes) => {\n mediaBytes.set(`media/${relativePath}`, bytes);\n }),\n );\n });\n await Promise.all(promises);\n }\n\n return { documentXml, numberingXml, relationships, mediaBytes };\n}\n\nasync function readXml(zip: JSZip, path: string): Promise<OoxmlNode[] | undefined> {\n const entry = zip.file(path);\n if (!entry) return undefined;\n const text = await entry.async(\"string\");\n return parser.parse(text) as OoxmlNode[];\n}\n\nfunction parseRelationships(nodes: OoxmlNode[] | undefined): Map<string, string> {\n const out = new Map<string, string>();\n if (!nodes) return out;\n // <Relationships><Relationship Id=\"rId1\" Type=\"...\" Target=\"media/image1.png\"/>...\n for (const top of nodes) {\n const children = top[\"Relationships\"];\n if (!Array.isArray(children)) continue;\n for (const child of children as OoxmlNode[]) {\n if (!(\"Relationship\" in child)) continue;\n const attrs = (child[\":@\"] ?? {}) as Record<string, unknown>;\n const id = attrs[\"@_Id\"];\n const target = attrs[\"@_Target\"];\n if (typeof id === \"string\" && typeof target === \"string\") {\n out.set(id, target);\n }\n }\n }\n return out;\n}\n\n// ---------- generic walkers over preserveOrder shape ----------\n\n/** Returns the tag name (e.g. \"w:p\") of a preserveOrder element, or null for text. */\nexport function tagOf(node: OoxmlNode): string | null {\n for (const key of Object.keys(node)) {\n if (key === \":@\") continue;\n return key;\n }\n return null;\n}\n\n/**\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/** docx half-points → CSS pt string. 24 (half-pt) → \"12pt\". */\nexport function halfPointsToCssPt(halfPoints: number): string | undefined {\n if (!Number.isFinite(halfPoints) || halfPoints <= 0) return undefined;\n const pt = halfPoints / 2;\n return `${stripTrailingZeros(pt)}pt`;\n}\n\n/** OOXML RRGGBB (no '#') → CSS hex with leading '#'. */\nexport function docxHexToCssColor(hex: string | undefined): string | undefined {\n if (!hex) return undefined;\n const m = /^[0-9a-f]{6}$/i.exec(hex.trim());\n if (!m) return undefined;\n return `#${hex.toLowerCase()}`;\n}\n\n/** OOXML alignment value → editor AlignValue. */\nexport function parseAlign(val: string | undefined): \"left\" | \"center\" | \"right\" | \"justify\" | undefined {\n if (!val) return undefined;\n switch (val.toLowerCase()) {\n case \"left\":\n case \"start\":\n return \"left\";\n case \"center\":\n return \"center\";\n case \"right\":\n case \"end\":\n return \"right\";\n case \"both\":\n case \"distribute\":\n case \"justify\":\n return \"justify\";\n default:\n return undefined;\n }\n}\n\n/** Parse XML attribute that should be a positive integer. */\nexport function parseIntAttr(val: unknown, fallback = 0): number {\n if (typeof val === \"number\" && Number.isFinite(val)) return Math.trunc(val);\n if (typeof val !== \"string\") return fallback;\n const n = Number.parseInt(val, 10);\n return Number.isFinite(n) ? n : fallback;\n}\n\nfunction stripTrailingZeros(n: number): string {\n // 12 → \"12\", 12.5 → \"12.5\"\n return Number.isInteger(n) ? n.toString() : n.toFixed(2).replace(/\\.?0+$/, \"\");\n}\n","import type { ImageEmbed } from \"@sofereditor/core\";\nimport { attr, childrenOf, tagOf, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/** 1 inch = 914400 EMU = 96 px → 1 px = 9525 EMU. */\nconst EMU_PER_PX = 9525;\n\nconst MIME_BY_EXT: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n bmp: \"image/bmp\",\n svg: \"image/svg+xml\",\n};\n\nexport interface ImageContext {\n relationships: Map<string, string>;\n mediaBytes: Map<string, Uint8Array>;\n}\n\n/**\n * Walk a `<w:drawing>` node and return an `ImageEmbed` if it contains a\n * recognized inline picture. Returns null for unsupported drawings (shapes,\n * charts, etc.).\n */\nexport function extractImageFromDrawing(\n drawing: OoxmlNode,\n ctx: ImageContext,\n): ImageEmbed | null {\n // The interesting bits live deep inside: a:blip carries `r:embed`, and\n // wp:extent carries cx/cy. We do a generic walk because the wrapping nodes\n // differ between inline (`wp:inline`) and anchored (`wp:anchor`) drawings.\n let embedId: string | undefined;\n let cx: number | undefined;\n let cy: number | undefined;\n\n walk(drawing, (n) => {\n const tag = tagOf(n);\n if (!tag) return;\n if (tag === \"a:blip\") {\n const id = attr(n, \"r:embed\") ?? attr(n, \"r:link\");\n if (id) embedId = id;\n } else if (tag === \"wp:extent\") {\n cx = parseIntAttr(attr(n, \"cx\"));\n cy = parseIntAttr(attr(n, \"cy\"));\n }\n });\n\n if (!embedId) return null;\n const target = ctx.relationships.get(embedId);\n if (!target) return null;\n // Relationship targets are typically \"media/image1.png\"; the zip lookup uses\n // the same path (we stored bytes keyed under \"media/<file>\").\n const bytes = ctx.mediaBytes.get(target);\n if (!bytes) return null;\n\n const ext = extensionOf(target);\n const mime = MIME_BY_EXT[ext] ?? \"image/png\";\n const src = `data:${mime};base64,${bytesToBase64(bytes)}`;\n const widthPx = cx != null ? Math.max(1, Math.round(cx / EMU_PER_PX)) : 100;\n const heightPx = cy != null ? Math.max(1, Math.round(cy / EMU_PER_PX)) : 100;\n\n return { type: \"image\", src, width: widthPx, height: heightPx };\n}\n\nfunction walk(node: OoxmlNode, visit: (n: OoxmlNode) => void): void {\n visit(node);\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) continue;\n walk(child, visit);\n }\n}\n\nfunction extensionOf(path: string): string {\n const m = /\\.([a-z0-9]+)$/i.exec(path);\n return m ? m[1].toLowerCase() : \"\";\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n // Browser path.\n if (typeof btoa === \"function\") {\n let bin = \"\";\n const CHUNK = 0x8000;\n for (let i = 0; i < bytes.length; i += CHUNK) {\n bin += String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + CHUNK)));\n }\n return btoa(bin);\n }\n // Node fallback.\n return Buffer.from(bytes).toString(\"base64\");\n}\n","import type { DeltaOp, MarkAttrs } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { docxHexToCssColor, halfPointsToCssPt } from \"./units\";\nimport { extractImageFromDrawing, type ImageContext } from \"./images\";\n\nexport interface RunContext extends ImageContext {}\n\n/**\n * Walk a paragraph's children (post `<w:pPr>`) and emit a `DeltaOp[]`. Combines\n * adjacent runs that share the same mark set into a single op for parity with\n * the export — Y.Text.applyDelta would merge them anyway, but pre-merging keeps\n * round-trip output stable.\n */\nexport function paragraphChildrenToDelta(children: OoxmlNode[], ctx: RunContext): DeltaOp[] {\n const ops: DeltaOp[] = [];\n for (const child of children) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:pPr\") continue;\n if (tag === \"w:r\") {\n collectRun(child, ctx, undefined, ops);\n } else if (tag === \"w:hyperlink\") {\n // hyperlinks carry an r:id that maps via relationships to the href.\n const rId = attr(child, \"r:id\");\n const href = rId ? ctx.relationships.get(rId) : undefined;\n const linkMark: MarkAttrs | undefined = href ? { link: { href } } : undefined;\n for (const sub of childrenOf(child)) {\n if (tagOf(sub) === \"w:r\") collectRun(sub, ctx, linkMark, ops);\n }\n }\n }\n return mergeAdjacent(ops);\n}\n\nfunction collectRun(\n run: OoxmlNode,\n ctx: RunContext,\n wrappingMarks: MarkAttrs | undefined,\n out: DeltaOp[],\n): void {\n const marks = mergeMarks(wrappingMarks, runMarks(findChild(run, \"w:rPr\")));\n for (const child of childrenOf(run)) {\n const tag = tagOf(child);\n if (!tag) continue;\n if (tag === \"w:t\") {\n const text = textContent(child);\n if (text.length > 0) push(out, text, marks);\n } else if (tag === \"w:tab\") {\n push(out, \"\\t\", marks);\n } else if (tag === \"w:br\") {\n // Treat w:br type=\"page\" as also a soft break for the model — we have no\n // explicit page-break node in the schema; pagination is computed live.\n push(out, \"\\n\", marks);\n } else if (tag === \"w:drawing\") {\n const embed = extractImageFromDrawing(child, ctx);\n if (embed) out.push({ insert: embed });\n }\n // Other run children (w:sym, w:fldChar, etc.) are silently dropped.\n }\n}\n\n/**\n * Os valores nomeados de `w:highlight` do OOXML. O Word só aceita esta lista\n * fechada — documentos com cor arbitrária usam `w:shd`, que é o que o nosso\n * export emite. `none` fica de fora de propósito: ausência = sem marca.\n */\nconst DOCX_HIGHLIGHT_COLORS: Record<string, string> = {\n yellow: \"#ffff00\",\n green: \"#00ff00\",\n cyan: \"#00ffff\",\n magenta: \"#ff00ff\",\n blue: \"#0000ff\",\n red: \"#ff0000\",\n darkblue: \"#000080\",\n darkcyan: \"#008080\",\n darkgreen: \"#008000\",\n darkmagenta: \"#800080\",\n darkred: \"#800000\",\n darkyellow: \"#808000\",\n darkgray: \"#808080\",\n lightgray: \"#c0c0c0\",\n black: \"#000000\",\n white: \"#ffffff\",\n};\n\nfunction runMarks(rPr: OoxmlNode | undefined): MarkAttrs {\n const m: MarkAttrs = {};\n if (!rPr) return m;\n for (const child of childrenOf(rPr)) {\n const tag = tagOf(child);\n if (!tag) continue;\n switch (tag) {\n case \"w:b\":\n if (!isFalse(attr(child, \"w:val\"))) m.bold = true;\n break;\n case \"w:i\":\n if (!isFalse(attr(child, \"w:val\"))) m.italic = true;\n break;\n case \"w:u\": {\n const val = attr(child, \"w:val\");\n if (val && val.toLowerCase() !== \"none\") m.underline = true;\n break;\n }\n case \"w:strike\":\n if (!isFalse(attr(child, \"w:val\"))) m.strike = true;\n break;\n case \"w:color\": {\n const hex = attr(child, \"w:val\");\n const css = docxHexToCssColor(hex);\n if (css) m.color = css;\n break;\n }\n case \"w:shd\": {\n // Sombreamento de RUN = marca-texto. `auto` significa \"sem cor\".\n const fill = attr(child, \"w:fill\");\n if (fill && fill.toLowerCase() !== \"auto\") {\n const css = docxHexToCssColor(fill);\n if (css) m.highlight = css;\n }\n break;\n }\n case \"w:highlight\": {\n const css = DOCX_HIGHLIGHT_COLORS[(attr(child, \"w:val\") ?? \"\").toLowerCase()];\n if (css) m.highlight = css;\n break;\n }\n // w:rFonts intentionally ignored: school standard is Arial for all\n // exams. Any font declared in the .docx is dropped on import so the\n // imported doc inherits Arial from `.ed-root` / PDF base stylesheet.\n case \"w:sz\": {\n const halfPts = Number.parseFloat(attr(child, \"w:val\") ?? \"\");\n const css = halfPointsToCssPt(halfPts);\n if (css) m.fontSize = css;\n break;\n }\n }\n }\n return m;\n}\n\nfunction mergeMarks(a: MarkAttrs | undefined, b: MarkAttrs): MarkAttrs {\n if (!a) return b;\n return { ...a, ...b };\n}\n\nfunction textContent(node: OoxmlNode): string {\n let out = \"\";\n for (const child of childrenOf(node)) {\n if (\"#text\" in child) {\n const t = child[\"#text\"];\n if (typeof t === \"string\") out += t;\n }\n }\n return out;\n}\n\nfunction isFalse(val: string | undefined): boolean {\n if (val == null) return false;\n const v = val.toLowerCase();\n return v === \"0\" || v === \"false\" || v === \"none\";\n}\n\nfunction push(out: DeltaOp[], text: string, marks: MarkAttrs): void {\n const op: DeltaOp =\n Object.keys(marks).length === 0 ? { insert: text } : { insert: text, attributes: { ...marks } };\n out.push(op);\n}\n\nfunction mergeAdjacent(ops: DeltaOp[]): DeltaOp[] {\n const out: DeltaOp[] = [];\n for (const op of ops) {\n const prev = out[out.length - 1];\n if (\n prev &&\n typeof prev.insert === \"string\" &&\n typeof op.insert === \"string\" &&\n sameMarks(prev.attributes, op.attributes)\n ) {\n prev.insert = prev.insert + op.insert;\n continue;\n }\n out.push(op);\n }\n return out;\n}\n\nfunction sameMarks(a: MarkAttrs | undefined, b: MarkAttrs | undefined): boolean {\n const ak = a ? Object.keys(a).sort() : [];\n const bk = b ? Object.keys(b).sort() : [];\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i++) {\n if (ak[i] !== bk[i]) return false;\n const av = (a as Record<string, unknown>)[ak[i]];\n const bv = (b as Record<string, unknown>)[bk[i]];\n if (typeof av === \"object\" && av !== null && typeof bv === \"object\" && bv !== null) {\n if (JSON.stringify(av) !== JSON.stringify(bv)) return false;\n } else if (av !== bv) {\n return false;\n }\n }\n return true;\n}\n","import type {\n AlignValue,\n AnswerLineSpacing,\n BlockAttrs,\n BlockType,\n DeltaOp,\n SerializedBlock,\n} from \"@sofereditor/core\";\nimport {\n attr,\n 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 SerializedBlock,\n SerializedCell,\n TableBorderPreset,\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 { paragraphChildrenToDelta, type RunContext } from \"./runs\";\nimport { docxHexToCssColor, parseIntAttr } from \"./units\";\n\n/**\n * Map a `<w:tbl>` element to a SerializedBlock(table). The output `cells` array\n * is row-major with `rows * cols` entries — covered slots are filled with\n * `{attrs: {covered: true}}` placeholders so it matches the invariant required\n * by `@sofereditor/core`'s `EditorDocument`.\n */\nexport function tableToBlock(tbl: OoxmlNode, ctx: RunContext): SerializedBlock {\n const tblGrid = findChild(tbl, \"w:tblGrid\");\n const gridCols = tblGrid ? findChildren(tblGrid, \"w:gridCol\") : [];\n const trs = findChildren(tbl, \"w:tr\");\n\n // The docx writer (and Word itself) may emit fewer `<w:gridCol>` entries than\n // the table's logical column count when a row has merged cells. Take the max\n // of (declared gridCols, widest row's total gridSpan) to stay robust.\n let cols = gridCols.length;\n for (const tr of trs) {\n const tcs = findChildren(tr, \"w:tc\");\n let count = 0;\n for (const tc of tcs) count += gridSpanOf(tc);\n if (count > cols) cols = count;\n }\n if (cols < 1) cols = 1;\n const rows = trs.length;\n\n // Build a row-major grid of slots. Each slot is either a real cell or marked\n // as covered. We track which (row, col) is the anchor for vMerge groups.\n const slots: Array<SerializedCell | null> = new Array(rows * cols).fill(null);\n // Anchor of the active vMerge per column index; resets when a non-merge cell\n // is placed in the same column.\n const vMergeAnchorByCol = new Map<number, { row: number; col: number }>();\n\n for (let r = 0; r < rows; r++) {\n const tr = trs[r];\n let cursor = 0;\n for (const tc of findChildren(tr, \"w:tc\")) {\n // Advance the cursor past already-occupied (covered-from-above) slots.\n while (cursor < cols && slots[r * cols + cursor] !== null) cursor++;\n if (cursor >= cols) break;\n\n const tcPr = findChild(tc, \"w:tcPr\");\n const gridSpan = gridSpanOf(tc);\n const vMergeNode = tcPr ? findChild(tcPr, \"w:vMerge\") : undefined;\n const vMergeVal = vMergeNode ? (attr(vMergeNode, \"w:val\") ?? \"continue\") : null;\n\n if (vMergeNode && vMergeVal !== \"restart\") {\n // Continuation of a vertical merge. Cover this row, and bump the anchor's\n // rowspan if we can find it.\n const anchor = vMergeAnchorByCol.get(cursor);\n if (anchor) {\n const anchorIdx = anchor.row * cols + anchor.col;\n const a = slots[anchorIdx];\n if (a) {\n a.attrs = { ...(a.attrs ?? {}), rowspan: (a.attrs?.rowspan ?? 1) + 1 };\n }\n }\n for (let c = 0; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n cursor += gridSpan;\n continue;\n }\n\n // Real cell: build its delta + attrs.\n const delta = cellChildrenToDelta(tc, ctx);\n const text = textOfDelta(delta);\n const cellAttrs: CellAttrs = {};\n if (gridSpan > 1) cellAttrs.colspan = gridSpan;\n // rowspan starts at 1 — vMerge continuations bump it.\n\n const realCell: SerializedCell = { text, delta, attrs: cellAttrs };\n slots[r * cols + cursor] = realCell;\n\n // Mark horizontally-covered slots from gridSpan.\n for (let c = 1; c < gridSpan && cursor + c < cols; c++) {\n slots[r * cols + cursor + c] = coveredCell();\n }\n\n if (vMergeVal === \"restart\") {\n vMergeAnchorByCol.set(cursor, { row: r, col: cursor });\n } else {\n vMergeAnchorByCol.delete(cursor);\n }\n cursor += gridSpan;\n }\n\n // Fill any trailing un-touched slots in this row with empty placeholders so\n // the row-major invariant holds (length = rows * cols).\n for (let c = 0; c < cols; c++) {\n if (slots[r * cols + c] === null) {\n slots[r * cols + c] = emptyCell();\n }\n }\n }\n\n const cells: SerializedCell[] = slots.map((s) => s ?? emptyCell());\n\n const attrs: BlockAttrs = { rows, cols };\n const preset = readBorderPreset(tbl);\n if (preset) attrs.borderPreset = preset;\n const borderColor = readBorderColor(tbl);\n if (borderColor) attrs.borderColor = borderColor;\n return { type: \"table\", text: \"\", delta: [], attrs, cells };\n}\n\n/**\n * Reduz `w:tblBorders` ao preset mais próximo.\n *\n * O modelo não tem borda por célula, então `w:tcBorders` continua ignorado —\n * deliberadamente. Combinação não mapeável cai em `all`, que é o padrão visual\n * e o comportamento menos surpreendente para quem importa um documento externo.\n *\n * Retorna `undefined` quando a tabela não declara `tblBorders`, para não gravar\n * a chave à toa.\n */\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\nfunction cellChildrenToDelta(tc: OoxmlNode, ctx: RunContext): DeltaOp[] {\n // A cell can contain multiple paragraphs; we concat their deltas with a\n // newline between adjacent ones to preserve visual separation. The cell\n // model in @sofereditor/core is a single Y.Text, so we flatten.\n const out: DeltaOp[] = [];\n let first = true;\n for (const child of childrenOf(tc)) {\n if (tagOf(child) !== \"w:p\") continue;\n if (!first) out.push({ insert: \"\\n\" });\n first = false;\n out.push(...paragraphChildrenToDelta(childrenOf(child), ctx));\n }\n return out;\n}\n\nfunction textOfDelta(delta: DeltaOp[]): string {\n let out = \"\";\n for (const op of delta) {\n if (typeof op.insert === \"string\") out += op.insert;\n }\n return out;\n}\n\nfunction coveredCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: { covered: true } };\n}\n\nfunction emptyCell(): SerializedCell {\n return { text: \"\", delta: [], attrs: {} };\n}\n","import type { ListKind, ListStyleType } from \"@sofereditor/core\";\nimport {\n attr,\n childrenOf,\n findChild,\n findChildren,\n tagOf,\n type OoxmlNode,\n} from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * Resolves per-paragraph `<w:numPr>` into the editor's `{listKind, listLevel}`.\n *\n * docx numbering is two-step:\n * 1. paragraphs reference a `numId` and an `ilvl`\n * 2. each `<w:num numId=\"...\">` points to a `<w:abstractNum>` via `<w:abstractNumId>`\n * 3. each `<w:lvl ilvl=\"...\">` declares a `<w:numFmt val=\"bullet|decimal|...\">`\n *\n * The export side uses two hardcoded reference names (\"ed-bullet\" / \"ed-ordered\"),\n * but Word-authored documents use arbitrary `numId`s — we resolve by reading the\n * `numFmt` of the target level.\n */\n/** `w:numFmt` → CSS `list-style-type` do editor. Não listados caem em \"decimal\". */\nconst FMT_TO_LIST_STYLE: Record<string, ListStyleType> = {\n decimal: \"decimal\",\n lowerLetter: \"lower-alpha\",\n upperLetter: \"upper-alpha\",\n lowerRoman: \"lower-roman\",\n upperRoman: \"upper-roman\",\n};\n\nexport interface ResolvedNumbering {\n listKind: ListKind;\n listLevel: number;\n /**\n * Número Word desta entrada (contador por instância `numId`+nível, honrando\n * `w:start` e resetando níveis mais profundos). Só para `ordered`. O Word\n * numera por INSTÂNCIA — parágrafos comuns entre itens não resetam a\n * contagem — enquanto o editor numera por adjacência; o importador usa este\n * ordinal para semear `listStart` nos líderes de grupo (ver docx.ts).\n */\n ordinal?: number;\n /** Estilo do nível (`w:numFmt` mapeado). Só para `ordered`. */\n listStyle?: ListStyleType;\n}\n\nexport class NumberingResolver {\n private numIdToAbstract = new Map<string, string>();\n private abstractLevelFmt = new Map<string, Map<number, string>>();\n private abstractLevelStart = new Map<string, Map<number, number>>();\n /** Contadores vivos por instância de lista (numId), indexados por nível. */\n private counters = new Map<string, number[]>();\n\n constructor(numberingXml: OoxmlNode[] | undefined) {\n if (!numberingXml) return;\n for (const top of numberingXml) {\n if (tagOf(top) !== \"w:numbering\") continue;\n for (const child of childrenOf(top)) {\n const tag = tagOf(child);\n if (tag === \"w:abstractNum\") {\n const abstractId = attr(child, \"w:abstractNumId\");\n if (!abstractId) continue;\n const levels = new Map<number, string>();\n const starts = new Map<number, number>();\n for (const lvl of findChildren(child, \"w:lvl\")) {\n const ilvl = parseIntAttr(attr(lvl, \"w:ilvl\"), 0);\n const fmt = attr(findChild(lvl, \"w:numFmt\") ?? {}, \"w:val\");\n if (fmt) levels.set(ilvl, fmt);\n const start = findChild(lvl, \"w:start\");\n if (start) starts.set(ilvl, parseIntAttr(attr(start, \"w:val\"), 1));\n }\n this.abstractLevelFmt.set(abstractId, levels);\n this.abstractLevelStart.set(abstractId, starts);\n } else if (tag === \"w:num\") {\n const numId = attr(child, \"w:numId\");\n const abstractRef = findChild(child, \"w:abstractNumId\");\n const abstractId = abstractRef ? attr(abstractRef, \"w:val\") : undefined;\n if (numId && abstractId) this.numIdToAbstract.set(numId, abstractId);\n }\n }\n }\n }\n\n /**\n * Resolves a paragraph's `<w:numPr>` into `{listKind, listLevel, ordinal?,\n * listStyle?}`, or null. STATEFUL for ordered lists: each call advances the\n * per-`numId` counter (Word instance semantics) — call exactly once per\n * numbered paragraph, in document order.\n */\n resolve(numPr: OoxmlNode | undefined): ResolvedNumbering | null {\n if (!numPr) return null;\n const ilvlRef = findChild(numPr, \"w:ilvl\");\n const numIdRef = findChild(numPr, \"w:numId\");\n if (!numIdRef) return null;\n const numId = attr(numIdRef, \"w:val\");\n if (!numId) return null;\n const listLevel = clampLevel(parseIntAttr(ilvlRef ? attr(ilvlRef, \"w:val\") : undefined, 0));\n const abstractId = this.numIdToAbstract.get(numId);\n if (abstractId == null) {\n // Numbering refs the export side defines without a numbering.xml entry visible\n // to us (the export-docx pipeline generates them dynamically via the docx lib).\n // Fall back by inspecting the numId itself when it is a known sentinel.\n return { listKind: \"bullet\", listLevel };\n }\n const levels = this.abstractLevelFmt.get(abstractId);\n const fmt = levels?.get(listLevel) ?? \"bullet\";\n const fmtLower = fmt.toLowerCase();\n const listKind: ListKind = fmtLower === \"bullet\" || fmtLower === \"none\" ? \"bullet\" : \"ordered\";\n if (listKind === \"bullet\") return { listKind, listLevel };\n\n // Ordinal Word: contador por numId; nível avança, níveis mais profundos resetam.\n const start = this.abstractLevelStart.get(abstractId)?.get(listLevel) ?? 1;\n const c = this.counters.get(numId) ?? [];\n c[listLevel] = c[listLevel] == null ? start : c[listLevel] + 1;\n c.length = listLevel + 1;\n this.counters.set(numId, c);\n\n return {\n listKind,\n listLevel,\n ordinal: c[listLevel],\n listStyle: FMT_TO_LIST_STYLE[fmt] ?? \"decimal\",\n };\n }\n}\n\nfunction clampLevel(n: number): number {\n if (!Number.isFinite(n)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(n)));\n}\n","import { detectPreset, mmToPx, type PageSettings } from \"@sofereditor/core\";\nimport { attr, findChild, type OoxmlNode } from \"./parse-xml\";\nimport { parseIntAttr } from \"./units\";\n\n/**\n * 1 inch = 1440 twips = 25.4 mm. So 1 twip = 25.4/1440 mm.\n *\n * Page size lives in `<w:sectPr><w:pgSz w:w=\"...\" w:h=\"...\"/></w:sectPr>` and\n * margins in `<w:pgMar w:top=\"...\" w:bottom=\"...\" w:left=\"...\" w:right=\"...\"/>`,\n * all in twips. This is the inverse of `sectionPropertiesFor` in\n * `@sofereditor/export-docx`.\n */\nconst MM_PER_TWIP = 25.4 / 1440;\n\nfunction twipsToPx(twips: number): number {\n return mmToPx(twips * MM_PER_TWIP);\n}\n\nexport function sectPrToPageSettings(\n sectPr: OoxmlNode | undefined,\n): PageSettings | undefined {\n if (!sectPr) return undefined;\n const pgSz = findChild(sectPr, \"w:pgSz\");\n const pgMar = findChild(sectPr, \"w:pgMar\");\n if (!pgSz && !pgMar) return undefined;\n\n const widthTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:w\"), 0) : 0;\n const heightTwips = pgSz ? parseIntAttr(attr(pgSz, \"w:h\"), 0) : 0;\n const topTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:top\"), 0) : 0;\n const bottomTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:bottom\"), 0) : 0;\n const leftTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:left\"), 0) : 0;\n const rightTwips = pgMar ? parseIntAttr(attr(pgMar, \"w:right\"), 0) : 0;\n\n if (widthTwips <= 0 && heightTwips <= 0 && topTwips <= 0 && leftTwips <= 0) {\n return undefined;\n }\n\n const width = widthTwips > 0 ? twipsToPx(widthTwips) : 794;\n const height = heightTwips > 0 ? twipsToPx(heightTwips) : 1123;\n const settings: PageSettings = {\n width,\n height,\n marginTop: twipsToPx(topTwips || 0),\n marginBottom: twipsToPx(bottomTwips || 0),\n marginLeft: twipsToPx(leftTwips || 0),\n marginRight: twipsToPx(rightTwips || 0),\n };\n settings.preset = detectPreset(settings.width, settings.height);\n return settings;\n}\n"],"mappings":"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","names":["attrs"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sofereditor/import-docx",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "@sofereditor/import-docx — Parse .docx (OOXML) in browser or Node into a SerializedDocument loadable by the Sofer editor.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "author": "Rafael Marreca",
@@ -54,12 +54,12 @@
54
54
  "test": "vitest run"
55
55
  },
56
56
  "dependencies": {
57
- "@sofereditor/core": "^0.5.0",
57
+ "@sofereditor/core": "^0.6.0",
58
58
  "fast-xml-parser": "^4.5.0",
59
59
  "jszip": "^3.10.1"
60
60
  },
61
61
  "devDependencies": {
62
- "@sofereditor/export-docx": "^0.5.0",
62
+ "@sofereditor/export-docx": "^0.6.0",
63
63
  "tsup": "^8.3.5",
64
64
  "typescript": "^5.7.2",
65
65
  "vitest": "^2.1.8"