xlrd-0.9.4/0000755000076500000240000000000012551375765012750 5ustar chrisstaff00000000000000xlrd-0.9.4/PKG-INFO0000644000076500000240000000174212551375765014051 0ustar chrisstaff00000000000000Metadata-Version: 1.1 Name: xlrd Version: 0.9.4 Summary: Library for developers to extract data from Microsoft Excel (tm) spreadsheet files Home-page: http://www.python-excel.org/ Author: John Machin Author-email: sjmachin@lexicon.net License: BSD Description: Extract data from Excel spreadsheets (.xls and .xlsx, versions 2.0 onwards) on any platform. Pure Python (2.6, 2.7, 3.2+). Strong support for Excel dates. Unicode-aware. Keywords: xls,excel,spreadsheet,workbook Platform: Any platform -- don't need Windows Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Operating System :: OS Independent Classifier: Topic :: Database Classifier: Topic :: Office/Business Classifier: Topic :: Software Development :: Libraries :: Python Modules xlrd-0.9.4/README.html0000644000076500000240000001110012155372403014546 0ustar chrisstaff00000000000000 The xlrd Module -- README

Python package "xlrd"

Purpose: Provide a library for developers to use to extract data from Microsoft Excel (tm) spreadsheet files. It is not an end-user tool.

Author: John Machin, Lingfo Pty Ltd (sjmachin@lexicon.net)

Licence: BSD-style (see licences.py)

Version of xlrd: 0.7.1 -- 2009-05-31

Versions of Python supported: 2.6-2.7.

External modules required:

The package itself is pure Python with no dependencies on modules or packages outside the standard Python distribution.

Versions of Excel supported: 2004, 2003, XP, 2000, 97, 95, 5.0, 4.0, 3.0, 2.1, 2.0. Support for Excel 2007 .xlsx files scheduled for version 0.7.1.

Outside the current scope: xlrd will safely and reliably ignore any of these if present in the file:

Unlikely to be done:

Particular emphasis (refer docs for details):

Quick start:

    import xlrd
    book = xlrd.open_workbook("myfile.xls")
    print "The number of worksheets is", book.nsheets
    print "Worksheet name(s):", book.sheet_names()
    sh = book.sheet_by_index(0)
    print sh.name, sh.nrows, sh.ncols
    print "Cell D30 is", sh.cell_value(rowx=29, colx=3)
    for rx in range(sh.nrows):
        print sh.row(rx)
    # Refer to docs for more details.
    # Feedback on API is welcomed.

Another quick start: This will show the first, second and last rows of each sheet in each file:

    OS-prompt>python PYDIR/scripts/runxlrd.py 3rows *blah*.xls

Installation:

Download URLs:

Acknowledgements:

xlrd-0.9.4/scripts/0000755000076500000240000000000012551375765014437 5ustar chrisstaff00000000000000xlrd-0.9.4/scripts/runxlrd.py0000644000076500000240000003766012551374703016512 0ustar chrisstaff00000000000000#!/usr/bin/env python # Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd # This script is part of the xlrd package, which is released under a # BSD-style licence. from __future__ import print_function cmd_doc = """ Commands: 2rows Print the contents of first and last row in each sheet 3rows Print the contents of first, second and last row in each sheet bench Same as "show", but doesn't print -- for profiling biff_count[1] Print a count of each type of BIFF record in the file biff_dump[1] Print a dump (char and hex) of the BIFF records in the file fonts hdr + print a dump of all font objects hdr Mini-overview of file (no per-sheet information) hotshot Do a hotshot profile run e.g. ... -f1 hotshot bench bigfile*.xls labels Dump of sheet.col_label_ranges and ...row... for each sheet name_dump Dump of each object in book.name_obj_list names Print brief information for each NAME record ov Overview of file profile Like "hotshot", but uses cProfile show Print the contents of all rows in each sheet version[0] Print versions of xlrd and Python and exit xfc Print "XF counts" and cell-type counts -- see code for details [0] means no file arg [1] means only one file arg i.e. no glob.glob pattern """ options = None if __name__ == "__main__": PSYCO = 0 import xlrd import sys, time, glob, traceback, gc from xlrd.timemachine import xrange, REPR class LogHandler(object): def __init__(self, logfileobj): self.logfileobj = logfileobj self.fileheading = None self.shown = 0 def setfileheading(self, fileheading): self.fileheading = fileheading self.shown = 0 def write(self, text): if self.fileheading and not self.shown: self.logfileobj.write(self.fileheading) self.shown = 1 self.logfileobj.write(text) null_cell = xlrd.empty_cell def show_row(bk, sh, rowx, colrange, printit): if bk.ragged_rows: colrange = range(sh.row_len(rowx)) if not colrange: return if printit: print() if bk.formatting_info: for colx, ty, val, cxfx in get_row_data(bk, sh, rowx, colrange): if printit: print("cell %s%d: type=%d, data: %r, xfx: %s" % (xlrd.colname(colx), rowx+1, ty, val, cxfx)) else: for colx, ty, val, _unused in get_row_data(bk, sh, rowx, colrange): if printit: print("cell %s%d: type=%d, data: %r" % (xlrd.colname(colx), rowx+1, ty, val)) def get_row_data(bk, sh, rowx, colrange): result = [] dmode = bk.datemode ctys = sh.row_types(rowx) cvals = sh.row_values(rowx) for colx in colrange: cty = ctys[colx] cval = cvals[colx] if bk.formatting_info: cxfx = str(sh.cell_xf_index(rowx, colx)) else: cxfx = '' if cty == xlrd.XL_CELL_DATE: try: showval = xlrd.xldate_as_tuple(cval, dmode) except xlrd.XLDateError as e: showval = "%s:%s" % (type(e).__name__, e) cty = xlrd.XL_CELL_ERROR elif cty == xlrd.XL_CELL_ERROR: showval = xlrd.error_text_from_code.get(cval, '' % cval) else: showval = cval result.append((colx, cty, showval, cxfx)) return result def bk_header(bk): print() print("BIFF version: %s; datemode: %s" % (xlrd.biff_text_from_num[bk.biff_version], bk.datemode)) print("codepage: %r (encoding: %s); countries: %r" % (bk.codepage, bk.encoding, bk.countries)) print("Last saved by: %r" % bk.user_name) print("Number of data sheets: %d" % bk.nsheets) print("Use mmap: %d; Formatting: %d; On demand: %d" % (bk.use_mmap, bk.formatting_info, bk.on_demand)) print("Ragged rows: %d" % bk.ragged_rows) if bk.formatting_info: print("FORMATs: %d, FONTs: %d, XFs: %d" % (len(bk.format_list), len(bk.font_list), len(bk.xf_list))) if not options.suppress_timing: print("Load time: %.2f seconds (stage 1) %.2f seconds (stage 2)" % (bk.load_time_stage_1, bk.load_time_stage_2)) print() def show_fonts(bk): print("Fonts:") for x in xrange(len(bk.font_list)): font = bk.font_list[x] font.dump(header='== Index %d ==' % x, indent=4) def show_names(bk, dump=0): bk_header(bk) if bk.biff_version < 50: print("Names not extracted in this BIFF version") return nlist = bk.name_obj_list print("Name list: %d entries" % len(nlist)) for nobj in nlist: if dump: nobj.dump(sys.stdout, header="\n=== Dump of name_obj_list[%d] ===" % nobj.name_index) else: print("[%d]\tName:%r macro:%r scope:%d\n\tresult:%r\n" % (nobj.name_index, nobj.name, nobj.macro, nobj.scope, nobj.result)) def print_labels(sh, labs, title): if not labs:return for rlo, rhi, clo, chi in labs: print("%s label range %s:%s contains:" % (title, xlrd.cellname(rlo, clo), xlrd.cellname(rhi-1, chi-1))) for rx in xrange(rlo, rhi): for cx in xrange(clo, chi): print(" %s: %r" % (xlrd.cellname(rx, cx), sh.cell_value(rx, cx))) def show_labels(bk): # bk_header(bk) hdr = 0 for shx in range(bk.nsheets): sh = bk.sheet_by_index(shx) clabs = sh.col_label_ranges rlabs = sh.row_label_ranges if clabs or rlabs: if not hdr: bk_header(bk) hdr = 1 print("sheet %d: name = %r; nrows = %d; ncols = %d" % (shx, sh.name, sh.nrows, sh.ncols)) print_labels(sh, clabs, 'Col') print_labels(sh, rlabs, 'Row') if bk.on_demand: bk.unload_sheet(shx) def show(bk, nshow=65535, printit=1): bk_header(bk) if 0: rclist = xlrd.sheet.rc_stats.items() rclist = sorted(rclist) print("rc stats") for k, v in rclist: print("0x%04x %7d" % (k, v)) if options.onesheet: try: shx = int(options.onesheet) except ValueError: shx = bk.sheet_by_name(options.onesheet).number shxrange = [shx] else: shxrange = range(bk.nsheets) # print("shxrange", list(shxrange)) for shx in shxrange: sh = bk.sheet_by_index(shx) nrows, ncols = sh.nrows, sh.ncols colrange = range(ncols) anshow = min(nshow, nrows) print("sheet %d: name = %s; nrows = %d; ncols = %d" % (shx, REPR(sh.name), sh.nrows, sh.ncols)) if nrows and ncols: # Beat the bounds for rowx in xrange(nrows): nc = sh.row_len(rowx) if nc: _junk = sh.row_types(rowx)[nc-1] _junk = sh.row_values(rowx)[nc-1] _junk = sh.cell(rowx, nc-1) for rowx in xrange(anshow-1): if not printit and rowx % 10000 == 1 and rowx > 1: print("done %d rows" % (rowx-1,)) show_row(bk, sh, rowx, colrange, printit) if anshow and nrows: show_row(bk, sh, nrows-1, colrange, printit) print() if bk.on_demand: bk.unload_sheet(shx) def count_xfs(bk): bk_header(bk) for shx in range(bk.nsheets): sh = bk.sheet_by_index(shx) nrows, ncols = sh.nrows, sh.ncols print("sheet %d: name = %r; nrows = %d; ncols = %d" % (shx, sh.name, sh.nrows, sh.ncols)) # Access all xfindexes to force gathering stats type_stats = [0, 0, 0, 0, 0, 0, 0] for rowx in xrange(nrows): for colx in xrange(sh.row_len(rowx)): xfx = sh.cell_xf_index(rowx, colx) assert xfx >= 0 cty = sh.cell_type(rowx, colx) type_stats[cty] += 1 print("XF stats", sh._xf_index_stats) print("type stats", type_stats) print() if bk.on_demand: bk.unload_sheet(shx) def main(cmd_args): import optparse global options, PSYCO usage = "\n%prog [options] command [input-file-patterns]\n" + cmd_doc oparser = optparse.OptionParser(usage) oparser.add_option( "-l", "--logfilename", default="", help="contains error messages") oparser.add_option( "-v", "--verbosity", type="int", default=0, help="level of information and diagnostics provided") oparser.add_option( "-m", "--mmap", type="int", default=-1, help="1: use mmap; 0: don't use mmap; -1: accept heuristic") oparser.add_option( "-e", "--encoding", default="", help="encoding override") oparser.add_option( "-f", "--formatting", type="int", default=0, help="0 (default): no fmt info\n" "1: fmt info (all cells)\n" ) oparser.add_option( "-g", "--gc", type="int", default=0, help="0: auto gc enabled; 1: auto gc disabled, manual collect after each file; 2: no gc") oparser.add_option( "-s", "--onesheet", default="", help="restrict output to this sheet (name or index)") oparser.add_option( "-u", "--unnumbered", action="store_true", default=0, help="omit line numbers or offsets in biff_dump") oparser.add_option( "-d", "--on-demand", action="store_true", default=0, help="load sheets on demand instead of all at once") oparser.add_option( "-t", "--suppress-timing", action="store_true", default=0, help="don't print timings (diffs are less messy)") oparser.add_option( "-r", "--ragged-rows", action="store_true", default=0, help="open_workbook(..., ragged_rows=True)") options, args = oparser.parse_args(cmd_args) if len(args) == 1 and args[0] in ("version", ): pass elif len(args) < 2: oparser.error("Expected at least 2 args, found %d" % len(args)) cmd = args[0] xlrd_version = getattr(xlrd, "__VERSION__", "unknown; before 0.5") if cmd == 'biff_dump': xlrd.dump(args[1], unnumbered=options.unnumbered) sys.exit(0) if cmd == 'biff_count': xlrd.count_records(args[1]) sys.exit(0) if cmd == 'version': print("xlrd: %s, from %s" % (xlrd_version, xlrd.__file__)) print("Python:", sys.version) sys.exit(0) if options.logfilename: logfile = LogHandler(open(options.logfilename, 'w')) else: logfile = sys.stdout mmap_opt = options.mmap mmap_arg = xlrd.USE_MMAP if mmap_opt in (1, 0): mmap_arg = mmap_opt elif mmap_opt != -1: print('Unexpected value (%r) for mmap option -- assuming default' % mmap_opt) fmt_opt = options.formatting | (cmd in ('xfc', )) gc_mode = options.gc if gc_mode: gc.disable() for pattern in args[1:]: for fname in glob.glob(pattern): print("\n=== File: %s ===" % fname) if logfile != sys.stdout: logfile.setfileheading("\n=== File: %s ===\n" % fname) if gc_mode == 1: n_unreachable = gc.collect() if n_unreachable: print("GC before open:", n_unreachable, "unreachable objects") if PSYCO: import psyco psyco.full() PSYCO = 0 try: t0 = time.time() bk = xlrd.open_workbook(fname, verbosity=options.verbosity, logfile=logfile, use_mmap=mmap_arg, encoding_override=options.encoding, formatting_info=fmt_opt, on_demand=options.on_demand, ragged_rows=options.ragged_rows, ) t1 = time.time() if not options.suppress_timing: print("Open took %.2f seconds" % (t1-t0,)) except xlrd.XLRDError as e: print("*** Open failed: %s: %s" % (type(e).__name__, e)) continue except KeyboardInterrupt: print("*** KeyboardInterrupt ***") traceback.print_exc(file=sys.stdout) sys.exit(1) except BaseException as e: print("*** Open failed: %s: %s" % (type(e).__name__, e)) traceback.print_exc(file=sys.stdout) continue t0 = time.time() if cmd == 'hdr': bk_header(bk) elif cmd == 'ov': # OverView show(bk, 0) elif cmd == 'show': # all rows show(bk) elif cmd == '2rows': # first row and last row show(bk, 2) elif cmd == '3rows': # first row, 2nd row and last row show(bk, 3) elif cmd == 'bench': show(bk, printit=0) elif cmd == 'fonts': bk_header(bk) show_fonts(bk) elif cmd == 'names': # named reference list show_names(bk) elif cmd == 'name_dump': # named reference list show_names(bk, dump=1) elif cmd == 'labels': show_labels(bk) elif cmd == 'xfc': count_xfs(bk) else: print("*** Unknown command <%s>" % cmd) sys.exit(1) del bk if gc_mode == 1: n_unreachable = gc.collect() if n_unreachable: print("GC post cmd:", fname, "->", n_unreachable, "unreachable objects") if not options.suppress_timing: t1 = time.time() print("\ncommand took %.2f seconds\n" % (t1-t0,)) return None av = sys.argv[1:] if not av: main(av) firstarg = av[0].lower() if firstarg == "hotshot": import hotshot, hotshot.stats av = av[1:] prof_log_name = "XXXX.prof" prof = hotshot.Profile(prof_log_name) # benchtime, result = prof.runcall(main, *av) result = prof.runcall(main, *(av, )) print("result", repr(result)) prof.close() stats = hotshot.stats.load(prof_log_name) stats.strip_dirs() stats.sort_stats('time', 'calls') stats.print_stats(20) elif firstarg == "profile": import cProfile av = av[1:] cProfile.run('main(av)', 'YYYY.prof') import pstats p = pstats.Stats('YYYY.prof') p.strip_dirs().sort_stats('cumulative').print_stats(30) elif firstarg == "psyco": PSYCO = 1 main(av[1:]) else: main(av) xlrd-0.9.4/setup.py0000644000076500000240000000353712155372403014454 0ustar chrisstaff00000000000000#!/usr/bin/env python from os import path import sys python_version = sys.version_info[:2] if python_version < (2, 6): raise Exception("This version of xlrd requires Python 2.6 or above. " "For older versions of Python, you can use the 0.8 series.") av = sys.argv if len(av) > 1 and av[1].lower() == "--egg": del av[1] from setuptools import setup else: from distutils.core import setup from xlrd.info import __VERSION__ setup( name = 'xlrd', version = __VERSION__, author = 'John Machin', author_email = 'sjmachin@lexicon.net', url = 'http://www.python-excel.org/', packages = ['xlrd'], scripts = [ 'scripts/runxlrd.py', ], package_data={ 'xlrd': [ 'doc/*.htm*', # 'doc/*.txt', 'examples/*.*', ], }, description = 'Library for developers to extract data from Microsoft Excel (tm) spreadsheet files', long_description = \ "Extract data from Excel spreadsheets (.xls and .xlsx, versions 2.0 onwards) on any platform. " \ "Pure Python (2.6, 2.7, 3.2+). Strong support for Excel dates. Unicode-aware.", platforms = ["Any platform -- don't need Windows"], license = 'BSD', keywords = ['xls', 'excel', 'spreadsheet', 'workbook'], classifiers = [ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Operating System :: OS Independent', 'Topic :: Database', 'Topic :: Office/Business', 'Topic :: Software Development :: Libraries :: Python Modules', ], ) xlrd-0.9.4/tests/0000755000076500000240000000000012551375765014112 5ustar chrisstaff00000000000000xlrd-0.9.4/tests/__init__.py0000644000076500000240000000000012155372403016173 0ustar chrisstaff00000000000000xlrd-0.9.4/tests/apachepoi_49609.xlsx0000644000076500000240000031572012551374567017545 0ustar chrisstaff00000000000000PK^<!L[content_types].xmln06P?z5&]Hx:*8Ev$]NvQY3g>fUwI ZTsVM[_x<O-G I;] Ƴ[&kI59md ]Ȭ!04$Iۚ6H:keXrv͙tV lQ rmuptd;I~Dʔq6CKtX)7b|6ث.b RMzz:t,Z4on]L9E1i_ξ+_t`|E)t &ϳPK^wIx*)!BEpx'?R) Q0yE͕}\{.rC4L*u^^n^g\XO8NVTj\%Շ%. ٢K%QLEȘ da2;OxJ"ogg'{oxo40hCAceY`G0mnE  Z:\c,%b&є"-f`;gpnGa}e#,[aV5 TqZ靟Yv\pCi|ik \ "/'G$뉡nUo|bzΎ&?`a⌋;. FJҵGQAʝqxZa09$(WB(v:*6*;@"Gc5\UtC)c 3|{3Z$W$QuUohӄ[ݞ"52ӣwչ#ʓgޝ^e&'+#{7Y8^?B+̓C޳+"휋.m%ft<m *f 2h[!LuMuS!VƧ-w/4+d]Tq W۰>6ySm:k>=Ƿ_/OS䷐A>i?* B @ m$ @ @ $H D @ ( P@@Eh @@ 4h D@  0@`C X @` ,X D`8 p@G?zPK^o8_? ~/>x_oGݿ~?~z-o???˿Oz7?~o?_C=]M}yWW%^~H/ー|_>{__{x_>9&?>?&X\ri7OcLJ׹;sW|/o_{C~7g~+{y~μ~5OY޾myzw,ι:޹9;ѹ}8ٽ16֧gxZ㱷8cs}y?뛳<6طgx c}?;짳%c=ŒמbâbkgaX0Y,y{,<|Z{,qxk0_5./<^]=^_\=xz <޾}oWdM[{}oW>E7],pa?<n/jm77w7+-ra>N~\\z-L:խ楀;ӟQr+INu=>^]Nvb ׀;jS?7;Wʧwowrp( wq;_?^eݎ>+CKeU@h]n;b!驣G@xy삃on.|^ ^^ r -TNr} ,r(ꆅwWon7ԩn})p{򿽹xZߨࡠ1aunWt*<}㡠dOn/ ̓w^nDHry{鲴RAQxuw%¾iUTto =K˼|quɚQ1v6r=yn~ύqR /$Ǖ=5\}}s+Qq>5`= .:Dono7+/|*h^y M}*xupn 0:˻aӋkՂ W":^D///s"b[ .^^^yk?U.$"ˋG".^EX|7ǖ"A<fգW2Nhvy-uXSq(\E1CؕHgr_E^\ڞY*uWwHOnDF+\l :֯sq~%]ϸ件 G}l$ĊW?%noiD¾e-rs*9ܝhxHRmv)mЀ^eɥvszj;*Br Zf[\KX$;uς/dx2ɦ*7K)bJ` &L(x'WQ)KJ=U^[$vN1xv.I2 ;xIy!HדڮTqtqػ|Z# ͕<vAwoﰾw($eq#Ɋois5,\Y Uނ0P0wWzoazVNMdgQ\7D .J955?QE^lSr q$Q(Y4kME\u-N* CV)xXr%'qEg/W⒤J.9B;{Ra1cȒEPދr}莋zM,$ .teHx/s]u߸fYREAqPpѳDTWppPjܘ^]X6~ `4W\p>{ .фXJŤ6K<bn)=]-FUD)RQ*Ժ^E)~l+w"$SER`q$X{eO:H篣-#{MA:q^<1KuWQL4Y-I>jK`h*X|"¢"g-D+*Aa6E8t18g(bgcFy%">$'ItvID"w~ Ï]-AawMҧ" LA%z|S&+Nb1mFb? 2}WoOedo;펄U{UC[䖖 {dPh-rsGTν2l-b%}_P%U/XX8~bNuȩ-}Ù8ӗxq2 {34ڸG*AX("x*.==uvE>؋YTS_0y>XnP;f JpWT)&t-I@D%Od 0ںnK_'һW$[[Iy,ڸ+K+:[*0N[iSFV~KFHN*hޘ;f`V\cݞgtO aK]5Jf|$SDg+zj()Eƥ/SE9< %)Rr?X#$F=i,PL@HA* ʻk2eː *ZʴIwsE bɋ}ZT{̑]YZm=N?NfrI#x=QELH-;,!%Br *Jhl:\gLFz.a8~KAE|)E*= 15Ӆ}scN%2HϤ!4>7ꔷT&YQ+Sv|* ,#{ں +JMun<ϭp'd,[[bX^՚\(l%hkTf R?פV_Ie ydșK%q=0l4F=gR߯s~FtCXK;e.7Ge?"I\En"|"GX %-╨H0Ը?m QB8A}VH:y~޶@ȻЏ M2a~*__rn¬.L%>/^f?:'_䛶OeRxq}W#F'e|tV)A`Zf#ɰs0CPR别Z.rl::~Hl9ERz8>%Ԯ5#+bY*VƷlzyz:^Tݴw)<dgC̲&"¬RA! \> RtpTqDZVQ=W,vߨ Uܵެ| ]'[Eh C\*O>T[?YoL9/Qa jVSô|p&;o}ئK7ΣWxy+Nluqظ+H< ? ^ߨ`m2[aeWm~C\Cp 3 .B/=釶-k,},~a|9N,T/u0/͓+VxT.]E K-Jj)Kg<\|Ҡ,Iim\N~n"(U\tŸ0p[pU %3AAPdiČܟO qm7] ?d_2zd#jBKz7mR-~K)/eO +1م Unƃ9YJ0g\a5 o)zǨ%ZS5qjUG4dobb)q 4-/sAs?խӧzIeVJ5IW(GI1Oҁc,BWD%a<:_fٕ=W`qAb+ 07X7Ij`P'mmKV͍s\skFF9y=WT4i9~Kţj{wWI`W`ţzuX"G1#8ҀGÝ{i}6Tby cw:CyoRw.?s]js]JaQ4h;2):@8Wn[DIJx8qOІX8~IhHT1'_YPThy~__D,5 :Z1G7 9ǯ/ri}O"2tc2j0'=Y!͍r9J>/UM#w狶M$`.!|6cS!8_E<+Z(9QqzCj5DBٓhm(b]A#It)'ym̦ I䁫0|L,[Z/6aDEäIO rL"gpBzAynƝ*j}9-9vXRi*8ZھKj FH͑\johLmI0 ۢٳA֩!bePO…/Q,Jks~b_utSMsqn:ۮD8H<لH+)ɱY &[䠵JE""wWq2>6ch0qSAƃw!/x p8bp B"2XDXZT 3Kݟ*2VmpT$K9TO=0U 25֑GKj?ۓj`٧iӱ8cK;sF_nz]TpESan1NT_^ )vb^OLM q#%]:AIZe'Aaz/b:ں31K<)X!7 '?\̴^&,Af䓕v>\MG\F̵)k(ubjVm<0yH4qۿמmQ`A=S4h'5p_A?|}f]QmEqs-S0Х^_$ImL[퓶 zb cO"/jB6gOEI}yy<(MEۯ^4^ɗm1HhHǓ] mZc4Х"oJ|WB_۶~zi-"W0nV|%MEC[D|T"Bȏu !5YS[Rf[өTqc#B"F*=\UWHL*!]LWoŮBRvYo )iʛ* Eii&KG#jXO)8X"5̐u'uGZ7ܩ"pedVH´Xc <^0t>C Z̩!43"ץ{.s+E_cq3ʽS"f*_\/@.P}} &}*yuP81ZQ)>+a*8uXEr}Yp1cj6׻zk8ZO+VXQ}] bA!b% 0LB)ksb2|#@ uTb%=`|Z r 0@*k<ǿ`T;9cfiuJ=x L?6*g¶{A[ 20l dEOSKb$H][e?LgDy,R 9o\J U/RX#"_4?_ 2]MZOԑaAV5NEF3S.UsZ"#߇j|%dF)"lT1An`e0U`1CSb* EHBH,K`}֒ 0Dž \3FJgGB߰U`̈N`64AW`LŠ '*QIo@>W`dnAE>`Ū+Oc| .AWK(h@p- q4Ֆӕ–Th@|K.xu j5RRƣM:Ϸ*0+!(0qM{sFдcI7r;0bz!X\ʘ#vV1R?ץֱyWt)K\[xaLS4vy0ydxVT{=2|BWqTێpA[DDKݜT0"Jr ~0)S1Ge>(f8.,a :}Iϖ5d t泶 ?-x[,&I2,3+f$xps\"_ix/NZ'#e?ѾnfߴE'>:fӊ(mkI"b#5 QºTMK_umb ꝟǶ@(\O31.p T7J2J)ˇtzj0P*c.A.n" +[›/tWYnJ'U!EuE.UoqaSjtIߣT`~IV]2fyJA5mǴ%LcqdS;P.Bԇx86* 1!4|qB* ދ{?-#x:5Dd>O-x z"XQ1(-mEfb3sQE}q.!.*sz.VsNP -\FVNx[-'_Ow_NKsEq;Q)kƢ;,:CT͙;=W+&t[^SrE*R^)~-U9nq SD7gY׻m{6#^{#ԗקI{_6#愜"|"A^4Q6qW.AP"wh'|" V`dI[DT֠&86IeR+G_)Wyy_zd&_ z3A_ekR@ϋZE_ExR"v8ߴ5Pչ +|O9i9^5618%hhRm#fB!k⺫O 3I74 +e~KjEpRҐ4tՓS(QK>r ŞS=}tRĜYԙ)4\<T,$v.@=4rsߪHT0L{Ep]!fk~qKC8E2U6HeNONhG= ")_AEG9W>H9;X:) vq?cܘRu:NZ2$]V@i;~Pf"E7y yř\ Z2(P1+_H)I Yn+9U[&Ri9Lʣh$2"O ,1}tx _`W(ԬsX&\;^2$×%k/mcL1Nswj)t6_Sxh,c <= -QS/Mϋ˿0 &j 1en𧆘G-gO7 ~MZGL,ZL_W3HQeXzd> ->aZђ[F8FCtnΦLYLPM^UP|]^qwH_0vC_坶o*E&yP4UpNPHOtdUbφL_DG1rx>V"G2gA,"WfM4fPV^ͅ7b"/c-Ḝ>3ٳ} 7a& )LCUbWU=O[-ck{~/QLI\Dn9p1;pKC?ǁv2UzgBW@jh[[\ _x*]'իůpz 2J\)iǂ[5<,*7*zճ YxN@P 1viTh MX!& (@Rf^z0;JLݝ /ND5t"";a; Ͽ*,DE³`U4 JOe 'r38AerQU |pf0UҾ(NK>  /.vy/¡]aGC%|Y7{ Q @ίU_ELgUdJHkUnU|5|۾O]Ɓ_ :GcҭohdU~B;4c*?U$<$!ۃu NԷ #\S]R%DMÂsǣ\vÕ)ZKNyޤvC0MGG׿^Fo'^1cź+d*"h @@:a\CudLES~%1ZMtRY_!F|a|:w0ʳVxj )d Ԭ1>TC3EWTATB*L|+ScQdfEJ9/ 9o Z+}N&} hKi`iEO~M=*K9u @TT5&9%m(9SjɎ$G{%G9aAL!DR0da*ԥ4*y7Y!]ྔ} IWc45 2_jSεbH{A6Ȅ.>TfSCtJ"d:ߨhvMO2=>7t|u`eW;RZ%bF#2-S^5LiEFʽCidKENf̔Lح/rkČFj:dx 2Հgg)_ |-_!S.`^_:`: +^Iݫb3Ncz:d"Q W|>&.ty: 1pLVh+sՎ%*ڞE+ PU#ED. d:PqbO\` J\x@AW{xIy(EW[&u ->r[<=Rٟ;`jp+GUN's^sL M!2Jiy20S/i8gSOK$d[tDqhzi˼*}]Wk^ݟK_D49w*}٠ yC#vJ߫O;G Q[[˨lEB5z4iːò 0aQ6c?j%q/U)̆*.B@H0~8j>(Ɉ'NxY- 8E_2lW;|^E|Ywc}"w\~۾j!J2$/2</k~_7uRK*+CxA 'B+3 ey*oa7W(W<כ=ICWCmu=wVUs_{q("j}д]- W[eQy7+FjSsW_2I!C zC܅ #&%*ox΂\Zr [QTOrdAOڰ/! ? ]`(/ Bfve*P0ЂK$1wkYRC*T:d[ŕ* ޑJ׽.{p,ĭ8В&zTY@ f>|8cN4Wg1i)z1pt=yw[)^DBx7 4.]f!U R&foyr@3Ů>{IfJ):$L3S<6wS)!ǭ/rp`왋 䆙"źƄZZ*;Ja)ԯP8AQL!3M%E^SL;ZD5afffCLV_l}Ln L0W,ecN/G}Zϯ6[O 35isRyKEJpfă~mCo>f"BbǘJ[LyK9/s3uL 1wګ9FHz=RGCfaxqOO 1_rEd^aEl]h*>TCL0JR_5AZz7COUCN3 ۞JQC̣+xU'2 fwKW}ñyjg" dx0E˯ nUC8hŬCT%4$3I:25t*B nz .`2hJӄ1Mϝޔ5_X$%J_* /_T+ey,8Nc!jhf+Ȏqh{\kKL*2v"x7grLcaNxU++/9JZN+;m]%=V4n]DH^+{Wy>euVG||PW9<*dse H§ࣶJWHz|\V9?&?WO*Qf`s>vw)?ȕ_W"e%qiQ.%z1-/s!Ib6ěU_E=8_) b1M]!N+(y-߶O]Gt=awb(3tfΟDMgNn梨Hй[c)c]%%ui9u GiMΫ1_wB8]^2npOJrҩS :# t_&vW{#`UX63(Gb Q m2_\Ie` 3"S( -/0SEĺ 5$C#V aHnNu1~*}_ԜTez0!t| ^(K:^l#6w䢁1hxKQixI:MlhQ2/+CїD2E}GDeϊ)_mhO)n*_g5 (`u__(DZ߻P+x"DL:sii~(u2e֝C5xQƬHò =1M~`=Gm81J#Di?pd\qUtp$rSO*g$M"&eDzYя"m͜bʸNьӣg [sԪ u_]{,~oxまm_H < Yf1u۞;F?QwZh6\VHP*0*B€Jfu%ݺ J]V < i}NW>:\cwUvltǡ]Gzei/j^*Bęj)iG4L}) 48S'JJLW1FYCIDan TD'5@hf-ac IߨD2 +3HnTYeX1 y71Z  k^\y%:,Mƙhٙ#d;n=Ť 'Jjgd L mf?U(LA6M+1ePC&ٹB&)}2\oyԕ cΖIW|q2H .,ֶ!A$gR,V!Aj8%{@ #&N=Fu. 1U񞧍YcbF<8iZs;l'~ieT[mB&ի(E%8*Ǩa[FH1:R 2w&n2/rx2g 3Y2l=TLD|11IK Y \4< )lCI1cG!ËWaCL wN{15 1q<`5P+Əd*+: 6߆6q^-8iY&o+ }ziPJTit*_=sNLK_LL5MG&{WOz@95~i첺Ŧ8d6:^qL'p8C4Lu+"|l mW;.=M.353 ,C7t}13xL) #+˄?ԫ_!}o_Y|Wo2+u۽VIv f}VD|3YUV5nf#\1b q@ò 3*t Q[ =jR)c*.5tOgL.!B=O*Q3Xөs2`u9Shۻ+qO 5;D_vR=VesIƏ).40ލLNBђ;d^/ba? N~As4y[ 4+Qz1HzZAL*0IrviٴqV ꫝ)d ʹXm(0ۊf?]W~KDJA4Qd 3iD*&6阉ɡE0SHeP3'w9%‹̾ 45AW@_*;BA'd.^i 2Hx ԯWo iEu+joM(Btcӱ׌ϯCW5D4~nN5[djp0cyg,Q5jUli3*i] WRɰ|}qS~ޮ(D)Nz|#(TRD_cd+`XU]:i.0/"4dlo*7i ,ˠ]Jh"[i@5,ilzƍǾJƺ+RwZe)R hלg9ۺ_mCK.!0\U \i/M[W[F>FLUy B_kD\=5YZw4wNʯu:FÉ.|L8ẉ#^'⿁:sw<:F[0z2f^#9 `SZL7՘)rn^}EZ^ѽiS2dw*aB#ɑcw*%u"t^zuz5[FdReR># IĭMBIB2&K~W$]o~s)x{n㼕FnR0,ڹȯ*/CDqsHVꫤ3o_Eң@M]E@<7a}"mrF yk^ ][&.٫ۣJOzpʚ2>3ݬ* Uru_=Aɦ\q$}"P:_ø`5<:B*P2d(ufDRgA3'SE: 4}㪙aST>lӈK@Tj̖^4 >z ꕃRZ!,[Lt5=@`J >%bh|pY3GhZ3!BjCQIc9}_m},NdL/dLQ&] 3u$ n׳<(gb^vY0L|-z.qQi|8i٦t,fnq u?{,X*!?rx-@3!T4BT5 V J幁Q =4=l' ؉3A׎i(zru`f Q) L )[W_Wԯ/ůja&g.:)]Iais~=g&eĸZޫDҴO,&+>p3hٳayxS<b3&M8q89E`EKrbtك6>H饳;W!'ȝ߸Udhҵ?oo7(F'~mx/! ;c|W z#Z*$'mKj(mJ3e;o9>čO^ b,樱m)tLm+J1u^2u녝*Jƞ"у6]?xrȜ@wXH.o{KYzC×D jyCB)lcV–WZԥ^Znb'VSёoJ_5Db;YfPBjq6zm}e)ҶbWA5(!p_,bDeD14ԣĺo:bj6,.0Mqƶ w\+l3$8@wHQJtkM&_xǠ.gbv^!wf,³;O}y`Q 3*jCBjWh 㐟 2'Rdz3\ՏIت甼C?|%5uȴkr5mʫ1P>)c c#XDJ"XAf1 ;KÖH4 EUYQ8jY) zJbU;lNŒ4dYlv$ . /"Սy5o{_J{}JS=*x0QڱwWƷu~|PH}DxYa-7:薜E¥"-ð1kWY|4蓺JQfbZwܧu uSeΖMgDֹSfJy{$Y6u]d4]eu1loU_߭(||WIh)}U[nkӪ*}#!sI!۸kXRxoNH;f_ut 1c_%~a瀥$:D q)5 #ѓT,N_Wqcpca yYä3%Hٴ` (W?$*˃?TJL LEJ/:tB\4wJo{WS 6c"XaeMeT+:Q4I^b'N[G U.gXLA T Tж_kAL oxi]er(d7 bL1D ȜNi,T#/MOGEN>7t2R("WPA\if|X]ZH=]RB`xx 0_$LY5mp,i. `FP]wx32:jz2Ȕ~/*\eJ!t1f5xj_gD u%Y[f&{([LCfWom VWVf>05ARb")ܥkA4RSGLM]I#ԡE z1CҌ_"NיjfbcifayŨ jhǘgd}u+b<A}Ⓛ#ZVX#wdˆ-A&f0 +C$3:S b旅&JKvnD47zJ&KuGn.%*7$~1UPS׎RWP]ζ)Ijz2 5[=1S5lw0tscEl[%0<0yn5oZÎX\sC#~̠C]8^LUieVXΰ3T5[tu_㠐zW˼*]W寫W/JVJǗ |*ǽŗwoIM'h_p < L3">QcO9Dͤa-DERE7Km?jH"i"L/ibIk>3Ub1q=/U }@ˈu*r0F@x ?ooW^I)*e@9[SGre]ŷ[uRzdN 2WƔF*̯o!\qOTv|SWaJG("߶O$T7BߵUҼ!6b2om:= QίC_%E2 hN>ʏu.\gIq?cDrf0wi3_$8a9PsC-dR]{EFke< o,t}{83'#1WMɈ_.,+`@2ڗKopt&*Jo1/ä m5'9/(o'}$xSIr̴rj8yP4FEmf!KK2;kh$(J.!dX2X (taVY)eJ24c '+\굉vi[ŨpCBjl"awrAS̗j`% Ӿ~MČMqXF&7 QPs-<ل(!SjUՉIn훆_*Fy=SjA*Ĭ(SSڊ ֻɳB$rA&}7ۺC*b(P_˺оAT AKUr_P-x2ioicwLH:}j_(d,'q'e:f6I k4Sh_lPi\x _!~o_pw_)NYŝ jTFį[8Ӕ$R CkOd4Z:$'"cg<[Wdzxx}"E@}H\@dU\V9Ϣ?nUR&5v_.~:0+.: F%}ow\o2/cO]NK'QVC+9ž~HX*1"4hlvz+g+rH| :Dlڇ:10Mh}ۺkVҙWٺtuS(=36ݏsGW<2?O}LLhS.MpCEB[th}l4A>cd/C:8W{&bAC<-* !ss~4l{X-&]UGCd2՝!r _A;zx3 "TX泾rub@,T֘2 rj^Vq2}0IE+N2{E;yCv|*jǘcoAu|mXaA$ԧ,)|2~ͻ懎PN-C5 ަ#݂,4 d4rR¥?5LGx< x:O 0MyHOQ"&i @RXgt[+bT nʅO 11a3H׾|UTHL.=NC̣H ܖWo4 ص̈uhⳈ)]A(NqEUEj0uA NxB\jcW ROvG|];Q dJ 4)_?l{髼bqjǴc骁l @8Ӹ+k|-@whKM7.ʾ 7aSMr\Z1eQ6@<Kf3bPt?0SAO"yjO[xE=7Gmme׳ rK;哺J8FӺJ|s3z8vx"j>]v۵u+5ahۗuv&Z*ɒi!>6u_vgxo*DZz-&CCL캏,Wwms:<Kޜ}ۻ6T dRLWI_~RN:*6hVKYo޴9Jdqtk|ÜCfԽ*IqLޱ#k`GsKKVqTG?nHe5^xp(3$T"NtI7ch;Hy9>T琼*vM!S*˰ tĨc^um}( RAD:)r.ti(~=]p%bu0혢W5,GJZj^gN#'he2:V2rҜb^q>m,!v%ы8չH3&5D -ӧd0vnĬcZ%,_-_`j1Z\HwhoPD9i 1/lWlA ] Anu3j%yh23~Ib2UZNOXd6\fݬNh-2bjm6!wS*g]}z9U$HQlkdE|J@Bq]74k_Sq1g}G<$tqʽsȜ> Og0ӥ% 8u.;d"#Ŷ;S-$dj^g%'$Iq`Y 23ەe/x)]A&i:jȓBWTލ5@ތ5 1SLp{y{ PrMh݌wc4:&M$gڢ{MZ |DĦ2)WN$d$BPx_3> C-!A&1pj9MLX{6"{ =QtJ 2՞v6FӐ]j>zG%x3ɘaP›^eLQ.6Τ9͊ZbfJDzWkW/ewoXWw"ldcma8]ޭ?(뢓S}~*Rs;iKZY^X .5S P+ Q[EfH::U3iScu }RnC\Ui]&0* RSct~2˺ y=髺 -?ldSMu_E- $ fvwTez,Xz-+DHmE"|*.bR0R*y=Os?UΦEOj1Sߺ1ZGYl ѫ 2\\ILx6FJIxŰwpU`3{t↘GIr&|1VF3T9vVR cKkBfż6:5̰aL8ho #!d-uY )":MEEȸj*JzI2͞,*)m5 )Z\u<wʣąo&vޫ,Cln^u|C/PUb M^RbG nspT. 櫽W~N튣tpK@2{d*e:d'&uGTZS!M+}tX 9LU^djp=.'f7 S"3ӈ1Viy&q}O4-0f: GD»9!qW4ws^dTČ>#2|6{ =(㷒)Z*gP@zsYNI$Ǯ[}g_Y. ,I&&Ԕ=|ޮ07*Du~e;1։GJ*)3uMA{PFg1_^@Ӓ ;ƮW}Jq2خW~g[%&)z\\|cx~JmG~74}S-F5>޾"ʜ1UN߼9ՖI0un.no1[:6Wݾ_?J'7X£gȠ\uM=IP:12 |b- ;4lH?y2&^>g|rLpJ*I@Y &SYo%db3@3Zɞ bhЕl@oU+h;}u|Mq7Ќ{rT0?ɲ][KI{dSM-.*hr _M3Ex7 43euH~e24ߵ%C: 43[MCs}@HP%R%h*1 aXZD_AKp|B聆LM1[B޼T 43U>*7D/(SJ 4ŲC[c7A6dlY8' 4~.,Y\E=m9:WX%*iqZ\QCM!$ߕ%q<ƀ B(KU~Pꬥ2~ʳǃLU0r͍k㺊Sg >H (g?N yɀ%}#9Rl1nY '&j'sr%.,oZآ a}9D*ZRBC8N*_oWft8GW]Wב*4_;^7mRh*4iy%@ٺ4W%Umi5taM}i*cw#{zb _XooWy2SK'[Ad|O.W{D?_KLw^QAO%{OW} "_tL {|3bf2fkaYC̬2[5~s -{+uQ!me󫝃S'<֨0U6-`/)!.u*`y`.z KiET<50z)Og2_#'U&"sXfF? Ru27qҋ p- S;e5]V|q@$ry>떲=k̾b'fpSGJi~E h]bU67gyZ"]Lr_1Ycs{>GyI]%!E,6\ۍZhU?GQJKW<~Epҙ/*h|~ UU8I|ͣ U=iZ,_ L64*=o.}_:֮y,U0Ɣu*"ou,U/~O:9+]0>P͌~R_mȳ2VfF6eӤ'%=cP 'cJ ? @j߭K>F irjOC=GAJt#<;| cAǿxfmnjs.d2_efT8C֯M\a25 dk8i D$O>/%ɮ})ӯvMW}e**˼Q.b2,s>;W-审JQƫ ОիX95 8~SKyl2V!Pӌe^VJ46! ;TFͰb&й>廼bMZgYh)Tim𮳝a[qxIj1,+>Yۊ҃aaT$h! } =v3ȝt}URSyLoW!!\̬&pmVwEt~BsQҮE|}VCi1* H]K*v F]0Zry JTAfM`:Bk:SG]f&dfw5Uw FPfpɰ\]BƎ "*d״#JpO*wbe/ B)襱$h2/0ħeuX|It_˗}'n/-ӾʩH򬮢%յW.UDi ʾnh`Pp &=T-o"?6r)Rnog1_}EX~z!]ҤYM_ogP..X,. g ;*uUpH#֤:^ +(D+Į^ԻXΩh; #(dV rqq !ntp$VeÎ( ߧYW? h%wBXdS$I';i(+̥l|]D2xYhGD'񶙕SPGޖ;VkUL3I~cE|W#l3`5<0 +5h͋%]m_$$ӢR׮ihiEe 9޾]|\Ǐ)QT <Ά o1WuDXi'P5R S`%ub@3ʠV7#S0uI[+Ǚhp3 1O $.J\2 1Om2+c4,!gxJE~G] &TpO8~Z"fTG##eᬎME,oUY+"vJJ[ZzG:68SԼnx )ڌXR"1e$4J`>VE6omՏrvt=u1 *d*0c,S:WZzyHIZ"fZQ&q3]!fZ(S"E|ʓFeb&׽nj/}CL5' %K*7ĔGM{+uyh>ꑂֱgۤXL|3 1]I$\P#&R0ctVd;bT__0K!3~n.U&"BThTM@ 1}a_G*b 1HT4474c REfv0ӉJF E]b׊iҞZ=fZD&.~i޺j0 3X)O/3)hi3aH!a-㎓:*=TꚚwy+5+< KmZ׫]VqdzCVBx;mĜ[Zjw(2CK^{a)݅y?,UÛގYbޥ/HUXv): <$SQYgrc(װJ4JYb~>K^ =@qeU\^`Wg&!5(\M|^KlC2 wXW9+~4}u.;7j 1cPc/[orxg^ TPy?e8_D}CC59K)Oޫ˸YKG*7ϭrQ*(9H0[c40jl}L% (,=װ*M /%,n;2/H02eIȶBL$)P8e*Uuzl$b/D^"[fy*#]tdnfB&LE0_i6T[ζ^qEX)H(g'eđ1L 2/*QIs|\HO<5*'%o;laF翌g34X!ChNV!f‘88n=4Ĵme+QS zzVWR=oEHۚ:`_PlvBZbʴky$]`J %[W˾3) gn`2X1㬇^+cSF,^ ӤeLlbR![54 1ԉb5q Wa'?#fCf%bƓZjj=Eݨ3uzW_"< ͠8 ٘zp %F6NyYDɭ2KvAl{YX<#lM{y>.&DHYԵd@TWa[ 0ł}<|VʹL߿~" dr=>'} 8d^Nz|^nj3 ::?EwӀh/*(ߔ{ʗuQMb$0"Y| *MƑ2t뙕tuЋ 0"ھUy6}]dĩcq/|[WQ`MWY=_޹ZL{y{7bC*E}5pLCU.3ݽݟ&Z#fIF)fNN4;>fLHT^S:>2N$]٘ ACWmSM > 2b}'Jp,ߕUԮF1ʏ}wc{7 7.jC!.;3LvEWQAOKk5L ɲ(i2<4I fjgA aNhGAK&f_ [ߤ6=*h&I]Kz_A3j+u7-lXag8zϴlgXa&z9,afԱ4w&1ՐlWLiV0OWlG}02$X,],(}_p46~3}!\6.;flW*8KphJi)/'n_-@hʾ]VQ풒?;坶 ~Pl@V1l{(~מE% ?B,Yf4Nsws}}О89oX/&,v']kGQŝ:3FJDR^\qI?$GERdUD>O: T)>k(>C(C:\c^7r(q{w6 +X/*275J-tĭ.gYEA#(}_:9>K q_ey~ZLHBĆ+}^w]^H_点~o*J6Zq}[WF~3|WW9Ջ8/ŚjX'R'>y_Rr%Z䇾 )я2Dk( }Jj]bxp{^٣L}jVtTbϖP_iTL_IzIb,^_d/AO;*PȈ0[u CEڒ*T ɢ sXX\;+TU>84e(A)&U:ҏد0I)b): _JWd៰>T˰_F f֫這lhgSDa2G匽*WiWqzIgqAfcpUh9">RY]4q^Eog~][M)DHP8ug5* 4$GL 4mcղR Дlɐ=TS<=u_0g8kIٹeb`/_NXoIFΫ+/fkчZr̈́JI5ِ O^Ga9vq7#z7 4O}򘾧Yz ̌SzkZyړ?N|]Z3SYg+f / D;5 gP^fJ z_bD4Lv mp3,8higE9_ ^25,c4̔wxxhg4bbobC:fF(=**_ niV"ڟSZx(T5@5Og)qxbkzµLCLW; $FXU'46I\X<;r+O8Zsn-Z~VKP?Yu}էq.ßGM^QhTMm_y=db `A=""=q׿諲™ۚS' }]:yos VI曾ǣxLλ~/U"^G/fi_ r-A^!ί $A*8+-C[zLN(>`WUF=He< .:oq_&.l@4$*g'&t5Q4fej7yX±:iLJՙEUdxsù-I1z*3u~LCƻ` 1S?r,n j|ޏ˖0JndK_M R./*1;аHǡwq6~;%lX^T|jEWWUV!J 1灯חm܃64yۻ$^ 'ΫM]ۍ)Ze6~īU7ູ곤M /]ȌjhLe{Ѩ+ed!UU 2}Q-ZvXŏͰF_$ն (|_mBeQڷXF?eڂb |Vн+ۻ-[(s^ƫi ":޳^+JJ|nc_\/;fXdEVq[M7on]1^ۦhѡՐA79($2L\U).JεiRRYĮ!BPXi`[REp}i#[eLXΌMF@!Vն'U$tO@OhUO׫8MLmg=,Ĵ=JUl9e*˸UIzN|DHF HvDcǴ&j7dFPD#?f3 0= (rojy@E\%ڕ3*i'UCLjjJ&]2W{r^B}qW!!W|?&Knx1FNg'345T1cS]:QWc]n|nb"כxhdsṊ^- +BI[[%ũpmXt2WtO5Գ|IŞ^y~/1H*xチeI~-%(G_k&_50bHz4t䯃4c/Z&ێ{w#[NK.cM Uo` !0cr4~tADWJ~TzZjy(&Xe -@xէ L^W «b>8PI+ v%Tu5LNˣΌҺ͵{ՒDVkC7 W>͓ǿAxieL=a^WIOfŋLzXU0:"0DےU-Ha<1b:;ὶ hCJ_՟~[w4GEUxz/NqGUTb hUg0v8%{|VNPMMcOd~4S5]OPgx6 _/b8݊=~IB2)jWn:M CoUlľjw~Zqpi%k!F0|n i`5XW~H\RR &%iuo42 '%MA"Z傻vnxL/>_ZG:T{|U2vZ KY[E &sd|U֐Čτ+|]qY S!/j-Ǟ(srr2iUhp/03^w} w[7mp8yW2 sl,s_wSE[q%.q_L-%~S-Xif E$ؘWn]F}TWv$[[ui︧2^)&?nP4=j"2e,2M*u *!5kPt텨K4L]~P$uI}+X܎o=Tb NtpRUёq-UMl$-kK|^DV: iPyam2mXBagJ \z"x:.b@xn"$j Bb} 7|cjG ȉワ gČjK1J 1y/?ӑJ/ʪ'm^n<]ǺV/YO0_D`mӽ!桾2#S6KH3)BSCJgJI}&54hd,SpӚՎBPk9Z銯[oj}IQOc A|X+6o!f<z 7{KbGWE(4L'5r if>4xLѭ^!na&O1]=IXGblxDQ4 HVr =Bh: 4h6(Awr4#LWCW=+"/L HCjn_o +{Z5%IJEZ'eLJQCECeU`Ox،w*a ӧ%3UZRxi{yE;jPw$nIY>l^~3f4QY}؟-ØͤEp[+^哾ʙ j.O*\d\)'}c& @F! )su\-Xɓغ_UhSaE"DO*8EMZCUbiݎ>%e_UH3=Ԫbu]E%,qpfg EUDQşVm]8#?|U2~(S]=N}!W}]&$ixD'*TtF6.n3 s+PKI#*!P7zD<)Əj3<.[Xo-qt`:1/S"cԙ rudƛsT-CԈ"LyluՍ"iЋjˠ5*R~}l5JdӰHsB^t4 (X Ќ9 yT`$btT(X+od`j~CD(~u],+N;QNޯKiRC:#EX_eawRUÛD~I2cM-Ǟn+g[;h9 umU ԥ5ٯH?S U\CE|(\$$.~&!aJ4L?Ë~/?g???~T/~y~/?⧟^?f~9/?/,_~8~z^?{~9?~yO߼/?o~| ow ?Ӌ-/=w=/oPK^_d!"A$H3皝>_??/\_w/׿?_o~o7?|ݿ?cww?O?7_~Wwy}ݿ|ː_~Ͽ_d~~/ǿկ/O\_oӟ~O_~O<] 2WG1t3SWٌ_vew;?ow{~/~𻬛׿GON;7k_~ߏ?0?g>m9F͵<̿k 0o^d:{?_#_3,b_>d9Ay`?+drWoc췗}W}}7}췷wol}qq<8]_xO<7? 2N?' =W="8cXq59ZSxW="8gq=)j\x=":kq)z\v 묝:kr)ܬv !7묝b:k r)ܬv #7묝:k@r)ܬv %7묝b8kOXr;)܎t%=b8kOXr;)܎tt%=b8kOXr)ܭv%w묝b:kXr)ܭv%w묝b:kXr)ܭv%묝b:kXr)ܯv%묝b:kXr?)܏|%=b8kϧXS,ygKY{>Œq֞OaS,ygZgKY;ŒuNaS,y)]gKޮv%oY;Œ묝buNm9kL 3WSD׿ڱ__]߿\N;r{wuqgm3xsu}vAn.޼yx{qyqw9f~7wW7wo5Aۻ7qn·e77Wwk 61._z{68-鯃X˛ˇuJ>=aٍ1n\]_ߵÕmߟooGw,rj܏m77oo)]^\]-[:ōzw?/q3rc&7|n[|scwF}[c[ͥ7voZ_tww _ǂnMWkQTͳ}4^,"7W /zjo"w.޼}Z^SE{_鑻nj/*h[|̹\Xu 4bRƱ_FpF=8,NjmsAFW'~+bsAq$yudD \9/OOj_ܝ o཮]~`9?o4r,r^g I=㟞ɗe&̓WbD'Z_v{1ԥ?9m[OLks$Da8<xO~8iD!?6o|E]L*QMœ`tr;^tl5#/m2)(;F)xwqMļY'7SqiX@֋u/']䩾]ѓMt5o}ۇx1JÙĥnb,Q:>Et(A*,<iY* ,@N?NxR+-op+!U::2G)x.">իe<@$pAx,x s;] Ђ>[\2:u]p>0({f?{xab㰏a f2n[Esp͍[s137;tȾ[S;bE<ŧ7\$9s0Sц^>[t'J Ӳ ½D n!K ?_ZSE=CG*2W|_T\=]vaytWFAF"shi}QCFFܰ;c] 2zQx3 q  }5r_x˜KEF< !lxRzAmPMs֥ AxugSGF:.=#T:2 aEz*2ף<$ud(a}8_T 7H06d2$2Q"*l 7`Qf]1`|# ".?l<|8h.l<:{k/qŎ81K$(xOH >+Be8MKd x$jcFIxIQ#k.(^Ҷ68SV(6uҡAM$S(*˨cۋZ0L:ScD\J1JÃcV<kG;ͯ\d/ +'W'g\Y#)Wy$?1H8I6)ޝ< xOu&s"CBT'YTx/Oe 1q4^c@dį-e!).P՘t_rx;YX`P 5EWb T0/@*B[PEnP Y/ ٿ\ʬ[!/)0T ';}NJ^~N萭58D$JV9)},x衤4^cCaDOK~8.IqJ?J][T ]^.Ft*^fƏ:l hU阈bc\R=U`rב+(;SkEI\feJ|TaQ~{}_9"So{uc?"~Nz )~ך|^V% \/ \Y}`XE|NT q!%Z$Wru$apӈe(SrΤڼw|oªNEf9uߵAА~-Z劥L6% B^).ll?AڧťLկA̋PIz9S""n*2KjEɰ(mlrܛE6hq)c.Rcuh0zR lbv-O(H g߳RWuNbs1 0SF@ߟ 2gAdNK|.J\b[VI`1JAƃS gnf>ЉSc _R/b#ۣ.>]H"+2&!!rZ##ڳ YwjJ2y @7w‘熌#jFҺ ).V0nO# F[R'Pî+{і_N\+j`s͸i<U6'`h銌\,2v6}~.=b{R>UX庋eT.IaEm5i?/M姿 ~/>;=w Uv ;-iʰ$ {}U);1T#^m.2?h +`VÇuM38GeMΉJ s>qJ|]?cD0H;:-' Z3AO4|^5} | 6yV$UL9ɗe:]Jik]jfܬ>uDIz"gVT;rRYcI/5D3+]y,\ };k)ė^UL~$>&[vuBVۥntr7z;=+V,aIqf^s [synJ3O/Di@aQ め3Uy )3/,,jsAF`7 LA#KP8cUZ~qU^+=ҽqltG!$X0L1DT J,P}v)|`huT/f]DP.&̊lp5Q.GbJ/%H7wr2U˸po(TPg|<L|IߊMi<_s@/<{x7-2|Q?VTdc\S9(+}{kt)?u\^-hi }{B\r k7VJH^3`dccBnC- ѕԋ/HQU!Fi-xayy*xK/D2ҡ1ն][47"$^]MIF; R$hcFT))\ -%7S*Y׀5&z$=!j|QFa=w.V7Q[0EH]`59z_[Tpg!mSQ}w.nE&!۳٨d: )q]yXKR~h μ=e2"W;=5`P1.#)-d=WsƃM̓wټ$ǣ+ v[^TRSk?INkchyi=z{c:\:0zkǺt`t^ϧ~KF.:si̩!((:;'\^-4 J(3i 9Gr)0l 0>$T$ϜvI@T=*R/4.h|yJ Ӯ&V4sFLV CYұQ*fX) ƦL=畩ؗ_LO+:aGHiFNbZLd^hTYXpKQ2|QqKi%y9_AO齥2<2aJ S֊{)7.~r4ml?RZGx첫jGn26I*̺?{Eߪ@ɪW+'mSDaObE2͠lۑMT? <>Rpd5vY# "|!ܸPNJ!2rJV+Ǣ_ZP+tLv3!8>XS󀱹4f^S|?dWdIqudT_^_Sթ:/:Dzcxi)a{ "35߁{lkr['ÕQQGe#P$AÐ`O b4W|>ȧu&^g'3IvZeah&H/ "dg/ \ b[+vNR[֢|wn;"2:71qm}ٸ`b ߵq8YlK(/ipjCh(qiCa袴sEJ#-c>7 5$`^c._Q#Pө|FtJ}UϋS'11lEUGQMO17V\O g9_ompzc89{c.=J?DD8+"vR/a](Tg 2:_(:b˘B)\8ݡW_񩿘dPFyўu:٥GBdUG0p"c@iiVp'b"d/Y |qYSG܆)F $@$FMZ"cy !ye ", _'giuQ7)h|a?T j\CI<wT`'jwd튓>ƆVeNUGp%2R`Bpi{$%J<6<,4w),:~ ,_K~6mN5<#eg=ǎa H~<L)xqnbrd 8MŤUAfnvҲ ats03c5d\m=O 3*Ŭ6R5hMHުX͗T+kXXXRjd ;iBOJz\jyȦtWO|x:zOޗمRO 3mҽ"`"%03F;T13W!PZzŢ.;I+qz+pb8b\4N\7cGK[uR J:lyYJVsYU.N| dC&BL1(#J5K2w!ťڜ@`cӺ2J@ s)\ \LN"f)1=),"& ,9Ra6|&z!y` :d\N|?%߂_%;`&*EО3#f{2\=*.f{C_վ.+uy1w_/!fFҾ ^Hr6aCa%8Xp>(("Gzb~G|XTE{*^؈%}EӸSܪئ"I(91sT>.-Ҽa.Lx)?sUڏ//Jtmqaծb8S1c2J&<4RqtŁ|]G.#p:dtֻͅHI8I^;_(H:٧z蒝Of,=N}$`B2XG{R+;NO17RNYa`볛xS"'ĤK(I/c6}?"Sx G9ůr#)sKR^EtTN1=ҒZb)˻I/{_fިV\ڙ2=F3bZ!hTfD[wc˗PiXN>6&uy6Oa.s") ZLAW0z|n2I톙?W1J6/])R25ky6oѾ+ӃA&mRxSaudZ2HXTdL* 3!K\!3&/˚׎ŖD y:dr;$:7_ /;&jfdS̃85T O'.~>S+%TJ1S^bUKC;Lr\S 4' C"wE-"ੁfڀSf*-`JR/,VA9+/umg #!gYlznmb6LhzяI(d 4)"94JpMkCN:qϵSj`;Mݗ5wtkXnRj'Nx %nQ1^ŢRʕR%9y]]Aӽ%;kc%7rY]NQCP:uxnyxA3TYDŽ.Kใfqhv4%YEl_f\!6*Q(|}2_W߾~7wT`d=PiK: `پQRWN:c]>7 XA+Fd/(P^9@Ⱎ9K+wZ\X|D'36nHϥ"L/y:5M1ʷ6*H&BZE0w}$^y?%'#zXGIt4u/x>O}. sGԿE ly"0O΁O0u6{ RfRy_>!oٗ > CN1\lk~J)uߒ>!d): ` o|Dԭ4<K* ` B L1vLّ\ҏASi%ي76dsx=i ct(jeRU-9UL~r2v5y EB b7oGfkh75&}pgBe˗)\3kH:MJHn9K7=LUt&7VA&@ʈZ`0rpE6OWaca꾢+fJQT}4DLBp>1\JW^%U&f.cc4J*wAL#U%8fS!S' P>UȔr0jNYY 2!SF-WoW \ HMh؎ؗub>jCbOtv5Z2O?LҠ#V1>21ԩ]'"4Jr0\Czm$U/zoLB&~UTtbHI=BR =!CAۦ^W r195KOEb(Gra$3IE(*piH Mv6J̲|VW6 樜/qa:HoF!ئDZ0T_U=/NG=N9hV: ~B>Юo*&iÏeߵQRSɨ^{ `?em:Jjx-%ZKJ\R] 9q|] ikQOJb`5p*slU.)^#qќ}0autε)B"q?6!$1+ΦaUxNGo* "E/P<c6=GW-rQ0U^fژLGIð8aRPQ.ՕWQVF_m"au_ v۩_\5 7ǔ}H"ҥ;R2J%QAKƨPHJ%75<*&y[GamKsUi$bJmh'**TWM rw;KN0n8/ }\Pp[h&ۍeȚ ^ 2-̆(~Kt3S9#yGu$nڒCε!8ăj 4 Na7Ll3xf f=LI?CÕU1'=O=A)(EZ2E z_e6$&Cr0tb _0 =6Q+焏c%[UÙJnN5/X+*RN0Q)s<;ڐN׍b o|X`uzDOM{hf@o_W\5# FQ> F@muP` dC2fW/%ؕA523 Qg 'U"[^ɔʜ-E0YGrƆ~SZ$^4`Bm}ΩN-wuė *}?tw(/ޕMq>ʏ}kXEX(?]#Y|Y1I~l PPɳ>%"Iun yn$9D9XёGEKBURQX8xBrf0G\tlȔ0&$a؇E7d[k>oW%,75KY'+ ^QQ!7gOC"c~HO[XnGx SaR`R&(Le'/0o$~T㤋KӠgW/vr0 +>KLZG !'^ |XT; </9|G4L99v_)4dG̴4)>)p+%͒DX[(uae/zW}X%-!lwQn342QGm؟O:hTPG4}*jаq?룄5_y[\6 f(_ԻxBjv=|Y} 6[9j(JŤJZAy&#v4AdQK<<"ZbV䪥Z6oI[kӠIn'^6 vGad)8+g<5K&$8׽+ 2EHj:
    INEl>6ϜPy"4Z|8c MlHrKf]) Ii6qUN0Rpѥfl(֔)bbSfT%+U͚'iprWkmFk/yUPLk5cV#YZXAS,4.bJSi:ER|2о^(GmPQ 4w%ޑ+cmh(M&"h2>*~Au!>''ˎ|:L˦k̤b.ea1Q^P|V?[ԥĕ*łt4o99cwI%QjSm)WTJf2[ 0gl2 2/ux̉s鈉)D@-9#&/m̓33)ܦT$ZrRki`CHDsJ*Bn )m6+ry-KN1:Ϧ3o*2Umd% |w8,6FaƆkJ;:>K %)[ `&-SJA's<xB.DC6V/%v`A*GM)h2*na@Td.&#ݓ9k@Af9!b6f'L(9.$9S;Ǔ 6ipsf2. \2@EDXÜ4UF3]go7E}>L&vՌ@o_Sw_ _iљ4;+d _e[zƨ烼ۦ"h8EgWF p=os`"h&mEb|POW|tXׅ.yYQ€yRFBςoaC{A/Y2t1a>Jo(EѽfAVn9~ɋ6MdEE`%#,n.0 tꈍUf3vIRXݯ(NȨԒ8 }d$|olz1p.@Gr~x/X( bPlEH[_c[]70Bq)G,TFjAAJx{=E[w0"=3-cSb,6?-)P4>VD[u LK2:6 !|֟I !i0@r4qNݫB l,춺XDjYP%X0UaRejQ5 d|#,\`W 2ݫ AAo#b^d%T2^'UDd=eUmXyhlL1 ^u6\$kȻwCbȎClꪊ`2XU"bF ХA-"w#dWW9|(Yo̤ݰGX<4ȌW- Dz5.^gTy6E?Gů7<@0xD2:;~ 0LQimF MY#$Uz$`P2&|ʼn4L~Rg@Ss+S/[0&)a&3ZI);efr۽t%kB!R-%#tR!mݩw/xQSBp>5quATl.Ӳ~ꘙS fJ阙 ďaN1S(e. 3jv|Njȍ%5z'-VWR(ve ZR]+d>kGơp2a 4NF]Ǧb` U*ܠ(ԹaEɩ|,Gُ0i*LT/,>18-Mh/"*u9ɱ*_m`]+ 8ߡafU} 5D{Mի4M#xOjn|TCG`MvAҼ(C~DËS^N+buPʿ'}ͩHp]lv$D_ZMի ipjh!lQyGc2$I `)7$fGW) cDiʓaf"5+XUgڢv:K s3Et $͊Ḫ@od4z3HvT/J鈙'>\"7+b#pt+Y3i<8Cz}mh^ѩ|4ʇ}]ԛt~Fќ*%IUF9_ HOBO(Vo:sy$ǎrQDHWgDU<󶺸=^8|FI9>* X76 &My:NUϋ$x&u?urȤ _~HR=_o[Kgx{RqE~vvEfG~BeIiuv *a/?RE ~]m<Ya(0OM-^n,^gf)9}bQhU]rn<9;KKꎬpEG/PQ@'B%0ִf HF:iB5&h%xV' I8@*sg*cCIi Gg|TTy}UϙX;N&m& Xc2') mRc&e+Q#9r̦!yE+ZQя *\ !o'>6<8v((9MXRYQ?֦呠Ⓕ5|< 'h8{`оD(XfIdž)_FVILvt,iu6OT3 _inÇRcRٟ`2ib nOKAqyM8t3V!.:%膘/I.R%}ulIsx/pLn'qt;0ӡ&MjΡo?`jIQD8#bIr/ ISL)7iTG)⠮мko1,$TY4|;1n s{YzꀙG㥢ݬTyնy᎗.u/2ᥪ&ܜj '*4) [.!d,MHV31gJw<}nxbei[.PRj*=7dQ.|熗GK, Wo:K{](dwM玗jI|}8{*׽!|.QBȞOْ>JQb<׷"Mf }VWZ}k/(iIі_eţFXu+~ ӦزuTM[-8|[:M!R-v.="C~ kD=c] acʯ&_x2Lǝ•K1dߌ4|䢘ş-q͜ΩkmNP2&j߂JIp2KO¸%6}Vԝ$PGUhE,"%cA*džWg^ VcJ}0ԶX{lt=[ -F*t0ox;R:cbɋKx툩vu2섗Ӫf<^حyR]MO:{ 1 a^TCluxc(1UbW+ GGp̝9/R6ށ*"eg}lW;xam!Kjjx$$& +f勐xD>5KWu欗%G FVu~9X<+uPAGsᒆAgb$˗s6 .oڂ8Ӎ°㿄O\1"D`FҶWSGwn>Ci_Z6iZ 2[)!ډl}G&!fW&@Ptʩ}a_/13mU )I<ɐ8r2U/dXT6.L.7+MPyXKS'ծkI*0%wGE͓Jn&aR P8331ذi0GiyhMal@RNa2;1-]YC*kj,B+u&-TڜNj{F0{HcSD {~U k}~)|)$IZ=(ɦH^w(*JɆJmY\ h^~"A/O%Wy@m]GyxuIE]w< QQBΎiQNXXR^?uGJBQ`|OTXI/I }/NE 2dҁgμ|+UMj@(_Q耂 8n5U=,.hEj7u_fu:oxW!G?t}B]"LGإ-[W=s8t6s<~9~>&v{x+`ReS 9]򺅊J"䳟곛Ұ'=KD?,a#Lp~ߩMWtGjoSLW|[U&?0AD3u+;a(E,֦! >,kDU;J%])v&/F~$YG"*:K2h0H[p 없i<~Vύx"ZJ.C*pi,^;TbFXґđldW/|Z{2|$`_=G2Rz:gK[%5|, gG1EʱZӑSx9fc D8Tv 3yLȨlbfcb\ew.b&M5da0Z%3lIM>m 3#' !z&~e}fltbSlj"lp*,2aKo$8mLUF^bSr!SLa%EEzR1fZ[L(0S֗ թz)R) 䇊A9ɮσ2*fb̔6IiRK~5,}DxSQ4bYMQ^ mSXȵ0QU@ک@Ȭ8秎gXݣHy:)U} OW 8Zzj&'cW*FhTEMAaSwg&+ Z\T!XEjoĩTpC̈́p1 U8k_'fBÚMC1klLɊ-u;jrEz (넠K#m4#(& 3+ _B/ur } GFfR l1LMiR:K* w}/ Fm픿 ?!άA ۝@9WM19Leh)9Tkۧn&AkO6[dϫ_~巯_+!wT\ģ6 N$$pWGy8ݕ^U2Usy~ZC:y_eu}H+fk֏a]{1P$t*sYB@z4>.syqLhO(,jGID%VDg}Jʼ+G>si䯲E[,6lZdLѾN p_u UPJ0M"`&HTҿe${8t#%8Els1QDR*=W(J/EѦb: MM~jbu` SGoѥmyC$d Z:SF% {-0BTAL1/}Fu3,X{J aNeiŏ_,,msJ˜MLwA5+Is1LL! FANj"*c6 0w(_t*6Սg̢ /k6#j^dR> Z$vV3K:AKr$]=K9Z_pT#ɦX _7߾"|-w</1z?fWD׃Łۦ>K> XGS~EG/cV$`ISj*.eZ ,m60^hqE=g<Q!Tc̣<,U En%&I .8 |>>R D.a$UQ҂ҔMCR |ULyu'a,$K')_ LڪW ,l +΍vu[~ n p$),be"o,s>JL$9Iߒ(Xx-aȔz8*%f2L=/-65LMSk#;9cٜ@fjG/`h΄WeyhqH4SM(JbIEOId?piZAC|x܄?#Gf };4”2r@P(F/K }T3?` E2KF&է 犸M6rtujJyʣu %RV_agRC -hg.L'*)i_ 2L3f\Ojql.pH2TR.µ˰8˾}w1w;֦;HyvZ}>)MMM,S|^m;NqAQ?2^ #L r?Ƅ9`V~6U;`2m Z$ZUږ^y 'xCEd.#}+0idbXؾ#Td} +_\- iss5OQpm `*hcV?~EeȊq[9L/aV킕խ+Ruc*^+(O: ^}xMlVT_1 KN:VjgR{! .u!|I't5LCA@A*#&yk.O!eJalflZuӕZ@!7XHe Y&$l'{YAea-*J{<|#Ɋ$cg +,D* XþhD pvX%"뉄upF5 KK6^#,Y u}ɇ(,3)|u>|*_Q> w}wgCؖd!wA XaY4{Zs#bPANʦ\ڑQ_&V9Z xel8ue:JZ)PDI2wh*8#%&0|ЖPO!'p_%m7YD+;dg`\Ta33I}/MʷOEJӣ[$e4 Ӿ!vO1) 3$ƧixyDNLHTW uui*:M3ұ̾/U=QˊyR 3^d'%:D4T#NuPd"τaEXc ։G.8",)dP+;gU4 2b:HL}֞C0j U&۔otx[i%]N{W5CO,|2[T2WHWA#=grWtm"m ;* ρ񜦯{i5O /W]F;b9eۊ딇L0Τŕ5xTOq[%dI/uJ4Ĥ< 4Q iW9 h!m{x~↔ycҾ\z4]}Btήq ޸wODw8x[e>715 yf4Ӹt uSE%S-2x&=ۼ&ӏ6r5c Pcf|%P_e+%BOm13 1t(e!҆Rxbz5]90W|S ̧iy;Lߗ3M _~gԗ~h4V2evq:dBxI稉4l[˜dOˑzPq|K5DˠNbudcK4` [NҫBM.6Jgf8KXq 8UZ!;v^A)+r羹8ӹ5ka Lt)'SUxo歳Y{7L7kOꫜP_ЋZ4L)YHcցj)wWED$H\> u}etސD۴'UwkCc7̍%5[Orms_W/4j_E=_KN{Iy/u.w*2TEًz%쐬7n$D#"g*rw?iqF;CBخF Q;U.uz}^/\*~$dgW=( S)]$ufYb7Be 'GɊW5=";5tf|=u;#*P_>Ӕ{Qcɔs+UFF?;:a!9wfmJV@a{,SRnEOD)Bb]oyRw|U݂hb2U`J7dW{TE%tz?:`6233 s _ؙᄎ.1e/ugi9! #ˑzz?uk?ϘD@ eX^+n#E_dr <1B1;:)رoGg8io_#J0VQcJcvw*I)Uj"9;UԋԝuhmRvN1}|xH}w<Ӷ]Z?X*nD:Ckʫq"!u +W,/!$|#W]%UtS$f^BDY# NIԅLzbB8˺ʱ'x . X2[W#XH[f(vwNk.b̂_5@|_׉wuѷQztGeb]?u%w M%@e]W|$$a O*A)3^q[ P63c'Ps ҪZhD'nHAiXHT *oĂ4ݘFp%z9AW%--jnb^:;C?WqzXp />)s4hyTB> t!(`2c'uDY 3e)XM˂L:1$@S&n N~>iilHRJ:@3\~4S: 4%ծdmh`M4Yec !#CX'yJ)~1E 3Xļj"?d=̀Y]c. 3#ФN 4 2æh9`bdxhz> 1ՠJG GU%Am2]`TCL[Ƕ{S̛ !IE~]d H`m hUTWOH2':b*Gte;bfJwzB4 1k_fC 1wF;ᆘHdFOhON!Qq\3*G7<5&1^'yvRen_} & kf_ArrU9qx SU\ y21I!dKuU96XULI̕n<"YTܢ^d]t#`;dc,ߦ%ɘcf(=\`&>H=\$,/ZTQ_}o}=yq|'z(ЍᵶTaz~(U2՘MșX׫Q_H6\ ;Ͷ 7yы .8@$ i ȉD}WɷE}=[Y @-[ʣ%?bq~_39ot}ajH}iY6'@NU_tIN+~y'#QE#}VAW5>KL m11]W(DiƽY}#ttyѧL]X" gco毙gq_bDE&ܫz|t`82Ua MXDZfq蠣Ml=qLUSV\xWqjҥf~GTL\CHk&y4=D]Piybbfh]~4h$R~-r`i١Ɍ#VK7U6pS͍jw *]Q8R&_ce*4DDak ֍e>H84&V+ݧWѕ$} /ϑh;Qid͕$}|qUCX1Ss_%1^!rv z/v(;TκoЅn<7ȴ $^JϷ SŌo8f7FD:WeGY!Q57.#~4 2EZ}Knt?xQ͋dB)mV* vYXdT3Dh5Q_dGy{}[M4IÐOǒ7&f[BAp&IB.*Gih]luޘ/^-֋#jڣ2SRZPxٹ UG.{hpSbD`He9^"&msk0O{ȖC̻2bs i.2a,YTZ,XkN D\ZbvV $ܒ<4v 2:VFn*RP!Gd/ʰ æ2cEH33:EZQhdZ4 }H@K͘/'uR"m dx_eU3O"/KҰGY땿}Z̓]QҲ*_.E3E^WѤVf*| 'L/f[*xA^B(ո.gy:vXح7"R5PSjhUNwV_ *>Y=e_#W]$@5zC?-/lgLzE>lķW(+YyGuQtn|G|x ^'YšSdBgi#r>wځWk,u&S@I|Q~ ѵ}}< ~_ y^mM%d6{UNZNP"y׶U .d:n8/5CRXfصw} :u%|6RuŰ)_c OBNidTG'J魸6-iߤcLHg˷ 3n{OZA^3:\ұZGa$ɰ*ORtSCIm $w#EEFOΗ)O#7"-NnKs5R8&D09%)A`R /28}/&0c$ EXn^Dz3d,U; $8QM??^HmjqNx]LU85҆A )oyE\{ɛo4c UmEWdT* 1&F i\'!橴jA+,oy7XZ2@L>u&tW R0T/S}S ,d1_!e2uPvz7ĄY"MW9 Zv2b2d }r^-^SKۙ[o9xaJ1!>ssq:vmz#D4Fg2}=C7ȼ5^[@3N2g>W$iyS' \HyYQk. ҇ycf0M;fg3RL$iJӾjYf|;pz9 3>L23\o*7_ e3慘!=6_̛''] 2-찋DiO0qIdG7Sa]ML4gnb}"BzQDL_dA)n+.Gi'>B{xr^'crAd,ګ,6 AwQЙq>ߩ`J2JvΟ$XWy#oc鈄D*$CsTLc21c&H\$ȟ^}WWG zꓠ7O{i>ʇ *sHvˇem&7[l⣺[k9Ξf׳z }\I9B+KUWY£z9G{~]&)ޔ^,~w$N@qq_BI%3K< c ]\g%o̚%'h߉Qoˤ{?nBsN]a㪋Tq,Z߿ѓ)>zmae"kNU:ެ &$N@Iv=[TrC$s[P_]Nip%weP$OVUO7wmWe%XkNbj:bfE>T [b"J}xԱUET&ηcaJi8Tfy4b7&j|KTqZqp%Ja}I}#eanEk7 JyKJӡL^D {,*F\XG-*!cΆ54< }S{E%ӑq_5_ɤ]&wTsi2%ҫYuR %6blMp 0՟S=Q:ʱԧE% 03.hɕӘ"WUgH!3Β$OY(d4 Zy;E4`%sf]#a-S g5x ^T~6j .S\<_A^uh f-d*Y{GwθxhI UdW2YŒqP2㚥f :ٟ:Q~9ߏ^ +'L,Z[LXGdy8,УRyeǏF{bͶ !\H:c|F(-g+k]vPA~Y(3H<~w*RQw%:_U`@hU?dDE~.W8^uIk =6mRc tIQI ROU=m JruqtP_O*S9^cE'c-'boګ]wՇ@_u)bRtg X˾ IsG#WeT0qˢ.xQk).j2[q1G=_eMԎ ']bHr>Ym39gjOCBuPQ`ԗvު*OC;F2DPr$;g8z~F(0톑' s;!^!Fi:J14* IҔ"#aKeQs4uqfժWTmt.19lYORnA"M|eR|E@0vHgXD6ihyGZ4H(|vqWR[OUf=%kR;V@18Z-4}$4zKȜ巤,Cj_[d(d8QWlhdjgZ&ϐD\6+r"h%G~ھ&A}hTsb9}1_aeVKiQGE&i _fz$ohʆL>^H/U&:"2kdF$,0_@~]<S^ &&O%Q뷿>B~+~F~o2yb*  [,jꌆoN*r4 \ЛQ2QV$:㜰ҷ Qh0Y =~w v7 Д[V9YޫR\r`,xA_(Yqa/}\<`l+BG ΋Wοy;~+nWAPl\5DϤyNzBPXpY=2l]n-Uo/d([D2}w*_Sw^U*[.xDoҍ`縯S+d"_5e@lɰ2N7JpKVk._蒸:+vX {x 0vX/\'AW '$q=ըh7<RG):4<*\OsGj{Aky!)'e:dJgB"/%ypi:TOi$`d dcjbxrBX3 n^/%XfxzҞzvb+2bK2%4Zi7Io<>M}cu)y)U? SLLL{Xt`^0#!R}_|oa/w?c\r=q^kHcTXz}̷U~bNt}bqļcƼVA-0 Vq M{^YC/LTU~i'0Vny#MV3UPXH^_%Azgx_}Wr\= )YTa眯v"`f2bn/z)!C]~ђ{).׫|\WɼTWG擺 ُ,?^9ΧdE>7@ (6U~i#`& +Q:%iqXDدǟơG~_uW\Wl؍[uλ#^0f_GBgL}nU7"tݼw]/!oС =4uծ'vȌcIr#3nRR7"&=cƮSd}k7Mš~{A6a}U2Awi-`b2ezUĕ]1S2IV$qʅ8r]_-R!N7[&/^i2JMY[UAt7B"\0cJ>o$y3lj+ .[ )2nЧu$"Wa'*5e#/h'+9>2 Se_EBkH)*Ty_f,{\Rk+ÞG=}]QV7i,sߖPmc7c20qts28d%*U8:*J)ėON+)\-#a .'s"ݝp<6Rx[AMT)Y#Ij sHi@|]>1>#bcֺ[(*bҮ w}Zjߥ.nV{_Sě_1.frLž4 1o ]HMGA9Q*1TL4\{}Fc. E}.W}t)`Д2 1cFr9Qi4<L! IS)QmSO =?MELlIۜDw,S y{_7F~TbJ3xKby2xgJ&y33٧U $gf|*7tRXӖ>FFCvYF4ڪ.(2Oި77iY /?FŽG) } j .NNijxU^opy^JkZ*p&%qZTKOPjPXKdbұ aQerLA|e2ދ~Ʒixx-JC@0ix)C͝e2uظ@5E92X8bQ}h6}O7'Lg>|]~}ZPw?c2`EGR>V qsW{3bEqn7ڣ8e;nx{B%̦[mD#5vC6/qͽSWᛘN ågyr*G罺T2s9~? uǥyVM?B{ɪ<_׈ =~X&GU]Qݻ O0^㺊bQ^(P!8@] ~~] yO=  "^/FFʺ8ALoc҆mu*"ήSw(sYN/ɻz}?&X徭 kcT<}l}/ھ1b/x*TVz=Ḽr&Fӏ.L5I FEH{dZiV:W\x ud(Y>MH kZdH"_$xDʿ3h0h2 'ņxuO\҈!b"})Շ)<ˑ:N~)r#հ %ݠWa[Z"KOMئǠ I}m[Ӻ[2 ۘp; 3PJTy_QeL)_ڍ#&^C;9Uo).JEP4 3E"R\]T1E@o9ML3XrOZO2bi=鼉#Yۦa? 74̼1iV%홮")FcI+z?k+~دl] 4~{B [7a&+6E.4רCbQ f*sr䒮[} 3PJ~bviHqD:hy2p@4|˯9S&uj/{#U9/U‹!(_F\9~)VxV0HM #Z_aG/*0K5I/2^c2 3cJ7Y=1əRLƾE&U 'seh"hⴇC_ž6(z|T%j ;.P$7#HR ^oOeU(M,ڈ>V>k>{1hV7Kj}?~R._Utԩi~]v0ȜХ'oC,!vq_ \uVI}e2AuЁø@:game4JߡлS#ͭ'$yg,楤_Q C2u'L7^G^ܯm ,QDG]+y>>Ǭ}R2瀅f% )6jg&>t*^#!z 4}G\$C WÈ\i,VUoEzm_/suu5[a>q2[SKq(ݱLL3*iE|82 23L![=ų H2.JF{_*;+A\)úoPD*XAdoZ>ګ% d+;{oCS;h>y/YepDآ5fxt (/ja\"b-YliM2uúB"/0h;CW&umb֦9Ĵ aʬm0S*N5̇4ko:f9 +3F_3i_xy W Q v˟={7Ͽ?4=o//7?ᦞgyO?~/?O?}o??߼~O8?t?'Ϟ~g?~~v/O~ŷ|z?x?׳?^߿ __n/׿<{C}Ǭv1oyAPK^<4r _rels\.rels1o0w$u;H ‚TKb%Y_SU*NmvЋ`VPvlFyZc015bJIdKQx9tKZquGo ّb3)ݩ1!b]o(Vc}׵Ѵg1`qqõ*q PK-^<!L[content_types].xmlPK-^>? 5F? ^I +? X9v? ~   ~ PH0(  >@  gg   dMbP?_%,*+&ffffff?'ffffff?(333333?)333333?" d,, ` `? ` `?U ,,,,,~ r       PH 0(  >@gg   dMbP?_%,*+&ffffff?'ffffff?(333333?)333333?" d,, ` `? ` `?U  ,,,,,,,,, , , ,~ ~ "~ ~ B~ ~ b ~  ~  ~ ~ ~ 2~ 0 (   p  6NMM?&]`  "d,, ` `? ` `?3l323  NM4  3QQ ; QQ3 ff6 ffffMNd4E4DFA33 &! 43*&! ! 4523 M NM43 3% 3&423 M NM4444% M3'44 >@gg   dMbP?_%,*+&ffffff?'ffffff?(333333?)333333?" d,, ` `? ` `?U ,,,   ! " # $ PH@0(  >@gg  FMicrosoft Excel 97-TabelleBiff8Oh+'0HP` x manfredManfred Moitzi1@@@|ҳ@Փ՜.+,D՜.+,\Root EntryF@WorkbookmCompObjIOle SummaryInformation(DocumentSummaryInformation8txlrd-0.9.4/tests/formula_test_names.xls0000644000076500000240000001700012155372403020511 0ustar chrisstaff00000000000000ࡱ;   Root Entry  !"#$%&'()*+,-./0123456789:<?@AC  \pCalc Ba==@ 8@"1Arial1Arial1Arial1Arial1Arial GENERAL                + ) , *    `Sheet1 Sheet2 Sheet3,, binopbool  singlesumD +testchoose AB C"d0testifa b" tfunca! tfuncvarb unaryminusTjb( 3  @@   DescriptionDataUnary operationSingle param sum Function callFunction with variable # args If functionChoose functionBinary operation ! bool  cc   dMbP?_%*+$!&C&"Times New Roman,Regular"&12&A)&&C&"Times New Roman,Regular"&12Page &P&333333?'333333?(-؂-?)-؂-?" d,,333333?333333?U } } v }     C @C @C @C Cb CC CPH0(  >@ gg   dMbP?_%*+$!&C&"Times New Roman,Regular"&12&A)&&C&"Times New Roman,Regular"&12Page &P&333333?'333333?(-؂-?)-؂-?" d,,333333?333333?U }  PH 0(  >@ gg   dMbP?_%*+$!&C&"Times New Roman,Regular"&12&A)&&C&"Times New Roman,Regular"&12Page &P&333333?'333333?(-؂-?)-؂-?" d,,333333?333333?U }  PH0 0(   >@gg  FMicrosoft Excel 97-TabelleBiff8Oh+'0HPh Thomas KluyverThomas Kluyver21@"n@@g`@q՜.+,D՜.+,\Root EntryFWorkbookCompObj;IOle =SummaryInformation(>DocumentSummaryInformation8Btxlrd-0.9.4/tests/formula_test_sjmachin.xls0000644000076500000240000004100012155372403021177 0ustar chrisstaff00000000000000ࡱ>  Root Entry F'@1Workbook3SummaryInformation(DocumentSummaryInformation8 \p John Machin Ba==ZSC6$8X@"1Arial1Arial1Arial1Arial1Arial1Arial1h8Cambria1,8Calibri18Calibri18Calibri1Calibri1Calibri1<Calibri1>Calibri1?Calibri14Calibri14Calibri1 Calibri1 Calibri1Calibri1Calibri1 Calibri1Calibri"$"#,##0;\-"$"#,##0"$"#,##0;[Red]\-"$"#,##0"$"#,##0.00;\-"$"#,##0.00#"$"#,##0.00;[Red]\-"$"#,##0.005*0_-"$"* #,##0_-;\-"$"* #,##0_-;_-"$"* "-"_-;_-@_-,)'_-* #,##0_-;\-* #,##0_-;_-* "-"_-;_-@_-=,8_-"$"* #,##0.00_-;\-"$"* #,##0.00_-;_-"$"* "-"??_-;_-@_-4+/_-* #,##0.00_-;\-* #,##0.00_-;_-* "-"??_-;_-@_-                                                                      ff + ) , *     P  P        `            a>    ||?}}A} _-;_-* "ef-@_- }A} _-;_-* "ef-@_- }A} _-;_-* "ef-@_- }A} _-;_-* "ef-@_- }A} _-;_-* "ef-@_- }A} _-;_-* "ef -@_- }A} _-;_-* "L-@_- }A} _-;_-* "L-@_- }A} _-;_-* "L-@_- }A} _-;_-* "L-@_- }A} _-;_-* "L-@_- }A} _-;_-* "L -@_- }A} _-;_-* "23-@_- }A} _-;_-* "23-@_- }A} _-;_-* "23-@_- }A} _-;_-* "23-@_- }A}  _-;_-* "23-@_- }A}! _-;_-* "23 -@_- }A}" _-;_-* "-@_- }A}# _-;_-* "-@_- }A}$ _-;_-* "-@_- }A}% _-;_-* "-@_- }A}& _-;_-* "-@_- }A}' _-;_-* " -@_- }A}( _-;_-* "-@_- }}) }_-;_-* "-@_-    }}* _-;_-* "-@_- ??? ??? ??? ???}-}/ _-;_-* "}A}0 a_-;_-* "-@_- }A}1 _-;_-* "-@_- }A}2 _-;_-* "?-@_- }A}3 _-;_-* "23-@_- }-}4 _-;_-* "}}5 ??v_-;_-* "̙-@_-    }A}6 }_-;_-* "-@_- }A}7 e_-;_-* "-@_- }x}8_-;_-* "-@_  }}9 ???_-;_-* "-@_??? ???  ??? ???}-}; _-;_-* "}U}< _-;_-* "-@_ }-}= _-;_-* " 20% - Accent1M 20% - Accent1 ef % 20% - Accent2M" 20% - Accent2 ef % 20% - Accent3M& 20% - Accent3 ef % 20% - Accent4M* 20% - Accent4 ef % 20% - Accent5M. 20% - Accent5 ef % 20% - Accent6M2 20% - Accent6  ef % 40% - Accent1M 40% - Accent1 L % 40% - Accent2M# 40% - Accent2 L湸 % 40% - Accent3M' 40% - Accent3 L % 40% - Accent4M+ 40% - Accent4 L % 40% - Accent5M/ 40% - Accent5 L % 40% - Accent6M3 40% - Accent6  Lմ % 60% - Accent1M 60% - Accent1 23 % 60% - Accent2M$ 60% - Accent2 23ٗ % 60% - Accent3M( 60% - Accent3 23֚ % 60% - Accent4M, 60% - Accent4 23 % 60% - Accent5M0 60% - Accent5 23 %! 60% - Accent6M4 60% - Accent6  23 % "Accent1AAccent1 O % #Accent2A!Accent2 PM % $Accent3A%Accent3 Y % %Accent4A)Accent4 d % &Accent5A-Accent5 K % 'Accent6A1Accent6  F %(Bad9Bad  %) Calculation Calculation  }% * Check Cell Check Cell  %????????? ???+ Comma,( Comma [0]-&Currency.. Currency [0]/Explanatory TextG5Explanatory Text % 0Good;Good  a%1 Heading 1G Heading 1 I}%O2 Heading 2G Heading 2 I}%?3 Heading 3G Heading 3 I}%234 Heading 49 Heading 4 I}% 5InputuInput ̙ ??v% 6 Linked CellK Linked Cell }% 7NeutralANeutral  e%"Normal 8Noteb Note   9OutputwOutput  ???%????????? ???:$Percent ;Title1Title I}% <TotalMTotal %OO= Warning Text? Warning Text %XTableStyleMedium9PivotStyleLight16`*Sheet1n0Sheet21Sheet3= DescriptionDataNon-latin1 text ! >A:20 formula textformula zero-length textformula boolean formula errorformula numberformula non-latin1 text) X*ccB  {-/  dMbP?_*+%&?'?(?)?MnMicrosoft Office Document Imag/ dLetterwidm" d??&U} $} m > >   $I$I? + %ABC@DEF@ ABCDEF "4 fooA )@@@   D ! >A:20=/J4;/>@7ggD  1  dMbP?_*+%&?'?(?)?"??&U>@7ggD  3  dMbP?_*+%&?'?(?)?"??&U>@7ggD  Oh+'0@H\p  John Machin John MachinMicrosoft Excel@$@3[0՜.+,0 PXd lt|  Sheet1Sheet2Sheet3  Worksheets F&Microsoft Office Excel 2003 WorksheetBiff8Excel.Sheet.89qCompObj rxlrd-0.9.4/tests/issue20.xls0000644000076500000240000001400012155372403016111 0ustar chrisstaff00000000000000ࡱ>   u'ɀ\punics Ba==`T%8X@"1Sans1Sans1Sans1Sans1Sans$#,##0_);($#,##0)$#,##0_);[Red]($#,##0)$#,##0.00_);($#,##0.00)!$#,##0.00_);[Red]($#,##0.00)/**_($* #,##0_);_($* (#,##0);_($* "-"_);_(@_),)'_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)7,2_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)4+/_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)                + ) , *    `83ffff̙̙3f3fff3f3f33333f33BSheet1^ Sheet2 Sheet3    Sheet1    Sheet2    Sheet3'aasaasasalegenda as  u'ɀ   dMbP?_*+% &L&C&[TAB]&R&L&CPage &[PAGE]&R&?'?(?)?"d"XX??U} $ } $ } $ } $ } $ } $ } $ } $                  (,|rrr>@      ggD u'ɀ   dMbP?_*+% &L&C&[TAB]&R&L&CPage &[PAGE]&R&?'?(?)?"d"XX??U} $ >@ggD u'ɀ   dMbP?_*+% &L&C&[TAB]&R&L&CPage &[PAGE]&R&?'?(?)?"d"XX??U} $ >@ggD  ՜.+,0 Oh+'0@ ( 4@4G@YX  !"#$%&'()*+,-./0123456789;=Root EntryWorkbooknDocumentSummaryInformation8:HSummaryInformation(<pxlrd-0.9.4/tests/merged_cells.xlsx0000644000076500000240000002207312270704230017440 0ustar chrisstaff00000000000000PK!U6+w([Content_Types].xml (TN0#(q[Bi

    <4Mox/}bN@;vCf ۨBI"c&\O8q"KH<ߊs@.h<⧄MdaT_PK![axl/_rels/workbook.xml.rels (j0E}-IR"gS ٶ{ d4ӇM1f{}vxGG5 (&Ec*{ Hˮjnz^Ő;MIi]ٗΏBFi}{翵mY69>C,uGًjW!+-dybaZ;,^i:kK„&:%$ l!Zaa9%?%Rl%&LuZO cȾPK!H6ixl/workbook.xmlRN0#4&TB!Qڳ7Uǎl:QC=p]d<3ru%cV&!% ͅgc|wOuLq&U~{9|j} HlF+Eآىn@ԦfGlcq[Zq΂ E.KQ.H HPDci,ye5>JJ$ < _mZ!6 S7C8nW#}[ȏ.qўS4M)NpW!,g/ <t}xM_ݽP#|)_h{ؘ5?hG*`?(,0X_zqFqoPK!L7xl/worksheets/sheet4.xmlM0 8i-ViȪ=TZUm^0ZvwbF Ox.U-M@ec"8P$sobw<oPɃO߁O]+lyjgd9v20Y-htP4 y#,^-n@k <0 s],N mc*jKb=vCuv@9gPK!q pxl/worksheets/sheet3.xmlMo0,߻f6vAQ{TUM  3(zÏμcf=Zvm%c|@hd>qfhү #H>< S=H)ť8h{wK cxLޜH]ś*$A>J%aACMʈJ&M;4Be tY>c~4$ &^ L1bma]ut(gZ[Wvr2u{`M,EF,2nQ%[NJeD >֗f}{7vtd%|JYw2Oڡ~J=L8-o|(<4 ժX}.@'d}.Fbo\C\ҼMT0 zSώt--g.—~?~xY'y92h!ы/ɋ>%mGEFD[t3q%'#qSgv 9feqwW@(^wdbh a8g.J pC*Xx8rbV`|XƻcǵYU3 Jݐ8b3+(QuK>QELKM2#'vi~ vlwu8+zHHJ:) ~L\E\O*t@G1lm~C*uG.R(:-ys^Di7QR8,b?SQ*q7C;+}ݧ;4pDZ K(NhwŘQ6㶷 [SYJ(p»g>X_xwu{\>k]Xy}钣M26PsFnJ'K,}䇦$Ǵ;@` >*8i"LI%\ xӕ=6u= r2f 3c (:jZ3sLs*UܚЅ ]M8kp6x"]$C<&>'eb. vJ|yXɾ8Ȯ]7R /=,.&'Qk5q&p(Kaݐ Sd›L17 jpSaS! 35'+ZzQ H )7 5)kdB|UtvaDξp|Fl&0_*3n'LE/pm&]8fIrS4d 7y` nίI R3U~cnrF:_*P}-p Tpl rۜ4LZéO !PLB]$K *++65vꦚeNƟf(MN1ߜ6&3(adE,Uz<{EUϲV)9Z[4^kd5!J?Q3qBoC~M m<.vpIYӦZY_p=al-Y}Nc͙ŋ4vjavl'S&A8|*~x1%M0g%<ҭPK!_8xl/worksheets/sheet1.xmlQMk0 Ӎn$)R`$rbj[VIh>{ғ C4KXd9 w%|oADVU<Eq="c =26=:3Ч4t2U;y~+2fUimPwy hcoUњ Ȫ~|[mK1V=,x%b-s%WÚue/MLfcӚpd}s]7\'ն6 J^I$#&d]TV_e|1 S9PK-!U6+w([Content_Types].xmlPK-!U0#L _rels/.relsPK-![alxl/_rels/workbook.xml.relsPK-!H6ixl/workbook.xmlPK-!L7\ xl/worksheets/sheet4.xmlPK-!ajiu xl/worksheets/sheet2.xmlPK-!q p}xl/worksheets/sheet3.xmlPK-!Kިxl/sharedStrings.xmlPK-!0 xl/styles.xmlPK-!%Sxl/theme/theme1.xmlPK-!_8<xl/worksheets/sheet1.xmlPK-!PY'?YdocProps/core.xmlPK-!_EbdocProps/app.xmlPK R xlrd-0.9.4/tests/picture_in_cell.xls0000644000076500000240000001500012155372403017760 0ustar chrisstaff00000000000000ࡱ;   Root Entry  !"#$%&'()*,/013  \pCalc Ba==@ 8@"1Arial1Arial1Arial1Arial GENERAL                + ) , *  x `Sheet1,,T~bvΘ[uRnJΘ[uPNG  IHDRPsHIDATxAHZqǿJ4VeV^MCŰd޾ 'qf0`'SoP[MLLRbNl̿0%_a6p6~401>Qô/Dn7ڪ* VWop `]wVke(fffy$02 n7N'@ V!ieH. K#| ]mR I`'@Ibqq^X l‹`>Vz惔VC=*Jr9lmmnSڠP(0::zrp~r J&-RU#4R$IlooG*9jB! 7SFO^!]81_;&\u" hoEՂ] o}}VlppKKKH&'"6n*t+$rbmmB,'ǃή hR"["Qcg RF]}>[T/ 69m5U6] CEWY?0BAwKvIENDB`3  @@  cc   dMbP?_%*+$!&C&"Times New Roman,Regular"&12&A)&&C&"Times New Roman,Regular"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U} st@(    dA ?Graphics 1'S]`>@_  gg  FMicrosoft Excel 97-TabelleBiff8Oh+'0@H d p | Christopher Withers5@@@lNI@b.O՜.+,D՜.+,\Root EntryF Workbook CompObj+IOle -SummaryInformation(.DocumentSummaryInformation82txlrd-0.9.4/tests/profiles.xls0000644000076500000240000010200012155372403016440 0ustar chrisstaff00000000000000ࡱ; ?<  !"#$%&'()*+,-./0123456789:;>@Root Entry  \pmanfred Ba= =@ 8@"1Arial1Arial1Arial1Arial1Arial GENERAL0 0.000 0.00%                + ) , *  " " "X "   "     83ffff̙̙3f3fff3f3f33333f33333`H PROFILEDEF*AXISDEF(TRAVERSALCHAINAGE'AXISDATUMLEVELS, PROFILELEVELS11Tzr83  @@  98PROFILabcdefghijklP8.2P8.3P8.4P8.5P8.6P8.7P9P9.1P9.2P9.3P9.4P9.5P9.6Q0 Quergefllei1h3h2h1a1a2b1c1f1g1l1l2j1k1j3j4g3g4d1c3e1f3f2g2b2c2j2k2 8 cc   dMbP?_%*+&C&P&C&F&333333?'333333?(-؂-؂?)[[?" d,,??U } #}  }                                 N N N  N"&*.26:>BFJN NRVZ^bfjnrvz~ N N N  N "&*.26:> N BFJNRVZ^bfjn N rvz~ N  N  N "&*. PH0(  >@gg   dMbP?_%*+&C&P&C&F&333333?'333333?(-؂-؂?)[[?" d,,??U } #} } }                           "   &   *   .   2   6  : PH 0(  >@gg   dMbP?_%*+%"&C&"Times New Roman,Standard"&12&A+(&C&"Times New Roman,Standard"&12Seite &P&333333?'333333?(-؂-?)-؂-?" d,,333333?333333?U }                                ~ o|?Ù?̯f@ @ @@33333@@ L @ (@ *@~   ~ o|?Ù?̯f@ @ @@33333@@ L @ (@ *@~   ~ o|?Ù?{ͯf@ @ @@33333@@ L @ (@ *@~   ~ o|?Ù?̯f@ @ @@33333@@ L @ (@ *@~   ~ o|?Ù?̯f@ @ @@33333@@ L @ (@ *@~   ~ o|?Ù?̯f@ @ @@33333@@ L @ (@ *@~   ~ o|?Ù?̯f@ @ @@33333@@ aL @ (@ *@~   ~ o|?Ù?̯f@ @ @@33333@@ L @ (@ *@~   ~  o|? Ù? ̯f@  @  @ @33333@ @ L @ (@ *@~  ~  ? !!? La@ ; @ p @ ]W 3@ ]0}@  'CL @ Aa(@ /*@ A*@ ~  GK? TL? w6^@ {` @ `" @ Apf3@ ҙ@ ;L @ 6(@ H#*@ L +@ ~  <&? A? }@ ] @  ] @ O0e543@ Zc&ٚ@ FalM @ d(@  ǃ*@ VH/+@ ~  %?  ? 8l@ sc @ o@ @ OoC3@ u@  MM @ .6%)@ Wq*@ K}:[+@ ~ ?I!4?z P@* 7 @? $7 @ù= @?@ >mzP>@ Fcg')@ U8e*@ <^+@PH0 0(   >@gg   dMbP?_%*+%"&C&"Times New Roman,Standard"&12&A+(&C&"Times New Roman,Standard"&12Seite &P&333333?'333333?(-؂-?)-؂-?" d,,333333?333333?U }           dp@eάp@ pe1p@m2p@ =f2p@T:Xgp@  3mʒp@!%4p@ 9̗p@p@ a+ep@eΫp@ iap@R!p@ |ap@cڬp@  Xp@ W/p@  1ZGUp@ qqtp@  qp@ ۧ1p@  5;Np@ p@  EJp@ !p@ ^p@Ji׶p@PH@0(  >@gg   dMbP?_%*+%"&C&"Times New Roman,Standard"&12&A+(&C&"Times New Roman,Standard"&12Seite &P&333333?'333333?(-؂-?)-؂-?" d,,333333?333333?U }               ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7  ?!dp@ [29p@DDZ Z%([ڏp@DQ?([p@ D)%Ёp@DDZ%뭁p@DQ?2h!p@DDZZ2 = By_p@DDZZ2 zNz_p@DDZZ2 2@p@DDZZ2  3mJp@DDZ Z% [Ɏ p@D Q?2([ڒp@DDZ Z2[닄6p@DDZ Z%9p@DQ?%ҩ+ep@DV-?%"p@ D?ZZ6-}up@DDZZ2 뭁p@DDZZ2 e/sp@DDZZ2 W[p@D Q?2x $(p@DDZ Z2:Mp@DDZ Z%Oep@DQ?% gp@DV-?%tˑp@D Q?%z6>p@DbX9?cڬp@Z6bp@ D?ZZ609|p@ D?ZZ%SVӕp@Dy?%ɯp@D?ɯp@Dfffffp@fffffp@fffffp@fffffp@  ?! Xp@ [ 2 $ p@D D Z Z % %Ώp@D Q? %p@ D ) Cxqāp@D D Z % 9p@D Q?2 p@D D Z Z 2 Sp@D D Z Z 2 nSp@D D Z Z 2 5p@D D Z Z 2 W&R?p@D D Z Z % MOXp@D Q?2 %Βp@D D Z Z 2 *p@D D Z Z % $ p@D Q?%  Yp@D V-?% Y/rp@D Q?% &P6p@D bX9? W/p@Z 6 }k/p@ D ?Z Z 6 Ip@ D ?Z Z %  #bp@D y?% SVӕp@D ? SVӕp@D  fffffp@ fffffp@ fffffp@ fffffp@   R O?! 1ZGUp@ [ 2 Sp@D D Z Z % |Yp@D Q? |Yp@ D ) <3p@D D Z % 2双xp@D Q?2 Ge΢p@D D Z Z 2 JÏLp@D D Z Z 2 \p@D D Z Z 2 f46@p@D D Z Z 2 Lp@D D Z Z % B #ۓp@D Q?2 N{&p@D D Z Z 2 hp@D D Z Z % %p@D Q?% j:p@D V-?% =ئ}p@D Q?% ~p@D bX9? qqtp@Z 6 op@ D ?Z Z 6 ]p@ D ?Z Z % I/p@D y?% *|bp@D ? *|bp@D  fffffp@ fffffp@ fffffp@ fffffp@   Ѳ ё?! qp@ [ 2 {[#ap@D D Z Z % qp@D Q? qp@ D )  (߃p@D D Z % IKp@D Q?2 mГp@D D Z Z 2 28p@D D Z Z 2 2ʙޒp@D D Z Z 2 V6ɒp@D D Z Z 2 Qp@D D Z Z % Eap@D Q?2 mLmp@D D Z Z 2 Quؔp@D D Z Z % w#SՖp@D Q?% sD#p@D V-?% `[p@D Q?% }wp@D bX9? ۧ1p@Z 6 r p@ D ?Z Z 6 =h $p@ D ?Z Z % :=p@D y?%  n5pp@D ?  n5pp@D  fffffp@ fffffp@ fffffp@ fffffp@   sMiX?! 5;Np@ [ 2 "+}p@D D Z Z % Ӈ yp@D Q? Ӈ yp@ D ) ]p@D D Z % Js p@D Q?2 WTp@D D Z Z 2  *p@D D Z Z 2 ]͵p@D D Z Z 2 l˧p@D D Z Z 2 g%| p@D D Z Z % ]NأΕp@D Q?2 -ؕp@D D Z Z 2 Ap@D D Z Z % ( p@D Q?% cp@D V-?% vcp@D y?% :qp@D ? :qp@D  fffffp@ fffffp@ fffffp@ fffffp@   2.(v?! EJp@ [ 2 K5p@D D Z Z % AّŖp@D Q? Aّņp@ D ) b2p@D D Z % X1p@D Q?2 j˘-p@D D Z Z 2 Ip@D D Z Z 2 {jp@D D Z Z 2 +t$ۖp@D D Z Z 2 <p@D D Z Z % 2/MJp@D Q?2 ¨Top@D D Z Z 2 (Np@D D Z Z % LŬp@D Q?% C>p@D V-?% 5K8p@D Q?% Mۼ,ܛp@D bX9? !p@Z 6  9Gp@ D ?Z Z 6 z(p@ D ?Z Z % XHtBp@D y?% G{up@D ? G{up@D  fffffp@ fffffp@ fffffp@ fffffp@  ~ !^p@ [2^p@DDZ Z%- p@DQ?- p@ D)- p@DDZ% p@DQ?2- p@DDZZ2 - p@DDZZ2 - p@DDZZ2 - p@DDZZ2 - p@DDZ Z%  p@D Q?2- p@DDZ Z2- p@DDZ Z%^p@DQ?%aӫp@DV-?%^p@D Q?%X7p@DbX9?Ji׶p@Z6-_p@ D?ZZ6/%-"yp@ D?ZZ%ɾƻp@Dy?%Źp@D?Źp@Dfffffp@fffffp@fffffp@fffffp@PHP0(  >@gg  FMicrosoft Excel 97-TabelleBiff8Oh+'0@H ` l x Manfred Moitzi33@@@@w4՜.+,D՜.+,\Root EntryFp=@WorkbooksCompObjIOle SummaryInformation(DocumentSummaryInformation8txlrd-0.9.4/tests/ragged.xls0000644000076500000240000001500012155372403016051 0ustar chrisstaff00000000000000ࡱ;   Root Entry  !"#$%&'()*+,-/2346  \pCalc Ba==@ 8@"1Arial1Arial1Arial1Arial GENERAL                + ) , *  `Sheet1Sheet2 Sheet3,,Tjb( 3  @@  8 abcdefghIjkl x cc   dMbP?_%*+$!&C&"Times New Roman,Regular"&12&A)&&C&"Times New Roman,Regular"&12Page &P&333333?'333333?(-؂-?)-؂-?" d,,333333?333333?U }               PH0(  >@gg   dMbP?_%*+$!&C&"Times New Roman,Regular"&12&A)&&C&"Times New Roman,Regular"&12Page &P&333333?'333333?(-؂-?)-؂-?" d,,333333?333333?U }  PH 0(  >@gg   dMbP?_%*+$!&C&"Times New Roman,Regular"&12&A)&&C&"Times New Roman,Regular"&12Page &P&333333?'333333?(-؂-?)-؂-?" d,,333333?333333?U }  PH0 0(   >@gg  FMicrosoft Excel 97-TabelleBiff8Oh+'0HPh Thomas KluyverThomas Kluyver3@@@n@ɪ[՜.+,D՜.+,\Root EntryF Workbook| CompObj.IOle 0SummaryInformation(1DocumentSummaryInformation85txlrd-0.9.4/tests/reveng1.xlsx0000644000076500000240000004045312155372403016371 0ustar chrisstaff00000000000000PK!ϛ.[Content_Types].xml (UN0#q)B)# |UǶxB<"CR(<b(&G&fLЫs *d1MN@ݫIb졗Fi{S}1ě﬽O`#9F`ή;א!,J݆Rt+BDɅ-F0Ȑ>6n.`-6s5]8':ʿ塕%w9q,U3%.>bwد >RnY9S&oPK!U0#L _rels/.rels (N0 HCnHLH!T$$@Jc?[iTb/Nú(A3b{jxVb"giaWl_xb#b4O r0Qahѓeܔ=P-<4Mox/}bN@;vCf ۨBI"c&\O8q"KH<ߊs@.h<⧄MdaT_PK!;Yxl/_rels/workbook.xml.rels (J0nvutW] ӦlLӷ7I-,R}L2}YCl٘ZyiJZ$WWlH7=bH䜔NRb{4RYIBW^i3Mv(CvFUƂYw"胨t5z1E|l@ <0JIL01 ?AoG%0vMa]3K/ܭ 6L^|4Kk08!x|!?PK!nqxl/workbook.xmlQO0';d'^$-P5A4^&RyX8vdK;;4Mē}wxyyhe+* dpUi&Ԯ׏XGR+^#pYyC8-V o+4R&*lja >{%䐘fVQC+!WZ9UCbNӐoD`Bq?oR#ۮp2|C**PFi~8'y6=͗i=Hie\ΛjgPZv-8?0@k͸8 0%32#SPK!%Sxl/theme/theme1.xmlYOo6w tom'uرMniXS@I}úa0l+t&[HJKՇD"|#uڃC"$q۫]z>8h{wK cxLޜH]ś*$A>J%aACMʈJ&M;4Be tY>c~4$ &^ L1bma]ut(gZ[Wvr2u{`M,EF,2nQ%[NJeD >֗f}{7vtd%|JYw2Oڡ~J=L8-o|(<4 ժX}.@'d}.Fbo\C\ҼMT0 zSώt--g.—~?~xY'y92h!ы/ɋ>%mGEFD[t3q%'#qSgv 9feqwW@(^wdbh a8g.J pC*Xx8rbV`|XƻcǵYU3 Jݐ8b3+(QuK>QELKM2#'vi~ vlwu8+zHHJ:) ~L\E\O*t@G1lm~C*uG.R(:-ys^Di7QR8,b?SQ*q7C;+}ݧ;4pDZ K(NhwŘQ6㶷 [SYJ(p»g>X_xwu{\>k]Xy}钣M26PsFnJ'K,}䇦$Ǵ;@` >*8i"LI%\ xӕ=6u= r2f 3c (:jZ3sLs*UܚЅ ]M8kp6x"]$C<&>'eb. vJ|yXɾ8Ȯ]7R /=,.&'Qk5q&p(Kaݐ Sd›L17 jpSaS! 35'+ZzQ H )7 5)kdB|UtvaDξp|Fl&0_*3n'LE/pm&]8fIrS4d 7y` nίI R3U~cnrF:_*P}-p Tpl rۜ4LZéO !PLB]$K *++65vꦚeNƟf(MN1ߜ6&3(adE,Uz<{EUϲV)9Z[4^kd5!J?Q3qBoC~M m<.vpIYӦZY_p=al-Y}Nc͙ŋ4vjavl'S&A8|*~x1%M0g%<ҭPK!;m2KB#xl/worksheets/_rels/sheet1.xml.rels0ECx{օ CS7"Ubۗ{ep6<f,Ժch{-A8 -Iy0Ьjm_/N,}W:=RY}H9EbAwk}m PK! 9xl/worksheets/sheet2.xmlVQs@~L=D0Z @ΤLLN.pMܩ1{RG ~7EִLhKxLHK-(<Ù0Dтh OR^Dm4DUSh"7,t7ibxUR6Nj E*}7aL%jzx6F]qpFot'[Heo|If2)L ss\PPE*Z/ ZmU(Ț&ؚ(L>z\ݯ5ŪsX"߰Vx(T4nDr,v)$>k?f_xʤq1Qya9:4ܱ]PoPK!Omxl/worksheets/sheet3.xmlMo6- Pڒ8qj{a2{("Ӷ(Hr>w2fB"D/=+#ʙfiDӖ #QrUVy/gqvywYe EBma[l>o(k6a[7"_a$>/_ YCe!.dq؋i.ݖu/f4yL(Jw)[sMm-ΧY:4v潔_1j'B6h%a+Qn,; }{Gig\uy<q27lv<šsHU~!pwbvmHGEm1{XGi-NܹYb׀^Op/OI_%C 18pcjipਏۼžӾ؞ËCig@dSxHC{ڬr{ⳃy(w'M]dr_bXV׫>dxȅ(_ >bcY!θ+o/usQ>ϋU`횗k_oS`/&{pŢY;à2Cw~YCOj~_7aҒ6(mF(mU y lʳ04WihtQgY|`A}.aˎ#9NJ@‘ǽ薹$ ! dJzr\Xʙ[ZLDTмeډF`RbՓtbJ$0 PTA`a{8 ƎiXӺPGgJ$E=~SE--tU!?%nSmza: Kۢ:%qHT[~r`C}:ɰVdLmG }ov1r*B떮o l%o['@'B0r`)%0B %Q$J˸"2`=p/eRxFhq7wS{Na"458֐N I HJY$aB#0X̩$DTN08D1"O&4öπhu|S=i<Ƞ*5ԑ5\a$}XVB1 9N C]%` 1h-Y0Vv-YWקjjLD]iA2#@4JR/R1i9erm&.\硵6Ikr{}8C!(xdL&xLL(h(}_~Ml8ߕjV<@ܡXM:)D;NEΛgIbx|iL0Z3)jE)@?=^Ɨ?_\B) Ў~RD?OήO;Ҝ6A/*b$ZQHx`EwD@" yq: Jc/Q4y"H >ؤ"~>;0Jkiς e%5Aqݹ$;(I^[|-":k|Oѻf{asRm5Ck>[+$6*Gm^ְ=~hu;f"I5^Sii-/iy_ye~ÌIb,n=ÓHdEihO=Ng,gc(FP]=x;Kf\< Q4h4di/-p/!M<ZG7HH/a!& wakִIho1:|=0<8FqGLB3" _1Fn"50`,jϊm|>N@{}#߽K .: @3fvB8)52򎈴/hU1bZ @`{ |wjd_Q8!( X2;%KJE h@32PF=DӟXgaJ6D3&/{4`4@ 'Ua&G:*-Ao{a0ۈ ;? j-blom6EiAV~* z9M`?NjU?+NKk9[F0/;xږipsa4}^%΋/pG۲_ uڇwl|ji2ow^ {ۆwj Uį>,2ajmxٺR4+ %Ǖ{^a^MO֍0I9SALxߞ߮z77/%m p!(Uo؍(as_^@gي-wϬvoOj\n"VG+|xe?PK!QhXxl/sharedStrings.xmlXn6y0GIk+h}hQKRݧ#YI\ $Hw;_5\ uiN ]R4<.2ZLpe~x0qn,_HSW,D݉Y ؚ{|-[kfYZs(L=Fˏ"8'NV.=nq|pO +J \>xY [. n Jo| ᄽI^TPYunR{zxa'وbJhЂ[`9nnK (+mp3oae/~u,ƧgTz z&R[Xp(WuOʀM@qK_ &?x'a +k I*ޕ QNpT_BZ~mȢnBKjHtz84*f5?h]/4qlB/];"=ஐLJ*%u t5iRuMW\@%/%t>%̂LJ mͬ_f_.|޵6]5q!<(vmPG:hD[Qn<ZtpvǼ*ߏ/];Ql9I-,U:8ã8<çq,?7q.h_gkWqM7Q p<,`p EܜWq8o73!>-lt}qqAeRpDSJ< w[N"LxӾ1񇰒~Q* @oͤ_v5QTbN9P2QDBռIZ!+6KrnE!_}+hHM-b,͘Zeov39} enD+(;ٻ3q \\8a4(YG2|9<.%= *=(dvUU}wE:(p81VUFa[7 # o!)VO'2O P B$)]Rp=X,ʜ~E,ay6@ÜO-qۭu3ZR%|_5nk!./fzh8y"%-d]y9'5i*Q)J1w0%.jOcQpPn((,h&̧3bc!`PId*xR@p+;E ?e:An ~neL nvƴgU`Zmmȧ6])$:!#_ +i/x뼙~*6B' n (^ NANu<~A:_Ga9s2RpAm"PkxfPxV5k-r sgy;zC>7y2Ú-p_#_ɖSsjЙ(o(j+ #x>qTjN\yu)dE^Ȝ7m06{Y2C~*,`ĔfɢÆط AHѷ1ePE|]T&hqt k)UdQg00Qi<د~$rypoS(tE҃~=y_O$@=OEo ԁvG/EOhA4zKzxdo]5FPK!xnQ@"'xl/printerSettings/printerSettings1.binZnF@97hziehH"CRJچj/~4Q:mF2Ð6i«Sի+g$a3@(M>.6%YkqzNW {ɕw_A7Z4ڷ]nF3*$_U_v} piۤ":ވsYwųg,ikmcM6/"4[$F')0"ݸCCChޑRi>^Q_GiӺ~@@@!@wQ#0o34>sW۹sq4wt3t;wx;Y6U?ͽn[uQpA<Gk2k9>f sFޛ2#"_<œu=oM!PK!u=Sxl/calcChain.xml]j0 ݃3vZԁtboha[NN ϡ>1TBrЌ.\+pY\\8~ _>c}W6oNBBD,ӳin|H;qܒUi[: 9$QM6W/D֥B޾-8k* w~>@?BHJJU*p \WU*pP0@xF E Ef` DظMC @Ҩ`xG \;Z[xڵ/#A PK!_KgdocProps/core.xml (|_K0C{m*{ŷܭ& Iۛ[P|='sIRM V(M@VH+s(riVCЪ!͇mPiܟeTMt;g >u]-!_7OCX~WP N[[޷6Rx6w07a[ fj-PbDB,:8*/ۻjʐ:N8˪,)~sCG.bz܂lF<ܧ_PK!FNdocProps/app.xml (Tj0}47rRFULчn KA^&_'d[wm tuÕա,X >g|G v$|:fp]ɷoʻ <$lH=b<=*Η wpS v)K'Is/E^ɜޭzP'k;qyo9-]Y)[ڧܱ63 1Uw]6Św qCkV-Ο@r߃\xY,h0lLbl\֑=4Tں P#gV/Z2R]:PJv*)!+jKH?vՋ=bЕ.@bi~ShF$me{; Z.b͹EN +h[S#1D0 Σ ?6*D !jo. | T>z*.*2<5g~0njsfGzj.ZJP=_{/{PK BD_rels/UT ;!zS;!zSux PKBDӗN _rels/.relsUT ;!zS;!zSux J1rζlڋnLu}{Ҋ=xoi64r^VV;vϋY48"܇4lt_JzȮFi'[$! WmѼKv SOǘ_ ![ ;P UyB mqXGN܋w~G˦RH PK BD docProps/UT ;!zS;!zSux PKBDwԟJdocProps/app.xmlUT ;!zS;!zSux N0 HCnBUMwA!u׈6bSm<=i3 ngڝ^z`.@3A]{P vU>`d$‘9RpдNm:Tƣmk {>c-['F` ƛ>z>C{QOFIb##QH ^5ᴦ`jpgL@ߨCdeF{SoC!bVR PKBDQqdocProps/core.xmlUT ;!zS;!zSux mj0 C=QYo;P[KMcXZӾ}e(?98abSMUK MVSdy^ݭMlMHO!b\dqkb"`s@3$%QuE[-&aVZ(Wf5z$ah~Y揟-<߄d!jj||jhʠ+PK BDxl/UT ;!zS;!zSux PK BD xl/_rels/UT ;!zS;!zSux PKuCDxl/_rels/workbook.xml.relsUT "zS"zSux J0nVƽW] $Ӧlo肶Oa~a||zL}%) 6B?J$yӚ\R&rӦ`.Mxl/workbook.xmlUT ;!zS;!zSux PN0#i"^g74Vc; iM"B6ዽit:([:VзׇJBVY.xn܎ 1c0q-XT[ZR Y6cFjKGGU.ޕlE<42P6PFȗgO0ͮ8G+ ]doZ[庁pHp7V4gz[Goe楷$5ǛQ>1⣉C4s}Z*T_(-OhyBLh%繂J[PO(8)9,>PK BDxl/worksheets/UT ;!zS;!zSux PK BDxl/worksheets/_rels/UT ;!zS;!zSux PKBDIϾ:#xl/worksheets/_rels/sheet1.xml.relsUT ;!zS;!zSux ˊA EPdo{"bEU?7 ]rpv;u\4tM vqU*K.p~}*GeSQB ETk"3A&C̞yDB#Gۮ12@Jqj'ہ:a6|swL?"(T#W xshBH 7_I~PKBDIϾ:#xl/worksheets/_rels/sheet2.xml.relsUT ;!zS;!zSux ˊA EPdo{"bEU?7 ]rpv;u\4tM vqU*K.p~}*GeSQB ETk"3A&C̞yDB#Gۮ12@Jqj'ہ:a6|swL?"(T#W xshBH 7_I~PKBD"SpUrxl/worksheets/sheet1.xmlUT ;!zS;!zSux R;O0ޑwGQ TU,LMrI,be_[sNBb=aWۋhЕtIڸK)"iW \-=`K+bՁq# Ъz^Yv6NW/45V[ F&?vG˴ kR[RX:{SSWyj"m 6ڧX}<\E 5ep/B;ȃc+6‹2+.I'o遡؏o nNָ(zhX.ΤB"1A@@[/00R-b=wd_{E"Sw̹gúLR:J?PKBD2'txl/worksheets/sheet2.xmlUT ;!zS;!zSux R;O0ޑw*i*&@&$ϲ-9'>{vb!t`.M xl/workbook.xmlUT;!zSux PK BDAcxl/worksheets/UT;!zSux PK BDAxl/worksheets/_rels/UT;!zSux PKBDIϾ:#xl/worksheets/_rels/sheet1.xml.relsUT;!zSux PKBDIϾ:#xl/worksheets/_rels/sheet2.xml.relsUT;!zSux PKBD"SpUr/xl/worksheets/sheet1.xmlUT;!zSux PKBD2'txl/worksheets/sheet2.xmlUT;!zSux PKxlrd-0.9.4/tests/test_biffh.py0000644000076500000240000000107612155372403016567 0ustar chrisstaff00000000000000import unittest import sys if sys.version_info[0] >= 3: from io import StringIO else: # Python 2.6+ does have the io module, but io.StringIO is strict about # unicode, which won't work for our test. from StringIO import StringIO from xlrd import biffh class TestHexDump(unittest.TestCase): def test_hex_char_dump(self): sio = StringIO() biffh.hex_char_dump(b"abc\0e\01", 0, 6, fout=sio) s = sio.getvalue() assert "61 62 63 00 65 01" in s, s assert "abc~e?" in s, s if __name__=='__main__': unittest.main() xlrd-0.9.4/tests/test_cell.py0000644000076500000240000000443012270704230016420 0ustar chrisstaff00000000000000# Portions Copyright (C) 2010, Manfred Moitzi under a BSD licence import sys import os import unittest import xlrd from .base import from_this_dir class TestCell(unittest.TestCase): def setUp(self): self.book = xlrd.open_workbook(from_this_dir('profiles.xls'), formatting_info=True) self.sheet = self.book.sheet_by_name('PROFILEDEF') def test_string_cell(self): cell = self.sheet.cell(0, 0) self.assertEqual(cell.ctype, xlrd.book.XL_CELL_TEXT) self.assertEqual(cell.value, 'PROFIL') self.assertTrue(cell.xf_index > 0) def test_number_cell(self): cell = self.sheet.cell(1, 1) self.assertEqual(cell.ctype, xlrd.book.XL_CELL_NUMBER) self.assertEqual(cell.value, 100) self.assertTrue(cell.xf_index > 0) def test_calculated_cell(self): sheet2 = self.book.sheet_by_name('PROFILELEVELS') cell = sheet2.cell(1, 3) self.assertEqual(cell.ctype, xlrd.book.XL_CELL_NUMBER) self.assertAlmostEqual(cell.value, 265.131, places=3) self.assertTrue(cell.xf_index > 0) def test_merged_cells(self): book = xlrd.open_workbook(from_this_dir('xf_class.xls'), formatting_info=True) sheet3 = book.sheet_by_name('table2') row_lo, row_hi, col_lo, col_hi = sheet3.merged_cells[0] self.assertEqual(sheet3.cell(row_lo, col_lo).value, 'MERGED') self.assertEqual((row_lo, row_hi, col_lo, col_hi), (3, 7, 2, 5)) def test_merged_cells_xlsx(self): book = xlrd.open_workbook(from_this_dir('merged_cells.xlsx')) sheet1 = book.sheet_by_name('Sheet1') expected = [] got = sheet1.merged_cells self.assertEqual(expected, got) sheet2 = book.sheet_by_name('Sheet2') expected = [(0, 1, 0, 2)] got = sheet2.merged_cells self.assertEqual(expected, got) sheet3 = book.sheet_by_name('Sheet3') expected = [(0, 1, 0, 2), (0, 1, 2, 4), (1, 4, 0, 2), (1, 9, 2, 4)] got = sheet3.merged_cells self.assertEqual(expected, got) sheet4 = book.sheet_by_name('Sheet4') expected = [(0, 1, 0, 2), (2, 20, 0, 1), (1, 6, 2, 5)] got = sheet4.merged_cells self.assertEqual(expected, got) xlrd-0.9.4/tests/test_comments_excel.xlsx0000644000076500000240000003667312270704230021072 0ustar chrisstaff00000000000000PK!$H=q[Content_Types].xml (Vn0W?DV@J{lJ?8 X$5&`$*D%Q{fgw֛`g *։,-M,a? hH wF[cWQ! L˅G7Vȅ\A/0X7Xf>z]$Y[WP%LX))<W:C2ө :F@8y[}$ HS)D#ȉo2I쮝4 {(Pf@:6iƴΕ Udmbb̢nst :SÁ/!M^UI#g,rhҖ%Hp^:W[;u{+aPK!U0#L _rels/.rels (N0 HCnHLH!T$$@Jc?[iTb/Nú(A3b{jxVb"giaWl_xb#b4O r0Qahѓeܔ=P-<4Mox/}bN@;vCf ۨBI"c&\O8q"KH<ߊs@.h<⧄MdaT_PK!Bp'xl/_rels/workbook.xml.rels (j0E}=R"gS ٖ<~[`p[nL6AFWg߉wZ#!KRd-[SKx=<=L:kH@ fB 7cU KhBp'֑^XGUiAW3M/%}q\t_VU귞L`! QT8NVI$ _&ɮ l0t1g}sKڇ^:% K2LhgGxl/worksheets/sheet4.xmlQO0 ߑ"w_i%T-'oɲa)o~K0IwS orV_H$F >0}Qv ӆy' P 4,Z+a}o[:p{ Pt4ڐ:EpgKslT=EՑe)ӫri-ͪǓ< ;Ǵ 6U٨4Q}^h58X^1;"Y8#)rsg@7_$`8Z+TC(b[t`k]-3Vuǻ./hq;϶||PK!ʐ+Lexl/drawings/vmlDrawing4.vmlTn0 ?!qCU;@ѡm@7`BX,:QӴ0@"|z{$Auܷ^ZYmniVbɏF$ ۭ-At3ЗQJ7$:=!ԺyF.䅜wKth@XSȇ>灖R5@!,R0G7G %MU!K6a]Zz㛨'h}c2D"kiOxaCIB;y#U[%",E:xN^*b`d+ QT۝3[R媡1Fبy <[CmvW1t5$^fbl,Te?[\BmEThX: /O6v@?x5n.s} ѱWԣS'Gk-ΌO3H&qȍxvUtnpS#mzխ*ooHЈW__[p.$1/_Vئb*Ȧb9K\.y"ݱ; c$=>cG)>c*N":f=o7PK!'{+8xl/worksheets/sheet3.xmlMK@ a~(R.²ADg;i;l;)Y{-^%̓owߩ/gF (kc>޷7GcKGk  h8vUK=RC*M!󧾋o>XXum*pu"ԡ \P)2~ݯXM*̇$Skw;xcx|&Ӵ>X7̅G'cl^`.=6TGu$(Ysy_W= RDwjfN5X6cIPK!߰[3xl/worksheets/sheet2.xmlMO@ Hl®(tCWIF$@=NV|H\zǞ~_/$ϡUB͍] W'bhp@%RXSO!Ыmӊ#,#e) a|l?}a+5ޏPm*oYjK*@n&!վSuKKo7,Wa{G2e`exsTK"XW'lOeP10֚*n` >`+yPɺP-N hgL Mw b{H<v{p[@gF(1*̴yj@練 0}D^Q?I_)eO3^ dQR͎PK!PA]#xl/worksheets/_rels/sheet2.xml.relsJ1n "R^>@n}{SuKKoo>Y>$Xȧh`!]| loA)E4p@U{}|rD$*%9?(En`I&}*r-ˠu;;Mso3tʦ=:v{p[gF(1ʴe@6 wS 꼆>Lbg"WOe}崸S.>2gd^ dǣE PK!w#xl/worksheets/_rels/sheet3.xml.relsj1 E>$RJ<لB%cktrPh;;I}I𘢁n@Qtɏ7p:,@q)E2p!]xؾd,0fVBl`5?#(X)SK%*m1[f{uY?( `&&Xǽ\4[=&ʰnV4R?,*x@^g"qM.L=Fm^uO8o[LNbk]b?;t3 3#reSQ ws)+y%5~$^w<_?R_NK:PzRtC' (h?PK! Kfxl/drawings/vmlDrawing3.vmlTMo0 C.Ic'>T;@ѡm6`BX,:lia2H8*'A<4^JYeniV`ɷoF$ ۭ-@ ?@Wk>'CJ]GC.$R0G'G %jMe.̀uã8>܃xDV*\J$F z%  uI+/j^+ RaՅIE&;DQnvlI>]/V5݌1Z1d *M>+:l vc*1'bk+a˫-_Fl PUw`[c > [#bϜPގ8={A<\hRpfl4iG*73%Dfl{};wu6liNݹ< 4:[e 9`%|0NEd*Sq,w5nY=.@8EO$f㙩1aguC͖ePK!sY3xl/worksheets/sheet1.xmlN@ Hw6EX%AB+TIF$۔>}D@^cg~$ϡ&GƇǻ+pI148p^)MuzRL,/'RgJU>RӈiÑUZRY>C/و>JgܶǑO V9-dUiJnV25nQ]\C-W]f;U!<95"o( &\qy̪QdU@9cBpqMZax !%703 ܻҘB%5;UnoU]O )7;D{a?Aa,\M2=^qh=_âěq7e {N>`Tu .+(hInOgZFc;+A$x_]'A[je1ڽ߬ft9lnv}'hrOʺ ϊ`VFvd9}|X"/>-6 vx| w3 iCۯ"<3>y/PK!Mgxl/drawings/vmlDrawing1.vmlTMo0 C.Ic{>T@ѡm6`AX,:$iaX2H:+~M;WiU;_/$v"-3S-oe<(`LQ_d(+uX1Q̨2z~a5QWe29#n*ۊwKCڴLz.n5<-v*cTJ bA*Fp_q 6n0Yv'=KלŃgbANȍG}D5z"].U7S/C(E|}ͮFZG8tfcRX[[zt7P$SN45+~ipRvM+'jijd?luk3k\j<, ΓU iQfa˕pfD0oVKʎ|ً6~Ά^e6sWinX>/_;mCM_·9KWsB/9u[$ݒ{pDe2as 4B|ڇyA8 PK!%Sxl/theme/theme1.xmlYOo6w tom'uرMniXS@I}úa0l+t&[HJKՇD"|#uڃC"$q۫]z>8h{wK cxLޜH]ś*$A>J%aACMʈJ&M;4Be tY>c~4$ &^ L1bma]ut(gZ[Wvr2u{`M,EF,2nQ%[NJeD >֗f}{7vtd%|JYw2Oڡ~J=L8-o|(<4 ժX}.@'d}.Fbo\C\ҼMT0 zSώt--g.—~?~xY'y92h!ы/ɋ>%mGEFD[t3q%'#qSgv 9feqwW@(^wdbh a8g.J pC*Xx8rbV`|XƻcǵYU3 Jݐ8b3+(QuK>QELKM2#'vi~ vlwu8+zHHJ:) ~L\E\O*t@G1lm~C*uG.R(:-ys^Di7QR8,b?SQ*q7C;+}ݧ;4pDZ K(NhwŘQ6㶷 [SYJ(p»g>X_xwu{\>k]Xy}钣M26PsFnJ'K,}䇦$Ǵ;@` >*8i"LI%\ xӕ=6u= r2f 3c (:jZ3sLs*UܚЅ ]M8kp6x"]$C<&>'eb. vJ|yXɾ8Ȯ]7R /=,.&'Qk5q&p(Kaݐ Sd›L17 jpSaS! 35'+ZzQ H )7 5)kdB|UtvaDξp|Fl&0_*3n'LE/pm&]8fIrS4d 7y` nίI R3U~cnrF:_*P}-p Tpl rۜ4LZéO !PLB]$K *++65vꦚeNƟf(MN1ߜ6&3(adE,Uz<{EUϲV)9Z[4^kd5!J?Q3qBoC~M m<.vpIYӦZY_p=al-Y}Nc͙ŋ4vjavl'S&A8|*~x1%M0g%<ҭPK! "xl/comments3.xmlQj0 F6^P衇D,7MU{<ȣ0y'FLǠ 0E{sz؃lBg\  n2 nHy֢7~9Mf.#E$|f]}\p-׼LT_{7 K+SPp׌7>G)xmt1>t8!W9N1Y$-2oVd!9+mb-4\`ZHFBEpYm ~]nnl|"ҿPK!_aJ9'xl/printerSettings/printerSettings1.binb003lZ #;CZΈ3`PK!v rxl/comments1.xmlTj0{v%)Bzz!}ao"~V nB>lWWܧT(EI1CӦ(VY+exEQIEy9l/C&۱#*sU`}Ru0I78fXZ?z.Be:kxA{B@YΧ\k~U;Im}312C'RlfZ )Gާ%6\nUPK!_aJ9'xl/printerSettings/printerSettings2.binb003lZ #;CZΈ3`PK!ig0xl/comments4.xmlD 0Dn=HE<vk ͦdWѿ7X73e z`>ua@!ձfz9vX<5~^P墬cH*[Dƽ\w<qDʗ6%t<& w1f \3Gz 9zY~B%l-֠`St.;ڳ PK!=Ӕ'xl/printerSettings/printerSettings3.binYMo0~!톄Z.m|ZKqѶ~~ 0^;Z D[iy?ĉÆ%W;_˃S!9~O($G  rC2<0 oV'+T>JGqrntr  0vS5o|ZReO4oXN + &@pe838*)?aÇL]׸Bœ bQҦPP Be6*$]g"qKt$.K V8w5:z׬e҂,1x᳉*7iZ5b%^kq :&m X,ˀe2` #ZPܳ % 7Pv#.#30sng; 熫uИXZ6 PK!m{xl/comments2.xmlTj!yݽ(iY (HDAűaٟۧBAdc ↙}"tNUŐ5!*Fns \)ó|v ҀT_.)GSWCFc!d42O{U\ʼeҽ}XZ=?!2^v f*hjjBz񏸙 gHYx8bDeNƥh&ҙ̸&[."x#;M}vɹZ.Vޫb PK!r$@QdocProps/core.xml (|QK0C{d:CہʞV&n+6iHi{I<:F&EE#+{(rkFCвɄajW@Ҏ awIp narx.f$RimP ^VzeT?)- Fc۶I1B~?/oq[ @E&oliWs 1WzRq(`CܳI*f1 -霥lN>@CCO\L%MYJlJ<ܗPK!_EbdocProps/app.xml (n0 ,@VQzذI&ӱPY2DHm$q2(ďn:Hb(| B<| 1@!V6)fl5Qm ˁ*_^ƪr}m \g PByӞ Mh{>|[mK1V=,x%b-s%WÚue/MLfcӚpd}s]7\'ն6 J^I$#&d]TV_e|1 S9PK-!$H=q[Content_Types].xmlPK-!U0#L _rels/.relsPK-!Bp'xl/_rels/workbook.xml.relsPK-!}P~xl/workbook.xmlPK-!>hgG xl/worksheets/sheet4.xmlPK-!ʐ+Le8 xl/drawings/vmlDrawing4.vmlPK-!'{+8xl/worksheets/sheet3.xmlPK-!߰[3+xl/worksheets/sheet2.xmlPK-!8;=]#xl/worksheets/_rels/sheet1.xml.relsPK-!PA]#xl/worksheets/_rels/sheet2.xml.relsPK-!w#xl/worksheets/_rels/sheet3.xml.relsPK-!-{]#6xl/worksheets/_rels/sheet4.xml.relsPK-! Kfhxl/drawings/vmlDrawing3.vmlPK-!sY3xl/worksheets/sheet1.xmlPK-!1)B@8 {xl/styles.xmlPK-!Mgxl/drawings/vmlDrawing1.vmlPK-!@Qhlxl/drawings/vmlDrawing2.vmlPK-!%S!xl/theme/theme1.xmlPK-! "(xl/comments3.xmlPK-!_aJ9')xl/printerSettings/printerSettings1.binPK-!v rm*xl/comments1.xmlPK-!_aJ9'+xl/printerSettings/printerSettings2.binPK-!ig0 ,xl/comments4.xmlPK-!=Ӕ',xl/printerSettings/printerSettings3.binPK-!m{/xl/comments2.xmlPK-!r$@Q0docProps/core.xmlPK-!_Ebi3docProps/app.xmlPKo66xlrd-0.9.4/tests/test_comments_gdocs.xlsx0000644000076500000240000001013312270704230021050 0ustar chrisstaff00000000000000PK=@Bxl/comments1.xml]N@ DXNr@J@m"eڭc$n3M* K_I >?^ 4W_wM0-;DG.P[!sێKctd2C=bI G:XDO>y a^_i;]ޭ (Et Wj9PKHHI+PK=@Bxl/drawings/vmlDrawing1.vmlRێ02T(jBH+Wl Dzi3N.3g\d U\˛3$t=5~zYD:aUMr7|@uG9(+ xCI O**)5yU dl>zZJGk<^KqǹtT+|<TvuWFaI՝u蒳R`U&O*bkTzĪ8dU.09G._t'pS%1#mez GCQGFt)Z*ᯟkc$di f3^1vp`reGΖ;#O#958 HCDTM# }P>*`T5wʘHo4TprV+O_I{i$aK=[amg_ LB޺L"1R JkPK`#>PK=@Bxl/worksheets/sheet1.xml͎0`߁.U.<1mٟs-4߂Jp6N~Nm `@E8'hEB c:c5$lӧAs% <@ۄU wr494*&4"3y.8 B H漾-Ee4ՎpJpsrT77ZAH)FS]<G!^朐Ƙ ݙ4Q3_냽'Hjfh_ӸGh8Fx,!sBӘj}?LkP(JG䬖 \sp p{9c 㗝aD;C5NPKq\PK=@B#xl/worksheets/_rels/sheet1.xml.relsj1 E`5E)%lB 0aktr:Z袻+=h9E9V780xwϠPp.,^$BJI/b'$:&u31Ⱦȸj'̷ mr.ӽ4u$-KNRSjD,J[\'R]j:2n4N]Իl&ٷXꜚ94(YI $KO`{Sx菧ȧnR L^]_!2;PK6bPK=@Bxl/workbook.xmlKn @O; I[Ue;R6U 0QX qwؖl10o0s_rxǂ|,6a^K<wS‰Qǂ)5jpO;UN&Zƣ&X$gj&4YUeuv DXGpΨ0TiHdt z͝Sss dCcM^-9l`,&kNFp~['||TL8k9WeqY?5ug^:jkd7[ek,p3C^p2'"ŕ/#Ƣ?PKm<PK=@Bxl/_rels/workbook.xml.relsJ0wsiWM"^>@HM6 3Oެb{ 0AyRHβN- cdOM;|IHw%3tƭJ|rF |糫rX&;*yRTH.O*-|+@a(n.Kt+ؘ| #`Ur4 R˭Lƾګ?D4? "3; MQ^\{F#{fl7HD "1Y#"suݓCy{Pκ=;ޯo B߁Iv5|sC>PK[22PK=@BHHI+xl/comments1.xmlPK=@B`#>xl/drawings/vmlDrawing1.vmlPK=@Bq\)xl/worksheets/sheet1.xmlPK=@BWi#xl/worksheets/_rels/sheet1.xml.relsPK=@Bttxl/sharedStrings.xmlPK=@B6b xl/styles.xmlPK=@Bm<xl/workbook.xmlPK=@B&5- xl/_rels/workbook.xml.relsPK=@Bi뚲( M _rels/.relsPK=@B[228 [Content_Types].xmlPK  xlrd-0.9.4/tests/test_formats.py0000644000076500000240000000573712155372403017174 0ustar chrisstaff00000000000000 # -*- coding: utf-8 -*- # Portions Copyright (C) 2010, Manfred Moitzi under a BSD licence from unittest import TestCase import sys import os import xlrd if sys.version_info[0] >= 3: def u(s): return s else: def u(s): return s.decode('utf-8') from .base import from_this_dir class TestCellContent(TestCase): def setUp(self): self.book = xlrd.open_workbook(from_this_dir('Formate.xls'), formatting_info=True) self.sheet = self.book.sheet_by_name(u('Blätt1')) def test_text_cells(self): for row, name in enumerate([u('Huber'), u('Äcker'), u('Öcker')]): cell = self.sheet.cell(row, 0) self.assertEqual(cell.ctype, xlrd.book.XL_CELL_TEXT) self.assertEqual(cell.value, name) self.assertTrue(cell.xf_index > 0) def test_date_cells(self): # see also 'Dates in Excel spreadsheets' in the documentation # convert: xldate_as_tuple(float, book.datemode) -> (year, month, # day, hour, minutes, seconds) for row, date in [(0, 2741.), (1, 38406.), (2, 32266.)]: cell = self.sheet.cell(row, 1) self.assertEqual(cell.ctype, xlrd.book.XL_CELL_DATE) self.assertEqual(cell.value, date) self.assertTrue(cell.xf_index > 0) def test_time_cells(self): # see also 'Dates in Excel spreadsheets' in the documentation # convert: xldate_as_tuple(float, book.datemode) -> (year, month, # day, hour, minutes, seconds) for row, time in [(3, .273611), (4, .538889), (5, .741123)]: cell = self.sheet.cell(row, 1) self.assertEqual(cell.ctype, xlrd.book.XL_CELL_DATE) self.assertAlmostEqual(cell.value, time, places=6) self.assertTrue(cell.xf_index > 0) def test_percent_cells(self): for row, time in [(6, .974), (7, .124)]: cell = self.sheet.cell(row, 1) self.assertEqual(cell.ctype, xlrd.book.XL_CELL_NUMBER) self.assertAlmostEqual(cell.value, time, places=3) self.assertTrue(cell.xf_index > 0) def test_currency_cells(self): for row, time in [(8, 1000.30), (9, 1.20)]: cell = self.sheet.cell(row, 1) self.assertEqual(cell.ctype, xlrd.book.XL_CELL_NUMBER) self.assertAlmostEqual(cell.value, time, places=2) self.assertTrue(cell.xf_index > 0) def test_get_from_merged_cell(self): sheet = self.book.sheet_by_name(u('ÖÄÜ')) cell = sheet.cell(2, 2) self.assertEqual(cell.ctype, xlrd.book.XL_CELL_TEXT) self.assertEqual(cell.value, 'MERGED CELLS') self.assertTrue(cell.xf_index > 0) def test_ignore_diagram(self): sheet = self.book.sheet_by_name(u('Blätt3')) cell = sheet.cell(0, 0) self.assertEqual(cell.ctype, xlrd.book.XL_CELL_NUMBER) self.assertEqual(cell.value, 100) self.assertTrue(cell.xf_index > 0) xlrd-0.9.4/tests/test_formulas.py0000644000076500000240000000442312155372403017340 0ustar chrisstaff00000000000000 # -*- coding: utf-8 -*- # Portions Copyright (C) 2010, Manfred Moitzi under a BSD licence from unittest import TestCase import os import sys import xlrd from .base import from_this_dir try: ascii except NameError: # For Python 2 def ascii(s): a = repr(s) if a.startswith(('u"', "u'")): a = a[1:] return a class TestFormulas(TestCase): def setUp(self): book = xlrd.open_workbook(from_this_dir('formula_test_sjmachin.xls')) self.sheet = book.sheet_by_index(0) def get_value(self, col, row): return ascii(self.sheet.col_values(col)[row]) def test_cell_B2(self): self.assertEqual( self.get_value(1, 1), r"'\u041c\u041e\u0421\u041a\u0412\u0410 \u041c\u043e\u0441\u043a\u0432\u0430'" ) def test_cell_B3(self): self.assertEqual(self.get_value(1, 2), '0.14285714285714285') def test_cell_B4(self): self.assertEqual(self.get_value(1, 3), "'ABCDEF'") def test_cell_B5(self): self.assertEqual(self.get_value(1, 4), "''") def test_cell_B6(self): self.assertEqual(self.get_value(1, 5), '1') def test_cell_B7(self): self.assertEqual(self.get_value(1, 6), '7') def test_cell_B8(self): self.assertEqual( self.get_value(1, 7), r"'\u041c\u041e\u0421\u041a\u0412\u0410 \u041c\u043e\u0441\u043a\u0432\u0430'" ) class TestNameFormulas(TestCase): def setUp(self): book = xlrd.open_workbook(from_this_dir('formula_test_names.xls')) self.sheet = book.sheet_by_index(0) def get_value(self, col, row): return ascii(self.sheet.col_values(col)[row]) def test_unaryop(self): self.assertEqual(self.get_value(1, 1), '-7.0') def test_attrsum(self): self.assertEqual(self.get_value(1, 2), '4.0') def test_func(self): self.assertEqual(self.get_value(1, 3), '6.0') def test_func_var_args(self): self.assertEqual(self.get_value(1, 4), '3.0') def test_if(self): self.assertEqual(self.get_value(1, 5), "'b'") def test_choose(self): self.assertEqual(self.get_value(1, 6), "'C'") xlrd-0.9.4/tests/test_open_workbook.py0000644000076500000240000000241612155372403020366 0ustar chrisstaff00000000000000from unittest import TestCase import os from xlrd import open_workbook from .base import from_this_dir class TestOpen(TestCase): # test different uses of open_workbook def test_names_demo(self): # For now, we just check this doesn't raise an error. open_workbook( from_this_dir(os.path.join('..','xlrd','examples','namesdemo.xls')) ) def test_ragged_rows_tidied_with_formatting(self): # For now, we just check this doesn't raise an error. open_workbook(from_this_dir('issue20.xls'), formatting_info=True) def test_BYTES_X00(self): # For now, we just check this doesn't raise an error. open_workbook(from_this_dir('picture_in_cell.xls'), formatting_info=True) def test_xlsx_simple(self): # For now, we just check this doesn't raise an error. open_workbook(from_this_dir('text_bar.xlsx')) # we should make assertions here that data has been # correctly processed. def test_xlsx(self): # For now, we just check this doesn't raise an error. open_workbook(from_this_dir('reveng1.xlsx')) # we should make assertions here that data has been # correctly processed. xlrd-0.9.4/tests/test_sheet.py0000644000076500000240000001062112551374567016632 0ustar chrisstaff00000000000000# Portions Copyright (C) 2010, Manfred Moitzi under a BSD licence from unittest import TestCase import os import sys import types import unittest import xlrd from .base import from_this_dir SHEETINDEX = 0 NROWS = 15 NCOLS = 13 ROW_ERR = NROWS + 10 COL_ERR = NCOLS + 10 class TestSheet(TestCase): sheetnames = ['PROFILEDEF', 'AXISDEF', 'TRAVERSALCHAINAGE', 'AXISDATUMLEVELS', 'PROFILELEVELS'] def setUp(self): self.book = xlrd.open_workbook(from_this_dir('profiles.xls'), formatting_info=True) def check_sheet_function(self, function): self.assertTrue(function(0, 0)) self.assertTrue(function(NROWS-1, NCOLS-1)) def check_sheet_function_index_error(self, function): self.assertRaises(IndexError, function, ROW_ERR, 0) self.assertRaises(IndexError, function, 0, COL_ERR) def check_col_slice(self, col_function): _slice = col_function(0, 2, NROWS-2) self.assertEqual(len(_slice), NROWS-4) def check_row_slice(self, row_function): _slice = row_function(0, 2, NCOLS-2) self.assertEqual(len(_slice), NCOLS-4) def test_nrows(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.assertEqual(sheet.nrows, NROWS) def test_ncols(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.assertEqual(sheet.ncols, NCOLS) def test_cell(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.assertNotEqual(xlrd.empty_cell, sheet.cell(0, 0)) self.assertNotEqual(xlrd.empty_cell, sheet.cell(NROWS-1, NCOLS-1)) def test_cell_error(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.check_sheet_function_index_error(sheet.cell) def test_cell_type(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.check_sheet_function(sheet.cell_type) def test_cell_type_error(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.check_sheet_function_index_error(sheet.cell_type) def test_cell_value(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.check_sheet_function(sheet.cell_value) def test_cell_value_error(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.check_sheet_function_index_error(sheet.cell_value) def test_cell_xf_index(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.check_sheet_function(sheet.cell_xf_index) def test_cell_xf_index_error(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.check_sheet_function_index_error(sheet.cell_xf_index) def test_col(self): sheet = self.book.sheet_by_index(SHEETINDEX) col = sheet.col(0) self.assertEqual(len(col), NROWS) def test_row(self): sheet = self.book.sheet_by_index(SHEETINDEX) row = sheet.row(0) self.assertEqual(len(row), NCOLS) def test_get_rows(self): sheet = self.book.sheet_by_index(SHEETINDEX) rows = sheet.get_rows() self.assertTrue(isinstance(rows, types.GeneratorType), True) self.assertEqual(len(list(rows)), sheet.nrows) def test_col_slice(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.check_col_slice(sheet.col_slice) def test_col_types(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.check_col_slice(sheet.col_types) def test_col_values(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.check_col_slice(sheet.col_values) def test_row_slice(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.check_row_slice(sheet.row_slice) def test_row_types(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.check_row_slice(sheet.col_types) def test_row_values(self): sheet = self.book.sheet_by_index(SHEETINDEX) self.check_col_slice(sheet.row_values) class TestSheetRagged(TestCase): def test_read_ragged(self): book = xlrd.open_workbook(from_this_dir('ragged.xls'), ragged_rows=True) sheet = book.sheet_by_index(0) self.assertEqual(sheet.row_len(0), 3) self.assertEqual(sheet.row_len(1), 2) self.assertEqual(sheet.row_len(2), 1) self.assertEqual(sheet.row_len(3), 4) self.assertEqual(sheet.row_len(4), 4) xlrd-0.9.4/tests/test_workbook.py0000644000076500000240000000271312155372403017345 0ustar chrisstaff00000000000000# Portions Copyright (C) 2010, Manfred Moitzi under a BSD licence from unittest import TestCase import os import sys from xlrd import open_workbook from xlrd.book import Book from xlrd.sheet import Sheet from .base import from_this_dir class TestWorkbook(TestCase): sheetnames = ['PROFILEDEF', 'AXISDEF', 'TRAVERSALCHAINAGE', 'AXISDATUMLEVELS', 'PROFILELEVELS'] def setUp(self): self.book = open_workbook(from_this_dir('profiles.xls')) def test_open_workbook(self): self.assertTrue(isinstance(self.book, Book)) def test_nsheets(self): self.assertEqual(self.book.nsheets, 5) def test_sheet_by_name(self): for name in self.sheetnames: sheet = self.book.sheet_by_name(name) self.assertTrue(isinstance(sheet, Sheet)) self.assertEqual(name, sheet.name) def test_sheet_by_index(self): for index in range(5): sheet = self.book.sheet_by_index(index) self.assertTrue(isinstance(sheet, Sheet)) self.assertEqual(sheet.name, self.sheetnames[index]) def test_sheets(self): sheets = self.book.sheets() for index, sheet in enumerate(sheets): self.assertTrue(isinstance(sheet, Sheet)) self.assertEqual(sheet.name, self.sheetnames[index]) def test_sheet_names(self): self.assertEqual(self.sheetnames, self.book.sheet_names()) xlrd-0.9.4/tests/test_xldate.py0000644000076500000240000000452012155372403016767 0ustar chrisstaff00000000000000#!/usr/bin/env python # Author: mozman # Purpose: test xldate.py # Created: 04.12.2010 # Copyright (C) 2010, Manfred Moitzi # License: BSD licence import sys import unittest from xlrd import xldate DATEMODE = 0 # 1900-based class TestXLDate(unittest.TestCase): def test_date_as_tuple(self): date = xldate.xldate_as_tuple(2741., DATEMODE) self.assertEqual(date, (1907, 7, 3, 0, 0, 0)) date = xldate.xldate_as_tuple(38406., DATEMODE) self.assertEqual(date, (2005, 2, 23, 0, 0, 0)) date = xldate.xldate_as_tuple(32266., DATEMODE) self.assertEqual(date, (1988, 5, 3, 0, 0, 0)) def test_time_as_tuple(self): time = xldate.xldate_as_tuple(.273611, DATEMODE) self.assertEqual(time, (0, 0, 0, 6, 34, 0)) time = xldate.xldate_as_tuple(.538889, DATEMODE) self.assertEqual(time, (0, 0, 0, 12, 56, 0)) time = xldate.xldate_as_tuple(.741123, DATEMODE) self.assertEqual(time, (0, 0, 0, 17, 47, 13)) def test_xldate_from_date_tuple(self): date = xldate.xldate_from_date_tuple( (1907, 7, 3), DATEMODE ) self.assertAlmostEqual(date, 2741.) date = xldate.xldate_from_date_tuple( (2005, 2, 23), DATEMODE ) self.assertAlmostEqual(date, 38406.) date = xldate.xldate_from_date_tuple( (1988, 5, 3), DATEMODE ) self.assertAlmostEqual(date, 32266.) def test_xldate_from_time_tuple(self): time = xldate.xldate_from_time_tuple( (6, 34, 0) ) self.assertAlmostEqual(time, .273611, places=6) time = xldate.xldate_from_time_tuple( (12, 56, 0) ) self.assertAlmostEqual(time, .538889, places=6) time = xldate.xldate_from_time_tuple( (17, 47, 13) ) self.assertAlmostEqual(time, .741123, places=6) def test_xldate_from_datetime_tuple(self): date = xldate.xldate_from_datetime_tuple( (1907, 7, 3, 6, 34, 0), DATEMODE) self.assertAlmostEqual(date, 2741.273611, places=6) date = xldate.xldate_from_datetime_tuple( (2005, 2, 23, 12, 56, 0), DATEMODE) self.assertAlmostEqual(date, 38406.538889, places=6) date = xldate.xldate_from_datetime_tuple( (1988, 5, 3, 17, 47, 13), DATEMODE) self.assertAlmostEqual(date, 32266.741123, places=6) if __name__=='__main__': unittest.main() xlrd-0.9.4/tests/test_xldate_to_datetime.py0000644000076500000240000001376012320534304021345 0ustar chrisstaff00000000000000############################################################################### # # Tests for the xlrd xldate.xldate_as_datetime() function. # import unittest from datetime import datetime from xlrd import xldate not_1904 = False is_1904 = True class TestConvertToDateTime(unittest.TestCase): """ Testcases to test the _xldate_to_datetime() function against dates extracted from Excel files, with 1900/1904 epochs. """ def test_dates_and_times_1900_epoch(self): """ Test the _xldate_to_datetime() function for dates and times in the Excel standard 1900 epoch. """ # Test Excel dates strings and corresponding serial date numbers taken # from an Excel file. excel_dates = [ # Excel's 0.0 date in the 1900 epoch is 1 day before 1900. ('1899-12-31T00:00:00.000', 0), # Date/time before the false Excel 1900 leapday. ('1900-02-28T02:11:11.986', 59.09111094906), # Date/time after the false Excel 1900 leapday. ('1900-03-01T05:46:44.068', 61.24078782403), # Random date/times in Excel's 0-9999.9999+ range. ('1982-08-25T00:15:20.213', 30188.010650613425), ('2065-04-19T00:16:48.290', 60376.011670023145), ('3222-06-11T03:08:08.251', 483014.13065105322), ('4379-08-03T06:14:48.580', 905652.26028449077), ('5949-12-30T12:59:54.263', 1479232.5416002662), # End of Excel's date range. ('9999-12-31T23:59:59.000', 2958465.999988426), ] # Convert the Excel date strings to datetime objects and compare # against the dateitme return value of xldate.xldate_as_datetime(). for excel_date in excel_dates: exp = datetime.strptime(excel_date[0], "%Y-%m-%dT%H:%M:%S.%f") got = xldate.xldate_as_datetime(excel_date[1], not_1904) self.assertEqual(got, exp) def test_dates_only_1900_epoch(self): """ Test the _xldate_to_datetime() function for dates in the Excel standard 1900 epoch. """ # Test Excel dates strings and corresponding serial date numbers taken # from an Excel file. excel_dates = [ # Excel's day 0 in the 1900 epoch is 1 day before 1900. ('1899-12-31', 0), # Excel's day 1 in the 1900 epoch. ('1900-01-01', 1), # Date/time before the false Excel 1900 leapday. ('1900-02-28', 59), # Date/time after the false Excel 1900 leapday. ('1900-03-01', 61), # Random date/times in Excel's 0-9999.9999+ range. ('1902-09-27', 1001), ('1999-12-31', 36525), ('2000-01-01', 36526), ('4000-12-31', 767376), ('4321-01-01', 884254), ('9999-01-01', 2958101), # End of Excel's date range. ('9999-12-31', 2958465), ] # Convert the Excel date strings to datetime objects and compare # against the dateitme return value of xldate.xldate_as_datetime(). for excel_date in excel_dates: exp = datetime.strptime(excel_date[0], "%Y-%m-%d") got = xldate.xldate_as_datetime(excel_date[1], not_1904) self.assertEqual(got, exp) def test_dates_only_1904_epoch(self): """ Test the _xldate_to_datetime() function for dates in the Excel Mac/1904 epoch. """ # Test Excel dates strings and corresponding serial date numbers taken # from an Excel file. excel_dates = [ # Excel's day 0 in the 1904 epoch. ('1904-01-01', 0), # Random date/times in Excel's 0-9999.9999+ range. ('1904-01-31', 30), ('1904-08-31', 243), ('1999-02-28', 34757), ('1999-12-31', 35063), ('2000-01-01', 35064), ('2400-12-31', 181526), ('4000-01-01', 765549), ('9999-01-01', 2956639), # End of Excel's date range. ('9999-12-31', 2957003), ] # Convert the Excel date strings to datetime objects and compare # against the dateitme return value of xldate.xldate_as_datetime(). for excel_date in excel_dates: exp = datetime.strptime(excel_date[0], "%Y-%m-%d") got = xldate.xldate_as_datetime(excel_date[1], is_1904) self.assertEqual(got, exp) def test_times_only(self): """ Test the _xldate_to_datetime() function for times only, i.e, the fractional part of the Excel date when the serial date is 0. """ # Test Excel dates strings and corresponding serial date numbers taken # from an Excel file. The 1899-12-31 date is Excel's day 0. excel_dates = [ # Random times in Excel's 0-0.9999+ range for 1 day. ('1899-12-31T00:00:00.000', 0), ('1899-12-31T00:15:20.213', 1.0650613425925924E-2), ('1899-12-31T02:24:37.095', 0.10042934027777778), ('1899-12-31T04:56:35.792', 0.2059698148148148), ('1899-12-31T07:31:20.407', 0.31343063657407405), ('1899-12-31T09:37:23.945', 0.40097158564814817), ('1899-12-31T12:09:48.602', 0.50681252314814818), ('1899-12-31T14:37:57.451', 0.60969271990740748), ('1899-12-31T17:04:02.415', 0.71113906250000003), ('1899-12-31T19:14:24.673', 0.80167445601851861), ('1899-12-31T21:39:05.944', 0.90215212962962965), ('1899-12-31T23:17:12.632', 0.97028509259259266), ('1899-12-31T23:59:59.999', 0.99999998842592586), ] # Convert the Excel date strings to datetime objects and compare # against the dateitme return value of xldate.xldate_as_datetime(). for excel_date in excel_dates: exp = datetime.strptime(excel_date[0], "%Y-%m-%dT%H:%M:%S.%f") got = xldate.xldate_as_datetime(excel_date[1], not_1904) self.assertEqual(got, exp) xlrd-0.9.4/tests/test_xlsx_comments.py0000644000076500000240000000275512270704230020414 0ustar chrisstaff00000000000000from unittest import TestCase import os from xlrd import open_workbook from .base import from_this_dir class TestXlsxComments(TestCase): def test_excel_comments(self): book = open_workbook(from_this_dir('test_comments_excel.xlsx')) sheet = book.sheet_by_index(0) note_map = sheet.cell_note_map self.assertEqual(len(note_map), 1) self.assertEqual(note_map[(0, 1)].text, 'hello') def test_excel_comments_multiline(self): book = open_workbook(from_this_dir('test_comments_excel.xlsx')) sheet = book.sheet_by_index(1) note_map = sheet.cell_note_map self.assertEqual(note_map[(1, 2)].text, '1st line\n2nd line') def test_excel_comments_two_t_elements(self): book = open_workbook(from_this_dir('test_comments_excel.xlsx')) sheet = book.sheet_by_index(2) note_map = sheet.cell_note_map self.assertEqual(note_map[(0, 0)].text, 'Author:\nTwo t elements') def test_excel_comments_no_t_elements(self): book = open_workbook(from_this_dir('test_comments_excel.xlsx')) sheet = book.sheet_by_index(3) note_map = sheet.cell_note_map self.assertEqual(note_map[(0,0)].text, '') def test_gdocs_comments(self): book = open_workbook(from_this_dir('test_comments_gdocs.xlsx')) sheet = book.sheet_by_index(0) note_map = sheet.cell_note_map self.assertEqual(len(note_map), 1) self.assertEqual(note_map[(0, 1)].text, 'Just a test') xlrd-0.9.4/tests/test_xlsx_parse.py0000644000076500000240000000276612551374567017725 0ustar chrisstaff00000000000000############################################################################### # # Test the parsing of problematic xlsx files from bug reports. # import unittest import xlrd from .base import from_this_dir class TestXlsxParse(unittest.TestCase): # Test parsing of problematic xlsx files. These are usually submitted # as part of bug reports as noted below. def test_for_github_issue_96(self): # Test for non-Excel file with forward slash file separator and # lowercase names. https://github.com/python-excel/xlrd/issues/96 workbook = xlrd.open_workbook(from_this_dir('apachepoi_49609.xlsx')) worksheet = workbook.sheet_by_index(0) # Test reading sample data from the worksheet. cell = worksheet.cell(0, 1) self.assertEqual(cell.value, 'Cycle') self.assertEqual(cell.ctype, xlrd.book.XL_CELL_TEXT) cell = worksheet.cell(1, 1) self.assertEqual(cell.value, 1) self.assertEqual(cell.ctype, xlrd.book.XL_CELL_NUMBER) def test_for_github_issue_101(self): # Test for non-Excel file with forward slash file separator # https://github.com/python-excel/xlrd/issues/101 workbook = xlrd.open_workbook(from_this_dir('self_evaluation_report_2014-05-19.xlsx')) worksheet = workbook.sheet_by_index(0) # Test reading sample data from the worksheet. cell = worksheet.cell(0, 0) self.assertEqual(cell.value, 'one') self.assertEqual(cell.ctype, xlrd.book.XL_CELL_TEXT) xlrd-0.9.4/tests/text_bar.xlsx0000644000076500000240000002000412155372403016620 0ustar chrisstaff00000000000000PK!q9+p[Content_Types].xml (̔MN0H!%nj?Kؓƪc[g PTDQ4f|[d9g#NiCz*a|v~6}y-欌pJ`t6i5lDV,D"5<qFz,m k "RrkBKPN+Zjqg[ 2Y+wBMLDq}̨iKǡ]?wVoέNMUB}%-iփ@\J=IB̺iޛ1"o^AAGc,ER'?_rM?;67PK!U0#L _rels/.rels (N0 HCnHLH!T$$@Jc?[iTb/Nú(A3b{jxVb"giaWl_xb#b4O r0Qahѓeܔ=P-<4Mox/}bN@;vCf ۨBI"c&\O8q"KH<ߊs@.h<⧄MdaT_PK! (xl/_rels/workbook.xml.rels (j0 }qne:A[&Q6'o?C@.$}?ЧjU%)Z(8>< ֶҝ`@CqNsD$%襤`)qm.cuy qB`b<_BYN.Ԙ0ǑJ&Kʇa8tPKPK!%Sxl/theme/theme1.xmlYOo6w tom'uرMniXS@I}úa0l+t&[HJKՇD"|#uڃC"$q۫]z>8h{wK cxLޜH]ś*$A>J%aACMʈJ&M;4Be tY>c~4$ &^ L1bma]ut(gZ[Wvr2u{`M,EF,2nQ%[NJeD >֗f}{7vtd%|JYw2Oڡ~J=L8-o|(<4 ժX}.@'d}.Fbo\C\ҼMT0 zSώt--g.—~?~xY'y92h!ы/ɋ>%mGEFD[t3q%'#qSgv 9feqwW@(^wdbh a8g.J pC*Xx8rbV`|XƻcǵYU3 Jݐ8b3+(QuK>QELKM2#'vi~ vlwu8+zHHJ:) ~L\E\O*t@G1lm~C*uG.R(:-ys^Di7QR8,b?SQ*q7C;+}ݧ;4pDZ K(NhwŘQ6㶷 [SYJ(p»g>X_xwu{\>k]Xy}钣M26PsFnJ'K,}䇦$Ǵ;@` >*8i"LI%\ xӕ=6u= r2f 3c (:jZ3sLs*UܚЅ ]M8kp6x"]$C<&>'eb. vJ|yXɾ8Ȯ]7R /=,.&'Qk5q&p(Kaݐ Sd›L17 jpSaS! 35'+ZzQ H )7 5)kdB|UtvaDξp|Fl&0_*3n'LE/pm&]8fIrS4d 7y` nίI R3U~cnrF:_*P}-p Tpl rۜ4LZéO !PLB]$K *++65vꦚeNƟf(MN1ߜ6&3(adE,Uz<{EUϲV)9Z[4^kd5!J?Q3qBoC~M m<.vpIYӦZY_p=al-Y}Nc͙ŋ4vjavl'S&A8|*~x1%M0g%<ҭPK!JK-xl/worksheets/sheet2.xmlPJ0 CMWY",m'm$YW޴eyyo^jĘ E }K} o۫{%5|cusyQ)Ӏ";TVRv@RA}f4E8"nZrV^txV?iqC$UO 3Ȧ0xLf1G⩫v;xC_8W/ f\U Ţz?b/PK!Įd xl/styles.xmlSj0BF mCa!@Ыl^hdgd;SXOO3ofAlL.`)),68J]DMR$%Jy$*:݇"!zMJ}ڤA6CYm@J XHI3[  g#9WGZ3hC̤%%qX &D67bݘeOU VE__}lKl4W =@<tPCNc7VL)yֹ /YodkYeŹg+7"ϻJzp@o볦9_Z ]Yp'mtXKy/ָX^)$Q ~Γ>LSNQPK!$a[=xl/worksheets/sheet1.xmlMO0 H(w14@S 1Mp@B|ݳmqIL U9D}:3b5\Q"+m\~l Z/x-./ѷ$X\D&}B|=Xu8uM{:&rVظh`]vX~w`iq`սEVi :l~jY,H@IP{)1<|Rr~zTIz0UW D8~Ë+>ͷS(x'fY ] NrAtaSZdH&L-Rl 8"`!jf.%',V62VpY_I+P 7×վa`n;koMcEϝXTLux`]䱡tC[P-X)Cc t8hMr&cu׾AJwLoX*Ɇٗcv7pqjNW *+  !"#$%&'()Root Entry F>ӓbYWorkbookKOle SummaryInformation( Oh+'0T(0 @Lmanfred@Y՜.+,0HP X`hp x  table1table2table3 Arbeitsbltter \pmanfred Ba==?8X@"1Calibri1Arial1Arial1Arial1 Calibri1Calibri1Calibri1Calibri1Calibri1 Calibri1?Calibri14Calibri1>Calibri1Calibri1Calibri1Calibri1<Calibri1Calibri1h8Cambria1,8Calibri18Calibri18Calibri14Calibri1 Calibri1 Calibri3#,##0\ " ";\-#,##0\ " "=#,##0\ " ";[Red]\-#,##0\ " "?#,##0.00\ " ";\-#,##0.00\ " "I"#,##0.00\ " ";[Red]\-#,##0.00\ " "q*6_-* #,##0\ " "_-;\-* #,##0\ " "_-;_-* "-"\ " "_-;_-@_-k)3_-* #,##0\ _ _-;\-* #,##0\ _ _-;_-* "-"\ _ _-;_-@_-,>_-* #,##0.00\ " "_-;\-* #,##0.00\ " "_-;_-* "-"??\ " "_-;_-@_-{+;_-* #,##0.00\ _ _-;\-* #,##0.00\ _ _-;_-* "-"??\ _ _-;_-@_-                                                                     + )     a>               P  P     ` , *   ff  H    H    H   " #     ! 0" 0  ||LV!}A} _ _-ef#0.0}A} _ _-ef#0.0}A} _ _-ef#0.0}A} _ _-ef#0.0}A} _ _-ef#0.0}A} _ _-ef #0.0}A} _ _-L#0.0}A} _ _-L#0.0}A} _ _-L#0.0}A} _ _-L#0.0}A} _ _-L#0.0}A} _ _-L #0.0}A} _ _-23#0.0}A} _ _-23#0.0}A} _ _-23#0.0}A} _ _-23#0.0}A}  _ _-23#0.0}A}! _ _-23 #0.0}A}" _ _-#0.0}A}# _ _-#0.0}A}$ _ _-#0.0}A}% _ _-#0.0}A}& _ _-#0.0}A}' _ _- #0.0}}( ???_ _-#0.0???-;_-????\ _ ???@_-@ ???}}) }_ _-#0.0-;_-?\ _ @_-@ }}, ??v_ _-̙#0.0-;_-?\ _ @_-@ }U}- _ _-#0.0-;_-}-}. _ _-}A}/ a_ _-#0.0}A}0 e_ _-#0.0}x}1_ _-#0-; ?\ @_}A}3 _ _-#0}-}4 _ _-}A}5 _ _-#0}A}6 _ _-?#0}A}7 _ _-23#0}-}8 _ _-}A}9 }_ _-#0}-}< _ _-}}= _ _-#0???-; ????\  ???@_-@ ???}d}J_ _-#0 P???-; P????\ 20% - Akzent1M 20% - Akzent1 ef % 20% - Akzent2M" 20% - Akzent2 ef % 20% - Akzent3M& 20% - Akzent3 ef % 20% - Akzent4M* 20% - Akzent4 ef % 20% - Akzent5M. 20% - Akzent5 ef % 20% - Akzent6M2 20% - Akzent6  ef % 40% - Akzent1M 40% - Akzent1 L % 40% - Akzent2M# 40% - Akzent2 L湸 % 40% - Akzent3M' 40% - Akzent3 L % 40% - Akzent4M+ 40% - Akzent4 L % 40% - Akzent5M/ 40% - Akzent5 L % 40% - Akzent6M3 40% - Akzent6  Lմ % 60% - Akzent1M 60% - Akzent1 23 % 60% - Akzent2M$ 60% - Akzent2 23ٗ % 60% - Akzent3M( 60% - Akzent3 23֚ % 60% - Akzent4M, 60% - Akzent4 23 % 60% - Akzent5M0 60% - Akzent5 23 %! 60% - Akzent6M4 60% - Akzent6  23 % "Akzent1AAkzent1 O % #Akzent2A!Akzent2 PM % $Akzent3A%Akzent3 Y % %Akzent4A)Akzent4 d % &Akzent5A-Akzent5 K % 'Akzent6A1Akzent6  F % (AusgabeyAusgabe  ???%????????? ???) Berechnung Berechnung  }% *$Dezimal+, Dezimal [0] ,EingabeyEingabe ̙ ??v%  -ErgebnisSErgebnis %OO.Erklrender TextG5Erklrender Text %/Gut9Gut  a% 0NeutralANeutral  e% 1Notizd Notiz  2$Prozent 3SchlechtCSchlecht  %&Standard4 berschrift= berschrift I}%5 berschrift 1O berschrift 1 I}%O6 berschrift 2O berschrift 2 I}%?7 berschrift 3O berschrift 3 I}%238 berschrift 4A berschrift 4 I}%9Verknpfte ZelleUVerknpfte Zelle }%:$Whrung;, Whrung [0]<Warnender TextC Warnender Text %=Zelle berprfenZelle berprfen  %????????? ???XTableStyleMedium9PivotStyleLight16`;table1,Gtable2pItable311d REDGREENBLUELEFTCENTERRIGHTTOPMIDDLEBOTTOM borderstyleMERGED- .Hcc PK![Content_Types].xmlj0Eжr(΢Iw},-j4 wP-t#bΙ{UTU^hd}㨫)*1P' ^W0)T9<l#$yi};~@(Hu* Dנz/0ǰ $ X3aZ,D0j~3߶b~i>3\`?/[G\!-Rk.sԻ..a濭?PK!֧6 _rels/.relsj0 }Q%v/C/}(h"O = C?hv=Ʌ%[xp{۵_Pѣ<1H0ORBdJE4b$q_6LR7`0̞O,En7Lib/SeеPK!kytheme/theme/themeManager.xml M @}w7c(EbˮCAǠҟ7՛K Y, e.|,H,lxɴIsQ}#Ր ֵ+!,^$j=GW)E+& 8PK!{#F theme/theme/theme1.xmlYo5#?Xs/&4(q|;q7P9"!! č*ĥ5C|HxgfYH߷=nR:A,Np8!v)U믾r tBSJp}$Z[KK*iPfn,d eI8B)_ZY^^_Je EwA26U,΂%>GVf"r Z%9 U$X,ۿ`%*q`mm)QŴ5ho^ݮ[~*za[uinIryڽ嶏_ym89z抇 _÷7{uoA>\\ox J8ƠAA@r*2," -Ba4ˈt !zrґd`87)ÌP\w7r{gO%Ϟ>9yɣO{76dq}_|g߾7UG@ D?~~S¨*&="{"ŽYӑ|@xK}==&=Y9Ѭ$;B𮐍 cx4}Fwj6 ٗv#ٵ+2V*=xf%‘g9k+k !c<0t~c tnf0(Ysܘ-pz۰r6Ĺ}U ''6<X@gǤ@=1 uص;XR1T'4qUO^O،`2h۾st+,stkBdq\`je{ؐSO`52:W1 ԉ,',Hl@o Y|N濋9GÆ5dXt")Ŵd bv9 d=&ʝ#zHuSlR;sA49xrHU{] ݝ fܔmCSvyY{1/fmV dV+EؿYj]ƚZ)Zq~8Y5D9ɐSƦV_ 1t+ &A6Nn9!T[NFkeN{JF9]5g>;//Rم=]F˞Q'kiI(㓍|W"`7zXɱMq"a ;$6 8[6p-*r8Ĭb}7$(<ʡ, J]Z90N}`/.yX?U.h`KIY$~KNFXY(V3H q eu8?RhdcΑhn`Ϙ r#cs+3b3<6V6(`T&9ӆ\V-gKo#0, x޷UJ$6Q^PK! ѐ'theme/theme/_rels/themeManager.xml.relsM 0wooӺ&݈Э5 6?$Q ,.aic21h:qm@RN;d`o7gK(M&$R(.1r'JЊT8V"AȻHu}|$b{P8g/]QAsم(#L[PK-![Content_Types].xmlPK-!֧6 +_rels/.relsPK-!kytheme/theme/themeManager.xmlPK-!{#F theme/theme/theme1.xmlPK-! ѐ' theme/theme/_rels/themeManager.xml.relsPK]    `EF  dMbP?_*+%,&ffffff?'ffffff?(333333?)333333?MCanon MX850 series Printer ߁ 4d A4BJDM @Rt,T`Op,T`OpRt,TT`Op,TT,TT`OpXX'  d Rt RtH1 Rt   Canon MX850 series Printer ߁ 4d A452<" d,, ` `? ` `?&`U  ,,,,@;  ; > ? @ A B C I D E H F G J Bx**>@  gg  BHH  dMbP?_*+%,&ffffff?'ffffff?(333333?)333333?" d,, ` `? ` `?&`U ,,,, K KK KKK KKK KKK <>@ gg  J.K  dMbP?_*+%,&ffffff?'ffffff?(333333?)333333?" d,, ` `? ` `?&`U ,,, > ? @ A B C (>@gg DocumentSummaryInformation8CompObjs F'Microsoft Office Excel 2003-Arbeitsbl.Biff8Excel.Sheet.89qxlrd-0.9.4/xlrd/0000755000076500000240000000000012551375765013721 5ustar chrisstaff00000000000000xlrd-0.9.4/xlrd/__init__.py0000644000076500000240000005162512551374567016042 0ustar chrisstaff00000000000000from os import path from .info import __VERSION__ #

    Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd

    #

    This module is part of the xlrd package, which is released under a # BSD-style licence.

    from . import licences ## #

    A Python module for extracting data from MS Excel (TM) spreadsheet files. #

    # Version 0.7.4 -- April 2012 #

    # #

    General information

    # #

    Acknowledgements

    # #

    # Development of this module would not have been possible without the document # "OpenOffice.org's Documentation of the Microsoft Excel File Format" # ("OOo docs" for short). # The latest version is available from OpenOffice.org in # PDF format # and # ODT format. # Small portions of the OOo docs are reproduced in this # document. A study of the OOo docs is recommended for those who wish a # deeper understanding of the Excel file layout than the xlrd docs can provide. #

    # #

    Backporting to Python 2.1 was partially funded by # # Journyx - provider of timesheet and project accounting solutions. # #

    # #

    Provision of formatting information in version 0.6.1 was funded by # # Simplistix Ltd. # #

    # #

    Unicode

    # #

    This module presents all text strings as Python unicode objects. # From Excel 97 onwards, text in Excel spreadsheets has been stored as Unicode. # Older files (Excel 95 and earlier) don't keep strings in Unicode; # a CODEPAGE record provides a codepage number (for example, 1252) which is # used by xlrd to derive the encoding (for same example: "cp1252") which is # used to translate to Unicode.

    # #

    If the CODEPAGE record is missing (possible if the file was created # by third-party software), xlrd will assume that the encoding is ascii, and keep going. # If the actual encoding is not ascii, a UnicodeDecodeError exception will be raised and # you will need to determine the encoding yourself, and tell xlrd: #

    #     book = xlrd.open_workbook(..., encoding_override="cp1252")
    # 

    #

    If the CODEPAGE record exists but is wrong (for example, the codepage # number is 1251, but the strings are actually encoded in koi8_r), # it can be overridden using the same mechanism. # The supplied runxlrd.py has a corresponding command-line argument, which # may be used for experimentation: #

    #     runxlrd.py -e koi8_r 3rows myfile.xls
    # 

    #

    The first place to look for an encoding ("codec name") is # # the Python documentation. #

    #
    # #

    Dates in Excel spreadsheets

    # #

    In reality, there are no such things. What you have are floating point # numbers and pious hope. # There are several problems with Excel dates:

    # #

    (1) Dates are not stored as a separate data type; they are stored as # floating point numbers and you have to rely on # (a) the "number format" applied to them in Excel and/or # (b) knowing which cells are supposed to have dates in them. # This module helps with (a) by inspecting the # format that has been applied to each number cell; # if it appears to be a date format, the cell # is classified as a date rather than a number. Feedback on this feature, # especially from non-English-speaking locales, would be appreciated.

    # #

    (2) Excel for Windows stores dates by default as the number of # days (or fraction thereof) since 1899-12-31T00:00:00. Excel for # Macintosh uses a default start date of 1904-01-01T00:00:00. The date # system can be changed in Excel on a per-workbook basis (for example: # Tools -> Options -> Calculation, tick the "1904 date system" box). # This is of course a bad idea if there are already dates in the # workbook. There is no good reason to change it even if there are no # dates in the workbook. Which date system is in use is recorded in the # workbook. A workbook transported from Windows to Macintosh (or vice # versa) will work correctly with the host Excel. When using this # module's xldate_as_tuple function to convert numbers from a workbook, # you must use the datemode attribute of the Book object. If you guess, # or make a judgement depending on where you believe the workbook was # created, you run the risk of being 1462 days out of kilter.

    # #

    Reference: # http://support.microsoft.com/default.aspx?scid=KB;EN-US;q180162

    # # #

    (3) The Excel implementation of the Windows-default 1900-based date system works on the # incorrect premise that 1900 was a leap year. It interprets the number 60 as meaning 1900-02-29, # which is not a valid date. Consequently any number less than 61 is ambiguous. Example: is 59 the # result of 1900-02-28 entered directly, or is it 1900-03-01 minus 2 days? The OpenOffice.org Calc # program "corrects" the Microsoft problem; entering 1900-02-27 causes the number 59 to be stored. # Save as an XLS file, then open the file with Excel -- you'll see 1900-02-28 displayed.

    # #

    Reference: http://support.microsoft.com/default.aspx?scid=kb;en-us;214326

    # #

    (4) The Macintosh-default 1904-based date system counts 1904-01-02 as day 1 and 1904-01-01 as day zero. # Thus any number such that (0.0 <= number < 1.0) is ambiguous. Is 0.625 a time of day (15:00:00), # independent of the calendar, # or should it be interpreted as an instant on a particular day (1904-01-01T15:00:00)? # The xldate_* functions in this module # take the view that such a number is a calendar-independent time of day (like Python's datetime.time type) for both # date systems. This is consistent with more recent Microsoft documentation # (for example, the help file for Excel 2002 which says that the first day # in the 1904 date system is 1904-01-02). # #

    (5) Usage of the Excel DATE() function may leave strange dates in a spreadsheet. Quoting the help file, # in respect of the 1900 date system: "If year is between 0 (zero) and 1899 (inclusive), # Excel adds that value to 1900 to calculate the year. For example, DATE(108,1,2) returns January 2, 2008 (1900+108)." # This gimmick, semi-defensible only for arguments up to 99 and only in the pre-Y2K-awareness era, # means that DATE(1899, 12, 31) is interpreted as 3799-12-31.

    # #

    For further information, please refer to the documentation for the xldate_* functions.

    # #

    Named references, constants, formulas, and macros

    # #

    # A name is used to refer to a cell, a group of cells, a constant # value, a formula, or a macro. Usually the scope of a name is global # across the whole workbook. However it can be local to a worksheet. # For example, if the sales figures are in different cells in # different sheets, the user may define the name "Sales" in each # sheet. There are built-in names, like "Print_Area" and # "Print_Titles"; these two are naturally local to a sheet. #

    # To inspect the names with a user interface like MS Excel, OOo Calc, # or Gnumeric, click on Insert/Names/Define. This will show the global # names, plus those local to the currently selected sheet. #

    # A Book object provides two dictionaries (name_map and # name_and_scope_map) and a list (name_obj_list) which allow various # ways of accessing the Name objects. There is one Name object for # each NAME record found in the workbook. Name objects have many # attributes, several of which are relevant only when obj.macro is 1. #

    # In the examples directory you will find namesdemo.xls which # showcases the many different ways that names can be used, and # xlrdnamesAPIdemo.py which offers 3 different queries for inspecting # the names in your files, and shows how to extract whatever a name is # referring to. There is currently one "convenience method", # Name.cell(), which extracts the value in the case where the name # refers to a single cell. More convenience methods are planned. The # source code for Name.cell (in __init__.py) is an extra source of # information on how the Name attributes hang together. #

    # #

    Name information is not extracted from files older than # Excel 5.0 (Book.biff_version < 50)

    # #

    Formatting

    # #

    Introduction

    # #

    This collection of features, new in xlrd version 0.6.1, is intended # to provide the information needed to (1) display/render spreadsheet contents # (say) on a screen or in a PDF file, and (2) copy spreadsheet data to another # file without losing the ability to display/render it.

    # #

    The Palette; Colour Indexes

    # #

    A colour is represented in Excel as a (red, green, blue) ("RGB") tuple # with each component in range(256). However it is not possible to access an # unlimited number of colours; each spreadsheet is limited to a palette of 64 different # colours (24 in Excel 3.0 and 4.0, 8 in Excel 2.0). Colours are referenced by an index # ("colour index") into this palette. # # Colour indexes 0 to 7 represent 8 fixed built-in colours: black, white, red, green, blue, # yellow, magenta, and cyan.

    # # The remaining colours in the palette (8 to 63 in Excel 5.0 and later) # can be changed by the user. In the Excel 2003 UI, Tools/Options/Color presents a palette # of 7 rows of 8 colours. The last two rows are reserved for use in charts.
    # The correspondence between this grid and the assigned # colour indexes is NOT left-to-right top-to-bottom.
    # Indexes 8 to 15 correspond to changeable # parallels of the 8 fixed colours -- for example, index 7 is forever cyan; # index 15 starts off being cyan but can be changed by the user.
    # # The default colour for each index depends on the file version; tables of the defaults # are available in the source code. If the user changes one or more colours, # a PALETTE record appears in the XLS file -- it gives the RGB values for *all* changeable # indexes.
    # Note that colours can be used in "number formats": "[CYAN]...." and "[COLOR8]...." refer # to colour index 7; "[COLOR16]...." will produce cyan # unless the user changes colour index 15 to something else.
    # #

    In addition, there are several "magic" colour indexes used by Excel:
    # 0x18 (BIFF3-BIFF4), 0x40 (BIFF5-BIFF8): System window text colour for border lines # (used in XF, CF, and WINDOW2 records)
    # 0x19 (BIFF3-BIFF4), 0x41 (BIFF5-BIFF8): System window background colour for pattern background # (used in XF and CF records )
    # 0x43: System face colour (dialogue background colour)
    # 0x4D: System window text colour for chart border lines
    # 0x4E: System window background colour for chart areas
    # 0x4F: Automatic colour for chart border lines (seems to be always Black)
    # 0x50: System ToolTip background colour (used in note objects)
    # 0x51: System ToolTip text colour (used in note objects)
    # 0x7FFF: System window text colour for fonts (used in FONT and CF records)
    # Note 0x7FFF appears to be the *default* colour index. It appears quite often in FONT # records.
    # #

    Default Formatting

    # # Default formatting is applied to all empty cells (those not described by a cell record). # Firstly row default information (ROW record, Rowinfo class) is used if available. # Failing that, column default information (COLINFO record, Colinfo class) is used if available. # As a last resort the worksheet/workbook default cell format will be used; this # should always be present in an Excel file, # described by the XF record with the fixed index 15 (0-based). By default, it uses the # worksheet/workbook default cell style, described by the very first XF record (index 0). # #

    Formatting features not included in xlrd version 0.6.1

    #
      #
    • Rich text i.e. strings containing partial bold italic # and underlined text, change of font inside a string, etc. # See OOo docs s3.4 and s3.2. # Rich text is included in version 0.7.2
    • #
    • Asian phonetic text (known as "ruby"), used for Japanese furigana. See OOo docs # s3.4.2 (p15)
    • #
    • Conditional formatting. See OOo docs # s5.12, s6.21 (CONDFMT record), s6.16 (CF record)
    • #
    • Miscellaneous sheet-level and book-level items e.g. printing layout, screen panes.
    • #
    • Modern Excel file versions don't keep most of the built-in # "number formats" in the file; Excel loads formats according to the # user's locale. Currently xlrd's emulation of this is limited to # a hard-wired table that applies to the US English locale. This may mean # that currency symbols, date order, thousands separator, decimals separator, etc # are inappropriate. Note that this does not affect users who are copying XLS # files, only those who are visually rendering cells.
    • #
    # #

    Loading worksheets on demand

    # #

    This feature, new in version 0.7.1, is governed by the on_demand argument # to the open_workbook() function and allows saving memory and time by loading # only those sheets that the caller is interested in, and releasing sheets # when no longer required.

    # #

    on_demand=False (default): No change. open_workbook() loads global data # and all sheets, releases resources no longer required (principally the # str or mmap object containing the Workbook stream), and returns.

    # #

    on_demand=True and BIFF version < 5.0: A warning message is emitted, # on_demand is recorded as False, and the old process is followed.

    # #

    on_demand=True and BIFF version >= 5.0: open_workbook() loads global # data and returns without releasing resources. At this stage, the only # information available about sheets is Book.nsheets and Book.sheet_names().

    # #

    Book.sheet_by_name() and Book.sheet_by_index() will load the requested # sheet if it is not already loaded.

    # #

    Book.sheets() will load all/any unloaded sheets.

    # #

    The caller may save memory by calling # Book.unload_sheet(sheet_name_or_index) when finished with the sheet. # This applies irrespective of the state of on_demand.

    # #

    The caller may re-load an unloaded sheet by calling Book.sheet_by_xxxx() # -- except if those required resources have been released (which will # have happened automatically when on_demand is false). This is the only # case where an exception will be raised.

    # #

    The caller may query the state of a sheet: # Book.sheet_loaded(sheet_name_or_index) -> a bool

    # #

    Book.release_resources() may used to save memory and close # any memory-mapped file before proceding to examine already-loaded # sheets. Once resources are released, no further sheets can be loaded.

    # #

    When using on-demand, it is advisable to ensure that # Book.release_resources() is always called even if an exception # is raised in your own code; otherwise if the input file has been # memory-mapped, the mmap.mmap object will not be closed and you will # not be able to access the physical file until your Python process # terminates. This can be done by calling Book.release_resources() # explicitly in the finally suite of a try/finally block. # New in xlrd 0.7.2: the Book object is a "context manager", so if # using Python 2.5 or later, you can wrap your code in a "with" # statement.

    ## import sys, zipfile, pprint from . import timemachine from .biffh import ( XLRDError, biff_text_from_num, error_text_from_code, XL_CELL_BLANK, XL_CELL_TEXT, XL_CELL_BOOLEAN, XL_CELL_ERROR, XL_CELL_EMPTY, XL_CELL_DATE, XL_CELL_NUMBER ) from .formula import * # is constrained by __all__ from .book import Book, colname #### TODO #### formula also has `colname` (restricted to 256 cols) from .sheet import empty_cell from .xldate import XLDateError, xldate_as_tuple if sys.version.startswith("IronPython"): # print >> sys.stderr, "...importing encodings" import encodings try: import mmap MMAP_AVAILABLE = 1 except ImportError: MMAP_AVAILABLE = 0 USE_MMAP = MMAP_AVAILABLE ## # # Open a spreadsheet file for data extraction. # # @param filename The path to the spreadsheet file to be opened. # # @param logfile An open file to which messages and diagnostics are written. # # @param verbosity Increases the volume of trace material written to the logfile. # # @param use_mmap Whether to use the mmap module is determined heuristically. # Use this arg to override the result. Current heuristic: mmap is used if it exists. # # @param file_contents ... as a string or an mmap.mmap object or some other behave-alike object. # If file_contents is supplied, filename will not be used, except (possibly) in messages. # # @param encoding_override Used to overcome missing or bad codepage information # in older-version files. Refer to discussion in the Unicode section above. #
    -- New in version 0.6.0 # # @param formatting_info Governs provision of a reference to an XF (eXtended Format) object # for each cell in the worksheet. #
    Default is False. This is backwards compatible and saves memory. # "Blank" cells (those with their own formatting information but no data) are treated as empty # (by ignoring the file's BLANK and MULBLANK records). # It cuts off any bottom "margin" of rows of empty (and blank) cells and # any right "margin" of columns of empty (and blank) cells. # Only cell_value and cell_type are available. #
    True provides all cells, including empty and blank cells. # XF information is available for each cell. #
    -- New in version 0.6.1 # # @param on_demand Governs whether sheets are all loaded initially or when demanded # by the caller. Please refer back to the section "Loading worksheets on demand" for details. #
    -- New in version 0.7.1 # # @param ragged_rows False (the default) means all rows are padded out with empty cells so that all # rows have the same size (Sheet.ncols). True means that there are no empty cells at the ends of rows. # This can result in substantial memory savings if rows are of widely varying sizes. See also the # Sheet.row_len() method. #
    -- New in version 0.7.2 # # @return An instance of the Book class. def open_workbook(filename=None, logfile=sys.stdout, verbosity=0, use_mmap=USE_MMAP, file_contents=None, encoding_override=None, formatting_info=False, on_demand=False, ragged_rows=False, ): peeksz = 4 if file_contents: peek = file_contents[:peeksz] else: f = open(filename, "rb") peek = f.read(peeksz) f.close() if peek == b"PK\x03\x04": # a ZIP file if file_contents: zf = zipfile.ZipFile(timemachine.BYTES_IO(file_contents)) else: zf = zipfile.ZipFile(filename) # Workaround for some third party files that use forward slashes and # lower case names. We map the expected name in lowercase to the # actual filename in the zip container. component_names = dict([(name.replace('\\', '/').lower(), name) for name in zf.namelist()]) if verbosity: logfile.write('ZIP component_names:\n') pprint.pprint(component_names, logfile) if 'xl/workbook.xml' in component_names: from . import xlsx bk = xlsx.open_workbook_2007_xml( zf, component_names, logfile=logfile, verbosity=verbosity, use_mmap=use_mmap, formatting_info=formatting_info, on_demand=on_demand, ragged_rows=ragged_rows, ) return bk if 'xl/workbook.bin' in component_names: raise XLRDError('Excel 2007 xlsb file; not supported') if 'content.xml' in component_names: raise XLRDError('Openoffice.org ODS file; not supported') raise XLRDError('ZIP file contents not a known type of workbook') from . import book bk = book.open_workbook_xls( filename=filename, logfile=logfile, verbosity=verbosity, use_mmap=use_mmap, file_contents=file_contents, encoding_override=encoding_override, formatting_info=formatting_info, on_demand=on_demand, ragged_rows=ragged_rows, ) return bk ## # For debugging: dump an XLS file's BIFF records in char & hex. # @param filename The path to the file to be dumped. # @param outfile An open file, to which the dump is written. # @param unnumbered If true, omit offsets (for meaningful diffs). def dump(filename, outfile=sys.stdout, unnumbered=False): from .biffh import biff_dump bk = Book() bk.biff2_8_load(filename=filename, logfile=outfile, ) biff_dump(bk.mem, bk.base, bk.stream_len, 0, outfile, unnumbered) ## # For debugging and analysis: summarise the file's BIFF records. # I.e. produce a sorted file of (record_name, count). # @param filename The path to the file to be summarised. # @param outfile An open file, to which the summary is written. def count_records(filename, outfile=sys.stdout): from .biffh import biff_count_records bk = Book() bk.biff2_8_load(filename=filename, logfile=outfile, ) biff_count_records(bk.mem, bk.base, bk.stream_len, outfile) xlrd-0.9.4/xlrd/biffh.py0000644000076500000240000004214712155372403015343 0ustar chrisstaff00000000000000# -*- coding: cp1252 -*- ## # Support module for the xlrd package. # #

    Portions copyright 2005-2010 Stephen John Machin, Lingfo Pty Ltd

    #

    This module is part of the xlrd package, which is released under a BSD-style licence.

    ## # 2010-03-01 SJM Reading SCL record # 2010-03-01 SJM Added more record IDs for biff_dump & biff_count # 2008-02-10 SJM BIFF2 BLANK record # 2008-02-08 SJM Preparation for Excel 2.0 support # 2008-02-02 SJM Added suffixes (_B2, _B2_ONLY, etc) on record names for biff_dump & biff_count # 2007-12-04 SJM Added support for Excel 2.x (BIFF2) files. # 2007-09-08 SJM Avoid crash when zero-length Unicode string missing options byte. # 2007-04-22 SJM Remove experimental "trimming" facility. from __future__ import print_function DEBUG = 0 from struct import unpack import sys from .timemachine import * class XLRDError(Exception): pass ## # Parent of almost all other classes in the package. Defines a common "dump" method # for debugging. class BaseObject(object): _repr_these = [] ## # @param f open file object, to which the dump is written # @param header text to write before the dump # @param footer text to write after the dump # @param indent number of leading spaces (for recursive calls) def dump(self, f=None, header=None, footer=None, indent=0): if f is None: f = sys.stderr if hasattr(self, "__slots__"): alist = [] for attr in self.__slots__: alist.append((attr, getattr(self, attr))) else: alist = self.__dict__.items() alist = sorted(alist) pad = " " * indent if header is not None: print(header, file=f) list_type = type([]) dict_type = type({}) for attr, value in alist: if getattr(value, 'dump', None) and attr != 'book': value.dump(f, header="%s%s (%s object):" % (pad, attr, value.__class__.__name__), indent=indent+4) elif attr not in self._repr_these and ( isinstance(value, list_type) or isinstance(value, dict_type) ): print("%s%s: %s, len = %d" % (pad, attr, type(value), len(value)), file=f) else: fprintf(f, "%s%s: %r\n", pad, attr, value) if footer is not None: print(footer, file=f) FUN, FDT, FNU, FGE, FTX = range(5) # unknown, date, number, general, text DATEFORMAT = FDT NUMBERFORMAT = FNU ( XL_CELL_EMPTY, XL_CELL_TEXT, XL_CELL_NUMBER, XL_CELL_DATE, XL_CELL_BOOLEAN, XL_CELL_ERROR, XL_CELL_BLANK, # for use in debugging, gathering stats, etc ) = range(7) biff_text_from_num = { 0: "(not BIFF)", 20: "2.0", 21: "2.1", 30: "3", 40: "4S", 45: "4W", 50: "5", 70: "7", 80: "8", 85: "8X", } ## #

    This dictionary can be used to produce a text version of the internal codes # that Excel uses for error cells. Here are its contents: #

    # 0x00: '#NULL!',  # Intersection of two cell ranges is empty
    # 0x07: '#DIV/0!', # Division by zero
    # 0x0F: '#VALUE!', # Wrong type of operand
    # 0x17: '#REF!',   # Illegal or deleted cell reference
    # 0x1D: '#NAME?',  # Wrong function or range name
    # 0x24: '#NUM!',   # Value range overflow
    # 0x2A: '#N/A',    # Argument or function not available
    # 

    error_text_from_code = { 0x00: '#NULL!', # Intersection of two cell ranges is empty 0x07: '#DIV/0!', # Division by zero 0x0F: '#VALUE!', # Wrong type of operand 0x17: '#REF!', # Illegal or deleted cell reference 0x1D: '#NAME?', # Wrong function or range name 0x24: '#NUM!', # Value range overflow 0x2A: '#N/A', # Argument or function not available } BIFF_FIRST_UNICODE = 80 XL_WORKBOOK_GLOBALS = WBKBLOBAL = 0x5 XL_WORKBOOK_GLOBALS_4W = 0x100 XL_WORKSHEET = WRKSHEET = 0x10 XL_BOUNDSHEET_WORKSHEET = 0x00 XL_BOUNDSHEET_CHART = 0x02 XL_BOUNDSHEET_VB_MODULE = 0x06 # XL_RK2 = 0x7e XL_ARRAY = 0x0221 XL_ARRAY2 = 0x0021 XL_BLANK = 0x0201 XL_BLANK_B2 = 0x01 XL_BOF = 0x809 XL_BOOLERR = 0x205 XL_BOOLERR_B2 = 0x5 XL_BOUNDSHEET = 0x85 XL_BUILTINFMTCOUNT = 0x56 XL_CF = 0x01B1 XL_CODEPAGE = 0x42 XL_COLINFO = 0x7D XL_COLUMNDEFAULT = 0x20 # BIFF2 only XL_COLWIDTH = 0x24 # BIFF2 only XL_CONDFMT = 0x01B0 XL_CONTINUE = 0x3c XL_COUNTRY = 0x8C XL_DATEMODE = 0x22 XL_DEFAULTROWHEIGHT = 0x0225 XL_DEFCOLWIDTH = 0x55 XL_DIMENSION = 0x200 XL_DIMENSION2 = 0x0 XL_EFONT = 0x45 XL_EOF = 0x0a XL_EXTERNNAME = 0x23 XL_EXTERNSHEET = 0x17 XL_EXTSST = 0xff XL_FEAT11 = 0x872 XL_FILEPASS = 0x2f XL_FONT = 0x31 XL_FONT_B3B4 = 0x231 XL_FORMAT = 0x41e XL_FORMAT2 = 0x1E # BIFF2, BIFF3 XL_FORMULA = 0x6 XL_FORMULA3 = 0x206 XL_FORMULA4 = 0x406 XL_GCW = 0xab XL_HLINK = 0x01B8 XL_QUICKTIP = 0x0800 XL_HORIZONTALPAGEBREAKS = 0x1b XL_INDEX = 0x20b XL_INTEGER = 0x2 # BIFF2 only XL_IXFE = 0x44 # BIFF2 only XL_LABEL = 0x204 XL_LABEL_B2 = 0x04 XL_LABELRANGES = 0x15f XL_LABELSST = 0xfd XL_LEFTMARGIN = 0x26 XL_TOPMARGIN = 0x28 XL_RIGHTMARGIN = 0x27 XL_BOTTOMMARGIN = 0x29 XL_HEADER = 0x14 XL_FOOTER = 0x15 XL_HCENTER = 0x83 XL_VCENTER = 0x84 XL_MERGEDCELLS = 0xE5 XL_MSO_DRAWING = 0x00EC XL_MSO_DRAWING_GROUP = 0x00EB XL_MSO_DRAWING_SELECTION = 0x00ED XL_MULRK = 0xbd XL_MULBLANK = 0xbe XL_NAME = 0x18 XL_NOTE = 0x1c XL_NUMBER = 0x203 XL_NUMBER_B2 = 0x3 XL_OBJ = 0x5D XL_PAGESETUP = 0xA1 XL_PALETTE = 0x92 XL_PANE = 0x41 XL_PRINTGRIDLINES = 0x2B XL_PRINTHEADERS = 0x2A XL_RK = 0x27e XL_ROW = 0x208 XL_ROW_B2 = 0x08 XL_RSTRING = 0xd6 XL_SCL = 0x00A0 XL_SHEETHDR = 0x8F # BIFF4W only XL_SHEETPR = 0x81 XL_SHEETSOFFSET = 0x8E # BIFF4W only XL_SHRFMLA = 0x04bc XL_SST = 0xfc XL_STANDARDWIDTH = 0x99 XL_STRING = 0x207 XL_STRING_B2 = 0x7 XL_STYLE = 0x293 XL_SUPBOOK = 0x1AE # aka EXTERNALBOOK in OOo docs XL_TABLEOP = 0x236 XL_TABLEOP2 = 0x37 XL_TABLEOP_B2 = 0x36 XL_TXO = 0x1b6 XL_UNCALCED = 0x5e XL_UNKNOWN = 0xffff XL_VERTICALPAGEBREAKS = 0x1a XL_WINDOW2 = 0x023E XL_WINDOW2_B2 = 0x003E XL_WRITEACCESS = 0x5C XL_WSBOOL = XL_SHEETPR XL_XF = 0xe0 XL_XF2 = 0x0043 # BIFF2 version of XF record XL_XF3 = 0x0243 # BIFF3 version of XF record XL_XF4 = 0x0443 # BIFF4 version of XF record boflen = {0x0809: 8, 0x0409: 6, 0x0209: 6, 0x0009: 4} bofcodes = (0x0809, 0x0409, 0x0209, 0x0009) XL_FORMULA_OPCODES = (0x0006, 0x0406, 0x0206) _cell_opcode_list = [ XL_BOOLERR, XL_FORMULA, XL_FORMULA3, XL_FORMULA4, XL_LABEL, XL_LABELSST, XL_MULRK, XL_NUMBER, XL_RK, XL_RSTRING, ] _cell_opcode_dict = {} for _cell_opcode in _cell_opcode_list: _cell_opcode_dict[_cell_opcode] = 1 def is_cell_opcode(c): return c in _cell_opcode_dict def upkbits(tgt_obj, src, manifest, local_setattr=setattr): for n, mask, attr in manifest: local_setattr(tgt_obj, attr, (src & mask) >> n) def upkbitsL(tgt_obj, src, manifest, local_setattr=setattr, local_int=int): for n, mask, attr in manifest: local_setattr(tgt_obj, attr, local_int((src & mask) >> n)) def unpack_string(data, pos, encoding, lenlen=1): nchars = unpack('<' + 'BH'[lenlen-1], data[pos:pos+lenlen])[0] pos += lenlen return unicode(data[pos:pos+nchars], encoding) def unpack_string_update_pos(data, pos, encoding, lenlen=1, known_len=None): if known_len is not None: # On a NAME record, the length byte is detached from the front of the string. nchars = known_len else: nchars = unpack('<' + 'BH'[lenlen-1], data[pos:pos+lenlen])[0] pos += lenlen newpos = pos + nchars return (unicode(data[pos:newpos], encoding), newpos) def unpack_unicode(data, pos, lenlen=2): "Return unicode_strg" nchars = unpack('<' + 'BH'[lenlen-1], data[pos:pos+lenlen])[0] if not nchars: # Ambiguous whether 0-length string should have an "options" byte. # Avoid crash if missing. return UNICODE_LITERAL("") pos += lenlen options = BYTES_ORD(data[pos]) pos += 1 # phonetic = options & 0x04 # richtext = options & 0x08 if options & 0x08: # rt = unpack(' endpos=%d pos=%d endsub=%d substrg=%r\n', ofs, dlen, base, endpos, pos, endsub, substrg) break hexd = ''.join(["%02x " % BYTES_ORD(c) for c in substrg]) chard = '' for c in substrg: c = chr(BYTES_ORD(c)) if c == '\0': c = '~' elif not (' ' <= c <= '~'): c = '?' chard += c if numbered: num_prefix = "%5d: " % (base+pos-ofs) fprintf(fout, "%s %-48s %s\n", num_prefix, hexd, chard) pos = endsub def biff_dump(mem, stream_offset, stream_len, base=0, fout=sys.stdout, unnumbered=False): pos = stream_offset stream_end = stream_offset + stream_len adj = base - stream_offset dummies = 0 numbered = not unnumbered num_prefix = '' while stream_end - pos >= 4: rc, length = unpack('') if numbered: num_prefix = "%5d: " % (adj + pos) fprintf(fout, "%s%04x %s len = %04x (%d)\n", num_prefix, rc, recname, length, length) pos += 4 hex_char_dump(mem, pos, length, adj+pos, fout, unnumbered) pos += length if dummies: if numbered: num_prefix = "%5d: " % (adj + savpos) fprintf(fout, "%s---- %d zero bytes skipped ----\n", num_prefix, dummies) if pos < stream_end: if numbered: num_prefix = "%5d: " % (adj + pos) fprintf(fout, "%s---- Misc bytes at end ----\n", num_prefix) hex_char_dump(mem, pos, stream_end-pos, adj + pos, fout, unnumbered) elif pos > stream_end: fprintf(fout, "Last dumped record has length (%d) that is too large\n", length) def biff_count_records(mem, stream_offset, stream_len, fout=sys.stdout): pos = stream_offset stream_end = stream_offset + stream_len tally = {} while stream_end - pos >= 4: rc, length = unpack('> sys.stderr, "...importing encodings" import encodings empty_cell = sheet.empty_cell # for exposure to the world ... DEBUG = 0 USE_FANCY_CD = 1 TOGGLE_GC = 0 import gc # gc.set_debug(gc.DEBUG_STATS) try: import mmap MMAP_AVAILABLE = 1 except ImportError: MMAP_AVAILABLE = 0 USE_MMAP = MMAP_AVAILABLE MY_EOF = 0xF00BAAA # not a 16-bit number SUPBOOK_UNK, SUPBOOK_INTERNAL, SUPBOOK_EXTERNAL, SUPBOOK_ADDIN, SUPBOOK_DDEOLE = range(5) SUPPORTED_VERSIONS = (80, 70, 50, 45, 40, 30, 21, 20) _code_from_builtin_name = { "Consolidate_Area": "\x00", "Auto_Open": "\x01", "Auto_Close": "\x02", "Extract": "\x03", "Database": "\x04", "Criteria": "\x05", "Print_Area": "\x06", "Print_Titles": "\x07", "Recorder": "\x08", "Data_Form": "\x09", "Auto_Activate": "\x0A", "Auto_Deactivate": "\x0B", "Sheet_Title": "\x0C", "_FilterDatabase": "\x0D", } builtin_name_from_code = {} code_from_builtin_name = {} for _bin, _bic in _code_from_builtin_name.items(): _bin = UNICODE_LITERAL(_bin) _bic = UNICODE_LITERAL(_bic) code_from_builtin_name[_bin] = _bic builtin_name_from_code[_bic] = _bin del _bin, _bic, _code_from_builtin_name def open_workbook_xls(filename=None, logfile=sys.stdout, verbosity=0, use_mmap=USE_MMAP, file_contents=None, encoding_override=None, formatting_info=False, on_demand=False, ragged_rows=False, ): t0 = time.clock() if TOGGLE_GC: orig_gc_enabled = gc.isenabled() if orig_gc_enabled: gc.disable() bk = Book() try: bk.biff2_8_load( filename=filename, file_contents=file_contents, logfile=logfile, verbosity=verbosity, use_mmap=use_mmap, encoding_override=encoding_override, formatting_info=formatting_info, on_demand=on_demand, ragged_rows=ragged_rows, ) t1 = time.clock() bk.load_time_stage_1 = t1 - t0 biff_version = bk.getbof(XL_WORKBOOK_GLOBALS) if not biff_version: raise XLRDError("Can't determine file's BIFF version") if biff_version not in SUPPORTED_VERSIONS: raise XLRDError( "BIFF version %s is not supported" % biff_text_from_num[biff_version] ) bk.biff_version = biff_version if biff_version <= 40: # no workbook globals, only 1 worksheet if on_demand: fprintf(bk.logfile, "*** WARNING: on_demand is not supported for this Excel version.\n" "*** Setting on_demand to False.\n") bk.on_demand = on_demand = False bk.fake_globals_get_sheet() elif biff_version == 45: # worksheet(s) embedded in global stream bk.parse_globals() if on_demand: fprintf(bk.logfile, "*** WARNING: on_demand is not supported for this Excel version.\n" "*** Setting on_demand to False.\n") bk.on_demand = on_demand = False else: bk.parse_globals() bk._sheet_list = [None for sh in bk._sheet_names] if not on_demand: bk.get_sheets() bk.nsheets = len(bk._sheet_list) if biff_version == 45 and bk.nsheets > 1: fprintf(bk.logfile, "*** WARNING: Excel 4.0 workbook (.XLW) file contains %d worksheets.\n" "*** Book-level data will be that of the last worksheet.\n", bk.nsheets ) if TOGGLE_GC: if orig_gc_enabled: gc.enable() t2 = time.clock() bk.load_time_stage_2 = t2 - t1 except: bk.release_resources() raise # normal exit if not on_demand: bk.release_resources() return bk ## # For debugging: dump the file's BIFF records in char & hex. # @param filename The path to the file to be dumped. # @param outfile An open file, to which the dump is written. # @param unnumbered If true, omit offsets (for meaningful diffs). def dump(filename, outfile=sys.stdout, unnumbered=False): bk = Book() bk.biff2_8_load(filename=filename, logfile=outfile, ) biff_dump(bk.mem, bk.base, bk.stream_len, 0, outfile, unnumbered) ## # For debugging and analysis: summarise the file's BIFF records. # I.e. produce a sorted file of (record_name, count). # @param filename The path to the file to be summarised. # @param outfile An open file, to which the summary is written. def count_records(filename, outfile=sys.stdout): bk = Book() bk.biff2_8_load(filename=filename, logfile=outfile, ) biff_count_records(bk.mem, bk.base, bk.stream_len, outfile) ## # Information relating to a named reference, formula, macro, etc. #
    -- New in version 0.6.0 #
    -- Name information is not extracted from files older than # Excel 5.0 (Book.biff_version < 50) class Name(BaseObject): _repr_these = ['stack'] book = None # parent ## # 0 = Visible; 1 = Hidden hidden = 0 ## # 0 = Command macro; 1 = Function macro. Relevant only if macro == 1 func = 0 ## # 0 = Sheet macro; 1 = VisualBasic macro. Relevant only if macro == 1 vbasic = 0 ## # 0 = Standard name; 1 = Macro name macro = 0 ## # 0 = Simple formula; 1 = Complex formula (array formula or user defined)
    # No examples have been sighted. complex = 0 ## # 0 = User-defined name; 1 = Built-in name # (common examples: Print_Area, Print_Titles; see OOo docs for full list) builtin = 0 ## # Function group. Relevant only if macro == 1; see OOo docs for values. funcgroup = 0 ## # 0 = Formula definition; 1 = Binary data
    No examples have been sighted. binary = 0 ## # The index of this object in book.name_obj_list name_index = 0 ## # A Unicode string. If builtin, decoded as per OOo docs. name = UNICODE_LITERAL("") ## # An 8-bit string. raw_formula = b'' ## # -1: The name is global (visible in all calculation sheets).
    # -2: The name belongs to a macro sheet or VBA sheet.
    # -3: The name is invalid.
    # 0 <= scope < book.nsheets: The name is local to the sheet whose index is scope. scope = -1 ## # The result of evaluating the formula, if any. # If no formula, or evaluation of the formula encountered problems, # the result is None. Otherwise the result is a single instance of the # Operand class. # result = None ## # This is a convenience method for the frequent use case where the name # refers to a single cell. # @return An instance of the Cell class. # @throws XLRDError The name is not a constant absolute reference # to a single cell. def cell(self): res = self.result if res: # result should be an instance of the Operand class kind = res.kind value = res.value if kind == oREF and len(value) == 1: ref3d = value[0] if (0 <= ref3d.shtxlo == ref3d.shtxhi - 1 and ref3d.rowxlo == ref3d.rowxhi - 1 and ref3d.colxlo == ref3d.colxhi - 1): sh = self.book.sheet_by_index(ref3d.shtxlo) return sh.cell(ref3d.rowxlo, ref3d.colxlo) self.dump(self.book.logfile, header="=== Dump of Name object ===", footer="======= End of dump =======", ) raise XLRDError("Not a constant absolute reference to a single cell") ## # This is a convenience method for the use case where the name # refers to one rectangular area in one worksheet. # @param clipped If true (the default), the returned rectangle is clipped # to fit in (0, sheet.nrows, 0, sheet.ncols) -- it is guaranteed that # 0 <= rowxlo <= rowxhi <= sheet.nrows and that the number of usable rows # in the area (which may be zero) is rowxhi - rowxlo; likewise for columns. # @return a tuple (sheet_object, rowxlo, rowxhi, colxlo, colxhi). # @throws XLRDError The name is not a constant absolute reference # to a single area in a single sheet. def area2d(self, clipped=True): res = self.result if res: # result should be an instance of the Operand class kind = res.kind value = res.value if kind == oREF and len(value) == 1: # only 1 reference ref3d = value[0] if 0 <= ref3d.shtxlo == ref3d.shtxhi - 1: # only 1 usable sheet sh = self.book.sheet_by_index(ref3d.shtxlo) if not clipped: return sh, ref3d.rowxlo, ref3d.rowxhi, ref3d.colxlo, ref3d.colxhi rowxlo = min(ref3d.rowxlo, sh.nrows) rowxhi = max(rowxlo, min(ref3d.rowxhi, sh.nrows)) colxlo = min(ref3d.colxlo, sh.ncols) colxhi = max(colxlo, min(ref3d.colxhi, sh.ncols)) assert 0 <= rowxlo <= rowxhi <= sh.nrows assert 0 <= colxlo <= colxhi <= sh.ncols return sh, rowxlo, rowxhi, colxlo, colxhi self.dump(self.book.logfile, header="=== Dump of Name object ===", footer="======= End of dump =======", ) raise XLRDError("Not a constant absolute reference to a single area in a single sheet") ## # Contents of a "workbook". #

    WARNING: You don't call this class yourself. You use the Book object that # was returned when you called xlrd.open_workbook("myfile.xls").

    class Book(BaseObject): ## # The number of worksheets present in the workbook file. # This information is available even when no sheets have yet been loaded. nsheets = 0 ## # Which date system was in force when this file was last saved.
    # 0 => 1900 system (the Excel for Windows default).
    # 1 => 1904 system (the Excel for Macintosh default).
    datemode = 0 # In case it's not specified in the file. ## # Version of BIFF (Binary Interchange File Format) used to create the file. # Latest is 8.0 (represented here as 80), introduced with Excel 97. # Earliest supported by this module: 2.0 (represented as 20). biff_version = 0 ## # List containing a Name object for each NAME record in the workbook. #
    -- New in version 0.6.0 name_obj_list = [] ## # An integer denoting the character set used for strings in this file. # For BIFF 8 and later, this will be 1200, meaning Unicode; more precisely, UTF_16_LE. # For earlier versions, this is used to derive the appropriate Python encoding # to be used to convert to Unicode. # Examples: 1252 -> 'cp1252', 10000 -> 'mac_roman' codepage = None ## # The encoding that was derived from the codepage. encoding = None ## # A tuple containing the (telephone system) country code for:
    # [0]: the user-interface setting when the file was created.
    # [1]: the regional settings.
    # Example: (1, 61) meaning (USA, Australia). # This information may give a clue to the correct encoding for an unknown codepage. # For a long list of observed values, refer to the OpenOffice.org documentation for # the COUNTRY record. countries = (0, 0) ## # What (if anything) is recorded as the name of the last user to save the file. user_name = UNICODE_LITERAL('') ## # A list of Font class instances, each corresponding to a FONT record. #
    -- New in version 0.6.1 font_list = [] ## # A list of XF class instances, each corresponding to an XF record. #
    -- New in version 0.6.1 xf_list = [] ## # A list of Format objects, each corresponding to a FORMAT record, in # the order that they appear in the input file. # It does not contain builtin formats. # If you are creating an output file using (for example) pyExcelerator, # use this list. # The collection to be used for all visual rendering purposes is format_map. #
    -- New in version 0.6.1 format_list = [] ## # The mapping from XF.format_key to Format object. #
    -- New in version 0.6.1 format_map = {} ## # This provides access via name to the extended format information for # both built-in styles and user-defined styles.
    # It maps name to (built_in, xf_index), where:
    # name is either the name of a user-defined style, # or the name of one of the built-in styles. Known built-in names are # Normal, RowLevel_1 to RowLevel_7, # ColLevel_1 to ColLevel_7, Comma, Currency, Percent, "Comma [0]", # "Currency [0]", Hyperlink, and "Followed Hyperlink".
    # built_in 1 = built-in style, 0 = user-defined
    # xf_index is an index into Book.xf_list.
    # References: OOo docs s6.99 (STYLE record); Excel UI Format/Style #
    -- New in version 0.6.1; since 0.7.4, extracted only if # open_workbook(..., formatting_info=True) style_name_map = {} ## # This provides definitions for colour indexes. Please refer to the # above section "The Palette; Colour Indexes" for an explanation # of how colours are represented in Excel.
    # Colour indexes into the palette map into (red, green, blue) tuples. # "Magic" indexes e.g. 0x7FFF map to None. # colour_map is what you need if you want to render cells on screen or in a PDF # file. If you are writing an output XLS file, use palette_record. #
    -- New in version 0.6.1. Extracted only if open_workbook(..., formatting_info=True) colour_map = {} ## # If the user has changed any of the colours in the standard palette, the XLS # file will contain a PALETTE record with 56 (16 for Excel 4.0 and earlier) # RGB values in it, and this list will be e.g. [(r0, b0, g0), ..., (r55, b55, g55)]. # Otherwise this list will be empty. This is what you need if you are # writing an output XLS file. If you want to render cells on screen or in a PDF # file, use colour_map. #
    -- New in version 0.6.1. Extracted only if open_workbook(..., formatting_info=True) palette_record = [] ## # Time in seconds to extract the XLS image as a contiguous string (or mmap equivalent). load_time_stage_1 = -1.0 ## # Time in seconds to parse the data from the contiguous string (or mmap equivalent). load_time_stage_2 = -1.0 ## # @return A list of all sheets in the book. # All sheets not already loaded will be loaded. def sheets(self): for sheetx in xrange(self.nsheets): if not self._sheet_list[sheetx]: self.get_sheet(sheetx) return self._sheet_list[:] ## # @param sheetx Sheet index in range(nsheets) # @return An object of the Sheet class def sheet_by_index(self, sheetx): return self._sheet_list[sheetx] or self.get_sheet(sheetx) ## # @param sheet_name Name of sheet required # @return An object of the Sheet class def sheet_by_name(self, sheet_name): try: sheetx = self._sheet_names.index(sheet_name) except ValueError: raise XLRDError('No sheet named <%r>' % sheet_name) return self.sheet_by_index(sheetx) ## # @return A list of the names of all the worksheets in the workbook file. # This information is available even when no sheets have yet been loaded. def sheet_names(self): return self._sheet_names[:] ## # @param sheet_name_or_index Name or index of sheet enquired upon # @return true if sheet is loaded, false otherwise #
    -- New in version 0.7.1 def sheet_loaded(self, sheet_name_or_index): if isinstance(sheet_name_or_index, int): sheetx = sheet_name_or_index else: try: sheetx = self._sheet_names.index(sheet_name_or_index) except ValueError: raise XLRDError('No sheet named <%r>' % sheet_name_or_index) return bool(self._sheet_list[sheetx]) ## # @param sheet_name_or_index Name or index of sheet to be unloaded. #
    -- New in version 0.7.1 def unload_sheet(self, sheet_name_or_index): if isinstance(sheet_name_or_index, int): sheetx = sheet_name_or_index else: try: sheetx = self._sheet_names.index(sheet_name_or_index) except ValueError: raise XLRDError('No sheet named <%r>' % sheet_name_or_index) self._sheet_list[sheetx] = None ## # This method has a dual purpose. You can call it to release # memory-consuming objects and (possibly) a memory-mapped file # (mmap.mmap object) when you have finished loading sheets in # on_demand mode, but still require the Book object to examine the # loaded sheets. It is also called automatically (a) when open_workbook # raises an exception and (b) if you are using a "with" statement, when # the "with" block is exited. Calling this method multiple times on the # same object has no ill effect. def release_resources(self): self._resources_released = 1 if hasattr(self.mem, "close"): # must be a mmap.mmap object self.mem.close() self.mem = None if hasattr(self.filestr, "close"): self.filestr.close() self.filestr = None self._sharedstrings = None self._rich_text_runlist_map = None def __enter__(self): return self def __exit__(self, exc_type, exc_value, exc_tb): self.release_resources() # return false ## # A mapping from (lower_case_name, scope) to a single Name object. #
    -- New in version 0.6.0 name_and_scope_map = {} ## # A mapping from lower_case_name to a list of Name objects. The list is # sorted in scope order. Typically there will be one item (of global scope) # in the list. #
    -- New in version 0.6.0 name_map = {} def __init__(self): self._sheet_list = [] self._sheet_names = [] self._sheet_visibility = [] # from BOUNDSHEET record self.nsheets = 0 self._sh_abs_posn = [] # sheet's absolute position in the stream self._sharedstrings = [] self._rich_text_runlist_map = {} self.raw_user_name = False self._sheethdr_count = 0 # BIFF 4W only self.builtinfmtcount = -1 # unknown as yet. BIFF 3, 4S, 4W self.initialise_format_info() self._all_sheets_count = 0 # includes macro & VBA sheets self._supbook_count = 0 self._supbook_locals_inx = None self._supbook_addins_inx = None self._all_sheets_map = [] # maps an all_sheets index to a calc-sheets index (or -1) self._externsheet_info = [] self._externsheet_type_b57 = [] self._extnsht_name_from_num = {} self._sheet_num_from_name = {} self._extnsht_count = 0 self._supbook_types = [] self._resources_released = 0 self.addin_func_names = [] self.name_obj_list = [] self.colour_map = {} self.palette_record = [] self.xf_list = [] self.style_name_map = {} self.mem = b'' self.filestr = b'' def biff2_8_load(self, filename=None, file_contents=None, logfile=sys.stdout, verbosity=0, use_mmap=USE_MMAP, encoding_override=None, formatting_info=False, on_demand=False, ragged_rows=False, ): # DEBUG = 0 self.logfile = logfile self.verbosity = verbosity self.use_mmap = use_mmap and MMAP_AVAILABLE self.encoding_override = encoding_override self.formatting_info = formatting_info self.on_demand = on_demand self.ragged_rows = ragged_rows if not file_contents: with open(filename, "rb") as f: f.seek(0, 2) # EOF size = f.tell() f.seek(0, 0) # BOF if size == 0: raise XLRDError("File size is 0 bytes") if self.use_mmap: self.filestr = mmap.mmap(f.fileno(), size, access=mmap.ACCESS_READ) self.stream_len = size else: self.filestr = f.read() self.stream_len = len(self.filestr) else: self.filestr = file_contents self.stream_len = len(file_contents) self.base = 0 if self.filestr[:8] != compdoc.SIGNATURE: # got this one at the antique store self.mem = self.filestr else: cd = compdoc.CompDoc(self.filestr, logfile=self.logfile) if USE_FANCY_CD: for qname in ['Workbook', 'Book']: self.mem, self.base, self.stream_len = \ cd.locate_named_stream(UNICODE_LITERAL(qname)) if self.mem: break else: raise XLRDError("Can't find workbook in OLE2 compound document") else: for qname in ['Workbook', 'Book']: self.mem = cd.get_named_stream(UNICODE_LITERAL(qname)) if self.mem: break else: raise XLRDError("Can't find workbook in OLE2 compound document") self.stream_len = len(self.mem) del cd if self.mem is not self.filestr: if hasattr(self.filestr, "close"): self.filestr.close() self.filestr = b'' self._position = self.base if DEBUG: print("mem: %s, base: %d, len: %d" % (type(self.mem), self.base, self.stream_len), file=self.logfile) def initialise_format_info(self): # needs to be done once per sheet for BIFF 4W :-( self.format_map = {} self.format_list = [] self.xfcount = 0 self.actualfmtcount = 0 # number of FORMAT records seen so far self._xf_index_to_xl_type_map = {0: XL_CELL_NUMBER} self._xf_epilogue_done = 0 self.xf_list = [] self.font_list = [] def get2bytes(self): pos = self._position buff_two = self.mem[pos:pos+2] lenbuff = len(buff_two) self._position += lenbuff if lenbuff < 2: return MY_EOF lo, hi = buff_two return (BYTES_ORD(hi) << 8) | BYTES_ORD(lo) def get_record_parts(self): pos = self._position mem = self.mem code, length = unpack('= 2: fprintf(self.logfile, "BOUNDSHEET: inx=%d vis=%r sheet_name=%r abs_posn=%d sheet_type=0x%02x\n", self._all_sheets_count, visibility, sheet_name, abs_posn, sheet_type) self._all_sheets_count += 1 if sheet_type != XL_BOUNDSHEET_WORKSHEET: self._all_sheets_map.append(-1) descr = { 1: 'Macro sheet', 2: 'Chart', 6: 'Visual Basic module', }.get(sheet_type, 'UNKNOWN') if DEBUG or self.verbosity >= 1: fprintf(self.logfile, "NOTE *** Ignoring non-worksheet data named %r (type 0x%02x = %s)\n", sheet_name, sheet_type, descr) else: snum = len(self._sheet_names) self._all_sheets_map.append(snum) self._sheet_names.append(sheet_name) self._sh_abs_posn.append(abs_posn) self._sheet_visibility.append(visibility) self._sheet_num_from_name[sheet_name] = snum def handle_builtinfmtcount(self, data): ### N.B. This count appears to be utterly useless. # DEBUG = 1 builtinfmtcount = unpack('= 2: fprintf(self.logfile, "*** No CODEPAGE record; assuming 1200 (utf_16_le)\n") else: codepage = self.codepage if codepage in encoding_from_codepage: encoding = encoding_from_codepage[codepage] elif 300 <= codepage <= 1999: encoding = 'cp' + str(codepage) else: encoding = 'unknown_codepage_' + str(codepage) if DEBUG or (self.verbosity and encoding != self.encoding) : fprintf(self.logfile, "CODEPAGE: codepage %r -> encoding %r\n", codepage, encoding) self.encoding = encoding if self.codepage != 1200: # utf_16_le # If we don't have a codec that can decode ASCII into Unicode, # we're well & truly stuffed -- let the punter know ASAP. try: _unused = unicode(b'trial', self.encoding) except BaseException as e: fprintf(self.logfile, "ERROR *** codepage %r -> encoding %r -> %s: %s\n", self.codepage, self.encoding, type(e).__name__.split(".")[-1], e) raise if self.raw_user_name: strg = unpack_string(self.user_name, 0, self.encoding, lenlen=1) strg = strg.rstrip() # if DEBUG: # print "CODEPAGE: user name decoded from %r to %r" % (self.user_name, strg) self.user_name = strg self.raw_user_name = False return self.encoding def handle_codepage(self, data): # DEBUG = 0 codepage = unpack('= 2 if self.biff_version >= 80: option_flags, other_info =unpack("= 1 blah2 = DEBUG or self.verbosity >= 2 if self.biff_version >= 80: num_refs = unpack("= 2: logf = self.logfile fprintf(logf, "FILEPASS:\n") hex_char_dump(data, 0, len(data), base=0, fout=logf) if self.biff_version >= 80: kind1, = unpack('= 2 bv = self.biff_version if bv < 50: return self.derive_encoding() # print # hex_char_dump(data, 0, len(data), fout=self.logfile) ( option_flags, kb_shortcut, name_len, fmla_len, extsht_index, sheet_index, menu_text_len, description_text_len, help_topic_text_len, status_bar_text_len, ) = unpack("> nshift) macro_flag = " M"[nobj.macro] if bv < 80: internal_name, pos = unpack_string_update_pos(data, 14, self.encoding, known_len=name_len) else: internal_name, pos = unpack_unicode_update_pos(data, 14, known_len=name_len) nobj.extn_sheet_num = extsht_index nobj.excel_sheet_index = sheet_index nobj.scope = None # patched up in the names_epilogue() method if blah: fprintf( self.logfile, "NAME[%d]:%s oflags=%d, name_len=%d, fmla_len=%d, extsht_index=%d, sheet_index=%d, name=%r\n", name_index, macro_flag, option_flags, name_len, fmla_len, extsht_index, sheet_index, internal_name) name = internal_name if nobj.builtin: name = builtin_name_from_code.get(name, "??Unknown??") if blah: print(" builtin: %s" % name, file=self.logfile) nobj.name = name nobj.raw_formula = data[pos:] nobj.basic_formula_len = fmla_len nobj.evaluated = 0 if blah: nobj.dump( self.logfile, header="--- handle_name: name[%d] ---" % name_index, footer="-------------------", ) def names_epilogue(self): blah = self.verbosity >= 2 f = self.logfile if blah: print("+++++ names_epilogue +++++", file=f) print("_all_sheets_map", REPR(self._all_sheets_map), file=f) print("_extnsht_name_from_num", REPR(self._extnsht_name_from_num), file=f) print("_sheet_num_from_name", REPR(self._sheet_num_from_name), file=f) num_names = len(self.name_obj_list) for namex in range(num_names): nobj = self.name_obj_list[namex] # Convert from excel_sheet_index to scope. # This is done here because in BIFF7 and earlier, the # BOUNDSHEET records (from which _all_sheets_map is derived) # come after the NAME records. if self.biff_version >= 80: sheet_index = nobj.excel_sheet_index if sheet_index == 0: intl_sheet_index = -1 # global elif 1 <= sheet_index <= len(self._all_sheets_map): intl_sheet_index = self._all_sheets_map[sheet_index-1] if intl_sheet_index == -1: # maps to a macro or VBA sheet intl_sheet_index = -2 # valid sheet reference but not useful else: # huh? intl_sheet_index = -3 # invalid elif 50 <= self.biff_version <= 70: sheet_index = nobj.extn_sheet_num if sheet_index == 0: intl_sheet_index = -1 # global else: sheet_name = self._extnsht_name_from_num[sheet_index] intl_sheet_index = self._sheet_num_from_name.get(sheet_name, -2) nobj.scope = intl_sheet_index for namex in range(num_names): nobj = self.name_obj_list[namex] # Parse the formula ... if nobj.macro or nobj.binary: continue if nobj.evaluated: continue evaluate_name_formula(self, nobj, namex, blah=blah) if self.verbosity >= 2: print("---------- name object dump ----------", file=f) for namex in range(num_names): nobj = self.name_obj_list[namex] nobj.dump(f, header="--- name[%d] ---" % namex) print("--------------------------------------", file=f) # # Build some dicts for access to the name objects # name_and_scope_map = {} # (name.lower(), scope): Name_object name_map = {} # name.lower() : list of Name_objects (sorted in scope order) for namex in range(num_names): nobj = self.name_obj_list[namex] name_lcase = nobj.name.lower() key = (name_lcase, nobj.scope) if key in name_and_scope_map and self.verbosity: fprintf(f, 'Duplicate entry %r in name_and_scope_map\n', key) name_and_scope_map[key] = nobj sort_data = (nobj.scope, namex, nobj) # namex (a temp unique ID) ensures the Name objects will not # be compared (fatal in py3) if name_lcase in name_map: name_map[name_lcase].append(sort_data) else: name_map[name_lcase] = [sort_data] for key in name_map.keys(): alist = name_map[key] alist.sort() name_map[key] = [x[2] for x in alist] self.name_and_scope_map = name_and_scope_map self.name_map = name_map def handle_obj(self, data): # Not doing much handling at all. # Worrying about embedded (BOF ... EOF) substreams is done elsewhere. # DEBUG = 1 obj_type, obj_id = unpack(' handle_obj type=%d id=0x%08x" % (obj_type, obj_id) def handle_supbook(self, data): # aka EXTERNALBOOK in OOo docs self._supbook_types.append(None) blah = DEBUG or self.verbosity >= 2 if blah: print("SUPBOOK:", file=self.logfile) hex_char_dump(data, 0, len(data), fout=self.logfile) num_sheets = unpack("= 2: fprintf(self.logfile, "SST: unique strings: %d\n", uniquestrings) while 1: code, nb, data = self.get_record_parts_conditional(XL_CONTINUE) if code is None: break nbt += nb if DEBUG >= 2: fprintf(self.logfile, "CONTINUE: adding %d bytes to SST -> %d\n", nb, nbt) strlist.append(data) self._sharedstrings, rt_runlist = unpack_SST_table(strlist, uniquestrings) if self.formatting_info: self._rich_text_runlist_map = rt_runlist if DEBUG: t1 = time.time() print("SST processing took %.2f seconds" % (t1 - t0, ), file=self.logfile) def handle_writeaccess(self, data): DEBUG = 0 if self.biff_version < 80: if not self.encoding: self.raw_user_name = True self.user_name = data return strg = unpack_string(data, 0, self.encoding, lenlen=1) else: strg = unpack_unicode(data, 0, lenlen=2) if DEBUG: fprintf(self.logfile, "WRITEACCESS: %d bytes; raw=%s %r\n", len(data), self.raw_user_name, strg) strg = strg.rstrip() self.user_name = strg def parse_globals(self): # DEBUG = 0 # no need to position, just start reading (after the BOF) formatting.initialise_book(self) while 1: rc, length, data = self.get_record_parts() if DEBUG: print("parse_globals: record code is 0x%04x" % rc, file=self.logfile) if rc == XL_SST: self.handle_sst(data) elif rc == XL_FONT or rc == XL_FONT_B3B4: self.handle_font(data) elif rc == XL_FORMAT: # XL_FORMAT2 is BIFF <= 3.0, can't appear in globals self.handle_format(data) elif rc == XL_XF: self.handle_xf(data) elif rc == XL_BOUNDSHEET: self.handle_boundsheet(data) elif rc == XL_DATEMODE: self.handle_datemode(data) elif rc == XL_CODEPAGE: self.handle_codepage(data) elif rc == XL_COUNTRY: self.handle_country(data) elif rc == XL_EXTERNNAME: self.handle_externname(data) elif rc == XL_EXTERNSHEET: self.handle_externsheet(data) elif rc == XL_FILEPASS: self.handle_filepass(data) elif rc == XL_WRITEACCESS: self.handle_writeaccess(data) elif rc == XL_SHEETSOFFSET: self.handle_sheetsoffset(data) elif rc == XL_SHEETHDR: self.handle_sheethdr(data) elif rc == XL_SUPBOOK: self.handle_supbook(data) elif rc == XL_NAME: self.handle_name(data) elif rc == XL_PALETTE: self.handle_palette(data) elif rc == XL_STYLE: self.handle_style(data) elif rc & 0xff == 9 and self.verbosity: fprintf(self.logfile, "*** Unexpected BOF at posn %d: 0x%04x len=%d data=%r\n", self._position - length - 4, rc, length, data) elif rc == XL_EOF: self.xf_epilogue() self.names_epilogue() self.palette_epilogue() if not self.encoding: self.derive_encoding() if self.biff_version == 45: # DEBUG = 0 if DEBUG: print("global EOF: position", self._position, file=self.logfile) # if DEBUG: # pos = self._position - 4 # print repr(self.mem[pos:pos+40]) return else: # if DEBUG: # print >> self.logfile, "parse_globals: ignoring record code 0x%04x" % rc pass def read(self, pos, length): data = self.mem[pos:pos+length] self._position = pos + len(data) return data def getbof(self, rqd_stream): # DEBUG = 1 # if DEBUG: print >> self.logfile, "getbof(): position", self._position if DEBUG: print("reqd: 0x%04x" % rqd_stream, file=self.logfile) def bof_error(msg): raise XLRDError('Unsupported format, or corrupt file: ' + msg) savpos = self._position opcode = self.get2bytes() if opcode == MY_EOF: bof_error('Expected BOF record; met end of file') if opcode not in bofcodes: bof_error('Expected BOF record; found %r' % self.mem[savpos:savpos+8]) length = self.get2bytes() if length == MY_EOF: bof_error('Incomplete BOF record[1]; met end of file') if not (4 <= length <= 20): bof_error( 'Invalid length (%d) for BOF record type 0x%04x' % (length, opcode)) padding = b'\0' * max(0, boflen[opcode] - length) data = self.read(self._position, length); if DEBUG: fprintf(self.logfile, "\ngetbof(): data=%r\n", data) if len(data) < length: bof_error('Incomplete BOF record[2]; met end of file') data += padding version1 = opcode >> 8 version2, streamtype = unpack('= 2: print("BOF: op=0x%04x vers=0x%04x stream=0x%04x buildid=%d buildyr=%d -> BIFF%d" \ % (opcode, version2, streamtype, build, year, version), file=self.logfile) got_globals = streamtype == XL_WORKBOOK_GLOBALS or ( version == 45 and streamtype == XL_WORKBOOK_GLOBALS_4W) if (rqd_stream == XL_WORKBOOK_GLOBALS and got_globals) or streamtype == rqd_stream: return version if version < 50 and streamtype == XL_WORKSHEET: return version if version >= 50 and streamtype == 0x0100: bof_error("Workspace file -- no spreadsheet data") bof_error( 'BOF not workbook/worksheet: op=0x%04x vers=0x%04x strm=0x%04x build=%d year=%d -> BIFF%d' \ % (opcode, version2, streamtype, build, year, version) ) # === helper functions def expand_cell_address(inrow, incol): # Ref : OOo docs, "4.3.4 Cell Addresses in BIFF8" outrow = inrow if incol & 0x8000: if outrow >= 32768: outrow -= 65536 relrow = 1 else: relrow = 0 outcol = incol & 0xFF if incol & 0x4000: if outcol >= 128: outcol -= 256 relcol = 1 else: relcol = 0 return outrow, outcol, relrow, relcol def colname(colx, _A2Z="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): assert colx >= 0 name = UNICODE_LITERAL('') while 1: quot, rem = divmod(colx, 26) name = _A2Z[rem] + name if not quot: return name colx = quot - 1 def display_cell_address(rowx, colx, relrow, relcol): if relrow: rowpart = "(*%s%d)" % ("+-"[rowx < 0], abs(rowx)) else: rowpart = "$%d" % (rowx+1,) if relcol: colpart = "(*%s%d)" % ("+-"[colx < 0], abs(colx)) else: colpart = "$" + colname(colx) return colpart + rowpart def unpack_SST_table(datatab, nstrings): "Return list of strings" datainx = 0 ndatas = len(datatab) data = datatab[0] datalen = len(data) pos = 8 strings = [] strappend = strings.append richtext_runs = {} local_unpack = unpack local_min = min local_BYTES_ORD = BYTES_ORD latin_1 = "latin_1" for _unused_i in xrange(nstrings): nchars = local_unpack('> 1, charsneed) rawstrg = data[pos:pos+2*charsavail] # if DEBUG: print "SST U16: nchars=%d pos=%d rawstrg=%r" % (nchars, pos, rawstrg) try: accstrg += unicode(rawstrg, "utf_16_le") except: # print "SST U16: nchars=%d pos=%d rawstrg=%r" % (nchars, pos, rawstrg) # Probable cause: dodgy data e.g. unfinished surrogate pair. # E.g. file unicode2.xls in pyExcelerator's examples has cells containing # unichr(i) for i in range(0x100000) # so this will include 0xD800 etc raise pos += 2*charsavail else: # Note: this is COMPRESSED (not ASCII!) encoding!!! charsavail = local_min(datalen - pos, charsneed) rawstrg = data[pos:pos+charsavail] # if DEBUG: print "SST CMPRSD: nchars=%d pos=%d rawstrg=%r" % (nchars, pos, rawstrg) accstrg += unicode(rawstrg, latin_1) pos += charsavail charsgot += charsavail if charsgot == nchars: break datainx += 1 data = datatab[datainx] datalen = len(data) options = local_BYTES_ORD(data[0]) pos = 1 if rtcount: runs = [] for runindex in xrange(rtcount): if pos == datalen: pos = 0 datainx += 1 data = datatab[datainx] datalen = len(data) runs.append(local_unpack("= datalen: # adjust to correct position in next record pos = pos - datalen datainx += 1 if datainx < ndatas: data = datatab[datainx] datalen = len(data) else: assert _unused_i == nstrings - 1 strappend(accstrg) return strings, richtext_runs xlrd-0.9.4/xlrd/compdoc.py0000644000076500000240000005136312270704230015704 0ustar chrisstaff00000000000000# -*- coding: cp1252 -*- ## # Implements the minimal functionality required # to extract a "Workbook" or "Book" stream (as one big string) # from an OLE2 Compound Document file. #

    Copyright � 2005-2012 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. # 2008-11-04 SJM Avoid assertion error when -1 used instead of -2 for first_SID of empty SCSS [Frank Hoffsuemmer] # 2007-09-08 SJM Warning message if sector sizes are extremely large. # 2007-05-07 SJM Meaningful exception instead of IndexError if a SAT (sector allocation table) is corrupted. # 2007-04-22 SJM Missing "<" in a struct.unpack call => can't open files on bigendian platforms. from __future__ import print_function import sys from struct import unpack from .timemachine import * import array ## # Magic cookie that should appear in the first 8 bytes of the file. SIGNATURE = b"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" EOCSID = -2 FREESID = -1 SATSID = -3 MSATSID = -4 EVILSID = -5 class CompDocError(Exception): pass class DirNode(object): def __init__(self, DID, dent, DEBUG=0, logfile=sys.stdout): # dent is the 128-byte directory entry self.DID = DID self.logfile = logfile (cbufsize, self.etype, self.colour, self.left_DID, self.right_DID, self.root_DID) = \ unpack(' 20: # allows for 2**20 bytes i.e. 1MB print("WARNING: sector size (2**%d) is preposterous; assuming 512 and continuing ..." \ % ssz, file=logfile) ssz = 9 if sssz > ssz: print("WARNING: short stream sector size (2**%d) is preposterous; assuming 64 and continuing ..." \ % sssz, file=logfile) sssz = 6 self.sec_size = sec_size = 1 << ssz self.short_sec_size = 1 << sssz if self.sec_size != 512 or self.short_sec_size != 64: print("@@@@ sec_size=%d short_sec_size=%d" % (self.sec_size, self.short_sec_size), file=logfile) ( SAT_tot_secs, self.dir_first_sec_sid, _unused, self.min_size_std_stream, SSAT_first_sec_sid, SSAT_tot_secs, MSATX_first_sec_sid, MSATX_tot_secs, # ) = unpack(' 1: print('MSATX: sid=%d (0x%08X)' % (sid, sid), file=logfile) if sid >= mem_data_secs: msg = "MSAT extension: accessing sector %d but only %d in file" % (sid, mem_data_secs) if DEBUG > 1: print(msg, file=logfile) break raise CompDocError(msg) elif sid < 0: raise CompDocError("MSAT extension: invalid sector id: %d" % sid) if seen[sid]: raise CompDocError("MSAT corruption: seen[%d] == %d" % (sid, seen[sid])) seen[sid] = 1 actual_MSATX_sectors += 1 if DEBUG and actual_MSATX_sectors > expected_MSATX_sectors: print("[1]===>>>", mem_data_secs, nent, SAT_sectors_reqd, expected_MSATX_sectors, actual_MSATX_sectors, file=logfile) offset = 512 + sec_size * sid MSAT.extend(unpack(fmt, mem[offset:offset+sec_size])) sid = MSAT.pop() # last sector id is sid of next sector in the chain if DEBUG and actual_MSATX_sectors != expected_MSATX_sectors: print("[2]===>>>", mem_data_secs, nent, SAT_sectors_reqd, expected_MSATX_sectors, actual_MSATX_sectors, file=logfile) if DEBUG: print("MSAT: len =", len(MSAT), file=logfile) dump_list(MSAT, 10, logfile) # # === build the SAT === # self.SAT = [] actual_SAT_sectors = 0 dump_again = 0 for msidx in xrange(len(MSAT)): msid = MSAT[msidx] if msid in (FREESID, EOCSID): # Specification: the MSAT array may be padded with trailing FREESID entries. # Toleration: a FREESID or EOCSID entry anywhere in the MSAT array will be ignored. continue if msid >= mem_data_secs: if not trunc_warned: print("WARNING *** File is truncated, or OLE2 MSAT is corrupt!!", file=logfile) print("INFO: Trying to access sector %d but only %d available" \ % (msid, mem_data_secs), file=logfile) trunc_warned = 1 MSAT[msidx] = EVILSID dump_again = 1 continue elif msid < -2: raise CompDocError("MSAT: invalid sector id: %d" % msid) if seen[msid]: raise CompDocError("MSAT extension corruption: seen[%d] == %d" % (msid, seen[msid])) seen[msid] = 2 actual_SAT_sectors += 1 if DEBUG and actual_SAT_sectors > SAT_sectors_reqd: print("[3]===>>>", mem_data_secs, nent, SAT_sectors_reqd, expected_MSATX_sectors, actual_MSATX_sectors, actual_SAT_sectors, msid, file=logfile) offset = 512 + sec_size * msid self.SAT.extend(unpack(fmt, mem[offset:offset+sec_size])) if DEBUG: print("SAT: len =", len(self.SAT), file=logfile) dump_list(self.SAT, 10, logfile) # print >> logfile, "SAT ", # for i, s in enumerate(self.SAT): # print >> logfile, "entry: %4d offset: %6d, next entry: %4d" % (i, 512 + sec_size * i, s) # print >> logfile, "%d:%d " % (i, s), print(file=logfile) if DEBUG and dump_again: print("MSAT: len =", len(MSAT), file=logfile) dump_list(MSAT, 10, logfile) for satx in xrange(mem_data_secs, len(self.SAT)): self.SAT[satx] = EVILSID print("SAT: len =", len(self.SAT), file=logfile) dump_list(self.SAT, 10, logfile) # # === build the directory === # dbytes = self._get_stream( self.mem, 512, self.SAT, self.sec_size, self.dir_first_sec_sid, name="directory", seen_id=3) dirlist = [] did = -1 for pos in xrange(0, len(dbytes), 128): did += 1 dirlist.append(DirNode(did, dbytes[pos:pos+128], 0, logfile)) self.dirlist = dirlist _build_family_tree(dirlist, 0, dirlist[0].root_DID) # and stand well back ... if DEBUG: for d in dirlist: d.dump(DEBUG) # # === get the SSCS === # sscs_dir = self.dirlist[0] assert sscs_dir.etype == 5 # root entry if sscs_dir.first_SID < 0 or sscs_dir.tot_size == 0: # Problem reported by Frank Hoffsuemmer: some software was # writing -1 instead of -2 (EOCSID) for the first_SID # when the SCCS was empty. Not having EOCSID caused assertion # failure in _get_stream. # Solution: avoid calling _get_stream in any case when the # SCSS appears to be empty. self.SSCS = "" else: self.SSCS = self._get_stream( self.mem, 512, self.SAT, sec_size, sscs_dir.first_SID, sscs_dir.tot_size, name="SSCS", seen_id=4) # if DEBUG: print >> logfile, "SSCS", repr(self.SSCS) # # === build the SSAT === # self.SSAT = [] if SSAT_tot_secs > 0 and sscs_dir.tot_size == 0: print("WARNING *** OLE2 inconsistency: SSCS size is 0 but SSAT size is non-zero", file=logfile) if sscs_dir.tot_size > 0: sid = SSAT_first_sec_sid nsecs = SSAT_tot_secs while sid >= 0 and nsecs > 0: if seen[sid]: raise CompDocError("SSAT corruption: seen[%d] == %d" % (sid, seen[sid])) seen[sid] = 5 nsecs -= 1 start_pos = 512 + sid * sec_size news = list(unpack(fmt, mem[start_pos:start_pos+sec_size])) self.SSAT.extend(news) sid = self.SAT[sid] if DEBUG: print("SSAT last sid %d; remaining sectors %d" % (sid, nsecs), file=logfile) assert nsecs == 0 and sid == EOCSID if DEBUG: print("SSAT", file=logfile) dump_list(self.SSAT, 10, logfile) if DEBUG: print("seen", file=logfile) dump_list(seen, 20, logfile) def _get_stream(self, mem, base, sat, sec_size, start_sid, size=None, name='', seen_id=None): # print >> self.logfile, "_get_stream", base, sec_size, start_sid, size sectors = [] s = start_sid if size is None: # nothing to check against while s >= 0: if seen_id is not None: if self.seen[s]: raise CompDocError("%s corruption: seen[%d] == %d" % (name, s, self.seen[s])) self.seen[s] = seen_id start_pos = base + s * sec_size sectors.append(mem[start_pos:start_pos+sec_size]) try: s = sat[s] except IndexError: raise CompDocError( "OLE2 stream %r: sector allocation table invalid entry (%d)" % (name, s) ) assert s == EOCSID else: todo = size while s >= 0: if seen_id is not None: if self.seen[s]: raise CompDocError("%s corruption: seen[%d] == %d" % (name, s, self.seen[s])) self.seen[s] = seen_id start_pos = base + s * sec_size grab = sec_size if grab > todo: grab = todo todo -= grab sectors.append(mem[start_pos:start_pos+grab]) try: s = sat[s] except IndexError: raise CompDocError( "OLE2 stream %r: sector allocation table invalid entry (%d)" % (name, s) ) assert s == EOCSID if todo != 0: fprintf(self.logfile, "WARNING *** OLE2 stream %r: expected size %d, actual size %d\n", name, size, size - todo) return b''.join(sectors) def _dir_search(self, path, storage_DID=0): # Return matching DirNode instance, or None head = path[0] tail = path[1:] dl = self.dirlist for child in dl[storage_DID].children: if dl[child].name.lower() == head.lower(): et = dl[child].etype if et == 2: return dl[child] if et == 1: if not tail: raise CompDocError("Requested component is a 'storage'") return self._dir_search(tail, child) dl[child].dump(1) raise CompDocError("Requested stream is not a 'user stream'") return None ## # Interrogate the compound document's directory; return the stream as a string if found, otherwise # return None. # @param qname Name of the desired stream e.g. u'Workbook'. Should be in Unicode or convertible thereto. def get_named_stream(self, qname): d = self._dir_search(qname.split("/")) if d is None: return None if d.tot_size >= self.min_size_std_stream: return self._get_stream( self.mem, 512, self.SAT, self.sec_size, d.first_SID, d.tot_size, name=qname, seen_id=d.DID+6) else: return self._get_stream( self.SSCS, 0, self.SSAT, self.short_sec_size, d.first_SID, d.tot_size, name=qname + " (from SSCS)", seen_id=None) ## # Interrogate the compound document's directory. # If the named stream is not found, (None, 0, 0) will be returned. # If the named stream is found and is contiguous within the original byte sequence ("mem") # used when the document was opened, # then (mem, offset_to_start_of_stream, length_of_stream) is returned. # Otherwise a new string is built from the fragments and (new_string, 0, length_of_stream) is returned. # @param qname Name of the desired stream e.g. u'Workbook'. Should be in Unicode or convertible thereto. def locate_named_stream(self, qname): d = self._dir_search(qname.split("/")) if d is None: return (None, 0, 0) if d.tot_size > self.mem_data_len: raise CompDocError("%r stream length (%d bytes) > file data size (%d bytes)" % (qname, d.tot_size, self.mem_data_len)) if d.tot_size >= self.min_size_std_stream: result = self._locate_stream( self.mem, 512, self.SAT, self.sec_size, d.first_SID, d.tot_size, qname, d.DID+6) if self.DEBUG: print("\nseen", file=self.logfile) dump_list(self.seen, 20, self.logfile) return result else: return ( self._get_stream( self.SSCS, 0, self.SSAT, self.short_sec_size, d.first_SID, d.tot_size, qname + " (from SSCS)", None), 0, d.tot_size ) def _locate_stream(self, mem, base, sat, sec_size, start_sid, expected_stream_size, qname, seen_id): # print >> self.logfile, "_locate_stream", base, sec_size, start_sid, expected_stream_size s = start_sid if s < 0: raise CompDocError("_locate_stream: start_sid (%d) is -ve" % start_sid) p = -99 # dummy previous SID start_pos = -9999 end_pos = -8888 slices = [] tot_found = 0 found_limit = (expected_stream_size + sec_size - 1) // sec_size while s >= 0: if self.seen[s]: print("_locate_stream(%s): seen" % qname, file=self.logfile); dump_list(self.seen, 20, self.logfile) raise CompDocError("%s corruption: seen[%d] == %d" % (qname, s, self.seen[s])) self.seen[s] = seen_id tot_found += 1 if tot_found > found_limit: raise CompDocError( "%s: size exceeds expected %d bytes; corrupt?" % (qname, found_limit * sec_size) ) # Note: expected size rounded up to higher sector if s == p+1: # contiguous sectors end_pos += sec_size else: # start new slice if p >= 0: # not first time slices.append((start_pos, end_pos)) start_pos = base + s * sec_size end_pos = start_pos + sec_size p = s s = sat[s] assert s == EOCSID assert tot_found == found_limit # print >> self.logfile, "_locate_stream(%s): seen" % qname; dump_list(self.seen, 20, self.logfile) if not slices: # The stream is contiguous ... just what we like! return (mem, start_pos, expected_stream_size) slices.append((start_pos, end_pos)) # print >> self.logfile, "+++>>> %d fragments" % len(slices) return (b''.join([mem[start_pos:end_pos] for start_pos, end_pos in slices]), 0, expected_stream_size) # ========================================================================================== def x_dump_line(alist, stride, f, dpos, equal=0): print("%5d%s" % (dpos, " ="[equal]), end=' ', file=f) for value in alist[dpos:dpos + stride]: print(str(value), end=' ', file=f) print(file=f) def dump_list(alist, stride, f=sys.stdout): def _dump_line(dpos, equal=0): print("%5d%s" % (dpos, " ="[equal]), end=' ', file=f) for value in alist[dpos:dpos + stride]: print(str(value), end=' ', file=f) print(file=f) pos = None oldpos = None for pos in xrange(0, len(alist), stride): if oldpos is None: _dump_line(pos) oldpos = pos elif alist[pos:pos+stride] != alist[oldpos:oldpos+stride]: if pos - oldpos > stride: _dump_line(pos - stride, equal=1) _dump_line(pos) oldpos = pos if oldpos is not None and pos is not None and pos != oldpos: _dump_line(pos, equal=1) xlrd-0.9.4/xlrd/doc/0000755000076500000240000000000012551375765014466 5ustar chrisstaff00000000000000xlrd-0.9.4/xlrd/doc/compdoc.html0000644000076500000240000000575712155372403017000 0ustar chrisstaff00000000000000 The compdoc Module

    The compdoc Module

    Implements the minimal functionality required to extract a "Workbook" or "Book" stream (as one big string) from an OLE2 Compound Document file.

    Copyright © 2005-2012 Stephen John Machin, Lingfo Pty Ltd

    This module is part of the xlrd package, which is released under a BSD-style licence.

    Module Contents

    CompDoc(mem, logfile=sys.stdout, DEBUG=0) (class) [#]

    Compound document handler.

    mem
    The raw contents of the file, as a string, or as an mmap.mmap() object. The only operation it needs to support is slicing.

    For more information about this class, see The CompDoc Class.

    SIGNATURE (variable) [#]

    Magic cookie that should appear in the first 8 bytes of the file.

    The CompDoc Class

    CompDoc(mem, logfile=sys.stdout, DEBUG=0) (class) [#]

    Compound document handler.

    mem
    The raw contents of the file, as a string, or as an mmap.mmap() object. The only operation it needs to support is slicing.

    get_named_stream(qname) [#]

    Interrogate the compound document's directory; return the stream as a string if found, otherwise return None.

    qname
    Name of the desired stream e.g. u'Workbook'. Should be in Unicode or convertible thereto.

    locate_named_stream(qname) [#]

    Interrogate the compound document's directory. If the named stream is not found, (None, 0, 0) will be returned. If the named stream is found and is contiguous within the original byte sequence ("mem") used when the document was opened, then (mem, offset_to_start_of_stream, length_of_stream) is returned. Otherwise a new string is built from the fragments and (new_string, 0, length_of_stream) is returned.

    qname
    Name of the desired stream e.g. u'Workbook'. Should be in Unicode or convertible thereto.

    xlrd-0.9.4/xlrd/doc/xlrd.html0000644000076500000240000031221212155372403016310 0ustar chrisstaff00000000000000 The xlrd Module

    The xlrd Module

    A Python module for extracting data from MS Excel (TM) spreadsheet files.

    Version 0.7.4 -- April 2012

    General information

    Acknowledgements

    Development of this module would not have been possible without the document "OpenOffice.org's Documentation of the Microsoft Excel File Format" ("OOo docs" for short). The latest version is available from OpenOffice.org in PDF format and ODT format. Small portions of the OOo docs are reproduced in this document. A study of the OOo docs is recommended for those who wish a deeper understanding of the Excel file layout than the xlrd docs can provide.

    Backporting to Python 2.1 was partially funded by Journyx - provider of timesheet and project accounting solutions.

    Provision of formatting information in version 0.6.1 was funded by Simplistix Ltd.

    Unicode

    This module presents all text strings as Python unicode objects. From Excel 97 onwards, text in Excel spreadsheets has been stored as Unicode. Older files (Excel 95 and earlier) don't keep strings in Unicode; a CODEPAGE record provides a codepage number (for example, 1252) which is used by xlrd to derive the encoding (for same example: "cp1252") which is used to translate to Unicode.

    If the CODEPAGE record is missing (possible if the file was created by third-party software), xlrd will assume that the encoding is ascii, and keep going. If the actual encoding is not ascii, a UnicodeDecodeError exception will be raised and you will need to determine the encoding yourself, and tell xlrd:

        book = xlrd.open_workbook(..., encoding_override="cp1252")
    

    If the CODEPAGE record exists but is wrong (for example, the codepage number is 1251, but the strings are actually encoded in koi8_r), it can be overridden using the same mechanism. The supplied runxlrd.py has a corresponding command-line argument, which may be used for experimentation:

        runxlrd.py -e koi8_r 3rows myfile.xls
    

    The first place to look for an encoding ("codec name") is the Python documentation.

    Dates in Excel spreadsheets

    In reality, there are no such things. What you have are floating point numbers and pious hope. There are several problems with Excel dates:

    (1) Dates are not stored as a separate data type; they are stored as floating point numbers and you have to rely on (a) the "number format" applied to them in Excel and/or (b) knowing which cells are supposed to have dates in them. This module helps with (a) by inspecting the format that has been applied to each number cell; if it appears to be a date format, the cell is classified as a date rather than a number. Feedback on this feature, especially from non-English-speaking locales, would be appreciated.

    (2) Excel for Windows stores dates by default as the number of days (or fraction thereof) since 1899-12-31T00:00:00. Excel for Macintosh uses a default start date of 1904-01-01T00:00:00. The date system can be changed in Excel on a per-workbook basis (for example: Tools -> Options -> Calculation, tick the "1904 date system" box). This is of course a bad idea if there are already dates in the workbook. There is no good reason to change it even if there are no dates in the workbook. Which date system is in use is recorded in the workbook. A workbook transported from Windows to Macintosh (or vice versa) will work correctly with the host Excel. When using this module's xldate_as_tuple function to convert numbers from a workbook, you must use the datemode attribute of the Book object. If you guess, or make a judgement depending on where you believe the workbook was created, you run the risk of being 1462 days out of kilter.

    Reference: http://support.microsoft.com/default.aspx?scid=KB;EN-US;q180162

    (3) The Excel implementation of the Windows-default 1900-based date system works on the incorrect premise that 1900 was a leap year. It interprets the number 60 as meaning 1900-02-29, which is not a valid date. Consequently any number less than 61 is ambiguous. Example: is 59 the result of 1900-02-28 entered directly, or is it 1900-03-01 minus 2 days? The OpenOffice.org Calc program "corrects" the Microsoft problem; entering 1900-02-27 causes the number 59 to be stored. Save as an XLS file, then open the file with Excel -- you'll see 1900-02-28 displayed.

    Reference: http://support.microsoft.com/default.aspx?scid=kb;en-us;214326

    (4) The Macintosh-default 1904-based date system counts 1904-01-02 as day 1 and 1904-01-01 as day zero. Thus any number such that (0.0 <= number < 1.0) is ambiguous. Is 0.625 a time of day (15:00:00), independent of the calendar, or should it be interpreted as an instant on a particular day (1904-01-01T15:00:00)? The xldate_* functions in this module take the view that such a number is a calendar-independent time of day (like Python's datetime.time type) for both date systems. This is consistent with more recent Microsoft documentation (for example, the help file for Excel 2002 which says that the first day in the 1904 date system is 1904-01-02).

    (5) Usage of the Excel DATE() function may leave strange dates in a spreadsheet. Quoting the help file, in respect of the 1900 date system: "If year is between 0 (zero) and 1899 (inclusive), Excel adds that value to 1900 to calculate the year. For example, DATE(108,1,2) returns January 2, 2008 (1900+108)." This gimmick, semi-defensible only for arguments up to 99 and only in the pre-Y2K-awareness era, means that DATE(1899, 12, 31) is interpreted as 3799-12-31.

    For further information, please refer to the documentation for the xldate_* functions.

    Named references, constants, formulas, and macros

    A name is used to refer to a cell, a group of cells, a constant value, a formula, or a macro. Usually the scope of a name is global across the whole workbook. However it can be local to a worksheet. For example, if the sales figures are in different cells in different sheets, the user may define the name "Sales" in each sheet. There are built-in names, like "Print_Area" and "Print_Titles"; these two are naturally local to a sheet.

    To inspect the names with a user interface like MS Excel, OOo Calc, or Gnumeric, click on Insert/Names/Define. This will show the global names, plus those local to the currently selected sheet.

    A Book object provides two dictionaries (name_map and name_and_scope_map) and a list (name_obj_list) which allow various ways of accessing the Name objects. There is one Name object for each NAME record found in the workbook. Name objects have many attributes, several of which are relevant only when obj.macro is 1.

    In the examples directory you will find namesdemo.xls which showcases the many different ways that names can be used, and xlrdnamesAPIdemo.py which offers 3 different queries for inspecting the names in your files, and shows how to extract whatever a name is referring to. There is currently one "convenience method", Name.cell(), which extracts the value in the case where the name refers to a single cell. More convenience methods are planned. The source code for Name.cell (in __init__.py) is an extra source of information on how the Name attributes hang together.

    Name information is not extracted from files older than Excel 5.0 (Book.biff_version < 50)

    Formatting

    Introduction

    This collection of features, new in xlrd version 0.6.1, is intended to provide the information needed to (1) display/render spreadsheet contents (say) on a screen or in a PDF file, and (2) copy spreadsheet data to another file without losing the ability to display/render it.

    The Palette; Colour Indexes

    A colour is represented in Excel as a (red, green, blue) ("RGB") tuple with each component in range(256). However it is not possible to access an unlimited number of colours; each spreadsheet is limited to a palette of 64 different colours (24 in Excel 3.0 and 4.0, 8 in Excel 2.0). Colours are referenced by an index ("colour index") into this palette. Colour indexes 0 to 7 represent 8 fixed built-in colours: black, white, red, green, blue, yellow, magenta, and cyan.

    The remaining colours in the palette (8 to 63 in Excel 5.0 and later) can be changed by the user. In the Excel 2003 UI, Tools/Options/Color presents a palette of 7 rows of 8 colours. The last two rows are reserved for use in charts.
    The correspondence between this grid and the assigned colour indexes is NOT left-to-right top-to-bottom.
    Indexes 8 to 15 correspond to changeable parallels of the 8 fixed colours -- for example, index 7 is forever cyan; index 15 starts off being cyan but can be changed by the user.
    The default colour for each index depends on the file version; tables of the defaults are available in the source code. If the user changes one or more colours, a PALETTE record appears in the XLS file -- it gives the RGB values for *all* changeable indexes.
    Note that colours can be used in "number formats": "[CYAN]...." and "[COLOR8]...." refer to colour index 7; "[COLOR16]...." will produce cyan unless the user changes colour index 15 to something else.

    In addition, there are several "magic" colour indexes used by Excel:
    0x18 (BIFF3-BIFF4), 0x40 (BIFF5-BIFF8): System window text colour for border lines (used in XF, CF, and WINDOW2 records)
    0x19 (BIFF3-BIFF4), 0x41 (BIFF5-BIFF8): System window background colour for pattern background (used in XF and CF records )
    0x43: System face colour (dialogue background colour)
    0x4D: System window text colour for chart border lines
    0x4E: System window background colour for chart areas
    0x4F: Automatic colour for chart border lines (seems to be always Black)
    0x50: System ToolTip background colour (used in note objects)
    0x51: System ToolTip text colour (used in note objects)
    0x7FFF: System window text colour for fonts (used in FONT and CF records)
    Note 0x7FFF appears to be the *default* colour index. It appears quite often in FONT records.

    Default Formatting

    Default formatting is applied to all empty cells (those not described by a cell record). Firstly row default information (ROW record, Rowinfo class) is used if available. Failing that, column default information (COLINFO record, Colinfo class) is used if available. As a last resort the worksheet/workbook default cell format will be used; this should always be present in an Excel file, described by the XF record with the fixed index 15 (0-based). By default, it uses the worksheet/workbook default cell style, described by the very first XF record (index 0).

    Formatting features not included in xlrd version 0.6.1

    • Rich text i.e. strings containing partial bold italic and underlined text, change of font inside a string, etc. See OOo docs s3.4 and s3.2. Rich text is included in version 0.7.2
    • Asian phonetic text (known as "ruby"), used for Japanese furigana. See OOo docs s3.4.2 (p15)
    • Conditional formatting. See OOo docs s5.12, s6.21 (CONDFMT record), s6.16 (CF record)
    • Miscellaneous sheet-level and book-level items e.g. printing layout, screen panes.
    • Modern Excel file versions don't keep most of the built-in "number formats" in the file; Excel loads formats according to the user's locale. Currently xlrd's emulation of this is limited to a hard-wired table that applies to the US English locale. This may mean that currency symbols, date order, thousands separator, decimals separator, etc are inappropriate. Note that this does not affect users who are copying XLS files, only those who are visually rendering cells.

    Loading worksheets on demand

    This feature, new in version 0.7.1, is governed by the on_demand argument to the open_workbook() function and allows saving memory and time by loading only those sheets that the caller is interested in, and releasing sheets when no longer required.

    on_demand=False (default): No change. open_workbook() loads global data and all sheets, releases resources no longer required (principally the str or mmap object containing the Workbook stream), and returns.

    on_demand=True and BIFF version < 5.0: A warning message is emitted, on_demand is recorded as False, and the old process is followed.

    on_demand=True and BIFF version >= 5.0: open_workbook() loads global data and returns without releasing resources. At this stage, the only information available about sheets is Book.nsheets and Book.sheet_names().

    Book.sheet_by_name() and Book.sheet_by_index() will load the requested sheet if it is not already loaded.

    Book.sheets() will load all/any unloaded sheets.

    The caller may save memory by calling Book.unload_sheet(sheet_name_or_index) when finished with the sheet. This applies irrespective of the state of on_demand.

    The caller may re-load an unloaded sheet by calling Book.sheet_by_xxxx() -- except if those required resources have been released (which will have happened automatically when on_demand is false). This is the only case where an exception will be raised.

    The caller may query the state of a sheet: Book.sheet_loaded(sheet_name_or_index) -> a bool

    Book.release_resources() may used to save memory and close any memory-mapped file before proceding to examine already-loaded sheets. Once resources are released, no further sheets can be loaded.

    When using on-demand, it is advisable to ensure that Book.release_resources() is always called even if an exception is raised in your own code; otherwise if the input file has been memory-mapped, the mmap.mmap object will not be closed and you will not be able to access the physical file until your Python process terminates. This can be done by calling Book.release_resources() explicitly in the finally suite of a try/finally block. New in xlrd 0.7.2: the Book object is a "context manager", so if using Python 2.5 or later, you can wrap your code in a "with" statement.

    Module Contents

    BaseObject (class) [#]

    Parent of almost all other classes in the package.

    For more information about this class, see The BaseObject Class.

    Book() (class) [#]

    Contents of a "workbook".

    For more information about this class, see The Book Class.

    Cell(ctype, value, xf_index=None) (class) [#]

    Contains the data for one cell.

    For more information about this class, see The Cell Class.

    cellname(rowx, colx) [#]

    Utility function: (5, 7) => 'H6'

    cellnameabs(rowx, colx, r1c1=0) [#]

    Utility function: (5, 7) => '$H$6'

    Colinfo (class) [#]

    Width and default formatting information that applies to one or more columns in a sheet.

    For more information about this class, see The Colinfo Class.

    colname(colx) [#]

    Utility function: 7 => 'H', 27 => 'AB'

    count_records(filename, outfile=sys.stdout) [#]

    For debugging and analysis: summarise the file's BIFF records. I.e. produce a sorted file of (record_name, count).

    filename
    The path to the file to be summarised.
    outfile
    An open file, to which the summary is written.

    dump(filename, outfile=sys.stdout, unnumbered=False) [#]

    For debugging: dump the file's BIFF records in char & hex.

    filename
    The path to the file to be dumped.
    outfile
    An open file, to which the dump is written.
    unnumbered
    If true, omit offsets (for meaningful diffs).

    empty_cell (variable) [#]

    There is one and only one instance of an empty cell -- it's a singleton. This is it. You may use a test like "acell is empty_cell".

    EqNeAttrs (class) [#]

    This mixin class exists solely so that Format, Font, and XF....

    For more information about this class, see The EqNeAttrs Class.

    error_text_from_code (variable) [#]

    This dictionary can be used to produce a text version of the internal codes that Excel uses for error cells. Here are its contents:

    0x00: '#NULL!',  # Intersection of two cell ranges is empty
    0x07: '#DIV/0!', # Division by zero
    0x0F: '#VALUE!', # Wrong type of operand
    0x17: '#REF!',   # Illegal or deleted cell reference
    0x1D: '#NAME?',  # Wrong function or range name
    0x24: '#NUM!',   # Value range overflow
    0x2A: '#N/A',    # Argument or function not available
    

    Font (class) [#]

    An Excel "font" contains the details of not only what is normally considered a font, but also several other display attributes.

    For more information about this class, see The Font Class.

    Format(format_key, ty, format_str) (class) [#]

    "Number format" information from a FORMAT record.

    For more information about this class, see The Format Class.

    Hyperlink (class) [#]

    Contains the attributes of a hyperlink.

    For more information about this class, see The Hyperlink Class.

    Name (class) [#]

    Information relating to a named reference, formula, macro, etc.

    For more information about this class, see The Name Class.

    Note (class) [#]

    Represents a user "comment" or "note".

    For more information about this class, see The Note Class.

    open_workbook(filename=None, logfile=sys.stdout, verbosity=0, use_mmap=USE_MMAP, file_contents=None, encoding_override=None, formatting_info=False, on_demand=False, ragged_rows=False, ) [#]

    Open a spreadsheet file for data extraction.

    filename
    The path to the spreadsheet file to be opened.
    logfile
    An open file to which messages and diagnostics are written.
    verbosity
    Increases the volume of trace material written to the logfile.
    use_mmap
    Whether to use the mmap module is determined heuristically. Use this arg to override the result. Current heuristic: mmap is used if it exists.
    file_contents
    ... as a string or an mmap.mmap object or some other behave-alike object. If file_contents is supplied, filename will not be used, except (possibly) in messages.
    encoding_override
    Used to overcome missing or bad codepage information in older-version files. Refer to discussion in the Unicode section above.
    -- New in version 0.6.0
    formatting_info
    Governs provision of a reference to an XF (eXtended Format) object for each cell in the worksheet.
    Default is False. This is backwards compatible and saves memory. "Blank" cells (those with their own formatting information but no data) are treated as empty (by ignoring the file's BLANK and MULBLANK records). It cuts off any bottom "margin" of rows of empty (and blank) cells and any right "margin" of columns of empty (and blank) cells. Only cell_value and cell_type are available.
    True provides all cells, including empty and blank cells. XF information is available for each cell.
    -- New in version 0.6.1
    on_demand
    Governs whether sheets are all loaded initially or when demanded by the caller. Please refer back to the section "Loading worksheets on demand" for details.
    -- New in version 0.7.1
    ragged_rows
    False (the default) means all rows are padded out with empty cells so that all rows have the same size (Sheet.ncols). True means that there are no empty cells at the ends of rows. This can result in substantial memory savings if rows are of widely varying sizes. See also the Sheet.row_len() method.
    -- New in version 0.7.2
    Returns:
    An instance of the Book class.

    Operand(akind=None, avalue=None, arank=0, atext='?') (class) [#]

    Used in evaluating formulas.

    For more information about this class, see The Operand Class.

    rangename3d(book, ref3d) [#]

    Utility function:
    Ref3D((1, 4, 5, 20, 7, 10)) => 'Sheet2:Sheet3!$H$6:$J$20'

    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)) R1C1 mode => 'Sheet1!R[-32]C[-13]:R[-23]C[12]' A1 mode => depends on base cell (browx, bcolx)

    Ref3D(atuple) (class) [#]

    Represents an absolute or relative 3-dimensional reference to a box of one or more cells.

    For more information about this class, see The Ref3D Class.

    Rowinfo() (class) [#]

    Height and default formatting information that applies to a row in a sheet.

    For more information about this class, see The Rowinfo Class.

    Sheet(book, position, name, number) (class) [#]

    Contains the data for one worksheet.

    For more information about this class, see The Sheet Class.

    XF (class) [#]

    eXtended Formatting information for cells, rows, columns and styles.

    For more information about this class, see The XF Class.

    XFAlignment (class) [#]

    A collection of the alignment and similar attributes of an XF record.

    For more information about this class, see The XFAlignment Class.

    XFBackground (class) [#]

    A collection of the background-related attributes of an XF record.

    For more information about this class, see The XFBackground Class.

    XFBorder (class) [#]

    A collection of the border-related attributes of an XF record.

    For more information about this class, see The XFBorder Class.

    XFProtection (class) [#]

    A collection of the protection-related attributes of an XF record.

    For more information about this class, see The XFProtection Class.

    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.

    xldate
    The Excel number
    datemode
    0: 1900-based, 1: 1904-based.
    WARNING: when using this function to interpret the contents of a workbook, you should pass in the Book.datemode attribute of that workbook. Whether the workbook has ever been anywhere near a Macintosh is irrelevant.
    Returns:
    Gregorian (year, month, day, hour, minute, nearest_second).
    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.
    Raises XLDateNegative:
    xldate < 0.00
    Raises XLDateAmbiguous:
    The 1900 leap-year problem (datemode == 0 and 1.0 <= xldate < 61.0)
    Raises XLDateTooLarge:
    Gregorian year 10000 or later
    Raises XLDateBadDatemode:
    datemode arg is neither 0 nor 1
    Raises XLDateError:
    Covers the 4 specific errors

    xldate_from_date_tuple((year, month, day), datemode) [#]

    Convert a date tuple (year, month, day) to an Excel date.

    year
    Gregorian year.
    month
    1 <= month <= 12
    day
    1 <= day <= last day of that (year, month)
    datemode
    0: 1900-based, 1: 1904-based.
    Raises XLDateAmbiguous:
    The 1900 leap-year problem (datemode == 0 and 1.0 <= xldate < 61.0)
    Raises XLDateBadDatemode:
    datemode arg is neither 0 nor 1
    Raises XLDateBadTuple:
    (year, month, day) is too early/late or has invalid component(s)
    Raises XLDateError:
    Covers the specific errors

    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.

    datetime_tuple
    (year, month, day, hour, minute, second)
    datemode
    0: 1900-based, 1: 1904-based.

    xldate_from_time_tuple((hour, minute, second)) [#]

    Convert a time tuple (hour, minute, second) to an Excel "date" value (fraction of a day).

    hour
    0 <= hour < 24
    minute
    0 <= minute < 60
    second
    0 <= second < 60
    Raises XLDateBadTuple:
    Out-of-range hour, minute, or second

    The BaseObject Class

    BaseObject (class) [#]

    Parent of almost all other classes in the package. Defines a common "dump" method for debugging.

    dump(f=None, header=None, footer=None, indent=0) [#]
    f
    open file object, to which the dump is written
    header
    text to write before the dump
    footer
    text to write after the dump
    indent
    number of leading spaces (for recursive calls)

    The Book Class

    Book() (class) [#]

    Contents of a "workbook".

    WARNING: You don't call this class yourself. You use the Book object that was returned when you called xlrd.open_workbook("myfile.xls").

    biff_version [#]

    Version of BIFF (Binary Interchange File Format) used to create the file. Latest is 8.0 (represented here as 80), introduced with Excel 97. Earliest supported by this module: 2.0 (represented as 20).

    codepage [#]

    An integer denoting the character set used for strings in this file. For BIFF 8 and later, this will be 1200, meaning Unicode; more precisely, UTF_16_LE. For earlier versions, this is used to derive the appropriate Python encoding to be used to convert to Unicode. Examples: 1252 -> 'cp1252', 10000 -> 'mac_roman'

    colour_map [#]

    This provides definitions for colour indexes. Please refer to the above section "The Palette; Colour Indexes" for an explanation of how colours are represented in Excel.
    Colour indexes into the palette map into (red, green, blue) tuples. "Magic" indexes e.g. 0x7FFF map to None. colour_map is what you need if you want to render cells on screen or in a PDF file. If you are writing an output XLS file, use palette_record.
    -- New in version 0.6.1. Extracted only if open_workbook(..., formatting_info=True)

    countries [#]

    A tuple containing the (telephone system) country code for:
    [0]: the user-interface setting when the file was created.
    [1]: the regional settings.
    Example: (1, 61) meaning (USA, Australia). This information may give a clue to the correct encoding for an unknown codepage. For a long list of observed values, refer to the OpenOffice.org documentation for the COUNTRY record.

    datemode [#]

    Which date system was in force when this file was last saved.
    0 => 1900 system (the Excel for Windows default).
    1 => 1904 system (the Excel for Macintosh default).

    encoding [#]

    The encoding that was derived from the codepage.

    font_list [#]

    A list of Font class instances, each corresponding to a FONT record.
    -- New in version 0.6.1

    format_list [#]

    A list of Format objects, each corresponding to a FORMAT record, in the order that they appear in the input file. It does not contain builtin formats. If you are creating an output file using (for example) pyExcelerator, use this list. The collection to be used for all visual rendering purposes is format_map.
    -- New in version 0.6.1

    format_map [#]

    The mapping from XF.format_key to Format object.
    -- New in version 0.6.1

    load_time_stage_1 [#]

    Time in seconds to extract the XLS image as a contiguous string (or mmap equivalent).

    load_time_stage_2 [#]

    Time in seconds to parse the data from the contiguous string (or mmap equivalent).

    name_and_scope_map [#]

    A mapping from (lower_case_name, scope) to a single Name object.
    -- New in version 0.6.0

    name_map [#]

    A mapping from lower_case_name to a list of Name objects. The list is sorted in scope order. Typically there will be one item (of global scope) in the list.
    -- New in version 0.6.0

    name_obj_list [#]

    List containing a Name object for each NAME record in the workbook.
    -- New in version 0.6.0

    nsheets [#]

    The number of worksheets present in the workbook file. This information is available even when no sheets have yet been loaded.

    palette_record [#]

    If the user has changed any of the colours in the standard palette, the XLS file will contain a PALETTE record with 56 (16 for Excel 4.0 and earlier) RGB values in it, and this list will be e.g. [(r0, b0, g0), ..., (r55, b55, g55)]. Otherwise this list will be empty. This is what you need if you are writing an output XLS file. If you want to render cells on screen or in a PDF file, use colour_map.
    -- New in version 0.6.1. Extracted only if open_workbook(..., formatting_info=True)

    release_resources() [#]

    This method has a dual purpose. You can call it to release memory-consuming objects and (possibly) a memory-mapped file (mmap.mmap object) when you have finished loading sheets in on_demand mode, but still require the Book object to examine the loaded sheets. It is also called automatically (a) when open_workbook raises an exception and (b) if you are using a "with" statement, when the "with" block is exited. Calling this method multiple times on the same object has no ill effect.

    sheet_by_index(sheetx) [#]
    sheetx
    Sheet index in range(nsheets)
    Returns:
    An object of the Sheet class

    sheet_by_name(sheet_name) [#]
    sheet_name
    Name of sheet required
    Returns:
    An object of the Sheet class

    sheet_loaded(sheet_name_or_index) [#]
    sheet_name_or_index
    Name or index of sheet enquired upon
    Returns:
    true if sheet is loaded, false otherwise
    -- New in version 0.7.1

    sheet_names() [#]
    Returns:
    A list of the names of all the worksheets in the workbook file. This information is available even when no sheets have yet been loaded.

    sheets() [#]
    Returns:
    A list of all sheets in the book. All sheets not already loaded will be loaded.

    style_name_map [#]

    This provides access via name to the extended format information for both built-in styles and user-defined styles.
    It maps name to (built_in, xf_index), where:
    name is either the name of a user-defined style, or the name of one of the built-in styles. Known built-in names are Normal, RowLevel_1 to RowLevel_7, ColLevel_1 to ColLevel_7, Comma, Currency, Percent, "Comma [0]", "Currency [0]", Hyperlink, and "Followed Hyperlink".
    built_in 1 = built-in style, 0 = user-defined
    xf_index is an index into Book.xf_list.
    References: OOo docs s6.99 (STYLE record); Excel UI Format/Style
    -- New in version 0.6.1; since 0.7.4, extracted only if open_workbook(..., formatting_info=True)

    unload_sheet(sheet_name_or_index) [#]
    sheet_name_or_index
    Name or index of sheet to be unloaded.
    -- New in version 0.7.1

    user_name [#]

    What (if anything) is recorded as the name of the last user to save the file.

    xf_list [#]

    A list of XF class instances, each corresponding to an XF record.
    -- New in version 0.6.1

    The Cell Class

    Cell(ctype, value, xf_index=None) (class) [#]

    Contains the data for one cell.

    WARNING: You don't call this class yourself. You access Cell objects via methods of the Sheet object(s) that you found in the Book object that was returned when you called xlrd.open_workbook("myfile.xls").

    Cell objects have three attributes: ctype is an int, value (which depends on ctype) and xf_index. If "formatting_info" is not enabled when the workbook is opened, xf_index will be None. The following table describes the types of cells and how their values are represented in Python.

    Type symbol Type number Python value
    XL_CELL_EMPTY 0 empty string u''
    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 u''. Note: this type will appear only when open_workbook(..., formatting_info=True) is used.

    The Colinfo Class

    Colinfo (class) [#]

    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 [see footnote!]).
    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."""
    Footnote: 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.
    """
    At the moment (2007-01-17) xlrd is going with the GCW version of the story. Reference to the source may be useful: see the computed_column_width(colx) method of the Sheet class.
    -- New in version 0.6.1

    bit1_flag [#]

    Value of a 1-bit flag whose purpose is unknown but is often seen set to 1

    collapsed [#]

    1 = column is collapsed

    hidden [#]

    1 = column is hidden

    outline_level [#]

    Outline level of the column, in range(7). (0 = no outline)

    width [#]

    Width of the column in 1/256 of the width of the zero character, using default font (first FONT record in the file).

    xf_index [#]

    XF index to be used for formatting empty cells.

    The EqNeAttrs Class

    EqNeAttrs (class) [#]

    This mixin class exists solely so that Format, Font, and XF.... objects can be compared by value of their attributes.

    The Font Class

    Font (class) [#]

    An Excel "font" contains the details of not only what is normally considered a font, but also several other display attributes. Items correspond to those in the Excel UI's Format/Cells/Font tab.
    -- New in version 0.6.1

    bold [#]

    1 = Characters are bold. Redundant; see "weight" attribute.

    character_set [#]

    Values: 0 = ANSI Latin, 1 = System default, 2 = Symbol, 77 = Apple Roman, 128 = ANSI Japanese Shift-JIS, 129 = ANSI Korean (Hangul), 130 = ANSI Korean (Johab), 134 = ANSI Chinese Simplified GBK, 136 = ANSI Chinese Traditional BIG5, 161 = ANSI Greek, 162 = ANSI Turkish, 163 = ANSI Vietnamese, 177 = ANSI Hebrew, 178 = ANSI Arabic, 186 = ANSI Baltic, 204 = ANSI Cyrillic, 222 = ANSI Thai, 238 = ANSI Latin II (Central European), 255 = OEM Latin I

    colour_index [#]

    An explanation of "colour index" is given in the Formatting section at the start of this document.

    escapement [#]

    1 = Superscript, 2 = Subscript.

    family [#]

    0 = None (unknown or don't care)
    1 = Roman (variable width, serifed)
    2 = Swiss (variable width, sans-serifed)
    3 = Modern (fixed width, serifed or sans-serifed)
    4 = Script (cursive)
    5 = Decorative (specialised, for example Old English, Fraktur)

    font_index [#]

    The 0-based index used to refer to this Font() instance. Note that index 4 is never used; xlrd supplies a dummy place-holder.

    height [#]

    Height of the font (in twips). A twip = 1/20 of a point.

    italic [#]

    1 = Characters are italic.

    name [#]

    The name of the font. Example: u"Arial"

    outline [#]

    1 = Font is outline style (Macintosh only)

    shadow [#]

    1 = Font is shadow style (Macintosh only)

    struck_out [#]

    1 = Characters are struck out.

    underline_type [#]

    0 = None
    1 = Single; 0x21 (33) = Single accounting
    2 = Double; 0x22 (34) = Double accounting

    underlined [#]

    1 = Characters are underlined. Redundant; see "underline_type" attribute.

    weight [#]

    Font weight (100-1000). Standard values are 400 for normal text and 700 for bold text.

    The Format Class

    Format(format_key, ty, format_str) (class) [#]

    "Number format" information from a FORMAT record.
    -- New in version 0.6.1

    format_key [#]

    The key into Book.format_map

    format_str [#]

    The format string

    type [#]

    A classification that has been inferred from the format string. Currently, this is used only to distinguish between numbers and dates.
    Values:
    FUN = 0 # unknown
    FDT = 1 # date
    FNU = 2 # number
    FGE = 3 # general
    FTX = 4 # text

    The Hyperlink Class

    Hyperlink (class) [#]

    Contains the attributes of a hyperlink. Hyperlink objects are accessible through Sheet.hyperlink_list and Sheet.hyperlink_map.
    -- New in version 0.7.2

    desc [#]

    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.

    fcolx [#]

    Index of first column

    frowx [#]

    Index of first row

    lcolx [#]

    Index of last column

    lrowx [#]

    Index of last row

    quicktip [#]

    The text of the "quick tip" displayed when the cursor hovers over the hyperlink.

    target [#]

    Target frame. Unicode string. Note: I have not seen a case of this. It seems impossible to create one in the Excel UI.

    textmark [#]

    "Textmark": the piece after the "#" in "http://docs.python.org/library#struct_module", or the Sheet1!A1:Z99 part when type is "workbook".

    type [#]

    Type of hyperlink. Unicode string, one of 'url', 'unc', 'local file', 'workbook', 'unknown'

    url_or_path [#]

    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 bytes (3.x) or str (2.x) string, with unknown encoding.

    The Name Class

    Name (class) [#]

    Information relating to a named reference, formula, macro, etc.
    -- New in version 0.6.0
    -- Name information is not extracted from files older than Excel 5.0 (Book.biff_version < 50)

    area2d(clipped=True) [#]

    This is a convenience method for the use case where the name refers to one rectangular area in one worksheet.

    clipped
    If true (the default), the returned rectangle is clipped to fit in (0, sheet.nrows, 0, sheet.ncols) -- it is guaranteed that 0 <= rowxlo <= rowxhi <= sheet.nrows and that the number of usable rows in the area (which may be zero) is rowxhi - rowxlo; likewise for columns.
    Returns:
    a tuple (sheet_object, rowxlo, rowxhi, colxlo, colxhi).
    Raises XLRDError:
    The name is not a constant absolute reference to a single area in a single sheet.

    binary [#]

    0 = Formula definition; 1 = Binary data
    No examples have been sighted.

    builtin [#]

    0 = User-defined name; 1 = Built-in name (common examples: Print_Area, Print_Titles; see OOo docs for full list)

    cell() [#]

    This is a convenience method for the frequent use case where the name refers to a single cell.

    Returns:
    An instance of the Cell class.
    Raises XLRDError:
    The name is not a constant absolute reference to a single cell.

    complex [#]

    0 = Simple formula; 1 = Complex formula (array formula or user defined)
    No examples have been sighted.

    func [#]

    0 = Command macro; 1 = Function macro. Relevant only if macro == 1

    funcgroup [#]

    Function group. Relevant only if macro == 1; see OOo docs for values.

    hidden [#]

    0 = Visible; 1 = Hidden

    macro [#]

    0 = Standard name; 1 = Macro name

    name [#]

    A Unicode string. If builtin, decoded as per OOo docs.

    name_index [#]

    The index of this object in book.name_obj_list

    raw_formula [#]

    An 8-bit string.

    result [#]

    The result of evaluating the formula, if any. If no formula, or evaluation of the formula encountered problems, the result is None. Otherwise the result is a single instance of the Operand class.

    scope [#]

    -1: The name is global (visible in all calculation sheets).
    -2: The name belongs to a macro sheet or VBA sheet.
    -3: The name is invalid.
    0 <= scope < book.nsheets: The name is local to the sheet whose index is scope.

    vbasic [#]

    0 = Sheet macro; 1 = VisualBasic macro. Relevant only if macro == 1

    The Note Class

    Note (class) [#]

    Represents a user "comment" or "note". Note objects are accessible through Sheet.cell_note_map.
    -- New in version 0.7.2

    author [#]

    Author of note

    col_hidden [#]

    True if the containing column is hidden

    colx [#]

    Column index

    rich_text_runlist [#]

    List of (offset_in_string, font_index) tuples. Unlike Sheet.rich_text_runlist_map, the first offset should always be 0.

    row_hidden [#]

    True if the containing row is hidden

    rowx [#]

    Row index

    show [#]

    True if note is always shown

    text [#]

    Text of the note

    The Operand Class

    Operand(akind=None, avalue=None, arank=0, atext='?') (class) [#]

    Used in evaluating formulas. The following table describes the kinds and how their values are represented.

    Kind symbol Kind number Value representation
    oBOOL 3 integer: 0 => False; 1 => True
    oERR 4 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

    kind [#]

    oUNK means that the kind of operand is not known unambiguously.

    text [#]

    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.

    value [#]

    None means that the actual value of the operand is a variable (depends on cell data), not a constant.

    The Ref3D Class

    Ref3D(atuple) (class) [#]

    Represents an absolute or relative 3-dimensional reference to a box of one or more cells.
    -- New in version 0.6.0

    The coords attribute is a tuple of the form:
    (shtxlo, shtxhi, rowxlo, rowxhi, colxlo, colxhi)
    where 0 <= thingxlo <= thingx < thingxhi.
    Note that 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: 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 that 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 also that 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 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, ...).

    The Rowinfo Class

    Rowinfo() (class) [#]

    Height and default formatting information that applies to a row in a sheet. Derived from ROW records.
    -- New in version 0.6.1

    height: Height of the row, in twips. One twip == 1/20 of a point.

    has_default_height: 0 = Row has custom height; 1 = Row has default height.

    outline_level: Outline level of the row (0 to 7)

    outline_group_starts_ends: 1 = Outline group starts or ends here (depending on where the outline buttons are located, see WSBOOL record [TODO ??]), and is collapsed

    hidden: 1 = Row is hidden (manually, or by a filter or outline group)

    height_mismatch: 1 = Row height and default font height do not match

    has_default_xf_index: 1 = the xf_index attribute is usable; 0 = ignore it

    xf_index: Index to default XF record for empty cells in this row. Don't use this if has_default_xf_index == 0.

    additional_space_above: 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.

    additional_space_below: 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.

    The Sheet Class

    Sheet(book, position, name, number) (class) [#]

    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 Cell class.

    WARNING: You don't call this class yourself. You access Sheet objects via the Book object that was returned when you called xlrd.open_workbook("myfile.xls").

    book [#]

    A reference to the Book object to which this sheet belongs. Example usage: some_sheet.book.datemode

    cell(rowx, colx) [#]

    Cell object in the given row and column.

    cell_note_map [#]

    A sparse mapping from (rowx, colx) to a Note object. Cells not containing a note ("comment") are not mapped.
    -- New in version 0.7.2

    cell_type(rowx, colx) [#]

    Type of the cell in the given row and column. Refer to the documentation of the Cell class.

    cell_value(rowx, colx) [#]

    Value of the cell in the given row and column.

    cell_xf_index(rowx, colx) [#]

    XF index of the cell in the given row and column. This is an index into Book.xf_list.
    -- New in version 0.6.1

    col(colx) [#]

    Returns a sequence of the Cell objects in the given column.

    col_label_ranges [#]

    List of address ranges of cells containing column labels. These are set up in Excel by Insert > Name > Labels > Columns.
    -- New in version 0.6.0
    How to deconstruct the list:

    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_slice(colx, start_rowx=0, end_rowx=None) [#]

    Returns a slice of the Cell objects in the given column.

    col_types(colx, start_rowx=0, end_rowx=None) [#]

    Returns a slice of the types of the cells in the given column.

    col_values(colx, start_rowx=0, end_rowx=None) [#]

    Returns a slice of the values of the cells in the given column.

    colinfo_map [#]

    The map from a column index to a Colinfo object. Often there is an entry in COLINFO records for all column indexes in range(257). Note that xlrd ignores the entry for the non-existent 257th column. On the other hand, there may be no entry for unused columns.
    -- New in version 0.6.1. Populated only if open_workbook(formatting_info=True).

    computed_column_width(colx) [#]

    Determine column display width.
    -- New in version 0.6.1

    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).
    Returns:
    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).

    default_additional_space_above [#]

    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 [#]

    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 [#]

    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 [#]

    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 [#]

    Default value to be used for a row if there is no ROW record for that row. From the optional DEFAULTROWHEIGHT record.

    defcolwidth [#]

    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 Colinfo class.
    -- New in version 0.6.1

    gcw [#]

    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 Colinfo class for discussion.

    has_pane_record [#]

    Boolean specifying if a PANE record was present, ignore unless you're xlutils.copy

    horizontal_page_breaks [#]

    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).
    -- New in version 0.7.2

    horz_split_first_visible [#]

    Index of first visible row in bottom frozen/split pane

    horz_split_pos [#]

    Number of rows in top pane (frozen panes; for split panes, see comments below in code)

    hyperlink_list [#]

    A list of Hyperlink objects corresponding to HLINK records found in the worksheet.
    -- New in version 0.7.2

    hyperlink_map [#]

    A sparse mapping from (rowx, colx) to an item in 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 hyperlink_list.
    -- New in version 0.7.2

    merged_cells [#]

    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.
    -- New in version 0.6.1. Extracted only if open_workbook(formatting_info=True).
    How to deconstruct the list:

    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.
    

    name [#]

    Name of sheet.

    ncols [#]

    Nominal number of columns in sheet. It is 1 + the maximum column index found, ignoring trailing empty cells. See also open_workbook(ragged_rows=?) and Sheet.row_len(row_index).

    nrows [#]

    Number of rows in sheet. A row index is in range(thesheet.nrows).

    rich_text_runlist_map [#]

    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:

    runlist = thesheet.rich_text_runlist_map.get((rowx, colx))
    if runlist:
        for offset, font_index in runlist:
            # do work here.
            pass
    
    Populated only if open_workbook(formatting_info=True).
    -- New in version 0.7.2.
     

    row(rowx) [#]

    Returns a sequence of the Cell objects in the given row.

    row_label_ranges [#]

    List of address ranges of cells containing row labels. For more details, see col_label_ranges above.
    -- New in version 0.6.0

    row_len(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 ncols cells.
    -- New in version 0.7.2

    row_slice(rowx, start_colx=0, end_colx=None) [#]

    Returns a slice of the Cell objects in the given row.

    row_types(rowx, start_colx=0, end_colx=None) [#]

    Returns a slice of the types of the cells in the given row.

    row_values(rowx, start_colx=0, end_colx=None) [#]

    Returns a slice of the values of the cells in the given row.

    rowinfo_map [#]

    The map from a row index to a Rowinfo object. Note that it is possible to have missing entries -- at least one source of XLS files doesn't bother writing ROW records.
    -- New in version 0.6.1. Populated only if open_workbook(formatting_info=True).

    split_active_pane [#]

    Frozen panes: ignore it. Split panes: explanation and diagrams in OOo docs.

    standardwidth [#]

    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 Colinfo class.
    -- New in version 0.6.1

    vert_split_first_visible [#]

    Index of first visible column in right frozen/split pane

    vert_split_pos [#]

    Number of columns in left pane (frozen panes; for split panes, see comments below in code)

    vertical_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).
    -- New in version 0.7.2

    visibility [#]

    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).

    The XF Class

    XF (class) [#]

    eXtended Formatting information for cells, rows, columns and styles.
    -- New in version 0.6.1

    Each of the 6 flags below describes the validity of a specific group of attributes.
    In cell XFs, flag==0 means the attributes of the parent style XF are used, (but only if the attributes are valid there); flag==1 means the attributes of this XF are used.
    In style XFs, flag==0 means the attribute setting is valid; flag==1 means the attribute should be ignored.
    Note that the API provides both "raw" XFs and "computed" XFs -- in the latter case, cell XFs have had the above inheritance mechanism applied.

    _alignment_flag [#]
    _background_flag [#]
    _border_flag [#]
    _font_flag [#]
    _format_flag [#]
    _protection_flag [#]

     

    alignment [#]

    An instance of an XFAlignment object.

    background [#]

    An instance of an XFBackground object.

    border [#]

    An instance of an XFBorder object.

    font_index [#]

    Index into Book.font_list

    format_key [#]

    Key into Book.format_map

    Warning: OOo docs on the XF record call this "Index to FORMAT record". It is not an index in the Python sense. It is a key to a map. It is true only for Excel 4.0 and earlier files that the key into format_map from an XF instance is the same as the index into format_list, and only if the index is less than 164.

    is_style [#]

    0 = cell XF, 1 = style XF

    parent_style_index [#]

    cell XF: Index into Book.xf_list of this XF's style XF
    style XF: 0xFFF

    protection [#]

    An instance of an XFProtection object.

    xf_index [#]

    Index into Book.xf_list

    The XFAlignment Class

    XFAlignment (class) [#]

    A collection of the alignment and similar attributes of an XF record. Items correspond to those in the Excel UI's Format/Cells/Alignment tab.
    -- New in version 0.6.1

    hor_align [#]

    Values: section 6.115 (p 214) of OOo docs

    indent_level [#]

    A number in range(15).

    rotation [#]

    Values: section 6.115 (p 215) of OOo docs.
    Note: file versions BIFF7 and earlier use the documented "orientation" attribute; this will be mapped (without loss) into "rotation".

    shrink_to_fit [#]

    1 = shrink font size to fit text into cell.

    text_direction [#]

    0 = according to context; 1 = left-to-right; 2 = right-to-left

    text_wrapped [#]

    1 = text is wrapped at right margin

    vert_align [#]

    Values: section 6.115 (p 215) of OOo docs

    The XFBackground Class

    XFBackground (class) [#]

    A collection of the background-related attributes of an XF record. Items correspond to those in the Excel UI's Format/Cells/Patterns tab. An explanation of "colour index" is given in the Formatting section at the start of this document.
    -- New in version 0.6.1

    background_colour_index [#]

    See section 3.11 of the OOo docs.

    fill_pattern [#]

    See section 3.11 of the OOo docs.

    pattern_colour_index [#]

    See section 3.11 of the OOo docs.

    The XFBorder Class

    XFBorder (class) [#]

    A collection of the border-related attributes of an XF record. Items correspond to those in the Excel UI's Format/Cells/Border tab.

    An explanations of "colour index" is given in the Formatting section at the start of this document. There are five line style attributes; possible values and the associated meanings are: 0 = No line, 1 = Thin, 2 = Medium, 3 = Dashed, 4 = Dotted, 5 = Thick, 6 = Double, 7 = Hair, 8 = Medium dashed, 9 = Thin dash-dotted, 10 = Medium dash-dotted, 11 = Thin dash-dot-dotted, 12 = Medium dash-dot-dotted, 13 = Slanted medium dash-dotted. The line styles 8 to 13 appear in BIFF8 files (Excel 97 and later) only. For pictures of the line styles, refer to OOo docs s3.10 (p22) "Line Styles for Cell Borders (BIFF3-BIFF8)".


    -- New in version 0.6.1
    bottom_colour_index [#]

    The colour index for the cell's bottom line

    bottom_line_style [#]

    The line style for the cell's bottom line

    diag_colour_index [#]

    The colour index for the cell's diagonal lines, if any

    diag_down [#]

    1 = draw a diagonal from top left to bottom right

    diag_line_style [#]

    The line style for the cell's diagonal lines, if any

    diag_up [#]

    1 = draw a diagonal from bottom left to top right

    left_colour_index [#]

    The colour index for the cell's left line

    left_line_style [#]

    The line style for the cell's left line

    right_colour_index [#]

    The colour index for the cell's right line

    right_line_style [#]

    The line style for the cell's right line

    top_colour_index [#]

    The colour index for the cell's top line

    top_line_style [#]

    The line style for the cell's top line

    The XFProtection Class

    XFProtection (class) [#]

    A collection of the protection-related attributes of an XF record. Items correspond to those in the Excel UI's Format/Cells/Protection tab. Note the OOo docs include the "cell or style" bit in this bundle of attributes. This is incorrect; the bit is used in determining which bundles to use.
    -- New in version 0.6.1

    cell_locked [#]

    1 = Cell is prevented from being changed, moved, resized, or deleted (only if the sheet is protected).

    formula_hidden [#]

    1 = Hide formula so that it doesn't appear in the formula bar when the cell is selected (only if the sheet is protected).

    xlrd-0.9.4/xlrd/examples/0000755000076500000240000000000012551375765015537 5ustar chrisstaff00000000000000xlrd-0.9.4/xlrd/examples/namesdemo.xls0000644000076500000240000005400011650243325020217 0ustar chrisstaff00000000000000ࡱ> *) \pStephen John Machin Ba==8X@"1}Arial1}Arial1}Arial1}Arial1}Arial"$"#,##0;\-"$"#,##0"$"#,##0;[Red]\-"$"#,##0"$"#,##0.00;\-"$"#,##0.00#"$"#,##0.00;[Red]\-"$"#,##0.005*0_-"$"* #,##0_-;\-"$"* #,##0_-;_-"$"* "-"_-;_-@_-,)'_-* #,##0_-;\-* #,##0_-;_-* "-"_-;_-@_-=,8_-"$"* #,##0.00_-;\-"$"* #,##0.00_-;_-"$"* "-"??_-;_-@_-4+/_-* #,##0.00_-;\-* #,##0.00_-;_-* "-"??_-;_-@_-                + ) , *    ` Sheet1 Sheet2Sheet3/Seamus O'Reilly=& A1Z10;" addnumstr{456A"all_local_ranges)9 @9 @9$ Apostrophe; # ASCII_Stringascii) BottomLine) 9#! EmptyString" Expenses; Faux) Intersection) ##%List)#@# LocalRange: localRange: Localrange:  Moscow;  NegInt numCatNum{, numcatnum2Gz(@EdL@  PosFloat@PosInt  ; * );;  Profit; $ rectangle1; $ rectangle2; % RelativeNeg; % RelativePos;  Sales;  twofivesix) UnicodeString"Union) ##vrai" Year_Tot; & >A:20; "wJanFebMarAprMayJunJulAugSepOctNovDecYear TotSalesExpensesProfit D <cc   r  dMbP?_*+%"??U   E@ N # 7   >@  7    dMbP?_*+%"??U>@7  '/  dMbP?_*+%MHP Mobile Printing PSS odXXLetterPRIV0''''$\KhC#[$IUPH dLetter [none] [none]Arial4Pd?SJM_2<Automatic>dEXCEL.EXE"dXX??U} $                      @J@ .@4NI % #J@C?@@ @* 0@@@P@`@p@@ @@@@( @) ## @ #ً( h@ ) ## h@  # (/@ #@#B/@  # ًC C @C@CC@C&2@@  LCascii,L{ 123456&2@@ <LC@LC 1234562 PLC )\LGz(@EdL@ 12.3456.789&1@ @ @@AtLC 12.3456.789p@C2P$2"."OOOImR  >@_ 7  h00  dMbP?_*+%"??U~ E@">@7 Oh+'0@H\p  John Machin John MachinMicrosoft Excel@a@==՜.+,0  PXp x Lingfo Pty Ltd Sheet1Sheet2Sheet3Seamus O'ReillyA1Z10 Apostrophe ExpensesSheet1!LocalRangeSheet2!localRangeSheet3!LocalrangeSheet3!Print_AreaSheet3!Print_TitlesProfit rectangle1 rectangle2 RelativeNeg RelativePosSales Year_Tot  Worksheets Named Ranges  "#$%&'(Root Entry F[Workbook0SummaryInformation(DocumentSummaryInformation8!xlrd-0.9.4/xlrd/examples/xlrdnameAPIdemo.py0000644000076500000240000001574212270704230021110 0ustar chrisstaff00000000000000# -*- coding: cp1252 -*- ## # Module/script example of the xlrd API for extracting information # about named references, named constants, etc. # #

    Copyright 2006 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 import xlrd from xlrd.timemachine import REPR import sys import glob def scope_as_string(book, scope): if 0 <= scope < book.nsheets: return "sheet #%d (%r)" % (scope, REPR(book.sheet_names()[scope])) if scope == -1: return "Global" if scope == -2: return "Macro/VBA" return "Unknown scope value (%r)" % REPR(scope) def do_scope_query(book, scope_strg, show_contents=0, f=sys.stdout): try: qscope = int(scope_strg) except ValueError: if scope_strg == "*": qscope = None # means "all' else: # so assume it's a sheet name ... qscope = book.sheet_names().index(scope_strg) print("%r => %d" % (scope_strg, qscope), file=f) for nobj in book.name_obj_list: if qscope is None or nobj.scope == qscope: show_name_object(book, nobj, show_contents, f) def show_name_details(book, name, show_contents=0, f=sys.stdout): """ book -- Book object obtained from xlrd.open_workbook(). name -- The name that's being investigated. show_contents -- 0: Don't; 1: Non-empty cells only; 2: All cells f -- Open output file handle. """ name_lcase = name.lower() # Excel names are case-insensitive. nobj_list = book.name_map.get(name_lcase) if not nobj_list: print("%r: unknown name" % name, file=f) return for nobj in nobj_list: show_name_object(book, nobj, show_contents, f) def show_name_details_in_scope( book, name, scope_strg, show_contents=0, f=sys.stdout, ): try: scope = int(scope_strg) except ValueError: # so assume it's a sheet name ... scope = book.sheet_names().index(scope_strg) print("%r => %d" % (scope_strg, scope), file=f) name_lcase = name.lower() # Excel names are case-insensitive. while 1: nobj = book.name_and_scope_map.get((name_lcase, scope)) if nobj: break print("Name %s not found in scope %d" % (REPR(name), scope), file=f) if scope == -1: return scope = -1 # Try again with global scope print("Name %s found in scope %d" % (REPR(name), scope), file=f) show_name_object(book, nobj, show_contents, f) def showable_cell_value(celltype, cellvalue, datemode): if celltype == xlrd.XL_CELL_DATE: try: showval = xlrd.xldate_as_tuple(cellvalue, datemode) except xlrd.XLDateError as e: showval = "%s:%s" % (type(e).__name__, e) elif celltype == xlrd.XL_CELL_ERROR: showval = xlrd.error_text_from_code.get( cellvalue, '' % cellvalue) else: showval = cellvalue return showval def show_name_object(book, nobj, show_contents=0, f=sys.stdout): print("\nName: %s, scope: %s (%s)" \ % (REPR(nobj.name), REPR(nobj.scope), scope_as_string(book, nobj.scope)), file=f) res = nobj.result print("Formula eval result: %s" % REPR(res), file=f) if res is None: return # result should be an instance of the Operand class kind = res.kind value = res.value if kind >= 0: # A scalar, or unknown ... you've seen all there is to see. pass elif kind == xlrd.oREL: # A list of Ref3D objects representing *relative* ranges for i in range(len(value)): ref3d = value[i] print("Range %d: %s ==> %s"% (i, REPR(ref3d.coords), REPR(xlrd.rangename3drel(book, ref3d))), file=f) elif kind == xlrd.oREF: # A list of Ref3D objects for i in range(len(value)): ref3d = value[i] print("Range %d: %s ==> %s"% (i, REPR(ref3d.coords), REPR(xlrd.rangename3d(book, ref3d))), file=f) if not show_contents: continue datemode = book.datemode for shx in range(ref3d.shtxlo, ref3d.shtxhi): sh = book.sheet_by_index(shx) print(" Sheet #%d (%s)" % (shx, sh.name), file=f) rowlim = min(ref3d.rowxhi, sh.nrows) collim = min(ref3d.colxhi, sh.ncols) for rowx in range(ref3d.rowxlo, rowlim): for colx in range(ref3d.colxlo, collim): cty = sh.cell_type(rowx, colx) if cty == xlrd.XL_CELL_EMPTY and show_contents == 1: continue cval = sh.cell_value(rowx, colx) sval = showable_cell_value(cty, cval, datemode) print(" (%3d,%3d) %-5s: %s" % (rowx, colx, xlrd.cellname(rowx, colx), REPR(sval)), file=f) if __name__ == "__main__": def usage(): text = """ usage: xlrdnameAIPdemo.py glob_pattern name scope show_contents where: "glob_pattern" designates a set of files "name" is a name or '*' (all names) "scope" is -1 (global) or a sheet number or a sheet name or * (all scopes) "show_contents" is one of 0 (no show), 1 (only non-empty cells), or 2 (all cells) Examples (script name and glob_pattern arg omitted for brevity) [Searching through book.name_obj_list] * * 0 lists all names * * 1 lists all names, showing referenced non-empty cells * 1 0 lists all names local to the 2nd sheet * Northern 0 lists all names local to the 'Northern' sheet * -1 0 lists all names with global scope [Initial direct access through book.name_map] Sales * 0 lists all occurrences of "Sales" in any scope [Direct access through book.name_and_scope_map] Revenue -1 0 checks if "Revenue" exists in global scope """ sys.stdout.write(text) if len(sys.argv) != 5: usage() sys.exit(0) arg_pattern = sys.argv[1] # glob pattern e.g. "foo*.xls" arg_name = sys.argv[2] # see below arg_scope = sys.argv[3] # see below arg_show_contents = int(sys.argv[4]) # 0: no show, 1: only non-empty cells, # 2: all cells for fname in glob.glob(arg_pattern): book = xlrd.open_workbook(fname) if arg_name == "*": # Examine book.name_obj_list to find all names # in a given scope ("*" => all scopes) do_scope_query(book, arg_scope, arg_show_contents) elif arg_scope == "*": # Using book.name_map to find all usage of a name. show_name_details(book, arg_name, arg_show_contents) else: # Using book.name_and_scope_map to find which if any instances # of a name are visible in the given scope, which can be supplied # as -1 (global) or a sheet number or a sheet name. show_name_details_in_scope(book, arg_name, arg_scope, arg_show_contents) xlrd-0.9.4/xlrd/formatting.py0000644000076500000240000013042712320534311016426 0ustar chrisstaff00000000000000# -*- coding: cp1252 -*- ## # Module for formatting information. # #

    Copyright 2005-2012 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. from __future__ import print_function DEBUG = 0 import re from struct import unpack from .timemachine import * from .biffh import BaseObject, unpack_unicode, unpack_string, \ upkbits, upkbitsL, fprintf, \ FUN, FDT, FNU, FGE, FTX, XL_CELL_NUMBER, XL_CELL_DATE, \ XL_FORMAT, XL_FORMAT2, \ XLRDError _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. } excel_default_palette_b5 = ( ( 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), (227, 227, 227), ( 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), ) excel_default_palette_b2 = excel_default_palette_b5[:16] # Following table borrowed from Gnumeric 1.4 source. # Checked against OOo docs and MS docs. excel_default_palette_b8 = ( # (red, green, blue) ( 0, 0, 0), (255,255,255), (255, 0, 0), ( 0,255, 0), # 0 ( 0, 0,255), (255,255, 0), (255, 0,255), ( 0,255,255), # 4 (128, 0, 0), ( 0,128, 0), ( 0, 0,128), (128,128, 0), # 8 (128, 0,128), ( 0,128,128), (192,192,192), (128,128,128), # 12 (153,153,255), (153, 51,102), (255,255,204), (204,255,255), # 16 (102, 0,102), (255,128,128), ( 0,102,204), (204,204,255), # 20 ( 0, 0,128), (255, 0,255), (255,255, 0), ( 0,255,255), # 24 (128, 0,128), (128, 0, 0), ( 0,128,128), ( 0, 0,255), # 28 ( 0,204,255), (204,255,255), (204,255,204), (255,255,153), # 32 (153,204,255), (255,153,204), (204,153,255), (255,204,153), # 36 ( 51,102,255), ( 51,204,204), (153,204, 0), (255,204, 0), # 40 (255,153, 0), (255,102, 0), (102,102,153), (150,150,150), # 44 ( 0, 51,102), ( 51,153,102), ( 0, 51, 0), ( 51, 51, 0), # 48 (153, 51, 0), (153, 51,102), ( 51, 51,153), ( 51, 51, 51), # 52 ) default_palette = { 80: excel_default_palette_b8, 70: excel_default_palette_b5, 50: excel_default_palette_b5, 45: excel_default_palette_b2, 40: excel_default_palette_b2, 30: excel_default_palette_b2, 21: excel_default_palette_b2, 20: excel_default_palette_b2, } """ 00H = Normal 01H = RowLevel_lv (see next field) 02H = ColLevel_lv (see next field) 03H = Comma 04H = Currency 05H = Percent 06H = Comma [0] (BIFF4-BIFF8) 07H = Currency [0] (BIFF4-BIFF8) 08H = Hyperlink (BIFF8) 09H = Followed Hyperlink (BIFF8) """ built_in_style_names = [ "Normal", "RowLevel_", "ColLevel_", "Comma", "Currency", "Percent", "Comma [0]", "Currency [0]", "Hyperlink", "Followed Hyperlink", ] def initialise_colour_map(book): book.colour_map = {} book.colour_indexes_used = {} if not book.formatting_info: return # Add the 8 invariant colours for i in xrange(8): book.colour_map[i] = excel_default_palette_b8[i] # Add the default palette depending on the version dpal = default_palette[book.biff_version] ndpal = len(dpal) for i in xrange(ndpal): book.colour_map[i+8] = dpal[i] # Add the specials -- None means the RGB value is not known # System window text colour for border lines book.colour_map[ndpal+8] = None # System window background colour for pattern background book.colour_map[ndpal+8+1] = None # for ci in ( 0x51, # System ToolTip text colour (used in note objects) 0x7FFF, # 32767, system window text colour for fonts ): book.colour_map[ci] = None def nearest_colour_index(colour_map, rgb, debug=0): # General purpose function. Uses Euclidean distance. # So far used only for pre-BIFF8 WINDOW2 record. # Doesn't have to be fast. # Doesn't have to be fancy. best_metric = 3 * 256 * 256 best_colourx = 0 for colourx, cand_rgb in colour_map.items(): if cand_rgb is None: continue metric = 0 for v1, v2 in zip(rgb, cand_rgb): metric += (v1 - v2) * (v1 - v2) if metric < best_metric: best_metric = metric best_colourx = colourx if metric == 0: break if 0 and debug: print("nearest_colour_index for %r is %r -> %r; best_metric is %d" \ % (rgb, best_colourx, colour_map[best_colourx], best_metric)) return best_colourx ## # This mixin class exists solely so that Format, Font, and XF.... objects # can be compared by value of their attributes. class EqNeAttrs(object): def __eq__(self, other): return self.__dict__ == other.__dict__ def __ne__(self, other): return self.__dict__ != other.__dict__ ## # An Excel "font" contains the details of not only what is normally # considered a font, but also several other display attributes. # Items correspond to those in the Excel UI's Format/Cells/Font tab. #
    -- New in version 0.6.1 class Font(BaseObject, EqNeAttrs): ## # 1 = Characters are bold. Redundant; see "weight" attribute. bold = 0 ## # Values: 0 = ANSI Latin, 1 = System default, 2 = Symbol, # 77 = Apple Roman, # 128 = ANSI Japanese Shift-JIS, # 129 = ANSI Korean (Hangul), # 130 = ANSI Korean (Johab), # 134 = ANSI Chinese Simplified GBK, # 136 = ANSI Chinese Traditional BIG5, # 161 = ANSI Greek, # 162 = ANSI Turkish, # 163 = ANSI Vietnamese, # 177 = ANSI Hebrew, # 178 = ANSI Arabic, # 186 = ANSI Baltic, # 204 = ANSI Cyrillic, # 222 = ANSI Thai, # 238 = ANSI Latin II (Central European), # 255 = OEM Latin I character_set = 0 ## # An explanation of "colour index" is given in the Formatting # section at the start of this document. colour_index = 0 ## # 1 = Superscript, 2 = Subscript. escapement = 0 ## # 0 = None (unknown or don't care)
    # 1 = Roman (variable width, serifed)
    # 2 = Swiss (variable width, sans-serifed)
    # 3 = Modern (fixed width, serifed or sans-serifed)
    # 4 = Script (cursive)
    # 5 = Decorative (specialised, for example Old English, Fraktur) family = 0 ## # The 0-based index used to refer to this Font() instance. # Note that index 4 is never used; xlrd supplies a dummy place-holder. font_index = 0 ## # Height of the font (in twips). A twip = 1/20 of a point. height = 0 ## # 1 = Characters are italic. italic = 0 ## # The name of the font. Example: u"Arial" name = UNICODE_LITERAL("") ## # 1 = Characters are struck out. struck_out = 0 ## # 0 = None
    # 1 = Single; 0x21 (33) = Single accounting
    # 2 = Double; 0x22 (34) = Double accounting underline_type = 0 ## # 1 = Characters are underlined. Redundant; see "underline_type" attribute. underlined = 0 ## # Font weight (100-1000). Standard values are 400 for normal text # and 700 for bold text. weight = 400 ## # 1 = Font is outline style (Macintosh only) outline = 0 ## # 1 = Font is shadow style (Macintosh only) shadow = 0 # No methods ... def handle_efont(book, data): # BIFF2 only if not book.formatting_info: return book.font_list[-1].colour_index = unpack('= 2 bv = book.biff_version k = len(book.font_list) if k == 4: f = Font() f.name = UNICODE_LITERAL('Dummy Font') f.font_index = k book.font_list.append(f) k += 1 f = Font() f.font_index = k book.font_list.append(f) if bv >= 50: ( f.height, option_flags, f.colour_index, f.weight, f.escapement, f.underline_type, f.family, f.character_set, ) = unpack('> 1 f.underlined = (option_flags & 4) >> 2 f.struck_out = (option_flags & 8) >> 3 f.outline = (option_flags & 16) >> 4 f.shadow = (option_flags & 32) >> 5 if bv >= 80: f.name = unpack_unicode(data, 14, lenlen=1) else: f.name = unpack_string(data, 14, book.encoding, lenlen=1) elif bv >= 30: f.height, option_flags, f.colour_index = unpack('> 1 f.underlined = (option_flags & 4) >> 2 f.struck_out = (option_flags & 8) >> 3 f.outline = (option_flags & 16) >> 4 f.shadow = (option_flags & 32) >> 5 f.name = unpack_string(data, 6, book.encoding, lenlen=1) # Now cook up the remaining attributes ... f.weight = [400, 700][f.bold] f.escapement = 0 # None f.underline_type = f.underlined # None or Single f.family = 0 # Unknown / don't care f.character_set = 1 # System default (0 means "ANSI Latin") else: # BIFF2 f.height, option_flags = unpack('> 1 f.underlined = (option_flags & 4) >> 2 f.struck_out = (option_flags & 8) >> 3 f.outline = 0 f.shadow = 0 f.name = unpack_string(data, 4, book.encoding, lenlen=1) # Now cook up the remaining attributes ... f.weight = [400, 700][f.bold] f.escapement = 0 # None f.underline_type = f.underlined # None or Single f.family = 0 # Unknown / don't care f.character_set = 1 # System default (0 means "ANSI Latin") if blah: f.dump( book.logfile, header="--- handle_font: font[%d] ---" % f.font_index, footer="-------------------", ) # === "Number formats" === ## # "Number format" information from a FORMAT record. #
    -- New in version 0.6.1 class Format(BaseObject, EqNeAttrs): ## # The key into Book.format_map format_key = 0 ## # A classification that has been inferred from the format string. # Currently, this is used only to distinguish between numbers and dates. #
    Values: #
    FUN = 0 # unknown #
    FDT = 1 # date #
    FNU = 2 # number #
    FGE = 3 # general #
    FTX = 4 # text type = FUN ## # The format string format_str = UNICODE_LITERAL('') def __init__(self, format_key, ty, format_str): self.format_key = format_key self.type = ty self.format_str = format_str std_format_strings = { # "std" == "standard for US English locale" # #### TODO ... a lot of work to tailor these to the user's locale. # See e.g. gnumeric-1.x.y/src/formats.c 0x00: "General", 0x01: "0", 0x02: "0.00", 0x03: "#,##0", 0x04: "#,##0.00", 0x05: "$#,##0_);($#,##0)", 0x06: "$#,##0_);[Red]($#,##0)", 0x07: "$#,##0.00_);($#,##0.00)", 0x08: "$#,##0.00_);[Red]($#,##0.00)", 0x09: "0%", 0x0a: "0.00%", 0x0b: "0.00E+00", 0x0c: "# ?/?", 0x0d: "# ??/??", 0x0e: "m/d/yy", 0x0f: "d-mmm-yy", 0x10: "d-mmm", 0x11: "mmm-yy", 0x12: "h:mm AM/PM", 0x13: "h:mm:ss AM/PM", 0x14: "h:mm", 0x15: "h:mm:ss", 0x16: "m/d/yy h:mm", 0x25: "#,##0_);(#,##0)", 0x26: "#,##0_);[Red](#,##0)", 0x27: "#,##0.00_);(#,##0.00)", 0x28: "#,##0.00_);[Red](#,##0.00)", 0x29: "_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)", 0x2a: "_($* #,##0_);_($* (#,##0);_($* \"-\"_);_(@_)", 0x2b: "_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)", 0x2c: "_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(@_)", 0x2d: "mm:ss", 0x2e: "[h]:mm:ss", 0x2f: "mm:ss.0", 0x30: "##0.0E+0", 0x31: "@", } fmt_code_ranges = [ # both-inclusive ranges of "standard" format codes # Source: the openoffice.org doc't # and the OOXML spec Part 4, section 3.8.30 ( 0, 0, FGE), ( 1, 13, FNU), (14, 22, FDT), (27, 36, FDT), # CJK date formats (37, 44, FNU), (45, 47, FDT), (48, 48, FNU), (49, 49, FTX), # Gnumeric assumes (or assumed) that built-in formats finish at 49, not at 163 (50, 58, FDT), # CJK date formats (59, 62, FNU), # Thai number (currency?) formats (67, 70, FNU), # Thai number (currency?) formats (71, 81, FDT), # Thai date formats ] std_format_code_types = {} for lo, hi, ty in fmt_code_ranges: for x in xrange(lo, hi+1): std_format_code_types[x] = ty del lo, hi, ty, x date_chars = UNICODE_LITERAL('ymdhs') # year, month/minute, day, hour, second date_char_dict = {} for _c in date_chars + date_chars.upper(): date_char_dict[_c] = 5 del _c, date_chars skip_char_dict = {} for _c in UNICODE_LITERAL('$-+/(): '): skip_char_dict[_c] = 1 num_char_dict = { UNICODE_LITERAL('0'): 5, UNICODE_LITERAL('#'): 5, UNICODE_LITERAL('?'): 5, } non_date_formats = { UNICODE_LITERAL('0.00E+00'):1, UNICODE_LITERAL('##0.0E+0'):1, UNICODE_LITERAL('General') :1, UNICODE_LITERAL('GENERAL') :1, # OOo Calc 1.1.4 does this. UNICODE_LITERAL('general') :1, # pyExcelerator 0.6.3 does this. UNICODE_LITERAL('@') :1, } fmt_bracketed_sub = re.compile(r'\[[^]]*\]').sub # Boolean format strings (actual cases) # u'"Yes";"Yes";"No"' # u'"True";"True";"False"' # u'"On";"On";"Off"' def is_date_format_string(book, fmt): # Heuristics: # Ignore "text" and [stuff in square brackets (aarrgghh -- see below)]. # Handle backslashed-escaped chars properly. # E.g. hh\hmm\mss\s should produce a display like 23h59m59s # Date formats have one or more of ymdhs (caseless) in them. # Numeric formats have # and 0. # N.B. u'General"."' hence get rid of "text" first. # TODO: Find where formats are interpreted in Gnumeric # TODO: u'[h]\\ \\h\\o\\u\\r\\s' ([h] means don't care about hours > 23) state = 0 s = '' for c in fmt: if state == 0: if c == UNICODE_LITERAL('"'): state = 1 elif c in UNICODE_LITERAL(r"\_*"): state = 2 elif c in skip_char_dict: pass else: s += c elif state == 1: if c == UNICODE_LITERAL('"'): state = 0 elif state == 2: # Ignore char after backslash, underscore or asterisk state = 0 assert 0 <= state <= 2 if book.verbosity >= 4: print("is_date_format_string: reduced format is %s" % REPR(s), file=book.logfile) s = fmt_bracketed_sub('', s) if s in non_date_formats: return False state = 0 separator = ";" got_sep = 0 date_count = num_count = 0 for c in s: if c in date_char_dict: date_count += date_char_dict[c] elif c in num_char_dict: num_count += num_char_dict[c] elif c == separator: got_sep = 1 # print num_count, date_count, repr(fmt) if date_count and not num_count: return True if num_count and not date_count: return False if date_count: if book.verbosity: fprintf(book.logfile, 'WARNING *** is_date_format: ambiguous d=%d n=%d fmt=%r\n', date_count, num_count, fmt) elif not got_sep: if book.verbosity: fprintf(book.logfile, "WARNING *** format %r produces constant result\n", fmt) return date_count > num_count def handle_format(self, data, rectype=XL_FORMAT): DEBUG = 0 bv = self.biff_version if rectype == XL_FORMAT2: bv = min(bv, 30) if not self.encoding: self.derive_encoding() strpos = 2 if bv >= 50: fmtkey = unpack('= 80: unistrg = unpack_unicode(data, 2) else: unistrg = unpack_string(data, strpos, self.encoding, lenlen=1) blah = DEBUG or self.verbosity >= 3 if blah: fprintf(self.logfile, "FORMAT: count=%d fmtkey=0x%04x (%d) s=%r\n", self.actualfmtcount, fmtkey, fmtkey, unistrg) is_date_s = self.is_date_format_string(unistrg) ty = [FGE, FDT][is_date_s] if not(fmtkey > 163 or bv < 50): # user_defined if fmtkey > 163 # N.B. Gnumeric incorrectly starts these at 50 instead of 164 :-( # if earlier than BIFF 5, standard info is useless std_ty = std_format_code_types.get(fmtkey, FUN) # print "std ty", std_ty is_date_c = std_ty == FDT if self.verbosity and 0 < fmtkey < 50 and (is_date_c ^ is_date_s): DEBUG = 2 fprintf(self.logfile, "WARNING *** Conflict between " "std format key %d and its format string %r\n", fmtkey, unistrg) if DEBUG == 2: fprintf(self.logfile, "ty: %d; is_date_c: %r; is_date_s: %r; fmt_strg: %r", ty, is_date_c, is_date_s, unistrg) fmtobj = Format(fmtkey, ty, unistrg) if blah: fmtobj.dump(self.logfile, header="--- handle_format [%d] ---" % (self.actualfmtcount-1, )) self.format_map[fmtkey] = fmtobj self.format_list.append(fmtobj) # ============================================================================= def handle_palette(book, data): if not book.formatting_info: return blah = DEBUG or book.verbosity >= 2 n_colours, = unpack('= 50] if ((DEBUG or book.verbosity >= 1) and n_colours != expected_n_colours): fprintf(book.logfile, "NOTE *** Expected %d colours in PALETTE record, found %d\n", expected_n_colours, n_colours) elif blah: fprintf(book.logfile, "PALETTE record with %d colours\n", n_colours) fmt = '> 8) & 0xff blue = (c >> 16) & 0xff old_rgb = book.colour_map[8+i] new_rgb = (red, green, blue) book.palette_record.append(new_rgb) book.colour_map[8+i] = new_rgb if blah: if new_rgb != old_rgb: print("%2d: %r -> %r" % (i, old_rgb, new_rgb), file=book.logfile) def palette_epilogue(book): # Check colour indexes in fonts etc. # This must be done here as FONT records # come *before* the PALETTE record :-( for font in book.font_list: if font.font_index == 4: # the missing font record continue cx = font.colour_index if cx == 0x7fff: # system window text colour continue if cx in book.colour_map: book.colour_indexes_used[cx] = 1 elif book.verbosity: print("Size of colour table:", len(book.colour_map), file=book.logfile) fprintf(book.logfile, "*** Font #%d (%r): colour index 0x%04x is unknown\n", font.font_index, font.name, cx) if book.verbosity >= 1: used = sorted(book.colour_indexes_used.keys()) print("\nColour indexes used:\n%r\n" % used, file=book.logfile) def handle_style(book, data): if not book.formatting_info: return blah = DEBUG or book.verbosity >= 2 bv = book.biff_version flag_and_xfx, built_in_id, level = unpack('= 80: try: name = unpack_unicode(data, 2, lenlen=2) except UnicodeDecodeError: print("STYLE: built_in=%d xf_index=%d built_in_id=%d level=%d" \ % (built_in, xf_index, built_in_id, level), file=book.logfile) print("raw bytes:", repr(data[2:]), file=book.logfile) raise else: name = unpack_string(data, 2, book.encoding, lenlen=1) if blah and not name: print("WARNING *** A user-defined style has a zero-length name", file=book.logfile) book.style_name_map[name] = (built_in, xf_index) if blah: fprintf(book.logfile, "STYLE: built_in=%d xf_index=%d built_in_id=%d level=%d name=%r\n", built_in, xf_index, built_in_id, level, name) def check_colour_indexes_in_obj(book, obj, orig_index): alist = sorted(obj.__dict__.items()) for attr, nobj in alist: if hasattr(nobj, 'dump'): check_colour_indexes_in_obj(book, nobj, orig_index) elif attr.find('colour_index') >= 0: if nobj in book.colour_map: book.colour_indexes_used[nobj] = 1 continue oname = obj.__class__.__name__ print("*** xf #%d : %s.%s = 0x%04x (unknown)" \ % (orig_index, oname, attr, nobj), file=book.logfile) def fill_in_standard_formats(book): for x in std_format_code_types.keys(): if x not in book.format_map: ty = std_format_code_types[x] # Note: many standard format codes (mostly CJK date formats) have # format strings that vary by locale; xlrd does not (yet) # handle those; the type (date or numeric) is recorded but the fmt_str will be None. fmt_str = std_format_strings.get(x) fmtobj = Format(x, ty, fmt_str) book.format_map[x] = fmtobj def handle_xf(self, data): ### self is a Book instance # DEBUG = 0 blah = DEBUG or self.verbosity >= 3 bv = self.biff_version xf = XF() xf.alignment = XFAlignment() xf.alignment.indent_level = 0 xf.alignment.shrink_to_fit = 0 xf.alignment.text_direction = 0 xf.border = XFBorder() xf.border.diag_up = 0 xf.border.diag_down = 0 xf.border.diag_colour_index = 0 xf.border.diag_line_style = 0 # no line xf.background = XFBackground() xf.protection = XFProtection() # fill in the known standard formats if bv >= 50 and not self.xfcount: # i.e. do this once before we process the first XF record fill_in_standard_formats(self) if bv >= 80: unpack_fmt = '> 2 for attr_stem in \ "format font alignment border background protection".split(): attr = "_" + attr_stem + "_flag" setattr(xf, attr, reg & 1) reg >>= 1 upkbitsL(xf.border, pkd_brdbkg1, ( (0, 0x0000000f, 'left_line_style'), (4, 0x000000f0, 'right_line_style'), (8, 0x00000f00, 'top_line_style'), (12, 0x0000f000, 'bottom_line_style'), (16, 0x007f0000, 'left_colour_index'), (23, 0x3f800000, 'right_colour_index'), (30, 0x40000000, 'diag_down'), (31, 0x80000000, 'diag_up'), )) upkbits(xf.border, pkd_brdbkg2, ( (0, 0x0000007F, 'top_colour_index'), (7, 0x00003F80, 'bottom_colour_index'), (14, 0x001FC000, 'diag_colour_index'), (21, 0x01E00000, 'diag_line_style'), )) upkbitsL(xf.background, pkd_brdbkg2, ( (26, 0xFC000000, 'fill_pattern'), )) upkbits(xf.background, pkd_brdbkg3, ( (0, 0x007F, 'pattern_colour_index'), (7, 0x3F80, 'background_colour_index'), )) elif bv >= 50: unpack_fmt = '> 2 for attr_stem in \ "format font alignment border background protection".split(): attr = "_" + attr_stem + "_flag" setattr(xf, attr, reg & 1) reg >>= 1 upkbitsL(xf.background, pkd_brdbkg1, ( ( 0, 0x0000007F, 'pattern_colour_index'), ( 7, 0x00003F80, 'background_colour_index'), (16, 0x003F0000, 'fill_pattern'), )) upkbitsL(xf.border, pkd_brdbkg1, ( (22, 0x01C00000, 'bottom_line_style'), (25, 0xFE000000, 'bottom_colour_index'), )) upkbits(xf.border, pkd_brdbkg2, ( ( 0, 0x00000007, 'top_line_style'), ( 3, 0x00000038, 'left_line_style'), ( 6, 0x000001C0, 'right_line_style'), ( 9, 0x0000FE00, 'top_colour_index'), (16, 0x007F0000, 'left_colour_index'), (23, 0x3F800000, 'right_colour_index'), )) elif bv >= 40: unpack_fmt = '> 6 xf.alignment.rotation = [0, 255, 90, 180][orientation] reg = pkd_used >> 2 for attr_stem in \ "format font alignment border background protection".split(): attr = "_" + attr_stem + "_flag" setattr(xf, attr, reg & 1) reg >>= 1 upkbits(xf.background, pkd_bkg_34, ( ( 0, 0x003F, 'fill_pattern'), ( 6, 0x07C0, 'pattern_colour_index'), (11, 0xF800, 'background_colour_index'), )) upkbitsL(xf.border, pkd_brd_34, ( ( 0, 0x00000007, 'top_line_style'), ( 3, 0x000000F8, 'top_colour_index'), ( 8, 0x00000700, 'left_line_style'), (11, 0x0000F800, 'left_colour_index'), (16, 0x00070000, 'bottom_line_style'), (19, 0x00F80000, 'bottom_colour_index'), (24, 0x07000000, 'right_line_style'), (27, 0xF8000000, 'right_colour_index'), )) elif bv == 30: unpack_fmt = '> 2 for attr_stem in \ "format font alignment border background protection".split(): attr = "_" + attr_stem + "_flag" setattr(xf, attr, reg & 1) reg >>= 1 upkbits(xf.background, pkd_bkg_34, ( ( 0, 0x003F, 'fill_pattern'), ( 6, 0x07C0, 'pattern_colour_index'), (11, 0xF800, 'background_colour_index'), )) upkbitsL(xf.border, pkd_brd_34, ( ( 0, 0x00000007, 'top_line_style'), ( 3, 0x000000F8, 'top_colour_index'), ( 8, 0x00000700, 'left_line_style'), (11, 0x0000F800, 'left_colour_index'), (16, 0x00070000, 'bottom_line_style'), (19, 0x00F80000, 'bottom_colour_index'), (24, 0x07000000, 'right_line_style'), (27, 0xF8000000, 'right_colour_index'), )) xf.alignment.vert_align = 2 # bottom xf.alignment.rotation = 0 elif bv == 21: #### Warning: incomplete treatment; formatting_info not fully supported. #### Probably need to offset incoming BIFF2 XF[n] to BIFF8-like XF[n+16], #### and create XF[0:16] like the standard ones in BIFF8 #### *AND* add 16 to all XF references in cell records :-( (xf.font_index, format_etc, halign_etc) = unpack('= 3 blah1 = DEBUG or self.verbosity >= 1 if blah: fprintf(self.logfile, "xf_epilogue called ...\n") def check_same(book_arg, xf_arg, parent_arg, attr): # the _arg caper is to avoid a Warning msg from Python 2.1 :-( if getattr(xf_arg, attr) != getattr(parent_arg, attr): fprintf(book_arg.logfile, "NOTE !!! XF[%d] parent[%d] %s different\n", xf_arg.xf_index, parent_arg.xf_index, attr) for xfx in xrange(num_xfs): xf = self.xf_list[xfx] if xf.format_key not in self.format_map: msg = "ERROR *** XF[%d] unknown format key (%d, 0x%04x)\n" fprintf(self.logfile, msg, xf.xf_index, xf.format_key, xf.format_key) xf.format_key = 0 fmt = self.format_map[xf.format_key] cellty = _cellty_from_fmtty[fmt.type] self._xf_index_to_xl_type_map[xf.xf_index] = cellty # Now for some assertions etc if not self.formatting_info: continue if xf.is_style: continue if not(0 <= xf.parent_style_index < num_xfs): if blah1: fprintf(self.logfile, "WARNING *** XF[%d]: is_style=%d but parent_style_index=%d\n", xf.xf_index, xf.is_style, xf.parent_style_index) # make it conform xf.parent_style_index = 0 if self.biff_version >= 30: if blah1: if xf.parent_style_index == xf.xf_index: fprintf(self.logfile, "NOTE !!! XF[%d]: parent_style_index is also %d\n", xf.xf_index, xf.parent_style_index) elif not self.xf_list[xf.parent_style_index].is_style: fprintf(self.logfile, "NOTE !!! XF[%d]: parent_style_index is %d; style flag not set\n", xf.xf_index, xf.parent_style_index) if blah1 and xf.parent_style_index > xf.xf_index: fprintf(self.logfile, "NOTE !!! XF[%d]: parent_style_index is %d; out of order?\n", xf.xf_index, xf.parent_style_index) parent = self.xf_list[xf.parent_style_index] if not xf._alignment_flag and not parent._alignment_flag: if blah1: check_same(self, xf, parent, 'alignment') if not xf._background_flag and not parent._background_flag: if blah1: check_same(self, xf, parent, 'background') if not xf._border_flag and not parent._border_flag: if blah1: check_same(self, xf, parent, 'border') if not xf._protection_flag and not parent._protection_flag: if blah1: check_same(self, xf, parent, 'protection') if not xf._format_flag and not parent._format_flag: if blah1 and xf.format_key != parent.format_key: fprintf(self.logfile, "NOTE !!! XF[%d] fmtk=%d, parent[%d] fmtk=%r\n%r / %r\n", xf.xf_index, xf.format_key, parent.xf_index, parent.format_key, self.format_map[xf.format_key].format_str, self.format_map[parent.format_key].format_str) if not xf._font_flag and not parent._font_flag: if blah1 and xf.font_index != parent.font_index: fprintf(self.logfile, "NOTE !!! XF[%d] fontx=%d, parent[%d] fontx=%r\n", xf.xf_index, xf.font_index, parent.xf_index, parent.font_index) def initialise_book(book): initialise_colour_map(book) book._xf_epilogue_done = 0 methods = ( handle_font, handle_efont, handle_format, is_date_format_string, handle_palette, palette_epilogue, handle_style, handle_xf, xf_epilogue, ) for method in methods: setattr(book.__class__, method.__name__, method) ## #

    A collection of the border-related attributes of an XF record. # Items correspond to those in the Excel UI's Format/Cells/Border tab.

    #

    An explanations of "colour index" is given in the Formatting # section at the start of this document. # There are five line style attributes; possible values and the # associated meanings are: # 0 = No line, # 1 = Thin, # 2 = Medium, # 3 = Dashed, # 4 = Dotted, # 5 = Thick, # 6 = Double, # 7 = Hair, # 8 = Medium dashed, # 9 = Thin dash-dotted, # 10 = Medium dash-dotted, # 11 = Thin dash-dot-dotted, # 12 = Medium dash-dot-dotted, # 13 = Slanted medium dash-dotted. # The line styles 8 to 13 appear in BIFF8 files (Excel 97 and later) only. # For pictures of the line styles, refer to OOo docs s3.10 (p22) # "Line Styles for Cell Borders (BIFF3-BIFF8)".

    #
    -- New in version 0.6.1 class XFBorder(BaseObject, EqNeAttrs): ## # The colour index for the cell's top line top_colour_index = 0 ## # The colour index for the cell's bottom line bottom_colour_index = 0 ## # The colour index for the cell's left line left_colour_index = 0 ## # The colour index for the cell's right line right_colour_index = 0 ## # The colour index for the cell's diagonal lines, if any diag_colour_index = 0 ## # The line style for the cell's top line top_line_style = 0 ## # The line style for the cell's bottom line bottom_line_style = 0 ## # The line style for the cell's left line left_line_style = 0 ## # The line style for the cell's right line right_line_style = 0 ## # The line style for the cell's diagonal lines, if any diag_line_style = 0 ## # 1 = draw a diagonal from top left to bottom right diag_down = 0 ## # 1 = draw a diagonal from bottom left to top right diag_up = 0 ## # A collection of the background-related attributes of an XF record. # Items correspond to those in the Excel UI's Format/Cells/Patterns tab. # An explanation of "colour index" is given in the Formatting # section at the start of this document. #
    -- New in version 0.6.1 class XFBackground(BaseObject, EqNeAttrs): ## # See section 3.11 of the OOo docs. fill_pattern = 0 ## # See section 3.11 of the OOo docs. background_colour_index = 0 ## # See section 3.11 of the OOo docs. pattern_colour_index = 0 ## # A collection of the alignment and similar attributes of an XF record. # Items correspond to those in the Excel UI's Format/Cells/Alignment tab. #
    -- New in version 0.6.1 class XFAlignment(BaseObject, EqNeAttrs): ## # Values: section 6.115 (p 214) of OOo docs hor_align = 0 ## # Values: section 6.115 (p 215) of OOo docs vert_align = 0 ## # Values: section 6.115 (p 215) of OOo docs.
    # Note: file versions BIFF7 and earlier use the documented # "orientation" attribute; this will be mapped (without loss) # into "rotation". rotation = 0 ## # 1 = text is wrapped at right margin text_wrapped = 0 ## # A number in range(15). indent_level = 0 ## # 1 = shrink font size to fit text into cell. shrink_to_fit = 0 ## # 0 = according to context; 1 = left-to-right; 2 = right-to-left text_direction = 0 ## # A collection of the protection-related attributes of an XF record. # Items correspond to those in the Excel UI's Format/Cells/Protection tab. # Note the OOo docs include the "cell or style" bit # in this bundle of attributes. # This is incorrect; the bit is used in determining which bundles to use. #
    -- New in version 0.6.1 class XFProtection(BaseObject, EqNeAttrs): ## # 1 = Cell is prevented from being changed, moved, resized, or deleted # (only if the sheet is protected). cell_locked = 0 ## # 1 = Hide formula so that it doesn't appear in the formula bar when # the cell is selected (only if the sheet is protected). formula_hidden = 0 ## # eXtended Formatting information for cells, rows, columns and styles. #
    -- New in version 0.6.1 # #

    Each of the 6 flags below describes the validity of # a specific group of attributes. #
    # In cell XFs, flag==0 means the attributes of the parent style XF are used, # (but only if the attributes are valid there); flag==1 means the attributes # of this XF are used.
    # In style XFs, flag==0 means the attribute setting is valid; flag==1 means # the attribute should be ignored.
    # Note that the API # provides both "raw" XFs and "computed" XFs -- in the latter case, cell XFs # have had the above inheritance mechanism applied. #

    class XF(BaseObject): ## # 0 = cell XF, 1 = style XF is_style = 0 ## # cell XF: Index into Book.xf_list # of this XF's style XF
    # style XF: 0xFFF parent_style_index = 0 ## # _format_flag = 0 ## # _font_flag = 0 ## # _alignment_flag = 0 ## # _border_flag = 0 ## # _background_flag = 0 ## #   _protection_flag = 0 ## # Index into Book.xf_list xf_index = 0 ## # Index into Book.font_list font_index = 0 ## # Key into Book.format_map #

    # Warning: OOo docs on the XF record call this "Index to FORMAT record". # It is not an index in the Python sense. It is a key to a map. # It is true only for Excel 4.0 and earlier files # that the key into format_map from an XF instance # is the same as the index into format_list, and only # if the index is less than 164. #

    format_key = 0 ## # An instance of an XFProtection object. protection = None ## # An instance of an XFBackground object. background = None ## # An instance of an XFAlignment object. alignment = None ## # An instance of an XFBorder object. border = None xlrd-0.9.4/xlrd/formula.py0000644000076500000240000027013512155372403015732 0ustar chrisstaff00000000000000# -*- coding: cp1252 -*- ## # Module for parsing/evaluating Microsoft Excel formulas. # #

    Copyright 2005-2012 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. from __future__ import print_function import copy from struct import unpack from .timemachine import * from .biffh import unpack_unicode_update_pos, unpack_string_update_pos, \ XLRDError, hex_char_dump, error_text_from_code, BaseObject __all__ = [ 'oBOOL', 'oERR', 'oNUM', 'oREF', 'oREL', 'oSTRG', 'oUNK', 'decompile_formula', 'dump_formula', 'evaluate_name_formula', 'okind_dict', 'rangename3d', 'rangename3drel', 'cellname', 'cellnameabs', 'colname', 'FMLA_TYPE_CELL', 'FMLA_TYPE_SHARED', 'FMLA_TYPE_ARRAY', 'FMLA_TYPE_COND_FMT', 'FMLA_TYPE_DATA_VAL', 'FMLA_TYPE_NAME', ] FMLA_TYPE_CELL = 1 FMLA_TYPE_SHARED = 2 FMLA_TYPE_ARRAY = 4 FMLA_TYPE_COND_FMT = 8 FMLA_TYPE_DATA_VAL = 16 FMLA_TYPE_NAME = 32 ALL_FMLA_TYPES = 63 FMLA_TYPEDESCR_MAP = { 1 : 'CELL', 2 : 'SHARED', 4 : 'ARRAY', 8 : 'COND-FMT', 16: 'DATA-VAL', 32: 'NAME', } _TOKEN_NOT_ALLOWED = { 0x01: ALL_FMLA_TYPES - FMLA_TYPE_CELL, # tExp 0x02: ALL_FMLA_TYPES - FMLA_TYPE_CELL, # tTbl 0x0F: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tIsect 0x10: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tUnion/List 0x11: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tRange 0x20: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tArray 0x23: FMLA_TYPE_SHARED, # tName 0x39: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tNameX 0x3A: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tRef3d 0x3B: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tArea3d 0x2C: FMLA_TYPE_CELL + FMLA_TYPE_ARRAY, # tRefN 0x2D: FMLA_TYPE_CELL + FMLA_TYPE_ARRAY, # tAreaN # plus weird stuff like tMem* }.get oBOOL = 3 oERR = 4 oMSNG = 5 # tMissArg oNUM = 2 oREF = -1 oREL = -2 oSTRG = 1 oUNK = 0 okind_dict = { -2: "oREL", -1: "oREF", 0 : "oUNK", 1 : "oSTRG", 2 : "oNUM", 3 : "oBOOL", 4 : "oERR", 5 : "oMSNG", } listsep = ',' #### probably should depend on locale # sztabN[opcode] -> the number of bytes to consume. # -1 means variable # -2 means this opcode not implemented in this version. # Which N to use? Depends on biff_version; see szdict. sztab0 = [-2, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -2, -1, 8, 4, 2, 2, 3, 9, 8, 2, 3, 8, 4, 7, 5, 5, 5, 2, 4, 7, 4, 7, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2, 3, -2, -2, -2, -2, -2, -2, -2] sztab1 = [-2, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -2, -1, 11, 5, 2, 2, 3, 9, 9, 2, 3, 11, 4, 7, 7, 7, 7, 3, 4, 7, 4, 7, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2, 3, -2, -2, -2, -2, -2, -2, -2] sztab2 = [-2, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -2, -1, 11, 5, 2, 2, 3, 9, 9, 3, 4, 11, 4, 7, 7, 7, 7, 3, 4, 7, 4, 7, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2] sztab3 = [-2, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -2, -1, -2, -2, 2, 2, 3, 9, 9, 3, 4, 15, 4, 7, 7, 7, 7, 3, 4, 7, 4, 7, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2, -2, 25, 18, 21, 18, 21, -2, -2] sztab4 = [-2, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -2, -2, 2, 2, 3, 9, 9, 3, 4, 5, 5, 9, 7, 7, 7, 3, 5, 9, 5, 9, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2, -2, 7, 7, 11, 7, 11, -2, -2] szdict = { 20 : sztab0, 21 : sztab0, 30 : sztab1, 40 : sztab2, 45 : sztab2, 50 : sztab3, 70 : sztab3, 80 : sztab4, } # For debugging purposes ... the name for each opcode # (without the prefix "t" used on OOo docs) onames = ['Unk00', 'Exp', 'Tbl', 'Add', 'Sub', 'Mul', 'Div', 'Power', 'Concat', 'LT', 'LE', 'EQ', 'GE', 'GT', 'NE', 'Isect', 'List', 'Range', 'Uplus', 'Uminus', 'Percent', 'Paren', 'MissArg', 'Str', 'Extended', 'Attr', 'Sheet', 'EndSheet', 'Err', 'Bool', 'Int', 'Num', 'Array', 'Func', 'FuncVar', 'Name', 'Ref', 'Area', 'MemArea', 'MemErr', 'MemNoMem', 'MemFunc', 'RefErr', 'AreaErr', 'RefN', 'AreaN', 'MemAreaN', 'MemNoMemN', '', '', '', '', '', '', '', '', 'FuncCE', 'NameX', 'Ref3d', 'Area3d', 'RefErr3d', 'AreaErr3d', '', ''] func_defs = { # index: (name, min#args, max#args, flags, #known_args, return_type, kargs) 0 : ('COUNT', 0, 30, 0x04, 1, 'V', 'R'), 1 : ('IF', 2, 3, 0x04, 3, 'V', 'VRR'), 2 : ('ISNA', 1, 1, 0x02, 1, 'V', 'V'), 3 : ('ISERROR', 1, 1, 0x02, 1, 'V', 'V'), 4 : ('SUM', 0, 30, 0x04, 1, 'V', 'R'), 5 : ('AVERAGE', 1, 30, 0x04, 1, 'V', 'R'), 6 : ('MIN', 1, 30, 0x04, 1, 'V', 'R'), 7 : ('MAX', 1, 30, 0x04, 1, 'V', 'R'), 8 : ('ROW', 0, 1, 0x04, 1, 'V', 'R'), 9 : ('COLUMN', 0, 1, 0x04, 1, 'V', 'R'), 10 : ('NA', 0, 0, 0x02, 0, 'V', ''), 11 : ('NPV', 2, 30, 0x04, 2, 'V', 'VR'), 12 : ('STDEV', 1, 30, 0x04, 1, 'V', 'R'), 13 : ('DOLLAR', 1, 2, 0x04, 1, 'V', 'V'), 14 : ('FIXED', 2, 3, 0x04, 3, 'V', 'VVV'), 15 : ('SIN', 1, 1, 0x02, 1, 'V', 'V'), 16 : ('COS', 1, 1, 0x02, 1, 'V', 'V'), 17 : ('TAN', 1, 1, 0x02, 1, 'V', 'V'), 18 : ('ATAN', 1, 1, 0x02, 1, 'V', 'V'), 19 : ('PI', 0, 0, 0x02, 0, 'V', ''), 20 : ('SQRT', 1, 1, 0x02, 1, 'V', 'V'), 21 : ('EXP', 1, 1, 0x02, 1, 'V', 'V'), 22 : ('LN', 1, 1, 0x02, 1, 'V', 'V'), 23 : ('LOG10', 1, 1, 0x02, 1, 'V', 'V'), 24 : ('ABS', 1, 1, 0x02, 1, 'V', 'V'), 25 : ('INT', 1, 1, 0x02, 1, 'V', 'V'), 26 : ('SIGN', 1, 1, 0x02, 1, 'V', 'V'), 27 : ('ROUND', 2, 2, 0x02, 2, 'V', 'VV'), 28 : ('LOOKUP', 2, 3, 0x04, 2, 'V', 'VR'), 29 : ('INDEX', 2, 4, 0x0c, 4, 'R', 'RVVV'), 30 : ('REPT', 2, 2, 0x02, 2, 'V', 'VV'), 31 : ('MID', 3, 3, 0x02, 3, 'V', 'VVV'), 32 : ('LEN', 1, 1, 0x02, 1, 'V', 'V'), 33 : ('VALUE', 1, 1, 0x02, 1, 'V', 'V'), 34 : ('TRUE', 0, 0, 0x02, 0, 'V', ''), 35 : ('FALSE', 0, 0, 0x02, 0, 'V', ''), 36 : ('AND', 1, 30, 0x04, 1, 'V', 'R'), 37 : ('OR', 1, 30, 0x04, 1, 'V', 'R'), 38 : ('NOT', 1, 1, 0x02, 1, 'V', 'V'), 39 : ('MOD', 2, 2, 0x02, 2, 'V', 'VV'), 40 : ('DCOUNT', 3, 3, 0x02, 3, 'V', 'RRR'), 41 : ('DSUM', 3, 3, 0x02, 3, 'V', 'RRR'), 42 : ('DAVERAGE', 3, 3, 0x02, 3, 'V', 'RRR'), 43 : ('DMIN', 3, 3, 0x02, 3, 'V', 'RRR'), 44 : ('DMAX', 3, 3, 0x02, 3, 'V', 'RRR'), 45 : ('DSTDEV', 3, 3, 0x02, 3, 'V', 'RRR'), 46 : ('VAR', 1, 30, 0x04, 1, 'V', 'R'), 47 : ('DVAR', 3, 3, 0x02, 3, 'V', 'RRR'), 48 : ('TEXT', 2, 2, 0x02, 2, 'V', 'VV'), 49 : ('LINEST', 1, 4, 0x04, 4, 'A', 'RRVV'), 50 : ('TREND', 1, 4, 0x04, 4, 'A', 'RRRV'), 51 : ('LOGEST', 1, 4, 0x04, 4, 'A', 'RRVV'), 52 : ('GROWTH', 1, 4, 0x04, 4, 'A', 'RRRV'), 56 : ('PV', 3, 5, 0x04, 5, 'V', 'VVVVV'), 57 : ('FV', 3, 5, 0x04, 5, 'V', 'VVVVV'), 58 : ('NPER', 3, 5, 0x04, 5, 'V', 'VVVVV'), 59 : ('PMT', 3, 5, 0x04, 5, 'V', 'VVVVV'), 60 : ('RATE', 3, 6, 0x04, 6, 'V', 'VVVVVV'), 61 : ('MIRR', 3, 3, 0x02, 3, 'V', 'RVV'), 62 : ('IRR', 1, 2, 0x04, 2, 'V', 'RV'), 63 : ('RAND', 0, 0, 0x0a, 0, 'V', ''), 64 : ('MATCH', 2, 3, 0x04, 3, 'V', 'VRR'), 65 : ('DATE', 3, 3, 0x02, 3, 'V', 'VVV'), 66 : ('TIME', 3, 3, 0x02, 3, 'V', 'VVV'), 67 : ('DAY', 1, 1, 0x02, 1, 'V', 'V'), 68 : ('MONTH', 1, 1, 0x02, 1, 'V', 'V'), 69 : ('YEAR', 1, 1, 0x02, 1, 'V', 'V'), 70 : ('WEEKDAY', 1, 2, 0x04, 2, 'V', 'VV'), 71 : ('HOUR', 1, 1, 0x02, 1, 'V', 'V'), 72 : ('MINUTE', 1, 1, 0x02, 1, 'V', 'V'), 73 : ('SECOND', 1, 1, 0x02, 1, 'V', 'V'), 74 : ('NOW', 0, 0, 0x0a, 0, 'V', ''), 75 : ('AREAS', 1, 1, 0x02, 1, 'V', 'R'), 76 : ('ROWS', 1, 1, 0x02, 1, 'V', 'R'), 77 : ('COLUMNS', 1, 1, 0x02, 1, 'V', 'R'), 78 : ('OFFSET', 3, 5, 0x04, 5, 'R', 'RVVVV'), 82 : ('SEARCH', 2, 3, 0x04, 3, 'V', 'VVV'), 83 : ('TRANSPOSE', 1, 1, 0x02, 1, 'A', 'A'), 86 : ('TYPE', 1, 1, 0x02, 1, 'V', 'V'), 92 : ('SERIESSUM', 4, 4, 0x02, 4, 'V', 'VVVA'), 97 : ('ATAN2', 2, 2, 0x02, 2, 'V', 'VV'), 98 : ('ASIN', 1, 1, 0x02, 1, 'V', 'V'), 99 : ('ACOS', 1, 1, 0x02, 1, 'V', 'V'), 100: ('CHOOSE', 2, 30, 0x04, 2, 'V', 'VR'), 101: ('HLOOKUP', 3, 4, 0x04, 4, 'V', 'VRRV'), 102: ('VLOOKUP', 3, 4, 0x04, 4, 'V', 'VRRV'), 105: ('ISREF', 1, 1, 0x02, 1, 'V', 'R'), 109: ('LOG', 1, 2, 0x04, 2, 'V', 'VV'), 111: ('CHAR', 1, 1, 0x02, 1, 'V', 'V'), 112: ('LOWER', 1, 1, 0x02, 1, 'V', 'V'), 113: ('UPPER', 1, 1, 0x02, 1, 'V', 'V'), 114: ('PROPER', 1, 1, 0x02, 1, 'V', 'V'), 115: ('LEFT', 1, 2, 0x04, 2, 'V', 'VV'), 116: ('RIGHT', 1, 2, 0x04, 2, 'V', 'VV'), 117: ('EXACT', 2, 2, 0x02, 2, 'V', 'VV'), 118: ('TRIM', 1, 1, 0x02, 1, 'V', 'V'), 119: ('REPLACE', 4, 4, 0x02, 4, 'V', 'VVVV'), 120: ('SUBSTITUTE', 3, 4, 0x04, 4, 'V', 'VVVV'), 121: ('CODE', 1, 1, 0x02, 1, 'V', 'V'), 124: ('FIND', 2, 3, 0x04, 3, 'V', 'VVV'), 125: ('CELL', 1, 2, 0x0c, 2, 'V', 'VR'), 126: ('ISERR', 1, 1, 0x02, 1, 'V', 'V'), 127: ('ISTEXT', 1, 1, 0x02, 1, 'V', 'V'), 128: ('ISNUMBER', 1, 1, 0x02, 1, 'V', 'V'), 129: ('ISBLANK', 1, 1, 0x02, 1, 'V', 'V'), 130: ('T', 1, 1, 0x02, 1, 'V', 'R'), 131: ('N', 1, 1, 0x02, 1, 'V', 'R'), 140: ('DATEVALUE', 1, 1, 0x02, 1, 'V', 'V'), 141: ('TIMEVALUE', 1, 1, 0x02, 1, 'V', 'V'), 142: ('SLN', 3, 3, 0x02, 3, 'V', 'VVV'), 143: ('SYD', 4, 4, 0x02, 4, 'V', 'VVVV'), 144: ('DDB', 4, 5, 0x04, 5, 'V', 'VVVVV'), 148: ('INDIRECT', 1, 2, 0x0c, 2, 'R', 'VV'), 162: ('CLEAN', 1, 1, 0x02, 1, 'V', 'V'), 163: ('MDETERM', 1, 1, 0x02, 1, 'V', 'A'), 164: ('MINVERSE', 1, 1, 0x02, 1, 'A', 'A'), 165: ('MMULT', 2, 2, 0x02, 2, 'A', 'AA'), 167: ('IPMT', 4, 6, 0x04, 6, 'V', 'VVVVVV'), 168: ('PPMT', 4, 6, 0x04, 6, 'V', 'VVVVVV'), 169: ('COUNTA', 0, 30, 0x04, 1, 'V', 'R'), 183: ('PRODUCT', 0, 30, 0x04, 1, 'V', 'R'), 184: ('FACT', 1, 1, 0x02, 1, 'V', 'V'), 189: ('DPRODUCT', 3, 3, 0x02, 3, 'V', 'RRR'), 190: ('ISNONTEXT', 1, 1, 0x02, 1, 'V', 'V'), 193: ('STDEVP', 1, 30, 0x04, 1, 'V', 'R'), 194: ('VARP', 1, 30, 0x04, 1, 'V', 'R'), 195: ('DSTDEVP', 3, 3, 0x02, 3, 'V', 'RRR'), 196: ('DVARP', 3, 3, 0x02, 3, 'V', 'RRR'), 197: ('TRUNC', 1, 2, 0x04, 2, 'V', 'VV'), 198: ('ISLOGICAL', 1, 1, 0x02, 1, 'V', 'V'), 199: ('DCOUNTA', 3, 3, 0x02, 3, 'V', 'RRR'), 204: ('USDOLLAR', 1, 2, 0x04, 2, 'V', 'VV'), 205: ('FINDB', 2, 3, 0x04, 3, 'V', 'VVV'), 206: ('SEARCHB', 2, 3, 0x04, 3, 'V', 'VVV'), 207: ('REPLACEB', 4, 4, 0x02, 4, 'V', 'VVVV'), 208: ('LEFTB', 1, 2, 0x04, 2, 'V', 'VV'), 209: ('RIGHTB', 1, 2, 0x04, 2, 'V', 'VV'), 210: ('MIDB', 3, 3, 0x02, 3, 'V', 'VVV'), 211: ('LENB', 1, 1, 0x02, 1, 'V', 'V'), 212: ('ROUNDUP', 2, 2, 0x02, 2, 'V', 'VV'), 213: ('ROUNDDOWN', 2, 2, 0x02, 2, 'V', 'VV'), 214: ('ASC', 1, 1, 0x02, 1, 'V', 'V'), 215: ('DBCS', 1, 1, 0x02, 1, 'V', 'V'), 216: ('RANK', 2, 3, 0x04, 3, 'V', 'VRV'), 219: ('ADDRESS', 2, 5, 0x04, 5, 'V', 'VVVVV'), 220: ('DAYS360', 2, 3, 0x04, 3, 'V', 'VVV'), 221: ('TODAY', 0, 0, 0x0a, 0, 'V', ''), 222: ('VDB', 5, 7, 0x04, 7, 'V', 'VVVVVVV'), 227: ('MEDIAN', 1, 30, 0x04, 1, 'V', 'R'), 228: ('SUMPRODUCT', 1, 30, 0x04, 1, 'V', 'A'), 229: ('SINH', 1, 1, 0x02, 1, 'V', 'V'), 230: ('COSH', 1, 1, 0x02, 1, 'V', 'V'), 231: ('TANH', 1, 1, 0x02, 1, 'V', 'V'), 232: ('ASINH', 1, 1, 0x02, 1, 'V', 'V'), 233: ('ACOSH', 1, 1, 0x02, 1, 'V', 'V'), 234: ('ATANH', 1, 1, 0x02, 1, 'V', 'V'), 235: ('DGET', 3, 3, 0x02, 3, 'V', 'RRR'), 244: ('INFO', 1, 1, 0x02, 1, 'V', 'V'), 247: ('DB', 4, 5, 0x04, 5, 'V', 'VVVVV'), 252: ('FREQUENCY', 2, 2, 0x02, 2, 'A', 'RR'), 261: ('ERROR.TYPE', 1, 1, 0x02, 1, 'V', 'V'), 269: ('AVEDEV', 1, 30, 0x04, 1, 'V', 'R'), 270: ('BETADIST', 3, 5, 0x04, 1, 'V', 'V'), 271: ('GAMMALN', 1, 1, 0x02, 1, 'V', 'V'), 272: ('BETAINV', 3, 5, 0x04, 1, 'V', 'V'), 273: ('BINOMDIST', 4, 4, 0x02, 4, 'V', 'VVVV'), 274: ('CHIDIST', 2, 2, 0x02, 2, 'V', 'VV'), 275: ('CHIINV', 2, 2, 0x02, 2, 'V', 'VV'), 276: ('COMBIN', 2, 2, 0x02, 2, 'V', 'VV'), 277: ('CONFIDENCE', 3, 3, 0x02, 3, 'V', 'VVV'), 278: ('CRITBINOM', 3, 3, 0x02, 3, 'V', 'VVV'), 279: ('EVEN', 1, 1, 0x02, 1, 'V', 'V'), 280: ('EXPONDIST', 3, 3, 0x02, 3, 'V', 'VVV'), 281: ('FDIST', 3, 3, 0x02, 3, 'V', 'VVV'), 282: ('FINV', 3, 3, 0x02, 3, 'V', 'VVV'), 283: ('FISHER', 1, 1, 0x02, 1, 'V', 'V'), 284: ('FISHERINV', 1, 1, 0x02, 1, 'V', 'V'), 285: ('FLOOR', 2, 2, 0x02, 2, 'V', 'VV'), 286: ('GAMMADIST', 4, 4, 0x02, 4, 'V', 'VVVV'), 287: ('GAMMAINV', 3, 3, 0x02, 3, 'V', 'VVV'), 288: ('CEILING', 2, 2, 0x02, 2, 'V', 'VV'), 289: ('HYPGEOMDIST', 4, 4, 0x02, 4, 'V', 'VVVV'), 290: ('LOGNORMDIST', 3, 3, 0x02, 3, 'V', 'VVV'), 291: ('LOGINV', 3, 3, 0x02, 3, 'V', 'VVV'), 292: ('NEGBINOMDIST', 3, 3, 0x02, 3, 'V', 'VVV'), 293: ('NORMDIST', 4, 4, 0x02, 4, 'V', 'VVVV'), 294: ('NORMSDIST', 1, 1, 0x02, 1, 'V', 'V'), 295: ('NORMINV', 3, 3, 0x02, 3, 'V', 'VVV'), 296: ('NORMSINV', 1, 1, 0x02, 1, 'V', 'V'), 297: ('STANDARDIZE', 3, 3, 0x02, 3, 'V', 'VVV'), 298: ('ODD', 1, 1, 0x02, 1, 'V', 'V'), 299: ('PERMUT', 2, 2, 0x02, 2, 'V', 'VV'), 300: ('POISSON', 3, 3, 0x02, 3, 'V', 'VVV'), 301: ('TDIST', 3, 3, 0x02, 3, 'V', 'VVV'), 302: ('WEIBULL', 4, 4, 0x02, 4, 'V', 'VVVV'), 303: ('SUMXMY2', 2, 2, 0x02, 2, 'V', 'AA'), 304: ('SUMX2MY2', 2, 2, 0x02, 2, 'V', 'AA'), 305: ('SUMX2PY2', 2, 2, 0x02, 2, 'V', 'AA'), 306: ('CHITEST', 2, 2, 0x02, 2, 'V', 'AA'), 307: ('CORREL', 2, 2, 0x02, 2, 'V', 'AA'), 308: ('COVAR', 2, 2, 0x02, 2, 'V', 'AA'), 309: ('FORECAST', 3, 3, 0x02, 3, 'V', 'VAA'), 310: ('FTEST', 2, 2, 0x02, 2, 'V', 'AA'), 311: ('INTERCEPT', 2, 2, 0x02, 2, 'V', 'AA'), 312: ('PEARSON', 2, 2, 0x02, 2, 'V', 'AA'), 313: ('RSQ', 2, 2, 0x02, 2, 'V', 'AA'), 314: ('STEYX', 2, 2, 0x02, 2, 'V', 'AA'), 315: ('SLOPE', 2, 2, 0x02, 2, 'V', 'AA'), 316: ('TTEST', 4, 4, 0x02, 4, 'V', 'AAVV'), 317: ('PROB', 3, 4, 0x04, 3, 'V', 'AAV'), 318: ('DEVSQ', 1, 30, 0x04, 1, 'V', 'R'), 319: ('GEOMEAN', 1, 30, 0x04, 1, 'V', 'R'), 320: ('HARMEAN', 1, 30, 0x04, 1, 'V', 'R'), 321: ('SUMSQ', 0, 30, 0x04, 1, 'V', 'R'), 322: ('KURT', 1, 30, 0x04, 1, 'V', 'R'), 323: ('SKEW', 1, 30, 0x04, 1, 'V', 'R'), 324: ('ZTEST', 2, 3, 0x04, 2, 'V', 'RV'), 325: ('LARGE', 2, 2, 0x02, 2, 'V', 'RV'), 326: ('SMALL', 2, 2, 0x02, 2, 'V', 'RV'), 327: ('QUARTILE', 2, 2, 0x02, 2, 'V', 'RV'), 328: ('PERCENTILE', 2, 2, 0x02, 2, 'V', 'RV'), 329: ('PERCENTRANK', 2, 3, 0x04, 2, 'V', 'RV'), 330: ('MODE', 1, 30, 0x04, 1, 'V', 'A'), 331: ('TRIMMEAN', 2, 2, 0x02, 2, 'V', 'RV'), 332: ('TINV', 2, 2, 0x02, 2, 'V', 'VV'), 336: ('CONCATENATE', 0, 30, 0x04, 1, 'V', 'V'), 337: ('POWER', 2, 2, 0x02, 2, 'V', 'VV'), 342: ('RADIANS', 1, 1, 0x02, 1, 'V', 'V'), 343: ('DEGREES', 1, 1, 0x02, 1, 'V', 'V'), 344: ('SUBTOTAL', 2, 30, 0x04, 2, 'V', 'VR'), 345: ('SUMIF', 2, 3, 0x04, 3, 'V', 'RVR'), 346: ('COUNTIF', 2, 2, 0x02, 2, 'V', 'RV'), 347: ('COUNTBLANK', 1, 1, 0x02, 1, 'V', 'R'), 350: ('ISPMT', 4, 4, 0x02, 4, 'V', 'VVVV'), 351: ('DATEDIF', 3, 3, 0x02, 3, 'V', 'VVV'), 352: ('DATESTRING', 1, 1, 0x02, 1, 'V', 'V'), 353: ('NUMBERSTRING', 2, 2, 0x02, 2, 'V', 'VV'), 354: ('ROMAN', 1, 2, 0x04, 2, 'V', 'VV'), 358: ('GETPIVOTDATA', 2, 2, 0x02, 2, 'V', 'RV'), 359: ('HYPERLINK', 1, 2, 0x04, 2, 'V', 'VV'), 360: ('PHONETIC', 1, 1, 0x02, 1, 'V', 'V'), 361: ('AVERAGEA', 1, 30, 0x04, 1, 'V', 'R'), 362: ('MAXA', 1, 30, 0x04, 1, 'V', 'R'), 363: ('MINA', 1, 30, 0x04, 1, 'V', 'R'), 364: ('STDEVPA', 1, 30, 0x04, 1, 'V', 'R'), 365: ('VARPA', 1, 30, 0x04, 1, 'V', 'R'), 366: ('STDEVA', 1, 30, 0x04, 1, 'V', 'R'), 367: ('VARA', 1, 30, 0x04, 1, 'V', 'R'), 368: ('BAHTTEXT', 1, 1, 0x02, 1, 'V', 'V'), 369: ('THAIDAYOFWEEK', 1, 1, 0x02, 1, 'V', 'V'), 370: ('THAIDIGIT', 1, 1, 0x02, 1, 'V', 'V'), 371: ('THAIMONTHOFYEAR', 1, 1, 0x02, 1, 'V', 'V'), 372: ('THAINUMSOUND', 1, 1, 0x02, 1, 'V', 'V'), 373: ('THAINUMSTRING', 1, 1, 0x02, 1, 'V', 'V'), 374: ('THAISTRINGLENGTH', 1, 1, 0x02, 1, 'V', 'V'), 375: ('ISTHAIDIGIT', 1, 1, 0x02, 1, 'V', 'V'), 376: ('ROUNDBAHTDOWN', 1, 1, 0x02, 1, 'V', 'V'), 377: ('ROUNDBAHTUP', 1, 1, 0x02, 1, 'V', 'V'), 378: ('THAIYEAR', 1, 1, 0x02, 1, 'V', 'V'), 379: ('RTD', 2, 5, 0x04, 1, 'V', 'V'), } tAttrNames = { 0x00: "Skip??", # seen in SAMPLES.XLS which shipped with Excel 5.0 0x01: "Volatile", 0x02: "If", 0x04: "Choose", 0x08: "Skip", 0x10: "Sum", 0x20: "Assign", 0x40: "Space", 0x41: "SpaceVolatile", } error_opcodes = set([0x07, 0x08, 0x0A, 0x0B, 0x1C, 0x1D, 0x2F]) tRangeFuncs = (min, max, min, max, min, max) tIsectFuncs = (max, min, max, min, max, min) def do_box_funcs(box_funcs, boxa, boxb): return tuple([ func(numa, numb) for func, numa, numb in zip(box_funcs, boxa.coords, boxb.coords) ]) def adjust_cell_addr_biff8(rowval, colval, reldelta, browx=None, bcolx=None): row_rel = (colval >> 15) & 1 col_rel = (colval >> 14) & 1 rowx = rowval colx = colval & 0xff if reldelta: if row_rel and rowx >= 32768: rowx -= 65536 if col_rel and colx >= 128: colx -= 256 else: if row_rel: rowx -= browx if col_rel: colx -= bcolx return rowx, colx, row_rel, col_rel def adjust_cell_addr_biff_le7( rowval, colval, reldelta, browx=None, bcolx=None): row_rel = (rowval >> 15) & 1 col_rel = (rowval >> 14) & 1 rowx = rowval & 0x3fff colx = colval if reldelta: if row_rel and rowx >= 8192: rowx -= 16384 if col_rel and colx >= 128: colx -= 256 else: if row_rel: rowx -= browx if col_rel: colx -= bcolx return rowx, colx, row_rel, col_rel def get_cell_addr(data, pos, bv, reldelta, browx=None, bcolx=None): if bv >= 80: rowval, colval = unpack("= 80: row1val, row2val, col1val, col2val = unpack(" addins %r" % (refx, info), file=bk.logfile) assert ref_first_sheetx == 0xFFFE == ref_last_sheetx return (-5, -5) if ref_recordx != bk._supbook_locals_inx: if blah: print("/// get_externsheet_local_range(refx=%d) -> external %r" % (refx, info), file=bk.logfile) return (-4, -4) # external reference if ref_first_sheetx == 0xFFFE == ref_last_sheetx: if blah: print("/// get_externsheet_local_range(refx=%d) -> unspecified sheet %r" % (refx, info), file=bk.logfile) return (-1, -1) # internal reference, any sheet if ref_first_sheetx == 0xFFFF == ref_last_sheetx: if blah: print("/// get_externsheet_local_range(refx=%d) -> deleted sheet(s)" % (refx, ), file=bk.logfile) return (-2, -2) # internal reference, deleted sheet(s) nsheets = len(bk._all_sheets_map) if not(0 <= ref_first_sheetx <= ref_last_sheetx < nsheets): if blah: print("/// get_externsheet_local_range(refx=%d) -> %r" % (refx, info), file=bk.logfile) print("--- first/last sheet not in range(%d)" % nsheets, file=bk.logfile) return (-102, -102) # stuffed up somewhere :-( xlrd_sheetx1 = bk._all_sheets_map[ref_first_sheetx] xlrd_sheetx2 = bk._all_sheets_map[ref_last_sheetx] if not(0 <= xlrd_sheetx1 <= xlrd_sheetx2): return (-3, -3) # internal reference, but to a macro sheet return xlrd_sheetx1, xlrd_sheetx2 def get_externsheet_local_range_b57( bk, raw_extshtx, ref_first_sheetx, ref_last_sheetx, blah=0): if raw_extshtx > 0: if blah: print("/// get_externsheet_local_range_b57(raw_extshtx=%d) -> external" % raw_extshtx, file=bk.logfile) return (-4, -4) # external reference if ref_first_sheetx == -1 and ref_last_sheetx == -1: return (-2, -2) # internal reference, deleted sheet(s) nsheets = len(bk._all_sheets_map) if not(0 <= ref_first_sheetx <= ref_last_sheetx < nsheets): if blah: print("/// get_externsheet_local_range_b57(%d, %d, %d) -> ???" \ % (raw_extshtx, ref_first_sheetx, ref_last_sheetx), file=bk.logfile) print("--- first/last sheet not in range(%d)" % nsheets, file=bk.logfile) return (-103, -103) # stuffed up somewhere :-( xlrd_sheetx1 = bk._all_sheets_map[ref_first_sheetx] xlrd_sheetx2 = bk._all_sheets_map[ref_last_sheetx] if not(0 <= xlrd_sheetx1 <= xlrd_sheetx2): return (-3, -3) # internal reference, but to a macro sheet return xlrd_sheetx1, xlrd_sheetx2 class FormulaError(Exception): pass ## # Used in evaluating formulas. # The following table describes the kinds and how their values # are represented.

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    Kind symbolKind numberValue representation
    oBOOL3integer: 0 => False; 1 => True
    oERR4None, or an int error code (same as XL_CELL_ERROR in the Cell class). #
    oMSNG5Used by Excel as a placeholder for a missing (not supplied) function # argument. Should *not* appear as a final formula result. Value is None.
    oNUM2A float. Note that there is no way of distinguishing dates.
    oREF-1The value is either None or a non-empty list of # absolute Ref3D instances.
    #
    oREL-2The value is None or a non-empty list of # fully or partially relative Ref3D instances. #
    oSTRG1A Unicode string.
    oUNK0The kind is unknown or ambiguous. The value is None
    #

    class Operand(object): ## # 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) ## #

    Represents an absolute or relative 3-dimensional reference to a box # of one or more cells.
    # -- New in version 0.6.0 #

    # #

    The coords attribute is a tuple of the form:
    # (shtxlo, shtxhi, rowxlo, rowxhi, colxlo, colxhi)
    # where 0 <= thingxlo <= thingx < thingxhi.
    # Note that 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: 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 that 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 also that 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 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, ...). class Ref3D(tuple): 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) import operator as opr 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 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) 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) ## # Utility function: (5, 7) => 'H6' def cellname(rowx, colx): """ (5, 7) => 'H6' """ return "%s%d" % (colname(colx), rowx+1) ## # Utility function: (5, 7) => '$H$6' def cellnameabs(rowx, colx, r1c1=0): """ (5, 7) => '$H$6' or 'R8C6'""" 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 ## # Utility function: 7 => 'H', 27 => 'AB' def colname(colx): """ 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) ) ## # Utility function: #
    Ref3D((1, 4, 5, 20, 7, 10)) => 'Sheet2:Sheet3!$H$6:$J$20' def rangename3d(book, ref3d): """ 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])) ## # Utility function: #
    Ref3D(coords=(0, 1, -32, -22, -13, 13), relflags=(0, 0, 1, 1, 1, 1)) # R1C1 mode => 'Sheet1!R[-32]C[-13]:R[-23]C[12]' # A1 mode => depends on base cell (browx, bcolx) def rangename3drel(book, ref3d, browx=None, bcolx=None, r1c1=0): 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 "" # ============================================================== xlrd-0.9.4/xlrd/info.py0000644000076500000240000000002612551375677015226 0ustar chrisstaff00000000000000__VERSION__ = "0.9.4" xlrd-0.9.4/xlrd/licences.py0000644000076500000240000000651611650243325016051 0ustar chrisstaff00000000000000# -*- coding: cp1252 -*- """ Portions copyright 2005-2009, Stephen John Machin, Lingfo Pty Ltd All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. None of the names of Stephen John Machin, Lingfo Pty Ltd and any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ """ /*- * Copyright (c) 2001 David Giffin. * All rights reserved. * * Based on the the Java version: Andrew Khan Copyright (c) 2000. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by * David Giffin ." * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by * David Giffin ." * * THIS SOFTWARE IS PROVIDED BY DAVID GIFFIN ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID GIFFIN OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ """ xlrd-0.9.4/xlrd/sheet.py0000644000076500000240000032202412551374567015405 0ustar chrisstaff00000000000000# -*- coding: cp1252 -*- ## #

    Portions copyright 2005-2013 Stephen John Machin, Lingfo Pty Ltd

    #

    This module is part of the xlrd package, which is released under a BSD-style licence.

    ## # 2010-04-25 SJM fix zoom factors cooking logic # 2010-04-15 CW r4253 fix zoom factors cooking logic # 2010-04-09 CW r4248 add a flag so xlutils knows whether or not to write a PANE record # 2010-03-29 SJM Fixed bug in adding new empty rows in put_cell_ragged # 2010-03-28 SJM Tailored put_cell method for each of ragged_rows=False (fixed speed regression) and =True (faster) # 2010-03-25 CW r4236 Slight refactoring to remove method calls # 2010-03-25 CW r4235 Collapse expand_cells into put_cell and enhance the raggedness. This should save even more memory! # 2010-03-25 CW r4234 remove duplicate chunks for extend_cells; refactor to remove put_number_cell and put_blank_cell which essentially duplicated the code of put_cell # 2010-03-10 SJM r4222 Added reading of the PANE record. # 2010-03-10 SJM r4221 Preliminary work on "cooked" mag factors; use at own peril # 2010-03-01 SJM Reading SCL record # 2010-03-01 SJM Added ragged_rows functionality # 2009-08-23 SJM Reduced CPU time taken by parsing MULBLANK records. # 2009-08-18 SJM Used __slots__ and sharing to reduce memory consumed by Rowinfo instances # 2009-05-31 SJM Fixed problem with no CODEPAGE record on extremely minimal BIFF2.x 3rd-party file # 2009-04-27 SJM Integrated on_demand patch by Armando Serrano Lombillo # 2008-02-09 SJM Excel 2.0: build XFs on the fly from cell attributes # 2007-12-04 SJM Added support for Excel 2.x (BIFF2) files. # 2007-10-11 SJM Added missing entry for blank cell type to ctype_text # 2007-07-11 SJM Allow for BIFF2/3-style FORMAT record in BIFF4/8 file # 2007-04-22 SJM Remove experimental "trimming" facility. from __future__ import print_function from array import array from struct import unpack, calcsize from .biffh import * from .timemachine import * from .formula import dump_formula, decompile_formula, rangename2d, FMLA_TYPE_CELL, FMLA_TYPE_SHARED from .formatting import nearest_colour_index, Format 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), ) ## #

    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 {@link #Cell} class.

    # #

    WARNING: You don't call this class yourself. You access Sheet objects via the Book object that # was returned when you called xlrd.open_workbook("myfile.xls").

    class Sheet(BaseObject): ## # Name of sheet. name = '' ## # A reference to the 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 1 + the maximum column index # found, ignoring trailing empty cells. See also open_workbook(ragged_rows=?) # and Sheet.{@link #Sheet.row_len}(row_index). ncols = 0 ## # The map from a column index to a {@link #Colinfo} object. Often there is an entry # in COLINFO records for all column indexes in range(257). # Note that xlrd ignores the entry for the non-existent # 257th column. On the other hand, there may be no entry for unused columns. #
    -- New in version 0.6.1. Populated only if open_workbook(formatting_info=True). colinfo_map = {} ## # The map from a row index to a {@link #Rowinfo} object. Note that it is possible # to have missing entries -- at least one source of XLS files doesn't # bother writing ROW records. #
    -- New in version 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. #
    -- New in version 0.6.0 #
    How to deconstruct the list: #
        # 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 col_label_ranges above. #
    -- New in version 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. #
    -- New in version 0.6.1. Extracted only if open_workbook(formatting_info=True). #
    How to deconstruct the list: #
        # 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: #
        # runlist = thesheet.rich_text_runlist_map.get((rowx, colx))
        # if runlist:
        #     for offset, font_index in runlist:
        #         # do work here.
        #         pass
        # 
    # Populated only if open_workbook(formatting_info=True). #
    -- New in version 0.7.2. #
      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 {@link #Colinfo} class. #
    -- New in version 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 {@link #Colinfo} class. #
    -- New in version 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 {@link #Colinfo} class for discussion. gcw = (0, ) * 256 ## #

    A list of {@link #Hyperlink} objects corresponding to HLINK records found # in the worksheet.
    -- New in version 0.7.2

    hyperlink_list = [] ## #

    A sparse mapping from (rowx, colx) to an item in {@link #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 hyperlink_list. #
    -- New in version 0.7.2

    hyperlink_map = {} ## #

    A sparse mapping from (rowx, colx) to a {@link #Note} object. # Cells not containing a note ("comment") are not mapped. #
    -- New in version 0.7.2

    cell_note_map = {} ## # Number of columns in left pane (frozen panes; for split panes, see comments below in code) vert_split_pos = 0 ## # Number of rows in top pane (frozen panes; for split panes, see comments below 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). #
    -- New in version 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). #
    -- New in version 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 = None # from WINDOW2 record self.cached_normal_view_mag_factor = None # from WINDOW2 record self.scl_mag_factor = None # from SCL record self._ixfe = None # BIFF2 only self._cell_attr_to_xfx = {} # BIFF2.0 only #### Don't initialise this here, use class attribute initialisation. #### self.gcw = (0, ) * 256 #### 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 ## # {@link #Cell} object in the given row and column. def cell(self, rowx, colx): 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, ) ## # Value of the cell in the given row and column. def cell_value(self, rowx, colx): return self._cell_values[rowx][colx] ## # Type of the cell in the given row and column. # Refer to the documentation of the {@link #Cell} class. def cell_type(self, rowx, colx): return self._cell_types[rowx][colx] ## # XF index of the cell in the given row and column. # This is an index into Book.{@link #Book.xf_list}. #
    -- New in version 0.6.1 def cell_xf_index(self, rowx, colx): 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 ## # 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 {@link #Sheet.ncols} cells. #
    -- New in version 0.7.2 def row_len(self, rowx): return len(self._cell_values[rowx]) ## # Returns a sequence of the {@link #Cell} objects in the given row. def row(self, rowx): return [ self.cell(rowx, colx) for colx in xrange(len(self._cell_values[rowx])) ] ## # Returns a generator for iterating through each row. def get_rows(self): return (self.row(index) for index in range(self.nrows)) ## # Returns a slice of the types # of the cells in the given row. def row_types(self, rowx, start_colx=0, end_colx=None): if end_colx is None: return self._cell_types[rowx][start_colx:] return self._cell_types[rowx][start_colx:end_colx] ## # Returns a slice of the values # of the cells in the given row. def row_values(self, rowx, start_colx=0, end_colx=None): if end_colx is None: return self._cell_values[rowx][start_colx:] return self._cell_values[rowx][start_colx:end_colx] ## # Returns a slice of the {@link #Cell} objects in the given row. def row_slice(self, rowx, start_colx=0, end_colx=None): 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) ] ## # Returns a slice of the {@link #Cell} objects in the given column. def col_slice(self, colx, start_rowx=0, end_rowx=None): 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) ] ## # Returns a slice of the values of the cells in the given column. def col_values(self, colx, start_rowx=0, end_rowx=None): 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) ] ## # Returns a slice of the types of the cells in the given column. def col_types(self, colx, start_rowx=0, end_rowx=None): 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) ] ## # Returns a sequence of the {@link #Cell} objects in the given column. def col(self, colx): return self.col_slice(colx) # Above two lines just for the docs. Here's the real McCoy: 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 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, '', -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:] = [''] * 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([''] * num_empty) # if fmt_info: # fmt_row.extend(self.bf * num_empty) types_row[ltr:] = self.bt * num_empty values_row[ltr:] = [''] * 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 optmisation. 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([''] * 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([''] * 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, has_defaults, 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 zoom == 0: # 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 for attr_stem in \ "format font alignment border background protection".split(): 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)") ## # Determine column display width. #
    -- New in version 0.6.1 #
    # @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). def computed_column_width(self, colx): 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=%d c=%d has %d extra data bytes: %s\n", h.frowx, h.fcolx, 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() data_len = len(data) fmt = ' Represents a user "comment" or "note". # Note objects are accessible through Sheet.{@link #Sheet.cell_note_map}. #
    -- New in version 0.7.2 #

    class Note(BaseObject): ## # 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 Sheet.{@link #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('') ## #

    Contains the attributes of a hyperlink. # Hyperlink objects are accessible through Sheet.{@link #Sheet.hyperlink_list} # and Sheet.{@link #Sheet.hyperlink_map}. #
    -- New in version 0.7.2 #

    class Hyperlink(BaseObject): ## # 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 bytes (3.x) or 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: I have not seen a case of this. # It seems impossible to create one in the Excel UI. target = None ## # "Textmark": 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('Contains the data for one cell.

    # #

    WARNING: You don't call this class yourself. You access Cell objects # via methods of the {@link #Sheet} object(s) that you found in the {@link #Book} object that # was returned when you called xlrd.open_workbook("myfile.xls").

    #

    Cell objects have three attributes: ctype is an int, value # (which depends on ctype) and xf_index. # If "formatting_info" is not enabled when the workbook is opened, xf_index will be None. # The following table describes the types of cells and how their values # are represented in Python.

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    Type symbolType numberPython value
    XL_CELL_EMPTY0empty string u''
    XL_CELL_TEXT1a Unicode string
    XL_CELL_NUMBER2float
    XL_CELL_DATE3float
    XL_CELL_BOOLEAN4int; 1 means TRUE, 0 means FALSE
    XL_CELL_ERROR5int representing internal Excel codes; for a text representation, # refer to the supplied dictionary error_text_from_code
    XL_CELL_BLANK6empty string u''. Note: this type will appear only when # open_workbook(..., formatting_info=True) is used.
    #

    class Cell(BaseObject): __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) ## # There is one and only one instance of an empty cell -- it's a singleton. This is it. # You may use a test like "acell is empty_cell". empty_cell = Cell(XL_CELL_EMPTY, '') ##### =============== Colinfo and Rowinfo ============================== ##### ## # 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 [see footnote!]). # #
    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.""" #
    # # Footnote: 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. #
    """
    # At the moment (2007-01-17) xlrd is going with the GCW version of the story. # Reference to the source may be useful: see the computed_column_width(colx) method # of the Sheet class. #
    -- New in version 0.6.1 #

    class Colinfo(BaseObject): ## # 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 ## #

    Height and default formatting information that applies to a row in a sheet. # Derived from ROW records. #
    -- New in version 0.6.1

    # #

    height: Height of the row, in twips. One twip == 1/20 of a point.

    # #

    has_default_height: 0 = Row has custom height; 1 = Row has default height.

    # #

    outline_level: Outline level of the row (0 to 7)

    # #

    outline_group_starts_ends: 1 = Outline group starts or ends here (depending on where the # outline buttons are located, see WSBOOL record [TODO ??]), # and is collapsed

    # #

    hidden: 1 = Row is hidden (manually, or by a filter or outline group)

    # #

    height_mismatch: 1 = Row height and default font height do not match

    # #

    has_default_xf_index: 1 = the xf_index attribute is usable; 0 = ignore it

    # #

    xf_index: Index to default XF record for empty cells in this row. # Don't use this if has_default_xf_index == 0.

    # #

    additional_space_above: 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.

    # #

    additional_space_below: 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.

    class Rowinfo(BaseObject): 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): self.height = None self.has_default_height = None self.outline_level = None self.outline_group_starts_ends = None self.hidden = None self.height_mismatch = None self.has_default_xf_index = None self.xf_index = None self.additional_space_above = None 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 xlrd-0.9.4/xlrd/timemachine.py0000644000076500000240000000335512155372403016546 0ustar chrisstaff00000000000000## #

    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 xlrd-0.9.4/xlrd/xldate.py0000644000076500000240000001732712320534304015542 0ustar chrisstaff00000000000000# -*- coding: cp1252 -*- # No part of the content of this file was derived from the works of David Giffin. ## #

    Copyright 2005-2008 Stephen John Machin, Lingfo Pty Ltd

    #

    This module is part of the xlrd package, which is released under a BSD-style licence.

    # #

    Provides function(s) for dealing with Microsoft Excel dates.

    ## # 2008-10-18 SJM Fix bug in xldate_from_date_tuple (affected some years after 2099) # 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) class XLDateError(ValueError): pass class XLDateNegative(XLDateError): pass class XLDateAmbiguous(XLDateError): pass class XLDateTooLarge(XLDateError): pass class XLDateBadDatemode(XLDateError): pass class XLDateBadTuple(XLDateError): pass _XLDAYS_TOO_LARGE = (2958466, 2958466 - 1462) # This is equivalent to 10000-01-01 ## # 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. #
    WARNING: when using this function to # interpret the contents of a workbook, you should pass in the Book.datemode # attribute of that workbook. Whether # the workbook has ever been anywhere near a Macintosh is irrelevant. # @return Gregorian (year, month, day, hour, minute, nearest_second). #
    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. # @throws XLDateNegative xldate < 0.00 # @throws XLDateAmbiguous The 1900 leap-year problem (datemode == 0 and 1.0 <= xldate < 61.0) # @throws XLDateTooLarge Gregorian year 10000 or later # @throws XLDateBadDatemode datemode arg is neither 0 nor 1 # @throws XLDateError Covers the 4 specific errors def xldate_as_tuple(xldate, datemode): 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) ## # Convert an Excel date/time number into a datetime.datetime object. # # @param xldate The Excel number # @param datemode 0: 1900-based, 1: 1904-based. # # @return a datetime.datetime() object. # def xldate_as_datetime(xldate, datemode): """Convert an Excel date/time number into a 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) ## # 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. # @throws XLDateAmbiguous The 1900 leap-year problem (datemode == 0 and 1.0 <= xldate < 61.0) # @throws XLDateBadDatemode datemode arg is neither 0 nor 1 # @throws XLDateBadTuple (year, month, day) is too early/late or has invalid component(s) # @throws XLDateError Covers the specific errors def xldate_from_date_tuple(date_tuple, datemode): """Create an excel date from a tuple of (year, month, day)""" 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) ## # 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 # @throws XLDateBadTuple Out-of-range hour, minute, or second def xldate_from_time_tuple(time_tuple): """Create an excel date from a tuple of (hour, minute, 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),)) ## # 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. def xldate_from_datetime_tuple(datetime_tuple, datemode): return ( xldate_from_date_tuple(datetime_tuple[:3], datemode) + xldate_from_time_tuple(datetime_tuple[3:]) ) xlrd-0.9.4/xlrd/xlsx.py0000644000076500000240000007661512551374567015307 0ustar chrisstaff00000000000000## # Portions copyright (c) 2008-2012 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, unicode_literals DEBUG = 0 import sys import re from .timemachine import * from .book import Book, Name from .biffh import error_text_from_code, XLRDError, XL_CELL_BLANK, XL_CELL_TEXT, XL_CELL_BOOLEAN, XL_CELL_ERROR from .formatting import is_date_format_string, Format, XF from .sheet import Sheet DLF = sys.stdout # Default Log File ET = None ET_has_iterparse = False Element_has_iter = False def ensure_elementtree_imported(verbosity, logfile): global ET, ET_has_iterparse, Element_has_iter if ET is not None: return if "IronPython" in sys.version: import xml.etree.ElementTree as ET #### 2.7.2.1: fails later with #### NotImplementedError: iterparse is not supported on IronPython. (CP #31923) else: try: import xml.etree.cElementTree as ET except ImportError: try: import cElementTree as ET except ImportError: try: import lxml.etree as ET except ImportError: try: import xml.etree.ElementTree as ET except ImportError: try: import elementtree.ElementTree as ET except ImportError: raise Exception("Failed to import an ElementTree implementation") if hasattr(ET, 'iterparse'): _dummy_stream = BYTES_IO(b'') try: ET.iterparse(_dummy_stream) ET_has_iterparse = True except NotImplementedError: pass Element_has_iter = hasattr(ET.ElementTree, 'iter') if verbosity: etree_version = repr([ (item, getattr(ET, item)) for item in ET.__dict__.keys() if item.lower().replace('_', '') == 'version' ]) print(ET.__file__, ET.__name__, etree_version, ET_has_iterparse, file=logfile) def split_tag(tag): pos = tag.rfind('}') + 1 if pos >= 2: return tag[:pos], tag[pos:] return '', tag def augment_keys(adict, uri): # uri must already be enclosed in {} for x in list(adict.keys()): adict[uri + x] = adict[x] _UPPERCASE_1_REL_INDEX = {} # Used in fast conversion of column names (e.g. "XFD") to indices (16383) for _x in xrange(26): _UPPERCASE_1_REL_INDEX["ABCDEFGHIJKLMNOPQRSTUVWXYZ"[_x]] = _x + 1 for _x in "123456789": _UPPERCASE_1_REL_INDEX[_x] = 0 del _x def cell_name_to_rowx_colx(cell_name, letter_value=_UPPERCASE_1_REL_INDEX): # Extract column index from cell name # A => 0, Z =>25, AA => 26, XFD => 16383 colx = 0 charx = -1 try: for c in cell_name: charx += 1 lv = letter_value[c] if lv: colx = colx * 26 + lv else: # start of row number; can't be '0' colx = colx - 1 assert 0 <= colx < X12_MAX_COLS break except KeyError: raise Exception('Unexpected character %r in cell name %r' % (c, cell_name)) rowx = int(cell_name[charx:]) - 1 return rowx, colx error_code_from_text = {} for _code, _text in error_text_from_code.items(): error_code_from_text[_text] = _code # === X12 === Excel 2007 .xlsx =============================================== U_SSML12 = "{http://schemas.openxmlformats.org/spreadsheetml/2006/main}" U_ODREL = "{http://schemas.openxmlformats.org/officeDocument/2006/relationships}" U_PKGREL = "{http://schemas.openxmlformats.org/package/2006/relationships}" U_CP = "{http://schemas.openxmlformats.org/package/2006/metadata/core-properties}" U_DC = "{http://purl.org/dc/elements/1.1/}" U_DCTERMS = "{http://purl.org/dc/terms/}" XML_SPACE_ATTR = "{http://www.w3.org/XML/1998/namespace}space" XML_WHITESPACE = "\t\n \r" X12_MAX_ROWS = 2 ** 20 X12_MAX_COLS = 2 ** 14 V_TAG = U_SSML12 + 'v' # cell child: value F_TAG = U_SSML12 + 'f' # cell child: formula IS_TAG = U_SSML12 + 'is' # cell child: inline string def unescape(s, subber=re.compile(r'_x[0-9A-Fa-f]{4,4}_', re.UNICODE).sub, repl=lambda mobj: unichr(int(mobj.group(0)[2:6], 16)), ): if "_" in s: return subber(repl, s) return s def cooked_text(self, elem): t = elem.text if t is None: return '' if elem.get(XML_SPACE_ATTR) != 'preserve': t = t.strip(XML_WHITESPACE) return ensure_unicode(unescape(t)) def get_text_from_si_or_is(self, elem, r_tag=U_SSML12+'r', t_tag=U_SSML12 +'t'): "Returns unescaped unicode" accum = [] for child in elem: # self.dump_elem(child) tag = child.tag if tag == t_tag: t = cooked_text(self, child) if t: # note: .text attribute can be None accum.append(t) elif tag == r_tag: for tnode in child: if tnode.tag == t_tag: t = cooked_text(self, tnode) if t: accum.append(t) return ''.join(accum) def map_attributes(amap, elem, obj): for xml_attr, obj_attr, cnv_func_or_const in amap: if not xml_attr: setattr(obj, obj_attr, cnv_func_or_const) continue if not obj_attr: continue #### FIX ME #### raw_value = elem.get(xml_attr) cooked_value = cnv_func_or_const(raw_value) setattr(obj, obj_attr, cooked_value) def cnv_ST_Xstring(s): if s is None: return "" return ensure_unicode(s) def cnv_xsd_unsignedInt(s): if not s: return None value = int(s) assert value >= 0 return value def cnv_xsd_boolean(s): if not s: return 0 if s in ("1", "true", "on"): return 1 if s in ("0", "false", "off"): return 0 raise ValueError("unexpected xsd:boolean value: %r" % s) _defined_name_attribute_map = ( ("name", "name", cnv_ST_Xstring, ), ("comment", "", cnv_ST_Xstring, ), ("customMenu", "", cnv_ST_Xstring, ), ("description", "", cnv_ST_Xstring, ), ("help", "", cnv_ST_Xstring, ), ("statusBar", "", cnv_ST_Xstring, ), ("localSheetId", "scope", cnv_xsd_unsignedInt, ), ("hidden", "hidden", cnv_xsd_boolean, ), ("function", "func", cnv_xsd_boolean, ), ("vbProcedure", "vbasic", cnv_xsd_boolean, ), ("xlm", "macro", cnv_xsd_boolean, ), ("functionGroupId", "funcgroup", cnv_xsd_unsignedInt, ), ("shortcutKey", "", cnv_ST_Xstring, ), ("publishToServer", "", cnv_xsd_boolean, ), ("workbookParameter", "", cnv_xsd_boolean, ), ("", "any_err", 0, ), ("", "any_external", 0, ), ("", "any_rel", 0, ), ("", "basic_formula_len", 0, ), ("", "binary", 0, ), ("", "builtin", 0, ), ("", "complex", 0, ), ("", "evaluated", 0, ), ("", "excel_sheet_index", 0, ), ("", "excel_sheet_num", 0, ), ("", "option_flags", 0, ), ("", "result", None, ), ("", "stack", None, ), ) def make_name_access_maps(bk): name_and_scope_map = {} # (name.lower(), scope): Name_object name_map = {} # name.lower() : list of Name_objects (sorted in scope order) num_names = len(bk.name_obj_list) for namex in xrange(num_names): nobj = bk.name_obj_list[namex] name_lcase = nobj.name.lower() key = (name_lcase, nobj.scope) if key in name_and_scope_map: msg = 'Duplicate entry %r in name_and_scope_map' % (key, ) if 0: raise XLRDError(msg) else: if bk.verbosity: print(msg, file=bk.logfile) name_and_scope_map[key] = nobj sort_data = (nobj.scope, namex, nobj) if name_lcase in name_map: name_map[name_lcase].append(sort_data) else: name_map[name_lcase] = [sort_data] for key in name_map.keys(): alist = name_map[key] alist.sort() name_map[key] = [x[2] for x in alist] bk.name_and_scope_map = name_and_scope_map bk.name_map = name_map class X12General(object): def process_stream(self, stream, heading=None): if self.verbosity >= 2 and heading is not None: fprintf(self.logfile, "\n=== %s ===\n", heading) self.tree = ET.parse(stream) getmethod = self.tag2meth.get for elem in self.tree.iter() if Element_has_iter else self.tree.getiterator(): if self.verbosity >= 3: self.dump_elem(elem) meth = getmethod(elem.tag) if meth: meth(self, elem) self.finish_off() def finish_off(self): pass def dump_elem(self, elem): fprintf(self.logfile, "===\ntag=%r len=%d attrib=%r text=%r tail=%r\n", split_tag(elem.tag)[1], len(elem), elem.attrib, elem.text, elem.tail) def dumpout(self, fmt, *vargs): text = (12 * ' ' + fmt + '\n') % vargs self.logfile.write(text) class X12Book(X12General): def __init__(self, bk, logfile=DLF, verbosity=False): self.bk = bk self.logfile = logfile self.verbosity = verbosity self.bk.nsheets = 0 self.bk.props = {} self.relid2path = {} self.relid2reltype = {} self.sheet_targets = [] # indexed by sheetx self.sheetIds = [] # indexed by sheetx core_props_menu = { U_CP+"lastModifiedBy": ("last_modified_by", cnv_ST_Xstring), U_DC+"creator": ("creator", cnv_ST_Xstring), U_DCTERMS+"modified": ("modified", cnv_ST_Xstring), U_DCTERMS+"created": ("created", cnv_ST_Xstring), } def process_coreprops(self, stream): if self.verbosity >= 2: fprintf(self.logfile, "\n=== coreProps ===\n") self.tree = ET.parse(stream) getmenu = self.core_props_menu.get props = {} for elem in self.tree.iter() if Element_has_iter else self.tree.getiterator(): if self.verbosity >= 3: self.dump_elem(elem) menu = getmenu(elem.tag) if menu: attr, func = menu value = func(elem.text) props[attr] = value self.bk.user_name = props.get('last_modified_by') or props.get('creator') self.bk.props = props if self.verbosity >= 2: fprintf(self.logfile, "props: %r\n", props) self.finish_off() def process_rels(self, stream): if self.verbosity >= 2: fprintf(self.logfile, "\n=== Relationships ===\n") tree = ET.parse(stream) r_tag = U_PKGREL + 'Relationship' for elem in tree.findall(r_tag): rid = elem.get('Id') target = elem.get('Target') reltype = elem.get('Type').split('/')[-1] if self.verbosity >= 2: self.dumpout('Id=%r Type=%r Target=%r', rid, reltype, target) self.relid2reltype[rid] = reltype # self.relid2path[rid] = 'xl/' + target if target.startswith('/'): self.relid2path[rid] = target[1:] # drop the / else: self.relid2path[rid] = 'xl/' + target def do_defined_name(self, elem): #### UNDER CONSTRUCTION #### if 0 and self.verbosity >= 3: self.dump_elem(elem) nobj = Name() bk = self.bk nobj.bk = bk nobj.name_index = len(bk.name_obj_list) bk.name_obj_list.append(nobj) nobj.name = elem.get('name') nobj.raw_formula = None # compiled bytecode formula -- not in XLSX nobj.formula_text = cooked_text(self, elem) map_attributes(_defined_name_attribute_map, elem, nobj) if nobj.scope is None: nobj.scope = -1 # global if nobj.name.startswith("_xlnm."): nobj.builtin = 1 if self.verbosity >= 2: nobj.dump(header='=== Name object ===') def do_defined_names(self, elem): for child in elem: self.do_defined_name(child) make_name_access_maps(self.bk) def do_sheet(self, elem): bk = self.bk sheetx = bk.nsheets # print elem.attrib rid = elem.get(U_ODREL + 'id') sheetId = int(elem.get('sheetId')) name = unescape(ensure_unicode(elem.get('name'))) reltype = self.relid2reltype[rid] target = self.relid2path[rid] if self.verbosity >= 2: self.dumpout( 'sheetx=%d sheetId=%r rid=%r type=%r name=%r', sheetx, sheetId, rid, reltype, name) if reltype != 'worksheet': if self.verbosity >= 2: self.dumpout('Ignoring sheet of type %r (name=%r)', reltype, name) return state = elem.get('state') visibility_map = { None: 0, 'visible': 0, 'hidden': 1, 'veryHidden': 2 } bk._sheet_visibility.append(visibility_map[state]) sheet = Sheet(bk, position=None, name=name, number=sheetx) sheet.utter_max_rows = X12_MAX_ROWS sheet.utter_max_cols = X12_MAX_COLS bk._sheet_list.append(sheet) bk._sheet_names.append(name) bk.nsheets += 1 self.sheet_targets.append(target) self.sheetIds.append(sheetId) def do_workbookpr(self, elem): datemode = cnv_xsd_boolean(elem.get('date1904')) if self.verbosity >= 2: self.dumpout('datemode=%r', datemode) self.bk.datemode = datemode tag2meth = { 'definedNames': do_defined_names, 'workbookPr': do_workbookpr, 'sheet': do_sheet, } augment_keys(tag2meth, U_SSML12) class X12SST(X12General): def __init__(self, bk, logfile=DLF, verbosity=0): self.bk = bk self.logfile = logfile self.verbosity = verbosity if ET_has_iterparse: self.process_stream = self.process_stream_iterparse else: self.process_stream = self.process_stream_findall def process_stream_iterparse(self, stream, heading=None): if self.verbosity >= 2 and heading is not None: fprintf(self.logfile, "\n=== %s ===\n", heading) si_tag = U_SSML12 + 'si' elemno = -1 sst = self.bk._sharedstrings for event, elem in ET.iterparse(stream): if elem.tag != si_tag: continue elemno = elemno + 1 if self.verbosity >= 3: fprintf(self.logfile, "element #%d\n", elemno) self.dump_elem(elem) result = get_text_from_si_or_is(self, elem) sst.append(result) elem.clear() # destroy all child elements if self.verbosity >= 2: self.dumpout('Entries in SST: %d', len(sst)) if self.verbosity >= 3: for x, s in enumerate(sst): fprintf(self.logfile, "SST x=%d s=%r\n", x, s) def process_stream_findall(self, stream, heading=None): if self.verbosity >= 2 and heading is not None: fprintf(self.logfile, "\n=== %s ===\n", heading) self.tree = ET.parse(stream) si_tag = U_SSML12 + 'si' elemno = -1 sst = self.bk._sharedstrings for elem in self.tree.findall(si_tag): elemno = elemno + 1 if self.verbosity >= 3: fprintf(self.logfile, "element #%d\n", elemno) self.dump_elem(elem) result = get_text_from_si_or_is(self, elem) sst.append(result) if self.verbosity >= 2: self.dumpout('Entries in SST: %d', len(sst)) class X12Styles(X12General): def __init__(self, bk, logfile=DLF, verbosity=0): self.bk = bk self.logfile = logfile self.verbosity = verbosity self.xf_counts = [0, 0] self.xf_type = None self.fmt_is_date = {} for x in list(range(14, 23)) + list(range(45, 48)): #### hard-coding FIX ME #### self.fmt_is_date[x] = 1 # dummy entry for XF 0 in case no Styles section self.bk._xf_index_to_xl_type_map[0] = 2 # fill_in_standard_formats(bk) #### pre-integration kludge def do_cellstylexfs(self, elem): self.xf_type = 0 def do_cellxfs(self, elem): self.xf_type = 1 def do_numfmt(self, elem): formatCode = ensure_unicode(elem.get('formatCode')) numFmtId = int(elem.get('numFmtId')) is_date = is_date_format_string(self.bk, formatCode) self.fmt_is_date[numFmtId] = is_date fmt_obj = Format(numFmtId, is_date + 2, formatCode) self.bk.format_map[numFmtId] = fmt_obj if self.verbosity >= 3: self.dumpout('numFmtId=%d formatCode=%r is_date=%d', numFmtId, formatCode, is_date) def do_xf(self, elem): if self.xf_type != 1: #### ignoring style XFs for the moment return xfx = self.xf_counts[self.xf_type] self.xf_counts[self.xf_type] = xfx + 1 xf = XF() self.bk.xf_list.append(xf) self.bk.xfcount += 1 numFmtId = int(elem.get('numFmtId', '0')) xf.format_key = numFmtId is_date = self.fmt_is_date.get(numFmtId, 0) self.bk._xf_index_to_xl_type_map[xfx] = is_date + 2 if self.verbosity >= 3: self.dumpout( 'xfx=%d numFmtId=%d', xfx, numFmtId, ) self.dumpout(repr(self.bk._xf_index_to_xl_type_map)) tag2meth = { 'cellStyleXfs': do_cellstylexfs, 'cellXfs': do_cellxfs, 'numFmt': do_numfmt, 'xf': do_xf, } augment_keys(tag2meth, U_SSML12) class X12Sheet(X12General): def __init__(self, sheet, logfile=DLF, verbosity=0): self.sheet = sheet self.logfile = logfile self.verbosity = verbosity self.rowx = -1 # We may need to count them. self.bk = sheet.book self.sst = self.bk._sharedstrings self.merged_cells = sheet.merged_cells self.warned_no_cell_name = 0 self.warned_no_row_num = 0 if ET_has_iterparse: self.process_stream = self.own_process_stream def own_process_stream(self, stream, heading=None): if self.verbosity >= 2 and heading is not None: fprintf(self.logfile, "\n=== %s ===\n", heading) getmethod = self.tag2meth.get row_tag = U_SSML12 + "row" self_do_row = self.do_row for event, elem in ET.iterparse(stream): if elem.tag == row_tag: self_do_row(elem) elem.clear() # destroy all child elements (cells) elif elem.tag == U_SSML12 + "dimension": self.do_dimension(elem) elif elem.tag == U_SSML12 + "mergeCell": self.do_merge_cell(elem) self.finish_off() def process_comments_stream(self, stream): root = ET.parse(stream).getroot() author_list = root[0] assert author_list.tag == U_SSML12 + 'authors' authors = [elem.text for elem in author_list] comment_list = root[1] assert comment_list.tag == U_SSML12 + 'commentList' cell_note_map = self.sheet.cell_note_map from .sheet import Note text_tag = U_SSML12 + 'text' r_tag = U_SSML12 + 'r' t_tag = U_SSML12 + 't' for elem in comment_list.findall(U_SSML12 + 'comment'): ts = elem.findall('./' + text_tag + '/' + t_tag) ts += elem.findall('./' + text_tag + '/' + r_tag + '/' + t_tag) ref = elem.get('ref') note = Note() note.author = authors[int(elem.get('authorId'))] note.rowx, note.colx = coords = cell_name_to_rowx_colx(ref) note.text = '' for t in ts: note.text += cooked_text(self, t) cell_note_map[coords] = note def do_dimension(self, elem): ref = elem.get('ref') # example: "A1:Z99" or just "A1" if ref: # print >> self.logfile, "dimension: ref=%r" % ref last_cell_ref = ref.split(':')[-1] # example: "Z99" rowx, colx = cell_name_to_rowx_colx(last_cell_ref) self.sheet._dimnrows = rowx + 1 self.sheet._dimncols = colx + 1 def do_merge_cell(self, elem): # The ref attribute should be a cell range like "B1:D5". ref = elem.get('ref') if ref: first_cell_ref, last_cell_ref = ref.split(':') first_rowx, first_colx = cell_name_to_rowx_colx(first_cell_ref) last_rowx, last_colx = cell_name_to_rowx_colx(last_cell_ref) self.merged_cells.append((first_rowx, last_rowx + 1, first_colx, last_colx + 1)) def do_row(self, row_elem): def bad_child_tag(child_tag): raise Exception('cell type %s has unexpected child <%s> at rowx=%r colx=%r' % (cell_type, child_tag, rowx, colx)) row_number = row_elem.get('r') if row_number is None: # Yes, it's optional. self.rowx += 1 explicit_row_number = 0 if self.verbosity and not self.warned_no_row_num: self.dumpout("no row number; assuming rowx=%d", self.rowx) self.warned_no_row_num = 1 else: self.rowx = int(row_number) - 1 explicit_row_number = 1 assert 0 <= self.rowx < X12_MAX_ROWS rowx = self.rowx colx = -1 if self.verbosity >= 3: self.dumpout(" row_number=%r rowx=%d explicit=%d", row_number, self.rowx, explicit_row_number) letter_value = _UPPERCASE_1_REL_INDEX for cell_elem in row_elem: cell_name = cell_elem.get('r') if cell_name is None: # Yes, it's optional. colx += 1 if self.verbosity and not self.warned_no_cell_name: self.dumpout("no cellname; assuming rowx=%d colx=%d", rowx, colx) self.warned_no_cell_name = 1 else: # Extract column index from cell name # A => 0, Z =>25, AA => 26, XFD => 16383 colx = 0 charx = -1 try: for c in cell_name: charx += 1 if c == '$': continue lv = letter_value[c] if lv: colx = colx * 26 + lv else: # start of row number; can't be '0' colx = colx - 1 assert 0 <= colx < X12_MAX_COLS break except KeyError: raise Exception('Unexpected character %r in cell name %r' % (c, cell_name)) if explicit_row_number and cell_name[charx:] != row_number: raise Exception('cell name %r but row number is %r' % (cell_name, row_number)) xf_index = int(cell_elem.get('s', '0')) cell_type = cell_elem.get('t', 'n') tvalue = None formula = None if cell_type == 'n': # n = number. Most frequent type. # child contains plain text which can go straight into float() # OR there's no text in which case it's a BLANK cell for child in cell_elem: child_tag = child.tag if child_tag == V_TAG: tvalue = child.text elif child_tag == F_TAG: formula = cooked_text(self, child) else: raise Exception('unexpected tag %r' % child_tag) if not tvalue: if self.bk.formatting_info: self.sheet.put_cell(rowx, colx, XL_CELL_BLANK, '', xf_index) else: self.sheet.put_cell(rowx, colx, None, float(tvalue), xf_index) elif cell_type == "s": # s = index into shared string table. 2nd most frequent type # child contains plain text which can go straight into int() for child in cell_elem: child_tag = child.tag if child_tag == V_TAG: tvalue = child.text elif child_tag == F_TAG: # formula not expected here, but gnumeric does it. formula = child.text else: bad_child_tag(child_tag) if not tvalue: # if self.bk.formatting_info: self.sheet.put_cell(rowx, colx, XL_CELL_BLANK, '', xf_index) else: value = self.sst[int(tvalue)] self.sheet.put_cell(rowx, colx, XL_CELL_TEXT, value, xf_index) elif cell_type == "str": # str = string result from formula. # Should have (formula) child; however in one file, all text cells are str with no formula. # child can contain escapes for child in cell_elem: child_tag = child.tag if child_tag == V_TAG: tvalue = cooked_text(self, child) elif child_tag == F_TAG: formula = cooked_text(self, child) else: bad_child_tag(child_tag) # assert tvalue is not None and formula is not None # Yuk. Fails with file created by gnumeric -- no tvalue! self.sheet.put_cell(rowx, colx, XL_CELL_TEXT, tvalue, xf_index) elif cell_type == "b": # b = boolean # child contains "0" or "1" # Maybe the data should be converted with cnv_xsd_boolean; # ECMA standard is silent; Excel 2007 writes 0 or 1 for child in cell_elem: child_tag = child.tag if child_tag == V_TAG: tvalue = child.text elif child_tag == F_TAG: formula = cooked_text(self, child) else: bad_child_tag(child_tag) self.sheet.put_cell(rowx, colx, XL_CELL_BOOLEAN, int(tvalue), xf_index) elif cell_type == "e": # e = error # child contains e.g. "#REF!" for child in cell_elem: child_tag = child.tag if child_tag == V_TAG: tvalue = child.text elif child_tag == F_TAG: formula = cooked_text(self, child) else: bad_child_tag(child_tag) value = error_code_from_text[tvalue] self.sheet.put_cell(rowx, colx, XL_CELL_ERROR, value, xf_index) elif cell_type == "inlineStr": # Not expected in files produced by Excel. # Only possible child is . # It's a way of allowing 3rd party s/w to write text (including rich text) cells # without having to build a shared string table for child in cell_elem: child_tag = child.tag if child_tag == IS_TAG: tvalue = get_text_from_si_or_is(self, child) else: bad_child_tag(child_tag) assert tvalue is not None self.sheet.put_cell(rowx, colx, XL_CELL_TEXT, tvalue, xf_index) else: raise Exception("Unknown cell type %r in rowx=%d colx=%d" % (cell_type, rowx, colx)) tag2meth = { 'row': do_row, } augment_keys(tag2meth, U_SSML12) def open_workbook_2007_xml( zf, component_names, logfile=sys.stdout, verbosity=0, use_mmap=0, formatting_info=0, on_demand=0, ragged_rows=0, ): ensure_elementtree_imported(verbosity, logfile) bk = Book() bk.logfile = logfile bk.verbosity = verbosity bk.formatting_info = formatting_info if formatting_info: raise NotImplementedError("formatting_info=True not yet implemented") bk.use_mmap = False #### Not supported initially bk.on_demand = on_demand if on_demand: if verbosity: print("WARNING *** on_demand=True not yet implemented; falling back to False", file=bk.logfile) bk.on_demand = False bk.ragged_rows = ragged_rows x12book = X12Book(bk, logfile, verbosity) zflo = zf.open(component_names['xl/_rels/workbook.xml.rels']) x12book.process_rels(zflo) del zflo zflo = zf.open(component_names['xl/workbook.xml']) x12book.process_stream(zflo, 'Workbook') del zflo props_name = 'docprops/core.xml' if props_name in component_names: zflo = zf.open(component_names[props_name]) x12book.process_coreprops(zflo) x12sty = X12Styles(bk, logfile, verbosity) if 'xl/styles.xml' in component_names: zflo = zf.open(component_names['xl/styles.xml']) x12sty.process_stream(zflo, 'styles') del zflo else: # seen in MS sample file MergedCells.xlsx pass sst_fname = 'xl/sharedstrings.xml' x12sst = X12SST(bk, logfile, verbosity) if sst_fname in component_names: zflo = zf.open(component_names[sst_fname]) x12sst.process_stream(zflo, 'SST') del zflo for sheetx in range(bk.nsheets): fname = x12book.sheet_targets[sheetx] zflo = zf.open(component_names[fname]) sheet = bk._sheet_list[sheetx] x12sheet = X12Sheet(sheet, logfile, verbosity) heading = "Sheet %r (sheetx=%d) from %r" % (sheet.name, sheetx, fname) x12sheet.process_stream(zflo, heading) del zflo comments_fname = 'xl/comments%d.xml' % (sheetx + 1) if comments_fname in component_names: comments_stream = zf.open(component_names[comments_fname]) x12sheet.process_comments_stream(comments_stream) del comments_stream sheet.tidy_dimensions() return bk