@sofereditor/export-docx 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -133,9 +133,20 @@ function makeParagraph(block, images) {
133
133
  return new import_docx.Paragraph({
134
134
  alignment: alignFor(block.attrs.align),
135
135
  bidirectional: block.attrs.dir === "rtl" ? true : void 0,
136
+ ...answerLineProps(block.attrs),
136
137
  children: deltaToRuns(block.delta, ARIAL, images)
137
138
  });
138
139
  }
140
+ function answerLineProps(attrs) {
141
+ if (attrs.answerLine !== true) return {};
142
+ const spacing = attrs.answerLineSpacing ?? 1;
143
+ return {
144
+ border: {
145
+ bottom: { style: import_docx.BorderStyle.SINGLE, size: 6, color: "000000", space: 1 }
146
+ },
147
+ spacing: { line: Math.round(240 * spacing), lineRule: import_docx.LineRuleType.AUTO }
148
+ };
149
+ }
139
150
  function makeHeading(block, images) {
140
151
  const level = clampHeadingLevel(block.attrs.level);
141
152
  return new import_docx.Paragraph({
@@ -195,6 +206,7 @@ function makeTable(block, images) {
195
206
  const columnWidths = isValidColWidths ? rawColWidths.map((px) => (0, import_docx.convertMillimetersToTwip)((0, import_core.pxToMm)(px))) : void 0;
196
207
  return new import_docx.Table({
197
208
  rows: rowsOut,
209
+ borders: docxTableBorders(block.attrs.borderPreset),
198
210
  ...columnWidths ? {
199
211
  columnWidths,
200
212
  // Fix the layout so Word honors the column widths verbatim instead
@@ -205,6 +217,27 @@ function makeTable(block, images) {
205
217
  } : { width: { size: 100, type: import_docx.WidthType.PERCENTAGE } }
206
218
  });
207
219
  }
220
+ function docxTableBorders(preset) {
221
+ const on = { style: import_docx.BorderStyle.SINGLE, size: 6, color: "CBD5E1" };
222
+ const off = { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" };
223
+ const s = (visivel) => visivel ? on : off;
224
+ const TRUTH = {
225
+ all: [true, true, true, true],
226
+ outer: [true, true, false, false],
227
+ horizontal: [true, false, true, false],
228
+ vertical: [false, true, false, true],
229
+ none: [false, false, false, false]
230
+ };
231
+ const [tb, lr, iH, iV] = TRUTH[preset ?? "all"];
232
+ return {
233
+ top: s(tb),
234
+ bottom: s(tb),
235
+ left: s(lr),
236
+ right: s(lr),
237
+ insideHorizontal: s(iH),
238
+ insideVertical: s(iV)
239
+ };
240
+ }
208
241
  function makeCell(cell, images) {
209
242
  const span = cell.attrs?.colspan && cell.attrs.colspan > 1 ? cell.attrs.colspan : 1;
210
243
  const rowSpan = cell.attrs?.rowspan && cell.attrs.rowspan > 1 ? cell.attrs.rowspan : 1;
@@ -252,32 +285,27 @@ function deltaToRuns(delta, defaults = {}, images) {
252
285
  if (out.length === 0) out.push(new import_docx.TextRun(""));
253
286
  return out;
254
287
  }
255
- function makeTextRun(text, marks, defaults) {
256
- const m = marks ?? {};
257
- const fontSize = parseFontSizeToHalfPoints(m.fontSize);
258
- const segments = text.split("\n");
259
- if (segments.length === 1) {
260
- return new import_docx.TextRun({
261
- text,
262
- bold: m.bold || defaults.bold,
263
- italics: m.italic || defaults.italics,
264
- underline: m.underline ? { type: import_docx.UnderlineType.SINGLE } : void 0,
265
- strike: m.strike,
266
- color: cssColorToDocxHex(m.color),
267
- font: m.fontFamily ?? defaults.font,
268
- size: fontSize ?? defaults.size
269
- });
270
- }
271
- return new import_docx.TextRun({
272
- text: text.replace(/\n/g, " "),
288
+ function textRunProps(text, m, defaults) {
289
+ const fill = cssColorToDocxHex(m.highlight);
290
+ return {
291
+ text,
273
292
  bold: m.bold || defaults.bold,
274
293
  italics: m.italic || defaults.italics,
275
294
  underline: m.underline ? { type: import_docx.UnderlineType.SINGLE } : void 0,
276
295
  strike: m.strike,
277
296
  color: cssColorToDocxHex(m.color),
278
297
  font: m.fontFamily ?? defaults.font,
279
- size: fontSize ?? defaults.size
280
- });
298
+ size: parseFontSizeToHalfPoints(m.fontSize) ?? defaults.size,
299
+ shading: fill ? { type: import_docx.ShadingType.CLEAR, color: "auto", fill } : void 0
300
+ };
301
+ }
302
+ function makeTextRun(text, marks, defaults) {
303
+ const m = marks ?? {};
304
+ const segments = text.split("\n");
305
+ if (segments.length === 1) {
306
+ return new import_docx.TextRun(textRunProps(text, m, defaults));
307
+ }
308
+ return new import_docx.TextRun(textRunProps(text.replace(/\n/g, " "), m, defaults));
281
309
  }
282
310
  function makeImageRun(embed, images) {
283
311
  const resolved = images.get(embed.src);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/docx.ts"],"sourcesContent":["export { documentToDocxBlob, documentToDocxBuffer } from \"./docx\";\nexport type { DocumentToDocxOptions, ResolvedImage } from \"./docx\";\n","import type {\n AlignValue,\n DeltaOp,\n ImageEmbed,\n LegacySerializedDocument,\n ListKind,\n MarkAttrs,\n SerializedBlock,\n SerializedCell,\n SerializedDocument,\n} from \"@sofereditor/core\";\nimport {\n DEFAULT_PAGE_SETTINGS,\n isImageEmbed,\n isLegacySerializedDocument,\n pxToMm,\n type PageSettings,\n} from \"@sofereditor/core\";\nimport {\n AlignmentType,\n BorderStyle,\n Document,\n HeadingLevel,\n ImageRun,\n LevelFormat,\n Packer,\n Paragraph,\n PageOrientation,\n ShadingType,\n Table,\n TableCell,\n TableLayoutType,\n TableRow,\n TextRun,\n UnderlineType,\n VerticalAlign,\n WidthType,\n convertMillimetersToTwip,\n} from \"docx\";\n\nconst BULLET_REF = \"ed-bullet\";\nconst ORDERED_REF = \"ed-ordered\";\n\nexport interface ResolvedImage {\n data: Uint8Array;\n type: \"png\" | \"jpg\" | \"gif\" | \"bmp\" | \"svg\";\n}\n\nexport interface DocumentToDocxOptions {\n /** Document title; embedded in core properties. */\n title?: string;\n /** Author for core properties. */\n creator?: string;\n /**\n * Resolve um `src` de imagem para bytes. Default: data URLs (decodificadas\n * localmente) e http(s) via fetch. Retornar null pula a imagem (contada em\n * `skippedImages`) sem abortar o export.\n */\n resolveImage?: (src: string) => Promise<ResolvedImage | null>;\n}\n\n/**\n * Build a docx `Document` from a `SerializedDocument`. Output is a Blob you can\n * download with `downloadBlob` from `@sofereditor/export-pdf`.\n *\n * Mappings:\n * - paragraph / heading / blockquote / codeBlock → Paragraph (with `heading`,\n * shading, monospace font for code)\n * - listItem → Paragraph with `numbering: { reference, level }`\n * - table → docx `Table`. `rowspan`/`colspan` map to TableCell's `rowSpan`/\n * `columnSpan`; \"covered\" cells are skipped (docx infers cover from spans)\n * - inline marks → TextRun props (bold, italics, underline, strike, color,\n * font, size in half-points)\n * - image embeds → ImageRun with bytes resolved via `resolveImage` (default:\n * data URLs decoded locally, http(s) fetched; unresolved images are\n * skipped and counted in `skippedImages` instead of aborting the export)\n */\nexport async function documentToDocxBlob(\n doc: SerializedDocument | LegacySerializedDocument,\n options: DocumentToDocxOptions = {},\n): Promise<{ blob: Blob; skippedImages: number }> {\n const normalized = normalize(doc);\n const { images, skipped } = await resolveAllImages(\n normalized,\n options.resolveImage ?? defaultResolveImage,\n );\n const built = buildDocument(normalized, options, images);\n return { blob: await Packer.toBlob(built), skippedImages: skipped };\n}\n\n/** Same as `documentToDocxBlob` but returns a Uint8Array (useful in Node/tests). */\nexport async function documentToDocxBuffer(\n doc: SerializedDocument | LegacySerializedDocument,\n options: DocumentToDocxOptions = {},\n): Promise<{ buffer: Uint8Array; skippedImages: number }> {\n const normalized = normalize(doc);\n const { images, skipped } = await resolveAllImages(\n normalized,\n options.resolveImage ?? defaultResolveImage,\n );\n const built = buildDocument(normalized, options, images);\n const buf = await Packer.toBuffer(built);\n // `Packer.toBuffer` returns a Node Buffer at runtime — coerce for consumers.\n return {\n buffer: buf instanceof Uint8Array ? buf : new Uint8Array(buf),\n skippedImages: skipped,\n };\n}\n\nfunction normalize(doc: SerializedDocument | LegacySerializedDocument): SerializedDocument {\n return isLegacySerializedDocument(doc) ? { blocks: doc } : doc;\n}\n\nfunction buildDocument(\n doc: SerializedDocument,\n options: DocumentToDocxOptions,\n images: Map<string, ResolvedImage | null>,\n): Document {\n const children = blocksToDocxChildren(doc, images);\n const settings = doc.pageSettings ?? DEFAULT_PAGE_SETTINGS;\n return new Document({\n creator: options.creator,\n title: options.title,\n numbering: {\n config: [\n {\n reference: BULLET_REF,\n levels: buildBulletLevels(),\n },\n {\n reference: ORDERED_REF,\n levels: buildOrderedLevels(),\n },\n ],\n },\n sections: [\n {\n properties: sectionPropertiesFor(settings),\n children,\n },\n ],\n });\n}\n\nfunction sectionPropertiesFor(settings: PageSettings) {\n return {\n page: {\n size: {\n width: convertMillimetersToTwip(pxToMm(settings.width)),\n height: convertMillimetersToTwip(pxToMm(settings.height)),\n orientation: PageOrientation.PORTRAIT,\n },\n margin: {\n top: convertMillimetersToTwip(pxToMm(settings.marginTop)),\n bottom: convertMillimetersToTwip(pxToMm(settings.marginBottom)),\n left: convertMillimetersToTwip(pxToMm(settings.marginLeft)),\n right: convertMillimetersToTwip(pxToMm(settings.marginRight)),\n },\n },\n };\n}\n\nfunction blocksToDocxChildren(\n doc: SerializedDocument,\n images: Map<string, ResolvedImage | null>,\n): Array<Paragraph | Table> {\n const out: Array<Paragraph | Table> = [];\n const blocks = doc.blocks;\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n switch (block.type) {\n case \"paragraph\":\n out.push(makeParagraph(block, images));\n break;\n case \"heading\":\n out.push(makeHeading(block, images));\n break;\n case \"blockquote\":\n out.push(makeBlockquote(block, images));\n break;\n case \"codeBlock\":\n out.push(makeCodeBlock(block, images));\n break;\n case \"listItem\":\n out.push(makeListItem(block, images));\n break;\n case \"table\":\n out.push(makeTable(block, images));\n break;\n default:\n out.push(makeParagraph(block, images));\n }\n }\n return out;\n}\n\n// ---------- block builders ----------\n\n// School standard: Arial for all body text. Code blocks override with\n// Consolas (monospace is more legible for code). Italics on blockquote is\n// kept as a stylistic default.\nconst ARIAL: RunDefaults = { font: \"Arial\" };\n\nfunction makeParagraph(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n return new Paragraph({\n alignment: alignFor(block.attrs.align),\n bidirectional: block.attrs.dir === \"rtl\" ? true : undefined,\n children: deltaToRuns(block.delta, ARIAL, images),\n });\n}\n\nfunction makeHeading(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n const level = clampHeadingLevel(block.attrs.level);\n return new Paragraph({\n heading: HEADING_LEVELS[level - 1],\n alignment: alignFor(block.attrs.align),\n children: deltaToRuns(block.delta, ARIAL, images),\n });\n}\n\nfunction makeBlockquote(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n return new Paragraph({\n alignment: alignFor(block.attrs.align),\n indent: { left: 480 }, // ~0.25\"\n border: {\n left: { color: \"CBD5E1\", space: 12, style: BorderStyle.SINGLE, size: 12 },\n },\n children: deltaToRuns(block.delta, { ...ARIAL, italics: true }, images),\n });\n}\n\nfunction makeCodeBlock(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n return new Paragraph({\n spacing: { before: 100, after: 100 },\n shading: { type: ShadingType.CLEAR, color: \"auto\", fill: \"F1F5F9\" },\n children: deltaToRuns(block.delta, { font: \"Consolas\", size: 20 }, images),\n });\n}\n\nfunction makeListItem(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n const kind: ListKind = block.attrs.listKind === \"ordered\" ? \"ordered\" : \"bullet\";\n const reference = kind === \"ordered\" ? ORDERED_REF : BULLET_REF;\n const level = clampListLevel(block.attrs.listLevel);\n return new Paragraph({\n numbering: { reference, level },\n alignment: alignFor(block.attrs.align),\n children: deltaToRuns(block.delta, ARIAL, images),\n });\n}\n\nfunction makeTable(block: SerializedBlock, images: Map<string, ResolvedImage | null>): Table {\n const rows = typeof block.attrs.rows === \"number\" ? block.attrs.rows : 0;\n const cols = typeof block.attrs.cols === \"number\" ? block.attrs.cols : 0;\n const cells = block.cells ?? [];\n\n const rowsOut: TableRow[] = [];\n for (let r = 0; r < rows; r++) {\n const cellsOut: TableCell[] = [];\n for (let c = 0; c < cols; c++) {\n const cell = cells[r * cols + c];\n if (!cell) {\n cellsOut.push(emptyCell());\n continue;\n }\n if (cell.attrs?.covered) continue;\n cellsOut.push(makeCell(cell, images));\n }\n rowsOut.push(new TableRow({ children: cellsOut }));\n }\n\n // Validate the RAW array against `cols` — filtering invalid entries first\n // (e.g. dropping a negative width) would silently shift the remaining\n // widths onto the wrong columns while still passing a length check.\n const rawColWidths = block.attrs.colWidths;\n const isValidColWidths =\n Array.isArray(rawColWidths) &&\n cols > 0 &&\n rawColWidths.length === cols &&\n rawColWidths.every((w): w is number => typeof w === \"number\" && w > 0);\n const columnWidths = isValidColWidths\n ? rawColWidths.map((px) => convertMillimetersToTwip(pxToMm(px)))\n : undefined;\n\n return new Table({\n rows: rowsOut,\n ...(columnWidths\n ? {\n columnWidths,\n // Fix the layout so Word honors the column widths verbatim instead\n // of autofitting to content (the OOXML default when `tblLayout` is\n // absent, which would make `columnWidths` a mere hint).\n layout: TableLayoutType.FIXED,\n width: { size: columnWidths.reduce((a, b) => a + b, 0), type: WidthType.DXA },\n }\n : { width: { size: 100, type: WidthType.PERCENTAGE } }),\n });\n}\n\nfunction makeCell(cell: SerializedCell, images: Map<string, ResolvedImage | null>): TableCell {\n const span = cell.attrs?.colspan && cell.attrs.colspan > 1 ? cell.attrs.colspan : 1;\n const rowSpan = cell.attrs?.rowspan && cell.attrs.rowspan > 1 ? cell.attrs.rowspan : 1;\n const fill = cssColorToDocxHex(cell.attrs?.bgColor);\n return new TableCell({\n columnSpan: span,\n rowSpan,\n verticalAlign: VerticalAlign.TOP,\n shading: fill ? { type: ShadingType.CLEAR, color: \"auto\", fill } : undefined,\n children: [\n new Paragraph({\n alignment: alignFor(cell.attrs?.align),\n children: deltaToRuns(cell.delta, ARIAL, images),\n }),\n ],\n });\n}\n\nfunction emptyCell(): TableCell {\n return new TableCell({ children: [new Paragraph(\"\")] });\n}\n\n// ---------- inline ----------\n\ninterface RunDefaults {\n bold?: boolean;\n italics?: boolean;\n font?: string;\n size?: number;\n}\n\nfunction deltaToRuns(\n delta: DeltaOp[],\n defaults: RunDefaults = {},\n images: Map<string, ResolvedImage | null>,\n): Array<TextRun | ImageRun> {\n if (delta.length === 0) return [new TextRun(\"\")];\n const out: Array<TextRun | ImageRun> = [];\n for (const op of delta) {\n if (typeof op.insert === \"string\") {\n if (op.insert.length === 0) continue;\n out.push(makeTextRun(op.insert, op.attributes, defaults));\n } else if (isImageEmbed(op.insert)) {\n const embed = op.insert;\n const img = makeImageRun(embed, images);\n if (img) out.push(img);\n // Captions are emitted as italic-smaller text on a new line right after\n // the image. Word-style \"Caption\" paragraph would need a paragraph\n // break, which we can't do mid-delta — visually correct but loses the\n // semantic \"Caption\" style.\n if (embed.caption && embed.caption.length > 0) {\n out.push(\n new TextRun({\n text: embed.caption,\n italics: true,\n size: 18, // 9pt — typical caption size\n break: 1,\n }),\n );\n }\n }\n }\n if (out.length === 0) out.push(new TextRun(\"\"));\n return out;\n}\n\nfunction makeTextRun(\n text: string,\n marks: MarkAttrs | undefined,\n defaults: RunDefaults,\n): TextRun {\n const m = marks ?? {};\n const fontSize = parseFontSizeToHalfPoints(m.fontSize);\n // TextRun reads newlines via `break`; convert \\n into break runs.\n const segments = text.split(\"\\n\");\n if (segments.length === 1) {\n return new TextRun({\n text,\n bold: m.bold || defaults.bold,\n italics: m.italic || defaults.italics,\n underline: m.underline ? { type: UnderlineType.SINGLE } : undefined,\n strike: m.strike,\n color: cssColorToDocxHex(m.color),\n font: m.fontFamily ?? defaults.font,\n size: fontSize ?? defaults.size,\n });\n }\n // Multi-line: rejoin via TextRun children — but TextRun expects a single text\n // string. Approximate with non-printing line breaks: produce a sequence of\n // TextRuns separated by `new TextRun({ break: 1 })` packed into the parent.\n // Simpler: collapse to a single TextRun with the string, treating \\n as a\n // soft-break placeholder. Word renders \\n inside a w:t as a literal space,\n // so we replace with a small symbol-friendly fallback.\n return new TextRun({\n text: text.replace(/\\n/g, \" \"),\n bold: m.bold || defaults.bold,\n italics: m.italic || defaults.italics,\n underline: m.underline ? { type: UnderlineType.SINGLE } : undefined,\n strike: m.strike,\n color: cssColorToDocxHex(m.color),\n font: m.fontFamily ?? defaults.font,\n size: fontSize ?? defaults.size,\n });\n}\n\nfunction makeImageRun(\n embed: ImageEmbed,\n images: Map<string, ResolvedImage | null>,\n): ImageRun | null {\n const resolved = images.get(embed.src);\n if (!resolved) return null;\n return new ImageRun({\n data: resolved.data,\n type: resolved.type,\n transformation: {\n width: Math.max(1, Math.round(embed.width)),\n height: Math.max(1, Math.round(embed.height)),\n },\n } as ConstructorParameters<typeof ImageRun>[0]);\n}\n\ninterface DecodedImage {\n bytes: Uint8Array;\n kind: \"png\" | \"jpg\" | \"gif\" | \"bmp\" | \"svg\";\n}\n\nfunction decodeDataUrl(src: string): DecodedImage | null {\n const m = /^data:image\\/(png|jpe?g|gif|bmp|svg\\+xml);base64,(.+)$/i.exec(src);\n if (!m) return null;\n const fmt = m[1].toLowerCase();\n const kind: DecodedImage[\"kind\"] =\n fmt === \"jpeg\" || fmt === \"jpg\"\n ? \"jpg\"\n : fmt === \"svg+xml\"\n ? \"svg\"\n : (fmt as \"png\" | \"gif\" | \"bmp\");\n const base64 = m[2];\n const bytes = base64ToBytes(base64);\n return { bytes, kind };\n}\n\n/**\n * Default `resolveImage`: decode data URLs locally, fetch http(s) URLs, and\n * return null for anything else (caller counts it in `skippedImages`).\n */\nasync function defaultResolveImage(src: string): Promise<ResolvedImage | null> {\n const decoded = decodeDataUrl(src);\n if (decoded) return { data: decoded.bytes, type: decoded.kind };\n if (/^https?:\\/\\//i.test(src) && typeof fetch === \"function\") {\n try {\n const res = await fetch(src);\n if (!res.ok) return null;\n const ct = (res.headers.get(\"content-type\") ?? \"\").toLowerCase();\n const matchedType: ResolvedImage[\"type\"] | null = ct.includes(\"png\")\n ? \"png\"\n : ct.includes(\"jpeg\") || ct.includes(\"jpg\")\n ? \"jpg\"\n : ct.includes(\"gif\")\n ? \"gif\"\n : ct.includes(\"bmp\")\n ? \"bmp\"\n : ct.includes(\"svg\")\n ? \"svg\"\n : null;\n // Unknown content-type (e.g. image/webp) with no recognizable extension\n // in the URL: skip rather than mislabeling bytes as PNG (would produce\n // a broken image in Word — contract is \"failure to resolve → skip\").\n const type = matchedType ?? typeFromExtension(src);\n if (!type) return null;\n return { data: new Uint8Array(await res.arrayBuffer()), type };\n } catch {\n return null;\n }\n }\n return null;\n}\n\nfunction typeFromExtension(src: string): ResolvedImage[\"type\"] | null {\n const m = /\\.(png|jpe?g|gif|bmp|svg)(\\?|#|$)/i.exec(src);\n if (!m) return null;\n const ext = m[1].toLowerCase();\n return ext === \"jpeg\" || ext === \"jpg\" ? \"jpg\" : (ext as ResolvedImage[\"type\"]);\n}\n\n/**\n * Async pre-pass: collect every image `src` referenced by the document\n * (block deltas and table cell deltas), resolve them in parallel via\n * `resolve`, and return a lookup map plus the count of embeds that failed to\n * resolve. The rest of the build pipeline stays synchronous and reads from\n * this map.\n */\nasync function resolveAllImages(\n doc: SerializedDocument,\n resolve: (src: string) => Promise<ResolvedImage | null>,\n): Promise<{ images: Map<string, ResolvedImage | null>; skipped: number }> {\n const srcs = new Set<string>();\n const collect = (delta: DeltaOp[]) => {\n for (const op of delta) if (isImageEmbed(op.insert)) srcs.add(op.insert.src);\n };\n for (const block of doc.blocks) {\n collect(block.delta);\n for (const cell of block.cells ?? []) collect(cell.delta);\n }\n const images = new Map<string, ResolvedImage | null>();\n await Promise.all(\n [...srcs].map(async (src) => {\n let resolved: ResolvedImage | null = null;\n try {\n resolved = await resolve(src);\n } catch {\n resolved = null;\n }\n // docx's ImageRun requires a `fallback` for type \"svg\" (RegularImageOptions);\n // without it the constructor throws synchronously inside buildDocument,\n // which is outside this try/catch and would abort the whole export. Treat\n // svg as unresolved (skip) until we add fallback support.\n if (resolved?.type === \"svg\") resolved = null;\n images.set(src, resolved);\n }),\n );\n // skipped conta OCORRÊNCIAS de embed sem resolução (não srcs únicos).\n let skipped = 0;\n const count = (delta: DeltaOp[]) => {\n for (const op of delta)\n if (isImageEmbed(op.insert) && !images.get(op.insert.src)) skipped++;\n };\n for (const block of doc.blocks) {\n count(block.delta);\n for (const cell of block.cells ?? []) count(cell.delta);\n }\n return { images, skipped };\n}\n\nfunction base64ToBytes(b64: string): Uint8Array {\n if (typeof atob === \"function\") {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n // Node fallback.\n return new Uint8Array(Buffer.from(b64, \"base64\"));\n}\n\n// ---------- attribute mapping ----------\n\nconst HEADING_LEVELS = [\n HeadingLevel.HEADING_1,\n HeadingLevel.HEADING_2,\n HeadingLevel.HEADING_3,\n HeadingLevel.HEADING_4,\n HeadingLevel.HEADING_5,\n HeadingLevel.HEADING_6,\n];\n\nfunction clampHeadingLevel(l: unknown): 1 | 2 | 3 | 4 | 5 | 6 {\n if (typeof l !== \"number\") return 1;\n const n = Math.trunc(l);\n if (n < 1) return 1;\n if (n > 6) return 6;\n return n as 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nfunction clampListLevel(l: unknown): number {\n if (typeof l !== \"number\" || !Number.isFinite(l)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(l)));\n}\n\nfunction alignFor(a: AlignValue | undefined): (typeof AlignmentType)[keyof typeof AlignmentType] | undefined {\n switch (a) {\n case \"left\":\n return AlignmentType.LEFT;\n case \"center\":\n return AlignmentType.CENTER;\n case \"right\":\n return AlignmentType.RIGHT;\n case \"justify\":\n return AlignmentType.JUSTIFIED;\n default:\n return undefined;\n }\n}\n\nfunction cssColorToDocxHex(color: string | undefined): string | undefined {\n if (!color) return undefined;\n // docx expects RRGGBB without leading '#'.\n const c = color.trim();\n if (/^#[0-9a-f]{6}$/i.test(c)) return c.slice(1).toUpperCase();\n if (/^#[0-9a-f]{3}$/i.test(c)) {\n // expand short hex\n return c\n .slice(1)\n .split(\"\")\n .map((d) => d + d)\n .join(\"\")\n .toUpperCase();\n }\n const rgb = /^rgb\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/i.exec(c);\n if (rgb) {\n const r = Number(rgb[1]);\n const g = Number(rgb[2]);\n const b = Number(rgb[3]);\n return [r, g, b].map((n) => Math.max(0, Math.min(255, n)).toString(16).padStart(2, \"0\")).join(\"\").toUpperCase();\n }\n // Named colors / other forms — let docx fall back to default.\n return undefined;\n}\n\n/**\n * docx sizes are in half-points (24 = 12pt). Parse common CSS sizes.\n * Returns `undefined` when not parseable so callers can pick a default.\n */\nfunction parseFontSizeToHalfPoints(size: string | undefined): number | undefined {\n if (!size) return undefined;\n const s = size.trim();\n const m = /^(-?\\d*\\.?\\d+)\\s*(pt|px|em|rem)?$/i.exec(s);\n if (!m) return undefined;\n const n = Number(m[1]);\n if (!Number.isFinite(n) || n <= 0) return undefined;\n const unit = (m[2] ?? \"pt\").toLowerCase();\n const pt =\n unit === \"pt\"\n ? n\n : unit === \"px\"\n ? n * 0.75 // CSS px → pt at 96dpi\n : n * 12;\n return Math.round(pt * 2);\n}\n\n// ---------- list numbering definitions ----------\n\nfunction buildBulletLevels(): Array<{\n level: number;\n format: (typeof LevelFormat)[keyof typeof LevelFormat];\n text: string;\n alignment: (typeof AlignmentType)[keyof typeof AlignmentType];\n style?: { paragraph?: { indent?: { left: number; hanging: number } } };\n}> {\n const symbols = [\"•\", \"◦\", \"▪\", \"•\", \"◦\", \"▪\"];\n return symbols.map((sym, i) => ({\n level: i,\n format: LevelFormat.BULLET,\n text: sym,\n alignment: AlignmentType.LEFT,\n style: {\n paragraph: { indent: { left: 360 + i * 360, hanging: 260 } },\n },\n }));\n}\n\nfunction buildOrderedLevels(): Array<{\n level: number;\n format: (typeof LevelFormat)[keyof typeof LevelFormat];\n text: string;\n alignment: (typeof AlignmentType)[keyof typeof AlignmentType];\n style?: { paragraph?: { indent?: { left: number; hanging: number } } };\n}> {\n const formats = [\n LevelFormat.DECIMAL,\n LevelFormat.LOWER_LETTER,\n LevelFormat.LOWER_ROMAN,\n LevelFormat.DECIMAL,\n LevelFormat.LOWER_LETTER,\n LevelFormat.LOWER_ROMAN,\n ];\n return formats.map((format, i) => ({\n level: i,\n format,\n text: `%${i + 1}.`,\n alignment: AlignmentType.LEFT,\n style: {\n paragraph: { indent: { left: 360 + i * 360, hanging: 260 } },\n },\n }));\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/docx.ts"],"sourcesContent":["export { documentToDocxBlob, documentToDocxBuffer } from \"./docx\";\nexport type { DocumentToDocxOptions, ResolvedImage } from \"./docx\";\n","import type {\n AlignValue,\n DeltaOp,\n ImageEmbed,\n LegacySerializedDocument,\n ListKind,\n MarkAttrs,\n SerializedBlock,\n SerializedCell,\n SerializedDocument,\n TableBorderPreset,\n} from \"@sofereditor/core\";\nimport {\n DEFAULT_PAGE_SETTINGS,\n isImageEmbed,\n isLegacySerializedDocument,\n pxToMm,\n type PageSettings,\n} from \"@sofereditor/core\";\nimport {\n AlignmentType,\n BorderStyle,\n Document,\n HeadingLevel,\n ImageRun,\n LevelFormat,\n LineRuleType,\n Packer,\n Paragraph,\n PageOrientation,\n ShadingType,\n Table,\n TableCell,\n TableLayoutType,\n TableRow,\n TextRun,\n UnderlineType,\n VerticalAlign,\n WidthType,\n convertMillimetersToTwip,\n} from \"docx\";\n\nconst BULLET_REF = \"ed-bullet\";\nconst ORDERED_REF = \"ed-ordered\";\n\nexport interface ResolvedImage {\n data: Uint8Array;\n type: \"png\" | \"jpg\" | \"gif\" | \"bmp\" | \"svg\";\n}\n\nexport interface DocumentToDocxOptions {\n /** Document title; embedded in core properties. */\n title?: string;\n /** Author for core properties. */\n creator?: string;\n /**\n * Resolve um `src` de imagem para bytes. Default: data URLs (decodificadas\n * localmente) e http(s) via fetch. Retornar null pula a imagem (contada em\n * `skippedImages`) sem abortar o export.\n */\n resolveImage?: (src: string) => Promise<ResolvedImage | null>;\n}\n\n/**\n * Build a docx `Document` from a `SerializedDocument`. Output is a Blob you can\n * download with `downloadBlob` from `@sofereditor/export-pdf`.\n *\n * Mappings:\n * - paragraph / heading / blockquote / codeBlock → Paragraph (with `heading`,\n * shading, monospace font for code)\n * - listItem → Paragraph with `numbering: { reference, level }`\n * - table → docx `Table`. `rowspan`/`colspan` map to TableCell's `rowSpan`/\n * `columnSpan`; \"covered\" cells are skipped (docx infers cover from spans)\n * - inline marks → TextRun props (bold, italics, underline, strike, color,\n * font, size in half-points)\n * - image embeds → ImageRun with bytes resolved via `resolveImage` (default:\n * data URLs decoded locally, http(s) fetched; unresolved images are\n * skipped and counted in `skippedImages` instead of aborting the export)\n */\nexport async function documentToDocxBlob(\n doc: SerializedDocument | LegacySerializedDocument,\n options: DocumentToDocxOptions = {},\n): Promise<{ blob: Blob; skippedImages: number }> {\n const normalized = normalize(doc);\n const { images, skipped } = await resolveAllImages(\n normalized,\n options.resolveImage ?? defaultResolveImage,\n );\n const built = buildDocument(normalized, options, images);\n return { blob: await Packer.toBlob(built), skippedImages: skipped };\n}\n\n/** Same as `documentToDocxBlob` but returns a Uint8Array (useful in Node/tests). */\nexport async function documentToDocxBuffer(\n doc: SerializedDocument | LegacySerializedDocument,\n options: DocumentToDocxOptions = {},\n): Promise<{ buffer: Uint8Array; skippedImages: number }> {\n const normalized = normalize(doc);\n const { images, skipped } = await resolveAllImages(\n normalized,\n options.resolveImage ?? defaultResolveImage,\n );\n const built = buildDocument(normalized, options, images);\n const buf = await Packer.toBuffer(built);\n // `Packer.toBuffer` returns a Node Buffer at runtime — coerce for consumers.\n return {\n buffer: buf instanceof Uint8Array ? buf : new Uint8Array(buf),\n skippedImages: skipped,\n };\n}\n\nfunction normalize(doc: SerializedDocument | LegacySerializedDocument): SerializedDocument {\n return isLegacySerializedDocument(doc) ? { blocks: doc } : doc;\n}\n\nfunction buildDocument(\n doc: SerializedDocument,\n options: DocumentToDocxOptions,\n images: Map<string, ResolvedImage | null>,\n): Document {\n const children = blocksToDocxChildren(doc, images);\n const settings = doc.pageSettings ?? DEFAULT_PAGE_SETTINGS;\n return new Document({\n creator: options.creator,\n title: options.title,\n numbering: {\n config: [\n {\n reference: BULLET_REF,\n levels: buildBulletLevels(),\n },\n {\n reference: ORDERED_REF,\n levels: buildOrderedLevels(),\n },\n ],\n },\n sections: [\n {\n properties: sectionPropertiesFor(settings),\n children,\n },\n ],\n });\n}\n\nfunction sectionPropertiesFor(settings: PageSettings) {\n return {\n page: {\n size: {\n width: convertMillimetersToTwip(pxToMm(settings.width)),\n height: convertMillimetersToTwip(pxToMm(settings.height)),\n orientation: PageOrientation.PORTRAIT,\n },\n margin: {\n top: convertMillimetersToTwip(pxToMm(settings.marginTop)),\n bottom: convertMillimetersToTwip(pxToMm(settings.marginBottom)),\n left: convertMillimetersToTwip(pxToMm(settings.marginLeft)),\n right: convertMillimetersToTwip(pxToMm(settings.marginRight)),\n },\n },\n };\n}\n\nfunction blocksToDocxChildren(\n doc: SerializedDocument,\n images: Map<string, ResolvedImage | null>,\n): Array<Paragraph | Table> {\n const out: Array<Paragraph | Table> = [];\n const blocks = doc.blocks;\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n switch (block.type) {\n case \"paragraph\":\n out.push(makeParagraph(block, images));\n break;\n case \"heading\":\n out.push(makeHeading(block, images));\n break;\n case \"blockquote\":\n out.push(makeBlockquote(block, images));\n break;\n case \"codeBlock\":\n out.push(makeCodeBlock(block, images));\n break;\n case \"listItem\":\n out.push(makeListItem(block, images));\n break;\n case \"table\":\n out.push(makeTable(block, images));\n break;\n default:\n out.push(makeParagraph(block, images));\n }\n }\n return out;\n}\n\n// ---------- block builders ----------\n\n// School standard: Arial for all body text. Code blocks override with\n// Consolas (monospace is more legible for code). Italics on blockquote is\n// kept as a stylistic default.\nconst ARIAL: RunDefaults = { font: \"Arial\" };\n\nfunction makeParagraph(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n return new Paragraph({\n alignment: alignFor(block.attrs.align),\n bidirectional: block.attrs.dir === \"rtl\" ? true : undefined,\n ...answerLineProps(block.attrs),\n children: deltaToRuns(block.delta, ARIAL, images),\n });\n}\n\n/**\n * Linha de resposta → borda inferior de parágrafo (`w:pBdr`) + entrelinha.\n * É como um documento profissional desenha uma pauta: a régua acompanha a\n * margem automaticamente, ao contrário de uma fileira de underlines.\n *\n * 240 twips = uma linha simples no OOXML.\n */\nfunction answerLineProps(attrs: SerializedBlock[\"attrs\"]) {\n if (attrs.answerLine !== true) return {};\n const spacing = attrs.answerLineSpacing ?? 1;\n return {\n border: {\n bottom: { style: BorderStyle.SINGLE, size: 6, color: \"000000\", space: 1 },\n },\n spacing: { line: Math.round(240 * spacing), lineRule: LineRuleType.AUTO },\n };\n}\n\nfunction makeHeading(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n const level = clampHeadingLevel(block.attrs.level);\n return new Paragraph({\n heading: HEADING_LEVELS[level - 1],\n alignment: alignFor(block.attrs.align),\n children: deltaToRuns(block.delta, ARIAL, images),\n });\n}\n\nfunction makeBlockquote(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n return new Paragraph({\n alignment: alignFor(block.attrs.align),\n indent: { left: 480 }, // ~0.25\"\n border: {\n left: { color: \"CBD5E1\", space: 12, style: BorderStyle.SINGLE, size: 12 },\n },\n children: deltaToRuns(block.delta, { ...ARIAL, italics: true }, images),\n });\n}\n\nfunction makeCodeBlock(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n return new Paragraph({\n spacing: { before: 100, after: 100 },\n shading: { type: ShadingType.CLEAR, color: \"auto\", fill: \"F1F5F9\" },\n children: deltaToRuns(block.delta, { font: \"Consolas\", size: 20 }, images),\n });\n}\n\nfunction makeListItem(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n const kind: ListKind = block.attrs.listKind === \"ordered\" ? \"ordered\" : \"bullet\";\n const reference = kind === \"ordered\" ? ORDERED_REF : BULLET_REF;\n const level = clampListLevel(block.attrs.listLevel);\n return new Paragraph({\n numbering: { reference, level },\n alignment: alignFor(block.attrs.align),\n children: deltaToRuns(block.delta, ARIAL, images),\n });\n}\n\nfunction makeTable(block: SerializedBlock, images: Map<string, ResolvedImage | null>): Table {\n const rows = typeof block.attrs.rows === \"number\" ? block.attrs.rows : 0;\n const cols = typeof block.attrs.cols === \"number\" ? block.attrs.cols : 0;\n const cells = block.cells ?? [];\n\n const rowsOut: TableRow[] = [];\n for (let r = 0; r < rows; r++) {\n const cellsOut: TableCell[] = [];\n for (let c = 0; c < cols; c++) {\n const cell = cells[r * cols + c];\n if (!cell) {\n cellsOut.push(emptyCell());\n continue;\n }\n if (cell.attrs?.covered) continue;\n cellsOut.push(makeCell(cell, images));\n }\n rowsOut.push(new TableRow({ children: cellsOut }));\n }\n\n // Validate the RAW array against `cols` — filtering invalid entries first\n // (e.g. dropping a negative width) would silently shift the remaining\n // widths onto the wrong columns while still passing a length check.\n const rawColWidths = block.attrs.colWidths;\n const isValidColWidths =\n Array.isArray(rawColWidths) &&\n cols > 0 &&\n rawColWidths.length === cols &&\n rawColWidths.every((w): w is number => typeof w === \"number\" && w > 0);\n const columnWidths = isValidColWidths\n ? rawColWidths.map((px) => convertMillimetersToTwip(pxToMm(px)))\n : undefined;\n\n return new Table({\n rows: rowsOut,\n borders: docxTableBorders(block.attrs.borderPreset),\n ...(columnWidths\n ? {\n columnWidths,\n // Fix the layout so Word honors the column widths verbatim instead\n // of autofitting to content (the OOXML default when `tblLayout` is\n // absent, which would make `columnWidths` a mere hint).\n layout: TableLayoutType.FIXED,\n width: { size: columnWidths.reduce((a, b) => a + b, 0), type: WidthType.DXA },\n }\n : { width: { size: 100, type: WidthType.PERCENTAGE } }),\n });\n}\n\n/**\n * Traduz o preset para `w:tblBorders`. Lado desligado = `BorderStyle.NONE`.\n *\n * A tabela-verdade sai direto de `cellBorderColors`: o que aquele helper liga em\n * TODA célula vira o par externo MAIS o interno correspondente. Ex.: em\n * `vertical`, toda célula tem left/right ligados — logo as laterais externas\n * (left/right) E as internas (insideV) ficam ligadas, e topo/base\n * (top/bottom/insideH) desligados.\n *\n * A tabela é a especificação; a função sai dela, não o contrário.\n */\nfunction docxTableBorders(preset: TableBorderPreset | undefined) {\n const on = { style: BorderStyle.SINGLE, size: 6, color: \"CBD5E1\" };\n const off = { style: BorderStyle.NONE, size: 0, color: \"auto\" };\n const s = (visivel: boolean) => (visivel ? on : off);\n\n // [top/bottom, left/right, insideH, insideV]\n const TRUTH: Record<TableBorderPreset, [boolean, boolean, boolean, boolean]> = {\n all: [true, true, true, true],\n outer: [true, true, false, false],\n horizontal: [true, false, true, false],\n vertical: [false, true, false, true],\n none: [false, false, false, false],\n };\n const [tb, lr, iH, iV] = TRUTH[preset ?? \"all\"];\n return {\n top: s(tb),\n bottom: s(tb),\n left: s(lr),\n right: s(lr),\n insideHorizontal: s(iH),\n insideVertical: s(iV),\n };\n}\n\nfunction makeCell(cell: SerializedCell, images: Map<string, ResolvedImage | null>): TableCell {\n const span = cell.attrs?.colspan && cell.attrs.colspan > 1 ? cell.attrs.colspan : 1;\n const rowSpan = cell.attrs?.rowspan && cell.attrs.rowspan > 1 ? cell.attrs.rowspan : 1;\n const fill = cssColorToDocxHex(cell.attrs?.bgColor);\n return new TableCell({\n columnSpan: span,\n rowSpan,\n verticalAlign: VerticalAlign.TOP,\n shading: fill ? { type: ShadingType.CLEAR, color: \"auto\", fill } : undefined,\n children: [\n new Paragraph({\n alignment: alignFor(cell.attrs?.align),\n children: deltaToRuns(cell.delta, ARIAL, images),\n }),\n ],\n });\n}\n\nfunction emptyCell(): TableCell {\n return new TableCell({ children: [new Paragraph(\"\")] });\n}\n\n// ---------- inline ----------\n\ninterface RunDefaults {\n bold?: boolean;\n italics?: boolean;\n font?: string;\n size?: number;\n}\n\nfunction deltaToRuns(\n delta: DeltaOp[],\n defaults: RunDefaults = {},\n images: Map<string, ResolvedImage | null>,\n): Array<TextRun | ImageRun> {\n if (delta.length === 0) return [new TextRun(\"\")];\n const out: Array<TextRun | ImageRun> = [];\n for (const op of delta) {\n if (typeof op.insert === \"string\") {\n if (op.insert.length === 0) continue;\n out.push(makeTextRun(op.insert, op.attributes, defaults));\n } else if (isImageEmbed(op.insert)) {\n const embed = op.insert;\n const img = makeImageRun(embed, images);\n if (img) out.push(img);\n // Captions are emitted as italic-smaller text on a new line right after\n // the image. Word-style \"Caption\" paragraph would need a paragraph\n // break, which we can't do mid-delta — visually correct but loses the\n // semantic \"Caption\" style.\n if (embed.caption && embed.caption.length > 0) {\n out.push(\n new TextRun({\n text: embed.caption,\n italics: true,\n size: 18, // 9pt — typical caption size\n break: 1,\n }),\n );\n }\n }\n }\n if (out.length === 0) out.push(new TextRun(\"\"));\n return out;\n}\n\n/**\n * Props de um `TextRun` a partir das marks. Fica numa função só porque\n * `makeTextRun` tem dois caminhos (linha única e multi-linha) que antes\n * duplicavam esta lista inteira — uma mark acrescentada em apenas um dos dois\n * se perdia silenciosamente em texto com quebra de linha.\n *\n * `highlight` sai como `w:shd` e NÃO como `w:highlight`: este último só aceita\n * ~15 valores nomeados e não sobreviveria a uma cor arbitrária do picker.\n */\nfunction textRunProps(text: string, m: MarkAttrs, defaults: RunDefaults) {\n const fill = cssColorToDocxHex(m.highlight);\n return {\n text,\n bold: m.bold || defaults.bold,\n italics: m.italic || defaults.italics,\n underline: m.underline ? { type: UnderlineType.SINGLE } : undefined,\n strike: m.strike,\n color: cssColorToDocxHex(m.color),\n font: m.fontFamily ?? defaults.font,\n size: parseFontSizeToHalfPoints(m.fontSize) ?? defaults.size,\n shading: fill ? { type: ShadingType.CLEAR, color: \"auto\", fill } : undefined,\n };\n}\n\nfunction makeTextRun(\n text: string,\n marks: MarkAttrs | undefined,\n defaults: RunDefaults,\n): TextRun {\n const m = marks ?? {};\n // TextRun reads newlines via `break`; convert \\n into break runs.\n const segments = text.split(\"\\n\");\n if (segments.length === 1) {\n return new TextRun(textRunProps(text, m, defaults));\n }\n // Multi-line: rejoin via TextRun children — but TextRun expects a single text\n // string. Approximate with non-printing line breaks: produce a sequence of\n // TextRuns separated by `new TextRun({ break: 1 })` packed into the parent.\n // Simpler: collapse to a single TextRun with the string, treating \\n as a\n // soft-break placeholder. Word renders \\n inside a w:t as a literal space,\n // so we replace with a small symbol-friendly fallback.\n return new TextRun(textRunProps(text.replace(/\\n/g, \" \"), m, defaults));\n}\n\nfunction makeImageRun(\n embed: ImageEmbed,\n images: Map<string, ResolvedImage | null>,\n): ImageRun | null {\n const resolved = images.get(embed.src);\n if (!resolved) return null;\n return new ImageRun({\n data: resolved.data,\n type: resolved.type,\n transformation: {\n width: Math.max(1, Math.round(embed.width)),\n height: Math.max(1, Math.round(embed.height)),\n },\n } as ConstructorParameters<typeof ImageRun>[0]);\n}\n\ninterface DecodedImage {\n bytes: Uint8Array;\n kind: \"png\" | \"jpg\" | \"gif\" | \"bmp\" | \"svg\";\n}\n\nfunction decodeDataUrl(src: string): DecodedImage | null {\n const m = /^data:image\\/(png|jpe?g|gif|bmp|svg\\+xml);base64,(.+)$/i.exec(src);\n if (!m) return null;\n const fmt = m[1].toLowerCase();\n const kind: DecodedImage[\"kind\"] =\n fmt === \"jpeg\" || fmt === \"jpg\"\n ? \"jpg\"\n : fmt === \"svg+xml\"\n ? \"svg\"\n : (fmt as \"png\" | \"gif\" | \"bmp\");\n const base64 = m[2];\n const bytes = base64ToBytes(base64);\n return { bytes, kind };\n}\n\n/**\n * Default `resolveImage`: decode data URLs locally, fetch http(s) URLs, and\n * return null for anything else (caller counts it in `skippedImages`).\n */\nasync function defaultResolveImage(src: string): Promise<ResolvedImage | null> {\n const decoded = decodeDataUrl(src);\n if (decoded) return { data: decoded.bytes, type: decoded.kind };\n if (/^https?:\\/\\//i.test(src) && typeof fetch === \"function\") {\n try {\n const res = await fetch(src);\n if (!res.ok) return null;\n const ct = (res.headers.get(\"content-type\") ?? \"\").toLowerCase();\n const matchedType: ResolvedImage[\"type\"] | null = ct.includes(\"png\")\n ? \"png\"\n : ct.includes(\"jpeg\") || ct.includes(\"jpg\")\n ? \"jpg\"\n : ct.includes(\"gif\")\n ? \"gif\"\n : ct.includes(\"bmp\")\n ? \"bmp\"\n : ct.includes(\"svg\")\n ? \"svg\"\n : null;\n // Unknown content-type (e.g. image/webp) with no recognizable extension\n // in the URL: skip rather than mislabeling bytes as PNG (would produce\n // a broken image in Word — contract is \"failure to resolve → skip\").\n const type = matchedType ?? typeFromExtension(src);\n if (!type) return null;\n return { data: new Uint8Array(await res.arrayBuffer()), type };\n } catch {\n return null;\n }\n }\n return null;\n}\n\nfunction typeFromExtension(src: string): ResolvedImage[\"type\"] | null {\n const m = /\\.(png|jpe?g|gif|bmp|svg)(\\?|#|$)/i.exec(src);\n if (!m) return null;\n const ext = m[1].toLowerCase();\n return ext === \"jpeg\" || ext === \"jpg\" ? \"jpg\" : (ext as ResolvedImage[\"type\"]);\n}\n\n/**\n * Async pre-pass: collect every image `src` referenced by the document\n * (block deltas and table cell deltas), resolve them in parallel via\n * `resolve`, and return a lookup map plus the count of embeds that failed to\n * resolve. The rest of the build pipeline stays synchronous and reads from\n * this map.\n */\nasync function resolveAllImages(\n doc: SerializedDocument,\n resolve: (src: string) => Promise<ResolvedImage | null>,\n): Promise<{ images: Map<string, ResolvedImage | null>; skipped: number }> {\n const srcs = new Set<string>();\n const collect = (delta: DeltaOp[]) => {\n for (const op of delta) if (isImageEmbed(op.insert)) srcs.add(op.insert.src);\n };\n for (const block of doc.blocks) {\n collect(block.delta);\n for (const cell of block.cells ?? []) collect(cell.delta);\n }\n const images = new Map<string, ResolvedImage | null>();\n await Promise.all(\n [...srcs].map(async (src) => {\n let resolved: ResolvedImage | null = null;\n try {\n resolved = await resolve(src);\n } catch {\n resolved = null;\n }\n // docx's ImageRun requires a `fallback` for type \"svg\" (RegularImageOptions);\n // without it the constructor throws synchronously inside buildDocument,\n // which is outside this try/catch and would abort the whole export. Treat\n // svg as unresolved (skip) until we add fallback support.\n if (resolved?.type === \"svg\") resolved = null;\n images.set(src, resolved);\n }),\n );\n // skipped conta OCORRÊNCIAS de embed sem resolução (não srcs únicos).\n let skipped = 0;\n const count = (delta: DeltaOp[]) => {\n for (const op of delta)\n if (isImageEmbed(op.insert) && !images.get(op.insert.src)) skipped++;\n };\n for (const block of doc.blocks) {\n count(block.delta);\n for (const cell of block.cells ?? []) count(cell.delta);\n }\n return { images, skipped };\n}\n\nfunction base64ToBytes(b64: string): Uint8Array {\n if (typeof atob === \"function\") {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n // Node fallback.\n return new Uint8Array(Buffer.from(b64, \"base64\"));\n}\n\n// ---------- attribute mapping ----------\n\nconst HEADING_LEVELS = [\n HeadingLevel.HEADING_1,\n HeadingLevel.HEADING_2,\n HeadingLevel.HEADING_3,\n HeadingLevel.HEADING_4,\n HeadingLevel.HEADING_5,\n HeadingLevel.HEADING_6,\n];\n\nfunction clampHeadingLevel(l: unknown): 1 | 2 | 3 | 4 | 5 | 6 {\n if (typeof l !== \"number\") return 1;\n const n = Math.trunc(l);\n if (n < 1) return 1;\n if (n > 6) return 6;\n return n as 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nfunction clampListLevel(l: unknown): number {\n if (typeof l !== \"number\" || !Number.isFinite(l)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(l)));\n}\n\nfunction alignFor(a: AlignValue | undefined): (typeof AlignmentType)[keyof typeof AlignmentType] | undefined {\n switch (a) {\n case \"left\":\n return AlignmentType.LEFT;\n case \"center\":\n return AlignmentType.CENTER;\n case \"right\":\n return AlignmentType.RIGHT;\n case \"justify\":\n return AlignmentType.JUSTIFIED;\n default:\n return undefined;\n }\n}\n\nfunction cssColorToDocxHex(color: string | undefined): string | undefined {\n if (!color) return undefined;\n // docx expects RRGGBB without leading '#'.\n const c = color.trim();\n if (/^#[0-9a-f]{6}$/i.test(c)) return c.slice(1).toUpperCase();\n if (/^#[0-9a-f]{3}$/i.test(c)) {\n // expand short hex\n return c\n .slice(1)\n .split(\"\")\n .map((d) => d + d)\n .join(\"\")\n .toUpperCase();\n }\n const rgb = /^rgb\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/i.exec(c);\n if (rgb) {\n const r = Number(rgb[1]);\n const g = Number(rgb[2]);\n const b = Number(rgb[3]);\n return [r, g, b].map((n) => Math.max(0, Math.min(255, n)).toString(16).padStart(2, \"0\")).join(\"\").toUpperCase();\n }\n // Named colors / other forms — let docx fall back to default.\n return undefined;\n}\n\n/**\n * docx sizes are in half-points (24 = 12pt). Parse common CSS sizes.\n * Returns `undefined` when not parseable so callers can pick a default.\n */\nfunction parseFontSizeToHalfPoints(size: string | undefined): number | undefined {\n if (!size) return undefined;\n const s = size.trim();\n const m = /^(-?\\d*\\.?\\d+)\\s*(pt|px|em|rem)?$/i.exec(s);\n if (!m) return undefined;\n const n = Number(m[1]);\n if (!Number.isFinite(n) || n <= 0) return undefined;\n const unit = (m[2] ?? \"pt\").toLowerCase();\n const pt =\n unit === \"pt\"\n ? n\n : unit === \"px\"\n ? n * 0.75 // CSS px → pt at 96dpi\n : n * 12;\n return Math.round(pt * 2);\n}\n\n// ---------- list numbering definitions ----------\n\nfunction buildBulletLevels(): Array<{\n level: number;\n format: (typeof LevelFormat)[keyof typeof LevelFormat];\n text: string;\n alignment: (typeof AlignmentType)[keyof typeof AlignmentType];\n style?: { paragraph?: { indent?: { left: number; hanging: number } } };\n}> {\n const symbols = [\"•\", \"◦\", \"▪\", \"•\", \"◦\", \"▪\"];\n return symbols.map((sym, i) => ({\n level: i,\n format: LevelFormat.BULLET,\n text: sym,\n alignment: AlignmentType.LEFT,\n style: {\n paragraph: { indent: { left: 360 + i * 360, hanging: 260 } },\n },\n }));\n}\n\nfunction buildOrderedLevels(): Array<{\n level: number;\n format: (typeof LevelFormat)[keyof typeof LevelFormat];\n text: string;\n alignment: (typeof AlignmentType)[keyof typeof AlignmentType];\n style?: { paragraph?: { indent?: { left: number; hanging: number } } };\n}> {\n const formats = [\n LevelFormat.DECIMAL,\n LevelFormat.LOWER_LETTER,\n LevelFormat.LOWER_ROMAN,\n LevelFormat.DECIMAL,\n LevelFormat.LOWER_LETTER,\n LevelFormat.LOWER_ROMAN,\n ];\n return formats.map((format, i) => ({\n level: i,\n format,\n text: `%${i + 1}.`,\n alignment: AlignmentType.LEFT,\n style: {\n paragraph: { indent: { left: 360 + i * 360, hanging: 260 } },\n },\n }));\n}\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  HeadingLevel,
13
13
  ImageRun,
14
14
  LevelFormat,
15
+ LineRuleType,
15
16
  Packer,
16
17
  Paragraph,
17
18
  PageOrientation,
@@ -131,9 +132,20 @@ function makeParagraph(block, images) {
131
132
  return new Paragraph({
132
133
  alignment: alignFor(block.attrs.align),
133
134
  bidirectional: block.attrs.dir === "rtl" ? true : void 0,
135
+ ...answerLineProps(block.attrs),
134
136
  children: deltaToRuns(block.delta, ARIAL, images)
135
137
  });
136
138
  }
139
+ function answerLineProps(attrs) {
140
+ if (attrs.answerLine !== true) return {};
141
+ const spacing = attrs.answerLineSpacing ?? 1;
142
+ return {
143
+ border: {
144
+ bottom: { style: BorderStyle.SINGLE, size: 6, color: "000000", space: 1 }
145
+ },
146
+ spacing: { line: Math.round(240 * spacing), lineRule: LineRuleType.AUTO }
147
+ };
148
+ }
137
149
  function makeHeading(block, images) {
138
150
  const level = clampHeadingLevel(block.attrs.level);
139
151
  return new Paragraph({
@@ -193,6 +205,7 @@ function makeTable(block, images) {
193
205
  const columnWidths = isValidColWidths ? rawColWidths.map((px) => convertMillimetersToTwip(pxToMm(px))) : void 0;
194
206
  return new Table({
195
207
  rows: rowsOut,
208
+ borders: docxTableBorders(block.attrs.borderPreset),
196
209
  ...columnWidths ? {
197
210
  columnWidths,
198
211
  // Fix the layout so Word honors the column widths verbatim instead
@@ -203,6 +216,27 @@ function makeTable(block, images) {
203
216
  } : { width: { size: 100, type: WidthType.PERCENTAGE } }
204
217
  });
205
218
  }
219
+ function docxTableBorders(preset) {
220
+ const on = { style: BorderStyle.SINGLE, size: 6, color: "CBD5E1" };
221
+ const off = { style: BorderStyle.NONE, size: 0, color: "auto" };
222
+ const s = (visivel) => visivel ? on : off;
223
+ const TRUTH = {
224
+ all: [true, true, true, true],
225
+ outer: [true, true, false, false],
226
+ horizontal: [true, false, true, false],
227
+ vertical: [false, true, false, true],
228
+ none: [false, false, false, false]
229
+ };
230
+ const [tb, lr, iH, iV] = TRUTH[preset ?? "all"];
231
+ return {
232
+ top: s(tb),
233
+ bottom: s(tb),
234
+ left: s(lr),
235
+ right: s(lr),
236
+ insideHorizontal: s(iH),
237
+ insideVertical: s(iV)
238
+ };
239
+ }
206
240
  function makeCell(cell, images) {
207
241
  const span = cell.attrs?.colspan && cell.attrs.colspan > 1 ? cell.attrs.colspan : 1;
208
242
  const rowSpan = cell.attrs?.rowspan && cell.attrs.rowspan > 1 ? cell.attrs.rowspan : 1;
@@ -250,32 +284,27 @@ function deltaToRuns(delta, defaults = {}, images) {
250
284
  if (out.length === 0) out.push(new TextRun(""));
251
285
  return out;
252
286
  }
253
- function makeTextRun(text, marks, defaults) {
254
- const m = marks ?? {};
255
- const fontSize = parseFontSizeToHalfPoints(m.fontSize);
256
- const segments = text.split("\n");
257
- if (segments.length === 1) {
258
- return new TextRun({
259
- text,
260
- bold: m.bold || defaults.bold,
261
- italics: m.italic || defaults.italics,
262
- underline: m.underline ? { type: UnderlineType.SINGLE } : void 0,
263
- strike: m.strike,
264
- color: cssColorToDocxHex(m.color),
265
- font: m.fontFamily ?? defaults.font,
266
- size: fontSize ?? defaults.size
267
- });
268
- }
269
- return new TextRun({
270
- text: text.replace(/\n/g, " "),
287
+ function textRunProps(text, m, defaults) {
288
+ const fill = cssColorToDocxHex(m.highlight);
289
+ return {
290
+ text,
271
291
  bold: m.bold || defaults.bold,
272
292
  italics: m.italic || defaults.italics,
273
293
  underline: m.underline ? { type: UnderlineType.SINGLE } : void 0,
274
294
  strike: m.strike,
275
295
  color: cssColorToDocxHex(m.color),
276
296
  font: m.fontFamily ?? defaults.font,
277
- size: fontSize ?? defaults.size
278
- });
297
+ size: parseFontSizeToHalfPoints(m.fontSize) ?? defaults.size,
298
+ shading: fill ? { type: ShadingType.CLEAR, color: "auto", fill } : void 0
299
+ };
300
+ }
301
+ function makeTextRun(text, marks, defaults) {
302
+ const m = marks ?? {};
303
+ const segments = text.split("\n");
304
+ if (segments.length === 1) {
305
+ return new TextRun(textRunProps(text, m, defaults));
306
+ }
307
+ return new TextRun(textRunProps(text.replace(/\n/g, " "), m, defaults));
279
308
  }
280
309
  function makeImageRun(embed, images) {
281
310
  const resolved = images.get(embed.src);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/docx.ts"],"sourcesContent":["import type {\n AlignValue,\n DeltaOp,\n ImageEmbed,\n LegacySerializedDocument,\n ListKind,\n MarkAttrs,\n SerializedBlock,\n SerializedCell,\n SerializedDocument,\n} from \"@sofereditor/core\";\nimport {\n DEFAULT_PAGE_SETTINGS,\n isImageEmbed,\n isLegacySerializedDocument,\n pxToMm,\n type PageSettings,\n} from \"@sofereditor/core\";\nimport {\n AlignmentType,\n BorderStyle,\n Document,\n HeadingLevel,\n ImageRun,\n LevelFormat,\n Packer,\n Paragraph,\n PageOrientation,\n ShadingType,\n Table,\n TableCell,\n TableLayoutType,\n TableRow,\n TextRun,\n UnderlineType,\n VerticalAlign,\n WidthType,\n convertMillimetersToTwip,\n} from \"docx\";\n\nconst BULLET_REF = \"ed-bullet\";\nconst ORDERED_REF = \"ed-ordered\";\n\nexport interface ResolvedImage {\n data: Uint8Array;\n type: \"png\" | \"jpg\" | \"gif\" | \"bmp\" | \"svg\";\n}\n\nexport interface DocumentToDocxOptions {\n /** Document title; embedded in core properties. */\n title?: string;\n /** Author for core properties. */\n creator?: string;\n /**\n * Resolve um `src` de imagem para bytes. Default: data URLs (decodificadas\n * localmente) e http(s) via fetch. Retornar null pula a imagem (contada em\n * `skippedImages`) sem abortar o export.\n */\n resolveImage?: (src: string) => Promise<ResolvedImage | null>;\n}\n\n/**\n * Build a docx `Document` from a `SerializedDocument`. Output is a Blob you can\n * download with `downloadBlob` from `@sofereditor/export-pdf`.\n *\n * Mappings:\n * - paragraph / heading / blockquote / codeBlock → Paragraph (with `heading`,\n * shading, monospace font for code)\n * - listItem → Paragraph with `numbering: { reference, level }`\n * - table → docx `Table`. `rowspan`/`colspan` map to TableCell's `rowSpan`/\n * `columnSpan`; \"covered\" cells are skipped (docx infers cover from spans)\n * - inline marks → TextRun props (bold, italics, underline, strike, color,\n * font, size in half-points)\n * - image embeds → ImageRun with bytes resolved via `resolveImage` (default:\n * data URLs decoded locally, http(s) fetched; unresolved images are\n * skipped and counted in `skippedImages` instead of aborting the export)\n */\nexport async function documentToDocxBlob(\n doc: SerializedDocument | LegacySerializedDocument,\n options: DocumentToDocxOptions = {},\n): Promise<{ blob: Blob; skippedImages: number }> {\n const normalized = normalize(doc);\n const { images, skipped } = await resolveAllImages(\n normalized,\n options.resolveImage ?? defaultResolveImage,\n );\n const built = buildDocument(normalized, options, images);\n return { blob: await Packer.toBlob(built), skippedImages: skipped };\n}\n\n/** Same as `documentToDocxBlob` but returns a Uint8Array (useful in Node/tests). */\nexport async function documentToDocxBuffer(\n doc: SerializedDocument | LegacySerializedDocument,\n options: DocumentToDocxOptions = {},\n): Promise<{ buffer: Uint8Array; skippedImages: number }> {\n const normalized = normalize(doc);\n const { images, skipped } = await resolveAllImages(\n normalized,\n options.resolveImage ?? defaultResolveImage,\n );\n const built = buildDocument(normalized, options, images);\n const buf = await Packer.toBuffer(built);\n // `Packer.toBuffer` returns a Node Buffer at runtime — coerce for consumers.\n return {\n buffer: buf instanceof Uint8Array ? buf : new Uint8Array(buf),\n skippedImages: skipped,\n };\n}\n\nfunction normalize(doc: SerializedDocument | LegacySerializedDocument): SerializedDocument {\n return isLegacySerializedDocument(doc) ? { blocks: doc } : doc;\n}\n\nfunction buildDocument(\n doc: SerializedDocument,\n options: DocumentToDocxOptions,\n images: Map<string, ResolvedImage | null>,\n): Document {\n const children = blocksToDocxChildren(doc, images);\n const settings = doc.pageSettings ?? DEFAULT_PAGE_SETTINGS;\n return new Document({\n creator: options.creator,\n title: options.title,\n numbering: {\n config: [\n {\n reference: BULLET_REF,\n levels: buildBulletLevels(),\n },\n {\n reference: ORDERED_REF,\n levels: buildOrderedLevels(),\n },\n ],\n },\n sections: [\n {\n properties: sectionPropertiesFor(settings),\n children,\n },\n ],\n });\n}\n\nfunction sectionPropertiesFor(settings: PageSettings) {\n return {\n page: {\n size: {\n width: convertMillimetersToTwip(pxToMm(settings.width)),\n height: convertMillimetersToTwip(pxToMm(settings.height)),\n orientation: PageOrientation.PORTRAIT,\n },\n margin: {\n top: convertMillimetersToTwip(pxToMm(settings.marginTop)),\n bottom: convertMillimetersToTwip(pxToMm(settings.marginBottom)),\n left: convertMillimetersToTwip(pxToMm(settings.marginLeft)),\n right: convertMillimetersToTwip(pxToMm(settings.marginRight)),\n },\n },\n };\n}\n\nfunction blocksToDocxChildren(\n doc: SerializedDocument,\n images: Map<string, ResolvedImage | null>,\n): Array<Paragraph | Table> {\n const out: Array<Paragraph | Table> = [];\n const blocks = doc.blocks;\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n switch (block.type) {\n case \"paragraph\":\n out.push(makeParagraph(block, images));\n break;\n case \"heading\":\n out.push(makeHeading(block, images));\n break;\n case \"blockquote\":\n out.push(makeBlockquote(block, images));\n break;\n case \"codeBlock\":\n out.push(makeCodeBlock(block, images));\n break;\n case \"listItem\":\n out.push(makeListItem(block, images));\n break;\n case \"table\":\n out.push(makeTable(block, images));\n break;\n default:\n out.push(makeParagraph(block, images));\n }\n }\n return out;\n}\n\n// ---------- block builders ----------\n\n// School standard: Arial for all body text. Code blocks override with\n// Consolas (monospace is more legible for code). Italics on blockquote is\n// kept as a stylistic default.\nconst ARIAL: RunDefaults = { font: \"Arial\" };\n\nfunction makeParagraph(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n return new Paragraph({\n alignment: alignFor(block.attrs.align),\n bidirectional: block.attrs.dir === \"rtl\" ? true : undefined,\n children: deltaToRuns(block.delta, ARIAL, images),\n });\n}\n\nfunction makeHeading(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n const level = clampHeadingLevel(block.attrs.level);\n return new Paragraph({\n heading: HEADING_LEVELS[level - 1],\n alignment: alignFor(block.attrs.align),\n children: deltaToRuns(block.delta, ARIAL, images),\n });\n}\n\nfunction makeBlockquote(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n return new Paragraph({\n alignment: alignFor(block.attrs.align),\n indent: { left: 480 }, // ~0.25\"\n border: {\n left: { color: \"CBD5E1\", space: 12, style: BorderStyle.SINGLE, size: 12 },\n },\n children: deltaToRuns(block.delta, { ...ARIAL, italics: true }, images),\n });\n}\n\nfunction makeCodeBlock(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n return new Paragraph({\n spacing: { before: 100, after: 100 },\n shading: { type: ShadingType.CLEAR, color: \"auto\", fill: \"F1F5F9\" },\n children: deltaToRuns(block.delta, { font: \"Consolas\", size: 20 }, images),\n });\n}\n\nfunction makeListItem(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n const kind: ListKind = block.attrs.listKind === \"ordered\" ? \"ordered\" : \"bullet\";\n const reference = kind === \"ordered\" ? ORDERED_REF : BULLET_REF;\n const level = clampListLevel(block.attrs.listLevel);\n return new Paragraph({\n numbering: { reference, level },\n alignment: alignFor(block.attrs.align),\n children: deltaToRuns(block.delta, ARIAL, images),\n });\n}\n\nfunction makeTable(block: SerializedBlock, images: Map<string, ResolvedImage | null>): Table {\n const rows = typeof block.attrs.rows === \"number\" ? block.attrs.rows : 0;\n const cols = typeof block.attrs.cols === \"number\" ? block.attrs.cols : 0;\n const cells = block.cells ?? [];\n\n const rowsOut: TableRow[] = [];\n for (let r = 0; r < rows; r++) {\n const cellsOut: TableCell[] = [];\n for (let c = 0; c < cols; c++) {\n const cell = cells[r * cols + c];\n if (!cell) {\n cellsOut.push(emptyCell());\n continue;\n }\n if (cell.attrs?.covered) continue;\n cellsOut.push(makeCell(cell, images));\n }\n rowsOut.push(new TableRow({ children: cellsOut }));\n }\n\n // Validate the RAW array against `cols` — filtering invalid entries first\n // (e.g. dropping a negative width) would silently shift the remaining\n // widths onto the wrong columns while still passing a length check.\n const rawColWidths = block.attrs.colWidths;\n const isValidColWidths =\n Array.isArray(rawColWidths) &&\n cols > 0 &&\n rawColWidths.length === cols &&\n rawColWidths.every((w): w is number => typeof w === \"number\" && w > 0);\n const columnWidths = isValidColWidths\n ? rawColWidths.map((px) => convertMillimetersToTwip(pxToMm(px)))\n : undefined;\n\n return new Table({\n rows: rowsOut,\n ...(columnWidths\n ? {\n columnWidths,\n // Fix the layout so Word honors the column widths verbatim instead\n // of autofitting to content (the OOXML default when `tblLayout` is\n // absent, which would make `columnWidths` a mere hint).\n layout: TableLayoutType.FIXED,\n width: { size: columnWidths.reduce((a, b) => a + b, 0), type: WidthType.DXA },\n }\n : { width: { size: 100, type: WidthType.PERCENTAGE } }),\n });\n}\n\nfunction makeCell(cell: SerializedCell, images: Map<string, ResolvedImage | null>): TableCell {\n const span = cell.attrs?.colspan && cell.attrs.colspan > 1 ? cell.attrs.colspan : 1;\n const rowSpan = cell.attrs?.rowspan && cell.attrs.rowspan > 1 ? cell.attrs.rowspan : 1;\n const fill = cssColorToDocxHex(cell.attrs?.bgColor);\n return new TableCell({\n columnSpan: span,\n rowSpan,\n verticalAlign: VerticalAlign.TOP,\n shading: fill ? { type: ShadingType.CLEAR, color: \"auto\", fill } : undefined,\n children: [\n new Paragraph({\n alignment: alignFor(cell.attrs?.align),\n children: deltaToRuns(cell.delta, ARIAL, images),\n }),\n ],\n });\n}\n\nfunction emptyCell(): TableCell {\n return new TableCell({ children: [new Paragraph(\"\")] });\n}\n\n// ---------- inline ----------\n\ninterface RunDefaults {\n bold?: boolean;\n italics?: boolean;\n font?: string;\n size?: number;\n}\n\nfunction deltaToRuns(\n delta: DeltaOp[],\n defaults: RunDefaults = {},\n images: Map<string, ResolvedImage | null>,\n): Array<TextRun | ImageRun> {\n if (delta.length === 0) return [new TextRun(\"\")];\n const out: Array<TextRun | ImageRun> = [];\n for (const op of delta) {\n if (typeof op.insert === \"string\") {\n if (op.insert.length === 0) continue;\n out.push(makeTextRun(op.insert, op.attributes, defaults));\n } else if (isImageEmbed(op.insert)) {\n const embed = op.insert;\n const img = makeImageRun(embed, images);\n if (img) out.push(img);\n // Captions are emitted as italic-smaller text on a new line right after\n // the image. Word-style \"Caption\" paragraph would need a paragraph\n // break, which we can't do mid-delta — visually correct but loses the\n // semantic \"Caption\" style.\n if (embed.caption && embed.caption.length > 0) {\n out.push(\n new TextRun({\n text: embed.caption,\n italics: true,\n size: 18, // 9pt — typical caption size\n break: 1,\n }),\n );\n }\n }\n }\n if (out.length === 0) out.push(new TextRun(\"\"));\n return out;\n}\n\nfunction makeTextRun(\n text: string,\n marks: MarkAttrs | undefined,\n defaults: RunDefaults,\n): TextRun {\n const m = marks ?? {};\n const fontSize = parseFontSizeToHalfPoints(m.fontSize);\n // TextRun reads newlines via `break`; convert \\n into break runs.\n const segments = text.split(\"\\n\");\n if (segments.length === 1) {\n return new TextRun({\n text,\n bold: m.bold || defaults.bold,\n italics: m.italic || defaults.italics,\n underline: m.underline ? { type: UnderlineType.SINGLE } : undefined,\n strike: m.strike,\n color: cssColorToDocxHex(m.color),\n font: m.fontFamily ?? defaults.font,\n size: fontSize ?? defaults.size,\n });\n }\n // Multi-line: rejoin via TextRun children — but TextRun expects a single text\n // string. Approximate with non-printing line breaks: produce a sequence of\n // TextRuns separated by `new TextRun({ break: 1 })` packed into the parent.\n // Simpler: collapse to a single TextRun with the string, treating \\n as a\n // soft-break placeholder. Word renders \\n inside a w:t as a literal space,\n // so we replace with a small symbol-friendly fallback.\n return new TextRun({\n text: text.replace(/\\n/g, \" \"),\n bold: m.bold || defaults.bold,\n italics: m.italic || defaults.italics,\n underline: m.underline ? { type: UnderlineType.SINGLE } : undefined,\n strike: m.strike,\n color: cssColorToDocxHex(m.color),\n font: m.fontFamily ?? defaults.font,\n size: fontSize ?? defaults.size,\n });\n}\n\nfunction makeImageRun(\n embed: ImageEmbed,\n images: Map<string, ResolvedImage | null>,\n): ImageRun | null {\n const resolved = images.get(embed.src);\n if (!resolved) return null;\n return new ImageRun({\n data: resolved.data,\n type: resolved.type,\n transformation: {\n width: Math.max(1, Math.round(embed.width)),\n height: Math.max(1, Math.round(embed.height)),\n },\n } as ConstructorParameters<typeof ImageRun>[0]);\n}\n\ninterface DecodedImage {\n bytes: Uint8Array;\n kind: \"png\" | \"jpg\" | \"gif\" | \"bmp\" | \"svg\";\n}\n\nfunction decodeDataUrl(src: string): DecodedImage | null {\n const m = /^data:image\\/(png|jpe?g|gif|bmp|svg\\+xml);base64,(.+)$/i.exec(src);\n if (!m) return null;\n const fmt = m[1].toLowerCase();\n const kind: DecodedImage[\"kind\"] =\n fmt === \"jpeg\" || fmt === \"jpg\"\n ? \"jpg\"\n : fmt === \"svg+xml\"\n ? \"svg\"\n : (fmt as \"png\" | \"gif\" | \"bmp\");\n const base64 = m[2];\n const bytes = base64ToBytes(base64);\n return { bytes, kind };\n}\n\n/**\n * Default `resolveImage`: decode data URLs locally, fetch http(s) URLs, and\n * return null for anything else (caller counts it in `skippedImages`).\n */\nasync function defaultResolveImage(src: string): Promise<ResolvedImage | null> {\n const decoded = decodeDataUrl(src);\n if (decoded) return { data: decoded.bytes, type: decoded.kind };\n if (/^https?:\\/\\//i.test(src) && typeof fetch === \"function\") {\n try {\n const res = await fetch(src);\n if (!res.ok) return null;\n const ct = (res.headers.get(\"content-type\") ?? \"\").toLowerCase();\n const matchedType: ResolvedImage[\"type\"] | null = ct.includes(\"png\")\n ? \"png\"\n : ct.includes(\"jpeg\") || ct.includes(\"jpg\")\n ? \"jpg\"\n : ct.includes(\"gif\")\n ? \"gif\"\n : ct.includes(\"bmp\")\n ? \"bmp\"\n : ct.includes(\"svg\")\n ? \"svg\"\n : null;\n // Unknown content-type (e.g. image/webp) with no recognizable extension\n // in the URL: skip rather than mislabeling bytes as PNG (would produce\n // a broken image in Word — contract is \"failure to resolve → skip\").\n const type = matchedType ?? typeFromExtension(src);\n if (!type) return null;\n return { data: new Uint8Array(await res.arrayBuffer()), type };\n } catch {\n return null;\n }\n }\n return null;\n}\n\nfunction typeFromExtension(src: string): ResolvedImage[\"type\"] | null {\n const m = /\\.(png|jpe?g|gif|bmp|svg)(\\?|#|$)/i.exec(src);\n if (!m) return null;\n const ext = m[1].toLowerCase();\n return ext === \"jpeg\" || ext === \"jpg\" ? \"jpg\" : (ext as ResolvedImage[\"type\"]);\n}\n\n/**\n * Async pre-pass: collect every image `src` referenced by the document\n * (block deltas and table cell deltas), resolve them in parallel via\n * `resolve`, and return a lookup map plus the count of embeds that failed to\n * resolve. The rest of the build pipeline stays synchronous and reads from\n * this map.\n */\nasync function resolveAllImages(\n doc: SerializedDocument,\n resolve: (src: string) => Promise<ResolvedImage | null>,\n): Promise<{ images: Map<string, ResolvedImage | null>; skipped: number }> {\n const srcs = new Set<string>();\n const collect = (delta: DeltaOp[]) => {\n for (const op of delta) if (isImageEmbed(op.insert)) srcs.add(op.insert.src);\n };\n for (const block of doc.blocks) {\n collect(block.delta);\n for (const cell of block.cells ?? []) collect(cell.delta);\n }\n const images = new Map<string, ResolvedImage | null>();\n await Promise.all(\n [...srcs].map(async (src) => {\n let resolved: ResolvedImage | null = null;\n try {\n resolved = await resolve(src);\n } catch {\n resolved = null;\n }\n // docx's ImageRun requires a `fallback` for type \"svg\" (RegularImageOptions);\n // without it the constructor throws synchronously inside buildDocument,\n // which is outside this try/catch and would abort the whole export. Treat\n // svg as unresolved (skip) until we add fallback support.\n if (resolved?.type === \"svg\") resolved = null;\n images.set(src, resolved);\n }),\n );\n // skipped conta OCORRÊNCIAS de embed sem resolução (não srcs únicos).\n let skipped = 0;\n const count = (delta: DeltaOp[]) => {\n for (const op of delta)\n if (isImageEmbed(op.insert) && !images.get(op.insert.src)) skipped++;\n };\n for (const block of doc.blocks) {\n count(block.delta);\n for (const cell of block.cells ?? []) count(cell.delta);\n }\n return { images, skipped };\n}\n\nfunction base64ToBytes(b64: string): Uint8Array {\n if (typeof atob === \"function\") {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n // Node fallback.\n return new Uint8Array(Buffer.from(b64, \"base64\"));\n}\n\n// ---------- attribute mapping ----------\n\nconst HEADING_LEVELS = [\n HeadingLevel.HEADING_1,\n HeadingLevel.HEADING_2,\n HeadingLevel.HEADING_3,\n HeadingLevel.HEADING_4,\n HeadingLevel.HEADING_5,\n HeadingLevel.HEADING_6,\n];\n\nfunction clampHeadingLevel(l: unknown): 1 | 2 | 3 | 4 | 5 | 6 {\n if (typeof l !== \"number\") return 1;\n const n = Math.trunc(l);\n if (n < 1) return 1;\n if (n > 6) return 6;\n return n as 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nfunction clampListLevel(l: unknown): number {\n if (typeof l !== \"number\" || !Number.isFinite(l)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(l)));\n}\n\nfunction alignFor(a: AlignValue | undefined): (typeof AlignmentType)[keyof typeof AlignmentType] | undefined {\n switch (a) {\n case \"left\":\n return AlignmentType.LEFT;\n case \"center\":\n return AlignmentType.CENTER;\n case \"right\":\n return AlignmentType.RIGHT;\n case \"justify\":\n return AlignmentType.JUSTIFIED;\n default:\n return undefined;\n }\n}\n\nfunction cssColorToDocxHex(color: string | undefined): string | undefined {\n if (!color) return undefined;\n // docx expects RRGGBB without leading '#'.\n const c = color.trim();\n if (/^#[0-9a-f]{6}$/i.test(c)) return c.slice(1).toUpperCase();\n if (/^#[0-9a-f]{3}$/i.test(c)) {\n // expand short hex\n return c\n .slice(1)\n .split(\"\")\n .map((d) => d + d)\n .join(\"\")\n .toUpperCase();\n }\n const rgb = /^rgb\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/i.exec(c);\n if (rgb) {\n const r = Number(rgb[1]);\n const g = Number(rgb[2]);\n const b = Number(rgb[3]);\n return [r, g, b].map((n) => Math.max(0, Math.min(255, n)).toString(16).padStart(2, \"0\")).join(\"\").toUpperCase();\n }\n // Named colors / other forms — let docx fall back to default.\n return undefined;\n}\n\n/**\n * docx sizes are in half-points (24 = 12pt). Parse common CSS sizes.\n * Returns `undefined` when not parseable so callers can pick a default.\n */\nfunction parseFontSizeToHalfPoints(size: string | undefined): number | undefined {\n if (!size) return undefined;\n const s = size.trim();\n const m = /^(-?\\d*\\.?\\d+)\\s*(pt|px|em|rem)?$/i.exec(s);\n if (!m) return undefined;\n const n = Number(m[1]);\n if (!Number.isFinite(n) || n <= 0) return undefined;\n const unit = (m[2] ?? \"pt\").toLowerCase();\n const pt =\n unit === \"pt\"\n ? n\n : unit === \"px\"\n ? n * 0.75 // CSS px → pt at 96dpi\n : n * 12;\n return Math.round(pt * 2);\n}\n\n// ---------- list numbering definitions ----------\n\nfunction buildBulletLevels(): Array<{\n level: number;\n format: (typeof LevelFormat)[keyof typeof LevelFormat];\n text: string;\n alignment: (typeof AlignmentType)[keyof typeof AlignmentType];\n style?: { paragraph?: { indent?: { left: number; hanging: number } } };\n}> {\n const symbols = [\"•\", \"◦\", \"▪\", \"•\", \"◦\", \"▪\"];\n return symbols.map((sym, i) => ({\n level: i,\n format: LevelFormat.BULLET,\n text: sym,\n alignment: AlignmentType.LEFT,\n style: {\n paragraph: { indent: { left: 360 + i * 360, hanging: 260 } },\n },\n }));\n}\n\nfunction buildOrderedLevels(): Array<{\n level: number;\n format: (typeof LevelFormat)[keyof typeof LevelFormat];\n text: string;\n alignment: (typeof AlignmentType)[keyof typeof AlignmentType];\n style?: { paragraph?: { indent?: { left: number; hanging: number } } };\n}> {\n const formats = [\n LevelFormat.DECIMAL,\n LevelFormat.LOWER_LETTER,\n LevelFormat.LOWER_ROMAN,\n LevelFormat.DECIMAL,\n LevelFormat.LOWER_LETTER,\n LevelFormat.LOWER_ROMAN,\n ];\n return formats.map((format, i) => ({\n level: i,\n format,\n text: `%${i + 1}.`,\n alignment: AlignmentType.LEFT,\n style: {\n paragraph: { indent: { left: 360 + i * 360, hanging: 260 } },\n },\n }));\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/docx.ts"],"sourcesContent":["import type {\n AlignValue,\n DeltaOp,\n ImageEmbed,\n LegacySerializedDocument,\n ListKind,\n MarkAttrs,\n SerializedBlock,\n SerializedCell,\n SerializedDocument,\n TableBorderPreset,\n} from \"@sofereditor/core\";\nimport {\n DEFAULT_PAGE_SETTINGS,\n isImageEmbed,\n isLegacySerializedDocument,\n pxToMm,\n type PageSettings,\n} from \"@sofereditor/core\";\nimport {\n AlignmentType,\n BorderStyle,\n Document,\n HeadingLevel,\n ImageRun,\n LevelFormat,\n LineRuleType,\n Packer,\n Paragraph,\n PageOrientation,\n ShadingType,\n Table,\n TableCell,\n TableLayoutType,\n TableRow,\n TextRun,\n UnderlineType,\n VerticalAlign,\n WidthType,\n convertMillimetersToTwip,\n} from \"docx\";\n\nconst BULLET_REF = \"ed-bullet\";\nconst ORDERED_REF = \"ed-ordered\";\n\nexport interface ResolvedImage {\n data: Uint8Array;\n type: \"png\" | \"jpg\" | \"gif\" | \"bmp\" | \"svg\";\n}\n\nexport interface DocumentToDocxOptions {\n /** Document title; embedded in core properties. */\n title?: string;\n /** Author for core properties. */\n creator?: string;\n /**\n * Resolve um `src` de imagem para bytes. Default: data URLs (decodificadas\n * localmente) e http(s) via fetch. Retornar null pula a imagem (contada em\n * `skippedImages`) sem abortar o export.\n */\n resolveImage?: (src: string) => Promise<ResolvedImage | null>;\n}\n\n/**\n * Build a docx `Document` from a `SerializedDocument`. Output is a Blob you can\n * download with `downloadBlob` from `@sofereditor/export-pdf`.\n *\n * Mappings:\n * - paragraph / heading / blockquote / codeBlock → Paragraph (with `heading`,\n * shading, monospace font for code)\n * - listItem → Paragraph with `numbering: { reference, level }`\n * - table → docx `Table`. `rowspan`/`colspan` map to TableCell's `rowSpan`/\n * `columnSpan`; \"covered\" cells are skipped (docx infers cover from spans)\n * - inline marks → TextRun props (bold, italics, underline, strike, color,\n * font, size in half-points)\n * - image embeds → ImageRun with bytes resolved via `resolveImage` (default:\n * data URLs decoded locally, http(s) fetched; unresolved images are\n * skipped and counted in `skippedImages` instead of aborting the export)\n */\nexport async function documentToDocxBlob(\n doc: SerializedDocument | LegacySerializedDocument,\n options: DocumentToDocxOptions = {},\n): Promise<{ blob: Blob; skippedImages: number }> {\n const normalized = normalize(doc);\n const { images, skipped } = await resolveAllImages(\n normalized,\n options.resolveImage ?? defaultResolveImage,\n );\n const built = buildDocument(normalized, options, images);\n return { blob: await Packer.toBlob(built), skippedImages: skipped };\n}\n\n/** Same as `documentToDocxBlob` but returns a Uint8Array (useful in Node/tests). */\nexport async function documentToDocxBuffer(\n doc: SerializedDocument | LegacySerializedDocument,\n options: DocumentToDocxOptions = {},\n): Promise<{ buffer: Uint8Array; skippedImages: number }> {\n const normalized = normalize(doc);\n const { images, skipped } = await resolveAllImages(\n normalized,\n options.resolveImage ?? defaultResolveImage,\n );\n const built = buildDocument(normalized, options, images);\n const buf = await Packer.toBuffer(built);\n // `Packer.toBuffer` returns a Node Buffer at runtime — coerce for consumers.\n return {\n buffer: buf instanceof Uint8Array ? buf : new Uint8Array(buf),\n skippedImages: skipped,\n };\n}\n\nfunction normalize(doc: SerializedDocument | LegacySerializedDocument): SerializedDocument {\n return isLegacySerializedDocument(doc) ? { blocks: doc } : doc;\n}\n\nfunction buildDocument(\n doc: SerializedDocument,\n options: DocumentToDocxOptions,\n images: Map<string, ResolvedImage | null>,\n): Document {\n const children = blocksToDocxChildren(doc, images);\n const settings = doc.pageSettings ?? DEFAULT_PAGE_SETTINGS;\n return new Document({\n creator: options.creator,\n title: options.title,\n numbering: {\n config: [\n {\n reference: BULLET_REF,\n levels: buildBulletLevels(),\n },\n {\n reference: ORDERED_REF,\n levels: buildOrderedLevels(),\n },\n ],\n },\n sections: [\n {\n properties: sectionPropertiesFor(settings),\n children,\n },\n ],\n });\n}\n\nfunction sectionPropertiesFor(settings: PageSettings) {\n return {\n page: {\n size: {\n width: convertMillimetersToTwip(pxToMm(settings.width)),\n height: convertMillimetersToTwip(pxToMm(settings.height)),\n orientation: PageOrientation.PORTRAIT,\n },\n margin: {\n top: convertMillimetersToTwip(pxToMm(settings.marginTop)),\n bottom: convertMillimetersToTwip(pxToMm(settings.marginBottom)),\n left: convertMillimetersToTwip(pxToMm(settings.marginLeft)),\n right: convertMillimetersToTwip(pxToMm(settings.marginRight)),\n },\n },\n };\n}\n\nfunction blocksToDocxChildren(\n doc: SerializedDocument,\n images: Map<string, ResolvedImage | null>,\n): Array<Paragraph | Table> {\n const out: Array<Paragraph | Table> = [];\n const blocks = doc.blocks;\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n switch (block.type) {\n case \"paragraph\":\n out.push(makeParagraph(block, images));\n break;\n case \"heading\":\n out.push(makeHeading(block, images));\n break;\n case \"blockquote\":\n out.push(makeBlockquote(block, images));\n break;\n case \"codeBlock\":\n out.push(makeCodeBlock(block, images));\n break;\n case \"listItem\":\n out.push(makeListItem(block, images));\n break;\n case \"table\":\n out.push(makeTable(block, images));\n break;\n default:\n out.push(makeParagraph(block, images));\n }\n }\n return out;\n}\n\n// ---------- block builders ----------\n\n// School standard: Arial for all body text. Code blocks override with\n// Consolas (monospace is more legible for code). Italics on blockquote is\n// kept as a stylistic default.\nconst ARIAL: RunDefaults = { font: \"Arial\" };\n\nfunction makeParagraph(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n return new Paragraph({\n alignment: alignFor(block.attrs.align),\n bidirectional: block.attrs.dir === \"rtl\" ? true : undefined,\n ...answerLineProps(block.attrs),\n children: deltaToRuns(block.delta, ARIAL, images),\n });\n}\n\n/**\n * Linha de resposta → borda inferior de parágrafo (`w:pBdr`) + entrelinha.\n * É como um documento profissional desenha uma pauta: a régua acompanha a\n * margem automaticamente, ao contrário de uma fileira de underlines.\n *\n * 240 twips = uma linha simples no OOXML.\n */\nfunction answerLineProps(attrs: SerializedBlock[\"attrs\"]) {\n if (attrs.answerLine !== true) return {};\n const spacing = attrs.answerLineSpacing ?? 1;\n return {\n border: {\n bottom: { style: BorderStyle.SINGLE, size: 6, color: \"000000\", space: 1 },\n },\n spacing: { line: Math.round(240 * spacing), lineRule: LineRuleType.AUTO },\n };\n}\n\nfunction makeHeading(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n const level = clampHeadingLevel(block.attrs.level);\n return new Paragraph({\n heading: HEADING_LEVELS[level - 1],\n alignment: alignFor(block.attrs.align),\n children: deltaToRuns(block.delta, ARIAL, images),\n });\n}\n\nfunction makeBlockquote(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n return new Paragraph({\n alignment: alignFor(block.attrs.align),\n indent: { left: 480 }, // ~0.25\"\n border: {\n left: { color: \"CBD5E1\", space: 12, style: BorderStyle.SINGLE, size: 12 },\n },\n children: deltaToRuns(block.delta, { ...ARIAL, italics: true }, images),\n });\n}\n\nfunction makeCodeBlock(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n return new Paragraph({\n spacing: { before: 100, after: 100 },\n shading: { type: ShadingType.CLEAR, color: \"auto\", fill: \"F1F5F9\" },\n children: deltaToRuns(block.delta, { font: \"Consolas\", size: 20 }, images),\n });\n}\n\nfunction makeListItem(\n block: SerializedBlock,\n images: Map<string, ResolvedImage | null>,\n): Paragraph {\n const kind: ListKind = block.attrs.listKind === \"ordered\" ? \"ordered\" : \"bullet\";\n const reference = kind === \"ordered\" ? ORDERED_REF : BULLET_REF;\n const level = clampListLevel(block.attrs.listLevel);\n return new Paragraph({\n numbering: { reference, level },\n alignment: alignFor(block.attrs.align),\n children: deltaToRuns(block.delta, ARIAL, images),\n });\n}\n\nfunction makeTable(block: SerializedBlock, images: Map<string, ResolvedImage | null>): Table {\n const rows = typeof block.attrs.rows === \"number\" ? block.attrs.rows : 0;\n const cols = typeof block.attrs.cols === \"number\" ? block.attrs.cols : 0;\n const cells = block.cells ?? [];\n\n const rowsOut: TableRow[] = [];\n for (let r = 0; r < rows; r++) {\n const cellsOut: TableCell[] = [];\n for (let c = 0; c < cols; c++) {\n const cell = cells[r * cols + c];\n if (!cell) {\n cellsOut.push(emptyCell());\n continue;\n }\n if (cell.attrs?.covered) continue;\n cellsOut.push(makeCell(cell, images));\n }\n rowsOut.push(new TableRow({ children: cellsOut }));\n }\n\n // Validate the RAW array against `cols` — filtering invalid entries first\n // (e.g. dropping a negative width) would silently shift the remaining\n // widths onto the wrong columns while still passing a length check.\n const rawColWidths = block.attrs.colWidths;\n const isValidColWidths =\n Array.isArray(rawColWidths) &&\n cols > 0 &&\n rawColWidths.length === cols &&\n rawColWidths.every((w): w is number => typeof w === \"number\" && w > 0);\n const columnWidths = isValidColWidths\n ? rawColWidths.map((px) => convertMillimetersToTwip(pxToMm(px)))\n : undefined;\n\n return new Table({\n rows: rowsOut,\n borders: docxTableBorders(block.attrs.borderPreset),\n ...(columnWidths\n ? {\n columnWidths,\n // Fix the layout so Word honors the column widths verbatim instead\n // of autofitting to content (the OOXML default when `tblLayout` is\n // absent, which would make `columnWidths` a mere hint).\n layout: TableLayoutType.FIXED,\n width: { size: columnWidths.reduce((a, b) => a + b, 0), type: WidthType.DXA },\n }\n : { width: { size: 100, type: WidthType.PERCENTAGE } }),\n });\n}\n\n/**\n * Traduz o preset para `w:tblBorders`. Lado desligado = `BorderStyle.NONE`.\n *\n * A tabela-verdade sai direto de `cellBorderColors`: o que aquele helper liga em\n * TODA célula vira o par externo MAIS o interno correspondente. Ex.: em\n * `vertical`, toda célula tem left/right ligados — logo as laterais externas\n * (left/right) E as internas (insideV) ficam ligadas, e topo/base\n * (top/bottom/insideH) desligados.\n *\n * A tabela é a especificação; a função sai dela, não o contrário.\n */\nfunction docxTableBorders(preset: TableBorderPreset | undefined) {\n const on = { style: BorderStyle.SINGLE, size: 6, color: \"CBD5E1\" };\n const off = { style: BorderStyle.NONE, size: 0, color: \"auto\" };\n const s = (visivel: boolean) => (visivel ? on : off);\n\n // [top/bottom, left/right, insideH, insideV]\n const TRUTH: Record<TableBorderPreset, [boolean, boolean, boolean, boolean]> = {\n all: [true, true, true, true],\n outer: [true, true, false, false],\n horizontal: [true, false, true, false],\n vertical: [false, true, false, true],\n none: [false, false, false, false],\n };\n const [tb, lr, iH, iV] = TRUTH[preset ?? \"all\"];\n return {\n top: s(tb),\n bottom: s(tb),\n left: s(lr),\n right: s(lr),\n insideHorizontal: s(iH),\n insideVertical: s(iV),\n };\n}\n\nfunction makeCell(cell: SerializedCell, images: Map<string, ResolvedImage | null>): TableCell {\n const span = cell.attrs?.colspan && cell.attrs.colspan > 1 ? cell.attrs.colspan : 1;\n const rowSpan = cell.attrs?.rowspan && cell.attrs.rowspan > 1 ? cell.attrs.rowspan : 1;\n const fill = cssColorToDocxHex(cell.attrs?.bgColor);\n return new TableCell({\n columnSpan: span,\n rowSpan,\n verticalAlign: VerticalAlign.TOP,\n shading: fill ? { type: ShadingType.CLEAR, color: \"auto\", fill } : undefined,\n children: [\n new Paragraph({\n alignment: alignFor(cell.attrs?.align),\n children: deltaToRuns(cell.delta, ARIAL, images),\n }),\n ],\n });\n}\n\nfunction emptyCell(): TableCell {\n return new TableCell({ children: [new Paragraph(\"\")] });\n}\n\n// ---------- inline ----------\n\ninterface RunDefaults {\n bold?: boolean;\n italics?: boolean;\n font?: string;\n size?: number;\n}\n\nfunction deltaToRuns(\n delta: DeltaOp[],\n defaults: RunDefaults = {},\n images: Map<string, ResolvedImage | null>,\n): Array<TextRun | ImageRun> {\n if (delta.length === 0) return [new TextRun(\"\")];\n const out: Array<TextRun | ImageRun> = [];\n for (const op of delta) {\n if (typeof op.insert === \"string\") {\n if (op.insert.length === 0) continue;\n out.push(makeTextRun(op.insert, op.attributes, defaults));\n } else if (isImageEmbed(op.insert)) {\n const embed = op.insert;\n const img = makeImageRun(embed, images);\n if (img) out.push(img);\n // Captions are emitted as italic-smaller text on a new line right after\n // the image. Word-style \"Caption\" paragraph would need a paragraph\n // break, which we can't do mid-delta — visually correct but loses the\n // semantic \"Caption\" style.\n if (embed.caption && embed.caption.length > 0) {\n out.push(\n new TextRun({\n text: embed.caption,\n italics: true,\n size: 18, // 9pt — typical caption size\n break: 1,\n }),\n );\n }\n }\n }\n if (out.length === 0) out.push(new TextRun(\"\"));\n return out;\n}\n\n/**\n * Props de um `TextRun` a partir das marks. Fica numa função só porque\n * `makeTextRun` tem dois caminhos (linha única e multi-linha) que antes\n * duplicavam esta lista inteira — uma mark acrescentada em apenas um dos dois\n * se perdia silenciosamente em texto com quebra de linha.\n *\n * `highlight` sai como `w:shd` e NÃO como `w:highlight`: este último só aceita\n * ~15 valores nomeados e não sobreviveria a uma cor arbitrária do picker.\n */\nfunction textRunProps(text: string, m: MarkAttrs, defaults: RunDefaults) {\n const fill = cssColorToDocxHex(m.highlight);\n return {\n text,\n bold: m.bold || defaults.bold,\n italics: m.italic || defaults.italics,\n underline: m.underline ? { type: UnderlineType.SINGLE } : undefined,\n strike: m.strike,\n color: cssColorToDocxHex(m.color),\n font: m.fontFamily ?? defaults.font,\n size: parseFontSizeToHalfPoints(m.fontSize) ?? defaults.size,\n shading: fill ? { type: ShadingType.CLEAR, color: \"auto\", fill } : undefined,\n };\n}\n\nfunction makeTextRun(\n text: string,\n marks: MarkAttrs | undefined,\n defaults: RunDefaults,\n): TextRun {\n const m = marks ?? {};\n // TextRun reads newlines via `break`; convert \\n into break runs.\n const segments = text.split(\"\\n\");\n if (segments.length === 1) {\n return new TextRun(textRunProps(text, m, defaults));\n }\n // Multi-line: rejoin via TextRun children — but TextRun expects a single text\n // string. Approximate with non-printing line breaks: produce a sequence of\n // TextRuns separated by `new TextRun({ break: 1 })` packed into the parent.\n // Simpler: collapse to a single TextRun with the string, treating \\n as a\n // soft-break placeholder. Word renders \\n inside a w:t as a literal space,\n // so we replace with a small symbol-friendly fallback.\n return new TextRun(textRunProps(text.replace(/\\n/g, \" \"), m, defaults));\n}\n\nfunction makeImageRun(\n embed: ImageEmbed,\n images: Map<string, ResolvedImage | null>,\n): ImageRun | null {\n const resolved = images.get(embed.src);\n if (!resolved) return null;\n return new ImageRun({\n data: resolved.data,\n type: resolved.type,\n transformation: {\n width: Math.max(1, Math.round(embed.width)),\n height: Math.max(1, Math.round(embed.height)),\n },\n } as ConstructorParameters<typeof ImageRun>[0]);\n}\n\ninterface DecodedImage {\n bytes: Uint8Array;\n kind: \"png\" | \"jpg\" | \"gif\" | \"bmp\" | \"svg\";\n}\n\nfunction decodeDataUrl(src: string): DecodedImage | null {\n const m = /^data:image\\/(png|jpe?g|gif|bmp|svg\\+xml);base64,(.+)$/i.exec(src);\n if (!m) return null;\n const fmt = m[1].toLowerCase();\n const kind: DecodedImage[\"kind\"] =\n fmt === \"jpeg\" || fmt === \"jpg\"\n ? \"jpg\"\n : fmt === \"svg+xml\"\n ? \"svg\"\n : (fmt as \"png\" | \"gif\" | \"bmp\");\n const base64 = m[2];\n const bytes = base64ToBytes(base64);\n return { bytes, kind };\n}\n\n/**\n * Default `resolveImage`: decode data URLs locally, fetch http(s) URLs, and\n * return null for anything else (caller counts it in `skippedImages`).\n */\nasync function defaultResolveImage(src: string): Promise<ResolvedImage | null> {\n const decoded = decodeDataUrl(src);\n if (decoded) return { data: decoded.bytes, type: decoded.kind };\n if (/^https?:\\/\\//i.test(src) && typeof fetch === \"function\") {\n try {\n const res = await fetch(src);\n if (!res.ok) return null;\n const ct = (res.headers.get(\"content-type\") ?? \"\").toLowerCase();\n const matchedType: ResolvedImage[\"type\"] | null = ct.includes(\"png\")\n ? \"png\"\n : ct.includes(\"jpeg\") || ct.includes(\"jpg\")\n ? \"jpg\"\n : ct.includes(\"gif\")\n ? \"gif\"\n : ct.includes(\"bmp\")\n ? \"bmp\"\n : ct.includes(\"svg\")\n ? \"svg\"\n : null;\n // Unknown content-type (e.g. image/webp) with no recognizable extension\n // in the URL: skip rather than mislabeling bytes as PNG (would produce\n // a broken image in Word — contract is \"failure to resolve → skip\").\n const type = matchedType ?? typeFromExtension(src);\n if (!type) return null;\n return { data: new Uint8Array(await res.arrayBuffer()), type };\n } catch {\n return null;\n }\n }\n return null;\n}\n\nfunction typeFromExtension(src: string): ResolvedImage[\"type\"] | null {\n const m = /\\.(png|jpe?g|gif|bmp|svg)(\\?|#|$)/i.exec(src);\n if (!m) return null;\n const ext = m[1].toLowerCase();\n return ext === \"jpeg\" || ext === \"jpg\" ? \"jpg\" : (ext as ResolvedImage[\"type\"]);\n}\n\n/**\n * Async pre-pass: collect every image `src` referenced by the document\n * (block deltas and table cell deltas), resolve them in parallel via\n * `resolve`, and return a lookup map plus the count of embeds that failed to\n * resolve. The rest of the build pipeline stays synchronous and reads from\n * this map.\n */\nasync function resolveAllImages(\n doc: SerializedDocument,\n resolve: (src: string) => Promise<ResolvedImage | null>,\n): Promise<{ images: Map<string, ResolvedImage | null>; skipped: number }> {\n const srcs = new Set<string>();\n const collect = (delta: DeltaOp[]) => {\n for (const op of delta) if (isImageEmbed(op.insert)) srcs.add(op.insert.src);\n };\n for (const block of doc.blocks) {\n collect(block.delta);\n for (const cell of block.cells ?? []) collect(cell.delta);\n }\n const images = new Map<string, ResolvedImage | null>();\n await Promise.all(\n [...srcs].map(async (src) => {\n let resolved: ResolvedImage | null = null;\n try {\n resolved = await resolve(src);\n } catch {\n resolved = null;\n }\n // docx's ImageRun requires a `fallback` for type \"svg\" (RegularImageOptions);\n // without it the constructor throws synchronously inside buildDocument,\n // which is outside this try/catch and would abort the whole export. Treat\n // svg as unresolved (skip) until we add fallback support.\n if (resolved?.type === \"svg\") resolved = null;\n images.set(src, resolved);\n }),\n );\n // skipped conta OCORRÊNCIAS de embed sem resolução (não srcs únicos).\n let skipped = 0;\n const count = (delta: DeltaOp[]) => {\n for (const op of delta)\n if (isImageEmbed(op.insert) && !images.get(op.insert.src)) skipped++;\n };\n for (const block of doc.blocks) {\n count(block.delta);\n for (const cell of block.cells ?? []) count(cell.delta);\n }\n return { images, skipped };\n}\n\nfunction base64ToBytes(b64: string): Uint8Array {\n if (typeof atob === \"function\") {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n // Node fallback.\n return new Uint8Array(Buffer.from(b64, \"base64\"));\n}\n\n// ---------- attribute mapping ----------\n\nconst HEADING_LEVELS = [\n HeadingLevel.HEADING_1,\n HeadingLevel.HEADING_2,\n HeadingLevel.HEADING_3,\n HeadingLevel.HEADING_4,\n HeadingLevel.HEADING_5,\n HeadingLevel.HEADING_6,\n];\n\nfunction clampHeadingLevel(l: unknown): 1 | 2 | 3 | 4 | 5 | 6 {\n if (typeof l !== \"number\") return 1;\n const n = Math.trunc(l);\n if (n < 1) return 1;\n if (n > 6) return 6;\n return n as 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nfunction clampListLevel(l: unknown): number {\n if (typeof l !== \"number\" || !Number.isFinite(l)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(l)));\n}\n\nfunction alignFor(a: AlignValue | undefined): (typeof AlignmentType)[keyof typeof AlignmentType] | undefined {\n switch (a) {\n case \"left\":\n return AlignmentType.LEFT;\n case \"center\":\n return AlignmentType.CENTER;\n case \"right\":\n return AlignmentType.RIGHT;\n case \"justify\":\n return AlignmentType.JUSTIFIED;\n default:\n return undefined;\n }\n}\n\nfunction cssColorToDocxHex(color: string | undefined): string | undefined {\n if (!color) return undefined;\n // docx expects RRGGBB without leading '#'.\n const c = color.trim();\n if (/^#[0-9a-f]{6}$/i.test(c)) return c.slice(1).toUpperCase();\n if (/^#[0-9a-f]{3}$/i.test(c)) {\n // expand short hex\n return c\n .slice(1)\n .split(\"\")\n .map((d) => d + d)\n .join(\"\")\n .toUpperCase();\n }\n const rgb = /^rgb\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/i.exec(c);\n if (rgb) {\n const r = Number(rgb[1]);\n const g = Number(rgb[2]);\n const b = Number(rgb[3]);\n return [r, g, b].map((n) => Math.max(0, Math.min(255, n)).toString(16).padStart(2, \"0\")).join(\"\").toUpperCase();\n }\n // Named colors / other forms — let docx fall back to default.\n return undefined;\n}\n\n/**\n * docx sizes are in half-points (24 = 12pt). Parse common CSS sizes.\n * Returns `undefined` when not parseable so callers can pick a default.\n */\nfunction parseFontSizeToHalfPoints(size: string | undefined): number | undefined {\n if (!size) return undefined;\n const s = size.trim();\n const m = /^(-?\\d*\\.?\\d+)\\s*(pt|px|em|rem)?$/i.exec(s);\n if (!m) return undefined;\n const n = Number(m[1]);\n if (!Number.isFinite(n) || n <= 0) return undefined;\n const unit = (m[2] ?? \"pt\").toLowerCase();\n const pt =\n unit === \"pt\"\n ? n\n : unit === \"px\"\n ? n * 0.75 // CSS px → pt at 96dpi\n : n * 12;\n return Math.round(pt * 2);\n}\n\n// ---------- list numbering definitions ----------\n\nfunction buildBulletLevels(): Array<{\n level: number;\n format: (typeof LevelFormat)[keyof typeof LevelFormat];\n text: string;\n alignment: (typeof AlignmentType)[keyof typeof AlignmentType];\n style?: { paragraph?: { indent?: { left: number; hanging: number } } };\n}> {\n const symbols = [\"•\", \"◦\", \"▪\", \"•\", \"◦\", \"▪\"];\n return symbols.map((sym, i) => ({\n level: i,\n format: LevelFormat.BULLET,\n text: sym,\n alignment: AlignmentType.LEFT,\n style: {\n paragraph: { indent: { left: 360 + i * 360, hanging: 260 } },\n },\n }));\n}\n\nfunction buildOrderedLevels(): Array<{\n level: number;\n format: (typeof LevelFormat)[keyof typeof LevelFormat];\n text: string;\n alignment: (typeof AlignmentType)[keyof typeof AlignmentType];\n style?: { paragraph?: { indent?: { left: number; hanging: number } } };\n}> {\n const formats = [\n LevelFormat.DECIMAL,\n LevelFormat.LOWER_LETTER,\n LevelFormat.LOWER_ROMAN,\n LevelFormat.DECIMAL,\n LevelFormat.LOWER_LETTER,\n LevelFormat.LOWER_ROMAN,\n ];\n return formats.map((format, i) => ({\n level: i,\n format,\n text: `%${i + 1}.`,\n alignment: AlignmentType.LEFT,\n style: {\n paragraph: { indent: { left: 360 + i * 360, hanging: 260 } },\n },\n }));\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sofereditor/export-docx",
3
- "version": "0.3.4",
3
+ "version": "0.4.0",
4
4
  "description": "@sofereditor/export-docx — Emit .docx from a Sofer SerializedDocument. Round-trip with @sofereditor/import-docx.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "author": "Rafael Marreca",
@@ -53,7 +53,7 @@
53
53
  "test": "vitest run"
54
54
  },
55
55
  "dependencies": {
56
- "@sofereditor/core": "^0.3.2",
56
+ "@sofereditor/core": "^0.4.0",
57
57
  "docx": "^9.6.1"
58
58
  },
59
59
  "devDependencies": {