commit
38cb04d322
11
.travis.yml
11
.travis.yml
|
|
@ -9,20 +9,21 @@ php:
|
|||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: 5.2
|
||||
- php: hhvm
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: "Sq+6bVtnPsu0mWX8DWQ+9bGAjxMcGorksUiHc4YIXEJsuDfVmVlH8tTD547IeCjDAx9MxXerZ2Z4HSjxTB70VEnJPvZMHI/EZn4Ny31YLHEthdZbV5Gd1h0TGp8VOzPKGShvGrtGBX6MvMfgpK4zuieVWbSfdKeecm8ZNLMpUd4="
|
||||
|
||||
before_script:
|
||||
before_install:
|
||||
## Packages
|
||||
- sudo apt-get -qq update > /dev/null
|
||||
- sudo apt-get -qq install graphviz > /dev/null
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -y graphviz
|
||||
|
||||
before_script:
|
||||
## Composer
|
||||
- composer self-update
|
||||
- composer install --prefer-source --dev
|
||||
- composer install --prefer-source
|
||||
## PHPDocumentor
|
||||
- mkdir -p build/docs
|
||||
- mkdir -p build/coverage
|
||||
|
|
|
|||
86
CHANGELOG.md
86
CHANGELOG.md
|
|
@ -1,13 +1,29 @@
|
|||
# Changelog
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
This is the changelog between releases of PHPWord. Releases are listed in reverse chronological order with the latest version listed on top, while additions/changes in each release are listed in chronological order. Changes in each release are divided into three parts: added or change features, bugfixes, and miscellaneous improvements. Each line contains short information about the change made, the person who made it, and the related issue number(s) in GitHub.
|
||||
|
||||
## 0.12.0 - 3 January 2015
|
||||
0.12.1 (30 August 2015)
|
||||
-------------------
|
||||
|
||||
Maintenance release. This release is focused primarily on ``TemplateProcessor``.
|
||||
|
||||
### Changes
|
||||
- Changed visibility of all private properties and methods of ``TemplateProcessor`` to ``protected``. - @RomanSyroeshko #498
|
||||
- Improved performance of ``TemplateProcessor::setValue()``. - @RomanSyroeshko @nicoSWD #513
|
||||
|
||||
### Bugfixes
|
||||
- Fixed issue with "Access denied" message while opening ``Sample_07_TemplateCloneRow.docx`` and ``Sample_23_TemplateBlock.docx`` result files on Windows platform. - @RomanSyroeshko @AshSat #532
|
||||
- Fixed ``PreserveText`` element alignment in footer (see ``Sample_12_HeaderFooter.php``). - @RomanSyroeshko @SSchwaiger #495
|
||||
|
||||
|
||||
|
||||
0.12.0 (3 January 2015)
|
||||
-----------------------
|
||||
|
||||
This release added form fields (textinput, checkbox, and dropdown), drawing shapes (arc, curve, line, polyline, rect, oval), and basic 2D chart (pie, doughnut, bar, line, area, scatter, radar) elements along with some new styles. Basic MsDoc reader is introduced.
|
||||
|
||||
### Features
|
||||
|
||||
- Element: Ability to add drawing shapes (arc, curve, line, polyline, rect, oval) using new `Shape` element - @ivanlanin #123
|
||||
- Font: New `scale`, `spacing`, and `kerning` property of font style - @ivanlanin
|
||||
- Paragraph: Added shading to the paragraph style for full width shading - @lrobert #264
|
||||
|
|
@ -29,7 +45,6 @@ This release added form fields (textinput, checkbox, and dropdown), drawing shap
|
|||
- Report style options enumerated when style unknown - @h6w
|
||||
|
||||
### Bugfixes
|
||||
|
||||
- Fix rare PclZip/realpath/PHP version problem - @andrew-kzoo #261
|
||||
- `addHTML` encoding and ampersand fixes for PHP 5.3 - @bskrtich #270
|
||||
- Page breaks on titles and tables - @ivanlanin #274
|
||||
|
|
@ -43,7 +58,6 @@ This release added form fields (textinput, checkbox, and dropdown), drawing shap
|
|||
- `<th>` tag is closed with `</td>` tag: - @franzholz #438
|
||||
|
||||
### Deprecated
|
||||
|
||||
- `Element\Link::getTarget()` replaced by `Element\Link::getSource()`
|
||||
- `Element\Section::getSettings()` and `Element\Section::setSettings()` replaced by `Element\Section::getStyle()` and `Element\Section::setStyle()`
|
||||
- `Shared\Drawing` and `Shared\Font` merged into `Shared\Converter`
|
||||
|
|
@ -52,7 +66,6 @@ This release added form fields (textinput, checkbox, and dropdown), drawing shap
|
|||
- `PhpWord->loadTemplate($filename)`
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- Docs: Add known issue on `README` about requirement for temporary folder to be writable and update `samples/index.php` for this requirement check - @ivanlanin #238
|
||||
- Docs: Correct elements.rst about Line - @chrissharkman #292
|
||||
- PclZip: Remove temporary file after used - @andrew-kzoo #265
|
||||
|
|
@ -63,18 +76,23 @@ This release added form fields (textinput, checkbox, and dropdown), drawing shap
|
|||
- Renamed `Template` into `TemplateProcessor` - @RomanSyroeshko #216
|
||||
- Reverted #51. All text escaping must be performed out of the library - @RomanSyroeshko #51
|
||||
|
||||
## 0.11.1 - 2 June 2014
|
||||
|
||||
|
||||
0.11.1 (2 June 2014)
|
||||
--------------------
|
||||
|
||||
This is an immediate bugfix release for HTML reader.
|
||||
|
||||
- HTML Reader: `<p>` and header tags puts no output - @canyildiz @ivanlanin #257
|
||||
|
||||
## 0.11.0 - 1 June 2014
|
||||
|
||||
|
||||
0.11.0 (1 June 2014)
|
||||
--------------------
|
||||
|
||||
This release marked the change of PHPWord license from LGPL 2.1 to LGPL 3. Four new elements were added: TextBox, ListItemRun, Field, and Line. Relative and absolute positioning for images and textboxes were added. Writer classes were refactored into parts, elements, and styles. ODT and RTF features were enhanced. Ability to add elements to PHPWord object via HTML were implemented. RTF and HTML reader were initiated.
|
||||
|
||||
### Features
|
||||
|
||||
- Image: Ability to define relative and absolute positioning - @basjan #217
|
||||
- Footer: Conform footer with header by adding firstPage, evenPage and by inheritance - @basjan @ivanlanin #219
|
||||
- Element: New `TextBox` element - @basjan @ivanlanin #228, #229, #231
|
||||
|
|
@ -104,7 +122,6 @@ This release marked the change of PHPWord license from LGPL 2.1 to LGPL 3. Four
|
|||
- RTF Writer: Basic table writing - @ivanlanin #245
|
||||
|
||||
### Bugfixes
|
||||
|
||||
- Header: All images added to the second header were assigned to the first header - @basjan #222
|
||||
- Conversion: Fix conversion from cm to pixel, pixel to cm, and pixel to point - @basjan #233, #234
|
||||
- PageBreak: Page break adds new line in the beginning of the new page - @ivanlanin #150
|
||||
|
|
@ -112,7 +129,6 @@ This release marked the change of PHPWord license from LGPL 2.1 to LGPL 3. Four
|
|||
- Title: Orphan `w:fldChar` caused OpenOffice to crash when opening DOCX - @ivanlanin #236
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Static classes `Footnotes`, `Endnotes`, and `TOC`
|
||||
- `Writer\Word2007\Part`: `Numbering::writeNumbering()`, `Settings::writeSettings()`, `WebSettings::writeWebSettings()`, `ContentTypes::writeContentTypes()`, `Styles::writeStyles()`, `Document::writeDocument()` all changed into `write()`
|
||||
- `Writer\Word2007\Part\DocProps`: Split into `Writer\Word2007\Part\DocPropsCore` and `Writer\Word2007\Part\DocPropsApp`
|
||||
|
|
@ -120,7 +136,6 @@ This release marked the change of PHPWord license from LGPL 2.1 to LGPL 3. Four
|
|||
- `Writer\HTML::writeDocument`: Replaced by `Writer\HTML::getContent`
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- License: Change the project license from LGPL 2.1 into LGPL 3.0 - #211
|
||||
- Word2007 Writer: New `Style\Image` class - @ivanlanin
|
||||
- Refactor: Replace static classes `Footnotes`, `Endnotes`, and `TOC` with `Collections` - @ivanlanin #206
|
||||
|
|
@ -136,18 +151,22 @@ This release marked the change of PHPWord license from LGPL 2.1 to LGPL 3. Four
|
|||
- QA: Improve dan update requirement check in `samples` folder - @ivanlanin
|
||||
|
||||
|
||||
## 0.10.1 - 21 May 2014
|
||||
|
||||
0.10.1 (21 May 2014)
|
||||
--------------------
|
||||
|
||||
This is a bugfix release for `php-zip` requirement in Composer.
|
||||
|
||||
- Change Composer requirements for php-zip from `require` to `suggest` - @bskrtich #246
|
||||
|
||||
## 0.10.0 - 4 May 2014
|
||||
|
||||
|
||||
0.10.0 (4 May 2014)
|
||||
-------------------
|
||||
|
||||
This release marked heavy refactorings on internal code structure with the creation of some abstract classes to reduce code duplication. `Element` subnamespace is introduced in this release to replace `Section`. Word2007 reader capability is greatly enhanced. Endnote is introduced. List numbering is now customizable. Basic HTML and PDF writing support is enabled. Basic ODText reader is introduced.
|
||||
|
||||
### Features
|
||||
|
||||
- Image: Get image dimensions without EXIF extension - @andrew-kzoo #184
|
||||
- Table: Add `tblGrid` element for Libre/Open Office table sizing - @gianis6 #183
|
||||
- Footnote: Ability to insert textbreak in footnote `$footnote->addTextBreak()` - @ivanlanin
|
||||
|
|
@ -187,12 +206,10 @@ This release marked heavy refactorings on internal code structure with the creat
|
|||
- Paragraph: Ability to define first line and right indentation - @ivanlanin
|
||||
|
||||
### Bugfixes
|
||||
|
||||
- Footnote: Footnote content doesn't show footnote reference number - @ivanlanin #170
|
||||
- Documentation: Error in a function - @theBeerNut #195
|
||||
|
||||
### Deprecated
|
||||
|
||||
- `createTextRun` replaced by `addTextRun`
|
||||
- `createFootnote` replaced by `addFootnote`
|
||||
- `createHeader` replaced by `addHeader`
|
||||
|
|
@ -208,7 +225,6 @@ This release marked heavy refactorings on internal code structure with the creat
|
|||
- `Style\Cell::getDefaultBorderColor`
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- Documentation: Simplify page level docblock - @ivanlanin #179
|
||||
- Writer: Refactor writer classes and create a new `Write\AbstractWriter` abstract class - @ivanlanin #160
|
||||
- General: Refactor folders: `Element` and `Exception` - @ivanlanin #187
|
||||
|
|
@ -228,27 +244,30 @@ This release marked heavy refactorings on internal code structure with the creat
|
|||
- Refactor: Split `AbstractContainer` from `AbstractElement` - @ivanlanin
|
||||
- Refactor: Apply composite pattern for Word2007 reader - @ivanlanin
|
||||
|
||||
## 0.9.1 - 27 Mar 2014
|
||||
|
||||
|
||||
0.9.1 (27 Mar 2014)
|
||||
-------------------
|
||||
|
||||
This is a bugfix release for PSR-4 compatibility.
|
||||
|
||||
- Fixed PSR-4 composer autoloader - @AntonTyutin
|
||||
|
||||
## 0.9.0 - 26 Mar 2014
|
||||
|
||||
|
||||
0.9.0 (26 Mar 2014)
|
||||
-------------------
|
||||
|
||||
This release marked the transformation to namespaces (PHP 5.3+).
|
||||
|
||||
### Features
|
||||
|
||||
- Image: Ability to use remote or GD images using `addImage()` on sections, headers, footer, cells, and textruns - @ivanlanin
|
||||
- Header: Ability to use remote or GD images using `addWatermark()` - @ivanlanin
|
||||
|
||||
### Bugfixes
|
||||
|
||||
- Preserve text doesn't render correctly when the text is not the first word, e.g. 'Page {PAGE}' - @ivanlanin
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- Move documentation to [Read The Docs](http://phpword.readthedocs.org/en/develop/) - @Progi1984 @ivanlanin #82
|
||||
- Reorganize and redesign samples folder - @ivanlanin #137
|
||||
- Use `PhpOffice\PhpWord` namespace for PSR compliance - @RomanSyroeshko @gabrielbull #159, #58
|
||||
|
|
@ -257,18 +276,23 @@ This release marked the transformation to namespaces (PHP 5.3+).
|
|||
- Merge Style\TableFull into Style\Table. Style\TableFull is deprecated - @ivanlanin #160
|
||||
- Merge Section\MemoryImage into Section\Image. Section\Image is deprecated - @ivanlanin #160
|
||||
|
||||
## 0.8.1 - 17 Mar 2014
|
||||
|
||||
|
||||
0.8.1 (17 Mar 2014)
|
||||
-------------------
|
||||
|
||||
This is a bugfix release for image detection functionality.
|
||||
|
||||
- Added fallback for computers that do not have exif_imagetype - @bskrtich, @gabrielbull
|
||||
|
||||
## 0.8.0 - 15 Mar 2014
|
||||
|
||||
|
||||
0.8.0 (15 Mar 2014)
|
||||
-------------------
|
||||
|
||||
This release merged a lot of improvements from the community. Unit tests introduced in this release and has reached 90% code coverage.
|
||||
|
||||
### Features
|
||||
|
||||
- Template: Permit to save a template generated as a file (PHPWord_Template::saveAs()) - @RomanSyroeshko #56, #57
|
||||
- Word2007: Support sections page numbering - @gabrielbull
|
||||
- Word2007: Added line height methods to mirror the line height settings in Word in the paragraph styling - @gabrielbull
|
||||
|
|
@ -300,7 +324,6 @@ This release merged a lot of improvements from the community. Unit tests introdu
|
|||
- TextBreak: Allow font and paragraph style for text break - @ivanlanin #18
|
||||
|
||||
### Bugfixes
|
||||
|
||||
- Fixed bug with cell styling - @gabrielbull
|
||||
- Fixed bug list items inside of cells - @gabrielbull
|
||||
- Adding a value that contains "&" in a template breaks it - @SiebelsTim #51
|
||||
|
|
@ -309,15 +332,16 @@ This release merged a lot of improvements from the community. Unit tests introdu
|
|||
- Footnote: Corrupt DOCX reported by MS Word when sections > 1 and not every sections have footnote - @ivanlanin #125
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- UnitTests - @Progi1984
|
||||
|
||||
## 0.7.0 - 28 Jan 2014
|
||||
|
||||
|
||||
0.7.0 (28 Jan 2014)
|
||||
-------------------
|
||||
|
||||
This is the first release after a long development hiatus in [CodePlex](https://phpword.codeplex.com/). This release initialized ODT and RTF Writer, along with some other new features for the existing Word2007 Writer, e.g. tab, multiple header, rowspan and colspan. [Composer](https://packagist.org/packages/phpoffice/phpword) and [Travis](https://travis-ci.org/PHPOffice/PHPWord) were added.
|
||||
|
||||
### Features
|
||||
|
||||
- Implement RTF Writer - @Progi1984 #1
|
||||
- Implement ODT Writer - @Progi1984 #2
|
||||
- Word2007: Add rowspan and colspan to cells - @kaystrobach
|
||||
|
|
@ -327,13 +351,11 @@ This is the first release after a long development hiatus in [CodePlex](https://
|
|||
- Added support for image wrapping style - @gabrielbull
|
||||
|
||||
### Bugfixes
|
||||
|
||||
- "Warning: Invalid error type specified in ...\PHPWord.php on line 226" is thrown when the specified template file is not found - @RomanSyroeshko #32
|
||||
- PHPWord_Shared_String.IsUTF8 returns FALSE for Cyrillic UTF-8 input - @RomanSyroeshko #34
|
||||
- Temporary files naming logic in PHPWord_Template can lead to a collision - @RomanSyroeshko #38
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- Add superscript/subscript styling in Excel2007 Writer - @MarkBaker
|
||||
- add indentation support to paragraphs - @deds
|
||||
- Support for Composer - @Progi1984 #27
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -29,7 +29,7 @@ $subsequent->addImage('resources/_mars.jpg', array('width' => 80, 'height' => 80
|
|||
|
||||
// Add footer
|
||||
$footer = $section->addFooter();
|
||||
$footer->addPreserveText(htmlspecialchars('Page {PAGE} of {NUMPAGES}.'), array('align' => 'center'));
|
||||
$footer->addPreserveText(htmlspecialchars('Page {PAGE} of {NUMPAGES}.'), null, array('align' => 'center'));
|
||||
$footer->addLink('http://google.com', htmlspecialchars('Direct Google'));
|
||||
|
||||
// Write some text
|
||||
|
|
|
|||
|
|
@ -25,73 +25,80 @@ use PhpOffice\PhpWord\Shared\ZipArchive;
|
|||
|
||||
class TemplateProcessor
|
||||
{
|
||||
const MAXIMUM_REPLACEMENTS_DEFAULT = -1;
|
||||
|
||||
/**
|
||||
* ZipArchive object.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
private $zipClass;
|
||||
protected $zipClass;
|
||||
|
||||
/**
|
||||
* @var string Temporary document filename (with path).
|
||||
*/
|
||||
private $temporaryDocumentFilename;
|
||||
protected $tempDocumentFilename;
|
||||
|
||||
/**
|
||||
* Content of main document part (in XML format) of the temporary document.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $temporaryDocumentMainPart;
|
||||
protected $tempDocumentMainPart;
|
||||
|
||||
/**
|
||||
* Content of headers (in XML format) of the temporary document.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $temporaryDocumentHeaders = array();
|
||||
protected $tempDocumentHeaders = array();
|
||||
|
||||
/**
|
||||
* Content of footers (in XML format) of the temporary document.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $temporaryDocumentFooters = array();
|
||||
protected $tempDocumentFooters = array();
|
||||
|
||||
/**
|
||||
* @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception.
|
||||
*
|
||||
* @param string $documentTemplate The fully qualified template filename.
|
||||
*
|
||||
* @throws \PhpOffice\PhpWord\Exception\CreateTemporaryFileException
|
||||
* @throws \PhpOffice\PhpWord\Exception\CopyFileException
|
||||
*/
|
||||
public function __construct($documentTemplate)
|
||||
{
|
||||
// Temporary document filename initialization
|
||||
$this->temporaryDocumentFilename = tempnam(Settings::getTempDir(), 'PhpWord');
|
||||
if (false === $this->temporaryDocumentFilename) {
|
||||
$this->tempDocumentFilename = tempnam(Settings::getTempDir(), 'PhpWord');
|
||||
if (false === $this->tempDocumentFilename) {
|
||||
throw new CreateTemporaryFileException();
|
||||
}
|
||||
|
||||
// Template file cloning
|
||||
if (false === copy($documentTemplate, $this->temporaryDocumentFilename)) {
|
||||
throw new CopyFileException($documentTemplate, $this->temporaryDocumentFilename);
|
||||
if (false === copy($documentTemplate, $this->tempDocumentFilename)) {
|
||||
throw new CopyFileException($documentTemplate, $this->tempDocumentFilename);
|
||||
}
|
||||
|
||||
// Temporary document content extraction
|
||||
$this->zipClass = new ZipArchive();
|
||||
$this->zipClass->open($this->temporaryDocumentFilename);
|
||||
$this->zipClass->open($this->tempDocumentFilename);
|
||||
$index = 1;
|
||||
while ($this->zipClass->locateName($this->getHeaderName($index)) !== false) {
|
||||
$this->temporaryDocumentHeaders[$index] = $this->zipClass->getFromName($this->getHeaderName($index));
|
||||
while (false !== $this->zipClass->locateName($this->getHeaderName($index))) {
|
||||
$this->tempDocumentHeaders[$index] = $this->fixBrokenMacros(
|
||||
$this->zipClass->getFromName($this->getHeaderName($index))
|
||||
);
|
||||
$index++;
|
||||
}
|
||||
$index = 1;
|
||||
while ($this->zipClass->locateName($this->getFooterName($index)) !== false) {
|
||||
$this->temporaryDocumentFooters[$index] = $this->zipClass->getFromName($this->getFooterName($index));
|
||||
while (false !== $this->zipClass->locateName($this->getFooterName($index))) {
|
||||
$this->tempDocumentFooters[$index] = $this->fixBrokenMacros(
|
||||
$this->zipClass->getFromName($this->getFooterName($index))
|
||||
);
|
||||
$index++;
|
||||
}
|
||||
$this->temporaryDocumentMainPart = $this->zipClass->getFromName('word/document.xml');
|
||||
$this->tempDocumentMainPart = $this->fixBrokenMacros($this->zipClass->getFromName('word/document.xml'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -100,7 +107,9 @@ class TemplateProcessor
|
|||
* @param \DOMDocument $xslDOMDocument
|
||||
* @param array $xslOptions
|
||||
* @param string $xslOptionsURI
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \PhpOffice\PhpWord\Exception\Exception
|
||||
*/
|
||||
public function applyXslStyleSheet($xslDOMDocument, $xslOptions = array(), $xslOptionsURI = '')
|
||||
|
|
@ -114,7 +123,7 @@ class TemplateProcessor
|
|||
}
|
||||
|
||||
$xmlDOMDocument = new \DOMDocument();
|
||||
if (false === $xmlDOMDocument->loadXML($this->temporaryDocumentMainPart)) {
|
||||
if (false === $xmlDOMDocument->loadXML($this->tempDocumentMainPart)) {
|
||||
throw new Exception('Could not load XML from the given template.');
|
||||
}
|
||||
|
||||
|
|
@ -123,25 +132,26 @@ class TemplateProcessor
|
|||
throw new Exception('Could not transform the given XML document.');
|
||||
}
|
||||
|
||||
$this->temporaryDocumentMainPart = $xmlTransformed;
|
||||
$this->tempDocumentMainPart = $xmlTransformed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $search
|
||||
* @param mixed $macro
|
||||
* @param mixed $replace
|
||||
* @param integer $limit
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setValue($search, $replace, $limit = -1)
|
||||
public function setValue($macro, $replace, $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT)
|
||||
{
|
||||
foreach ($this->temporaryDocumentHeaders as $index => $headerXML) {
|
||||
$this->temporaryDocumentHeaders[$index] = $this->setValueForPart($this->temporaryDocumentHeaders[$index], $search, $replace, $limit);
|
||||
foreach ($this->tempDocumentHeaders as $index => $headerXML) {
|
||||
$this->tempDocumentHeaders[$index] = $this->setValueForPart($this->tempDocumentHeaders[$index], $macro, $replace, $limit);
|
||||
}
|
||||
|
||||
$this->temporaryDocumentMainPart = $this->setValueForPart($this->temporaryDocumentMainPart, $search, $replace, $limit);
|
||||
$this->tempDocumentMainPart = $this->setValueForPart($this->tempDocumentMainPart, $macro, $replace, $limit);
|
||||
|
||||
foreach ($this->temporaryDocumentFooters as $index => $headerXML) {
|
||||
$this->temporaryDocumentFooters[$index] = $this->setValueForPart($this->temporaryDocumentFooters[$index], $search, $replace, $limit);
|
||||
foreach ($this->tempDocumentFooters as $index => $headerXML) {
|
||||
$this->tempDocumentFooters[$index] = $this->setValueForPart($this->tempDocumentFooters[$index], $macro, $replace, $limit);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,13 +162,13 @@ class TemplateProcessor
|
|||
*/
|
||||
public function getVariables()
|
||||
{
|
||||
$variables = $this->getVariablesForPart($this->temporaryDocumentMainPart);
|
||||
$variables = $this->getVariablesForPart($this->tempDocumentMainPart);
|
||||
|
||||
foreach ($this->temporaryDocumentHeaders as $headerXML) {
|
||||
foreach ($this->tempDocumentHeaders as $headerXML) {
|
||||
$variables = array_merge($variables, $this->getVariablesForPart($headerXML));
|
||||
}
|
||||
|
||||
foreach ($this->temporaryDocumentFooters as $footerXML) {
|
||||
foreach ($this->tempDocumentFooters as $footerXML) {
|
||||
$variables = array_merge($variables, $this->getVariablesForPart($footerXML));
|
||||
}
|
||||
|
||||
|
|
@ -170,16 +180,18 @@ class TemplateProcessor
|
|||
*
|
||||
* @param string $search
|
||||
* @param integer $numberOfClones
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \PhpOffice\PhpWord\Exception\Exception
|
||||
*/
|
||||
public function cloneRow($search, $numberOfClones)
|
||||
{
|
||||
if (substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
|
||||
if ('${' !== substr($search, 0, 2) && '}' !== substr($search, -1)) {
|
||||
$search = '${' . $search . '}';
|
||||
}
|
||||
|
||||
$tagPos = strpos($this->temporaryDocumentMainPart, $search);
|
||||
$tagPos = strpos($this->tempDocumentMainPart, $search);
|
||||
if (!$tagPos) {
|
||||
throw new Exception("Can not clone row, template variable not found or variable contains markup.");
|
||||
}
|
||||
|
|
@ -219,7 +231,7 @@ class TemplateProcessor
|
|||
}
|
||||
$result .= $this->getSlice($rowEnd);
|
||||
|
||||
$this->temporaryDocumentMainPart = $result;
|
||||
$this->tempDocumentMainPart = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -228,6 +240,7 @@ class TemplateProcessor
|
|||
* @param string $blockname
|
||||
* @param integer $clones
|
||||
* @param boolean $replace
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function cloneBlock($blockname, $clones = 1, $replace = true)
|
||||
|
|
@ -235,7 +248,7 @@ class TemplateProcessor
|
|||
$xmlBlock = null;
|
||||
preg_match(
|
||||
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
|
||||
$this->temporaryDocumentMainPart,
|
||||
$this->tempDocumentMainPart,
|
||||
$matches
|
||||
);
|
||||
|
||||
|
|
@ -247,10 +260,10 @@ class TemplateProcessor
|
|||
}
|
||||
|
||||
if ($replace) {
|
||||
$this->temporaryDocumentMainPart = str_replace(
|
||||
$this->tempDocumentMainPart = str_replace(
|
||||
$matches[2] . $matches[3] . $matches[4],
|
||||
implode('', $cloned),
|
||||
$this->temporaryDocumentMainPart
|
||||
$this->tempDocumentMainPart
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -263,21 +276,22 @@ class TemplateProcessor
|
|||
*
|
||||
* @param string $blockname
|
||||
* @param string $replacement
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function replaceBlock($blockname, $replacement)
|
||||
{
|
||||
preg_match(
|
||||
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
|
||||
$this->temporaryDocumentMainPart,
|
||||
$this->tempDocumentMainPart,
|
||||
$matches
|
||||
);
|
||||
|
||||
if (isset($matches[3])) {
|
||||
$this->temporaryDocumentMainPart = str_replace(
|
||||
$this->tempDocumentMainPart = str_replace(
|
||||
$matches[2] . $matches[3] . $matches[4],
|
||||
$replacement,
|
||||
$this->temporaryDocumentMainPart
|
||||
$this->tempDocumentMainPart
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -286,6 +300,7 @@ class TemplateProcessor
|
|||
* Delete a block of text.
|
||||
*
|
||||
* @param string $blockname
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteBlock($blockname)
|
||||
|
|
@ -297,18 +312,19 @@ class TemplateProcessor
|
|||
* Saves the result document.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \PhpOffice\PhpWord\Exception\Exception
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
foreach ($this->temporaryDocumentHeaders as $index => $headerXML) {
|
||||
$this->zipClass->addFromString($this->getHeaderName($index), $this->temporaryDocumentHeaders[$index]);
|
||||
foreach ($this->tempDocumentHeaders as $index => $headerXML) {
|
||||
$this->zipClass->addFromString($this->getHeaderName($index), $this->tempDocumentHeaders[$index]);
|
||||
}
|
||||
|
||||
$this->zipClass->addFromString('word/document.xml', $this->temporaryDocumentMainPart);
|
||||
$this->zipClass->addFromString('word/document.xml', $this->tempDocumentMainPart);
|
||||
|
||||
foreach ($this->temporaryDocumentFooters as $index => $headerXML) {
|
||||
$this->zipClass->addFromString($this->getFooterName($index), $this->temporaryDocumentFooters[$index]);
|
||||
foreach ($this->tempDocumentFooters as $index => $headerXML) {
|
||||
$this->zipClass->addFromString($this->getFooterName($index), $this->tempDocumentFooters[$index]);
|
||||
}
|
||||
|
||||
// Close zip file
|
||||
|
|
@ -316,7 +332,7 @@ class TemplateProcessor
|
|||
throw new Exception('Could not close zip file.');
|
||||
}
|
||||
|
||||
return $this->temporaryDocumentFilename;
|
||||
return $this->tempDocumentFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -325,6 +341,7 @@ class TemplateProcessor
|
|||
* @since 0.8.0
|
||||
*
|
||||
* @param string $fileName
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function saveAs($fileName)
|
||||
|
|
@ -335,28 +352,53 @@ class TemplateProcessor
|
|||
unlink($fileName);
|
||||
}
|
||||
|
||||
rename($tempFileName, $fileName);
|
||||
/*
|
||||
* Note: we do not use ``rename`` function here, because it looses file ownership data on Windows platform.
|
||||
* As a result, user cannot open the file directly getting "Access denied" message.
|
||||
*
|
||||
* @see https://github.com/PHPOffice/PHPWord/issues/532
|
||||
*/
|
||||
copy($tempFileName, $fileName);
|
||||
unlink($tempFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and replace placeholders in the given XML section.
|
||||
* Finds parts of broken macros and sticks them together.
|
||||
* Macros, while being edited, could be implicitly broken by some of the word processors.
|
||||
*
|
||||
* @since 0.13.0
|
||||
*
|
||||
* @param string $documentPart The document part in XML representation.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function fixBrokenMacros($documentPart)
|
||||
{
|
||||
$fixedDocumentPart = $documentPart;
|
||||
|
||||
$fixedDocumentPart = preg_replace_callback(
|
||||
'|\$\{([^\}]+)\}|U',
|
||||
function ($match) {
|
||||
return strip_tags($match[0]);
|
||||
},
|
||||
$fixedDocumentPart
|
||||
);
|
||||
|
||||
return $fixedDocumentPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and replace macros in the given XML section.
|
||||
*
|
||||
* @param string $documentPartXML
|
||||
* @param string $search
|
||||
* @param string $replace
|
||||
* @param integer $limit
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function setValueForPart($documentPartXML, $search, $replace, $limit)
|
||||
{
|
||||
$pattern = '|\$\{([^\}]+)\}|U';
|
||||
preg_match_all($pattern, $documentPartXML, $matches);
|
||||
foreach ($matches[0] as $value) {
|
||||
$valueCleaned = preg_replace('/<[^>]+>/', '', $value);
|
||||
$valueCleaned = preg_replace('/<\/[^>]+>/', '', $valueCleaned);
|
||||
$documentPartXML = str_replace($value, $valueCleaned, $documentPartXML);
|
||||
}
|
||||
|
||||
if (substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
|
||||
$search = '${' . $search . '}';
|
||||
}
|
||||
|
|
@ -365,15 +407,21 @@ class TemplateProcessor
|
|||
$replace = utf8_encode($replace);
|
||||
}
|
||||
|
||||
// Note: we can't use the same function for both cases here, because of performance considerations.
|
||||
if (self::MAXIMUM_REPLACEMENTS_DEFAULT === $limit) {
|
||||
return str_replace($search, $replace, $documentPartXML);
|
||||
} else {
|
||||
$regExpDelim = '/';
|
||||
$escapedSearch = preg_quote($search, $regExpDelim);
|
||||
return preg_replace("{$regExpDelim}{$escapedSearch}{$regExpDelim}u", $replace, $documentPartXML, $limit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all variables in $documentPartXML.
|
||||
*
|
||||
* @param string $documentPartXML
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getVariablesForPart($documentPartXML)
|
||||
|
|
@ -387,9 +435,10 @@ class TemplateProcessor
|
|||
* Get the name of the footer file for $index.
|
||||
*
|
||||
* @param integer $index
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getFooterName($index)
|
||||
protected function getFooterName($index)
|
||||
{
|
||||
return sprintf('word/footer%d.xml', $index);
|
||||
}
|
||||
|
|
@ -398,9 +447,10 @@ class TemplateProcessor
|
|||
* Get the name of the header file for $index.
|
||||
*
|
||||
* @param integer $index
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getHeaderName($index)
|
||||
protected function getHeaderName($index)
|
||||
{
|
||||
return sprintf('word/header%d.xml', $index);
|
||||
}
|
||||
|
|
@ -409,15 +459,17 @@ class TemplateProcessor
|
|||
* Find the start position of the nearest table row before $offset.
|
||||
*
|
||||
* @param integer $offset
|
||||
*
|
||||
* @return integer
|
||||
*
|
||||
* @throws \PhpOffice\PhpWord\Exception\Exception
|
||||
*/
|
||||
private function findRowStart($offset)
|
||||
protected function findRowStart($offset)
|
||||
{
|
||||
$rowStart = strrpos($this->temporaryDocumentMainPart, '<w:tr ', ((strlen($this->temporaryDocumentMainPart) - $offset) * -1));
|
||||
$rowStart = strrpos($this->tempDocumentMainPart, '<w:tr ', ((strlen($this->tempDocumentMainPart) - $offset) * -1));
|
||||
|
||||
if (!$rowStart) {
|
||||
$rowStart = strrpos($this->temporaryDocumentMainPart, '<w:tr>', ((strlen($this->temporaryDocumentMainPart) - $offset) * -1));
|
||||
$rowStart = strrpos($this->tempDocumentMainPart, '<w:tr>', ((strlen($this->tempDocumentMainPart) - $offset) * -1));
|
||||
}
|
||||
if (!$rowStart) {
|
||||
throw new Exception('Can not find the start position of the row to clone.');
|
||||
|
|
@ -430,11 +482,12 @@ class TemplateProcessor
|
|||
* Find the end position of the nearest table row after $offset.
|
||||
*
|
||||
* @param integer $offset
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
private function findRowEnd($offset)
|
||||
protected function findRowEnd($offset)
|
||||
{
|
||||
return strpos($this->temporaryDocumentMainPart, '</w:tr>', $offset) + 7;
|
||||
return strpos($this->tempDocumentMainPart, '</w:tr>', $offset) + 7;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -442,14 +495,15 @@ class TemplateProcessor
|
|||
*
|
||||
* @param integer $startPosition
|
||||
* @param integer $endPosition
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getSlice($startPosition, $endPosition = 0)
|
||||
protected function getSlice($startPosition, $endPosition = 0)
|
||||
{
|
||||
if (!$endPosition) {
|
||||
$endPosition = strlen($this->temporaryDocumentMainPart);
|
||||
$endPosition = strlen($this->tempDocumentMainPart);
|
||||
}
|
||||
|
||||
return substr($this->temporaryDocumentMainPart, $startPosition, ($endPosition - $startPosition));
|
||||
return substr($this->tempDocumentMainPart, $startPosition, ($endPosition - $startPosition));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@ class PreserveTextTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
$oPreserveText = new PreserveText(
|
||||
'text',
|
||||
array('align' => 'center'),
|
||||
array('marginLeft' => 600, 'marginRight' => 600, 'marginTop' => 600, 'marginBottom' => 600)
|
||||
array('size' => 16, 'color' => '1B2232'),
|
||||
array('align' => 'center')
|
||||
);
|
||||
$this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Font', $oPreserveText->getFontStyle());
|
||||
$this->assertInstanceOf(
|
||||
|
|
|
|||
|
|
@ -51,14 +51,14 @@ final class TemplateProcessorTest extends \PHPUnit_Framework_TestCase
|
|||
$templateZip = new \ZipArchive();
|
||||
$templateZip->open($templateFqfn);
|
||||
$templateXml = $templateZip->getFromName('word/document.xml');
|
||||
if ($templateZip->close() === false) {
|
||||
if (false === $templateZip->close()) {
|
||||
throw new \Exception("Could not close zip file \"{$templateZip}\".");
|
||||
}
|
||||
|
||||
$documentZip = new \ZipArchive();
|
||||
$documentZip->open($documentFqfn);
|
||||
$documentXml = $documentZip->getFromName('word/document.xml');
|
||||
if ($documentZip->close() === false) {
|
||||
if (false === $documentZip->close()) {
|
||||
throw new \Exception("Could not close zip file \"{$documentZip}\".");
|
||||
}
|
||||
|
||||
|
|
@ -78,19 +78,19 @@ final class TemplateProcessorTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
final public function testXslStyleSheetCanBeApplied($actualDocumentFqfn)
|
||||
{
|
||||
$expectedDocumentFqfn = __DIR__ . "/_files/documents/without_table_macros.docx";
|
||||
$expectedDocumentFqfn = __DIR__ . '/_files/documents/without_table_macros.docx';
|
||||
|
||||
$actualDocumentZip = new \ZipArchive();
|
||||
$actualDocumentZip->open($actualDocumentFqfn);
|
||||
$actualDocumentXml = $actualDocumentZip->getFromName('word/document.xml');
|
||||
if ($actualDocumentZip->close() === false) {
|
||||
if (false === $actualDocumentZip->close()) {
|
||||
throw new \Exception("Could not close zip file \"{$actualDocumentFqfn}\".");
|
||||
}
|
||||
|
||||
$expectedDocumentZip = new \ZipArchive();
|
||||
$expectedDocumentZip->open($expectedDocumentFqfn);
|
||||
$expectedDocumentXml = $expectedDocumentZip->getFromName('word/document.xml');
|
||||
if ($expectedDocumentZip->close() === false) {
|
||||
if (false === $expectedDocumentZip->close()) {
|
||||
throw new \Exception("Could not close zip file \"{$expectedDocumentFqfn}\".");
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ final class TemplateProcessorTest extends \PHPUnit_Framework_TestCase
|
|||
* We have to use error control below, because \XSLTProcessor::setParameter omits warning on failure.
|
||||
* This warning fails the test.
|
||||
*/
|
||||
@$templateProcessor->applyXslStyleSheet($xslDOMDocument, array(1 => 'somevalue'));
|
||||
@$templateProcessor->applyXslStyleSheet($xslDOMDocument, array(1 => htmlspecialchars('somevalue', ENT_COMPAT, 'UTF-8')));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -157,9 +157,9 @@ final class TemplateProcessorTest extends \PHPUnit_Framework_TestCase
|
|||
);
|
||||
|
||||
$docName = 'clone-test-result.docx';
|
||||
$templateProcessor->setValue('tableHeader', utf8_decode('ééé'));
|
||||
$templateProcessor->setValue('tableHeader', utf8_decode(htmlspecialchars('ééé', ENT_COMPAT, 'UTF-8')));
|
||||
$templateProcessor->cloneRow('userId', 1);
|
||||
$templateProcessor->setValue('userId#1', 'Test');
|
||||
$templateProcessor->setValue('userId#1', htmlspecialchars('Test', ENT_COMPAT, 'UTF-8'));
|
||||
$templateProcessor->saveAs($docName);
|
||||
$docFound = file_exists($docName);
|
||||
unlink($docName);
|
||||
|
|
@ -171,7 +171,7 @@ final class TemplateProcessorTest extends \PHPUnit_Framework_TestCase
|
|||
* @covers ::saveAs
|
||||
* @test
|
||||
*/
|
||||
public function testVariablesCanBeReplacedInHeaderAndFooter()
|
||||
public function testMacrosCanBeReplacedInHeaderAndFooter()
|
||||
{
|
||||
$templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/header-footer.docx');
|
||||
|
||||
|
|
@ -181,9 +181,9 @@ final class TemplateProcessorTest extends \PHPUnit_Framework_TestCase
|
|||
);
|
||||
|
||||
$docName = 'header-footer-test-result.docx';
|
||||
$templateProcessor->setValue('headerValue', 'Header Value');
|
||||
$templateProcessor->setValue('documentContent', 'Document text.');
|
||||
$templateProcessor->setValue('footerValue', 'Footer Value');
|
||||
$templateProcessor->setValue('headerValue', htmlspecialchars('Header Value', ENT_COMPAT, 'UTF-8'));
|
||||
$templateProcessor->setValue('documentContent', htmlspecialchars('Document text.', ENT_COMPAT, 'UTF-8'));
|
||||
$templateProcessor->setValue('footerValue', htmlspecialchars('Footer Value', ENT_COMPAT, 'UTF-8'));
|
||||
$templateProcessor->saveAs($docName);
|
||||
$docFound = file_exists($docName);
|
||||
unlink($docName);
|
||||
|
|
|
|||
Loading…
Reference in New Issue