Skip to content

Commit 5fce89e

Browse files
authored
Merge pull request #108 from redokun/fix-105
Ods writer: add support for basic text styling (Fix #105)
2 parents 3560f11 + 219e4e6 commit 5fce89e

File tree

4 files changed

+400
-51
lines changed

4 files changed

+400
-51
lines changed

src/PhpSpreadsheet/Writer/Ods/Content.php

+169-51
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
44

5-
/**
5+
/*
66
* PhpSpreadsheet.
77
*
88
* Copyright (c) 2006 - 2015 PhpSpreadsheet
@@ -27,37 +27,45 @@
2727
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
2828
*/
2929

30+
use PhpOffice\PhpSpreadsheet\Cell;
31+
use PhpOffice\PhpSpreadsheet\Cell\DataType;
32+
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
33+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
34+
use PhpOffice\PhpSpreadsheet\Style\Fill;
35+
use PhpOffice\PhpSpreadsheet\Style\Font;
36+
use PhpOffice\PhpSpreadsheet\Worksheet;
37+
use PhpOffice\PhpSpreadsheet\Writer\Exception;
38+
use PhpOffice\PhpSpreadsheet\Writer\Ods;
39+
use PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Comment;
40+
3041
/**
3142
* @category PhpSpreadsheet
3243
*
44+
* @method Ods getParentWriter
45+
*
3346
* @copyright Copyright (c) 2006 - 2015 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
3447
* @author Alexander Pervakov <[email protected]>
3548
*/
3649
class Content extends WriterPart
3750
{
3851
const NUMBER_COLS_REPEATED_MAX = 1024;
3952
const NUMBER_ROWS_REPEATED_MAX = 1048576;
53+
const CELL_STYLE_PREFIX = 'ce';
4054

4155
/**
4256
* Write content.xml to XML format.
4357
*
44-
* @param \PhpOffice\PhpSpreadsheet\Spreadsheet $spreadsheet
45-
*
4658
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
4759
*
4860
* @return string XML Output
4961
*/
50-
public function write(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null)
62+
public function write()
5163
{
52-
if (!$spreadsheet) {
53-
$spreadsheet = $this->getParentWriter()->getSpreadsheet(); /* @var $spreadsheet PhpSpreadsheet */
54-
}
55-
5664
$objWriter = null;
5765
if ($this->getParentWriter()->getUseDiskCaching()) {
58-
$objWriter = new \PhpOffice\PhpSpreadsheet\Shared\XMLWriter(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
66+
$objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
5967
} else {
60-
$objWriter = new \PhpOffice\PhpSpreadsheet\Shared\XMLWriter(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter::STORAGE_MEMORY);
68+
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
6169
}
6270

6371
// XML header
@@ -101,12 +109,18 @@ public function write(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null)
101109

102110
$objWriter->writeElement('office:scripts');
103111
$objWriter->writeElement('office:font-face-decls');
104-
$objWriter->writeElement('office:automatic-styles');
112+
113+
// Styles XF
114+
$objWriter->startElement('office:automatic-styles');
115+
$this->writeXfStyles($objWriter, $this->getParentWriter()->getSpreadsheet());
116+
$objWriter->endElement();
105117

106118
$objWriter->startElement('office:body');
107119
$objWriter->startElement('office:spreadsheet');
108120
$objWriter->writeElement('table:calculation-settings');
121+
109122
$this->writeSheets($objWriter);
123+
110124
$objWriter->writeElement('table:named-expressions');
111125
$objWriter->endElement();
112126
$objWriter->endElement();
@@ -118,14 +132,14 @@ public function write(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null)
118132
/**
119133
* Write sheets.
120134
*
121-
* @param \PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter
135+
* @param XMLWriter $objWriter
122136
*/
123-
private function writeSheets(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter)
137+
private function writeSheets(XMLWriter $objWriter)
124138
{
125-
$spreadsheet = $this->getParentWriter()->getSpreadsheet(); /* @var $spreadsheet PhpSpreadsheet */
139+
$spreadsheet = $this->getParentWriter()->getSpreadsheet(); /* @var $spreadsheet Spreadsheet */
126140

127-
$sheet_count = $spreadsheet->getSheetCount();
128-
for ($i = 0; $i < $sheet_count; ++$i) {
141+
$sheetCount = $spreadsheet->getSheetCount();
142+
for ($i = 0; $i < $sheetCount; ++$i) {
129143
$objWriter->startElement('table:table');
130144
$objWriter->writeAttribute('table:name', $spreadsheet->getSheet($i)->getTitle());
131145
$objWriter->writeElement('office:forms');
@@ -140,16 +154,16 @@ private function writeSheets(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWrit
140154
/**
141155
* Write rows of the specified sheet.
142156
*
143-
* @param \PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter
144-
* @param \PhpOffice\PhpSpreadsheet\Worksheet $sheet
157+
* @param XMLWriter $objWriter
158+
* @param Worksheet $sheet
145159
*/
146-
private function writeRows(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $sheet)
160+
private function writeRows(XMLWriter $objWriter, Worksheet $sheet)
147161
{
148-
$number_rows_repeated = self::NUMBER_ROWS_REPEATED_MAX;
162+
$numberRowsRepeated = self::NUMBER_ROWS_REPEATED_MAX;
149163
$span_row = 0;
150164
$rows = $sheet->getRowIterator();
151165
while ($rows->valid()) {
152-
--$number_rows_repeated;
166+
--$numberRowsRepeated;
153167
$row = $rows->current();
154168
if ($row->getCellIterator()->valid()) {
155169
if ($span_row) {
@@ -176,70 +190,77 @@ private function writeRows(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter
176190
/**
177191
* Write cells of the specified row.
178192
*
179-
* @param \PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter
180-
* @param \PhpOffice\PhpSpreadsheet\Worksheet\Row $row
193+
* @param XMLWriter $objWriter
194+
* @param Worksheet\Row $row
181195
*
182-
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
196+
* @throws Exception
183197
*/
184-
private function writeCells(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet\Row $row)
198+
private function writeCells(XMLWriter $objWriter, Worksheet\Row $row)
185199
{
186-
$number_cols_repeated = self::NUMBER_COLS_REPEATED_MAX;
187-
$prev_column = -1;
200+
$numberColsRepeated = self::NUMBER_COLS_REPEATED_MAX;
201+
$prevColumn = -1;
188202
$cells = $row->getCellIterator();
189203
while ($cells->valid()) {
204+
/** @var Cell $cell */
190205
$cell = $cells->current();
191-
$column = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($cell->getColumn()) - 1;
206+
$column = Cell::columnIndexFromString($cell->getColumn()) - 1;
192207

193-
$this->writeCellSpan($objWriter, $column, $prev_column);
208+
$this->writeCellSpan($objWriter, $column, $prevColumn);
194209
$objWriter->startElement('table:table-cell');
195210

211+
// Style XF
212+
$style = $cell->getXfIndex();
213+
if ($style !== null) {
214+
$objWriter->writeAttribute('table:style-name', self::CELL_STYLE_PREFIX . $style);
215+
}
216+
196217
switch ($cell->getDataType()) {
197-
case \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_BOOL:
218+
case DataType::TYPE_BOOL:
198219
$objWriter->writeAttribute('office:value-type', 'boolean');
199220
$objWriter->writeAttribute('office:value', $cell->getValue());
200221
$objWriter->writeElement('text:p', $cell->getValue());
201222
break;
202-
case \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_ERROR:
203-
throw new \PhpOffice\PhpSpreadsheet\Writer\Exception('Writing of error not implemented yet.');
223+
case DataType::TYPE_ERROR:
224+
throw new Exception('Writing of error not implemented yet.');
204225
break;
205-
case \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_FORMULA:
226+
case DataType::TYPE_FORMULA:
206227
try {
207-
$formula_value = $cell->getCalculatedValue();
208-
} catch (Exception $e) {
209-
$formula_value = $cell->getValue();
228+
$formulaValue = $cell->getCalculatedValue();
229+
} catch (\Exception $e) {
230+
$formulaValue = $cell->getValue();
210231
}
211232
$objWriter->writeAttribute('table:formula', 'of:' . $cell->getValue());
212-
if (is_numeric($formula_value)) {
233+
if (is_numeric($formulaValue)) {
213234
$objWriter->writeAttribute('office:value-type', 'float');
214235
} else {
215236
$objWriter->writeAttribute('office:value-type', 'string');
216237
}
217-
$objWriter->writeAttribute('office:value', $formula_value);
218-
$objWriter->writeElement('text:p', $formula_value);
238+
$objWriter->writeAttribute('office:value', $formulaValue);
239+
$objWriter->writeElement('text:p', $formulaValue);
219240
break;
220-
case \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_INLINE:
221-
throw new \PhpOffice\PhpSpreadsheet\Writer\Exception('Writing of inline not implemented yet.');
241+
case DataType::TYPE_INLINE:
242+
throw new Exception('Writing of inline not implemented yet.');
222243
break;
223-
case \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC:
244+
case DataType::TYPE_NUMERIC:
224245
$objWriter->writeAttribute('office:value-type', 'float');
225246
$objWriter->writeAttribute('office:value', $cell->getValue());
226247
$objWriter->writeElement('text:p', $cell->getValue());
227248
break;
228-
case \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING:
249+
case DataType::TYPE_STRING:
229250
$objWriter->writeAttribute('office:value-type', 'string');
230251
$objWriter->writeElement('text:p', $cell->getValue());
231252
break;
232253
}
233-
Cell\Comment::write($objWriter, $cell);
254+
Comment::write($objWriter, $cell);
234255
$objWriter->endElement();
235-
$prev_column = $column;
256+
$prevColumn = $column;
236257
$cells->next();
237258
}
238-
$number_cols_repeated = $number_cols_repeated - $prev_column - 1;
239-
if ($number_cols_repeated > 0) {
240-
if ($number_cols_repeated > 1) {
259+
$numberColsRepeated = $numberColsRepeated - $prevColumn - 1;
260+
if ($numberColsRepeated > 0) {
261+
if ($numberColsRepeated > 1) {
241262
$objWriter->startElement('table:table-cell');
242-
$objWriter->writeAttribute('table:number-columns-repeated', $number_cols_repeated);
263+
$objWriter->writeAttribute('table:number-columns-repeated', $numberColsRepeated);
243264
$objWriter->endElement();
244265
} else {
245266
$objWriter->writeElement('table:table-cell');
@@ -250,11 +271,11 @@ private function writeCells(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWrite
250271
/**
251272
* Write span.
252273
*
253-
* @param \PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter
274+
* @param XMLWriter $objWriter
254275
* @param int $curColumn
255276
* @param int $prevColumn
256277
*/
257-
private function writeCellSpan(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, $curColumn, $prevColumn)
278+
private function writeCellSpan(XMLWriter $objWriter, $curColumn, $prevColumn)
258279
{
259280
$diff = $curColumn - $prevColumn - 1;
260281
if (1 === $diff) {
@@ -265,4 +286,101 @@ private function writeCellSpan(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWr
265286
$objWriter->endElement();
266287
}
267288
}
289+
290+
/**
291+
* Write XF cell styles.
292+
*
293+
* @param XMLWriter $writer
294+
* @param Spreadsheet $spreadsheet
295+
*/
296+
private function writeXfStyles(XMLWriter $writer, Spreadsheet $spreadsheet)
297+
{
298+
foreach ($spreadsheet->getCellXfCollection() as $style) {
299+
$writer->startElement('style:style');
300+
$writer->writeAttribute('style:name', self::CELL_STYLE_PREFIX . $style->getIndex());
301+
$writer->writeAttribute('style:family', 'table-cell');
302+
$writer->writeAttribute('style:parent-style-name', 'Default');
303+
304+
/*
305+
* style:text-properties
306+
*/
307+
308+
// Font
309+
$writer->startElement('style:text-properties');
310+
311+
$font = $style->getFont();
312+
313+
if ($font->getBold()) {
314+
$writer->writeAttribute('fo:font-weight', 'bold');
315+
$writer->writeAttribute('style:font-weight-complex', 'bold');
316+
$writer->writeAttribute('style:font-weight-asian', 'bold');
317+
}
318+
319+
if ($font->getItalic()) {
320+
$writer->writeAttribute('fo:font-style', 'italic');
321+
}
322+
323+
if ($color = $font->getColor()) {
324+
$writer->writeAttribute('fo:color', sprintf('#%s', $color->getRGB()));
325+
}
326+
327+
if ($family = $font->getName()) {
328+
$writer->writeAttribute('fo:font-family', $family);
329+
}
330+
331+
if ($size = $font->getSize()) {
332+
$writer->writeAttribute('fo:font-size', sprintf('%.1fpt', $size));
333+
}
334+
335+
if ($font->getUnderline() && $font->getUnderline() != Font::UNDERLINE_NONE) {
336+
$writer->writeAttribute('style:text-underline-style', 'solid');
337+
$writer->writeAttribute('style:text-underline-width', 'auto');
338+
$writer->writeAttribute('style:text-underline-color', 'font-color');
339+
340+
switch ($font->getUnderline()) {
341+
case Font::UNDERLINE_DOUBLE:
342+
$writer->writeAttribute('style:text-underline-type', 'double');
343+
break;
344+
case Font::UNDERLINE_SINGLE:
345+
$writer->writeAttribute('style:text-underline-type', 'single');
346+
break;
347+
}
348+
}
349+
350+
$writer->endElement(); // Close style:text-properties
351+
352+
/*
353+
* style:table-cell-properties
354+
*/
355+
356+
$writer->startElement('style:table-cell-properties');
357+
$writer->writeAttribute('style:rotation-align', 'none');
358+
359+
// Fill
360+
if ($fill = $style->getFill()) {
361+
switch ($fill->getFillType()) {
362+
case Fill::FILL_SOLID:
363+
$writer->writeAttribute('fo:background-color', sprintf(
364+
'#%s',
365+
strtolower($fill->getStartColor()->getRGB())
366+
));
367+
break;
368+
case Fill::FILL_GRADIENT_LINEAR:
369+
case Fill::FILL_GRADIENT_PATH:
370+
/// TODO :: To be implemented
371+
break;
372+
case Fill::FILL_NONE:
373+
default:
374+
}
375+
}
376+
377+
$writer->endElement(); // Close style:table-cell-properties
378+
379+
/*
380+
* End
381+
*/
382+
383+
$writer->endElement(); // Close style:style
384+
}
385+
}
268386
}

0 commit comments

Comments
 (0)