1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx; |
4
|
|
|
|
5
|
|
|
use Composer\Pcre\Preg; |
6
|
|
|
use PhpOffice\PhpSpreadsheet\Cell\Coordinate; |
7
|
|
|
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces; |
8
|
|
|
use PhpOffice\PhpSpreadsheet\Shared\Drawing as SharedDrawing; |
9
|
|
|
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter; |
10
|
|
|
use PhpOffice\PhpSpreadsheet\Spreadsheet; |
11
|
|
|
use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing; |
12
|
|
|
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing; |
13
|
|
|
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException; |
14
|
|
|
|
15
|
|
|
class Drawing extends WriterPart |
16
|
|
|
{ |
17
|
|
|
/** |
18
|
|
|
* Write drawings to XML format. |
19
|
|
|
* |
20
|
|
|
* @param bool $includeCharts Flag indicating if we should include drawing details for charts |
21
|
|
|
* |
22
|
|
|
* @return string XML Output |
23
|
|
|
*/ |
24
|
122 |
|
public function writeDrawings(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, bool $includeCharts = false): string |
25
|
|
|
{ |
26
|
|
|
// Create XML writer |
27
|
122 |
|
$objWriter = null; |
28
|
122 |
|
if ($this->getParentWriter()->getUseDiskCaching()) { |
29
|
|
|
$objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); |
30
|
|
|
} else { |
31
|
122 |
|
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
// XML header |
35
|
122 |
|
$objWriter->startDocument('1.0', 'UTF-8', 'yes'); |
36
|
|
|
|
37
|
|
|
// xdr:wsDr |
38
|
122 |
|
$objWriter->startElement('xdr:wsDr'); |
39
|
122 |
|
$objWriter->writeAttribute('xmlns:xdr', Namespaces::SPREADSHEET_DRAWING); |
40
|
122 |
|
$objWriter->writeAttribute('xmlns:a', Namespaces::DRAWINGML); |
41
|
|
|
|
42
|
|
|
// Loop through images and write drawings |
43
|
122 |
|
$i = 1; |
44
|
122 |
|
$iterator = $worksheet->getDrawingCollection()->getIterator(); |
45
|
122 |
|
while ($iterator->valid()) { |
46
|
|
|
/** @var BaseDrawing $pDrawing */ |
47
|
53 |
|
$pDrawing = $iterator->current(); |
48
|
53 |
|
$pRelationId = $i; |
49
|
53 |
|
$hlinkClickId = $pDrawing->getHyperlink() === null ? null : ++$i; |
50
|
|
|
|
51
|
53 |
|
$this->writeDrawing($objWriter, $pDrawing, $pRelationId, $hlinkClickId); |
52
|
|
|
|
53
|
53 |
|
$iterator->next(); |
54
|
53 |
|
++$i; |
55
|
|
|
} |
56
|
|
|
|
57
|
122 |
|
if ($includeCharts) { |
58
|
78 |
|
$chartCount = $worksheet->getChartCount(); |
59
|
|
|
// Loop through charts and write the chart position |
60
|
78 |
|
if ($chartCount > 0) { |
61
|
78 |
|
for ($c = 0; $c < $chartCount; ++$c) { |
62
|
78 |
|
$chart = $worksheet->getChartByIndex((string) $c); |
63
|
78 |
|
if ($chart !== false) { |
64
|
78 |
|
$this->writeChart($objWriter, $chart, $c + $i); |
65
|
|
|
} |
66
|
|
|
} |
67
|
|
|
} |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
// unparsed AlternateContent |
71
|
|
|
/** @var string[][][][] */ |
72
|
122 |
|
$unparsedLoadedData = $worksheet->getParentOrThrow()->getUnparsedLoadedData(); |
73
|
122 |
|
if (isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingAlternateContents'])) { |
74
|
4 |
|
foreach ($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingAlternateContents'] as $drawingAlternateContent) { |
75
|
4 |
|
$objWriter->writeRaw($drawingAlternateContent); |
76
|
|
|
} |
77
|
|
|
} |
78
|
|
|
|
79
|
122 |
|
$objWriter->endElement(); |
80
|
|
|
|
81
|
|
|
// Return |
82
|
122 |
|
return $objWriter->getData(); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Write drawings to XML format. |
87
|
|
|
*/ |
88
|
78 |
|
public function writeChart(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Chart\Chart $chart, int $relationId = -1): void |
89
|
|
|
{ |
90
|
78 |
|
$tl = $chart->getTopLeftPosition(); |
91
|
78 |
|
$tlColRow = Coordinate::indexesFromString($tl['cell']); |
92
|
78 |
|
$br = $chart->getBottomRightPosition(); |
93
|
|
|
|
94
|
78 |
|
$isTwoCellAnchor = $br['cell'] !== ''; |
95
|
78 |
|
if ($isTwoCellAnchor) { |
96
|
76 |
|
$brColRow = Coordinate::indexesFromString($br['cell']); |
97
|
|
|
|
98
|
76 |
|
$objWriter->startElement('xdr:twoCellAnchor'); |
99
|
|
|
|
100
|
76 |
|
$objWriter->startElement('xdr:from'); |
101
|
76 |
|
$objWriter->writeElement('xdr:col', (string) ($tlColRow[0] - 1)); |
102
|
76 |
|
$objWriter->writeElement('xdr:colOff', self::stringEmu($tl['xOffset'])); |
103
|
76 |
|
$objWriter->writeElement('xdr:row', (string) ($tlColRow[1] - 1)); |
104
|
76 |
|
$objWriter->writeElement('xdr:rowOff', self::stringEmu($tl['yOffset'])); |
105
|
76 |
|
$objWriter->endElement(); |
106
|
76 |
|
$objWriter->startElement('xdr:to'); |
107
|
76 |
|
$objWriter->writeElement('xdr:col', (string) ($brColRow[0] - 1)); |
108
|
76 |
|
$objWriter->writeElement('xdr:colOff', self::stringEmu($br['xOffset'])); |
109
|
76 |
|
$objWriter->writeElement('xdr:row', (string) ($brColRow[1] - 1)); |
110
|
76 |
|
$objWriter->writeElement('xdr:rowOff', self::stringEmu($br['yOffset'])); |
111
|
76 |
|
$objWriter->endElement(); |
112
|
3 |
|
} elseif ($chart->getOneCellAnchor()) { |
113
|
1 |
|
$objWriter->startElement('xdr:oneCellAnchor'); |
114
|
|
|
|
115
|
1 |
|
$objWriter->startElement('xdr:from'); |
116
|
1 |
|
$objWriter->writeElement('xdr:col', (string) ($tlColRow[0] - 1)); |
117
|
1 |
|
$objWriter->writeElement('xdr:colOff', self::stringEmu($tl['xOffset'])); |
118
|
1 |
|
$objWriter->writeElement('xdr:row', (string) ($tlColRow[1] - 1)); |
119
|
1 |
|
$objWriter->writeElement('xdr:rowOff', self::stringEmu($tl['yOffset'])); |
120
|
1 |
|
$objWriter->endElement(); |
121
|
1 |
|
$objWriter->startElement('xdr:ext'); |
122
|
1 |
|
$objWriter->writeAttribute('cx', self::stringEmu($br['xOffset'])); |
123
|
1 |
|
$objWriter->writeAttribute('cy', self::stringEmu($br['yOffset'])); |
124
|
1 |
|
$objWriter->endElement(); |
125
|
|
|
} else { |
126
|
2 |
|
$objWriter->startElement('xdr:absoluteAnchor'); |
127
|
2 |
|
$objWriter->startElement('xdr:pos'); |
128
|
2 |
|
$objWriter->writeAttribute('x', '0'); |
129
|
2 |
|
$objWriter->writeAttribute('y', '0'); |
130
|
2 |
|
$objWriter->endElement(); |
131
|
2 |
|
$objWriter->startElement('xdr:ext'); |
132
|
2 |
|
$objWriter->writeAttribute('cx', self::stringEmu($br['xOffset'])); |
133
|
2 |
|
$objWriter->writeAttribute('cy', self::stringEmu($br['yOffset'])); |
134
|
2 |
|
$objWriter->endElement(); |
135
|
|
|
} |
136
|
|
|
|
137
|
78 |
|
$objWriter->startElement('xdr:graphicFrame'); |
138
|
78 |
|
$objWriter->writeAttribute('macro', ''); |
139
|
78 |
|
$objWriter->startElement('xdr:nvGraphicFramePr'); |
140
|
78 |
|
$objWriter->startElement('xdr:cNvPr'); |
141
|
78 |
|
$objWriter->writeAttribute('name', 'Chart ' . $relationId); |
142
|
78 |
|
$objWriter->writeAttribute('id', (string) (1025 * $relationId)); |
143
|
78 |
|
$objWriter->endElement(); |
144
|
78 |
|
$objWriter->startElement('xdr:cNvGraphicFramePr'); |
145
|
78 |
|
$objWriter->startElement('a:graphicFrameLocks'); |
146
|
78 |
|
$objWriter->endElement(); |
147
|
78 |
|
$objWriter->endElement(); |
148
|
78 |
|
$objWriter->endElement(); |
149
|
|
|
|
150
|
78 |
|
$objWriter->startElement('xdr:xfrm'); |
151
|
78 |
|
$objWriter->startElement('a:off'); |
152
|
78 |
|
$objWriter->writeAttribute('x', '0'); |
153
|
78 |
|
$objWriter->writeAttribute('y', '0'); |
154
|
78 |
|
$objWriter->endElement(); |
155
|
78 |
|
$objWriter->startElement('a:ext'); |
156
|
78 |
|
$objWriter->writeAttribute('cx', '0'); |
157
|
78 |
|
$objWriter->writeAttribute('cy', '0'); |
158
|
78 |
|
$objWriter->endElement(); |
159
|
78 |
|
$objWriter->endElement(); |
160
|
|
|
|
161
|
78 |
|
$objWriter->startElement('a:graphic'); |
162
|
78 |
|
$objWriter->startElement('a:graphicData'); |
163
|
78 |
|
$objWriter->writeAttribute('uri', Namespaces::CHART); |
164
|
78 |
|
$objWriter->startElement('c:chart'); |
165
|
78 |
|
$objWriter->writeAttribute('xmlns:c', Namespaces::CHART); |
166
|
78 |
|
$objWriter->writeAttribute('xmlns:r', Namespaces::SCHEMA_OFFICE_DOCUMENT); |
167
|
78 |
|
$objWriter->writeAttribute('r:id', 'rId' . $relationId); |
168
|
78 |
|
$objWriter->endElement(); |
169
|
78 |
|
$objWriter->endElement(); |
170
|
78 |
|
$objWriter->endElement(); |
171
|
78 |
|
$objWriter->endElement(); |
172
|
|
|
|
173
|
78 |
|
$objWriter->startElement('xdr:clientData'); |
174
|
78 |
|
$objWriter->endElement(); |
175
|
|
|
|
176
|
78 |
|
$objWriter->endElement(); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Write drawings to XML format. |
181
|
|
|
*/ |
182
|
53 |
|
public function writeDrawing(XMLWriter $objWriter, BaseDrawing $drawing, int $relationId = -1, ?int $hlinkClickId = null): void |
183
|
|
|
{ |
184
|
53 |
|
if ($relationId >= 0) { |
185
|
53 |
|
$isTwoCellAnchor = $drawing->getCoordinates2() !== ''; |
186
|
53 |
|
if ($isTwoCellAnchor) { |
187
|
|
|
// xdr:twoCellAnchor |
188
|
31 |
|
$objWriter->startElement('xdr:twoCellAnchor'); |
189
|
31 |
|
if ($drawing->validEditAs()) { |
190
|
26 |
|
$objWriter->writeAttribute('editAs', $drawing->getEditAs()); |
191
|
|
|
} |
192
|
|
|
// Image location |
193
|
31 |
|
$aCoordinates = Coordinate::indexesFromString($drawing->getCoordinates()); |
194
|
31 |
|
$aCoordinates2 = Coordinate::indexesFromString($drawing->getCoordinates2()); |
195
|
|
|
|
196
|
|
|
// xdr:from |
197
|
31 |
|
$objWriter->startElement('xdr:from'); |
198
|
31 |
|
$objWriter->writeElement('xdr:col', (string) ($aCoordinates[0] - 1)); |
199
|
31 |
|
$objWriter->writeElement('xdr:colOff', self::stringEmu($drawing->getOffsetX())); |
200
|
31 |
|
$objWriter->writeElement('xdr:row', (string) ($aCoordinates[1] - 1)); |
201
|
31 |
|
$objWriter->writeElement('xdr:rowOff', self::stringEmu($drawing->getOffsetY())); |
202
|
31 |
|
$objWriter->endElement(); |
203
|
|
|
|
204
|
|
|
// xdr:to |
205
|
31 |
|
$objWriter->startElement('xdr:to'); |
206
|
31 |
|
$objWriter->writeElement('xdr:col', (string) ($aCoordinates2[0] - 1)); |
207
|
31 |
|
$objWriter->writeElement('xdr:colOff', self::stringEmu($drawing->getOffsetX2())); |
208
|
31 |
|
$objWriter->writeElement('xdr:row', (string) ($aCoordinates2[1] - 1)); |
209
|
31 |
|
$objWriter->writeElement('xdr:rowOff', self::stringEmu($drawing->getOffsetY2())); |
210
|
31 |
|
$objWriter->endElement(); |
211
|
|
|
} else { |
212
|
|
|
// xdr:oneCellAnchor |
213
|
24 |
|
$objWriter->startElement('xdr:oneCellAnchor'); |
214
|
|
|
// Image location |
215
|
24 |
|
$aCoordinates = Coordinate::indexesFromString($drawing->getCoordinates()); |
216
|
|
|
|
217
|
|
|
// xdr:from |
218
|
24 |
|
$objWriter->startElement('xdr:from'); |
219
|
24 |
|
$objWriter->writeElement('xdr:col', (string) ($aCoordinates[0] - 1)); |
220
|
24 |
|
$objWriter->writeElement('xdr:colOff', self::stringEmu($drawing->getOffsetX())); |
221
|
24 |
|
$objWriter->writeElement('xdr:row', (string) ($aCoordinates[1] - 1)); |
222
|
24 |
|
$objWriter->writeElement('xdr:rowOff', self::stringEmu($drawing->getOffsetY())); |
223
|
24 |
|
$objWriter->endElement(); |
224
|
|
|
|
225
|
|
|
// xdr:ext |
226
|
24 |
|
$objWriter->startElement('xdr:ext'); |
227
|
24 |
|
$objWriter->writeAttribute('cx', self::stringEmu($drawing->getWidth())); |
228
|
24 |
|
$objWriter->writeAttribute('cy', self::stringEmu($drawing->getHeight())); |
229
|
24 |
|
$objWriter->endElement(); |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
// xdr:pic |
233
|
53 |
|
$objWriter->startElement('xdr:pic'); |
234
|
|
|
|
235
|
|
|
// xdr:nvPicPr |
236
|
53 |
|
$objWriter->startElement('xdr:nvPicPr'); |
237
|
|
|
|
238
|
|
|
// xdr:cNvPr |
239
|
53 |
|
$objWriter->startElement('xdr:cNvPr'); |
240
|
53 |
|
$objWriter->writeAttribute('id', (string) $relationId); |
241
|
53 |
|
$objWriter->writeAttribute('name', $drawing->getName()); |
242
|
53 |
|
$objWriter->writeAttribute('descr', $drawing->getDescription()); |
243
|
|
|
|
244
|
|
|
//a:hlinkClick |
245
|
53 |
|
$this->writeHyperLinkDrawing($objWriter, $hlinkClickId); |
246
|
|
|
|
247
|
53 |
|
$objWriter->endElement(); |
248
|
|
|
|
249
|
|
|
// xdr:cNvPicPr |
250
|
53 |
|
$objWriter->startElement('xdr:cNvPicPr'); |
251
|
|
|
|
252
|
|
|
// a:picLocks |
253
|
53 |
|
$objWriter->startElement('a:picLocks'); |
254
|
53 |
|
$objWriter->writeAttribute('noChangeAspect', '1'); |
255
|
53 |
|
$objWriter->endElement(); |
256
|
|
|
|
257
|
53 |
|
$objWriter->endElement(); |
258
|
|
|
|
259
|
53 |
|
$objWriter->endElement(); |
260
|
|
|
|
261
|
|
|
// xdr:blipFill |
262
|
53 |
|
$objWriter->startElement('xdr:blipFill'); |
263
|
|
|
|
264
|
|
|
// a:blip |
265
|
53 |
|
$objWriter->startElement('a:blip'); |
266
|
53 |
|
$objWriter->writeAttribute('xmlns:r', Namespaces::SCHEMA_OFFICE_DOCUMENT); |
267
|
53 |
|
$objWriter->writeAttribute('r:embed', 'rId' . $relationId); |
268
|
53 |
|
$temp = $drawing->getOpacity(); |
269
|
53 |
|
if (is_int($temp) && $temp >= 0 && $temp <= 100000) { |
270
|
4 |
|
$objWriter->startElement('a:alphaModFix'); |
271
|
4 |
|
$objWriter->writeAttribute('amt', "$temp"); |
272
|
4 |
|
$objWriter->endElement(); // a:alphaModFix |
273
|
|
|
} |
274
|
53 |
|
$objWriter->endElement(); // a:blip |
275
|
|
|
|
276
|
53 |
|
$srcRect = $drawing->getSrcRect(); |
277
|
53 |
|
if (!empty($srcRect)) { |
278
|
2 |
|
$objWriter->startElement('a:srcRect'); |
279
|
2 |
|
foreach ($srcRect as $key => $value) { |
280
|
2 |
|
$objWriter->writeAttribute($key, (string) $value); |
281
|
|
|
} |
282
|
2 |
|
$objWriter->endElement(); // a:srcRect |
283
|
2 |
|
$objWriter->startElement('a:stretch'); |
284
|
2 |
|
$objWriter->endElement(); // a:stretch |
285
|
|
|
} else { |
286
|
|
|
// a:stretch |
287
|
51 |
|
$objWriter->startElement('a:stretch'); |
288
|
51 |
|
$objWriter->writeElement('a:fillRect', null); |
289
|
51 |
|
$objWriter->endElement(); |
290
|
|
|
} |
291
|
|
|
|
292
|
53 |
|
$objWriter->endElement(); |
293
|
|
|
|
294
|
|
|
// xdr:spPr |
295
|
53 |
|
$objWriter->startElement('xdr:spPr'); |
296
|
|
|
|
297
|
|
|
// a:xfrm |
298
|
53 |
|
$objWriter->startElement('a:xfrm'); |
299
|
53 |
|
$objWriter->writeAttribute('rot', (string) SharedDrawing::degreesToAngle($drawing->getRotation())); |
300
|
53 |
|
self::writeAttributeIf($objWriter, $drawing->getFlipVertical(), 'flipV', '1'); |
301
|
53 |
|
self::writeAttributeIf($objWriter, $drawing->getFlipHorizontal(), 'flipH', '1'); |
302
|
53 |
|
if ($isTwoCellAnchor) { |
303
|
31 |
|
$objWriter->startElement('a:ext'); |
304
|
31 |
|
$objWriter->writeAttribute('cx', self::stringEmu($drawing->getWidth())); |
305
|
31 |
|
$objWriter->writeAttribute('cy', self::stringEmu($drawing->getHeight())); |
306
|
31 |
|
$objWriter->endElement(); |
307
|
|
|
} |
308
|
53 |
|
$objWriter->endElement(); |
309
|
|
|
|
310
|
|
|
// a:prstGeom |
311
|
53 |
|
$objWriter->startElement('a:prstGeom'); |
312
|
53 |
|
$objWriter->writeAttribute('prst', 'rect'); |
313
|
|
|
|
314
|
|
|
// a:avLst |
315
|
53 |
|
$objWriter->writeElement('a:avLst', null); |
316
|
|
|
|
317
|
53 |
|
$objWriter->endElement(); |
318
|
|
|
|
319
|
53 |
|
if ($drawing->getShadow()->getVisible()) { |
320
|
|
|
// a:effectLst |
321
|
6 |
|
$objWriter->startElement('a:effectLst'); |
322
|
|
|
|
323
|
|
|
// a:outerShdw |
324
|
6 |
|
$objWriter->startElement('a:outerShdw'); |
325
|
6 |
|
$objWriter->writeAttribute('blurRad', self::stringEmu($drawing->getShadow()->getBlurRadius())); |
326
|
6 |
|
$objWriter->writeAttribute('dist', self::stringEmu($drawing->getShadow()->getDistance())); |
327
|
6 |
|
$objWriter->writeAttribute('dir', (string) SharedDrawing::degreesToAngle($drawing->getShadow()->getDirection())); |
328
|
6 |
|
$objWriter->writeAttribute('algn', $drawing->getShadow()->getAlignment()); |
329
|
6 |
|
$objWriter->writeAttribute('rotWithShape', '0'); |
330
|
|
|
|
331
|
|
|
// a:srgbClr |
332
|
6 |
|
$objWriter->startElement('a:srgbClr'); |
333
|
6 |
|
$objWriter->writeAttribute('val', $drawing->getShadow()->getColor()->getRGB()); |
334
|
|
|
|
335
|
|
|
// a:alpha |
336
|
6 |
|
$objWriter->startElement('a:alpha'); |
337
|
6 |
|
$objWriter->writeAttribute('val', (string) ($drawing->getShadow()->getAlpha() * 1000)); |
338
|
6 |
|
$objWriter->endElement(); |
339
|
|
|
|
340
|
6 |
|
$objWriter->endElement(); |
341
|
|
|
|
342
|
6 |
|
$objWriter->endElement(); |
343
|
|
|
|
344
|
6 |
|
$objWriter->endElement(); |
345
|
|
|
} |
346
|
53 |
|
$objWriter->endElement(); |
347
|
|
|
|
348
|
53 |
|
$objWriter->endElement(); |
349
|
|
|
|
350
|
|
|
// xdr:clientData |
351
|
53 |
|
$objWriter->writeElement('xdr:clientData', null); |
352
|
|
|
|
353
|
53 |
|
$objWriter->endElement(); |
354
|
|
|
} else { |
355
|
|
|
throw new WriterException('Invalid parameters passed.'); |
356
|
|
|
} |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
/** |
360
|
|
|
* Write VML header/footer images to XML format. |
361
|
|
|
* |
362
|
|
|
* @return string XML Output |
363
|
|
|
*/ |
364
|
3 |
|
public function writeVMLHeaderFooterImages(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet): string |
365
|
|
|
{ |
366
|
|
|
// Create XML writer |
367
|
3 |
|
$objWriter = null; |
368
|
3 |
|
if ($this->getParentWriter()->getUseDiskCaching()) { |
369
|
|
|
$objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); |
370
|
|
|
} else { |
371
|
3 |
|
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
// XML header |
375
|
3 |
|
$objWriter->startDocument('1.0', 'UTF-8', 'yes'); |
376
|
|
|
|
377
|
|
|
// Header/footer images |
378
|
3 |
|
$images = $worksheet->getHeaderFooter()->getImages(); |
379
|
|
|
|
380
|
|
|
// xml |
381
|
3 |
|
$objWriter->startElement('xml'); |
382
|
3 |
|
$objWriter->writeAttribute('xmlns:v', Namespaces::URN_VML); |
383
|
3 |
|
$objWriter->writeAttribute('xmlns:o', Namespaces::URN_MSOFFICE); |
384
|
3 |
|
$objWriter->writeAttribute('xmlns:x', Namespaces::URN_EXCEL); |
385
|
|
|
|
386
|
|
|
// o:shapelayout |
387
|
3 |
|
$objWriter->startElement('o:shapelayout'); |
388
|
3 |
|
$objWriter->writeAttribute('v:ext', 'edit'); |
389
|
|
|
|
390
|
|
|
// o:idmap |
391
|
3 |
|
$objWriter->startElement('o:idmap'); |
392
|
3 |
|
$objWriter->writeAttribute('v:ext', 'edit'); |
393
|
3 |
|
$objWriter->writeAttribute('data', '1'); |
394
|
3 |
|
$objWriter->endElement(); |
395
|
|
|
|
396
|
3 |
|
$objWriter->endElement(); |
397
|
|
|
|
398
|
|
|
// v:shapetype |
399
|
3 |
|
$objWriter->startElement('v:shapetype'); |
400
|
3 |
|
$objWriter->writeAttribute('id', '_x0000_t75'); |
401
|
3 |
|
$objWriter->writeAttribute('coordsize', '21600,21600'); |
402
|
3 |
|
$objWriter->writeAttribute('o:spt', '75'); |
403
|
3 |
|
$objWriter->writeAttribute('o:preferrelative', 't'); |
404
|
3 |
|
$objWriter->writeAttribute('path', 'm@4@5l@4@11@9@11@9@5xe'); |
405
|
3 |
|
$objWriter->writeAttribute('filled', 'f'); |
406
|
3 |
|
$objWriter->writeAttribute('stroked', 'f'); |
407
|
|
|
|
408
|
|
|
// v:stroke |
409
|
3 |
|
$objWriter->startElement('v:stroke'); |
410
|
3 |
|
$objWriter->writeAttribute('joinstyle', 'miter'); |
411
|
3 |
|
$objWriter->endElement(); |
412
|
|
|
|
413
|
|
|
// v:formulas |
414
|
3 |
|
$objWriter->startElement('v:formulas'); |
415
|
|
|
|
416
|
|
|
// v:f |
417
|
3 |
|
$objWriter->startElement('v:f'); |
418
|
3 |
|
$objWriter->writeAttribute('eqn', 'if lineDrawn pixelLineWidth 0'); |
419
|
3 |
|
$objWriter->endElement(); |
420
|
|
|
|
421
|
|
|
// v:f |
422
|
3 |
|
$objWriter->startElement('v:f'); |
423
|
3 |
|
$objWriter->writeAttribute('eqn', 'sum @0 1 0'); |
424
|
3 |
|
$objWriter->endElement(); |
425
|
|
|
|
426
|
|
|
// v:f |
427
|
3 |
|
$objWriter->startElement('v:f'); |
428
|
3 |
|
$objWriter->writeAttribute('eqn', 'sum 0 0 @1'); |
429
|
3 |
|
$objWriter->endElement(); |
430
|
|
|
|
431
|
|
|
// v:f |
432
|
3 |
|
$objWriter->startElement('v:f'); |
433
|
3 |
|
$objWriter->writeAttribute('eqn', 'prod @2 1 2'); |
434
|
3 |
|
$objWriter->endElement(); |
435
|
|
|
|
436
|
|
|
// v:f |
437
|
3 |
|
$objWriter->startElement('v:f'); |
438
|
3 |
|
$objWriter->writeAttribute('eqn', 'prod @3 21600 pixelWidth'); |
439
|
3 |
|
$objWriter->endElement(); |
440
|
|
|
|
441
|
|
|
// v:f |
442
|
3 |
|
$objWriter->startElement('v:f'); |
443
|
3 |
|
$objWriter->writeAttribute('eqn', 'prod @3 21600 pixelHeight'); |
444
|
3 |
|
$objWriter->endElement(); |
445
|
|
|
|
446
|
|
|
// v:f |
447
|
3 |
|
$objWriter->startElement('v:f'); |
448
|
3 |
|
$objWriter->writeAttribute('eqn', 'sum @0 0 1'); |
449
|
3 |
|
$objWriter->endElement(); |
450
|
|
|
|
451
|
|
|
// v:f |
452
|
3 |
|
$objWriter->startElement('v:f'); |
453
|
3 |
|
$objWriter->writeAttribute('eqn', 'prod @6 1 2'); |
454
|
3 |
|
$objWriter->endElement(); |
455
|
|
|
|
456
|
|
|
// v:f |
457
|
3 |
|
$objWriter->startElement('v:f'); |
458
|
3 |
|
$objWriter->writeAttribute('eqn', 'prod @7 21600 pixelWidth'); |
459
|
3 |
|
$objWriter->endElement(); |
460
|
|
|
|
461
|
|
|
// v:f |
462
|
3 |
|
$objWriter->startElement('v:f'); |
463
|
3 |
|
$objWriter->writeAttribute('eqn', 'sum @8 21600 0'); |
464
|
3 |
|
$objWriter->endElement(); |
465
|
|
|
|
466
|
|
|
// v:f |
467
|
3 |
|
$objWriter->startElement('v:f'); |
468
|
3 |
|
$objWriter->writeAttribute('eqn', 'prod @7 21600 pixelHeight'); |
469
|
3 |
|
$objWriter->endElement(); |
470
|
|
|
|
471
|
|
|
// v:f |
472
|
3 |
|
$objWriter->startElement('v:f'); |
473
|
3 |
|
$objWriter->writeAttribute('eqn', 'sum @10 21600 0'); |
474
|
3 |
|
$objWriter->endElement(); |
475
|
|
|
|
476
|
3 |
|
$objWriter->endElement(); |
477
|
|
|
|
478
|
|
|
// v:path |
479
|
3 |
|
$objWriter->startElement('v:path'); |
480
|
3 |
|
$objWriter->writeAttribute('o:extrusionok', 'f'); |
481
|
3 |
|
$objWriter->writeAttribute('gradientshapeok', 't'); |
482
|
3 |
|
$objWriter->writeAttribute('o:connecttype', 'rect'); |
483
|
3 |
|
$objWriter->endElement(); |
484
|
|
|
|
485
|
|
|
// o:lock |
486
|
3 |
|
$objWriter->startElement('o:lock'); |
487
|
3 |
|
$objWriter->writeAttribute('v:ext', 'edit'); |
488
|
3 |
|
$objWriter->writeAttribute('aspectratio', 't'); |
489
|
3 |
|
$objWriter->endElement(); |
490
|
|
|
|
491
|
3 |
|
$objWriter->endElement(); |
492
|
|
|
|
493
|
|
|
// Loop through images |
494
|
3 |
|
foreach ($images as $key => $value) { |
495
|
3 |
|
$this->writeVMLHeaderFooterImage($objWriter, $key, $value); |
496
|
|
|
} |
497
|
|
|
|
498
|
3 |
|
$objWriter->endElement(); |
499
|
|
|
|
500
|
|
|
// Return |
501
|
3 |
|
return $objWriter->getData(); |
502
|
|
|
} |
503
|
|
|
|
504
|
|
|
/** |
505
|
|
|
* Write VML comment to XML format. |
506
|
|
|
* |
507
|
|
|
* @param string $reference Reference |
508
|
|
|
*/ |
509
|
3 |
|
private function writeVMLHeaderFooterImage(XMLWriter $objWriter, string $reference, HeaderFooterDrawing $image): void |
510
|
|
|
{ |
511
|
|
|
// Calculate object id |
512
|
3 |
|
if (!Preg::isMatch('{(\d+)}', md5($reference), $m)) { |
513
|
|
|
// @codeCoverageIgnoreStart |
514
|
|
|
throw new WriterException('Regexp failure in writeVMLHeaderFooterImage'); |
515
|
|
|
// @codeCoverageIgnoreEnd |
516
|
|
|
} |
517
|
3 |
|
$id = 1500 + ((int) substr($m[1], 0, 2) * 1); |
518
|
|
|
|
519
|
|
|
// Calculate offset |
520
|
3 |
|
$width = $image->getWidth(); |
521
|
3 |
|
$height = $image->getHeight(); |
522
|
3 |
|
$marginLeft = $image->getOffsetX(); |
523
|
3 |
|
$marginTop = $image->getOffsetY(); |
524
|
|
|
|
525
|
|
|
// v:shape |
526
|
3 |
|
$objWriter->startElement('v:shape'); |
527
|
3 |
|
$objWriter->writeAttribute('id', $reference); |
528
|
3 |
|
$objWriter->writeAttribute('o:spid', '_x0000_s' . $id); |
529
|
3 |
|
$objWriter->writeAttribute('type', '#_x0000_t75'); |
530
|
3 |
|
$objWriter->writeAttribute('style', "position:absolute;margin-left:{$marginLeft}px;margin-top:{$marginTop}px;width:{$width}px;height:{$height}px;z-index:1"); |
531
|
|
|
|
532
|
|
|
// v:imagedata |
533
|
3 |
|
$objWriter->startElement('v:imagedata'); |
534
|
3 |
|
$objWriter->writeAttribute('o:relid', 'rId' . $reference); |
535
|
3 |
|
$objWriter->writeAttribute('o:title', $image->getName()); |
536
|
3 |
|
$objWriter->endElement(); |
537
|
|
|
|
538
|
|
|
// o:lock |
539
|
3 |
|
$objWriter->startElement('o:lock'); |
540
|
3 |
|
$objWriter->writeAttribute('v:ext', 'edit'); |
541
|
3 |
|
$objWriter->writeAttribute('textRotation', 't'); |
542
|
3 |
|
$objWriter->endElement(); |
543
|
|
|
|
544
|
3 |
|
$objWriter->endElement(); |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
/** |
548
|
|
|
* Get an array of all drawings. |
549
|
|
|
* |
550
|
|
|
* @return BaseDrawing[] All drawings in PhpSpreadsheet |
551
|
|
|
*/ |
552
|
375 |
|
public function allDrawings(Spreadsheet $spreadsheet): array |
553
|
|
|
{ |
554
|
|
|
// Get an array of all drawings |
555
|
375 |
|
$aDrawings = []; |
556
|
|
|
|
557
|
|
|
// Loop through PhpSpreadsheet |
558
|
375 |
|
$sheetCount = $spreadsheet->getSheetCount(); |
559
|
375 |
|
for ($i = 0; $i < $sheetCount; ++$i) { |
560
|
|
|
// Loop through images and add to array |
561
|
375 |
|
$iterator = $spreadsheet->getSheet($i)->getDrawingCollection()->getIterator(); |
562
|
375 |
|
while ($iterator->valid()) { |
563
|
53 |
|
$aDrawings[] = $iterator->current(); |
564
|
|
|
|
565
|
53 |
|
$iterator->next(); |
566
|
|
|
} |
567
|
|
|
} |
568
|
|
|
|
569
|
375 |
|
return $aDrawings; |
570
|
|
|
} |
571
|
|
|
|
572
|
53 |
|
private function writeHyperLinkDrawing(XMLWriter $objWriter, ?int $hlinkClickId): void |
573
|
|
|
{ |
574
|
53 |
|
if ($hlinkClickId === null) { |
575
|
51 |
|
return; |
576
|
|
|
} |
577
|
|
|
|
578
|
2 |
|
$objWriter->startElement('a:hlinkClick'); |
579
|
2 |
|
$objWriter->writeAttribute('xmlns:r', Namespaces::SCHEMA_OFFICE_DOCUMENT); |
580
|
2 |
|
$objWriter->writeAttribute('r:id', 'rId' . $hlinkClickId); |
581
|
2 |
|
$objWriter->endElement(); |
582
|
|
|
} |
583
|
|
|
|
584
|
120 |
|
private static function stringEmu(int $pixelValue): string |
585
|
|
|
{ |
586
|
120 |
|
return (string) SharedDrawing::pixelsToEMU($pixelValue); |
587
|
|
|
} |
588
|
|
|
|
589
|
53 |
|
private static function writeAttributeIf(XMLWriter $objWriter, ?bool $condition, string $attr, string $val): void |
590
|
|
|
{ |
591
|
53 |
|
if ($condition) { |
592
|
1 |
|
$objWriter->writeAttribute($attr, $val); |
593
|
|
|
} |
594
|
|
|
} |
595
|
|
|
} |
596
|
|
|
|