Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

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

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
   4  
   5  use PhpOffice\PhpSpreadsheet\Cell\DataType;
   6  use PhpOffice\PhpSpreadsheet\RichText\RichText;
   7  use PhpOffice\PhpSpreadsheet\RichText\Run;
   8  use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
   9  use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
  10  use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
  11  
  12  class StringTable extends WriterPart
  13  {
  14      /**
  15       * Create worksheet stringtable.
  16       *
  17       * @param Worksheet $worksheet Worksheet
  18       * @param string[] $existingTable Existing table to eventually merge with
  19       *
  20       * @return string[] String table for worksheet
  21       */
  22      public function createStringTable(Worksheet $worksheet, $existingTable = null)
  23      {
  24          // Create string lookup table
  25          $aStringTable = [];
  26          $cellCollection = null;
  27          $aFlippedStringTable = null; // For faster lookup
  28  
  29          // Is an existing table given?
  30          if (($existingTable !== null) && is_array($existingTable)) {
  31              $aStringTable = $existingTable;
  32          }
  33  
  34          // Fill index array
  35          $aFlippedStringTable = $this->flipStringTable($aStringTable);
  36  
  37          // Loop through cells
  38          foreach ($worksheet->getCoordinates() as $coordinate) {
  39              $cell = $worksheet->getCell($coordinate);
  40              $cellValue = $cell->getValue();
  41              if (
  42                  !is_object($cellValue) &&
  43                  ($cellValue !== null) &&
  44                  $cellValue !== '' &&
  45                  ($cell->getDataType() == DataType::TYPE_STRING || $cell->getDataType() == DataType::TYPE_STRING2 || $cell->getDataType() == DataType::TYPE_NULL) &&
  46                  !isset($aFlippedStringTable[$cellValue])
  47              ) {
  48                  $aStringTable[] = $cellValue;
  49                  $aFlippedStringTable[$cellValue] = true;
  50              } elseif (
  51                  $cellValue instanceof RichText &&
  52                  ($cellValue !== null) &&
  53                  !isset($aFlippedStringTable[$cellValue->getHashCode()])
  54              ) {
  55                  $aStringTable[] = $cellValue;
  56                  $aFlippedStringTable[$cellValue->getHashCode()] = true;
  57              }
  58          }
  59  
  60          return $aStringTable;
  61      }
  62  
  63      /**
  64       * Write string table to XML format.
  65       *
  66       * @param string[] $stringTable
  67       *
  68       * @return string XML Output
  69       */
  70      public function writeStringTable(array $stringTable)
  71      {
  72          // Create XML writer
  73          $objWriter = null;
  74          if ($this->getParentWriter()->getUseDiskCaching()) {
  75              $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
  76          } else {
  77              $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
  78          }
  79  
  80          // XML header
  81          $objWriter->startDocument('1.0', 'UTF-8', 'yes');
  82  
  83          // String table
  84          $objWriter->startElement('sst');
  85          $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
  86          $objWriter->writeAttribute('uniqueCount', count($stringTable));
  87  
  88          // Loop through string table
  89          foreach ($stringTable as $textElement) {
  90              $objWriter->startElement('si');
  91  
  92              if (!$textElement instanceof RichText) {
  93                  $textToWrite = StringHelper::controlCharacterPHP2OOXML($textElement);
  94                  $objWriter->startElement('t');
  95                  if ($textToWrite !== trim($textToWrite)) {
  96                      $objWriter->writeAttribute('xml:space', 'preserve');
  97                  }
  98                  $objWriter->writeRawData($textToWrite);
  99                  $objWriter->endElement();
 100              } elseif ($textElement instanceof RichText) {
 101                  $this->writeRichText($objWriter, $textElement);
 102              }
 103  
 104              $objWriter->endElement();
 105          }
 106  
 107          $objWriter->endElement();
 108  
 109          return $objWriter->getData();
 110      }
 111  
 112      /**
 113       * Write Rich Text.
 114       *
 115       * @param string $prefix Optional Namespace prefix
 116       */
 117      public function writeRichText(XMLWriter $objWriter, RichText $richText, $prefix = null): void
 118      {
 119          if ($prefix !== null) {
 120              $prefix .= ':';
 121          }
 122  
 123          // Loop through rich text elements
 124          $elements = $richText->getRichTextElements();
 125          foreach ($elements as $element) {
 126              // r
 127              $objWriter->startElement($prefix . 'r');
 128  
 129              // rPr
 130              if ($element instanceof Run) {
 131                  // rPr
 132                  $objWriter->startElement($prefix . 'rPr');
 133  
 134                  // rFont
 135                  $objWriter->startElement($prefix . 'rFont');
 136                  $objWriter->writeAttribute('val', $element->getFont()->getName());
 137                  $objWriter->endElement();
 138  
 139                  // Bold
 140                  $objWriter->startElement($prefix . 'b');
 141                  $objWriter->writeAttribute('val', ($element->getFont()->getBold() ? 'true' : 'false'));
 142                  $objWriter->endElement();
 143  
 144                  // Italic
 145                  $objWriter->startElement($prefix . 'i');
 146                  $objWriter->writeAttribute('val', ($element->getFont()->getItalic() ? 'true' : 'false'));
 147                  $objWriter->endElement();
 148  
 149                  // Superscript / subscript
 150                  if ($element->getFont()->getSuperscript() || $element->getFont()->getSubscript()) {
 151                      $objWriter->startElement($prefix . 'vertAlign');
 152                      if ($element->getFont()->getSuperscript()) {
 153                          $objWriter->writeAttribute('val', 'superscript');
 154                      } elseif ($element->getFont()->getSubscript()) {
 155                          $objWriter->writeAttribute('val', 'subscript');
 156                      }
 157                      $objWriter->endElement();
 158                  }
 159  
 160                  // Strikethrough
 161                  $objWriter->startElement($prefix . 'strike');
 162                  $objWriter->writeAttribute('val', ($element->getFont()->getStrikethrough() ? 'true' : 'false'));
 163                  $objWriter->endElement();
 164  
 165                  // Color
 166                  $objWriter->startElement($prefix . 'color');
 167                  $objWriter->writeAttribute('rgb', $element->getFont()->getColor()->getARGB());
 168                  $objWriter->endElement();
 169  
 170                  // Size
 171                  $objWriter->startElement($prefix . 'sz');
 172                  $objWriter->writeAttribute('val', $element->getFont()->getSize());
 173                  $objWriter->endElement();
 174  
 175                  // Underline
 176                  $objWriter->startElement($prefix . 'u');
 177                  $objWriter->writeAttribute('val', $element->getFont()->getUnderline());
 178                  $objWriter->endElement();
 179  
 180                  $objWriter->endElement();
 181              }
 182  
 183              // t
 184              $objWriter->startElement($prefix . 't');
 185              $objWriter->writeAttribute('xml:space', 'preserve');
 186              $objWriter->writeRawData(StringHelper::controlCharacterPHP2OOXML($element->getText()));
 187              $objWriter->endElement();
 188  
 189              $objWriter->endElement();
 190          }
 191      }
 192  
 193      /**
 194       * Write Rich Text.
 195       *
 196       * @param RichText|string $richText text string or Rich text
 197       * @param string $prefix Optional Namespace prefix
 198       */
 199      public function writeRichTextForCharts(XMLWriter $objWriter, $richText = null, $prefix = null): void
 200      {
 201          if (!$richText instanceof RichText) {
 202              $textRun = $richText;
 203              $richText = new RichText();
 204              $richText->createTextRun($textRun);
 205          }
 206  
 207          if ($prefix !== null) {
 208              $prefix .= ':';
 209          }
 210  
 211          // Loop through rich text elements
 212          $elements = $richText->getRichTextElements();
 213          foreach ($elements as $element) {
 214              // r
 215              $objWriter->startElement($prefix . 'r');
 216  
 217              // rPr
 218              $objWriter->startElement($prefix . 'rPr');
 219  
 220              // Bold
 221              $objWriter->writeAttribute('b', ($element->getFont()->getBold() ? 1 : 0));
 222              // Italic
 223              $objWriter->writeAttribute('i', ($element->getFont()->getItalic() ? 1 : 0));
 224              // Underline
 225              $underlineType = $element->getFont()->getUnderline();
 226              switch ($underlineType) {
 227                  case 'single':
 228                      $underlineType = 'sng';
 229  
 230                      break;
 231                  case 'double':
 232                      $underlineType = 'dbl';
 233  
 234                      break;
 235              }
 236              $objWriter->writeAttribute('u', $underlineType);
 237              // Strikethrough
 238              $objWriter->writeAttribute('strike', ($element->getFont()->getStrikethrough() ? 'sngStrike' : 'noStrike'));
 239  
 240              // rFont
 241              $objWriter->startElement($prefix . 'latin');
 242              $objWriter->writeAttribute('typeface', $element->getFont()->getName());
 243              $objWriter->endElement();
 244  
 245              $objWriter->endElement();
 246  
 247              // t
 248              $objWriter->startElement($prefix . 't');
 249              $objWriter->writeRawData(StringHelper::controlCharacterPHP2OOXML($element->getText()));
 250              $objWriter->endElement();
 251  
 252              $objWriter->endElement();
 253          }
 254      }
 255  
 256      /**
 257       * Flip string table (for index searching).
 258       *
 259       * @param array $stringTable Stringtable
 260       *
 261       * @return array
 262       */
 263      public function flipStringTable(array $stringTable)
 264      {
 265          // Return value
 266          $returnValue = [];
 267  
 268          // Loop through stringtable and add flipped items to $returnValue
 269          foreach ($stringTable as $key => $value) {
 270              if (!$value instanceof RichText) {
 271                  $returnValue[$value] = $key;
 272              } elseif ($value instanceof RichText) {
 273                  $returnValue[$value->getHashCode()] = $key;
 274              }
 275          }
 276  
 277          return $returnValue;
 278      }
 279  }