None, or an int error code (same as XL_CELL_ERROR in the Cell class).
+
+
+
+
oMSNG
+
5
+
Used by Excel as a placeholder for a missing (not supplied) function
+ argument. Should *not* appear as a final formula result. Value is None.
+
+
+
oNUM
+
2
+
A float. Note that there is no way of distinguishing dates.
+
+
+
oREF
+
-1
+
The value is either None or a non-empty list of
+ absolute Ref3D instances.
+
+
+
+
oREL
+
-2
+
The value is None or a non-empty list of
+ fully or partially relative Ref3D instances.
+
+
+
+
oSTRG
+
1
+
A Unicode string.
+
+
+
oUNK
+
0
+
The kind is unknown or ambiguous. The value is None
+
+
+ """
+
+ #: None means that the actual value of the operand is a variable
+ #: (depends on cell data), not a constant.
+ value = None
+
+ #: oUNK means that the kind of operand is not known unambiguously.
+ kind = oUNK
+
+ #: The reconstituted text of the original formula. Function names will be
+ #: in English irrespective of the original language, which doesn't seem
+ #: to be recorded anywhere. The separator is ",", not ";" or whatever else
+ #: might be more appropriate for the end-user's locale; patches welcome.
+ text = '?'
+
+ def __init__(self, akind=None, avalue=None, arank=0, atext='?'):
+ if akind is not None:
+ self.kind = akind
+ if avalue is not None:
+ self.value = avalue
+ self.rank = arank
+ # rank is an internal gizmo (operator precedence);
+ # it's used in reconstructing formula text.
+ self.text = atext
+
+ def __repr__(self):
+ kind_text = okind_dict.get(self.kind, "?Unknown kind?")
+ return "Operand(kind=%s, value=%r, text=%r)" \
+ % (kind_text, self.value, self.text)
+
+
+class Ref3D(tuple):
+ """
+ Represents an absolute or relative 3-dimensional reference to a box
+ of one or more cells.
+
+ The ``coords`` attribute is a tuple of the form::
+
+ (shtxlo, shtxhi, rowxlo, rowxhi, colxlo, colxhi)
+
+ where ``0 <= thingxlo <= thingx < thingxhi``.
+
+ .. note::
+ It is quite possible to have ``thingx > nthings``; for example
+ ``Print_Titles`` could have ``colxhi == 256`` and/or ``rowxhi == 65536``
+ irrespective of how many columns/rows are actually used in the worksheet.
+ The caller will need to decide how to handle this situation.
+ Keyword: :class:`IndexError` :-)
+
+ The components of the coords attribute are also available as individual
+ attributes: ``shtxlo``, ``shtxhi``, ``rowxlo``, ``rowxhi``, ``colxlo``, and
+ ``colxhi``.
+
+ The ``relflags`` attribute is a 6-tuple of flags which indicate whether
+ the corresponding (sheet|row|col)(lo|hi) is relative (1) or absolute (0).
+
+ .. note::
+ There is necessarily no information available as to what cell(s)
+ the reference could possibly be relative to. The caller must decide what
+ if any use to make of ``oREL`` operands.
+
+ .. note:
+ A partially relative reference may well be a typo.
+ For example, define name ``A1Z10`` as ``$a$1:$z10`` (missing ``$`` after
+ ``z``) while the cursor is on cell ``Sheet3!A27``.
+
+ The resulting :class:`Ref3D` instance will have
+ ``coords = (2, 3, 0, -16, 0, 26)``
+ and ``relflags = (0, 0, 0, 1, 0, 0).
+
+ So far, only one possibility of a sheet-relative component in
+ a reference has been noticed: a 2D reference located in the
+ "current sheet".
+
+ This will appear as ``coords = (0, 1, ...)`` and
+ ``relflags = (1, 1, ...)``.
+
+ .. versionadded:: 0.6.0
+ """
+
+ def __init__(self, atuple):
+ self.coords = atuple[0:6]
+ self.relflags = atuple[6:12]
+ if not self.relflags:
+ self.relflags = (0, 0, 0, 0, 0, 0)
+ (self.shtxlo, self.shtxhi,
+ self.rowxlo, self.rowxhi,
+ self.colxlo, self.colxhi) = self.coords
+
+ def __repr__(self):
+ if not self.relflags or self.relflags == (0, 0, 0, 0, 0, 0):
+ return "Ref3D(coords=%r)" % (self.coords, )
+ else:
+ return "Ref3D(coords=%r, relflags=%r)" \
+ % (self.coords, self.relflags)
+
+tAdd = 0x03
+tSub = 0x04
+tMul = 0x05
+tDiv = 0x06
+tPower = 0x07
+tConcat = 0x08
+tLT, tLE, tEQ, tGE, tGT, tNE = range(0x09, 0x0F)
+
+
+def nop(x):
+ return x
+
+def _opr_pow(x, y): return x ** y
+
+def _opr_lt(x, y): return x < y
+def _opr_le(x, y): return x <= y
+def _opr_eq(x, y): return x == y
+def _opr_ge(x, y): return x >= y
+def _opr_gt(x, y): return x > y
+def _opr_ne(x, y): return x != y
+
+def num2strg(num):
+ """
+ Attempt to emulate Excel's default conversion from number to string.
+ """
+ s = str(num)
+ if s.endswith(".0"):
+ s = s[:-2]
+ return s
+
+_arith_argdict = {oNUM: nop, oSTRG: float}
+_cmp_argdict = {oNUM: nop, oSTRG: nop}
+# Seems no conversions done on relops; in Excel, "1" > 9 produces TRUE.
+_strg_argdict = {oNUM:num2strg, oSTRG:nop}
+binop_rules = {
+ tAdd: (_arith_argdict, oNUM, opr.add, 30, '+'),
+ tSub: (_arith_argdict, oNUM, opr.sub, 30, '-'),
+ tMul: (_arith_argdict, oNUM, opr.mul, 40, '*'),
+ tDiv: (_arith_argdict, oNUM, opr.truediv, 40, '/'),
+ tPower: (_arith_argdict, oNUM, _opr_pow, 50, '^',),
+ tConcat:(_strg_argdict, oSTRG, opr.add, 20, '&'),
+ tLT: (_cmp_argdict, oBOOL, _opr_lt, 10, '<'),
+ tLE: (_cmp_argdict, oBOOL, _opr_le, 10, '<='),
+ tEQ: (_cmp_argdict, oBOOL, _opr_eq, 10, '='),
+ tGE: (_cmp_argdict, oBOOL, _opr_ge, 10, '>='),
+ tGT: (_cmp_argdict, oBOOL, _opr_gt, 10, '>'),
+ tNE: (_cmp_argdict, oBOOL, _opr_ne, 10, '<>'),
+}
+
+unop_rules = {
+ 0x13: (lambda x: -x, 70, '-', ''), # unary minus
+ 0x12: (lambda x: x, 70, '+', ''), # unary plus
+ 0x14: (lambda x: x / 100.0, 60, '', '%'),# percent
+}
+
+LEAF_RANK = 90
+FUNC_RANK = 90
+
+STACK_ALARM_LEVEL = 5
+STACK_PANIC_LEVEL = 10
+
+def evaluate_name_formula(bk, nobj, namex, blah=0, level=0):
+ if level > STACK_ALARM_LEVEL:
+ blah = 1
+ data = nobj.raw_formula
+ fmlalen = nobj.basic_formula_len
+ bv = bk.biff_version
+ reldelta = 1 # All defined name formulas use "Method B" [OOo docs]
+ if blah:
+ print("::: evaluate_name_formula %r %r %d %d %r level=%d"
+ % (namex, nobj.name, fmlalen, bv, data, level), file=bk.logfile)
+ hex_char_dump(data, 0, fmlalen, fout=bk.logfile)
+ if level > STACK_PANIC_LEVEL:
+ raise XLRDError("Excessive indirect references in NAME formula")
+ sztab = szdict[bv]
+ pos = 0
+ stack = []
+ any_rel = 0
+ any_err = 0
+ any_external = 0
+ unk_opnd = Operand(oUNK, None)
+ error_opnd = Operand(oERR, None)
+ spush = stack.append
+
+ def do_binop(opcd, stk):
+ assert len(stk) >= 2
+ bop = stk.pop()
+ aop = stk.pop()
+ argdict, result_kind, func, rank, sym = binop_rules[opcd]
+ otext = ''.join([
+ '('[:aop.rank < rank],
+ aop.text,
+ ')'[:aop.rank < rank],
+ sym,
+ '('[:bop.rank < rank],
+ bop.text,
+ ')'[:bop.rank < rank],
+ ])
+ resop = Operand(result_kind, None, rank, otext)
+ try:
+ bconv = argdict[bop.kind]
+ aconv = argdict[aop.kind]
+ except KeyError:
+ stk.append(resop)
+ return
+ if bop.value is None or aop.value is None:
+ stk.append(resop)
+ return
+ bval = bconv(bop.value)
+ aval = aconv(aop.value)
+ result = func(aval, bval)
+ if result_kind == oBOOL:
+ result = 1 if result else 0
+ resop.value = result
+ stk.append(resop)
+
+ def do_unaryop(opcode, result_kind, stk):
+ assert len(stk) >= 1
+ aop = stk.pop()
+ val = aop.value
+ func, rank, sym1, sym2 = unop_rules[opcode]
+ otext = ''.join([
+ sym1,
+ '('[:aop.rank < rank],
+ aop.text,
+ ')'[:aop.rank < rank],
+ sym2,
+ ])
+ if val is not None:
+ val = func(val)
+ stk.append(Operand(result_kind, val, rank, otext))
+
+ def not_in_name_formula(op_arg, oname_arg):
+ msg = "ERROR *** Token 0x%02x (%s) found in NAME formula" \
+ % (op_arg, oname_arg)
+ raise FormulaError(msg)
+
+ if fmlalen == 0:
+ stack = [unk_opnd]
+
+ while 0 <= pos < fmlalen:
+ op = BYTES_ORD(data[pos])
+ opcode = op & 0x1f
+ optype = (op & 0x60) >> 5
+ if optype:
+ opx = opcode + 32
+ else:
+ opx = opcode
+ oname = onames[opx] # + [" RVA"][optype]
+ sz = sztab[opx]
+ if blah:
+ print("Pos:%d Op:0x%02x Name:t%s Sz:%d opcode:%02xh optype:%02xh"
+ % (pos, op, oname, sz, opcode, optype), file=bk.logfile)
+ print("Stack =", stack, file=bk.logfile)
+ if sz == -2:
+ msg = 'ERROR *** Unexpected token 0x%02x ("%s"); biff_version=%d' \
+ % (op, oname, bv)
+ raise FormulaError(msg)
+ if not optype:
+ if 0x00 <= opcode <= 0x02: # unk_opnd, tExp, tTbl
+ not_in_name_formula(op, oname)
+ elif 0x03 <= opcode <= 0x0E:
+ # Add, Sub, Mul, Div, Power
+ # tConcat
+ # tLT, ..., tNE
+ do_binop(opcode, stack)
+ elif opcode == 0x0F: # tIsect
+ if blah: print("tIsect pre", stack, file=bk.logfile)
+ assert len(stack) >= 2
+ bop = stack.pop()
+ aop = stack.pop()
+ sym = ' '
+ rank = 80 ########## check #######
+ otext = ''.join([
+ '('[:aop.rank < rank],
+ aop.text,
+ ')'[:aop.rank < rank],
+ sym,
+ '('[:bop.rank < rank],
+ bop.text,
+ ')'[:bop.rank < rank],
+ ])
+ res = Operand(oREF)
+ res.text = otext
+ if bop.kind == oERR or aop.kind == oERR:
+ res.kind = oERR
+ elif bop.kind == oUNK or aop.kind == oUNK:
+ # This can happen with undefined
+ # (go search in the current sheet) labels.
+ # For example =Bob Sales
+ # Each label gets a NAME record with an empty formula (!)
+ # Evaluation of the tName token classifies it as oUNK
+ # res.kind = oREF
+ pass
+ elif bop.kind == oREF == aop.kind:
+ if aop.value is not None and bop.value is not None:
+ assert len(aop.value) == 1
+ assert len(bop.value) == 1
+ coords = do_box_funcs(
+ tIsectFuncs, aop.value[0], bop.value[0])
+ res.value = [Ref3D(coords)]
+ elif bop.kind == oREL == aop.kind:
+ res.kind = oREL
+ if aop.value is not None and bop.value is not None:
+ assert len(aop.value) == 1
+ assert len(bop.value) == 1
+ coords = do_box_funcs(
+ tIsectFuncs, aop.value[0], bop.value[0])
+ relfa = aop.value[0].relflags
+ relfb = bop.value[0].relflags
+ if relfa == relfb:
+ res.value = [Ref3D(coords + relfa)]
+ else:
+ pass
+ spush(res)
+ if blah: print("tIsect post", stack, file=bk.logfile)
+ elif opcode == 0x10: # tList
+ if blah: print("tList pre", stack, file=bk.logfile)
+ assert len(stack) >= 2
+ bop = stack.pop()
+ aop = stack.pop()
+ sym = ','
+ rank = 80 ########## check #######
+ otext = ''.join([
+ '('[:aop.rank < rank],
+ aop.text,
+ ')'[:aop.rank < rank],
+ sym,
+ '('[:bop.rank < rank],
+ bop.text,
+ ')'[:bop.rank < rank],
+ ])
+ res = Operand(oREF, None, rank, otext)
+ if bop.kind == oERR or aop.kind == oERR:
+ res.kind = oERR
+ elif bop.kind in (oREF, oREL) and aop.kind in (oREF, oREL):
+ res.kind = oREF
+ if aop.kind == oREL or bop.kind == oREL:
+ res.kind = oREL
+ if aop.value is not None and bop.value is not None:
+ assert len(aop.value) >= 1
+ assert len(bop.value) == 1
+ res.value = aop.value + bop.value
+ else:
+ pass
+ spush(res)
+ if blah: print("tList post", stack, file=bk.logfile)
+ elif opcode == 0x11: # tRange
+ if blah: print("tRange pre", stack, file=bk.logfile)
+ assert len(stack) >= 2
+ bop = stack.pop()
+ aop = stack.pop()
+ sym = ':'
+ rank = 80 ########## check #######
+ otext = ''.join([
+ '('[:aop.rank < rank],
+ aop.text,
+ ')'[:aop.rank < rank],
+ sym,
+ '('[:bop.rank < rank],
+ bop.text,
+ ')'[:bop.rank < rank],
+ ])
+ res = Operand(oREF, None, rank, otext)
+ if bop.kind == oERR or aop.kind == oERR:
+ res = oERR
+ elif bop.kind == oREF == aop.kind:
+ if aop.value is not None and bop.value is not None:
+ assert len(aop.value) == 1
+ assert len(bop.value) == 1
+ coords = do_box_funcs(
+ tRangeFuncs, aop.value[0], bop.value[0])
+ res.value = [Ref3D(coords)]
+ elif bop.kind == oREL == aop.kind:
+ res.kind = oREL
+ if aop.value is not None and bop.value is not None:
+ assert len(aop.value) == 1
+ assert len(bop.value) == 1
+ coords = do_box_funcs(
+ tRangeFuncs, aop.value[0], bop.value[0])
+ relfa = aop.value[0].relflags
+ relfb = bop.value[0].relflags
+ if relfa == relfb:
+ res.value = [Ref3D(coords + relfa)]
+ else:
+ pass
+ spush(res)
+ if blah: print("tRange post", stack, file=bk.logfile)
+ elif 0x12 <= opcode <= 0x14: # tUplus, tUminus, tPercent
+ do_unaryop(opcode, oNUM, stack)
+ elif opcode == 0x15: # tParen
+ # source cosmetics
+ pass
+ elif opcode == 0x16: # tMissArg
+ spush(Operand(oMSNG, None, LEAF_RANK, ''))
+ elif opcode == 0x17: # tStr
+ if bv <= 70:
+ strg, newpos = unpack_string_update_pos(
+ data, pos+1, bk.encoding, lenlen=1)
+ else:
+ strg, newpos = unpack_unicode_update_pos(
+ data, pos+1, lenlen=1)
+ sz = newpos - pos
+ if blah: print(" sz=%d strg=%r" % (sz, strg), file=bk.logfile)
+ text = '"' + strg.replace('"', '""') + '"'
+ spush(Operand(oSTRG, strg, LEAF_RANK, text))
+ elif opcode == 0x18: # tExtended
+ # new with BIFF 8
+ assert bv >= 80
+ # not in OOo docs
+ raise FormulaError("tExtended token not implemented")
+ elif opcode == 0x19: # tAttr
+ subop, nc = unpack("= 1
+ aop = stack[-1]
+ otext = 'SUM(%s)' % aop.text
+ stack[-1] = Operand(oNUM, None, FUNC_RANK, otext)
+ else:
+ sz = 4
+ if blah:
+ print(" subop=%02xh subname=t%s sz=%d nc=%02xh"
+ % (subop, subname, sz, nc), file=bk.logfile)
+ elif 0x1A <= opcode <= 0x1B: # tSheet, tEndSheet
+ assert bv < 50
+ raise FormulaError("tSheet & tEndsheet tokens not implemented")
+ elif 0x1C <= opcode <= 0x1F: # tErr, tBool, tInt, tNum
+ inx = opcode - 0x1C
+ nb = [1, 1, 2, 8][inx]
+ kind = [oERR, oBOOL, oNUM, oNUM][inx]
+ value, = unpack("<" + "BBHd"[inx], data[pos+1:pos+1+nb])
+ if inx == 2: # tInt
+ value = float(value)
+ text = str(value)
+ elif inx == 3: # tNum
+ text = str(value)
+ elif inx == 1: # tBool
+ text = ('FALSE', 'TRUE')[value]
+ else:
+ text = '"' +error_text_from_code[value] + '"'
+ spush(Operand(kind, value, LEAF_RANK, text))
+ else:
+ raise FormulaError("Unhandled opcode: 0x%02x" % opcode)
+ if sz <= 0:
+ raise FormulaError("Size not set for opcode 0x%02x" % opcode)
+ pos += sz
+ continue
+ if opcode == 0x00: # tArray
+ spush(unk_opnd)
+ elif opcode == 0x01: # tFunc
+ nb = 1 + int(bv >= 40)
+ funcx = unpack("<" + " BH"[nb], data[pos+1:pos+1+nb])[0]
+ func_attrs = func_defs.get(funcx, None)
+ if not func_attrs:
+ print("*** formula/tFunc unknown FuncID:%d"
+ % funcx, file=bk.logfile)
+ spush(unk_opnd)
+ else:
+ func_name, nargs = func_attrs[:2]
+ if blah:
+ print(" FuncID=%d name=%s nargs=%d"
+ % (funcx, func_name, nargs), file=bk.logfile)
+ assert len(stack) >= nargs
+ if nargs:
+ argtext = listsep.join(arg.text for arg in stack[-nargs:])
+ otext = "%s(%s)" % (func_name, argtext)
+ del stack[-nargs:]
+ else:
+ otext = func_name + "()"
+ res = Operand(oUNK, None, FUNC_RANK, otext)
+ spush(res)
+ elif opcode == 0x02: #tFuncVar
+ nb = 1 + int(bv >= 40)
+ nargs, funcx = unpack("= nargs
+ assert len(stack) >= nargs
+ argtext = listsep.join(arg.text for arg in stack[-nargs:])
+ otext = "%s(%s)" % (func_name, argtext)
+ res = Operand(oUNK, None, FUNC_RANK, otext)
+ if funcx == 1: # IF
+ testarg = stack[-nargs]
+ if testarg.kind not in (oNUM, oBOOL):
+ if blah and testarg.kind != oUNK:
+ print("IF testarg kind?", file=bk.logfile)
+ elif testarg.value not in (0, 1):
+ if blah and testarg.value is not None:
+ print("IF testarg value?", file=bk.logfile)
+ else:
+ if nargs == 2 and not testarg.value:
+ # IF(FALSE, tv) => FALSE
+ res.kind, res.value = oBOOL, 0
+ else:
+ respos = -nargs + 2 - int(testarg.value)
+ chosen = stack[respos]
+ if chosen.kind == oMSNG:
+ res.kind, res.value = oNUM, 0
+ else:
+ res.kind, res.value = chosen.kind, chosen.value
+ if blah:
+ print("$$$$$$ IF => constant", file=bk.logfile)
+ elif funcx == 100: # CHOOSE
+ testarg = stack[-nargs]
+ if testarg.kind == oNUM:
+ if 1 <= testarg.value < nargs:
+ chosen = stack[-nargs + int(testarg.value)]
+ if chosen.kind == oMSNG:
+ res.kind, res.value = oNUM, 0
+ else:
+ res.kind, res.value = chosen.kind, chosen.value
+ del stack[-nargs:]
+ spush(res)
+ elif opcode == 0x03: #tName
+ tgtnamex = unpack("> bk.logfile, " ", res
+ # spush(res)
+ elif opcode == 0x0D: #tAreaN
+ not_in_name_formula(op, oname)
+ # res = get_cell_range_addr(data, pos+1, bv, reldelta=1)
+ # # note *ALL* tAreaN usage has signed offset for relative addresses
+ # any_rel = 1
+ # if blah: print >> bk.logfile, " ", res
+ elif opcode == 0x1A: # tRef3d
+ if bv >= 80:
+ res = get_cell_addr(data, pos+3, bv, reldelta)
+ refx = unpack("= 80:
+ res1, res2 = get_cell_range_addr(data, pos+3, bv, reldelta)
+ refx = unpack("= 80:
+ refx, tgtnamex = unpack(" 0:
+ refx -= 1
+ elif refx < 0:
+ refx = -refx - 1
+ else:
+ dodgy = 1
+ if blah:
+ print(" origrefx=%d refx=%d tgtnamex=%d dodgy=%d"
+ % (origrefx, refx, tgtnamex, dodgy), file=bk.logfile)
+ if tgtnamex == namex:
+ if blah: print("!!!! Self-referential !!!!", file=bk.logfile)
+ dodgy = any_err = 1
+ if not dodgy:
+ if bv >= 80:
+ shx1, shx2 = get_externsheet_local_range(bk, refx, blah)
+ elif origrefx > 0:
+ shx1, shx2 = (-4, -4) # external ref
+ else:
+ exty = bk._externsheet_type_b57[refx]
+ if exty == 4: # non-specific sheet in own doc't
+ shx1, shx2 = (-1, -1) # internal, any sheet
+ else:
+ shx1, shx2 = (-666, -666)
+ if dodgy or shx1 < -1:
+ otext = "<>" \
+ % (tgtnamex, origrefx)
+ res = Operand(oUNK, None, LEAF_RANK, otext)
+ else:
+ tgtobj = bk.name_obj_list[tgtnamex]
+ if not tgtobj.evaluated:
+ ### recursive ###
+ evaluate_name_formula(bk, tgtobj, tgtnamex, blah, level+1)
+ if tgtobj.macro or tgtobj.binary or tgtobj.any_err:
+ if blah:
+ tgtobj.dump(
+ bk.logfile,
+ header="!!! bad tgtobj !!!",
+ footer="------------------",
+ )
+ res = Operand(oUNK, None)
+ any_err = any_err or tgtobj.macro or tgtobj.binary or tgtobj.any_err
+ any_rel = any_rel or tgtobj.any_rel
+ else:
+ assert len(tgtobj.stack) == 1
+ res = copy.deepcopy(tgtobj.stack[0])
+ res.rank = LEAF_RANK
+ if tgtobj.scope == -1:
+ res.text = tgtobj.name
+ else:
+ res.text = "%s!%s" \
+ % (bk._sheet_names[tgtobj.scope], tgtobj.name)
+ if blah:
+ print(" tNameX: setting text to", repr(res.text), file=bk.logfile)
+ spush(res)
+ elif opcode in error_opcodes:
+ any_err = 1
+ spush(error_opnd)
+ else:
+ if blah:
+ print("FORMULA: /// Not handled yet: t" + oname, file=bk.logfile)
+ any_err = 1
+ if sz <= 0:
+ raise FormulaError("Fatal: token size is not positive")
+ pos += sz
+ any_rel = not not any_rel
+ if blah:
+ fprintf(bk.logfile, "End of formula. level=%d any_rel=%d any_err=%d stack=%r\n",
+ level, not not any_rel, any_err, stack)
+ if len(stack) >= 2:
+ print("*** Stack has unprocessed args", file=bk.logfile)
+ print(file=bk.logfile)
+ nobj.stack = stack
+ if len(stack) != 1:
+ nobj.result = None
+ else:
+ nobj.result = stack[0]
+ nobj.any_rel = any_rel
+ nobj.any_err = any_err
+ nobj.any_external = any_external
+ nobj.evaluated = 1
+
+#### under construction #############################################################################
+def decompile_formula(bk, fmla, fmlalen,
+ fmlatype=None, browx=None, bcolx=None,
+ blah=0, level=0, r1c1=0):
+ if level > STACK_ALARM_LEVEL:
+ blah = 1
+ reldelta = fmlatype in (FMLA_TYPE_SHARED, FMLA_TYPE_NAME, FMLA_TYPE_COND_FMT, FMLA_TYPE_DATA_VAL)
+ data = fmla
+ bv = bk.biff_version
+ if blah:
+ print("::: decompile_formula len=%d fmlatype=%r browx=%r bcolx=%r reldelta=%d %r level=%d"
+ % (fmlalen, fmlatype, browx, bcolx, reldelta, data, level), file=bk.logfile)
+ hex_char_dump(data, 0, fmlalen, fout=bk.logfile)
+ if level > STACK_PANIC_LEVEL:
+ raise XLRDError("Excessive indirect references in formula")
+ sztab = szdict[bv]
+ pos = 0
+ stack = []
+ any_rel = 0
+ any_err = 0
+ unk_opnd = Operand(oUNK, None)
+ error_opnd = Operand(oERR, None)
+ spush = stack.append
+
+ def do_binop(opcd, stk):
+ assert len(stk) >= 2
+ bop = stk.pop()
+ aop = stk.pop()
+ argdict, result_kind, func, rank, sym = binop_rules[opcd]
+ otext = ''.join([
+ '('[:aop.rank < rank],
+ aop.text,
+ ')'[:aop.rank < rank],
+ sym,
+ '('[:bop.rank < rank],
+ bop.text,
+ ')'[:bop.rank < rank],
+ ])
+ resop = Operand(result_kind, None, rank, otext)
+ stk.append(resop)
+
+ def do_unaryop(opcode, result_kind, stk):
+ assert len(stk) >= 1
+ aop = stk.pop()
+ func, rank, sym1, sym2 = unop_rules[opcode]
+ otext = ''.join([
+ sym1,
+ '('[:aop.rank < rank],
+ aop.text,
+ ')'[:aop.rank < rank],
+ sym2,
+ ])
+ stk.append(Operand(result_kind, None, rank, otext))
+
+ def unexpected_opcode(op_arg, oname_arg):
+ msg = "ERROR *** Unexpected token 0x%02x (%s) found in formula type %s" \
+ % (op_arg, oname_arg, FMLA_TYPEDESCR_MAP[fmlatype])
+ print(msg, file=bk.logfile)
+ # raise FormulaError(msg)
+
+ if fmlalen == 0:
+ stack = [unk_opnd]
+
+ while 0 <= pos < fmlalen:
+ op = BYTES_ORD(data[pos])
+ opcode = op & 0x1f
+ optype = (op & 0x60) >> 5
+ if optype:
+ opx = opcode + 32
+ else:
+ opx = opcode
+ oname = onames[opx] # + [" RVA"][optype]
+ sz = sztab[opx]
+ if blah:
+ print("Pos:%d Op:0x%02x opname:t%s Sz:%d opcode:%02xh optype:%02xh"
+ % (pos, op, oname, sz, opcode, optype), file=bk.logfile)
+ print("Stack =", stack, file=bk.logfile)
+ if sz == -2:
+ msg = 'ERROR *** Unexpected token 0x%02x ("%s"); biff_version=%d' \
+ % (op, oname, bv)
+ raise FormulaError(msg)
+ if _TOKEN_NOT_ALLOWED(opx, 0) & fmlatype:
+ unexpected_opcode(op, oname)
+ if not optype:
+ if opcode <= 0x01: # tExp
+ if bv >= 30:
+ fmt = '= 2
+ bop = stack.pop()
+ aop = stack.pop()
+ sym = ' '
+ rank = 80 ########## check #######
+ otext = ''.join([
+ '('[:aop.rank < rank],
+ aop.text,
+ ')'[:aop.rank < rank],
+ sym,
+ '('[:bop.rank < rank],
+ bop.text,
+ ')'[:bop.rank < rank],
+ ])
+ res = Operand(oREF)
+ res.text = otext
+ if bop.kind == oERR or aop.kind == oERR:
+ res.kind = oERR
+ elif bop.kind == oUNK or aop.kind == oUNK:
+ # This can happen with undefined
+ # (go search in the current sheet) labels.
+ # For example =Bob Sales
+ # Each label gets a NAME record with an empty formula (!)
+ # Evaluation of the tName token classifies it as oUNK
+ # res.kind = oREF
+ pass
+ elif bop.kind == oREF == aop.kind:
+ pass
+ elif bop.kind == oREL == aop.kind:
+ res.kind = oREL
+ else:
+ pass
+ spush(res)
+ if blah: print("tIsect post", stack, file=bk.logfile)
+ elif opcode == 0x10: # tList
+ if blah: print("tList pre", stack, file=bk.logfile)
+ assert len(stack) >= 2
+ bop = stack.pop()
+ aop = stack.pop()
+ sym = ','
+ rank = 80 ########## check #######
+ otext = ''.join([
+ '('[:aop.rank < rank],
+ aop.text,
+ ')'[:aop.rank < rank],
+ sym,
+ '('[:bop.rank < rank],
+ bop.text,
+ ')'[:bop.rank < rank],
+ ])
+ res = Operand(oREF, None, rank, otext)
+ if bop.kind == oERR or aop.kind == oERR:
+ res.kind = oERR
+ elif bop.kind in (oREF, oREL) and aop.kind in (oREF, oREL):
+ res.kind = oREF
+ if aop.kind == oREL or bop.kind == oREL:
+ res.kind = oREL
+ else:
+ pass
+ spush(res)
+ if blah: print("tList post", stack, file=bk.logfile)
+ elif opcode == 0x11: # tRange
+ if blah: print("tRange pre", stack, file=bk.logfile)
+ assert len(stack) >= 2
+ bop = stack.pop()
+ aop = stack.pop()
+ sym = ':'
+ rank = 80 ########## check #######
+ otext = ''.join([
+ '('[:aop.rank < rank],
+ aop.text,
+ ')'[:aop.rank < rank],
+ sym,
+ '('[:bop.rank < rank],
+ bop.text,
+ ')'[:bop.rank < rank],
+ ])
+ res = Operand(oREF, None, rank, otext)
+ if bop.kind == oERR or aop.kind == oERR:
+ res = oERR
+ elif bop.kind == oREF == aop.kind:
+ pass
+ else:
+ pass
+ spush(res)
+ if blah: print("tRange post", stack, file=bk.logfile)
+ elif 0x12 <= opcode <= 0x14: # tUplus, tUminus, tPercent
+ do_unaryop(opcode, oNUM, stack)
+ elif opcode == 0x15: # tParen
+ # source cosmetics
+ pass
+ elif opcode == 0x16: # tMissArg
+ spush(Operand(oMSNG, None, LEAF_RANK, ''))
+ elif opcode == 0x17: # tStr
+ if bv <= 70:
+ strg, newpos = unpack_string_update_pos(
+ data, pos+1, bk.encoding, lenlen=1)
+ else:
+ strg, newpos = unpack_unicode_update_pos(
+ data, pos+1, lenlen=1)
+ sz = newpos - pos
+ if blah: print(" sz=%d strg=%r" % (sz, strg), file=bk.logfile)
+ text = '"' + strg.replace('"', '""') + '"'
+ spush(Operand(oSTRG, None, LEAF_RANK, text))
+ elif opcode == 0x18: # tExtended
+ # new with BIFF 8
+ assert bv >= 80
+ # not in OOo docs, don't even know how to determine its length
+ raise FormulaError("tExtended token not implemented")
+ elif opcode == 0x19: # tAttr
+ subop, nc = unpack("= 1
+ aop = stack[-1]
+ otext = 'SUM(%s)' % aop.text
+ stack[-1] = Operand(oNUM, None, FUNC_RANK, otext)
+ else:
+ sz = 4
+ if blah:
+ print(" subop=%02xh subname=t%s sz=%d nc=%02xh"
+ % (subop, subname, sz, nc), file=bk.logfile)
+ elif 0x1A <= opcode <= 0x1B: # tSheet, tEndSheet
+ assert bv < 50
+ raise FormulaError("tSheet & tEndsheet tokens not implemented")
+ elif 0x1C <= opcode <= 0x1F: # tErr, tBool, tInt, tNum
+ inx = opcode - 0x1C
+ nb = [1, 1, 2, 8][inx]
+ kind = [oERR, oBOOL, oNUM, oNUM][inx]
+ value, = unpack("<" + "BBHd"[inx], data[pos+1:pos+1+nb])
+ if inx == 2: # tInt
+ value = float(value)
+ text = str(value)
+ elif inx == 3: # tNum
+ text = str(value)
+ elif inx == 1: # tBool
+ text = ('FALSE', 'TRUE')[value]
+ else:
+ text = '"' +error_text_from_code[value] + '"'
+ spush(Operand(kind, None, LEAF_RANK, text))
+ else:
+ raise FormulaError("Unhandled opcode: 0x%02x" % opcode)
+ if sz <= 0:
+ raise FormulaError("Size not set for opcode 0x%02x" % opcode)
+ pos += sz
+ continue
+ if opcode == 0x00: # tArray
+ spush(unk_opnd)
+ elif opcode == 0x01: # tFunc
+ nb = 1 + int(bv >= 40)
+ funcx = unpack("<" + " BH"[nb], data[pos+1:pos+1+nb])[0]
+ func_attrs = func_defs.get(funcx, None)
+ if not func_attrs:
+ print("*** formula/tFunc unknown FuncID:%d" % funcx, file=bk.logfile)
+ spush(unk_opnd)
+ else:
+ func_name, nargs = func_attrs[:2]
+ if blah:
+ print(" FuncID=%d name=%s nargs=%d"
+ % (funcx, func_name, nargs), file=bk.logfile)
+ assert len(stack) >= nargs
+ if nargs:
+ argtext = listsep.join(arg.text for arg in stack[-nargs:])
+ otext = "%s(%s)" % (func_name, argtext)
+ del stack[-nargs:]
+ else:
+ otext = func_name + "()"
+ res = Operand(oUNK, None, FUNC_RANK, otext)
+ spush(res)
+ elif opcode == 0x02: #tFuncVar
+ nb = 1 + int(bv >= 40)
+ nargs, funcx = unpack("= nargs
+ assert len(stack) >= nargs
+ argtext = listsep.join(arg.text for arg in stack[-nargs:])
+ otext = "%s(%s)" % (func_name, argtext)
+ res = Operand(oUNK, None, FUNC_RANK, otext)
+ del stack[-nargs:]
+ spush(res)
+ elif opcode == 0x03: #tName
+ tgtnamex = unpack("> bk.logfile, " ", res
+ res1, res2 = get_cell_range_addr(
+ data, pos+1, bv, reldelta, browx, bcolx)
+ if blah: print(" ", res1, res2, file=bk.logfile)
+ rowx1, colx1, row_rel1, col_rel1 = res1
+ rowx2, colx2, row_rel2, col_rel2 = res2
+ coords = (rowx1, rowx2+1, colx1, colx2+1)
+ relflags = (row_rel1, row_rel2, col_rel1, col_rel2)
+ if sum(relflags): # relative
+ okind = oREL
+ else:
+ okind = oREF
+ if blah: print(" ", coords, relflags, file=bk.logfile)
+ otext = rangename2drel(coords, relflags, browx, bcolx, r1c1)
+ res = Operand(okind, None, LEAF_RANK, otext)
+ spush(res)
+ elif opcode == 0x1A: # tRef3d
+ if bv >= 80:
+ res = get_cell_addr(data, pos+3, bv, reldelta, browx, bcolx)
+ refx = unpack("= 80:
+ res1, res2 = get_cell_range_addr(data, pos+3, bv, reldelta)
+ refx = unpack("= 80:
+ refx, tgtnamex = unpack(" 0:
+ refx -= 1
+ elif refx < 0:
+ refx = -refx - 1
+ else:
+ dodgy = 1
+ if blah:
+ print(" origrefx=%d refx=%d tgtnamex=%d dodgy=%d"
+ % (origrefx, refx, tgtnamex, dodgy), file=bk.logfile)
+ # if tgtnamex == namex:
+ # if blah: print >> bk.logfile, "!!!! Self-referential !!!!"
+ # dodgy = any_err = 1
+ if not dodgy:
+ if bv >= 80:
+ shx1, shx2 = get_externsheet_local_range(bk, refx, blah)
+ elif origrefx > 0:
+ shx1, shx2 = (-4, -4) # external ref
+ else:
+ exty = bk._externsheet_type_b57[refx]
+ if exty == 4: # non-specific sheet in own doc't
+ shx1, shx2 = (-1, -1) # internal, any sheet
+ else:
+ shx1, shx2 = (-666, -666)
+ okind = oUNK
+ ovalue = None
+ if shx1 == -5: # addin func name
+ okind = oSTRG
+ ovalue = bk.addin_func_names[tgtnamex]
+ otext = '"' + ovalue.replace('"', '""') + '"'
+ elif dodgy or shx1 < -1:
+ otext = "<>" \
+ % (tgtnamex, origrefx)
+ else:
+ tgtobj = bk.name_obj_list[tgtnamex]
+ if tgtobj.scope == -1:
+ otext = tgtobj.name
+ else:
+ otext = "%s!%s" \
+ % (bk._sheet_names[tgtobj.scope], tgtobj.name)
+ if blah:
+ print(" tNameX: setting text to", repr(res.text), file=bk.logfile)
+ res = Operand(okind, ovalue, LEAF_RANK, otext)
+ spush(res)
+ elif opcode in error_opcodes:
+ any_err = 1
+ spush(error_opnd)
+ else:
+ if blah:
+ print("FORMULA: /// Not handled yet: t" + oname, file=bk.logfile)
+ any_err = 1
+ if sz <= 0:
+ raise FormulaError("Fatal: token size is not positive")
+ pos += sz
+ any_rel = not not any_rel
+ if blah:
+ print("End of formula. level=%d any_rel=%d any_err=%d stack=%r" %
+ (level, not not any_rel, any_err, stack), file=bk.logfile)
+ if len(stack) >= 2:
+ print("*** Stack has unprocessed args", file=bk.logfile)
+ print(file=bk.logfile)
+
+ if len(stack) != 1:
+ result = None
+ else:
+ result = stack[0].text
+ return result
+
+#### under deconstruction ###
+def dump_formula(bk, data, fmlalen, bv, reldelta, blah=0, isname=0):
+ if blah:
+ print("dump_formula", fmlalen, bv, len(data), file=bk.logfile)
+ hex_char_dump(data, 0, fmlalen, fout=bk.logfile)
+ assert bv >= 80 #### this function needs updating ####
+ sztab = szdict[bv]
+ pos = 0
+ stack = []
+ any_rel = 0
+ any_err = 0
+ spush = stack.append
+ while 0 <= pos < fmlalen:
+ op = BYTES_ORD(data[pos])
+ opcode = op & 0x1f
+ optype = (op & 0x60) >> 5
+ if optype:
+ opx = opcode + 32
+ else:
+ opx = opcode
+ oname = onames[opx] # + [" RVA"][optype]
+
+ sz = sztab[opx]
+ if blah:
+ print("Pos:%d Op:0x%02x Name:t%s Sz:%d opcode:%02xh optype:%02xh"
+ % (pos, op, oname, sz, opcode, optype), file=bk.logfile)
+ if not optype:
+ if 0x01 <= opcode <= 0x02: # tExp, tTbl
+ # reference to a shared formula or table record
+ rowx, colx = unpack("= 2
+ bop = stack.pop()
+ aop = stack.pop()
+ spush(aop + bop)
+ if blah: print("tlist post", stack, file=bk.logfile)
+ elif opcode == 0x11: # tRange
+ if blah: print("tRange pre", stack, file=bk.logfile)
+ assert len(stack) >= 2
+ bop = stack.pop()
+ aop = stack.pop()
+ assert len(aop) == 1
+ assert len(bop) == 1
+ result = do_box_funcs(tRangeFuncs, aop[0], bop[0])
+ spush(result)
+ if blah: print("tRange post", stack, file=bk.logfile)
+ elif opcode == 0x0F: # tIsect
+ if blah: print("tIsect pre", stack, file=bk.logfile)
+ assert len(stack) >= 2
+ bop = stack.pop()
+ aop = stack.pop()
+ assert len(aop) == 1
+ assert len(bop) == 1
+ result = do_box_funcs(tIsectFuncs, aop[0], bop[0])
+ spush(result)
+ if blah: print("tIsect post", stack, file=bk.logfile)
+ elif opcode == 0x19: # tAttr
+ subop, nc = unpack("= 40)
+ funcx = unpack("<" + " BH"[nb], data[pos+1:pos+1+nb])
+ if blah: print(" FuncID=%d" % funcx, file=bk.logfile)
+ elif opcode == 0x02: #tFuncVar
+ nb = 1 + int(bv >= 40)
+ nargs, funcx = unpack("= 2:
+ print("*** Stack has unprocessed args", file=bk.logfile)
+
+# === Some helper functions for displaying cell references ===
+
+# I'm aware of only one possibility of a sheet-relative component in
+# a reference: a 2D reference located in the "current sheet".
+# xlrd stores this internally with bounds of (0, 1, ...) and
+# relative flags of (1, 1, ...). These functions display the
+# sheet component as empty, just like Excel etc.
+
+def rownamerel(rowx, rowxrel, browx=None, r1c1=0):
+ # if no base rowx is provided, we have to return r1c1
+ if browx is None:
+ r1c1 = True
+ if not rowxrel:
+ if r1c1:
+ return "R%d" % (rowx+1)
+ return "$%d" % (rowx+1)
+ if r1c1:
+ if rowx:
+ return "R[%d]" % rowx
+ return "R"
+ return "%d" % ((browx + rowx) % 65536 + 1)
+
+def colnamerel(colx, colxrel, bcolx=None, r1c1=0):
+ # if no base colx is provided, we have to return r1c1
+ if bcolx is None:
+ r1c1 = True
+ if not colxrel:
+ if r1c1:
+ return "C%d" % (colx + 1)
+ return "$" + colname(colx)
+ if r1c1:
+ if colx:
+ return "C[%d]" % colx
+ return "C"
+ return colname((bcolx + colx) % 256)
+
+def cellname(rowx, colx):
+ """Utility function: ``(5, 7)`` => ``'H6'``"""
+ return "%s%d" % (colname(colx), rowx+1)
+
+def cellnameabs(rowx, colx, r1c1=0):
+ """Utility function: ``(5, 7)`` => ``'$H$6'``"""
+ if r1c1:
+ return "R%dC%d" % (rowx+1, colx+1)
+ return "$%s$%d" % (colname(colx), rowx+1)
+
+def cellnamerel(rowx, colx, rowxrel, colxrel, browx=None, bcolx=None, r1c1=0):
+ if not rowxrel and not colxrel:
+ return cellnameabs(rowx, colx, r1c1)
+ if (rowxrel and browx is None) or (colxrel and bcolx is None):
+ # must flip the whole cell into R1C1 mode
+ r1c1 = True
+ c = colnamerel(colx, colxrel, bcolx, r1c1)
+ r = rownamerel(rowx, rowxrel, browx, r1c1)
+ if r1c1:
+ return r + c
+ return c + r
+
+def colname(colx):
+ """Utility function: ``7`` => ``'H'``, ``27`` => ``'AB'``"""
+ alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ if colx <= 25:
+ return alphabet[colx]
+ else:
+ xdiv26, xmod26 = divmod(colx, 26)
+ return alphabet[xdiv26 - 1] + alphabet[xmod26]
+
+def rangename2d(rlo, rhi, clo, chi, r1c1=0):
+ """ ``(5, 20, 7, 10)`` => ``'$H$6:$J$20'`` """
+ if r1c1:
+ return
+ if rhi == rlo+1 and chi == clo+1:
+ return cellnameabs(rlo, clo, r1c1)
+ return "%s:%s" % (cellnameabs(rlo, clo, r1c1), cellnameabs(rhi-1, chi-1, r1c1))
+
+def rangename2drel(rlo_rhi_clo_chi, rlorel_rhirel_clorel_chirel, browx=None, bcolx=None, r1c1=0):
+ rlo, rhi, clo, chi = rlo_rhi_clo_chi
+ rlorel, rhirel, clorel, chirel = rlorel_rhirel_clorel_chirel
+ if (rlorel or rhirel) and browx is None:
+ r1c1 = True
+ if (clorel or chirel) and bcolx is None:
+ r1c1 = True
+ return "%s:%s" % (
+ cellnamerel(rlo, clo, rlorel, clorel, browx, bcolx, r1c1),
+ cellnamerel(rhi-1, chi-1, rhirel, chirel, browx, bcolx, r1c1),
+ )
+
+
+def rangename3d(book, ref3d):
+ """
+ Utility function:
+ ``Ref3D(1, 4, 5, 20, 7, 10)`` =>
+ ``'Sheet2:Sheet3!$H$6:$J$20'``
+ (assuming Excel's default sheetnames)
+ """
+ coords = ref3d.coords
+ return "%s!%s" % (
+ sheetrange(book, *coords[:2]),
+ rangename2d(*coords[2:6]))
+
+def rangename3drel(book, ref3d, browx=None, bcolx=None, r1c1=0):
+ """
+ Utility function:
+ ``Ref3D(coords=(0, 1, -32, -22, -13, 13), relflags=(0, 0, 1, 1, 1, 1))``
+
+ In R1C1 mode => ``'Sheet1!R[-32]C[-13]:R[-23]C[12]'``
+
+ In A1 mode => depends on base cell ``(browx, bcolx)``
+ """
+ coords = ref3d.coords
+ relflags = ref3d.relflags
+ shdesc = sheetrangerel(book, coords[:2], relflags[:2])
+ rngdesc = rangename2drel(coords[2:6], relflags[2:6], browx, bcolx, r1c1)
+ if not shdesc:
+ return rngdesc
+ return "%s!%s" % (shdesc, rngdesc)
+
+def quotedsheetname(shnames, shx):
+ if shx >= 0:
+ shname = shnames[shx]
+ else:
+ shname = {
+ -1: "?internal; any sheet?",
+ -2: "internal; deleted sheet",
+ -3: "internal; macro sheet",
+ -4: "<>",
+ }.get(shx, "?error %d?" % shx)
+ if "'" in shname:
+ return "'" + shname.replace("'", "''") + "'"
+ if " " in shname:
+ return "'" + shname + "'"
+ return shname
+
+def sheetrange(book, slo, shi):
+ shnames = book.sheet_names()
+ shdesc = quotedsheetname(shnames, slo)
+ if slo != shi-1:
+ shdesc += ":" + quotedsheetname(shnames, shi-1)
+ return shdesc
+
+def sheetrangerel(book, srange, srangerel):
+ slo, shi = srange
+ slorel, shirel = srangerel
+ if not slorel and not shirel:
+ return sheetrange(book, slo, shi)
+ assert (slo == 0 == shi-1) and slorel and shirel
+ return ""
+
+# ==============================================================
diff --git a/.venv/lib/python3.9/site-packages/xlrd/info.py b/.venv/lib/python3.9/site-packages/xlrd/info.py
new file mode 100644
index 00000000..f26b6bb2
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlrd/info.py
@@ -0,0 +1 @@
+__version__ = __VERSION__ = "2.0.1"
diff --git a/.venv/lib/python3.9/site-packages/xlrd/sheet.py b/.venv/lib/python3.9/site-packages/xlrd/sheet.py
new file mode 100644
index 00000000..a8313980
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlrd/sheet.py
@@ -0,0 +1,2490 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2005-2013 Stephen John Machin, Lingfo Pty Ltd
+# This module is part of the xlrd package, which is released under a
+# BSD-style licence.
+
+from __future__ import print_function
+
+from array import array
+from struct import calcsize, unpack
+
+from .biffh import *
+from .formatting import Format, nearest_colour_index
+from .formula import (
+ FMLA_TYPE_CELL, FMLA_TYPE_SHARED, decompile_formula, dump_formula,
+ rangename2d,
+)
+from .timemachine import *
+
+DEBUG = 0
+OBJ_MSO_DEBUG = 0
+
+_WINDOW2_options = (
+ # Attribute names and initial values to use in case
+ # a WINDOW2 record is not written.
+ ("show_formulas", 0),
+ ("show_grid_lines", 1),
+ ("show_sheet_headers", 1),
+ ("panes_are_frozen", 0),
+ ("show_zero_values", 1),
+ ("automatic_grid_line_colour", 1),
+ ("columns_from_right_to_left", 0),
+ ("show_outline_symbols", 1),
+ ("remove_splits_if_pane_freeze_is_removed", 0),
+ # Multiple sheets can be selected, but only one can be active
+ # (hold down Ctrl and click multiple tabs in the file in OOo)
+ ("sheet_selected", 0),
+ # "sheet_visible" should really be called "sheet_active"
+ # and is 1 when this sheet is the sheet displayed when the file
+ # is open. More than likely only one sheet should ever be set as
+ # visible.
+ # This would correspond to the Book's sheet_active attribute, but
+ # that doesn't exist as WINDOW1 records aren't currently processed.
+ # The real thing is the visibility attribute from the BOUNDSHEET record.
+ ("sheet_visible", 0),
+ ("show_in_page_break_preview", 0),
+)
+
+
+
+class Sheet(BaseObject):
+ """
+ Contains the data for one worksheet.
+
+ In the cell access functions, ``rowx`` is a row index, counting from
+ zero, and ``colx`` is a column index, counting from zero.
+ Negative values for row/column indexes and slice positions are supported in
+ the expected fashion.
+
+ For information about cell types and cell values, refer to the documentation
+ of the :class:`Cell` class.
+
+ .. warning::
+
+ You don't instantiate this class yourself. You access :class:`Sheet`
+ objects via the :class:`~xlrd.book.Book` object that
+ was returned when you called :func:`xlrd.open_workbook`.
+ """
+
+ #: Name of sheet.
+ name = ''
+
+ #: A reference to the :class:`~xlrd.book.Book` object to which this sheet
+ #: belongs.
+ #:
+ #: Example usage: ``some_sheet.book.datemode``
+ book = None
+
+ #: Number of rows in sheet. A row index is in ``range(thesheet.nrows)``.
+ nrows = 0
+
+ #: Nominal number of columns in sheet. It is one more than the maximum
+ #: column index found, ignoring trailing empty cells.
+ #: See also the ``ragged_rows`` parameter to :func:`~xlrd.open_workbook`
+ #: and :meth:`~xlrd.sheet.Sheet.row_len`.
+ ncols = 0
+
+
+ #: The map from a column index to a :class:`Colinfo` object. Often there is
+ #: an entry in ``COLINFO`` records for all column indexes in ``range(257)``.
+ #:
+ #: .. note::
+ #: xlrd ignores the entry for the non-existent
+ #: 257th column.
+ #:
+ #: On the other hand, there may be no entry for unused columns.
+ #:
+ #: .. versionadded:: 0.6.1
+ #:
+ #: Populated only if ``open_workbook(..., formatting_info=True)``
+ colinfo_map = {}
+
+ #: The map from a row index to a :class:`Rowinfo` object.
+ #:
+ #: ..note::
+ #: It is possible to have missing entries -- at least one source of
+ #: XLS files doesn't bother writing ``ROW`` records.
+ #:
+ #: .. versionadded:: 0.6.1
+ #:
+ #: Populated only if ``open_workbook(..., formatting_info=True)``
+ rowinfo_map = {}
+
+ #: List of address ranges of cells containing column labels.
+ #: These are set up in Excel by Insert > Name > Labels > Columns.
+ #:
+ #: .. versionadded:: 0.6.0
+ #:
+ #: How to deconstruct the list:
+ #:
+ #: .. code-block:: python
+ #:
+ #: for crange in thesheet.col_label_ranges:
+ #: rlo, rhi, clo, chi = crange
+ #: for rx in xrange(rlo, rhi):
+ #: for cx in xrange(clo, chi):
+ #: print "Column label at (rowx=%d, colx=%d) is %r" \
+ #: (rx, cx, thesheet.cell_value(rx, cx))
+ col_label_ranges = []
+
+ #: List of address ranges of cells containing row labels.
+ #: For more details, see :attr:`col_label_ranges`.
+ #:
+ #: .. versionadded:: 0.6.0
+ row_label_ranges = []
+
+ #: List of address ranges of cells which have been merged.
+ #: These are set up in Excel by Format > Cells > Alignment, then ticking
+ #: the "Merge cells" box.
+ #:
+ #: .. note::
+ #: The upper limits are exclusive: i.e. ``[2, 3, 7, 9]`` only
+ #: spans two cells.
+ #:
+ #: .. note:: Extracted only if ``open_workbook(..., formatting_info=True)``
+ #:
+ #: .. versionadded:: 0.6.1
+ #:
+ #: How to deconstruct the list:
+ #:
+ #: .. code-block:: python
+ #:
+ #: for crange in thesheet.merged_cells:
+ #: rlo, rhi, clo, chi = crange
+ #: for rowx in xrange(rlo, rhi):
+ #: for colx in xrange(clo, chi):
+ #: # cell (rlo, clo) (the top left one) will carry the data
+ #: # and formatting info; the remainder will be recorded as
+ #: # blank cells, but a renderer will apply the formatting info
+ #: # for the top left cell (e.g. border, pattern) to all cells in
+ #: # the range.
+ merged_cells = []
+
+ #: Mapping of ``(rowx, colx)`` to list of ``(offset, font_index)`` tuples.
+ #: The offset defines where in the string the font begins to be used.
+ #: Offsets are expected to be in ascending order.
+ #: If the first offset is not zero, the meaning is that the cell's ``XF``'s
+ #: font should be used from offset 0.
+ #:
+ #: This is a sparse mapping. There is no entry for cells that are not
+ #: formatted with rich text.
+ #:
+ #: How to use:
+ #:
+ #: .. code-block:: python
+ #:
+ #: runlist = thesheet.rich_text_runlist_map.get((rowx, colx))
+ #: if runlist:
+ #: for offset, font_index in runlist:
+ #: # do work here.
+ #: pass
+ #:
+ #: .. versionadded:: 0.7.2
+ #:
+ #: Populated only if ``open_workbook(..., formatting_info=True)``
+ rich_text_runlist_map = {}
+
+ #: Default column width from ``DEFCOLWIDTH`` record, else ``None``.
+ #: From the OOo docs:
+ #:
+ #: Column width in characters, using the width of the zero character
+ #: from default font (first FONT record in the file). Excel adds some
+ #: extra space to the default width, depending on the default font and
+ #: default font size. The algorithm how to exactly calculate the resulting
+ #: column width is not known.
+ #: Example: The default width of 8 set in this record results in a column
+ #: width of 8.43 using Arial font with a size of 10 points.
+ #:
+ #: For the default hierarchy, refer to the :class:`Colinfo` class.
+ #:
+ #: .. versionadded:: 0.6.1
+ defcolwidth = None
+
+ #: Default column width from ``STANDARDWIDTH`` record, else ``None``.
+ #:
+ #: From the OOo docs:
+ #:
+ #: Default width of the columns in 1/256 of the width of the zero
+ #: character, using default font (first FONT record in the file).
+ #:
+ #: For the default hierarchy, refer to the :class:`Colinfo` class.
+ #:
+ #: .. versionadded:: 0.6.1
+ standardwidth = None
+
+ #: Default value to be used for a row if there is
+ #: no ``ROW`` record for that row.
+ #: From the *optional* ``DEFAULTROWHEIGHT`` record.
+ default_row_height = None
+
+ #: Default value to be used for a row if there is
+ #: no ``ROW`` record for that row.
+ #: From the *optional* ``DEFAULTROWHEIGHT`` record.
+ default_row_height_mismatch = None
+
+ #: Default value to be used for a row if there is
+ #: no ``ROW`` record for that row.
+ #: From the *optional* ``DEFAULTROWHEIGHT`` record.
+ default_row_hidden = None
+
+ #: Default value to be used for a row if there is
+ #: no ``ROW`` record for that row.
+ #: From the *optional* ``DEFAULTROWHEIGHT`` record.
+ default_additional_space_above = None
+
+ #: Default value to be used for a row if there is
+ #: no ``ROW`` record for that row.
+ #: From the *optional* ``DEFAULTROWHEIGHT`` record.
+ default_additional_space_below = None
+
+ #: Visibility of the sheet:
+ #: ::
+ #:
+ #: 0 = visible
+ #: 1 = hidden (can be unhidden by user -- Format -> Sheet -> Unhide)
+ #: 2 = "very hidden" (can be unhidden only by VBA macro).
+ visibility = 0
+
+ #: A 256-element tuple corresponding to the contents of the GCW record for
+ #: this sheet. If no such record, treat as all bits zero.
+ #: Applies to BIFF4-7 only. See docs of the :class:`Colinfo` class for
+ #: discussion.
+ gcw = (0, ) * 256
+
+ #: A list of :class:`Hyperlink` objects corresponding to ``HLINK`` records
+ #: found in the worksheet.
+ #:
+ #: .. versionadded:: 0.7.2
+ hyperlink_list = []
+
+ #: A sparse mapping from ``(rowx, colx)`` to an item in
+ #: :attr:`~xlrd.sheet.Sheet.hyperlink_list`.
+ #: Cells not covered by a hyperlink are not mapped.
+ #: It is possible using the Excel UI to set up a hyperlink that
+ #: covers a larger-than-1x1 rectangle of cells.
+ #: Hyperlink rectangles may overlap (Excel doesn't check).
+ #: When a multiply-covered cell is clicked on, the hyperlink that is
+ #: activated
+ #: (and the one that is mapped here) is the last in
+ #: :attr:`~xlrd.sheet.Sheet.hyperlink_list`.
+ #:
+ #: .. versionadded:: 0.7.2
+ hyperlink_map = {}
+
+ #: A sparse mapping from ``(rowx, colx)`` to a :class:`Note` object.
+ #: Cells not containing a note ("comment") are not mapped.
+ #:
+ #: .. versionadded:: 0.7.2
+ cell_note_map = {}
+
+ #: Number of columns in left pane (frozen panes; for split panes, see
+ #: comments in code)
+ vert_split_pos = 0
+
+ #: Number of rows in top pane (frozen panes; for split panes, see comments
+ #: in code)
+ horz_split_pos = 0
+
+ #: Index of first visible row in bottom frozen/split pane
+ horz_split_first_visible = 0
+
+ #: Index of first visible column in right frozen/split pane
+ vert_split_first_visible = 0
+
+ #: Frozen panes: ignore it. Split panes: explanation and diagrams in
+ #: OOo docs.
+ split_active_pane = 0
+
+ #: Boolean specifying if a ``PANE`` record was present, ignore unless you're
+ #: ``xlutils.copy``
+ has_pane_record = 0
+
+ #: A list of the horizontal page breaks in this sheet.
+ #: Breaks are tuples in the form
+ #: ``(index of row after break, start col index, end col index)``.
+ #:
+ #: Populated only if ``open_workbook(..., formatting_info=True)``
+ #:
+ #: .. versionadded:: 0.7.2
+ horizontal_page_breaks = []
+
+ #: A list of the vertical page breaks in this sheet.
+ #: Breaks are tuples in the form
+ #: ``(index of col after break, start row index, end row index)``.
+ #:
+ #: Populated only if ``open_workbook(..., formatting_info=True)``
+ #:
+ #: .. versionadded:: 0.7.2
+ vertical_page_breaks = []
+
+ def __init__(self, book, position, name, number):
+ self.book = book
+ self.biff_version = book.biff_version
+ self._position = position
+ self.logfile = book.logfile
+ self.bt = array('B', [XL_CELL_EMPTY])
+ self.bf = array('h', [-1])
+ self.name = name
+ self.number = number
+ self.verbosity = book.verbosity
+ self.formatting_info = book.formatting_info
+ self.ragged_rows = book.ragged_rows
+ if self.ragged_rows:
+ self.put_cell = self.put_cell_ragged
+ else:
+ self.put_cell = self.put_cell_unragged
+ self._xf_index_to_xl_type_map = book._xf_index_to_xl_type_map
+ self.nrows = 0 # actual, including possibly empty cells
+ self.ncols = 0
+ self._maxdatarowx = -1 # highest rowx containing a non-empty cell
+ self._maxdatacolx = -1 # highest colx containing a non-empty cell
+ self._dimnrows = 0 # as per DIMENSIONS record
+ self._dimncols = 0
+ self._cell_values = []
+ self._cell_types = []
+ self._cell_xf_indexes = []
+ self.defcolwidth = None
+ self.standardwidth = None
+ self.default_row_height = None
+ self.default_row_height_mismatch = 0
+ self.default_row_hidden = 0
+ self.default_additional_space_above = 0
+ self.default_additional_space_below = 0
+ self.colinfo_map = {}
+ self.rowinfo_map = {}
+ self.col_label_ranges = []
+ self.row_label_ranges = []
+ self.merged_cells = []
+ self.rich_text_runlist_map = {}
+ self.horizontal_page_breaks = []
+ self.vertical_page_breaks = []
+ self._xf_index_stats = [0, 0, 0, 0]
+ self.visibility = book._sheet_visibility[number] # from BOUNDSHEET record
+ for attr, defval in _WINDOW2_options:
+ setattr(self, attr, defval)
+ self.first_visible_rowx = 0
+ self.first_visible_colx = 0
+ self.gridline_colour_index = 0x40
+ self.gridline_colour_rgb = None # pre-BIFF8
+ self.hyperlink_list = []
+ self.hyperlink_map = {}
+ self.cell_note_map = {}
+
+ # Values calculated by xlrd to predict the mag factors that
+ # will actually be used by Excel to display your worksheet.
+ # Pass these values to xlwt when writing XLS files.
+ # Warning 1: Behaviour of OOo Calc and Gnumeric has been observed to differ from Excel's.
+ # Warning 2: A value of zero means almost exactly what it says. Your sheet will be
+ # displayed as a very tiny speck on the screen. xlwt will reject attempts to set
+ # a mag_factor that is not (10 <= mag_factor <= 400).
+ self.cooked_page_break_preview_mag_factor = 60
+ self.cooked_normal_view_mag_factor = 100
+
+ # Values (if any) actually stored on the XLS file
+ self.cached_page_break_preview_mag_factor = 0 # default (60%), from WINDOW2 record
+ self.cached_normal_view_mag_factor = 0 # default (100%), from WINDOW2 record
+ self.scl_mag_factor = None # from SCL record
+
+ self._ixfe = None # BIFF2 only
+ self._cell_attr_to_xfx = {} # BIFF2.0 only
+
+ if self.biff_version >= 80:
+ self.utter_max_rows = 65536
+ else:
+ self.utter_max_rows = 16384
+ self.utter_max_cols = 256
+
+ self._first_full_rowx = -1
+
+ # self._put_cell_exceptions = 0
+ # self._put_cell_row_widenings = 0
+ # self._put_cell_rows_appended = 0
+ # self._put_cell_cells_appended = 0
+
+ def cell(self, rowx, colx):
+ """
+ :class:`Cell` object in the given row and column.
+ """
+ if self.formatting_info:
+ xfx = self.cell_xf_index(rowx, colx)
+ else:
+ xfx = None
+ return Cell(
+ self._cell_types[rowx][colx],
+ self._cell_values[rowx][colx],
+ xfx,
+ )
+
+ def cell_value(self, rowx, colx):
+ "Value of the cell in the given row and column."
+ return self._cell_values[rowx][colx]
+
+ def cell_type(self, rowx, colx):
+ """
+ Type of the cell in the given row and column.
+
+ Refer to the documentation of the :class:`Cell` class.
+ """
+ return self._cell_types[rowx][colx]
+
+ def cell_xf_index(self, rowx, colx):
+ """
+ XF index of the cell in the given row and column.
+ This is an index into :attr:`~xlrd.book.Book.xf_list`.
+
+ .. versionadded:: 0.6.1
+ """
+ self.req_fmt_info()
+ xfx = self._cell_xf_indexes[rowx][colx]
+ if xfx > -1:
+ self._xf_index_stats[0] += 1
+ return xfx
+ # Check for a row xf_index
+ try:
+ xfx = self.rowinfo_map[rowx].xf_index
+ if xfx > -1:
+ self._xf_index_stats[1] += 1
+ return xfx
+ except KeyError:
+ pass
+ # Check for a column xf_index
+ try:
+ xfx = self.colinfo_map[colx].xf_index
+ if xfx == -1: xfx = 15
+ self._xf_index_stats[2] += 1
+ return xfx
+ except KeyError:
+ # If all else fails, 15 is used as hardwired global default xf_index.
+ self._xf_index_stats[3] += 1
+ return 15
+
+ def row_len(self, rowx):
+ """
+ Returns the effective number of cells in the given row. For use with
+ ``open_workbook(ragged_rows=True)`` which is likely to produce rows
+ with fewer than :attr:`~Sheet.ncols` cells.
+
+ .. versionadded:: 0.7.2
+ """
+ return len(self._cell_values[rowx])
+
+ def row(self, rowx):
+ """
+ Returns a sequence of the :class:`Cell` objects in the given row.
+ """
+ return [
+ self.cell(rowx, colx)
+ for colx in xrange(len(self._cell_values[rowx]))
+ ]
+
+ def __getitem__(self, item):
+ """
+ Takes either rowindex or (rowindex, colindex) as an index,
+ and returns either row or cell respectively.
+ """
+ try:
+ rowix, colix = item
+ except TypeError:
+ # it's not a tuple (or of right size), let's try indexing as is
+ # if this is a problem, let this error propagate back
+ return self.row(item)
+ else:
+ return self.cell(rowix, colix)
+
+ def get_rows(self):
+ "Returns a generator for iterating through each row."
+ return (self.row(index) for index in range(self.nrows))
+
+ # makes `for row in sheet` natural and intuitive
+ __iter__ = get_rows
+
+ def row_types(self, rowx, start_colx=0, end_colx=None):
+ """
+ Returns a slice of the types of the cells in the given row.
+ """
+ if end_colx is None:
+ return self._cell_types[rowx][start_colx:]
+ return self._cell_types[rowx][start_colx:end_colx]
+
+ def row_values(self, rowx, start_colx=0, end_colx=None):
+ """
+ Returns a slice of the values of the cells in the given row.
+ """
+ if end_colx is None:
+ return self._cell_values[rowx][start_colx:]
+ return self._cell_values[rowx][start_colx:end_colx]
+
+ def row_slice(self, rowx, start_colx=0, end_colx=None):
+ """
+ Returns a slice of the :class:`Cell` objects in the given row.
+ """
+ nc = len(self._cell_values[rowx])
+ if start_colx < 0:
+ start_colx += nc
+ if start_colx < 0:
+ start_colx = 0
+ if end_colx is None or end_colx > nc:
+ end_colx = nc
+ elif end_colx < 0:
+ end_colx += nc
+ return [
+ self.cell(rowx, colx)
+ for colx in xrange(start_colx, end_colx)
+ ]
+
+ def col_slice(self, colx, start_rowx=0, end_rowx=None):
+ """
+ Returns a slice of the :class:`Cell` objects in the given column.
+ """
+ nr = self.nrows
+ if start_rowx < 0:
+ start_rowx += nr
+ if start_rowx < 0:
+ start_rowx = 0
+ if end_rowx is None or end_rowx > nr:
+ end_rowx = nr
+ elif end_rowx < 0:
+ end_rowx += nr
+ return [
+ self.cell(rowx, colx)
+ for rowx in xrange(start_rowx, end_rowx)
+ ]
+
+ def col_values(self, colx, start_rowx=0, end_rowx=None):
+ """
+ Returns a slice of the values of the cells in the given column.
+ """
+ nr = self.nrows
+ if start_rowx < 0:
+ start_rowx += nr
+ if start_rowx < 0:
+ start_rowx = 0
+ if end_rowx is None or end_rowx > nr:
+ end_rowx = nr
+ elif end_rowx < 0:
+ end_rowx += nr
+ return [
+ self._cell_values[rowx][colx]
+ for rowx in xrange(start_rowx, end_rowx)
+ ]
+
+ def col_types(self, colx, start_rowx=0, end_rowx=None):
+ """
+ Returns a slice of the types of the cells in the given column.
+ """
+ nr = self.nrows
+ if start_rowx < 0:
+ start_rowx += nr
+ if start_rowx < 0:
+ start_rowx = 0
+ if end_rowx is None or end_rowx > nr:
+ end_rowx = nr
+ elif end_rowx < 0:
+ end_rowx += nr
+ return [
+ self._cell_types[rowx][colx]
+ for rowx in xrange(start_rowx, end_rowx)
+ ]
+
+ col = col_slice
+
+ # === Following methods are used in building the worksheet.
+ # === They are not part of the API.
+
+ def tidy_dimensions(self):
+ if self.verbosity >= 3:
+ fprintf(
+ self.logfile,
+ "tidy_dimensions: nrows=%d ncols=%d \n",
+ self.nrows, self.ncols,
+ )
+ if 1 and self.merged_cells:
+ nr = nc = 0
+ umaxrows = self.utter_max_rows
+ umaxcols = self.utter_max_cols
+ for crange in self.merged_cells:
+ rlo, rhi, clo, chi = crange
+ if not (0 <= rlo < rhi <= umaxrows) or not (0 <= clo < chi <= umaxcols):
+ fprintf(self.logfile,
+ "*** WARNING: sheet #%d (%r), MERGEDCELLS bad range %r\n",
+ self.number, self.name, crange)
+ if rhi > nr: nr = rhi
+ if chi > nc: nc = chi
+ if nc > self.ncols:
+ self.ncols = nc
+ self._first_full_rowx = -2
+ if nr > self.nrows:
+ # we put one empty cell at (nr-1,0) to make sure
+ # we have the right number of rows. The ragged rows
+ # will sort out the rest if needed.
+ self.put_cell(nr-1, 0, XL_CELL_EMPTY, UNICODE_LITERAL(''), -1)
+ if (self.verbosity >= 1 and
+ (self.nrows != self._dimnrows or self.ncols != self._dimncols)):
+ fprintf(
+ self.logfile,
+ "NOTE *** sheet %d (%r): DIMENSIONS R,C = %d,%d should be %d,%d\n",
+ self.number,
+ self.name,
+ self._dimnrows,
+ self._dimncols,
+ self.nrows,
+ self.ncols,
+ )
+ if not self.ragged_rows:
+ # fix ragged rows
+ ncols = self.ncols
+ s_cell_types = self._cell_types
+ s_cell_values = self._cell_values
+ s_cell_xf_indexes = self._cell_xf_indexes
+ s_fmt_info = self.formatting_info
+ # for rowx in xrange(self.nrows):
+ if self._first_full_rowx == -2:
+ ubound = self.nrows
+ else:
+ ubound = self._first_full_rowx
+ for rowx in xrange(ubound):
+ trow = s_cell_types[rowx]
+ rlen = len(trow)
+ nextra = ncols - rlen
+ if nextra > 0:
+ s_cell_values[rowx][rlen:] = [UNICODE_LITERAL('')] * nextra
+ trow[rlen:] = self.bt * nextra
+ if s_fmt_info:
+ s_cell_xf_indexes[rowx][rlen:] = self.bf * nextra
+
+ def put_cell_ragged(self, rowx, colx, ctype, value, xf_index):
+ if ctype is None:
+ # we have a number, so look up the cell type
+ ctype = self._xf_index_to_xl_type_map[xf_index]
+ assert 0 <= colx < self.utter_max_cols
+ assert 0 <= rowx < self.utter_max_rows
+ fmt_info = self.formatting_info
+
+ try:
+ nr = rowx + 1
+ if self.nrows < nr:
+
+ scta = self._cell_types.append
+ scva = self._cell_values.append
+ scxa = self._cell_xf_indexes.append
+ bt = self.bt
+ bf = self.bf
+ for _unused in xrange(self.nrows, nr):
+ scta(bt * 0)
+ scva([])
+ if fmt_info:
+ scxa(bf * 0)
+ self.nrows = nr
+
+ types_row = self._cell_types[rowx]
+ values_row = self._cell_values[rowx]
+ if fmt_info:
+ fmt_row = self._cell_xf_indexes[rowx]
+ ltr = len(types_row)
+ if colx >= self.ncols:
+ self.ncols = colx + 1
+ num_empty = colx - ltr
+ if not num_empty:
+ # most common case: colx == previous colx + 1
+ # self._put_cell_cells_appended += 1
+ types_row.append(ctype)
+ values_row.append(value)
+ if fmt_info:
+ fmt_row.append(xf_index)
+ return
+ if num_empty > 0:
+ num_empty += 1
+ # self._put_cell_row_widenings += 1
+ # types_row.extend(self.bt * num_empty)
+ # values_row.extend([UNICODE_LITERAL('')] * num_empty)
+ # if fmt_info:
+ # fmt_row.extend(self.bf * num_empty)
+ types_row[ltr:] = self.bt * num_empty
+ values_row[ltr:] = [UNICODE_LITERAL('')] * num_empty
+ if fmt_info:
+ fmt_row[ltr:] = self.bf * num_empty
+ types_row[colx] = ctype
+ values_row[colx] = value
+ if fmt_info:
+ fmt_row[colx] = xf_index
+ except:
+ print("put_cell", rowx, colx, file=self.logfile)
+ raise
+
+ def put_cell_unragged(self, rowx, colx, ctype, value, xf_index):
+ if ctype is None:
+ # we have a number, so look up the cell type
+ ctype = self._xf_index_to_xl_type_map[xf_index]
+ # assert 0 <= colx < self.utter_max_cols
+ # assert 0 <= rowx < self.utter_max_rows
+ try:
+ self._cell_types[rowx][colx] = ctype
+ self._cell_values[rowx][colx] = value
+ if self.formatting_info:
+ self._cell_xf_indexes[rowx][colx] = xf_index
+ except IndexError:
+ # print >> self.logfile, "put_cell extending", rowx, colx
+ # self.extend_cells(rowx+1, colx+1)
+ # self._put_cell_exceptions += 1
+ nr = rowx + 1
+ nc = colx + 1
+ assert 1 <= nc <= self.utter_max_cols
+ assert 1 <= nr <= self.utter_max_rows
+ if nc > self.ncols:
+ self.ncols = nc
+ # The row self._first_full_rowx and all subsequent rows
+ # are guaranteed to have length == self.ncols. Thus the
+ # "fix ragged rows" section of the tidy_dimensions method
+ # doesn't need to examine them.
+ if nr < self.nrows:
+ # cell data is not in non-descending row order *AND*
+ # self.ncols has been bumped up.
+ # This very rare case ruins this optimisation.
+ self._first_full_rowx = -2
+ elif rowx > self._first_full_rowx > -2:
+ self._first_full_rowx = rowx
+ if nr <= self.nrows:
+ # New cell is in an existing row, so extend that row (if necessary).
+ # Note that nr < self.nrows means that the cell data
+ # is not in ascending row order!!
+ trow = self._cell_types[rowx]
+ nextra = self.ncols - len(trow)
+ if nextra > 0:
+ # self._put_cell_row_widenings += 1
+ trow.extend(self.bt * nextra)
+ if self.formatting_info:
+ self._cell_xf_indexes[rowx].extend(self.bf * nextra)
+ self._cell_values[rowx].extend([UNICODE_LITERAL('')] * nextra)
+ else:
+ scta = self._cell_types.append
+ scva = self._cell_values.append
+ scxa = self._cell_xf_indexes.append
+ fmt_info = self.formatting_info
+ nc = self.ncols
+ bt = self.bt
+ bf = self.bf
+ for _unused in xrange(self.nrows, nr):
+ # self._put_cell_rows_appended += 1
+ scta(bt * nc)
+ scva([UNICODE_LITERAL('')] * nc)
+ if fmt_info:
+ scxa(bf * nc)
+ self.nrows = nr
+ # === end of code from extend_cells()
+ try:
+ self._cell_types[rowx][colx] = ctype
+ self._cell_values[rowx][colx] = value
+ if self.formatting_info:
+ self._cell_xf_indexes[rowx][colx] = xf_index
+ except:
+ print("put_cell", rowx, colx, file=self.logfile)
+ raise
+ except:
+ print("put_cell", rowx, colx, file=self.logfile)
+ raise
+
+
+ # === Methods after this line neither know nor care about how cells are stored.
+
+ def read(self, bk):
+ global rc_stats
+ DEBUG = 0
+ blah = DEBUG or self.verbosity >= 2
+ blah_rows = DEBUG or self.verbosity >= 4
+ blah_formulas = 0 and blah
+ r1c1 = 0
+ oldpos = bk._position
+ bk._position = self._position
+ XL_SHRFMLA_ETC_ETC = (
+ XL_SHRFMLA, XL_ARRAY, XL_TABLEOP, XL_TABLEOP2,
+ XL_ARRAY2, XL_TABLEOP_B2,
+ )
+ self_put_cell = self.put_cell
+ local_unpack = unpack
+ bk_get_record_parts = bk.get_record_parts
+ bv = self.biff_version
+ fmt_info = self.formatting_info
+ do_sst_rich_text = fmt_info and bk._rich_text_runlist_map
+ rowinfo_sharing_dict = {}
+ txos = {}
+ eof_found = 0
+ while 1:
+ # if DEBUG: print "SHEET.READ: about to read from position %d" % bk._position
+ rc, data_len, data = bk_get_record_parts()
+ # if rc in rc_stats:
+ # rc_stats[rc] += 1
+ # else:
+ # rc_stats[rc] = 1
+ # if DEBUG: print "SHEET.READ: op 0x%04x, %d bytes %r" % (rc, data_len, data)
+ if rc == XL_NUMBER:
+ # [:14] in following stmt ignores extraneous rubbish at end of record.
+ # Sample file testEON-8.xls supplied by Jan Kraus.
+ rowx, colx, xf_index, d = local_unpack('> 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 not r.has_default_xf_index:
+ r.xf_index = -1
+ self.rowinfo_map[rowx] = r
+ if 0 and r.xf_index > -1:
+ fprintf(self.logfile,
+ "**ROW %d %d %d\n",
+ self.number, rowx, r.xf_index)
+ if blah_rows:
+ print('ROW', rowx, bits1, bits2, file=self.logfile)
+ r.dump(self.logfile,
+ header="--- sh #%d, rowx=%d ---" % (self.number, rowx))
+ elif rc in XL_FORMULA_OPCODES: # 06, 0206, 0406
+ # DEBUG = 1
+ # if DEBUG: print "FORMULA: rc: 0x%04x data: %r" % (rc, data)
+ if bv >= 50:
+ rowx, colx, xf_index, result_str, flags = local_unpack('= 30:
+ rowx, colx, xf_index, result_str, flags = local_unpack(' 255: break # Excel does 0 to 256 inclusive
+ self.colinfo_map[colx] = c
+ if 0:
+ fprintf(self.logfile,
+ "**COL %d %d %d\n",
+ self.number, colx, c.xf_index)
+ if blah:
+ fprintf(
+ self.logfile,
+ "COLINFO sheet #%d cols %d-%d: wid=%d xf_index=%d flags=0x%04x\n",
+ self.number, first_colx, last_colx, c.width, c.xf_index, flags,
+ )
+ c.dump(self.logfile, header='===')
+ elif rc == XL_DEFCOLWIDTH:
+ self.defcolwidth, = local_unpack(">= 1
+ self.gcw = tuple(gcw)
+ if 0:
+ showgcw = "".join(map(lambda x: "F "[x], gcw)).rstrip().replace(' ', '.')
+ print("GCW:", showgcw, file=self.logfile)
+ elif rc == XL_BLANK:
+ if not fmt_info: continue
+ rowx, colx, xf_index = local_unpack('> self.logfile, "BLANK", rowx, colx, xf_index
+ self_put_cell(rowx, colx, XL_CELL_BLANK, '', xf_index)
+ elif rc == XL_MULBLANK: # 00BE
+ if not fmt_info: continue
+ nitems = data_len >> 1
+ result = local_unpack("<%dH" % nitems, data)
+ rowx, mul_first = result[:2]
+ mul_last = result[-1]
+ # print >> self.logfile, "MULBLANK", rowx, mul_first, mul_last, data_len, nitems, mul_last + 4 - mul_first
+ assert nitems == mul_last + 4 - mul_first
+ pos = 2
+ for colx in xrange(mul_first, mul_last + 1):
+ self_put_cell(rowx, colx, XL_CELL_BLANK, '', result[pos])
+ pos += 1
+ elif rc == XL_DIMENSION or rc == XL_DIMENSION2:
+ if data_len == 0:
+ # Four zero bytes after some other record. See github issue 64.
+ continue
+ # 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:
+ dim_tuple = local_unpack(' found EOF", file=self.logfile)
+ elif rc == XL_COUNTRY:
+ bk.handle_country(data)
+ elif rc == XL_LABELRANGES:
+ pos = 0
+ pos = unpack_cell_range_address_list_update_pos(
+ self.row_label_ranges, data, pos, bv, addr_size=8,
+ )
+ pos = unpack_cell_range_address_list_update_pos(
+ self.col_label_ranges, data, pos, bv, addr_size=8,
+ )
+ assert pos == data_len
+ elif rc == XL_ARRAY:
+ row1x, rownx, col1x, colnx, array_flags, tokslen = \
+ local_unpack("= 80
+ num_CFs, needs_recalc, browx1, browx2, bcolx1, bcolx2 = \
+ unpack("<6H", data[0:12])
+ if self.verbosity >= 1:
+ fprintf(
+ self.logfile,
+ "\n*** WARNING: Ignoring CONDFMT (conditional formatting) record\n"
+ "*** in Sheet %d (%r).\n"
+ "*** %d CF record(s); needs_recalc_or_redraw = %d\n"
+ "*** Bounding box is %s\n",
+ self.number, self.name, num_CFs, needs_recalc,
+ rangename2d(browx1, browx2+1, bcolx1, bcolx2+1),
+ )
+ olist = [] # updated by the function
+ pos = unpack_cell_range_address_list_update_pos(
+ olist, data, 12, bv, addr_size=8)
+ # print >> self.logfile, repr(result), len(result)
+ if self.verbosity >= 1:
+ fprintf(
+ self.logfile,
+ "*** %d individual range(s):\n"
+ "*** %s\n",
+ len(olist),
+ ", ".join(rangename2d(*coords) for coords in olist),
+ )
+ elif rc == XL_CF:
+ if not fmt_info: continue
+ cf_type, cmp_op, sz1, sz2, flags = unpack("> 26) & 1
+ bord_block = (flags >> 28) & 1
+ patt_block = (flags >> 29) & 1
+ if self.verbosity >= 1:
+ fprintf(
+ self.logfile,
+ "\n*** WARNING: Ignoring CF (conditional formatting) sub-record.\n"
+ "*** cf_type=%d, cmp_op=%d, sz1=%d, sz2=%d, flags=0x%08x\n"
+ "*** optional data blocks: font=%d, border=%d, pattern=%d\n",
+ cf_type, cmp_op, sz1, sz2, flags,
+ font_block, bord_block, patt_block,
+ )
+ # hex_char_dump(data, 0, data_len, fout=self.logfile)
+ pos = 12
+ if font_block:
+ (font_height, font_options, weight, escapement, underline,
+ font_colour_index, two_bits, font_esc, font_underl) = unpack("<64x i i H H B 3x i 4x i i i 18x", data[pos:pos+118])
+ font_style = (two_bits > 1) & 1
+ posture = (font_options > 1) & 1
+ font_canc = (two_bits > 7) & 1
+ cancellation = (font_options > 7) & 1
+ if self.verbosity >= 1:
+ fprintf(
+ self.logfile,
+ "*** Font info: height=%d, weight=%d, escapement=%d,\n"
+ "*** underline=%d, colour_index=%d, esc=%d, underl=%d,\n"
+ "*** style=%d, posture=%d, canc=%d, cancellation=%d\n",
+ font_height, weight, escapement, underline,
+ font_colour_index, font_esc, font_underl,
+ font_style, posture, font_canc, cancellation,
+ )
+ pos += 118
+ if bord_block:
+ pos += 8
+ if patt_block:
+ pos += 4
+ fmla1 = data[pos:pos+sz1]
+ pos += sz1
+ if blah and sz1:
+ fprintf(self.logfile, "*** formula 1:\n")
+ dump_formula(bk, fmla1, sz1, bv, reldelta=0, blah=1)
+ fmla2 = data[pos:pos+sz2]
+ pos += sz2
+ assert pos == data_len
+ if blah and sz2:
+ fprintf(self.logfile, "*** formula 2:\n")
+ dump_formula(bk, fmla2, sz2, bv, reldelta=0, blah=1)
+ elif rc == XL_DEFAULTROWHEIGHT:
+ if data_len == 4:
+ bits, self.default_row_height = unpack("> 1) & 1
+ self.default_additional_space_above = (bits >> 2) & 1
+ self.default_additional_space_below = (bits >> 3) & 1
+ elif rc == XL_MERGEDCELLS:
+ if not fmt_info: continue
+ pos = unpack_cell_range_address_list_update_pos(
+ self.merged_cells, data, 0, bv, addr_size=8)
+ if blah:
+ fprintf(self.logfile,
+ "MERGEDCELLS: %d ranges\n", (pos - 2) // 8)
+ assert pos == data_len, \
+ "MERGEDCELLS: pos=%d data_len=%d" % (pos, data_len)
+ elif rc == XL_WINDOW2:
+ if bv >= 80 and data_len >= 14:
+ (
+ options,
+ self.first_visible_rowx, self.first_visible_colx,
+ self.gridline_colour_index,
+ self.cached_page_break_preview_mag_factor,
+ self.cached_normal_view_mag_factor
+ ) = unpack("= 30 # BIFF3-7
+ (
+ options,
+ self.first_visible_rowx, self.first_visible_colx,
+ ) = unpack(">= 1
+ elif rc == XL_SCL:
+ num, den = unpack("= 0:
+ print(
+ "WARNING *** SCL rcd sheet %d: should have 0.1 <= num/den <= 4; got %d/%d"
+ % (self.number, num, den),
+ file=self.logfile,
+ )
+ result = 100
+ self.scl_mag_factor = result
+ elif rc == XL_PANE:
+ (
+ self.vert_split_pos,
+ self.horz_split_pos,
+ self.horz_split_first_visible,
+ self.vert_split_first_visible,
+ self.split_active_pane,
+ ) = unpack("= 80)) + 2 == data_len
+ pos = 2
+ if bv < 80:
+ while pos < data_len:
+ self.horizontal_page_breaks.append((local_unpack("= 80)) + 2 == data_len
+ pos = 2
+ if bv < 80:
+ while pos < data_len:
+ self.vertical_page_breaks.append((local_unpack("> 15) & 1
+ r.has_default_xf_index = bits2 & 1
+ r.xf_index = xf_index
+ # r.outline_level = 0 # set in __init__
+ # r.outline_group_starts_ends = 0 # set in __init__
+ # r.hidden = 0 # set in __init__
+ # r.height_mismatch = 0 # set in __init__
+ # r.additional_space_above = 0 # set in __init__
+ # r.additional_space_below = 0 # set in __init__
+ self.rowinfo_map[rowx] = r
+ if 0 and r.xf_index > -1:
+ fprintf(self.logfile,
+ "**ROW %d %d %d\n",
+ self.number, rowx, r.xf_index)
+ if blah_rows:
+ print('ROW_B2', rowx, bits1, file=self.logfile)
+ r.dump(self.logfile,
+ header="--- sh #%d, rowx=%d ---" % (self.number, rowx))
+ elif rc == XL_COLWIDTH: # BIFF2 only
+ if not fmt_info: continue
+ first_colx, last_colx, width\
+ = local_unpack("= 30) + 1
+ nchars_expected = unpack("<" + "BH"[lenlen - 1], data[:lenlen])[0]
+ offset = lenlen
+ if bv < 80:
+ enc = bk.encoding or bk.derive_encoding()
+ nchars_found = 0
+ result = UNICODE_LITERAL("")
+ while 1:
+ if bv >= 80:
+ flag = BYTES_ORD(data[offset]) & 1
+ enc = ("latin_1", "utf_16_le")[flag]
+ offset += 1
+ chunk = unicode(data[offset:], enc)
+ result += chunk
+ nchars_found += len(chunk)
+ if nchars_found == nchars_expected:
+ return result
+ if nchars_found > nchars_expected:
+ msg = ("STRING/CONTINUE: expected %d chars, found %d"
+ % (nchars_expected, nchars_found))
+ raise XLRDError(msg)
+ rc, _unused_len, data = bk.get_record_parts()
+ if rc != XL_CONTINUE:
+ raise XLRDError(
+ "Expected CONTINUE record; found record-type 0x%04X" % rc)
+ offset = 0
+
+ def update_cooked_mag_factors(self):
+ # Cached values are used ONLY for the non-active view mode.
+ # When the user switches to the non-active view mode,
+ # if the cached value for that mode is not valid,
+ # Excel pops up a window which says:
+ # "The number must be between 10 and 400. Try again by entering a number in this range."
+ # When the user hits OK, it drops into the non-active view mode
+ # but uses the magn from the active mode.
+ # NOTE: definition of "valid" depends on mode ... see below
+ blah = DEBUG or self.verbosity > 0
+ if self.show_in_page_break_preview:
+ if self.scl_mag_factor is None: # no SCL record
+ self.cooked_page_break_preview_mag_factor = 100 # Yes, 100, not 60, NOT a typo
+ else:
+ self.cooked_page_break_preview_mag_factor = self.scl_mag_factor
+ zoom = self.cached_normal_view_mag_factor
+ if not (10 <= zoom <=400):
+ if blah:
+ print(
+ "WARNING *** WINDOW2 rcd sheet %d: Bad cached_normal_view_mag_factor: %d"
+ % (self.number, self.cached_normal_view_mag_factor),
+ file=self.logfile,
+ )
+ zoom = self.cooked_page_break_preview_mag_factor
+ self.cooked_normal_view_mag_factor = zoom
+ else:
+ # normal view mode
+ if self.scl_mag_factor is None: # no SCL record
+ self.cooked_normal_view_mag_factor = 100
+ else:
+ self.cooked_normal_view_mag_factor = self.scl_mag_factor
+ zoom = self.cached_page_break_preview_mag_factor
+ if not zoom:
+ # VALID, defaults to 60
+ zoom = 60
+ elif not (10 <= zoom <= 400):
+ if blah:
+ print(
+ "WARNING *** WINDOW2 rcd sheet %r: Bad cached_page_break_preview_mag_factor: %r"
+ % (self.number, self.cached_page_break_preview_mag_factor),
+ file=self.logfile,
+ )
+ zoom = self.cooked_normal_view_mag_factor
+ self.cooked_page_break_preview_mag_factor = zoom
+
+ def fixed_BIFF2_xfindex(self, cell_attr, rowx, colx, true_xfx=None):
+ DEBUG = 0
+ blah = DEBUG or self.verbosity >= 2
+ if self.biff_version == 21:
+ if self.book.xf_list:
+ if true_xfx is not None:
+ xfx = true_xfx
+ else:
+ xfx = BYTES_ORD(cell_attr[0]) & 0x3F
+ if xfx == 0x3F:
+ if self._ixfe is None:
+ raise XLRDError("BIFF2 cell record has XF index 63 but no preceding IXFE record.")
+ xfx = self._ixfe
+ # OOo docs are capable of interpretation that each
+ # cell record is preceded immediately by its own IXFE record.
+ # Empirical evidence is that (sensibly) an IXFE record applies to all
+ # following cell records until another IXFE comes along.
+ return xfx
+ # Have either Excel 2.0, or broken 2.1 w/o XF records -- same effect.
+ self.biff_version = self.book.biff_version = 20
+ #### check that XF slot in cell_attr is zero
+ xfx_slot = BYTES_ORD(cell_attr[0]) & 0x3F
+ assert xfx_slot == 0
+ xfx = self._cell_attr_to_xfx.get(cell_attr)
+ if xfx is not None:
+ return xfx
+ if blah:
+ fprintf(self.logfile, "New cell_attr %r at (%r, %r)\n", cell_attr, rowx, colx)
+ if not self.book.xf_list:
+ for xfx in xrange(16):
+ self.insert_new_BIFF20_xf(cell_attr=b"\x40\x00\x00", style=xfx < 15)
+ xfx = self.insert_new_BIFF20_xf(cell_attr=cell_attr)
+ return xfx
+
+ def insert_new_BIFF20_xf(self, cell_attr, style=0):
+ DEBUG = 0
+ blah = DEBUG or self.verbosity >= 2
+ book = self.book
+ xfx = len(book.xf_list)
+ xf = self.fake_XF_from_BIFF20_cell_attr(cell_attr, style)
+ xf.xf_index = xfx
+ book.xf_list.append(xf)
+ if blah:
+ xf.dump(self.logfile, header="=== Faked XF %d ===" % xfx, footer="======")
+ if xf.format_key not in book.format_map:
+ if 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)
+ fmt = Format(xf.format_key, FUN, UNICODE_LITERAL("General"))
+ book.format_map[xf.format_key] = fmt
+ book.format_list.append(fmt)
+ 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 = book.format_map[xf.format_key]
+ cellty = cellty_from_fmtty[fmt.type]
+ self._xf_index_to_xl_type_map[xf.xf_index] = cellty
+ self._cell_attr_to_xfx[cell_attr] = xfx
+ return xfx
+
+ def fake_XF_from_BIFF20_cell_attr(self, cell_attr, style=0):
+ from .formatting import XF, XFAlignment, XFBorder, XFBackground, XFProtection
+ 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()
+ (prot_bits, font_and_format, halign_etc) = unpack('> 6
+ upkbits(xf.protection, prot_bits, (
+ (6, 0x40, 'cell_locked'),
+ (7, 0x80, 'formula_hidden'),
+ ))
+ xf.alignment.hor_align = halign_etc & 0x07
+ 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 = (0x0FFF, 0)[style]
+ xf.alignment.vert_align = 2 # bottom
+ xf.alignment.rotation = 0
+ attr_stems = [
+ 'format',
+ 'font',
+ 'alignment',
+ 'border',
+ 'background',
+ 'protection',
+ ]
+ for attr_stem in attr_stems:
+ attr = "_" + attr_stem + "_flag"
+ setattr(xf, attr, 1)
+ return xf
+
+ def req_fmt_info(self):
+ if not self.formatting_info:
+ raise XLRDError("Feature requires open_workbook(..., formatting_info=True)")
+
+ def computed_column_width(self, colx):
+ """
+ Determine column display width.
+
+ :param colx:
+ Index of the queried column, range 0 to 255.
+ Note that it is possible to find out the width that will be used to
+ display columns with no cell information e.g. column IV (colx=255).
+
+ :return:
+ The column width that will be used for displaying
+ the given column by Excel, in units of 1/256th of the width of a
+ standard character (the digit zero in the first font).
+
+ .. versionadded:: 0.6.1
+ """
+ self.req_fmt_info()
+ if self.biff_version >= 80:
+ colinfo = self.colinfo_map.get(colx, None)
+ if colinfo is not None:
+ return colinfo.width
+ if self.standardwidth is not None:
+ return self.standardwidth
+ elif self.biff_version >= 40:
+ if self.gcw[colx]:
+ if self.standardwidth is not None:
+ return self.standardwidth
+ else:
+ colinfo = self.colinfo_map.get(colx, None)
+ if colinfo is not None:
+ return colinfo.width
+ elif self.biff_version == 30:
+ colinfo = self.colinfo_map.get(colx, None)
+ if colinfo is not None:
+ return colinfo.width
+ # All roads lead to Rome and the DEFCOLWIDTH ...
+ if self.defcolwidth is not None:
+ return self.defcolwidth * 256
+ return 8 * 256 # 8 is what Excel puts in a DEFCOLWIDTH record
+
+ def handle_hlink(self, data):
+ # DEBUG = 1
+ if DEBUG: print("\n=== hyperlink ===", file=self.logfile)
+ record_size = len(data)
+ h = Hyperlink()
+ h.frowx, h.lrowx, h.fcolx, h.lcolx, guid0, dummy, options = unpack(' 0:
+ fprintf(
+ self.logfile,
+ "*** WARNING: hyperlink at R%dC%d has %d extra data bytes: %s\n",
+ h.frowx + 1,
+ h.fcolx + 1,
+ extra_nbytes,
+ REPR(data[-extra_nbytes:]),
+ )
+ # Seen: b"\x00\x00" also b"A\x00", b"V\x00"
+ elif extra_nbytes < 0:
+ raise XLRDError("Bug or corrupt file, send copy of input file for debugging")
+
+ self.hyperlink_list.append(h)
+ for rowx in xrange(h.frowx, h.lrowx+1):
+ for colx in xrange(h.fcolx, h.lcolx+1):
+ self.hyperlink_map[rowx, colx] = h
+
+ def handle_quicktip(self, data):
+ rcx, frowx, lrowx, fcolx, lcolx = unpack('<5H', data[:10])
+ assert rcx == XL_QUICKTIP
+ assert self.hyperlink_list
+ h = self.hyperlink_list[-1]
+ assert (frowx, lrowx, fcolx, lcolx) == (h.frowx, h.lrowx, h.fcolx, h.lcolx)
+ assert data[-2:] == b'\x00\x00'
+ h.quicktip = unicode(data[10:-2], 'utf_16_le')
+
+ def handle_msodrawingetc(self, recid, data_len, data):
+ if not OBJ_MSO_DEBUG:
+ return
+ DEBUG = 1
+ if self.biff_version < 80:
+ return
+ o = MSODrawing()
+ pos = 0
+ while pos < data_len:
+ tmp, fbt, cb = unpack('> 4) & 0xFFF
+ if ver == 0xF:
+ ndb = 0 # container
+ else:
+ ndb = cb
+ if DEBUG:
+ hex_char_dump(data, pos, ndb + 8, base=0, fout=self.logfile)
+ fprintf(self.logfile,
+ "fbt:0x%04X inst:%d ver:0x%X cb:%d (0x%04X)\n",
+ fbt, inst, ver, cb, cb)
+ if fbt == 0xF010: # Client Anchor
+ assert ndb == 18
+ (o.anchor_unk,
+ o.anchor_colx_lo, o.anchor_rowx_lo,
+ o.anchor_colx_hi, o.anchor_rowx_hi) = unpack(' 0:
+ rc2, data2_len, data2 = self.book.get_record_parts()
+ assert rc2 == XL_NOTE
+ dummy_rowx, nb = unpack('> 1) & 1
+ o.row_hidden = (option_flags >> 7) & 1
+ o.col_hidden = (option_flags >> 8) & 1
+ # XL97 dev kit book says NULL [sic] bytes padding between string count and string data
+ # to ensure that string is word-aligned. Appears to be nonsense.
+ o.author, endpos = unpack_unicode_update_pos(data, 8, lenlen=2)
+ # There is a random/undefined byte after the author string (not counted in the
+ # string length).
+ # Issue 4 on github: Google Spreadsheet doesn't write the undefined byte.
+ assert (data_len - endpos) in (0, 1)
+ if OBJ_MSO_DEBUG:
+ o.dump(self.logfile, header="=== Note ===", footer= " ")
+ txo = txos.get(o._object_id)
+ if txo:
+ o.text = txo.text
+ o.rich_text_runlist = txo.rich_text_runlist
+ self.cell_note_map[o.rowx, o.colx] = o
+
+ def handle_txo(self, data):
+ if self.biff_version < 80:
+ return
+ o = MSTxo()
+ fmt = '2}:<{}>".format(self.number, self.name)
+
+
+class MSODrawing(BaseObject):
+ pass
+
+
+class MSObj(BaseObject):
+ pass
+
+
+class MSTxo(BaseObject):
+ pass
+
+
+class Note(BaseObject):
+ """
+ Represents a user "comment" or "note".
+ Note objects are accessible through :attr:`Sheet.cell_note_map`.
+
+ .. versionadded:: 0.7.2
+ """
+
+ #: Author of note
+ author = UNICODE_LITERAL('')
+
+ #: ``True`` if the containing column is hidden
+ col_hidden = 0
+
+ #: Column index
+ colx = 0
+
+ #: List of ``(offset_in_string, font_index)`` tuples.
+ #: Unlike :attr:`Sheet.rich_text_runlist_map`, the first offset should
+ #: always be 0.
+ rich_text_runlist = None
+
+ #: True if the containing row is hidden
+ row_hidden = 0
+
+ #: Row index
+ rowx = 0
+
+ #: True if note is always shown
+ show = 0
+
+ #: Text of the note
+ text = UNICODE_LITERAL('')
+
+
+class Hyperlink(BaseObject):
+ """
+ Contains the attributes of a hyperlink.
+ Hyperlink objects are accessible through :attr:`Sheet.hyperlink_list`
+ and :attr:`Sheet.hyperlink_map`.
+
+ .. versionadded:: 0.7.2
+ """
+
+ #: Index of first row
+ frowx = None
+
+ #: Index of last row
+ lrowx = None
+
+ #: Index of first column
+ fcolx = None
+
+ #: Index of last column
+ lcolx = None
+
+ #: Type of hyperlink. Unicode string, one of 'url', 'unc',
+ #: 'local file', 'workbook', 'unknown'
+ type = None
+
+ #: The URL or file-path, depending in the type. Unicode string, except
+ #: in the rare case of a local but non-existent file with non-ASCII
+ #: characters in the name, in which case only the "8.3" filename is
+ #: available, as a :class:`bytes` (3.x) or :class:`str` (2.x) string,
+ #: *with unknown encoding.*
+ url_or_path = None
+
+ #: Description.
+ #: This is displayed in the cell,
+ #: and should be identical to the cell value. Unicode string, or ``None``.
+ #: It seems impossible NOT to have a description created by the Excel UI.
+ desc = None
+
+ #: Target frame. Unicode string.
+ #:
+ #: .. note::
+ #: No cases of this have been seen in the wild.
+ #: It seems impossible to create one in the Excel UI.
+ target = None
+
+ #: The piece after the "#" in
+ #: "http://docs.python.org/library#struct_module", or the ``Sheet1!A1:Z99``
+ #: part when type is "workbook".
+ textmark = None
+
+ #: The text of the "quick tip" displayed when the cursor
+ #: hovers over the hyperlink.
+ quicktip = None
+
+# === helpers ===
+
+def unpack_RK(rk_str):
+ flags = BYTES_ORD(rk_str[0])
+ if flags & 2:
+ # There's a SIGNED 30-bit integer in there!
+ i, = unpack('>= 2 # div by 4 to drop the 2 flag bits
+ if flags & 1:
+ return i / 100.0
+ return float(i)
+ else:
+ # It's the most significant 30 bits of an IEEE 754 64-bit FP number
+ d, = unpack('
+
+
Type symbol
+
Type number
+
Python value
+
+
+
XL_CELL_EMPTY
+
0
+
empty string ''
+
+
+
XL_CELL_TEXT
+
1
+
a Unicode string
+
+
+
XL_CELL_NUMBER
+
2
+
float
+
+
+
XL_CELL_DATE
+
3
+
float
+
+
+
XL_CELL_BOOLEAN
+
4
+
int; 1 means TRUE, 0 means FALSE
+
+
+
XL_CELL_ERROR
+
5
+
int representing internal Excel codes; for a text representation,
+ refer to the supplied dictionary error_text_from_code
+
+
+
XL_CELL_BLANK
+
6
+
empty string ''. Note: this type will appear only when
+ open_workbook(..., formatting_info=True) is used.
+
+
+ """
+
+ __slots__ = ['ctype', 'value', 'xf_index']
+
+ def __init__(self, ctype, value, xf_index=None):
+ self.ctype = ctype
+ self.value = value
+ self.xf_index = xf_index
+
+ def __repr__(self):
+ if self.xf_index is None:
+ return "%s:%r" % (ctype_text[self.ctype], self.value)
+ else:
+ return "%s:%r (XF:%r)" % (ctype_text[self.ctype], self.value, self.xf_index)
+
+empty_cell = Cell(XL_CELL_EMPTY, UNICODE_LITERAL(''))
+
+##### =============== Colinfo and Rowinfo ============================== #####
+
+
+class Colinfo(BaseObject):
+ """
+ Width and default formatting information that applies to one or
+ more columns in a sheet. Derived from ``COLINFO`` records.
+
+ Here is the default hierarchy for width, according to the OOo docs:
+
+ In BIFF3, if a ``COLINFO`` record is missing for a column,
+ the width specified in the record ``DEFCOLWIDTH`` is used instead.
+
+ In BIFF4-BIFF7, the width set in this ``COLINFO`` record is only used,
+ if the corresponding bit for this column is cleared in the ``GCW``
+ record, otherwise the column width set in the ``DEFCOLWIDTH`` record
+ is used (the ``STANDARDWIDTH`` record is always ignored in this case [#f1]_).
+
+ In BIFF8, if a ``COLINFO`` record is missing for a column,
+ the width specified in the record ``STANDARDWIDTH`` is used.
+ If this ``STANDARDWIDTH`` record is also missing,
+ the column width of the record ``DEFCOLWIDTH`` is used instead.
+
+ .. [#f1] The docs on the ``GCW`` record say this:
+
+ If a bit is set, the corresponding column uses the width set in the
+ ``STANDARDWIDTH`` record. If a bit is cleared, the corresponding column
+ uses the width set in the ``COLINFO`` record for this column.
+
+ If a bit is set, and the worksheet does not contain the ``STANDARDWIDTH``
+ record, or if the bit is cleared, and the worksheet does not contain the
+ ``COLINFO`` record, the ``DEFCOLWIDTH`` record of the worksheet will be
+ used instead.
+
+ xlrd goes with the GCW version of the story.
+ Reference to the source may be useful: see
+ :meth:`Sheet.computed_column_width`.
+
+ .. versionadded:: 0.6.1
+ """
+
+ #: Width of the column in 1/256 of the width of the zero character,
+ #: using default font (first ``FONT`` record in the file).
+ width = 0
+
+ #: XF index to be used for formatting empty cells.
+ xf_index = -1
+
+ #: 1 = column is hidden
+ hidden = 0
+
+ #: Value of a 1-bit flag whose purpose is unknown
+ #: but is often seen set to 1
+ bit1_flag = 0
+
+ #: Outline level of the column, in ``range(7)``.
+ #: (0 = no outline)
+ outline_level = 0
+
+ #: 1 = column is collapsed
+ collapsed = 0
+
+_USE_SLOTS = 1
+
+
+class Rowinfo(BaseObject):
+ """
+ Height and default formatting information that applies to a row in a sheet.
+ Derived from ``ROW`` records.
+
+ .. versionadded:: 0.6.1
+ """
+
+ if _USE_SLOTS:
+ __slots__ = (
+ "height",
+ "has_default_height",
+ "outline_level",
+ "outline_group_starts_ends",
+ "hidden",
+ "height_mismatch",
+ "has_default_xf_index",
+ "xf_index",
+ "additional_space_above",
+ "additional_space_below",
+ )
+
+ def __init__(self):
+ #: Height of the row, in twips. One twip == 1/20 of a point.
+ self.height = None
+
+ #: 0 = Row has custom height; 1 = Row has default height.
+ self.has_default_height = None
+
+ #: Outline level of the row (0 to 7)
+ self.outline_level = None
+
+ #: 1 = Outline group starts or ends here (depending on where the
+ #: outline buttons are located, see ``WSBOOL`` record, which is not
+ #: parsed by xlrd), *and* is collapsed.
+ self.outline_group_starts_ends = None
+
+ #: 1 = Row is hidden (manually, or by a filter or outline group)
+ self.hidden = None
+
+ #: 1 = Row height and default font height do not match.
+ self.height_mismatch = None
+
+ #: 1 = the xf_index attribute is usable; 0 = ignore it.
+ self.has_default_xf_index = None
+
+ #: Index to default :class:`~xlrd.formatting.XF` record for empty cells
+ #: in this row. Don't use this if ``has_default_xf_index == 0``.
+ self.xf_index = None
+
+ #: This flag is set if the upper border of at least one cell in this
+ #: row or if the lower border of at least one cell in the row above is
+ #: formatted with a thick line style. Thin and medium line styles are
+ #: not taken into account.
+ self.additional_space_above = None
+
+ #: This flag is set if the lower border of at least one cell in this row
+ #: or if the upper border of at least one cell in the row below is
+ #: formatted with a medium or thick line style. Thin line styles are not
+ #: taken into account.
+ self.additional_space_below = None
+
+ def __getstate__(self):
+ return (
+ self.height,
+ self.has_default_height,
+ self.outline_level,
+ self.outline_group_starts_ends,
+ self.hidden,
+ self.height_mismatch,
+ self.has_default_xf_index,
+ self.xf_index,
+ self.additional_space_above,
+ self.additional_space_below,
+ )
+
+ def __setstate__(self, state):
+ (
+ self.height,
+ self.has_default_height,
+ self.outline_level,
+ self.outline_group_starts_ends,
+ self.hidden,
+ self.height_mismatch,
+ self.has_default_xf_index,
+ self.xf_index,
+ self.additional_space_above,
+ self.additional_space_below,
+ ) = state
diff --git a/.venv/lib/python3.9/site-packages/xlrd/timemachine.py b/.venv/lib/python3.9/site-packages/xlrd/timemachine.py
new file mode 100644
index 00000000..a519299e
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlrd/timemachine.py
@@ -0,0 +1,53 @@
+##
+#
Copyright (c) 2006-2012 Stephen John Machin, Lingfo Pty Ltd
+#
This module is part of the xlrd package, which is released under a BSD-style licence.
+##
+
+# timemachine.py -- adaptation for single codebase.
+# Currently supported: 2.6 to 2.7, 3.2+
+# usage: from timemachine import *
+
+from __future__ import print_function
+
+import sys
+
+python_version = sys.version_info[:2] # e.g. version 2.6 -> (2, 6)
+
+if python_version >= (3, 0):
+ # Python 3
+ BYTES_LITERAL = lambda x: x.encode('latin1')
+ UNICODE_LITERAL = lambda x: x
+ BYTES_ORD = lambda byte: byte
+ from io import BytesIO as BYTES_IO
+ def fprintf(f, fmt, *vargs):
+ fmt = fmt.replace("%r", "%a")
+ if fmt.endswith('\n'):
+ print(fmt[:-1] % vargs, file=f)
+ else:
+ print(fmt % vargs, end=' ', file=f)
+ EXCEL_TEXT_TYPES = (str, bytes, bytearray) # xlwt: isinstance(obj, EXCEL_TEXT_TYPES)
+ REPR = ascii
+ xrange = range
+ unicode = lambda b, enc: b.decode(enc)
+ ensure_unicode = lambda s: s
+ unichr = chr
+else:
+ # Python 2
+ BYTES_LITERAL = lambda x: x
+ UNICODE_LITERAL = lambda x: x.decode('latin1')
+ BYTES_ORD = ord
+ from cStringIO import StringIO as BYTES_IO
+ def fprintf(f, fmt, *vargs):
+ if fmt.endswith('\n'):
+ print(fmt[:-1] % vargs, file=f)
+ else:
+ print(fmt % vargs, end=' ', file=f)
+ try:
+ EXCEL_TEXT_TYPES = basestring # xlwt: isinstance(obj, EXCEL_TEXT_TYPES)
+ except NameError:
+ EXCEL_TEXT_TYPES = (str, unicode)
+ REPR = repr
+ xrange = xrange
+ # following used only to overcome 2.x ElementTree gimmick which
+ # returns text as `str` if it's ascii, otherwise `unicode`
+ ensure_unicode = unicode # used only in xlsx.py
diff --git a/.venv/lib/python3.9/site-packages/xlrd/xldate.py b/.venv/lib/python3.9/site-packages/xlrd/xldate.py
new file mode 100644
index 00000000..d84c6508
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlrd/xldate.py
@@ -0,0 +1,248 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2005-2008 Stephen John Machin, Lingfo Pty Ltd
+# This module is part of the xlrd package, which is released under a
+# BSD-style licence.
+# No part of the content of this file was derived from the works of David Giffin.
+"""
+Tools for working with dates and times in Excel files.
+
+The conversion from ``days`` to ``(year, month, day)`` starts with
+an integral "julian day number" aka JDN.
+FWIW:
+
+- JDN 0 corresponds to noon on Monday November 24 in Gregorian year -4713.
+
+More importantly:
+
+- Noon on Gregorian 1900-03-01 (day 61 in the 1900-based system) is JDN 2415080.0
+- Noon on Gregorian 1904-01-02 (day 1 in the 1904-based system) is JDN 2416482.0
+
+"""
+import datetime
+
+_JDN_delta = (2415080 - 61, 2416482 - 1)
+assert _JDN_delta[1] - _JDN_delta[0] == 1462
+
+# Pre-calculate the datetime epochs for efficiency.
+epoch_1904 = datetime.datetime(1904, 1, 1)
+epoch_1900 = datetime.datetime(1899, 12, 31)
+epoch_1900_minus_1 = datetime.datetime(1899, 12, 30)
+
+# This is equivalent to 10000-01-01:
+_XLDAYS_TOO_LARGE = (2958466, 2958466 - 1462)
+
+
+class XLDateError(ValueError):
+ "A base class for all datetime-related errors."
+
+
+class XLDateNegative(XLDateError):
+ "``xldate < 0.00``"
+
+
+class XLDateAmbiguous(XLDateError):
+ "The 1900 leap-year problem ``(datemode == 0 and 1.0 <= xldate < 61.0)``"
+
+
+class XLDateTooLarge(XLDateError):
+ "Gregorian year 10000 or later"
+
+
+class XLDateBadDatemode(XLDateError):
+ "``datemode`` arg is neither 0 nor 1"
+
+
+class XLDateBadTuple(XLDateError):
+ pass
+
+
+def xldate_as_tuple(xldate, datemode):
+ """
+ Convert an Excel number (presumed to represent a date, a datetime or a time) into
+ a tuple suitable for feeding to datetime or mx.DateTime constructors.
+
+ :param xldate: The Excel number
+ :param datemode: 0: 1900-based, 1: 1904-based.
+ :raises xlrd.xldate.XLDateNegative:
+ :raises xlrd.xldate.XLDateAmbiguous:
+
+ :raises xlrd.xldate.XLDateTooLarge:
+ :raises xlrd.xldate.XLDateBadDatemode:
+ :raises xlrd.xldate.XLDateError:
+ :returns: Gregorian ``(year, month, day, hour, minute, nearest_second)``.
+
+ .. warning::
+
+ When using this function to interpret the contents of a workbook, you
+ should pass in the :attr:`~xlrd.book.Book.datemode`
+ attribute of that workbook. Whether the workbook has ever been anywhere
+ near a Macintosh is irrelevant.
+
+ .. admonition:: Special case
+
+ If ``0.0 <= xldate < 1.0``, it is assumed to represent a time;
+ ``(0, 0, 0, hour, minute, second)`` will be returned.
+
+ .. note::
+
+ ``1904-01-01`` is not regarded as a valid date in the ``datemode==1``
+ system; its "serial number" is zero.
+ """
+ if datemode not in (0, 1):
+ raise XLDateBadDatemode(datemode)
+ if xldate == 0.00:
+ return (0, 0, 0, 0, 0, 0)
+ if xldate < 0.00:
+ raise XLDateNegative(xldate)
+ xldays = int(xldate)
+ frac = xldate - xldays
+ seconds = int(round(frac * 86400.0))
+ assert 0 <= seconds <= 86400
+ if seconds == 86400:
+ hour = minute = second = 0
+ xldays += 1
+ else:
+ # second = seconds % 60; minutes = seconds // 60
+ minutes, second = divmod(seconds, 60)
+ # minute = minutes % 60; hour = minutes // 60
+ hour, minute = divmod(minutes, 60)
+ if xldays >= _XLDAYS_TOO_LARGE[datemode]:
+ raise XLDateTooLarge(xldate)
+
+ if xldays == 0:
+ return (0, 0, 0, hour, minute, second)
+
+ if xldays < 61 and datemode == 0:
+ raise XLDateAmbiguous(xldate)
+
+ jdn = xldays + _JDN_delta[datemode]
+ yreg = ((((jdn * 4 + 274277) // 146097) * 3 // 4) + jdn + 1363) * 4 + 3
+ mp = ((yreg % 1461) // 4) * 535 + 333
+ d = ((mp % 16384) // 535) + 1
+ # mp /= 16384
+ mp >>= 14
+ if mp >= 10:
+ return ((yreg // 1461) - 4715, mp - 9, d, hour, minute, second)
+ else:
+ return ((yreg // 1461) - 4716, mp + 3, d, hour, minute, second)
+
+
+def xldate_as_datetime(xldate, datemode):
+ """
+ Convert an Excel date/time number into a :class:`datetime.datetime` object.
+
+ :param xldate: The Excel number
+ :param datemode: 0: 1900-based, 1: 1904-based.
+
+ :returns: A :class:`datetime.datetime` object.
+ """
+
+ # Set the epoch based on the 1900/1904 datemode.
+ if datemode:
+ epoch = epoch_1904
+ else:
+ if xldate < 60:
+ epoch = epoch_1900
+ else:
+ # Workaround Excel 1900 leap year bug by adjusting the epoch.
+ epoch = epoch_1900_minus_1
+
+ # The integer part of the Excel date stores the number of days since
+ # the epoch and the fractional part stores the percentage of the day.
+ days = int(xldate)
+ fraction = xldate - days
+
+ # Get the the integer and decimal seconds in Excel's millisecond resolution.
+ seconds = int(round(fraction * 86400000.0))
+ seconds, milliseconds = divmod(seconds, 1000)
+
+ return epoch + datetime.timedelta(days, seconds, 0, milliseconds)
+
+
+# === conversions from date/time to xl numbers
+
+def _leap(y):
+ if y % 4: return 0
+ if y % 100: return 1
+ if y % 400: return 0
+ return 1
+
+_days_in_month = (None, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
+
+
+def xldate_from_date_tuple(date_tuple, datemode):
+ """
+ Convert a date tuple (year, month, day) to an Excel date.
+
+ :param year: Gregorian year.
+ :param month: ``1 <= month <= 12``
+ :param day: ``1 <= day <= last day of that (year, month)``
+ :param datemode: 0: 1900-based, 1: 1904-based.
+ :raises xlrd.xldate.XLDateAmbiguous:
+ :raises xlrd.xldate.XLDateBadDatemode:
+ :raises xlrd.xldate.XLDateBadTuple:
+ ``(year, month, day)`` is too early/late or has invalid component(s)
+ :raises xlrd.xldate.XLDateError:
+ """
+ year, month, day = date_tuple
+
+ if datemode not in (0, 1):
+ raise XLDateBadDatemode(datemode)
+
+ if year == 0 and month == 0 and day == 0:
+ return 0.00
+
+ if not (1900 <= year <= 9999):
+ raise XLDateBadTuple("Invalid year: %r" % ((year, month, day),))
+ if not (1 <= month <= 12):
+ raise XLDateBadTuple("Invalid month: %r" % ((year, month, day),))
+ if (day < 1 or
+ (day > _days_in_month[month] and not(day == 29 and month == 2 and _leap(year)))):
+ raise XLDateBadTuple("Invalid day: %r" % ((year, month, day),))
+
+ Yp = year + 4716
+ M = month
+ if M <= 2:
+ Yp = Yp - 1
+ Mp = M + 9
+ else:
+ Mp = M - 3
+ jdn = (1461 * Yp // 4) + ((979 * Mp + 16) // 32) + \
+ day - 1364 - (((Yp + 184) // 100) * 3 // 4)
+ xldays = jdn - _JDN_delta[datemode]
+ if xldays <= 0:
+ raise XLDateBadTuple("Invalid (year, month, day): %r" % ((year, month, day),))
+ if xldays < 61 and datemode == 0:
+ raise XLDateAmbiguous("Before 1900-03-01: %r" % ((year, month, day),))
+ return float(xldays)
+
+
+def xldate_from_time_tuple(time_tuple):
+ """
+ Convert a time tuple ``(hour, minute, second)`` to an Excel "date" value
+ (fraction of a day).
+
+ :param hour: ``0 <= hour < 24``
+ :param minute: ``0 <= minute < 60``
+ :param second: ``0 <= second < 60``
+ :raises xlrd.xldate.XLDateBadTuple: Out-of-range hour, minute, or second
+ """
+ hour, minute, second = time_tuple
+ if 0 <= hour < 24 and 0 <= minute < 60 and 0 <= second < 60:
+ return ((second / 60.0 + minute) / 60.0 + hour) / 24.0
+ raise XLDateBadTuple("Invalid (hour, minute, second): %r" % ((hour, minute, second),))
+
+
+def xldate_from_datetime_tuple(datetime_tuple, datemode):
+ """
+ Convert a datetime tuple ``(year, month, day, hour, minute, second)`` to an
+ Excel date value.
+ For more details, refer to other xldate_from_*_tuple functions.
+
+ :param datetime_tuple: ``(year, month, day, hour, minute, second)``
+ :param datemode: 0: 1900-based, 1: 1904-based.
+ """
+ return (
+ xldate_from_date_tuple(datetime_tuple[:3], datemode) +
+ xldate_from_time_tuple(datetime_tuple[3:])
+ )
diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/DESCRIPTION.rst b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/DESCRIPTION.rst
new file mode 100644
index 00000000..bc339693
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/DESCRIPTION.rst
@@ -0,0 +1,85 @@
+|Travis|_ |Coveralls|_ |Docs|_ |PyPI|_
+
+.. |Travis| image:: https://api.travis-ci.org/python-excel/xlwt.svg?branch=master
+.. _Travis: https://travis-ci.org/python-excel/xlwt
+
+.. |Coveralls| image:: https://coveralls.io/repos/python-excel/xlwt/badge.svg?branch=master
+.. _Coveralls: https://coveralls.io/r/python-excel/xlwt?branch=master
+
+.. |Docs| image:: https://readthedocs.org/projects/xlwt/badge/?version=latest
+.. _Docs: https://xlwt.readthedocs.org/en/latest/
+
+.. |PyPI| image:: https://badge.fury.io/py/xlwt.svg
+.. _PyPI: https://badge.fury.io/py/xlwt
+
+xlwt
+====
+
+This is a library for developers to use to generate
+spreadsheet files compatible with Microsoft Excel versions 95 to 2003.
+
+The package itself is pure Python with no dependencies on modules or packages
+outside the standard Python distribution.
+
+Please read this before using this package:
+https://groups.google.com/d/msg/python-excel/P6TjJgFVjMI/g8d0eWxTBQAJ
+
+Installation
+============
+
+Do the following in your virtualenv::
+
+ pip install xlwt
+
+Quick start
+===========
+
+.. code-block:: python
+
+ import xlwt
+ from datetime import datetime
+
+ style0 = xlwt.easyxf('font: name Times New Roman, color-index red, bold on',
+ num_format_str='#,##0.00')
+ style1 = xlwt.easyxf(num_format_str='D-MMM-YY')
+
+ wb = xlwt.Workbook()
+ ws = wb.add_sheet('A Test Sheet')
+
+ ws.write(0, 0, 1234.56, style0)
+ ws.write(1, 0, datetime.now(), style1)
+ ws.write(2, 0, 1)
+ ws.write(2, 1, 1)
+ ws.write(2, 2, xlwt.Formula("A3+B3"))
+
+ wb.save('example.xls')
+
+
+Documentation
+=============
+
+Documentation can be found in the ``docs`` directory of the xlwt package.
+If these aren't sufficient, please consult the code in the
+examples directory and the source code itself.
+
+The latest documentation can also be found at:
+https://xlwt.readthedocs.org/en/latest/
+
+Problems?
+=========
+Try the following in this order:
+
+- Read the source
+
+- Ask a question on https://groups.google.com/group/python-excel/
+
+Acknowledgements
+================
+
+xlwt is a fork of the pyExcelerator package, which was developed by
+Roman V. Kiseliov. This product includes software developed by
+Roman V. Kiseliov .
+
+xlwt uses ANTLR v 2.7.7 to generate its formula compiler.
+
+
diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/METADATA b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/METADATA
new file mode 100644
index 00000000..1960bc95
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/METADATA
@@ -0,0 +1,113 @@
+Metadata-Version: 2.0
+Name: xlwt
+Version: 1.3.0
+Summary: Library to create spreadsheet files compatible with MS Excel 97/2000/XP/2003 XLS files, on any platform, with Python 2.6, 2.7, 3.3+
+Home-page: http://www.python-excel.org/
+Author: John Machin
+Author-email: sjmachin@lexicon.net
+License: BSD
+Download-URL: https://pypi.python.org/pypi/xlwt
+Keywords: xls excel spreadsheet workbook worksheet pyExcelerator
+Platform: Platform Independent
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
+Classifier: Topic :: Database
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+
+|Travis|_ |Coveralls|_ |Docs|_ |PyPI|_
+
+.. |Travis| image:: https://api.travis-ci.org/python-excel/xlwt.svg?branch=master
+.. _Travis: https://travis-ci.org/python-excel/xlwt
+
+.. |Coveralls| image:: https://coveralls.io/repos/python-excel/xlwt/badge.svg?branch=master
+.. _Coveralls: https://coveralls.io/r/python-excel/xlwt?branch=master
+
+.. |Docs| image:: https://readthedocs.org/projects/xlwt/badge/?version=latest
+.. _Docs: https://xlwt.readthedocs.org/en/latest/
+
+.. |PyPI| image:: https://badge.fury.io/py/xlwt.svg
+.. _PyPI: https://badge.fury.io/py/xlwt
+
+xlwt
+====
+
+This is a library for developers to use to generate
+spreadsheet files compatible with Microsoft Excel versions 95 to 2003.
+
+The package itself is pure Python with no dependencies on modules or packages
+outside the standard Python distribution.
+
+Please read this before using this package:
+https://groups.google.com/d/msg/python-excel/P6TjJgFVjMI/g8d0eWxTBQAJ
+
+Installation
+============
+
+Do the following in your virtualenv::
+
+ pip install xlwt
+
+Quick start
+===========
+
+.. code-block:: python
+
+ import xlwt
+ from datetime import datetime
+
+ style0 = xlwt.easyxf('font: name Times New Roman, color-index red, bold on',
+ num_format_str='#,##0.00')
+ style1 = xlwt.easyxf(num_format_str='D-MMM-YY')
+
+ wb = xlwt.Workbook()
+ ws = wb.add_sheet('A Test Sheet')
+
+ ws.write(0, 0, 1234.56, style0)
+ ws.write(1, 0, datetime.now(), style1)
+ ws.write(2, 0, 1)
+ ws.write(2, 1, 1)
+ ws.write(2, 2, xlwt.Formula("A3+B3"))
+
+ wb.save('example.xls')
+
+
+Documentation
+=============
+
+Documentation can be found in the ``docs`` directory of the xlwt package.
+If these aren't sufficient, please consult the code in the
+examples directory and the source code itself.
+
+The latest documentation can also be found at:
+https://xlwt.readthedocs.org/en/latest/
+
+Problems?
+=========
+Try the following in this order:
+
+- Read the source
+
+- Ask a question on https://groups.google.com/group/python-excel/
+
+Acknowledgements
+================
+
+xlwt is a fork of the pyExcelerator package, which was developed by
+Roman V. Kiseliov. This product includes software developed by
+Roman V. Kiseliov .
+
+xlwt uses ANTLR v 2.7.7 to generate its formula compiler.
+
+
diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/RECORD b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/RECORD
new file mode 100644
index 00000000..faa8d39c
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/RECORD
@@ -0,0 +1,47 @@
+xlwt-1.3.0.dist-info/DESCRIPTION.rst,sha256=Wd9ksNg8D2Z8Ly_hTdFNt5MbC0sDd-kgkuHFyoriPjw,2249
+xlwt-1.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+xlwt-1.3.0.dist-info/METADATA,sha256=f90RqO9sD7PFwnWjetPQ-GT36U1mXzm0xYynVzkwhRY,3530
+xlwt-1.3.0.dist-info/RECORD,,
+xlwt-1.3.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+xlwt-1.3.0.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110
+xlwt-1.3.0.dist-info/metadata.json,sha256=bMNWYp1NWNRK9gf0AG8xPg3B8hRK4naNQAafW_Jtpa0,1390
+xlwt-1.3.0.dist-info/top_level.txt,sha256=PoPrMegX_ucpxfEzLnC48zwqCZ5Hg_dd2RJPyetxXeM,5
+xlwt/BIFFRecords.py,sha256=40mRM4FcmyX56EE4hxnKa2g4X-qM8uk2BaeogIiKq6M,97384
+xlwt/Bitmap.py,sha256=kRgc798XDjOQO40y_1l0nAGQ8GY8-p-97wAcmkSTs1g,10930
+xlwt/Cell.py,sha256=Krnmim2i1YMdWHg84QmTqtSq75TYFqsP7uLVunGUZ1Q,8559
+xlwt/Column.py,sha256=ESDuWPhep6ep43QV1v7lki4Swc_U1vKBg8edgQpy3Js,1510
+xlwt/CompoundDoc.py,sha256=24OGRRYNSu7k4ZcfLel3_GuV0Zm4qze1PoPXA6mduKk,9931
+xlwt/ExcelFormula.py,sha256=mXvz4Gzapq9_oMsuOqBe5DeYa9HvcTbFoBFpbgdrg8o,1382
+xlwt/ExcelFormulaLexer.py,sha256=aVlKlztxjs9St00BKa4RZGmg8n_ibvR5jM5oGaKFfn4,4197
+xlwt/ExcelFormulaParser.py,sha256=oYVXnJdDWVE8AyYOptf35zMJCW_GFGtFrLTn8izUI5c,22209
+xlwt/ExcelMagic.py,sha256=Iiie4crRsWMNoKiBXabjpREizmSg4viJvVlwHkmOo6w,28839
+xlwt/Formatting.py,sha256=im51qDuey32heZBUby9yvzm0MzCkDvdScunNYrIPARQ,8542
+xlwt/Row.py,sha256=yPY8Pek6_XvOtWqf-LuLZQlwUwGoY2WiQSU9g50CD4o,11732
+xlwt/Style.py,sha256=yBovNDMAqwnT0zbLGZlkHx28yfnIaWxGxvnQM3CZ2Jg,23716
+xlwt/UnicodeUtils.py,sha256=GKCZs3-0goLp45_UZT-JVIoFQQMmQVeG1xHJvkdHNCs,5033
+xlwt/Utils.py,sha256=wJvREgjXgMPOXjdYhrIaOXOmdd1W4RPAWCMYUSCgU8Q,5150
+xlwt/Workbook.py,sha256=h7y2DfyLeK45HO3_sMuzKp__5bM9Vh_turYu98AWoLk,23632
+xlwt/Worksheet.py,sha256=6BEdViMAiMao9fe-mCvZl0SPfw-oedaQpXIFQxUwew4,47776
+xlwt/__init__.py,sha256=rrcaxLu2cY1M0Qcs1tNzx1__ELofdtunClAMAbGMWkU,298
+xlwt/__pycache__/BIFFRecords.cpython-39.pyc,,
+xlwt/__pycache__/Bitmap.cpython-39.pyc,,
+xlwt/__pycache__/Cell.cpython-39.pyc,,
+xlwt/__pycache__/Column.cpython-39.pyc,,
+xlwt/__pycache__/CompoundDoc.cpython-39.pyc,,
+xlwt/__pycache__/ExcelFormula.cpython-39.pyc,,
+xlwt/__pycache__/ExcelFormulaLexer.cpython-39.pyc,,
+xlwt/__pycache__/ExcelFormulaParser.cpython-39.pyc,,
+xlwt/__pycache__/ExcelMagic.cpython-39.pyc,,
+xlwt/__pycache__/Formatting.cpython-39.pyc,,
+xlwt/__pycache__/Row.cpython-39.pyc,,
+xlwt/__pycache__/Style.cpython-39.pyc,,
+xlwt/__pycache__/UnicodeUtils.cpython-39.pyc,,
+xlwt/__pycache__/Utils.cpython-39.pyc,,
+xlwt/__pycache__/Workbook.cpython-39.pyc,,
+xlwt/__pycache__/Worksheet.cpython-39.pyc,,
+xlwt/__pycache__/__init__.cpython-39.pyc,,
+xlwt/__pycache__/antlr.cpython-39.pyc,,
+xlwt/__pycache__/compat.cpython-39.pyc,,
+xlwt/antlr.py,sha256=WKDx0cGuoefxGmuKEI6QFhQoLha_ayZagDeRrCL_8u0,84153
+xlwt/compat.py,sha256=HDct50mxSl7XfVYPi3PqfS5ub-bcjTvtPuBjZnF8sNo,544
+xlwt/excel-formula.g,sha256=F3vU8gQSdz7-9qiBI-G6ax9RZ7Aa-eq1laMj5zxnn7E,11337
diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/REQUESTED b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/WHEEL
new file mode 100644
index 00000000..8b6dd1b5
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.29.0)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/metadata.json b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/metadata.json
new file mode 100644
index 00000000..9f1162f5
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/metadata.json
@@ -0,0 +1 @@
+{"classifiers": ["Operating System :: OS Independent", "Programming Language :: Python", "License :: OSI Approved :: BSD License", "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Office/Business :: Financial :: Spreadsheet", "Topic :: Database", "Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6"], "download_url": "https://pypi.python.org/pypi/xlwt", "extensions": {"python.details": {"contacts": [{"email": "sjmachin@lexicon.net", "name": "John Machin", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://www.python-excel.org/"}}}, "generator": "bdist_wheel (0.29.0)", "keywords": ["xls", "excel", "spreadsheet", "workbook", "worksheet", "pyExcelerator"], "license": "BSD", "metadata_version": "2.0", "name": "xlwt", "platform": "Platform Independent", "summary": "Library to create spreadsheet files compatible with MS Excel 97/2000/XP/2003 XLS files, on any platform, with Python 2.6, 2.7, 3.3+", "version": "1.3.0"}
\ No newline at end of file
diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/top_level.txt
new file mode 100644
index 00000000..688866a1
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+xlwt
diff --git a/.venv/lib/python3.9/site-packages/xlwt/BIFFRecords.py b/.venv/lib/python3.9/site-packages/xlwt/BIFFRecords.py
new file mode 100644
index 00000000..5b831376
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt/BIFFRecords.py
@@ -0,0 +1,2457 @@
+# -*- coding: cp1252 -*-
+from struct import pack
+from .UnicodeUtils import upack1, upack2, upack2rt
+from .compat import basestring, unicode, unicode_type, xrange, iteritems
+
+class SharedStringTable(object):
+ _SST_ID = 0x00FC
+ _CONTINUE_ID = 0x003C
+
+ def __init__(self, encoding):
+ self.encoding = encoding
+ self._str_indexes = {}
+ self._rt_indexes = {}
+ self._tally = []
+ self._add_calls = 0
+ # Following 3 attrs are used for temporary storage in the
+ # get_biff_record() method and methods called by it. The pseudo-
+ # initialisation here is for documentation purposes only.
+ self._sst_record = None
+ self._continues = None
+ self._current_piece = None
+
+ def add_str(self, s):
+ if self.encoding != 'ascii' and not isinstance(s, unicode_type):
+ s = unicode(s, self.encoding)
+ self._add_calls += 1
+ if s not in self._str_indexes:
+ idx = len(self._str_indexes) + len(self._rt_indexes)
+ self._str_indexes[s] = idx
+ self._tally.append(1)
+ else:
+ idx = self._str_indexes[s]
+ self._tally[idx] += 1
+ return idx
+
+ def add_rt(self, rt):
+ rtList = []
+ for s, xf in rt:
+ if self.encoding != 'ascii' and not isinstance(s, unicode_type):
+ s = unicode(s, self.encoding)
+ rtList.append((s, xf))
+ rt = tuple(rtList)
+ self._add_calls += 1
+ if rt not in self._rt_indexes:
+ idx = len(self._str_indexes) + len(self._rt_indexes)
+ self._rt_indexes[rt] = idx
+ self._tally.append(1)
+ else:
+ idx = self._rt_indexes[rt]
+ self._tally[idx] += 1
+ return idx
+
+ def del_str(self, idx):
+ # This is called when we are replacing the contents of a string cell.
+ # handles both regular and rt strings
+ assert self._tally[idx] > 0
+ self._tally[idx] -= 1
+ self._add_calls -= 1
+
+ def str_index(self, s):
+ return self._str_indexes[s]
+
+ def rt_index(self, rt):
+ return self._rt_indexes[rt]
+
+ def get_biff_record(self):
+ self._sst_record = b''
+ self._continues = [None, None]
+ self._current_piece = pack(' 0x2020: # limit for BIFF7/8
+ chunks = []
+ pos = 0
+ while pos < len(data):
+ chunk_pos = pos + 0x2020
+ chunk = data[pos:chunk_pos]
+ chunks.append(chunk)
+ pos = chunk_pos
+ continues = pack('<2H', self._REC_ID, len(chunks[0])) + chunks[0]
+ for chunk in chunks[1:]:
+ continues += pack('<2H%ds'%len(chunk), 0x003C, len(chunk), chunk)
+ # 0x003C -- CONTINUE record id
+ return continues
+ else:
+ return self.get_rec_header() + data
+
+
+class Biff8BOFRecord(BiffRecord):
+ """
+ Offset Size Contents
+ 0 2 Version, contains 0600H for BIFF8 and BIFF8X
+ 2 2 Type of the following data:
+ 0005H = Workbook globals
+ 0006H = Visual Basic module
+ 0010H = Worksheet
+ 0020H = Chart
+ 0040H = Macro sheet
+ 0100H = Workspace file
+ 4 2 Build identifier
+ 6 2 Build year
+ 8 4 File history flags
+ 12 4 Lowest Excel version that can read all records in this file
+ """
+ _REC_ID = 0x0809
+ # stream types
+ BOOK_GLOBAL = 0x0005
+ VB_MODULE = 0x0006
+ WORKSHEET = 0x0010
+ CHART = 0x0020
+ MACROSHEET = 0x0040
+ WORKSPACE = 0x0100
+
+ def __init__(self, rec_type):
+ version = 0x0600
+ build = 0x0DBB
+ year = 0x07CC
+ file_hist_flags = 0x00
+ ver_can_read = 0x06
+
+ self._rec_data = pack('<4H2I', version, rec_type, build, year, file_hist_flags, ver_can_read)
+
+
+class InteraceHdrRecord(BiffRecord):
+ _REC_ID = 0x00E1
+
+ def __init__(self):
+ self._rec_data = pack('BB', 0xB0, 0x04)
+
+
+class InteraceEndRecord(BiffRecord):
+ _REC_ID = 0x00E2
+
+ def __init__(self):
+ self._rec_data = b''
+
+
+class MMSRecord(BiffRecord):
+ _REC_ID = 0x00C1
+
+ def __init__(self):
+ self._rec_data = pack('> 15
+ c = low_15 | high_15
+ passwd_hash ^= c
+ passwd_hash ^= len(plaintext)
+ passwd_hash ^= 0xCE4B
+ return passwd_hash
+
+ def __init__(self, passwd = ""):
+ self._rec_data = pack('=8
+ 2 var. List of OFFSET structures for all portions. Each OFFSET contains the following data:
+ Offset Size Contents
+ 0 4 Absolute stream position of first string of the portion
+ 4 2 Position of first string of the portion inside of current record,
+ including record header. This counter restarts at zero, if the SST
+ record is continued with a CONTINUE record.
+ 6 2 Not used
+ """
+ _REC_ID = 0x00FF
+
+ def __init__(self, sst_stream_pos, str_placement, portions_len):
+ extsst = {}
+ abs_stream_pos = sst_stream_pos
+ str_counter = 0
+ portion_counter = 0
+ while str_counter < len(str_placement):
+ str_chunk_num, pos_in_chunk = str_placement[str_counter]
+ if str_chunk_num != portion_counter:
+ portion_counter = str_chunk_num
+ abs_stream_pos += portions_len[portion_counter-1]
+ #print hex(abs_stream_pos)
+ str_stream_pos = abs_stream_pos + pos_in_chunk + 4 # header
+ extsst[str_counter] = (pos_in_chunk, str_stream_pos)
+ str_counter += 1
+
+ exsst_str_count_delta = max(8, len(str_placement)*8/0x2000) # maybe smth else?
+ self._rec_data = pack(' last_used_row or first_used_col > last_used_col:
+ # Special case: empty worksheet
+ first_used_row = first_used_col = 0
+ last_used_row = last_used_col = -1
+ self._rec_data = pack('<2L3H',
+ first_used_row, last_used_row + 1,
+ first_used_col, last_used_col + 1,
+ 0x00)
+
+
+class Window2Record(BiffRecord):
+ """
+ Record WINDOW2, BIFF8:
+
+ Offset Size Contents
+ 0 2 Option flags (see below)
+ 2 2 Index to first visible row
+ 4 2 Index to first visible column
+ 6 2 Colour index of grid line colour. Note that in BIFF2-BIFF7 an RGB colour is
+ written instead.
+ 8 2 Not used
+ 10 2 Cached magnification factor in page break preview (in percent); 0 = Default (60%)
+ 12 2 Cached magnification factor in normal view (in percent); 0 = Default (100%)
+ 14 4 Not used
+
+ In BIFF8 this record stores used magnification factors for page break
+ preview and normal view. These values are used to restore the
+ magnification, when the view is changed. The real magnification of the
+ currently active view is stored in the SCL record. The type of the
+ active view is stored in the option flags field (see below).
+
+ 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 describes unfrozen or frozen panes.
+
+ *** This class appends the optional SCL record ***
+
+ Record SCL, BIFF4-BIFF8:
+
+ This record stores the magnification of the active view of the current worksheet.
+ In BIFF8 this can be either the normal view or the page break preview.
+ This is determined in the WINDOW2 record. The SCL record is part of the
+ Sheet View Settings Block.
+
+ Offset Size Contents
+ 0 2 Numerator of the view magnification fraction (num)
+ 2 2 Denumerator [denominator] of the view magnification fraction (den)
+ The magnification is stored as reduced fraction. The magnification results from num/den.
+
+ SJM note: Excel expresses (e.g.) 25% in reduced form i.e. 1/4. Reason unknown. This code
+ writes 25/100, and Excel is happy with that.
+
+ """
+ _REC_ID = 0x023E
+
+ def __init__(self, options, first_visible_row, first_visible_col,
+ grid_colour, preview_magn, normal_magn, scl_magn):
+ self._rec_data = pack('<7HL', options,
+ first_visible_row, first_visible_col,
+ grid_colour,
+ 0x00,
+ preview_magn, normal_magn,
+ 0x00)
+ if scl_magn is not None:
+ self._scl_rec = pack('<4H', 0x00A0, 4, scl_magn, 100)
+ else:
+ self._scl_rec = b''
+
+ def get(self):
+ return self.get_rec_header() + self._rec_data + self._scl_rec
+
+
+class PanesRecord(BiffRecord):
+ """
+ This record stores the position of window panes. It is part of the Sheet
+ View Settings Block. If the sheet does not contain any splits, this
+ record will not occur.
+ A sheet can be split in two different ways, with unfrozen panes or with
+ frozen panes. A flag in the WINDOW2 record specifies, if the panes are
+ frozen, which affects the contents of this record.
+
+ Record PANE, BIFF2-BIFF8:
+ Offset Size Contents
+ 0 2 Position of the vertical split
+ (px, 0 = No vertical split):
+ Unfrozen pane: Width of the left pane(s)
+ (in twips = 1/20 of a point)
+ Frozen pane: Number of visible
+ columns in left pane(s)
+ 2 2 Position of the horizontal split
+ (py, 0 = No horizontal split):
+ Unfrozen pane: Height of the top pane(s)
+ (in twips = 1/20 of a point)
+ Frozen pane: Number of visible
+ rows in top pane(s)
+ 4 2 Index to first visible row
+ in bottom pane(s)
+ 6 2 Index to first visible column
+ in right pane(s)
+ 8 1 Identifier of pane with active
+ cell cursor
+ [9] 1 Not used (BIFF5-BIFF8 only, not written
+ in BIFF2-BIFF4)
+
+ If the panes are frozen, pane 0 is always active, regardless
+ of the cursor position. The correct identifiers for all possible
+ combinations of visible panes are shown in the following pictures.
+
+ px = 0, py = 0 px = 0, py > 0
+ -------------------------- ------------|-------------
+ | | | |
+ | | | 3 |
+ | | | |
+ - 3 - --------------------------
+ | | | |
+ | | | 2 |
+ | | | |
+ -------------------------- ------------|-------------
+
+ px > 0, py = 0 px > 0, py > 0
+ ------------|------------- ------------|-------------
+ | | | | | |
+ | | | | 3 | 2 |
+ | | | | | |
+ - 3 | 1 - --------------------------
+ | | | | | |
+ | | | | 1 | 0 |
+ | | | | | |
+ ------------|------------- ------------|-------------
+ """
+ _REC_ID = 0x0041
+
+ valid_active_pane = {
+ # entries are of the form:
+ # (int(px > 0),int(px>0)) -> allowed values
+ (0,0):(3,),
+ (0,1):(2,3),
+ (1,0):(1,3),
+ (1,1):(0,1,2,3),
+ }
+
+ def __init__(self, px, py, first_row_bottom, first_col_right, active_pane):
+ allowed = self.valid_active_pane.get(
+ (int(px > 0),int(py > 0))
+ )
+ if active_pane not in allowed:
+ raise ValueError('Cannot set active_pane to %i, must be one of %s' % (
+ active_pane, ', '.join(allowed)
+ ))
+ self._rec_data = pack('<5H',
+ px, py,
+ first_row_bottom, first_col_right,
+ active_pane)
+
+
+class RowRecord(BiffRecord):
+ """
+ This record contains the properties of a single row in a sheet. Rows
+ and cells in a sheet are divided into blocks of 32 rows.
+
+ Record ROW, BIFF3-BIFF8:
+
+ Offset Size Contents
+ 0 2 Index of this row
+ 2 2 Index to column of the first cell which is described by a cell record
+ 4 2 Index to column of the last cell which is described by a cell record,
+ increased by 1
+ 6 2 Bit Mask Contents
+ 14-0 7FFFH Height of the row, in twips = 1/20 of a point
+ 15 8000H 0 = Row has custom height; 1 = Row has default height
+ 8 2 Not used
+ 10 2 In BIFF3-BIFF4 this field contains a relative offset
+ to calculate stream position of the first cell record
+ for this row. In BIFF5-BIFF8 this field is not used
+ anymore, but the DBCELL record instead.
+ 12 4 Option flags and default row formatting:
+ Bit Mask Contents
+ 2-0 00000007H Outline level of the row
+ 4 00000010H 1 = Outline group starts or ends here (depending
+ on where the outline buttons are located,
+ see WSBOOL record), and is collapsed
+ 5 00000020H 1 = Row is hidden (manually, or by a filter or outline group)
+ 6 00000040H 1 = Row height and default font height do not match
+ 7 00000080H 1 = Row has explicit default format (fl)
+ 8 00000100H Always 1
+ 27-16 0FFF0000H If fl=1: Index to default XF record
+ 28 10000000H 1 = Additional space above the row. This flag is set,
+ if the upper border of at least one cell in this row
+ or if the lower border of at least one cell in the row
+ above is formatted with a thick line style.
+ Thin and medium line styles are not taken into account.
+ 29 20000000H 1 = Additional space below the row. This flag is set,
+ if the lower border of at least one cell in this row
+ or if the upper border of at least one cell in the row
+ below is formatted with a medium or thick line style.
+ Thin line styles are not taken into account.
+ """
+
+ _REC_ID = 0x0208
+
+ def __init__(self, index, first_col, last_col, height_options, options):
+ self._rec_data = pack('<6HL', index, first_col, last_col + 1,
+ height_options,
+ 0x00, 0x00,
+ options)
+
+class LabelSSTRecord(BiffRecord):
+ """
+ This record represents a cell that contains a string. It replaces the
+ LABEL record and RSTRING record used in BIFF2-BIFF7.
+ """
+ _REC_ID = 0x00FD
+
+ def __init__(self, row, col, xf_idx, sst_idx):
+ self._rec_data = pack('<3HL', row, col, xf_idx, sst_idx)
+
+
+class MergedCellsRecord(BiffRecord):
+ """
+ This record contains all merged cell ranges of the current sheet.
+
+ Record MERGEDCELLS, BIFF8:
+
+ Offset Size Contents
+ 0 var. Cell range address list with all merged ranges
+
+ ------------------------------------------------------------------
+
+ A cell range address list consists of a field with the number of ranges
+ and the list of the range addresses.
+
+ Cell range address list, BIFF8:
+
+ Offset Size Contents
+ 0 2 Number of following cell range addresses (nm)
+ 2 8*nm List of nm cell range addresses
+
+ ---------------------------------------------------------------------
+ Cell range address, BIFF8:
+
+ Offset Size Contents
+ 0 2 Index to first row
+ 2 2 Index to last row
+ 4 2 Index to first column
+ 6 2 Index to last column
+
+ """
+ _REC_ID = 0x00E5
+
+ def __init__(self, merged_list):
+ i = len(merged_list) - 1
+ while i >= 0:
+ j = 0
+ merged = b''
+ while (i >= 0) and (j < 0x403):
+ r1, r2, c1, c2 = merged_list[i]
+ merged += pack('<4H', r1, r2, c1, c2)
+ i -= 1
+ j += 1
+ self._rec_data += pack('<3H', self._REC_ID, len(merged) + 2, j) + \
+ merged
+
+ # for some reason Excel doesn't use CONTINUE
+ def get(self):
+ return self._rec_data
+
+class MulBlankRecord(BiffRecord):
+ """
+ This record represents a cell range of empty cells. All cells are
+ located in the same row.
+
+ Record MULBLANK, BIFF5-BIFF8:
+
+ Offset Size Contents
+ 0 2 Index to row
+ 2 2 Index to first column (fc)
+ 4 2*nc List of nc=lc-fc+1 16-bit indexes to XF records
+ 4+2*nc 2 Index to last column (lc)
+ """
+ _REC_ID = 0x00BE
+
+ def __init__(self, row, first_col, last_col, xf_index):
+ blanks_count = last_col-first_col+1
+ self._rec_data = pack('<%dH' % blanks_count, *([xf_index] * blanks_count))
+ self._rec_data = pack('<2H', row, first_col) + self._rec_data + pack('" Set new font
+ &","
+ Set new font with specified style .
+ The style is in most cases one of
+ "Regular", "Bold", "Italic", or "Bold Italic".
+ But this setting is dependent on the used font,
+ it may differ (localised style names, or "Standard",
+ "Oblique", ...). (BIFF5-BIFF8)
+ & Set font height in points ( is a decimal value).
+ If this command is followed by a plain number to be printed
+ in the header, it will be separated from the font height
+ with a space character.
+
+ """
+ _REC_ID = 0x0014
+
+ def __init__(self, header_str):
+ self._rec_data = upack2(header_str)
+
+class FooterRecord(BiffRecord):
+ """
+ Semantic is equal to HEADER record
+ """
+ _REC_ID = 0x0015
+
+ def __init__(self, footer_str):
+ self._rec_data = upack2(footer_str)
+
+
+class HCenterRecord(BiffRecord):
+ """
+ This record is part of the Page Settings Block. It specifies if the
+ sheet is centred horizontally when printed.
+
+ Record HCENTER, BIFF3-BIFF8:
+
+ Offset Size Contents
+ 0 2 0 = Print sheet left aligned
+ 1 = Print sheet centred horizontally
+
+ """
+ _REC_ID = 0x0083
+
+ def __init__(self, is_horz_center):
+ self._rec_data = pack(' 0) Menu text (Unicode string without length field, 3.4)
+ [var.] var. (optional, only if ld > 0) Description text (Unicode string without length field, 3.4)
+ [var.] var. (optional, only if lh > 0) Help topic text (Unicode string without length field, 3.4)
+ [var.] var. (optional, only if ls > 0) Status bar text (Unicode string without length field, 3.4)
+ """
+ _REC_ID = 0x0018
+
+ def __init__(self, options, keyboard_shortcut, name, sheet_index, rpn, menu_text='', desc_text='', help_text='', status_text=''):
+ if type(name) == int:
+ uname = chr(name)
+ else:
+ uname = upack1(name)[1:]
+ uname_len = len(uname)
+
+ #~ self._rec_data = pack('", see 3.9.1)
+
+ """
+
+ def __init__(self, num_sheets):
+ self._rec_data = pack('
+# Portions are Copyright (c) 2002-2004 John McNamara (Perl Spreadsheet::WriteExcel)
+
+from .BIFFRecords import BiffRecord
+from struct import pack, unpack
+
+
+def _size_col(sheet, col):
+ return sheet.col_width(col)
+
+
+def _size_row(sheet, row):
+ return sheet.row_height(row)
+
+
+def _position_image(sheet, row_start, col_start, x1, y1, width, height):
+ """Calculate the vertices that define the position of the image as required by
+ the OBJ record.
+
+ +------------+------------+
+ | A | B |
+ +-----+------------+------------+
+ | |(x1,y1) | |
+ | 1 |(A1)._______|______ |
+ | | | | |
+ | | | | |
+ +-----+----| BITMAP |-----+
+ | | | | |
+ | 2 | |______________. |
+ | | | (B2)|
+ | | | (x2,y2)|
+ +---- +------------+------------+
+
+ Example of a bitmap that covers some of the area from cell A1 to cell B2.
+
+ Based on the width and height of the bitmap we need to calculate 8 vars:
+ col_start, row_start, col_end, row_end, x1, y1, x2, y2.
+ The width and height of the cells are also variable and have to be taken into
+ account.
+ The values of col_start and row_start are passed in from the calling
+ function. The values of col_end and row_end are calculated by subtracting
+ the width and height of the bitmap from the width and height of the
+ underlying cells.
+ The vertices are expressed as a percentage of the underlying cell width as
+ follows (rhs values are in pixels):
+
+ x1 = X / W *1024
+ y1 = Y / H *256
+ x2 = (X-1) / W *1024
+ y2 = (Y-1) / H *256
+
+ Where: X is distance from the left side of the underlying cell
+ Y is distance from the top of the underlying cell
+ W is the width of the cell
+ H is the height of the cell
+
+ Note: the SDK incorrectly states that the height should be expressed as a
+ percentage of 1024.
+
+ col_start - Col containing upper left corner of object
+ row_start - Row containing top left corner of object
+ x1 - Distance to left side of object
+ y1 - Distance to top of object
+ width - Width of image frame
+ height - Height of image frame
+
+ """
+ # Adjust start column for offsets that are greater than the col width
+ while x1 >= _size_col(sheet, col_start):
+ x1 -= _size_col(sheet, col_start)
+ col_start += 1
+ # Adjust start row for offsets that are greater than the row height
+ while y1 >= _size_row(sheet, row_start):
+ y1 -= _size_row(sheet, row_start)
+ row_start += 1
+ # Initialise end cell to the same as the start cell
+ row_end = row_start # Row containing bottom right corner of object
+ col_end = col_start # Col containing lower right corner of object
+ width = width + x1 - 1
+ height = height + y1 - 1
+ # Subtract the underlying cell widths to find the end cell of the image
+ while (width >= _size_col(sheet, col_end)):
+ width -= _size_col(sheet, col_end)
+ col_end += 1
+ # Subtract the underlying cell heights to find the end cell of the image
+ while (height >= _size_row(sheet, row_end)):
+ height -= _size_row(sheet, row_end)
+ row_end += 1
+ # Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
+ # with zero height or width.
+ if ((_size_col(sheet, col_start) == 0) or (_size_col(sheet, col_end) == 0)
+ or (_size_row(sheet, row_start) == 0) or (_size_row(sheet, row_end) == 0)):
+ return
+ # Convert the pixel values to the percentage value expected by Excel
+ x1 = int(float(x1) / _size_col(sheet, col_start) * 1024)
+ y1 = int(float(y1) / _size_row(sheet, row_start) * 256)
+ # Distance to right side of object
+ x2 = int(float(width) / _size_col(sheet, col_end) * 1024)
+ # Distance to bottom of object
+ y2 = int(float(height) / _size_row(sheet, row_end) * 256)
+ return (col_start, x1, row_start, y1, col_end, x2, row_end, y2)
+
+
+class ObjBmpRecord(BiffRecord):
+ _REC_ID = 0x005D # Record identifier
+
+ def __init__(self, row, col, sheet, im_data_bmp, x, y, scale_x, scale_y):
+ # Scale the frame of the image.
+ width = im_data_bmp.width * scale_x
+ height = im_data_bmp.height * scale_y
+
+ # Calculate the vertices of the image and write the OBJ record
+ coordinates = _position_image(sheet, row, col, x, y, width, height)
+ # print coordinates
+ col_start, x1, row_start, y1, col_end, x2, row_end, y2 = coordinates
+
+ """Store the OBJ record that precedes an IMDATA record. This could be generalise
+ to support other Excel objects.
+
+ """
+ cObj = 0x0001 # Count of objects in file (set to 1)
+ OT = 0x0008 # Object type. 8 = Picture
+ id = 0x0001 # Object ID
+ grbit = 0x0614 # Option flags
+ colL = col_start # Col containing upper left corner of object
+ dxL = x1 # Distance from left side of cell
+ rwT = row_start # Row containing top left corner of object
+ dyT = y1 # Distance from top of cell
+ colR = col_end # Col containing lower right corner of object
+ dxR = x2 # Distance from right of cell
+ rwB = row_end # Row containing bottom right corner of object
+ dyB = y2 # Distance from bottom of cell
+ cbMacro = 0x0000 # Length of FMLA structure
+ Reserved1 = 0x0000 # Reserved
+ Reserved2 = 0x0000 # Reserved
+ icvBack = 0x09 # Background colour
+ icvFore = 0x09 # Foreground colour
+ fls = 0x00 # Fill pattern
+ fAuto = 0x00 # Automatic fill
+ icv = 0x08 # Line colour
+ lns = 0xff # Line style
+ lnw = 0x01 # Line weight
+ fAutoB = 0x00 # Automatic border
+ frs = 0x0000 # Frame style
+ cf = 0x0009 # Image format, 9 = bitmap
+ Reserved3 = 0x0000 # Reserved
+ cbPictFmla = 0x0000 # Length of FMLA structure
+ Reserved4 = 0x0000 # Reserved
+ grbit2 = 0x0001 # Option flags
+ Reserved5 = 0x0000 # Reserved
+
+ data = pack(" 0xFFFF):
+ raise Exception("bitmap: largest image width supported is 65k.")
+ if (height > 0xFFFF):
+ raise Exception("bitmap: largest image height supported is 65k.")
+ # Read and remove the bitmap planes and bpp data. Verify them.
+ planes, bitcount = unpack(" about 7 Mb == (109*512 * 128)
+# ... SAT
+# ... directory stream
+#
+# NOTE: this layout is "ad hoc". It can be more general. RTFM
+
+class XlsDoc:
+ SECTOR_SIZE = 0x0200
+ MIN_LIMIT = 0x1000
+
+ SID_FREE_SECTOR = -1
+ SID_END_OF_CHAIN = -2
+ SID_USED_BY_SAT = -3
+ SID_USED_BY_MSAT = -4
+
+ def __init__(self):
+ #self.book_stream = '' # padded
+ self.book_stream_sect = []
+
+ self.dir_stream = ''
+ self.dir_stream_sect = []
+
+ self.packed_SAT = ''
+ self.SAT_sect = []
+
+ self.packed_MSAT_1st = ''
+ self.packed_MSAT_2nd = ''
+ self.MSAT_sect_2nd = []
+
+ self.header = ''
+
+ def _build_directory(self): # align on sector boundary
+ self.dir_stream = b''
+
+ dentry_name = u'Root Entry\x00'.encode('utf-16-le')
+ dentry_name_sz = len(dentry_name)
+ dentry_name_pad = b'\x00'*(64 - dentry_name_sz)
+ dentry_type = 0x05 # root storage
+ dentry_colour = 0x01 # black
+ dentry_did_left = -1
+ dentry_did_right = -1
+ dentry_did_root = 1
+ dentry_start_sid = -2
+ dentry_stream_sz = 0
+
+ self.dir_stream += struct.pack('<64s H 2B 3l 9L l L L',
+ dentry_name + dentry_name_pad,
+ dentry_name_sz,
+ dentry_type,
+ dentry_colour,
+ dentry_did_left,
+ dentry_did_right,
+ dentry_did_root,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ dentry_start_sid,
+ dentry_stream_sz,
+ 0
+ )
+
+ dentry_name = u'Workbook\x00'.encode('utf-16-le')
+ dentry_name_sz = len(dentry_name)
+ dentry_name_pad = b'\x00'*(64 - dentry_name_sz)
+ dentry_type = 0x02 # user stream
+ dentry_colour = 0x01 # black
+ dentry_did_left = -1
+ dentry_did_right = -1
+ dentry_did_root = -1
+ dentry_start_sid = 0
+ dentry_stream_sz = self.book_stream_len
+
+ self.dir_stream += struct.pack('<64s H 2B 3l 9L l L L',
+ dentry_name + dentry_name_pad,
+ dentry_name_sz,
+ dentry_type,
+ dentry_colour,
+ dentry_did_left,
+ dentry_did_right,
+ dentry_did_root,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ dentry_start_sid,
+ dentry_stream_sz,
+ 0
+ )
+
+ # padding
+ dentry_name = b''
+ dentry_name_sz = len(dentry_name)
+ dentry_name_pad = b'\x00'*(64 - dentry_name_sz)
+ dentry_type = 0x00 # empty
+ dentry_colour = 0x01 # black
+ dentry_did_left = -1
+ dentry_did_right = -1
+ dentry_did_root = -1
+ dentry_start_sid = -2
+ dentry_stream_sz = 0
+
+ self.dir_stream += struct.pack('<64s H 2B 3l 9L l L L',
+ dentry_name + dentry_name_pad,
+ dentry_name_sz,
+ dentry_type,
+ dentry_colour,
+ dentry_did_left,
+ dentry_did_right,
+ dentry_did_root,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ dentry_start_sid,
+ dentry_stream_sz,
+ 0
+ ) * 2
+
+ def _build_sat(self):
+ # Build SAT
+ book_sect_count = self.book_stream_len >> 9
+ dir_sect_count = len(self.dir_stream) >> 9
+
+ total_sect_count = book_sect_count + dir_sect_count
+ SAT_sect_count = 0
+ MSAT_sect_count = 0
+ SAT_sect_count_limit = 109
+ while total_sect_count > 128*SAT_sect_count or SAT_sect_count > SAT_sect_count_limit:
+ SAT_sect_count += 1
+ total_sect_count += 1
+ if SAT_sect_count > SAT_sect_count_limit:
+ MSAT_sect_count += 1
+ total_sect_count += 1
+ SAT_sect_count_limit += 127
+
+
+ SAT = [self.SID_FREE_SECTOR]*128*SAT_sect_count
+
+ sect = 0
+ while sect < book_sect_count - 1:
+ self.book_stream_sect.append(sect)
+ SAT[sect] = sect + 1
+ sect += 1
+ self.book_stream_sect.append(sect)
+ SAT[sect] = self.SID_END_OF_CHAIN
+ sect += 1
+
+ while sect < book_sect_count + MSAT_sect_count:
+ self.MSAT_sect_2nd.append(sect)
+ SAT[sect] = self.SID_USED_BY_MSAT
+ sect += 1
+
+ while sect < book_sect_count + MSAT_sect_count + SAT_sect_count:
+ self.SAT_sect.append(sect)
+ SAT[sect] = self.SID_USED_BY_SAT
+ sect += 1
+
+ while sect < book_sect_count + MSAT_sect_count + SAT_sect_count + dir_sect_count - 1:
+ self.dir_stream_sect.append(sect)
+ SAT[sect] = sect + 1
+ sect += 1
+ self.dir_stream_sect.append(sect)
+ SAT[sect] = self.SID_END_OF_CHAIN
+ sect += 1
+
+ self.packed_SAT = struct.pack('<%dl' % (SAT_sect_count*128), *SAT)
+
+ MSAT_1st = [self.SID_FREE_SECTOR]*109
+ for i, SAT_sect_num in zip(range(0, 109), self.SAT_sect):
+ MSAT_1st[i] = SAT_sect_num
+ self.packed_MSAT_1st = struct.pack('<109l', *MSAT_1st)
+
+ MSAT_2nd = [self.SID_FREE_SECTOR]*128*MSAT_sect_count
+ if MSAT_sect_count > 0:
+ MSAT_2nd[- 1] = self.SID_END_OF_CHAIN
+
+ i = 109
+ msat_sect = 0
+ sid_num = 0
+ while i < SAT_sect_count:
+ if (sid_num + 1) % 128 == 0:
+ #print 'link: ',
+ msat_sect += 1
+ if msat_sect < len(self.MSAT_sect_2nd):
+ MSAT_2nd[sid_num] = self.MSAT_sect_2nd[msat_sect]
+ else:
+ #print 'sid: ',
+ MSAT_2nd[sid_num] = self.SAT_sect[i]
+ i += 1
+ #print sid_num, MSAT_2nd[sid_num]
+ sid_num += 1
+
+ self.packed_MSAT_2nd = struct.pack('<%dl' % (MSAT_sect_count*128), *MSAT_2nd)
+
+ #print vars()
+ #print zip(range(0, sect), SAT)
+ #print self.book_stream_sect
+ #print self.MSAT_sect_2nd
+ #print MSAT_2nd
+ #print self.SAT_sect
+ #print self.dir_stream_sect
+
+
+ def _build_header(self):
+ doc_magic = b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1'
+ file_uid = b'\x00'*16
+ rev_num = b'\x3E\x00'
+ ver_num = b'\x03\x00'
+ byte_order = b'\xFE\xFF'
+ log_sect_size = struct.pack('"
+ge_pattern = r">="
+le_pattern = r"<="
+
+pattern_type_tuples = (
+ (flt_const_pattern, ExcelFormulaParser.NUM_CONST),
+ (int_const_pattern, ExcelFormulaParser.INT_CONST),
+ (str_const_pattern, ExcelFormulaParser.STR_CONST),
+# (range2d_pattern , ExcelFormulaParser.RANGE2D),
+ (ref2d_r1c1_pattern, ExcelFormulaParser.REF2D_R1C1),
+ (ref2d_pattern , ExcelFormulaParser.REF2D),
+ (true_pattern , ExcelFormulaParser.TRUE_CONST),
+ (false_pattern , ExcelFormulaParser.FALSE_CONST),
+ (if_pattern , ExcelFormulaParser.FUNC_IF),
+ (choose_pattern , ExcelFormulaParser.FUNC_CHOOSE),
+ (name_pattern , ExcelFormulaParser.NAME),
+ (quotename_pattern, ExcelFormulaParser.QUOTENAME),
+ (ne_pattern, ExcelFormulaParser.NE),
+ (ge_pattern, ExcelFormulaParser.GE),
+ (le_pattern, ExcelFormulaParser.LE),
+)
+
+_re = recompile(
+ '(' + ')|('.join(i[0] for i in pattern_type_tuples) + ')',
+ VERBOSE+IGNORECASE)
+
+_toktype = [None] + [i[1] for i in pattern_type_tuples]
+# need dummy at start because re.MatchObject.lastindex counts from 1
+
+single_char_lookup = {
+ '=': ExcelFormulaParser.EQ,
+ '<': ExcelFormulaParser.LT,
+ '>': ExcelFormulaParser.GT,
+ '+': ExcelFormulaParser.ADD,
+ '-': ExcelFormulaParser.SUB,
+ '*': ExcelFormulaParser.MUL,
+ '/': ExcelFormulaParser.DIV,
+ ':': ExcelFormulaParser.COLON,
+ ';': ExcelFormulaParser.SEMICOLON,
+ ',': ExcelFormulaParser.COMMA,
+ '(': ExcelFormulaParser.LP,
+ ')': ExcelFormulaParser.RP,
+ '&': ExcelFormulaParser.CONCAT,
+ '%': ExcelFormulaParser.PERCENT,
+ '^': ExcelFormulaParser.POWER,
+ '!': ExcelFormulaParser.BANG,
+ }
+
+class Lexer(TokenStream):
+ def __init__(self, text):
+ self._text = text[:]
+ self._pos = 0
+ self._line = 0
+
+ def isEOF(self):
+ return len(self._text) <= self._pos
+
+ def curr_ch(self):
+ return self._text[self._pos]
+
+ def next_ch(self, n = 1):
+ self._pos += n
+
+ def is_whitespace(self):
+ return self.curr_ch() in " \t\n\r\f\v"
+
+ def match_pattern(self):
+ m = _re.match(self._text, self._pos)
+ if not m:
+ return None
+ self._pos = m.end(0)
+ return Tok(type = _toktype[m.lastindex], text = m.group(0), col = m.start(0) + 1)
+
+ def nextToken(self):
+ # skip whitespace
+ while not self.isEOF() and self.is_whitespace():
+ self.next_ch()
+ if self.isEOF():
+ return Tok(type = EOF)
+ # first, try to match token with 2 or more chars
+ t = self.match_pattern()
+ if t:
+ return t
+ # second, we want 1-char tokens
+ te = self.curr_ch()
+ try:
+ ty = single_char_lookup[te]
+ except KeyError:
+ raise TokenStreamException(
+ "Unexpected char %r in column %u." % (self.curr_ch(), self._pos))
+ self.next_ch()
+ return Tok(type=ty, text=te, col=self._pos)
+
+if __name__ == '__main__':
+ try:
+ for t in Lexer(""" 1.23 456 "abcd" R2C2 a1 iv65536 true false if choose a_name 'qname' <> >= <= """):
+ print(t)
+ except TokenStreamException as e:
+ print("error:", e)
diff --git a/.venv/lib/python3.9/site-packages/xlwt/ExcelFormulaParser.py b/.venv/lib/python3.9/site-packages/xlwt/ExcelFormulaParser.py
new file mode 100644
index 00000000..ece64e6d
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt/ExcelFormulaParser.py
@@ -0,0 +1,669 @@
+### $ANTLR 2.7.7 (20060930): "xlwt/excel-formula.g" -> "ExcelFormulaParser.py"$
+### import antlr and other modules ..
+from . import antlr
+
+### header action >>>
+import struct
+from . import Utils
+from .UnicodeUtils import upack1
+from .ExcelMagic import *
+
+_RVAdelta = {"R": 0, "V": 0x20, "A": 0x40}
+_RVAdeltaRef = {"R": 0, "V": 0x20, "A": 0x40, "D": 0x20}
+_RVAdeltaArea = {"R": 0, "V": 0x20, "A": 0x40, "D": 0}
+
+
+class FormulaParseException(Exception):
+ """
+ An exception indicating that a Formula could not be successfully parsed.
+ """
+### header action <<<
+### preamble action>>>
+
+### preamble action <<<
+
+### >>>The Known Token Types <<<
+SKIP = antlr.SKIP
+INVALID_TYPE = antlr.INVALID_TYPE
+EOF_TYPE = antlr.EOF_TYPE
+EOF = antlr.EOF
+NULL_TREE_LOOKAHEAD = antlr.NULL_TREE_LOOKAHEAD
+MIN_USER_TYPE = antlr.MIN_USER_TYPE
+TRUE_CONST = 4
+FALSE_CONST = 5
+STR_CONST = 6
+NUM_CONST = 7
+INT_CONST = 8
+FUNC_IF = 9
+FUNC_CHOOSE = 10
+NAME = 11
+QUOTENAME = 12
+EQ = 13
+NE = 14
+GT = 15
+LT = 16
+GE = 17
+LE = 18
+ADD = 19
+SUB = 20
+MUL = 21
+DIV = 22
+POWER = 23
+PERCENT = 24
+LP = 25
+RP = 26
+LB = 27
+RB = 28
+COLON = 29
+COMMA = 30
+SEMICOLON = 31
+REF2D = 32
+REF2D_R1C1 = 33
+BANG = 34
+CONCAT = 35
+
+class Parser(antlr.LLkParser):
+ ### user action >>>
+ ### user action <<<
+
+ def __init__(self, *args, **kwargs):
+ antlr.LLkParser.__init__(self, *args, **kwargs)
+ self.tokenNames = _tokenNames
+ ### __init__ header action >>>
+ self.rpn = b""
+ self.sheet_references = []
+ self.xcall_references = []
+ ### __init__ header action <<<
+
+ def formula(self):
+
+ pass
+ self.expr("V")
+
+ def expr(self,
+ arg_type
+ ):
+
+ pass
+ self.prec0_expr(arg_type)
+ while True:
+ if ((self.LA(1) >= EQ and self.LA(1) <= LE)):
+ pass
+ la1 = self.LA(1)
+ if False:
+ pass
+ elif la1 and la1 in [EQ]:
+ pass
+ self.match(EQ)
+ op = struct.pack('B', ptgEQ)
+ elif la1 and la1 in [NE]:
+ pass
+ self.match(NE)
+ op = struct.pack('B', ptgNE)
+ elif la1 and la1 in [GT]:
+ pass
+ self.match(GT)
+ op = struct.pack('B', ptgGT)
+ elif la1 and la1 in [LT]:
+ pass
+ self.match(LT)
+ op = struct.pack('B', ptgLT)
+ elif la1 and la1 in [GE]:
+ pass
+ self.match(GE)
+ op = struct.pack('B', ptgGE)
+ elif la1 and la1 in [LE]:
+ pass
+ self.match(LE)
+ op = struct.pack('B', ptgLE)
+ else:
+ raise antlr.NoViableAltException(self.LT(1), self.getFilename())
+
+ self.prec0_expr(arg_type)
+ self.rpn += op
+ else:
+ break
+
+
+ def prec0_expr(self,
+ arg_type
+ ):
+
+ pass
+ self.prec1_expr(arg_type)
+ while True:
+ if (self.LA(1)==CONCAT):
+ pass
+ pass
+ self.match(CONCAT)
+ op = struct.pack('B', ptgConcat)
+ self.prec1_expr(arg_type)
+ self.rpn += op
+ else:
+ break
+
+
+ def prec1_expr(self,
+ arg_type
+ ):
+
+ pass
+ self.prec2_expr(arg_type)
+ while True:
+ if (self.LA(1)==ADD or self.LA(1)==SUB):
+ pass
+ la1 = self.LA(1)
+ if False:
+ pass
+ elif la1 and la1 in [ADD]:
+ pass
+ self.match(ADD)
+ op = struct.pack('B', ptgAdd)
+ elif la1 and la1 in [SUB]:
+ pass
+ self.match(SUB)
+ op = struct.pack('B', ptgSub)
+ else:
+ raise antlr.NoViableAltException(self.LT(1), self.getFilename())
+
+ self.prec2_expr(arg_type)
+ self.rpn += op;
+ # print "**prec1_expr4 %s" % arg_type
+ else:
+ break
+
+
+ def prec2_expr(self,
+ arg_type
+ ):
+
+ pass
+ self.prec3_expr(arg_type)
+ while True:
+ if (self.LA(1)==MUL or self.LA(1)==DIV):
+ pass
+ la1 = self.LA(1)
+ if False:
+ pass
+ elif la1 and la1 in [MUL]:
+ pass
+ self.match(MUL)
+ op = struct.pack('B', ptgMul)
+ elif la1 and la1 in [DIV]:
+ pass
+ self.match(DIV)
+ op = struct.pack('B', ptgDiv)
+ else:
+ raise antlr.NoViableAltException(self.LT(1), self.getFilename())
+
+ self.prec3_expr(arg_type)
+ self.rpn += op
+ else:
+ break
+
+
+ def prec3_expr(self,
+ arg_type
+ ):
+
+ pass
+ self.prec4_expr(arg_type)
+ while True:
+ if (self.LA(1)==POWER):
+ pass
+ pass
+ self.match(POWER)
+ op = struct.pack('B', ptgPower)
+ self.prec4_expr(arg_type)
+ self.rpn += op
+ else:
+ break
+
+
+ def prec4_expr(self,
+ arg_type
+ ):
+
+ pass
+ self.prec5_expr(arg_type)
+ la1 = self.LA(1)
+ if False:
+ pass
+ elif la1 and la1 in [PERCENT]:
+ pass
+ self.match(PERCENT)
+ self.rpn += struct.pack('B', ptgPercent)
+ elif la1 and la1 in [EOF,EQ,NE,GT,LT,GE,LE,ADD,SUB,MUL,DIV,POWER,RP,COMMA,SEMICOLON,CONCAT]:
+ pass
+ else:
+ raise antlr.NoViableAltException(self.LT(1), self.getFilename())
+
+
+ def prec5_expr(self,
+ arg_type
+ ):
+
+ la1 = self.LA(1)
+ if False:
+ pass
+ elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,LP,REF2D]:
+ pass
+ self.primary(arg_type)
+ elif la1 and la1 in [SUB]:
+ pass
+ self.match(SUB)
+ self.primary(arg_type)
+ self.rpn += struct.pack('B', ptgUminus)
+ else:
+ raise antlr.NoViableAltException(self.LT(1), self.getFilename())
+
+
+ def primary(self,
+ arg_type
+ ):
+
+ str_tok = None
+ int_tok = None
+ num_tok = None
+ ref2d_tok = None
+ ref2d1_tok = None
+ ref2d2_tok = None
+ ref3d_ref2d = None
+ ref3d_ref2d2 = None
+ name_tok = None
+ func_tok = None
+ la1 = self.LA(1)
+ if False:
+ pass
+ elif la1 and la1 in [TRUE_CONST]:
+ pass
+ self.match(TRUE_CONST)
+ self.rpn += struct.pack("2B", ptgBool, 1)
+ elif la1 and la1 in [FALSE_CONST]:
+ pass
+ self.match(FALSE_CONST)
+ self.rpn += struct.pack("2B", ptgBool, 0)
+ elif la1 and la1 in [STR_CONST]:
+ pass
+ str_tok = self.LT(1)
+ self.match(STR_CONST)
+ self.rpn += struct.pack("B", ptgStr) + upack1(str_tok.text[1:-1].replace("\"\"", "\""))
+ elif la1 and la1 in [NUM_CONST]:
+ pass
+ num_tok = self.LT(1)
+ self.match(NUM_CONST)
+ self.rpn += struct.pack(" max_argc or arg_count < min_argc:
+ raise Exception("%d parameters for function: %s" % (arg_count, func_tok.text))
+ if xcall:
+ func_ptg = ptgFuncVarR + _RVAdelta[func_type]
+ self.rpn += struct.pack("<2BH", func_ptg, arg_count + 1, 255) # 255 is magic XCALL function
+ elif min_argc == max_argc:
+ func_ptg = ptgFuncR + _RVAdelta[func_type]
+ self.rpn += struct.pack("",
+ "EOF",
+ "<2>",
+ "NULL_TREE_LOOKAHEAD",
+ "TRUE_CONST",
+ "FALSE_CONST",
+ "STR_CONST",
+ "NUM_CONST",
+ "INT_CONST",
+ "FUNC_IF",
+ "FUNC_CHOOSE",
+ "NAME",
+ "QUOTENAME",
+ "EQ",
+ "NE",
+ "GT",
+ "LT",
+ "GE",
+ "LE",
+ "ADD",
+ "SUB",
+ "MUL",
+ "DIV",
+ "POWER",
+ "PERCENT",
+ "LP",
+ "RP",
+ "LB",
+ "RB",
+ "COLON",
+ "COMMA",
+ "SEMICOLON",
+ "REF2D",
+ "REF2D_R1C1",
+ "BANG",
+ "CONCAT"
+]
+
+
+### generate bit set
+def mk_tokenSet_0():
+ ### var1
+ data = [ 37681618946, 0]
+ return data
+_tokenSet_0 = antlr.BitSet(mk_tokenSet_0())
+
diff --git a/.venv/lib/python3.9/site-packages/xlwt/ExcelMagic.py b/.venv/lib/python3.9/site-packages/xlwt/ExcelMagic.py
new file mode 100644
index 00000000..2c292621
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt/ExcelMagic.py
@@ -0,0 +1,862 @@
+# -*- coding: ascii -*-
+"""
+lots of Excel Magic Numbers
+"""
+
+# Boundaries BIFF8+
+
+MAX_ROW = 65536
+MAX_COL = 256
+
+
+biff_records = {
+ 0x0000: "DIMENSIONS",
+ 0x0001: "BLANK",
+ 0x0002: "INTEGER",
+ 0x0003: "NUMBER",
+ 0x0004: "LABEL",
+ 0x0005: "BOOLERR",
+ 0x0006: "FORMULA",
+ 0x0007: "STRING",
+ 0x0008: "ROW",
+ 0x0009: "BOF",
+ 0x000A: "EOF",
+ 0x000B: "INDEX",
+ 0x000C: "CALCCOUNT",
+ 0x000D: "CALCMODE",
+ 0x000E: "PRECISION",
+ 0x000F: "REFMODE",
+ 0x0010: "DELTA",
+ 0x0011: "ITERATION",
+ 0x0012: "PROTECT",
+ 0x0013: "PASSWORD",
+ 0x0014: "HEADER",
+ 0x0015: "FOOTER",
+ 0x0016: "EXTERNCOUNT",
+ 0x0017: "EXTERNSHEET",
+ 0x0018: "NAME",
+ 0x0019: "WINDOWPROTECT",
+ 0x001A: "VERTICALPAGEBREAKS",
+ 0x001B: "HORIZONTALPAGEBREAKS",
+ 0x001C: "NOTE",
+ 0x001D: "SELECTION",
+ 0x001E: "FORMAT",
+ 0x001F: "FORMATCOUNT",
+ 0x0020: "COLUMNDEFAULT",
+ 0x0021: "ARRAY",
+ 0x0022: "1904",
+ 0x0023: "EXTERNNAME",
+ 0x0024: "COLWIDTH",
+ 0x0025: "DEFAULTROWHEIGHT",
+ 0x0026: "LEFTMARGIN",
+ 0x0027: "RIGHTMARGIN",
+ 0x0028: "TOPMARGIN",
+ 0x0029: "BOTTOMMARGIN",
+ 0x002A: "PRINTHEADERS",
+ 0x002B: "PRINTGRIDLINES",
+ 0x002F: "FILEPASS",
+ 0x0031: "FONT",
+ 0x0036: "TABLE",
+ 0x003C: "CONTINUE",
+ 0x003D: "WINDOW1",
+ 0x003E: "WINDOW2",
+ 0x0040: "BACKUP",
+ 0x0041: "PANE",
+ 0x0042: "CODEPAGE",
+ 0x0043: "XF",
+ 0x0044: "IXFE",
+ 0x0045: "EFONT",
+ 0x004D: "PLS",
+ 0x0050: "DCON",
+ 0x0051: "DCONREF",
+ 0x0053: "DCONNAME",
+ 0x0055: "DEFCOLWIDTH",
+ 0x0056: "BUILTINFMTCNT",
+ 0x0059: "XCT",
+ 0x005A: "CRN",
+ 0x005B: "FILESHARING",
+ 0x005C: "WRITEACCESS",
+ 0x005D: "OBJ",
+ 0x005E: "UNCALCED",
+ 0x005F: "SAFERECALC",
+ 0x0060: "TEMPLATE",
+ 0x0063: "OBJPROTECT",
+ 0x007D: "COLINFO",
+ 0x007E: "RK",
+ 0x007F: "IMDATA",
+ 0x0080: "GUTS",
+ 0x0081: "WSBOOL",
+ 0x0082: "GRIDSET",
+ 0x0083: "HCENTER",
+ 0x0084: "VCENTER",
+ 0x0085: "BOUNDSHEET",
+ 0x0086: "WRITEPROT",
+ 0x0087: "ADDIN",
+ 0x0088: "EDG",
+ 0x0089: "PUB",
+ 0x008C: "COUNTRY",
+ 0x008D: "HIDEOBJ",
+ 0x008E: "BUNDLESOFFSET",
+ 0x008F: "BUNDLEHEADER",
+ 0x0090: "SORT",
+ 0x0091: "SUB",
+ 0x0092: "PALETTE",
+ 0x0093: "STYLE",
+ 0x0094: "LHRECORD",
+ 0x0095: "LHNGRAPH",
+ 0x0096: "SOUND",
+ 0x0098: "LPR",
+ 0x0099: "STANDARDWIDTH",
+ 0x009A: "FNGROUPNAME",
+ 0x009B: "FILTERMODE",
+ 0x009C: "FNGROUPCOUNT",
+ 0x009D: "AUTOFILTERINFO",
+ 0x009E: "AUTOFILTER",
+ 0x00A0: "SCL",
+ 0x00A1: "SETUP",
+ 0x00A9: "COORDLIST",
+ 0x00AB: "GCW",
+ 0x00AE: "SCENMAN",
+ 0x00AF: "SCENARIO",
+ 0x00B0: "SXVIEW",
+ 0x00B1: "SXVD",
+ 0x00B2: "SXVI",
+ 0x00B4: "SXIVD",
+ 0x00B5: "SXLI",
+ 0x00B6: "SXPI",
+ 0x00B8: "DOCROUTE",
+ 0x00B9: "RECIPNAME",
+ 0x00BC: "SHRFMLA",
+ 0x00BD: "MULRK",
+ 0x00BE: "MULBLANK",
+ 0x00C1: "MMS",
+ 0x00C2: "ADDMENU",
+ 0x00C3: "DELMENU",
+ 0x00C5: "SXDI",
+ 0x00C6: "SXDB",
+ 0x00C7: "SXFIELD",
+ 0x00C8: "SXINDEXLIST",
+ 0x00C9: "SXDOUBLE",
+ 0x00CD: "SXSTRING",
+ 0x00CE: "SXDATETIME",
+ 0x00D0: "SXTBL",
+ 0x00D1: "SXTBRGITEM",
+ 0x00D2: "SXTBPG",
+ 0x00D3: "OBPROJ",
+ 0x00D5: "SXIDSTM",
+ 0x00D6: "RSTRING",
+ 0x00D7: "DBCELL",
+ 0x00DA: "BOOKBOOL",
+ 0x00DC: "SXEXT|PARAMQRY",
+ 0x00DD: "SCENPROTECT",
+ 0x00DE: "OLESIZE",
+ 0x00DF: "UDDESC",
+ 0x00E0: "XF",
+ 0x00E1: "INTERFACEHDR",
+ 0x00E2: "INTERFACEEND",
+ 0x00E3: "SXVS",
+ 0x00E5: "MERGEDCELLS",
+ 0x00E9: "BITMAP",
+ 0x00EB: "MSODRAWINGGROUP",
+ 0x00EC: "MSODRAWING",
+ 0x00ED: "MSODRAWINGSELECTION",
+ 0x00F0: "SXRULE",
+ 0x00F1: "SXEX",
+ 0x00F2: "SXFILT",
+ 0x00F6: "SXNAME",
+ 0x00F7: "SXSELECT",
+ 0x00F8: "SXPAIR",
+ 0x00F9: "SXFMLA",
+ 0x00FB: "SXFORMAT",
+ 0x00FC: "SST",
+ 0x00FD: "LABELSST",
+ 0x00FF: "EXTSST",
+ 0x0100: "SXVDEX",
+ 0x0103: "SXFORMULA",
+ 0x0122: "SXDBEX",
+ 0x0137: "CHTRINSERT",
+ 0x0138: "CHTRINFO",
+ 0x013B: "CHTRCELLCONTENT",
+ 0x013D: "TABID",
+ 0x0140: "CHTRMOVERANGE",
+ 0x014D: "CHTRINSERTTAB",
+ 0x015F: "LABELRANGES",
+ 0x0160: "USESELFS",
+ 0x0161: "DSF",
+ 0x0162: "XL5MODIFY",
+ 0x0196: "CHTRHEADER",
+ 0x01A9: "USERBVIEW",
+ 0x01AA: "USERSVIEWBEGIN",
+ 0x01AB: "USERSVIEWEND",
+ 0x01AD: "QSI",
+ 0x01AE: "SUPBOOK",
+ 0x01AF: "PROT4REV",
+ 0x01B0: "CONDFMT",
+ 0x01B1: "CF",
+ 0x01B2: "DVAL",
+ 0x01B5: "DCONBIN",
+ 0x01B6: "TXO",
+ 0x01B7: "REFRESHALL",
+ 0x01B8: "HLINK",
+ 0x01BA: "CODENAME",
+ 0x01BB: "SXFDBTYPE",
+ 0x01BC: "PROT4REVPASS",
+ 0x01BE: "DV",
+ 0x01C0: "XL9FILE",
+ 0x01C1: "RECALCID",
+ 0x0200: "DIMENSIONS",
+ 0x0201: "BLANK",
+ 0x0203: "NUMBER",
+ 0x0204: "LABEL",
+ 0x0205: "BOOLERR",
+ 0x0206: "FORMULA",
+ 0x0207: "STRING",
+ 0x0208: "ROW",
+ 0x0209: "BOF",
+ 0x020B: "INDEX",
+ 0x0218: "NAME",
+ 0x0221: "ARRAY",
+ 0x0223: "EXTERNNAME",
+ 0x0225: "DEFAULTROWHEIGHT",
+ 0x0231: "FONT",
+ 0x0236: "TABLE",
+ 0x023E: "WINDOW2",
+ 0x0243: "XF",
+ 0x027E: "RK",
+ 0x0293: "STYLE",
+ 0x0406: "FORMULA",
+ 0x0409: "BOF",
+ 0x041E: "FORMAT",
+ 0x0443: "XF",
+ 0x04BC: "SHRFMLA",
+ 0x0800: "SCREENTIP",
+ 0x0803: "WEBQRYSETTINGS",
+ 0x0804: "WEBQRYTABLES",
+ 0x0809: "BOF",
+ 0x0862: "SHEETLAYOUT",
+ 0x0867: "SHEETPROTECTION",
+ 0x1001: "UNITS",
+ 0x1002: "ChartChart",
+ 0x1003: "ChartSeries",
+ 0x1006: "ChartDataformat",
+ 0x1007: "ChartLineformat",
+ 0x1009: "ChartMarkerformat",
+ 0x100A: "ChartAreaformat",
+ 0x100B: "ChartPieformat",
+ 0x100C: "ChartAttachedlabel",
+ 0x100D: "ChartSeriestext",
+ 0x1014: "ChartChartformat",
+ 0x1015: "ChartLegend",
+ 0x1016: "ChartSerieslist",
+ 0x1017: "ChartBar",
+ 0x1018: "ChartLine",
+ 0x1019: "ChartPie",
+ 0x101A: "ChartArea",
+ 0x101B: "ChartScatter",
+ 0x101C: "ChartChartline",
+ 0x101D: "ChartAxis",
+ 0x101E: "ChartTick",
+ 0x101F: "ChartValuerange",
+ 0x1020: "ChartCatserrange",
+ 0x1021: "ChartAxislineformat",
+ 0x1022: "ChartFormatlink",
+ 0x1024: "ChartDefaulttext",
+ 0x1025: "ChartText",
+ 0x1026: "ChartFontx",
+ 0x1027: "ChartObjectLink",
+ 0x1032: "ChartFrame",
+ 0x1033: "BEGIN",
+ 0x1034: "END",
+ 0x1035: "ChartPlotarea",
+ 0x103A: "Chart3D",
+ 0x103C: "ChartPicf",
+ 0x103D: "ChartDropbar",
+ 0x103E: "ChartRadar",
+ 0x103F: "ChartSurface",
+ 0x1040: "ChartRadararea",
+ 0x1041: "ChartAxisparent",
+ 0x1043: "ChartLegendxn",
+ 0x1044: "ChartShtprops",
+ 0x1045: "ChartSertocrt",
+ 0x1046: "ChartAxesused",
+ 0x1048: "ChartSbaseref",
+ 0x104A: "ChartSerparent",
+ 0x104B: "ChartSerauxtrend",
+ 0x104E: "ChartIfmt",
+ 0x104F: "ChartPos",
+ 0x1050: "ChartAlruns",
+ 0x1051: "ChartAI",
+ 0x105B: "ChartSerauxerrbar",
+ 0x105D: "ChartSerfmt",
+ 0x105F: "Chart3DDataFormat",
+ 0x1060: "ChartFbi",
+ 0x1061: "ChartBoppop",
+ 0x1062: "ChartAxcext",
+ 0x1063: "ChartDat",
+ 0x1064: "ChartPlotgrowth",
+ 0x1065: "ChartSiindex",
+ 0x1066: "ChartGelframe",
+ 0x1067: "ChartBoppcustom",
+ 0xFFFF: ""
+}
+
+
+all_funcs_by_name = {
+ # Includes Analysis ToolPak aka ATP aka add-in aka xcall functions,
+ # distinguished by -ve opcode.
+ # name: (opcode, min # args, max # args, func return type, func arg types)
+ # + in func arg types means more of the same.
+ 'ABS' : ( 24, 1, 1, 'V', 'V'),
+ 'ACCRINT' : ( -1, 6, 7, 'V', 'VVVVVVV'),
+ 'ACCRINTM' : ( -1, 3, 5, 'V', 'VVVVV'),
+ 'ACOS' : ( 99, 1, 1, 'V', 'V'),
+ 'ACOSH' : (233, 1, 1, 'V', 'V'),
+ 'ADDRESS' : (219, 2, 5, 'V', 'VVVVV'),
+ 'AMORDEGRC' : ( -1, 7, 7, 'V', 'VVVVVVV'),
+ 'AMORLINC' : ( -1, 7, 7, 'V', 'VVVVVVV'),
+ 'AND' : ( 36, 1, 30, 'V', 'D+'),
+ 'AREAS' : ( 75, 1, 1, 'V', 'R'),
+ 'ASC' : (214, 1, 1, 'V', 'V'),
+ 'ASIN' : ( 98, 1, 1, 'V', 'V'),
+ 'ASINH' : (232, 1, 1, 'V', 'V'),
+ 'ATAN' : ( 18, 1, 1, 'V', 'V'),
+ 'ATAN2' : ( 97, 2, 2, 'V', 'VV'),
+ 'ATANH' : (234, 1, 1, 'V', 'V'),
+ 'AVEDEV' : (269, 1, 30, 'V', 'D+'),
+ 'AVERAGE' : ( 5, 1, 30, 'V', 'D+'),
+ 'AVERAGEA' : (361, 1, 30, 'V', 'D+'),
+ 'BAHTTEXT' : (368, 1, 1, 'V', 'V'),
+ 'BESSELI' : ( -1, 2, 2, 'V', 'VV'),
+ 'BESSELJ' : ( -1, 2, 2, 'V', 'VV'),
+ 'BESSELK' : ( -1, 2, 2, 'V', 'VV'),
+ 'BESSELY' : ( -1, 2, 2, 'V', 'VV'),
+ 'BETADIST' : (270, 3, 5, 'V', 'VVVVV'),
+ 'BETAINV' : (272, 3, 5, 'V', 'VVVVV'),
+ 'BIN2DEC' : ( -1, 1, 1, 'V', 'V'),
+ 'BIN2HEX' : ( -1, 1, 2, 'V', 'VV'),
+ 'BIN2OCT' : ( -1, 1, 2, 'V', 'VV'),
+ 'BINOMDIST' : (273, 4, 4, 'V', 'VVVV'),
+ 'CEILING' : (288, 2, 2, 'V', 'VV'),
+ 'CELL' : (125, 1, 2, 'V', 'VR'),
+ 'CHAR' : (111, 1, 1, 'V', 'V'),
+ 'CHIDIST' : (274, 2, 2, 'V', 'VV'),
+ 'CHIINV' : (275, 2, 2, 'V', 'VV'),
+ 'CHITEST' : (306, 2, 2, 'V', 'AA'),
+ 'CHOOSE' : (100, 2, 30, 'R', 'VR+'),
+ 'CLEAN' : (162, 1, 1, 'V', 'V'),
+ 'CODE' : (121, 1, 1, 'V', 'V'),
+ 'COLUMN' : ( 9, 0, 1, 'V', 'R'),
+ 'COLUMNS' : ( 77, 1, 1, 'V', 'R'),
+ 'COMBIN' : (276, 2, 2, 'V', 'VV'),
+ 'COMPLEX' : ( -1, 2, 3, 'V', 'VVV'),
+ 'CONCATENATE' : (336, 1, 30, 'V', 'V+'),
+ 'CONFIDENCE' : (277, 3, 3, 'V', 'VVV'),
+ 'CONVERT' : ( -1, 3, 3, 'V', 'VVV'),
+ 'CORREL' : (307, 2, 2, 'V', 'AA'),
+ 'COS' : ( 16, 1, 1, 'V', 'V'),
+ 'COSH' : (230, 1, 1, 'V', 'V'),
+ 'COUNT' : ( 0, 1, 30, 'V', 'D+'),
+ 'COUNTA' : (169, 1, 30, 'V', 'D+'),
+ 'COUNTBLANK' : (347, 1, 1, 'V', 'R'),
+ 'COUNTIF' : (346, 2, 2, 'V', 'RV'),
+ 'COUPDAYBS' : ( -1, 3, 5, 'V', 'VVVVV'),
+ 'COUPDAYS' : ( -1, 3, 5, 'V', 'VVVVV'),
+ 'COUPDAYSNC' : ( -1, 3, 5, 'V', 'VVVVV'),
+ 'COUPNCD' : ( -1, 3, 5, 'V', 'VVVVV'),
+ 'COUPNUM' : ( -1, 3, 5, 'V', 'VVVVV'),
+ 'COUPPCD' : ( -1, 3, 5, 'V', 'VVVVV'),
+ 'COVAR' : (308, 2, 2, 'V', 'AA'),
+ 'CRITBINOM' : (278, 3, 3, 'V', 'VVV'),
+ 'CUMIPMT' : ( -1, 6, 6, 'V', 'VVVVVV'),
+ 'CUMPRINC' : ( -1, 6, 6, 'V', 'VVVVVV'),
+ 'DATE' : ( 65, 3, 3, 'V', 'VVV'),
+ 'DATEDIF' : (351, 3, 3, 'V', 'VVV'),
+ 'DATEVALUE' : (140, 1, 1, 'V', 'V'),
+ 'DAVERAGE' : ( 42, 3, 3, 'V', 'RRR'),
+ 'DAY' : ( 67, 1, 1, 'V', 'V'),
+ 'DAYS360' : (220, 2, 3, 'V', 'VVV'),
+ 'DB' : (247, 4, 5, 'V', 'VVVVV'),
+ 'DBCS' : (215, 1, 1, 'V', 'V'),
+ 'DCOUNT' : ( 40, 3, 3, 'V', 'RRR'),
+ 'DCOUNTA' : (199, 3, 3, 'V', 'RRR'),
+ 'DDB' : (144, 4, 5, 'V', 'VVVVV'),
+ 'DEC2BIN' : ( -1, 1, 2, 'V', 'VV'),
+ 'DEC2HEX' : ( -1, 1, 2, 'V', 'VV'),
+ 'DEC2OCT' : ( -1, 1, 2, 'V', 'VV'),
+ 'DEGREES' : (343, 1, 1, 'V', 'V'),
+ 'DELTA' : ( -1, 1, 2, 'V', 'VV'),
+ 'DEVSQ' : (318, 1, 30, 'V', 'D+'),
+ 'DGET' : (235, 3, 3, 'V', 'RRR'),
+ 'DISC' : ( -1, 4, 5, 'V', 'VVVVV'),
+ 'DMAX' : ( 44, 3, 3, 'V', 'RRR'),
+ 'DMIN' : ( 43, 3, 3, 'V', 'RRR'),
+ 'DOLLAR' : ( 13, 1, 2, 'V', 'VV'),
+ 'DOLLARDE' : ( -1, 2, 2, 'V', 'VV'),
+ 'DOLLARFR' : ( -1, 2, 2, 'V', 'VV'),
+ 'DPRODUCT' : (189, 3, 3, 'V', 'RRR'),
+ 'DSTDEV' : ( 45, 3, 3, 'V', 'RRR'),
+ 'DSTDEVP' : (195, 3, 3, 'V', 'RRR'),
+ 'DSUM' : ( 41, 3, 3, 'V', 'RRR'),
+ 'DURATION' : ( -1, 5, 6, 'V', 'VVVVVV'),
+ 'DVAR' : ( 47, 3, 3, 'V', 'RRR'),
+ 'DVARP' : (196, 3, 3, 'V', 'RRR'),
+ 'EDATE' : ( -1, 2, 2, 'V', 'VV'),
+ 'EFFECT' : ( -1, 2, 2, 'V', 'VV'),
+ 'EOMONTH' : ( -1, 1, 2, 'V', 'VV'),
+ 'ERF' : ( -1, 1, 2, 'V', 'VV'),
+ 'ERFC' : ( -1, 1, 1, 'V', 'V'),
+ 'ERROR.TYPE' : (261, 1, 1, 'V', 'V'),
+ 'EVEN' : (279, 1, 1, 'V', 'V'),
+ 'EXACT' : (117, 2, 2, 'V', 'VV'),
+ 'EXP' : ( 21, 1, 1, 'V', 'V'),
+ 'EXPONDIST' : (280, 3, 3, 'V', 'VVV'),
+ 'FACT' : (184, 1, 1, 'V', 'V'),
+ 'FACTDOUBLE' : ( -1, 1, 1, 'V', 'V'),
+ 'FALSE' : ( 35, 0, 0, 'V', '-'),
+ 'FDIST' : (281, 3, 3, 'V', 'VVV'),
+ 'FIND' : (124, 2, 3, 'V', 'VVV'),
+ 'FINDB' : (205, 2, 3, 'V', 'VVV'),
+ 'FINV' : (282, 3, 3, 'V', 'VVV'),
+ 'FISHER' : (283, 1, 1, 'V', 'V'),
+ 'FISHERINV' : (284, 1, 1, 'V', 'V'),
+ 'FIXED' : ( 14, 2, 3, 'V', 'VVV'),
+ 'FLOOR' : (285, 2, 2, 'V', 'VV'),
+ 'FORECAST' : (309, 3, 3, 'V', 'VAA'),
+ 'FREQUENCY' : (252, 2, 2, 'A', 'RR'),
+ 'FTEST' : (310, 2, 2, 'V', 'AA'),
+ 'FV' : ( 57, 3, 5, 'V', 'VVVVV'),
+ 'FVSCHEDULE' : ( -1, 2, 2, 'V', 'VA'),
+ 'GAMMADIST' : (286, 4, 4, 'V', 'VVVV'),
+ 'GAMMAINV' : (287, 3, 3, 'V', 'VVV'),
+ 'GAMMALN' : (271, 1, 1, 'V', 'V'),
+ 'GCD' : ( -1, 1, 29, 'V', 'V+'),
+ 'GEOMEAN' : (319, 1, 30, 'V', 'D+'),
+ 'GESTEP' : ( -1, 1, 2, 'V', 'VV'),
+ 'GETPIVOTDATA': (358, 2, 30, 'A', 'VAV+'),
+ 'GROWTH' : ( 52, 1, 4, 'A', 'RRRV'),
+ 'HARMEAN' : (320, 1, 30, 'V', 'D+'),
+ 'HEX2BIN' : ( -1, 1, 2, 'V', 'VV'),
+ 'HEX2DEC' : ( -1, 1, 1, 'V', 'V'),
+ 'HEX2OCT' : ( -1, 1, 2, 'V', 'VV'),
+ 'HLOOKUP' : (101, 3, 4, 'V', 'VRRV'),
+ 'HOUR' : ( 71, 1, 1, 'V', 'V'),
+ 'HYPERLINK' : (359, 1, 2, 'V', 'VV'),
+ 'HYPGEOMDIST' : (289, 4, 4, 'V', 'VVVV'),
+ 'IF' : ( 1, 2, 3, 'R', 'VRR'),
+ 'IMABS' : ( -1, 1, 1, 'V', 'V'),
+ 'IMAGINARY' : ( -1, 1, 1, 'V', 'V'),
+ 'IMARGUMENT' : ( -1, 1, 1, 'V', 'V'),
+ 'IMCONJUGATE' : ( -1, 1, 1, 'V', 'V'),
+ 'IMCOS' : ( -1, 1, 1, 'V', 'V'),
+ 'IMDIV' : ( -1, 2, 2, 'V', 'VV'),
+ 'IMEXP' : ( -1, 1, 1, 'V', 'V'),
+ 'IMLN' : ( -1, 1, 1, 'V', 'V'),
+ 'IMLOG10' : ( -1, 1, 1, 'V', 'V'),
+ 'IMLOG2' : ( -1, 1, 1, 'V', 'V'),
+ 'IMPOWER' : ( -1, 2, 2, 'V', 'VV'),
+ 'IMPRODUCT' : ( -1, 2, 2, 'V', 'VV'),
+ 'IMREAL' : ( -1, 1, 1, 'V', 'V'),
+ 'IMSIN' : ( -1, 1, 1, 'V', 'V'),
+ 'IMSQRT' : ( -1, 1, 1, 'V', 'V'),
+ 'IMSUB' : ( -1, 2, 2, 'V', 'VV'),
+ 'IMSUM' : ( -1, 1, 29, 'V', 'V+'),
+ 'INDEX' : ( 29, 2, 4, 'R', 'RVVV'),
+ 'INDIRECT' : (148, 1, 2, 'R', 'VV'),
+ 'INFO' : (244, 1, 1, 'V', 'V'),
+ 'INT' : ( 25, 1, 1, 'V', 'V'),
+ 'INTERCEPT' : (311, 2, 2, 'V', 'AA'),
+ 'INTRATE' : ( -1, 4, 5, 'V', 'VVVVV'),
+ 'IPMT' : (167, 4, 6, 'V', 'VVVVVV'),
+ 'IRR' : ( 62, 1, 2, 'V', 'RV'),
+ 'ISBLANK' : (129, 1, 1, 'V', 'V'),
+ 'ISERR' : (126, 1, 1, 'V', 'V'),
+ 'ISERROR' : ( 3, 1, 1, 'V', 'V'),
+ 'ISEVEN' : ( -1, 1, 1, 'V', 'V'),
+ 'ISLOGICAL' : (198, 1, 1, 'V', 'V'),
+ 'ISNA' : ( 2, 1, 1, 'V', 'V'),
+ 'ISNONTEXT' : (190, 1, 1, 'V', 'V'),
+ 'ISNUMBER' : (128, 1, 1, 'V', 'V'),
+ 'ISODD' : ( -1, 1, 1, 'V', 'V'),
+ 'ISPMT' : (350, 4, 4, 'V', 'VVVV'),
+ 'ISREF' : (105, 1, 1, 'V', 'R'),
+ 'ISTEXT' : (127, 1, 1, 'V', 'V'),
+ 'KURT' : (322, 1, 30, 'V', 'D+'),
+ 'LARGE' : (325, 2, 2, 'V', 'RV'),
+ 'LCM' : ( -1, 1, 29, 'V', 'V+'),
+ 'LEFT' : (115, 1, 2, 'V', 'VV'),
+ 'LEFTB' : (208, 1, 2, 'V', 'VV'),
+ 'LEN' : ( 32, 1, 1, 'V', 'V'),
+ 'LENB' : (211, 1, 1, 'V', 'V'),
+ 'LINEST' : ( 49, 1, 4, 'A', 'RRVV'),
+ 'LN' : ( 22, 1, 1, 'V', 'V'),
+ 'LOG' : (109, 1, 2, 'V', 'VV'),
+ 'LOG10' : ( 23, 1, 1, 'V', 'V'),
+ 'LOGEST' : ( 51, 1, 4, 'A', 'RRVV'),
+ 'LOGINV' : (291, 3, 3, 'V', 'VVV'),
+ 'LOGNORMDIST' : (290, 3, 3, 'V', 'VVV'),
+ 'LOOKUP' : ( 28, 2, 3, 'V', 'VRR'),
+ 'LOWER' : (112, 1, 1, 'V', 'V'),
+ 'MATCH' : ( 64, 2, 3, 'V', 'VRR'),
+ 'MAX' : ( 7, 1, 30, 'V', 'D+'),
+ 'MAXA' : (362, 1, 30, 'V', 'D+'),
+ 'MDETERM' : (163, 1, 1, 'V', 'A'),
+ 'MDURATION' : ( -1, 5, 6, 'V', 'VVVVVV'),
+ 'MEDIAN' : (227, 1, 30, 'V', 'D+'),
+ 'MID' : ( 31, 3, 3, 'V', 'VVV'),
+ 'MIDB' : (210, 3, 3, 'V', 'VVV'),
+ 'MIN' : ( 6, 1, 30, 'V', 'D+'),
+ 'MINA' : (363, 1, 30, 'V', 'D+'),
+ 'MINUTE' : ( 72, 1, 1, 'V', 'V'),
+ 'MINVERSE' : (164, 1, 1, 'A', 'A'),
+ 'MIRR' : ( 61, 3, 3, 'V', 'RVV'),
+ 'MMULT' : (165, 2, 2, 'A', 'AA'),
+ 'MOD' : ( 39, 2, 2, 'V', 'VV'),
+ 'MODE' : (330, 1, 30, 'V', 'A+'), ################ weird #################
+ 'MONTH' : ( 68, 1, 1, 'V', 'V'),
+ 'MROUND' : ( -1, 2, 2, 'V', 'VV'),
+ 'MULTINOMIAL' : ( -1, 1, 29, 'V', 'V+'),
+ 'N' : (131, 1, 1, 'V', 'R'),
+ 'NA' : ( 10, 0, 0, 'V', '-'),
+ 'NEGBINOMDIST': (292, 3, 3, 'V', 'VVV'),
+ 'NETWORKDAYS' : ( -1, 2, 3, 'V', 'VVR'),
+ 'NOMINAL' : ( -1, 2, 2, 'V', 'VV'),
+ 'NORMDIST' : (293, 4, 4, 'V', 'VVVV'),
+ 'NORMINV' : (295, 3, 3, 'V', 'VVV'),
+ 'NORMSDIST' : (294, 1, 1, 'V', 'V'),
+ 'NORMSINV' : (296, 1, 1, 'V', 'V'),
+ 'NOT' : ( 38, 1, 1, 'V', 'V'),
+ 'NOW' : ( 74, 0, 0, 'V', '-'),
+ 'NPER' : ( 58, 3, 5, 'V', 'VVVVV'),
+ 'NPV' : ( 11, 2, 30, 'V', 'VD+'),
+ 'OCT2BIN' : ( -1, 1, 2, 'V', 'VV'),
+ 'OCT2DEC' : ( -1, 1, 1, 'V', 'V'),
+ 'OCT2HEX' : ( -1, 1, 2, 'V', 'VV'),
+ 'ODD' : (298, 1, 1, 'V', 'V'),
+ 'ODDFPRICE' : ( -1, 9, 9, 'V', 'VVVVVVVVV'),
+ 'ODDFYIELD' : ( -1, 9, 9, 'V', 'VVVVVVVVV'),
+ 'ODDLPRICE' : ( -1, 8, 8, 'V', 'VVVVVVVV'),
+ 'ODDLYIELD' : ( -1, 8, 8, 'V', 'VVVVVVVV'),
+ 'OFFSET' : ( 78, 3, 5, 'R', 'RVVVV'),
+ 'OR' : ( 37, 1, 30, 'V', 'D+'),
+ 'PEARSON' : (312, 2, 2, 'V', 'AA'),
+ 'PERCENTILE' : (328, 2, 2, 'V', 'RV'),
+ 'PERCENTRANK' : (329, 2, 3, 'V', 'RVV'),
+ 'PERMUT' : (299, 2, 2, 'V', 'VV'),
+ 'PHONETIC' : (360, 1, 1, 'V', 'R'),
+ 'PI' : ( 19, 0, 0, 'V', '-'),
+ 'PMT' : ( 59, 3, 5, 'V', 'VVVVV'),
+ 'POISSON' : (300, 3, 3, 'V', 'VVV'),
+ 'POWER' : (337, 2, 2, 'V', 'VV'),
+ 'PPMT' : (168, 4, 6, 'V', 'VVVVVV'),
+ 'PRICE' : ( -1, 6, 7, 'V', 'VVVVVVV'),
+ 'PRICEDISC' : ( -1, 4, 5, 'V', 'VVVVV'),
+ 'PRICEMAT' : ( -1, 5, 6, 'V', 'VVVVVV'),
+ 'PROB' : (317, 3, 4, 'V', 'AAVV'),
+ 'PRODUCT' : (183, 1, 30, 'V', 'D+'),
+ 'PROPER' : (114, 1, 1, 'V', 'V'),
+ 'PV' : ( 56, 3, 5, 'V', 'VVVVV'),
+ 'QUARTILE' : (327, 2, 2, 'V', 'RV'),
+ 'QUOTIENT' : ( -1, 2, 2, 'V', 'VV'),
+ 'RADIANS' : (342, 1, 1, 'V', 'V'),
+ 'RAND' : ( 63, 0, 0, 'V', '-'),
+ 'RANDBETWEEN' : ( -1, 2, 2, 'V', 'VV'),
+ 'RANK' : (216, 2, 3, 'V', 'VRV'),
+ 'RATE' : ( 60, 3, 6, 'V', 'VVVVVV'),
+ 'RECEIVED' : ( -1, 4, 5, 'V', 'VVVVV'),
+ 'REPLACE' : (119, 4, 4, 'V', 'VVVV'),
+ 'REPLACEB' : (207, 4, 4, 'V', 'VVVV'),
+ 'REPT' : ( 30, 2, 2, 'V', 'VV'),
+ 'RIGHT' : (116, 1, 2, 'V', 'VV'),
+ 'RIGHTB' : (209, 1, 2, 'V', 'VV'),
+ 'ROMAN' : (354, 1, 2, 'V', 'VV'),
+ 'ROUND' : ( 27, 2, 2, 'V', 'VV'),
+ 'ROUNDDOWN' : (213, 2, 2, 'V', 'VV'),
+ 'ROUNDUP' : (212, 2, 2, 'V', 'VV'),
+ 'ROW' : ( 8, 0, 1, 'V', 'R'),
+ 'ROWS' : ( 76, 1, 1, 'V', 'R'),
+ 'RSQ' : (313, 2, 2, 'V', 'AA'),
+ 'RTD' : (379, 3, 30, 'A', 'VVV+'),
+ 'SEARCH' : ( 82, 2, 3, 'V', 'VVV'),
+ 'SEARCHB' : (206, 2, 3, 'V', 'VVV'),
+ 'SECOND' : ( 73, 1, 1, 'V', 'V'),
+ 'SERIESSUM' : ( -1, 4, 4, 'V', 'VVVA'),
+ 'SIGN' : ( 26, 1, 1, 'V', 'V'),
+ 'SIN' : ( 15, 1, 1, 'V', 'V'),
+ 'SINH' : (229, 1, 1, 'V', 'V'),
+ 'SKEW' : (323, 1, 30, 'V', 'D+'),
+ 'SLN' : (142, 3, 3, 'V', 'VVV'),
+ 'SLOPE' : (315, 2, 2, 'V', 'AA'),
+ 'SMALL' : (326, 2, 2, 'V', 'RV'),
+ 'SQRT' : ( 20, 1, 1, 'V', 'V'),
+ 'SQRTPI' : ( -1, 1, 1, 'V', 'V'),
+ 'STANDARDIZE' : (297, 3, 3, 'V', 'VVV'),
+ 'STDEV' : ( 12, 1, 30, 'V', 'D+'),
+ 'STDEVA' : (366, 1, 30, 'V', 'D+'),
+ 'STDEVP' : (193, 1, 30, 'V', 'D+'),
+ 'STDEVPA' : (364, 1, 30, 'V', 'D+'),
+ 'STEYX' : (314, 2, 2, 'V', 'AA'),
+ 'SUBSTITUTE' : (120, 3, 4, 'V', 'VVVV'),
+ 'SUBTOTAL' : (344, 2, 30, 'V', 'VR+'),
+ 'SUM' : ( 4, 1, 30, 'V', 'D+'),
+ 'SUMIF' : (345, 2, 3, 'V', 'RVR'),
+ 'SUMPRODUCT' : (228, 1, 30, 'V', 'A+'),
+ 'SUMSQ' : (321, 1, 30, 'V', 'D+'),
+ 'SUMX2MY2' : (304, 2, 2, 'V', 'AA'),
+ 'SUMX2PY2' : (305, 2, 2, 'V', 'AA'),
+ 'SUMXMY2' : (303, 2, 2, 'V', 'AA'),
+ 'SYD' : (143, 4, 4, 'V', 'VVVV'),
+ 'T' : (130, 1, 1, 'V', 'R'),
+ 'TAN' : ( 17, 1, 1, 'V', 'V'),
+ 'TANH' : (231, 1, 1, 'V', 'V'),
+ 'TBILLEQ' : ( -1, 3, 3, 'V', 'VVV'),
+ 'TBILLPRICE' : ( -1, 3, 3, 'V', 'VVV'),
+ 'TBILLYIELD' : ( -1, 3, 3, 'V', 'VVV'),
+ 'TDIST' : (301, 3, 3, 'V', 'VVV'),
+ 'TEXT' : ( 48, 2, 2, 'V', 'VV'),
+ 'TIME' : ( 66, 3, 3, 'V', 'VVV'),
+ 'TIMEVALUE' : (141, 1, 1, 'V', 'V'),
+ 'TINV' : (332, 2, 2, 'V', 'VV'),
+ 'TODAY' : (221, 0, 0, 'V', '-'),
+ 'TRANSPOSE' : ( 83, 1, 1, 'A', 'A'),
+ 'TREND' : ( 50, 1, 4, 'A', 'RRRV'),
+ 'TRIM' : (118, 1, 1, 'V', 'V'),
+ 'TRIMMEAN' : (331, 2, 2, 'V', 'RV'),
+ 'TRUE' : ( 34, 0, 0, 'V', '-'),
+ 'TRUNC' : (197, 1, 2, 'V', 'VV'),
+ 'TTEST' : (316, 4, 4, 'V', 'AAVV'),
+ 'TYPE' : ( 86, 1, 1, 'V', 'V'),
+ 'UPPER' : (113, 1, 1, 'V', 'V'),
+ 'USDOLLAR' : (204, 1, 2, 'V', 'VV'),
+ 'VALUE' : ( 33, 1, 1, 'V', 'V'),
+ 'VAR' : ( 46, 1, 30, 'V', 'D+'),
+ 'VARA' : (367, 1, 30, 'V', 'D+'),
+ 'VARP' : (194, 1, 30, 'V', 'D+'),
+ 'VARPA' : (365, 1, 30, 'V', 'D+'),
+ 'VDB' : (222, 5, 7, 'V', 'VVVVVVV'),
+ 'VLOOKUP' : (102, 3, 4, 'V', 'VRRV'),
+ 'WEEKDAY' : ( 70, 1, 2, 'V', 'VV'),
+ 'WEEKNUM' : ( -1, 1, 2, 'V', 'VV'),
+ 'WEIBULL' : (302, 4, 4, 'V', 'VVVV'),
+ 'WORKDAY' : ( -1, 2, 3, 'V', 'VVR'),
+ 'XIRR' : ( -1, 2, 3, 'V', 'AAV'),
+ 'XNPV' : ( -1, 3, 3, 'V', 'VAA'),
+ 'YEAR' : ( 69, 1, 1, 'V', 'V'),
+ 'YEARFRAC' : ( -1, 2, 3, 'V', 'VVV'),
+ 'YIELD' : ( -1, 6, 7, 'V', 'VVVVVVV'),
+ 'YIELDDISC' : ( -1, 4, 5, 'V', 'VVVVV'),
+ 'YIELDMAT' : ( -1, 5, 6, 'V', 'VVVVVV'),
+ 'ZTEST' : (324, 2, 3, 'V', 'RVV'),
+ }
+
+# Formulas Parse things
+
+ptgExp = 0x01
+ptgTbl = 0x02
+ptgAdd = 0x03
+ptgSub = 0x04
+ptgMul = 0x05
+ptgDiv = 0x06
+ptgPower = 0x07
+ptgConcat = 0x08
+ptgLT = 0x09
+ptgLE = 0x0a
+ptgEQ = 0x0b
+ptgGE = 0x0c
+ptgGT = 0x0d
+ptgNE = 0x0e
+ptgIsect = 0x0f
+ptgUnion = 0x10
+ptgRange = 0x11
+ptgUplus = 0x12
+ptgUminus = 0x13
+ptgPercent = 0x14
+ptgParen = 0x15
+ptgMissArg = 0x16
+ptgStr = 0x17
+ptgExtend = 0x18
+ptgAttr = 0x19
+ptgSheet = 0x1a
+ptgEndSheet = 0x1b
+ptgErr = 0x1c
+ptgBool = 0x1d
+ptgInt = 0x1e
+ptgNum = 0x1f
+
+ptgArrayR = 0x20
+ptgFuncR = 0x21
+ptgFuncVarR = 0x22
+ptgNameR = 0x23
+ptgRefR = 0x24
+ptgAreaR = 0x25
+ptgMemAreaR = 0x26
+ptgMemErrR = 0x27
+ptgMemNoMemR = 0x28
+ptgMemFuncR = 0x29
+ptgRefErrR = 0x2a
+ptgAreaErrR = 0x2b
+ptgRefNR = 0x2c
+ptgAreaNR = 0x2d
+ptgMemAreaNR = 0x2e
+ptgMemNoMemNR = 0x2f
+ptgNameXR = 0x39
+ptgRef3dR = 0x3a
+ptgArea3dR = 0x3b
+ptgRefErr3dR = 0x3c
+ptgAreaErr3dR = 0x3d
+
+ptgArrayV = 0x40
+ptgFuncV = 0x41
+ptgFuncVarV = 0x42
+ptgNameV = 0x43
+ptgRefV = 0x44
+ptgAreaV = 0x45
+ptgMemAreaV = 0x46
+ptgMemErrV = 0x47
+ptgMemNoMemV = 0x48
+ptgMemFuncV = 0x49
+ptgRefErrV = 0x4a
+ptgAreaErrV = 0x4b
+ptgRefNV = 0x4c
+ptgAreaNV = 0x4d
+ptgMemAreaNV = 0x4e
+ptgMemNoMemNV = 0x4f
+ptgFuncCEV = 0x58
+ptgNameXV = 0x59
+ptgRef3dV = 0x5a
+ptgArea3dV = 0x5b
+ptgRefErr3dV = 0x5c
+ptgAreaErr3dV = 0x5d
+
+ptgArrayA = 0x60
+ptgFuncA = 0x61
+ptgFuncVarA = 0x62
+ptgNameA = 0x63
+ptgRefA = 0x64
+ptgAreaA = 0x65
+ptgMemAreaA = 0x66
+ptgMemErrA = 0x67
+ptgMemNoMemA = 0x68
+ptgMemFuncA = 0x69
+ptgRefErrA = 0x6a
+ptgAreaErrA = 0x6b
+ptgRefNA = 0x6c
+ptgAreaNA = 0x6d
+ptgMemAreaNA = 0x6e
+ptgMemNoMemNA = 0x6f
+ptgFuncCEA = 0x78
+ptgNameXA = 0x79
+ptgRef3dA = 0x7a
+ptgArea3dA = 0x7b
+ptgRefErr3dA = 0x7c
+ptgAreaErr3dA = 0x7d
+
+
+PtgNames = {
+ ptgExp : "ptgExp",
+ ptgTbl : "ptgTbl",
+ ptgAdd : "ptgAdd",
+ ptgSub : "ptgSub",
+ ptgMul : "ptgMul",
+ ptgDiv : "ptgDiv",
+ ptgPower : "ptgPower",
+ ptgConcat : "ptgConcat",
+ ptgLT : "ptgLT",
+ ptgLE : "ptgLE",
+ ptgEQ : "ptgEQ",
+ ptgGE : "ptgGE",
+ ptgGT : "ptgGT",
+ ptgNE : "ptgNE",
+ ptgIsect : "ptgIsect",
+ ptgUnion : "ptgUnion",
+ ptgRange : "ptgRange",
+ ptgUplus : "ptgUplus",
+ ptgUminus : "ptgUminus",
+ ptgPercent : "ptgPercent",
+ ptgParen : "ptgParen",
+ ptgMissArg : "ptgMissArg",
+ ptgStr : "ptgStr",
+ ptgExtend : "ptgExtend",
+ ptgAttr : "ptgAttr",
+ ptgSheet : "ptgSheet",
+ ptgEndSheet : "ptgEndSheet",
+ ptgErr : "ptgErr",
+ ptgBool : "ptgBool",
+ ptgInt : "ptgInt",
+ ptgNum : "ptgNum",
+ ptgArrayR : "ptgArrayR",
+ ptgFuncR : "ptgFuncR",
+ ptgFuncVarR : "ptgFuncVarR",
+ ptgNameR : "ptgNameR",
+ ptgRefR : "ptgRefR",
+ ptgAreaR : "ptgAreaR",
+ ptgMemAreaR : "ptgMemAreaR",
+ ptgMemErrR : "ptgMemErrR",
+ ptgMemNoMemR : "ptgMemNoMemR",
+ ptgMemFuncR : "ptgMemFuncR",
+ ptgRefErrR : "ptgRefErrR",
+ ptgAreaErrR : "ptgAreaErrR",
+ ptgRefNR : "ptgRefNR",
+ ptgAreaNR : "ptgAreaNR",
+ ptgMemAreaNR : "ptgMemAreaNR",
+ ptgMemNoMemNR : "ptgMemNoMemNR",
+ ptgNameXR : "ptgNameXR",
+ ptgRef3dR : "ptgRef3dR",
+ ptgArea3dR : "ptgArea3dR",
+ ptgRefErr3dR : "ptgRefErr3dR",
+ ptgAreaErr3dR : "ptgAreaErr3dR",
+ ptgArrayV : "ptgArrayV",
+ ptgFuncV : "ptgFuncV",
+ ptgFuncVarV : "ptgFuncVarV",
+ ptgNameV : "ptgNameV",
+ ptgRefV : "ptgRefV",
+ ptgAreaV : "ptgAreaV",
+ ptgMemAreaV : "ptgMemAreaV",
+ ptgMemErrV : "ptgMemErrV",
+ ptgMemNoMemV : "ptgMemNoMemV",
+ ptgMemFuncV : "ptgMemFuncV",
+ ptgRefErrV : "ptgRefErrV",
+ ptgAreaErrV : "ptgAreaErrV",
+ ptgRefNV : "ptgRefNV",
+ ptgAreaNV : "ptgAreaNV",
+ ptgMemAreaNV : "ptgMemAreaNV",
+ ptgMemNoMemNV : "ptgMemNoMemNV",
+ ptgFuncCEV : "ptgFuncCEV",
+ ptgNameXV : "ptgNameXV",
+ ptgRef3dV : "ptgRef3dV",
+ ptgArea3dV : "ptgArea3dV",
+ ptgRefErr3dV : "ptgRefErr3dV",
+ ptgAreaErr3dV : "ptgAreaErr3dV",
+ ptgArrayA : "ptgArrayA",
+ ptgFuncA : "ptgFuncA",
+ ptgFuncVarA : "ptgFuncVarA",
+ ptgNameA : "ptgNameA",
+ ptgRefA : "ptgRefA",
+ ptgAreaA : "ptgAreaA",
+ ptgMemAreaA : "ptgMemAreaA",
+ ptgMemErrA : "ptgMemErrA",
+ ptgMemNoMemA : "ptgMemNoMemA",
+ ptgMemFuncA : "ptgMemFuncA",
+ ptgRefErrA : "ptgRefErrA",
+ ptgAreaErrA : "ptgAreaErrA",
+ ptgRefNA : "ptgRefNA",
+ ptgAreaNA : "ptgAreaNA",
+ ptgMemAreaNA : "ptgMemAreaNA",
+ ptgMemNoMemNA : "ptgMemNoMemNA",
+ ptgFuncCEA : "ptgFuncCEA",
+ ptgNameXA : "ptgNameXA",
+ ptgRef3dA : "ptgRef3dA",
+ ptgArea3dA : "ptgArea3dA",
+ ptgRefErr3dA : "ptgRefErr3dA",
+ ptgAreaErr3dA : "ptgAreaErr3dA"
+}
+
+
+error_msg_by_code = {
+ 0x00: u"#NULL!", # intersection of two cell ranges is empty
+ 0x07: u"#DIV/0!", # division by zero
+ 0x0F: u"#VALUE!", # wrong type of operand
+ 0x17: u"#REF!", # illegal or deleted cell reference
+ 0x1D: u"#NAME?", # wrong function or range name
+ 0x24: u"#NUM!", # value range overflow
+ 0x2A: u"#N/A" # argument or function not available
+}
diff --git a/.venv/lib/python3.9/site-packages/xlwt/Formatting.py b/.venv/lib/python3.9/site-packages/xlwt/Formatting.py
new file mode 100644
index 00000000..5405d659
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt/Formatting.py
@@ -0,0 +1,265 @@
+'''
+Formatting
+==========
+
+The XF record is able to store explicit cell formatting attributes or the
+attributes of a cell style. Explicit formatting includes the reference to
+a cell style XF record. This allows to extend a defined cell style with
+some explicit attributes. The formatting attributes are divided into
+6 groups:
+
+============= ==========================================================
+Group Attributes
+============= ==========================================================
+Number format Number format index (index to FORMAT record)
+Font Font index (index to FONT record)
+Alignment Horizontal and vertical alignment, text wrap, indentation,
+ orientation/rotation, text direction
+Border Border line styles and colours
+Background Background area style and colours
+Protection Cell locked, formula hidden
+============= ==========================================================
+
+For each group a flag in the cell XF record specifies whether to use the
+attributes contained in that XF record or in the referenced style
+XF record. In style XF records, these flags specify whether the attributes
+will overwrite explicit cell formatting when the style is applied to
+a cell. Changing a cell style (without applying this style to a cell) will
+change all cells which already use that style and do not contain explicit
+cell attributes for the changed style attributes. If a cell XF record does
+not contain explicit attributes in a group (if the attribute group flag
+is not set), it repeats the attributes of its style XF record.
+
+'''
+
+from . import BIFFRecords
+
+class Font(object):
+
+ ESCAPEMENT_NONE = 0x00
+ ESCAPEMENT_SUPERSCRIPT = 0x01
+ ESCAPEMENT_SUBSCRIPT = 0x02
+
+ UNDERLINE_NONE = 0x00
+ UNDERLINE_SINGLE = 0x01
+ UNDERLINE_SINGLE_ACC = 0x21
+ UNDERLINE_DOUBLE = 0x02
+ UNDERLINE_DOUBLE_ACC = 0x22
+
+ FAMILY_NONE = 0x00
+ FAMILY_ROMAN = 0x01
+ FAMILY_SWISS = 0x02
+ FAMILY_MODERN = 0x03
+ FAMILY_SCRIPT = 0x04
+ FAMILY_DECORATIVE = 0x05
+
+ CHARSET_ANSI_LATIN = 0x00
+ CHARSET_SYS_DEFAULT = 0x01
+ CHARSET_SYMBOL = 0x02
+ CHARSET_APPLE_ROMAN = 0x4D
+ CHARSET_ANSI_JAP_SHIFT_JIS = 0x80
+ CHARSET_ANSI_KOR_HANGUL = 0x81
+ CHARSET_ANSI_KOR_JOHAB = 0x82
+ CHARSET_ANSI_CHINESE_GBK = 0x86
+ CHARSET_ANSI_CHINESE_BIG5 = 0x88
+ CHARSET_ANSI_GREEK = 0xA1
+ CHARSET_ANSI_TURKISH = 0xA2
+ CHARSET_ANSI_VIETNAMESE = 0xA3
+ CHARSET_ANSI_HEBREW = 0xB1
+ CHARSET_ANSI_ARABIC = 0xB2
+ CHARSET_ANSI_BALTIC = 0xBA
+ CHARSET_ANSI_CYRILLIC = 0xCC
+ CHARSET_ANSI_THAI = 0xDE
+ CHARSET_ANSI_LATIN_II = 0xEE
+ CHARSET_OEM_LATIN_I = 0xFF
+
+ def __init__(self):
+ # twip = 1/20 of a point = 1/1440 of a inch
+ # usually resolution == 96 pixels per 1 inch
+ # (rarely 120 pixels per 1 inch or another one)
+
+ self.height = 0x00C8 # 200: this is font with height 10 points
+ self.italic = False
+ self.struck_out = False
+ self.outline = False
+ self.shadow = False
+ self.colour_index = 0x7FFF
+ self.bold = False
+ self._weight = 0x0190 # 0x02BC gives bold font
+ self.escapement = self.ESCAPEMENT_NONE
+ self.underline = self.UNDERLINE_NONE
+ self.family = self.FAMILY_NONE
+ self.charset = self.CHARSET_SYS_DEFAULT
+ self.name = 'Arial'
+
+ def get_biff_record(self):
+ height = self.height
+
+ options = 0x00
+ if self.bold:
+ options |= 0x01
+ self._weight = 0x02BC
+ if self.italic:
+ options |= 0x02
+ if self.underline != self.UNDERLINE_NONE:
+ options |= 0x04
+ if self.struck_out:
+ options |= 0x08
+ if self.outline:
+ options |= 0x010
+ if self.shadow:
+ options |= 0x020
+
+ colour_index = self.colour_index
+ weight = self._weight
+ escapement = self.escapement
+ underline = self.underline
+ family = self.family
+ charset = self.charset
+ name = self.name
+
+ return BIFFRecords.FontRecord(height, options, colour_index, weight, escapement,
+ underline, family, charset,
+ name)
+
+ def _search_key(self):
+ return (
+ self.height,
+ self.italic,
+ self.struck_out,
+ self.outline,
+ self.shadow,
+ self.colour_index,
+ self.bold,
+ self._weight,
+ self.escapement,
+ self.underline,
+ self.family,
+ self.charset,
+ self.name,
+ )
+
+class Alignment(object):
+ HORZ_GENERAL = 0x00
+ HORZ_LEFT = 0x01
+ HORZ_CENTER = 0x02
+ HORZ_RIGHT = 0x03
+ HORZ_FILLED = 0x04
+ HORZ_JUSTIFIED = 0x05 # BIFF4-BIFF8X
+ HORZ_CENTER_ACROSS_SEL = 0x06 # Centred across selection (BIFF4-BIFF8X)
+ HORZ_DISTRIBUTED = 0x07 # Distributed (BIFF8X)
+
+ VERT_TOP = 0x00
+ VERT_CENTER = 0x01
+ VERT_BOTTOM = 0x02
+ VERT_JUSTIFIED = 0x03 # Justified (BIFF5-BIFF8X)
+ VERT_DISTRIBUTED = 0x04 # Distributed (BIFF8X)
+
+ DIRECTION_GENERAL = 0x00 # BIFF8X
+ DIRECTION_LR = 0x01
+ DIRECTION_RL = 0x02
+
+ ORIENTATION_NOT_ROTATED = 0x00
+ ORIENTATION_STACKED = 0x01
+ ORIENTATION_90_CC = 0x02
+ ORIENTATION_90_CW = 0x03
+
+ ROTATION_0_ANGLE = 0x00
+ ROTATION_STACKED = 0xFF
+
+ WRAP_AT_RIGHT = 0x01
+ NOT_WRAP_AT_RIGHT = 0x00
+
+ SHRINK_TO_FIT = 0x01
+ NOT_SHRINK_TO_FIT = 0x00
+
+ def __init__(self):
+ self.horz = self.HORZ_GENERAL
+ self.vert = self.VERT_BOTTOM
+ self.dire = self.DIRECTION_GENERAL
+ self.orie = self.ORIENTATION_NOT_ROTATED
+ self.rota = self.ROTATION_0_ANGLE
+ self.wrap = self.NOT_WRAP_AT_RIGHT
+ self.shri = self.NOT_SHRINK_TO_FIT
+ self.inde = 0
+ self.merg = 0
+
+ def _search_key(self):
+ return (
+ self.horz, self.vert, self.dire, self.orie, self.rota,
+ self.wrap, self.shri, self.inde, self.merg,
+ )
+
+class Borders(object):
+ NO_LINE = 0x00
+ THIN = 0x01
+ MEDIUM = 0x02
+ DASHED = 0x03
+ DOTTED = 0x04
+ THICK = 0x05
+ DOUBLE = 0x06
+ HAIR = 0x07
+ #The following for BIFF8
+ MEDIUM_DASHED = 0x08
+ THIN_DASH_DOTTED = 0x09
+ MEDIUM_DASH_DOTTED = 0x0A
+ THIN_DASH_DOT_DOTTED = 0x0B
+ MEDIUM_DASH_DOT_DOTTED = 0x0C
+ SLANTED_MEDIUM_DASH_DOTTED = 0x0D
+
+ NEED_DIAG1 = 0x01
+ NEED_DIAG2 = 0x01
+ NO_NEED_DIAG1 = 0x00
+ NO_NEED_DIAG2 = 0x00
+
+ def __init__(self):
+ self.left = self.NO_LINE
+ self.right = self.NO_LINE
+ self.top = self.NO_LINE
+ self.bottom = self.NO_LINE
+ self.diag = self.NO_LINE
+
+ self.left_colour = 0x40
+ self.right_colour = 0x40
+ self.top_colour = 0x40
+ self.bottom_colour = 0x40
+ self.diag_colour = 0x40
+
+ self.need_diag1 = self.NO_NEED_DIAG1
+ self.need_diag2 = self.NO_NEED_DIAG2
+
+ def _search_key(self):
+ return (
+ self.left, self.right, self.top, self.bottom, self.diag,
+ self.left_colour, self.right_colour, self.top_colour,
+ self.bottom_colour, self.diag_colour,
+ self.need_diag1, self.need_diag2,
+ )
+
+class Pattern(object):
+ # patterns 0x00 - 0x12
+ NO_PATTERN = 0x00
+ SOLID_PATTERN = 0x01
+
+ def __init__(self):
+ self.pattern = self.NO_PATTERN
+ self.pattern_fore_colour = 0x40
+ self.pattern_back_colour = 0x41
+
+ def _search_key(self):
+ return (
+ self.pattern,
+ self.pattern_fore_colour,
+ self.pattern_back_colour,
+ )
+
+class Protection(object):
+ def __init__(self):
+ self.cell_locked = 1
+ self.formula_hidden = 0
+
+ def _search_key(self):
+ return (
+ self.cell_locked,
+ self.formula_hidden,
+ )
diff --git a/.venv/lib/python3.9/site-packages/xlwt/Row.py b/.venv/lib/python3.9/site-packages/xlwt/Row.py
new file mode 100644
index 00000000..f597a9c7
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt/Row.py
@@ -0,0 +1,293 @@
+# -*- coding: windows-1252 -*-
+
+from decimal import Decimal
+from . import BIFFRecords
+from . import Style
+from .Cell import StrCell, BlankCell, NumberCell, FormulaCell, MulBlankCell, BooleanCell, ErrorCell, \
+ _get_cells_biff_data_mul
+from . import ExcelFormula
+import datetime as dt
+from .Formatting import Font
+from .compat import basestring, xrange, int_types, iteritems
+
+
+class Row(object):
+ __slots__ = [# private variables
+ "__idx",
+ "__parent",
+ "__parent_wb",
+ "__cells",
+ "__min_col_idx",
+ "__max_col_idx",
+ "__xf_index",
+ "__has_default_xf_index",
+ "__height_in_pixels",
+ # public variables
+ "height",
+ "has_default_height",
+ "height_mismatch",
+ "level",
+ "collapse",
+ "hidden",
+ "space_above",
+ "space_below"]
+
+ def __init__(self, rowx, parent_sheet):
+ if not (isinstance(rowx, int_types) and 0 <= rowx <= 65535):
+ raise ValueError("row index was %r, not allowed by .xls format" % rowx)
+ self.__idx = rowx
+ self.__parent = parent_sheet
+ self.__parent_wb = parent_sheet.get_parent()
+ self.__cells = {}
+ self.__min_col_idx = 0
+ self.__max_col_idx = 0
+ self.__xf_index = 0x0F
+ self.__has_default_xf_index = 0
+ self.__height_in_pixels = 0x11
+
+ self.height = 0x00FF
+ self.has_default_height = 0x00
+ self.height_mismatch = 0
+ self.level = 0
+ self.collapse = 0
+ self.hidden = 0
+ self.space_above = 0
+ self.space_below = 0
+
+
+ def __adjust_height(self, style):
+ twips = style.font.height
+ points = float(twips)/20.0
+ # Cell height in pixels can be calcuted by following approx. formula:
+ # cell height in pixels = font height in points * 83/50 + 2/5
+ # It works when screen resolution is 96 dpi
+ pix = int(round(points*83.0/50.0 + 2.0/5.0))
+ if pix > self.__height_in_pixels:
+ self.__height_in_pixels = pix
+
+
+ def __adjust_bound_col_idx(self, *args):
+ for arg in args:
+ iarg = int(arg)
+ if not ((0 <= iarg <= 255) and arg == iarg):
+ raise ValueError("column index (%r) not an int in range(256)" % arg)
+ sheet = self.__parent
+ if iarg < self.__min_col_idx:
+ self.__min_col_idx = iarg
+ if iarg > self.__max_col_idx:
+ self.__max_col_idx = iarg
+ if iarg < sheet.first_used_col:
+ sheet.first_used_col = iarg
+ if iarg > sheet.last_used_col:
+ sheet.last_used_col = iarg
+
+ def __excel_date_dt(self, date):
+ adj = False
+ if isinstance(date, dt.date):
+ if self.__parent_wb.dates_1904:
+ epoch_tuple = (1904, 1, 1)
+ else:
+ epoch_tuple = (1899, 12, 31)
+ adj = True
+ if isinstance(date, dt.datetime):
+ epoch = dt.datetime(*epoch_tuple)
+ else:
+ epoch = dt.date(*epoch_tuple)
+ else: # it's a datetime.time instance
+ date = dt.datetime.combine(dt.datetime(1900, 1, 1), date)
+ epoch = dt.datetime(1900, 1, 1)
+ delta = date - epoch
+ xldate = delta.days + delta.seconds / 86400.0
+ # Add a day for Excel's missing leap day in 1900
+ if adj and xldate > 59:
+ xldate += 1
+ return xldate
+
+ def get_height_in_pixels(self):
+ return self.__height_in_pixels
+
+
+ def set_style(self, style):
+ self.__adjust_height(style)
+ self.__xf_index = self.__parent_wb.add_style(style)
+ self.__has_default_xf_index = 1
+
+
+ def get_xf_index(self):
+ return self.__xf_index
+
+
+ def get_cells_count(self):
+ return len(self.__cells)
+
+
+ def get_min_col(self):
+ return self.__min_col_idx
+
+
+ def get_max_col(self):
+ return self.__max_col_idx
+
+
+ def get_row_biff_data(self):
+ height_options = (self.height & 0x07FFF)
+ height_options |= (self.has_default_height & 0x01) << 15
+
+ options = (self.level & 0x07) << 0
+ options |= (self.collapse & 0x01) << 4
+ options |= (self.hidden & 0x01) << 5
+ options |= (self.height_mismatch & 0x01) << 6
+ options |= (self.__has_default_xf_index & 0x01) << 7
+ options |= (0x01 & 0x01) << 8
+ options |= (self.__xf_index & 0x0FFF) << 16
+ options |= (self.space_above & 1) << 28
+ options |= (self.space_below & 1) << 29
+
+ return BIFFRecords.RowRecord(self.__idx, self.__min_col_idx,
+ self.__max_col_idx, height_options, options).get()
+
+ def insert_cell(self, col_index, cell_obj):
+ if col_index in self.__cells:
+ if not self.__parent._cell_overwrite_ok:
+ msg = "Attempt to overwrite cell: sheetname=%r rowx=%d colx=%d" \
+ % (self.__parent.name, self.__idx, col_index)
+ raise Exception(msg)
+ prev_cell_obj = self.__cells[col_index]
+ sst_idx = getattr(prev_cell_obj, 'sst_idx', None)
+ if sst_idx is not None:
+ self.__parent_wb.del_str(sst_idx)
+ self.__cells[col_index] = cell_obj
+
+ def insert_mulcells(self, colx1, colx2, cell_obj):
+ self.insert_cell(colx1, cell_obj)
+ for col_index in xrange(colx1+1, colx2+1):
+ self.insert_cell(col_index, None)
+
+ def get_cells_biff_data(self):
+ cell_items = [item for item in iteritems(self.__cells) if item[1] is not None]
+ cell_items.sort() # in column order
+ return _get_cells_biff_data_mul(self.__idx, cell_items)
+ # previously:
+ # return ''.join([cell.get_biff_data() for colx, cell in cell_items])
+
+ def get_index(self):
+ return self.__idx
+
+ def set_cell_text(self, colx, value, style=Style.default_style):
+ self.__adjust_height(style)
+ self.__adjust_bound_col_idx(colx)
+ xf_index = self.__parent_wb.add_style(style)
+ self.insert_cell(colx, StrCell(self.__idx, colx, xf_index, self.__parent_wb.add_str(value)))
+
+ def set_cell_blank(self, colx, style=Style.default_style):
+ self.__adjust_height(style)
+ self.__adjust_bound_col_idx(colx)
+ xf_index = self.__parent_wb.add_style(style)
+ self.insert_cell(colx, BlankCell(self.__idx, colx, xf_index))
+
+ def set_cell_mulblanks(self, first_colx, last_colx, style=Style.default_style):
+ assert 0 <= first_colx <= last_colx <= 255
+ self.__adjust_height(style)
+ self.__adjust_bound_col_idx(first_colx, last_colx)
+ xf_index = self.__parent_wb.add_style(style)
+ # ncols = last_colx - first_colx + 1
+ self.insert_mulcells(first_colx, last_colx, MulBlankCell(self.__idx, first_colx, last_colx, xf_index))
+
+ def set_cell_number(self, colx, number, style=Style.default_style):
+ self.__adjust_height(style)
+ self.__adjust_bound_col_idx(colx)
+ xf_index = self.__parent_wb.add_style(style)
+ self.insert_cell(colx, NumberCell(self.__idx, colx, xf_index, number))
+
+ def set_cell_date(self, colx, datetime_obj, style=Style.default_style):
+ self.__adjust_height(style)
+ self.__adjust_bound_col_idx(colx)
+ xf_index = self.__parent_wb.add_style(style)
+ self.insert_cell(colx,
+ NumberCell(self.__idx, colx, xf_index, self.__excel_date_dt(datetime_obj)))
+
+ def set_cell_formula(self, colx, formula, style=Style.default_style, calc_flags=0):
+ self.__adjust_height(style)
+ self.__adjust_bound_col_idx(colx)
+ xf_index = self.__parent_wb.add_style(style)
+ self.__parent_wb.add_sheet_reference(formula)
+ self.insert_cell(colx, FormulaCell(self.__idx, colx, xf_index, formula, calc_flags=0))
+
+ def set_cell_boolean(self, colx, value, style=Style.default_style):
+ self.__adjust_height(style)
+ self.__adjust_bound_col_idx(colx)
+ xf_index = self.__parent_wb.add_style(style)
+ self.insert_cell(colx, BooleanCell(self.__idx, colx, xf_index, bool(value)))
+
+ def set_cell_error(self, colx, error_string_or_code, style=Style.default_style):
+ self.__adjust_height(style)
+ self.__adjust_bound_col_idx(colx)
+ xf_index = self.__parent_wb.add_style(style)
+ self.insert_cell(colx, ErrorCell(self.__idx, colx, xf_index, error_string_or_code))
+
+ def write(self, col, label, style=Style.default_style):
+ self.__adjust_height(style)
+ self.__adjust_bound_col_idx(col)
+ style_index = self.__parent_wb.add_style(style)
+ if isinstance(label, basestring):
+ if len(label) > 0:
+ self.insert_cell(col,
+ StrCell(self.__idx, col, style_index, self.__parent_wb.add_str(label))
+ )
+ else:
+ self.insert_cell(col, BlankCell(self.__idx, col, style_index))
+ elif isinstance(label, bool): # bool is subclass of int; test bool first
+ self.insert_cell(col, BooleanCell(self.__idx, col, style_index, label))
+ elif isinstance(label, int_types+(float, Decimal)):
+ self.insert_cell(col, NumberCell(self.__idx, col, style_index, label))
+ elif isinstance(label, (dt.datetime, dt.date, dt.time)):
+ date_number = self.__excel_date_dt(label)
+ self.insert_cell(col, NumberCell(self.__idx, col, style_index, date_number))
+ elif label is None:
+ self.insert_cell(col, BlankCell(self.__idx, col, style_index))
+ elif isinstance(label, ExcelFormula.Formula):
+ self.__parent_wb.add_sheet_reference(label)
+ self.insert_cell(col, FormulaCell(self.__idx, col, style_index, label))
+ elif isinstance(label, (list, tuple)):
+ self.__rich_text_helper(col, label, style, style_index)
+ else:
+ raise Exception("Unexpected data type %r" % type(label))
+
+ def set_cell_rich_text(self, col, rich_text_list, style=Style.default_style):
+ self.__adjust_height(style)
+ self.__adjust_bound_col_idx(col)
+ if not isinstance(rich_text_list, (list, tuple)):
+ raise Exception("Unexpected data type %r" % type(rich_text_list))
+ self.__rich_text_helper(col, rich_text_list, style)
+
+ def __rich_text_helper(self, col, rich_text_list, style, style_index=None):
+ if style_index is None:
+ style_index = self.__parent_wb.add_style(style)
+ default_font = None
+ rt = []
+ for data in rich_text_list:
+ if isinstance(data, basestring):
+ s = data
+ font = default_font
+ elif isinstance(data, (list, tuple)):
+ if not isinstance(data[0], basestring) or not isinstance(data[1], Font):
+ raise Exception ("Unexpected data type %r, %r" % (type(data[0]), type(data[1])))
+ s = data[0]
+ font = self.__parent_wb.add_font(data[1])
+ else:
+ raise Exception ("Unexpected data type %r" % type(data))
+ if s:
+ rt.append((s, font))
+ if default_font is None:
+ default_font = self.__parent_wb.add_font(style.font)
+ if rt:
+ self.insert_cell(col, StrCell(self.__idx, col, style_index, self.__parent_wb.add_rt(rt)))
+ else:
+ self.insert_cell(col, BlankCell(self.__idx, col, style_index))
+
+ write_blanks = set_cell_mulblanks
+ write_rich_text = set_cell_rich_text
+
+
+
+
diff --git a/.venv/lib/python3.9/site-packages/xlwt/Style.py b/.venv/lib/python3.9/site-packages/xlwt/Style.py
new file mode 100644
index 00000000..dc9fc949
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt/Style.py
@@ -0,0 +1,741 @@
+from __future__ import print_function
+# -*- coding: windows-1252 -*-
+
+from . import Formatting
+from .BIFFRecords import NumberFormatRecord, XFRecord, StyleRecord
+from .compat import basestring, xrange
+
+FIRST_USER_DEFINED_NUM_FORMAT_IDX = 164
+
+class XFStyle(object):
+
+ def __init__(self):
+ self.num_format_str = 'General'
+ self.font = Formatting.Font()
+ self.alignment = Formatting.Alignment()
+ self.borders = Formatting.Borders()
+ self.pattern = Formatting.Pattern()
+ self.protection = Formatting.Protection()
+
+default_style = XFStyle()
+
+class StyleCollection(object):
+ _std_num_fmt_list = [
+ 'general',
+ '0',
+ '0.00',
+ '#,##0',
+ '#,##0.00',
+ '"$"#,##0_);("$"#,##0)',
+ '"$"#,##0_);[Red]("$"#,##0)',
+ '"$"#,##0.00_);("$"#,##0.00)',
+ '"$"#,##0.00_);[Red]("$"#,##0.00)',
+ '0%',
+ '0.00%',
+ '0.00E+00',
+ '# ?/?',
+ '# ??/??',
+ 'M/D/YY',
+ 'D-MMM-YY',
+ 'D-MMM',
+ 'MMM-YY',
+ 'h:mm AM/PM',
+ 'h:mm:ss AM/PM',
+ 'h:mm',
+ 'h:mm:ss',
+ 'M/D/YY h:mm',
+ '_(#,##0_);(#,##0)',
+ '_(#,##0_);[Red](#,##0)',
+ '_(#,##0.00_);(#,##0.00)',
+ '_(#,##0.00_);[Red](#,##0.00)',
+ '_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)',
+ '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)',
+ '_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)',
+ '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)',
+ 'mm:ss',
+ '[h]:mm:ss',
+ 'mm:ss.0',
+ '##0.0E+0',
+ '@'
+ ]
+
+ def __init__(self, style_compression=0):
+ self.style_compression = style_compression
+ self.stats = [0, 0, 0, 0, 0, 0]
+ self._font_id2x = {}
+ self._font_x2id = {}
+ self._font_val2x = {}
+
+ for x in (0, 1, 2, 3, 5): # The font with index 4 is omitted in all BIFF versions
+ font = Formatting.Font()
+ search_key = font._search_key()
+ self._font_id2x[font] = x
+ self._font_x2id[x] = font
+ self._font_val2x[search_key] = x
+
+ self._xf_id2x = {}
+ self._xf_x2id = {}
+ self._xf_val2x = {}
+
+ self._num_formats = {}
+ for fmtidx, fmtstr in zip(range(0, 23), StyleCollection._std_num_fmt_list[0:23]):
+ self._num_formats[fmtstr] = fmtidx
+ for fmtidx, fmtstr in zip(range(37, 50), StyleCollection._std_num_fmt_list[23:]):
+ self._num_formats[fmtstr] = fmtidx
+
+ self.default_style = XFStyle()
+ self._default_xf = self._add_style(self.default_style)[0]
+
+ def add(self, style):
+ if style == None:
+ return 0x10
+ return self._add_style(style)[1]
+
+ def _add_style(self, style):
+ num_format_str = style.num_format_str
+ if num_format_str in self._num_formats:
+ num_format_idx = self._num_formats[num_format_str]
+ else:
+ num_format_idx = (
+ FIRST_USER_DEFINED_NUM_FORMAT_IDX
+ + len(self._num_formats)
+ - len(StyleCollection._std_num_fmt_list)
+ )
+ self._num_formats[num_format_str] = num_format_idx
+
+ font = style.font
+ if font in self._font_id2x:
+ font_idx = self._font_id2x[font]
+ self.stats[0] += 1
+ elif self.style_compression:
+ search_key = font._search_key()
+ font_idx = self._font_val2x.get(search_key)
+ if font_idx is not None:
+ self._font_id2x[font] = font_idx
+ self.stats[1] += 1
+ else:
+ font_idx = len(self._font_x2id) + 1 # Why plus 1? Font 4 is missing
+ self._font_id2x[font] = font_idx
+ self._font_val2x[search_key] = font_idx
+ self._font_x2id[font_idx] = font
+ self.stats[2] += 1
+ else:
+ font_idx = len(self._font_id2x) + 1
+ self._font_id2x[font] = font_idx
+ self.stats[2] += 1
+
+ gof = (style.alignment, style.borders, style.pattern, style.protection)
+ xf = (font_idx, num_format_idx) + gof
+ if xf in self._xf_id2x:
+ xf_index = self._xf_id2x[xf]
+ self.stats[3] += 1
+ elif self.style_compression == 2:
+ xf_key = (font_idx, num_format_idx) + tuple(obj._search_key() for obj in gof)
+ xf_index = self._xf_val2x.get(xf_key)
+ if xf_index is not None:
+ self._xf_id2x[xf] = xf_index
+ self.stats[4] += 1
+ else:
+ xf_index = 0x10 + len(self._xf_x2id)
+ self._xf_id2x[xf] = xf_index
+ self._xf_val2x[xf_key] = xf_index
+ self._xf_x2id[xf_index] = xf
+ self.stats[5] += 1
+ else:
+ xf_index = 0x10 + len(self._xf_id2x)
+ self._xf_id2x[xf] = xf_index
+ self.stats[5] += 1
+
+ if xf_index >= 0xFFF:
+ # 12 bits allowed, 0xFFF is a sentinel value
+ raise ValueError("More than 4094 XFs (styles)")
+
+ return xf, xf_index
+
+ def add_font(self, font):
+ return self._add_font(font)
+
+ def _add_font(self, font):
+ if font in self._font_id2x:
+ font_idx = self._font_id2x[font]
+ self.stats[0] += 1
+ elif self.style_compression:
+ search_key = font._search_key()
+ font_idx = self._font_val2x.get(search_key)
+ if font_idx is not None:
+ self._font_id2x[font] = font_idx
+ self.stats[1] += 1
+ else:
+ font_idx = len(self._font_x2id) + 1 # Why plus 1? Font 4 is missing
+ self._font_id2x[font] = font_idx
+ self._font_val2x[search_key] = font_idx
+ self._font_x2id[font_idx] = font
+ self.stats[2] += 1
+ else:
+ font_idx = len(self._font_id2x) + 1
+ self._font_id2x[font] = font_idx
+ self.stats[2] += 1
+
+ return font_idx
+
+
+ def get_biff_data(self):
+ result = b''
+ result += self._all_fonts()
+ result += self._all_num_formats()
+ result += self._all_cell_styles()
+ result += self._all_styles()
+ return result
+
+ def _all_fonts(self):
+ result = b''
+ if self.style_compression:
+ fonts = self._font_x2id.items()
+ else:
+ fonts = [(x, o) for o, x in self._font_id2x.items()]
+ for font_idx, font in sorted(fonts):
+ result += font.get_biff_record().get()
+ return result
+
+ def _all_num_formats(self):
+ result = b''
+ alist = [
+ (v, k)
+ for k, v in self._num_formats.items()
+ if v >= FIRST_USER_DEFINED_NUM_FORMAT_IDX
+ ]
+ alist.sort()
+ for fmtidx, fmtstr in alist:
+ result += NumberFormatRecord(fmtidx, fmtstr).get()
+ return result
+
+ def _all_cell_styles(self):
+ result = b''
+ for i in range(0, 16):
+ result += XFRecord(self._default_xf, 'style').get()
+ if self.style_compression == 2:
+ styles = self._xf_x2id.items()
+ else:
+ styles = [(x, o) for o, x in self._xf_id2x.items()]
+ for xf_idx, xf in sorted(styles):
+ result += XFRecord(xf).get()
+ return result
+
+ def _all_styles(self):
+ return StyleRecord().get()
+
+# easyxf and its supporting objects ###################################
+
+class EasyXFException(Exception):
+ pass
+
+class EasyXFCallerError(EasyXFException):
+ pass
+
+class EasyXFAuthorError(EasyXFException):
+ pass
+
+class IntULim(object):
+ # If astring represents a valid unsigned integer ('123', '0xabcd', etc)
+ # and it is <= limit, return the int value; otherwise return None.
+
+ def __init__(self, limit):
+ self.limit = limit
+
+ def __call__(self, astring):
+ try:
+ value = int(astring, 0)
+ except ValueError:
+ return None
+ if not 0 <= value <= self.limit:
+ return None
+ return value
+
+bool_map = {
+ # Text values for all Boolean attributes
+ '1': 1, 'yes': 1, 'true': 1, 'on': 1,
+ '0': 0, 'no': 0, 'false': 0, 'off': 0,
+ }
+
+border_line_map = {
+ # Text values for these borders attributes:
+ # left, right, top, bottom and diag
+ 'no_line': 0x00,
+ 'thin': 0x01,
+ 'medium': 0x02,
+ 'dashed': 0x03,
+ 'dotted': 0x04,
+ 'thick': 0x05,
+ 'double': 0x06,
+ 'hair': 0x07,
+ 'medium_dashed': 0x08,
+ 'thin_dash_dotted': 0x09,
+ 'medium_dash_dotted': 0x0a,
+ 'thin_dash_dot_dotted': 0x0b,
+ 'medium_dash_dot_dotted': 0x0c,
+ 'slanted_medium_dash_dotted': 0x0d,
+ }
+
+charset_map = {
+ # Text values for font.charset
+ 'ansi_latin': 0x00,
+ 'sys_default': 0x01,
+ 'symbol': 0x02,
+ 'apple_roman': 0x4d,
+ 'ansi_jap_shift_jis': 0x80,
+ 'ansi_kor_hangul': 0x81,
+ 'ansi_kor_johab': 0x82,
+ 'ansi_chinese_gbk': 0x86,
+ 'ansi_chinese_big5': 0x88,
+ 'ansi_greek': 0xa1,
+ 'ansi_turkish': 0xa2,
+ 'ansi_vietnamese': 0xa3,
+ 'ansi_hebrew': 0xb1,
+ 'ansi_arabic': 0xb2,
+ 'ansi_baltic': 0xba,
+ 'ansi_cyrillic': 0xcc,
+ 'ansi_thai': 0xde,
+ 'ansi_latin_ii': 0xee,
+ 'oem_latin_i': 0xff,
+ }
+
+
+# Text values for colour indices. "grey" is a synonym of "gray".
+# The names are those given by Microsoft Excel 2003 to the colours
+# in the default palette. There is no great correspondence with
+# any W3C name-to-RGB mapping.
+_colour_map_text = """\
+aqua 0x31
+black 0x08
+blue 0x0C
+blue_gray 0x36
+bright_green 0x0B
+brown 0x3C
+coral 0x1D
+cyan_ega 0x0F
+dark_blue 0x12
+dark_blue_ega 0x12
+dark_green 0x3A
+dark_green_ega 0x11
+dark_purple 0x1C
+dark_red 0x10
+dark_red_ega 0x10
+dark_teal 0x38
+dark_yellow 0x13
+gold 0x33
+gray_ega 0x17
+gray25 0x16
+gray40 0x37
+gray50 0x17
+gray80 0x3F
+green 0x11
+ice_blue 0x1F
+indigo 0x3E
+ivory 0x1A
+lavender 0x2E
+light_blue 0x30
+light_green 0x2A
+light_orange 0x34
+light_turquoise 0x29
+light_yellow 0x2B
+lime 0x32
+magenta_ega 0x0E
+ocean_blue 0x1E
+olive_ega 0x13
+olive_green 0x3B
+orange 0x35
+pale_blue 0x2C
+periwinkle 0x18
+pink 0x0E
+plum 0x3D
+purple_ega 0x14
+red 0x0A
+rose 0x2D
+sea_green 0x39
+silver_ega 0x16
+sky_blue 0x28
+tan 0x2F
+teal 0x15
+teal_ega 0x15
+turquoise 0x0F
+violet 0x14
+white 0x09
+yellow 0x0D"""
+
+colour_map = {}
+for _line in _colour_map_text.splitlines():
+ _name, _num = _line.split()
+ _num = int(_num, 0)
+ colour_map[_name] = _num
+ if 'gray' in _name:
+ colour_map[_name.replace('gray', 'grey')] = _num
+del _colour_map_text, _line, _name, _num
+
+def add_palette_colour(colour_str, colour_index):
+ if not (8 <= colour_index <= 63):
+ raise Exception("add_palette_colour: colour_index (%d) not in range(8, 64)" %
+ (colour_index))
+ colour_map[colour_str] = colour_index
+
+# user-defined palette defines 56 RGB colors from entry 8 - 64
+#excel_default_palette_b8 = [ # (red, green, blue)
+# ( 0, 0, 0), (255,255,255), (255, 0, 0), ( 0,255, 0),
+# ( 0, 0,255), (255,255, 0), (255, 0,255), ( 0,255,255),
+# (128, 0, 0), ( 0,128, 0), ( 0, 0,128), (128,128, 0),
+# (128, 0,128), ( 0,128,128), (192,192,192), (128,128,128),
+# (153,153,255), (153, 51,102), (255,255,204), (204,255,255),
+# (102, 0,102), (255,128,128), ( 0,102,204), (204,204,255),
+# ( 0, 0,128), (255, 0,255), (255,255, 0), ( 0,255,255),
+# (128, 0,128), (128, 0, 0), ( 0,128,128), ( 0, 0,255),
+# ( 0,204,255), (204,255,255), (204,255,204), (255,255,153),
+# (153,204,255), (255,153,204), (204,153,255), (255,204,153),
+# ( 51,102,255), ( 51,204,204), (153,204, 0), (255,204, 0),
+# (255,153, 0), (255,102, 0), (102,102,153), (150,150,150),
+# ( 0, 51,102), ( 51,153,102), ( 0, 51, 0), ( 51, 51, 0),
+# (153, 51, 0), (153, 51,102), ( 51, 51,153), ( 51, 51, 51),
+# ]
+
+# Default colour table for BIFF8 copied from
+# OpenOffice.org's Documentation of the Microsoft Excel File Format, Excel Version 2003
+# Note palette has LSB padded with 2 bytes 0x00
+excel_default_palette_b8 = (
+0x00000000,
+0xFFFFFF00,
+0xFF000000,
+0x00FF0000,
+0x0000FF00,
+0xFFFF0000,
+0xFF00FF00,
+0x00FFFF00,
+0x80000000,
+0x00800000,
+0x00008000,
+0x80800000,
+0x80008000,
+0x00808000,
+0xC0C0C000,
+0x80808000,
+0x9999FF00,
+0x99336600,
+0xFFFFCC00,
+0xCCFFFF00,
+0x66006600,
+0xFF808000,
+0x0066CC00,
+0xCCCCFF00,
+0x00008000,
+0xFF00FF00,
+0xFFFF0000,
+0x00FFFF00,
+0x80008000,
+0x80000000,
+0x00808000,
+0x0000FF00,
+0x00CCFF00,
+0xCCFFFF00,
+0xCCFFCC00,
+0xFFFF9900,
+0x99CCFF00,
+0xFF99CC00,
+0xCC99FF00,
+0xFFCC9900,
+0x3366FF00,
+0x33CCCC00,
+0x99CC0000,
+0xFFCC0000,
+0xFF990000,
+0xFF660000,
+0x66669900,
+0x96969600,
+0x00336600,
+0x33996600,
+0x00330000,
+0x33330000,
+0x99330000,
+0x99336600,
+0x33339900,
+0x33333300)
+
+assert len(excel_default_palette_b8) == 56
+
+pattern_map = {
+ # Text values for pattern.pattern
+ # xlwt/doc/pattern_examples.xls showcases all of these patterns.
+ 'no_fill': 0,
+ 'none': 0,
+ 'solid': 1,
+ 'solid_fill': 1,
+ 'solid_pattern': 1,
+ 'fine_dots': 2,
+ 'alt_bars': 3,
+ 'sparse_dots': 4,
+ 'thick_horz_bands': 5,
+ 'thick_vert_bands': 6,
+ 'thick_backward_diag': 7,
+ 'thick_forward_diag': 8,
+ 'big_spots': 9,
+ 'bricks': 10,
+ 'thin_horz_bands': 11,
+ 'thin_vert_bands': 12,
+ 'thin_backward_diag': 13,
+ 'thin_forward_diag': 14,
+ 'squares': 15,
+ 'diamonds': 16,
+ }
+
+def any_str_func(s):
+ return s.strip()
+
+def colour_index_func(s, maxval=0x7F):
+ try:
+ value = int(s, 0)
+ except ValueError:
+ return None
+ if not (0 <= value <= maxval):
+ return None
+ return value
+
+colour_index_func_7 = colour_index_func
+
+def colour_index_func_15(s):
+ return colour_index_func(s, maxval=0x7FFF)
+
+def rotation_func(s):
+ try:
+ value = int(s, 0)
+ except ValueError:
+ return None
+ if not (-90 <= value <= 90):
+ raise EasyXFCallerError("rotation %d: should be -90 to +90 degrees" % value)
+ if value < 0:
+ value = 90 - value # encode as 91 to 180 (clockwise)
+ return value
+
+xf_dict = {
+ 'align': 'alignment', # synonym
+ 'alignment': {
+ 'dire': {
+ 'general': 0,
+ 'lr': 1,
+ 'rl': 2,
+ },
+ 'direction': 'dire',
+ 'horiz': 'horz',
+ 'horizontal': 'horz',
+ 'horz': {
+ 'general': 0,
+ 'left': 1,
+ 'center': 2,
+ 'centre': 2, # "align: horiz centre" means xf.alignment.horz is set to 2
+ 'right': 3,
+ 'filled': 4,
+ 'justified': 5,
+ 'center_across_selection': 6,
+ 'centre_across_selection': 6,
+ 'distributed': 7,
+ },
+ 'inde': IntULim(15), # restriction: 0 <= value <= 15
+ 'indent': 'inde',
+ 'rota': [{'stacked': 255, 'none': 0, }, rotation_func],
+ 'rotation': 'rota',
+ 'shri': bool_map,
+ 'shrink': 'shri',
+ 'shrink_to_fit': 'shri',
+ 'vert': {
+ 'top': 0,
+ 'center': 1,
+ 'centre': 1,
+ 'bottom': 2,
+ 'justified': 3,
+ 'distributed': 4,
+ },
+ 'vertical': 'vert',
+ 'wrap': bool_map,
+ },
+ 'border': 'borders',
+ 'borders': {
+ 'left': [border_line_map, IntULim(0x0d)],
+ 'right': [border_line_map, IntULim(0x0d)],
+ 'top': [border_line_map, IntULim(0x0d)],
+ 'bottom': [border_line_map, IntULim(0x0d)],
+ 'diag': [border_line_map, IntULim(0x0d)],
+ 'top_colour': [colour_map, colour_index_func_7],
+ 'bottom_colour': [colour_map, colour_index_func_7],
+ 'left_colour': [colour_map, colour_index_func_7],
+ 'right_colour': [colour_map, colour_index_func_7],
+ 'diag_colour': [colour_map, colour_index_func_7],
+ 'top_color': 'top_colour',
+ 'bottom_color': 'bottom_colour',
+ 'left_color': 'left_colour',
+ 'right_color': 'right_colour',
+ 'diag_color': 'diag_colour',
+ 'need_diag1': bool_map,
+ 'need_diag2': bool_map,
+ },
+ 'font': {
+ 'bold': bool_map,
+ 'charset': charset_map,
+ 'color': 'colour_index',
+ 'color_index': 'colour_index',
+ 'colour': 'colour_index',
+ 'colour_index': [colour_map, colour_index_func_15],
+ 'escapement': {'none': 0, 'superscript': 1, 'subscript': 2},
+ 'family': {'none': 0, 'roman': 1, 'swiss': 2, 'modern': 3, 'script': 4, 'decorative': 5, },
+ 'height': IntULim(0xFFFF), # practical limits are much narrower e.g. 160 to 1440 (8pt to 72pt)
+ 'italic': bool_map,
+ 'name': any_str_func,
+ 'outline': bool_map,
+ 'shadow': bool_map,
+ 'struck_out': bool_map,
+ 'underline': [bool_map, {'none': 0, 'single': 1, 'single_acc': 0x21, 'double': 2, 'double_acc': 0x22, }],
+ },
+ 'pattern': {
+ 'back_color': 'pattern_back_colour',
+ 'back_colour': 'pattern_back_colour',
+ 'fore_color': 'pattern_fore_colour',
+ 'fore_colour': 'pattern_fore_colour',
+ 'pattern': [pattern_map, IntULim(16)],
+ 'pattern_back_color': 'pattern_back_colour',
+ 'pattern_back_colour': [colour_map, colour_index_func_7],
+ 'pattern_fore_color': 'pattern_fore_colour',
+ 'pattern_fore_colour': [colour_map, colour_index_func_7],
+ },
+ 'protection': {
+ 'cell_locked' : bool_map,
+ 'formula_hidden': bool_map,
+ },
+ }
+
+def _esplit(s, split_char, esc_char="\\"):
+ escaped = False
+ olist = ['']
+ for c in s:
+ if escaped:
+ olist[-1] += c
+ escaped = False
+ elif c == esc_char:
+ escaped = True
+ elif c == split_char:
+ olist.append('')
+ else:
+ olist[-1] += c
+ return olist
+
+def _parse_strg_to_obj(strg, obj, parse_dict,
+ field_sep=",", line_sep=";", intro_sep=":", esc_char="\\", debug=False):
+ for line in _esplit(strg, line_sep, esc_char):
+ line = line.strip()
+ if not line:
+ break
+ split_line = _esplit(line, intro_sep, esc_char)
+ if len(split_line) != 2:
+ raise EasyXFCallerError('line %r should have exactly 1 "%c"' % (line, intro_sep))
+ section, item_str = split_line
+ section = section.strip().lower()
+ for counter in range(2):
+ result = parse_dict.get(section)
+ if result is None:
+ raise EasyXFCallerError('section %r is unknown' % section)
+ if isinstance(result, dict):
+ break
+ if not isinstance(result, str):
+ raise EasyXFAuthorError(
+ 'section %r should map to dict or str object; found %r' % (section, type(result)))
+ # synonym
+ old_section = section
+ section = result
+ else:
+ raise EasyXFAuthorError('Attempt to define synonym of synonym (%r: %r)' % (old_section, result))
+ section_dict = result
+ section_obj = getattr(obj, section, None)
+ if section_obj is None:
+ raise EasyXFAuthorError('instance of %s class has no attribute named %s' % (obj.__class__.__name__, section))
+ for kv_str in _esplit(item_str, field_sep, esc_char):
+ guff = kv_str.split()
+ if not guff:
+ continue
+ k = guff[0].lower().replace('-', '_')
+ v = ' '.join(guff[1:])
+ if not v:
+ raise EasyXFCallerError("no value supplied for %s.%s" % (section, k))
+ for counter in xrange(2):
+ result = section_dict.get(k)
+ if result is None:
+ raise EasyXFCallerError('%s.%s is not a known attribute' % (section, k))
+ if not isinstance(result, basestring):
+ break
+ # synonym
+ old_k = k
+ k = result
+ else:
+ raise EasyXFAuthorError('Attempt to define synonym of synonym (%r: %r)' % (old_k, result))
+ value_info = result
+ if not isinstance(value_info, list):
+ value_info = [value_info]
+ for value_rule in value_info:
+ if isinstance(value_rule, dict):
+ # dict maps strings to integer field values
+ vl = v.lower().replace('-', '_')
+ if vl in value_rule:
+ value = value_rule[vl]
+ break
+ elif callable(value_rule):
+ value = value_rule(v)
+ if value is not None:
+ break
+ else:
+ raise EasyXFAuthorError("unknown value rule for attribute %r: %r" % (k, value_rule))
+ else:
+ raise EasyXFCallerError("unexpected value %r for %s.%s" % (v, section, k))
+ try:
+ orig = getattr(section_obj, k)
+ except AttributeError:
+ raise EasyXFAuthorError('%s.%s in dictionary but not in supplied object' % (section, k))
+ if debug: print("+++ %s.%s = %r # %s; was %r" % (section, k, value, v, orig))
+ setattr(section_obj, k, value)
+
+def easyxf(strg_to_parse="", num_format_str=None,
+ field_sep=",", line_sep=";", intro_sep=":", esc_char="\\", debug=False):
+ """
+ This function is used to create and configure
+ :class:`XFStyle` objects for use with (for example) the
+ :meth:`Worksheet.write` method.
+
+ It takes a string to be parsed to obtain attribute values for
+ :class:`Alignment`, :class:`Borders`, :class:`Font`, :class:`Pattern` and
+ :class:`Protection` objects.
+
+ Refer to the examples in the file `examples/xlwt_easyxf_simple_demo.py`
+ and to the `xf_dict` dictionary in :mod:`xlwt.Style`.
+
+ Various synonyms including color/colour, center/centre and gray/grey are
+ allowed. Case is irrelevant (except maybe in font names). ``-`` may be used
+ instead of ``_``.
+
+ Example: ``font: bold on; align: wrap on, vert centre, horiz center``
+
+ :param num_format_str:
+
+ To get the "number format string" of an existing
+ cell whose format you want to reproduce, select the cell and click on
+ Format/Cells/Number/Custom. Otherwise, refer to Excel help.
+
+ Examples: ``"#,##0.00"``, ``"dd/mm/yyyy"``
+
+ :return: An :class:`XFstyle` object.
+
+ """
+ xfobj = XFStyle()
+ if num_format_str is not None:
+ xfobj.num_format_str = num_format_str
+ if strg_to_parse:
+ _parse_strg_to_obj(strg_to_parse, xfobj, xf_dict,
+ field_sep=field_sep, line_sep=line_sep, intro_sep=intro_sep, esc_char=esc_char, debug=debug)
+ return xfobj
+
+def easyfont(strg_to_parse="", field_sep=",", esc_char="\\", debug=False):
+ xfobj = XFStyle()
+ if strg_to_parse:
+ _parse_strg_to_obj("font: " + strg_to_parse, xfobj, xf_dict,
+ field_sep=field_sep, line_sep=";", intro_sep=":", esc_char=esc_char, debug=debug)
+ return xfobj.font
diff --git a/.venv/lib/python3.9/site-packages/xlwt/UnicodeUtils.py b/.venv/lib/python3.9/site-packages/xlwt/UnicodeUtils.py
new file mode 100644
index 00000000..2ec45df4
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt/UnicodeUtils.py
@@ -0,0 +1,122 @@
+# -*- coding: windows-1252 -*-
+
+'''
+From BIFF8 on, strings are always stored using UTF-16LE text encoding. The
+character array is a sequence of 16-bit values4. Additionally it is
+possible to use a compressed format, which omits the high bytes of all
+characters, if they are all zero.
+
+The following tables describe the standard format of the entire string, but
+in many records the strings differ from this format. This will be mentioned
+separately. It is possible (but not required) to store Rich-Text formatting
+information and Asian phonetic information inside a Unicode string. This
+results in four different ways to store a string. The character array
+is not zero-terminated.
+
+The string consists of the character count (as usual an 8-bit value or
+a 16-bit value), option flags, the character array and optional formatting
+information. If the string is empty, sometimes the option flags field will
+not occur. This is mentioned at the respective place.
+
+Offset Size Contents
+0 1 or 2 Length of the string (character count, ln)
+1 or 2 1 Option flags:
+ Bit Mask Contents
+ 0 01H Character compression (ccompr):
+ 0 = Compressed (8-bit characters)
+ 1 = Uncompressed (16-bit characters)
+ 2 04H Asian phonetic settings (phonetic):
+ 0 = Does not contain Asian phonetic settings
+ 1 = Contains Asian phonetic settings
+ 3 08H Rich-Text settings (richtext):
+ 0 = Does not contain Rich-Text settings
+ 1 = Contains Rich-Text settings
+[2 or 3] 2 (optional, only if richtext=1) Number of Rich-Text formatting runs (rt)
+[var.] 4 (optional, only if phonetic=1) Size of Asian phonetic settings block (in bytes, sz)
+var. ln or
+ 2·ln Character array (8-bit characters or 16-bit characters, dependent on ccompr)
+[var.] 4·rt (optional, only if richtext=1) List of rt formatting runs
+[var.] sz (optional, only if phonetic=1) Asian Phonetic Settings Block
+'''
+
+from .compat import unicode, unicode_type
+from struct import pack
+
+def upack2(s, encoding='ascii'):
+ # If not unicode, make it so.
+ if isinstance(s, unicode_type):
+ us = s
+ else:
+ us = unicode(s, encoding)
+ # Limit is based on number of content characters
+ # (not on number of bytes in packed result)
+ len_us = len(us)
+ if len_us > 32767:
+ raise Exception('String longer than 32767 characters')
+ try:
+ encs = us.encode('latin1')
+ # Success here means all chars are in U+0000 to U+00FF
+ # inclusive, meaning that we can use "compressed format".
+ flag = 0
+ n_items = len_us
+ except UnicodeEncodeError:
+ encs = us.encode('utf_16_le')
+ flag = 1
+ n_items = len(encs) // 2
+ # n_items is the number of "double byte characters" i.e. MS C wchars
+ # Can't use len(us).
+ # len(u"\U0001D400") -> 1 on a wide-unicode build
+ # and 2 on a narrow-unicode build.
+ # We need n_items == 2 in this case.
+ return pack(' 32767:
+ raise Exception('String longer than 32767 characters')
+ try:
+ encs = us.encode('latin1')
+ # Success here means all chars are in U+0000 to U+00FF
+ # inclusive, meaning that we can use "compressed format".
+ flag = 0 | 8
+ n_items = len(encs)
+ except UnicodeEncodeError:
+ encs = us.encode('utf_16_le')
+ flag = 1 | 8
+ n_items = len(encs) // 2 # see comments in upack2 function above
+ return pack(' 255:
+ raise Exception('String longer than 255 characters')
+ try:
+ encs = us.encode('latin1')
+ flag = 0
+ n_items = len_us
+ except UnicodeEncodeError:
+ encs = us.encode('utf_16_le')
+ flag = 1
+ n_items = len(encs) // 2
+ return pack(' 0, 'Z' -> 25, 'AA' -> 26, etc
+ """
+ col = 0
+ power = 1
+ for i in xrange(len(colname)-1, -1, -1):
+ ch = colname[i]
+ col += (ord(ch) - ord('A') + 1) * power
+ power *= 26
+ return col - 1
+
+
+def cell_to_rowcol(cell):
+ """Convert an Excel cell reference string in A1 notation
+ to numeric row/col notation.
+
+ Returns: row, col, row_abs, col_abs
+
+ """
+ m = _re_cell_ex.match(cell)
+ if not m:
+ raise Exception("Ill-formed single_cell reference: %s" % cell)
+ col_abs, col, row_abs, row = m.groups()
+ row_abs = bool(row_abs)
+ col_abs = bool(col_abs)
+ row = int(row) - 1
+ col = col_by_name(col.upper())
+ return row, col, row_abs, col_abs
+
+
+def cell_to_rowcol2(cell):
+ """Convert an Excel cell reference string in A1 notation
+ to numeric row/col notation.
+
+ Returns: row, col
+
+ """
+ m = _re_cell_ex.match(cell)
+ if not m:
+ raise Exception("Error in cell format")
+ col_abs, col, row_abs, row = m.groups()
+ # Convert base26 column string to number
+ # All your Base are belong to us.
+ row = int(row) - 1
+ col = col_by_name(col.upper())
+ return row, col
+
+
+def rowcol_to_cell(row, col, row_abs=False, col_abs=False):
+ """Convert numeric row/col notation to an Excel cell reference string in
+ A1 notation.
+
+ """
+ assert 0 <= row < MAX_ROW # MAX_ROW counts from 1
+ assert 0 <= col < MAX_COL # MAX_COL counts from 1
+ d = col // 26
+ m = col % 26
+ chr1 = "" # Most significant character in AA1
+ if row_abs:
+ row_abs = '$'
+ else:
+ row_abs = ''
+ if col_abs:
+ col_abs = '$'
+ else:
+ col_abs = ''
+ if d > 0:
+ chr1 = chr(ord('A') + d - 1)
+ chr2 = chr(ord('A') + m)
+ # Zero index to 1-index
+ return col_abs + chr1 + chr2 + row_abs + str(row + 1)
+
+def rowcol_pair_to_cellrange(row1, col1, row2, col2,
+ row1_abs=False, col1_abs=False, row2_abs=False, col2_abs=False):
+ """Convert two (row,column) pairs
+ into a cell range string in A1:B2 notation.
+
+ Returns: cell range string
+ """
+ assert row1 <= row2
+ assert col1 <= col2
+ return (
+ rowcol_to_cell(row1, col1, row1_abs, col1_abs)
+ + ":"
+ + rowcol_to_cell(row2, col2, row2_abs, col2_abs)
+ )
+
+def cellrange_to_rowcol_pair(cellrange):
+ """Convert cell range string in A1 notation to numeric row/col
+ pair.
+
+ Returns: row1, col1, row2, col2
+
+ """
+ cellrange = cellrange.upper()
+ # Convert a row range: '1:3'
+ res = _re_row_range.match(cellrange)
+ if res:
+ row1 = int(res.group(1)) - 1
+ col1 = 0
+ row2 = int(res.group(2)) - 1
+ col2 = -1
+ return row1, col1, row2, col2
+ # Convert a column range: 'A:A' or 'B:G'.
+ # A range such as A:A is equivalent to A1:A16384, so add rows as required
+ res = _re_col_range.match(cellrange)
+ if res:
+ col1 = col_by_name(res.group(1).upper())
+ row1 = 0
+ col2 = col_by_name(res.group(2).upper())
+ row2 = -1
+ return row1, col1, row2, col2
+ # Convert a cell range: 'A1:B7'
+ res = _re_cell_range.match(cellrange)
+ if res:
+ row1, col1 = cell_to_rowcol2(res.group(1))
+ row2, col2 = cell_to_rowcol2(res.group(2))
+ return row1, col1, row2, col2
+ # Convert a cell reference: 'A1' or 'AD2000'
+ res = _re_cell_ref.match(cellrange)
+ if res:
+ row1, col1 = cell_to_rowcol2(res.group(1))
+ return row1, col1, row1, col1
+ raise Exception("Unknown cell reference %s" % (cellrange))
+
+
+def cell_to_packed_rowcol(cell):
+ """ pack row and column into the required 4 byte format """
+ row, col, row_abs, col_abs = cell_to_rowcol(cell)
+ if col >= MAX_COL:
+ raise Exception("Column %s greater than IV in formula" % cell)
+ if row >= MAX_ROW: # this for BIFF8. for BIFF7 available 2^14
+ raise Exception("Row %s greater than %d in formula" % (cell, MAX_ROW))
+ col |= int(not row_abs) << 15
+ col |= int(not col_abs) << 14
+ return row, col
+
+# === sheetname functions ===
+
+def valid_sheet_name(sheet_name):
+ if sheet_name == u"" or sheet_name[0] == u"'" or len(sheet_name) > 31:
+ return False
+ for c in sheet_name:
+ if c in u"[]:\\?/*\x00":
+ return False
+ return True
+
+def quote_sheet_name(unquoted_sheet_name):
+ if not valid_sheet_name(unquoted_sheet_name):
+ raise Exception(
+ 'attempt to quote an invalid worksheet name %r' % unquoted_sheet_name)
+ return u"'" + unquoted_sheet_name.replace(u"'", u"''") + u"'"
diff --git a/.venv/lib/python3.9/site-packages/xlwt/Workbook.py b/.venv/lib/python3.9/site-packages/xlwt/Workbook.py
new file mode 100644
index 00000000..4209ea06
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt/Workbook.py
@@ -0,0 +1,712 @@
+# -*- coding: windows-1252 -*-
+# Record Order in BIFF8
+# Workbook Globals Substream
+# BOF Type = workbook globals
+# Interface Header
+# MMS
+# Interface End
+# WRITEACCESS
+# CODEPAGE
+# DSF
+# TABID
+# FNGROUPCOUNT
+# Workbook Protection Block
+# WINDOWPROTECT
+# PROTECT
+# PASSWORD
+# PROT4REV
+# PROT4REVPASS
+# BACKUP
+# HIDEOBJ
+# WINDOW1
+# DATEMODE
+# PRECISION
+# REFRESHALL
+# BOOKBOOL
+# FONT +
+# FORMAT *
+# XF +
+# STYLE +
+# ? PALETTE
+# USESELFS
+#
+# BOUNDSHEET +
+#
+# COUNTRY
+# ? Link Table
+# SST
+# ExtSST
+# EOF
+
+from . import BIFFRecords
+from . import Style
+from .compat import unicode_type, int_types, basestring
+
+class Workbook(object):
+ """
+ This is a class representing a workbook and all its contents. When creating
+ Excel files with xlwt, you will normally start by instantiating an
+ object of this class.
+ """
+
+ #################################################################
+ ## Constructor
+ #################################################################
+ def __init__(self, encoding='ascii', style_compression=0):
+ self.encoding = encoding
+ self.__owner = 'None'
+ self.__country_code = None # 0x07 is Russia :-)
+ self.__wnd_protect = 0
+ self.__obj_protect = 0
+ self.__protect = 0
+ self.__backup_on_save = 0
+ # for WINDOW1 record
+ self.__hpos_twips = 0x01E0
+ self.__vpos_twips = 0x005A
+ self.__width_twips = 0x3FCF
+ self.__height_twips = 0x2A4E
+ self.__custom_palette_b8 = None
+
+ self.__active_sheet = 0
+ self.__first_tab_index = 0
+ self.__selected_tabs = 0x01
+ self.__tab_width_twips = 0x0258
+
+ self.__wnd_hidden = 0
+ self.__wnd_mini = 0
+ self.__hscroll_visible = 1
+ self.__vscroll_visible = 1
+ self.__tabs_visible = 1
+
+ self.__styles = Style.StyleCollection(style_compression)
+
+ self.__dates_1904 = 0
+ self.__use_cell_values = 1
+
+ self.__sst = BIFFRecords.SharedStringTable(self.encoding)
+
+ self.__worksheets = []
+ self.__worksheet_idx_from_name = {}
+ self.__sheet_refs = {}
+ self._supbook_xref = {}
+ self._xcall_xref = {}
+ self._ownbook_supbookx = None
+ self._ownbook_supbook_ref = None
+ self._xcall_supbookx = None
+ self._xcall_supbook_ref = None
+
+
+
+ #################################################################
+ ## Properties, "getters", "setters"
+ #################################################################
+
+ def get_style_stats(self):
+ return self.__styles.stats[:]
+
+ def set_owner(self, value):
+ self.__owner = value
+
+ def get_owner(self):
+ return self.__owner
+
+ owner = property(get_owner, set_owner)
+
+ #################################################################
+
+ def set_country_code(self, value):
+ self.__country_code = value
+
+ def get_country_code(self):
+ return self.__country_code
+
+ country_code = property(get_country_code, set_country_code)
+
+ #################################################################
+
+ def set_wnd_protect(self, value):
+ self.__wnd_protect = int(value)
+
+ def get_wnd_protect(self):
+ return bool(self.__wnd_protect)
+
+ wnd_protect = property(get_wnd_protect, set_wnd_protect)
+
+ #################################################################
+
+ def set_obj_protect(self, value):
+ self.__obj_protect = int(value)
+
+ def get_obj_protect(self):
+ return bool(self.__obj_protect)
+
+ obj_protect = property(get_obj_protect, set_obj_protect)
+
+ #################################################################
+
+ def set_protect(self, value):
+ self.__protect = int(value)
+
+ def get_protect(self):
+ return bool(self.__protect)
+
+ protect = property(get_protect, set_protect)
+
+ #################################################################
+
+ def set_backup_on_save(self, value):
+ self.__backup_on_save = int(value)
+
+ def get_backup_on_save(self):
+ return bool(self.__backup_on_save)
+
+ backup_on_save = property(get_backup_on_save, set_backup_on_save)
+
+ #################################################################
+
+ def set_hpos(self, value):
+ self.__hpos_twips = value & 0xFFFF
+
+ def get_hpos(self):
+ return self.__hpos_twips
+
+ hpos = property(get_hpos, set_hpos)
+
+ #################################################################
+
+ def set_vpos(self, value):
+ self.__vpos_twips = value & 0xFFFF
+
+ def get_vpos(self):
+ return self.__vpos_twips
+
+ vpos = property(get_vpos, set_vpos)
+
+ #################################################################
+
+ def set_width(self, value):
+ self.__width_twips = value & 0xFFFF
+
+ def get_width(self):
+ return self.__width_twips
+
+ width = property(get_width, set_width)
+
+ #################################################################
+
+ def set_height(self, value):
+ self.__height_twips = value & 0xFFFF
+
+ def get_height(self):
+ return self.__height_twips
+
+ height = property(get_height, set_height)
+
+ #################################################################
+
+ def set_active_sheet(self, value):
+ self.__active_sheet = value & 0xFFFF
+ self.__first_tab_index = self.__active_sheet
+
+ def get_active_sheet(self):
+ return self.__active_sheet
+
+ active_sheet = property(get_active_sheet, set_active_sheet)
+
+ #################################################################
+
+ def set_tab_width(self, value):
+ self.__tab_width_twips = value & 0xFFFF
+
+ def get_tab_width(self):
+ return self.__tab_width_twips
+
+ tab_width = property(get_tab_width, set_tab_width)
+
+ #################################################################
+
+ def set_wnd_visible(self, value):
+ self.__wnd_hidden = int(not value)
+
+ def get_wnd_visible(self):
+ return not bool(self.__wnd_hidden)
+
+ wnd_visible = property(get_wnd_visible, set_wnd_visible)
+
+ #################################################################
+
+ def set_wnd_mini(self, value):
+ self.__wnd_mini = int(value)
+
+ def get_wnd_mini(self):
+ return bool(self.__wnd_mini)
+
+ wnd_mini = property(get_wnd_mini, set_wnd_mini)
+
+ #################################################################
+
+ def set_hscroll_visible(self, value):
+ self.__hscroll_visible = int(value)
+
+ def get_hscroll_visible(self):
+ return bool(self.__hscroll_visible)
+
+ hscroll_visible = property(get_hscroll_visible, set_hscroll_visible)
+
+ #################################################################
+
+ def set_vscroll_visible(self, value):
+ self.__vscroll_visible = int(value)
+
+ def get_vscroll_visible(self):
+ return bool(self.__vscroll_visible)
+
+ vscroll_visible = property(get_vscroll_visible, set_vscroll_visible)
+
+ #################################################################
+
+ def set_tabs_visible(self, value):
+ self.__tabs_visible = int(value)
+
+ def get_tabs_visible(self):
+ return bool(self.__tabs_visible)
+
+ tabs_visible = property(get_tabs_visible, set_tabs_visible)
+
+ #################################################################
+
+ def set_dates_1904(self, value):
+ self.__dates_1904 = int(value)
+
+ def get_dates_1904(self):
+ return bool(self.__dates_1904)
+
+ dates_1904 = property(get_dates_1904, set_dates_1904)
+
+ #################################################################
+
+ def set_use_cell_values(self, value):
+ self.__use_cell_values = int(value)
+
+ def get_use_cell_values(self):
+ return bool(self.__use_cell_values)
+
+ use_cell_values = property(get_use_cell_values, set_use_cell_values)
+
+ #################################################################
+
+ def get_default_style(self):
+ return self.__styles.default_style
+
+ default_style = property(get_default_style)
+
+ #################################################################
+
+ def set_colour_RGB(self, colour_index, red, green, blue):
+ if not(8 <= colour_index <= 63):
+ raise Exception("set_colour_RGB: colour_index (%d) not in range(8, 64)" %
+ colour_index)
+ if min(red, green, blue) < 0 or max(red, green, blue) > 255:
+ raise Exception("set_colour_RGB: colour values (%d,%d,%d) must be in range(0, 256)"
+ % (red, green, blue))
+ if self.__custom_palette_b8 is None:
+ self.__custom_palette_b8 = list(Style.excel_default_palette_b8)
+ # User-defined Palette starts at colour index 8,
+ # so subtract 8 from colour_index when placing in palette
+ palette_index = colour_index - 8
+ self.__custom_palette_b8[palette_index] = red << 24 | green << 16 | blue << 8
+
+ ##################################################################
+ ## Methods
+ ##################################################################
+
+ def add_style(self, style):
+ return self.__styles.add(style)
+
+ def add_font(self, font):
+ return self.__styles.add_font(font)
+
+ def add_str(self, s):
+ return self.__sst.add_str(s)
+
+ def del_str(self, sst_idx):
+ self.__sst.del_str(sst_idx)
+
+ def str_index(self, s):
+ return self.__sst.str_index(s)
+
+ def add_rt(self, rt):
+ return self.__sst.add_rt(rt)
+
+ def rt_index(self, rt):
+ return self.__sst.rt_index(rt)
+
+ def add_sheet(self, sheetname, cell_overwrite_ok=False):
+ """
+ This method is used to create Worksheets in a Workbook.
+
+ :param sheetname:
+
+ The name to use for this sheet, as it will appear in the
+ tabs at the bottom of the Excel application.
+
+ :param cell_overwrite_ok:
+
+ If ``True``, cells in the added worksheet will not raise an
+ exception if written to more than once.
+
+ :return:
+
+ The :class:`~xlwt.Worksheet.Worksheet` that was added.
+
+ """
+ from . import Utils
+ from .Worksheet import Worksheet
+ if not isinstance(sheetname, unicode_type):
+ sheetname = sheetname.decode(self.encoding)
+ if not Utils.valid_sheet_name(sheetname):
+ raise Exception("invalid worksheet name %r" % sheetname)
+ lower_name = sheetname.lower()
+ if lower_name in self.__worksheet_idx_from_name:
+ raise Exception("duplicate worksheet name %r" % sheetname)
+ self.__worksheet_idx_from_name[lower_name] = len(self.__worksheets)
+ self.__worksheets.append(Worksheet(sheetname, self, cell_overwrite_ok))
+ return self.__worksheets[-1]
+
+ def get_sheet(self, sheet):
+ if isinstance(sheet, int_types):
+ return self.__worksheets[sheet]
+ elif isinstance(sheet, basestring):
+ sheetnum = self.sheet_index(sheet)
+ return self.__worksheets[sheetnum]
+ else:
+ raise Exception("sheet must be integer or string")
+
+ def sheet_index(self, sheetname):
+ try:
+ sheetnum = self.__worksheet_idx_from_name[sheetname.lower()]
+ except KeyError:
+ self.raise_bad_sheetname(sheetname)
+
+ return sheetnum
+
+ def raise_bad_sheetname(self, sheetname):
+ raise Exception("Formula: unknown sheet name %s" % sheetname)
+
+ def convert_sheetindex(self, strg_ref, n_sheets):
+ idx = int(strg_ref)
+ if 0 <= idx < n_sheets:
+ return idx
+ msg = "Formula: sheet index (%s) >= number of sheets (%d)" % (strg_ref, n_sheets)
+ raise Exception(msg)
+
+ def _get_supbook_index(self, tag):
+ if tag in self._supbook_xref:
+ return self._supbook_xref[tag]
+ self._supbook_xref[tag] = idx = len(self._supbook_xref)
+ return idx
+
+ def setup_ownbook(self):
+ self._ownbook_supbookx = self._get_supbook_index(('ownbook', 0))
+ self._ownbook_supbook_ref = None
+ reference = (self._ownbook_supbookx, 0xFFFE, 0xFFFE)
+ if reference in self.__sheet_refs:
+ raise Exception("can't happen")
+ self.__sheet_refs[reference] = self._ownbook_supbook_ref = len(self.__sheet_refs)
+
+ def setup_xcall(self):
+ self._xcall_supbookx = self._get_supbook_index(('xcall', 0))
+ self._xcall_supbook_ref = None
+ reference = (self._xcall_supbookx, 0xFFFE, 0xFFFE)
+ if reference in self.__sheet_refs:
+ raise Exception("can't happen")
+ self.__sheet_refs[reference] = self._xcall_supbook_ref = len(self.__sheet_refs)
+
+ def add_sheet_reference(self, formula):
+ patches = []
+ n_sheets = len(self.__worksheets)
+ sheet_refs, xcall_refs = formula.get_references()
+
+ for ref0, ref1, offset in sheet_refs:
+ if not ref0.isdigit():
+ try:
+ ref0n = self.__worksheet_idx_from_name[ref0.lower()]
+ except KeyError:
+ self.raise_bad_sheetname(ref0)
+ else:
+ ref0n = self.convert_sheetindex(ref0, n_sheets)
+ if ref1 == ref0:
+ ref1n = ref0n
+ elif not ref1.isdigit():
+ try:
+ ref1n = self.__worksheet_idx_from_name[ref1.lower()]
+ except KeyError:
+ self.raise_bad_sheetname(ref1)
+ else:
+ ref1n = self.convert_sheetindex(ref1, n_sheets)
+ if ref1n < ref0n:
+ msg = "Formula: sheets out of order; %r:%r -> (%d, %d)" \
+ % (ref0, ref1, ref0n, ref1n)
+ raise Exception(msg)
+ if self._ownbook_supbookx is None:
+ self.setup_ownbook()
+ reference = (self._ownbook_supbookx, ref0n, ref1n)
+ if reference in self.__sheet_refs:
+ patches.append((offset, self.__sheet_refs[reference]))
+ else:
+ nrefs = len(self.__sheet_refs)
+ if nrefs > 65535:
+ raise Exception('More than 65536 inter-sheet references')
+ self.__sheet_refs[reference] = nrefs
+ patches.append((offset, nrefs))
+
+ for funcname, offset in xcall_refs:
+ if self._ownbook_supbookx is None:
+ self.setup_ownbook()
+ if self._xcall_supbookx is None:
+ self.setup_xcall()
+ # print funcname, self._supbook_xref
+ patches.append((offset, self._xcall_supbook_ref))
+ if not isinstance(funcname, unicode_type):
+ funcname = funcname.decode(self.encoding)
+ if funcname in self._xcall_xref:
+ idx = self._xcall_xref[funcname]
+ else:
+ self._xcall_xref[funcname] = idx = len(self._xcall_xref)
+ patches.append((offset + 2, idx + 1))
+
+ formula.patch_references(patches)
+
+ ##################################################################
+ ## BIFF records generation
+ ##################################################################
+
+ def __bof_rec(self):
+ return BIFFRecords.Biff8BOFRecord(BIFFRecords.Biff8BOFRecord.BOOK_GLOBAL).get()
+
+ def __eof_rec(self):
+ return BIFFRecords.EOFRecord().get()
+
+ def __intf_hdr_rec(self):
+ return BIFFRecords.InteraceHdrRecord().get()
+
+ def __intf_end_rec(self):
+ return BIFFRecords.InteraceEndRecord().get()
+
+ def __intf_mms_rec(self):
+ return BIFFRecords.MMSRecord().get()
+
+ def __write_access_rec(self):
+ return BIFFRecords.WriteAccessRecord(self.__owner).get()
+
+ def __wnd_protect_rec(self):
+ return BIFFRecords.WindowProtectRecord(self.__wnd_protect).get()
+
+ def __obj_protect_rec(self):
+ return BIFFRecords.ObjectProtectRecord(self.__obj_protect).get()
+
+ def __protect_rec(self):
+ return BIFFRecords.ProtectRecord(self.__protect).get()
+
+ def __password_rec(self):
+ return BIFFRecords.PasswordRecord().get()
+
+ def __prot4rev_rec(self):
+ return BIFFRecords.Prot4RevRecord().get()
+
+ def __prot4rev_pass_rec(self):
+ return BIFFRecords.Prot4RevPassRecord().get()
+
+ def __backup_rec(self):
+ return BIFFRecords.BackupRecord(self.__backup_on_save).get()
+
+ def __hide_obj_rec(self):
+ return BIFFRecords.HideObjRecord().get()
+
+ def __window1_rec(self):
+ flags = 0
+ flags |= (self.__wnd_hidden) << 0
+ flags |= (self.__wnd_mini) << 1
+ flags |= (self.__hscroll_visible) << 3
+ flags |= (self.__vscroll_visible) << 4
+ flags |= (self.__tabs_visible) << 5
+
+ return BIFFRecords.Window1Record(self.__hpos_twips, self.__vpos_twips,
+ self.__width_twips, self.__height_twips,
+ flags,
+ self.__active_sheet, self.__first_tab_index,
+ self.__selected_tabs, self.__tab_width_twips).get()
+
+ def __codepage_rec(self):
+ return BIFFRecords.CodepageBiff8Record().get()
+
+ def __country_rec(self):
+ if not self.__country_code:
+ return b''
+ return BIFFRecords.CountryRecord(self.__country_code, self.__country_code).get()
+
+ def __dsf_rec(self):
+ return BIFFRecords.DSFRecord().get()
+
+ def __tabid_rec(self):
+ return BIFFRecords.TabIDRecord(len(self.__worksheets)).get()
+
+ def __fngroupcount_rec(self):
+ return BIFFRecords.FnGroupCountRecord().get()
+
+ def __datemode_rec(self):
+ return BIFFRecords.DateModeRecord(self.__dates_1904).get()
+
+ def __precision_rec(self):
+ return BIFFRecords.PrecisionRecord(self.__use_cell_values).get()
+
+ def __refresh_all_rec(self):
+ return BIFFRecords.RefreshAllRecord().get()
+
+ def __bookbool_rec(self):
+ return BIFFRecords.BookBoolRecord().get()
+
+ def __all_fonts_num_formats_xf_styles_rec(self):
+ return self.__styles.get_biff_data()
+
+ def __palette_rec(self):
+ if self.__custom_palette_b8 is None:
+ return b''
+ info = BIFFRecords.PaletteRecord(self.__custom_palette_b8).get()
+ return info
+
+ def __useselfs_rec(self):
+ return BIFFRecords.UseSelfsRecord().get()
+
+ def __boundsheets_rec(self, data_len_before, data_len_after, sheet_biff_lens):
+ # .................................
+ # BOUNDSEHEET0
+ # BOUNDSEHEET1
+ # BOUNDSEHEET2
+ # ..................................
+ # WORKSHEET0
+ # WORKSHEET1
+ # WORKSHEET2
+ boundsheets_len = 0
+ for sheet in self.__worksheets:
+ boundsheets_len += len(BIFFRecords.BoundSheetRecord(
+ 0x00, sheet.visibility, sheet.name, self.encoding
+ ).get())
+
+ start = data_len_before + boundsheets_len + data_len_after
+
+ result = b''
+ for sheet_biff_len, sheet in zip(sheet_biff_lens, self.__worksheets):
+ result += BIFFRecords.BoundSheetRecord(
+ start, sheet.visibility, sheet.name, self.encoding
+ ).get()
+ start += sheet_biff_len
+ return result
+
+ def __all_links_rec(self):
+ pieces = []
+ temp = [(idx, tag) for tag, idx in self._supbook_xref.items()]
+ temp.sort()
+ for idx, tag in temp:
+ stype, snum = tag
+ if stype == 'ownbook':
+ rec = BIFFRecords.InternalReferenceSupBookRecord(len(self.__worksheets)).get()
+ pieces.append(rec)
+ elif stype == 'xcall':
+ rec = BIFFRecords.XcallSupBookRecord().get()
+ pieces.append(rec)
+ temp = [(idx, name) for name, idx in self._xcall_xref.items()]
+ temp.sort()
+ for idx, name in temp:
+ rec = BIFFRecords.ExternnameRecord(
+ options=0, index=0, name=name, fmla='\x02\x00\x1c\x17').get()
+ pieces.append(rec)
+ else:
+ raise Exception('unknown supbook stype %r' % stype)
+ if len(self.__sheet_refs) > 0:
+ # get references in index order
+ temp = [(idx, ref) for ref, idx in self.__sheet_refs.items()]
+ temp.sort()
+ temp = [ref for idx, ref in temp]
+ externsheet_record = BIFFRecords.ExternSheetRecord(temp).get()
+ pieces.append(externsheet_record)
+ return b''.join(pieces)
+
+ def __sst_rec(self):
+ return self.__sst.get_biff_record()
+
+ def __ext_sst_rec(self, abs_stream_pos):
+ return b''
+ #return BIFFRecords.ExtSSTRecord(abs_stream_pos, self.sst_record.str_placement,
+ #self.sst_record.portions_len).get()
+
+ def get_biff_data(self):
+ before = b''
+ before += self.__bof_rec()
+ before += self.__intf_hdr_rec()
+ before += self.__intf_mms_rec()
+ before += self.__intf_end_rec()
+ before += self.__write_access_rec()
+ before += self.__codepage_rec()
+ before += self.__dsf_rec()
+ before += self.__tabid_rec()
+ before += self.__fngroupcount_rec()
+ before += self.__wnd_protect_rec()
+ before += self.__protect_rec()
+ before += self.__obj_protect_rec()
+ before += self.__password_rec()
+ before += self.__prot4rev_rec()
+ before += self.__prot4rev_pass_rec()
+ before += self.__backup_rec()
+ before += self.__hide_obj_rec()
+ before += self.__window1_rec()
+ before += self.__datemode_rec()
+ before += self.__precision_rec()
+ before += self.__refresh_all_rec()
+ before += self.__bookbool_rec()
+ before += self.__all_fonts_num_formats_xf_styles_rec()
+ before += self.__palette_rec()
+ before += self.__useselfs_rec()
+
+ country = self.__country_rec()
+ all_links = self.__all_links_rec()
+
+ shared_str_table = self.__sst_rec()
+ after = country + all_links + shared_str_table
+
+ ext_sst = self.__ext_sst_rec(0) # need fake cause we need calc stream pos
+ eof = self.__eof_rec()
+
+ self.__worksheets[self.__active_sheet].selected = True
+ sheets = b''
+ sheet_biff_lens = []
+ for sheet in self.__worksheets:
+ data = sheet.get_biff_data()
+ sheets += data
+ sheet_biff_lens.append(len(data))
+
+ bundlesheets = self.__boundsheets_rec(len(before), len(after)+len(ext_sst)+len(eof), sheet_biff_lens)
+
+ sst_stream_pos = len(before) + len(bundlesheets) + len(country) + len(all_links)
+ ext_sst = self.__ext_sst_rec(sst_stream_pos)
+
+ return before + bundlesheets + after + ext_sst + eof + sheets
+
+ def save(self, filename_or_stream):
+ """
+ This method is used to save the Workbook to a file in native Excel
+ format.
+
+ :param filename_or_stream:
+ This can be a string containing a filename of
+ the file, in which case the excel file is saved to disk using the name
+ provided. It can also be a stream object with a write method, such as
+ a :class:`~io.StringIO`, in which case the data for the excel
+ file is written to the stream.
+ """
+ from . import CompoundDoc
+
+ doc = CompoundDoc.XlsDoc()
+ doc.save(filename_or_stream, self.get_biff_data())
+
+
diff --git a/.venv/lib/python3.9/site-packages/xlwt/Worksheet.py b/.venv/lib/python3.9/site-packages/xlwt/Worksheet.py
new file mode 100644
index 00000000..c306f3c9
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt/Worksheet.py
@@ -0,0 +1,1420 @@
+# -*- coding: windows-1252 -*-
+# BOF
+# UNCALCED
+# INDEX
+# Calculation Settings Block
+# PRINTHEADERS
+# PRINTGRIDLINES
+# GRIDSET
+# GUTS
+# DEFAULTROWHEIGHT
+# WSBOOL
+# Page Settings Block
+# Worksheet Protection Block
+# DEFCOLWIDTH
+# COLINFO
+# SORT
+# DIMENSIONS
+# Row Blocks
+# WINDOW2
+# SCL
+# PANE
+# SELECTION
+# STANDARDWIDTH
+# MERGEDCELLS
+# LABELRANGES
+# PHONETIC
+# Conditional Formatting Table
+# Hyperlink Table
+# Data Validity Table
+# SHEETLAYOUT (BIFF8X only)
+# SHEETPROTECTION (BIFF8X only)
+# RANGEPROTECTION (BIFF8X only)
+# EOF
+
+from . import BIFFRecords
+from . import Bitmap
+from . import Style
+from .Row import Row
+from .Column import Column
+from .compat import unicode, itervalues
+import tempfile
+
+class Worksheet(object):
+ """
+ This is a class
+ representing the contents of a sheet in a workbook.
+
+ .. warning::
+
+ You don't normally create instances of this class yourself.
+ They are returned from calls to :meth:`~xlwt.Workbook.Workbook.add_sheet`.
+ """
+ # a safe default value, 3 is always valid!
+ active_pane = 3
+
+ #################################################################
+ ## Constructor
+ #################################################################
+ def __init__(self, sheetname, parent_book, cell_overwrite_ok=False):
+ self.Row = Row
+ self.Column = Column
+
+ self.__name = sheetname
+ self.__parent = parent_book
+ self._cell_overwrite_ok = cell_overwrite_ok
+
+ self.__rows = {}
+ self.__cols = {}
+ self.__merged_ranges = []
+ self.__bmp_rec = b''
+
+ self.__show_formulas = 0
+ self.__show_grid = 1
+ self.__show_headers = 1
+ self.__panes_frozen = 0
+ self.show_zero_values = 1
+ self.__auto_colour_grid = 1
+ self.__cols_right_to_left = 0
+ self.__show_outline = 1
+ self.__remove_splits = 0
+ # Multiple sheets can be selected, but only one can be active
+ # (hold down Ctrl and click multiple tabs in the file in OOo)
+ self.__selected = 0
+ # "sheet_visible" should really be called "sheet_active"
+ # and is 1 when this sheet is the sheet displayed when the file
+ # is open. More than likely only one sheet should ever be set as
+ # visible.
+ # The same sheet should be specified in Workbook.active_sheet
+ # (that way, both the WINDOW1 record in the book and the WINDOW2
+ # records in each sheet will be in agreement)
+ # The visibility of the sheet is found in the "visibility"
+ # attribute obtained from the BOUNDSHEET record.
+ self.__sheet_visible = 0
+ self.__page_preview = 0
+
+ self.__first_visible_row = 0
+ self.__first_visible_col = 0
+ self.__grid_colour = 0x40
+ self.__preview_magn = 0 # use default (60%)
+ self.__normal_magn = 0 # use default (100%)
+ self.__scl_magn = None
+ self.explicit_magn_setting = False
+
+ self.visibility = 0 # from/to BOUNDSHEET record.
+
+ self.__vert_split_pos = None
+ self.__horz_split_pos = None
+ self.__vert_split_first_visible = None
+ self.__horz_split_first_visible = None
+
+ # This is a caller-settable flag:
+
+ self.split_position_units_are_twips = False
+
+ # Default is False for backward compatibility with pyExcelerator
+ # and previous versions of xlwt.
+ # if panes_frozen:
+ # vert/horz_split_pos are taken as number of rows/cols
+ # else: # split
+ # if split_position_units_are_twips:
+ # vert/horz_split_pos are taken as number of twips
+ # else:
+ # vert/horz_split_pos are taken as
+ # number of rows(cols) * default row(col) height (width) (i.e. 12.75 (8.43) somethings)
+ # and converted to twips by approximate formulas
+ # Callers who are copying an existing file should use
+ # xlwt_worksheet.split_position_units_are_twips = True
+ # because that's what's actually in the file.
+
+ # There are 20 twips to a point. There are 72 points to an inch.
+
+ self.__row_gut_width = 0
+ self.__col_gut_height = 0
+
+ self.__show_auto_page_breaks = 1
+ self.__dialogue_sheet = 0
+ self.__auto_style_outline = 0
+ self.__outline_below = 0
+ self.__outline_right = 0
+ self.__fit_num_pages = 0
+ self.__show_row_outline = 1
+ self.__show_col_outline = 1
+ self.__alt_expr_eval = 0
+ self.__alt_formula_entries = 0
+
+ self.__row_default_height = 0x00FF
+ self.row_default_height_mismatch = 0
+ self.row_default_hidden = 0
+ self.row_default_space_above = 0
+ self.row_default_space_below = 0
+
+ self.__col_default_width = 0x0008
+
+ self.__calc_mode = 1
+ self.__calc_count = 0x0064
+ self.__RC_ref_mode = 1
+ self.__iterations_on = 0
+ self.__delta = 0.001
+ self.__save_recalc = 0
+
+ self.__print_headers = 0
+ self.__print_grid = 0
+ self.__grid_set = 1
+ self.__vert_page_breaks = []
+ self.__horz_page_breaks = []
+ self.__header_str = '&P'
+ self.__footer_str = '&F'
+ self.__print_centered_vert = 0
+ self.__print_centered_horz = 1
+ self.__left_margin = 0.3 #0.5
+ self.__right_margin = 0.3 #0.5
+ self.__top_margin = 0.61 #1.0
+ self.__bottom_margin = 0.37 #1.0
+ self.__paper_size_code = 9 # A4
+ self.__print_scaling = 100
+ self.__start_page_number = 1
+ self.__fit_width_to_pages = 1
+ self.__fit_height_to_pages = 1
+ self.__print_in_rows = 1
+ self.__portrait = 1
+ self.__print_not_colour = 0
+ self.__print_draft = 0
+ self.__print_notes = 0
+ self.__print_notes_at_end = 0
+ self.__print_omit_errors = 0
+ self.__print_hres = 0x012C # 300 dpi
+ self.__print_vres = 0x012C # 300 dpi
+ self.__header_margin = 0.1
+ self.__footer_margin = 0.1
+ self.__copies_num = 1
+
+ self.__wnd_protect = 0
+ self.__obj_protect = 0
+ self.__protect = 0
+ self.__scen_protect = 0
+ self.__password = ''
+
+ self.last_used_row = 0
+ self.first_used_row = 65535
+ self.last_used_col = 0
+ self.first_used_col = 255
+ self.row_tempfile = None
+ self.__flushed_rows = {}
+ self.__row_visible_levels = 0
+
+ #################################################################
+ ## Properties, "getters", "setters"
+ #################################################################
+
+ def set_name(self, value):
+ self.__name = value
+
+ def get_name(self):
+ return self.__name
+
+ name = property(get_name, set_name)
+
+ #################################################################
+
+ def get_parent(self):
+ return self.__parent
+
+ parent = property(get_parent)
+
+ #################################################################
+
+ def get_rows(self):
+ return self.__rows
+
+ rows = property(get_rows)
+
+ #################################################################
+
+ def get_cols(self):
+ return self.__cols
+
+ cols = property(get_cols)
+
+ #################################################################
+
+ def get_merged_ranges(self):
+ return self.__merged_ranges
+
+ merged_ranges = property(get_merged_ranges)
+
+ #################################################################
+
+ def get_bmp_rec(self):
+ return self.__bmp_rec
+
+ bmp_rec = property(get_bmp_rec)
+
+ #################################################################
+
+ def set_show_formulas(self, value):
+ self.__show_formulas = int(value)
+
+ def get_show_formulas(self):
+ return bool(self.__show_formulas)
+
+ show_formulas = property(get_show_formulas, set_show_formulas)
+
+ #################################################################
+
+ def set_show_grid(self, value):
+ self.__show_grid = int(value)
+
+ def get_show_grid(self):
+ return bool(self.__show_grid)
+
+ show_grid = property(get_show_grid, set_show_grid)
+
+ #################################################################
+
+ def set_show_headers(self, value):
+ self.__show_headers = int(value)
+
+ def get_show_headers(self):
+ return bool(self.__show_headers)
+
+ show_headers = property(get_show_headers, set_show_headers)
+
+ #################################################################
+
+ def set_panes_frozen(self, value):
+ self.__panes_frozen = int(value)
+
+ def get_panes_frozen(self):
+ return bool(self.__panes_frozen)
+
+ panes_frozen = property(get_panes_frozen, set_panes_frozen)
+
+ #################################################################
+
+ ### def set_show_empty_as_zero(self, value):
+ ### self.__show_empty_as_zero = int(value)
+
+ ### def get_show_empty_as_zero(self):
+ ### return bool(self.__show_empty_as_zero)
+
+ ### show_empty_as_zero = property(get_show_empty_as_zero, set_show_empty_as_zero)
+
+ #################################################################
+
+ def set_auto_colour_grid(self, value):
+ self.__auto_colour_grid = int(value)
+
+ def get_auto_colour_grid(self):
+ return bool(self.__auto_colour_grid)
+
+ auto_colour_grid = property(get_auto_colour_grid, set_auto_colour_grid)
+
+ #################################################################
+
+ def set_cols_right_to_left(self, value):
+ self.__cols_right_to_left = int(value)
+
+ def get_cols_right_to_left(self):
+ return bool(self.__cols_right_to_left)
+
+ cols_right_to_left = property(get_cols_right_to_left, set_cols_right_to_left)
+
+ #################################################################
+
+ def set_show_outline(self, value):
+ self.__show_outline = int(value)
+
+ def get_show_outline(self):
+ return bool(self.__show_outline)
+
+ show_outline = property(get_show_outline, set_show_outline)
+
+ #################################################################
+
+ def set_remove_splits(self, value):
+ self.__remove_splits = int(value)
+
+ def get_remove_splits(self):
+ return bool(self.__remove_splits)
+
+ remove_splits = property(get_remove_splits, set_remove_splits)
+
+ #################################################################
+
+ def set_selected(self, value):
+ self.__selected = int(value)
+
+ def get_selected(self):
+ return bool(self.__selected)
+
+ selected = property(get_selected, set_selected)
+
+ #################################################################
+
+ def set_sheet_visible(self, value):
+ self.__sheet_visible = int(value)
+
+ def get_sheet_visible(self):
+ return bool(self.__sheet_visible)
+
+ sheet_visible = property(get_sheet_visible, set_sheet_visible)
+
+ #################################################################
+
+ def set_page_preview(self, value):
+ self.__page_preview = int(value)
+
+ def get_page_preview(self):
+ return bool(self.__page_preview)
+
+ page_preview = property(get_page_preview, set_page_preview)
+
+ #################################################################
+
+ def set_first_visible_row(self, value):
+ self.__first_visible_row = value
+
+ def get_first_visible_row(self):
+ return self.__first_visible_row
+
+ first_visible_row = property(get_first_visible_row, set_first_visible_row)
+
+ #################################################################
+
+ def set_first_visible_col(self, value):
+ self.__first_visible_col = value
+
+ def get_first_visible_col(self):
+ return self.__first_visible_col
+
+ first_visible_col = property(get_first_visible_col, set_first_visible_col)
+
+ #################################################################
+
+ def set_grid_colour(self, value):
+ self.__grid_colour = value
+
+ def get_grid_colour(self):
+ return self.__grid_colour
+
+ grid_colour = property(get_grid_colour, set_grid_colour)
+
+ #################################################################
+
+ def set_preview_magn(self, value):
+ self.__preview_magn = value
+
+ def get_preview_magn(self):
+ return self.__preview_magn
+
+ preview_magn = property(get_preview_magn, set_preview_magn)
+
+ #################################################################
+
+ def set_normal_magn(self, value):
+ self.__normal_magn = value
+
+ def get_normal_magn(self):
+ return self.__normal_magn
+
+ normal_magn = property(get_normal_magn, set_normal_magn)
+
+ #################################################################
+
+ def set_scl_magn(self, value):
+ self.__scl_magn = value
+
+ def get_scl_magn(self):
+ return self.__scl_magn
+
+ scl_magn = property(get_scl_magn, set_scl_magn)
+
+
+ #################################################################
+
+ def set_vert_split_pos(self, value):
+ self.__vert_split_pos = abs(value)
+
+ def get_vert_split_pos(self):
+ return self.__vert_split_pos
+
+ vert_split_pos = property(get_vert_split_pos, set_vert_split_pos)
+
+ #################################################################
+
+ def set_horz_split_pos(self, value):
+ self.__horz_split_pos = abs(value)
+
+ def get_horz_split_pos(self):
+ return self.__horz_split_pos
+
+ horz_split_pos = property(get_horz_split_pos, set_horz_split_pos)
+
+ #################################################################
+
+ def set_vert_split_first_visible(self, value):
+ self.__vert_split_first_visible = abs(value)
+
+ def get_vert_split_first_visible(self):
+ return self.__vert_split_first_visible
+
+ vert_split_first_visible = property(get_vert_split_first_visible, set_vert_split_first_visible)
+
+ #################################################################
+
+ def set_horz_split_first_visible(self, value):
+ self.__horz_split_first_visible = abs(value)
+
+ def get_horz_split_first_visible(self):
+ return self.__horz_split_first_visible
+
+ horz_split_first_visible = property(get_horz_split_first_visible, set_horz_split_first_visible)
+
+ #################################################################
+
+ #def set_row_gut_width(self, value):
+ # self.__row_gut_width = value
+ #
+ #def get_row_gut_width(self):
+ # return self.__row_gut_width
+ #
+ #row_gut_width = property(get_row_gut_width, set_row_gut_width)
+ #
+ #################################################################
+ #
+ #def set_col_gut_height(self, value):
+ # self.__col_gut_height = value
+ #
+ #def get_col_gut_height(self):
+ # return self.__col_gut_height
+ #
+ #col_gut_height = property(get_col_gut_height, set_col_gut_height)
+ #
+ #################################################################
+
+ def set_show_auto_page_breaks(self, value):
+ self.__show_auto_page_breaks = int(value)
+
+ def get_show_auto_page_breaks(self):
+ return bool(self.__show_auto_page_breaks)
+
+ show_auto_page_breaks = property(get_show_auto_page_breaks, set_show_auto_page_breaks)
+
+ #################################################################
+
+ def set_dialogue_sheet(self, value):
+ self.__dialogue_sheet = int(value)
+
+ def get_dialogue_sheet(self):
+ return bool(self.__dialogue_sheet)
+
+ dialogue_sheet = property(get_dialogue_sheet, set_dialogue_sheet)
+
+ #################################################################
+
+ def set_auto_style_outline(self, value):
+ self.__auto_style_outline = int(value)
+
+ def get_auto_style_outline(self):
+ return bool(self.__auto_style_outline)
+
+ auto_style_outline = property(get_auto_style_outline, set_auto_style_outline)
+
+ #################################################################
+
+ def set_outline_below(self, value):
+ self.__outline_below = int(value)
+
+ def get_outline_below(self):
+ return bool(self.__outline_below)
+
+ outline_below = property(get_outline_below, set_outline_below)
+
+ #################################################################
+
+ def set_outline_right(self, value):
+ self.__outline_right = int(value)
+
+ def get_outline_right(self):
+ return bool(self.__outline_right)
+
+ outline_right = property(get_outline_right, set_outline_right)
+
+ #################################################################
+
+ def set_fit_num_pages(self, value):
+ self.__fit_num_pages = value
+
+ def get_fit_num_pages(self):
+ return self.__fit_num_pages
+
+ fit_num_pages = property(get_fit_num_pages, set_fit_num_pages)
+
+ #################################################################
+
+ def set_show_row_outline(self, value):
+ self.__show_row_outline = int(value)
+
+ def get_show_row_outline(self):
+ return bool(self.__show_row_outline)
+
+ show_row_outline = property(get_show_row_outline, set_show_row_outline)
+
+ #################################################################
+
+ def set_show_col_outline(self, value):
+ self.__show_col_outline = int(value)
+
+ def get_show_col_outline(self):
+ return bool(self.__show_col_outline)
+
+ show_col_outline = property(get_show_col_outline, set_show_col_outline)
+
+ #################################################################
+
+ def set_alt_expr_eval(self, value):
+ self.__alt_expr_eval = int(value)
+
+ def get_alt_expr_eval(self):
+ return bool(self.__alt_expr_eval)
+
+ alt_expr_eval = property(get_alt_expr_eval, set_alt_expr_eval)
+
+ #################################################################
+
+ def set_alt_formula_entries(self, value):
+ self.__alt_formula_entries = int(value)
+
+ def get_alt_formula_entries(self):
+ return bool(self.__alt_formula_entries)
+
+ alt_formula_entries = property(get_alt_formula_entries, set_alt_formula_entries)
+
+ #################################################################
+
+ def set_row_default_height(self, value):
+ self.__row_default_height = value
+
+ def get_row_default_height(self):
+ return self.__row_default_height
+
+ row_default_height = property(get_row_default_height, set_row_default_height)
+
+ #################################################################
+
+ def set_col_default_width(self, value):
+ self.__col_default_width = value
+
+ def get_col_default_width(self):
+ return self.__col_default_width
+
+ col_default_width = property(get_col_default_width, set_col_default_width)
+
+ #################################################################
+
+ def set_calc_mode(self, value):
+ self.__calc_mode = value & 0x03
+
+ def get_calc_mode(self):
+ return self.__calc_mode
+
+ calc_mode = property(get_calc_mode, set_calc_mode)
+
+ #################################################################
+
+ def set_calc_count(self, value):
+ self.__calc_count = value
+
+ def get_calc_count(self):
+ return self.__calc_count
+
+ calc_count = property(get_calc_count, set_calc_count)
+
+ #################################################################
+
+ def set_RC_ref_mode(self, value):
+ self.__RC_ref_mode = int(value)
+
+ def get_RC_ref_mode(self):
+ return bool(self.__RC_ref_mode)
+
+ RC_ref_mode = property(get_RC_ref_mode, set_RC_ref_mode)
+
+ #################################################################
+
+ def set_iterations_on(self, value):
+ self.__iterations_on = int(value)
+
+ def get_iterations_on(self):
+ return bool(self.__iterations_on)
+
+ iterations_on = property(get_iterations_on, set_iterations_on)
+
+ #################################################################
+
+ def set_delta(self, value):
+ self.__delta = value
+
+ def get_delta(self):
+ return self.__delta
+
+ delta = property(get_delta, set_delta)
+
+ #################################################################
+
+ def set_save_recalc(self, value):
+ self.__save_recalc = int(value)
+
+ def get_save_recalc(self):
+ return bool(self.__save_recalc)
+
+ save_recalc = property(get_save_recalc, set_save_recalc)
+
+ #################################################################
+
+ def set_print_headers(self, value):
+ self.__print_headers = int(value)
+
+ def get_print_headers(self):
+ return bool(self.__print_headers)
+
+ print_headers = property(get_print_headers, set_print_headers)
+
+ #################################################################
+
+ def set_print_grid(self, value):
+ self.__print_grid = int(value)
+
+ def get_print_grid(self):
+ return bool(self.__print_grid)
+
+ print_grid = property(get_print_grid, set_print_grid)
+
+ #################################################################
+ #
+ #def set_grid_set(self, value):
+ # self.__grid_set = int(value)
+ #
+ #def get_grid_set(self):
+ # return bool(self.__grid_set)
+ #
+ #grid_set = property(get_grid_set, set_grid_set)
+ #
+ #################################################################
+
+ def set_vert_page_breaks(self, value):
+ self.__vert_page_breaks = value
+
+ def get_vert_page_breaks(self):
+ return self.__vert_page_breaks
+
+ vert_page_breaks = property(get_vert_page_breaks, set_vert_page_breaks)
+
+ #################################################################
+
+ def set_horz_page_breaks(self, value):
+ self.__horz_page_breaks = value
+
+ def get_horz_page_breaks(self):
+ return self.__horz_page_breaks
+
+ horz_page_breaks = property(get_horz_page_breaks, set_horz_page_breaks)
+
+ #################################################################
+
+ def set_header_str(self, value):
+ if isinstance(value, str):
+ value = unicode(value, self.__parent.encoding)
+ self.__header_str = value
+
+ def get_header_str(self):
+ return self.__header_str
+
+ header_str = property(get_header_str, set_header_str)
+
+ #################################################################
+
+ def set_footer_str(self, value):
+ if isinstance(value, str):
+ value = unicode(value, self.__parent.encoding)
+ self.__footer_str = value
+
+ def get_footer_str(self):
+ return self.__footer_str
+
+ footer_str = property(get_footer_str, set_footer_str)
+
+ #################################################################
+
+ def set_print_centered_vert(self, value):
+ self.__print_centered_vert = int(value)
+
+ def get_print_centered_vert(self):
+ return bool(self.__print_centered_vert)
+
+ print_centered_vert = property(get_print_centered_vert, set_print_centered_vert)
+
+ #################################################################
+
+ def set_print_centered_horz(self, value):
+ self.__print_centered_horz = int(value)
+
+ def get_print_centered_horz(self):
+ return bool(self.__print_centered_horz)
+
+ print_centered_horz = property(get_print_centered_horz, set_print_centered_horz)
+
+ #################################################################
+
+ def set_left_margin(self, value):
+ self.__left_margin = value
+
+ def get_left_margin(self):
+ return self.__left_margin
+
+ left_margin = property(get_left_margin, set_left_margin)
+
+ #################################################################
+
+ def set_right_margin(self, value):
+ self.__right_margin = value
+
+ def get_right_margin(self):
+ return self.__right_margin
+
+ right_margin = property(get_right_margin, set_right_margin)
+
+ #################################################################
+
+ def set_top_margin(self, value):
+ self.__top_margin = value
+
+ def get_top_margin(self):
+ return self.__top_margin
+
+ top_margin = property(get_top_margin, set_top_margin)
+
+ #################################################################
+
+ def set_bottom_margin(self, value):
+ self.__bottom_margin = value
+
+ def get_bottom_margin(self):
+ return self.__bottom_margin
+
+ bottom_margin = property(get_bottom_margin, set_bottom_margin)
+
+ #################################################################
+
+ def set_paper_size_code(self, value):
+ self.__paper_size_code = value
+
+ def get_paper_size_code(self):
+ return self.__paper_size_code
+
+ paper_size_code = property(get_paper_size_code, set_paper_size_code)
+
+ #################################################################
+
+ def set_print_scaling(self, value):
+ self.__print_scaling = value
+
+ def get_print_scaling(self):
+ return self.__print_scaling
+
+ print_scaling = property(get_print_scaling, set_print_scaling)
+
+ #################################################################
+
+ def set_start_page_number(self, value):
+ self.__start_page_number = value
+
+ def get_start_page_number(self):
+ return self.__start_page_number
+
+ start_page_number = property(get_start_page_number, set_start_page_number)
+
+ #################################################################
+
+ def set_fit_width_to_pages(self, value):
+ self.__fit_width_to_pages = value
+
+ def get_fit_width_to_pages(self):
+ return self.__fit_width_to_pages
+
+ fit_width_to_pages = property(get_fit_width_to_pages, set_fit_width_to_pages)
+
+ #################################################################
+
+ def set_fit_height_to_pages(self, value):
+ self.__fit_height_to_pages = value
+
+ def get_fit_height_to_pages(self):
+ return self.__fit_height_to_pages
+
+ fit_height_to_pages = property(get_fit_height_to_pages, set_fit_height_to_pages)
+
+ #################################################################
+
+ def set_print_in_rows(self, value):
+ self.__print_in_rows = int(value)
+
+ def get_print_in_rows(self):
+ return bool(self.__print_in_rows)
+
+ print_in_rows = property(get_print_in_rows, set_print_in_rows)
+
+ #################################################################
+
+ def set_portrait(self, value):
+ self.__portrait = int(value)
+
+ def get_portrait(self):
+ return bool(self.__portrait)
+
+ portrait = property(get_portrait, set_portrait)
+
+ #################################################################
+
+ def set_print_colour(self, value):
+ self.__print_not_colour = int(not value)
+
+ def get_print_colour(self):
+ return not bool(self.__print_not_colour)
+
+ print_colour = property(get_print_colour, set_print_colour)
+
+ #################################################################
+
+ def set_print_draft(self, value):
+ self.__print_draft = int(value)
+
+ def get_print_draft(self):
+ return bool(self.__print_draft)
+
+ print_draft = property(get_print_draft, set_print_draft)
+
+ #################################################################
+
+ def set_print_notes(self, value):
+ self.__print_notes = int(value)
+
+ def get_print_notes(self):
+ return bool(self.__print_notes)
+
+ print_notes = property(get_print_notes, set_print_notes)
+
+ #################################################################
+
+ def set_print_notes_at_end(self, value):
+ self.__print_notes_at_end = int(value)
+
+ def get_print_notes_at_end(self):
+ return bool(self.__print_notes_at_end)
+
+ print_notes_at_end = property(get_print_notes_at_end, set_print_notes_at_end)
+
+ #################################################################
+
+ def set_print_omit_errors(self, value):
+ self.__print_omit_errors = int(value)
+
+ def get_print_omit_errors(self):
+ return bool(self.__print_omit_errors)
+
+ print_omit_errors = property(get_print_omit_errors, set_print_omit_errors)
+
+ #################################################################
+
+ def set_print_hres(self, value):
+ self.__print_hres = value
+
+ def get_print_hres(self):
+ return self.__print_hres
+
+ print_hres = property(get_print_hres, set_print_hres)
+
+ #################################################################
+
+ def set_print_vres(self, value):
+ self.__print_vres = value
+
+ def get_print_vres(self):
+ return self.__print_vres
+
+ print_vres = property(get_print_vres, set_print_vres)
+
+ #################################################################
+
+ def set_header_margin(self, value):
+ self.__header_margin = value
+
+ def get_header_margin(self):
+ return self.__header_margin
+
+ header_margin = property(get_header_margin, set_header_margin)
+
+ #################################################################
+
+ def set_footer_margin(self, value):
+ self.__footer_margin = value
+
+ def get_footer_margin(self):
+ return self.__footer_margin
+
+ footer_margin = property(get_footer_margin, set_footer_margin)
+
+ #################################################################
+
+ def set_copies_num(self, value):
+ self.__copies_num = value
+
+ def get_copies_num(self):
+ return self.__copies_num
+
+ copies_num = property(get_copies_num, set_copies_num)
+
+ ##################################################################
+
+ def set_wnd_protect(self, value):
+ self.__wnd_protect = int(value)
+
+ def get_wnd_protect(self):
+ return bool(self.__wnd_protect)
+
+ wnd_protect = property(get_wnd_protect, set_wnd_protect)
+
+ #################################################################
+
+ def set_obj_protect(self, value):
+ self.__obj_protect = int(value)
+
+ def get_obj_protect(self):
+ return bool(self.__obj_protect)
+
+ obj_protect = property(get_obj_protect, set_obj_protect)
+
+ #################################################################
+
+ def set_protect(self, value):
+ self.__protect = int(value)
+
+ def get_protect(self):
+ return bool(self.__protect)
+
+ protect = property(get_protect, set_protect)
+
+ #################################################################
+
+ def set_scen_protect(self, value):
+ self.__scen_protect = int(value)
+
+ def get_scen_protect(self):
+ return bool(self.__scen_protect)
+
+ scen_protect = property(get_scen_protect, set_scen_protect)
+
+ #################################################################
+
+ def set_password(self, value):
+ self.__password = value
+
+ def get_password(self):
+ return self.__password
+
+ password = property(get_password, set_password)
+
+ ##################################################################
+ ## Methods
+ ##################################################################
+
+ def get_parent(self):
+ return self.__parent
+
+ def write(self, r, c, label="", style=Style.default_style):
+ """
+ This method is used to write a cell to a :class:`Worksheet`.
+
+ :param r:
+
+ The zero-relative number of the row in the worksheet to which
+ the cell should be written.
+
+ :param c:
+
+ The zero-relative number of the column in the worksheet to which
+ the cell should be written.
+
+ :param label:
+
+ The data value to be written.
+
+ An :class:`int`, :class:`long`, or
+ :class:`~decimal.Decimal` instance is converted to :class:`float`.
+
+ A :class:`unicode` instance is written as is. A :class:`bytes`
+ instance is converted to :class:`unicode` using the
+ encoding, which defaults to ``ascii``, specified when the
+ :class:`Workbook` instance was created.
+
+ A :class:`~datetime.datetime`, :class:`~datetime.date` or
+ :class:`~datetime.time` instance is converted into Excel date format
+ (a float representing the number of days since (typically)
+ ``1899-12-31T00:00:00``, under the pretence that
+ 1900 was a leap year).
+
+ A :class:`bool` instance will show up as ``TRUE`` or ``FALSE`` in
+ Excel.
+
+ ``None`` causes the cell to be blank: no data, only formatting.
+
+ An :class:`xlwt.Formula` instance causes an Excel formula to be
+ written.
+
+ :param style:
+
+ A style, also known as an XF (extended format), is an
+ :class:`~xlwt.Style.XFStyle` object, which encapsulates the
+ formatting applied to the cell and its contents.
+
+ :class:`~xlwt.Style.XFStyle` objects are best set up using the
+ :func:`~xlwt.Style.easyxf` function. They may also be set up by
+ setting attributes in :class:`Alignment`, :class:`Borders`,
+ :class:`Pattern`, :class:`Font` and :class:`Protection` objects then
+ setting those objects and a format string as attributes of an
+ :class:`~xlwt.Style.XFStyle` object.
+ """
+ self.row(r).write(c, label, style)
+
+ def write_rich_text(self, r, c, rich_text_list, style=Style.default_style):
+ self.row(r).set_cell_rich_text(c, rich_text_list, style)
+
+ def merge(self, r1, r2, c1, c2, style=Style.default_style):
+ # Stand-alone merge of previously written cells.
+ # Problems: (1) style to be used should be existing style of
+ # the top-left cell, not an arg.
+ # (2) should ensure that any previous data value in
+ # non-top-left cells is nobbled.
+ # Note: if a cell is set by a data record then later
+ # is referenced by a [MUL]BLANK record, Excel will blank
+ # out the cell on the screen, but OOo & Gnu will not
+ # blank it out. Need to do something better than writing
+ # multiple records. In the meantime, avoid this method and use
+ # write_merge() instead.
+ if c2 > c1:
+ self.row(r1).write_blanks(c1 + 1, c2, style)
+ for r in range(r1+1, r2+1):
+ self.row(r).write_blanks(c1, c2, style)
+ self.__merged_ranges.append((r1, r2, c1, c2))
+
+ def write_merge(self, r1, r2, c1, c2, label="", style=Style.default_style):
+ assert 0 <= c1 <= c2 <= 255
+ assert 0 <= r1 <= r2 <= 65535
+ self.write(r1, c1, label, style)
+ if c2 > c1:
+ self.row(r1).write_blanks(c1 + 1, c2, style) # skip (r1, c1)
+ for r in range(r1+1, r2+1):
+ self.row(r).write_blanks(c1, c2, style)
+ self.__merged_ranges.append((r1, r2, c1, c2))
+
+ def insert_bitmap(self, filename, row, col, x = 0, y = 0, scale_x = 1, scale_y = 1):
+ bmp = Bitmap.ImDataBmpRecord(filename)
+ obj = Bitmap.ObjBmpRecord(row, col, self, bmp, x, y, scale_x, scale_y)
+
+ self.__bmp_rec += obj.get() + bmp.get()
+
+ def insert_bitmap_data(self, data, row, col, x = 0, y = 0, scale_x = 1, scale_y = 1):
+ bmp = Bitmap.ImRawDataBmpRecord(data)
+ obj = Bitmap.ObjBmpRecord(row, col, self, bmp, x, y, scale_x, scale_y)
+
+ self.__bmp_rec += obj.get() + bmp.get()
+
+ def col(self, indx):
+ if indx not in self.__cols:
+ self.__cols[indx] = self.Column(indx, self)
+ return self.__cols[indx]
+
+ def row(self, indx):
+ if indx not in self.__rows:
+ if indx in self.__flushed_rows:
+ raise Exception("Attempt to reuse row index %d of sheet %r after flushing" % (indx, self.__name))
+ self.__rows[indx] = self.Row(indx, self)
+ if indx > self.last_used_row:
+ self.last_used_row = indx
+ if indx < self.first_used_row:
+ self.first_used_row = indx
+ return self.__rows[indx]
+
+ def row_height(self, row): # in pixels
+ if row in self.__rows:
+ return self.__rows[row].get_height_in_pixels()
+ else:
+ return 17
+
+ def col_width(self, col): # in pixels
+ if col in self.__cols:
+ return self.__cols[col].width_in_pixels()
+ else:
+ return 64
+
+
+ ##################################################################
+ ## BIFF records generation
+ ##################################################################
+
+ def __bof_rec(self):
+ return BIFFRecords.Biff8BOFRecord(BIFFRecords.Biff8BOFRecord.WORKSHEET).get()
+
+ def __update_row_visible_levels(self):
+ if self.__rows:
+ temp = max(self.__rows[r].level for r in self.__rows) + 1
+ self.__row_visible_levels = max(temp, self.__row_visible_levels)
+
+ def __guts_rec(self):
+ self.__update_row_visible_levels()
+ col_visible_levels = 0
+ if len(self.__cols) != 0:
+ col_visible_levels = max(self.__cols[c].level for c in self.__cols) + 1
+ return BIFFRecords.GutsRecord(
+ self.__row_gut_width, self.__col_gut_height, self.__row_visible_levels, col_visible_levels).get()
+
+ def __defaultrowheight_rec(self):
+ options = 0x0000
+ options |= (self.row_default_height_mismatch & 1) << 0
+ options |= (self.row_default_hidden & 1) << 1
+ options |= (self.row_default_space_above & 1) << 2
+ options |= (self.row_default_space_below & 1) << 3
+ defht = self.__row_default_height
+ return BIFFRecords.DefaultRowHeightRecord(options, defht).get()
+
+ def __wsbool_rec(self):
+ options = 0x00
+ options |= (self.__show_auto_page_breaks & 0x01) << 0
+ options |= (self.__dialogue_sheet & 0x01) << 4
+ options |= (self.__auto_style_outline & 0x01) << 5
+ options |= (self.__outline_below & 0x01) << 6
+ options |= (self.__outline_right & 0x01) << 7
+ options |= (self.__fit_num_pages & 0x01) << 8
+ options |= (self.__show_row_outline & 0x01) << 10
+ options |= (self.__show_col_outline & 0x01) << 11
+ options |= (self.__alt_expr_eval & 0x01) << 14
+ options |= (self.__alt_formula_entries & 0x01) << 15
+
+ return BIFFRecords.WSBoolRecord(options).get()
+
+ def __eof_rec(self):
+ return BIFFRecords.EOFRecord().get()
+
+ def __colinfo_rec(self):
+ result = b''
+ for col in self.__cols:
+ result += self.__cols[col].get_biff_record()
+ return result
+
+ def __dimensions_rec(self):
+ return BIFFRecords.DimensionsRecord(
+ self.first_used_row, self.last_used_row,
+ self.first_used_col, self.last_used_col
+ ).get()
+
+ def __window2_rec(self):
+ # Appends SCL record.
+ options = 0
+ options |= (self.__show_formulas & 0x01) << 0
+ options |= (self.__show_grid & 0x01) << 1
+ options |= (self.__show_headers & 0x01) << 2
+ options |= (self.__panes_frozen & 0x01) << 3
+ options |= (self.show_zero_values & 0x01) << 4
+ options |= (self.__auto_colour_grid & 0x01) << 5
+ options |= (self.__cols_right_to_left & 0x01) << 6
+ options |= (self.__show_outline & 0x01) << 7
+ options |= (self.__remove_splits & 0x01) << 8
+ options |= (self.__selected & 0x01) << 9
+ options |= (self.__sheet_visible & 0x01) << 10
+ options |= (self.__page_preview & 0x01) << 11
+ if self.explicit_magn_setting:
+ # Experimentation: caller can set the scl magn.
+ # None -> no SCL record written
+ # Otherwise 10 <= scl_magn <= 400 or scl_magn == 0
+ # Note: value 0 means use 100 for normal view, 60 for page break preview
+ # BREAKING NEWS: Excel interprets scl_magn = 0 very literally, your
+ # sheet appears like a tiny dot on the screen
+ scl_magn = self.__scl_magn
+ else:
+ if self.__page_preview:
+ scl_magn = self.__preview_magn
+ magn_default = 60
+ else:
+ scl_magn = self.__normal_magn
+ magn_default = 100
+ if scl_magn == magn_default or scl_magn == 0:
+ # Emulate what we think MS does
+ scl_magn = None # don't write an SCL record
+ return BIFFRecords.Window2Record(
+ options, self.__first_visible_row, self.__first_visible_col,
+ self.__grid_colour,
+ self.__preview_magn, self.__normal_magn, scl_magn).get()
+
+ def __panes_rec(self):
+ if self.__vert_split_pos is None and self.__horz_split_pos is None:
+ return b""
+
+ if self.__vert_split_pos is None:
+ self.__vert_split_pos = 0
+ if self.__horz_split_pos is None:
+ self.__horz_split_pos = 0
+
+ if self.__panes_frozen:
+ if self.__vert_split_first_visible is None:
+ self.__vert_split_first_visible = self.__vert_split_pos
+ if self.__horz_split_first_visible is None:
+ self.__horz_split_first_visible = self.__horz_split_pos
+
+ # when frozen, the active pane has to be specifically set:
+ if self.__vert_split_pos > 0 and self.__horz_split_pos > 0:
+ active_pane = 0
+ elif self.__vert_split_pos > 0 and self.__horz_split_pos == 0:
+ active_pane = 1
+ elif self.__vert_split_pos == 0 and self.__horz_split_pos > 0:
+ active_pane = 2
+ else:
+ active_pane = 3
+ else:
+ if self.__vert_split_first_visible is None:
+ self.__vert_split_first_visible = 0
+ if self.__horz_split_first_visible is None:
+ self.__horz_split_first_visible = 0
+ if not self.split_position_units_are_twips:
+ # inspired by pyXLWriter
+ if self.__horz_split_pos > 0:
+ self.__horz_split_pos = 20 * self.__horz_split_pos + 255
+ if self.__vert_split_pos > 0:
+ self.__vert_split_pos = 113.879 * self.__vert_split_pos + 390
+
+ # when split, the active pain can be set as required:
+ active_pane = self.active_pane
+
+ result = BIFFRecords.PanesRecord(*map(int, (
+ self.__vert_split_pos,
+ self.__horz_split_pos,
+ self.__horz_split_first_visible,
+ self.__vert_split_first_visible,
+ active_pane
+ ))).get()
+
+ return result
+
+ def __row_blocks_rec(self):
+ result = []
+ for row in itervalues(self.__rows):
+ result.append(row.get_row_biff_data())
+ result.append(row.get_cells_biff_data())
+ return b''.join(result)
+
+ def __merged_rec(self):
+ return BIFFRecords.MergedCellsRecord(self.__merged_ranges).get()
+
+ def __bitmaps_rec(self):
+ return self.__bmp_rec
+
+ def __calc_settings_rec(self):
+ result = b''
+ result += BIFFRecords.CalcModeRecord(self.__calc_mode & 0x01).get()
+ result += BIFFRecords.CalcCountRecord(self.__calc_count & 0xFFFF).get()
+ result += BIFFRecords.RefModeRecord(self.__RC_ref_mode & 0x01).get()
+ result += BIFFRecords.IterationRecord(self.__iterations_on & 0x01).get()
+ result += BIFFRecords.DeltaRecord(self.__delta).get()
+ result += BIFFRecords.SaveRecalcRecord(self.__save_recalc & 0x01).get()
+ return result
+
+ def __print_settings_rec(self):
+ result = b''
+ result += BIFFRecords.PrintHeadersRecord(self.__print_headers).get()
+ result += BIFFRecords.PrintGridLinesRecord(self.__print_grid).get()
+ result += BIFFRecords.GridSetRecord(self.__grid_set).get()
+ result += BIFFRecords.HorizontalPageBreaksRecord(self.__horz_page_breaks).get()
+ result += BIFFRecords.VerticalPageBreaksRecord(self.__vert_page_breaks).get()
+ result += BIFFRecords.HeaderRecord(self.__header_str).get()
+ result += BIFFRecords.FooterRecord(self.__footer_str).get()
+ result += BIFFRecords.HCenterRecord(self.__print_centered_horz).get()
+ result += BIFFRecords.VCenterRecord(self.__print_centered_vert).get()
+ result += BIFFRecords.LeftMarginRecord(self.__left_margin).get()
+ result += BIFFRecords.RightMarginRecord(self.__right_margin).get()
+ result += BIFFRecords.TopMarginRecord(self.__top_margin).get()
+ result += BIFFRecords.BottomMarginRecord(self.__bottom_margin).get()
+
+ setup_page_options = (self.__print_in_rows & 0x01) << 0
+ setup_page_options |= (self.__portrait & 0x01) << 1
+ setup_page_options |= (0x00 & 0x01) << 2
+ setup_page_options |= (self.__print_not_colour & 0x01) << 3
+ setup_page_options |= (self.__print_draft & 0x01) << 4
+ setup_page_options |= (self.__print_notes & 0x01) << 5
+ setup_page_options |= (0x00 & 0x01) << 6
+ setup_page_options |= (0x01 & 0x01) << 7
+ setup_page_options |= (self.__print_notes_at_end & 0x01) << 9
+ setup_page_options |= (self.__print_omit_errors & 0x03) << 10
+
+ result += BIFFRecords.SetupPageRecord(self.__paper_size_code,
+ self.__print_scaling,
+ self.__start_page_number,
+ self.__fit_width_to_pages,
+ self.__fit_height_to_pages,
+ setup_page_options,
+ self.__print_hres,
+ self.__print_vres,
+ self.__header_margin,
+ self.__footer_margin,
+ self.__copies_num).get()
+ return result
+
+ def __protection_rec(self):
+ result = b''
+ result += BIFFRecords.ProtectRecord(self.__protect).get()
+ result += BIFFRecords.ScenProtectRecord(self.__scen_protect).get()
+ result += BIFFRecords.WindowProtectRecord(self.__wnd_protect).get()
+ result += BIFFRecords.ObjectProtectRecord(self.__obj_protect).get()
+ result += BIFFRecords.PasswordRecord(self.__password).get()
+ return result
+
+ def get_biff_data(self):
+ result = [
+ self.__bof_rec(),
+ self.__calc_settings_rec(),
+ self.__guts_rec(),
+ self.__defaultrowheight_rec(),
+ self.__wsbool_rec(),
+ self.__colinfo_rec(),
+ self.__dimensions_rec(),
+ self.__print_settings_rec(),
+ self.__protection_rec(),
+ ]
+ if self.row_tempfile:
+ self.row_tempfile.flush()
+ self.row_tempfile.seek(0)
+ result.append(self.row_tempfile.read())
+ self.row_tempfile.seek(0, 2) # to EOF
+ # Above seek() is necessary to avoid a spurious IOError
+ # with Errno 0 if the caller continues on writing rows
+ # and flushing row data after the save().
+ # See https://bugs.python.org/issue3207
+ result.extend([
+ self.__row_blocks_rec(),
+ self.__merged_rec(),
+ self.__bitmaps_rec(),
+ self.__window2_rec(),
+ self.__panes_rec(),
+ self.__eof_rec(),
+ ])
+ return b''.join(result)
+
+ def flush_row_data(self):
+ if self.row_tempfile is None:
+ self.row_tempfile = tempfile.TemporaryFile()
+ self.row_tempfile.write(self.__row_blocks_rec())
+ for rowx in self.__rows:
+ self.__flushed_rows[rowx] = 1
+ self.__update_row_visible_levels()
+ self.__rows = {}
+
+
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__init__.py b/.venv/lib/python3.9/site-packages/xlwt/__init__.py
new file mode 100644
index 00000000..1a2d64b8
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt/__init__.py
@@ -0,0 +1,9 @@
+__VERSION__ = '1.3.0'
+
+from .Workbook import Workbook
+from .Worksheet import Worksheet
+from .Row import Row
+from .Column import Column
+from .Formatting import Font, Alignment, Borders, Pattern, Protection
+from .Style import XFStyle, easyxf, easyfont, add_palette_colour
+from .ExcelFormula import *
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/BIFFRecords.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/BIFFRecords.cpython-39.pyc
new file mode 100644
index 00000000..cd21689b
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/BIFFRecords.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Bitmap.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Bitmap.cpython-39.pyc
new file mode 100644
index 00000000..34bd47d4
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Bitmap.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Cell.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Cell.cpython-39.pyc
new file mode 100644
index 00000000..2908c00f
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Cell.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Column.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Column.cpython-39.pyc
new file mode 100644
index 00000000..c4f17610
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Column.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/CompoundDoc.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/CompoundDoc.cpython-39.pyc
new file mode 100644
index 00000000..41e183fc
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/CompoundDoc.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormula.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormula.cpython-39.pyc
new file mode 100644
index 00000000..4ce76676
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormula.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormulaLexer.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormulaLexer.cpython-39.pyc
new file mode 100644
index 00000000..18f4069c
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormulaLexer.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormulaParser.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormulaParser.cpython-39.pyc
new file mode 100644
index 00000000..3783a865
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormulaParser.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelMagic.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelMagic.cpython-39.pyc
new file mode 100644
index 00000000..877e858b
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelMagic.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Formatting.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Formatting.cpython-39.pyc
new file mode 100644
index 00000000..ed4b73c9
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Formatting.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Row.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Row.cpython-39.pyc
new file mode 100644
index 00000000..f82cf703
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Row.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Style.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Style.cpython-39.pyc
new file mode 100644
index 00000000..e7fbcc66
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Style.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/UnicodeUtils.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/UnicodeUtils.cpython-39.pyc
new file mode 100644
index 00000000..b288d0d0
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/UnicodeUtils.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Utils.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Utils.cpython-39.pyc
new file mode 100644
index 00000000..558a75fd
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Utils.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Workbook.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Workbook.cpython-39.pyc
new file mode 100644
index 00000000..a1a6629d
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Workbook.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Worksheet.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Worksheet.cpython-39.pyc
new file mode 100644
index 00000000..aa164de1
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Worksheet.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/__init__.cpython-39.pyc
new file mode 100644
index 00000000..b99e9993
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/__init__.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/antlr.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/antlr.cpython-39.pyc
new file mode 100644
index 00000000..6d6ba3a9
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/antlr.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/compat.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/compat.cpython-39.pyc
new file mode 100644
index 00000000..9893c513
Binary files /dev/null and b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/compat.cpython-39.pyc differ
diff --git a/.venv/lib/python3.9/site-packages/xlwt/antlr.py b/.venv/lib/python3.9/site-packages/xlwt/antlr.py
new file mode 100644
index 00000000..b9d93f0d
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt/antlr.py
@@ -0,0 +1,2864 @@
+from __future__ import print_function
+## This file is part of PyANTLR. See LICENSE.txt for license
+## details..........Copyright (C) Wolfgang Haefelinger, 2004.
+
+## This file was copied for use with xlwt from the 2.7.7 ANTLR distribution. Yes, it
+## says 2.7.5 below. The 2.7.5 distribution version didn't have a
+## version in it.
+
+## Here is the contents of the ANTLR 2.7.7 LICENSE.txt referred to above.
+
+# SOFTWARE RIGHTS
+#
+# ANTLR 1989-2006 Developed by Terence Parr
+# Partially supported by University of San Francisco & jGuru.com
+#
+# We reserve no legal rights to the ANTLR--it is fully in the
+# public domain. An individual or company may do whatever
+# they wish with source code distributed with ANTLR or the
+# code generated by ANTLR, including the incorporation of
+# ANTLR, or its output, into commerical software.
+#
+# We encourage users to develop software with ANTLR. However,
+# we do ask that credit is given to us for developing
+# ANTLR. By "credit", we mean that if you use ANTLR or
+# incorporate any source code into one of your programs
+# (commercial product, research project, or otherwise) that
+# you acknowledge this fact somewhere in the documentation,
+# research report, etc... If you like ANTLR and have
+# developed a nice tool with the output, please mention that
+# you developed it using ANTLR. In addition, we ask that the
+# headers remain intact in our source code. As long as these
+# guidelines are kept, we expect to continue enhancing this
+# system and expect to make other tools available as they are
+# completed.
+#
+# The primary ANTLR guy:
+#
+# Terence Parr
+# parrt@cs.usfca.edu
+# parrt@antlr.org
+
+## End of contents of the ANTLR 2.7.7 LICENSE.txt ########################
+
+## get sys module
+import sys
+
+from .compat import long, basestring, int_types, xrange
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### global symbols ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+### ANTLR Standard Tokens
+SKIP = -1
+INVALID_TYPE = 0
+EOF_TYPE = 1
+EOF = 1
+NULL_TREE_LOOKAHEAD = 3
+MIN_USER_TYPE = 4
+
+### ANTLR's EOF Symbol
+EOF_CHAR = ''
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### general functions ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+## Version should be automatically derived from configure.in. For now,
+## we need to bump it ourselfs. Don't remove the tags.
+##
+def version():
+ r = {
+ 'major' : '2',
+ 'minor' : '7',
+ 'micro' : '5',
+ 'patch' : '' ,
+ 'version': '2.7.5'
+ }
+ return r
+##
+
+def error(fmt,*args):
+ if fmt:
+ print("error: ", fmt % tuple(args))
+
+def ifelse(cond,_then,_else):
+ if cond :
+ r = _then
+ else:
+ r = _else
+ return r
+
+def is_string_type(x):
+ # return (isinstance(x,str) or isinstance(x,unicode))
+ # Simplify; xlwt doesn't support Python < 2.3
+ return isinstance(basestring)
+
+def assert_string_type(x):
+ assert is_string_type(x)
+ pass
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### ANTLR Exceptions ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class ANTLRException(Exception):
+
+ def __init__(self, *args):
+ Exception.__init__(self, *args)
+
+
+class RecognitionException(ANTLRException):
+
+ def __init__(self, *args):
+ ANTLRException.__init__(self, *args)
+ self.fileName = None
+ self.line = -1
+ self.column = -1
+ if len(args) >= 2:
+ self.fileName = args[1]
+ if len(args) >= 3:
+ self.line = args[2]
+ if len(args) >= 4:
+ self.column = args[3]
+
+ def __str__(self):
+ buf = ['']
+ if self.fileName:
+ buf.append(self.fileName + ":")
+ if self.line != -1:
+ if not self.fileName:
+ buf.append("line ")
+ buf.append(str(self.line))
+ if self.column != -1:
+ buf.append(":" + str(self.column))
+ buf.append(":")
+ buf.append(" ")
+ return str('').join(buf)
+
+ __repr__ = __str__
+
+
+class NoViableAltException(RecognitionException):
+
+ def __init__(self, *args):
+ RecognitionException.__init__(self, *args)
+ self.token = None
+ self.node = None
+ if isinstance(args[0],AST):
+ self.node = args[0]
+ elif isinstance(args[0],Token):
+ self.token = args[0]
+ else:
+ raise TypeError("NoViableAltException requires Token or AST argument")
+
+ def __str__(self):
+ if self.token:
+ line = self.token.getLine()
+ col = self.token.getColumn()
+ text = self.token.getText()
+ return "unexpected symbol at line %s (column %s): \"%s\"" % (line,col,text)
+ if self.node == ASTNULL:
+ return "unexpected end of subtree"
+ assert self.node
+ ### hackish, we assume that an AST contains method getText
+ return "unexpected node: %s" % (self.node.getText())
+
+ __repr__ = __str__
+
+
+class NoViableAltForCharException(RecognitionException):
+
+ def __init__(self, *args):
+ self.foundChar = None
+ if len(args) == 2:
+ self.foundChar = args[0]
+ scanner = args[1]
+ RecognitionException.__init__(self, "NoViableAlt",
+ scanner.getFilename(),
+ scanner.getLine(),
+ scanner.getColumn())
+ elif len(args) == 4:
+ self.foundChar = args[0]
+ fileName = args[1]
+ line = args[2]
+ column = args[3]
+ RecognitionException.__init__(self, "NoViableAlt",
+ fileName, line, column)
+ else:
+ RecognitionException.__init__(self, "NoViableAlt",
+ '', -1, -1)
+
+ def __str__(self):
+ mesg = "unexpected char: "
+ if self.foundChar >= ' ' and self.foundChar <= '~':
+ mesg += "'" + self.foundChar + "'"
+ elif self.foundChar:
+ mesg += "0x" + hex(ord(self.foundChar)).upper()[2:]
+ else:
+ mesg += ""
+ return mesg
+
+ __repr__ = __str__
+
+
+class SemanticException(RecognitionException):
+
+ def __init__(self, *args):
+ RecognitionException.__init__(self, *args)
+
+
+class MismatchedCharException(RecognitionException):
+
+ NONE = 0
+ CHAR = 1
+ NOT_CHAR = 2
+ RANGE = 3
+ NOT_RANGE = 4
+ SET = 5
+ NOT_SET = 6
+
+ def __init__(self, *args):
+ self.args = args
+ if len(args) == 5:
+ # Expected range / not range
+ if args[3]:
+ self.mismatchType = MismatchedCharException.NOT_RANGE
+ else:
+ self.mismatchType = MismatchedCharException.RANGE
+ self.foundChar = args[0]
+ self.expecting = args[1]
+ self.upper = args[2]
+ self.scanner = args[4]
+ RecognitionException.__init__(self, "Mismatched char range",
+ self.scanner.getFilename(),
+ self.scanner.getLine(),
+ self.scanner.getColumn())
+ elif len(args) == 4 and is_string_type(args[1]):
+ # Expected char / not char
+ if args[2]:
+ self.mismatchType = MismatchedCharException.NOT_CHAR
+ else:
+ self.mismatchType = MismatchedCharException.CHAR
+ self.foundChar = args[0]
+ self.expecting = args[1]
+ self.scanner = args[3]
+ RecognitionException.__init__(self, "Mismatched char",
+ self.scanner.getFilename(),
+ self.scanner.getLine(),
+ self.scanner.getColumn())
+ elif len(args) == 4 and isinstance(args[1], BitSet):
+ # Expected BitSet / not BitSet
+ if args[2]:
+ self.mismatchType = MismatchedCharException.NOT_SET
+ else:
+ self.mismatchType = MismatchedCharException.SET
+ self.foundChar = args[0]
+ self.set = args[1]
+ self.scanner = args[3]
+ RecognitionException.__init__(self, "Mismatched char set",
+ self.scanner.getFilename(),
+ self.scanner.getLine(),
+ self.scanner.getColumn())
+ else:
+ self.mismatchType = MismatchedCharException.NONE
+ RecognitionException.__init__(self, "Mismatched char")
+
+ ## Append a char to the msg buffer. If special,
+ # then show escaped version
+ #
+ def appendCharName(self, sb, c):
+ if not c or c == 65535:
+ # 65535 = (char) -1 = EOF
+ sb.append("''")
+ elif c == '\n':
+ sb.append("'\\n'")
+ elif c == '\r':
+ sb.append("'\\r'");
+ elif c == '\t':
+ sb.append("'\\t'")
+ else:
+ sb.append('\'' + c + '\'')
+
+ ##
+ # Returns an error message with line number/column information
+ #
+ def __str__(self):
+ sb = ['']
+ sb.append(RecognitionException.__str__(self))
+
+ if self.mismatchType == MismatchedCharException.CHAR:
+ sb.append("expecting ")
+ self.appendCharName(sb, self.expecting)
+ sb.append(", found ")
+ self.appendCharName(sb, self.foundChar)
+ elif self.mismatchType == MismatchedCharException.NOT_CHAR:
+ sb.append("expecting anything but '")
+ self.appendCharName(sb, self.expecting)
+ sb.append("'; got it anyway")
+ elif self.mismatchType in [MismatchedCharException.RANGE, MismatchedCharException.NOT_RANGE]:
+ sb.append("expecting char ")
+ if self.mismatchType == MismatchedCharException.NOT_RANGE:
+ sb.append("NOT ")
+ sb.append("in range: ")
+ self.appendCharName(sb, self.expecting)
+ sb.append("..")
+ self.appendCharName(sb, self.upper)
+ sb.append(", found ")
+ self.appendCharName(sb, self.foundChar)
+ elif self.mismatchType in [MismatchedCharException.SET, MismatchedCharException.NOT_SET]:
+ sb.append("expecting ")
+ if self.mismatchType == MismatchedCharException.NOT_SET:
+ sb.append("NOT ")
+ sb.append("one of (")
+ for i in range(len(self.set)):
+ self.appendCharName(sb, self.set[i])
+ sb.append("), found ")
+ self.appendCharName(sb, self.foundChar)
+
+ return str().join(sb).strip()
+
+ __repr__ = __str__
+
+
+class MismatchedTokenException(RecognitionException):
+
+ NONE = 0
+ TOKEN = 1
+ NOT_TOKEN = 2
+ RANGE = 3
+ NOT_RANGE = 4
+ SET = 5
+ NOT_SET = 6
+
+ def __init__(self, *args):
+ self.args = args
+ self.tokenNames = []
+ self.token = None
+ self.tokenText = ''
+ self.node = None
+ if len(args) == 6:
+ # Expected range / not range
+ if args[3]:
+ self.mismatchType = MismatchedTokenException.NOT_RANGE
+ else:
+ self.mismatchType = MismatchedTokenException.RANGE
+ self.tokenNames = args[0]
+ self.expecting = args[2]
+ self.upper = args[3]
+ self.fileName = args[5]
+
+ elif len(args) == 4 and isinstance(args[2], int):
+ # Expected token / not token
+ if args[3]:
+ self.mismatchType = MismatchedTokenException.NOT_TOKEN
+ else:
+ self.mismatchType = MismatchedTokenException.TOKEN
+ self.tokenNames = args[0]
+ self.expecting = args[2]
+
+ elif len(args) == 4 and isinstance(args[2], BitSet):
+ # Expected BitSet / not BitSet
+ if args[3]:
+ self.mismatchType = MismatchedTokenException.NOT_SET
+ else:
+ self.mismatchType = MismatchedTokenException.SET
+ self.tokenNames = args[0]
+ self.set = args[2]
+
+ else:
+ self.mismatchType = MismatchedTokenException.NONE
+ RecognitionException.__init__(self, "Mismatched Token: expecting any AST node", "", -1, -1)
+
+ if len(args) >= 2:
+ if isinstance(args[1],Token):
+ self.token = args[1]
+ self.tokenText = self.token.getText()
+ RecognitionException.__init__(self, "Mismatched Token",
+ self.fileName,
+ self.token.getLine(),
+ self.token.getColumn())
+ elif isinstance(args[1],AST):
+ self.node = args[1]
+ self.tokenText = str(self.node)
+ RecognitionException.__init__(self, "Mismatched Token",
+ "",
+ self.node.getLine(),
+ self.node.getColumn())
+ else:
+ self.tokenText = ""
+ RecognitionException.__init__(self, "Mismatched Token",
+ "", -1, -1)
+
+ def appendTokenName(self, sb, tokenType):
+ if tokenType == INVALID_TYPE:
+ sb.append("")
+ elif tokenType < 0 or tokenType >= len(self.tokenNames):
+ sb.append("<" + str(tokenType) + ">")
+ else:
+ sb.append(self.tokenNames[tokenType])
+
+ ##
+ # Returns an error message with line number/column information
+ #
+ def __str__(self):
+ sb = ['']
+ sb.append(RecognitionException.__str__(self))
+
+ if self.mismatchType == MismatchedTokenException.TOKEN:
+ sb.append("expecting ")
+ self.appendTokenName(sb, self.expecting)
+ sb.append(", found " + self.tokenText)
+ elif self.mismatchType == MismatchedTokenException.NOT_TOKEN:
+ sb.append("expecting anything but '")
+ self.appendTokenName(sb, self.expecting)
+ sb.append("'; got it anyway")
+ elif self.mismatchType in [MismatchedTokenException.RANGE, MismatchedTokenException.NOT_RANGE]:
+ sb.append("expecting token ")
+ if self.mismatchType == MismatchedTokenException.NOT_RANGE:
+ sb.append("NOT ")
+ sb.append("in range: ")
+ self.appendTokenName(sb, self.expecting)
+ sb.append("..")
+ self.appendTokenName(sb, self.upper)
+ sb.append(", found " + self.tokenText)
+ elif self.mismatchType in [MismatchedTokenException.SET, MismatchedTokenException.NOT_SET]:
+ sb.append("expecting ")
+ if self.mismatchType == MismatchedTokenException.NOT_SET:
+ sb.append("NOT ")
+ sb.append("one of (")
+ for i in range(len(self.set)):
+ self.appendTokenName(sb, self.set[i])
+ sb.append("), found " + self.tokenText)
+
+ return str().join(sb).strip()
+
+ __repr__ = __str__
+
+
+class TokenStreamException(ANTLRException):
+
+ def __init__(self, *args):
+ ANTLRException.__init__(self, *args)
+
+
+# Wraps an Exception in a TokenStreamException
+class TokenStreamIOException(TokenStreamException):
+
+ def __init__(self, *args):
+ if args and isinstance(args[0], Exception):
+ io = args[0]
+ TokenStreamException.__init__(self, str(io))
+ self.io = io
+ else:
+ TokenStreamException.__init__(self, *args)
+ self.io = self
+
+
+# Wraps a RecognitionException in a TokenStreamException
+class TokenStreamRecognitionException(TokenStreamException):
+
+ def __init__(self, *args):
+ if args and isinstance(args[0], RecognitionException):
+ recog = args[0]
+ TokenStreamException.__init__(self, str(recog))
+ self.recog = recog
+ else:
+ raise TypeError("TokenStreamRecognitionException requires RecognitionException argument")
+
+ def __str__(self):
+ return str(self.recog)
+
+ __repr__ = __str__
+
+
+class TokenStreamRetryException(TokenStreamException):
+
+ def __init__(self, *args):
+ TokenStreamException.__init__(self, *args)
+
+
+class CharStreamException(ANTLRException):
+
+ def __init__(self, *args):
+ ANTLRException.__init__(self, *args)
+
+
+# Wraps an Exception in a CharStreamException
+class CharStreamIOException(CharStreamException):
+
+ def __init__(self, *args):
+ if args and isinstance(args[0], Exception):
+ io = args[0]
+ CharStreamException.__init__(self, str(io))
+ self.io = io
+ else:
+ CharStreamException.__init__(self, *args)
+ self.io = self
+
+
+class TryAgain(Exception):
+ pass
+
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### Token ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class Token(object):
+ SKIP = -1
+ INVALID_TYPE = 0
+ EOF_TYPE = 1
+ EOF = 1
+ NULL_TREE_LOOKAHEAD = 3
+ MIN_USER_TYPE = 4
+
+ def __init__(self,**argv):
+ try:
+ self.type = argv['type']
+ except:
+ self.type = INVALID_TYPE
+ try:
+ self.text = argv['text']
+ except:
+ self.text = ""
+
+ def isEOF(self):
+ return (self.type == EOF_TYPE)
+
+ def getColumn(self):
+ return 0
+
+ def getLine(self):
+ return 0
+
+ def getFilename(self):
+ return None
+
+ def setFilename(self,name):
+ return self
+
+ def getText(self):
+ return ""
+
+ def setText(self,text):
+ if is_string_type(text):
+ pass
+ else:
+ raise TypeError("Token.setText requires string argument")
+ return self
+
+ def setColumn(self,column):
+ return self
+
+ def setLine(self,line):
+ return self
+
+ def getType(self):
+ return self.type
+
+ def setType(self,type):
+ if isinstance(type,int):
+ self.type = type
+ else:
+ raise TypeError("Token.setType requires integer argument")
+ return self
+
+ def toString(self):
+ ## not optimal
+ type_ = self.type
+ if type_ == 3:
+ tval = 'NULL_TREE_LOOKAHEAD'
+ elif type_ == 1:
+ tval = 'EOF_TYPE'
+ elif type_ == 0:
+ tval = 'INVALID_TYPE'
+ elif type_ == -1:
+ tval = 'SKIP'
+ else:
+ tval = type_
+ return '["%s",<%s>]' % (self.getText(),tval)
+
+ __str__ = toString
+ __repr__ = toString
+
+### static attribute ..
+Token.badToken = Token( type=INVALID_TYPE, text="")
+
+if __name__ == "__main__":
+ print("testing ..")
+ T = Token.badToken
+ print(T)
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### CommonToken ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class CommonToken(Token):
+
+ def __init__(self,**argv):
+ Token.__init__(self,**argv)
+ self.line = 0
+ self.col = 0
+ try:
+ self.line = argv['line']
+ except:
+ pass
+ try:
+ self.col = argv['col']
+ except:
+ pass
+
+ def getLine(self):
+ return self.line
+
+ def getText(self):
+ return self.text
+
+ def getColumn(self):
+ return self.col
+
+ def setLine(self,line):
+ self.line = line
+ return self
+
+ def setText(self,text):
+ self.text = text
+ return self
+
+ def setColumn(self,col):
+ self.col = col
+ return self
+
+ def toString(self):
+ ## not optimal
+ type_ = self.type
+ if type_ == 3:
+ tval = 'NULL_TREE_LOOKAHEAD'
+ elif type_ == 1:
+ tval = 'EOF_TYPE'
+ elif type_ == 0:
+ tval = 'INVALID_TYPE'
+ elif type_ == -1:
+ tval = 'SKIP'
+ else:
+ tval = type_
+ d = {
+ 'text' : self.text,
+ 'type' : tval,
+ 'line' : self.line,
+ 'colm' : self.col
+ }
+
+ fmt = '["%(text)s",<%(type)s>,line=%(line)s,col=%(colm)s]'
+ return fmt % d
+
+ __str__ = toString
+ __repr__ = toString
+
+
+if __name__ == '__main__' :
+ T = CommonToken()
+ print(T)
+ T = CommonToken(col=15,line=1,text="some text", type=5)
+ print(T)
+ T = CommonToken()
+ T.setLine(1).setColumn(15).setText("some text").setType(5)
+ print(T)
+ print(T.getLine())
+ print(T.getColumn())
+ print(T.getText())
+ print(T.getType())
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### CommonHiddenStreamToken ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class CommonHiddenStreamToken(CommonToken):
+ def __init__(self,*args):
+ CommonToken.__init__(self,*args)
+ self.hiddenBefore = None
+ self.hiddenAfter = None
+
+ def getHiddenAfter(self):
+ return self.hiddenAfter
+
+ def getHiddenBefore(self):
+ return self.hiddenBefore
+
+ def setHiddenAfter(self,t):
+ self.hiddenAfter = t
+
+ def setHiddenBefore(self, t):
+ self.hiddenBefore = t
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### Queue ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+## Shall be a circular buffer on tokens ..
+class Queue(object):
+
+ def __init__(self):
+ self.buffer = [] # empty list
+
+ def append(self,item):
+ self.buffer.append(item)
+
+ def elementAt(self,index):
+ return self.buffer[index]
+
+ def reset(self):
+ self.buffer = []
+
+ def removeFirst(self):
+ self.buffer.pop(0)
+
+ def length(self):
+ return len(self.buffer)
+
+ def __str__(self):
+ return str(self.buffer)
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### InputBuffer ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class InputBuffer(object):
+ def __init__(self):
+ self.nMarkers = 0
+ self.markerOffset = 0
+ self.numToConsume = 0
+ self.queue = Queue()
+
+ def __str__(self):
+ return "(%s,%s,%s,%s)" % (
+ self.nMarkers,
+ self.markerOffset,
+ self.numToConsume,
+ self.queue)
+
+ def __repr__(self):
+ return str(self)
+
+ def commit(self):
+ self.nMarkers -= 1
+
+ def consume(self) :
+ self.numToConsume += 1
+
+ ## probably better to return a list of items
+ ## because of unicode. Or return a unicode
+ ## string ..
+ def getLAChars(self) :
+ i = self.markerOffset
+ n = self.queue.length()
+ s = ''
+ while i 0:
+ if self.nMarkers > 0:
+ # guess mode -- leave leading characters and bump offset.
+ self.markerOffset += 1
+ else:
+ # normal mode -- remove first character
+ self.queue.removeFirst()
+ self.numToConsume -= 1
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### CharBuffer ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class CharBuffer(InputBuffer):
+ def __init__(self,reader):
+ ##assert isinstance(reader,file)
+ super(CharBuffer,self).__init__()
+ ## a reader is supposed to be anything that has
+ ## a method 'read(int)'.
+ self.input = reader
+
+ def __str__(self):
+ base = super(CharBuffer,self).__str__()
+ return "CharBuffer{%s,%s" % (base,str(input))
+
+ def fill(self,amount):
+ try:
+ self.syncConsume()
+ while self.queue.length() < (amount + self.markerOffset) :
+ ## retrieve just one char - what happend at end
+ ## of input?
+ c = self.input.read(1)
+ ### python's behaviour is to return the empty string on
+ ### EOF, ie. no exception whatsoever is thrown. An empty
+ ### python string has the nice feature that it is of
+ ### type 'str' and "not ''" would return true. Contrary,
+ ### one can't do this: '' in 'abc'. This should return
+ ### false, but all we get is then a TypeError as an
+ ### empty string is not a character.
+
+ ### Let's assure then that we have either seen a
+ ### character or an empty string (EOF).
+ assert len(c) == 0 or len(c) == 1
+
+ ### And it shall be of type string (ASCII or UNICODE).
+ assert is_string_type(c)
+
+ ### Just append EOF char to buffer. Note that buffer may
+ ### contain then just more than one EOF char ..
+
+ ### use unicode chars instead of ASCII ..
+ self.queue.append(c)
+ except Exception as e:
+ raise CharStreamIOException(e)
+ ##except: # (mk) Cannot happen ...
+ ##error ("unexpected exception caught ..")
+ ##assert 0
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### LexerSharedInputState ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class LexerSharedInputState(object):
+ def __init__(self,ibuf):
+ assert isinstance(ibuf,InputBuffer)
+ self.input = ibuf
+ self.column = 1
+ self.line = 1
+ self.tokenStartColumn = 1
+ self.tokenStartLine = 1
+ self.guessing = 0
+ self.filename = None
+
+ def reset(self):
+ self.column = 1
+ self.line = 1
+ self.tokenStartColumn = 1
+ self.tokenStartLine = 1
+ self.guessing = 0
+ self.filename = None
+ self.input.reset()
+
+ def LA(self,k):
+ return self.input.LA(k)
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### TokenStream ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class TokenStream(object):
+ def nextToken(self):
+ pass
+
+ def __iter__(self):
+ return TokenStreamIterator(self)
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### TokenStreamIterator ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class TokenStreamIterator(object):
+ def __init__(self,inst):
+ if isinstance(inst,TokenStream):
+ self.inst = inst
+ return
+ raise TypeError("TokenStreamIterator requires TokenStream object")
+
+ def next(self):
+ assert self.inst
+ item = self.inst.nextToken()
+ if not item or item.isEOF():
+ raise StopIteration()
+ return item
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### TokenStreamSelector ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class TokenStreamSelector(TokenStream):
+
+ def __init__(self):
+ self._input = None
+ self._stmap = {}
+ self._stack = []
+
+ def addInputStream(self,stream,key):
+ self._stmap[key] = stream
+
+ def getCurrentStream(self):
+ return self._input
+
+ def getStream(self,sname):
+ try:
+ stream = self._stmap[sname]
+ except:
+ raise ValueError("TokenStream " + sname + " not found");
+ return stream;
+
+ def nextToken(self):
+ while 1:
+ try:
+ return self._input.nextToken()
+ except TokenStreamRetryException:
+ ### just retry "forever"
+ pass
+
+ def pop(self):
+ stream = self._stack.pop();
+ self.select(stream);
+ return stream;
+
+ def push(self,arg):
+ self._stack.append(self._input);
+ self.select(arg)
+
+ def retry(self):
+ raise TokenStreamRetryException()
+
+ def select(self,arg):
+ if isinstance(arg,TokenStream):
+ self._input = arg
+ return
+ if is_string_type(arg):
+ self._input = self.getStream(arg)
+ return
+ raise TypeError("TokenStreamSelector.select requires " +
+ "TokenStream or string argument")
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### TokenStreamBasicFilter ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class TokenStreamBasicFilter(TokenStream):
+
+ def __init__(self,input):
+
+ self.input = input;
+ self.discardMask = BitSet()
+
+ def discard(self,arg):
+ if isinstance(arg,int):
+ self.discardMask.add(arg)
+ return
+ if isinstance(arg,BitSet):
+ self.discardMark = arg
+ return
+ raise TypeError("TokenStreamBasicFilter.discard requires" +
+ "integer or BitSet argument")
+
+ def nextToken(self):
+ tok = self.input.nextToken()
+ while tok and self.discardMask.member(tok.getType()):
+ tok = self.input.nextToken()
+ return tok
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### TokenStreamHiddenTokenFilter ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class TokenStreamHiddenTokenFilter(TokenStreamBasicFilter):
+
+ def __init__(self,input):
+ TokenStreamBasicFilter.__init__(self,input)
+ self.hideMask = BitSet()
+ self.nextMonitoredToken = None
+ self.lastHiddenToken = None
+ self.firstHidden = None
+
+ def consume(self):
+ self.nextMonitoredToken = self.input.nextToken()
+
+ def consumeFirst(self):
+ self.consume()
+
+ p = None;
+ while self.hideMask.member(self.LA(1).getType()) or \
+ self.discardMask.member(self.LA(1).getType()):
+ if self.hideMask.member(self.LA(1).getType()):
+ if not p:
+ p = self.LA(1)
+ else:
+ p.setHiddenAfter(self.LA(1))
+ self.LA(1).setHiddenBefore(p)
+ p = self.LA(1)
+ self.lastHiddenToken = p
+ if not self.firstHidden:
+ self.firstHidden = p
+ self.consume()
+
+ def getDiscardMask(self):
+ return self.discardMask
+
+ def getHiddenAfter(self,t):
+ return t.getHiddenAfter()
+
+ def getHiddenBefore(self,t):
+ return t.getHiddenBefore()
+
+ def getHideMask(self):
+ return self.hideMask
+
+ def getInitialHiddenToken(self):
+ return self.firstHidden
+
+ def hide(self,m):
+ if isinstance(m,int):
+ self.hideMask.add(m)
+ return
+ if isinstance(m.BitMask):
+ self.hideMask = m
+ return
+
+ def LA(self,i):
+ return self.nextMonitoredToken
+
+ def nextToken(self):
+ if not self.LA(1):
+ self.consumeFirst()
+
+ monitored = self.LA(1)
+
+ monitored.setHiddenBefore(self.lastHiddenToken)
+ self.lastHiddenToken = None
+
+ self.consume()
+ p = monitored
+
+ while self.hideMask.member(self.LA(1).getType()) or \
+ self.discardMask.member(self.LA(1).getType()):
+ if self.hideMask.member(self.LA(1).getType()):
+ p.setHiddenAfter(self.LA(1))
+ if p != monitored:
+ self.LA(1).setHiddenBefore(p)
+ p = self.lastHiddenToken = self.LA(1)
+ self.consume()
+ return monitored
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### StringBuffer ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class StringBuffer:
+ def __init__(self,string=None):
+ if string:
+ self.text = list(string)
+ else:
+ self.text = []
+
+ def setLength(self,sz):
+ if not sz :
+ self.text = []
+ return
+ assert sz>0
+ if sz >= self.length():
+ return
+ ### just reset to empty buffer
+ self.text = self.text[0:sz]
+
+ def length(self):
+ return len(self.text)
+
+ def append(self,c):
+ self.text.append(c)
+
+ ### return buffer as string. Arg 'a' is used as index
+ ## into the buffer and 2nd argument shall be the length.
+ ## If 2nd args is absent, we return chars till end of
+ ## buffer starting with 'a'.
+ def getString(self,a=None,length=None):
+ if not a :
+ a = 0
+ assert a>=0
+ if a>= len(self.text) :
+ return ""
+
+ if not length:
+ ## no second argument
+ L = self.text[a:]
+ else:
+ assert (a+length) <= len(self.text)
+ b = a + length
+ L = self.text[a:b]
+ s = ""
+ for x in L : s += x
+ return s
+
+ toString = getString ## alias
+
+ def __str__(self):
+ return str(self.text)
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### Reader ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+## When reading Japanese chars, it happens that a stream returns a
+## 'char' of length 2. This looks like a bug in the appropriate
+## codecs - but I'm rather unsure about this. Anyway, if this is
+## the case, I'm going to split this string into a list of chars
+## and put them on hold, ie. on a buffer. Next time when called
+## we read from buffer until buffer is empty.
+## wh: nov, 25th -> problem does not appear in Python 2.4.0.c1.
+
+class Reader(object):
+ def __init__(self,stream):
+ self.cin = stream
+ self.buf = []
+
+ def read(self,num):
+ assert num==1
+
+ if len(self.buf):
+ return self.buf.pop()
+
+ ## Read a char - this may return a string.
+ ## Is this a bug in codecs/Python?
+ c = self.cin.read(1)
+
+ if not c or len(c)==1:
+ return c
+
+ L = list(c)
+ L.reverse()
+ for x in L:
+ self.buf.append(x)
+
+ ## read one char ..
+ return self.read(1)
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### CharScanner ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class CharScanner(TokenStream):
+ ## class members
+ NO_CHAR = 0
+ EOF_CHAR = '' ### EOF shall be the empty string.
+
+ def __init__(self, *argv, **kwargs):
+ super(CharScanner, self).__init__()
+ self.saveConsumedInput = True
+ self.tokenClass = None
+ self.caseSensitive = True
+ self.caseSensitiveLiterals = True
+ self.literals = None
+ self.tabsize = 8
+ self._returnToken = None
+ self.commitToPath = False
+ self.traceDepth = 0
+ self.text = StringBuffer()
+ self.hashString = hash(self)
+ self.setTokenObjectClass(CommonToken)
+ self.setInput(*argv)
+
+ def __iter__(self):
+ return CharScannerIterator(self)
+
+ def setInput(self,*argv):
+ ## case 1:
+ ## if there's no arg we default to read from
+ ## standard input
+ if not argv:
+ import sys
+ self.setInput(sys.stdin)
+ return
+
+ ## get 1st argument
+ arg1 = argv[0]
+
+ ## case 2:
+ ## if arg1 is a string, we assume it's a file name
+ ## and open a stream using 2nd argument as open
+ ## mode. If there's no 2nd argument we fall back to
+ ## mode '+rb'.
+ if is_string_type(arg1):
+ f = open(arg1,"rb")
+ self.setInput(f)
+ self.setFilename(arg1)
+ return
+
+ ## case 3:
+ ## if arg1 is a file we wrap it by a char buffer (
+ ## some additional checks?? No, can't do this in
+ ## general).
+ if isinstance(arg1,file):
+ self.setInput(CharBuffer(arg1))
+ return
+
+ ## case 4:
+ ## if arg1 is of type SharedLexerInputState we use
+ ## argument as is.
+ if isinstance(arg1,LexerSharedInputState):
+ self.inputState = arg1
+ return
+
+ ## case 5:
+ ## check whether argument type is of type input
+ ## buffer. If so create a SharedLexerInputState and
+ ## go ahead.
+ if isinstance(arg1,InputBuffer):
+ self.setInput(LexerSharedInputState(arg1))
+ return
+
+ ## case 6:
+ ## check whether argument type has a method read(int)
+ ## If so create CharBuffer ...
+ try:
+ if arg1.read:
+ rd = Reader(arg1)
+ cb = CharBuffer(rd)
+ ss = LexerSharedInputState(cb)
+ self.inputState = ss
+ return
+ except:
+ pass
+
+ ## case 7:
+ ## raise wrong argument exception
+ raise TypeError(argv)
+
+ def setTabSize(self,size) :
+ self.tabsize = size
+
+ def getTabSize(self) :
+ return self.tabsize
+
+ def setCaseSensitive(self,t) :
+ self.caseSensitive = t
+
+ def setCommitToPath(self,commit) :
+ self.commitToPath = commit
+
+ def setFilename(self,f) :
+ self.inputState.filename = f
+
+ def setLine(self,line) :
+ self.inputState.line = line
+
+ def setText(self,s) :
+ self.resetText()
+ self.text.append(s)
+
+ def getCaseSensitive(self) :
+ return self.caseSensitive
+
+ def getCaseSensitiveLiterals(self) :
+ return self.caseSensitiveLiterals
+
+ def getColumn(self) :
+ return self.inputState.column
+
+ def setColumn(self,c) :
+ self.inputState.column = c
+
+ def getCommitToPath(self) :
+ return self.commitToPath
+
+ def getFilename(self) :
+ return self.inputState.filename
+
+ def getInputBuffer(self) :
+ return self.inputState.input
+
+ def getInputState(self) :
+ return self.inputState
+
+ def setInputState(self,state) :
+ assert isinstance(state,LexerSharedInputState)
+ self.inputState = state
+
+ def getLine(self) :
+ return self.inputState.line
+
+ def getText(self) :
+ return str(self.text)
+
+ def getTokenObject(self) :
+ return self._returnToken
+
+ def LA(self,i) :
+ c = self.inputState.input.LA(i)
+ if not self.caseSensitive:
+ ### E0006
+ c = c.__class__.lower(c)
+ return c
+
+ def makeToken(self,type) :
+ try:
+ ## dynamically load a class
+ assert self.tokenClass
+ tok = self.tokenClass()
+ tok.setType(type)
+ tok.setColumn(self.inputState.tokenStartColumn)
+ tok.setLine(self.inputState.tokenStartLine)
+ return tok
+ except:
+ self.panic("unable to create new token")
+ return Token.badToken
+
+ def mark(self) :
+ return self.inputState.input.mark()
+
+ def _match_bitset(self,b) :
+ if b.member(self.LA(1)):
+ self.consume()
+ else:
+ raise MismatchedCharException(self.LA(1), b, False, self)
+
+ def _match_string(self,s) :
+ for c in s:
+ if self.LA(1) == c:
+ self.consume()
+ else:
+ raise MismatchedCharException(self.LA(1), c, False, self)
+
+ def match(self,item):
+ if is_string_type(item):
+ return self._match_string(item)
+ else:
+ return self._match_bitset(item)
+
+ def matchNot(self,c) :
+ if self.LA(1) != c:
+ self.consume()
+ else:
+ raise MismatchedCharException(self.LA(1), c, True, self)
+
+ def matchRange(self,c1,c2) :
+ if self.LA(1) < c1 or self.LA(1) > c2 :
+ raise MismatchedCharException(self.LA(1), c1, c2, False, self)
+ else:
+ self.consume()
+
+ def newline(self) :
+ self.inputState.line += 1
+ self.inputState.column = 1
+
+ def tab(self) :
+ c = self.getColumn()
+ nc = ( ((c-1)/self.tabsize) + 1) * self.tabsize + 1
+ self.setColumn(nc)
+
+ def panic(self,s='') :
+ print("CharScanner: panic: " + s)
+ sys.exit(1)
+
+ def reportError(self,s) :
+ if not self.getFilename():
+ print("error: " + str(s))
+ else:
+ print(self.getFilename() + ": error: " + str(s))
+
+ def reportWarning(self,s) :
+ if not self.getFilename():
+ print("warning: " + str(s))
+ else:
+ print(self.getFilename() + ": warning: " + str(s))
+
+ def resetText(self) :
+ self.text.setLength(0)
+ self.inputState.tokenStartColumn = self.inputState.column
+ self.inputState.tokenStartLine = self.inputState.line
+
+ def rewind(self,pos) :
+ self.inputState.input.rewind(pos)
+
+ def setTokenObjectClass(self,cl):
+ self.tokenClass = cl
+
+ def testForLiteral(self,token):
+ if not token:
+ return
+ assert isinstance(token,Token)
+
+ _type = token.getType()
+
+ ## special tokens can't be literals
+ if _type in [SKIP,INVALID_TYPE,EOF_TYPE,NULL_TREE_LOOKAHEAD] :
+ return
+
+ _text = token.getText()
+ if not _text:
+ return
+
+ assert is_string_type(_text)
+ _type = self.testLiteralsTable(_text,_type)
+ token.setType(_type)
+ return _type
+
+ def testLiteralsTable(self,*args):
+ if is_string_type(args[0]):
+ s = args[0]
+ i = args[1]
+ else:
+ s = self.text.getString()
+ i = args[0]
+
+ ## check whether integer has been given
+ if not isinstance(i,int):
+ assert isinstance(i,int)
+
+ ## check whether we have a dict
+ assert isinstance(self.literals,dict)
+ try:
+ ## E0010
+ if not self.caseSensitiveLiterals:
+ s = s.__class__.lower(s)
+ i = self.literals[s]
+ except:
+ pass
+ return i
+
+ def toLower(self,c):
+ return c.__class__.lower()
+
+ def traceIndent(self):
+ print(' ' * self.traceDepth)
+
+ def traceIn(self,rname):
+ self.traceDepth += 1
+ self.traceIndent()
+ print("> lexer %s c== %s" % (rname,self.LA(1)))
+
+ def traceOut(self,rname):
+ self.traceIndent()
+ print("< lexer %s c== %s" % (rname,self.LA(1)))
+ self.traceDepth -= 1
+
+ def uponEOF(self):
+ pass
+
+ def append(self,c):
+ if self.saveConsumedInput :
+ self.text.append(c)
+
+ def commit(self):
+ self.inputState.input.commit()
+
+ def consume(self):
+ if not self.inputState.guessing:
+ c = self.LA(1)
+ if self.caseSensitive:
+ self.append(c)
+ else:
+ # use input.LA(), not LA(), to get original case
+ # CharScanner.LA() would toLower it.
+ c = self.inputState.input.LA(1)
+ self.append(c)
+
+ if c and c in "\t":
+ self.tab()
+ else:
+ self.inputState.column += 1
+ self.inputState.input.consume()
+
+ ## Consume chars until one matches the given char
+ def consumeUntil_char(self,c):
+ while self.LA(1) != EOF_CHAR and self.LA(1) != c:
+ self.consume()
+
+ ## Consume chars until one matches the given set
+ def consumeUntil_bitset(self,bitset):
+ while self.LA(1) != EOF_CHAR and not self.set.member(self.LA(1)):
+ self.consume()
+
+ ### If symbol seen is EOF then generate and set token, otherwise
+ ### throw exception.
+ def default(self,la1):
+ if not la1 :
+ self.uponEOF()
+ self._returnToken = self.makeToken(EOF_TYPE)
+ else:
+ self.raise_NoViableAlt(la1)
+
+ def filterdefault(self,la1,*args):
+ if not la1:
+ self.uponEOF()
+ self._returnToken = self.makeToken(EOF_TYPE)
+ return
+
+ if not args:
+ self.consume()
+ raise TryAgain()
+ else:
+ ### apply filter object
+ self.commit();
+ try:
+ func=args[0]
+ func(*args[1:])
+ except RecognitionException as e:
+ ## catastrophic failure
+ self.reportError(e);
+ self.consume();
+ raise TryAgain()
+
+ def raise_NoViableAlt(self,la1=None):
+ if not la1: la1 = self.LA(1)
+ fname = self.getFilename()
+ line = self.getLine()
+ col = self.getColumn()
+ raise NoViableAltForCharException(la1,fname,line,col)
+
+ def set_return_token(self,_create,_token,_ttype,_offset):
+ if _create and not _token and (not _ttype == SKIP):
+ string = self.text.getString(_offset)
+ _token = self.makeToken(_ttype)
+ _token.setText(string)
+ self._returnToken = _token
+ return _token
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### CharScannerIterator ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class CharScannerIterator:
+
+ def __init__(self,inst):
+ if isinstance(inst,CharScanner):
+ self.inst = inst
+ return
+ raise TypeError("CharScannerIterator requires CharScanner object")
+
+ def next(self):
+ assert self.inst
+ item = self.inst.nextToken()
+ if not item or item.isEOF():
+ raise StopIteration()
+ return item
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### BitSet ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+### I'm assuming here that a long is 64bits. It appears however, that
+### a long is of any size. That means we can use a single long as the
+### bitset (!), ie. Python would do almost all the work (TBD).
+
+class BitSet(object):
+ BITS = 64
+ NIBBLE = 4
+ LOG_BITS = 6
+ MOD_MASK = BITS -1
+
+ def __init__(self,data=None):
+ if not data:
+ BitSet.__init__(self,[long(0)])
+ return
+ if isinstance(data,int):
+ BitSet.__init__(self,[long(data)])
+ return
+ if isinstance(data,long):
+ BitSet.__init__(self,[data])
+ return
+ if not isinstance(data,list):
+ raise TypeError("BitSet requires integer, long, or " +
+ "list argument")
+ for x in data:
+ if not isinstance(x, int_types):
+ raise TypeError(self,"List argument item is " +
+ "not a long: %s" % (x))
+ self.data = data
+
+ def __str__(self):
+ bits = len(self.data) * BitSet.BITS
+ s = ""
+ for i in xrange(0,bits):
+ if self.at(i):
+ s += "1"
+ else:
+ s += "o"
+ if not ((i+1) % 10):
+ s += '|%s|' % (i+1)
+ return s
+
+ def __repr__(self):
+ return str(self)
+
+ def member(self,item):
+ if not item:
+ return False
+
+ if isinstance(item,int):
+ return self.at(item)
+
+ if not is_string_type(item):
+ raise TypeError(self,"char or unichar expected: %s" % (item))
+
+ ## char is a (unicode) string with at most lenght 1, ie.
+ ## a char.
+
+ if len(item) != 1:
+ raise TypeError(self,"char expected: %s" % (item))
+
+ ### handle ASCII/UNICODE char
+ num = ord(item)
+
+ ### check whether position num is in bitset
+ return self.at(num)
+
+ def wordNumber(self,bit):
+ return bit >> BitSet.LOG_BITS
+
+ def bitMask(self,bit):
+ pos = bit & BitSet.MOD_MASK ## bit mod BITS
+ return (1 << pos)
+
+ def set(self,bit,on=True):
+ # grow bitset as required (use with care!)
+ i = self.wordNumber(bit)
+ mask = self.bitMask(bit)
+ if i>=len(self.data):
+ d = i - len(self.data) + 1
+ for x in xrange(0,d):
+ self.data.append(0)
+ assert len(self.data) == i+1
+ if on:
+ self.data[i] |= mask
+ else:
+ self.data[i] &= (~mask)
+
+ ### make add an alias for set
+ add = set
+
+ def off(self,bit,off=True):
+ self.set(bit,not off)
+
+ def at(self,bit):
+ i = self.wordNumber(bit)
+ v = self.data[i]
+ m = self.bitMask(bit)
+ return v & m
+
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### some further funcs ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+def illegalarg_ex(func):
+ raise ValueError(
+ "%s is only valid if parser is built for debugging" %
+ (func.func_name))
+
+def runtime_ex(func):
+ raise RuntimeError(
+ "%s is only valid if parser is built for debugging" %
+ (func.func_name))
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### TokenBuffer ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class TokenBuffer(object):
+ def __init__(self,stream):
+ self.input = stream
+ self.nMarkers = 0
+ self.markerOffset = 0
+ self.numToConsume = 0
+ self.queue = Queue()
+
+ def reset(self) :
+ self.nMarkers = 0
+ self.markerOffset = 0
+ self.numToConsume = 0
+ self.queue.reset()
+
+ def consume(self) :
+ self.numToConsume += 1
+
+ def fill(self, amount):
+ self.syncConsume()
+ while self.queue.length() < (amount + self.markerOffset):
+ self.queue.append(self.input.nextToken())
+
+ def getInput(self):
+ return self.input
+
+ def LA(self,k) :
+ self.fill(k)
+ return self.queue.elementAt(self.markerOffset + k - 1).type
+
+ def LT(self,k) :
+ self.fill(k)
+ return self.queue.elementAt(self.markerOffset + k - 1)
+
+ def mark(self) :
+ self.syncConsume()
+ self.nMarkers += 1
+ return self.markerOffset
+
+ def rewind(self,mark) :
+ self.syncConsume()
+ self.markerOffset = mark
+ self.nMarkers -= 1
+
+ def syncConsume(self) :
+ while self.numToConsume > 0:
+ if self.nMarkers > 0:
+ # guess mode -- leave leading characters and bump offset.
+ self.markerOffset += 1
+ else:
+ # normal mode -- remove first character
+ self.queue.removeFirst()
+ self.numToConsume -= 1
+
+ def __str__(self):
+ return "(%s,%s,%s,%s,%s)" % (
+ self.input,
+ self.nMarkers,
+ self.markerOffset,
+ self.numToConsume,
+ self.queue)
+
+ def __repr__(self):
+ return str(self)
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### ParserSharedInputState ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class ParserSharedInputState(object):
+
+ def __init__(self):
+ self.input = None
+ self.reset()
+
+ def reset(self):
+ self.guessing = 0
+ self.filename = None
+ if self.input:
+ self.input.reset()
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### Parser ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class Parser(object):
+
+ def __init__(self, *args, **kwargs):
+ self.tokenNames = None
+ self.returnAST = None
+ self.astFactory = None
+ self.tokenTypeToASTClassMap = {}
+ self.ignoreInvalidDebugCalls = False
+ self.traceDepth = 0
+ if not args:
+ self.inputState = ParserSharedInputState()
+ return
+ arg0 = args[0]
+ assert isinstance(arg0,ParserSharedInputState)
+ self.inputState = arg0
+ return
+
+ def getTokenTypeToASTClassMap(self):
+ return self.tokenTypeToASTClassMap
+
+
+ def addMessageListener(self, l):
+ if not self.ignoreInvalidDebugCalls:
+ illegalarg_ex(self.addMessageListener)
+
+ def addParserListener(self,l) :
+ if (not self.ignoreInvalidDebugCalls) :
+ illegalarg_ex(self.addParserListener)
+
+ def addParserMatchListener(self, l) :
+ if (not self.ignoreInvalidDebugCalls) :
+ illegalarg_ex(self.addParserMatchListener)
+
+ def addParserTokenListener(self, l) :
+ if (not self.ignoreInvalidDebugCalls):
+ illegalarg_ex(self.addParserTokenListener)
+
+ def addSemanticPredicateListener(self, l) :
+ if (not self.ignoreInvalidDebugCalls):
+ illegalarg_ex(self.addSemanticPredicateListener)
+
+ def addSyntacticPredicateListener(self, l) :
+ if (not self.ignoreInvalidDebugCalls):
+ illegalarg_ex(self.addSyntacticPredicateListener)
+
+ def addTraceListener(self, l) :
+ if (not self.ignoreInvalidDebugCalls):
+ illegalarg_ex(self.addTraceListener)
+
+ def consume(self):
+ raise NotImplementedError()
+
+ def _consumeUntil_type(self,tokenType):
+ while self.LA(1) != EOF_TYPE and self.LA(1) != tokenType:
+ self.consume()
+
+ def _consumeUntil_bitset(self, set):
+ while self.LA(1) != EOF_TYPE and not set.member(self.LA(1)):
+ self.consume()
+
+ def consumeUntil(self,arg):
+ if isinstance(arg,int):
+ self._consumeUntil_type(arg)
+ else:
+ self._consumeUntil_bitset(arg)
+
+ def defaultDebuggingSetup(self):
+ pass
+
+ def getAST(self) :
+ return self.returnAST
+
+ def getASTFactory(self) :
+ return self.astFactory
+
+ def getFilename(self) :
+ return self.inputState.filename
+
+ def getInputState(self) :
+ return self.inputState
+
+ def setInputState(self, state) :
+ self.inputState = state
+
+ def getTokenName(self,num) :
+ return self.tokenNames[num]
+
+ def getTokenNames(self) :
+ return self.tokenNames
+
+ def isDebugMode(self) :
+ return self.false
+
+ def LA(self, i):
+ raise NotImplementedError()
+
+ def LT(self, i):
+ raise NotImplementedError()
+
+ def mark(self):
+ return self.inputState.input.mark()
+
+ def _match_int(self,t):
+ if (self.LA(1) != t):
+ raise MismatchedTokenException(
+ self.tokenNames, self.LT(1), t, False, self.getFilename())
+ else:
+ self.consume()
+
+ def _match_set(self, b):
+ if (not b.member(self.LA(1))):
+ raise MismatchedTokenException(
+ self.tokenNames,self.LT(1), b, False, self.getFilename())
+ else:
+ self.consume()
+
+ def match(self,set) :
+ if isinstance(set,int):
+ self._match_int(set)
+ return
+ if isinstance(set,BitSet):
+ self._match_set(set)
+ return
+ raise TypeError("Parser.match requires integer ot BitSet argument")
+
+ def matchNot(self,t):
+ if self.LA(1) == t:
+ raise MismatchedTokenException(
+ self.tokenNames, self.LT(1), t, True, self.getFilename())
+ else:
+ self.consume()
+
+ def removeMessageListener(self, l) :
+ if (not self.ignoreInvalidDebugCalls):
+ runtime_ex(self.removeMessageListener)
+
+ def removeParserListener(self, l) :
+ if (not self.ignoreInvalidDebugCalls):
+ runtime_ex(self.removeParserListener)
+
+ def removeParserMatchListener(self, l) :
+ if (not self.ignoreInvalidDebugCalls):
+ runtime_ex(self.removeParserMatchListener)
+
+ def removeParserTokenListener(self, l) :
+ if (not self.ignoreInvalidDebugCalls):
+ runtime_ex(self.removeParserTokenListener)
+
+ def removeSemanticPredicateListener(self, l) :
+ if (not self.ignoreInvalidDebugCalls):
+ runtime_ex(self.removeSemanticPredicateListener)
+
+ def removeSyntacticPredicateListener(self, l) :
+ if (not self.ignoreInvalidDebugCalls):
+ runtime_ex(self.removeSyntacticPredicateListener)
+
+ def removeTraceListener(self, l) :
+ if (not self.ignoreInvalidDebugCalls):
+ runtime_ex(self.removeTraceListener)
+
+ def reportError(self,x) :
+ fmt = "syntax error:"
+ f = self.getFilename()
+ if f:
+ fmt = ("%s:" % f) + fmt
+ if isinstance(x,Token):
+ line = x.getColumn()
+ col = x.getLine()
+ text = x.getText()
+ fmt = fmt + 'unexpected symbol at line %s (column %s) : "%s"'
+ print(fmt % (line,col,text), file=sys.stderr)
+ else:
+ print(fmt,str(x), file=sys.stderr)
+
+ def reportWarning(self,s):
+ f = self.getFilename()
+ if f:
+ print("%s:warning: %s" % (f,str(s)))
+ else:
+ print("warning: %s" % (str(s)))
+
+ def rewind(self, pos) :
+ self.inputState.input.rewind(pos)
+
+ def setASTFactory(self, f) :
+ self.astFactory = f
+
+ def setASTNodeClass(self, cl) :
+ self.astFactory.setASTNodeType(cl)
+
+ def setASTNodeType(self, nodeType) :
+ self.setASTNodeClass(nodeType)
+
+ def setDebugMode(self, debugMode) :
+ if (not self.ignoreInvalidDebugCalls):
+ runtime_ex(self.setDebugMode)
+
+ def setFilename(self, f) :
+ self.inputState.filename = f
+
+ def setIgnoreInvalidDebugCalls(self, value) :
+ self.ignoreInvalidDebugCalls = value
+
+ def setTokenBuffer(self, t) :
+ self.inputState.input = t
+
+ def traceIndent(self):
+ print(" " * self.traceDepth)
+
+ def traceIn(self,rname):
+ self.traceDepth += 1
+ self.trace("> ", rname)
+
+ def traceOut(self,rname):
+ self.trace("< ", rname)
+ self.traceDepth -= 1
+
+ ### wh: moved from ASTFactory to Parser
+ def addASTChild(self,currentAST, child):
+ if not child:
+ return
+ if not currentAST.root:
+ currentAST.root = child
+ elif not currentAST.child:
+ currentAST.root.setFirstChild(child)
+ else:
+ currentAST.child.setNextSibling(child)
+ currentAST.child = child
+ currentAST.advanceChildToEnd()
+
+ ### wh: moved from ASTFactory to Parser
+ def makeASTRoot(self,currentAST,root) :
+ if root:
+ ### Add the current root as a child of new root
+ root.addChild(currentAST.root)
+ ### The new current child is the last sibling of the old root
+ currentAST.child = currentAST.root
+ currentAST.advanceChildToEnd()
+ ### Set the new root
+ currentAST.root = root
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### LLkParser ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class LLkParser(Parser):
+
+ def __init__(self, *args, **kwargs):
+ try:
+ arg1 = args[0]
+ except:
+ arg1 = 1
+
+ if isinstance(arg1,int):
+ super(LLkParser,self).__init__()
+ self.k = arg1
+ return
+
+ if isinstance(arg1,ParserSharedInputState):
+ super(LLkParser,self).__init__(arg1)
+ self.set_k(1,*args)
+ return
+
+ if isinstance(arg1,TokenBuffer):
+ super(LLkParser,self).__init__()
+ self.setTokenBuffer(arg1)
+ self.set_k(1,*args)
+ return
+
+ if isinstance(arg1,TokenStream):
+ super(LLkParser,self).__init__()
+ tokenBuf = TokenBuffer(arg1)
+ self.setTokenBuffer(tokenBuf)
+ self.set_k(1,*args)
+ return
+
+ ### unknown argument
+ raise TypeError("LLkParser requires integer, " +
+ "ParserSharedInputStream or TokenStream argument")
+
+ def consume(self):
+ self.inputState.input.consume()
+
+ def LA(self,i):
+ return self.inputState.input.LA(i)
+
+ def LT(self,i):
+ return self.inputState.input.LT(i)
+
+ def set_k(self,index,*args):
+ try:
+ self.k = args[index]
+ except:
+ self.k = 1
+
+ def trace(self,ee,rname):
+ print(type(self))
+ self.traceIndent()
+ guess = ""
+ if self.inputState.guessing > 0:
+ guess = " [guessing]"
+ print((ee + rname + guess))
+ for i in xrange(1,self.k+1):
+ if i != 1:
+ print(", ")
+ if self.LT(i) :
+ v = self.LT(i).getText()
+ else:
+ v = "null"
+ print("LA(%s) == %s" % (i,v))
+ print("\n")
+
+ def traceIn(self,rname):
+ self.traceDepth += 1;
+ self.trace("> ", rname);
+
+ def traceOut(self,rname):
+ self.trace("< ", rname);
+ self.traceDepth -= 1;
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### TreeParserSharedInputState ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class TreeParserSharedInputState(object):
+ def __init__(self):
+ self.guessing = 0
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### TreeParser ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class TreeParser(object):
+
+ def __init__(self, *args, **kwargs):
+ self.inputState = TreeParserSharedInputState()
+ self._retTree = None
+ self.tokenNames = []
+ self.returnAST = None
+ self.astFactory = ASTFactory()
+ self.traceDepth = 0
+
+ def getAST(self):
+ return self.returnAST
+
+ def getASTFactory(self):
+ return self.astFactory
+
+ def getTokenName(self,num) :
+ return self.tokenNames[num]
+
+ def getTokenNames(self):
+ return self.tokenNames
+
+ def match(self,t,set) :
+ assert isinstance(set,int) or isinstance(set,BitSet)
+ if not t or t == ASTNULL:
+ raise MismatchedTokenException(self.getTokenNames(), t,set, False)
+
+ if isinstance(set,int) and t.getType() != set:
+ raise MismatchedTokenException(self.getTokenNames(), t,set, False)
+
+ if isinstance(set,BitSet) and not set.member(t.getType):
+ raise MismatchedTokenException(self.getTokenNames(), t,set, False)
+
+ def matchNot(self,t, ttype) :
+ if not t or (t == ASTNULL) or (t.getType() == ttype):
+ raise MismatchedTokenException(self.getTokenNames(), t, ttype, True)
+
+ def reportError(self,ex):
+ print("error:",ex, file=sys.stderr)
+
+ def reportWarning(self, s):
+ print("warning:",s)
+
+ def setASTFactory(self,f):
+ self.astFactory = f
+
+ def setASTNodeType(self,nodeType):
+ self.setASTNodeClass(nodeType)
+
+ def setASTNodeClass(self,nodeType):
+ self.astFactory.setASTNodeType(nodeType)
+
+ def traceIndent(self):
+ print(" " * self.traceDepth)
+
+ def traceIn(self,rname,t):
+ self.traceDepth += 1
+ self.traceIndent()
+ print(("> " + rname + "(" +
+ ifelse(t,str(t),"null") + ")" +
+ ifelse(self.inputState.guessing>0,"[guessing]","")))
+
+ def traceOut(self,rname,t):
+ self.traceIndent()
+ print(("< " + rname + "(" +
+ ifelse(t,str(t),"null") + ")" +
+ ifelse(self.inputState.guessing>0,"[guessing]","")))
+ self.traceDepth -= 1
+
+ ### wh: moved from ASTFactory to TreeParser
+ def addASTChild(self,currentAST, child):
+ if not child:
+ return
+ if not currentAST.root:
+ currentAST.root = child
+ elif not currentAST.child:
+ currentAST.root.setFirstChild(child)
+ else:
+ currentAST.child.setNextSibling(child)
+ currentAST.child = child
+ currentAST.advanceChildToEnd()
+
+ ### wh: moved from ASTFactory to TreeParser
+ def makeASTRoot(self,currentAST,root):
+ if root:
+ ### Add the current root as a child of new root
+ root.addChild(currentAST.root)
+ ### The new current child is the last sibling of the old root
+ currentAST.child = currentAST.root
+ currentAST.advanceChildToEnd()
+ ### Set the new root
+ currentAST.root = root
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### funcs to work on trees ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+def rightmost(ast):
+ if ast:
+ while(ast.right):
+ ast = ast.right
+ return ast
+
+def cmptree(s,t,partial):
+ while(s and t):
+ ### as a quick optimization, check roots first.
+ if not s.equals(t):
+ return False
+
+ ### if roots match, do full list match test on children.
+ if not cmptree(s.getFirstChild(),t.getFirstChild(),partial):
+ return False
+
+ s = s.getNextSibling()
+ t = t.getNextSibling()
+
+ r = ifelse(partial,not t,not s and not t)
+ return r
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### AST ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class AST(object):
+ def __init__(self):
+ pass
+
+ def addChild(self, c):
+ pass
+
+ def equals(self, t):
+ return False
+
+ def equalsList(self, t):
+ return False
+
+ def equalsListPartial(self, t):
+ return False
+
+ def equalsTree(self, t):
+ return False
+
+ def equalsTreePartial(self, t):
+ return False
+
+ def findAll(self, tree):
+ return None
+
+ def findAllPartial(self, subtree):
+ return None
+
+ def getFirstChild(self):
+ return self
+
+ def getNextSibling(self):
+ return self
+
+ def getText(self):
+ return ""
+
+ def getType(self):
+ return INVALID_TYPE
+
+ def getLine(self):
+ return 0
+
+ def getColumn(self):
+ return 0
+
+ def getNumberOfChildren(self):
+ return 0
+
+ def initialize(self, t):
+ pass
+
+ def setFirstChild(self, c):
+ pass
+
+ def setNextSibling(self, n):
+ pass
+
+ def setText(self, text):
+ pass
+
+ def setType(self, ttype):
+ pass
+
+ def toString(self):
+ self.getText()
+
+ __str__ = toString
+
+ def toStringList(self):
+ return self.getText()
+
+ def toStringTree(self):
+ return self.getText()
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### ASTNULLType ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+### There is only one instance of this class **/
+class ASTNULLType(AST):
+ def __init__(self):
+ AST.__init__(self)
+ pass
+
+ def getText(self):
+ return ""
+
+ def getType(self):
+ return NULL_TREE_LOOKAHEAD
+
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### BaseAST ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class BaseAST(AST):
+
+ verboseStringConversion = False
+ tokenNames = None
+
+ def __init__(self):
+ self.down = None ## kid
+ self.right = None ## sibling
+
+ def addChild(self,node):
+ if node:
+ t = rightmost(self.down)
+ if t:
+ t.right = node
+ else:
+ assert not self.down
+ self.down = node
+
+ def getNumberOfChildren(self):
+ t = self.down
+ n = 0
+ while t:
+ n += 1
+ t = t.right
+ return n
+
+ def doWorkForFindAll(self,v,target,partialMatch):
+ sibling = self
+
+ while sibling:
+ c1 = partialMatch and sibling.equalsTreePartial(target)
+ if c1:
+ v.append(sibling)
+ else:
+ c2 = not partialMatch and sibling.equalsTree(target)
+ if c2:
+ v.append(sibling)
+
+ ### regardless of match or not, check any children for matches
+ if sibling.getFirstChild():
+ sibling.getFirstChild().doWorkForFindAll(v,target,partialMatch)
+
+ sibling = sibling.getNextSibling()
+
+ ### Is node t equal to 'self' in terms of token type and text?
+ def equals(self,t):
+ if not t:
+ return False
+ return self.getText() == t.getText() and self.getType() == t.getType()
+
+ ### Is t an exact structural and equals() match of this tree. The
+ ### 'self' reference is considered the start of a sibling list.
+ ###
+ def equalsList(self, t):
+ return cmptree(self, t, partial=False)
+
+ ### Is 't' a subtree of this list?
+ ### The siblings of the root are NOT ignored.
+ ###
+ def equalsListPartial(self,t):
+ return cmptree(self,t,partial=True)
+
+ ### Is tree rooted at 'self' equal to 't'? The siblings
+ ### of 'self' are ignored.
+ ###
+ def equalsTree(self, t):
+ return self.equals(t) and \
+ cmptree(self.getFirstChild(), t.getFirstChild(), partial=False)
+
+ ### Is 't' a subtree of the tree rooted at 'self'? The siblings
+ ### of 'self' are ignored.
+ ###
+ def equalsTreePartial(self, t):
+ if not t:
+ return True
+ return self.equals(t) and cmptree(
+ self.getFirstChild(), t.getFirstChild(), partial=True)
+
+ ### Walk the tree looking for all exact subtree matches. Return
+ ### an ASTEnumerator that lets the caller walk the list
+ ### of subtree roots found herein.
+ def findAll(self,target):
+ roots = []
+
+ ### the empty tree cannot result in an enumeration
+ if not target:
+ return None
+ # find all matches recursively
+ self.doWorkForFindAll(roots, target, False)
+ return roots
+
+ ### Walk the tree looking for all subtrees. Return
+ ### an ASTEnumerator that lets the caller walk the list
+ ### of subtree roots found herein.
+ def findAllPartial(self,sub):
+ roots = []
+
+ ### the empty tree cannot result in an enumeration
+ if not sub:
+ return None
+
+ self.doWorkForFindAll(roots, sub, True) ### find all matches recursively
+ return roots
+
+ ### Get the first child of this node None if not children
+ def getFirstChild(self):
+ return self.down
+
+ ### Get the next sibling in line after this one
+ def getNextSibling(self):
+ return self.right
+
+ ### Get the token text for this node
+ def getText(self):
+ return ""
+
+ ### Get the token type for this node
+ def getType(self):
+ return 0
+
+ def getLine(self):
+ return 0
+
+ def getColumn(self):
+ return 0
+
+ ### Remove all children */
+ def removeChildren(self):
+ self.down = None
+
+ def setFirstChild(self,c):
+ self.down = c
+
+ def setNextSibling(self, n):
+ self.right = n
+
+ ### Set the token text for this node
+ def setText(self, text):
+ pass
+
+ ### Set the token type for this node
+ def setType(self, ttype):
+ pass
+
+ ### static
+ def setVerboseStringConversion(verbose,names):
+ verboseStringConversion = verbose
+ tokenNames = names
+ setVerboseStringConversion = staticmethod(setVerboseStringConversion)
+
+ ### Return an array of strings that maps token ID to it's text.
+ ## @since 2.7.3
+ def getTokenNames():
+ return tokenNames
+
+ def toString(self):
+ return self.getText()
+
+ ### return tree as lisp string - sibling included
+ def toStringList(self):
+ ts = self.toStringTree()
+ sib = self.getNextSibling()
+ if sib:
+ ts += sib.toStringList()
+ return ts
+
+ __str__ = toStringList
+
+ ### return tree as string - siblings ignored
+ def toStringTree(self):
+ ts = ""
+ kid = self.getFirstChild()
+ if kid:
+ ts += " ("
+ ts += " " + self.toString()
+ if kid:
+ ts += kid.toStringList()
+ ts += " )"
+ return ts
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### CommonAST ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+### Common AST node implementation
+class CommonAST(BaseAST):
+ def __init__(self,token=None):
+ super(CommonAST,self).__init__()
+ self.ttype = INVALID_TYPE
+ self.text = ""
+ self.line = 0
+ self.column= 0
+ self.initialize(token)
+ #assert self.text
+
+ ### Get the token text for this node
+ def getText(self):
+ return self.text
+
+ ### Get the token type for this node
+ def getType(self):
+ return self.ttype
+
+ ### Get the line for this node
+ def getLine(self):
+ return self.line
+
+ ### Get the column for this node
+ def getColumn(self):
+ return self.column
+
+ def initialize(self,*args):
+ if not args:
+ return
+
+ arg0 = args[0]
+
+ if isinstance(arg0,int):
+ arg1 = args[1]
+ self.setType(arg0)
+ self.setText(arg1)
+ return
+
+ if isinstance(arg0,AST) or isinstance(arg0,Token):
+ self.setText(arg0.getText())
+ self.setType(arg0.getType())
+ self.line = arg0.getLine()
+ self.column = arg0.getColumn()
+ return
+
+ ### Set the token text for this node
+ def setText(self,text_):
+ assert is_string_type(text_)
+ self.text = text_
+
+ ### Set the token type for this node
+ def setType(self,ttype_):
+ assert isinstance(ttype_,int)
+ self.ttype = ttype_
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### CommonASTWithHiddenTokens ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class CommonASTWithHiddenTokens(CommonAST):
+
+ def __init__(self,*args):
+ CommonAST.__init__(self,*args)
+ self.hiddenBefore = None
+ self.hiddenAfter = None
+
+ def getHiddenAfter(self):
+ return self.hiddenAfter
+
+ def getHiddenBefore(self):
+ return self.hiddenBefore
+
+ def initialize(self,*args):
+ CommonAST.initialize(self,*args)
+ if args and isinstance(args[0],Token):
+ assert isinstance(args[0],CommonHiddenStreamToken)
+ self.hiddenBefore = args[0].getHiddenBefore()
+ self.hiddenAfter = args[0].getHiddenAfter()
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### ASTPair ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class ASTPair(object):
+ def __init__(self):
+ self.root = None ### current root of tree
+ self.child = None ### current child to which siblings are added
+
+ ### Make sure that child is the last sibling */
+ def advanceChildToEnd(self):
+ if self.child:
+ while self.child.getNextSibling():
+ self.child = self.child.getNextSibling()
+
+ ### Copy an ASTPair. Don't call it clone() because we want type-safety */
+ def copy(self):
+ tmp = ASTPair()
+ tmp.root = self.root
+ tmp.child = self.child
+ return tmp
+
+ def toString(self):
+ r = ifelse(not root,"null",self.root.getText())
+ c = ifelse(not child,"null",self.child.getText())
+ return "[%s,%s]" % (r,c)
+
+ __str__ = toString
+ __repr__ = toString
+
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### ASTFactory ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class ASTFactory(object):
+ def __init__(self,table=None):
+ self._class = None
+ self._classmap = ifelse(table,table,None)
+
+ def create(self,*args):
+ if not args:
+ return self.create(INVALID_TYPE)
+
+ arg0 = args[0]
+ arg1 = None
+ arg2 = None
+
+ try:
+ arg1 = args[1]
+ arg2 = args[2]
+ except:
+ pass
+
+ # ctor(int)
+ if isinstance(arg0,int) and not arg2:
+ ### get class for 'self' type
+ c = self.getASTNodeType(arg0)
+ t = self.create(c)
+ if t:
+ t.initialize(arg0, ifelse(arg1,arg1,""))
+ return t
+
+ # ctor(int,something)
+ if isinstance(arg0,int) and arg2:
+ t = self.create(arg2)
+ if t:
+ t.initialize(arg0,arg1)
+ return t
+
+ # ctor(AST)
+ if isinstance(arg0,AST):
+ t = self.create(arg0.getType())
+ if t:
+ t.initialize(arg0)
+ return t
+
+ # ctor(token)
+ if isinstance(arg0,Token) and not arg1:
+ ttype = arg0.getType()
+ assert isinstance(ttype,int)
+ t = self.create(ttype)
+ if t:
+ t.initialize(arg0)
+ return t
+
+ # ctor(token,class)
+ if isinstance(arg0,Token) and arg1:
+ assert isinstance(arg1,type)
+ assert issubclass(arg1,AST)
+ # this creates instance of 'arg1' using 'arg0' as
+ # argument. Wow, that's magic!
+ t = arg1(arg0)
+ assert t and isinstance(t,AST)
+ return t
+
+ # ctor(class)
+ if isinstance(arg0,type):
+ ### next statement creates instance of type (!)
+ t = arg0()
+ assert isinstance(t,AST)
+ return t
+
+
+ def setASTNodeClass(self,className=None):
+ if not className:
+ return
+ assert isinstance(className,type)
+ assert issubclass(className,AST)
+ self._class = className
+
+ ### kind of misnomer - use setASTNodeClass instead.
+ setASTNodeType = setASTNodeClass
+
+ def getASTNodeClass(self):
+ return self._class
+
+
+
+ def getTokenTypeToASTClassMap(self):
+ return self._classmap
+
+ def setTokenTypeToASTClassMap(self,amap):
+ self._classmap = amap
+
+ def error(self, e):
+ import sys
+ print(e, file=sys.stderr)
+
+ def setTokenTypeASTNodeType(self, tokenType, className):
+ """
+ Specify a mapping between a token type and a (AST) class.
+ """
+ if not self._classmap:
+ self._classmap = {}
+
+ if not className:
+ try:
+ del self._classmap[tokenType]
+ except:
+ pass
+ else:
+ ### here we should also perform actions to ensure that
+ ### a. class can be loaded
+ ### b. class is a subclass of AST
+ ###
+ assert isinstance(className,type)
+ assert issubclass(className,AST) ## a & b
+ ### enter the class
+ self._classmap[tokenType] = className
+
+ def getASTNodeType(self,tokenType):
+ """
+ For a given token type return the AST node type. First we
+ lookup a mapping table, second we try _class
+ and finally we resolve to "antlr.CommonAST".
+ """
+
+ # first
+ if self._classmap:
+ try:
+ c = self._classmap[tokenType]
+ if c:
+ return c
+ except:
+ pass
+ # second
+ if self._class:
+ return self._class
+
+ # default
+ return CommonAST
+
+ ### methods that have been moved to file scope - just listed
+ ### here to be somewhat consistent with original API
+ def dup(self,t):
+ return dup(t,self)
+
+ def dupList(self,t):
+ return dupList(t,self)
+
+ def dupTree(self,t):
+ return dupTree(t,self)
+
+ ### methods moved to other classes
+ ### 1. makeASTRoot -> Parser
+ ### 2. addASTChild -> Parser
+
+ ### non-standard: create alias for longish method name
+ maptype = setTokenTypeASTNodeType
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### ASTVisitor ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+class ASTVisitor(object):
+ def __init__(self,*args):
+ pass
+
+ def visit(self,ast):
+ pass
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+### static methods and variables ###
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###
+
+ASTNULL = ASTNULLType()
+
+### wh: moved from ASTFactory as there's nothing ASTFactory-specific
+### in this method.
+def make(*nodes):
+ if not nodes:
+ return None
+
+ for i in xrange(0,len(nodes)):
+ node = nodes[i]
+ if node:
+ assert isinstance(node,AST)
+
+ root = nodes[0]
+ tail = None
+ if root:
+ root.setFirstChild(None)
+
+ for i in xrange(1,len(nodes)):
+ if not nodes[i]:
+ continue
+ if not root:
+ root = tail = nodes[i]
+ elif not tail:
+ root.setFirstChild(nodes[i])
+ tail = root.getFirstChild()
+ else:
+ tail.setNextSibling(nodes[i])
+ tail = tail.getNextSibling()
+
+ ### Chase tail to last sibling
+ while tail.getNextSibling():
+ tail = tail.getNextSibling()
+ return root
+
+def dup(t,factory):
+ if not t:
+ return None
+
+ if factory:
+ dup_t = factory.create(t.__class__)
+ else:
+ raise TypeError("dup function requires ASTFactory argument")
+ dup_t.initialize(t)
+ return dup_t
+
+def dupList(t,factory):
+ result = dupTree(t,factory)
+ nt = result
+ while t:
+ ## for each sibling of the root
+ t = t.getNextSibling()
+ nt.setNextSibling(dupTree(t,factory))
+ nt = nt.getNextSibling()
+ return result
+
+def dupTree(t,factory):
+ result = dup(t,factory)
+ if t:
+ result.setFirstChild(dupList(t.getFirstChild(),factory))
+ return result
+
+###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+### $Id$
+
+# Local Variables: ***
+# mode: python ***
+# py-indent-offset: 4 ***
+# End: ***
diff --git a/.venv/lib/python3.9/site-packages/xlwt/compat.py b/.venv/lib/python3.9/site-packages/xlwt/compat.py
new file mode 100644
index 00000000..4f77a574
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt/compat.py
@@ -0,0 +1,28 @@
+import sys
+
+PY3 = sys.version_info[0] >= 3
+
+if PY3:
+ unicode = bytes.decode
+ unicode_type = str
+ basestring = str
+ xrange = range
+ int_types = (int,)
+ long = int
+
+ def iteritems(d):
+ return iter(d.items())
+ def itervalues(d):
+ return iter(d.values())
+else:
+ # Python 2
+ unicode = unicode_type = unicode
+ basestring = basestring
+ xrange = xrange
+ int_types = (int, long)
+ long = long
+
+ def iteritems(d):
+ return d.iteritems()
+ def itervalues(d):
+ return d.itervalues()
diff --git a/.venv/lib/python3.9/site-packages/xlwt/excel-formula.g b/.venv/lib/python3.9/site-packages/xlwt/excel-formula.g
new file mode 100644
index 00000000..d98d9b90
--- /dev/null
+++ b/.venv/lib/python3.9/site-packages/xlwt/excel-formula.g
@@ -0,0 +1,374 @@
+header {
+ import struct
+ import Utils
+ from UnicodeUtils import upack1
+ from ExcelMagic import *
+
+ _RVAdelta = {"R": 0, "V": 0x20, "A": 0x40}
+ _RVAdeltaRef = {"R": 0, "V": 0x20, "A": 0x40, "D": 0x20}
+ _RVAdeltaArea = {"R": 0, "V": 0x20, "A": 0x40, "D": 0}
+
+
+ class FormulaParseException(Exception):
+ """
+ An exception indicating that a Formula could not be successfully parsed.
+ """
+}
+
+header "ExcelFormulaParser.__init__" {
+ self.rpn = ""
+ self.sheet_references = []
+ self.xcall_references = []
+}
+
+options {
+ language = "Python";
+}
+
+class ExcelFormulaParser extends Parser;
+options {
+ k = 2;
+ defaultErrorHandler = false;
+ buildAST = false;
+}
+
+
+tokens {
+ TRUE_CONST;
+ FALSE_CONST;
+ STR_CONST;
+ NUM_CONST;
+ INT_CONST;
+
+ FUNC_IF;
+ FUNC_CHOOSE;
+ NAME;
+ QUOTENAME;
+
+ EQ;
+ NE;
+ GT;
+ LT;
+ GE;
+ LE;
+
+ ADD;
+ SUB;
+ MUL;
+ DIV;
+
+ POWER;
+ PERCENT;
+
+ LP;
+ RP;
+
+ LB;
+ RB;
+
+ COLON;
+ COMMA;
+ SEMICOLON;
+ REF2D;
+ REF2D_R1C1;
+ BANG;
+}
+
+formula
+ : expr["V"]
+ ;
+
+expr[arg_type]
+ : // {print "\n**expr %s" % arg_type}
+ prec0_expr[arg_type]
+ (
+ (
+ EQ { op = struct.pack('B', ptgEQ) }
+ | NE { op = struct.pack('B', ptgNE) }
+ | GT { op = struct.pack('B', ptgGT) }
+ | LT { op = struct.pack('B', ptgLT) }
+ | GE { op = struct.pack('B', ptgGE) }
+ | LE { op = struct.pack('B', ptgLE) }
+ )
+ prec0_expr[arg_type] { self.rpn += op }
+ )*
+ ;
+
+prec0_expr[arg_type]
+ : prec1_expr[arg_type]
+ (
+ (
+ CONCAT { op = struct.pack('B', ptgConcat) }
+ )
+ prec1_expr[arg_type] { self.rpn += op }
+ )*
+ ;
+
+prec1_expr[arg_type]
+ : // {print "**prec1_expr1 %s" % arg_type}
+ prec2_expr[arg_type]
+ // {print "**prec1_expr2 %s" % arg_type}
+ (
+ (
+ ADD { op = struct.pack('B', ptgAdd) }
+ | SUB { op = struct.pack('B', ptgSub) }
+ )
+ // {print "**prec1_expr3 %s" % arg_type}
+ prec2_expr[arg_type]
+ { self.rpn += op;
+ // print "**prec1_expr4 %s" % arg_type
+ }
+ )*
+ ;
+
+
+prec2_expr[arg_type]
+ : prec3_expr[arg_type]
+ (
+ (
+ MUL { op = struct.pack('B', ptgMul) }
+ | DIV { op = struct.pack('B', ptgDiv) }
+ )
+ prec3_expr[arg_type] { self.rpn += op }
+ )*
+ ;
+
+prec3_expr[arg_type]
+ : prec4_expr[arg_type]
+ (
+ (
+ POWER { op = struct.pack('B', ptgPower) }
+ )
+ prec4_expr[arg_type] { self.rpn += op }
+ )*
+ ;
+
+prec4_expr[arg_type]
+ : prec5_expr[arg_type]
+ (
+ PERCENT { self.rpn += struct.pack('B', ptgPercent) }
+ )?
+ ;
+
+prec5_expr[arg_type]
+ : primary[arg_type]
+ | SUB primary[arg_type] { self.rpn += struct.pack('B', ptgUminus) }
+ ;
+
+primary[arg_type]
+ : TRUE_CONST
+ {
+ self.rpn += struct.pack("2B", ptgBool, 1)
+ }
+ | FALSE_CONST
+ {
+ self.rpn += struct.pack("2B", ptgBool, 0)
+ }
+ | str_tok:STR_CONST
+ {
+ self.rpn += struct.pack("B", ptgStr) + upack1(str_tok.text[1:-1].replace("\"\"", "\""))
+ }
+ | int_tok:INT_CONST
+ {
+ // print "**int_const", int_tok.text
+ int_value = int(int_tok.text)
+ if int_value <= 65535:
+ self.rpn += struct.pack(" max_argc or arg_count < min_argc:
+ raise Exception, "%d parameters for function: %s" % (arg_count, func_tok.text)
+ if xcall:
+ func_ptg = ptgFuncVarR + _RVAdelta[func_type]
+ self.rpn += struct.pack("<2BH", func_ptg, arg_count + 1, 255) // 255 is magic XCALL function
+ elif min_argc == max_argc:
+ func_ptg = ptgFuncR + _RVAdelta[func_type]
+ self.rpn += struct.pack("