def handle_palette(book, data):
    if not book.formatting_info:
        return
    blah = DEBUG or book.verbosity >= 2
    n_colours, = unpack("<H", data[:2])
    expected_n_colours = (16, 56)[book.biff_version >= 50]
    if (DEBUG or book.verbosity >= 1) and n_colours != expected_n_colours:
        fprintf(
            book.logfile, "NOTE *** Expected %d colours in PALETTE record, found %d\n", expected_n_colours, n_colours
        )
    elif blah:
        fprintf(book.logfile, "PALETTE record with %d colours\n", n_colours)
    fmt = "<xx%di" % n_colours  # use i to avoid long integers
    expected_size = 4 * n_colours + 2
    actual_size = len(data)
    tolerance = 4
    if not expected_size <= actual_size <= expected_size + tolerance:
        raise XLRDError("PALETTE record: expected size %d, actual size %d" % (expected_size, actual_size))
    colours = unpack(fmt, data[:expected_size])
    assert book.palette_record == []  # There should be only 1 PALETTE record
    # a colour will be 0xbbggrr
    # IOW, red is at the little end
    for i in xrange(n_colours):
        c = colours[i]
        red = c & 0xFF
        green = (c >> 8) & 0xFF
        blue = (c >> 16) & 0xFF
        old_rgb = book.colour_map[8 + i]
        new_rgb = (red, green, blue)
        book.palette_record.append(new_rgb)
        book.colour_map[8 + i] = new_rgb
        if blah:
            if new_rgb != old_rgb:
                print("%2d: %r -> %r" % (i, old_rgb, new_rgb), file=book.logfile)
Example #2
0
def handle_palette(book, data):
    if not book.formatting_info:
        return
    blah = DEBUG or book.verbosity >= 2
    n_colours, = unpack('<H', data[:2])
    expected_n_colours = (16, 56)[book.biff_version >= 50]
    if ((DEBUG or book.verbosity >= 1) and n_colours != expected_n_colours):
        fprintf(book.logfile,
                "NOTE *** Expected %d colours in PALETTE record, found %d\n",
                expected_n_colours, n_colours)
    elif blah:
        fprintf(book.logfile, "PALETTE record with %d colours\n", n_colours)
    fmt = '<xx%di' % n_colours  # use i to avoid long integers
    expected_size = 4 * n_colours + 2
    actual_size = len(data)
    tolerance = 4
    if not expected_size <= actual_size <= expected_size + tolerance:
        raise XLRDError('PALETTE record: expected size %d, actual size %d' %
                        (expected_size, actual_size))
    colours = unpack(fmt, data[:expected_size])
    assert book.palette_record == []  # There should be only 1 PALETTE record
    # a colour will be 0xbbggrr
    # IOW, red is at the little end
    for i in xrange(n_colours):
        c = colours[i]
        red = c & 0xff
        green = (c >> 8) & 0xff
        blue = (c >> 16) & 0xff
        old_rgb = book.colour_map[8 + i]
        new_rgb = (red, green, blue)
        book.palette_record.append(new_rgb)
        book.colour_map[8 + i] = new_rgb
        if blah:
            if new_rgb != old_rgb:
                print >> book.logfile, "%2d: %r -> %r" % (i, old_rgb, new_rgb)
Example #3
0
def is_date_format_string(book, fmt):
    # Heuristics:
    # Ignore "text" and [stuff in square brackets (aarrgghh -- see below)].
    # Handle backslashed-escaped chars properly.
    # E.g. hh\hmm\mss\s should produce a display like 23h59m59s
    # Date formats have one or more of ymdhs (caseless) in them.
    # Numeric formats have # and 0.
    # N.B. u'General"."' hence get rid of "text" first.
    # TODO: Find where formats are interpreted in Gnumeric
    # TODO: u'[h]\\ \\h\\o\\u\\r\\s' ([h] means don't care about hours > 23)
    state = 0
    s = ''
    ignorable = skip_char_dict.has_key
    for c in fmt:
        if state == 0:
            if c == '"':
                state = 1
            elif c in r"\_*":
                state = 2
            elif ignorable(c):
                pass
            else:
                s += c
        elif state == 1:
            if c == '"':
                state = 0
        elif state == 2:
            # Ignore char after backslash, underscore or asterisk
            state = 0
        assert 0 <= state <= 2
    if book.verbosity >= 4:
        print ("is_date_format_string: reduced format is %r" % s)
    s = fmt_bracketed_sub('', s)
    if non_date_formats.has_key(s):
        return False
    state = 0
    separator = ";"
    got_sep = 0
    date_count = num_count = 0
    for c in s:
        if date_char_dict.has_key(c):
            date_count += date_char_dict[c]
        elif num_char_dict.has_key(c):
            num_count += num_char_dict[c]
        elif c == separator:
            got_sep = 1
    # print num_count, date_count, repr(fmt)
    if date_count and not num_count:
        return True
    if num_count and not date_count:
        return False
    if date_count:
        fprintf(book.logfile,
            'WARNING *** is_date_format: ambiguous d=%d n=%d fmt=%r\n',
            date_count, num_count, fmt)
    elif not got_sep:
        fprintf(book.logfile,
            "WARNING *** format %r produces constant result\n",
            fmt)
    return date_count > num_count
Example #4
0
def is_date_format_string(book, fmt):
    # Heuristics:
    # Ignore "text" and [stuff in square brackets (aarrgghh -- see below)].
    # Handle backslashed-escaped chars properly.
    # E.g. hh\hmm\mss\s should produce a display like 23h59m59s
    # Date formats have one or more of ymdhs (caseless) in them.
    # Numeric formats have # and 0.
    # N.B. u'General"."' hence get rid of "text" first.
    # TODO: Find where formats are interpreted in Gnumeric
    # TODO: u'[h]\\ \\h\\o\\u\\r\\s' ([h] means don't care about hours > 23)
    state = 0
    s = ''
    ignorable = skip_char_dict.has_key
    for c in fmt:
        if state == 0:
            if c == '"':
                state = 1
            elif c in r"\_*":
                state = 2
            elif ignorable(c):
                pass
            else:
                s += c
        elif state == 1:
            if c == '"':
                state = 0
        elif state == 2:
            # Ignore char after backslash, underscore or asterisk
            state = 0
        assert 0 <= state <= 2
    if book.verbosity >= 4:
        print("is_date_format_string: reduced format is %r" % s)
    s = fmt_bracketed_sub('', s)
    if non_date_formats.has_key(s):
        return False
    state = 0
    separator = ";"
    got_sep = 0
    date_count = num_count = 0
    for c in s:
        if date_char_dict.has_key(c):
            date_count += date_char_dict[c]
        elif num_char_dict.has_key(c):
            num_count += num_char_dict[c]
        elif c == separator:
            got_sep = 1
    # print num_count, date_count, repr(fmt)
    if date_count and not num_count:
        return True
    if num_count and not date_count:
        return False
    if date_count:
        fprintf(book.logfile,
                'WARNING *** is_date_format: ambiguous d=%d n=%d fmt=%r\n',
                date_count, num_count, fmt)
    elif not got_sep:
        fprintf(book.logfile,
                "WARNING *** format %r produces constant result\n", fmt)
    return date_count > num_count
 def check_same(book_arg, xf_arg, parent_arg, attr):
     # the _arg caper is to avoid a Warning msg from Python 2.1 :-(
     if getattr(xf_arg, attr) != getattr(parent_arg, attr):
         fprintf(
             book_arg.logfile,
             "NOTE !!! XF[%d] parent[%d] %s different\n",
             xf_arg.xf_index,
             parent_arg.xf_index,
             attr,
         )
def handle_style(book, data):
    if not book.formatting_info:
        return
    blah = DEBUG or book.verbosity >= 2
    bv = book.biff_version
    flag_and_xfx, built_in_id, level = unpack("<HBB", data[:4])
    xf_index = flag_and_xfx & 0x0FFF
    if data == b"\0\0\0\0" and "Normal" not in book.style_name_map:
        # Erroneous record (doesn't have built-in bit set).
        # Example file supplied by Jeff Bell.
        built_in = 1
        built_in_id = 0
        xf_index = 0
        name = "Normal"
        level = 255
    elif flag_and_xfx & 0x8000:
        # built-in style
        built_in = 1
        name = built_in_style_names[built_in_id]
        if 1 <= built_in_id <= 2:
            name += str(level + 1)
    else:
        # user-defined style
        built_in = 0
        built_in_id = 0
        level = 0
        if bv >= 80:
            try:
                name = unpack_unicode(data, 2, lenlen=2)
            except UnicodeDecodeError:
                print(
                    "STYLE: built_in=%d xf_index=%d built_in_id=%d level=%d" % (built_in, xf_index, built_in_id, level),
                    file=book.logfile,
                )
                print("raw bytes:", repr(data[2:]), file=book.logfile)
                raise
        else:
            name = unpack_string(data, 2, book.encoding, lenlen=1)
        if blah and not name:
            print("WARNING *** A user-defined style has a zero-length name", file=book.logfile)
    book.style_name_map[name] = (built_in, xf_index)
    if blah:
        fprintf(
            book.logfile,
            "STYLE: built_in=%d xf_index=%d built_in_id=%d level=%d name=%r\n",
            built_in,
            xf_index,
            built_in_id,
            level,
            name,
        )
def palette_epilogue(book):
    # Check colour indexes in fonts etc.
    # This must be done here as FONT records
    # come *before* the PALETTE record :-(
    for font in book.font_list:
        if font.font_index == 4:  # the missing font record
            continue
        cx = font.colour_index
        if cx == 0x7FFF:  # system window text colour
            continue
        if cx in book.colour_map:
            book.colour_indexes_used[cx] = 1
        elif book.verbosity:
            print("Size of colour table:", len(book.colour_map), file=book.logfile)
            fprintf(book.logfile, "*** Font #%d (%r): colour index 0x%04x is unknown\n", font.font_index, font.name, cx)
    if book.verbosity >= 1:
        used = sorted(book.colour_indexes_used.keys())
        print("\nColour indexes used:\n%r\n" % used, file=book.logfile)
def palette_epilogue(book):
    # Check colour indexes in fonts etc.
    # This must be done here as FONT records
    # come *before* the PALETTE record :-(
    for font in book.font_list:
        if font.font_index == 4: # the missing font record
            continue
        cx = font.colour_index
        if cx == 0x7fff: # system window text colour
            continue
        if cx in book.colour_map:
            book.colour_indexes_used[cx] = 1
        elif book.verbosity:
            print("Size of colour table:", len(book.colour_map), file=book.logfile)
            fprintf(book.logfile, "*** Font #%d (%r): colour index 0x%04x is unknown\n",
                font.font_index, font.name, cx)
    if book.verbosity >= 1:
        used = sorted(book.colour_indexes_used.keys())
        print("\nColour indexes used:\n%r\n" % used, file=book.logfile)
def handle_style(book, data):
    if not book.formatting_info:
        return
    blah = DEBUG or book.verbosity >= 2
    bv = book.biff_version
    flag_and_xfx, built_in_id, level = unpack('<HBB', data[:4])
    xf_index = flag_and_xfx & 0x0fff
    if (data == b"\0\0\0\0"
    and "Normal" not in book.style_name_map):
        # Erroneous record (doesn't have built-in bit set).
        # Example file supplied by Jeff Bell.
        built_in = 1
        built_in_id = 0
        xf_index = 0
        name = "Normal"
        level = 255
    elif flag_and_xfx & 0x8000:
        # built-in style
        built_in = 1
        name = built_in_style_names[built_in_id]
        if 1 <= built_in_id <= 2:
            name += str(level + 1)
    else:
        # user-defined style
        built_in = 0
        built_in_id = 0
        level = 0
        if bv >= 80:
            try:
                name = unpack_unicode(data, 2, lenlen=2)
            except UnicodeDecodeError:
                print("STYLE: built_in=%d xf_index=%d built_in_id=%d level=%d" \
                    % (built_in, xf_index, built_in_id, level), file=book.logfile)
                print("raw bytes:", repr(data[2:]), file=book.logfile)
                raise
        else:
            name = unpack_string(data, 2, book.encoding, lenlen=1)
        if blah and not name:
            print("WARNING *** A user-defined style has a zero-length name", file=book.logfile)
    book.style_name_map[name] = (built_in, xf_index)
    if blah:
        fprintf(book.logfile, "STYLE: built_in=%d xf_index=%d built_in_id=%d level=%d name=%r\n",
            built_in, xf_index, built_in_id, level, name)
Example #10
0
def handle_format(self, data, rectype=XL_FORMAT):
    DEBUG = 0
    bv = self.biff_version
    if rectype == XL_FORMAT2:
        bv = min(bv, 30)
    if not self.encoding:
        self.derive_encoding()
    strpos = 2
    if bv >= 50:
        fmtkey = unpack('<H', data[0:2])[0]
    else:
        fmtkey = self.actualfmtcount
        if bv <= 30:
            strpos = 0
    self.actualfmtcount += 1
    if bv >= 80:
        unistrg = unpack_unicode(data, 2)
    else:
        unistrg = unpack_string(data, strpos, self.encoding, lenlen=1)
    blah = DEBUG or self.verbosity >= 3
    if blah:
        fprintf(self.logfile,
            "FORMAT: count=%d fmtkey=0x%04x (%d) s=%r\n",
            self.actualfmtcount, fmtkey, fmtkey, unistrg)
    is_date_s = self.is_date_format_string(unistrg)
    ty = [FGE, FDT][is_date_s]
    if not(fmtkey > 163 or bv < 50):
        # user_defined if fmtkey > 163
        # N.B. Gnumeric incorrectly starts these at 50 instead of 164 :-(
        # if earlier than BIFF 5, standard info is useless
        std_ty = std_format_code_types.get(fmtkey, FUN)
        # print "std ty", std_ty
        is_date_c = std_ty == FDT
        if 0 < fmtkey < 50 and (is_date_c ^ is_date_s):
            DEBUG = 2
            fprintf(self.logfile,
                "WARNING *** Conflict between "
                "std format key %d and its format string %r\n",
                fmtkey, unistrg)
    if DEBUG == 2:
        fprintf(self.logfile,
            "ty: %d; is_date_c: %r; is_date_s: %r; fmt_strg: %r",
            ty, is_date_c, is_date_s, unistrg)
    fmtobj = Format(fmtkey, ty, unistrg)
    if blah:
        fmtobj.dump(self.logfile,
            header="--- handle_format [%d] ---" % (self.actualfmtcount-1, ))
    self.format_map[fmtkey] = fmtobj
    self.format_list.append(fmtobj)
def handle_format(self, data, rectype=XL_FORMAT):
    DEBUG = 0
    bv = self.biff_version
    if rectype == XL_FORMAT2:
        bv = min(bv, 30)
    if not self.encoding:
        self.derive_encoding()
    strpos = 2
    if bv >= 50:
        fmtkey = unpack('<H', data[0:2])[0]
    else:
        fmtkey = self.actualfmtcount
        if bv <= 30:
            strpos = 0
    self.actualfmtcount += 1
    if bv >= 80:
        unistrg = unpack_unicode(data, 2)
    else:
        unistrg = unpack_string(data, strpos, self.encoding, lenlen=1)
    blah = DEBUG or self.verbosity >= 3
    if blah:
        fprintf(self.logfile,
            "FORMAT: count=%d fmtkey=0x%04x (%d) s=%r\n",
            self.actualfmtcount, fmtkey, fmtkey, unistrg)
    is_date_s = self.is_date_format_string(unistrg)
    ty = [FGE, FDT][is_date_s]
    if not(fmtkey > 163 or bv < 50):
        # user_defined if fmtkey > 163
        # N.B. Gnumeric incorrectly starts these at 50 instead of 164 :-(
        # if earlier than BIFF 5, standard info is useless
        std_ty = std_format_code_types.get(fmtkey, FUN)
        # print "std ty", std_ty
        is_date_c = std_ty == FDT
        if self.verbosity and 0 < fmtkey < 50 and (is_date_c ^ is_date_s):
            DEBUG = 2
            fprintf(self.logfile,
                "WARNING *** Conflict between "
                "std format key %d and its format string %r\n",
                fmtkey, unistrg)
    if DEBUG == 2:
        fprintf(self.logfile,
            "ty: %d; is_date_c: %r; is_date_s: %r; fmt_strg: %r",
            ty, is_date_c, is_date_s, unistrg)
    fmtobj = Format(fmtkey, ty, unistrg)
    if blah:
        fmtobj.dump(self.logfile,
            header="--- handle_format [%d] ---" % (self.actualfmtcount-1, ))
    self.format_map[fmtkey] = fmtobj
    self.format_list.append(fmtobj)
Example #12
0
def xf_epilogue(self):
    # self is a Book instance.
    self._xf_epilogue_done = 1
    num_xfs = len(self.xf_list)
    blah = DEBUG or self.verbosity >= 3
    blah1 = DEBUG or self.verbosity >= 1
    if blah:
        fprintf(self.logfile, "xf_epilogue called ...\n")

    def check_same(book_arg, xf_arg, parent_arg, attr):
        # the _arg caper is to avoid a Warning msg from Python 2.1 :-(
        if getattr(xf_arg, attr) != getattr(parent_arg, attr):
            fprintf(book_arg.logfile,
                "NOTE !!! XF[%d] parent[%d] %s different\n",
                xf_arg.xf_index, parent_arg.xf_index, attr)

    for xfx in xrange(num_xfs):
        xf = self.xf_list[xfx]
        if not self.format_map.has_key(xf.format_key):
            msg = "ERROR *** XF[%d] unknown format key (%d, 0x%04x)\n"
            fprintf(self.logfile, msg,
                    xf.xf_index, xf.format_key, xf.format_key)
            xf.format_key = 0
        cellty_from_fmtty = {
            FNU: XL_CELL_NUMBER,
            FUN: XL_CELL_NUMBER,
            FGE: XL_CELL_NUMBER,
            FDT: XL_CELL_DATE,
            FTX: XL_CELL_NUMBER, # Yes, a number can be formatted as text.
            }
        fmt = self.format_map[xf.format_key]
        cellty = cellty_from_fmtty[fmt.type]
        self._xf_index_to_xl_type_map[xf.xf_index] = cellty
        # Now for some assertions etc
        if not self.formatting_info:
            continue
        if xf.is_style:
            continue
        if not(0 <= xf.parent_style_index < num_xfs):
            fprintf(self.logfile,
                "WARNING *** XF[%d]: is_style=%d but parent_style_index=%d\n",
                xf.xf_index, xf.is_style, xf.parent_style_index)
            # make it conform
            xf.parent_style_index = 0
        if self.biff_version >= 30:
            assert xf.parent_style_index != xf.xf_index
            assert self.xf_list[xf.parent_style_index].is_style
            if blah1 and xf.parent_style_index > xf.xf_index:
                fprintf(self.logfile,
                    "NOTE !!! XF[%d]: parent_style_index is %d; out of order?\n",
                    xf.xf_index, xf.parent_style_index)
            parent = self.xf_list[xf.parent_style_index]
            if not xf._alignment_flag and not parent._alignment_flag:
                if blah1: check_same(self, xf, parent, 'alignment')
            if not xf._background_flag and not parent._background_flag:
                if blah1: check_same(self, xf, parent, 'background')
            if not xf._border_flag and not parent._border_flag:
                if blah1: check_same(self, xf, parent, 'border')
            if not xf._protection_flag and not parent._protection_flag:
                if blah1: check_same(self, xf, parent, 'protection')
            if not xf._format_flag and not parent._format_flag:
                if blah1 and xf.format_key != parent.format_key:
                    fprintf(self.logfile,
                        "NOTE !!! XF[%d] fmtk=%d, parent[%d] fmtk=%r\n%r / %r\n",
                        xf.xf_index, xf.format_key, parent.xf_index, parent.format_key,
                        self.format_map[xf.format_key].format_str,
                        self.format_map[parent.format_key].format_str)
            if not xf._font_flag and not parent._font_flag:
                if blah1 and xf.font_index != parent.font_index:
                    fprintf(self.logfile,
                        "NOTE !!! XF[%d] fontx=%d, parent[%d] fontx=%r\n",
                        xf.xf_index, xf.font_index, parent.xf_index, parent.font_index)
Example #13
0
def handle_xf(self, data):
    ### self is a Book instance
    # DEBUG = 0
    blah = DEBUG or self.verbosity >= 3
    bv = self.biff_version
    xf = XF()
    xf.alignment = XFAlignment()
    xf.alignment.indent_level = 0
    xf.alignment.shrink_to_fit = 0
    xf.alignment.text_direction = 0
    xf.border = XFBorder()
    xf.border.diag_up = 0
    xf.border.diag_down = 0
    xf.border.diag_colour_index = 0
    xf.border.diag_line_style = 0 # no line
    xf.background = XFBackground()
    xf.protection = XFProtection()
    # fill in the known standard formats
    if bv >= 50 and not self.xfcount:
        # i.e. do this once before we process the first XF record
        for x in std_format_code_types.keys():
            if not self.format_map.has_key(x):
                ty = std_format_code_types[x]
                fmt_str = std_format_strings[x]
                fmtobj = Format(x, ty, fmt_str)
                self.format_map[x] = fmtobj
    if bv >= 80:
        unpack_fmt = '<HHHBBBBIiH'
        (xf.font_index, xf.format_key, pkd_type_par,
        pkd_align1, xf.alignment.rotation, pkd_align2,
        pkd_used, pkd_brdbkg1, pkd_brdbkg2, pkd_brdbkg3,
        ) = unpack(unpack_fmt, data[0:20])
        upkbits(xf.protection, pkd_type_par, (
            (0, 0x01, 'cell_locked'),
            (1, 0x02, 'formula_hidden'),
            ))
        upkbits(xf, pkd_type_par, (
            (2, 0x0004, 'is_style'),
            # Following is not in OOo docs, but is mentioned
            # in Gnumeric source and also in (deep breath)
            # org.apache.poi.hssf.record.ExtendedFormatRecord.java
            (3, 0x0008, 'lotus_123_prefix'), # Meaning is not known.
            (4, 0xFFF0, 'parent_style_index'),
            ))
        upkbits(xf.alignment, pkd_align1, (
            (0, 0x07, 'hor_align'),
            (3, 0x08, 'text_wrapped'),
            (4, 0x70, 'vert_align'),
            ))
        upkbits(xf.alignment, pkd_align2, (
            (0, 0x0f, 'indent_level'),
            (4, 0x10, 'shrink_to_fit'),
            (6, 0xC0, 'text_direction'),
            ))
        reg = pkd_used >> 2
        for attr_stem in \
            "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, reg & 1)
            reg >>= 1
        upkbitsL(xf.border, pkd_brdbkg1, (
            (0,  0x0000000f,  'left_line_style'),
            (4,  0x000000f0,  'right_line_style'),
            (8,  0x00000f00,  'top_line_style'),
            (12, 0x0000f000,  'bottom_line_style'),
            (16, 0x007f0000,  'left_colour_index'),
            (23, 0x3f800000,  'right_colour_index'),
            (30, 0x40000000,  'diag_down'),
            (31, 0x80000000, 'diag_up'),
            ))
        upkbits(xf.border, pkd_brdbkg2, (
            (0,  0x0000007F, 'top_colour_index'),
            (7,  0x00003F80, 'bottom_colour_index'),
            (14, 0x001FC000, 'diag_colour_index'),
            (21, 0x01E00000, 'diag_line_style'),
            ))
        upkbitsL(xf.background, pkd_brdbkg2, (
            (26, 0xFC000000, 'fill_pattern'),
            ))
        upkbits(xf.background, pkd_brdbkg3, (
            (0, 0x007F, 'pattern_colour_index'),
            (7, 0x3F80, 'background_colour_index'),
            ))
    elif bv >= 50:
        unpack_fmt = '<HHHBBIi'
        (xf.font_index, xf.format_key, pkd_type_par,
        pkd_align1, pkd_orient_used,
        pkd_brdbkg1, pkd_brdbkg2,
        ) = unpack(unpack_fmt, data[0:16])
        upkbits(xf.protection, pkd_type_par, (
            (0, 0x01, 'cell_locked'),
            (1, 0x02, 'formula_hidden'),
            ))
        upkbits(xf, pkd_type_par, (
            (2, 0x0004, 'is_style'),
            (3, 0x0008, 'lotus_123_prefix'), # Meaning is not known.
            (4, 0xFFF0, 'parent_style_index'),
            ))
        upkbits(xf.alignment, pkd_align1, (
            (0, 0x07, 'hor_align'),
            (3, 0x08, 'text_wrapped'),
            (4, 0x70, 'vert_align'),
            ))
        orientation = pkd_orient_used & 0x03
        xf.alignment.rotation = [0, 255, 90, 180][orientation]
        reg = pkd_orient_used >> 2
        for attr_stem in \
            "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, reg & 1)
            reg >>= 1
        upkbitsL(xf.background, pkd_brdbkg1, (
            ( 0, 0x0000007F, 'pattern_colour_index'),
            ( 7, 0x00003F80, 'background_colour_index'),
            (16, 0x003F0000, 'fill_pattern'),
            ))
        upkbitsL(xf.border, pkd_brdbkg1, (
            (22, 0x01C00000,  'bottom_line_style'),
            (25, 0xFE000000, 'bottom_colour_index'),
            ))
        upkbits(xf.border, pkd_brdbkg2, (
            ( 0, 0x00000007, 'top_line_style'),
            ( 3, 0x00000038, 'left_line_style'),
            ( 6, 0x000001C0, 'right_line_style'),
            ( 9, 0x0000FE00, 'top_colour_index'),
            (16, 0x007F0000, 'left_colour_index'),
            (23, 0x3F800000, 'right_colour_index'),
            ))
    elif bv >= 40:
        unpack_fmt = '<BBHBBHI'
        (xf.font_index, xf.format_key, pkd_type_par,
        pkd_align_orient, pkd_used,
        pkd_bkg_34, pkd_brd_34,
        ) = unpack(unpack_fmt, data[0:12])
        upkbits(xf.protection, pkd_type_par, (
            (0, 0x01, 'cell_locked'),
            (1, 0x02, 'formula_hidden'),
            ))
        upkbits(xf, pkd_type_par, (
            (2, 0x0004, 'is_style'),
            (3, 0x0008, 'lotus_123_prefix'), # Meaning is not known.
            (4, 0xFFF0, 'parent_style_index'),
            ))
        upkbits(xf.alignment, pkd_align_orient, (
            (0, 0x07, 'hor_align'),
            (3, 0x08, 'text_wrapped'),
            (4, 0x30, 'vert_align'),
            ))
        orientation = (pkd_align_orient & 0xC0) >> 6
        xf.alignment.rotation = [0, 255, 90, 180][orientation]
        reg = pkd_used >> 2
        for attr_stem in \
            "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, reg & 1)
            reg >>= 1
        upkbits(xf.background, pkd_bkg_34, (
            ( 0, 0x003F, 'fill_pattern'),
            ( 6, 0x07C0, 'pattern_colour_index'),
            (11, 0xF800, 'background_colour_index'),
            ))
        upkbitsL(xf.border, pkd_brd_34, (
            ( 0, 0x00000007,  'top_line_style'),
            ( 3, 0x000000F8,  'top_colour_index'),
            ( 8, 0x00000700,  'left_line_style'),
            (11, 0x0000F800,  'left_colour_index'),
            (16, 0x00070000,  'bottom_line_style'),
            (19, 0x00F80000,  'bottom_colour_index'),
            (24, 0x07000000,  'right_line_style'),
            (27, 0xF8000000, 'right_colour_index'),
            ))
    elif bv == 30:
        unpack_fmt = '<BBBBHHI'
        (xf.font_index, xf.format_key, pkd_type_prot,
        pkd_used, pkd_align_par,
        pkd_bkg_34, pkd_brd_34,
        ) = unpack(unpack_fmt, data[0:12])
        upkbits(xf.protection, pkd_type_prot, (
            (0, 0x01, 'cell_locked'),
            (1, 0x02, 'formula_hidden'),
            ))
        upkbits(xf, pkd_type_prot, (
            (2, 0x0004, 'is_style'),
            (3, 0x0008, 'lotus_123_prefix'), # Meaning is not known.
            ))
        upkbits(xf.alignment, pkd_align_par, (
            (0, 0x07, 'hor_align'),
            (3, 0x08, 'text_wrapped'),
            ))
        upkbits(xf, pkd_align_par, (
            (4, 0xFFF0, 'parent_style_index'),
            ))
        reg = pkd_used >> 2
        for attr_stem in \
            "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, reg & 1)
            reg >>= 1
        upkbits(xf.background, pkd_bkg_34, (
            ( 0, 0x003F, 'fill_pattern'),
            ( 6, 0x07C0, 'pattern_colour_index'),
            (11, 0xF800, 'background_colour_index'),
            ))
        upkbitsL(xf.border, pkd_brd_34, (
            ( 0, 0x00000007,  'top_line_style'),
            ( 3, 0x000000F8,  'top_colour_index'),
            ( 8, 0x00000700,  'left_line_style'),
            (11, 0x0000F800,  'left_colour_index'),
            (16, 0x00070000,  'bottom_line_style'),
            (19, 0x00F80000,  'bottom_colour_index'),
            (24, 0x07000000,  'right_line_style'),
            (27, 0xF8000000, 'right_colour_index'),
            ))
        xf.alignment.vert_align = 2 # bottom
        xf.alignment.rotation = 0
    elif bv == 21:
        #### Warning: incomplete treatment; formatting_info not fully supported.
        #### Probably need to offset incoming BIFF2 XF[n] to BIFF8-like XF[n+16],
        #### and create XF[0:16] like the standard ones in BIFF8
        #### *AND* add 16 to all XF references in cell records :-(
        (xf.font_index, format_etc, halign_etc) = unpack('<BxBB', data)
        xf.format_key = format_etc & 0x3F
        upkbits(xf.protection, format_etc, (
            (6, 0x40, 'cell_locked'),
            (7, 0x80, 'formula_hidden'),
            ))
        upkbits(xf.alignment, halign_etc, (
            (0, 0x07, 'hor_align'),
            ))
        for mask, side in ((0x08, 'left'), (0x10, 'right'), (0x20, 'top'), (0x40, 'bottom')):
            if halign_etc & mask:
                colour_index, line_style = 8, 1 # black, thin
            else:
                colour_index, line_style = 0, 0 # none, none
            setattr(xf.border, side + '_colour_index', colour_index)
            setattr(xf.border, side + '_line_style', line_style)
        bg = xf.background
        if halign_etc & 0x80:
            bg.fill_pattern = 17
        else:
            bg.fill_pattern = 0
        bg.background_colour_index = 9 # white
        bg.pattern_colour_index = 8 # black
        xf.parent_style_index = 0 # ???????????
        xf.alignment.vert_align = 2 # bottom
        xf.alignment.rotation = 0
        for attr_stem in \
            "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, 1)
    else:
        raise XLRDError('programmer stuff-up: bv=%d' % bv)

    xf.xf_index = len(self.xf_list)
    self.xf_list.append(xf)
    self.xfcount += 1
    if blah:
        xf.dump(
            self.logfile,
            header="--- handle_xf: xf[%d] ---" % xf.xf_index,
            footer=" ",
        )
    # Now for some assertions ...
    if self.formatting_info:
        if xf.is_style and xf.parent_style_index != 0x0FFF:
            msg = "WARNING *** XF[%d] is a style XF but parent_style_index is 0x%04x, not 0x0fff\n"
            fprintf(self.logfile, msg, xf.xf_index, xf.parent_style_index)
        check_colour_indexes_in_obj(self, xf, xf.xf_index)
    if not self.format_map.has_key(xf.format_key):
        msg = "WARNING *** XF[%d] unknown (raw) format key (%d, 0x%04x)\n"
        fprintf(self.logfile, msg,
                xf.xf_index, xf.format_key, xf.format_key)
        xf.format_key = 0
def handle_xf(self, data):
    ### self is a Book instance
    # DEBUG = 0
    blah = DEBUG or self.verbosity >= 3
    bv = self.biff_version
    xf = XF()
    xf.alignment = XFAlignment()
    xf.alignment.indent_level = 0
    xf.alignment.shrink_to_fit = 0
    xf.alignment.text_direction = 0
    xf.border = XFBorder()
    xf.border.diag_up = 0
    xf.border.diag_down = 0
    xf.border.diag_colour_index = 0
    xf.border.diag_line_style = 0  # no line
    xf.background = XFBackground()
    xf.protection = XFProtection()
    # fill in the known standard formats
    if bv >= 50 and not self.xfcount:
        # i.e. do this once before we process the first XF record
        fill_in_standard_formats(self)
    if bv >= 80:
        unpack_fmt = "<HHHBBBBIiH"
        (
            xf.font_index,
            xf.format_key,
            pkd_type_par,
            pkd_align1,
            xf.alignment.rotation,
            pkd_align2,
            pkd_used,
            pkd_brdbkg1,
            pkd_brdbkg2,
            pkd_brdbkg3,
        ) = unpack(unpack_fmt, data[0:20])
        upkbits(xf.protection, pkd_type_par, ((0, 0x01, "cell_locked"), (1, 0x02, "formula_hidden")))
        upkbits(
            xf,
            pkd_type_par,
            (
                (2, 0x0004, "is_style"),
                # Following is not in OOo docs, but is mentioned
                # in Gnumeric source and also in (deep breath)
                # org.apache.poi.hssf.record.ExtendedFormatRecord.java
                (3, 0x0008, "lotus_123_prefix"),  # Meaning is not known.
                (4, 0xFFF0, "parent_style_index"),
            ),
        )
        upkbits(xf.alignment, pkd_align1, ((0, 0x07, "hor_align"), (3, 0x08, "text_wrapped"), (4, 0x70, "vert_align")))
        upkbits(
            xf.alignment,
            pkd_align2,
            ((0, 0x0F, "indent_level"), (4, 0x10, "shrink_to_fit"), (6, 0xC0, "text_direction")),
        )
        reg = pkd_used >> 2
        for attr_stem in "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, reg & 1)
            reg >>= 1
        upkbitsL(
            xf.border,
            pkd_brdbkg1,
            (
                (0, 0x0000000F, "left_line_style"),
                (4, 0x000000F0, "right_line_style"),
                (8, 0x00000F00, "top_line_style"),
                (12, 0x0000F000, "bottom_line_style"),
                (16, 0x007F0000, "left_colour_index"),
                (23, 0x3F800000, "right_colour_index"),
                (30, 0x40000000, "diag_down"),
                (31, 0x80000000, "diag_up"),
            ),
        )
        upkbits(
            xf.border,
            pkd_brdbkg2,
            (
                (0, 0x0000007F, "top_colour_index"),
                (7, 0x00003F80, "bottom_colour_index"),
                (14, 0x001FC000, "diag_colour_index"),
                (21, 0x01E00000, "diag_line_style"),
            ),
        )
        upkbitsL(xf.background, pkd_brdbkg2, ((26, 0xFC000000, "fill_pattern"),))
        upkbits(
            xf.background, pkd_brdbkg3, ((0, 0x007F, "pattern_colour_index"), (7, 0x3F80, "background_colour_index"))
        )
    elif bv >= 50:
        unpack_fmt = "<HHHBBIi"
        (xf.font_index, xf.format_key, pkd_type_par, pkd_align1, pkd_orient_used, pkd_brdbkg1, pkd_brdbkg2) = unpack(
            unpack_fmt, data[0:16]
        )
        upkbits(xf.protection, pkd_type_par, ((0, 0x01, "cell_locked"), (1, 0x02, "formula_hidden")))
        upkbits(
            xf,
            pkd_type_par,
            (
                (2, 0x0004, "is_style"),
                (3, 0x0008, "lotus_123_prefix"),  # Meaning is not known.
                (4, 0xFFF0, "parent_style_index"),
            ),
        )
        upkbits(xf.alignment, pkd_align1, ((0, 0x07, "hor_align"), (3, 0x08, "text_wrapped"), (4, 0x70, "vert_align")))
        orientation = pkd_orient_used & 0x03
        xf.alignment.rotation = [0, 255, 90, 180][orientation]
        reg = pkd_orient_used >> 2
        for attr_stem in "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, reg & 1)
            reg >>= 1
        upkbitsL(
            xf.background,
            pkd_brdbkg1,
            (
                (0, 0x0000007F, "pattern_colour_index"),
                (7, 0x00003F80, "background_colour_index"),
                (16, 0x003F0000, "fill_pattern"),
            ),
        )
        upkbitsL(
            xf.border, pkd_brdbkg1, ((22, 0x01C00000, "bottom_line_style"), (25, 0xFE000000, "bottom_colour_index"))
        )
        upkbits(
            xf.border,
            pkd_brdbkg2,
            (
                (0, 0x00000007, "top_line_style"),
                (3, 0x00000038, "left_line_style"),
                (6, 0x000001C0, "right_line_style"),
                (9, 0x0000FE00, "top_colour_index"),
                (16, 0x007F0000, "left_colour_index"),
                (23, 0x3F800000, "right_colour_index"),
            ),
        )
    elif bv >= 40:
        unpack_fmt = "<BBHBBHI"
        (xf.font_index, xf.format_key, pkd_type_par, pkd_align_orient, pkd_used, pkd_bkg_34, pkd_brd_34) = unpack(
            unpack_fmt, data[0:12]
        )
        upkbits(xf.protection, pkd_type_par, ((0, 0x01, "cell_locked"), (1, 0x02, "formula_hidden")))
        upkbits(
            xf,
            pkd_type_par,
            (
                (2, 0x0004, "is_style"),
                (3, 0x0008, "lotus_123_prefix"),  # Meaning is not known.
                (4, 0xFFF0, "parent_style_index"),
            ),
        )
        upkbits(
            xf.alignment, pkd_align_orient, ((0, 0x07, "hor_align"), (3, 0x08, "text_wrapped"), (4, 0x30, "vert_align"))
        )
        orientation = (pkd_align_orient & 0xC0) >> 6
        xf.alignment.rotation = [0, 255, 90, 180][orientation]
        reg = pkd_used >> 2
        for attr_stem in "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, reg & 1)
            reg >>= 1
        upkbits(
            xf.background,
            pkd_bkg_34,
            ((0, 0x003F, "fill_pattern"), (6, 0x07C0, "pattern_colour_index"), (11, 0xF800, "background_colour_index")),
        )
        upkbitsL(
            xf.border,
            pkd_brd_34,
            (
                (0, 0x00000007, "top_line_style"),
                (3, 0x000000F8, "top_colour_index"),
                (8, 0x00000700, "left_line_style"),
                (11, 0x0000F800, "left_colour_index"),
                (16, 0x00070000, "bottom_line_style"),
                (19, 0x00F80000, "bottom_colour_index"),
                (24, 0x07000000, "right_line_style"),
                (27, 0xF8000000, "right_colour_index"),
            ),
        )
    elif bv == 30:
        unpack_fmt = "<BBBBHHI"
        (xf.font_index, xf.format_key, pkd_type_prot, pkd_used, pkd_align_par, pkd_bkg_34, pkd_brd_34) = unpack(
            unpack_fmt, data[0:12]
        )
        upkbits(xf.protection, pkd_type_prot, ((0, 0x01, "cell_locked"), (1, 0x02, "formula_hidden")))
        upkbits(xf, pkd_type_prot, ((2, 0x0004, "is_style"), (3, 0x0008, "lotus_123_prefix")))  # Meaning is not known.
        upkbits(xf.alignment, pkd_align_par, ((0, 0x07, "hor_align"), (3, 0x08, "text_wrapped")))
        upkbits(xf, pkd_align_par, ((4, 0xFFF0, "parent_style_index"),))
        reg = pkd_used >> 2
        for attr_stem in "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, reg & 1)
            reg >>= 1
        upkbits(
            xf.background,
            pkd_bkg_34,
            ((0, 0x003F, "fill_pattern"), (6, 0x07C0, "pattern_colour_index"), (11, 0xF800, "background_colour_index")),
        )
        upkbitsL(
            xf.border,
            pkd_brd_34,
            (
                (0, 0x00000007, "top_line_style"),
                (3, 0x000000F8, "top_colour_index"),
                (8, 0x00000700, "left_line_style"),
                (11, 0x0000F800, "left_colour_index"),
                (16, 0x00070000, "bottom_line_style"),
                (19, 0x00F80000, "bottom_colour_index"),
                (24, 0x07000000, "right_line_style"),
                (27, 0xF8000000, "right_colour_index"),
            ),
        )
        xf.alignment.vert_align = 2  # bottom
        xf.alignment.rotation = 0
    elif bv == 21:
        #### Warning: incomplete treatment; formatting_info not fully supported.
        #### Probably need to offset incoming BIFF2 XF[n] to BIFF8-like XF[n+16],
        #### and create XF[0:16] like the standard ones in BIFF8
        #### *AND* add 16 to all XF references in cell records :-(
        (xf.font_index, format_etc, halign_etc) = unpack("<BxBB", data)
        xf.format_key = format_etc & 0x3F
        upkbits(xf.protection, format_etc, ((6, 0x40, "cell_locked"), (7, 0x80, "formula_hidden")))
        upkbits(xf.alignment, halign_etc, ((0, 0x07, "hor_align"),))
        for mask, side in ((0x08, "left"), (0x10, "right"), (0x20, "top"), (0x40, "bottom")):
            if halign_etc & mask:
                colour_index, line_style = 8, 1  # black, thin
            else:
                colour_index, line_style = 0, 0  # none, none
            setattr(xf.border, side + "_colour_index", colour_index)
            setattr(xf.border, side + "_line_style", line_style)
        bg = xf.background
        if halign_etc & 0x80:
            bg.fill_pattern = 17
        else:
            bg.fill_pattern = 0
        bg.background_colour_index = 9  # white
        bg.pattern_colour_index = 8  # black
        xf.parent_style_index = 0  # ???????????
        xf.alignment.vert_align = 2  # bottom
        xf.alignment.rotation = 0
        for attr_stem in "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, 1)
    else:
        raise XLRDError("programmer stuff-up: bv=%d" % bv)

    xf.xf_index = len(self.xf_list)
    self.xf_list.append(xf)
    self.xfcount += 1
    if blah:
        xf.dump(self.logfile, header="--- handle_xf: xf[%d] ---" % xf.xf_index, footer=" ")
    try:
        fmt = self.format_map[xf.format_key]
        cellty = _cellty_from_fmtty[fmt.type]
    except KeyError:
        cellty = XL_CELL_NUMBER
    self._xf_index_to_xl_type_map[xf.xf_index] = cellty

    # Now for some assertions ...
    if self.formatting_info:
        if self.verbosity and xf.is_style and xf.parent_style_index != 0x0FFF:
            msg = "WARNING *** XF[%d] is a style XF but parent_style_index is 0x%04x, not 0x0fff\n"
            fprintf(self.logfile, msg, xf.xf_index, xf.parent_style_index)
        check_colour_indexes_in_obj(self, xf, xf.xf_index)
    if xf.format_key not in self.format_map:
        msg = "WARNING *** XF[%d] unknown (raw) format key (%d, 0x%04x)\n"
        if self.verbosity:
            fprintf(self.logfile, msg, xf.xf_index, xf.format_key, xf.format_key)
        xf.format_key = 0
def xf_epilogue(self):
    # self is a Book instance.
    self._xf_epilogue_done = 1
    num_xfs = len(self.xf_list)
    blah = DEBUG or self.verbosity >= 3
    blah1 = DEBUG or self.verbosity >= 1
    if blah:
        fprintf(self.logfile, "xf_epilogue called ...\n")

    def check_same(book_arg, xf_arg, parent_arg, attr):
        # the _arg caper is to avoid a Warning msg from Python 2.1 :-(
        if getattr(xf_arg, attr) != getattr(parent_arg, attr):
            fprintf(
                book_arg.logfile,
                "NOTE !!! XF[%d] parent[%d] %s different\n",
                xf_arg.xf_index,
                parent_arg.xf_index,
                attr,
            )

    for xfx in xrange(num_xfs):
        xf = self.xf_list[xfx]
        if xf.format_key not in self.format_map:
            msg = "ERROR *** XF[%d] unknown format key (%d, 0x%04x)\n"
            fprintf(self.logfile, msg, xf.xf_index, xf.format_key, xf.format_key)
            xf.format_key = 0

        fmt = self.format_map[xf.format_key]
        cellty = _cellty_from_fmtty[fmt.type]
        self._xf_index_to_xl_type_map[xf.xf_index] = cellty
        # Now for some assertions etc
        if not self.formatting_info:
            continue
        if xf.is_style:
            continue
        if not (0 <= xf.parent_style_index < num_xfs):
            if blah1:
                fprintf(
                    self.logfile,
                    "WARNING *** XF[%d]: is_style=%d but parent_style_index=%d\n",
                    xf.xf_index,
                    xf.is_style,
                    xf.parent_style_index,
                )
            # make it conform
            xf.parent_style_index = 0
        if self.biff_version >= 30:
            if blah1:
                if xf.parent_style_index == xf.xf_index:
                    fprintf(
                        self.logfile,
                        "NOTE !!! XF[%d]: parent_style_index is also %d\n",
                        xf.xf_index,
                        xf.parent_style_index,
                    )
                elif not self.xf_list[xf.parent_style_index].is_style:
                    fprintf(
                        self.logfile,
                        "NOTE !!! XF[%d]: parent_style_index is %d; style flag not set\n",
                        xf.xf_index,
                        xf.parent_style_index,
                    )
            if blah1 and xf.parent_style_index > xf.xf_index:
                fprintf(
                    self.logfile,
                    "NOTE !!! XF[%d]: parent_style_index is %d; out of order?\n",
                    xf.xf_index,
                    xf.parent_style_index,
                )
            parent = self.xf_list[xf.parent_style_index]
            if not xf._alignment_flag and not parent._alignment_flag:
                if blah1:
                    check_same(self, xf, parent, "alignment")
            if not xf._background_flag and not parent._background_flag:
                if blah1:
                    check_same(self, xf, parent, "background")
            if not xf._border_flag and not parent._border_flag:
                if blah1:
                    check_same(self, xf, parent, "border")
            if not xf._protection_flag and not parent._protection_flag:
                if blah1:
                    check_same(self, xf, parent, "protection")
            if not xf._format_flag and not parent._format_flag:
                if blah1 and xf.format_key != parent.format_key:
                    fprintf(
                        self.logfile,
                        "NOTE !!! XF[%d] fmtk=%d, parent[%d] fmtk=%r\n%r / %r\n",
                        xf.xf_index,
                        xf.format_key,
                        parent.xf_index,
                        parent.format_key,
                        self.format_map[xf.format_key].format_str,
                        self.format_map[parent.format_key].format_str,
                    )
            if not xf._font_flag and not parent._font_flag:
                if blah1 and xf.font_index != parent.font_index:
                    fprintf(
                        self.logfile,
                        "NOTE !!! XF[%d] fontx=%d, parent[%d] fontx=%r\n",
                        xf.xf_index,
                        xf.font_index,
                        parent.xf_index,
                        parent.font_index,
                    )
 def check_same(book_arg, xf_arg, parent_arg, attr):
     # the _arg caper is to avoid a Warning msg from Python 2.1 :-(
     if getattr(xf_arg, attr) != getattr(parent_arg, attr):
         fprintf(book_arg.logfile,
             "NOTE !!! XF[%d] parent[%d] %s different\n",
             xf_arg.xf_index, parent_arg.xf_index, attr)
def xf_epilogue(self):
    # self is a Book instance.
    self._xf_epilogue_done = 1
    num_xfs = len(self.xf_list)
    blah = DEBUG or self.verbosity >= 3
    blah1 = DEBUG or self.verbosity >= 1
    if blah:
        fprintf(self.logfile, "xf_epilogue called ...\n")

    def check_same(book_arg, xf_arg, parent_arg, attr):
        # the _arg caper is to avoid a Warning msg from Python 2.1 :-(
        if getattr(xf_arg, attr) != getattr(parent_arg, attr):
            fprintf(book_arg.logfile,
                "NOTE !!! XF[%d] parent[%d] %s different\n",
                xf_arg.xf_index, parent_arg.xf_index, attr)

    for xfx in xrange(num_xfs):
        xf = self.xf_list[xfx]
        if xf.format_key not in self.format_map:
            msg = "ERROR *** XF[%d] unknown format key (%d, 0x%04x)\n"
            fprintf(self.logfile, msg,
                    xf.xf_index, xf.format_key, xf.format_key)
            xf.format_key = 0

        fmt = self.format_map[xf.format_key]
        cellty = _cellty_from_fmtty[fmt.type]
        self._xf_index_to_xl_type_map[xf.xf_index] = cellty
        # Now for some assertions etc
        if not self.formatting_info:
            continue
        if xf.is_style:
            continue
        if not(0 <= xf.parent_style_index < num_xfs):
            if blah1:
                fprintf(self.logfile,
                    "WARNING *** XF[%d]: is_style=%d but parent_style_index=%d\n",
                    xf.xf_index, xf.is_style, xf.parent_style_index)
            # make it conform
            xf.parent_style_index = 0
        if self.biff_version >= 30:
            if blah1:
                if xf.parent_style_index == xf.xf_index:
                    fprintf(self.logfile,
                        "NOTE !!! XF[%d]: parent_style_index is also %d\n",
                        xf.xf_index, xf.parent_style_index)
                elif not self.xf_list[xf.parent_style_index].is_style:
                    fprintf(self.logfile,
                        "NOTE !!! XF[%d]: parent_style_index is %d; style flag not set\n",
                        xf.xf_index, xf.parent_style_index)                
            if blah1 and xf.parent_style_index > xf.xf_index:
                fprintf(self.logfile,
                    "NOTE !!! XF[%d]: parent_style_index is %d; out of order?\n",
                    xf.xf_index, xf.parent_style_index)
            parent = self.xf_list[xf.parent_style_index]
            if not xf._alignment_flag and not parent._alignment_flag:
                if blah1: check_same(self, xf, parent, 'alignment')
            if not xf._background_flag and not parent._background_flag:
                if blah1: check_same(self, xf, parent, 'background')
            if not xf._border_flag and not parent._border_flag:
                if blah1: check_same(self, xf, parent, 'border')
            if not xf._protection_flag and not parent._protection_flag:
                if blah1: check_same(self, xf, parent, 'protection')
            if not xf._format_flag and not parent._format_flag:
                if blah1 and xf.format_key != parent.format_key:
                    fprintf(self.logfile,
                        "NOTE !!! XF[%d] fmtk=%d, parent[%d] fmtk=%r\n%r / %r\n",
                        xf.xf_index, xf.format_key, parent.xf_index, parent.format_key,
                        self.format_map[xf.format_key].format_str,
                        self.format_map[parent.format_key].format_str)
            if not xf._font_flag and not parent._font_flag:
                if blah1 and xf.font_index != parent.font_index:
                    fprintf(self.logfile,
                        "NOTE !!! XF[%d] fontx=%d, parent[%d] fontx=%r\n",
                        xf.xf_index, xf.font_index, parent.xf_index, parent.font_index)
def handle_xf(self, data):
    ### self is a Book instance
    # DEBUG = 0
    blah = DEBUG or self.verbosity >= 3
    bv = self.biff_version
    xf = XF()
    xf.alignment = XFAlignment()
    xf.alignment.indent_level = 0
    xf.alignment.shrink_to_fit = 0
    xf.alignment.text_direction = 0
    xf.border = XFBorder()
    xf.border.diag_up = 0
    xf.border.diag_down = 0
    xf.border.diag_colour_index = 0
    xf.border.diag_line_style = 0 # no line
    xf.background = XFBackground()
    xf.protection = XFProtection()
    # fill in the known standard formats
    if bv >= 50 and not self.xfcount:
        # i.e. do this once before we process the first XF record
        fill_in_standard_formats(self)
    if bv >= 80:
        unpack_fmt = '<HHHBBBBIiH'
        (xf.font_index, xf.format_key, pkd_type_par,
        pkd_align1, xf.alignment.rotation, pkd_align2,
        pkd_used, pkd_brdbkg1, pkd_brdbkg2, pkd_brdbkg3,
        ) = unpack(unpack_fmt, data[0:20])
        upkbits(xf.protection, pkd_type_par, (
            (0, 0x01, 'cell_locked'),
            (1, 0x02, 'formula_hidden'),
            ))
        upkbits(xf, pkd_type_par, (
            (2, 0x0004, 'is_style'),
            # Following is not in OOo docs, but is mentioned
            # in Gnumeric source and also in (deep breath)
            # org.apache.poi.hssf.record.ExtendedFormatRecord.java
            (3, 0x0008, 'lotus_123_prefix'), # Meaning is not known.
            (4, 0xFFF0, 'parent_style_index'),
            ))
        upkbits(xf.alignment, pkd_align1, (
            (0, 0x07, 'hor_align'),
            (3, 0x08, 'text_wrapped'),
            (4, 0x70, 'vert_align'),
            ))
        upkbits(xf.alignment, pkd_align2, (
            (0, 0x0f, 'indent_level'),
            (4, 0x10, 'shrink_to_fit'),
            (6, 0xC0, 'text_direction'),
            ))
        reg = pkd_used >> 2
        for attr_stem in \
            "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, reg & 1)
            reg >>= 1
        upkbitsL(xf.border, pkd_brdbkg1, (
            (0,  0x0000000f,  'left_line_style'),
            (4,  0x000000f0,  'right_line_style'),
            (8,  0x00000f00,  'top_line_style'),
            (12, 0x0000f000,  'bottom_line_style'),
            (16, 0x007f0000,  'left_colour_index'),
            (23, 0x3f800000,  'right_colour_index'),
            (30, 0x40000000,  'diag_down'),
            (31, 0x80000000, 'diag_up'),
            ))
        upkbits(xf.border, pkd_brdbkg2, (
            (0,  0x0000007F, 'top_colour_index'),
            (7,  0x00003F80, 'bottom_colour_index'),
            (14, 0x001FC000, 'diag_colour_index'),
            (21, 0x01E00000, 'diag_line_style'),
            ))
        upkbitsL(xf.background, pkd_brdbkg2, (
            (26, 0xFC000000, 'fill_pattern'),
            ))
        upkbits(xf.background, pkd_brdbkg3, (
            (0, 0x007F, 'pattern_colour_index'),
            (7, 0x3F80, 'background_colour_index'),
            ))
    elif bv >= 50:
        unpack_fmt = '<HHHBBIi'
        (xf.font_index, xf.format_key, pkd_type_par,
        pkd_align1, pkd_orient_used,
        pkd_brdbkg1, pkd_brdbkg2,
        ) = unpack(unpack_fmt, data[0:16])
        upkbits(xf.protection, pkd_type_par, (
            (0, 0x01, 'cell_locked'),
            (1, 0x02, 'formula_hidden'),
            ))
        upkbits(xf, pkd_type_par, (
            (2, 0x0004, 'is_style'),
            (3, 0x0008, 'lotus_123_prefix'), # Meaning is not known.
            (4, 0xFFF0, 'parent_style_index'),
            ))
        upkbits(xf.alignment, pkd_align1, (
            (0, 0x07, 'hor_align'),
            (3, 0x08, 'text_wrapped'),
            (4, 0x70, 'vert_align'),
            ))
        orientation = pkd_orient_used & 0x03
        xf.alignment.rotation = [0, 255, 90, 180][orientation]
        reg = pkd_orient_used >> 2
        for attr_stem in \
            "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, reg & 1)
            reg >>= 1
        upkbitsL(xf.background, pkd_brdbkg1, (
            ( 0, 0x0000007F, 'pattern_colour_index'),
            ( 7, 0x00003F80, 'background_colour_index'),
            (16, 0x003F0000, 'fill_pattern'),
            ))
        upkbitsL(xf.border, pkd_brdbkg1, (
            (22, 0x01C00000,  'bottom_line_style'),
            (25, 0xFE000000, 'bottom_colour_index'),
            ))
        upkbits(xf.border, pkd_brdbkg2, (
            ( 0, 0x00000007, 'top_line_style'),
            ( 3, 0x00000038, 'left_line_style'),
            ( 6, 0x000001C0, 'right_line_style'),
            ( 9, 0x0000FE00, 'top_colour_index'),
            (16, 0x007F0000, 'left_colour_index'),
            (23, 0x3F800000, 'right_colour_index'),
            ))
    elif bv >= 40:
        unpack_fmt = '<BBHBBHI'
        (xf.font_index, xf.format_key, pkd_type_par,
        pkd_align_orient, pkd_used,
        pkd_bkg_34, pkd_brd_34,
        ) = unpack(unpack_fmt, data[0:12])
        upkbits(xf.protection, pkd_type_par, (
            (0, 0x01, 'cell_locked'),
            (1, 0x02, 'formula_hidden'),
            ))
        upkbits(xf, pkd_type_par, (
            (2, 0x0004, 'is_style'),
            (3, 0x0008, 'lotus_123_prefix'), # Meaning is not known.
            (4, 0xFFF0, 'parent_style_index'),
            ))
        upkbits(xf.alignment, pkd_align_orient, (
            (0, 0x07, 'hor_align'),
            (3, 0x08, 'text_wrapped'),
            (4, 0x30, 'vert_align'),
            ))
        orientation = (pkd_align_orient & 0xC0) >> 6
        xf.alignment.rotation = [0, 255, 90, 180][orientation]
        reg = pkd_used >> 2
        for attr_stem in \
            "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, reg & 1)
            reg >>= 1
        upkbits(xf.background, pkd_bkg_34, (
            ( 0, 0x003F, 'fill_pattern'),
            ( 6, 0x07C0, 'pattern_colour_index'),
            (11, 0xF800, 'background_colour_index'),
            ))
        upkbitsL(xf.border, pkd_brd_34, (
            ( 0, 0x00000007,  'top_line_style'),
            ( 3, 0x000000F8,  'top_colour_index'),
            ( 8, 0x00000700,  'left_line_style'),
            (11, 0x0000F800,  'left_colour_index'),
            (16, 0x00070000,  'bottom_line_style'),
            (19, 0x00F80000,  'bottom_colour_index'),
            (24, 0x07000000,  'right_line_style'),
            (27, 0xF8000000, 'right_colour_index'),
            ))
    elif bv == 30:
        unpack_fmt = '<BBBBHHI'
        (xf.font_index, xf.format_key, pkd_type_prot,
        pkd_used, pkd_align_par,
        pkd_bkg_34, pkd_brd_34,
        ) = unpack(unpack_fmt, data[0:12])
        upkbits(xf.protection, pkd_type_prot, (
            (0, 0x01, 'cell_locked'),
            (1, 0x02, 'formula_hidden'),
            ))
        upkbits(xf, pkd_type_prot, (
            (2, 0x0004, 'is_style'),
            (3, 0x0008, 'lotus_123_prefix'), # Meaning is not known.
            ))
        upkbits(xf.alignment, pkd_align_par, (
            (0, 0x07, 'hor_align'),
            (3, 0x08, 'text_wrapped'),
            ))
        upkbits(xf, pkd_align_par, (
            (4, 0xFFF0, 'parent_style_index'),
            ))
        reg = pkd_used >> 2
        for attr_stem in \
            "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, reg & 1)
            reg >>= 1
        upkbits(xf.background, pkd_bkg_34, (
            ( 0, 0x003F, 'fill_pattern'),
            ( 6, 0x07C0, 'pattern_colour_index'),
            (11, 0xF800, 'background_colour_index'),
            ))
        upkbitsL(xf.border, pkd_brd_34, (
            ( 0, 0x00000007,  'top_line_style'),
            ( 3, 0x000000F8,  'top_colour_index'),
            ( 8, 0x00000700,  'left_line_style'),
            (11, 0x0000F800,  'left_colour_index'),
            (16, 0x00070000,  'bottom_line_style'),
            (19, 0x00F80000,  'bottom_colour_index'),
            (24, 0x07000000,  'right_line_style'),
            (27, 0xF8000000, 'right_colour_index'),
            ))
        xf.alignment.vert_align = 2 # bottom
        xf.alignment.rotation = 0
    elif bv == 21:
        #### Warning: incomplete treatment; formatting_info not fully supported.
        #### Probably need to offset incoming BIFF2 XF[n] to BIFF8-like XF[n+16],
        #### and create XF[0:16] like the standard ones in BIFF8
        #### *AND* add 16 to all XF references in cell records :-(
        (xf.font_index, format_etc, halign_etc) = unpack('<BxBB', data)
        xf.format_key = format_etc & 0x3F
        upkbits(xf.protection, format_etc, (
            (6, 0x40, 'cell_locked'),
            (7, 0x80, 'formula_hidden'),
            ))
        upkbits(xf.alignment, halign_etc, (
            (0, 0x07, 'hor_align'),
            ))
        for mask, side in ((0x08, 'left'), (0x10, 'right'), (0x20, 'top'), (0x40, 'bottom')):
            if halign_etc & mask:
                colour_index, line_style = 8, 1 # black, thin
            else:
                colour_index, line_style = 0, 0 # none, none
            setattr(xf.border, side + '_colour_index', colour_index)
            setattr(xf.border, side + '_line_style', line_style)
        bg = xf.background
        if halign_etc & 0x80:
            bg.fill_pattern = 17
        else:
            bg.fill_pattern = 0
        bg.background_colour_index = 9 # white
        bg.pattern_colour_index = 8 # black
        xf.parent_style_index = 0 # ???????????
        xf.alignment.vert_align = 2 # bottom
        xf.alignment.rotation = 0
        for attr_stem in \
            "format font alignment border background protection".split():
            attr = "_" + attr_stem + "_flag"
            setattr(xf, attr, 1)
    else:
        raise XLRDError('programmer stuff-up: bv=%d' % bv)

    xf.xf_index = len(self.xf_list)
    self.xf_list.append(xf)
    self.xfcount += 1
    if blah:
        xf.dump(
            self.logfile,
            header="--- handle_xf: xf[%d] ---" % xf.xf_index,
            footer=" ",
        )
    try:
        fmt = self.format_map[xf.format_key]
        cellty = _cellty_from_fmtty[fmt.type]
    except KeyError:
        cellty = XL_CELL_NUMBER
    self._xf_index_to_xl_type_map[xf.xf_index] = cellty

    # Now for some assertions ...
    if self.formatting_info:
        if self.verbosity and xf.is_style and xf.parent_style_index != 0x0FFF:
            msg = "WARNING *** XF[%d] is a style XF but parent_style_index is 0x%04x, not 0x0fff\n"
            fprintf(self.logfile, msg, xf.xf_index, xf.parent_style_index)
        check_colour_indexes_in_obj(self, xf, xf.xf_index)
    if xf.format_key not in self.format_map:
        msg = "WARNING *** XF[%d] unknown (raw) format key (%d, 0x%04x)\n"
        if self.verbosity:
            fprintf(self.logfile, msg,
                xf.xf_index, xf.format_key, xf.format_key)
        xf.format_key = 0