From 2b4c15b92f62a47b2b97b8e5b2ec206f7ac79413 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Fri, 21 Jan 2011 13:11:28 +0000 Subject: [PATCH] Feature: Added support for cell comments in the Gnumeric and Excel2003XML Readers git-svn-id: https://phpexcel.svn.codeplex.com/svn/trunk@67186 2327b42d-5241-43d6-9e2a-de5ac946f064 --- Classes/PHPExcel/Comment.php | 35 +- Classes/PHPExcel/Reader/Excel2003XML.php | 363 ++++++++++-------- Classes/PHPExcel/Reader/Gnumeric.php | 17 + Classes/PHPExcel/Style.php | 12 +- .../Functionality Cross-Reference.xls | Bin 35840 -> 36352 bytes changelog.txt | 3 + 6 files changed, 255 insertions(+), 175 deletions(-) diff --git a/Classes/PHPExcel/Comment.php b/Classes/PHPExcel/Comment.php index 3a2f947c..34492da6 100644 --- a/Classes/PHPExcel/Comment.php +++ b/Classes/PHPExcel/Comment.php @@ -91,6 +91,13 @@ class PHPExcel_Comment implements PHPExcel_IComparable */ private $_fillColor; + /** + * Alignment + * + * @var string + */ + private $_alignment; + /** * Create a new PHPExcel_Comment * @@ -99,9 +106,10 @@ class PHPExcel_Comment implements PHPExcel_IComparable public function __construct() { // Initialise variables - $this->_author = 'Author'; - $this->_text = new PHPExcel_RichText(); - $this->_fillColor = new PHPExcel_Style_Color('FFFFFFE1'); + $this->_author = 'Author'; + $this->_text = new PHPExcel_RichText(); + $this->_fillColor = new PHPExcel_Style_Color('FFFFFFE1'); + $this->_alignment = PHPExcel_Style_Alignment::HORIZONTAL_GENERAL; } /** @@ -253,6 +261,26 @@ class PHPExcel_Comment implements PHPExcel_IComparable return $this->_fillColor; } + /** + * Set Alignment + * + * @param string $pValue + * @return PHPExcel_Comment + */ + public function setAlignment($pValue = PHPExcel_Style_Alignment::HORIZONTAL_GENERAL) { + $this->_alignment = $pValue; + return $this; + } + + /** + * Get Alignment + * + * @return string + */ + public function getAlignment() { + return $this->_alignment; + } + /** * Get hash code * @@ -268,6 +296,7 @@ class PHPExcel_Comment implements PHPExcel_IComparable . $this->_marginTop . ($this->_visible ? 1 : 0) . $this->_fillColor->getHashCode() + . $this->_alignment . __CLASS__ ); } diff --git a/Classes/PHPExcel/Reader/Excel2003XML.php b/Classes/PHPExcel/Reader/Excel2003XML.php index e86e165d..27d31216 100644 --- a/Classes/PHPExcel/Reader/Excel2003XML.php +++ b/Classes/PHPExcel/Reader/Excel2003XML.php @@ -554,180 +554,203 @@ class PHPExcel_Reader_Excel2003XML implements PHPExcel_Reader_IReader } $columnID = 'A'; - foreach($worksheet->Table->Column as $columnData) { - $columnData_ss = $columnData->attributes($namespaces['ss']); - if (isset($columnData_ss['Index'])) { - $columnID = PHPExcel_Cell::stringFromColumnIndex($columnData_ss['Index']-1); - } - if (isset($columnData_ss['Width'])) { - $columnWidth = $columnData_ss['Width']; -// echo 'Setting column width for '.$columnID.' to '.$columnWidth.'
'; - $objPHPExcel->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4); - } - ++$columnID; - } - - $rowID = 1; - foreach($worksheet->Table->Row as $rowData) { - $rowHasData = false; - $row_ss = $rowData->attributes($namespaces['ss']); - if (isset($row_ss['Index'])) { - $rowID = (integer) $row_ss['Index']; - } -// echo 'Row '.$rowID.'
'; - - $columnID = 'A'; - foreach($rowData->Cell as $cell) { - - $cell_ss = $cell->attributes($namespaces['ss']); - if (isset($cell_ss['Index'])) { - $columnID = PHPExcel_Cell::stringFromColumnIndex($cell_ss['Index']-1); + if (isset($worksheet->Table->Column)) { + foreach($worksheet->Table->Column as $columnData) { + $columnData_ss = $columnData->attributes($namespaces['ss']); + if (isset($columnData_ss['Index'])) { + $columnID = PHPExcel_Cell::stringFromColumnIndex($columnData_ss['Index']-1); } - $cellRange = $columnID.$rowID; - - if (!is_null($this->getReadFilter())) { - if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) { - continue; - } - } - - if ((isset($cell_ss['MergeAcross'])) || (isset($cell_ss['MergeDown']))) { - $columnTo = $columnID; - if (isset($cell_ss['MergeAcross'])) { - $columnTo = PHPExcel_Cell::stringFromColumnIndex(PHPExcel_Cell::columnIndexFromString($columnID) + $cell_ss['MergeAcross'] -1); - } - $rowTo = $rowID; - if (isset($cell_ss['MergeDown'])) { - $rowTo = $rowTo + $cell_ss['MergeDown']; - } - $cellRange .= ':'.$columnTo.$rowTo; - $objPHPExcel->getActiveSheet()->mergeCells($cellRange); - } - - $cellIsSet = $hasCalculatedValue = false; - $cellDataFormula = ''; - if (isset($cell_ss['Formula'])) { - $cellDataFormula = $cell_ss['Formula']; - // added this as a check for array formulas - if (isset($cell_ss['ArrayRange'])) { - $cellDataCSEFormula = $cell_ss['ArrayRange']; -// echo "found an array formula at ".$columnID.$rowID."
"; - } - $hasCalculatedValue = true; - } - if (isset($cell->Data)) { - $cellValue = $cellData = $cell->Data; - $type = PHPExcel_Cell_DataType::TYPE_NULL; - $cellData_ss = $cellData->attributes($namespaces['ss']); - if (isset($cellData_ss['Type'])) { - $cellDataType = $cellData_ss['Type']; - switch ($cellDataType) { - /* - const TYPE_STRING = 's'; - const TYPE_FORMULA = 'f'; - const TYPE_NUMERIC = 'n'; - const TYPE_BOOL = 'b'; - const TYPE_NULL = 's'; - const TYPE_INLINE = 'inlineStr'; - const TYPE_ERROR = 'e'; - */ - case 'String' : - $type = PHPExcel_Cell_DataType::TYPE_STRING; - break; - case 'Number' : - $type = PHPExcel_Cell_DataType::TYPE_NUMERIC; - $cellValue = (float) $cellValue; - if (floor($cellValue) == $cellValue) { - $cellValue = (integer) $cellValue; - } - break; - case 'Boolean' : - $type = PHPExcel_Cell_DataType::TYPE_BOOL; - $cellValue = ($cellValue != 0); - break; - case 'DateTime' : - $type = PHPExcel_Cell_DataType::TYPE_NUMERIC; - $cellValue = PHPExcel_Shared_Date::PHPToExcel(strtotime($cellValue)); - break; - case 'Error' : - $type = PHPExcel_Cell_DataType::TYPE_ERROR; - break; - } - } - if ($hasCalculatedValue) { - $type = PHPExcel_Cell_DataType::TYPE_FORMULA; - $columnNumber = PHPExcel_Cell::columnIndexFromString($columnID); - // Convert R1C1 style references to A1 style references (but only when not quoted) - $temp = explode('"',$cellDataFormula); - foreach($temp as $key => &$value) { - // Only replace in alternate array entries (i.e. non-quoted blocks) - if (($key % 2) == 0) { - preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/',$value, $cellReferences,PREG_SET_ORDER+PREG_OFFSET_CAPTURE); - // Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way - // through the formula from left to right. Reversing means that we work right to left.through - // the formula - $cellReferences = array_reverse($cellReferences); - // Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent, - // then modify the formula to use that new reference - foreach($cellReferences as $cellReference) { - $rowReference = $cellReference[2][0]; - // Empty R reference is the current row - if ($rowReference == '') $rowReference = $rowID; - // Bracketed R references are relative to the current row - if ($rowReference{0} == '[') $rowReference = $rowID + trim($rowReference,'[]'); - $columnReference = $cellReference[4][0]; - // Empty C reference is the current column - if ($columnReference == '') $columnReference = $columnNumber; - // Bracketed C references are relative to the current column - if ($columnReference{0} == '[') $columnReference = $columnNumber + trim($columnReference,'[]'); - $A1CellReference = PHPExcel_Cell::stringFromColumnIndex($columnReference-1).$rowReference; - $value = substr_replace($value,$A1CellReference,$cellReference[0][1],strlen($cellReference[0][0])); - } - } - } - unset($value); - // Then rebuild the formula string - $cellDataFormula = implode('"',$temp); - } - -// echo 'Cell '.$columnID.$rowID.' is a '.$type.' with a value of '.(($hasCalculatedValue) ? $cellDataFormula : $cellValue).'
'; -// - $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $cellValue),$type); - if ($hasCalculatedValue) { -// echo 'Formula result is '.$cellValue.'
'; - $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setCalculatedValue($cellValue); - } - $cellIsSet = $rowHasData = true; - } - - if (($cellIsSet) && (isset($cell_ss['StyleID']))) { - $style = (string) $cell_ss['StyleID']; -// echo 'Cell style for '.$columnID.$rowID.' is '.$style.'
'; - if ((isset($this->_styles[$style])) && (count($this->_styles[$style]) > 0)) { -// echo 'Cell '.$columnID.$rowID.'
'; -// print_r($this->_styles[$style]); -// echo '
'; - if (!$objPHPExcel->getActiveSheet()->cellExists($columnID.$rowID)) { - $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setValue(NULL); - } - $objPHPExcel->getActiveSheet()->getStyle($cellRange)->applyFromArray($this->_styles[$style]); - } + if (isset($columnData_ss['Width'])) { + $columnWidth = $columnData_ss['Width']; +// echo 'Setting column width for '.$columnID.' to '.$columnWidth.'
'; + $objPHPExcel->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4); } ++$columnID; } + } - if ($rowHasData) { - if (isset($row_ss['StyleID'])) { - $rowStyle = $row_ss['StyleID']; + $rowID = 1; + if (isset($worksheet->Table->Row)) { + foreach($worksheet->Table->Row as $rowData) { + $rowHasData = false; + $row_ss = $rowData->attributes($namespaces['ss']); + if (isset($row_ss['Index'])) { + $rowID = (integer) $row_ss['Index']; } - if (isset($row_ss['Height'])) { - $rowHeight = $row_ss['Height']; -// echo 'Setting row height to '.$rowHeight.'
'; - $objPHPExcel->getActiveSheet()->getRowDimension($rowID)->setRowHeight($rowHeight); +// echo 'Row '.$rowID.'
'; + + $columnID = 'A'; + foreach($rowData->Cell as $cell) { + + $cell_ss = $cell->attributes($namespaces['ss']); + if (isset($cell_ss['Index'])) { + $columnID = PHPExcel_Cell::stringFromColumnIndex($cell_ss['Index']-1); + } + $cellRange = $columnID.$rowID; + + if (!is_null($this->getReadFilter())) { + if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) { + continue; + } + } + + if ((isset($cell_ss['MergeAcross'])) || (isset($cell_ss['MergeDown']))) { + $columnTo = $columnID; + if (isset($cell_ss['MergeAcross'])) { + $columnTo = PHPExcel_Cell::stringFromColumnIndex(PHPExcel_Cell::columnIndexFromString($columnID) + $cell_ss['MergeAcross'] -1); + } + $rowTo = $rowID; + if (isset($cell_ss['MergeDown'])) { + $rowTo = $rowTo + $cell_ss['MergeDown']; + } + $cellRange .= ':'.$columnTo.$rowTo; + $objPHPExcel->getActiveSheet()->mergeCells($cellRange); + } + + $cellIsSet = $hasCalculatedValue = false; + $cellDataFormula = ''; + if (isset($cell_ss['Formula'])) { + $cellDataFormula = $cell_ss['Formula']; + // added this as a check for array formulas + if (isset($cell_ss['ArrayRange'])) { + $cellDataCSEFormula = $cell_ss['ArrayRange']; +// echo "found an array formula at ".$columnID.$rowID."
"; + } + $hasCalculatedValue = true; + } + if (isset($cell->Data)) { + $cellValue = $cellData = $cell->Data; + $type = PHPExcel_Cell_DataType::TYPE_NULL; + $cellData_ss = $cellData->attributes($namespaces['ss']); + if (isset($cellData_ss['Type'])) { + $cellDataType = $cellData_ss['Type']; + switch ($cellDataType) { + /* + const TYPE_STRING = 's'; + const TYPE_FORMULA = 'f'; + const TYPE_NUMERIC = 'n'; + const TYPE_BOOL = 'b'; + const TYPE_NULL = 's'; + const TYPE_INLINE = 'inlineStr'; + const TYPE_ERROR = 'e'; + */ + case 'String' : + $type = PHPExcel_Cell_DataType::TYPE_STRING; + break; + case 'Number' : + $type = PHPExcel_Cell_DataType::TYPE_NUMERIC; + $cellValue = (float) $cellValue; + if (floor($cellValue) == $cellValue) { + $cellValue = (integer) $cellValue; + } + break; + case 'Boolean' : + $type = PHPExcel_Cell_DataType::TYPE_BOOL; + $cellValue = ($cellValue != 0); + break; + case 'DateTime' : + $type = PHPExcel_Cell_DataType::TYPE_NUMERIC; + $cellValue = PHPExcel_Shared_Date::PHPToExcel(strtotime($cellValue)); + break; + case 'Error' : + $type = PHPExcel_Cell_DataType::TYPE_ERROR; + break; + } + } + + if ($hasCalculatedValue) { + $type = PHPExcel_Cell_DataType::TYPE_FORMULA; + $columnNumber = PHPExcel_Cell::columnIndexFromString($columnID); + // Convert R1C1 style references to A1 style references (but only when not quoted) + $temp = explode('"',$cellDataFormula); + foreach($temp as $key => &$value) { + // Only replace in alternate array entries (i.e. non-quoted blocks) + if (($key % 2) == 0) { + preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/',$value, $cellReferences,PREG_SET_ORDER+PREG_OFFSET_CAPTURE); + // Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way + // through the formula from left to right. Reversing means that we work right to left.through + // the formula + $cellReferences = array_reverse($cellReferences); + // Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent, + // then modify the formula to use that new reference + foreach($cellReferences as $cellReference) { + $rowReference = $cellReference[2][0]; + // Empty R reference is the current row + if ($rowReference == '') $rowReference = $rowID; + // Bracketed R references are relative to the current row + if ($rowReference{0} == '[') $rowReference = $rowID + trim($rowReference,'[]'); + $columnReference = $cellReference[4][0]; + // Empty C reference is the current column + if ($columnReference == '') $columnReference = $columnNumber; + // Bracketed C references are relative to the current column + if ($columnReference{0} == '[') $columnReference = $columnNumber + trim($columnReference,'[]'); + $A1CellReference = PHPExcel_Cell::stringFromColumnIndex($columnReference-1).$rowReference; + $value = substr_replace($value,$A1CellReference,$cellReference[0][1],strlen($cellReference[0][0])); + } + } + } + unset($value); + // Then rebuild the formula string + $cellDataFormula = implode('"',$temp); + } + +// echo 'Cell '.$columnID.$rowID.' is a '.$type.' with a value of '.(($hasCalculatedValue) ? $cellDataFormula : $cellValue).'
'; +// + $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $cellValue),$type); + if ($hasCalculatedValue) { +// echo 'Formula result is '.$cellValue.'
'; + $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setCalculatedValue($cellValue); + } + $cellIsSet = $rowHasData = true; + } + + if (isset($cell->Comment)) { +// echo 'comment found
'; + $commentAttributes = $cell->Comment->attributes($namespaces['ss']); + $author = 'unknown'; + if (isset($commentAttributes->Author)) { + $author = (string)$commentAttributes->Author; +// echo 'Author: ',$author,'
'; + } + $node = $cell->Comment->Data->asXML(); +// $annotation = str_replace('html:','',substr($node,49,-10)); +// echo $annotation,'
'; + $annotation = strip_tags($node); +// echo 'Annotation: ',$annotation,'
'; + $objPHPExcel->getActiveSheet()->getComment( $columnID.$rowID ) + ->setAuthor( $author ) + ->setText($this->_parseRichText($annotation) ); + } + + if (($cellIsSet) && (isset($cell_ss['StyleID']))) { + $style = (string) $cell_ss['StyleID']; +// echo 'Cell style for '.$columnID.$rowID.' is '.$style.'
'; + if ((isset($this->_styles[$style])) && (count($this->_styles[$style]) > 0)) { +// echo 'Cell '.$columnID.$rowID.'
'; +// print_r($this->_styles[$style]); +// echo '
'; + if (!$objPHPExcel->getActiveSheet()->cellExists($columnID.$rowID)) { + $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setValue(NULL); + } + $objPHPExcel->getActiveSheet()->getStyle($cellRange)->applyFromArray($this->_styles[$style]); + } + } + ++$columnID; } + + if ($rowHasData) { + if (isset($row_ss['StyleID'])) { + $rowStyle = $row_ss['StyleID']; + } + if (isset($row_ss['Height'])) { + $rowHeight = $row_ss['Height']; +// echo 'Setting row height to '.$rowHeight.'
'; + $objPHPExcel->getActiveSheet()->getRowDimension($rowID)->setRowHeight($rowHeight); + } + } + + ++$rowID; } - - ++$rowID; } ++$worksheetID; } @@ -736,4 +759,12 @@ class PHPExcel_Reader_Excel2003XML implements PHPExcel_Reader_IReader return $objPHPExcel; } + private function _parseRichText($is = '') { + $value = new PHPExcel_RichText(); + + $value->createText($is); + + return $value; + } + } diff --git a/Classes/PHPExcel/Reader/Gnumeric.php b/Classes/PHPExcel/Reader/Gnumeric.php index be637bf2..275b770d 100644 --- a/Classes/PHPExcel/Reader/Gnumeric.php +++ b/Classes/PHPExcel/Reader/Gnumeric.php @@ -406,6 +406,7 @@ class PHPExcel_Reader_Gnumeric implements PHPExcel_Reader_IReader if (isset($sheet->PrintInformation->Margins)) { foreach($sheet->PrintInformation->Margins->children('gnm',TRUE) as $key => $margin) { $marginAttributes = $margin->attributes(); + $marginSize = 72 / 100; // Default switch($marginAttributes['PrefUnit']) { case 'mm' : $marginSize = intval($marginAttributes['Points']) / 100; @@ -506,6 +507,14 @@ class PHPExcel_Reader_Gnumeric implements PHPExcel_Reader_IReader $objPHPExcel->getActiveSheet()->getCell($column.$row)->setValueExplicit($cell,$type); } + if ((!$this->_readDataOnly) && (isset($sheet->Objects))) { + foreach($sheet->Objects->children('gnm',TRUE) as $key => $comment) { + $commentAttributes = $comment->attributes(); + $objPHPExcel->getActiveSheet()->getComment( (string)$commentAttributes->ObjectBound ) + ->setAuthor( (string)$commentAttributes->Author ) + ->setText($this->_parseRichText((string)$commentAttributes->Text) ); + } + } // echo '$maxCol=',$maxCol,'; $maxRow=',$maxRow,'
'; // foreach($sheet->Styles->StyleRegion as $styleRegion) { @@ -867,6 +876,14 @@ class PHPExcel_Reader_Gnumeric implements PHPExcel_Reader_IReader return $styleArray; } + private function _parseRichText($is = '') { + $value = new PHPExcel_RichText(); + + $value->createText($is); + + return $value; + } + private static function _parseGnumericColour($gnmColour) { list($gnmR,$gnmG,$gnmB) = explode(':',$gnmColour); $gnmR = substr(str_pad($gnmR,4,'0',STR_PAD_RIGHT),0,2); diff --git a/Classes/PHPExcel/Style.php b/Classes/PHPExcel/Style.php index 53604453..3b340363 100644 --- a/Classes/PHPExcel/Style.php +++ b/Classes/PHPExcel/Style.php @@ -632,13 +632,13 @@ class PHPExcel_Style implements PHPExcel_IComparable } return md5( - $this->getFill()->getHashCode() - . $this->getFont()->getHashCode() - . $this->getBorders()->getHashCode() - . $this->getAlignment()->getHashCode() - . $this->getNumberFormat()->getHashCode() + $this->_fill->getHashCode() + . $this->_font->getHashCode() + . $this->_borders->getHashCode() + . $this->_alignment->getHashCode() + . $this->_numberFormat->getHashCode() . $hashConditionals - . $this->getProtection()->getHashCode() + . $this->_protection->getHashCode() . __CLASS__ ); } diff --git a/Documentation/Functionality Cross-Reference.xls b/Documentation/Functionality Cross-Reference.xls index a41593881537a35e8a50db4673f3f2803408234d..3270fb5e04ab1ef3074f2e1cefda5b0674e65efc 100644 GIT binary patch delta 1315 zcma)*&ubGw6vw|ayV<5O*|Z@@sVS&LL?|{^1wpOVB(2%CCDnr`r6xuLdgwtxw2)qe z5<%LGh+q{wcx}QKC{5* zQY<^aXN#Hv@ML0%S#`FZFxf!#H}dXDEf!8qp>DLO6_b|n?+uYHtNWI zf8A%Q-QkO41EvNJ)1-T#ayBjYnP4(Q<7xqm5>^(Z-}E2G9MeFs>m7 zh78tdwmG`+G*YzN^c0^LpeT1wLly&0WQWh|#Hc4Spw#i|g6>Ekj<@fP)BnLaFeiBVjnRbJK@HEia6qBp##0IRLHY`^IQ?l!(59>a16 zrC}K2eji^W2pE{9Bmr?Hd8#pl8!{=H~T7jH8@U9!TZUtGLAa*B*~Pw=4a{G9EH$p3@fS)S` z2D1TVH5c6K%}3Zgctyezfr1Qd48a+xsU=3O415gFk|ryNs4!01>?$%(Q^*%6yVQY! z;cPeqgJc{7Lt(;Xt<*W2C)!P7Vf5Xs;>;|y*);YW6F&wfsvnqgGqqF5y(UrbHXA9idJ-WE?C4|v52`L#Ju1ZyCVeQVxCyUfI2WN_5q1a z4oUp!{}&i|o(!xE?hFP%F%S==J^;vv@sQ;}`uG{rp>jTOeb*VJ7zBVx*fod5Uq=Ar=b8>siLMFSU&048_j7;Z}H}6XO!Za!CD94ndZ4;|b zGcs?!n5EA@xuAz_GgH+u0giwF|Nl1uYM*?eO9ITX0dYQbNlfPH{xUIvWm3-%rm~dD Xzj~?|jVG7(*7H~c