Ivan Lanin 2014-05-24 13:48:27 +07:00
parent 5ff47f44e9
commit 1e9a498ca2
9 changed files with 166 additions and 122 deletions

View File

@ -56,38 +56,24 @@ abstract class AbstractContainer extends AbstractElement
// Get arguments // Get arguments
$args = func_get_args(); $args = func_get_args();
$argsCount = func_num_args();
$withoutP = in_array($this->container, array('TextRun', 'Footnote', 'Endnote', 'ListItemRun')); $withoutP = in_array($this->container, array('TextRun', 'Footnote', 'Endnote', 'ListItemRun'));
if ($withoutP && ($elementName == 'Text' || $elementName == 'PreserveText')) { if ($withoutP && ($elementName == 'Text' || $elementName == 'PreserveText')) {
$args[3] = null; $args[3] = null; // Remove paragraph style for texts in textrun
} }
// Create element dynamically // Create element using reflection
$reflection = new \ReflectionClass($elementClass);
$elementArgs = $args;
array_shift($elementArgs); // Shift an element off the beginning of array: the $elementName
/** @var \PhpOffice\PhpWord\Element\AbstractElement $element Type hint */ /** @var \PhpOffice\PhpWord\Element\AbstractElement $element Type hint */
if ($argsCount == 2) { // TextRun, TextBox, Table, Footnote, Endnote $element = $reflection->newInstanceArgs($elementArgs);
$element = new $elementClass($args[1]);
} elseif ($argsCount == 3) { // Object, TextBreak, Title
$element = new $elementClass($args[1], $args[2]);
} elseif ($argsCount == 4) { // PreserveText, Text, Image
$element = new $elementClass($args[1], $args[2], $args[3]);
} elseif ($argsCount == 5) { // CheckBox, Link, ListItemRun, TOC
$element = new $elementClass($args[1], $args[2], $args[3], $args[4]);
} elseif ($argsCount == 6) { // ListItem
$element = new $elementClass($args[1], $args[2], $args[3], $args[4], $args[5]);
} else { // Page Break
$element = new $elementClass();
}
// Set relation Id for media collection // Set relation Id for media collection
$mediaContainer = $this->getMediaContainer(); $mediaContainer = $this->getMediaContainer();
if (in_array($elementName, array('Link', 'Image', 'Object'))) { if (in_array($elementName, array('Link', 'Image', 'Object'))) {
if ($elementName == 'Image') { /** @var \PhpOffice\PhpWord\Element\Image $element Type hint */
/** @var \PhpOffice\PhpWord\Element\Image $element Type hint */ $image = ($elementName == 'Image') ? $element : null;
$rId = Media::addElement($mediaContainer, strtolower($elementName), $args[1], $element); $rId = Media::addElement($mediaContainer, strtolower($elementName), $args[1], $image);
} else {
$rId = Media::addElement($mediaContainer, strtolower($elementName), $args[1]);
}
$element->setRelationId($rId); $element->setRelationId($rId);
} }
if ($elementName == 'Object') { if ($elementName == 'Object') {

View File

@ -284,6 +284,7 @@ class Image extends AbstractElement
* *
* @param bool $base64 * @param bool $base64
* @return string|null * @return string|null
* @since 0.11.0
*/ */
public function getImageStringData($base64 = false) public function getImageStringData($base64 = false)
{ {
@ -291,6 +292,7 @@ class Image extends AbstractElement
$actualSource = null; $actualSource = null;
$imageBinary = null; $imageBinary = null;
$imageData = null; $imageData = null;
$isTemp = false;
// Get actual source from archive image or other source // Get actual source from archive image or other source
// Return null if not found // Return null if not found
@ -301,6 +303,7 @@ class Image extends AbstractElement
$zip = new ZipArchive(); $zip = new ZipArchive();
if ($zip->open($zipFilename) !== false) { if ($zip->open($zipFilename) !== false) {
if ($zip->locateName($imageFilename)) { if ($zip->locateName($imageFilename)) {
$isTemp = true;
$zip->extractTo(sys_get_temp_dir(), $imageFilename); $zip->extractTo(sys_get_temp_dir(), $imageFilename);
$actualSource = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $imageFilename; $actualSource = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $imageFilename;
} }
@ -313,7 +316,7 @@ class Image extends AbstractElement
return null; return null;
} }
// Read image binary data and convert to hex // Read image binary data and convert to hex/base64 string
if ($this->sourceType == self::SOURCE_GD) { if ($this->sourceType == self::SOURCE_GD) {
$imageResource = call_user_func($this->imageCreateFunc, $actualSource); $imageResource = call_user_func($this->imageCreateFunc, $actualSource);
ob_start(); ob_start();
@ -335,6 +338,11 @@ class Image extends AbstractElement
} }
} }
// Delete temporary file if necessary
if ($isTemp === true) {
@unlink($actualSource);
}
return $imageData; return $imageData;
} }

View File

@ -139,37 +139,53 @@ class Media
* Get media elements * Get media elements
* *
* @param string $container section|headerx|footerx|footnote|endnote * @param string $container section|headerx|footerx|footnote|endnote
* @param string $mediaType image|object|link * @param string $type image|object|link
* @return array * @return array
* @since 0.10.0 * @since 0.10.0
*/ */
public static function getElements($container, $mediaType = null) public static function getElements($container, $type = null)
{ {
$mediaElements = array(); $elements = array();
// If header/footer, search for headerx and footerx where x is number // If header/footer, search for headerx and footerx where x is number
if ($container == 'header' || $container == 'footer') { if ($container == 'header' || $container == 'footer') {
foreach (self::$elements as $key => $val) { foreach (self::$elements as $key => $val) {
if (substr($key, 0, 6) == $container) { if (substr($key, 0, 6) == $container) {
$mediaElements[$key] = $val; $elements[$key] = $val;
} }
} }
return $elements;
} else { } else {
if (!array_key_exists($container, self::$elements)) { if (!array_key_exists($container, self::$elements)) {
return $mediaElements; return $elements;
} }
foreach (self::$elements[$container] as $mediaKey => $mediaData) { return self::getElementsByType($container, $type);
if (!is_null($mediaType)) { }
if ($mediaType == $mediaData['type']) { }
$mediaElements[$mediaKey] = $mediaData;
} /**
} else { * Get elements by media type
$mediaElements[$mediaKey] = $mediaData; *
* @param string $container section|footnote|endnote
* @param string $type image|object|link
* @return array
* @since 0.11.0 Splitted from `getElements` to reduce complexity
*/
private static function getElementsByType($container, $type = null)
{
$elements = array();
foreach (self::$elements[$container] as $key => $data) {
if ($type !== null) {
if ($type == $data['type']) {
$elements[$key] = $data;
} }
} else {
$elements[$key] = $data;
} }
} }
return $mediaElements; return $elements;
} }
/** /**

View File

@ -453,25 +453,41 @@ abstract class AbstractPart
$attribute = ($attribute === null) ? 'w:val' : $attribute; $attribute = ($attribute === null) ? 'w:val' : $attribute;
$attributeValue = $xmlReader->getAttribute($attribute, $node); $attributeValue = $xmlReader->getAttribute($attribute, $node);
// Assign style value based on conversion model $styleValue = $this->readStyleDef($method, $attributeValue, $expected);
if ($method == self::READ_VALUE) { if ($styleValue !== null) {
$styles[$styleProp] = $attributeValue; $styles[$styleProp] = $styleValue;
} elseif ($method == self::READ_SIZE) {
$styles[$styleProp] = $attributeValue / 2;
} elseif ($method == self::READ_TRUE) {
$styles[$styleProp] = true;
} elseif ($method == self::READ_FALSE) {
$styles[$styleProp] = false;
} elseif ($method == self::READ_EQUAL && $attributeValue == $expected) {
$styles[$styleProp] = true;
} }
} }
} }
/** @var array $styles Type hint */
return $styles; return $styles;
} }
/**
* Return style definition based on conversion method
*
* @param string $method
* @param mixed $attributeValue
* @param mixed $expected
* @return mixed
*/
private function readStyleDef($method, $attributeValue, $expected)
{
$style = $attributeValue;
if ($method == self::READ_SIZE) {
$style = $attributeValue / 2;
} elseif ($method == self::READ_TRUE) {
$style = true;
} elseif ($method == self::READ_FALSE) {
$style = false;
} elseif ($method == self::READ_EQUAL && $attributeValue == $expected) {
$style = true;
}
return $style;
}
/** /**
* Returns the target of image, object, or link as stored in ::readMainRels * Returns the target of image, object, or link as stored in ::readMainRels
* *

View File

@ -214,7 +214,7 @@ class Html
*/ */
case 'li': case 'li':
$cNodes = $node->childNodes; $cNodes = $node->childNodes;
if (count($cNodes) > 0) { if ($cNodes->length > 0) {
$text = ''; $text = '';
foreach ($cNodes as $cNode) { foreach ($cNodes as $cNode) {
if ($cNode->nodeName == '#text') { if ($cNode->nodeName == '#text') {
@ -240,7 +240,7 @@ class Html
*/ */
if ($node->nodeName != 'li') { if ($node->nodeName != 'li') {
$cNodes = $node->childNodes; $cNodes = $node->childNodes;
if (count($cNodes) > 0) { if ($cNodes->length > 0) {
foreach ($cNodes as $cNode) { foreach ($cNodes as $cNode) {
self::parseNode($cNode, $newobject, $styles, $data); self::parseNode($cNode, $newobject, $styles, $data);
} }

View File

@ -37,15 +37,13 @@ class TextBreak extends Text
if (!$this->withoutP) { if (!$this->withoutP) {
$hasStyle = $element->hasStyle(); $hasStyle = $element->hasStyle();
$this->writeOpeningWP();
if ($hasStyle) { if ($hasStyle) {
$this->writeOpeningWP();
$xmlWriter->startElement('w:pPr'); $xmlWriter->startElement('w:pPr');
$this->writeFontStyle(); $this->writeFontStyle();
$xmlWriter->endElement(); // w:pPr $xmlWriter->endElement(); // w:pPr
$this->writeClosingWP();
} else {
$xmlWriter->writeElement('w:p');
} }
$this->writeClosingWP();
} else { } else {
$xmlWriter->writeElement('w:br'); $xmlWriter->writeElement('w:br');
} }

View File

@ -24,6 +24,8 @@ use PhpOffice\PhpWord\Style\NumberingLevel;
/** /**
* Word2007 numbering part writer: word/numbering.xml * Word2007 numbering part writer: word/numbering.xml
*
* @since 0.10.0
*/ */
class Numbering extends AbstractPart class Numbering extends AbstractPart
{ {
@ -97,12 +99,6 @@ class Numbering extends AbstractPart
*/ */
private function writeLevel(XMLWriter $xmlWriter, NumberingLevel $level) private function writeLevel(XMLWriter $xmlWriter, NumberingLevel $level)
{ {
$tabPos = $level->getTabPos();
$left = $level->getLeft();
$hanging = $level->getHanging();
$font = $level->getFont();
$hint = $level->getHint();
$xmlWriter->startElement('w:lvl'); $xmlWriter->startElement('w:lvl');
$xmlWriter->writeAttribute('w:ilvl', $level->getLevel()); $xmlWriter->writeAttribute('w:ilvl', $level->getLevel());
@ -124,48 +120,64 @@ class Numbering extends AbstractPart
} }
} }
// Paragraph styles // Paragraph & font styles
if (!is_null($tabPos) || !is_null($left) || !is_null($hanging)) { $this->writeParagraph($xmlWriter, $level);
$xmlWriter->startElement('w:pPr'); $this->writeFont($xmlWriter, $level);
if (!is_null($tabPos)) {
$xmlWriter->startElement('w:tabs');
$xmlWriter->startElement('w:tab');
$xmlWriter->writeAttribute('w:val', 'num');
$xmlWriter->writeAttribute('w:pos', $tabPos);
$xmlWriter->endElement(); // w:tab
$xmlWriter->endElement(); // w:tabs
}
if (!is_null($left) || !is_null($hanging)) {
$xmlWriter->startElement('w:ind');
if (!is_null($left)) {
$xmlWriter->writeAttribute('w:left', $left);
}
if (!is_null($hanging)) {
$xmlWriter->writeAttribute('w:hanging', $hanging);
}
$xmlWriter->endElement(); // w:ind
}
$xmlWriter->endElement(); // w:pPr
}
// Font styles
if (!is_null($font) || !is_null($hint)) {
$xmlWriter->startElement('w:rPr');
$xmlWriter->startElement('w:rFonts');
if (!is_null($font)) {
$xmlWriter->writeAttribute('w:ascii', $font);
$xmlWriter->writeAttribute('w:hAnsi', $font);
$xmlWriter->writeAttribute('w:cs', $font);
}
if (!is_null($hint)) {
$xmlWriter->writeAttribute('w:hint', $hint);
}
$xmlWriter->endElement(); // w:rFonts
$xmlWriter->endElement(); // w:rPr
}
$xmlWriter->endElement(); // w:lvl $xmlWriter->endElement(); // w:lvl
} }
/**
* Write level paragraph
*
* @since 0.11.0
* @todo Use paragraph style writer
*/
private function writeParagraph(XMLWriter $xmlWriter, NumberingLevel $level)
{
$tabPos = $level->getTabPos();
$left = $level->getLeft();
$hanging = $level->getHanging();
$xmlWriter->startElement('w:pPr');
$xmlWriter->startElement('w:tabs');
$xmlWriter->startElement('w:tab');
$xmlWriter->writeAttribute('w:val', 'num');
$xmlWriter->writeAttributeIf($tabPos !== null, 'w:pos', $tabPos);
$xmlWriter->writeAttribute('w:pos', $tabPos);
$xmlWriter->endElement(); // w:tab
$xmlWriter->endElement(); // w:tabs
$xmlWriter->startElement('w:ind');
$xmlWriter->writeAttributeIf($left !== null, 'w:left', $left);
$xmlWriter->writeAttributeIf($hanging !== null, 'w:hanging', $hanging);
$xmlWriter->endElement(); // w:ind
$xmlWriter->endElement(); // w:pPr
}
/**
* Write level font
*
* @since 0.11.0
* @todo Use font style writer
*/
private function writeFont(XMLWriter $xmlWriter, NumberingLevel $level)
{
$font = $level->getFont();
$hint = $level->getHint();
$xmlWriter->startElement('w:rPr');
$xmlWriter->startElement('w:rFonts');
$xmlWriter->writeAttributeIf($font !== null, 'w:ascii', $font);
$xmlWriter->writeAttributeIf($font !== null, 'w:hAnsi', $font);
$xmlWriter->writeAttributeIf($font !== null, 'w:cs', $font);
$xmlWriter->writeAttributeIf($hint !== null, 'w:hint', $hint);
$xmlWriter->endElement(); // w:rFonts
$xmlWriter->endElement(); // w:rPr
}
/** /**
* Get random hexadecimal number value * Get random hexadecimal number value
* *

View File

@ -50,43 +50,51 @@ class Rels extends AbstractPart
* Write relationships * Write relationships
* *
* @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter
* @param null|array $xmlRels * @param array $xmlRels
* @param null|array $mediaRels * @param array $mediaRels
* @param integer $relId * @param int $relId
*/ */
protected function writeRels(XMLWriter $xmlWriter, $xmlRels = null, $mediaRels = null, $relId = 1) protected function writeRels(XMLWriter $xmlWriter, $xmlRels = array(), $mediaRels = array(), $relId = 1)
{ {
$xmlWriter->startDocument('1.0', 'UTF-8', 'yes'); $xmlWriter->startDocument('1.0', 'UTF-8', 'yes');
$xmlWriter->startElement('Relationships'); $xmlWriter->startElement('Relationships');
$xmlWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); $xmlWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
// XML files relationships // XML files relationships
if (is_array($xmlRels)) { foreach ($xmlRels as $target => $type) {
foreach ($xmlRels as $target => $type) { $this->writeRel($xmlWriter, $relId++, $type, $target);
$this->writeRel($xmlWriter, $relId++, $type, $target);
}
} }
// Media relationships // Media relationships
if (is_array($mediaRels)) { foreach ($mediaRels as $mediaRel) {
$typePrefix = 'officeDocument/2006/relationships/'; $this->writeMediaRel($xmlWriter, $relId++, $mediaRel);
$typeMapping = array('image' => 'image', 'object' => 'oleObject', 'link' => 'hyperlink');
$targetPaths = array('image' => 'media/', 'object' => 'embeddings/');
foreach ($mediaRels as $mediaRel) {
$mediaType = $mediaRel['type'];
$type = array_key_exists($mediaType, $typeMapping) ? $typeMapping[$mediaType] : $mediaType;
$target = array_key_exists($mediaType, $targetPaths) ? $targetPaths[$mediaType] : '';
$target .= $mediaRel['target'];
$targetMode = ($type == 'hyperlink') ? 'External' : '';
$this->writeRel($xmlWriter, $relId++, $typePrefix . $type, $target, $targetMode);
}
} }
$xmlWriter->endElement(); // Relationships $xmlWriter->endElement(); // Relationships
} }
/**
* Write media relationships
*
* @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter
* @param int $relId
* @param array $mediaRel
*/
private function writeMediaRel(XMLWriter $xmlWriter, $relId, $mediaRel)
{
$typePrefix = 'officeDocument/2006/relationships/';
$typeMapping = array('image' => 'image', 'object' => 'oleObject', 'link' => 'hyperlink');
$targetMapping = array('image' => 'media/', 'object' => 'embeddings/');
$mediaType = $mediaRel['type'];
$type = array_key_exists($mediaType, $typeMapping) ? $typeMapping[$mediaType] : $mediaType;
$targetPrefix = array_key_exists($mediaType, $targetMapping) ? $targetMapping[$mediaType] : '';
$target = $mediaRel['target'];
$targetMode = ($type == 'hyperlink') ? 'External' : '';
$this->writeRel($xmlWriter, $relId, $typePrefix . $type, $targetPrefix . $target, $targetMode);
}
/** /**
* Write individual rels entry * Write individual rels entry
* *

View File

@ -39,7 +39,7 @@ class RelsPart extends Rels
public function write() public function write()
{ {
$xmlWriter = $this->getXmlWriter(); $xmlWriter = $this->getXmlWriter();
$this->writeRels($xmlWriter, null, $this->media); $this->writeRels($xmlWriter, array(), $this->media);
return $xmlWriter->getData(); return $xmlWriter->getData();
} }