See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401]
1 <?php 2 3 namespace Box\Spout\Writer\XLSX\Manager\Style; 4 5 use Box\Spout\Common\Entity\Style\Color; 6 use Box\Spout\Common\Entity\Style\Style; 7 use Box\Spout\Writer\XLSX\Helper\BorderHelper; 8 9 /** 10 * Class StyleManager 11 * Manages styles to be applied to a cell 12 */ 13 class StyleManager extends \Box\Spout\Writer\Common\Manager\Style\StyleManager 14 { 15 /** @var StyleRegistry */ 16 protected $styleRegistry; 17 18 /** 19 * For empty cells, we can specify a style or not. If no style are specified, 20 * then the software default will be applied. But sometimes, it may be useful 21 * to override this default style, for instance if the cell should have a 22 * background color different than the default one or some borders 23 * (fonts property don't really matter here). 24 * 25 * @param int $styleId 26 * @return bool Whether the cell should define a custom style 27 */ 28 public function shouldApplyStyleOnEmptyCell($styleId) 29 { 30 $associatedFillId = $this->styleRegistry->getFillIdForStyleId($styleId); 31 $hasStyleCustomFill = ($associatedFillId !== null && $associatedFillId !== 0); 32 33 $associatedBorderId = $this->styleRegistry->getBorderIdForStyleId($styleId); 34 $hasStyleCustomBorders = ($associatedBorderId !== null && $associatedBorderId !== 0); 35 36 $associatedFormatId = $this->styleRegistry->getFormatIdForStyleId($styleId); 37 $hasStyleCustomFormats = ($associatedFormatId !== null && $associatedFormatId !== 0); 38 39 return ($hasStyleCustomFill || $hasStyleCustomBorders || $hasStyleCustomFormats); 40 } 41 42 /** 43 * Returns the content of the "styles.xml" file, given a list of styles. 44 * 45 * @return string 46 */ 47 public function getStylesXMLFileContent() 48 { 49 $content = <<<'EOD' 50 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 51 <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> 52 EOD; 53 54 $content .= $this->getFormatsSectionContent(); 55 $content .= $this->getFontsSectionContent(); 56 $content .= $this->getFillsSectionContent(); 57 $content .= $this->getBordersSectionContent(); 58 $content .= $this->getCellStyleXfsSectionContent(); 59 $content .= $this->getCellXfsSectionContent(); 60 $content .= $this->getCellStylesSectionContent(); 61 62 $content .= <<<'EOD' 63 </styleSheet> 64 EOD; 65 66 return $content; 67 } 68 69 /** 70 * Returns the content of the "<numFmts>" section. 71 * 72 * @return string 73 */ 74 protected function getFormatsSectionContent() 75 { 76 $tags = []; 77 $registeredFormats = $this->styleRegistry->getRegisteredFormats(); 78 foreach ($registeredFormats as $styleId) { 79 $numFmtId = $this->styleRegistry->getFormatIdForStyleId($styleId); 80 81 //Built-in formats do not need to be declared, skip them 82 if ($numFmtId < 164) { 83 continue; 84 } 85 86 /** @var Style $style */ 87 $style = $this->styleRegistry->getStyleFromStyleId($styleId); 88 $format = $style->getFormat(); 89 $tags[] = '<numFmt numFmtId="' . $numFmtId . '" formatCode="' . $format . '"/>'; 90 } 91 $content = '<numFmts count="' . \count($tags) . '">'; 92 $content .= \implode('', $tags); 93 $content .= '</numFmts>'; 94 95 return $content; 96 } 97 98 /** 99 * Returns the content of the "<fonts>" section. 100 * 101 * @return string 102 */ 103 protected function getFontsSectionContent() 104 { 105 $registeredStyles = $this->styleRegistry->getRegisteredStyles(); 106 107 $content = '<fonts count="' . \count($registeredStyles) . '">'; 108 109 /** @var Style $style */ 110 foreach ($registeredStyles as $style) { 111 $content .= '<font>'; 112 113 $content .= '<sz val="' . $style->getFontSize() . '"/>'; 114 $content .= '<color rgb="' . Color::toARGB($style->getFontColor()) . '"/>'; 115 $content .= '<name val="' . $style->getFontName() . '"/>'; 116 117 if ($style->isFontBold()) { 118 $content .= '<b/>'; 119 } 120 if ($style->isFontItalic()) { 121 $content .= '<i/>'; 122 } 123 if ($style->isFontUnderline()) { 124 $content .= '<u/>'; 125 } 126 if ($style->isFontStrikethrough()) { 127 $content .= '<strike/>'; 128 } 129 130 $content .= '</font>'; 131 } 132 133 $content .= '</fonts>'; 134 135 return $content; 136 } 137 138 /** 139 * Returns the content of the "<fills>" section. 140 * 141 * @return string 142 */ 143 protected function getFillsSectionContent() 144 { 145 $registeredFills = $this->styleRegistry->getRegisteredFills(); 146 147 // Excel reserves two default fills 148 $fillsCount = \count($registeredFills) + 2; 149 $content = \sprintf('<fills count="%d">', $fillsCount); 150 151 $content .= '<fill><patternFill patternType="none"/></fill>'; 152 $content .= '<fill><patternFill patternType="gray125"/></fill>'; 153 154 // The other fills are actually registered by setting a background color 155 foreach ($registeredFills as $styleId) { 156 /** @var Style $style */ 157 $style = $this->styleRegistry->getStyleFromStyleId($styleId); 158 159 $backgroundColor = $style->getBackgroundColor(); 160 $content .= \sprintf( 161 '<fill><patternFill patternType="solid"><fgColor rgb="%s"/></patternFill></fill>', 162 $backgroundColor 163 ); 164 } 165 166 $content .= '</fills>'; 167 168 return $content; 169 } 170 171 /** 172 * Returns the content of the "<borders>" section. 173 * 174 * @return string 175 */ 176 protected function getBordersSectionContent() 177 { 178 $registeredBorders = $this->styleRegistry->getRegisteredBorders(); 179 180 // There is one default border with index 0 181 $borderCount = \count($registeredBorders) + 1; 182 183 $content = '<borders count="' . $borderCount . '">'; 184 185 // Default border starting at index 0 186 $content .= '<border><left/><right/><top/><bottom/></border>'; 187 188 foreach ($registeredBorders as $styleId) { 189 /** @var \Box\Spout\Common\Entity\Style\Style $style */ 190 $style = $this->styleRegistry->getStyleFromStyleId($styleId); 191 $border = $style->getBorder(); 192 $content .= '<border>'; 193 194 // @link https://github.com/box/spout/issues/271 195 $sortOrder = ['left', 'right', 'top', 'bottom']; 196 197 foreach ($sortOrder as $partName) { 198 if ($border->hasPart($partName)) { 199 /** @var $part \Box\Spout\Common\Entity\Style\BorderPart */ 200 $part = $border->getPart($partName); 201 $content .= BorderHelper::serializeBorderPart($part); 202 } 203 } 204 205 $content .= '</border>'; 206 } 207 208 $content .= '</borders>'; 209 210 return $content; 211 } 212 213 /** 214 * Returns the content of the "<cellStyleXfs>" section. 215 * 216 * @return string 217 */ 218 protected function getCellStyleXfsSectionContent() 219 { 220 return <<<'EOD' 221 <cellStyleXfs count="1"> 222 <xf borderId="0" fillId="0" fontId="0" numFmtId="0"/> 223 </cellStyleXfs> 224 EOD; 225 } 226 227 /** 228 * Returns the content of the "<cellXfs>" section. 229 * 230 * @return string 231 */ 232 protected function getCellXfsSectionContent() 233 { 234 $registeredStyles = $this->styleRegistry->getRegisteredStyles(); 235 236 $content = '<cellXfs count="' . \count($registeredStyles) . '">'; 237 238 foreach ($registeredStyles as $style) { 239 $styleId = $style->getId(); 240 $fillId = $this->getFillIdForStyleId($styleId); 241 $borderId = $this->getBorderIdForStyleId($styleId); 242 $numFmtId = $this->getFormatIdForStyleId($styleId); 243 244 $content .= '<xf numFmtId="' . $numFmtId . '" fontId="' . $styleId . '" fillId="' . $fillId . '" borderId="' . $borderId . '" xfId="0"'; 245 246 if ($style->shouldApplyFont()) { 247 $content .= ' applyFont="1"'; 248 } 249 250 $content .= \sprintf(' applyBorder="%d"', $style->shouldApplyBorder() ? 1 : 0); 251 252 if ($style->shouldApplyCellAlignment() || $style->shouldWrapText()) { 253 $content .= ' applyAlignment="1">'; 254 $content .= '<alignment'; 255 if ($style->shouldApplyCellAlignment()) { 256 $content .= \sprintf(' horizontal="%s"', $style->getCellAlignment()); 257 } 258 if ($style->shouldWrapText()) { 259 $content .= ' wrapText="1"'; 260 } 261 $content .= '/>'; 262 $content .= '</xf>'; 263 } else { 264 $content .= '/>'; 265 } 266 } 267 268 $content .= '</cellXfs>'; 269 270 return $content; 271 } 272 273 /** 274 * Returns the fill ID associated to the given style ID. 275 * For the default style, we don't a fill. 276 * 277 * @param int $styleId 278 * @return int 279 */ 280 private function getFillIdForStyleId($styleId) 281 { 282 // For the default style (ID = 0), we don't want to override the fill. 283 // Otherwise all cells of the spreadsheet will have a background color. 284 $isDefaultStyle = ($styleId === 0); 285 286 return $isDefaultStyle ? 0 : ($this->styleRegistry->getFillIdForStyleId($styleId) ?: 0); 287 } 288 289 /** 290 * Returns the fill ID associated to the given style ID. 291 * For the default style, we don't a border. 292 * 293 * @param int $styleId 294 * @return int 295 */ 296 private function getBorderIdForStyleId($styleId) 297 { 298 // For the default style (ID = 0), we don't want to override the border. 299 // Otherwise all cells of the spreadsheet will have a border. 300 $isDefaultStyle = ($styleId === 0); 301 302 return $isDefaultStyle ? 0 : ($this->styleRegistry->getBorderIdForStyleId($styleId) ?: 0); 303 } 304 305 /** 306 * Returns the format ID associated to the given style ID. 307 * For the default style use general format. 308 * 309 * @param int $styleId 310 * @return int 311 */ 312 private function getFormatIdForStyleId($styleId) 313 { 314 // For the default style (ID = 0), we don't want to override the format. 315 // Otherwise all cells of the spreadsheet will have a format. 316 $isDefaultStyle = ($styleId === 0); 317 318 return $isDefaultStyle ? 0 : ($this->styleRegistry->getFormatIdForStyleId($styleId) ?: 0); 319 } 320 321 /** 322 * Returns the content of the "<cellStyles>" section. 323 * 324 * @return string 325 */ 326 protected function getCellStylesSectionContent() 327 { 328 return <<<'EOD' 329 <cellStyles count="1"> 330 <cellStyle builtinId="0" name="Normal" xfId="0"/> 331 </cellStyles> 332 EOD; 333 } 334 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body