Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 39 and 401]

   1  <?php
   2  
   3  namespace Box\Spout\Writer\ODS\Manager\Style;
   4  
   5  use Box\Spout\Common\Entity\Style\BorderPart;
   6  use Box\Spout\Common\Entity\Style\CellAlignment;
   7  use Box\Spout\Writer\Common\Entity\Worksheet;
   8  use Box\Spout\Writer\ODS\Helper\BorderHelper;
   9  
  10  /**
  11   * Class StyleManager
  12   * Manages styles to be applied to a cell
  13   */
  14  class StyleManager extends \Box\Spout\Writer\Common\Manager\Style\StyleManager
  15  {
  16      /** @var StyleRegistry */
  17      protected $styleRegistry;
  18  
  19      /**
  20       * Returns the content of the "styles.xml" file, given a list of styles.
  21       *
  22       * @param int $numWorksheets Number of worksheets created
  23       * @return string
  24       */
  25      public function getStylesXMLFileContent($numWorksheets)
  26      {
  27          $content = <<<'EOD'
  28  <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  29  <office:document-styles office:version="1.2" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:msoxl="http://schemas.microsoft.com/office/excel/formula" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:xlink="http://www.w3.org/1999/xlink">
  30  EOD;
  31  
  32          $content .= $this->getFontFaceSectionContent();
  33          $content .= $this->getStylesSectionContent();
  34          $content .= $this->getAutomaticStylesSectionContent($numWorksheets);
  35          $content .= $this->getMasterStylesSectionContent($numWorksheets);
  36  
  37          $content .= <<<'EOD'
  38  </office:document-styles>
  39  EOD;
  40  
  41          return $content;
  42      }
  43  
  44      /**
  45       * Returns the content of the "<office:font-face-decls>" section, inside "styles.xml" file.
  46       *
  47       * @return string
  48       */
  49      protected function getFontFaceSectionContent()
  50      {
  51          $content = '<office:font-face-decls>';
  52          foreach ($this->styleRegistry->getUsedFonts() as $fontName) {
  53              $content .= '<style:font-face style:name="' . $fontName . '" svg:font-family="' . $fontName . '"/>';
  54          }
  55          $content .= '</office:font-face-decls>';
  56  
  57          return $content;
  58      }
  59  
  60      /**
  61       * Returns the content of the "<office:styles>" section, inside "styles.xml" file.
  62       *
  63       * @return string
  64       */
  65      protected function getStylesSectionContent()
  66      {
  67          $defaultStyle = $this->getDefaultStyle();
  68  
  69          return <<<EOD
  70  <office:styles>
  71      <number:number-style style:name="N0">
  72          <number:number number:min-integer-digits="1"/>
  73      </number:number-style>
  74      <style:style style:data-style-name="N0" style:family="table-cell" style:name="Default">
  75          <style:table-cell-properties fo:background-color="transparent" style:vertical-align="automatic"/>
  76          <style:text-properties fo:color="#{$defaultStyle->getFontColor()}"
  77                                 fo:font-size="{$defaultStyle->getFontSize()}pt" style:font-size-asian="{$defaultStyle->getFontSize()}pt" style:font-size-complex="{$defaultStyle->getFontSize()}pt"
  78                                 style:font-name="{$defaultStyle->getFontName()}" style:font-name-asian="{$defaultStyle->getFontName()}" style:font-name-complex="{$defaultStyle->getFontName()}"/>
  79      </style:style>
  80  </office:styles>
  81  EOD;
  82      }
  83  
  84      /**
  85       * Returns the content of the "<office:automatic-styles>" section, inside "styles.xml" file.
  86       *
  87       * @param int $numWorksheets Number of worksheets created
  88       * @return string
  89       */
  90      protected function getAutomaticStylesSectionContent($numWorksheets)
  91      {
  92          $content = '<office:automatic-styles>';
  93  
  94          for ($i = 1; $i <= $numWorksheets; $i++) {
  95              $content .= <<<EOD
  96  <style:page-layout style:name="pm$i">
  97      <style:page-layout-properties style:first-page-number="continue" style:print="objects charts drawings" style:table-centering="none"/>
  98      <style:header-style/>
  99      <style:footer-style/>
 100  </style:page-layout>
 101  EOD;
 102          }
 103  
 104          $content .= '</office:automatic-styles>';
 105  
 106          return $content;
 107      }
 108  
 109      /**
 110       * Returns the content of the "<office:master-styles>" section, inside "styles.xml" file.
 111       *
 112       * @param int $numWorksheets Number of worksheets created
 113       * @return string
 114       */
 115      protected function getMasterStylesSectionContent($numWorksheets)
 116      {
 117          $content = '<office:master-styles>';
 118  
 119          for ($i = 1; $i <= $numWorksheets; $i++) {
 120              $content .= <<<EOD
 121  <style:master-page style:name="mp$i" style:page-layout-name="pm$i">
 122      <style:header/>
 123      <style:header-left style:display="false"/>
 124      <style:footer/>
 125      <style:footer-left style:display="false"/>
 126  </style:master-page>
 127  EOD;
 128          }
 129  
 130          $content .= '</office:master-styles>';
 131  
 132          return $content;
 133      }
 134  
 135      /**
 136       * Returns the contents of the "<office:font-face-decls>" section, inside "content.xml" file.
 137       *
 138       * @return string
 139       */
 140      public function getContentXmlFontFaceSectionContent()
 141      {
 142          $content = '<office:font-face-decls>';
 143          foreach ($this->styleRegistry->getUsedFonts() as $fontName) {
 144              $content .= '<style:font-face style:name="' . $fontName . '" svg:font-family="' . $fontName . '"/>';
 145          }
 146          $content .= '</office:font-face-decls>';
 147  
 148          return $content;
 149      }
 150  
 151      /**
 152       * Returns the contents of the "<office:automatic-styles>" section, inside "content.xml" file.
 153       *
 154       * @param Worksheet[] $worksheets
 155       * @return string
 156       */
 157      public function getContentXmlAutomaticStylesSectionContent($worksheets)
 158      {
 159          $content = '<office:automatic-styles>';
 160  
 161          foreach ($this->styleRegistry->getRegisteredStyles() as $style) {
 162              $content .= $this->getStyleSectionContent($style);
 163          }
 164  
 165          $content .= <<<'EOD'
 166  <style:style style:family="table-column" style:name="co1">
 167      <style:table-column-properties fo:break-before="auto"/>
 168  </style:style>
 169  <style:style style:family="table-row" style:name="ro1">
 170      <style:table-row-properties fo:break-before="auto" style:row-height="15pt" style:use-optimal-row-height="true"/>
 171  </style:style>
 172  EOD;
 173  
 174          foreach ($worksheets as $worksheet) {
 175              $worksheetId = $worksheet->getId();
 176              $isSheetVisible = $worksheet->getExternalSheet()->isVisible() ? 'true' : 'false';
 177  
 178              $content .= <<<EOD
 179  <style:style style:family="table" style:master-page-name="mp$worksheetId" style:name="ta$worksheetId">
 180      <style:table-properties style:writing-mode="lr-tb" table:display="$isSheetVisible"/>
 181  </style:style>
 182  EOD;
 183          }
 184  
 185          $content .= '</office:automatic-styles>';
 186  
 187          return $content;
 188      }
 189  
 190      /**
 191       * Returns the contents of the "<style:style>" section, inside "<office:automatic-styles>" section
 192       *
 193       * @param \Box\Spout\Common\Entity\Style\Style $style
 194       * @return string
 195       */
 196      protected function getStyleSectionContent($style)
 197      {
 198          $styleIndex = $style->getId() + 1; // 1-based
 199  
 200          $content = '<style:style style:data-style-name="N0" style:family="table-cell" style:name="ce' . $styleIndex . '" style:parent-style-name="Default">';
 201  
 202          $content .= $this->getTextPropertiesSectionContent($style);
 203          $content .= $this->getParagraphPropertiesSectionContent($style);
 204          $content .= $this->getTableCellPropertiesSectionContent($style);
 205  
 206          $content .= '</style:style>';
 207  
 208          return $content;
 209      }
 210  
 211      /**
 212       * Returns the contents of the "<style:text-properties>" section, inside "<style:style>" section
 213       *
 214       * @param \Box\Spout\Common\Entity\Style\Style $style
 215       * @return string
 216       */
 217      private function getTextPropertiesSectionContent($style)
 218      {
 219          if (!$style->shouldApplyFont()) {
 220              return '';
 221          }
 222  
 223          return '<style:text-properties '
 224              . $this->getFontSectionContent($style)
 225              . '/>';
 226      }
 227  
 228      /**
 229       * Returns the contents of the fonts definition section, inside "<style:text-properties>" section
 230       *
 231       * @param \Box\Spout\Common\Entity\Style\Style $style
 232       *
 233       * @return string
 234       */
 235      private function getFontSectionContent($style)
 236      {
 237          $defaultStyle = $this->getDefaultStyle();
 238          $content = '';
 239  
 240          $fontColor = $style->getFontColor();
 241          if ($fontColor !== $defaultStyle->getFontColor()) {
 242              $content .= ' fo:color="#' . $fontColor . '"';
 243          }
 244  
 245          $fontName = $style->getFontName();
 246          if ($fontName !== $defaultStyle->getFontName()) {
 247              $content .= ' style:font-name="' . $fontName . '" style:font-name-asian="' . $fontName . '" style:font-name-complex="' . $fontName . '"';
 248          }
 249  
 250          $fontSize = $style->getFontSize();
 251          if ($fontSize !== $defaultStyle->getFontSize()) {
 252              $content .= ' fo:font-size="' . $fontSize . 'pt" style:font-size-asian="' . $fontSize . 'pt" style:font-size-complex="' . $fontSize . 'pt"';
 253          }
 254  
 255          if ($style->isFontBold()) {
 256              $content .= ' fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"';
 257          }
 258          if ($style->isFontItalic()) {
 259              $content .= ' fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic"';
 260          }
 261          if ($style->isFontUnderline()) {
 262              $content .= ' style:text-underline-style="solid" style:text-underline-type="single"';
 263          }
 264          if ($style->isFontStrikethrough()) {
 265              $content .= ' style:text-line-through-style="solid"';
 266          }
 267  
 268          return $content;
 269      }
 270  
 271      /**
 272       * Returns the contents of the "<style:paragraph-properties>" section, inside "<style:style>" section
 273       *
 274       * @param \Box\Spout\Common\Entity\Style\Style $style
 275       *
 276       * @return string
 277       */
 278      private function getParagraphPropertiesSectionContent($style)
 279      {
 280          if (!$style->shouldApplyCellAlignment()) {
 281              return '';
 282          }
 283  
 284          return '<style:paragraph-properties '
 285              . $this->getCellAlignmentSectionContent($style)
 286              . '/>';
 287      }
 288  
 289      /**
 290       * Returns the contents of the cell alignment definition for the "<style:paragraph-properties>" section
 291       *
 292       * @param \Box\Spout\Common\Entity\Style\Style $style
 293       *
 294       * @return string
 295       */
 296      private function getCellAlignmentSectionContent($style)
 297      {
 298          return \sprintf(
 299              ' fo:text-align="%s" ',
 300              $this->transformCellAlignment($style->getCellAlignment())
 301          );
 302      }
 303  
 304      /**
 305       * Even though "left" and "right" alignments are part of the spec, and interpreted
 306       * respectively as "start" and "end", using the recommended values increase compatibility
 307       * with software that will read the created ODS file.
 308       *
 309       * @param string $cellAlignment
 310       *
 311       * @return string
 312       */
 313      private function transformCellAlignment($cellAlignment)
 314      {
 315          switch ($cellAlignment) {
 316              case CellAlignment::LEFT: return 'start';
 317              case CellAlignment::RIGHT: return 'end';
 318              default: return $cellAlignment;
 319          }
 320      }
 321  
 322      /**
 323       * Returns the contents of the "<style:table-cell-properties>" section, inside "<style:style>" section
 324       *
 325       * @param \Box\Spout\Common\Entity\Style\Style $style
 326       * @return string
 327       */
 328      private function getTableCellPropertiesSectionContent($style)
 329      {
 330          $content = '<style:table-cell-properties ';
 331  
 332          if ($style->shouldWrapText()) {
 333              $content .= $this->getWrapTextXMLContent();
 334          }
 335  
 336          if ($style->shouldApplyBorder()) {
 337              $content .= $this->getBorderXMLContent($style);
 338          }
 339  
 340          if ($style->shouldApplyBackgroundColor()) {
 341              $content .= $this->getBackgroundColorXMLContent($style);
 342          }
 343  
 344          $content .= '/>';
 345  
 346          return $content;
 347      }
 348  
 349      /**
 350       * Returns the contents of the wrap text definition for the "<style:table-cell-properties>" section
 351       *
 352       * @return string
 353       */
 354      private function getWrapTextXMLContent()
 355      {
 356          return ' fo:wrap-option="wrap" style:vertical-align="automatic" ';
 357      }
 358  
 359      /**
 360       * Returns the contents of the borders definition for the "<style:table-cell-properties>" section
 361       *
 362       * @param \Box\Spout\Common\Entity\Style\Style $style
 363       * @return string
 364       */
 365      private function getBorderXMLContent($style)
 366      {
 367          $borders = \array_map(function (BorderPart $borderPart) {
 368              return BorderHelper::serializeBorderPart($borderPart);
 369          }, $style->getBorder()->getParts());
 370  
 371          return \sprintf(' %s ', \implode(' ', $borders));
 372      }
 373  
 374      /**
 375       * Returns the contents of the background color definition for the "<style:table-cell-properties>" section
 376       *
 377       * @param \Box\Spout\Common\Entity\Style\Style $style
 378       * @return string
 379       */
 380      private function getBackgroundColorXMLContent($style)
 381      {
 382          return \sprintf(' fo:background-color="#%s" ', $style->getBackgroundColor());
 383      }
 384  }