Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

   1  <?php
   2  
   3  namespace Box\Spout\Writer\XLSX\Manager\Style;
   4  
   5  use Box\Spout\Common\Entity\Style\Color;
   6  use Box\Spout\Common\Entity\Style\Style;
   7  use Box\Spout\Writer\XLSX\Helper\BorderHelper;
   8  
   9  /**
  10   * Class StyleManager
  11   * Manages styles to be applied to a cell
  12   */
  13  class StyleManager extends \Box\Spout\Writer\Common\Manager\Style\StyleManager
  14  {
  15      /** @var StyleRegistry */
  16      protected $styleRegistry;
  17  
  18      /**
  19       * For empty cells, we can specify a style or not. If no style are specified,
  20       * then the software default will be applied. But sometimes, it may be useful
  21       * to override this default style, for instance if the cell should have a
  22       * background color different than the default one or some borders
  23       * (fonts property don't really matter here).
  24       *
  25       * @param int $styleId
  26       * @return bool Whether the cell should define a custom style
  27       */
  28      public function shouldApplyStyleOnEmptyCell($styleId)
  29      {
  30          $associatedFillId = $this->styleRegistry->getFillIdForStyleId($styleId);
  31          $hasStyleCustomFill = ($associatedFillId !== null && $associatedFillId !== 0);
  32  
  33          $associatedBorderId = $this->styleRegistry->getBorderIdForStyleId($styleId);
  34          $hasStyleCustomBorders = ($associatedBorderId !== null && $associatedBorderId !== 0);
  35  
  36          return ($hasStyleCustomFill || $hasStyleCustomBorders);
  37      }
  38  
  39      /**
  40       * Returns the content of the "styles.xml" file, given a list of styles.
  41       *
  42       * @return string
  43       */
  44      public function getStylesXMLFileContent()
  45      {
  46          $content = <<<'EOD'
  47  <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  48  <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
  49  EOD;
  50  
  51          $content .= $this->getFontsSectionContent();
  52          $content .= $this->getFillsSectionContent();
  53          $content .= $this->getBordersSectionContent();
  54          $content .= $this->getCellStyleXfsSectionContent();
  55          $content .= $this->getCellXfsSectionContent();
  56          $content .= $this->getCellStylesSectionContent();
  57  
  58          $content .= <<<'EOD'
  59  </styleSheet>
  60  EOD;
  61  
  62          return $content;
  63      }
  64  
  65      /**
  66       * Returns the content of the "<fonts>" section.
  67       *
  68       * @return string
  69       */
  70      protected function getFontsSectionContent()
  71      {
  72          $registeredStyles = $this->styleRegistry->getRegisteredStyles();
  73  
  74          $content = '<fonts count="' . count($registeredStyles) . '">';
  75  
  76          /** @var Style $style */
  77          foreach ($registeredStyles as $style) {
  78              $content .= '<font>';
  79  
  80              $content .= '<sz val="' . $style->getFontSize() . '"/>';
  81              $content .= '<color rgb="' . Color::toARGB($style->getFontColor()) . '"/>';
  82              $content .= '<name val="' . $style->getFontName() . '"/>';
  83  
  84              if ($style->isFontBold()) {
  85                  $content .= '<b/>';
  86              }
  87              if ($style->isFontItalic()) {
  88                  $content .= '<i/>';
  89              }
  90              if ($style->isFontUnderline()) {
  91                  $content .= '<u/>';
  92              }
  93              if ($style->isFontStrikethrough()) {
  94                  $content .= '<strike/>';
  95              }
  96  
  97              $content .= '</font>';
  98          }
  99  
 100          $content .= '</fonts>';
 101  
 102          return $content;
 103      }
 104  
 105      /**
 106       * Returns the content of the "<fills>" section.
 107       *
 108       * @return string
 109       */
 110      protected function getFillsSectionContent()
 111      {
 112          $registeredFills = $this->styleRegistry->getRegisteredFills();
 113  
 114          // Excel reserves two default fills
 115          $fillsCount = count($registeredFills) + 2;
 116          $content = sprintf('<fills count="%d">', $fillsCount);
 117  
 118          $content .= '<fill><patternFill patternType="none"/></fill>';
 119          $content .= '<fill><patternFill patternType="gray125"/></fill>';
 120  
 121          // The other fills are actually registered by setting a background color
 122          foreach ($registeredFills as $styleId) {
 123              /** @var Style $style */
 124              $style = $this->styleRegistry->getStyleFromStyleId($styleId);
 125  
 126              $backgroundColor = $style->getBackgroundColor();
 127              $content .= sprintf(
 128                  '<fill><patternFill patternType="solid"><fgColor rgb="%s"/></patternFill></fill>',
 129                  $backgroundColor
 130              );
 131          }
 132  
 133          $content .= '</fills>';
 134  
 135          return $content;
 136      }
 137  
 138      /**
 139       * Returns the content of the "<borders>" section.
 140       *
 141       * @return string
 142       */
 143      protected function getBordersSectionContent()
 144      {
 145          $registeredBorders = $this->styleRegistry->getRegisteredBorders();
 146  
 147          // There is one default border with index 0
 148          $borderCount = count($registeredBorders) + 1;
 149  
 150          $content = '<borders count="' . $borderCount . '">';
 151  
 152          // Default border starting at index 0
 153          $content .= '<border><left/><right/><top/><bottom/></border>';
 154  
 155          foreach ($registeredBorders as $styleId) {
 156              /** @var \Box\Spout\Common\Entity\Style\Style $style */
 157              $style = $this->styleRegistry->getStyleFromStyleId($styleId);
 158              $border = $style->getBorder();
 159              $content .= '<border>';
 160  
 161              // @link https://github.com/box/spout/issues/271
 162              $sortOrder = ['left', 'right', 'top', 'bottom'];
 163  
 164              foreach ($sortOrder as $partName) {
 165                  if ($border->hasPart($partName)) {
 166                      /** @var $part \Box\Spout\Common\Entity\Style\BorderPart */
 167                      $part = $border->getPart($partName);
 168                      $content .= BorderHelper::serializeBorderPart($part);
 169                  }
 170              }
 171  
 172              $content .= '</border>';
 173          }
 174  
 175          $content .= '</borders>';
 176  
 177          return $content;
 178      }
 179  
 180      /**
 181       * Returns the content of the "<cellStyleXfs>" section.
 182       *
 183       * @return string
 184       */
 185      protected function getCellStyleXfsSectionContent()
 186      {
 187          return <<<'EOD'
 188  <cellStyleXfs count="1">
 189      <xf borderId="0" fillId="0" fontId="0" numFmtId="0"/>
 190  </cellStyleXfs>
 191  EOD;
 192      }
 193  
 194      /**
 195       * Returns the content of the "<cellXfs>" section.
 196       *
 197       * @return string
 198       */
 199      protected function getCellXfsSectionContent()
 200      {
 201          $registeredStyles = $this->styleRegistry->getRegisteredStyles();
 202  
 203          $content = '<cellXfs count="' . count($registeredStyles) . '">';
 204  
 205          foreach ($registeredStyles as $style) {
 206              $styleId = $style->getId();
 207              $fillId = $this->getFillIdForStyleId($styleId);
 208              $borderId = $this->getBorderIdForStyleId($styleId);
 209  
 210              $content .= '<xf numFmtId="0" fontId="' . $styleId . '" fillId="' . $fillId . '" borderId="' . $borderId . '" xfId="0"';
 211  
 212              if ($style->shouldApplyFont()) {
 213                  $content .= ' applyFont="1"';
 214              }
 215  
 216              $content .= sprintf(' applyBorder="%d"', $style->shouldApplyBorder() ? 1 : 0);
 217  
 218              if ($style->shouldWrapText()) {
 219                  $content .= ' applyAlignment="1">';
 220                  $content .= '<alignment wrapText="1"/>';
 221                  $content .= '</xf>';
 222              } else {
 223                  $content .= '/>';
 224              }
 225          }
 226  
 227          $content .= '</cellXfs>';
 228  
 229          return $content;
 230      }
 231  
 232      /**
 233       * Returns the fill ID associated to the given style ID.
 234       * For the default style, we don't a fill.
 235       *
 236       * @param int $styleId
 237       * @return int
 238       */
 239      private function getFillIdForStyleId($styleId)
 240      {
 241          // For the default style (ID = 0), we don't want to override the fill.
 242          // Otherwise all cells of the spreadsheet will have a background color.
 243          $isDefaultStyle = ($styleId === 0);
 244  
 245          return $isDefaultStyle ? 0 : ($this->styleRegistry->getFillIdForStyleId($styleId) ?: 0);
 246      }
 247  
 248      /**
 249       * Returns the fill ID associated to the given style ID.
 250       * For the default style, we don't a border.
 251       *
 252       * @param int $styleId
 253       * @return int
 254       */
 255      private function getBorderIdForStyleId($styleId)
 256      {
 257          // For the default style (ID = 0), we don't want to override the border.
 258          // Otherwise all cells of the spreadsheet will have a border.
 259          $isDefaultStyle = ($styleId === 0);
 260  
 261          return $isDefaultStyle ? 0 : ($this->styleRegistry->getBorderIdForStyleId($styleId) ?: 0);
 262      }
 263  
 264      /**
 265       * Returns the content of the "<cellStyles>" section.
 266       *
 267       * @return string
 268       */
 269      protected function getCellStylesSectionContent()
 270      {
 271          return <<<'EOD'
 272  <cellStyles count="1">
 273      <cellStyle builtinId="0" name="Normal" xfId="0"/>
 274  </cellStyles>
 275  EOD;
 276      }
 277  }