@office-kit/pptx-preview 0.6.2 → 0.8.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 +51 -0
- package/dist/index-B6ISDjnu.d.ts +93 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/node.d.ts +36 -3
- package/dist/node.js +62 -15
- package/dist/node.js.map +1 -1
- package/dist/{src-CVYeIBFN.js → src-D95l1T9U.js} +212 -17
- package/dist/src-D95l1T9U.js.map +1 -0
- package/package.json +1 -1
- package/dist/index-BNnucizH.d.ts +0 -45
- package/dist/src-CVYeIBFN.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getEffectiveColorMap, getGroupChildren, getGroupTransform, getParagraphAlignment, getParagraphBullet, getParagraphBulletImageBytes, getParagraphBulletStyle, getParagraphIndent, getParagraphLevel, getParagraphPropertiesEffective, getParagraphSpacing, getPresentationFonts, getPresentationTheme, getShapeAdjustValues, getShapeAltTitle, getShapeBodyPrEffective, getShapeBoundsResolved, getShapeChartSpec, getShapeClickAction, getShapeCustomGeometry, getShapeDescription, getShapeEffectsEffective, getShapeFillColorResolved, getShapeFillEffective, getShapeFlip, getShapeGradientFill, getShapeGradientFillEffective, getShapeHyperlink, getShapeHyperlinkTooltip, getShapeImageBiLevelThreshold, getShapeImageBrightness, getShapeImageBytes, getShapeImageContrast, getShapeImageCrop, getShapeImageDuotone, getShapeImageFillBytes, getShapeImageFormat, getShapeImageLinkUrl, getShapeImageOpacity, getShapeImagePartName, getShapeKind, getShapeName, getShapeParagraphCount, getShapeParagraphElements, getShapePatternFill, getShapePlaceholderType, getShapePreset, getShapeRotation, getShapeRunClickAction, getShapeRunFormat, getShapeRunFormatEffective, getShapeRunHyperlink, getShapeRunHyperlinkTooltip, getShapeStrokeArrow, getShapeStrokeCap, getShapeStrokeColorResolved, getShapeStrokeCompound, getShapeStrokeDash, getShapeStrokeEffective, getShapeStrokeJoin, getShapeTextAnchor, getShapeTextAutoFitParams, getShapeTextBodyRotationDeg, getShapeTextColumns, getShapeTextDirection, getShapeTextMargins, getShapeXmlString, getSlideBackground, getSlideBackgroundGradientFill, getSlideBackgroundImageBytes, getSlideBackgroundPatternFill, getSlideIndex, getSlideLayout, getSlideLayoutBackground, getSlideLayoutBackgroundGradientFill, getSlideLayoutBackgroundImageBytes, getSlideLayoutBackgroundPatternFill, getSlideLayoutShapes, getSlideMasterBackground, getSlideMasterBackgroundGradientFill, getSlideMasterBackgroundImageBytes, getSlideMasterBackgroundPatternFill, getSlideMasterShapes, getSlideShapes, getSlideSize, getTableCellAnchor, getTableCellBorders, getTableCellFill, getTableCellMargins, getTableCellParagraphs, getTableCellSpan, getTableCells, getTableColumnWidths, getTableDimensions, getTableRowHeights, getTableStyleFlags, isChartShape, isParagraphBulletPicture, isShapeImageGrayscale, isShapePlaceholder, isShapeTextBox, isTableShape, resolveDeckBodyTextColor } from "@office-kit/pptx";
|
|
1
|
+
import { getEffectiveColorMap, getGroupChildren, getGroupTransform, getParagraphAlignment, getParagraphBullet, getParagraphBulletImageBytes, getParagraphBulletStyle, getParagraphIndent, getParagraphLevel, getParagraphPropertiesEffective, getParagraphSpacing, getPresentationFonts, getPresentationTheme, getShapeAdjustValues, getShapeAltTitle, getShapeBodyPrEffective, getShapeBoundsResolved, getShapeChartSpec, getShapeClickAction, getShapeCustomGeometry, getShapeDescription, getShapeEffectsEffective, getShapeFillColorResolved, getShapeFillEffective, getShapeFlip, getShapeGradientFill, getShapeGradientFillEffective, getShapeHyperlink, getShapeHyperlinkTooltip, getShapeImageBiLevelThreshold, getShapeImageBrightness, getShapeImageBytes, getShapeImageContrast, getShapeImageCrop, getShapeImageDuotone, getShapeImageFillBytes, getShapeImageFormat, getShapeImageLinkUrl, getShapeImageOpacity, getShapeImagePartName, getShapeKind, getShapeName, getShapeParagraphCount, getShapeParagraphElements, getShapePatternFill, getShapePlaceholderType, getShapePreset, getShapeRotation, getShapeRunClickAction, getShapeRunFormat, getShapeRunFormatEffective, getShapeRunHyperlink, getShapeRunHyperlinkTooltip, getShapeStrokeArrow, getShapeStrokeCap, getShapeStrokeColorResolved, getShapeStrokeCompound, getShapeStrokeDash, getShapeStrokeEffective, getShapeStrokeJoin, getShapeTextAnchor, getShapeTextAutoFitParams, getShapeTextBodyRotationDeg, getShapeTextColumns, getShapeTextDirection, getShapeTextMargins, getShapeXmlString, getSlideBackground, getSlideBackgroundGradientFill, getSlideBackgroundImageBytes, getSlideBackgroundPatternFill, getSlideIndex, getSlideLayout, getSlideLayoutBackground, getSlideLayoutBackgroundGradientFill, getSlideLayoutBackgroundImageBytes, getSlideLayoutBackgroundPatternFill, getSlideLayoutShapes, getSlideMasterBackground, getSlideMasterBackgroundGradientFill, getSlideMasterBackgroundImageBytes, getSlideMasterBackgroundPatternFill, getSlideMasterShapes, getSlideShapes, getSlideSize, getSlides, getTableCellAnchor, getTableCellBorders, getTableCellFill, getTableCellMargins, getTableCellParagraphs, getTableCellSpan, getTableCells, getTableColumnWidths, getTableDimensions, getTableRowHeights, getTableStyleFlags, isChartShape, isParagraphBulletPicture, isShapeImageGrayscale, isShapePlaceholder, isShapeTextBox, isTableShape, resolveDeckBodyTextColor } from "@office-kit/pptx";
|
|
2
2
|
//#region src/text-layout.ts
|
|
3
3
|
const SANS = "Carlito";
|
|
4
4
|
const SERIF = "Caladea";
|
|
@@ -91,7 +91,8 @@ const layoutCore = (input, measure) => {
|
|
|
91
91
|
const buildLines = (contentLeft, contentRight) => {
|
|
92
92
|
const lines = [];
|
|
93
93
|
let cursorY = 0;
|
|
94
|
-
for (
|
|
94
|
+
for (let paraIndex = 0; paraIndex < input.paragraphs.length; paraIndex++) {
|
|
95
|
+
const para = input.paragraphs[paraIndex];
|
|
95
96
|
cursorY += para.spcBefPx;
|
|
96
97
|
const wrapLeft = contentLeft + para.marLpx;
|
|
97
98
|
const wrapRight = contentRight - para.marRpx;
|
|
@@ -154,6 +155,7 @@ const layoutCore = (input, measure) => {
|
|
|
154
155
|
const lineLeft = bullet ? Math.max(wrapLeft, firstLeft + bulletLead) : (isFirst ? firstLeft : wrapLeft) + bulletLead;
|
|
155
156
|
const line = {
|
|
156
157
|
tokens: toks,
|
|
158
|
+
paraIndex,
|
|
157
159
|
ascent,
|
|
158
160
|
descent,
|
|
159
161
|
lineGap,
|
|
@@ -1917,7 +1919,7 @@ const buildSvgTextInput = (a) => {
|
|
|
1917
1919
|
underline: fmt?.underline ?? true
|
|
1918
1920
|
};
|
|
1919
1921
|
}
|
|
1920
|
-
const family = substituteFamily(fmt?.font ?? a.themeFace);
|
|
1922
|
+
const family = (a.resolveFamily ?? substituteFamily)(fmt?.font ?? a.themeFace);
|
|
1921
1923
|
const sizePx = run.sizePt * scale * PX_PER_PT;
|
|
1922
1924
|
const fillHex = fmt?.color !== void 0 && fmt.color !== null ? resolveColor(fmt.color, a.theme, "#000000") : a.defaultColor;
|
|
1923
1925
|
const superSub = fmt?.baseline !== void 0 && fmt.baseline !== 0 ? fmt.baseline > 0 ? 1 : -1 : 0;
|
|
@@ -2018,7 +2020,7 @@ const buildBullet = (a, para, pi) => {
|
|
|
2018
2020
|
const fillHex = para.bulletDetail.color ? resolveColor(para.bulletDetail.color, a.theme, "#000000") : a.defaultColor;
|
|
2019
2021
|
return {
|
|
2020
2022
|
text: char,
|
|
2021
|
-
family: substituteFamily(para.bulletDetail.font ?? DEFAULT_BULLET_FONT),
|
|
2023
|
+
family: (a.resolveFamily ?? substituteFamily)(para.bulletDetail.font ?? DEFAULT_BULLET_FONT),
|
|
2022
2024
|
sizePx,
|
|
2023
2025
|
fillHex,
|
|
2024
2026
|
...para.bulletImageHref ? { imageHref: para.bulletImageHref } : {}
|
|
@@ -2059,14 +2061,14 @@ const presetTextRect = (preset) => {
|
|
|
2059
2061
|
default: return null;
|
|
2060
2062
|
}
|
|
2061
2063
|
};
|
|
2062
|
-
const
|
|
2064
|
+
const resolveTextBodyModel = (pres, shape, bounds, theme, phType, measure, defaultColor) => {
|
|
2063
2065
|
let paragraphCount;
|
|
2064
2066
|
try {
|
|
2065
2067
|
paragraphCount = getShapeParagraphCount(shape);
|
|
2066
2068
|
} catch {
|
|
2067
|
-
return
|
|
2069
|
+
return null;
|
|
2068
2070
|
}
|
|
2069
|
-
if (paragraphCount === 0) return
|
|
2071
|
+
if (paragraphCount === 0) return null;
|
|
2070
2072
|
const defaultPt = placeholderDefaultPt(phType);
|
|
2071
2073
|
const themeFonts = getPresentationFonts(pres);
|
|
2072
2074
|
const themeFace = phType === "title" || phType === "ctrTitle" ? themeFonts?.majorLatin ?? null : themeFonts?.minorLatin ?? null;
|
|
@@ -2111,7 +2113,7 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx) => {
|
|
|
2111
2113
|
innerW = rectW;
|
|
2112
2114
|
innerH = rectH;
|
|
2113
2115
|
}
|
|
2114
|
-
if (innerW <= 0 || innerH <= 0) return
|
|
2116
|
+
if (innerW <= 0 || innerH <= 0) return null;
|
|
2115
2117
|
const svgTextRect = (v) => v === "none" || v === "upright" ? {
|
|
2116
2118
|
x: innerX,
|
|
2117
2119
|
y: innerY,
|
|
@@ -2249,7 +2251,7 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx) => {
|
|
|
2249
2251
|
indent
|
|
2250
2252
|
});
|
|
2251
2253
|
}
|
|
2252
|
-
if (!hasAnyText) return
|
|
2254
|
+
if (!hasAnyText) return null;
|
|
2253
2255
|
const authoredAutofit = getShapeTextAutoFitParams(shape);
|
|
2254
2256
|
let autoFitScale = authoredAutofit?.fontScale ?? 1;
|
|
2255
2257
|
let lineHeightScale = 1 - (authoredAutofit?.lnSpcReduction ?? 0);
|
|
@@ -2320,14 +2322,14 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx) => {
|
|
|
2320
2322
|
lineHeightScale,
|
|
2321
2323
|
defaultPt,
|
|
2322
2324
|
themeFace,
|
|
2323
|
-
defaultColor
|
|
2325
|
+
defaultColor,
|
|
2324
2326
|
anchor: anchor === "center" || anchor === "bottom" ? anchor : "top",
|
|
2325
2327
|
wrap: effectiveBody.wrap !== "none",
|
|
2326
2328
|
innerX: fitRect.x,
|
|
2327
2329
|
innerY: fitRect.y,
|
|
2328
2330
|
innerW: fitRect.w,
|
|
2329
2331
|
innerH: fitRect.h,
|
|
2330
|
-
measure
|
|
2332
|
+
measure,
|
|
2331
2333
|
vert: fitVert,
|
|
2332
2334
|
columns: fitColumns
|
|
2333
2335
|
};
|
|
@@ -2336,11 +2338,33 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx) => {
|
|
|
2336
2338
|
if (measureTextBodyHeight(buildSvgTextInput({
|
|
2337
2339
|
...fitArgsBase,
|
|
2338
2340
|
autoFitScale: s
|
|
2339
|
-
}),
|
|
2341
|
+
}), measure) <= fitBoxPx) break;
|
|
2340
2342
|
s -= AUTOFIT_STEP;
|
|
2341
2343
|
}
|
|
2342
2344
|
autoFitScale = Math.max(AUTOFIT_FLOOR, s);
|
|
2343
2345
|
}
|
|
2346
|
+
return {
|
|
2347
|
+
paraData,
|
|
2348
|
+
numberLabels,
|
|
2349
|
+
authoredAutofit,
|
|
2350
|
+
autoFitScale,
|
|
2351
|
+
lineHeightScale,
|
|
2352
|
+
defaultPt,
|
|
2353
|
+
themeFace,
|
|
2354
|
+
effectiveDefaultFont,
|
|
2355
|
+
effectiveBody,
|
|
2356
|
+
anchor,
|
|
2357
|
+
innerX,
|
|
2358
|
+
innerY,
|
|
2359
|
+
innerW,
|
|
2360
|
+
innerH,
|
|
2361
|
+
svgTextRect
|
|
2362
|
+
};
|
|
2363
|
+
};
|
|
2364
|
+
const renderTextBody = (pres, shape, bounds, theme, phType, ctx) => {
|
|
2365
|
+
const model = resolveTextBodyModel(pres, shape, bounds, theme, phType, ctx.measure, activeDeckTextColor);
|
|
2366
|
+
if (model === null) return "";
|
|
2367
|
+
const { paraData, numberLabels, authoredAutofit, autoFitScale, defaultPt, themeFace, effectiveDefaultFont, effectiveBody, anchor, innerX, innerY, innerW, innerH, svgTextRect } = model;
|
|
2344
2368
|
const paragraphs = [];
|
|
2345
2369
|
for (let pi = 0; pi < paraData.length; pi++) {
|
|
2346
2370
|
const para = paraData[pi];
|
|
@@ -4318,6 +4342,27 @@ const buildEffectsFilter = (pres, shape) => {
|
|
|
4318
4342
|
defs: `<defs><filter id="${id}" x="-25%" y="-25%" width="150%" height="150%">${primitives.join("")}</filter></defs>`
|
|
4319
4343
|
};
|
|
4320
4344
|
};
|
|
4345
|
+
const flattenedDescendantCount = (group, dropPlaceholders) => {
|
|
4346
|
+
let count = 0;
|
|
4347
|
+
for (const child of getGroupChildren(group)) {
|
|
4348
|
+
if (!dropPlaceholders || !isShapePlaceholder(child)) count += 1;
|
|
4349
|
+
if (getShapeKind(child) === "group") count += flattenedDescendantCount(child, dropPlaceholders);
|
|
4350
|
+
}
|
|
4351
|
+
return count;
|
|
4352
|
+
};
|
|
4353
|
+
const topLevelShapes = (shapes, opts) => {
|
|
4354
|
+
const out = [];
|
|
4355
|
+
let skip = 0;
|
|
4356
|
+
for (const shape of shapes) {
|
|
4357
|
+
if (skip > 0) {
|
|
4358
|
+
skip -= 1;
|
|
4359
|
+
continue;
|
|
4360
|
+
}
|
|
4361
|
+
out.push(shape);
|
|
4362
|
+
if (getShapeKind(shape) === "group") skip = flattenedDescendantCount(shape, opts.dropPlaceholders);
|
|
4363
|
+
}
|
|
4364
|
+
return out;
|
|
4365
|
+
};
|
|
4321
4366
|
const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
4322
4367
|
const size = getSlideSize(pres) ?? DEFAULT_SIZE;
|
|
4323
4368
|
const W = size.width;
|
|
@@ -4393,13 +4438,13 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
|
|
|
4393
4438
|
let layoutBgShapes = "";
|
|
4394
4439
|
const layoutForBg = getSlideLayout(slide);
|
|
4395
4440
|
if (layoutForBg) try {
|
|
4396
|
-
const masterShapes = getSlideMasterShapes(pres, layoutForBg);
|
|
4397
|
-
const layoutShapes = getSlideLayoutShapes(pres, layoutForBg);
|
|
4441
|
+
const masterShapes = topLevelShapes(getSlideMasterShapes(pres, layoutForBg), { dropPlaceholders: true });
|
|
4442
|
+
const layoutShapes = topLevelShapes(getSlideLayoutShapes(pres, layoutForBg), { dropPlaceholders: true });
|
|
4398
4443
|
layoutBgShapes = [...masterShapes, ...layoutShapes].map((s) => renderShape(s, pres, theme, ctx)).join("");
|
|
4399
4444
|
} catch {
|
|
4400
4445
|
layoutBgShapes = "";
|
|
4401
4446
|
}
|
|
4402
|
-
const shapesSvg = getSlideShapes(slide).map((s) => renderShape(s, pres, theme, ctx)).join("");
|
|
4447
|
+
const shapesSvg = topLevelShapes(getSlideShapes(slide), { dropPlaceholders: false }).map((s) => renderShape(s, pres, theme, ctx)).join("");
|
|
4403
4448
|
return [
|
|
4404
4449
|
`<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">`,
|
|
4405
4450
|
bgGradientDefs,
|
|
@@ -4421,6 +4466,156 @@ const detectImageFormatLocal = (bytes) => {
|
|
|
4421
4466
|
return null;
|
|
4422
4467
|
};
|
|
4423
4468
|
//#endregion
|
|
4424
|
-
|
|
4469
|
+
//#region src/audit.ts
|
|
4470
|
+
const DEFAULT_TOLERANCE_PX = 1;
|
|
4471
|
+
const round2 = (n) => Math.round(n * 100) / 100;
|
|
4472
|
+
function* walkShapes(shapes) {
|
|
4473
|
+
for (const shape of shapes) if (getShapeKind(shape) !== "group") yield shape;
|
|
4474
|
+
}
|
|
4475
|
+
const passthroughFamily = (family) => family ?? "Carlito";
|
|
4476
|
+
const auditShape = (pres, theme, shape, slideIndex, measure, tolerancePx, reportSoftWraps, issues) => {
|
|
4477
|
+
const kind = getShapeKind(shape);
|
|
4478
|
+
if (kind !== "shape" && kind !== "graphicFrame") return;
|
|
4479
|
+
const bounds = getShapeBoundsResolved(pres, shape);
|
|
4480
|
+
if (!bounds) return;
|
|
4481
|
+
const model = resolveTextBodyModel(pres, shape, {
|
|
4482
|
+
x: bounds.x,
|
|
4483
|
+
y: bounds.y,
|
|
4484
|
+
w: bounds.w,
|
|
4485
|
+
h: bounds.h
|
|
4486
|
+
}, theme, getShapePlaceholderType(shape), measure, "#000000");
|
|
4487
|
+
if (model === null) return;
|
|
4488
|
+
const vert = verticalLayoutOf(model.effectiveBody.vert ?? getShapeTextDirection(shape));
|
|
4489
|
+
const cols = getShapeTextColumns(shape);
|
|
4490
|
+
const columns = vert === "none" && cols && cols.count >= 2 ? {
|
|
4491
|
+
count: cols.count,
|
|
4492
|
+
gapPx: cols.gapEmu !== void 0 ? cols.gapEmu / EMU_PER_PX : 12
|
|
4493
|
+
} : null;
|
|
4494
|
+
const rect = model.svgTextRect(vert);
|
|
4495
|
+
if (rect.w <= 0 || rect.h <= 0) return;
|
|
4496
|
+
const autoFitScale = model.authoredAutofit ? model.autoFitScale : 1;
|
|
4497
|
+
const input = buildSvgTextInput({
|
|
4498
|
+
pres,
|
|
4499
|
+
shape,
|
|
4500
|
+
theme,
|
|
4501
|
+
paraData: model.paraData,
|
|
4502
|
+
numberLabels: model.numberLabels,
|
|
4503
|
+
autoFitScale,
|
|
4504
|
+
lineHeightScale: model.lineHeightScale,
|
|
4505
|
+
defaultPt: model.defaultPt,
|
|
4506
|
+
themeFace: model.themeFace,
|
|
4507
|
+
defaultColor: "#000000",
|
|
4508
|
+
anchor: model.anchor,
|
|
4509
|
+
wrap: model.effectiveBody.wrap !== "none",
|
|
4510
|
+
innerX: rect.x,
|
|
4511
|
+
innerY: rect.y,
|
|
4512
|
+
innerW: rect.w,
|
|
4513
|
+
innerH: rect.h,
|
|
4514
|
+
measure,
|
|
4515
|
+
vert,
|
|
4516
|
+
columns,
|
|
4517
|
+
resolveFamily: passthroughFamily
|
|
4518
|
+
});
|
|
4519
|
+
const core = layoutCore(input, measure);
|
|
4520
|
+
const frame = vert === "cw90" || vert === "cw270" ? {
|
|
4521
|
+
x: input.boxXpx + input.boxWpx / 2 - input.boxHpx / 2,
|
|
4522
|
+
y: input.boxYpx + input.boxHpx / 2 - input.boxWpx / 2,
|
|
4523
|
+
w: input.boxHpx,
|
|
4524
|
+
h: input.boxWpx
|
|
4525
|
+
} : {
|
|
4526
|
+
x: input.boxXpx,
|
|
4527
|
+
y: input.boxYpx,
|
|
4528
|
+
w: input.boxWpx,
|
|
4529
|
+
h: input.boxHpx
|
|
4530
|
+
};
|
|
4531
|
+
let inkTop = Infinity;
|
|
4532
|
+
let inkBottom = -Infinity;
|
|
4533
|
+
let overflowX = 0;
|
|
4534
|
+
let anyInk = false;
|
|
4535
|
+
for (const p of core.placements) {
|
|
4536
|
+
const toks = [...p.line.tokens];
|
|
4537
|
+
while (toks.length > 0 && (toks[toks.length - 1].isSpace || toks[toks.length - 1].isBreak)) toks.pop();
|
|
4538
|
+
if (!toks.some((t) => !t.isSpace && !t.isBreak)) continue;
|
|
4539
|
+
anyInk = true;
|
|
4540
|
+
let lineW = 0;
|
|
4541
|
+
for (const t of toks) if (!t.isBreak) lineW += t.width;
|
|
4542
|
+
const left = p.line.textAnchor === "middle" ? p.line.anchorX - lineW / 2 : p.line.textAnchor === "end" ? p.line.anchorX - lineW : p.line.anchorX;
|
|
4543
|
+
const over = Math.max(frame.x - (left + p.dx), left + p.dx + lineW - (frame.x + frame.w));
|
|
4544
|
+
if (over > overflowX) overflowX = over;
|
|
4545
|
+
inkTop = Math.min(inkTop, p.baselineY - p.line.ascent);
|
|
4546
|
+
inkBottom = Math.max(inkBottom, p.baselineY + p.line.descent);
|
|
4547
|
+
}
|
|
4548
|
+
if (!anyInk) return;
|
|
4549
|
+
const approximate = detectApproximate(input.paragraphs, measure);
|
|
4550
|
+
const slideRef = {
|
|
4551
|
+
slideIndex,
|
|
4552
|
+
shapeName: getShapeName(shape),
|
|
4553
|
+
approximate
|
|
4554
|
+
};
|
|
4555
|
+
if (overflowX > tolerancePx) issues.push({
|
|
4556
|
+
...slideRef,
|
|
4557
|
+
kind: "overflow-x",
|
|
4558
|
+
overflowPx: round2(overflowX)
|
|
4559
|
+
});
|
|
4560
|
+
const overflowY = Math.max(frame.y - inkTop, inkBottom - (frame.y + frame.h));
|
|
4561
|
+
if (overflowY > tolerancePx) issues.push({
|
|
4562
|
+
...slideRef,
|
|
4563
|
+
kind: "overflow-y",
|
|
4564
|
+
overflowPx: round2(overflowY)
|
|
4565
|
+
});
|
|
4566
|
+
if (reportSoftWraps && vert === "none") {
|
|
4567
|
+
const lineCounts = /* @__PURE__ */ new Map();
|
|
4568
|
+
for (const p of core.placements) lineCounts.set(p.line.paraIndex, (lineCounts.get(p.line.paraIndex) ?? 0) + 1);
|
|
4569
|
+
input.paragraphs.forEach((para, pi) => {
|
|
4570
|
+
const breaks = para.pieces.reduce((n, pc) => n + (pc.isBreak ? 1 : 0), 0);
|
|
4571
|
+
const extraLines = (lineCounts.get(pi) ?? 0) - 1 - breaks;
|
|
4572
|
+
if (extraLines > 0) issues.push({
|
|
4573
|
+
...slideRef,
|
|
4574
|
+
kind: "soft-wrap",
|
|
4575
|
+
paragraphIndex: pi,
|
|
4576
|
+
extraLines
|
|
4577
|
+
});
|
|
4578
|
+
});
|
|
4579
|
+
}
|
|
4580
|
+
};
|
|
4581
|
+
const detectApproximate = (paragraphs, measure) => {
|
|
4582
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4583
|
+
for (const para of paragraphs) for (const piece of para.pieces) {
|
|
4584
|
+
if (piece.isBreak || piece.text === "") continue;
|
|
4585
|
+
const key = `${piece.family}|${piece.sizePx}|${piece.bold}|${piece.italic}|${piece.text}`;
|
|
4586
|
+
if (seen.has(key)) continue;
|
|
4587
|
+
seen.add(key);
|
|
4588
|
+
const spec = {
|
|
4589
|
+
family: piece.family,
|
|
4590
|
+
sizePx: piece.sizePx,
|
|
4591
|
+
bold: piece.bold,
|
|
4592
|
+
italic: piece.italic,
|
|
4593
|
+
letterSpacingPx: piece.letterSpacingPx
|
|
4594
|
+
};
|
|
4595
|
+
const r = measure(piece.text, spec);
|
|
4596
|
+
if (r.approximate === true || r.ascentPx === void 0) return true;
|
|
4597
|
+
}
|
|
4598
|
+
return false;
|
|
4599
|
+
};
|
|
4600
|
+
/**
|
|
4601
|
+
* Measures every text body in the deck and reports text that escapes its box
|
|
4602
|
+
* (`overflow-x` / `overflow-y`) and, opt-in, paragraphs that soft-wrap
|
|
4603
|
+
* (`soft-wrap`, 段落ち).
|
|
4604
|
+
*
|
|
4605
|
+
* Table cell text is not audited (v1 covers shape text bodies, including
|
|
4606
|
+
* placeholders and shapes inside groups).
|
|
4607
|
+
*/
|
|
4608
|
+
const auditTextLayout = (pres, options = {}) => {
|
|
4609
|
+
const measure = options.measureText ?? defaultMeasurer;
|
|
4610
|
+
const tolerancePx = options.tolerancePx ?? DEFAULT_TOLERANCE_PX;
|
|
4611
|
+
const reportSoftWraps = options.reportSoftWraps ?? false;
|
|
4612
|
+
const theme = getPresentationTheme(pres);
|
|
4613
|
+
const issues = [];
|
|
4614
|
+
const slides = getSlides(pres);
|
|
4615
|
+
for (let slideIndex = 0; slideIndex < slides.length; slideIndex++) for (const shape of walkShapes(getSlideShapes(slides[slideIndex]))) auditShape(pres, theme, shape, slideIndex, measure, tolerancePx, reportSoftWraps, issues);
|
|
4616
|
+
return issues;
|
|
4617
|
+
};
|
|
4618
|
+
//#endregion
|
|
4619
|
+
export { MONO as a, TIMES as c, substituteFamily as d, AVG_GLYPH_W_RATIO$1 as i, defaultMeasurer as l, renderSlideSvg as n, SANS as o, ARIAL as r, SERIF as s, auditTextLayout as t, isCjk as u };
|
|
4425
4620
|
|
|
4426
|
-
//# sourceMappingURL=src-
|
|
4621
|
+
//# sourceMappingURL=src-D95l1T9U.js.map
|