Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

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