@office-kit/pptx-preview 0.14.0 → 0.16.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/README.md +36 -5
- package/dist/{index-DUFPGYOf.d.ts → index-D1r4NkUr.d.ts} +50 -11
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/node.js +2 -2
- package/dist/{src-Bwmf324Z.js → src-u1-ERSHC.js} +544 -120
- package/dist/src-u1-ERSHC.js.map +1 -0
- package/fonts/LICENSES.md +1 -1
- package/package.json +3 -4
- package/dist/src-Bwmf324Z.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { emu, getEffectiveColorMap, getGroupChildren, getGroupTransform, getParagraphAlignment, getParagraphBullet, getParagraphBulletImageBytes, getParagraphBulletStyle, getParagraphIndent, getParagraphLevel, getParagraphPropertiesEffective, getParagraphSpacing, getPresentationFirstSlideNumber, getPresentationFonts, getPresentationTheme, getPresetGeometry, getShapeAdjustValues, getShapeAltTitle, getShapeBodyPrEffective, getShapeBounds, getShapeBoundsResolved, getShapeChartSpec, getShapeClickAction, getShapeCustomGeometry, getShapeDescription, getShapeEffectsEffective, getShapeFillColorResolved, getShapeFillEffective, getShapeFillOpacity, getShapeFlip, getShapeGradientFill, getShapeGradientFillEffective, getShapeHyperlinkTooltip, getShapeId, getShapeImageBiLevelThreshold, getShapeImageBrightness, getShapeImageBytes, getShapeImageContrast, getShapeImageCrop, getShapeImageDuotone, getShapeImageFillBytes, getShapeImageFillLayout, getShapeImageFormat, getShapeImageIntrinsicSize, getShapeImageLinkUrl, getShapeImageOpacity, getShapeImagePartName, getShapeKind, getShapeName, getShapeParagraphCount, getShapeParagraphElements, getShapePatternFill, getShapePlaceholderType, getShapePreset, getShapeRotation, getShapeRunClickAction, getShapeRunFormat, getShapeRunFormatEffective, getShapeRunHyperlink, getShapeRunHyperlinkTooltip, getShapeStrokeArrow, getShapeStrokeCap, getShapeStrokeColorResolved, getShapeStrokeCompound, getShapeStrokeDash, getShapeStrokeEffective, getShapeStrokeJoin, getShapeStrokeOpacity, getShapeText3D, getShapeTextAnchor, getShapeTextAutoFit, getShapeTextAutoFitParams, getShapeTextBodyRotationDeg, getShapeTextColumns, getShapeTextDirection, getShapeTextMargins, getShapeXmlString, getSlideBackground, getSlideBackgroundGradientFill, getSlideBackgroundImageBytes, getSlideBackgroundImageCrop, getSlideBackgroundImageFillLayout, getSlideBackgroundImageIntrinsicSize, getSlideBackgroundImageOpacity, getSlideBackgroundPatternFill, getSlideIndex, getSlideLayout, getSlideLayoutBackground, getSlideLayoutBackgroundGradientFill, getSlideLayoutBackgroundImageBytes, getSlideLayoutBackgroundPatternFill, getSlideLayoutShapes, getSlideMasterBackground, getSlideMasterBackgroundGradientFill, getSlideMasterBackgroundImageBytes, getSlideMasterBackgroundPatternFill, getSlideMasterShapes, getSlideShapes, getSlideSize, getSlides, getTableCellAnchor, getTableCellAppearanceEffective, getTableCellMargins, getTableCellParagraphs, getTableCellRunFormatEffective, getTableCellSpan, getTableCellText3D, getTableCellTextDirection, getTableCells, getTableColumnWidths, getTableDimensions, getTableRowHeights, isChartShape, isParagraphBulletPicture, isShapeHidden, isShapeImageGrayscale, isShapePlaceholder, isShapeTextBox, isSlideBackgroundGraphicsHidden, isSlideLayoutBackgroundGraphicsHidden, isTableShape, resolveDeckBodyTextColor } from "@office-kit/pptx";
|
|
1
|
+
import { emu, getEffectiveColorMap, getGroupChildren, getGroupTransform, getParagraphAlignment, getParagraphBullet, getParagraphBulletImageBytes, getParagraphBulletStyle, getParagraphElementLanguages, getParagraphIndent, getParagraphLevel, getParagraphPropertiesEffective, getParagraphSpacing, getPresentationFirstSlideNumber, getPresentationFonts, getPresentationTheme, getPresetGeometry, getShape3D, getShapeAdjustValues, getShapeAltTitle, getShapeBodyPrEffective, getShapeBounds, getShapeBoundsResolved, getShapeChartSpec, getShapeClickAction, getShapeCustomGeometry, getShapeDescription, getShapeEffectsEffective, getShapeFillColorResolved, getShapeFillEffective, getShapeFillOpacity, getShapeFlip, getShapeGradientFill, getShapeGradientFillEffective, getShapeHyperlinkTooltip, getShapeId, getShapeImageBiLevelThreshold, getShapeImageBrightness, getShapeImageBytes, getShapeImageContrast, getShapeImageCrop, getShapeImageDuotone, getShapeImageFillBytes, getShapeImageFillLayout, getShapeImageFormat, getShapeImageIntrinsicSize, getShapeImageLinkUrl, getShapeImageOpacity, getShapeImagePartName, getShapeKind, getShapeName, getShapeParagraphCount, getShapeParagraphElements, getShapePatternFill, getShapePlaceholderType, getShapePreset, getShapeRotation, getShapeRunClickAction, getShapeRunFormat, getShapeRunFormatEffective, getShapeRunHyperlink, getShapeRunHyperlinkTooltip, getShapeStrokeArrow, getShapeStrokeCap, getShapeStrokeColorResolved, getShapeStrokeCompound, getShapeStrokeDash, getShapeStrokeEffective, getShapeStrokeGradient, getShapeStrokeJoin, getShapeStrokeOpacity, getShapeStrokeSketch, getShapeText3D, getShapeTextAnchor, getShapeTextAutoFit, getShapeTextAutoFitParams, getShapeTextBodyRotationDeg, getShapeTextColumns, getShapeTextDirection, getShapeTextMargins, getShapeXmlString, getSlideBackground, getSlideBackgroundGradientFill, getSlideBackgroundImageBytes, getSlideBackgroundImageCrop, getSlideBackgroundImageFillLayout, getSlideBackgroundImageIntrinsicSize, getSlideBackgroundImageOpacity, getSlideBackgroundPatternFill, getSlideIndex, getSlideLayout, getSlideLayoutBackground, getSlideLayoutBackgroundGradientFill, getSlideLayoutBackgroundImageBytes, getSlideLayoutBackgroundPatternFill, getSlideLayoutShapes, getSlideMasterBackground, getSlideMasterBackgroundGradientFill, getSlideMasterBackgroundImageBytes, getSlideMasterBackgroundPatternFill, getSlideMasterShapes, getSlideShapes, getSlideSize, getSlides, getTableBackgroundEffective, getTableCellAnchor, getTableCellAppearanceEffective, getTableCellMargins, getTableCellParagraphs, getTableCellRunFormatEffective, getTableCellSpan, getTableCellText3D, getTableCellTextDirection, getTableCells, getTableColumnWidths, getTableDimensions, getTableRowHeights, isChartShape, isParagraphBulletPicture, isShapeHidden, isShapeImageGrayscale, isShapePlaceholder, isShapeTextBox, isSlideBackgroundGraphicsHidden, isSlideLayoutBackgroundGraphicsHidden, isTableShape, resolveDeckBodyTextColor } from "@office-kit/pptx";
|
|
2
2
|
//#region src/text-body-rect.ts
|
|
3
3
|
/**
|
|
4
4
|
* Where a shape lays out its text, as fractions of its own extent: the
|
|
@@ -29,7 +29,7 @@ function shapeTextRect(shape) {
|
|
|
29
29
|
* {@link shapeTextRect}, or `null` for the whole box) placed in `bounds`, less
|
|
30
30
|
* the body insets. All coordinates and margins must use the same unit
|
|
31
31
|
* (normally EMU). When margins collapse a region, the region is kept without
|
|
32
|
-
* them, as
|
|
32
|
+
* them, as the reference desktop app does.
|
|
33
33
|
*/
|
|
34
34
|
function resolveTextBodyRect(bounds, margins, region) {
|
|
35
35
|
const x = bounds.x + (region?.l ?? 0) * bounds.w;
|
|
@@ -53,17 +53,17 @@ function resolveTextBodyRect(bounds, margins, region) {
|
|
|
53
53
|
}
|
|
54
54
|
//#endregion
|
|
55
55
|
//#region src/text-body-style.ts
|
|
56
|
-
/** Mac
|
|
56
|
+
/** The reference desktop app on Mac uses zero spacing when `spcCol` is absent. */
|
|
57
57
|
const DEFAULT_COLUMN_GAP_PX = 0;
|
|
58
|
-
const EMU_PER_PX$
|
|
58
|
+
const EMU_PER_PX$3 = 9525;
|
|
59
59
|
const NONE = {
|
|
60
60
|
declarations: "",
|
|
61
61
|
transform: ""
|
|
62
62
|
};
|
|
63
63
|
/**
|
|
64
64
|
* `<a:bodyPr vert="…"/>` as CSS. `writing-mode` is the right primitive for
|
|
65
|
-
* the East-Asian and Mongolian cases, and `text-orientation` for the
|
|
66
|
-
* ones, which stack upright glyphs. `vert270` reads bottom-to-top, which no
|
|
65
|
+
* the East-Asian and Mongolian cases, and `text-orientation` for the
|
|
66
|
+
* `wordArtVert` ones, which stack upright glyphs. `vert270` reads bottom-to-top, which no
|
|
67
67
|
* writing-mode expresses on its own — hence the half turn. Its columns still
|
|
68
68
|
* follow the OOXML vertical-rl progression before that half turn; using
|
|
69
69
|
* vertical-lr here reverses the column order relative to the SVG renderer.
|
|
@@ -101,7 +101,7 @@ function verticalTextStyle(vert) {
|
|
|
101
101
|
*/
|
|
102
102
|
function textColumnsStyle(cols) {
|
|
103
103
|
if (!cols || cols.count < 2) return "";
|
|
104
|
-
const gapPx = cols.gapEmu !== void 0 ? (cols.gapEmu / EMU_PER_PX$
|
|
104
|
+
const gapPx = cols.gapEmu !== void 0 ? (cols.gapEmu / EMU_PER_PX$3).toFixed(2) : DEFAULT_COLUMN_GAP_PX;
|
|
105
105
|
return `column-count:${cols.count};column-gap:${gapPx}px`;
|
|
106
106
|
}
|
|
107
107
|
//#endregion
|
|
@@ -217,6 +217,30 @@ const paragraphNumberLabels = (paraData) => {
|
|
|
217
217
|
return numberLabels;
|
|
218
218
|
};
|
|
219
219
|
//#endregion
|
|
220
|
+
//#region src/decimal-separator.ts
|
|
221
|
+
const separators = /* @__PURE__ */ new Map();
|
|
222
|
+
/**
|
|
223
|
+
* The character a decimal tab stop aligns on for a run's language
|
|
224
|
+
* (`<a:rPr lang>`): `,` for `de-DE`, `.` for `en-US`. Runs without a
|
|
225
|
+
* language, or with one Intl does not know, align on `.`. The reference desktop app on Mac
|
|
226
|
+
* uses the run language too, not the UI or system locale (native capture
|
|
227
|
+
* 2026-10-07: Set Proofing Language to German moved alignment to `,`).
|
|
228
|
+
*/
|
|
229
|
+
function decimalSeparatorOf(lang) {
|
|
230
|
+
if (!lang) return ".";
|
|
231
|
+
let separator = separators.get(lang);
|
|
232
|
+
if (separator === void 0) {
|
|
233
|
+
separator = ".";
|
|
234
|
+
try {
|
|
235
|
+
separator = new Intl.NumberFormat(lang, { numberingSystem: "latn" }).formatToParts(1.5).find((part) => part.type === "decimal")?.value ?? ".";
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (!(error instanceof RangeError)) throw error;
|
|
238
|
+
}
|
|
239
|
+
separators.set(lang, separator);
|
|
240
|
+
}
|
|
241
|
+
return separator;
|
|
242
|
+
}
|
|
243
|
+
//#endregion
|
|
220
244
|
//#region src/emf.ts
|
|
221
245
|
const WORD_BYTES = 4;
|
|
222
246
|
const SHORT_BYTES = 2;
|
|
@@ -493,7 +517,7 @@ const FALLBACK_DESCENT = .22;
|
|
|
493
517
|
const FALLBACK_LINEGAP = .08;
|
|
494
518
|
const BASELINE_LEADING_DROP = .036;
|
|
495
519
|
const GRID_NUDGE_X = -.75;
|
|
496
|
-
const EMU_PER_PX$
|
|
520
|
+
const EMU_PER_PX$2 = 9525;
|
|
497
521
|
let reflectionNamespace = 0;
|
|
498
522
|
const EAST_ASIAN_CHAR = /[ᄀ-ᅟ⺀-〾ぁ-㏿㐀-䶿一-鿿ꀀ-가-힣豈-︰-﹏-⦆¢-₩]/;
|
|
499
523
|
const CLOSING_PUNCT = /^[、。,.・)」』】〉》”’!?:;ー〜…,.!?:;)\]}]+$/;
|
|
@@ -540,6 +564,8 @@ const escapeXml$1 = (s) => s.replace(/[&<>"]/g, (c) => ({
|
|
|
540
564
|
">": ">",
|
|
541
565
|
"\"": """
|
|
542
566
|
})[c] ?? c);
|
|
567
|
+
const CSS_IDENT = /^-?[A-Za-z_\u0080-\u{10FFFF}][\w\u0080-\u{10FFFF}-]*$/u;
|
|
568
|
+
const cssFamilyName = (family) => family.split(" ").every((word) => CSS_IDENT.test(word)) ? family : `"${family.replace(/["\\]/g, "\\$&")}"`;
|
|
543
569
|
const fmt = (n) => {
|
|
544
570
|
const r = Math.round(n * 100) / 100;
|
|
545
571
|
return Object.is(r, -0) ? "0" : String(r);
|
|
@@ -678,7 +704,7 @@ const layoutCore = (input, measure) => {
|
|
|
678
704
|
fieldWidth = decimalWidth = 0;
|
|
679
705
|
} else {
|
|
680
706
|
fieldWidth += token.width;
|
|
681
|
-
const decimal = token.text.indexOf(".");
|
|
707
|
+
const decimal = token.text.indexOf(token.piece.decimalSeparator ?? ".");
|
|
682
708
|
decimalWidth = decimal < 0 ? decimalWidth + token.width : mWidth(token.text.slice(0, decimal), {
|
|
683
709
|
...specOf(token.piece),
|
|
684
710
|
sizePx: renderedSizePxOf(token.piece)
|
|
@@ -836,7 +862,7 @@ const lineContentExtent = (line, dx) => {
|
|
|
836
862
|
};
|
|
837
863
|
};
|
|
838
864
|
const resolveFillPaints = (placements) => {
|
|
839
|
-
if (!placements.some(({ line }) => line.tokens.some((t) => t.piece.fillPaint))) return {
|
|
865
|
+
if (!placements.some(({ line }) => line.tokens.some((t) => t.piece.fillPaint || t.piece.outlinePaint))) return {
|
|
840
866
|
defs: "",
|
|
841
867
|
placements
|
|
842
868
|
};
|
|
@@ -864,12 +890,7 @@ const resolveFillPaints = (placements) => {
|
|
|
864
890
|
};
|
|
865
891
|
const painted = /* @__PURE__ */ new Map();
|
|
866
892
|
let defs = "";
|
|
867
|
-
const
|
|
868
|
-
const repaint = (piece) => {
|
|
869
|
-
const paint = piece.fillPaint;
|
|
870
|
-
if (paint === void 0) return piece;
|
|
871
|
-
let next = pieces.get(piece);
|
|
872
|
-
if (next) return next;
|
|
893
|
+
const resolve = (paint) => {
|
|
873
894
|
let fill = painted.get(paint);
|
|
874
895
|
if (fill === void 0) {
|
|
875
896
|
const built = paint(bounds);
|
|
@@ -877,9 +898,16 @@ const resolveFillPaints = (placements) => {
|
|
|
877
898
|
fill = built.fill;
|
|
878
899
|
painted.set(paint, fill);
|
|
879
900
|
}
|
|
901
|
+
return fill;
|
|
902
|
+
};
|
|
903
|
+
const pieces = /* @__PURE__ */ new Map();
|
|
904
|
+
const repaint = (piece) => {
|
|
905
|
+
let next = pieces.get(piece);
|
|
906
|
+
if (next) return next;
|
|
880
907
|
next = {
|
|
881
908
|
...piece,
|
|
882
|
-
fillHex:
|
|
909
|
+
...piece.fillPaint ? { fillHex: resolve(piece.fillPaint) } : {},
|
|
910
|
+
...piece.outlinePaint ? { outlineHex: resolve(piece.outlinePaint) } : {}
|
|
883
911
|
};
|
|
884
912
|
pieces.set(piece, next);
|
|
885
913
|
return next;
|
|
@@ -888,7 +916,7 @@ const resolveFillPaints = (placements) => {
|
|
|
888
916
|
...placement,
|
|
889
917
|
line: {
|
|
890
918
|
...placement.line,
|
|
891
|
-
tokens: placement.line.tokens.map((token) => token.piece.fillPaint ? {
|
|
919
|
+
tokens: placement.line.tokens.map((token) => token.piece.fillPaint || token.piece.outlinePaint ? {
|
|
892
920
|
...token,
|
|
893
921
|
piece: repaint(token.piece)
|
|
894
922
|
} : token)
|
|
@@ -989,7 +1017,7 @@ const emitPlacements = (placements, effectsOnly = false, effectKind = "all") =>
|
|
|
989
1017
|
if (line.bullet && !effectsOnly) {
|
|
990
1018
|
const b = line.bullet.b;
|
|
991
1019
|
if (b.imageHref) parts.push(`<image x="${fmt(line.bullet.x + dx + GRID_NUDGE_X)}" y="${fmt(baselineY - b.sizePx)}" width="${fmt(b.sizePx)}" height="${fmt(b.sizePx)}" href="${b.imageHref}" xlink:href="${b.imageHref}" preserveAspectRatio="xMidYMid meet"/>`);
|
|
992
|
-
else parts.push(`<text x="${fmt(line.bullet.x + dx + GRID_NUDGE_X)}" y="${fmt(baselineY)}" font-family="${escapeXml$1(b.family)}" font-size="${fmt(b.sizePx)}" fill="${b.fillHex}" xml:space="preserve">${escapeXml$1(b.text)}</text>`);
|
|
1020
|
+
else parts.push(`<text x="${fmt(line.bullet.x + dx + GRID_NUDGE_X)}" y="${fmt(baselineY)}" font-family="${escapeXml$1(b.cssFamily ?? b.family)}" font-size="${fmt(b.sizePx)}" fill="${b.fillHex}" xml:space="preserve">${escapeXml$1(b.text)}</text>`);
|
|
993
1021
|
}
|
|
994
1022
|
parts.push(emitLine(line, baselineY, dx, line.descent, (kind) => nextEffectId(kind), effectsOnly, effectKind));
|
|
995
1023
|
}
|
|
@@ -1086,7 +1114,7 @@ const emitTextReflections = (groups, textAnchor, x0, baselineY, descent, nextEff
|
|
|
1086
1114
|
continue;
|
|
1087
1115
|
}
|
|
1088
1116
|
const scaleY = reflection.scaleY ?? 1;
|
|
1089
|
-
const distPx = (reflection.offsetEmu ?? 0) / EMU_PER_PX$
|
|
1117
|
+
const distPx = (reflection.offsetEmu ?? 0) / EMU_PER_PX$2;
|
|
1090
1118
|
const contactY = baselineY + descent;
|
|
1091
1119
|
const angleRad = (reflection.angleDeg ?? 0) * Math.PI / 180;
|
|
1092
1120
|
const offsetX = distPx * Math.cos(angleRad);
|
|
@@ -1099,7 +1127,7 @@ const emitTextReflections = (groups, textAnchor, x0, baselineY, descent, nextEff
|
|
|
1099
1127
|
const gradientId = `${id}-gradient`;
|
|
1100
1128
|
const maskId = `${id}-mask`;
|
|
1101
1129
|
const filterId = `${id}-blur`;
|
|
1102
|
-
const blurPx = Math.max(0, (reflection.blurEmu ?? 0) / EMU_PER_PX$
|
|
1130
|
+
const blurPx = Math.max(0, (reflection.blurEmu ?? 0) / EMU_PER_PX$2 / 2);
|
|
1103
1131
|
const fadeDirection = reflection.fadeDirection;
|
|
1104
1132
|
const defs = `<defs><linearGradient id="${gradientId}" ${fadeDirection === void 0 || fadeDirection === 90 ? "x1=\"0\" y1=\"1\" x2=\"0\" y2=\"0\"" : (() => {
|
|
1105
1133
|
const radians = fadeDirection * Math.PI / 180;
|
|
@@ -1135,8 +1163,8 @@ const emitTextOuterEffects = (groups, textAnchor, x0, baselineY, nextId) => {
|
|
|
1135
1163
|
if (shadow) {
|
|
1136
1164
|
const id = nextId("outer-shadow");
|
|
1137
1165
|
const angle = (shadow.angleDeg ?? 45) * Math.PI / 180;
|
|
1138
|
-
const distance = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$
|
|
1139
|
-
const blur = Math.max(0, (shadow.blurEmu ?? 50800) / EMU_PER_PX$
|
|
1166
|
+
const distance = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$2;
|
|
1167
|
+
const blur = Math.max(0, (shadow.blurEmu ?? 50800) / EMU_PER_PX$2 / 2);
|
|
1140
1168
|
const glyphSize = renderedSizePxOf(group.piece);
|
|
1141
1169
|
const pad = Math.max(1, distance + blur * 3);
|
|
1142
1170
|
const color = shadow.color ?? "#000000";
|
|
@@ -1146,7 +1174,7 @@ const emitTextOuterEffects = (groups, textAnchor, x0, baselineY, nextId) => {
|
|
|
1146
1174
|
}
|
|
1147
1175
|
if (glow) {
|
|
1148
1176
|
const id = nextId("glow");
|
|
1149
|
-
const radius = Math.max(0, (glow.radiusEmu ?? 63500) / EMU_PER_PX$
|
|
1177
|
+
const radius = Math.max(0, (glow.radiusEmu ?? 63500) / EMU_PER_PX$2);
|
|
1150
1178
|
const glyphSize = renderedSizePxOf(group.piece);
|
|
1151
1179
|
const pad = Math.max(1, radius * 2);
|
|
1152
1180
|
const color = glow.color ?? "#FFFF00";
|
|
@@ -1170,9 +1198,9 @@ const emitTextInnerShadows = (groups, textAnchor, x0, baselineY, nextId) => {
|
|
|
1170
1198
|
}
|
|
1171
1199
|
const id = nextId("inner-shadow");
|
|
1172
1200
|
const angle = (shadow.angleDeg ?? 45) * Math.PI / 180;
|
|
1173
|
-
const dx = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$
|
|
1174
|
-
const dy = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$
|
|
1175
|
-
const blurPx = Math.max(0, (shadow.blurEmu ?? 50800) / EMU_PER_PX$
|
|
1201
|
+
const dx = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$2 * Math.cos(angle);
|
|
1202
|
+
const dy = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$2 * Math.sin(angle);
|
|
1203
|
+
const blurPx = Math.max(0, (shadow.blurEmu ?? 50800) / EMU_PER_PX$2 / 2);
|
|
1176
1204
|
const color = shadow.color ?? "#000000";
|
|
1177
1205
|
const opacity = Math.max(0, Math.min(1, shadow.opacity ?? 1));
|
|
1178
1206
|
const defs = `<defs><filter id="${id}" x="-25%" y="-25%" width="150%" height="150%"><feGaussianBlur in="SourceAlpha" stdDeviation="${fmt(blurPx)}" result="innerBlur"/><feOffset in="innerBlur" dx="${fmt(dx)}" dy="${fmt(dy)}" result="innerOff"/><feComposite in="innerOff" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="innerMask"/><feFlood flood-color="${escapeXml$1(color)}" flood-opacity="${opacity.toFixed(3)}" result="innerColor"/><feComposite in="innerColor" in2="innerMask" operator="in" result="innerOut"/><feComposite in="innerOut" in2="SourceAlpha" operator="in"/></filter></defs>`;
|
|
@@ -1186,8 +1214,8 @@ const emitTextBevel = (groups, textAnchor, x0, baselineY, nextId) => {
|
|
|
1186
1214
|
const bevel = groups.find((g) => g.piece.bevel && !g.isTab && g.width > 0)?.piece.bevel;
|
|
1187
1215
|
if (!bevel) return "";
|
|
1188
1216
|
const id = nextId("bevel");
|
|
1189
|
-
const widthPx = bevel.widthEmu / EMU_PER_PX$
|
|
1190
|
-
const surfaceScale = bevel.heightEmu / EMU_PER_PX$
|
|
1217
|
+
const widthPx = bevel.widthEmu / EMU_PER_PX$2;
|
|
1218
|
+
const surfaceScale = bevel.heightEmu / EMU_PER_PX$2 * bevel.relief;
|
|
1191
1219
|
const table = [...bevel.profile.slice(1).map(() => 0), ...bevel.profile];
|
|
1192
1220
|
const flat = Math.sin(bevel.elevationDeg * Math.PI / 180);
|
|
1193
1221
|
const lightGain = bevel.contrast / (1 - flat);
|
|
@@ -1350,7 +1378,7 @@ const tspan = (g) => {
|
|
|
1350
1378
|
const p = g.piece;
|
|
1351
1379
|
const sizePx = renderedSizePxOf(p);
|
|
1352
1380
|
const attrs = [
|
|
1353
|
-
`font-family="${escapeXml$1(p.family)}"`,
|
|
1381
|
+
`font-family="${escapeXml$1(p.cssFamily ?? p.family)}"`,
|
|
1354
1382
|
`font-size="${fmt(sizePx)}"`,
|
|
1355
1383
|
`fill="${p.fillHex}"`
|
|
1356
1384
|
];
|
|
@@ -1461,7 +1489,7 @@ function browserTextMeasurer() {
|
|
|
1461
1489
|
}
|
|
1462
1490
|
//#endregion
|
|
1463
1491
|
//#region src/text-bevel.ts
|
|
1464
|
-
const DEFAULT_BEVEL_EMU = 76200;
|
|
1492
|
+
const DEFAULT_BEVEL_EMU$1 = 76200;
|
|
1465
1493
|
const DEFAULT_PRESET = "circle";
|
|
1466
1494
|
const DEFAULT_RIG = {
|
|
1467
1495
|
type: "threePt",
|
|
@@ -1582,8 +1610,8 @@ const RELIEF = 2.5;
|
|
|
1582
1610
|
const textBevelOf = (text3d) => {
|
|
1583
1611
|
const bevel = text3d?.bevelTop;
|
|
1584
1612
|
if (!bevel) return null;
|
|
1585
|
-
const widthEmu = bevel.widthEmu ?? DEFAULT_BEVEL_EMU;
|
|
1586
|
-
const heightEmu = bevel.heightEmu ?? DEFAULT_BEVEL_EMU;
|
|
1613
|
+
const widthEmu = bevel.widthEmu ?? DEFAULT_BEVEL_EMU$1;
|
|
1614
|
+
const heightEmu = bevel.heightEmu ?? DEFAULT_BEVEL_EMU$1;
|
|
1587
1615
|
if (widthEmu <= 0 || heightEmu <= 0) return null;
|
|
1588
1616
|
const rig = text3d.scene?.lightRig ?? DEFAULT_RIG;
|
|
1589
1617
|
const look = RIG_LOOK(rig.type);
|
|
@@ -1603,6 +1631,63 @@ const textBevelOf = (text3d) => {
|
|
|
1603
1631
|
};
|
|
1604
1632
|
};
|
|
1605
1633
|
//#endregion
|
|
1634
|
+
//#region src/shape-3d.ts
|
|
1635
|
+
const EMU_PER_PX$1 = 9525;
|
|
1636
|
+
const DEFAULT_BEVEL_EMU = 76200;
|
|
1637
|
+
const LIGHT_AZIMUTH_DEG = 225;
|
|
1638
|
+
const LIGHT_ELEVATION_DEG = 45;
|
|
1639
|
+
const radians = (degrees) => degrees * Math.PI / 180;
|
|
1640
|
+
/**
|
|
1641
|
+
* An SVG `matrix(...)` for the camera's explicit rotation about the point
|
|
1642
|
+
* (`cx`, `cy`) in px, or `null` when the camera keeps the shape face-on.
|
|
1643
|
+
* The camera turns, so the shape appears turned the opposite way: a camera
|
|
1644
|
+
* revolution of +6° (Picture Style "Rotated, White") tilts the picture 6°
|
|
1645
|
+
* counter-clockwise.
|
|
1646
|
+
*/
|
|
1647
|
+
const cameraTransform = (shape3d, cx, cy) => {
|
|
1648
|
+
const rotation = shape3d?.scene?.cameraRotation;
|
|
1649
|
+
if (!rotation) return null;
|
|
1650
|
+
const { latitudeDeg, longitudeDeg, revolutionDeg } = rotation;
|
|
1651
|
+
if (latitudeDeg === 0 && longitudeDeg === 0 && revolutionDeg === 0) return null;
|
|
1652
|
+
const lon = radians(-longitudeDeg);
|
|
1653
|
+
const lat = radians(-latitudeDeg);
|
|
1654
|
+
const rev = radians(-revolutionDeg);
|
|
1655
|
+
const ex = [Math.cos(lon), Math.sin(lon) * Math.sin(lat)];
|
|
1656
|
+
const ey = [0, Math.cos(lat)];
|
|
1657
|
+
const cos = Math.cos(rev);
|
|
1658
|
+
const sin = Math.sin(rev);
|
|
1659
|
+
const turn = (v) => [cos * v[0] - sin * v[1], sin * v[0] + cos * v[1]];
|
|
1660
|
+
const [a, b] = turn(ex);
|
|
1661
|
+
const [c, d] = turn(ey);
|
|
1662
|
+
return `matrix(${[
|
|
1663
|
+
a,
|
|
1664
|
+
b,
|
|
1665
|
+
c,
|
|
1666
|
+
d,
|
|
1667
|
+
cx - a * cx - c * cy,
|
|
1668
|
+
cy - b * cx - d * cy
|
|
1669
|
+
].map((n) => n.toFixed(6)).join(" ")})`;
|
|
1670
|
+
};
|
|
1671
|
+
/**
|
|
1672
|
+
* The `<filter>` body that shades a top bevel onto whatever the shape painted
|
|
1673
|
+
* (picture, fill and border alike): a lit height map built from the blurred
|
|
1674
|
+
* outline, multiplied in so flat areas keep their color and slopes facing
|
|
1675
|
+
* away from the light darken, plus a specular highlight on the lit slopes.
|
|
1676
|
+
* `null` when the shape has no top bevel.
|
|
1677
|
+
*/
|
|
1678
|
+
const bevelFilterPrimitives = (shape3d) => {
|
|
1679
|
+
const bevel = shape3d?.bevelTop;
|
|
1680
|
+
if (!bevel) return null;
|
|
1681
|
+
const widthPx = (bevel.widthEmu ?? DEFAULT_BEVEL_EMU) / EMU_PER_PX$1;
|
|
1682
|
+
const heightPx = (bevel.heightEmu ?? DEFAULT_BEVEL_EMU) / EMU_PER_PX$1;
|
|
1683
|
+
if (widthPx <= 0 || heightPx <= 0) return null;
|
|
1684
|
+
const blur = widthPx / 2;
|
|
1685
|
+
const surfaceScale = Math.min(10, Math.max(1, heightPx / widthPx * 6));
|
|
1686
|
+
const diffuse = 1 / Math.sin(radians(LIGHT_ELEVATION_DEG));
|
|
1687
|
+
const light = `<feDistantLight azimuth="${LIGHT_AZIMUTH_DEG}" elevation="${LIGHT_ELEVATION_DEG}"/>`;
|
|
1688
|
+
return `<feGaussianBlur in="SourceAlpha" stdDeviation="${blur.toFixed(2)}" result="bevelHeight"/><feDiffuseLighting in="bevelHeight" surfaceScale="${surfaceScale.toFixed(2)}" diffuseConstant="${diffuse.toFixed(4)}" lighting-color="#fff" result="bevelShade">${light}</feDiffuseLighting><feComposite in="SourceGraphic" in2="bevelShade" operator="arithmetic" k1="1" result="bevelLit"/><feSpecularLighting in="bevelHeight" surfaceScale="${surfaceScale.toFixed(2)}" specularConstant="0.6" specularExponent="20" lighting-color="#fff" result="bevelSpec">${light}</feSpecularLighting><feComposite in="bevelSpec" in2="SourceAlpha" operator="in" result="bevelGloss"/><feComposite in="bevelLit" in2="bevelGloss" operator="arithmetic" k2="1" k3="1"/>`;
|
|
1689
|
+
};
|
|
1690
|
+
//#endregion
|
|
1606
1691
|
//#region src/render-slide.ts
|
|
1607
1692
|
const clickActionHref = (pres, action) => {
|
|
1608
1693
|
if (!action) return void 0;
|
|
@@ -1631,6 +1716,10 @@ const DEFAULT_BODY_PT = 18;
|
|
|
1631
1716
|
const DEFAULT_TITLE_PT = 44;
|
|
1632
1717
|
const DEFAULT_FONT = "Calibri, 'Helvetica Neue', Arial, sans-serif";
|
|
1633
1718
|
const browserFontFamily = (family) => family ? `${JSON.stringify(family)}, ${DEFAULT_FONT}` : DEFAULT_FONT;
|
|
1719
|
+
const BROWSER_FAMILIES = {
|
|
1720
|
+
resolveFamily: browserFontFamily,
|
|
1721
|
+
familyIsCssList: true
|
|
1722
|
+
};
|
|
1634
1723
|
const DEFAULT_BULLET_FONT = "Arial";
|
|
1635
1724
|
const DEFAULT_INSET_X = 91440;
|
|
1636
1725
|
const DEFAULT_INSET_Y = 45720;
|
|
@@ -1749,20 +1838,64 @@ const renderPicture = (shape, pres, x, y, w, h, transform, textOverlay, bytes, f
|
|
|
1749
1838
|
}
|
|
1750
1839
|
const opacityAttr = opacity !== 1 ? ` opacity="${opacity.toFixed(3)}"` : "";
|
|
1751
1840
|
const border = outline.stroke !== "none" && outline.strokeWidth > 0 ? `<g${transform} fill="none" stroke="${outline.stroke}" stroke-width="${E(outline.strokeWidth)}"${outline.strokeAttrs ? ` ${outline.strokeAttrs}` : ""}>${pictureClipGeometry(shape, preset, x, y, w, h)}</g>` : "";
|
|
1841
|
+
const under = outline.fill !== "none" ? `${outline.defs}<g${transform} fill="${outline.fill}"${outline.fillAttrs}>${pictureClipGeometry(shape, preset, x, y, w, h)}</g>` : "";
|
|
1752
1842
|
if (layout?.mode === "tile") {
|
|
1753
1843
|
const intrinsic = getShapeImageIntrinsicSize(shape);
|
|
1754
1844
|
if (intrinsic) {
|
|
1755
1845
|
const pattern = imageTilePattern(dataUrl, layout, intrinsic, x, y, w, h, cropL, cropT, cropR, cropB);
|
|
1756
1846
|
const geometry = pictureClipGeometry(shape, preset, x, y, w, h);
|
|
1757
|
-
return `${clipDef}${pattern.defs}
|
|
1847
|
+
return `${clipDef}${pattern.defs}${pictureEffects(pres, shape, {
|
|
1848
|
+
x,
|
|
1849
|
+
y,
|
|
1850
|
+
w,
|
|
1851
|
+
h
|
|
1852
|
+
}, `${under}<g${transform} fill="${pattern.fill}"${filterAttr}${opacityAttr}>${geometry}</g>${border}`)}<g${transform}>${textOverlay}</g>`;
|
|
1758
1853
|
}
|
|
1759
1854
|
}
|
|
1760
|
-
return `${clipDef}
|
|
1855
|
+
return `${clipDef}${pictureEffects(pres, shape, {
|
|
1856
|
+
x,
|
|
1857
|
+
y,
|
|
1858
|
+
w,
|
|
1859
|
+
h
|
|
1860
|
+
}, `${under}<g${transform}${clipAttr}><image x="${E(imgX)}" y="${E(imgY)}" width="${E(imgW)}" height="${E(imgH)}" href="${dataUrl}" xlink:href="${dataUrl}" preserveAspectRatio="none"${filterAttr}${opacityAttr}/></g>${border}`)}<g${transform}>${textOverlay}</g>`;
|
|
1761
1861
|
}
|
|
1762
1862
|
const linkUrl = getShapeImageLinkUrl(shape);
|
|
1763
1863
|
const label = !bytes ? linkUrl ? `picture (link: ${linkUrl.length > 48 ? linkUrl.slice(0, 45) + "…" : linkUrl})` : "picture (no bytes)" : `picture (${format ?? "unknown"}${bytes ? `, ${bytes.byteLength} B` : ""})`;
|
|
1764
1864
|
return `<g data-pptx-fallback="image"${transform}><rect x="${E(x)}" y="${E(y)}" width="${E(w)}" height="${E(h)}" fill="#F3F4F6" stroke="#9CA3AF" stroke-width="${E(9525)}" stroke-dasharray="${E(5e4)},${E(3e4)}"/>${renderPicturePlaceholderLabel(x, y, w, h, label)}${textOverlay}</g>`;
|
|
1765
1865
|
};
|
|
1866
|
+
/**
|
|
1867
|
+
* Wraps a picture's markup in its 3-D approximation (bevel lighting, then the
|
|
1868
|
+
* camera rotation) and its `<a:effectLst>` (shadows, glow, soft edge and the
|
|
1869
|
+
* reflection), the same effects a shape gets.
|
|
1870
|
+
*/
|
|
1871
|
+
const pictureEffects = (pres, shape, box, markup) => {
|
|
1872
|
+
const shape3d = getShape3D(shape);
|
|
1873
|
+
let defs = "";
|
|
1874
|
+
let content = markup;
|
|
1875
|
+
let mirrored = markup;
|
|
1876
|
+
const bevel = bevelFilterPrimitives(shape3d);
|
|
1877
|
+
if (bevel) {
|
|
1878
|
+
const id = mintId();
|
|
1879
|
+
defs += `<defs><filter id="${id}" x="-10%" y="-10%" width="120%" height="120%" color-interpolation-filters="sRGB">${bevel}</filter></defs>`;
|
|
1880
|
+
content = `<g filter="url(#${id})">${content}</g>`;
|
|
1881
|
+
}
|
|
1882
|
+
const camera = cameraTransform(shape3d, (box.x + box.w / 2) / EMU_PER_PX, (box.y + box.h / 2) / EMU_PER_PX);
|
|
1883
|
+
if (camera) {
|
|
1884
|
+
content = `<g transform="${camera}">${content}</g>`;
|
|
1885
|
+
mirrored = `<g transform="${camera}">${mirrored}</g>`;
|
|
1886
|
+
}
|
|
1887
|
+
const reflection = buildReflection(pres, shape, mirrored, box);
|
|
1888
|
+
const fx = buildEffectsFilter(pres, shape);
|
|
1889
|
+
if (fx) {
|
|
1890
|
+
defs += fx.defs;
|
|
1891
|
+
content = `<g filter="url(#${fx.id})">${content}</g>`;
|
|
1892
|
+
}
|
|
1893
|
+
if (reflection) {
|
|
1894
|
+
defs += reflection.defs;
|
|
1895
|
+
content = reflection.svg + content;
|
|
1896
|
+
}
|
|
1897
|
+
return defs + content;
|
|
1898
|
+
};
|
|
1766
1899
|
const imageTilePattern = (dataUrl, layout, intrinsic, x, y, w, h, cropL = 0, cropT = 0, cropR = 0, cropB = 0) => {
|
|
1767
1900
|
const sourceW = intrinsic.width * (layout.scaleX ?? 1);
|
|
1768
1901
|
const sourceH = intrinsic.height * (layout.scaleY ?? 1);
|
|
@@ -2064,7 +2197,22 @@ const patternDef = (pat) => {
|
|
|
2064
2197
|
fillAttr: `url(#${id})`
|
|
2065
2198
|
};
|
|
2066
2199
|
};
|
|
2067
|
-
const textFillPaint = (fill, theme) => (box) => {
|
|
2200
|
+
const textFillPaint = (fill, theme, solid) => (box) => {
|
|
2201
|
+
if (fill.kind === "none") return {
|
|
2202
|
+
defs: "",
|
|
2203
|
+
fill: "none"
|
|
2204
|
+
};
|
|
2205
|
+
if (fill.kind === "image") {
|
|
2206
|
+
if (!fill.bytes) return {
|
|
2207
|
+
defs: "",
|
|
2208
|
+
fill: solid
|
|
2209
|
+
};
|
|
2210
|
+
const id = mintId();
|
|
2211
|
+
return {
|
|
2212
|
+
defs: `<defs><pattern id="${id}" patternUnits="userSpaceOnUse" x="${box.x.toFixed(2)}" y="${box.y.toFixed(2)}" width="${box.w.toFixed(2)}" height="${box.h.toFixed(2)}"><image href="${bytesToDataUrl(fill.bytes)}" width="${box.w.toFixed(2)}" height="${box.h.toFixed(2)}" preserveAspectRatio="none"/></pattern></defs>`,
|
|
2213
|
+
fill: `url(#${id})`
|
|
2214
|
+
};
|
|
2215
|
+
}
|
|
2068
2216
|
if (fill.kind === "pattern") {
|
|
2069
2217
|
const built = patternDef({
|
|
2070
2218
|
preset: fill.preset,
|
|
@@ -2122,6 +2270,32 @@ const buildArrowMarker = (type, width, length, color, orient) => {
|
|
|
2122
2270
|
def: `<defs><marker id="${id}" viewBox="0 0 ${w} ${h}" refX="${w}" refY="${h / 2}" markerWidth="${w}" markerHeight="${h}" orient="${orient}">${body}</marker></defs>`
|
|
2123
2271
|
};
|
|
2124
2272
|
};
|
|
2273
|
+
const unrotatedGradientTransform = (grad, shape, pres) => {
|
|
2274
|
+
const bounds = shape && (pres ? getShapeBoundsResolved(pres, shape) : getShapeBounds(shape));
|
|
2275
|
+
if (grad.rotateWithShape !== false || !shape || !bounds || bounds.w <= 0 || bounds.h <= 0) return "";
|
|
2276
|
+
const angle = getShapeRotation(shape) * Math.PI / 180;
|
|
2277
|
+
const cos = Math.cos(angle);
|
|
2278
|
+
const sin = Math.sin(angle);
|
|
2279
|
+
const w = bounds.w;
|
|
2280
|
+
const h = bounds.h;
|
|
2281
|
+
const rotatedW = Math.abs(cos) * w + Math.abs(sin) * h;
|
|
2282
|
+
const rotatedH = Math.abs(sin) * w + Math.abs(cos) * h;
|
|
2283
|
+
const flip = getShapeFlip(shape);
|
|
2284
|
+
const sx = flip?.horizontal ? -1 : 1;
|
|
2285
|
+
const sy = flip?.vertical ? -1 : 1;
|
|
2286
|
+
const a = sx * cos * rotatedW / w;
|
|
2287
|
+
const b = -sy * sin * rotatedW / h;
|
|
2288
|
+
const c = sx * sin * rotatedH / w;
|
|
2289
|
+
const d = sy * cos * rotatedH / h;
|
|
2290
|
+
return ` gradientTransform="matrix(${[
|
|
2291
|
+
a,
|
|
2292
|
+
b,
|
|
2293
|
+
c,
|
|
2294
|
+
d,
|
|
2295
|
+
(1 - a - c) / 2,
|
|
2296
|
+
(1 - b - d) / 2
|
|
2297
|
+
].map((value) => value.toFixed(6)).join(" ")})"`;
|
|
2298
|
+
};
|
|
2125
2299
|
const paint = (shape, fill, stroke, theme, isPlaceholder, pres) => {
|
|
2126
2300
|
let fillColor;
|
|
2127
2301
|
let fillAttrs = "";
|
|
@@ -2136,33 +2310,7 @@ const paint = (shape, fill, stroke, theme, isPlaceholder, pres) => {
|
|
|
2136
2310
|
else if (fill.kind === "gradient") {
|
|
2137
2311
|
const grad = shape ? pres ? getShapeGradientFillEffective(pres, shape) : getShapeGradientFill(shape) : null;
|
|
2138
2312
|
if (grad) {
|
|
2139
|
-
|
|
2140
|
-
const bounds = shape && (pres ? getShapeBoundsResolved(pres, shape) : getShapeBounds(shape));
|
|
2141
|
-
if (grad.rotateWithShape === false && shape && bounds && bounds.w > 0 && bounds.h > 0) {
|
|
2142
|
-
const angle = getShapeRotation(shape) * Math.PI / 180;
|
|
2143
|
-
const cos = Math.cos(angle);
|
|
2144
|
-
const sin = Math.sin(angle);
|
|
2145
|
-
const w = bounds.w;
|
|
2146
|
-
const h = bounds.h;
|
|
2147
|
-
const rotatedW = Math.abs(cos) * w + Math.abs(sin) * h;
|
|
2148
|
-
const rotatedH = Math.abs(sin) * w + Math.abs(cos) * h;
|
|
2149
|
-
const flip = getShapeFlip(shape);
|
|
2150
|
-
const sx = flip?.horizontal ? -1 : 1;
|
|
2151
|
-
const sy = flip?.vertical ? -1 : 1;
|
|
2152
|
-
const a = sx * cos * rotatedW / w;
|
|
2153
|
-
const b = -sy * sin * rotatedW / h;
|
|
2154
|
-
const c = sx * sin * rotatedH / w;
|
|
2155
|
-
const d = sy * cos * rotatedH / h;
|
|
2156
|
-
transform = ` gradientTransform="matrix(${[
|
|
2157
|
-
a,
|
|
2158
|
-
b,
|
|
2159
|
-
c,
|
|
2160
|
-
d,
|
|
2161
|
-
(1 - a - c) / 2,
|
|
2162
|
-
(1 - b - d) / 2
|
|
2163
|
-
].map((value) => value.toFixed(6)).join(" ")})"`;
|
|
2164
|
-
}
|
|
2165
|
-
const built = gradientDef(grad, theme, transform);
|
|
2313
|
+
const built = gradientDef(grad, theme, unrotatedGradientTransform(grad, shape, pres));
|
|
2166
2314
|
defs = built.defs;
|
|
2167
2315
|
fillColor = built.fillAttr;
|
|
2168
2316
|
} else fillColor = (shape && pres ? getShapeFillColorResolved(pres, shape) : null) ?? "none";
|
|
@@ -2179,10 +2327,34 @@ const paint = (shape, fill, stroke, theme, isPlaceholder, pres) => {
|
|
|
2179
2327
|
let strokeWidth = 0;
|
|
2180
2328
|
const strokeAttrParts = [];
|
|
2181
2329
|
let markerAttrs = "";
|
|
2330
|
+
let headColor = strokeColor;
|
|
2331
|
+
let tailColor = strokeColor;
|
|
2182
2332
|
if (stroke.kind === "solid") {
|
|
2183
2333
|
let resolved = null;
|
|
2184
2334
|
if (shape && pres) resolved = getShapeStrokeColorResolved(pres, shape);
|
|
2185
2335
|
strokeColor = resolved ?? resolveColor(stroke.color, theme, "#9CA3AF");
|
|
2336
|
+
headColor = strokeColor;
|
|
2337
|
+
tailColor = strokeColor;
|
|
2338
|
+
} else if (stroke.kind === "gradient") {
|
|
2339
|
+
const grad = shape ? getShapeStrokeGradient(shape, pres) : null;
|
|
2340
|
+
const bounds = shape && (pres ? getShapeBoundsResolved(pres, shape) : getShapeBounds(shape));
|
|
2341
|
+
if (grad && bounds) {
|
|
2342
|
+
const stopColor = (stop) => stop ? stop.resolvedColor ?? resolveColor(stop.color, theme, "#9CA3AF") : "#9CA3AF";
|
|
2343
|
+
const ordered = [...grad.stops].sort((a, b) => a.offset - b.offset);
|
|
2344
|
+
headColor = stopColor(ordered[0]);
|
|
2345
|
+
tailColor = stopColor(ordered[ordered.length - 1]);
|
|
2346
|
+
const widthEmu = stroke.widthEmu ?? 9525;
|
|
2347
|
+
const built = bounds.w > 0 && bounds.h > 0 ? gradientDef(grad, theme, unrotatedGradientTransform(grad, shape, pres)) : gradientDef(grad, theme, "", {
|
|
2348
|
+
x: (bounds.w > 0 ? bounds.x : bounds.x - widthEmu / 2) / EMU_PER_PX,
|
|
2349
|
+
y: (bounds.h > 0 ? bounds.y : bounds.y - widthEmu / 2) / EMU_PER_PX,
|
|
2350
|
+
w: Math.max(bounds.w, widthEmu) / EMU_PER_PX,
|
|
2351
|
+
h: Math.max(bounds.h, widthEmu) / EMU_PER_PX
|
|
2352
|
+
});
|
|
2353
|
+
defs += built.defs;
|
|
2354
|
+
strokeColor = built.fillAttr;
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
if (strokeColor !== "none" && (stroke.kind === "solid" || stroke.kind === "gradient")) {
|
|
2186
2358
|
strokeWidth = stroke.widthEmu ?? 9525;
|
|
2187
2359
|
if (shape) {
|
|
2188
2360
|
const opacity = getShapeStrokeOpacity(shape, pres);
|
|
@@ -2208,12 +2380,12 @@ const paint = (shape, fill, stroke, theme, isPlaceholder, pres) => {
|
|
|
2208
2380
|
const head = getShapeStrokeArrow(shape, "head", pres);
|
|
2209
2381
|
const tail = getShapeStrokeArrow(shape, "tail", pres);
|
|
2210
2382
|
if (head && head.type !== "none") {
|
|
2211
|
-
const m = buildArrowMarker(head.type, head.width, head.length,
|
|
2383
|
+
const m = buildArrowMarker(head.type, head.width, head.length, headColor, "auto-start-reverse");
|
|
2212
2384
|
defs += m.def;
|
|
2213
2385
|
markerAttrs += ` marker-start="url(#${m.id})"`;
|
|
2214
2386
|
}
|
|
2215
2387
|
if (tail && tail.type !== "none") {
|
|
2216
|
-
const m = buildArrowMarker(tail.type, tail.width, tail.length,
|
|
2388
|
+
const m = buildArrowMarker(tail.type, tail.width, tail.length, tailColor, "auto");
|
|
2217
2389
|
defs += m.def;
|
|
2218
2390
|
markerAttrs += ` marker-end="url(#${m.id})"`;
|
|
2219
2391
|
}
|
|
@@ -2260,7 +2432,7 @@ const cssColorWithOpacity = (hex, opacity) => {
|
|
|
2260
2432
|
};
|
|
2261
2433
|
const renderRun = (text, format, theme, effectivePt, defaultColor, _wasDefault = false, suppressLineHeight = false, lineHeightOverride = void 0) => {
|
|
2262
2434
|
if (text === "") return "";
|
|
2263
|
-
if (format?.textFill) {
|
|
2435
|
+
if (format?.textFill || format?.outline?.fill) {
|
|
2264
2436
|
const { textFill: _fill, outline: _outline, shadow: _shadow, glow: _glow, underline: _underline, strike: _strike, ...layout } = format;
|
|
2265
2437
|
return renderRun(text, {
|
|
2266
2438
|
...layout,
|
|
@@ -2320,6 +2492,10 @@ const renderRun = (text, format, theme, effectivePt, defaultColor, _wasDefault =
|
|
|
2320
2492
|
const LINE_HEIGHT = 1.05;
|
|
2321
2493
|
const AUTOFIT_FLOOR = .25;
|
|
2322
2494
|
const AUTOFIT_STEP = .05;
|
|
2495
|
+
const decimalSeparatorField = (lang) => {
|
|
2496
|
+
const separator = decimalSeparatorOf(lang);
|
|
2497
|
+
return separator === "." ? {} : { decimalSeparator: separator };
|
|
2498
|
+
};
|
|
2323
2499
|
const underlineStyleOf = (fmt) => {
|
|
2324
2500
|
const u = fmt?.underline;
|
|
2325
2501
|
if (u === void 0 || u === false || u === "none") return "none";
|
|
@@ -2349,7 +2525,7 @@ const strikeStyleOf = (fmt) => {
|
|
|
2349
2525
|
if (s === "dblStrike") return "double";
|
|
2350
2526
|
return s !== void 0 && s !== false && s !== "noStrike";
|
|
2351
2527
|
};
|
|
2352
|
-
const hasPaintedTextFill = (run) => !!run.fmt?.textFill && !run.href;
|
|
2528
|
+
const hasPaintedTextFill = (run) => !!(run.fmt?.textFill || run.fmt?.outline?.fill) && !run.href;
|
|
2353
2529
|
const alignOf = (a) => a === "center" || a === "right" || a === "justify" ? a : "left";
|
|
2354
2530
|
const verticalLayoutOf = (vert) => {
|
|
2355
2531
|
switch (vert) {
|
|
@@ -2387,6 +2563,13 @@ const smallCapsParts = (text, base) => {
|
|
|
2387
2563
|
flush();
|
|
2388
2564
|
return parts;
|
|
2389
2565
|
};
|
|
2566
|
+
const faceOf = (a, font) => {
|
|
2567
|
+
const family = (a.resolveFamily ?? substituteFamily)(font);
|
|
2568
|
+
return a.familyIsCssList ? { family } : {
|
|
2569
|
+
family,
|
|
2570
|
+
cssFamily: cssFamilyName(family)
|
|
2571
|
+
};
|
|
2572
|
+
};
|
|
2390
2573
|
const buildSvgTextInput = (a) => {
|
|
2391
2574
|
const scale = a.autoFitScale;
|
|
2392
2575
|
const paragraphs = a.paraData.map((para, pi) => {
|
|
@@ -2395,7 +2578,7 @@ const buildSvgTextInput = (a) => {
|
|
|
2395
2578
|
if (run.text === "\n") {
|
|
2396
2579
|
pieces.push({
|
|
2397
2580
|
...breakPiece(),
|
|
2398
|
-
|
|
2581
|
+
...faceOf(a, run.fmt?.font ?? a.themeFace),
|
|
2399
2582
|
sizePx: run.sizePt * scale * PX_PER_PT,
|
|
2400
2583
|
bold: run.fmt?.bold ?? false,
|
|
2401
2584
|
italic: run.fmt?.italic ?? false
|
|
@@ -2411,7 +2594,7 @@ const buildSvgTextInput = (a) => {
|
|
|
2411
2594
|
underline: fmt?.underline ?? true
|
|
2412
2595
|
};
|
|
2413
2596
|
}
|
|
2414
|
-
const
|
|
2597
|
+
const face = faceOf(a, fmt?.font ?? a.themeFace);
|
|
2415
2598
|
const sizePx = run.sizePt * scale * PX_PER_PT;
|
|
2416
2599
|
const fillHex = fmt?.color !== void 0 && fmt.color !== null ? resolveColor(fmt.color, a.theme, "#000000") : a.defaultColor;
|
|
2417
2600
|
const baseline = fmt?.baseline ?? 0;
|
|
@@ -2420,14 +2603,17 @@ const buildSvgTextInput = (a) => {
|
|
|
2420
2603
|
const cap = fmt?.cap;
|
|
2421
2604
|
const caps = cap === "all" || cap === "small";
|
|
2422
2605
|
const base = {
|
|
2423
|
-
|
|
2606
|
+
...face,
|
|
2424
2607
|
sizePx,
|
|
2425
2608
|
bold: fmt?.bold ?? false,
|
|
2426
2609
|
italic: fmt?.italic ?? false,
|
|
2427
2610
|
letterSpacingPx,
|
|
2428
2611
|
kerning,
|
|
2429
2612
|
fillHex,
|
|
2430
|
-
...
|
|
2613
|
+
...hasPaintedTextFill(run) ? { fillPaint: fmt?.textFill ? textFillPaint(fmt.textFill, a.theme, fillHex) : () => ({
|
|
2614
|
+
defs: "",
|
|
2615
|
+
fill: fillHex
|
|
2616
|
+
}) } : {},
|
|
2431
2617
|
...fmt?.underlineColor !== void 0 && fmt.underlineColor !== null ? { underlineHex: resolveColor(fmt.underlineColor, a.theme, fillHex) } : {},
|
|
2432
2618
|
underline: underlineStyleOf(fmt),
|
|
2433
2619
|
strike: strikeStyleOf(fmt),
|
|
@@ -2436,6 +2622,21 @@ const buildSvgTextInput = (a) => {
|
|
|
2436
2622
|
outlineHex: resolveColor(fmt.outline.color, a.theme, "#000000"),
|
|
2437
2623
|
outlineWidthPx: (fmt.outline.widthEmu ?? 9525) / EMU_PER_PX
|
|
2438
2624
|
} : {},
|
|
2625
|
+
...fmt?.outline?.fill && !run.href ? (() => {
|
|
2626
|
+
const fill = fmt.outline.fill;
|
|
2627
|
+
const first = fill.stops[0];
|
|
2628
|
+
return {
|
|
2629
|
+
outlineHex: first ? first.resolvedColor ?? resolveColor(first.color, a.theme, "#000000") : "#000000",
|
|
2630
|
+
outlinePaint: (box) => {
|
|
2631
|
+
const built = gradientDef(fill, a.theme, "", box);
|
|
2632
|
+
return {
|
|
2633
|
+
defs: built.defs,
|
|
2634
|
+
fill: built.fillAttr
|
|
2635
|
+
};
|
|
2636
|
+
},
|
|
2637
|
+
outlineWidthPx: (fmt.outline.widthEmu ?? 9525) / EMU_PER_PX
|
|
2638
|
+
};
|
|
2639
|
+
})() : {},
|
|
2439
2640
|
...fmt?.shadow ? { shadow: {
|
|
2440
2641
|
...fmt.shadow,
|
|
2441
2642
|
...fmt.shadow.color ? { color: resolveColor(fmt.shadow.color, a.theme, "#000000") } : {}
|
|
@@ -2452,7 +2653,8 @@ const buildSvgTextInput = (a) => {
|
|
|
2452
2653
|
} } : {},
|
|
2453
2654
|
...a.bevel ? { bevel: a.bevel } : {},
|
|
2454
2655
|
href: run.href ?? null,
|
|
2455
|
-
...run.hrefTip !== void 0 ? { hrefTip: run.hrefTip } : {}
|
|
2656
|
+
...run.hrefTip !== void 0 ? { hrefTip: run.hrefTip } : {},
|
|
2657
|
+
...run.decimalSeparator !== void 0 ? { decimalSeparator: run.decimalSeparator } : {}
|
|
2456
2658
|
};
|
|
2457
2659
|
const segs = run.text.split("\n");
|
|
2458
2660
|
segs.forEach((seg, i) => {
|
|
@@ -2547,10 +2749,9 @@ const buildBullet = (a, para, pi) => {
|
|
|
2547
2749
|
const baseSizePx = (para.runs.find((r) => r.text !== "\n" && r.text !== "")?.sizePt ?? a.defaultPt) * PX_PER_PT * a.autoFitScale;
|
|
2548
2750
|
const sizePx = para.bulletDetail.sizePct !== null ? baseSizePx * para.bulletDetail.sizePct : para.bulletDetail.sizePts !== null ? para.bulletDetail.sizePts * PX_PER_PT * a.autoFitScale : baseSizePx;
|
|
2549
2751
|
const fillHex = bulletFillOf(para, a.theme, a.defaultColor);
|
|
2550
|
-
const bulletFont = para.bulletDetail.fontFollowText ? para.runs.find((r) => r.text !== "\n" && r.text !== "")?.fmt?.font ?? DEFAULT_BULLET_FONT : para.bulletDetail.font ?? DEFAULT_BULLET_FONT;
|
|
2551
2752
|
return {
|
|
2552
2753
|
text: char,
|
|
2553
|
-
|
|
2754
|
+
...faceOf(a, para.bulletDetail.fontFollowText ? para.runs.find((r) => r.text !== "\n" && r.text !== "")?.fmt?.font ?? DEFAULT_BULLET_FONT : para.bulletDetail.font ?? DEFAULT_BULLET_FONT),
|
|
2554
2755
|
sizePx,
|
|
2555
2756
|
fillHex,
|
|
2556
2757
|
...para.bulletImageHref ? { imageHref: para.bulletImageHref } : {}
|
|
@@ -2561,7 +2762,7 @@ const bulletFillOf = (para, theme, defaultColor) => {
|
|
|
2561
2762
|
const runColor = para.runs.find((r) => r.text !== "\n" && r.text !== "")?.fmt?.color;
|
|
2562
2763
|
return runColor === void 0 || runColor === null ? defaultColor : resolveColor(runColor, theme, defaultColor);
|
|
2563
2764
|
};
|
|
2564
|
-
const resolveTextBodyModel = (pres, shape, bounds, theme, phType, measure, defaultColor, useSvgTextRect = false) => {
|
|
2765
|
+
const resolveTextBodyModel = (pres, shape, bounds, theme, phType, measure, defaultColor, useSvgTextRect = false, resolveFamily = substituteFamily) => {
|
|
2565
2766
|
let paragraphCount;
|
|
2566
2767
|
try {
|
|
2567
2768
|
paragraphCount = getShapeParagraphCount(shape);
|
|
@@ -2684,10 +2885,11 @@ const resolveTextBodyModel = (pres, shape, bounds, theme, phType, measure, defau
|
|
|
2684
2885
|
} catch {
|
|
2685
2886
|
elements = [];
|
|
2686
2887
|
}
|
|
2888
|
+
const languages = elements.length ? getParagraphElementLanguages(shape, p) : [];
|
|
2687
2889
|
let rIdx = 0;
|
|
2688
2890
|
let fieldIndex = 0;
|
|
2689
2891
|
let breakIndex = 0;
|
|
2690
|
-
for (const el of elements) {
|
|
2892
|
+
for (const [elementIndex, el] of elements.entries()) {
|
|
2691
2893
|
if (el.kind === "br") {
|
|
2692
2894
|
const fmt = getShapeRunFormatEffective(pres, shape, p, { breakIndex: breakIndex++ });
|
|
2693
2895
|
runs.push({
|
|
@@ -2722,6 +2924,7 @@ const resolveTextBodyModel = (pres, shape, bounds, theme, phType, measure, defau
|
|
|
2722
2924
|
text: txt,
|
|
2723
2925
|
fmt,
|
|
2724
2926
|
sizePt,
|
|
2927
|
+
...decimalSeparatorField(languages[elementIndex]),
|
|
2725
2928
|
...href !== void 0 ? { href } : {},
|
|
2726
2929
|
...hrefTip !== void 0 ? { hrefTip } : {}
|
|
2727
2930
|
});
|
|
@@ -2778,7 +2981,8 @@ const resolveTextBodyModel = (pres, shape, bounds, theme, phType, measure, defau
|
|
|
2778
2981
|
innerH: fitRect.h,
|
|
2779
2982
|
measure,
|
|
2780
2983
|
vert: fitVert,
|
|
2781
|
-
columns: fitColumns
|
|
2984
|
+
columns: fitColumns,
|
|
2985
|
+
resolveFamily
|
|
2782
2986
|
};
|
|
2783
2987
|
let s = 1;
|
|
2784
2988
|
while (s > AUTOFIT_FLOOR) {
|
|
@@ -2818,6 +3022,7 @@ const resolveTextBodyModel = (pres, shape, bounds, theme, phType, measure, defau
|
|
|
2818
3022
|
measure,
|
|
2819
3023
|
vert,
|
|
2820
3024
|
autoFitScale,
|
|
3025
|
+
resolveFamily,
|
|
2821
3026
|
columns: vert === "none" && cols && cols.count >= 2 ? {
|
|
2822
3027
|
count: cols.count,
|
|
2823
3028
|
gapPx: cols.gapEmu !== void 0 ? cols.gapEmu / EMU_PER_PX : 0
|
|
@@ -2861,9 +3066,15 @@ const renderHtmlParagraphs = (paraData, numberLabels, theme, autoFitScale, defau
|
|
|
2861
3066
|
let runFmt = run.fmt;
|
|
2862
3067
|
if (run.href) {
|
|
2863
3068
|
const hlinkColor = theme ? normalizeHex(theme.hyperlink) : "#0563C1";
|
|
2864
|
-
const { textFill: _linkFill, ...linkFmt } = runFmt ?? {};
|
|
3069
|
+
const { textFill: _linkFill, outline: linkOutline, ...linkFmt } = runFmt ?? {};
|
|
3070
|
+
const { fill: outlineFill, ...solidOutline } = linkOutline ?? {};
|
|
3071
|
+
const outlineColor = outlineFill?.stops[0]?.color ?? solidOutline.color;
|
|
2865
3072
|
runFmt = {
|
|
2866
3073
|
...linkFmt,
|
|
3074
|
+
...linkOutline ? { outline: {
|
|
3075
|
+
...solidOutline,
|
|
3076
|
+
...outlineColor !== void 0 ? { color: outlineColor } : {}
|
|
3077
|
+
} } : {},
|
|
2867
3078
|
color: hlinkColor,
|
|
2868
3079
|
underline: runFmt?.underline ?? true
|
|
2869
3080
|
};
|
|
@@ -2894,8 +3105,8 @@ const renderHtmlParagraphs = (paraData, numberLabels, theme, autoFitScale, defau
|
|
|
2894
3105
|
"padding:0",
|
|
2895
3106
|
`text-align:${ALIGNMENT_TO_CSS[para.align] ?? "left"}`,
|
|
2896
3107
|
lineHeightCss,
|
|
2897
|
-
leftPx > 0 ? `padding-
|
|
2898
|
-
rightPx > 0 ? `padding-
|
|
3108
|
+
leftPx > 0 ? `padding-inline-start:${leftPx.toFixed(2)}px` : "",
|
|
3109
|
+
rightPx > 0 ? `padding-inline-end:${rightPx.toFixed(2)}px` : "",
|
|
2899
3110
|
firstLinePx !== 0 ? `text-indent:${firstLinePx.toFixed(2)}px` : ""
|
|
2900
3111
|
].filter(Boolean);
|
|
2901
3112
|
let prefix = "";
|
|
@@ -2929,7 +3140,7 @@ const renderHtmlParagraphs = (paraData, numberLabels, theme, autoFitScale, defau
|
|
|
2929
3140
|
return paragraphs;
|
|
2930
3141
|
};
|
|
2931
3142
|
const renderTextBody = (pres, shape, bounds, theme, phType, ctx, output = "body") => {
|
|
2932
|
-
const model = resolveTextBodyModel(pres, shape, bounds, theme, phType, ctx.measure, activeDeckTextColor, ctx.mode === "svg" && output === "body");
|
|
3143
|
+
const model = resolveTextBodyModel(pres, shape, bounds, theme, phType, ctx.measure, activeDeckTextColor, ctx.mode === "svg" && output === "body", ctx.resolveFamily);
|
|
2933
3144
|
if (model === null) return "";
|
|
2934
3145
|
const bevel = textBevelOf(getShapeText3D(shape));
|
|
2935
3146
|
const { paraData, numberLabels, authoredAutofit, autoFitScale, defaultPt, themeFace, effectiveDefaultFont, effectiveBody, anchor, innerX, innerY, innerW, innerH, svgTextRect } = model;
|
|
@@ -2965,7 +3176,7 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx, output = "body"
|
|
|
2965
3176
|
innerW: vInnerW,
|
|
2966
3177
|
innerH: vInnerH,
|
|
2967
3178
|
measure: ctx.mode === "svg" ? ctx.measure : browserTextMeasurer() ?? ctx.measure,
|
|
2968
|
-
...ctx.mode === "foreignObject" ? { resolveFamily:
|
|
3179
|
+
...ctx.mode === "foreignObject" ? BROWSER_FAMILIES : { resolveFamily: ctx.resolveFamily },
|
|
2969
3180
|
vert: svgVert,
|
|
2970
3181
|
columns: svgColumns,
|
|
2971
3182
|
bevel
|
|
@@ -2975,7 +3186,7 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx, output = "body"
|
|
|
2975
3186
|
...svgArgsBase,
|
|
2976
3187
|
autoFitScale: authoredAutofit ? autoFitScale : 1,
|
|
2977
3188
|
effectsOnly: true,
|
|
2978
|
-
|
|
3189
|
+
...BROWSER_FAMILIES
|
|
2979
3190
|
};
|
|
2980
3191
|
const effects = [
|
|
2981
3192
|
paraData.some((para) => para.runs.some((run) => run.fmt?.reflection != null)) ? buildAndLayoutSvgText({
|
|
@@ -3056,7 +3267,7 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx, output = "body"
|
|
|
3056
3267
|
vert: svgVert,
|
|
3057
3268
|
columns: null,
|
|
3058
3269
|
effectsOnly: true,
|
|
3059
|
-
|
|
3270
|
+
...BROWSER_FAMILIES,
|
|
3060
3271
|
bevel
|
|
3061
3272
|
};
|
|
3062
3273
|
const hasCharacterReflection = paraData.some((para) => para.runs.some((run) => run.fmt?.reflection !== void 0 && run.fmt.reflection !== null));
|
|
@@ -3113,6 +3324,7 @@ const renderTextEffectsSvg = (pres, slide, shape, bounds, opts = {}) => {
|
|
|
3113
3324
|
groupReflected: false,
|
|
3114
3325
|
mode: "foreignObject",
|
|
3115
3326
|
measure: opts.measureText ?? browserTextMeasurer() ?? defaultMeasurer,
|
|
3327
|
+
resolveFamily: substituteFamily,
|
|
3116
3328
|
ownShapes: true,
|
|
3117
3329
|
background: {
|
|
3118
3330
|
id: "",
|
|
@@ -3149,6 +3361,7 @@ const renderTableCellEffectsSvgInternal = (pres, slide, shape, cellPosition, bou
|
|
|
3149
3361
|
groupReflected: false,
|
|
3150
3362
|
mode: "foreignObject",
|
|
3151
3363
|
measure: opts.measureText ?? browserTextMeasurer() ?? defaultMeasurer,
|
|
3364
|
+
resolveFamily: substituteFamily,
|
|
3152
3365
|
ownShapes: true,
|
|
3153
3366
|
background: {
|
|
3154
3367
|
id: "",
|
|
@@ -4554,10 +4767,11 @@ const cellParaData = (paragraphs, cell, pres) => {
|
|
|
4554
4767
|
const bulletIsPicture = isParagraphBulletPicture(cell, index);
|
|
4555
4768
|
const bulletImageBytes = bulletIsPicture ? getParagraphBulletImageBytes(cell, index) : null;
|
|
4556
4769
|
const runs = [];
|
|
4770
|
+
const languages = getParagraphElementLanguages(cell, index);
|
|
4557
4771
|
let rIdx = 0;
|
|
4558
4772
|
let fieldIndex = 0;
|
|
4559
4773
|
let breakIndex = 0;
|
|
4560
|
-
for (const el of para.elements) {
|
|
4774
|
+
for (const [elementIndex, el] of para.elements.entries()) {
|
|
4561
4775
|
if (el.kind === "br") {
|
|
4562
4776
|
const fmt = getTableCellRunFormatEffective(pres, cell, index, { breakIndex: breakIndex++ });
|
|
4563
4777
|
runs.push({
|
|
@@ -4574,6 +4788,7 @@ const cellParaData = (paragraphs, cell, pres) => {
|
|
|
4574
4788
|
text: el.text,
|
|
4575
4789
|
fmt,
|
|
4576
4790
|
sizePt: fmt?.size ?? DEFAULT_BODY_PT,
|
|
4791
|
+
...decimalSeparatorField(languages[elementIndex]),
|
|
4577
4792
|
...href ? {
|
|
4578
4793
|
href,
|
|
4579
4794
|
...el.tooltip !== void 0 ? { hrefTip: el.tooltip } : {}
|
|
@@ -4642,7 +4857,7 @@ const renderTableCellText = (cell, paragraphs, cx, cy, cw, ch, color, pres, shap
|
|
|
4642
4857
|
innerW: innerW * EMU_PER_PX,
|
|
4643
4858
|
innerH: innerH * EMU_PER_PX,
|
|
4644
4859
|
measure: ctx.mode === "svg" ? ctx.measure : browserTextMeasurer() ?? ctx.measure,
|
|
4645
|
-
...ctx.mode === "foreignObject" ? { resolveFamily:
|
|
4860
|
+
...ctx.mode === "foreignObject" ? BROWSER_FAMILIES : { resolveFamily: ctx.resolveFamily },
|
|
4646
4861
|
vert: verticalLayoutOf(getTableCellTextDirection(cell)),
|
|
4647
4862
|
columns: null,
|
|
4648
4863
|
...effectsOnly ? { effectsOnly: true } : {},
|
|
@@ -4679,6 +4894,33 @@ const renderTableCellText = (cell, paragraphs, cx, cy, cw, ch, color, pres, shap
|
|
|
4679
4894
|
const body = renderHtmlParagraphs(paraData, numberLabels, theme, 1, DEFAULT_BODY_PT, color).join("");
|
|
4680
4895
|
return `<foreignObject x="${px(innerX)}" y="${px(innerY)}" width="${px(innerW)}" height="${px(innerH)}"><div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;flex-direction:column;justify-content:${justify};width:100%;height:100%;box-sizing:border-box;overflow:hidden;line-height:${LINE_HEIGHT};font-family:${familyFont};color:${color};word-break:break-word;${directionStyle}">${body}</div></foreignObject>${fillOverlay()}${bevelOverlay()}`;
|
|
4681
4896
|
};
|
|
4897
|
+
const TWO_STOP_TABLE_GRADIENT_EXPONENT = 1.85;
|
|
4898
|
+
const TWO_STOP_TABLE_GRADIENT_SAMPLES = 16;
|
|
4899
|
+
const twoStopTableGradient = (grad, theme) => {
|
|
4900
|
+
if (grad.path !== void 0 || grad.stops.length !== 2) return grad;
|
|
4901
|
+
const [start, end] = [...grad.stops].sort((a, b) => a.offset - b.offset);
|
|
4902
|
+
if (!start || !end || end.offset <= start.offset) return grad;
|
|
4903
|
+
const span = end.offset - start.offset;
|
|
4904
|
+
const colorOf = (stop) => stop.resolvedColor ?? resolveColor(stop.color, theme, "#E5E7EB");
|
|
4905
|
+
const startColor = colorOf(start);
|
|
4906
|
+
const endColor = colorOf(end);
|
|
4907
|
+
const stops = Array.from({ length: 17 }, (_, i) => {
|
|
4908
|
+
const along = i / TWO_STOP_TABLE_GRADIENT_SAMPLES;
|
|
4909
|
+
const startWeight = (1 - along) ** TWO_STOP_TABLE_GRADIENT_EXPONENT;
|
|
4910
|
+
const color = mixHex(startColor, endColor, startWeight);
|
|
4911
|
+
const opacity = start.opacity === void 0 && end.opacity === void 0 ? void 0 : (start.opacity ?? 1) * startWeight + (end.opacity ?? 1) * (1 - startWeight);
|
|
4912
|
+
return {
|
|
4913
|
+
offset: start.offset + along * span,
|
|
4914
|
+
color,
|
|
4915
|
+
resolvedColor: color,
|
|
4916
|
+
...opacity === void 0 ? {} : { opacity }
|
|
4917
|
+
};
|
|
4918
|
+
});
|
|
4919
|
+
return {
|
|
4920
|
+
...grad,
|
|
4921
|
+
stops
|
|
4922
|
+
};
|
|
4923
|
+
};
|
|
4682
4924
|
const renderTable = (shape, pres, x, y, w, h, transform, theme, ctx) => {
|
|
4683
4925
|
let dims;
|
|
4684
4926
|
let widths;
|
|
@@ -4711,6 +4953,16 @@ const renderTable = (shape, pres, x, y, w, h, transform, theme, ctx) => {
|
|
|
4711
4953
|
const tableThemeFace = getPresentationFonts(pres)?.minorLatin ?? null;
|
|
4712
4954
|
const out = [];
|
|
4713
4955
|
out.push(`<g${transform}>`);
|
|
4956
|
+
const background = getTableBackgroundEffective(pres, shape);
|
|
4957
|
+
const tableW = (colXs[dims.cols] ?? xPx) - xPx;
|
|
4958
|
+
const tableH = (rowYs[dims.rows] ?? yPx) - yPx;
|
|
4959
|
+
if (background?.fill.kind === "solid") {
|
|
4960
|
+
const opacity = background.fillOpacity !== void 0 && background.fillOpacity < 1 ? ` fill-opacity="${background.fillOpacity.toFixed(3)}"` : "";
|
|
4961
|
+
out.push(`<rect x="${px(xPx)}" y="${px(yPx)}" width="${px(tableW)}" height="${px(tableH)}" fill="${resolveColor(background.fill.color, theme, "#FFFFFF")}"${opacity}/>`);
|
|
4962
|
+
} else if (background?.gradient) {
|
|
4963
|
+
const built = gradientDef(twoStopTableGradient(background.gradient, theme), theme);
|
|
4964
|
+
out.push(`${built.defs}<rect x="${px(xPx)}" y="${px(yPx)}" width="${px(tableW)}" height="${px(tableH)}" fill="${built.fillAttr}"/>`);
|
|
4965
|
+
}
|
|
4714
4966
|
const borderEdges = [];
|
|
4715
4967
|
const borderEdgeCandidates = /* @__PURE__ */ new Map();
|
|
4716
4968
|
for (let r = 0; r < dims.rows; r++) for (let c = 0; c < dims.cols; c++) {
|
|
@@ -4733,7 +4985,8 @@ const renderTable = (shape, pres, x, y, w, h, transform, theme, ctx) => {
|
|
|
4733
4985
|
else if (fill.kind !== "inherit") resolvedFill = "none";
|
|
4734
4986
|
else resolvedFill = "none";
|
|
4735
4987
|
const cellTextColor = textColor;
|
|
4736
|
-
|
|
4988
|
+
const fillOpacity = fill.kind === "solid" && appearance.fillOpacity !== void 0 && appearance.fillOpacity < 1 ? ` fill-opacity="${appearance.fillOpacity.toFixed(3)}"` : "";
|
|
4989
|
+
out.push(`<g data-pptx-cell="${r},${c}"><rect x="${px(cx)}" y="${px(cy)}" width="${px(cw)}" height="${px(ch)}" fill="${resolvedFill}"${fillOpacity}/>`);
|
|
4737
4990
|
const borders = appearance.borders;
|
|
4738
4991
|
const dashAttr = (dash, widthPx) => {
|
|
4739
4992
|
if (!dash || dash === "solid") return "";
|
|
@@ -4893,6 +5146,148 @@ const PATH_SHADE = {
|
|
|
4893
5146
|
lighten: "fill=\"#fff\" fill-opacity=\"0.4\"",
|
|
4894
5147
|
lightenLess: "fill=\"#fff\" fill-opacity=\"0.2\""
|
|
4895
5148
|
};
|
|
5149
|
+
const SKETCH_AMPLITUDE = {
|
|
5150
|
+
curved: .008,
|
|
5151
|
+
freehand: .014,
|
|
5152
|
+
scribble: .022
|
|
5153
|
+
};
|
|
5154
|
+
const SKETCH_STEP = .12;
|
|
5155
|
+
const seededRandom = (seed) => {
|
|
5156
|
+
let state = seed >>> 0;
|
|
5157
|
+
return () => {
|
|
5158
|
+
state = state + 1831565813 >>> 0;
|
|
5159
|
+
let t = state;
|
|
5160
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
5161
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
5162
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
5163
|
+
};
|
|
5164
|
+
};
|
|
5165
|
+
/**
|
|
5166
|
+
* An approximation of a sketched outline (`ask:lineSketchStyleProps`): every
|
|
5167
|
+
* edge of `geom` is redrawn as a seeded hand-drawn wobble. Only used for a
|
|
5168
|
+
* preset geometry the writer left crisp; sketches the reference desktop app wrote already
|
|
5169
|
+
* carry their hand-drawn path as custom geometry.
|
|
5170
|
+
*/
|
|
5171
|
+
const sketchGeometry = (geom, sketch, w, h, seed) => {
|
|
5172
|
+
const random = seededRandom(seed);
|
|
5173
|
+
const jitter = (amp) => (random() * 2 - 1) * amp;
|
|
5174
|
+
const paths = geom.paths.map((path) => {
|
|
5175
|
+
const cw = path.w ?? w;
|
|
5176
|
+
const ch = path.h ?? h;
|
|
5177
|
+
const size = Math.max(Math.min(cw, ch), Math.max(cw, ch) / 4);
|
|
5178
|
+
const amp = SKETCH_AMPLITUDE[sketch] * size;
|
|
5179
|
+
const step = SKETCH_STEP * Math.max(cw, ch);
|
|
5180
|
+
const nudge = (pt) => ({
|
|
5181
|
+
x: pt.x + jitter(amp),
|
|
5182
|
+
y: pt.y + jitter(amp)
|
|
5183
|
+
});
|
|
5184
|
+
const commands = [];
|
|
5185
|
+
let curX = 0;
|
|
5186
|
+
let curY = 0;
|
|
5187
|
+
let startX = 0;
|
|
5188
|
+
let startY = 0;
|
|
5189
|
+
const wobbleTo = (x, y) => {
|
|
5190
|
+
const dx = x - curX;
|
|
5191
|
+
const dy = y - curY;
|
|
5192
|
+
const len = Math.hypot(dx, dy);
|
|
5193
|
+
const count = step > 0 ? Math.max(1, Math.round(len / step)) : 1;
|
|
5194
|
+
const nx = len > 0 ? -dy / len : 0;
|
|
5195
|
+
const ny = len > 0 ? dx / len : 0;
|
|
5196
|
+
let px = curX;
|
|
5197
|
+
let py = curY;
|
|
5198
|
+
for (let i = 1; i <= count; i++) {
|
|
5199
|
+
const last = i === count;
|
|
5200
|
+
const ex = curX + dx * i / count + (last ? 0 : jitter(amp / 2));
|
|
5201
|
+
const ey = curY + dy * i / count + (last ? 0 : jitter(amp / 2));
|
|
5202
|
+
const bend = jitter(amp);
|
|
5203
|
+
const control = {
|
|
5204
|
+
x: (px + ex) / 2 + nx * bend,
|
|
5205
|
+
y: (py + ey) / 2 + ny * bend
|
|
5206
|
+
};
|
|
5207
|
+
commands.push({
|
|
5208
|
+
kind: "quadBezTo",
|
|
5209
|
+
pts: [control, {
|
|
5210
|
+
x: ex,
|
|
5211
|
+
y: ey
|
|
5212
|
+
}]
|
|
5213
|
+
});
|
|
5214
|
+
px = ex;
|
|
5215
|
+
py = ey;
|
|
5216
|
+
}
|
|
5217
|
+
curX = x;
|
|
5218
|
+
curY = y;
|
|
5219
|
+
};
|
|
5220
|
+
for (const cmd of path.commands) switch (cmd.kind) {
|
|
5221
|
+
case "moveTo":
|
|
5222
|
+
curX = cmd.pt.x;
|
|
5223
|
+
curY = cmd.pt.y;
|
|
5224
|
+
startX = curX;
|
|
5225
|
+
startY = curY;
|
|
5226
|
+
commands.push(cmd);
|
|
5227
|
+
break;
|
|
5228
|
+
case "lnTo":
|
|
5229
|
+
wobbleTo(cmd.pt.x, cmd.pt.y);
|
|
5230
|
+
break;
|
|
5231
|
+
case "quadBezTo":
|
|
5232
|
+
commands.push({
|
|
5233
|
+
kind: "quadBezTo",
|
|
5234
|
+
pts: [nudge(cmd.pts[0]), cmd.pts[1]]
|
|
5235
|
+
});
|
|
5236
|
+
curX = cmd.pts[1].x;
|
|
5237
|
+
curY = cmd.pts[1].y;
|
|
5238
|
+
break;
|
|
5239
|
+
case "cubicBezTo":
|
|
5240
|
+
commands.push({
|
|
5241
|
+
kind: "cubicBezTo",
|
|
5242
|
+
pts: [
|
|
5243
|
+
nudge(cmd.pts[0]),
|
|
5244
|
+
nudge(cmd.pts[1]),
|
|
5245
|
+
cmd.pts[2]
|
|
5246
|
+
]
|
|
5247
|
+
});
|
|
5248
|
+
curX = cmd.pts[2].x;
|
|
5249
|
+
curY = cmd.pts[2].y;
|
|
5250
|
+
break;
|
|
5251
|
+
case "arcTo":
|
|
5252
|
+
for (const s of arcToCubicSegments(curX, curY, cmd.wR, cmd.hR, cmd.stAng, cmd.swAng)) {
|
|
5253
|
+
commands.push({
|
|
5254
|
+
kind: "cubicBezTo",
|
|
5255
|
+
pts: [
|
|
5256
|
+
nudge({
|
|
5257
|
+
x: s.c1x,
|
|
5258
|
+
y: s.c1y
|
|
5259
|
+
}),
|
|
5260
|
+
nudge({
|
|
5261
|
+
x: s.c2x,
|
|
5262
|
+
y: s.c2y
|
|
5263
|
+
}),
|
|
5264
|
+
{
|
|
5265
|
+
x: s.ex,
|
|
5266
|
+
y: s.ey
|
|
5267
|
+
}
|
|
5268
|
+
]
|
|
5269
|
+
});
|
|
5270
|
+
curX = s.ex;
|
|
5271
|
+
curY = s.ey;
|
|
5272
|
+
}
|
|
5273
|
+
break;
|
|
5274
|
+
case "close":
|
|
5275
|
+
if (curX !== startX || curY !== startY) wobbleTo(startX, startY);
|
|
5276
|
+
commands.push(cmd);
|
|
5277
|
+
curX = startX;
|
|
5278
|
+
curY = startY;
|
|
5279
|
+
break;
|
|
5280
|
+
}
|
|
5281
|
+
return {
|
|
5282
|
+
...path,
|
|
5283
|
+
commands
|
|
5284
|
+
};
|
|
5285
|
+
});
|
|
5286
|
+
return {
|
|
5287
|
+
...geom,
|
|
5288
|
+
paths
|
|
5289
|
+
};
|
|
5290
|
+
};
|
|
4896
5291
|
/**
|
|
4897
5292
|
* Renders an evaluated {@link CustomGeometry} (a `<a:custGeom>` or a preset
|
|
4898
5293
|
* from `getPresetGeometry`) as `<path>`s in the shape's slide box. Returns
|
|
@@ -5109,6 +5504,11 @@ const renderShapeContent = (shape, pres, theme, ctx) => {
|
|
|
5109
5504
|
const customGeom = rawPreset === null ? getShapeCustomGeometry(shape) : null;
|
|
5110
5505
|
let geomSvg = customGeom !== null ? customGeometryToSvg(customGeom, x, y, w, h, p.fill, fa, p.stroke, p.strokeWidth, sa, ma) : "";
|
|
5111
5506
|
const isCustGeom = geomSvg === "" && rawPreset === null && getShapeXmlString(shape).includes("custGeom");
|
|
5507
|
+
const sketch = rawPreset !== null ? getShapeStrokeSketch(shape) : null;
|
|
5508
|
+
if (geomSvg !== "") {} else if (sketch !== null && p.stroke !== "none") {
|
|
5509
|
+
const geometry = getPresetGeometry(preset, bounds, getShapeAdjustValues(shape));
|
|
5510
|
+
if (geometry !== null) geomSvg = customGeometryToSvg(sketchGeometry(geometry, sketch, w, h, getShapeId(shape)), x, y, w, h, p.fill, fa, p.stroke, p.strokeWidth, sa, ma);
|
|
5511
|
+
}
|
|
5112
5512
|
if (geomSvg !== "") {} else if (preset === "rect") geomSvg = `<rect x="${E(x)}" y="${E(y)}" width="${E(w)}" height="${E(h)}" fill="${p.fill}"${fa} stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}"${sa}${ma}/>`;
|
|
5113
5513
|
else {
|
|
5114
5514
|
const geometry = getPresetGeometry(preset, bounds, getShapeAdjustValues(shape));
|
|
@@ -5201,11 +5601,14 @@ const buildReflection = (pres, shape, geomRaw, box) => {
|
|
|
5201
5601
|
const f = refl.scaleY ?? -1;
|
|
5202
5602
|
const startA = refl.startOpacity ?? 1;
|
|
5203
5603
|
const endA = refl.opacity ?? 0;
|
|
5604
|
+
const startPos = refl.startPosition ?? 0;
|
|
5605
|
+
const endPos = Math.max(startPos, refl.endPosition ?? 1);
|
|
5204
5606
|
const contactPx = (box.y + box.h) / EMU_PER_PX;
|
|
5205
5607
|
const transform = `translate(0 ${(refl.distEmu / EMU_PER_PX + contactPx * (1 - f)).toFixed(2)}) scale(1 ${f})`;
|
|
5206
5608
|
const maskId = mintId();
|
|
5207
5609
|
const gradId = mintId();
|
|
5208
|
-
const
|
|
5610
|
+
const [y1, y2] = f < 0 ? [1, 0] : [0, 1];
|
|
5611
|
+
const defs = `<defs><linearGradient id="${gradId}" x1="0" y1="${y1}" x2="0" y2="${y2}"><stop offset="${startPos.toFixed(3)}" stop-color="#fff" stop-opacity="${startA.toFixed(3)}"/><stop offset="${endPos.toFixed(3)}" stop-color="#fff" stop-opacity="${endA.toFixed(3)}"/></linearGradient><mask id="${maskId}" maskContentUnits="objectBoundingBox"><rect width="1" height="1" fill="url(#${gradId})"/></mask></defs>`;
|
|
5209
5612
|
return {
|
|
5210
5613
|
svg: `<g transform="${transform}" mask="url(#${maskId})" data-pptx-reflection="1">${geomRaw}</g>`,
|
|
5211
5614
|
defs
|
|
@@ -5253,9 +5656,9 @@ const buildEffectsFilter = (pres, shape) => {
|
|
|
5253
5656
|
primitives.push(`<feMorphology in="SourceAlpha" operator="dilate" radius="${dilatePx.toFixed(2)}" result="glowExp${i}"/>`, `<feGaussianBlur in="glowExp${i}" stdDeviation="${featherPx.toFixed(2)}" result="glowBlur${i}"/>`, `<feFlood flood-color="${color}" flood-opacity="${opacity.toFixed(3)}" result="glowCol${i}"/>`, `<feComposite in="glowCol${i}" in2="glowBlur${i}" operator="in" result="glowOut${i}"/>`);
|
|
5254
5657
|
layers.push(`glowOut${i}`);
|
|
5255
5658
|
} else if (e.kind === "softEdge") {
|
|
5256
|
-
const
|
|
5659
|
+
const radiusPx = e.radiusEmu / EMU_PER_PX;
|
|
5257
5660
|
const i = primitives.length;
|
|
5258
|
-
primitives.push(`<feGaussianBlur in="
|
|
5661
|
+
primitives.push(`<feMorphology in="SourceAlpha" operator="erode" radius="${(radiusPx / 2).toFixed(2)}" result="softCore${i}"/>`, `<feGaussianBlur in="softCore${i}" stdDeviation="${(radiusPx / 4).toFixed(2)}" result="softMask${i}"/>`, `<feComposite in="SourceGraphic" in2="softMask${i}" operator="in" result="softOut${i}"/>`);
|
|
5259
5662
|
layers.length = 0;
|
|
5260
5663
|
layers.push(`softOut${i}`);
|
|
5261
5664
|
} else if (e.kind === "blur") {
|
|
@@ -5297,14 +5700,33 @@ const topLevelShapes = (shapes, opts) => {
|
|
|
5297
5700
|
}
|
|
5298
5701
|
return out;
|
|
5299
5702
|
};
|
|
5300
|
-
const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
5703
|
+
const renderSlideSvg = (pres, slide, opts = {}) => renderSurface(pres, {
|
|
5704
|
+
slide,
|
|
5705
|
+
layout: getSlideLayout(slide),
|
|
5706
|
+
master: false
|
|
5707
|
+
}, opts);
|
|
5708
|
+
/**
|
|
5709
|
+
* Draws a slide layout — or, with `master`, its slide master — the way
|
|
5710
|
+
* the reference desktop app's Slide Master view shows it behind the placeholders: the
|
|
5711
|
+
* background and the decorative (non-placeholder) shapes of the master and,
|
|
5712
|
+
* for a layout, of the layout itself. A layout that hides background graphics
|
|
5713
|
+
* leaves the master's shapes out. Placeholders are not drawn; an editor draws
|
|
5714
|
+
* them with their prompt text over this.
|
|
5715
|
+
*/
|
|
5716
|
+
const renderSlideLayoutSvg = (pres, layout, opts = {}) => renderSurface(pres, {
|
|
5717
|
+
slide: null,
|
|
5718
|
+
layout,
|
|
5719
|
+
master: opts.master ?? false
|
|
5720
|
+
}, opts);
|
|
5721
|
+
const renderSurface = (pres, surface, opts) => {
|
|
5722
|
+
const { slide, master } = surface;
|
|
5301
5723
|
const size = getSlideSize(pres) ?? DEFAULT_SIZE;
|
|
5302
5724
|
const W = size.width;
|
|
5303
5725
|
const H = size.height;
|
|
5304
5726
|
const theme = getPresentationTheme(pres);
|
|
5305
|
-
activeColorMap = getEffectiveColorMap(slide);
|
|
5306
|
-
activeDeckTextColor = resolveDeckBodyTextColor(slide) ?? "#000000";
|
|
5307
|
-
const position = getSlides(pres).indexOf(slide);
|
|
5727
|
+
activeColorMap = getEffectiveColorMap(slide ?? surface.layout);
|
|
5728
|
+
activeDeckTextColor = resolveDeckBodyTextColor(slide ?? surface.layout) ?? "#000000";
|
|
5729
|
+
const position = slide ? getSlides(pres).indexOf(slide) : 0;
|
|
5308
5730
|
activeSlideNumber = String(getPresentationFirstSlideNumber(pres) + (position < 0 ? 0 : position));
|
|
5309
5731
|
const ctx = {
|
|
5310
5732
|
groupScale: {
|
|
@@ -5314,6 +5736,7 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
|
5314
5736
|
groupReflected: false,
|
|
5315
5737
|
mode: opts.textLayout ?? "foreignObject",
|
|
5316
5738
|
measure: opts.measureText ?? defaultMeasurer,
|
|
5739
|
+
resolveFamily: opts.textLayout === "svg" ? opts.resolveFamily ?? substituteFamily : substituteFamily,
|
|
5317
5740
|
ownShapes: true,
|
|
5318
5741
|
background: {
|
|
5319
5742
|
id: "",
|
|
@@ -5322,11 +5745,11 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
|
5322
5745
|
},
|
|
5323
5746
|
inverseGroupTransform: ""
|
|
5324
5747
|
};
|
|
5325
|
-
let bg = getSlideBackground(slide);
|
|
5748
|
+
let bg = slide ? getSlideBackground(slide) : { kind: "inherit" };
|
|
5326
5749
|
if (bg.kind === "inherit") {
|
|
5327
|
-
const layout =
|
|
5750
|
+
const layout = surface.layout;
|
|
5328
5751
|
if (layout) {
|
|
5329
|
-
const layoutBg = getSlideLayoutBackground(layout);
|
|
5752
|
+
const layoutBg = master ? { kind: "inherit" } : getSlideLayoutBackground(layout);
|
|
5330
5753
|
if (layoutBg.kind !== "inherit") bg = layoutBg;
|
|
5331
5754
|
else {
|
|
5332
5755
|
const masterBg = getSlideMasterBackground(pres, layout);
|
|
@@ -5341,11 +5764,11 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
|
5341
5764
|
let bgGradientDefs = "";
|
|
5342
5765
|
if (bg.kind === "solid") bgColor = resolveColor(bg.color, theme, "#FFFFFF");
|
|
5343
5766
|
else if (bg.kind === "gradient") {
|
|
5344
|
-
let grad = getSlideBackgroundGradientFill(slide);
|
|
5767
|
+
let grad = slide ? getSlideBackgroundGradientFill(slide) : null;
|
|
5345
5768
|
if (!grad) {
|
|
5346
|
-
const layout =
|
|
5769
|
+
const layout = surface.layout;
|
|
5347
5770
|
if (layout) {
|
|
5348
|
-
grad = getSlideLayoutBackgroundGradientFill(layout);
|
|
5771
|
+
grad = master ? null : getSlideLayoutBackgroundGradientFill(layout);
|
|
5349
5772
|
if (!grad) grad = getSlideMasterBackgroundGradientFill(pres, layout);
|
|
5350
5773
|
}
|
|
5351
5774
|
}
|
|
@@ -5355,11 +5778,11 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
|
5355
5778
|
bgGradient = `<rect width="${E(W)}" height="${E(H)}" fill="${built.fillAttr}"/>`;
|
|
5356
5779
|
}
|
|
5357
5780
|
} else if (bg.kind === "pattern") {
|
|
5358
|
-
let pat = getSlideBackgroundPatternFill(pres, slide);
|
|
5781
|
+
let pat = slide ? getSlideBackgroundPatternFill(pres, slide) : null;
|
|
5359
5782
|
if (!pat) {
|
|
5360
|
-
const layout =
|
|
5783
|
+
const layout = surface.layout;
|
|
5361
5784
|
if (layout) {
|
|
5362
|
-
pat = getSlideLayoutBackgroundPatternFill(pres, layout);
|
|
5785
|
+
pat = master ? null : getSlideLayoutBackgroundPatternFill(pres, layout);
|
|
5363
5786
|
if (!pat) pat = getSlideMasterBackgroundPatternFill(pres, layout);
|
|
5364
5787
|
}
|
|
5365
5788
|
}
|
|
@@ -5371,23 +5794,23 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
|
5371
5794
|
} else if (theme && bg.kind === "inherit") bgColor = normalizeHex(theme.light1);
|
|
5372
5795
|
let bgImage = "";
|
|
5373
5796
|
if (bg.kind === "image") {
|
|
5374
|
-
let bytes = getSlideBackgroundImageBytes(slide);
|
|
5797
|
+
let bytes = slide ? getSlideBackgroundImageBytes(slide) : null;
|
|
5375
5798
|
if (!bytes && pres) {
|
|
5376
|
-
const layout =
|
|
5799
|
+
const layout = surface.layout;
|
|
5377
5800
|
if (layout) {
|
|
5378
|
-
bytes = getSlideLayoutBackgroundImageBytes(pres, layout);
|
|
5801
|
+
bytes = master ? null : getSlideLayoutBackgroundImageBytes(pres, layout);
|
|
5379
5802
|
if (!bytes) bytes = getSlideMasterBackgroundImageBytes(pres, layout);
|
|
5380
5803
|
}
|
|
5381
5804
|
}
|
|
5382
5805
|
if (bytes) {
|
|
5383
5806
|
const fmt = detectImageFormatLocal(bytes);
|
|
5384
5807
|
const dataUrl = `data:${fmt ? imageMime[fmt] ?? "image/png" : "image/png"};base64,${u8ToBase64(bytes)}`;
|
|
5385
|
-
const layout = getSlideBackgroundImageFillLayout(slide);
|
|
5386
|
-
const crop = getSlideBackgroundImageCrop(slide);
|
|
5808
|
+
const layout = slide ? getSlideBackgroundImageFillLayout(slide) : null;
|
|
5809
|
+
const crop = slide ? getSlideBackgroundImageCrop(slide) : null;
|
|
5387
5810
|
const cropLeft = crop?.left ?? 0, cropTop = crop?.top ?? 0;
|
|
5388
5811
|
const cropRight = crop?.right ?? 0, cropBottom = crop?.bottom ?? 0;
|
|
5389
|
-
const opacity = getSlideBackgroundImageOpacity(slide) ?? 1;
|
|
5390
|
-
const intrinsic = layout?.mode === "tile" ? getSlideBackgroundImageIntrinsicSize(slide) : null;
|
|
5812
|
+
const opacity = (slide && getSlideBackgroundImageOpacity(slide)) ?? 1;
|
|
5813
|
+
const intrinsic = layout?.mode === "tile" && slide ? getSlideBackgroundImageIntrinsicSize(slide) : null;
|
|
5391
5814
|
if (layout?.mode === "tile" && intrinsic) {
|
|
5392
5815
|
const pattern = imageTilePattern(dataUrl, layout, intrinsic, 0, 0, W, H, cropLeft, cropTop, cropRight, cropBottom);
|
|
5393
5816
|
bgImage = `${pattern.defs}<rect width="${E(W)}" height="${E(H)}" fill="${pattern.fill}" opacity="${opacity}"/>`;
|
|
@@ -5408,10 +5831,11 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
|
5408
5831
|
}
|
|
5409
5832
|
}
|
|
5410
5833
|
let layoutBgShapes = "";
|
|
5411
|
-
const layoutForBg =
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
const
|
|
5834
|
+
const layoutForBg = surface.layout;
|
|
5835
|
+
const paintBackground = opts.background ?? true;
|
|
5836
|
+
if (paintBackground && layoutForBg && !(slide && isSlideBackgroundGraphicsHidden(slide))) try {
|
|
5837
|
+
const masterShapes = topLevelShapes(!master && isSlideLayoutBackgroundGraphicsHidden(layoutForBg) ? [] : getSlideMasterShapes(pres, layoutForBg), { dropPlaceholders: true });
|
|
5838
|
+
const layoutShapes = topLevelShapes(master ? [] : getSlideLayoutShapes(pres, layoutForBg), { dropPlaceholders: true });
|
|
5415
5839
|
const bgCtx = {
|
|
5416
5840
|
...ctx,
|
|
5417
5841
|
ownShapes: false
|
|
@@ -5420,12 +5844,12 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
|
5420
5844
|
} catch {
|
|
5421
5845
|
layoutBgShapes = "";
|
|
5422
5846
|
}
|
|
5423
|
-
const shapesSvg = topLevelShapes(getSlideShapes(slide), { dropPlaceholders: false }).map((s) => renderShape(s, pres, theme, ctx)).join("");
|
|
5847
|
+
const shapesSvg = topLevelShapes(slide ? getSlideShapes(slide) : [], { dropPlaceholders: false }).map((s) => renderShape(s, pres, theme, ctx)).join("");
|
|
5424
5848
|
const backgroundSvg = `${bgOpacity === void 0 ? "" : `<rect width="${E(W)}" height="${E(H)}" fill="#FFFFFF"/>`}<rect width="${E(W)}" height="${E(H)}" fill="${bgColor}"${bgOpacityAttr}/>${bgGradient}${bgImage}`;
|
|
5425
5849
|
return [
|
|
5426
5850
|
`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 ${E(W)} ${E(H)}" preserveAspectRatio="xMidYMid meet">`,
|
|
5427
5851
|
bgGradientDefs,
|
|
5428
|
-
ctx.background.id ? `<defs><g id="${ctx.background.id}">${backgroundSvg}</g></defs
|
|
5852
|
+
ctx.background.id ? `<defs><g id="${ctx.background.id}">${backgroundSvg}</g></defs>${paintBackground ? `<use href="#${ctx.background.id}" xlink:href="#${ctx.background.id}"/>` : ""}` : paintBackground ? backgroundSvg : "",
|
|
5429
5853
|
layoutBgShapes,
|
|
5430
5854
|
shapesSvg,
|
|
5431
5855
|
"</svg>"
|
|
@@ -5458,7 +5882,7 @@ const auditShape = (pres, theme, shape, slideIndex, measure, tolerancePx, report
|
|
|
5458
5882
|
y: bounds.y,
|
|
5459
5883
|
w: bounds.w,
|
|
5460
5884
|
h: bounds.h
|
|
5461
|
-
}, theme, getShapePlaceholderType(shape), measure, "#000000");
|
|
5885
|
+
}, theme, getShapePlaceholderType(shape), measure, "#000000", false, passthroughFamily);
|
|
5462
5886
|
if (model === null) return;
|
|
5463
5887
|
const vert = verticalLayoutOf(model.effectiveBody.vert ?? getShapeTextDirection(shape));
|
|
5464
5888
|
const cols = model.effectiveBody.columns;
|
|
@@ -5601,7 +6025,7 @@ const auditTextLayout = (pres, options = {}) => {
|
|
|
5601
6025
|
*
|
|
5602
6026
|
* `1` for a shape with no `<a:normAutofit>`: `<a:noAutofit>` overflows and
|
|
5603
6027
|
* `<a:spAutoFit>` grows the box instead, so neither shrinks the text. A shape
|
|
5604
|
-
* with a baked `fontScale` reports that, unchanged — it is what
|
|
6028
|
+
* with a baked `fontScale` reports that, unchanged — it is what the reference desktop app
|
|
5605
6029
|
* itself applied.
|
|
5606
6030
|
*/
|
|
5607
6031
|
function shapeAutoFitScale(pres, shape, options = {}) {
|
|
@@ -5634,6 +6058,6 @@ function shapeTextAnchorOffset(pres, shape, options = {}) {
|
|
|
5634
6058
|
};
|
|
5635
6059
|
}
|
|
5636
6060
|
//#endregion
|
|
5637
|
-
export {
|
|
6061
|
+
export { shapeTextRect as S, paragraphNumberLabels as _, renderSlideSvg as a, verticalTextStyle as b, AVG_GLYPH_W_RATIO as c, SERIF as d, TIMES as f, decimalSeparatorOf as g, substituteFamily as h, renderSlideLayoutSvg as i, MONO as l, isCjk as m, shapeTextAnchorOffset as n, renderTextEffectsSvg as o, defaultMeasurer as p, auditTextLayout as r, ARIAL as s, shapeAutoFitScale as t, SANS as u, textUnderlineStyle as v, resolveTextBodyRect as x, textColumnsStyle as y };
|
|
5638
6062
|
|
|
5639
|
-
//# sourceMappingURL=src-
|
|
6063
|
+
//# sourceMappingURL=src-u1-ERSHC.js.map
|