/** * Read styles.xml. * * @param \PhpOffice\PhpWord\PhpWord $phpWord * @return void */ public function read(PhpWord $phpWord) { $xmlReader = new XMLReader(); $xmlReader->getDomFromZip($this->docFile, $this->xmlFile); $nodes = $xmlReader->getElements('w:style'); if ($nodes->length > 0) { foreach ($nodes as $node) { $type = $xmlReader->getAttribute('w:type', $node); $name = $xmlReader->getAttribute('w:styleId', $node); if (is_null($name)) { $name = $xmlReader->getAttribute('w:val', $node, 'w:name'); } preg_match('/Heading(\\d)/', $name, $headingMatches); // $default = ($xmlReader->getAttribute('w:default', $node) == 1); switch ($type) { case 'paragraph': $paragraphStyle = $this->readParagraphStyle($xmlReader, $node); $fontStyle = $this->readFontStyle($xmlReader, $node); if (!empty($headingMatches)) { $phpWord->addTitleStyle($headingMatches[1], $fontStyle, $paragraphStyle); } else { if (empty($fontStyle)) { if (is_array($paragraphStyle)) { $phpWord->addParagraphStyle($name, $paragraphStyle); } } else { $phpWord->addFontStyle($name, $fontStyle, $paragraphStyle); } } break; case 'character': $fontStyle = $this->readFontStyle($xmlReader, $node); if (!empty($fontStyle)) { $phpWord->addFontStyle($name, $fontStyle); } break; case 'table': $tStyle = $this->readTableStyle($xmlReader, $node); if (!empty($tStyle)) { $phpWord->addTableStyle($name, $tStyle); } break; } } } }
/** * Read (footnotes|endnotes).xml. * * @param \PhpOffice\PhpWord\PhpWord $phpWord * @return void */ public function read(PhpWord $phpWord) { $getMethod = "get{$this->collection}"; $collection = $phpWord->{$getMethod}()->getItems(); $xmlReader = new XMLReader(); $xmlReader->getDomFromZip($this->docFile, $this->xmlFile); $nodes = $xmlReader->getElements('*'); if ($nodes->length > 0) { foreach ($nodes as $node) { $id = $xmlReader->getAttribute('w:id', $node); $type = $xmlReader->getAttribute('w:type', $node); // Avoid w:type "separator" and "continuationSeparator" // Only look for <footnote> or <endnote> without w:type attribute if (is_null($type) && isset($collection[$id])) { $element = $collection[$id]; $pNodes = $xmlReader->getElements('w:p/*', $node); foreach ($pNodes as $pNode) { $this->readRun($xmlReader, $pNode, $element, $this->collection); } $addMethod = "add{$this->element}"; $phpWord->{$addMethod}($element); } } } }
/** * Read (footnotes|endnotes).xml * * @param \PhpOffice\PhpWord\PhpWord $phpWord */ public function read(PhpWord &$phpWord) { $this->type = $this->type == 'endnotes' ? 'endnotes' : 'footnotes'; $collectionClass = 'PhpOffice\\PhpWord\\' . ucfirst($this->type); $collection = $collectionClass::getElements(); $xmlReader = new XMLReader(); $xmlReader->getDomFromZip($this->docFile, $this->xmlFile); $nodes = $xmlReader->getElements('*'); if ($nodes->length > 0) { foreach ($nodes as $node) { $id = $xmlReader->getAttribute('w:id', $node); $type = $xmlReader->getAttribute('w:type', $node); // Avoid w:type "separator" and "continuationSeparator" // Only look for <footnote> or <endnote> without w:type attribute if (is_null($type) && array_key_exists($id, $collection)) { $element = $collection[$id]; $pNodes = $xmlReader->getElements('w:p/*', $node); foreach ($pNodes as $pNode) { $this->readRun($xmlReader, $pNode, $element, $type); } $collectionClass::setElement($id, $element); } } } }
/** * Read all relationship files * * @param string $docFile * @return array */ private function readRelationships($docFile) { $rels = array(); $xmlFile = 'META-INF/manifest.xml'; $xmlReader = new XMLReader(); $xmlReader->getDomFromZip($docFile, $xmlFile); $nodes = $xmlReader->getElements('manifest:file-entry'); foreach ($nodes as $node) { $type = $xmlReader->getAttribute('manifest:media-type', $node); $target = $xmlReader->getAttribute('manifest:full-path', $node); $rels[] = array('type' => $type, 'target' => $target); } return $rels; }
/** * Read content.xml * * @param \PhpOffice\PhpWord\PhpWord $phpWord */ public function read(PhpWord &$phpWord) { $xmlReader = new XMLReader(); $xmlReader->getDomFromZip($this->docFile, $this->xmlFile); $nodes = $xmlReader->getElements('office:body/office:text/*'); if ($nodes->length > 0) { $section = $phpWord->addSection(); foreach ($nodes as $node) { // $styleName = $xmlReader->getAttribute('text:style-name', $node); switch ($node->nodeName) { case 'text:h': // Heading $depth = $xmlReader->getAttribute('text:outline-level', $node); $section->addTitle($node->nodeValue, $depth); break; case 'text:p': // Paragraph $section->addText($node->nodeValue); break; case 'text:list': // List $listItems = $xmlReader->getElements('text:list-item/text:p', $node); foreach ($listItems as $listItem) { // $listStyleName = $xmlReader->getAttribute('text:style-name', $listItem); $section->addListItem($listItem->nodeValue); } break; } } } }
/** * Read meta.xml * * @param \PhpOffice\PhpWord\PhpWord $phpWord * @todo Process property type */ public function read(PhpWord &$phpWord) { $xmlReader = new XMLReader(); $xmlReader->getDomFromZip($this->docFile, $this->xmlFile); $docProps = $phpWord->getDocumentProperties(); $metaNode = $xmlReader->getElement('office:meta'); // Standard properties $properties = array('title' => 'dc:title', 'subject' => 'dc:subject', 'description' => 'dc:description', 'keywords' => 'meta:keyword', 'creator' => 'meta:initial-creator', 'lastModifiedBy' => 'dc:creator'); foreach ($properties as $property => $path) { $method = "set{$property}"; $propertyNode = $xmlReader->getElement($path, $metaNode); if ($propertyNode !== null && method_exists($docProps, $method)) { $docProps->{$method}($propertyNode->nodeValue); } } // Custom properties $propertyNodes = $xmlReader->getElements('meta:user-defined', $metaNode); foreach ($propertyNodes as $propertyNode) { $property = $xmlReader->getAttribute('meta:name', $propertyNode); // Set category, company, and manager property if (in_array($property, array('Category', 'Company', 'Manager'))) { $method = "set{$property}"; $docProps->{$method}($propertyNode->nodeValue); // Set other custom properties } else { $docProps->setCustomProperty($property, $propertyNode->nodeValue); } } }
/** * Read numbering level definition from w:abstractNum and w:num * * @param integer $levelId * @return array */ private function readLevel(XMLReader $xmlReader, \DOMElement $subnode, $levelId) { $level = array(); $level['level'] = $levelId; $level['start'] = $xmlReader->getAttribute('w:val', $subnode, 'w:start'); $level['format'] = $xmlReader->getAttribute('w:val', $subnode, 'w:numFmt'); $level['restart'] = $xmlReader->getAttribute('w:val', $subnode, 'w:lvlRestart'); $level['suffix'] = $xmlReader->getAttribute('w:val', $subnode, 'w:suff'); $level['text'] = $xmlReader->getAttribute('w:val', $subnode, 'w:lvlText'); $level['align'] = $xmlReader->getAttribute('w:val', $subnode, 'w:lvlJc'); $level['tab'] = $xmlReader->getAttribute('w:pos', $subnode, 'w:pPr/w:tabs/w:tab'); $level['left'] = $xmlReader->getAttribute('w:left', $subnode, 'w:pPr/w:ind'); $level['hanging'] = $xmlReader->getAttribute('w:hanging', $subnode, 'w:pPr/w:ind'); $level['font'] = $xmlReader->getAttribute('w:ascii', $subnode, 'w:rPr/w:rFonts'); $level['hint'] = $xmlReader->getAttribute('w:hint', $subnode, 'w:rPr/w:rFonts'); foreach ($level as $key => $value) { if (is_null($value)) { unset($level[$key]); } } return $level; }
/** * Read custom document properties. * * @param \PhpOffice\PhpWord\PhpWord $phpWord * @return void */ public function read(PhpWord $phpWord) { $xmlReader = new XMLReader(); $xmlReader->getDomFromZip($this->docFile, $this->xmlFile); $docProps = $phpWord->getDocInfo(); $nodes = $xmlReader->getElements('*'); if ($nodes->length > 0) { foreach ($nodes as $node) { $propertyName = $xmlReader->getAttribute('name', $node); $attributeNode = $xmlReader->getElement('*', $node); $attributeType = $attributeNode->nodeName; $attributeValue = $attributeNode->nodeValue; $attributeValue = DocInfo::convertProperty($attributeValue, $attributeType); $attributeType = DocInfo::convertPropertyType($attributeType); $docProps->setCustomProperty($propertyName, $attributeValue, $attributeType); } } }
/** * Read style definition * * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $parentNode * @param array $styleDefs * @ignoreScrutinizerPatch * @return array */ protected function readStyleDefs(XMLReader $xmlReader, \DOMElement $parentNode = null, $styleDefs = array()) { $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); $styleValue = $this->readStyleDef($method, $attributeValue, $expected); if ($styleValue !== null) { $styles[$styleProp] = $styleValue; } } } return $styles; }
/** * Read w:p node * * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $node * @param \PhpOffice\PhpWord\Element\Section $section * * @todo <w:lastRenderedPageBreak> */ private function readWPNode(XMLReader $xmlReader, \DOMElement $node, Section &$section) { // Page break if ($xmlReader->getAttribute('w:type', $node, 'w:r/w:br') == 'page') { $section->addPageBreak(); // PageBreak } // Paragraph $this->readParagraph($xmlReader, $node, $section); // Section properties if ($xmlReader->elementExists('w:pPr/w:sectPr', $node)) { $sectPrNode = $xmlReader->getElement('w:pPr/w:sectPr', $node); if ($sectPrNode !== null) { $this->readWSectPrNode($xmlReader, $sectPrNode, $section); } $section = $this->phpWord->addSection(); } }
/** * Get relationship array * * @param string $docFile * @param string $xmlFile * @param string $targetPrefix * @return array */ private function getRels($docFile, $xmlFile, $targetPrefix = '') { $metaPrefix = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/'; $officePrefix = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/'; $rels = array(); $xmlReader = new XMLReader(); $xmlReader->getDomFromZip($docFile, $xmlFile); $nodes = $xmlReader->getElements('*'); foreach ($nodes as $node) { $rId = $xmlReader->getAttribute('Id', $node); $type = $xmlReader->getAttribute('Type', $node); $target = $xmlReader->getAttribute('Target', $node); // Remove URL prefixes from $type to make it easier to read $type = str_replace($metaPrefix, '', $type); $type = str_replace($officePrefix, '', $type); $docPart = str_replace('.xml', '', $target); // Do not add prefix to link source if (!in_array($type, array('hyperlink'))) { $target = $targetPrefix . $target; } // Push to return array $rels[$rId] = array('type' => $type, 'target' => $target, 'docPart' => $docPart); } ksort($rels); return $rels; }
/** * Read w:tcPr * * @return array|null */ private function readCellStyle(XMLReader $xmlReader, \DOMElement $domNode) { $style = null; $mapping = array('w:shd' => 'bgColor', 'w:vAlign' => 'valign', 'w:textDirection' => 'textDirection', 'w:gridSpan' => 'gridSpan', 'w:vMerge' => 'vMerge'); $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: $style[$property] = $xmlReader->getAttribute('w:val', $node); break; } } return $style; }
/** * Read w:tblPr * * @return string|array|null * @todo Capture w:tblStylePr w:type="firstRow" */ protected function readTableStyle(XMLReader $xmlReader, \DOMElement $domNode) { $style = null; $margins = array('top', 'left', 'bottom', 'right'); $borders = $margins + array('insideH', 'insideV'); if ($xmlReader->elementExists('w:tblPr', $domNode)) { if ($xmlReader->elementExists('w:tblPr/w:tblStyle', $domNode)) { $style = $xmlReader->getAttribute('w:val', $domNode, 'w:tblPr/w:tblStyle'); } else { $style = array(); $mapping = array('w:tblCellMar' => 'cellMargin', 'w:tblBorders' => 'border'); $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; } } } } return $style; }