Ods reader: method loadIntoExisting() now uses DOMDocument to parse content

This commit is contained in:
Paolo Agostinetto 2017-02-18 20:38:10 +01:00
parent bcd1bc364c
commit e46c298fe1
1 changed files with 267 additions and 101 deletions

View File

@ -4,7 +4,10 @@ namespace PhpOffice\PhpSpreadsheet\Reader;
use DateTime; use DateTime;
use DateTimeZone; use DateTimeZone;
use PhpOffice\PhpSpreadsheet\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Shared\File; use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
/** /**
* Copyright (c) 2006 - 2016 PhpSpreadsheet. * Copyright (c) 2006 - 2016 PhpSpreadsheet.
@ -292,11 +295,16 @@ class Ods extends BaseReader implements IReader
$zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass();
/** @var \ZipArchive $zip */
$zip = new $zipClass(); $zip = new $zipClass();
if (!$zip->open($pFilename)) { if (!$zip->open($pFilename)) {
throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.'); throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.');
} }
/*
* Meta
*/
$xml = simplexml_load_string( $xml = simplexml_load_string(
$this->securityScan($zip->getFromName('meta.xml')), $this->securityScan($zip->getFromName('meta.xml')),
'SimpleXMLElement', 'SimpleXMLElement',
@ -382,50 +390,94 @@ class Ods extends BaseReader implements IReader
} }
} }
$xml = simplexml_load_string( /*
* Content
*/
$dom = new \DOMDocument('1.01', 'UTF-8');
$dom->loadXML(
$this->securityScan($zip->getFromName('content.xml')), $this->securityScan($zip->getFromName('content.xml')),
'SimpleXMLElement',
\PhpOffice\PhpSpreadsheet\Settings::getLibXmlLoaderOptions() \PhpOffice\PhpSpreadsheet\Settings::getLibXmlLoaderOptions()
); );
$namespacesContent = $xml->getNamespaces(true);
$workbook = $xml->children($namespacesContent['office']); $officeNs = $dom->lookupNamespaceUri("office");
foreach ($workbook->body->spreadsheet as $workbookData) { $tableNs = $dom->lookupNamespaceUri("table");
$workbookData = $workbookData->children($namespacesContent['table']); $textNs = $dom->lookupNamespaceUri("text");
$spreadsheets = $dom->getElementsByTagNameNS($officeNs, "body")
->item(0)
->getElementsByTagNameNS($officeNs, "spreadsheet");
foreach ($spreadsheets as $workbookData) {
/** @var \DOMElement $workbookData */
$tables = $workbookData->getElementsByTagNameNS($tableNs, "table");
$worksheetID = 0; $worksheetID = 0;
foreach ($workbookData->table as $worksheetDataSet) { foreach ($tables as $worksheetDataSet) {
$worksheetData = $worksheetDataSet->children($namespacesContent['table']); /** @var \DOMElement $worksheetDataSet */
$worksheetDataAttributes = $worksheetDataSet->attributes($namespacesContent['table']);
if ((isset($this->loadSheetsOnly)) && (isset($worksheetDataAttributes['name'])) && $worksheetName = $worksheetDataSet->getAttributeNS($tableNs, "name");
(!in_array($worksheetDataAttributes['name'], $this->loadSheetsOnly))) {
// Check loadSheetsOnly
if (isset($this->loadSheetsOnly)
&& $worksheetName
&& !in_array($worksheetName, $this->loadSheetsOnly)) {
continue; continue;
} }
// Create new Worksheet // Create sheet
$spreadsheet->createSheet(); $spreadsheet->createSheet();
$spreadsheet->setActiveSheetIndex($worksheetID); $spreadsheet->setActiveSheetIndex($worksheetID);
if (isset($worksheetDataAttributes['name'])) {
$worksheetName = (string) $worksheetDataAttributes['name']; if ($worksheetName) {
// Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in
// formula cells... during the load, all formulae should be correct, and we're simply // formula cells... during the load, all formulae should be correct, and we're simply
// bringing the worksheet name in line with the formula, not the reverse // bringing the worksheet name in line with the formula, not the reverse
$spreadsheet->getActiveSheet()->setTitle($worksheetName, false); $spreadsheet->getActiveSheet()->setTitle($worksheetName, false);
} }
// Go through every child of table element
$rowID = 1; $rowID = 1;
foreach ($worksheetData as $key => $rowData) { foreach ($worksheetDataSet->childNodes as $childNode) {
/** @var \DOMElement $childNode */
// Filter elements which are not under the "table" ns
if($childNode->namespaceURI != $tableNs){
continue;
}
$key = $childNode->nodeName;
// Remove ns from node name
if(strpos($key, ":") !== false){
$keyChunks = explode(":", $key);
$key = array_pop($keyChunks);
}
switch ($key) { switch ($key) {
case 'table-header-rows': case 'table-header-rows':
foreach ($rowData as $keyRowData => $cellData) { /// TODO :: Figure this out. This is only a partial implementation I guess.
$rowData = $cellData; // ($rowData it's not used at all)
break;
} // foreach ($rowData as $keyRowData => $cellData) {
// $rowData = $cellData;
// break;
// }
break; break;
case 'table-row': case 'table-row':
$rowDataTableAttributes = $rowData->attributes($namespacesContent['table']);
$rowRepeats = (isset($rowDataTableAttributes['number-rows-repeated'])) ? $rowDataTableAttributes['number-rows-repeated'] : 1; if($childNode->hasAttributeNS($tableNs, 'number-rows-repeated')){
$rowRepeats = $childNode->getAttributeNS($tableNs, 'number-rows-repeated');
}
else{
$rowRepeats = 1;
}
$columnID = 'A'; $columnID = 'A';
foreach ($rowData as $key => $cellData) { foreach ($childNode->childNodes as $key => $cellData) {
/** @var \DOMElement $cellData */
if ($this->getReadFilter() !== null) { if ($this->getReadFilter() !== null) {
if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) { if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
++$columnID; ++$columnID;
@ -433,93 +485,98 @@ class Ods extends BaseReader implements IReader
} }
} }
$cellDataText = (isset($namespacesContent['text'])) ? $cellData->children($namespacesContent['text']) : ''; // Initialize variables
$cellDataOffice = $cellData->children($namespacesContent['office']); $formatting = $hyperlink = null;
$cellDataOfficeAttributes = $cellData->attributes($namespacesContent['office']);
$cellDataTableAttributes = $cellData->attributes($namespacesContent['table']);
$type = $formatting = $hyperlink = null;
$hasCalculatedValue = false; $hasCalculatedValue = false;
$cellDataFormula = ''; $cellDataFormula = '';
if (isset($cellDataTableAttributes['formula'])) {
$cellDataFormula = $cellDataTableAttributes['formula']; if ($cellData->hasAttributeNS($tableNs, "formula")) {
$cellDataFormula = $cellData->getAttributeNS($tableNs, "formula");
$hasCalculatedValue = true; $hasCalculatedValue = true;
} }
if (isset($cellDataOffice->annotation)) { // Annotations
$annotationText = $cellDataOffice->annotation->children($namespacesContent['text']); $annotation = $cellData->getElementsByTagNameNS($officeNs, "annotation");
$textArray = [];
foreach ($annotationText as $t) { if ($annotation->length > 0) {
if (isset($t->span)) { $textNode = $annotation->item(0)->getElementsByTagNameNS($textNs, "p");
foreach ($t->span as $text) {
$textArray[] = (string) $text; if($textNode->length > 0){
}
} else { $text = $this->scanElementForText($textNode->item(0));
$textArray[] = (string) $t;
} $spreadsheet->getActiveSheet()
} ->getComment($columnID . $rowID)
$text = implode("\n", $textArray); ->setText($this->parseRichText($text));
$spreadsheet->getActiveSheet()->getComment($columnID . $rowID)->setText($this->parseRichText($text));
// ->setAuthor( $author ) // ->setAuthor( $author )
}
} }
if (isset($cellDataText->p)) { // Content
$paragraphs = [];
foreach ($cellData->childNodes as $item) {
/** @var \DOMElement $item */
// Filter text:p elements
if($item->nodeName == "text:p"){
$paragraphs[] = $item;
}
}
if (count($paragraphs) > 0) {
// Consolidate if there are multiple p records (maybe with spans as well) // Consolidate if there are multiple p records (maybe with spans as well)
$dataArray = []; $dataArray = [];
// Text can have multiple text:p and within those, multiple text:span. // Text can have multiple text:p and within those, multiple text:span.
// text:p newlines, but text:span does not. // text:p newlines, but text:span does not.
// Also, here we assume there is no text data is span fields are specified, since // Also, here we assume there is no text data is span fields are specified, since
// we have no way of knowing proper positioning anyway. // we have no way of knowing proper positioning anyway.
foreach ($cellDataText->p as $pData) {
if (isset($pData->span)) { foreach ($paragraphs as $pData) {
// span sections do not newline, so we just create one large string here $dataArray[] = $this->scanElementForText($pData);
$spanSection = '';
foreach ($pData->span as $spanData) {
$spanSection .= $spanData;
}
array_push($dataArray, $spanSection);
} elseif (isset($pData->a)) {
//Reading the hyperlinks in p
array_push($dataArray, $pData->a);
} else {
array_push($dataArray, $pData);
}
} }
$allCellDataText = implode($dataArray, "\n"); $allCellDataText = implode($dataArray, "\n");
switch ($cellDataOfficeAttributes['value-type']) { $type = $cellData->getAttributeNS($officeNs, 'value-type');
switch ($type) {
case 'string': case 'string':
$type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING; $type = DataType::TYPE_STRING;
$dataValue = $allCellDataText; $dataValue = $allCellDataText;
if (isset($dataValue->a)) {
$dataValue = $dataValue->a; /// TODO :: Fix this: usually it's text:p > text:a, not just text:a
$cellXLinkAttributes = $dataValue->attributes($namespacesContent['xlink']); // if (isset($dataValue->a)) {
$hyperlink = $cellXLinkAttributes['href']; // $dataValue = $dataValue->a;
} // $cellXLinkAttributes = $dataValue->attributes($namespacesContent['xlink']);
// $hyperlink = $cellXLinkAttributes['href'];
// }
break; break;
case 'boolean': case 'boolean':
$type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_BOOL; $type = DataType::TYPE_BOOL;
$dataValue = ($allCellDataText == 'TRUE') ? true : false; $dataValue = ($allCellDataText == 'TRUE') ? true : false;
break; break;
case 'percentage': case 'percentage':
$type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; $type = DataType::TYPE_NUMERIC;
$dataValue = (float) $cellDataOfficeAttributes['value']; $dataValue = (float)$cellData->getAttributeNS($officeNs, 'value');
if (floor($dataValue) == $dataValue) { if (floor($dataValue) == $dataValue) {
$dataValue = (int) $dataValue; $dataValue = (int) $dataValue;
} }
$formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_PERCENTAGE_00; $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_PERCENTAGE_00;
break; break;
case 'currency': case 'currency':
$type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; $type = DataType::TYPE_NUMERIC;
$dataValue = (float) $cellDataOfficeAttributes['value']; $dataValue = (float)$cellData->getAttributeNS($officeNs, 'value');
if (floor($dataValue) == $dataValue) { if (floor($dataValue) == $dataValue) {
$dataValue = (int) $dataValue; $dataValue = (int) $dataValue;
} }
$formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_CURRENCY_USD_SIMPLE; $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_CURRENCY_USD_SIMPLE;
break; break;
case 'float': case 'float':
$type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; $type = DataType::TYPE_NUMERIC;
$dataValue = (float) $cellDataOfficeAttributes['value']; $dataValue = (float)$cellData->getAttributeNS($officeNs, 'value');
if (floor($dataValue) == $dataValue) { if (floor($dataValue) == $dataValue) {
if ($dataValue == (int) $dataValue) { if ($dataValue == (int) $dataValue) {
$dataValue = (int) $dataValue; $dataValue = (int) $dataValue;
@ -529,41 +586,70 @@ class Ods extends BaseReader implements IReader
} }
break; break;
case 'date': case 'date':
$type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; $type = DataType::TYPE_NUMERIC;
$dateObj = new DateTime($cellDataOfficeAttributes['date-value'], $GMT); $value = $cellData->getAttributeNS($officeNs, 'date-value');
$dateObj = new DateTime($value, $GMT);
$dateObj->setTimeZone($timezoneObj); $dateObj->setTimeZone($timezoneObj);
list($year, $month, $day, $hour, $minute, $second) = explode(' ', $dateObj->format('Y m d H i s')); list($year, $month, $day, $hour, $minute, $second) = explode(' ',
$dataValue = \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel($year, $month, $day, $hour, $minute, $second); $dateObj->format('Y m d H i s')
);
$dataValue = \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel(
$year, $month, $day, $hour, $minute, $second
);
if ($dataValue != floor($dataValue)) { if ($dataValue != floor($dataValue)) {
$formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_XLSX15 . ' ' . \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_TIME4; $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_XLSX15
. ' '
. \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_TIME4;
} else { } else {
$formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_XLSX15; $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_XLSX15;
} }
break; break;
case 'time': case 'time':
$type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; $type = DataType::TYPE_NUMERIC;
$dataValue = \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(strtotime('01-01-1970 ' . implode(':', sscanf($cellDataOfficeAttributes['time-value'], 'PT%dH%dM%dS'))));
$timeValue = $cellData->getAttributeNS($officeNs, 'time-value');
$dataValue = \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(
strtotime(
'01-01-1970 ' . implode(':', sscanf($timeValue, 'PT%dH%dM%dS'))
)
);
$formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_TIME4; $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_TIME4;
break; break;
default:
$dataValue = null;
} }
} else { } else {
$type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NULL; $type = DataType::TYPE_NULL;
$dataValue = null; $dataValue = null;
} }
if ($hasCalculatedValue) { if ($hasCalculatedValue) {
$type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_FORMULA; $type = DataType::TYPE_FORMULA;
$cellDataFormula = substr($cellDataFormula, strpos($cellDataFormula, ':=') + 1); $cellDataFormula = substr($cellDataFormula, strpos($cellDataFormula, ':=') + 1);
$temp = explode('"', $cellDataFormula); $temp = explode('"', $cellDataFormula);
$tKey = false; $tKey = false;
foreach ($temp as &$value) { foreach ($temp as &$value) {
// Only replace in alternate array entries (i.e. non-quoted blocks) // Only replace in alternate array entries (i.e. non-quoted blocks)
if ($tKey = !$tKey) { if ($tKey = !$tKey) {
$value = preg_replace('/\[([^\.]+)\.([^\.]+):\.([^\.]+)\]/Ui', '$1!$2:$3', $value); // Cell range reference in another sheet
$value = preg_replace('/\[([^\.]+)\.([^\.]+)\]/Ui', '$1!$2', $value); // Cell reference in another sheet // Cell range reference in another sheet
$value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/Ui', '$1:$2', $value); // Cell range reference $value = preg_replace('/\[([^\.]+)\.([^\.]+):\.([^\.]+)\]/Ui', '$1!$2:$3', $value);
$value = preg_replace('/\[\.([^\.]+)\]/Ui', '$1', $value); // Simple cell reference
$value = \PhpOffice\PhpSpreadsheet\Calculation::translateSeparator(';', ',', $value, $inBraces); // Cell reference in another sheet
$value = preg_replace('/\[([^\.]+)\.([^\.]+)\]/Ui', '$1!$2', $value);
// Cell range reference
$value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/Ui', '$1:$2', $value);
// Simple cell reference
$value = preg_replace('/\[\.([^\.]+)\]/Ui', '$1', $value);
$value = Calculation::translateSeparator(';', ',', $value, $inBraces);
} }
} }
unset($value); unset($value);
@ -571,26 +657,55 @@ class Ods extends BaseReader implements IReader
$cellDataFormula = implode('"', $temp); $cellDataFormula = implode('"', $temp);
} }
$colRepeats = (isset($cellDataTableAttributes['number-columns-repeated'])) ? $cellDataTableAttributes['number-columns-repeated'] : 1; if($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')){
$colRepeats = (int)$cellData->getAttributeNS($tableNs, 'number-columns-repeated');
}
else{
$colRepeats = 1;
}
if ($type !== null) { if ($type !== null) {
for ($i = 0; $i < $colRepeats; ++$i) { for ($i = 0; $i < $colRepeats; ++$i) {
if ($i > 0) { if ($i > 0) {
++$columnID; ++$columnID;
} }
if ($type !== \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NULL) {
if ($type !== DataType::TYPE_NULL) {
for ($rowAdjust = 0; $rowAdjust < $rowRepeats; ++$rowAdjust) { for ($rowAdjust = 0; $rowAdjust < $rowRepeats; ++$rowAdjust) {
$rID = $rowID + $rowAdjust; $rID = $rowID + $rowAdjust;
$spreadsheet->getActiveSheet()->getCell($columnID . $rID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $dataValue), $type);
$cell = $spreadsheet->getActiveSheet()
->getCell($columnID . $rID);
// Set value
if($hasCalculatedValue){
$cell->setValueExplicit($cellDataFormula, $type);
}
else{
$cell->setValueExplicit($dataValue, $type);
}
if ($hasCalculatedValue) { if ($hasCalculatedValue) {
$spreadsheet->getActiveSheet()->getCell($columnID . $rID)->setCalculatedValue($dataValue); $cell->setCalculatedValue($dataValue);
} }
// Set other properties
if ($formatting !== null) { if ($formatting !== null) {
$spreadsheet->getActiveSheet()->getStyle($columnID . $rID)->getNumberFormat()->setFormatCode($formatting); $spreadsheet->getActiveSheet()
->getStyle($columnID . $rID)
->getNumberFormat()
->setFormatCode($formatting);
} else { } else {
$spreadsheet->getActiveSheet()->getStyle($columnID . $rID)->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_GENERAL); $spreadsheet->getActiveSheet()
->getStyle($columnID . $rID)
->getNumberFormat()
->setFormatCode(NumberFormat::FORMAT_GENERAL);
} }
if ($hyperlink !== null) { if ($hyperlink !== null) {
$spreadsheet->getActiveSheet()->getCell($columnID . $rID)->getHyperlink()->setUrl($hyperlink); $cell->getHyperlink()
->setUrl($hyperlink);
} }
} }
} }
@ -598,15 +713,23 @@ class Ods extends BaseReader implements IReader
} }
// Merged cells // Merged cells
if ((isset($cellDataTableAttributes['number-columns-spanned'])) || (isset($cellDataTableAttributes['number-rows-spanned']))) { if ($childNode->hasAttributeNS($tableNs, 'number-columns-spanned')
if (($type !== \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NULL) || (!$this->readDataOnly)) { || $childNode->hasAttributeNS($tableNs, 'number-rows-spanned')
) {
if (($type !== DataType::TYPE_NULL) || (!$this->readDataOnly)) {
$columnTo = $columnID; $columnTo = $columnID;
if (isset($cellDataTableAttributes['number-columns-spanned'])) {
$columnTo = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex(\PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($columnID) + $cellDataTableAttributes['number-columns-spanned'] - 2); if ($cellData->hasAttributeNS($tableNs, 'number-columns-spanned')) {
$columnIndex = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($columnID);
$columnIndex += (int)$cellData->getAttributeNS($tableNs, 'number-columns-spanned');
$columnIndex -= 2;
$columnTo = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($columnIndex);
} }
$rowTo = $rowID; $rowTo = $rowID;
if (isset($cellDataTableAttributes['number-rows-spanned'])) { if ($cellData->hasAttributeNS($tableNs, 'number-rows-spanned')) {
$rowTo = $rowTo + $cellDataTableAttributes['number-rows-spanned'] - 1; $rowTo = $rowTo + (int)$cellData->getAttributeNS($tableNs, 'number-rows-spanned') - 1;
} }
$cellRange = $columnID . $rowID . ':' . $columnTo . $rowTo; $cellRange = $columnID . $rowID . ':' . $columnTo . $rowTo;
$spreadsheet->getActiveSheet()->mergeCells($cellRange); $spreadsheet->getActiveSheet()->mergeCells($cellRange);
@ -627,10 +750,53 @@ class Ods extends BaseReader implements IReader
return $spreadsheet; return $spreadsheet;
} }
/**
* Recursively scan element
*
* @param \DOMNode $element
* @return string
*/
protected function scanElementForText(\DOMNode $element){
$str = "";
foreach($element->childNodes as $child){
/** @var \DOMNode $child */
if($child->nodeType == XML_TEXT_NODE){
$str .= $child->nodeValue;
}
elseif($child->nodeType == XML_ELEMENT_NODE && $child->nodeName == "text:s"){
// It's a space
// Multiple spaces?
if(isset($child->attributes["text:c"])){
/** @var \DOMAttr $cAttr */
$cAttr = $child->attributes["text:c"];
$multiplier = (int)$cAttr->nodeValue;
}
else{
$multiplier = 1;
}
$str .= str_repeat(" ", $multiplier);
}
if($child->hasChildNodes()){
$str .= $this->scanElementForText($child);
}
}
return $str;
}
/**
* @param string $is
* @return \PhpOffice\PhpSpreadsheet\RichText
*/
private function parseRichText($is = '') private function parseRichText($is = '')
{ {
$value = new \PhpOffice\PhpSpreadsheet\RichText(); $value = new \PhpOffice\PhpSpreadsheet\RichText();
$value->createText($is); $value->createText($is);
return $value; return $value;