@office-kit/pptx-preview 0.14.0 → 0.15.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 +14 -5
- package/dist/{index-DUFPGYOf.d.ts → index-74n14k3Y.d.ts} +32 -8
- 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-pb3C61GD.js} +512 -105
- package/dist/src-pb3C61GD.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 = /^[、。,.・)」』】〉》”’!?:;ー〜…,.!?:;)\]}]+$/;
|
|
@@ -678,7 +702,7 @@ const layoutCore = (input, measure) => {
|
|
|
678
702
|
fieldWidth = decimalWidth = 0;
|
|
679
703
|
} else {
|
|
680
704
|
fieldWidth += token.width;
|
|
681
|
-
const decimal = token.text.indexOf(".");
|
|
705
|
+
const decimal = token.text.indexOf(token.piece.decimalSeparator ?? ".");
|
|
682
706
|
decimalWidth = decimal < 0 ? decimalWidth + token.width : mWidth(token.text.slice(0, decimal), {
|
|
683
707
|
...specOf(token.piece),
|
|
684
708
|
sizePx: renderedSizePxOf(token.piece)
|
|
@@ -836,7 +860,7 @@ const lineContentExtent = (line, dx) => {
|
|
|
836
860
|
};
|
|
837
861
|
};
|
|
838
862
|
const resolveFillPaints = (placements) => {
|
|
839
|
-
if (!placements.some(({ line }) => line.tokens.some((t) => t.piece.fillPaint))) return {
|
|
863
|
+
if (!placements.some(({ line }) => line.tokens.some((t) => t.piece.fillPaint || t.piece.outlinePaint))) return {
|
|
840
864
|
defs: "",
|
|
841
865
|
placements
|
|
842
866
|
};
|
|
@@ -864,12 +888,7 @@ const resolveFillPaints = (placements) => {
|
|
|
864
888
|
};
|
|
865
889
|
const painted = /* @__PURE__ */ new Map();
|
|
866
890
|
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;
|
|
891
|
+
const resolve = (paint) => {
|
|
873
892
|
let fill = painted.get(paint);
|
|
874
893
|
if (fill === void 0) {
|
|
875
894
|
const built = paint(bounds);
|
|
@@ -877,9 +896,16 @@ const resolveFillPaints = (placements) => {
|
|
|
877
896
|
fill = built.fill;
|
|
878
897
|
painted.set(paint, fill);
|
|
879
898
|
}
|
|
899
|
+
return fill;
|
|
900
|
+
};
|
|
901
|
+
const pieces = /* @__PURE__ */ new Map();
|
|
902
|
+
const repaint = (piece) => {
|
|
903
|
+
let next = pieces.get(piece);
|
|
904
|
+
if (next) return next;
|
|
880
905
|
next = {
|
|
881
906
|
...piece,
|
|
882
|
-
fillHex:
|
|
907
|
+
...piece.fillPaint ? { fillHex: resolve(piece.fillPaint) } : {},
|
|
908
|
+
...piece.outlinePaint ? { outlineHex: resolve(piece.outlinePaint) } : {}
|
|
883
909
|
};
|
|
884
910
|
pieces.set(piece, next);
|
|
885
911
|
return next;
|
|
@@ -888,7 +914,7 @@ const resolveFillPaints = (placements) => {
|
|
|
888
914
|
...placement,
|
|
889
915
|
line: {
|
|
890
916
|
...placement.line,
|
|
891
|
-
tokens: placement.line.tokens.map((token) => token.piece.fillPaint ? {
|
|
917
|
+
tokens: placement.line.tokens.map((token) => token.piece.fillPaint || token.piece.outlinePaint ? {
|
|
892
918
|
...token,
|
|
893
919
|
piece: repaint(token.piece)
|
|
894
920
|
} : token)
|
|
@@ -1086,7 +1112,7 @@ const emitTextReflections = (groups, textAnchor, x0, baselineY, descent, nextEff
|
|
|
1086
1112
|
continue;
|
|
1087
1113
|
}
|
|
1088
1114
|
const scaleY = reflection.scaleY ?? 1;
|
|
1089
|
-
const distPx = (reflection.offsetEmu ?? 0) / EMU_PER_PX$
|
|
1115
|
+
const distPx = (reflection.offsetEmu ?? 0) / EMU_PER_PX$2;
|
|
1090
1116
|
const contactY = baselineY + descent;
|
|
1091
1117
|
const angleRad = (reflection.angleDeg ?? 0) * Math.PI / 180;
|
|
1092
1118
|
const offsetX = distPx * Math.cos(angleRad);
|
|
@@ -1099,7 +1125,7 @@ const emitTextReflections = (groups, textAnchor, x0, baselineY, descent, nextEff
|
|
|
1099
1125
|
const gradientId = `${id}-gradient`;
|
|
1100
1126
|
const maskId = `${id}-mask`;
|
|
1101
1127
|
const filterId = `${id}-blur`;
|
|
1102
|
-
const blurPx = Math.max(0, (reflection.blurEmu ?? 0) / EMU_PER_PX$
|
|
1128
|
+
const blurPx = Math.max(0, (reflection.blurEmu ?? 0) / EMU_PER_PX$2 / 2);
|
|
1103
1129
|
const fadeDirection = reflection.fadeDirection;
|
|
1104
1130
|
const defs = `<defs><linearGradient id="${gradientId}" ${fadeDirection === void 0 || fadeDirection === 90 ? "x1=\"0\" y1=\"1\" x2=\"0\" y2=\"0\"" : (() => {
|
|
1105
1131
|
const radians = fadeDirection * Math.PI / 180;
|
|
@@ -1135,8 +1161,8 @@ const emitTextOuterEffects = (groups, textAnchor, x0, baselineY, nextId) => {
|
|
|
1135
1161
|
if (shadow) {
|
|
1136
1162
|
const id = nextId("outer-shadow");
|
|
1137
1163
|
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$
|
|
1164
|
+
const distance = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$2;
|
|
1165
|
+
const blur = Math.max(0, (shadow.blurEmu ?? 50800) / EMU_PER_PX$2 / 2);
|
|
1140
1166
|
const glyphSize = renderedSizePxOf(group.piece);
|
|
1141
1167
|
const pad = Math.max(1, distance + blur * 3);
|
|
1142
1168
|
const color = shadow.color ?? "#000000";
|
|
@@ -1146,7 +1172,7 @@ const emitTextOuterEffects = (groups, textAnchor, x0, baselineY, nextId) => {
|
|
|
1146
1172
|
}
|
|
1147
1173
|
if (glow) {
|
|
1148
1174
|
const id = nextId("glow");
|
|
1149
|
-
const radius = Math.max(0, (glow.radiusEmu ?? 63500) / EMU_PER_PX$
|
|
1175
|
+
const radius = Math.max(0, (glow.radiusEmu ?? 63500) / EMU_PER_PX$2);
|
|
1150
1176
|
const glyphSize = renderedSizePxOf(group.piece);
|
|
1151
1177
|
const pad = Math.max(1, radius * 2);
|
|
1152
1178
|
const color = glow.color ?? "#FFFF00";
|
|
@@ -1170,9 +1196,9 @@ const emitTextInnerShadows = (groups, textAnchor, x0, baselineY, nextId) => {
|
|
|
1170
1196
|
}
|
|
1171
1197
|
const id = nextId("inner-shadow");
|
|
1172
1198
|
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$
|
|
1199
|
+
const dx = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$2 * Math.cos(angle);
|
|
1200
|
+
const dy = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$2 * Math.sin(angle);
|
|
1201
|
+
const blurPx = Math.max(0, (shadow.blurEmu ?? 50800) / EMU_PER_PX$2 / 2);
|
|
1176
1202
|
const color = shadow.color ?? "#000000";
|
|
1177
1203
|
const opacity = Math.max(0, Math.min(1, shadow.opacity ?? 1));
|
|
1178
1204
|
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 +1212,8 @@ const emitTextBevel = (groups, textAnchor, x0, baselineY, nextId) => {
|
|
|
1186
1212
|
const bevel = groups.find((g) => g.piece.bevel && !g.isTab && g.width > 0)?.piece.bevel;
|
|
1187
1213
|
if (!bevel) return "";
|
|
1188
1214
|
const id = nextId("bevel");
|
|
1189
|
-
const widthPx = bevel.widthEmu / EMU_PER_PX$
|
|
1190
|
-
const surfaceScale = bevel.heightEmu / EMU_PER_PX$
|
|
1215
|
+
const widthPx = bevel.widthEmu / EMU_PER_PX$2;
|
|
1216
|
+
const surfaceScale = bevel.heightEmu / EMU_PER_PX$2 * bevel.relief;
|
|
1191
1217
|
const table = [...bevel.profile.slice(1).map(() => 0), ...bevel.profile];
|
|
1192
1218
|
const flat = Math.sin(bevel.elevationDeg * Math.PI / 180);
|
|
1193
1219
|
const lightGain = bevel.contrast / (1 - flat);
|
|
@@ -1461,7 +1487,7 @@ function browserTextMeasurer() {
|
|
|
1461
1487
|
}
|
|
1462
1488
|
//#endregion
|
|
1463
1489
|
//#region src/text-bevel.ts
|
|
1464
|
-
const DEFAULT_BEVEL_EMU = 76200;
|
|
1490
|
+
const DEFAULT_BEVEL_EMU$1 = 76200;
|
|
1465
1491
|
const DEFAULT_PRESET = "circle";
|
|
1466
1492
|
const DEFAULT_RIG = {
|
|
1467
1493
|
type: "threePt",
|
|
@@ -1582,8 +1608,8 @@ const RELIEF = 2.5;
|
|
|
1582
1608
|
const textBevelOf = (text3d) => {
|
|
1583
1609
|
const bevel = text3d?.bevelTop;
|
|
1584
1610
|
if (!bevel) return null;
|
|
1585
|
-
const widthEmu = bevel.widthEmu ?? DEFAULT_BEVEL_EMU;
|
|
1586
|
-
const heightEmu = bevel.heightEmu ?? DEFAULT_BEVEL_EMU;
|
|
1611
|
+
const widthEmu = bevel.widthEmu ?? DEFAULT_BEVEL_EMU$1;
|
|
1612
|
+
const heightEmu = bevel.heightEmu ?? DEFAULT_BEVEL_EMU$1;
|
|
1587
1613
|
if (widthEmu <= 0 || heightEmu <= 0) return null;
|
|
1588
1614
|
const rig = text3d.scene?.lightRig ?? DEFAULT_RIG;
|
|
1589
1615
|
const look = RIG_LOOK(rig.type);
|
|
@@ -1603,6 +1629,63 @@ const textBevelOf = (text3d) => {
|
|
|
1603
1629
|
};
|
|
1604
1630
|
};
|
|
1605
1631
|
//#endregion
|
|
1632
|
+
//#region src/shape-3d.ts
|
|
1633
|
+
const EMU_PER_PX$1 = 9525;
|
|
1634
|
+
const DEFAULT_BEVEL_EMU = 76200;
|
|
1635
|
+
const LIGHT_AZIMUTH_DEG = 225;
|
|
1636
|
+
const LIGHT_ELEVATION_DEG = 45;
|
|
1637
|
+
const radians = (degrees) => degrees * Math.PI / 180;
|
|
1638
|
+
/**
|
|
1639
|
+
* An SVG `matrix(...)` for the camera's explicit rotation about the point
|
|
1640
|
+
* (`cx`, `cy`) in px, or `null` when the camera keeps the shape face-on.
|
|
1641
|
+
* The camera turns, so the shape appears turned the opposite way: a camera
|
|
1642
|
+
* revolution of +6° (Picture Style "Rotated, White") tilts the picture 6°
|
|
1643
|
+
* counter-clockwise.
|
|
1644
|
+
*/
|
|
1645
|
+
const cameraTransform = (shape3d, cx, cy) => {
|
|
1646
|
+
const rotation = shape3d?.scene?.cameraRotation;
|
|
1647
|
+
if (!rotation) return null;
|
|
1648
|
+
const { latitudeDeg, longitudeDeg, revolutionDeg } = rotation;
|
|
1649
|
+
if (latitudeDeg === 0 && longitudeDeg === 0 && revolutionDeg === 0) return null;
|
|
1650
|
+
const lon = radians(-longitudeDeg);
|
|
1651
|
+
const lat = radians(-latitudeDeg);
|
|
1652
|
+
const rev = radians(-revolutionDeg);
|
|
1653
|
+
const ex = [Math.cos(lon), Math.sin(lon) * Math.sin(lat)];
|
|
1654
|
+
const ey = [0, Math.cos(lat)];
|
|
1655
|
+
const cos = Math.cos(rev);
|
|
1656
|
+
const sin = Math.sin(rev);
|
|
1657
|
+
const turn = (v) => [cos * v[0] - sin * v[1], sin * v[0] + cos * v[1]];
|
|
1658
|
+
const [a, b] = turn(ex);
|
|
1659
|
+
const [c, d] = turn(ey);
|
|
1660
|
+
return `matrix(${[
|
|
1661
|
+
a,
|
|
1662
|
+
b,
|
|
1663
|
+
c,
|
|
1664
|
+
d,
|
|
1665
|
+
cx - a * cx - c * cy,
|
|
1666
|
+
cy - b * cx - d * cy
|
|
1667
|
+
].map((n) => n.toFixed(6)).join(" ")})`;
|
|
1668
|
+
};
|
|
1669
|
+
/**
|
|
1670
|
+
* The `<filter>` body that shades a top bevel onto whatever the shape painted
|
|
1671
|
+
* (picture, fill and border alike): a lit height map built from the blurred
|
|
1672
|
+
* outline, multiplied in so flat areas keep their color and slopes facing
|
|
1673
|
+
* away from the light darken, plus a specular highlight on the lit slopes.
|
|
1674
|
+
* `null` when the shape has no top bevel.
|
|
1675
|
+
*/
|
|
1676
|
+
const bevelFilterPrimitives = (shape3d) => {
|
|
1677
|
+
const bevel = shape3d?.bevelTop;
|
|
1678
|
+
if (!bevel) return null;
|
|
1679
|
+
const widthPx = (bevel.widthEmu ?? DEFAULT_BEVEL_EMU) / EMU_PER_PX$1;
|
|
1680
|
+
const heightPx = (bevel.heightEmu ?? DEFAULT_BEVEL_EMU) / EMU_PER_PX$1;
|
|
1681
|
+
if (widthPx <= 0 || heightPx <= 0) return null;
|
|
1682
|
+
const blur = widthPx / 2;
|
|
1683
|
+
const surfaceScale = Math.min(10, Math.max(1, heightPx / widthPx * 6));
|
|
1684
|
+
const diffuse = 1 / Math.sin(radians(LIGHT_ELEVATION_DEG));
|
|
1685
|
+
const light = `<feDistantLight azimuth="${LIGHT_AZIMUTH_DEG}" elevation="${LIGHT_ELEVATION_DEG}"/>`;
|
|
1686
|
+
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"/>`;
|
|
1687
|
+
};
|
|
1688
|
+
//#endregion
|
|
1606
1689
|
//#region src/render-slide.ts
|
|
1607
1690
|
const clickActionHref = (pres, action) => {
|
|
1608
1691
|
if (!action) return void 0;
|
|
@@ -1749,20 +1832,64 @@ const renderPicture = (shape, pres, x, y, w, h, transform, textOverlay, bytes, f
|
|
|
1749
1832
|
}
|
|
1750
1833
|
const opacityAttr = opacity !== 1 ? ` opacity="${opacity.toFixed(3)}"` : "";
|
|
1751
1834
|
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>` : "";
|
|
1835
|
+
const under = outline.fill !== "none" ? `${outline.defs}<g${transform} fill="${outline.fill}"${outline.fillAttrs}>${pictureClipGeometry(shape, preset, x, y, w, h)}</g>` : "";
|
|
1752
1836
|
if (layout?.mode === "tile") {
|
|
1753
1837
|
const intrinsic = getShapeImageIntrinsicSize(shape);
|
|
1754
1838
|
if (intrinsic) {
|
|
1755
1839
|
const pattern = imageTilePattern(dataUrl, layout, intrinsic, x, y, w, h, cropL, cropT, cropR, cropB);
|
|
1756
1840
|
const geometry = pictureClipGeometry(shape, preset, x, y, w, h);
|
|
1757
|
-
return `${clipDef}${pattern.defs}
|
|
1841
|
+
return `${clipDef}${pattern.defs}${pictureEffects(pres, shape, {
|
|
1842
|
+
x,
|
|
1843
|
+
y,
|
|
1844
|
+
w,
|
|
1845
|
+
h
|
|
1846
|
+
}, `${under}<g${transform} fill="${pattern.fill}"${filterAttr}${opacityAttr}>${geometry}</g>${border}`)}<g${transform}>${textOverlay}</g>`;
|
|
1758
1847
|
}
|
|
1759
1848
|
}
|
|
1760
|
-
return `${clipDef}
|
|
1849
|
+
return `${clipDef}${pictureEffects(pres, shape, {
|
|
1850
|
+
x,
|
|
1851
|
+
y,
|
|
1852
|
+
w,
|
|
1853
|
+
h
|
|
1854
|
+
}, `${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
1855
|
}
|
|
1762
1856
|
const linkUrl = getShapeImageLinkUrl(shape);
|
|
1763
1857
|
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
1858
|
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
1859
|
};
|
|
1860
|
+
/**
|
|
1861
|
+
* Wraps a picture's markup in its 3-D approximation (bevel lighting, then the
|
|
1862
|
+
* camera rotation) and its `<a:effectLst>` (shadows, glow, soft edge and the
|
|
1863
|
+
* reflection), the same effects a shape gets.
|
|
1864
|
+
*/
|
|
1865
|
+
const pictureEffects = (pres, shape, box, markup) => {
|
|
1866
|
+
const shape3d = getShape3D(shape);
|
|
1867
|
+
let defs = "";
|
|
1868
|
+
let content = markup;
|
|
1869
|
+
let mirrored = markup;
|
|
1870
|
+
const bevel = bevelFilterPrimitives(shape3d);
|
|
1871
|
+
if (bevel) {
|
|
1872
|
+
const id = mintId();
|
|
1873
|
+
defs += `<defs><filter id="${id}" x="-10%" y="-10%" width="120%" height="120%" color-interpolation-filters="sRGB">${bevel}</filter></defs>`;
|
|
1874
|
+
content = `<g filter="url(#${id})">${content}</g>`;
|
|
1875
|
+
}
|
|
1876
|
+
const camera = cameraTransform(shape3d, (box.x + box.w / 2) / EMU_PER_PX, (box.y + box.h / 2) / EMU_PER_PX);
|
|
1877
|
+
if (camera) {
|
|
1878
|
+
content = `<g transform="${camera}">${content}</g>`;
|
|
1879
|
+
mirrored = `<g transform="${camera}">${mirrored}</g>`;
|
|
1880
|
+
}
|
|
1881
|
+
const reflection = buildReflection(pres, shape, mirrored, box);
|
|
1882
|
+
const fx = buildEffectsFilter(pres, shape);
|
|
1883
|
+
if (fx) {
|
|
1884
|
+
defs += fx.defs;
|
|
1885
|
+
content = `<g filter="url(#${fx.id})">${content}</g>`;
|
|
1886
|
+
}
|
|
1887
|
+
if (reflection) {
|
|
1888
|
+
defs += reflection.defs;
|
|
1889
|
+
content = reflection.svg + content;
|
|
1890
|
+
}
|
|
1891
|
+
return defs + content;
|
|
1892
|
+
};
|
|
1766
1893
|
const imageTilePattern = (dataUrl, layout, intrinsic, x, y, w, h, cropL = 0, cropT = 0, cropR = 0, cropB = 0) => {
|
|
1767
1894
|
const sourceW = intrinsic.width * (layout.scaleX ?? 1);
|
|
1768
1895
|
const sourceH = intrinsic.height * (layout.scaleY ?? 1);
|
|
@@ -2064,7 +2191,22 @@ const patternDef = (pat) => {
|
|
|
2064
2191
|
fillAttr: `url(#${id})`
|
|
2065
2192
|
};
|
|
2066
2193
|
};
|
|
2067
|
-
const textFillPaint = (fill, theme) => (box) => {
|
|
2194
|
+
const textFillPaint = (fill, theme, solid) => (box) => {
|
|
2195
|
+
if (fill.kind === "none") return {
|
|
2196
|
+
defs: "",
|
|
2197
|
+
fill: "none"
|
|
2198
|
+
};
|
|
2199
|
+
if (fill.kind === "image") {
|
|
2200
|
+
if (!fill.bytes) return {
|
|
2201
|
+
defs: "",
|
|
2202
|
+
fill: solid
|
|
2203
|
+
};
|
|
2204
|
+
const id = mintId();
|
|
2205
|
+
return {
|
|
2206
|
+
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>`,
|
|
2207
|
+
fill: `url(#${id})`
|
|
2208
|
+
};
|
|
2209
|
+
}
|
|
2068
2210
|
if (fill.kind === "pattern") {
|
|
2069
2211
|
const built = patternDef({
|
|
2070
2212
|
preset: fill.preset,
|
|
@@ -2122,6 +2264,32 @@ const buildArrowMarker = (type, width, length, color, orient) => {
|
|
|
2122
2264
|
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
2265
|
};
|
|
2124
2266
|
};
|
|
2267
|
+
const unrotatedGradientTransform = (grad, shape, pres) => {
|
|
2268
|
+
const bounds = shape && (pres ? getShapeBoundsResolved(pres, shape) : getShapeBounds(shape));
|
|
2269
|
+
if (grad.rotateWithShape !== false || !shape || !bounds || bounds.w <= 0 || bounds.h <= 0) return "";
|
|
2270
|
+
const angle = getShapeRotation(shape) * Math.PI / 180;
|
|
2271
|
+
const cos = Math.cos(angle);
|
|
2272
|
+
const sin = Math.sin(angle);
|
|
2273
|
+
const w = bounds.w;
|
|
2274
|
+
const h = bounds.h;
|
|
2275
|
+
const rotatedW = Math.abs(cos) * w + Math.abs(sin) * h;
|
|
2276
|
+
const rotatedH = Math.abs(sin) * w + Math.abs(cos) * h;
|
|
2277
|
+
const flip = getShapeFlip(shape);
|
|
2278
|
+
const sx = flip?.horizontal ? -1 : 1;
|
|
2279
|
+
const sy = flip?.vertical ? -1 : 1;
|
|
2280
|
+
const a = sx * cos * rotatedW / w;
|
|
2281
|
+
const b = -sy * sin * rotatedW / h;
|
|
2282
|
+
const c = sx * sin * rotatedH / w;
|
|
2283
|
+
const d = sy * cos * rotatedH / h;
|
|
2284
|
+
return ` gradientTransform="matrix(${[
|
|
2285
|
+
a,
|
|
2286
|
+
b,
|
|
2287
|
+
c,
|
|
2288
|
+
d,
|
|
2289
|
+
(1 - a - c) / 2,
|
|
2290
|
+
(1 - b - d) / 2
|
|
2291
|
+
].map((value) => value.toFixed(6)).join(" ")})"`;
|
|
2292
|
+
};
|
|
2125
2293
|
const paint = (shape, fill, stroke, theme, isPlaceholder, pres) => {
|
|
2126
2294
|
let fillColor;
|
|
2127
2295
|
let fillAttrs = "";
|
|
@@ -2136,33 +2304,7 @@ const paint = (shape, fill, stroke, theme, isPlaceholder, pres) => {
|
|
|
2136
2304
|
else if (fill.kind === "gradient") {
|
|
2137
2305
|
const grad = shape ? pres ? getShapeGradientFillEffective(pres, shape) : getShapeGradientFill(shape) : null;
|
|
2138
2306
|
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);
|
|
2307
|
+
const built = gradientDef(grad, theme, unrotatedGradientTransform(grad, shape, pres));
|
|
2166
2308
|
defs = built.defs;
|
|
2167
2309
|
fillColor = built.fillAttr;
|
|
2168
2310
|
} else fillColor = (shape && pres ? getShapeFillColorResolved(pres, shape) : null) ?? "none";
|
|
@@ -2179,10 +2321,34 @@ const paint = (shape, fill, stroke, theme, isPlaceholder, pres) => {
|
|
|
2179
2321
|
let strokeWidth = 0;
|
|
2180
2322
|
const strokeAttrParts = [];
|
|
2181
2323
|
let markerAttrs = "";
|
|
2324
|
+
let headColor = strokeColor;
|
|
2325
|
+
let tailColor = strokeColor;
|
|
2182
2326
|
if (stroke.kind === "solid") {
|
|
2183
2327
|
let resolved = null;
|
|
2184
2328
|
if (shape && pres) resolved = getShapeStrokeColorResolved(pres, shape);
|
|
2185
2329
|
strokeColor = resolved ?? resolveColor(stroke.color, theme, "#9CA3AF");
|
|
2330
|
+
headColor = strokeColor;
|
|
2331
|
+
tailColor = strokeColor;
|
|
2332
|
+
} else if (stroke.kind === "gradient") {
|
|
2333
|
+
const grad = shape ? getShapeStrokeGradient(shape, pres) : null;
|
|
2334
|
+
const bounds = shape && (pres ? getShapeBoundsResolved(pres, shape) : getShapeBounds(shape));
|
|
2335
|
+
if (grad && bounds) {
|
|
2336
|
+
const stopColor = (stop) => stop ? stop.resolvedColor ?? resolveColor(stop.color, theme, "#9CA3AF") : "#9CA3AF";
|
|
2337
|
+
const ordered = [...grad.stops].sort((a, b) => a.offset - b.offset);
|
|
2338
|
+
headColor = stopColor(ordered[0]);
|
|
2339
|
+
tailColor = stopColor(ordered[ordered.length - 1]);
|
|
2340
|
+
const widthEmu = stroke.widthEmu ?? 9525;
|
|
2341
|
+
const built = bounds.w > 0 && bounds.h > 0 ? gradientDef(grad, theme, unrotatedGradientTransform(grad, shape, pres)) : gradientDef(grad, theme, "", {
|
|
2342
|
+
x: (bounds.w > 0 ? bounds.x : bounds.x - widthEmu / 2) / EMU_PER_PX,
|
|
2343
|
+
y: (bounds.h > 0 ? bounds.y : bounds.y - widthEmu / 2) / EMU_PER_PX,
|
|
2344
|
+
w: Math.max(bounds.w, widthEmu) / EMU_PER_PX,
|
|
2345
|
+
h: Math.max(bounds.h, widthEmu) / EMU_PER_PX
|
|
2346
|
+
});
|
|
2347
|
+
defs += built.defs;
|
|
2348
|
+
strokeColor = built.fillAttr;
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
if (strokeColor !== "none" && (stroke.kind === "solid" || stroke.kind === "gradient")) {
|
|
2186
2352
|
strokeWidth = stroke.widthEmu ?? 9525;
|
|
2187
2353
|
if (shape) {
|
|
2188
2354
|
const opacity = getShapeStrokeOpacity(shape, pres);
|
|
@@ -2208,12 +2374,12 @@ const paint = (shape, fill, stroke, theme, isPlaceholder, pres) => {
|
|
|
2208
2374
|
const head = getShapeStrokeArrow(shape, "head", pres);
|
|
2209
2375
|
const tail = getShapeStrokeArrow(shape, "tail", pres);
|
|
2210
2376
|
if (head && head.type !== "none") {
|
|
2211
|
-
const m = buildArrowMarker(head.type, head.width, head.length,
|
|
2377
|
+
const m = buildArrowMarker(head.type, head.width, head.length, headColor, "auto-start-reverse");
|
|
2212
2378
|
defs += m.def;
|
|
2213
2379
|
markerAttrs += ` marker-start="url(#${m.id})"`;
|
|
2214
2380
|
}
|
|
2215
2381
|
if (tail && tail.type !== "none") {
|
|
2216
|
-
const m = buildArrowMarker(tail.type, tail.width, tail.length,
|
|
2382
|
+
const m = buildArrowMarker(tail.type, tail.width, tail.length, tailColor, "auto");
|
|
2217
2383
|
defs += m.def;
|
|
2218
2384
|
markerAttrs += ` marker-end="url(#${m.id})"`;
|
|
2219
2385
|
}
|
|
@@ -2260,7 +2426,7 @@ const cssColorWithOpacity = (hex, opacity) => {
|
|
|
2260
2426
|
};
|
|
2261
2427
|
const renderRun = (text, format, theme, effectivePt, defaultColor, _wasDefault = false, suppressLineHeight = false, lineHeightOverride = void 0) => {
|
|
2262
2428
|
if (text === "") return "";
|
|
2263
|
-
if (format?.textFill) {
|
|
2429
|
+
if (format?.textFill || format?.outline?.fill) {
|
|
2264
2430
|
const { textFill: _fill, outline: _outline, shadow: _shadow, glow: _glow, underline: _underline, strike: _strike, ...layout } = format;
|
|
2265
2431
|
return renderRun(text, {
|
|
2266
2432
|
...layout,
|
|
@@ -2320,6 +2486,10 @@ const renderRun = (text, format, theme, effectivePt, defaultColor, _wasDefault =
|
|
|
2320
2486
|
const LINE_HEIGHT = 1.05;
|
|
2321
2487
|
const AUTOFIT_FLOOR = .25;
|
|
2322
2488
|
const AUTOFIT_STEP = .05;
|
|
2489
|
+
const decimalSeparatorField = (lang) => {
|
|
2490
|
+
const separator = decimalSeparatorOf(lang);
|
|
2491
|
+
return separator === "." ? {} : { decimalSeparator: separator };
|
|
2492
|
+
};
|
|
2323
2493
|
const underlineStyleOf = (fmt) => {
|
|
2324
2494
|
const u = fmt?.underline;
|
|
2325
2495
|
if (u === void 0 || u === false || u === "none") return "none";
|
|
@@ -2349,7 +2519,7 @@ const strikeStyleOf = (fmt) => {
|
|
|
2349
2519
|
if (s === "dblStrike") return "double";
|
|
2350
2520
|
return s !== void 0 && s !== false && s !== "noStrike";
|
|
2351
2521
|
};
|
|
2352
|
-
const hasPaintedTextFill = (run) => !!run.fmt?.textFill && !run.href;
|
|
2522
|
+
const hasPaintedTextFill = (run) => !!(run.fmt?.textFill || run.fmt?.outline?.fill) && !run.href;
|
|
2353
2523
|
const alignOf = (a) => a === "center" || a === "right" || a === "justify" ? a : "left";
|
|
2354
2524
|
const verticalLayoutOf = (vert) => {
|
|
2355
2525
|
switch (vert) {
|
|
@@ -2427,7 +2597,10 @@ const buildSvgTextInput = (a) => {
|
|
|
2427
2597
|
letterSpacingPx,
|
|
2428
2598
|
kerning,
|
|
2429
2599
|
fillHex,
|
|
2430
|
-
...
|
|
2600
|
+
...hasPaintedTextFill(run) ? { fillPaint: fmt?.textFill ? textFillPaint(fmt.textFill, a.theme, fillHex) : () => ({
|
|
2601
|
+
defs: "",
|
|
2602
|
+
fill: fillHex
|
|
2603
|
+
}) } : {},
|
|
2431
2604
|
...fmt?.underlineColor !== void 0 && fmt.underlineColor !== null ? { underlineHex: resolveColor(fmt.underlineColor, a.theme, fillHex) } : {},
|
|
2432
2605
|
underline: underlineStyleOf(fmt),
|
|
2433
2606
|
strike: strikeStyleOf(fmt),
|
|
@@ -2436,6 +2609,21 @@ const buildSvgTextInput = (a) => {
|
|
|
2436
2609
|
outlineHex: resolveColor(fmt.outline.color, a.theme, "#000000"),
|
|
2437
2610
|
outlineWidthPx: (fmt.outline.widthEmu ?? 9525) / EMU_PER_PX
|
|
2438
2611
|
} : {},
|
|
2612
|
+
...fmt?.outline?.fill && !run.href ? (() => {
|
|
2613
|
+
const fill = fmt.outline.fill;
|
|
2614
|
+
const first = fill.stops[0];
|
|
2615
|
+
return {
|
|
2616
|
+
outlineHex: first ? first.resolvedColor ?? resolveColor(first.color, a.theme, "#000000") : "#000000",
|
|
2617
|
+
outlinePaint: (box) => {
|
|
2618
|
+
const built = gradientDef(fill, a.theme, "", box);
|
|
2619
|
+
return {
|
|
2620
|
+
defs: built.defs,
|
|
2621
|
+
fill: built.fillAttr
|
|
2622
|
+
};
|
|
2623
|
+
},
|
|
2624
|
+
outlineWidthPx: (fmt.outline.widthEmu ?? 9525) / EMU_PER_PX
|
|
2625
|
+
};
|
|
2626
|
+
})() : {},
|
|
2439
2627
|
...fmt?.shadow ? { shadow: {
|
|
2440
2628
|
...fmt.shadow,
|
|
2441
2629
|
...fmt.shadow.color ? { color: resolveColor(fmt.shadow.color, a.theme, "#000000") } : {}
|
|
@@ -2452,7 +2640,8 @@ const buildSvgTextInput = (a) => {
|
|
|
2452
2640
|
} } : {},
|
|
2453
2641
|
...a.bevel ? { bevel: a.bevel } : {},
|
|
2454
2642
|
href: run.href ?? null,
|
|
2455
|
-
...run.hrefTip !== void 0 ? { hrefTip: run.hrefTip } : {}
|
|
2643
|
+
...run.hrefTip !== void 0 ? { hrefTip: run.hrefTip } : {},
|
|
2644
|
+
...run.decimalSeparator !== void 0 ? { decimalSeparator: run.decimalSeparator } : {}
|
|
2456
2645
|
};
|
|
2457
2646
|
const segs = run.text.split("\n");
|
|
2458
2647
|
segs.forEach((seg, i) => {
|
|
@@ -2684,10 +2873,11 @@ const resolveTextBodyModel = (pres, shape, bounds, theme, phType, measure, defau
|
|
|
2684
2873
|
} catch {
|
|
2685
2874
|
elements = [];
|
|
2686
2875
|
}
|
|
2876
|
+
const languages = elements.length ? getParagraphElementLanguages(shape, p) : [];
|
|
2687
2877
|
let rIdx = 0;
|
|
2688
2878
|
let fieldIndex = 0;
|
|
2689
2879
|
let breakIndex = 0;
|
|
2690
|
-
for (const el of elements) {
|
|
2880
|
+
for (const [elementIndex, el] of elements.entries()) {
|
|
2691
2881
|
if (el.kind === "br") {
|
|
2692
2882
|
const fmt = getShapeRunFormatEffective(pres, shape, p, { breakIndex: breakIndex++ });
|
|
2693
2883
|
runs.push({
|
|
@@ -2722,6 +2912,7 @@ const resolveTextBodyModel = (pres, shape, bounds, theme, phType, measure, defau
|
|
|
2722
2912
|
text: txt,
|
|
2723
2913
|
fmt,
|
|
2724
2914
|
sizePt,
|
|
2915
|
+
...decimalSeparatorField(languages[elementIndex]),
|
|
2725
2916
|
...href !== void 0 ? { href } : {},
|
|
2726
2917
|
...hrefTip !== void 0 ? { hrefTip } : {}
|
|
2727
2918
|
});
|
|
@@ -2861,9 +3052,15 @@ const renderHtmlParagraphs = (paraData, numberLabels, theme, autoFitScale, defau
|
|
|
2861
3052
|
let runFmt = run.fmt;
|
|
2862
3053
|
if (run.href) {
|
|
2863
3054
|
const hlinkColor = theme ? normalizeHex(theme.hyperlink) : "#0563C1";
|
|
2864
|
-
const { textFill: _linkFill, ...linkFmt } = runFmt ?? {};
|
|
3055
|
+
const { textFill: _linkFill, outline: linkOutline, ...linkFmt } = runFmt ?? {};
|
|
3056
|
+
const { fill: outlineFill, ...solidOutline } = linkOutline ?? {};
|
|
3057
|
+
const outlineColor = outlineFill?.stops[0]?.color ?? solidOutline.color;
|
|
2865
3058
|
runFmt = {
|
|
2866
3059
|
...linkFmt,
|
|
3060
|
+
...linkOutline ? { outline: {
|
|
3061
|
+
...solidOutline,
|
|
3062
|
+
...outlineColor !== void 0 ? { color: outlineColor } : {}
|
|
3063
|
+
} } : {},
|
|
2867
3064
|
color: hlinkColor,
|
|
2868
3065
|
underline: runFmt?.underline ?? true
|
|
2869
3066
|
};
|
|
@@ -2894,8 +3091,8 @@ const renderHtmlParagraphs = (paraData, numberLabels, theme, autoFitScale, defau
|
|
|
2894
3091
|
"padding:0",
|
|
2895
3092
|
`text-align:${ALIGNMENT_TO_CSS[para.align] ?? "left"}`,
|
|
2896
3093
|
lineHeightCss,
|
|
2897
|
-
leftPx > 0 ? `padding-
|
|
2898
|
-
rightPx > 0 ? `padding-
|
|
3094
|
+
leftPx > 0 ? `padding-inline-start:${leftPx.toFixed(2)}px` : "",
|
|
3095
|
+
rightPx > 0 ? `padding-inline-end:${rightPx.toFixed(2)}px` : "",
|
|
2899
3096
|
firstLinePx !== 0 ? `text-indent:${firstLinePx.toFixed(2)}px` : ""
|
|
2900
3097
|
].filter(Boolean);
|
|
2901
3098
|
let prefix = "";
|
|
@@ -4554,10 +4751,11 @@ const cellParaData = (paragraphs, cell, pres) => {
|
|
|
4554
4751
|
const bulletIsPicture = isParagraphBulletPicture(cell, index);
|
|
4555
4752
|
const bulletImageBytes = bulletIsPicture ? getParagraphBulletImageBytes(cell, index) : null;
|
|
4556
4753
|
const runs = [];
|
|
4754
|
+
const languages = getParagraphElementLanguages(cell, index);
|
|
4557
4755
|
let rIdx = 0;
|
|
4558
4756
|
let fieldIndex = 0;
|
|
4559
4757
|
let breakIndex = 0;
|
|
4560
|
-
for (const el of para.elements) {
|
|
4758
|
+
for (const [elementIndex, el] of para.elements.entries()) {
|
|
4561
4759
|
if (el.kind === "br") {
|
|
4562
4760
|
const fmt = getTableCellRunFormatEffective(pres, cell, index, { breakIndex: breakIndex++ });
|
|
4563
4761
|
runs.push({
|
|
@@ -4574,6 +4772,7 @@ const cellParaData = (paragraphs, cell, pres) => {
|
|
|
4574
4772
|
text: el.text,
|
|
4575
4773
|
fmt,
|
|
4576
4774
|
sizePt: fmt?.size ?? DEFAULT_BODY_PT,
|
|
4775
|
+
...decimalSeparatorField(languages[elementIndex]),
|
|
4577
4776
|
...href ? {
|
|
4578
4777
|
href,
|
|
4579
4778
|
...el.tooltip !== void 0 ? { hrefTip: el.tooltip } : {}
|
|
@@ -4679,6 +4878,33 @@ const renderTableCellText = (cell, paragraphs, cx, cy, cw, ch, color, pres, shap
|
|
|
4679
4878
|
const body = renderHtmlParagraphs(paraData, numberLabels, theme, 1, DEFAULT_BODY_PT, color).join("");
|
|
4680
4879
|
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
4880
|
};
|
|
4881
|
+
const TWO_STOP_TABLE_GRADIENT_EXPONENT = 1.85;
|
|
4882
|
+
const TWO_STOP_TABLE_GRADIENT_SAMPLES = 16;
|
|
4883
|
+
const twoStopTableGradient = (grad, theme) => {
|
|
4884
|
+
if (grad.path !== void 0 || grad.stops.length !== 2) return grad;
|
|
4885
|
+
const [start, end] = [...grad.stops].sort((a, b) => a.offset - b.offset);
|
|
4886
|
+
if (!start || !end || end.offset <= start.offset) return grad;
|
|
4887
|
+
const span = end.offset - start.offset;
|
|
4888
|
+
const colorOf = (stop) => stop.resolvedColor ?? resolveColor(stop.color, theme, "#E5E7EB");
|
|
4889
|
+
const startColor = colorOf(start);
|
|
4890
|
+
const endColor = colorOf(end);
|
|
4891
|
+
const stops = Array.from({ length: 17 }, (_, i) => {
|
|
4892
|
+
const along = i / TWO_STOP_TABLE_GRADIENT_SAMPLES;
|
|
4893
|
+
const startWeight = (1 - along) ** TWO_STOP_TABLE_GRADIENT_EXPONENT;
|
|
4894
|
+
const color = mixHex(startColor, endColor, startWeight);
|
|
4895
|
+
const opacity = start.opacity === void 0 && end.opacity === void 0 ? void 0 : (start.opacity ?? 1) * startWeight + (end.opacity ?? 1) * (1 - startWeight);
|
|
4896
|
+
return {
|
|
4897
|
+
offset: start.offset + along * span,
|
|
4898
|
+
color,
|
|
4899
|
+
resolvedColor: color,
|
|
4900
|
+
...opacity === void 0 ? {} : { opacity }
|
|
4901
|
+
};
|
|
4902
|
+
});
|
|
4903
|
+
return {
|
|
4904
|
+
...grad,
|
|
4905
|
+
stops
|
|
4906
|
+
};
|
|
4907
|
+
};
|
|
4682
4908
|
const renderTable = (shape, pres, x, y, w, h, transform, theme, ctx) => {
|
|
4683
4909
|
let dims;
|
|
4684
4910
|
let widths;
|
|
@@ -4711,6 +4937,16 @@ const renderTable = (shape, pres, x, y, w, h, transform, theme, ctx) => {
|
|
|
4711
4937
|
const tableThemeFace = getPresentationFonts(pres)?.minorLatin ?? null;
|
|
4712
4938
|
const out = [];
|
|
4713
4939
|
out.push(`<g${transform}>`);
|
|
4940
|
+
const background = getTableBackgroundEffective(pres, shape);
|
|
4941
|
+
const tableW = (colXs[dims.cols] ?? xPx) - xPx;
|
|
4942
|
+
const tableH = (rowYs[dims.rows] ?? yPx) - yPx;
|
|
4943
|
+
if (background?.fill.kind === "solid") {
|
|
4944
|
+
const opacity = background.fillOpacity !== void 0 && background.fillOpacity < 1 ? ` fill-opacity="${background.fillOpacity.toFixed(3)}"` : "";
|
|
4945
|
+
out.push(`<rect x="${px(xPx)}" y="${px(yPx)}" width="${px(tableW)}" height="${px(tableH)}" fill="${resolveColor(background.fill.color, theme, "#FFFFFF")}"${opacity}/>`);
|
|
4946
|
+
} else if (background?.gradient) {
|
|
4947
|
+
const built = gradientDef(twoStopTableGradient(background.gradient, theme), theme);
|
|
4948
|
+
out.push(`${built.defs}<rect x="${px(xPx)}" y="${px(yPx)}" width="${px(tableW)}" height="${px(tableH)}" fill="${built.fillAttr}"/>`);
|
|
4949
|
+
}
|
|
4714
4950
|
const borderEdges = [];
|
|
4715
4951
|
const borderEdgeCandidates = /* @__PURE__ */ new Map();
|
|
4716
4952
|
for (let r = 0; r < dims.rows; r++) for (let c = 0; c < dims.cols; c++) {
|
|
@@ -4733,7 +4969,8 @@ const renderTable = (shape, pres, x, y, w, h, transform, theme, ctx) => {
|
|
|
4733
4969
|
else if (fill.kind !== "inherit") resolvedFill = "none";
|
|
4734
4970
|
else resolvedFill = "none";
|
|
4735
4971
|
const cellTextColor = textColor;
|
|
4736
|
-
|
|
4972
|
+
const fillOpacity = fill.kind === "solid" && appearance.fillOpacity !== void 0 && appearance.fillOpacity < 1 ? ` fill-opacity="${appearance.fillOpacity.toFixed(3)}"` : "";
|
|
4973
|
+
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
4974
|
const borders = appearance.borders;
|
|
4738
4975
|
const dashAttr = (dash, widthPx) => {
|
|
4739
4976
|
if (!dash || dash === "solid") return "";
|
|
@@ -4893,6 +5130,148 @@ const PATH_SHADE = {
|
|
|
4893
5130
|
lighten: "fill=\"#fff\" fill-opacity=\"0.4\"",
|
|
4894
5131
|
lightenLess: "fill=\"#fff\" fill-opacity=\"0.2\""
|
|
4895
5132
|
};
|
|
5133
|
+
const SKETCH_AMPLITUDE = {
|
|
5134
|
+
curved: .008,
|
|
5135
|
+
freehand: .014,
|
|
5136
|
+
scribble: .022
|
|
5137
|
+
};
|
|
5138
|
+
const SKETCH_STEP = .12;
|
|
5139
|
+
const seededRandom = (seed) => {
|
|
5140
|
+
let state = seed >>> 0;
|
|
5141
|
+
return () => {
|
|
5142
|
+
state = state + 1831565813 >>> 0;
|
|
5143
|
+
let t = state;
|
|
5144
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
5145
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
5146
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
5147
|
+
};
|
|
5148
|
+
};
|
|
5149
|
+
/**
|
|
5150
|
+
* An approximation of a sketched outline (`ask:lineSketchStyleProps`): every
|
|
5151
|
+
* edge of `geom` is redrawn as a seeded hand-drawn wobble. Only used for a
|
|
5152
|
+
* preset geometry the writer left crisp; sketches the reference desktop app wrote already
|
|
5153
|
+
* carry their hand-drawn path as custom geometry.
|
|
5154
|
+
*/
|
|
5155
|
+
const sketchGeometry = (geom, sketch, w, h, seed) => {
|
|
5156
|
+
const random = seededRandom(seed);
|
|
5157
|
+
const jitter = (amp) => (random() * 2 - 1) * amp;
|
|
5158
|
+
const paths = geom.paths.map((path) => {
|
|
5159
|
+
const cw = path.w ?? w;
|
|
5160
|
+
const ch = path.h ?? h;
|
|
5161
|
+
const size = Math.max(Math.min(cw, ch), Math.max(cw, ch) / 4);
|
|
5162
|
+
const amp = SKETCH_AMPLITUDE[sketch] * size;
|
|
5163
|
+
const step = SKETCH_STEP * Math.max(cw, ch);
|
|
5164
|
+
const nudge = (pt) => ({
|
|
5165
|
+
x: pt.x + jitter(amp),
|
|
5166
|
+
y: pt.y + jitter(amp)
|
|
5167
|
+
});
|
|
5168
|
+
const commands = [];
|
|
5169
|
+
let curX = 0;
|
|
5170
|
+
let curY = 0;
|
|
5171
|
+
let startX = 0;
|
|
5172
|
+
let startY = 0;
|
|
5173
|
+
const wobbleTo = (x, y) => {
|
|
5174
|
+
const dx = x - curX;
|
|
5175
|
+
const dy = y - curY;
|
|
5176
|
+
const len = Math.hypot(dx, dy);
|
|
5177
|
+
const count = step > 0 ? Math.max(1, Math.round(len / step)) : 1;
|
|
5178
|
+
const nx = len > 0 ? -dy / len : 0;
|
|
5179
|
+
const ny = len > 0 ? dx / len : 0;
|
|
5180
|
+
let px = curX;
|
|
5181
|
+
let py = curY;
|
|
5182
|
+
for (let i = 1; i <= count; i++) {
|
|
5183
|
+
const last = i === count;
|
|
5184
|
+
const ex = curX + dx * i / count + (last ? 0 : jitter(amp / 2));
|
|
5185
|
+
const ey = curY + dy * i / count + (last ? 0 : jitter(amp / 2));
|
|
5186
|
+
const bend = jitter(amp);
|
|
5187
|
+
const control = {
|
|
5188
|
+
x: (px + ex) / 2 + nx * bend,
|
|
5189
|
+
y: (py + ey) / 2 + ny * bend
|
|
5190
|
+
};
|
|
5191
|
+
commands.push({
|
|
5192
|
+
kind: "quadBezTo",
|
|
5193
|
+
pts: [control, {
|
|
5194
|
+
x: ex,
|
|
5195
|
+
y: ey
|
|
5196
|
+
}]
|
|
5197
|
+
});
|
|
5198
|
+
px = ex;
|
|
5199
|
+
py = ey;
|
|
5200
|
+
}
|
|
5201
|
+
curX = x;
|
|
5202
|
+
curY = y;
|
|
5203
|
+
};
|
|
5204
|
+
for (const cmd of path.commands) switch (cmd.kind) {
|
|
5205
|
+
case "moveTo":
|
|
5206
|
+
curX = cmd.pt.x;
|
|
5207
|
+
curY = cmd.pt.y;
|
|
5208
|
+
startX = curX;
|
|
5209
|
+
startY = curY;
|
|
5210
|
+
commands.push(cmd);
|
|
5211
|
+
break;
|
|
5212
|
+
case "lnTo":
|
|
5213
|
+
wobbleTo(cmd.pt.x, cmd.pt.y);
|
|
5214
|
+
break;
|
|
5215
|
+
case "quadBezTo":
|
|
5216
|
+
commands.push({
|
|
5217
|
+
kind: "quadBezTo",
|
|
5218
|
+
pts: [nudge(cmd.pts[0]), cmd.pts[1]]
|
|
5219
|
+
});
|
|
5220
|
+
curX = cmd.pts[1].x;
|
|
5221
|
+
curY = cmd.pts[1].y;
|
|
5222
|
+
break;
|
|
5223
|
+
case "cubicBezTo":
|
|
5224
|
+
commands.push({
|
|
5225
|
+
kind: "cubicBezTo",
|
|
5226
|
+
pts: [
|
|
5227
|
+
nudge(cmd.pts[0]),
|
|
5228
|
+
nudge(cmd.pts[1]),
|
|
5229
|
+
cmd.pts[2]
|
|
5230
|
+
]
|
|
5231
|
+
});
|
|
5232
|
+
curX = cmd.pts[2].x;
|
|
5233
|
+
curY = cmd.pts[2].y;
|
|
5234
|
+
break;
|
|
5235
|
+
case "arcTo":
|
|
5236
|
+
for (const s of arcToCubicSegments(curX, curY, cmd.wR, cmd.hR, cmd.stAng, cmd.swAng)) {
|
|
5237
|
+
commands.push({
|
|
5238
|
+
kind: "cubicBezTo",
|
|
5239
|
+
pts: [
|
|
5240
|
+
nudge({
|
|
5241
|
+
x: s.c1x,
|
|
5242
|
+
y: s.c1y
|
|
5243
|
+
}),
|
|
5244
|
+
nudge({
|
|
5245
|
+
x: s.c2x,
|
|
5246
|
+
y: s.c2y
|
|
5247
|
+
}),
|
|
5248
|
+
{
|
|
5249
|
+
x: s.ex,
|
|
5250
|
+
y: s.ey
|
|
5251
|
+
}
|
|
5252
|
+
]
|
|
5253
|
+
});
|
|
5254
|
+
curX = s.ex;
|
|
5255
|
+
curY = s.ey;
|
|
5256
|
+
}
|
|
5257
|
+
break;
|
|
5258
|
+
case "close":
|
|
5259
|
+
if (curX !== startX || curY !== startY) wobbleTo(startX, startY);
|
|
5260
|
+
commands.push(cmd);
|
|
5261
|
+
curX = startX;
|
|
5262
|
+
curY = startY;
|
|
5263
|
+
break;
|
|
5264
|
+
}
|
|
5265
|
+
return {
|
|
5266
|
+
...path,
|
|
5267
|
+
commands
|
|
5268
|
+
};
|
|
5269
|
+
});
|
|
5270
|
+
return {
|
|
5271
|
+
...geom,
|
|
5272
|
+
paths
|
|
5273
|
+
};
|
|
5274
|
+
};
|
|
4896
5275
|
/**
|
|
4897
5276
|
* Renders an evaluated {@link CustomGeometry} (a `<a:custGeom>` or a preset
|
|
4898
5277
|
* from `getPresetGeometry`) as `<path>`s in the shape's slide box. Returns
|
|
@@ -5109,6 +5488,11 @@ const renderShapeContent = (shape, pres, theme, ctx) => {
|
|
|
5109
5488
|
const customGeom = rawPreset === null ? getShapeCustomGeometry(shape) : null;
|
|
5110
5489
|
let geomSvg = customGeom !== null ? customGeometryToSvg(customGeom, x, y, w, h, p.fill, fa, p.stroke, p.strokeWidth, sa, ma) : "";
|
|
5111
5490
|
const isCustGeom = geomSvg === "" && rawPreset === null && getShapeXmlString(shape).includes("custGeom");
|
|
5491
|
+
const sketch = rawPreset !== null ? getShapeStrokeSketch(shape) : null;
|
|
5492
|
+
if (geomSvg !== "") {} else if (sketch !== null && p.stroke !== "none") {
|
|
5493
|
+
const geometry = getPresetGeometry(preset, bounds, getShapeAdjustValues(shape));
|
|
5494
|
+
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);
|
|
5495
|
+
}
|
|
5112
5496
|
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
5497
|
else {
|
|
5114
5498
|
const geometry = getPresetGeometry(preset, bounds, getShapeAdjustValues(shape));
|
|
@@ -5201,11 +5585,14 @@ const buildReflection = (pres, shape, geomRaw, box) => {
|
|
|
5201
5585
|
const f = refl.scaleY ?? -1;
|
|
5202
5586
|
const startA = refl.startOpacity ?? 1;
|
|
5203
5587
|
const endA = refl.opacity ?? 0;
|
|
5588
|
+
const startPos = refl.startPosition ?? 0;
|
|
5589
|
+
const endPos = Math.max(startPos, refl.endPosition ?? 1);
|
|
5204
5590
|
const contactPx = (box.y + box.h) / EMU_PER_PX;
|
|
5205
5591
|
const transform = `translate(0 ${(refl.distEmu / EMU_PER_PX + contactPx * (1 - f)).toFixed(2)}) scale(1 ${f})`;
|
|
5206
5592
|
const maskId = mintId();
|
|
5207
5593
|
const gradId = mintId();
|
|
5208
|
-
const
|
|
5594
|
+
const [y1, y2] = f < 0 ? [1, 0] : [0, 1];
|
|
5595
|
+
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
5596
|
return {
|
|
5210
5597
|
svg: `<g transform="${transform}" mask="url(#${maskId})" data-pptx-reflection="1">${geomRaw}</g>`,
|
|
5211
5598
|
defs
|
|
@@ -5253,9 +5640,9 @@ const buildEffectsFilter = (pres, shape) => {
|
|
|
5253
5640
|
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
5641
|
layers.push(`glowOut${i}`);
|
|
5255
5642
|
} else if (e.kind === "softEdge") {
|
|
5256
|
-
const
|
|
5643
|
+
const radiusPx = e.radiusEmu / EMU_PER_PX;
|
|
5257
5644
|
const i = primitives.length;
|
|
5258
|
-
primitives.push(`<feGaussianBlur in="
|
|
5645
|
+
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
5646
|
layers.length = 0;
|
|
5260
5647
|
layers.push(`softOut${i}`);
|
|
5261
5648
|
} else if (e.kind === "blur") {
|
|
@@ -5297,14 +5684,33 @@ const topLevelShapes = (shapes, opts) => {
|
|
|
5297
5684
|
}
|
|
5298
5685
|
return out;
|
|
5299
5686
|
};
|
|
5300
|
-
const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
5687
|
+
const renderSlideSvg = (pres, slide, opts = {}) => renderSurface(pres, {
|
|
5688
|
+
slide,
|
|
5689
|
+
layout: getSlideLayout(slide),
|
|
5690
|
+
master: false
|
|
5691
|
+
}, opts);
|
|
5692
|
+
/**
|
|
5693
|
+
* Draws a slide layout — or, with `master`, its slide master — the way
|
|
5694
|
+
* the reference desktop app's Slide Master view shows it behind the placeholders: the
|
|
5695
|
+
* background and the decorative (non-placeholder) shapes of the master and,
|
|
5696
|
+
* for a layout, of the layout itself. A layout that hides background graphics
|
|
5697
|
+
* leaves the master's shapes out. Placeholders are not drawn; an editor draws
|
|
5698
|
+
* them with their prompt text over this.
|
|
5699
|
+
*/
|
|
5700
|
+
const renderSlideLayoutSvg = (pres, layout, opts = {}) => renderSurface(pres, {
|
|
5701
|
+
slide: null,
|
|
5702
|
+
layout,
|
|
5703
|
+
master: opts.master ?? false
|
|
5704
|
+
}, opts);
|
|
5705
|
+
const renderSurface = (pres, surface, opts) => {
|
|
5706
|
+
const { slide, master } = surface;
|
|
5301
5707
|
const size = getSlideSize(pres) ?? DEFAULT_SIZE;
|
|
5302
5708
|
const W = size.width;
|
|
5303
5709
|
const H = size.height;
|
|
5304
5710
|
const theme = getPresentationTheme(pres);
|
|
5305
|
-
activeColorMap = getEffectiveColorMap(slide);
|
|
5306
|
-
activeDeckTextColor = resolveDeckBodyTextColor(slide) ?? "#000000";
|
|
5307
|
-
const position = getSlides(pres).indexOf(slide);
|
|
5711
|
+
activeColorMap = getEffectiveColorMap(slide ?? surface.layout);
|
|
5712
|
+
activeDeckTextColor = resolveDeckBodyTextColor(slide ?? surface.layout) ?? "#000000";
|
|
5713
|
+
const position = slide ? getSlides(pres).indexOf(slide) : 0;
|
|
5308
5714
|
activeSlideNumber = String(getPresentationFirstSlideNumber(pres) + (position < 0 ? 0 : position));
|
|
5309
5715
|
const ctx = {
|
|
5310
5716
|
groupScale: {
|
|
@@ -5322,11 +5728,11 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
|
5322
5728
|
},
|
|
5323
5729
|
inverseGroupTransform: ""
|
|
5324
5730
|
};
|
|
5325
|
-
let bg = getSlideBackground(slide);
|
|
5731
|
+
let bg = slide ? getSlideBackground(slide) : { kind: "inherit" };
|
|
5326
5732
|
if (bg.kind === "inherit") {
|
|
5327
|
-
const layout =
|
|
5733
|
+
const layout = surface.layout;
|
|
5328
5734
|
if (layout) {
|
|
5329
|
-
const layoutBg = getSlideLayoutBackground(layout);
|
|
5735
|
+
const layoutBg = master ? { kind: "inherit" } : getSlideLayoutBackground(layout);
|
|
5330
5736
|
if (layoutBg.kind !== "inherit") bg = layoutBg;
|
|
5331
5737
|
else {
|
|
5332
5738
|
const masterBg = getSlideMasterBackground(pres, layout);
|
|
@@ -5341,11 +5747,11 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
|
5341
5747
|
let bgGradientDefs = "";
|
|
5342
5748
|
if (bg.kind === "solid") bgColor = resolveColor(bg.color, theme, "#FFFFFF");
|
|
5343
5749
|
else if (bg.kind === "gradient") {
|
|
5344
|
-
let grad = getSlideBackgroundGradientFill(slide);
|
|
5750
|
+
let grad = slide ? getSlideBackgroundGradientFill(slide) : null;
|
|
5345
5751
|
if (!grad) {
|
|
5346
|
-
const layout =
|
|
5752
|
+
const layout = surface.layout;
|
|
5347
5753
|
if (layout) {
|
|
5348
|
-
grad = getSlideLayoutBackgroundGradientFill(layout);
|
|
5754
|
+
grad = master ? null : getSlideLayoutBackgroundGradientFill(layout);
|
|
5349
5755
|
if (!grad) grad = getSlideMasterBackgroundGradientFill(pres, layout);
|
|
5350
5756
|
}
|
|
5351
5757
|
}
|
|
@@ -5355,11 +5761,11 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
|
5355
5761
|
bgGradient = `<rect width="${E(W)}" height="${E(H)}" fill="${built.fillAttr}"/>`;
|
|
5356
5762
|
}
|
|
5357
5763
|
} else if (bg.kind === "pattern") {
|
|
5358
|
-
let pat = getSlideBackgroundPatternFill(pres, slide);
|
|
5764
|
+
let pat = slide ? getSlideBackgroundPatternFill(pres, slide) : null;
|
|
5359
5765
|
if (!pat) {
|
|
5360
|
-
const layout =
|
|
5766
|
+
const layout = surface.layout;
|
|
5361
5767
|
if (layout) {
|
|
5362
|
-
pat = getSlideLayoutBackgroundPatternFill(pres, layout);
|
|
5768
|
+
pat = master ? null : getSlideLayoutBackgroundPatternFill(pres, layout);
|
|
5363
5769
|
if (!pat) pat = getSlideMasterBackgroundPatternFill(pres, layout);
|
|
5364
5770
|
}
|
|
5365
5771
|
}
|
|
@@ -5371,23 +5777,23 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
|
5371
5777
|
} else if (theme && bg.kind === "inherit") bgColor = normalizeHex(theme.light1);
|
|
5372
5778
|
let bgImage = "";
|
|
5373
5779
|
if (bg.kind === "image") {
|
|
5374
|
-
let bytes = getSlideBackgroundImageBytes(slide);
|
|
5780
|
+
let bytes = slide ? getSlideBackgroundImageBytes(slide) : null;
|
|
5375
5781
|
if (!bytes && pres) {
|
|
5376
|
-
const layout =
|
|
5782
|
+
const layout = surface.layout;
|
|
5377
5783
|
if (layout) {
|
|
5378
|
-
bytes = getSlideLayoutBackgroundImageBytes(pres, layout);
|
|
5784
|
+
bytes = master ? null : getSlideLayoutBackgroundImageBytes(pres, layout);
|
|
5379
5785
|
if (!bytes) bytes = getSlideMasterBackgroundImageBytes(pres, layout);
|
|
5380
5786
|
}
|
|
5381
5787
|
}
|
|
5382
5788
|
if (bytes) {
|
|
5383
5789
|
const fmt = detectImageFormatLocal(bytes);
|
|
5384
5790
|
const dataUrl = `data:${fmt ? imageMime[fmt] ?? "image/png" : "image/png"};base64,${u8ToBase64(bytes)}`;
|
|
5385
|
-
const layout = getSlideBackgroundImageFillLayout(slide);
|
|
5386
|
-
const crop = getSlideBackgroundImageCrop(slide);
|
|
5791
|
+
const layout = slide ? getSlideBackgroundImageFillLayout(slide) : null;
|
|
5792
|
+
const crop = slide ? getSlideBackgroundImageCrop(slide) : null;
|
|
5387
5793
|
const cropLeft = crop?.left ?? 0, cropTop = crop?.top ?? 0;
|
|
5388
5794
|
const cropRight = crop?.right ?? 0, cropBottom = crop?.bottom ?? 0;
|
|
5389
|
-
const opacity = getSlideBackgroundImageOpacity(slide) ?? 1;
|
|
5390
|
-
const intrinsic = layout?.mode === "tile" ? getSlideBackgroundImageIntrinsicSize(slide) : null;
|
|
5795
|
+
const opacity = (slide && getSlideBackgroundImageOpacity(slide)) ?? 1;
|
|
5796
|
+
const intrinsic = layout?.mode === "tile" && slide ? getSlideBackgroundImageIntrinsicSize(slide) : null;
|
|
5391
5797
|
if (layout?.mode === "tile" && intrinsic) {
|
|
5392
5798
|
const pattern = imageTilePattern(dataUrl, layout, intrinsic, 0, 0, W, H, cropLeft, cropTop, cropRight, cropBottom);
|
|
5393
5799
|
bgImage = `${pattern.defs}<rect width="${E(W)}" height="${E(H)}" fill="${pattern.fill}" opacity="${opacity}"/>`;
|
|
@@ -5408,10 +5814,11 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
|
5408
5814
|
}
|
|
5409
5815
|
}
|
|
5410
5816
|
let layoutBgShapes = "";
|
|
5411
|
-
const layoutForBg =
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
const
|
|
5817
|
+
const layoutForBg = surface.layout;
|
|
5818
|
+
const paintBackground = opts.background ?? true;
|
|
5819
|
+
if (paintBackground && layoutForBg && !(slide && isSlideBackgroundGraphicsHidden(slide))) try {
|
|
5820
|
+
const masterShapes = topLevelShapes(!master && isSlideLayoutBackgroundGraphicsHidden(layoutForBg) ? [] : getSlideMasterShapes(pres, layoutForBg), { dropPlaceholders: true });
|
|
5821
|
+
const layoutShapes = topLevelShapes(master ? [] : getSlideLayoutShapes(pres, layoutForBg), { dropPlaceholders: true });
|
|
5415
5822
|
const bgCtx = {
|
|
5416
5823
|
...ctx,
|
|
5417
5824
|
ownShapes: false
|
|
@@ -5420,12 +5827,12 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
|
5420
5827
|
} catch {
|
|
5421
5828
|
layoutBgShapes = "";
|
|
5422
5829
|
}
|
|
5423
|
-
const shapesSvg = topLevelShapes(getSlideShapes(slide), { dropPlaceholders: false }).map((s) => renderShape(s, pres, theme, ctx)).join("");
|
|
5830
|
+
const shapesSvg = topLevelShapes(slide ? getSlideShapes(slide) : [], { dropPlaceholders: false }).map((s) => renderShape(s, pres, theme, ctx)).join("");
|
|
5424
5831
|
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
5832
|
return [
|
|
5426
5833
|
`<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
5834
|
bgGradientDefs,
|
|
5428
|
-
ctx.background.id ? `<defs><g id="${ctx.background.id}">${backgroundSvg}</g></defs
|
|
5835
|
+
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
5836
|
layoutBgShapes,
|
|
5430
5837
|
shapesSvg,
|
|
5431
5838
|
"</svg>"
|
|
@@ -5601,7 +6008,7 @@ const auditTextLayout = (pres, options = {}) => {
|
|
|
5601
6008
|
*
|
|
5602
6009
|
* `1` for a shape with no `<a:normAutofit>`: `<a:noAutofit>` overflows and
|
|
5603
6010
|
* `<a:spAutoFit>` grows the box instead, so neither shrinks the text. A shape
|
|
5604
|
-
* with a baked `fontScale` reports that, unchanged — it is what
|
|
6011
|
+
* with a baked `fontScale` reports that, unchanged — it is what the reference desktop app
|
|
5605
6012
|
* itself applied.
|
|
5606
6013
|
*/
|
|
5607
6014
|
function shapeAutoFitScale(pres, shape, options = {}) {
|
|
@@ -5634,6 +6041,6 @@ function shapeTextAnchorOffset(pres, shape, options = {}) {
|
|
|
5634
6041
|
};
|
|
5635
6042
|
}
|
|
5636
6043
|
//#endregion
|
|
5637
|
-
export {
|
|
6044
|
+
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
6045
|
|
|
5639
|
-
//# sourceMappingURL=src-
|
|
6046
|
+
//# sourceMappingURL=src-pb3C61GD.js.map
|