Rels.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
  3. use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
  4. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  5. use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
  6. use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
  7. class Rels extends WriterPart
  8. {
  9. /**
  10. * Write relationships to XML format.
  11. *
  12. * @param Spreadsheet $spreadsheet
  13. *
  14. * @throws WriterException
  15. *
  16. * @return string XML Output
  17. */
  18. public function writeRelationships(Spreadsheet $spreadsheet)
  19. {
  20. // Create XML writer
  21. $objWriter = null;
  22. if ($this->getParentWriter()->getUseDiskCaching()) {
  23. $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
  24. } else {
  25. $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
  26. }
  27. // XML header
  28. $objWriter->startDocument('1.0', 'UTF-8', 'yes');
  29. // Relationships
  30. $objWriter->startElement('Relationships');
  31. $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
  32. $customPropertyList = $spreadsheet->getProperties()->getCustomProperties();
  33. if (!empty($customPropertyList)) {
  34. // Relationship docProps/app.xml
  35. $this->writeRelationship(
  36. $objWriter,
  37. 4,
  38. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties',
  39. 'docProps/custom.xml'
  40. );
  41. }
  42. // Relationship docProps/app.xml
  43. $this->writeRelationship(
  44. $objWriter,
  45. 3,
  46. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties',
  47. 'docProps/app.xml'
  48. );
  49. // Relationship docProps/core.xml
  50. $this->writeRelationship(
  51. $objWriter,
  52. 2,
  53. 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties',
  54. 'docProps/core.xml'
  55. );
  56. // Relationship xl/workbook.xml
  57. $this->writeRelationship(
  58. $objWriter,
  59. 1,
  60. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument',
  61. 'xl/workbook.xml'
  62. );
  63. // a custom UI in workbook ?
  64. if ($spreadsheet->hasRibbon()) {
  65. $this->writeRelationShip(
  66. $objWriter,
  67. 5,
  68. 'http://schemas.microsoft.com/office/2006/relationships/ui/extensibility',
  69. $spreadsheet->getRibbonXMLData('target')
  70. );
  71. }
  72. $objWriter->endElement();
  73. return $objWriter->getData();
  74. }
  75. /**
  76. * Write workbook relationships to XML format.
  77. *
  78. * @param Spreadsheet $spreadsheet
  79. *
  80. * @throws WriterException
  81. *
  82. * @return string XML Output
  83. */
  84. public function writeWorkbookRelationships(Spreadsheet $spreadsheet)
  85. {
  86. // Create XML writer
  87. $objWriter = null;
  88. if ($this->getParentWriter()->getUseDiskCaching()) {
  89. $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
  90. } else {
  91. $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
  92. }
  93. // XML header
  94. $objWriter->startDocument('1.0', 'UTF-8', 'yes');
  95. // Relationships
  96. $objWriter->startElement('Relationships');
  97. $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
  98. // Relationship styles.xml
  99. $this->writeRelationship(
  100. $objWriter,
  101. 1,
  102. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles',
  103. 'styles.xml'
  104. );
  105. // Relationship theme/theme1.xml
  106. $this->writeRelationship(
  107. $objWriter,
  108. 2,
  109. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme',
  110. 'theme/theme1.xml'
  111. );
  112. // Relationship sharedStrings.xml
  113. $this->writeRelationship(
  114. $objWriter,
  115. 3,
  116. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings',
  117. 'sharedStrings.xml'
  118. );
  119. // Relationships with sheets
  120. $sheetCount = $spreadsheet->getSheetCount();
  121. for ($i = 0; $i < $sheetCount; ++$i) {
  122. $this->writeRelationship(
  123. $objWriter,
  124. ($i + 1 + 3),
  125. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet',
  126. 'worksheets/sheet' . ($i + 1) . '.xml'
  127. );
  128. }
  129. // Relationships for vbaProject if needed
  130. // id : just after the last sheet
  131. if ($spreadsheet->hasMacros()) {
  132. $this->writeRelationShip(
  133. $objWriter,
  134. ($i + 1 + 3),
  135. 'http://schemas.microsoft.com/office/2006/relationships/vbaProject',
  136. 'vbaProject.bin'
  137. );
  138. ++$i; //increment i if needed for an another relation
  139. }
  140. $objWriter->endElement();
  141. return $objWriter->getData();
  142. }
  143. /**
  144. * Write worksheet relationships to XML format.
  145. *
  146. * Numbering is as follows:
  147. * rId1 - Drawings
  148. * rId_hyperlink_x - Hyperlinks
  149. *
  150. * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet
  151. * @param int $pWorksheetId
  152. * @param bool $includeCharts Flag indicating if we should write charts
  153. *
  154. * @throws WriterException
  155. *
  156. * @return string XML Output
  157. */
  158. public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet, $pWorksheetId = 1, $includeCharts = false)
  159. {
  160. // Create XML writer
  161. $objWriter = null;
  162. if ($this->getParentWriter()->getUseDiskCaching()) {
  163. $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
  164. } else {
  165. $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
  166. }
  167. // XML header
  168. $objWriter->startDocument('1.0', 'UTF-8', 'yes');
  169. // Relationships
  170. $objWriter->startElement('Relationships');
  171. $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
  172. // Write drawing relationships?
  173. $d = 0;
  174. $drawingOriginalIds = [];
  175. $unparsedLoadedData = $pWorksheet->getParent()->getUnparsedLoadedData();
  176. if (isset($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingOriginalIds'])) {
  177. $drawingOriginalIds = $unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingOriginalIds'];
  178. }
  179. if ($includeCharts) {
  180. $charts = $pWorksheet->getChartCollection();
  181. } else {
  182. $charts = [];
  183. }
  184. if (($pWorksheet->getDrawingCollection()->count() > 0) || (count($charts) > 0) || $drawingOriginalIds) {
  185. $relPath = '../drawings/drawing' . $pWorksheetId . '.xml';
  186. $rId = ++$d;
  187. if (isset($drawingOriginalIds[$relPath])) {
  188. $rId = (int) (substr($drawingOriginalIds[$relPath], 3));
  189. }
  190. $this->writeRelationship(
  191. $objWriter,
  192. $rId,
  193. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing',
  194. $relPath
  195. );
  196. }
  197. // Write hyperlink relationships?
  198. $i = 1;
  199. foreach ($pWorksheet->getHyperlinkCollection() as $hyperlink) {
  200. if (!$hyperlink->isInternal()) {
  201. $this->writeRelationship(
  202. $objWriter,
  203. '_hyperlink_' . $i,
  204. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink',
  205. $hyperlink->getUrl(),
  206. 'External'
  207. );
  208. ++$i;
  209. }
  210. }
  211. // Write comments relationship?
  212. $i = 1;
  213. if (count($pWorksheet->getComments()) > 0) {
  214. $this->writeRelationship(
  215. $objWriter,
  216. '_comments_vml' . $i,
  217. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing',
  218. '../drawings/vmlDrawing' . $pWorksheetId . '.vml'
  219. );
  220. $this->writeRelationship(
  221. $objWriter,
  222. '_comments' . $i,
  223. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments',
  224. '../comments' . $pWorksheetId . '.xml'
  225. );
  226. }
  227. // Write header/footer relationship?
  228. $i = 1;
  229. if (count($pWorksheet->getHeaderFooter()->getImages()) > 0) {
  230. $this->writeRelationship(
  231. $objWriter,
  232. '_headerfooter_vml' . $i,
  233. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing',
  234. '../drawings/vmlDrawingHF' . $pWorksheetId . '.vml'
  235. );
  236. }
  237. $this->writeUnparsedRelationship($pWorksheet, $objWriter, 'ctrlProps', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp');
  238. $this->writeUnparsedRelationship($pWorksheet, $objWriter, 'vmlDrawings', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing');
  239. $this->writeUnparsedRelationship($pWorksheet, $objWriter, 'printerSettings', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings');
  240. $objWriter->endElement();
  241. return $objWriter->getData();
  242. }
  243. private function writeUnparsedRelationship(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet, XMLWriter $objWriter, $relationship, $type)
  244. {
  245. $unparsedLoadedData = $pWorksheet->getParent()->getUnparsedLoadedData();
  246. if (!isset($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()][$relationship])) {
  247. return;
  248. }
  249. foreach ($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()][$relationship] as $rId => $value) {
  250. $this->writeRelationship(
  251. $objWriter,
  252. $rId,
  253. $type,
  254. $value['relFilePath']
  255. );
  256. }
  257. }
  258. /**
  259. * Write drawing relationships to XML format.
  260. *
  261. * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet
  262. * @param int &$chartRef Chart ID
  263. * @param bool $includeCharts Flag indicating if we should write charts
  264. *
  265. * @throws WriterException
  266. *
  267. * @return string XML Output
  268. */
  269. public function writeDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet, &$chartRef, $includeCharts = false)
  270. {
  271. // Create XML writer
  272. $objWriter = null;
  273. if ($this->getParentWriter()->getUseDiskCaching()) {
  274. $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
  275. } else {
  276. $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
  277. }
  278. // XML header
  279. $objWriter->startDocument('1.0', 'UTF-8', 'yes');
  280. // Relationships
  281. $objWriter->startElement('Relationships');
  282. $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
  283. // Loop through images and write relationships
  284. $i = 1;
  285. $iterator = $pWorksheet->getDrawingCollection()->getIterator();
  286. while ($iterator->valid()) {
  287. if ($iterator->current() instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing
  288. || $iterator->current() instanceof MemoryDrawing) {
  289. // Write relationship for image drawing
  290. /** @var \PhpOffice\PhpSpreadsheet\Worksheet\Drawing $drawing */
  291. $drawing = $iterator->current();
  292. $this->writeRelationship(
  293. $objWriter,
  294. $i,
  295. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
  296. '../media/' . str_replace(' ', '', $drawing->getIndexedFilename())
  297. );
  298. $i = $this->writeDrawingHyperLink($objWriter, $drawing, $i);
  299. }
  300. $iterator->next();
  301. ++$i;
  302. }
  303. if ($includeCharts) {
  304. // Loop through charts and write relationships
  305. $chartCount = $pWorksheet->getChartCount();
  306. if ($chartCount > 0) {
  307. for ($c = 0; $c < $chartCount; ++$c) {
  308. $this->writeRelationship(
  309. $objWriter,
  310. $i++,
  311. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart',
  312. '../charts/chart' . ++$chartRef . '.xml'
  313. );
  314. }
  315. }
  316. }
  317. $objWriter->endElement();
  318. return $objWriter->getData();
  319. }
  320. /**
  321. * Write header/footer drawing relationships to XML format.
  322. *
  323. * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet
  324. *
  325. * @throws WriterException
  326. *
  327. * @return string XML Output
  328. */
  329. public function writeHeaderFooterDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet)
  330. {
  331. // Create XML writer
  332. $objWriter = null;
  333. if ($this->getParentWriter()->getUseDiskCaching()) {
  334. $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
  335. } else {
  336. $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
  337. }
  338. // XML header
  339. $objWriter->startDocument('1.0', 'UTF-8', 'yes');
  340. // Relationships
  341. $objWriter->startElement('Relationships');
  342. $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
  343. // Loop through images and write relationships
  344. foreach ($pWorksheet->getHeaderFooter()->getImages() as $key => $value) {
  345. // Write relationship for image drawing
  346. $this->writeRelationship(
  347. $objWriter,
  348. $key,
  349. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
  350. '../media/' . $value->getIndexedFilename()
  351. );
  352. }
  353. $objWriter->endElement();
  354. return $objWriter->getData();
  355. }
  356. /**
  357. * Write Override content type.
  358. *
  359. * @param XMLWriter $objWriter XML Writer
  360. * @param int $pId Relationship ID. rId will be prepended!
  361. * @param string $pType Relationship type
  362. * @param string $pTarget Relationship target
  363. * @param string $pTargetMode Relationship target mode
  364. *
  365. * @throws WriterException
  366. */
  367. private function writeRelationship(XMLWriter $objWriter, $pId, $pType, $pTarget, $pTargetMode = '')
  368. {
  369. if ($pType != '' && $pTarget != '') {
  370. // Write relationship
  371. $objWriter->startElement('Relationship');
  372. $objWriter->writeAttribute('Id', 'rId' . $pId);
  373. $objWriter->writeAttribute('Type', $pType);
  374. $objWriter->writeAttribute('Target', $pTarget);
  375. if ($pTargetMode != '') {
  376. $objWriter->writeAttribute('TargetMode', $pTargetMode);
  377. }
  378. $objWriter->endElement();
  379. } else {
  380. throw new WriterException('Invalid parameters passed.');
  381. }
  382. }
  383. /**
  384. * @param $objWriter
  385. * @param \PhpOffice\PhpSpreadsheet\Worksheet\Drawing $drawing
  386. * @param $i
  387. *
  388. * @throws WriterException
  389. *
  390. * @return int
  391. */
  392. private function writeDrawingHyperLink($objWriter, $drawing, $i)
  393. {
  394. if ($drawing->getHyperlink() === null) {
  395. return $i;
  396. }
  397. ++$i;
  398. $this->writeRelationship(
  399. $objWriter,
  400. $i,
  401. 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink',
  402. $drawing->getHyperlink()->getUrl(),
  403. $drawing->getHyperlink()->getTypeHyperlink()
  404. );
  405. return $i;
  406. }
  407. }