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