返回 oh-my-ppt
shape-view-box.ts
根目录 / src / main / io / pptx-import / shape-view-box.ts
1 import type { SvgPathBounds } from '@arcsin1/pptx-ooxml-geometry'
2 import type { PptxXmlShapeMetadata } from './xml-shape-metadata'
3
4 const PRESET_SHAPES_WITH_LOCAL_VIEWBOX = new Set([
5 'arc',
6 'blockarc',
7 'chevron',
8 'curvedleftarrow',
9 'curvedrightarrow',
10 'donut',
11 'ellipse',
12 'line',
13 'parallelogram',
14 'pie',
15 'rect',
16 'round1rect',
17 'roundrect',
18 'straightconnector1',
19 'trapezoid',
20 'triangle'
21 ])
22
23 const clampNumber = (value: unknown, fallback = 0): number => {
24 const number = Number(value)
25 return Number.isFinite(number) ? number : fallback
26 }
27
28 /**
29 * Adapts a geometry-only SVG bound to the imported element frame. This stays
30 * in the application because it relies on importer metadata and render policy.
31 */
32 export const getSvgShapeViewBox = (
33 element: Record<string, unknown>,
34 pathBounds: SvgPathBounds,
35 pathData: string,
36 xmlShape?: PptxXmlShapeMetadata
37 ): SvgPathBounds => {
38 const width = clampNumber(element.width)
39 const height = clampNumber(element.height)
40 const xmlPreset = xmlShape?.preset.toLowerCase() || ''
41 const epsilon = 0.5
42 const pathMaxX = pathBounds.minX + pathBounds.width
43 const pathMaxY = pathBounds.minY + pathBounds.height
44 const pathFitsElement =
45 pathBounds.minX >= -epsilon &&
46 pathBounds.minY >= -epsilon &&
47 pathMaxX <= width + epsilon &&
48 pathMaxY <= height + epsilon
49 if (width > 0 && height > 0 && xmlShape?.isCustomGeometry && pathFitsElement) {
50 return {
51 minX: 0,
52 minY: 0,
53 width: Math.max(0.0001, width),
54 height: Math.max(0.0001, height)
55 }
56 }
57 if (width > 0 && height > 0 && PRESET_SHAPES_WITH_LOCAL_VIEWBOX.has(xmlPreset)) {
58 return {
59 minX: 0,
60 minY: 0,
61 width: Math.max(0.0001, width),
62 height: Math.max(0.0001, height)
63 }
64 }
65 const pathFillsElement = pathBounds.width >= width * 0.9 && pathBounds.height >= height * 0.9
66 const pathHasInteriorOffset = pathBounds.minX > epsilon || pathBounds.minY > epsilon
67 const isArcPath = /(?:^|[\s,])A[\s,]/i.test(pathData)
68 if (
69 width > 0 &&
70 height > 0 &&
71 pathFitsElement &&
72 (pathFillsElement || pathHasInteriorOffset || isArcPath)
73 ) {
74 return {
75 minX: 0,
76 minY: 0,
77 width: Math.max(0.0001, width),
78 height: Math.max(0.0001, height)
79 }
80 }
81 return pathBounds
82 }
83
83 lines TYPESCRIPT