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