From e1104f18627bbbc4e9ae24bc52dd2ebfbf468c98 Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Fri, 13 Jul 2012 18:20:06 +0200 Subject: [PATCH] ADDED : Implement support for AutoFilter in PHPExcel_Writer_Excel5 --- .../DgContainer/SpgrContainer/SpContainer.php | 27 +++++++ Classes/PHPExcel/Writer/Excel5.php | 62 +++++++++++++++- Classes/PHPExcel/Writer/Excel5/Escher.php | 33 +++++++-- Classes/PHPExcel/Writer/Excel5/Workbook.php | 49 +++++++++++++ Classes/PHPExcel/Writer/Excel5/Worksheet.php | 72 +++++++++++++++---- changelog.txt | 1 + 6 files changed, 225 insertions(+), 19 deletions(-) diff --git a/Classes/PHPExcel/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php b/Classes/PHPExcel/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php index e027eb4..0b362dc 100644 --- a/Classes/PHPExcel/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php +++ b/Classes/PHPExcel/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php @@ -55,6 +55,13 @@ class PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer */ private $_spType; + /** + * Shape flag + * + * @var int + */ + private $_spFlag; + /** * Shape index (usually group shape has index 0, and the rest: 1,2,3...) * @@ -171,6 +178,26 @@ class PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer return $this->_spType; } + /** + * Set the shape flag + * + * @param int $value + */ + public function setSpFlag($value) + { + $this->_spFlag = $value; + } + + /** + * Get the shape flag + * + * @return int + */ + public function getSpFlag() + { + return $this->_spFlag; + } + /** * Set the shape index * diff --git a/Classes/PHPExcel/Writer/Excel5.php b/Classes/PHPExcel/Writer/Excel5.php index 1bab643..599d0d4 100644 --- a/Classes/PHPExcel/Writer/Excel5.php +++ b/Classes/PHPExcel/Writer/Excel5.php @@ -282,7 +282,7 @@ class PHPExcel_Writer_Excel5 implements PHPExcel_Writer_IWriter $escher = null; // check if there are any shapes for this sheet - if (count($sheet->getDrawingCollection()) == 0) { + if (count($sheet->getDrawingCollection()) == 0 && $sheet->getAutoFilter() == '') { continue; } @@ -322,6 +322,8 @@ class PHPExcel_Writer_Excel5 implements PHPExcel_Writer_IWriter // set the shape type $spContainer->setSpType(0x004B); + // set the shape flag + $spContainer->setSpFlag(0x02); // set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index) $reducedSpId = $countShapes[$sheetIndex]; @@ -356,6 +358,64 @@ class PHPExcel_Writer_Excel5 implements PHPExcel_Writer_IWriter $spgrContainer->addChild($spContainer); } + + // AutoFilters + if($sheet->getAutoFilter() != ''){ + $rangeBounds = PHPExcel_Cell::rangeBoundaries($sheet->getAutoFilter()); + $iNumColStart = $rangeBounds[0][0]; + $iNumColEnd = $rangeBounds[1][0]; + + $iInc = $iNumColStart; + while($iInc <= $iNumColEnd){ + ++$countShapes[$sheetIndex]; + + // create an Drawing Object for the dropdown + $oDrawing = new PHPExcel_Worksheet_BaseDrawing(); + // get the coordinates of drawing + $cDrawing = PHPExcel_Cell::stringFromColumnIndex($iInc - 1) . $rangeBounds[0][1]; + $oDrawing->setCoordinates($cDrawing); + $oDrawing->setWorksheet($sheet); + + // add the shape + $spContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer(); + // set the shape type + $spContainer->setSpType(0x00C9); + // set the shape flag + $spContainer->setSpFlag(0x01); + + // set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index) + $reducedSpId = $countShapes[$sheetIndex]; + $spId = $reducedSpId + | ($sheet->getParent()->getIndex($sheet) + 1) << 10; + $spContainer->setSpId($spId); + + // keep track of last reducedSpId + $lastReducedSpId = $reducedSpId; + + // keep track of last spId + $lastSpId = $spId; + + $spContainer->setOPT(0x007F, 0x01040104); // Protection -> fLockAgainstGrouping + $spContainer->setOPT(0x00BF, 0x00080008); // Text -> fFitTextToShape + $spContainer->setOPT(0x01BF, 0x00010000); // Fill Style -> fNoFillHitTest + $spContainer->setOPT(0x01FF, 0x00080000); // Line Style -> fNoLineDrawDash + $spContainer->setOPT(0x03BF, 0x000A0000); // Group Shape -> fPrint + + // set coordinates and offsets, client anchor + $endCoordinates = PHPExcel_Cell::stringFromColumnIndex(PHPExcel_Cell::stringFromColumnIndex($iInc - 1)); + $endCoordinates .= $rangeBounds[0][1] + 1; + + $spContainer->setStartCoordinates($cDrawing); + $spContainer->setStartOffsetX(0); + $spContainer->setStartOffsetY(0); + $spContainer->setEndCoordinates($endCoordinates); + $spContainer->setEndOffsetX(0); + $spContainer->setEndOffsetY(0); + + $spgrContainer->addChild($spContainer); + $iInc++; + } + } // identifier clusters, used for workbook Escher object $this->_IDCLs[$dgId] = $lastReducedSpId; diff --git a/Classes/PHPExcel/Writer/Excel5/Escher.php b/Classes/PHPExcel/Writer/Excel5/Escher.php index 332d848..91fc27b 100644 --- a/Classes/PHPExcel/Writer/Excel5/Escher.php +++ b/Classes/PHPExcel/Writer/Excel5/Escher.php @@ -52,7 +52,13 @@ class PHPExcel_Writer_Excel5_Escher */ private $_spOffsets; - + /** + * Shape types. + * + * @var array + */ + private $_spTypes; + /** * Constructor * @@ -81,6 +87,7 @@ class PHPExcel_Writer_Excel5_Escher $writer = new PHPExcel_Writer_Excel5_Escher($dgContainer); $this->_data = $writer->close(); $this->_spOffsets = $writer->getSpOffsets(); + $this->_spTypes = $writer->getSpTypes(); } break; @@ -307,13 +314,15 @@ class PHPExcel_Writer_Excel5_Escher // get the shape offsets relative to the spgrContainer record $spOffsets = $writer->getSpOffsets(); - + $spTypes = $writer->getSpTypes(); + // save the shape offsets relative to dgContainer foreach ($spOffsets as & $spOffset) { $spOffset += 24; // add length of dgContainer header data (8 bytes) plus dg data (16 bytes) } $this->_spOffsets = $spOffsets; + $this->_spTypes = $spTypes; } // write the record @@ -339,6 +348,7 @@ class PHPExcel_Writer_Excel5_Escher // initialize spape offsets $totalSize = 8; $spOffsets = array(); + $spTypes = array(); // treat the inner data foreach ($this->_object->getChildren() as $spContainer) { @@ -349,6 +359,8 @@ class PHPExcel_Writer_Excel5_Escher // save the shape offsets (where new shape records begin) $totalSize += strlen($spData); $spOffsets[] = $totalSize; + + $spTypes = array_merge($spTypes, $writer->getSpTypes()); } // write the record @@ -364,6 +376,7 @@ class PHPExcel_Writer_Excel5_Escher $this->_data = $header . $innerData; $this->_spOffsets = $spOffsets; + $this->_spTypes = $spTypes; break; case 'PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer': @@ -386,6 +399,7 @@ class PHPExcel_Writer_Excel5_Escher $data .= $header . pack('VVVV', 0,0,0,0); } + $this->_spTypes[] = ($this->_object->getSpType()); // write the shape record $recVer = 0x2; @@ -450,10 +464,10 @@ class PHPExcel_Writer_Excel5_Escher // end offsetY $endOffsetY = $this->_object->getEndOffsetY(); - $clientAnchorData = pack('vvvvvvvvv', 0x02, + $clientAnchorData = pack('vvvvvvvvv', $this->_object->getSpFlag(), $c1, $startOffsetX, $r1, $startOffsetY, $c2, $endOffsetX, $r2, $endOffsetY); - + $length = strlen($clientAnchorData); $recVerInstance = $recVer; @@ -509,4 +523,15 @@ class PHPExcel_Writer_Excel5_Escher return $this->_spOffsets; } + /** + * Gets the shape types + * + * @return array + */ + public function getSpTypes() + { + return $this->_spTypes; + } + + } diff --git a/Classes/PHPExcel/Writer/Excel5/Workbook.php b/Classes/PHPExcel/Writer/Excel5/Workbook.php index a49655f..fafc123 100644 --- a/Classes/PHPExcel/Writer/Excel5/Workbook.php +++ b/Classes/PHPExcel/Writer/Excel5/Workbook.php @@ -779,6 +779,19 @@ class PHPExcel_Writer_Excel5_Workbook extends PHPExcel_Writer_Excel5_BIFFwriter $chunk .= $this->writeData($this->_writeDefinedNameBiff8(pack('C', 0x06), $formulaData, $i + 1, true)); } } + + // write autofilters, if any + for ($i = 0; $i < $total_worksheets; ++$i) { + $sheetAutoFilter = $this->_phpExcel->getSheet($i)->getAutoFilter(); + if($sheetAutoFilter != ''){ + $rangeBounds = PHPExcel_Cell::rangeBoundaries($sheetAutoFilter); + + //Autofilter built in name + $name = pack('C', 0x0D); + + $chunk .= $this->writeData($this->_writeShortNameBiff8($name, $i + 1, $rangeBounds, true)); + } + } return $chunk; } @@ -817,6 +830,42 @@ class PHPExcel_Writer_Excel5_Workbook extends PHPExcel_Writer_Excel5_BIFFwriter return $header . $data; } + + /** + * Write a short NAME record + * + * @param string $name + * @param string $sheetIndex 1-based sheet index the defined name applies to. 0 = global + * @param int[][] $range rangeboundaries + * @param bool $isHidden + * @return string Complete binary record data + * */ + private function _writeShortNameBiff8($name, $sheetIndex = 0, $rangeBounds, $isHidden = false){ + $record = 0x0018; + + // option flags + $options = ($isHidden ? 0x21 : 0x00); + + $extra = pack('Cvvvvv', + 0x3B, + $sheetIndex - 1, + $rangeBounds[0][1] - 1, + $rangeBounds[1][1] - 1, + $rangeBounds[0][0] - 1, + $rangeBounds[1][0] - 1); + + // size of the formula (in bytes) + $sz = strlen($extra); + + // combine the parts + $data = pack('vCCvvvCCCCC', $options, 0, 1, $sz, 0, $sheetIndex, 0, 0, 0, 0, 0) + . $name . $extra; + $length = strlen($data); + + $header = pack('vv', $record, $length); + + return $header . $data; + } /** * Stores the CODEPAGE biff record. diff --git a/Classes/PHPExcel/Writer/Excel5/Worksheet.php b/Classes/PHPExcel/Writer/Excel5/Worksheet.php index c090922..ec1f34d 100644 --- a/Classes/PHPExcel/Writer/Excel5/Worksheet.php +++ b/Classes/PHPExcel/Writer/Excel5/Worksheet.php @@ -390,6 +390,11 @@ class PHPExcel_Writer_Excel5_Worksheet extends PHPExcel_Writer_Excel5_BIFFwriter $this->_writeColinfo($this->_colinfo[$i]); } } + + if ($_phpSheet->getAutoFilter() != '') { + // Write AUTOFILTERINFO + $this->_writeAutoFilterInfo(); + } // Write sheet dimensions $this->_writeDimensions(); @@ -2017,7 +2022,7 @@ class PHPExcel_Writer_Excel5_Worksheet extends PHPExcel_Writer_Excel5_BIFFwriter $fPrintGrid = $this->_phpSheet->getPrintGridlines() ? 1 : 0; // Boolean flag $header = pack("vv", $record, $length); - $data = pack("v", $fPrintGrid); + $data = pack("v", $fPrintGrid); $this->_append($header . $data); } @@ -2037,6 +2042,21 @@ class PHPExcel_Writer_Excel5_Worksheet extends PHPExcel_Writer_Excel5_BIFFwriter $this->_append($header . $data); } + /** + * Write the AUTOFILTERINFO BIFF record. This is used to configure the number of autofilter select used in the sheet. + */ + private function _writeAutoFilterInfo(){ + $record = 0x009D; // Record identifier + $length = 0x0002; // Bytes to follow + + $rangeBounds = PHPExcel_Cell::rangeBoundaries($this->_phpSheet->getAutoFilter()); + $iNumFilters = 1 + $rangeBounds[1][0] - $rangeBounds[0][0]; + + $header = pack("vv", $record, $length); + $data = pack("v", $iNumFilters); + $this->_append($header . $data); + } + /** * Write the GUTS BIFF record. This is used to configure the gutter margins * where Excel outline symbols are displayed. The visibility of the gutters is @@ -2681,7 +2701,7 @@ class PHPExcel_Writer_Excel5_Worksheet extends PHPExcel_Writer_Excel5_BIFFwriter $writer = new PHPExcel_Writer_Excel5_Escher($this->_escher); $data = $writer->close(); $spOffsets = $writer->getSpOffsets(); - + $spTypes = $writer->getSpTypes(); // write the neccesary MSODRAWING, OBJ records // split the Escher stream @@ -2692,7 +2712,6 @@ class PHPExcel_Writer_Excel5_Worksheet extends PHPExcel_Writer_Excel5_BIFFwriter $record = 0x00EC; // Record identifier // chunk of Escher stream for one shape - $dataChunk = substr($data, $spOffsets[$i -1], $spOffsets[$i] - $spOffsets[$i - 1]); $length = strlen($dataChunk); @@ -2705,17 +2724,42 @@ class PHPExcel_Writer_Excel5_Worksheet extends PHPExcel_Writer_Excel5_BIFFwriter $objData = ''; // ftCmo - $objData .= - pack('vvvvvVVV' - , 0x0015 // 0x0015 = ftCmo - , 0x0012 // length of ftCmo data - , 0x0008 // object type, 0x0008 = picture - , $i // object id number, Excel seems to use 1-based index, local for the sheet - , 0x6011 // option flags, 0x6011 is what OpenOffice.org uses - , 0 // reserved - , 0 // reserved - , 0 // reserved - ); + if($spTypes[$i] == 0x00C9){ + // Add ftCmo (common object data) subobject + $objData .= + pack('vvvvvVVV' + , 0x0015 // 0x0015 = ftCmo + , 0x0012 // length of ftCmo data + , 0x0014 // object type, 0x0014 = filter + , $i // object id number, Excel seems to use 1-based index, local for the sheet + , 0x2101 // option flags, 0x2001 is what OpenOffice.org uses + , 0 // reserved + , 0 // reserved + , 0 // reserved + ); + + // Add ftSbs Scroll bar subobject + $objData .= pack('vv', 0x00C, 0x0014); + $objData .= pack('H*', '0000000000000000640001000A00000010000100'); + // Add ftLbsData (List box data) subobject + $objData .= pack('vv', 0x0013, 0x1FEE); + $objData .= pack('H*', '00000000010001030000020008005700'); + } + else { + // Add ftCmo (common object data) subobject + $objData .= + pack('vvvvvVVV' + , 0x0015 // 0x0015 = ftCmo + , 0x0012 // length of ftCmo data + , 0x0008 // object type, 0x0008 = picture + , $i // object id number, Excel seems to use 1-based index, local for the sheet + , 0x6011 // option flags, 0x6011 is what OpenOffice.org uses + , 0 // reserved + , 0 // reserved + , 0 // reserved + ); + } + // ftEnd $objData .= pack('vv' diff --git a/changelog.txt b/changelog.txt index c2556b6..cc7f5a0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -29,6 +29,7 @@ Fixed in develop branch: Current options are tcPDF, mPDF, DomPDF tcPDF Library has now been removed from the deployment bundle - Feature: (MBaker) Initial version of HTML Reader +- Feature: (Progi1984) & (blazzy) Work items 9605 - Implement support for AutoFilter in PHPExcel_Writer_Excel5 - Bugfix: (cyberconte) Patch 12318 - OOCalc cells containing inside the tag - Bugfix: (schir1964) Fix to listWorksheetInfo() method for OOCalc Reader - Bugfix: (MBaker) Support for "e" (epoch) date format mask