See Release Notes
Long Term Support Release
Differences Between: [Versions 401 and 402] [Versions 401 and 403]
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 $styleAttributes = $style->attributes(); 105 if ($styleAttributes !== null && ($styleAttributes['startRow'] <= $maxRow) && ($styleAttributes['startCol'] <= $maxCol)) { 106 $cellRange = $this->readStyleRange($styleAttributes, $maxCol, $maxRow); 107 108 $styleAttributes = $style->Style->attributes(); 109 110 $styleArray = []; 111 // We still set the number format mask for date/time values, even if readDataOnly is true 112 // so that we can identify whether a float is a float or a date value 113 $formatCode = $styleAttributes ? (string) $styleAttributes['Format'] : null; 114 if ($formatCode && Date::isDateTimeFormatCode($formatCode)) { 115 $styleArray['numberFormat']['formatCode'] = $formatCode; 116 } 117 if ($this->readDataOnly === false && $styleAttributes !== null) { 118 // If readDataOnly is false, we set all formatting information 119 $styleArray['numberFormat']['formatCode'] = $formatCode; 120 $styleArray = $this->readStyle($styleArray, $styleAttributes, $style); 121 } 122 $this->spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray); 123 } 124 } 125 } 126 127 private function addBorderDiagonal(SimpleXMLElement $srssb, array &$styleArray): void 128 { 129 if (isset($srssb->Diagonal, $srssb->{'Rev-Diagonal'})) { 130 $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes()); 131 $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_BOTH; 132 } elseif (isset($srssb->Diagonal)) { 133 $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes()); 134 $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_UP; 135 } elseif (isset($srssb->{'Rev-Diagonal'})) { 136 $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->{'Rev-Diagonal'}->attributes()); 137 $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_DOWN; 138 } 139 } 140 141 private function addBorderStyle(SimpleXMLElement $srssb, array &$styleArray, string $direction): void 142 { 143 $ucDirection = ucfirst($direction); 144 if (isset($srssb->$ucDirection)) { 145 $styleArray['borders'][$direction] = self::parseBorderAttributes($srssb->$ucDirection->attributes()); 146 } 147 } 148 149 private function calcRotation(SimpleXMLElement $styleAttributes): int 150 { 151 $rotation = (int) $styleAttributes->Rotation; 152 if ($rotation >= 270 && $rotation <= 360) { 153 $rotation -= 360; 154 } 155 $rotation = (abs($rotation) > 90) ? 0 : $rotation; 156 157 return $rotation; 158 } 159 160 private static function addStyle(array &$styleArray, string $key, string $value): void 161 { 162 if (array_key_exists($value, self::$mappings[$key])) { 163 $styleArray[$key] = self::$mappings[$key][$value]; 164 } 165 } 166 167 private static function addStyle2(array &$styleArray, string $key1, string $key, string $value): void 168 { 169 if (array_key_exists($value, self::$mappings[$key])) { 170 $styleArray[$key1][$key] = self::$mappings[$key][$value]; 171 } 172 } 173 174 private static function parseBorderAttributes(?SimpleXMLElement $borderAttributes): array 175 { 176 $styleArray = []; 177 if ($borderAttributes !== null) { 178 if (isset($borderAttributes['Color'])) { 179 $styleArray['color']['rgb'] = self::parseGnumericColour($borderAttributes['Color']); 180 } 181 182 self::addStyle($styleArray, 'borderStyle', (string) $borderAttributes['Style']); 183 } 184 185 return $styleArray; 186 } 187 188 private static function parseGnumericColour(string $gnmColour): string 189 { 190 [$gnmR, $gnmG, $gnmB] = explode(':', $gnmColour); 191 $gnmR = substr(str_pad($gnmR, 4, '0', STR_PAD_RIGHT), 0, 2); 192 $gnmG = substr(str_pad($gnmG, 4, '0', STR_PAD_RIGHT), 0, 2); 193 $gnmB = substr(str_pad($gnmB, 4, '0', STR_PAD_RIGHT), 0, 2); 194 195 return $gnmR . $gnmG . $gnmB; 196 } 197 198 private function addColors(array &$styleArray, SimpleXMLElement $styleAttributes): void 199 { 200 $RGB = self::parseGnumericColour((string) $styleAttributes['Fore']); 201 $styleArray['font']['color']['rgb'] = $RGB; 202 $RGB = self::parseGnumericColour((string) $styleAttributes['Back']); 203 $shade = (string) $styleAttributes['Shade']; 204 if (($RGB !== '000000') || ($shade !== '0')) { 205 $RGB2 = self::parseGnumericColour((string) $styleAttributes['PatternColor']); 206 if ($shade === '1') { 207 $styleArray['fill']['startColor']['rgb'] = $RGB; 208 $styleArray['fill']['endColor']['rgb'] = $RGB2; 209 } else { 210 $styleArray['fill']['endColor']['rgb'] = $RGB; 211 $styleArray['fill']['startColor']['rgb'] = $RGB2; 212 } 213 self::addStyle2($styleArray, 'fill', 'fillType', $shade); 214 } 215 } 216 217 private function readStyleRange(SimpleXMLElement $styleAttributes, int $maxCol, int $maxRow): string 218 { 219 $startColumn = Coordinate::stringFromColumnIndex((int) $styleAttributes['startCol'] + 1); 220 $startRow = $styleAttributes['startRow'] + 1; 221 222 $endColumn = ($styleAttributes['endCol'] > $maxCol) ? $maxCol : (int) $styleAttributes['endCol']; 223 $endColumn = Coordinate::stringFromColumnIndex($endColumn + 1); 224 225 $endRow = 1 + (($styleAttributes['endRow'] > $maxRow) ? $maxRow : (int) $styleAttributes['endRow']); 226 $cellRange = $startColumn . $startRow . ':' . $endColumn . $endRow; 227 228 return $cellRange; 229 } 230 231 private function readStyle(array $styleArray, SimpleXMLElement $styleAttributes, SimpleXMLElement $style): array 232 { 233 self::addStyle2($styleArray, 'alignment', 'horizontal', (string) $styleAttributes['HAlign']); 234 self::addStyle2($styleArray, 'alignment', 'vertical', (string) $styleAttributes['VAlign']); 235 $styleArray['alignment']['wrapText'] = $styleAttributes['WrapText'] == '1'; 236 $styleArray['alignment']['textRotation'] = $this->calcRotation($styleAttributes); 237 $styleArray['alignment']['shrinkToFit'] = $styleAttributes['ShrinkToFit'] == '1'; 238 $styleArray['alignment']['indent'] = ((int) ($styleAttributes['Indent']) > 0) ? $styleAttributes['indent'] : 0; 239 240 $this->addColors($styleArray, $styleAttributes); 241 242 $fontAttributes = $style->Style->Font->attributes(); 243 if ($fontAttributes !== null) { 244 $styleArray['font']['name'] = (string) $style->Style->Font; 245 $styleArray['font']['size'] = (int) ($fontAttributes['Unit']); 246 $styleArray['font']['bold'] = $fontAttributes['Bold'] == '1'; 247 $styleArray['font']['italic'] = $fontAttributes['Italic'] == '1'; 248 $styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1'; 249 self::addStyle2($styleArray, 'font', 'underline', (string) $fontAttributes['Underline']); 250 251 switch ($fontAttributes['Script']) { 252 case '1': 253 $styleArray['font']['superscript'] = true; 254 255 break; 256 case '-1': 257 $styleArray['font']['subscript'] = true; 258 259 break; 260 } 261 } 262 263 if (isset($style->Style->StyleBorder)) { 264 $srssb = $style->Style->StyleBorder; 265 $this->addBorderStyle($srssb, $styleArray, 'top'); 266 $this->addBorderStyle($srssb, $styleArray, 'bottom'); 267 $this->addBorderStyle($srssb, $styleArray, 'left'); 268 $this->addBorderStyle($srssb, $styleArray, 'right'); 269 $this->addBorderDiagonal($srssb, $styleArray); 270 } 271 if (isset($style->Style->HyperLink)) { 272 // TO DO 273 $hyperlink = $style->Style->HyperLink->attributes(); 274 } 275 276 return $styleArray; 277 } 278 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body