Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
1 <?php 2 3 namespace PhpOffice\PhpSpreadsheet\Writer\Ods; 4 5 use PhpOffice\PhpSpreadsheet\Cell\Cell; 6 use PhpOffice\PhpSpreadsheet\Cell\Coordinate; 7 use PhpOffice\PhpSpreadsheet\Cell\DataType; 8 use PhpOffice\PhpSpreadsheet\Shared\XMLWriter; 9 use PhpOffice\PhpSpreadsheet\Spreadsheet; 10 use PhpOffice\PhpSpreadsheet\Style\Fill; 11 use PhpOffice\PhpSpreadsheet\Style\Font; 12 use PhpOffice\PhpSpreadsheet\Worksheet\Row; 13 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; 14 use PhpOffice\PhpSpreadsheet\Writer\Exception; 15 use PhpOffice\PhpSpreadsheet\Writer\Ods; 16 use PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Comment; 17 18 /** 19 * @category PhpSpreadsheet 20 * 21 * @method Ods getParentWriter 22 * 23 * @copyright Copyright (c) 2006 - 2015 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet) 24 * @author Alexander Pervakov <frost-nzcr4@jagmort.com> 25 */ 26 class Content extends WriterPart 27 { 28 const NUMBER_COLS_REPEATED_MAX = 1024; 29 const NUMBER_ROWS_REPEATED_MAX = 1048576; 30 const CELL_STYLE_PREFIX = 'ce'; 31 32 /** 33 * Write content.xml to XML format. 34 * 35 * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception 36 * 37 * @return string XML Output 38 */ 39 public function write() 40 { 41 $objWriter = null; 42 if ($this->getParentWriter()->getUseDiskCaching()) { 43 $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); 44 } else { 45 $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); 46 } 47 48 // XML header 49 $objWriter->startDocument('1.0', 'UTF-8'); 50 51 // Content 52 $objWriter->startElement('office:document-content'); 53 $objWriter->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'); 54 $objWriter->writeAttribute('xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'); 55 $objWriter->writeAttribute('xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'); 56 $objWriter->writeAttribute('xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'); 57 $objWriter->writeAttribute('xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'); 58 $objWriter->writeAttribute('xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'); 59 $objWriter->writeAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink'); 60 $objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/'); 61 $objWriter->writeAttribute('xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'); 62 $objWriter->writeAttribute('xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'); 63 $objWriter->writeAttribute('xmlns:presentation', 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0'); 64 $objWriter->writeAttribute('xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'); 65 $objWriter->writeAttribute('xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'); 66 $objWriter->writeAttribute('xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'); 67 $objWriter->writeAttribute('xmlns:math', 'http://www.w3.org/1998/Math/MathML'); 68 $objWriter->writeAttribute('xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'); 69 $objWriter->writeAttribute('xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'); 70 $objWriter->writeAttribute('xmlns:ooo', 'http://openoffice.org/2004/office'); 71 $objWriter->writeAttribute('xmlns:ooow', 'http://openoffice.org/2004/writer'); 72 $objWriter->writeAttribute('xmlns:oooc', 'http://openoffice.org/2004/calc'); 73 $objWriter->writeAttribute('xmlns:dom', 'http://www.w3.org/2001/xml-events'); 74 $objWriter->writeAttribute('xmlns:xforms', 'http://www.w3.org/2002/xforms'); 75 $objWriter->writeAttribute('xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'); 76 $objWriter->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); 77 $objWriter->writeAttribute('xmlns:rpt', 'http://openoffice.org/2005/report'); 78 $objWriter->writeAttribute('xmlns:of', 'urn:oasis:names:tc:opendocument:xmlns:of:1.2'); 79 $objWriter->writeAttribute('xmlns:xhtml', 'http://www.w3.org/1999/xhtml'); 80 $objWriter->writeAttribute('xmlns:grddl', 'http://www.w3.org/2003/g/data-view#'); 81 $objWriter->writeAttribute('xmlns:tableooo', 'http://openoffice.org/2009/table'); 82 $objWriter->writeAttribute('xmlns:field', 'urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0'); 83 $objWriter->writeAttribute('xmlns:formx', 'urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0'); 84 $objWriter->writeAttribute('xmlns:css3t', 'http://www.w3.org/TR/css3-text/'); 85 $objWriter->writeAttribute('office:version', '1.2'); 86 87 $objWriter->writeElement('office:scripts'); 88 $objWriter->writeElement('office:font-face-decls'); 89 90 // Styles XF 91 $objWriter->startElement('office:automatic-styles'); 92 $this->writeXfStyles($objWriter, $this->getParentWriter()->getSpreadsheet()); 93 $objWriter->endElement(); 94 95 $objWriter->startElement('office:body'); 96 $objWriter->startElement('office:spreadsheet'); 97 $objWriter->writeElement('table:calculation-settings'); 98 99 $this->writeSheets($objWriter); 100 101 $objWriter->writeElement('table:named-expressions'); 102 $objWriter->endElement(); 103 $objWriter->endElement(); 104 $objWriter->endElement(); 105 106 return $objWriter->getData(); 107 } 108 109 /** 110 * Write sheets. 111 * 112 * @param XMLWriter $objWriter 113 */ 114 private function writeSheets(XMLWriter $objWriter) 115 { 116 $spreadsheet = $this->getParentWriter()->getSpreadsheet(); // @var $spreadsheet Spreadsheet 117 118 $sheetCount = $spreadsheet->getSheetCount(); 119 for ($i = 0; $i < $sheetCount; ++$i) { 120 $objWriter->startElement('table:table'); 121 $objWriter->writeAttribute('table:name', $spreadsheet->getSheet($i)->getTitle()); 122 $objWriter->writeElement('office:forms'); 123 $objWriter->startElement('table:table-column'); 124 $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX); 125 $objWriter->endElement(); 126 $this->writeRows($objWriter, $spreadsheet->getSheet($i)); 127 $objWriter->endElement(); 128 } 129 } 130 131 /** 132 * Write rows of the specified sheet. 133 * 134 * @param XMLWriter $objWriter 135 * @param Worksheet $sheet 136 */ 137 private function writeRows(XMLWriter $objWriter, Worksheet $sheet) 138 { 139 $numberRowsRepeated = self::NUMBER_ROWS_REPEATED_MAX; 140 $span_row = 0; 141 $rows = $sheet->getRowIterator(); 142 while ($rows->valid()) { 143 --$numberRowsRepeated; 144 $row = $rows->current(); 145 if ($row->getCellIterator()->valid()) { 146 if ($span_row) { 147 $objWriter->startElement('table:table-row'); 148 if ($span_row > 1) { 149 $objWriter->writeAttribute('table:number-rows-repeated', $span_row); 150 } 151 $objWriter->startElement('table:table-cell'); 152 $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX); 153 $objWriter->endElement(); 154 $objWriter->endElement(); 155 $span_row = 0; 156 } 157 $objWriter->startElement('table:table-row'); 158 $this->writeCells($objWriter, $row); 159 $objWriter->endElement(); 160 } else { 161 ++$span_row; 162 } 163 $rows->next(); 164 } 165 } 166 167 /** 168 * Write cells of the specified row. 169 * 170 * @param XMLWriter $objWriter 171 * @param Row $row 172 * 173 * @throws Exception 174 */ 175 private function writeCells(XMLWriter $objWriter, Row $row) 176 { 177 $numberColsRepeated = self::NUMBER_COLS_REPEATED_MAX; 178 $prevColumn = -1; 179 $cells = $row->getCellIterator(); 180 while ($cells->valid()) { 181 /** @var \PhpOffice\PhpSpreadsheet\Cell\Cell $cell */ 182 $cell = $cells->current(); 183 $column = Coordinate::columnIndexFromString($cell->getColumn()) - 1; 184 185 $this->writeCellSpan($objWriter, $column, $prevColumn); 186 $objWriter->startElement('table:table-cell'); 187 $this->writeCellMerge($objWriter, $cell); 188 189 // Style XF 190 $style = $cell->getXfIndex(); 191 if ($style !== null) { 192 $objWriter->writeAttribute('table:style-name', self::CELL_STYLE_PREFIX . $style); 193 } 194 195 switch ($cell->getDataType()) { 196 case DataType::TYPE_BOOL: 197 $objWriter->writeAttribute('office:value-type', 'boolean'); 198 $objWriter->writeAttribute('office:value', $cell->getValue()); 199 $objWriter->writeElement('text:p', $cell->getValue()); 200 201 break; 202 case DataType::TYPE_ERROR: 203 throw new Exception('Writing of error not implemented yet.'); 204 205 break; 206 case DataType::TYPE_FORMULA: 207 $formulaValue = $cell->getValue(); 208 if ($this->getParentWriter()->getPreCalculateFormulas()) { 209 try { 210 $formulaValue = $cell->getCalculatedValue(); 211 } catch (Exception $e) { 212 // don't do anything 213 } 214 } 215 $objWriter->writeAttribute('table:formula', 'of:' . $cell->getValue()); 216 if (is_numeric($formulaValue)) { 217 $objWriter->writeAttribute('office:value-type', 'float'); 218 } else { 219 $objWriter->writeAttribute('office:value-type', 'string'); 220 } 221 $objWriter->writeAttribute('office:value', $formulaValue); 222 $objWriter->writeElement('text:p', $formulaValue); 223 224 break; 225 case DataType::TYPE_INLINE: 226 throw new Exception('Writing of inline not implemented yet.'); 227 228 break; 229 case DataType::TYPE_NUMERIC: 230 $objWriter->writeAttribute('office:value-type', 'float'); 231 $objWriter->writeAttribute('office:value', $cell->getValue()); 232 $objWriter->writeElement('text:p', $cell->getValue()); 233 234 break; 235 case DataType::TYPE_STRING: 236 $objWriter->writeAttribute('office:value-type', 'string'); 237 $objWriter->writeElement('text:p', $cell->getValue()); 238 239 break; 240 } 241 Comment::write($objWriter, $cell); 242 $objWriter->endElement(); 243 $prevColumn = $column; 244 $cells->next(); 245 } 246 $numberColsRepeated = $numberColsRepeated - $prevColumn - 1; 247 if ($numberColsRepeated > 0) { 248 if ($numberColsRepeated > 1) { 249 $objWriter->startElement('table:table-cell'); 250 $objWriter->writeAttribute('table:number-columns-repeated', $numberColsRepeated); 251 $objWriter->endElement(); 252 } else { 253 $objWriter->writeElement('table:table-cell'); 254 } 255 } 256 } 257 258 /** 259 * Write span. 260 * 261 * @param XMLWriter $objWriter 262 * @param int $curColumn 263 * @param int $prevColumn 264 */ 265 private function writeCellSpan(XMLWriter $objWriter, $curColumn, $prevColumn) 266 { 267 $diff = $curColumn - $prevColumn - 1; 268 if (1 === $diff) { 269 $objWriter->writeElement('table:table-cell'); 270 } elseif ($diff > 1) { 271 $objWriter->startElement('table:table-cell'); 272 $objWriter->writeAttribute('table:number-columns-repeated', $diff); 273 $objWriter->endElement(); 274 } 275 } 276 277 /** 278 * Write XF cell styles. 279 * 280 * @param XMLWriter $writer 281 * @param Spreadsheet $spreadsheet 282 */ 283 private function writeXfStyles(XMLWriter $writer, Spreadsheet $spreadsheet) 284 { 285 foreach ($spreadsheet->getCellXfCollection() as $style) { 286 $writer->startElement('style:style'); 287 $writer->writeAttribute('style:name', self::CELL_STYLE_PREFIX . $style->getIndex()); 288 $writer->writeAttribute('style:family', 'table-cell'); 289 $writer->writeAttribute('style:parent-style-name', 'Default'); 290 291 // style:text-properties 292 293 // Font 294 $writer->startElement('style:text-properties'); 295 296 $font = $style->getFont(); 297 298 if ($font->getBold()) { 299 $writer->writeAttribute('fo:font-weight', 'bold'); 300 $writer->writeAttribute('style:font-weight-complex', 'bold'); 301 $writer->writeAttribute('style:font-weight-asian', 'bold'); 302 } 303 304 if ($font->getItalic()) { 305 $writer->writeAttribute('fo:font-style', 'italic'); 306 } 307 308 if ($color = $font->getColor()) { 309 $writer->writeAttribute('fo:color', sprintf('#%s', $color->getRGB())); 310 } 311 312 if ($family = $font->getName()) { 313 $writer->writeAttribute('fo:font-family', $family); 314 } 315 316 if ($size = $font->getSize()) { 317 $writer->writeAttribute('fo:font-size', sprintf('%.1Fpt', $size)); 318 } 319 320 if ($font->getUnderline() && $font->getUnderline() != Font::UNDERLINE_NONE) { 321 $writer->writeAttribute('style:text-underline-style', 'solid'); 322 $writer->writeAttribute('style:text-underline-width', 'auto'); 323 $writer->writeAttribute('style:text-underline-color', 'font-color'); 324 325 switch ($font->getUnderline()) { 326 case Font::UNDERLINE_DOUBLE: 327 $writer->writeAttribute('style:text-underline-type', 'double'); 328 329 break; 330 case Font::UNDERLINE_SINGLE: 331 $writer->writeAttribute('style:text-underline-type', 'single'); 332 333 break; 334 } 335 } 336 337 $writer->endElement(); // Close style:text-properties 338 339 // style:table-cell-properties 340 341 $writer->startElement('style:table-cell-properties'); 342 $writer->writeAttribute('style:rotation-align', 'none'); 343 344 // Fill 345 if ($fill = $style->getFill()) { 346 switch ($fill->getFillType()) { 347 case Fill::FILL_SOLID: 348 $writer->writeAttribute('fo:background-color', sprintf( 349 '#%s', 350 strtolower($fill->getStartColor()->getRGB()) 351 )); 352 353 break; 354 case Fill::FILL_GRADIENT_LINEAR: 355 case Fill::FILL_GRADIENT_PATH: 356 /// TODO :: To be implemented 357 break; 358 case Fill::FILL_NONE: 359 default: 360 } 361 } 362 363 $writer->endElement(); // Close style:table-cell-properties 364 365 // End 366 367 $writer->endElement(); // Close style:style 368 } 369 } 370 371 /** 372 * Write attributes for merged cell. 373 * 374 * @param XMLWriter $objWriter 375 * @param Cell $cell 376 * 377 * @throws \PhpOffice\PhpSpreadsheet\Exception 378 */ 379 private function writeCellMerge(XMLWriter $objWriter, Cell $cell) 380 { 381 if (!$cell->isMergeRangeValueCell()) { 382 return; 383 } 384 385 $mergeRange = Coordinate::splitRange($cell->getMergeRange()); 386 [$startCell, $endCell] = $mergeRange[0]; 387 $start = Coordinate::coordinateFromString($startCell); 388 $end = Coordinate::coordinateFromString($endCell); 389 $columnSpan = Coordinate::columnIndexFromString($end[0]) - Coordinate::columnIndexFromString($start[0]) + 1; 390 $rowSpan = $end[1] - $start[1] + 1; 391 392 $objWriter->writeAttribute('table:number-columns-spanned', $columnSpan); 393 $objWriter->writeAttribute('table:number-rows-spanned', $rowSpan); 394 } 395 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body