From 056ff5e676fcac97f48354ee2075a991de442fe1 Mon Sep 17 00:00:00 2001 From: Ivan Lanin Date: Tue, 10 Jun 2014 07:11:32 +0700 Subject: [PATCH 1/3] Basic chart support --- samples/Sample_32_Chart.php | 26 ++ src/PhpWord/Collection/Charts.php | 27 ++ src/PhpWord/Element/AbstractContainer.php | 4 +- src/PhpWord/Element/AbstractElement.php | 2 +- src/PhpWord/Element/Chart.php | 129 +++++++ src/PhpWord/Element/Footnote.php | 2 +- src/PhpWord/Element/Title.php | 2 +- src/PhpWord/PhpWord.php | 6 +- src/PhpWord/Shared/XMLWriter.php | 23 ++ src/PhpWord/Writer/Word2007.php | 36 ++ src/PhpWord/Writer/Word2007/Element/Chart.php | 79 +++++ src/PhpWord/Writer/Word2007/Part/Chart.php | 331 ++++++++++++++++++ .../Writer/Word2007/Part/ContentTypes.php | 7 +- .../Tests/Writer/Word2007/ElementTest.php | 2 +- 14 files changed, 668 insertions(+), 8 deletions(-) create mode 100644 samples/Sample_32_Chart.php create mode 100644 src/PhpWord/Collection/Charts.php create mode 100644 src/PhpWord/Element/Chart.php create mode 100644 src/PhpWord/Writer/Word2007/Element/Chart.php create mode 100644 src/PhpWord/Writer/Word2007/Part/Chart.php diff --git a/samples/Sample_32_Chart.php b/samples/Sample_32_Chart.php new file mode 100644 index 00000000..52c19f3c --- /dev/null +++ b/samples/Sample_32_Chart.php @@ -0,0 +1,26 @@ +addSection(array('colsNum' => 2)); +$phpWord->addTitleStyle(1, array('size' => 14, 'bold' => true), array('keepNext' => true, 'spaceBefore' => 240)); + +$charts = array('pie', 'doughnut', 'line', 'area', 'scatter', 'bar', 'radar'); +$labels = array('A', 'B', 'C', 'D', 'E'); +$data = array(1, 3, 2, 5, 4); + +foreach ($charts as $chart) { + $section->addTitle(ucfirst($chart), 1); + $section->addChart($chart, $labels, $data); + $section->addTextBreak(); +} + +// Save file +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; +} diff --git a/src/PhpWord/Collection/Charts.php b/src/PhpWord/Collection/Charts.php new file mode 100644 index 00000000..cfccee2e --- /dev/null +++ b/src/PhpWord/Collection/Charts.php @@ -0,0 +1,27 @@ + array('Section'), 'TOC' => array('Section'), 'PageBreak' => array('Section'), + 'Chart' => array('Section'), ); // Special condition, e.g. preservetext can only exists in cell when // the cell is located in header or footer diff --git a/src/PhpWord/Element/AbstractElement.php b/src/PhpWord/Element/AbstractElement.php index fe133fdd..3fd2e4d0 100644 --- a/src/PhpWord/Element/AbstractElement.php +++ b/src/PhpWord/Element/AbstractElement.php @@ -109,7 +109,7 @@ abstract class AbstractElement protected $mediaRelation = false; /** - * Is part of collection; true for Title, Footnote, and Endnote + * Is part of collection; true for Title, Footnote, Endnote, and Chart * * @var bool */ diff --git a/src/PhpWord/Element/Chart.php b/src/PhpWord/Element/Chart.php new file mode 100644 index 00000000..46b7b784 --- /dev/null +++ b/src/PhpWord/Element/Chart.php @@ -0,0 +1,129 @@ +setType($type); + $this->setLabels($labels); + $this->setData($data); + } + + /** + * Get type + * + * @return array + */ + public function getType() + { + return $this->type; + } + + /** + * Set type + * + * @param array $value + */ + public function setType($value) + { + $enum = array('pie', 'doughnut', 'line', 'bar', 'area', 'radar', 'scatter'); + $this->type = $this->setEnumVal($value, $enum, 'pie'); + } + + /** + * Get labels + * + * @return array + */ + public function getLabels() + { + return $this->labels; + } + + /** + * Set labels + * + * @param array $value + */ + public function setLabels($value) + { + $this->labels = $value; + } + + /** + * Get data + * + * @return array + */ + public function getData() + { + return $this->data; + } + + /** + * Set data + * + * @param array $value + */ + public function setData($value) + { + $this->data = $value; + } +} diff --git a/src/PhpWord/Element/Footnote.php b/src/PhpWord/Element/Footnote.php index e263933c..162a703e 100644 --- a/src/PhpWord/Element/Footnote.php +++ b/src/PhpWord/Element/Footnote.php @@ -37,7 +37,7 @@ class Footnote extends AbstractContainer protected $paragraphStyle; /** - * Is part of collection; true for Title, Footnote, and Endnote + * Is part of collection * * @var bool */ diff --git a/src/PhpWord/Element/Title.php b/src/PhpWord/Element/Title.php index a4faeb70..8d385845 100644 --- a/src/PhpWord/Element/Title.php +++ b/src/PhpWord/Element/Title.php @@ -47,7 +47,7 @@ class Title extends AbstractElement private $style; /** - * Is part of collection; true for Title, Footnote, and Endnote + * Is part of collection * * @var bool */ diff --git a/src/PhpWord/PhpWord.php b/src/PhpWord/PhpWord.php index de9d8433..d4c8df9c 100644 --- a/src/PhpWord/PhpWord.php +++ b/src/PhpWord/PhpWord.php @@ -26,9 +26,11 @@ use PhpOffice\PhpWord\Exception\Exception; * @method Collection\Titles getTitles() * @method Collection\Footnotes getFootnotes() * @method Collection\Endnotes getEndnotes() + * @method Collection\Charts getCharts() * @method int addTitle(Element\Title $title) * @method int addFootnote(Element\Footnote $footnote) * @method int addEndnote(Element\Endnote $endnote) + * @method int addChart(Element\Chart $chart) * * @method Style\Paragraph addParagraphStyle(string $styleName, array $styles) * @method Style\Font addFontStyle(string $styleName, mixed $fontStyle, mixed $paragraphStyle = null) @@ -80,7 +82,7 @@ class PhpWord { $this->documentProperties = new DocumentProperties(); - $collections = array('Titles', 'Footnotes', 'Endnotes'); + $collections = array('Titles', 'Footnotes', 'Endnotes', 'Charts'); foreach ($collections as $collection) { $class = 'PhpOffice\\PhpWord\\Collection\\' . $collection; $this->collections[$collection] = new $class(); @@ -108,7 +110,7 @@ class PhpWord $addCollection = array(); $addStyle = array(); - $collections = array('Title', 'Footnote', 'Endnote'); + $collections = array('Title', 'Footnote', 'Endnote', 'Chart'); foreach ($collections as $collection) { $getCollection[] = strtolower("get{$collection}s"); $addCollection[] = strtolower("add{$collection}"); diff --git a/src/PhpWord/Shared/XMLWriter.php b/src/PhpWord/Shared/XMLWriter.php index cb00c70b..b2057bb9 100644 --- a/src/PhpWord/Shared/XMLWriter.php +++ b/src/PhpWord/Shared/XMLWriter.php @@ -139,6 +139,29 @@ class XMLWriter } } + /** + * Write simple element and attribute(s) block + * + * There are two options: + * 1. If the `$attributes` is an array, then it's an associative array of attributes + * 2. If not, then it's a simple attribute-value pair + * + * @param string $element + * @param string|array $attributes + * @param string $value + */ + public function writeBlock($element, $attributes, $value = null) + { + $this->xmlWriter->startElement($element); + if (!is_array($attributes)) { + $attributes = array($attributes => $value); + } + foreach ($attributes as $attribute => $value) { + $this->xmlWriter->writeAttribute($attribute, $value); + } + $this->xmlWriter->endElement(); + } + /** * Write element if ... * diff --git a/src/PhpWord/Writer/Word2007.php b/src/PhpWord/Writer/Word2007.php index 751b58b6..d15f40ac 100644 --- a/src/PhpWord/Writer/Word2007.php +++ b/src/PhpWord/Writer/Word2007.php @@ -71,6 +71,7 @@ class Word2007 extends AbstractWriter implements WriterInterface 'Footer' => '', 'Footnotes' => '', 'Endnotes' => '', + 'Chart' => '', ); foreach (array_keys($this->parts) as $partName) { $partClass = get_class($this) . '\\Part\\' . $partName; @@ -127,6 +128,7 @@ class Word2007 extends AbstractWriter implements WriterInterface $this->addNotes($zip, $rId, 'footnote'); $this->addNotes($zip, $rId, 'endnote'); + $this->addChart($zip, $rId); // Write parts foreach ($this->parts as $partName => $fileName) { @@ -249,6 +251,40 @@ class Word2007 extends AbstractWriter implements WriterInterface } } + /** + * Add chart + * + * @param \PhpOffice\PhpWord\Shared\ZipArchive $zip + * @param integer $rId + */ + private function addChart(ZipArchive $zip, &$rId) + { + $phpWord = $this->getPhpWord(); + + $collection = $phpWord->getCharts(); + $index = 0; + if ($collection->countItems() > 0) { + foreach ($collection->getItems() as $chart) { + $index++; + $rId++; + $filename = "charts/chart{$index}.xml"; + + // ContentTypes.xml + $this->contentTypes['override']["/word/{$filename}"] = 'chart'; + + // word/_rels/document.xml.rel + $this->relationships[] = array('target' => $filename, 'type' => 'chart', 'rID' => $rId); + + // word/charts/chartN.xml + /** @var \PhpOffice\PhpWord\Element\Chart $chart */ + $chart->setRelationId($rId); + $writerPart = $this->getWriterPart('Chart'); + $writerPart->setElement($chart); + $zip->addFromString("word/{$filename}", $writerPart->write()); + } + } + } + /** * Register content types for each media * diff --git a/src/PhpWord/Writer/Word2007/Element/Chart.php b/src/PhpWord/Writer/Word2007/Element/Chart.php new file mode 100644 index 00000000..3c4e43f2 --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Element/Chart.php @@ -0,0 +1,79 @@ +getXmlWriter(); + $element = $this->getElement(); + if (!$element instanceof ChartElement) { + return; + } + + if (!$this->withoutP) { + $xmlWriter->startElement('w:p'); + } + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:drawing'); + $xmlWriter->startElement('wp:inline'); + + // EMU + $xmlWriter->startElement('wp:extent'); + $xmlWriter->writeAttribute('cx', '2000000'); + $xmlWriter->writeAttribute('cy', '2000000'); + $xmlWriter->endElement(); // wp:extent + + $xmlWriter->startElement('wp:docPr'); + $xmlWriter->writeAttribute('id', $element->getRelationId()); + $xmlWriter->writeAttribute('name', 'Chart'. $element->getRelationId()); + $xmlWriter->endElement(); // wp:docPr + + $xmlWriter->startElement('a:graphic'); + $xmlWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + $xmlWriter->startElement('a:graphicData'); + $xmlWriter->writeAttribute('uri', 'http://schemas.openxmlformats.org/drawingml/2006/chart'); + + $xmlWriter->startElement('c:chart'); + $xmlWriter->writeAttribute('r:id', 'rId' . $element->getRelationId()); + $xmlWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart'); + $xmlWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $xmlWriter->endElement(); // c:chart + + $xmlWriter->endElement(); // a:graphicData + $xmlWriter->endElement(); // a:graphic + + $xmlWriter->endElement(); // wp:inline + $xmlWriter->endElement(); // w:drawing + $xmlWriter->endElement(); // w:r + + $this->endElementP(); // w:p + } +} diff --git a/src/PhpWord/Writer/Word2007/Part/Chart.php b/src/PhpWord/Writer/Word2007/Part/Chart.php new file mode 100644 index 00000000..13525fe7 --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Part/Chart.php @@ -0,0 +1,331 @@ +element = $element; + } + + /** + * Write part + * + * @return string + */ + public function write() + { + $xmlWriter = $this->getXmlWriter(); + + $xmlWriter->startDocument('1.0', 'UTF-8', 'yes'); + $xmlWriter->startElement('c:chartSpace'); + $xmlWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart'); + $xmlWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + $xmlWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + + $xmlWriter->writeBlock('c:roundedCorners', 'val', '0'); + + $xmlWriter->startElement('c:chart'); + $this->writePlotArea($xmlWriter); + $xmlWriter->endElement(); // c:chart + + $xmlWriter->endElement(); // c:chartSpace + + return $xmlWriter->getData(); + } + + /** + * Write plot area + */ + private function writePlotArea(XMLWriter $xmlWriter) + { + $xmlWriter->startElement('c:plotArea'); + + $method = "write{$this->element->getType()}Chart"; + $this->$method($xmlWriter); + + $xmlWriter->endElement(); // c:plotArea + } + + /** + * Write pie chart + * + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_PieChart.html + */ + private function writePieChart(XMLWriter $xmlWriter) + { + $xmlWriter->startElement('c:pieChart'); + + $xmlWriter->writeBlock('c:varyColors', 'val', '1'); + + $this->writeSeries($xmlWriter); + + $xmlWriter->endElement(); // c:pie3DChart + } + + /** + * Write doughnut chart + * + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_DoughnutChart.html + */ + private function writeDoughnutChart(XMLWriter $xmlWriter) + { + $xmlWriter->startElement('c:doughnutChart'); + + $xmlWriter->writeBlock('c:varyColors', 'val', '1'); + $xmlWriter->writeBlock('c:holeSize', 'val', '75'); + + $this->writeSeries($xmlWriter); + + $xmlWriter->endElement(); // c:doughnutChart + } + + /** + * Write bar chart + * + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_BarChart.html + */ + private function writeBarChart(XMLWriter $xmlWriter) + { + $xmlWriter->startElement('c:barChart'); + + $xmlWriter->writeBlock('c:varyColors', 'val', '0'); + $xmlWriter->writeBlock('c:barDir', 'val', 'col'); // bar|col + $xmlWriter->writeBlock('c:axId', 'val', '1'); + $xmlWriter->writeBlock('c:axId', 'val', '2'); + + $this->writeSeries($xmlWriter); + + $xmlWriter->endElement(); // c:barChart + + // Axes + $this->writeAxis($xmlWriter, 'cat'); + $this->writeAxis($xmlWriter, 'val'); + } + + /** + * Write line chart + * + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_LineChart.html + */ + private function writeLineChart(XMLWriter $xmlWriter) + { + $xmlWriter->startElement('c:lineChart'); + + $xmlWriter->writeBlock('c:varyColors', 'val', '0'); + $xmlWriter->writeBlock('c:axId', 'val', '1'); + $xmlWriter->writeBlock('c:axId', 'val', '2'); + + $this->writeSeries($xmlWriter); + + $xmlWriter->endElement(); // c:lineChart + + // Axes + $this->writeAxis($xmlWriter, 'cat'); + $this->writeAxis($xmlWriter, 'val'); + } + + /** + * Write area chart + * + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_AreaChart.html + */ + private function writeAreaChart(XMLWriter $xmlWriter) + { + $xmlWriter->startElement('c:areaChart'); + + $xmlWriter->writeBlock('c:varyColors', 'val', '0'); + $xmlWriter->writeBlock('c:axId', 'val', '1'); + $xmlWriter->writeBlock('c:axId', 'val', '2'); + + $this->writeSeries($xmlWriter); + + $xmlWriter->endElement(); // c:areaChart + + // Axes + $this->writeAxis($xmlWriter, 'cat'); + $this->writeAxis($xmlWriter, 'val'); + } + + /** + * Write radar chart + * + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_RadarChart.html + */ + private function writeRadarChart(XMLWriter $xmlWriter) + { + $xmlWriter->startElement('c:radarChart'); + + $xmlWriter->writeBlock('c:varyColors', 'val', '0'); + $xmlWriter->writeBlock('c:radarStyle', 'val', 'standard'); + $xmlWriter->writeBlock('c:axId', 'val', '1'); + $xmlWriter->writeBlock('c:axId', 'val', '2'); + + $this->writeSeries($xmlWriter); + + $xmlWriter->endElement(); // c:radarChart + + // Axes + $this->writeAxis($xmlWriter, 'cat'); + $this->writeAxis($xmlWriter, 'val'); + } + + /** + * Write scatter chart + * + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_ScatterChart.html + */ + private function writeScatterChart(XMLWriter $xmlWriter) + { + $xmlWriter->startElement('c:scatterChart'); + + $xmlWriter->writeBlock('c:varyColors', 'val', '0'); + $xmlWriter->writeBlock('c:scatterStyle', 'val', 'lineMarker'); + $xmlWriter->writeBlock('c:axId', 'val', '1'); + $xmlWriter->writeBlock('c:axId', 'val', '2'); + + $this->writeSeries($xmlWriter, true); + + $xmlWriter->endElement(); // c:scatterChart + + // Axes + $this->writeAxis($xmlWriter, 'cat'); + $this->writeAxis($xmlWriter, 'val'); + } + + /** + * Write series + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param bool $scatter + */ + private function writeSeries(XMLWriter $xmlWriter, $scatter = false) + { + $xmlWriter->startElement('c:ser'); + + $xmlWriter->writeBlock('c:idx', 'val', '0'); + $xmlWriter->writeBlock('c:order', 'val', '0'); + + if ($scatter === true) { + $this->writeSeriesItems($xmlWriter, 'xVal', $this->element->getLabels()); + $this->writeSeriesItems($xmlWriter, 'yVal', $this->element->getData()); + } else { + $this->writeSeriesItems($xmlWriter, 'cat', $this->element->getLabels()); + $this->writeSeriesItems($xmlWriter, 'val', $this->element->getData()); + } + + $xmlWriter->endElement(); // c:ser + } + + /** + * Write series items + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param string $type + * @param array $values + */ + private function writeSeriesItems(XMLWriter $xmlWriter, $type, $values) + { + $types = array( + 'cat' => array('c:cat', 'c:strRef', 'c:strCache'), + 'val' => array('c:val', 'c:numRef', 'c:numCache'), + 'xVal' => array('c:xVal', 'c:strRef', 'c:strCache'), + 'yVal' => array('c:yVal', 'c:numRef', 'c:numCache'), + ); + list($itemType, $itemRef, $itemCache) = $types[$type]; + + $xmlWriter->startElement($itemType); + $xmlWriter->startElement($itemRef); + $xmlWriter->startElement($itemCache); + + $index = 0; + foreach ($values as $value) { + $xmlWriter->startElement('c:pt'); + $xmlWriter->writeAttribute('idx', $index); + + $xmlWriter->startElement('c:v'); + $xmlWriter->writeRaw($value); + $xmlWriter->endElement(); // c:v + + $xmlWriter->endElement(); // c:pt + $index++; + } + + $xmlWriter->endElement(); // $itemCache + + $xmlWriter->endElement(); // $itemRef + $xmlWriter->endElement(); // $itemType + } + + /** + * Write axis + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param string $type + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_CatAx.html + */ + private function writeAxis(XMLWriter $xmlWriter, $type) + { + $types = array( + 'cat' => array('c:catAx', '1', 'b', '2'), + 'val' => array('c:valAx', '2', 'l', '1'), + ); + list($axisType, $axisId, $axisPos, $axisCross) = $types[$type]; + + $xmlWriter->startElement($axisType); + + $xmlWriter->writeBlock('c:axId', 'val', $axisId); + $xmlWriter->writeBlock('c:axPos', 'val', $axisPos); + $xmlWriter->writeBlock('c:crossAx', 'val', $axisCross); + + $xmlWriter->startElement('c:scaling'); + $xmlWriter->writeBlock('c:orientation', 'val', 'minMax'); + $xmlWriter->endElement(); // c:scaling + + $xmlWriter->startElement('c:spPr'); + $xmlWriter->writeElement('a:noFill'); + $xmlWriter->startElement('a:ln'); + $xmlWriter->startElement('a:solidFill'); + $xmlWriter->writeBlock('a:srgbClr', 'val', '0FB7'); + $xmlWriter->endElement(); // a:solidFill + $xmlWriter->endElement(); // a:ln + $xmlWriter->endElement(); // c:crossAx + + $xmlWriter->endElement(); // $type + } +} diff --git a/src/PhpWord/Writer/Word2007/Part/ContentTypes.php b/src/PhpWord/Writer/Word2007/Part/ContentTypes.php index b6f23f47..ac636d3a 100644 --- a/src/PhpWord/Writer/Word2007/Part/ContentTypes.php +++ b/src/PhpWord/Writer/Word2007/Part/ContentTypes.php @@ -37,6 +37,7 @@ class ContentTypes extends AbstractPart $openXMLPrefix = 'application/vnd.openxmlformats-'; $wordMLPrefix = $openXMLPrefix . 'officedocument.wordprocessingml.'; + $drawingMLPrefix = $openXMLPrefix . 'officedocument.drawingml.'; $overrides = array( '/docProps/core.xml' => $openXMLPrefix . 'package.core-properties+xml', '/docProps/app.xml' => $openXMLPrefix . 'officedocument.extended-properties+xml', @@ -53,7 +54,11 @@ class ContentTypes extends AbstractPart $defaults = $contentTypes['default']; if (!empty($contentTypes['override'])) { foreach ($contentTypes['override'] as $key => $val) { - $overrides[$key] = $wordMLPrefix . $val . '+xml'; + if ($val == 'chart') { + $overrides[$key] = $drawingMLPrefix . $val . '+xml'; + } else { + $overrides[$key] = $wordMLPrefix . $val . '+xml'; + } } } diff --git a/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php b/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php index c774e9f3..2e76df12 100644 --- a/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php +++ b/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php @@ -41,7 +41,7 @@ class ElementTest extends \PHPUnit_Framework_TestCase $elements = array( 'CheckBox', 'Container', 'Footnote', 'Image', 'Link', 'ListItem', 'ListItemRun', 'Object', 'PreserveText', 'Table', 'Text', 'TextBox', 'TextBreak', 'Title', 'TOC', - 'Field', 'Line', 'Shape' + 'Field', 'Line', 'Shape', 'Chart' ); foreach ($elements as $element) { $objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Element\\' . $element; From a93b686ca3826cf83dcec6a46a5dac2a10c9be4e Mon Sep 17 00:00:00 2001 From: Ivan Lanin Date: Wed, 11 Jun 2014 23:04:13 +0700 Subject: [PATCH 2/3] Chart improvements --- src/PhpWord/Element/Chart.php | 4 +- src/PhpWord/Writer/Word2007/Element/Chart.php | 15 +- src/PhpWord/Writer/Word2007/Part/Chart.php | 315 ++++++++---------- 3 files changed, 152 insertions(+), 182 deletions(-) diff --git a/src/PhpWord/Element/Chart.php b/src/PhpWord/Element/Chart.php index 46b7b784..5a93b827 100644 --- a/src/PhpWord/Element/Chart.php +++ b/src/PhpWord/Element/Chart.php @@ -69,7 +69,7 @@ class Chart extends AbstractElement /** * Get type * - * @return array + * @return string */ public function getType() { @@ -79,7 +79,7 @@ class Chart extends AbstractElement /** * Set type * - * @param array $value + * @param string $value */ public function setType($value) { diff --git a/src/PhpWord/Writer/Word2007/Element/Chart.php b/src/PhpWord/Writer/Word2007/Element/Chart.php index 3c4e43f2..454ebce5 100644 --- a/src/PhpWord/Writer/Word2007/Element/Chart.php +++ b/src/PhpWord/Writer/Word2007/Element/Chart.php @@ -37,6 +37,8 @@ class Chart extends AbstractElement return; } + $rId = $element->getRelationId(); + if (!$this->withoutP) { $xmlWriter->startElement('w:p'); } @@ -46,15 +48,8 @@ class Chart extends AbstractElement $xmlWriter->startElement('wp:inline'); // EMU - $xmlWriter->startElement('wp:extent'); - $xmlWriter->writeAttribute('cx', '2000000'); - $xmlWriter->writeAttribute('cy', '2000000'); - $xmlWriter->endElement(); // wp:extent - - $xmlWriter->startElement('wp:docPr'); - $xmlWriter->writeAttribute('id', $element->getRelationId()); - $xmlWriter->writeAttribute('name', 'Chart'. $element->getRelationId()); - $xmlWriter->endElement(); // wp:docPr + $xmlWriter->writeBlock('wp:extent', array('cx' => '2000000', 'cy' => '2000000')); + $xmlWriter->writeBlock('wp:docPr', array('id' => $rId, 'name' => "Chart{$rId}")); $xmlWriter->startElement('a:graphic'); $xmlWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); @@ -62,7 +57,7 @@ class Chart extends AbstractElement $xmlWriter->writeAttribute('uri', 'http://schemas.openxmlformats.org/drawingml/2006/chart'); $xmlWriter->startElement('c:chart'); - $xmlWriter->writeAttribute('r:id', 'rId' . $element->getRelationId()); + $xmlWriter->writeAttribute('r:id', "rId{$rId}"); $xmlWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart'); $xmlWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); $xmlWriter->endElement(); // c:chart diff --git a/src/PhpWord/Writer/Word2007/Part/Chart.php b/src/PhpWord/Writer/Word2007/Part/Chart.php index 13525fe7..8d922013 100644 --- a/src/PhpWord/Writer/Word2007/Part/Chart.php +++ b/src/PhpWord/Writer/Word2007/Part/Chart.php @@ -24,6 +24,7 @@ use PhpOffice\PhpWord\Element\Chart as ChartElement; * Word2007 chart part writer: word/charts/chartx.xml * * @since 0.12.0 + * @link http://www.datypic.com/sc/ooxml/e-draw-chart_chartSpace.html * @SuppressWarnings(PHPMD.UnusedPrivateMethod) */ class Chart extends AbstractPart @@ -35,6 +36,18 @@ class Chart extends AbstractPart */ private $element; + private $types = array( + 'pie' => array('type' => 'pieChart', 'colors' => 1), + 'doughnut' => array('type' => 'doughnutChart', 'colors' => 1, 'hole' => 75), + 'bar' => array('type' => 'barChart', 'colors' => 0, 'axes' => true, 'bar' => 'col'), + 'line' => array('type' => 'lineChart', 'colors' => 0, 'axes' => true), + 'area' => array('type' => 'areaChart', 'colors' => 0, 'axes' => true), + 'radar' => array('type' => 'radarChart', 'colors' => 0, 'axes' => true, 'radar' => 'standard'), + 'scatter' => array('type' => 'scatterChart', 'colors' => 0, 'axes' => true, 'scatter' => 'marker'), + ); + + private $options = array(); + /** * Set chart element */ @@ -58,176 +71,92 @@ class Chart extends AbstractPart $xmlWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); $xmlWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); - $xmlWriter->writeBlock('c:roundedCorners', 'val', '0'); + $xmlWriter->writeBlock('c:date1904', 'val', 1); + $xmlWriter->writeBlock('c:lang', 'val', 'en-US'); + $xmlWriter->writeBlock('c:roundedCorners', 'val', 0); + + $this->writeChart($xmlWriter); + $this->writeShape($xmlWriter); - $xmlWriter->startElement('c:chart'); - $this->writePlotArea($xmlWriter); - $xmlWriter->endElement(); // c:chart $xmlWriter->endElement(); // c:chartSpace return $xmlWriter->getData(); } + /** + * Write chart + * + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_Chart.html + */ + private function writeChart(XMLWriter $xmlWriter) + { + $xmlWriter->startElement('c:chart'); + + $xmlWriter->writeBlock('c:autoTitleDeleted', 'val', 1); + $xmlWriter->writeBlock('c:dispBlanksAs', 'val', 'zero'); + + $this->writePlotArea($xmlWriter); + // $this->writeLegend($xmlWriter); + + $xmlWriter->endElement(); // c:chart + } + /** * Write plot area + * + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_PlotArea.html + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_PieChart.html + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_DoughnutChart.html + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_BarChart.html + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_LineChart.html + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_AreaChart.html + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_RadarChart.html + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_ScatterChart.html */ private function writePlotArea(XMLWriter $xmlWriter) { - $xmlWriter->startElement('c:plotArea'); + $type = $this->element->getType(); + $this->options = $this->types[$type]; - $method = "write{$this->element->getType()}Chart"; - $this->$method($xmlWriter); + $xmlWriter->startElement('c:plotArea'); + $xmlWriter->writeElement('c:layout'); + + // Chart + $xmlWriter->startElement('c:' . $this->options['type']); + + $xmlWriter->writeBlock('c:varyColors', 'val', $this->options['colors']); + if (isset($this->options['hole'])) { + $xmlWriter->writeBlock('c:holeSize', 'val', $this->options['hole']); + } + if (isset($this->options['bar'])) { + $xmlWriter->writeBlock('c:barDir', 'val', $this->options['bar']); // bar|col + } + if (isset($this->options['radar'])) { + $xmlWriter->writeBlock('c:radarStyle', 'val', $this->options['radar']); + } + if (isset($this->options['scatter'])) { + $xmlWriter->writeBlock('c:scatterStyle', 'val', $this->options['scatter']); + } + if (isset($this->options['axes'])) { + $xmlWriter->writeBlock('c:axId', 'val', 1); + $xmlWriter->writeBlock('c:axId', 'val', 2); + } + + // Series + $this->writeSeries($xmlWriter, isset($this->options['scatter'])); + + $xmlWriter->endElement(); // chart type + + // Axes + if (isset($this->options['axes'])) { + $this->writeAxis($xmlWriter, 'cat'); + $this->writeAxis($xmlWriter, 'val'); + } $xmlWriter->endElement(); // c:plotArea } - /** - * Write pie chart - * - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_PieChart.html - */ - private function writePieChart(XMLWriter $xmlWriter) - { - $xmlWriter->startElement('c:pieChart'); - - $xmlWriter->writeBlock('c:varyColors', 'val', '1'); - - $this->writeSeries($xmlWriter); - - $xmlWriter->endElement(); // c:pie3DChart - } - - /** - * Write doughnut chart - * - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_DoughnutChart.html - */ - private function writeDoughnutChart(XMLWriter $xmlWriter) - { - $xmlWriter->startElement('c:doughnutChart'); - - $xmlWriter->writeBlock('c:varyColors', 'val', '1'); - $xmlWriter->writeBlock('c:holeSize', 'val', '75'); - - $this->writeSeries($xmlWriter); - - $xmlWriter->endElement(); // c:doughnutChart - } - - /** - * Write bar chart - * - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_BarChart.html - */ - private function writeBarChart(XMLWriter $xmlWriter) - { - $xmlWriter->startElement('c:barChart'); - - $xmlWriter->writeBlock('c:varyColors', 'val', '0'); - $xmlWriter->writeBlock('c:barDir', 'val', 'col'); // bar|col - $xmlWriter->writeBlock('c:axId', 'val', '1'); - $xmlWriter->writeBlock('c:axId', 'val', '2'); - - $this->writeSeries($xmlWriter); - - $xmlWriter->endElement(); // c:barChart - - // Axes - $this->writeAxis($xmlWriter, 'cat'); - $this->writeAxis($xmlWriter, 'val'); - } - - /** - * Write line chart - * - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_LineChart.html - */ - private function writeLineChart(XMLWriter $xmlWriter) - { - $xmlWriter->startElement('c:lineChart'); - - $xmlWriter->writeBlock('c:varyColors', 'val', '0'); - $xmlWriter->writeBlock('c:axId', 'val', '1'); - $xmlWriter->writeBlock('c:axId', 'val', '2'); - - $this->writeSeries($xmlWriter); - - $xmlWriter->endElement(); // c:lineChart - - // Axes - $this->writeAxis($xmlWriter, 'cat'); - $this->writeAxis($xmlWriter, 'val'); - } - - /** - * Write area chart - * - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_AreaChart.html - */ - private function writeAreaChart(XMLWriter $xmlWriter) - { - $xmlWriter->startElement('c:areaChart'); - - $xmlWriter->writeBlock('c:varyColors', 'val', '0'); - $xmlWriter->writeBlock('c:axId', 'val', '1'); - $xmlWriter->writeBlock('c:axId', 'val', '2'); - - $this->writeSeries($xmlWriter); - - $xmlWriter->endElement(); // c:areaChart - - // Axes - $this->writeAxis($xmlWriter, 'cat'); - $this->writeAxis($xmlWriter, 'val'); - } - - /** - * Write radar chart - * - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_RadarChart.html - */ - private function writeRadarChart(XMLWriter $xmlWriter) - { - $xmlWriter->startElement('c:radarChart'); - - $xmlWriter->writeBlock('c:varyColors', 'val', '0'); - $xmlWriter->writeBlock('c:radarStyle', 'val', 'standard'); - $xmlWriter->writeBlock('c:axId', 'val', '1'); - $xmlWriter->writeBlock('c:axId', 'val', '2'); - - $this->writeSeries($xmlWriter); - - $xmlWriter->endElement(); // c:radarChart - - // Axes - $this->writeAxis($xmlWriter, 'cat'); - $this->writeAxis($xmlWriter, 'val'); - } - - /** - * Write scatter chart - * - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_ScatterChart.html - */ - private function writeScatterChart(XMLWriter $xmlWriter) - { - $xmlWriter->startElement('c:scatterChart'); - - $xmlWriter->writeBlock('c:varyColors', 'val', '0'); - $xmlWriter->writeBlock('c:scatterStyle', 'val', 'lineMarker'); - $xmlWriter->writeBlock('c:axId', 'val', '1'); - $xmlWriter->writeBlock('c:axId', 'val', '2'); - - $this->writeSeries($xmlWriter, true); - - $xmlWriter->endElement(); // c:scatterChart - - // Axes - $this->writeAxis($xmlWriter, 'cat'); - $this->writeAxis($xmlWriter, 'val'); - } - /** * Write series * @@ -238,8 +167,16 @@ class Chart extends AbstractPart { $xmlWriter->startElement('c:ser'); - $xmlWriter->writeBlock('c:idx', 'val', '0'); - $xmlWriter->writeBlock('c:order', 'val', '0'); + $xmlWriter->writeBlock('c:idx', 'val', 0); + $xmlWriter->writeBlock('c:order', 'val', 0); + + if (isset($this->options['scatter'])) { + $xmlWriter->startElement('c:spPr'); + $xmlWriter->startElement('a:ln'); + $xmlWriter->writeElement('a:noFill'); + $xmlWriter->endElement(); // a:ln + $xmlWriter->endElement(); // c:spPr + } if ($scatter === true) { $this->writeSeriesItems($xmlWriter, 'xVal', $this->element->getLabels()); @@ -262,16 +199,15 @@ class Chart extends AbstractPart private function writeSeriesItems(XMLWriter $xmlWriter, $type, $values) { $types = array( - 'cat' => array('c:cat', 'c:strRef', 'c:strCache'), - 'val' => array('c:val', 'c:numRef', 'c:numCache'), - 'xVal' => array('c:xVal', 'c:strRef', 'c:strCache'), - 'yVal' => array('c:yVal', 'c:numRef', 'c:numCache'), + 'cat' => array('c:cat', 'c:strLit'), + 'val' => array('c:val', 'c:numLit'), + 'xVal' => array('c:xVal', 'c:strLit'), + 'yVal' => array('c:yVal', 'c:numLit'), ); - list($itemType, $itemRef, $itemCache) = $types[$type]; + list($itemType, $itemLit) = $types[$type]; $xmlWriter->startElement($itemType); - $xmlWriter->startElement($itemRef); - $xmlWriter->startElement($itemCache); + $xmlWriter->startElement($itemLit); $index = 0; foreach ($values as $value) { @@ -286,9 +222,7 @@ class Chart extends AbstractPart $index++; } - $xmlWriter->endElement(); // $itemCache - - $xmlWriter->endElement(); // $itemRef + $xmlWriter->endElement(); // $itemLit $xmlWriter->endElement(); // $itemType } @@ -302,8 +236,8 @@ class Chart extends AbstractPart private function writeAxis(XMLWriter $xmlWriter, $type) { $types = array( - 'cat' => array('c:catAx', '1', 'b', '2'), - 'val' => array('c:valAx', '2', 'l', '1'), + 'cat' => array('c:catAx', 1, 'b', 2), + 'val' => array('c:valAx', 2, 'l', 1), ); list($axisType, $axisId, $axisPos, $axisCross) = $types[$type]; @@ -312,6 +246,18 @@ class Chart extends AbstractPart $xmlWriter->writeBlock('c:axId', 'val', $axisId); $xmlWriter->writeBlock('c:axPos', 'val', $axisPos); $xmlWriter->writeBlock('c:crossAx', 'val', $axisCross); + $xmlWriter->writeBlock('c:auto', 'val', 1); + + if (isset($this->options['axes'])) { + $xmlWriter->writeBlock('c:delete', 'val', 0); + $xmlWriter->writeBlock('c:majorTickMark', 'val', 'none'); + $xmlWriter->writeBlock('c:minorTickMark', 'val', 'none'); + $xmlWriter->writeBlock('c:tickLblPos', 'val', 'none'); // nextTo + // $xmlWriter->writeBlock('c:crosses', 'val', 'autoZero'); + } + if (isset($this->options['radar'])) { + $xmlWriter->writeElement('c:majorGridlines'); + } $xmlWriter->startElement('c:scaling'); $xmlWriter->writeBlock('c:orientation', 'val', 'minMax'); @@ -319,13 +265,42 @@ class Chart extends AbstractPart $xmlWriter->startElement('c:spPr'); $xmlWriter->writeElement('a:noFill'); + $xmlWriter->startElement('a:ln'); $xmlWriter->startElement('a:solidFill'); - $xmlWriter->writeBlock('a:srgbClr', 'val', '0FB7'); + // $xmlWriter->writeBlock('a:srgbClr', 'val', '0FF000'); $xmlWriter->endElement(); // a:solidFill $xmlWriter->endElement(); // a:ln - $xmlWriter->endElement(); // c:crossAx - $xmlWriter->endElement(); // $type + $xmlWriter->endElement(); // c:spPr + + $xmlWriter->endElement(); // $axisType + } + + /** + * Write legend + * + * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_Legend.html + */ + private function writeLegend(XMLWriter $xmlWriter) + { + $xmlWriter->startElement('c:legend'); + $xmlWriter->writeElement('c:layout'); + $xmlWriter->writeBlock('c:legendPos', 'val', 'r'); + $xmlWriter->endElement(); // c:legend + } + + /** + * Write shape + * + * @link http://www.datypic.com/sc/ooxml/t-a_CT_ShapeProperties.html + */ + private function writeShape(XMLWriter $xmlWriter) + { + $xmlWriter->startElement('c:spPr'); + $xmlWriter->startElement('a:ln'); + $xmlWriter->writeElement('a:noFill'); + $xmlWriter->endElement(); // a:ln + $xmlWriter->endElement(); // c:spPr } } From 3fef19093c9deaf8bdcb024bfad056451c0e0bdd Mon Sep 17 00:00:00 2001 From: Ivan Lanin Date: Thu, 12 Jun 2014 18:15:00 +0700 Subject: [PATCH 3/3] #278: Basic chart --- CHANGELOG.md | 1 + README.md | 2 + docs/elements.rst | 7 + docs/intro.rst | 2 + docs/src/documentation.md | 8 + samples/Sample_32_Chart.php | 22 ++- src/PhpWord/Element/Chart.php | 67 +++------ src/PhpWord/Shared/XMLWriter.php | 2 +- src/PhpWord/Writer/Word2007/Element/Chart.php | 4 +- src/PhpWord/Writer/Word2007/Part/Chart.php | 142 +++++++++--------- tests/PhpWord/Tests/Writer/RTFTest.php | 2 +- .../Tests/Writer/Word2007/ElementTest.php | 26 ++++ 12 files changed, 156 insertions(+), 129 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebfd3263..302cbf98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This release added drawing shapes (arc, curve, line, polyline, rect, oval) eleme - RTF Writer: Support for sections, margins, and borders - @ivanlanin GH-249 - Section: Ability to set paper size, e.g. A4, A3, and Legal - @ivanlanin GH-249 - General: New `PhpWord::save()` method to encapsulate `IOFactory` - @ivanlanin +- Element: Basic 2D charts (pie, doughnut, bar, line, area, scatter, radar) - @ivanlanin GH-278 ### Bugfixes diff --git a/README.md b/README.md index 97e5b642..5a432cba 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,8 @@ With PHPWord, you can create DOCX, ODT, or RTF documents dynamically using your - Insert list items as bulleted, numbered, or multilevel - Insert hyperlinks - Insert footnotes and endnotes +- Insert drawing shapes (arc, curve, line, polyline, rect, oval) +- Insert charts (pie, doughnut, bar, line, area, scatter, radar) - Create document from templates - Use XSL 1.0 style sheets to transform main document part of OOXML template - ... and many more features on progress diff --git a/docs/elements.rst b/docs/elements.rst index a01c9fd4..ee827326 100644 --- a/docs/elements.rst +++ b/docs/elements.rst @@ -49,6 +49,8 @@ column shows the containers while the rows lists the elements. +-------+-----------------+-----------+----------+----------+---------+------------+------------+ | 20 | Shape | v | v | v | v | v | v | +-------+-----------------+-----------+----------+----------+---------+------------+------------+ +| 21 | Chart | v | - | - | - | - | - | ++-------+-----------------+-----------+----------+----------+---------+------------+------------+ Legend: @@ -399,3 +401,8 @@ Shapes ------ To be completed. + +Charts +------ + +To be completed. diff --git a/docs/intro.rst b/docs/intro.rst index 5045d4dc..241b581c 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -45,6 +45,8 @@ Features - Insert list items as bulleted, numbered, or multilevel - Insert hyperlinks - Insert footnotes and endnotes +- Insert drawing shapes (arc, curve, line, polyline, rect, oval) +- Insert charts (pie, doughnut, bar, line, area, scatter, radar) - Create document from templates - Use XSL 1.0 style sheets to transform main document part of OOXML template diff --git a/docs/src/documentation.md b/docs/src/documentation.md index 49505c7f..0b21f634 100644 --- a/docs/src/documentation.md +++ b/docs/src/documentation.md @@ -37,6 +37,7 @@ Don't forget to change `code::` directive to `code-block::` in the resulting rst - [Fields](#fields) - [Lines](#lines) - [Shapes](#shapes) + - [Charts](#charts) - [Styles](#styles) - [Section](#section) - [Font](#font) @@ -76,6 +77,8 @@ PHPWord is an open source project licensed under the terms of [LGPL version 3](h - Insert list items as bulleted, numbered, or multilevel - Insert hyperlinks - Insert footnotes and endnotes +- Insert drawing shapes (arc, curve, line, polyline, rect, oval) +- Insert charts (pie, doughnut, bar, line, area, scatter, radar) - Create document from templates - Use XSL 1.0 style sheets to transform main document part of OOXML template - ... and many more features on progress @@ -440,6 +443,7 @@ Below are the matrix of element availability in each container. The column shows | 18 | Field | v | v | v | v | v | v | | 19 | Line | v | v | v | v | v | v | | 20 | Shape | v | v | v | v | v | v | +| 21 | Chart | v | - | - | - | - | - | Legend: @@ -737,6 +741,10 @@ To be completed. To be completed. +## Charts + +To be completed. + # Styles ## Section diff --git a/samples/Sample_32_Chart.php b/samples/Sample_32_Chart.php index 52c19f3c..26d6c420 100644 --- a/samples/Sample_32_Chart.php +++ b/samples/Sample_32_Chart.php @@ -9,13 +9,23 @@ $phpWord = new \PhpOffice\PhpWord\PhpWord(); $section = $phpWord->addSection(array('colsNum' => 2)); $phpWord->addTitleStyle(1, array('size' => 14, 'bold' => true), array('keepNext' => true, 'spaceBefore' => 240)); -$charts = array('pie', 'doughnut', 'line', 'area', 'scatter', 'bar', 'radar'); -$labels = array('A', 'B', 'C', 'D', 'E'); -$data = array(1, 3, 2, 5, 4); +$chartTypes = array('pie', 'doughnut', 'bar', 'line', 'area', 'scatter', 'radar'); +$twoSeries = array('bar', 'line', 'area', 'scatter', 'radar'); +$threeSeries = array('bar', 'line'); +$categories = array('A', 'B', 'C', 'D', 'E'); +$series1 = array(1, 3, 2, 5, 4); +$series2 = array(3, 1, 7, 2, 6); +$series3 = array(8, 3, 2, 5, 4); -foreach ($charts as $chart) { - $section->addTitle(ucfirst($chart), 1); - $section->addChart($chart, $labels, $data); +foreach ($chartTypes as $chartType) { + $section->addTitle(ucfirst($chartType), 1); + $chart = $section->addChart($chartType, $categories, $series1); + if (in_array($chartType, $twoSeries)) { + $chart->addSeries($categories, $series2); + } + if (in_array($chartType, $threeSeries)) { + $chart->addSeries($categories, $series3); + } $section->addTextBreak(); } diff --git a/src/PhpWord/Element/Chart.php b/src/PhpWord/Element/Chart.php index 5a93b827..24f2bb78 100644 --- a/src/PhpWord/Element/Chart.php +++ b/src/PhpWord/Element/Chart.php @@ -39,31 +39,23 @@ class Chart extends AbstractElement private $type = 'pie'; /** - * Labels + * Series * * @var array */ - private $labels = array(); - - /** - * Data - * - * @var array - */ - private $data = array(); + private $series = array(); /** * Create new instance * * @param string $type - * @param array $labels - * @param array $data + * @param array $categories + * @param array $values */ - public function __construct($type, $labels, $data) + public function __construct($type, $categories, $values) { $this->setType($type); - $this->setLabels($labels); - $this->setData($data); + $this->addSeries($categories, $values); } /** @@ -88,42 +80,23 @@ class Chart extends AbstractElement } /** - * Get labels + * Add series + * + * @param array $categories + * @param array $values + */ + public function addSeries($categories, $values) + { + $this->series[] = array('categories' => $categories, 'values' => $values); + } + + /** + * Get series * * @return array */ - public function getLabels() + public function getSeries() { - return $this->labels; - } - - /** - * Set labels - * - * @param array $value - */ - public function setLabels($value) - { - $this->labels = $value; - } - - /** - * Get data - * - * @return array - */ - public function getData() - { - return $this->data; - } - - /** - * Set data - * - * @param array $value - */ - public function setData($value) - { - $this->data = $value; + return $this->series; } } diff --git a/src/PhpWord/Shared/XMLWriter.php b/src/PhpWord/Shared/XMLWriter.php index b2057bb9..8691ae1d 100644 --- a/src/PhpWord/Shared/XMLWriter.php +++ b/src/PhpWord/Shared/XMLWriter.php @@ -150,7 +150,7 @@ class XMLWriter * @param string|array $attributes * @param string $value */ - public function writeBlock($element, $attributes, $value = null) + public function writeElementBlock($element, $attributes, $value = null) { $this->xmlWriter->startElement($element); if (!is_array($attributes)) { diff --git a/src/PhpWord/Writer/Word2007/Element/Chart.php b/src/PhpWord/Writer/Word2007/Element/Chart.php index 454ebce5..4330e2fe 100644 --- a/src/PhpWord/Writer/Word2007/Element/Chart.php +++ b/src/PhpWord/Writer/Word2007/Element/Chart.php @@ -48,8 +48,8 @@ class Chart extends AbstractElement $xmlWriter->startElement('wp:inline'); // EMU - $xmlWriter->writeBlock('wp:extent', array('cx' => '2000000', 'cy' => '2000000')); - $xmlWriter->writeBlock('wp:docPr', array('id' => $rId, 'name' => "Chart{$rId}")); + $xmlWriter->writeElementBlock('wp:extent', array('cx' => '2000000', 'cy' => '2000000')); + $xmlWriter->writeElementBlock('wp:docPr', array('id' => $rId, 'name' => "Chart{$rId}")); $xmlWriter->startElement('a:graphic'); $xmlWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); diff --git a/src/PhpWord/Writer/Word2007/Part/Chart.php b/src/PhpWord/Writer/Word2007/Part/Chart.php index 8d922013..66e43914 100644 --- a/src/PhpWord/Writer/Word2007/Part/Chart.php +++ b/src/PhpWord/Writer/Word2007/Part/Chart.php @@ -25,7 +25,6 @@ use PhpOffice\PhpWord\Element\Chart as ChartElement; * * @since 0.12.0 * @link http://www.datypic.com/sc/ooxml/e-draw-chart_chartSpace.html - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) */ class Chart extends AbstractPart { @@ -36,6 +35,11 @@ class Chart extends AbstractPart */ private $element; + /** + * Type definition + * + * @var array + */ private $types = array( 'pie' => array('type' => 'pieChart', 'colors' => 1), 'doughnut' => array('type' => 'doughnutChart', 'colors' => 1, 'hole' => 75), @@ -46,6 +50,11 @@ class Chart extends AbstractPart 'scatter' => array('type' => 'scatterChart', 'colors' => 0, 'axes' => true, 'scatter' => 'marker'), ); + /** + * Chart options + * + * @var array + */ private $options = array(); /** @@ -71,14 +80,9 @@ class Chart extends AbstractPart $xmlWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); $xmlWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); - $xmlWriter->writeBlock('c:date1904', 'val', 1); - $xmlWriter->writeBlock('c:lang', 'val', 'en-US'); - $xmlWriter->writeBlock('c:roundedCorners', 'val', 0); - $this->writeChart($xmlWriter); $this->writeShape($xmlWriter); - $xmlWriter->endElement(); // c:chartSpace return $xmlWriter->getData(); @@ -93,11 +97,9 @@ class Chart extends AbstractPart { $xmlWriter->startElement('c:chart'); - $xmlWriter->writeBlock('c:autoTitleDeleted', 'val', 1); - $xmlWriter->writeBlock('c:dispBlanksAs', 'val', 'zero'); + $xmlWriter->writeElementBlock('c:autoTitleDeleted', 'val', 1); $this->writePlotArea($xmlWriter); - // $this->writeLegend($xmlWriter); $xmlWriter->endElement(); // c:chart } @@ -125,27 +127,33 @@ class Chart extends AbstractPart // Chart $xmlWriter->startElement('c:' . $this->options['type']); - $xmlWriter->writeBlock('c:varyColors', 'val', $this->options['colors']); + $xmlWriter->writeElementBlock('c:varyColors', 'val', $this->options['colors']); + if ($type == 'area') { + $xmlWriter->writeElementBlock('c:grouping', 'val', 'standard'); + } if (isset($this->options['hole'])) { - $xmlWriter->writeBlock('c:holeSize', 'val', $this->options['hole']); + $xmlWriter->writeElementBlock('c:holeSize', 'val', $this->options['hole']); } if (isset($this->options['bar'])) { - $xmlWriter->writeBlock('c:barDir', 'val', $this->options['bar']); // bar|col + $xmlWriter->writeElementBlock('c:barDir', 'val', $this->options['bar']); // bar|col + $xmlWriter->writeElementBlock('c:grouping', 'val', 'clustered'); } if (isset($this->options['radar'])) { - $xmlWriter->writeBlock('c:radarStyle', 'val', $this->options['radar']); + $xmlWriter->writeElementBlock('c:radarStyle', 'val', $this->options['radar']); } if (isset($this->options['scatter'])) { - $xmlWriter->writeBlock('c:scatterStyle', 'val', $this->options['scatter']); - } - if (isset($this->options['axes'])) { - $xmlWriter->writeBlock('c:axId', 'val', 1); - $xmlWriter->writeBlock('c:axId', 'val', 2); + $xmlWriter->writeElementBlock('c:scatterStyle', 'val', $this->options['scatter']); } // Series $this->writeSeries($xmlWriter, isset($this->options['scatter'])); + // Axes + if (isset($this->options['axes'])) { + $xmlWriter->writeElementBlock('c:axId', 'val', 1); + $xmlWriter->writeElementBlock('c:axId', 'val', 2); + } + $xmlWriter->endElement(); // chart type // Axes @@ -165,28 +173,34 @@ class Chart extends AbstractPart */ private function writeSeries(XMLWriter $xmlWriter, $scatter = false) { - $xmlWriter->startElement('c:ser'); + $series = $this->element->getSeries(); - $xmlWriter->writeBlock('c:idx', 'val', 0); - $xmlWriter->writeBlock('c:order', 'val', 0); + $index = 0; + foreach ($series as $seriesItem) { + $categories = $seriesItem['categories']; + $values = $seriesItem['values']; - if (isset($this->options['scatter'])) { - $xmlWriter->startElement('c:spPr'); - $xmlWriter->startElement('a:ln'); - $xmlWriter->writeElement('a:noFill'); - $xmlWriter->endElement(); // a:ln - $xmlWriter->endElement(); // c:spPr + $xmlWriter->startElement('c:ser'); + + $xmlWriter->writeElementBlock('c:idx', 'val', $index); + $xmlWriter->writeElementBlock('c:order', 'val', $index); + + if (isset($this->options['scatter'])) { + $this->writeShape($xmlWriter); + } + + if ($scatter === true) { + $this->writeSeriesItem($xmlWriter, 'xVal', $categories); + $this->writeSeriesItem($xmlWriter, 'yVal', $values); + } else { + $this->writeSeriesItem($xmlWriter, 'cat', $categories); + $this->writeSeriesItem($xmlWriter, 'val', $values); + } + + $xmlWriter->endElement(); // c:ser + $index++; } - if ($scatter === true) { - $this->writeSeriesItems($xmlWriter, 'xVal', $this->element->getLabels()); - $this->writeSeriesItems($xmlWriter, 'yVal', $this->element->getData()); - } else { - $this->writeSeriesItems($xmlWriter, 'cat', $this->element->getLabels()); - $this->writeSeriesItems($xmlWriter, 'val', $this->element->getData()); - } - - $xmlWriter->endElement(); // c:ser } /** @@ -196,7 +210,7 @@ class Chart extends AbstractPart * @param string $type * @param array $values */ - private function writeSeriesItems(XMLWriter $xmlWriter, $type, $values) + private function writeSeriesItem(XMLWriter $xmlWriter, $type, $values) { $types = array( 'cat' => array('c:cat', 'c:strLit'), @@ -243,63 +257,47 @@ class Chart extends AbstractPart $xmlWriter->startElement($axisType); - $xmlWriter->writeBlock('c:axId', 'val', $axisId); - $xmlWriter->writeBlock('c:axPos', 'val', $axisPos); - $xmlWriter->writeBlock('c:crossAx', 'val', $axisCross); - $xmlWriter->writeBlock('c:auto', 'val', 1); + $xmlWriter->writeElementBlock('c:axId', 'val', $axisId); + $xmlWriter->writeElementBlock('c:axPos', 'val', $axisPos); + $xmlWriter->writeElementBlock('c:crossAx', 'val', $axisCross); + $xmlWriter->writeElementBlock('c:auto', 'val', 1); if (isset($this->options['axes'])) { - $xmlWriter->writeBlock('c:delete', 'val', 0); - $xmlWriter->writeBlock('c:majorTickMark', 'val', 'none'); - $xmlWriter->writeBlock('c:minorTickMark', 'val', 'none'); - $xmlWriter->writeBlock('c:tickLblPos', 'val', 'none'); // nextTo - // $xmlWriter->writeBlock('c:crosses', 'val', 'autoZero'); + $xmlWriter->writeElementBlock('c:delete', 'val', 0); + $xmlWriter->writeElementBlock('c:majorTickMark', 'val', 'none'); + $xmlWriter->writeElementBlock('c:minorTickMark', 'val', 'none'); + $xmlWriter->writeElementBlock('c:tickLblPos', 'val', 'none'); // nextTo + $xmlWriter->writeElementBlock('c:crosses', 'val', 'autoZero'); } if (isset($this->options['radar'])) { $xmlWriter->writeElement('c:majorGridlines'); } $xmlWriter->startElement('c:scaling'); - $xmlWriter->writeBlock('c:orientation', 'val', 'minMax'); + $xmlWriter->writeElementBlock('c:orientation', 'val', 'minMax'); $xmlWriter->endElement(); // c:scaling - $xmlWriter->startElement('c:spPr'); - $xmlWriter->writeElement('a:noFill'); - - $xmlWriter->startElement('a:ln'); - $xmlWriter->startElement('a:solidFill'); - // $xmlWriter->writeBlock('a:srgbClr', 'val', '0FF000'); - $xmlWriter->endElement(); // a:solidFill - $xmlWriter->endElement(); // a:ln - - $xmlWriter->endElement(); // c:spPr + $this->writeShape($xmlWriter, true); $xmlWriter->endElement(); // $axisType } - /** - * Write legend - * - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_Legend.html - */ - private function writeLegend(XMLWriter $xmlWriter) - { - $xmlWriter->startElement('c:legend'); - $xmlWriter->writeElement('c:layout'); - $xmlWriter->writeBlock('c:legendPos', 'val', 'r'); - $xmlWriter->endElement(); // c:legend - } - /** * Write shape * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param bool $line * @link http://www.datypic.com/sc/ooxml/t-a_CT_ShapeProperties.html */ - private function writeShape(XMLWriter $xmlWriter) + private function writeShape(XMLWriter $xmlWriter, $line = false) { $xmlWriter->startElement('c:spPr'); $xmlWriter->startElement('a:ln'); - $xmlWriter->writeElement('a:noFill'); + if ($line === true) { + $xmlWriter->writeElement('a:solidFill'); + } else { + $xmlWriter->writeElement('a:noFill'); + } $xmlWriter->endElement(); // a:ln $xmlWriter->endElement(); // c:spPr } diff --git a/tests/PhpWord/Tests/Writer/RTFTest.php b/tests/PhpWord/Tests/Writer/RTFTest.php index c1448106..5b983b35 100644 --- a/tests/PhpWord/Tests/Writer/RTFTest.php +++ b/tests/PhpWord/Tests/Writer/RTFTest.php @@ -91,7 +91,7 @@ class RTFTest extends \PHPUnit_Framework_TestCase $this->assertTrue(file_exists($file)); - unlink($file); + @unlink($file); } /** diff --git a/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php b/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php index 2e76df12..0ba29f2f 100644 --- a/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php +++ b/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php @@ -148,4 +148,30 @@ class ElementTest extends \PHPUnit_Framework_TestCase $this->assertTrue($doc->elementExists($path)); } } + + /** + * Test shape elements + */ + public function testChartElement() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $chartTypes = array('pie', 'doughnut', 'bar', 'line', 'area', 'scatter', 'radar'); + $categories = array('A', 'B', 'C', 'D', 'E'); + $series1 = array(1, 3, 2, 5, 4); + foreach ($chartTypes as $chartType) { + $section->addChart($chartType, $categories, $series1); + } + + $doc = TestHelperDOCX::getDocument($phpWord); + + $index = 0; + foreach ($chartTypes as $chartType) { + $index++; + $file = "word/charts/chart{$index}.xml"; + $path = "/c:chartSpace/c:chart/c:plotArea/c:{$chartType}Chart"; + $this->assertTrue($doc->elementExists($path, $file)); + } + } }