PHPExcel - Clone sheet and keep its original style - php

I've tried to check every possible similar solution both here and in the PHPExcel official documentation / forums, but I didn't find any solution to my issue.
The problem
I'm trying to clone (or copy, being honest) a sheet to parse it into another file created through phpexcel by keeping the style of the cloned sheet.
The setup is:
sheet.xls <--- File to OPEN & COPY
PHPExcel object <-- File that gets created X times in a for loop, where I need to append Y Sheets according to a set of arrays.
What works
The cloning & appending works beautifully, takes time because of some strange notices related to a phpexcel file:
Notice: Undefined offset: 1 in \serverpath\PHPExcel\Classes\PHPExcel.php on line 729
Notice: Undefined offset: 2 in \serverpath\PHPExcel\Classes\PHPExcel.php on line 729
Notice: Undefined offset: 3 in \serverpath\PHPExcel\Classes\PHPExcel.php on line 729
Notice: Undefined offset: 4 in \serverpath\PHPExcel\Classes\PHPExcel.php on line 729
EDIT ::
Line 729 refers to this:
foreach ($sheet->getCellCollection(false) as $cellID) {
$cell = $sheet->getCell($cellID);
++$countReferencesCellXf[$cell->getXfIndex()]; // line 729
}
Which is about styles as far as I can tell.
<-- There are thousand of these, no idea where they are coming from though, the files are getting generated correctly, they just lose their format as said above.
What doesn't work
The generated files LOSES the original format but keeps the formula, hence every single border (and any style) of the original "template" (sheet.xls) is lost.
The relevant part of the code
I'm only posting the really relevant code here, mostly because it's about a thousand lines of code.
File that will later be saved creation (happens in parent foreach) :
$file = new PHPExcel();
Cloning (happens inside a child foreach after the creation above) :
$sd = $objReader->load("sheet.xls");
$sc = $sd ->getActiveSheet()->copy();
$clonedSheet = clone $sc;
Appending (happens N times inside a child foreach of the cloning above) :
$ficheName = "not relevant tbh and less than 31 characters";
$temporarySheet = clone $clonedSheet;
$temporarySheet->setTitle($ficheName);
$file->addSheet($temporarySheet,0);
$file->setActiveSheetIndex($file->getIndex($temporarySheet));
unset($temporarySheet);
// some actions are done here
Saving (outside of the foreach, happens in the same foreach where the PHPExcel object gets created:
$objWriter = PHPExcel_IOFactory::createWriter($file, 'Excel5');
$objWriter->save($filename);
Restrictions
I have absolutely no restrictions about what kind of excel format I'm supposed to use, I'm using 2003 because I have some machines that only works with excel 2003, but they will soon be upgrading to office 2010, so literally any reader and writer is okay, I'm using 2003 because I've always used it and had no problem so far.
I am forced, though, to clone the XLS sheet inside another file, the only possible trick I can do is clone the sheet inside the same file and save it later by keeping the original one, but if there is any other chance to "export" the style I would really appreciate it.
What I have already checked:
PHPExcel clone .xlsm with macros
http://www.mindfiresolutions.com/Cloning-a-XLS-worksheet-in-PHP--Mindfire-Solutions-933.php
PHPExcel 1.8.0 - Creating many sheets by cloning a template sheet gets slower with each clone
Workaround for copying style with PHPExcel
EDIT ::
I've also tried to:
Open the file and get the sheet instead of cloning the original one - Problem persists.
Tried to use Excel2007 both for reading and writing - Problem persist.
Tried NOT to use ->copy() - Problem persists.
UPDATED phpexcel to 1.8, now the Notice above appears on line 1079, but refers to the same exact piece of code - Problem persists.

Okay, I've figured out a possible workaround.
Because the problem seems to be with:
clone
PHPExcel Worksheet ->copy() prototype
Referencing PHPExcel Worksheet
I've thought about that:
Instead of creating a new PHPExcel object instance, just OPEN the original file.
Append the file with other instances of the same file, by copying the sheet still from the same file.
Remove the LAST sheet when finished.
So, in a nutshell, I've changed this:
$file = new PHPExcel();
To this:
$file = $objReader->load("sheet.xlsx"); // decided to work with excel2007
And this:
$objWriter = PHPExcel_IOFactory::createWriter($file, 'Excel5');
$objWriter->save($filename);
To this:
$sheetCount = $file->getSheetCount();
$file->removeSheetByIndex($sheetCount - 1);
$objWriter = PHPExcel_IOFactory::createWriter($file, 'Excel2007'); // same story, excel 2007 instead of 2003
$objWriter->save($filename);
Now I don't have any error and everything is working as expected, despite I'm sure that there may be another cleverer solution.

If you don't change the format of sheet.xls then try to
A) use .xlsx
B) rename *.xlsx to *.zip
C) unzip sheet.zip, and the files you haved saved
D) copy the .xls/styles.xml from sheet to the saved files
E) repack and rename *.zip to *.xlsx
and your format is back.
You can minimize the problem a bit by not generating in a loop in php but rather run the php in a loop.

Related

Reading File With PhpSpreadsheet Sheet Out of Bounds Error

Trying to read from a XLSX file with PhpSpreadsheet however getting below error:
PHP Fatal error: Uncaught PhpOffice\PhpSpreadsheet\Exception: Your requested sheet index: -1 is out of bounds. The actual number of sheets is 0. in C:\php\projects\stock\vendor\phpoffice\phpspreadsheet\src\PhpSpreadsheet\Spreadsheet.php:678
There is definitely a worksheet and my code below to pull a single cell works fine when I remove the table and just have unformatted data.
$spreadsheet = new PhpOffice\PhpSpreadsheet\Spreadsheet();
$inputFileName = "path/to/temp_file_download.xlsx";
$inputFileType = \PhpOffice\PhpSpreadsheet\IOFactory::identify($inputFileName);
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);
$spreadsheet = $reader->load($inputFileName);
echo $cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(2, 2)->getValue();
It seems that saving the file with Excel allows it to work just fine however excel does not complain that it is corrupt etc. I have no control over the actual spreadsheet as generated from an external source.
Just wondering if anyone has had similar issues and have any ideas for working around or what I may be doing wrong?
Tried to use the older PhpExcel also to get this to work however similar issues occur until I save the file again.
Have also tried XLSXReader with similar issues:
PHP Fatal error: Uncaught Exception: File /xl/workbook.xml does not exist in the Excel file in C:\php\projects\stock\vendor\XLSXReader\XLSXReader.php:57
Stack trace:
#0 C:\php\projects\stock\vendor\XLSXReader\XLSXReader.php(70): XLSXReader->getEntryData()
#1 C:\php\projects\stock\vendor\XLSXReader\XLSXReader.php(47): XLSXReader->parse()
#2 C:\php\projects\stock\stock.php(17): XLSXReader->__construct()
#3 {main}
thrown in C:\php\projects\stock\vendor\XLSXReader\XLSXReader.php on line 57
And have seen many issues on web for people having issues reading these files that area created like so however still not sure of a simple workaround.
Adding the workaround mentioned below hack for non standard xlsx seemed to do the trick!
https://github.com/PHPOffice/PHPExcel/issues/1187
Just need to add the class extending the xlsx class to phpspreadsheet then call that class instead and all works!

File downloaded via Podio Export API is not readable through code

Use-case:
Export data from different apps using batch EXPORT APIs to merge into a master excel file and use it for reporting purpose...
Implementation Details
I'm using Podio export API to get data from an application. Application name is Kall8-number-text (as an example).. here is the code snippet
Code Snippet
$batch_id = PodioItem::export(11804702,"xlsx",array("filters" => array( "kall8-number-text" => "510-592-5916") ));
PodioBatch::get( $batch_id );
$file = PodioFile::get($file_id);
// Download the file. This might take a while...
$file_content = $file->get_raw();
// Store the file on local disk
$path_to_file= "downloads/".$name;
file_put_contents($path_to_file, $file_content);
Problem Description:
I'm trying to read downloaded file using phpexcel library but getting error "You tried to set a sheet active by the out of bounds index: 0. The actual number of sheets is 0"
This error shows that file has NO sheet but it is not true. File has data/sheet and it shows upon opening that file.
One interesting fact, if I open the same excel file (manually by double click) and SAVE without making any change, then same code works fine. In my end to end process, I cannot add a manual step to open file every-time to proceed further...
For your information, I thought this is a PHPExcel bug and contacted Mark Backer (coordinator PHPOffice Suit) and he replied with following remarks which seems true.
"My guess would be non-standard namespacing in the file that's generated, which loading and saving in MS Excel fixes"
File Reading Code
$objReader = new PHPExcel_Reader_Excel2007();
$objReader->setReadDataOnly(true);
$objPHPExcel = $objReader->load('callsheet.xlsx');
$objPHPExcel->setActiveSheetIndex(0);
$dataArray = $objPHPExcel->getActiveSheet()->toArray(null, true,true,true);
var_dump($dataArray);
Error Trace
Fatal error: Uncaught exception 'PHPExcel_Exception' with message 'You tried to set a sheet active by the out of bounds index: 0. The actual number of sheets is 0.' in E:\xampp\htdocs\podioexcel\Classes\PHPExcel.php:688 Stack trace: #0 E:\xampp\htdocs\podioexcel\test.php(18): PHPExcel-
setActiveSheetIndex(0) #1 {main} thrown in E:\xampp\htdocs\podioexcel\Classes\PHPExcel.php on line 688
Can you help me to address this issue? This is holding up my project completely.
File Path: https://drive.google.com/file/d/0B79S561prrEBUDY1NEhXQ1JySWM/view
Original question at Stackoverflow: Error While loading excel sheet Using phpexcel
Ejaz

PHPExcel saving xlsx to xls, some cells are blank

I have code for converting xls to xlsx via PHPExcel:
$objPHPexcel = PHPExcel_IOFactory::load('file.xlsx');
$objWriter = PHPExcel_IOFactory::createWriter($objPHPexcel, 'Excel5');
$objWriter->save('new_file.xls');
But when I open created xls file some cells are empty.
What could be the problem?
Thanks!
Link to download original xlsx file
you should use one of following as formate type.
1)Excel5 -> file format between Excel Version 95 to 2003
2)Excel2003XML -> file format for Excel 2003
3)Excel2007 -> file format for Excel 2007
and add following line at line no.3 in your code before save file statement.
$fileType=PHPExcel_IOFactory::identify("file.xlsx"); //We can get file reader type automatically using
it generate file as per your requirement.
I do notice that some of these cells contain references to external files, e.g. cell A23 contains
='[Форма 1-2 от 15.12 ЯНВАРЬ екат.xls]НТТЗМ'!A77
Logically, PHPExcel can't simply access that external file (Форма 1-2 от 15.12 ЯНВАРЬ екат.xls) to retrieve data; nor can it handle references to external files... the file may not even exist; and if it did, there is a major overhead in loading (recursively) every external file that might be referenced in formulae.
While you haven't indicated which cells might be blank in the newly saved file, it would be my guess that they're the cells that contain these external file references

phpexcel - using it with excel template (chart goes missing) php

I have tried to use phpexcel with my own template file. phpexcel loads the file and writes data to some cells A2, A3, A4 for example.. and opens an output file with the new data.
my template file has chart built-in.. all i want to phpexcel to do is to populate values in cells and don't touch the chart. And, open the new file. (Please note that I don't want to make the chart in code.. I want the chart to pre-exist with in my template in same format as I created originally). Only the data should update.
But, when i try to do this.. the chart itself goes missing from the resulting file. After trying various ways.. still failed.
And, i found the following code from http://phpexcel.codeplex.com/discussions/397263
require_once 'Classes/PHPExcel.php';
/** PHPExcel_IOFactory */
include 'Classes/PHPExcel/IOFactory.php';
$target ='Results/';
$fileType = 'Excel2007';
$InputFileName = $target.'Result.xlsx';
$OutputFileName = $target . '_Result.xlsx';
//Read the file (including chart template)
$objReader = PHPExcel_IOFactory::createReader($fileType);
$objReader->setIncludeCharts(TRUE);
$objPHPExcel = $objReader->load($InputFileName);
//Change the file
$objPHPExcel->setActiveSheetIndex(0)
// Add data
->setCellValue('C3','10' )
->setCellValue('C4','20' )
->setCellValue('C5','30')
->setCellValue('C5','40' );
//Write the file (including chart)
PHPExcel_Settings::setZipClass(PHPExcel_Settings::PCLZIP);
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $fileType);
$objWriter->setIncludeCharts(TRUE);
$objWriter->save($OutputFileName);
The above code works in excel 2010 and now keeps my chart in tact... but still when I try to use filetype "Excel5" it doesn't work.
It throws the following error:
Fatal error: Call to undefined method PHPExcel_Reader_Excel5::setIncludeCharts()
in D:\IT\bfstools\PHPExcel\MyExamples\test1.php on line 16
Please provide a simple solution where I want my template file to work with .xls and .xlsx and all my original chart in the template file should stay intact. I do not want the chart removed it from the resulting file. Neither do I plan to create the chart using phpexcel code. (why write unnecessary code when excel can do all the work for you).
I want the easiest way out which is just to use everything with in my template and just populate cells with new data. And, my existing chart in the template comes live automatically. I don't want to write unnecessary code while I can safely rely on excel template and charting functions.
Please help.
There's a very good reason for this:
Charting is only implemented in core, and for the Excel2007 Readers and Writers at this point in time, so all of the other readers or writers will ignore charts, treat them as though they simply don't exist. The intention is to roll out charting to the other readers/writers over the coming year.
EDIT
I see from your comment that you don't understand how PHPExcel works at all, so I have a lot of explaining to do.
PHPExcel is not a library for "editing" workbook files: you're not using PHPExcel to change a file, you're changing a PHPExcel object that can be loaded from a file, and can subsequently be written to a file.
PHPExcel Core is an in-memory representation of the spreadsheet, with the different constituent objects such as worksheets, cells, images, styles, etc all represented as PHP Objects.
The PHPExcel Readers parse a spreadsheet file and load all the components from a file that they have been programmed to recognise, and create the appropriate PHPExcel core objects from those file components. If there is no equivalent PHPExcel Core object (such as Pivot Tables), then that file component can't be "loaded"; if the loader hasn't been programmed to recognise a file component, then it can't be loaded. In these cases, those elements from the file are simply ignored. Once the Reader has done it's job, a PHPExcel object exists, and the spreadsheet file is closed and forgotten.
When a PHPExcel Core object exists in memory, you have a set of methods allowing you to manipulate and change it, to add, modify or delete Core elements; but these work purely on the "in memory" collection of worksheet, cell, style objects that comprise the PHPExcel Core. The Core exists without knowledge of having been loaded from a file or having been created using a PHP "new PHPExcel()" statement; it makes no changes to files in any way.
When writing, the reverse is true. Each Writer takes the PHPExcel core objects, and writes them to a file in the appropriate format (Excel BIFF, OfficeOpenXML, HTML, etc). Like the Readers, each writer can only write those PHPExcel Core objects that it has been programmed to write. If it has not been programmed to write (for example, charts) then any charts defined in the PHPExcel Core will be ignored because that writer simply doesn't know how to write them yet. Likewise, features that exist in PHPExcel Core that are not supported by the file format that is being written to (such as cell styles for the CSV Writer) are ignored.
So to support a spreadsheet feature such as charts, it is necessary for the PHPExcel Core object collection to have been modified to provide an "in memory" representation of those elements, and for the different Readers to have been programmed to recognise those elements in the file they are loading and to convert them to the appropriate PHPExcel Core objects, and for the different Writers to have been programmed to convert the PHPExcel core representation to the appropriate file representation.
Each Reader and each Writer needs to be programmed individually. Charts is a relatively new feature, only added to the PHPExcel Core in the 1.7.7 release, and at this point only the Reader and Writer for the Excel2007 format have been programmed to recognise chart elements.
While it is the intention of the developers to extend this to cover the other formats as well, the necessary code isn't created automagically. Programming each individual Reader and Writer takes time and effort. While the Chart code for the Excel2007 Reader and Writer has now stabilised to the point where it is now no longer considered "experimental", and development focus is turning to writing the necessary code for chart handling in the Excel5 Reader and Writer, it is work that has not yet been completed.
If you can use Golang, try Excelize. Support save file without losing original charts of XLSX.
Try set setIncludeCharts
$objReader = PHPExcel_IOFactory::createReader('Excel2007');
// Tell the reader to include charts when it loads a file
$objReader->setIncludeCharts(TRUE);
// Load the file
$objPHPExcel = $objReader->load($filePath);

File size issue with PEAR Spreadsheet Excel Writer?

I have this code:
$book = new Spreadsheet_Excel_Writer();
$book->send('book.xls');
$report =& $book->addWorksheet('sheet');
$i = 0;
while ($i<145){
$report->write($i, 0, 'dsbajk_'.$i);
$i++;
}
$book->close();
This produces a file of 6 KB but when I tried to open it in Excel there is an error with the file, something like: Excel found content that couldn't read and is possible that some data was lost. (I can't write the exact message because is in Spanish).
If I change 145 to 144 (or less) in the loop no messages is displayed, so I think is a problem with the buffer or size of the text. I need to replace the while loop with a db query with more data so is important this works well.
I tried this in other server and I didn't have this problem, so maybe is something with the config.
I apreciate any help.
As Marc B said switch to PHPExcel is a good decision if you're starting a new project. It supports xlsx and other modern formats as well as the traditional xls. It also has more options and is easy to install and use.
I used PHPExcel in my project and I'm pretty happy. :)

Categories