Refactor reader

This commit is contained in:
Ivan Lanin 2014-05-19 22:49:25 +07:00
parent f7bee23f65
commit 69a3a0f4f2
2 changed files with 135 additions and 207 deletions

View File

@ -22,9 +22,22 @@ use PhpOffice\PhpWord\Shared\XMLReader;
/** /**
* Abstract part reader * Abstract part reader
*
* This class is inherited by ODText reader
*/ */
abstract class AbstractPart abstract class AbstractPart
{ {
/**
* Conversion method
*
* @const int
*/
const READ_VALUE = 'attributeValue'; // Read attribute value
const READ_EQUAL = 'attributeEquals'; // Read `true` when attribute value equals specified value
const READ_TRUE = 'attributeTrue'; // Read `true` when element exists
const READ_FALSE = 'attributeFalse'; // Read `false` when element exists
const READ_SIZE = 'attributeMultiplyByTwo'; // Read special attribute value for Font::$size
/** /**
* Document file * Document file
* *
@ -145,58 +158,26 @@ abstract class AbstractPart
protected function readParagraphStyle(XMLReader $xmlReader, \DOMElement $domNode) protected function readParagraphStyle(XMLReader $xmlReader, \DOMElement $domNode)
{ {
if (!$xmlReader->elementExists('w:pPr', $domNode)) { if (!$xmlReader->elementExists('w:pPr', $domNode)) {
return ''; return null;
} }
$style = array(); $styleNode = $xmlReader->getElement('w:pPr', $domNode);
$mapping = array( $styleDefs = array(
'w:pStyle' => 'styleName', 'styleName' => array(self::READ_VALUE, 'w:pStyle'),
'w:ind' => 'indent', 'w:spacing' => 'spacing', 'align' => array(self::READ_VALUE, 'w:jc'),
'w:jc' => 'align', 'w:basedOn' => 'basedOn', 'w:next' => 'next', 'basedOn' => array(self::READ_VALUE, 'w:basedOn'),
'w:widowControl' => 'widowControl', 'w:keepNext' => 'keepNext', 'next' => array(self::READ_VALUE, 'w:next'),
'w:keepLines' => 'keepLines', 'w:pageBreakBefore' => 'pageBreakBefore', 'indent' => array(self::READ_VALUE, 'w:ind', 'w:left'),
'hanging' => array(self::READ_VALUE, 'w:ind', 'w:hanging'),
'spaceAfter' => array(self::READ_VALUE, 'w:spacing', 'w:after'),
'spaceBefore' => array(self::READ_VALUE, 'w:spacing', 'w:before'),
'widowControl' => array(self::READ_FALSE, 'w:widowControl'),
'keepNext' => array(self::READ_TRUE, 'w:keepNext'),
'keepLines' => array(self::READ_TRUE, 'w:keepLines'),
'pageBreakBefore' => array(self::READ_TRUE, 'w:pageBreakBefore'),
); );
$nodes = $xmlReader->getElements('w:pPr/*', $domNode); return $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
foreach ($nodes as $node) {
if (!array_key_exists($node->nodeName, $mapping)) {
continue;
}
$property = $mapping[$node->nodeName];
switch ($node->nodeName) {
case 'w:ind':
$style['indent'] = $xmlReader->getAttribute('w:left', $node);
$style['hanging'] = $xmlReader->getAttribute('w:hanging', $node);
break;
case 'w:spacing':
$style['spaceAfter'] = $xmlReader->getAttribute('w:after', $node);
$style['spaceBefore'] = $xmlReader->getAttribute('w:before', $node);
// Commented. Need to adjust the number when return value is null
// $style['spacing'] = $xmlReader->getAttribute('w:line', $node);
break;
case 'w:keepNext':
case 'w:keepLines':
case 'w:pageBreakBefore':
$style[$property] = true;
break;
case 'w:widowControl':
$style[$property] = false;
break;
case 'w:pStyle':
case 'w:jc':
case 'w:basedOn':
case 'w:next':
$style[$property] = $xmlReader->getAttribute('w:val', $node);
break;
}
}
return $style;
} }
/** /**
@ -204,7 +185,7 @@ abstract class AbstractPart
* *
* @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
* @param \DOMElement $domNode * @param \DOMElement $domNode
* @return array|null * @return array
*/ */
protected function readFontStyle(XMLReader $xmlReader, \DOMElement $domNode) protected function readFontStyle(XMLReader $xmlReader, \DOMElement $domNode)
{ {
@ -219,62 +200,26 @@ abstract class AbstractPart
return null; return null;
} }
$style = array(); $styleNode = $xmlReader->getElement('w:rPr', $domNode);
$mapping = array( $styleDefs = array(
'w:rStyle' => 'styleName', 'styleName' => array(self::READ_VALUE, 'w:rStyle'),
'w:b' => 'bold', 'w:i' => 'italic', 'w:color' => 'color', 'w:u' => 'underline', 'name' => array(self::READ_VALUE, 'w:rFonts', 'w:ascii'),
'w:strike' => 'strikethrough', 'w:dstrike' => 'doubleStrikethrough', 'hint' => array(self::READ_VALUE, 'w:rFonts', 'w:hint'),
'w:highlight' => 'fgColor', 'w:sz' => 'size', 'size' => array(self::READ_SIZE, 'w:sz'),
'w:rFonts' => 'name', 'w:vertAlign' => 'superScript', 'color' => array(self::READ_VALUE, 'w:color'),
'w:smallCaps' => 'smallCaps', 'w:caps' => 'allCaps', 'underline' => array(self::READ_VALUE, 'w:u'),
'bold' => array(self::READ_TRUE, 'w:b'),
'italic' => array(self::READ_TRUE, 'w:i'),
'strikethrough' => array(self::READ_TRUE, 'w:strike'),
'doubleStrikethrough' => array(self::READ_TRUE, 'w:dstrike'),
'smallCaps' => array(self::READ_TRUE, 'w:smallCaps'),
'allCaps' => array(self::READ_TRUE, 'w:caps'),
'superScript' => array(self::READ_EQUAL, 'w:vertAlign', 'w:val', 'superscript'),
'subScript' => array(self::READ_EQUAL, 'w:vertAlign', 'w:val', 'subscript'),
'fgColor' => array(self::READ_VALUE, 'w:highlight'),
); );
$nodes = $xmlReader->getElements('w:rPr/*', $domNode); return $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
foreach ($nodes as $node) {
if (!array_key_exists($node->nodeName, $mapping)) {
continue;
}
$property = $mapping[$node->nodeName];
switch ($node->nodeName) {
case 'w:rFonts':
$style['name'] = $xmlReader->getAttribute('w:ascii', $node);
$style['hint'] = $xmlReader->getAttribute('w:hint', $node);
break;
case 'w:b':
case 'w:i':
case 'w:strike':
case 'w:dstrike':
case 'w:smallCaps':
case 'w:caps':
$style[$property] = true;
break;
case 'w:rStyle':
case 'w:u':
case 'w:highlight':
case 'w:color':
$style[$property] = $xmlReader->getAttribute('w:val', $node);
break;
case 'w:sz':
$style[$property] = $xmlReader->getAttribute('w:val', $node) / 2;
break;
case 'w:vertAlign':
$style[$property] = $xmlReader->getAttribute('w:val', $node);
if ($style[$property] == 'superscript') {
$style['superScript'] = true;
} else {
$style['superScript'] = false;
$style['subScript'] = true;
}
break;
}
}
return $style;
} }
/** /**
@ -295,42 +240,64 @@ abstract class AbstractPart
if ($xmlReader->elementExists('w:tblPr/w:tblStyle', $domNode)) { if ($xmlReader->elementExists('w:tblPr/w:tblStyle', $domNode)) {
$style = $xmlReader->getAttribute('w:val', $domNode, 'w:tblPr/w:tblStyle'); $style = $xmlReader->getAttribute('w:val', $domNode, 'w:tblPr/w:tblStyle');
} else { } else {
$style = array(); $styleNode = $xmlReader->getElement('w:tblPr', $domNode);
$mapping = array( // $styleDefs['styleName'] = array(self::READ_VALUE, 'w:tblStyle');
'w:tblCellMar' => 'cellMargin', foreach ($margins as $side) {
'w:tblBorders' => 'border', $ucfSide = ucfirst($side);
); $styleDefs["cellMargin$ucfSide"] = array(self::READ_VALUE, "w:tblCellMar/w:$side", 'w:w');
$nodes = $xmlReader->getElements('w:tblPr/*', $domNode);
foreach ($nodes as $node) {
if (!array_key_exists($node->nodeName, $mapping)) {
continue;
}
// $property = $mapping[$node->nodeName];
switch ($node->nodeName) {
case 'w:tblCellMar':
foreach ($margins as $side) {
$ucfSide = ucfirst($side);
$style["cellMargin$ucfSide"] = $xmlReader->getAttribute('w:w', $node, "w:$side");
}
break;
case 'w:tblBorders':
foreach ($borders as $side) {
$ucfSide = ucfirst($side);
$style["border{$ucfSide}Size"] = $xmlReader->getAttribute('w:sz', $node, "w:$side");
$style["border{$ucfSide}Color"] = $xmlReader->getAttribute('w:color', $node, "w:$side");
}
break;
}
} }
foreach ($borders as $side) {
$ucfSide = ucfirst($side);
$styleDefs["border{$ucfSide}Size"] = array(self::READ_VALUE, "w:tblBorders/w:$side", 'w:sz');
$styleDefs["border{$ucfSide}Color"] = array(self::READ_VALUE, "w:tblBorders/w:$side", 'w:color');
}
$style = $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
} }
} }
return $style; return $style;
} }
/**
* Read style definition
*
* @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
* @param \DOMElement $domNode
* @param array $styleDefs
* @return array
*/
protected function readStyleDefs(XMLReader $xmlReader, \DOMElement $parentNode, $styleDefs)
{
$styles = array();
foreach ($styleDefs as $styleProp => $styleVal) {
@list($method, $element, $attribute, $expected) = $styleVal;
if ($xmlReader->elementExists($element, $parentNode)) {
$node = $xmlReader->getElement($element, $parentNode);
// Use w:val as default if no attribute assigned
$attribute = ($attribute === null) ? 'w:val' : $attribute;
$attributeValue = $xmlReader->getAttribute($attribute, $node);
// 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;
}
}
}
return $styles;
}
/** /**
* 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

@ -56,9 +56,7 @@ class Document extends AbstractPart
if (!is_null($settingsNode)) { if (!is_null($settingsNode)) {
$settings = $this->readSectionStyle($xmlReader, $settingsNode); $settings = $this->readSectionStyle($xmlReader, $settingsNode);
$section->setSettings($settings); $section->setSettings($settings);
if (!is_null($settings)) { $this->readHeaderFooter($settings, $section);
$this->readHeaderFooter($settings, $section);
}
} }
$section = $phpWord->addSection(); $section = $phpWord->addSection();
} }
@ -71,9 +69,7 @@ class Document extends AbstractPart
case 'w:sectPr': // Last section case 'w:sectPr': // Last section
$settings = $this->readSectionStyle($xmlReader, $node); $settings = $this->readSectionStyle($xmlReader, $node);
$section->setSettings($settings); $section->setSettings($settings);
if (!is_null($settings)) { $this->readHeaderFooter($settings, $section);
$this->readHeaderFooter($settings, $section);
}
break; break;
} }
} }
@ -273,61 +269,41 @@ class Document extends AbstractPart
* *
* @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
* @param \DOMElement $domNode * @param \DOMElement $domNode
* @return array|null * @return array
*/ */
private function readSectionStyle(XMLReader $xmlReader, \DOMElement $domNode) private function readSectionStyle(XMLReader $xmlReader, \DOMElement $domNode)
{ {
$ret = null; $styleDefs = array(
$mapping = array( 'breakType' => array(self::READ_VALUE, 'w:type'),
'w:type' => 'breakType', 'w:pgSz' => 'pageSize', 'pageSizeW' => array(self::READ_VALUE, 'w:pgSz', 'w:w'),
'w:pgMar' => 'pageMargin', 'w:cols' => 'columns', 'pageSizeH' => array(self::READ_VALUE, 'w:pgSz', 'w:h'),
'w:headerReference' => 'header', 'w:footerReference' => 'footer', 'orientation' => array(self::READ_VALUE, 'w:pgSz', 'w:orient'),
'colsNum' => array(self::READ_VALUE, 'w:cols', 'w:num'),
'colsSpace' => array(self::READ_VALUE, 'w:cols', 'w:space'),
'topMargin' => array(self::READ_VALUE, 'w:pgMar', 'w:top'),
'leftMargin' => array(self::READ_VALUE, 'w:pgMar', 'w:left'),
'bottomMargin' => array(self::READ_VALUE, 'w:pgMar', 'w:bottom'),
'rightMargin' => array(self::READ_VALUE, 'w:pgMar', 'w:right'),
'headerHeight' => array(self::READ_VALUE, 'w:pgMar', 'w:header'),
'footerHeight' => array(self::READ_VALUE, 'w:pgMar', 'w:footer'),
'gutter' => array(self::READ_VALUE, 'w:pgMar', 'w:gutter'),
); );
$styles = $this->readStyleDefs($xmlReader, $domNode, $styleDefs);
// Header and footer
// @todo Cleanup this part
$nodes = $xmlReader->getElements('*', $domNode); $nodes = $xmlReader->getElements('*', $domNode);
foreach ($nodes as $node) { foreach ($nodes as $node) {
if (!array_key_exists($node->nodeName, $mapping)) { if ($node->nodeName == 'w:headerReference' || $node->nodeName == 'w:footerReference') {
continue; $id = $xmlReader->getAttribute('r:id', $node);
} $styles['hf'][$id] = array(
$property = $mapping[$node->nodeName]; 'method' => str_replace('w:', '', str_replace('Reference', '', $node->nodeName)),
switch ($node->nodeName) { 'type' => $xmlReader->getAttribute('w:type', $node),
);
case 'w:type':
$ret['breakType'] = $xmlReader->getAttribute('w:val', $node);
break;
case 'w:pgSz':
$ret['pageSizeW'] = $xmlReader->getAttribute('w:w', $node);
$ret['pageSizeH'] = $xmlReader->getAttribute('w:h', $node);
$ret['orientation'] = $xmlReader->getAttribute('w:orient', $node);
break;
case 'w:pgMar':
$ret['topMargin'] = $xmlReader->getAttribute('w:top', $node);
$ret['leftMargin'] = $xmlReader->getAttribute('w:left', $node);
$ret['bottomMargin'] = $xmlReader->getAttribute('w:bottom', $node);
$ret['rightMargin'] = $xmlReader->getAttribute('w:right', $node);
$ret['headerHeight'] = $xmlReader->getAttribute('w:header', $node);
$ret['footerHeight'] = $xmlReader->getAttribute('w:footer', $node);
$ret['gutter'] = $xmlReader->getAttribute('w:gutter', $node);
break;
case 'w:cols':
$ret['colsNum'] = $xmlReader->getAttribute('w:num', $node);
$ret['colsSpace'] = $xmlReader->getAttribute('w:space', $node);
break;
case 'w:headerReference':
case 'w:footerReference':
$id = $xmlReader->getAttribute('r:id', $node);
$ret['hf'][$id] = array(
'method' => $property,
'type' => $xmlReader->getAttribute('w:type', $node),
);
break;
} }
} }
return $ret; return $styles;
} }
/** /**
@ -335,33 +311,18 @@ class Document extends AbstractPart
* *
* @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
* @param \DOMElement $domNode * @param \DOMElement $domNode
* @return array|null * @return array
*/ */
private function readCellStyle(XMLReader $xmlReader, \DOMElement $domNode) private function readCellStyle(XMLReader $xmlReader, \DOMElement $domNode)
{ {
$style = null; $styleDefs = array(
$mapping = array( 'valign' => array(self::READ_VALUE, 'w:vAlign'),
'w:shd' => 'bgColor', 'textDirection' => array(self::READ_VALUE, 'w:textDirection'),
'w:vAlign' => 'valign', 'w:textDirection' => 'textDirection', 'gridSpan' => array(self::READ_VALUE, 'w:gridSpan'),
'w:gridSpan' => 'gridSpan', 'w:vMerge' => 'vMerge', 'vMerge' => array(self::READ_VALUE, 'w:vMerge'),
'bgColor' => array(self::READ_VALUE, 'w:shd/w:fill'),
); );
$nodes = $xmlReader->getElements('*', $domNode);
foreach ($nodes as $node) {
if (!array_key_exists($node->nodeName, $mapping)) {
continue;
}
$property = $mapping[$node->nodeName];
switch ($node->nodeName) {
case 'w:shd':
$style['bgColor'] = $xmlReader->getAttribute('w:fill', $node);
break;
default: return $this->readStyleDefs($xmlReader, $domNode, $styleDefs);
$style[$property] = $xmlReader->getAttribute('w:val', $node);
break;
}
}
return $style;
} }
} }