From 3c7b2e23da28e124f8d337ed7e330616592f3a0b Mon Sep 17 00:00:00 2001 From: Paolo Agostinetto Date: Sat, 18 Feb 2017 13:36:08 +0100 Subject: [PATCH 01/12] Added unit tests for Ods reader --- tests/PhpSpreadsheetTests/Reader/OdsTest.php | 189 +++++++++++++++++++ tests/data/Reader/Ods/numbers.ods | Bin 0 -> 9003 bytes 2 files changed, 189 insertions(+) create mode 100644 tests/PhpSpreadsheetTests/Reader/OdsTest.php create mode 100644 tests/data/Reader/Ods/numbers.ods diff --git a/tests/PhpSpreadsheetTests/Reader/OdsTest.php b/tests/PhpSpreadsheetTests/Reader/OdsTest.php new file mode 100644 index 00000000..b530acee --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/OdsTest.php @@ -0,0 +1,189 @@ +spreadsheet){ + $filename = __DIR__ . '/../../../samples/templates/OOCalcTest.ods'; + + // Create new Spreadsheet + $this->spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); + + // Load into this instance + $reader = new Ods(); + $this->spreadsheet = $reader->loadIntoExisting($filename, $this->spreadsheet); + } + + return $this->spreadsheet; + } + + public function testReadValueAndComments(){ + + $spreadsheet = $this->load(); + + $this->assertInstanceOf('PhpOffice\PhpSpreadsheet\Spreadsheet', $spreadsheet); + + $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 + */ + + $filename = __DIR__ . '/../../data/Reader/Ods/numbers.ods'; + + // Create new Spreadsheet + $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); + + // Load into this instance + $reader = new Ods(); + $spreadsheet = $reader->loadIntoExisting($filename, $spreadsheet); + $firstSheet = $spreadsheet->getActiveSheet(); + + $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->load(); + $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()); + } + + /* + * Below some test for features not implemented yet + */ + + public function testReadBoldItalicUnderline() + { + $this->markTestSkipped("Features not implemented yet"); + + $spreadsheet = $this->load(); + $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()); + } + + public function testReadRichTExt(){ + + $this->markTestSkipped("Features not implemented yet"); + + $spreadsheet = $this->load(); + $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 testReadHyperlinks(){ + + $this->markTestSkipped("Features not implemented fully"); + + $spreadsheet = $this->load(); + $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()); + } +} diff --git a/tests/data/Reader/Ods/numbers.ods b/tests/data/Reader/Ods/numbers.ods new file mode 100644 index 0000000000000000000000000000000000000000..b386a340c8df3f66af4eec7858eb2e9d9f127eff GIT binary patch literal 9003 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 Date: Sat, 18 Feb 2017 13:55:22 +0100 Subject: [PATCH 02/12] Ods reader: test loading of Worksheets --- tests/PhpSpreadsheetTests/Reader/OdsTest.php | 35 ++++++++++++++----- tests/data/Reader/Ods/data.ods | Bin 0 -> 9259 bytes tests/data/Reader/Ods/numbers.ods | Bin 9003 -> 0 bytes 3 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 tests/data/Reader/Ods/data.ods delete mode 100644 tests/data/Reader/Ods/numbers.ods diff --git a/tests/PhpSpreadsheetTests/Reader/OdsTest.php b/tests/PhpSpreadsheetTests/Reader/OdsTest.php index b530acee..56a37338 100644 --- a/tests/PhpSpreadsheetTests/Reader/OdsTest.php +++ b/tests/PhpSpreadsheetTests/Reader/OdsTest.php @@ -28,7 +28,7 @@ class OdsTest extends \PHPUnit_Framework_TestCase /** * @return \PhpOffice\PhpSpreadsheet\Spreadsheet */ - protected function load(){ + protected function loadOOCalcTest(){ if(!$this->spreadsheet){ $filename = __DIR__ . '/../../../samples/templates/OOCalcTest.ods'; @@ -44,9 +44,28 @@ class OdsTest extends \PHPUnit_Framework_TestCase return $this->spreadsheet; } + public function testLoadWorksheets() + { + $filename = __DIR__ . '/../../data/Reader/Ods/data.ods'; + + // Load into this instance + $reader = new Ods(); + $spreadsheet = $reader->loadIntoExisting($filename, new \PhpOffice\PhpSpreadsheet\Spreadsheet()); + + $this->assertInstanceOf('PhpOffice\PhpSpreadsheet\Spreadsheet', $spreadsheet); + +// $this->assertCount(1, $spreadsheet->getAllSheets()); + + $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->load(); + $spreadsheet = $this->loadOOCalcTest(); $this->assertInstanceOf('PhpOffice\PhpSpreadsheet\Spreadsheet', $spreadsheet); @@ -96,7 +115,7 @@ class OdsTest extends \PHPUnit_Framework_TestCase * Percentage, Currency */ - $filename = __DIR__ . '/../../data/Reader/Ods/numbers.ods'; + $filename = __DIR__ . '/../../data/Reader/Ods/data.ods'; // Create new Spreadsheet $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); @@ -104,7 +123,7 @@ class OdsTest extends \PHPUnit_Framework_TestCase // Load into this instance $reader = new Ods(); $spreadsheet = $reader->loadIntoExisting($filename, $spreadsheet); - $firstSheet = $spreadsheet->getActiveSheet(); + $firstSheet = $spreadsheet->getSheet(0); $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell("A1")->getDataType()); // Percentage (10%) $this->assertEquals(0.1, $firstSheet->getCell("A1")->getValue()); @@ -121,7 +140,7 @@ class OdsTest extends \PHPUnit_Framework_TestCase public function testReadColors() { - $spreadsheet = $this->load(); + $spreadsheet = $this->loadOOCalcTest(); $firstSheet = $spreadsheet->getSheet(0); // Background color @@ -141,7 +160,7 @@ class OdsTest extends \PHPUnit_Framework_TestCase { $this->markTestSkipped("Features not implemented yet"); - $spreadsheet = $this->load(); + $spreadsheet = $this->loadOOCalcTest(); $firstSheet = $spreadsheet->getSheet(0); // Font styles @@ -163,7 +182,7 @@ class OdsTest extends \PHPUnit_Framework_TestCase $this->markTestSkipped("Features not implemented yet"); - $spreadsheet = $this->load(); + $spreadsheet = $this->loadOOCalcTest(); $firstSheet = $spreadsheet->getSheet(0); $this->assertEquals( @@ -177,7 +196,7 @@ class OdsTest extends \PHPUnit_Framework_TestCase $this->markTestSkipped("Features not implemented fully"); - $spreadsheet = $this->load(); + $spreadsheet = $this->loadOOCalcTest(); $firstSheet = $spreadsheet->getSheet(0); $hyperlink = $firstSheet->getCell("A29"); diff --git a/tests/data/Reader/Ods/data.ods b/tests/data/Reader/Ods/data.ods new file mode 100644 index 0000000000000000000000000000000000000000..7abc5c45e6e6864ecb7b5b1585d183174a240df6 GIT binary patch literal 9259 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 Date: Sat, 18 Feb 2017 20:38:10 +0100 Subject: [PATCH 03/12] Ods reader: method loadIntoExisting() now uses DOMDocument to parse content --- src/PhpSpreadsheet/Reader/Ods.php | 368 ++++++++++++++++++++++-------- 1 file changed, 267 insertions(+), 101 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index 09d539b0..17cece18 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -4,7 +4,10 @@ namespace PhpOffice\PhpSpreadsheet\Reader; use DateTime; use DateTimeZone; +use PhpOffice\PhpSpreadsheet\Calculation; +use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Shared\File; +use PhpOffice\PhpSpreadsheet\Style\NumberFormat; /** * Copyright (c) 2006 - 2016 PhpSpreadsheet. @@ -292,11 +295,16 @@ class Ods extends BaseReader implements IReader $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); + /** @var \ZipArchive $zip */ $zip = new $zipClass(); if (!$zip->open($pFilename)) { throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.'); } + /* + * Meta + */ + $xml = simplexml_load_string( $this->securityScan($zip->getFromName('meta.xml')), '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')), - 'SimpleXMLElement', \PhpOffice\PhpSpreadsheet\Settings::getLibXmlLoaderOptions() ); - $namespacesContent = $xml->getNamespaces(true); - $workbook = $xml->children($namespacesContent['office']); - foreach ($workbook->body->spreadsheet as $workbookData) { - $workbookData = $workbookData->children($namespacesContent['table']); + $officeNs = $dom->lookupNamespaceUri("office"); + $tableNs = $dom->lookupNamespaceUri("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; - foreach ($workbookData->table as $worksheetDataSet) { - $worksheetData = $worksheetDataSet->children($namespacesContent['table']); - $worksheetDataAttributes = $worksheetDataSet->attributes($namespacesContent['table']); - if ((isset($this->loadSheetsOnly)) && (isset($worksheetDataAttributes['name'])) && - (!in_array($worksheetDataAttributes['name'], $this->loadSheetsOnly))) { + foreach ($tables as $worksheetDataSet) { + /** @var \DOMElement $worksheetDataSet */ + + $worksheetName = $worksheetDataSet->getAttributeNS($tableNs, "name"); + + // Check loadSheetsOnly + if (isset($this->loadSheetsOnly) + && $worksheetName + && !in_array($worksheetName, $this->loadSheetsOnly)) { continue; } - // Create new Worksheet + // Create sheet $spreadsheet->createSheet(); $spreadsheet->setActiveSheetIndex($worksheetID); - if (isset($worksheetDataAttributes['name'])) { - $worksheetName = (string) $worksheetDataAttributes['name']; + + if ($worksheetName) { // 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 // bringing the worksheet name in line with the formula, not the reverse $spreadsheet->getActiveSheet()->setTitle($worksheetName, false); } + // Go through every child of table element $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) { case 'table-header-rows': - foreach ($rowData as $keyRowData => $cellData) { - $rowData = $cellData; - break; - } + /// TODO :: Figure this out. This is only a partial implementation I guess. + // ($rowData it's not used at all) + +// foreach ($rowData as $keyRowData => $cellData) { +// $rowData = $cellData; +// break; +// } break; 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'; - foreach ($rowData as $key => $cellData) { + foreach ($childNode->childNodes as $key => $cellData) { + /** @var \DOMElement $cellData */ + if ($this->getReadFilter() !== null) { if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) { ++$columnID; @@ -433,93 +485,98 @@ class Ods extends BaseReader implements IReader } } - $cellDataText = (isset($namespacesContent['text'])) ? $cellData->children($namespacesContent['text']) : ''; - $cellDataOffice = $cellData->children($namespacesContent['office']); - $cellDataOfficeAttributes = $cellData->attributes($namespacesContent['office']); - $cellDataTableAttributes = $cellData->attributes($namespacesContent['table']); - - $type = $formatting = $hyperlink = null; + // Initialize variables + $formatting = $hyperlink = null; $hasCalculatedValue = false; $cellDataFormula = ''; - if (isset($cellDataTableAttributes['formula'])) { - $cellDataFormula = $cellDataTableAttributes['formula']; + + if ($cellData->hasAttributeNS($tableNs, "formula")) { + $cellDataFormula = $cellData->getAttributeNS($tableNs, "formula"); $hasCalculatedValue = true; } - if (isset($cellDataOffice->annotation)) { - $annotationText = $cellDataOffice->annotation->children($namespacesContent['text']); - $textArray = []; - foreach ($annotationText as $t) { - if (isset($t->span)) { - foreach ($t->span as $text) { - $textArray[] = (string) $text; - } - } else { - $textArray[] = (string) $t; - } - } - $text = implode("\n", $textArray); - $spreadsheet->getActiveSheet()->getComment($columnID . $rowID)->setText($this->parseRichText($text)); + // Annotations + $annotation = $cellData->getElementsByTagNameNS($officeNs, "annotation"); + + if ($annotation->length > 0) { + $textNode = $annotation->item(0)->getElementsByTagNameNS($textNs, "p"); + + if($textNode->length > 0){ + + $text = $this->scanElementForText($textNode->item(0)); + + $spreadsheet->getActiveSheet() + ->getComment($columnID . $rowID) + ->setText($this->parseRichText($text)); // ->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) $dataArray = []; // Text can have multiple text:p and within those, multiple text:span. // text:p newlines, but text:span does not. // Also, here we assume there is no text data is span fields are specified, since // we have no way of knowing proper positioning anyway. - foreach ($cellDataText->p as $pData) { - if (isset($pData->span)) { - // span sections do not newline, so we just create one large string here - $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); - } + + foreach ($paragraphs as $pData) { + $dataArray[] = $this->scanElementForText($pData); } $allCellDataText = implode($dataArray, "\n"); - switch ($cellDataOfficeAttributes['value-type']) { + $type = $cellData->getAttributeNS($officeNs, 'value-type'); + + switch ($type) { case 'string': - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING; + $type = DataType::TYPE_STRING; $dataValue = $allCellDataText; - if (isset($dataValue->a)) { - $dataValue = $dataValue->a; - $cellXLinkAttributes = $dataValue->attributes($namespacesContent['xlink']); - $hyperlink = $cellXLinkAttributes['href']; - } + + /// TODO :: Fix this: usually it's text:p > text:a, not just text:a +// if (isset($dataValue->a)) { +// $dataValue = $dataValue->a; +// $cellXLinkAttributes = $dataValue->attributes($namespacesContent['xlink']); +// $hyperlink = $cellXLinkAttributes['href']; +// } break; case 'boolean': - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_BOOL; + $type = DataType::TYPE_BOOL; $dataValue = ($allCellDataText == 'TRUE') ? true : false; break; case 'percentage': - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; - $dataValue = (float) $cellDataOfficeAttributes['value']; + $type = DataType::TYPE_NUMERIC; + $dataValue = (float)$cellData->getAttributeNS($officeNs, 'value'); + if (floor($dataValue) == $dataValue) { $dataValue = (int) $dataValue; } $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_PERCENTAGE_00; break; case 'currency': - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; - $dataValue = (float) $cellDataOfficeAttributes['value']; + $type = DataType::TYPE_NUMERIC; + $dataValue = (float)$cellData->getAttributeNS($officeNs, 'value'); + if (floor($dataValue) == $dataValue) { $dataValue = (int) $dataValue; } $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_CURRENCY_USD_SIMPLE; break; case 'float': - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; - $dataValue = (float) $cellDataOfficeAttributes['value']; + $type = DataType::TYPE_NUMERIC; + $dataValue = (float)$cellData->getAttributeNS($officeNs, 'value'); + if (floor($dataValue) == $dataValue) { if ($dataValue == (int) $dataValue) { $dataValue = (int) $dataValue; @@ -529,41 +586,70 @@ class Ods extends BaseReader implements IReader } break; case 'date': - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; - $dateObj = new DateTime($cellDataOfficeAttributes['date-value'], $GMT); + $type = DataType::TYPE_NUMERIC; + $value = $cellData->getAttributeNS($officeNs, 'date-value'); + + $dateObj = new DateTime($value, $GMT); $dateObj->setTimeZone($timezoneObj); - list($year, $month, $day, $hour, $minute, $second) = explode(' ', $dateObj->format('Y m d H i s')); - $dataValue = \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel($year, $month, $day, $hour, $minute, $second); + list($year, $month, $day, $hour, $minute, $second) = explode(' ', + $dateObj->format('Y m d H i s') + ); + + $dataValue = \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel( + $year, $month, $day, $hour, $minute, $second + ); + 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 { $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_XLSX15; } break; case 'time': - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; - $dataValue = \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(strtotime('01-01-1970 ' . implode(':', sscanf($cellDataOfficeAttributes['time-value'], 'PT%dH%dM%dS')))); + $type = DataType::TYPE_NUMERIC; + + $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; break; + + default: + $dataValue = null; } } else { - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NULL; + $type = DataType::TYPE_NULL; $dataValue = null; } if ($hasCalculatedValue) { - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_FORMULA; + $type = DataType::TYPE_FORMULA; $cellDataFormula = substr($cellDataFormula, strpos($cellDataFormula, ':=') + 1); $temp = explode('"', $cellDataFormula); $tKey = false; foreach ($temp as &$value) { // Only replace in alternate array entries (i.e. non-quoted blocks) 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 - $value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/Ui', '$1:$2', $value); // Cell range reference - $value = preg_replace('/\[\.([^\.]+)\]/Ui', '$1', $value); // Simple cell reference - $value = \PhpOffice\PhpSpreadsheet\Calculation::translateSeparator(';', ',', $value, $inBraces); + + // Cell range reference in another sheet + $value = preg_replace('/\[([^\.]+)\.([^\.]+):\.([^\.]+)\]/Ui', '$1!$2:$3', $value); + + // 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); @@ -571,26 +657,55 @@ class Ods extends BaseReader implements IReader $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) { for ($i = 0; $i < $colRepeats; ++$i) { + if ($i > 0) { ++$columnID; } - if ($type !== \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NULL) { + + if ($type !== DataType::TYPE_NULL) { for ($rowAdjust = 0; $rowAdjust < $rowRepeats; ++$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) { - $spreadsheet->getActiveSheet()->getCell($columnID . $rID)->setCalculatedValue($dataValue); + $cell->setCalculatedValue($dataValue); } + + // Set other properties if ($formatting !== null) { - $spreadsheet->getActiveSheet()->getStyle($columnID . $rID)->getNumberFormat()->setFormatCode($formatting); + $spreadsheet->getActiveSheet() + ->getStyle($columnID . $rID) + ->getNumberFormat() + ->setFormatCode($formatting); } 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) { - $spreadsheet->getActiveSheet()->getCell($columnID . $rID)->getHyperlink()->setUrl($hyperlink); + $cell->getHyperlink() + ->setUrl($hyperlink); } } } @@ -598,15 +713,23 @@ class Ods extends BaseReader implements IReader } // Merged cells - if ((isset($cellDataTableAttributes['number-columns-spanned'])) || (isset($cellDataTableAttributes['number-rows-spanned']))) { - if (($type !== \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NULL) || (!$this->readDataOnly)) { + if ($childNode->hasAttributeNS($tableNs, 'number-columns-spanned') + || $childNode->hasAttributeNS($tableNs, 'number-rows-spanned') + ) { + if (($type !== DataType::TYPE_NULL) || (!$this->readDataOnly)) { $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; - 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; $spreadsheet->getActiveSheet()->mergeCells($cellRange); @@ -627,10 +750,53 @@ class Ods extends BaseReader implements IReader 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 = '') { $value = new \PhpOffice\PhpSpreadsheet\RichText(); - $value->createText($is); return $value; From 1dba2d1766ba23b0bb29852f10ce91ce13f39ad2 Mon Sep 17 00:00:00 2001 From: Paolo Agostinetto Date: Sat, 18 Feb 2017 20:49:48 +0100 Subject: [PATCH 04/12] Ods reader: tests for repeated spaces and rich text --- tests/PhpSpreadsheetTests/Reader/OdsTest.php | 96 +++++++++++-------- tests/data/Reader/Ods/data.ods | Bin 9259 -> 11764 bytes 2 files changed, 56 insertions(+), 40 deletions(-) diff --git a/tests/PhpSpreadsheetTests/Reader/OdsTest.php b/tests/PhpSpreadsheetTests/Reader/OdsTest.php index 56a37338..442c2c23 100644 --- a/tests/PhpSpreadsheetTests/Reader/OdsTest.php +++ b/tests/PhpSpreadsheetTests/Reader/OdsTest.php @@ -23,34 +23,48 @@ class OdsTest extends \PHPUnit_Framework_TestCase /** * @var \PhpOffice\PhpSpreadsheet\Spreadsheet */ - public $spreadsheet; + public $spreadsheetOOCalcTest; + + /** + * @var \PhpOffice\PhpSpreadsheet\Spreadsheet + */ + public $spreadsheetData; /** * @return \PhpOffice\PhpSpreadsheet\Spreadsheet */ - protected function loadOOCalcTest(){ + protected function loadOOCalcTestFile(){ - if(!$this->spreadsheet){ + if(!$this->spreadsheetOOCalcTest){ $filename = __DIR__ . '/../../../samples/templates/OOCalcTest.ods'; - // Create new Spreadsheet - $this->spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); - // Load into this instance $reader = new Ods(); - $this->spreadsheet = $reader->loadIntoExisting($filename, $this->spreadsheet); + $this->spreadsheetOOCalcTest = $reader->loadIntoExisting($filename, new \PhpOffice\PhpSpreadsheet\Spreadsheet()); } - return $this->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 testLoadWorksheets() { - $filename = __DIR__ . '/../../data/Reader/Ods/data.ods'; - - // Load into this instance - $reader = new Ods(); - $spreadsheet = $reader->loadIntoExisting($filename, new \PhpOffice\PhpSpreadsheet\Spreadsheet()); + $spreadsheet = $this->loadDataFile(); $this->assertInstanceOf('PhpOffice\PhpSpreadsheet\Spreadsheet', $spreadsheet); @@ -65,9 +79,7 @@ class OdsTest extends \PHPUnit_Framework_TestCase public function testReadValueAndComments(){ - $spreadsheet = $this->loadOOCalcTest(); - - $this->assertInstanceOf('PhpOffice\PhpSpreadsheet\Spreadsheet', $spreadsheet); + $spreadsheet = $this->loadOOCalcTestFile(); $firstSheet = $spreadsheet->getSheet(0); @@ -115,14 +127,8 @@ class OdsTest extends \PHPUnit_Framework_TestCase * Percentage, Currency */ - $filename = __DIR__ . '/../../data/Reader/Ods/data.ods'; + $spreadsheet = $this->loadDataFile(); - // Create new Spreadsheet - $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); - - // Load into this instance - $reader = new Ods(); - $spreadsheet = $reader->loadIntoExisting($filename, $spreadsheet); $firstSheet = $spreadsheet->getSheet(0); $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell("A1")->getDataType()); // Percentage (10%) @@ -140,7 +146,7 @@ class OdsTest extends \PHPUnit_Framework_TestCase public function testReadColors() { - $spreadsheet = $this->loadOOCalcTest(); + $spreadsheet = $this->loadOOCalcTestFile(); $firstSheet = $spreadsheet->getSheet(0); // Background color @@ -152,6 +158,30 @@ class OdsTest extends \PHPUnit_Framework_TestCase $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()); + + } + /* * Below some test for features not implemented yet */ @@ -160,7 +190,7 @@ class OdsTest extends \PHPUnit_Framework_TestCase { $this->markTestSkipped("Features not implemented yet"); - $spreadsheet = $this->loadOOCalcTest(); + $spreadsheet = $this->loadOOCalcTestFile(); $firstSheet = $spreadsheet->getSheet(0); // Font styles @@ -178,25 +208,11 @@ class OdsTest extends \PHPUnit_Framework_TestCase $this->assertTrue($style->getFont()->getItalic()); } - public function testReadRichTExt(){ - - $this->markTestSkipped("Features not implemented yet"); - - $spreadsheet = $this->loadOOCalcTest(); - $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 testReadHyperlinks(){ $this->markTestSkipped("Features not implemented fully"); - $spreadsheet = $this->loadOOCalcTest(); + $spreadsheet = $this->loadOOCalcTestFile(); $firstSheet = $spreadsheet->getSheet(0); $hyperlink = $firstSheet->getCell("A29"); diff --git a/tests/data/Reader/Ods/data.ods b/tests/data/Reader/Ods/data.ods index 7abc5c45e6e6864ecb7b5b1585d183174a240df6..5f58d457a42c0a1c5655d6d4b889402425b25a5a 100644 GIT binary patch delta 8135 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 delta 5599 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 From 6d6353c0f139a3557849dd6709612557a1967a48 Mon Sep 17 00:00:00 2001 From: Paolo Agostinetto Date: Sat, 18 Feb 2017 20:59:14 +0100 Subject: [PATCH 05/12] Ods reader: fix reading of cells with hyperlinks --- src/PhpSpreadsheet/Reader/Ods.php | 17 +++++++----- tests/PhpSpreadsheetTests/Reader/OdsTest.php | 27 +++++++++----------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index 17cece18..9e855ab8 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -403,6 +403,7 @@ class Ods extends BaseReader implements IReader $officeNs = $dom->lookupNamespaceUri("office"); $tableNs = $dom->lookupNamespaceUri("table"); $textNs = $dom->lookupNamespaceUri("text"); + $xlinkNs = $dom->lookupNamespaceUri("xlink"); $spreadsheets = $dom->getElementsByTagNameNS($officeNs, "body") ->item(0) @@ -513,7 +514,10 @@ class Ods extends BaseReader implements IReader } // Content + + /** @var \DOMElement[] $paragraphs */ $paragraphs = []; + foreach ($cellData->childNodes as $item) { /** @var \DOMElement $item */ @@ -544,12 +548,13 @@ class Ods extends BaseReader implements IReader $type = DataType::TYPE_STRING; $dataValue = $allCellDataText; - /// TODO :: Fix this: usually it's text:p > text:a, not just text:a -// if (isset($dataValue->a)) { -// $dataValue = $dataValue->a; -// $cellXLinkAttributes = $dataValue->attributes($namespacesContent['xlink']); -// $hyperlink = $cellXLinkAttributes['href']; -// } + foreach ($paragraphs as $paragraph) { + $link = $paragraph->getElementsByTagNameNS($textNs, "a"); + if($link->length > 0){ + $hyperlink = $link->item(0)->getAttributeNS($xlinkNs, "href"); + } + } + break; case 'boolean': $type = DataType::TYPE_BOOL; diff --git a/tests/PhpSpreadsheetTests/Reader/OdsTest.php b/tests/PhpSpreadsheetTests/Reader/OdsTest.php index 442c2c23..1b53278a 100644 --- a/tests/PhpSpreadsheetTests/Reader/OdsTest.php +++ b/tests/PhpSpreadsheetTests/Reader/OdsTest.php @@ -15,7 +15,6 @@ use PhpOffice\PhpSpreadsheet\Style\Font; /* * @todo Fix sheet name (is not imported correctly) * @todo Sheets count is incorrect - * @todo Support rich text: cells values with styles in them are not imported correctly (text missing!) * @todo The class doesn't read the bold/italic/underline properties */ class OdsTest extends \PHPUnit_Framework_TestCase @@ -182,6 +181,18 @@ class OdsTest extends \PHPUnit_Framework_TestCase } + 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 */ @@ -207,18 +218,4 @@ class OdsTest extends \PHPUnit_Framework_TestCase $this->assertTrue($style->getFont()->getBold()); $this->assertTrue($style->getFont()->getItalic()); } - - public function testReadHyperlinks(){ - - $this->markTestSkipped("Features not implemented fully"); - - $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()); - } } From 1667056515e40beddca6a378373045829d4c441f Mon Sep 17 00:00:00 2001 From: Paolo Agostinetto Date: Sat, 18 Feb 2017 21:02:33 +0100 Subject: [PATCH 06/12] IReadFilter interface: fixed docblocks --- src/PhpSpreadsheet/Reader/IReadFilter.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/IReadFilter.php b/src/PhpSpreadsheet/Reader/IReadFilter.php index a4131c09..75b613f3 100644 --- a/src/PhpSpreadsheet/Reader/IReadFilter.php +++ b/src/PhpSpreadsheet/Reader/IReadFilter.php @@ -29,9 +29,9 @@ interface IReadFilter /** * Should this cell be read? * - * @param $column Column address (as a string value like "A", or "IV") - * @param $row Row number - * @param $worksheetName Optional worksheet name + * @param $column string Column address (as a string value like "A", or "IV") + * @param $row int Row number + * @param $worksheetName string Optional worksheet name * * @return bool */ From 4cf7beef3101fbe9e932cde7ffb56949ca63ce05 Mon Sep 17 00:00:00 2001 From: Paolo Agostinetto Date: Sat, 18 Feb 2017 21:10:06 +0100 Subject: [PATCH 07/12] Ods reader: few readability tweaks --- src/PhpSpreadsheet/Reader/Ods.php | 47 ++++++++++++++++++------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index 9e855ab8..ee29af4b 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -126,10 +126,10 @@ class Ods extends BaseReader implements IReader ); $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(); while ($xml->read()) { - // Quickly jump through to the office:body node + // Quickly jump through to the office:body node while ($xml->name !== 'office:body') { if ($xml->isEmptyElement) { $xml->read(); @@ -137,10 +137,10 @@ class Ods extends BaseReader implements IReader $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()) { 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 { $worksheetNames[] = $xml->getAttribute('table:name'); $xml->next(); @@ -180,10 +180,10 @@ class Ods extends BaseReader implements IReader ); $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(); while ($xml->read()) { - // Quickly jump through to the office:body node + // Quickly jump through to the office:body node while ($xml->name !== 'office:body') { if ($xml->isEmptyElement) { $xml->read(); @@ -191,7 +191,7 @@ class Ods extends BaseReader implements IReader $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()) { if ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) { $worksheetNames[] = $xml->getAttribute('table:name'); @@ -204,7 +204,7 @@ class Ods extends BaseReader implements IReader '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; do { $xml->read(); @@ -214,7 +214,7 @@ class Ods extends BaseReader implements IReader $tmpInfo['totalRows'] += $rowspan; $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells); $currCells = 0; - // Step into the row + // Step into the row $xml->read(); do { if ($xml->name == 'table:table-cell' && $xml->nodeType == XMLReader::ELEMENT) { @@ -432,9 +432,9 @@ class Ods extends BaseReader implements IReader $spreadsheet->setActiveSheetIndex($worksheetID); if ($worksheetName) { - // 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 - // bringing the worksheet name in line with the formula, not the reverse + // 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 + // bringing the worksheet name in line with the formula, not the reverse $spreadsheet->getActiveSheet()->setTitle($worksheetName, false); } @@ -459,7 +459,8 @@ class Ods extends BaseReader implements IReader switch ($key) { case 'table-header-rows': /// TODO :: Figure this out. This is only a partial implementation I guess. - // ($rowData it's not used at all) + // ($rowData it's not used at all and I'm not sure that PHPExcel + // has an API for this) // foreach ($rowData as $keyRowData => $cellData) { // $rowData = $cellData; @@ -531,6 +532,7 @@ class Ods extends BaseReader implements IReader // Consolidate if there are multiple p records (maybe with spans as well) $dataArray = []; + // Text can have multiple text:p and within those, multiple text:span. // text:p newlines, but text:span does not. // Also, here we assume there is no text data is span fields are specified, since @@ -639,26 +641,28 @@ class Ods extends BaseReader implements IReader $temp = explode('"', $cellDataFormula); $tKey = false; 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) { - // Cell range reference in another sheet + // Cell range reference in another sheet $value = preg_replace('/\[([^\.]+)\.([^\.]+):\.([^\.]+)\]/Ui', '$1!$2:$3', $value); - // Cell reference in another sheet + // Cell reference in another sheet $value = preg_replace('/\[([^\.]+)\.([^\.]+)\]/Ui', '$1!$2', $value); - // Cell range reference + // Cell range reference $value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/Ui', '$1:$2', $value); - // Simple cell reference + // Simple cell reference $value = preg_replace('/\[\.([^\.]+)\]/Ui', '$1', $value); $value = Calculation::translateSeparator(';', ',', $value, $inBraces); } } unset($value); - // Then rebuild the formula string + + // Then rebuild the formula string $cellDataFormula = implode('"', $temp); } @@ -717,7 +721,7 @@ class Ods extends BaseReader implements IReader } } - // Merged cells + // Merged cells if ($childNode->hasAttributeNS($tableNs, 'number-columns-spanned') || $childNode->hasAttributeNS($tableNs, 'number-rows-spanned') ) { @@ -732,10 +736,13 @@ class Ods extends BaseReader implements IReader $columnTo = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($columnIndex); } + $rowTo = $rowID; + if ($cellData->hasAttributeNS($tableNs, 'number-rows-spanned')) { $rowTo = $rowTo + (int)$cellData->getAttributeNS($tableNs, 'number-rows-spanned') - 1; } + $cellRange = $columnID . $rowID . ':' . $columnTo . $rowTo; $spreadsheet->getActiveSheet()->mergeCells($cellRange); } From b50ca6e27c3d6d2c8ca49a07943843152adb3627 Mon Sep 17 00:00:00 2001 From: Paolo Agostinetto Date: Mon, 20 Feb 2017 21:00:42 +0100 Subject: [PATCH 08/12] Ods reader: added/fixed PHPDoc and classes without namespace --- src/PhpSpreadsheet/Reader/Ods.php | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index ee29af4b..2d44ee6c 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -64,7 +64,10 @@ class Ods extends BaseReader implements IReader $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); $mimeType = 'UNKNOWN'; + // Load file + + /** @var \ZipArchive $zip */ $zip = new $zipClass(); if ($zip->open($pFilename) === true) { // check if it is an OOXML archive @@ -104,6 +107,7 @@ class Ods extends BaseReader implements IReader * @param string $pFilename * * @throws Exception + * @return string[] */ public function listWorksheetNames($pFilename) { @@ -111,6 +115,7 @@ class Ods extends BaseReader implements IReader $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); + /** @var \ZipArchive $zip */ $zip = new $zipClass(); if (!$zip->open($pFilename)) { throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.'); @@ -118,8 +123,8 @@ class Ods extends BaseReader implements IReader $worksheetNames = []; - $xml = new XMLReader(); - $res = $xml->xml( + $xml = new \XMLReader(); + $xml->xml( $this->securityScanFile('zip://' . realpath($pFilename) . '#content.xml'), null, \PhpOffice\PhpSpreadsheet\Settings::getLibXmlLoaderOptions() @@ -139,12 +144,12 @@ class Ods extends BaseReader implements IReader } // Now read each node until we find our first table:table node 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 do { $worksheetNames[] = $xml->getAttribute('table:name'); $xml->next(); - } while ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT); + } while ($xml->name == 'table:table' && $xml->nodeType == \XMLReader::ELEMENT); } } } @@ -158,6 +163,7 @@ class Ods extends BaseReader implements IReader * @param string $pFilename * * @throws Exception + * @return array */ public function listWorksheetInfo($pFilename) { @@ -167,12 +173,13 @@ class Ods extends BaseReader implements IReader $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); + /** @var \ZipArchive $zip */ $zip = new $zipClass(); if (!$zip->open($pFilename)) { throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.'); } - $xml = new XMLReader(); + $xml = new \XMLReader(); $res = $xml->xml( $this->securityScanFile('zip://' . realpath($pFilename) . '#content.xml'), null, @@ -193,7 +200,7 @@ class Ods extends BaseReader implements IReader } // Now read each node until we find our first table:table node 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'); $tmpInfo = [ @@ -208,7 +215,7 @@ class Ods extends BaseReader implements IReader $currCells = 0; do { $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 = empty($rowspan) ? 1 : $rowspan; $tmpInfo['totalRows'] += $rowspan; @@ -217,14 +224,14 @@ class Ods extends BaseReader implements IReader // Step into the row $xml->read(); do { - if ($xml->name == 'table:table-cell' && $xml->nodeType == XMLReader::ELEMENT) { + if ($xml->name == 'table:table-cell' && $xml->nodeType == \XMLReader::ELEMENT) { if (!$xml->isEmptyElement) { ++$currCells; $xml->next(); } else { $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'); $currCells += $mergeSize; $xml->read(); From c954eddf5760891110c6fd7913de0b9e0ba6f0a2 Mon Sep 17 00:00:00 2001 From: Paolo Agostinetto Date: Mon, 20 Feb 2017 21:02:04 +0100 Subject: [PATCH 09/12] Ods reader: fix sheet count and added a test for sheet names --- src/PhpSpreadsheet/Reader/Ods.php | 4 +++- tests/PhpSpreadsheetTests/Reader/OdsTest.php | 21 +++++++++++++++---- tests/data/Reader/Ods/data.ods | Bin 11764 -> 11764 bytes 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index 2d44ee6c..e5c9c600 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -435,7 +435,9 @@ class Ods extends BaseReader implements IReader } // Create sheet - $spreadsheet->createSheet(); + if($worksheetID > 0){ + $spreadsheet->createSheet(); // First sheet is added by default + } $spreadsheet->setActiveSheetIndex($worksheetID); if ($worksheetName) { diff --git a/tests/PhpSpreadsheetTests/Reader/OdsTest.php b/tests/PhpSpreadsheetTests/Reader/OdsTest.php index 1b53278a..e71f52fd 100644 --- a/tests/PhpSpreadsheetTests/Reader/OdsTest.php +++ b/tests/PhpSpreadsheetTests/Reader/OdsTest.php @@ -13,9 +13,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Ods; use PhpOffice\PhpSpreadsheet\Style\Font; /* - * @todo Fix sheet name (is not imported correctly) - * @todo Sheets count is incorrect - * @todo The class doesn't read the bold/italic/underline properties + * @todo The class doesn't read the bold/italic/underline properties (rich text) */ class OdsTest extends \PHPUnit_Framework_TestCase { @@ -61,13 +59,28 @@ class OdsTest extends \PHPUnit_Framework_TestCase 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->assertCount(1, $spreadsheet->getAllSheets()); + $this->assertEquals(2, $spreadsheet->getSheetCount()); $firstSheet = $spreadsheet->getSheet(0); $this->assertInstanceOf('PhpOffice\PhpSpreadsheet\Worksheet', $firstSheet); diff --git a/tests/data/Reader/Ods/data.ods b/tests/data/Reader/Ods/data.ods index 5f58d457a42c0a1c5655d6d4b889402425b25a5a..3171eb6b7e31487b89848a91b89cae9adaa7ad35 100644 GIT binary patch delta 1162 zcmewo{Uw?wz?+#xgn@yBf#LeXi9A6}*B5R~xXTZuCff@r)ZY(&WF=C!zn=B-b!#uj zquk1GL|RzGV;8bU-glDrI%e`xMbrMz>-<}XUR}Gk_Cm$?b1%9toBTZY@@4tH$ud-DVSYw`^w(6_b%Gsntm-_yu{AkEXU#spN+>6!FHz;ntN2H z3*YO=H{aSYk&RX6BO7DA+!NkJ^A-Fj^CBNVedCs*BNv(MA~*Y%6UY8Qz4c+MA74Fb zn3}gUX#Mrm7w_H;nLWv}=<}?u^!+BTntwAjVg#&(C0D=tm1()}npd=Xe9N*`TgwV> z*w(#H%2xPS-ooErLzVd**(~-vP z7b?C7Lw{L!UGpg0=quO$FzbQJo+nM7Oc`q@@J%hRlxkA@v!#LO#Cw^)rJbBlmj)Y& z3v|ioykY4HE_1gOGU8m8sd)acNWS9#dJTb{{*qOTTo`hGNpwoTI`G6hBA|@ntfKIV zu-17W>ckf)UH-E4Ki3D|=mn}h7u*|mNIqQoj(y+f?Flb$9qilG^3>r@l4?!+tz}LD z>m44jd)ay~5;^rkpoFPlf=h34%zbmV1FZ`W?dy!YEqo{D!qxD$Nsnw7i#R)MnU=ul zdBocKzPv!(U$AK3wu5>RSKRwUL?4FRxzs zR`9^af3JDZ!i#nLfJ3X zH_E)j>$m=64e(~?U~~VRG@FHip;(21 z0XdORJ}9a*d7Ta)7b4wHHWX75Dap^zNlGlzM^Wn|Y6i**Euz*S3aD(dte6mlX#%2r zfMzM^@F1IYP}Bk@ldA=h;o#r^=B~~B;8rtBcPRsJv=i1t!@0v0#W2nErT z+cm*v@6~kSxV|vR3*?c>oLWX8sX85*$&p$jK=+HVLGtY6RoXI)PLmt8g(qj|h=A05 z(3Y9}5~wCml!u3rNrVBGyCHRefgB8 zE^)UTnjGBgb~$lxs^iYpki5KP@{&D2UgzIB^y=ERwHGSl-S%7fPJTXTp51fHlMBV; zugU4@Dn9O*v@Fc>+EyVIvr>V=*|%=IlKs21HuGY^T6l%20Ipker z`LOp#SunRmX21c#e1Qk`j`sx0ntx4rGtD&aT%OCZM-xrmo@`vH+wwvB>8Gr%R&jG> zf}c&({#vzH=I&kJt(r5H&DXE2_~sov<fJpY^m}|Wtz~vyyRziuF`?m{Az(j&Y?CaM0yS^Jg zKJ|5Jxpqna)OyEztTnvPop?Xl79COI-BngsS7cBnuT(zq)z6zJGj;X>{(H z&F_*(%+mdfGkR0RZ$BxNSjXmf$XjoQO2aj-mjRX2mn&u*^5;I$@aNpc_~|nO;;dAQ z85f>7c*yU7MQZsJWyQ$}v&25#@&73HslT3S;_oR7)xB5_EbE`RVj@g3qUe!IYuIHY%PWFw4cLd)N-LOODD{{41 zzH+hDEx58-#cN>?pQAbhyXO^)<6mo&8Db=~-pqcn)%M_7*4Wxq4b6MTUWP0#*_>hr zN}f!SKe#>HZCOA&SFEPnqVGLUe4Tdxr@Zj*{0Aofdu*-}5@uw4Jh@M}o3U}Slt^TK zdSpBg``xdH*7=z&Gr!0lySJ*?((GSvecJhgwd?mTZ-04pd;7}^Ti3|1j^2EU?egk{ zZyz40oL7}E;k@{7jjcfy|3hcnb8WXLemfxX>g(;o?dJ1dEk3zp_q)5_Uu~ae_bPVR z+jl9K_DTAReLnf)&8@06{<_<5o~f_t?2~^Qe*fqcrOo>!vi|Z0c(Zeq-M**H#Bv1mrNOicPi`lR;GkvMqWt zw~i)E(PXj7aiVC7KsKBdn;fmB#s=~F Date: Mon, 20 Feb 2017 21:02:49 +0100 Subject: [PATCH 10/12] Removed PhpStorm comment on top of file --- tests/PhpSpreadsheetTests/Reader/OdsTest.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/PhpSpreadsheetTests/Reader/OdsTest.php b/tests/PhpSpreadsheetTests/Reader/OdsTest.php index e71f52fd..32737e20 100644 --- a/tests/PhpSpreadsheetTests/Reader/OdsTest.php +++ b/tests/PhpSpreadsheetTests/Reader/OdsTest.php @@ -1,10 +1,4 @@ Date: Mon, 20 Feb 2017 21:05:25 +0100 Subject: [PATCH 11/12] php-cs run: fixed code style for new/changed files --- src/PhpSpreadsheet/Reader/Ods.php | 130 +++++++++---------- tests/PhpSpreadsheetTests/Reader/OdsTest.php | 125 +++++++++--------- 2 files changed, 121 insertions(+), 134 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index e5c9c600..33254ff3 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -107,6 +107,7 @@ class Ods extends BaseReader implements IReader * @param string $pFilename * * @throws Exception + * * @return string[] */ public function listWorksheetNames($pFilename) @@ -163,6 +164,7 @@ class Ods extends BaseReader implements IReader * @param string $pFilename * * @throws Exception + * * @return array */ public function listWorksheetInfo($pFilename) @@ -173,7 +175,7 @@ class Ods extends BaseReader implements IReader $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); - /** @var \ZipArchive $zip */ + /** @var \ZipArchive $zip */ $zip = new $zipClass(); if (!$zip->open($pFilename)) { throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.'); @@ -407,25 +409,23 @@ class Ods extends BaseReader implements IReader \PhpOffice\PhpSpreadsheet\Settings::getLibXmlLoaderOptions() ); - $officeNs = $dom->lookupNamespaceUri("office"); - $tableNs = $dom->lookupNamespaceUri("table"); - $textNs = $dom->lookupNamespaceUri("text"); - $xlinkNs = $dom->lookupNamespaceUri("xlink"); + $officeNs = $dom->lookupNamespaceUri('office'); + $tableNs = $dom->lookupNamespaceUri('table'); + $textNs = $dom->lookupNamespaceUri('text'); + $xlinkNs = $dom->lookupNamespaceUri('xlink'); - $spreadsheets = $dom->getElementsByTagNameNS($officeNs, "body") + $spreadsheets = $dom->getElementsByTagNameNS($officeNs, 'body') ->item(0) - ->getElementsByTagNameNS($officeNs, "spreadsheet"); + ->getElementsByTagNameNS($officeNs, 'spreadsheet'); foreach ($spreadsheets as $workbookData) { /** @var \DOMElement $workbookData */ - - $tables = $workbookData->getElementsByTagNameNS($tableNs, "table"); + $tables = $workbookData->getElementsByTagNameNS($tableNs, 'table'); $worksheetID = 0; foreach ($tables as $worksheetDataSet) { /** @var \DOMElement $worksheetDataSet */ - - $worksheetName = $worksheetDataSet->getAttributeNS($tableNs, "name"); + $worksheetName = $worksheetDataSet->getAttributeNS($tableNs, 'name'); // Check loadSheetsOnly if (isset($this->loadSheetsOnly) @@ -435,7 +435,7 @@ class Ods extends BaseReader implements IReader } // Create sheet - if($worksheetID > 0){ + if ($worksheetID > 0) { $spreadsheet->createSheet(); // First sheet is added by default } $spreadsheet->setActiveSheetIndex($worksheetID); @@ -453,15 +453,15 @@ class Ods extends BaseReader implements IReader /** @var \DOMElement $childNode */ // Filter elements which are not under the "table" ns - if($childNode->namespaceURI != $tableNs){ + if ($childNode->namespaceURI != $tableNs) { continue; } $key = $childNode->nodeName; // Remove ns from node name - if(strpos($key, ":") !== false){ - $keyChunks = explode(":", $key); + if (strpos($key, ':') !== false) { + $keyChunks = explode(':', $key); $key = array_pop($keyChunks); } @@ -470,7 +470,7 @@ class Ods extends BaseReader implements IReader /// TODO :: Figure this out. This is only a partial implementation I guess. // ($rowData it's not used at all and I'm not sure that PHPExcel // has an API for this) - + // foreach ($rowData as $keyRowData => $cellData) { // $rowData = $cellData; // break; @@ -478,16 +478,15 @@ class Ods extends BaseReader implements IReader break; case 'table-row': - if($childNode->hasAttributeNS($tableNs, 'number-rows-repeated')){ + if ($childNode->hasAttributeNS($tableNs, 'number-rows-repeated')) { $rowRepeats = $childNode->getAttributeNS($tableNs, 'number-rows-repeated'); - } - else{ + } else { $rowRepeats = 1; } - + $columnID = 'A'; foreach ($childNode->childNodes as $key => $cellData) { - /** @var \DOMElement $cellData */ + /* @var \DOMElement $cellData */ if ($this->getReadFilter() !== null) { if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) { @@ -501,19 +500,18 @@ class Ods extends BaseReader implements IReader $hasCalculatedValue = false; $cellDataFormula = ''; - if ($cellData->hasAttributeNS($tableNs, "formula")) { - $cellDataFormula = $cellData->getAttributeNS($tableNs, "formula"); + if ($cellData->hasAttributeNS($tableNs, 'formula')) { + $cellDataFormula = $cellData->getAttributeNS($tableNs, 'formula'); $hasCalculatedValue = true; } // Annotations - $annotation = $cellData->getElementsByTagNameNS($officeNs, "annotation"); + $annotation = $cellData->getElementsByTagNameNS($officeNs, 'annotation'); if ($annotation->length > 0) { - $textNode = $annotation->item(0)->getElementsByTagNameNS($textNs, "p"); - - if($textNode->length > 0){ + $textNode = $annotation->item(0)->getElementsByTagNameNS($textNs, 'p'); + if ($textNode->length > 0) { $text = $this->scanElementForText($textNode->item(0)); $spreadsheet->getActiveSheet() @@ -532,13 +530,12 @@ class Ods extends BaseReader implements IReader /** @var \DOMElement $item */ // Filter text:p elements - if($item->nodeName == "text:p"){ + if ($item->nodeName == 'text:p') { $paragraphs[] = $item; } } if (count($paragraphs) > 0) { - // Consolidate if there are multiple p records (maybe with spans as well) $dataArray = []; @@ -560,9 +557,9 @@ class Ods extends BaseReader implements IReader $dataValue = $allCellDataText; foreach ($paragraphs as $paragraph) { - $link = $paragraph->getElementsByTagNameNS($textNs, "a"); - if($link->length > 0){ - $hyperlink = $link->item(0)->getAttributeNS($xlinkNs, "href"); + $link = $paragraph->getElementsByTagNameNS($textNs, 'a'); + if ($link->length > 0) { + $hyperlink = $link->item(0)->getAttributeNS($xlinkNs, 'href'); } } @@ -573,7 +570,7 @@ class Ods extends BaseReader implements IReader break; case 'percentage': $type = DataType::TYPE_NUMERIC; - $dataValue = (float)$cellData->getAttributeNS($officeNs, 'value'); + $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value'); if (floor($dataValue) == $dataValue) { $dataValue = (int) $dataValue; @@ -582,7 +579,7 @@ class Ods extends BaseReader implements IReader break; case 'currency': $type = DataType::TYPE_NUMERIC; - $dataValue = (float)$cellData->getAttributeNS($officeNs, 'value'); + $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value'); if (floor($dataValue) == $dataValue) { $dataValue = (int) $dataValue; @@ -591,7 +588,7 @@ class Ods extends BaseReader implements IReader break; case 'float': $type = DataType::TYPE_NUMERIC; - $dataValue = (float)$cellData->getAttributeNS($officeNs, 'value'); + $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value'); if (floor($dataValue) == $dataValue) { if ($dataValue == (int) $dataValue) { @@ -635,7 +632,6 @@ class Ods extends BaseReader implements IReader ); $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_TIME4; break; - default: $dataValue = null; } @@ -650,41 +646,37 @@ class Ods extends BaseReader implements IReader $temp = explode('"', $cellDataFormula); $tKey = false; foreach ($temp as &$value) { - // Only replace in alternate array entries (i.e. non-quoted blocks) if ($tKey = !$tKey) { - // Cell range reference in another sheet $value = preg_replace('/\[([^\.]+)\.([^\.]+):\.([^\.]+)\]/Ui', '$1!$2:$3', $value); - + // 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); - + // Then rebuild the formula string $cellDataFormula = implode('"', $temp); } - if($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')){ - $colRepeats = (int)$cellData->getAttributeNS($tableNs, 'number-columns-repeated'); - } - else{ + if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) { + $colRepeats = (int) $cellData->getAttributeNS($tableNs, 'number-columns-repeated'); + } else { $colRepeats = 1; } if ($type !== null) { for ($i = 0; $i < $colRepeats; ++$i) { - if ($i > 0) { ++$columnID; } @@ -697,10 +689,9 @@ class Ods extends BaseReader implements IReader ->getCell($columnID . $rID); // Set value - if($hasCalculatedValue){ + if ($hasCalculatedValue) { $cell->setValueExplicit($cellDataFormula, $type); - } - else{ + } else { $cell->setValueExplicit($dataValue, $type); } @@ -738,9 +729,8 @@ class Ods extends BaseReader implements IReader $columnTo = $columnID; if ($cellData->hasAttributeNS($tableNs, 'number-columns-spanned')) { - $columnIndex = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($columnID); - $columnIndex += (int)$cellData->getAttributeNS($tableNs, 'number-columns-spanned'); + $columnIndex += (int) $cellData->getAttributeNS($tableNs, 'number-columns-spanned'); $columnIndex -= 2; $columnTo = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($columnIndex); @@ -749,7 +739,7 @@ class Ods extends BaseReader implements IReader $rowTo = $rowID; if ($cellData->hasAttributeNS($tableNs, 'number-rows-spanned')) { - $rowTo = $rowTo + (int)$cellData->getAttributeNS($tableNs, 'number-rows-spanned') - 1; + $rowTo = $rowTo + (int) $cellData->getAttributeNS($tableNs, 'number-rows-spanned') - 1; } $cellRange = $columnID . $rowID . ':' . $columnTo . $rowTo; @@ -772,38 +762,35 @@ class Ods extends BaseReader implements IReader } /** - * Recursively scan element + * Recursively scan element. * * @param \DOMNode $element + * * @return string */ - protected function scanElementForText(\DOMNode $element){ - - $str = ""; - foreach($element->childNodes as $child){ + protected function scanElementForText(\DOMNode $element) + { + $str = ''; + foreach ($element->childNodes as $child) { /** @var \DOMNode $child */ - - if($child->nodeType == XML_TEXT_NODE){ + if ($child->nodeType == XML_TEXT_NODE) { $str .= $child->nodeValue; - } - elseif($child->nodeType == XML_ELEMENT_NODE && $child->nodeName == "text:s"){ + } elseif ($child->nodeType == XML_ELEMENT_NODE && $child->nodeName == 'text:s') { // It's a space // Multiple spaces? - if(isset($child->attributes["text:c"])){ - + if (isset($child->attributes['text:c'])) { /** @var \DOMAttr $cAttr */ - $cAttr = $child->attributes["text:c"]; - $multiplier = (int)$cAttr->nodeValue; - } - else{ + $cAttr = $child->attributes['text:c']; + $multiplier = (int) $cAttr->nodeValue; + } else { $multiplier = 1; } - $str .= str_repeat(" ", $multiplier); + $str .= str_repeat(' ', $multiplier); } - if($child->hasChildNodes()){ + if ($child->hasChildNodes()) { $str .= $this->scanElementForText($child); } } @@ -813,6 +800,7 @@ class Ods extends BaseReader implements IReader /** * @param string $is + * * @return \PhpOffice\PhpSpreadsheet\RichText */ private function parseRichText($is = '') diff --git a/tests/PhpSpreadsheetTests/Reader/OdsTest.php b/tests/PhpSpreadsheetTests/Reader/OdsTest.php index 32737e20..4b958f9c 100644 --- a/tests/PhpSpreadsheetTests/Reader/OdsTest.php +++ b/tests/PhpSpreadsheetTests/Reader/OdsTest.php @@ -24,9 +24,9 @@ class OdsTest extends \PHPUnit_Framework_TestCase /** * @return \PhpOffice\PhpSpreadsheet\Spreadsheet */ - protected function loadOOCalcTestFile(){ - - if(!$this->spreadsheetOOCalcTest){ + protected function loadOOCalcTestFile() + { + if (!$this->spreadsheetOOCalcTest) { $filename = __DIR__ . '/../../../samples/templates/OOCalcTest.ods'; // Load into this instance @@ -40,9 +40,9 @@ class OdsTest extends \PHPUnit_Framework_TestCase /** * @return \PhpOffice\PhpSpreadsheet\Spreadsheet */ - protected function loadDataFile(){ - - if(!$this->spreadsheetData){ + protected function loadDataFile() + { + if (!$this->spreadsheetData) { $filename = __DIR__ . '/../../data/Reader/Ods/data.ods'; // Load into this instance @@ -63,8 +63,8 @@ class OdsTest extends \PHPUnit_Framework_TestCase // Test "listWorksheetNames" method $this->assertEquals([ - "Sheet1", - "Second Sheet", + 'Sheet1', + 'Second Sheet', ], $reader->listWorksheetNames($filename)); } @@ -83,8 +83,8 @@ class OdsTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('PhpOffice\PhpSpreadsheet\Worksheet', $secondSheet); } - public function testReadValueAndComments(){ - + public function testReadValueAndComments() + { $spreadsheet = $this->loadOOCalcTestFile(); $firstSheet = $spreadsheet->getSheet(0); @@ -93,41 +93,41 @@ class OdsTest extends \PHPUnit_Framework_TestCase $this->assertEquals('N', $firstSheet->getHighestColumn()); // Simple cell value - $this->assertEquals("Test String 1", $firstSheet->getCell("A1")->getValue()); + $this->assertEquals('Test String 1', $firstSheet->getCell('A1')->getValue()); // Merged cell - $this->assertEquals("BOX", $firstSheet->getCell("B18")->getValue()); + $this->assertEquals('BOX', $firstSheet->getCell('B18')->getValue()); // Comments/Annotations $this->assertEquals( - "Test for a simple colour-formatted string", - $firstSheet->getComment("A1")->getText()->getPlainText() + '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_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('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('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('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('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_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_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 + $this->assertEquals(DataType::TYPE_FORMULA, $firstSheet->getCell('C6')->getDataType()); // Formula + $this->assertEquals('=TRUE()', $firstSheet->getCell('C6')->getValue()); // Formula /* * Percentage, Currency @@ -137,17 +137,17 @@ class OdsTest extends \PHPUnit_Framework_TestCase $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('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('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('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()); + $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A5')->getDataType()); // Currency ($20) + $this->assertEquals(20, $firstSheet->getCell('A5')->getValue()); } public function testReadColors() @@ -157,47 +157,46 @@ class OdsTest extends \PHPUnit_Framework_TestCase // Background color - $style = $firstSheet->getCell("K3")->getStyle(); + $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()); + $this->assertEquals('none', $style->getFill()->getFillType()); + $this->assertEquals('FFFFFFFF', $style->getFill()->getStartColor()->getARGB()); + $this->assertEquals('FF000000', $style->getFill()->getEndColor()->getARGB()); } - public function testReadRichText(){ - + 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() + 'And this row should be autofit height with text wrap', + $firstSheet->getCell('A28')->getValue() ); } - public function testReadCellsWithRepeatedSpaces(){ - + 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()); - + $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(){ - + public function testReadHyperlinks() + { $spreadsheet = $this->loadOOCalcTestFile(); $firstSheet = $spreadsheet->getSheet(0); - $hyperlink = $firstSheet->getCell("A29"); + $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()); + $this->assertEquals('PHPExcel', $hyperlink->getValue()); + $this->assertEquals('http://www.phpexcel.net/', $hyperlink->getHyperlink()->getUrl()); } /* @@ -206,22 +205,22 @@ class OdsTest extends \PHPUnit_Framework_TestCase public function testReadBoldItalicUnderline() { - $this->markTestSkipped("Features not implemented yet"); + $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()); + $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(); + $style = $firstSheet->getCell('E3')->getStyle(); $this->assertEquals(Font::UNDERLINE_SINGLE, $style->getFont()->getUnderline()); - $style = $firstSheet->getCell("E1")->getStyle(); + $style = $firstSheet->getCell('E1')->getStyle(); $this->assertTrue($style->getFont()->getBold()); $this->assertTrue($style->getFont()->getItalic()); } From 529617c77dca59c1e6c9c9f0555d4ad5b9859df7 Mon Sep 17 00:00:00 2001 From: Paolo Agostinetto Date: Mon, 20 Feb 2017 22:14:07 +0100 Subject: [PATCH 12/12] Fixed CS errors --- src/PhpSpreadsheet/Reader/Ods.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index 33254ff3..cf514883 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -477,7 +477,6 @@ class Ods extends BaseReader implements IReader // } break; case 'table-row': - if ($childNode->hasAttributeNS($tableNs, 'number-rows-repeated')) { $rowRepeats = $childNode->getAttributeNS($tableNs, 'number-rows-repeated'); } else { @@ -604,12 +603,18 @@ class Ods extends BaseReader implements IReader $dateObj = new DateTime($value, $GMT); $dateObj->setTimeZone($timezoneObj); - list($year, $month, $day, $hour, $minute, $second) = explode(' ', + list($year, $month, $day, $hour, $minute, $second) = explode( + ' ', $dateObj->format('Y m d H i s') ); $dataValue = \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel( - $year, $month, $day, $hour, $minute, $second + $year, + $month, + $day, + $hour, + $minute, + $second ); if ($dataValue != floor($dataValue)) {