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
DJ_F4a
diff --git a/tests/PhpWord/_includes/XmlDocument.php b/tests/PhpWord/_includes/XmlDocument.php
index c82c5a8e..21a12105 100644
--- a/tests/PhpWord/_includes/XmlDocument.php
+++ b/tests/PhpWord/_includes/XmlDocument.php
@@ -97,6 +97,7 @@ class XmlDocument
if (null === $this->xpath) {
$this->xpath = new \DOMXpath($this->dom);
+ $this->xpath->registerNamespace('w14', 'http://schemas.microsoft.com/office/word/2010/wordml');
}
return $this->xpath->query($path);
From dc7cb1ee75b2edcc3ab1c19671f3b245ec353e3f Mon Sep 17 00:00:00 2001
From: troosan
Date: Wed, 13 Dec 2017 22:48:58 +0100
Subject: [PATCH 055/176] update changelog & doc
---
CHANGELOG.md | 1 +
src/PhpWord/Element/Comment.php | 1 +
src/PhpWord/Element/TrackChange.php | 1 +
src/PhpWord/Reader/Word2007/AbstractPart.php | 4 ++--
4 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7dc8b458..51f92434 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -42,6 +42,7 @@ This is the last version to support PHP 5.3
- Padded the $args array to remove error - @kaigoh #1150, @reformed #870
- Fix incorrect image size between windows and mac - @bskrtich #874
- Fix adding HTML table to document - @mogilvie @arivanbastos #324
+- Fix parsing on/off values (w:val="true|false|1|0|on|off") - @troosan #1221 #1219
### Deprecated
- PhpWord->getProtection(), get it from the settings instead PhpWord->getSettings()->getDocumentProtection();
diff --git a/src/PhpWord/Element/Comment.php b/src/PhpWord/Element/Comment.php
index 908b8785..18836929 100644
--- a/src/PhpWord/Element/Comment.php
+++ b/src/PhpWord/Element/Comment.php
@@ -19,6 +19,7 @@ namespace PhpOffice\PhpWord\Element;
/**
* Comment element
+ * @see http://datypic.com/sc/ooxml/t-w_CT_Comment.html
*/
class Comment extends TrackChange
{
diff --git a/src/PhpWord/Element/TrackChange.php b/src/PhpWord/Element/TrackChange.php
index 9ed623f9..d14fc201 100644
--- a/src/PhpWord/Element/TrackChange.php
+++ b/src/PhpWord/Element/TrackChange.php
@@ -19,6 +19,7 @@ namespace PhpOffice\PhpWord\Element;
/**
* TrackChange element
+ * @see http://datypic.com/sc/ooxml/t-w_CT_TrackChange.html
*/
class TrackChange extends AbstractContainer
{
diff --git a/src/PhpWord/Reader/Word2007/AbstractPart.php b/src/PhpWord/Reader/Word2007/AbstractPart.php
index 4b7f6e0a..6a48fd46 100644
--- a/src/PhpWord/Reader/Word2007/AbstractPart.php
+++ b/src/PhpWord/Reader/Word2007/AbstractPart.php
@@ -384,7 +384,7 @@ abstract class AbstractPart
{
$style = null;
$margins = array('top', 'left', 'bottom', 'right');
- $borders = $margins + array('insideH', 'insideV');
+ $borders = array_merge($margins, array('insideH', 'insideV'));
if ($xmlReader->elementExists('w:tblPr', $domNode)) {
if ($xmlReader->elementExists('w:tblPr/w:tblStyle', $domNode)) {
@@ -422,7 +422,7 @@ abstract class AbstractPart
'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'),
+ 'bgColor' => array(self::READ_VALUE, 'w:shd', 'w:fill'),
);
return $this->readStyleDefs($xmlReader, $domNode, $styleDefs);
From 9e029415cc3eafad7581346b8ed84e05d7674b08 Mon Sep 17 00:00:00 2001
From: troosan
Date: Wed, 13 Dec 2017 23:17:01 +0100
Subject: [PATCH 056/176] align with pull request submitted in PHPOffice/Commom
---
src/PhpWord/Metadata/Protection.php | 23 ++++---
.../Shared/Microsoft/PasswordEncoder.php | 68 ++++++++++++-------
src/PhpWord/Writer/Word2007/Part/Settings.php | 2 +-
.../Shared/Microsoft/PasswordEncoderTest.php | 6 +-
4 files changed, 58 insertions(+), 41 deletions(-)
diff --git a/src/PhpWord/Metadata/Protection.php b/src/PhpWord/Metadata/Protection.php
index 09d08aac..bb1cc1ad 100644
--- a/src/PhpWord/Metadata/Protection.php
+++ b/src/PhpWord/Metadata/Protection.php
@@ -18,6 +18,7 @@
namespace PhpOffice\PhpWord\Metadata;
use PhpOffice\PhpWord\SimpleType\DocProtect;
+use PhpOffice\PhpWord\Shared\Microsoft\PasswordEncoder;
/**
* Document protection class
@@ -50,11 +51,11 @@ class Protection
private $spinCount = 100000;
/**
- * Cryptographic Hashing Algorithm (see to \PhpOffice\PhpWord\Writer\Word2007\Part\Settings::$algorithmMapping)
+ * Cryptographic Hashing Algorithm (see constants defined in \PhpOffice\PhpWord\Shared\Microsoft\PasswordEncoder)
*
- * @var int
+ * @var string
*/
- private $mswordAlgorithmSid = 4;
+ private $algorithm = PasswordEncoder::ALGORITHM_SHA_1;
/**
* Salt for Password Verifier
@@ -146,24 +147,24 @@ class Protection
}
/**
- * Get algorithm-sid
+ * Get algorithm
*
- * @return int
+ * @return string
*/
- public function getMswordAlgorithmSid()
+ public function getAlgorithm()
{
- return $this->mswordAlgorithmSid;
+ return $this->algorithm;
}
/**
- * Set algorithm-sid (see \PhpOffice\PhpWord\Writer\Word2007\Part\Settings::$algorithmMapping)
+ * Set algorithm
*
- * @param $mswordAlgorithmSid
+ * @param $algorithm
* @return self
*/
- public function setMswordAlgorithmSid($mswordAlgorithmSid)
+ public function setMswordAlgorithmSid($algorithm)
{
- $this->mswordAlgorithmSid = $mswordAlgorithmSid;
+ $this->algorithm = $algorithm;
return $this;
}
diff --git a/src/PhpWord/Shared/Microsoft/PasswordEncoder.php b/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
index cddcfcd3..a3ba345c 100644
--- a/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
+++ b/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
@@ -22,21 +22,36 @@ namespace PhpOffice\PhpWord\Shared\Microsoft;
*/
class PasswordEncoder
{
+ const ALGORITHM_MD2 = 'MD2';
+ const ALGORITHM_MD4 = 'MD4';
+ const ALGORITHM_MD5 = 'MD5';
+ const ALGORITHM_SHA_1 = 'SHA-1';
+ const ALGORITHM_SHA_256 = 'SHA-256';
+ const ALGORITHM_SHA_384 = 'SHA-384';
+ const ALGORITHM_SHA_512 = 'SHA-512';
+ const ALGORITHM_RIPEMD = 'RIPEMD';
+ const ALGORITHM_RIPEMD_160 = 'RIPEMD-160';
+ const ALGORITHM_MAC = 'MAC';
+ const ALGORITHM_HMAC= 'HMAC';
+
+ /**
+ * Mapping between algorithm name and algorithm ID
+ *
+ * @var array
+ * @see https://msdn.microsoft.com/en-us/library/documentformat.openxml.wordprocessing.writeprotection.cryptographicalgorithmsid(v=office.14).aspx
+ */
private static $algorithmMapping = array(
- 1 => 'md2',
- 2 => 'md4',
- 3 => 'md5',
- 4 => 'sha1',
- 5 => '', // 'mac' -> not possible with hash()
- 6 => 'ripemd',
- 7 => 'ripemd160',
- 8 => '',
- 9 => '', //'hmac' -> not possible with hash()
- 10 => '',
- 11 => '',
- 12 => 'sha256',
- 13 => 'sha384',
- 14 => 'sha512',
+ self::ALGORITHM_MD2 => array(1, 'md2'),
+ self::ALGORITHM_MD4 => array(2, 'md4'),
+ self::ALGORITHM_MD5 => array(3, 'md5'),
+ self::ALGORITHM_SHA_1 => array(4, 'sha1'),
+ self::ALGORITHM_MAC => array(5, ''), // 'mac' -> not possible with hash()
+ self::ALGORITHM_RIPEMD => array(6, 'ripemd'),
+ self::ALGORITHM_RIPEMD_160 => array(7, 'ripemd160'),
+ self::ALGORITHM_HMAC => array(9, ''), //'hmac' -> not possible with hash()
+ self::ALGORITHM_SHA_256 => array(12, 'sha256'),
+ self::ALGORITHM_SHA_384 => array(13, 'sha384'),
+ self::ALGORITHM_SHA_512 => array(14, 'sha512'),
);
private static $initialCodeArray = array(
@@ -82,12 +97,12 @@ class PasswordEncoder
* @see https://blogs.msdn.microsoft.com/vsod/2010/04/05/how-to-set-the-editing-restrictions-in-word-using-open-xml-sdk-2-0/
*
* @param string $password
- * @param number $algorithmSid
+ * @param string $algorithmName
* @param string $salt
- * @param number $spinCount
+ * @param integer $spinCount
* @return string
*/
- public static function hashPassword($password, $algorithmSid = 4, $salt = null, $spinCount = 10000)
+ public static function hashPassword($password, $algorithmName = PasswordEncoder::ALGORITHM_SHA_1, $salt = null, $spinCount = 10000)
{
$origEncoding = mb_internal_encoding();
mb_internal_encoding('UTF-8');
@@ -118,7 +133,7 @@ class PasswordEncoder
// Implementation Notes List:
// Word requires that the initial hash of the password with the salt not be considered in the count.
// The initial hash of salt + key is not included in the iteration count.
- $algorithm = self::getAlgorithm($algorithmSid);
+ $algorithm = self::getAlgorithm($algorithmName);
$generatedKey = hash($algorithm, $salt . $generatedKey, true);
for ($i = 0; $i < $spinCount; $i++) {
@@ -134,12 +149,12 @@ class PasswordEncoder
/**
* Get algorithm from self::$algorithmMapping
*
- * @param int $sid
+ * @param string $algorithmName
* @return string
*/
- private static function getAlgorithm($sid)
+ private static function getAlgorithm($algorithmName)
{
- $algorithm = self::$algorithmMapping[$sid];
+ $algorithm = self::$algorithmMapping[$algorithmName][1];
if ($algorithm == '') {
$algorithm = 'sha1';
}
@@ -155,16 +170,17 @@ class PasswordEncoder
*/
private static function buildCombinedKey($byteChars)
{
+ $byteCharsLength = count($byteChars);
// Compute the high-order word
// Initialize from the initial code array (see above), depending on the passwords length.
- $highOrderWord = self::$initialCodeArray[count($byteChars) - 1];
+ $highOrderWord = self::$initialCodeArray[$byteCharsLength - 1];
// For each character in the password:
// For every bit in the character, starting with the least significant and progressing to (but excluding)
// the most significant, if the bit is set, XOR the key’s high-order word with the corresponding word from
// the Encryption Matrix
- for ($i = 0; $i < count($byteChars); $i++) {
- $tmp = self::$passwordMaxLength - count($byteChars) + $i;
+ for ($i = 0; $i < $byteCharsLength; $i++) {
+ $tmp = self::$passwordMaxLength - $byteCharsLength + $i;
$matrixRow = self::$encryptionMatrix[$tmp];
for ($intBit = 0; $intBit < 7; $intBit++) {
if (($byteChars[$i] & (0x0001 << $intBit)) != 0) {
@@ -177,12 +193,12 @@ class PasswordEncoder
// Initialize with 0
$lowOrderWord = 0;
// For each character in the password, going backwards
- for ($i = count($byteChars) - 1; $i >= 0; $i--) {
+ for ($i = $byteCharsLength - 1; $i >= 0; $i--) {
// low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR character
$lowOrderWord = (((($lowOrderWord >> 14) & 0x0001) | (($lowOrderWord << 1) & 0x7FFF)) ^ $byteChars[$i]);
}
// Lastly, low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR strPassword length XOR 0xCE4B.
- $lowOrderWord = (((($lowOrderWord >> 14) & 0x0001) | (($lowOrderWord << 1) & 0x7FFF)) ^ count($byteChars) ^ 0xCE4B);
+ $lowOrderWord = (((($lowOrderWord >> 14) & 0x0001) | (($lowOrderWord << 1) & 0x7FFF)) ^ $byteCharsLength ^ 0xCE4B);
// Combine the Low and High Order Word
return self::int32(($highOrderWord << 16) + $lowOrderWord);
diff --git a/src/PhpWord/Writer/Word2007/Part/Settings.php b/src/PhpWord/Writer/Word2007/Part/Settings.php
index 565aab2c..f292583e 100644
--- a/src/PhpWord/Writer/Word2007/Part/Settings.php
+++ b/src/PhpWord/Writer/Word2007/Part/Settings.php
@@ -193,7 +193,7 @@ class Settings extends AbstractPart
if ($documentProtection->getSalt() == null) {
$documentProtection->setSalt(openssl_random_pseudo_bytes(16));
}
- $passwordHash = PasswordEncoder::hashPassword($documentProtection->getPassword(), $documentProtection->getMswordAlgorithmSid(), $documentProtection->getSalt(), $documentProtection->getSpinCount());
+ $passwordHash = PasswordEncoder::hashPassword($documentProtection->getPassword(), $documentProtection->getAlgorithm(), $documentProtection->getSalt(), $documentProtection->getSpinCount());
$this->settings['w:documentProtection'] = array(
'@attributes' => array(
'w:enforcement' => 1,
diff --git a/tests/PhpWord/Shared/Microsoft/PasswordEncoderTest.php b/tests/PhpWord/Shared/Microsoft/PasswordEncoderTest.php
index 7b2bd3e7..c42a6eb4 100644
--- a/tests/PhpWord/Shared/Microsoft/PasswordEncoderTest.php
+++ b/tests/PhpWord/Shared/Microsoft/PasswordEncoderTest.php
@@ -51,7 +51,7 @@ class PasswordEncoderTest extends \PHPUnit\Framework\TestCase
$salt = base64_decode('uq81pJRRGFIY5U+E9gt8tA==');
//when
- $hashPassword = PasswordEncoder::hashPassword($password, 4, $salt);
+ $hashPassword = PasswordEncoder::hashPassword($password, PasswordEncoder::ALGORITHM_SHA_1, $salt);
//then
TestCase::assertEquals('QiDOcpia1YzSVJPiKPwWebl9p/0=', $hashPassword);
@@ -67,7 +67,7 @@ class PasswordEncoderTest extends \PHPUnit\Framework\TestCase
$salt = base64_decode('uq81pJRRGFIY5U+E9gt8tA==');
//when
- $hashPassword = PasswordEncoder::hashPassword($password, 5, $salt);
+ $hashPassword = PasswordEncoder::hashPassword($password, PasswordEncoder::ALGORITHM_MAC, $salt);
//then
TestCase::assertEquals('QiDOcpia1YzSVJPiKPwWebl9p/0=', $hashPassword);
@@ -83,7 +83,7 @@ class PasswordEncoderTest extends \PHPUnit\Framework\TestCase
$salt = base64_decode('uq81pJRRGFIY5U+E9gt8tA==');
//when
- $hashPassword = PasswordEncoder::hashPassword($password, 5, $salt, 1);
+ $hashPassword = PasswordEncoder::hashPassword($password, PasswordEncoder::ALGORITHM_MAC, $salt, 1);
//then
TestCase::assertEquals('rDV9sgdDsztoCQlvRCb1lF2wxNg=', $hashPassword);
From f7d2ad7201bc91f79253a6e0fdbcdaf0154679d5 Mon Sep 17 00:00:00 2001
From: troosan
Date: Wed, 13 Dec 2017 23:24:37 +0100
Subject: [PATCH 057/176] formatting
---
src/PhpWord/Metadata/Protection.php | 2 +-
.../Shared/Microsoft/PasswordEncoder.php | 26 +++++++++----------
2 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/src/PhpWord/Metadata/Protection.php b/src/PhpWord/Metadata/Protection.php
index bb1cc1ad..634751fb 100644
--- a/src/PhpWord/Metadata/Protection.php
+++ b/src/PhpWord/Metadata/Protection.php
@@ -17,8 +17,8 @@
namespace PhpOffice\PhpWord\Metadata;
-use PhpOffice\PhpWord\SimpleType\DocProtect;
use PhpOffice\PhpWord\Shared\Microsoft\PasswordEncoder;
+use PhpOffice\PhpWord\SimpleType\DocProtect;
/**
* Document protection class
diff --git a/src/PhpWord/Shared/Microsoft/PasswordEncoder.php b/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
index a3ba345c..a6a607a1 100644
--- a/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
+++ b/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
@@ -32,7 +32,7 @@ class PasswordEncoder
const ALGORITHM_RIPEMD = 'RIPEMD';
const ALGORITHM_RIPEMD_160 = 'RIPEMD-160';
const ALGORITHM_MAC = 'MAC';
- const ALGORITHM_HMAC= 'HMAC';
+ const ALGORITHM_HMAC = 'HMAC';
/**
* Mapping between algorithm name and algorithm ID
@@ -41,17 +41,17 @@ class PasswordEncoder
* @see https://msdn.microsoft.com/en-us/library/documentformat.openxml.wordprocessing.writeprotection.cryptographicalgorithmsid(v=office.14).aspx
*/
private static $algorithmMapping = array(
- self::ALGORITHM_MD2 => array(1, 'md2'),
- self::ALGORITHM_MD4 => array(2, 'md4'),
- self::ALGORITHM_MD5 => array(3, 'md5'),
- self::ALGORITHM_SHA_1 => array(4, 'sha1'),
- self::ALGORITHM_MAC => array(5, ''), // 'mac' -> not possible with hash()
- self::ALGORITHM_RIPEMD => array(6, 'ripemd'),
+ self::ALGORITHM_MD2 => array(1, 'md2'),
+ self::ALGORITHM_MD4 => array(2, 'md4'),
+ self::ALGORITHM_MD5 => array(3, 'md5'),
+ self::ALGORITHM_SHA_1 => array(4, 'sha1'),
+ self::ALGORITHM_MAC => array(5, ''), // 'mac' -> not possible with hash()
+ self::ALGORITHM_RIPEMD => array(6, 'ripemd'),
self::ALGORITHM_RIPEMD_160 => array(7, 'ripemd160'),
- self::ALGORITHM_HMAC => array(9, ''), //'hmac' -> not possible with hash()
- self::ALGORITHM_SHA_256 => array(12, 'sha256'),
- self::ALGORITHM_SHA_384 => array(13, 'sha384'),
- self::ALGORITHM_SHA_512 => array(14, 'sha512'),
+ self::ALGORITHM_HMAC => array(9, ''), //'hmac' -> not possible with hash()
+ self::ALGORITHM_SHA_256 => array(12, 'sha256'),
+ self::ALGORITHM_SHA_384 => array(13, 'sha384'),
+ self::ALGORITHM_SHA_512 => array(14, 'sha512'),
);
private static $initialCodeArray = array(
@@ -99,10 +99,10 @@ class PasswordEncoder
* @param string $password
* @param string $algorithmName
* @param string $salt
- * @param integer $spinCount
+ * @param int $spinCount
* @return string
*/
- public static function hashPassword($password, $algorithmName = PasswordEncoder::ALGORITHM_SHA_1, $salt = null, $spinCount = 10000)
+ public static function hashPassword($password, $algorithmName = self::ALGORITHM_SHA_1, $salt = null, $spinCount = 10000)
{
$origEncoding = mb_internal_encoding();
mb_internal_encoding('UTF-8');
From 5a57409df028bb609f9f180424c0a0f489334b6f Mon Sep 17 00:00:00 2001
From: troosan
Date: Wed, 13 Dec 2017 23:55:48 +0100
Subject: [PATCH 058/176] fix tests
---
src/PhpWord/Metadata/Protection.php | 2 +-
src/PhpWord/Shared/Microsoft/PasswordEncoder.php | 11 +++++++++++
src/PhpWord/Writer/Word2007/Part/Settings.php | 4 ++--
tests/PhpWord/Writer/Word2007/Part/SettingsTest.php | 3 ++-
4 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/src/PhpWord/Metadata/Protection.php b/src/PhpWord/Metadata/Protection.php
index 634751fb..35391cb2 100644
--- a/src/PhpWord/Metadata/Protection.php
+++ b/src/PhpWord/Metadata/Protection.php
@@ -162,7 +162,7 @@ class Protection
* @param $algorithm
* @return self
*/
- public function setMswordAlgorithmSid($algorithm)
+ public function setAlgorithm($algorithm)
{
$this->algorithm = $algorithm;
diff --git a/src/PhpWord/Shared/Microsoft/PasswordEncoder.php b/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
index a6a607a1..d3a03d97 100644
--- a/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
+++ b/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
@@ -162,6 +162,17 @@ class PasswordEncoder
return $algorithm;
}
+ /**
+ * Returns the algorithm ID
+ *
+ * @param sting $algorithmName
+ * @return int
+ */
+ public static function getAlgorithmId($algorithmName)
+ {
+ return self::$algorithmMapping[$algorithmName][0];
+ }
+
/**
* Build combined key from low-order word and high-order word
*
diff --git a/src/PhpWord/Writer/Word2007/Part/Settings.php b/src/PhpWord/Writer/Word2007/Part/Settings.php
index 6ac5ec4a..e56e2612 100644
--- a/src/PhpWord/Writer/Word2007/Part/Settings.php
+++ b/src/PhpWord/Writer/Word2007/Part/Settings.php
@@ -183,7 +183,7 @@ class Settings extends AbstractPart
private function setDocumentProtection($documentProtection)
{
if ($documentProtection->getEditing() !== null) {
- if (empty($documentProtection->getPassword())) {
+ if ($documentProtection->getPassword() == null) {
$this->settings['w:documentProtection'] = array(
'@attributes' => array(
'w:enforcement' => 1,
@@ -202,7 +202,7 @@ class Settings extends AbstractPart
'w:cryptProviderType' => 'rsaFull',
'w:cryptAlgorithmClass' => 'hash',
'w:cryptAlgorithmType' => 'typeAny',
- 'w:cryptAlgorithmSid' => $documentProtection->getMswordAlgorithmSid(),
+ 'w:cryptAlgorithmSid' => PasswordEncoder::getAlgorithmId($documentProtection->getAlgorithm()),
'w:cryptSpinCount' => $documentProtection->getSpinCount(),
'w:hash' => $passwordHash,
'w:salt' => base64_encode($documentProtection->getSalt()),
diff --git a/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php b/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php
index 7a355042..1e6af567 100644
--- a/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php
+++ b/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php
@@ -24,6 +24,7 @@ use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\SimpleType\Zoom;
use PhpOffice\PhpWord\Style\Language;
use PhpOffice\PhpWord\TestHelperDOCX;
+use PhpOffice\PhpWord\Shared\Microsoft\PasswordEncoder;
/**
* Test class for PhpOffice\PhpWord\Writer\Word2007\Part\Settings
@@ -65,7 +66,7 @@ class SettingsTest extends \PHPUnit\Framework\TestCase
$phpWord->getSettings()->getDocumentProtection()->setEditing('readOnly');
$phpWord->getSettings()->getDocumentProtection()->setPassword('testÄö@€!$&');
$phpWord->getSettings()->getDocumentProtection()->setSalt(base64_decode('uq81pJRRGFIY5U+E9gt8tA=='));
- $phpWord->getSettings()->getDocumentProtection()->setMswordAlgorithmSid(1);
+ $phpWord->getSettings()->getDocumentProtection()->setAlgorithm(PasswordEncoder::ALGORITHM_MD2);
$phpWord->getSettings()->getDocumentProtection()->setSpinCount(10);
$doc = TestHelperDOCX::getDocument($phpWord);
From 5d5362a3fda20d3e79c089510a19e696d836cf63 Mon Sep 17 00:00:00 2001
From: troosan
Date: Thu, 14 Dec 2017 00:15:23 +0100
Subject: [PATCH 059/176] sort imports
---
tests/PhpWord/Writer/Word2007/Part/SettingsTest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php b/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php
index 1e6af567..50b444b8 100644
--- a/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php
+++ b/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php
@@ -21,10 +21,10 @@ use PhpOffice\PhpWord\ComplexType\ProofState;
use PhpOffice\PhpWord\ComplexType\TrackChangesView;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Settings;
+use PhpOffice\PhpWord\Shared\Microsoft\PasswordEncoder;
use PhpOffice\PhpWord\SimpleType\Zoom;
use PhpOffice\PhpWord\Style\Language;
use PhpOffice\PhpWord\TestHelperDOCX;
-use PhpOffice\PhpWord\Shared\Microsoft\PasswordEncoder;
/**
* Test class for PhpOffice\PhpWord\Writer\Word2007\Part\Settings
From 87acd3764bb0a3ce435475cca9b3a123d8df944f Mon Sep 17 00:00:00 2001
From: Gabriel Caruso
Date: Thu, 14 Dec 2017 12:21:16 -0200
Subject: [PATCH 060/176] Clean elses
---
src/PhpWord/Shared/OLERead.php | 40 ++++++++++++------------
src/PhpWord/Shared/PCLZip/pclzip.lib.php | 20 ++++++------
2 files changed, 30 insertions(+), 30 deletions(-)
diff --git a/src/PhpWord/Shared/OLERead.php b/src/PhpWord/Shared/OLERead.php
index 1321b8da..bcdda0c3 100644
--- a/src/PhpWord/Shared/OLERead.php
+++ b/src/PhpWord/Shared/OLERead.php
@@ -115,7 +115,7 @@ class OLERead
$bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS)/4;
}
// @codeCoverageIgnoreEnd
-
+
for ($i = 0; $i < $bbdBlocks; ++$i) {
$bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos);
$pos += 4;
@@ -192,27 +192,27 @@ class OLERead
$block = self::getInt4d($this->smallBlockChain, $block*4);
}
- return $streamData;
- } else {
- $numBlocks = $this->props[$stream]['size'] / self::BIG_BLOCK_SIZE;
- if ($this->props[$stream]['size'] % self::BIG_BLOCK_SIZE != 0) {
- ++$numBlocks;
- }
-
- if ($numBlocks == 0) {
- return '';// @codeCoverageIgnore
- }
-
- $block = $this->props[$stream]['startBlock'];
-
- while ($block != -2) {
- $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
- $streamData .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
- $block = self::getInt4d($this->bigBlockChain, $block*4);
- }
-
return $streamData;
}
+
+ $numBlocks = $this->props[$stream]['size'] / self::BIG_BLOCK_SIZE;
+ if ($this->props[$stream]['size'] % self::BIG_BLOCK_SIZE != 0) {
+ ++$numBlocks;
+ }
+
+ if ($numBlocks == 0) {
+ return '';// @codeCoverageIgnore
+ }
+
+ $block = $this->props[$stream]['startBlock'];
+
+ while ($block != -2) {
+ $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
+ $streamData .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
+ $block = self::getInt4d($this->bigBlockChain, $block*4);
+ }
+
+ return $streamData;
}
/**
diff --git a/src/PhpWord/Shared/PCLZip/pclzip.lib.php b/src/PhpWord/Shared/PCLZip/pclzip.lib.php
index 5620c754..3fbc9327 100644
--- a/src/PhpWord/Shared/PCLZip/pclzip.lib.php
+++ b/src/PhpWord/Shared/PCLZip/pclzip.lib.php
@@ -1244,9 +1244,9 @@ class PclZip
{
if (PCLZIP_ERROR_EXTERNAL == 1) {
return (PclErrorCode());
- } else {
- return ($this->error_code);
}
+
+ return ($this->error_code);
}
// --------------------------------------------------------------------------------
@@ -1289,9 +1289,9 @@ class PclZip
if ($p_with_code) {
return ($v_value . ' (' . $this->error_code . ')');
- } else {
- return ($v_value);
}
+
+ return ($v_value);
}
// --------------------------------------------------------------------------------
@@ -1304,13 +1304,13 @@ class PclZip
{
if (PCLZIP_ERROR_EXTERNAL == 1) {
return (PclErrorString());
- } else {
- if ($p_full) {
- return ($this->errorName(true) . " : " . $this->error_string);
- } else {
- return ($this->error_string . " [code " . $this->error_code . "]");
- }
}
+
+ if ($p_full) {
+ return ($this->errorName(true) . " : " . $this->error_string);
+ }
+
+ return ($this->error_string . " [code " . $this->error_code . "]");
}
// --------------------------------------------------------------------------------
From 46a037ebd0ace99f10052d19aa6511a85b471f69 Mon Sep 17 00:00:00 2001
From: troosan
Date: Mon, 18 Dec 2017 17:02:34 +0100
Subject: [PATCH 061/176] add composer scripts
---
composer.json | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/composer.json b/composer.json
index 70b60b46..41730374 100644
--- a/composer.json
+++ b/composer.json
@@ -34,12 +34,22 @@
"name": "Antoine de Troostembergh"
}
],
+ "scripts": {
+ "check": [
+ "./vendor/bin/php-cs-fixer fix --ansi --dry-run --diff",
+ "./vendor/bin/phpcs --report-width=200 --report-summary --report-full samples/ src/ tests/ --ignore=src/PhpWord/Shared/PCLZip --standard=PSR2 -n",
+ "./vendor/bin/phpmd src/,tests/ text ./phpmd.xml.dist --exclude pclzip.lib.php",
+ "./vendor/bin/phpunit --color=always"
+ ],
+ "fix": [
+ "./vendor/bin/php-cs-fixer fix --ansi"
+ ]
+ },
"require": {
"php": ">=5.3.3",
"ext-xml": "*",
"zendframework/zend-escaper": "^2.2",
- "zendframework/zend-stdlib": "^2.2 || ^3.0",
- "phpoffice/common": "^0.2"
+ "zendframework/zend-stdlib": "^2.2 || ^3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.36",
From 7908491ba3e24f170da5aea61fbf9b85c6c21abb Mon Sep 17 00:00:00 2001
From: troosan
Date: Tue, 19 Dec 2017 22:14:52 +0100
Subject: [PATCH 062/176] revert mistakenly deleted line
---
composer.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/composer.json b/composer.json
index 41730374..2774ad98 100644
--- a/composer.json
+++ b/composer.json
@@ -49,7 +49,8 @@
"php": ">=5.3.3",
"ext-xml": "*",
"zendframework/zend-escaper": "^2.2",
- "zendframework/zend-stdlib": "^2.2 || ^3.0"
+ "zendframework/zend-stdlib": "^2.2 || ^3.0",
+ "phpoffice/common": "^0.2"
},
"require-dev": {
"phpunit/phpunit": "^4.8.36",
From 3e6745f14697d74b74e8d3ab9af670c8fad2ee3f Mon Sep 17 00:00:00 2001
From: SRG Group
Date: Thu, 21 Dec 2017 00:03:52 +0100
Subject: [PATCH 063/176] HTML image support & TextRun paragraph style (#934)
* Adding setParagraphStyle to Textrun for indentation
* Html Image support added
* fix formatting, add tests & update changelog
---
CHANGELOG.md | 3 ++
src/PhpWord/Element/TextRun.php | 24 +++++++++-
src/PhpWord/Shared/Html.php | 58 +++++++++++++++++++++++
tests/PhpWord/Element/ListItemRunTest.php | 4 +-
tests/PhpWord/Element/TextRunTest.php | 33 ++++++++++++-
tests/PhpWord/Shared/HtmlTest.php | 19 ++++++++
6 files changed, 136 insertions(+), 5 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 51f92434..ae1618a2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,8 @@ This is the last version to support PHP 5.3
- Implement PageBreak for odt writer @cookiekiller #863 #824
- Allow to force an update of all fields on opening a document - @troosan #951
- Allow adding a CheckBox in a TextRun - @irond #727
+- Add support for HTML img tag - @srggroup #934
+- Add support for password protection for docx - @mariahaubner #1019
### Fixed
- Loosen dependency to Zend
@@ -43,6 +45,7 @@ This is the last version to support PHP 5.3
- Fix incorrect image size between windows and mac - @bskrtich #874
- Fix adding HTML table to document - @mogilvie @arivanbastos #324
- Fix parsing on/off values (w:val="true|false|1|0|on|off") - @troosan #1221 #1219
+- Fix error on Empty Dropdown Entry - @ComputerTinker #592
### Deprecated
- PhpWord->getProtection(), get it from the settings instead PhpWord->getSettings()->getDocumentProtection();
diff --git a/src/PhpWord/Element/TextRun.php b/src/PhpWord/Element/TextRun.php
index d8a898b4..6d9ae9f4 100644
--- a/src/PhpWord/Element/TextRun.php
+++ b/src/PhpWord/Element/TextRun.php
@@ -43,7 +43,7 @@ class TextRun extends AbstractContainer
*/
public function __construct($paragraphStyle = null)
{
- $this->paragraphStyle = $this->setNewStyle(new Paragraph(), $paragraphStyle);
+ $this->paragraphStyle = $this->setParagraphStyle($paragraphStyle);
}
/**
@@ -55,4 +55,26 @@ class TextRun extends AbstractContainer
{
return $this->paragraphStyle;
}
+
+ /**
+ * Set Paragraph style
+ *
+ * @param string|array|\PhpOffice\PhpWord\Style\Paragraph $style
+ * @return string|\PhpOffice\PhpWord\Style\Paragraph
+ */
+ public function setParagraphStyle($style = null)
+ {
+ if (is_array($style)) {
+ $this->paragraphStyle = new Paragraph();
+ $this->paragraphStyle->setStyleByArray($style);
+ } elseif ($style instanceof Paragraph) {
+ $this->paragraphStyle = $style;
+ } elseif (null === $style) {
+ $this->paragraphStyle = new Paragraph();
+ } else {
+ $this->paragraphStyle = $style;
+ }
+
+ return $this->paragraphStyle;
+ }
}
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 739bfb16..8310e515 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -136,6 +136,7 @@ class Html
'ul' => array('List', null, null, $styles, $data, 3, null),
'ol' => array('List', null, null, $styles, $data, 7, null),
'li' => array('ListItem', $node, $element, $styles, $data, null, null),
+ 'img' => array('Image', $node, $element, $styles, null, null, null),
'br' => array('LineBreak', null, $element, $styles, null, null, null),
);
@@ -506,6 +507,63 @@ class Html
return $styles;
}
+ /**
+ * Parse image node
+ *
+ * @param \DOMNode $node
+ * @param \PhpOffice\PhpWord\Element\AbstractContainer $element
+ *
+ * @return \PhpOffice\PhpWord\Element\Image
+ **/
+ private static function parseImage($node, $element)
+ {
+ $style = array();
+ foreach ($node->attributes as $attribute) {
+ switch ($attribute->name) {
+ case 'src':
+ $src = $attribute->value;
+ break;
+ case 'width':
+ $width = $attribute->value;
+ $style['width'] = $width;
+ break;
+ case 'height':
+ $height = $attribute->value;
+ $style['height'] = $height;
+ break;
+ case 'style':
+ $styleattr = explode(';', $attribute->value);
+ foreach ($styleattr as $attr) {
+ if (strpos($attr, ':')) {
+ list($k, $v) = explode(':', $attr);
+ switch ($k) {
+ case 'float':
+ if (trim($v) == 'right') {
+ $style['hPos'] = \PhpOffice\PhpWord\Style\Image::POS_RIGHT;
+ $style['hPosRelTo'] = \PhpOffice\PhpWord\Style\Image::POS_RELTO_PAGE;
+ $style['pos'] = \PhpOffice\PhpWord\Style\Image::POS_RELATIVE;
+ $style['wrap'] = \PhpOffice\PhpWord\Style\Image::WRAP_TIGHT;
+ $style['overlap'] = true;
+ }
+ if (trim($v) == 'left') {
+ $style['hPos'] = \PhpOffice\PhpWord\Style\Image::POS_LEFT;
+ $style['hPosRelTo'] = \PhpOffice\PhpWord\Style\Image::POS_RELTO_PAGE;
+ $style['pos'] = \PhpOffice\PhpWord\Style\Image::POS_RELATIVE;
+ $style['wrap'] = \PhpOffice\PhpWord\Style\Image::WRAP_TIGHT;
+ $style['overlap'] = true;
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ $newElement = $element->addImage($src, $style);
+
+ return $newElement;
+ }
+
/**
* Transforms a CSS border style into a word border style
*
diff --git a/tests/PhpWord/Element/ListItemRunTest.php b/tests/PhpWord/Element/ListItemRunTest.php
index 999756ba..84beec02 100644
--- a/tests/PhpWord/Element/ListItemRunTest.php
+++ b/tests/PhpWord/Element/ListItemRunTest.php
@@ -27,13 +27,13 @@ class ListItemRunTest extends \PHPUnit\Framework\TestCase
/**
* New instance
*/
- public function testConstructNull()
+ public function testConstruct()
{
$oListItemRun = new ListItemRun();
$this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\ListItemRun', $oListItemRun);
$this->assertCount(0, $oListItemRun->getElements());
- $this->assertNull($oListItemRun->getParagraphStyle());
+ $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Paragraph', $oListItemRun->getParagraphStyle());
}
/**
diff --git a/tests/PhpWord/Element/TextRunTest.php b/tests/PhpWord/Element/TextRunTest.php
index 27f5af6b..59b8b89f 100644
--- a/tests/PhpWord/Element/TextRunTest.php
+++ b/tests/PhpWord/Element/TextRunTest.php
@@ -18,6 +18,8 @@
namespace PhpOffice\PhpWord\Element;
use PhpOffice\PhpWord\PhpWord;
+use PhpOffice\PhpWord\SimpleType\Jc;
+use PhpOffice\PhpWord\Style\Paragraph;
/**
* Test class for PhpOffice\PhpWord\Element\TextRun
@@ -29,13 +31,13 @@ class TextRunTest extends \PHPUnit\Framework\TestCase
/**
* New instance
*/
- public function testConstructNull()
+ public function testConstruct()
{
$oTextRun = new TextRun();
$this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\TextRun', $oTextRun);
$this->assertCount(0, $oTextRun->getElements());
- $this->assertNull($oTextRun->getParagraphStyle());
+ $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Paragraph', $oTextRun->getParagraphStyle());
}
/**
@@ -62,6 +64,21 @@ class TextRunTest extends \PHPUnit\Framework\TestCase
$this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Paragraph', $oTextRun->getParagraphStyle());
}
+ /**
+ * New instance with object
+ */
+ public function testConstructObject()
+ {
+ $oParagraphStyle = new Paragraph();
+ $oParagraphStyle->setAlignment(Jc::BOTH);
+ $oTextRun = new TextRun($oParagraphStyle);
+
+ $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\TextRun', $oTextRun);
+ $this->assertCount(0, $oTextRun->getElements());
+ $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Paragraph', $oTextRun->getParagraphStyle());
+ $this->assertEquals(Jc::BOTH, $oTextRun->getParagraphStyle()->getAlignment());
+ }
+
/**
* Add text
*/
@@ -152,4 +169,16 @@ class TextRunTest extends \PHPUnit\Framework\TestCase
$this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Footnote', $element);
$this->assertCount(1, $oTextRun->getElements());
}
+
+ /**
+ * Get paragraph style
+ */
+ public function testParagraph()
+ {
+ $oText = new TextRun('paragraphStyle');
+ $this->assertEquals('paragraphStyle', $oText->getParagraphStyle());
+
+ $oText->setParagraphStyle(array('alignment' => Jc::CENTER, 'spaceAfter' => 100));
+ $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Paragraph', $oText->getParagraphStyle());
+ }
}
diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php
index bfe24c58..d168c09e 100644
--- a/tests/PhpWord/Shared/HtmlTest.php
+++ b/tests/PhpWord/Shared/HtmlTest.php
@@ -252,4 +252,23 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
$this->assertEquals('This is some text', $doc->getElement('/w:document/w:body/w:p/w:r[1]/w:t')->nodeValue);
$this->assertEquals('with a linebreak.', $doc->getElement('/w:document/w:body/w:p/w:r[2]/w:t')->nodeValue);
}
+
+ public function testParseImage()
+ {
+ $src = __DIR__ . '/../_files/images/firefox.png';
+
+ $phpWord = new \PhpOffice\PhpWord\PhpWord();
+ $section = $phpWord->addSection();
+ $html = '

';
+ Html::addHtml($section, $html);
+
+ $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
+
+ $baseXpath = '/w:document/w:body/w:p/w:r';
+ $this->assertTrue($doc->elementExists($baseXpath . '/w:pict/v:shape'));
+ $this->assertStringMatchesFormat('%Swidth:150pt%S', $doc->getElementAttribute($baseXpath . '[1]/w:pict/v:shape', 'style'));
+ $this->assertStringMatchesFormat('%Sheight:200pt%S', $doc->getElementAttribute($baseXpath . '[1]/w:pict/v:shape', 'style'));
+ $this->assertStringMatchesFormat('%Smso-position-horizontal:right%S', $doc->getElementAttribute($baseXpath . '[1]/w:pict/v:shape', 'style'));
+ $this->assertStringMatchesFormat('%Smso-position-horizontal:left%S', $doc->getElementAttribute($baseXpath . '[2]/w:pict/v:shape', 'style'));
+ }
}
From ed704da5b2fcf76f8c0dca4b9836ae3bcda65604 Mon Sep 17 00:00:00 2001
From: troosan
Date: Mon, 25 Dec 2017 01:24:20 +0100
Subject: [PATCH 064/176] set release date
---
CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ae1618a2..47567dfc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,7 @@ Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
-v0.14.0 (?? Dec 2017)
+v0.14.0 (28 Dec 2017)
----------------------
This release fixes several bugs and adds some new features.
This is the last version to support PHP 5.3
From 56720df4875df75ed42ed07f4e3bdfc851cccce1 Mon Sep 17 00:00:00 2001
From: troosan
Date: Mon, 25 Dec 2017 01:32:34 +0100
Subject: [PATCH 065/176] update version
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 85808888..ac5f3b95 100644
--- a/README.md
+++ b/README.md
@@ -82,7 +82,7 @@ You can of course also manually edit your composer.json file
```json
{
"require": {
- "phpoffice/phpword": "v0.13.*"
+ "phpoffice/phpword": "v0.14.*"
}
}
```
From 7250b15e74ff91c72d103ca40ae12f4524a0bf76 Mon Sep 17 00:00:00 2001
From: troosan
Date: Mon, 25 Dec 2017 08:33:02 +0100
Subject: [PATCH 066/176] Title can be added in Cell
---
src/PhpWord/Element/AbstractContainer.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/PhpWord/Element/AbstractContainer.php b/src/PhpWord/Element/AbstractContainer.php
index 0de0cbce..e3022b50 100644
--- a/src/PhpWord/Element/AbstractContainer.php
+++ b/src/PhpWord/Element/AbstractContainer.php
@@ -209,7 +209,7 @@ abstract class AbstractContainer extends AbstractElement
'Footnote' => array('Section', 'TextRun', 'Cell'),
'Endnote' => array('Section', 'TextRun', 'Cell'),
'PreserveText' => array('Section', 'Header', 'Footer', 'Cell'),
- 'Title' => array('Section'),
+ 'Title' => array('Section', 'Cell'),
'TOC' => array('Section'),
'PageBreak' => array('Section'),
'Chart' => array('Section', 'Cell'),
From 512cf952aeaaa9badcce3c0eb88114f6beaeeb22 Mon Sep 17 00:00:00 2001
From: troosan
Date: Mon, 25 Dec 2017 20:42:37 +0100
Subject: [PATCH 067/176] randomise temp directory name to avoid collisions
---
src/PhpWord/Writer/AbstractWriter.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/PhpWord/Writer/AbstractWriter.php b/src/PhpWord/Writer/AbstractWriter.php
index 50a0cad3..2be03b06 100644
--- a/src/PhpWord/Writer/AbstractWriter.php
+++ b/src/PhpWord/Writer/AbstractWriter.php
@@ -216,7 +216,7 @@ abstract class AbstractWriter implements WriterInterface
protected function getTempFile($filename)
{
// Temporary directory
- $this->setTempDir(Settings::getTempDir() . '/PHPWordWriter/');
+ $this->setTempDir(Settings::getTempDir() . uniqid('/PHPWordWriter_'). '/');
// Temporary file
$this->originalFilename = $filename;
From fce1bf28c870132bef8c0f604477e14b2fa54185 Mon Sep 17 00:00:00 2001
From: troosan
Date: Mon, 25 Dec 2017 22:05:46 +0100
Subject: [PATCH 068/176] format code
---
src/PhpWord/Writer/AbstractWriter.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/PhpWord/Writer/AbstractWriter.php b/src/PhpWord/Writer/AbstractWriter.php
index 2be03b06..884769d7 100644
--- a/src/PhpWord/Writer/AbstractWriter.php
+++ b/src/PhpWord/Writer/AbstractWriter.php
@@ -216,7 +216,7 @@ abstract class AbstractWriter implements WriterInterface
protected function getTempFile($filename)
{
// Temporary directory
- $this->setTempDir(Settings::getTempDir() . uniqid('/PHPWordWriter_'). '/');
+ $this->setTempDir(Settings::getTempDir() . uniqid('/PHPWordWriter_') . '/');
// Temporary file
$this->originalFilename = $filename;
From b614497ae6dd44280be1c2dda56772198bcd25ae Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 29 Dec 2017 02:30:53 +0100
Subject: [PATCH 069/176] fix dependencies to have 7.1 compatible build (#1228)
* add assertions in test methods without assertions
* loosen dependencies so 7.0 & 7.1 builds can succeed
* fix some scrutinizer errors
* update release date
---
.scrutinizer.yml | 2 +-
.travis.yml | 4 +-
CHANGELOG.md | 4 +-
composer.json | 9 ++--
docs/ISSUE_TEMPLATE.md | 17 +++++--
docs/PULL_REQUEST_TEMPLATE.md | 7 ++-
run_tests.sh | 20 --------
src/PhpWord/Element/AbstractElement.php | 12 ++---
src/PhpWord/Element/Field.php | 2 +-
src/PhpWord/Element/TrackChange.php | 2 -
src/PhpWord/Metadata/Protection.php | 2 +-
src/PhpWord/Reader/Word2007/Settings.php | 2 +-
src/PhpWord/Shared/Converter.php | 38 +++++++--------
src/PhpWord/Shared/Html.php | 1 +
.../Shared/Microsoft/PasswordEncoder.php | 2 +-
src/PhpWord/Shared/ZipArchive.php | 2 +-
src/PhpWord/Style/Paper.php | 8 ++--
src/PhpWord/Writer/PDF/MPDF.php | 24 +++++++---
src/PhpWord/Writer/Word2007/Part/Comments.php | 4 +-
tests/PhpWord/Shared/ZipArchiveTest.php | 46 +++++++++----------
tests/PhpWord/Writer/ODTextTest.php | 1 +
tests/PhpWord/Writer/RTFTest.php | 1 +
.../Writer/Word2007/Part/DocumentTest.php | 3 +-
23 files changed, 105 insertions(+), 108 deletions(-)
delete mode 100755 run_tests.sh
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
index 6f982d8e..c8fe57cf 100644
--- a/.scrutinizer.yml
+++ b/.scrutinizer.yml
@@ -15,7 +15,7 @@ tools:
ruleset: phpmd.xml.dist
external_code_coverage:
enabled: true
- timeout: 900
+ timeout: 1200
php_cpd: true
# php_sim: # Temporarily disabled to allow focus on things other than duplicates
# min_mass: 40
diff --git a/.travis.yml b/.travis.yml
index 0ec84081..d63b7bb2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,14 +9,14 @@ php:
- 5.6
- 7.0
- 7.1
+ - 7.2
matrix:
include:
- php: 5.6
env: COVERAGE=1
allow_failures:
- - php: 7.0
- - php: 7.1
+ - php: 7.2
cache:
directories:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 47567dfc..93945189 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,10 +3,10 @@ Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
-v0.14.0 (28 Dec 2017)
+v0.14.0 (29 Dec 2017)
----------------------
This release fixes several bugs and adds some new features.
-This is the last version to support PHP 5.3
+This version brings compatibility with PHP 7.0 & 7.1
### Added
- Possibility to control the footnote numbering - @troosan #1068
diff --git a/composer.json b/composer.json
index 2774ad98..3cc4b131 100644
--- a/composer.json
+++ b/composer.json
@@ -46,23 +46,22 @@
]
},
"require": {
- "php": ">=5.3.3",
+ "php": "^5.3.3 || ^7.0",
"ext-xml": "*",
"zendframework/zend-escaper": "^2.2",
"zendframework/zend-stdlib": "^2.2 || ^3.0",
"phpoffice/common": "^0.2"
},
"require-dev": {
- "phpunit/phpunit": "^4.8.36",
+ "phpunit/phpunit": "^4.8.36 || ^5.0",
"phpdocumentor/phpdocumentor":"2.*",
- "twig/twig":"1.27",
"squizlabs/php_codesniffer": "^2.7",
"friendsofphp/php-cs-fixer": "^2.0",
"phpmd/phpmd": "2.*",
- "phploc/phploc": "2.*",
+ "phploc/phploc": "2.* || 3.* || 4.*",
"dompdf/dompdf":"0.8.*",
"tecnickcom/tcpdf": "6.*",
- "mpdf/mpdf": "5.*"
+ "mpdf/mpdf": "5.* || 6.* || 7.*"
},
"suggest": {
"ext-zip": "Allows writing OOXML and ODF",
diff --git a/docs/ISSUE_TEMPLATE.md b/docs/ISSUE_TEMPLATE.md
index 58981f8e..ee811b00 100644
--- a/docs/ISSUE_TEMPLATE.md
+++ b/docs/ISSUE_TEMPLATE.md
@@ -1,28 +1,35 @@
-Issue tracker is **ONLY** used for reporting bugs. NO NEW FEATURE ACCEPTED! Use [stackoverflow](https://stackoverflow.com/questions/tagged/phpword) for supporting issues.
+This is:
+
+- [ ] a bug report
+- [ ] a feature request
+- [ ] **not** a usage question (ask them on https://stackoverflow.com/questions/tagged/phpword)
# Expected Behavior
Please describe the behavior you are expecting.
-# Current Behavior
+### Current Behavior
What is the current behavior?
-# Failure Information
+### Failure Information
Please help provide information about the failure.
-## How to Reproduce
+### How to Reproduce
Please provide a code sample that reproduces the issue.
```php
+addSection();
$section->...
```
-## Context
+### Context
* PHP version:
* PHPWord version: 0.14
diff --git a/docs/PULL_REQUEST_TEMPLATE.md b/docs/PULL_REQUEST_TEMPLATE.md
index ad9788c4..cff513a3 100644
--- a/docs/PULL_REQUEST_TEMPLATE.md
+++ b/docs/PULL_REQUEST_TEMPLATE.md
@@ -1,12 +1,11 @@
-# Description
+### Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.
Fixes # (issue)
-# Checklist:
+### Checklist:
-- [ ] I have commented my code, particularly in hard-to-understand areas
-- [ ] I have run phpunit, phpcs, php-cs-fixer, phpmd
+- [ ] I have run `composer check` and no errors were reported
- [ ] The new code is covered by unit tests
- [ ] I have update the documentation to describe the changes
diff --git a/run_tests.sh b/run_tests.sh
deleted file mode 100755
index a5d94259..00000000
--- a/run_tests.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-echo "Running composer update"
-composer update
-
-## PHP_CodeSniffer
-echo "Running CodeSniffer"
-./vendor/bin/phpcs src/ tests/ --standard=PSR2 -n --ignore=src/PhpWord/Shared/PCLZip
-
-## PHP-CS-Fixer
-echo "Running CS Fixer"
-./vendor/bin/php-cs-fixer fix --diff --verbose --dry-run
-
-## PHP Mess Detector
-echo "Running Mess Detector"
-./vendor/bin/phpmd src/,tests/ text ./phpmd.xml.dist --exclude pclzip.lib.php
-
-## PHPUnit
-echo "Running PHPUnit"
-./vendor/bin/phpunit -c ./
-
diff --git a/src/PhpWord/Element/AbstractElement.php b/src/PhpWord/Element/AbstractElement.php
index 81e18528..a65c50f4 100644
--- a/src/PhpWord/Element/AbstractElement.php
+++ b/src/PhpWord/Element/AbstractElement.php
@@ -228,7 +228,7 @@ abstract class AbstractElement
/**
* Get element unique ID
*
- * @return int
+ * @return string
*/
public function getElementId()
{
@@ -425,18 +425,18 @@ abstract class AbstractElement
/**
* Set enum value
*
- * @param mixed $value
- * @param array $enum
- * @param mixed $default
+ * @param string|null $value
+ * @param string[] $enum
+ * @param string|null $default
*
* @throws \InvalidArgumentException
- * @return mixed
+ * @return string|null
*
* @todo Merge with the same method in AbstractStyle
*/
protected function setEnumVal($value = null, $enum = array(), $default = null)
{
- if ($value != null && trim($value) != '' && !empty($enum) && !in_array($value, $enum)) {
+ if ($value !== null && trim($value) != '' && !empty($enum) && !in_array($value, $enum)) {
throw new \InvalidArgumentException("Invalid style value: {$value}");
} elseif ($value === null || trim($value) == '') {
$value = $default;
diff --git a/src/PhpWord/Element/Field.php b/src/PhpWord/Element/Field.php
index 6ea63c6b..7b33a479 100644
--- a/src/PhpWord/Element/Field.php
+++ b/src/PhpWord/Element/Field.php
@@ -211,7 +211,7 @@ class Field extends AbstractElement
* @throws \InvalidArgumentException
* @return null|string|TextRun
*/
- public function setText($text)
+ public function setText($text = null)
{
if (isset($text)) {
if (is_string($text) || $text instanceof TextRun) {
diff --git a/src/PhpWord/Element/TrackChange.php b/src/PhpWord/Element/TrackChange.php
index d14fc201..44327f26 100644
--- a/src/PhpWord/Element/TrackChange.php
+++ b/src/PhpWord/Element/TrackChange.php
@@ -52,8 +52,6 @@ class TrackChange extends AbstractContainer
{
$this->author = $author;
$this->date = $date;
-
- return $this;
}
/**
diff --git a/src/PhpWord/Metadata/Protection.php b/src/PhpWord/Metadata/Protection.php
index 35391cb2..39ebc3de 100644
--- a/src/PhpWord/Metadata/Protection.php
+++ b/src/PhpWord/Metadata/Protection.php
@@ -182,7 +182,7 @@ class Protection
/**
* Set salt. Salt HAS to be 16 characters, or an exception will be thrown.
*
- * @param $salt
+ * @param string $salt
* @throws \InvalidArgumentException
* @return self
*/
diff --git a/src/PhpWord/Reader/Word2007/Settings.php b/src/PhpWord/Reader/Word2007/Settings.php
index c116425e..ccdbed25 100644
--- a/src/PhpWord/Reader/Word2007/Settings.php
+++ b/src/PhpWord/Reader/Word2007/Settings.php
@@ -152,7 +152,7 @@ class Settings extends AbstractPart
$revisionView = new TrackChangesView();
$revisionView->setMarkup(filter_var($xmlReader->getAttribute('w:markup', $node), FILTER_VALIDATE_BOOLEAN));
$revisionView->setComments($xmlReader->getAttribute('w:comments', $node));
- $revisionView->setInsDel($xmlReader->getAttribute('w:insDel', $node));
+ $revisionView->setInsDel(filter_var($xmlReader->getAttribute('w:insDel', $node), FILTER_VALIDATE_BOOLEAN));
$revisionView->setFormatting(filter_var($xmlReader->getAttribute('w:formatting', $node), FILTER_VALIDATE_BOOLEAN));
$revisionView->setInkAnnotations(filter_var($xmlReader->getAttribute('w:inkAnnotations', $node), FILTER_VALIDATE_BOOLEAN));
$phpWord->getSettings()->setRevisionView($revisionView);
diff --git a/src/PhpWord/Shared/Converter.php b/src/PhpWord/Shared/Converter.php
index bae8985d..56687c98 100644
--- a/src/PhpWord/Shared/Converter.php
+++ b/src/PhpWord/Shared/Converter.php
@@ -33,7 +33,7 @@ class Converter
/**
* Convert centimeter to twip
*
- * @param int $centimeter
+ * @param float $centimeter
* @return float
*/
public static function cmToTwip($centimeter = 1)
@@ -44,7 +44,7 @@ class Converter
/**
* Convert centimeter to inch
*
- * @param int $centimeter
+ * @param float $centimeter
* @return float
*/
public static function cmToInch($centimeter = 1)
@@ -55,7 +55,7 @@ class Converter
/**
* Convert centimeter to pixel
*
- * @param int $centimeter
+ * @param float $centimeter
* @return float
*/
public static function cmToPixel($centimeter = 1)
@@ -66,7 +66,7 @@ class Converter
/**
* Convert centimeter to point
*
- * @param int $centimeter
+ * @param float $centimeter
* @return float
*/
public static function cmToPoint($centimeter = 1)
@@ -77,8 +77,8 @@ class Converter
/**
* Convert centimeter to EMU
*
- * @param int $centimeter
- * @return int
+ * @param float $centimeter
+ * @return float
*/
public static function cmToEmu($centimeter = 1)
{
@@ -88,8 +88,8 @@ class Converter
/**
* Convert inch to twip
*
- * @param int $inch
- * @return int
+ * @param float $inch
+ * @return float
*/
public static function inchToTwip($inch = 1)
{
@@ -99,7 +99,7 @@ class Converter
/**
* Convert inch to centimeter
*
- * @param int $inch
+ * @param float $inch
* @return float
*/
public static function inchToCm($inch = 1)
@@ -110,8 +110,8 @@ class Converter
/**
* Convert inch to pixel
*
- * @param int $inch
- * @return int
+ * @param float $inch
+ * @return float
*/
public static function inchToPixel($inch = 1)
{
@@ -121,8 +121,8 @@ class Converter
/**
* Convert inch to point
*
- * @param int $inch
- * @return int
+ * @param float $inch
+ * @return float
*/
public static function inchToPoint($inch = 1)
{
@@ -132,8 +132,8 @@ class Converter
/**
* Convert inch to EMU
*
- * @param int $inch
- * @return int
+ * @param float $inch
+ * @return float
*/
public static function inchToEmu($inch = 1)
{
@@ -144,7 +144,7 @@ class Converter
* Convert pixel to twip
*
* @param int $pixel
- * @return int
+ * @return float
*/
public static function pixelToTwip($pixel = 1)
{
@@ -188,7 +188,7 @@ class Converter
* Convert point to twip unit
*
* @param int $point
- * @return int
+ * @return float
*/
public static function pointToTwip($point = 1)
{
@@ -210,7 +210,7 @@ class Converter
* Convert point to EMU
*
* @param int $point
- * @return int
+ * @return float
*/
public static function pointToEmu($point = 1)
{
@@ -221,7 +221,7 @@ class Converter
* Convert EMU to pixel
*
* @param int $emu
- * @return int
+ * @return float
*/
public static function emuToPixel($emu = 1)
{
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 8310e515..d8a10b57 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -518,6 +518,7 @@ class Html
private static function parseImage($node, $element)
{
$style = array();
+ $src = null;
foreach ($node->attributes as $attribute) {
switch ($attribute->name) {
case 'src':
diff --git a/src/PhpWord/Shared/Microsoft/PasswordEncoder.php b/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
index d3a03d97..1c7b4c6c 100644
--- a/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
+++ b/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
@@ -165,7 +165,7 @@ class PasswordEncoder
/**
* Returns the algorithm ID
*
- * @param sting $algorithmName
+ * @param string $algorithmName
* @return int
*/
public static function getAlgorithmId($algorithmName)
diff --git a/src/PhpWord/Shared/ZipArchive.php b/src/PhpWord/Shared/ZipArchive.php
index bb42a92a..3d8d0a41 100644
--- a/src/PhpWord/Shared/ZipArchive.php
+++ b/src/PhpWord/Shared/ZipArchive.php
@@ -161,7 +161,7 @@ class ZipArchive
{
if (!$this->usePclzip) {
if ($this->zip->close() === false) {
- throw new Exception("Could not close zip file {$this->filename}.");
+ throw new Exception("Could not close zip file {$this->filename}: ");
}
}
diff --git a/src/PhpWord/Style/Paper.php b/src/PhpWord/Style/Paper.php
index 2fbf59d2..09e4769e 100644
--- a/src/PhpWord/Style/Paper.php
+++ b/src/PhpWord/Style/Paper.php
@@ -118,14 +118,14 @@ class Paper extends AbstractStyle
/**
* Width
*
- * @var int (twip)
+ * @var float (twip)
*/
private $width;
/**
* Height
*
- * @var int (twip)
+ * @var float (twip)
*/
private $height;
@@ -175,7 +175,7 @@ class Paper extends AbstractStyle
/**
* Get width
*
- * @return int
+ * @return float
*/
public function getWidth()
{
@@ -185,7 +185,7 @@ class Paper extends AbstractStyle
/**
* Get height
*
- * @return int
+ * @return float
*/
public function getHeight()
{
diff --git a/src/PhpWord/Writer/PDF/MPDF.php b/src/PhpWord/Writer/PDF/MPDF.php
index 80c2eccf..e238057b 100644
--- a/src/PhpWord/Writer/PDF/MPDF.php
+++ b/src/PhpWord/Writer/PDF/MPDF.php
@@ -17,6 +17,8 @@
namespace PhpOffice\PhpWord\Writer\PDF;
+use PhpOffice\PhpWord\PhpWord;
+use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\Writer\WriterInterface;
/**
@@ -27,12 +29,14 @@ use PhpOffice\PhpWord\Writer\WriterInterface;
*/
class MPDF extends AbstractRenderer implements WriterInterface
{
- /**
- * Name of renderer include file
- *
- * @var string
- */
- protected $includeFile = 'mpdf.php';
+ public function __construct(PhpWord $phpWord)
+ {
+ if (file_exists(Settings::getPdfRendererPath() . '/mpdf.php')) {
+ // MPDF version 5.* needs this file to be included, later versions not
+ $this->includeFile = 'mpdf.php';
+ }
+ parent::__construct($phpWord);
+ }
/**
* Save PhpWord to file.
@@ -48,7 +52,13 @@ class MPDF extends AbstractRenderer implements WriterInterface
$orientation = strtoupper('portrait');
// Create PDF
- $pdf = new \mpdf();
+ if ($this->includeFile != null) {
+ // MPDF version 5.*
+ $pdf = new \mpdf();
+ } else {
+ // MPDF version > 6.*
+ $pdf = new \Mpdf\Mpdf();
+ }
$pdf->_setPageSize($paperSize, $orientation);
$pdf->addPage($orientation);
diff --git a/src/PhpWord/Writer/Word2007/Part/Comments.php b/src/PhpWord/Writer/Word2007/Part/Comments.php
index 4551ca92..2b8f9267 100644
--- a/src/PhpWord/Writer/Word2007/Part/Comments.php
+++ b/src/PhpWord/Writer/Word2007/Part/Comments.php
@@ -29,7 +29,7 @@ class Comments extends AbstractPart
/**
* Comments collection to be written
*
- * @var \PhpOffice\PhpWord\Collection\Comments
+ * @var \PhpOffice\PhpWord\Element\Comment[]
*/
protected $elements;
@@ -92,7 +92,7 @@ class Comments extends AbstractPart
/**
* Set element
*
- * @param \PhpOffice\PhpWord\Collection\Comments $elements
+ * @param \PhpOffice\PhpWord\Element\Comment[] $elements
* @return self
*/
public function setElements($elements)
diff --git a/tests/PhpWord/Shared/ZipArchiveTest.php b/tests/PhpWord/Shared/ZipArchiveTest.php
index 91f0f030..cb095127 100644
--- a/tests/PhpWord/Shared/ZipArchiveTest.php
+++ b/tests/PhpWord/Shared/ZipArchiveTest.php
@@ -27,34 +27,34 @@ use PhpOffice\PhpWord\Settings;
*/
class ZipArchiveTest extends \PHPUnit\Framework\TestCase
{
- /**
- * Test close method exception: Working in local, not working in Travis
- *
- * expectedException \PhpOffice\PhpWord\Exception\Exception
- * expectedExceptionMessage Could not close zip file
- * covers ::close
- */
- public function testCloseException()
- {
- // $zipFile = __DIR__ . "/../_files/documents/ziptest.zip";
+// /**
+// * Test close method exception: Working in local, not working in Travis
+// *
+// * expectedException \PhpOffice\PhpWord\Exception\Exception
+// * expectedExceptionMessage Could not close zip file
+// * covers ::close
+// */
+// public function testCloseException()
+// {
+// $zipFile = __DIR__ . "/../_files/documents/ziptest.zip";
- // $object = new ZipArchive();
- // $object->open($zipFile, ZipArchive::CREATE);
- // $object->addFromString('content/string.txt', 'Test');
+// $object = new ZipArchive();
+// $object->open($zipFile, ZipArchive::CREATE);
+// $object->addFromString('content/string.txt', 'Test');
- // // Lock the file
- // $resource = fopen($zipFile, "w");
- // flock($resource, LOCK_EX);
+// // Lock the file
+// $resource = fopen($zipFile, "w");
+// flock($resource, LOCK_EX);
- // // Closing the file should throws an exception
- // $object->close();
+// // Closing the file should throws an exception
+// $object->close();
- // // Unlock the file
- // flock($resource, LOCK_UN);
- // fclose($resource);
+// // Unlock the file
+// flock($resource, LOCK_UN);
+// fclose($resource);
- // @unlink($zipFile);
- }
+// @unlink($zipFile);
+// }
/**
* Test all methods
diff --git a/tests/PhpWord/Writer/ODTextTest.php b/tests/PhpWord/Writer/ODTextTest.php
index bb1b9538..1984de0f 100644
--- a/tests/PhpWord/Writer/ODTextTest.php
+++ b/tests/PhpWord/Writer/ODTextTest.php
@@ -110,6 +110,7 @@ class ODTextTest extends \PHPUnit\Framework\TestCase
$section->addText('Test');
$writer = new ODText($phpWord);
$writer->save('php://output');
+ $this->assertNotNull($this->getActualOutput());
}
/**
diff --git a/tests/PhpWord/Writer/RTFTest.php b/tests/PhpWord/Writer/RTFTest.php
index f4442043..803087e5 100644
--- a/tests/PhpWord/Writer/RTFTest.php
+++ b/tests/PhpWord/Writer/RTFTest.php
@@ -111,5 +111,6 @@ class RTFTest extends \PHPUnit\Framework\TestCase
$section->addText(htmlspecialchars('Test', ENT_COMPAT, 'UTF-8'));
$writer = new RTF($phpWord);
$writer->save('php://output');
+ $this->assertNotNull($this->getActualOutput());
}
}
diff --git a/tests/PhpWord/Writer/Word2007/Part/DocumentTest.php b/tests/PhpWord/Writer/Word2007/Part/DocumentTest.php
index 42c098cd..6998e717 100644
--- a/tests/PhpWord/Writer/Word2007/Part/DocumentTest.php
+++ b/tests/PhpWord/Writer/Word2007/Part/DocumentTest.php
@@ -58,7 +58,8 @@ class DocumentTest extends \PHPUnit\Framework\TestCase
$docInfo->setCustomProperty('key6', new \DateTime());
$docInfo->setCustomProperty('key7', time(), DocInfo::PROPERTY_TYPE_DATE);
- TestHelperDOCX::getDocument($phpWord);
+ $doc = TestHelperDOCX::getDocument($phpWord);
+ $this->assertNotNull($doc);
// $this->assertTrue($doc->elementExists('/Properties/property[name="key1"]/vt:lpwstr'));
// $this->assertTrue($doc->elementExists('/Properties/property[name="key2"]/vt:bool'));
From fd7ee764380ad1c99c9075be4963d19d239bec54 Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 29 Dec 2017 03:01:36 +0100
Subject: [PATCH 070/176] create alias for develop branch
---
composer.json | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/composer.json b/composer.json
index 3cc4b131..aa4a2415 100644
--- a/composer.json
+++ b/composer.json
@@ -74,5 +74,10 @@
"psr-4": {
"PhpOffice\\PhpWord\\": "src/PhpWord"
}
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-develop": "0.15.0-dev"
+ }
}
}
From d2b9e88047c0533db351bcefc0bedbe703b09411 Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 29 Dec 2017 14:36:07 +0100
Subject: [PATCH 071/176] add parsing of "align" HTML attribute
---
CHANGELOG.md | 7 ++++++
samples/Sample_26_Html.php | 2 +-
src/PhpWord/Shared/Html.php | 39 ++++++++++++++++++++-----------
tests/PhpWord/Shared/HtmlTest.php | 4 +++-
4 files changed, 36 insertions(+), 16 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 93945189..b5396d8d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,13 @@ Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
+v0.15.0 (?? ??? 2018)
+----------------------
+### Added
+- Parsing of "align" HTML attribute - @troosan
+
+### Fixed
+
v0.14.0 (29 Dec 2017)
----------------------
This release fixes several bugs and adds some new features.
diff --git a/samples/Sample_26_Html.php b/samples/Sample_26_Html.php
index ba06b063..b993f834 100644
--- a/samples/Sample_26_Html.php
+++ b/samples/Sample_26_Html.php
@@ -28,7 +28,7 @@ $html .= '';
-$html .= '
+$html .= '
| header a |
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index d8a10b57..38d326c1 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -85,6 +85,8 @@ class Html
case 'style':
$styles = self::parseStyle($attribute, $styles);
break;
+ case 'align':
+ $styles['alignment'] = self::mapAlign($attribute->value);
}
}
}
@@ -431,20 +433,7 @@ class Html
}
break;
case 'text-align':
- switch ($cValue) {
- case 'left':
- $styles['alignment'] = Jc::START;
- break;
- case 'right':
- $styles['alignment'] = Jc::END;
- break;
- case 'center':
- $styles['alignment'] = Jc::CENTER;
- break;
- case 'justify':
- $styles['alignment'] = Jc::BOTH;
- break;
- }
+ $styles['alignment'] = self::mapAlign($cValue);
break;
case 'font-size':
$styles['size'] = Converter::cssToPoint($cValue);
@@ -584,6 +573,28 @@ class Html
}
}
+ /**
+ * Transforms a HTML/CSS alignment into a \PhpOffice\PhpWord\SimpleType\Jc
+ *
+ * @param string $cssAlignment
+ * @return string|null
+ */
+ private static function mapAlign($cssAlignment)
+ {
+ switch ($cssAlignment) {
+ case 'left':
+ return Jc::START;
+ case 'right':
+ return Jc::END;
+ case 'center':
+ return Jc::CENTER;
+ case 'justify':
+ return Jc::BOTH;
+ }
+
+ return null;
+ }
+
/**
* Parse line break
*
diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php
index d168c09e..c7d36470 100644
--- a/tests/PhpWord/Shared/HtmlTest.php
+++ b/tests/PhpWord/Shared/HtmlTest.php
@@ -187,7 +187,7 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
{
$phpWord = new \PhpOffice\PhpWord\PhpWord();
$section = $phpWord->addSection();
- $html = '
+ $html = '
| header a |
@@ -205,6 +205,8 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
$this->assertTrue($doc->elementExists('/w:document/w:body/w:tbl'));
$this->assertTrue($doc->elementExists('/w:document/w:body/w:tbl/w:tr/w:tc'));
+ $this->assertTrue($doc->elementExists('/w:document/w:body/w:tbl/w:tblPr/w:jc'));
+ $this->assertEquals(Jc::START, $doc->getElementAttribute('/w:document/w:body/w:tbl/w:tblPr/w:jc', 'w:val'));
}
/**
From 1d8e7b8374547fac04c432cda2f54503077829f2 Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 29 Dec 2017 14:36:56 +0100
Subject: [PATCH 072/176] split composer scripts, add description (only works
with composer 1.6)
---
composer.json | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/composer.json b/composer.json
index aa4a2415..a79c8e19 100644
--- a/composer.json
+++ b/composer.json
@@ -35,16 +35,28 @@
}
],
"scripts": {
+ "test": [
+ "./vendor/bin/phpunit --color=always"
+ ],
+ "test-no-coverage": [
+ "./vendor/bin/phpunit --color=always --no-coverage"
+ ],
"check": [
"./vendor/bin/php-cs-fixer fix --ansi --dry-run --diff",
"./vendor/bin/phpcs --report-width=200 --report-summary --report-full samples/ src/ tests/ --ignore=src/PhpWord/Shared/PCLZip --standard=PSR2 -n",
"./vendor/bin/phpmd src/,tests/ text ./phpmd.xml.dist --exclude pclzip.lib.php",
- "./vendor/bin/phpunit --color=always"
+ "@test"
],
"fix": [
"./vendor/bin/php-cs-fixer fix --ansi"
]
},
+ "scripts-descriptions": {
+ "test": "Runs all unit tests",
+ "test-no-coverage": "Runs all unit tests, without code coverage",
+ "check": "Runs PHP CheckStyle and PHP Mess detector",
+ "fix": "Fixes issues found by PHP-CS"
+ },
"require": {
"php": "^5.3.3 || ^7.0",
"ext-xml": "*",
From b20cd4fa9f5db9ee8c028d7ebe34597e9c540340 Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 29 Dec 2017 14:37:26 +0100
Subject: [PATCH 073/176] output the source code of the sample that was run
---
samples/Sample_Header.php | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/samples/Sample_Header.php b/samples/Sample_Header.php
index c4996049..36478ad6 100644
--- a/samples/Sample_Header.php
+++ b/samples/Sample_Header.php
@@ -43,13 +43,19 @@ $pageHeading = IS_INDEX ? '' : "{$pageHeading}
";
// Populate samples
$files = '';
if ($handle = opendir('.')) {
- while (false !== ($file = readdir($handle))) {
+ $sampleFiles = array();
+ while (false !== ($sampleFile = readdir($handle))) {
+ $sampleFiles[] = $sampleFile;
+ }
+ sort($sampleFiles);
+ closedir($handle);
+
+ foreach ($sampleFiles as $file) {
if (preg_match('/^Sample_\d+_/', $file)) {
$name = str_replace('_', ' ', preg_replace('/(Sample_|\.php)/', '', $file));
$files .= "{$name}";
}
}
- closedir($handle);
}
/**
@@ -78,6 +84,11 @@ function write($phpWord, $filename, $writers)
}
$result .= getEndingNotes($writers);
+ $result .= '';
+ if (file_exists($filename . '.php')) {
+ $result .= highlight_file($filename . '.php', true);
+ }
+ $result .= '
';
return $result;
}
From 46e179d1484b12005a0882e5bf12ee76bb5c6856 Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 29 Dec 2017 14:42:48 +0100
Subject: [PATCH 074/176] add instructions on how to run the samples in a
browser
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index ac5f3b95..4e3d1e2d 100644
--- a/README.md
+++ b/README.md
@@ -161,7 +161,8 @@ $objWriter->save('helloWorld.html');
/* Note: we skip PDF, because "HTML-to-PDF" approach is used to create PDF documents. */
```
-More examples are provided in the [samples folder](samples/). You can also read the [Developers' Documentation](http://phpword.readthedocs.org/) and the [API Documentation](http://phpoffice.github.io/PHPWord/docs/master/) for more detail.
+More examples are provided in the [samples folder](samples/). For an easy access to those samples launch `php -S localhost:8000` in the samples directory then browse to [http://localhost:8000](http://localhost:8000) to view the samples.
+You can also read the [Developers' Documentation](http://phpword.readthedocs.org/) and the [API Documentation](http://phpoffice.github.io/PHPWord/docs/master/) for more detail.
## Contributing
From 709ea1e14c2765ef2c439a46bafde1e027bd23f1 Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 29 Dec 2017 14:48:12 +0100
Subject: [PATCH 075/176] update changelog [skip ci]
---
CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b5396d8d..1f22bb2e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
v0.15.0 (?? ??? 2018)
----------------------
### Added
-- Parsing of "align" HTML attribute - @troosan
+- Parsing of "align" HTML attribute - @troosan #1231
### Fixed
From 526d0ac8defcf42354a402ce6e112fe21a89848c Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 29 Dec 2017 03:01:36 +0100
Subject: [PATCH 076/176] create alias for develop branch
---
composer.json | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/composer.json b/composer.json
index 3cc4b131..aa4a2415 100644
--- a/composer.json
+++ b/composer.json
@@ -74,5 +74,10 @@
"psr-4": {
"PhpOffice\\PhpWord\\": "src/PhpWord"
}
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-develop": "0.15.0-dev"
+ }
}
}
From 400a8e65d35010c88076e425aec7af1f9132e331 Mon Sep 17 00:00:00 2001
From: Maxim
Date: Fri, 29 Dec 2017 21:19:35 +0200
Subject: [PATCH 077/176] rename 'Object' classes to 'ObjectElement' (php 7.2
compatibility) (#1185)
merge develop branch
---
.travis.yml | 5 +++++
CHANGELOG.md | 3 ++-
src/PhpWord/Element/AbstractContainer.php | 6 ++---
src/PhpWord/Element/AbstractElement.php | 7 ++++--
.../Element/{Object.php => OLEObject.php} | 4 ++--
.../Element/{Object.php => OLEObject.php} | 6 ++---
tests/PhpWord/Element/CellTest.php | 2 +-
tests/PhpWord/Element/ObjectTest.php | 22 +++++++++----------
tests/PhpWord/Element/SectionTest.php | 2 +-
tests/PhpWord/Writer/Word2007/ElementTest.php | 2 +-
10 files changed, 34 insertions(+), 25 deletions(-)
rename src/PhpWord/Element/{Object.php => OLEObject.php} (98%)
rename src/PhpWord/Writer/Word2007/Element/{Object.php => OLEObject.php} (95%)
diff --git a/.travis.yml b/.travis.yml
index d63b7bb2..148877ed 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,6 +16,11 @@ matrix:
- php: 5.6
env: COVERAGE=1
allow_failures:
+<<<<<<< HEAD
+=======
+ - php: 7.0
+ - php: 7.1
+>>>>>>> branch 'php72_support_object_classes' of https://github.com/SailorMax/PHPWord
- php: 7.2
cache:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1f22bb2e..71b331d6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,7 +37,8 @@ This version brings compatibility with PHP 7.0 & 7.1
### Fixed
- Loosen dependency to Zend
- Images are not being printed when generating PDF - @hubertinio #1074 #431
-- Fixed some PHP 7 warnings - @likeuntomurphy #927
+- Fixed some PHP 7 warnings - @ likeuntomurphy #927
+- Fixed PHP 7.2 compatibility (renamed `Object` class names to `ObjectElement`) - @SailorMax #1185
- Fixed Word 97 reader - @alsofronie @Benpxpx @mario-rivera #912 #920 #892
- Fixed image loading over https - @troosan #988
- Impossibility to set different even and odd page headers - @troosan #981
diff --git a/src/PhpWord/Element/AbstractContainer.php b/src/PhpWord/Element/AbstractContainer.php
index e3022b50..b00424b7 100644
--- a/src/PhpWord/Element/AbstractContainer.php
+++ b/src/PhpWord/Element/AbstractContainer.php
@@ -37,7 +37,7 @@ namespace PhpOffice\PhpWord\Element;
* @method PageBreak addPageBreak()
* @method Table addTable(mixed $style = null)
* @method Image addImage(string $source, mixed $style = null, bool $isWatermark = false)
- * @method \PhpOffice\PhpWord\Element\Object addObject(string $source, mixed $style = null)
+ * @method \PhpOffice\PhpWord\Element\OLEObject addObject(string $source, mixed $style = null)
* @method TextBox addTextBox(mixed $style = null)
* @method Field addField(string $type = null, array $properties = array(), array $options = array(), mixed $text = null)
* @method Line addLine(mixed $lineStyle = null)
@@ -87,7 +87,7 @@ abstract class AbstractContainer extends AbstractElement
);
$functions = array();
foreach ($elements as $element) {
- $functions['add' . strtolower($element)] = $element;
+ $functions['add' . strtolower($element)] = $element == 'Object' ? 'OLEObject' : $element;
}
// Run valid `add` command
@@ -193,7 +193,7 @@ abstract class AbstractContainer extends AbstractElement
'Link' => $generalContainers,
'TextBreak' => $generalContainers,
'Image' => $generalContainers,
- 'Object' => $generalContainers,
+ 'OLEObject' => $generalContainers,
'Field' => $generalContainers,
'Line' => $generalContainers,
'Shape' => $generalContainers,
diff --git a/src/PhpWord/Element/AbstractElement.php b/src/PhpWord/Element/AbstractElement.php
index a65c50f4..63892b74 100644
--- a/src/PhpWord/Element/AbstractElement.php
+++ b/src/PhpWord/Element/AbstractElement.php
@@ -358,11 +358,14 @@ abstract class AbstractElement
*/
private function setMediaRelation()
{
- if (!$this instanceof Link && !$this instanceof Image && !$this instanceof Object) {
+ if (!$this instanceof Link && !$this instanceof Image && !$this instanceof OLEObject) {
return;
}
$elementName = substr(get_class($this), strrpos(get_class($this), '\\') + 1);
+ if ($elementName == 'OLEObject') {
+ $elementName = 'Object';
+ }
$mediaPart = $this->getMediaPart();
$source = $this->getSource();
$image = null;
@@ -372,7 +375,7 @@ abstract class AbstractElement
$rId = Media::addElement($mediaPart, strtolower($elementName), $source, $image);
$this->setRelationId($rId);
- if ($this instanceof Object) {
+ if ($this instanceof OLEObject) {
$icon = $this->getIcon();
$rId = Media::addElement($mediaPart, 'image', $icon, new Image($icon));
$this->setImageRelationId($rId);
diff --git a/src/PhpWord/Element/Object.php b/src/PhpWord/Element/OLEObject.php
similarity index 98%
rename from src/PhpWord/Element/Object.php
rename to src/PhpWord/Element/OLEObject.php
index 8fe83224..5da94c3a 100644
--- a/src/PhpWord/Element/Object.php
+++ b/src/PhpWord/Element/OLEObject.php
@@ -21,9 +21,9 @@ use PhpOffice\PhpWord\Exception\InvalidObjectException;
use PhpOffice\PhpWord\Style\Image as ImageStyle;
/**
- * Object element
+ * OLEObject element
*/
-class Object extends AbstractElement
+class OLEObject extends AbstractElement
{
/**
* Ole-Object Src
diff --git a/src/PhpWord/Writer/Word2007/Element/Object.php b/src/PhpWord/Writer/Word2007/Element/OLEObject.php
similarity index 95%
rename from src/PhpWord/Writer/Word2007/Element/Object.php
rename to src/PhpWord/Writer/Word2007/Element/OLEObject.php
index 8231ec0c..50891d97 100644
--- a/src/PhpWord/Writer/Word2007/Element/Object.php
+++ b/src/PhpWord/Writer/Word2007/Element/OLEObject.php
@@ -20,11 +20,11 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element;
use PhpOffice\PhpWord\Writer\Word2007\Style\Image as ImageStyleWriter;
/**
- * Object element writer
+ * OLEObject element writer
*
* @since 0.10.0
*/
-class Object extends AbstractElement
+class OLEObject extends AbstractElement
{
/**
* Write object element.
@@ -33,7 +33,7 @@ class Object extends AbstractElement
{
$xmlWriter = $this->getXmlWriter();
$element = $this->getElement();
- if (!$element instanceof \PhpOffice\PhpWord\Element\Object) {
+ if (!$element instanceof \PhpOffice\PhpWord\Element\OLEObject) {
return;
}
diff --git a/tests/PhpWord/Element/CellTest.php b/tests/PhpWord/Element/CellTest.php
index 4e8daa0e..a1132cfa 100644
--- a/tests/PhpWord/Element/CellTest.php
+++ b/tests/PhpWord/Element/CellTest.php
@@ -181,7 +181,7 @@ class CellTest extends \PHPUnit\Framework\TestCase
$element = $oCell->addObject($src);
$this->assertCount(1, $oCell->getElements());
- $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Object', $element);
+ $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\OLEObject', $element);
}
/**
diff --git a/tests/PhpWord/Element/ObjectTest.php b/tests/PhpWord/Element/ObjectTest.php
index 71f12974..ba761b70 100644
--- a/tests/PhpWord/Element/ObjectTest.php
+++ b/tests/PhpWord/Element/ObjectTest.php
@@ -18,9 +18,9 @@
namespace PhpOffice\PhpWord\Element;
/**
- * Test class for PhpOffice\PhpWord\Element\Object
+ * Test class for PhpOffice\PhpWord\Element\OLEObject
*
- * @coversDefaultClass \PhpOffice\PhpWord\Element\Object
+ * @coversDefaultClass \PhpOffice\PhpWord\Element\OLEObject
* @runTestsInSeparateProcesses
*/
class ObjectTest extends \PHPUnit\Framework\TestCase
@@ -31,9 +31,9 @@ class ObjectTest extends \PHPUnit\Framework\TestCase
public function testConstructWithSupportedFiles()
{
$src = __DIR__ . '/../_files/documents/reader.docx';
- $oObject = new Object($src);
+ $oObject = new OLEObject($src);
- $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Object', $oObject);
+ $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\OLEObject', $oObject);
$this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Image', $oObject->getStyle());
$this->assertEquals($src, $oObject->getSource());
}
@@ -44,9 +44,9 @@ class ObjectTest extends \PHPUnit\Framework\TestCase
public function testConstructWithSupportedFilesLong()
{
$src = __DIR__ . '/../_files/documents/sheet.xls';
- $oObject = new Object($src);
+ $oObject = new OLEObject($src);
- $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Object', $oObject);
+ $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\OLEObject', $oObject);
$this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Image', $oObject->getStyle());
$this->assertEquals($src, $oObject->getSource());
}
@@ -59,7 +59,7 @@ class ObjectTest extends \PHPUnit\Framework\TestCase
public function testConstructWithNotSupportedFiles()
{
$src = __DIR__ . '/../_files/xsl/passthrough.xsl';
- $oObject = new Object($src);
+ $oObject = new OLEObject($src);
$oObject->getSource();
}
@@ -69,9 +69,9 @@ class ObjectTest extends \PHPUnit\Framework\TestCase
public function testConstructWithSupportedFilesAndStyle()
{
$src = __DIR__ . '/../_files/documents/sheet.xls';
- $oObject = new Object($src, array('width' => '230px'));
+ $oObject = new OLEObject($src, array('width' => '230px'));
- $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Object', $oObject);
+ $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\OLEObject', $oObject);
$this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Image', $oObject->getStyle());
$this->assertEquals($src, $oObject->getSource());
}
@@ -82,7 +82,7 @@ class ObjectTest extends \PHPUnit\Framework\TestCase
public function testRelationId()
{
$src = __DIR__ . '/../_files/documents/sheet.xls';
- $oObject = new Object($src);
+ $oObject = new OLEObject($src);
$iVal = rand(1, 1000);
$oObject->setRelationId($iVal);
@@ -95,7 +95,7 @@ class ObjectTest extends \PHPUnit\Framework\TestCase
public function testImageRelationId()
{
$src = __DIR__ . '/../_files/documents/sheet.xls';
- $oObject = new Object($src);
+ $oObject = new OLEObject($src);
$iVal = rand(1, 1000);
$oObject->setImageRelationId($iVal);
diff --git a/tests/PhpWord/Element/SectionTest.php b/tests/PhpWord/Element/SectionTest.php
index 8b6c9a43..20f0f0f7 100644
--- a/tests/PhpWord/Element/SectionTest.php
+++ b/tests/PhpWord/Element/SectionTest.php
@@ -70,7 +70,7 @@ class SectionTest extends \PHPUnit\Framework\TestCase
'PageBreak',
'Table',
'ListItem',
- 'Object',
+ 'OLEObject',
'Image',
'Title',
'TextRun',
diff --git a/tests/PhpWord/Writer/Word2007/ElementTest.php b/tests/PhpWord/Writer/Word2007/ElementTest.php
index 12f810ce..4f0d50d9 100644
--- a/tests/PhpWord/Writer/Word2007/ElementTest.php
+++ b/tests/PhpWord/Writer/Word2007/ElementTest.php
@@ -44,7 +44,7 @@ class ElementTest extends \PHPUnit\Framework\TestCase
{
$elements = array(
'CheckBox', 'Container', 'Footnote', 'Image', 'Link', 'ListItem', 'ListItemRun',
- 'Object', 'PreserveText', 'Table', 'Text', 'TextBox', 'TextBreak', 'Title', 'TOC',
+ 'OLEObject', 'PreserveText', 'Table', 'Text', 'TextBox', 'TextBreak', 'Title', 'TOC',
'Field', 'Line', 'Shape', 'Chart', 'FormField', 'SDT', 'Bookmark',
);
foreach ($elements as $element) {
From 2d87fd320dffbbdafdf280dda78dcfce34f024c1 Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 29 Dec 2017 20:27:05 +0100
Subject: [PATCH 078/176] fix merge
---
.travis.yml | 5 -----
1 file changed, 5 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 148877ed..d63b7bb2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,11 +16,6 @@ matrix:
- php: 5.6
env: COVERAGE=1
allow_failures:
-<<<<<<< HEAD
-=======
- - php: 7.0
- - php: 7.1
->>>>>>> branch 'php72_support_object_classes' of https://github.com/SailorMax/PHPWord
- php: 7.2
cache:
From d1a79b79f1cc0622bb2da1a555e0614726a5874f Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 29 Dec 2017 21:29:31 +0100
Subject: [PATCH 079/176] add branch alias for 0.15.0 [skip ci]
---
composer.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/composer.json b/composer.json
index aa4a2415..09e795de 100644
--- a/composer.json
+++ b/composer.json
@@ -77,7 +77,7 @@
},
"extra": {
"branch-alias": {
- "dev-develop": "0.15.0-dev"
+ "dev-master": "0.15.0-dev"
}
}
}
From ca29ceb1fa60c63216f15a30338c2faaeac1edb1 Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 29 Dec 2017 21:33:29 +0100
Subject: [PATCH 080/176] add branch alias for 0.15.0 [skip ci]
---
composer.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/composer.json b/composer.json
index 09e795de..cba86eb1 100644
--- a/composer.json
+++ b/composer.json
@@ -77,7 +77,7 @@
},
"extra": {
"branch-alias": {
- "dev-master": "0.15.0-dev"
+ "dev-master": "0.15-dev"
}
}
}
From 23693b403c32c13a355adf0e2a32642a480066da Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 29 Dec 2017 21:47:55 +0100
Subject: [PATCH 081/176] change impl to avoid compilation issue
---
src/PhpWord/Writer/PDF/MPDF.php | 32 +++++++++++++++++++++++++-------
1 file changed, 25 insertions(+), 7 deletions(-)
diff --git a/src/PhpWord/Writer/PDF/MPDF.php b/src/PhpWord/Writer/PDF/MPDF.php
index e238057b..b6980a9d 100644
--- a/src/PhpWord/Writer/PDF/MPDF.php
+++ b/src/PhpWord/Writer/PDF/MPDF.php
@@ -29,6 +29,12 @@ use PhpOffice\PhpWord\Writer\WriterInterface;
*/
class MPDF extends AbstractRenderer implements WriterInterface
{
+ /**
+ * Overridden to set the correct includefile, only needed for MPDF 5
+ *
+ * @codeCoverageIgnore
+ * @param PhpWord $phpWord
+ */
public function __construct(PhpWord $phpWord)
{
if (file_exists(Settings::getPdfRendererPath() . '/mpdf.php')) {
@@ -52,13 +58,8 @@ class MPDF extends AbstractRenderer implements WriterInterface
$orientation = strtoupper('portrait');
// Create PDF
- if ($this->includeFile != null) {
- // MPDF version 5.*
- $pdf = new \mpdf();
- } else {
- // MPDF version > 6.*
- $pdf = new \Mpdf\Mpdf();
- }
+ $mPdfClass = $this->getMPdfClassName();
+ $pdf = new $mPdfClass();
$pdf->_setPageSize($paperSize, $orientation);
$pdf->addPage($orientation);
@@ -78,4 +79,21 @@ class MPDF extends AbstractRenderer implements WriterInterface
parent::restoreStateAfterSave($fileHandle);
}
+
+ /**
+ * Return classname of MPDF to instantiate
+ *
+ * @codeCoverageIgnore
+ * @return string
+ */
+ private function getMPdfClassName()
+ {
+ if ($this->includeFile != null) {
+ // MPDF version 5.*
+ return '\mpdf';
+ }
+
+ // MPDF version > 6.*
+ return '\Mpdf\Mpdf';
+ }
}
From d480aab1b000c23f1035b62b8370492fe1d4cce2 Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 29 Dec 2017 21:29:31 +0100
Subject: [PATCH 082/176] add branch alias for 0.15.0 [skip ci]
---
composer.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/composer.json b/composer.json
index a79c8e19..2d2743ec 100644
--- a/composer.json
+++ b/composer.json
@@ -89,7 +89,7 @@
},
"extra": {
"branch-alias": {
- "dev-develop": "0.15.0-dev"
+ "dev-master": "0.15.0-dev"
}
}
}
From 7d929fee8cc0aae31edbe71c7f6adf8cb3c4b545 Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 29 Dec 2017 21:33:29 +0100
Subject: [PATCH 083/176] add branch alias for 0.15.0 [skip ci]
---
composer.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/composer.json b/composer.json
index 2d2743ec..614865d8 100644
--- a/composer.json
+++ b/composer.json
@@ -89,7 +89,7 @@
},
"extra": {
"branch-alias": {
- "dev-master": "0.15.0-dev"
+ "dev-master": "0.15-dev"
}
}
}
From bdca366d9109d91a8deeb46558b4ed0ef347bc04 Mon Sep 17 00:00:00 2001
From: troosan
Date: Wed, 3 Jan 2018 14:15:59 +0100
Subject: [PATCH 084/176] remove link to obsolete URL [skip ci]
---
README.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/README.md b/README.md
index ac5f3b95..61f48036 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,6 @@ Read more about PHPWord:
- [Getting started](#getting-started)
- [Contributing](#contributing)
- [Developers' Documentation](http://phpword.readthedocs.org/)
-- [API Documentation](http://phpoffice.github.io/PHPWord/docs/master/)
## Features
From 4a530d1d97b064b87d9e2a1cc64cab996246569c Mon Sep 17 00:00:00 2001
From: troosan
Date: Wed, 3 Jan 2018 17:16:19 +0100
Subject: [PATCH 085/176] Do not show script source when running from CLI
---
samples/Sample_Header.php | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/samples/Sample_Header.php b/samples/Sample_Header.php
index 36478ad6..ac51f983 100644
--- a/samples/Sample_Header.php
+++ b/samples/Sample_Header.php
@@ -84,11 +84,6 @@ function write($phpWord, $filename, $writers)
}
$result .= getEndingNotes($writers);
- $result .= '';
- if (file_exists($filename . '.php')) {
- $result .= highlight_file($filename . '.php', true);
- }
- $result .= '
';
return $result;
}
@@ -127,6 +122,12 @@ function getEndingNotes($writers)
}
}
$result .= '';
+
+ $result .= '';
+ if (file_exists($filename . '.php')) {
+ $result .= highlight_file($filename . '.php', true);
+ }
+ $result .= '';
}
}
From 99b04f0353e4c2d398f8b4c4db37df5e5772fc1d Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 12 Jan 2018 23:42:22 +0100
Subject: [PATCH 086/176] fix reading of docx default style (#1238)
---
CHANGELOG.md | 7 +-
docs/elements.rst | 7 +-
docs/general.rst | 3 +-
docs/installing.rst | 1 -
src/PhpWord/Reader/Word2007/AbstractPart.php | 68 +++++++++++++++++--
src/PhpWord/Reader/Word2007/Settings.php | 4 +-
src/PhpWord/Reader/Word2007/Styles.php | 23 +++++++
src/PhpWord/Style/Paragraph.php | 1 -
src/PhpWord/Writer/Word2007/Element/Shape.php | 4 +-
src/PhpWord/Writer/Word2007/Part/Styles.php | 21 ++++--
.../Writer/Word2007/Style/Paragraph.php | 2 +-
11 files changed, 115 insertions(+), 26 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 71b331d6..62694eea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,9 @@ v0.15.0 (?? ??? 2018)
- Parsing of "align" HTML attribute - @troosan #1231
### Fixed
+- fix reading of docx default style - @troosan #1238
+
+
v0.14.0 (29 Dec 2017)
----------------------
@@ -58,6 +61,8 @@ This version brings compatibility with PHP 7.0 & 7.1
### Deprecated
- PhpWord->getProtection(), get it from the settings instead PhpWord->getSettings()->getDocumentProtection();
+
+
v0.13.0 (31 July 2016)
-------------------
This release brings several improvements in `TemplateProcessor`, automatic output escaping feature for OOXML, ODF, HTML, and RTF (turned off, by default).
@@ -77,7 +82,7 @@ Manual installation feature has been dropped since the release. Please, use [Com
- Improved error message for the case when `autoload.php` is not found. - @RomanSyroeshko #371
- Renamed the `align` option of `NumberingLevel`, `Frame`, `Table`, and `Paragraph` styles into `alignment`. - @RomanSyroeshko
- Improved performance of `TemplateProcessor::setValue()`. - @kazitanvirahsan #614, #617
-- Fixed some HTML tags not rendering any output (p, header & table) - #257, #324 - @twmobius and @garethellis
+- Fixed some HTML tags not rendering any output (p, header & table) - #257, #324 - @twmobius and @garethellis
### Deprecated
- `getAlign` and `setAlign` methods of `NumberingLevel`, `Frame`, `Table`, and `Paragraph` styles.
diff --git a/docs/elements.rst b/docs/elements.rst
index c73ffa06..94ff2667 100644
--- a/docs/elements.rst
+++ b/docs/elements.rst
@@ -160,7 +160,7 @@ Parameters:
- ``$text``. Text that appears in the document.
- ``$depth``. Depth of list item.
- ``$fontStyle``. See :ref:`font-style`.
-- ``$listStyle``. List style of the current element TYPE\_NUMBER,
+- ``$listStyle``. List style of the current element TYPE\_NUMBER,
TYPE\_ALPHANUM, TYPE\_BULLET\_FILLED, etc. See list of constants in PHPWord\\Style\\ListItem.
- ``$paragraphStyle``. See :ref:`paragraph-style`.
@@ -345,7 +345,7 @@ The footnote numbering can be controlled by setting the FootnoteProperties on th
.. code-block:: php
$fp = new PhpWord\SimpleType\FootnoteProperties();
- //sets the position of the footnote (pageBottom (default), beneathText, sectEnd, docEnd)
+ //sets the position of the footnote (pageBottom (default), beneathText, sectEnd, docEnd)
$fp->setPos(FootnoteProperties::POSITION_DOC_END);
//set the number format to use (decimal (default), upperRoman, upperLetter, ...)
$fp->setNumFmt(FootnoteProperties::NUMBER_FORMAT_LOWER_ROMAN);
@@ -353,7 +353,6 @@ The footnote numbering can be controlled by setting the FootnoteProperties on th
$fp->setNumStart(2);
//when to restart counting (continuous (default), eachSect, eachPage)
$fp->setNumRestart(FootnoteProperties::RESTART_NUMBER_EACH_PAGE);
-
//And finaly, set it on the Section
$section->setFootnoteProperties($properties);
@@ -379,7 +378,7 @@ To be completed
Fields
------
-Currently the following fields are supported:
+Currently the following fields are supported:
- PAGE
- NUMPAGES
diff --git a/docs/general.rst b/docs/general.rst
index ae090f2d..09a23cee 100644
--- a/docs/general.rst
+++ b/docs/general.rst
@@ -167,7 +167,6 @@ Use mirror margins to set up facing pages for double-sided documents, such as bo
$phpWord->getSettings()->setMirrorMargins(true);
-
Spelling and grammatical checks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -191,7 +190,7 @@ You can also specify the status of the spell and grammar checks, marking spellin
Track Revisions
~~~~~~~~~~~~~~~
-Track changes can be activated using ``setTrackRevisions``, you can furture specify
+Track changes can be activated using ``setTrackRevisions``, you can furture specify
- Not to use move syntax, instead moved items will be seen as deleted in one place and added in another
- Not track formatting revisions
diff --git a/docs/installing.rst b/docs/installing.rst
index 37e4d379..4f407f54 100644
--- a/docs/installing.rst
+++ b/docs/installing.rst
@@ -51,7 +51,6 @@ Example:
}
}
-
Using samples
-------------
diff --git a/src/PhpWord/Reader/Word2007/AbstractPart.php b/src/PhpWord/Reader/Word2007/AbstractPart.php
index 6a48fd46..3d853e8f 100644
--- a/src/PhpWord/Reader/Word2007/AbstractPart.php
+++ b/src/PhpWord/Reader/Word2007/AbstractPart.php
@@ -307,7 +307,7 @@ abstract class AbstractPart
$styleNode = $xmlReader->getElement('w:pPr', $domNode);
$styleDefs = array(
- 'styleName' => array(self::READ_VALUE, 'w:pStyle'),
+ 'styleName' => array(self::READ_VALUE, array('w:pStyle', 'w:name')),
'alignment' => array(self::READ_VALUE, 'w:jc'),
'basedOn' => array(self::READ_VALUE, 'w:basedOn'),
'next' => array(self::READ_VALUE, 'w:next'),
@@ -349,9 +349,9 @@ abstract class AbstractPart
$styleNode = $xmlReader->getElement('w:rPr', $domNode);
$styleDefs = array(
'styleName' => array(self::READ_VALUE, 'w:rStyle'),
- 'name' => array(self::READ_VALUE, 'w:rFonts', 'w:ascii'),
+ 'name' => array(self::READ_VALUE, 'w:rFonts', array('w:ascii', 'w:hAnsi', 'w:eastAsia', 'w:cs')),
'hint' => array(self::READ_VALUE, 'w:rFonts', 'w:hint'),
- 'size' => array(self::READ_SIZE, 'w:sz'),
+ 'size' => array(self::READ_SIZE, array('w:sz', 'w:szCs')),
'color' => array(self::READ_VALUE, 'w:color'),
'underline' => array(self::READ_VALUE, 'w:u'),
'bold' => array(self::READ_TRUE, 'w:b'),
@@ -364,9 +364,7 @@ abstract class AbstractPart
'subScript' => array(self::READ_EQUAL, 'w:vertAlign', 'w:val', 'subscript'),
'fgColor' => array(self::READ_VALUE, 'w:highlight'),
'rtl' => array(self::READ_TRUE, 'w:rtl'),
- 'font-latin' => array(self::READ_VALUE, 'w:font', 'w:val'),
- 'font-eastAsia' => array(self::READ_VALUE, 'w:font', 'w:eastAsia'),
- 'font-bidi' => array(self::READ_VALUE, 'w:font', 'w:bidi'),
+ 'lang' => array(self::READ_VALUE, 'w:lang'),
);
return $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
@@ -400,6 +398,7 @@ abstract class AbstractPart
$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');
+ $styleDefs["border{$ucfSide}Style"] = array(self::READ_VALUE, "w:tblBorders/w:$side", 'w:val');
}
$style = $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
}
@@ -428,6 +427,54 @@ abstract class AbstractPart
return $this->readStyleDefs($xmlReader, $domNode, $styleDefs);
}
+ /**
+ * Returns the first child element found
+ *
+ * @param XMLReader $xmlReader
+ * @param \DOMElement $parentNode
+ * @param string|array $elements
+ * @return string|null
+ */
+ private function findPossibleElement(XMLReader $xmlReader, \DOMElement $parentNode = null, $elements)
+ {
+ if (is_array($elements)) {
+ //if element is an array, we take the first element that exists in the XML
+ foreach ($elements as $possibleElement) {
+ if ($xmlReader->elementExists($possibleElement, $parentNode)) {
+ return $possibleElement;
+ }
+ }
+ } else {
+ return $elements;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the first attribute found
+ *
+ * @param XMLReader $xmlReader
+ * @param \DOMElement $node
+ * @param string|array $attributes
+ * @return string|null
+ */
+ private function findPossibleAttribute(XMLReader $xmlReader, \DOMElement $node, $attributes)
+ {
+ //if attribute is an array, we take the first attribute that exists in the XML
+ if (is_array($attributes)) {
+ foreach ($attributes as $possibleAttribute) {
+ if ($xmlReader->getAttribute($possibleAttribute, $node)) {
+ return $possibleAttribute;
+ }
+ }
+ } else {
+ return $attributes;
+ }
+
+ return null;
+ }
+
/**
* Read style definition
*
@@ -442,11 +489,18 @@ abstract class AbstractPart
$styles = array();
foreach ($styleDefs as $styleProp => $styleVal) {
- @list($method, $element, $attribute, $expected) = $styleVal;
+ list($method, $element, $attribute, $expected) = array_pad($styleVal, 4, null);
+
+ $element = $this->findPossibleElement($xmlReader, $parentNode, $element);
+ if ($element === null) {
+ continue;
+ }
if ($xmlReader->elementExists($element, $parentNode)) {
$node = $xmlReader->getElement($element, $parentNode);
+ $attribute = $this->findPossibleAttribute($xmlReader, $node, $attribute);
+
// Use w:val as default if no attribute assigned
$attribute = ($attribute === null) ? 'w:val' : $attribute;
$attributeValue = $xmlReader->getAttribute($attribute, $node);
diff --git a/src/PhpWord/Reader/Word2007/Settings.php b/src/PhpWord/Reader/Word2007/Settings.php
index ccdbed25..581a546d 100644
--- a/src/PhpWord/Reader/Word2007/Settings.php
+++ b/src/PhpWord/Reader/Word2007/Settings.php
@@ -80,8 +80,8 @@ class Settings extends AbstractPart
$themeFontLang = new Language();
$themeFontLang->setLatin($val);
- $themeFontLang->setLatin($eastAsia);
- $themeFontLang->setLatin($bidi);
+ $themeFontLang->setEastAsia($eastAsia);
+ $themeFontLang->setBidirectional($bidi);
$phpWord->getSettings()->setThemeFontLang($themeFontLang);
}
diff --git a/src/PhpWord/Reader/Word2007/Styles.php b/src/PhpWord/Reader/Word2007/Styles.php
index b8e6f22b..c6e64e45 100644
--- a/src/PhpWord/Reader/Word2007/Styles.php
+++ b/src/PhpWord/Reader/Word2007/Styles.php
@@ -19,6 +19,7 @@ namespace PhpOffice\PhpWord\Reader\Word2007;
use PhpOffice\Common\XMLReader;
use PhpOffice\PhpWord\PhpWord;
+use PhpOffice\PhpWord\Style\Language;
/**
* Styles reader
@@ -37,6 +38,28 @@ class Styles extends AbstractPart
$xmlReader = new XMLReader();
$xmlReader->getDomFromZip($this->docFile, $this->xmlFile);
+ $fontDefaults = $xmlReader->getElement('w:docDefaults/w:rPrDefault');
+ if ($fontDefaults !== null) {
+ $fontDefaultStyle = $this->readFontStyle($xmlReader, $fontDefaults);
+ if (array_key_exists('name', $fontDefaultStyle)) {
+ $phpWord->setDefaultFontName($fontDefaultStyle['name']);
+ }
+ if (array_key_exists('size', $fontDefaultStyle)) {
+ $phpWord->setDefaultFontSize($fontDefaultStyle['size']);
+ }
+ if (array_key_exists('lang', $fontDefaultStyle)) {
+ $phpWord->getSettings()->setThemeFontLang(new Language($fontDefaultStyle['lang']));
+ }
+ }
+
+ $paragraphDefaults = $xmlReader->getElement('w:docDefaults/w:pPrDefault');
+ if ($paragraphDefaults !== null) {
+ $paragraphDefaultStyle = $this->readParagraphStyle($xmlReader, $paragraphDefaults);
+ if ($paragraphDefaultStyle != null) {
+ $phpWord->setDefaultParagraphStyle();
+ }
+ }
+
$nodes = $xmlReader->getElements('w:style');
if ($nodes->length > 0) {
foreach ($nodes as $node) {
diff --git a/src/PhpWord/Style/Paragraph.php b/src/PhpWord/Style/Paragraph.php
index c00dc97c..7e40d9e4 100644
--- a/src/PhpWord/Style/Paragraph.php
+++ b/src/PhpWord/Style/Paragraph.php
@@ -20,7 +20,6 @@ namespace PhpOffice\PhpWord\Style;
use PhpOffice\Common\Text;
use PhpOffice\PhpWord\Exception\InvalidStyleException;
use PhpOffice\PhpWord\SimpleType\Jc;
-use PhpOffice\PhpWord\SimpleType\LineSpacingRule;
use PhpOffice\PhpWord\SimpleType\TextAlignment;
/**
diff --git a/src/PhpWord/Writer/Word2007/Element/Shape.php b/src/PhpWord/Writer/Word2007/Element/Shape.php
index e384db06..9f111293 100644
--- a/src/PhpWord/Writer/Word2007/Element/Shape.php
+++ b/src/PhpWord/Writer/Word2007/Element/Shape.php
@@ -154,12 +154,12 @@ class Shape extends AbstractElement
case 'arc':
case 'line':
$points = explode(' ', $value);
- @list($start, $end) = $points;
+ list($start, $end) = array_pad($points, 2, null);
$points = array('start' => $start, 'end' => $end);
break;
case 'curve':
$points = explode(' ', $value);
- @list($start, $end, $point1, $point2) = $points;
+ list($start, $end, $point1, $point2) = array_pad($points, 4, null);
$points = array('start' => $start, 'end' => $end, 'point1' => $point1, 'point2' => $point2);
break;
}
diff --git a/src/PhpWord/Writer/Word2007/Part/Styles.php b/src/PhpWord/Writer/Word2007/Part/Styles.php
index 126cda4f..1cc94806 100644
--- a/src/PhpWord/Writer/Word2007/Part/Styles.php
+++ b/src/PhpWord/Writer/Word2007/Part/Styles.php
@@ -18,7 +18,6 @@
namespace PhpOffice\PhpWord\Writer\Word2007\Part;
use PhpOffice\Common\XMLWriter;
-use PhpOffice\PhpWord\Settings as PhpWordSettings;
use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Style\Font as FontStyle;
use PhpOffice\PhpWord\Style\Paragraph as ParagraphStyle;
@@ -82,9 +81,10 @@ class Styles extends AbstractPart
*/
private function writeDefaultStyles(XMLWriter $xmlWriter, $styles)
{
- $fontName = PhpWordSettings::getDefaultFontName();
- $fontSize = PhpWordSettings::getDefaultFontSize();
- $language = $this->getParentWriter()->getPhpWord()->getSettings()->getThemeFontLang();
+ $phpWord = $this->getParentWriter()->getPhpWord();
+ $fontName = $phpWord->getDefaultFontName();
+ $fontSize = $phpWord->getDefaultFontSize();
+ $language = $phpWord->getSettings()->getThemeFontLang();
$latinLanguage = ($language == null || $language->getLatin() === null) ? 'en-US' : $language->getLatin();
// Default font
@@ -123,7 +123,18 @@ class Styles extends AbstractPart
$xmlWriter->writeAttribute('w:val', 'Normal');
$xmlWriter->endElement(); // w:name
if (isset($styles['Normal'])) {
- $styleWriter = new ParagraphStyleWriter($xmlWriter, $styles['Normal']);
+ $normalStyle = $styles['Normal'];
+ // w:pPr
+ if ($normalStyle instanceof Fontstyle && $normalStyle->getParagraph() != null) {
+ $styleWriter = new ParagraphStyleWriter($xmlWriter, $normalStyle->getParagraph());
+ $styleWriter->write();
+ } elseif ($normalStyle instanceof ParagraphStyle) {
+ $styleWriter = new ParagraphStyleWriter($xmlWriter, $normalStyle);
+ $styleWriter->write();
+ }
+
+ // w:rPr
+ $styleWriter = new FontStyleWriter($xmlWriter, $normalStyle);
$styleWriter->write();
}
$xmlWriter->endElement(); // w:style
diff --git a/src/PhpWord/Writer/Word2007/Style/Paragraph.php b/src/PhpWord/Writer/Word2007/Style/Paragraph.php
index 424b87f8..8915fb4c 100644
--- a/src/PhpWord/Writer/Word2007/Style/Paragraph.php
+++ b/src/PhpWord/Writer/Word2007/Style/Paragraph.php
@@ -109,7 +109,7 @@ class Paragraph extends AbstractStyle
//Paragraph contextualSpacing
$xmlWriter->writeElementIf($styles['contextualSpacing'] === true, 'w:contextualSpacing');
- //Paragraph contextualSpacing
+ //Paragraph textAlignment
$xmlWriter->writeElementIf($styles['textAlignment'] !== null, 'w:textAlignment', 'w:val', $styles['textAlignment']);
// Child style: alignment, indentation, spacing, and shading
From 4c68ebbe9dcb887c049ff1569df170551c2d5d0e Mon Sep 17 00:00:00 2001
From: samimussbach
Date: Sat, 13 Jan 2018 10:03:53 +0100
Subject: [PATCH 087/176] Parse formatting inside HTML lists (#1239)
---
CHANGELOG.md | 1 +
README.md | 2 +-
docs/elements.rst | 4 +-
samples/Sample_26_Html.php | 39 +++++++++---
src/PhpWord/Element/AbstractContainer.php | 7 ++-
src/PhpWord/Shared/Html.php | 75 ++++++++++++++++------
tests/PhpWord/Shared/HtmlTest.php | 76 +++++++++++++++++++++--
7 files changed, 170 insertions(+), 34 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 62694eea..d6f8b2da 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@ v0.15.0 (?? ??? 2018)
----------------------
### Added
- Parsing of "align" HTML attribute - @troosan #1231
+- Parse formatting inside HTML lists - @troosan @samimussbach #1239 #945 #1215 #508
### Fixed
- fix reading of docx default style - @troosan #1238
diff --git a/README.md b/README.md
index 07b0d439..59fc3c44 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
PHPWord is a library written in pure PHP that provides a set of classes to write to and read from different document file formats. The current version of PHPWord supports Microsoft [Office Open XML](http://en.wikipedia.org/wiki/Office_Open_XML) (OOXML or OpenXML), OASIS [Open Document Format for Office Applications](http://en.wikipedia.org/wiki/OpenDocument) (OpenDocument or ODF), [Rich Text Format](http://en.wikipedia.org/wiki/Rich_Text_Format) (RTF), HTML, and PDF.
-PHPWord is an open source project licensed under the terms of [LGPL version 3](https://github.com/PHPOffice/PHPWord/blob/develop/COPYING.LESSER). PHPWord is aimed to be a high quality software product by incorporating [continuous integration](https://travis-ci.org/PHPOffice/PHPWord) and [unit testing](http://phpoffice.github.io/PHPWord/coverage/develop/). You can learn more about PHPWord by reading the [Developers' Documentation](http://phpword.readthedocs.org/) and the [API Documentation](http://phpoffice.github.io/PHPWord/docs/develop/).
+PHPWord is an open source project licensed under the terms of [LGPL version 3](https://github.com/PHPOffice/PHPWord/blob/develop/COPYING.LESSER). PHPWord is aimed to be a high quality software product by incorporating [continuous integration](https://travis-ci.org/PHPOffice/PHPWord) and [unit testing](http://phpoffice.github.io/PHPWord/coverage/develop/). You can learn more about PHPWord by reading the [Developers' Documentation](http://phpword.readthedocs.org/).
If you have any questions, please ask on [StackOverFlow](https://stackoverflow.com/questions/tagged/phpword)
diff --git a/docs/elements.rst b/docs/elements.rst
index 94ff2667..fe673304 100644
--- a/docs/elements.rst
+++ b/docs/elements.rst
@@ -405,8 +405,10 @@ For instance for the INDEX field, you can do the following (See `Index Field for
$fieldText->addText('My ');
$fieldText->addText('bold index', ['bold' => true]);
$fieldText->addText(' entry');
+ $section->addField('XE', array(), array(), $fieldText);
- $section->addField('INDEX', array(), array('\\e " " \\h "A" \\c "3"'), $fieldText);
+ //this actually adds the index
+ $section->addField('INDEX', array(), array('\\e " " \\h "A" \\c "3"'), 'right click to update index');
Line
----
diff --git a/samples/Sample_26_Html.php b/samples/Sample_26_Html.php
index b993f834..99a35f9c 100644
--- a/samples/Sample_26_Html.php
+++ b/samples/Sample_26_Html.php
@@ -9,25 +9,50 @@ $section = $phpWord->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
';
-$html .= 'List with complex content:
';
+$html .= 'Unordered (bulleted) list:
';
+$html .= '';
+
+$html .= 'Ordered (numbered) list:
';
+$html .= '
+ List 1 item 1
+ - List 1 item 2
+
+ - sub list 1
+ - sub list 2
+
+ - List 1 item 3
+
+ A second list, numbering should restart
+
+ - List 2 item 1
+ - List 2 item 2
+
+ - sub list 1
+ - sub list 2
+
+ - List 2 item 3
+
+ - sub list 1, restarts with a
+ - sub list 2
+
+
';
+
+$html .= 'List with formatted content:
';
$html .= '
-
- list item1
+ big list item1
-
- list item2
+ list item2 in bold
';
+$html .= 'A table with formatting:
';
$html .= '
diff --git a/src/PhpWord/Element/AbstractContainer.php b/src/PhpWord/Element/AbstractContainer.php
index b00424b7..28d45672 100644
--- a/src/PhpWord/Element/AbstractContainer.php
+++ b/src/PhpWord/Element/AbstractContainer.php
@@ -33,11 +33,10 @@ namespace PhpOffice\PhpWord\Element;
* @method CheckBox addCheckBox(string $name, $text, mixed $fStyle = null, mixed $pStyle = null)
* @method Title addTitle(string $text, int $depth = 1)
* @method TOC addTOC(mixed $fontStyle = null, mixed $tocStyle = null, int $minDepth = 1, int $maxDepth = 9)
- *
* @method PageBreak addPageBreak()
* @method Table addTable(mixed $style = null)
* @method Image addImage(string $source, mixed $style = null, bool $isWatermark = false)
- * @method \PhpOffice\PhpWord\Element\OLEObject addObject(string $source, mixed $style = null)
+ * @method OLEObject addOLEObject(string $source, mixed $style = null)
* @method TextBox addTextBox(mixed $style = null)
* @method Field addField(string $type = null, array $properties = array(), array $options = array(), mixed $text = null)
* @method Line addLine(mixed $lineStyle = null)
@@ -46,6 +45,8 @@ namespace PhpOffice\PhpWord\Element;
* @method FormField addFormField(string $type, mixed $fStyle = null, mixed $pStyle = null)
* @method SDT addSDT(string $type)
*
+ * @method \PhpOffice\PhpWord\Element\OLEObject addObject(string $source, mixed $style = null) deprecated, use addOLEObject instead
+ *
* @since 0.10.0
*/
abstract class AbstractContainer extends AbstractElement
@@ -200,7 +201,7 @@ abstract class AbstractContainer extends AbstractElement
'FormField' => $generalContainers,
'SDT' => $generalContainers,
'TrackChange' => $generalContainers,
- 'TextRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox', 'TrackChange'),
+ 'TextRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox', 'TrackChange', 'ListItemRun'),
'ListItem' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'),
'ListItemRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'),
'Table' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'),
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 38d326c1..fd0bd545 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -18,10 +18,10 @@
namespace PhpOffice\PhpWord\Shared;
use PhpOffice\PhpWord\Element\AbstractContainer;
-use PhpOffice\PhpWord\Element\Cell;
use PhpOffice\PhpWord\Element\Row;
use PhpOffice\PhpWord\Element\Table;
use PhpOffice\PhpWord\SimpleType\Jc;
+use PhpOffice\PhpWord\SimpleType\NumberFormat;
/**
* Common Html functions
@@ -30,6 +30,8 @@ use PhpOffice\PhpWord\SimpleType\Jc;
*/
class Html
{
+ private static $listIndex = 0;
+
/**
* Add HTML parts.
*
@@ -135,8 +137,8 @@ class Html
'tr' => array('Row', $node, $element, $styles, null, null, null),
'td' => array('Cell', $node, $element, $styles, null, null, null),
'th' => array('Cell', $node, $element, $styles, null, null, null),
- 'ul' => array('List', null, null, $styles, $data, 3, null),
- 'ol' => array('List', null, null, $styles, $data, 7, null),
+ 'ul' => array('List', $node, $element, $styles, $data, null, null),
+ 'ol' => array('List', $node, $element, $styles, $data, null, null),
'li' => array('ListItem', $node, $element, $styles, $data, null, null),
'img' => array('Image', $node, $element, $styles, null, null, null),
'br' => array('LineBreak', null, $element, $styles, null, null, null),
@@ -330,7 +332,7 @@ class Html
* @param \DOMNode $node
* @param \PhpOffice\PhpWord\Element\Table $element
* @param array &$styles
- * @return Cell $element
+ * @return \PhpOffice\PhpWord\Element\Cell $element
*/
private static function parseCell($node, $element, &$styles)
{
@@ -365,18 +367,56 @@ class Html
/**
* Parse list node
*
+ * @param \DOMNode $node
+ * @param \PhpOffice\PhpWord\Element\AbstractContainer $element
* @param array &$styles
* @param array &$data
- * @param string $argument1 List type
*/
- private static function parseList(&$styles, &$data, $argument1)
+ private static function parseList($node, $element, &$styles, &$data)
{
+ $isOrderedList = $node->nodeName == 'ol';
if (isset($data['listdepth'])) {
$data['listdepth']++;
} else {
$data['listdepth'] = 0;
+ $styles['list'] = 'listStyle_' . self::$listIndex++;
+ $element->getPhpWord()->addNumberingStyle($styles['list'], self::getListStyle($isOrderedList));
}
- $styles['list']['listType'] = $argument1;
+ }
+
+ private static function getListStyle($isOrderedList)
+ {
+ if ($isOrderedList) {
+ return array(
+ 'type' => 'multilevel',
+ 'levels' => array(
+ array('format' => NumberFormat::DECIMAL, 'text' => '%1.', 'alignment' => 'left', 'tabPos' => 720, 'left' => 720, 'hanging' => 360),
+ array('format' => NumberFormat::LOWER_LETTER, 'text' => '%2.', 'alignment' => 'left', 'tabPos' => 1440, 'left' => 1440, 'hanging' => 360),
+ array('format' => NumberFormat::LOWER_ROMAN, 'text' => '%3.', 'alignment' => 'right', 'tabPos' => 2160, 'left' => 2160, 'hanging' => 180),
+ array('format' => NumberFormat::DECIMAL, 'text' => '%4.', 'alignment' => 'left', 'tabPos' => 2880, 'left' => 2880, 'hanging' => 360),
+ array('format' => NumberFormat::LOWER_LETTER, 'text' => '%5.', 'alignment' => 'left', 'tabPos' => 3600, 'left' => 3600, 'hanging' => 360),
+ array('format' => NumberFormat::LOWER_ROMAN, 'text' => '%6.', 'alignment' => 'right', 'tabPos' => 4320, 'left' => 4320, 'hanging' => 180),
+ array('format' => NumberFormat::DECIMAL, 'text' => '%7.', 'alignment' => 'left', 'tabPos' => 5040, 'left' => 5040, 'hanging' => 360),
+ array('format' => NumberFormat::LOWER_LETTER, 'text' => '%8.', 'alignment' => 'left', 'tabPos' => 5760, 'left' => 5760, 'hanging' => 360),
+ array('format' => NumberFormat::LOWER_ROMAN, 'text' => '%9.', 'alignment' => 'right', 'tabPos' => 6480, 'left' => 6480, 'hanging' => 180),
+ ),
+ );
+ }
+
+ return array(
+ 'type' => 'hybridMultilevel',
+ 'levels' => array(
+ array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 720, 'left' => 720, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'),
+ array('format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 1440, 'left' => 1440, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'),
+ array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 2160, 'left' => 2160, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'),
+ array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 2880, 'left' => 2880, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'),
+ array('format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 3600, 'left' => 3600, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'),
+ array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 4320, 'left' => 4320, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'),
+ array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 5040, 'left' => 5040, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'),
+ array('format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 5760, 'left' => 5760, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'),
+ array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 6480, 'left' => 6480, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'),
+ ),
+ );
}
/**
@@ -394,17 +434,10 @@ class Html
{
$cNodes = $node->childNodes;
if (!empty($cNodes)) {
- $text = '';
+ $listRun = $element->addListItemRun($data['listdepth'], $styles['list'], $styles['paragraph']);
foreach ($cNodes as $cNode) {
- if ($cNode->nodeName == '#text') {
- $text = $cNode->nodeValue;
- }
+ self::parseNode($cNode, $listRun, $styles, $data);
}
- //ideally we should be parsing child nodes for any style, for now just take the text
- if ('' == trim($text) && '' != trim($node->textContent)) {
- $text = trim($node->textContent);
- }
- $element->addListItem($text, $data['listdepth'], $styles['font'], $styles['list'], $styles['paragraph']);
}
}
@@ -462,6 +495,12 @@ class Html
}
$styles['italic'] = $tValue;
break;
+ case 'margin-top':
+ $styles['spaceBefore'] = Converter::cssToPoint($cValue);
+ break;
+ case 'margin-bottom':
+ $styles['spaceAfter'] = Converter::cssToPoint($cValue);
+ break;
case 'border-color':
$styles['color'] = trim($cValue, '#');
break;
@@ -582,14 +621,14 @@ class Html
private static function mapAlign($cssAlignment)
{
switch ($cssAlignment) {
- case 'left':
- return Jc::START;
case 'right':
return Jc::END;
case 'center':
return Jc::CENTER;
case 'justify':
return Jc::BOTH;
+ default:
+ return Jc::START;
}
return null;
diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php
index c7d36470..9c4cfd55 100644
--- a/tests/PhpWord/Shared/HtmlTest.php
+++ b/tests/PhpWord/Shared/HtmlTest.php
@@ -35,7 +35,8 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
$content = '';
// Default
- $section = new Section(1);
+ $phpWord = new \PhpOffice\PhpWord\PhpWord();
+ $section = $phpWord->addSection();
$this->assertCount(0, $section->getElements());
// Heading
@@ -57,7 +58,7 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
$this->assertCount(7, $section->getElements());
// Other parts
- $section = new Section(1);
+ $section = $phpWord->addSection();
$content = '';
$content .= '';
$content .= '';
@@ -172,10 +173,11 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
{
$phpWord = new \PhpOffice\PhpWord\PhpWord();
$section = $phpWord->addSection();
- Html::addHtml($section, 'test
');
+ Html::addHtml($section, 'test
');
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:jc'));
+ $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:spacing'));
$this->assertEquals(Jc::CENTER, $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:pPr/w:jc', 'w:val'));
$this->assertEquals('single', $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:r/w:rPr/w:u', 'w:val'));
}
@@ -224,7 +226,7 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
- list item2
+ list item2
';
@@ -235,6 +237,69 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:t'));
$this->assertEquals('list item1', $doc->getElement('/w:document/w:body/w:p[1]/w:r/w:t')->nodeValue);
$this->assertEquals('list item2', $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:t')->nodeValue);
+ $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:rPr/w:b'));
+ }
+
+ /**
+ * Tests parsing of ul/li
+ */
+ public function tesOrderedListNumbering()
+ {
+ $phpWord = new \PhpOffice\PhpWord\PhpWord();
+ $section = $phpWord->addSection();
+ $html = '
+ - List 1 item 1
+ - List 1 item 2
+
+ Some Text
+
+ - List 2 item 1
+ - List 2 item 2
+
';
+ Html::addHtml($section, $html, false, false);
+
+ $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
+ echo $doc->printXml();
+ $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:numPr/w:numId'));
+ $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:t'));
+
+ $this->assertEquals('List 1 item 1', $doc->getElement('/w:document/w:body/w:p[1]/w:r/w:t')->nodeValue);
+ $this->assertEquals('List 2 item 1', $doc->getElement('/w:document/w:body/w:p[4]/w:r/w:t')->nodeValue);
+
+ $firstListnumId = $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:pPr/w:numPr/w:numId', 'w:val');
+ $secondListnumId = $doc->getElementAttribute('/w:document/w:body/w:p[4]/w:pPr/w:numPr/w:numId', 'w:val');
+
+ $this->assertNotEquals($firstListnumId, $secondListnumId);
+ }
+
+ /**
+ * Tests parsing of ul/li
+ */
+ public function testParseListWithFormat()
+ {
+ $phpWord = new \PhpOffice\PhpWord\PhpWord();
+ $section = $phpWord->addSection();
+ $html = preg_replace('/\s+/', ' ', '
+ - Some text before
+
+ list item1 bold with text after bold
+
+ and some after
+
+ -
+
+ list item2
+
+
+
');
+ Html::addHtml($section, $html, false, false);
+
+ $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
+ $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:numPr/w:numId'));
+ $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:t'));
+ $this->assertEquals('list item2', $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:t')->nodeValue);
+ $this->assertTrue($doc->elementExists('/w:document/w:body/w:p[1]/w:r[3]/w:rPr/w:b'));
+ $this->assertEquals('bold', $doc->getElement('/w:document/w:body/w:p[1]/w:r[3]/w:t')->nodeValue);
}
/**
@@ -255,6 +320,9 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
$this->assertEquals('with a linebreak.', $doc->getElement('/w:document/w:body/w:p/w:r[2]/w:t')->nodeValue);
}
+ /**
+ * Test parsing of img
+ */
public function testParseImage()
{
$src = __DIR__ . '/../_files/images/firefox.png';
From 8ed3cacfe8d6ddb9c44843ed981d0bede3a9aab9 Mon Sep 17 00:00:00 2001
From: Gabriel Caruso
Date: Tue, 16 Jan 2018 20:19:54 -0200
Subject: [PATCH 088/176] Refactoring
---
src/PhpWord/Writer/Word2007/Element/Container.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/PhpWord/Writer/Word2007/Element/Container.php b/src/PhpWord/Writer/Word2007/Element/Container.php
index 47dae29b..b6d1145c 100644
--- a/src/PhpWord/Writer/Word2007/Element/Container.php
+++ b/src/PhpWord/Writer/Word2007/Element/Container.php
@@ -46,7 +46,7 @@ class Container extends AbstractElement
return;
}
$containerClass = substr(get_class($container), strrpos(get_class($container), '\\') + 1);
- $withoutP = in_array($containerClass, array('TextRun', 'Footnote', 'Endnote', 'ListItemRun')) ? true : false;
+ $withoutP = in_array($containerClass, array('TextRun', 'Footnote', 'Endnote', 'ListItemRun'));
$xmlWriter = $this->getXmlWriter();
// Loop through elements
From 0425a25cdbbb820eded9473f4026ef32db57baa4 Mon Sep 17 00:00:00 2001
From: troosan
Date: Thu, 25 Jan 2018 23:24:21 +0100
Subject: [PATCH 089/176] Add parsing of HTML links
---
samples/Sample_26_Html.php | 2 ++
src/PhpWord/Shared/Html.php | 23 +++++++++++++++++++++++
tests/PhpWord/Shared/HtmlTest.php | 13 +++++++++++++
3 files changed, 38 insertions(+)
diff --git a/samples/Sample_26_Html.php b/samples/Sample_26_Html.php
index 99a35f9c..69d9d131 100644
--- a/samples/Sample_26_Html.php
+++ b/samples/Sample_26_Html.php
@@ -10,6 +10,8 @@ $html = 'Adding element via HTML
';
$html .= 'Some well formed HTML snippet needs to be used
';
$html .= 'With for example some1 inline formatting1
';
+$html .= 'A link to Read the docs
';
+
$html .= 'Unordered (bulleted) list:
';
$html .= '';
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index fd0bd545..0f5f446a 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -142,6 +142,7 @@ class Html
'li' => array('ListItem', $node, $element, $styles, $data, null, null),
'img' => array('Image', $node, $element, $styles, null, null, null),
'br' => array('LineBreak', null, $element, $styles, null, null, null),
+ 'a' => array('Link', $node, $element, $styles, null, null, null),
);
$newElement = null;
@@ -643,4 +644,26 @@ class Html
{
$element->addTextBreak();
}
+
+ /**
+ * Parse link node
+ *
+ * @param \DOMNode $node
+ * @param \PhpOffice\PhpWord\Element\AbstractContainer $element
+ * @param array $styles
+ */
+ private static function parseLink($node, $element, &$styles)
+ {
+ $target = null;
+ foreach ($node->attributes as $attribute) {
+ switch ($attribute->name) {
+ case 'href':
+ $target = $attribute->value;
+ break;
+ }
+ }
+ self::parseInlineStyle($node, $styles['font']);
+
+ return $element->addLink($target, $node->textContent, $styles['font'], $styles['paragraph']);
+ }
}
diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php
index 9c4cfd55..6122924f 100644
--- a/tests/PhpWord/Shared/HtmlTest.php
+++ b/tests/PhpWord/Shared/HtmlTest.php
@@ -341,4 +341,17 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
$this->assertStringMatchesFormat('%Smso-position-horizontal:right%S', $doc->getElementAttribute($baseXpath . '[1]/w:pict/v:shape', 'style'));
$this->assertStringMatchesFormat('%Smso-position-horizontal:left%S', $doc->getElementAttribute($baseXpath . '[2]/w:pict/v:shape', 'style'));
}
+
+ public function testParseLink()
+ {
+ $phpWord = new \PhpOffice\PhpWord\PhpWord();
+ $section = $phpWord->addSection();
+ $html = 'link text
';
+ Html::addHtml($section, $html);
+
+ $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
+
+ $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:hyperlink'));
+ $this->assertEquals('link text', $doc->getElement('/w:document/w:body/w:p/w:hyperlink/w:r/w:t')->nodeValue);
+ }
}
From 30183e28810391b66c07e8f896421449830a3558 Mon Sep 17 00:00:00 2001
From: Nicolas Dermine
Date: Fri, 26 Jan 2018 18:31:35 +0100
Subject: [PATCH 090/176] fix typo in comment
---
src/PhpWord/TemplateProcessor.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php
index c46038ee..5dd7b0bf 100644
--- a/src/PhpWord/TemplateProcessor.php
+++ b/src/PhpWord/TemplateProcessor.php
@@ -422,7 +422,7 @@ class TemplateProcessor
}
/*
- * Note: we do not use `rename` function here, because it looses file ownership data on Windows platform.
+ * Note: we do not use `rename` function here, because it loses 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
From caba7e238ff05c2cfd2a438d3f4cb77ee235623e Mon Sep 17 00:00:00 2001
From: Samuel Laulhau
Date: Wed, 31 Jan 2018 10:17:40 +0100
Subject: [PATCH 091/176] Delete VERSION
Delete VERSION file since it's outdated so not usefull anymore
---
VERSION | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 VERSION
diff --git a/VERSION b/VERSION
deleted file mode 100644
index 51de3305..00000000
--- a/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-0.13.0
\ No newline at end of file
From 8a9a4784d91da529ab327670f6712f719541491a Mon Sep 17 00:00:00 2001
From: Sami Mussbach
Date: Thu, 1 Feb 2018 13:58:08 +0100
Subject: [PATCH 092/176] add (failing) test and correct documentation sample
to valid HTML
---
samples/Sample_26_Html.php | 11 +++++----
tests/PhpWord/Shared/HtmlTest.php | 37 +++++++++++++++++++++++++++++++
2 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/samples/Sample_26_Html.php b/samples/Sample_26_Html.php
index 69d9d131..b05f8d08 100644
--- a/samples/Sample_26_Html.php
+++ b/samples/Sample_26_Html.php
@@ -29,10 +29,13 @@ $html .= '
- List 2 item 1
- List 2 item 2
-
- - sub list 1
- - sub list 2
-
+ -
+
+ - sub list 1
+ - sub list 2
+
+
+
- List 2 item 3
- sub list 1, restarts with a
diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php
index 6122924f..936c35f9 100644
--- a/tests/PhpWord/Shared/HtmlTest.php
+++ b/tests/PhpWord/Shared/HtmlTest.php
@@ -272,6 +272,43 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
$this->assertNotEquals($firstListnumId, $secondListnumId);
}
+ /**
+ * Tests parsing of nested ul/li
+ */
+ public function testOrderedNestedListNumbering()
+ {
+ $phpWord = new \PhpOffice\PhpWord\PhpWord();
+ $section = $phpWord->addSection();
+ $html = '
+ - List 1 item 1
+ - List 1 item 2
+
+ Some Text
+
+ - List 2 item 1
+ -
+
+ - sub list 1
+ - sub list 2
+
+
+
';
+ Html::addHtml($section, $html, false, false);
+
+ $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
+ echo $doc->printXml();
+ $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:numPr/w:numId'));
+ $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:t'));
+
+ $this->assertEquals('List 1 item 1', $doc->getElement('/w:document/w:body/w:p[1]/w:r/w:t')->nodeValue);
+ $this->assertEquals('List 2 item 1', $doc->getElement('/w:document/w:body/w:p[4]/w:r/w:t')->nodeValue);
+
+ $firstListnumId = $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:pPr/w:numPr/w:numId', 'w:val');
+ $secondListnumId = $doc->getElementAttribute('/w:document/w:body/w:p[4]/w:pPr/w:numPr/w:numId', 'w:val');
+
+ $this->assertNotEquals($firstListnumId, $secondListnumId);
+ }
+
/**
* Tests parsing of ul/li
*/
From e0096dba084c767582e15917bf5b357c790a5995 Mon Sep 17 00:00:00 2001
From: Sami Mussbach
Date: Thu, 1 Feb 2018 14:12:56 +0100
Subject: [PATCH 093/176] fix typo in test method name
---
tests/PhpWord/Shared/HtmlTest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php
index 6122924f..97a8fb15 100644
--- a/tests/PhpWord/Shared/HtmlTest.php
+++ b/tests/PhpWord/Shared/HtmlTest.php
@@ -243,7 +243,7 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
/**
* Tests parsing of ul/li
*/
- public function tesOrderedListNumbering()
+ public function testOrderedListNumbering()
{
$phpWord = new \PhpOffice\PhpWord\PhpWord();
$section = $phpWord->addSection();
From edd9617c03888ce4dd2e28c2fcacfad98194f4fc Mon Sep 17 00:00:00 2001
From: Nicolas Dermine
Date: Sun, 4 Feb 2018 08:42:38 +0100
Subject: [PATCH 094/176] fix typo
---
CONTRIBUTING.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 335ad2d5..e62f2e6f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -6,7 +6,7 @@ We want to create a high quality document writer and reader library that people
- **Be brief, but be bold**. State your issues briefly. But speak out your ideas loudly, even if you can't or don't know how to implement it right away. The world will be better with limitless innovations.
- **Follow PHP-FIG standards**. We follow PHP Standards Recommendations (PSRs) by [PHP Framework Interoperability Group](http://www.php-fig.org/). If you're not familiar with these standards, please, [familiarize yourself now](https://github.com/php-fig/fig-standards). Also, please, use [PHPCodeSniffer](http://pear.php.net/package/PHP_CodeSniffer/) to validate your code against PSRs.
-- **Test your code**. Nobody else knows your code better than you. So, it's completely yours mission to test the changes you made before pull request submission. We use [PHPUnit](https://phpunit.de/) for our testing purposes and recommend you using this tool too. [Here](https://phpunit.de/presentations.html) you can find PHPUnit best practices and additional information on effective unit testing, which helps us making PHPWord better day to day. Do not hesitate to smoke it carefully. It's a great investment in quality of your work, and it saves you years of life.
+- **Test your code**. Nobody else knows your code better than you. So, it's completely your mission to test the changes you made before pull request submission. We use [PHPUnit](https://phpunit.de/) for our testing purposes and recommend you using this tool too. [Here](https://phpunit.de/presentations.html) you can find PHPUnit best practices and additional information on effective unit testing, which helps us making PHPWord better day to day. Do not hesitate to smoke it carefully. It's a great investment in quality of your work, and it saves you years of life.
- **Request pull in separate branch**. Do not submit your request to the master branch. But create a separate branch named specifically for the issue that you addressed. Read [GitHub manual](https://help.github.com/articles/using-pull-requests) to find out more about this. If you are new to GitHub, read [this short manual](https://help.github.com/articles/fork-a-repo) to get yourself familiar with forks and how git works in general. [This video](http://www.youtube.com/watch?v=-zvHQXnBO6c) explains how to synchronize your Github Fork with the Branch of PHPWord.
That's it. Thank you for your interest in PHPWord, and welcome!
From 46a5f96d3b5104aaa6b016e5df0730e4382058e7 Mon Sep 17 00:00:00 2001
From: troosan
Date: Tue, 6 Feb 2018 23:16:32 +0100
Subject: [PATCH 095/176] fix parsing of table and p inside table cells
---
CHANGELOG.md | 2 ++
samples/Sample_26_Html.php | 16 +++++++--
src/PhpWord/Shared/Html.php | 38 +++++++++++++++++++++-
src/PhpWord/Writer/Word2007/Style/Font.php | 4 +++
tests/PhpWord/Shared/HtmlTest.php | 6 ++--
5 files changed, 60 insertions(+), 6 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d6f8b2da..fca2922c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,9 +8,11 @@ v0.15.0 (?? ??? 2018)
### Added
- Parsing of "align" HTML attribute - @troosan #1231
- Parse formatting inside HTML lists - @troosan @samimussbach #1239 #945 #1215 #508
+- Parsing of CSS `direction` instruction, HTML `lang` attribute, formatting inside table cell - @troosan #
### Fixed
- fix reading of docx default style - @troosan #1238
+- fix the size unit of when parsing html images - @troosan #1254
diff --git a/samples/Sample_26_Html.php b/samples/Sample_26_Html.php
index 69d9d131..d54d548c 100644
--- a/samples/Sample_26_Html.php
+++ b/samples/Sample_26_Html.php
@@ -7,11 +7,13 @@ $phpWord = new \PhpOffice\PhpWord\PhpWord();
$section = $phpWord->addSection();
$html = 'Adding element via HTML
';
-$html .= 'Some well formed HTML snippet needs to be used
';
+$html .= 'Some well-formed HTML snippet needs to be used
';
$html .= 'With for example some1 inline formatting1
';
$html .= 'A link to Read the docs
';
+$html .= 'היי, זה פסקה מימין לשמאל
';
+
$html .= 'Unordered (bulleted) list:
';
$html .= '';
@@ -65,10 +67,20 @@ $html .= '
| 1 | 2 |
- | 4 | 5 | 6 |
+ | This is bold text | | 6 |
';
+$html .= 'Table inside another table:
';
+$html .= '
+ |
+
+ |
+ | Cell in parent table |
+
';
+
\PhpOffice\PhpWord\Shared\Html::addHtml($section, $html, false, false);
// Save file
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 0f5f446a..2eeaae8b 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -31,6 +31,7 @@ use PhpOffice\PhpWord\SimpleType\NumberFormat;
class Html
{
private static $listIndex = 0;
+ private static $xpath;
/**
* Add HTML parts.
@@ -65,6 +66,7 @@ class Html
$dom = new \DOMDocument();
$dom->preserveWhiteSpace = $preserveWhiteSpace;
$dom->loadXML($html);
+ self::$xpath = new \DOMXpath($dom);
$node = $dom->getElementsByTagName('body');
self::parseNode($node->item(0), $element);
@@ -89,6 +91,10 @@ class Html
break;
case 'align':
$styles['alignment'] = self::mapAlign($attribute->value);
+ break;
+ case 'lang':
+ $styles['lang'] = $attribute->value;
+ break;
}
}
}
@@ -343,8 +349,33 @@ class Html
if (!empty($colspan)) {
$cellStyles['gridSpan'] = $colspan - 0;
}
+ $cell = $element->addCell(null, $cellStyles);
- return $element->addCell(null, $cellStyles);
+ if (self::shouldAddTextRun($node)) {
+ return $cell->addTextRun(self::parseInlineStyle($node, $styles['paragraph']));
+ }
+
+ return $cell;
+ }
+
+ /**
+ * Checks if $node contains an HTML element that cannot be added to TextRun
+ *
+ * @param \DOMNode $node
+ * @return bool Returns true if the node contains an HTML element that cannot be added to TextRun
+ */
+ private static function shouldAddTextRun(\DOMNode $node)
+ {
+ if (!$node->hasChildNodes()) {
+ return false;
+ }
+
+ $containsBlockElement = self::$xpath->query('.//table|./p', $node)->length > 0;
+ if ($containsBlockElement) {
+ return false;
+ }
+
+ return true;
}
/**
@@ -469,6 +500,9 @@ class Html
case 'text-align':
$styles['alignment'] = self::mapAlign($cValue);
break;
+ case 'direction':
+ $styles['rtl'] = $cValue === 'rtl';
+ break;
case 'font-size':
$styles['size'] = Converter::cssToPoint($cValue);
break;
@@ -556,10 +590,12 @@ class Html
case 'width':
$width = $attribute->value;
$style['width'] = $width;
+ $style['unit'] = \PhpOffice\PhpWord\Style\Image::UNIT_PX;
break;
case 'height':
$height = $attribute->value;
$style['height'] = $height;
+ $style['unit'] = \PhpOffice\PhpWord\Style\Image::UNIT_PX;
break;
case 'style':
$styleattr = explode(';', $attribute->value);
diff --git a/src/PhpWord/Writer/Word2007/Style/Font.php b/src/PhpWord/Writer/Word2007/Style/Font.php
index 9c2714dc..ecaad416 100644
--- a/src/PhpWord/Writer/Word2007/Style/Font.php
+++ b/src/PhpWord/Writer/Word2007/Style/Font.php
@@ -90,6 +90,10 @@ class Font extends AbstractStyle
$xmlWriter->writeAttributeIf($language->getLatin() !== null, 'w:val', $language->getLatin());
$xmlWriter->writeAttributeIf($language->getEastAsia() !== null, 'w:eastAsia', $language->getEastAsia());
$xmlWriter->writeAttributeIf($language->getBidirectional() !== null, 'w:bidi', $language->getBidirectional());
+ //if bidi is not set but we are writing RTL, write the latin language in the bidi tag
+ if ($style->isRTL() && $language->getBidirectional() === null && $language->getLatin() !== null) {
+ $xmlWriter->writeAttribute('w:bidi', $language->getLatin());
+ }
$xmlWriter->endElement();
}
diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php
index 97a8fb15..7d5f0b4c 100644
--- a/tests/PhpWord/Shared/HtmlTest.php
+++ b/tests/PhpWord/Shared/HtmlTest.php
@@ -259,7 +259,7 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
Html::addHtml($section, $html, false, false);
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
- echo $doc->printXml();
+
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:numPr/w:numId'));
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:t'));
@@ -336,8 +336,8 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
$baseXpath = '/w:document/w:body/w:p/w:r';
$this->assertTrue($doc->elementExists($baseXpath . '/w:pict/v:shape'));
- $this->assertStringMatchesFormat('%Swidth:150pt%S', $doc->getElementAttribute($baseXpath . '[1]/w:pict/v:shape', 'style'));
- $this->assertStringMatchesFormat('%Sheight:200pt%S', $doc->getElementAttribute($baseXpath . '[1]/w:pict/v:shape', 'style'));
+ $this->assertStringMatchesFormat('%Swidth:150px%S', $doc->getElementAttribute($baseXpath . '[1]/w:pict/v:shape', 'style'));
+ $this->assertStringMatchesFormat('%Sheight:200px%S', $doc->getElementAttribute($baseXpath . '[1]/w:pict/v:shape', 'style'));
$this->assertStringMatchesFormat('%Smso-position-horizontal:right%S', $doc->getElementAttribute($baseXpath . '[1]/w:pict/v:shape', 'style'));
$this->assertStringMatchesFormat('%Smso-position-horizontal:left%S', $doc->getElementAttribute($baseXpath . '[2]/w:pict/v:shape', 'style'));
}
From 47c837abef8f25fa0f28da352cd3e94c8be99ece Mon Sep 17 00:00:00 2001
From: troosan
Date: Tue, 6 Feb 2018 23:31:56 +0100
Subject: [PATCH 096/176] add unit tests
---
src/PhpWord/Shared/Html.php | 4 ----
tests/PhpWord/Shared/HtmlTest.php | 29 ++++++++++++++++++++++++++++-
2 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 2eeaae8b..e11d7390 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -366,10 +366,6 @@ class Html
*/
private static function shouldAddTextRun(\DOMNode $node)
{
- if (!$node->hasChildNodes()) {
- return false;
- }
-
$containsBlockElement = self::$xpath->query('.//table|./p', $node)->length > 0;
if ($containsBlockElement) {
return false;
diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php
index 7d5f0b4c..44fe97fc 100644
--- a/tests/PhpWord/Shared/HtmlTest.php
+++ b/tests/PhpWord/Shared/HtmlTest.php
@@ -150,6 +150,33 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
$this->assertEquals('15', $doc->getElementAttribute('/w:document/w:body/w:p[2]/w:r/w:rPr/w:sz', 'w:val'));
}
+ /**
+ * Test direction style
+ */
+ public function testParseTextDirection()
+ {
+ $phpWord = new \PhpOffice\PhpWord\PhpWord();
+ $section = $phpWord->addSection();
+ Html::addHtml($section, 'test');
+
+ $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
+ $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:rPr/w:rtl'));
+ }
+
+ /**
+ * Test html lang
+ */
+ public function testParseLang()
+ {
+ $phpWord = new \PhpOffice\PhpWord\PhpWord();
+ $section = $phpWord->addSection();
+ Html::addHtml($section, 'test');
+
+ $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
+ $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:rPr/w:lang'));
+ $this->assertEquals('fr-BE', $doc->getElementAttribute('/w:document/w:body/w:p/w:r/w:rPr/w:lang', 'w:val'));
+ }
+
/**
* Test font-family style
*/
@@ -199,7 +226,7 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
| 1 | 2 |
- | 4 | 5 | 6 |
+ | This is bold text | 5 | 6 |
';
Html::addHtml($section, $html);
From 46476d71014a1136810bdf7576724edc6e50fe30 Mon Sep 17 00:00:00 2001
From: troosan
Date: Wed, 7 Feb 2018 07:09:27 +0100
Subject: [PATCH 097/176] update phpdoc
---
src/PhpWord/Shared/Html.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index e11d7390..2a92ed2a 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -339,7 +339,7 @@ class Html
* @param \DOMNode $node
* @param \PhpOffice\PhpWord\Element\Table $element
* @param array &$styles
- * @return \PhpOffice\PhpWord\Element\Cell $element
+ * @return \PhpOffice\PhpWord\Element\Cell|\PhpOffice\PhpWord\Element\TextRun $element
*/
private static function parseCell($node, $element, &$styles)
{
From 33739ea21cbdc74b661ed8e9de42a75db7c6391e Mon Sep 17 00:00:00 2001
From: troosan
Date: Wed, 7 Feb 2018 21:39:01 +0100
Subject: [PATCH 098/176] cannot add list on textrun
---
src/PhpWord/Shared/Html.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 2a92ed2a..a3ea0cc0 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -366,7 +366,7 @@ class Html
*/
private static function shouldAddTextRun(\DOMNode $node)
{
- $containsBlockElement = self::$xpath->query('.//table|./p', $node)->length > 0;
+ $containsBlockElement = self::$xpath->query('.//table|./p|./ul|./li', $node)->length > 0;
if ($containsBlockElement) {
return false;
}
From 304173c4d78b65685e2e91654beccd573fe8b4ee Mon Sep 17 00:00:00 2001
From: troosan
Date: Thu, 8 Feb 2018 07:02:28 +0100
Subject: [PATCH 099/176] fix nested list
---
src/PhpWord/Element/AbstractElement.php | 13 +++++++++++++
src/PhpWord/Shared/Html.php | 7 +++++--
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/src/PhpWord/Element/AbstractElement.php b/src/PhpWord/Element/AbstractElement.php
index 63892b74..52279645 100644
--- a/src/PhpWord/Element/AbstractElement.php
+++ b/src/PhpWord/Element/AbstractElement.php
@@ -93,6 +93,13 @@ abstract class AbstractElement
*/
private $nestedLevel = 0;
+ /**
+ * A reference to the parent
+ *
+ * @var \PhpOffice\PhpWord\Element\AbstractElement
+ */
+ private $parent;
+
/**
* Parent container type
*
@@ -321,6 +328,11 @@ abstract class AbstractElement
$this->commentRangeEnd->setEndElement($this);
}
+ public function getParent()
+ {
+ return $this->parent;
+ }
+
/**
* Set parent container
*
@@ -331,6 +343,7 @@ abstract class AbstractElement
public function setParentContainer(AbstractElement $container)
{
$this->parentContainer = substr(get_class($container), strrpos(get_class($container), '\\') + 1);
+ $this->parent = $container;
// Set nested level
$this->nestedLevel = $container->getNestedLevel();
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index a3ea0cc0..971776ff 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -366,7 +366,7 @@ class Html
*/
private static function shouldAddTextRun(\DOMNode $node)
{
- $containsBlockElement = self::$xpath->query('.//table|./p|./ul|./li', $node)->length > 0;
+ $containsBlockElement = self::$xpath->query('.//table|./p|./ul|./ol', $node)->length > 0;
if ($containsBlockElement) {
return false;
}
@@ -402,7 +402,7 @@ class Html
*/
private static function parseList($node, $element, &$styles, &$data)
{
- $isOrderedList = $node->nodeName == 'ol';
+ $isOrderedList = $node->nodeName === 'ol';
if (isset($data['listdepth'])) {
$data['listdepth']++;
} else {
@@ -410,6 +410,9 @@ class Html
$styles['list'] = 'listStyle_' . self::$listIndex++;
$element->getPhpWord()->addNumberingStyle($styles['list'], self::getListStyle($isOrderedList));
}
+ if ($node->parentNode->nodeName === 'li') {
+ return $element->getParent();
+ }
}
private static function getListStyle($isOrderedList)
From 24f3463f9af8a4ade049d11202ee8e34bec92ec3 Mon Sep 17 00:00:00 2001
From: troosan
Date: Thu, 8 Feb 2018 07:18:02 +0100
Subject: [PATCH 100/176] remove output
---
CHANGELOG.md | 3 ++-
tests/PhpWord/Shared/HtmlTest.php | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fca2922c..21f4ec81 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,11 +8,12 @@ v0.15.0 (?? ??? 2018)
### Added
- Parsing of "align" HTML attribute - @troosan #1231
- Parse formatting inside HTML lists - @troosan @samimussbach #1239 #945 #1215 #508
-- Parsing of CSS `direction` instruction, HTML `lang` attribute, formatting inside table cell - @troosan #
+- Parsing of CSS `direction` instruction, HTML `lang` attribute, formatting inside table cell - @troosan #1273 #1252 #1254
### Fixed
- fix reading of docx default style - @troosan #1238
- fix the size unit of when parsing html images - @troosan #1254
+- fixed HTML parsing of nested lists - @troosan #1265
diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php
index b1ebf349..ac68b887 100644
--- a/tests/PhpWord/Shared/HtmlTest.php
+++ b/tests/PhpWord/Shared/HtmlTest.php
@@ -323,7 +323,7 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
Html::addHtml($section, $html, false, false);
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
- echo $doc->printXml();
+
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:numPr/w:numId'));
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:t'));
From 9cd5ab74330f9957b99c7d4634bf95fdb7e1a9e6 Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 9 Feb 2018 17:17:13 +0100
Subject: [PATCH 101/176] update changelog
---
CHANGELOG.md | 1 +
samples/Sample_26_Html.php | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 21f4ec81..3d3f60f3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ v0.15.0 (?? ??? 2018)
- fix reading of docx default style - @troosan #1238
- fix the size unit of when parsing html images - @troosan #1254
- fixed HTML parsing of nested lists - @troosan #1265
+- Save PNG alpha information when using remote images. @samsullivan #779
diff --git a/samples/Sample_26_Html.php b/samples/Sample_26_Html.php
index 6e505fdb..f6086357 100644
--- a/samples/Sample_26_Html.php
+++ b/samples/Sample_26_Html.php
@@ -37,7 +37,6 @@ $html .= '
- sub list 2
-
List 2 item 3
- sub list 1, restarts with a
From 604e60cae9cad0e5d114ddda0c88c6e2ebe93693 Mon Sep 17 00:00:00 2001
From: troosan
Date: Fri, 9 Feb 2018 21:49:11 +0100
Subject: [PATCH 102/176] Add support for Track changes (#1262)
* add changed information to HTML writer
* add missing writeFontStyle
* refactor track changes
* set the style
* update documentation and release note
* Update the changelog and doc
* fix scrutinizer issues
---
CHANGELOG.md | 1 +
docs/elements.rst | 41 +++++++++--
samples/Sample_16_Object.php | 2 +-
samples/Sample_39_TrackChanges.php | 29 ++++++++
samples/resources/Sample_11_ReadWord2007.docx | Bin 67881 -> 63320 bytes
samples/resources/Sample_24_ReadODText.odt | Bin 11952 -> 11540 bytes
src/PhpWord/Element/AbstractContainer.php | 4 +-
src/PhpWord/Element/AbstractElement.php | 39 +++++++++++
src/PhpWord/Element/Comment.php | 4 +-
src/PhpWord/Element/TrackChange.php | 31 ++++++++-
src/PhpWord/Reader/ODText/Content.php | 50 +++++++++++++-
src/PhpWord/Reader/Word2007/AbstractPart.php | 27 +++++++-
src/PhpWord/Reader/Word2007/Styles.php | 2 +-
src/PhpWord/Writer/HTML/Element/Text.php | 65 ++++++++++++++++++
src/PhpWord/Writer/ODText/Element/Text.php | 60 +++++++++++-----
src/PhpWord/Writer/ODText/Part/Content.php | 55 +++++++++++++++
src/PhpWord/Writer/Word2007/Element/Text.php | 54 ++++++++++++++-
tests/PhpWord/Element/TrackChangeTest.php | 44 ++++++++++++
tests/PhpWord/Shared/HtmlTest.php | 2 +-
tests/PhpWord/Writer/Word2007/ElementTest.php | 18 ++++-
20 files changed, 488 insertions(+), 40 deletions(-)
create mode 100644 samples/Sample_39_TrackChanges.php
create mode 100644 tests/PhpWord/Element/TrackChangeTest.php
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d6f8b2da..f82759dc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ v0.15.0 (?? ??? 2018)
### Added
- Parsing of "align" HTML attribute - @troosan #1231
- Parse formatting inside HTML lists - @troosan @samimussbach #1239 #945 #1215 #508
+- Add support for Track changes @Cip @troosan #354 #1262
### Fixed
- fix reading of docx default style - @troosan #1238
diff --git a/docs/elements.rst b/docs/elements.rst
index fe673304..7df3b163 100644
--- a/docs/elements.rst
+++ b/docs/elements.rst
@@ -31,7 +31,7 @@ column shows the containers while the rows lists the elements.
+-------+-----------------+-----------+----------+----------+---------+------------+------------+
| 11 | Watermark | - | v | - | - | - | - |
+-------+-----------------+-----------+----------+----------+---------+------------+------------+
-| 12 | Object | v | v | v | v | v | v |
+| 12 | OLEObject | v | v | v | v | v | v |
+-------+-----------------+-----------+----------+----------+---------+------------+------------+
| 13 | TOC | v | - | - | - | - | - |
+-------+-----------------+-----------+----------+----------+---------+------------+------------+
@@ -77,6 +77,13 @@ italics, etc) or other elements, e.g. images or links. The syntaxes are as follo
For available styling options see :ref:`font-style` and :ref:`paragraph-style`.
+If you want to enable track changes on added text you can mark it as INSERTED or DELETED by a specific user at a given time:
+
+.. code-block:: php
+
+ $text = $section->addText('Hello World!');
+ $text->setChanged(\PhpOffice\PhpWord\Element\ChangedElement::TYPE_INSERTED, 'Fred', (new \DateTime()));
+
Titles
~~~~~~
@@ -276,11 +283,11 @@ Objects
-------
You can add OLE embeddings, such as Excel spreadsheets or PowerPoint
-presentations to the document by using ``addObject`` method.
+presentations to the document by using ``addOLEObject`` method.
.. code-block:: php
- $section->addObject($src, [$style]);
+ $section->addOLEObject($src, [$style]);
Table of contents
-----------------
@@ -309,7 +316,7 @@ Footnotes & endnotes
You can create footnotes with ``addFootnote`` and endnotes with
``addEndnote`` in texts or textruns, but it's recommended to use textrun
to have better layout. You can use ``addText``, ``addLink``,
-``addTextBreak``, ``addImage``, ``addObject`` on footnotes and endnotes.
+``addTextBreak``, ``addImage``, ``addOLEObject`` on footnotes and endnotes.
On textrun:
@@ -465,4 +472,28 @@ The comment can contain formatted text. Once the comment has been added, it can
// link the comment to the text you just created
$text->setCommentStart($comment);
-If no end is set for a comment using the ``setCommentEnd``, the comment will be ended automatically at the end of the element it is started on.
\ No newline at end of file
+If no end is set for a comment using the ``setCommentEnd``, the comment will be ended automatically at the end of the element it is started on.
+
+Track Changes
+-------------
+
+Track changes can be set on text elements. There are 2 ways to set the change information on an element.
+Either by calling the `setChangeInfo()`, or by setting the `TrackChange` instance on the element with `setTrackChange()`.
+
+.. code-block:: php
+ $phpWord = new \PhpOffice\PhpWord\PhpWord();
+
+ // New portrait section
+ $section = $phpWord->addSection();
+ $textRun = $section->addTextRun();
+
+ $text = $textRun->addText('Hello World! Time to ');
+
+ $text = $textRun->addText('wake ', array('bold' => true));
+ $text->setChangeInfo(TrackChange::INSERTED, 'Fred', time() - 1800);
+
+ $text = $textRun->addText('up');
+ $text->setTrackChange(new TrackChange(TrackChange::INSERTED, 'Fred'));
+
+ $text = $textRun->addText('go to sleep');
+ $text->setChangeInfo(TrackChange::DELETED, 'Barney', new \DateTime('@' . (time() - 3600)));
diff --git a/samples/Sample_16_Object.php b/samples/Sample_16_Object.php
index 8b05b9e8..c4db7f61 100644
--- a/samples/Sample_16_Object.php
+++ b/samples/Sample_16_Object.php
@@ -9,7 +9,7 @@ $phpWord = new \PhpOffice\PhpWord\PhpWord();
$section = $phpWord->addSection();
$section->addText('You can open this OLE object by double clicking on the icon:');
$section->addTextBreak(2);
-$section->addObject('resources/_sheet.xls');
+$section->addOLEObject('resources/_sheet.xls');
// Save file
echo write($phpWord, basename(__FILE__, '.php'), $writers);
diff --git a/samples/Sample_39_TrackChanges.php b/samples/Sample_39_TrackChanges.php
new file mode 100644
index 00000000..e6a30668
--- /dev/null
+++ b/samples/Sample_39_TrackChanges.php
@@ -0,0 +1,29 @@
+addSection();
+$textRun = $section->addTextRun();
+
+$text = $textRun->addText('Hello World! Time to ');
+
+$text = $textRun->addText('wake ', array('bold' => true));
+$text->setChangeInfo(TrackChange::INSERTED, 'Fred', time() - 1800);
+
+$text = $textRun->addText('up');
+$text->setTrackChange(new TrackChange(TrackChange::INSERTED, 'Fred'));
+
+$text = $textRun->addText('go to sleep');
+$text->setChangeInfo(TrackChange::DELETED, 'Barney', new \DateTime('@' . (time() - 3600)));
+
+// Save file
+echo write($phpWord, basename(__FILE__, '.php'), $writers);
+if (!CLI) {
+ include_once 'Sample_Footer.php';
+}
diff --git a/samples/resources/Sample_11_ReadWord2007.docx b/samples/resources/Sample_11_ReadWord2007.docx
index c9a50f485a3c8d942fc7982061c5905f342c7634..3faf6f1213f13e82466ef29200862498d2ef7014 100644
GIT binary patch
delta 52080
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 |