Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
   4  
   5  use PhpOffice\PhpSpreadsheet\Style\Alignment;
   6  use PhpOffice\PhpSpreadsheet\Style\Border;
   7  use PhpOffice\PhpSpreadsheet\Style\Borders;
   8  use PhpOffice\PhpSpreadsheet\Style\Color;
   9  use PhpOffice\PhpSpreadsheet\Style\Fill;
  10  use PhpOffice\PhpSpreadsheet\Style\Font;
  11  use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
  12  use PhpOffice\PhpSpreadsheet\Style\Protection;
  13  use PhpOffice\PhpSpreadsheet\Style\Style;
  14  use SimpleXMLElement;
  15  
  16  class Styles extends BaseParserClass
  17  {
  18      /**
  19       * Theme instance.
  20       *
  21       * @var Theme
  22       */
  23      private static $theme = null;
  24  
  25      private $styles = [];
  26  
  27      private $cellStyles = [];
  28  
  29      private $styleXml;
  30  
  31      public function __construct(SimpleXMLElement $styleXml)
  32      {
  33          $this->styleXml = $styleXml;
  34      }
  35  
  36      public function setStyleBaseData(?Theme $theme = null, $styles = [], $cellStyles = []): void
  37      {
  38          self::$theme = $theme;
  39          $this->styles = $styles;
  40          $this->cellStyles = $cellStyles;
  41      }
  42  
  43      private static function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void
  44      {
  45          $fontStyle->setName((string) $fontStyleXml->name['val']);
  46          $fontStyle->setSize((float) $fontStyleXml->sz['val']);
  47  
  48          if (isset($fontStyleXml->b)) {
  49              $fontStyle->setBold(!isset($fontStyleXml->b['val']) || self::boolean((string) $fontStyleXml->b['val']));
  50          }
  51          if (isset($fontStyleXml->i)) {
  52              $fontStyle->setItalic(!isset($fontStyleXml->i['val']) || self::boolean((string) $fontStyleXml->i['val']));
  53          }
  54          if (isset($fontStyleXml->strike)) {
  55              $fontStyle->setStrikethrough(!isset($fontStyleXml->strike['val']) || self::boolean((string) $fontStyleXml->strike['val']));
  56          }
  57          $fontStyle->getColor()->setARGB(self::readColor($fontStyleXml->color));
  58  
  59          if (isset($fontStyleXml->u) && !isset($fontStyleXml->u['val'])) {
  60              $fontStyle->setUnderline(Font::UNDERLINE_SINGLE);
  61          } elseif (isset($fontStyleXml->u, $fontStyleXml->u['val'])) {
  62              $fontStyle->setUnderline((string) $fontStyleXml->u['val']);
  63          }
  64  
  65          if (isset($fontStyleXml->vertAlign, $fontStyleXml->vertAlign['val'])) {
  66              $verticalAlign = strtolower((string) $fontStyleXml->vertAlign['val']);
  67              if ($verticalAlign === 'superscript') {
  68                  $fontStyle->setSuperscript(true);
  69              }
  70              if ($verticalAlign === 'subscript') {
  71                  $fontStyle->setSubscript(true);
  72              }
  73          }
  74      }
  75  
  76      private static function readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void
  77      {
  78          if ($numfmtStyleXml->count() === 0) {
  79              return;
  80          }
  81          $numfmt = $numfmtStyleXml->attributes();
  82          if ($numfmt->count() > 0 && isset($numfmt['formatCode'])) {
  83              $numfmtStyle->setFormatCode((string) $numfmt['formatCode']);
  84          }
  85      }
  86  
  87      private static function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void
  88      {
  89          if ($fillStyleXml->gradientFill) {
  90              /** @var SimpleXMLElement $gradientFill */
  91              $gradientFill = $fillStyleXml->gradientFill[0];
  92              if (!empty($gradientFill['type'])) {
  93                  $fillStyle->setFillType((string) $gradientFill['type']);
  94              }
  95              $fillStyle->setRotation((float) ($gradientFill['degree']));
  96              $gradientFill->registerXPathNamespace('sml', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
  97              $fillStyle->getStartColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color));
  98              $fillStyle->getEndColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color));
  99          } elseif ($fillStyleXml->patternFill) {
 100              $patternType = (string) $fillStyleXml->patternFill['patternType'] != '' ? (string) $fillStyleXml->patternFill['patternType'] : 'solid';
 101              $fillStyle->setFillType($patternType);
 102              if ($fillStyleXml->patternFill->fgColor) {
 103                  $fillStyle->getStartColor()->setARGB(self::readColor($fillStyleXml->patternFill->fgColor, true));
 104              } else {
 105                  $fillStyle->getStartColor()->setARGB('FF000000');
 106              }
 107              if ($fillStyleXml->patternFill->bgColor) {
 108                  $fillStyle->getEndColor()->setARGB(self::readColor($fillStyleXml->patternFill->bgColor, true));
 109              }
 110          }
 111      }
 112  
 113      private static function readBorderStyle(Borders $borderStyle, SimpleXMLElement $borderStyleXml): void
 114      {
 115          $diagonalUp = self::boolean((string) $borderStyleXml['diagonalUp']);
 116          $diagonalDown = self::boolean((string) $borderStyleXml['diagonalDown']);
 117          if (!$diagonalUp && !$diagonalDown) {
 118              $borderStyle->setDiagonalDirection(Borders::DIAGONAL_NONE);
 119          } elseif ($diagonalUp && !$diagonalDown) {
 120              $borderStyle->setDiagonalDirection(Borders::DIAGONAL_UP);
 121          } elseif (!$diagonalUp && $diagonalDown) {
 122              $borderStyle->setDiagonalDirection(Borders::DIAGONAL_DOWN);
 123          } else {
 124              $borderStyle->setDiagonalDirection(Borders::DIAGONAL_BOTH);
 125          }
 126  
 127          self::readBorder($borderStyle->getLeft(), $borderStyleXml->left);
 128          self::readBorder($borderStyle->getRight(), $borderStyleXml->right);
 129          self::readBorder($borderStyle->getTop(), $borderStyleXml->top);
 130          self::readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
 131          self::readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
 132      }
 133  
 134      private static function readBorder(Border $border, SimpleXMLElement $borderXml): void
 135      {
 136          if (isset($borderXml['style'])) {
 137              $border->setBorderStyle((string) $borderXml['style']);
 138          }
 139          if (isset($borderXml->color)) {
 140              $border->getColor()->setARGB(self::readColor($borderXml->color));
 141          }
 142      }
 143  
 144      private static function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void
 145      {
 146          $alignment->setHorizontal((string) $alignmentXml->alignment['horizontal']);
 147          $alignment->setVertical((string) $alignmentXml->alignment['vertical']);
 148  
 149          $textRotation = 0;
 150          if ((int) $alignmentXml->alignment['textRotation'] <= 90) {
 151              $textRotation = (int) $alignmentXml->alignment['textRotation'];
 152          } elseif ((int) $alignmentXml->alignment['textRotation'] > 90) {
 153              $textRotation = 90 - (int) $alignmentXml->alignment['textRotation'];
 154          }
 155  
 156          $alignment->setTextRotation((int) $textRotation);
 157          $alignment->setWrapText(self::boolean((string) $alignmentXml->alignment['wrapText']));
 158          $alignment->setShrinkToFit(self::boolean((string) $alignmentXml->alignment['shrinkToFit']));
 159          $alignment->setIndent((int) ((string) $alignmentXml->alignment['indent']) > 0 ? (int) ((string) $alignmentXml->alignment['indent']) : 0);
 160          $alignment->setReadOrder((int) ((string) $alignmentXml->alignment['readingOrder']) > 0 ? (int) ((string) $alignmentXml->alignment['readingOrder']) : 0);
 161      }
 162  
 163      private function readStyle(Style $docStyle, $style): void
 164      {
 165          if ($style->numFmt instanceof SimpleXMLElement) {
 166              self::readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
 167          } else {
 168              $docStyle->getNumberFormat()->setFormatCode($style->numFmt);
 169          }
 170  
 171          if (isset($style->font)) {
 172              self::readFontStyle($docStyle->getFont(), $style->font);
 173          }
 174  
 175          if (isset($style->fill)) {
 176              self::readFillStyle($docStyle->getFill(), $style->fill);
 177          }
 178  
 179          if (isset($style->border)) {
 180              self::readBorderStyle($docStyle->getBorders(), $style->border);
 181          }
 182  
 183          if (isset($style->alignment->alignment)) {
 184              self::readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
 185          }
 186  
 187          // protection
 188          if (isset($style->protection)) {
 189              $this->readProtectionLocked($docStyle, $style);
 190              $this->readProtectionHidden($docStyle, $style);
 191          }
 192  
 193          // top-level style settings
 194          if (isset($style->quotePrefix)) {
 195              $docStyle->setQuotePrefix(true);
 196          }
 197      }
 198  
 199      private function readProtectionLocked(Style $docStyle, $style): void
 200      {
 201          if (isset($style->protection['locked'])) {
 202              if (self::boolean((string) $style->protection['locked'])) {
 203                  $docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED);
 204              } else {
 205                  $docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
 206              }
 207          }
 208      }
 209  
 210      private function readProtectionHidden(Style $docStyle, $style): void
 211      {
 212          if (isset($style->protection['hidden'])) {
 213              if (self::boolean((string) $style->protection['hidden'])) {
 214                  $docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED);
 215              } else {
 216                  $docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED);
 217              }
 218          }
 219      }
 220  
 221      private static function readColor($color, $background = false)
 222      {
 223          if (isset($color['rgb'])) {
 224              return (string) $color['rgb'];
 225          } elseif (isset($color['indexed'])) {
 226              return Color::indexedColor($color['indexed'] - 7, $background)->getARGB();
 227          } elseif (isset($color['theme'])) {
 228              if (self::$theme !== null) {
 229                  $returnColour = self::$theme->getColourByIndex((int) $color['theme']);
 230                  if (isset($color['tint'])) {
 231                      $tintAdjust = (float) $color['tint'];
 232                      $returnColour = Color::changeBrightness($returnColour, $tintAdjust);
 233                  }
 234  
 235                  return 'FF' . $returnColour;
 236              }
 237          }
 238  
 239          return ($background) ? 'FFFFFFFF' : 'FF000000';
 240      }
 241  
 242      public function dxfs($readDataOnly = false)
 243      {
 244          $dxfs = [];
 245          if (!$readDataOnly && $this->styleXml) {
 246              //    Conditional Styles
 247              if ($this->styleXml->dxfs) {
 248                  foreach ($this->styleXml->dxfs->dxf as $dxf) {
 249                      $style = new Style(false, true);
 250                      $this->readStyle($style, $dxf);
 251                      $dxfs[] = $style;
 252                  }
 253              }
 254              //    Cell Styles
 255              if ($this->styleXml->cellStyles) {
 256                  foreach ($this->styleXml->cellStyles->cellStyle as $cellStyle) {
 257                      if ((int) ($cellStyle['builtinId']) == 0) {
 258                          if (isset($this->cellStyles[(int) ($cellStyle['xfId'])])) {
 259                              // Set default style
 260                              $style = new Style();
 261                              $this->readStyle($style, $this->cellStyles[(int) ($cellStyle['xfId'])]);
 262  
 263                              // normal style, currently not using it for anything
 264                          }
 265                      }
 266                  }
 267              }
 268          }
 269  
 270          return $dxfs;
 271      }
 272  
 273      public function styles()
 274      {
 275          return $this->styles;
 276      }
 277  
 278      private static function getArrayItem($array, $key = 0)
 279      {
 280          return $array[$key] ?? null;
 281      }
 282  }