ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Html.php
Go to the documentation of this file.
1 <?php
2 
4 
5 use DOMDocument;
6 use DOMElement;
7 use DOMNode;
8 use DOMText;
19 use Throwable;
20 
22 class Html extends BaseReader
23 {
27  const TEST_SAMPLE_SIZE = 2048;
28 
34  protected $inputEncoding = 'ANSI';
35 
41  protected $sheetIndex = 0;
42 
48  protected $formats = [
49  'h1' => [
50  'font' => [
51  'bold' => true,
52  'size' => 24,
53  ],
54  ], // Bold, 24pt
55  'h2' => [
56  'font' => [
57  'bold' => true,
58  'size' => 18,
59  ],
60  ], // Bold, 18pt
61  'h3' => [
62  'font' => [
63  'bold' => true,
64  'size' => 13.5,
65  ],
66  ], // Bold, 13.5pt
67  'h4' => [
68  'font' => [
69  'bold' => true,
70  'size' => 12,
71  ],
72  ], // Bold, 12pt
73  'h5' => [
74  'font' => [
75  'bold' => true,
76  'size' => 10,
77  ],
78  ], // Bold, 10pt
79  'h6' => [
80  'font' => [
81  'bold' => true,
82  'size' => 7.5,
83  ],
84  ], // Bold, 7.5pt
85  'a' => [
86  'font' => [
87  'underline' => true,
88  'color' => [
89  'argb' => Color::COLOR_BLUE,
90  ],
91  ],
92  ], // Blue underlined
93  'hr' => [
94  'borders' => [
95  'bottom' => [
96  'borderStyle' => Border::BORDER_THIN,
97  'color' => [
99  ],
100  ],
101  ],
102  ], // Bottom border
103  'strong' => [
104  'font' => [
105  'bold' => true,
106  ],
107  ], // Bold
108  'b' => [
109  'font' => [
110  'bold' => true,
111  ],
112  ], // Bold
113  'i' => [
114  'font' => [
115  'italic' => true,
116  ],
117  ], // Italic
118  'em' => [
119  'font' => [
120  'italic' => true,
121  ],
122  ], // Italic
123  ];
124 
125  protected $rowspan = [];
126 
130  public function __construct()
131  {
132  parent::__construct();
133  $this->securityScanner = XmlScanner::getInstance($this);
134  }
135 
143  public function canRead($pFilename)
144  {
145  // Check if file exists
146  try {
147  $this->openFile($pFilename);
148  } catch (Exception $e) {
149  return false;
150  }
151 
152  $beginning = $this->readBeginning();
153  $startWithTag = self::startsWithTag($beginning);
154  $containsTags = self::containsTags($beginning);
155  $endsWithTag = self::endsWithTag($this->readEnding());
156 
157  fclose($this->fileHandle);
158 
159  return $startWithTag && $containsTags && $endsWithTag;
160  }
161 
162  private function readBeginning()
163  {
164  fseek($this->fileHandle, 0);
165 
166  return fread($this->fileHandle, self::TEST_SAMPLE_SIZE);
167  }
168 
169  private function readEnding()
170  {
171  $meta = stream_get_meta_data($this->fileHandle);
172  $filename = $meta['uri'];
173 
174  $size = filesize($filename);
175  if ($size === 0) {
176  return '';
177  }
178 
179  $blockSize = self::TEST_SAMPLE_SIZE;
180  if ($size < $blockSize) {
181  $blockSize = $size;
182  }
183 
184  fseek($this->fileHandle, $size - $blockSize);
185 
186  return fread($this->fileHandle, $blockSize);
187  }
188 
189  private static function startsWithTag($data)
190  {
191  return '<' === substr(trim($data), 0, 1);
192  }
193 
194  private static function endsWithTag($data)
195  {
196  return '>' === substr(trim($data), -1, 1);
197  }
198 
199  private static function containsTags($data)
200  {
201  return strlen($data) !== strlen(strip_tags($data));
202  }
203 
211  public function load($pFilename)
212  {
213  // Create new Spreadsheet
214  $spreadsheet = new Spreadsheet();
215 
216  // Load into this instance
217  return $this->loadIntoExisting($pFilename, $spreadsheet);
218  }
219 
231  public function setInputEncoding($pValue)
232  {
233  $this->inputEncoding = $pValue;
234 
235  return $this;
236  }
237 
247  public function getInputEncoding()
248  {
249  return $this->inputEncoding;
250  }
251 
252  // Data Array used for testing only, should write to Spreadsheet object on completion of tests
253  protected $dataArray = [];
254 
255  protected $tableLevel = 0;
256 
257  protected $nestedColumn = ['A'];
258 
259  protected function setTableStartColumn($column)
260  {
261  if ($this->tableLevel == 0) {
262  $column = 'A';
263  }
265  $this->nestedColumn[$this->tableLevel] = $column;
266 
267  return $this->nestedColumn[$this->tableLevel];
268  }
269 
270  protected function getTableStartColumn()
271  {
272  return $this->nestedColumn[$this->tableLevel];
273  }
274 
275  protected function releaseTableStartColumn()
276  {
278 
279  return array_pop($this->nestedColumn);
280  }
281 
282  protected function flushCell(Worksheet $sheet, $column, $row, &$cellContent): void
283  {
284  if (is_string($cellContent)) {
285  // Simple String content
286  if (trim($cellContent) > '') {
287  // Only actually write it if there's content in the string
288  // Write to worksheet to be done here...
289  // ... we return the cell so we can mess about with styles more easily
290  $sheet->setCellValue($column . $row, $cellContent);
291  $this->dataArray[$row][$column] = $cellContent;
292  }
293  } else {
294  // We have a Rich Text run
295  // TODO
296  $this->dataArray[$row][$column] = 'RICH TEXT: ' . $cellContent;
297  }
298  $cellContent = (string) '';
299  }
300 
301  private function processDomElementBody(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child): void
302  {
303  $attributeArray = [];
304  foreach ($child->attributes as $attribute) {
305  $attributeArray[$attribute->name] = $attribute->value;
306  }
307 
308  if ($child->nodeName === 'body') {
309  $row = 1;
310  $column = 'A';
311  $cellContent = '';
312  $this->tableLevel = 0;
313  $this->processDomElement($child, $sheet, $row, $column, $cellContent);
314  } else {
315  $this->processDomElementTitle($sheet, $row, $column, $cellContent, $child, $attributeArray);
316  }
317  }
318 
319  private function processDomElementTitle(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
320  {
321  if ($child->nodeName === 'title') {
322  $this->processDomElement($child, $sheet, $row, $column, $cellContent);
323  $sheet->setTitle($cellContent, true, true);
324  $cellContent = '';
325  } else {
326  $this->processDomElementSpanEtc($sheet, $row, $column, $cellContent, $child, $attributeArray);
327  }
328  }
329 
330  private static $spanEtc = ['span', 'div', 'font', 'i', 'em', 'strong', 'b'];
331 
332  private function processDomElementSpanEtc(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
333  {
334  if (in_array($child->nodeName, self::$spanEtc)) {
335  if (isset($attributeArray['class']) && $attributeArray['class'] === 'comment') {
336  $sheet->getComment($column . $row)
337  ->getText()
338  ->createTextRun($child->textContent);
339  }
340  $this->processDomElement($child, $sheet, $row, $column, $cellContent);
341 
342  if (isset($this->formats[$child->nodeName])) {
343  $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
344  }
345  } else {
346  $this->processDomElementHr($sheet, $row, $column, $cellContent, $child, $attributeArray);
347  }
348  }
349 
350  private function processDomElementHr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
351  {
352  if ($child->nodeName === 'hr') {
353  $this->flushCell($sheet, $column, $row, $cellContent);
354  ++$row;
355  if (isset($this->formats[$child->nodeName])) {
356  $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
357  }
358  ++$row;
359  }
360  // fall through to br
361  $this->processDomElementBr($sheet, $row, $column, $cellContent, $child, $attributeArray);
362  }
363 
364  private function processDomElementBr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
365  {
366  if ($child->nodeName === 'br' || $child->nodeName === 'hr') {
367  if ($this->tableLevel > 0) {
368  // If we're inside a table, replace with a \n and set the cell to wrap
369  $cellContent .= "\n";
370  $sheet->getStyle($column . $row)->getAlignment()->setWrapText(true);
371  } else {
372  // Otherwise flush our existing content and move the row cursor on
373  $this->flushCell($sheet, $column, $row, $cellContent);
374  ++$row;
375  }
376  } else {
377  $this->processDomElementA($sheet, $row, $column, $cellContent, $child, $attributeArray);
378  }
379  }
380 
381  private function processDomElementA(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
382  {
383  if ($child->nodeName === 'a') {
384  foreach ($attributeArray as $attributeName => $attributeValue) {
385  switch ($attributeName) {
386  case 'href':
387  $sheet->getCell($column . $row)->getHyperlink()->setUrl($attributeValue);
388  if (isset($this->formats[$child->nodeName])) {
389  $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
390  }
391 
392  break;
393  case 'class':
394  if ($attributeValue === 'comment-indicator') {
395  break; // Ignore - it's just a red square.
396  }
397  }
398  }
399  // no idea why this should be needed
400  //$cellContent .= ' ';
401  $this->processDomElement($child, $sheet, $row, $column, $cellContent);
402  } else {
403  $this->processDomElementH1Etc($sheet, $row, $column, $cellContent, $child, $attributeArray);
404  }
405  }
406 
407  private static $h1Etc = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'p'];
408 
409  private function processDomElementH1Etc(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
410  {
411  if (in_array($child->nodeName, self::$h1Etc)) {
412  if ($this->tableLevel > 0) {
413  // If we're inside a table, replace with a \n
414  $cellContent .= $cellContent ? "\n" : '';
415  $sheet->getStyle($column . $row)->getAlignment()->setWrapText(true);
416  $this->processDomElement($child, $sheet, $row, $column, $cellContent);
417  } else {
418  if ($cellContent > '') {
419  $this->flushCell($sheet, $column, $row, $cellContent);
420  ++$row;
421  }
422  $this->processDomElement($child, $sheet, $row, $column, $cellContent);
423  $this->flushCell($sheet, $column, $row, $cellContent);
424 
425  if (isset($this->formats[$child->nodeName])) {
426  $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
427  }
428 
429  ++$row;
430  $column = 'A';
431  }
432  } else {
433  $this->processDomElementLi($sheet, $row, $column, $cellContent, $child, $attributeArray);
434  }
435  }
436 
437  private function processDomElementLi(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
438  {
439  if ($child->nodeName === 'li') {
440  if ($this->tableLevel > 0) {
441  // If we're inside a table, replace with a \n
442  $cellContent .= $cellContent ? "\n" : '';
443  $this->processDomElement($child, $sheet, $row, $column, $cellContent);
444  } else {
445  if ($cellContent > '') {
446  $this->flushCell($sheet, $column, $row, $cellContent);
447  }
448  ++$row;
449  $this->processDomElement($child, $sheet, $row, $column, $cellContent);
450  $this->flushCell($sheet, $column, $row, $cellContent);
451  $column = 'A';
452  }
453  } else {
454  $this->processDomElementImg($sheet, $row, $column, $cellContent, $child, $attributeArray);
455  }
456  }
457 
458  private function processDomElementImg(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
459  {
460  if ($child->nodeName === 'img') {
461  $this->insertImage($sheet, $column, $row, $attributeArray);
462  } else {
463  $this->processDomElementTable($sheet, $row, $column, $cellContent, $child, $attributeArray);
464  }
465  }
466 
467  private function processDomElementTable(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
468  {
469  if ($child->nodeName === 'table') {
470  $this->flushCell($sheet, $column, $row, $cellContent);
471  $column = $this->setTableStartColumn($column);
472  if ($this->tableLevel > 1 && $row > 1) {
473  --$row;
474  }
475  $this->processDomElement($child, $sheet, $row, $column, $cellContent);
476  $column = $this->releaseTableStartColumn();
477  if ($this->tableLevel > 1) {
478  ++$column;
479  } else {
480  ++$row;
481  }
482  } else {
483  $this->processDomElementTr($sheet, $row, $column, $cellContent, $child, $attributeArray);
484  }
485  }
486 
487  private function processDomElementTr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
488  {
489  if ($child->nodeName === 'tr') {
490  $column = $this->getTableStartColumn();
491  $cellContent = '';
492  $this->processDomElement($child, $sheet, $row, $column, $cellContent);
493 
494  if (isset($attributeArray['height'])) {
495  $sheet->getRowDimension($row)->setRowHeight($attributeArray['height']);
496  }
497 
498  ++$row;
499  } else {
500  $this->processDomElementThTdOther($sheet, $row, $column, $cellContent, $child, $attributeArray);
501  }
502  }
503 
504  private function processDomElementThTdOther(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
505  {
506  if ($child->nodeName !== 'td' && $child->nodeName !== 'th') {
507  $this->processDomElement($child, $sheet, $row, $column, $cellContent);
508  } else {
509  $this->processDomElementThTd($sheet, $row, $column, $cellContent, $child, $attributeArray);
510  }
511  }
512 
513  private function processDomElementBgcolor(Worksheet $sheet, int $row, string $column, array $attributeArray): void
514  {
515  if (isset($attributeArray['bgcolor'])) {
516  $sheet->getStyle("$column$row")->applyFromArray(
517  [
518  'fill' => [
519  'fillType' => Fill::FILL_SOLID,
520  'color' => ['rgb' => $this->getStyleColor($attributeArray['bgcolor'])],
521  ],
522  ]
523  );
524  }
525  }
526 
527  private function processDomElementWidth(Worksheet $sheet, string $column, array $attributeArray): void
528  {
529  if (isset($attributeArray['width'])) {
530  $sheet->getColumnDimension($column)->setWidth($attributeArray['width']);
531  }
532  }
533 
534  private function processDomElementHeight(Worksheet $sheet, int $row, array $attributeArray): void
535  {
536  if (isset($attributeArray['height'])) {
537  $sheet->getRowDimension($row)->setRowHeight($attributeArray['height']);
538  }
539  }
540 
541  private function processDomElementAlign(Worksheet $sheet, int $row, string $column, array $attributeArray): void
542  {
543  if (isset($attributeArray['align'])) {
544  $sheet->getStyle($column . $row)->getAlignment()->setHorizontal($attributeArray['align']);
545  }
546  }
547 
548  private function processDomElementVAlign(Worksheet $sheet, int $row, string $column, array $attributeArray): void
549  {
550  if (isset($attributeArray['valign'])) {
551  $sheet->getStyle($column . $row)->getAlignment()->setVertical($attributeArray['valign']);
552  }
553  }
554 
555  private function processDomElementDataFormat(Worksheet $sheet, int $row, string $column, array $attributeArray): void
556  {
557  if (isset($attributeArray['data-format'])) {
558  $sheet->getStyle($column . $row)->getNumberFormat()->setFormatCode($attributeArray['data-format']);
559  }
560  }
561 
562  private function processDomElementThTd(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
563  {
564  while (isset($this->rowspan[$column . $row])) {
565  ++$column;
566  }
567  $this->processDomElement($child, $sheet, $row, $column, $cellContent);
568 
569  // apply inline style
570  $this->applyInlineStyle($sheet, $row, $column, $attributeArray);
571 
572  $this->flushCell($sheet, $column, $row, $cellContent);
573 
574  $this->processDomElementBgcolor($sheet, $row, $column, $attributeArray);
575  $this->processDomElementWidth($sheet, $column, $attributeArray);
576  $this->processDomElementHeight($sheet, $row, $attributeArray);
577  $this->processDomElementAlign($sheet, $row, $column, $attributeArray);
578  $this->processDomElementVAlign($sheet, $row, $column, $attributeArray);
579  $this->processDomElementDataFormat($sheet, $row, $column, $attributeArray);
580 
581  if (isset($attributeArray['rowspan'], $attributeArray['colspan'])) {
582  //create merging rowspan and colspan
583  $columnTo = $column;
584  for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
585  ++$columnTo;
586  }
587  $range = $column . $row . ':' . $columnTo . ($row + (int) $attributeArray['rowspan'] - 1);
588  foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
589  $this->rowspan[$value] = true;
590  }
591  $sheet->mergeCells($range);
592  $column = $columnTo;
593  } elseif (isset($attributeArray['rowspan'])) {
594  //create merging rowspan
595  $range = $column . $row . ':' . $column . ($row + (int) $attributeArray['rowspan'] - 1);
596  foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
597  $this->rowspan[$value] = true;
598  }
599  $sheet->mergeCells($range);
600  } elseif (isset($attributeArray['colspan'])) {
601  //create merging colspan
602  $columnTo = $column;
603  for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
604  ++$columnTo;
605  }
606  $sheet->mergeCells($column . $row . ':' . $columnTo . $row);
607  $column = $columnTo;
608  }
609 
610  ++$column;
611  }
612 
613  protected function processDomElement(DOMNode $element, Worksheet $sheet, int &$row, string &$column, string &$cellContent): void
614  {
615  foreach ($element->childNodes as $child) {
616  if ($child instanceof DOMText) {
617  $domText = preg_replace('/\s+/u', ' ', trim($child->nodeValue));
618  if (is_string($cellContent)) {
619  // simply append the text if the cell content is a plain text string
620  $cellContent .= $domText;
621  }
622  // but if we have a rich text run instead, we need to append it correctly
623  // TODO
624  } elseif ($child instanceof DOMElement) {
625  $this->processDomElementBody($sheet, $row, $column, $cellContent, $child);
626  }
627  }
628  }
629 
637  public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
638  {
639  // Validate
640  if (!$this->canRead($pFilename)) {
641  throw new Exception($pFilename . ' is an Invalid HTML file.');
642  }
643 
644  // Create a new DOM object
645  $dom = new DOMDocument();
646  // Reload the HTML file into the DOM object
647  try {
648  $loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scanFile($pFilename), 'HTML-ENTITIES', 'UTF-8'));
649  } catch (Throwable $e) {
650  $loaded = false;
651  }
652  if ($loaded === false) {
653  throw new Exception('Failed to load ' . $pFilename . ' as a DOM Document', 0, $e ?? null);
654  }
655 
656  return $this->loadDocument($dom, $spreadsheet);
657  }
658 
664  public function loadFromString($content, ?Spreadsheet $spreadsheet = null): Spreadsheet
665  {
666  // Create a new DOM object
667  $dom = new DOMDocument();
668  // Reload the HTML file into the DOM object
669  try {
670  $loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scan($content), 'HTML-ENTITIES', 'UTF-8'));
671  } catch (Throwable $e) {
672  $loaded = false;
673  }
674  if ($loaded === false) {
675  throw new Exception('Failed to load content as a DOM Document', 0, $e ?? null);
676  }
677 
678  return $this->loadDocument($dom, $spreadsheet ?? new Spreadsheet());
679  }
680 
684  private function loadDocument(DOMDocument $document, Spreadsheet $spreadsheet): Spreadsheet
685  {
686  while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
687  $spreadsheet->createSheet();
688  }
689  $spreadsheet->setActiveSheetIndex($this->sheetIndex);
690 
691  // Discard white space
692  $document->preserveWhiteSpace = false;
693 
694  $row = 0;
695  $column = 'A';
696  $content = '';
697  $this->rowspan = [];
698  $this->processDomElement($document, $spreadsheet->getActiveSheet(), $row, $column, $content);
699 
700  // Return
701  return $spreadsheet;
702  }
703 
709  public function getSheetIndex()
710  {
711  return $this->sheetIndex;
712  }
713 
721  public function setSheetIndex($pValue)
722  {
723  $this->sheetIndex = $pValue;
724 
725  return $this;
726  }
727 
743  private function applyInlineStyle(&$sheet, $row, $column, $attributeArray): void
744  {
745  if (!isset($attributeArray['style'])) {
746  return;
747  }
748 
749  if (isset($attributeArray['rowspan'], $attributeArray['colspan'])) {
750  $columnTo = $column;
751  for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
752  ++$columnTo;
753  }
754  $range = $column . $row . ':' . $columnTo . ($row + (int) $attributeArray['rowspan'] - 1);
755  $cellStyle = $sheet->getStyle($range);
756  } elseif (isset($attributeArray['rowspan'])) {
757  $range = $column . $row . ':' . $column . ($row + (int) $attributeArray['rowspan'] - 1);
758  $cellStyle = $sheet->getStyle($range);
759  } elseif (isset($attributeArray['colspan'])) {
760  $columnTo = $column;
761  for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
762  ++$columnTo;
763  }
764  $range = $column . $row . ':' . $columnTo . $row;
765  $cellStyle = $sheet->getStyle($range);
766  } else {
767  $cellStyle = $sheet->getStyle($column . $row);
768  }
769 
770  // add color styles (background & text) from dom element,currently support : td & th, using ONLY inline css style with RGB color
771  $styles = explode(';', $attributeArray['style']);
772  foreach ($styles as $st) {
773  $value = explode(':', $st);
774  $styleName = isset($value[0]) ? trim($value[0]) : null;
775  $styleValue = isset($value[1]) ? trim($value[1]) : null;
776 
777  if (!$styleName) {
778  continue;
779  }
780 
781  switch ($styleName) {
782  case 'background':
783  case 'background-color':
784  $styleColor = $this->getStyleColor($styleValue);
785 
786  if (!$styleColor) {
787  continue 2;
788  }
789 
790  $cellStyle->applyFromArray(['fill' => ['fillType' => Fill::FILL_SOLID, 'color' => ['rgb' => $styleColor]]]);
791 
792  break;
793  case 'color':
794  $styleColor = $this->getStyleColor($styleValue);
795 
796  if (!$styleColor) {
797  continue 2;
798  }
799 
800  $cellStyle->applyFromArray(['font' => ['color' => ['rgb' => $styleColor]]]);
801 
802  break;
803 
804  case 'border':
805  $this->setBorderStyle($cellStyle, $styleValue, 'allBorders');
806 
807  break;
808 
809  case 'border-top':
810  $this->setBorderStyle($cellStyle, $styleValue, 'top');
811 
812  break;
813 
814  case 'border-bottom':
815  $this->setBorderStyle($cellStyle, $styleValue, 'bottom');
816 
817  break;
818 
819  case 'border-left':
820  $this->setBorderStyle($cellStyle, $styleValue, 'left');
821 
822  break;
823 
824  case 'border-right':
825  $this->setBorderStyle($cellStyle, $styleValue, 'right');
826 
827  break;
828 
829  case 'font-size':
830  $cellStyle->getFont()->setSize(
831  (float) $styleValue
832  );
833 
834  break;
835 
836  case 'font-weight':
837  if ($styleValue === 'bold' || $styleValue >= 500) {
838  $cellStyle->getFont()->setBold(true);
839  }
840 
841  break;
842 
843  case 'font-style':
844  if ($styleValue === 'italic') {
845  $cellStyle->getFont()->setItalic(true);
846  }
847 
848  break;
849 
850  case 'font-family':
851  $cellStyle->getFont()->setName(str_replace('\'', '', $styleValue));
852 
853  break;
854 
855  case 'text-decoration':
856  switch ($styleValue) {
857  case 'underline':
858  $cellStyle->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
859 
860  break;
861  case 'line-through':
862  $cellStyle->getFont()->setStrikethrough(true);
863 
864  break;
865  }
866 
867  break;
868 
869  case 'text-align':
870  $cellStyle->getAlignment()->setHorizontal($styleValue);
871 
872  break;
873 
874  case 'vertical-align':
875  $cellStyle->getAlignment()->setVertical($styleValue);
876 
877  break;
878 
879  case 'width':
880  $sheet->getColumnDimension($column)->setWidth(
881  (float) str_replace(['px', 'pt'], '', $styleValue)
882  );
883 
884  break;
885 
886  case 'height':
887  $sheet->getRowDimension($row)->setRowHeight(
888  (float) str_replace(['px', 'pt'], '', $styleValue)
889  );
890 
891  break;
892 
893  case 'word-wrap':
894  $cellStyle->getAlignment()->setWrapText(
895  $styleValue === 'break-word'
896  );
897 
898  break;
899 
900  case 'text-indent':
901  $cellStyle->getAlignment()->setIndent(
902  (int) str_replace(['px'], '', $styleValue)
903  );
904 
905  break;
906  }
907  }
908  }
909 
915  public function getStyleColor($value)
916  {
917  if (strpos($value, '#') === 0) {
918  return substr($value, 1);
919  }
920 
921  return \PhpOffice\PhpSpreadsheet\Helper\Html::colourNameLookup((string) $value);
922  }
923 
928  private function insertImage(Worksheet $sheet, $column, $row, array $attributes): void
929  {
930  if (!isset($attributes['src'])) {
931  return;
932  }
933 
934  $src = urldecode($attributes['src']);
935  $width = isset($attributes['width']) ? (float) $attributes['width'] : null;
936  $height = isset($attributes['height']) ? (float) $attributes['height'] : null;
937  $name = $attributes['alt'] ?? null;
938 
939  $drawing = new Drawing();
940  $drawing->setPath($src);
941  $drawing->setWorksheet($sheet);
942  $drawing->setCoordinates($column . $row);
943  $drawing->setOffsetX(0);
944  $drawing->setOffsetY(10);
945  $drawing->setResizeProportional(true);
946 
947  if ($name) {
948  $drawing->setName($name);
949  }
950 
951  if ($width) {
952  $drawing->setWidth((int) $width);
953  }
954 
955  if ($height) {
956  $drawing->setHeight((int) $height);
957  }
958 
959  $sheet->getColumnDimension($column)->setWidth(
960  $drawing->getWidth() / 6
961  );
962 
963  $sheet->getRowDimension($row)->setRowHeight(
964  $drawing->getHeight() * 0.9
965  );
966  }
967 
968  private static $borderMappings = [
969  'dash-dot' => Border::BORDER_DASHDOT,
970  'dash-dot-dot' => Border::BORDER_DASHDOTDOT,
971  'dashed' => Border::BORDER_DASHED,
972  'dotted' => Border::BORDER_DOTTED,
973  'double' => Border::BORDER_DOUBLE,
974  'hair' => Border::BORDER_HAIR,
975  'medium' => Border::BORDER_MEDIUM,
976  'medium-dashed' => Border::BORDER_MEDIUMDASHED,
977  'medium-dash-dot' => Border::BORDER_MEDIUMDASHDOT,
978  'medium-dash-dot-dot' => Border::BORDER_MEDIUMDASHDOTDOT,
979  'none' => Border::BORDER_NONE,
980  'slant-dash-dot' => Border::BORDER_SLANTDASHDOT,
981  'solid' => Border::BORDER_THIN,
982  'thick' => Border::BORDER_THICK,
983  ];
984 
985  public static function getBorderMappings(): array
986  {
987  return self::$borderMappings;
988  }
989 
997  public function getBorderStyle($style)
998  {
999  return (array_key_exists($style, self::$borderMappings)) ? self::$borderMappings[$style] : null;
1000  }
1001 
1006  private function setBorderStyle(Style $cellStyle, $styleValue, $type): void
1007  {
1008  if (trim($styleValue) === Border::BORDER_NONE) {
1009  $borderStyle = Border::BORDER_NONE;
1010  $color = null;
1011  } else {
1012  $borderArray = explode(' ', $styleValue);
1013  $borderCount = count($borderArray);
1014  if ($borderCount >= 3) {
1015  $borderStyle = $borderArray[1];
1016  $color = $borderArray[2];
1017  } else {
1018  $borderStyle = $borderArray[0];
1019  $color = $borderArray[1] ?? null;
1020  }
1021  }
1022 
1023  $cellStyle->applyFromArray([
1024  'borders' => [
1025  $type => [
1026  'borderStyle' => $this->getBorderStyle($borderStyle),
1027  'color' => ['rgb' => $this->getStyleColor($color)],
1028  ],
1029  ],
1030  ]);
1031  }
1032 }
getStyleColor($value)
Check if has #, so we can get clean hex.
Definition: Html.php:915
processDomElementTr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray)
Definition: Html.php:487
processDomElementLi(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray)
Definition: Html.php:437
loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
Definition: Html.php:637
$style
Definition: example_012.php:70
$size
Definition: RandomTest.php:84
processDomElementBody(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child)
Definition: Html.php:301
mergeCells($pRange)
Set merge on a cell range.
Definition: Worksheet.php:1660
processDomElementBgcolor(Worksheet $sheet, int $row, string $column, array $attributeArray)
Definition: Html.php:513
processDomElementTable(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray)
Definition: Html.php:467
insertImage(Worksheet $sheet, $column, $row, array $attributes)
Definition: Html.php:928
const TEST_SAMPLE_SIZE
Sample size to read to determine if it&#39;s HTML or not.
Definition: Html.php:27
$type
flushCell(Worksheet $sheet, $column, $row, &$cellContent)
Definition: Html.php:282
processDomElementH1Etc(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray)
Definition: Html.php:409
loadDocument(DOMDocument $document, Spreadsheet $spreadsheet)
Loads PhpSpreadsheet from DOMDocument into PhpSpreadsheet instance.
Definition: Html.php:684
processDomElementHeight(Worksheet $sheet, int $row, array $attributeArray)
Definition: Html.php:534
__construct()
Create a new HTML Reader instance.
Definition: Html.php:130
applyInlineStyle(&$sheet, $row, $column, $attributeArray)
Apply inline css inline style.
Definition: Html.php:743
processDomElementA(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray)
Definition: Html.php:381
setBorderStyle(Style $cellStyle, $styleValue, $type)
Definition: Html.php:1006
processDomElementHr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray)
Definition: Html.php:350
processDomElementVAlign(Worksheet $sheet, int $row, string $column, array $attributeArray)
Definition: Html.php:548
getStyle($pCellCoordinate)
Get style for cell.
Definition: Worksheet.php:1420
setTitle($title, $updateFormulaCellReferences=true, $validate=true)
Set title.
Definition: Worksheet.php:841
setCellValue($pCoordinate, $pValue)
Set a cell value.
Definition: Worksheet.php:1112
static static getBorderMappings()
Definition: Html.php:985
processDomElementSpanEtc(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray)
Definition: Html.php:332
processDomElement(DOMNode $element, Worksheet $sheet, int &$row, string &$column, string &$cellContent)
Definition: Html.php:613
load($pFilename)
Loads Spreadsheet from file.
Definition: Html.php:211
getHyperlink($pCellCoordinate)
Get hyperlink.
Definition: Worksheet.php:2740
processDomElementWidth(Worksheet $sheet, string $column, array $attributeArray)
Definition: Html.php:527
createSheet($sheetIndex=null)
Create sheet and add it to this workbook.
setActiveSheetIndex($pIndex)
Set active sheet index.
loadFromString($content, ?Spreadsheet $spreadsheet=null)
Spreadsheet from content.
Definition: Html.php:664
getRowDimension(int $pRow)
Get row dimension at a specific row.
Definition: Worksheet.php:1358
static getInstance(Reader\IReader $reader)
Definition: XmlScanner.php:39
getSheetIndex()
Get sheet index.
Definition: Html.php:709
canRead($pFilename)
Validate that the current file is an HTML file.
Definition: Html.php:143
if(array_key_exists('yes', $_REQUEST)) $attributes
Definition: getconsent.php:85
processDomElementBr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray)
Definition: Html.php:364
$filename
Definition: buildRTE.php:89
$row
processDomElementThTdOther(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray)
Definition: Html.php:504
processDomElementImg(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray)
Definition: Html.php:458
PhpSpreadsheet root directory.
Definition: Html.php:22
getInputEncoding()
Get input encoding.
Definition: Html.php:247
getColumnDimension(string $pColumn)
Get column dimension at a specific column.
Definition: Worksheet.php:1375
getBorderStyle($style)
Map html border style to PhpSpreadsheet border style.
Definition: Html.php:997
processDomElementThTd(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray)
Definition: Html.php:562
$i
Definition: disco.tpl.php:19
static extractAllCellReferencesInRange($cellRange)
Extract all cell references in range, which may be comprised of multiple cell ranges.
Definition: Coordinate.php:338
processDomElementTitle(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray)
Definition: Html.php:319
processDomElementAlign(Worksheet $sheet, int $row, string $column, array $attributeArray)
Definition: Html.php:541
getComment($pCellCoordinate)
Get comment for cell.
Definition: Worksheet.php:2297
setInputEncoding($pValue)
Set input encoding.
Definition: Html.php:231
openFile($pFilename)
Open file for reading.
Definition: BaseReader.php:145
processDomElementDataFormat(Worksheet $sheet, int $row, string $column, array $attributeArray)
Definition: Html.php:555
setSheetIndex($pValue)
Set sheet index.
Definition: Html.php:721
$data
Definition: bench.php:6