reamkit 1.23.0 → 1.25.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 (102) hide show
  1. package/README.md +22 -11
  2. package/dist/esm/core/bmp.d.ts +33 -0
  3. package/dist/esm/core/bmp.js +276 -0
  4. package/dist/esm/core/converter/facade.d.ts +3 -3
  5. package/dist/esm/core/converter/facade.js +12 -0
  6. package/dist/esm/core/converter/ream.d.ts +48 -4
  7. package/dist/esm/core/converter/ream.js +25 -4
  8. package/dist/esm/core/document-model/index.d.ts +1 -1
  9. package/dist/esm/core/document-model/types.d.ts +54 -0
  10. package/dist/esm/core/drawingml/chart-geometry.js +3 -2
  11. package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
  12. package/dist/esm/core/drawingml/diagram/colors.js +125 -0
  13. package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
  14. package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
  15. package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
  16. package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
  17. package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
  18. package/dist/esm/core/drawingml/diagram/run.js +50 -0
  19. package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
  20. package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
  21. package/dist/esm/core/drawingml/preset-geometry.js +116 -6
  22. package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
  23. package/dist/esm/core/drawingml/text-warp.js +355 -0
  24. package/dist/esm/core/drawingml/theme-parser.js +9 -3
  25. package/dist/esm/core/font/measure.d.ts +12 -0
  26. package/dist/esm/core/font/measure.js +36 -0
  27. package/dist/esm/core/images.d.ts +30 -2
  28. package/dist/esm/core/images.js +121 -5
  29. package/dist/esm/core/metafile/emf.js +144 -16
  30. package/dist/esm/core/metafile/picture.d.ts +49 -0
  31. package/dist/esm/core/metafile/picture.js +70 -1
  32. package/dist/esm/core/metafile/wmf.js +53 -4
  33. package/dist/esm/core/ole/escher-blip.js +11 -1
  34. package/dist/esm/core/outline.d.ts +17 -0
  35. package/dist/esm/core/outline.js +30 -0
  36. package/dist/esm/core/style-cascade/resolver.js +1 -0
  37. package/dist/esm/core/style-cascade/types.d.ts +3 -1
  38. package/dist/esm/excel/print-model.js +2 -2
  39. package/dist/esm/excel/sheet-drawing.js +1 -1
  40. package/dist/esm/excel/sheet-to-flow.d.ts +10 -0
  41. package/dist/esm/excel/sheet-to-flow.js +30 -3
  42. package/dist/esm/html/html-writer.js +5 -3
  43. package/dist/esm/index.d.ts +3 -0
  44. package/dist/esm/index.js +2 -1
  45. package/dist/esm/layout/page-doc.d.ts +35 -0
  46. package/dist/esm/layout/page-doc.js +14 -1
  47. package/dist/esm/layout/styled-layout.js +217 -30
  48. package/dist/esm/markdown/markdown-writer.d.ts +41 -0
  49. package/dist/esm/markdown/markdown-writer.js +733 -0
  50. package/dist/esm/pdf/shading.js +8 -6
  51. package/dist/esm/pdf/styled-page-emitter.js +62 -4
  52. package/dist/esm/pdf/vector-graphics.js +1 -1
  53. package/dist/esm/pdf-reader/annots.d.ts +24 -0
  54. package/dist/esm/pdf-reader/annots.js +126 -0
  55. package/dist/esm/pdf-reader/content.d.ts +131 -5
  56. package/dist/esm/pdf-reader/content.js +169 -12
  57. package/dist/esm/pdf-reader/display.d.ts +56 -0
  58. package/dist/esm/pdf-reader/display.js +162 -0
  59. package/dist/esm/pdf-reader/document.d.ts +36 -1
  60. package/dist/esm/pdf-reader/document.js +92 -25
  61. package/dist/esm/pdf-reader/embedded-fonts.d.ts +31 -0
  62. package/dist/esm/pdf-reader/embedded-fonts.js +94 -0
  63. package/dist/esm/pdf-reader/flow-build.d.ts +61 -6
  64. package/dist/esm/pdf-reader/flow-build.js +128 -22
  65. package/dist/esm/pdf-reader/font.js +185 -4
  66. package/dist/esm/pdf-reader/image-decode.js +56 -5
  67. package/dist/esm/pdf-reader/images.d.ts +6 -0
  68. package/dist/esm/pdf-reader/images.js +25 -5
  69. package/dist/esm/pdf-reader/jpeg.d.ts +18 -0
  70. package/dist/esm/pdf-reader/jpeg.js +419 -0
  71. package/dist/esm/pdf-reader/layout.d.ts +1 -1
  72. package/dist/esm/pdf-reader/layout.js +221 -32
  73. package/dist/esm/pdf-reader/pattern-tint.d.ts +17 -0
  74. package/dist/esm/pdf-reader/pattern-tint.js +181 -0
  75. package/dist/esm/pdf-reader/reader.d.ts +9 -1
  76. package/dist/esm/pdf-reader/reader.js +22 -6
  77. package/dist/esm/pdf-reader/shading.d.ts +14 -0
  78. package/dist/esm/pdf-reader/shading.js +27 -1
  79. package/dist/esm/pdf-reader/tagged.js +156 -17
  80. package/dist/esm/pdf-reader/text.d.ts +13 -1
  81. package/dist/esm/pdf-reader/text.js +70 -3
  82. package/dist/esm/pdf-reader/vector.d.ts +25 -1
  83. package/dist/esm/pdf-reader/vector.js +168 -12
  84. package/dist/esm/pptx/ppt/ppt-reader.js +95 -27
  85. package/dist/esm/pptx/ppt/ppt-text.d.ts +41 -0
  86. package/dist/esm/pptx/ppt/ppt-text.js +327 -37
  87. package/dist/esm/pptx/pptx-reader.js +90 -10
  88. package/dist/esm/pptx/slide-parser.d.ts +4 -1
  89. package/dist/esm/pptx/slide-parser.js +104 -13
  90. package/dist/esm/pptx/sp-helpers.d.ts +7 -3
  91. package/dist/esm/pptx/sp-helpers.js +45 -7
  92. package/dist/esm/pptx/table-style.d.ts +7 -0
  93. package/dist/esm/pptx/table-style.js +59 -7
  94. package/dist/esm/svg/svg-writer.js +1 -0
  95. package/dist/esm/word/document-parser.d.ts +4 -1
  96. package/dist/esm/word/document-parser.js +4 -1
  97. package/dist/esm/word/docx-reader.js +26 -13
  98. package/dist/esm/word/docx-writer.js +15 -1
  99. package/dist/esm/word/drawing-parser.d.ts +20 -0
  100. package/dist/esm/word/drawing-parser.js +44 -5
  101. package/dist/esm/word/table-parser.js +3 -0
  102. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { FEATURES } from "../core/ir/features.js";
2
+ import { headingLevelOf } from "../core/outline.js";
2
3
  import { detectImageFormat } from "../core/images.js";
3
4
  import { PathBuilder, flipTransform, svgPathData } from "../core/vector.js";
4
5
  import { EMPTY_STYLE_SHEET, resolveParagraphProperties, resolveRunProperties } from "../core/style-cascade/resolver.js";
@@ -321,8 +322,8 @@ function emitOneShape(out, shape, ctx) {
321
322
  }
322
323
  function emitParagraph(out, p, ctx) {
323
324
  const resolved = resolveParagraphProperties(p.properties, EMPTY_STYLE_SHEET);
324
- const lvl = resolved.outlineLevel;
325
- const tag = lvl !== void 0 && lvl >= 0 && lvl <= 8 ? `h${Math.min(lvl, 5) + 1}` : "p";
325
+ const lvl = headingLevelOf(resolved);
326
+ const tag = lvl !== void 0 ? `h${String(lvl)}` : "p";
326
327
  const style = paragraphCss(resolved);
327
328
  const dir = resolved.bidi ? " dir=\"rtl\"" : "";
328
329
  const anchored = (p.bookmarks ?? []).filter((b) => ctx.referencedAnchors.has(b));
@@ -653,7 +654,8 @@ var MIME_BY_FORMAT = {
653
654
  png: "image/png",
654
655
  jpeg2000: "image/jp2",
655
656
  gif: "image/gif",
656
- tiff: "image/tiff"
657
+ tiff: "image/tiff",
658
+ bmp: "image/bmp"
657
659
  };
658
660
  function dataUri(resource, store) {
659
661
  if (resource === void 0) return void 0;
@@ -49,6 +49,9 @@ export type { FontProvider, FontRequest, FontAnswer } from './core/fonts/provide
49
49
  export { svgWriter, writeSvg } from './svg/svg-writer.js';
50
50
  export type { SvgWriteOptions } from './svg/svg-writer.js';
51
51
  export { htmlWriter, writeHtml } from './html/html-writer.js';
52
+ export { markdownWriter, writeMarkdown } from './markdown/markdown-writer.js';
53
+ export type { StreamFilter, StreamFilters } from './pdf-reader/document.js';
54
+ export type { MarkdownWriteOptions } from './markdown/markdown-writer.js';
52
55
  export { docxWriter, writeDocx } from './word/docx-writer.js';
53
56
  export { layoutStyledDocument } from './layout/styled-layout.js';
54
57
  export type { LaidOutDocument } from './layout/page-doc.js';
package/dist/esm/index.js CHANGED
@@ -17,8 +17,9 @@ import { readXlsx, xlsxReader } from "./excel/xlsx-reader.js";
17
17
  import { NO_FONT, callerFontProvider, chainProviders, embeddedDocFontProvider, isEmbeddingRestricted, localFontProvider, readOs2FsType, remoteFontProvider } from "./core/fonts/provider.js";
18
18
  import { docxWriter, writeDocx } from "./word/docx-writer.js";
19
19
  import { htmlWriter, writeHtml } from "./html/html-writer.js";
20
+ import { markdownWriter, writeMarkdown } from "./markdown/markdown-writer.js";
20
21
  import { svgWriter, writeSvg } from "./svg/svg-writer.js";
21
22
  import { createConverter } from "./core/converter/facade.js";
22
23
  import { WrongPasswordError, isEncryptedPackage } from "./core/crypto/offcrypto.js";
23
24
  import { Ream } from "./core/converter/ream.js";
24
- export { ConversionLossError, FEATURES, FontRegistry, NO_FONT, Ream, ResourceStore, WrongPasswordError, callerFontProvider, chainProviders, createConverter, createHyphenator, createLanguageHyphenator, docxReader, docxWriter, eighthPtToPt, embeddedDocFontProvider, emuToPt, fetchFontSet, formatLoss, getHyphenator, halfPtToPt, htmlWriter, inchToPt, isEmbeddingRestricted, isEncryptedPackage, layoutStyledDocument, localFontProvider, mmToPt, parseTtf, pt, pxToPt, readDocx, readOs2FsType, readXlsx, remoteFontProvider, renderStyledPdf, resolveFamilyKey, signPdf, splitPatternBundle, subsetTtf, svgWriter, twipsToPt, writeDocx, writeHtml, writeSvg, xlsxReader };
25
+ export { ConversionLossError, FEATURES, FontRegistry, NO_FONT, Ream, ResourceStore, WrongPasswordError, callerFontProvider, chainProviders, createConverter, createHyphenator, createLanguageHyphenator, docxReader, docxWriter, eighthPtToPt, embeddedDocFontProvider, emuToPt, fetchFontSet, formatLoss, getHyphenator, halfPtToPt, htmlWriter, inchToPt, isEmbeddingRestricted, isEncryptedPackage, layoutStyledDocument, localFontProvider, markdownWriter, mmToPt, parseTtf, pt, pxToPt, readDocx, readOs2FsType, readXlsx, remoteFontProvider, renderStyledPdf, resolveFamilyKey, signPdf, splitPatternBundle, subsetTtf, svgWriter, twipsToPt, writeDocx, writeHtml, writeMarkdown, writeSvg, xlsxReader };
@@ -326,6 +326,32 @@ export interface TextLineItem extends PageItemBase {
326
326
  * the plot's own tick labels. Absent ⇒ horizontal.
327
327
  */
328
328
  readonly rotationDeg?: number;
329
+ /**
330
+ * §20.1.9.10 `a:prstTxWarp` — the line is WordArt: each glyph is placed on
331
+ * its own, bent through the preset's curve and stretched onto the shape's
332
+ * box. Absent ⇒ the line is set flat, one text matrix for the whole of it.
333
+ */
334
+ readonly warp?: TextLineWarp;
335
+ }
336
+ /**
337
+ * What placing a warped line needs: the preset, the shape box the warped block
338
+ * is stretched onto, and the box the UN-warped block occupies — both in the
339
+ * page's top-left frame, so a glyph's position in the second maps straight into
340
+ * the first.
341
+ */
342
+ export interface TextLineWarp {
343
+ readonly preset: string;
344
+ readonly adjust?: number;
345
+ readonly boxX: Pt;
346
+ readonly boxY: Pt;
347
+ readonly boxWidth: Pt;
348
+ readonly boxHeight: Pt;
349
+ /** The un-warped block's left edge and width (its widest line). */
350
+ readonly srcX: Pt;
351
+ readonly srcWidth: Pt;
352
+ /** The un-warped block's ink top and total ink height. */
353
+ readonly srcTop: Pt;
354
+ readonly srcHeight: Pt;
329
355
  }
330
356
  /** One edge of a table-cell frame; `(x, y)` is the cell box's top-left corner. */
331
357
  export interface BorderItem extends PageItemBase {
@@ -422,6 +448,15 @@ export interface PagePaintPlan {
422
448
  * per {@link PageItemBase.pictureId}. They paint where the shape pass does.
423
449
  */
424
450
  readonly pictures: ReadonlyArray<ReadonlyArray<PageItem>>;
451
+ /**
452
+ * §19.3.1 — the items of a page that STATES its own paint order, each run in
453
+ * the order it paints, the runs sorted by the order they were given. A slide
454
+ * is such a page: its shape tree is a z-order, and a picture standing after a
455
+ * rectangle in it is drawn over that rectangle. Split by kind instead — every
456
+ * image, then every shape — the six logos of 54542_cropped_bitmap.pptx were
457
+ * painted first and the six panels they sit on were painted over them.
458
+ */
459
+ readonly ordered: ReadonlyArray<ReadonlyArray<PageItem>>;
425
460
  }
426
461
  export declare function paintPlan(commands: ReadonlyArray<PageItem>): PagePaintPlan;
427
462
  export interface LaidOutDocument {
@@ -7,11 +7,23 @@ function paintPlan(commands) {
7
7
  const lines = [];
8
8
  const pictures = /* @__PURE__ */ new Map();
9
9
  const behind = [];
10
+ const ordered = /* @__PURE__ */ new Map();
11
+ const orderOf = /* @__PURE__ */ new Map();
10
12
  for (const c of commands) {
11
13
  if (c.behind === true) {
12
14
  behind.push(c);
13
15
  continue;
14
16
  }
17
+ if (c.z !== void 0 && c.type !== "fill" && c.type !== "border" && (c.type !== "line" || c.pictureId !== void 0)) {
18
+ const key = c.pictureId !== void 0 ? `p${c.pictureId}` : `i${orderOf.size}`;
19
+ const run = ordered.get(key);
20
+ if (run) run.push(c);
21
+ else {
22
+ ordered.set(key, [c]);
23
+ orderOf.set(key, c.z);
24
+ }
25
+ continue;
26
+ }
15
27
  if (c.pictureId !== void 0) {
16
28
  const group = pictures.get(c.pictureId);
17
29
  if (group) group.push(c);
@@ -44,7 +56,8 @@ function paintPlan(commands) {
44
56
  borders,
45
57
  shapes,
46
58
  lines,
47
- pictures: [...pictures.values()]
59
+ pictures: [...pictures.values()],
60
+ ordered: [...ordered.entries()].sort((a, b) => (orderOf.get(a[0]) ?? 0) - (orderOf.get(b[0]) ?? 0)).map(([, run]) => run)
48
61
  };
49
62
  }
50
63
  function assertNeverPageItem(item) {
@@ -4,6 +4,7 @@ import { shapeText } from "../core/font/opentype-layout.js";
4
4
  import { createFontMeasure } from "../core/font/measure.js";
5
5
  import { halfPtToPt, pt } from "../core/ir/units.js";
6
6
  import { ResourceStore } from "../core/ir/resources.js";
7
+ import { headingLevelOf } from "../core/outline.js";
7
8
  import { prepareImage } from "../core/images.js";
8
9
  import { PathBuilder, flipTransform } from "../core/vector.js";
9
10
  import { isEmf, readEmf } from "../core/metafile/emf.js";
@@ -1030,8 +1031,8 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
1030
1031
  const fillImage = shape.fill.kind === "picture" && shape.fill.imageResource !== void 0 ? imageResources?.get(shape.fill.imageResource) : void 0;
1031
1032
  const fillImageResourceName = fillImage?.resourceName;
1032
1033
  const fillImageTile = shape.fill.kind === "picture" && shape.fill.tiled === true && fillImage?.prepared ? shape.fill.tileSizePt ?? {
1033
- widthPt: fillImage.prepared.widthPx * 72 / 96 * (shape.fill.tileScale?.sx ?? 1),
1034
- heightPt: fillImage.prepared.heightPx * 72 / 96 * (shape.fill.tileScale?.sy ?? 1)
1034
+ widthPt: fillImage.prepared.widthPx * 72 / (fillImage.prepared.dpiX ?? 96) * (shape.fill.tileScale?.sx ?? 1),
1035
+ heightPt: fillImage.prepared.heightPx * 72 / (fillImage.prepared.dpiY ?? 96) * (shape.fill.tileScale?.sy ?? 1)
1035
1036
  } : void 0;
1036
1037
  const fillColorHex = shape.fill.kind === "solid" ? shape.fill.colorHex : fillGradient ? gradientToSolid(fillGradient) : void 0;
1037
1038
  const stroke = buildStroke(shape.line);
@@ -1049,8 +1050,10 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
1049
1050
  const textLineGaps = /* @__PURE__ */ new Map();
1050
1051
  let textHeightPt = 0;
1051
1052
  const vertical = text?.vertical;
1053
+ const upright = text?.upright;
1054
+ const warp = text?.warp;
1052
1055
  if (text && text.content.length > 0) {
1053
- const innerWidth = vertical ? Math.max(1, heightPt - insetTopPt - insetBottomPt) : Math.max(1, widthPt - insetLeftPt - insetRightPt);
1056
+ const innerWidth = warp ? Number.MAX_SAFE_INTEGER : vertical ? Math.max(1, heightPt - insetTopPt - insetBottomPt) : Math.max(1, widthPt - insetLeftPt - insetRightPt);
1054
1057
  for (const el of text.content) {
1055
1058
  if (el.kind === "image") {
1056
1059
  const res = el.image.resource ? imageResources?.get(el.image.resource) : void 0;
@@ -1143,6 +1146,16 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
1143
1146
  }
1144
1147
  }
1145
1148
  if (text?.autoFit && !vertical && textLines.length > 0) heightPt = textHeightPt + insetTopPt + insetBottomPt;
1149
+ if (text?.shrinkToFit === true && text.fitToBox !== true && warp === void 0 && textLines.length > 0 && fitPass < MAX_FIT_PASSES) {
1150
+ const innerW = Math.max(1, widthPt - insetLeftPt - insetRightPt);
1151
+ const innerH = Math.max(1, heightPt - insetTopPt - insetBottomPt);
1152
+ const widest = textLines.reduce((a, l) => Math.max(a, l.contentWidthPt), 0);
1153
+ const k = Math.min(1, innerW / Math.max(1, widest), innerH / Math.max(1, textHeightPt));
1154
+ if (Number.isFinite(k) && k > .05 && k < .98) return layoutShapeBlock({
1155
+ ...shape,
1156
+ text: scaleTextBody(text, k)
1157
+ }, options, fontResources, imageResources, contentWidth, maxHeight, box, fitPass + 1);
1158
+ }
1146
1159
  if (text?.fitToBox === true && textLines.length > 0 && fitPass < MAX_FIT_PASSES) {
1147
1160
  const innerW = Math.max(1, widthPt - insetLeftPt - insetRightPt);
1148
1161
  const innerH = Math.max(1, heightPt - insetTopPt - insetBottomPt);
@@ -1166,6 +1179,10 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
1166
1179
  text: scaleTextBody(text, k)
1167
1180
  }, options, fontResources, imageResources, contentWidth, maxHeight, box, fitPass + 1);
1168
1181
  }
1182
+ const warpInk = warp && textLines.length > 0 ? {
1183
+ above: lineInk(textLines[0]).above,
1184
+ below: lineInk(textLines[textLines.length - 1]).below
1185
+ } : void 0;
1169
1186
  const paths = buildShapePaths(shape.geometry, widthPt, heightPt);
1170
1187
  const markerPaths = lineEndPaths(paths, shape.line, stroke?.widthPt ?? 0);
1171
1188
  const scale = shape.width > 0 ? widthPt / shape.width : 1;
@@ -1187,6 +1204,7 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
1187
1204
  ...markerPaths.length > 0 ? { markerPaths } : {},
1188
1205
  ...fillImageResourceName ? { fillImageResourceName } : {},
1189
1206
  ...fillImageTile ? { fillImageTile } : {},
1207
+ ...fillImageTile && shape.fill.kind === "picture" && shape.fill.tileFromCentre === true ? { fillImageTileFromCentre: true } : {},
1190
1208
  ...fillImageResourceName && shape.fill.imageCrop ? { fillImageCrop: shape.fill.imageCrop } : {},
1191
1209
  ...fillImageResourceName && shape.fill.imageFillRect ? { fillImageRect: shape.fill.imageFillRect } : {},
1192
1210
  ...fillImageResourceName && shape.fill.duotone ? { fillImageDuotone: shape.fill.duotone } : {},
@@ -1202,6 +1220,12 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
1202
1220
  spacingBeforePt: pp.spacingBefore ?? 0,
1203
1221
  spacingAfterPt: pp.spacingAfter ?? 0,
1204
1222
  textLines,
1223
+ ...warp && warpInk ? { textWarp: {
1224
+ preset: warp.preset,
1225
+ ...warp.adjust !== void 0 ? { adjust: warp.adjust } : {},
1226
+ inkAbovePt: warpInk.above,
1227
+ inkBelowPt: warpInk.below
1228
+ } } : {},
1205
1229
  ...textCharts.size > 0 ? { textCharts } : {},
1206
1230
  ...textShapes.size > 0 ? { textShapes } : {},
1207
1231
  ...textTables.size > 0 ? { textTables } : {},
@@ -1213,6 +1237,7 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
1213
1237
  insetTopPt,
1214
1238
  insetBottomPt,
1215
1239
  anchor: text?.anchor ?? "t",
1240
+ ...upright ? { upright } : {},
1216
1241
  ...vertical ? { vertical } : {},
1217
1242
  ...shape.altText ? { altText: shape.altText } : {},
1218
1243
  ...shape.float ? { float: shape.float } : {}
@@ -1863,6 +1888,9 @@ function chartPageItems(laid, x, bottomYUp, pageHeight, structId) {
1863
1888
  var nextPictureId = 1;
1864
1889
  function emitShapeText(sh, x, bottomYUp, sink, pageHeight, figId) {
1865
1890
  if (sh.textLines.length === 0) return;
1891
+ const turnDeg = sh.upright === true || sh.flipH || sh.flipV ? 0 : -(sh.rotation60k || 0) / 6e4;
1892
+ const centreX = x + sh.widthPt / 2;
1893
+ const centreY = bottomYUp + sh.heightPt / 2;
1866
1894
  if (sh.vertical) {
1867
1895
  const up = sh.vertical === "vert270";
1868
1896
  const innerHeight = Math.max(1, sh.heightPt - sh.insetTopPt - sh.insetBottomPt);
@@ -1872,18 +1900,23 @@ function emitShapeText(sh, x, bottomYUp, sink, pageHeight, figId) {
1872
1900
  const lineOffset = alignmentOffset(line.resolved.alignment, line.contentWidthPt, innerHeight);
1873
1901
  const off = lineBaselineOffset(line, line.resolved);
1874
1902
  const baselineAcross = up ? across + h - off : across + off;
1903
+ const origin = spinAboutCentre(up ? x + baselineAcross : x + sh.widthPt - baselineAcross, up ? bottomYUp + sh.insetBottomPt + lineOffset : bottomYUp + sh.heightPt - sh.insetTopPt - lineOffset, centreX, centreY, turnDeg);
1875
1904
  sink.push({
1876
1905
  type: "line",
1877
1906
  line,
1878
- originX: pt(up ? x + baselineAcross : x + sh.widthPt - baselineAcross),
1879
- baselineY: pt(pageHeight - (up ? bottomYUp + sh.insetBottomPt + lineOffset : bottomYUp + sh.heightPt - sh.insetTopPt - lineOffset)),
1880
- rotationDeg: up ? 90 : -90,
1907
+ originX: pt(origin.x),
1908
+ baselineY: pt(pageHeight - origin.y),
1909
+ rotationDeg: (up ? 90 : -90) + turnDeg,
1881
1910
  ...figId !== void 0 ? { structId: figId } : {}
1882
1911
  });
1883
1912
  across += h;
1884
1913
  }
1885
1914
  return;
1886
1915
  }
1916
+ if (sh.textWarp) {
1917
+ emitWarpedText(sh, sh.textWarp, x, bottomYUp, sink, pageHeight, figId);
1918
+ return;
1919
+ }
1887
1920
  const shapeTop = bottomYUp + sh.heightPt;
1888
1921
  const innerWidth = Math.max(1, sh.widthPt - sh.insetLeftPt - sh.insetRightPt);
1889
1922
  let textY;
@@ -1913,16 +1946,102 @@ function emitShapeText(sh, x, bottomYUp, sink, pageHeight, figId) {
1913
1946
  return;
1914
1947
  }
1915
1948
  const indentLeft = line.resolved.indentLeft + (line.firstLine ? line.resolved.indentFirstLine : 0);
1949
+ const origin = spinAboutCentre(x + sh.insetLeftPt + indentLeft + lineOffset, textY + lineBaselineOffset(line, line.resolved), centreX, centreY, turnDeg);
1916
1950
  sink.push({
1917
1951
  type: "line",
1918
1952
  line,
1919
- originX: pt(x + sh.insetLeftPt + indentLeft + lineOffset),
1920
- baselineY: pt(pageHeight - (textY + lineBaselineOffset(line, line.resolved))),
1953
+ originX: pt(origin.x),
1954
+ baselineY: pt(pageHeight - origin.y),
1955
+ ...turnDeg !== 0 ? { rotationDeg: turnDeg } : {},
1921
1956
  ...figId !== void 0 ? { structId: figId } : {}
1922
1957
  });
1923
1958
  textY -= sh.textLineGaps?.get(i) ?? 0;
1924
1959
  });
1925
1960
  }
1961
+ /**
1962
+ * A point spun about another, counter-clockwise in the y-up page frame.
1963
+ *
1964
+ * @param px The point's x.
1965
+ * @param py The point's y (y-up).
1966
+ * @param cx The centre's x.
1967
+ * @param cy The centre's y (y-up).
1968
+ * @param deg How far to turn, counter-clockwise; zero returns the point.
1969
+ * @returns The turned point.
1970
+ */
1971
+ function spinAboutCentre(px, py, cx, cy, deg) {
1972
+ if (deg === 0) return {
1973
+ x: px,
1974
+ y: py
1975
+ };
1976
+ const rad = deg * Math.PI / 180;
1977
+ const cos = Math.cos(rad);
1978
+ const sin = Math.sin(rad);
1979
+ const dx = px - cx;
1980
+ const dy = py - cy;
1981
+ return {
1982
+ x: cx + dx * cos - dy * sin,
1983
+ y: cy + dx * sin + dy * cos
1984
+ };
1985
+ }
1986
+ /**
1987
+ * §20.1.9.10 — place a WordArt body's lines in the block's own frame and hand
1988
+ * each one the warp that maps that frame onto the shape's box.
1989
+ *
1990
+ * The block is laid out flat: its lines stack by their own heights, each
1991
+ * aligned within the widest of them, and its ink starts at the box's top-left
1992
+ * corner. Nothing here bends anything — the emitter does that, glyph by glyph,
1993
+ * from the two frames this records.
1994
+ *
1995
+ * @param sh The laid-out shape.
1996
+ * @param warp The shape's warp and the block's ink reach.
1997
+ * @param x The shape's left edge on the page.
1998
+ * @param bottomYUp The shape's bottom edge, y-up from the page's bottom.
1999
+ * @param sink Where the line items are pushed.
2000
+ * @param pageHeight The page's height, for the y-up ⇄ top-left conversion.
2001
+ * @param figId The tagged-PDF Figure this text belongs to, when it has one.
2002
+ */
2003
+ function emitWarpedText(sh, warp, x, bottomYUp, sink, pageHeight, figId) {
2004
+ const boxX = x + sh.insetLeftPt;
2005
+ const boxY = pageHeight - (bottomYUp + sh.heightPt) + sh.insetTopPt;
2006
+ const boxWidth = Math.max(1, sh.widthPt - sh.insetLeftPt - sh.insetRightPt);
2007
+ const boxHeight = Math.max(1, sh.heightPt - sh.insetTopPt - sh.insetBottomPt);
2008
+ const srcWidth = Math.max(1, ...sh.textLines.map((l) => l.contentWidthPt));
2009
+ const baselines = [];
2010
+ let top = 0;
2011
+ sh.textLines.forEach((line, i) => {
2012
+ const h = computeLineHeight(line, line.resolved);
2013
+ baselines.push(top + h - lineBaselineOffset(line, line.resolved));
2014
+ top += h + (sh.textLineGaps?.get(i) ?? 0);
2015
+ });
2016
+ const first = baselines[0] ?? 0;
2017
+ const last = baselines[baselines.length - 1] ?? 0;
2018
+ const srcTop = boxY;
2019
+ const srcHeight = Math.max(1, last - first + warp.inkAbovePt + warp.inkBelowPt);
2020
+ const shift = srcTop + warp.inkAbovePt - first;
2021
+ sh.textLines.forEach((line, i) => {
2022
+ if (sh.textCharts?.has(i) === true || sh.textShapes?.has(i) === true) return;
2023
+ if (sh.textTables?.has(i) === true) return;
2024
+ sink.push({
2025
+ type: "line",
2026
+ line,
2027
+ originX: pt(boxX + alignmentOffset(line.resolved.alignment, line.contentWidthPt, srcWidth)),
2028
+ baselineY: pt(baselines[i] + shift),
2029
+ warp: {
2030
+ preset: warp.preset,
2031
+ ...warp.adjust !== void 0 ? { adjust: warp.adjust } : {},
2032
+ boxX: pt(boxX),
2033
+ boxY: pt(boxY),
2034
+ boxWidth: pt(boxWidth),
2035
+ boxHeight: pt(boxHeight),
2036
+ srcX: pt(boxX),
2037
+ srcWidth: pt(srcWidth),
2038
+ srcTop: pt(srcTop),
2039
+ srcHeight: pt(srcHeight)
2040
+ },
2041
+ ...figId !== void 0 ? { structId: figId } : {}
2042
+ });
2043
+ });
2044
+ }
1926
2045
  /** The page a table is being drawn onto, as a cell's floats need to see it. */
1927
2046
  function cellAnchorCtx(asm) {
1928
2047
  return {
@@ -1962,6 +2081,19 @@ function emitCellFloat(cf, ctx, frame) {
1962
2081
  if (float.wrap !== "none") ctx.exclude(float, x, topYUp, cf.block.widthPt, cf.block.heightPt);
1963
2082
  }
1964
2083
  function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
2084
+ const castsUnderPicture = sh.fillImageResourceName !== void 0 ? sh.shadow : void 0;
2085
+ const shadowedFill = castsUnderPicture !== void 0;
2086
+ const group = shadowedFill ? { pictureId: nextPictureId++ } : {};
2087
+ if (castsUnderPicture) sink.push({
2088
+ type: "shape",
2089
+ shape: {
2090
+ paths: sh.paths,
2091
+ shadow: castsUnderPicture,
2092
+ transform: flipTransform(buildShapeTransform(x, bottomYUp, sh.widthPt, sh.heightPt, sh.rotation60k, sh.flipH, sh.flipV), pageHeight)
2093
+ },
2094
+ ...group,
2095
+ ...figId !== void 0 ? { structId: figId } : {}
2096
+ });
1965
2097
  if (sh.fillImageResourceName) {
1966
2098
  const clip = {
1967
2099
  paths: sh.paths,
@@ -1974,12 +2106,13 @@ function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
1974
2106
  y: pageHeight - (bottomYUp + sh.heightPt),
1975
2107
  width: sh.widthPt,
1976
2108
  height: sh.heightPt
1977
- })) {
2109
+ }, sh.fillImageTileFromCentre === true)) {
1978
2110
  if (item.type !== "image") continue;
1979
2111
  sink.push({
1980
2112
  ...item,
1981
2113
  clip,
1982
2114
  ...fig,
2115
+ ...group,
1983
2116
  ...sh.fillAlpha !== void 0 ? { alpha: sh.fillAlpha } : {}
1984
2117
  });
1985
2118
  }
@@ -2000,7 +2133,8 @@ function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
2000
2133
  ...sh.fillAlpha !== void 0 ? { alpha: sh.fillAlpha } : {},
2001
2134
  ...sh.fillImageDuotone ? { duotone: sh.fillImageDuotone } : {},
2002
2135
  clip,
2003
- ...fig
2136
+ ...fig,
2137
+ ...group
2004
2138
  });
2005
2139
  }
2006
2140
  }
@@ -2021,9 +2155,10 @@ function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
2021
2155
  ...sh.fillGradient ? { fillGradient: sh.fillGradient } : {},
2022
2156
  ...sh.fillAlpha !== void 0 ? { fillAlpha: sh.fillAlpha } : {},
2023
2157
  ...sh.stroke ? { stroke: sh.stroke } : {},
2024
- ...sh.shadow ? { shadow: sh.shadow } : {},
2158
+ ...sh.shadow && !shadowedFill ? { shadow: sh.shadow } : {},
2025
2159
  transform: flipTransform(buildShapeTransform(x, bottomYUp, sh.widthPt, sh.heightPt, sh.rotation60k, sh.flipH, sh.flipV), pageHeight)
2026
2160
  },
2161
+ ...group,
2027
2162
  ...figId !== void 0 ? { structId: figId } : {}
2028
2163
  });
2029
2164
  for (const child of sh.children ?? []) emitShapeItems(child.laid, x + child.xPt, bottomYUp + sh.heightPt - child.yPt - child.laid.heightPt, sink, pageHeight, figId);
@@ -3647,6 +3782,30 @@ function alignmentOffset(alignment, lineWidth, available) {
3647
3782
  return 0;
3648
3783
  }
3649
3784
  /**
3785
+ * How far a line's INK reaches either side of its baseline — the union of its
3786
+ * glyphs' own bounding boxes, not the line box its font metrics describe. Only
3787
+ * WordArt needs this: it stretches the marks it draws onto its shape, and the
3788
+ * empty band an ascender or descender reserves is not one of them.
3789
+ *
3790
+ * @param line The laid-out line.
3791
+ * @returns Points above the baseline the ink rises, and below it that it drops.
3792
+ */
3793
+ function lineInk(line) {
3794
+ let above = 0;
3795
+ let below = 0;
3796
+ for (const tok of line.tokens) {
3797
+ if (tok.kind !== "text" || tok.isSpace) continue;
3798
+ const ink = tok.font.measure.textInkPt(tok.text, tok.fontSizePt);
3799
+ const rise = tok.risePt ?? 0;
3800
+ above = Math.max(above, ink.above + rise);
3801
+ below = Math.max(below, ink.below - rise);
3802
+ }
3803
+ return {
3804
+ above,
3805
+ below
3806
+ };
3807
+ }
3808
+ /**
3650
3809
  * §17.4.85 `w:vMerge` — a vertical merge is ONE cell, so its continuation rows
3651
3810
  * are painted with the START cell's shading. Word writes the fill on the cell
3652
3811
  * that opens the merge and nothing on the rest; painting only what is written,
@@ -4066,7 +4225,9 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
4066
4225
  const padRightBase = cellMar?.right ?? tableMar?.right ?? DEFAULT_CELL_PADDING_TWIPS * TWIP_TO_PT;
4067
4226
  const padLeftPt = padLeftBase + (cell.properties.icon ? CF_ICON_GUTTER_PT : 0);
4068
4227
  const padRightPt = padRightBase;
4228
+ const turned = mergeRole === "middle" || mergeRole === "end" ? void 0 : cell.properties.textDirection;
4069
4229
  const innerWidth = Math.max(1, widthPt - padLeftPt - padRightPt);
4230
+ const measureWidth = turned ? NO_WRAP_MEASURE_WIDTH : innerWidth;
4070
4231
  const lines = [];
4071
4232
  const nestedTables = [];
4072
4233
  const cellShapes = [];
@@ -4085,7 +4246,7 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
4085
4246
  if (mergeRole !== "middle" && mergeRole !== "end") {
4086
4247
  for (const el of cell.content) if (el.kind === "paragraph") {
4087
4248
  const noWrap = cell.properties.noWrap === true;
4088
- const block = layoutParagraphBlock(el.paragraph, options, fontResources, imageResources, noWrap ? NO_WRAP_MEASURE_WIDTH : innerWidth);
4249
+ const block = layoutParagraphBlock(el.paragraph, options, fontResources, imageResources, noWrap ? NO_WRAP_MEASURE_WIDTH : measureWidth);
4089
4250
  const cutAt = widthPt - padLeftPt;
4090
4251
  if (noWrap && (block.lines[0]?.contentWidthPt ?? 0) > cutAt) clipped = true;
4091
4252
  const keep = noWrap ? clipLineToWidth(block.lines, cutAt, cell.properties.hashOnOverflow === true) : block.lines;
@@ -4136,7 +4297,9 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
4136
4297
  }
4137
4298
  const colEnd = colStart + colSpan - 1;
4138
4299
  const borders = resolveCellBorders(cell.properties.borders, tableProps.borders, rowIdx, colStart, colEnd, rowCount, colCount, mergeRole, leftNeighborBorders, aboveNeighborBorders);
4139
- const totalHeightPt = mergeRole === "middle" || mergeRole === "end" ? 0 : padTopPt + contentHeightPt + padBottomPt;
4300
+ const alongPt = turned ? lines.reduce((a, l) => Math.max(a, l.contentWidthPt), 0) : 0;
4301
+ const heightPt = turned ? alongPt : contentHeightPt;
4302
+ const totalHeightPt = mergeRole === "middle" || mergeRole === "end" ? 0 : padTopPt + heightPt + padBottomPt;
4140
4303
  return {
4141
4304
  widthPt,
4142
4305
  padTopPt,
@@ -4153,8 +4316,9 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
4153
4316
  ...nestedTables.length > 0 ? { nestedTables } : {},
4154
4317
  ...cellShapes.length > 0 ? { shapes: cellShapes } : {},
4155
4318
  ...cellFloats.length > 0 ? { floats: cellFloats } : {},
4156
- contentHeightPt,
4319
+ contentHeightPt: heightPt,
4157
4320
  totalHeightPt,
4321
+ ...turned ? { textDirection: turned } : {},
4158
4322
  ...cell.properties.verticalAlign ? { verticalAlign: cell.properties.verticalAlign } : {},
4159
4323
  colStart,
4160
4324
  colSpan,
@@ -4209,16 +4373,8 @@ function resolveCellBorders(cellBorders, tableBorders, rowIdx, colStart, colEnd,
4209
4373
  return out;
4210
4374
  }
4211
4375
  function paragraphStructType(resolved) {
4212
- const lvl = resolved.outlineLevel;
4213
- if (lvl !== void 0 && lvl >= 0 && lvl <= 8) return `H${Math.min(lvl, 5) + 1}`;
4214
- return headingFromStyleId(resolved.styleId) ?? "P";
4215
- }
4216
- function headingFromStyleId(styleId) {
4217
- if (!styleId) return null;
4218
- const m = /^Heading\s*([1-9])$/i.exec(styleId);
4219
- if (m) return `H${Math.min(Number(m[1]), 6)}`;
4220
- if (/^(Title|Subtitle)$/i.test(styleId)) return "H1";
4221
- return null;
4376
+ const lvl = headingLevelOf(resolved);
4377
+ return lvl !== void 0 ? `H${String(lvl)}` : "P";
4222
4378
  }
4223
4379
  function dominantParagraphLang(lines) {
4224
4380
  const counts = /* @__PURE__ */ new Map();
@@ -4807,16 +4963,24 @@ var PageAssembler = class PageAssembler {
4807
4963
  * one: stretched over the page it is a blur, and its 128 pixels are 96 points
4808
4964
  * at the resolution Office measures a picture in.
4809
4965
  */
4810
- static tileItems = (resourceName, natural, box) => {
4966
+ static tileItems = (resourceName, natural, box, fromCentre = false) => {
4811
4967
  const tw = Math.max(1, natural.widthPt);
4812
4968
  const th = Math.max(1, natural.heightPt);
4813
- const cols = Math.min(200, Math.ceil(box.width / tw));
4814
- const rows = Math.min(200, Math.ceil(box.height / th));
4969
+ const first = (start, span, step) => {
4970
+ if (!fromCentre) return start;
4971
+ let at = start + span / 2 - step / 2;
4972
+ while (at > start) at -= step;
4973
+ return at;
4974
+ };
4975
+ const x0 = first(box.x, box.width, tw);
4976
+ const y0 = first(box.y, box.height, th);
4977
+ const cols = Math.min(200, Math.max(1, Math.ceil((box.x + box.width - x0) / tw)));
4978
+ const rows = Math.min(200, Math.max(1, Math.ceil((box.y + box.height - y0) / th)));
4815
4979
  const out = [];
4816
4980
  for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) out.push({
4817
4981
  type: "image",
4818
- x: pt(box.x + c * tw),
4819
- y: pt(box.y + r * th),
4982
+ x: pt(x0 + c * tw),
4983
+ y: pt(y0 + r * th),
4820
4984
  width: pt(tw),
4821
4985
  height: pt(th),
4822
4986
  imageResourceName: resourceName
@@ -5191,7 +5355,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
5191
5355
  const chunks = !row.cantSplit && row.heightPt > room && asm.colHasContent() && room > 0 ? splitRowIntoChunks(row, room, page) : row.heightPt > page ? splitRowIntoChunks(row, page) : [row];
5192
5356
  for (let ci = 0; ci < chunks.length; ci++) {
5193
5357
  const chunk = chunks[ci];
5194
- const forcedBreak = ci === 0 && row.breakBefore && !isLeadingHeader && asm.current.length > 0;
5358
+ const forcedBreak = ci === 0 && row.breakBefore && !isLeadingHeader && asm.pageHasContent();
5195
5359
  const overflow = asm.cursorY - chunk.heightPt < asm.bottomLimit() && asm.colHasContent();
5196
5360
  if (forcedBreak || overflow) {
5197
5361
  if (forcedBreak) asm.flushPage();
@@ -5484,6 +5648,29 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
5484
5648
  });
5485
5649
  }
5486
5650
  };
5651
+ if (cell.textDirection !== void 0) {
5652
+ const up = cell.textDirection === "vert270";
5653
+ const along = Math.max(1, boxHeightPt - cell.padTopPt - cell.padBottomPt);
5654
+ const cellBottomYUp = rowTop - boxHeightPt;
5655
+ let across = cell.padLeftPt;
5656
+ for (const line of cell.lines) {
5657
+ const h = computeLineHeight(line, line.resolved);
5658
+ const lineOffset = alignmentOffset(line.resolved.alignment, line.contentWidthPt, along);
5659
+ const off = lineBaselineOffset(line, line.resolved);
5660
+ const baselineAcross = up ? across + h - off : across + off;
5661
+ out.push({
5662
+ type: "line",
5663
+ line,
5664
+ originX: pt(up ? cellX + baselineAcross : cellX + cell.widthPt - baselineAcross),
5665
+ baselineY: pt(pageHeight - (up ? cellBottomYUp + cell.padBottomPt + lineOffset : rowTop - cell.padTopPt - lineOffset)),
5666
+ rotationDeg: up ? 90 : -90,
5667
+ ...structId !== void 0 ? { structId } : {}
5668
+ });
5669
+ across += h;
5670
+ }
5671
+ drawNestedBefore(cell.lines.length);
5672
+ continue;
5673
+ }
5487
5674
  for (const [lineIdx, line] of cell.lines.entries()) {
5488
5675
  drawNestedBefore(lineIdx);
5489
5676
  textY -= cell.lineGaps?.get(lineIdx) ?? 0;
@@ -0,0 +1,41 @@
1
+ import { DocumentWriter, WriteResult } from '../core/ir/adapters.js';
2
+ import { FlowDoc } from '../core/ir/flow.js';
3
+ /** Options for {@link writeMarkdown}. */
4
+ export interface MarkdownWriteOptions {
5
+ /**
6
+ * How a picture reaches the output. `'dataUri'` (the default) inlines the
7
+ * bytes as a `data:` URI, keeping the single-blob {@link WriteResult}
8
+ * contract and the zero-I/O promise; `'link'` emits a relative
9
+ * `./media/…` path and records that the bytes were not written anywhere;
10
+ * `'drop'` omits pictures entirely.
11
+ */
12
+ readonly images?: 'dataUri' | 'link' | 'drop';
13
+ /**
14
+ * What a page break becomes. `'drop'` (the default) leaves it out and
15
+ * reports it: a paginated document breaks its pages wherever the layout
16
+ * needs to, and a rule at every one of them would litter the text. `'rule'`
17
+ * writes a `---` thematic break instead, which is what a SLIDE DECK wants —
18
+ * the `.pptx` and `.ppt` readers mark each slide boundary with a page break,
19
+ * and it is the only structure a deck has.
20
+ */
21
+ readonly pageBreaks?: 'rule' | 'drop';
22
+ }
23
+ /**
24
+ * Render a {@link FlowDoc} to GitHub-Flavored Markdown (ir-design §7).
25
+ *
26
+ * A flow medium: no pagination, no layout engine and no fonts, so this is a
27
+ * pure, zero-I/O transform. Markdown says far less than the document model
28
+ * does — alignment, indents, colour, font metrics, tab stops and page
29
+ * geometry have no expression at all — so those are dropped and reported as
30
+ * {@link Loss} entries, deduplicated so one recurring omission reports once.
31
+ *
32
+ * @param flow The format-neutral interlayer document tree.
33
+ * @param options Picture handling; see {@link MarkdownWriteOptions}.
34
+ * @returns The encoded Markdown bytes plus the recorded {@link Loss} list.
35
+ */
36
+ export declare function writeMarkdown(flow: FlowDoc, options?: MarkdownWriteOptions): WriteResult;
37
+ /**
38
+ * The flow-medium {@link DocumentWriter} adapter (id `'md'`), wrapping
39
+ * {@link writeMarkdown}, with the set of {@link FEATURES} it renders.
40
+ */
41
+ export declare const markdownWriter: DocumentWriter<FlowDoc>;