1 |
efrain |
1 |
<?php
|
|
|
2 |
|
|
|
3 |
namespace PhpOffice\PhpSpreadsheet\Reader\Gnumeric;
|
|
|
4 |
|
|
|
5 |
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
|
|
6 |
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
|
|
7 |
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
|
|
8 |
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
|
|
9 |
use PhpOffice\PhpSpreadsheet\Style\Border;
|
|
|
10 |
use PhpOffice\PhpSpreadsheet\Style\Borders;
|
|
|
11 |
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
|
|
12 |
use PhpOffice\PhpSpreadsheet\Style\Font;
|
|
|
13 |
use SimpleXMLElement;
|
|
|
14 |
|
|
|
15 |
class Styles
|
|
|
16 |
{
|
|
|
17 |
/**
|
|
|
18 |
* @var Spreadsheet
|
|
|
19 |
*/
|
|
|
20 |
private $spreadsheet;
|
|
|
21 |
|
|
|
22 |
/**
|
|
|
23 |
* @var bool
|
|
|
24 |
*/
|
|
|
25 |
protected $readDataOnly = false;
|
|
|
26 |
|
|
|
27 |
/** @var array */
|
|
|
28 |
public static $mappings = [
|
|
|
29 |
'borderStyle' => [
|
|
|
30 |
'0' => Border::BORDER_NONE,
|
|
|
31 |
'1' => Border::BORDER_THIN,
|
|
|
32 |
'2' => Border::BORDER_MEDIUM,
|
|
|
33 |
'3' => Border::BORDER_SLANTDASHDOT,
|
|
|
34 |
'4' => Border::BORDER_DASHED,
|
|
|
35 |
'5' => Border::BORDER_THICK,
|
|
|
36 |
'6' => Border::BORDER_DOUBLE,
|
|
|
37 |
'7' => Border::BORDER_DOTTED,
|
|
|
38 |
'8' => Border::BORDER_MEDIUMDASHED,
|
|
|
39 |
'9' => Border::BORDER_DASHDOT,
|
|
|
40 |
'10' => Border::BORDER_MEDIUMDASHDOT,
|
|
|
41 |
'11' => Border::BORDER_DASHDOTDOT,
|
|
|
42 |
'12' => Border::BORDER_MEDIUMDASHDOTDOT,
|
|
|
43 |
'13' => Border::BORDER_MEDIUMDASHDOTDOT,
|
|
|
44 |
],
|
|
|
45 |
'fillType' => [
|
|
|
46 |
'1' => Fill::FILL_SOLID,
|
|
|
47 |
'2' => Fill::FILL_PATTERN_DARKGRAY,
|
|
|
48 |
'3' => Fill::FILL_PATTERN_MEDIUMGRAY,
|
|
|
49 |
'4' => Fill::FILL_PATTERN_LIGHTGRAY,
|
|
|
50 |
'5' => Fill::FILL_PATTERN_GRAY125,
|
|
|
51 |
'6' => Fill::FILL_PATTERN_GRAY0625,
|
|
|
52 |
'7' => Fill::FILL_PATTERN_DARKHORIZONTAL, // horizontal stripe
|
|
|
53 |
'8' => Fill::FILL_PATTERN_DARKVERTICAL, // vertical stripe
|
|
|
54 |
'9' => Fill::FILL_PATTERN_DARKDOWN, // diagonal stripe
|
|
|
55 |
'10' => Fill::FILL_PATTERN_DARKUP, // reverse diagonal stripe
|
|
|
56 |
'11' => Fill::FILL_PATTERN_DARKGRID, // diagoanl crosshatch
|
|
|
57 |
'12' => Fill::FILL_PATTERN_DARKTRELLIS, // thick diagonal crosshatch
|
|
|
58 |
'13' => Fill::FILL_PATTERN_LIGHTHORIZONTAL,
|
|
|
59 |
'14' => Fill::FILL_PATTERN_LIGHTVERTICAL,
|
|
|
60 |
'15' => Fill::FILL_PATTERN_LIGHTUP,
|
|
|
61 |
'16' => Fill::FILL_PATTERN_LIGHTDOWN,
|
|
|
62 |
'17' => Fill::FILL_PATTERN_LIGHTGRID, // thin horizontal crosshatch
|
|
|
63 |
'18' => Fill::FILL_PATTERN_LIGHTTRELLIS, // thin diagonal crosshatch
|
|
|
64 |
],
|
|
|
65 |
'horizontal' => [
|
|
|
66 |
'1' => Alignment::HORIZONTAL_GENERAL,
|
|
|
67 |
'2' => Alignment::HORIZONTAL_LEFT,
|
|
|
68 |
'4' => Alignment::HORIZONTAL_RIGHT,
|
|
|
69 |
'8' => Alignment::HORIZONTAL_CENTER,
|
|
|
70 |
'16' => Alignment::HORIZONTAL_CENTER_CONTINUOUS,
|
|
|
71 |
'32' => Alignment::HORIZONTAL_JUSTIFY,
|
|
|
72 |
'64' => Alignment::HORIZONTAL_CENTER_CONTINUOUS,
|
|
|
73 |
],
|
|
|
74 |
'underline' => [
|
|
|
75 |
'1' => Font::UNDERLINE_SINGLE,
|
|
|
76 |
'2' => Font::UNDERLINE_DOUBLE,
|
|
|
77 |
'3' => Font::UNDERLINE_SINGLEACCOUNTING,
|
|
|
78 |
'4' => Font::UNDERLINE_DOUBLEACCOUNTING,
|
|
|
79 |
],
|
|
|
80 |
'vertical' => [
|
|
|
81 |
'1' => Alignment::VERTICAL_TOP,
|
|
|
82 |
'2' => Alignment::VERTICAL_BOTTOM,
|
|
|
83 |
'4' => Alignment::VERTICAL_CENTER,
|
|
|
84 |
'8' => Alignment::VERTICAL_JUSTIFY,
|
|
|
85 |
],
|
|
|
86 |
];
|
|
|
87 |
|
|
|
88 |
public function __construct(Spreadsheet $spreadsheet, bool $readDataOnly)
|
|
|
89 |
{
|
|
|
90 |
$this->spreadsheet = $spreadsheet;
|
|
|
91 |
$this->readDataOnly = $readDataOnly;
|
|
|
92 |
}
|
|
|
93 |
|
|
|
94 |
public function read(SimpleXMLElement $sheet, int $maxRow, int $maxCol): void
|
|
|
95 |
{
|
|
|
96 |
if ($sheet->Styles->StyleRegion !== null) {
|
|
|
97 |
$this->readStyles($sheet->Styles->StyleRegion, $maxRow, $maxCol);
|
|
|
98 |
}
|
|
|
99 |
}
|
|
|
100 |
|
|
|
101 |
private function readStyles(SimpleXMLElement $styleRegion, int $maxRow, int $maxCol): void
|
|
|
102 |
{
|
|
|
103 |
foreach ($styleRegion as $style) {
|
|
|
104 |
/** @scrutinizer ignore-call */
|
|
|
105 |
$styleAttributes = $style->attributes();
|
|
|
106 |
if ($styleAttributes !== null && ($styleAttributes['startRow'] <= $maxRow) && ($styleAttributes['startCol'] <= $maxCol)) {
|
|
|
107 |
$cellRange = $this->readStyleRange($styleAttributes, $maxCol, $maxRow);
|
|
|
108 |
|
|
|
109 |
$styleAttributes = $style->Style->attributes();
|
|
|
110 |
|
|
|
111 |
$styleArray = [];
|
|
|
112 |
// We still set the number format mask for date/time values, even if readDataOnly is true
|
|
|
113 |
// so that we can identify whether a float is a float or a date value
|
|
|
114 |
$formatCode = $styleAttributes ? (string) $styleAttributes['Format'] : null;
|
|
|
115 |
if ($formatCode && Date::isDateTimeFormatCode($formatCode)) {
|
|
|
116 |
$styleArray['numberFormat']['formatCode'] = $formatCode;
|
|
|
117 |
}
|
|
|
118 |
if ($this->readDataOnly === false && $styleAttributes !== null) {
|
|
|
119 |
// If readDataOnly is false, we set all formatting information
|
|
|
120 |
$styleArray['numberFormat']['formatCode'] = $formatCode;
|
|
|
121 |
$styleArray = $this->readStyle($styleArray, $styleAttributes, /** @scrutinizer ignore-type */ $style);
|
|
|
122 |
}
|
|
|
123 |
$this->spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray);
|
|
|
124 |
}
|
|
|
125 |
}
|
|
|
126 |
}
|
|
|
127 |
|
|
|
128 |
private function addBorderDiagonal(SimpleXMLElement $srssb, array &$styleArray): void
|
|
|
129 |
{
|
|
|
130 |
if (isset($srssb->Diagonal, $srssb->{'Rev-Diagonal'})) {
|
|
|
131 |
$styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes());
|
|
|
132 |
$styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_BOTH;
|
|
|
133 |
} elseif (isset($srssb->Diagonal)) {
|
|
|
134 |
$styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes());
|
|
|
135 |
$styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_UP;
|
|
|
136 |
} elseif (isset($srssb->{'Rev-Diagonal'})) {
|
|
|
137 |
$styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->{'Rev-Diagonal'}->attributes());
|
|
|
138 |
$styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_DOWN;
|
|
|
139 |
}
|
|
|
140 |
}
|
|
|
141 |
|
|
|
142 |
private function addBorderStyle(SimpleXMLElement $srssb, array &$styleArray, string $direction): void
|
|
|
143 |
{
|
|
|
144 |
$ucDirection = ucfirst($direction);
|
|
|
145 |
if (isset($srssb->$ucDirection)) {
|
|
|
146 |
$styleArray['borders'][$direction] = self::parseBorderAttributes($srssb->$ucDirection->attributes());
|
|
|
147 |
}
|
|
|
148 |
}
|
|
|
149 |
|
|
|
150 |
private function calcRotation(SimpleXMLElement $styleAttributes): int
|
|
|
151 |
{
|
|
|
152 |
$rotation = (int) $styleAttributes->Rotation;
|
|
|
153 |
if ($rotation >= 270 && $rotation <= 360) {
|
|
|
154 |
$rotation -= 360;
|
|
|
155 |
}
|
|
|
156 |
$rotation = (abs($rotation) > 90) ? 0 : $rotation;
|
|
|
157 |
|
|
|
158 |
return $rotation;
|
|
|
159 |
}
|
|
|
160 |
|
|
|
161 |
private static function addStyle(array &$styleArray, string $key, string $value): void
|
|
|
162 |
{
|
|
|
163 |
if (array_key_exists($value, self::$mappings[$key])) {
|
|
|
164 |
$styleArray[$key] = self::$mappings[$key][$value];
|
|
|
165 |
}
|
|
|
166 |
}
|
|
|
167 |
|
|
|
168 |
private static function addStyle2(array &$styleArray, string $key1, string $key, string $value): void
|
|
|
169 |
{
|
|
|
170 |
if (array_key_exists($value, self::$mappings[$key])) {
|
|
|
171 |
$styleArray[$key1][$key] = self::$mappings[$key][$value];
|
|
|
172 |
}
|
|
|
173 |
}
|
|
|
174 |
|
|
|
175 |
private static function parseBorderAttributes(?SimpleXMLElement $borderAttributes): array
|
|
|
176 |
{
|
|
|
177 |
$styleArray = [];
|
|
|
178 |
if ($borderAttributes !== null) {
|
|
|
179 |
if (isset($borderAttributes['Color'])) {
|
|
|
180 |
$styleArray['color']['rgb'] = self::parseGnumericColour($borderAttributes['Color']);
|
|
|
181 |
}
|
|
|
182 |
|
|
|
183 |
self::addStyle($styleArray, 'borderStyle', (string) $borderAttributes['Style']);
|
|
|
184 |
}
|
|
|
185 |
|
|
|
186 |
return $styleArray;
|
|
|
187 |
}
|
|
|
188 |
|
|
|
189 |
private static function parseGnumericColour(string $gnmColour): string
|
|
|
190 |
{
|
|
|
191 |
[$gnmR, $gnmG, $gnmB] = explode(':', $gnmColour);
|
|
|
192 |
$gnmR = substr(str_pad($gnmR, 4, '0', STR_PAD_RIGHT), 0, 2);
|
|
|
193 |
$gnmG = substr(str_pad($gnmG, 4, '0', STR_PAD_RIGHT), 0, 2);
|
|
|
194 |
$gnmB = substr(str_pad($gnmB, 4, '0', STR_PAD_RIGHT), 0, 2);
|
|
|
195 |
|
|
|
196 |
return $gnmR . $gnmG . $gnmB;
|
|
|
197 |
}
|
|
|
198 |
|
|
|
199 |
private function addColors(array &$styleArray, SimpleXMLElement $styleAttributes): void
|
|
|
200 |
{
|
|
|
201 |
$RGB = self::parseGnumericColour((string) $styleAttributes['Fore']);
|
|
|
202 |
$styleArray['font']['color']['rgb'] = $RGB;
|
|
|
203 |
$RGB = self::parseGnumericColour((string) $styleAttributes['Back']);
|
|
|
204 |
$shade = (string) $styleAttributes['Shade'];
|
|
|
205 |
if (($RGB !== '000000') || ($shade !== '0')) {
|
|
|
206 |
$RGB2 = self::parseGnumericColour((string) $styleAttributes['PatternColor']);
|
|
|
207 |
if ($shade === '1') {
|
|
|
208 |
$styleArray['fill']['startColor']['rgb'] = $RGB;
|
|
|
209 |
$styleArray['fill']['endColor']['rgb'] = $RGB2;
|
|
|
210 |
} else {
|
|
|
211 |
$styleArray['fill']['endColor']['rgb'] = $RGB;
|
|
|
212 |
$styleArray['fill']['startColor']['rgb'] = $RGB2;
|
|
|
213 |
}
|
|
|
214 |
self::addStyle2($styleArray, 'fill', 'fillType', $shade);
|
|
|
215 |
}
|
|
|
216 |
}
|
|
|
217 |
|
|
|
218 |
private function readStyleRange(SimpleXMLElement $styleAttributes, int $maxCol, int $maxRow): string
|
|
|
219 |
{
|
|
|
220 |
$startColumn = Coordinate::stringFromColumnIndex((int) $styleAttributes['startCol'] + 1);
|
|
|
221 |
$startRow = $styleAttributes['startRow'] + 1;
|
|
|
222 |
|
|
|
223 |
$endColumn = ($styleAttributes['endCol'] > $maxCol) ? $maxCol : (int) $styleAttributes['endCol'];
|
|
|
224 |
$endColumn = Coordinate::stringFromColumnIndex($endColumn + 1);
|
|
|
225 |
|
|
|
226 |
$endRow = 1 + (($styleAttributes['endRow'] > $maxRow) ? $maxRow : (int) $styleAttributes['endRow']);
|
|
|
227 |
$cellRange = $startColumn . $startRow . ':' . $endColumn . $endRow;
|
|
|
228 |
|
|
|
229 |
return $cellRange;
|
|
|
230 |
}
|
|
|
231 |
|
|
|
232 |
private function readStyle(array $styleArray, SimpleXMLElement $styleAttributes, SimpleXMLElement $style): array
|
|
|
233 |
{
|
|
|
234 |
self::addStyle2($styleArray, 'alignment', 'horizontal', (string) $styleAttributes['HAlign']);
|
|
|
235 |
self::addStyle2($styleArray, 'alignment', 'vertical', (string) $styleAttributes['VAlign']);
|
|
|
236 |
$styleArray['alignment']['wrapText'] = $styleAttributes['WrapText'] == '1';
|
|
|
237 |
$styleArray['alignment']['textRotation'] = $this->calcRotation($styleAttributes);
|
|
|
238 |
$styleArray['alignment']['shrinkToFit'] = $styleAttributes['ShrinkToFit'] == '1';
|
|
|
239 |
$styleArray['alignment']['indent'] = ((int) ($styleAttributes['Indent']) > 0) ? $styleAttributes['indent'] : 0;
|
|
|
240 |
|
|
|
241 |
$this->addColors($styleArray, $styleAttributes);
|
|
|
242 |
|
|
|
243 |
$fontAttributes = $style->Style->Font->attributes();
|
|
|
244 |
if ($fontAttributes !== null) {
|
|
|
245 |
$styleArray['font']['name'] = (string) $style->Style->Font;
|
|
|
246 |
$styleArray['font']['size'] = (int) ($fontAttributes['Unit']);
|
|
|
247 |
$styleArray['font']['bold'] = $fontAttributes['Bold'] == '1';
|
|
|
248 |
$styleArray['font']['italic'] = $fontAttributes['Italic'] == '1';
|
|
|
249 |
$styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1';
|
|
|
250 |
self::addStyle2($styleArray, 'font', 'underline', (string) $fontAttributes['Underline']);
|
|
|
251 |
|
|
|
252 |
switch ($fontAttributes['Script']) {
|
|
|
253 |
case '1':
|
|
|
254 |
$styleArray['font']['superscript'] = true;
|
|
|
255 |
|
|
|
256 |
break;
|
|
|
257 |
case '-1':
|
|
|
258 |
$styleArray['font']['subscript'] = true;
|
|
|
259 |
|
|
|
260 |
break;
|
|
|
261 |
}
|
|
|
262 |
}
|
|
|
263 |
|
|
|
264 |
if (isset($style->Style->StyleBorder)) {
|
|
|
265 |
$srssb = $style->Style->StyleBorder;
|
|
|
266 |
$this->addBorderStyle($srssb, $styleArray, 'top');
|
|
|
267 |
$this->addBorderStyle($srssb, $styleArray, 'bottom');
|
|
|
268 |
$this->addBorderStyle($srssb, $styleArray, 'left');
|
|
|
269 |
$this->addBorderStyle($srssb, $styleArray, 'right');
|
|
|
270 |
$this->addBorderDiagonal($srssb, $styleArray);
|
|
|
271 |
}
|
|
|
272 |
// TO DO
|
|
|
273 |
/*
|
|
|
274 |
if (isset($style->Style->HyperLink)) {
|
|
|
275 |
$hyperlink = $style->Style->HyperLink->attributes();
|
|
|
276 |
}
|
|
|
277 |
*/
|
|
|
278 |
|
|
|
279 |
return $styleArray;
|
|
|
280 |
}
|
|
|
281 |
}
|