`__
- `Rich Text Format (RTF) Specification, version
diff --git a/docs/src/documentation.md b/docs/src/documentation.md
index bcf38a048d..84522c1b5f 100644
--- a/docs/src/documentation.md
+++ b/docs/src/documentation.md
@@ -33,6 +33,9 @@ Don't forget to change `code::` directive to `code-block::` in the resulting rst
- [Table of contents](#table-of-contents)
- [Footnotes & endnotes](#footnotes-endnotes)
- [Checkboxes](#checkboxes)
+ - [Textboxes](#textboxes)
+ - [Fields](#fields)
+ - [Lines](#lines)
- [Templates](#templates)
- [Writers & readers](#writers-readers)
- [OOXML](#ooxml)
@@ -40,6 +43,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,11 +51,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).
-No Windows operating system is needed for usage because the resulting DOCX, ODT, or RTF files can be opened by all major [word processing softwares](http://en.wikipedia.org/wiki/List_of_word_processors).
-
-PHPWord is an open source project licensed under LGPL. PHPWord is [unit tested](https://travis-ci.org/PHPOffice/PHPWord) to make sure that the released versions are stable.
-
-**Want to contribute?** [Fork us](https://github.com/PHPOffice/PHPWord/fork) or [submit](https://github.com/PHPOffice/PHPWord/issues) your bug reports or feature requests to us.
+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
@@ -82,19 +82,18 @@ Below are the supported features for each file formats.
| Features | | DOCX | ODT | RTF | HTML | PDF |
|-------------------------|--------------------|------|-----|-----|------|-----|
-| **Document Properties** | Standard | ✓ | | | | |
-| | Extended | ✓ | | | | |
-| | UserDefined | ✓ | | | | |
+| **Document Properties** | Standard | ✓ | ✓ | ✓ | ✓ | |
+| | Custom | ✓ | ✓ | | | |
| **Element Type** | Text | ✓ | ✓ | ✓ | ✓ | ✓ |
| | Text Run | ✓ | ✓ | ✓ | ✓ | ✓ |
-| | Title | ✓ | | | ✓ | ✓ |
-| | Link | ✓ | ✓ | | ✓ | ✓ |
+| | Title | ✓ | ✓ | | ✓ | ✓ |
+| | Link | ✓ | ✓ | ✓ | ✓ | ✓ |
| | Preserve Text | ✓ | | | | |
| | Text Break | ✓ | ✓ | ✓ | ✓ | ✓ |
-| | Page Break | ✓ | | | | |
+| | Page Break | ✓ | | ✓ | | |
| | List | ✓ | | | | |
-| | Table | ✓ | ✓ | | ✓ | ✓ |
-| | Image | ✓ | ✓ | | ✓ | |
+| | Table | ✓ | ✓ | ✓ | ✓ | ✓ |
+| | Image | ✓ | ✓ | ✓ | ✓ | |
| | Object | ✓ | | | | |
| | Watermark | ✓ | | | | |
| | Table of Contents | ✓ | | | | |
@@ -112,35 +111,43 @@ Below are the supported features for each file formats.
### Readers
-| Features | | DOCX | ODT | RTF |
-|-------------------------|--------------------|------|-----|-----|
-| **Document Properties** | Standard | ✓ | | |
-| | Extended | ✓ | | |
-| | UserDefined | ✓ | | |
-| **Element Type** | Text | ✓ | ✓ | |
-| | Text Run | ✓ | | |
-| | Title | ✓ | ✓ | |
-| | Link | ✓ | | |
-| | Preserve Text | ✓ | | |
-| | Text Break | ✓ | | |
-| | Page Break | ✓ | | |
-| | List | ✓ | ✓ | |
-| | Table | ✓ | | |
-| | Image | ✓ | | |
-| | Object | | | |
-| | Watermark | | | |
-| | Table of Contents | | | |
-| | Header | ✓ | | |
-| | Footer | ✓ | | |
-| | Footnote | ✓ | | |
-| | Endnote | ✓ | | |
-| **Graphs** | 2D basic graphs | | | |
-| | 2D advanced graphs | | | |
-| | 3D graphs | | | |
-| **Math** | OMML support | | | |
-| | MathML support | | | |
-| **Bonus** | Encryption | | | |
-| | Protection | | | |
+| Features | | DOCX | ODT | RTF | HTML|
+|-------------------------|--------------------|------|-----|-----|-----|
+| **Document Properties** | Standard | ✓ | | | |
+| | Custom | ✓ | | | |
+| **Element Type** | Text | ✓ | ✓ | ✓ | ✓ |
+| | Text Run | ✓ | | | |
+| | Title | ✓ | ✓ | | |
+| | Link | ✓ | | | |
+| | Preserve Text | ✓ | | | |
+| | Text Break | ✓ | | | |
+| | Page Break | ✓ | | | |
+| | List | ✓ | ✓ | | ✓ |
+| | Table | ✓ | | | ✓ |
+| | Image | ✓ | | | |
+| | Object | | | | |
+| | Watermark | | | | |
+| | Table of Contents | | | | |
+| | Header | ✓ | | | |
+| | Footer | ✓ | | | |
+| | Footnote | ✓ | | | |
+| | Endnote | ✓ | | | |
+| **Graphs** | 2D basic graphs | | | | |
+| | 2D advanced graphs | | | | |
+| | 3D graphs | | | | |
+| **Math** | OMML support | | | | |
+| | MathML support | | | | |
+| **Bonus** | Encryption | | | | |
+| | Protection | | | | |
+
+## Contributing
+
+We welcome everyone to contribute to PHPWord. Below are some of the things that you can do to contribute:
+
+- Read [our contributing guide](https://github.com/PHPOffice/PHPWord/blob/master/CONTRIBUTING.md)
+- [Fork us](https://github.com/PHPOffice/PHPWord/fork) and [request a pull](https://github.com/PHPOffice/PHPWord/pulls) to the [develop](https://github.com/PHPOffice/PHPWord/tree/develop) branch
+- Submit [bug reports or feature requests](https://github.com/PHPOffice/PHPWord/issues) to GitHub
+- Follow [@PHPWord](https://twitter.com/PHPWord) and [@PHPOffice](https://twitter.com/PHPOffice) on Twitter
# Installing/configuring
@@ -443,18 +450,21 @@ Below are the matrix of element availability in each container. The column shows
| 2 | Text Run | v | v | v | v | - | - |
| 3 | Link | v | v | v | v | v | v |
| 4 | Title | v | ? | ? | ? | ? | ? |
-| 5 | Preserve Text | ? | v | v | v* | ? | ? |
+| 5 | Preserve Text | ? | v | v | v* | - | - |
| 6 | Text Break | v | v | v | v | v | v |
| 7 | Page Break | v | - | - | - | - | - |
| 8 | List | v | v | v | v | - | - |
-| 9 | Table | v | v | v | ? | - | - |
+| 9 | Table | v | v | v | v | - | - |
| 10 | Image | v | v | v | v | v | v |
| 11 | Watermark | - | v | - | - | - | - |
| 12 | Object | v | v | v | v | v | v |
| 13 | TOC | v | - | - | - | - | - |
| 14 | Footnote | v | - | - | v** | v** | - |
| 15 | Endnote | v | - | - | v** | v** | - |
-| 16 | CheckBox | v | v | v | v | ? | ? |
+| 16 | CheckBox | v | v | v | v | - | - |
+| 17 | TextBox | v | v | v | v | - | - |
+| 18 | Field | v | v | v | v | v | v |
+| 19 | Line | v | v | v | v | v | v |
Legend:
@@ -487,7 +497,7 @@ $section->addText('I am simple paragraph', $fontStyle, $paragraphStyle);
$textrun = $section->addTextRun();
$textrun->addText('I am bold', array('bold' => true));
$textrun->addText('I am italic', array('italic' => true));
-$textrun->addText('I am colored, array('color' => 'AACC00'));
+$textrun->addText('I am colored', array('color' => 'AACC00'));
```
Defined style examples:
@@ -828,6 +838,18 @@ $section->addCheckBox($name, $text, [$fontStyle], [$paragraphStyle])
- `$fontStyle` See "Font style" section.
- `$paragraphStyle` See "Paragraph style" section.
+## Textboxes
+
+To be completed.
+
+## Fields
+
+To be completed.
+
+## Lines
+
+To be completed.
+
# Templates
You can create a docx template with included search-patterns that can be replaced by any value you wish. Only single-line values can be replaced. To load a template file, use the `loadTemplate` method. After loading the docx template, you can use the `setValue` method to change the value of a search pattern. The search-pattern model is: `${search-pattern}`. It is not possible to add new PHPWord elements to a loaded template file.
@@ -926,6 +948,67 @@ 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");
+```
+
+## Create numbered headings
+
+Define a numbering style and title styles, and match the two styles (with `pStyle` and `numStyle`) like below.
+
+```php
+$phpWord->addNumberingStyle(
+ 'hNum',
+ array('type' => 'multilevel', 'levels' => array(
+ array('pStyle' => 'Heading1', 'format' => 'decimal', 'text' => '%1'),
+ array('pStyle' => 'Heading2', 'format' => 'decimal', 'text' => '%1.%2'),
+ array('pStyle' => 'Heading3', 'format' => 'decimal', 'text' => '%1.%2.%3'),
+ )
+ )
+);
+$phpWord->addTitleStyle(1, array('size' => 16), array('numStyle' => 'hNum', 'numLevel' => 0));
+$phpWord->addTitleStyle(2, array('size' => 14), array('numStyle' => 'hNum', 'numLevel' => 1));
+$phpWord->addTitleStyle(3, array('size' => 12), array('numStyle' => 'hNum', 'numLevel' => 2));
+
+$section->addTitle('Heading 1', 1);
+$section->addTitle('Heading 2', 2);
+$section->addTitle('Heading 3', 3);
+```
+
# Frequently asked questions
## Is this the same with PHPWord that I found in CodePlex?
@@ -938,13 +1021,18 @@ PHPWord requires PHP 5.3+ since 0.8, while PHPWord 0.6.3 from CodePlex can run w
# References
+## ISO/IEC 29500, Third edition, 2012-09-01
+
+- [Part 1: Fundamentals and Markup Language Reference](http://standards.iso.org/ittf/PubliclyAvailableStandards/c061750_ISO_IEC_29500-1_2012.zip)
+- [Part 2: Open Packaging Conventions](http://standards.iso.org/ittf/PubliclyAvailableStandards/c061796_ISO_IEC_29500-2_2012.zip)
+- [Part 3: Markup Compatibility and Extensibility](http://standards.iso.org/ittf/PubliclyAvailableStandards/c061797_ISO_IEC_29500-3_2012.zip)
+- [Part 4: Transitional Migration Features](http://standards.iso.org/ittf/PubliclyAvailableStandards/c061798_ISO_IEC_29500-4_2012.zip)
+
## Formal specifications
-- [Office Open XML (OOXML) (ECMA-376) Schema](http://www.schemacentral.com/sc/ooxml/ss.html)
-- [Oasis OpenDocument Standard Version 1.2](http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os.html)
-- [Rich Text Format (RTF) Specification, version 1.9.1](http://www.microsoft.com/en-us/download/details.aspx?id=10725)
+- [Oasis OpenDocument Standard Version 1.2](http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os.html)
+- [Rich Text Format (RTF) Specification, version 1.9.1](http://www.microsoft.com/en-us/download/details.aspx?id=10725)
## Other resources
-- [DocumentFormat.OpenXml.Wordprocessing Namespace on MSDN](http://msdn.microsoft.com/en-us/library/documentformat.openxml.wordprocessing%28v=office.14%29.aspx)
-
+- [DocumentFormat.OpenXml.Wordprocessing Namespace on MSDN](http://msdn.microsoft.com/en-us/library/documentformat.openxml.wordprocessing%28v=office.14%29.aspx)
diff --git a/phpmd.xml b/phpmd.xml
deleted file mode 100644
index c1ebb770d3..0000000000
--- a/phpmd.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/phpmd.xml.dist b/phpmd.xml.dist
new file mode 100644
index 0000000000..f0b62b2d6e
--- /dev/null
+++ b/phpmd.xml.dist
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index c7e5967697..015dd2edd1 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -10,7 +10,7 @@
syntaxCheck="false">
- ./tests/PhpWord/
+ ./tests/PhpWord
@@ -22,6 +22,7 @@
+
\ No newline at end of file
diff --git a/phpword.ini.dist b/phpword.ini.dist
new file mode 100644
index 0000000000..4a51ee11ce
--- /dev/null
+++ b/phpword.ini.dist
@@ -0,0 +1,14 @@
+; Default config file for PHPWord
+; Copy this file into phpword.ini and use Settings::loadConfig to load
+
+[General]
+
+compatibility = true
+zipClass = ZipArchive
+pdfRendererName = DomPDF
+pdfRendererPath =
+
+[Font]
+
+defaultFontName = Arial
+defaultFontSize = 10
diff --git a/samples/Sample_01_SimpleText.php b/samples/Sample_01_SimpleText.php
index 7202ed7edb..311f33508f 100644
--- a/samples/Sample_01_SimpleText.php
+++ b/samples/Sample_01_SimpleText.php
@@ -22,7 +22,8 @@
$section->addText('I am styled by a font style definition.', 'rStyle');
$section->addText('I am styled by a paragraph style definition.', null, 'pStyle');
$section->addText('I am styled by both font and paragraph style.', 'rStyle', 'pStyle');
-$section->addTextBreak();
+
+$section->addPageBreak();
// Inline font style
$fontStyle['name'] = 'Times New Roman';
@@ -36,10 +37,11 @@
$fontStyle['fgColor'] = 'yellow';
$fontStyle['smallCaps'] = true;
$section->addText('I am inline styled.', $fontStyle);
+
$section->addTextBreak();
// Link
-$section->addLink('http://www.google.com', null, 'NLink');
+$section->addLink('http://www.google.com', 'Google');
$section->addTextBreak();
// Image
diff --git a/samples/Sample_05_Multicolumn.php b/samples/Sample_05_Multicolumn.php
index a3083824b8..f47370605e 100644
--- a/samples/Sample_05_Multicolumn.php
+++ b/samples/Sample_05_Multicolumn.php
@@ -18,7 +18,7 @@
'colsNum' => 2,
'colsSpace' => 1440,
'breakType' => 'continuous'));
-$section->addText('Three columns, one inch (1440 twips) spacing. ' . $filler);
+$section->addText('Two columns, one inch (1440 twips) spacing. ' . $filler);
// Normal
$section = $phpWord->addSection(array('breakType' => 'continuous'));
diff --git a/samples/Sample_09_Tables.php b/samples/Sample_09_Tables.php
index 5b4b130098..882653bc71 100644
--- a/samples/Sample_09_Tables.php
+++ b/samples/Sample_09_Tables.php
@@ -51,7 +51,7 @@
// 3. colspan (gridSpan) and rowspan (vMerge)
-$section->addTextBreak(1);
+$section->addPageBreak();
$section->addText("Table with colspan and rowspan", $header);
$styleTable = array('borderSize' => 6, 'borderColor' => '999999');
@@ -84,6 +84,17 @@
$table->addCell(2000, $cellVCentered)->addText('D', null, $cellHCentered);
$table->addCell(null, $cellRowContinue);
+// 4. Nested table
+
+$section->addTextBreak(2);
+$section->addText('Nested table in a centered and 50% width table.', $header);
+
+$table = $section->addTable(array('width' => 50 * 50, 'unit' => 'pct', 'align' => 'center'));
+$cell = $table->addRow()->addCell();
+$cell->addText('This cell contains nested table.');
+$innerCell = $cell->addTable(array('align' => 'center'))->addRow()->addCell();
+$innerCell->addText('Inside nested table');
+
// Save file
echo write($phpWord, basename(__FILE__, '.php'), $writers);
if (!CLI) {
diff --git a/samples/Sample_11_ReadWord2007.php b/samples/Sample_11_ReadWord2007.php
index 09d9cab079..c0b54c7a49 100644
--- a/samples/Sample_11_ReadWord2007.php
+++ b/samples/Sample_11_ReadWord2007.php
@@ -3,7 +3,8 @@
// Read contents
$name = basename(__FILE__, '.php');
-$source = "resources/{$name}.docx";
+$source = __DIR__ . "/resources/{$name}.docx";
+
echo date('H:i:s'), " Reading contents from `{$source}`", EOL;
$phpWord = \PhpOffice\PhpWord\IOFactory::load($source);
diff --git a/samples/Sample_12_HeaderFooter.php b/samples/Sample_12_HeaderFooter.php
index 8e05328697..0fd56edc0a 100644
--- a/samples/Sample_12_HeaderFooter.php
+++ b/samples/Sample_12_HeaderFooter.php
@@ -25,6 +25,7 @@
// Add header for all other pages
$subsequent = $section->addHeader();
$subsequent->addText("Subsequent pages in Section 1 will Have this!");
+$subsequent->addImage('resources/_mars.jpg', array('width' => 80, 'height' => 80));
// Add footer
$footer = $section->addFooter();
diff --git a/samples/Sample_13_Images.php b/samples/Sample_13_Images.php
index 65e38bf5d0..dd0c88012d 100644
--- a/samples/Sample_13_Images.php
+++ b/samples/Sample_13_Images.php
@@ -26,11 +26,45 @@
foreach ($wrappingStyles as $wrappingStyle) {
$section->addTextBreak(5);
$section->addText('Wrapping style ' . $wrappingStyle);
- $section->addImage('resources/_earth.jpg', array('marginTop' => -1, 'marginLeft' => 1,
+ $section->addImage('resources/_earth.jpg', array('positioning' => 'relative', 'marginTop' => -1, 'marginLeft' => 1,
'width' => 80, 'height' => 80, 'wrappingStyle' => $wrappingStyle));
$section->addText($text);
}
+//Absolute positioning
+$section->addTextBreak(3);
+$section->addText('Absolute positioning: see top right corner of page');
+$section->addImage(
+ 'resources/_mars.jpg',
+ array(
+ '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)
+ )
+);
+
+//Relative positioning
+$section->addTextBreak(3);
+$section->addText('Relative positioning: Horizontal position center relative to column,');
+$section->addText('Vertical position top relative to line');
+$section->addImage(
+ 'resources/_mars.jpg',
+ array(
+ 'width' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(3),
+ 'height' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(3),
+ 'positioning' => \PhpOffice\PhpWord\Style\Image::POSITION_RELATIVE,
+ 'posHorizontal' => \PhpOffice\PhpWord\Style\Image::POSITION_HORIZONTAL_CENTER,
+ 'posHorizontalRel' => \PhpOffice\PhpWord\Style\Image::POSITION_RELATIVE_TO_COLUMN,
+ 'posVertical' => \PhpOffice\PhpWord\Style\Image::POSITION_VERTICAL_TOP,
+ 'posVerticalRel' => \PhpOffice\PhpWord\Style\Image::POSITION_RELATIVE_TO_LINE
+ )
+);
+
// Save file
echo write($phpWord, basename(__FILE__, '.php'), $writers);
if (!CLI) {
diff --git a/samples/Sample_14_ListItem.php b/samples/Sample_14_ListItem.php
index cd22df8e06..3a3d34abc5 100644
--- a/samples/Sample_14_ListItem.php
+++ b/samples/Sample_14_ListItem.php
@@ -18,7 +18,7 @@
array('format' => 'decimal', 'text' => '%1.', 'left' => 360, 'hanging' => 360, 'tabPos' => 360),
array('format' => 'upperLetter', 'text' => '%2.', 'left' => 720, 'hanging' => 360, 'tabPos' => 720),
)
- )
+ )
);
$predefinedMultilevel = array('listType' => \PhpOffice\PhpWord\Style\ListItem::TYPE_NUMBER_NESTED);
@@ -54,6 +54,37 @@
$section->addListItem('List Item 7', 0, 'myOwnStyle', $predefinedMultilevel, 'P-Style');
$section->addTextBreak(2);
+$section->addText('List with inline formatting.');
+$listItemRun = $section->addListItemRun();
+$listItemRun->addText('List item 1');
+$listItemRun->addText(' in bold', array('bold'=>true));
+$listItemRun = $section->addListItemRun();
+$listItemRun->addText('List item 2');
+$listItemRun->addText(' in italic', array('italic'=>true));
+$listItemRun = $section->addListItemRun();
+$listItemRun->addText('List item 3');
+$listItemRun->addText(' underlined', array('underline'=>'dash'));
+$section->addTextBreak(2);
+
+// Numbered heading
+
+$phpWord->addNumberingStyle(
+ 'headingNumbering',
+ array('type' => 'multilevel', 'levels' => array(
+ array('pStyle' => 'Heading1', 'format' => 'decimal', 'text' => '%1'),
+ array('pStyle' => 'Heading2', 'format' => 'decimal', 'text' => '%1.%2'),
+ array('pStyle' => 'Heading3', 'format' => 'decimal', 'text' => '%1.%2.%3'),
+ )
+ )
+);
+$phpWord->addTitleStyle(1, array('size' => 16), array('numStyle' => 'headingNumbering', 'numLevel' => 0));
+$phpWord->addTitleStyle(2, array('size' => 14), array('numStyle' => 'headingNumbering', 'numLevel' => 1));
+$phpWord->addTitleStyle(3, array('size' => 12), array('numStyle' => 'headingNumbering', 'numLevel' => 2));
+
+$section->addTitle('Heading 1', 1);
+$section->addTitle('Heading 2', 2);
+$section->addTitle('Heading 3', 3);
+
// Save file
echo write($phpWord, basename(__FILE__, '.php'), $writers);
if (!CLI) {
diff --git a/samples/Sample_24_ReadODText.php b/samples/Sample_24_ReadODText.php
index bb5332e662..42df23ae2b 100644
--- a/samples/Sample_24_ReadODText.php
+++ b/samples/Sample_24_ReadODText.php
@@ -3,7 +3,8 @@
// Read contents
$name = basename(__FILE__, '.php');
-$source = "resources/{$name}.odt";
+$source = __DIR__ . "/resources/{$name}.odt";
+
echo date('H:i:s'), " Reading contents from `{$source}`", EOL;
$phpWord = \PhpOffice\PhpWord\IOFactory::load($source, 'ODText');
diff --git a/samples/Sample_25_TextBox.php b/samples/Sample_25_TextBox.php
new file mode 100644
index 0000000000..0a659ddb3d
--- /dev/null
+++ b/samples/Sample_25_TextBox.php
@@ -0,0 +1,39 @@
+addSection();
+
+// In section
+$textbox = $section->addTextBox(array('align' => 'center', 'width' => 400, 'height' => 150, 'borderSize' => 1, 'borderColor' => '#FF0000'));
+$textbox->addText('Text box content in section.');
+$textbox->addText('Another line.');
+$cell = $textbox->addTable()->addRow()->addCell();
+$cell->addText('Table inside textbox');
+
+// Inside table
+$section->addTextBreak(2);
+$cell = $section->addTable()->addRow()->addCell(300);
+$textbox = $cell->addTextBox(array('borderSize' => 1, 'borderColor' => '#0000FF', 'innerMargin' => 100));
+$textbox->addText('Textbox inside table');
+
+// Inside header with textrun
+$header = $section->addHeader();
+$textbox = $header->addTextBox(array('width' => 600, 'borderSize' => 1, 'borderColor' => '#00FF00'));
+$textrun = $textbox->addTextRun();
+$textrun->addText('TextBox in header. TextBox can contain a TextRun ');
+$textrun->addText('with bold text', array('bold' => true));
+$textrun->addText(', ');
+$textrun->addLink('http://www.google.com', 'link');
+$textrun->addText(', and image ');
+$textrun->addImage('resources/_earth.jpg', array('width' => 18, 'height' => 18));
+$textrun->addText('.');
+
+// Save file
+echo write($phpWord, basename(__FILE__, '.php'), $writers);
+if (!CLI) {
+ include_once 'Sample_Footer.php';
+}
diff --git a/samples/Sample_26_Html.php b/samples/Sample_26_Html.php
new file mode 100644
index 0000000000..92b3aa498c
--- /dev/null
+++ b/samples/Sample_26_Html.php
@@ -0,0 +1,23 @@
+addSection();
+$html = 'Adding element via HTML
';
+$html .= 'Some well formed HTML snippet needs to be used
';
+$html .= 'With for example some1 inline formatting1
';
+$html .= 'Unordered (bulleted) list:
';
+$html .= '';
+$html .= 'Ordered (numbered) list:
';
+$html .= '- Item 1
- Item 2
';
+
+\PhpOffice\PhpWord\Shared\Html::addHtml($section, $html);
+
+// Save file
+echo write($phpWord, basename(__FILE__, '.php'), $writers);
+if (!CLI) {
+ include_once 'Sample_Footer.php';
+}
\ No newline at end of file
diff --git a/samples/Sample_27_Field.php b/samples/Sample_27_Field.php
new file mode 100644
index 0000000000..fd75037275
--- /dev/null
+++ b/samples/Sample_27_Field.php
@@ -0,0 +1,31 @@
+addSection();
+
+// Add Field elements
+// See Element/Field.php for all options
+$section->addText('Date field:');
+$section->addField('DATE', array('dateformat'=>'dddd d MMMM yyyy H:mm:ss'), array('PreserveFormat'));
+
+$section->addText('Page field:');
+$section->addField('PAGE', array('format'=>'ArabicDash'));
+
+$section->addText('Number of pages field:');
+$section->addField('NUMPAGES', array('format'=>'Arabic', 'numformat'=>'0,00'), array('PreserveFormat'));
+
+$textrun = $section->addTextRun(array('align' => 'center'));
+$textrun->addText('This is the date of lunar calendar ');
+$textrun->addField('DATE', array('dateformat'=>'d-M-yyyy H:mm:ss'), array('PreserveFormat', 'LunarCalendar'));
+$textrun->addText(' written in a textrun.');
+
+// Save file
+echo write($phpWord, basename(__FILE__, '.php'), $writers);
+if (!CLI) {
+ include_once 'Sample_Footer.php';
+}
diff --git a/samples/Sample_28_ReadRTF.php b/samples/Sample_28_ReadRTF.php
new file mode 100644
index 0000000000..76ac3d48bb
--- /dev/null
+++ b/samples/Sample_28_ReadRTF.php
@@ -0,0 +1,15 @@
+addSection();
+
+// Add Line elements
+// See Element/Line.php for all options
+$section->addText('Horizontal Line (Inline style):');
+$section->addLine(
+ array(
+ 'width' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(4),
+ 'height' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(0),
+ 'positioning' => 'absolute'
+ )
+);
+$section->addText('Vertical Line (Inline style):');
+$section->addLine(
+ array(
+ 'width' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(0),
+ 'height' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(1),
+ 'positioning' => 'absolute'
+ )
+);
+// Two text break
+$section->addTextBreak(1);
+
+$section->addText('Positioned Line (red):');
+$section->addLine(
+ array(
+ 'width' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(4),
+ 'height' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(1),
+ 'positioning' => 'absolute',
+ 'posHorizontalRel' => 'page',
+ 'posVerticalRel' => 'page',
+ 'marginLeft' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(10),
+ 'marginTop' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(8),
+ 'wrappingStyle' => \PhpOffice\PhpWord\Style\Image::WRAPPING_STYLE_SQUARE,
+ 'color' => 'red'
+ )
+);
+
+$section->addText('Horizontal Formatted Line');
+$section->addLine(
+ array(
+ 'width' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(15),
+ 'height' => \PhpOffice\PhpWord\Shared\Drawing::centimetersToPixels(0),
+ 'positioning' => 'absolute',
+ 'beginArrow' => \PhpOffice\PhpWord\Style\Line::ARROW_STYLE_BLOCK,
+ 'endArrow' => \PhpOffice\PhpWord\Style\Line::ARROW_STYLE_OVAL,
+ 'dash' => \PhpOffice\PhpWord\Style\Line::DASH_STYLE_LONG_DASH_DOT_DOT,
+ 'weight' => 10
+ )
+);
+
+// Save file
+echo write($phpWord, basename(__FILE__, '.php'), $writers);
+if (!CLI) {
+ include_once 'Sample_Footer.php';
+}
diff --git a/samples/Sample_30_ReadHTML.php b/samples/Sample_30_ReadHTML.php
new file mode 100644
index 0000000000..029f8c8cfd
--- /dev/null
+++ b/samples/Sample_30_ReadHTML.php
@@ -0,0 +1,15 @@
+');
define('SCRIPT_FILENAME', basename($_SERVER['SCRIPT_FILENAME'], '.php'));
define('IS_INDEX', SCRIPT_FILENAME == 'index');
-require_once '../src/PhpWord/Autoloader.php';
-\PhpOffice\PhpWord\Autoloader::register();
+require_once __DIR__ . '/../src/PhpWord/Autoloader.php';
+Autoloader::register();
+Settings::loadConfig();
// Set writers
$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf', 'HTML' => 'html', 'PDF' => 'pdf');
// Set PDF renderer
-$rendererName = \PhpOffice\PhpWord\Settings::PDF_RENDERER_DOMPDF;
-$rendererLibraryPath = ''; // DomPDF library path
-
-if (!\PhpOffice\PhpWord\Settings::setPdfRenderer($rendererName, $rendererLibraryPath)) {
+if (Settings::getPdfRendererPath() === null) {
$writers['PDF'] = null;
}
@@ -60,9 +62,9 @@ function write($phpWord, $filename, $writers)
foreach ($writers as $writer => $extension) {
$result .= date('H:i:s') . " Write to {$writer} format";
if (!is_null($extension)) {
- $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer);
- $xmlWriter->save("{$filename}.{$extension}");
- rename("{$filename}.{$extension}", "results/{$filename}.{$extension}");
+ $xmlWriter = IOFactory::createWriter($phpWord, $writer);
+ $xmlWriter->save(__DIR__ . "/{$filename}.{$extension}");
+ rename(__DIR__ . "/{$filename}.{$extension}", __DIR__ . "/results/{$filename}.{$extension}");
} else {
$result .= ' ... NOT DONE!';
}
diff --git a/samples/index.php b/samples/index.php
index 71e8518ad7..420c542019 100644
--- a/samples/index.php
+++ b/samples/index.php
@@ -1,5 +1,13 @@
array('PHP 5.3.0', version_compare(phpversion(), '5.3.0', '>=')),
+ 'xml' => array('PHP extension XML', extension_loaded('xml')),
+ 'zip' => array('PHP extension ZipArchive (optional)', extension_loaded('zip')),
+ 'gd' => array('PHP extension GD (optional)', extension_loaded('gd')),
+ 'xmlw' => array('PHP extension XMLWriter (optional)', extension_loaded('xmlwriter')),
+ 'xsl' => array('PHP extension XSL (optional)', extension_loaded('xsl')),
+);
if (!CLI) {
?>
-
-$requirements = array(
- 'php' => array('PHP 5.3.0', version_compare(phpversion(), '5.3.0', '>=')),
- 'zip' => array('PHP extension ZipArchive', extension_loaded('zip')),
- 'xml' => array('PHP extension XML', extension_loaded('xml')),
- 'gd' => array('PHP extension GD (optional)', extension_loaded('gd')),
-);
-echo "Requirements
";
-echo "";
-foreach ($requirements as $key => $value) {
- $status = $value[1] ? 'passed' : 'failed';
- echo "- {$value[0]} ... {$status}
";
-}
-echo "
";
+Requirement check:";
+ echo "";
+ foreach ($requirements as $key => $value) {
+ list($label, $result) = $value;
+ $status = $result ? 'passed' : 'failed';
+ echo "- {$label} ... {$status}
";
+ }
+ echo "
";
include_once 'Sample_Footer.php';
+} else {
+ echo 'Requirement check:' . PHP_EOL;
+ foreach ($requirements as $key => $value) {
+ list($label, $result) = $value;
+ $status = $result ? '32m passed' : '31m failed';
+ echo "{$label} ... \033[{$status}\033[0m" . PHP_EOL;
+ }
}
diff --git a/samples/resources/Sample_11_ReadWord2007.docx b/samples/resources/Sample_11_ReadWord2007.docx
index 2143c628c5..c9a50f485a 100644
Binary files a/samples/resources/Sample_11_ReadWord2007.docx and b/samples/resources/Sample_11_ReadWord2007.docx differ
diff --git a/samples/resources/Sample_24_ReadODText.odt b/samples/resources/Sample_24_ReadODText.odt
index 9e18e61917..d37c4e6629 100644
Binary files a/samples/resources/Sample_24_ReadODText.odt and b/samples/resources/Sample_24_ReadODText.odt differ
diff --git a/samples/resources/Sample_28_ReadRTF.rtf b/samples/resources/Sample_28_ReadRTF.rtf
new file mode 100644
index 0000000000..6f9ac0f8b8
--- /dev/null
+++ b/samples/resources/Sample_28_ReadRTF.rtf
@@ -0,0 +1,21 @@
+{\rtf1
+\ansi\ansicpg1252
+\deff0
+{\fonttbl{\f0\fnil\fcharset0 Arial;}{\f1\fnil\fcharset0 Times New Roman;}}
+{\colortbl;\red255\green0\blue0;\red14\green0\blue0}
+{\*\generator PhpWord;}
+
+{\info{\title }{\subject }{\category }{\keywords }{\comment }{\author }{\operator }{\creatim \yr2014\mo05\dy27\hr23\min36\sec45}{\revtim \yr2014\mo05\dy27\hr23\min36\sec45}{\company }{\manager }}
+\deftab720\viewkind1\uc1\pard\nowidctlpar\lang1036\kerning1\fs20
+{Welcome to PhpWord}\par
+\pard\nowidctlpar{\cf0\f0 Hello World!}\par
+\par
+\par
+\pard\nowidctlpar{\cf0\f0\fs32\b\i I am styled by a font style definition.}\par
+\pard\nowidctlpar{\cf0\f0 I am styled by a paragraph style definition.}\par
+\pard\nowidctlpar\qc\sa100{\cf0\f0\fs32\b\i I am styled by both font and paragraph style.}\par
+\pard\nowidctlpar{\cf1\f1\fs40\b\i\ul\strike\super I am inline styled.}\par
+\par
+{\field {\*\fldinst {HYPERLINK "http://www.google.com"}}{\fldrslt {Google}}}\par
+\par
+}
\ No newline at end of file
diff --git a/samples/resources/Sample_30_ReadHTML.html b/samples/resources/Sample_30_ReadHTML.html
new file mode 100644
index 0000000000..5593298bfb
--- /dev/null
+++ b/samples/resources/Sample_30_ReadHTML.html
@@ -0,0 +1,15 @@
+
+
+
+PHPWord
+
+
+Adding element via HTML
+Some well formed HTML snippet needs to be used
+With for example some1 inline formatting1
+Unordered (bulleted) list:
+
+Ordered (numbered) list:
+- Item 1
- Item 2
+
+
diff --git a/src/PhpWord/Autoloader.php b/src/PhpWord/Autoloader.php
index 5bb331abb0..c467f836c3 100644
--- a/src/PhpWord/Autoloader.php
+++ b/src/PhpWord/Autoloader.php
@@ -1,10 +1,18 @@
items;
+ }
+
+ /**
+ * Get item by index
+ *
+ * @param int $index
+ * @return mixed
+ */
+ public function getItem($index)
+ {
+ if (array_key_exists($index, $this->items)) {
+ return $this->items[$index];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Set item
+ *
+ * @param int $index
+ * @param mixed $item
+ */
+ public function setItem($index, $item)
+ {
+ if (array_key_exists($index, $this->items)) {
+ $this->items[$index] = $item;
+ }
+ }
+
+ /**
+ * Add new item
+ *
+ * @param mixed $item
+ * @return int
+ */
+ public function addItem($item)
+ {
+ $index = $this->countItems() + 1;
+ $this->items[$index] = $item;
+
+ return $index;
+ }
+
+ /**
+ * Get item count
+ *
+ * @return int
+ */
+ public function countItems()
+ {
+ return count($this->items);
+ }
+}
diff --git a/src/PhpWord/Collection/Endnotes.php b/src/PhpWord/Collection/Endnotes.php
new file mode 100644
index 0000000000..c16486ae65
--- /dev/null
+++ b/src/PhpWord/Collection/Endnotes.php
@@ -0,0 +1,27 @@
+isCustomPropertySet($propertyName)) {
return $this->customProperties[$propertyName]['value'];
+ } else {
+ return null;
}
-
}
/**
@@ -422,8 +431,9 @@ public function getCustomPropertyType($propertyName)
{
if ($this->isCustomPropertySet($propertyName)) {
return $this->customProperties[$propertyName]['type'];
+ } else {
+ return null;
}
-
}
/**
@@ -478,49 +488,23 @@ public function setCustomProperty($propertyName, $propertyValue = '', $propertyT
*/
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;
@@ -559,10 +543,36 @@ public static function convertPropertyType($propertyType)
*/
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 8af70d421d..4bbe5ddff5 100644
--- a/src/PhpWord/Element/AbstractContainer.php
+++ b/src/PhpWord/Element/AbstractContainer.php
@@ -1,25 +1,45 @@
setElementIndex($this->countElements() + 1);
- $element->setElementId();
- $this->elements[] = $element;
- }
-
- /**
- * Get all elements
+ * Container type Section|Header|Footer|Footnote|Endnote|Cell|TextRun|TextBox|ListItemRun
*
- * @return array
+ * @var string
*/
- public function getElements()
- {
- return $this->elements;
- }
+ protected $container;
/**
- * Count elements
+ * Magic method to catch all 'addElement' variation
*
- * @return integer
- */
- public function countElements()
- {
- return count($this->elements);
- }
-
- /**
- * Add text element
+ * This removes addText, addTextRun, etc. When adding new element, we have to
+ * add the model in the class docblock with `@method`.
*
- * @param string $text
- * @param mixed $fontStyle
- * @param mixed $paragraphStyle
- * @return Text
+ * Warning: This makes capitalization matters, e.g. addCheckbox or addcheckbox won't work.
+ *
+ * @param mixed $function
+ * @param mixed $args
+ * @return \PhpOffice\PhpWord\Element\AbstractElement
*/
- public function addText($text, $fontStyle = null, $paragraphStyle = null)
+ public function __call($function, $args)
{
- $this->checkValidity('text');
-
- // Reset paragraph style for footnote and textrun. They have their own
- if (in_array($this->container, array('textrun', 'footnote', 'endnote'))) {
- $paragraphStyle = null;
+ $elements = array('Text', 'TextRun', 'Link', 'PreserveText', 'TextBreak',
+ 'ListItem', 'ListItemRun', 'Table', 'Image', 'Object', 'Footnote',
+ 'Endnote', 'CheckBox', 'TextBox', 'Field', 'Line');
+ $functions = array();
+ for ($i = 0; $i < count($elements); $i++) {
+ $functions[$i] = 'add' . $elements[$i];
}
- $text = String::toUTF8($text);
- $textObject = new Text($text, $fontStyle, $paragraphStyle);
- $textObject->setDocPart($this->getDocPart(), $this->getDocPartId());
- $this->addElement($textObject);
-
- return $textObject;
- }
+ // Run valid `add` command
+ if (in_array($function, $functions)) {
+ $element = str_replace('add', '', $function);
- /**
- * Add textrun element
- *
- * @param mixed $paragraphStyle
- * @return TextRun
- */
- public function addTextRun($paragraphStyle = null)
- {
- $this->checkValidity('textrun');
+ // Special case for TextBreak
+ // @todo Remove the `$count` parameter in 1.0.0 to make this element similiar to other elements?
+ if ($element == 'TextBreak') {
+ @list($count, $fontStyle, $paragraphStyle) = $args; // Suppress error
+ if ($count === null) {
+ $count = 1;
+ }
+ for ($i = 1; $i <= $count; $i++) {
+ $this->addElement($element, $fontStyle, $paragraphStyle);
+ }
- $textRun = new TextRun($paragraphStyle);
- $textRun->setDocPart($this->getDocPart(), $this->getDocPartId());
- $this->addElement($textRun);
+ // All other elements
+ } else {
+ array_unshift($args, $element); // Prepend element name to the beginning of args array
+ return call_user_func_array(array($this, 'addElement'), $args);
+ }
+ }
- return $textRun;
+ return null;
}
/**
- * Add link element
+ * Add element
*
- * @param string $linkSrc
- * @param string $linkName
- * @param mixed $fontStyle
- * @param mixed $paragraphStyle
- * @return Link
- */
- public function addLink($linkSrc, $linkName = null, $fontStyle = null, $paragraphStyle = null)
- {
- $this->checkValidity('link');
- $elementDocPart = $this->checkElementDocPart();
-
- $link = new Link(String::toUTF8($linkSrc), String::toUTF8($linkName), $fontStyle, $paragraphStyle);
- $link->setDocPart($this->getDocPart(), $this->getDocPartId());
- $rId = Media::addElement($elementDocPart, 'link', $linkSrc);
- $link->setRelationId($rId);
- $this->addElement($link);
-
- return $link;
- }
-
- /**
- * Add a Title Element
+ * Each element has different number of parameters passed
*
- * @param string $text
- * @param int $depth
- * @return Title
- * @todo Enable title element in other containers
+ * @param string $elementName
+ * @return \PhpOffice\PhpWord\Element\AbstractElement
*/
- public function addTitle($text, $depth = 1)
+ protected function addElement($elementName)
{
- $this->checkValidity('title');
-
- $styles = Style::getStyles();
- if (array_key_exists('Heading_' . $depth, $styles)) {
- $style = 'Heading' . $depth;
- } else {
- $style = null;
+ $elementClass = __NAMESPACE__ . '\\' . $elementName;
+ $this->checkValidity($elementName);
+
+ // Get arguments
+ $args = func_get_args();
+ $withoutP = in_array($this->container, array('TextRun', 'Footnote', 'Endnote', 'ListItemRun', 'Field'));
+ if ($withoutP && ($elementName == 'Text' || $elementName == 'PreserveText')) {
+ $args[3] = null; // Remove paragraph style for texts in textrun
+ }
+ $source = '';
+ if (count($args) > 1) {
+ $source = $args[1];
}
- $text = String::toUTF8($text);
- $title = new Title($text, $depth, $style);
- $title->setDocPart($this->getDocPart(), $this->getDocPartId());
- $data = Titles::addTitle($text, $depth);
- $anchor = $data[0];
- $bookmarkId = $data[1];
- $title->setAnchor($anchor);
- $title->setBookmarkId($bookmarkId);
- $this->addElement($title);
-
- return $title;
- }
-
- /**
- * Add preserve text element
- *
- * @param string $text
- * @param mixed $fontStyle
- * @param mixed $paragraphStyle
- * @return PreserveText
- */
- public function addPreserveText($text, $fontStyle = null, $paragraphStyle = null)
- {
- $this->checkValidity('preservetext');
-
- $preserveText = new PreserveText(String::toUTF8($text), $fontStyle, $paragraphStyle);
- $preserveText->setDocPart($this->getDocPart(), $this->getDocPartId());
- $this->addElement($preserveText);
- return $preserveText;
- }
+ // Create element using reflection
+ $reflection = new \ReflectionClass($elementClass);
+ $elementArgs = $args;
+ array_shift($elementArgs); // Shift the $elementName off the beginning of array
- /**
- * Add text break element
- *
- * @param int $count
- * @param mixed $fontStyle
- * @param mixed $paragraphStyle
- */
- public function addTextBreak($count = 1, $fontStyle = null, $paragraphStyle = null)
- {
- $this->checkValidity('textbreak');
+ /** @var \PhpOffice\PhpWord\Element\AbstractElement $element Type hint */
+ $element = $reflection->newInstanceArgs($elementArgs);
- for ($i = 1; $i <= $count; $i++) {
- $textBreak = new TextBreak($fontStyle, $paragraphStyle);
- $textBreak->setDocPart($this->getDocPart(), $this->getDocPartId());
- $this->addElement($textBreak);
- }
- }
+ // Set nested level and relation Id
+ $this->setElementNestedLevel($element);
+ $this->setElementRelationId($element, $elementName, $source);
- /**
- * Add listitem element
- *
- * @param string $text
- * @param int $depth
- * @param mixed $fontStyle
- * @param mixed $styleList
- * @param mixed $paragraphStyle
- * @return ListItem
- */
- public function addListItem($text, $depth = 0, $fontStyle = null, $styleList = null, $paragraphStyle = null)
- {
- $this->checkValidity('listitem');
+ // Set other properties and add element into collection
+ $element->setDocPart($this->getDocPart(), $this->getDocPartId());
+ $element->setElementIndex($this->countElements() + 1);
+ $element->setElementId();
+ $element->setPhpWord($this->phpWord);
- $listItem = new ListItem(String::toUTF8($text), $depth, $fontStyle, $styleList, $paragraphStyle);
- $listItem->setDocPart($this->getDocPart(), $this->getDocPartId());
- $this->addElement($listItem);
+ $this->elements[] = $element;
- return $listItem;
+ return $element;
}
/**
- * Add table element
+ * Get all elements
*
- * @param mixed $style
- * @return Table
+ * @return array
*/
- public function addTable($style = null)
+ public function getElements()
{
- $this->checkValidity('table');
-
- $table = new Table($this->getDocPart(), $this->getDocPartId(), $style);
- $this->addElement($table);
-
- return $table;
+ return $this->elements;
}
/**
- * Add image element
+ * Count elements
*
- * @param string $src
- * @param mixed $style Image style
- * @param boolean $isWatermark
- * @return Image
+ * @return int
*/
- public function addImage($src, $style = null, $isWatermark = false)
+ public function countElements()
{
- $this->checkValidity('image');
- $elementDocPart = $this->checkElementDocPart();
-
- $image = new Image($src, $style, $isWatermark);
- $image->setDocPart($this->getDocPart(), $this->getDocPartId());
- $rId = Media::addElement($elementDocPart, 'image', $src, $image);
- $image->setRelationId($rId);
- $this->addElement($image);
-
- return $image;
+ return count($this->elements);
}
/**
- * Add OLE-object element
- *
- * All exceptions should be handled by \PhpOffice\PhpWord\Element\Object
- *
- * @param string $src
- * @param mixed $style
- * @return Object
- * @throws \PhpOffice\PhpWord\Exception\Exception
- * @todo Enable OLE object element in header and footer
+ * Set element nested level based on container; add one when it's inside a cell
*/
- public function addObject($src, $style = null)
+ private function setElementNestedLevel(AbstractElement $element)
{
- $this->checkValidity('object');
- $elementDocPart = $this->checkElementDocPart();
-
- $object = new Object($src, $style);
- $object->setDocPart($this->getDocPart(), $this->getDocPartId());
- if (!is_null($object->getSource())) {
- $inf = pathinfo($src);
- $ext = $inf['extension'];
- if (strlen($ext) == 4 && strtolower(substr($ext, -1)) == 'x') {
- $ext = substr($ext, 0, -1);
- }
- $icon = realpath(__DIR__ . "/../resources/{$ext}.png");
- $rId = Media::addElement($elementDocPart, 'object', $src);
- $object->setRelationId($rId);
- $rIdimg = Media::addElement($elementDocPart, 'image', $icon, new Image($icon));
- $object->setImageRelationId($rIdimg);
- $this->addElement($object);
-
- return $object;
+ if ($this->container == 'Cell') {
+ $element->setNestedLevel($this->getNestedLevel() + 1);
} else {
- throw new InvalidObjectException();
+ $element->setNestedLevel($this->getNestedLevel());
}
}
/**
- * Add footnote element
- *
- * @param mixed $paragraphStyle
- * @return Footnote
- */
- public function addFootnote($paragraphStyle = null)
- {
- $this->checkValidity('footnote');
-
- $footnote = new Footnote($paragraphStyle);
- $rId = Footnotes::addElement($footnote);
-
- $footnote->setDocPart('footnote', $this->getDocPartId());
- $footnote->setRelationId($rId);
- $this->addElement($footnote);
-
- return $footnote;
- }
-
- /**
- * Add endnote element
- *
- * @param mixed $paragraphStyle
- * @return Endnote
- */
- public function addEndnote($paragraphStyle = null)
- {
- $this->checkValidity('endnote');
-
- $endnote = new Endnote($paragraphStyle);
- $rId = Endnotes::addElement($endnote);
-
- $endnote->setDocPart('endnote', $this->getDocPartId());
- $endnote->setRelationId($rId);
- $this->addElement($endnote);
-
- return $endnote;
- }
-
- /**
- * Add a CheckBox Element
+ * Set relation Id
*
- * @param string $name
- * @param string $text
- * @param mixed $fontStyle
- * @param mixed $paragraphStyle
- * @return CheckBox
+ * @param string $elementName
+ * @param string $source
*/
- public function addCheckBox($name, $text, $fontStyle = null, $paragraphStyle = null)
+ private function setElementRelationId(AbstractElement $element, $elementName, $source)
{
- $this->checkValidity('checkbox');
+ $mediaContainer = $this->getMediaContainer();
+ $hasMediaRelation = in_array($elementName, array('Link', 'Image', 'Object'));
+ $hasOtherRelation = in_array($elementName, array('Footnote', 'Endnote', 'Title'));
+
+ // Set relation Id for media elements (link, image, object; legacy of OOXML)
+ // Only Image that needs to be passed to Media class
+ if ($hasMediaRelation) {
+ /** @var \PhpOffice\PhpWord\Element\Image $element Type hint */
+ $image = ($elementName == 'Image') ? $element : null;
+ $rId = Media::addElement($mediaContainer, strtolower($elementName), $source, $image);
+ $element->setRelationId($rId);
+ }
- $checkBox = new CheckBox(String::toUTF8($name), String::toUTF8($text), $fontStyle, $paragraphStyle);
- $checkBox->setDocPart($this->getDocPart(), $this->getDocPartId());
- $this->addElement($checkBox);
+ // Set relation Id for icon of object element
+ if ($elementName == 'Object') {
+ /** @var \PhpOffice\PhpWord\Element\Object $element Type hint */
+ $rIdIcon = Media::addElement($mediaContainer, 'image', $element->getIcon(), new Image($element->getIcon()));
+ $element->setImageRelationId($rIdIcon);
+ }
- return $checkBox;
+ // Set relation Id for elements that will be registered in the Collection subnamespaces
+ if ($hasOtherRelation && $this->phpWord instanceof PhpWord) {
+ $addMethod = "add{$elementName}";
+ $rId = $this->phpWord->$addMethod($element);
+ $element->setRelationId($rId);
+ }
}
/**
* Check if a method is allowed for the current container
*
* @param string $method
- * @return boolean
+ * @return bool
+ * @throws \BadMethodCallException
*/
private function checkValidity($method)
{
// Valid containers for each element
- $allContainers = array('section', 'header', 'footer', 'cell', 'textrun', 'footnote', 'endnote');
+ $allContainers = array(
+ 'Section', 'Header', 'Footer', 'Footnote', 'Endnote',
+ 'Cell', 'TextRun', 'TextBox', 'ListItemRun',
+ );
$validContainers = array(
- 'text' => $allContainers,
- 'link' => $allContainers,
- 'textbreak' => $allContainers,
- 'image' => $allContainers,
- 'object' => $allContainers,
- 'textrun' => array('section', 'header', 'footer', 'cell'),
- 'listitem' => array('section', 'header', 'footer', 'cell'),
- 'checkbox' => array('section', 'header', 'footer', 'cell'),
- 'table' => array('section', 'header', 'footer'),
- 'footnote' => array('section', 'textrun', 'cell'),
- 'endnote' => array('section', 'textrun', 'cell'),
- 'preservetext' => array('header', 'footer', 'cell'),
- 'title' => array('section'),
+ 'Text' => $allContainers,
+ 'Link' => $allContainers,
+ 'TextBreak' => $allContainers,
+ 'Image' => $allContainers,
+ 'Object' => $allContainers,
+ 'Field' => $allContainers,
+ 'Line' => $allContainers,
+ 'TextRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'),
+ 'ListItem' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'),
+ 'ListItemRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'),
+ 'Table' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'),
+ 'CheckBox' => array('Section', 'Header', 'Footer', 'Cell'),
+ 'TextBox' => array('Section', 'Header', 'Footer', 'Cell'),
+ 'Footnote' => array('Section', 'TextRun', 'Cell'),
+ 'Endnote' => array('Section', 'TextRun', 'Cell'),
+ 'PreserveText' => array('Header', 'Footer', 'Cell'),
);
// Special condition, e.g. preservetext can only exists in cell when
// the cell is located in header or footer
$validSubcontainers = array(
- 'preservetext' => array(array('cell'), array('header', 'footer')),
- 'footnote' => array(array('cell', 'textrun'), array('section')),
- 'endnote' => array(array('cell', 'textrun'), array('section')),
+ 'PreserveText' => array(array('Cell'), array('Header', 'Footer')),
+ 'Footnote' => array(array('Cell', 'TextRun'), array('Section')),
+ 'Endnote' => array(array('Cell', 'TextRun'), array('Section')),
);
// Check if a method is valid for current container
if (array_key_exists($method, $validContainers)) {
if (!in_array($this->container, $validContainers[$method])) {
- throw new \BadMethodCallException();
+ throw new \BadMethodCallException("Cannot add $method in $this->container.");
}
}
// Check if a method is valid for current container, located in other container
@@ -396,7 +273,7 @@ private function checkValidity($method)
$allowedDocParts = $rules[1];
foreach ($containers as $container) {
if ($this->container == $container && !in_array($this->getDocPart(), $allowedDocParts)) {
- throw new \BadMethodCallException();
+ throw new \BadMethodCallException("Cannot add $method in $this->container.");
}
}
}
@@ -405,16 +282,21 @@ private function checkValidity($method)
}
/**
- * Return element location in document: section, headerx, or footerx
+ * Return media element (image, object, link) container name
+ *
+ * @return string section|headerx|footerx|footnote|endnote
*/
- private function checkElementDocPart()
+ private function getMediaContainer()
{
- $isCellTextrun = in_array($this->container, array('cell', 'textrun'));
- $docPart = $isCellTextrun ? $this->getDocPart() : $this->container;
- $docPartId = $isCellTextrun ? $this->getDocPartId() : $this->sectionId;
- $inHeaderFooter = ($docPart == 'header' || $docPart == 'footer');
+ $partName = $this->container;
+ if (in_array($partName, array('Cell', 'TextRun', 'TextBox', 'ListItemRun'))) {
+ $partName = $this->getDocPart();
+ }
+ if ($partName == 'Header' || $partName == 'Footer') {
+ $partName .= $this->getDocPartId();
+ }
- return $inHeaderFooter ? $docPart . $docPartId : $docPart;
+ return strtolower($partName);
}
/**
@@ -422,6 +304,7 @@ private function checkElementDocPart()
*
* @param string $src
* @param mixed $style
+ * @return \PhpOffice\PhpWord\Element\Image
* @deprecated 0.9.0
* @codeCoverageIgnore
*/
@@ -434,6 +317,7 @@ public function addMemoryImage($src, $style = null)
* Create textrun element
*
* @param mixed $paragraphStyle
+ * @return \PhpOffice\PhpWord\Element\TextRun
* @deprecated 0.10.0
* @codeCoverageIgnore
*/
@@ -446,6 +330,7 @@ public function createTextRun($paragraphStyle = null)
* Create footnote element
*
* @param mixed $paragraphStyle
+ * @return \PhpOffice\PhpWord\Element\Footnote
* @deprecated 0.10.0
* @codeCoverageIgnore
*/
diff --git a/src/PhpWord/Element/AbstractElement.php b/src/PhpWord/Element/AbstractElement.php
index 6e993748ea..ca45ef23f9 100644
--- a/src/PhpWord/Element/AbstractElement.php
+++ b/src/PhpWord/Element/AbstractElement.php
@@ -1,14 +1,23 @@
phpWord;
+ }
+
+ /**
+ * Set PhpWord as reference
+ *
+ * @param \PhpOffice\PhpWord\PhpWord
+ */
+ public function setPhpWord(PhpWord &$phpWord = null)
+ {
+ $this->phpWord = &$phpWord;
+ }
+
/**
* Get section number
*
- * @return integer
+ * @return int
*/
public function getSectionId()
{
@@ -89,7 +127,7 @@ public function getSectionId()
* Set doc part
*
* @param string $docPart
- * @param integer $docPartId
+ * @param int $docPartId
*/
public function setDocPart($docPart, $docPartId = 1)
{
@@ -110,7 +148,7 @@ public function getDocPart()
/**
* Get doc part Id
*
- * @return integer
+ * @return int
*/
public function getDocPartId()
{
@@ -168,21 +206,41 @@ public function getRelationId()
/**
* Set relation Id
*
- * @param int $rId
+ * @param int $value
+ */
+ public function setRelationId($value)
+ {
+ $this->relationId = $value;
+ }
+
+ /**
+ * Get nested level
+ *
+ * @return int
+ */
+ public function getNestedLevel()
+ {
+ return $this->nestedLevel;
+ }
+
+ /**
+ * Set nested level
+ *
+ * @param int $value
*/
- public function setRelationId($rId)
+ public function setNestedLevel($value)
{
- $this->relationId = $rId;
+ $this->nestedLevel = $value;
}
/**
- * Check if element is located in section doc part (as opposed to header/footer)
+ * Check if element is located in Section doc part (as opposed to Header/Footer)
*
- * @return boolean
+ * @return bool
*/
public function isInSection()
{
- return ($this->docPart == 'section');
+ return ($this->docPart == 'Section');
}
/**
@@ -190,14 +248,13 @@ public function isInSection()
*
* @param mixed $styleObject Style object
* @param mixed $styleValue Style value
- * @param boolean $returnObject Always return object
+ * @param bool $returnObject Always return object
+ * @return mixed
*/
protected function setStyle($styleObject, $styleValue = null, $returnObject = false)
{
if (!is_null($styleValue) && is_array($styleValue)) {
- foreach ($styleValue as $key => $value) {
- $styleObject->setStyleValue($key, $value);
- }
+ $styleObject->setStyleByArray($styleValue);
$style = $styleObject;
} else {
$style = $returnObject ? $styleObject : $styleValue;
diff --git a/src/PhpWord/Element/Cell.php b/src/PhpWord/Element/Cell.php
index e3d3dd0688..ea49bc7bc8 100644
--- a/src/PhpWord/Element/Cell.php
+++ b/src/PhpWord/Element/Cell.php
@@ -1,10 +1,18 @@
container = 'cell';
- $this->setDocPart($docPart, $docPartId);
$this->width = $width;
- $this->cellStyle = $this->setStyle(new CellStyle(), $style, true);
+ $this->style = $this->setStyle(new CellStyle(), $style, true);
}
/**
@@ -53,7 +62,7 @@ public function __construct($docPart, $docPartId, $width = null, $style = null)
*/
public function getStyle()
{
- return $this->cellStyle;
+ return $this->style;
}
/**
diff --git a/src/PhpWord/Element/CheckBox.php b/src/PhpWord/Element/CheckBox.php
index f273c128d4..a5620580fe 100644
--- a/src/PhpWord/Element/CheckBox.php
+++ b/src/PhpWord/Element/CheckBox.php
@@ -1,14 +1,24 @@
name = $name;
+ $this->name = String::toUTF8($name);
return $this;
}
diff --git a/src/PhpWord/Element/Endnote.php b/src/PhpWord/Element/Endnote.php
index 07d219690e..20278898f8 100644
--- a/src/PhpWord/Element/Endnote.php
+++ b/src/PhpWord/Element/Endnote.php
@@ -1,10 +1,18 @@
container = 'endnote';
$this->paragraphStyle = $this->setStyle(new Paragraph(), $paragraphStyle);
}
}
diff --git a/src/PhpWord/Element/Field.php b/src/PhpWord/Element/Field.php
new file mode 100644
index 0000000000..50f0522fb1
--- /dev/null
+++ b/src/PhpWord/Element/Field.php
@@ -0,0 +1,181 @@
+array(
+ 'properties'=>array(
+ 'format' => array('Arabic', 'ArabicDash', 'alphabetic', 'ALPHABETIC', 'roman', 'ROMAN'),
+ ),
+ 'options'=>array('PreserveFormat')
+ ),
+ 'NUMPAGES'=>array(
+ 'properties'=>array(
+ 'format' => array('Arabic', 'ArabicDash', 'alphabetic', 'ALPHABETIC', 'roman', 'ROMAN'),
+ 'numformat' => array('0', '0,00', '#.##0', '#.##0,00', '€ #.##0,00(€ #.##0,00)', '0%', '0,00%')
+ ),
+ 'options'=>array('PreserveFormat')
+ ),
+ 'DATE'=>array(
+ 'properties'=> array(
+ 'dateformat' =>array('d-M-yyyy', 'dddd d MMMM yyyy', 'd MMMM yyyy', 'd-M-yy', 'yyyy-MM-dd',
+ 'd-MMM-yy', 'd/M/yyyy', 'd MMM. yy', 'd/M/yy', 'MMM-yy', 'd-M-yyy H:mm', 'd-M-yyyy H:mm:ss',
+ 'h:mm am/pm', 'h:mm:ss am/pm', 'HH:mm', 'HH:mm:ss')
+ ),
+ 'options'=>array('PreserveFormat', 'LunarCalendar', 'SakaEraCalendar', 'LastUsedFormat')
+ )
+ );
+
+ /**
+ * Field type
+ *
+ * @var string
+ */
+ protected $type;
+
+ /**
+ * Field properties
+ *
+ * @var array
+ */
+ protected $properties = array();
+
+ /**
+ * Field options
+ *
+ * @var array
+ */
+ protected $options = array();
+
+ /**
+ * Create a new Field Element
+ *
+ * @param string $type
+ * @param array $properties
+ * @param array $options
+ */
+ public function __construct($type = null, $properties = array(), $options = array())
+ {
+ $this->setType($type);
+ $this->setProperties($properties);
+ $this->setOptions($options);
+ }
+
+ /**
+ * Set Field type
+ *
+ * @param string $type
+ * @return string
+ * @throws \InvalidArgumentException
+ */
+ public function setType($type = null)
+ {
+ if (isset($type)) {
+ if (array_key_exists($type, $this->fieldsArray)) {
+ $this->type = $type;
+ } else {
+ throw new \InvalidArgumentException("Invalid type");
+ }
+ }
+ return $this->type;
+ }
+
+ /**
+ * Get Field type
+ *
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Set Field properties
+ *
+ * @param array $properties
+ * @return self
+ * @throws \InvalidArgumentException
+ */
+ public function setProperties($properties = array())
+ {
+ if (is_array($properties)) {
+ foreach (array_keys($properties) as $propkey) {
+ if (!(array_key_exists($propkey, $this->fieldsArray[$this->type]['properties']))) {
+ throw new \InvalidArgumentException("Invalid property");
+ }
+ }
+ $this->properties = array_merge($this->properties, $properties);
+ }
+ return $this->properties;
+ }
+
+ /**
+ * Get Field properties
+ *
+ * @return array
+ */
+ public function getProperties()
+ {
+ return $this->properties;
+ }
+
+ /**
+ * Set Field options
+ *
+ * @param array $options
+ * @return self
+ * @throws \InvalidArgumentException
+ */
+ public function setOptions($options = array())
+ {
+ if (is_array($options)) {
+ foreach (array_keys($options) as $optionkey) {
+ if (!(array_key_exists($optionkey, $this->fieldsArray[$this->type]['options']))) {
+ throw new \InvalidArgumentException("Invalid option");
+ }
+ }
+ $this->options = array_merge($this->options, $options);
+ }
+ return $this->options;
+ }
+
+ /**
+ * Get Field properties
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return $this->options;
+ }
+}
diff --git a/src/PhpWord/Element/Footer.php b/src/PhpWord/Element/Footer.php
index 50ec823606..142ccfda1a 100644
--- a/src/PhpWord/Element/Footer.php
+++ b/src/PhpWord/Element/Footer.php
@@ -1,10 +1,18 @@
container = 'footer';
$this->sectionId = $sectionId;
$this->setType($type);
- $this->setDocPart($this->container, ($sectionId - 1) * 3 + $footerId);
+ $this->setDocPart($this->container, ($sectionId - 1) * 3 + $containerId);
}
/**
@@ -48,6 +66,9 @@ public function __construct($sectionId, $footerId = 1, $type = self::AUTO)
*/
public function setType($value = self::AUTO)
{
+ if (!in_array($value, array(self::AUTO, self::FIRST, self::EVEN))) {
+ $value = self::AUTO;
+ }
$this->type = $value;
}
@@ -61,4 +82,34 @@ public function getType()
{
return $this->type;
}
+
+ /**
+ * Reset type to default
+ *
+ * @return string
+ */
+ public function resetType()
+ {
+ return $this->type = self::AUTO;
+ }
+
+ /**
+ * First page only header
+ *
+ * @return string
+ */
+ public function firstPage()
+ {
+ return $this->type = self::FIRST;
+ }
+
+ /**
+ * Even numbered pages only
+ *
+ * @return string
+ */
+ public function evenPage()
+ {
+ return $this->type = self::EVEN;
+ }
}
diff --git a/src/PhpWord/Element/Footnote.php b/src/PhpWord/Element/Footnote.php
index bb8d617000..76311c6bbd 100644
--- a/src/PhpWord/Element/Footnote.php
+++ b/src/PhpWord/Element/Footnote.php
@@ -1,10 +1,18 @@
container = 'footnote';
$this->paragraphStyle = $this->setStyle(new Paragraph(), $paragraphStyle);
}
diff --git a/src/PhpWord/Element/Header.php b/src/PhpWord/Element/Header.php
index 2c963d84ea..feaa86e81a 100644
--- a/src/PhpWord/Element/Header.php
+++ b/src/PhpWord/Element/Header.php
@@ -1,52 +1,31 @@
container = 'header';
- $this->sectionId = $sectionId;
- $this->setType($type);
- $this->setDocPart($this->container, ($sectionId - 1) * 3 + $headerId);
- }
+ protected $container = 'Header';
/**
* Add a Watermark Element
@@ -59,58 +38,4 @@ public function addWatermark($src, $style = null)
{
return $this->addImage($src, $style, true);
}
-
- /**
- * Set header type
- *
- * @param string $value
- * @since 0.10.0
- */
- public function setType($value = self::AUTO)
- {
- if (!in_array($value, array(self::AUTO, self::FIRST, self::EVEN))) {
- $value = self::AUTO;
- }
- $this->type = $value;
- }
-
- /**
- * Get header type
- *
- * @return string
- */
- public function getType()
- {
- return $this->type;
- }
-
- /**
- * Reset type to default
- *
- * @return string
- */
- public function resetType()
- {
- return $this->type = self::AUTO;
- }
-
- /**
- * First page only header
- *
- * @return string
- */
- public function firstPage()
- {
- return $this->type = self::FIRST;
- }
-
- /**
- * Even numbered pages only
- *
- * @return string
- */
- public function evenPage()
- {
- return $this->type = self::EVEN;
- }
}
diff --git a/src/PhpWord/Element/Image.php b/src/PhpWord/Element/Image.php
index 53becbbfc3..a1cb8250f7 100644
--- a/src/PhpWord/Element/Image.php
+++ b/src/PhpWord/Element/Image.php
@@ -1,17 +1,25 @@
source = $source;
- $this->setIsWatermark($isWatermark);
+ $this->setIsWatermark($watermark);
$this->style = $this->setStyle(new ImageStyle(), $style, true);
$this->checkImage($source);
@@ -166,19 +174,19 @@ public function getMediaId()
*
* @return boolean
*/
- public function getIsWatermark()
+ public function isWatermark()
{
- return $this->isWatermark;
+ return $this->watermark;
}
/**
* Set is watermark
*
- * @param boolean $pValue
+ * @param boolean $value
*/
- public function setIsWatermark($pValue)
+ public function setIsWatermark($value)
{
- $this->isWatermark = $pValue;
+ $this->watermark = $value;
}
/**
@@ -226,9 +234,9 @@ public function getImageExtension()
*
* @return boolean
*/
- public function getIsMemImage()
+ public function isMemImage()
{
- return $this->isMemImage;
+ return $this->memoryImage;
}
/**
@@ -271,10 +279,83 @@ public function setMediaIndex($value)
$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
*
* @param string $source
+ * @throws \PhpOffice\PhpWord\Exception\InvalidImageException
+ * @throws \PhpOffice\PhpWord\Exception\UnsupportedImageTypeException
*/
private function checkImage($source)
{
@@ -314,14 +395,14 @@ private function checkImage($source)
private function setSourceType($source)
{
if (stripos(strrev($source), strrev('.php')) === 0) {
- $this->isMemImage = true;
+ $this->memoryImage = true;
$this->sourceType = self::SOURCE_GD;
} elseif (strpos($source, 'zip://') !== false) {
- $this->isMemImage = false;
+ $this->memoryImage = false;
$this->sourceType = self::SOURCE_ARCHIVE;
} else {
- $this->isMemImage = (filter_var($source, FILTER_VALIDATE_URL) !== false);
- $this->sourceType = $this->isMemImage ? self::SOURCE_GD : self::SOURCE_LOCAL;
+ $this->memoryImage = (filter_var($source, FILTER_VALIDATE_URL) !== false);
+ $this->sourceType = $this->memoryImage ? self::SOURCE_GD : self::SOURCE_LOCAL;
}
}
@@ -338,8 +419,7 @@ private function getArchiveImageSize($source)
list($zipFilename, $imageFilename) = explode('#', $source);
$tempFilename = tempnam(sys_get_temp_dir(), 'PHPWordImage');
- $zipClass = Settings::getZipClass();
- $zip = new $zipClass();
+ $zip = new ZipArchive();
if ($zip->open($zipFilename) !== false) {
if ($zip->locateName($imageFilename)) {
$imageContent = $zip->getFromName($imageFilename);
@@ -409,4 +489,26 @@ private function setProportionalSize($actualWidth, $actualHeight)
}
}
}
+
+ /**
+ * Get is watermark
+ *
+ * @deprecated 0.10.0
+ * @codeCoverageIgnore
+ */
+ public function getIsWatermark()
+ {
+ return $this->isWatermark();
+ }
+
+ /**
+ * Get is memory image
+ *
+ * @deprecated 0.10.0
+ * @codeCoverageIgnore
+ */
+ public function getIsMemImage()
+ {
+ return $this->isMemImage();
+ }
}
diff --git a/src/PhpWord/Element/Line.php b/src/PhpWord/Element/Line.php
new file mode 100644
index 0000000000..4acca8eda3
--- /dev/null
+++ b/src/PhpWord/Element/Line.php
@@ -0,0 +1,53 @@
+style = $this->setStyle(new LineStyle(), $style);
+ }
+
+ /**
+ * Get line style
+ *
+ * @return \PhpOffice\PhpWord\Style\Line
+ */
+ public function getStyle()
+ {
+ return $this->style;
+ }
+}
diff --git a/src/PhpWord/Element/Link.php b/src/PhpWord/Element/Link.php
index 31b06c68aa..3ffe58d4a4 100644
--- a/src/PhpWord/Element/Link.php
+++ b/src/PhpWord/Element/Link.php
@@ -1,14 +1,23 @@
target = $target;
- $this->text = is_null($text) ? $target : $text;
+ $this->target = String::toUTF8($target);
+ $this->text = is_null($text) ? $this->target : String::toUTF8($text);
$this->fontStyle = $this->setStyle(new Font('text'), $fontStyle);
$this->paragraphStyle = $this->setStyle(new Paragraph(), $paragraphStyle);
diff --git a/src/PhpWord/Element/ListItem.php b/src/PhpWord/Element/ListItem.php
index 660615375e..59bd83917c 100644
--- a/src/PhpWord/Element/ListItem.php
+++ b/src/PhpWord/Element/ListItem.php
@@ -1,14 +1,23 @@
textObject = new Text($text, $fontStyle, $paragraphStyle);
+ $this->textObject = new Text(String::toUTF8($text), $fontStyle, $paragraphStyle);
$this->depth = $depth;
// Version >= 0.10.0 will pass numbering style name. Older version will use old method
@@ -61,7 +69,9 @@ public function __construct($text, $depth = 0, $fontStyle = null, $listStyle = n
}
/**
- * Get ListItem style
+ * Get style
+ *
+ * @return \PhpOffice\PhpWord\Style\ListItem
*/
public function getStyle()
{
@@ -69,7 +79,9 @@ public function getStyle()
}
/**
- * Get ListItem TextRun
+ * Get Text object
+ *
+ * @return \PhpOffice\PhpWord\Element\Text
*/
public function getTextObject()
{
@@ -77,10 +89,23 @@ public function getTextObject()
}
/**
- * Get ListItem depth
+ * Get depth
+ *
+ * @return int
*/
public function getDepth()
{
return $this->depth;
}
+
+ /**
+ * Get text
+ *
+ * @return string
+ * @since 0.11.0
+ */
+ public function getText()
+ {
+ return $this->textObject->getText();
+ }
}
diff --git a/src/PhpWord/Element/ListItemRun.php b/src/PhpWord/Element/ListItemRun.php
new file mode 100644
index 0000000000..fb219f91fb
--- /dev/null
+++ b/src/PhpWord/Element/ListItemRun.php
@@ -0,0 +1,82 @@
+depth = $depth;
+
+ // Version >= 0.10.0 will pass numbering style name. Older version will use old method
+ if (!is_null($listStyle) && is_string($listStyle)) {
+ $this->style = new ListItemStyle($listStyle);
+ } else {
+ $this->style = $this->setStyle(new ListItemStyle(), $listStyle, true);
+ }
+ $this->paragraphStyle = $this->setStyle(new Paragraph(), $paragraphStyle);
+ }
+
+ /**
+ * Get ListItem style
+ */
+ public function getStyle()
+ {
+ return $this->style;
+ }
+
+ /**
+ * Get ListItem depth
+ */
+ public function getDepth()
+ {
+ return $this->depth;
+ }
+}
diff --git a/src/PhpWord/Element/Object.php b/src/PhpWord/Element/Object.php
index 7407688a81..a63c186936 100644
--- a/src/PhpWord/Element/Object.php
+++ b/src/PhpWord/Element/Object.php
@@ -1,14 +1,23 @@
source = $src;
+ $this->source = $source;
$this->style = $this->setStyle(new ImageStyle(), $style, true);
+ $this->icon = realpath(__DIR__ . "/../resources/{$ext}.png");
+
return $this;
} else {
- return false;
+ throw new InvalidObjectException();
}
}
/**
- * Get Image style
+ * Get object source
+ *
+ * @return string
+ */
+ public function getSource()
+ {
+ return $this->source;
+ }
+
+ /**
+ * Get object style
*
* @return \PhpOffice\PhpWord\Style\Image
*/
@@ -68,17 +102,17 @@ public function getStyle()
}
/**
- * Get Source
+ * Get object icon
*
* @return string
*/
- public function getSource()
+ public function getIcon()
{
- return $this->source;
+ return $this->icon;
}
/**
- * Get Image Relation ID
+ * Get image relation ID
*
* @return int
*/
diff --git a/src/PhpWord/Element/PageBreak.php b/src/PhpWord/Element/PageBreak.php
index d2f85f2083..a1e7e998e8 100644
--- a/src/PhpWord/Element/PageBreak.php
+++ b/src/PhpWord/Element/PageBreak.php
@@ -1,10 +1,18 @@
fontStyle = $this->setStyle(new Font('text'), $fontStyle);
$this->paragraphStyle = $this->setStyle(new Paragraph(), $paragraphStyle);
- $matches = preg_split('/({.*?})/', $text, null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+ $this->text = String::toUTF8($text);
+ $matches = preg_split('/({.*?})/', $this->text, null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
if (isset($matches[0])) {
$this->text = $matches;
}
diff --git a/src/PhpWord/Element/Row.php b/src/PhpWord/Element/Row.php
index dc4b8a4430..d7dcea3443 100644
--- a/src/PhpWord/Element/Row.php
+++ b/src/PhpWord/Element/Row.php
@@ -1,10 +1,18 @@
setDocPart($docPart, $docPartId);
$this->height = $height;
$this->style = $this->setStyle(new RowStyle(), $style, true);
}
@@ -59,18 +64,23 @@ public function __construct($docPart, $docPartId, $height = null, $style = null)
*
* @param int $width
* @param mixed $style
+ * @return \PhpOffice\PhpWord\Element\Cell
*/
public function addCell($width = null, $style = null)
{
- $cell = new Cell($this->getDocPart(), $this->getDocPartId(), $width, $style);
+ $cell = new Cell($width, $style);
+ $cell->setDocPart($this->getDocPart(), $this->getDocPartId());
+ $cell->setPhpWord($this->phpWord);
+ $cell->setNestedLevel($this->getNestedLevel());
$this->cells[] = $cell;
+
return $cell;
}
/**
* Get all cells
*
- * @return array
+ * @return \PhpOffice\PhpWord\Element\Cell[]
*/
public function getCells()
{
diff --git a/src/PhpWord/Element/Section.php b/src/PhpWord/Element/Section.php
index 64e226ab58..b7cee6a2df 100644
--- a/src/PhpWord/Element/Section.php
+++ b/src/PhpWord/Element/Section.php
@@ -1,24 +1,35 @@
container = 'section';
$this->sectionId = $sectionCount;
$this->setDocPart($this->container, $this->sectionId);
$this->settings = new SectionSettings();
@@ -82,12 +92,24 @@ public function getSettings()
return $this->settings;
}
+ /**
+ * Add a Title Element
+ *
+ * @param string $text
+ * @param int $depth
+ * @return \PhpOffice\PhpWord\Element\Title
+ */
+ public function addTitle($text, $depth = 1)
+ {
+ return $this->addElement('Title', $text, $depth);
+ }
+
/**
* Add a PageBreak Element
*/
public function addPageBreak()
{
- $this->elements[] = new PageBreak();
+ return $this->addElement('PageBreak');
}
/**
@@ -101,9 +123,7 @@ public function addPageBreak()
*/
public function addTOC($fontStyle = null, $tocStyle = null, $minDepth = 1, $maxDepth = 9)
{
- $toc = new TOC($fontStyle, $tocStyle, $minDepth, $maxDepth);
- $this->elements[] = $toc;
- return $toc;
+ return $this->addElement('TOC', $fontStyle, $tocStyle, $minDepth, $maxDepth);
}
/**
@@ -179,14 +199,17 @@ public function hasDifferentFirstPage()
*/
private function addHeaderFooter($type = Header::AUTO, $header = true)
{
+ $containerClass = substr(get_class($this), 0, strrpos(get_class($this), '\\')) . '\\' .
+ ($header ? 'Header' : 'Footer');
$collectionArray = $header ? 'headers' : 'footers';
- $containerClass = 'PhpOffice\\PhpWord\\Element\\';
- $containerClass .= ($header ? 'Header' : 'Footer');
$collection = &$this->$collectionArray;
if (in_array($type, array(Header::AUTO, Header::FIRST, Header::EVEN))) {
$index = count($collection);
+ /** @var \PhpOffice\PhpWord\Element\AbstractContainer $container Type hint */
$container = new $containerClass($this->sectionId, ++$index, $type);
+ $container->setPhpWord($this->phpWord);
+
$collection[$index] = $container;
return $container;
} else {
diff --git a/src/PhpWord/Element/TOC.php b/src/PhpWord/Element/TOC.php
index ac48a49d40..9ac745545c 100644
--- a/src/PhpWord/Element/TOC.php
+++ b/src/PhpWord/Element/TOC.php
@@ -1,15 +1,23 @@
TOCStyle = new TOCStyle();
if (!is_null($tocStyle) && is_array($tocStyle)) {
- foreach ($tocStyle as $key => $value) {
- $this->TOCStyle->setStyleValue($key, $value);
- }
+ $this->TOCStyle->setStyleByArray($tocStyle);
}
- if (!is_null($fontStyle)) {
- if (is_array($fontStyle)) {
- $this->fontStyle = new Font();
- foreach ($fontStyle as $key => $value) {
- $this->fontStyle->setStyleValue($key, $value);
- }
- } else {
- $this->fontStyle = $fontStyle;
- }
+ if (!is_null($fontStyle) && is_array($fontStyle)) {
+ $this->fontStyle = new Font();
+ $this->fontStyle->setStyleByArray($fontStyle);
+ } else {
+ $this->fontStyle = $fontStyle;
}
$this->minDepth = $minDepth;
@@ -87,16 +89,21 @@ public function __construct($fontStyle = null, $tocStyle = null, $minDepth = 1,
*/
public function getTitles()
{
- $titles = Titles::getTitles();
+ if (!$this->phpWord instanceof PhpWord) {
+ return array();
+ }
+
+ $titles = $this->phpWord->getTitles()->getItems();
foreach ($titles as $i => $title) {
- if ($this->minDepth > $title['depth']) {
+ /** @var \PhpOffice\PhpWord\Element\Title $title Type hint */
+ $depth = $title->getDepth();
+ if ($this->minDepth > $depth) {
unset($titles[$i]);
}
- if (($this->maxDepth != 0) && ($this->maxDepth < $title['depth'])) {
+ if (($this->maxDepth != 0) && ($this->maxDepth < $depth)) {
unset($titles[$i]);
}
}
- $titles = array_merge(array(), $titles);
return $titles;
}
diff --git a/src/PhpWord/Element/Table.php b/src/PhpWord/Element/Table.php
index 1fa729e172..ace634607d 100644
--- a/src/PhpWord/Element/Table.php
+++ b/src/PhpWord/Element/Table.php
@@ -1,10 +1,18 @@
setDocPart($docPart, $docPartId);
$this->style = $this->setStyle(new TableStyle(), $style);
}
/**
* Add a row
*
- * @param integer $height
+ * @param int $height
* @param mixed $style
+ * @return \PhpOffice\PhpWord\Element\Row
*/
public function addRow($height = null, $style = null)
{
- $row = new Row($this->getDocPart(), $this->getDocPartId(), $height, $style);
+ $row = new Row($height, $style);
+ $row->setDocPart($this->getDocPart(), $this->getDocPartId());
+ $row->setPhpWord($this->phpWord);
+ $row->setNestedLevel($this->getNestedLevel());
$this->rows[] = $row;
+
return $row;
}
/**
* Add a cell
*
- * @param integer $width
+ * @param int $width
* @param mixed $style
- * @return Cell
+ * @return \PhpOffice\PhpWord\Element\Cell
*/
public function addCell($width = null, $style = null)
{
$index = count($this->rows) - 1;
- $cell = $this->rows[$index]->addCell($width, $style);
+ $row = $this->rows[$index];
+ $cell = $row->addCell($width, $style);
+
return $cell;
}
/**
* Get all rows
*
- * @return array
+ * @return \PhpOffice\PhpWord\Element\Row[]
*/
public function getRows()
{
@@ -99,29 +110,29 @@ public function getStyle()
}
/**
- * Set table width
+ * Get table width
*
- * @param integer $width
+ * @return int
*/
- public function setWidth($width)
+ public function getWidth()
{
- $this->width = $width;
+ return $this->width;
}
/**
- * Get table width
+ * Set table width
*
- * @return integer
+ * @param int $width
*/
- public function getWidth()
+ public function setWidth($width)
{
- return $this->width;
+ $this->width = $width;
}
/**
* Get column count
*
- * @return integer
+ * @return int
*/
public function countColumns()
{
@@ -129,7 +140,9 @@ public function countColumns()
if (is_array($this->rows)) {
$rowCount = count($this->rows);
for ($i = 0; $i < $rowCount; $i++) {
- $cellCount = count($this->rows[$i]->getCells());
+ /** @var \PhpOffice\PhpWord\Element\Row $row Type hint */
+ $row = $this->rows[$i];
+ $cellCount = count($row->getCells());
if ($columnCount < $cellCount) {
$columnCount = $cellCount;
}
diff --git a/src/PhpWord/Element/Text.php b/src/PhpWord/Element/Text.php
index 96e0e164e9..52fccb3f28 100644
--- a/src/PhpWord/Element/Text.php
+++ b/src/PhpWord/Element/Text.php
@@ -1,14 +1,23 @@
setParagraphStyle($paragraphStyle);
} elseif (is_array($style)) {
$this->fontStyle = new Font('text', $paragraphStyle);
- $this->fontStyle->setArrayStyle($style);
+ $this->fontStyle->setStyleByArray($style);
} elseif (null === $style) {
$this->fontStyle = new Font('text', $paragraphStyle);
} else {
@@ -97,7 +106,7 @@ public function setParagraphStyle($style = null)
{
if (is_array($style)) {
$this->paragraphStyle = new Paragraph;
- $this->paragraphStyle->setArrayStyle($style);
+ $this->paragraphStyle->setStyleByArray($style);
} elseif ($style instanceof Paragraph) {
$this->paragraphStyle = $style;
} elseif (null === $style) {
@@ -127,7 +136,7 @@ public function getParagraphStyle()
*/
public function setText($text)
{
- $this->text = $text;
+ $this->text = String::toUTF8($text);
return $this;
}
diff --git a/src/PhpWord/Element/TextBox.php b/src/PhpWord/Element/TextBox.php
new file mode 100644
index 0000000000..06c9518167
--- /dev/null
+++ b/src/PhpWord/Element/TextBox.php
@@ -0,0 +1,60 @@
+style = $this->setStyle(new TextBoxStyle(), $style);
+ }
+
+ /**
+ * Get textbox style
+ *
+ * @return \PhpOffice\PhpWord\Style\TextBox
+ */
+ public function getStyle()
+ {
+ return $this->style;
+ }
+}
diff --git a/src/PhpWord/Element/TextBreak.php b/src/PhpWord/Element/TextBreak.php
index b80ff6812b..aa6ab582b2 100644
--- a/src/PhpWord/Element/TextBreak.php
+++ b/src/PhpWord/Element/TextBreak.php
@@ -1,10 +1,18 @@
setParagraphStyle($paragraphStyle);
} elseif (is_array($style)) {
$this->fontStyle = new Font('text', $paragraphStyle);
- $this->fontStyle->setArrayStyle($style);
+ $this->fontStyle->setStyleByArray($style);
} else {
$this->fontStyle = $style;
$this->setParagraphStyle($paragraphStyle);
@@ -89,7 +97,7 @@ public function setParagraphStyle($style = null)
{
if (is_array($style)) {
$this->paragraphStyle = new Paragraph;
- $this->paragraphStyle->setArrayStyle($style);
+ $this->paragraphStyle->setStyleByArray($style);
} elseif ($style instanceof Paragraph) {
$this->paragraphStyle = $style;
} else {
@@ -107,4 +115,14 @@ public function getParagraphStyle()
{
return $this->paragraphStyle;
}
+
+ /**
+ * Has font/paragraph style defined
+ *
+ * @return bool
+ */
+ public function hasStyle()
+ {
+ return !is_null($this->fontStyle) || !is_null($this->paragraphStyle);
+ }
}
diff --git a/src/PhpWord/Element/TextRun.php b/src/PhpWord/Element/TextRun.php
index a8428d1e0a..7583710404 100644
--- a/src/PhpWord/Element/TextRun.php
+++ b/src/PhpWord/Element/TextRun.php
@@ -1,10 +1,18 @@
container = 'textrun';
$this->paragraphStyle = $this->setStyle(new Paragraph(), $paragraphStyle);
}
diff --git a/src/PhpWord/Element/Title.php b/src/PhpWord/Element/Title.php
index 93b48d626c..d5206879dc 100644
--- a/src/PhpWord/Element/Title.php
+++ b/src/PhpWord/Element/Title.php
@@ -1,14 +1,25 @@
style = $style;
- }
- $this->text = $text;
+ $this->text = String::toUTF8($text);
$this->depth = $depth;
+ if (array_key_exists('Heading_' . $this->depth, Style::getStyles())) {
+ $this->style = 'Heading' . $this->depth;
+ }
return $this;
}
- /**
- * Set Anchor
- *
- * @param int $anchor
- */
- public function setAnchor($anchor)
- {
- $this->anchor = $anchor;
- }
-
- /**
- * Get Anchor
- *
- * @return int
- */
- public function getAnchor()
- {
- return $this->anchor;
- }
-
- /**
- * Set Bookmark ID
- *
- * @param int $bookmarkId
- */
- public function setBookmarkId($bookmarkId)
- {
- $this->bookmarkId = $bookmarkId;
- }
-
- /**
- * Get Anchor
- *
- * @return int
- */
- public function getBookmarkId()
- {
- return $this->bookmarkId;
- }
-
/**
* Get Title Text content
*
diff --git a/src/PhpWord/Endnotes.php b/src/PhpWord/Endnotes.php
deleted file mode 100644
index 6587dfe946..0000000000
--- a/src/PhpWord/Endnotes.php
+++ /dev/null
@@ -1,95 +0,0 @@
-load($filename);
}
+
+ /**
+ * Check if it's a concrete class (not abstract nor interface)
+ *
+ * @param string $class
+ * @return bool
+ */
+ private static function isConcreteClass($class)
+ {
+ $reflection = new \ReflectionClass($class);
+
+ return !$reflection->isAbstract() && !$reflection->isInterface();
+ }
}
diff --git a/src/PhpWord/Media.php b/src/PhpWord/Media.php
index 7d129b866b..7f35841c43 100644
--- a/src/PhpWord/Media.php
+++ b/src/PhpWord/Media.php
@@ -1,10 +1,18 @@
getIsMemImage();
+ $isMemImage = $image->isMemImage();
$extension = $image->getImageExtension();
$mediaData['imageExtension'] = $extension;
$mediaData['imageType'] = $image->getImageType();
@@ -131,37 +139,53 @@ public static function countElements($container, $mediaType = null)
* 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/PhpWord.php b/src/PhpWord/PhpWord.php
index d4b1335676..c54faa3615 100644
--- a/src/PhpWord/PhpWord.php
+++ b/src/PhpWord/PhpWord.php
@@ -1,36 +1,43 @@
documentProperties = new DocumentProperties();
- $this->defaultFontName = self::DEFAULT_FONT_NAME;
- $this->defaultFontSize = self::DEFAULT_FONT_SIZE;
+ $this->titles = new Titles();
+ $this->footnotes = new Footnotes();
+ $this->endnotes = new Endnotes();
}
/**
@@ -92,6 +108,16 @@ public function setDocumentProperties(DocumentProperties $documentProperties)
return $this;
}
+ /**
+ * Get all sections
+ *
+ * @return \PhpOffice\PhpWord\Element\Section[]
+ */
+ public function getSections()
+ {
+ return $this->sections;
+ }
+
/**
* Create new section
*
@@ -101,11 +127,75 @@ public function setDocumentProperties(DocumentProperties $documentProperties)
public function addSection($settings = null)
{
$section = new Section(count($this->sections) + 1, $settings);
+ $section->setPhpWord($this);
$this->sections[] = $section;
return $section;
}
+ /**
+ * Get titles
+ *
+ * @return \PhpOffice\PhpWord\Collection\Titles
+ */
+ public function getTitles()
+ {
+ return $this->titles;
+ }
+
+ /**
+ * Add new title
+ *
+ * @param \PhpOffice\PhpWord\Element\Title $title
+ * @return int
+ */
+ public function addTitle($title)
+ {
+ return $this->titles->addItem($title);
+ }
+
+ /**
+ * Get footnotes
+ *
+ * @return \PhpOffice\PhpWord\Collection\Footnotes
+ */
+ public function getFootnotes()
+ {
+ return $this->footnotes;
+ }
+
+ /**
+ * Add new footnote
+ *
+ * @param \PhpOffice\PhpWord\Element\Footnote $footnote
+ * @return int
+ */
+ public function addFootnote($footnote)
+ {
+ return $this->footnotes->addItem($footnote);
+ }
+
+ /**
+ * Get endnotes
+ *
+ * @return \PhpOffice\PhpWord\Collection\Endnotes
+ */
+ public function getEndnotes()
+ {
+ return $this->endnotes;
+ }
+
+ /**
+ * Add new endnote
+ *
+ * @param \PhpOffice\PhpWord\Element\Endnote $endnote
+ * @return int
+ */
+ public function addEndnote($endnote)
+ {
+ return $this->endnotes->addItem($endnote);
+ }
+
/**
* Get default font name
*
@@ -113,7 +203,7 @@ public function addSection($settings = null)
*/
public function getDefaultFontName()
{
- return $this->defaultFontName;
+ return Settings::getDefaultFontName();
}
/**
@@ -123,7 +213,7 @@ public function getDefaultFontName()
*/
public function setDefaultFontName($fontName)
{
- $this->defaultFontName = $fontName;
+ Settings::setDefaultFontName($fontName);
}
/**
@@ -133,7 +223,7 @@ public function setDefaultFontName($fontName)
*/
public function getDefaultFontSize()
{
- return $this->defaultFontSize;
+ return Settings::getDefaultFontSize();
}
/**
@@ -143,17 +233,18 @@ public function getDefaultFontSize()
*/
public function setDefaultFontSize($fontSize)
{
- $this->defaultFontSize = $fontSize;
+ Settings::setDefaultFontSize($fontSize);
}
/**
* Set default paragraph style definition to styles.xml
*
* @param array $styles Paragraph style definition
+ * @return \PhpOffice\PhpWord\Style\Paragraph
*/
public function setDefaultParagraphStyle($styles)
{
- Style::setDefaultParagraphStyle($styles);
+ return Style::setDefaultParagraphStyle($styles);
}
/**
@@ -161,10 +252,11 @@ public function setDefaultParagraphStyle($styles)
*
* @param string $styleName
* @param array $styles
+ * @return \PhpOffice\PhpWord\Style\Paragraph
*/
public function addParagraphStyle($styleName, $styles)
{
- Style::addParagraphStyle($styleName, $styles);
+ return Style::addParagraphStyle($styleName, $styles);
}
/**
@@ -173,10 +265,11 @@ public function addParagraphStyle($styleName, $styles)
* @param string $styleName
* @param mixed $fontStyle
* @param mixed $paragraphStyle
+ * @return \PhpOffice\PhpWord\Style\Font
*/
public function addFontStyle($styleName, $fontStyle, $paragraphStyle = null)
{
- Style::addFontStyle($styleName, $fontStyle, $paragraphStyle);
+ return Style::addFontStyle($styleName, $fontStyle, $paragraphStyle);
}
/**
@@ -185,54 +278,48 @@ public function addFontStyle($styleName, $fontStyle, $paragraphStyle = null)
* @param string $styleName
* @param mixed $styleTable
* @param mixed $styleFirstRow
+ * @return \PhpOffice\PhpWord\Style\Table
*/
public function addTableStyle($styleName, $styleTable, $styleFirstRow = null)
{
- Style::addTableStyle($styleName, $styleTable, $styleFirstRow);
+ return Style::addTableStyle($styleName, $styleTable, $styleFirstRow);
}
/**
- * Adds a heading style definition to styles.xml
- *
- * @param int $titleCount
- * @param mixed $fontStyle
- * @param mixed $paragraphStyle
- */
- public function addTitleStyle($titleCount, $fontStyle, $paragraphStyle = null)
- {
- Style::addTitleStyle($titleCount, $fontStyle, $paragraphStyle);
- }
-
- /**
- * Adds a hyperlink style to styles.xml
+ * Adds a numbering style
*
* @param string $styleName
* @param mixed $styles
+ * @return \PhpOffice\PhpWord\Style\Numbering
*/
- public function addLinkStyle($styleName, $styles)
+ public function addNumberingStyle($styleName, $styles)
{
- Style::addLinkStyle($styleName, $styles);
+ return Style::addNumberingStyle($styleName, $styles);
}
/**
- * Adds a numbering style
+ * Adds a hyperlink style to styles.xml
*
* @param string $styleName
* @param mixed $styles
+ * @return \PhpOffice\PhpWord\Style\Font
*/
- public function addNumberingStyle($styleName, $styles)
+ public function addLinkStyle($styleName, $styles)
{
- Style::addNumberingStyle($styleName, $styles);
+ return Style::addLinkStyle($styleName, $styles);
}
/**
- * Get all sections
+ * Adds a heading style definition to styles.xml
*
- * @return \PhpOffice\PhpWord\Element\Section[]
+ * @param int $depth
+ * @param mixed $fontStyle
+ * @param mixed $paragraphStyle
+ * @return \PhpOffice\PhpWord\Style\Font
*/
- public function getSections()
+ public function addTitleStyle($depth, $fontStyle, $paragraphStyle = null)
{
- return $this->sections;
+ return Style::addTitleStyle($depth, $fontStyle, $paragraphStyle);
}
/**
diff --git a/src/PhpWord/Reader/AbstractReader.php b/src/PhpWord/Reader/AbstractReader.php
index 424ff2cd9b..c08914e5c6 100644
--- a/src/PhpWord/Reader/AbstractReader.php
+++ b/src/PhpWord/Reader/AbstractReader.php
@@ -1,10 +1,18 @@
readDataOnly;
return true;
@@ -46,47 +55,47 @@ public function getReadDataOnly()
/**
* Set read data only
*
- * @param bool $pValue
+ * @param bool $value
* @return self
*/
- public function setReadDataOnly($pValue = true)
+ public function setReadDataOnly($value = true)
{
- $this->readDataOnly = $pValue;
+ $this->readDataOnly = $value;
return $this;
}
/**
* Open file for reading
*
- * @param string $pFilename
+ * @param string $filename
* @return resource
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
- protected function openFile($pFilename)
+ protected function openFile($filename)
{
// Check if file exists
- if (!file_exists($pFilename) || !is_readable($pFilename)) {
- throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ if (!file_exists($filename) || !is_readable($filename)) {
+ throw new Exception("Could not open " . $filename . " for reading! File does not exist.");
}
// Open file
- $this->fileHandle = fopen($pFilename, 'r');
+ $this->fileHandle = fopen($filename, 'r');
if ($this->fileHandle === false) {
- throw new Exception("Could not open file " . $pFilename . " for reading.");
+ throw new Exception("Could not open file " . $filename . " for reading.");
}
}
/**
* Can the current ReaderInterface read the file?
*
- * @param string $pFilename
+ * @param string $filename
* @return bool
*/
- public function canRead($pFilename)
+ public function canRead($filename)
{
// Check if file exists
try {
- $this->openFile($pFilename);
+ $this->openFile($filename);
} catch (Exception $e) {
return false;
}
@@ -96,4 +105,15 @@ public function canRead($pFilename)
return true;
}
+
+ /**
+ * Read data only?
+ *
+ * @deprecated 0.10.0
+ * @codeCoverageIgnore
+ */
+ public function getReadDataOnly()
+ {
+ return $this->isReadDataOnly();
+ }
}
diff --git a/src/PhpWord/Reader/HTML.php b/src/PhpWord/Reader/HTML.php
new file mode 100644
index 0000000000..a6582a3f26
--- /dev/null
+++ b/src/PhpWord/Reader/HTML.php
@@ -0,0 +1,50 @@
+canRead($docFile)) {
+ $section = $phpWord->addSection();
+ HTMLParser::addHtml($section, file_get_contents($docFile), true);
+ } else {
+ throw new \Exception("Cannot read {$docFile}.");
+ }
+
+ return $phpWord;
+ }
+}
diff --git a/src/PhpWord/Reader/ODText.php b/src/PhpWord/Reader/ODText.php
index db4708b47b..19efdc5170 100644
--- a/src/PhpWord/Reader/ODText.php
+++ b/src/PhpWord/Reader/ODText.php
@@ -1,10 +1,18 @@
'Content',
+ 'meta.xml' => 'Meta',
);
foreach ($readerParts as $xmlFile => $partName) {
@@ -54,6 +63,7 @@ private function readPart(PhpWord &$phpWord, $relationships, $partName, $docFile
{
$partClass = "PhpOffice\\PhpWord\\Reader\\ODText\\{$partName}";
if (class_exists($partClass)) {
+ /** @var \PhpOffice\PhpWord\Reader\ODText\AbstractPart $part Type hint */
$part = new $partClass($docFile, $xmlFile);
$part->setRels($relationships);
$part->read($phpWord);
diff --git a/src/PhpWord/Reader/ODText/AbstractPart.php b/src/PhpWord/Reader/ODText/AbstractPart.php
index 15c6719080..95f700847e 100644
--- a/src/PhpWord/Reader/ODText/AbstractPart.php
+++ b/src/PhpWord/Reader/ODText/AbstractPart.php
@@ -1,50 +1,30 @@
getDomFromZip($this->docFile, $this->xmlFile);
+ $docProps = $phpWord->getDocumentProperties();
+
+ $metaNode = $xmlReader->getElement('office:meta');
+
+ // Standard properties
+ $properties = array(
+ 'title' => 'dc:title',
+ 'subject' => 'dc:subject',
+ 'description' => 'dc:description',
+ 'keywords' => 'meta:keyword',
+ 'creator' => 'meta:initial-creator',
+ 'lastModifiedBy' => 'dc:creator',
+ // 'created' => 'meta:creation-date',
+ // 'modified' => 'dc:date',
+ );
+ foreach ($properties as $property => $path) {
+ $method = "set{$property}";
+ $propertyNode = $xmlReader->getElement($path, $metaNode);
+ if ($propertyNode !== null && method_exists($docProps, $method)) {
+ $docProps->$method($propertyNode->nodeValue);
+ }
+ }
+
+ // Custom properties
+ $propertyNodes = $xmlReader->getElements('meta:user-defined', $metaNode);
+ foreach ($propertyNodes as $propertyNode) {
+ $property = $xmlReader->getAttribute('meta:name', $propertyNode);
+
+ // Set category, company, and manager property
+ if (in_array($property, array('Category', 'Company', 'Manager'))) {
+ $method = "set{$property}";
+ $docProps->$method($propertyNode->nodeValue);
+
+ // Set other custom properties
+ } else {
+ $docProps->setCustomProperty($property, $propertyNode->nodeValue);
+ }
+ }
+ }
+}
diff --git a/src/PhpWord/Reader/RTF.php b/src/PhpWord/Reader/RTF.php
new file mode 100644
index 0000000000..9d5d813bf4
--- /dev/null
+++ b/src/PhpWord/Reader/RTF.php
@@ -0,0 +1,51 @@
+canRead($docFile)) {
+ $doc = new Document();
+ $doc->rtf = file_get_contents($docFile);
+ $doc->read($phpWord);
+ } else {
+ throw new \Exception("Cannot read {$docFile}.");
+ }
+
+ return $phpWord;
+ }
+}
diff --git a/src/PhpWord/Reader/RTF/Document.php b/src/PhpWord/Reader/RTF/Document.php
new file mode 100644
index 0000000000..cb082fdc63
--- /dev/null
+++ b/src/PhpWord/Reader/RTF/Document.php
@@ -0,0 +1,393 @@
+ 'markOpening', // {
+ 125 => 'markClosing', // }
+ 92 => 'markBackslash', // \
+ 10 => 'markNewline', // LF
+ 13 => 'markNewline' // CR
+ );
+
+ $this->phpWord = $phpWord;
+ $this->section = $phpWord->addSection();
+ $this->textrun = $this->section->addTextRun();
+ $this->length = strlen($this->rtf);
+
+ $this->flags['paragraph'] = true; // Set paragraph flag from the beginning
+
+ // Walk each characters
+ while ($this->offset < $this->length) {
+ $char = $this->rtf[$this->offset];
+ $ascii = ord($char);
+
+ if (array_key_exists($ascii, $markers)) { // Marker found: {, }, \, LF, or CR
+ $markerFunction = $markers[$ascii];
+ $this->$markerFunction();
+ } else {
+ if ($this->isControl === false) { // Non control word: Push character
+ $this->pushText($char);
+ } else {
+ if (preg_match("/^[a-zA-Z0-9-]?$/", $char)) { // No delimiter: Buffer control
+ $this->control .= $char;
+ $this->isFirst = false;
+ } else { // Delimiter found: Parse buffered control
+ if ($this->isFirst) {
+ $this->isFirst = false;
+ } else {
+ if ($char == ' ') { // Discard space as a control word delimiter
+ $this->flushControl(true);
+ }
+ }
+ }
+ }
+ }
+ $this->offset++;
+ }
+ $this->flushText();
+ }
+
+ /**
+ * Mark opening braket `{` character
+ */
+ private function markOpening()
+ {
+ $this->flush(true);
+ array_push($this->groups, $this->flags);
+ }
+
+ /**
+ * Mark closing braket `}` character
+ */
+ private function markClosing()
+ {
+ $this->flush(true);
+ $this->flags = array_pop($this->groups);
+ }
+
+ /**
+ * Mark backslash `\` character
+ */
+ private function markBackslash()
+ {
+ if ($this->isFirst) {
+ $this->setControl(false);
+ $this->text .= '\\';
+ } else {
+ $this->flush();
+ $this->setControl(true);
+ $this->control = '';
+ }
+ }
+
+ /**
+ * Mark newline character: Flush control word because it's not possible to span multiline
+ */
+ private function markNewline()
+ {
+ if ($this->isControl) {
+ $this->flushControl(true);
+ }
+ }
+
+ /**
+ * Flush control word or text
+ *
+ * @param bool $isControl
+ */
+ private function flush($isControl = false)
+ {
+ if ($this->isControl) {
+ $this->flushControl($isControl);
+ } else {
+ $this->flushText();
+ }
+ }
+
+ /**
+ * Flush control word
+ *
+ * @param bool $isControl
+ */
+ private function flushControl($isControl = false)
+ {
+ if (preg_match("/^([A-Za-z]+)(-?[0-9]*) ?$/", $this->control, $match) === 1) {
+ list(, $control, $parameter) = $match;
+ $this->parseControl($control, $parameter);
+ }
+
+ if ($isControl === true) {
+ $this->setControl(false);
+ }
+ }
+
+ /**
+ * Flush text in queue
+ */
+ private function flushText()
+ {
+ if ($this->text != '') {
+ if (isset($this->flags['property'])) { // Set property
+ $this->flags['value'] = $this->text;
+ } else { // Set text
+ if ($this->flags['paragraph'] === true) {
+ $this->flags['paragraph'] = false;
+ $this->flags['text'] = $this->text;
+ }
+ }
+
+ // Add text if it's not flagged as skipped
+ if (!isset($this->flags['skipped'])) {
+ $this->readText();
+ }
+
+ $this->text = '';
+ }
+ }
+
+ /**
+ * Reset control word and first char state
+ *
+ * @param bool $value
+ */
+ private function setControl($value)
+ {
+ $this->isControl = $value;
+ $this->isFirst = $value;
+ }
+
+ /**
+ * Push text into queue
+ *
+ * @param string $char
+ */
+ private function pushText($char)
+ {
+ if ($char == '<') {
+ $this->text .= "<";
+ } elseif ($char == '>') {
+ $this->text .= ">";
+ } else {
+ $this->text .= $char;
+ }
+ }
+
+ /**
+ * Parse control
+ *
+ * @param string $control
+ * @param string $parameter
+ */
+ private function parseControl($control, $parameter)
+ {
+ $controls = array(
+ 'par' => array(self::PARA, 'paragraph', true),
+ 'b' => array(self::STYL, 'font', 'bold', true),
+ 'i' => array(self::STYL, 'font', 'italic', true),
+ 'u' => array(self::STYL, 'font', 'underline', true),
+ 'strike' => array(self::STYL, 'font', 'strikethrough',true),
+ 'fs' => array(self::STYL, 'font', 'size', $parameter),
+ 'qc' => array(self::STYL, 'paragraph', 'align', 'center'),
+ 'sa' => array(self::STYL, 'paragraph', 'spaceAfter', $parameter),
+ 'fonttbl' => array(self::SKIP, 'fonttbl', null),
+ 'colortbl' => array(self::SKIP, 'colortbl', null),
+ 'info' => array(self::SKIP, 'info', null),
+ 'generator' => array(self::SKIP, 'generator', null),
+ 'title' => array(self::SKIP, 'title', null),
+ 'subject' => array(self::SKIP, 'subject', null),
+ 'category' => array(self::SKIP, 'category', null),
+ 'keywords' => array(self::SKIP, 'keywords', null),
+ 'comment' => array(self::SKIP, 'comment', null),
+ 'shppict' => array(self::SKIP, 'pic', null),
+ 'fldinst' => array(self::SKIP, 'link', null),
+ );
+
+ if (array_key_exists($control, $controls)) {
+ list($function) = $controls[$control];
+ if (method_exists($this, $function)) {
+ $directives = $controls[$control];
+ array_shift($directives); // remove the function variable; we won't need it
+ $this->$function($directives);
+ }
+ }
+ }
+
+ /**
+ * Read paragraph
+ *
+ * @param array $directives
+ */
+ private function readParagraph($directives)
+ {
+ list($property, $value) = $directives;
+ $this->textrun = $this->section->addTextRun();
+ $this->flags[$property] = $value;
+ }
+
+ /**
+ * Read style
+ *
+ * @param array $directives
+ */
+ private function readStyle($directives)
+ {
+ list($style, $property, $value) = $directives;
+ $this->flags['styles'][$style][$property] = $value;
+ }
+
+ /**
+ * Read skip
+ *
+ * @param array $directives
+ */
+ private function readSkip($directives)
+ {
+ list($property) = $directives;
+ $this->flags['property'] = $property;
+ $this->flags['skipped'] = true;
+ }
+
+ /**
+ * Read text
+ */
+ private function readText()
+ {
+ $text = $this->textrun->addText($this->text);
+ if (isset($this->flags['styles']['font'])) {
+ $text->getFontStyle()->setStyleByArray($this->flags['styles']['font']);
+ }
+ }
+}
diff --git a/src/PhpWord/Reader/ReaderInterface.php b/src/PhpWord/Reader/ReaderInterface.php
index 2829d4abb3..361c413796 100644
--- a/src/PhpWord/Reader/ReaderInterface.php
+++ b/src/PhpWord/Reader/ReaderInterface.php
@@ -1,31 +1,41 @@
setRels($relationships);
$part->read($phpWord);
@@ -101,8 +110,7 @@ private function readRelationships($docFile)
// word/_rels/*.xml.rels
$wordRelsPath = 'word/_rels/';
- $zipClass = Settings::getZipClass();
- $zip = new $zipClass();
+ $zip = new ZipArchive();
if ($zip->open($docFile) === true) {
for ($i = 0; $i < $zip->numFiles; $i++) {
$xmlFile = $zip->getNameIndex($i);
diff --git a/src/PhpWord/Reader/Word2007/AbstractPart.php b/src/PhpWord/Reader/Word2007/AbstractPart.php
index 57ad4815dc..a7261d6d9d 100644
--- a/src/PhpWord/Reader/Word2007/AbstractPart.php
+++ b/src/PhpWord/Reader/Word2007/AbstractPart.php
@@ -1,10 +1,18 @@
rels = $value;
}
+ /**
+ * Read w:p
+ *
+ * @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 = 'document')
+ {
+ // Paragraph style
+ $paragraphStyle = null;
+ $headingMatches = array();
+ if ($xmlReader->elementExists('w:pPr', $domNode)) {
+ $paragraphStyle = $this->readParagraphStyle($xmlReader, $domNode);
+ if (is_array($paragraphStyle) && array_key_exists('styleName', $paragraphStyle)) {
+ preg_match('/Heading(\d)/', $paragraphStyle['styleName'], $headingMatches);
+ }
+ }
+
+ // PreserveText
+ if ($xmlReader->elementExists('w:r/w:instrText', $domNode)) {
+ $ignoreText = false;
+ $textContent = '';
+ $fontStyle = $this->readFontStyle($xmlReader, $domNode);
+ $nodes = $xmlReader->getElements('w:r', $domNode);
+ foreach ($nodes as $node) {
+ $instrText = $xmlReader->getValue('w:instrText', $node);
+ if ($xmlReader->elementExists('w:fldChar', $node)) {
+ $fldCharType = $xmlReader->getAttribute('w:fldCharType', $node, 'w:fldChar');
+ if ($fldCharType == 'begin') {
+ $ignoreText = true;
+ } elseif ($fldCharType == 'end') {
+ $ignoreText = false;
+ }
+ }
+ if (!is_null($instrText)) {
+ $textContent .= '{' . $instrText . '}';
+ } else {
+ if ($ignoreText === false) {
+ $textContent .= $xmlReader->getValue('w:t', $node);
+ }
+ }
+ }
+ $parent->addPreserveText($textContent, $fontStyle, $paragraphStyle);
+
+ // List item
+ } elseif ($xmlReader->elementExists('w:pPr/w:numPr', $domNode)) {
+ $textContent = '';
+ $numId = $xmlReader->getAttribute('w:val', $domNode, 'w:pPr/w:numPr/w:numId');
+ $levelId = $xmlReader->getAttribute('w:val', $domNode, 'w:pPr/w:numPr/w:ilvl');
+ $nodes = $xmlReader->getElements('w:r', $domNode);
+ foreach ($nodes as $node) {
+ $textContent .= $xmlReader->getValue('w:t', $node);
+ }
+ $parent->addListItem($textContent, $levelId, null, "PHPWordList{$numId}", $paragraphStyle);
+
+ // Heading
+ } elseif (!empty($headingMatches)) {
+ $textContent = '';
+ $nodes = $xmlReader->getElements('w:r', $domNode);
+ foreach ($nodes as $node) {
+ $textContent .= $xmlReader->getValue('w:t', $node);
+ }
+ $parent->addTitle($textContent, $headingMatches[1]);
+
+ // Text and TextRun
+ } else {
+ $runCount = $xmlReader->countElements('w:r', $domNode);
+ $linkCount = $xmlReader->countElements('w:hyperlink', $domNode);
+ $runLinkCount = $runCount + $linkCount;
+ if ($runLinkCount == 0) {
+ $parent->addTextBreak(null, $paragraphStyle);
+ } else {
+ if ($runLinkCount > 1) {
+ $textrun = $parent->addTextRun($paragraphStyle);
+ $textParent = &$textrun;
+ } else {
+ $textParent = &$parent;
+ }
+ $nodes = $xmlReader->getElements('*', $domNode);
+ foreach ($nodes as $node) {
+ $this->readRun($xmlReader, $node, $textParent, $docPart, $paragraphStyle);
+ }
+ }
+ }
+ }
+
/**
* Read w:r
*
@@ -128,144 +241,144 @@ protected function readRun(XMLReader $xmlReader, \DOMElement $domNode, &$parent,
}
/**
- * Read w:pPr
+ * Read w:tbl
*
- * @return string|array|null
+ * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
+ * @param \DOMElement $domNode
+ * @param mixed $parent
+ * @param string $docPart
*/
- protected function readParagraphStyle(XMLReader $xmlReader, \DOMElement $domNode)
+ protected function readTable(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart = 'document')
{
- $style = null;
- if ($xmlReader->elementExists('w:pPr', $domNode)) {
- if ($xmlReader->elementExists('w:pPr/w:pStyle', $domNode)) {
- $style = $xmlReader->getAttribute('w:val', $domNode, 'w:pPr/w:pStyle');
- } else {
- $style = array();
- $mapping = array(
- 'w:ind' => 'indent', 'w:spacing' => 'spacing',
- 'w:jc' => 'align', 'w:basedOn' => 'basedOn', 'w:next' => 'next',
- 'w:widowControl' => 'widowControl', 'w:keepNext' => 'keepNext',
- 'w:keepLines' => 'keepLines', 'w:pageBreakBefore' => 'pageBreakBefore',
+ // Table style
+ $tblStyle = null;
+ if ($xmlReader->elementExists('w:tblPr', $domNode)) {
+ $tblStyle = $this->readTableStyle($xmlReader, $domNode);
+ }
+
+ /** @var \PhpOffice\PhpWord\Element\Table $table Type hint */
+ $table = $parent->addTable($tblStyle);
+ $tblNodes = $xmlReader->getElements('*', $domNode);
+ foreach ($tblNodes as $tblNode) {
+ if ($tblNode->nodeName == 'w:tblGrid') { // Column
+ // @todo Do something with table columns
+
+ } elseif ($tblNode->nodeName == 'w:tr') { // Row
+ $rowHeight = $xmlReader->getAttribute('w:val', $tblNode, 'w:trPr/w:trHeight');
+ $rowHRule = $xmlReader->getAttribute('w:hRule', $tblNode, 'w:trPr/w:trHeight');
+ $rowHRule = $rowHRule == 'exact' ? true : false;
+ $rowStyle = array(
+ 'tblHeader' => $xmlReader->elementExists('w:trPr/w:tblHeader', $tblNode),
+ 'cantSplit' => $xmlReader->elementExists('w:trPr/w:cantSplit', $tblNode),
+ 'exactHeight' => $rowHRule,
);
- $nodes = $xmlReader->getElements('w:pPr/*', $domNode);
- foreach ($nodes as $node) {
- if (!array_key_exists($node->nodeName, $mapping)) {
- continue;
- }
- $property = $mapping[$node->nodeName];
- switch ($node->nodeName) {
-
- case 'w:ind':
- $style['indent'] = $xmlReader->getAttribute('w:left', $node);
- $style['hanging'] = $xmlReader->getAttribute('w:hanging', $node);
- break;
-
- case 'w:spacing':
- $style['spaceAfter'] = $xmlReader->getAttribute('w:after', $node);
- $style['spaceBefore'] = $xmlReader->getAttribute('w:before', $node);
- // Commented. Need to adjust the number when return value is null
- // $style['spacing'] = $xmlReader->getAttribute('w:line', $node);
- break;
-
- case 'w:keepNext':
- case 'w:keepLines':
- case 'w:pageBreakBefore':
- $style[$property] = true;
- break;
-
- case 'w:widowControl':
- $style[$property] = false;
- break;
-
- case 'w:jc':
- case 'w:basedOn':
- case 'w:next':
- $style[$property] = $xmlReader->getAttribute('w:val', $node);
- break;
+ $row = $table->addRow($rowHeight, $rowStyle);
+ $rowNodes = $xmlReader->getElements('*', $tblNode);
+ foreach ($rowNodes as $rowNode) {
+ if ($rowNode->nodeName == 'w:trPr') { // Row style
+ // @todo Do something with row style
+
+ } elseif ($rowNode->nodeName == 'w:tc') { // Cell
+ $cellWidth = $xmlReader->getAttribute('w:w', $rowNode, 'w:tcPr/w:tcW');
+ $cellStyle = null;
+ $cellStyleNode = $xmlReader->getElement('w:tcPr', $rowNode);
+ if (!is_null($cellStyleNode)) {
+ $cellStyle = $this->readCellStyle($xmlReader, $cellStyleNode);
+ }
+
+ $cell = $row->addCell($cellWidth, $cellStyle);
+ $cellNodes = $xmlReader->getElements('*', $rowNode);
+ foreach ($cellNodes as $cellNode) {
+ if ($cellNode->nodeName == 'w:p') { // Paragraph
+ $this->readParagraph($xmlReader, $cellNode, $cell, $docPart);
+ }
+ }
}
}
}
}
+ }
- return $style;
+ /**
+ * Read w:pPr
+ *
+ * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
+ * @param \DOMElement $domNode
+ * @return array|null
+ */
+ protected function readParagraphStyle(XMLReader $xmlReader, \DOMElement $domNode)
+ {
+ if (!$xmlReader->elementExists('w:pPr', $domNode)) {
+ return null;
+ }
+
+ $styleNode = $xmlReader->getElement('w:pPr', $domNode);
+ $styleDefs = array(
+ 'styleName' => array(self::READ_VALUE, 'w:pStyle'),
+ 'align' => array(self::READ_VALUE, 'w:jc'),
+ 'basedOn' => array(self::READ_VALUE, 'w:basedOn'),
+ 'next' => array(self::READ_VALUE, 'w:next'),
+ 'indent' => array(self::READ_VALUE, 'w:ind', 'w:left'),
+ 'hanging' => array(self::READ_VALUE, 'w:ind', 'w:hanging'),
+ 'spaceAfter' => array(self::READ_VALUE, 'w:spacing', 'w:after'),
+ 'spaceBefore' => array(self::READ_VALUE, 'w:spacing', 'w:before'),
+ 'widowControl' => array(self::READ_FALSE, 'w:widowControl'),
+ 'keepNext' => array(self::READ_TRUE, 'w:keepNext'),
+ 'keepLines' => array(self::READ_TRUE, 'w:keepLines'),
+ 'pageBreakBefore' => array(self::READ_TRUE, 'w:pageBreakBefore'),
+ );
+
+ return $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
}
/**
* Read w:rPr
*
- * @return string|array|null
+ * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
+ * @param \DOMElement $domNode
+ * @return array
*/
protected function readFontStyle(XMLReader $xmlReader, \DOMElement $domNode)
{
- $style = null;
+ if (is_null($domNode)) {
+ return null;
+ }
// Hyperlink has an extra w:r child
if ($domNode->nodeName == 'w:hyperlink') {
$domNode = $xmlReader->getElement('w:r', $domNode);
}
- if (is_null($domNode)) {
- return $style;
+ if (!$xmlReader->elementExists('w:rPr', $domNode)) {
+ return null;
}
- if ($xmlReader->elementExists('w:rPr', $domNode)) {
- if ($xmlReader->elementExists('w:rPr/w:rStyle', $domNode)) {
- $style = $xmlReader->getAttribute('w:val', $domNode, 'w:rPr/w:rStyle');
- } else {
- $style = array();
- $mapping = array(
- 'w:b' => 'bold', 'w:i' => 'italic', 'w:color' => 'color',
- 'w:strike' => 'strikethrough', 'w:u' => 'underline',
- 'w:highlight' => 'fgColor', 'w:sz' => 'size',
- 'w:rFonts' => 'name', 'w:vertAlign' => 'superScript',
- );
- $nodes = $xmlReader->getElements('w:rPr/*', $domNode);
- foreach ($nodes as $node) {
- if (!array_key_exists($node->nodeName, $mapping)) {
- continue;
- }
- $property = $mapping[$node->nodeName];
- switch ($node->nodeName) {
-
- case 'w:rFonts':
- $style['name'] = $xmlReader->getAttribute('w:ascii', $node);
- $style['hint'] = $xmlReader->getAttribute('w:hint', $node);
- break;
-
- case 'w:b':
- case 'w:i':
- case 'w:strike':
- $style[$property] = true;
- break;
-
- case 'w:u':
- case 'w:highlight':
- case 'w:color':
- $style[$property] = $xmlReader->getAttribute('w:val', $node);
- break;
-
- case 'w:sz':
- $style[$property] = $xmlReader->getAttribute('w:val', $node) / 2;
- break;
-
- case 'w:vertAlign':
- $style[$property] = $xmlReader->getAttribute('w:val', $node);
- if ($style[$property] == 'superscript') {
- $style['superScript'] = true;
- } else {
- $style['superScript'] = false;
- $style['subScript'] = true;
- }
- break;
- }
- }
- }
- }
-
- return $style;
+ $styleNode = $xmlReader->getElement('w:rPr', $domNode);
+ $styleDefs = array(
+ 'styleName' => array(self::READ_VALUE, 'w:rStyle'),
+ 'name' => array(self::READ_VALUE, 'w:rFonts', 'w:ascii'),
+ 'hint' => array(self::READ_VALUE, 'w:rFonts', 'w:hint'),
+ 'size' => array(self::READ_SIZE, 'w:sz'),
+ 'color' => array(self::READ_VALUE, 'w:color'),
+ 'underline' => array(self::READ_VALUE, 'w:u'),
+ 'bold' => array(self::READ_TRUE, 'w:b'),
+ 'italic' => array(self::READ_TRUE, 'w:i'),
+ 'strikethrough' => array(self::READ_TRUE, 'w:strike'),
+ 'doubleStrikethrough' => array(self::READ_TRUE, 'w:dstrike'),
+ 'smallCaps' => array(self::READ_TRUE, 'w:smallCaps'),
+ 'allCaps' => array(self::READ_TRUE, 'w:caps'),
+ 'superScript' => array(self::READ_EQUAL, 'w:vertAlign', 'w:val', 'superscript'),
+ 'subScript' => array(self::READ_EQUAL, 'w:vertAlign', 'w:val', 'subscript'),
+ 'fgColor' => array(self::READ_VALUE, 'w:highlight'),
+ );
+
+ return $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
}
/**
* Read w:tblPr
*
+ * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
+ * @param \DOMElement $domNode
* @return string|array|null
* @todo Capture w:tblStylePr w:type="firstRow"
*/
@@ -279,39 +392,101 @@ protected function readTableStyle(XMLReader $xmlReader, \DOMElement $domNode)
if ($xmlReader->elementExists('w:tblPr/w:tblStyle', $domNode)) {
$style = $xmlReader->getAttribute('w:val', $domNode, 'w:tblPr/w:tblStyle');
} else {
- $style = array();
- $mapping = array(
- 'w:tblCellMar' => 'cellMargin',
- 'w:tblBorders' => 'border',
- );
+ $styleNode = $xmlReader->getElement('w:tblPr', $domNode);
+ $styleDefs = array();
+ // $styleDefs['styleName'] = array(self::READ_VALUE, 'w:tblStyle');
+ foreach ($margins as $side) {
+ $ucfSide = ucfirst($side);
+ $styleDefs["cellMargin$ucfSide"] = array(self::READ_VALUE, "w:tblCellMar/w:$side", 'w:w');
+ }
+ foreach ($borders as $side) {
+ $ucfSide = ucfirst($side);
+ $styleDefs["border{$ucfSide}Size"] = array(self::READ_VALUE, "w:tblBorders/w:$side", 'w:sz');
+ $styleDefs["border{$ucfSide}Color"] = array(self::READ_VALUE, "w:tblBorders/w:$side", 'w:color');
+ }
+ $style = $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
+ }
+ }
- $nodes = $xmlReader->getElements('w:tblPr/*', $domNode);
- foreach ($nodes as $node) {
- if (!array_key_exists($node->nodeName, $mapping)) {
- continue;
- }
- // $property = $mapping[$node->nodeName];
- switch ($node->nodeName) {
+ return $style;
+ }
- case 'w:tblCellMar':
- foreach ($margins as $side) {
- $ucfSide = ucfirst($side);
- $style["cellMargin$ucfSide"] = $xmlReader->getAttribute('w:w', $node, "w:$side");
- }
- break;
+ /**
+ * Read w:tcPr
+ *
+ * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
+ * @param \DOMElement $domNode
+ * @return array
+ */
+ private function readCellStyle(XMLReader $xmlReader, \DOMElement $domNode)
+ {
+ $styleDefs = array(
+ 'valign' => array(self::READ_VALUE, 'w:vAlign'),
+ 'textDirection' => array(self::READ_VALUE, 'w:textDirection'),
+ 'gridSpan' => array(self::READ_VALUE, 'w:gridSpan'),
+ 'vMerge' => array(self::READ_VALUE, 'w:vMerge'),
+ 'bgColor' => array(self::READ_VALUE, 'w:shd/w:fill'),
+ );
+
+ return $this->readStyleDefs($xmlReader, $domNode, $styleDefs);
+ }
- case 'w:tblBorders':
- foreach ($borders as $side) {
- $ucfSide = ucfirst($side);
- $style["border{$ucfSide}Size"] = $xmlReader->getAttribute('w:sz', $node, "w:$side");
- $style["border{$ucfSide}Color"] = $xmlReader->getAttribute('w:color', $node, "w:$side");
- }
- break;
- }
+ /**
+ * Read style definition
+ *
+ * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
+ * @param \DOMElement $parentNode
+ * @param array $styleDefs
+ * @ignoreScrutinizerPatch
+ * @return array
+ */
+ protected function readStyleDefs(XMLReader $xmlReader, \DOMElement $parentNode = null, $styleDefs = array())
+ {
+ $styles = array();
+
+ foreach ($styleDefs as $styleProp => $styleVal) {
+ @list($method, $element, $attribute, $expected) = $styleVal;
+
+ if ($xmlReader->elementExists($element, $parentNode)) {
+ $node = $xmlReader->getElement($element, $parentNode);
+
+ // Use w:val as default if no attribute assigned
+ $attribute = ($attribute === null) ? 'w:val' : $attribute;
+ $attributeValue = $xmlReader->getAttribute($attribute, $node);
+
+ $styleValue = $this->readStyleDef($method, $attributeValue, $expected);
+ if ($styleValue !== null) {
+ $styles[$styleProp] = $styleValue;
}
}
}
+ return $styles;
+ }
+
+ /**
+ * Return style definition based on conversion method
+ *
+ * @param string $method
+ * @ignoreScrutinizerPatch
+ * @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;
}
diff --git a/src/PhpWord/Reader/Word2007/DocProps.php b/src/PhpWord/Reader/Word2007/DocProps.php
deleted file mode 100644
index 2c76417aad..0000000000
--- a/src/PhpWord/Reader/Word2007/DocProps.php
+++ /dev/null
@@ -1,63 +0,0 @@
-getDomFromZip($this->docFile, $this->xmlFile);
-
- $docProps = $phpWord->getDocumentProperties();
-
- $nodes = $xmlReader->getElements('*');
- if ($nodes->length > 0) {
- foreach ($nodes as $node) {
- if (!array_key_exists($node->nodeName, $this->mapping)) {
- continue;
- }
- $method = $this->mapping[$node->nodeName];
- $value = $node->nodeValue == '' ? null : $node->nodeValue;
- if (array_key_exists($node->nodeName, $this->callbacks)) {
- $value = $this->callbacks[$node->nodeName]($value);
- }
- if (method_exists($docProps, $method)) {
- $docProps->$method($value);
- }
- }
- }
- }
-}
diff --git a/src/PhpWord/Reader/Word2007/DocPropsApp.php b/src/PhpWord/Reader/Word2007/DocPropsApp.php
index 7797528aec..ddbe474f1a 100644
--- a/src/PhpWord/Reader/Word2007/DocPropsApp.php
+++ b/src/PhpWord/Reader/Word2007/DocPropsApp.php
@@ -1,18 +1,28 @@
'setCompany', 'Manager' => 'setManager');
+
+ /**
+ * Callback functions
+ *
+ * @var array
+ */
+ protected $callbacks = array();
}
diff --git a/src/PhpWord/Reader/Word2007/DocPropsCore.php b/src/PhpWord/Reader/Word2007/DocPropsCore.php
index fd943875ed..0b92b64dcd 100644
--- a/src/PhpWord/Reader/Word2007/DocPropsCore.php
+++ b/src/PhpWord/Reader/Word2007/DocPropsCore.php
@@ -1,18 +1,31 @@
'strtotime', 'dcterms:modified' => 'strtotime');
+
+ /**
+ * Read core/extended document properties
+ *
+ * @param \PhpOffice\PhpWord\PhpWord $phpWord
+ */
+ public function read(PhpWord &$phpWord)
+ {
+ $xmlReader = new XMLReader();
+ $xmlReader->getDomFromZip($this->docFile, $this->xmlFile);
+
+ $docProps = $phpWord->getDocumentProperties();
+
+ $nodes = $xmlReader->getElements('*');
+ if ($nodes->length > 0) {
+ foreach ($nodes as $node) {
+ if (!array_key_exists($node->nodeName, $this->mapping)) {
+ continue;
+ }
+ $method = $this->mapping[$node->nodeName];
+ $value = $node->nodeValue == '' ? null : $node->nodeValue;
+ if (array_key_exists($node->nodeName, $this->callbacks)) {
+ $value = $this->callbacks[$node->nodeName]($value);
+ }
+ if (method_exists($docProps, $method)) {
+ $docProps->$method($value);
+ }
+ }
+ }
+ }
}
diff --git a/src/PhpWord/Reader/Word2007/DocPropsCustom.php b/src/PhpWord/Reader/Word2007/DocPropsCustom.php
index 6c67dc7a64..efbbfaa5c8 100644
--- a/src/PhpWord/Reader/Word2007/DocPropsCustom.php
+++ b/src/PhpWord/Reader/Word2007/DocPropsCustom.php
@@ -1,10 +1,18 @@
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);
- if (!is_null($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);
- if (!is_null($settings)) {
- $this->readHeaderFooter($settings, $section);
- }
- break;
+ if (array_key_exists($node->nodeName, $readMethods)) {
+ $readMethod = $readMethods[$node->nodeName];
+ $this->$readMethod($xmlReader, $node, $section);
}
}
}
@@ -78,14 +66,16 @@ public function read(PhpWord &$phpWord)
* @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();
@@ -93,15 +83,9 @@ private function readHeaderFooter($settings, &$section)
$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);
}
}
}
@@ -111,240 +95,88 @@ private function readHeaderFooter($settings, &$section)
}
/**
- * Read w:p
- *
- * @param mixed $parent
- * @param string $docPart
- *
- * @todo Get font style for preserve text
- */
- private function readParagraph(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart)
- {
- // Paragraph style
- $paragraphStyle = null;
- $headingMatches = array();
- if ($xmlReader->elementExists('w:pPr', $domNode)) {
- $paragraphStyle = $this->readParagraphStyle($xmlReader, $domNode);
- if (is_string($paragraphStyle)) {
- preg_match('/Heading(\d)/', $paragraphStyle, $headingMatches);
- }
- }
-
- // PreserveText
- if ($xmlReader->elementExists('w:r/w:instrText', $domNode)) {
- $ignoreText = false;
- $textContent = '';
- $fontStyle = $this->readFontStyle($xmlReader, $domNode);
- $nodes = $xmlReader->getElements('w:r', $domNode);
- foreach ($nodes as $node) {
- $instrText = $xmlReader->getValue('w:instrText', $node);
- if ($xmlReader->elementExists('w:fldChar', $node)) {
- $fldCharType = $xmlReader->getAttribute('w:fldCharType', $node, 'w:fldChar');
- if ($fldCharType == 'begin') {
- $ignoreText = true;
- } elseif ($fldCharType == 'end') {
- $ignoreText = false;
- }
- }
- if (!is_null($instrText)) {
- $textContent .= '{' . $instrText . '}';
- } else {
- if ($ignoreText === false) {
- $textContent .= $xmlReader->getValue('w:t', $node);
- }
- }
- }
- $parent->addPreserveText($textContent, $fontStyle, $paragraphStyle);
-
- // List item
- } elseif ($xmlReader->elementExists('w:pPr/w:numPr', $domNode)) {
- $textContent = '';
- $numId = $xmlReader->getAttribute('w:val', $domNode, 'w:pPr/w:numPr/w:numId');
- $levelId = $xmlReader->getAttribute('w:val', $domNode, 'w:pPr/w:numPr/w:ilvl');
- $nodes = $xmlReader->getElements('w:r', $domNode);
- foreach ($nodes as $node) {
- $textContent .= $xmlReader->getValue('w:t', $node);
- }
- $parent->addListItem($textContent, $levelId, null, "PHPWordList{$numId}", $paragraphStyle);
-
- // Heading
- } elseif (!empty($headingMatches)) {
- $textContent = '';
- $nodes = $xmlReader->getElements('w:r', $domNode);
- foreach ($nodes as $node) {
- $textContent .= $xmlReader->getValue('w:t', $node);
- }
- $parent->addTitle($textContent, $headingMatches[1]);
-
- // Text and TextRun
- } else {
- $runCount = $xmlReader->countElements('w:r', $domNode);
- $linkCount = $xmlReader->countElements('w:hyperlink', $domNode);
- $runLinkCount = $runCount + $linkCount;
- if ($runLinkCount == 0) {
- $parent->addTextBreak(null, $paragraphStyle);
- } else {
- if ($runLinkCount > 1) {
- $textrun = $parent->addTextRun($paragraphStyle);
- $textParent = &$textrun;
- } else {
- $textParent = &$parent;
- }
- $nodes = $xmlReader->getElements('*', $domNode);
- foreach ($nodes as $node) {
- $this->readRun($xmlReader, $node, $textParent, $docPart, $paragraphStyle);
- }
- }
- }
- }
-
- /**
- * Read w:tbl
+ * Read w:sectPr
*
- * @param mixed $parent
- * @param string $docPart
+ * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
+ * @param \DOMElement $domNode
+ * @ignoreScrutinizerPatch
+ * @return array
*/
- private function readTable(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart)
+ private function readSectionStyle(XMLReader $xmlReader, \DOMElement $domNode)
{
- // Table style
- $tblStyle = null;
- if ($xmlReader->elementExists('w:tblPr', $domNode)) {
- $tblStyle = $this->readTableStyle($xmlReader, $domNode);
- }
-
- $table = $parent->addTable($tblStyle);
- $tblNodes = $xmlReader->getElements('*', $domNode);
- foreach ($tblNodes as $tblNode) {
- if ($tblNode->nodeName == 'w:tblGrid') { // Column
- // @todo Do something with table columns
+ $styleDefs = array(
+ 'breakType' => array(self::READ_VALUE, 'w:type'),
+ 'pageSizeW' => array(self::READ_VALUE, 'w:pgSz', 'w:w'),
+ 'pageSizeH' => array(self::READ_VALUE, 'w:pgSz', 'w:h'),
+ 'orientation' => array(self::READ_VALUE, 'w:pgSz', 'w:orient'),
+ 'colsNum' => array(self::READ_VALUE, 'w:cols', 'w:num'),
+ 'colsSpace' => array(self::READ_VALUE, 'w:cols', 'w:space'),
+ 'topMargin' => array(self::READ_VALUE, 'w:pgMar', 'w:top'),
+ 'leftMargin' => array(self::READ_VALUE, 'w:pgMar', 'w:left'),
+ 'bottomMargin' => array(self::READ_VALUE, 'w:pgMar', 'w:bottom'),
+ 'rightMargin' => array(self::READ_VALUE, 'w:pgMar', 'w:right'),
+ 'headerHeight' => array(self::READ_VALUE, 'w:pgMar', 'w:header'),
+ 'footerHeight' => array(self::READ_VALUE, 'w:pgMar', 'w:footer'),
+ 'gutter' => array(self::READ_VALUE, 'w:pgMar', 'w:gutter'),
+ );
+ $styles = $this->readStyleDefs($xmlReader, $domNode, $styleDefs);
- } elseif ($tblNode->nodeName == 'w:tr') { // Row
- $rowHeight = $xmlReader->getAttribute('w:val', $tblNode, 'w:trPr/w:trHeight');
- $rowHRule = $xmlReader->getAttribute('w:hRule', $tblNode, 'w:trPr/w:trHeight');
- $rowHRule = $rowHRule == 'exact' ? true : false;
- $rowStyle = array(
- 'tblHeader' => $xmlReader->elementExists('w:trPr/w:tblHeader', $tblNode),
- 'cantSplit' => $xmlReader->elementExists('w:trPr/w:cantSplit', $tblNode),
- 'exactHeight' => $rowHRule,
+ // Header and footer
+ // @todo Cleanup this part
+ $nodes = $xmlReader->getElements('*', $domNode);
+ foreach ($nodes as $node) {
+ if ($node->nodeName == 'w:headerReference' || $node->nodeName == 'w:footerReference') {
+ $id = $xmlReader->getAttribute('r:id', $node);
+ $styles['hf'][$id] = array(
+ 'method' => str_replace('w:', '', str_replace('Reference', '', $node->nodeName)),
+ 'type' => $xmlReader->getAttribute('w:type', $node),
);
-
- $row = $table->addRow($rowHeight, $rowStyle);
- $rowNodes = $xmlReader->getElements('*', $tblNode);
- foreach ($rowNodes as $rowNode) {
- if ($rowNode->nodeName == 'w:trPr') { // Row style
- // @todo Do something with row style
-
- } elseif ($rowNode->nodeName == 'w:tc') { // Cell
- $cellWidth = $xmlReader->getAttribute('w:w', $rowNode, 'w:tcPr/w:tcW');
- $cellStyle = null;
- $cellStyleNode = $xmlReader->getElement('w:tcPr', $rowNode);
- if (!is_null($cellStyleNode)) {
- $cellStyle = $this->readCellStyle($xmlReader, $cellStyleNode);
- }
-
- $cell = $row->addCell($cellWidth, $cellStyle);
- $cellNodes = $xmlReader->getElements('*', $rowNode);
- foreach ($cellNodes as $cellNode) {
- if ($cellNode->nodeName == 'w:p') { // Paragraph
- $this->readParagraph($xmlReader, $cellNode, $cell, $docPart);
- }
- }
- }
- }
}
}
+
+ return $styles;
}
/**
- * Read w:sectPr
+ * Read w:p node
*
- * @return array|null
+ * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
+ * @param \DOMElement $node
+ * @param \PhpOffice\PhpWord\Element\Section $section
+ *
+ * @todo
*/
- private function readSectionStyle(XMLReader $xmlReader, \DOMElement $domNode)
+ private function readWPNode(XMLReader $xmlReader, \DOMElement $node, Section &$section)
{
- $ret = null;
- $mapping = array(
- 'w:type' => 'breakType', 'w:pgSz' => 'pageSize',
- 'w:pgMar' => 'pageMargin', 'w:cols' => 'columns',
- 'w:headerReference' => 'header', 'w:footerReference' => 'footer',
- );
- $nodes = $xmlReader->getElements('*', $domNode);
- foreach ($nodes as $node) {
- if (!array_key_exists($node->nodeName, $mapping)) {
- continue;
- }
- $property = $mapping[$node->nodeName];
- switch ($node->nodeName) {
-
- case 'w:type':
- $ret['breakType'] = $xmlReader->getAttribute('w:val', $node);
- break;
-
- case 'w:pgSz':
- $ret['pageSizeW'] = $xmlReader->getAttribute('w:w', $node);
- $ret['pageSizeH'] = $xmlReader->getAttribute('w:h', $node);
- $ret['orientation'] = $xmlReader->getAttribute('w:orient', $node);
- break;
-
- case 'w:pgMar':
- $ret['topMargin'] = $xmlReader->getAttribute('w:top', $node);
- $ret['leftMargin'] = $xmlReader->getAttribute('w:left', $node);
- $ret['bottomMargin'] = $xmlReader->getAttribute('w:bottom', $node);
- $ret['rightMargin'] = $xmlReader->getAttribute('w:right', $node);
- $ret['headerHeight'] = $xmlReader->getAttribute('w:header', $node);
- $ret['footerHeight'] = $xmlReader->getAttribute('w:footer', $node);
- $ret['gutter'] = $xmlReader->getAttribute('w:gutter', $node);
- break;
+ // Page break
+ if ($xmlReader->getAttribute('w:type', $node, 'w:r/w:br') == 'page') {
+ $section->addPageBreak(); // PageBreak
+ }
- case 'w:cols':
- $ret['colsNum'] = $xmlReader->getAttribute('w:num', $node);
- $ret['colsSpace'] = $xmlReader->getAttribute('w:space', $node);
- break;
+ // Paragraph
+ $this->readParagraph($xmlReader, $node, $section);
- case 'w:headerReference':
- case 'w:footerReference':
- $id = $xmlReader->getAttribute('r:id', $node);
- $ret['hf'][$id] = array(
- 'method' => $property,
- 'type' => $xmlReader->getAttribute('w:type', $node),
- );
- break;
+ // 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();
}
-
- return $ret;
}
/**
- * Read w:tcPr
+ * Read w:sectPr node
*
- * @return array|null
+ * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
+ * @param \DOMElement $node
+ * @param \PhpOffice\PhpWord\Element\Section $section
*/
- private function readCellStyle(XMLReader $xmlReader, \DOMElement $domNode)
+ private function readWSectPrNode(XMLReader $xmlReader, \DOMElement $node, Section &$section)
{
- $style = null;
- $mapping = array(
- 'w:shd' => 'bgColor',
- 'w:vAlign' => 'valign', 'w:textDirection' => 'textDirection',
- 'w:gridSpan' => 'gridSpan', 'w:vMerge' => 'vMerge',
- );
- $nodes = $xmlReader->getElements('*', $domNode);
- foreach ($nodes as $node) {
- if (!array_key_exists($node->nodeName, $mapping)) {
- continue;
- }
- $property = $mapping[$node->nodeName];
- switch ($node->nodeName) {
- case 'w:shd':
- $style['bgColor'] = $xmlReader->getAttribute('w:fill', $node);
- break;
-
- default:
- $style[$property] = $xmlReader->getAttribute('w:val', $node);
- break;
- }
- }
-
- return $style;
+ $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 235dab4b55..c493c34790 100644
--- a/src/PhpWord/Reader/Word2007/Endnotes.php
+++ b/src/PhpWord/Reader/Word2007/Endnotes.php
@@ -1,23 +1,40 @@
collection}";
+ $collection = $phpWord->$getMethod()->getItems();
+
+ $xmlReader = new XMLReader();
+ $xmlReader->getDomFromZip($this->docFile, $this->xmlFile);
+ $nodes = $xmlReader->getElements('*');
+ if ($nodes->length > 0) {
+ foreach ($nodes as $node) {
+ $id = $xmlReader->getAttribute('w:id', $node);
+ $type = $xmlReader->getAttribute('w:type', $node);
+
+ // Avoid w:type "separator" and "continuationSeparator"
+ // Only look for or without w:type attribute
+ if (is_null($type) && array_key_exists($id, $collection)) {
+ $element = $collection[$id];
+ $pNodes = $xmlReader->getElements('w:p/*', $node);
+ foreach ($pNodes as $pNode) {
+ $this->readRun($xmlReader, $pNode, $element, $this->collection);
+ }
+ $addMethod = "add{$this->element}";
+ $phpWord->$addMethod($element);
+ }
+ }
+ }
+ }
}
diff --git a/src/PhpWord/Reader/Word2007/Notes.php b/src/PhpWord/Reader/Word2007/Notes.php
deleted file mode 100644
index bdb0b9e8b5..0000000000
--- a/src/PhpWord/Reader/Word2007/Notes.php
+++ /dev/null
@@ -1,59 +0,0 @@
-type = ($this->type == 'endnotes') ? 'endnotes' : 'footnotes';
- $collectionClass = 'PhpOffice\\PhpWord\\' . ucfirst($this->type);
- $collection = $collectionClass::getElements();
-
- $xmlReader = new XMLReader();
- $xmlReader->getDomFromZip($this->docFile, $this->xmlFile);
- $nodes = $xmlReader->getElements('*');
- if ($nodes->length > 0) {
- foreach ($nodes as $node) {
- $id = $xmlReader->getAttribute('w:id', $node);
- $type = $xmlReader->getAttribute('w:type', $node);
-
- // Avoid w:type "separator" and "continuationSeparator"
- // Only look for or without w:type attribute
- if (is_null($type) && array_key_exists($id, $collection)) {
- $element = $collection[$id];
- $pNodes = $xmlReader->getElements('w:p/*', $node);
- foreach ($pNodes as $pNode) {
- $this->readRun($xmlReader, $pNode, $element, $type);
- }
- $collectionClass::setElement($id, $element);
- }
- }
- }
- }
-}
diff --git a/src/PhpWord/Reader/Word2007/Numbering.php b/src/PhpWord/Reader/Word2007/Numbering.php
index 5cd3f7ae06..2dd3f5217b 100644
--- a/src/PhpWord/Reader/Word2007/Numbering.php
+++ b/src/PhpWord/Reader/Word2007/Numbering.php
@@ -1,10 +1,18 @@
0) {
+ self::$defaultFontSize = $value;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Load setting from phpword.yml or phpword.yml.dist
+ *
+ * @param string $filename
+ * @return array
+ */
+ public static function loadConfig($filename = null)
+ {
+ // Get config file
+ $configFile = null;
+ $configPath = __DIR__ . '/../../';
+ if ($filename !== null) {
+ $files = array($filename);
+ } else {
+ $files = array("{$configPath}phpword.ini", "{$configPath}phpword.ini.dist");
+ }
+ foreach ($files as $file) {
+ if (file_exists($file)) {
+ $configFile = realpath($file);
+ break;
+ }
+ }
+
+ // Parse config file
+ $config = array();
+ if ($configFile !== null) {
+ $config = @parse_ini_file($configFile);
+ if ($config === false) {
+ return $config;
+ }
+ }
+
+ // Set config value
+ foreach ($config as $key => $value) {
+ $method = "set{$key}";
+ if (method_exists(__CLASS__, $method)) {
+ self::$method($value);
+ }
+ }
+
+ return $config;
+ }
+
+ /**
+ * Return the compatibility option used by the XMLWriter
+ *
+ * @deprecated 0.10.0
+ * @codeCoverageIgnore
+ */
+ public static function getCompatibility()
+ {
+ return self::hasCompatibility();
+ }
}
diff --git a/src/PhpWord/Shared/Drawing.php b/src/PhpWord/Shared/Drawing.php
index 58a6ee1a9c..5ee7e96087 100644
--- a/src/PhpWord/Shared/Drawing.php
+++ b/src/PhpWord/Shared/Drawing.php
@@ -1,10 +1,18 @@
' . $html . '