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