2323use PhpOffice \PhpSpreadsheet \Shared \Xls as SharedXls ;
2424use PhpOffice \PhpSpreadsheet \Spreadsheet ;
2525use PhpOffice \PhpSpreadsheet \Style \Alignment ;
26+ use PhpOffice \PhpSpreadsheet \Style \Border ;
2627use PhpOffice \PhpSpreadsheet \Style \Borders ;
2728use PhpOffice \PhpSpreadsheet \Style \Conditional ;
2829use PhpOffice \PhpSpreadsheet \Style \Fill ;
@@ -163,6 +164,26 @@ class Xls extends BaseReader
163164 // Size of stream blocks when using RC4 encryption
164165 const REKEY_BLOCK = 0x400 ;
165166
167+ // should be consistent with Writer\Xls\Style\CellBorder
168+ const BORDER_STYLE_MAP = [
169+ Border::BORDER_NONE , // => 0x00,
170+ Border::BORDER_THIN , // => 0x01,
171+ Border::BORDER_MEDIUM , // => 0x02,
172+ Border::BORDER_DASHED , // => 0x03,
173+ Border::BORDER_DOTTED , // => 0x04,
174+ Border::BORDER_THICK , // => 0x05,
175+ Border::BORDER_DOUBLE , // => 0x06,
176+ Border::BORDER_HAIR , // => 0x07,
177+ Border::BORDER_MEDIUMDASHED , // => 0x08,
178+ Border::BORDER_DASHDOT , // => 0x09,
179+ Border::BORDER_MEDIUMDASHDOT , // => 0x0A,
180+ Border::BORDER_DASHDOTDOT , // => 0x0B,
181+ Border::BORDER_MEDIUMDASHDOTDOT , // => 0x0C,
182+ Border::BORDER_SLANTDASHDOT , // => 0x0D,
183+ Border::BORDER_OMIT , // => 0x0E,
184+ Border::BORDER_OMIT , // => 0x0F,
185+ ];
186+
166187 /**
167188 * Summary Information stream data.
168189 */
@@ -682,6 +703,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
682703 // Parse the individual sheets
683704 $ this ->activeSheetSet = false ;
684705 foreach ($ this ->sheets as $ sheet ) {
706+ $ selectedCells = '' ;
685707 if ($ sheet ['sheetType ' ] != 0x00 ) {
686708 // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
687709 continue ;
@@ -889,7 +911,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
889911
890912 break ;
891913 case self ::XLS_TYPE_SELECTION :
892- $ this ->readSelection ();
914+ $ selectedCells = $ this ->readSelection ();
893915
894916 break ;
895917 case self ::XLS_TYPE_MERGEDCELLS :
@@ -1091,6 +1113,9 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
10911113 $ this ->phpSheet ->getComment ($ cellAddress )->setAuthor ($ noteDetails ['author ' ])->setText ($ this ->parseRichText ($ noteDetails ['objTextData ' ]['text ' ]));
10921114 }
10931115 }
1116+ if ($ selectedCells !== '' ) {
1117+ $ this ->phpSheet ->setSelectedCells ($ selectedCells );
1118+ }
10941119 }
10951120 if ($ this ->activeSheetSet === false ) {
10961121 $ this ->spreadsheet ->setActiveSheetIndex (0 );
@@ -1945,12 +1970,9 @@ private function readFont(): void
19451970 $ objFont ->colorIndex = $ colorIndex ;
19461971
19471972 // offset: 6; size: 2; font weight
1948- $ weight = self ::getUInt2d ($ recordData , 6 );
1949- switch ($ weight ) {
1950- case 0x02BC :
1951- $ objFont ->setBold (true );
1952-
1953- break ;
1973+ $ weight = self ::getUInt2d ($ recordData , 6 ); // regular=400 bold=700
1974+ if ($ weight >= 550 ) {
1975+ $ objFont ->setBold (true );
19541976 }
19551977
19561978 // offset: 8; size: 2; escapement type
@@ -4358,10 +4380,11 @@ private function readPane(): void
43584380 /**
43594381 * Read SELECTION record. There is one such record for each pane in the sheet.
43604382 */
4361- private function readSelection (): void
4383+ private function readSelection (): string
43624384 {
43634385 $ length = self ::getUInt2d ($ this ->data , $ this ->pos + 2 );
43644386 $ recordData = $ this ->readRecordData ($ this ->data , $ this ->pos + 4 , $ length );
4387+ $ selectedCells = '' ;
43654388
43664389 // move stream pointer to next record
43674390 $ this ->pos += 4 + $ length ;
@@ -4403,6 +4426,8 @@ private function readSelection(): void
44034426
44044427 $ this ->phpSheet ->setSelectedCells ($ selectedCells );
44054428 }
4429+
4430+ return $ selectedCells ;
44064431 }
44074432
44084433 private function includeCellRangeFiltered (string $ cellRangeAddress ): bool
@@ -7326,6 +7351,11 @@ public function getMapCellStyleXfIndex(): array
73267351 return $ this ->mapCellStyleXfIndex ;
73277352 }
73287353
7354+ /**
7355+ * Parse conditional formatting blocks.
7356+ *
7357+ * @see https://www.openoffice.org/sc/excelfileformat.pdf Search for CFHEADER followed by CFRULE
7358+ */
73297359 private function readCFHeader (): array
73307360 {
73317361 $ length = self ::getUInt2d ($ this ->data , $ this ->pos + 2 );
@@ -7387,20 +7417,27 @@ private function readCFRule(array $cellRangeAddresses): void
73877417 $ options = self ::getInt4d ($ recordData , 6 );
73887418
73897419 $ style = new Style (false , true ); // non-supervisor, conditional
7420+ $ noFormatSet = true ;
73907421 //$this->getCFStyleOptions($options, $style);
73917422
73927423 $ hasFontRecord = (bool ) ((0x04000000 & $ options ) >> 26 );
73937424 $ hasAlignmentRecord = (bool ) ((0x08000000 & $ options ) >> 27 );
73947425 $ hasBorderRecord = (bool ) ((0x10000000 & $ options ) >> 28 );
73957426 $ hasFillRecord = (bool ) ((0x20000000 & $ options ) >> 29 );
73967427 $ hasProtectionRecord = (bool ) ((0x40000000 & $ options ) >> 30 );
7428+ // note unexpected values for following 4
7429+ $ hasBorderLeft = !(bool ) (0x00000400 & $ options );
7430+ $ hasBorderRight = !(bool ) (0x00000800 & $ options );
7431+ $ hasBorderTop = !(bool ) (0x00001000 & $ options );
7432+ $ hasBorderBottom = !(bool ) (0x00002000 & $ options );
73977433
73987434 $ offset = 12 ;
73997435
74007436 if ($ hasFontRecord === true ) {
74017437 $ fontStyle = substr ($ recordData , $ offset , 118 );
74027438 $ this ->getCFFontStyle ($ fontStyle , $ style );
74037439 $ offset += 118 ;
7440+ $ noFormatSet = false ;
74047441 }
74057442
74067443 if ($ hasAlignmentRecord === true ) {
@@ -7410,15 +7447,17 @@ private function readCFRule(array $cellRangeAddresses): void
74107447 }
74117448
74127449 if ($ hasBorderRecord === true ) {
7413- // $borderStyle = substr($recordData, $offset, 8);
7414- // $this->getCFBorderStyle($borderStyle, $style);
7450+ $ borderStyle = substr ($ recordData , $ offset , 8 );
7451+ $ this ->getCFBorderStyle ($ borderStyle , $ style, $ hasBorderLeft , $ hasBorderRight , $ hasBorderTop , $ hasBorderBottom );
74157452 $ offset += 8 ;
7453+ $ noFormatSet = false ;
74167454 }
74177455
74187456 if ($ hasFillRecord === true ) {
74197457 $ fillStyle = substr ($ recordData , $ offset , 4 );
74207458 $ this ->getCFFillStyle ($ fillStyle , $ style );
74217459 $ offset += 4 ;
7460+ $ noFormatSet = false ;
74227461 }
74237462
74247463 if ($ hasProtectionRecord === true ) {
@@ -7446,7 +7485,7 @@ private function readCFRule(array $cellRangeAddresses): void
74467485 $ offset += $ size2 ;
74477486 }
74487487
7449- $ this ->setCFRules ($ cellRangeAddresses , $ type , $ operator , $ formula1 , $ formula2 , $ style );
7488+ $ this ->setCFRules ($ cellRangeAddresses , $ type , $ operator , $ formula1 , $ formula2 , $ style, $ noFormatSet );
74507489 }
74517490
74527491 /*private function getCFStyleOptions(int $options, Style $style): void
@@ -7459,9 +7498,23 @@ private function getCFFontStyle(string $options, Style $style): void
74597498 if ($ fontSize !== -1 ) {
74607499 $ style ->getFont ()->setSize ($ fontSize / 20 ); // Convert twips to points
74617500 }
7501+ $ options68 = self ::getInt4d ($ options , 68 );
7502+ $ options88 = self ::getInt4d ($ options , 88 );
74627503
7463- $ bold = self ::getUInt2d ($ options , 72 ) === 700 ; // 400 = normal, 700 = bold
7464- $ style ->getFont ()->setBold ($ bold );
7504+ if (($ options88 & 2 ) === 0 ) {
7505+ $ bold = self ::getUInt2d ($ options , 72 ); // 400 = normal, 700 = bold
7506+ if ($ bold !== 0 ) {
7507+ $ style ->getFont ()->setBold ($ bold >= 550 );
7508+ }
7509+ if (($ options68 & 2 ) !== 0 ) {
7510+ $ style ->getFont ()->setItalic (true );
7511+ }
7512+ }
7513+ if (($ options88 & 0x80 ) === 0 ) {
7514+ if (($ options68 & 0x80 ) !== 0 ) {
7515+ $ style ->getFont ()->setStrikethrough (true );
7516+ }
7517+ }
74657518
74667519 $ color = self ::getInt4d ($ options , 80 );
74677520
@@ -7474,9 +7527,45 @@ private function getCFFontStyle(string $options, Style $style): void
74747527 {
74757528 }*/
74767529
7477- /* private function getCFBorderStyle(string $options, Style $style): void
7530+ private function getCFBorderStyle (string $ options , Style $ style, bool $ hasBorderLeft , bool $ hasBorderRight , bool $ hasBorderTop , bool $ hasBorderBottom ): void
74787531 {
7479- }*/
7532+ $ valueArray = unpack ('V ' , $ options );
7533+ $ value = is_array ($ valueArray ) ? $ valueArray [1 ] : 0 ;
7534+ $ left = $ value & 15 ;
7535+ $ right = ($ value >> 4 ) & 15 ;
7536+ $ top = ($ value >> 8 ) & 15 ;
7537+ $ bottom = ($ value >> 12 ) & 15 ;
7538+ $ leftc = ($ value >> 16 ) & 0x7F ;
7539+ $ rightc = ($ value >> 23 ) & 0x7F ;
7540+ $ valueArray = unpack ('V ' , substr ($ options , 4 ));
7541+ $ value = is_array ($ valueArray ) ? $ valueArray [1 ] : 0 ;
7542+ $ topc = $ value & 0x7F ;
7543+ $ bottomc = ($ value & 0x3F80 ) >> 7 ;
7544+ if ($ hasBorderLeft ) {
7545+ $ style ->getBorders ()->getLeft ()
7546+ ->setBorderStyle (self ::BORDER_STYLE_MAP [$ left ]);
7547+ $ style ->getBorders ()->getLeft ()->getColor ()
7548+ ->setRGB (Xls \Color::map ($ leftc , $ this ->palette , $ this ->version )['rgb ' ]);
7549+ }
7550+ if ($ hasBorderRight ) {
7551+ $ style ->getBorders ()->getRight ()
7552+ ->setBorderStyle (self ::BORDER_STYLE_MAP [$ right ]);
7553+ $ style ->getBorders ()->getRight ()->getColor ()
7554+ ->setRGB (Xls \Color::map ($ rightc , $ this ->palette , $ this ->version )['rgb ' ]);
7555+ }
7556+ if ($ hasBorderTop ) {
7557+ $ style ->getBorders ()->getTop ()
7558+ ->setBorderStyle (self ::BORDER_STYLE_MAP [$ top ]);
7559+ $ style ->getBorders ()->getTop ()->getColor ()
7560+ ->setRGB (Xls \Color::map ($ topc , $ this ->palette , $ this ->version )['rgb ' ]);
7561+ }
7562+ if ($ hasBorderBottom ) {
7563+ $ style ->getBorders ()->getBottom ()
7564+ ->setBorderStyle (self ::BORDER_STYLE_MAP [$ bottom ]);
7565+ $ style ->getBorders ()->getBottom ()->getColor ()
7566+ ->setRGB (Xls \Color::map ($ bottomc , $ this ->palette , $ this ->version )['rgb ' ]);
7567+ }
7568+ }
74807569
74817570 private function getCFFillStyle (string $ options , Style $ style ): void
74827571 {
@@ -7526,12 +7615,14 @@ private function readCFFormula(string $recordData, int $offset, int $size): floa
75267615 }
75277616 }
75287617
7529- private function setCFRules (array $ cellRanges , string $ type , string $ operator , null |float |int |string $ formula1 , null |float |int |string $ formula2 , Style $ style ): void
7618+ private function setCFRules (array $ cellRanges , string $ type , string $ operator , null |float |int |string $ formula1 , null |float |int |string $ formula2 , Style $ style, bool $ noFormatSet ): void
75307619 {
75317620 foreach ($ cellRanges as $ cellRange ) {
75327621 $ conditional = new Conditional ();
7622+ $ conditional ->setNoFormatSet ($ noFormatSet );
75337623 $ conditional ->setConditionType ($ type );
75347624 $ conditional ->setOperatorType ($ operator );
7625+ $ conditional ->setStopIfTrue (true );
75357626 if ($ formula1 !== null ) {
75367627 $ conditional ->addCondition ($ formula1 );
75377628 }
0 commit comments