From 6a81691d453c57c1215da78357b13a87636195a2 Mon Sep 17 00:00:00 2001 From: Ivan Lanin Date: Mon, 16 Jun 2014 00:09:14 +0700 Subject: [PATCH] #266: Ability to add textinput, checkbox, and dropdown form elements --- CHANGELOG.md | 3 +- README.md | 1 + docs/elements.rst | 7 + docs/intro.rst | 1 + docs/src/documentation.md | 7 + samples/Sample_33_FormField.php | 26 +++ src/PhpWord/Element/AbstractContainer.php | 6 +- src/PhpWord/Element/Chart.php | 2 +- src/PhpWord/Element/CheckBox.php | 2 + src/PhpWord/Element/FormField.php | 195 ++++++++++++++++++ src/PhpWord/Shared/Drawing.php | 20 +- src/PhpWord/Shared/Font.php | 18 +- .../Writer/Word2007/Element/FormField.php | 161 +++++++++++++++ .../Tests/Writer/Word2007/ElementTest.php | 25 ++- 14 files changed, 449 insertions(+), 25 deletions(-) create mode 100644 samples/Sample_33_FormField.php create mode 100644 src/PhpWord/Element/FormField.php create mode 100644 src/PhpWord/Writer/Word2007/Element/FormField.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 155ecb35..49fe9993 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ This is the changelog between releases of PHPWord. Releases are listed in revers ## 0.12.0 - Not yet released -This release added drawing shapes (arc, curve, line, polyline, rect, oval) and basic 2D chart (pie, doughnut, bar, line, area, scatter, radar) elements along with some new styles. +This release added form fields (textinput, checkbox, and dropdown), drawing shapes (arc, curve, line, polyline, rect, oval), and basic 2D chart (pie, doughnut, bar, line, area, scatter, radar) elements along with some new styles. ### Features @@ -17,6 +17,7 @@ This release added drawing shapes (arc, curve, line, polyline, rect, oval) and b - General: New `Shared\Converter` static class - @ivanlanin - Chart: Basic 2D chart (pie, doughnut, bar, line, area, scatter, radar) - @ivanlanin GH-278 - Chart: 3D charts and ability to set width and height - @ivanlanin +- FormField: Ability to add textinput, checkbox, and dropdown form elements - @ivanlanin GH-266 ### Bugfixes diff --git a/README.md b/README.md index 5a432cba..825f7926 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ With PHPWord, you can create DOCX, ODT, or RTF documents dynamically using your - Insert footnotes and endnotes - Insert drawing shapes (arc, curve, line, polyline, rect, oval) - Insert charts (pie, doughnut, bar, line, area, scatter, radar) +- Insert form fields (textinput, checkbox, and dropdown) - 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 ee827326..e4baf70e 100644 --- a/docs/elements.rst +++ b/docs/elements.rst @@ -51,6 +51,8 @@ column shows the containers while the rows lists the elements. +-------+-----------------+-----------+----------+----------+---------+------------+------------+ | 21 | Chart | v | - | - | - | - | - | +-------+-----------------+-----------+----------+----------+---------+------------+------------+ +| 22 | Form fields | v | v | v | v | v | v | ++-------+-----------------+-----------+----------+----------+---------+------------+------------+ Legend: @@ -406,3 +408,8 @@ Charts ------ To be completed. + +Form fields +----------- + +To be completed. diff --git a/docs/intro.rst b/docs/intro.rst index 241b581c..b604298f 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -47,6 +47,7 @@ Features - Insert footnotes and endnotes - Insert drawing shapes (arc, curve, line, polyline, rect, oval) - Insert charts (pie, doughnut, bar, line, area, scatter, radar) +- Insert form fields (textinput, checkbox, and dropdown) - 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 39e61862..20996808 100644 --- a/docs/src/documentation.md +++ b/docs/src/documentation.md @@ -38,6 +38,7 @@ Don't forget to change `code::` directive to `code-block::` in the resulting rst - [Lines](#lines) - [Shapes](#shapes) - [Charts](#charts) + - [FormFields](#form-fields) - [Styles](#styles) - [Section](#section) - [Font](#font) @@ -79,6 +80,7 @@ PHPWord is an open source project licensed under the terms of [LGPL version 3](h - Insert footnotes and endnotes - Insert drawing shapes (arc, curve, line, polyline, rect, oval) - Insert charts (pie, doughnut, bar, line, area, scatter, radar) +- Insert form fields (textinput, checkbox, and dropdown) - Create document from templates - Use XSL 1.0 style sheets to transform main document part of OOXML template - ... and many more features on progress @@ -444,6 +446,7 @@ Below are the matrix of element availability in each container. The column shows | 19 | Line | v | v | v | v | v | v | | 20 | Shape | v | v | v | v | v | v | | 21 | Chart | v | - | - | - | - | - | +| 22 | Form Fields | v | v | v | v | v | v | Legend: @@ -745,6 +748,10 @@ To be completed. To be completed. +## Form fields + +To be completed. + # Styles ## Section diff --git a/samples/Sample_33_FormField.php b/samples/Sample_33_FormField.php new file mode 100644 index 00000000..f7b7c852 --- /dev/null +++ b/samples/Sample_33_FormField.php @@ -0,0 +1,26 @@ +addSection(); + +$textrun = $section->addTextRun(); +$textrun->addText('Form fields can be added in a text run and can be in form of textinput '); +$textrun->addFormField('textinput')->setName('MyTextBox'); +$textrun->addText(', checkbox '); +$textrun->addFormField('checkbox')->setDefault(true); +$textrun->addText(', or dropdown '); +$textrun->addFormField('dropdown')->setEntries(array('Choice 1', 'Choice 2', 'Choice 3')); +$textrun->addText('. You have to set document protection to "forms" to enable dropdown.'); + +$section->addText('They can also be added as a stand alone paragraph.'); +$section->addFormField('textinput')->setValue('Your name'); + +// 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 19e78c21..3ec11390 100644 --- a/src/PhpWord/Element/AbstractContainer.php +++ b/src/PhpWord/Element/AbstractContainer.php @@ -41,7 +41,8 @@ namespace PhpOffice\PhpWord\Element; * @method Field addField(string $type = null, array $properties = array(), array $options = array()) * @method Line addLine(mixed $lineStyle = null) * @method Shape addObject(string $type, mixed $style = null) - * @method Chart addChart() + * @method Chart addChart(string $type, array $categories, array $values, array $style = null) + * @method FormField addFormField(string $type, mixed $fStyle = null, mixed $pStyle = null) * * @since 0.10.0 */ @@ -78,7 +79,7 @@ abstract class AbstractContainer extends AbstractElement $elements = array('Text', 'TextRun', 'Link', 'PreserveText', 'TextBreak', 'ListItem', 'ListItemRun', 'Table', 'Image', 'Object', 'Footnote', 'Endnote', 'CheckBox', 'TextBox', 'Field', 'Line', 'Shape', - 'Title', 'TOC', 'PageBreak', 'Chart'); + 'Title', 'TOC', 'PageBreak', 'Chart', 'FormField'); $functions = array(); for ($i = 0; $i < count($elements); $i++) { $functions[$i] = 'add' . $elements[$i]; @@ -190,6 +191,7 @@ abstract class AbstractContainer extends AbstractElement 'Field' => $allContainers, 'Line' => $allContainers, 'Shape' => $allContainers, + 'FormField' => $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/Chart.php b/src/PhpWord/Element/Chart.php index 629db63b..2d709b8c 100644 --- a/src/PhpWord/Element/Chart.php +++ b/src/PhpWord/Element/Chart.php @@ -19,7 +19,6 @@ namespace PhpOffice\PhpWord\Element; use PhpOffice\PhpWord\Style\Chart as ChartStyle; - /** * Chart element * @@ -61,6 +60,7 @@ class Chart extends AbstractElement * @param string $type * @param array $categories * @param array $values + * @param array $style */ public function __construct($type, $categories, $values, $style = null) { diff --git a/src/PhpWord/Element/CheckBox.php b/src/PhpWord/Element/CheckBox.php index a5620580..d3b2a3c6 100644 --- a/src/PhpWord/Element/CheckBox.php +++ b/src/PhpWord/Element/CheckBox.php @@ -21,6 +21,8 @@ use PhpOffice\PhpWord\Shared\String; /** * Check box element + * + * @since 0.10.0 */ class CheckBox extends Text { diff --git a/src/PhpWord/Element/FormField.php b/src/PhpWord/Element/FormField.php new file mode 100644 index 00000000..7bd61be1 --- /dev/null +++ b/src/PhpWord/Element/FormField.php @@ -0,0 +1,195 @@ +setType($type); + } + + /** + * Get type + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Set type + * + * @param string $value + * @return self + */ + public function setType($value) + { + $enum = array('textinput', 'checkbox', 'dropdown'); + $this->type = $this->setEnumVal($value, $enum, $this->type); + + return $this; + } + + /** + * Get name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set name + * + * @param string|bool|int $value + * @return self + */ + public function setName($value) + { + $this->name = $value; + + return $this; + } + + /** + * Get default + * + * @return string|bool|int + */ + public function getDefault() + { + return $this->default; + } + + /** + * Set default + * + * @param string|bool|int $value + * @return self + */ + public function setDefault($value) + { + $this->default = $value; + + return $this; + } + + /** + * Get value + * + * @return string|bool|int + */ + public function getValue() + { + return $this->value; + } + + /** + * Set value + * + * @param string|bool|int $value + * @return self + */ + public function setValue($value) + { + $this->value = $value; + + return $this; + } + + /** + * Get entries + * + * @return array + */ + public function getEntries() + { + return $this->entries; + } + + /** + * Set entries + * + * @param array $value + * @return self + */ + public function setEntries($value) + { + $this->entries = $value; + + return $this; + } +} diff --git a/src/PhpWord/Shared/Drawing.php b/src/PhpWord/Shared/Drawing.php index 00d8b565..59fbe026 100644 --- a/src/PhpWord/Shared/Drawing.php +++ b/src/PhpWord/Shared/Drawing.php @@ -18,12 +18,12 @@ namespace PhpOffice\PhpWord\Shared; /** - * Common drawing functions; replaced by `Converter` + * DEPRECATED: Common drawing functions; Use 'Converter' * * @deprecated 0.12.0 * @codeCoverageIgnore */ -class Drawing extends Converter +class Drawing { /** * Convert pixels to EMU @@ -33,7 +33,7 @@ class Drawing extends Converter */ public static function pixelsToEMU($value = 0) { - return self::pixelToEmu($value); + return Converter::pixelToEmu($value); } /** @@ -44,7 +44,7 @@ class Drawing extends Converter */ public static function emuToPixels($value = 0) { - return self::emuToPixel($value); + return Converter::emuToPixel($value); } /** @@ -55,7 +55,7 @@ class Drawing extends Converter */ public static function pixelsToPoints($value = 0) { - return self::pixelToPoint($value); + return Converter::pixelToPoint($value); } /** @@ -66,7 +66,7 @@ class Drawing extends Converter */ public static function pointsToPixels($value = 0) { - return self::pointToPixel($value); + return Converter::pointToPixel($value); } /** @@ -77,7 +77,7 @@ class Drawing extends Converter */ public static function degreesToAngle($value = 0) { - return self::degreeToAngle($value); + return Converter::degreeToAngle($value); } /** @@ -88,7 +88,7 @@ class Drawing extends Converter */ public static function angleToDegrees($value = 0) { - return self::angleToDegree($value); + return Converter::angleToDegree($value); } /** @@ -99,7 +99,7 @@ class Drawing extends Converter */ public static function pixelsToCentimeters($value = 0) { - return self::pixelToCm($value); + return Converter::pixelToCm($value); } /** @@ -110,6 +110,6 @@ class Drawing extends Converter */ public static function centimetersToPixels($value = 0) { - return self::cmToPixel($value); + return Converter::cmToPixel($value); } } diff --git a/src/PhpWord/Shared/Font.php b/src/PhpWord/Shared/Font.php index 6aeacef7..231b0bbd 100644 --- a/src/PhpWord/Shared/Font.php +++ b/src/PhpWord/Shared/Font.php @@ -18,12 +18,12 @@ namespace PhpOffice\PhpWord\Shared; /** - * Common font functions; replaced by `Converter` + * DEPRECATED: Common font functions; Use 'Converter' * * @deprecated 0.12.0 * @codeCoverageIgnore */ -class Font extends Converter +class Font { /** * Calculate an (approximate) pixel size, based on a font points size @@ -33,7 +33,7 @@ class Font extends Converter */ public static function fontSizeToPixels($fontSizeInPoints = 12) { - return self::pointToPixel($fontSizeInPoints); + return Converter::pointToPixel($fontSizeInPoints); } /** @@ -44,7 +44,7 @@ class Font extends Converter */ public static function inchSizeToPixels($sizeInInch = 1) { - return self::inchToPixel($sizeInInch); + return Converter::inchToPixel($sizeInInch); } /** @@ -55,7 +55,7 @@ class Font extends Converter */ public static function centimeterSizeToPixels($sizeInCm = 1) { - return self::cmToPixel($sizeInCm); + return Converter::cmToPixel($sizeInCm); } /** @@ -66,7 +66,7 @@ class Font extends Converter */ public static function centimeterSizeToTwips($sizeInCm = 1) { - return self::cmToTwip($sizeInCm); + return Converter::cmToTwip($sizeInCm); } /** @@ -77,7 +77,7 @@ class Font extends Converter */ public static function inchSizeToTwips($sizeInInch = 1) { - return self::inchToTwip($sizeInInch); + return Converter::inchToTwip($sizeInInch); } /** @@ -88,7 +88,7 @@ class Font extends Converter */ public static function pixelSizeToTwips($sizeInPixel = 1) { - return self::pixelToTwip($sizeInPixel); + return Converter::pixelToTwip($sizeInPixel); } /** @@ -99,6 +99,6 @@ class Font extends Converter */ public static function pointSizeToTwips($sizeInPoint = 1) { - return self::pointToTwip($sizeInPoint); + return Converter::pointToTwip($sizeInPoint); } } diff --git a/src/PhpWord/Writer/Word2007/Element/FormField.php b/src/PhpWord/Writer/Word2007/Element/FormField.php new file mode 100644 index 00000000..21003314 --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Element/FormField.php @@ -0,0 +1,161 @@ +getXmlWriter(); + $element = $this->getElement(); + if (!$element instanceof FormFieldElement) { + return; + } + + $type = $element->getType(); + $instructions = array('textinput' => 'FORMTEXT', 'checkbox' => 'FORMCHECKBOX', 'dropdown' => 'FORMDROPDOWN'); + $instruction = $instructions[$type]; + $writeFormField = "write{$type}"; + $name = $element->getName(); + if ($name === null) { + $name = $type . $element->getElementId(); + } + $value = $element->getValue(); + if ($value === null) { + $value = str_repeat(' ', self::FILLER_LENGTH); + } + + $this->startElementP(); + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:fldChar'); + $xmlWriter->writeAttribute('w:fldCharType', 'begin'); + $xmlWriter->startElement('w:ffData'); + $xmlWriter->writeElementBlock('w:enabled', 'w:val', 1); + $xmlWriter->writeElementBlock('w:name', 'w:val', $name); + $xmlWriter->writeElementBlock('w:calcOnExit', 'w:val', 0); + $this->$writeFormField($xmlWriter, $element); + $xmlWriter->endElement(); // w:ffData + $xmlWriter->endElement(); // w:fldChar + $xmlWriter->endElement(); // w:r + + $xmlWriter->startElement('w:r'); + $this->writeFontStyle(); + $xmlWriter->startElement('w:instrText'); + $xmlWriter->writeAttribute('xml:space', 'preserve'); + $xmlWriter->writeRaw("{$instruction}"); + $xmlWriter->endElement();// w:instrText + $xmlWriter->endElement(); // w:r + + $xmlWriter->startElement('w:r'); + $this->writeFontStyle(); + $xmlWriter->writeElementBlock('w:fldChar', 'w:fldCharType', 'separate'); + $xmlWriter->endElement(); // w:r + + $xmlWriter->startElement('w:r'); + $this->writeFontStyle(); + $xmlWriter->startElement('w:t'); + $xmlWriter->writeAttribute('xml:space', 'preserve'); + $xmlWriter->writeRaw($value); + $xmlWriter->endElement(); // w:t + $xmlWriter->endElement(); // w:r + + $xmlWriter->startElement('w:r'); + $this->writeFontStyle(); + $xmlWriter->writeElementBlock('w:fldChar', 'w:fldCharType', 'end'); + $xmlWriter->endElement(); // w:r + + $this->endElementP(); // w:p + } + + /** + * Write textinput + * + * @link http://www.datypic.com/sc/ooxml/t-w_CT_FFTextInput.html + */ + private function writeTextInput(XMLWriter $xmlWriter, FormFieldElement $element) + { + $default = $element->getDefault(); + + $xmlWriter->startElement('w:textInput'); + $xmlWriter->writeElementBlock('w:default', 'w:val', $default); + $xmlWriter->endElement(); + } + + /** + * Write checkbox + * + * @link http://www.datypic.com/sc/ooxml/t-w_CT_FFCheckBox.html + */ + private function writeCheckBox(XMLWriter $xmlWriter, FormFieldElement $element) + { + $default = $element->getDefault() ? 1 : 0; + $value = $element->getValue(); + if ($value == null) { + $value = $default; + } + $value = $value ? 1 : 0; + + $xmlWriter->startElement('w:checkBox'); + $xmlWriter->writeElementBlock('w:sizeAuto', 'w:val', ''); + $xmlWriter->writeElementBlock('w:default', 'w:val', $default); + $xmlWriter->writeElementBlock('w:checked', 'w:val', $value); + $xmlWriter->endElement(); + } + + /** + * Write dropdown + * + * @link http://www.datypic.com/sc/ooxml/t-w_CT_FFDDList.html + */ + private function writeDropDown(XMLWriter $xmlWriter, FormFieldElement $element) + { + $default = $element->getDefault(); + $value = $element->getValue(); + if ($value == null) { + $value = $default; + } + $entries = $element->getEntries(); + + $xmlWriter->startElement('w:ddList'); + $xmlWriter->writeElementBlock('w:result', 'w:val', $value); + $xmlWriter->writeElementBlock('w:default', 'w:val', $default); + foreach ($entries as $entry) { + $xmlWriter->writeElementBlock('w:listEntry', 'w:val', $entry); + } + $xmlWriter->endElement(); + } +} diff --git a/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php b/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php index 528cafd5..6c10f3a5 100644 --- a/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php +++ b/tests/PhpWord/Tests/Writer/Word2007/ElementTest.php @@ -152,11 +152,11 @@ class ElementTest extends \PHPUnit_Framework_TestCase /** * Test shape elements */ - public function testChartElement() + public function testChartElements() { $phpWord = new PhpWord(); $section = $phpWord->addSection(); - $style = array('width' => 1000000, 'height' => 1000000, '3d' => true); + $style = array('width' => 1000000, 'height' => 1000000); $chartTypes = array('pie', 'doughnut', 'bar', 'line', 'area', 'scatter', 'radar'); $categories = array('A', 'B', 'C', 'D', 'E'); @@ -164,6 +164,7 @@ class ElementTest extends \PHPUnit_Framework_TestCase foreach ($chartTypes as $chartType) { $section->addChart($chartType, $categories, $series1, $style); } + $section->addChart('pie', $categories, $series1, array('3d' => true)); $doc = TestHelperDOCX::getDocument($phpWord); @@ -175,4 +176,24 @@ class ElementTest extends \PHPUnit_Framework_TestCase $this->assertTrue($doc->elementExists($path, $file)); } } + + /** + * Test form fields + */ + public function testFormFieldElements() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $section->addFormField('textinput')->setName('MyTextBox'); + $section->addFormField('checkbox')->setDefault(true)->setValue('Your name'); + $section->addFormField('dropdown')->setEntries(array('Choice 1', 'Choice 2', 'Choice 3')); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $path = "/w:document/w:body/w:p/w:r/w:fldChar/w:ffData"; + $this->assertTrue($doc->elementExists($path . '/w:textInput')); + $this->assertTrue($doc->elementExists($path . '/w:checkBox')); + $this->assertTrue($doc->elementExists($path . '/w:ddList')); + } }