Styles and dropdowns are gone - php

Using PHPSpreadsheet, when populating an existing spreadsheet, all styles, conditional formatting, dropdowns, etc. are gone.
How can I retain the original settings while writing to php://output?
Here's a screenshot of what the base ODS file should look like:
When opening this template and writing values from the database to the R/A/S/C/I cells, the outcome is this:
Here is the code I have so far. You can ignore $modulePath etc. as that is just the lookup of the full system path to the file.
$original = $modulePath . '/resources/ISO27k-RASCI-tool.ods';
$reader = IOFactory::createReader('Ods');
$sheet = $reader->load($original);
$sheet->setActiveSheetIndex(1);
$sheet->setHasMacros(true);
$col = range('A', 'Z');
$teams = $this->dataRecord->Annex()->Teams();
foreach ($teams as $i => $team) {
$sheet->getActiveSheet()->setCellValue($col[$i + 2] . '1', $team->Name);
}
$writer = new Ods($sheet);
$writer->save('php://output');

Be aware of that not all excel/ods styling commands are implemented in phpspreedsheet.
As well there is no "pivottable" and "format as table" support.
Your table looks like you use at least "format as table".
The problem is that you crate a new file and your file is only a kind of template. Unsupported cell styling will be skipped. The only solution is that you must style your output with the styling commands.

Related

Style & layout is not copied while creating new pptx from pptx in PHPPresentation

I want to split slides of one pptx file into seperated pptx files, containing one slide each. The content/text is copied but the layout & styling is not copied. Here is the code.
Can anyone please help ?
<?php
use PhpOffice\PhpPresentation\PhpPresentation;
use PhpOffice\PhpPresentation\IOFactory;
use PhpOffice\PhpPresentation\Style\Color;
use PhpOffice\PhpPresentation\Style\Alignment;
use PhpOffice\PhpPresentation\Slide\SlideLayout;
$objReader = \PhpOffice\PhpPresentation\IOFactory::createReader('PowerPoint2007');
$objPHPPowerPoint = $objReader->load('a.pptx');
$totalSlides = $objPHPPowerPoint->getSlideCount();
$oMasterSlide = $objPHPPowerPoint->getAllMasterSlides()[0];
$documentProperties = $objPHPPowerPoint->getDocumentProperties();
for ( $count = 0; $count < $totalSlides; $count++ ) {
$objPHPPresentation = new PhpPresentation();
$slide = $objPHPPowerPoint->getSlide( $count );
$background = $slide->getBackground();
$newSlide = $objPHPPresentation->addSlide( $slide );
$newSlide->setBackground ( $background );
$objPHPPresentation->setAllMasterSlides( $oMasterSlide );
$objPHPPresentation->removeSlideByIndex(0);
$oWriterPPTX = \PhpOffice\PhpPresentation\IOFactory::createWriter($objPHPPresentation, 'PowerPoint2007');
$oWriterPPTX->save($count.'.pptx');
}
I don't think it's an issue with your code - more an issue with the underlying libraries - as mentioned here: PhpPresentation imagecreatefromstring(): Data is not in a recognized format - PHP7.2
It ran a test to see if it was something I could replicate - and I was able to. The key difference in my test was in one presentation I had a simple background, and in the other it was a gradient.
This slide caused problems:
But this one was copied over fine:
With the more complex background I got errors like:
PHP Warning: imagecreatefromstring(): Data is not in a recognized format
My code is even less complicated than yours, I just clone the original slideshow and remove all except a single slide before saving it:
for ( $count = 0; $count < $totalSlides; $count++ ) {
$copyVersion = clone $objPHPPowerPoint;
foreach ($copyVersion->getAllSlides() as $index => $slide) {
if ($index !== $count) {
$copyVersion->removeSlideByIndex($index);
}
}
$oWriterPPTX = \PhpOffice\PhpPresentation\IOFactory::createWriter($copyVersion, 'PowerPoint2007');
$oWriterPPTX->save($count.'.pptx');
}
Sorry if this doesn't exactly solve your problem, but hopefully it can help identify why it's happening. The other answer I linked to has more information about finding unsupported images types in your slides.
You can try using Aspose.Slides Cloud SDK for PHP to split a presentation into separate slides and save them to many formats. You can evaluate this REST-based API making 150 free API calls per month for API learning and presentation processing. The following code example shows you how to split a presentation and save slides to PPTX format using Aspose.Slides Cloud:
use Aspose\Slides\Cloud\Sdk\Api\Configuration;
use Aspose\Slides\Cloud\Sdk\Api\SlidesApi;
use Aspose\Slides\Cloud\Sdk\Model;
$configuration = new Configuration();
$configuration->setAppSid("my_client_id");
$configuration->setAppKey("my_client_key");
$slidesApi = new SlidesApi(null, $configuration);
$filePath = "example.pptx";
// Upload the file to the default storage.
$fileStream = fopen($filePath, 'r');
$slidesApi->uploadFile($filePath, $fileStream);
// Split the file and save the slides in PPTX format in the same folder.
$response = $slidesApi->split($filePath, null, Model\SlideExportFormat::PPTX);
// Download files of the slides.
foreach($response->getSlides() as $slide) {
$slideFilePath = pathinfo($slide->getHref())["basename"];
$slideFile = $slidesApi->downloadFile($slideFilePath);
echo $slideFile->getRealPath(), "\r\n";
}
Sometimes it is necessary to split a presentation without using any code. In this case, you can use Online PowerPoint Splitter.
I work as a Support Developer at Aspose.

PHPSpreadsheet, Drawing - move and size with cells

I try to set the setting "Move and size with cells" for my images in PHPSpreadsheet, but cant find any information anywhere. Now my question is, if that feature is implemented yet, and if it is, how its called.
Maybe is too late for "help YOU" but this will be useful for another people
I had the same problem and found 2 "things"
Nothing
Google is a bunch of millons links/sublinks to 50 top-web. Google is the old AOL of 1998
But, I didn't give up and I could "fix" it. Actually, in the folder of PHPSpreadsheet you will see the next next path folder;
/PhpOffice/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx
There're a file called: Drawing.php, in this file you will find a function called: writeDrawing
there you will find the following code;
[Line: 152]
$objWriter->startElement('xdr:oneCellAnchor');
It must be replace with:
$objWriter->startElement('xdr:twoCellAnchor');
After, 2 o 3 lines, under those code you will find this code;
[Line:166 to 169]
$objWriter->startElement('xdr:ext');
$objWriter->writeAttribute('cx', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getWidth()));
$objWriter->writeAttribute('cy', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getHeight()));
$objWriter->endElement();
Add comment marks like "//" or "/* code */" and comment it for avoid these code lines. And now, add the following code;
$objWriter->startElement('xdr:to');
$objWriter->writeElement('xdr:col', $aCoordinates[0] - 1);
$objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getWidth()));
$objWriter->writeElement('xdr:row', $aCoordinates[1]);
$objWriter->writeElement('xdr:rowOff', "0");
$objWriter->endElement();
In the following code, should be fix
[Line:178 to 182]
$objWriter->startElement('xdr:cNvPr');
$objWriter->writeAttribute('id', $pRelationId);
$objWriter->writeAttribute('name', $pDrawing->getName());
$objWriter->writeAttribute('descr', $pDrawing->getDescription());
$objWriter->endElement();
the fixed code (don't add "$objWriter->endElement()");
$objWriter->startElement('xdr:cNvPr');
$objWriter->writeAttribute('name', $pDrawing->getName() . $pRelationId);
$objWriter->writeAttribute('descr', $pDrawing->getDescription());
$objWriter->writeAttribute('id', $pRelationId+1);
At Now, in the [Line: 183] INSERT this code;
$objWriter->startElement('a:extLst');
$objWriter->startElement('a:ext');
$objWriter->writeAttribute('uri', "{FF2B5EF4-FFF2-40B4-BE49-F238E27FC236}");
$objWriter->endElement(); // this close a:ext
$objWriter->endElement(); // this close a:extLst
$objWriter->endElement(); // thise xdr:cNvPr [this will replace the $objWriter->endElement(); of Line 182. It was lost in the previous step
Now, a code like this;
require "../../PhpOffice/vendor/autoload.php";
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
...
$imgfile = base64_decode($img_from_query_sql);
$fname = "../tmp/".rand(). ".xlsx";
file_put_contents($fname, $imgfile);
$drawing = new PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$Cell = $Letter[$j] . $row;
$drawing->setPath($fname);
$drawing->setCoordinates($Cell );
$drawing->setResizeProportional(false);
$drawing->setWidth(300);
$drawing->setHeight(300);
$drawing->setWorksheet($sheet);
$sheet->setTitle("TitleOfSheet");
$writer = new Xlsx($spreadsheet);
$writer->save("php://output");
...
Execute and download your excel with images. Check your images: right click->"Size and properties..."
Will appear a right-panel of "Image format". In sub-menu "properties" should be a 3 "radio option" (circle with black point)
Move and size with cells
Move but don't size with cells
Don't move or size with cells
Phpspreadsheet orignally, makes pictures with option 2-> Move but don't size with cells but, we changed the code of Drawing.php and now all images are created with option 1->Move and size with cells
At now, if we have a excel file with a list of "things" and 1 image per row, we can apply filters and if some rows doesn't have any image the following image-row will not overlapping over the visible row (matching results).
Each picture filtered will be hidden with is parent row, and each picture matching with filter will be 'stick' to his parent row as always should have been
UPDATE: 28-07-22 - SpreadSheet 1.23
In vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php still exist a function called writeDrawing in line 184;
This functions has changed a bit since the last version I used. Now, writeDrawing have many improves and doesn't require edit any codeline;
Currenly, "BaseDrawing" is a data type that have a new method called;
setCoordinates2 (two) instead of setCoordinates (withtout "2" at end)
You can use setCoordinates2 in your main_custom_file_excel_creator.php like;
$Cell = "A2";
$drawing->setCoordinates2($Cell); //instead of setCoordinates
The updated function (writeDrawing) from line 184 to 340, will use $drawing. This $drawing variable is type BaseDrawing so, in line 187 it will check if is set or not and will store a boolean in $isTwoCellAnchor
It will help us for some setting like #Wind Cloud was looking. Can be solved doing a simple;
$drawing->setEditAs("absolute");
The new version 1.23 (2022-jul-28) have better class Drawing;
Now you can do it without any edit. You only need set the correct methods;
for Move but don't size with cells
require "../../PhpOffice/vendor/autoload.php";
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
...
$imgfile = base64_decode($img_from_query_sql);
$fname = "../tmp/somefile123.xlsx";
file_put_contents($fname, $imgfile);
$drawing = new PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$Cell = "A2";
$drawing->setPath($fname);
$drawing->setCoordinates2($Cell ); //USE THE NEW METHOD SETTER
$drawing->setResizeProportional(false);
$drawing->setWidth(300);
$drawing->setHeight(300);
$drawing->setWorksheet($sheet);
$sheet->setTitle("TitleOfSheet");
$writer = new Xlsx($spreadsheet);
$writer->save("php://output");
A user called Wind Cloud asked;
I want to set "Don't move or size with cells" instead. Do you know
how? – Wind Cloud
In the new version ^1.23 will be very easy;
require "../../PhpOffice/vendor/autoload.php";
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
...
$imgfile = base64_decode($img_from_query_sql);
$fname = "../tmp/somefile123.xlsx";
file_put_contents($fname, $imgfile);
$drawing = new PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$Cell = "A2";
$drawing->setPath($fname);
$drawing->setCoordinates**2**($Cell);
$drawing->setEditAs("absolute"); //HERE IS YOUR KEY
$drawing->setResizeProportional(false);
$drawing->setWidth(300);
$drawing->setHeight(300);
$drawing->setWorksheet($sheet);
$sheet->setTitle("TitleOfSheet");
$writer = new Xlsx($spreadsheet);
$writer->save("php://output");

use "exceldatatables" to join two sheets of a different workbook

I have to create an Excel file with a data sheet that will vary according to a database and a sheet containing multiple PivotTables that have their own PivotCharts.
"data.xlsx" contains a sheet with all new data.
"graph.xlsx" contains a sheet with old data and a sheet with PivotTables.
My goal is to have "graph.xlsx" containing a sheet with all new data and the sheet with PivotTables.
I found a perfect lib to do this : https://github.com/svrnm/exceldatatables
But I block on the use of it, I would open "graph.xlsx" delete its sheet named "brut data", then to add a new sheet named "brut data" initialized with the new data contained in "data.xlsx".
To do it I saw this function from ExcelWorkbook.php a Class of this lib.
public function addWorksheet(ExcelWorksheet $worksheet, $id = null, $name = null)
But I don't understand how to use it.
(I'm the author of the mentioned library)
There are two steps you need to take, to achieve your goal, the first is reading the data.xlsx. The second is writing that data into your graph.xlsx. The library is solving step two:
require_once('../vendor/autoload.php');
$dataTable = new Svrnm\ExcelDataTables\ExcelDataTable();
$in = 'graph.xlsx';
$out = 'out.xlsx';
$data = /* ... step 1 ... */
$dataTable->showHeaders()->addRows($data)->attachToFile($in, $out);
For step 1 you could leverage PHPExcel. I haven't tested it, but something similar like this:
$r = PHPExcel_IOFactory::createReader('Excel2007');
$data = $r->load($filename)->getActiveSheet()->toArray(null, true, true);
There is another option: You could unpack both graph.xlsx and data.xlsx and merge the sheets.

How to update csv column names with database table header

I am facing this problem some past days and now frustrate because I have to do it.
I need to update my CSV file columns name with database table header. My database table fields are different from CSV file. Now the problem is that first I want to update column name of CSV file with database table headers and then import its data with field mapping into database.
Please help me I don't know how I can solve this.
This is my php code:
$file = $_POST['fileName'];
$filename = "../files/" . $file;
$list = $_POST['str'];
$array_imp = implode(',', $list);
$array_exp = explode(',', $array_imp);
$fp = fopen("../files/" . $file, "w");
$num = count($fp);
for ($c = 0; $c < $num; $c++) {
if ($fp[c] !== '') {
fputcsv($fp, $array_exp);
}
}
fclose($fp);
require_once("../csv/DataSource.php");
$path = "../files/test_mysql.csv";
$dbtable = $ext[0];
$csv = new File_CSV_DataSource;
$csv->load($path);
$csvData = $csv->connect();
$res='';
foreach($csvData as $key)
{ print_r($key[1]);
$myKey ='';
$myVal='';
foreach($key as $k=>$v)
{
$myKey .=$k.',';
$myVal .="'".$v."',";
}
$myKey = substr($myKey, 0, -1);
$myVal = substr($myVal, 0, -1);
$query="insert into tablename($myKey)values($myVal)";
$res= mysql_query($query);
You have got an existing file of which the first line needs to be replaced.
This has been generally outlined here:
Overwrite Line in File with PHP
Some little explanation (and some tips that are not covered in the other question). Most often it's easier to operate with two files here:
The existing file (to be copied from)
A new file that temporarily will be used to write into.
When done, the old file will be deleted and the new file will be renamed to the name of the old file.
Your code does not work because you are already writing the new first line into the old file. That will chop-off the rest of the file when you close it.
Also you look misguided about some basic PHP features, e.g. using count on a file-handle does not help you to get the number of lines. It will just return 1.
Here is step by step what you need to do:
Open the existing file to read from. Just read the first line of it to advance the file-pointer (fgets)
Open a new file to write into. Write the new headers into it (as you already successfully do).
Copy all remaining data from the first file into the new, second file. PHP has a function for that, it is called stream_copy_to_stream.
Close both files.
Now check if the new file is what you're looking for. When this all works, you need to add some more steps:
Rename the original file to a new name. This can be done with rename.
Rename the file you've been written to to the original filename.
If you want, you then can delete the file you renamed in 5. - but only if you don't need it any longer.
And that's it. I hope this is helpful. The PHP manual contains example code for all the functions mentioned and linked. Good luck. And if you don't understand your own code, use the manual to read about it first. That reduces the places where you can introduce errors.
If you are managing to insert the table headers then you're half way there.
It sounds to me like you need to append the data after the headers something like:
$data = $headers;
if($fp[c]!=='')
{
$data .= fputcsv($fp, $array_exp);
}
Notice the dot '.' before the equals '=' in the if statement. This will add none blank $fp[c]values after the headers.

PHPExcel - Existing array functions get converted into normal functions?

Greetings all,
I'm trying to write a script that loads an existing spreadsheet containing a number of array formulas, add data to a worksheet and save it. When opening the file after the script runs, the spreadsheet's formulas are no longer array formulas.
Below is the stripped down version of what I'm attempting:
$excelFile = new PHPExcel();
$fileName = 'blah.xlsx';
$excelReader = PHPExcel_IOFactory::createReader('Excel2007');
$excelFile = $excelReader->load($fileName);
//first sheet contains formulas to process the resulting dump
$excelFile->setActiveSheetIndex(1);
// just to illustrate what's used when retrieving data
...
while($record = db_fetch_object($queryResult)) {
$excelFile->getActiveSheet()->setCellValueByColumnAndRow($col, $row, $record->field);
}
$excelWriter = PHPExcel_IOFactory::createWriter($excelFile, 'Excel2007');
$excelWriter->save($fileName);
After the script runs, a formula that once appeared as:
{=SUM(A1:C6)}
Now appears as:
=SUM(A1:C6)
Thanks in advance for your insight and input
Tony
It seems that the PHPExcel Cell object does not handle a formula element's attributes, so things like "t=array" would be lost by the time you get to createWriter.
To resolve this issue, we've made modifications to the cell and excel2007 reader and writer classes.
In cell.php:
private $_formulaAttributes;
// getter and setter functions
In reader/excel2007.php:
line 769 - after $this->castToFormula...
if(isset($c->f['t'])){
$attributes = array();
$attributes = $c->f;
$docSheet->getCell($r)->setFormulaAttributes($attributes);
}
In writer/excel2007/worksheet.php:
line 1042 - after case 'f':
$attributes = $pCell->getFormulaAttributes();
if($attributes['t'] == 'array') {
$objWriter->startElement('f');
$objWriter->writeAttribute('t', 'array');
$objWriter->writeAttribute('ref', $pCell->getCoordinate());
$objWriter->writeAttribute('aca', '1');
$objWriter->writeAttribute('ca', '1');
$objWriter->text(substr($pCell->getValue(), 1));
$objWriter->endElement();
} else {
$objWriter->writeElement('f', substr($pCell->getValue(), 1));
}
hope this helps someone...
Unfortunately, the PHPExcel readers and writers don't yet support array formulas. I believed that the Excel2007 reader/writer did, but your experience suggests otherwise.

Categories