44 */
55
66import { type RelEntry , resolveRelTarget } from '../parser/rel-parser'
7- import { emuToPx } from '../parser/units'
87import { parseXml , type SafeXmlNode } from '../parser/xml-parser'
98import { parseBaseProps } from './nodes/base-node'
109import { type ChartNodeData , parseChartNode } from './nodes/chart-node'
@@ -178,28 +177,10 @@ function parseDiagramFrame(
178177 return undefined
179178}
180179
181- /**
182- * Read xfrm off/ext from a shape-like node (dsp:sp uses dsp:spPr > a:xfrm).
183- */
184- function readShapeBounds ( node : SafeXmlNode ) : { x : number ; y : number ; w : number ; h : number } | null {
185- const spPr = node . child ( 'spPr' )
186- if ( ! spPr . exists ( ) ) return null
187- const xfrm = spPr . child ( 'xfrm' )
188- if ( ! xfrm . exists ( ) ) return null
189- const off = xfrm . child ( 'off' )
190- const ext = xfrm . child ( 'ext' )
191- const x = emuToPx ( off . numAttr ( 'x' ) ?? 0 )
192- const y = emuToPx ( off . numAttr ( 'y' ) ?? 0 )
193- const w = emuToPx ( ext . numAttr ( 'cx' ) ?? 0 )
194- const h = emuToPx ( ext . numAttr ( 'cy' ) ?? 0 )
195- return { x, y, w, h }
196- }
197-
198180/**
199181 * Build a GroupNodeData from a diagram drawing XML string.
200182 * Diagram drawings use dsp: namespace (drawingml 2008); structure is dsp:drawing > dsp:spTree > dsp:sp.
201- * Diagram shapes use their own coordinate space; we compute childOffset/childExtent from
202- * the actual bounding box of all shapes so remapping preserves layout and spacing.
183+ * Diagram shapes are positioned in the graphicFrame's own coordinate space.
203184 */
204185function buildDiagramGroup (
205186 base : ReturnType < typeof parseBaseProps > ,
@@ -218,64 +199,25 @@ function buildDiagramGroup(
218199 }
219200
220201 const CHILD_TAGS = new Set ( [ 'sp' , 'pic' , 'grpSp' , 'graphicFrame' , 'cxnSp' ] )
221- // Circular presets need isotropic scaling; tree/org-chart style diagrams should keep native axis scaling.
222- const CIRCULAR_PRESETS = new Set ( [ 'pie' , 'arc' , 'blockArc' , 'donut' , 'circularArrow' ] )
223202 const children : SafeXmlNode [ ] = [ ]
224- let minX = Number . POSITIVE_INFINITY
225- let minY = Number . POSITIVE_INFINITY
226- let maxRight = Number . NEGATIVE_INFINITY
227- let maxBottom = Number . NEGATIVE_INFINITY
228- let hasCircularPreset = false
229203
230204 for ( const child of spTree . allChildren ( ) ) {
231205 if ( CHILD_TAGS . has ( child . localName ) ) {
232206 children . push ( child )
233- const prst = child . child ( 'spPr' ) . child ( 'prstGeom' ) . attr ( 'prst' )
234- if ( prst && CIRCULAR_PRESETS . has ( prst ) ) hasCircularPreset = true
235- const b = readShapeBounds ( child )
236- if ( b ) {
237- minX = Math . min ( minX , b . x )
238- minY = Math . min ( minY , b . y )
239- maxRight = Math . max ( maxRight , b . x + b . w )
240- maxBottom = Math . max ( maxBottom , b . y + b . h )
241- }
242207 }
243208 }
244209
245- const hasBounds =
246- minX !== Number . POSITIVE_INFINITY &&
247- minY !== Number . POSITIVE_INFINITY &&
248- maxRight !== Number . NEGATIVE_INFINITY &&
249- maxBottom !== Number . NEGATIVE_INFINITY
250-
251- // Check if shapes extend significantly beyond the diagram frame (negative offsets or huge extents).
252- // When decorative shapes (e.g. blockArc) have large negative coordinates, including them in
253- // the bounding box distorts the layout. Fall back to frame-based coordinates in that case.
254- const bboxSpansNegative = hasBounds && ( minX < 0 || minY < 0 )
255- const bboxMuchLargerThanFrame =
256- hasBounds && ( maxRight - minX > base . size . w * 2 || maxBottom - minY > base . size . h * 2 )
257- const useFrameCoords = bboxSpansNegative || bboxMuchLargerThanFrame
258-
259210 // Use the graphicFrame's own dimensions as the child coordinate space.
260211 // Diagram shapes are positioned in the frame's coordinate space (EMU converted to px).
261212 // Using frame dimensions gives a 1:1 scale, preserving original positions and sizes.
262213 // This avoids enlarging shapes when the bounding box is smaller than the frame.
263- let extentW = Math . max ( 1 , base . size . w )
264- let extentH = Math . max ( 1 , base . size . h )
265- let offX = 0
266- let offY = 0
267-
268- if ( ! hasBounds ) {
269- extentW = Math . max ( 1 , base . size . w )
270- extentH = Math . max ( 1 , base . size . h )
271- offX = 0
272- offY = 0
273- }
214+ const extentW = Math . max ( 1 , base . size . w )
215+ const extentH = Math . max ( 1 , base . size . h )
274216
275217 return {
276218 ...base ,
277219 nodeType : 'group' ,
278- childOffset : { x : offX , y : offY } ,
220+ childOffset : { x : 0 , y : 0 } ,
279221 childExtent : { w : extentW , h : extentH } ,
280222 children,
281223 }
0 commit comments