コード例 #1
0
ファイル: Book.php プロジェクト: WaylandAce/xls-reader
 public function handleName($data)
 {
     $bv = $this->biff_version;
     if ($bv < 50) {
         return;
     }
     $this->deriveEncoding();
     # <HBBHHH4B
     list($option_flags, $kb_shortcut, $name_len, $fmla_len, $extsht_index, $sheet_index, $menu_text_len, $description_text_len, $help_topic_text_len, $status_bar_text_len) = array_values(unpack('va/C2b/v3c/C4d', substr($data, 0, 14)));
     $nobj = new Name();
     $nobj->book = $this;
     ### CIRCUAL ###
     $name_index = count($this->name_obj_list);
     $this->name_obj_list[] = $nobj;
     # Not used anywhere in code. Commenting out.
     # $nobj->option_flags = $option_flags;
     $attrs = [['hidden', 1, 0], ['func', 2, 1], ['vbasic', 4, 2], ['macro', 8, 3], ['complex', 0x10, 4], ['builtin', 0x20, 5], ['funcgroup', 0xfc0, 6], ['binary', 0x1000, 12]];
     foreach ($attrs as $row) {
         list($attr, $mask, $nshift) = $row;
         $nobj->{$attr} = ($option_flags & $mask) >> $nshift;
     }
     $macro_flag = $nobj->macro ? 'M' : ' ';
     if ($bv < 80) {
         list($internal_name, $pos) = Helper::unpack_string_update_pos($data, 14, $this->encoding, $name_len);
     } else {
         list($internal_name, $pos) = Helper::unpack_unicode_update_pos($data, 14, 2, $name_len);
     }
     $nobj->extn_sheet_num = $extsht_index;
     $nobj->excel_sheet_index = $sheet_index;
     $nobj->scope = Null;
     # patched up in the names_epilogue() method
     Helper::debug("NAME[%d]:%s oflags=%d, name_len=%d, fmla_len=%d, extsht_index=%d, sheet_index=%d, name=%s, %d", $name_index, $macro_flag, $option_flags, $name_len, $fmla_len, $extsht_index, $sheet_index, $internal_name, strlen($internal_name));
     $name = $internal_name;
     if ($nobj->builtin) {
         $name = isset(Defs::$builtin_name_from_code[$name]) ? Defs::$builtin_name_from_code[$name] : "??Unknown??";
         Helper::debug("    builtin: %s", $name);
     }
     $nobj->name = $name;
     $nobj->raw_formula = substr($data, $pos);
     $nobj->basic_formula_len = $fmla_len;
     $nobj->evaluated = 0;
     Helper::debug($nobj->dump("--- handle_name: name[{$name_index}] ---", '-------------------'));
 }
コード例 #2
0
ファイル: Sheet.php プロジェクト: WaylandAce/xls-reader
 /**
  * @param Book $bk
  * @return int
  * @throws XLSParserException
  */
 function read(Book $bk)
 {
     # Unused in code
     # $r1c1 = 0;
     $oldpos = $bk->position;
     $saved_obj_id = Null;
     $bk->position = $this->position;
     $XL_SHRFMLA_ETC_ETC = [XL_SHRFMLA, XL_ARRAY, XL_TABLEOP, XL_TABLEOP2, XL_ARRAY2, XL_TABLEOP_B2];
     $bv = $this->biff_version;
     $fmt_info = $this->formatting_info;
     $do_sst_rich_text = $fmt_info && $bk->rich_text_runlist_map;
     $rowinfo_sharing_dict = [];
     /** @var $txos MSTxo[] */
     $txos = [];
     $eof_found = 0;
     while (true) {
         list($rc, $data_len, $data) = $bk->readRecordParts();
         switch ($rc) {
             case XL_NUMBER:
                 # <HHHd
                 list($rowx, $colx, $xf_index, $d) = array_values(unpack('v3a/db', substr($data, 0, 14)));
                 $this->put_cell($rowx, $colx, Null, $d, $xf_index);
                 break;
             case XL_LABELSST:
                 # <HHHi
                 list($rowx, $colx, $xf_index, $sstindex) = array_values(unpack('v3a/ib', $data));
                 $this->put_cell($rowx, $colx, XL_CELL_TEXT, $bk->sharedstrings[$sstindex], $xf_index);
                 if ($do_sst_rich_text) {
                     if (isset($bk->rich_text_runlist_map[$sstindex])) {
                         $this->rich_text_runlist_map[$rowx][$colx] = $bk->rich_text_runlist_map[$sstindex];
                     }
                 }
                 break;
             case XL_LABEL:
                 # <HHH
                 list($rowx, $colx, $xf_index) = array_values(unpack('v3a', substr($data, 0, 6)));
                 if ($bv < BIFF_FIRST_UNICODE) {
                     $strg = Helper::unpack_string($data, 6, $bk->encoding ?: $bk->deriveEncoding(), 2);
                 } else {
                     $strg = Helper::unpack_unicode($data, 6, 2);
                 }
                 $this->put_cell($rowx, $colx, XL_CELL_TEXT, $strg, $xf_index);
                 break;
             case XL_STRING:
                 # <HHH
                 list($rowx, $colx, $xf_index) = array_values(unpack('v3a', substr($data, 0, 6)));
                 if ($bv < BIFF_FIRST_UNICODE) {
                     list($strg, $pos) = Helper::unpack_string_update_pos($data, 6, $bk->encoding ?: $bk->deriveEncoding(), 2);
                     $nrt = ord($data[$pos]);
                     $pos++;
                     $runlist = [];
                     for ($tmp = 0; $tmp < $nrt; $tmp++) {
                         $runlist[] = array_values(unpack('C2a', substr($data, $pos, 2)));
                         $pos += 2;
                     }
                     assert($pos == strlen($data));
                 } else {
                     list($strg, $pos) = Helper::unpack_unicode_update_pos($data, 6, 2);
                     list($nrt) = array_values(unpack('v', substr($data, $pos, 2)));
                     $pos += 2;
                     $runlist = [];
                     for ($tmp = 0; $tmp < $nrt; $tmp++) {
                         $runlist[] = array_values(unpack('v2a', substr($data, $pos, 4)));
                         $pos += 4;
                     }
                     assert($pos == strlen($data));
                 }
                 $this->put_cell($rowx, $colx, XL_CELL_TEXT, $strg, $xf_index);
                 $this->rich_text_runlist_map[$rowx][$colx] = $runlist;
                 break;
             case XL_RK:
                 # <HHH
                 list($rowx, $colx, $xf_index) = array_values(unpack('v3a', substr($data, 0, 6)));
                 $d = Helper::unpack_RK(substr($data, 6, 4));
                 $this->put_cell($rowx, $colx, Null, $d, $xf_index);
                 break;
             case XL_MULRK:
                 # <HH
                 list($mulrk_row, $mulrk_first) = array_values(unpack('v2', substr($data, 0, 4)));
                 # <H
                 list($mulrk_last) = array_values(unpack('v', substr($data, -2)));
                 $pos = 4;
                 for ($colx = $mulrk_first; $colx <= $mulrk_last; $colx++) {
                     # <H
                     list($xf_index) = array_values(unpack('v', substr($data, $pos, 2)));
                     $d = Helper::unpack_RK(substr($data, $pos + 2, 4));
                     $pos += 6;
                     $this->put_cell($mulrk_row, $colx, Null, $d, $xf_index);
                 }
                 break;
             case XL_ROW:
                 # Version 0.6.0a3: ROW records are just not worth using (for memory allocation).
                 # Version 0.6.1: now used for formatting info.
                 if (!$fmt_info) {
                     break;
                 }
                 # <H4xH4xi
                 list($rowx, , , , , $bits1, , , , , $bits2) = array_values(unpack('va/c4b/vc/c4d/i', substr($data, 0, 16)));
                 if (!(0 <= $rowx && $rowx < $this->utter_max_rows)) {
                     break;
                 }
                 $r = isset($rowinfo_sharing_dict[$bits1][$bits2]) ? $rowinfo_sharing_dict[$bits1][$bits2] : Null;
                 if ($r == Null) {
                     $r = new Rowinfo();
                     # Using upkbits() is far too slow on a file
                     # with 30 sheets each with 10K rows :-(
                     #    upkbits(r, bits1, (
                     #        ( 0, 0x7FFF, 'height'),
                     #        (15, 0x8000, 'has_default_height'),
                     #        ))
                     #    upkbits(r, bits2, (
                     #        ( 0, 0x00000007, 'outline_level'),
                     #        ( 4, 0x00000010, 'outline_group_starts_ends'),
                     #        ( 5, 0x00000020, 'hidden'),
                     #        ( 6, 0x00000040, 'height_mismatch'),
                     #        ( 7, 0x00000080, 'has_default_xf_index'),
                     #        (16, 0x0FFF0000, 'xf_index'),
                     #        (28, 0x10000000, 'additional_space_above'),
                     #        (29, 0x20000000, 'additional_space_below'),
                     #        ))
                     # So:
                     $r->height = $bits1 & 0x7fff;
                     $r->has_default_height = $bits1 >> 15 & 1;
                     $r->outline_level = $bits2 & 7;
                     $r->outline_group_starts_ends = $bits2 >> 4 & 1;
                     $r->hidden = $bits2 >> 5 & 1;
                     $r->height_mismatch = $bits2 >> 6 & 1;
                     $r->has_default_xf_index = $bits2 >> 7 & 1;
                     $r->xf_index = $bits2 >> 16 & 0xfff;
                     $r->additional_space_above = $bits2 >> 28 & 1;
                     $r->additional_space_below = $bits2 >> 29 & 1;
                     if (!$r->has_default_xf_index) {
                         $r->xf_index = -1;
                     }
                     $rowinfo_sharing_dict[$bits1][$bits2] = $r;
                 }
                 $this->rowinfo_map[$rowx] = $r;
                 break;
             case 0x6:
                 # XL_FORMULA_OPCODES:
             # XL_FORMULA_OPCODES:
             case 0x406:
                 # XL_FORMULA_OPCODES:
             # XL_FORMULA_OPCODES:
             case 0x206:
                 # XL_FORMULA_OPCODES:
                 if ($bv >= 50) {
                     # <HHH8sH
                     list($rowx, $colx, $xf_index, $flags) = array_values(unpack('va/vb/vc/x8/ve', substr($data, 0, 16)));
                     $result_str = substr($data, 6, 8);
                     $lenlen = 2;
                     $tkarr_offset = 20;
                 } elseif ($bv >= 30) {
                     # <HHH8sH
                     list($rowx, $colx, $xf_index, $flags) = array_values(unpack('va/vb/vc/x8/ve', substr($data, 0, 16)));
                     $result_str = substr($data, 6, 8);
                     $lenlen = 2;
                     $tkarr_offset = 16;
                 } else {
                     # BIFF2
                     # <HH3s8sB
                     list($rowx, $colx, $flags) = array_values(unpack('va/vb/x11/cc', substr($data, 0, 16)));
                     $cell_attr = substr($data, 4, 3);
                     $result_str = substr($data, 7, 8);
                     $xf_index = $this->fixed_BIFF2_xfindex($cell_attr, $rowx, $colx);
                     $lenlen = 1;
                     $tkarr_offset = 16;
                 }
                 if (substr($result_str, 6, 2) == "ÿÿ") {
                     $first_byte = ord($result_str[0]);
                     switch ($first_byte) {
                         case 0:
                             # need to read next record (STRING)
                             $gotstring = 0;
                             # actually there's an optional SHRFMLA or ARRAY etc record to skip over
                             list($rc2, $data2_len, $data2) = $bk->readRecordParts();
                             switch ($rc2) {
                                 case XL_STRING:
                                 case XL_STRING_B2:
                                     $gotstring = 1;
                                     break;
                                 case XL_ARRAY:
                                     # <HHBBBxxxxxH
                                     //                                        list($row1x, $rownx, $col1x, $colnx, $array_flags, $tokslen) = array_values(unpack("v2a/c3b/x5/vc", substr($data2, 0, 14)));
                                     break;
                                 case XL_SHRFMLA:
                                     # <HHBBxBH
                                     //                                        list($row1x, $rownx, $col1x, $colnx, $nfmlas, $tokslen) = array_values(unpack("v2a/C2b/x/Cc/vd", substr($data, 0, 10)));
                                     break;
                                 default:
                                     if (!in_array($rc2, $XL_SHRFMLA_ETC_ETC)) {
                                         throw new XLSParserException(sprintf("Expected SHRFMLA, ARRAY, TABLEOP* or STRING record; found 0x%04x", $rc2));
                                     }
                             }
                             if (!$gotstring) {
                                 list($rc2, $tmp, $data2) = $bk->readRecordParts();
                                 if ($rc2 !== XL_STRING && $rc2 !== XL_STRING_B2) {
                                     throw new XLSParserException(sprintf("Expected STRING record; found 0x%04x", $rc2));
                                 }
                             }
                             $strg = $this->string_record_contents($data2);
                             $this->put_cell($rowx, $colx, XL_CELL_TEXT, $strg, $xf_index);
                             break;
                         case 1:
                             # boolean formula result
                             $value = ord($result_str[2]);
                             $this->put_cell($rowx, $colx, XL_CELL_BOOLEAN, $value, $xf_index);
                             break;
                         case 2:
                             # Error in cell
                             $value = ord($result_str[2]);
                             $this->put_cell($rowx, $colx, XL_CELL_ERROR, $value, $xf_index);
                             break;
                         case 3:
                             # empty ... i.e. empty (zero-length) string, NOT an empty cell.
                             $this->put_cell($rowx, $colx, XL_CELL_TEXT, "", $xf_index);
                             break;
                         default:
                             throw new XLSParserException(sprintf("unexpected special case (0x%02x) in FORMULA", $first_byte));
                     }
                 } else {
                     # <d
                     list($d) = array_values(unpack("d", $result_str));
                     $this->put_cell($rowx, $colx, Null, $d, $xf_index);
                 }
                 break;
             case XL_BOOLERR:
                 # <HHHBB
                 list($rowx, $colx, $xf_index, $value, $is_err) = array_values(unpack('v3a/c2', substr($data, 0, 8)));
                 # Note OOo Calc 2.0 writes 9-byte BOOLERR records.
                 # OOo docs say 8. Excel writes 8.
                 $cellty = $is_err ? XL_CELL_ERROR : XL_CELL_BOOLEAN;
                 $this->put_cell($rowx, $colx, $cellty, $value, $xf_index);
                 break;
             case XL_COLINFO:
                 if (!$fmt_info) {
                     break;
                 }
                 $c = new Colinfo();
                 # <HHHHH
                 list($first_colx, $last_colx, $c->width, $c->xf_index, $flags) = array_values(unpack('v5a', substr($data, 0, 10)));
                 #### Colinfo.width is denominated in 256ths of a character,
                 #### *not* in characters.
                 if (!(0 <= $first_colx && $first_colx <= $last_colx && $last_colx <= 256)) {
                     # Note: 256 instead of 255 is a common mistake.
                     # We silently ignore the non-existing 257th column in that case.
                     break;
                 }
                 Helper::upkbits($c, $flags, [[0, 0x1, 'hidden'], [1, 0x2, 'bit1_flag'], [8, 0x700, 'outline_level'], [12, 0x1000, 'collapsed']]);
                 for ($colx = $first_colx; $colx <= $last_colx; $colx++) {
                     if ($colx > 255) {
                         break;
                     }
                     # Excel does 0 to 256 inclusive
                     $this->colinfo_map[$colx] = $c;
                 }
                 break;
             case XL_DEFCOLWIDTH:
                 # <HHHHH
                 list($this->defcolwidth) = array_values(unpack('v', substr($data, 0, 2)));
                 break;
             case XL_STANDARDWIDTH:
                 if ($data_len == 2) {
                     list($this->standardwidth) = array_values(unpack('v', substr($data, 0, 2)));
                 }
                 break;
             case XL_GCW:
                 if (!$fmt_info) {
                     break;
                 }
                 assert($data_len == 34);
                 assert(substr($data, 0, 2) == " ");
                 # <8i
                 $iguff = array_values(unpack("i8", substr($data, 2, 32)));
                 $gcw = [];
                 //                    print_r($iguff);
                 foreach ($iguff as $bits) {
                     for ($j = 0; $j < 32; $j++) {
                         $gow[] = $bits & 1;
                         $bits >>= 1;
                     }
                 }
                 $this->gcw = $gcw;
                 break;
             case XL_BLANK:
                 if (!$fmt_info) {
                     break;
                 }
                 # <HHH
                 list($rowx, $colx, $xf_index) = array_values(unpack('v3a', substr($data, 0, 6)));
                 $this->put_cell($rowx, $colx, XL_CELL_BLANK, '', $xf_index);
                 break;
             case XL_MULBLANK:
                 if (!$fmt_info) {
                     break;
                 }
                 $nitems = $data_len >> 1;
                 # "<%dH" % nitems
                 $result = array_values(unpack('v' . $nitems . 'a', $data));
                 $rowx = $result[0];
                 $mul_first = $result[1];
                 $mul_last = $result[count($result) - 1];
                 assert($nitems == $mul_last + 4 - $mul_first);
                 $pos = 2;
                 for ($colx = $mul_first; $colx <= $mul_last; $colx++) {
                     $this->put_cell($rowx, $colx, XL_CELL_BLANK, '', $result[$pos]);
                     $pos++;
                 }
                 break;
             case XL_DIMENSION:
             case XL_DIMENSION2:
                 # Four zero bytes after some other record. See xlrd github issue 64.
                 if ($data_len == 0) {
                     break;
                 }
                 # if data_len == 10:
                 # Was crashing on BIFF 4.0 file w/o the two trailing unused bytes.
                 # Reported by Ralph Heimburger.
                 if ($bv < 80) {
                     # <HxxH
                     list($this->dimnrows, , , $this->dimncols) = array_values(unpack('va/c2b/vc', substr($data, 2, 6)));
                 } else {
                     # <ixxH
                     list($this->dimnrows, , , $this->dimncols) = array_values(unpack('ia/c2b/vc', substr($data, 4, 8)));
                 }
                 $this->nrows = $this->ncols = 0;
                 if (in_array($bv, [21, 30, 40]) && $this->book->xf_list && !$this->book->xf_epilogue_done) {
                     $this->book->XFEpilogue();
                 }
                 break;
             case XL_HLINK:
                 $this->handle_hlink($data);
                 break;
             case XL_QUICKTIP:
                 $this->handle_quicktip($data);
                 break;
             case XL_EOF:
                 $eof_found = true;
                 break;
             case XL_OBJ:
                 # handle SHEET-level objects; note there's a separate Book.handle_obj
                 $saved_obj = $this->handle_obj($data);
                 //                    $saved_obj_id = Null;
                 if ($saved_obj) {
                     $saved_obj_id = $saved_obj->id;
                 }
                 break;
             case XL_MSO_DRAWING:
                 $this->handle_msodrawingetc($rc, $data_len, $data);
                 break;
             case XL_TXO:
                 $txo = $this->handle_txo($data);
                 if ($txo && $saved_obj_id) {
                     $txos[$saved_obj_id] = $txo;
                     $saved_obj_id = Null;
                 }
                 break;
             case XL_NOTE:
                 $this->handle_note($data, $txos);
                 break;
             case XL_FEAT11:
                 $this->handle_feat11($data);
                 break;
             case 0x809:
                 # bofcodes
             # bofcodes
             case 0x409:
                 # bofcodes
             # bofcodes
             case 0x209:
                 # bofcodes
             # bofcodes
             case 0x9:
                 # bofcodes
                 # <HH
                 list($version, $boftype) = array_values(unpack('v2', substr($data, 0, 4)));
                 if ($boftype != 0x20) {
                     Helper::log("*** Unexpected embedded BOF (0x%04x) at offset %d: version=0x%04x type=0x%04x", $rc, $bk->position - $data_len - 4, $version, $boftype);
                 }
                 $done = false;
                 while (!$done) {
                     list($code, $data_len, $data) = $this->book->readRecordParts();
                     if ($code == XL_EOF) {
                         $done = true;
                     }
                 }
                 break;
             case XL_COUNTRY:
                 $bk->handleCountry($data);
                 break;
             case XL_LABELRANGES:
                 // @TODO
                 die(Helper::explain_const($rc, 'XL_'));
                 break;
             case XL_ARRAY:
                 // @TODO
                 # <HHBBBxxxxxH
                 list($row1x, $rownx, $col1x, $colnx, $array_flags, $tokslen) = array_values(unpack("v2a/C3b/x5/vc", substr($data, 0, 14)));
                 break;
             case XL_SHRFMLA:
                 # <HHBBxBH
                 list($row1x, $rownx, $col1x, $colnx, $nfmlas, $tokslen) = array_values(unpack("v2a/C2b/x/Cc/vD", substr($data, 0, 10)));
                 // @TODO ? Or not?
                 break;
             case XL_CONDFMT:
                 if (!$fmt_info) {
                     break;
                 }
                 assert($bv >= 80);
                 # <6H
                 list($num_CFs, $needs_recalc, $browx1, $browx2, $bcolx1, $bcolx2) = array_values(unpack('v6', substr($data, 0, 12)));
                 $olist = [];
                 # updated by the function
                 $pos = Helper::unpack_cell_range_address_list_update_pos($olist, $data, 12, $bv, 8);
                 break;
             case XL_CF:
                 if (!$fmt_info) {
                     break;
                 }
                 # <BBHHi
                 list($cf_type, $cmp_op, $sz1, $sz2, $flags) = array_values(unpack("C2a/v2b/ic", substr($data, 0, 10)));
                 $font_block = $flags >> 26 & 1;
                 $bord_block = $flags >> 28 & 1;
                 $patt_block = $flags >> 29 & 1;
                 $pos = 12;
                 if ($font_block) {
                     # <64x i i H H B 3x i 4x i i i 18x
                     list($font_height, $font_options, $weight, $escapement, $underline, $font_colour_index, $two_bits, $font_esc, $font_underl) = array_values(unpack("x64/i2a/v2b/Cc/x3/id/x4/i3e/x18", substr($data, $pos, 118)));
                     $font_style = $two_bits > 1 & 1;
                     $posture = $font_options > 1 & 1;
                     $font_canc = $two_bits > 7 & 1;
                     $cancellation = $font_options > 7 & 1;
                     $pos += 118;
                 }
                 if ($bord_block) {
                     $pos += 8;
                 }
                 if ($patt_block) {
                     $pos += 8;
                 }
                 $fmla1 = substr($data, $pos, $sz2);
                 $pos += $sz2;
                 assert($pos = $data_len);
                 //                    die(Helper::explain_const($rc, 'XL_'));
                 break;
             case XL_DEFAULTROWHEIGHT:
                 if ($data_len == 4) {
                     # <HH
                     list($bits, $this->default_row_height) = array_values(unpack('v2a', substr($data, 0, 4)));
                 } elseif ($data_len == 2) {
                     # <H
                     list($this->default_row_height) = array_values(unpack('v', substr($data, 0, 2)));
                     $bits = 0;
                 } else {
                     $bits = 0;
                 }
                 $this->default_row_height_mismatch = $bits & 1;
                 $this->default_row_hidden = $bits >> 1 & 1;
                 $this->default_additional_space_above = $bits >> 2 & 1;
                 $this->default_additional_space_below = $bits >> 3 & 1;
                 break;
             case XL_MERGEDCELLS:
                 if (!$fmt_info) {
                     continue;
                 }
                 $pos = Helper::unpack_cell_range_address_list_update_pos($this->merged_cells, $data, 0, $bv, 8);
                 assert($pos == $data_len);
                 break;
             case XL_WINDOW2:
                 if ($bv >= 80 && $data_len >= 14) {
                     # <HHHHxxHH
                     list($options, $this->first_visible_rowx, $this->first_visible_colx, $this->gridline_colour_index, $this->cached_page_break_preview_mag_factor, $this->cached_normal_view_mag_factor) = array_values(unpack('v7a', substr($data, 0, 14)));
                 } else {
                     assert($bv >= 30);
                     # BIFF3-7
                     # <HHH
                     list($options, $this->first_visible_rowx, $this->first_visible_colx) = array_values(unpack('v3a', substr($data, 0, 6)));
                     # <BBB
                     $this->gridline_colour_rgb = array_values(unpack('C3a', substr($data, 6, 3)));
                     $this->gridline_colour_index = Helper::nearest_colour_index($this->book->colour_map, $this->gridline_colour_rgb);
                     $this->cached_page_break_preview_mag_factor = 0;
                     # default (60%)
                     $this->cached_normal_view_mag_factor = 0;
                     # default (100%)
                 }
                 # options -- Bit, Mask, Contents:
                 # 0 0001H 0 = Show formula results 1 = Show formulas
                 # 1 0002H 0 = Do not show grid lines 1 = Show grid lines
                 # 2 0004H 0 = Do not show sheet headers 1 = Show sheet headers
                 # 3 0008H 0 = Panes are not frozen 1 = Panes are frozen (freeze)
                 # 4 0010H 0 = Show zero values as empty cells 1 = Show zero values
                 # 5 0020H 0 = Manual grid line colour 1 = Automatic grid line colour
                 # 6 0040H 0 = Columns from left to right 1 = Columns from right to left
                 # 7 0080H 0 = Do not show outline symbols 1 = Show outline symbols
                 # 8 0100H 0 = Keep splits if pane freeze is removed 1 = Remove splits if pane freeze is removed
                 # 9 0200H 0 = Sheet not selected 1 = Sheet selected (BIFF5-BIFF8)
                 # 10 0400H 0 = Sheet not visible 1 = Sheet visible (BIFF5-BIFF8)
                 # 11 0800H 0 = Show in normal view 1 = Show in page break preview (BIFF8)
                 # The freeze flag specifies, if a following PANE record (6.71) describes unfrozen or frozen panes.
                 foreach (Defs::$WINDOW2_options as $attr => $defval) {
                     $this->{$attr} = $options & 1;
                     $options >>= 1;
                 }
                 break;
             case XL_SCL:
                 list($num, $den) = array_values(unpack('v2', $data));
                 $result = 0;
                 if ($den) {
                     $result = (int) ($num * 100 / $den);
                 }
                 if (!(10 <= $result && $result <= 40)) {
                     $result = 100;
                 }
                 $this->scl_mag_factor = $result;
                 break;
             case XL_PANE:
                 list($this->vert_split_pos, $this->horz_split_pos, $this->horz_split_first_visible, $this->vert_split_first_visible, $this->split_active_pane) = array_values(unpack('v4a/Cb', substr($data, 0, 9)));
                 $this->has_pane_record = 1;
                 break;
             case XL_HORIZONTALPAGEBREAKS:
                 if (!$fmt_info) {
                     break;
                 }
                 # <H
                 list($num_breaks) = array_values(unpack('v', substr($data, 0, 2)));
                 assert($num_breaks * (2 + 4 * ($bv >= 80)) + 2 == $data_len);
                 $pos = 2;
                 if ($bv < 80) {
                     while ($pos < $data_len) {
                         # <H
                         list($tmp) = array_values(unpack('v', substr($data, $pos, 2)));
                         $this->horizontal_page_breaks[] = [$tmp, 0, 255];
                         $pos += 2;
                     }
                 } else {
                     while ($pos < $data_len) {
                         # <HHH
                         $this->horizontal_page_breaks[] = array_values(unpack('v3', substr($data, $pos, 6)));
                         $pos += 6;
                     }
                 }
                 break;
             case XL_VERTICALPAGEBREAKS:
                 // @TODO
                 die(Helper::explain_const($rc, 'XL_'));
                 break;
             default:
                 if ($bv <= 45) {
                     #### all of the following are for BIFF <= 4W
                 } else {
                     //                        $const_name = Helper::explain_const($rc, 'XL_');
                 }
                 break;
         }
         if ($eof_found) {
             break;
         }
     }
     if (!$eof_found) {
         throw new XLSParserException("Sheet {$this->number} ({$this->name}) missing EOF record");
     }
     $this->tidy_dimensions();
     $this->update_cooked_mag_factors();
     $bk->position = $oldpos;
     return 1;
 }