StringTable.php 9.7 KB

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