Merge pull request #98 from redokun/fix-85
Ods reader: various fixes and unit tests added (bug #85)
This commit is contained in:
		
						commit
						f7e1aa3c41
					
				| @ -29,9 +29,9 @@ interface IReadFilter | |||||||
|     /** |     /** | ||||||
|      * Should this cell be read? |      * Should this cell be read? | ||||||
|      * |      * | ||||||
|      * @param $column Column address (as a string value like "A", or "IV") |      * @param $column string Column address (as a string value like "A", or "IV") | ||||||
|      * @param $row Row number |      * @param $row int Row number | ||||||
|      * @param $worksheetName Optional worksheet name |      * @param $worksheetName string Optional worksheet name | ||||||
|      * |      * | ||||||
|      * @return bool |      * @return bool | ||||||
|      */ |      */ | ||||||
|  | |||||||
| @ -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. | ||||||
| @ -61,7 +64,10 @@ class Ods extends BaseReader implements IReader | |||||||
|         $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); |         $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); | ||||||
| 
 | 
 | ||||||
|         $mimeType = 'UNKNOWN'; |         $mimeType = 'UNKNOWN'; | ||||||
|  | 
 | ||||||
|         // Load file
 |         // Load file
 | ||||||
|  | 
 | ||||||
|  |         /** @var \ZipArchive $zip */ | ||||||
|         $zip = new $zipClass(); |         $zip = new $zipClass(); | ||||||
|         if ($zip->open($pFilename) === true) { |         if ($zip->open($pFilename) === true) { | ||||||
|             // check if it is an OOXML archive
 |             // check if it is an OOXML archive
 | ||||||
| @ -101,6 +107,8 @@ class Ods extends BaseReader implements IReader | |||||||
|      * @param string $pFilename |      * @param string $pFilename | ||||||
|      * |      * | ||||||
|      * @throws Exception |      * @throws Exception | ||||||
|  |      * | ||||||
|  |      * @return string[] | ||||||
|      */ |      */ | ||||||
|     public function listWorksheetNames($pFilename) |     public function listWorksheetNames($pFilename) | ||||||
|     { |     { | ||||||
| @ -108,6 +116,7 @@ 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.'); | ||||||
| @ -115,18 +124,18 @@ class Ods extends BaseReader implements IReader | |||||||
| 
 | 
 | ||||||
|         $worksheetNames = []; |         $worksheetNames = []; | ||||||
| 
 | 
 | ||||||
|         $xml = new XMLReader(); |         $xml = new \XMLReader(); | ||||||
|         $res = $xml->xml( |         $xml->xml( | ||||||
|             $this->securityScanFile('zip://' . realpath($pFilename) . '#content.xml'), |             $this->securityScanFile('zip://' . realpath($pFilename) . '#content.xml'), | ||||||
|             null, |             null, | ||||||
|             \PhpOffice\PhpSpreadsheet\Settings::getLibXmlLoaderOptions() |             \PhpOffice\PhpSpreadsheet\Settings::getLibXmlLoaderOptions() | ||||||
|         ); |         ); | ||||||
|         $xml->setParserProperty(2, true); |         $xml->setParserProperty(2, true); | ||||||
| 
 | 
 | ||||||
|         //    Step into the first level of content of the XML
 |         // Step into the first level of content of the XML
 | ||||||
|         $xml->read(); |         $xml->read(); | ||||||
|         while ($xml->read()) { |         while ($xml->read()) { | ||||||
|             //    Quickly jump through to the office:body node
 |             // Quickly jump through to the office:body node
 | ||||||
|             while ($xml->name !== 'office:body') { |             while ($xml->name !== 'office:body') { | ||||||
|                 if ($xml->isEmptyElement) { |                 if ($xml->isEmptyElement) { | ||||||
|                     $xml->read(); |                     $xml->read(); | ||||||
| @ -134,14 +143,14 @@ class Ods extends BaseReader implements IReader | |||||||
|                     $xml->next(); |                     $xml->next(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             //    Now read each node until we find our first table:table node
 |             // Now read each node until we find our first table:table node
 | ||||||
|             while ($xml->read()) { |             while ($xml->read()) { | ||||||
|                 if ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) { |                 if ($xml->name == 'table:table' && $xml->nodeType == \XMLReader::ELEMENT) { | ||||||
|                     //    Loop through each table:table node reading the table:name attribute for each worksheet name
 |                     // Loop through each table:table node reading the table:name attribute for each worksheet name
 | ||||||
|                     do { |                     do { | ||||||
|                         $worksheetNames[] = $xml->getAttribute('table:name'); |                         $worksheetNames[] = $xml->getAttribute('table:name'); | ||||||
|                         $xml->next(); |                         $xml->next(); | ||||||
|                     } while ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT); |                     } while ($xml->name == 'table:table' && $xml->nodeType == \XMLReader::ELEMENT); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -155,6 +164,8 @@ class Ods extends BaseReader implements IReader | |||||||
|      * @param string $pFilename |      * @param string $pFilename | ||||||
|      * |      * | ||||||
|      * @throws Exception |      * @throws Exception | ||||||
|  |      * | ||||||
|  |      * @return array | ||||||
|      */ |      */ | ||||||
|     public function listWorksheetInfo($pFilename) |     public function listWorksheetInfo($pFilename) | ||||||
|     { |     { | ||||||
| @ -164,12 +175,13 @@ 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.'); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $xml = new XMLReader(); |         $xml = new \XMLReader(); | ||||||
|         $res = $xml->xml( |         $res = $xml->xml( | ||||||
|             $this->securityScanFile('zip://' . realpath($pFilename) . '#content.xml'), |             $this->securityScanFile('zip://' . realpath($pFilename) . '#content.xml'), | ||||||
|             null, |             null, | ||||||
| @ -177,10 +189,10 @@ class Ods extends BaseReader implements IReader | |||||||
|         ); |         ); | ||||||
|         $xml->setParserProperty(2, true); |         $xml->setParserProperty(2, true); | ||||||
| 
 | 
 | ||||||
|         //    Step into the first level of content of the XML
 |         // Step into the first level of content of the XML
 | ||||||
|         $xml->read(); |         $xml->read(); | ||||||
|         while ($xml->read()) { |         while ($xml->read()) { | ||||||
|             //    Quickly jump through to the office:body node
 |             // Quickly jump through to the office:body node
 | ||||||
|             while ($xml->name !== 'office:body') { |             while ($xml->name !== 'office:body') { | ||||||
|                 if ($xml->isEmptyElement) { |                 if ($xml->isEmptyElement) { | ||||||
|                     $xml->read(); |                     $xml->read(); | ||||||
| @ -188,9 +200,9 @@ class Ods extends BaseReader implements IReader | |||||||
|                     $xml->next(); |                     $xml->next(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|                 //    Now read each node until we find our first table:table node
 |                 // Now read each node until we find our first table:table node
 | ||||||
|             while ($xml->read()) { |             while ($xml->read()) { | ||||||
|                 if ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) { |                 if ($xml->name == 'table:table' && $xml->nodeType == \XMLReader::ELEMENT) { | ||||||
|                     $worksheetNames[] = $xml->getAttribute('table:name'); |                     $worksheetNames[] = $xml->getAttribute('table:name'); | ||||||
| 
 | 
 | ||||||
|                     $tmpInfo = [ |                     $tmpInfo = [ | ||||||
| @ -201,27 +213,27 @@ class Ods extends BaseReader implements IReader | |||||||
|                         'totalColumns' => 0, |                         'totalColumns' => 0, | ||||||
|                     ]; |                     ]; | ||||||
| 
 | 
 | ||||||
|                     //    Loop through each child node of the table:table element reading
 |                     // Loop through each child node of the table:table element reading
 | ||||||
|                     $currCells = 0; |                     $currCells = 0; | ||||||
|                     do { |                     do { | ||||||
|                         $xml->read(); |                         $xml->read(); | ||||||
|                         if ($xml->name == 'table:table-row' && $xml->nodeType == XMLReader::ELEMENT) { |                         if ($xml->name == 'table:table-row' && $xml->nodeType == \XMLReader::ELEMENT) { | ||||||
|                             $rowspan = $xml->getAttribute('table:number-rows-repeated'); |                             $rowspan = $xml->getAttribute('table:number-rows-repeated'); | ||||||
|                             $rowspan = empty($rowspan) ? 1 : $rowspan; |                             $rowspan = empty($rowspan) ? 1 : $rowspan; | ||||||
|                             $tmpInfo['totalRows'] += $rowspan; |                             $tmpInfo['totalRows'] += $rowspan; | ||||||
|                             $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells); |                             $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells); | ||||||
|                             $currCells = 0; |                             $currCells = 0; | ||||||
|                             //    Step into the row
 |                             // Step into the row
 | ||||||
|                             $xml->read(); |                             $xml->read(); | ||||||
|                             do { |                             do { | ||||||
|                                 if ($xml->name == 'table:table-cell' && $xml->nodeType == XMLReader::ELEMENT) { |                                 if ($xml->name == 'table:table-cell' && $xml->nodeType == \XMLReader::ELEMENT) { | ||||||
|                                     if (!$xml->isEmptyElement) { |                                     if (!$xml->isEmptyElement) { | ||||||
|                                         ++$currCells; |                                         ++$currCells; | ||||||
|                                         $xml->next(); |                                         $xml->next(); | ||||||
|                                     } else { |                                     } else { | ||||||
|                                         $xml->read(); |                                         $xml->read(); | ||||||
|                                     } |                                     } | ||||||
|                                 } elseif ($xml->name == 'table:covered-table-cell' && $xml->nodeType == XMLReader::ELEMENT) { |                                 } elseif ($xml->name == 'table:covered-table-cell' && $xml->nodeType == \XMLReader::ELEMENT) { | ||||||
|                                     $mergeSize = $xml->getAttribute('table:number-columns-repeated'); |                                     $mergeSize = $xml->getAttribute('table:number-columns-repeated'); | ||||||
|                                     $currCells += $mergeSize; |                                     $currCells += $mergeSize; | ||||||
|                                     $xml->read(); |                                     $xml->read(); | ||||||
| @ -292,11 +304,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 +399,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'); | ||||||
|  |         $xlinkNs = $dom->lookupNamespaceUri('xlink'); | ||||||
|  | 
 | ||||||
|  |         $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']); |                 $worksheetName = $worksheetDataSet->getAttributeNS($tableNs, 'name'); | ||||||
|                 if ((isset($this->loadSheetsOnly)) && (isset($worksheetDataAttributes['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(); |                 if ($worksheetID > 0) { | ||||||
|  |                     $spreadsheet->createSheet(); // First sheet is added by default
 | ||||||
|  |                 } | ||||||
|                 $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 and I'm not sure that PHPExcel
 | ||||||
|                                 break; |                             //          has an API for this)
 | ||||||
|                             } | 
 | ||||||
|  | //                            foreach ($rowData as $keyRowData => $cellData) {
 | ||||||
|  | //                                $rowData = $cellData;
 | ||||||
|  | //                                break;
 | ||||||
|  | //                            }
 | ||||||
|                             break; |                             break; | ||||||
|                         case 'table-row': |                         case 'table-row': | ||||||
|                             $rowDataTableAttributes = $rowData->attributes($namespacesContent['table']); |                             if ($childNode->hasAttributeNS($tableNs, 'number-rows-repeated')) { | ||||||
|                             $rowRepeats = (isset($rowDataTableAttributes['number-rows-repeated'])) ? $rowDataTableAttributes['number-rows-repeated'] : 1; |                                 $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 +494,101 @@ 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) { | ||||||
|                                             } |                                         $text = $this->scanElementForText($textNode->item(0)); | ||||||
|                                         } else { | 
 | ||||||
|                                             $textArray[] = (string) $t; |                                         $spreadsheet->getActiveSheet() | ||||||
|                                         } |                                             ->getComment($columnID . $rowID) | ||||||
|                                     } |                                             ->setText($this->parseRichText($text)); | ||||||
|                                     $text = implode("\n", $textArray); |  | ||||||
|                                     $spreadsheet->getActiveSheet()->getComment($columnID . $rowID)->setText($this->parseRichText($text)); |  | ||||||
| //                                                                    ->setAuthor( $author )
 | //                                                                    ->setAuthor( $author )
 | ||||||
|  |                                     } | ||||||
|                                 } |                                 } | ||||||
| 
 | 
 | ||||||
|                                 if (isset($cellDataText->p)) { |                                 // Content
 | ||||||
|  | 
 | ||||||
|  |                                 /** @var \DOMElement[] $paragraphs */ | ||||||
|  |                                 $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; |                                             foreach ($paragraphs as $paragraph) { | ||||||
|                                                 $cellXLinkAttributes = $dataValue->attributes($namespacesContent['xlink']); |                                                 $link = $paragraph->getElementsByTagNameNS($textNs, 'a'); | ||||||
|                                                 $hyperlink = $cellXLinkAttributes['href']; |                                                 if ($link->length > 0) { | ||||||
|  |                                                     $hyperlink = $link->item(0)->getAttributeNS($xlinkNs, '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,85 +598,155 @@ 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
 |                                             // Cell range reference in another sheet
 | ||||||
|                                             $value = preg_replace('/\[([^\.]+)\.([^\.]+)\]/Ui', '$1!$2', $value); //  Cell reference in another sheet
 |                                             $value = preg_replace('/\[([^\.]+)\.([^\.]+):\.([^\.]+)\]/Ui', '$1!$2:$3', $value); | ||||||
|                                             $value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/Ui', '$1:$2', $value); //  Cell range reference
 | 
 | ||||||
|                                             $value = preg_replace('/\[\.([^\.]+)\]/Ui', '$1', $value); //  Simple cell reference
 |                                             // Cell reference in another sheet
 | ||||||
|                                             $value = \PhpOffice\PhpSpreadsheet\Calculation::translateSeparator(';', ',', $value, $inBraces); |                                             $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); | ||||||
|                                     //    Then rebuild the formula string
 | 
 | ||||||
|  |                                     // Then rebuild the formula string
 | ||||||
|                                     $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) { |                                                 if ($hasCalculatedValue) { | ||||||
|                                                     $spreadsheet->getActiveSheet()->getCell($columnID . $rID)->setCalculatedValue($dataValue); |                                                     $cell->setValueExplicit($cellDataFormula, $type); | ||||||
|                                                 } |  | ||||||
|                                                 if ($formatting !== null) { |  | ||||||
|                                                     $spreadsheet->getActiveSheet()->getStyle($columnID . $rID)->getNumberFormat()->setFormatCode($formatting); |  | ||||||
|                                                 } else { |                                                 } else { | ||||||
|                                                     $spreadsheet->getActiveSheet()->getStyle($columnID . $rID)->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_GENERAL); |                                                     $cell->setValueExplicit($dataValue, $type); | ||||||
|                                                 } |                                                 } | ||||||
|  | 
 | ||||||
|  |                                                 if ($hasCalculatedValue) { | ||||||
|  |                                                     $cell->setCalculatedValue($dataValue); | ||||||
|  |                                                 } | ||||||
|  | 
 | ||||||
|  |                                                 // Set other properties
 | ||||||
|  |                                                 if ($formatting !== null) { | ||||||
|  |                                                     $spreadsheet->getActiveSheet() | ||||||
|  |                                                         ->getStyle($columnID . $rID) | ||||||
|  |                                                         ->getNumberFormat() | ||||||
|  |                                                         ->setFormatCode($formatting); | ||||||
|  |                                                 } else { | ||||||
|  |                                                     $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); | ||||||
|                                                 } |                                                 } | ||||||
|                                             } |                                             } | ||||||
|                                         } |                                         } | ||||||
|                                     } |                                     } | ||||||
|                                 } |                                 } | ||||||
| 
 | 
 | ||||||
|                                 //    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'])) { | 
 | ||||||
|                                             $rowTo = $rowTo + $cellDataTableAttributes['number-rows-spanned'] - 1; |                                         if ($cellData->hasAttributeNS($tableNs, 'number-rows-spanned')) { | ||||||
|  |                                             $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 +766,51 @@ 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; | ||||||
|  | |||||||
							
								
								
									
										227
									
								
								tests/PhpSpreadsheetTests/Reader/OdsTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								tests/PhpSpreadsheetTests/Reader/OdsTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,227 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace PhpOffice\PhpSpreadsheetTests\Reader; | ||||||
|  | 
 | ||||||
|  | use PhpOffice\PhpSpreadsheet\Cell\DataType; | ||||||
|  | use PhpOffice\PhpSpreadsheet\Reader\Ods; | ||||||
|  | use PhpOffice\PhpSpreadsheet\Style\Font; | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * @todo The class doesn't read the bold/italic/underline properties (rich text) | ||||||
|  |  */ | ||||||
|  | class OdsTest extends \PHPUnit_Framework_TestCase | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * @var \PhpOffice\PhpSpreadsheet\Spreadsheet | ||||||
|  |      */ | ||||||
|  |     public $spreadsheetOOCalcTest; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @var \PhpOffice\PhpSpreadsheet\Spreadsheet | ||||||
|  |      */ | ||||||
|  |     public $spreadsheetData; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @return \PhpOffice\PhpSpreadsheet\Spreadsheet | ||||||
|  |      */ | ||||||
|  |     protected function loadOOCalcTestFile() | ||||||
|  |     { | ||||||
|  |         if (!$this->spreadsheetOOCalcTest) { | ||||||
|  |             $filename = __DIR__ . '/../../../samples/templates/OOCalcTest.ods'; | ||||||
|  | 
 | ||||||
|  |             // Load into this instance
 | ||||||
|  |             $reader = new Ods(); | ||||||
|  |             $this->spreadsheetOOCalcTest = $reader->loadIntoExisting($filename, new \PhpOffice\PhpSpreadsheet\Spreadsheet()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return $this->spreadsheetOOCalcTest; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @return \PhpOffice\PhpSpreadsheet\Spreadsheet | ||||||
|  |      */ | ||||||
|  |     protected function loadDataFile() | ||||||
|  |     { | ||||||
|  |         if (!$this->spreadsheetData) { | ||||||
|  |             $filename = __DIR__ . '/../../data/Reader/Ods/data.ods'; | ||||||
|  | 
 | ||||||
|  |             // Load into this instance
 | ||||||
|  |             $reader = new Ods(); | ||||||
|  |             $this->spreadsheetData = $reader->loadIntoExisting($filename, new \PhpOffice\PhpSpreadsheet\Spreadsheet()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return $this->spreadsheetData; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testReadFileProperties() | ||||||
|  |     { | ||||||
|  |         $filename = __DIR__ . '/../../data/Reader/Ods/data.ods'; | ||||||
|  | 
 | ||||||
|  |         // Load into this instance
 | ||||||
|  |         $reader = new Ods(); | ||||||
|  | 
 | ||||||
|  |         // Test "listWorksheetNames" method
 | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals([ | ||||||
|  |             'Sheet1', | ||||||
|  |             'Second Sheet', | ||||||
|  |         ], $reader->listWorksheetNames($filename)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testLoadWorksheets() | ||||||
|  |     { | ||||||
|  |         $spreadsheet = $this->loadDataFile(); | ||||||
|  | 
 | ||||||
|  |         $this->assertInstanceOf('PhpOffice\PhpSpreadsheet\Spreadsheet', $spreadsheet); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals(2, $spreadsheet->getSheetCount()); | ||||||
|  | 
 | ||||||
|  |         $firstSheet = $spreadsheet->getSheet(0); | ||||||
|  |         $this->assertInstanceOf('PhpOffice\PhpSpreadsheet\Worksheet', $firstSheet); | ||||||
|  | 
 | ||||||
|  |         $secondSheet = $spreadsheet->getSheet(1); | ||||||
|  |         $this->assertInstanceOf('PhpOffice\PhpSpreadsheet\Worksheet', $secondSheet); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testReadValueAndComments() | ||||||
|  |     { | ||||||
|  |         $spreadsheet = $this->loadOOCalcTestFile(); | ||||||
|  | 
 | ||||||
|  |         $firstSheet = $spreadsheet->getSheet(0); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals(29, $firstSheet->getHighestRow()); | ||||||
|  |         $this->assertEquals('N', $firstSheet->getHighestColumn()); | ||||||
|  | 
 | ||||||
|  |         // Simple cell value
 | ||||||
|  |         $this->assertEquals('Test String 1', $firstSheet->getCell('A1')->getValue()); | ||||||
|  | 
 | ||||||
|  |         // Merged cell
 | ||||||
|  |         $this->assertEquals('BOX', $firstSheet->getCell('B18')->getValue()); | ||||||
|  | 
 | ||||||
|  |         // Comments/Annotations
 | ||||||
|  |         $this->assertEquals( | ||||||
|  |             'Test for a simple colour-formatted string', | ||||||
|  |             $firstSheet->getComment('A1')->getText()->getPlainText() | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         // Data types
 | ||||||
|  |         $this->assertEquals(DataType::TYPE_STRING, $firstSheet->getCell('A1')->getDataType()); | ||||||
|  |         $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('B1')->getDataType()); // Int
 | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('B6')->getDataType()); // Float
 | ||||||
|  |         $this->assertEquals(1.23, $firstSheet->getCell('B6')->getValue()); | ||||||
|  |         $this->assertEquals(0, $firstSheet->getCell('G10')->getValue()); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A10')->getDataType()); // Date
 | ||||||
|  |         $this->assertEquals(22269.0, $firstSheet->getCell('A10')->getValue()); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A13')->getDataType()); // Time
 | ||||||
|  |         $this->assertEquals(25569.0625, $firstSheet->getCell('A13')->getValue()); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A15')->getDataType()); // Date + Time
 | ||||||
|  |         $this->assertEquals(22269.0625, $firstSheet->getCell('A15')->getValue()); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A11')->getDataType()); // Fraction
 | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals(DataType::TYPE_BOOL, $firstSheet->getCell('D6')->getDataType()); | ||||||
|  |         $this->assertTrue($firstSheet->getCell('D6')->getValue()); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals(DataType::TYPE_FORMULA, $firstSheet->getCell('C6')->getDataType()); // Formula
 | ||||||
|  |         $this->assertEquals('=TRUE()', $firstSheet->getCell('C6')->getValue()); // Formula
 | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |          * Percentage, Currency | ||||||
|  |          */ | ||||||
|  | 
 | ||||||
|  |         $spreadsheet = $this->loadDataFile(); | ||||||
|  | 
 | ||||||
|  |         $firstSheet = $spreadsheet->getSheet(0); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A1')->getDataType()); // Percentage (10%)
 | ||||||
|  |         $this->assertEquals(0.1, $firstSheet->getCell('A1')->getValue()); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A2')->getDataType()); // Percentage (10.00%)
 | ||||||
|  |         $this->assertEquals(0.1, $firstSheet->getCell('A2')->getValue()); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A4')->getDataType()); // Currency (€10.00)
 | ||||||
|  |         $this->assertEquals(10, $firstSheet->getCell('A4')->getValue()); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A5')->getDataType()); // Currency ($20)
 | ||||||
|  |         $this->assertEquals(20, $firstSheet->getCell('A5')->getValue()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testReadColors() | ||||||
|  |     { | ||||||
|  |         $spreadsheet = $this->loadOOCalcTestFile(); | ||||||
|  |         $firstSheet = $spreadsheet->getSheet(0); | ||||||
|  | 
 | ||||||
|  |         // Background color
 | ||||||
|  | 
 | ||||||
|  |         $style = $firstSheet->getCell('K3')->getStyle(); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals('none', $style->getFill()->getFillType()); | ||||||
|  |         $this->assertEquals('FFFFFFFF', $style->getFill()->getStartColor()->getARGB()); | ||||||
|  |         $this->assertEquals('FF000000', $style->getFill()->getEndColor()->getARGB()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testReadRichText() | ||||||
|  |     { | ||||||
|  |         $spreadsheet = $this->loadOOCalcTestFile(); | ||||||
|  |         $firstSheet = $spreadsheet->getSheet(0); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals( | ||||||
|  |             "I don't know if OOCalc supports Rich Text in the same way as Excel, " . | ||||||
|  |             'And this row should be autofit height with text wrap', | ||||||
|  |             $firstSheet->getCell('A28')->getValue() | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testReadCellsWithRepeatedSpaces() | ||||||
|  |     { | ||||||
|  |         $spreadsheet = $this->loadDataFile(); | ||||||
|  |         $firstSheet = $spreadsheet->getSheet(0); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals('This has    4 spaces before and 2 after  ', $firstSheet->getCell('A8')->getValue()); | ||||||
|  |         $this->assertEquals('This only one after ', $firstSheet->getCell('A9')->getValue()); | ||||||
|  |         $this->assertEquals('Test with DIFFERENT styles     and multiple spaces:  ', $firstSheet->getCell('A10')->getValue()); | ||||||
|  |         $this->assertEquals("test with new \nLines", $firstSheet->getCell('A11')->getValue()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testReadHyperlinks() | ||||||
|  |     { | ||||||
|  |         $spreadsheet = $this->loadOOCalcTestFile(); | ||||||
|  |         $firstSheet = $spreadsheet->getSheet(0); | ||||||
|  | 
 | ||||||
|  |         $hyperlink = $firstSheet->getCell('A29'); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals(DataType::TYPE_STRING, $hyperlink->getDataType()); | ||||||
|  |         $this->assertEquals('PHPExcel', $hyperlink->getValue()); | ||||||
|  |         $this->assertEquals('http://www.phpexcel.net/', $hyperlink->getHyperlink()->getUrl()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* | ||||||
|  |      * Below some test for features not implemented yet | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     public function testReadBoldItalicUnderline() | ||||||
|  |     { | ||||||
|  |         $this->markTestSkipped('Features not implemented yet'); | ||||||
|  | 
 | ||||||
|  |         $spreadsheet = $this->loadOOCalcTestFile(); | ||||||
|  |         $firstSheet = $spreadsheet->getSheet(0); | ||||||
|  | 
 | ||||||
|  |         // Font styles
 | ||||||
|  | 
 | ||||||
|  |         $style = $firstSheet->getCell('A1')->getStyle(); | ||||||
|  |         $this->assertEquals('FF000000', $style->getFont()->getColor()->getARGB()); | ||||||
|  |         $this->assertEquals(11, $style->getFont()->getSize()); | ||||||
|  |         $this->assertEquals(Font::UNDERLINE_NONE, $style->getFont()->getUnderline()); | ||||||
|  | 
 | ||||||
|  |         $style = $firstSheet->getCell('E3')->getStyle(); | ||||||
|  |         $this->assertEquals(Font::UNDERLINE_SINGLE, $style->getFont()->getUnderline()); | ||||||
|  | 
 | ||||||
|  |         $style = $firstSheet->getCell('E1')->getStyle(); | ||||||
|  |         $this->assertTrue($style->getFont()->getBold()); | ||||||
|  |         $this->assertTrue($style->getFont()->getItalic()); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								tests/data/Reader/Ods/data.ods
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/data/Reader/Ods/data.ods
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Adrien Crivelli
						Adrien Crivelli