xlrd-1.1.0/0000755000175000017500000000000013146745735013343 5ustar travistravis00000000000000xlrd-1.1.0/docs/0000755000175000017500000000000013146745735014273 5ustar travistravis00000000000000xlrd-1.1.0/docs/Makefile0000644000175000017500000001144713146745673015743 0ustar travistravis00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xlrd-1.1.0/docs/acknowledgements.rst0000644000175000017500000000153313146745673020362 0ustar travistravis00000000000000Acknowledgements ================ Development of this package 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. __ http://sc.openoffice.org/excelfileformat.pdf __ http://sc.openoffice.org/excelfileformat.odt Backporting to Python 2.1 was partially funded by `Journyx - provider of timesheet and project accounting solutions`__. __ http://journyx.com/ Provision of formatting information in version 0.6.1 was funded by `Simplistix Ltd`__. __ http://www.simplistix.co.uk xlrd-1.1.0/docs/api.rst0000644000175000017500000000156113146745673015602 0ustar travistravis00000000000000API Reference ============= xlrd ---- .. automodule:: xlrd :members: xlrd.biffh ---------- .. automodule:: xlrd.biffh :members: xlrd.book --------- .. automodule:: xlrd.book :members: xlrd.compdoc ------------ .. automodule:: xlrd.compdoc :members: xlrd.formatting --------------- .. automodule:: xlrd.formatting :members: xlrd.formula ------------- .. automodule:: xlrd.formula :members: xlrd.sheet ---------- .. currentmodule:: xlrd.sheet .. autoclass:: xlrd.sheet.Sheet :members: :exclude-members: gcw, col .. method:: col(colx) Returns a sequence of the :class:`Cell` objects in the given column. .. autoattribute:: xlrd.sheet.Sheet.gcw :annotation: .. automodule:: xlrd.sheet :members: :exclude-members: Sheet xlrd.xldate ----------- .. currentmodule:: xlrd.xldate .. automodule:: xlrd.xldate :members: xlrd-1.1.0/docs/changes.rst0000644000175000017500000004233513146745673016445 0ustar travistravis00000000000000Changes ======= .. currentmodule:: xlrd 1.1.0 (22 August 2017) ---------------------- - Fix for parsing of merged cells containing a single cell reference in xlsx files. - Fix for "invalid literal for int() with base 10: 'true'" when reading some xlsx files. - Make xldate_as_datetime available to import direct from xlrd. - Build universal wheels. - Sphinx documentation. - Document the problem with XML vulnerabilities in xlsx files and mitigation measures. - Fix :class:`NameError` on ``has_defaults is not defined``. - Some whitespace and code style tweaks. - Make example in README compatible with both Python 2 and 3. - Add default value for cells containing errors that causeed parsing of some xlsx files to fail. - Add Python 3.6 to the list of supported Python versions, drop 3.3 and 2.6. - Use generator expressions to avoid unnecessary lists in memory. - Document unicode encoding used in Excel files from Excel 97 onwards. - Report hyperlink errors in R1C1 syntax. Thanks to the following for their contributions to this release: - icereval@gmail.com - Daniel Rech - Ville Skyttä - Yegor Yefremov - Maxime Lorant - Alexandr N Zamaraev - Zhaorong Ma - Jon Dufresne - Chris McIntyre - coltleese@gmail.com - Ivan Masá 1.0.0 (2 June 2016) ------------------- - Official support, such as it is, is now for 2.6, 2.7, 3.3+ - Fixes a bug in looking up non-lowercase sheet filenames by ensuring that the sheet targets are transformed the same way as the component_names dict keys. - Fixes a bug for ``ragged_rows=False`` when merged cells increases the number of columns in the sheet. This requires all rows to be extended to ensure equal row lengths that match the number of columns in the sheet. - Fixes to enable reading of SAP-generated .xls files. - support BIFF4 files with missing FORMAT records. - support files with missing WINDOW2 record. - Empty cells are now always unicode strings, they were a bytestring on Python 2 and a unicode string on Python 3. - Fix for ```` ``inlineStr`` attribute without ```` child. - Fix for a zoom of ``None`` causing problems on Python 3. - Fix parsing of bad dimensions. - Fix xlsx sheet to comments relationship. Thanks to the following for their contributions to this release: - Lars-Erik Hannelius - Deshi Xiao - Stratos Moro - Volker Diels-Grabsch - John McNamara - Ville Skyttä - Patrick Fuller - Dragon Dave McKee - Gunnlaugur Þór Briem 0.9.4 (14 July 2015) -------------------- - Automated tests are now run on Python 3.4 - Use ``ElementTree.iter()`` if available, instead of the deprecated ``getiterator()`` when parsing xlsx files. - Fix #106 : Exception Value: unorderable types: Name() < Name() - Create row generator expression with Sheet.get_rows() - Fix for forward slash file separator and lowercase names within xlsx internals. Thanks to the following for their contributions to this release: - Corey Farwell - Jonathan Kamens - Deepak N - Brandon R. Stoner - John McNamara 0.9.3 (8 Apr 2014) ------------------ - Github issue #49 - Github issue #64 - skip meaningless chunk of 4 zero bytes between two otherwise-valid BIFF records - Github issue #61 - fix updating of escapement attribute of Font objects read from workbooks. - Implemented ``Sheet.visibility`` for xlsx files - Ignore anchors (``$``) in cell references - Dropped support for Python 2.5 and earlier, Python 2.6 is now the earliest Python release supported - Read xlsx merged cell elements. - Read cell comments in .xlsx files. - Added xldate_as_datetime() function to convert from Excel serial date/time to datetime.datetime object. Thanks to the following for their contributions to this release: - John Machin - Caleb Epstein - Martin Panter - John McNamara - Gunnlaugur Þór Briem - Stephen Lewis 0.9.2 (9 Apr 2013) ------------------ - Fix some packaging issues that meant docs and examples were missing from the tarball. - Fixed a small but serious regression that caused problems opening .xlsx files. 0.9.1 (5 Apr 2013) ------------------ - Many fixes bugs in Python 3 support. - Fix bug where ragged rows needed fixing when formatting info was being parsed. - Improved handling of aberrant Excel 4.0 Worksheet files. - Various bug fixes. - Simplify a lot of the distribution packaging. - Remove unused and duplicate imports. Thanks to the following for their contributions to this release: - Thomas Kluyver 0.9.0 (31 Jan 2013) ------------------- - Support for Python 3.2+ - Many new unit test added. - Continuous integration tests are now run. - Various bug fixes. Special thanks to Thomas Kluyver and Martin Panter for their work on Python 3 compatibility. Thanks to Manfred Moitzi for re-licensing his unit tests so we could include them. Thanks to the following for their contributions to this release: - "holm" - Victor Safronovich - Ross Jones 0.8.0 (22 Aug 2012) ------------------- - More work-arounds for broken source files. - Support for reading .xlsx files. - Drop support for Python 2.5 and older. 0.7.8 (7 June 2012) ------------------- - Ignore superfluous zero bytes at end of xls OBJECT record. - Fix assertion error when reading file with xlwt-written bitmap. 0.7.7 (13 Apr 2012) ------------------- - More packaging changes, this time to support 2to3. 0.7.6 (3 Apr 2012) ------------------ - Fix more packaging issues. 0.7.5 (3 Apr 2012) ------------------ - Fix packaging issue that missed ``version.txt`` from the distributions. 0.7.4 (2 Apr 2012) ------------------ - More tolerance of out-of-spec files. - Fix bugs reading long text formula results. 0.7.3 (28 Feb 2012) ------------------- - Packaging and documentation updates. 0.7.2 (21 Feb 2012) ------------------- - Tolerant handling of files with extra zero bytes at end of NUMBER record. Sample provided by Jan Kraus. - Added access to cell notes/comments. Many cross-references added to Sheet class docs. - Added code to extract hyperlink (HLINK) records. Based on a patch supplied by John Morrisey. - Extraction of rich text formatting info based on code supplied by Nathan van Gheem. - added handling of BIFF2 WINDOW2 record. - Included modified version of page breaks patch from Sam Listopad. - Added reading of the PANE record. - Reading SCL record. New attribute ``Sheet.scl_mag_factor``. - Lots of bug fixes. - Added ``ragged_rows`` functionality. 0.7.1 (31 May 2009) ------------------- - Backed out "slash'n'burn" of sheet resources in unload_sheet(). Fixed problem with STYLE records on some Mac Excel files. - quieten warnings - Integrated on_demand patch by Armando Serrano Lombillo 0.7.0 (11 March 2009) --------------------- + colname utility function now supports more than 256 columns. + Fix bug where BIFF record type 0x806 was being regarded as a formula opcode. + Ignore PALETTE record when formatting_info is false. + Tolerate up to 4 bytes trailing junk on PALETTE record. + Fixed bug in unused utility function xldate_from_date_tuple which affected some years after 2099. + Added code for inspecting as-yet-unused record types: FILEPASS, TXO, NOTE. + Added inspection code for add_in function calls. + Added support for unnumbered biff_dump (better for doing diffs). + ignore distutils cruft + Avoid assertion error in compdoc when -1 used instead of -2 for first_SID of empty SCSS + Make version numbers match up. + Enhanced recovery from out-of-order/missing/wrong CODEPAGE record. + Added Name.area2d convenience method. + Avoided some checking of XF info when formatting_info is false. + Minor changes in preparation for XLSX support. + remove duplicate files that were out of date. + Basic support for Excel 2.0 + Decouple Book init & load. + runxlrd: minor fix for xfc. + More Excel 2.x work. + is_date_format() tweak. + Better detection of IronPython. + Better error message (including first 8 bytes of file) when file is not in a supported format. + More BIFF2 formatting: ROW, COLWIDTH, and COLUMNDEFAULT records; + finished stage 1 of XF records. + More work on supporting BIFF2 (Excel 2.x) files. + Added support for Excel 2.x (BIFF2) files. Data only, no formatting info. Alpha. + Wasn't coping with EXTERNSHEET record followed by CONTINUE record(s). + Allow for BIFF2/3-style FORMAT record in BIFF4/8 file + Avoid crash when zero-length Unicode string missing options byte. + Warning message if sector sizes are extremely large. + Work around corrupt STYLE record + Added missing entry for blank cell type to ctype_text + Added "fonts" command to runxlrd script + Warning: style XF whose parent XF index != 0xFFF + Logfile arg wasn't being passed from open_workbook to compdoc.CompDoc. 0.6.1 (10 June 2007) --------------------- + Version number updated to 0.6.1 + Documented runxlrd.py commands in its usage message. Changed commands: dump to biff_dump, count_records to biff_count. 0.6.1a5 ------- + Bug fixed: Missing "<" in a struct.unpack call means can't open files on bigendian platforms. Discovered by "Mihalis". + Removed antique undocumented Book.get_name_dict method and experimental "trimming" facility. + Meaningful exception instead of IndexError if a SAT (sector allocation table) is corrupted. + If no CODEPAGE record in pre-8.0 file, assume ascii and keep going (instead of raising exception). 0.6.1a4 ------- + At least one source of XLS files writes parent style XF records *after* the child cell XF records that refer to them, triggering IndexError in 0.5.2 and AssertionError in later versions. Reported with sample file by Todd O'Bryan. Fixed by changing to two-pass processing of XF records. + Formatting info in pre-BIFF8 files: Ensured appropriate defaults and lossless conversions to make the info BIFF8-compatible. Fixed bug in extracting the "used" flags. + Fixed problems discovered with opening test files from Planmaker 2006 (http://www.softmaker.com/english/ofwcomp_en.htm): (1) Four files have reduced size of PALETTE record (51 and 32 colours; Excel writes 56 always). xlrd now emits a NOTE to the logfile and continues. (2) FORMULA records use the Excel 2.x record code 0x0021 instead of 0x0221. xlrd now continues silently. (3) In two files, at the OLE2 compound document level, the internal directory says that the length of the Short-Stream Container Stream is 16384 bytes, but the actual contents are 11264 and 9728 bytes respectively. xlrd now emits a WARNING to the logfile and continues. + After discussion with Daniel Rentz, the concept of two lists of XF (eXtended Format) objects (raw_xf_list and computed_xf_list) has been abandoned. There is now a single list, called xf_list 0.6.1a3 ------- + Added Book.sheets ... for sheetx, sheet in enumerate(book.sheets): + Formatting info: extraction of sheet-level flags from WINDOW2 record, and sheet.visibility from BOUNDSHEET record. Added Macintosh- only Font attributes "outline" and "shadow'. 0.6.1a2 ------- + Added extraction of merged cells info. + pyExcelerator uses "general" instead of "General" for the generic "number format". Worked around. + Crystal Reports writes "WORKBOOK" in the OLE2 Compound Document directory instead of "Workbook". Changed to case-insensitive directory search. Reported by Vic Simkus. 0.6.1a1 (18 Dec 2006) --------------------- + Added formatting information for cells (font, "number format", background, border, alignment and protection) and rows/columns (height/width etc). To save memory and time for those who don't need it, this information is extracted only if formatting_info=1 is supplied to the open_workbook() function. The cell records BLANK and MULBLANKS which contain no data, only formatting information, will continue to be ignored in the default (no formatting info) case. + Ralph Heimburger reported a problem with xlrd being intolerant about an Excel 4.0 file (created by "some web app") with a DIMENSIONS record that omitted Microsoft's usual padding with 2 unused bytes. Fixed. 0.6.0a4 (not released) ---------------------- + Added extraction of human-readable formulas from NAME records. + Worked around OOo Calc writing 9-byte BOOLERR records instead of 8. Reported by Rory Campbell-Lange. + This history file converted to descending chronological order and HTML format. 0.6.0a3 (19 Sept 2006) ---------------------- + Names: minor bugfixes; added script xlrdnameAPIdemo.py + ROW records were being used as additional hints for sizing memory requirements. In some files the ROW records overstate the number of used columns, and/or there are ROW records for rows that have no data in them. This would cause xlrd to report sheet.ncols and/or sheet.nrows as larger than reasonably expected. Change: ROW records are ignored. The number of columns/rows is based solely on the highest column/row index seen in non-empty data records. Empty data records (types BLANK and MULBLANKS) which contain no data, only formatting information, have always been ignored, and this will continue. Consequence: trailing rows and columns which contain only empty cells will vanish. 0.6.0a2 (13 Sept 2006) ---------------------- + Fixed a bug reported by Rory Campbell-Lange.: "open failed"; incorrect assumptions about the layout of array formulas which return strings. + Further work on defined names, especially the API. 0.6.0a1 (8 Sept 2006) --------------------- + Sheet objects have two new convenience methods: col_values(colx, start_rowx=0, end_rowx=None) and the corresponding col_types. Suggested by Dennis O'Brien. + BIFF 8 file missing its CODEPAGE record: xlrd will now assume utf_16_le encoding (the only possibility) and keep going. + Older files missing a CODEPAGE record: an exception will be raised. Thanks to Sergey Krushinsky for a sample file. The open_workbook() function has a new argument (encoding_override) which can be used if the CODEPAGE record is missing or incorrect (for example, codepage=1251 but the data is actually encoded in koi8_r). The runxlrd.py script takes a corresponding -e argument, for example -e cp1251 + Further work done on parsing "number formats". Thanks to Chris Withers for the ``"General_)"`` example. + Excel 97 introduced the concept of row and column labels, defined by Insert > Name > Labels. The ranges containing the labels are now exposed as the Sheet attributes row_label_ranges and col_label_ranges. + The major effort in this 0.6.0 release has been the provision of access to named cell ranges and named constants (Excel: Insert/Name/Define). Juan C. Mendez provided very useful real-world sample files. 0.5.3a1 (24 May 2006) --------------------- + John Popplewell and Richard Sharp provided sample files which caused any reliance at all on DIMENSIONS records and ROW records to be abandoned. + If the file size is not a whole number of OLE sectors, a warning message is logged. Previously this caused an exception to be raised. 0.5.2 (14 March 2006) --------------------- + public release + Updated version numbers, README, HISTORY. 0.5.2a3 (13 March 2006) ----------------------- + Gnumeric writes user-defined formats with format codes starting at 50 instead of 164; worked around. + Thanks to Didrik Pinte for reporting the need for xlrd to be more tolerant of the idiosyncracies of other software, for supplying sample files, and for performing alpha testing. + '_' character in a format should be treated like an escape character; fixed. + An "empty" formula result means a zero-length string, not an empty cell! Fixed. 0.5.2a2 (9 March 2006) ---------------------- + Found that Gnumeric writes all DIMENSIONS records with nrows and ncols each 1 less than they should be (except when it clamps ncols at 256!), and pyXLwriter doesn't write ROW records. Cell memory pre- allocation was generalised to use ROW records if available with fall- back to DIMENSIONS records. 0.5.2a1 (6 March 2006) ---------------------- + pyXLwriter writes DIMENSIONS record with antique opcode 0x0000 instead of 0x0200; worked around + A file written by Gnumeric had zeroes in DIMENSIONS record but data in cell A1; worked around 0.5.1 (18 Feb 2006) -------------------- + released to Journyx + Python 2.1 mmap requires file to be opened for update access. Added fall-back to read-only access without mmap if 2.1 open fails because "permission denied". 0.5 (7 Feb 2006) ---------------- + released to Journyx + Now works with Python 2.1. Backporting to Python 2.1 was partially funded by Journyx - provider of timesheet and project accounting solutions (http://journyx.com/) + open_workbook() can be given the contents of a file instead of its name. Thanks to Remco Boerma for the suggestion. + New module attribute __VERSION__ (as a string; for example "0.5") + Minor enhancements to classification of formats as date or not-date. + Added warnings about files with inconsistent OLE compound document structures. Thanks to Roman V. Kiseliov (author of pyExcelerator) for the tip-off. 0.4a1, (7 Sept 2005) -------------------- + released to Laurent T. + Book and sheet objects can now be pickled and unpickled. Instead of reading a large spreadsheet multiple times, consider pickling it once and loading the saved pickle; can be much faster. Thanks to Laurent Thioudellet for the enhancement request. + Using the mmap module can be turned off. But you would only do that for benchmarking purposes. + Handling NUMBER records has been made faster 0.3a1 (15 May 2005) ------------------- - first public release xlrd-1.1.0/docs/conf.py0000644000175000017500000000134513146745673015576 0ustar travistravis00000000000000import datetime import os, pkginfo on_rtd = os.environ.get('READTHEDOCS', None) == 'True' pkg_info = pkginfo.Develop(os.path.join(os.path.dirname(__file__), os.pardir)) intersphinx_mapping = {'http://docs.python.org': None} extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx'] source_suffix = '.rst' master_doc = 'index' project = u'xlrd' copyright = '2005-%s Stephen John Machin, Lingfo Pty Ltd' % datetime.datetime.now().year version = release = pkg_info.version exclude_patterns = ['_build'] pygments_style = 'sphinx' if on_rtd: html_theme = 'default' else: html_theme = 'classic' htmlhelp_basename = project+'doc' intersphinx_mapping = {'python': ('http://docs.python.org', None)} autodoc_member_order = 'bysource' xlrd-1.1.0/docs/dates.rst0000644000175000017500000000762513146745673016140 0ustar travistravis00000000000000Dates in Excel spreadsheets =========================== .. currentmodule:: xlrd.xldate 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: - the "number format" applied to them in Excel and/or - knowing which cells are supposed to have dates in them. This module helps with the former 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 package's :func:`xldate_as_tuple` function to convert numbers from a workbook, you must use the :attr:`~xlrd.Book.datemode` attribute of the :class:`~xlrd.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: https://support.microsoft.com/en-us/help/180162/xl-the-1900-date-system-vs.-the-1904-date-system 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. For 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 and you'll see ``1900-02-28`` displayed. Reference: https://support.microsoft.com/en-us/help/214326/excel-incorrectly-assumes-that-the-year-1900-is-a-leap-year 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 functions in :mod:`~xlrd.xldate` take the view that such a number is a calendar-independent time of day (like Python's :class:`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 functions in :mod:`~xlrd.xldate`. xlrd-1.1.0/docs/development.rst0000644000175000017500000000324413146745673017353 0ustar travistravis00000000000000Development =========== .. highlight:: bash This package is developed using continuous integration which can be found here: https://travis-ci.org/python-excel/xlrd If you wish to contribute to this project, then you should fork the repository found here: https://github.com/python-excel/xlrd Once that has been done and you have a checkout, you can follow these instructions to perform various development tasks: Setting up a virtualenv ----------------------- The recommended way to set up a development environment is to turn your checkout into a virtualenv and then install the package in editable form as follows:: $ virtualenv . $ bin/pip install -Ur requirements.txt $ bin/pip install -e . Running the tests ----------------- Once you've set up a virtualenv, the tests can be run as follows:: $ bin/nosetests To run tests on all the versions of Python that are supported, you can do:: $ bin/tox If you change the supported python versions in ``.travis.yml``, please remember to do the following to update ``tox.ini``:: $ bin/panci --to=tox .travis.yml > tox.ini Building the documentation -------------------------- The Sphinx documentation is built by doing the following, having activated the virtualenv above, from the directory containing setup.py:: $ cd docs $ make html Making a release ---------------- To make a release, just update the version in ``xlrd.info.__VERSION__``, update the change log, tag it, push to https://github.com/python-excel/xlrd and Travis CI should take care of the rest. Once the above is done, make sure to go to https://readthedocs.org/projects/xlrd/versions/ and make sure the new release is marked as an Active Version. xlrd-1.1.0/docs/formatting.rst0000644000175000017500000001053113146745673017200 0ustar travistravis00000000000000Formatting information in Excel Spreadsheets ============================================ Introduction ------------ This collection of features, new in xlrd version 0.6.1, is intended to provide the information needed to: - display/render spreadsheet contents (say) on a screen or in a PDF file - copy spreadsheet data to another file without losing the ability to display/render it. .. _palette: 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, :class:`~xlrd.sheet.Rowinfo` class) is used if available. - Failing that, column default information (``COLINFO`` record, :class:`~xlrd.sheet.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 ---------------------------------------- - 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:: This does not affect users who are copying XLS files, only those who are visually rendering cells. xlrd-1.1.0/docs/index.rst0000644000175000017500000000132313146745673016134 0ustar travistravis00000000000000xlrd documentation ================== xlrd is a library for reading data and formatting information from Excel files, whether they are .xls or .xlsx files. .. toctree:: :maxdepth: 1 unicode.rst dates.rst references.rst formatting.rst on_demand.rst vulnerabilities.rst api.rst You may also wish to consult the `tutorial`__. __ https://github.com/python-excel/tutorial For details of how to install the package or get involved in its development, please see the sections below: .. toctree:: :maxdepth: 1 installation.rst development.rst changes.rst acknowledgements.rst licenses.rst Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` xlrd-1.1.0/docs/installation.rst0000644000175000017500000000073013146745673017527 0ustar travistravis00000000000000Installation Instructions ========================= If you want to experiment with xlrd, the easiest way to install it is to do the following in a virtualenv:: pip install xlrd If your package uses setuptools and you decide to use xlrd, then you should add it as a requirement by adding an ``install_requires`` parameter in your call to ``setup`` as follows: .. code-block:: python setup( # other stuff here install_requires=['xlrd'], ) xlrd-1.1.0/docs/licenses.rst0000644000175000017500000000731713146745673016643 0ustar travistravis00000000000000Licenses ======== There are two licenses associated with xlrd. This one relates to the bulk of the work done on the library:: 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. This one covers some earlier work:: /*- * 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-1.1.0/docs/make.bat0000644000175000017500000001112313146745673015677 0ustar travistravis00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end xlrd-1.1.0/docs/on_demand.rst0000644000175000017500000000472213146745673016757 0ustar travistravis00000000000000Loading worksheets on demand ============================= .. currentmodule:: xlrd.book This feature, new in version 0.7.1, is governed by the ``on_demand`` argument to the :func:`~xlrd.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. :func:`~xlrd.open_workbook` loads global data and all sheets, releases resources no longer required (principally the :class:`str` or :class:`mmap.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: :func:`~xlrd.open_workbook` loads global data and returns without releasing resources. At this stage, the only information available about sheets is :attr:`Book.nsheets` and :meth:`Book.sheet_names`. :meth:`Book.sheet_by_name` and :meth:`Book.sheet_by_index` will load the requested sheet if it is not already loaded. :meth:`Book.sheets` will load all unloaded sheets. The caller may save memory by calling :meth:`Book.unload_sheet` when finished with the sheet. This applies irrespective of the state of ``on_demand``. The caller may re-load an unloaded sheet by calling :meth:`Book.sheet_by_name` or :meth:`Book.sheet_by_index`, except if the 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 using :meth:`Book.sheet_loaded`. :meth:`Book.release_resources` may used to save memory and close any memory-mapped file before proceeding 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 :meth:`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 :class:`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 :meth:`Book.release_resources` explicitly in the finally part of a try/finally block. The Book object is also a context manager, so you can wrap your code in a ``with`` statement that will make sure underlying resources are closed. xlrd-1.1.0/docs/references.rst0000644000175000017500000000345013146745673017151 0ustar travistravis00000000000000Named references, constants, formulas, and macros ================================================= .. currentmodule:: xlrd.book 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 :class:`Book` object provides two dictionaries (:attr:`Book.name_map` and :attr:`Book.name_and_scope_map`) and a list (:attr:`Book.name_obj_list`) which allow various ways of accessing the :class:`Name` objects. There is one :class:`Name` object for each `NAME` record found in the workbook. :class:`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", :meth:`Name.cell`, which extracts the value in the case where the name refers to a single cell. The source code for :meth:`Name.cell` is an extra source of information on how the :class:`Name` attributes hang together. .. note:: Name information is *not* extracted from files older than Excel 5.0 (``Book.biff_version < 50``). xlrd-1.1.0/docs/unicode.rst0000644000175000017500000000265413146745673016463 0ustar travistravis00000000000000Handling of Unicode =================== This package presents all text strings as Python unicode objects. From Excel 97 onwards, text in Excel spreadsheets has been stored as `UTF-16LE `_ (a 16-bit Unicode Transformation Format). 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 :class:`UnicodeDecodeError` exception will be raised and you will need to determine the encoding yourself, and tell xlrd: .. code-block:: python 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: .. code-block:: bash runxlrd.py -e koi8_r 3rows myfile.xls The first place to look for an encoding, the "codec name", is `the Python documentation`__. __ https://docs.python.org/library/codecs.html#standard-encodings xlrd-1.1.0/docs/vulnerabilities.rst0000644000175000017500000000314613146745673020233 0ustar travistravis00000000000000XML vulnerabilities and Excel files =================================== If your code ingests ``.xlsx`` files that come from sources in which you do not have absolute trust, please be aware that ``.xlsx`` files are made up of XML and, as such, are susceptible to the vulnerabilities of XML. xlrd uses ElementTree to parse XML, but as you'll find if you look into it, there are many different ElementTree implementations. A good summary of vulnerabilities you should worry can be found here: :ref:`xml-vulnerabilities`. For clarity, xlrd will try and import ElementTree from the following sources. The list is in priority order, with those earlier in the list being preferred to those later in the list: 1. `xml.etree.cElementTree`__ __ https://docs.python.org/2/library/xml.etree.elementtree.html 2. `cElementTree`__ __ http://effbot.org/zone/celementtree.htm 3. `lxml.etree`__ __ http://lxml.de/api/lxml.etree-module.html 4. `xml.etree.ElementTree`__ __ https://docs.python.org/2/library/xml.etree.elementtree.html 5. `elementtree.ElementTree`__ __ http://effbot.org/zone/element-index.htm To guard against these problems, you should consider the `defusedxml`__ project which can be used as follows: __ https://pypi.python.org/pypi/defusedxml/ .. code-block:: python import defusedxml from defusedxml.common import EntitiesForbidden from xlrd import open_workbook defusedxml.defuse_stdlib() def secure_open_workbook(**kwargs): try: return open_workbook(**kwargs) except EntitiesForbidden: raise ValueError('Please use a xlsx file without XEE') xlrd-1.1.0/scripts/0000755000175000017500000000000013146745735015032 5ustar travistravis00000000000000xlrd-1.1.0/scripts/runxlrd.py0000644000175000017500000003760013146745673017111 0ustar travistravis00000000000000#!/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-1.1.0/tests/0000755000175000017500000000000013146745735014505 5ustar travistravis00000000000000xlrd-1.1.0/tests/Formate.xls0000644000175000017500000002500013146745673016630 0ustar travistravis00000000000000ࡱ;  Root Entry  \pCalc Ba==@ 8X@"1Calibri1Arial1Arial1Arial1Calibri1 Calibri1Calibri1Calibri GENERAL DD/MM/YYYY 0.000000DDDD", "MMMM\ DD", "YYYY HH:MMH:MM:SS\ AM/PM0% 0.0%y:_-* #,##0.00" "_-;\-* #,##0.00" "_-;_-* \-??" "_-;_-@_-L_-* #,##0.00\ [$ -407]_-;\-* #,##0.00\ [$ -407]_-;_-* \-??\ [$ -407]_-;_-@_-                + )  *           A ! !  8  H    H    H   `Bltt1 ܅Bltt3[Formate11Trj03  @@  HuberckerckerMorgenMittagAbendsgutschlechtvielwenig MERGED CELLSREDGREENBLUE R cc   dMbP?_%,*+&ffffff?'ffffff?(333333?)333333?" d,, ` `? ` `?U }  ,,,,,,,,, , ~ * ~ X ~ * -؂-؂? >>? 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-1.1.0/tests/__init__.py0000644000175000017500000000000013146745673016605 0ustar travistravis00000000000000xlrd-1.1.0/tests/apachepoi_49609.xlsx0000644000175000017500000031572013146745673020142 0ustar travistravis00000000000000PK^<!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-^oF|^?z""mp7-<_=m]7V|񀱴T탔'8D?b\H;Q`GSH8̜f+unC%+|׶(y>^`S% ʲTKWRD)P*j=#%8’LGT- 8x@э }In=ʅqxo@bSN?~O#bKZ8a]%I5@4매83~% t 4nVG$%l*VZ~z9b'{S<Pؼ?cc׀ok77777x\0ΏΛ*Uo3w\*SKŁTUr)y >A{՚VVڥMvm-Rwқ @CJ2CZ3u :ƝT~I>F4 㱩@V'EF\+? h-9 o ;t-܃2Dr!|Ny]:OX^T[ZLzsfaewl@Yt|gYؓ3@|hâ}9͐&!}!{ ̐X.WJfGI<]T,j충 ඙>C2mM J>0N3D2ː,@JqoAY0 aJ1"R_XAAc&i ދ".Հm 5m.qȷO"K 55`/Is!W3 jD5 ؞kMU gLǹ5H4O9P@} Lj=%P@[ Q iht*:PKF>PK]?[Content_Types].xmlSN!+ۦк0̴ KuQ?;3X^ Vfƚp3hْؔU6%}_>Ob>*{HbI=([0`R`DLp/J4o[.`$f:Rn]˷*U)_PKPF'3PK]? _rels/.relsϱ0hn1bLX >@-G!^VŷwYDV@*'\fMUg\dL0N.ԱA;pԈFڴ5wRR#ŎwP|ۗpI͠?n|%,(`]|%YVPKUPK]?#xl/worksheets/_rels/sheet1.xml.rels 0}"ޤu!"[0$IHo6.g.s{h(GKXh4N֐Tev<ӄ1݄at% C DPudRY?cLCuŞ6wPflFK}]ۮVf2=>RD)JO,TSz PKOd0PK]?xl/worksheets/sheet1.xml]wJ+XW>YD@9ꨬ*$? 0}m(kC!oۍB")\=Hiꍄ3.Y B..$d YHB@BYNlI_#l}KD AS !B@B$il$F@/߱(pd#]*# v#D3GHʩmH60l#Iߟp@9G‘s#&-TƒtʀRi64D!Jr`[$ %AI'(J.^a{n$$IW( ! m삽D 1J.s6Qj" ! R\`EX KĒ!,yoٻEetҠ3RCqBh-8yNHΩ!8!Cp N?~ؗT~ _X%- :kNBTWUsUˏI'#Ok`rbZ ktAyuF2RIF)ms6thf=ah=hf>h>`C;KlEtWz &VT'< #$wDj%3!Ph{dꑙH(4XP EBLhl C&0CfBCPh]D\2Bv Lh< l##aE+:Uʆ.Y߳9zzxl/_rels/workbook.xml.relsPK]?PF'3[Content_Types].xmlPK]?U _rels/.relsPK]?Od0#xl/worksheets/_rels/sheet1.xml.relsPK]?a/K2- xl/worksheets/sheet1.xmlPK]?L ~#pxl/drawings/_rels/drawing1.xml.relsPK]?i/?xl/drawings/drawing1.xmlPK h.xlrd-1.1.0/tests/base.py0000644000175000017500000000016613146745673015775 0ustar travistravis00000000000000import os def from_this_dir(filename): return os.path.join(os.path.dirname(os.path.abspath(__file__)), filename) xlrd-1.1.0/tests/biff4_no_format_no_window2.xls0000644000175000017500000000537213146745673022450 0ustar travistravis00000000000000 6 ddd1 Arial1 Arial1 Arial1 Arial1 MS Sans Serif1 MS Sans Serif1 MS Sans Serif1 MS Sans Serif1 MS Sans Serif1 MS Sans Serif1 MS Sans Serif1 MS Sans Serif1 MS Sans SerifC C  C  C  C  C C C C C C C C C C C  C ! C  C  C  C  C #xAC "xAqqqqC !xC "xC "xC "xAqqqC !xC !xC !xAC !xAC !xAC !xA} } } }   ID TOTAL MAIN SUB ADOG & CAT PRODUCTSDog & Cat Product  B BIRD PRODUCTS Bird Product  CSMALL ANIMAL PRODUCTSSmall Animal Product  D GIFT VOUCHER  E  F FISH PRODUCTS Fish Product  GGROOMINGGooming  H  I     J  REMAINDERED Remaindered Stock   K  PET KITTEN  Pet Kitten   L PET SMALL ANIMAL Pet Small Animal   M  MICROCHIPPING  Microchipping   NPET FISHPet Fish  OPET BIRDPet Bird  P PET PUPPY Pet Puppy  Q  R  SPET SITTING/BOARDINGPet Sitting/Boarding  T TANK RESCUETraining - Dog fees  U  VFOOD DOG & CATDog & Cat Food  W FOOD BIRDS Bird Food  XFOOD SMALL ANIMALSmall Animal Food  YTREATS FOR ALLTreats For All  ZGIFTWAREGiftware xlrd-1.1.0/tests/err_cell_empty.xlsx0000644000175000017500000037121013146745673020437 0ustar travistravis00000000000000PKHFGx[Content_Types].xmlSDc#cd`ia``0` fd3YED}٧>=һT+3n9F&&FyFFZ!D "$@l8; y TUT ~W~W~WKN0E Ӫq!>C@,$V[T*Ԏ;oxU6GOҚ TVHӔyr׿d05XN!eQmdmsZ@uhz !N}TSh& =һT+3n9F&&FyFFZ!D "$@l8; y TUT ~W~W~W=0 $yiBQ8JgcH\QheW&- b`.|ysْF:mg[CUq,( 7kΝ.":l a%7R5D%u2bY iT=KudiK6g& (wI>j}PKHL1akmxxl/SharedStrings.xmlSDc#cd`ia``0` fd3YED}٧>=һT+3n9F&&FyFFZ!D "$@l8; y TUT ~W~W~Wս[ȕ&<  eJLҌ׮=T;c="// zUE[]uAiUm{޷.YF_X;ftwH;FSՐ*3#~G;v߳o?>ɏ?}Go {[{?z?qj}47?~q?_ݷy[7߹_-}?ygǟ[O~sO[7Wo W?V~%xG׿m<=|9~l>>|#17[3c[&JO_l^l?|r>xs6(@<ה q=#[,wfXt.$49&bJ%qw'(/'Nr}+MAGLg_qO9i{X Kwu;V LǮuFo }K8<44q4dh) CI`x|AXcZ~d{CJh3n8]5#^u;/''΍r!]Űyr ŁB@2G@!R{&f)8BQD4tq^G"vC#D[a;>{6$>1mJ>Ԓn\/Ӗz!Ƿ|BŏxYvHҘ^y]HID_†}(nL1S8hdSh{<;B>2DT;|0"kY)sqϷ>NJaբc/? ]Zm?#fg SDO7skHV310.W{9m#}*Տr9a_{Xڰvn(hL5J` cɣ.LS0I߁>;KanwaЅ_ 4:b~X;ٴܼ[nox•xV Sg`7˜ +eeُГҰdTݎ%%?7Kr:J`~]x.#F xWXa@Br`$l,mby6=wW zYܗHgRc@Vyĺo8bRMg+_OJ#Q`7]+oKxݱ \>C㠋NvC$H0BKCEhc!dk',~\X+ `%d:{Ȇ$iJ< kH**L&{K qFY3OWs õ(Ĝ@"nmH[BhOx+DwX+U(bE9u|O{w_[F.~+(ͯ+VvS߅S#@K1qr׋xs,]3@_/6R+{ f^\-҆/ܬ!L[_Wy1[=ς6,Q*Qm!6-X.*ߪyPpβu6];x 'Np|בAOo8^Y0 @9}YiFck ՕLa6{yݘ*P1L^Wgu~:<-,;ЖƵo`sٺXAYZD oA$BB}N 0`G/7ϩSLGI>Τ&KZ>*g< \(oDžWw,ql| 6_+ol(˽mNQp O4l^bO  )yӥDBrͳܰ8S>00Soʽf+9*>,xFA;  Ɋh1F\څ}[bSWc@Wdx`gs.ժ8^^ܽ-je ڨP{GZT[Ry&伉ALZ*]K:Nh2*$&'$IvIllC%I1[ m޸o?vr h.AkF׹ƅ#z:SGK4*uQ,a:nĕ*ktR~仇Ǝl~-nX Ϻȇ]Q]=6 8ý;PӊX_Zq{1:/޲qFI7#tZ2R:SiͨcC;(9u#pUH*ݞhF $2Kup Fwpw&i8נ8pV=g$(ikd]@@ MB!m_Ng4_ϡ^"8{ڠPc^@=M|?̹!5ǛQc>֌|m|Z{A Q}حj'<;jSi&DK^zGtBI0BF">@o5rd?KX&gpBU}Ϸle෬+sy<|Sʣ,W\/ԉ|68 O!ݨ %4(ZST W볢X:YZSH[ 恃5d-΂{WLN7))Lkċ42]/y+CTTQ~Mĩ'w胯\s,qfFfzޅSn7WHp.8hgv&] @{B Jڞ:+|q.z$!OX$z(d.gUKR䀅ߛEKrr݃Gjh,U< _.LL8At} ʹ*R,]Y-P>h s pҶ$_6m/!e\]yoh_B<.J9-맞 v,Z N^fr# TpAouz~[^mds63׆aZ\z~Vƹa?c Io`MQ8ÄP^UC!XڶIwrx##! {k)gr okpn ƽk;뫳zǾ΁s4 gGܛ}"ӀIZ6z0 JnS78l D.riTFb1i,UTī1ꧠ6C}ayHW`uY!,Tc,!,  i&,!,Hsr`a ,A,dg8Ń9xc,b=n6g{ |;|䵦%WPmJf +C7Nvq_--P eMz3UdƠ^B$~T8xB6Ɉن0jGfgw=|*PO =n.ވzM+J-PrʹͲRk+'L_ Vb׃) nU l٨Vo,eVܢf"Ejꔪf~ԲjCms=1~"70جK,[3|i$)Tn(;>az&*ĭa! F wWBIQ_ػj/`(6X)ęH>axAW`4sq h_˴ۏ]Ph޴ 5 MR_wV,T&Y/V"[+bUOM3ӂ@g5B/TiBFYa|?X0}SӾ-ky\N׏I*ܥ78qW >*;φZ(VKhX|? p+Qػkvv/W)zJ-kQ/@oMUO/Gx!:xK$vKO]W³oE6[;4cx88%{$w!dgBS,kd(s|}_VKUV=Z|~-tvٔ,.::"BZV3Aj`kծJ"G:i%w}mL{Z*TbP,t/a:If9*[Ȫc !`$tiNADF!uh=\ 2MV8]nG( oGSymyGu O^L&G3Tj!D{/{Ob=&c3t4[T0P߷ 2%A螘GaRr-%;'lB|Lo纵̓ԺadvcOV`O洷tΧj=߬Z# n~7J~] 6Fvf:IoC5FѤia,6;dz8ɉ'mIT|\iZ80kKƐ )ѥ0jcȇ1ȧD Ch!:F(dSi1eqiߧY~^&IL幭-6jВo| -nΧP0IPMG.'ron?B0{Pg2+&&) p.Fw+gsc{[ث̬0@ndBϽ'iPm$¸{W.YMvWRe0cT~7Qy(Zd55>%dn!ko6N||NI#'Ѝ$ =}KM_F~Q /èOEE>Š,!pG>9ٽu8SI&(]@ۦbʐq8; ;r*Mw&K:;ObFm\Hz:u'Y ޙ:[K˕yZW\4#W³7x"o)1VeKz,N@/#d=!dBs4a%B^GGc"7іl ov1S%#w7mRkK.~7lTIz;؞TQcP XƑLGY`:HZ.`j8Mvo jb^łsLW} z`Y/@PXDgEnp/2QYz' IY<*o56&jA56dFt6ϖM{G%;t҂s *)n# !D߽Zxg$bSba`UO;9{8ϐ.,mMiuH0<bRnDwv}Y&h6TlYWb* E c)i6p~ 5PICr UWOn xTqV ˱e// ;*̊FtȭFTr'rvݾm QJ0;u%lt5Mgc%%|0 խNZrx.PHM6o; *R2)} % ],chbONb[*+ wk)_ zYTyrq]qw.IϖzEU{ܝN{$](+t VZÞƶih{uvu >2iU]5i5үJ YAш ]ZHؘ#\+kefӋ`p/ϸĭ趡tvj~#C6bxr*A_)luƨí>+7hIZqX@#B 2/nbz`s[ֈ)?EWõ#~v:+x%hHFpZ\@m~%tוAMҋۣ*8n9qvvLk"}x@/`yu8ɭ+oN/'3KS.W 4աs~>lro;HkHXgX!1/S'y:,82fűE;Hq  q8ٵC,;=9#BNM3B-Q(x b|8IӋSç81~[3T{7b9mk7~j%O7Ho7=J[@48dʲ2֥Q1lT0%~TqB^l?b2mB"CҲ8j#ΖBh-)Z۩z\ܰӾFW*hGn9(z,4is!:v;X|W⤨6LJ#{dFvwa+Ϊ5iVqޘiVy>ˬ1ΒMAcauF7gzxЛpmN.63[[ؤ"斂Ps"hbl'݌' ` hIgF`,Jx`$liuIϗ]֪vJ~ .s Ih|?oP\EX\QK]tM/˕]T۩X/?OzK?* *fmsASj7:IYa2qUBer#HGA[FB ' ]֨iVTSLzV &^g58]eLök#`i | W𑊾QQ5rA;n6ZOb\Mtn4Wnx;4"BP=t36H]$_ΠXa{sW>.J:u{o`eVpUQ@[|(;8!m?FtE8+d\N&ݶ[*imR:el%,P2gFx{P?{<Z=2U`H|] ڈ]B?]s5I~,JZ!F:qqt>d8W-`#{A X1\51}v5ez1"C h@ nOeY(KZ qrqinlmk0g{sЀ.&zr3A 8 TDLR-t.,m"cN:Zj򸿏.Z)= @GS |QZ !Us9Y: e0mi,Cēol~ @[7ȧSZމ9Bgz  7p@o{j[zsDj8 !>:_׶UgOiQ,UvϬUτWz~_{Y-RD20jƴ Gܕ1k⅜b%[EABF'YFFn P9QE7Jܧyl5XmB{.ts"O͈`{2@ ߑpyQ^. LkY-"ߴ8ˣmon~ @*{%nK`4Cڶ%jmrfb@ųtg3b|RxyNR 0}$[ͼӢogJ jAZ=VvF92s|w4])vb)$Wn'Ej_F8@%&Hx*:2ꭕaWV:ɥ8ƲunBW!~qކp?b泼Du ~;[1:?Jպf.g-s9׿n!NկӋ^l]:K3yړ*&Gxl!vp0m>M!/i@Ik_8.y,d 9 ng˅P'vDJ: V} d5/)ٓƂXtM,dg`qVD;A%,Eb,mL֟/Oyuf6NnkٴJ,oUf1k\(_F$Oݦ,lE4;r.4A|:L["U";dExs6pk} ~3<,{%FA;0ZT_/G3߷9x 9c.X%`M[>GKe˚RÆj/8e] 扔/U%-d!hiz{P?T wM$?}#_u Z_ [.<@s>|*呺ʽ1ˠh:Ѯ@K mf&L]b@drzN("z1l8DdB^u3v*~#r*FC\ C„Kda4%Ѿ"p+(R`~'8(v&t/9{B dL&.|9ɫi)]SܾF\H<ׁߏc,}XoWZSo u9烄a! 1t`h% SyžDצ {>H^с  {]R ܺ}BXdǓ1GW4MU`Wq)p+*LZ0uEJ_qj Q$/2R*]Ui, :ж hSr2-ڔ>mhZ$Y#vJ/EQ!Ӊ= *Mb`RP6lH(M-n sa})lORɵZ6YPՃ=FO)@YRunI}Kxi%zhE=):OW^JP. mBK*]wNF'TT"|*{f'#+|W SMpb D,EFփA(b2=]&&I=Cv&Z@:fį3IyLWk{cQB $.3"/dQ@_B:h.'Ӥڴ2p0>gWBB[s81a-jl`%;aHgŲaDҖKPH}C+ #n5e8䕬11{ze>M'vN65phGso Zt/rRszuo{5{Baӛy=OW ET>(`l9ך:iՠVhڱT<ҏ786R+XfV Z_<ZJ-6K[BH3;ҔMn=l/h272׺?n'@‹*)5e X#ͬHSDʞE MI^ړѶ'b slw냺(0JeUٱa(ʎWA~CGPΕP\924RtaRRr MJ/ֵ0])xPJΦ_`yk w>;1"<6 C11!?/R]sރc2ȼ#`gs9MwdfO21|X5EUA6uY""W  O|  8g?מ>H'e1qf48|h Xk x̕hj!y] x.*_z[m5}mہ̹O ], Į%FB-2^x9k|PLY]7?Vev~sjz3hwZޝM'#v\IH`|W5gWEt[!~wi΍ NHeW4g颸Nr ̘[/ppjbo9e P$ "_.qb63̸i8i:g첽QzPHk%z`w`j.g'{ًNNj=cby,8Bu03uf!?K_p>a7l.JTǂSiT:;'~yqŁ,V]pɑy^`q抙lfӌ"Lg\]Bq^3(R#QzK |c..0uwb " Fԑ ucD2p,|f7h,nwGD=dbw8?rTQFbe1t_6-n^|WZ.q;\iq7Y}7K.FU:.榅:O͕H !QLeyKǛUf{cS15#i3TM]&Հlgt.x+.2,վ~@9GYr0@x[Yige5Ϳ+ػ~[_6Q}S<.Z:ݚзtw%|[6DԴ.+rz>b, `^fLZC@V6bXF XIbu\BP*tc.Dk kQz1Utg@ӑc}lj8lMJAΜ$AMg Gm\Y>Tp~-FC bvSXL%w>ƿt7]xA2 %W\>'Q5$œ~w<8<)ɤ{zYtטnx8)2gj=b?. e1`лԦuq{}iV+4rY='>\~.{nBIr [&b!RaT%0*MnLE: X#w75%+`DIC>X}@ޏ?NW;9۾jf]vA- }J*z#I+ee=9ٓ+nc'ǿv^7 :,[\ÎN5;"֎#ë́?ZZ,NW!{FsKL^lȶkpW_\BvD:rXp)cWg$`W!_f@cxʧᘏ0ʠTO>Lz[2CAfZ8ƛīCB@ͻjHTJM`QшPe̙*vw-naգ~ٜBѧUEvI}"4\-4l\޸&UM1=*ٺ5{8OJapu]Ʀ:U:+& ;\$xH {#wbOՕB8T YY>C-͞'?HUzGL}=^K]d Z4fl 3R3>?YZxywui`xur %4sJnMNX|uWpUʠu4yBT~+#NgefTlUY4\&ʦ;W`hs"#؀$K8 "PzۮVj!5{ 5|kzn b^0q"b/ZmB6:>eY, yho8ձxxc K ?-T$԰jrRu6򚜓ctD-%WyʪG 8GDNҧQ`sa0p\i^Uh0 d{m (,h&/6>Ϋ]RyQ4E1ݙ_Е}7>=1܃LP4| 㸣N6~r l dž[z3e^7bzRy7rjRY ܊іJ+.Lk){ɣl䕐1n_ [["i H=և{p f9"V T;?*j=6Rϳ~\i< ?۶ UU{F 1m(prg?^t+qXAyEѴcl}U[(5ՒoQrmJU*rN ?_*R6Ǒסh٧uMU}YQ11kΖgjEL@܂[2@CeCWVi[: 9epw_SSNٕ0b[P кxe V+JO }I*aGf_ Lbu^8mT8NjaYc~iؑ0t&悶uQ_oҼ[Zn<E($>v#}%7"KЗKٺVFE39P^7謋eh$]^=q' 6;XRhnW,i뾾}]>=aBwo(08|IG_h W0\l?5bI~,P k^5]zȰWLۢn 35|' F k/Hj4g[؂sWu <U{ []F5`Ɨ(5'X'wpMPOa}W0eCʻ;O N3΀q$`.:i"K/,)l8 @7T$QJFPaT+mZ _t6\DS`]AdA$1}. 'Q;;~uaZ0-(2UJZgkW J%ȗ`"*^o1 jc.bםV3&lv uv. Eh , %?kãQqoʐuP6aӒir"z=2t,ԃ cCbqްQMѼGcU,(ѕc@ PūBM #`J`HT$bF(hƿ ˱GbU}v~a:[.*oJzO1`ӌ=/8ޮ V "j8nX\Uup^ٶFrlLӕfWNҬgYPÂہu`s\06".5s,]ӳT5G%ϡf^0Rcoa1%.Q:{ZmJMm$G*~*Ӷ o>Pݡ-S+GՕTEGW͸#D,X p܉32!!Y=J \{KJ%-J9+s"SIAlHŪTZyZۚ8介zz{IӴrP?Jf`Od u> e^r4[s鷾j G7jY8/MWKK '!*.$ : m@Nh yߠAh alUУ ;N#kwe {VA:p0 &QV ><񣻲ݽqcRGu![ۿ~3qOHNuf)l3QȆ ࠹̱8vGƣy^d|wUe\SВhV°Фԇ?q g}]Edؠiqrq+:rK2G;-e +e @2q p< ŝyOxu -,Ffzό ڡW.'иp L#Vgg4ZI5u%^Ǧ!X*!9 X($Av0bBbkR@|>d:NBCDOWV+>S0fYC \W|[f(\:!j+NmGYj# ;R=36;~z5m= Q05SŞkCM cWQ=R518X-;a]yJsjTaSo0Oՠ Dž6\Nu䙊W" bvN߳+d +YBjKp}j&@Ԣ[#bR>|6G>\y- O<0$QA"0łAx VAC tF#ڱPB+T RK@1 U #@(!uN*+U=ߐC kepM/zyp*s_ y$3Ap}OsMu;OY.A7^%dxssq#g)aUQŠUB0nϓby@|z}ԸRS)Ѳ|&(ih"eL;8#;.`p(jfzeSg-v[+FbQ5lCls( ;S*',8rn/FBCExh] L 3tVbpBw+ C 뤻̐lY3o=R\0|q8p|VVjr} kfRس#աk4"3idw3i0×Y R.&b"]ʍ}ZQ@Pt+\zZ]_]($L V\ގk.yފM%y鴸̖{{t"[QQv((Hu~(0ƯH3)VR3t'e@iXhw7hVȠ~-͑%JO-OCV^OtBcib]=Tڷ]tap4R#Q.[sQܬ"h$D`(2l*΋P)U=cL[ ';lr]JP!^|l^-v& j$AKV G݉lZ/l < IA rT骻vu oa#tO?/fYs)QbwDtmG9 b,&LX\l6T7~3lήC&zg0v&i;Tm۩hjEfќj|[3 _eG= .v"aZYo*1n-{Mnìv\o=*vaG$՗9-&t@Wٲ{J?h粃]hpA1g&w+k?߭xaqwSc\r@,-f1{4AreyPƯsЙՂc_eD >x7`Nͪ*Aat"t<7Pa:e)n3}j`npM8Foa*V8p.X]Q'{ %▵Q6Up< "ƆK?8p=NjFU޵ܪXdYZża1l'բQ]#5*e -Aj=mЪ12Ľ]S l튃ty4AD ewWX\#SgPߗA fchrqC}Q} u9G9O;WcT޳y:hɉs/`# 3CߛG/l61g1:6bxTgh+[^[ 6DG ՗Mj #|x^h|s玳}ba;g9ퟡ,b2=Khqnxn/\P5>w إz饁=*oyssJ&7qU^o_\Fh$Pr740ϡSE>䇋\ۀ $QLCziN= 8?yow9Ww8v x,4tCBYF"0N>GOH*)eQ0ߌiB3P8Ѝ'M3dQαCty' KR=G6ej b$@`t9Ub#q10[aܐ$J%R-0 < k{.>=y+2?'1䦩[Fi@+wRFsI sly:q6lb|jKߩb4 a^h6` aV^]w2PO8~xap\:&\"|&K :Qp"ѐV*+ӴN1%K#% i2QFu&o,WX$a|><\xN⸑cqYFMK}.P ̈́czN4p K9 <*8 Nz1 g Obʀ0;BOfbzS+# 4pnAN3Efz@ӳD2! $Loؘrn]"L c9)u7 nLLb<>wrSyԭ!3EsG0dS,3U"8Hl GYiƨII<>G4Ћcsu cb?^Fl9ɖ֙ "gf7~KO?钞x`^iQ-<\ejx 7$7פ?[[+ӊ{iS \M+87D>sMLwMJd 4 xHo!H{&*1\Ȯ |`L.l3|X02DWx,7tBLPxnSY Au9R#a) YӸĸĈɽDL,]dka .4 btCѨSz8pDya Ssg5²j"~ܼrɯ )0w^x:i=3D4>'Oe/(VDrͽH}DG ?+CG'rd 0v4\Bh eU˲'tު004f+xY 4vG8sv\9%f2lS.xƑISmK]$h#'/|NayyR=ug`LVI;XgEsKxA!!GL}Y|G(Gӳ]/~"dd $cF r`!Q fQ^RhVՄX^m 0vM6}ؙtd07e:s$5Z ϥ7e3D (?vrzPIX@c=RݾwA.;Kc _sÛϛ&S` L n N=aܮ01qs4|h8@6[~61G#0F@\Gc-?`*9W~+wЈ4$x#ୢZOQFTɫ0 [P&EoXEoDF$z#P (z#=@6&E`peoD }cu^a95=F^1=XeↁbZV P^6 ?1YN< ܒu9ox >Q- B_2"SM S=|yr\2KUVͯ% CW,|*~JBHO|.Rh^n>u%i9/KW3/,8 ;Qy5 y9 /z²s+s)h^0vǛܐfG]Dj;Fa*{Xz \h 4,9'3X]YFTSlI,SuBi9_$v_6?yCw~PKHLh(Dxxl/_rels/workbook.xml.relsSDc#cd`ia``0` fd3YED}٧>=һT+3n9F&&FyFFZ!D "$@l8; y TUT ~W~W~WN0 @ 4@fIB(qjmfƁOBNm~}W/ v^AU Л`{)xj֗ zLHZ^8hMH"S<)pNJ2GMEs iԜɨVw(ey#Ӝzv mf|h}0#zI< y!+AuJ}H[r-qHeϧq:}o>_<}PKHXj>!;#xxl/drawings/_rels/drawing1.xml.relsSDc#cd`ia``0` fd3YED}٧>=һT+3n9F&&FyFFZ!D "$@l8; y TUT WWWMvXr Zwˮi+|$m)IB$0niٜgiyz/__矾t矾~OO_7?}_~ןoo~~ןW_?o_xO?7o?u?>_3>qӯ?/ӟ~O?>~8_ן?7_~WI6ɅIB$`&II &imIFd`&Yi<|xiYRgC=^jx=TPoC=^r5x=TePwKݽ^w/~RwKݽ^w/uzݽuRwKݽ^w/uzݽdA|@ A A A A A AMIMIMIMtŇ[[[[[ksuuuuu6u6u6u6u6u6c/2PwnSwnSwnSwPwPwPwPwPwPw 2 uwuwuw.uw.uw.uw.uw.uw.uw.uw./RwRwG}^wuy}uQwG}^wuy}uQw݇jBd&L8rCh'>DD@C$h(>DDCd(>DHCbdlbd3)[6f39{6h3I6j3Y#¶툴m;"n;ێ#q;"r;܎:#Bu;"v;ݎz#y;"z;ގ#·};"~;ߎ#;";:#B;";z#"$;";#¸24;";#BD;";:#BRT;";z#bd;";#¹rt;";#;";:#B;";z#;";#º;";#;";:#B;";z#;";+.+.+.+.+.3F9s_S\*-6^9so3Wn9so3~9so3W̡.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]K%]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]+]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5]k5ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݠ ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-ݢ[-'{{'{{'{{'{{'{{'{{'{{'{{'{{'{{'{{'{{'{{'{{'{{'{{'{{'{{'{{'{{'{{'{{'{{'_1E!IǓd?rAwT'~~';x9?^cE;xY?^c;xy?"?f㽉gyobMޛ}F&f彉gyobmޛ}F&fOz1371JM>{(3Z71ZM>{(3h;}F&fQg{obޛ}F&fQg4|obߛ}FǗ?}h3j71M>{h371M>{h'c}ߛ}F&fgT~obߛ}F&fg~ob_D>{Ϩ371M>{Ϩ371M>̟cgtob%}F &fQgobE}F&fQgtaw16M>|3 71FM>|3J71V0"?f gob}F5&f gob}F=&fa?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`a?`L?1`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`e?X`M?X5`c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83c?8L?83~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~_~PKH5Kxxl/drawings/drawing1.xmlSDc#cd`ia``0` fd3YED}٧>=һT+3n9F&&FyFFZ!D "$@l8; y TUT WWWK\Yzv-,APB.(&2L2AUQ؀ Sɬ8j}ӛ7?W>o>Շ޽zϏz7yZ^]]\ܽwoͻw+/ Vͷ_>]^?]߾y<_O~~qՏ<?߽o~qՏ<㫗?ù_#^oۧn~ʷߝ÷o_۷Ux-׿ۯxy7?勯}z_+-^_O?{m}_ŕ??ܞ_w_~x_w߽d7勏??{Wvio~w~g_kOO_^O?g}I>~o}ŏO>i_Xa?n};|O|-+xbAg)ޣXPAY(oQ|#;xyba-^XX|`yW,Y|5 Ǩ4E`]KQx0*/ Fe4ŗ`_x%4``LQ0*0 +f4`T` LQ0*0 Ff4`T`x4`T` LQ0*0 kf4`T` LQ0*0 Ff4`T`x4`T` `T`rQI0ȁF(8`T`rQi0ʁF(>Vi0ʁF(4` LV0*0 F9p4` LQ|` LQ0*0 Ff4`T` LN0*0 Ff4`T` LQy`T` LQ0*0 Ff4`T` L^0*0 Ff4`T` LQy`T` LQ0*0 Ff4`T` LA0*0 Ff4`T` LQy`T` LQ0*0 Ff4`T` LQ0*0 Ff4`T` LQyOGi 4J0F Ѩ,EFhTa*̢Qi40F&8pTb"RQ81 G5&(, 3pd"Q8J2 GI&,5(pe"Qi8^2 GU&,U*pTe"Q82+U&,U*pTe"Q82 GU&,U*pTe"Q82 GU&,U*3pVe"Q82 GU&,U*pTe"Qi8^2 GU&,U*pTe"Q82U&,U*pTe"Q82 GU&,U*pTe"Q82 GU&,U*3pUe"Q82 GU&,U*pTe"Qi8ު2 GU&,U*pTe"Q82;U&,U*pTe"Q82 GU&,U*pTe"Q82 GU&,U*3pWe"Q82 GU&,U*pTe"Qi8ޫ2 GU&,U*pTe"Q82U&,U*pTe"Q82 GU&,UxGUǃ82 # GU&hbDP,U*AU*q4ѳpTe"&zLDQ8Y82Gp4E9^]2GpDE…*qTeLQY82GUfDU*qTeLQY82GUf$^yLQY82GUfDU*qTeLQY82GUfDU*qTeLQY82 KUfxDU*qTeLQY82GUfDU*qTeLQY82GUfDU*pReתLQY82GUfDU*qTeLQY82GUfDU*qTeLQY82 kUfxDU*qTeLQY82GUfDU*qTeLQY82GUfDU*pQeLQY82GUfDU*AUD5녣*qԬLQ^82GzD5*qԬLV^82GzD5녣*qTeLQY82GUfDU*qTeLQ8ޫ2GUf4T*qTeLQY82GUfDU*qTeLQY82GUfDU*qTeL^Y82GUfDU*qTeLQY82GUfxGU&(.UxGUǃ828Q qTep<*AUxGUc8^_28Q qTe"/Um GU&hQi8>pTe"/Um GU&hR8pTe"Y/U*pTe"Q82 GU&,U*pTe"Q82 GU& T*pTe"Q82 GU&,U*pTe"QI8^_2 GU&,U*pTe"Z82 GU&,U*pTe"Q82 GU&,U*pTe"Q82 GU& oT*pTe"Q82 GU&,U*pTe"Qi8^2 GU&,U*pTe"V82 GU&,U*pTe"Q82 GU&,U*pTe"Q82 GU& T*pTe"Q82 GU&,U*pTe"Qi8ި2 GU&,U*pTe"^82 GU&,U*pTe"Q82 GU&,U*pTe"Q82 GU& T*pTe"Q82 GU&,U*pTe"Qi8ީ2 GU&,U*pTe"Q82 GU&,U*pTe"Q82 GU&,U*pTe"Q82 GU&2 GU&,U*pTe"J82 GU&,U*pTe"Q82 GU&,Uͅ*pTe"Q82 GU& U*pTe"Q82 GU&,U*pTe"Qi8^2 GU&,U*pTe"F82 GU&,U*pTe"Q82 GU&,U*pTe"Q82 GU& oU*pTe"Q82 GU&,U*pTe"Qi8^2 GU&,U*pTe"N82 GU&,U*pTe"Q82 GU&,U*pTe"Q82 GU& U*pTe"Q82 GU&,U*pTe"Qi8ު2 GU&,U*pTe"A82 GU&,U*pTe"Q82 GU&,U*pTe"Q82 GU& U*pTe"Q82 GU&,U*pTe"Qi8ޫ2 GU&,U*pTe"o/T*pTe"Q82 GU&,U*pTe"Qi8>2 GU&,U*pTe"R82 GU&,U*pTe"Q82 GU&,U*pTe"Q LQ$8^2GpDE…*q LQ$\828Q8j GU&Y/UfpTeQ8kU&(.UHpTe""Q8 GU&,U*pTe"Q82 GUx,U*3pQe"Q82 GU&,U*pTe"Q82 GU&,U*pTeWQ82[U&,U*pTe"Q82 GU&,U*pTe"Q82 GUx,U*3pSe"Q82 GU&,U*pTe"Q82 GU&,U*pTe7Q82{U&,U*pTe"Q82 GU&,U*pTe"Q82 GUx,U*3p|Pe"Q82 GU&,U*pTe"Q82 GU&,U*pTewQ82GU&,U*pTe"Q82 GU&,U*pTe"Q82 GUx,U*<ǻ U&,U*pTe"Q82 GU&,U*pTe"Q82 GU,U*3pTe"Q82 GU&,U*pTe"Q82 GU&,U*pTeQ82+U&,U*pTe"Q82 GU&,U*pTe"Q82 GU&xw,U*3pVep<*q LQ$\82GpDE…*q LQ$\82GpDE…*p 7LQY82GUfDU*qTeLQY82GUfDU*qTeLQY82 +UfxDU*qTeLQY82GUfDU*qTeLQY82GUfDU*pVewLQY82GUfDU*qTeLQY82GUfDU*qTeLQY82 UfxDU*qTeLQY82GUfDU*qTeLQY82GUfDU*pUeLQY82GUfDU*qTeLQY82GUfDU*qTeLQY82 ;UfDU*qTeLQY82GUfDU*qTeLQY82GUfDU*pWexDU*qTeLQY82GUfDU*qTeLQY82GUfDU*p|PeLQY82GUfDU*qTeLQY82GUfDU*qTeLQY82 GUfxDU*qTeLQY82GUfDU*qTeLQY82GUfDU*pPeתLQY82GUfDU*qTeLQY82GUfDU*qTe qTe""Fi8^ GU&(.UHpTe""Q LQ^82GzD5녣*qԬLQ$8ު2Gp4D…*q LQ$\82GUfDU*qTeLQY82GUfDU*qTeLZY82GUfDU*qTeLQY82GUfDU*qTeLQ8ޫ2GUf4oT*qTeLQY82GUfDU*qTeLQY82GUfDU*qTeLVY82GUfDU*qTeLQY82GUfDU*qTeLQ8>2GUf4T*qTeLQY82GUfDU*qTeLQY82GUfDU9>\2GUf4U*qTeLQY82GUfDU*qTeLQY82GUfDU*qTeLAY82GUfDU*qTeLQY82GUfDU*qTeLQ8^2GUf4U*qTeLQY82GUfDU*qTeLQY82GUfDU*qTeLBY82GUfDU*qTeLQY82GUfDU*qTeLQ8ި2GUf4/U*qTeLQY82GUfDU*qTeLQY82GUfDU*qTeLJY82GUfDU*qTeLQY82GUfDU*qTeLQ8ީ2GUf4U*qTeLQY82GUfDU*qTeLQY82GUfDU*qTeLFY82GUfDU*qTeLQY82GUfDU*qTeLQ8>2GUf4oU*qTeLQY82GUfDU*qTeLQY82GUfDU*qTeLNY82GUfDU*qTeLQY82GUfDU*qTeLQy*qTeL^Y82GUfDU*qTeLQY82GUfDU*qTeLQ8^2GUf4T*qTeLQY82GUfDU*qTeLQY82GUfDU*qTeLQY82GUfDU*qTeLQY82GUfDU*qTeLQ8^2GUf$/T*qTeLQY82GUfDU*qTeLQY82GUfDU*qTeLRY82GUfDU*qTeLQY82GUfDU*qTeLQ8ު2GUf4T*qTeLQY82GUfDU*qTeLQY82GUfDU*qTeLZY82GUfDU*qTeLQY82GUfDU*qTeLQ8ޫ2GUf4oT*qTeLQY82GUfDU*qTeLQY82GUfDU*qTeLVY82GUfDU*qTeLQY82GUfDU*qTeLQ8>2GUf4T*qTeLQY82GUfDU*qTeLQY82GUfDU9>e"Qi<2Ga&(Lȣ23yf"Q3yg"Q<3G}&Lȣ>3yg"Q<3G}&Lȣ>xg"Qi<>3G}&Lȣ>3yg"Q<3G}&Lȣ>3yg"Q<3k}&L@STh& MR@J4HfT E HSTi&*MR@4HfT PSTj&JMR@*5HfT RSTj&JMR@*5HfT RSTj&JMRY@*5HfD RSTj&JMR@*5HfT RSTj&JMR@*5HfT RSTj&JMZ@*5HfT RSTj&JMR@*5HfT RSTj&JMRY@+5HfD o RSTj&JMR@*5HfT RSTj&JMR@*5HfT RSTj&JMV@*5HfT RSTj&JMR@*5HfT RSTj&JMRY@>*5HfD  RSTj&JMR@*5HfT RSTj&JMR@*5HfT OL@*5H&yL R3Tj*JR@*5HL R3Tj*JR@*5H, / R3Tj"JR@*5HL R3Tj*JR@*5HL R3Tj*JJ@*5H&L R3Tj*JR@*5HL R3Tj*JR@*5H, R3TjO7RSTj&JMR@*5HfT RSTj&JMR@*5HfT RSTj&JMR@*5HfT RSTj&JMR@*5HfT RSTj&JMRY@*5HfD RSTj&JMR@*5HfT RSTj&JMR@*5HfT RSTj&JMZ@*5HfT RSTj&JMR@*5HfT RSTj&JMRY@+5HfD o RSTj&JMR@*5HfT RSTj&JMR@*5HfT RSTj&JMV@*5HfT RSTj&JMR@*5HfT RSTj&JMRY@>*5HfD  RSTj&JMR@*5HfT RSTj&JMR@*5HfT ՅRSTj&JM^@*5HfT RSTj&JMR@*5HfT RSTj&JMRY@^*5HfD  RSTj&JMR@*5HfT RSTj&JMR@*5HfT RSTj&JMQ@*5HfT RSTj&JMR@*5HfT RSTj&JMRY@^+5Hf4 NrT RSTj&JMR@*5HfT RSTj&JMR@*5HfyT RTj&JMR@*5HfT RSTj&JMR@*5HfT RSTjJMR@*5+fT RSTj&JMR@*5HfT RSTj&JMR@*5HfyT RVj&JMR@*5HfT RSTj&JMR@*5HfT RSTjJMR@*5fT RSTj&JMR@*5HfT RSTj&JMR@*5HfT RUj&JMR@*5HfT RSTj&JMR@*5HfT RSTjJMR@*5;fT RSTj&JMR@*5HfT RSTj&JMR@*5Hfy}T RWj&JMR@*5HfT RSTj&JMR@*5HfT RSTjJMR@*5fT RSTj&JMR@*5HfT RSTj&JMR@*5HfyT R|Tj&JMR@*5HfT RSTj&JMR@*5HfT RSTjJMR@*5 fT RSTj&JMR@*5@R@JH)N t8Tj*F@JH)N t8Tj*R@JHL R3Tj*JR@*5H, o R3Tj*JR(JMJ:@*5HpT RS'JMR:@*5HpT RS'JMR:@*5HfD RSTj&JMR@*5HfT RSTj&JMRY@+5HfT RSTj&JMF@*5HfT RSTj&JMR@*5HfT RSTj&JMR@*5HfD o RSTj&J GTj*R@JH)N t|Tj*R@JH)N t8Tj"wR@*5HL R3Tj*JR@*5ț L R3Tj*JR@*5H&yL R3Tj*JR@*5HL RTj*JR@*5HL R3Tj"JR@*5HL R3Tj*JR@*5 +L R3Tj*JR@*5H&L R3Tj*JR@*5HL RVj*JR@*5HL R3Tj7JR@*5HL R3Tj*JR@*5 L R3Tj*JR@*5H&yL R3Tj*JR@*5HL RUj*JR@*5HL R3Tj"WJR@*5HL R3Tj*JR@*5 ;L R3Tj*JR@*5H&yL R3Tj*JR@*5HL RWj*JR@*5HL R3Tj"7JR@*5HL R3Tj*JR@*5 L R3Tj*JR@*5H&yL R3Tj*JR@*5HL R|Tj*JR@*5HL R3Tj"wJR@*5HL R3Tj*JR@*5 L R3Tj*JR@*5H&yL R3Tj*JR@*5Hȣ@*5@R(JMpR@@*5HHibT WL R|0\1Tj< RI  yHȣ@*5@R@0Tj*0L y RS49Tj< RI RS41>Tj< RQ  yHȣ@*5@R(J 'Qj< RQ  yHȣ@*5@R(J GTj< RI o yHȣ@*5@R(J GTj< RQ  y;ȣ@*5@R(J GTj< RQ  yHȣ@*5@^(J GTj< RQ  yHȣ@*5@R(J '|Pj< RQ  yHȣ@*5@R(J GTj< RI  yHȣ@*5@R(J GTj< RQ  yȻ ȣ@*5@R(J GTj< RQ  yHȣ@*5@R(J GTj< RQ  yHȣ@*5@R(J 'Rj< RQ  yHȣ@*5@R(J GTj< RI  yHȣ@*5@R(J GTj< RQ  yȣ@*5@R(J GTj< RQ  yHȣ@*5@V(J GTj< RQ  yHȣ@*5@R(J 'Sj< RQ  yHȣ@*5@R(J GTj< RI  yHȣ@*5@R(J GTj< RQ  yȣ@*5@R(J GTj< RQ  yHȣ@*5@Q(J GTj< RQ  yHȣ@*5@R(J Pj< RQ  yHȣ@*5@R(J GTj< RI / yHȣ@*5@R(J GTj< RQ  y+ȣ@*5@R(J GTj< RQ  yHȣ@*5@Z(J GTj< RQ  yHȣ@*5@R(J 'Qj< RQ  yHȣ@*5@R(J GTj< RI o yHȣ@*5@R(J GTj< RQ  y;ȣ@*5@R(J GTj< RQ  yHȣ@*5@^(J GTj< RQ  yHȣ@*5@R(J '|Pj< RQ  yHȣ@*5@R(J GTj< RI  yHȣ@*5@R(J GTj< RQ  yȇ ȣ@*5@R(J GTj< RQ  yHȣ@*5@R(J GTj< RQ  yHȣ@*5@R(J 'Rj< RQ  yHȣ@*5@R(J GTj< RI  /o>}PKHP' xxl/styles.xmlSDc#cd`ia``0` fd3YED}٧>=һT+3n9F&&FyFFZ!D "$@l8; y TUT ~W~W~WW]n1~.wN7Iiբl Rīݵw*T \(4j茎P/$*ב9%;(_=p5E_%xjA)o臂MiQKrp#/U'u^Go]5(nf^n=,7ѝk\(v17cw@ 4ח濮/WLzj»ǫOZhbpm8AcF~cR##9:4a = PKH|waPxxl/workbook.xmlSDc#cd`ia``0` fd3YED}٧>=һT+3n9F&&FyFFZ!D "$@l8; y TUT ~W~W~WQN0'~҇{שhgo4@"hY0Z}PKH3#xxl/worksheets/_rels/sheet1.xml.relsSDc#cd`ia``0` fd3YED}٧>=һT+3n9F&&FyFFZ!D "$@l8; y TUT ~W~W~W; 1Z;Mv-Dh#&!Yx$`,,,g~X]ȇ 9πQV7/6hl! W ZQXRLPBUS[G&%4J8THLl*IXVx[UǎLEhT.+8>a USz.PKHju xxl/worksheets/sheet1.xmlSDc#cd`ia``0` fd3YED}٧>=һT+3n9F&&FyFFZ!D "$@l8; y TUT ~W~W~WͲ-;vfh/vOFJf.NT!2A-"T>[5H ξw[0cl{?7ӿ7_7_wo?7ͿOOo?o/O?y_^?_?6||~/7wtK~ןo^7?}~/??՟wo?7?OLOO_?W?GW7/?W?/?/ן?o?oۏ/ݿ_wG???__o)Wo?/cWoF~W?שϟsM[#s/y?O?}T>?ͷ'o/ׄK?;S~7} }:~Upjÿ*";#GD$AD"ID&Q~ ~ 1?aҋ7~?:ޏL#Hg?O ǿ?9П<=47?fVdDL $BM&!!Hӡzi4HyEC)T~gj5/9 5O0#*?BUDe>BۥZC hPP?gPqJsUj{1o3LaFBx?ob 9:K凃wBd@#m[fV`t}@#ݍCnzj8PaIA.._H78 L=~ ptt%"]uŷ twKE|4$݃-`tQ@#݃^}L1Nch$%iTy#ڔj#7t|56\5h]6*ōDT`)9TF(l"y@C.Q ΍T`#)%凣|O=eDKm^يbjSfG%#ă!t v$䒡 l66Hq$ݰST?:? b:PK,Ζ*Rua*K;,Վw1G1NChAK,Ζ*R,ڕjG{$ʩ:i Rl"s'Kuڕj'$T3NCt]vTΝD x1:`NKTA* UסTPU*$~7#{Yp=KDA[<:HT,%U݁NBˎaGkgOUulT=SE^SOu?d͏Ic/% G`>Qqmu(GuR-#>Y ,A g6 K A[cK*bPdT㍝hQ6qhMئбDOE+xqJOTh'"KKӉv:M}An*Pn 7B%.KGA]<6I^bSe;ub[yN}Lo#ODă[(^eN'%f 3-Z.LEUf 3\r?b&5<R-h+J}?{+*+Jh# &^12FrRsIEvRGmYIq+*'N*I:W`F;Z$)5RKTd)u^co1\QIRlmwGiKyT<Bg$)TtH8}Zvz61J'%J*}FRRfJIE#;<Z2R{`N'5 HUq$bCyEa"%q-mc.QR HJsV)HJJMu^6b|E"(+zB !cPlu]"X|lG|Q_~8F"&vZ.=Gi}>B^"W%GEXbGK[H>*hG)h(6WR>*9"aG4iJ(r )WRB*n^z ."&Qel'-Qtu-(|%CG%,^2aZ$R۴DH%RpEϩDBoqR 8[BDHKK}/c'I}VI•J'|TB'5'I}9)UrR"Nbė Ҥ%J*-/n))B_I)DJJEH睔cN*nC۶iJ䥾+n^$/w\IyD^J;_@F(JT&uı-HK"DZۄ_Ii/? wL (-})/R+NӜ;ZRTRZ*9%F"FSaZBrDLeSG2)nx{e%P*; 2J 'KT&/-\~)gReKe1<L^ȝNKTf/ŝ+LZʤԝx@O!"n ɤB%Z**܄̤++-Z*ޛGǖ49/RT2i)WVZ*;T{4DDDKZ*!hZ>[++-Z*cݓ~(ޓaILձۼDLeSE$압1J͋)0-Jcpy,ޒy̕$d k2"aZ&1ױ1QLmjKb5'..GOm\d6^ҷLj51߲DPT|\HPT! #>@=Bjp=T.2T\{ePrG7<o!CXK UAC_y~r`rq2B] Z|c޲RT|y;VxWQ8,Un|#OUS坃BoT J'.iE[pF!Q5]" W|̠;஢DUqBI4FF<8PJ[y*( DU!Qŗ]EBJM|͝Ua:@_j],wK;_EBJ=XN#ODl4UgulWP*CUg*t<*DOUSA4UT$xϔr%=Pm\]*]xJz+aTuTPUF~ ħ<_CGr;UNB,WS[S/[z<h DPUT˕;nWUR:!:`K$UeI.@$6 Taqz 3QZS~cǮGUQE>uVrTClwJRUT*[G# y52( QTe>y2`c:-!kBfwy42+(dGyA3s,fԓ<*ݘyY2,ոNl$.(dGA3 &%Xfe%0\ͬ̓&5k0d5C*2BvX.z/S8{Lj$4ڮѠ_A64 /(h2hn=/4c: 0j^> ڸ^lˠ'T5:1ױ5lcĩz1 Iy ؆T,913ÞӠ7b^6v`Ip0p`t`ǁmӌwbFL7`)`Kďi\/e!6)6۰JE\"i߳H%XUi\'dnXqE!{$6]Fk$,4``GmT#f_ۛ^ 51 kӸ^€mҀm=fTM1mڄYi\/d!6)6K)##lF~ ƼFmB4__Gm"pL3 Oi\/d6t8̨DcxaQ_P_>Iz! IyFFJ0_4c!5+qB+H<+9E1BH|y s Zb^#_Qz1  /#dFx/dj9ZY^4^Az^d岇z2%& z }*ht_zEA{W '5?N3CmvzeM}B+H<++)D\i&b&:}jӸ^}/qbՠ/|-f ˚w Y W %CF%%ǫ76ר _-sKY W %SƶZ(LküF~!i\/f!__~:3I)3AR)_1_Wxq R X%Y8'Cig:krӸ^~~;'C"  bk4P_T_;/ޱ5ea-q!@׸]Ӹ^…҅vjx0%Z?JV/:Uv ^uX.(j ;6y0[fT7EF,vq͗q;;fj盼Q2~/5N`'kE-!qbޔTJSmnW/5R@)NN8SjFP88X*@'Ɲ/(h;Ȣzyr14hJ5F@#V?Ӹ^ˆ҈}hMAIT By?Ʈx-c֤>앲t‡҇갃؀FF<=N3[8r\c";F>,s{Ȉir8FS^c"Wbшq3(Js82Nt|0RјF앲hxyE){NGFt["eGkq1TMy (^)9vD REȈZKlG2$bFFg$F{5",{,*"0Qpd>"fGacfPpd^k$XD YUE`,/(fOcHbJa f,%!y05+z,j"/VQ1X>`F=ʎW ȯ> qR~E_2eG_0dT4c:KY~{vOz) ᔱJ_4c:KmtF~E_Ӹ^B~E)G~!Q)OӌL`iJ,ĵqJR~%JX%/fJ,KkWb%n=,W+yW–]B $f@͠R*gOkWb%.=,W+yW6_"fr MkWb%.=GT.(fJT̎ARL{Y$X4߰?ށLk Xb&=, X,y X,tgfPwwj,5,b,I<,a0Qy~fPwM5 S7%`YH$%XHDM'B*AoLX$,(qQǂ%S`A~a05, 'J2rxD~J\ bINSe`%2 RQO55Sƃe1i\/f`2)aR1 Oq҃eعK`4cx5, .˨`Yj`q;$2*e퐙$ns^#`rKYH,%XHCH$2^p`J- Q3.1`Y040`Y1`Ĕ`ȨGyzf;5, .(/e`y׃8͘wkWK\迊_Y/?N.;cf5+ -g_ܘ2b诌DH1g2rx(BF_]*9oO(e%q^UW<BFH]9U+tfmXJ[b!Uz 2XB֓> \;{1^WWU^=_r5z[UQ}b"ԗ.(fOW#"eFkWEUî&=_|*̨kt-F~U_ՊqE!{(Ra5zEي? ,z׋ňQt0f: )z11bv/:_`_j$6֧k Lt%04?|QCԉL>DɔhpٌD=R@]5U}Tf fE`ua\`Jt}2|rO^5Q &׋uQCYԊjG3^=)BWk0d^V MQÄ{ M>@2,tU-j$|čcAQbPbqۤg3QLJmDUaq1jy ۄ {Oz! IyLF* ĥJOo3[;  繡e%aY*IJ^fǙdVx_5! ƫӸ^BmRm]fnbc°}znk&.؆BnQGmԶ$B~27K^51 vE0ޞ=:lÒ-Ѭ).x HM>l85>l>P0a}2 ͖zoCO$]ذ l!&amdžrTtE{WrX/dsBmD5+:4^IJ^ ,B|1S@x%2^iJl-Qx%<}(bf̨5r<"Ӹ^x%ix%>z(RFH auX: V+d$]SO5ŏEzgzQQbXT *1>QwEQQtvQy6STcqwc6zQbXZ-eEMZLFMiN b5j|Ai\/jƊTc_~:5駭p룇asb,‚#罌=Lm=Q1; -h5 +FAAZ2B2]p0OfYSu ,Ȋٮ(ȸe\=au)$Ϗӌ{۴kXe5VeȨƸe)0dTȎⰊ*m45R0;YJ)CȨ"+ yêhy2BTU["dGq3VYUuyEqE{ªD$2*q1oK\fw>x֋UQˠxy2kfJG"^b> E\(heE2wč'm٥Dv}nCL˙]Qu+1.@ wjtMʤRfe]k= œ!(sU`K駼qӰ^컌ˠ|ycf%3$42 /(d^Ì"[=%b\Q&`鵑zkks4//&`1P3荘hWk\/f66^2f<\BLYćg9yH}rVk O}.(gG:%z#5Fs^6_#/龌=k\by~fPoXktMd^1g4P`T`GmXQðޱG*#K|Gy-RdM*[\d\/k66 H5.ZyJ02SAqaW|@4aaGmTDĨ 7.uꚐI} YMЅ66 Pb=8 +8l\/f!6)þt8fT<;lcf k&faeIPο_Mm=R9;FjFu^cW[KG+-tX:,xtXpԀ!#b{iɰFai\/d!Âa# ,ѐט@&li&\FQȞ"'2C|B] $<:Fa(xzE1{DXptgH=5z nc^#-GFW:_FQP2)Z/4zg`k4X@ JZ,j0ĺ=,e70L1 ̵ƃ`v` {-b;ʰ]Ȱ]ʰs rGIGL5IOF(^A؎Be9R9#$fb WK9qb;:W΢`G'\FQ'NU'3R3ʿX+h1ъ2X;tBōEͯꚘъb;Z1_FQ+syQKXZF(^Ar/(hYo/4RfH%MRlvbuM(ł<%-.b;*`JКQk7/{;z1_FQdXs(O1K5ﱤר՘Qq(JԘL`J} f5nlnLэ<2ؼBHLlXcbEhxwE!{؁KijY[cc;Xou㛷.(f;PUCB~"$Jl}kJ?Ӹ^BR%v`zg#^6)kJ7E3*1^FQ̎$zkOkscD]3 &b\qtE1{؁[oS"fdvfTy35>` 4(|!}aJ*T )x$hͨNupƆlÊڛ<Іq( cl*|{b$!aG52`VTOe( S'v1;itv&ƃj}ev 8s;(fa5^cW&=&,AŎj4QbL=giF%Taؖ?,TX*OsBMdW"=SW%a\@vEQ{|X"M% <\ԓ)aĐ,kXBNz9 !!J3 NAO&G:qbY81ON,K'=N,cu7y'SFf^cŲbVﷺ=V,cʎ0Pw5^, /="(J2vL#5y‹4bYzL^Lm?Ts xo5Sl2?IK5Iɤ@j~OALSJcY1U՛ю՛QǎerOUR46')7kXfNz) 33<)OBD'FqcY*XF7t.(iX%rf̨15R, )V(CuE{X&gɐJ+ƞFĪPbXJH%V _ߘl5B!ƗnBH!V,?-|X>x|X-.3~0 b15BӸ^B) 1UATH7q{CbXA6xamXŒӸ^†i 03081@jFb1&IVX!I 0V+Q30!Ƈy2e,k W֘&,ς&'$QDŽSjQE2<s7e+lXЂW=i;3f_VX q+R+FaȨ2֓"mVƃU`A]jSуT+y0YWB>kq1q`XP*:06*Q]~\"Jy~f,F]*+U|q?,=+&>I`H̎6<0cUYJK㸌Iɘ.<4"UP݅*/2bYz !QNPɀ^s~-NWk (ε˙ Qt0gzu3Cj r?ί隘فE.;( j3bQCStM"ub%e(3Se%R&3B #)zNׄ,OqـE!; AS6 _51+<9(QfP6C̠Η9 H_C1/3J0P]FQ̎u]x j5WM0:_5)qفE);A3y'Cꝍ`%P\|׋QÁ!qjuVͽK'z9/`sV/m`|e`?01iKuq^"> Ƭ`(2bvH0rF&b&VE4p`t`ǁmDC"ev |kyXUqҸ^BmRm$T{ÆDHP"hdfPk&h`LJ8;e\'h QGmSA,y(,I x-R$.)i\/i66qHN1$TcP[=okT؆*앲XVm‚PaTaGmhziH6TaБ"uM̨^13{RmdܭKPaL35nlQa!zwKYMͣ6T`H}q &6XZUXq ۤ <*læ["ebԇyzfL'cY^6`qBlO_Di'C*AtMbY,<&=8 iPOi-gx2C)^FTaq RRa*ݺFJ`P\8׌<ӽ+zIae+i(ø(2ȰZFH !#SꚔфE420/(eǡH:yPgf9`5XUIX@ K(S|j;Ѓb,Qǃ>x(bocupxzi()n`K# a lvޠ?,LX&,xLX@şS*gI1U؅P]W^FQQ3BjM8dІűȰƆa_5׋Yذ mؗ/HRJ^dFFɅ"ƅvaFB.,H<.,҈S*fIl`Ra} Y<-T.UQa;)uFJ̶=#5v{_cv6ahg\/haviv ۧՃCwt/5"lgVXuB>ۥ="lwtcHfP `kD":4aaG|:#%?+2A/5.lgVхq(Jv<A k}YqOqۥI3I*Q`/#qZ3DlهqQqۥɇɠRu)qCÆVk|>kڍ-|.}N>L*D<8۶GzYU칣(JڣvRUA&E<%,'3b}+窎5N`'V;˼!cvE!{[aKita'vqƵ( lE-|!}aY*q탩Sf*uMlĪƸ^ˆ҈#v%%ɐ+$ɱFoŜR::调u>%]C5jRuMҨҦ=\FQv`9LGm^u+hF Qs8`J:fTLI\"갴Ξu\FAѣ"-ub)ԌkXD% Z,#*1=J,ST5Հq(^Ro;=R,Ny2#&K$_+5!{,lID%{\QGE,!cӒ{q^!vD%Ƈ.(dOX$MŚzѽ$tXI\"WbQq(sZ2bіHY]n>kLXD"׸R&,JɄɔ5q!.|2͠K;>k\Xd}d]X.,J=.,QI!!Hx Q.I ׈",U30^B^FQQO ɐ3ih,SqREOUXD%D01ZP7,`-XVǫZ0F2 NÒTT1RW%P,QCiK2NzI Kd7i I'a{iK,2WuB( D?LqS U4(Ɔ%aw{NzI  Kddh:9ߓ11NfO\Z+&Tb_}EI{--0b$&)6^#K& W}_FQО-t 5bj2Zl,FZc[1 (S#}0&c8%6khZw0{ШŲbIjb {B#>F'kXb/VW_FQ/PWj5dM5:I'zKƊt2(JڣwW+OYgXƒkXf7V$i\/iƲtcS!nKcfPg.6x{^#24cYLbLLJ̸Q)OӌO)Y>Es )˞갌zK"6?N3x=>Oq\FQȞ80 yzfL`$QaUXQ'3"TX*,{TXFAJ!%&Hꕍ%;5*, ƋӸ^Be²GeZ !8 z#5*, |i\/f²Ta٣26Z-3y(e(D6R0Aq1~7Xh`YZ/?TEju`5 ,ZɌ n\FQ̞C)cfTobWoYc :V4`|~2 .갂 9yɌ!ӫeF_Q.ՐBg#$e@͠;^,^=Ua[y B*A+8żFT`ŧB$`ze;cfP'6e+(rq+R`jVPM1R3ʿoQH:AK VHɠ!32`щ VX(`EJBLLj=R@͠75 9,$Xx$Xj0Uƶɔ妠 }ט&,Ȃ& V yh zjua5& =Oz1 V +bB~>R1 k(bv(0@͠KukXezJ ,~VVyz2f8z6]m\/e6`FS )n`fH}0%@0i\/gv`FQΎÐup C*A)Xzikbfi\'f~.(fqH:\ǐy~f{8]3[Ӹ^l̠^!.6sZ7kbf y 43[0(f3dRX>]ZXUxzE1;A}>m`A>K,篡bf f`M<fT4c~ 9/k קqفE!;g ӌll<]3K%קqYE9;$A3 c0/Q`Cff\/dV`FQfT.CܑB _X6,ok,si\/f66LfHLfkm&fT`o:,&F LƌE̓޸lsnk؆|%,I9 9̌dϏӌq֥kBFUCQqMe_,CO1W76:Ioy/5lCVMqۤ<l,|gXkm&e`wuQq(J#6R"d f05kCUz}g'Q~mh!@ w;tM^1ss?z1 I1D2:32!zwB^#6_EBBkO_(AH=5,{~Cu5__~m$Rjc63N'F|_E#uKX W/~0%jPJq".{KK#u Z W𨯀Kj$-P'Z I44=+ō2)4$Vc~a MRq R `Jy$1 X6XL,H<,`i!iƼXE% 02B@IQ!Of%F_QV6_A_iF0 5+j90/(d (al_ץXEqٸ^Bt8fR!32b1P׶  kX`AeQG |U<>c5F@#Z͹шQLj$v> `>P|G3NcRX#b| .(iOI؁Zju5=R4IX\#"J,XR,zXDUDG1ˈ,})YjZRWeQGE,˃)a<#5~ :nA5Z,۸i\'$XZ,zJ"1;*it_LZ ئEb=a*:Č[$ۂ JFEbi\/hŢb u#L!ƓqƌE6c8%-Xf,zXDaώ1@ kXd3B<,Xf,zXD%6R1 1q,6ގ9,XR,zXĺ- ?q Y,WY"iɌ W!_ΧNz! 5<fH(μFeV`;w Z(,X(LjJ!b&kXfvX ,K= ,Or=uAT^2+0qr4P`Y*/?$OӌoLyl3q~exQ])1R3S5RZ5דHU6Hv<;Uh,5XH V<&=?P3߾aEXV7"aU"EX!&`(x)FBވPȰ2, ʰ*dX2O&I;A^$-،z#5>V> V+VDKvDJ D1X #k|XeV i\/hêa*v9#$<$Dذ66 V VQɘ`<ݾ`u rc̉?QDžUtTy0%&ܞQv|BJB,q( 0R9$jy Ɔ11bXFHq҈U2b2f<=N3oІ56 +4aUڰJ6LLJ !BWט&jM&,e1a*n)nd͠7fk\XeVT%I%xyEA{*kqÃ)3$61껅au ŠjWɅ%׿=a 9 iRFUaE Uag.(eϽبBFtHnyNWk0d^M %Qt0dz2C,{jO_֦kbANz13bv4hJyzf2]P`\r >QDA3B Cp~Lׄ,WBfe3ȟziƼ^"> S/z!2Bv4$ϟdF=$%`l8( Q fPg_~E1{TX ? y~fۼ`;{M3``2lHy~fLHOtMȬ6mٸ^BR/dT㴙qz _;ۯ:$|K{׎ *GIiž~lvRkkدk%+Cbs  wkײtMl&<,.NK~!Qq3sSr/5kg%>e,.NKfLFJNdT4 f{u;qڥ=k'DNXI?PQcLdkk^qڥ=kG#v/@ }Y}Nz1 K1w0b}}/(g񬢨CHR@͠^bƃE`ScF/(fX%B*AO/5,˪%XD g/(fO-XD5fG-cfPg\^iK 7=,X ,yX^22"q1KkXbV /(eKآKp`4c:)ױ7vZ;½tO:!gt`t3*q1]8pӸ^%ǁ%!;͏ӌ|XvZ+ G*0>~E!{XX`F茟HcI^Nz! JB"Bv\ }~רꫨ/VNQ,F1Jy~f֨w Q}q(s2kedT4c:ۏypF|%_E]KP|qS( s 2R(kJ("( ٣!O3OfLkr^c^1TFQrX6JL' Qkph,OOz1 핥F1BHTÐ2wW^c0_<_>}.(gh !8@ⵝ hkWӸ^}e龲d6Esfl%ˈTut# _rEQ{ XvyɌzl 1` XU.;Qǀe<Ш>Ӎb q&ulI8_i\/ft`2)4dF=h>5 ,\qKY(,X(LjC*fH}d`%XU}3J"$XOcF9%1P3>vA^2k*WTe`Dʎq1~oC!5dW1Qr! S{Ӌ/3C22 [|[5{,_-Kɂɘ=}cf1`%ض#%XH V<qFt['3IgyW5{,HA V+ROX^{Ύcb$1+$Gg z+XAV+R+t@OT(>HY# JWb\Pucx$X!77<R_Acc1`%,(`EJBLNHNɹ6B"fb\@vJ/k,XA Yт2bXrJM(SG QײƂ`Ւ -O.(f+(LpFFMll\Y# J-ZW\FQʞF`TV=wb'kLű5U]#*ZQTaU*EXUS~ 1P3 cRaEX{gNz1 VVHU=%6KS5* |i܏AgQ404 ɻexCf`uQ/56 |i\/iaêaSVQqwSzwPҚ:,>ktX:LaUJ:L&CP*iTP%#ױƈUaĸ7i\/iaĪ4bc*R_is TuBqi\/gĪTbգ*xC"gdTΤF,5JU[JJ,͒Q-Xn]b !QsOG~tb^*+^1˼uE11!\$=`kXe%o3){\QGU<"H=T6x k"_C1B̨~_:83!6@3ǘؚH]3 Nz13bv4h<1>~Oׄ2luauBe 3hYF%RƁԉy*ךkMl430(fG]A}.bW͞As6c1/`cVO3j0^FQ̎0& 6̨iƼ1Zb> Bff0`uvy͐0@<6cz {1/`cfm\/f`FQ̎M{9it >K篡ybqKQ$̨̠it˥}׺tMl"`Oz!2BvԙgHR1ZWbfe8iPMCayڥVKbXEl\/f663#yZ WqڤH~ mPgHL 3#f_ۻk[#6_IM6_(2Bȯmc=̓a87,J,]2/$:,&3Wmrnq#bbfbqjW kfE;mzA Iy׆W6 Őyfke&f_Y6__mĖB굍iFkTsnk+bkkQƐ#dF,C.^K5)굮2/}E){6}zK"x}Lפ, z) I)O3B̏ӌycɼFmz+CF/(dؗBHMaK [ΰF_Y1_A@K-´z0#q3al%د+Ӹ^~i~!Q93N/5+|n40. ̓V}} >kW`UT A@pQ|Q"iɌ kC^c[¢4^AZ@K2O>2e l05+*!(cc=:iƸ{kBfUi\/dἂt^Ňy2^xqƃrtMȬ ?,W+/lW03eHr^#KE9P@G.(g X^+":ZWq R{ ̓({'kY$坭W˼$Q^;%x0ŕO6VՍz,5kgUӸ^}}_K q avl ƼF,*Qq(ScC|@<:͠N`׾Ɓ۬Ӹ^ҁ힓;rv|t z㭽Ƃh^1گ-w=_; -tFHq 1ɂpX-+f1т;2bXTgi=fs 1k<؎;z0VQǃdq1P3ݵB]37b&l&l'&cv~DF8SWkZ 5!jv`ܤ2Bh0 j  w=,4.5`;)av}͌IXT`žŁ 㸌ϹXI!$"v TdX_:qO(<(2 ASuz,ׁz/(f:HqQ4r"ui\/eai:}05ZXc_j0r;m/(f:aQ3گ.~ubbدhJzi3^#_"Ӹ^B|R|uRŜO=m=ֈ&3__G|$q@O9H} ǼF}6qq:Ѣ ̨ Rj:Ӹ^B}R}u`7z4dF=+kWKG_Qد(WElz :?H̾"V v]kWK\h\/fa_c~9'CiX5vk]&faTƈC=+lDȨ ǼFE_AmYD_I(WHV  y~fP# ,_WP"/>xE9{@F, uL1OAqfn7q`X`iy׋Z8(X$&Ʀ\WOFFk\Xd&.,J=.,* @Oӌ65&, YMЄ 2BDFT5z9ڼ5&, S׋Y(MؗIPmܤ7{NfT';_5I Q%!âadLsqYă)q7C;iF#؈" . 1b ˴:=A{FjFiKB XN,I'ȉɠG:uzy(joFuZS 5Z, EZ, -Kdx(R̹'C:n:͌Ƌ%^^FQΞ`t%Ȩyzf&H$XKR_FQ)PTD1Rccz1bI1ի3. QG%V[*fGYgTg? S ;ڰ,lX6, 1!@N[^klXbkj=daYذ$mX"&C&IR! y KnF .(d K$<R! 5,?Ԣ=(JSHNTJy~;Q`خ 2*,X ,{X.:!;j cr^2]迲_Y_MX.#R1tt{)q_׮Dft_E,WK_1Rcja^2]˨P_YQ_۳y0bvԘy,vֺqR~e$T6B*C!Q_׮nvͨx2BY"c1s{xkYi\/cὲ^2( ;c&׈,7M9,W+{Wƞa#R14xX^c0_>7/(e22*q1N!_Y/O4_Y)\T515ͤOz1 U*UH.яC"f@M@)Q_E/4P_EBKYFJ~!bvٞF~! U* 8͘75n*辪p_E9YHqSI|˻URWO_Uد"W!%ƫ !Uaॐi`E0n/qY"X!&sƊ,4dFBƀa40`E1`cRϲǀ148^c0`|.40`E1`+D)B*S^c0`|(40`E/?N͔z 1Pc(Feo^FQVLUÁyFjL >u?,XzXs()ghk,XKrJ Vɂɜ2)1Rcj]X*,RPɂ>{E9{,XE;%B*AӏuB4X n.(f`e32)~% 6ڱ`UH0՟+|2bf<=Nc&*:ZI>fuE{**)v4DfPo3ƁUv`:3UɁ.(gdjD=Pcۘj`5ءNT`[]FQ VI4m!R[Cq{ lŽ&Ve1a\t$H7cT%K/5* ;٩J*?(٣*NTh s 4ky"_!62{0!`uFfb ^734 k(dqqYE!;$Ay͔kD Ā} q9( qҠCɻ 3Pch0%`쿌( qҠ&@ J#Z_1ٸ^쿌ˠ)ɐzj D}  QCԙHT5C^b> Rĸ^lˠT!!@܀_!12/(d2W2VQ`S1/^cf!b\/f^FQePgJL#5c^> Ƭz/oˠ\V+y15|m|u>qڤ-Sj$ެ`j05kKi63l}E1{F~IR)cz߳_ ~2Bد xߑ!᱑ *׶}m}Kqڤ} Yh Wh2%)cX u!oeļ{>H1Pch05kKtB7^^z|{!RvԘyڅ:{qe^;y%`HLkYC!^^";Z/]FQC~J#qc^v^Q{2bvt7/6RoAP6tk]&gV_QQ}2rvt7h2gGcfЏ9:LŎmQ3/3o]D6_ b>>:7b^v_Q憎xvE11cKy4dF¸/yLׄ+Ҿ.(d_2dL"dcqZtMȬ\Qq eLKRJHNT̎Qh^k%A,`>Pq*QAL}bR{ɐxitc̡vj@7\FQ̎K hjFH<ΎAFd>PiQ۽A*k~f̏!46>(XBg^FQvی1RcjtM4p`t`903CL#5z+HCH0.Q<-$!%ALMn*ݓ GR[?k4!4R8-4!5ALvJ@JlQtEؠ>ֈC>(EA{S\ 'C|w >:;ذC0qq ; 7Y*F ꬟CbFƷ@\FQvR31 әj:!5., V.UDv_FA"M䐑!Ϗӌ鄼 ϸƄEa¸i\/da¢4aL4GUi)MT5., =jO:A2E<ȷ,,tX:,ztXĖ"e g5*, ,B*,J=*,R!F2IeI^"k #j0v*Q,N7z0q1]Y˾ eZ0dT4c:^k/5+Jj+2J~Eit8dRkB*Aoد+<,W+zCFRBbFNJ%q׋Y$W^J,OcfPic/ƀ%6`I7:.(fO-X"$ȨiƼ,_Wq;翾,WT%lGer \;wz1q_W⊈Ӹ^}%/3)),;8͘NȃiJlӸ^|%i/2Q!;8͘Yꅼ|%6_YŜ|ea4_cs fD X"m?5+6=%` sR?0'3Lc05+>+ JddK}KČXc^cN迸eLKƌZJ}1P3ǘ`)AZ.M迲_IJy dÌMDگ~e_Y]֛~(9{WvT3Ofy쾲+}1=+D21b-Js ׸+K2/p2BT~ekDȈv&5z5q_WVet_|2b}eO|xdH5bͤ`}_^2믬zQd_,/(gʤ_4c:<R3"W+! fT5:FeTʤdX&`/@͠7f`kWKuΨ_YLKƌ&/ ~0ťkOxqۼFe!TK(SImӋyB݌7/(f(T̎󏎁Ae+BX HV< vD̎A`7ֲF!Mi\/f!`# O3B‚!o IײFo4sH V<%`jFHE̤Fe+BqӸ^BDGR/A)Xoۋy+‚qi\/fa`,%NT5dVSM z0nuE1{̓eFF?kLX&LVd0aU1az RmH,+y „LX&J1P!4cޘf`%XNz! Vj]DȎbq13銐? Kfz!3ןlPˏ2Cgf2]3 ͿOz13bv0z_Lqwr7z-N ue8i\`4ۘn[{58_c\/e6`FQfIC8i{ZXQqe8iЄx0RfLw<k(dQg\/d_FQeP#KfT5FK^ʬˠ= fT4c:*{~} YlL Q~4UǐJy~fP;ދy5z~qw(a ,/8͘7B^b> /Q~4|30fB~kU$M/U;,&_eedwyv`cSmڄ,&_Vrq]C~mk&ܗ|43W_FQ̎/:bxg@˼I4TZY/>u׋YȯMʯ#6rR ƁLk0]_| 4__G~mX%RYOӌ鞃Ly~ uKYدMگc RȨ~jQZ Xqڤ,!);H/uJDʎAԱ~h^1w.(fO1ȈׁE`X{tM(^)ͩ= )%C!q1ȯ+e1>P~2RȯJ!3/3V tkׁ8~b3zE1{CF=̎*α5@p'Ӹ^B~R~ubC`H=8`H=5:Y~mOz1 ᘧ̨iƼ,_g Oz! ʞ.;Z͏ӌ)kWa] /nx!*hAH9 c^P_ςU*U:̔hFЦzV9UӸN_}EI{*|aփ!v`F!Ӹ^B) 0@ca>R/nH(aȲF!8-$Xx$X!76?N3r\ y+B3q+R+g̨itX y+B *0.=  8͘7&kX۷BHV<`Q~9cfPGc[Se+B2^FQVhz1P3Q`E(0W)eQ`viR1 ul_X*,j,Sтque\=ȈitcOs]#`ܽ4`UJJL< ju€qӸczMs zX%3n_GH zUaR}pxRKtAR3dˆc& =JN0>4cKo+1{0g~o4;'`75c% 3R!wo Eq vf9vsCy׈Pgro+E$ Ο0p'Nt,D]SP27;[. mYg]P&oj큞q2\d[.YU`7nf7q;JnUpnGJ~±lےvQς8sٟ&#M5|ɣD{0p7 Uax޺|j7NKtϿS vgvsCl3RSPoEiR iS9LQfvS_B3Pܻ_ֹgDy vgvsîԏ)0{zvCmaذQrݸa۴a\mSjE1Tw_6 vWmӎms9mnɇd#ro}[.[6qK z0na{5Tc^d6aظYya۴a\m#ɼGj7cu좍oKEq Mkƭ ۦ 2ltC@2 uA]k.ƭڦk|2ɸ盕ݐѷ\w2`7n%6-6\xɸKHǽHmBnRkVk57dT6-rkpkecGcø]nm#T B)X@\p|Colnfm=o2k6kˬmkҵzCEf-Y۹nʬmւˬ]!;"ث)W0nGT+o[vB8Kn-,rk. `/]Z #:4_`o3:ɣ%,k.۵]ٵZp^y 3{j7yw#_d]ְ6l|9q~ H~=-aE-aك7؍[ [pZܣCr]8;Myc ڱc dž{zvCP_o+EqcEƭ[Ў-[ %oOHy/rl.ZN`7n؂vl_~<7CN=kMu^n,[Mtnʲmق˲r_)P:&c6}[ |M&nʳڳ.϶O+z H=;^yƽȳ³/;oll;y6~ !xϏn#"Ƕ vΎdaܮeZ~#6x(pS]:8c)](?_%Ί\7 v^jA|j7I=/2l0llظusm԰!$q<##cO-s-[nRlVlKL)BJm $o[/rlpl|`7qvvc۱y |z H>Zٴ/llc vVmגmwI}CP>?TΪ:]lu vWmךˏ'pJy՝4#ȳ³7M\y]{v1tgs nꍃ"v&`7pg;LڐR;j7Ici;ij!~ia.vRPNcW }[.[6^QCe_jc!5GGHyωǽHBe:gf;f;\ qO_xn*cc;cj~cʱڱd1S!(h!+zd![Vd;Wykv1Ԯ(BrFmxX$!؊*b;Xq{0n`;H{6H:`ۨQ=C6`WF^7 AK $c#v@ݸC"_X^7qځK.=~R 2Ѿ="v&=X^7^5Sє') ûJgsx\עk|yĕ_گEk2HKV %n0TQ=ѓEq`BnwԤ )q;gr:*IyӍؔm^$ײk\r`/Zr-Zv51Tw_ֲpkfV[ڭe[>yOnǸ/&ˋZn7ƭZn-Z6^zY2#/kY5YYu.k˞[E硧dxX4 ϋZjM֨fVk|9Ԛ{Zx=$oNI&6ܸ'/RkZTeVk|usKe] znꍳyZˬ֢*Z+Je־xCwkj |~vSo{(Zay^+Z^+x}z‘R;j7^ ‚-+V`+.VP{Ta^P HVl[7 *Vb+.V̧RHUJ *"VXE>}`7p؊Vlť /F'H [!AZ)Š-[ +6 rRlmrl)(B'VGO EcŽۂ\7 tvC?$[aZl9UV}C@r>=RѼY",he a.VH}nCP2MuFjYيl<=R74zY$يl|yݸd+Zd+dTA2B2 ֣.RlU(6n{s`7nتVlե*7VSd=c=AZI*${`7r%٪l%*HS)PyI*$ {`'^_%[uIJKZUdCz^I*$Md>dZUdɆ{zvCoHHU!q+Vd.VI|-{zFj7cex]تPlيVlեت !Hz-\hދ[M6uRl7|L&X`,[Mh6uFl&"&#wn%>4[M6nuFlKx3!0cvcL*T_Bt`7rڪVmե* 09oEH͢?-mv8G6jn/)q!a 7c{t-<n³y{@S2MQZ.J\X6w`7qaٌ=ͨ { |zvCo'ֹM]m`7o،ü=ͨɼl WkބcV vVmӎms9 3%gH6lip,J-[R &,ۦ-lt>?R!2wQlْ:``/n> ZԨq`T l^x0*i5[=&4ۦ5FM'NK}硧d(l[$6lI71llKmӗ<$ zc -lK7K6>hzl^C➆7Ifǖ"q+ǶiǶFKNRotP:Eluk(pad[`+] "e`RdN."`eMۮ[Ђ-`B5R}tO5W{{=,lAH6YXn#|`TǩxLx,5U$FoE.Dm ,^7hӑIN=Wcɶ"l;#Fd[в-lӑcylcvSN>[an B5 vW-h7nCH>@ tXƭt[к-t[@>c1pȶ¶ŝmo\7Gy.۶R :mʗ}kۅk{J\ۮ]rm;c O#&wn[4"߶ 77؋us˷(p cmZ.\7nʸڸ.㶣S)ng|oۅo vWm׾m'ߦqk?:"׶ ۼ vVm׮m'צ㞾! xOn荂}gۅg vVmמm'Ϧ&6HqOn /rlplaq+ǶkǶcq/sb"öa{nʰڰ}x ? %g؆ Emgǖyc۵c]mG]&pOIcvS79Y-[VM\YC[ei#;ɎE`˖եvVЖpY̗jӅ{zvCXVlY';XusKx{z H-kc^;Xey`Ƶa.vJ"%M'gvWzzp鵃#$PǷnz-3'5lnvӔ !)AzmuHײnJZ.v`4G͏n"v^˲j`ƽø]zy) 9Y{۱ȯ¯~l5}kt1=$&HBɊŃZTzz &KT,aMjOvMuC{=TzEz- W 7M\鵨Zt鵈?!0U"RGTluI\dآ0ln1aʰEmآ˰ES3"ԶIsHE!dȒ-)d.,gvc1/mQE_vFmWG9j7NY\dڢ0mr1i0qil) L76"kCή¹nwi!;n5oɶ(dT,ےmQ˶m~=1TfhȷEdjdm#]-CP2MufmE- &U# 79 %"i0<%f8 K}rBɊ-)re!9i_ $[U5pKJ%-ܒK ) O׳E-m+D9mKʶ%mے˶%4`) w nꍖ]ikKڊ,QNڲrmIrmi^d+m΋ȴ%6myiKڴ%iKأM#%T襧/mE[%Ewvnw!H:/0B_k"o8Ix9ۥY/t!yPgK|9I4[bVyݼfKZ%fK.MlMQYĖp+˖eK.˖H} d#1GE-c+^nʱ%ؾx(\"ÖaB vV-kÖ]-c#Gj7ơȯex3nʯeײ˯eOG6yy_¯yݸ_گe_$r !G"_35hnvK= =Z~-/kY5y0^+JeײKe^2i) 7{_¯{.2Z~ˏycxދZzM;ȬX4]7yZ&%ۢ#%C0+ZaV?TwSn/.Voxl#yOn蝸~ vV~hV\~i) .׆{{Ez^nJ 5ƃ*nOOyP]:gq/kZ-3n&FQ= &OE QȰ6lUַ6l>9Ֆ 2re\aH [O7 \ [uZ! GH=mV* lت2lU2lu=fl Eu`Bɋ+ [Ղ[TCOɸzmeHUd͊z-6uszRo{~vCĽHU׸lnJUתKU)os7񂦺ȯUk'w`7qתkת<χ+E"VѯwE>_گU_dHPXa ʰUm*68y/|{·Ώn蝸a ʰUmؾx =$ Cilev8L4Nc6qUS\Ug v8 D|Cyj7m<{(pUd`7p!ڌ=lFup1\g&.usͨ'Rl1c=љ|k=pv8 SfTê1&w6HmB vWmӲm#&Ȁ/|OAiU_E -#&lۦmm,ǵ͏nwy7Emo;o7~\7qTK8/;"Ѷ 77 \M%6,3TboT-:"Ѷ m7M\M%62YʼCO)ѶaEp܋L&LaƭLۦM2mI+1VNqtȵmµyȕk۴k\mCǝ;j79I8xk(raTM+۶i۶lF*WAj73HmBFvWmӲˏ'ZDiKNܨmK/"ݶ ] v#WmӺms TcCz#ȶaR+m .Л)Z!$.1蝼 \:)l`7oڂvmVRw nٶ l*w`/p6a.OC=mpl[M7mA۶ms(S \/j;/mA6/nJ-ۂK<թOnx,l t[M56mAmB)P |l B+l .PURp!qРw^dڂ0m|yݼi ڴi X<%M"iS- L[Цˏ_*o RH:e>L vV-h\-UX6Pޘ,ΖmS7  \Y][evl\icIm/m;M5y1mmKXjH m"Ѷh۸;y%v-vhI~񿽇z JEH,6s`7p%v-vhrnj0>cn׻SM|kٵmܢnʵڵ.׶M}>&?vvccfc-_KEo۸QyЕo۵oɷَL"뎡Mu*oHUB2 u aZ.[6 UڵtX R2M"v&{ڸusN:oYm~vC Efl,&ø=w"5q! H:ӵ~E7EM6:Xq70n`;Z!%To6/2l0ls]7 mrQlڍuj.;ICH6u)ȕd;d;\@%;j7yv/ll[^74AeG9j7W=iCh6٭`]ˮ]jr)ls n (DY1wI(R*pP:Ox |hB^NE[T-j]-m*pis nꝩz\ڢpmQdזkڵEk$a#TixI\dڢ0mId/0nixSQmMqlB &d[Բ-d[Cj)L|~vSZpE- VJ( ĂM\~`7o%ؒl%ؒ !x{VH%k:q+^K. GLXowQ-kӖ]-Oץ=tkٚ1T1x*,k.)8usK4eTG Hl֤`Ha.VЕ7R2MQV)Šm 2Vl|9 ܥ I/UT7ٵaZٵvm ۵ZvZ!Jc mVٵvm q+V].Vy!6Lu]lvyGUeƷ]7qZxsA#"Vٮ흅¯F(V].Vy9B2 uy;ZeCdgKü]zNd#~G^תkYE&us˯邲dޞҵvcxHU鱊zMh0o^xpSΑn)yNѺHUAzMh0p^vz J Jz-*&cv>{kPt[Όm-OZv=>okU۵/?+}z J>"V_VkB^7ZT!$&ݐ5[Ŋn¯qhFMTx u{`=[60o`3j|!1Awn譼߃y1{ys0oa336dC$8߃%ąc386~;>% qovSo%Ʋ}L\ &.,qDz5eò}-s-$nBq{$QsK2dCs.$nB{$Qs%ML "dZ. \HC \H60pd3 Q;lWW \X60pe33gSyO=$؎ck(nԅv8،*{`H>#$ֹkބc["ƭۦrl/GH=?Rz, nVM&j`7o66c0k)Ƕc;؎k(n6{qq0nbۦ/|H3> f5` ۦl(Cʯ!$_^6‚y+iFK>6?R{0EzmzM7kkKmk ɸԫp[hp܋ [`vmW-hqsaՊaa °%u,a0na ) F6]/2l0lI}w6ll殛wayᢞRd׆'k" & vkl宛]vm'<\SP2pkM=^kE 7 P~m~mwd;Em=o2l6l˰D[MdCz'EmMCf`7pevmvaI{|uC`!l)ۍu,hȲ² 솮,ۮ-NMNk/ґIQUwlT_E ͦ"3llKpCa\mnШ7N\.\?ormvm˵xSt|~vSA۶/mmnʶڶ}xmz JNm=$ƛ^mQ]lb`7no;ЀST௕ٵEnʵڵdEd➒6XM`f"vkĕk;k;ȵQez JMpދ<-NvVО ϦF %FڎE`Y(7[iCk/?ϛWysdd_lQ|``7pl:pP/tfs nc0HElQٸ9 <;HEt`7p٢l4)zC,{u_٢lȚ-)ft(+)Pz 4[M^wYq0pfXmVHj7FS\$ڢmȢ-)h.Ml#8-.lQh6a8olQkHMMK=m~vC hŸHE!zy+d$txTBJ>?Tw,[3UlْlQ[HMNKu@H==R^ѻ"er Ζ ^7y" cvSoW,[MDlus-b4Un%YZdْlBbƧ-q58R*pP8kY$,<ز\9 U̖RU낔 |~vSoH,[(ز5abDKlMq^8-lIX6"=e]-a]!%;H%dĚ]7f"=a*sPOA>;$D[U.E߯]-a4G'`OEɗ--rmI ^6^_7 :9U%/nKt[GU۸9ܥےQRu6Bʷ%m"ߖoJ$m49 J0G͏n^Ml[J¶-+ۖmK.ۖ<#Gj7)ȵed/̮@]7Gqgk(J)cvSwpO7E- &{dvm9 vCĽȴe6mC-36_7qL[&T:BP'^3n܋<[fϖu`7nٲl2/U{~vS[u_2k$flEi5[&ͦ6m*pl*pJdl͛"͖Y%M vW-k͖]-fS1T/8vh~I̒-ydZel:pשQT'lyalؒ<&ٰqus˰e^r)h'N"ٯ%yj4_㢉0n_d=ʞR%_ Ï"Y%yh4`㚉0o`ˤԾ(BPod7~Yd $O6l\2q]\`4a 1TNk^Wn [aÖxUy`7pe؊6le y/eR5a{,2lE6yaʰm؊˰<Ʃƪ44n蝸" v`7n!،ø=ͨ C2nPޘ~M547_7y{QbE!\ƨOAQvc ^E æM\60qa3܉pQCgnqlJn±{QSJ!gH(U \F_knBy{Qs[&L7PJ|e=v8Lcٌ9LsDԨ+.h߃Yk6>K|lF͕ Jh试ٳe v8 ٌ};Cm}r.:n`7p66rl:p_ 86Pz:G7EmM7rlvl968TdHj7N&: n`7p6ؾxN⋏G=%T' |bۄbSG ۦRl/> |~vSo섇E-ɦ \I%[pIԤ )cvSolE-v(XqO0pf Xc&mLF D[~86nw0_bP ! $""h;Բ,hS{a.~IR ǡԲ,h( D[P˲mW-h\-:JGJ~j7Z. \6ubnʴm6]A=׊y{k/2mA6ufnʴm68/Ǵ9j7NL[-*شnwU麴z HmlwTV-){6sa.H~])(8ygKrƞus8y6i/U{zvC6a].,[R-(,{wdcH-vCUcKʢظUus˱S/,Ȳ²%wlm9丮XCoCֈRX#:1@{*wh/8{U7Y-ۥ,r&lz).+e=7(plcfd.l˲]J}F'WcR.vlsd.v-fT;(|l1 <ȱ]ذǥ69˱]r';.pl/疷]R Pܠzcӟ[&'w9K2( \YdWe,ٴe&'wYK/.*!W2@rl86siǦkMNrl4_EbcX3*\+;Hݠ fY.v+ZKL-'dn0mzɴlni}DwJ}B/j;Ȳ` Zq'l7[۵M(na+ }' vc_'hMfv۽'=Q}~}l//dwciϯ4&vc])t Ra[;Ȱ`< qaٰ.v+qoC@xw˱cw_4&v_]~햦 k56R}R_ v_"_'hN~fv-M UIaKI}\ k75l{k7;MNk^S%yo';ȯװ"69yJ}BZrbGդE69wrM=l=̓ö$Z^'hMf-YK.0nԃmZfbܤZ&ج%YKrS1k+ }2kIs6kZb\f-m.x@@0WfzYAn-[vY5}mr2n[˞kVʬ]#krY`t3 qYl־\^[:2۳cm><̃ZϳjZf]^-+ׅݞk+ }w jE.ڪjZqY\-!{>oXd X5oufd [jE.:/!{{>d X5}uVܕZaV\Vl}>Ho-AVUg:_'hMVU+.VG kAQ^mxA j6/ګUjZqy} {{>oS,A^WE{5eܯfV\^(EEBJ}B؀\ZOuNЌZaV\^(Eo"{y V34&VثW+vd ݽRwj(ګv>oqZQ xy^M|7y^Z9 ZnՏ$5ȫUjD͸ɫUjժ$(ھy ^M|7y^ZUn h}$ʫ*x5}uV܍ZeV]^n+wDyB*x5}uf*{j3@B跻ʓ_kU` qUlժ˪UUv{ZUVmEY V UmՌb7[jճ[MBgZՖAVUñrU[5ݛmr2nUu'P a+ }T jڪ5jZuY\VRwjڪF|oqZUxK^O;AVUÑrU[5݆mr2nU¸=3Bj˧[Uk` qUkl՚˪5w(oR}RxkA^WÑrM{F^Wk.֤";PR}R <Ȭ50k8aI^Ȭ56ke֚]%yo'd]قZ NЌZc\fIIFoc¼/x[k1 [k֚˭5 a+ }' c4&ح5[kx@EJ6 3 @&ZҍU&'v?vPP:dd5]ФYKd.NL{z´!3 @&Zz!X}ykJ>'dZ{x \z+{f'xA5<́5j2/6(q)>Z럿=bmPA0֢a{dmPR}468G !9}'wTܺw͸A Nk֢!+ ~-Gdf 'Amۧ:`ڀGA?pf-@qf`'Am}s@@= ״ǭTkqݭ՚6Soq{ڠdCKQjw\;2o|r68G j{z[Z;&f1v器i͸ Nkڲk^ vhf)f}h_[&>\jj އ( :Ȫڪ4}fdj˪g3=fYC4zUڧi69˧qO}{>!"z̸lڡmڍ_HmNi۴eӎmii>i6#i'ٴmicI 854}ŭmڍGmӌOmir\0wu'dJi7~9M;ɦlM;ろK¸W<Ȧڦ͸ɦlM;G@5sчCɴRoAqkv qL;X}R\xw;:V2~$-`͸I,L;<2mz—ɴ3HZԁ YL;Y.vzd>DYo@fA22 FN̚d2tɴ# {2 i'4ݏuVɴeifFYoC@:dfdN0iͬɤlNI;?fYi4D:A3kh'kӥNOYCfA-$YC;١.v*KwiNjlPZ69K&ri{lPڠ}㔶6hz_d.vJ>iǛlPZ%7@3mh' /Ӗ6ɠ9(L{2ggI 6gllF{M=vK[.mɩ/A´i9K;..rh;.èJsPP,d.m2~缴Eɢ]l.E}]=vyӬ+569K]&!|o2 #3 v:kRh7l_.F {{=b״WڠLڥMv2oqL%iNMQ- : 3 ve7ɕK4d.v)Sw\ꥌylfA2*:nҴKKXK\.V YR.gqٴʸi&̸NczRQڞh+u2 ;H]Mvizd.vmexo+ mۊOHVwM_tTZѿoSq.v˝e$pKx~iٴiSvkGMNibcgmk2i:6-SvM]6M[RR;iwOO3:fv|i´=>MBy5 vkF>gMuBv [1:+)L{e8 3 vkF#gƍu %nO4 6D aLo)3 vkFgfU۴en%_d57rivi/ʯ.Mox n}_6fA** LK4Vi_.\]Ei 2rivi.MwF}Lm+0m9:ScTZ*PJ&N.'>Di I#I4}mr2lIKrmTi:P؟kdҒ6ix7iVJKlҒˤ%N a+uN0hI{JEZM~ ђI[N%E;NR(WҎx)H%8o*M }ۥҒS^Ԍ fA"-iVJ"b"-DZڞVirL;H%*iZU,X%JK[=>UZ+)ȥ%X.a.-\Znx>=#$VTZ*aU\69K%%wh㊄qT^ܺ\Z. 'iɸ].-bY7+$Ӳiʴe69wvɴlpC@Ý42JZ3 M̃3Ӧ2-M.= L[d'5riY4}urF.-\ZVY;3 LzPc]2-+W;P QR;˽i8'o^tZ:-l3ZN+.VZMB뽿RhH iEڴgN j6lZ4 a+u_>3 VM:9+lYئM+i°=;$t̰lZ6MQ}6jmZqٴ°=6MBF 3 VLӃ_'gfM5ZaV\2xvIJеu]:93lإK+°=;%t9̰\Z.MF~6gҊ˥Qw0:PgҊvi䬸q2TaV\.m_gWJkҊViz̰Bczخ]i´@Ŷ%H-əqc"DZL܇ȢeюٳҮAjNL,Zmhv,Ձ:}fAjǃNΌH30n-mq s 2iU4EurV8{I.V=&MBxs{Le ;ȤUmp@& gT6ieҪD7m ;ȥ5p@. '4viҚlOa{\JsAҚvi8wisriMʤ ϵMhڤ؁&LZ5)kI_dNfA&iC6i8tIk.ֶ۞ao'd~^-H5-p@" 4i%ҚTOIBJshMk4;дFñ5Zsi&=h+u.cvFkZԁ5NhѾ\^[-=ͱT*M,̸4Z 4p@c\y40k?%F}6i3ӆ"mp"WV޾`%vD;*lg 5d6\C6>!sv~hG E଴id6-!U!xal0ӎhGMcg Ud6(),ՁZ֚_bLQqC683n'A)lA>Rx{viƕ#>4-hd6(#9|oxh)?mP} ;e5[.OKQ;"n>083nШ}Vp^_CXoCŮ+%Ƨ}6Vjڧɰ=>mP+u)#HZaSvN;\:}t#HZ܁aSvN;\:pIs@@tpaٴC4;08#Bs'vٴ1w@C_t{5Ͱd!eZ3æ`vdڱ=B {{♠|CBsgM*pC- {zǸiQL;HRYi NiǶ{@@ww#ȣң<083m,أ.vm_M%O:PюȌ;ȣң<08+n<08ˣ´W-GE;El–܁ɰ]퐛۱/ͳTje>rK62P ܮ$fl7jj4|`pfXP\BRJE>˿Ӯ viFgM'vK1=Ku.wPP3Jڗq+ͥUށܗTj/F7w3 viFg捥+˥.ȠJ oJG+;ȩکySvS]N8}fAVV j礧_O跷mf3 vkFgM'vI[3B¸2D3 vkFgMRvI[CB¼2zVcwUU)3RfvB[:jGfANN L 5vj˩ݞ›{{j$n-h̬NcvڭuMOV?ABB  Njg dK vkFSgE/kR(',Ͱdڭe 6Vh,nLC#az)ڏx)q|;ɴeN!HZ2-dZR wk  |Z> $pAb\>-mx>!#s#)ȧ%pA> $iӒ\%LBxs;ybl3iI4DMAmZrٴ} ti/FQkҒvi8 isriI.|oC@ҒriW3Qi]N!Hڥ.-\ZZݰdZ2 g$-pAb\2-)[U\ onIۄ̸tZ: g$pAb\:-d( ƭqߞ̼ZB $-pAbzXqXU[ R1I+5CX%RKD%ٞR:fi|Z> dp Af]>-{6C@$ʦdӲi8 ks2۴iYCa{۱TjHhӲi8 KVqAf]:-ow7Qaٴ,mZIYڴ2۴i3@Bxk{&H2>>aɴ,eZIYʴ2˴iٳ1mz;[nL{YsLRUDL8 L.= L[6OƁZ3 M8 KVqAf]6-˽_6 jʺt3 N8 KVqAf]:-+E$r v=A.-KVqA.(.´=.ͱTjN riYtiGdviҲU;Pxs˥,Êٴ"mZYEڴ ۴iE3#w*)[/EwiE괊iiťӊUԕCB,_dm6iEʴi8L+.V!L;ȣUp A ThwomkIڤMN!lҪˤUO4 iuGڣ=N Ѿ\^Z-%)zvCڡЪˡU _=;KuNw hM[4>дE-ZsY4)S Q+uNvEkڢ-Nhlњˢ5RȚhdњh8|i[hM椰=mP6aY-Nhڢ,Z36)lD_odF$Кh8tiC h(MBJ}BS)3 ִAáM4:ؠ5Ak"uo }ִ>ÙM39X5>k%fK RfAi}3g8s>k.}֔.>_odFϚg8pi}/ףVv0JfAiyg8n;*n(g %d}6m>s'd^M]^bQicLۣ -*/1侮N NhV ѐnXсZ,cQacM #yg; hlysmHyQhGM#g5*Ö@h H-1器FF Nhڒ´#vV>4ڡ5 5hkå)N Q+̨4ڡ5M5gkå' iJD46#HZtq7.08KRC\Bxgojks$)M6hK%t a؎M6g+å%7ۣW-B+GA;Ak4\`pfXA;\P޼$ao'd^L;HR5.083mX.vHڎ3aw-,GD;Dk4[`pV4[`p2nD;8I Ȍ:HR5,083jX}2TSB^t-G@;@^ ~h Nh<# [5L@]F'1+3ȢҢ5,083o*NhˢiY@b0iif L ei(F:5EݒÜJƇa3 vJhସid.v:sj+uͰdکe 6j'˴%NeTR(5;,gL;L3æRdvd)mi+u{֖3HZ\ac2tɴS).ȴ:@Ṵ̃\ک]6hNK;v4ڢ)J[B&cmv9\ک]7Nh0orMiFqddNmhLczJY%/1|k;jYS[5/083p,ت.vnO xWFO++H]ZxaSvR\J풝 }~}.h̴VX].vퟻ|mRM WNN  qtir,ZrK40083l.iK]h°={.9_\܉xK4/083m*.i˧]|Gk jj4``pfXP\Blo'd d.mh̴Jcv%7QY}]5yPP Ҧ iٜ=aE=OcӅvMM!3*mڗiKUEi{\I^Փh j.h̴JcvT%7Qrwn;HZьaSvJ]*(mMA%5rivi4e`pfTnKࢴ=*mit32i6i4e`pV4e`p2lIe/2 [ATK >qA&& jM2i[YIRxk/ՁjFp3 vkFgME*vۣ$i{ZmdFnh̨Dcv!]26ii4GjX)a-4ZV^ ,n5g4ZV#4ҔFJc]-{vC@<>ب:Y,GhX)v-,ZBK ӖTZ쉗h3i8sO+.V"")AbA:hCi8tN+.V/z ->Ϳw鴢uN(Zԁ:(?ҌO>aKfh$Ԋj8whs jE+=auN+Źu(ӊKebal1 VOÑE49Pا}izw;`dCT-ԊVj8qh +5c^6$KI_8W]i gU3.P<]1/-) ֱThɪd8HjM*k2c^6czT'8lêa81j*0c^G&ؖc佪^8j*{/c^޽_=KIY~5HqUp@Պ TV\tǼ{z9ꓲ6`lV6 [WmeE˛{E$/'eU VU{+_]1/>= KI lRa#-6lyy7kmt0s QU(l_–m]1/.~RhkdN؜j+['c^X'Rw$N؇i}K'c^޼_%E:@a3 Դs†M;'l9݆b(8[ZrjZ9akoyyv('R}RxiAΩimvNFs?WIaKuV7^ Դr~M+'X9 QN:PHd6Ni'c^,)u>)KL}`S 7m~cD˛XRR}R 685m} 76NtǼl}`=ɱTj685m} 76NtǼQBJ ԴoFM&ld7fIaKuֺ՗wTP7 \yyYu:57}+M3sip2WM^٤)uT6ϿripVԆ~p2WNN:P(\c|RB48+q e0 C\#>  B48, q9MX-j>&훨dW2-JJCpVkK;*m6i{ ʨM)mVP1v;*l,.a{ jv*J}B˨̴ctiSicᅺ&[{z[*YhϿjLk,#?8ՠ6ކ0myo5#HnZnQ#iSvry9H^C$_OO#HyZyQ+iSvry=m9~v:fA""zH^i{_ jJۇI~XsL;HRJ~pfTǾ\^O[,J{z´h%d$?83mvf_.Ǩ!|;1Ȑ+imn=T!UAgUå%Jslr'dm|3RiTi6ViJ;+J۱˱RȚej%?8+lj%?8K&! ۣ$T$fA&&F3Mڗacc?cP1zMaC K4iKJoa؎]b:@n}ySz *=hܚaK_odF$N)j!?83j*NhKrE =ыdFdNij?8+jj?8ˠIE`RZɌ:HZQQSiv>;]')j>ۇ:@QS3j?83j*Nvg˝0"Eqg+ Y AL+3vg˝mh\+wv{v;;;3rgQF{5Eq{HjjANΨoରod.wvJE%ORfA  4vg_.AmhwRoٚ"o$N-Ϩe̴Fcyv*yi{䙄0m!;# fqxl/drawings/_rels/drawing1.xml.relsSDUTWPKH5K ]xl/drawings/drawing1.xmlSDUTWPKHP'  xl/styles.xmlSDUT~WPKH|waP xl/workbook.xmlSDUT~WPKH3# xl/worksheets/_rels/sheet1.xml.relsSDUT~WPKHju  xl/worksheets/sheet1.xmlSDUT~WPK Txlrd-1.1.0/tests/formula_test_names.xls0000644000175000017500000001700013146745673021123 0ustar travistravis00000000000000ࡱ;   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-1.1.0/tests/formula_test_sjmachin.xls0000644000175000017500000004100013146745673021611 0ustar travistravis00000000000000ࡱ>  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-1.1.0/tests/issue150.xlsx0000644000175000017500000030131013146745673017002 0ustar travistravis00000000000000PKe%GԨ.F[content_types].xmlUT UUux Tj0B)I4=k[Ėv;_Yi(IKVxfvfWɢkjښ91_^^$o[fp֡ͤlVRN|zL 5I ^ڑD_QX& "B*op#3k C`ƒ{(Ժ&hg gwhΕsXnLhdBgl@AXPS uS ҦDIc5y7J; l"|PnFi3|UUʮ+qM<Ai_&g%XE<)]!3L"'>PK c%Gxl/UT UlUux PK d%G xl/_rels/UT gUlUux PKd%G+,xl/_rels/workbook.xml.relsUT gUUux 1k0B^vJ`7K@ lؒ)8u tp7Cג/)hy}|?!Pi$\ BpkgY,IR+N)2{y~]o}[4]u饵`8} 1ymé7a}lw?3.5IМ[\ʯWyMsS )䷐A>~ :@7X  @ @ A$@ $H D @E} P@@ ("PDa@S4h @@M40  0@`!C, ,X D`G} p@8"pGoPK^^TXfF WbE\]g¼cw?s_Ek] vjӌs#+ T 65k}N07t PK^YyE@AUc SU=~pmEl\Ͼer篿|qWwOǝחW߮=^~^'wy}8{zޣg?]<篮o_=8}7z쵻~˫7럯.򅙩g/ϟ:ˋW'W?=iy>3'7{t^|./a~֟y |G?x~1;=?ŏ7/#rsqǫxe}ӿ]\|?ݜW~V_SF;g8?g6AxN.~<œ?oO//x?s7"xv'/olb3h%Y.wf{Iǒ~sG듫טf&b8gfs NA_^~׿;gߟ=ggWMg?^=~ztl]?ٶ~ճ}g{Kma_>,a! m=X>,aˇ-z|XBa.w'ˇž. nPˇ¶mض$EۖBdۚBeےQl[# mKf$!mI$-Զ%9ڒIimIGRZ[#)-֖JkKd%C"YimIHVZ[r$+-A֖$JkKd%KھdIZۗ,)Bk%Ehm_K}ɒ"}ɒ"/YR%Kڒ%UimɒdIUZ[*-YR֖,JkKT%Kڒ%UimɒdISZ[)-YҔ֖,iJkK4%Kaɒ&vX ,iBk%KJ-Y҅KtÒ%]hdIZ;,Y҅댖ڒ%]imɒdIWZ[d(-Y2֖,JkK %Kڒ%CimɒAvIwI8NofT?:9PĈo޻kjFJ>cb$&?\ ~¾%#㑼WI|9{506{)\bPFb-%W/n73f҂ Z#ԐK*'zk,ڤ/4*dܔܜo)ʻ=`*=~t:|MV>5J=d6Bn8]ӏ&0*VcfcJMM姾a6"&⣷#jt!ֺ!0`ԜSf$7ZT3mo!g}O 6FLnmRà7Uظ }ZI6>:[:dvof)(5QyF0Umr!P yL#am cS;F5kH@8$@;R `IZ1{EҍSpQZ!vU۽˩G1Oj\01B*U /%%иˆW)jB_cwwT1ň/aeXޏں,FhfN/M9M!tVQh*'$AܱllD,#x+yYBx(aW0MM"c"aD``@fCFhn\^̣20bC!S>X4Qv2|qe5J :+OxGT)ͣBW"FtÔk |B0@(EjE=EPWyE_)F\e񑅈;f fE@xU= ѶUseWzSNh6|ޱ5,0^ b_5`1?(,Dֈ1âN\-آ#E%: {n͹sUELepdJ@`WQyfŘ`dž gAB"8 "EcomKQNrނej'U݅Le(NE,F %rEF+ ץfY"`XHRFX:1y-]jV8.Jv#.@8DOaTEb$7hՌ}`P,|4bbZ-W+…2G1OۊQҭ(UL4 *G?$F|I/Uh{qÍS%#` ?a]BxGρ`܋?3ԛ|Nt8̷V/n72 #N1#z!>3n>yIɲ9N"\!b0v 982ubͨ*ԈcjWGlLºa bu$adCggKeF0/f'Ll; [˪D(٦@g-f]bU{f;Z跍+k+#4]l;Sl<0m q7 C%Y){ vCfS6 A)B05fDX҇5d:`E>0mb (Pppf[ͩwBU"ROCA)3ڪ Y{ ЮzBFWsD-NFӕc)K<`J,wKH+ ggR-el]lѼ1fODxvW"%<"V"\/JAaktyB1&ѣ"FxLՆY66,Z5:64:%IUD멩q=/ 5nǨ†aq(3jRd&e^ |(,jY+Au]rPgwE ndbVN9M-1l,.‹jakb!,BFK4$߅_@:y[2IGHpg1qBqxyLq7bذ(:F83^Ѣ!TRA'W,3X7E+IGNhHd>vXCvmWդ] EvEXpAՀ梥SĸbUTPz;̑X{O@xI*%Oao*@V\<.S-JRUVNyֈUI~PtVJ-(-eڭ,1,, vKY\MH&Sfc(_ZF4 㢅"BWf.fx " TE|kQJ ffyY2j~H@(k*{`F˘2# m+}bK+{Z7ET}αژCRe\Y]I93 Z/)~q[=cຩ =fXo#㉹ό`EpT-/x S%G~TY|)Sy'%!.3_7V~IQ!32<lU՚(~"DPhg*Btl U; Gs="u&Za ;HFHA#ڭ!Cgڬ!<[&@-/x 8ԄK&ݯG 9R1vܲ~C<\ps&Z.Zn)RI8mKG_ q ;, Cp&Ea\,yY @ p=T#A*=J8#ux1Q];&o*O0 |]J=Ȝ;^ew ETPMvbԤ׾0bye!RS]F2)YAyLqG_& 1woJ q T-K#wY55RRW,j@~j(VvBV( #ؽrvEN51c__Wp;.4^ UbV%mr̨5hFWΨO9>@xKDJ?EUdn$Ƚ@8!X<}R%)p {xual'|h-b"QjeaKb -!`Z.BEl HPBE[Ju9X-T}g0LiU"Tħ4O) ,s: Cb]#:##LrM]~ XTz9ⱚ۔t߬{+ yhqv rζǢEW$\E]R޿&U|MZR_PXM Pv2XO!F003J1#RuU=OǤ(.}^ħx!.n72z1XpE&K$؊]r%jRn!=fCRSHٹόD;- A}I_nd+'+&8Z.J{T Fg^UfoR]"y>!5-ue;b̌4&U GZ^ѬF#2# Vad+y!-bO躝ra. qu~{jm qRm:iޕ%lά6﹐^n v -"Q][;Rg#+[KP. X E ^dr0t+l"VA@8ٵZ L0Bɟ6l犚<>FQhi_KͮSE)*]J1Ǿ&Sv+Юuy )OMQtv缫Xv(Oa2"fW'K>ە$(f:]*JQv3zY#P<1Z+ T9Pc\0)풖g-cFJq ? ǫS>Dm0ӫ]8J.{'Qer,A:wg%"?Ty'\ =E!O'q.G3G6ŝry剪K-Q $R.kz[m>]k4fs]?(Q0h]XFFě TC8oȣR#:AsyZ!dKa8)V zJUY~OwAR- Fk*}J`%Ug`,q,ŁNZTy+FA}vmg swLJ0Z_[1TYn}qK;)1;.Te).v}{hP.LL,2[\8{|"'.Xm jȀPh}KH9hQlWA۴ۦW`F{#9Jm ؕ")oՆ*1!h[/t82k̔\d!>1̔ZdBÔHٳwQ-ϡӝO&7L.l*i3ț 0䪤;HV#0y= [mhOi7qoGSY7e\-q`*I&$+jW>V&=nD H<0j.yxQXD_nkY"叓D>"9+#Ԗ_h.knpwٍ 6^0LsBք~JM.x8Cê.}6cm.p48㼓] \|zl+=qU_wY)ƼK\ĄwK|p8'uHZVg^qչ]cȌ h>@sջ"9,cЈ31 .37rԔ<PLܺ !%XY`2혩hDmId džKN #}1cZ6:k\r3Vie >G=!oRCxG6~u ^"˃;W;x`G-y|;#Hg>$0 J4/" ;14WRd/ECbm.m(R"V½ȱ#<B.}v;'p+w>tԔt 鬲Ν !3t=zzB򂝘XdY? 1ִS/ 7wqro2BƆ# ::el?ve)35|rGxL=--} H2/z(QrLJjUIsQklةt8ӷ}źȼ+ϵRpvsJȉil/"qV`#b/")N\Imد䓌+N.{ݱWFB67j31k[~=~'AwL~BYt*:^J-2p\GFjGylm"tLN|m7j@,W3N1 ҘE~D"8[v~St=f'LqAsGKIeM`FK䖍@TX):而iy6o|*-Jĥ98L76MDՍ>g]5JEz|;}&ӝEۙ+ (D~ ش$R.He$?0%аEdtގ.>RĐ%˱Rb:#Jpۚ >H~ ".ӇLj]E`)Hw{u P|:Q]{R2"(;1aq]F ְ(R]50f2+~QŪ͎`+fĂ 0)מ[,]"ߨơ26wE2$Ւ~wO*E&U˜ `S|gf@/`@hmF "l]"c%^HxH?%K_4#9fy)]∻Υw{5Z;9#mG[#aIt5- "lȑ!92N }`<e FC['ɪI`Ģ3C#q!g;HwlyA^G&YL v]DvI#} q1ѕg1yN4Ɲ+v5tvuLƖkI ;Y>=cox:BQhu"Lӌ:Zt/WcHTtԋRiMRFTG"^a[RM^NpbĈq"VmP-ҊD$ L_|hBV+csT2;'jeEAQȎ-f~J/;RY^ K~Y/" Fn% Tkuz2J}Z$|$Z-֞kVbD]<|x9VnoN> NQjѪ jx1A-2< 1GH 71I-х1~wR[m2zNE*YqB~j"xv\:n7y+)Mn250VNPejYqM]h8j!7j=^SWT"YJ=/.H=dR/kT0G:~񤄦ӭS+,cW?p_3R Rޥs$r18bJF)Z]nKHٛAu1s~'жMVRa3CO2:4 P<1gR2r\scdŧ1"7c#%*6ɽV$,2,0,ZZJnN `T02we `9Akyrcuk50fDK/9:20yi|.K[:JJR^`B!(c:3evRJ;Q8C yg$-#*<@o&#8}1//bCXGgqׇjZ.EG}% ׿xk#<&A6FׂHt["zh46]\$f\\7F}5 Xֶָ&?qʃ2v-?ɋ :jR{-2u(T;ÿEfJH _"쒺F߉d8)H`FMlͥԛ%?`z![yKXGpMۃtS&mD8)k[)ny +'G}Z At)2㧿$ ֹ;Q+UpJa4ټƠ`Ğ $|IH;N &Ҽqlၝ 4|2&d,ٝ:M!0r5=G +l]w4tځzɢM!""I+)H&ISGJ mF˶7W>G'@i @Bgb8gМKoB3õ^ #5@[: h, eFz:#[*~*FT](wu+Ž5 lTfD[2Tac$Տ/3JqSEJ<)JN0de*陑/.úKl yw%69Œ|ؘ.TTf4|g !yH}XrJ=K]iH"1ɘ^$=6ds,qB~/*Qf$:C`ѦŻCbcr K+Il4ƅrܔ|-cٽԋZݲ3xh٘ P[vD@#/(Y4~I9F8jFqAh:8z"*E mZYDerΎj/W 㡕B12)J9wB~6@ W#hJbWy5FI)OO{C?1}V[s;JsF{arTb}EO"qz2,Ջ_.p3r'rEg!AT%K7:VAe\sʉect&>(^% R\ܰUzuOIt7zBl\+T۪7rݾ `(?^NN@^cCZUF SF;+ghT>W٬YMP (ZE峖Q^c^٬O1x,}Տw[F!&85Վk)I+ȤgYcmkcyWw:_*{L\~Sx 6%baܕsuQG, ѺGTymF#ƽO_iܰ(<廜W x5T\Z oш96FtWUWB<ٹ8>-f鳵Qg=#F3ۦؽWđ0'L)̍! ^"K';@!fVYax$axX;H*3~pr[yטI.^@Cf 1S)[_>{檖kgʎB#; 0wK\Q,#^OյBL6$]PrkS;%"F6D/ҏVeՎn(ΐhĜgLQxIQj?IZ GxPiBz?˝}wu%6Jv升xLr^z]ȴ:OQ8V$NAѲ+E8WCetsnxW) 6ߋ3MsǯKJ!M++|䨯3131k}Vo,w˻F*q]S3v?S3|ݏ/:ø O* ?D R(W>Sun`+d|.y7bhT l\'}/*H[_i+ u0lzBA@t5}"&tY]wطr5&zFR&ӫP2wq|Q.h:$G WD->ڴG 9O\h', *;W{n+iNZ۔(<+E+|EL'NA{e2G9~q-" ƑV2"8VMАm{CC'L lLEY" q-J|dP PFd#)*ܘ/sr6{J,"TL*ܮQc3C$į*<4`2b5'!Bu{Ҫ}K\$`I8]C{86]uMګګM{m8iE7nJ¥Fpu6\eL49Kȸ.TAjYVc,N}LOUV36K?1(9JomLBzDCGGfxs%)\ J'*#޳}WAa}^e]ʌ]kvWFm]Z}#?ER.>1f}WYH!K@Ez4`\r2s7$BN0j,YnGqr|\VAS=!Jbǽ*^X{rпUh{ubbnzB]_%owL>-KlʍyDKd/{Ic!(.it6ԣn Q0 2^rͨ8e*d)EڴJ7)BO.ky/yi$¯#y#WGjPH\xS o׫ƙ n޻)| ҹ2!&kbɀ5һ#8XzM,grqM}7 +)ް}TXyoJ› ,O1<{K>Lv!Y}<3Zmo-TVJQ2e`/yPvq4w 2+?F:w./ 2;xWy:Eʸ)<0y="P+tא;}J7`KtdNQIBkXL2! 2#S@u!3@q'SA&#'fT]Bfcjӝ{qWyXLK1Z} |1h\Cfh/w q;o,-%ova)KM85d6fG os)]!4D{:O.3ɎO*ܱ `mQ򱼗|h zqhg5Y0's;n6|(|x5iGXL\ N-^/p.{j0{d@;eGl Ll /)gTPxKvPK&#s!Q^,z, 5c; sƽJďFb3#qd ]-89BWy/r݌dwFyjH<D=4`Q[F&栉ķ^ǀtnv2'%&UUߦ5q'[@ e9*N(;%_6mYҌ+XK2D yXe^w`v<3 xT$2os9yjRzV#9NKJG@qy_gV%~TGkv'.r_jfVy/] Sёw/3=#vܗZƮ6`Q}U8V5$/(%4G~M#O ,D7lkjԫٍWIq`CR̅/+lpM^)O>d1zX6It\g0|ae/}}#$-WG !ʨk0]e/L}8)l}I''Zm?0LfB渱wo $OOF#5SZɁMWei_F9QXǑ5L浩]m^$G*b;]v %ZO ctbaAIrYR[l %rurKSS-n-"jr^?X9xƜ3x:אBsb0PbYl 49~Ay V4Q&RTE(ƺ.1(zOG&L^ dif8jf %nbqqaK 3O$yF2P]a&SA+:}טuxѐm\P 2;Q|7~EP$J*8j,x6GrՓT[aK.s4y#La"|đw[0TNg`/092 B#)kg_<e&W{+|Q!_ 1\8-\bibF$Y _i%OBܟq}^e *s4V R%t x.d6Rxc%1ҽM}/8γ\m+j &YK-!U:H $"5%ˎ(X+} -|"W9=s1n7?HQm8Lr/2|.V9'2:w2_UkD z]0;Vm D7Ey_;'u"HLܻͥ9لQ_jUe*͆kƱ*zռ_ #or +n 5JNr<ܗ\f_w㶟IO: T Hl5}ND2Glv(NBebسXNcsBK{VZ%tDq37U3V /s  *<*G䙢Zvj0hȄKЦa/vP ԫM9B͸Ǻ{}32c9 57C+5V >D\jJ̌{1ظS`VTRan1ESLY4=/+9m} ̜uvwLGtMq 3W6zK-Q?ef53#Ŷx5fDFcyPÆbU&KDLJgkC#k/yZ69 g۱`w ߨA'ܛ-O!4PnXCfOUq VeP!&4F"=]!f K:2zSx05hzCl\>{X.}(|履[]x(<7->*H^P%#cA;oDRVz2b4Ob|w(cI 2gE<[:O%~a+Cїʣ6Y^MK}̊\uԥ&Ǭ$k y(ZuC)oN c6Za-%݇{R$@VK4% n^%=FpN㉥^"4e*#76wցF/jb-{>s;wܗAu hi2\Yl8QnHSDdf͵>Stmɶ~.VyT+ ʘWܶۧ_U:u.,ډ%D۝ eR@7(nzt̷rU"jz"y_U4¹O^"s_/VA %Q>1A?/wqjV;uq-N6ghVsw)_*F?hR*xR+YaM5].3*2u7yWȘJzkqWQėQq"X]McdiO6P;eDvTtWqclg$^nuiVȃ<݊~$BZ|4H%؁CܝWTⱴ| I:;n& u8}K{͇JnY󲴗X&D ulU%T ܠMaQ8.7;DklvJ*j)<#u*. I$`\zXXeJk]9rٸZr1MA&ӷˑFKo 31Fj@^*L9V#m ffi aK31AhsSnK$dpc!uO{]gnlEbHOVM!)3EJUSyea@̌2|[K> y=6^ld;D 6 H!&پ&twY(ѐYZ$s5P{5 2#=fnYGv;[w/ⰭwOg-2qf[aa_&t{K{'W1{&RV>ePpMKT)sE1L!f$܀ˈ/m/tq2g\he  1҆ 1xԊknNq ỏ-+SU垙Ԕ\som]\cf &ܮgK lJ N[J?K:ns_w9u".FE:F-[{pDp n'W){6,{,98pHWc%RW@Q}E`Y3?EfP=rQnV{n~ōLrܗڹ;Ia}U"$F:?^^Ʉv! hv.^=m~foS?F*l{}KBOT{`&+Зj>[TCP-JNKQ#lM!d3m #Er>*7 d#s%a_z)zxN'G"GZjv|q"&fȔle&a yV|,&-%/j 5lY._+a{63!5r?w(&7ͶKSJßŕ{_HMRX8;Ȩ+qP{8+Dz6]y̗8RcSD: 4Q dl͞MSiB4GڍƆȼ+|l-qi"Ho|3HN)< ;NS[6%f2{y&k13"Qg_:h87,0|I~D 3V̉"qv_"YM.e 'B$: 4z8zUk^U晳  M{WyqGв&/@3]$/yM𵶀4# `n#{45h]&kh[F-@sh]wkӒ>wd>ЫJl]lPDqRvϢ E4:gM=dFxF7KW2{e*Ep31^@f"`k/ D9|h$Fy=bhK/㇝6QI[Dx??%Btq@|md7pbuN;JWDZ[Gԇ.&CADs٭X7ZCq/ ӻ{1" 혿he?)YC!xe4dF@i+a`o]8C۔ͻ@> UrM -{~_T#"YW*{㚛ظN~n5 ~0ߊʬ4L"Fp]HX儛lwݝ\e&6Gj'~)W9dWdp}_gc\Gdp_N:}}"vܭt5+/Ȣ"%^K܎i:0^/uZMد4,u:&?jeaּ䵀qg堿P˭k5ܸv>B3|_*غ^&WYjq6<[Όќnw $pzﻛ4fvR@cf3a/% sZv-Q G;#A34@FLxzCYK" ,MA'R?h9m4D.mVMc:*!KII>ݧPwFr0Ľa ]R;-٩c$P^ (w) [_} -qemm"eYKnLq{7 .LD2eon /O,Ի}ɧyNEDCe$ֵ+WSVRA& # 3Q+Rcj/q cK{c*NO"fGB"'ԉJL[huF!7]+|jC~ި*zK>L6Hp]Ӱ͚^>>DmxxgiHYVI1baeLJ?P̽Jx?}x B.Fhn\Fctν` qvw / i7ˣjnV{. 7Ĺĝ' sһjy8GP]egv9]Rp U3 [F>qeԁ %7{s9AhHuQxy؝xRSGpȚ]Pyy$[K-SVOрy"ZbIYq <4bFLRyC *j dJ&Rul}LjlU 2wFd=劖SQ砅gf]ewXFa[H P* LR/*  lsn_r#jekn鼥dT[Fł5`s]0h"y+UP"x/@6V0;Ҷ};'y-`/ryj 0J!X^jߞY0<[~իeI䱫9W)cn'&t늷[[l;q8b{ jcپl pUhUCn)Q]49NMcDa8/ejP\ /!tiIO? 4D2n4^j5!N_r!F0JJ7O&9^ u$Nib:V@j2r EFr |I&n5y/Y͋3Q_r -Z̡˧:.X υZ*~K>M547Kt{h0{ 3 `hh&9탛MpΠhØr ,uWgGY54ŊdFU|Vh(.#@bS_/Ŋ~w7^XVP2: Ecr_i?zo$]a& T~[}\!"2G@A3<}neh5,D]Kfª 'Z)<ʼ.Xm0X{hr2'cѠhx8F&qDX jdzDPf0(<%+rRrO@sKaU+ 4OB9;q/@sψdWW8>xdu[u[VT9chA:NJ>hݹX"eHfG+Rh2Q1pIc8Xyۑ8?&rAuZf=$S(bTJZB#{d>yz/*4J_UsLo:Kdn`/vNZqR7(~-󋾕~[*}* jqc߿Xe_8E\6\ޖR ^\yGωrOZȸ"\gjύX$A V~$dSITKكJWM-EGӒ_C ͙'M0{DFmo'$-eqS02VBruF1]k|e0 OZ7@|SH]'#wfc ^=|EdiJ z}Ex Sho(B7iğyͺ+E4o%g HxC"֍!5Kq WyH5f$TFL*cE"L lq?JؚxaTnk'rSpZ~_/Tt/#bhűXB&W|+Zij iMGn 4!RlL;m+D/-;s'Uy@:oJ%bv)JN9\逩;{/3"|-"4r 1D1%mkbo;{׈:RRI5Oߗ\i8Jx'8һBL4fc]#f|8dA]!&IBlFvXřdl|Wy e޶ԗ<([2uDLZ p,FLhEb+oegWoyRlW.; dM 05+`c`/J<4`]{}hd|;'*Y= /iuK2"۷x7x/.Ǚvޅ^NUvūSm^Yz*WliM2#JfeǦլ!:aU}[ DSsg-"X*. ia7X9,&H7BT`{;K ncӼLWY2"k_ۗzM$qI^woid~P_rו#ܘ.NR*C`ZS׫_g3ԗ\KJT(BfEkHI-"-^}{V.B +"WA,"fQz셋1B5ƕi:zzێۻ/uXê7<2>9//4NKHD:2)Y&N!dOCKN!2+|pMai!b@H 7M/} Bc]a2TXZњIinǠN2K㶉[ǎI{ӶD}ND\dʆ-`ͲKBe0Ȱ2#ct WǨ2$ྔL$U C ȊLg21_2DQ%+9KFG%<;2a)İA^VcPB&WdL{&ϺM&+wg[?lRwˇgU ]Չ8-yPqNoc])XSϚ$)|7BV2^>įu 0#O(qJ01c&)܎>~n re3fIRҲ 1Ku,pF|huwF̈lw؉$ua7 LQZ}8[8L{b5 c-;Wsx:/}4#ct*.[x(|j9mY]P&jAn7~V4Kܡ{bѠI! 8̛WW~*_YLl1:Z!?.ЫܜRݻd/Jf%G 2׈X"c냞/?Tvxz: &y 'zV$ml bg zi޹ < -&arr Ua~St_;Q.b\U}K|r6W$k-NgYes\b*-EZ7D:v2IxmĭQ46̪$F* -Ff7tïruglomc{]b9nnw %{1f]֏E;@7 پǻ^cT;jX֛vK'. 5/;qn'Un OdizSHwMCڎ|mmU79vD$W\X+sS 5(_]LI4;kjiE""wwƝo%09rGoooS8y  񦭎SO;oRHy8X4dǍ$VPɹ-j3ٛDQ;~Q| -?*YVP|]K%?׎s9 am0[=VږRjSՎK+>g1$\@B)qq4\e܁VM1sn_Jb8MȉQX6}(|7\qw[03.㢡Z[>fƋ|Rl xYG3\Cb2(oRyypfkpPyCaԊ<^fFC V4j]21 tE;2_+(JϹV 0Qd)~RڶsWcF8GR-7Mr;3'WW}r߽Uhh4kXe[ *.FpnXӒU AD)x:tLT= SpQ/~VkDPH *-.]wrd86RBp|gF_UUzm93io^XcྕXgKo )ω=1P w%gE)\քv&rG{uNMu~ mQ5cb]e|pud]婬GFaJi+FaID*w{WyFq8VP泾9W6&րd=s5`§r<-K5`FZO(IC?V8kǰ΀;fnd0t$k ][li\pW귰5NPdN?Ěv t't#) 3Ͼ-󥖉|]u<4fqWu?4f2ݑ%YIc&clk@< (La&WDjd8#`D^PdԬO?2>4N ~JFrK{UuJ|=[S "qҶ /ᦖ%^< b8Qm{hd"H&XWf#3gYWQ_;BLޛWWrwԻ{4SJg Xlx1RPtwem*pUX"\|q1V)jɪ}%*6 )'+{UlEj\rTmUV ngxKkGۿRPt}8ۍ[ >tOU >L 6G}+W)+2vs~f8!Ʒk^2#oW k1p`v7wG(rOjȄ~Ym.±DK~d"hZ\%-@ͥUi{oz6:5^鳵_})!IP= _!I/ @Yv@#&W,y/ysb=Ne/̤:*ZVF!AiMuԈK, 1CY^Q$lc·{l=.N*r0t?KJ ďd An$ VVOd! geI/@pd3J2 &#oMBaI/ˬ+r@mCMA峹R; +OKQZ2T8W6/\xP^PQxK^G?* `i/q$}IG 0c\aR&xNpX-e <C:W`Mzi̧oO)] 0Or#Qy!~q$^&^+зš"iBa˚)|WhE_r L~wz+()43Kx~H'haIUSDkX͘'N75)|NxԠ֊9E,5h;,vU5hz 24' UiPėq>rvO&)5#37+8 y{޾L4ae%fRK5Xr?}?}/2x2_oA*'|`1_bkxcYɹZ_x0"O:#̝M&%&޹&*ytL(Wv\얡͔~徐/j5&~_EDDeLz(}_)YᯯE'_~<[JW\m陬;KSzWkS$8p`B0)FfWgѼ*.B#tqYj 4"Wɓea_*^'|T BS^rVZs.i["#r7^=SE^rȈWNvvBccĺ/VSu﬇gUIȳM#s:@$hFȎfB%:%DϺhMn}8S)핇ːn %f7Z,):W<ˠI̸\|͗+n84XK,ӘdEXdϷʑΤ=B34&g4S%RXtht63?cMN%p'v} /VwEVM} ;Um%dr;*q,Es~G e5b>Ek@ti˕I 2#>PȠ s9̏z 2®gĕdR"ԷOmj$i1rMFRZZ ^^FލATv&k{ИP"җa4faHA섊L]JRg*#NdYP$𺸰cJ 3w9B<sn>JAZ4Z@]u]%Ԋ@ʴS`\%n"̣Nj΁Qy@L|8 7sK|RÙc-_bf\ts/W)y2? Xkk{9B߽QmoTԎh@-vöԾiƨ~*w˙l|~(Îruß_7Fܕ7z嚛傳*h9Ř*1*)BܺK'0 Y]tWYqB`۶ۍ"DDZmYoiq֡Z$D0*2x{H$>t%sT_Zx9K}g4M~V{ՆƁ̃^ j1V*h,WW{oYGM~aa:ۺ=rs7uoyL#"MQQ@S)eۑF61T|#do,9^7v1}e"A)$n 5v$kj(JZ} +Rg![H$*1sBI*h.`z\,Z28I%nbjXb 0~eZn̴Vu+G\8n8B~݈U)<W|9 ?ݻy cBĖMT*F/5hnl\W{q4k.qKDV_2fVXU+V \q+[EM+Etc ?@sk8u[WykK/e= 4O=,n|sUk(|i`]jF7YfbFM Z0Bŏ25`>Ҏjx4jI'Mp?JfܡEeS͇BMLDIqQLAd f5a( #NjFL& dtX.FFݤbã]XDJ.(< wc` ?jB]^֠"WbIS.h9EyF[G0M5QI?@/y[-W\ XbmC(o9D>:VYKY枟_76ܓn4K~G4Ŗt#*}EH.\r&Lmfw!Who3CN})WuĝIA+,밿zz oZQpcǃe*]T10wzt}׫tj_Vܳ W /f͇gQ}i@jf˾Ӌ?]7ʱN+:wыԋ^Ł2UW/xWU"_[*+r41K=JyX#)i^7OH+lӝqh9X5>!'_ؽw(p TDՐLAZ)< ˜qyKϳ_rslb;[m5_>WLP-%_䲛H6$P?7Q7~)Q=;8 *[LB<%#.~OiۻbGPm!Phyڱ="l0lo /T|^.bID Uf8 ]°yׇgW[kvŪMl5wGh"sa 1jFC([ul "q/u40y$k 5NA<,%C ;zW"^Ѳw_mwG7#P;R$1sx{J&q8AA&pQ]&y-L;3+Ǧqbwp/*0@z):Pk03'^05c /#w 6:MZХLGbM$EgKR,NTOgr 1ONnz;Gx@L3ڀÖ17\Zz@yK|jb];EyF Q\&fӛ}^8|bS#6|(%۫?)*.X|z=Vr,~T!6,{/FEǑ韋Uq[%K;]%G(\zT4ew3}%.Z'_Ku\&+)펰.Ho)I5@Ail?|%lL `Hy/̑Ց9˛HTՙ*!3w0S˟H` BGzuL YF$5Ef"! 'mtFes3M3$R2Pl/6%fzK]!Zng{13G 7SӀ#d3 Lz܀]&ivg:m=بA䶪 07WYm:Lfg-w0_%~Ti`Ej/y& v^_/ 3x1R҉ 0OZ2noC&NK%`JB$;"FLk"k5bƏvvگܗRI ĈT6,}8 @KK~]Pjנ'2m`7GZH)|ִ"1'9j`2/@a"Qm%؇FLdDQҿy2J~FCFO,Kaɽy(xK{$B^ R!&-{傽[?bTϔmK{(|*.Ļ\;CbG$S)vZPx/Ep^BƳ[}#8 8 ĬW였ˎƓe^T8H3=^ f".)f.Ldkͩ[WMq̗mF0S#tս*Bfv鈢峤ɄUGrud:F-8~Q|ыȌN0, o ^o6T=K +y׎Z'o #02jvNG}Ð"C1:I;QGq %7/}l &dDbM/m#-bdʴ7cgm> X/Ldx {SXyBҢhIo#QJegQ^VSe޾Y1$\r[.>*' KTNǼGXSgDX1v=* 2w?ji 5{g71*!O0uPT &l7|Wyj Tn 1OmU ZʀsF!eU/3P!KWо+qO1.j6`>ٴb8Is\u4`@\"vN"SD(@XcӚ]dsyllnD{)d+Na[RP!&p25p]!&EE7D 1ZWyݬVu웎*=;? 9HUO+Sy ʱ{З|$QkPsc;s!}O:1q0kOĥ*1 B{"XvH!&׸r)*bԅ;S8HÇ8Q kJսb]劘&-4{tjgj;@k-4v`ۭ֡~J)<^bB'a`^)^7/LqpԁNv׫J4=d`WMʪ^) U Α-nCʎ3O,gjqG͔/zљ:whg(' 7|JLYkqgALG}Uʎ#86.ݶ}.V92a(ٗZaVf})eK԰Et+J\B"V*kvIHBjߜo~A64re[/;U~KgN.|buE@;K~Ч}fQ7Gʂgt2>sB!R?n䳻pJY~Mيr<"m`j쒛*t厹%ԇ%?.ŭێ,XY:So fKȩכG/Zohnk܏𸐐X e{o/NʸWxB+Ъe6 HX)ǘ\03rN@8eJ|-‡}0yh&ү̗zVWi=$L)fPMVP-7HuRC"EqsYUa)fօ 47} :ˌ+NVr7 p\},K|eD|A0P ͪtL%ء6s ėĕ%^-ǨHl5g$Tvb%,R ,8Py_f/` ~Spy SvrSxyZ -.c%`g:4{_qVt\&rU"IQp:-+  5~w(zCbf']I@)͵ɶS@slSy~SZIO>"V4 KlUNvHФ CYCXKz\؟_3ǯgMxkDHL5ަM9/"[mV@E}(|7?9 vO+5sS@ 2X'_FCBjbDκXK.Q^_Ujot;!ț"ř̠m8| 3I_dd{[A̵- %ji40:ML-+IVi00󩧻N(of!,b/02YWRm?qAeCV̹nr/HFm)R&UUܧywo_7"M ^eȪ ?Tڮ"i#a_Q>zWU>V@~O䳔R/T=Kitigjb8mdX *A~VIjN\LOrbVDOүGqǹqի}gIG\f#7bI n_ƮL$ ~W9dWbߘsS?CVKsn09 l~RT]}E?]K+_*^[̾*Hո_zMr(/!M%NDm].U%jMcc;Ӂ'(h$JO.6]m'48rm߷+Kt (9&=%qፐ~Q4 \ySyP4" 8%ِ1vO$C H#v_J$׺W8OILuE ⳎH9l!{%ZX&B2:`X/ 9qq:,"L 7j{Ѭ-B]8R9]VpU\Z$^R b|hē-!POD _4|>+Wdtf6ز_*iͺ}Wg^ >^N2q戈oFn݀$h:"BԽG[mDvw]Atwe/,3W\ZY4#n$qͣyjTQ .~W S%+~WG̾Gďce;}ARXUK 4 aq%lͺ)]װ 4Oy ~jwL% X.K̤)G@Z_b&c4Z>SA@N3<`ECX^9a[ڇ.d+²{G\7Z&N,f#rCĕ,%f\ /^=/i~cE?%RDaX5N2KNhchUWBܢ"O/;>|/*T1z̵9K=K۵y~𝳸ٯ˥V#]+-=m)vwzyƾx}W&>b~ 3&'3qz{R#^ß*qG16% Gd,\ ̐+W'% zK~eNk~OsstSyw[>UۑqS/"Ĵu$)NbLli\d90CvS( El+lޯ`ЌI$&w=M+_V݌Ϗ~e01oe$X2w=vnԗk_eԗ|t"3[F~WfҶܗF o% 4e+P'2WNd Lsmq!A(@̄%d෯5n;Yn~P+oZ@oVM]ZnЍؙb ^#  \|!R33jI<=X09#3i3[YmZK5~2=VS}̱(-̈j(xo_ ;Upo3gǫ| ؑHƋIЇbA#%riGYL >V0ji3?%V5 $re?Ue/TeÆˍH3 j@>c?ZS#Br PR>K|"n[`])_giy,VjuոxFǷjq8o.G~+7QM; by8>`aR%WTx'_$YTg2_ Zd>Dƞ]e*&UeVJ9=C3 v#:}A{i["BB(VK,xQIDJ?˾lk'٨abd[6(MW ĠUS ty5;/FsHZta.,?U"]WJ4BOjX r{Vg/r:#9J_*v):4f/1'7 Ú}"ꖹe*~lSvg#G`x9O3 =M#gEb}DzeάSy@T6YKd{$tn;{r qZKC&GHÓ^&%hS[~GB&ˌwXaf~}1NyvTany]L FX^Nwʉ#4< 3tir g4!ΰC 3OѯIӗzĠ^~LJ=S Ǜ9âK&'s0g@kЌi`MhXg0UPtHb{k]qZ*pYQЉ]Ż,Z͜,2@y+hY4Xk;`[/HYtyh\Db$'d,VXdH,!3Pd*,Ž~?~ͷ/2/x#b@ W9乖z{U@U Y}V(ycy>p]6< lD8˵U|!(QEZ%\[ځWyNJ.BBO]mb]j_g_'@k+, b0չ_b0#P21[,GM_.&Ni,؁/ -A /ZeCE׸ZzuoGF$0cOr,4Qv~gmHǚY9_* IȲҲr@BbUVĨce Β_j;뮬x!:ŽNhhc22⒲w"qZƌ-"cj/LG -PdC-Бx')O7:#BGr_ /[,`>n !V !j `$B"4D /7d/mVgz@I}!\ ܶQ()u)>ԊNX)*$;z)̕#__Pfd$/Zⱖ2UHy"]{nT>G02c2vX>o&.QpM~og}Y!̈Hm℗T 2h-= BL/x8Ȗ㋉;R3ҿ+`2_j}NVbk6fb  \ٲ]!&^uC&#Sˌ!F@? 1|Bz5ie<@]eY8T+@&t{~Wxy(ts{IBhgzkkDߺuv%`Zpɲ0ѰwNod|Rf$qs<=u yWx[x,G2i{ 0϶i %@+ 0OA{cکkp%P|oɦ_"fܽ%QЀ RǑ~q?J&Ez.v H&ӀY!c~fyNkĸF_+GvX&.Bz(쉪"+bF:g,*k\ڠ\~BJb€sLBcO8uax(|.fk ;h62)$U;|9NJsCC&H>?{?{y/2|G_HngX 貑1!чbʪtiӯ*LJO%7Zvd?SDHPP&,>寧 QrnEU(ICagt)lb`"D;gZ&^u˳wDf˸o>]RH4WVƼoabu۷r5Bvsw5iOsٺU֢g'ssXƔ_",s*f?UF% 8Z%a m{ 6U҅!r8˗\e08da&O@dG2_j{ƾf*w46F"{!8!q˴n 'x]c:b<8όsGd.د~N{?,ۖnESyXvasn"9 G4F[fƃIF}T*JbEذ#7H-PFnlϔBL / SVzTFr]1 *7V>+yѸ Dn -XkKSF:"=1)<(XfFbR_j6g;7P_'ҥidf>h2sQ}6qc 1_(R9_@55brTxK0ΆshԗLV3Ạ 8OV-izF-B>;0qy:1,L/&3Iu*dEHa8 8i ҫlr 2sHQ]eDd<[GAD;Ψ:d>Q^,{kiȌ|M;%^T6z+ۮA!6Zp88HNdF] ثXAf$>LDۻrWjď5;g@çuF>h>&B~0{20k/0\qC6ED^ W-"|,O7|7bVWt?Jaf`Ԝf#) \03cZ<0s͸&bnWY'3eie^l9;nFsz2cЇCg4l+N4cۤswJФ ȮryP5E_'8``{ ׾oz{(j>[U LBgڶ*0=*)Z(<全Y^]T| qk&@kgjm)FCUN|vFN}E20%Zԫ{]_U"Øu^CAӢ70xn&pMw#gSߩU* }/JG|]SW{VןzS`y2UwMzDid,$Hw { wI^} 6)mREGflʇ;LɸA[QNJJ"T9HZr 2q'[ąnUwG xKo,P *J՗5`e. Qw} kC`D {;k[U&Z:D*TH"_OLfk)6"UmbZR=@+J*fc.BC _x/ E0m? bi"p_JCfFS32A ݧ 3#yaD9# 2p7 3l7Xi4 + 48ֲ]\zBCa)/EV|)GS>fc 'ARO3bţL2?4fxӊ%c1^&※rz. b3^pfu_X:`TDMX_BMjUVv=ji!KkKPmxgjv.NVcaJ#L^4!˱gڪ!;k(9;{{Ϣ{w>^>pw]9tqj^^zSɭ/^^w\~}OGi{۫+'˗xgO9}8ᛗ/1@'/=t^}|Osxo>]grf./^=xOO;`/X'o+xg)#ȭc}\O翼.^>yg ?}.~qƯӓg\/3凹?zzW;ۮ%F#7?ZVK郙f;3lN:}pyr+L3W&d8gfs / x:?ߞfφΞg?=ޙ=~>{̟bl?6ٳ}׳gٻSuoo vӇs}7}Xx׃½Nhp {< ÞLaaO qe>-\l" [ɖ9--S^Df˔I2eFL-Sn$j˔IڔIڔIIڔI Iڔ Y!Yڔ!Y"Yڔ"Y#Yڔ#Y$Yڔ$Y%Yڔ%E:eINYRSk%E:eINYRC:eINYRSTkSTkSTkSTkSTkSTkSTkSTkSTkSTkS4kS4kS4kS4kS4kS4kS4k۔%M6eIMY҄mStU> _ۦ,׶)Kmʒ.|m _-kStkStkStkS kS kS kS kS kS kS k./޾wEǗu'/W3v|՚k5Tr *f#JRk3&Ż5b$}*U~N>jR*M|AQޣΗFJ%e+f`rb/9oF.:?MIFćI^yɽZϹu6roJF}|zi-w =T|^DwpE^f$4j֕?$FZKmQ7z֮{)֚K,J#6&%Nw7ͫ _n{C0P!i!ۭsUd!+Xur@Ei20!jM^!c! Φ ߥJ U0Q+ѾGPS cهX5+bSl9XE(1$;hK .Aa K,ޢ.uxJR}`EVHM ]bhE<{^}b +>LшNn 9t+"^2B =Y%\\Q53WED<Q.ba} -WEK1G~pҥ ULPUYk o󬆅p@SW*V `A+,I Kϖ&q!\<.޺կ1 2VVz@DyU\9kࡄXJF`!QJX}r"BƂVb˨w!dq&rwIQ >+AciٶB#yPRvO jB2=j* u(넌p;;Ԯ* l@6q26rBTu+2"DQndK%xߦd]`BѪiMdPU5zc\L2\RH7 #`;j*QoIb+)VKU:^Dˍ`qj0JS[6 ~rG.`kcdQm|Ȝdӎ֑ C%ۏ@ @M}S{9b!}f'VgV'ٰ!ԱfFm[]^hus]T%[Zms^`&0+dh5 ^mOh7Б&%a@pA31@ 1, -H+pYo{r! +TpGԄ>͆AP@=ÚcĎjJM1q_C]8 +"؏қ ;$OR"'FׂV0B[! tϾ(qKr"b-gը$Bl6t8.FX"Dӌkx=37t_p!L11"c Sݓbr6w K]$-%q].&.~]+b>3T)DvYXwz:b}J b/H뚵Cl4y^ I "-Ed8dUy ] wXԎxuddK^Hu+¸;6 S@y%P5"Hy#lx#fzlt߳嗋3am#c:7c]UY u5 lHʃ[?0#va:nx+bd!X voNg}̑Wg6"74-Et DjyCAWЫ&5y`>t 4ޱ5WO ְAmFZk,/X>ۡa.5 #I䅀؛cqe ]PBIP7g(6 P]tj7AɛmVNF]|xԳ>3EU]D݆Y`2[^uIhg-Qʱ6w@ܪR޶k%`b#SV܇W4b֥ JHmK,vG,&$@+ߵ }$V&8 m VA?"0l PC8QF>!k!ng}=ZѲ{R8T>12&;j TR{cd }&OkDn¾Yp6Z@ P9YR !; 1,nG!TQ2Ba6vw07JFEU!yΈL  M8p\Z F7ʟoMXlu]M#:SF3 !&m3"ޮXxw;7vElݫ9y⭱ܐUE btvc __#V෽DgvA3qQ}hq.5Lأ9ܛH#[VoVͪ]- i'NBQEOƮJiɏ(6PT}O#Ȫ|=doJ3 +؋}lGUv!(; NfvfB`UX3cmo+ԄEvgQovsdϐAw\}rHȩ +bOB.ydjEM)fUO_ mk-٨tyJX};L]Xuvvf+WBF;Br6z-BrNKyˆqf4T[jj PC7B}1h;HT[jkL!fܢda\-[-WYdӠՖ:|KEK2XpXE~XB^J2iI($E:c|QV})bEL˟EU1IQm}+ ^.Aޗ zy"6aoioI QkfUeUfZbKlH&0O?f[Ud//YR̂ZgIk ?0PKRߗjoPл#\w@TEčA ֫u#rKؿS'v" xx^`yaCcW,(b:Sط#|pԋM۩L7Rc+m?+#3Tu8_#xtu_\jwq꼲rC˴ZK_荡՘RxҎ٦WQ&ۍ)*bҹUeR y ̏^Yiy4M/Pe`+bX&`)@ǕN+u \vb"ӕKI+DvcJZ78>…jXa3zH-]_J*eɄn ^@dnIp5-*CVuر%\cJ}R"2J/9e H\GcמA,e˩Arz=/"'Q /Ǣg?06&vf[Ҷ3f*2",۰hK|n\kJX)azʔkm5bӏ䶾/:qq\8w h|ZS!cI/ٛMVLFCc(/ FP:s2%O\_8yZSR U%4FU:nҸ֔0Sy)hzgb^c}1ڜ] )*v%8JKCc9X^t+FdWTX46JhφQEC#n#Ҭ̔:X/*Å{p]gJ $@ƇAl0fu2>D%\;H J:^Ѷ7{ d\55lX/vc}RԦF^|DY+q^mM)Y*RuTr np1M؅[!c Þ]Y?e fQ-޺^"]kj,mKE 3+2GzZSR90dW6%qnX["uYg+ꅷOS,j$T)w>bƙY?FW"nɯ* *>쬒8@u#Ȧ*k:y';~ . P/ƨm9|) s^+ KNF `4׍<sF6K MI#۸~LI#%W]y'+u Ύ|+L}5nw`=k߫S9VV:g'?(#u[282}QOvhyȡÊ`S K2^OꬑZUoZ, 0EAp$dSݭ_72r}ŞT'6#h0:)s 'u?T|b# glS@W"aϔػ%uud>.U\VJqy.#:@̀&>[lᰧRS,eҸ89Dzde.1K{b>O!PG`D=*u%ݱhպ d|ސONJ1zBĚ@U Ѣۥ44œ5쇖OF,vX-oU9=s&J-\f+qoigFF$㋡P-˧ À$]=S UY Lq<i8ИNYJӯnC56#'dIih$nk:Sߙv܇ݞOB&QzrKSH/l יz)+h1BpLCPGbLv{ bk8X3 C} ݞSp3FiTSygUcv|+id:L]#L=J7Iʀ8h]0N\pkFܱVCDT(p)- 9%uN7g2,@t }ձl,:^ҳub|GuƚHTqak*?o}ku "ƚ6UšQ}oJY*Mɦ`K:Hy&R 8k#&Yw !UZϴ^DA2"u&x-0;KvoJiO$JS1$y._'rZ*O@3YPoN\zAwF:.R۶54r)'#A#+{;n@ޢ_?Ј FHWm[@#Kl!A&1W˾4G"QϜeZUH+J-qՏZow,2>{S+| q%mbƒ@ƧÔwȶj/-DFҖW;(*8;Yj?bLOgBfX)u[!DmK!c`+>t*HgܰA!C4>DHbʞиn@aI Ȉigox\*v;qkdlHȿ$kݩNWSOU 4[5%X W޸SaCp"gKcss꒡|(0]zc%CIT$1uXKFilÖ(]F6iřFTS֔4Bz閨̙\͓`jމ3$$g\) ky /kp.a^mMPqr6 ܵHgiZe:bR}τvԙIYJcӡ mv_n`4MwJ0y54.8_}V5B'׻]J[2C%PQOi+bEHA!S]Dɕ|mXiȦj }Jp)zƧǙ)H;|c\׳w`p"HPr=+y琘U6]]#r%Ghq*|$ځ)#G _F)`.`(I&WPcb$n$6 kRr^I)8{%&[<*'SWX-ʸПkM&NfݐrVN/L)\fp(4W~guV&6SHA8Y>S%XhpLUj#@!wA@Cj[Zib,f$;Z(u)AZY 3 q`(>%1W!JzUKbf&O]瑘9be"ԝ‰Gī 殷Uś%r,%GJqS d!_&Sv!yT-o{W aNr)WQq]F!&+܁`w/5r0NnLN%L;Bɥ/f #nUo_ '\>]RrB$᭝}`NWJM̭;y[&Hj4cvFL{ cq1QvvOӹF19۩uɹk5H|"Yz[B0eS\wK k:})]eqD7sZ,^s1nWvOCZd r}Ӑr9[C&+s)*5d1w6\坽7Y&չE7}W{_1W%z_|Aʾ\aTY!b7e>VZh {2s,%s{Ap2!_nN-@ԯ%)ܙ,3eMovG孰v]\h<_k+l=(?El5a;yw;_P=sJ*)+HDj(ml謐a@>H+qKI&g{ier{9f?ȷp35VjXouZjY[IaSc./$ *qd˯+;LtJ^7>1ԦFFb# #gMcn%:Θ^e ˔znNVTD6O$%PoR23t>${ "W6(l8Ok< l%O rʑq|un(B=_۩`,Eo~s/y3cIO]dCSC%vpwX;Wjɔ:)&QWϴO rUq񂛕SpXD27]|*|/(Y{3^>9B iM22]aԀ 3%`c?:b"JN 2 Hܑm}x/6pG)[KBfZRLRTYȶĮ pL7e[+U"{R0Pf I=f௫o #.0ZKC&59gʢ= 2+YTBVRZh `ΌLG`HȤd&<LWFlT'}A dd HܹQ ` %;s)\m~mP ^>L^sup5f Kn0n6M(q]5SYYզMS 4);If0l&AUl\&٣e(dV2gjFo?f[f͕wp4ѭ@s <ݗѠwS,n)Ҡ98tWfd;o~Jw|Wc+.x{䑏 sRyL;:Jb)ճ򔴘_oho7R#.X/{IɘVEog&JNVXYϘ 7kW;+u=£q_k+T,vi$ &/;zNހP^zꀶh*+EnhȾꗰR3]R\d rYf9(>GF1UJf+?ۘHL~v6L @<+u1O j n9q)+ d`_Ask%GEVW:/}`:{<_[Y-{VJd`K-{n,.@(5ep6륩]y*Drm8QgI w8lX2n $Wc$IPoI39KSr 3X_JB@k})3&a.JR=Q |LqErj6).&QmGJj%0 ̞ -5+ziμ~u!Gkq 1a#MX8a:m28Yf8L9 8fY$b"M }s}1+Ki1y./IuPM!sueΆh7DCH_2aZ~kRӑ\ 1Ij/5Bt}m8]2+>Hi~Lݪ*9nf᎞T8J1&wo+wptJ1+|a?_'~kn8? 3~k %\bwo pd.ѹ{O $KTdʒ!VG!] 11Y<נB̧MzdwLb>5װސp19jj8_ >)"9 FA&_8zf!ζ6I[b$/4 9Bf^3ɐJMD\ɒS]3;o8Ħ f/,VWl;Da5j/OYpŶ7YnՇ+<]3}(8^3jQ_S&O 2)h׊OK:\Qi$hWf/mpag_$RI;Eif)"Dr+| yXV`ZpbucZ[c/LDpp~umKP(2C< k,T*:Fvz3#1řIrS)"EFLȨŒ ImmI;ܖ3Saj}J~+&uJw_ōB,`7r/rnM|#N{ f^m*ܯxW f r\eY4[CaY.E`Kw!g ;G^Ͱ_YJhHe(_(#}praRk2:FoD@i|+<44hÔRVHR25-cQ&o1IՋmbp?̳S/V>ڨ;_."Ҝq;eEsBH{idJ?Xȼn_5lv6 ne6W.cE`_SGVLTu5GI}6"#EZ@ْmg$-/x" e 0k13U)qxԌ,s:&'>VZ~Em6W:Ql][㪺RpRH rPvfa,2/ "D3SaZBV.T; qj!/$\h .8J.IeDvzT0Y{@>oJ1#q2gDx%MƁh$%GI}%@CȶG&,@TX/rb xX.S峨B&ڋ8Z>}\ Rs1:B,n˥}sF|d7Ѷ\%Yn]Υ5܏R OLVeKc&TH+..$f( kUf"),z |)lk&rdR#\^!E}nk 1~)\of~m? fb-J] 1Y\:B q~I3prė̊ o̶֖'1tZE:慿dCٹ0t2KaSJRݗR c uo 1q=Wܽbp%75fNnEgd fvFJm5K٪0s890^ 2YG2u]aӞR;A !|NnƖLA*"̆L8[Aa@5 Re[AS_챁bP CI0n 630a_wV}RԶ6Ǒ=9-+l %X94Q'A3%C^;4CkKtiդM9,ޔcs ' )a#ZJϦs* j%{۾tp@PHty&ٯv~׻_*+/А QW]'9m*+ PR Մ?V# g\5S7sJK a,.,Mi%54{+<`ebȅHՕV3uuo/`~N8wEPKR +86'K+"TVR42+Iyx ^xj 0-l "W~V8_ZsX%w݃}_"k[;і(V:kD|/;Wė]z!O4pq8\R.Ht8h/ ȈYSA,>6W:DD5`!* -F&Wx/uj_:TEs+$a2'R 6;5N" *@8`@9Q_^s3^ pLJ%Uh e/E?rrnᒢd|W FUvkK%W'rJdcd\ mKa+g vCsnXnyd7Bp+|2f5V5D͎.ЌG&xL]KXW36[zRJr#15荲R,siEC ++6 ` w/e1k_GJVXIS/7-BW*8!mb>U5[K 1).8:^22u:8i_!5tА{ 0Kr߶P bY. YG(v҈YAgc_Mp%/u%1rmږḂ0R{I(|V;3w@݃@fDұhMs!s4ݏҐI'm *HSc=6)d-HSmSa&6R/.ً} \w+|:95  ma\!暕zUS E&l_fKE40nֈ9MQaɕ &5u4YhJܲ W$Kh|{IBT(/aTՈ, vEoS%upjm* C.66f4,ʭsR9G[S7ɨ-WwA|*5ALHcd&ɜegBW_w4/wj_">0CHɫ~ +aTiI}τ$_ 'u47/AsZ'y-_ȗ(SWprIW37FYp+BVi;H WJZU Z? #~LBqۼw&]KX!=C{k_C_*2 v㑒}V:$T+ 3_n7r'OKL?J+,i:n\i]4w俻Ɨ22ߩG pڗ|_R"rg7IW{DyXSb9#/ʑz ^TX;%}RW,q76X$t^eq !(Z "WؙScW86#7U:춠BI8r!iةSŀ~sSY{7(I]`iM%Rh,j'QX xb7L/+re%ƫ -IYF< .|'nmّSږE#LnUy &Pm#2O)Ӂ#S&\8I~ԥAd80!iI/ 5##Fk_ 4`-YKfyq%aTN{¥@si\VDs`{h$:/ h) f6*kV[hcQSY <еLh Q4ql9D ^Ow$f:q6N.88EƸ.'_9EKA3zXFl|mB$sL U4b*SRʯ_0'y{#ɰiu-VWwhPA$hgN/5)F&cIB_bk;_L9]0~#g% ȅ6^VN_W~Զbʈ,_0Xg?slq0GmEB|BFz'wCljOI^ZALɾ>h+T"|Iǐ9a: 2ll!s!tx~ks(A/Մ@jd6TlK>K>80dzh\)쪶>0u3 )Hld*ڦռHS6{֞)K'bXM+:8 E"n{JKӆȖ{;6 !!I !a {yM`"*DVmKZ! `h>F.9 +ߤP6j 0!vJdkUk!aX=5} {7Z>r@ȆaWâf"Ehd"b20TO4>rzilEjT2v5FA&#~(D2Zx5KA"񚳧b/å 9~q2ݖQȟA۶ Wꆒ+òi&nUD4didud? Ss@0se>a-r^3^3Hk^}˴p˝-:4R';t2șlV0{N:f_/:'0.6t!Omd}i)f 9L%'n03/ Id0I*^KA|d4n. 5##j2@I?mΗ<6T pD! 2 +5lk۞uxe IO76k.29bDuk$*%i] .H<Xr m޲;2˖fnY- 9HЌ$sDc#13D~ |Z,ZĻI1Y9] ?vkVl}!-=$WҺd{1KD@~}} dr/8)څ d@0,sR#6z57_&ښ'HO E|ܧ >Qsl>g KqnD? vG.[R/"w/^"ǹy7 O"'qogR0fbG(_I#jN +p!7pwݦF*a8jxCaeOD?iw3|kd]4Ѹ{I^S\\k|=6&cӏ d(I7 <풵vygE~KYeU_f9blKBV b \+ g/:gFhd*pqcx%&$=Tq"2}7{N3U٥槄52 [.vkLac#?ld_ǯPp5ml1]5ogE#1 >f%N3)| ` n2aCF!bB4K5i<5%-6I&1]{XM/u/'-t.6W( s3FاB'[@'۞TPlS?nXTXeDrDgA 7ᾷKb y½ `Fj33*D$6SH`S!yqn 4br3%q 8mKBmHs)l򚙽0(|S/~WA! EAɗ/Ob=jK 1Wũ^ȭ1N bݰ pKAfS ELNnke8U6KCΌL.Ok==Kb&pr W:xѥ0!sl#6'yr=3[쭐ҪjDXjpiȜX|d6v/6~^6 G|ո#H!5lKzpvEaFBa[) 3r7H 3ULt?r+|lsy{Hk;l̬t\v9JC&Jz!/AF֖ܲ;R?29<`ә"!HᓈYXcA۴H1x֗jy_e&n]%Px5ѭsBiahzI3,VnqH6vf2? ag:ܼLwJ dvХem8jFW W^+]/@@O| ?>z'ա urpgJ#5q)SdQ|!.gǐRK9-eR+l ﭰS#Ѯ~%ߖ2w+<_k+䀨$M|ds&So7 No C6/e Emw_S,'w2 $w^7##-k^4GzeQ~#C; 翄LVAlyjV&)un_Y*ţ_IU;r^U>ސ{1HynSN H%e{_'e̞MRc:"\;)qi|-{N4Q[S!`$Yl5G3R%nH b g.>7 YY]8J">@Rސ~>FjqUI-pi v@?yUHِ="cME *-b< +WnJqS_Vūt BGf < na理]E)n壟u.>Kr*\Af'e:]KfV|Ֆ%`r"KἴG:*7i`"Qp8FjG/'wn `I] 1%'ҕK#XBkdo,ۑ5T4`RS+-[!+@ɑdC).PmlAfHYJDt-KfY' yɅ/0bX3sv8`z! 1W !IǙQA:Y-bR+ձҀIe%@E ұ#h k܂/;3)5Sl ʹs5qSe& lY1X9UU_o+VJb#`^so%N[Kef;! / /e/m/f]$/c 7v:ٻrP$ٶ^[52VM`Y)QPe"w/Z30p'jHw%%`kC쵝]c!M!6UdU7)۠9mݬ`> Ȝ׺7YFH9 :_;U &n @C?Tvo35W;_wt⫝\+ ̡L;)tOr8ylKX!;Țܧ|_ЁZ aPYuͅ&diԯeԲO}(+%FLVV`y}$_I+cm/XI!GaTF[ǀ+*zNGʷ 4g 瘾SVԎRn,b[P#WY.87(7Q?K)ITNHL =֨@ ŪIjf3k$ ^ ,զ|vSlѫ*|f;8p[_&n .8^bz /,i1-;)un3L-bb`"ip6aߝ=a4&n+j$"o䜞m|)'Lh/ K|$fKeKA‘̔p]~v @!p# y1{D?OX'cbn{39IW.;.?k+s?@ / \TK!p6V~" Kg1AP/vI irԝScϵV\ǾϩMn$:6R1CSc#F(VFH43b8,ȖB;p5np(|[ZH܅mQ˿ 2dop;Ym@Z.Mլ d:/ wqP E?\p{c+qųm*lE/L'%amKVl2sKB˕FNyd/ /f!ř$ضoŹA}Ha 2Ix,D.?f"2!_J "FθVO%1ۄQx%17X[7H%gSfA! 4k9M\g+ 3_x+|.%4dRy'P66r vPc&fկ#ܲdq 3n1Y6$4 3FDk 8neR)0U 3+"fF@ҭ1cMxbZ1Ka3}^p3Pa0ts m%H ྔFMRspudܜQs6ER67I%tNML3 ;7 5t}7SuUІ宦DMDquRPa'#wc=B͗}V-ĭP2~R$CJVa79bHd'#Ve9P[*daqM h_~,ŲUZkݕboY-DJo^͵U+ xkI 4 tDĀauYo]]sHӬ.~u9jd>_"< VT9(+^Ƚ| -䔴 bV` #VަL Kx|.;ɞW_wt/l}WRvqs/aݑpNy>UVPb92H9vumK49H:1IZ/{Ik륦rV bX3`54 +O"Xɥx\j:&PMd~#WF;y^2:#$Vݘ9Ȇ 7tP}m [%>n4dϙfE!J#[pV(R ?S8Ŧk_vK*JLG%uwqe;nL&\M[!"!sXi]6O7aTfGXv+ <۾v#CZH5h_YntO#L"ǵ`_.G+FFf$Qb`dK4[(]%W{Q%^>KhN /!}l.7hkȵC/6hY9F:l~2 tJuL(%jWyz/~%ĪNj`*+,svvP\ϔ:?:Dہ\YaCM e냔PKZS_ q7 n0xgýV} eW  %9푯wV>B袩ovV go @j"ueo٥f \K}ʠƥv^Sq.}J0Q |/8+Ku~L@#;/?J+/k5w^~ҧ,"ōy  RC_:JWG0V$ &mbl%=/u R;/êipda ɒڅi3p -{6Έji6Ipn ˧}DRILVx $]+*Ro~I3s9#C6l$drf6(?#N"{UGr^tXDY<'{.TY8΁g bဘʂJY $%c-M~~UyR@|$#Q!tOyaI1Ky;i_I+d ͉IDY-!prnI"K_E+ػu%o`N>K%"BR^z/.6>k~G)dqp0λ|/%'DN*Dd rR\1G/kY),҇O䱶w睑z?\wZ_ urLݯYhղϣU<ޗ2uҧ]HV  6 q&'W*- [Dd e+hQ&0+.7GU,Al]!9]~X,[ iL"V0Y[.`{J 36 ymK^)GRvԶ䕢K ȹFL *#حQV>V陲9)|6x+ YSST!qf\>յ$^휔~1<+.Ș-:_)"o)8 `#r?0;ie?lҀIbJdsy,dKdX4]) }AG,})3(2+\/"1 0cK+}+|6ޗScb\ahB0܆m)|u 1 =[= JJE/vFC&"\͸5dRxo lLdۆcn d/ Ewb˖ > b"E3RaU[n]}zD mV?Sv!⢇;6 1%v,7Y)De6 T?TNoiKωNB\ g3䫍;_Ɨ/M/k(+D ۃ dN)ػ>W"bjRQL*=Nq/ԳD *+gm|er3'}ukoY4HVd4xwV: _HlM5]4 "P>ww ?L4.VYS;&̍RhбWV`c@BB=DRV֜Z|_TφDjÅ]~ҧn2hiWo~V;9L+c9hVW,}î::L-} O֗0Sd+|]MckyR(2hں)11jȶڗzŹsxwBs0в':%D̺"8XU!:(,Gg~*\) u [3QfhT60/^KrIGJvrNVHY[GDp.B51fXq*)8ɮ`_3Ax(.4Vn7{Sʶ*R8Oް.8wnTxV lkAIf 3Bf[_9TynlK(4$܄hfƢL/uCe|O%!873W$dHPLΊz (\','ݳ(|8s.hk6/d4n} NڤOB?`u`Bq7>̵E~RYFHgDeu252{"CD䖊dnɐ3SYM p2 (|_%`v8LvF&1 ĺb?05K 0W"H0.5Art*~b_ B)b"]i w/s݅BRވ eWL6hCa"mI2р i+%3R YuQQɫݯ;_;o/JDY\Cj+2YCa Un3 *Ff6WM\= PkĖ_藲HKl>vKXiѝtɎuQVjav%۽jeNsS!_I++aJ_ OA):Bou "җH4D0D"xmKb?8: QFіRN H ځ;XAGG$AA4V)l>$ln˕#zBn+f*C;|ŰTЧ4O5LB897pXC/| ?IgnBf+ddI P(HtY9^K!f OQBL(n;+nIbͱ-w̧P{0aaŋcFawȽNf>/c_vA!֌}m[ȼ4d/.(Ȥu5:KA&|6=ߡI/3)xeԕ"⮪_RfpfKA3q:i*EHݹQnfL5'B&mvjd>M4>TbZ0ӵAJ45_ 3  ,p۶T0zfI(dՉYGDū!8 fznyrB0 `>˂iU^>QmVxLy '+T-ͫ"`ܟQqrʣZWsoH6(FFo 8+qx+y< @n[eiq"X1Պ /.36$0$N}/OH0)2` cvVpcӓZ>dHL5Z71IK_kee{̚ ~`^B!8$"횞նmz]NR=SNœOZE^ >UVJwB]g !EVbN,__HH>VD.S0i$R9"u #sUZh5IVGx^+%d< q&|}T8˞B;a}8W$VY+8r䙣9UB(E罼EuT?(#W m/N.A(zF,?*+mhS9bdṛ҇qZFwVr]naZlEZa $16uwH4'WU9 ɀڸ56pwԪB̕;D#p6b>l{n brmt~dɕSwW%Onɕ.R2M|&8aۯR+@$2RIāaHߜ|9!K%ǒS]R\> ňĽtˇݪi$#5W7vG:UWw\XR%ͼn}K.E}9{ 6-kHae85`*kSc0Y pJK 0Z&9{7 01aR5$cvE2jJ{I?qj%qM%-S!ھ*"+Q 1W\)ڼ?(&e%6Z lVqn {_! 0ơ]\҉zVm ekH:\R?K+s5M~/J?~ b(~?rbcV쪉Rfs Zc)BFy6;%8"j:o_FиܷeSH|=ԯ{ 3}DlD!jz5Uߤ rsޮ>!r#6\K FR/ p@&i(9Z %9Ͱ7H|3'"KALܐ"t ׫ЬI.UQ,lKf7H+VI &\x̒ҮԨAU#A{6eh>G ![q8/ e fج|k T-5\39 ju di2"_*LqD 39In7lRIcR8Wf {*UG 3_*>\ocֽ6q~^8]d^cCqkJ9؞̧c"US70-"`3[c&1!m0d .|f3'#`;3 3+p"y̓e#r{ɢ0y͙sVKAfLVk$HlvmS R[y{]]K=rcd~ ui*OEkȤ0&gV`f<z6y&rp/Ilks3QYUkt~W3((h'JnZpSi e"c 9jGA Nq<WDTJG*fH+!kl +/CTVVS)ÇݛZ[5΁#VCQީC)5qYM}+T83;.ԇ8(e\!eZ_NI}}Yb>Pt/2oQraT*b;𣴒n糦dI~ڝ8#I]m,,z}5_[)jE_aзP< /9 کj2I˺іTؘaE: zJpķRWiIGN%pcFO>)m@(\y(➌x˧qw[X<D>_ڬ-ڑSa omi{4HNO (vFVTv_Jdlv.@=+f3\:PEgBJ san'eOǹ!)1ݻXɮJ&^ ^GMeASh).3k -_Xsе^h1{ 0!fN0=s%φ&8s$뼖kQ*DX5qoR 4up1Ɯ- *̳p3Ey)Єś<C$%J<_@svpzRnh>cY4BK 3Wc0s%O,ԁ̇H]ġ[,v'd@Cѐ@ƁLvSYH0KAf]D|SvKbT.GqR,_ 13kq,X`d6ll#IsjzlLm)\7! +mQ@wnZ҈ b\^f¥sR`JȶI* A\=]~)\/dvdCBz#XVYtsu{7 1 rq2; 1_o\ֻ%SHr]24Og`U7L6C:&nB#\{Ǹy]uZ,Lf51[!&cI<~V%)k~MY2 1:WؕC)urd΁:ؖr'"5@lTIKwVDRXJ?/*8atƠL;$<;}O/[].ےnJdWO5_}z_Qg8M]jUU>QF:e#w\raqdUc ̬š9K/wD(L@kcKe"I}#$F|W7VYoR Pq>_i+kQ~t@Z9Xz_JC䀀[_x' rN6;UVhK*I;iy#7WV>4G'"]vXA> I Qc ǀӚ|$}}_\SlKYE3$TI:cafVpܼ_ 3gJZU}:d3;ڒ̤jE-i1c+qJtleaC :Ω F?\\xJ|u>:)ո|c_H p,ۡ2[C?){Rǩ rH5XąȵE3)(9;u9e3/#E")r[qǒ*3\)$J/W!C#+̍c͜pJ$_;3/ҙ*,!r6.RS\LN0(\8Պت|Tb \ s\E{4b,&f1YbbNBLujDo@SYKeUdR[E$Li&yk,LT륒ϥL.s,".XKA M]U.+@"/[d>}8JRŹW sHLjuW@&-b}mMf7,öݤ )1lKZ,41RFsXfSrɒx\1aS~L/3pۥ;!t BG`[l%;iL:* Er)lKdzW̹h[s/BZf#-`HD~)G\rV\RX-8HvwL:bkk7 ._9%g.-ʴ9уd VpI[[l\2voHYFTȶ/6`9Iv7n2s,/O~+$ ]w +Iz2׮|I|ZUx8(DPϧzmb,֔c~2a)8hAHc3[3 hbQݔҭYLngWvf@t^ڢe#R֡ \Y~]Mz9:)ͫ];^2/JHզD9P`&޻ާJL`I 7|DqFxV<_"^I y/; qx i}## p ] \\;,ԣ^D@3S Ϥq,S9XS#nN,ʞ 9Kd\woB89lk'4ȩ L]bw*_GhU/H57ԯ0;H|5;z)\r7YC/m9ҥs :\7Jb>tlfy+Ɉ#FB̧Pd1R)Qض62R2*i/QeiwI3F%#BL`%%UƇ-o^ 1s(x;1[*c%֞.Q"&x-ۅPϐi_;;_E(Sl3+?1ylU!W5({m1*t@ sfbVVŹvxWY)TW螺jcK!&iMcGo QRq~,!ә͕.̹ quga1Sӭ"m@sq`5n .ǸyIf3j363J̖ 3k_*s~/Ja&y;sAWVkpD2u^!)[/%Hu,Y,Rh"iBR|N{V^A1[vh>fKZGZ;*nml[k'ީwKTę!w[aȰl&4$8p@䢎Y2uęo^Zy({j5]Y"w#A}H2#;WVlVHe19 Ɓy_X9HZy,gA147z+ e.pK>ل82lYbca7v3h_ƃb )Xi  #xDQNCG -@ٝB(yj|3̵X iȗ6SL&!oOs(TorS$Xɶ K5{*p%3d$P"R )3D*K`0M\8/uNčFDIfpȩQ"]-Up)3(!)PZ1sHrm7Y!fam" +d@pmRHvUh߽<b WoҴJB 0Y%8ִxWjZɪ,)|aXj}mC y>(|sZol&PE[V4d>7!.'1#`_"8:+V381F.Zz n(/Fxn5YjL}j%%Op8fM\ .2+ $mlZs"VpI<Ӧ=| .5 $!Ue?Jd*bƗ ܅ q)hk 銶K hc3'^2x=8XY[G6:x+\Ww /afIm)L[]Wxt}Z {sav c̍^k3+2[%D9nUET &.WbjBL*phop6YM%-v3)ŒT*| w؈.(|(32?ոpz2g!/Cjd$ѵ9I0;űI(_W^wm/^^^c:w },ed*+J"emKXRPy&ZJD4 r~Qva˝/ۅe:0~#Yꈔ8VJd`tfW;+f"!V07H従n;l}pzTD L\;,={yts9ðѝGjl H*쾗B.c !%۷ի1]M'y&Dl¹gm%SgzP/ʚTW` |]V2tfJl%ݖ9NX Ae^ 7%dai4Q*'NJu*̬\co\"][:h_LKhۮM| ʄ)D4Fy4gsp6K@їr7p)|e9f 4!&rm_ 3Sc2/+++*@h~M 9۾34Sw˧06x(̬d`!Z~)PYfR6, H2f} |@B)xŒ>= pmu Ȣ$(_-s'C Ṫ\Hu7dvn Cˆ)&ͯ #1={UGruk‹&#`q䊸|*%#q&n*| +DYAL—D~*Cm2~dBR{T:֘ϕ|q}.&aK,_Ȩ9X{#|4n? +kjb+mA)P;м_0.7gITL碷%/&Sǹy !Q = .4Bܓ~I+E_I:j$*dIVȐ5Yhn~I+J %&qVKvZ;Ftl演''wIA3(<)1::buG<${.R/)$0K^8a_f9_)X†4 r nTץ rKR4FR+$.Eө,BI=́ڶL]860SdJ@I@ܰnysnB"%Q!p)`SL|Taچ\l\'g*W򉮐*}DTm vRA:3/)urf ]8bDdMf]M^,ٝ܊HId~t;Y- 1 '5zCxȾ\gu+%sfK^GSC>Jai`z RcڄS, 3H_J.*E,,~/3\8\.B"xuQdr_ n[7AFZ GܣS +/,"SOMKذ띕9+C\KYaoǿLN[Cv)Vf2gT`GLUӴN]֭.X{5F}Їci0 clK]Eؙ9O-±ǖ ~g~ WZ8H2kt+ϼ"T}V"ԵJzn$u(P3%̐trX9#SGY#ľO YQG. :'rKC)|{b=R;!g /yXQJ4iSc$%x,Y4S,%4sEvL~IJ vB '$P"J JLp_ۋ)t No䔮b ,JeH֌B'DkYZ|8] Gf!3t|K6ș_.@f63nzL N$IL+1CZmZ_1c^uvD%)d,$}d#rOIV'"+G+|)F+kV_KAzj <] 2lE$+D f?2y8}1i͘VBLRɹQ]Q)mRv/usLe(ݱQLE铈Ib?2 Xn.WOѫ2F"~ɻPR#viV$3%سu˥W(vceL`ޕ// GZt)$ :[y?䈉K8vA%~mj{D .C]8'y+|x:>\;t+\v Ož6?*qᒢXQ}{`ISVp" _q\ᡸOm c e%!d@ΗF;|{#yC54 +&Lv+iYW[ \b~ ,|Wm,߈°]wf"ZT: in J.O39fRA0c<, ȥ0s cTQD/9i 4W KL++D\+5br4LD"@P2t?BZ%q3C_D*Թ@dB)h:Lm,#E=硼RTp pǖGTaӁjԞ~I3 'J|h3qvdIʮ*z\vm1F=?4 3kiTJXXUYV` J_ 3-H8w>]WVH6Ȟ4V4S}WW2p\V;Y܏RhiZM hNK(@cz 3W2B^l_n?2{p:䂌Ƚa>a-fVJnPRY($)Q S.)3z@hf6PbX&]Tb[[hmyuU.ǻ{HXM-kL o}-< 42h]m@&.& oz5Gkr^#NT&'J)dYO峴Ad3e%QI0 6_se3clo0 K󉀡i(zFF ֋lKYa%#\Ɇ粷wVb/3,'sXYi#(5ޗ:, ]tZfo\rbwQrTxMٲ@ SrpsIH \^|WwRI68ߒSItri8HY[it;VwCG!!/ʤㆠU:M`\He4̆-Rv$0a@j5N+@3Zi, W-T\=;],9x(p~RW;WqĒ8*||p|~K 8[=?D.3X ȹH Ws!ڶ0)j2 #sǵw.I$ia5xrAJwblȬݏ8I:< GAG+$o2;b_2d+#^g NmKY;@x7hژa7%- \R(,̪ pK$ґlR mß 093jdܰ/a%Fp|Ю0)wz#x+KĬm. !@tv@泇;rr)|ȩ,#Mo8(.nY)\EztVdCPd0CUl.B ~SP&vQ$)BKAPBYXŵI d^Ĭl"%BG@:U( &{ժb\&8b .۽d HDfp ~V^Z䪋k% 2׹͞\f.d>-0*o-" HVv֘9)F0 fN*rWӕ5fr{_HZ[7IVs㪿{;k6|} 3+S^eR&dNOH +L@SƜ܈ѭ+$2mIbG=^v߁Tlssdf 3_ˈR3[E$}Ӑǃd3ɋF74wg9%o?OO?ß?_?_O?o94/w_?/?mٟϿ} ciz_z PK c%G_rels/UT UlUux PK^<4r _rels/.relsUT ,@LUux P=O0ݑ]TjjKb%Y>ÿǭPE׽zFNQj]zu{|ѤI5Ч9g4﫪Ɨ4;%o .ߪ/(gT4rL0;NׄR\`bGGWAGP@u6l& PpkPx\}PKe%GԨ.F[content_types].xmlUTUux PK c%GAxl/UTUux PK d%G Axl/_rels/UTgUux PKd%G+,xl/_rels/workbook.xml.relsUTgUux PK^< bxl/sharedstrings.xmlUT,@Lux PK^   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-1.1.0/tests/merged_cells.xlsx0000644000175000017500000002207313146745673020057 0ustar travistravis00000000000000PK!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-1.1.0/tests/picture_in_cell.xls0000644000175000017500000001500013146745673020372 0ustar travistravis00000000000000ࡱ;   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-1.1.0/tests/profiles.xls0000644000175000017500000010200013146745673017052 0ustar travistravis00000000000000ࡱ; ?<  !"#$%&'()*+,-./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-1.1.0/tests/ragged.xls0000644000175000017500000001500013146745673016463 0ustar travistravis00000000000000ࡱ;   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-1.1.0/tests/reveng1.xlsx0000644000175000017500000004045313146745673017003 0ustar travistravis00000000000000PK!ϛ.[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-1.1.0/tests/test_biffh.py0000644000175000017500000000107613146745673017201 0ustar travistravis00000000000000import 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-1.1.0/tests/test_cell.py0000644000175000017500000000534213146745673017042 0ustar travistravis00000000000000# Portions Copyright (C) 2010, Manfred Moitzi under a BSD licence import sys import os import unittest import xlrd from xlrd.timemachine import UNICODE_LITERAL 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_empty_cell(self): sheet = self.book.sheet_by_name('TRAVERSALCHAINAGE') cell = sheet.cell(0, 0) self.assertEqual(cell.ctype, xlrd.book.XL_CELL_EMPTY) self.assertEqual(cell.value, '') self.assertEqual(type(cell.value), type(UNICODE_LITERAL(''))) self.assertTrue(cell.xf_index > 0) 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.assertEqual(type(cell.value), type(UNICODE_LITERAL(''))) 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-1.1.0/tests/test_comments_excel.xlsx0000644000175000017500000003667313146745673021511 0ustar travistravis00000000000000PK!$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-1.1.0/tests/test_comments_excel_sheet2.xlsx0000644000175000017500000007666413146745673022767 0ustar travistravis00000000000000PK!aPL,[Content_Types].xml (TKO16A$a}S^F7+H Ͷ^ Fk]%KX/Ln2EަO;`FW&62w9%hu`df~}voynM:`fbQqM['g^-1\r(Oy+A*] ǃvFT M1RXQ~1†iZvJ cb{y%!uW߭kעȃ΃X]qMP2.lO'x_Hs$8Mx[cʥ _G|Vgy8x됦a5|Ppi.hzg٢~PK!}T  _rels/.rels (MN0H} PnRwLibv!=ECU=͛f0={E tJFkZ$H6zLQl,(M?peKc<\ٻ`0chGaC|Uw<ԀjɶJ@ت` %TKhC& Ig/P|^{-Ƀ!x4$<z?GO)8.t;9,WjfgQ#)Sx|'KY}PK!Gxl/_rels/workbook.xml.rels (j0{-ȹBmB^[&$}EBS{1=,z5#u)(:ε v7 u*`S]__לHe+)X4e>KAsAnQy 4Lm} b7wo7:P!9qa ԱEVpG( wK2}Z :b1]xJ1`%a>}ܓEu,a)6#Ͼ PK!mJ%xl/workbook.xmlRMo0 tOq,Xa(֮=+2 $CkChy22_H#w X'dSZBSF>FsJѐWp.i{>s&]FKE9QnljИ9ǧ=E WxUE,gRaaRƈF{JY;/a";WȻ(Bz(2ӴO6FVeӛFҴ{}^im|Q\oY9w/oo' ?I,ua߆,|d0b_AJtA?z8ES^#?Um$,a&xqZBcE@Pͮf_(&W՛;D-k^kYt-i诛%tXKFt&fKkƦ){EuԢƙ A((0A' m>-_noȮ'0r]EPK!XM  xl/styles.xmlM0FIؖ$C!¦Ы,KX}iY{^虙WRr!jJZ,)NF %kNt>U?F#_RA%=t;Ƣ8JwaD`92,vA&MְrŸ́<݃km4+vϭ[V\ j| c^)-.lːT;DAI׈NvqB笪ɉ) Pll<[3q렓Sq͘yߑϨMJZ&'K)KkA6%|\hD,$;dp{>~1nvZUQ=F=H8w1}w[{oھkRV)3Խ6=pp`NҳѳD# .^ѽ^~꓇ QҫSPK!eu#xl/worksheets/_rels/sheet2.xml.relsj1 @=Lxr \C,t`iu( zQӓՙL1hun  v3:$DUJc)QȞDġV=M|πnTg \ tMu}Cy~gU*偋 iN6,~i7PK!Cpxl/worksheets/sheet2.xmlT]o }xO6i-ڨZV֭}&pm$]˔>^ s98";N<Znl~;y L =Wx+h}E h榃Tj1u: L 8^Dɖ}*aeVCG yٹ Qj򊗜Ӓ[Lhv2O'@o4K3fܡThխlD#<̅C UASplO(&mA =Wly p*F87,p{Pj@%QBj֖l D Vofdx=~d)ʤb<+sk-; f)a%0ߕILFl-[GT|HdLlFaP<ŕ1~J_IQP{'z&[+ kqS2gaHx\&\?ޛЌGsn2}"F_[X!~~,q2EaFPK!0kxl/theme/theme1.xmlYOoE#F{oc'vGuر[hF[x=N3' G$$DA\q@@VR>MԯNDJ++2a,/$nECA6٥D-ʵ? dXiJF8,nx (MKoP(\HbWϿ})zg'8yV#x'˯?oOz3?^?O?~B,z_=yǿ~xPiL$M>7Ck9I#L nꎊ)f>\<|HL|3.ŅzI2O.&e>Ƈ8qBۙ5toG1sD1IB? }J^wi(#SKID ݠ1eBp{8yC]$f94^c>Y[XE>#{Sq c8 >;-&~ ..R(zy s^Fvԇ$*cߓqrB3' }'g7t4Kf"߇ފAV_] 2H7Hk;hIf;ZX_Fڲe}NM;SIvưõ[H5Dt(?]oQ|fNL{d׀O&kNa4%d8?L_H-Ak1h fx-jWBxlB -6j>},khxd׺rXg([x?eޓϲكkS1'|^=aѱnRvPK!t xl/worksheets/sheet1.xmlKk0 l4viZ6=+,3 IZuPk BǛ>޳3 ">ŏ+cF8G[2:,OYZr|Y | ]dnɓ)q?:b~%EXIEab_VPK!Ρxl/sharedStrings.xmlDA 0Ew]HBOhͤf7"σog3DU OC˹Ax-uaUZb |К*He7sF?(qM]t@i!ЀZ(<ΈR2ZPK !riTTdocProps/thumbnail.jpegJFIFHHICC_PROFILEappl mntrRGB XYZ   acspAPPLappl-appl descodscmxlcprt8wtptrXYZ0gXYZDbXYZXrTRClchad|,bTRClgTRCldescGeneric RGB ProfileGeneric RGB Profilemluc skSK(xhrHR(caES$ptBR&ukUA*frFU(Vaeobecn RGB profilGeneri ki RGB profilPerfil RGB genricPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBu( RGB r_icϏProfilo RGB genericoGenerisk RGB-profil| RGB \ |Obecn RGB profil RGB Allgemeines RGB-Profilltalnos RGB profilfn RGB cϏeNN, RGB 000000Profil RGB generic  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfileGenerel RGB-beskrivelsetextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&ltExifMM*>F(iNHHCC }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?5SGOYtu;9tM`U:[|?Тյ7Se?׃?S x3 /<)ozeς:}6]O 'Y h p5ܰ%T>J@ W;5Cs|V CoFg8|ƶڞkۅݸ('Ə i>&xVjg< 1P¶^?&1Io]b/!S x3 /?׃?<?z95/xZK?W,.N|cZ018`LG (ƪ oS x3 /c0~Ox%x7 W$5}WE:]uiu [y ʹ]mfDvBq}N#O 7Ꮜ+Z#ZE񅗊~燼1;]'ºt&=Cçſ:vS x3 / T#~ #3"?ր<:o :y7~纻{yFgGgbI&=/¯'^ wKj'ogo >4kV[Jk=oHԴvϩ^YYGn€=7z_|#;\^C=$_ <)?jml]5|?7x_ W;5x/K^:މ |sƗjZfg/o-ID u)]X~0!(6Ml+im%߇x 2{AGrX/O<9=W_}CKVxV#\x@>'ĶY v/%of]ᯅ<?~VԴ:W~>6itȒ,l[-wI綶xg??{s%'#>7 s9'^05M?f_tgb"]U|Q%mlSq}vvxgf̿?=OxǶ?|ecGƚ͝K{]Z_-u("H|eg 7(ykopfAGW<ş!}q 󧆼DfPf9$^XRm, H?!Ws@Gm\xg4ox}_!Ws@>Đkׇg4+~(ou=¶VVZ}j;;KKDu3ƨIY@:]>",Ókz6ڶ?j:u{;MvkkXH 6hO{?#6.h 3˚?CB>o 2koŚ]^qS\ԗO4}6-meuKceHB@k:=*y*TtSҋV|rJքVIj̿ ~ 8<b5q`0ZV'p`9\N"iƍΤQg&WHh oh״N 5>AHn݌zŘШ [ 7:>φmիCxJ|6Y*M*Xz1椽N\]j0t_!rY d*;׼?nZ׊=|fg4X,4X^hnZ;+Kb~!/r>*u=jN)NNKMN4Jj^=Z*BZA'd|҂Y{;]4; O'XƉ84>x,ەֵhZ|+ ];<#{h5ͿgE(ҧU9\:sVsu*9Τ BUc'4ֳ߳$IU~ٟ|d߀~5 n}Ʃ:,ykqC'Xxt _zܖ:&+a(VXNzYܴ+VUbkMVeyUW'm;~_ѥfeXӖ!ftJ1TܪSfJ|7t4+;Q*/;ͺu[Of?R?j |W鷺:MrڅAuN _ú]ږq\Z|֗yy8*ӤT.taUΜZrrVHsBdV)KI}{Go_W֗/ig7(e|ϳf.=Xzz}͏&ߍ_ѵmrL~E4XkV}A"FTO,ldpkg?@LNG@VG ~ g<;*L%Q["H ",C+ N{?#6.h 3˚?CB>o Џ?⮳MǑMR<'$Rj~;tm:4:2̱F0Df  >i :{Kip\EۤO +$SDऑGXP?mi?~+jlu? |Oi ,]'W|D)&sq:뾮-4-OI.O^4<>&Xl6H>$tbY&2".w:?xsG -xsG -xsG -xsG -xsG -|mm> ௃3_ xKWv O?÷5徚\jtm'_u_\x[4m7^$Gukʗ&ֵRV _?m)=|f* 56Qz1l[Ѡ4bo& (wGʮr .FHȯg8:VɞnR2qVUsF_!^T=ԕ>H<_~~1#y$W$T-ws䝻rN쓷]tY?/fnUn[{z0LP08CSG4|s\J46aV?2庯cvY?%[n{ɷoOUivݏJJ?cC{_U֕  :($QMGuzG49%AzG49%CRyGA]QkO]jm,=s dg8q_ڼ,N bq~R)u)$Ú*Hltp˕e9 H A0:J<9A#vJ<9A#vJ<9A#vJ<9A#v<Lj^? kKi#Q[vOďDž|Qc xn#ΩJXx:]fJѼ!5KZNٝ;QCWo[s֟<'Bex_%vg ? HV6jm#Zo?ikƨ? NIҿ9# }iqx\#`=@ch? HV6jm#Zo?ikƨ? GHt!4!wY< q׽VΟX[6Q"F0 s_}!+7Kp>7_U~IҤg9733Y[f:mY%䓒NI$_Rbu?GTci P+M_5@6@7myŭ+K bl#= A%Ҍ¾!eDk23$eT F_-gx I;_4JM%2GRoi( :?I@wiJ?ŧ~. ])exw͐fVI3gy1 $ mBgxž??JV4{{<$mzFĿO[u9W\ޗa?쿕</eXO9/7{/GO?}*?y_K Qrz_xn_ʏ#xe==>!굖_~f1lC7jwl,?cMIz-o5uL<^_+|W#Ӓ?W<VĺTPtf'ij \"[[k19 US WyB?@%w)x!WyB?@%w)x!WyB?@ljte"M^cռ2E !@4'G%w)x!WyB?@%w)x!WyB?@%w)x!^&__EDX=/2/.0yoS@\-eu98B+~rGQ_K9ӳYC]s/2}q]?wSgG{??n>?G&Cq?>/7$3ɿ ~}OƏM?34}~_oH=7SgA!_?g1ϯ֓{?{ת~k1Oޏh%X~!cpc2$ 5x$')pW#Ӓ?V|kNus: ^zLJfmz#mG~ q} [KSRéXLNb x!$$(,'3@lP?T}~_Umo@lP%qyi ސ~oDF Nbwu8'}(m>g?v/*ݷmm4OKdMվ >ŝpwsZH)6{κZtWpE%Gb (#k<9x9*9ML~/.l_ҵ;?dj[L%%pTH 5]/'WQ녛UR,6eu|d=7?.q? W"_?~ MK$+2O-Tng?%n'C*Ȕoopǟ@|Üe;A)] #۹$'2O_?~ MK$+2O-Tng?%n'C*ȇ7WO3߈Sxs ̿Ca@?jEWL߈G?xs[kh&*hX'8>xoşN>OF{2]H$։z HFtb)9O#%Z8FQ哯 eV1tz>bJj$ҩRsIgA[A unފ3#N[ hg?v/*ݷmm󿋗vͻ3x?(2I:mI=03XsmWT}[KPy}{sZswuq,ɺIeؒI&> O># }A<=} x~xvREE[k+ƙ}Pq_Ɩ6@׿ ~xZm_ S[Imb[Y<39^!>hHa7U=3 л]ѿ_kC¿./t!_to? 7 rƓtxS2`&G+ಬ*`>W]ѿ_kC¿./t!_to? 7 xWn9='žo&t(8cCalU wʏ/y:3@[x;Œ 4Vm6ԃ#@=hy'0@ 'xgw# 'xgw# 'xgw#%&k]l/HSž) {-f#E ½' &½' &½' &¾' &?!~'xgBnKQž#ž]HY|;I$alu3K3$I4!_to? 7 xWnD<+B_7@_g} Jx<%K XۤHu$eu`XA<᷁~">Uo]׆4Y4{=46=߃.(#+5.dF%k|Ž$Z&MKW~Kѵ úڬ$ u+Yi.Ԇw"0soN6K:՜ 7 og72"$;dy!1!0RFj83t=?C 7@#ڇ>'|3j83thW1Y2¾!LWþj0%LJ^\MѦUޮ=?C 7@#ڇ>'|3j83t=?'W gգûT.;>ZFpNuj83t=?C 7@#ڇ>'|3j83tx?BZ#Ş#o|Sp5ҷ)ɴ{ :G|O|g??q?f{PφC>n9+S>Stx'Bxp(Þ\@G$؃_2G|ٲHOC 7@#ڇ>'|3j83t=?[C៏fx#op'Un S[i;dSy_I}05%i7u瑧lg0I5w#2_ S77촯 x#No=k%iFMu:io/Fm-~ ԍf*z%Эӿk@%Эӿk@%Эӿk@%Эӿk@%Эӿk@lj>6fkCl<߫xe{LikIO%Эӿk@%Эӿk@%Эӿk@%Эӿk@%Эӿk@w_xOFLX;K `&IL3d+g:K$[_րK$[_րK$[_րK$[_րK$[_ր9?xր߉eK,!Ysԉc'ld Ú?,oZ?,oZ?,oZ?,oZkB?W*<V9H|X4_tdb1IN-oIR Px?VWi?VWi?VWi?VWi⯉^᧏ >%zf{Ku4zw9X݂Bl|"Դ kiycFŮ[i3xOx6Xtua6ॊF*v";\8,B dHHVVVVVJxgLgc§{=UAUAUAUAUAm.3Ã|ŕpwsVVVVV?\ńfTnaoGPoGPoGPoGP$(r-?P1[qq#Z&AT?Zueݼv,v:(JmQ4d3|o' o5. !+LV]įA#h-q5;ItjPF-oJoү>uh9q~"<gX'S^?ß14QkP>XftV9wK*cVPRK/+/V >^$W_-/|I[@&Z_x _L'"mr-O_DTg_}_. t1<+0i0U >^$W_-/|I[@&Z_x _L'"miO%|EiJm<@VX:/u|;dcN2GD% aeϗ?+hK/+/V >^$W_-/|I[@&Z_x _9?xMZ i/J< 7Nzs"b$?2ğJeϗ?+hK/+/V >^$W_2U"Ams^ n+ ^ё~u%%Nd2 AtiO%|E?2ğJeϗ?+hK/+/VWzuOGiK^ >p+( (X"]<Ťgܻߜa@P$GQܒAcxDAkCymuTP@y%N.I?owVRxqMLj|CukMMR_9'V6 Ġ򵮹c~DŽ4?hǎ>gQ,<RMsUA{w+Ciul?B'%Zt} 3Xxk3y7Jm,$T.ҡ/@|'@ڏ%;Q?j?@ >YG ?]rn4?A3/u #ռ2w}#j lj?@ >YG ?]OgK ,vti^ŰLpEPL'"sn v4?.@|'@ڏ%;Q?j?@ >YG<1qk4icQlP7Qƿ-cI'4?.@|'@ڏ%;Q?j?@ xP#±Q .9>aif,/u#w8O( ?]OgK ,vt?.8/ ՗_]i;o C'/d kv  gu]_iVǿu+ i1\IexwEU-ȴ@nt3ãȶ§`]"eq7/",dKxI$j}"-J}zֿYY[ZšN{6uZ>&~\XL ]^,KFDbgRZEGFuX9=K:?:?:?:?:?Vox_WhZE1Z?H˴" UUVEhܺu_g'=g'=g'=g'=g'=rzL45O ^-U`l-ՐM䂡!:ϳ{X枀{X枀{X枀{X枀{X枀9?A3k:xIZלSq퉤Ɏ6 VB7:?:?:?:?Io-xO7ǡ9S 5O 'ú1eui%XΟod xO cz> xO cz> xO cz>*ǦS̖zC!x<ņi|CqrGx%UlASQek/7:coE>$PGJMe{I[l&|b~^jmfK~֛&~_m<@6^"G5h_g;iɪN~߲xgEg SQ;^/gZ]i\ u )n[(a+:R9@= _OxO i3=V ?c{'A? u?[@ߴf/.M%]n-gOD-G,s ^!rJ`dl.5.D5gholkT21hMG`P;xLj5wJ#YTڋRB:f7/A bVZKSLYo/#+G-0H:RD+'q,x|9"51n:du.sKLI PK!eK@docProps/app.xml (Ao0  9i1 bHW`vgNc!dOv#{>Sz86.0 Y.2& {"2b%'$o>M -FHY5sL ,>9U pj^yoг\gGF_by~ e; 磷ݩMZ}m[g pJ& gk0s:v4蔜TܢyO:Wrڪt*y3B HZup_i D `v-q?B?^ 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-1.1.0/tests/test_formats.py0000644000175000017500000000573713146745673017606 0ustar travistravis00000000000000 # -*- 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-1.1.0/tests/test_formulas.py0000644000175000017500000000435713146745673017760 0ustar travistravis00000000000000 # -*- 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-1.1.0/tests/test_missing_records.py0000644000175000017500000000122313146745673021307 0ustar travistravis00000000000000from unittest import TestCase from xlrd import open_workbook from xlrd.biffh import XL_CELL_TEXT from .base import from_this_dir class TestMissingRecords(TestCase): def setUp(self): path = from_this_dir('biff4_no_format_no_window2.xls') self.book = open_workbook(path) self.sheet = self.book.sheet_by_index(0) def test_default_format(self): cell = self.sheet.cell(0, 0) self.assertEqual(cell.ctype, XL_CELL_TEXT) def test_default_window2_options(self): self.assertEqual(self.sheet.cached_page_break_preview_mag_factor, 0) self.assertEqual(self.sheet.cached_normal_view_mag_factor, 0) xlrd-1.1.0/tests/test_open_workbook.py0000644000175000017500000000263513146745673021003 0ustar travistravis00000000000000from 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. def test_err_cell_empty(self): # For cell with type "e" (error) but without inner 'val' tags open_workbook(from_this_dir('err_cell_empty.xlsx')) xlrd-1.1.0/tests/test_sheet.py0000644000175000017500000001134113146745673017227 0ustar travistravis00000000000000# 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 xlrd.timemachine import xrange 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) class TestMergedCells(TestCase): def test_tidy_dimensions(self): book = xlrd.open_workbook(from_this_dir('merged_cells.xlsx')) for sheet in book.sheets(): for rowx in xrange(sheet.nrows): self.assertEqual(sheet.row_len(rowx), sheet.ncols) xlrd-1.1.0/tests/test_workbook.py0000644000175000017500000000270513146745673017760 0ustar travistravis00000000000000# 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-1.1.0/tests/test_xldate.py0000644000175000017500000000452013146745673017401 0ustar travistravis00000000000000#!/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-1.1.0/tests/test_xldate_to_datetime.py0000644000175000017500000001376013146745673021765 0ustar travistravis00000000000000############################################################################### # # 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-1.1.0/tests/test_xlsx_comments.py0000644000175000017500000000356613146745673021034 0ustar travistravis00000000000000from 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') def test_excel_comments_with_multi_sheets(self): book = open_workbook(from_this_dir('test_comments_excel_sheet2.xlsx')) sheet = book.sheet_by_index(1) note_map = sheet.cell_note_map self.assertEqual(len(note_map), 1) self.assertEqual(note_map[(1, 1)].text, 'Note lives here') self.assertEqual(len(book.sheet_by_index(0).cell_note_map), 0) xlrd-1.1.0/tests/test_xlsx_parse.py0000644000175000017500000000537313146745673020317 0ustar travistravis00000000000000############################################################################### # # 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_75(self): # Test inlineStr attribute without child. # https://github.com/python-excel/xlrd/issues/75 workbook = xlrd.open_workbook(from_this_dir('apachepoi_52348.xlsx')) worksheet = workbook.sheet_by_index(0) # Test an empty inlineStr cell. cell = worksheet.cell(0, 0) self.assertEqual(cell.value, '') self.assertEqual(cell.ctype, xlrd.book.XL_CELL_EMPTY) # Test a non-empty inlineStr cell. cell = worksheet.cell(1, 2) self.assertEqual(cell.value, 'Category') self.assertEqual(cell.ctype, xlrd.book.XL_CELL_TEXT) 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) def test_for_github_issue_150(self): # Test for non-Excel file with a non-lowercase worksheet filename. # https://github.com/python-excel/xlrd/issues/150 workbook = xlrd.open_workbook(from_this_dir('issue150.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) xlrd-1.1.0/tests/text_bar.xlsx0000644000175000017500000002000413146745673017232 0ustar travistravis00000000000000PK!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-1.1.0/xlrd/0000755000175000017500000000000013146745735014314 5ustar travistravis00000000000000xlrd-1.1.0/xlrd/examples/0000755000175000017500000000000013146745735016132 5ustar travistravis00000000000000xlrd-1.1.0/xlrd/examples/namesdemo.xls0000644000175000017500000005400013146745673020632 0ustar travistravis00000000000000ࡱ> *) \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-1.1.0/xlrd/examples/xlrdnameAPIdemo.py0000644000175000017500000001573413146745673021530 0ustar travistravis00000000000000# -*- 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-1.1.0/xlrd/__init__.py0000644000175000017500000001431313146745673016430 0ustar travistravis00000000000000# 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 .info import __VERSION__ 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 from .sheet import empty_cell from .xldate import XLDateError, xldate_as_tuple, xldate_as_datetime from .xlsx import X12Book 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 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, ): """ 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: A string or an :class:`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. See :doc:`unicode`. :param formatting_info: The default is ``False``, which saves memory. In this case, "Blank" cells, which are those with their own formatting information but no data, are treated as empty by ignoring the file's ``BLANK`` and ``MULBLANK`` records. This cuts off any bottom or right "margin" of rows of empty or blank cells. Only :meth:`~xlrd.sheet.Sheet.cell_value` and :meth:`~xlrd.sheet.Sheet.cell_type` are available. When ``True``, formatting information will be read from the spreadsheet file. This provides all cells, including empty and blank cells. Formatting information is available for each cell. Note that this will raise a NotImplementedError when used with an xlsx file. :param on_demand: Governs whether sheets are all loaded initially or when demanded by the caller. See :doc:`on_demand`. :param ragged_rows: The default of ``False`` means all rows are padded out with empty cells so that all rows have the same size as found in :attr:`~xlrd.sheet.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 :meth:`~xlrd.sheet.Sheet.row_len` method. :returns: An instance of the :class:`~xlrd.book.Book` class. """ peeksz = 4 if file_contents: peek = file_contents[:peeksz] else: with open(filename, "rb") as f: peek = f.read(peeksz) 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([(X12Book.convert_filename(name), 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 def dump(filename, outfile=sys.stdout, unnumbered=False): """ 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). """ 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) def count_records(filename, outfile=sys.stdout): """ For debugging and analysis: summarise the file's BIFF records. ie: 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. """ 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-1.1.0/xlrd/biffh.py0000644000175000017500000004045013146745673015750 0ustar travistravis00000000000000# -*- coding: cp1252 -*- # 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. from __future__ import print_function DEBUG = 0 from struct import unpack from .timemachine import * import sys class XLRDError(Exception): """ An exception indicating problems reading data from an Excel file. """ class BaseObject(object): """ Parent of almost all other classes in the package. Defines a common :meth:`dump` method for debugging. """ _repr_these = [] def dump(self, f=None, header=None, footer=None, indent=0): """ :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) """ 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. 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 class Name(BaseObject): """ Information relating to a named reference, formula, macro, etc. .. note:: Name information is **not** extracted from files older than Excel 5.0 (``Book.biff_version < 50``) """ _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). #: #: .. note:: 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 #: #: .. note:: 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 #: :class:`~xlrd.formula.Operand` class. # result = None def cell(self): """ This is a convenience method for the frequent use case where the name refers to a single cell. :returns: An instance of the :class:`~xlrd.sheet.Cell` class. :raises xlrd.biffh.XLRDError: The name is not a constant absolute reference to a single cell. """ 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") def area2d(self, clipped=True): """ 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. :returns: a tuple ``(sheet_object, rowxlo, rowxhi, colxlo, colxhi)``. :raises xlrd.biffh.XLRDError: The name is not a constant absolute reference to a single area in a single sheet. """ 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") class Book(BaseObject): """ Contents of a "workbook". .. warning:: You should not instantiate this class yourself. You use the :class:`Book` object that was returned when you called :func:`~xlrd.open_workbook`. """ #: 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). #: #: Defaults to 0 in case it's not specified in the file. datemode = 0 #: 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 :class:`Name` object for each ``NAME`` record in the #: workbook. #: #: .. versionadded:: 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 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 :class:`~xlrd.formatting.Font` class instances, #: each corresponding to a FONT record. #: #: .. versionadded:: 0.6.1 font_list = [] #: A list of :class:`~xlrd.formatting.XF` class instances, #: each corresponding to an ``XF`` record. #: #: .. versionadded:: 0.6.1 xf_list = [] #: A list of :class:`~xlrd.formatting.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) :mod:`xlwt`, #: use this list. #: #: The collection to be used for all visual rendering purposes is #: :attr:`format_map`. #: #: .. versionadded:: 0.6.1 format_list = [] ## #: The mapping from :attr:`~xlrd.formatting.XF.format_key` to #: :class:`~xlrd.formatting.Format` object. #: #: .. versionadded:: 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`` has the following meanings #: #: 1: #: built-in style #: #: 0: #: user-defined #: #: ``xf_index`` is an index into :attr:`Book.xf_list`. #: #: References: OOo docs s6.99 (``STYLE`` record); Excel UI Format/Style #: #: .. versionadded:: 0.6.1 #: #: Extracted only if ``open_workbook(..., formatting_info=True)`` #: #: .. versionadded:: 0.7.4 style_name_map = {} #: This provides definitions for colour indexes. Please refer to #: :ref:`palette` 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``. #: #: :attr:`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 #: :attr:`palette_record`. #: #: .. note:: Extracted only if ``open_workbook(..., formatting_info=True)`` #: #: .. versionadded:: 0.6.1 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 :attr:`colour_map`. #: #: .. note:: Extracted only if ``open_workbook(..., formatting_info=True)`` #: #: .. versionadded:: 0.6.1 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 def sheets(self): """ :returns: A list of all sheets in the book. All sheets not already loaded will be loaded. """ for sheetx in xrange(self.nsheets): if not self._sheet_list[sheetx]: self.get_sheet(sheetx) return self._sheet_list[:] def sheet_by_index(self, sheetx): """ :param sheetx: Sheet index in ``range(nsheets)`` :returns: A :class:`~xlrd.sheet.Sheet`. """ return self._sheet_list[sheetx] or self.get_sheet(sheetx) def sheet_by_name(self, sheet_name): """ :param sheet_name: Name of the sheet required. :returns: A :class:`~xlrd.sheet.Sheet`. """ try: sheetx = self._sheet_names.index(sheet_name) except ValueError: raise XLRDError('No sheet named <%r>' % sheet_name) return self.sheet_by_index(sheetx) def sheet_names(self): """ :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. """ return self._sheet_names[:] def sheet_loaded(self, sheet_name_or_index): """ :param sheet_name_or_index: Name or index of sheet enquired upon :returns: ``True`` if sheet is loaded, ``False`` otherwise. .. versionadded:: 0.7.1 """ 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]) def unload_sheet(self, sheet_name_or_index): """ :param sheet_name_or_index: Name or index of sheet to be unloaded. .. versionadded:: 0.7.1 """ 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 def release_resources(self): """ This method has a dual purpose. You can call it to release memory-consuming objects and (possibly) a memory-mapped file (:class:`mmap.mmap` object) when you have finished loading sheets in ``on_demand`` mode, but still require the :class:`Book` object to examine the loaded sheets. It is also called automatically (a) when :func:`~xlrd.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. """ 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 :class:`Name` #: object. #: #: .. versionadded:: 0.6.0 name_and_scope_map = {} #: A mapping from `lower_case_name` to a list of :class:`Name` objects. #: The list is sorted in scope order. Typically there will be one item #: (of global scope) in the list. #: #: .. versionadded:: 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-1.1.0/xlrd/compdoc.py0000644000175000017500000005075713146745673016331 0ustar travistravis00000000000000# -*- coding: cp1252 -*- # 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. # No part of the content of this file was derived from the works of # David Giffin. """ Implements the minimal functionality required to extract a "Workbook" or "Book" stream (as one big string) from an OLE2 Compound Document file. """ 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 def get_named_stream(self, qname): """ 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. """ 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) def locate_named_stream(self, 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. :param qname: Name of the desired stream e.g. ``u'Workbook'``. Should be in Unicode or convertible thereto. """ 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-1.1.0/xlrd/formatting.py0000644000175000017500000013003513146745673017043 0ustar travistravis00000000000000# -*- coding: cp1252 -*- # 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. # No part of the content of this file was derived from the works of # David Giffin. """ Module for formatting information. """ 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_CELL_TEXT, \ 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 class EqNeAttrs(object): """ This mixin class exists solely so that :class:`Format`, :class:`Font`, and :class:`XF` objects can be compared by value of their attributes. """ def __eq__(self, other): return self.__dict__ == other.__dict__ def __ne__(self, other): return self.__dict__ != other.__dict__ class Font(BaseObject, EqNeAttrs): """ 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. .. versionadded:: 0.6.1 """ #: 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 :ref:`palette`. colour_index = 0 #: 1 = Superscript, 2 = Subscript. escapement = 0 #: Values:: #: #: 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 #: Values:: #: #: 0 = None #: 1 = Single; 0x21 (33) = Single accounting #: 2 = Double; 0x22 (34) = Double accounting underline_type = 0 #: 1 = Characters are underlined. Redundant; see #: :attr:`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 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" === class Format(BaseObject, EqNeAttrs): """ "Number format" information from a ``FORMAT`` record. .. versionadded:: 0.6.1 """ #: The key into :attr:`~xlrd.book.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] try: fmt = self.format_map[xf.format_key] cellty = _cellty_from_fmtty[fmt.type] except KeyError: cellty = XL_CELL_TEXT 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) class XFBorder(BaseObject, EqNeAttrs): """ 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 :ref:`palette`. 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)".

    .. versionadded:: 0.6.1 """ #: 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 class XFBackground(BaseObject, EqNeAttrs): """ 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 explanations of "colour index" is given in :ref:`palette`. .. versionadded:: 0.6.1 """ #: 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 class XFAlignment(BaseObject, EqNeAttrs): """ 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. .. versionadded:: 0.6.1 """ #: 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 #: :attr:`orientation` attribute; this will be mapped (without loss) #: into :attr:`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 class XFProtection(BaseObject, EqNeAttrs): """ 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. .. versionadded:: 0.6.1 """ #: 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 class XF(BaseObject): """ eXtended Formatting information for cells, rows, columns and styles. 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:: the API provides both "raw" XFs and "computed" XFs. In the latter case, cell XFs have had the above inheritance mechanism applied. .. versionadded:: 0.6.1 """ #: 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 :attr:`~xlrd.book.Book.xf_list` xf_index = 0 #: Index into :attr:`~xlrd.book.Book.font_list` font_index = 0 #: Key into :attr:`~xlrd.book.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 :class:`XFProtection` object. protection = None #: An instance of an :class:`XFBackground` object. background = None #: An instance of an :class:`XFAlignment` object. alignment = None #: An instance of an :class:`XFBorder` object. border = None xlrd-1.1.0/xlrd/formula.py0000644000175000017500000027073313146745673016350 0ustar travistravis00000000000000# -*- coding: cp1252 -*- # 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. # No part of the content of this file was derived from the works of # David Giffin. """ Module for parsing/evaluating Microsoft Excel formulas. """ 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', 'Operand', 'Ref3D', ] 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 class Operand(object): """ Used in evaluating formulas. The following table describes the kinds and how their values are represented. .. raw:: html
    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
    """ #: None means that the actual value of the operand is a variable #: (depends on cell data), not a constant. value = None #: oUNK means that the kind of operand is not known unambiguously. kind = oUNK #: The reconstituted text of the original formula. Function names will be #: in English irrespective of the original language, which doesn't seem #: to be recorded anywhere. The separator is ",", not ";" or whatever else #: might be more appropriate for the end-user's locale; patches welcome. text = '?' def __init__(self, akind=None, avalue=None, arank=0, atext='?'): if akind is not None: self.kind = akind if avalue is not None: self.value = avalue self.rank = arank # rank is an internal gizmo (operator precedence); # it's used in reconstructing formula text. self.text = atext def __repr__(self): kind_text = okind_dict.get(self.kind, "?Unknown kind?") return "Operand(kind=%s, value=%r, text=%r)" \ % (kind_text, self.value, self.text) class Ref3D(tuple): """ Represents an absolute or relative 3-dimensional reference to a box of one or more cells. The ``coords`` attribute is a tuple of the form:: (shtxlo, shtxhi, rowxlo, rowxhi, colxlo, colxhi) where ``0 <= thingxlo <= thingx < thingxhi``. .. note:: It is quite possible to have ``thingx > nthings``; for example ``Print_Titles`` could have ``colxhi == 256`` and/or ``rowxhi == 65536`` irrespective of how many columns/rows are actually used in the worksheet. The caller will need to decide how to handle this situation. Keyword: :class:`IndexError` :-) The components of the coords attribute are also available as individual attributes: ``shtxlo``, ``shtxhi``, ``rowxlo``, ``rowxhi``, ``colxlo``, and ``colxhi``. The ``relflags`` attribute is a 6-tuple of flags which indicate whether the corresponding (sheet|row|col)(lo|hi) is relative (1) or absolute (0). .. note:: There is necessarily no information available as to what cell(s) the reference could possibly be relative to. The caller must decide what if any use to make of ``oREL`` operands. .. note: A partially relative reference may well be a typo. For example, define name ``A1Z10`` as ``$a$1:$z10`` (missing ``$`` after ``z``) while the cursor is on cell ``Sheet3!A27``. The resulting :class:`Ref3D` instance will have ``coords = (2, 3, 0, -16, 0, 26)`` and ``relflags = (0, 0, 0, 1, 0, 0).
    So far, only one possibility of a sheet-relative component in a reference has been noticed: a 2D reference located in the "current sheet". This will appear as ``coords = (0, 1, ...)`` and ``relflags = (1, 1, ...)``. .. versionadded:: 0.6.0 """ def __init__(self, atuple): self.coords = atuple[0:6] self.relflags = atuple[6:12] if not self.relflags: self.relflags = (0, 0, 0, 0, 0, 0) (self.shtxlo, self.shtxhi, self.rowxlo, self.rowxhi, self.colxlo, self.colxhi) = self.coords def __repr__(self): if not self.relflags or self.relflags == (0, 0, 0, 0, 0, 0): return "Ref3D(coords=%r)" % (self.coords, ) else: return "Ref3D(coords=%r, relflags=%r)" \ % (self.coords, self.relflags) tAdd = 0x03 tSub = 0x04 tMul = 0x05 tDiv = 0x06 tPower = 0x07 tConcat = 0x08 tLT, tLE, tEQ, tGE, tGT, tNE = range(0x09, 0x0F) 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) def cellname(rowx, colx): """Utility function: ``(5, 7)`` => ``'H6'``""" return "%s%d" % (colname(colx), rowx+1) def cellnameabs(rowx, colx, r1c1=0): """Utility function: ``(5, 7)`` => ``'$H$6'``""" if r1c1: return "R%dC%d" % (rowx+1, colx+1) return "$%s$%d" % (colname(colx), rowx+1) def cellnamerel(rowx, colx, rowxrel, colxrel, browx=None, bcolx=None, r1c1=0): if not rowxrel and not colxrel: return cellnameabs(rowx, colx, r1c1) if (rowxrel and browx is None) or (colxrel and bcolx is None): # must flip the whole cell into R1C1 mode r1c1 = True c = colnamerel(colx, colxrel, bcolx, r1c1) r = rownamerel(rowx, rowxrel, browx, r1c1) if r1c1: return r + c return c + r def colname(colx): """Utility function: ``7`` => ``'H'``, ``27`` => ``'AB'``""" alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" if colx <= 25: return alphabet[colx] else: xdiv26, xmod26 = divmod(colx, 26) return alphabet[xdiv26 - 1] + alphabet[xmod26] def rangename2d(rlo, rhi, clo, chi, r1c1=0): """ ``(5, 20, 7, 10)`` => ``'$H$6:$J$20'`` """ if r1c1: return if rhi == rlo+1 and chi == clo+1: return cellnameabs(rlo, clo, r1c1) return "%s:%s" % (cellnameabs(rlo, clo, r1c1), cellnameabs(rhi-1, chi-1, r1c1)) def rangename2drel(rlo_rhi_clo_chi, rlorel_rhirel_clorel_chirel, browx=None, bcolx=None, r1c1=0): rlo, rhi, clo, chi = rlo_rhi_clo_chi rlorel, rhirel, clorel, chirel = rlorel_rhirel_clorel_chirel if (rlorel or rhirel) and browx is None: r1c1 = True if (clorel or chirel) and bcolx is None: r1c1 = True return "%s:%s" % ( cellnamerel(rlo, clo, rlorel, clorel, browx, bcolx, r1c1), cellnamerel(rhi-1, chi-1, rhirel, chirel, browx, bcolx, r1c1) ) def rangename3d(book, ref3d): """ Utility function: ``Ref3D(1, 4, 5, 20, 7, 10)`` => ``'Sheet2:Sheet3!$H$6:$J$20'`` (assuming Excel's default sheetnames) """ coords = ref3d.coords return "%s!%s" % ( sheetrange(book, *coords[:2]), rangename2d(*coords[2:6])) def rangename3drel(book, ref3d, browx=None, bcolx=None, r1c1=0): """ Utility function: ``Ref3D(coords=(0, 1, -32, -22, -13, 13), relflags=(0, 0, 1, 1, 1, 1))`` In R1C1 mode => ``'Sheet1!R[-32]C[-13]:R[-23]C[12]'`` In A1 mode => depends on base cell ``(browx, bcolx)`` """ coords = ref3d.coords relflags = ref3d.relflags shdesc = sheetrangerel(book, coords[:2], relflags[:2]) rngdesc = rangename2drel(coords[2:6], relflags[2:6], browx, bcolx, r1c1) if not shdesc: return rngdesc return "%s!%s" % (shdesc, rngdesc) def quotedsheetname(shnames, shx): if shx >= 0: shname = shnames[shx] else: shname = { -1: "?internal; any sheet?", -2: "internal; deleted sheet", -3: "internal; macro sheet", -4: "<>", }.get(shx, "?error %d?" % shx) if "'" in shname: return "'" + shname.replace("'", "''") + "'" if " " in shname: return "'" + shname + "'" return shname def sheetrange(book, slo, shi): shnames = book.sheet_names() shdesc = quotedsheetname(shnames, slo) if slo != shi-1: shdesc += ":" + quotedsheetname(shnames, shi-1) return shdesc def sheetrangerel(book, srange, srangerel): slo, shi = srange slorel, shirel = srangerel if not slorel and not shirel: return sheetrange(book, slo, shi) assert (slo == 0 == shi-1) and slorel and shirel return "" # ============================================================== xlrd-1.1.0/xlrd/info.py0000644000175000017500000000002613146745673015620 0ustar travistravis00000000000000__VERSION__ = "1.1.0" xlrd-1.1.0/xlrd/sheet.py0000644000175000017500000031730213146745673016005 0ustar travistravis00000000000000# -*- coding: cp1252 -*- # Copyright (c) 2005-2013 Stephen John Machin, Lingfo Pty Ltd # This module is part of the xlrd package, which is released under a # BSD-style licence. from __future__ import print_function from array import array from struct import 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), ) class Sheet(BaseObject): """ Contains the data for one worksheet. In the cell access functions, ``rowx`` is a row index, counting from zero, and ``colx`` is a column index, counting from zero. Negative values for row/column indexes and slice positions are supported in the expected fashion. For information about cell types and cell values, refer to the documentation of the :class:`Cell` class. .. warning:: You don't instantiate this class yourself. You access :class:`Sheet` objects via the :class:`~xlrd.book.Book` object that was returned when you called :func:`xlrd.open_workbook`. """ #: Name of sheet. name = '' #: A reference to the :class:`~xlrd.book.Book` object to which this sheet #: belongs. #: #: Example usage: ``some_sheet.book.datemode`` book = None #: Number of rows in sheet. A row index is in ``range(thesheet.nrows)``. nrows = 0 #: Nominal number of columns in sheet. It is one more than the maximum #: column index found, ignoring trailing empty cells. #: See also the ``ragged_rows`` parameter to :func:`~xlrd.open_workbook` #: and :meth:`~xlrd.sheet.Sheet.row_len`. ncols = 0 #: The map from a column index to a :class:`Colinfo` object. Often there is #: an entry in ``COLINFO`` records for all column indexes in ``range(257)``. #: #: .. note:: #: xlrd ignores the entry for the non-existent #: 257th column. #: #: On the other hand, there may be no entry for unused columns. #: #: .. versionadded:: 0.6.1 #: #: Populated only if ``open_workbook(..., formatting_info=True)`` colinfo_map = {} #: The map from a row index to a :class:`Rowinfo` object. #: #: ..note:: #: It is possible to have missing entries -- at least one source of #: XLS files doesn't bother writing ``ROW`` records. #: #: .. versionadded:: 0.6.1 #: #: Populated only if ``open_workbook(..., formatting_info=True)`` rowinfo_map = {} #: List of address ranges of cells containing column labels. #: These are set up in Excel by Insert > Name > Labels > Columns. #: #: .. versionadded:: 0.6.0 #: #: How to deconstruct the list: #: #: .. code-block:: python #: #: for crange in thesheet.col_label_ranges: #: rlo, rhi, clo, chi = crange #: for rx in xrange(rlo, rhi): #: for cx in xrange(clo, chi): #: print "Column label at (rowx=%d, colx=%d) is %r" \ #: (rx, cx, thesheet.cell_value(rx, cx)) col_label_ranges = [] #: List of address ranges of cells containing row labels. #: For more details, see :attr:`col_label_ranges`. #: #: .. versionadded:: 0.6.0 row_label_ranges = [] #: List of address ranges of cells which have been merged. #: These are set up in Excel by Format > Cells > Alignment, then ticking #: the "Merge cells" box. #: #: .. note:: #: The upper limits are exclusive: i.e. ``[2, 3, 7, 9]`` only #: spans two cells. #: #: .. note:: Extracted only if ``open_workbook(..., formatting_info=True)`` #: #: .. versionadded:: 0.6.1 #: #: How to deconstruct the list: #: #: .. code-block:: python #: #: for crange in thesheet.merged_cells: #: rlo, rhi, clo, chi = crange #: for rowx in xrange(rlo, rhi): #: for colx in xrange(clo, chi): #: # cell (rlo, clo) (the top left one) will carry the data #: # and formatting info; the remainder will be recorded as #: # blank cells, but a renderer will apply the formatting info #: # for the top left cell (e.g. border, pattern) to all cells in #: # the range. merged_cells = [] #: Mapping of ``(rowx, colx)`` to list of ``(offset, font_index)`` tuples. #: The offset defines where in the string the font begins to be used. #: Offsets are expected to be in ascending order. #: If the first offset is not zero, the meaning is that the cell's ``XF``'s #: font should be used from offset 0. #: #: This is a sparse mapping. There is no entry for cells that are not #: formatted with rich text. #: #: How to use: #: #: .. code-block:: python #: #: runlist = thesheet.rich_text_runlist_map.get((rowx, colx)) #: if runlist: #: for offset, font_index in runlist: #: # do work here. #: pass #: #: .. versionadded:: 0.7.2 #: #: Populated only if ``open_workbook(..., formatting_info=True)`` rich_text_runlist_map = {} #: Default column width from ``DEFCOLWIDTH`` record, else ``None``. #: From the OOo docs: #: #: Column width in characters, using the width of the zero character #: from default font (first FONT record in the file). Excel adds some #: extra space to the default width, depending on the default font and #: default font size. The algorithm how to exactly calculate the resulting #: column width is not known. #: Example: The default width of 8 set in this record results in a column #: width of 8.43 using Arial font with a size of 10 points. #: #: For the default hierarchy, refer to the :class:`Colinfo` class. #: #: .. versionadded:: 0.6.1 defcolwidth = None #: Default column width from ``STANDARDWIDTH`` record, else ``None``. #: #: From the OOo docs: #: #: Default width of the columns in 1/256 of the width of the zero #: character, using default font (first FONT record in the file). #: #: For the default hierarchy, refer to the :class:`Colinfo` class. #: #: .. versionadded:: 0.6.1 standardwidth = None #: Default value to be used for a row if there is #: no ``ROW`` record for that row. #: From the *optional* ``DEFAULTROWHEIGHT`` record. default_row_height = None #: Default value to be used for a row if there is #: no ``ROW`` record for that row. #: From the *optional* ``DEFAULTROWHEIGHT`` record. default_row_height_mismatch = None #: Default value to be used for a row if there is #: no ``ROW`` record for that row. #: From the *optional* ``DEFAULTROWHEIGHT`` record. default_row_hidden = None #: Default value to be used for a row if there is #: no ``ROW`` record for that row. #: From the *optional* ``DEFAULTROWHEIGHT`` record. default_additional_space_above = None #: Default value to be used for a row if there is #: no ``ROW`` record for that row. #: From the *optional* ``DEFAULTROWHEIGHT`` record. default_additional_space_below = None #: Visibility of the sheet:: #: #: 0 = visible #: 1 = hidden (can be unhidden by user -- Format -> Sheet -> Unhide) #: 2 = "very hidden" (can be unhidden only by VBA macro). visibility = 0 #: A 256-element tuple corresponding to the contents of the GCW record for #: this sheet. If no such record, treat as all bits zero. #: Applies to BIFF4-7 only. See docs of the :class:`Colinfo` class for #: discussion. gcw = (0, ) * 256 #: A list of :class:`Hyperlink` objects corresponding to ``HLINK`` records #: found in the worksheet. #: #: .. versionadded:: 0.7.2 hyperlink_list = [] #: A sparse mapping from ``(rowx, colx)`` to an item in #: :attr:`~xlrd.sheet.Sheet.hyperlink_list`. #: Cells not covered by a hyperlink are not mapped. #: It is possible using the Excel UI to set up a hyperlink that #: covers a larger-than-1x1 rectangle of cells. #: Hyperlink rectangles may overlap (Excel doesn't check). #: When a multiply-covered cell is clicked on, the hyperlink that is #: activated #: (and the one that is mapped here) is the last in #: :attr:`~xlrd.sheet.Sheet.hyperlink_list`. #: #: .. versionadded:: 0.7.2 hyperlink_map = {} #: A sparse mapping from ``(rowx, colx)`` to a :class:`Note` object. #: Cells not containing a note ("comment") are not mapped. #: #: .. versionadded:: 0.7.2 cell_note_map = {} #: Number of columns in left pane (frozen panes; for split panes, see #: comments in code) vert_split_pos = 0 #: Number of rows in top pane (frozen panes; for split panes, see comments #: in code) horz_split_pos = 0 #: Index of first visible row in bottom frozen/split pane horz_split_first_visible = 0 #: Index of first visible column in right frozen/split pane vert_split_first_visible = 0 #: Frozen panes: ignore it. Split panes: explanation and diagrams in #: OOo docs. split_active_pane = 0 #: Boolean specifying if a ``PANE`` record was present, ignore unless you're #: ``xlutils.copy`` has_pane_record = 0 #: A list of the horizontal page breaks in this sheet. #: Breaks are tuples in the form #: ``(index of row after break, start col index, end col index)``. #: #: Populated only if ``open_workbook(..., formatting_info=True)`` #: #: .. versionadded:: 0.7.2 horizontal_page_breaks = [] #: A list of the vertical page breaks in this sheet. #: Breaks are tuples in the form #: ``(index of col after break, start row index, end row index)``. #: #: Populated only if ``open_workbook(..., formatting_info=True)`` #: #: .. versionadded:: 0.7.2 vertical_page_breaks = [] def __init__(self, book, position, name, number): self.book = book self.biff_version = book.biff_version self._position = position self.logfile = book.logfile self.bt = array('B', [XL_CELL_EMPTY]) self.bf = array('h', [-1]) self.name = name self.number = number self.verbosity = book.verbosity self.formatting_info = book.formatting_info self.ragged_rows = book.ragged_rows if self.ragged_rows: self.put_cell = self.put_cell_ragged else: self.put_cell = self.put_cell_unragged self._xf_index_to_xl_type_map = book._xf_index_to_xl_type_map self.nrows = 0 # actual, including possibly empty cells self.ncols = 0 self._maxdatarowx = -1 # highest rowx containing a non-empty cell self._maxdatacolx = -1 # highest colx containing a non-empty cell self._dimnrows = 0 # as per DIMENSIONS record self._dimncols = 0 self._cell_values = [] self._cell_types = [] self._cell_xf_indexes = [] self.defcolwidth = None self.standardwidth = None self.default_row_height = None self.default_row_height_mismatch = 0 self.default_row_hidden = 0 self.default_additional_space_above = 0 self.default_additional_space_below = 0 self.colinfo_map = {} self.rowinfo_map = {} self.col_label_ranges = [] self.row_label_ranges = [] self.merged_cells = [] self.rich_text_runlist_map = {} self.horizontal_page_breaks = [] self.vertical_page_breaks = [] self._xf_index_stats = [0, 0, 0, 0] self.visibility = book._sheet_visibility[number] # from BOUNDSHEET record for attr, defval in _WINDOW2_options: setattr(self, attr, defval) self.first_visible_rowx = 0 self.first_visible_colx = 0 self.gridline_colour_index = 0x40 self.gridline_colour_rgb = None # pre-BIFF8 self.hyperlink_list = [] self.hyperlink_map = {} self.cell_note_map = {} # Values calculated by xlrd to predict the mag factors that # will actually be used by Excel to display your worksheet. # Pass these values to xlwt when writing XLS files. # Warning 1: Behaviour of OOo Calc and Gnumeric has been observed to differ from Excel's. # Warning 2: A value of zero means almost exactly what it says. Your sheet will be # displayed as a very tiny speck on the screen. xlwt will reject attempts to set # a mag_factor that is not (10 <= mag_factor <= 400). self.cooked_page_break_preview_mag_factor = 60 self.cooked_normal_view_mag_factor = 100 # Values (if any) actually stored on the XLS file self.cached_page_break_preview_mag_factor = 0 # default (60%), from WINDOW2 record self.cached_normal_view_mag_factor = 0 # default (100%), from WINDOW2 record self.scl_mag_factor = None # from SCL record self._ixfe = None # BIFF2 only self._cell_attr_to_xfx = {} # BIFF2.0 only if self.biff_version >= 80: self.utter_max_rows = 65536 else: self.utter_max_rows = 16384 self.utter_max_cols = 256 self._first_full_rowx = -1 # self._put_cell_exceptions = 0 # self._put_cell_row_widenings = 0 # self._put_cell_rows_appended = 0 # self._put_cell_cells_appended = 0 def cell(self, rowx, colx): """ :class:`Cell` object in the given row and column. """ if self.formatting_info: xfx = self.cell_xf_index(rowx, colx) else: xfx = None return Cell( self._cell_types[rowx][colx], self._cell_values[rowx][colx], xfx, ) def cell_value(self, rowx, colx): "Value of the cell in the given row and column." return self._cell_values[rowx][colx] def cell_type(self, rowx, colx): """ Type of the cell in the given row and column. Refer to the documentation of the :class:`Cell` class. """ return self._cell_types[rowx][colx] def cell_xf_index(self, rowx, colx): """ XF index of the cell in the given row and column. This is an index into :attr:`~xlrd.book.Book.xf_list`. .. versionadded:: 0.6.1 """ self.req_fmt_info() xfx = self._cell_xf_indexes[rowx][colx] if xfx > -1: self._xf_index_stats[0] += 1 return xfx # Check for a row xf_index try: xfx = self.rowinfo_map[rowx].xf_index if xfx > -1: self._xf_index_stats[1] += 1 return xfx except KeyError: pass # Check for a column xf_index try: xfx = self.colinfo_map[colx].xf_index if xfx == -1: xfx = 15 self._xf_index_stats[2] += 1 return xfx except KeyError: # If all else fails, 15 is used as hardwired global default xf_index. self._xf_index_stats[3] += 1 return 15 def row_len(self, rowx): """ Returns the effective number of cells in the given row. For use with ``open_workbook(ragged_rows=True)`` which is likely to produce rows with fewer than :attr:`~Sheet.ncols` cells. .. versionadded:: 0.7.2 """ return len(self._cell_values[rowx]) def row(self, rowx): """ Returns a sequence of the :class:`Cell` objects in the given row. """ return [ self.cell(rowx, colx) for colx in xrange(len(self._cell_values[rowx])) ] def get_rows(self): "Returns a generator for iterating through each row." return (self.row(index) for index in range(self.nrows)) def row_types(self, rowx, start_colx=0, end_colx=None): """ Returns a slice of the types of the cells in the given row. """ if end_colx is None: return self._cell_types[rowx][start_colx:] return self._cell_types[rowx][start_colx:end_colx] def row_values(self, rowx, start_colx=0, end_colx=None): """ Returns a slice of the values of the cells in the given row. """ if end_colx is None: return self._cell_values[rowx][start_colx:] return self._cell_values[rowx][start_colx:end_colx] def row_slice(self, rowx, start_colx=0, end_colx=None): """ Returns a slice of the :class:`Cell` objects in the given row. """ nc = len(self._cell_values[rowx]) if start_colx < 0: start_colx += nc if start_colx < 0: start_colx = 0 if end_colx is None or end_colx > nc: end_colx = nc elif end_colx < 0: end_colx += nc return [ self.cell(rowx, colx) for colx in xrange(start_colx, end_colx) ] def col_slice(self, colx, start_rowx=0, end_rowx=None): """ Returns a slice of the :class:`Cell` objects in the given column. """ nr = self.nrows if start_rowx < 0: start_rowx += nr if start_rowx < 0: start_rowx = 0 if end_rowx is None or end_rowx > nr: end_rowx = nr elif end_rowx < 0: end_rowx += nr return [ self.cell(rowx, colx) for rowx in xrange(start_rowx, end_rowx) ] def col_values(self, colx, start_rowx=0, end_rowx=None): """ Returns a slice of the values of the cells in the given column. """ nr = self.nrows if start_rowx < 0: start_rowx += nr if start_rowx < 0: start_rowx = 0 if end_rowx is None or end_rowx > nr: end_rowx = nr elif end_rowx < 0: end_rowx += nr return [ self._cell_values[rowx][colx] for rowx in xrange(start_rowx, end_rowx) ] def col_types(self, colx, start_rowx=0, end_rowx=None): """ Returns a slice of the types of the cells in the given column. """ nr = self.nrows if start_rowx < 0: start_rowx += nr if start_rowx < 0: start_rowx = 0 if end_rowx is None or end_rowx > nr: end_rowx = nr elif end_rowx < 0: end_rowx += nr return [ self._cell_types[rowx][colx] for rowx in xrange(start_rowx, end_rowx) ] col = col_slice # === Following methods are used in building the worksheet. # === They are not part of the API. def tidy_dimensions(self): if self.verbosity >= 3: fprintf(self.logfile, "tidy_dimensions: nrows=%d ncols=%d \n", self.nrows, self.ncols, ) if 1 and self.merged_cells: nr = nc = 0 umaxrows = self.utter_max_rows umaxcols = self.utter_max_cols for crange in self.merged_cells: rlo, rhi, clo, chi = crange if not (0 <= rlo < rhi <= umaxrows) \ or not (0 <= clo < chi <= umaxcols): fprintf(self.logfile, "*** WARNING: sheet #%d (%r), MERGEDCELLS bad range %r\n", self.number, self.name, crange) if rhi > nr: nr = rhi if chi > nc: nc = chi if nc > self.ncols: self.ncols = nc self._first_full_rowx = -2 if nr > self.nrows: # we put one empty cell at (nr-1,0) to make sure # we have the right number of rows. The ragged rows # will sort out the rest if needed. self.put_cell(nr-1, 0, XL_CELL_EMPTY, UNICODE_LITERAL(''), -1) if self.verbosity >= 1 \ and (self.nrows != self._dimnrows or self.ncols != self._dimncols): fprintf(self.logfile, "NOTE *** sheet %d (%r): DIMENSIONS R,C = %d,%d should be %d,%d\n", self.number, self.name, self._dimnrows, self._dimncols, self.nrows, self.ncols, ) if not self.ragged_rows: # fix ragged rows ncols = self.ncols s_cell_types = self._cell_types s_cell_values = self._cell_values s_cell_xf_indexes = self._cell_xf_indexes s_fmt_info = self.formatting_info # for rowx in xrange(self.nrows): if self._first_full_rowx == -2: ubound = self.nrows else: ubound = self._first_full_rowx for rowx in xrange(ubound): trow = s_cell_types[rowx] rlen = len(trow) nextra = ncols - rlen if nextra > 0: s_cell_values[rowx][rlen:] = [UNICODE_LITERAL('')] * nextra trow[rlen:] = self.bt * nextra if s_fmt_info: s_cell_xf_indexes[rowx][rlen:] = self.bf * nextra def put_cell_ragged(self, rowx, colx, ctype, value, xf_index): if ctype is None: # we have a number, so look up the cell type ctype = self._xf_index_to_xl_type_map[xf_index] assert 0 <= colx < self.utter_max_cols assert 0 <= rowx < self.utter_max_rows fmt_info = self.formatting_info try: nr = rowx + 1 if self.nrows < nr: scta = self._cell_types.append scva = self._cell_values.append scxa = self._cell_xf_indexes.append bt = self.bt bf = self.bf for _unused in xrange(self.nrows, nr): scta(bt * 0) scva([]) if fmt_info: scxa(bf * 0) self.nrows = nr types_row = self._cell_types[rowx] values_row = self._cell_values[rowx] if fmt_info: fmt_row = self._cell_xf_indexes[rowx] ltr = len(types_row) if colx >= self.ncols: self.ncols = colx + 1 num_empty = colx - ltr if not num_empty: # most common case: colx == previous colx + 1 # self._put_cell_cells_appended += 1 types_row.append(ctype) values_row.append(value) if fmt_info: fmt_row.append(xf_index) return if num_empty > 0: num_empty += 1 # self._put_cell_row_widenings += 1 # types_row.extend(self.bt * num_empty) # values_row.extend([UNICODE_LITERAL('')] * num_empty) # if fmt_info: # fmt_row.extend(self.bf * num_empty) types_row[ltr:] = self.bt * num_empty values_row[ltr:] = [UNICODE_LITERAL('')] * num_empty if fmt_info: fmt_row[ltr:] = self.bf * num_empty types_row[colx] = ctype values_row[colx] = value if fmt_info: fmt_row[colx] = xf_index except: print("put_cell", rowx, colx, file=self.logfile) raise def put_cell_unragged(self, rowx, colx, ctype, value, xf_index): if ctype is None: # we have a number, so look up the cell type ctype = self._xf_index_to_xl_type_map[xf_index] # assert 0 <= colx < self.utter_max_cols # assert 0 <= rowx < self.utter_max_rows try: self._cell_types[rowx][colx] = ctype self._cell_values[rowx][colx] = value if self.formatting_info: self._cell_xf_indexes[rowx][colx] = xf_index except IndexError: # print >> self.logfile, "put_cell extending", rowx, colx # self.extend_cells(rowx+1, colx+1) # self._put_cell_exceptions += 1 nr = rowx + 1 nc = colx + 1 assert 1 <= nc <= self.utter_max_cols assert 1 <= nr <= self.utter_max_rows if nc > self.ncols: self.ncols = nc # The row self._first_full_rowx and all subsequent rows # are guaranteed to have length == self.ncols. Thus the # "fix ragged rows" section of the tidy_dimensions method # doesn't need to examine them. if nr < self.nrows: # cell data is not in non-descending row order *AND* # self.ncols has been bumped up. # This very rare case ruins this 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([UNICODE_LITERAL('')] * nextra) else: scta = self._cell_types.append scva = self._cell_values.append scxa = self._cell_xf_indexes.append fmt_info = self.formatting_info nc = self.ncols bt = self.bt bf = self.bf for _unused in xrange(self.nrows, nr): # self._put_cell_rows_appended += 1 scta(bt * nc) scva([UNICODE_LITERAL('')] * nc) if fmt_info: scxa(bf * nc) self.nrows = nr # === end of code from extend_cells() try: self._cell_types[rowx][colx] = ctype self._cell_values[rowx][colx] = value if self.formatting_info: self._cell_xf_indexes[rowx][colx] = xf_index except: print("put_cell", rowx, colx, file=self.logfile) raise except: print("put_cell", rowx, colx, file=self.logfile) raise # === Methods after this line neither know nor care about how cells are stored. def read(self, bk): global rc_stats DEBUG = 0 blah = DEBUG or self.verbosity >= 2 blah_rows = DEBUG or self.verbosity >= 4 blah_formulas = 0 and blah r1c1 = 0 oldpos = bk._position bk._position = self._position XL_SHRFMLA_ETC_ETC = ( XL_SHRFMLA, XL_ARRAY, XL_TABLEOP, XL_TABLEOP2, XL_ARRAY2, XL_TABLEOP_B2, ) self_put_cell = self.put_cell local_unpack = unpack bk_get_record_parts = bk.get_record_parts bv = self.biff_version fmt_info = self.formatting_info do_sst_rich_text = fmt_info and bk._rich_text_runlist_map rowinfo_sharing_dict = {} txos = {} eof_found = 0 while 1: # if DEBUG: print "SHEET.READ: about to read from position %d" % bk._position rc, data_len, data = bk_get_record_parts() # if rc in rc_stats: # rc_stats[rc] += 1 # else: # rc_stats[rc] = 1 # if DEBUG: print "SHEET.READ: op 0x%04x, %d bytes %r" % (rc, data_len, data) if rc == XL_NUMBER: # [:14] in following stmt ignores extraneous rubbish at end of record. # Sample file testEON-8.xls supplied by Jan Kraus. rowx, colx, xf_index, d = local_unpack('> 15) & 1 r.outline_level = bits2 & 7 r.outline_group_starts_ends = (bits2 >> 4) & 1 r.hidden = (bits2 >> 5) & 1 r.height_mismatch = (bits2 >> 6) & 1 r.has_default_xf_index = (bits2 >> 7) & 1 r.xf_index = (bits2 >> 16) & 0xfff r.additional_space_above = (bits2 >> 28) & 1 r.additional_space_below = (bits2 >> 29) & 1 if not r.has_default_xf_index: r.xf_index = -1 self.rowinfo_map[rowx] = r if 0 and r.xf_index > -1: fprintf(self.logfile, "**ROW %d %d %d\n", self.number, rowx, r.xf_index) if blah_rows: print('ROW', rowx, bits1, bits2, file=self.logfile) r.dump(self.logfile, header="--- sh #%d, rowx=%d ---" % (self.number, rowx)) elif rc in XL_FORMULA_OPCODES: # 06, 0206, 0406 # DEBUG = 1 # if DEBUG: print "FORMULA: rc: 0x%04x data: %r" % (rc, data) if bv >= 50: rowx, colx, xf_index, result_str, flags = local_unpack('= 30: rowx, colx, xf_index, result_str, flags = local_unpack(' 255: break # Excel does 0 to 256 inclusive self.colinfo_map[colx] = c if 0: fprintf(self.logfile, "**COL %d %d %d\n", self.number, colx, c.xf_index) if blah: fprintf( self.logfile, "COLINFO sheet #%d cols %d-%d: wid=%d xf_index=%d flags=0x%04x\n", self.number, first_colx, last_colx, c.width, c.xf_index, flags, ) c.dump(self.logfile, header='===') elif rc == XL_DEFCOLWIDTH: self.defcolwidth, = local_unpack(">= 1 self.gcw = tuple(gcw) if 0: showgcw = "".join(map(lambda x: "F "[x], gcw)).rstrip().replace(' ', '.') print("GCW:", showgcw, file=self.logfile) elif rc == XL_BLANK: if not fmt_info: continue rowx, colx, xf_index = local_unpack('> self.logfile, "BLANK", rowx, colx, xf_index self_put_cell(rowx, colx, XL_CELL_BLANK, '', xf_index) elif rc == XL_MULBLANK: # 00BE if not fmt_info: continue nitems = data_len >> 1 result = local_unpack("<%dH" % nitems, data) rowx, mul_first = result[:2] mul_last = result[-1] # print >> self.logfile, "MULBLANK", rowx, mul_first, mul_last, data_len, nitems, mul_last + 4 - mul_first assert nitems == mul_last + 4 - mul_first pos = 2 for colx in xrange(mul_first, mul_last + 1): self_put_cell(rowx, colx, XL_CELL_BLANK, '', result[pos]) pos += 1 elif rc == XL_DIMENSION or rc == XL_DIMENSION2: if data_len == 0: # Four zero bytes after some other record. See github issue 64. continue # if data_len == 10: # Was crashing on BIFF 4.0 file w/o the two trailing unused bytes. # Reported by Ralph Heimburger. if bv < 80: dim_tuple = local_unpack(' found EOF", file=self.logfile) elif rc == XL_COUNTRY: bk.handle_country(data) elif rc == XL_LABELRANGES: pos = 0 pos = unpack_cell_range_address_list_update_pos( self.row_label_ranges, data, pos, bv, addr_size=8, ) pos = unpack_cell_range_address_list_update_pos( self.col_label_ranges, data, pos, bv, addr_size=8, ) assert pos == data_len elif rc == XL_ARRAY: row1x, rownx, col1x, colnx, array_flags, tokslen = \ local_unpack("= 80 num_CFs, needs_recalc, browx1, browx2, bcolx1, bcolx2 = \ unpack("<6H", data[0:12]) if self.verbosity >= 1: fprintf(self.logfile, "\n*** WARNING: Ignoring CONDFMT (conditional formatting) record\n" "*** in Sheet %d (%r).\n" "*** %d CF record(s); needs_recalc_or_redraw = %d\n" "*** Bounding box is %s\n", self.number, self.name, num_CFs, needs_recalc, rangename2d(browx1, browx2+1, bcolx1, bcolx2+1), ) olist = [] # updated by the function pos = unpack_cell_range_address_list_update_pos( olist, data, 12, bv, addr_size=8) # print >> self.logfile, repr(result), len(result) if self.verbosity >= 1: fprintf(self.logfile, "*** %d individual range(s):\n" "*** %s\n", len(olist), ", ".join(rangename2d(*coords) for coords in olist), ) elif rc == XL_CF: if not fmt_info: continue cf_type, cmp_op, sz1, sz2, flags = unpack("> 26) & 1 bord_block = (flags >> 28) & 1 patt_block = (flags >> 29) & 1 if self.verbosity >= 1: fprintf(self.logfile, "\n*** WARNING: Ignoring CF (conditional formatting) sub-record.\n" "*** cf_type=%d, cmp_op=%d, sz1=%d, sz2=%d, flags=0x%08x\n" "*** optional data blocks: font=%d, border=%d, pattern=%d\n", cf_type, cmp_op, sz1, sz2, flags, font_block, bord_block, patt_block, ) # hex_char_dump(data, 0, data_len, fout=self.logfile) pos = 12 if font_block: (font_height, font_options, weight, escapement, underline, font_colour_index, two_bits, font_esc, font_underl) = \ unpack("<64x i i H H B 3x i 4x i i i 18x", data[pos:pos+118]) font_style = (two_bits > 1) & 1 posture = (font_options > 1) & 1 font_canc = (two_bits > 7) & 1 cancellation = (font_options > 7) & 1 if self.verbosity >= 1: fprintf(self.logfile, "*** Font info: height=%d, weight=%d, escapement=%d,\n" "*** underline=%d, colour_index=%d, esc=%d, underl=%d,\n" "*** style=%d, posture=%d, canc=%d, cancellation=%d\n", font_height, weight, escapement, underline, font_colour_index, font_esc, font_underl, font_style, posture, font_canc, cancellation, ) pos += 118 if bord_block: pos += 8 if patt_block: pos += 4 fmla1 = data[pos:pos+sz1] pos += sz1 if blah and sz1: fprintf(self.logfile, "*** formula 1:\n", ) dump_formula(bk, fmla1, sz1, bv, reldelta=0, blah=1) fmla2 = data[pos:pos+sz2] pos += sz2 assert pos == data_len if blah and sz2: fprintf(self.logfile, "*** formula 2:\n", ) dump_formula(bk, fmla2, sz2, bv, reldelta=0, blah=1) elif rc == XL_DEFAULTROWHEIGHT: if data_len == 4: bits, self.default_row_height = unpack("> 1) & 1 self.default_additional_space_above = (bits >> 2) & 1 self.default_additional_space_below = (bits >> 3) & 1 elif rc == XL_MERGEDCELLS: if not fmt_info: continue pos = unpack_cell_range_address_list_update_pos( self.merged_cells, data, 0, bv, addr_size=8) if blah: fprintf(self.logfile, "MERGEDCELLS: %d ranges\n", (pos - 2) // 8) assert pos == data_len, \ "MERGEDCELLS: pos=%d data_len=%d" % (pos, data_len) elif rc == XL_WINDOW2: if bv >= 80 and data_len >= 14: (options, self.first_visible_rowx, self.first_visible_colx, self.gridline_colour_index, self.cached_page_break_preview_mag_factor, self.cached_normal_view_mag_factor ) = unpack("= 30 # BIFF3-7 (options, self.first_visible_rowx, self.first_visible_colx, ) = unpack(">= 1 elif rc == XL_SCL: num, den = unpack("= 0: print(( "WARNING *** SCL rcd sheet %d: should have 0.1 <= num/den <= 4; got %d/%d" % (self.number, num, den) ), file=self.logfile) result = 100 self.scl_mag_factor = result elif rc == XL_PANE: ( self.vert_split_pos, self.horz_split_pos, self.horz_split_first_visible, self.vert_split_first_visible, self.split_active_pane, ) = unpack("= 80)) + 2 == data_len pos = 2 if bv < 80: while pos < data_len: self.horizontal_page_breaks.append((local_unpack("= 80)) + 2 == data_len pos = 2 if bv < 80: while pos < data_len: self.vertical_page_breaks.append((local_unpack("> 15) & 1 r.has_default_xf_index = bits2 & 1 r.xf_index = xf_index # r.outline_level = 0 # set in __init__ # r.outline_group_starts_ends = 0 # set in __init__ # r.hidden = 0 # set in __init__ # r.height_mismatch = 0 # set in __init__ # r.additional_space_above = 0 # set in __init__ # r.additional_space_below = 0 # set in __init__ self.rowinfo_map[rowx] = r if 0 and r.xf_index > -1: fprintf(self.logfile, "**ROW %d %d %d\n", self.number, rowx, r.xf_index) if blah_rows: print('ROW_B2', rowx, bits1, file=self.logfile) r.dump(self.logfile, header="--- sh #%d, rowx=%d ---" % (self.number, rowx)) elif rc == XL_COLWIDTH: # BIFF2 only if not fmt_info: continue first_colx, last_colx, width\ = local_unpack("= 30) + 1 nchars_expected = unpack("<" + "BH"[lenlen - 1], data[:lenlen])[0] offset = lenlen if bv < 80: enc = bk.encoding or bk.derive_encoding() nchars_found = 0 result = UNICODE_LITERAL("") while 1: if bv >= 80: flag = BYTES_ORD(data[offset]) & 1 enc = ("latin_1", "utf_16_le")[flag] offset += 1 chunk = unicode(data[offset:], enc) result += chunk nchars_found += len(chunk) if nchars_found == nchars_expected: return result if nchars_found > nchars_expected: msg = ("STRING/CONTINUE: expected %d chars, found %d" % (nchars_expected, nchars_found)) raise XLRDError(msg) rc, _unused_len, data = bk.get_record_parts() if rc != XL_CONTINUE: raise XLRDError( "Expected CONTINUE record; found record-type 0x%04X" % rc) offset = 0 def update_cooked_mag_factors(self): # Cached values are used ONLY for the non-active view mode. # When the user switches to the non-active view mode, # if the cached value for that mode is not valid, # Excel pops up a window which says: # "The number must be between 10 and 400. Try again by entering a number in this range." # When the user hits OK, it drops into the non-active view mode # but uses the magn from the active mode. # NOTE: definition of "valid" depends on mode ... see below blah = DEBUG or self.verbosity > 0 if self.show_in_page_break_preview: if self.scl_mag_factor is None: # no SCL record self.cooked_page_break_preview_mag_factor = 100 # Yes, 100, not 60, NOT a typo else: self.cooked_page_break_preview_mag_factor = self.scl_mag_factor zoom = self.cached_normal_view_mag_factor if not (10 <= zoom <=400): if blah: print(( "WARNING *** WINDOW2 rcd sheet %d: Bad cached_normal_view_mag_factor: %d" % (self.number, self.cached_normal_view_mag_factor) ), file=self.logfile) zoom = self.cooked_page_break_preview_mag_factor self.cooked_normal_view_mag_factor = zoom else: # normal view mode if self.scl_mag_factor is None: # no SCL record self.cooked_normal_view_mag_factor = 100 else: self.cooked_normal_view_mag_factor = self.scl_mag_factor zoom = self.cached_page_break_preview_mag_factor if not zoom: # VALID, defaults to 60 zoom = 60 elif not (10 <= zoom <= 400): if blah: print(( "WARNING *** WINDOW2 rcd sheet %r: Bad cached_page_break_preview_mag_factor: %r" % (self.number, self.cached_page_break_preview_mag_factor) ), file=self.logfile) zoom = self.cooked_normal_view_mag_factor self.cooked_page_break_preview_mag_factor = zoom def fixed_BIFF2_xfindex(self, cell_attr, rowx, colx, true_xfx=None): DEBUG = 0 blah = DEBUG or self.verbosity >= 2 if self.biff_version == 21: if self.book.xf_list: if true_xfx is not None: xfx = true_xfx else: xfx = BYTES_ORD(cell_attr[0]) & 0x3F if xfx == 0x3F: if self._ixfe is None: raise XLRDError("BIFF2 cell record has XF index 63 but no preceding IXFE record.") xfx = self._ixfe # OOo docs are capable of interpretation that each # cell record is preceded immediately by its own IXFE record. # Empirical evidence is that (sensibly) an IXFE record applies to all # following cell records until another IXFE comes along. return xfx # Have either Excel 2.0, or broken 2.1 w/o XF records -- same effect. self.biff_version = self.book.biff_version = 20 #### check that XF slot in cell_attr is zero xfx_slot = BYTES_ORD(cell_attr[0]) & 0x3F assert xfx_slot == 0 xfx = self._cell_attr_to_xfx.get(cell_attr) if xfx is not None: return xfx if blah: fprintf(self.logfile, "New cell_attr %r at (%r, %r)\n", cell_attr, rowx, colx) if not self.book.xf_list: for xfx in xrange(16): self.insert_new_BIFF20_xf(cell_attr=b"\x40\x00\x00", style=xfx < 15) xfx = self.insert_new_BIFF20_xf(cell_attr=cell_attr) return xfx def insert_new_BIFF20_xf(self, cell_attr, style=0): DEBUG = 0 blah = DEBUG or self.verbosity >= 2 book = self.book xfx = len(book.xf_list) xf = self.fake_XF_from_BIFF20_cell_attr(cell_attr, style) xf.xf_index = xfx book.xf_list.append(xf) if blah: xf.dump(self.logfile, header="=== Faked XF %d ===" % xfx, footer="======") if xf.format_key not in book.format_map: if xf.format_key: msg = "ERROR *** XF[%d] unknown format key (%d, 0x%04x)\n" fprintf(self.logfile, msg, xf.xf_index, xf.format_key, xf.format_key) fmt = Format(xf.format_key, FUN, UNICODE_LITERAL("General")) book.format_map[xf.format_key] = fmt book.format_list.append(fmt) cellty_from_fmtty = { FNU: XL_CELL_NUMBER, FUN: XL_CELL_NUMBER, FGE: XL_CELL_NUMBER, FDT: XL_CELL_DATE, FTX: XL_CELL_NUMBER, # Yes, a number can be formatted as text. } fmt = book.format_map[xf.format_key] cellty = cellty_from_fmtty[fmt.type] self._xf_index_to_xl_type_map[xf.xf_index] = cellty self._cell_attr_to_xfx[cell_attr] = xfx return xfx def fake_XF_from_BIFF20_cell_attr(self, cell_attr, style=0): from .formatting import XF, XFAlignment, XFBorder, XFBackground, XFProtection xf = XF() xf.alignment = XFAlignment() xf.alignment.indent_level = 0 xf.alignment.shrink_to_fit = 0 xf.alignment.text_direction = 0 xf.border = XFBorder() xf.border.diag_up = 0 xf.border.diag_down = 0 xf.border.diag_colour_index = 0 xf.border.diag_line_style = 0 # no line xf.background = XFBackground() xf.protection = XFProtection() (prot_bits, font_and_format, halign_etc) = unpack('> 6 upkbits(xf.protection, prot_bits, ( (6, 0x40, 'cell_locked'), (7, 0x80, 'formula_hidden'), )) xf.alignment.hor_align = halign_etc & 0x07 for mask, side in ((0x08, 'left'), (0x10, 'right'), (0x20, 'top'), (0x40, 'bottom')): if halign_etc & mask: colour_index, line_style = 8, 1 # black, thin else: colour_index, line_style = 0, 0 # none, none setattr(xf.border, side + '_colour_index', colour_index) setattr(xf.border, side + '_line_style', line_style) bg = xf.background if halign_etc & 0x80: bg.fill_pattern = 17 else: bg.fill_pattern = 0 bg.background_colour_index = 9 # white bg.pattern_colour_index = 8 # black xf.parent_style_index = (0x0FFF, 0)[style] xf.alignment.vert_align = 2 # bottom xf.alignment.rotation = 0 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)") def computed_column_width(self, colx): """ Determine column display width. :param colx: Index of the queried column, range 0 to 255. Note that it is possible to find out the width that will be used to display columns with no cell information e.g. column IV (colx=255). :return: The column width that will be used for displaying the given column by Excel, in units of 1/256th of the width of a standard character (the digit zero in the first font). .. versionadded:: 0.6.1 """ self.req_fmt_info() if self.biff_version >= 80: colinfo = self.colinfo_map.get(colx, None) if colinfo is not None: return colinfo.width if self.standardwidth is not None: return self.standardwidth elif self.biff_version >= 40: if self.gcw[colx]: if self.standardwidth is not None: return self.standardwidth else: colinfo = self.colinfo_map.get(colx, None) if colinfo is not None: return colinfo.width elif self.biff_version == 30: colinfo = self.colinfo_map.get(colx, None) if colinfo is not None: return colinfo.width # All roads lead to Rome and the DEFCOLWIDTH ... if self.defcolwidth is not None: return self.defcolwidth * 256 return 8 * 256 # 8 is what Excel puts in a DEFCOLWIDTH record def handle_hlink(self, data): # DEBUG = 1 if DEBUG: print("\n=== hyperlink ===", file=self.logfile) record_size = len(data) h = Hyperlink() h.frowx, h.lrowx, h.fcolx, h.lcolx, guid0, dummy, options = unpack(' 0: fprintf( self.logfile, "*** WARNING: hyperlink at R%dC%d has %d extra data bytes: %s\n", h.frowx + 1, h.fcolx + 1, extra_nbytes, REPR(data[-extra_nbytes:]) ) # Seen: b"\x00\x00" also b"A\x00", b"V\x00" elif extra_nbytes < 0: raise XLRDError("Bug or corrupt file, send copy of input file for debugging") self.hyperlink_list.append(h) for rowx in xrange(h.frowx, h.lrowx+1): for colx in xrange(h.fcolx, h.lcolx+1): self.hyperlink_map[rowx, colx] = h def handle_quicktip(self, data): rcx, frowx, lrowx, fcolx, lcolx = unpack('<5H', data[:10]) assert rcx == XL_QUICKTIP assert self.hyperlink_list h = self.hyperlink_list[-1] assert (frowx, lrowx, fcolx, lcolx) == (h.frowx, h.lrowx, h.fcolx, h.lcolx) assert data[-2:] == b'\x00\x00' h.quicktip = unicode(data[10:-2], 'utf_16_le') def handle_msodrawingetc(self, recid, data_len, data): if not OBJ_MSO_DEBUG: return DEBUG = 1 if self.biff_version < 80: return o = MSODrawing() pos = 0 while pos < data_len: tmp, fbt, cb = unpack('> 4) & 0xFFF if ver == 0xF: ndb = 0 # container else: ndb = cb if DEBUG: hex_char_dump(data, pos, ndb + 8, base=0, fout=self.logfile) fprintf(self.logfile, "fbt:0x%04X inst:%d ver:0x%X cb:%d (0x%04X)\n", fbt, inst, ver, cb, cb) if fbt == 0xF010: # Client Anchor assert ndb == 18 (o.anchor_unk, o.anchor_colx_lo, o.anchor_rowx_lo, o.anchor_colx_hi, o.anchor_rowx_hi) = unpack(' 0: rc2, data2_len, data2 = self.book.get_record_parts() assert rc2 == XL_NOTE dummy_rowx, nb = unpack('> 1) & 1 o.row_hidden = (option_flags >> 7) & 1 o.col_hidden = (option_flags >> 8) & 1 # XL97 dev kit book says NULL [sic] bytes padding between string count and string data # to ensure that string is word-aligned. Appears to be nonsense. o.author, endpos = unpack_unicode_update_pos(data, 8, lenlen=2) # There is a random/undefined byte after the author string (not counted in the # string length). # Issue 4 on github: Google Spreadsheet doesn't write the undefined byte. assert (data_len - endpos) in (0, 1) if OBJ_MSO_DEBUG: o.dump(self.logfile, header="=== Note ===", footer= " ") txo = txos.get(o._object_id) if txo: o.text = txo.text o.rich_text_runlist = txo.rich_text_runlist self.cell_note_map[o.rowx, o.colx] = o def handle_txo(self, data): if self.biff_version < 80: return o = MSTxo() data_len = len(data) fmt = '