reamkit 1.22.0 → 1.24.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/esm/core/bmp.d.ts +33 -0
- package/dist/esm/core/bmp.js +276 -0
- package/dist/esm/core/converter/ream.d.ts +16 -0
- package/dist/esm/core/converter/ream.js +59 -9
- package/dist/esm/core/document-model/types.d.ts +55 -0
- package/dist/esm/core/drawingml/chart-geometry.js +14 -7
- package/dist/esm/core/drawingml/colors.d.ts +4 -2
- package/dist/esm/core/drawingml/colors.js +15 -0
- package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
- package/dist/esm/core/drawingml/diagram/colors.js +125 -0
- package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
- package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
- package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
- package/dist/esm/core/drawingml/diagram/run.js +50 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
- package/dist/esm/core/drawingml/preset-geometry.js +116 -6
- package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
- package/dist/esm/core/drawingml/text-warp.js +355 -0
- package/dist/esm/core/drawingml/theme-parser.d.ts +38 -0
- package/dist/esm/core/drawingml/theme-parser.js +73 -4
- package/dist/esm/core/font/measure.d.ts +12 -0
- package/dist/esm/core/font/measure.js +36 -0
- package/dist/esm/core/fonts/families.d.ts +10 -0
- package/dist/esm/core/fonts/families.js +44 -0
- package/dist/esm/core/fonts/index.d.ts +3 -2
- package/dist/esm/core/fonts/remote-fonts.d.ts +50 -0
- package/dist/esm/core/fonts/remote-fonts.js +114 -8
- package/dist/esm/core/fonts/scripts.d.ts +26 -0
- package/dist/esm/core/fonts/scripts.js +177 -0
- package/dist/esm/core/images.d.ts +30 -2
- package/dist/esm/core/images.js +121 -5
- package/dist/esm/core/metafile/blit.d.ts +33 -0
- package/dist/esm/core/metafile/blit.js +67 -0
- package/dist/esm/core/metafile/dib.d.ts +61 -0
- package/dist/esm/core/metafile/dib.js +205 -0
- package/dist/esm/core/metafile/emf.js +280 -38
- package/dist/esm/core/metafile/picture.d.ts +61 -1
- package/dist/esm/core/metafile/picture.js +70 -1
- package/dist/esm/core/metafile/wmf.js +202 -13
- package/dist/esm/core/ole/escher-blip.d.ts +12 -0
- package/dist/esm/core/ole/escher-blip.js +98 -0
- package/dist/esm/{pdf-reader → core}/png-encode.js +1 -1
- package/dist/esm/excel/print-model.js +5 -3
- package/dist/esm/excel/sheet-drawing.js +1 -1
- package/dist/esm/excel/sheet-to-flow.js +16 -2
- package/dist/esm/excel/styles-parser.js +1 -1
- package/dist/esm/excel/xls/biff-reader.js +53 -18
- package/dist/esm/excel/xls/biff-styles.d.ts +5 -2
- package/dist/esm/excel/xls/biff-styles.js +9 -7
- package/dist/esm/excel/xls/escher.js +90 -24
- package/dist/esm/html/html-writer.js +2 -1
- package/dist/esm/layout/page-doc.d.ts +74 -0
- package/dist/esm/layout/page-doc.js +14 -1
- package/dist/esm/layout/styled-layout.d.ts +2 -2
- package/dist/esm/layout/styled-layout.js +475 -48
- package/dist/esm/pdf/shading.js +8 -6
- package/dist/esm/pdf/styled-page-emitter.js +77 -5
- package/dist/esm/pdf/vector-graphics.js +1 -1
- package/dist/esm/pdf-reader/image-decode.js +1 -1
- package/dist/esm/pptx/embedded-fonts.d.ts +31 -0
- package/dist/esm/pptx/embedded-fonts.js +104 -0
- package/dist/esm/pptx/placeholder-cascade.d.ts +73 -6
- package/dist/esm/pptx/placeholder-cascade.js +114 -18
- package/dist/esm/pptx/ppt/ppt-reader.js +191 -13
- package/dist/esm/pptx/ppt/ppt-text.d.ts +107 -1
- package/dist/esm/pptx/ppt/ppt-text.js +873 -144
- package/dist/esm/pptx/pptx-reader.d.ts +8 -0
- package/dist/esm/pptx/pptx-reader.js +117 -15
- package/dist/esm/pptx/preset-table-styles.d.ts +9 -0
- package/dist/esm/pptx/preset-table-styles.js +27 -0
- package/dist/esm/pptx/slide-parser.d.ts +11 -2
- package/dist/esm/pptx/slide-parser.js +203 -44
- package/dist/esm/pptx/sp-helpers.d.ts +9 -4
- package/dist/esm/pptx/sp-helpers.js +48 -9
- package/dist/esm/pptx/table-style.d.ts +16 -1
- package/dist/esm/pptx/table-style.js +104 -10
- package/dist/esm/svg/svg-writer.js +1 -0
- package/dist/esm/word/document-parser.d.ts +11 -2
- package/dist/esm/word/document-parser.js +10 -7
- package/dist/esm/word/docx-reader.js +32 -16
- package/dist/esm/word/docx-to-pdf.d.ts +8 -7
- package/dist/esm/word/docx-to-pdf.js +25 -7
- package/dist/esm/word/docx-writer.js +4 -0
- package/dist/esm/word/drawing-parser.d.ts +20 -0
- package/dist/esm/word/drawing-parser.js +67 -22
- package/dist/esm/word/numbering-parser.d.ts +2 -1
- package/dist/esm/word/numbering-parser.js +6 -6
- package/dist/esm/word/paragraph-properties.d.ts +2 -1
- package/dist/esm/word/paragraph-properties.js +2 -2
- package/dist/esm/word/run-properties.d.ts +5 -2
- package/dist/esm/word/run-properties.js +13 -8
- package/dist/esm/word/styles-parser.d.ts +2 -1
- package/dist/esm/word/styles-parser.js +12 -12
- package/dist/esm/word/table-parser.js +6 -3
- package/dist/esm/word/theme-fonts.d.ts +10 -0
- package/dist/esm/word/theme-fonts.js +28 -0
- package/package.json +1 -1
- /package/dist/esm/{pdf-reader → core}/png-encode.d.ts +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveFamilyStyle } from "../core/fonts/remote-fonts.js";
|
|
2
|
+
import { scriptForCodepoint } from "../core/fonts/scripts.js";
|
|
2
3
|
import { shapeText } from "../core/font/opentype-layout.js";
|
|
3
4
|
import { createFontMeasure } from "../core/font/measure.js";
|
|
4
5
|
import { halfPtToPt, pt } from "../core/ir/units.js";
|
|
@@ -948,8 +949,9 @@ function layoutBodyElement(el, options, fontResources, imageResources, contentWi
|
|
|
948
949
|
if (el.kind === "paragraph") return layoutParagraphBlock(el.paragraph, options, fontResources, imageResources, contentWidth);
|
|
949
950
|
if (el.kind === "table") return layoutTableBlock(el.table, options, fontResources, imageResources, contentWidth);
|
|
950
951
|
if (el.kind === "image") {
|
|
951
|
-
const
|
|
952
|
-
|
|
952
|
+
const res = el.image.resource ? imageResources?.get(el.image.resource) : void 0;
|
|
953
|
+
const meta = res?.metafile;
|
|
954
|
+
return meta ? layoutMetafileBlock(el.image, meta, res.metafileImages, options, fontResources, contentWidth, maxHeight, box) : layoutImageBlock(el.image, imageResources, contentWidth, maxHeight, box);
|
|
953
955
|
}
|
|
954
956
|
if (el.kind === "chart") return layoutChartBlock(el.chart, options, fontResources, contentWidth, maxHeight, imageResources);
|
|
955
957
|
return layoutShapeBlock(el.shape, options, fontResources, imageResources, contentWidth, maxHeight, box);
|
|
@@ -1027,9 +1029,9 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1027
1029
|
const fillGradient = shape.fill.kind === "gradient" ? shape.fill.gradient : void 0;
|
|
1028
1030
|
const fillImage = shape.fill.kind === "picture" && shape.fill.imageResource !== void 0 ? imageResources?.get(shape.fill.imageResource) : void 0;
|
|
1029
1031
|
const fillImageResourceName = fillImage?.resourceName;
|
|
1030
|
-
const fillImageTile = shape.fill.kind === "picture" && shape.fill.tiled === true && fillImage?.prepared ? {
|
|
1031
|
-
widthPt: fillImage.prepared.widthPx * 72 / 96,
|
|
1032
|
-
heightPt: fillImage.prepared.heightPx * 72 / 96
|
|
1032
|
+
const fillImageTile = shape.fill.kind === "picture" && shape.fill.tiled === true && fillImage?.prepared ? shape.fill.tileSizePt ?? {
|
|
1033
|
+
widthPt: fillImage.prepared.widthPx * 72 / (fillImage.prepared.dpiX ?? 96) * (shape.fill.tileScale?.sx ?? 1),
|
|
1034
|
+
heightPt: fillImage.prepared.heightPx * 72 / (fillImage.prepared.dpiY ?? 96) * (shape.fill.tileScale?.sy ?? 1)
|
|
1033
1035
|
} : void 0;
|
|
1034
1036
|
const fillColorHex = shape.fill.kind === "solid" ? shape.fill.colorHex : fillGradient ? gradientToSolid(fillGradient) : void 0;
|
|
1035
1037
|
const stroke = buildStroke(shape.line);
|
|
@@ -1047,8 +1049,9 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1047
1049
|
const textLineGaps = /* @__PURE__ */ new Map();
|
|
1048
1050
|
let textHeightPt = 0;
|
|
1049
1051
|
const vertical = text?.vertical;
|
|
1052
|
+
const warp = text?.warp;
|
|
1050
1053
|
if (text && text.content.length > 0) {
|
|
1051
|
-
const innerWidth = vertical ? Math.max(1, heightPt - insetTopPt - insetBottomPt) : Math.max(1, widthPt - insetLeftPt - insetRightPt);
|
|
1054
|
+
const innerWidth = warp ? Number.MAX_SAFE_INTEGER : vertical ? Math.max(1, heightPt - insetTopPt - insetBottomPt) : Math.max(1, widthPt - insetLeftPt - insetRightPt);
|
|
1052
1055
|
for (const el of text.content) {
|
|
1053
1056
|
if (el.kind === "image") {
|
|
1054
1057
|
const res = el.image.resource ? imageResources?.get(el.image.resource) : void 0;
|
|
@@ -1141,6 +1144,16 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1141
1144
|
}
|
|
1142
1145
|
}
|
|
1143
1146
|
if (text?.autoFit && !vertical && textLines.length > 0) heightPt = textHeightPt + insetTopPt + insetBottomPt;
|
|
1147
|
+
if (text?.shrinkToFit === true && text.fitToBox !== true && warp === void 0 && textLines.length > 0 && fitPass < MAX_FIT_PASSES) {
|
|
1148
|
+
const innerW = Math.max(1, widthPt - insetLeftPt - insetRightPt);
|
|
1149
|
+
const innerH = Math.max(1, heightPt - insetTopPt - insetBottomPt);
|
|
1150
|
+
const widest = textLines.reduce((a, l) => Math.max(a, l.contentWidthPt), 0);
|
|
1151
|
+
const k = Math.min(1, innerW / Math.max(1, widest), innerH / Math.max(1, textHeightPt));
|
|
1152
|
+
if (Number.isFinite(k) && k > .05 && k < .98) return layoutShapeBlock({
|
|
1153
|
+
...shape,
|
|
1154
|
+
text: scaleTextBody(text, k)
|
|
1155
|
+
}, options, fontResources, imageResources, contentWidth, maxHeight, box, fitPass + 1);
|
|
1156
|
+
}
|
|
1144
1157
|
if (text?.fitToBox === true && textLines.length > 0 && fitPass < MAX_FIT_PASSES) {
|
|
1145
1158
|
const innerW = Math.max(1, widthPt - insetLeftPt - insetRightPt);
|
|
1146
1159
|
const innerH = Math.max(1, heightPt - insetTopPt - insetBottomPt);
|
|
@@ -1164,6 +1177,10 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1164
1177
|
text: scaleTextBody(text, k)
|
|
1165
1178
|
}, options, fontResources, imageResources, contentWidth, maxHeight, box, fitPass + 1);
|
|
1166
1179
|
}
|
|
1180
|
+
const warpInk = warp && textLines.length > 0 ? {
|
|
1181
|
+
above: lineInk(textLines[0]).above,
|
|
1182
|
+
below: lineInk(textLines[textLines.length - 1]).below
|
|
1183
|
+
} : void 0;
|
|
1167
1184
|
const paths = buildShapePaths(shape.geometry, widthPt, heightPt);
|
|
1168
1185
|
const markerPaths = lineEndPaths(paths, shape.line, stroke?.widthPt ?? 0);
|
|
1169
1186
|
const scale = shape.width > 0 ? widthPt / shape.width : 1;
|
|
@@ -1185,6 +1202,7 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1185
1202
|
...markerPaths.length > 0 ? { markerPaths } : {},
|
|
1186
1203
|
...fillImageResourceName ? { fillImageResourceName } : {},
|
|
1187
1204
|
...fillImageTile ? { fillImageTile } : {},
|
|
1205
|
+
...fillImageTile && shape.fill.kind === "picture" && shape.fill.tileFromCentre === true ? { fillImageTileFromCentre: true } : {},
|
|
1188
1206
|
...fillImageResourceName && shape.fill.imageCrop ? { fillImageCrop: shape.fill.imageCrop } : {},
|
|
1189
1207
|
...fillImageResourceName && shape.fill.imageFillRect ? { fillImageRect: shape.fill.imageFillRect } : {},
|
|
1190
1208
|
...fillImageResourceName && shape.fill.duotone ? { fillImageDuotone: shape.fill.duotone } : {},
|
|
@@ -1200,6 +1218,12 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1200
1218
|
spacingBeforePt: pp.spacingBefore ?? 0,
|
|
1201
1219
|
spacingAfterPt: pp.spacingAfter ?? 0,
|
|
1202
1220
|
textLines,
|
|
1221
|
+
...warp && warpInk ? { textWarp: {
|
|
1222
|
+
preset: warp.preset,
|
|
1223
|
+
...warp.adjust !== void 0 ? { adjust: warp.adjust } : {},
|
|
1224
|
+
inkAbovePt: warpInk.above,
|
|
1225
|
+
inkBelowPt: warpInk.below
|
|
1226
|
+
} } : {},
|
|
1203
1227
|
...textCharts.size > 0 ? { textCharts } : {},
|
|
1204
1228
|
...textShapes.size > 0 ? { textShapes } : {},
|
|
1205
1229
|
...textTables.size > 0 ? { textTables } : {},
|
|
@@ -1225,11 +1249,11 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1225
1249
|
* frame. So one is turned into the other rather than growing a block kind, a
|
|
1226
1250
|
* pagination arm and an emitter of its own.
|
|
1227
1251
|
*/
|
|
1228
|
-
function layoutMetafileBlock(image, pic, options, fontResources, contentWidth, maxHeight, box) {
|
|
1252
|
+
function layoutMetafileBlock(image, pic, blitted, options, fontResources, contentWidth, maxHeight, box) {
|
|
1229
1253
|
const placed = layoutImageBlock(image, void 0, contentWidth, maxHeight, box);
|
|
1230
1254
|
const widthPt = placed.widthPt;
|
|
1231
1255
|
const heightPt = placed.heightPt;
|
|
1232
|
-
const layout = rotateDrawing(metafileDrawing(pic, widthPt, heightPt, options, fontResources, image.colorChange), (image.rotation60k ?? 0) / 6e4, widthPt / 2, heightPt / 2);
|
|
1256
|
+
const layout = rotateDrawing(metafileDrawing(pic, widthPt, heightPt, options, fontResources, image.colorChange, blitted), (image.rotation60k ?? 0) / 6e4, widthPt / 2, heightPt / 2);
|
|
1233
1257
|
const pp = image.paragraphProperties;
|
|
1234
1258
|
return {
|
|
1235
1259
|
kind: "chart",
|
|
@@ -1293,7 +1317,16 @@ function rotateDrawing(layout, deg, cx, cy) {
|
|
|
1293
1317
|
y: p.y,
|
|
1294
1318
|
rotationDeg: (t.rotationDeg ?? 0) - deg
|
|
1295
1319
|
};
|
|
1296
|
-
})
|
|
1320
|
+
}),
|
|
1321
|
+
...layout.images ? { images: layout.images.map((img) => {
|
|
1322
|
+
const c = at(img.x + img.width / 2, img.y + img.height / 2);
|
|
1323
|
+
return {
|
|
1324
|
+
...img,
|
|
1325
|
+
x: c.x - img.width / 2,
|
|
1326
|
+
y: c.y - img.height / 2,
|
|
1327
|
+
rotationDeg: (img.rotationDeg ?? 0) + deg
|
|
1328
|
+
};
|
|
1329
|
+
}) } : {}
|
|
1297
1330
|
};
|
|
1298
1331
|
}
|
|
1299
1332
|
/**
|
|
@@ -1301,7 +1334,7 @@ function rotateDrawing(layout, deg, cx, cy) {
|
|
|
1301
1334
|
* `heightPt` one and turned y-up about its bottom-left corner — the frame both
|
|
1302
1335
|
* a chart block and an inline picture token are drawn in.
|
|
1303
1336
|
*/
|
|
1304
|
-
function metafileDrawing(pic, widthPt, heightPt, options, fontResources, colorChange) {
|
|
1337
|
+
function metafileDrawing(pic, widthPt, heightPt, options, fontResources, colorChange, blitted) {
|
|
1305
1338
|
const recolour = (hex) => {
|
|
1306
1339
|
if (hex === void 0 || colorChange === void 0) return hex;
|
|
1307
1340
|
if (hex.toUpperCase() !== colorChange.fromHex.toUpperCase()) return hex;
|
|
@@ -1313,8 +1346,26 @@ function metafileDrawing(pic, widthPt, heightPt, options, fontResources, colorCh
|
|
|
1313
1346
|
const mapY = (y) => heightPt - (y - pic.top) * sy;
|
|
1314
1347
|
const shapes = [];
|
|
1315
1348
|
const texts = [];
|
|
1349
|
+
const images = [];
|
|
1316
1350
|
let seq = 0;
|
|
1351
|
+
let blit = 0;
|
|
1317
1352
|
for (const prim of pic.prims) {
|
|
1353
|
+
if (prim.kind === "image") {
|
|
1354
|
+
const resourceName = blitted?.[blit++] ?? "";
|
|
1355
|
+
if (resourceName === "") {
|
|
1356
|
+
seq++;
|
|
1357
|
+
continue;
|
|
1358
|
+
}
|
|
1359
|
+
images.push({
|
|
1360
|
+
seq: seq++,
|
|
1361
|
+
resourceName,
|
|
1362
|
+
x: mapX(prim.x),
|
|
1363
|
+
y: mapY(prim.y + prim.height),
|
|
1364
|
+
width: prim.width * sx,
|
|
1365
|
+
height: prim.height * sy
|
|
1366
|
+
});
|
|
1367
|
+
continue;
|
|
1368
|
+
}
|
|
1318
1369
|
if (prim.kind === "path") {
|
|
1319
1370
|
const fill = recolour(prim.fillColorHex);
|
|
1320
1371
|
const strokeHex = recolour(prim.stroke?.colorHex);
|
|
@@ -1349,7 +1400,6 @@ function metafileDrawing(pic, widthPt, heightPt, options, fontResources, colorCh
|
|
|
1349
1400
|
});
|
|
1350
1401
|
continue;
|
|
1351
1402
|
}
|
|
1352
|
-
if (prim.kind !== "text") continue;
|
|
1353
1403
|
const { variant } = options.registry.resolveByStyle(prim.bold === true, prim.italic === true);
|
|
1354
1404
|
const font = fontResources.get(variant);
|
|
1355
1405
|
if (!font) continue;
|
|
@@ -1372,7 +1422,8 @@ function metafileDrawing(pic, widthPt, heightPt, options, fontResources, colorCh
|
|
|
1372
1422
|
}
|
|
1373
1423
|
return {
|
|
1374
1424
|
shapes,
|
|
1375
|
-
texts
|
|
1425
|
+
texts,
|
|
1426
|
+
...images.length > 0 ? { images } : {}
|
|
1376
1427
|
};
|
|
1377
1428
|
}
|
|
1378
1429
|
function layoutChartBlock(block, options, fontResources, contentWidth, maxHeight, imageResources) {
|
|
@@ -1699,9 +1750,29 @@ function collectImageResources(body, headersFooters, options) {
|
|
|
1699
1750
|
if (!bytes) continue;
|
|
1700
1751
|
if (isEmf(bytes) || isWmf(bytes)) {
|
|
1701
1752
|
try {
|
|
1753
|
+
const metafile = isEmf(bytes) ? readEmf(bytes) : readWmf(bytes);
|
|
1754
|
+
const metafileImages = [];
|
|
1755
|
+
for (const prim of metafile.prims) {
|
|
1756
|
+
if (prim.kind !== "image") continue;
|
|
1757
|
+
let blitted;
|
|
1758
|
+
try {
|
|
1759
|
+
blitted = prepareImage(prim.png, { flattenAlpha });
|
|
1760
|
+
} catch {
|
|
1761
|
+
metafileImages.push("");
|
|
1762
|
+
continue;
|
|
1763
|
+
}
|
|
1764
|
+
counter++;
|
|
1765
|
+
const name = `Im${counter}`;
|
|
1766
|
+
metafileImages.push(name);
|
|
1767
|
+
out.set(`${resourceId}#${String(metafileImages.length)}`, {
|
|
1768
|
+
resourceName: name,
|
|
1769
|
+
prepared: blitted
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1702
1772
|
out.set(resourceId, {
|
|
1703
1773
|
resourceName: "",
|
|
1704
|
-
metafile
|
|
1774
|
+
metafile,
|
|
1775
|
+
...metafileImages.length > 0 ? { metafileImages } : {}
|
|
1705
1776
|
});
|
|
1706
1777
|
} catch {}
|
|
1707
1778
|
continue;
|
|
@@ -1722,7 +1793,7 @@ function collectImageResources(body, headersFooters, options) {
|
|
|
1722
1793
|
}
|
|
1723
1794
|
function chartPageItems(laid, x, bottomYUp, pageHeight, structId) {
|
|
1724
1795
|
const fig = structId !== void 0 ? { structId } : {};
|
|
1725
|
-
const ordered = laid.layout.shapes.some((sh) => sh.seq !== void 0);
|
|
1796
|
+
const ordered = laid.layout.shapes.some((sh) => sh.seq !== void 0) || (laid.layout.images?.length ?? 0) > 0;
|
|
1726
1797
|
const picture = ordered ? { pictureId: nextPictureId++ } : {};
|
|
1727
1798
|
const backdrop = [];
|
|
1728
1799
|
if (laid.background) {
|
|
@@ -1791,7 +1862,23 @@ function chartPageItems(laid, x, bottomYUp, pageHeight, structId) {
|
|
|
1791
1862
|
...picture,
|
|
1792
1863
|
seq: t.seq ?? Number.MAX_SAFE_INTEGER
|
|
1793
1864
|
}));
|
|
1794
|
-
const
|
|
1865
|
+
const blits = (laid.layout.images ?? []).map((img) => ({
|
|
1866
|
+
type: "image",
|
|
1867
|
+
x: pt(x + img.x),
|
|
1868
|
+
y: pt(pageHeight - (bottomYUp + img.y + img.height)),
|
|
1869
|
+
width: pt(img.width),
|
|
1870
|
+
height: pt(img.height),
|
|
1871
|
+
imageResourceName: img.resourceName,
|
|
1872
|
+
...img.rotationDeg ? { rotationDeg: img.rotationDeg } : {},
|
|
1873
|
+
...fig,
|
|
1874
|
+
...picture,
|
|
1875
|
+
seq: img.seq ?? 0
|
|
1876
|
+
}));
|
|
1877
|
+
const all = [
|
|
1878
|
+
...shapes,
|
|
1879
|
+
...texts,
|
|
1880
|
+
...blits
|
|
1881
|
+
];
|
|
1795
1882
|
if (ordered) all.sort((a, b) => a.seq - b.seq);
|
|
1796
1883
|
return [...backdrop, ...all.map(({ seq: _seq, ...item }) => item)];
|
|
1797
1884
|
}
|
|
@@ -1819,6 +1906,10 @@ function emitShapeText(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
|
1819
1906
|
}
|
|
1820
1907
|
return;
|
|
1821
1908
|
}
|
|
1909
|
+
if (sh.textWarp) {
|
|
1910
|
+
emitWarpedText(sh, sh.textWarp, x, bottomYUp, sink, pageHeight, figId);
|
|
1911
|
+
return;
|
|
1912
|
+
}
|
|
1822
1913
|
const shapeTop = bottomYUp + sh.heightPt;
|
|
1823
1914
|
const innerWidth = Math.max(1, sh.widthPt - sh.insetLeftPt - sh.insetRightPt);
|
|
1824
1915
|
let textY;
|
|
@@ -1858,6 +1949,65 @@ function emitShapeText(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
|
1858
1949
|
textY -= sh.textLineGaps?.get(i) ?? 0;
|
|
1859
1950
|
});
|
|
1860
1951
|
}
|
|
1952
|
+
/**
|
|
1953
|
+
* §20.1.9.10 — place a WordArt body's lines in the block's own frame and hand
|
|
1954
|
+
* each one the warp that maps that frame onto the shape's box.
|
|
1955
|
+
*
|
|
1956
|
+
* The block is laid out flat: its lines stack by their own heights, each
|
|
1957
|
+
* aligned within the widest of them, and its ink starts at the box's top-left
|
|
1958
|
+
* corner. Nothing here bends anything — the emitter does that, glyph by glyph,
|
|
1959
|
+
* from the two frames this records.
|
|
1960
|
+
*
|
|
1961
|
+
* @param sh The laid-out shape.
|
|
1962
|
+
* @param warp The shape's warp and the block's ink reach.
|
|
1963
|
+
* @param x The shape's left edge on the page.
|
|
1964
|
+
* @param bottomYUp The shape's bottom edge, y-up from the page's bottom.
|
|
1965
|
+
* @param sink Where the line items are pushed.
|
|
1966
|
+
* @param pageHeight The page's height, for the y-up ⇄ top-left conversion.
|
|
1967
|
+
* @param figId The tagged-PDF Figure this text belongs to, when it has one.
|
|
1968
|
+
*/
|
|
1969
|
+
function emitWarpedText(sh, warp, x, bottomYUp, sink, pageHeight, figId) {
|
|
1970
|
+
const boxX = x + sh.insetLeftPt;
|
|
1971
|
+
const boxY = pageHeight - (bottomYUp + sh.heightPt) + sh.insetTopPt;
|
|
1972
|
+
const boxWidth = Math.max(1, sh.widthPt - sh.insetLeftPt - sh.insetRightPt);
|
|
1973
|
+
const boxHeight = Math.max(1, sh.heightPt - sh.insetTopPt - sh.insetBottomPt);
|
|
1974
|
+
const srcWidth = Math.max(1, ...sh.textLines.map((l) => l.contentWidthPt));
|
|
1975
|
+
const baselines = [];
|
|
1976
|
+
let top = 0;
|
|
1977
|
+
sh.textLines.forEach((line, i) => {
|
|
1978
|
+
const h = computeLineHeight(line, line.resolved);
|
|
1979
|
+
baselines.push(top + h - lineBaselineOffset(line, line.resolved));
|
|
1980
|
+
top += h + (sh.textLineGaps?.get(i) ?? 0);
|
|
1981
|
+
});
|
|
1982
|
+
const first = baselines[0] ?? 0;
|
|
1983
|
+
const last = baselines[baselines.length - 1] ?? 0;
|
|
1984
|
+
const srcTop = boxY;
|
|
1985
|
+
const srcHeight = Math.max(1, last - first + warp.inkAbovePt + warp.inkBelowPt);
|
|
1986
|
+
const shift = srcTop + warp.inkAbovePt - first;
|
|
1987
|
+
sh.textLines.forEach((line, i) => {
|
|
1988
|
+
if (sh.textCharts?.has(i) === true || sh.textShapes?.has(i) === true) return;
|
|
1989
|
+
if (sh.textTables?.has(i) === true) return;
|
|
1990
|
+
sink.push({
|
|
1991
|
+
type: "line",
|
|
1992
|
+
line,
|
|
1993
|
+
originX: pt(boxX + alignmentOffset(line.resolved.alignment, line.contentWidthPt, srcWidth)),
|
|
1994
|
+
baselineY: pt(baselines[i] + shift),
|
|
1995
|
+
warp: {
|
|
1996
|
+
preset: warp.preset,
|
|
1997
|
+
...warp.adjust !== void 0 ? { adjust: warp.adjust } : {},
|
|
1998
|
+
boxX: pt(boxX),
|
|
1999
|
+
boxY: pt(boxY),
|
|
2000
|
+
boxWidth: pt(boxWidth),
|
|
2001
|
+
boxHeight: pt(boxHeight),
|
|
2002
|
+
srcX: pt(boxX),
|
|
2003
|
+
srcWidth: pt(srcWidth),
|
|
2004
|
+
srcTop: pt(srcTop),
|
|
2005
|
+
srcHeight: pt(srcHeight)
|
|
2006
|
+
},
|
|
2007
|
+
...figId !== void 0 ? { structId: figId } : {}
|
|
2008
|
+
});
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
1861
2011
|
/** The page a table is being drawn onto, as a cell's floats need to see it. */
|
|
1862
2012
|
function cellAnchorCtx(asm) {
|
|
1863
2013
|
return {
|
|
@@ -1897,6 +2047,19 @@ function emitCellFloat(cf, ctx, frame) {
|
|
|
1897
2047
|
if (float.wrap !== "none") ctx.exclude(float, x, topYUp, cf.block.widthPt, cf.block.heightPt);
|
|
1898
2048
|
}
|
|
1899
2049
|
function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
2050
|
+
const castsUnderPicture = sh.fillImageResourceName !== void 0 ? sh.shadow : void 0;
|
|
2051
|
+
const shadowedFill = castsUnderPicture !== void 0;
|
|
2052
|
+
const group = shadowedFill ? { pictureId: nextPictureId++ } : {};
|
|
2053
|
+
if (castsUnderPicture) sink.push({
|
|
2054
|
+
type: "shape",
|
|
2055
|
+
shape: {
|
|
2056
|
+
paths: sh.paths,
|
|
2057
|
+
shadow: castsUnderPicture,
|
|
2058
|
+
transform: flipTransform(buildShapeTransform(x, bottomYUp, sh.widthPt, sh.heightPt, sh.rotation60k, sh.flipH, sh.flipV), pageHeight)
|
|
2059
|
+
},
|
|
2060
|
+
...group,
|
|
2061
|
+
...figId !== void 0 ? { structId: figId } : {}
|
|
2062
|
+
});
|
|
1900
2063
|
if (sh.fillImageResourceName) {
|
|
1901
2064
|
const clip = {
|
|
1902
2065
|
paths: sh.paths,
|
|
@@ -1909,12 +2072,13 @@ function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
|
1909
2072
|
y: pageHeight - (bottomYUp + sh.heightPt),
|
|
1910
2073
|
width: sh.widthPt,
|
|
1911
2074
|
height: sh.heightPt
|
|
1912
|
-
})) {
|
|
2075
|
+
}, sh.fillImageTileFromCentre === true)) {
|
|
1913
2076
|
if (item.type !== "image") continue;
|
|
1914
2077
|
sink.push({
|
|
1915
2078
|
...item,
|
|
1916
2079
|
clip,
|
|
1917
2080
|
...fig,
|
|
2081
|
+
...group,
|
|
1918
2082
|
...sh.fillAlpha !== void 0 ? { alpha: sh.fillAlpha } : {}
|
|
1919
2083
|
});
|
|
1920
2084
|
}
|
|
@@ -1935,7 +2099,8 @@ function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
|
1935
2099
|
...sh.fillAlpha !== void 0 ? { alpha: sh.fillAlpha } : {},
|
|
1936
2100
|
...sh.fillImageDuotone ? { duotone: sh.fillImageDuotone } : {},
|
|
1937
2101
|
clip,
|
|
1938
|
-
...fig
|
|
2102
|
+
...fig,
|
|
2103
|
+
...group
|
|
1939
2104
|
});
|
|
1940
2105
|
}
|
|
1941
2106
|
}
|
|
@@ -1956,9 +2121,10 @@ function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
|
1956
2121
|
...sh.fillGradient ? { fillGradient: sh.fillGradient } : {},
|
|
1957
2122
|
...sh.fillAlpha !== void 0 ? { fillAlpha: sh.fillAlpha } : {},
|
|
1958
2123
|
...sh.stroke ? { stroke: sh.stroke } : {},
|
|
1959
|
-
...sh.shadow ? { shadow: sh.shadow } : {},
|
|
2124
|
+
...sh.shadow && !shadowedFill ? { shadow: sh.shadow } : {},
|
|
1960
2125
|
transform: flipTransform(buildShapeTransform(x, bottomYUp, sh.widthPt, sh.heightPt, sh.rotation60k, sh.flipH, sh.flipV), pageHeight)
|
|
1961
2126
|
},
|
|
2127
|
+
...group,
|
|
1962
2128
|
...figId !== void 0 ? { structId: figId } : {}
|
|
1963
2129
|
});
|
|
1964
2130
|
for (const child of sh.children ?? []) emitShapeItems(child.laid, x + child.xPt, bottomYUp + sh.heightPt - child.yPt - child.laid.heightPt, sink, pageHeight, figId);
|
|
@@ -2173,24 +2339,197 @@ function runFontKeyAndParsed(options, ascii, bold, italic) {
|
|
|
2173
2339
|
}
|
|
2174
2340
|
const byFamily = options.registriesByFamily;
|
|
2175
2341
|
if (byFamily && byFamily.size > 0) {
|
|
2176
|
-
|
|
2342
|
+
const named = resolveFamilyStyle(ascii);
|
|
2343
|
+
let key = named.key;
|
|
2177
2344
|
let reg = byFamily.get(key);
|
|
2178
2345
|
if (!reg) {
|
|
2179
2346
|
key = byFamily.keys().next().value;
|
|
2180
2347
|
reg = byFamily.get(key);
|
|
2181
2348
|
}
|
|
2182
|
-
const
|
|
2349
|
+
const wantBold = bold || named.bold === true;
|
|
2350
|
+
const wantItalic = italic || named.italic === true;
|
|
2351
|
+
const { variant, parsed } = reg.resolveByStyle(wantBold, wantItalic);
|
|
2183
2352
|
return {
|
|
2184
2353
|
fontKey: `${key}:${variant}`,
|
|
2185
|
-
parsed
|
|
2354
|
+
parsed,
|
|
2355
|
+
...syntheticFace(variant, wantBold, wantItalic, named.widthScale)
|
|
2186
2356
|
};
|
|
2187
2357
|
}
|
|
2188
2358
|
const { variant, parsed } = options.registry.resolveByStyle(bold, italic);
|
|
2189
2359
|
return {
|
|
2190
2360
|
fontKey: variant,
|
|
2191
|
-
parsed
|
|
2361
|
+
parsed,
|
|
2362
|
+
...syntheticFace(variant, bold, italic, resolveFamilyStyle(ascii).widthScale)
|
|
2363
|
+
};
|
|
2364
|
+
}
|
|
2365
|
+
/**
|
|
2366
|
+
* The face a CHARACTER falls back to when the run's own has no glyph for it.
|
|
2367
|
+
*
|
|
2368
|
+
* The curated substitutes are Latin families: Han, Kana, Hangul, Arabic, Thai
|
|
2369
|
+
* and the geometric symbols are a notdef box in every one of them, and 2145
|
|
2370
|
+
* characters of the corpus are exactly those. A face for the character's
|
|
2371
|
+
* writing system is fetched when the document holds one (see `addScriptFonts`),
|
|
2372
|
+
* and this is where a run reaches it — followed by every other family already
|
|
2373
|
+
* loaded, because a glyph one of them happens to carry is still better than a
|
|
2374
|
+
* box.
|
|
2375
|
+
*
|
|
2376
|
+
* Called from BOTH walks that need it — the glyph-subset collection and the
|
|
2377
|
+
* tokenizer — so the face a character is measured, subset and drawn with is one
|
|
2378
|
+
* decision made once.
|
|
2379
|
+
*
|
|
2380
|
+
* @param options The render options (the loaded registries).
|
|
2381
|
+
* @param cp The code point with no glyph in the run's own face.
|
|
2382
|
+
* @param bold Whether the run is bold…
|
|
2383
|
+
* @param italic …and whether it is italic.
|
|
2384
|
+
* @returns The face to draw it with, or `undefined` when nobody has it.
|
|
2385
|
+
*/
|
|
2386
|
+
function fallbackFaceKey(options, cp, bold, italic) {
|
|
2387
|
+
const byFamily = options.registriesByFamily;
|
|
2388
|
+
if (!byFamily || byFamily.size === 0) return void 0;
|
|
2389
|
+
const script = scriptForCodepoint(cp, byFamily.has("jp") ? "jp" : byFamily.has("kr") ? "kr" : "sc");
|
|
2390
|
+
const order = script ? [script] : [];
|
|
2391
|
+
for (const key of byFamily.keys()) if (key !== script) order.push(key);
|
|
2392
|
+
for (const key of order) {
|
|
2393
|
+
const reg = byFamily.get(key);
|
|
2394
|
+
if (!reg) continue;
|
|
2395
|
+
const { variant, parsed } = reg.resolveByStyle(bold, italic);
|
|
2396
|
+
if (parsed.glyphForCodepoint(cp) !== 0) return {
|
|
2397
|
+
fontKey: `${key}:${variant}`,
|
|
2398
|
+
parsed
|
|
2399
|
+
};
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
/**
|
|
2403
|
+
* §9.2.2 — the face a request had to settle for, and what is left to draw.
|
|
2404
|
+
*
|
|
2405
|
+
* A registry may hold one face and be asked for four: `FontBytesByVariant`
|
|
2406
|
+
* requires only `regular`, so a caller who supplies a single file (or a script
|
|
2407
|
+
* fallback with no bold cut) had every heading drawn plain. What the file does
|
|
2408
|
+
* not carry, the page draws: a stroke around the glyphs for weight, a shear for
|
|
2409
|
+
* slant — the same two tricks a word processor calls faux bold and faux italic.
|
|
2410
|
+
*
|
|
2411
|
+
* @param variant The face the registry actually gave.
|
|
2412
|
+
* @param bold Whether bold was asked for.
|
|
2413
|
+
* @param italic Whether italic was.
|
|
2414
|
+
* @param widthScale The squeeze a condensed NAME asks for, when it does.
|
|
2415
|
+
* @returns `{ synthetic }` when something is missing, `{}` when nothing is.
|
|
2416
|
+
*/
|
|
2417
|
+
function syntheticFace(variant, bold, italic, widthScale) {
|
|
2418
|
+
const missingBold = bold && variant !== "bold" && variant !== "boldItalic";
|
|
2419
|
+
const missingItalic = italic && variant !== "italic" && variant !== "boldItalic";
|
|
2420
|
+
if (!missingBold && !missingItalic && widthScale === void 0) return {};
|
|
2421
|
+
return { synthetic: {
|
|
2422
|
+
...missingBold ? { bold: true } : {},
|
|
2423
|
+
...missingItalic ? { italic: true } : {},
|
|
2424
|
+
...widthScale !== void 0 ? { widthScale } : {}
|
|
2425
|
+
} };
|
|
2426
|
+
}
|
|
2427
|
+
/**
|
|
2428
|
+
* `text` cut into the stretches ONE face draws — the whole string as a single
|
|
2429
|
+
* piece when the run's own face covers it, which is nearly always.
|
|
2430
|
+
*
|
|
2431
|
+
* @param plan The run being tokenized.
|
|
2432
|
+
* @param text The stretch of it to cut.
|
|
2433
|
+
*/
|
|
2434
|
+
function splitByFace(plan, text) {
|
|
2435
|
+
const first = faceOf(plan, text.codePointAt(0) ?? 0);
|
|
2436
|
+
if (!plan.faceFor || plan.faceFor.size === 0) return [{
|
|
2437
|
+
text,
|
|
2438
|
+
face: first
|
|
2439
|
+
}];
|
|
2440
|
+
const out = [];
|
|
2441
|
+
let buf = "";
|
|
2442
|
+
let face = first;
|
|
2443
|
+
for (const ch of text) {
|
|
2444
|
+
const next = faceOf(plan, ch.codePointAt(0));
|
|
2445
|
+
if (buf !== "" && next.font !== face.font) {
|
|
2446
|
+
out.push({
|
|
2447
|
+
text: buf,
|
|
2448
|
+
face
|
|
2449
|
+
});
|
|
2450
|
+
buf = "";
|
|
2451
|
+
}
|
|
2452
|
+
face = next;
|
|
2453
|
+
buf += ch;
|
|
2454
|
+
}
|
|
2455
|
+
if (buf !== "") out.push({
|
|
2456
|
+
text: buf,
|
|
2457
|
+
face
|
|
2458
|
+
});
|
|
2459
|
+
return out;
|
|
2460
|
+
}
|
|
2461
|
+
/**
|
|
2462
|
+
* The face a character of a run is drawn with — its own, unless the run carries
|
|
2463
|
+
* a fallback for that code point (see {@link facesForRun}).
|
|
2464
|
+
*/
|
|
2465
|
+
function faceOf(plan, cp) {
|
|
2466
|
+
return plan.faceFor?.get(cp) ?? {
|
|
2467
|
+
font: plan.font,
|
|
2468
|
+
...plan.synthetic ? { synthetic: plan.synthetic } : {}
|
|
2192
2469
|
};
|
|
2193
2470
|
}
|
|
2471
|
+
/**
|
|
2472
|
+
* `text` cut into the stretches ONE face draws, by the same decision the
|
|
2473
|
+
* tokenizer makes — the whole string as a single span when the run's own face
|
|
2474
|
+
* covers it, which is nearly always.
|
|
2475
|
+
*
|
|
2476
|
+
* @param options The render options (the loaded registries).
|
|
2477
|
+
* @param primary The run's own face.
|
|
2478
|
+
* @param resolved Its resolved properties (weight and slant).
|
|
2479
|
+
* @param text The run's text.
|
|
2480
|
+
*/
|
|
2481
|
+
function faceSpans(options, primary, resolved, text) {
|
|
2482
|
+
const out = [];
|
|
2483
|
+
let cur = primary;
|
|
2484
|
+
let buf = "";
|
|
2485
|
+
for (const ch of text) {
|
|
2486
|
+
const cp = ch.codePointAt(0);
|
|
2487
|
+
const face = primary.parsed.glyphForCodepoint(cp) !== 0 ? primary : fallbackFaceKey(options, cp, resolved.bold, resolved.italic) ?? primary;
|
|
2488
|
+
if (buf !== "" && face.fontKey !== cur.fontKey) {
|
|
2489
|
+
out.push({
|
|
2490
|
+
...cur,
|
|
2491
|
+
text: buf
|
|
2492
|
+
});
|
|
2493
|
+
buf = "";
|
|
2494
|
+
}
|
|
2495
|
+
cur = face;
|
|
2496
|
+
buf += ch;
|
|
2497
|
+
}
|
|
2498
|
+
if (buf !== "") out.push({
|
|
2499
|
+
...cur,
|
|
2500
|
+
text: buf
|
|
2501
|
+
});
|
|
2502
|
+
return out;
|
|
2503
|
+
}
|
|
2504
|
+
/**
|
|
2505
|
+
* The face for every character of `text` the run's own cannot draw.
|
|
2506
|
+
*
|
|
2507
|
+
* @param options The render options (the loaded registries).
|
|
2508
|
+
* @param fontResources The faces the document embeds, by key.
|
|
2509
|
+
* @param primary The run's own face.
|
|
2510
|
+
* @param resolved Its resolved properties (weight and slant).
|
|
2511
|
+
* @param text The run's text.
|
|
2512
|
+
* @returns A map from code point to face, or `undefined` when the run's own
|
|
2513
|
+
* face draws all of it.
|
|
2514
|
+
*/
|
|
2515
|
+
function facesForRun(options, fontResources, primary, resolved, text) {
|
|
2516
|
+
let out;
|
|
2517
|
+
for (const ch of text) {
|
|
2518
|
+
const cp = ch.codePointAt(0);
|
|
2519
|
+
if (out?.has(cp) === true || primary.glyphForCodepoint(cp) !== 0) continue;
|
|
2520
|
+
const fallback = fallbackFaceKey(options, cp, resolved.bold, resolved.italic);
|
|
2521
|
+
if (!fallback) continue;
|
|
2522
|
+
const font = fontResources.get(fallback.fontKey);
|
|
2523
|
+
if (!font) continue;
|
|
2524
|
+
out ??= /* @__PURE__ */ new Map();
|
|
2525
|
+
const { variant } = { variant: fallback.fontKey.split(":")[1] ?? "" };
|
|
2526
|
+
out.set(cp, {
|
|
2527
|
+
font,
|
|
2528
|
+
...syntheticFace(variant === "bold" || variant === "boldItalic" || variant === "italic" ? variant : "regular", resolved.bold, resolved.italic)
|
|
2529
|
+
});
|
|
2530
|
+
}
|
|
2531
|
+
return out;
|
|
2532
|
+
}
|
|
2194
2533
|
function lookupFont(resources, fontKey) {
|
|
2195
2534
|
return resources.get(fontKey) ?? resources.values().next().value;
|
|
2196
2535
|
}
|
|
@@ -2207,8 +2546,22 @@ function collectFontResources(body, headersFooters, options, imageResources) {
|
|
|
2207
2546
|
};
|
|
2208
2547
|
used.set(fontKey, bucket);
|
|
2209
2548
|
}
|
|
2210
|
-
const
|
|
2211
|
-
for (const
|
|
2549
|
+
const text = run.listMarker === true ? markerText(run.text, parsed) : run.text;
|
|
2550
|
+
for (const span of faceSpans(options, {
|
|
2551
|
+
fontKey,
|
|
2552
|
+
parsed
|
|
2553
|
+
}, resolved, text)) {
|
|
2554
|
+
let sink = used.get(span.fontKey);
|
|
2555
|
+
if (!sink) {
|
|
2556
|
+
sink = {
|
|
2557
|
+
parsed: span.parsed,
|
|
2558
|
+
gids: /* @__PURE__ */ new Set()
|
|
2559
|
+
};
|
|
2560
|
+
used.set(span.fontKey, sink);
|
|
2561
|
+
}
|
|
2562
|
+
const shaped = shapeText(span.text, span.parsed.glyphForCodepoint, span.parsed.advanceWidths, span.parsed.ligatures, span.parsed.kerning, span.parsed.joiningForms);
|
|
2563
|
+
for (const g of shaped.gids) sink.gids.add(g);
|
|
2564
|
+
}
|
|
2212
2565
|
if (run.text.includes(" ")) for (const stop of resolveParagraphProperties(para.properties, options.styles).tabs) {
|
|
2213
2566
|
const ch = stop.leader === void 0 ? void 0 : TAB_LEADER_CHARS.get(stop.leader);
|
|
2214
2567
|
if (ch !== void 0) bucket.gids.add(parsed.glyphForCodepoint(ch.codePointAt(0)));
|
|
@@ -2578,7 +2931,7 @@ function tokenizeParagraph(paragraph, options, fontResources, imageResources, co
|
|
|
2578
2931
|
imageWidthPt: widthPt,
|
|
2579
2932
|
imageHeightPt: heightPt,
|
|
2580
2933
|
imageResourceName: res?.resourceName ?? "",
|
|
2581
|
-
...res?.metafile ? { imageMetafile: metafileDrawing(res.metafile, drawBox?.widthPt ?? widthPt, drawBox?.heightPt ?? heightPt, options, fontResources) } : {},
|
|
2934
|
+
...res?.metafile ? { imageMetafile: metafileDrawing(res.metafile, drawBox?.widthPt ?? widthPt, drawBox?.heightPt ?? heightPt, options, fontResources, void 0, res.metafileImages) } : {},
|
|
2582
2935
|
...run.inlineImage.outline ? { imageOutline: run.inlineImage.outline } : {},
|
|
2583
2936
|
...run.inlineImage.shadow ? { imageShadow: run.inlineImage.shadow } : {},
|
|
2584
2937
|
...run.inlineImage.wash ? { imageWash: run.inlineImage.wash } : {},
|
|
@@ -2618,12 +2971,16 @@ function tokenizeParagraph(paragraph, options, fontResources, imageResources, co
|
|
|
2618
2971
|
};
|
|
2619
2972
|
}
|
|
2620
2973
|
const resolvedRun = resolveRunProperties(run.properties, paragraph.properties, options.styles);
|
|
2621
|
-
const { fontKey } = runFontKeyAndParsed(options, resolvedRun.fontFamily.ascii, resolvedRun.bold, resolvedRun.italic);
|
|
2974
|
+
const { fontKey, synthetic } = runFontKeyAndParsed(options, resolvedRun.fontFamily.ascii, resolvedRun.bold, resolvedRun.italic);
|
|
2622
2975
|
const script = SCRIPT_OFFSET[resolvedRun.verticalAlign] ?? 0;
|
|
2976
|
+
const own = lookupFont(fontResources, fontKey);
|
|
2977
|
+
const faceFor = facesForRun(options, fontResources, own.parsed, resolvedRun, run.text);
|
|
2623
2978
|
return {
|
|
2624
2979
|
run,
|
|
2625
2980
|
resolvedRun,
|
|
2626
|
-
font:
|
|
2981
|
+
font: own,
|
|
2982
|
+
...faceFor ? { faceFor } : {},
|
|
2983
|
+
...synthetic ? { synthetic } : {},
|
|
2627
2984
|
fontSizePt: script === 0 ? resolvedRun.fontSizePt : resolvedRun.fontSizePt * SCRIPT_SCALE,
|
|
2628
2985
|
...script === 0 ? {} : { risePt: resolvedRun.fontSizePt * script },
|
|
2629
2986
|
isImage: false,
|
|
@@ -2690,8 +3047,8 @@ function tokenizePlansLtr(plans) {
|
|
|
2690
3047
|
continue;
|
|
2691
3048
|
}
|
|
2692
3049
|
const highlight = (plan.run.commentRangeRefs?.length ?? 0) > 0;
|
|
2693
|
-
for (const t of tokenizeText(plan.run.text)) {
|
|
2694
|
-
const text = plan.run.listMarker === true ? markerText(
|
|
3050
|
+
for (const t of tokenizeText(plan.run.text)) for (const piece of splitByFace(plan, t.text)) {
|
|
3051
|
+
const text = plan.run.listMarker === true ? markerText(piece.text, plan.font.parsed) : piece.text;
|
|
2695
3052
|
tokens.push({
|
|
2696
3053
|
kind: "text",
|
|
2697
3054
|
text,
|
|
@@ -2704,10 +3061,11 @@ function tokenizePlansLtr(plans) {
|
|
|
2704
3061
|
...plan.run.columnBreak ? { columnBreak: true } : {},
|
|
2705
3062
|
...highlight ? { highlight: true } : {},
|
|
2706
3063
|
resolvedRun: plan.resolvedRun,
|
|
2707
|
-
font:
|
|
3064
|
+
font: piece.face.font,
|
|
3065
|
+
...piece.face.synthetic ? { synthetic: piece.face.synthetic } : {},
|
|
2708
3066
|
fontSizePt: plan.fontSizePt,
|
|
2709
3067
|
...plan.risePt !== void 0 ? { risePt: plan.risePt } : {},
|
|
2710
|
-
widthPt: measureRunText(plan, text),
|
|
3068
|
+
widthPt: measureRunText(plan, text, piece.face),
|
|
2711
3069
|
bidiLevel: 0
|
|
2712
3070
|
});
|
|
2713
3071
|
}
|
|
@@ -2747,8 +3105,12 @@ function markerText(text, parsed) {
|
|
|
2747
3105
|
* @param text The token's text.
|
|
2748
3106
|
* @returns The advance width in points.
|
|
2749
3107
|
*/
|
|
2750
|
-
function measureRunText(plan, text) {
|
|
2751
|
-
const
|
|
3108
|
+
function measureRunText(plan, text, face) {
|
|
3109
|
+
const drawn = face ?? {
|
|
3110
|
+
font: plan.font,
|
|
3111
|
+
...plan.synthetic ? { synthetic: plan.synthetic } : {}
|
|
3112
|
+
};
|
|
3113
|
+
const base = drawn.font.measure.textWidthPt(text, plan.fontSizePt) * (drawn.synthetic?.widthScale ?? 1);
|
|
2752
3114
|
const extra = plan.resolvedRun.letterSpacingPt;
|
|
2753
3115
|
return extra === void 0 || extra === 0 ? base : base + extra * [...text].length;
|
|
2754
3116
|
}
|
|
@@ -2795,6 +3157,7 @@ function tokenizePlansBidi(plans, realLevels) {
|
|
|
2795
3157
|
let bufStart = 0;
|
|
2796
3158
|
let curSpace = false;
|
|
2797
3159
|
let curLevel = -1;
|
|
3160
|
+
let curFace = { font: plan.font };
|
|
2798
3161
|
const flush = (endExclusive) => {
|
|
2799
3162
|
if (endExclusive <= bufStart) return;
|
|
2800
3163
|
const raw = chars.slice(bufStart, endExclusive).join("");
|
|
@@ -2810,10 +3173,11 @@ function tokenizePlansBidi(plans, realLevels) {
|
|
|
2810
3173
|
...plan.run.columnBreak ? { columnBreak: true } : {},
|
|
2811
3174
|
...(plan.run.commentRangeRefs?.length ?? 0) > 0 ? { highlight: true } : {},
|
|
2812
3175
|
resolvedRun: plan.resolvedRun,
|
|
2813
|
-
font:
|
|
3176
|
+
font: curFace.font,
|
|
3177
|
+
...curFace.synthetic ? { synthetic: curFace.synthetic } : {},
|
|
2814
3178
|
fontSizePt: plan.fontSizePt,
|
|
2815
3179
|
...plan.risePt !== void 0 ? { risePt: plan.risePt } : {},
|
|
2816
|
-
widthPt: measureRunText(plan, text),
|
|
3180
|
+
widthPt: measureRunText(plan, text, curFace),
|
|
2817
3181
|
bidiLevel: curLevel
|
|
2818
3182
|
});
|
|
2819
3183
|
};
|
|
@@ -2821,14 +3185,17 @@ function tokenizePlansBidi(plans, realLevels) {
|
|
|
2821
3185
|
const ch = chars[c];
|
|
2822
3186
|
const isSpace = /\s/.test(ch);
|
|
2823
3187
|
const level = realLevels[realIdx] ?? 0;
|
|
3188
|
+
const face = faceOf(plan, ch.codePointAt(0));
|
|
2824
3189
|
if (c === 0) {
|
|
2825
3190
|
curSpace = isSpace;
|
|
2826
3191
|
curLevel = level;
|
|
2827
|
-
|
|
3192
|
+
curFace = face;
|
|
3193
|
+
} else if (isSpace !== curSpace || level !== curLevel || face.font !== curFace.font) {
|
|
2828
3194
|
flush(c);
|
|
2829
3195
|
bufStart = c;
|
|
2830
3196
|
curSpace = isSpace;
|
|
2831
3197
|
curLevel = level;
|
|
3198
|
+
curFace = face;
|
|
2832
3199
|
}
|
|
2833
3200
|
realIdx++;
|
|
2834
3201
|
}
|
|
@@ -3381,6 +3748,30 @@ function alignmentOffset(alignment, lineWidth, available) {
|
|
|
3381
3748
|
return 0;
|
|
3382
3749
|
}
|
|
3383
3750
|
/**
|
|
3751
|
+
* How far a line's INK reaches either side of its baseline — the union of its
|
|
3752
|
+
* glyphs' own bounding boxes, not the line box its font metrics describe. Only
|
|
3753
|
+
* WordArt needs this: it stretches the marks it draws onto its shape, and the
|
|
3754
|
+
* empty band an ascender or descender reserves is not one of them.
|
|
3755
|
+
*
|
|
3756
|
+
* @param line The laid-out line.
|
|
3757
|
+
* @returns Points above the baseline the ink rises, and below it that it drops.
|
|
3758
|
+
*/
|
|
3759
|
+
function lineInk(line) {
|
|
3760
|
+
let above = 0;
|
|
3761
|
+
let below = 0;
|
|
3762
|
+
for (const tok of line.tokens) {
|
|
3763
|
+
if (tok.kind !== "text" || tok.isSpace) continue;
|
|
3764
|
+
const ink = tok.font.measure.textInkPt(tok.text, tok.fontSizePt);
|
|
3765
|
+
const rise = tok.risePt ?? 0;
|
|
3766
|
+
above = Math.max(above, ink.above + rise);
|
|
3767
|
+
below = Math.max(below, ink.below - rise);
|
|
3768
|
+
}
|
|
3769
|
+
return {
|
|
3770
|
+
above,
|
|
3771
|
+
below
|
|
3772
|
+
};
|
|
3773
|
+
}
|
|
3774
|
+
/**
|
|
3384
3775
|
* §17.4.85 `w:vMerge` — a vertical merge is ONE cell, so its continuation rows
|
|
3385
3776
|
* are painted with the START cell's shading. Word writes the fill on the cell
|
|
3386
3777
|
* that opens the merge and nothing on the rest; painting only what is written,
|
|
@@ -3656,10 +4047,10 @@ function measureSingleLine(paragraph, options, fontResources) {
|
|
|
3656
4047
|
let total = 0;
|
|
3657
4048
|
for (const run of paragraph.runs) {
|
|
3658
4049
|
const resolved = resolveRunProperties(run.properties, paragraph.properties, options.styles);
|
|
3659
|
-
const { fontKey } = runFontKeyAndParsed(options, resolved.fontFamily.ascii, resolved.bold, resolved.italic);
|
|
4050
|
+
const { fontKey, synthetic } = runFontKeyAndParsed(options, resolved.fontFamily.ascii, resolved.bold, resolved.italic);
|
|
3660
4051
|
const font = lookupFont(fontResources, fontKey);
|
|
3661
4052
|
const fontSizePt = resolved.fontSizePt;
|
|
3662
|
-
total += font.measure.textWidthPt(run.text, fontSizePt);
|
|
4053
|
+
total += font.measure.textWidthPt(run.text, fontSizePt) * (synthetic?.widthScale ?? 1);
|
|
3663
4054
|
}
|
|
3664
4055
|
return total;
|
|
3665
4056
|
}
|
|
@@ -3800,7 +4191,9 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
3800
4191
|
const padRightBase = cellMar?.right ?? tableMar?.right ?? DEFAULT_CELL_PADDING_TWIPS * TWIP_TO_PT;
|
|
3801
4192
|
const padLeftPt = padLeftBase + (cell.properties.icon ? CF_ICON_GUTTER_PT : 0);
|
|
3802
4193
|
const padRightPt = padRightBase;
|
|
4194
|
+
const turned = mergeRole === "middle" || mergeRole === "end" ? void 0 : cell.properties.textDirection;
|
|
3803
4195
|
const innerWidth = Math.max(1, widthPt - padLeftPt - padRightPt);
|
|
4196
|
+
const measureWidth = turned ? NO_WRAP_MEASURE_WIDTH : innerWidth;
|
|
3804
4197
|
const lines = [];
|
|
3805
4198
|
const nestedTables = [];
|
|
3806
4199
|
const cellShapes = [];
|
|
@@ -3819,7 +4212,7 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
3819
4212
|
if (mergeRole !== "middle" && mergeRole !== "end") {
|
|
3820
4213
|
for (const el of cell.content) if (el.kind === "paragraph") {
|
|
3821
4214
|
const noWrap = cell.properties.noWrap === true;
|
|
3822
|
-
const block = layoutParagraphBlock(el.paragraph, options, fontResources, imageResources, noWrap ? NO_WRAP_MEASURE_WIDTH :
|
|
4215
|
+
const block = layoutParagraphBlock(el.paragraph, options, fontResources, imageResources, noWrap ? NO_WRAP_MEASURE_WIDTH : measureWidth);
|
|
3823
4216
|
const cutAt = widthPt - padLeftPt;
|
|
3824
4217
|
if (noWrap && (block.lines[0]?.contentWidthPt ?? 0) > cutAt) clipped = true;
|
|
3825
4218
|
const keep = noWrap ? clipLineToWidth(block.lines, cutAt, cell.properties.hashOnOverflow === true) : block.lines;
|
|
@@ -3870,7 +4263,9 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
3870
4263
|
}
|
|
3871
4264
|
const colEnd = colStart + colSpan - 1;
|
|
3872
4265
|
const borders = resolveCellBorders(cell.properties.borders, tableProps.borders, rowIdx, colStart, colEnd, rowCount, colCount, mergeRole, leftNeighborBorders, aboveNeighborBorders);
|
|
3873
|
-
const
|
|
4266
|
+
const alongPt = turned ? lines.reduce((a, l) => Math.max(a, l.contentWidthPt), 0) : 0;
|
|
4267
|
+
const heightPt = turned ? alongPt : contentHeightPt;
|
|
4268
|
+
const totalHeightPt = mergeRole === "middle" || mergeRole === "end" ? 0 : padTopPt + heightPt + padBottomPt;
|
|
3874
4269
|
return {
|
|
3875
4270
|
widthPt,
|
|
3876
4271
|
padTopPt,
|
|
@@ -3887,8 +4282,9 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
3887
4282
|
...nestedTables.length > 0 ? { nestedTables } : {},
|
|
3888
4283
|
...cellShapes.length > 0 ? { shapes: cellShapes } : {},
|
|
3889
4284
|
...cellFloats.length > 0 ? { floats: cellFloats } : {},
|
|
3890
|
-
contentHeightPt,
|
|
4285
|
+
contentHeightPt: heightPt,
|
|
3891
4286
|
totalHeightPt,
|
|
4287
|
+
...turned ? { textDirection: turned } : {},
|
|
3892
4288
|
...cell.properties.verticalAlign ? { verticalAlign: cell.properties.verticalAlign } : {},
|
|
3893
4289
|
colStart,
|
|
3894
4290
|
colSpan,
|
|
@@ -4541,16 +4937,24 @@ var PageAssembler = class PageAssembler {
|
|
|
4541
4937
|
* one: stretched over the page it is a blur, and its 128 pixels are 96 points
|
|
4542
4938
|
* at the resolution Office measures a picture in.
|
|
4543
4939
|
*/
|
|
4544
|
-
static tileItems = (resourceName, natural, box) => {
|
|
4940
|
+
static tileItems = (resourceName, natural, box, fromCentre = false) => {
|
|
4545
4941
|
const tw = Math.max(1, natural.widthPt);
|
|
4546
4942
|
const th = Math.max(1, natural.heightPt);
|
|
4547
|
-
const
|
|
4548
|
-
|
|
4943
|
+
const first = (start, span, step) => {
|
|
4944
|
+
if (!fromCentre) return start;
|
|
4945
|
+
let at = start + span / 2 - step / 2;
|
|
4946
|
+
while (at > start) at -= step;
|
|
4947
|
+
return at;
|
|
4948
|
+
};
|
|
4949
|
+
const x0 = first(box.x, box.width, tw);
|
|
4950
|
+
const y0 = first(box.y, box.height, th);
|
|
4951
|
+
const cols = Math.min(200, Math.max(1, Math.ceil((box.x + box.width - x0) / tw)));
|
|
4952
|
+
const rows = Math.min(200, Math.max(1, Math.ceil((box.y + box.height - y0) / th)));
|
|
4549
4953
|
const out = [];
|
|
4550
4954
|
for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) out.push({
|
|
4551
4955
|
type: "image",
|
|
4552
|
-
x: pt(
|
|
4553
|
-
y: pt(
|
|
4956
|
+
x: pt(x0 + c * tw),
|
|
4957
|
+
y: pt(y0 + r * th),
|
|
4554
4958
|
width: pt(tw),
|
|
4555
4959
|
height: pt(th),
|
|
4556
4960
|
imageResourceName: resourceName
|
|
@@ -4925,7 +5329,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
4925
5329
|
const chunks = !row.cantSplit && row.heightPt > room && asm.colHasContent() && room > 0 ? splitRowIntoChunks(row, room, page) : row.heightPt > page ? splitRowIntoChunks(row, page) : [row];
|
|
4926
5330
|
for (let ci = 0; ci < chunks.length; ci++) {
|
|
4927
5331
|
const chunk = chunks[ci];
|
|
4928
|
-
const forcedBreak = ci === 0 && row.breakBefore && !isLeadingHeader && asm.
|
|
5332
|
+
const forcedBreak = ci === 0 && row.breakBefore && !isLeadingHeader && asm.pageHasContent();
|
|
4929
5333
|
const overflow = asm.cursorY - chunk.heightPt < asm.bottomLimit() && asm.colHasContent();
|
|
4930
5334
|
if (forcedBreak || overflow) {
|
|
4931
5335
|
if (forcedBreak) asm.flushPage();
|
|
@@ -5218,6 +5622,29 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
|
|
|
5218
5622
|
});
|
|
5219
5623
|
}
|
|
5220
5624
|
};
|
|
5625
|
+
if (cell.textDirection !== void 0) {
|
|
5626
|
+
const up = cell.textDirection === "vert270";
|
|
5627
|
+
const along = Math.max(1, boxHeightPt - cell.padTopPt - cell.padBottomPt);
|
|
5628
|
+
const cellBottomYUp = rowTop - boxHeightPt;
|
|
5629
|
+
let across = cell.padLeftPt;
|
|
5630
|
+
for (const line of cell.lines) {
|
|
5631
|
+
const h = computeLineHeight(line, line.resolved);
|
|
5632
|
+
const lineOffset = alignmentOffset(line.resolved.alignment, line.contentWidthPt, along);
|
|
5633
|
+
const off = lineBaselineOffset(line, line.resolved);
|
|
5634
|
+
const baselineAcross = up ? across + h - off : across + off;
|
|
5635
|
+
out.push({
|
|
5636
|
+
type: "line",
|
|
5637
|
+
line,
|
|
5638
|
+
originX: pt(up ? cellX + baselineAcross : cellX + cell.widthPt - baselineAcross),
|
|
5639
|
+
baselineY: pt(pageHeight - (up ? cellBottomYUp + cell.padBottomPt + lineOffset : rowTop - cell.padTopPt - lineOffset)),
|
|
5640
|
+
rotationDeg: up ? 90 : -90,
|
|
5641
|
+
...structId !== void 0 ? { structId } : {}
|
|
5642
|
+
});
|
|
5643
|
+
across += h;
|
|
5644
|
+
}
|
|
5645
|
+
drawNestedBefore(cell.lines.length);
|
|
5646
|
+
continue;
|
|
5647
|
+
}
|
|
5221
5648
|
for (const [lineIdx, line] of cell.lines.entries()) {
|
|
5222
5649
|
drawNestedBefore(lineIdx);
|
|
5223
5650
|
textY -= cell.lineGaps?.get(lineIdx) ?? 0;
|