* Parse a SmartArt diagram graphicFrame by resolving the diagram drawing fallback XML. * The drawing XML contains pre-rendered shapes in a spTree that we can display as a group.
( graphicFrame: SafeXmlNode, rels: Map<string, RelEntry>, slidePath: string, diagramDrawings: Map<string, string> )
| 111 | * The drawing XML contains pre-rendered shapes in a spTree that we can display as a group. |
| 112 | */ |
| 113 | function parseDiagramFrame( |
| 114 | graphicFrame: SafeXmlNode, |
| 115 | rels: Map<string, RelEntry>, |
| 116 | slidePath: string, |
| 117 | diagramDrawings: Map<string, string> |
| 118 | ): GroupNodeData | undefined { |
| 119 | const base = parseBaseProps(graphicFrame) |
| 120 | const slideDir = slidePath.substring(0, slidePath.lastIndexOf('/')) |
| 121 | const drawingCandidates = Array.from(rels.values()) |
| 122 | .filter( |
| 123 | (entry) => entry.type.includes('diagramDrawing') || entry.target.includes('diagrams/drawing') |
| 124 | ) |
| 125 | .map((entry) => { |
| 126 | const target = entry.target |
| 127 | const match = target.match(/drawing(\d+)/) |
| 128 | return { |
| 129 | target, |
| 130 | num: match ? Number.parseInt(match[1], 10) : undefined, |
| 131 | } |
| 132 | }) |
| 133 | |
| 134 | // Extract the diagram data rId from the relIds element to identify which diagram this is |
| 135 | const graphic = graphicFrame.child('graphic') |
| 136 | const graphicData = graphic.child('graphicData') |
| 137 | const relIds = graphicData.child('relIds') |
| 138 | |
| 139 | // Strategy 1: Match data file number to drawing file number |
| 140 | // e.g. data3.xml → drawing3.xml |
| 141 | if (relIds.exists()) { |
| 142 | const dmRId = relIds.attr('r:dm') ?? relIds.attr('dm') |
| 143 | if (dmRId) { |
| 144 | const dmRel = rels.get(dmRId) |
| 145 | if (dmRel) { |
| 146 | // Extract the number from the data target (e.g. "data3" → "3") |
| 147 | const numMatch = dmRel.target.match(/data(\d+)/) |
| 148 | if (numMatch) { |
| 149 | const drawingNum = Number.parseInt(numMatch[1], 10) |
| 150 | // Prefer exact drawingN; if absent, use the nearest numbered drawing relation. |
| 151 | const ordered = drawingCandidates.slice().sort((a, b) => { |
| 152 | const da = a.num === undefined ? Number.POSITIVE_INFINITY : Math.abs(a.num - drawingNum) |
| 153 | const db = b.num === undefined ? Number.POSITIVE_INFINITY : Math.abs(b.num - drawingNum) |
| 154 | return da - db |
| 155 | }) |
| 156 | for (const candidate of ordered) { |
| 157 | const drawingPath = resolveRelTarget(slideDir, candidate.target) |
| 158 | const drawingXml = diagramDrawings.get(drawingPath) |
| 159 | if (drawingXml) { |
| 160 | return buildDiagramGroup(base, drawingXml) |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | // Strategy 2: Fallback - find any diagramDrawing relationship |
| 169 | for (const candidate of drawingCandidates) { |
| 170 | const drawingPath = resolveRelTarget(slideDir, candidate.target) |
no test coverage detected