PhpSpreadsheet charts not saving and/or corrupting file - php

So I'm working on a reporting application that requires generating Xls files with charts. The charts don't appear to be working though. I've tried just replacing the code below with the example in the PhpSpreadsheet github, but that also doesn't work.
I have the following for inserting the data (and chart) into the worksheet:
$this->spreadsheet->createSheet();
$worksheet = $this->spreadsheet->getActiveSheet();
$worksheet->setTitle($title);
$periodStartArray = array(array('', $periodStartDate, $periodEndDate));
while (!empty($stack)) {
$node = array_pop($stack);
if ($node->getData()['portfolioData'][0] != null) {
if ($node->getData()['portfolioData'][0]->getIncludeInStatusSide()) {
$startValue = (float)$this->getDataPoint($node, 'general', $this->periodStart, 'value');
$startValuePercent = number_format(($startValue / $this->startingTotalValues) * 100, $this->percentDecimals, $decimalPoints, $separators);
$endValue = (float)$this->getDataPoint($node, 'general', $this->periodEnd, 'value');
$endValuePercent = number_format(($endValue / $this->endingTotalValues) * 100, $this->percentDecimals, $decimalPoints, $separators);
$startArray = array($node->getData()['portfolioData'][0]->getNavn(), $startValuePercent, $endValuePercent);
$periodStartArray[] = $startArray;
}
}
}
$maxSize = count($periodStartArray);
$worksheet->fromArray($periodStartArray);
//$title . '!$B$1'
$dataSeriesLabels1 = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $title . '!$B$1', null, 1),
];
// $maxSize - 1 should give the actual number of data points (since the first entry in the array is the
// date row
$dataString = "'" . $title . "'" . '!$A$2:$A$' . $maxSize;
$xAxisTickValues1 = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $dataString, null, $maxSize - 1)
];
$dataString = "'" . $title . "'" . '!$B$2:$B$' . $maxSize;
$dataSeriesValues1 = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $dataString, null, $maxSize - 1)
];
$series = new DataSeries(
DataSeries::TYPE_PIECHART,
null,
range(0, count($dataSeriesValues1) - 1),
$dataSeriesLabels1,
$xAxisTickValues1,
$dataSeriesValues1
);
$layout = new Layout();
$layout->setShowVal(true);
$layout->setShowPercent(false);
$plotArea = new PlotArea($layout, array($series));
$legend = new Legend(Legend::POSITION_RIGHT, null, false);
$title = new Title($title);
$chart1 = new Chart(
'chart1',
$title,
$legend,
$plotArea,
true,
0,
null,
null
);
$chart1->setTopLeftPosition('A10');
$chart1->setBottomRightPosition('P25');
$worksheet->addChart($chart1);
It successfully inserts the data into the spreadsheet, but does not print out any charts. Here is where I'm saving the spreadsheet:
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xls($this->spreadsheet);
$writer->setIncludeCharts(true);
$writer->save($this->outputFile);
So I'm including the charts, but no charts appear. When I change the format to Xlsx, it seems to at least try to insert the charts but they're corrupt and Excel removes them. It's just simple data. Literally one row of a company name in the A column and two dates of values down the B and C columns. When I manually insert the charts after the excel file gets generated it works no problem.

use this
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
instaea of 0
like in sample file
https://github.com/PHPOffice/PhpSpreadsheet/blob/master/samples/templates/chartSpreadsheet.php

Change the 0 to 'gap' when creating the chart.
$chart1 = new Chart(
'chart1',
$title,
$legend,
$plotArea,
true,
'gap', // use 'gap' instead of 0,
null,
null
);
Below is the issue for PhPSpreadsheet
File containing a chart can not be opened by Excel 2003/2013/2019

Related

We found a problem with some content in 'Spreadsheet.xlsx'. Do you want us to try to recover as much as we can?

I have the following basic PHP project (just one file plus composer config):
composer.json
{
"config": {
"optimize-autoloader": true,
"platform": {
"php": "7.4.9"
}
},
"require": {
"phpoffice/phpspreadsheet": "1.10.0"
}
}
index.php
<?php
require_once __DIR__ . '/vendor/autoload.php';
function errorHandler() {
return true;
}
set_error_handler('errorHandler');
$sheets = array(
array('index' => 0, 'title' => 'Graph'),
array('index' => 1, 'title' => 'Data'),
);
$phpSpreadsheetObject = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
foreach ($sheets as $sheet) {
$name = $sheet['title'];
if ($sheet['index']) {
$worksheet[$name] = $phpSpreadsheetObject->createSheet($sheet['index']);
} else {
$worksheet[$name] = $phpSpreadsheetObject->getActiveSheet();
}
$phpSpreadsheetObject->setActiveSheetIndex($sheet['index']);
$worksheet[$name]->setTitle($sheet['title']);
}
$sheet = 'Graph'; // !!! SHEET CHANGE
$phpSpreadsheetObject->setActiveSheetIndex(1);
$worksheet[$sheet]->getColumnDimension('A')->setWidth("50");
// Charts
// Clients Chart
$xAxisTickValues = array(new \PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues('String', "'Data'!A2:A4", null, 3));
$dataSeriesValues = array(new \PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues('Number', "'Data'!B2:B4", null, 3));
$chartSeries = new \PhpOffice\PhpSpreadsheet\Chart\DataSeries(
\PhpOffice\PhpSpreadsheet\Chart\DataSeries::TYPE_BARCHART, // plotType
\PhpOffice\PhpSpreadsheet\Chart\DataSeries::GROUPING_CLUSTERED, // plotGrouping
range(0, count($dataSeriesValues) - 1), // plotOrder
[], // plotLabel
$xAxisTickValues, // plotCategory
$dataSeriesValues // plotValues
);
$chartSeries->setPlotDirection(\PhpOffice\PhpSpreadsheet\Chart\DataSeries::DIRECTION_COLUMN);
$plotArea = new \PhpOffice\PhpSpreadsheet\Chart\PlotArea(null, array($chartSeries));
$title = new \PhpOffice\PhpSpreadsheet\Chart\Title('Clients');
$yAxisLabel = new \PhpOffice\PhpSpreadsheet\Chart\Title('');
$charts = new \PhpOffice\PhpSpreadsheet\Chart\Chart(
'clients', // name
$title, // title
null, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
0, // displayBlanksAs
null, // xAxisLabel
$yAxisLabel // yAxisLabel
);
$charts->setTopLeftPosition('A1');
$charts->setBottomRightPosition('B19');
$worksheet[$sheet]->addChart($charts);
$sheet = 'Data'; // !!! SHEET CHANGE
$phpSpreadsheetObject->setActiveSheetIndex(1);
$dataArray = array(
1 => array('Date', 'Clients'),
2 => array(date('m/d/y', strtotime('01/01/2021')), '500'),
3 => array(date('m/d/y', strtotime('01/02/2021')), '725'),
4 => array(date('m/d/y', strtotime('01/03/2021')), '930'),
);
foreach (range('A', 'B') as $columnID) {
$worksheet[$sheet]->getColumnDimension($columnID)->setAutoSize(true);
}
$worksheet[$sheet]->fromArray($dataArray, ' ', 'A1');
// set the first tab as active
$phpSpreadsheetObject->setActiveSheetIndex(0);
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header("Content-Disposition: attachment;filename=Spreadsheet.xlsx");
header('Cache-Control: max-age=0');
$objWriter = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($phpSpreadsheetObject);
$objWriter->setIncludeCharts(true);
$objWriter->save('php://output');
?>
Setup:
$ composer i
When I visit the url:
http://localhost/index.php
the following Excel file is downloaded:
where you have 2 sheets: { Graph, Data }. The Graph is genreated based on the data on sheet: Data.
So far so good.
My Problem is: When I upgrade:
"phpoffice/phpspreadsheet": "1.10.0" -> "phpoffice/phpspreadsheet": "1.10.1"
(just patch update)
and hit the same url again, I get the following errors when trying to open the generated Excel file:
We found a problem with some content in 'Spreadsheet (1).xlsx'. Do you want us to try to recover as much as we can? if you trust the source of this workbook, click Yes.
and then this other error:
and the graph is not shown.
Any idea what modification I need to do on my code above to get rid of those errors and get the graph rendered?
Thanks!
I found the solution.
On the code above, just replace: 0, // displayBlanksAs -> 'gap', // displayBlanksAs.
On latest versions like: "phpoffice/phpspreadsheet": "1.16", there is a constant defined for this: DataSeries::EMPTY_AS_GAP, // displayBlanksAs.
That constant is not present on earlier versions like: 1.10.1.
Thanks!

PHPExcel and Office 2016

I'm currently using PHPExcel (v1.8.0) to generate an xlxs file with datas and graph. But when I open my generated document in office I get an error. I've tried to precise the orientation with the "setPlotDirection" method, and delete the y axis label.
This is the code :
$categories = array(
new \PHPExcel_Chart_DataSeriesValues('String', "'".$this->oSheet->getTitle()."'!\$A$5:\$A$31", null, 1)
);
$values = array(
new \PHPExcel_Chart_DataSeriesValues('Number', "'".$this->oSheet->getTitle()."'!\$BA$5:\$BA$31", null, 1),
);
$this->createChart($categories, $values, 'Title', 'Xlegend', 'YLegend', 'A34', 'K50');
function createChart($categories, $values, $title, $xLegnd, $yLegend, $topLeft, $bottomRight)
{
$series = new \PHPExcel_Chart_DataSeries(
\PHPExcel_Chart_DataSeries::TYPE_BARCHART,
\PHPExcel_Chart_DataSeries::GROUPING_STANDARD,
range(0, count($values) - 1),
null,
$categories,
$values
);
$series->setPlotDirection(PHPExcel_Chart_DataSeries::DIRECTION_VERTICAL);
$layout = new PHPExcel_Chart_Layout();
$layout->setShowVal(true);
$plotArea = new \PHPExcel_Chart_PlotArea(null, array($series));
$title = new \PHPExcel_Chart_Title($title);
$yAxisLabel = new \PHPExcel_Chart_Title($yLegend);
$xAxisLabel = new \PHPExcel_Chart_Title($xLegnd);
$chart = new \PHPExcel_Chart(
$title,
$title,
null,
$plotArea,
true,
0,
null,
$yAxisLabel
);
$chart->setTopLeftPosition($topLeft);
$chart->setBottomRightPosition($bottomRight);
$this->oSheet->addChart($chart);
}
All works fine with Excel 2010/2011 and Libre Office. But I get this error with Excel 2016 :
<recoveryLog>
<logFileName>error066200_01.xml</logFileName>
<summary>Des erreurs ont été détectées dans le fichier xxx</summary>
<removedParts>
<removedPart>Partie supprimée: /xl/drawings/drawing3.xml partie. (Forme de dessin)</removedPart>
</removedParts>
</recoveryLog>
Any idea ?
Thanks

Chart not rendered properly

I have a small table
and want to create a TYPE_DONUTCHART with the PHPExcel library. I took the example code from the Examples/33chartcreate-pie.php file and adapted it accordingly:
$dataseriesLabels2 = array(
//new PHPExcel_Chart_DataSeriesValues('String', 'Summen!$A$1', NULL, 1),
);
$xAxisTickValues2 = array(
new PHPExcel_Chart_DataSeriesValues('String', 'Summen!$B$1:$C$1', NULL, 2),
);
$dataSeriesValues2 = array(
new PHPExcel_Chart_DataSeriesValues('Number', 'Summen!$B$2:$C$2', NULL, 2),
);
The file is served by the script, but when I try to open it, an error message occurs:
We found a problem with some content in "Myfile.xlsx". Do you want us to try to recover as much as we can? If you trust the source of this workbook, click Yes.
When I click Yes and activate editing on the document, I get the following message like this:
Excel has finished the check and repair of your file. Some parts of this file have been repaired or discarded.
Removed part: Part /xl/drawings/drawing4.xml. (drawing form)
What's wrong in my code? What do I have to modify?
Notes:
As you can see, I'm using another dataset than in the example. There's only data in the range A1:C2 (see screenshot above).
The worksheets name is Summen, I double checked, that the problem is not a typo of the sheet.
It's been asked in the comments, I have already tried removing the comment from the dataseriesLabels2 entry.
My example
$objPHPExcel = new PHPExcel();
$objWorksheet = $objPHPExcel->getActiveSheet();
$objWorksheet->fromArray(
array(
array('Whatever', 'Cat1', 'Cat2'),
array(77, 247, 128),
)
);
$objWorksheet->setTitle('Summen');
$dataSeriesLabels1 = array(
// new PHPExcel_Chart_DataSeriesValues('String', 'Summen!$A$1', NULL, 1),
);
$xAxisTickValues1 = array(
new PHPExcel_Chart_DataSeriesValues('String', 'Summen!$B$1:$C$1', NULL, 2),
);
$dataSeriesValues1 = array(
new PHPExcel_Chart_DataSeriesValues('Number', 'Summen!$B$2:$C$2', NULL, 2),
);
$series1 = new PHPExcel_Chart_DataSeries(
PHPExcel_Chart_DataSeries::TYPE_DONUTCHART, // plotType
NULL, // plotGrouping
range(0, count($dataSeriesValues1)-1), // plotOrder
$dataSeriesLabels1, // plotLabel
$xAxisTickValues1, // plotCategory
$dataSeriesValues1 // plotValues
);
$layout1 = new PHPExcel_Chart_Layout();
$layout1->setShowVal(TRUE);
$layout1->setShowPercent(TRUE);
$plotArea1 = new PHPExcel_Chart_PlotArea($layout1, array($series1));
$legend1 = new PHPExcel_Chart_Legend(PHPExcel_Chart_Legend::POSITION_RIGHT, NULL, false);
$title1 = new PHPExcel_Chart_Title('Test Pie Chart');
$chart1 = new PHPExcel_Chart(
'chart1', // name
$title1, // title
$legend1, // legend
$plotArea1, // plotArea
true, // plotVisibleOnly
0, // displayBlanksAs
NULL, // xAxisLabel
NULL // yAxisLabel - Pie charts don't have a Y-Axis
);
// Set the position where the chart should appear in the worksheet
$chart1->setTopLeftPosition('A7');
$chart1->setBottomRightPosition('H20');
// Add the chart to the worksheet
$objWorksheet->addChart($chart1);
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
$objWriter->setIncludeCharts(TRUE);
$objWriter->save(str_replace('.php', '.xlsx', __FILE__));
generates correctly without any errors

create separate pdf files in yii

I am trying to create 2 separate pdf files with different content. I have the following code which creates the 2 files.
$invoice = Invoices::model()->findByPk($_GET['id']);
$invoicedetail = InvoicesDetail::model()->findAll(array("condition"=>"invoiceno=".$invoice->INVOICENO . " && biditems_id is null"));
$client = $invoice->pROJ->oRDERNO->addressesinvoices;
$invoices = Invoices::model()->findAll(array("condition"=>"PROJID=".$invoice->PROJID . " && INVOICENO != ".$invoice->INVOICENO));
/*$this->renderPartial("partialInvoice",
array(
"invoice"=>$invoice,
"client"=>$client,
//"lookupprocesstotalArr"=>$lookupprocesstotalArr,
"adjustments"=>$invoices,
"invoicedetail"=>$invoicedetail[0]
),true);
*/
$html = $this->renderPartial("partialInvoice",
array(
"invoice"=>$invoice,
"client"=>$client,
//"lookupprocesstotalArr"=>$lookupprocesstotalArr,
"adjustments"=>$invoices,
"invoicedetail"=>$invoicedetail[0]),true);
$filename_invoice = 'invoice_'.$invoice->INVOICENO.'.pdf';
Yii::import('application.extensions.pdfable.WkHtmlToPdf');
$pdf = new WkHtmlToPdf(array(
'no-outline', // Make Chrome not complain
'margin-top' => 10,
'margin-right' => 10,
'margin-bottom' => 10,
'margin-left' => 10,
));
$pdf->setOptions(array(
'orientation' => 'portrait'
));
// Add a HTML file, a HTML string or a page from a URL
$pdf->addPage($html);
// Save the PDF
$pdf->saveAs("/tmp/$filename_invoice");
// ... or send to client as file download
/*if(!$pdf->send($filename)){
throw new Exception('Could not create PDF: '.$pdf->getError());
}*/
$globalController = new GlobalController;
//$project = Projects::model()->findAll(array("condition"=>"PROJID=".$_GET['PROJID']));
//$projid = $_GET['PROJID'];
$pdfDetail = new WkHtmlToPdf();
$orientation = "Landscape";
$pdfDetail->setOptions(array('orientation'=>$orientation,
'no-outline', // Make Chrome not complain
'margin-right' => 10,
'margin-bottom' => 5,
'margin-left' => 10,
));
//$this->layout = 'pdf';
//get list of process stages for this project
$lastrun = $globalController->lastRun($invoice->PROJID);
$processstages = ExtHtml::listData($lastrun, 'ID',array('cumulative','ITEM'));
$lookupprocesstotalArr = $globalController->lookupprocesstotal($invoice->PROJID,$processstages);
// get all lines
$connection = Yii::app()->db;
$sql = "SELECT jobs.JOBNO, jobs.NAME, FSP, LSP, SHOTS, KM
FROM hdb.jobs
join detailsseismic on jobs.JOBNO = detailsseismic.JOBNO
where jobs.PROJID = :pid";
$command=$connection->createCommand($sql);
$command->bindValue(":pid",$invoice->PROJID,PDO::PARAM_INT);
$command->execute();
$lines = $command->queryAll();
$dates = array();
foreach ($lines as $arr) {
$sql = "SELECT jobsprocesscomplete.datedone, ITEM_fk
FROM hdb.jobsprocesscomplete
left join lookupprocess on jobsprocesscomplete.lookupprocess_id = lookupprocess.ID
left join biditems on lookupprocess.biditems_id = biditems.ITEMID
where jobno = :jobno";
$command=$connection->createCommand($sql);
$command->bindValue(":jobno",$arr['JOBNO'],PDO::PARAM_INT);
$command->execute();
$dates[$arr['JOBNO']] = $command->queryAll();
}
/*echo $this->renderPartial('pdf_'.$_GET['type'],array(
'lookupprocesstotalArr'=>$lookupprocesstotalArr,
"currency"=>$project[0]->currency0->currency,
"projectpercent"=>$project[0]->PERCENT,
"name"=>$project[0]->PROJECT . " - " . $project[0]->oRDERNO->cONTACTsugar->first_name . " " . $project[0]->oRDERNO->cONTACTsugar->last_name,
"lines"=>$lines,
"dates"=>$dates,
"project"=>$project[0]));
*/
$detail = $this->renderPartial('/lookupprocess/pdf_detail',array(
'lookupprocesstotalArr'=>$lookupprocesstotalArr,
"currency"=>$invoice->pROJ->currency0->currency,
"projectpercent"=>$invoice->pROJ->PERCENT,
"name"=>$invoice->pROJ->PROJECT . " - " . $invoice->pROJ->oRDERNO->cONTACTsugar->first_name . " " . $invoice->pROJ->oRDERNO->cONTACTsugar->last_name,
"lines"=>$lines,
"dates"=>$dates,
"project"=>$invoice->pROJ
),true);
$filename_detail = $invoice->PROJID . "_" . date("ymd-his") . ".pdf";
// Add a HTML file, a HTML string or a page from a URL
$pdfDetail->addPage($detail);
// Save the PDF
$pdfDetail->saveAs("/tmp/$filename_detail");
shell_exec("gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=/tmp/$filename_invoice /tmp/$filename_invoice /tmp/$filename_detail");
echo "gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=/tmp/$filename_invoice /tmp/$filename_invoice /tmp/$filename_detail";
exit();
/*
// ... or send to client as file download
if(!$pdf->send($filename_invoice)){
throw new Exception('Could not create PDF: '.$pdf->getError());
}
The problem i have is the 1st pdf that gets created is overwritten by the content of the 2nd pdf which makes no sense.
I do not know the class you are using but this code seems suspicios:
-sOutputFile=/tmp/$filename_invoice /tmp/$filename_invoice /tmp/$filename_detail
For me it seems that you convert 2 files into 1 output files, so logically you overwrite it!?

Database Query during Dynamic Drupal node creation to get Entity ID

I have a script which grabs the latest image from our surf webcam, it saves a node of type Photos. In that content type i have an entity field which references the swell size (content type swell_data). I can add a static entity id no problem:
// Reference Swell Data ID 176821
$node->field_buoy_ref[$node->language][0]['target_id'] = 176821;
How would I dynamically find the latest node created by the swell_data content type (added hourly using a CRON job) and use that value instead? For your reference this is the current script i am using (thanks to FooNinja)
define('DRUPAL_ROOT', getcwd());
require_once 'includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
$bodytext = "This was the view from the Camera at ".date('gA')." on ".date('l F jS Y');
$node = new stdClass(); // Create a new node object
$node->type = "photos"; // Or page, or whatever content type you like
node_object_prepare($node);
$node->title = "Caswell Camera ". date('D M j G:i');
$node->language = LANGUAGE_NONE;
// Author
$node->name = "Gower Live"; // UID of the author of the node; or use $node->name
// Publish Node
$node->status = 1;
//Set Body Text
$node->body[$node->language][0]['value'] = $bodytext;
$node->body[$node->language][0]['summary'] = text_summary($bodytext);
$node->body[$node->language][0]['format'] = 'filtered_html';
//Set Image Location (Caswell)
$node->field_photo_location[$node->language][0]['lat'] = 51.570195982718374;
$node->field_photo_location[$node->language][0]['lng'] = -4.030849980797484;
// Set the Photos date
$node->field_image_date[$node->language][0]['value'] = date("Y-m-d H:i:s");
// From the Webcam
$node->field_webcam_photo[$node->language][0]['value'] = "Caswell";
// Reference Wave Buoy
$node->field_buoy_ref[$node->language][0]['target_id'] = 176821; // I'd this value dynamically pulled from the DB
// Attach Latest Webcam Image
$file_path = drupal_realpath('images/webcams/caswell-webcam.jpg');
$file = (object) array(
'uid' => 1,
'uri' => $file_path,
'filemime' => file_get_mimetype($file_path),
'status' => 1,
);
// You can specify a subdirectory, e.g. public://foo/
$file = file_copy($file, 'public://');
$node->field_user_photo[$node->language][0] = (array) $file;
$path = 'node_created_on' . date('YmdHis');
$node->path = array('alias' => $path);
if($node = node_submit($node)) { // Prepare node for saving
node_save($node);
echo "Node with nid " . $node->nid . " saved!\n";
}
thanks
Lee
I added the following below the Bootstrap Line and bingo.
$result = db_query("SELECT nid, title, created FROM {node} WHERE type = :type AND title =:title", array(
':type' => 'wavebuoy_data',
':title' => "Buoy ". date('D M j gA'),
));
foreach($result as $row) {
$wave_buoy_ref_value = $row->nid;
//print_r($row);
}
Then when building the node, added the following:
// Reference Wave Buoy
$node->field_buoy_ref[$node->language][0]['target_id'] = $wave_buoy_ref_value;
Lee

Categories