(grpNode: SafeXmlNode)
| 21 | * Parse a group shape XML node (`p:grpSp`) into GroupNodeData. |
| 22 | */ |
| 23 | export function parseGroupNode(grpNode: SafeXmlNode): GroupNodeData { |
| 24 | const base = parseBaseProps(grpNode) |
| 25 | |
| 26 | // --- Child coordinate space from grpSpPr > a:xfrm --- |
| 27 | // OOXML: when chOff/chExt omitted, child box equals group box (chOff=0,0, chExt=ext). |
| 28 | const grpSpPr = grpNode.child('grpSpPr') |
| 29 | const xfrm = grpSpPr.child('xfrm') |
| 30 | const chOff = xfrm.child('chOff') |
| 31 | const chExt = xfrm.child('chExt') |
| 32 | |
| 33 | const childOffset: Position = chOff.exists() |
| 34 | ? { x: emuToPx(chOff.numAttr('x') ?? 0), y: emuToPx(chOff.numAttr('y') ?? 0) } |
| 35 | : { x: 0, y: 0 } |
| 36 | |
| 37 | const childExtent: Size = (() => { |
| 38 | if (!chExt.exists()) return { w: base.size.w, h: base.size.h } |
| 39 | const cx = chExt.numAttr('cx') |
| 40 | const cy = chExt.numAttr('cy') |
| 41 | return { |
| 42 | w: cx !== undefined && cx > 0 ? emuToPx(cx) : base.size.w, |
| 43 | h: cy !== undefined && cy > 0 ? emuToPx(cy) : base.size.h, |
| 44 | } |
| 45 | })() |
| 46 | |
| 47 | // --- Collect direct child shape nodes --- |
| 48 | const children: SafeXmlNode[] = [] |
| 49 | for (const child of grpNode.allChildren()) { |
| 50 | if (GROUP_CHILD_TAGS.has(child.localName)) { |
| 51 | children.push(child) |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | return { |
| 56 | ...base, |
| 57 | nodeType: 'group', |
| 58 | childOffset, |
| 59 | childExtent, |
| 60 | children, |
| 61 | } |
| 62 | } |
no test coverage detected