Ei kuvausta

Word2007.php 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <?php
  2. /**
  3. * This file is part of PHPWord - A pure PHP library for reading and writing
  4. * word processing documents.
  5. *
  6. * PHPWord is free software distributed under the terms of the GNU Lesser
  7. * General Public License version 3 as published by the Free Software Foundation.
  8. *
  9. * For the full copyright and license information, please read the LICENSE
  10. * file that was distributed with this source code. For the full list of
  11. * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
  12. *
  13. * @link https://github.com/PHPOffice/PHPWord
  14. * @copyright 2010-2014 PHPWord contributors
  15. * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
  16. */
  17. namespace PhpOffice\PhpWord\Writer;
  18. use PhpOffice\PhpWord\Element\Section;
  19. use PhpOffice\PhpWord\Media;
  20. use PhpOffice\PhpWord\PhpWord;
  21. use PhpOffice\PhpWord\Shared\ZipArchive;
  22. /**
  23. * Word2007 writer
  24. */
  25. class Word2007 extends AbstractWriter implements WriterInterface
  26. {
  27. /**
  28. * Content types values
  29. *
  30. * @var array
  31. */
  32. private $contentTypes = array('default' => array(), 'override' => array());
  33. /**
  34. * Document relationship
  35. *
  36. * @var array
  37. */
  38. private $relationships = array();
  39. /**
  40. * Create new Word2007 writer
  41. *
  42. * @param \PhpOffice\PhpWord\PhpWord
  43. */
  44. public function __construct(PhpWord $phpWord = null)
  45. {
  46. // Assign PhpWord
  47. $this->setPhpWord($phpWord);
  48. // Create parts
  49. $this->parts = array(
  50. 'ContentTypes' => '[Content_Types].xml',
  51. 'Rels' => '_rels/.rels',
  52. 'DocPropsApp' => 'docProps/app.xml',
  53. 'DocPropsCore' => 'docProps/core.xml',
  54. 'DocPropsCustom' => 'docProps/custom.xml',
  55. 'RelsDocument' => 'word/_rels/document.xml.rels',
  56. 'Document' => 'word/document.xml',
  57. 'Styles' => 'word/styles.xml',
  58. 'Numbering' => 'word/numbering.xml',
  59. 'Settings' => 'word/settings.xml',
  60. 'WebSettings' => 'word/webSettings.xml',
  61. 'FontTable' => 'word/fontTable.xml',
  62. 'Theme' => 'word/theme/theme1.xml',
  63. 'RelsPart' => '',
  64. 'Header' => '',
  65. 'Footer' => '',
  66. 'Footnotes' => '',
  67. 'Endnotes' => '',
  68. 'Chart' => '',
  69. );
  70. foreach (array_keys($this->parts) as $partName) {
  71. $partClass = get_class($this) . '\\Part\\' . $partName;
  72. if (class_exists($partClass)) {
  73. /** @var \PhpOffice\PhpWord\Writer\Word2007\Part\AbstractPart $part Type hint */
  74. $part = new $partClass();
  75. $part->setParentWriter($this);
  76. $this->writerParts[strtolower($partName)] = $part;
  77. }
  78. }
  79. // Set package paths
  80. $this->mediaPaths = array('image' => 'word/media/', 'object' => 'word/embeddings/');
  81. }
  82. /**
  83. * Save document by name.
  84. *
  85. * @param string $filename
  86. * @return void
  87. */
  88. public function save($filename = null)
  89. {
  90. $filename = $this->getTempFile($filename);
  91. $zip = $this->getZipArchive($filename);
  92. $phpWord = $this->getPhpWord();
  93. // Content types
  94. $this->contentTypes['default'] = array(
  95. 'rels' => 'application/vnd.openxmlformats-package.relationships+xml',
  96. 'xml' => 'application/xml',
  97. );
  98. // Add section media files
  99. $sectionMedia = Media::getElements('section');
  100. if (!empty($sectionMedia)) {
  101. $this->addFilesToPackage($zip, $sectionMedia);
  102. $this->registerContentTypes($sectionMedia);
  103. foreach ($sectionMedia as $element) {
  104. $this->relationships[] = $element;
  105. }
  106. }
  107. // Add header/footer media files & relations
  108. $this->addHeaderFooterMedia($zip, 'header');
  109. $this->addHeaderFooterMedia($zip, 'footer');
  110. // Add header/footer contents
  111. $rId = Media::countElements('section') + 6; // @see Rels::writeDocRels for 6 first elements
  112. $sections = $phpWord->getSections();
  113. foreach ($sections as $section) {
  114. $this->addHeaderFooterContent($section, $zip, 'header', $rId);
  115. $this->addHeaderFooterContent($section, $zip, 'footer', $rId);
  116. }
  117. $this->addNotes($zip, $rId, 'footnote');
  118. $this->addNotes($zip, $rId, 'endnote');
  119. $this->addChart($zip, $rId);
  120. // Write parts
  121. foreach ($this->parts as $partName => $fileName) {
  122. if ($fileName != '') {
  123. $zip->addFromString($fileName, $this->getWriterPart($partName)->write());
  124. }
  125. }
  126. // Close zip archive and cleanup temp file
  127. $zip->close();
  128. $this->cleanupTempFile();
  129. }
  130. /**
  131. * Get content types
  132. *
  133. * @return array
  134. */
  135. public function getContentTypes()
  136. {
  137. return $this->contentTypes;
  138. }
  139. /**
  140. * Get content types
  141. *
  142. * @return array
  143. */
  144. public function getRelationships()
  145. {
  146. return $this->relationships;
  147. }
  148. /**
  149. * Add header/footer media files, e.g. footer1.xml.rels.
  150. *
  151. * @param \PhpOffice\PhpWord\Shared\ZipArchive $zip
  152. * @param string $docPart
  153. * @return void
  154. */
  155. private function addHeaderFooterMedia(ZipArchive $zip, $docPart)
  156. {
  157. $elements = Media::getElements($docPart);
  158. if (!empty($elements)) {
  159. foreach ($elements as $file => $media) {
  160. if (count($media) > 0) {
  161. if (!empty($media)) {
  162. $this->addFilesToPackage($zip, $media);
  163. $this->registerContentTypes($media);
  164. }
  165. /** @var \PhpOffice\PhpWord\Writer\Word2007\Part\AbstractPart $writerPart Type hint */
  166. $writerPart = $this->getWriterPart('relspart')->setMedia($media);
  167. $zip->addFromString("word/_rels/{$file}.xml.rels", $writerPart->write());
  168. }
  169. }
  170. }
  171. }
  172. /**
  173. * Add header/footer content.
  174. *
  175. * @param \PhpOffice\PhpWord\Element\Section &$section
  176. * @param \PhpOffice\PhpWord\Shared\ZipArchive $zip
  177. * @param string $elmType header|footer
  178. * @param integer &$rId
  179. * @return void
  180. */
  181. private function addHeaderFooterContent(Section &$section, ZipArchive $zip, $elmType, &$rId)
  182. {
  183. $getFunction = $elmType == 'header' ? 'getHeaders' : 'getFooters';
  184. $elmCount = ($section->getSectionId() - 1) * 3;
  185. $elements = $section->$getFunction();
  186. foreach ($elements as &$element) {
  187. /** @var \PhpOffice\PhpWord\Element\AbstractElement $element Type hint */
  188. $elmCount++;
  189. $element->setRelationId(++$rId);
  190. $elmFile = "{$elmType}{$elmCount}.xml"; // e.g. footer1.xml
  191. $this->contentTypes['override']["/word/$elmFile"] = $elmType;
  192. $this->relationships[] = array('target' => $elmFile, 'type' => $elmType, 'rID' => $rId);
  193. /** @var \PhpOffice\PhpWord\Writer\Word2007\Part\AbstractPart $writerPart Type hint */
  194. $writerPart = $this->getWriterPart($elmType)->setElement($element);
  195. $zip->addFromString("word/$elmFile", $writerPart->write());
  196. }
  197. }
  198. /**
  199. * Add footnotes/endnotes
  200. *
  201. * @param \PhpOffice\PhpWord\Shared\ZipArchive $zip
  202. * @param integer &$rId
  203. * @param string $noteType
  204. * @return void
  205. */
  206. private function addNotes(ZipArchive $zip, &$rId, $noteType = 'footnote')
  207. {
  208. $phpWord = $this->getPhpWord();
  209. $noteType = ($noteType == 'endnote') ? 'endnote' : 'footnote';
  210. $partName = "{$noteType}s";
  211. $method = 'get' . $partName;
  212. $collection = $phpWord->$method();
  213. // Add footnotes media files, relations, and contents
  214. /** @var \PhpOffice\PhpWord\Collection\AbstractCollection $collection Type hint */
  215. if ($collection->countItems() > 0) {
  216. $media = Media::getElements($noteType);
  217. $this->addFilesToPackage($zip, $media);
  218. $this->registerContentTypes($media);
  219. $this->contentTypes['override']["/word/{$partName}.xml"] = $partName;
  220. $this->relationships[] = array('target' => "{$partName}.xml", 'type' => $partName, 'rID' => ++$rId);
  221. // Write relationships file, e.g. word/_rels/footnotes.xml
  222. if (!empty($media)) {
  223. /** @var \PhpOffice\PhpWord\Writer\Word2007\Part\AbstractPart $writerPart Type hint */
  224. $writerPart = $this->getWriterPart('relspart')->setMedia($media);
  225. $zip->addFromString("word/_rels/{$partName}.xml.rels", $writerPart->write());
  226. }
  227. // Write content file, e.g. word/footnotes.xml
  228. $writerPart = $this->getWriterPart($partName)->setElements($collection->getItems());
  229. $zip->addFromString("word/{$partName}.xml", $writerPart->write());
  230. }
  231. }
  232. /**
  233. * Add chart.
  234. *
  235. * @param \PhpOffice\PhpWord\Shared\ZipArchive $zip
  236. * @param integer &$rId
  237. * @return void
  238. */
  239. private function addChart(ZipArchive $zip, &$rId)
  240. {
  241. $phpWord = $this->getPhpWord();
  242. $collection = $phpWord->getCharts();
  243. $index = 0;
  244. if ($collection->countItems() > 0) {
  245. foreach ($collection->getItems() as $chart) {
  246. $index++;
  247. $rId++;
  248. $filename = "charts/chart{$index}.xml";
  249. // ContentTypes.xml
  250. $this->contentTypes['override']["/word/{$filename}"] = 'chart';
  251. // word/_rels/document.xml.rel
  252. $this->relationships[] = array('target' => $filename, 'type' => 'chart', 'rID' => $rId);
  253. // word/charts/chartN.xml
  254. /** @var \PhpOffice\PhpWord\Element\Chart $chart */
  255. $chart->setRelationId($rId);
  256. $writerPart = $this->getWriterPart('Chart');
  257. $writerPart->setElement($chart);
  258. $zip->addFromString("word/{$filename}", $writerPart->write());
  259. }
  260. }
  261. }
  262. /**
  263. * Register content types for each media.
  264. *
  265. * @param array $media
  266. * @return void
  267. */
  268. private function registerContentTypes($media)
  269. {
  270. foreach ($media as $medium) {
  271. $mediumType = $medium['type'];
  272. if ($mediumType == 'image') {
  273. $extension = $medium['imageExtension'];
  274. if (!isset($this->contentTypes['default'][$extension])) {
  275. $this->contentTypes['default'][$extension] = $medium['imageType'];
  276. }
  277. } elseif ($mediumType == 'object') {
  278. if (!isset($this->contentTypes['default']['bin'])) {
  279. $this->contentTypes['default']['bin'] = 'application/vnd.openxmlformats-officedocument.oleObject';
  280. }
  281. }
  282. }
  283. }
  284. }