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

View File

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

View File

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

View File

@ -453,25 +453,41 @@ abstract class AbstractPart
$attribute = ($attribute === null) ? 'w:val' : $attribute;
$attributeValue = $xmlReader->getAttribute($attribute, $node);
// Assign style value based on conversion model
if ($method == self::READ_VALUE) {
$styles[$styleProp] = $attributeValue;
} elseif ($method == self::READ_SIZE) {
$styles[$styleProp] = $attributeValue / 2;
} elseif ($method == self::READ_TRUE) {
$styles[$styleProp] = true;
} elseif ($method == self::READ_FALSE) {
$styles[$styleProp] = false;
} elseif ($method == self::READ_EQUAL && $attributeValue == $expected) {
$styles[$styleProp] = true;
$styleValue = $this->readStyleDef($method, $attributeValue, $expected);
if ($styleValue !== null) {
$styles[$styleProp] = $styleValue;
}
}
}
/** @var array $styles Type hint */
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
*

View File

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

View File

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

View File

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

View File

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

View File

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