reamkit 1.29.0 → 1.30.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.
Files changed (50) hide show
  1. package/dist/esm/core/document-model/types.d.ts +2 -0
  2. package/dist/esm/core/drawingml/shape-render.js +13 -1
  3. package/dist/esm/core/fonts/index.d.ts +1 -1
  4. package/dist/esm/core/fonts/remote-fonts.d.ts +8 -0
  5. package/dist/esm/core/fonts/remote-fonts.js +100 -17
  6. package/dist/esm/core/ir/flow.d.ts +17 -0
  7. package/dist/esm/index.d.ts +1 -1
  8. package/dist/esm/pdf-reader/annot-draw.js +93 -1
  9. package/dist/esm/pdf-reader/annots.d.ts +18 -0
  10. package/dist/esm/pdf-reader/annots.js +86 -9
  11. package/dist/esm/pdf-reader/cmap.js +5 -2
  12. package/dist/esm/pdf-reader/content.d.ts +17 -4
  13. package/dist/esm/pdf-reader/content.js +163 -11
  14. package/dist/esm/pdf-reader/display.js +16 -0
  15. package/dist/esm/pdf-reader/embedded-fonts.d.ts +24 -0
  16. package/dist/esm/pdf-reader/embedded-fonts.js +48 -9
  17. package/dist/esm/pdf-reader/flow-build.d.ts +96 -5
  18. package/dist/esm/pdf-reader/flow-build.js +210 -23
  19. package/dist/esm/pdf-reader/font.d.ts +27 -1
  20. package/dist/esm/pdf-reader/font.js +313 -29
  21. package/dist/esm/pdf-reader/glyf-outline.js +13 -2
  22. package/dist/esm/pdf-reader/glyph-shapes.d.ts +18 -0
  23. package/dist/esm/pdf-reader/glyph-shapes.js +57 -0
  24. package/dist/esm/pdf-reader/image-decode.js +70 -4
  25. package/dist/esm/pdf-reader/images.d.ts +5 -0
  26. package/dist/esm/pdf-reader/images.js +4 -2
  27. package/dist/esm/pdf-reader/jbig2.d.ts +40 -1
  28. package/dist/esm/pdf-reader/jbig2.js +78 -16
  29. package/dist/esm/pdf-reader/jpeg.d.ts +6 -3
  30. package/dist/esm/pdf-reader/jpeg.js +21 -1
  31. package/dist/esm/pdf-reader/layout.d.ts +26 -2
  32. package/dist/esm/pdf-reader/layout.js +638 -92
  33. package/dist/esm/pdf-reader/lexer.d.ts +10 -0
  34. package/dist/esm/pdf-reader/lexer.js +17 -0
  35. package/dist/esm/pdf-reader/pattern-tint.d.ts +11 -1
  36. package/dist/esm/pdf-reader/pattern-tint.js +21 -3
  37. package/dist/esm/pdf-reader/regions.d.ts +25 -0
  38. package/dist/esm/pdf-reader/regions.js +167 -0
  39. package/dist/esm/pdf-reader/shading.d.ts +58 -2
  40. package/dist/esm/pdf-reader/shading.js +181 -11
  41. package/dist/esm/pdf-reader/struct-tree.js +112 -8
  42. package/dist/esm/pdf-reader/tagged.js +27 -7
  43. package/dist/esm/pdf-reader/text-rules.js +1 -1
  44. package/dist/esm/pdf-reader/text.js +9 -14
  45. package/dist/esm/pdf-reader/vector.d.ts +8 -2
  46. package/dist/esm/pdf-reader/vector.js +34 -5
  47. package/dist/esm/word/docx-writer.js +137 -36
  48. package/dist/esm/word/drawing-parser.js +7 -2
  49. package/dist/esm/word/paragraph-properties.js +2 -0
  50. package/package.json +5 -3
@@ -17,6 +17,9 @@ var REL_NUMBERING = "http://schemas.openxmlformats.org/officeDocument/2006/relat
17
17
  var REL_HYPERLINK = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
18
18
  var REL_IMAGE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
19
19
  var NUMBERING_PART = "word/numbering.xml";
20
+ var REL_FONT_TABLE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable";
21
+ var FONT_TABLE_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml";
22
+ var FONT_TABLE_PART = "word/fontTable.xml";
20
23
  var REL_FOOTNOTES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes";
21
24
  var REL_ENDNOTES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes";
22
25
  var FOOTNOTES_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml";
@@ -127,7 +130,10 @@ function writeDocx(flow) {
127
130
  drawingSeq: 0,
128
131
  chartParts: [],
129
132
  chartSeq: 0,
130
- ...flow.charts ? { charts: flow.charts } : {}
133
+ ...flow.charts ? { charts: flow.charts } : {},
134
+ ...flow.faceFamilies ? { faceFamilies: flow.faceFamilies } : {},
135
+ familiesUsed: /* @__PURE__ */ new Map(),
136
+ zRanks: zRanksOf(flow)
131
137
  };
132
138
  const docScope = newScope();
133
139
  const extraParts = [];
@@ -179,6 +185,17 @@ function writeDocx(flow) {
179
185
  }, state, losses, docScope, extraParts, extraPartRels);
180
186
  const commentParaIds = emitComments(flow.comments, state, losses, docScope, extraParts, extraPartRels);
181
187
  emitCommentsExtended(flow.comments, commentParaIds, docScope, extraParts);
188
+ const fontTablePart = state.familiesUsed.size > 0 ? {
189
+ path: FONT_TABLE_PART,
190
+ data: encoder.encode(fontTableXml(state.familiesUsed)),
191
+ contentType: FONT_TABLE_CONTENT_TYPE
192
+ } : void 0;
193
+ if (fontTablePart) docScope.rels.push({
194
+ id: `rId${++docScope.relSeq}`,
195
+ type: REL_FONT_TABLE,
196
+ target: "fontTable.xml",
197
+ targetMode: "Internal"
198
+ });
182
199
  const partRelationships = [...docScope.rels.length > 0 ? [{
183
200
  sourcePart: "word/document.xml",
184
201
  relationships: docScope.rels
@@ -192,6 +209,7 @@ function writeDocx(flow) {
192
209
  contentType: DOC_CONTENT_TYPE
193
210
  },
194
211
  ...numberingPart ? [numberingPart] : [],
212
+ ...fontTablePart ? [fontTablePart] : [],
195
213
  ...extraParts,
196
214
  ...state.chartParts,
197
215
  ...state.mediaParts
@@ -207,6 +225,47 @@ function writeDocx(flow) {
207
225
  losses
208
226
  };
209
227
  }
228
+ function fontTableXml(families) {
229
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:fonts xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">${[...families.values()].map((f) => `<w:font w:name="${escapeAttr(f.family)}"><w:family w:val="${f.generic}"/><w:pitch w:val="${f.generic === "modern" ? "fixed" : "variable"}"/></w:font>`).join("")}</w:fonts>`;
230
+ }
231
+ /**
232
+ * §20.4.2.3 `relativeHeight` — a float's z-order, written the way Word writes
233
+ * it: a rank over every z-order the document states, counted up from Word's
234
+ * own floor.
235
+ *
236
+ * The model's z-order is an ORDER, and may be any number. Written as it stood,
237
+ * a PDF's first two marks got 0 and 1 — which Word, and LibreOffice after it,
238
+ * read as "above everything": an "APPROVED" stamp's blue box came back over
239
+ * its own lettering. A tagged reading counts up from minus a million, and a
240
+ * negative number is not a value the attribute admits at all.
241
+ */
242
+ function relativeHeight(zOrder, state) {
243
+ const at = (zOrder !== void 0 ? state.zRanks.get(zOrder) : void 0) ?? state.zRanks.size + ++state.drawingSeq;
244
+ const step = Math.max(1, Math.min(Z_STEP, Math.floor((Z_CEILING - Z_FLOOR) / Z_ROOM)));
245
+ return Math.min(Z_FLOOR + at * step, Z_CEILING);
246
+ }
247
+ /** Word's first float, and the highest value §20.4.2.3 lets a float take. */
248
+ var Z_FLOOR = 251658240;
249
+ var Z_CEILING = 503316479;
250
+ /** …and the step Word counts up in, while there is room for it. */
251
+ var Z_STEP = 1024;
252
+ var Z_ROOM = 1 << 17;
253
+ /** Every z-order the document's floats state, and each one's rank among them. */
254
+ function zRanksOf(flow) {
255
+ const seen = /* @__PURE__ */ new Set();
256
+ const visit = (blocks) => {
257
+ for (const el of blocks) {
258
+ const z = el.kind === "image" ? el.image.float?.zOrder : el.kind === "shape" ? el.shape.float?.zOrder : el.kind === "chart" ? el.chart.float?.zOrder : void 0;
259
+ if (z !== void 0 && Number.isFinite(z)) seen.add(z);
260
+ if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
261
+ }
262
+ };
263
+ visit(flow.body);
264
+ for (const band of flow.headersFooters?.values() ?? []) visit(band);
265
+ for (const note of flow.footnotes?.values() ?? []) visit(note);
266
+ for (const note of flow.endnotes?.values() ?? []) visit(note);
267
+ return new Map([...seen].sort((a, b) => a - b).map((z, i) => [z, i]));
268
+ }
210
269
  function emitNotes(notes, cfg, state, losses, docScope, extraParts, extraPartRels) {
211
270
  if (!notes || notes.size === 0) return;
212
271
  const scope = newScope();
@@ -388,14 +447,17 @@ var docxWriter = {
388
447
  */
389
448
  function emitBody(out, blocks, losses, state, scope, sectPrByClosingIndex) {
390
449
  let carried = [];
450
+ let carrier;
391
451
  const flush = () => {
392
- if (carried.length > 0) out.push(`<w:p>${carried.join("")}</w:p>`);
452
+ if (carried.length > 0) out.push(`<w:p>${pPrWithSect(carrier ?? {})}${carried.join("")}</w:p>`);
393
453
  carried = [];
454
+ carrier = void 0;
394
455
  };
395
456
  blocks.forEach((el, idx) => {
396
457
  const closing = sectPrByClosingIndex.get(idx);
397
458
  const anchored = closing === void 0 ? floatingDrawingRun(el, losses, state, scope) : void 0;
398
459
  if (anchored !== void 0) {
460
+ carrier ??= el.kind === "image" ? el.image.paragraphProperties : el.kind === "shape" ? el.shape.paragraphProperties : void 0;
399
461
  carried.push(anchored);
400
462
  return;
401
463
  }
@@ -411,7 +473,7 @@ function emitBody(out, blocks, losses, state, scope, sectPrByClosingIndex) {
411
473
  */
412
474
  function floatingDrawingRun(el, losses, state, scope) {
413
475
  if (el.kind === "image" && el.image.float) {
414
- const drawing = drawingXml(el.image.resource, el.image.width, el.image.height, el.image.altText, state, scope, el.image.float);
476
+ const drawing = drawingXml(el.image.resource, el.image.width, el.image.height, el.image.altText, state, scope, el.image.float, el.image);
415
477
  if (drawing === "") {
416
478
  losses.push({
417
479
  severity: "dropped",
@@ -435,7 +497,7 @@ function emitBlock(out, el, losses, state, scope, closingSectPr) {
435
497
  return;
436
498
  }
437
499
  if (el.kind === "image") {
438
- const drawing = drawingXml(el.image.resource, el.image.width, el.image.height, el.image.altText, state, scope, el.image.float);
500
+ const drawing = drawingXml(el.image.resource, el.image.width, el.image.height, el.image.altText, state, scope, el.image.float, el.image);
439
501
  if (drawing) out.push(`<w:p>${pPrWithSect(el.image.paragraphProperties, closingSectPr)}<w:r>${drawing}</w:r></w:p>`);
440
502
  else {
441
503
  losses.push({
@@ -514,7 +576,7 @@ function drawingFrame(float, cx, cy, head, graphic, state, rotation60k) {
514
576
  const emu = (pt) => Number.isFinite(pt) ? Math.round((pt ?? 0) * EMU_PER_PT) : 0;
515
577
  const d = float.wrapDist;
516
578
  const dist = ` distT="${String(emu(d?.topPt))}" distB="${String(emu(d?.bottomPt))}" distL="${String(emu(d?.leftPt))}" distR="${String(emu(d?.rightPt))}"`;
517
- const z = float.zOrder ?? ++state.drawingSeq;
579
+ const z = relativeHeight(float.zOrder, state);
518
580
  const attrs = `${dist} simplePos="0" relativeHeight="${String(z)}" behindDoc="${float.behind ? "1" : "0"}" locked="0" layoutInCell="${float.inCell === false ? "0" : "1"}" allowOverlap="1"`;
519
581
  const pos = (tag, p, fallback) => {
520
582
  const from = p?.relativeFrom ?? fallback;
@@ -550,7 +612,7 @@ function effectExtentXml(cx, cy, rotation60k) {
550
612
  const v = overhang(cy, cx * s + cy * c);
551
613
  return h === 0 && v === 0 ? none : `<wp:effectExtent l="${String(h)}" t="${String(v)}" r="${String(h)}" b="${String(v)}"/>`;
552
614
  }
553
- function drawingXml(resource, widthPt, heightPt, altText, state, scope, float) {
615
+ function drawingXml(resource, widthPt, heightPt, altText, state, scope, float, look) {
554
616
  if (resource === void 0) return "";
555
617
  const relId = mediaRelId(resource, state, scope);
556
618
  if (relId === void 0) return "";
@@ -558,8 +620,15 @@ function drawingXml(resource, widthPt, heightPt, altText, state, scope, float) {
558
620
  const cy = Math.round(heightPt * EMU_PER_PT);
559
621
  const id = ++state.drawingSeq;
560
622
  const descr = altText ? ` descr="${escapeAttr(altText)}"` : "";
561
- const graphic = `<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="${id}" name="Image ${id}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${relId}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic>`;
562
- return drawingFrame(float, cx, cy, `<wp:docPr id="${id}" name="Image ${id}"${descr}/>`, graphic, state);
623
+ const graphic = `<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="${id}" name="Image ${id}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${relId}">${look?.alpha !== void 0 && look.alpha < 1 ? `<a:alphaModFix amt="${String(Math.round(Math.max(0, look.alpha) * 1e5))}"/>` : ""}</a:blip>${srcRectXml(look?.crop)}<a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr>${xfrmXml(look, cx, cy)}<a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic>`;
624
+ return drawingFrame(float, cx, cy, `<wp:docPr id="${id}" name="Image ${id}"${descr}/>`, graphic, state, look?.rotation60k);
625
+ }
626
+ /** §20.1.8.55 `a:srcRect` — each edge cut away, in thousandths of a percent. */
627
+ function srcRectXml(crop) {
628
+ if (!crop) return "";
629
+ const edge = (name, v) => v > 0 ? ` ${name}="${String(Math.round(v * 1e5))}"` : "";
630
+ const attrs = edge("l", crop.left) + edge("t", crop.top) + edge("r", crop.right) + edge("b", crop.bottom);
631
+ return attrs === "" ? "" : `<a:srcRect${attrs}/>`;
563
632
  }
564
633
  var WPS_URI = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape";
565
634
  function shapeDrawingXml(shape, losses, state, scope) {
@@ -624,13 +693,24 @@ function geomXml(g) {
624
693
  return `<a:prstGeom prst="${escapeAttr(g.preset ?? "rect")}"><a:avLst>${gds}</a:avLst></a:prstGeom>`;
625
694
  }
626
695
  function fillXml(f) {
627
- if (f.kind === "solid" && f.colorHex) return `<a:solidFill><a:srgbClr val="${f.colorHex}"/></a:solidFill>`;
628
- if (f.kind === "gradient" && f.gradient) return gradFillXml(f.gradient);
696
+ if (f.kind === "solid" && f.colorHex) return `<a:solidFill>${srgbXml(f.colorHex, f.alpha)}</a:solidFill>`;
697
+ if (f.kind === "gradient" && f.gradient) return gradFillXml(f.gradient, f.alpha);
629
698
  return "<a:noFill/>";
630
699
  }
631
- function gradFillXml(g) {
700
+ /**
701
+ * §20.1.2.3.19 `a:srgbClr`, with the §20.1.2.3.1 `a:alpha` of a fill that is
702
+ * seen through. The model carried the opacity and the writer dropped it:
703
+ * bug1755507.pdf lays a card on a shadow painted at a fifth of full strength,
704
+ * and the shadow came back as a solid black slab around the card.
705
+ */
706
+ function srgbXml(colorHex, alpha) {
707
+ if (alpha === void 0 || !(alpha < 1)) return `<a:srgbClr val="${colorHex}"/>`;
708
+ const val = Math.round(Math.max(0, alpha) * 1e5);
709
+ return `<a:srgbClr val="${colorHex}"><a:alpha val="${String(val)}"/></a:srgbClr>`;
710
+ }
711
+ function gradFillXml(g, alpha) {
632
712
  return `<a:gradFill><a:gsLst>${g.stops.map((s) => {
633
- return `<a:gs pos="${Math.round(Math.max(0, Math.min(1, s.offset)) * 1e5)}"><a:srgbClr val="${s.colorHex}"/></a:gs>`;
713
+ return `<a:gs pos="${Math.round(Math.max(0, Math.min(1, s.offset)) * 1e5)}">${srgbXml(s.colorHex, alpha)}</a:gs>`;
634
714
  }).join("")}</a:gsLst>${g.kind === "radial" ? "<a:path path=\"circle\"/>" : `<a:lin ang="${Math.round(((g.angle ?? 0) % 360 + 360) % 360 * 6e4)}" scaled="1"/>`}</a:gradFill>`;
635
715
  }
636
716
  function lineXml(l) {
@@ -638,8 +718,13 @@ function lineXml(l) {
638
718
  const cap = l.cap === "round" ? " cap=\"rnd\"" : l.cap === "square" ? " cap=\"sq\"" : l.cap === "flat" ? " cap=\"flat\"" : "";
639
719
  const inner = [];
640
720
  if (l.fill === "none") inner.push("<a:noFill/>");
641
- else if (l.colorHex) inner.push(`<a:solidFill><a:srgbClr val="${l.colorHex}"/></a:solidFill>`);
642
- if (l.dash) inner.push(`<a:prstDash val="${l.dash}"/>`);
721
+ else if (l.colorHex) inner.push(`<a:solidFill>${srgbXml(l.colorHex, l.alpha)}</a:solidFill>`);
722
+ const custom = l.customDash ?? [];
723
+ if (custom.length >= 2) {
724
+ const pairs = [];
725
+ for (let i = 0; i + 1 < custom.length; i += 2) pairs.push(`<a:ds d="${Math.round(custom[i] * 1e5)}" sp="${Math.round(custom[i + 1] * 1e5)}"/>`);
726
+ inner.push(`<a:custDash>${pairs.join("")}</a:custDash>`);
727
+ } else if (l.dash) inner.push(`<a:prstDash val="${l.dash}"/>`);
643
728
  return `<a:ln${w}${cap}>${inner.join("")}</a:ln>`;
644
729
  }
645
730
  function txbxXml(text, losses, state, scope) {
@@ -704,6 +789,7 @@ function tblPrXml(p) {
704
789
  if (p.alignment && p.alignment !== "left") out.push(`<w:jc w:val="${p.alignment}"/>`);
705
790
  const borders = bordersXml("w:tblBorders", p.borders);
706
791
  if (borders) out.push(borders);
792
+ if (p.layout === "fixed") out.push("<w:tblLayout w:type=\"fixed\"/>");
707
793
  const margins = cellMarginsXml("w:tblCellMar", p.defaultCellMargins);
708
794
  if (margins) out.push(margins);
709
795
  return `<w:tblPr>${out.join("")}</w:tblPr>`;
@@ -747,9 +833,11 @@ var BORDER_SIDES = [
747
833
  ];
748
834
  function bordersXml(tag, borders) {
749
835
  if (!borders) return "";
750
- const sides = BORDER_SIDES.map(([key, el]) => {
836
+ const sides = BORDER_SIDES.map(([key, name]) => {
751
837
  const b = borders[key];
752
838
  if (!b) return "";
839
+ if (tag === "w:pBdr" && key === "insideV") return "";
840
+ const el = tag === "w:pBdr" && key === "insideH" ? "w:between" : name;
753
841
  const sz = b.width !== void 0 ? ` w:sz="${Math.round(b.width * 8)}"` : "";
754
842
  const color = b.colorHex !== void 0 ? ` w:color="${b.colorHex}"` : "";
755
843
  return `<${el} w:val="${b.style}"${sz}${color}/>`;
@@ -809,10 +897,10 @@ function hyperlinkXml(run, inner, _state, scope) {
809
897
  }
810
898
  function runXml(run, state, scope) {
811
899
  if (run.math !== void 0) return `<m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">${omathXml(run.math)}</m:oMath>`;
812
- const rPr = rPrXml(run.properties);
900
+ const rPr = rPrXml(run.properties, state);
813
901
  if (run.inlineImage !== void 0) {
814
902
  const img = run.inlineImage;
815
- const drawing = drawingXml(img.resource, img.width, img.height, void 0, state, scope);
903
+ const drawing = drawingXml(img.resource, img.width, img.height, void 0, state, scope, void 0, img);
816
904
  if (drawing) return `<w:r>${rPr}${drawing}</w:r>`;
817
905
  if (run.text === "" && !run.pageBreak) return "";
818
906
  }
@@ -822,22 +910,25 @@ function runXml(run, state, scope) {
822
910
  if (run.commentRef !== void 0) return `<w:r>${rPr}<w:commentReference w:id="${escapeAttr(run.commentRef)}"/></w:r>`;
823
911
  const brk = run.pageBreak ? "<w:br w:type=\"page\"/>" : "";
824
912
  if (run.field !== void 0 && run.text !== "") {
825
- const inner = `<w:r>${rPr}<w:t xml:space="preserve">${escapeXml(run.text)}</w:t></w:r>`;
826
- return `<w:fldSimple w:instr=" ${run.field} ">${inner}</w:fldSimple>${brk}`;
913
+ const piece = (inner) => `<w:r>${rPr}${inner}</w:r>`;
914
+ return piece("<w:fldChar w:fldCharType=\"begin\"/>") + piece(`<w:instrText xml:space="preserve"> ${run.field} </w:instrText>`) + piece("<w:fldChar w:fldCharType=\"separate\"/>") + piece(`<w:t xml:space="preserve">${escapeXml(run.text)}</w:t>`) + piece("<w:fldChar w:fldCharType=\"end\"/>") + brk;
827
915
  }
828
916
  if (run.text === "") return brk ? `<w:r>${rPr}${brk}</w:r>` : "";
829
917
  return `<w:r>${rPr}${run.text.split(" ").map((piece) => piece === "" ? "" : `<w:t xml:space="preserve">${escapeXml(piece)}</w:t>`).join("<w:tab/>")}${brk}</w:r>`;
830
918
  }
831
- function rPrXml(r) {
919
+ function rPrXml(r, state) {
832
920
  const out = [];
833
921
  const states = (key) => r[key] !== void 0 && r[key] !== DEFAULT_RUN[key];
834
- const fonts = rFontsXml(r.fontFamily);
922
+ const fonts = rFontsXml(r.fontFamily, state);
835
923
  if (fonts) out.push(fonts);
836
- if (states("bold")) out.push(toggle("w:b", r.bold));
837
- if (states("italic")) out.push(toggle("w:i", r.italic));
924
+ if (states("bold")) out.push(toggle("w:b", r.bold), toggle("w:bCs", r.bold));
925
+ if (states("italic")) out.push(toggle("w:i", r.italic), toggle("w:iCs", r.italic));
838
926
  if (states("strike")) out.push(toggle("w:strike", r.strike));
839
927
  if (states("colorHex")) out.push(`<w:color w:val="${r.colorHex}"/>`);
840
- if (states("fontSizePt")) out.push(`<w:sz w:val="${Math.round(r.fontSizePt * 2)}"/>`);
928
+ if (states("fontSizePt")) {
929
+ const half = Math.round(r.fontSizePt * 2);
930
+ out.push(`<w:sz w:val="${half}"/>`, `<w:szCs w:val="${half}"/>`);
931
+ }
841
932
  if (states("underline")) out.push(underlineXml(r));
842
933
  if (r.shadingColorHex !== void 0) out.push(runShdXml(r.shadingColorHex));
843
934
  if (states("verticalAlign")) out.push(`<w:vertAlign w:val="${r.verticalAlign}"/>`);
@@ -854,15 +945,8 @@ function runShdXml(fill) {
854
945
  }
855
946
  function pPrBody(p) {
856
947
  const out = [];
857
- if (p.numbering) out.push(`<w:numPr><w:ilvl w:val="${p.numbering.ilvl}"/><w:numId w:val="${escapeAttr(p.numbering.numId)}"/></w:numPr>`);
858
- if (p.outlineLevel !== void 0) out.push(`<w:outlineLvl w:val="${p.outlineLevel}"/>`);
859
948
  if (p.pageBreakBefore) out.push("<w:pageBreakBefore/>");
860
- if (p.bidi !== DEFAULT_PARA.bidi) out.push(toggle("w:bidi", p.bidi));
861
- const ind = indXml(p);
862
- if (ind) out.push(ind);
863
- const spacing = spacingXml(p);
864
- if (spacing) out.push(spacing);
865
- if (JC.has(p.alignment) && p.alignment !== DEFAULT_PARA.alignment) out.push(`<w:jc w:val="${p.alignment}"/>`);
949
+ if (p.numbering) out.push(`<w:numPr><w:ilvl w:val="${p.numbering.ilvl}"/><w:numId w:val="${escapeAttr(p.numbering.numId)}"/></w:numPr>`);
866
950
  const pBdr = bordersXml("w:pBdr", p.borders);
867
951
  if (pBdr) out.push(pBdr);
868
952
  const tabs = p.tabs ?? [];
@@ -874,6 +958,13 @@ function pPrBody(p) {
874
958
  }).join("");
875
959
  out.push(`<w:tabs>${stops}</w:tabs>`);
876
960
  }
961
+ if (p.bidi !== void 0 && p.bidi !== DEFAULT_PARA.bidi) out.push(toggle("w:bidi", p.bidi));
962
+ const spacing = spacingXml(p);
963
+ if (spacing) out.push(spacing);
964
+ const ind = indXml(p);
965
+ if (ind) out.push(ind);
966
+ if (JC.has(p.alignment) && p.alignment !== DEFAULT_PARA.alignment) out.push(`<w:jc w:val="${p.alignment}"/>`);
967
+ if (p.outlineLevel !== void 0) out.push(`<w:outlineLvl w:val="${p.outlineLevel}"/>`);
877
968
  return out.join("");
878
969
  }
879
970
  function pPrWithSect(p, closingSectPr) {
@@ -920,12 +1011,22 @@ function spacingXml(p) {
920
1011
  }
921
1012
  return attrs.length > 0 ? `<w:spacing ${attrs.join(" ")}/>` : "";
922
1013
  }
923
- function rFontsXml(fonts) {
1014
+ function rFontsXml(fonts, state) {
1015
+ if (fonts === void 0) return "";
924
1016
  const d = DEFAULT_RUN.fontFamily;
1017
+ const family = (name) => {
1018
+ const known = state?.faceFamilies?.get(name);
1019
+ if (known === void 0) return name;
1020
+ state?.familiesUsed.set(known.family, known);
1021
+ return known.family;
1022
+ };
925
1023
  const attrs = [];
926
- if (fonts.ascii && fonts.ascii !== d.ascii) attrs.push(`w:ascii="${escapeAttr(fonts.ascii)}"`);
927
- if (fonts.hAnsi && fonts.hAnsi !== d.hAnsi) attrs.push(`w:hAnsi="${escapeAttr(fonts.hAnsi)}"`);
928
- if (fonts.cs && fonts.cs !== d.cs) attrs.push(`w:cs="${escapeAttr(fonts.cs)}"`);
1024
+ if (fonts.ascii && fonts.ascii !== d.ascii) attrs.push(`w:ascii="${escapeAttr(family(fonts.ascii))}"`);
1025
+ if (fonts.hAnsi && fonts.hAnsi !== d.hAnsi) attrs.push(`w:hAnsi="${escapeAttr(family(fonts.hAnsi))}"`);
1026
+ const face = fonts.ascii !== void 0 && state?.faceFamilies?.has(fonts.ascii) === true ? family(fonts.ascii) : void 0;
1027
+ if (face !== void 0 && fonts.eastAsia === void 0) attrs.push(`w:eastAsia="${escapeAttr(face)}"`);
1028
+ if (fonts.cs && fonts.cs !== d.cs) attrs.push(`w:cs="${escapeAttr(family(fonts.cs))}"`);
1029
+ else if (face !== void 0) attrs.push(`w:cs="${escapeAttr(face)}"`);
929
1030
  return attrs.length > 0 ? `<w:rFonts ${attrs.join(" ")}/>` : "";
930
1031
  }
931
1032
  function toggle(tag, on) {
@@ -2128,13 +2128,17 @@ function parseLine(spPr, resolveColor) {
2128
2128
  const cap = capRaw === "rnd" ? "round" : capRaw === "sq" ? "square" : capRaw === "flat" ? "flat" : void 0;
2129
2129
  let noFill = false;
2130
2130
  let colorHex;
2131
+ let alpha;
2131
2132
  let dash;
2132
2133
  let customDash;
2133
2134
  let headEnd;
2134
2135
  let tailEnd;
2135
2136
  for (const c of poChildren(ln)) if (poIs(c, "a:noFill")) noFill = true;
2136
- else if (poIs(c, "a:solidFill")) colorHex = colorFromContainer(c, resolveColor);
2137
- else if (poIs(c, "a:prstDash")) dash = normalizeDash(poAttr(c, "val"));
2137
+ else if (poIs(c, "a:solidFill")) {
2138
+ alpha = containerAlpha(c);
2139
+ const hex = colorFromContainer(c, resolveColor);
2140
+ colorHex = hex !== void 0 && alpha !== void 0 ? unblendWhite(hex, alpha) : hex;
2141
+ } else if (poIs(c, "a:prstDash")) dash = normalizeDash(poAttr(c, "val"));
2138
2142
  else if (poIs(c, "a:custDash")) customDash = parseCustDash(c);
2139
2143
  else if (poIs(c, "a:headEnd")) headEnd = parseLineEnd(c);
2140
2144
  else if (poIs(c, "a:tailEnd")) tailEnd = parseLineEnd(c);
@@ -2145,6 +2149,7 @@ function parseLine(spPr, resolveColor) {
2145
2149
  ...customDash && customDash.length > 0 ? { customDash } : {},
2146
2150
  ...cap ? { cap } : {},
2147
2151
  ...noFill ? { fill: "none" } : {},
2152
+ ...alpha !== void 0 && alpha < 1 ? { alpha } : {},
2148
2153
  ...headEnd ? { headEnd } : {},
2149
2154
  ...tailEnd ? { tailEnd } : {}
2150
2155
  };
@@ -271,10 +271,12 @@ function parseParagraphBorders(node) {
271
271
  const bottom = edge("w:bottom");
272
272
  const left = edge("w:left", "w:start");
273
273
  const right = edge("w:right", "w:end");
274
+ const between = edge("w:between");
274
275
  if (top) out.top = top;
275
276
  if (bottom) out.bottom = bottom;
276
277
  if (left) out.left = left;
277
278
  if (right) out.right = right;
279
+ if (between) out.insideH = between;
278
280
  return Object.keys(out).length > 0 ? out : void 0;
279
281
  }
280
282
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reamkit",
3
- "version": "1.29.0",
3
+ "version": "1.30.0",
4
4
  "description": "Ream — convert DOCX, XLSX, PPTX and PDF to PDF, SVG, HTML, DOCX and XLSX, built from scratch on the ECMA-376 and ISO 32000 specifications. Parse once, convert anywhere.",
5
5
  "license": "MIT",
6
6
  "author": "Alex Krassavin <info@reamkit.dev>",
@@ -63,8 +63,8 @@
63
63
  "typecheck": "tsc -p tsconfig.eslint.json --noEmit",
64
64
  "lint": "eslint .",
65
65
  "lint:fix": "eslint . --fix",
66
- "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\" \"scripts/**/*.ts\"",
67
- "format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\" \"scripts/**/*.ts\"",
66
+ "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\" \"scripts/**/*.ts\" \"stand/**/*.ts\"",
67
+ "format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\" \"scripts/**/*.ts\" \"stand/**/*.ts\"",
68
68
  "corpus:build": "tsx scripts/corpus/build-corpus.ts",
69
69
  "corpus:fetch": "tsx scripts/corpus/fetch-corpus.ts",
70
70
  "corpus:sandbox:build": "docker build -t docgen-losandbox:latest scripts/corpus/sandbox/",
@@ -77,6 +77,8 @@
77
77
  "corpus:visual": "tsx scripts/corpus/visual-diff.ts",
78
78
  "corpus:scout": "tsx scripts/corpus/scout.ts",
79
79
  "corpus:pixel": "tsx scripts/corpus/pixel-scout.ts",
80
+ "stand": "tsx stand/serve.ts",
81
+ "stand:build": "tsx stand/build.ts",
80
82
  "typecheck:dist": "tsc -p tsconfig.json --noEmit"
81
83
  },
82
84
  "dependencies": {