diff --git a/CHANGELOG.md b/CHANGELOG.md
index fce2bdac..fcaf1388 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,6 +26,10 @@ This release marked the change of PHPWord license from LGPL 2.1 to LGPL 3; new r
- PDF Writer: Add TCPDF and mPDF as optional PDF renderer library - @ivanlanin
- ODT Writer: Enable title element and custom document properties - @ivanlanin
- ODT Reader: Ability to read standard and custom document properties - @ivanlanin
+- Word2007 Writer: Enable the missing custom document properties writer - @ivanlanin
+- Image: Enable "image float left" - @ivanlanin GH-244
+- RTF Writer: Ability to write document properties - @ivanlanin
+- RTF Writer: Ability to write image - @ivanlanin
### Bugfixes
@@ -39,6 +43,7 @@ This release marked the change of PHPWord license from LGPL 2.1 to LGPL 3; new r
- `Writer\Word2007\Part`: `Numbering::writeNumbering()`, `Settings::writeSettings()`, `WebSettings::writeWebSettings()`, `ContentTypes::writeContentTypes()`, `Styles::writeStyles()`, `Document::writeDocument()` all changed into `write()`
- `Writer\Word2007\Part\DocProps`: Split into `Writer\Word2007\Part\DocPropsCore` and `Writer\Word2007\Part\DocPropsApp`
- `Element\Title::getBookmarkId()` replaced by `Element\Title::getRelationId()`
+- `Writer\HTML::writeDocument`: Replaced by `Writer\HTML::getContent`
### Miscellaneous
@@ -54,6 +59,12 @@ This release marked the change of PHPWord license from LGPL 2.1 to LGPL 3; new r
- Style: Change behaviour of `set...` function of boolean properties; when none is defined, assumed true - @ivanlanin
- Shared: Unify PHP ZipArchive and PCLZip features into PhpWord ZipArchive - @ivanlanin
+## 0.10.1 - 21 May 2014
+
+This is a bugfix release for `php-zip` requirement in Composer.
+
+- Change Composer requirements for php-zip from `require` to `suggest` - @bskrtich GH-246
+
## 0.10.0 - 4 May 2014
This release marked heavy refactorings on internal code structure with the creation of some abstract classes to reduce code duplication. `Element` subnamespace is introduced in this release to replace `Section`. Word2007 reader capability is greatly enhanced. Endnote is introduced. List numbering is now customizable. Basic HTML and PDF writing support is enabled. Basic ODText reader is introduced.
diff --git a/LICENSE.md b/COPYING
similarity index 77%
rename from LICENSE.md
rename to COPYING
index a4343e8e..94a9ed02 100644
--- a/LICENSE.md
+++ b/COPYING
@@ -1,189 +1,74 @@
-# PHPWord License
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
-PHPWord, a pure PHP library for reading and writing word processing documents
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
-Copyright (c) 2014 PHPWord
+ Preamble
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Lesser General Public License for more details.
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
-You should have received a copy of the GNU Lesser General Public License
-along with this program. If not, see .
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
-## GNU LESSER GENERAL PUBLIC LICENSE
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
-Version 3, 29 June 2007
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
- 0. Additional Definitions.
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
+ The precise terms and conditions for copying, distribution and
+modification follow.
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
-
-## GNU GENERAL PUBLIC LICENSE
-
-Version 3, 29 June 2007
-
-TERMS AND CONDITIONS
+ TERMS AND CONDITIONS
0. Definitions.
@@ -732,3 +617,58 @@ reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/COPYING.LESSER b/COPYING.LESSER
new file mode 100644
index 00000000..65c5ca88
--- /dev/null
+++ b/COPYING.LESSER
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..3f6a8c38
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+PHPWord, a pure PHP library for reading and writing word processing documents.
+
+Copyright (c) 2010-2014 PHPWord.
+
+PHPWord is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License version 3 as published by
+the Free Software Foundation.
+
+PHPWord is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License version 3 for more details.
+
+You should have received a copy of the GNU Lesser General Public License version 3
+along with PHPWord. If not, see .
diff --git a/README.md b/README.md
index eb8dd445..ecbe1354 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
PHPWord is a library written in pure PHP that provides a set of classes to write to and read from different document file formats. The current version of PHPWord supports Microsoft [Office Open XML](http://en.wikipedia.org/wiki/Office_Open_XML) (OOXML or OpenXML), OASIS [Open Document Format for Office Applications](http://en.wikipedia.org/wiki/OpenDocument) (OpenDocument or ODF), [Rich Text Format](http://en.wikipedia.org/wiki/Rich_Text_Format) (RTF), HTML, and PDF.
-PHPWord is an open source project licensed under the terms of [LGPL version 3](https://github.com/PHPOffice/PHPWord/blob/develop/LICENSE.md). PHPWord is aimed to be a high quality software product by incorporating [continuous integration](https://travis-ci.org/PHPOffice/PHPWord) and [unit testing](http://phpoffice.github.io/PHPWord/coverage/develop/). You can learn more about PHPWord by reading the [Developers' Documentation](http://phpword.readthedocs.org/) and the [API Documentation](http://phpoffice.github.io/PHPWord/docs/develop/).
+PHPWord is an open source project licensed under the terms of [LGPL version 3](https://github.com/PHPOffice/PHPWord/blob/develop/COPYING.LESSER). PHPWord is aimed to be a high quality software product by incorporating [continuous integration](https://travis-ci.org/PHPOffice/PHPWord) and [unit testing](http://phpoffice.github.io/PHPWord/coverage/develop/). You can learn more about PHPWord by reading the [Developers' Documentation](http://phpword.readthedocs.org/) and the [API Documentation](http://phpoffice.github.io/PHPWord/docs/develop/).
## Features
diff --git a/composer.json b/composer.json
index 483d2701..2f9a0915 100644
--- a/composer.json
+++ b/composer.json
@@ -44,11 +44,11 @@
"mpdf/mpdf": "5.*"
},
"suggest": {
- "ext-zip": "Used to create docx files",
- "ext-gd2": "Required to add images",
- "ext-xmlwriter": "Required to write DOCX and ODT",
- "ext-xsl": "Required to apply XSL style sheet to template part",
- "dompdf/dompdf": "Required to write PDF"
+ "ext-zip": "Used to write DOCX and ODT",
+ "ext-gd2": "Used to add images",
+ "ext-xmlwriter": "Used to write DOCX and ODT",
+ "ext-xsl": "Used to apply XSL style sheet to template part",
+ "dompdf/dompdf": "Used to write PDF"
},
"autoload": {
"psr-4": {
diff --git a/docs/intro.rst b/docs/intro.rst
index 3c6277f5..a64fb2ad 100644
--- a/docs/intro.rst
+++ b/docs/intro.rst
@@ -13,7 +13,7 @@ Applications `__
Format `__ (RTF).
PHPWord is an open source project licensed under the terms of `LGPL
-version 3 `__.
+version 3 `__.
PHPWord is aimed to be a high quality software product by incorporating
`continuous integration `__ and
`unit testing `__.
@@ -61,7 +61,7 @@ Writers
+---------------------------+----------------------+--------+-------+-------+--------+-------+
| Features | | DOCX | ODT | RTF | HTML | PDF |
+===========================+======================+========+=======+=======+========+=======+
-| **Document Properties** | Standard | ✓ | ✓ | | | |
+| **Document Properties** | Standard | ✓ | ✓ | ✓ | ✓ | ✓ |
+---------------------------+----------------------+--------+-------+-------+--------+-------+
| | Custom | ✓ | ✓ | | | |
+---------------------------+----------------------+--------+-------+-------+--------+-------+
@@ -83,7 +83,7 @@ Writers
+---------------------------+----------------------+--------+-------+-------+--------+-------+
| | Table | ✓ | ✓ | | ✓ | ✓ |
+---------------------------+----------------------+--------+-------+-------+--------+-------+
-| | Image | ✓ | ✓ | | ✓ | |
+| | Image | ✓ | ✓ | ✓ | ✓ | |
+---------------------------+----------------------+--------+-------+-------+--------+-------+
| | Object | ✓ | | | | |
+---------------------------+----------------------+--------+-------+-------+--------+-------+
diff --git a/docs/recipes.rst b/docs/recipes.rst
index 033452b3..d900af8a 100644
--- a/docs/recipes.rst
+++ b/docs/recipes.rst
@@ -2,3 +2,42 @@
Recipes
=======
+
+Create float left image
+-----------------------
+
+Use absolute positioning relative to margin horizontally and to line
+vertically.
+
+.. code-block:: php
+
+ $imageStyle = array(
+ 'width' => 40,
+ 'height' => 40
+ 'wrappingStyle' => 'square',
+ 'positioning' => 'absolute',
+ 'posHorizontalRel' => 'margin',
+ 'posVerticalRel' => 'line',
+ );
+ $textrun->addImage('resources/_earth.jpg', $imageStyle);
+ $textrun->addText($lipsumText);
+
+Download the produced file automatically
+----------------------------------------
+
+Use ``php://output`` as the filename.
+
+.. code-block:: php
+
+ $phpWord = new \PhpOffice\PhpWord\PhpWord();
+ $section = $phpWord->createSection();
+ $section->addText('Hello World!');
+ $file = 'HelloWorld.docx';
+ header("Content-Description: File Transfer");
+ header('Content-Disposition: attachment; filename="' . $file . '"');
+ header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');
+ header('Content-Transfer-Encoding: binary');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ header('Expires: 0');
+ $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007');
+ $xmlWriter->save("php://output");
diff --git a/docs/src/documentation.md b/docs/src/documentation.md
index ffa3edc3..6d34ff9f 100644
--- a/docs/src/documentation.md
+++ b/docs/src/documentation.md
@@ -40,6 +40,7 @@ Don't forget to change `code::` directive to `code-block::` in the resulting rst
- [RTF](#rtf)
- [HTML](#html)
- [PDF](#pdf)
+- [Recipes](#recipes)
- [Frequently asked questions](#frequently-asked-questions)
- [References](#references)
@@ -47,7 +48,7 @@ Don't forget to change `code::` directive to `code-block::` in the resulting rst
PHPWord is a library written in pure PHP that provides a set of classes to write to and read from different document file formats. The current version of PHPWord supports Microsoft [Office Open XML](http://en.wikipedia.org/wiki/Office_Open_XML) (OOXML or OpenXML), OASIS [Open Document Format for Office Applications](http://en.wikipedia.org/wiki/OpenDocument) (OpenDocument or ODF), and [Rich Text Format](http://en.wikipedia.org/wiki/Rich_Text_Format) (RTF).
-PHPWord is an open source project licensed under the terms of [LGPL version 3](https://github.com/PHPOffice/PHPWord/blob/develop/LICENSE.md). PHPWord is aimed to be a high quality software product by incorporating [continuous integration](https://travis-ci.org/PHPOffice/PHPWord) and [unit testing](http://phpoffice.github.io/PHPWord/coverage/develop/). You can learn more about PHPWord by reading this Developers' Documentation and the [API Documentation](http://phpoffice.github.io/PHPWord/docs/develop/).
+PHPWord is an open source project licensed under the terms of [LGPL version 3](https://github.com/PHPOffice/PHPWord/blob/develop/COPYING.LESSER). PHPWord is aimed to be a high quality software product by incorporating [continuous integration](https://travis-ci.org/PHPOffice/PHPWord) and [unit testing](http://phpoffice.github.io/PHPWord/coverage/develop/). You can learn more about PHPWord by reading this Developers' Documentation and the [API Documentation](http://phpoffice.github.io/PHPWord/docs/develop/).
## Features
@@ -78,7 +79,7 @@ Below are the supported features for each file formats.
| Features | | DOCX | ODT | RTF | HTML | PDF |
|-------------------------|--------------------|------|-----|-----|------|-----|
-| **Document Properties** | Standard | ✓ | ✓ | | | |
+| **Document Properties** | Standard | ✓ | ✓ | ✓ | ✓ | |
| | Custom | ✓ | ✓ | | | |
| **Element Type** | Text | ✓ | ✓ | ✓ | ✓ | ✓ |
| | Text Run | ✓ | ✓ | ✓ | ✓ | ✓ |
@@ -89,7 +90,7 @@ Below are the supported features for each file formats.
| | Page Break | ✓ | | ✓ | | |
| | List | ✓ | | | | |
| | Table | ✓ | ✓ | | ✓ | ✓ |
-| | Image | ✓ | ✓ | | ✓ | |
+| | Image | ✓ | ✓ | ✓ | ✓ | |
| | Object | ✓ | | | | |
| | Watermark | ✓ | | | | |
| | Table of Contents | ✓ | | | | |
@@ -929,6 +930,44 @@ To be completed.
To be completed.
+# Recipes
+
+## Create float left image
+
+Use absolute positioning relative to margin horizontally and to line vertically.
+
+```php
+$imageStyle = array(
+ 'width' => 40,
+ 'height' => 40
+ 'wrappingStyle' => 'square',
+ 'positioning' => 'absolute',
+ 'posHorizontalRel' => 'margin',
+ 'posVerticalRel' => 'line',
+);
+$textrun->addImage('resources/_earth.jpg', $imageStyle);
+$textrun->addText($lipsumText);
+```
+
+## Download the produced file automatically
+
+Use `php://output` as the filename.
+
+```php
+$phpWord = new \PhpOffice\PhpWord\PhpWord();
+$section = $phpWord->createSection();
+$section->addText('Hello World!');
+$file = 'HelloWorld.docx';
+header("Content-Description: File Transfer");
+header('Content-Disposition: attachment; filename="' . $file . '"');
+header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');
+header('Content-Transfer-Encoding: binary');
+header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+header('Expires: 0');
+$xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007');
+$xmlWriter->save("php://output");
+```
+
# Frequently asked questions
## Is this the same with PHPWord that I found in CodePlex?
diff --git a/samples/Sample_13_Images.php b/samples/Sample_13_Images.php
index dd5209bf..dd0c8801 100644
--- a/samples/Sample_13_Images.php
+++ b/samples/Sample_13_Images.php
@@ -40,6 +40,9 @@ $section->addImage(
'width' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(3),
'height' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(3),
'positioning' => \PhpOffice\PhpWord\Style\Image::POSITION_ABSOLUTE,
+ 'posHorizontal' => \PhpOffice\PhpWord\Style\Image::POSITION_HORIZONTAL_RIGHT,
+ 'posHorizontalRel' => \PhpOffice\PhpWord\Style\Image::POSITION_RELATIVE_TO_PAGE,
+ 'posVerticalRel' => \PhpOffice\PhpWord\Style\Image::POSITION_RELATIVE_TO_PAGE,
'marginLeft' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(15.5),
'marginTop' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(1.55)
)
diff --git a/src/PhpWord/Autoloader.php b/src/PhpWord/Autoloader.php
index 6acfff21..c467f836 100644
--- a/src/PhpWord/Autoloader.php
+++ b/src/PhpWord/Autoloader.php
@@ -22,6 +22,7 @@ namespace PhpOffice\PhpWord;
*/
class Autoloader
{
+ /** @const string */
const NAMESPACE_PREFIX = 'PhpOffice\\PhpWord\\';
/**
@@ -46,6 +47,7 @@ class Autoloader
$file = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, $prefixLength));
$file = realpath(__DIR__ . (empty($file) ? '' : DIRECTORY_SEPARATOR) . $file . '.php');
if (file_exists($file)) {
+ /** @noinspection PhpIncludeInspection Dynamic includes */
require_once $file;
}
}
diff --git a/src/PhpWord/DocumentProperties.php b/src/PhpWord/DocumentProperties.php
index 17d57c1d..5644c3d9 100644
--- a/src/PhpWord/DocumentProperties.php
+++ b/src/PhpWord/DocumentProperties.php
@@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord;
*/
class DocumentProperties
{
- /** Constants */
+ /** @const string Property type constants */
const PROPERTY_TYPE_BOOLEAN = 'b';
const PROPERTY_TYPE_INTEGER = 'i';
const PROPERTY_TYPE_FLOAT = 'f';
@@ -488,49 +488,23 @@ class DocumentProperties
*/
public static function convertProperty($propertyValue, $propertyType)
{
- switch ($propertyType) {
- case 'empty': // Empty
+ $conversion = self::getConversion($propertyType);
+
+ switch ($conversion) {
+ case 'empty': // Empty
return '';
- case 'null': // Null
+ case 'null': // Null
return null;
- case 'i1': // 1-Byte Signed Integer
- case 'i2': // 2-Byte Signed Integer
- case 'i4': // 4-Byte Signed Integer
- case 'i8': // 8-Byte Signed Integer
- case 'int': // Integer
+ case 'int': // Signed integer
return (int) $propertyValue;
- case 'ui1': // 1-Byte Unsigned Integer
- case 'ui2': // 2-Byte Unsigned Integer
- case 'ui4': // 4-Byte Unsigned Integer
- case 'ui8': // 8-Byte Unsigned Integer
- case 'uint': // Unsigned Integer
+ case 'uint': // Unsigned integer
return abs((int) $propertyValue);
- case 'r4': // 4-Byte Real Number
- case 'r8': // 8-Byte Real Number
- case 'decimal': // Decimal
+ case 'float': // Float
return (float) $propertyValue;
- case 'date': // Date and Time
- case 'filetime': // File Time
+ case 'date': // Date
return strtotime($propertyValue);
- case 'bool': // Boolean
+ case 'bool': // Boolean
return ($propertyValue == 'true') ? true : false;
- case 'lpstr': // LPSTR
- case 'lpwstr': // LPWSTR
- case 'bstr': // Basic String
- case 'cy': // Currency
- case 'error': // Error Status Code
- case 'vector': // Vector
- case 'array': // Array
- case 'blob': // Binary Blob
- case 'oblob': // Binary Blob Object
- case 'stream': // Binary Stream
- case 'ostream': // Binary Stream Object
- case 'storage': // Binary Storage
- case 'ostorage': // Binary Storage Object
- case 'vstream': // Binary Versioned Stream
- case 'clsid': // Class ID
- case 'cf': // Clipboard Data
- return $propertyValue;
}
return $propertyValue;
@@ -569,10 +543,36 @@ class DocumentProperties
*/
private function setValue($value, $default)
{
- if (is_null($value) || $value == '') {
+ if ($value === null || $value == '') {
$value = $default;
}
return $value;
}
+
+ /**
+ * Get conversion model depending on property type
+ *
+ * @param string $propertyType
+ * @return string
+ */
+ private static function getConversion($propertyType)
+ {
+ $conversions = array(
+ 'empty' => array('empty'),
+ 'null' => array('null'),
+ 'int' => array('i1', 'i2', 'i4', 'i8', 'int'),
+ 'uint' => array('ui1', 'ui2', 'ui4', 'ui8', 'uint'),
+ 'float' => array('r4', 'r8', 'decimal'),
+ 'bool' => array('bool'),
+ 'date' => array('date', 'filetime'),
+ );
+ foreach ($conversions as $conversion => $types) {
+ if (in_array($propertyType, $types)) {
+ return $conversion;
+ }
+ }
+
+ return 'string';
+ }
}
diff --git a/src/PhpWord/Element/AbstractContainer.php b/src/PhpWord/Element/AbstractContainer.php
index f141f4f0..56425f4f 100644
--- a/src/PhpWord/Element/AbstractContainer.php
+++ b/src/PhpWord/Element/AbstractContainer.php
@@ -56,37 +56,24 @@ abstract class AbstractContainer extends AbstractElement
// Get arguments
$args = func_get_args();
- $argsCount = func_num_args();
$withoutP = in_array($this->container, array('TextRun', 'Footnote', 'Endnote', 'ListItemRun'));
if ($withoutP && ($elementName == 'Text' || $elementName == 'PreserveText')) {
- $args[3] = null;
+ $args[3] = null; // Remove paragraph style for texts in textrun
}
- // Create element dynamically
-
+ // Create element using reflection
+ $reflection = new \ReflectionClass($elementClass);
+ $elementArgs = $args;
+ array_shift($elementArgs); // Shift an element off the beginning of array: the $elementName
/** @var \PhpOffice\PhpWord\Element\AbstractElement $element Type hint */
- if ($argsCount == 2) { // TextRun, TextBox, Table, Footnote, Endnote
- $element = new $elementClass($args[1]);
- } elseif ($argsCount == 3) { // Object, TextBreak, Title
- $element = new $elementClass($args[1], $args[2]);
- } elseif ($argsCount == 4) { // PreserveText, Text, Image
- $element = new $elementClass($args[1], $args[2], $args[3]);
- } elseif ($argsCount == 5) { // CheckBox, Link, ListItemRun, TOC
- $element = new $elementClass($args[1], $args[2], $args[3], $args[4]);
- } elseif ($argsCount == 6) { // ListItem
- $element = new $elementClass($args[1], $args[2], $args[3], $args[4], $args[5]);
- } else { // Page Break
- $element = new $elementClass();
- }
+ $element = $reflection->newInstanceArgs($elementArgs);
// Set relation Id for media collection
$mediaContainer = $this->getMediaContainer();
if (in_array($elementName, array('Link', 'Image', 'Object'))) {
- if ($elementName == 'Image') {
- $rId = Media::addElement($mediaContainer, strtolower($elementName), $args[1], $element);
- } else {
- $rId = Media::addElement($mediaContainer, strtolower($elementName), $args[1]);
- }
+ /** @var \PhpOffice\PhpWord\Element\Image $element Type hint */
+ $image = ($elementName == 'Image') ? $element : null;
+ $rId = Media::addElement($mediaContainer, strtolower($elementName), $args[1], $image);
$element->setRelationId($rId);
}
if ($elementName == 'Object') {
diff --git a/src/PhpWord/Element/Image.php b/src/PhpWord/Element/Image.php
index 8e30b78c..a1cb8250 100644
--- a/src/PhpWord/Element/Image.php
+++ b/src/PhpWord/Element/Image.php
@@ -279,6 +279,77 @@ class Image extends AbstractElement
$this->mediaIndex = $value;
}
+ /**
+ * Get image string data
+ *
+ * @param bool $base64
+ * @return string|null
+ * @since 0.11.0
+ */
+ public function getImageStringData($base64 = false)
+ {
+ $source = $this->source;
+ $actualSource = null;
+ $imageBinary = null;
+ $imageData = null;
+ $isTemp = false;
+
+ // Get actual source from archive image or other source
+ // Return null if not found
+ if ($this->sourceType == self::SOURCE_ARCHIVE) {
+ $source = substr($source, 6);
+ list($zipFilename, $imageFilename) = explode('#', $source);
+
+ $zip = new ZipArchive();
+ if ($zip->open($zipFilename) !== false) {
+ if ($zip->locateName($imageFilename)) {
+ $isTemp = true;
+ $zip->extractTo(sys_get_temp_dir(), $imageFilename);
+ $actualSource = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $imageFilename;
+ }
+ }
+ $zip->close();
+ } else {
+ $actualSource = $source;
+ }
+
+ // Can't find any case where $actualSource = null hasn't captured by
+ // preceding exceptions. Please uncomment when you find the case and
+ // put the case into Element\ImageTest.
+ // if ($actualSource === null) {
+ // return null;
+ // }
+
+ // Read image binary data and convert to hex/base64 string
+ if ($this->sourceType == self::SOURCE_GD) {
+ $imageResource = call_user_func($this->imageCreateFunc, $actualSource);
+ ob_start();
+ call_user_func($this->imageFunc, $imageResource);
+ $imageBinary = ob_get_contents();
+ ob_end_clean();
+ } else {
+ $fileHandle = fopen($actualSource, 'rb', false);
+ if ($fileHandle !== false) {
+ $imageBinary = fread($fileHandle, filesize($actualSource));
+ fclose($fileHandle);
+ }
+ }
+ if ($imageBinary !== null) {
+ if ($base64) {
+ $imageData = chunk_split(base64_encode($imageBinary));
+ } else {
+ $imageData = chunk_split(bin2hex($imageBinary));
+ }
+ }
+
+ // Delete temporary file if necessary
+ if ($isTemp === true) {
+ @unlink($actualSource);
+ }
+
+ return $imageData;
+ }
+
/**
* Check memory image, supported type, image functions, and proportional width/height
*
diff --git a/src/PhpWord/Media.php b/src/PhpWord/Media.php
index 8cb82013..7f35841c 100644
--- a/src/PhpWord/Media.php
+++ b/src/PhpWord/Media.php
@@ -139,37 +139,53 @@ class Media
* Get media elements
*
* @param string $container section|headerx|footerx|footnote|endnote
- * @param string $mediaType image|object|link
+ * @param string $type image|object|link
* @return array
* @since 0.10.0
*/
- public static function getElements($container, $mediaType = null)
+ public static function getElements($container, $type = null)
{
- $mediaElements = array();
+ $elements = array();
// If header/footer, search for headerx and footerx where x is number
if ($container == 'header' || $container == 'footer') {
foreach (self::$elements as $key => $val) {
if (substr($key, 0, 6) == $container) {
- $mediaElements[$key] = $val;
+ $elements[$key] = $val;
}
}
+ return $elements;
} else {
if (!array_key_exists($container, self::$elements)) {
- return $mediaElements;
+ return $elements;
}
- foreach (self::$elements[$container] as $mediaKey => $mediaData) {
- if (!is_null($mediaType)) {
- if ($mediaType == $mediaData['type']) {
- $mediaElements[$mediaKey] = $mediaData;
- }
- } else {
- $mediaElements[$mediaKey] = $mediaData;
+ return self::getElementsByType($container, $type);
+ }
+ }
+
+ /**
+ * Get elements by media type
+ *
+ * @param string $container section|footnote|endnote
+ * @param string $type image|object|link
+ * @return array
+ * @since 0.11.0 Splitted from `getElements` to reduce complexity
+ */
+ private static function getElementsByType($container, $type = null)
+ {
+ $elements = array();
+
+ foreach (self::$elements[$container] as $key => $data) {
+ if ($type !== null) {
+ if ($type == $data['type']) {
+ $elements[$key] = $data;
}
+ } else {
+ $elements[$key] = $data;
}
}
- return $mediaElements;
+ return $elements;
}
/**
diff --git a/src/PhpWord/Reader/AbstractReader.php b/src/PhpWord/Reader/AbstractReader.php
index a243c5d2..3cc2d490 100644
--- a/src/PhpWord/Reader/AbstractReader.php
+++ b/src/PhpWord/Reader/AbstractReader.php
@@ -22,6 +22,7 @@ use PhpOffice\PhpWord\Exception\Exception;
/**
* Reader abstract class
*
+ * @since 0.8.0
* @codeCoverageIgnore Abstract class
*/
abstract class AbstractReader implements ReaderInterface
diff --git a/src/PhpWord/Reader/ODText/AbstractPart.php b/src/PhpWord/Reader/ODText/AbstractPart.php
index 815e60fd..2097df9c 100644
--- a/src/PhpWord/Reader/ODText/AbstractPart.php
+++ b/src/PhpWord/Reader/ODText/AbstractPart.php
@@ -18,82 +18,13 @@
namespace PhpOffice\PhpWord\Reader\ODText;
use PhpOffice\PhpWord\Reader\Word2007\AbstractPart as Word2007AbstractPart;
-use PhpOffice\PhpWord\Shared\XMLReader;
/**
* Abstract part reader
+ *
+ * @since 0.10.0
+ * @codeCoverageIgnore Nothing in here yet
*/
abstract class AbstractPart extends Word2007AbstractPart
{
- /**
- * Read w:p (override)
- *
- * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
- * @param \DOMElement $domNode
- * @param mixed $parent
- * @param string $docPart
- *
- * @todo Get font style for preserve text
- */
- protected function readParagraph(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart)
- {
- }
-
- /**
- * Read w:r (override)
- *
- * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
- * @param \DOMElement $domNode
- * @param mixed $parent
- * @param string $docPart
- * @param mixed $paragraphStyle
- */
- protected function readRun(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart, $paragraphStyle = null)
- {
- }
-
- /**
- * Read w:tbl (override)
- *
- * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
- * @param \DOMElement $domNode
- * @param mixed $parent
- * @param string $docPart
- */
- protected function readTable(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart)
- {
- }
-
- /**
- * Read w:pPr (override)
- */
- protected function readParagraphStyle(XMLReader $xmlReader, \DOMElement $domNode)
- {
- }
-
- /**
- * Read w:rPr (override)
- */
- protected function readFontStyle(XMLReader $xmlReader, \DOMElement $domNode)
- {
- }
-
- /**
- * Read w:tblPr (override)
- */
- protected function readTableStyle(XMLReader $xmlReader, \DOMElement $domNode)
- {
- }
-
- /**
- * Read style definition (override)
- *
- * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
- * @param \DOMElement $parentNode
- * @param array $styleDefs
- * @return array
- */
- protected function readStyleDefs(XMLReader $xmlReader, \DOMElement $parentNode = null, $styleDefs = array())
- {
- }
}
diff --git a/src/PhpWord/Reader/ReaderInterface.php b/src/PhpWord/Reader/ReaderInterface.php
index df663197..361c4137 100644
--- a/src/PhpWord/Reader/ReaderInterface.php
+++ b/src/PhpWord/Reader/ReaderInterface.php
@@ -19,6 +19,8 @@ namespace PhpOffice\PhpWord\Reader;
/**
* Reader interface
+ *
+ * @since 0.8.0
*/
interface ReaderInterface
{
diff --git a/src/PhpWord/Reader/Word2007/AbstractPart.php b/src/PhpWord/Reader/Word2007/AbstractPart.php
index 9a0e0c63..8f14daed 100644
--- a/src/PhpWord/Reader/Word2007/AbstractPart.php
+++ b/src/PhpWord/Reader/Word2007/AbstractPart.php
@@ -24,6 +24,8 @@ use PhpOffice\PhpWord\Shared\XMLReader;
* Abstract part reader
*
* This class is inherited by ODText reader
+ *
+ * @since 0.10.0
*/
abstract class AbstractPart
{
@@ -96,7 +98,7 @@ abstract class AbstractPart
*
* @todo Get font style for preserve text
*/
- protected function readParagraph(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart)
+ protected function readParagraph(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart = 'document')
{
// Paragraph style
$paragraphStyle = null;
@@ -246,7 +248,7 @@ abstract class AbstractPart
* @param mixed $parent
* @param string $docPart
*/
- protected function readTable(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart)
+ protected function readTable(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart = 'document')
{
// Table style
$tblStyle = null;
@@ -451,17 +453,9 @@ abstract class AbstractPart
$attribute = ($attribute === null) ? 'w:val' : $attribute;
$attributeValue = $xmlReader->getAttribute($attribute, $node);
- // Assign style value based on conversion model
- if ($method == self::READ_VALUE) {
- $styles[$styleProp] = $attributeValue;
- } elseif ($method == self::READ_SIZE) {
- $styles[$styleProp] = $attributeValue / 2;
- } elseif ($method == self::READ_TRUE) {
- $styles[$styleProp] = true;
- } elseif ($method == self::READ_FALSE) {
- $styles[$styleProp] = false;
- } elseif ($method == self::READ_EQUAL && $attributeValue == $expected) {
- $styles[$styleProp] = true;
+ $styleValue = $this->readStyleDef($method, $attributeValue, $expected);
+ if ($styleValue !== null) {
+ $styles[$styleProp] = $styleValue;
}
}
}
@@ -469,6 +463,31 @@ abstract class AbstractPart
return $styles;
}
+ /**
+ * Return style definition based on conversion method
+ *
+ * @param string $method
+ * @param mixed $attributeValue
+ * @param mixed $expected
+ * @return mixed
+ */
+ private function readStyleDef($method, $attributeValue, $expected)
+ {
+ $style = $attributeValue;
+
+ if ($method == self::READ_SIZE) {
+ $style = $attributeValue / 2;
+ } elseif ($method == self::READ_TRUE) {
+ $style = true;
+ } elseif ($method == self::READ_FALSE) {
+ $style = false;
+ } elseif ($method == self::READ_EQUAL && $attributeValue == $expected) {
+ $style = true;
+ }
+
+ return $style;
+ }
+
/**
* Returns the target of image, object, or link as stored in ::readMainRels
*
diff --git a/src/PhpWord/Reader/Word2007/DocPropsApp.php b/src/PhpWord/Reader/Word2007/DocPropsApp.php
index 06b58bcb..ddbe474f 100644
--- a/src/PhpWord/Reader/Word2007/DocPropsApp.php
+++ b/src/PhpWord/Reader/Word2007/DocPropsApp.php
@@ -19,6 +19,8 @@ namespace PhpOffice\PhpWord\Reader\Word2007;
/**
* Extended properties reader
+ *
+ * @since 0.10.0
*/
class DocPropsApp extends DocPropsCore
{
diff --git a/src/PhpWord/Reader/Word2007/DocPropsCore.php b/src/PhpWord/Reader/Word2007/DocPropsCore.php
index ae59ad51..0b92b64d 100644
--- a/src/PhpWord/Reader/Word2007/DocPropsCore.php
+++ b/src/PhpWord/Reader/Word2007/DocPropsCore.php
@@ -22,6 +22,8 @@ use PhpOffice\PhpWord\Shared\XMLReader;
/**
* Core properties reader
+ *
+ * @since 0.10.0
*/
class DocPropsCore extends AbstractPart
{
diff --git a/src/PhpWord/Reader/Word2007/DocPropsCustom.php b/src/PhpWord/Reader/Word2007/DocPropsCustom.php
index 969118b1..efbbfaa5 100644
--- a/src/PhpWord/Reader/Word2007/DocPropsCustom.php
+++ b/src/PhpWord/Reader/Word2007/DocPropsCustom.php
@@ -23,6 +23,8 @@ use PhpOffice\PhpWord\Shared\XMLReader;
/**
* Custom properties reader
+ *
+ * @since 0.11.0
*/
class DocPropsCustom extends AbstractPart
{
diff --git a/src/PhpWord/Reader/Word2007/Document.php b/src/PhpWord/Reader/Word2007/Document.php
index 3ac4e0eb..80d2dde2 100644
--- a/src/PhpWord/Reader/Word2007/Document.php
+++ b/src/PhpWord/Reader/Word2007/Document.php
@@ -19,12 +19,23 @@ namespace PhpOffice\PhpWord\Reader\Word2007;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Shared\XMLReader;
+use PhpOffice\PhpWord\Element\Section;
/**
* Document reader
+ *
+ * @since 0.10.0
+ * @SuppressWarnings(PHPMD.UnusedPrivateMethod) For readWPNode
*/
class Document extends AbstractPart
{
+ /**
+ * PhpWord object
+ *
+ * @var \PhpOffice\PhpWord\PhpWord
+ */
+ private $phpWord;
+
/**
* Read document.xml
*
@@ -32,45 +43,18 @@ class Document extends AbstractPart
*/
public function read(PhpWord &$phpWord)
{
+ $this->phpWord = $phpWord;
$xmlReader = new XMLReader();
$xmlReader->getDomFromZip($this->docFile, $this->xmlFile);
+ $readMethods = array('w:p' => 'readWPNode', 'w:tbl' => 'readTable', 'w:sectPr' => 'readWSectPrNode');
$nodes = $xmlReader->getElements('w:body/*');
if ($nodes->length > 0) {
- $section = $phpWord->addSection();
+ $section = $this->phpWord->addSection();
foreach ($nodes as $node) {
- switch ($node->nodeName) {
-
- case 'w:p': // Paragraph
- // Page break
- // @todo
- if ($xmlReader->getAttribute('w:type', $node, 'w:r/w:br') == 'page') {
- $section->addPageBreak(); // PageBreak
- }
-
- // Paragraph
- $this->readParagraph($xmlReader, $node, $section, 'document');
- // Section properties
- if ($xmlReader->elementExists('w:pPr/w:sectPr', $node)) {
- $settingsNode = $xmlReader->getElement('w:pPr/w:sectPr', $node);
- if (!is_null($settingsNode)) {
- $settings = $this->readSectionStyle($xmlReader, $settingsNode);
- $section->setSettings($settings);
- $this->readHeaderFooter($settings, $section);
- }
- $section = $phpWord->addSection();
- }
- break;
-
- case 'w:tbl': // Table
- $this->readTable($xmlReader, $node, $section, 'document');
- break;
-
- case 'w:sectPr': // Last section
- $settings = $this->readSectionStyle($xmlReader, $node);
- $section->setSettings($settings);
- $this->readHeaderFooter($settings, $section);
- break;
+ if (array_key_exists($node->nodeName, $readMethods)) {
+ $readMethod = $readMethods[$node->nodeName];
+ $this->$readMethod($xmlReader, $node, $section);
}
}
}
@@ -82,14 +66,16 @@ class Document extends AbstractPart
* @param array $settings
* @param \PhpOffice\PhpWord\Element\Section $section
*/
- private function readHeaderFooter($settings, &$section)
+ private function readHeaderFooter($settings, Section &$section)
{
+ $readMethods = array('w:p' => 'readParagraph', 'w:tbl' => 'readTable');
+
if (is_array($settings) && array_key_exists('hf', $settings)) {
foreach ($settings['hf'] as $rId => $hfSetting) {
if (array_key_exists($rId, $this->rels['document'])) {
list($hfType, $xmlFile, $docPart) = array_values($this->rels['document'][$rId]);
- $method = "add{$hfType}";
- $hfObject = $section->$method($hfSetting['type']);
+ $addMethod = "add{$hfType}";
+ $hfObject = $section->$addMethod($hfSetting['type']);
// Read header/footer content
$xmlReader = new XMLReader();
@@ -97,15 +83,9 @@ class Document extends AbstractPart
$nodes = $xmlReader->getElements('*');
if ($nodes->length > 0) {
foreach ($nodes as $node) {
- switch ($node->nodeName) {
-
- case 'w:p': // Paragraph
- $this->readParagraph($xmlReader, $node, $hfObject, $docPart);
- break;
-
- case 'w:tbl': // Table
- $this->readTable($xmlReader, $node, $hfObject, $docPart);
- break;
+ if (array_key_exists($node->nodeName, $readMethods)) {
+ $readMethod = $readMethods[$node->nodeName];
+ $this->$readMethod($xmlReader, $node, $hfObject, $docPart);
}
}
}
@@ -155,4 +135,47 @@ class Document extends AbstractPart
return $styles;
}
+
+ /**
+ * Read w:p node
+ *
+ * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
+ * @param \DOMElement $node
+ * @param \PhpOffice\PhpWord\Element\Section $section
+ *
+ * @todo
+ */
+ private function readWPNode(XMLReader $xmlReader, \DOMElement $node, Section &$section)
+ {
+ // Page break
+ if ($xmlReader->getAttribute('w:type', $node, 'w:r/w:br') == 'page') {
+ $section->addPageBreak(); // PageBreak
+ }
+
+ // Paragraph
+ $this->readParagraph($xmlReader, $node, $section);
+
+ // Section properties
+ if ($xmlReader->elementExists('w:pPr/w:sectPr', $node)) {
+ $sectPrNode = $xmlReader->getElement('w:pPr/w:sectPr', $node);
+ if ($sectPrNode !== null) {
+ $this->readWSectPrNode($xmlReader, $sectPrNode, $section);
+ }
+ $section = $this->phpWord->addSection();
+ }
+ }
+
+ /**
+ * Read w:sectPr node
+ *
+ * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
+ * @param \DOMElement $node
+ * @param \PhpOffice\PhpWord\Element\Section $section
+ */
+ private function readWSectPrNode(XMLReader $xmlReader, \DOMElement $node, Section &$section)
+ {
+ $settings = $this->readSectionStyle($xmlReader, $node);
+ $section->setSettings($settings);
+ $this->readHeaderFooter($settings, $section);
+ }
}
diff --git a/src/PhpWord/Reader/Word2007/Endnotes.php b/src/PhpWord/Reader/Word2007/Endnotes.php
index 02bf9e9e..c493c347 100644
--- a/src/PhpWord/Reader/Word2007/Endnotes.php
+++ b/src/PhpWord/Reader/Word2007/Endnotes.php
@@ -19,6 +19,8 @@ namespace PhpOffice\PhpWord\Reader\Word2007;
/**
* Endnotes reader
+ *
+ * @since 0.10.0
*/
class Endnotes extends Footnotes
{
diff --git a/src/PhpWord/Reader/Word2007/Footnotes.php b/src/PhpWord/Reader/Word2007/Footnotes.php
index 7aadf6c0..47713cfb 100644
--- a/src/PhpWord/Reader/Word2007/Footnotes.php
+++ b/src/PhpWord/Reader/Word2007/Footnotes.php
@@ -22,6 +22,8 @@ use PhpOffice\PhpWord\Shared\XMLReader;
/**
* Footnotes reader
+ *
+ * @since 0.10.0
*/
class Footnotes extends AbstractPart
{
diff --git a/src/PhpWord/Reader/Word2007/Numbering.php b/src/PhpWord/Reader/Word2007/Numbering.php
index 81939ae8..2dd3f521 100644
--- a/src/PhpWord/Reader/Word2007/Numbering.php
+++ b/src/PhpWord/Reader/Word2007/Numbering.php
@@ -22,6 +22,8 @@ use PhpOffice\PhpWord\Shared\XMLReader;
/**
* Numbering reader
+ *
+ * @since 0.10.0
*/
class Numbering extends AbstractPart
{
diff --git a/src/PhpWord/Reader/Word2007/Styles.php b/src/PhpWord/Reader/Word2007/Styles.php
index dd73f410..7dc4b6ea 100644
--- a/src/PhpWord/Reader/Word2007/Styles.php
+++ b/src/PhpWord/Reader/Word2007/Styles.php
@@ -22,6 +22,8 @@ use PhpOffice\PhpWord\Shared\XMLReader;
/**
* Styles reader
+ *
+ * @since 0.10.0
*/
class Styles extends AbstractPart
{
diff --git a/src/PhpWord/Settings.php b/src/PhpWord/Settings.php
index 8d9e2ace..cb74389a 100644
--- a/src/PhpWord/Settings.php
+++ b/src/PhpWord/Settings.php
@@ -344,7 +344,7 @@ class Settings
// Parse config file
$config = array();
if ($configFile !== null) {
- $config = parse_ini_file($configFile);
+ $config = @parse_ini_file($configFile);
if ($config === false) {
return $config;
}
diff --git a/src/PhpWord/Shared/String.php b/src/PhpWord/Shared/String.php
index 259e904c..1e4504cc 100644
--- a/src/PhpWord/Shared/String.php
+++ b/src/PhpWord/Shared/String.php
@@ -88,12 +88,26 @@ class String
/**
* Returns unicode from UTF8 text
*
+ * The function is splitted to reduce cyclomatic complexity
+ *
* @param string $text UTF8 text
* @return string Unicode text
* @since 0.11.0
- * @link http://www.randomchaos.com/documents/?source=php_and_unicode
*/
public static function toUnicode($text)
+ {
+ return self::unicodeToEntities(self::utf8ToUnicode($text));
+ }
+
+ /**
+ * Returns unicode array from UTF8 text
+ *
+ * @param string $text UTF8 text
+ * @return array
+ * @since 0.11.0
+ * @link http://www.randomchaos.com/documents/?source=php_and_unicode
+ */
+ private static function utf8ToUnicode($text)
{
$unicode = array();
$values = array();
@@ -122,8 +136,21 @@ class String
}
}
- // Converts text with utf8 characters into rtf utf8 entites preserving ascii
+ return $unicode;
+ }
+
+ /**
+ * Returns entites from unicode array
+ *
+ * @param array $unicode
+ * @return string
+ * @since 0.11.0
+ * @link http://www.randomchaos.com/documents/?source=php_and_unicode
+ */
+ private static function unicodeToEntities($unicode)
+ {
$entities = '';
+
foreach ($unicode as $value) {
if ($value != 65279) {
$entities .= $value > 127 ? '\uc0{\u' . $value . '}' : chr($value);
diff --git a/src/PhpWord/Shared/XMLWriter.php b/src/PhpWord/Shared/XMLWriter.php
index 683e09c0..81e8e286 100644
--- a/src/PhpWord/Shared/XMLWriter.php
+++ b/src/PhpWord/Shared/XMLWriter.php
@@ -58,11 +58,6 @@ class XMLWriter
*/
public function __construct($tempLocation = self::STORAGE_MEMORY, $tempFolder = './')
{
- // Define date format
- if (!defined('DATE_W3C')) {
- define('DATE_W3C', 'Y-m-d\TH:i:sP');
- }
-
// Create internal XMLWriter
$this->xmlWriter = new \XMLWriter();
@@ -73,9 +68,8 @@ class XMLWriter
// Create temporary filename
$this->tempFile = @tempnam($tempFolder, 'xml');
- // Open storage
+ // Fallback to memory when temporary file cannot be used
if ($this->xmlWriter->openUri($this->tempFile) === false) {
- // Fallback to memory...
$this->xmlWriter->openMemory();
}
}
@@ -110,9 +104,16 @@ class XMLWriter
*
* @param mixed $function
* @param mixed $args
+ * @throws \BadMethodCallException
*/
public function __call($function, $args)
{
+ // Catch exception
+ if (method_exists($this->xmlWriter, $function) === false) {
+ throw new \BadMethodCallException("Method '{$function}' does not exists.");
+ }
+
+ // Run method
try {
@call_user_func_array(array($this->xmlWriter, $function), $args);
} catch (\Exception $ex) {
diff --git a/src/PhpWord/Style/AbstractStyle.php b/src/PhpWord/Style/AbstractStyle.php
index 1b916934..8167c4d2 100644
--- a/src/PhpWord/Style/AbstractStyle.php
+++ b/src/PhpWord/Style/AbstractStyle.php
@@ -265,7 +265,7 @@ abstract class AbstractStyle
{
if ($value != null && trim($value) != '' && !empty($enum) && !in_array($value, $enum)) {
throw new \InvalidArgumentException('Invalid style value.');
- } elseif (is_null($value) || trim($value) == '') {
+ } elseif ($value === null || trim($value) == '') {
$value = $default;
}
diff --git a/src/PhpWord/Style/Border.php b/src/PhpWord/Style/Border.php
index f0fd8650..84116d7a 100644
--- a/src/PhpWord/Style/Border.php
+++ b/src/PhpWord/Style/Border.php
@@ -81,7 +81,7 @@ class Border extends AbstractStyle
/**
* Get border size
*
- * @return int[]
+ * @return integer[]
*/
public function getBorderSize()
{
diff --git a/src/PhpWord/Style/Cell.php b/src/PhpWord/Style/Cell.php
index 95ed13b4..2d1b88d0 100644
--- a/src/PhpWord/Style/Cell.php
+++ b/src/PhpWord/Style/Cell.php
@@ -144,7 +144,7 @@ class Cell extends Border
*/
public function getBgColor()
{
- if (!is_null($this->shading)) {
+ if ($this->shading !== null) {
return $this->shading->getFill();
} else {
return null;
diff --git a/src/PhpWord/Style/Font.php b/src/PhpWord/Style/Font.php
index 07eebdb2..0775b8b3 100644
--- a/src/PhpWord/Style/Font.php
+++ b/src/PhpWord/Style/Font.php
@@ -540,7 +540,7 @@ class Font extends AbstractStyle
*/
public function getBgColor()
{
- if (!is_null($this->shading)) {
+ if ($this->shading !== null) {
return $this->shading->getFill();
} else {
return null;
diff --git a/src/PhpWord/Style/Image.php b/src/PhpWord/Style/Image.php
index fc9f9630..33c85ddf 100644
--- a/src/PhpWord/Style/Image.php
+++ b/src/PhpWord/Style/Image.php
@@ -61,6 +61,7 @@ class Image extends AbstractStyle
const POSITION_RELATIVE_TO_PAGE = 'page';
const POSITION_RELATIVE_TO_COLUMN = 'column'; // horizontal only
const POSITION_RELATIVE_TO_CHAR = 'char'; // horizontal only
+ const POSITION_RELATIVE_TO_TEXT = 'text'; // vertical only
const POSITION_RELATIVE_TO_LINE = 'line'; // vertical only
const POSITION_RELATIVE_TO_LMARGIN = 'left-margin-area'; // horizontal only
const POSITION_RELATIVE_TO_RMARGIN = 'right-margin-area'; // horizontal only
@@ -103,14 +104,14 @@ class Image extends AbstractStyle
*
* @var int
*/
- private $marginTop;
+ private $marginTop = 0;
/**
* Margin Left
*
* @var int
*/
- private $marginLeft;
+ private $marginLeft = 0;
/**
* Wrapping style
@@ -247,9 +248,9 @@ class Image extends AbstractStyle
* @param int $value
* @return self
*/
- public function setMarginTop($value = null)
+ public function setMarginTop($value = 0)
{
- $this->marginTop = $value;
+ $this->marginTop = $this->setIntVal($value, 0);
return $this;
}
@@ -270,9 +271,9 @@ class Image extends AbstractStyle
* @param int $value
* @return self
*/
- public function setMarginLeft($value = null)
+ public function setMarginLeft($value = 0)
{
- $this->marginLeft = $value;
+ $this->marginLeft = $this->setIntVal($value, 0);
return $this;
}
@@ -352,7 +353,7 @@ class Image extends AbstractStyle
{
$enum = array(
self::POSITION_HORIZONTAL_LEFT, self::POSITION_HORIZONTAL_CENTER,
- self::POSITION_HORIZONTAL_RIGHT,
+ self::POSITION_HORIZONTAL_RIGHT, self::POSITION_ABSOLUTE
);
$this->posHorizontal = $this->setEnumVal($alignment, $enum, $this->posHorizontal);
@@ -381,7 +382,7 @@ class Image extends AbstractStyle
$enum = array(
self::POSITION_VERTICAL_TOP, self::POSITION_VERTICAL_CENTER,
self::POSITION_VERTICAL_BOTTOM, self::POSITION_VERTICAL_INSIDE,
- self::POSITION_VERTICAL_OUTSIDE,
+ self::POSITION_VERTICAL_OUTSIDE, self::POSITION_ABSOLUTE
);
$this->posVertical = $this->setEnumVal($alignment, $enum, $this->posVertical);
@@ -439,7 +440,7 @@ class Image extends AbstractStyle
{
$enum = array(
self::POSITION_RELATIVE_TO_MARGIN, self::POSITION_RELATIVE_TO_PAGE,
- self::POSITION_RELATIVE_TO_LINE,
+ self::POSITION_RELATIVE_TO_TEXT, self::POSITION_RELATIVE_TO_LINE,
self::POSITION_RELATIVE_TO_TMARGIN, self::POSITION_RELATIVE_TO_BMARGIN,
self::POSITION_RELATIVE_TO_IMARGIN, self::POSITION_RELATIVE_TO_OMARGIN,
);
diff --git a/src/PhpWord/Style/LineNumbering.php b/src/PhpWord/Style/LineNumbering.php
index d49c7f4e..b93ce03f 100644
--- a/src/PhpWord/Style/LineNumbering.php
+++ b/src/PhpWord/Style/LineNumbering.php
@@ -20,11 +20,12 @@ namespace PhpOffice\PhpWord\Style;
/**
* Line numbering style
*
- * @link http://www.schemacentral.com/sc/ooxml/e-w_lnNumType-1.html
+ * @link http://www.schemacentral.com/sc/ooxml/t-w_CT_LineNumber.html
* @since 0.10.0
*/
class LineNumbering extends AbstractStyle
{
+ /** @const string Line numbering restart setting http://www.schemacentral.com/sc/ooxml/a-w_restart-1.html */
const LINE_NUMBERING_CONTINUOUS = 'continuous';
const LINE_NUMBERING_NEW_PAGE = 'newPage';
const LINE_NUMBERING_NEW_SECTION = 'newSection';
diff --git a/src/PhpWord/Style/ListItem.php b/src/PhpWord/Style/ListItem.php
index 554d75b0..a689c691 100644
--- a/src/PhpWord/Style/ListItem.php
+++ b/src/PhpWord/Style/ListItem.php
@@ -65,7 +65,7 @@ class ListItem extends AbstractStyle
*/
public function __construct($numStyle = null)
{
- if (!is_null($numStyle)) {
+ if ($numStyle !== null) {
$this->setNumStyle($numStyle);
} else {
$this->setListType();
@@ -149,7 +149,7 @@ class ListItem extends AbstractStyle
{
// Check if legacy style already registered in global Style collection
$numStyle = "PHPWordList{$this->listType}";
- if (!is_null(Style::getStyle($numStyle))) {
+ if (Style::getStyle($numStyle) !== null) {
$this->setNumStyle($numStyle);
return;
}
diff --git a/src/PhpWord/Style/Paragraph.php b/src/PhpWord/Style/Paragraph.php
index 8609b5ab..e70833ca 100644
--- a/src/PhpWord/Style/Paragraph.php
+++ b/src/PhpWord/Style/Paragraph.php
@@ -171,7 +171,7 @@ class Paragraph extends AbstractStyle
*/
public function getSpaceBefore()
{
- if (!is_null($this->spacing)) {
+ if ($this->spacing !== null) {
return $this->spacing->getBefore();
} else {
return null;
@@ -196,7 +196,7 @@ class Paragraph extends AbstractStyle
*/
public function getSpaceAfter()
{
- if (!is_null($this->spacing)) {
+ if ($this->spacing !== null) {
return $this->spacing->getAfter();
} else {
return null;
@@ -221,7 +221,7 @@ class Paragraph extends AbstractStyle
*/
public function getSpacing()
{
- if (!is_null($this->spacing)) {
+ if ($this->spacing !== null) {
return $this->spacing->getLine();
} else {
return null;
@@ -278,7 +278,7 @@ class Paragraph extends AbstractStyle
*/
public function getIndent()
{
- if (!is_null($this->indentation)) {
+ if ($this->indentation !== null) {
return $this->indentation->getLeft();
} else {
return null;
@@ -303,7 +303,7 @@ class Paragraph extends AbstractStyle
*/
public function getHanging()
{
- if (!is_null($this->indentation)) {
+ if ($this->indentation !== null) {
return $this->indentation->getHanging();
} else {
return null;
diff --git a/src/PhpWord/Style/Table.php b/src/PhpWord/Style/Table.php
index 68b53463..24f50667 100644
--- a/src/PhpWord/Style/Table.php
+++ b/src/PhpWord/Style/Table.php
@@ -169,7 +169,7 @@ class Table extends Border
*/
public function getBgColor()
{
- if (!is_null($this->shading)) {
+ if ($this->shading !== null) {
return $this->shading->getFill();
}
@@ -192,7 +192,7 @@ class Table extends Border
/**
* Get TLRBHV Border Size
*
- * @return int[]
+ * @return integer[]
*/
public function getBorderSize()
{
diff --git a/src/PhpWord/Style/TextBox.php b/src/PhpWord/Style/TextBox.php
index da19cd10..9f0a1dde 100644
--- a/src/PhpWord/Style/TextBox.php
+++ b/src/PhpWord/Style/TextBox.php
@@ -162,7 +162,7 @@ class TextBox extends Image
/**
* Get cell margin
*
- * @return int[]
+ * @return integer[]
*/
public function getInnerMargin()
{
@@ -179,7 +179,7 @@ class TextBox extends Image
$hasInnerMargins = false;
$margins = $this->getInnerMargin();
for ($i = 0; $i < count($margins); $i++) {
- if (!is_null($margins[$i])) {
+ if ($margins[$i] !== null) {
$hasInnerMargins = true;
}
}
diff --git a/src/PhpWord/Writer/AbstractWriter.php b/src/PhpWord/Writer/AbstractWriter.php
index 52a1a28c..367b7729 100644
--- a/src/PhpWord/Writer/AbstractWriter.php
+++ b/src/PhpWord/Writer/AbstractWriter.php
@@ -258,7 +258,7 @@ abstract class AbstractWriter implements WriterInterface
*
* @param string $filename
* @return \PhpOffice\PhpWord\Shared\ZipArchive
- * @throws \PhpOffice\PhpWord\Exception\Exception
+ * @throws \Exception
*/
protected function getZipArchive($filename)
{
@@ -271,13 +271,46 @@ abstract class AbstractWriter implements WriterInterface
$zip = new ZipArchive();
if ($zip->open($filename, ZipArchive::OVERWRITE) !== true) {
if ($zip->open($filename, ZipArchive::CREATE) !== true) {
- throw new Exception("Could not open " . $filename . " for writing.");
+ throw new \Exception("Could not open '{$filename}' for writing.");
}
}
return $zip;
}
+ /**
+ * Open file for writing
+ *
+ * @param string $filename
+ * @return resource
+ * @throws \Exception
+ * @since 0.11.0
+ */
+ protected function openFile($filename)
+ {
+ $filename = $this->getTempFile($filename);
+ $fileHandle = fopen($filename, 'w');
+ if ($fileHandle === false) {
+ throw new \Exception("Could not open '{$filename}' for writing.");
+ }
+
+ return $fileHandle;
+ }
+
+ /**
+ * Write content to file
+ *
+ * @param resource $fileHandle
+ * @param string $content
+ * @since 0.11.0
+ */
+ protected function writeFile(&$fileHandle, $content)
+ {
+ fwrite($fileHandle, $content);
+ fclose($fileHandle);
+ $this->cleanupTempFile();
+ }
+
/**
* Add files to package
*
diff --git a/src/PhpWord/Writer/HTML.php b/src/PhpWord/Writer/HTML.php
index 98cab55e..29173ff2 100644
--- a/src/PhpWord/Writer/HTML.php
+++ b/src/PhpWord/Writer/HTML.php
@@ -17,17 +17,7 @@
namespace PhpOffice\PhpWord\Writer;
-use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\PhpWord;
-use PhpOffice\PhpWord\Settings;
-use PhpOffice\PhpWord\Style;
-use PhpOffice\PhpWord\Style\Font;
-use PhpOffice\PhpWord\Style\Paragraph;
-use PhpOffice\PhpWord\Writer\HTML\Element\Container;
-use PhpOffice\PhpWord\Writer\HTML\Element\TextRun as TextRunWriter;
-use PhpOffice\PhpWord\Writer\HTML\Style\Font as FontStyleWriter;
-use PhpOffice\PhpWord\Writer\HTML\Style\Generic as GenericStyleWriter;
-use PhpOffice\PhpWord\Writer\HTML\Style\Paragraph as ParagraphStyleWriter;
/**
* HTML writer
@@ -57,6 +47,17 @@ class HTML extends AbstractWriter implements WriterInterface
public function __construct(PhpWord $phpWord = null)
{
$this->setPhpWord($phpWord);
+
+ $this->parts = array('Head', 'Body');
+ foreach ($this->parts as $partName) {
+ $partClass = 'PhpOffice\\PhpWord\\Writer\\HTML\\Part\\' . $partName;
+ if (class_exists($partClass)) {
+ /** @var \PhpOffice\PhpWord\Writer\HTML\Part\AbstractPart $part Type hint */
+ $part = new $partClass();
+ $part->setParentWriter($this);
+ $this->writerParts[strtolower($partName)] = $part;
+ }
+ }
}
/**
@@ -67,182 +68,26 @@ class HTML extends AbstractWriter implements WriterInterface
*/
public function save($filename = null)
{
- $this->setTempDir(sys_get_temp_dir() . '/PHPWordWriter/');
- $hFile = fopen($filename, 'w');
- if ($hFile !== false) {
- fwrite($hFile, $this->writeDocument());
- fclose($hFile);
- } else {
- throw new Exception("Can't open file");
- }
- $this->clearTempDir();
- }
-
- /**
- * Get phpWord data
- *
- * @return string
- */
- public function writeDocument()
- {
- $content = '';
- $content .= '' . PHP_EOL;
- $content .= '' . PHP_EOL;
- $content .= '' . PHP_EOL;
- $content .= '' . PHP_EOL;
- $content .= $this->writeHead();
- $content .= '' . PHP_EOL;
- $content .= '' . PHP_EOL;
- $content .= $this->writeBody();
- $content .= $this->writeNotes();
- $content .= '' . PHP_EOL;
- $content .= '' . PHP_EOL;
-
- return $content;
- }
-
- /**
- * Generate HTML header
- *
- * @return string
- */
- private function writeHead()
- {
- $phpWord = $this->getPhpWord();
- $properties = $phpWord->getDocumentProperties();
- $propertiesMapping = array(
- 'creator' => 'author',
- 'title' => '',
- 'description' => '',
- 'subject' => '',
- 'keywords' => '',
- 'category' => '',
- 'company' => '',
- 'manager' => ''
- );
- $title = $properties->getTitle();
- $title = ($title != '') ? $title : 'PHPWord';
-
- $content = '';
- $content .= '' . PHP_EOL;
- $content .= '' . htmlspecialchars($title) . '' . PHP_EOL;
- foreach ($propertiesMapping as $key => $value) {
- $value = ($value == '') ? $key : $value;
- $method = "get" . $key;
- if ($properties->$method() != '') {
- $content .= '' . PHP_EOL;
- }
- }
- $content .= $this->writeStyles();
-
- return $content;
+ $fileHandle = $this->openFile($filename);
+ $this->writeFile($fileHandle, $this->getContent());
}
/**
* Get content
*
* @return string
+ * @since 0.11.0
*/
- private function writeBody()
+ public function getContent()
{
- $phpWord = $this->getPhpWord();
$content = '';
- $sections = $phpWord->getSections();
- $countSections = count($sections);
-
- if ($countSections > 0) {
- foreach ($sections as $section) {
- $writer = new Container($this, $section);
- $content .= $writer->write();
- }
- }
-
- return $content;
- }
-
- /**
- * Get styles
- *
- * @return string
- */
- private function writeStyles()
- {
- $css = '' . PHP_EOL;
-
- return $css;
- }
-
- /**
- * Write footnote/endnote contents as textruns
- */
- private function writeNotes()
- {
- $phpWord = $this->getPhpWord();
- $content = PHP_EOL;
-
- if (!empty($this->notes)) {
- $content .= "
" . PHP_EOL;
- foreach ($this->notes as $noteId => $noteMark) {
- list($noteType, $noteTypeId) = explode('-', $noteMark);
- $method = 'get' . ($noteType == 'endnote' ? 'Endnotes' : 'Footnotes');
- $collection = $phpWord->$method()->getItems();
-
- if (array_key_exists($noteTypeId, $collection)) {
- $element = $collection[$noteTypeId];
- $noteAnchor = "";
- $noteAnchor .= "{$noteId}";
-
- $writer = new TextRunWriter($this, $element);
- $writer->setOpeningText($noteAnchor);
- $content .= $writer->write();
- }
- }
- }
+ $content .= '' . PHP_EOL;
+ $content .= '' . PHP_EOL;
+ $content .= '' . PHP_EOL;
+ $content .= $this->getWriterPart('Head')->write();
+ $content .= $this->getWriterPart('Body')->write();
+ $content .= '' . PHP_EOL;
return $content;
}
@@ -277,4 +122,16 @@ class HTML extends AbstractWriter implements WriterInterface
{
$this->notes[$noteId] = $noteMark;
}
+
+ /**
+ * Write document
+ *
+ * @return string
+ * @deprecated 0.11.0
+ * @codeCoverageIgnore
+ */
+ public function writeDocument()
+ {
+ return $this->getContent();
+ }
}
diff --git a/src/PhpWord/Writer/HTML/Element/Image.php b/src/PhpWord/Writer/HTML/Element/Image.php
index dafc1c38..ab78990b 100644
--- a/src/PhpWord/Writer/HTML/Element/Image.php
+++ b/src/PhpWord/Writer/HTML/Element/Image.php
@@ -18,7 +18,6 @@
namespace PhpOffice\PhpWord\Writer\HTML\Element;
use PhpOffice\PhpWord\Element\Image as ImageElement;
-use PhpOffice\PhpWord\Shared\ZipArchive;
use PhpOffice\PhpWord\Writer\HTML\Style\Image as ImageStyleWriter;
/**
@@ -43,10 +42,11 @@ class Image extends Text
$content = '';
if (!$parentWriter->isPdf()) {
- $imageData = $this->getBase64ImageData($this->element);
- if (!is_null($imageData)) {
+ $imageData = $this->element->getImageStringData(true);
+ if ($imageData !== null) {
$styleWriter = new ImageStyleWriter($this->element->getStyle());
$style = $styleWriter->write();
+ $imageData = 'data:' . $this->element->getImageType() . ';base64,' . $imageData;
$content .= $this->writeOpening();
$content .= "
";
@@ -56,60 +56,4 @@ class Image extends Text
return $content;
}
-
- /**
- * Get Base64 image data
- *
- * @param \PhpOffice\PhpWord\Element\Image $element
- * @return string|null
- */
- private function getBase64ImageData(ImageElement $element)
- {
- $source = $element->getSource();
- $imageType = $element->getImageType();
- $imageData = null;
- $imageBinary = null;
- $actualSource = null;
-
- // Get actual source from archive image or other source
- // Return null if not found
- if ($element->getSourceType() == ImageElement::SOURCE_ARCHIVE) {
- $source = substr($source, 6);
- list($zipFilename, $imageFilename) = explode('#', $source);
-
- $zip = new ZipArchive();
- if ($zip->open($zipFilename) !== false) {
- if ($zip->locateName($imageFilename)) {
- $zip->extractTo($this->parentWriter->getTempDir(), $imageFilename);
- $actualSource = $this->parentWriter->getTempDir() . DIRECTORY_SEPARATOR . $imageFilename;
- }
- }
- $zip->close();
- } else {
- $actualSource = $source;
- }
- if (is_null($actualSource)) {
- return null;
- }
-
- // Read image binary data and convert into Base64
- if ($element->getSourceType() == ImageElement::SOURCE_GD) {
- $imageResource = call_user_func($element->getImageCreateFunction(), $actualSource);
- ob_start();
- call_user_func($element->getImageFunction(), $imageResource);
- $imageBinary = ob_get_contents();
- ob_end_clean();
- } else {
- if ($fileHandle = fopen($actualSource, 'rb', false)) {
- $imageBinary = fread($fileHandle, filesize($actualSource));
- fclose($fileHandle);
- }
- }
- if (!is_null($imageBinary)) {
- $base64 = chunk_split(base64_encode($imageBinary));
- $imageData = 'data:' . $imageType . ';base64,' . $base64;
- }
-
- return $imageData;
- }
}
diff --git a/src/PhpWord/Writer/HTML/Part/AbstractPart.php b/src/PhpWord/Writer/HTML/Part/AbstractPart.php
new file mode 100644
index 00000000..319aa20f
--- /dev/null
+++ b/src/PhpWord/Writer/HTML/Part/AbstractPart.php
@@ -0,0 +1,68 @@
+parentWriter = $writer;
+ }
+
+ /**
+ * Get parent writer
+ *
+ * @return \PhpOffice\PhpWord\Writer\AbstractWriter
+ * @throws \PhpOffice\PhpWord\Exception\Exception
+ */
+ public function getParentWriter()
+ {
+ if ($this->parentWriter !== null) {
+ return $this->parentWriter;
+ } else {
+ throw new Exception('No parent WriterInterface assigned.');
+ }
+ }
+}
diff --git a/src/PhpWord/Writer/HTML/Part/Body.php b/src/PhpWord/Writer/HTML/Part/Body.php
new file mode 100644
index 00000000..4fd61a83
--- /dev/null
+++ b/src/PhpWord/Writer/HTML/Part/Body.php
@@ -0,0 +1,89 @@
+getParentWriter()->getPhpWord();
+
+ $content = '';
+
+ $content .= '' . PHP_EOL;
+ $sections = $phpWord->getSections();
+ foreach ($sections as $section) {
+ $writer = new Container($this->getParentWriter(), $section);
+ $content .= $writer->write();
+ }
+
+ $content .= $this->writeNotes();
+ $content .= '' . PHP_EOL;
+
+ return $content;
+ }
+
+ /**
+ * Write footnote/endnote contents as textruns
+ *
+ * @return string
+ */
+ private function writeNotes()
+ {
+ /** @var \PhpOffice\PhpWord\Writer\HTML $parentWriter Type hint */
+ $parentWriter = $this->getParentWriter();
+ $phpWord = $parentWriter->getPhpWord();
+ $notes = $parentWriter->getNotes();
+
+ $content = '';
+
+ if (!empty($notes)) {
+ $content .= "
" . PHP_EOL;
+ foreach ($notes as $noteId => $noteMark) {
+ list($noteType, $noteTypeId) = explode('-', $noteMark);
+ $method = 'get' . ($noteType == 'endnote' ? 'Endnotes' : 'Footnotes');
+ $collection = $phpWord->$method()->getItems();
+
+ if (array_key_exists($noteTypeId, $collection)) {
+ $element = $collection[$noteTypeId];
+ $noteAnchor = "";
+ $noteAnchor .= "{$noteId}";
+
+ $writer = new TextRunWriter($this->getParentWriter(), $element);
+ $writer->setOpeningText($noteAnchor);
+ $content .= $writer->write();
+ }
+ }
+ }
+
+ return $content;
+ }
+}
diff --git a/src/PhpWord/Writer/HTML/Part/Head.php b/src/PhpWord/Writer/HTML/Part/Head.php
new file mode 100644
index 00000000..edbc8dcf
--- /dev/null
+++ b/src/PhpWord/Writer/HTML/Part/Head.php
@@ -0,0 +1,128 @@
+getParentWriter()->getPhpWord()->getDocumentProperties();
+ $propertiesMapping = array(
+ 'creator' => 'author',
+ 'title' => '',
+ 'description' => '',
+ 'subject' => '',
+ 'keywords' => '',
+ 'category' => '',
+ 'company' => '',
+ 'manager' => ''
+ );
+ $title = $docProps->getTitle();
+ $title = ($title != '') ? $title : 'PHPWord';
+
+ $content = '';
+
+ $content .= '' . PHP_EOL;
+ $content .= '' . PHP_EOL;
+ $content .= '' . htmlspecialchars($title) . '' . PHP_EOL;
+ foreach ($propertiesMapping as $key => $value) {
+ $value = ($value == '') ? $key : $value;
+ $method = "get" . $key;
+ if ($docProps->$method() != '') {
+ $content .= '' . PHP_EOL;
+ }
+ }
+ $content .= $this->writeStyles();
+ $content .= '' . PHP_EOL;
+
+ return $content;
+ }
+ /**
+ * Get styles
+ *
+ * @return string
+ */
+ private function writeStyles()
+ {
+ $css = '' . PHP_EOL;
+
+ return $css;
+ }
+}
diff --git a/src/PhpWord/Writer/ODText/Part/AbstractPart.php b/src/PhpWord/Writer/ODText/Part/AbstractPart.php
index d5397ffd..31118ef9 100644
--- a/src/PhpWord/Writer/ODText/Part/AbstractPart.php
+++ b/src/PhpWord/Writer/ODText/Part/AbstractPart.php
@@ -28,6 +28,11 @@ use PhpOffice\PhpWord\Writer\Word2007\Part\AbstractPart as Word2007AbstractPart;
*/
abstract class AbstractPart extends Word2007AbstractPart
{
+ /**
+ * @var string Date format
+ */
+ protected $dateFormat = 'Y-m-d\TH:i:s.000';
+
/**
* Write common root attributes
*/
diff --git a/src/PhpWord/Writer/ODText/Part/Content.php b/src/PhpWord/Writer/ODText/Part/Content.php
index 900edfdd..f16937a0 100644
--- a/src/PhpWord/Writer/ODText/Part/Content.php
+++ b/src/PhpWord/Writer/ODText/Part/Content.php
@@ -115,12 +115,14 @@ class Content extends AbstractPart
$xmlWriter->startElement('office:automatic-styles');
$this->writeTextStyles($xmlWriter);
- foreach ($this->autoStyles as $element => $style) {
+ foreach ($this->autoStyles as $element => $styles) {
$writerClass = 'PhpOffice\\PhpWord\\Writer\\ODText\\Style\\' . $element;
+ foreach ($styles as $style) {
- /** @var \PhpOffice\PhpWord\Writer\ODText\Style\AbstractStyle $styleWriter Type hint */
- $styleWriter = new $writerClass($xmlWriter, $style);
- $styleWriter->write();
+ /** @var \PhpOffice\PhpWord\Writer\ODText\Style\AbstractStyle $styleWriter Type hint */
+ $styleWriter = new $writerClass($xmlWriter, $style);
+ $styleWriter->write();
+ }
}
$xmlWriter->endElement(); // office:automatic-styles
diff --git a/src/PhpWord/Writer/ODText/Part/Meta.php b/src/PhpWord/Writer/ODText/Part/Meta.php
index 50a38047..0d240a68 100644
--- a/src/PhpWord/Writer/ODText/Part/Meta.php
+++ b/src/PhpWord/Writer/ODText/Part/Meta.php
@@ -53,12 +53,12 @@ class Meta extends AbstractPart
$xmlWriter->writeElement('dc:subject', $docProps->getSubject());
$xmlWriter->writeElement('dc:description', $docProps->getDescription());
$xmlWriter->writeElement('dc:creator', $docProps->getLastModifiedBy());
- $xmlWriter->writeElement('dc:date', gmdate('Y-m-d\TH:i:s.000', $docProps->getModified()));
+ $xmlWriter->writeElement('dc:date', gmdate($this->dateFormat, $docProps->getModified()));
// Extended properties
$xmlWriter->writeElement('meta:generator', 'PHPWord');
$xmlWriter->writeElement('meta:initial-creator', $docProps->getCreator());
- $xmlWriter->writeElement('meta:creation-date', gmdate('Y-m-d\TH:i:s.000', $docProps->getCreated()));
+ $xmlWriter->writeElement('meta:creation-date', gmdate($this->dateFormat, $docProps->getCreated()));
$xmlWriter->writeElement('meta:keyword', $docProps->getKeywords());
// Category, company, and manager are put in meta namespace
diff --git a/src/PhpWord/Writer/PDF.php b/src/PhpWord/Writer/PDF.php
index 98dc1220..17865563 100644
--- a/src/PhpWord/Writer/PDF.php
+++ b/src/PhpWord/Writer/PDF.php
@@ -65,13 +65,13 @@ class PDF
* @param string $name Renderer library method name
* @param mixed[] $arguments Array of arguments to pass to the renderer method
* @return mixed Returned data from the PDF renderer wrapper method
- * @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function __call($name, $arguments)
{
- if ($this->renderer === null) {
- throw new Exception("PDF Rendering library has not been defined.");
- }
+ // Note: Commented because all exceptions should already be catched by `__construct`
+ // if ($this->renderer === null) {
+ // throw new Exception("PDF Rendering library has not been defined.");
+ // }
return call_user_func_array(array($this->renderer, $name), $arguments);
}
diff --git a/src/PhpWord/Writer/PDF/AbstractRenderer.php b/src/PhpWord/Writer/PDF/AbstractRenderer.php
index f7266d1a..83b02251 100644
--- a/src/PhpWord/Writer/PDF/AbstractRenderer.php
+++ b/src/PhpWord/Writer/PDF/AbstractRenderer.php
@@ -84,6 +84,7 @@ abstract class AbstractRenderer extends HTML
parent::__construct($phpWord);
$includeFile = Settings::getPdfRendererPath() . '/' . $this->includeFile;
if (file_exists($includeFile)) {
+ /** @noinspection PhpIncludeInspection Dynamic includes */
require_once $includeFile;
} else {
throw new Exception('Unable to load PDF Rendering library');
diff --git a/src/PhpWord/Writer/PDF/DomPDF.php b/src/PhpWord/Writer/PDF/DomPDF.php
index 4effc154..a40e2cea 100644
--- a/src/PhpWord/Writer/PDF/DomPDF.php
+++ b/src/PhpWord/Writer/PDF/DomPDF.php
@@ -50,7 +50,7 @@ class DomPDF extends AbstractRenderer implements WriterInterface
// Create PDF
$pdf = new \DOMPDF();
$pdf->set_paper(strtolower($paperSize), $orientation);
- $pdf->load_html($this->writeDocument());
+ $pdf->load_html($this->getContent());
$pdf->render();
// Write to file
diff --git a/src/PhpWord/Writer/PDF/MPDF.php b/src/PhpWord/Writer/PDF/MPDF.php
index d38d5b66..9d4e050c 100644
--- a/src/PhpWord/Writer/PDF/MPDF.php
+++ b/src/PhpWord/Writer/PDF/MPDF.php
@@ -61,7 +61,7 @@ class MPDF extends AbstractRenderer implements WriterInterface
$pdf->setKeywords($docProps->getKeywords());
$pdf->setCreator($docProps->getCreator());
- $pdf->writeHTML($this->writeDocument());
+ $pdf->writeHTML($this->getContent());
// Write to file
fwrite($fileHandle, $pdf->output($filename, 'S'));
diff --git a/src/PhpWord/Writer/PDF/TCPDF.php b/src/PhpWord/Writer/PDF/TCPDF.php
index 05f02756..669a2cdd 100644
--- a/src/PhpWord/Writer/PDF/TCPDF.php
+++ b/src/PhpWord/Writer/PDF/TCPDF.php
@@ -54,7 +54,7 @@ class TCPDF extends AbstractRenderer implements WriterInterface
$pdf->setPrintFooter(false);
$pdf->addPage();
$pdf->setFont($this->getFont());
- $pdf->writeHTML($this->writeDocument());
+ $pdf->writeHTML($this->getContent());
// Write document properties
$phpWord = $this->getPhpWord();
diff --git a/src/PhpWord/Writer/RTF.php b/src/PhpWord/Writer/RTF.php
index 315e6007..d7fed942 100644
--- a/src/PhpWord/Writer/RTF.php
+++ b/src/PhpWord/Writer/RTF.php
@@ -17,13 +17,7 @@
namespace PhpOffice\PhpWord\Writer;
-use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\PhpWord;
-use PhpOffice\PhpWord\Settings;
-use PhpOffice\PhpWord\Shared\Drawing;
-use PhpOffice\PhpWord\Style;
-use PhpOffice\PhpWord\Style\Font;
-use PhpOffice\PhpWord\Writer\RTF\Element\Container;
/**
* RTF writer
@@ -32,20 +26,6 @@ use PhpOffice\PhpWord\Writer\RTF\Element\Container;
*/
class RTF extends AbstractWriter implements WriterInterface
{
- /**
- * Color register
- *
- * @var array
- */
- private $colorTable;
-
- /**
- * Font register
- *
- * @var array
- */
- private $fontTable;
-
/**
* Last paragraph style
*
@@ -54,39 +34,55 @@ class RTF extends AbstractWriter implements WriterInterface
private $lastParagraphStyle;
/**
- * Create new RTF writer
+ * Create new instance
+ *
* @param \PhpOffice\PhpWord\PhpWord $phpWord
*/
public function __construct(PhpWord $phpWord = null)
{
$this->setPhpWord($phpWord);
+
+ $this->parts = array('Header', 'Document');
+ foreach ($this->parts as $partName) {
+ $partClass = get_class($this) . '\\Part\\' . $partName;
+ if (class_exists($partClass)) {
+ /** @var \PhpOffice\PhpWord\Writer\RTF\Part\AbstractPart $part Type hint */
+ $part = new $partClass();
+ $part->setParentWriter($this);
+ $this->writerParts[strtolower($partName)] = $part;
+ }
+ }
}
/**
- * Save PhpWord to file
+ * Save content to file
*
* @param string $filename
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function save($filename = null)
{
- $filename = $this->getTempFile($filename);
- $hFile = fopen($filename, 'w');
- if ($hFile !== false) {
- fwrite($hFile, $this->writeDocument());
- fclose($hFile);
- } else {
- throw new Exception("Can't open file");
- }
- $this->cleanupTempFile();
+ $fileHandle = $this->openFile($filename);
+ $this->writeFile($fileHandle, $this->getContent());
}
/**
- * Get color table
+ * Get content
+ *
+ * @return string
+ * @since 0.11.0
*/
- public function getColorTable()
+ private function getContent()
{
- return $this->colorTable;
+ $content = '';
+
+ $content .= '{';
+ $content .= '\rtf1' . PHP_EOL;
+ $content .= $this->getWriterPart('Header')->write();
+ $content .= $this->getWriterPart('Document')->write();
+ $content .= '}';
+
+ return $content;
}
/**
@@ -94,7 +90,15 @@ class RTF extends AbstractWriter implements WriterInterface
*/
public function getFontTable()
{
- return $this->fontTable;
+ return $this->getWriterPart('Header')->getFontTable();
+ }
+
+ /**
+ * Get color table
+ */
+ public function getColorTable()
+ {
+ return $this->getWriterPart('Header')->getColorTable();
}
/**
@@ -114,172 +118,4 @@ class RTF extends AbstractWriter implements WriterInterface
{
$this->lastParagraphStyle = $value;
}
-
- /**
- * Get all data
- *
- * @return string
- */
- private function writeDocument()
- {
- $this->fontTable = $this->populateFontTable();
- $this->colorTable = $this->populateColorTable();
-
- // Set the default character set
- $content = '{\rtf1';
- $content .= '\ansi\ansicpg1252'; // Set the default font (the first one)
- $content .= '\deff0'; // Set the default tab size (720 twips)
- $content .= '\deftab720';
- $content .= PHP_EOL;
-
- // Set the font tbl group
- $content .= '{\fonttbl';
- foreach ($this->fontTable as $idx => $font) {
- $content .= '{\f' . $idx . '\fnil\fcharset0 ' . $font . ';}';
- }
- $content .= '}' . PHP_EOL;
-
- // Set the color tbl group
- $content .= '{\colortbl ';
- foreach ($this->colorTable as $color) {
- $arrColor = Drawing::htmlToRGB($color);
- $content .= ';\red' . $arrColor[0] . '\green' . $arrColor[1] . '\blue' . $arrColor[2] . '';
- }
- $content .= ';}' . PHP_EOL;
-
- $content .= '{\*\generator PhpWord;}' . PHP_EOL; // Set the generator
- $content .= '\viewkind4'; // Set the view mode of the document
- $content .= '\uc1'; // Set the numberof bytes that follows a unicode character
- $content .= '\pard'; // Resets to default paragraph properties.
- $content .= '\nowidctlpar'; // No widow/orphan control
- $content .= '\lang1036'; // Applies a language to a text run (1036 : French (France))
- $content .= '\kerning1'; // Point size (in half-points) above which to kern character pairs
- $content .= '\fs' . (Settings::getDefaultFontSize() * 2); // Set the font size in half-points
- $content .= PHP_EOL;
-
- // Body
- $content .= $this->writeContent();
-
- $content .= '}';
-
- return $content;
- }
-
- /**
- * Get content data
- *
- * @return string
- */
- private function writeContent()
- {
- $content = '';
-
- $sections = $this->getPhpWord()->getSections();
- foreach ($sections as $section) {
- $writer = new Container($this, $section);
- $content .= $writer->write();
- }
-
- return $content;
- }
-
- /**
- * Get all fonts
- *
- * @return array
- */
- private function populateFontTable()
- {
- $phpWord = $this->getPhpWord();
- $fontTable = array();
- $fontTable[] = Settings::getDefaultFontName();
-
- // Browse styles
- $styles = Style::getStyles();
- if (count($styles) > 0) {
- foreach ($styles as $style) {
- // Font
- if ($style instanceof Font) {
- if (in_array($style->getName(), $fontTable) == false) {
- $fontTable[] = $style->getName();
- }
- }
- }
- }
-
- // Search all fonts used
- $sections = $phpWord->getSections();
- $countSections = count($sections);
- if ($countSections > 0) {
- foreach ($sections as $section) {
- $elements = $section->getElements();
- foreach ($elements as $element) {
- if (method_exists($element, 'getFontStyle')) {
- $fontStyle = $element->getFontStyle();
- if ($fontStyle instanceof Font) {
- if (in_array($fontStyle->getName(), $fontTable) == false) {
- $fontTable[] = $fontStyle->getName();
- }
- }
- }
- }
- }
- }
-
- return $fontTable;
- }
-
- /**
- * Get all colors
- *
- * @return array
- */
- private function populateColorTable()
- {
- $phpWord = $this->getPhpWord();
- $defaultFontColor = Settings::DEFAULT_FONT_COLOR;
- $colorTable = array();
-
- // Browse styles
- $styles = Style::getStyles();
- if (count($styles) > 0) {
- foreach ($styles as $style) {
- // Font
- if ($style instanceof Font) {
- $color = $style->getColor();
- $fgcolor = $style->getFgColor();
- if (!in_array($color, $colorTable) && $color != $defaultFontColor && !empty($color)) {
- $colorTable[] = $color;
- }
- if (!in_array($fgcolor, $colorTable) && $fgcolor != $defaultFontColor && !empty($fgcolor)) {
- $colorTable[] = $fgcolor;
- }
- }
- }
- }
-
- // Search all fonts used
- $sections = $phpWord->getSections();
- $countSections = count($sections);
- if ($countSections > 0) {
- foreach ($sections as $section) {
- $elements = $section->getElements();
- foreach ($elements as $element) {
- if (method_exists($element, 'getFontStyle')) {
- $fontStyle = $element->getFontStyle();
- if ($fontStyle instanceof Font) {
- if (in_array($fontStyle->getColor(), $colorTable) == false) {
- $colorTable[] = $fontStyle->getColor();
- }
- if (in_array($fontStyle->getFgColor(), $colorTable) == false) {
- $colorTable[] = $fontStyle->getFgColor();
- }
- }
- }
- }
- }
- }
-
- return $colorTable;
- }
}
diff --git a/src/PhpWord/Writer/RTF/Element/Image.php b/src/PhpWord/Writer/RTF/Element/Image.php
new file mode 100644
index 00000000..1daae2a0
--- /dev/null
+++ b/src/PhpWord/Writer/RTF/Element/Image.php
@@ -0,0 +1,57 @@
+element instanceof ImageElement) {
+ return '';
+ }
+
+ $this->getStyles();
+ $style = $this->element->getStyle();
+
+ $content = '';
+ $content .= $this->writeOpening();
+ $content .= '{\*\shppict {\pict';
+ $content .= '\pngblip\picscalex100\picscaley100';
+ $content .= '\picwgoal' . round(Font::pixelSizeToTwips($style->getWidth()));
+ $content .= '\pichgoal' . round(Font::pixelSizeToTwips($style->getHeight()));
+ $content .= PHP_EOL;
+ $content .= $this->element->getImageStringData();
+ $content .= '}}';
+ $content .= $this->writeClosing();
+
+ return $content;
+ }
+}
diff --git a/src/PhpWord/Writer/RTF/Part/AbstractPart.php b/src/PhpWord/Writer/RTF/Part/AbstractPart.php
new file mode 100644
index 00000000..b10e5654
--- /dev/null
+++ b/src/PhpWord/Writer/RTF/Part/AbstractPart.php
@@ -0,0 +1,29 @@
+writeInfo();
+ $content .= $this->writeFormatting();
+ $content .= $this->writeSections();
+
+ return $content;
+ }
+
+ /**
+ * Write document information
+ *
+ * @return string
+ */
+ private function writeInfo()
+ {
+ $docProps = $this->getParentWriter()->getPhpWord()->getDocumentProperties();
+ $properties = array('title', 'subject', 'category', 'keywords', 'comment',
+ 'author', 'operator', 'creatim', 'revtim', 'company', 'manager');
+ $mapping = array('comment' => 'description', 'author' => 'creator', 'operator' => 'lastModifiedBy',
+ 'creatim' => 'created', 'revtim' => 'modified');
+ $dateFields = array('creatim', 'revtim');
+
+ $content = '';
+
+ $content .= '{';
+ $content .= '\info';
+ foreach ($properties as $property) {
+ $method = 'get' . (array_key_exists($property, $mapping) ? $mapping[$property] : $property);
+ $value = $docProps->$method();
+ $value = in_array($property, $dateFields) ? $this->getDateValue($value) : $value;
+ $content .= "{\\{$property} {$value}}";
+ }
+ $content .= '}';
+ $content .= PHP_EOL;
+
+ return $content;
+ }
+
+ /**
+ * Write document formatting properties
+ *
+ * @return string
+ */
+ private function writeFormatting()
+ {
+ $content = '';
+
+ $content .= '\deftab720'; // Set the default tab size (720 twips)
+ $content .= '\viewkind1'; // Set the view mode of the document
+
+ $content .= '\uc1'; // Set the numberof bytes that follows a unicode character
+ $content .= '\pard'; // Resets to default paragraph properties.
+ $content .= '\nowidctlpar'; // No widow/orphan control
+ $content .= '\lang1036'; // Applies a language to a text run (1036 : French (France))
+ $content .= '\kerning1'; // Point size (in half-points) above which to kern character pairs
+ $content .= '\fs' . (Settings::getDefaultFontSize() * 2); // Set the font size in half-points
+ $content .= PHP_EOL;
+
+ return $content;
+ }
+
+ /**
+ * Write sections
+ *
+ * @return string
+ */
+ private function writeSections()
+ {
+ $content = '';
+
+ $sections = $this->getParentWriter()->getPhpWord()->getSections();
+ foreach ($sections as $section) {
+ $writer = new Container($this->getParentWriter(), $section);
+ $content .= $writer->write();
+ }
+
+ return $content;
+ }
+
+ /**
+ * Get date value
+ *
+ * The format of date value is `\yr?\mo?\dy?\hr?\min?\sec?`
+ *
+ * @param int $value
+ * @return string
+ */
+ private function getDateValue($value)
+ {
+ $dateParts = array(
+ 'Y' => 'yr',
+ 'm' => 'mo',
+ 'd' => 'dy',
+ 'H' => 'hr',
+ 'i' => 'min',
+ 's' => 'sec',
+ );
+ $result = '';
+ foreach ($dateParts as $dateFormat => $controlWord) {
+ $result .= '\\' . $controlWord . date($dateFormat, $value);
+ }
+
+ return $result;
+ }
+}
diff --git a/src/PhpWord/Writer/RTF/Part/Header.php b/src/PhpWord/Writer/RTF/Part/Header.php
new file mode 100644
index 00000000..c401b500
--- /dev/null
+++ b/src/PhpWord/Writer/RTF/Part/Header.php
@@ -0,0 +1,227 @@
+fontTable;
+ }
+
+ /**
+ * Get color table
+ */
+ public function getColorTable()
+ {
+ return $this->colorTable;
+ }
+
+ /**
+ * Write part
+ *
+ * @return string
+ */
+ public function write()
+ {
+ $this->registerFont();
+
+ $content = '';
+
+ $content .= $this->writeCharset();
+ $content .= $this->writeDefaults();
+ $content .= $this->writeFontTable();
+ $content .= $this->writeColorTable();
+ $content .= $this->writeGenerator();
+ $content .= PHP_EOL;
+
+ return $content;
+ }
+
+ /**
+ * Write character set
+ *
+ * @return string
+ */
+ private function writeCharset()
+ {
+ $content = '';
+
+ $content .= '\ansi';
+ $content .= '\ansicpg1252';
+ $content .= PHP_EOL;
+
+ return $content;
+ }
+
+ /**
+ * Write header defaults
+ *
+ * @return string
+ */
+ private function writeDefaults()
+ {
+ $content = '';
+
+ $content .= '\deff0';
+ $content .= PHP_EOL;
+
+ return $content;
+ }
+
+ /**
+ * Write font table
+ *
+ * @return string
+ */
+ private function writeFontTable()
+ {
+ $content = '';
+
+ $content .= '{';
+ $content .= '\fonttbl';
+ foreach ($this->fontTable as $index => $font) {
+ $content .= "{\\f{$index}\\fnil\\fcharset0{$font};}";
+ }
+ $content .= '}';
+ $content .= PHP_EOL;
+
+ return $content;
+ }
+
+ /**
+ * Write color table
+ *
+ * @return string
+ */
+ private function writeColorTable()
+ {
+ $content = '';
+
+ $content .= '{';
+ $content .= '\colortbl';
+ foreach ($this->colorTable as $color) {
+ list($red, $green, $blue) = Drawing::htmlToRGB($color);
+ $content .= ";\\red{$red}\\green{$green}\\blue{$blue}";
+ }
+ $content .= '}';
+ $content .= PHP_EOL;
+
+ return $content;
+ }
+
+ /**
+ * Write
+ *
+ * @return string
+ */
+ private function writeGenerator()
+ {
+ $content = '';
+
+ $content .= '{\*\generator PhpWord;}'; // Set the generator
+ $content .= PHP_EOL;
+
+ return $content;
+ }
+
+ /**
+ * Register all fonts and colors in both named and inline styles to appropriate header table
+ */
+ private function registerFont()
+ {
+ $phpWord = $this->getParentWriter()->getPhpWord();
+ $this->fontTable[] = Settings::getDefaultFontName();
+
+ // Search named styles
+ $styles = Style::getStyles();
+ foreach ($styles as $style) {
+ $this->registerFontItems($style);
+ }
+
+ // Search inline styles
+ $sections = $phpWord->getSections();
+ foreach ($sections as $section) {
+ $elements = $section->getElements();
+ foreach ($elements as $element) {
+ if (method_exists($element, 'getFontStyle')) {
+ $style = $element->getFontStyle();
+ $this->registerFontItems($style);
+ }
+ }
+ }
+ }
+
+ /**
+ * Register fonts and colors
+ *
+ * @param \PhpOffice\PhpWord\Style\AbstractStyle $style
+ */
+ private function registerFontItems($style)
+ {
+ $defaultFont = Settings::getDefaultFontName();
+ $defaultColor = Settings::DEFAULT_FONT_COLOR;
+
+ if ($style instanceof Font) {
+ $this->registerFontItem($this->fontTable, $style->getName(), $defaultFont);
+ $this->registerFontItem($this->colorTable, $style->getColor(), $defaultColor);
+ $this->registerFontItem($this->colorTable, $style->getFgColor(), $defaultColor);
+ }
+ }
+
+ /**
+ * Register individual font and color
+ *
+ * @param array $table
+ * @param string $value
+ * @param string $default
+ */
+ private function registerFontItem(&$table, $value, $default)
+ {
+ if (in_array($value, $table) === false && $value !== null && $value != $default) {
+ $table[] = $value;
+ }
+ }
+}
diff --git a/src/PhpWord/Writer/Word2007.php b/src/PhpWord/Writer/Word2007.php
index 7d3c73bc..751b58b6 100644
--- a/src/PhpWord/Writer/Word2007.php
+++ b/src/PhpWord/Writer/Word2007.php
@@ -53,23 +53,24 @@ class Word2007 extends AbstractWriter implements WriterInterface
// Create parts
$this->parts = array(
- 'ContentTypes' => '[Content_Types].xml',
- 'Rels' => '_rels/.rels',
- 'DocPropsApp' => 'docProps/app.xml',
- 'DocPropsCore' => 'docProps/core.xml',
- 'RelsDocument' => 'word/_rels/document.xml.rels',
- 'Document' => 'word/document.xml',
- 'Styles' => 'word/styles.xml',
- 'Numbering' => 'word/numbering.xml',
- 'Settings' => 'word/settings.xml',
- 'WebSettings' => 'word/webSettings.xml',
- 'FontTable' => 'word/fontTable.xml',
- 'Theme' => 'word/theme/theme1.xml',
- 'RelsPart' => '',
- 'Header' => '',
- 'Footer' => '',
- 'Footnotes' => '',
- 'Endnotes' => '',
+ 'ContentTypes' => '[Content_Types].xml',
+ 'Rels' => '_rels/.rels',
+ 'DocPropsApp' => 'docProps/app.xml',
+ 'DocPropsCore' => 'docProps/core.xml',
+ 'DocPropsCustom' => 'docProps/custom.xml',
+ 'RelsDocument' => 'word/_rels/document.xml.rels',
+ 'Document' => 'word/document.xml',
+ 'Styles' => 'word/styles.xml',
+ 'Numbering' => 'word/numbering.xml',
+ 'Settings' => 'word/settings.xml',
+ 'WebSettings' => 'word/webSettings.xml',
+ 'FontTable' => 'word/fontTable.xml',
+ 'Theme' => 'word/theme/theme1.xml',
+ 'RelsPart' => '',
+ 'Header' => '',
+ 'Footer' => '',
+ 'Footnotes' => '',
+ 'Endnotes' => '',
);
foreach (array_keys($this->parts) as $partName) {
$partClass = get_class($this) . '\\Part\\' . $partName;
diff --git a/src/PhpWord/Writer/Word2007/Element/Container.php b/src/PhpWord/Writer/Word2007/Element/Container.php
index 7d1c8104..3dad824d 100644
--- a/src/PhpWord/Writer/Word2007/Element/Container.php
+++ b/src/PhpWord/Writer/Word2007/Element/Container.php
@@ -17,8 +17,10 @@
namespace PhpOffice\PhpWord\Writer\Word2007\Element;
+use PhpOffice\PhpWord\Element\AbstractElement as Element;
use PhpOffice\PhpWord\Element\AbstractContainer as ContainerElement;
use PhpOffice\PhpWord\Element\TextBreak as TextBreakElement;
+use PhpOffice\PhpWord\Shared\XMLWriter;
/**
* Container element writer (section, textrun, header, footnote, cell, etc.)
@@ -51,36 +53,52 @@ class Container extends AbstractElement
$elements = $container->getElements();
$elementClass = '';
foreach ($elements as $element) {
- $elementClass = substr(get_class($element), strrpos(get_class($element), '\\') + 1);
- $writerClass = $this->namespace . '\\' . $elementClass;
-
- // Check it's a page break. No need to write it, instead, flag containers' pageBreakBefore
- // to be assigned to the next element
- if ($elementClass == 'PageBreak') {
- $this->setPageBreakBefore(true);
- continue;
- }
- if (class_exists($writerClass)) {
- // Get container's page break before and reset it
- $pageBreakBefore = $this->hasPageBreakBefore();
- $this->setPageBreakBefore(false);
-
- /** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $writer Type hint */
- $writer = new $writerClass($xmlWriter, $element, $withoutP);
- $writer->setPageBreakBefore($pageBreakBefore);
- $writer->write();
- }
+ $elementClass = $this->writeElement($xmlWriter, $element, $withoutP);
}
- // Special case for Cell: They have to contain a w:p element at the end. The $elementClass contains
- // the last element name. If it's empty string or Table, the last element is not w:p
- if ($containerClass == 'Cell') {
- if ($elementClass == '' || $elementClass == 'Table') {
- $writerClass = $this->namespace . '\\TextBreak';
- /** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $writer Type hint */
- $writer = new $writerClass($xmlWriter, new TextBreakElement(), $withoutP);
- $writer->write();
- }
+ // Special case for Cell: They have to contain a w:p element at the end.
+ // The $elementClass contains the last element name. If it's empty string
+ // or Table, the last element is not w:p
+ $writeLastTextBreak = ($containerClass == 'Cell') && ($elementClass == '' || $elementClass == 'Table');
+ if ($writeLastTextBreak) {
+ $writerClass = $this->namespace . '\\TextBreak';
+ /** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $writer Type hint */
+ $writer = new $writerClass($xmlWriter, new TextBreakElement(), $withoutP);
+ $writer->write();
}
}
+
+ /**
+ * Write individual element
+ *
+ * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter
+ * @param \PhpOffice\PhpWord\Element\AbstractElement $element
+ * @param bool $withoutP
+ * @return string
+ */
+ private function writeElement(XMLWriter $xmlWriter, Element $element, $withoutP)
+ {
+ $elementClass = substr(get_class($element), strrpos(get_class($element), '\\') + 1);
+ $writerClass = $this->namespace . '\\' . $elementClass;
+
+ // Check it's a page break. No need to write it, instead, flag containers'
+ // pageBreakBefore to be assigned to the next element
+ if ($elementClass == 'PageBreak') {
+ $this->setPageBreakBefore(true);
+ return $elementClass;
+ }
+
+ if (class_exists($writerClass)) {
+ // Get container's page break before and reset it
+ $pageBreakBefore = $this->hasPageBreakBefore();
+ $this->setPageBreakBefore(false);
+
+ /** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $writer Type hint */
+ $writer = new $writerClass($xmlWriter, $element, $withoutP);
+ $writer->setPageBreakBefore($pageBreakBefore);
+ $writer->write();
+ }
+
+ return $elementClass;
+ }
}
diff --git a/src/PhpWord/Writer/Word2007/Element/TextBreak.php b/src/PhpWord/Writer/Word2007/Element/TextBreak.php
index 227b1b30..5f4bd3ce 100644
--- a/src/PhpWord/Writer/Word2007/Element/TextBreak.php
+++ b/src/PhpWord/Writer/Word2007/Element/TextBreak.php
@@ -37,15 +37,13 @@ class TextBreak extends Text
if (!$this->withoutP) {
$hasStyle = $element->hasStyle();
+ $this->writeOpeningWP();
if ($hasStyle) {
- $this->writeOpeningWP();
$xmlWriter->startElement('w:pPr');
$this->writeFontStyle();
$xmlWriter->endElement(); // w:pPr
- $this->writeClosingWP();
- } else {
- $xmlWriter->writeElement('w:p');
}
+ $this->writeClosingWP();
} else {
$xmlWriter->writeElement('w:br');
}
diff --git a/src/PhpWord/Writer/Word2007/Part/AbstractPart.php b/src/PhpWord/Writer/Word2007/Part/AbstractPart.php
index fbd4a6e5..610a7616 100644
--- a/src/PhpWord/Writer/Word2007/Part/AbstractPart.php
+++ b/src/PhpWord/Writer/Word2007/Part/AbstractPart.php
@@ -33,6 +33,11 @@ abstract class AbstractPart
*/
protected $parentWriter;
+ /**
+ * @var string Date format
+ */
+ protected $dateFormat = 'Y-m-d\TH:i:sP';
+
/**
* Write part
*
diff --git a/src/PhpWord/Writer/Word2007/Part/ContentTypes.php b/src/PhpWord/Writer/Word2007/Part/ContentTypes.php
index f8cb2f26..b6f23f47 100644
--- a/src/PhpWord/Writer/Word2007/Part/ContentTypes.php
+++ b/src/PhpWord/Writer/Word2007/Part/ContentTypes.php
@@ -40,6 +40,7 @@ class ContentTypes extends AbstractPart
$overrides = array(
'/docProps/core.xml' => $openXMLPrefix . 'package.core-properties+xml',
'/docProps/app.xml' => $openXMLPrefix . 'officedocument.extended-properties+xml',
+ '/docProps/custom.xml' => $openXMLPrefix . 'officedocument.custom-properties+xml',
'/word/document.xml' => $wordMLPrefix . 'document.main+xml',
'/word/styles.xml' => $wordMLPrefix . 'styles+xml',
'/word/numbering.xml' => $wordMLPrefix . 'numbering+xml',
diff --git a/src/PhpWord/Writer/Word2007/Part/DocPropsCore.php b/src/PhpWord/Writer/Word2007/Part/DocPropsCore.php
index 2b3bce5a..38f6d235 100644
--- a/src/PhpWord/Writer/Word2007/Part/DocPropsCore.php
+++ b/src/PhpWord/Writer/Word2007/Part/DocPropsCore.php
@@ -54,13 +54,13 @@ class DocPropsCore extends AbstractPart
// dcterms:created
$xmlWriter->startElement('dcterms:created');
$xmlWriter->writeAttribute('xsi:type', 'dcterms:W3CDTF');
- $xmlWriter->writeRaw(date(DATE_W3C, $phpWord->getDocumentProperties()->getCreated()));
+ $xmlWriter->writeRaw(date($this->dateFormat, $phpWord->getDocumentProperties()->getCreated()));
$xmlWriter->endElement();
// dcterms:modified
$xmlWriter->startElement('dcterms:modified');
$xmlWriter->writeAttribute('xsi:type', 'dcterms:W3CDTF');
- $xmlWriter->writeRaw(date(DATE_W3C, $phpWord->getDocumentProperties()->getModified()));
+ $xmlWriter->writeRaw(date($this->dateFormat, $phpWord->getDocumentProperties()->getModified()));
$xmlWriter->endElement();
$xmlWriter->endElement(); // cp:coreProperties
diff --git a/src/PhpWord/Writer/Word2007/Part/DocPropsCustom.php b/src/PhpWord/Writer/Word2007/Part/DocPropsCustom.php
new file mode 100644
index 00000000..c5a3cf9e
--- /dev/null
+++ b/src/PhpWord/Writer/Word2007/Part/DocPropsCustom.php
@@ -0,0 +1,78 @@
+getParentWriter()->getPhpWord();
+ $xmlWriter = $this->getXmlWriter();
+
+ $xmlWriter->startDocument('1.0', 'UTF-8', 'yes');
+ $xmlWriter->startElement('Properties');
+ $xmlWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/officeDocument/2006/custom-properties');
+ $xmlWriter->writeAttribute('xmlns:vt', 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
+
+ $docProps = $phpWord->getDocumentProperties();
+ $properties = $docProps->getCustomProperties();
+ foreach ($properties as $key => $property) {
+ $propertyValue = $docProps->getCustomPropertyValue($property);
+ $propertyType = $docProps->getCustomPropertyType($property);
+
+ $xmlWriter->startElement('property');
+ $xmlWriter->writeAttribute('fmtid', '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}');
+ $xmlWriter->writeAttribute('pid', $key + 2);
+ $xmlWriter->writeAttribute('name', $property);
+ switch ($propertyType) {
+ case 'i':
+ $xmlWriter->writeElement('vt:i4', $propertyValue);
+ break;
+ case 'f':
+ $xmlWriter->writeElement('vt:r8', $propertyValue);
+ break;
+ case 'b':
+ $xmlWriter->writeElement('vt:bool', ($propertyValue) ? 'true' : 'false');
+ break;
+ case 'd':
+ $xmlWriter->startElement('vt:filetime');
+ $xmlWriter->writeRaw(date($this->dateFormat, $propertyValue));
+ $xmlWriter->endElement();
+ break;
+ default:
+ $xmlWriter->writeElement('vt:lpwstr', $propertyValue);
+ break;
+ }
+ $xmlWriter->endElement(); // property
+ }
+
+ $xmlWriter->endElement(); // Properties
+
+ return $xmlWriter->getData();
+ }
+}
diff --git a/src/PhpWord/Writer/Word2007/Part/Numbering.php b/src/PhpWord/Writer/Word2007/Part/Numbering.php
index 2678ac55..05cbf7b9 100644
--- a/src/PhpWord/Writer/Word2007/Part/Numbering.php
+++ b/src/PhpWord/Writer/Word2007/Part/Numbering.php
@@ -24,6 +24,8 @@ use PhpOffice\PhpWord\Style\NumberingLevel;
/**
* Word2007 numbering part writer: word/numbering.xml
+ *
+ * @since 0.10.0
*/
class Numbering extends AbstractPart
{
@@ -97,12 +99,6 @@ class Numbering extends AbstractPart
*/
private function writeLevel(XMLWriter $xmlWriter, NumberingLevel $level)
{
- $tabPos = $level->getTabPos();
- $left = $level->getLeft();
- $hanging = $level->getHanging();
- $font = $level->getFont();
- $hint = $level->getHint();
-
$xmlWriter->startElement('w:lvl');
$xmlWriter->writeAttribute('w:ilvl', $level->getLevel());
@@ -124,48 +120,63 @@ class Numbering extends AbstractPart
}
}
- // Paragraph styles
- if (!is_null($tabPos) || !is_null($left) || !is_null($hanging)) {
- $xmlWriter->startElement('w:pPr');
- if (!is_null($tabPos)) {
- $xmlWriter->startElement('w:tabs');
- $xmlWriter->startElement('w:tab');
- $xmlWriter->writeAttribute('w:val', 'num');
- $xmlWriter->writeAttribute('w:pos', $tabPos);
- $xmlWriter->endElement(); // w:tab
- $xmlWriter->endElement(); // w:tabs
- }
- if (!is_null($left) || !is_null($hanging)) {
- $xmlWriter->startElement('w:ind');
- if (!is_null($left)) {
- $xmlWriter->writeAttribute('w:left', $left);
- }
- if (!is_null($hanging)) {
- $xmlWriter->writeAttribute('w:hanging', $hanging);
- }
- $xmlWriter->endElement(); // w:ind
- }
- $xmlWriter->endElement(); // w:pPr
- }
+ // Paragraph & font styles
+ $this->writeParagraph($xmlWriter, $level);
+ $this->writeFont($xmlWriter, $level);
- // Font styles
- if (!is_null($font) || !is_null($hint)) {
- $xmlWriter->startElement('w:rPr');
- $xmlWriter->startElement('w:rFonts');
- if (!is_null($font)) {
- $xmlWriter->writeAttribute('w:ascii', $font);
- $xmlWriter->writeAttribute('w:hAnsi', $font);
- $xmlWriter->writeAttribute('w:cs', $font);
- }
- if (!is_null($hint)) {
- $xmlWriter->writeAttribute('w:hint', $hint);
- }
- $xmlWriter->endElement(); // w:rFonts
- $xmlWriter->endElement(); // w:rPr
- }
$xmlWriter->endElement(); // w:lvl
}
+ /**
+ * Write level paragraph
+ *
+ * @since 0.11.0
+ * @todo Use paragraph style writer
+ */
+ private function writeParagraph(XMLWriter $xmlWriter, NumberingLevel $level)
+ {
+ $tabPos = $level->getTabPos();
+ $left = $level->getLeft();
+ $hanging = $level->getHanging();
+
+ $xmlWriter->startElement('w:pPr');
+
+ $xmlWriter->startElement('w:tabs');
+ $xmlWriter->startElement('w:tab');
+ $xmlWriter->writeAttribute('w:val', 'num');
+ $xmlWriter->writeAttributeIf($tabPos !== null, 'w:pos', $tabPos);
+ $xmlWriter->endElement(); // w:tab
+ $xmlWriter->endElement(); // w:tabs
+
+ $xmlWriter->startElement('w:ind');
+ $xmlWriter->writeAttributeIf($left !== null, 'w:left', $left);
+ $xmlWriter->writeAttributeIf($hanging !== null, 'w:hanging', $hanging);
+ $xmlWriter->endElement(); // w:ind
+
+ $xmlWriter->endElement(); // w:pPr
+ }
+
+ /**
+ * Write level font
+ *
+ * @since 0.11.0
+ * @todo Use font style writer
+ */
+ private function writeFont(XMLWriter $xmlWriter, NumberingLevel $level)
+ {
+ $font = $level->getFont();
+ $hint = $level->getHint();
+
+ $xmlWriter->startElement('w:rPr');
+ $xmlWriter->startElement('w:rFonts');
+ $xmlWriter->writeAttributeIf($font !== null, 'w:ascii', $font);
+ $xmlWriter->writeAttributeIf($font !== null, 'w:hAnsi', $font);
+ $xmlWriter->writeAttributeIf($font !== null, 'w:cs', $font);
+ $xmlWriter->writeAttributeIf($hint !== null, 'w:hint', $hint);
+ $xmlWriter->endElement(); // w:rFonts
+ $xmlWriter->endElement(); // w:rPr
+ }
+
/**
* Get random hexadecimal number value
*
diff --git a/src/PhpWord/Writer/Word2007/Part/Rels.php b/src/PhpWord/Writer/Word2007/Part/Rels.php
index af57a768..562deb35 100644
--- a/src/PhpWord/Writer/Word2007/Part/Rels.php
+++ b/src/PhpWord/Writer/Word2007/Part/Rels.php
@@ -35,9 +35,10 @@ class Rels extends AbstractPart
public function write()
{
$xmlRels = array(
- 'docProps/core.xml' => 'package/2006/relationships/metadata/core-properties',
- 'docProps/app.xml' => 'officeDocument/2006/relationships/extended-properties',
- 'word/document.xml' => 'officeDocument/2006/relationships/officeDocument',
+ 'docProps/core.xml' => 'package/2006/relationships/metadata/core-properties',
+ 'docProps/app.xml' => 'officeDocument/2006/relationships/extended-properties',
+ 'docProps/custom.xml' => 'officeDocument/2006/relationships/custom-properties',
+ 'word/document.xml' => 'officeDocument/2006/relationships/officeDocument',
);
$xmlWriter = $this->getXmlWriter();
$this->writeRels($xmlWriter, $xmlRels);
@@ -49,43 +50,51 @@ class Rels extends AbstractPart
* Write relationships
*
* @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter
- * @param null|array $xmlRels
- * @param null|array $mediaRels
- * @param integer $relId
+ * @param array $xmlRels
+ * @param array $mediaRels
+ * @param int $relId
*/
- protected function writeRels(XMLWriter $xmlWriter, $xmlRels = null, $mediaRels = null, $relId = 1)
+ protected function writeRels(XMLWriter $xmlWriter, $xmlRels = array(), $mediaRels = array(), $relId = 1)
{
$xmlWriter->startDocument('1.0', 'UTF-8', 'yes');
$xmlWriter->startElement('Relationships');
$xmlWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
// XML files relationships
- if (is_array($xmlRels)) {
- foreach ($xmlRels as $target => $type) {
- $this->writeRel($xmlWriter, $relId++, $type, $target);
- }
+ foreach ($xmlRels as $target => $type) {
+ $this->writeRel($xmlWriter, $relId++, $type, $target);
}
// Media relationships
- if (is_array($mediaRels)) {
- $typePrefix = 'officeDocument/2006/relationships/';
- $typeMapping = array('image' => 'image', 'object' => 'oleObject', 'link' => 'hyperlink');
- $targetPaths = array('image' => 'media/', 'object' => 'embeddings/');
-
- foreach ($mediaRels as $mediaRel) {
- $mediaType = $mediaRel['type'];
- $type = array_key_exists($mediaType, $typeMapping) ? $typeMapping[$mediaType] : $mediaType;
- $target = array_key_exists($mediaType, $targetPaths) ? $targetPaths[$mediaType] : '';
- $target .= $mediaRel['target'];
- $targetMode = ($type == 'hyperlink') ? 'External' : '';
-
- $this->writeRel($xmlWriter, $relId++, $typePrefix . $type, $target, $targetMode);
- }
+ foreach ($mediaRels as $mediaRel) {
+ $this->writeMediaRel($xmlWriter, $relId++, $mediaRel);
}
$xmlWriter->endElement(); // Relationships
}
+ /**
+ * Write media relationships
+ *
+ * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter
+ * @param int $relId
+ * @param array $mediaRel
+ */
+ private function writeMediaRel(XMLWriter $xmlWriter, $relId, $mediaRel)
+ {
+ $typePrefix = 'officeDocument/2006/relationships/';
+ $typeMapping = array('image' => 'image', 'object' => 'oleObject', 'link' => 'hyperlink');
+ $targetMapping = array('image' => 'media/', 'object' => 'embeddings/');
+
+ $mediaType = $mediaRel['type'];
+ $type = array_key_exists($mediaType, $typeMapping) ? $typeMapping[$mediaType] : $mediaType;
+ $targetPrefix = array_key_exists($mediaType, $targetMapping) ? $targetMapping[$mediaType] : '';
+ $target = $mediaRel['target'];
+ $targetMode = ($type == 'hyperlink') ? 'External' : '';
+
+ $this->writeRel($xmlWriter, $relId, $typePrefix . $type, $targetPrefix . $target, $targetMode);
+ }
+
/**
* Write individual rels entry
*
diff --git a/src/PhpWord/Writer/Word2007/Part/RelsPart.php b/src/PhpWord/Writer/Word2007/Part/RelsPart.php
index a3697834..627a2bcd 100644
--- a/src/PhpWord/Writer/Word2007/Part/RelsPart.php
+++ b/src/PhpWord/Writer/Word2007/Part/RelsPart.php
@@ -39,7 +39,7 @@ class RelsPart extends Rels
public function write()
{
$xmlWriter = $this->getXmlWriter();
- $this->writeRels($xmlWriter, null, $this->media);
+ $this->writeRels($xmlWriter, array(), $this->media);
return $xmlWriter->getData();
}
diff --git a/src/PhpWord/Writer/Word2007/Part/Styles.php b/src/PhpWord/Writer/Word2007/Part/Styles.php
index a0a6317a..0d688e36 100644
--- a/src/PhpWord/Writer/Word2007/Part/Styles.php
+++ b/src/PhpWord/Writer/Word2007/Part/Styles.php
@@ -20,9 +20,9 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part;
use PhpOffice\PhpWord\Settings as PhpWordSettings;
use PhpOffice\PhpWord\Shared\XMLWriter;
use PhpOffice\PhpWord\Style;
-use PhpOffice\PhpWord\Style\Font;
-use PhpOffice\PhpWord\Style\Paragraph;
-use PhpOffice\PhpWord\Style\Table;
+use PhpOffice\PhpWord\Style\Font as FontStyle;
+use PhpOffice\PhpWord\Style\Paragraph as ParagraphStyle;
+use PhpOffice\PhpWord\Style\Table as TableStyle;
use PhpOffice\PhpWord\Writer\Word2007\Style\Font as FontStyleWriter;
use PhpOffice\PhpWord\Writer\Word2007\Style\Paragraph as ParagraphStyleWriter;
use PhpOffice\PhpWord\Writer\Word2007\Style\Table as TableStyleWriter;
@@ -31,6 +31,7 @@ use PhpOffice\PhpWord\Writer\Word2007\Style\Table as TableStyleWriter;
* Word2007 styles part writer: word/styles.xml
*
* @todo Do something with the numbering style introduced in 0.10.0
+ * @SuppressWarnings(PHPMD.UnusedPrivateMethod) For writeFontStyle, writeParagraphStyle, and writeTableStyle
*/
class Styles extends AbstractPart
{
@@ -59,88 +60,11 @@ class Styles extends AbstractPart
continue;
}
- // Font style
- if ($style instanceof Font) {
- $paragraphStyle = $style->getParagraph();
- $styleType = $style->getStyleType();
- $type = ($styleType == 'title') ? 'paragraph' : 'character';
- if (!is_null($paragraphStyle)) {
- $type = 'paragraph';
- }
-
- $xmlWriter->startElement('w:style');
- $xmlWriter->writeAttribute('w:type', $type);
- if ($styleType == 'title') {
- $arrStyle = explode('_', $styleName);
- $styleId = 'Heading' . $arrStyle[1];
- $styleName = 'heading ' . $arrStyle[1];
- $styleLink = 'Heading' . $arrStyle[1] . 'Char';
- $xmlWriter->writeAttribute('w:styleId', $styleId);
-
- $xmlWriter->startElement('w:link');
- $xmlWriter->writeAttribute('w:val', $styleLink);
- $xmlWriter->endElement();
- }
- $xmlWriter->startElement('w:name');
- $xmlWriter->writeAttribute('w:val', $styleName);
- $xmlWriter->endElement();
-
- // Parent style
- $xmlWriter->writeElementIf(!is_null($paragraphStyle), 'w:basedOn', 'w:val', 'Normal');
-
- // w:pPr
- if (!is_null($paragraphStyle)) {
- $styleWriter = new ParagraphStyleWriter($xmlWriter, $paragraphStyle);
- $styleWriter->write();
- }
-
- // w:rPr
- $styleWriter = new FontStyleWriter($xmlWriter, $style);
- $styleWriter->write();
-
- $xmlWriter->endElement();
-
- // Paragraph style
- } elseif ($style instanceof Paragraph) {
- $xmlWriter->startElement('w:style');
- $xmlWriter->writeAttribute('w:type', 'paragraph');
- $xmlWriter->writeAttribute('w:customStyle', '1');
- $xmlWriter->writeAttribute('w:styleId', $styleName);
- $xmlWriter->startElement('w:name');
- $xmlWriter->writeAttribute('w:val', $styleName);
- $xmlWriter->endElement();
-
- // Parent style
- $basedOn = $style->getBasedOn();
- $xmlWriter->writeElementIf(!is_null($basedOn), 'w:basedOn', 'w:val', $basedOn);
-
- // Next paragraph style
- $next = $style->getNext();
- $xmlWriter->writeElementIf(!is_null($next), 'w:next', 'w:val', $next);
-
- // w:pPr
- $styleWriter = new ParagraphStyleWriter($xmlWriter, $style);
- $styleWriter->write();
-
- $xmlWriter->endElement();
-
- // Table style
- } elseif ($style instanceof Table) {
- $xmlWriter->startElement('w:style');
- $xmlWriter->writeAttribute('w:type', 'table');
- $xmlWriter->writeAttribute('w:customStyle', '1');
- $xmlWriter->writeAttribute('w:styleId', $styleName);
- $xmlWriter->startElement('w:name');
- $xmlWriter->writeAttribute('w:val', $styleName);
- $xmlWriter->endElement();
- $xmlWriter->startElement('w:uiPriority');
- $xmlWriter->writeAttribute('w:val', '99');
- $xmlWriter->endElement();
-
- $styleWriter = new TableStyleWriter($xmlWriter, $style);
- $styleWriter->write();
-
- $xmlWriter->endElement(); // w:style
+ // Get style class and execute if the private method exists
+ $styleClass = substr(get_class($style), strrpos(get_class($style), '\\') + 1);
+ $method = "write{$styleClass}Style";
+ if (method_exists($this, $method)) {
+ $this->$method($xmlWriter, $styleName, $style);
}
}
}
@@ -213,4 +137,115 @@ class Styles extends AbstractPart
$xmlWriter->endElement(); // w:style
}
}
+
+ /**
+ * Write font style
+ *
+ * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter
+ * @param string $styleName
+ * @param \PhpOffice\PhpWord\Style\Font $style
+ */
+ private function writeFontStyle(XMLWriter $xmlWriter, $styleName, FontStyle $style)
+ {
+ $paragraphStyle = $style->getParagraph();
+ $styleType = $style->getStyleType();
+ $type = ($styleType == 'title') ? 'paragraph' : 'character';
+ if (!is_null($paragraphStyle)) {
+ $type = 'paragraph';
+ }
+
+ $xmlWriter->startElement('w:style');
+ $xmlWriter->writeAttribute('w:type', $type);
+
+ // Heading style
+ if ($styleType == 'title') {
+ $arrStyle = explode('_', $styleName);
+ $styleId = 'Heading' . $arrStyle[1];
+ $styleName = 'heading ' . $arrStyle[1];
+ $styleLink = 'Heading' . $arrStyle[1] . 'Char';
+ $xmlWriter->writeAttribute('w:styleId', $styleId);
+
+ $xmlWriter->startElement('w:link');
+ $xmlWriter->writeAttribute('w:val', $styleLink);
+ $xmlWriter->endElement();
+ }
+
+ // Style name
+ $xmlWriter->startElement('w:name');
+ $xmlWriter->writeAttribute('w:val', $styleName);
+ $xmlWriter->endElement();
+
+ // Parent style
+ $xmlWriter->writeElementIf(!is_null($paragraphStyle), 'w:basedOn', 'w:val', 'Normal');
+
+ // w:pPr
+ if (!is_null($paragraphStyle)) {
+ $styleWriter = new ParagraphStyleWriter($xmlWriter, $paragraphStyle);
+ $styleWriter->write();
+ }
+
+ // w:rPr
+ $styleWriter = new FontStyleWriter($xmlWriter, $style);
+ $styleWriter->write();
+
+ $xmlWriter->endElement();
+ }
+
+ /**
+ * Write paragraph style
+ *
+ * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter
+ * @param string $styleName
+ * @param \PhpOffice\PhpWord\Style\Paragraph $style
+ */
+ private function writeParagraphStyle(XMLWriter $xmlWriter, $styleName, ParagraphStyle $style)
+ {
+ $xmlWriter->startElement('w:style');
+ $xmlWriter->writeAttribute('w:type', 'paragraph');
+ $xmlWriter->writeAttribute('w:customStyle', '1');
+ $xmlWriter->writeAttribute('w:styleId', $styleName);
+ $xmlWriter->startElement('w:name');
+ $xmlWriter->writeAttribute('w:val', $styleName);
+ $xmlWriter->endElement();
+
+ // Parent style
+ $basedOn = $style->getBasedOn();
+ $xmlWriter->writeElementIf(!is_null($basedOn), 'w:basedOn', 'w:val', $basedOn);
+
+ // Next paragraph style
+ $next = $style->getNext();
+ $xmlWriter->writeElementIf(!is_null($next), 'w:next', 'w:val', $next);
+
+ // w:pPr
+ $styleWriter = new ParagraphStyleWriter($xmlWriter, $style);
+ $styleWriter->write();
+
+ $xmlWriter->endElement();
+ }
+
+ /**
+ * Write table style
+ *
+ * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter
+ * @param string $styleName
+ * @param \PhpOffice\PhpWord\Style\Table $style
+ */
+ private function writeTableStyle(XMLWriter $xmlWriter, $styleName, TableStyle $style)
+ {
+ $xmlWriter->startElement('w:style');
+ $xmlWriter->writeAttribute('w:type', 'table');
+ $xmlWriter->writeAttribute('w:customStyle', '1');
+ $xmlWriter->writeAttribute('w:styleId', $styleName);
+ $xmlWriter->startElement('w:name');
+ $xmlWriter->writeAttribute('w:val', $styleName);
+ $xmlWriter->endElement();
+ $xmlWriter->startElement('w:uiPriority');
+ $xmlWriter->writeAttribute('w:val', '99');
+ $xmlWriter->endElement();
+
+ $styleWriter = new TableStyleWriter($xmlWriter, $style);
+ $styleWriter->write();
+
+ $xmlWriter->endElement(); // w:style
+ }
}
diff --git a/src/PhpWord/Writer/Word2007/Style/Image.php b/src/PhpWord/Writer/Word2007/Style/Image.php
index 36b308f1..b6b46535 100644
--- a/src/PhpWord/Writer/Word2007/Style/Image.php
+++ b/src/PhpWord/Writer/Word2007/Style/Image.php
@@ -65,16 +65,11 @@ class Image extends AbstractStyle
// Absolute/relative positioning
$positioning = $style->getPositioning();
$styleArray['position'] = $positioning;
- if ($positioning == ImageStyle::POSITION_ABSOLUTE) {
- $styleArray['mso-position-horizontal-relative'] = 'page';
- $styleArray['mso-position-vertical-relative'] = 'page';
- } elseif ($positioning == ImageStyle::POSITION_RELATIVE) {
+ if ($positioning !== null) {
$styleArray['mso-position-horizontal'] = $style->getPosHorizontal();
$styleArray['mso-position-vertical'] = $style->getPosVertical();
$styleArray['mso-position-horizontal-relative'] = $style->getPosHorizontalRel();
$styleArray['mso-position-vertical-relative'] = $style->getPosVerticalRel();
- $styleArray['margin-left'] = 0;
- $styleArray['margin-top'] = 0;
}
// Wrapping style
diff --git a/src/PhpWord/Writer/Word2007/Style/MarginBorder.php b/src/PhpWord/Writer/Word2007/Style/MarginBorder.php
index 23143ef5..88bb0109 100644
--- a/src/PhpWord/Writer/Word2007/Style/MarginBorder.php
+++ b/src/PhpWord/Writer/Word2007/Style/MarginBorder.php
@@ -29,7 +29,7 @@ class MarginBorder extends AbstractStyle
/**
* Sizes
*
- * @var int[]
+ * @var integer[]
*/
private $sizes = array();
@@ -103,7 +103,7 @@ class MarginBorder extends AbstractStyle
/**
* Set sizes
*
- * @param int[] $value
+ * @param integer[] $value
*/
public function setSizes($value)
{
diff --git a/tests/PhpWord/Tests/Element/ImageTest.php b/tests/PhpWord/Tests/Element/ImageTest.php
index b04b5fe6..11b33d87 100644
--- a/tests/PhpWord/Tests/Element/ImageTest.php
+++ b/tests/PhpWord/Tests/Element/ImageTest.php
@@ -38,6 +38,7 @@ class ImageTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($oImage->getSource(), $src);
$this->assertEquals($oImage->getMediaId(), md5($src));
$this->assertEquals($oImage->isWatermark(), false);
+ $this->assertEquals($oImage->getSourceType(), Image::SOURCE_LOCAL);
$this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Image', $oImage->getStyle());
}
diff --git a/tests/PhpWord/Tests/Element/ListItemRunTest.php b/tests/PhpWord/Tests/Element/ListItemRunTest.php
index c034a8f8..37d679f9 100644
--- a/tests/PhpWord/Tests/Element/ListItemRunTest.php
+++ b/tests/PhpWord/Tests/Element/ListItemRunTest.php
@@ -51,6 +51,17 @@ class ListItemRunTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($oListItemRun->getParagraphStyle(), 'pStyle');
}
+ /**
+ * New instance with string
+ */
+ public function testConstructListString()
+ {
+ $oListItemRun = new ListItemRun(0, 'numberingStyle');
+
+ $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\ListItemRun', $oListItemRun);
+ $this->assertCount(0, $oListItemRun->getElements());
+ }
+
/**
* New instance with array
*/
diff --git a/tests/PhpWord/Tests/Element/SectionTest.php b/tests/PhpWord/Tests/Element/SectionTest.php
index 271e81e3..af7595e2 100644
--- a/tests/PhpWord/Tests/Element/SectionTest.php
+++ b/tests/PhpWord/Tests/Element/SectionTest.php
@@ -73,7 +73,7 @@ class SectionTest extends \PHPUnit_Framework_TestCase
{
$expected = 'landscape';
$object = new Section(0);
- $object->setSettings(array('orientation' => $expected));
+ $object->setSettings(array('orientation' => $expected, 'foo' => null));
$this->assertEquals($expected, $object->getSettings()->getOrientation());
}
diff --git a/tests/PhpWord/Tests/SettingsTest.php b/tests/PhpWord/Tests/SettingsTest.php
index d188595e..61364034 100644
--- a/tests/PhpWord/Tests/SettingsTest.php
+++ b/tests/PhpWord/Tests/SettingsTest.php
@@ -103,6 +103,14 @@ class SettingsTest extends \PHPUnit_Framework_TestCase
'defaultFontName' => 'Arial',
'defaultFontSize' => 10,
);
+
+ // Test default value
+ $this->assertEquals($expected, Settings::loadConfig());
+
+ // Test with valid file
$this->assertEquals($expected, Settings::loadConfig(__DIR__ . '/../../../phpword.ini.dist'));
+
+ // Test with invalid file
+ $this->assertEmpty(Settings::loadConfig(__DIR__ . '/../../../phpunit.xml.dist'));
}
}
diff --git a/tests/PhpWord/Tests/Shared/StringTest.php b/tests/PhpWord/Tests/Shared/StringTest.php
index 086823a2..bf5862fb 100644
--- a/tests/PhpWord/Tests/Shared/StringTest.php
+++ b/tests/PhpWord/Tests/Shared/StringTest.php
@@ -54,4 +54,14 @@ class StringTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('', String::controlCharacterPHP2OOXML(''));
$this->assertEquals('_x0008_', String::controlCharacterPHP2OOXML(chr(0x08)));
}
+
+ /**
+ * Test unicode conversion
+ */
+ public function testToUnicode()
+ {
+ $this->assertEquals('a', String::toUnicode('a'));
+ $this->assertEquals('\uc0{\u8364}', String::toUnicode('€'));
+ $this->assertEquals('\uc0{\u233}', String::toUnicode('é'));
+ }
}
diff --git a/tests/PhpWord/Tests/Shared/XMLWriterTest.php b/tests/PhpWord/Tests/Shared/XMLWriterTest.php
new file mode 100644
index 00000000..08db3918
--- /dev/null
+++ b/tests/PhpWord/Tests/Shared/XMLWriterTest.php
@@ -0,0 +1,40 @@
+foo();
+ }
+}
diff --git a/tests/PhpWord/Tests/Style/AbstractStyleTest.php b/tests/PhpWord/Tests/Style/AbstractStyleTest.php
index 15ff8fac..d35e1090 100644
--- a/tests/PhpWord/Tests/Style/AbstractStyleTest.php
+++ b/tests/PhpWord/Tests/Style/AbstractStyleTest.php
@@ -45,6 +45,7 @@ class AbstractStyleTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(true, self::callProtectedMethod($stub, 'setBoolVal', array(true, false)));
$this->assertEquals(12, self::callProtectedMethod($stub, 'setIntVal', array(12, 200)));
$this->assertEquals(871.1, self::callProtectedMethod($stub, 'setFloatVal', array(871.1, 2.1)));
+ $this->assertEquals(871.1, self::callProtectedMethod($stub, 'setFloatVal', array('871.1', 2.1)));
$this->assertEquals('a', self::callProtectedMethod($stub, 'setEnumVal', array('a', array('a', 'b'), 'b')));
}
diff --git a/tests/PhpWord/Tests/Style/CellTest.php b/tests/PhpWord/Tests/Style/CellTest.php
index 1a026710..f9131728 100644
--- a/tests/PhpWord/Tests/Style/CellTest.php
+++ b/tests/PhpWord/Tests/Style/CellTest.php
@@ -52,7 +52,11 @@ class CellTest extends \PHPUnit_Framework_TestCase
foreach ($attributes as $key => $value) {
$set = "set{$key}";
$get = "get{$key}";
+
+ $this->assertNull($object->$get()); // Init with null value
+
$object->$set($value);
+
$this->assertEquals($value, $object->$get());
}
}
diff --git a/tests/PhpWord/Tests/Style/FontTest.php b/tests/PhpWord/Tests/Style/FontTest.php
index ca2105fb..432b29fb 100644
--- a/tests/PhpWord/Tests/Style/FontTest.php
+++ b/tests/PhpWord/Tests/Style/FontTest.php
@@ -74,6 +74,7 @@ class FontTest extends \PHPUnit_Framework_TestCase
);
foreach ($attributes as $key => $default) {
$get = is_bool($default) ? "is{$key}" : "get{$key}";
+ $this->assertEquals($default, $object->$get());
$object->setStyleValue("$key", null);
$this->assertEquals($default, $object->$get());
$object->setStyleValue("$key", '');
diff --git a/tests/PhpWord/Tests/Style/ParagraphTest.php b/tests/PhpWord/Tests/Style/ParagraphTest.php
index 357371c6..32e46985 100644
--- a/tests/PhpWord/Tests/Style/ParagraphTest.php
+++ b/tests/PhpWord/Tests/Style/ParagraphTest.php
@@ -96,6 +96,20 @@ class ParagraphTest extends \PHPUnit_Framework_TestCase
}
}
+ /**
+ * Test get null style value
+ */
+ public function testGetNullStyleValue()
+ {
+ $object = new Paragraph();
+
+ $attributes = array('spacing', 'indent', 'hanging', 'spaceBefore', 'spaceAfter');
+ foreach ($attributes as $key) {
+ $get = "get{$key}";
+ $this->assertNull($object->$get());
+ }
+ }
+
/**
* Test tabs
*/
diff --git a/tests/PhpWord/Tests/Style/TableTest.php b/tests/PhpWord/Tests/Style/TableTest.php
index a7b46c1f..2afbab74 100644
--- a/tests/PhpWord/Tests/Style/TableTest.php
+++ b/tests/PhpWord/Tests/Style/TableTest.php
@@ -74,6 +74,9 @@ class TableTest extends \PHPUnit_Framework_TestCase
'cellMarginLeft' => 240,
'cellMarginRight' => 240,
'cellMarginBottom' => 240,
+ 'align' => 'center',
+ 'width' => 100,
+ 'unit' => 'pct',
);
foreach ($attributes as $key => $value) {
$set = "set{$key}";
@@ -146,6 +149,7 @@ class TableTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($value, $object->$get());
}
$this->assertEquals($values, $object->getCellMargin());
+ $this->assertTrue($object->hasMargin());
}
/**
diff --git a/tests/PhpWord/Tests/Writer/HTMLTest.php b/tests/PhpWord/Tests/Writer/HTMLTest.php
index 34e9d2bd..0a59b3df 100644
--- a/tests/PhpWord/Tests/Writer/HTMLTest.php
+++ b/tests/PhpWord/Tests/Writer/HTMLTest.php
@@ -92,8 +92,8 @@ class HTMLTest extends \PHPUnit_Framework_TestCase
$textrun = $section->addTextRun('Paragraph');
$textrun->addLink('http://test.com');
$textrun->addImage($localImage);
- $textrun->addFootnote();
- $textrun->addEndnote();
+ $textrun->addFootnote()->addText('Footnote');
+ $textrun->addEndnote()->addText('Endnote');
$section = $phpWord->addSection();
diff --git a/tests/PhpWord/Tests/Writer/ODText/Part/ContentTest.php b/tests/PhpWord/Tests/Writer/ODText/Part/ContentTest.php
index 03f0cfeb..f75946cc 100644
--- a/tests/PhpWord/Tests/Writer/ODText/Part/ContentTest.php
+++ b/tests/PhpWord/Tests/Writer/ODText/Part/ContentTest.php
@@ -50,8 +50,9 @@ class ContentTest extends \PHPUnit_Framework_TestCase
$phpWord->setDefaultFontName('Verdana');
$phpWord->addFontStyle('Font', array('size' => 11));
$phpWord->addParagraphStyle('Paragraph', array('align' => 'center'));
+ $phpWord->addTableStyle('tblStyle', array('width' => 100));
- $section = $phpWord->addSection();
+ $section = $phpWord->addSection(array('colsNum' => 2));
$section->addText($expected);
$section->addText('Test font style', 'Font');
$section->addText('Test paragraph style', null, 'Paragraph');
@@ -60,14 +61,14 @@ class ContentTest extends \PHPUnit_Framework_TestCase
$section->addTextBreak();
$section->addPageBreak();
$section->addListItem('Test list item');
- $section->addImage($imageSrc);
+ $section->addImage($imageSrc, array('width' => 50));
$section->addObject($objectSrc);
$section->addTOC();
$textrun = $section->addTextRun();
$textrun->addText('Test text run');
- $table = $section->addTable();
+ $table = $section->addTable(array('width' => 50));
$cell = $table->addRow()->addCell();
$cell = $table->addRow()->addCell();
$cell->addText('Test');
@@ -82,6 +83,8 @@ class ContentTest extends \PHPUnit_Framework_TestCase
$footer = $section->addFooter();
$footer->addPreserveText('{PAGE}');
+ $table = $section->addTable('tblStyle')->addRow()->addCell();
+
$doc = TestHelperDOCX::getDocument($phpWord, 'ODText');
$element = "/office:document-content/office:body/office:text/text:section/text:p";
diff --git a/tests/PhpWord/Tests/Writer/ODText/StyleTest.php b/tests/PhpWord/Tests/Writer/ODText/StyleTest.php
index 387accfc..cd5ea0eb 100644
--- a/tests/PhpWord/Tests/Writer/ODText/StyleTest.php
+++ b/tests/PhpWord/Tests/Writer/ODText/StyleTest.php
@@ -28,7 +28,7 @@ class StyleTest extends \PHPUnit_Framework_TestCase
*/
public function testEmptyStyles()
{
- $styles = array('Font', 'Paragraph');
+ $styles = array('Font', 'Paragraph', 'Image', 'Section', 'Table');
foreach ($styles as $style) {
$objectClass = 'PhpOffice\\PhpWord\\Writer\\ODText\\Style\\' . $style;
$xmlWriter = new XMLWriter();
diff --git a/tests/PhpWord/Tests/_files/documents/reader.docx b/tests/PhpWord/Tests/_files/documents/reader.docx
index 5f37ef4b..d09091b1 100644
Binary files a/tests/PhpWord/Tests/_files/documents/reader.docx and b/tests/PhpWord/Tests/_files/documents/reader.docx differ
diff --git a/tests/PhpWord/Tests/_files/documents/reader.odt b/tests/PhpWord/Tests/_files/documents/reader.odt
index 9e18e619..d37c4e66 100644
Binary files a/tests/PhpWord/Tests/_files/documents/reader.odt and b/tests/PhpWord/Tests/_files/documents/reader.odt differ