diff --git a/CHANGELOG.md b/CHANGELOG.md index d6df064a06..36c27c26e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ This is the changelog between releases of PHPWord. Releases are listed in revers ### Features -None yet. +- Element: Ability to add drawing shapes (arc, curve, line, polyline, rect, oval) using new `Shape` element - @ivanlanin GH-123 ### Bugfixes diff --git a/docs/elements.rst b/docs/elements.rst index 7d07674206..a3a8171721 100644 --- a/docs/elements.rst +++ b/docs/elements.rst @@ -47,6 +47,8 @@ column shows the containers while the rows lists the elements. +-------+-----------------+-----------+----------+----------+---------+------------+------------+ | 19 | Line | v | v | v | v | v | v | +-------+-----------------+-----------+----------+----------+---------+------------+------------+ +| 20 | Shapes | v | v | v | v | v | v | ++-------+-----------------+-----------+----------+----------+---------+------------+------------+ Legend: @@ -490,7 +492,12 @@ Fields To be completed -Line +Lines +----- + +To be completed + +Shapes ------ To be completed diff --git a/docs/intro.rst b/docs/intro.rst index aca5b24153..5045d4dc1d 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -99,11 +99,11 @@ Writers +---------------------------+----------------------+--------+-------+-------+--------+-------+ | | Endnote | ✓ | | | ✓ | | +---------------------------+----------------------+--------+-------+-------+--------+-------+ -| **Graphs** | 2D basic graphs | | | | | | +| **Graphs** | 2D basic graphs | ✓ | | | | | +---------------------------+----------------------+--------+-------+-------+--------+-------+ | | 2D advanced graphs | | | | | | +---------------------------+----------------------+--------+-------+-------+--------+-------+ -| | 3D graphs | | | | | | +| | 3D graphs | ✓ | | | | | +---------------------------+----------------------+--------+-------+-------+--------+-------+ | **Math** | OMML support | | | | | | +---------------------------+----------------------+--------+-------+-------+--------+-------+ diff --git a/docs/src/documentation.md b/docs/src/documentation.md index 84522c1b5f..e971640144 100644 --- a/docs/src/documentation.md +++ b/docs/src/documentation.md @@ -36,6 +36,7 @@ Don't forget to change `code::` directive to `code-block::` in the resulting rst - [Textboxes](#textboxes) - [Fields](#fields) - [Lines](#lines) + - [Shapes](#shapes) - [Templates](#templates) - [Writers & readers](#writers-readers) - [OOXML](#ooxml) @@ -101,9 +102,9 @@ Below are the supported features for each file formats. | | Footer | ✓ | | | | | | | Footnote | ✓ | | | ✓ | | | | Endnote | ✓ | | | ✓ | | -| **Graphs** | 2D basic graphs | | | | | | +| **Graphs** | 2D basic graphs | ✓ | | | | | | | 2D advanced graphs | | | | | | -| | 3D graphs | | | | | | +| | 3D graphs | ✓ | | | | | | **Math** | OMML support | | | | | | | | MathML support | | | | | | | **Bonus** | Encryption | | | | | | @@ -465,6 +466,7 @@ Below are the matrix of element availability in each container. The column shows | 17 | TextBox | v | v | v | v | - | - | | 18 | Field | v | v | v | v | v | v | | 19 | Line | v | v | v | v | v | v | +| 20 | Shape | v | v | v | v | v | v | Legend: @@ -850,6 +852,10 @@ To be completed. To be completed. +## Shapes + +To be completed. + # Templates You can create a docx template with included search-patterns that can be replaced by any value you wish. Only single-line values can be replaced. To load a template file, use the `loadTemplate` method. After loading the docx template, you can use the `setValue` method to change the value of a search pattern. The search-pattern model is: `${search-pattern}`. It is not possible to add new PHPWord elements to a loaded template file. diff --git a/phpmd.xml.dist b/phpmd.xml.dist index f0b62b2d6e..5eb348ecfe 100644 --- a/phpmd.xml.dist +++ b/phpmd.xml.dist @@ -18,7 +18,7 @@ - + diff --git a/samples/Sample_31_Shape.php b/samples/Sample_31_Shape.php new file mode 100644 index 0000000000..02399992e7 --- /dev/null +++ b/samples/Sample_31_Shape.php @@ -0,0 +1,82 @@ +addTitleStyle(1, array('size' => 14, 'bold' => true)); + +$section = $phpWord->addSection(); + +// Arc +$section->addTitle('Arc', 1); +$section->addShape( + 'arc', + array( + 'points' => '-90 20', + 'frame' => array('width' => 120, 'height' => 120), + 'outline' => array('color' => '#333333', 'weight' => 2, 'startArrow' => 'oval', 'endArrow' => 'open'), + ) +); + +// Curve +$section->addTitle('Curve', 1); +$section->addShape( + 'curve', + array( + 'points' => '1,100 200,1 1,50 200,50', 'connector' => 'elbow', + 'outline' => array('color' => '#66cc00', 'weight' => 2, 'dash' => 'dash', 'startArrow' => 'diamond', 'endArrow' => 'block'), + ) +); + +// Line +$section->addTitle('Line', 1); +$section->addShape( + 'line', + array( + 'points' => '1,1 150,30', + 'outline' => array('color' => '#cc00ff', 'line' => 'thickThin', 'weight' => 3, 'startArrow' => 'oval', 'endArrow' => 'classic'), + ) +); + +// Polyline +$section->addTitle('Polyline', 1); +$section->addShape( + 'polyline', + array( + 'points' => '1,30 20,10 55,20 75,10 100,40 115,50, 120,15 200,50', + 'outline' => array('color' => '#cc6666', 'weight' => 2, 'startArrow' => 'none', 'endArrow' => 'classic'), + ) +); + +// Rectangle +$section->addTitle('Rectangle', 1); +$section->addShape( + 'rect', + array( + 'roundness' => 0.2, + 'frame' => array('width' => 100, 'height' => 100, 'left' => 1, 'top' => 1), + 'fill' => array('color' => '#FFCC33'), + 'outline' => array('color' => '#990000', 'weight' => 1), + 'shadow' => array(), + ) +); + +// Oval +$section->addTitle('Oval', 1); +$section->addShape( + 'oval', + array( + 'frame' => array('width' => 100, 'height' => 70, 'left' => 1, 'top' => 1), + 'fill' => array('color' => '#33CC99'), + 'outline' => array('color' => '#333333', 'weight' => 2), + 'extrusion' => array(), + ) +); + +// Save file +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; +} diff --git a/src/PhpWord/Element/AbstractContainer.php b/src/PhpWord/Element/AbstractContainer.php index 4bbe5ddff5..ae750de995 100644 --- a/src/PhpWord/Element/AbstractContainer.php +++ b/src/PhpWord/Element/AbstractContainer.php @@ -39,6 +39,7 @@ * @method TextBox addTextBox($style = null) * @method Field addField($type = null, $properties = array(), $options = array()) * @method Line addLine($lineStyle = null) + * @method Shape addObject($type, $style = null) * * @since 0.10.0 */ @@ -74,7 +75,7 @@ public function __call($function, $args) { $elements = array('Text', 'TextRun', 'Link', 'PreserveText', 'TextBreak', 'ListItem', 'ListItemRun', 'Table', 'Image', 'Object', 'Footnote', - 'Endnote', 'CheckBox', 'TextBox', 'Field', 'Line'); + 'Endnote', 'CheckBox', 'TextBox', 'Field', 'Line', 'Shape'); $functions = array(); for ($i = 0; $i < count($elements); $i++) { $functions[$i] = 'add' . $elements[$i]; @@ -242,6 +243,7 @@ private function checkValidity($method) 'Object' => $allContainers, 'Field' => $allContainers, 'Line' => $allContainers, + 'Shape' => $allContainers, 'TextRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'), 'ListItem' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'), 'ListItemRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'), diff --git a/src/PhpWord/Element/AbstractElement.php b/src/PhpWord/Element/AbstractElement.php index ca45ef23f9..c8e574b7e5 100644 --- a/src/PhpWord/Element/AbstractElement.php +++ b/src/PhpWord/Element/AbstractElement.php @@ -262,4 +262,25 @@ protected function setStyle($styleObject, $styleValue = null, $returnObject = fa return $style; } + + /** + * Set enum value + * + * @param mixed $value + * @param array $enum + * @param mixed $default + * @return mixed + * @throws \InvalidArgumentException + * @todo Merge with the same method in AbstractStyle + */ + protected function setEnumVal($value = null, $enum = array(), $default = null) + { + if ($value != null && trim($value) != '' && !empty($enum) && !in_array($value, $enum)) { + throw new \InvalidArgumentException("Invalid style value: {$value}"); + } elseif ($value === null || trim($value) == '') { + $value = $default; + } + + return $value; + } } diff --git a/src/PhpWord/Element/Shape.php b/src/PhpWord/Element/Shape.php new file mode 100644 index 0000000000..9b3aae4ffe --- /dev/null +++ b/src/PhpWord/Element/Shape.php @@ -0,0 +1,88 @@ +setType($type); + $this->style = $this->setStyle(new ShapeStyle(), $style); + } + + /** + * Get type + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Set pattern + * + * @param string $value + * @return self + */ + public function setType($value = null) + { + $enum = array('arc', 'curve', 'line', 'polyline', 'rect', 'oval'); + $this->type = $this->setEnumVal($value, $enum, null); + + return $this; + } + + /** + * Get shape style + * + * @return \PhpOffice\PhpWord\Style\Shape + */ + public function getStyle() + { + return $this->style; + } +} diff --git a/src/PhpWord/Style/AbstractStyle.php b/src/PhpWord/Style/AbstractStyle.php index 13ef8f9b26..4d3758889d 100644 --- a/src/PhpWord/Style/AbstractStyle.php +++ b/src/PhpWord/Style/AbstractStyle.php @@ -264,7 +264,7 @@ protected function setFloatVal($value, $default = null) protected function setEnumVal($value = null, $enum = array(), $default = null) { if ($value != null && trim($value) != '' && !empty($enum) && !in_array($value, $enum)) { - throw new \InvalidArgumentException('Invalid style value.'); + throw new \InvalidArgumentException("Invalid style value: {$value}"); } elseif ($value === null || trim($value) == '') { $value = $default; } diff --git a/src/PhpWord/Style/Extrusion.php b/src/PhpWord/Style/Extrusion.php new file mode 100644 index 0000000000..ccbb26505b --- /dev/null +++ b/src/PhpWord/Style/Extrusion.php @@ -0,0 +1,106 @@ +setStyleByArray($style); + } + + /** + * Get type + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Set pattern + * + * @param string $value + * @return self + */ + public function setType($value = null) + { + $enum = array(self::EXTRUSION_PARALLEL, self::EXTRUSION_PERSPECTIVE); + $this->type = $this->setEnumVal($value, $enum, null); + + return $this; + } + + /** + * Get color + * + * @return string + */ + public function getColor() + { + return $this->color; + } + + /** + * Set color + * + * @param string $value + * @return self + */ + public function setColor($value = null) + { + $this->color = $value; + + return $this; + } +} diff --git a/src/PhpWord/Style/Fill.php b/src/PhpWord/Style/Fill.php new file mode 100644 index 0000000000..08c7a85739 --- /dev/null +++ b/src/PhpWord/Style/Fill.php @@ -0,0 +1,69 @@ +setStyleByArray($style); + } + + /** + * Get color + * + * @return string + */ + public function getColor() + { + return $this->color; + } + + /** + * Set color + * + * @param string $value + * @return self + */ + public function setColor($value = null) + { + $this->color = $value; + + return $this; + } +} diff --git a/src/PhpWord/Style/Frame.php b/src/PhpWord/Style/Frame.php new file mode 100644 index 0000000000..fce3f4a997 --- /dev/null +++ b/src/PhpWord/Style/Frame.php @@ -0,0 +1,159 @@ +setStyleByArray($style); + } + + /** + * Get width + * + * @return int|float + */ + public function getWidth() + { + return $this->width; + } + + /** + * Set width + * + * @param int|float $value + * @return self + */ + public function setWidth($value = null) + { + $this->width = $this->setNumericVal($value, null); + + return $this; + } + + /** + * Get height + * + * @return int|float + */ + public function getHeight() + { + return $this->height; + } + + /** + * Set height + * + * @param int|float $value + * @return self + */ + public function setHeight($value = null) + { + $this->height = $this->setNumericVal($value, null); + + return $this; + } + + /** + * Get left + * + * @return int|float + */ + public function getLeft() + { + return $this->left; + } + + /** + * Set left + * + * @param int|float $value + * @return self + */ + public function setLeft($value = 0) + { + $this->left = $this->setNumericVal($value, 0); + + return $this; + } + + /** + * Get topmost position + * + * @return int|float + */ + public function getTop() + { + return $this->top; + } + + /** + * Set topmost position + * + * @param int|float $value + * @return self + */ + public function setTop($value = 0) + { + $this->top = $this->setNumericVal($value, 0); + + return $this; + } +} diff --git a/src/PhpWord/Style/Outline.php b/src/PhpWord/Style/Outline.php new file mode 100644 index 0000000000..7e6b19cf90 --- /dev/null +++ b/src/PhpWord/Style/Outline.php @@ -0,0 +1,248 @@ +setStyleByArray($style); + } + + /** + * Get weight + * + * @return int|float + */ + public function getWeight() + { + return $this->weight; + } + + /** + * Set weight + * + * @param int|float $value + * @return self + */ + public function setWeight($value = null) + { + $this->weight = $this->setNumericVal($value, null); + + return $this; + } + + /** + * Get color + * + * @return string + */ + public function getColor() + { + return $this->color; + } + + /** + * Set color + * + * @param string $value + * @return self + */ + public function setColor($value = null) + { + $this->color = $value; + + return $this; + } + + /** + * Get dash type + * + * @return string + */ + public function getDash() + { + return $this->dash; + } + + /** + * Set dash type + * + * @param string $value + * @return self + */ + public function setDash($value = null) + { + $this->dash = $value; + + return $this; + } + + /** + * Get line style + * + * @return string + */ + public function getLine() + { + return $this->line; + } + + /** + * Set line style + * + * @param string $value + * @return self + */ + public function setLine($value = null) + { + $enum = array(self::LINE_SINGLE, self::LINE_THIN_THIN, self::LINE_THIN_THICK, + self::LINE_THICK_THIN, self::LINE_THICK_BETWEEN_THIN); + $this->line = $this->setEnumVal($value, $enum, null); + + return $this; + } + + /** + * Get startArrow + * + * @return string + */ + public function getStartArrow() + { + return $this->startArrow; + } + + /** + * Set pattern + * + * @param string $value + * @return self + */ + public function setStartArrow($value = null) + { + $enum = array(self::ARROW_NONE, self::ARROW_BLOCK, self::ARROW_CLASSIC, + self::ARROW_OVAL, self::ARROW_DIAMOND, self::ARROW_OPEN); + $this->startArrow = $this->setEnumVal($value, $enum, null); + + return $this; + } + + /** + * Get endArrow + * + * @return string + */ + public function getEndArrow() + { + return $this->endArrow; + } + + /** + * Set pattern + * + * @param string $value + * @return self + */ + public function setEndArrow($value = null) + { + $enum = array(self::ARROW_NONE, self::ARROW_BLOCK, self::ARROW_CLASSIC, + self::ARROW_OVAL, self::ARROW_DIAMOND, self::ARROW_OPEN); + $this->endArrow = $this->setEnumVal($value, $enum, null); + + return $this; + } +} diff --git a/src/PhpWord/Style/Shadow.php b/src/PhpWord/Style/Shadow.php new file mode 100644 index 0000000000..deafbff0f8 --- /dev/null +++ b/src/PhpWord/Style/Shadow.php @@ -0,0 +1,97 @@ +setStyleByArray($style); + } + + /** + * Get color + * + * @return string + */ + public function getColor() + { + return $this->color; + } + + /** + * Set color + * + * @param string $value + * @return self + */ + public function setColor($value = null) + { + $this->color = $value; + + return $this; + } + + /** + * Get offset + * + * @return string + */ + public function getOffset() + { + return $this->offset; + } + + /** + * Set offset + * + * @param string $value + * @return self + */ + public function setOffset($value = null) + { + $this->offset = $value; + + return $this; + } +} diff --git a/src/PhpWord/Style/Shape.php b/src/PhpWord/Style/Shape.php new file mode 100644 index 0000000000..c9809920bb --- /dev/null +++ b/src/PhpWord/Style/Shape.php @@ -0,0 +1,255 @@ +setStyleByArray($style); + } + + /** + * Get points + * + * @return string + */ + public function getPoints() + { + return $this->points; + } + + /** + * Set points + * + * @param string $value + * @return self + */ + public function setPoints($value = null) + { + $this->points = $value; + + return $this; + } + + /** + * Get roundness + * + * @return int|float + */ + public function getRoundness() + { + return $this->roundness; + } + + /** + * Set roundness + * + * @param int|float $value + * @return self + */ + public function setRoundness($value = null) + { + $this->roundness = $this->setNumericVal($value, null); + + return $this; + } + + /** + * Get frame + * + * @return \PhpOffice\PhpWord\Style\Frame + */ + public function getFrame() + { + return $this->frame; + } + + /** + * Set frame + * + * @param mixed $value + * @return self + */ + public function setFrame($value = null) + { + $this->setObjectVal($value, 'Frame', $this->frame); + + return $this; + } + + /** + * Get fill + * + * @return \PhpOffice\PhpWord\Style\Fill + */ + public function getFill() + { + return $this->fill; + } + + /** + * Set fill + * + * @param mixed $value + * @return self + */ + public function setFill($value = null) + { + $this->setObjectVal($value, 'Fill', $this->fill); + + return $this; + } + + /** + * Get outline + * + * @return \PhpOffice\PhpWord\Style\Outline + */ + public function getOutline() + { + return $this->outline; + } + + /** + * Set outline + * + * @param mixed $value + * @return self + */ + public function setOutline($value = null) + { + $this->setObjectVal($value, 'Outline', $this->outline); + + return $this; + } + + /** + * Get shadow + * + * @return \PhpOffice\PhpWord\Style\Shadow + */ + public function getShadow() + { + return $this->shadow; + } + + /** + * Set shadow + * + * @param mixed $value + * @return self + */ + public function setShadow($value = null) + { + $this->setObjectVal($value, 'Shadow', $this->shadow); + + return $this; + } + + /** + * Get 3D extrusion + * + * @return \PhpOffice\PhpWord\Style\Extrusion + */ + public function getExtrusion() + { + return $this->extrusion; + } + + /** + * Set 3D extrusion + * + * @param mixed $value + * @return self + */ + public function setExtrusion($value = null) + { + $this->setObjectVal($value, 'Extrusion', $this->extrusion); + + return $this; + } +} diff --git a/src/PhpWord/Writer/HTML/Style/Font.php b/src/PhpWord/Writer/HTML/Style/Font.php index 18f2828751..8645a1f402 100644 --- a/src/PhpWord/Writer/HTML/Style/Font.php +++ b/src/PhpWord/Writer/HTML/Style/Font.php @@ -17,7 +17,6 @@ namespace PhpOffice\PhpWord\Writer\HTML\Style; -use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Style\Font as FontStyle; /** diff --git a/src/PhpWord/Writer/Word2007/Element/Shape.php b/src/PhpWord/Writer/Word2007/Element/Shape.php new file mode 100644 index 0000000000..5590861c8a --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Element/Shape.php @@ -0,0 +1,169 @@ +getXmlWriter(); + $element = $this->getElement(); + if (!$element instanceof ShapeElement) { + return; + } + + $style = $element->getStyle(); + $type = $element->getType(); + if ($type == 'rect' && $style->getRoundness() !== null) { + $type = 'roundrect'; + } + $method = "write{$type}"; + + if (!$this->withoutP) { + $xmlWriter->startElement('w:p'); + } + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:pict'); + $xmlWriter->startElement("v:{$type}"); + + // Element style + if (method_exists($this, $method)) { + $this->$method($xmlWriter, $style); + } + + // Child style + $styleWriter = new ShapeStyleWriter($xmlWriter, $style); + $styleWriter->write(); + + $xmlWriter->endElement(); // v:$type + $xmlWriter->endElement(); // w:pict + $xmlWriter->endElement(); // w:r + + if (!$this->withoutP) { + $xmlWriter->endElement(); // w:p + } + } + + /** + * Write arc + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Style\Shape $style + */ + private function writeArc(XMLWriter $xmlWriter, ShapeStyle $style) + { + $points = $this->getPoints('arc', $style->getPoints()); + + $xmlWriter->writeAttributeIf($points['start'] !== null, 'startAngle', $points['start']); + $xmlWriter->writeAttributeIf($points['end'] !== null, 'endAngle', $points['end']); + } + + /** + * Write curve + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Style\Shape $style + */ + private function writeCurve(XMLWriter $xmlWriter, ShapeStyle $style) + { + $points = $this->getPoints('curve', $style->getPoints()); + + $this->writeLine($xmlWriter, $style); + $xmlWriter->writeAttributeIf($points['point1'] !== null, 'control1', $points['point1']); + $xmlWriter->writeAttributeIf($points['point2'] !== null, 'control2', $points['point2']); + } + + /** + * Write line + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Style\Shape $style + */ + private function writeLine(XMLWriter $xmlWriter, ShapeStyle $style) + { + $points = $this->getPoints('line', $style->getPoints()); + + $xmlWriter->writeAttributeIf($points['start'] !== null, 'from', $points['start']); + $xmlWriter->writeAttributeIf($points['end'] !== null, 'to', $points['end']); + } + + /** + * Write polyline + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Style\Shape $style + */ + private function writePolyline(XMLWriter $xmlWriter, ShapeStyle $style) + { + $xmlWriter->writeAttributeIf($style->getPoints() !== null, 'points', $style->getPoints()); + } + + /** + * Write rectangle + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Style\Shape $style + */ + private function writeRoundRect(XMLWriter $xmlWriter, ShapeStyle $style) + { + $xmlWriter->writeAttribute('arcsize', $style->getRoundness()); + } + + /** + * Set points + * + * @param string $type + * @param string $value + * @return array + */ + private function getPoints($type, $value) + { + $points = array(); + + switch ($type) { + case 'arc': + case 'line': + $points = explode(' ', $value); + @list($start, $end) = $points; + $points = array('start' => $start, 'end' => $end); + break; + case 'curve': + $points = explode(' ', $value); + @list($start, $end, $point1, $point2) = $points; + $points = array('start' => $start, 'end' => $end, 'point1' => $point1, 'point2' => $point2); + break; + } + + return $points; + } +} diff --git a/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php b/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php index 61c8bae808..d91e37b8d2 100644 --- a/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php +++ b/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php @@ -102,4 +102,40 @@ protected function convertTwip($value, $default = 0) return $value * $factor; } + + /** + * Write child style + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param string $name + * @param mixed $value + */ + protected function writeChildStyle(XMLWriter $xmlWriter, $name, $value) + { + if ($value !== null) { + $class = "PhpOffice\\PhpWord\\Writer\\Word2007\\Style\\" . $name; + + /** @var \PhpOffice\PhpWord\Writer\Word2007\Style\AbstractStyle $writer */ + $writer = new $class($xmlWriter, $value); + $writer->write(); + } + } + + /** + * Assemble style array into style string + * + * @param array $styles + * @return string + */ + protected function assembleStyle($styles = array()) + { + $style = ''; + foreach ($styles as $key => $value) { + if (!is_null($value) && $value != '') { + $style .= "{$key}:{$value}; "; + } + } + + return trim($style); + } } diff --git a/src/PhpWord/Writer/Word2007/Style/Extrusion.php b/src/PhpWord/Writer/Word2007/Style/Extrusion.php new file mode 100644 index 0000000000..aa8ba1eadf --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Style/Extrusion.php @@ -0,0 +1,44 @@ +getStyle(); + if (!$style instanceof \PhpOffice\PhpWord\Style\Extrusion) { + return; + } + $xmlWriter = $this->getXmlWriter(); + + $xmlWriter->startElement("o:extrusion"); + $xmlWriter->writeAttribute('on', 't'); + $xmlWriter->writeAttributeIf($style->getType() !== null, 'type', $style->getType()); + $xmlWriter->writeAttributeIf($style->getColor() !== null, 'color', $style->getColor()); + $xmlWriter->endElement(); + } +} diff --git a/src/PhpWord/Writer/Word2007/Style/Fill.php b/src/PhpWord/Writer/Word2007/Style/Fill.php new file mode 100644 index 0000000000..0695f8eff0 --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Style/Fill.php @@ -0,0 +1,41 @@ +getStyle(); + if (!$style instanceof \PhpOffice\PhpWord\Style\Fill) { + return; + } + $xmlWriter = $this->getXmlWriter(); + + $xmlWriter->writeAttribute('on', 't'); + $xmlWriter->writeAttributeIf($style->getColor() !== null, 'fillcolor', $style->getColor()); + } +} diff --git a/src/PhpWord/Writer/Word2007/Style/Frame.php b/src/PhpWord/Writer/Word2007/Style/Frame.php new file mode 100644 index 0000000000..dd2337eec8 --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Style/Frame.php @@ -0,0 +1,56 @@ +getStyle(); + if (!$style instanceof \PhpOffice\PhpWord\Style\Frame) { + return; + } + $xmlWriter = $this->getXmlWriter(); + + $styles = array(); + $properties = array( + 'left' => 'margin-left', + 'top' => 'margin-top', + 'width' => 'width', + 'height' => 'height', + ); + + foreach ($properties as $key => $property) { + $method = "get{$key}"; + $value = $style->$method(); + if ($value !== null) { + $styles[$property] = $style->$method() . 'pt'; + } + } + + $xmlWriter->writeAttribute('style', $this->assembleStyle($styles)); + } +} diff --git a/src/PhpWord/Writer/Word2007/Style/Image.php b/src/PhpWord/Writer/Word2007/Style/Image.php index 280b569a4a..aebc93c114 100644 --- a/src/PhpWord/Writer/Word2007/Style/Image.php +++ b/src/PhpWord/Writer/Word2007/Style/Image.php @@ -152,22 +152,4 @@ protected function getElementStyle(ImageStyle $style) return $styles; } - - /** - * Assemble style array into style string - * - * @param array $styles - * @return string - */ - protected function assembleStyle($styles = array()) - { - $style = ''; - foreach ($styles as $key => $value) { - if (!is_null($value) && $value != '') { - $style .= "{$key}:{$value}; "; - } - } - - return trim($style); - } } diff --git a/src/PhpWord/Writer/Word2007/Style/Outline.php b/src/PhpWord/Writer/Word2007/Style/Outline.php new file mode 100644 index 0000000000..5003d9fca6 --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Style/Outline.php @@ -0,0 +1,48 @@ +getStyle(); + if (!$style instanceof \PhpOffice\PhpWord\Style\Outline) { + return; + } + $xmlWriter = $this->getXmlWriter(); + + $xmlWriter->startElement("v:stroke"); + $xmlWriter->writeAttribute('on', 't'); + $xmlWriter->writeAttributeIf($style->getColor() !== null, 'color', $style->getColor()); + $xmlWriter->writeAttributeIf($style->getWeight() !== null, 'weight', $style->getWeight() . 'pt'); + $xmlWriter->writeAttributeIf($style->getDash() !== null, 'dashstyle', $style->getDash()); + $xmlWriter->writeAttributeIf($style->getLine() !== null, 'linestyle', $style->getLine()); + $xmlWriter->writeAttributeIf($style->getStartArrow() !== null, 'startarrow', $style->getStartArrow()); + $xmlWriter->writeAttributeIf($style->getEndArrow() !== null, 'endarrow', $style->getEndArrow()); + $xmlWriter->endElement(); + } +} diff --git a/src/PhpWord/Writer/Word2007/Style/Paragraph.php b/src/PhpWord/Writer/Word2007/Style/Paragraph.php index f328770083..0e97a7d7dc 100644 --- a/src/PhpWord/Writer/Word2007/Style/Paragraph.php +++ b/src/PhpWord/Writer/Word2007/Style/Paragraph.php @@ -110,24 +110,6 @@ private function writeStyle() } } - /** - * Write child style - * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param string $name - * @param mixed $value - */ - private function writeChildStyle(XMLWriter $xmlWriter, $name, $value) - { - if ($value !== null) { - $class = "PhpOffice\\PhpWord\\Writer\\Word2007\\Style\\" . $name; - - /** @var \PhpOffice\PhpWord\Writer\Word2007\Style\AbstractStyle $writer */ - $writer = new $class($xmlWriter, $value); - $writer->write(); - } - } - /** * Write tabs * diff --git a/src/PhpWord/Writer/Word2007/Style/Shadow.php b/src/PhpWord/Writer/Word2007/Style/Shadow.php new file mode 100644 index 0000000000..dd7c769aca --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Style/Shadow.php @@ -0,0 +1,44 @@ +getStyle(); + if (!$style instanceof \PhpOffice\PhpWord\Style\Shadow) { + return; + } + $xmlWriter = $this->getXmlWriter(); + + $xmlWriter->startElement("v:shadow"); + $xmlWriter->writeAttribute('on', 't'); + $xmlWriter->writeAttributeIf($style->getColor() !== null, 'color', $style->getColor()); + $xmlWriter->writeAttributeIf($style->getOffset() !== null, 'offset', $style->getOffset()); + $xmlWriter->endElement(); + } +} diff --git a/src/PhpWord/Writer/Word2007/Style/Shape.php b/src/PhpWord/Writer/Word2007/Style/Shape.php new file mode 100644 index 0000000000..7eafb18e1a --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Style/Shape.php @@ -0,0 +1,45 @@ +getStyle(); + if (!$style instanceof \PhpOffice\PhpWord\Style\Shape) { + return; + } + + $xmlWriter = $this->getXmlWriter(); + + $childStyles = array('Frame', 'Fill', 'Outline', 'Shadow', 'Extrusion'); + foreach ($childStyles as $childStyle) { + $method = "get{$childStyle}"; + $this->writeChildStyle($xmlWriter, $childStyle, $style->$method()); + } + } +} diff --git a/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php b/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php index da7504b5c1..6ba3477be0 100644 --- a/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php +++ b/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php @@ -41,7 +41,7 @@ public function testUnmatchedElements() $elements = array( 'CheckBox', 'Container', 'Footnote', 'Image', 'Link', 'ListItem', 'ListItemRun', 'Object', 'PreserveText', 'Table', 'Text', 'TextBox', 'TextBreak', 'Title', 'TOC', - 'Field', 'Line' + 'Field', 'Line', 'Shape' ); foreach ($elements as $element) { $objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Element\\' . $element; @@ -61,10 +61,91 @@ public function testLineElement() { $phpWord = new PhpWord(); $section = $phpWord->addSection(); + $section->addLine(array('width' => 1000, 'height' => 1000, 'positioning' => 'absolute', 'flip' => true)); $doc = TestHelperDOCX::getDocument($phpWord); $element = "/w:document/w:body/w:p/w:r/w:pict/v:shapetype"; $this->assertTrue($doc->elementExists($element)); } + + /** + * Test shape elements + */ + public function testShapeElements() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + // Arc + $section->addShape( + 'arc', + array( + 'points' => '-90 20', + 'frame' => array('width' => 120, 'height' => 120), + 'outline' => array('color' => '#333333', 'weight' => 2, 'startArrow' => 'oval', 'endArrow' => 'open'), + ) + ); + + // Curve + $section->addShape( + 'curve', + array( + 'points' => '1,100 200,1 1,50 200,50', 'connector' => 'elbow', + 'outline' => array('color' => '#66cc00', 'weight' => 2, 'dash' => 'dash', + 'startArrow' => 'diamond', 'endArrow' => 'block'), + ) + ); + + // Line + $section->addShape( + 'line', + array( + 'points' => '1,1 150,30', + 'outline' => array('color' => '#cc00ff', 'line' => 'thickThin', 'weight' => 3, + 'startArrow' => 'oval', 'endArrow' => 'classic'), + ) + ); + + // Polyline + $section->addShape( + 'polyline', + array( + 'points' => '1,30 20,10 55,20 75,10 100,40 115,50, 120,15 200,50', + 'outline' => array('color' => '#cc6666', 'weight' => 2, + 'startArrow' => 'none', 'endArrow' => 'classic'), + ) + ); + + // Rectangle + $section->addShape( + 'rect', + array( + 'roundness' => 0.2, + 'frame' => array('width' => 100, 'height' => 100, 'left' => 1, 'top' => 1), + 'fill' => array('color' => '#FFCC33'), + 'outline' => array('color' => '#990000', 'weight' => 1), + 'shadow' => array('color' => '#EEEEEE', 'offset' => '3pt,3pt'), + ) + ); + + // Oval + $section->addShape( + 'oval', + array( + 'frame' => array('width' => 100, 'height' => 70, 'left' => 1, 'top' => 1), + 'fill' => array('color' => '#33CC99'), + 'outline' => array('color' => '#333333', 'weight' => 2), + 'extrusion' => array('type' => 'perspective', 'color' => '#EEEEEE'), + ) + ); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $elements = array('arc', 'curve', 'line', 'polyline', 'roundrect', 'oval'); + foreach ($elements as $element) { + $path = "/w:document/w:body/w:p/w:r/w:pict/v:{$element}"; + $this->assertTrue($doc->elementExists($path)); + } + } } diff --git a/tests/PhpWord/Tests/Writer/Word2007/StyleTest.php b/tests/PhpWord/Tests/Writer/Word2007/StyleTest.php index 71cbc30068..c840d2074f 100644 --- a/tests/PhpWord/Tests/Writer/Word2007/StyleTest.php +++ b/tests/PhpWord/Tests/Writer/Word2007/StyleTest.php @@ -31,7 +31,7 @@ public function testEmptyStyles() $styles = array( 'Alignment', 'Cell', 'Font', 'Image', 'Indentation', 'LineNumbering', 'Paragraph', 'Row', 'Section', 'Shading', 'Spacing', 'Tab', 'Table', - 'TextBox', 'Line' + 'TextBox', 'Line', 'Shape', 'Frame', 'Outline', 'Fill', 'Shadow', 'Extrusion', ); foreach ($styles as $style) { $objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Style\\' . $style;