mirror of
https://github.com/kennethreitz/tablib.git
synced 2026-06-05 06:56:13 +00:00
committed by
Iuri de Silvio
parent
e66eb4a189
commit
0ca5520bbc
@@ -1,5 +1,5 @@
|
||||
Tablib includes some vendorized python libraries: ordereddict, odfpy, pyyaml,
|
||||
simplejson, unicodecsv, xlrd, xlrd3, xlwt, and xlwt3.
|
||||
simplejson, unicodecsv.
|
||||
|
||||
Markup License
|
||||
==============
|
||||
@@ -295,165 +295,3 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
The views and conclusions contained in the software and documentation are those of the
|
||||
authors and should not be interpreted as representing official policies, either expressed
|
||||
or implied, of Jeremy Dunck.
|
||||
|
||||
|
||||
|
||||
|
||||
XLWT (and XLWT3) License
|
||||
========================
|
||||
|
||||
Portions copyright © 2007, Stephen John Machin, Lingfo Pty Ltd
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. None of the names of Stephen John Machin, Lingfo Pty Ltd and any
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
|
||||
"""
|
||||
Copyright (C) 2005 Roman V. Kiseliov
|
||||
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. All advertising materials mentioning features or use of this
|
||||
software must display the following acknowledgment:
|
||||
"This product includes software developed by
|
||||
Roman V. Kiseliov <roman@kiseliov.ru>."
|
||||
|
||||
4. Redistributions of any form whatsoever must retain the following
|
||||
acknowledgment:
|
||||
"This product includes software developed by
|
||||
Roman V. Kiseliov <roman@kiseliov.ru>."
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Roman V. Kiseliov ``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 Roman V. Kiseliov 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.
|
||||
|
||||
Roman V. Kiseliov
|
||||
Russia
|
||||
Kursk
|
||||
Libknecht St., 4
|
||||
|
||||
+7(0712)56-09-83
|
||||
|
||||
<roman@kiseliov.ru>
|
||||
Subject: pyExcelerator
|
||||
|
||||
|
||||
|
||||
|
||||
XLRD (and XLRD3) License
|
||||
========================
|
||||
|
||||
Portions copyright © 2005-2009, Stephen John Machin, Lingfo Pty Ltd
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. None of the names of Stephen John Machin, Lingfo Pty Ltd and any
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Copyright (c) 2001 David Giffin.
|
||||
All rights reserved.
|
||||
|
||||
Based on the the Java version: Andrew Khan Copyright (c) 2000.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
3. All advertising materials mentioning features or use of this
|
||||
software must display the following acknowledgment:
|
||||
"This product includes software developed by
|
||||
David Giffin <david@giffin.org>."
|
||||
|
||||
4. Redistributions of any form whatsoever must retain the following
|
||||
acknowledgment:
|
||||
"This product includes software developed by
|
||||
David Giffin <david@giffin.org>."
|
||||
|
||||
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.
|
||||
|
||||
@@ -38,13 +38,9 @@ packages = [
|
||||
'tablib', 'tablib.formats',
|
||||
'tablib.packages',
|
||||
'tablib.packages.omnijson',
|
||||
'tablib.packages.xlwt',
|
||||
'tablib.packages.xlrd',
|
||||
'tablib.packages.odf',
|
||||
'tablib.packages.yaml',
|
||||
'tablib.packages.dbfpy',
|
||||
'tablib.packages.xlwt3',
|
||||
'tablib.packages.xlrd3',
|
||||
'tablib.packages.odf3',
|
||||
'tablib.packages.yaml3',
|
||||
'tablib.packages.dbfpy3'
|
||||
@@ -53,6 +49,8 @@ packages = [
|
||||
install = [
|
||||
'openpyxl',
|
||||
'unicodecsv',
|
||||
'xlrd',
|
||||
'xlwt',
|
||||
]
|
||||
|
||||
with open('tablib/core.py', 'r') as fd:
|
||||
|
||||
@@ -22,9 +22,6 @@ except ImportError:
|
||||
|
||||
if is_py3:
|
||||
from io import BytesIO
|
||||
import tablib.packages.xlwt3 as xlwt
|
||||
import tablib.packages.xlrd3 as xlrd
|
||||
from tablib.packages.xlrd3.biffh import XLRDError
|
||||
from tablib.packages import markup3 as markup
|
||||
from tablib.packages.odf3 import opendocument, style, text, table
|
||||
import tablib.packages.dbfpy3 as dbfpy
|
||||
@@ -42,9 +39,6 @@ if is_py3:
|
||||
else:
|
||||
from cStringIO import StringIO as BytesIO
|
||||
from cStringIO import StringIO
|
||||
import tablib.packages.xlwt as xlwt
|
||||
import tablib.packages.xlrd as xlrd
|
||||
from tablib.packages.xlrd.biffh import XLRDError
|
||||
from tablib.packages import markup
|
||||
from itertools import ifilter
|
||||
from tablib.packages.odf import opendocument, style, text, table
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
|
||||
import sys
|
||||
|
||||
from tablib.compat import BytesIO, xlwt, xlrd, XLRDError, xrange
|
||||
from tablib.compat import BytesIO, xrange
|
||||
import tablib
|
||||
import xlrd
|
||||
import xlwt
|
||||
from xlrd.biffh import XLRDError
|
||||
|
||||
title = 'xls'
|
||||
extensions = ('xls',)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,639 +0,0 @@
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
##
|
||||
# Support module for the xlrd package.
|
||||
#
|
||||
# <p>Portions copyright © 2005-2008 Stephen John Machin, Lingfo Pty Ltd</p>
|
||||
# <p>This module is part of the xlrd package, which is released under a BSD-style licence.</p>
|
||||
##
|
||||
|
||||
# 2008-02-10 SJM BIFF2 BLANK record
|
||||
# 2008-02-08 SJM Preparation for Excel 2.0 support
|
||||
# 2008-02-02 SJM Added suffixes (_B2, _B2_ONLY, etc) on record names for biff_dump & biff_count
|
||||
# 2007-12-04 SJM Added support for Excel 2.x (BIFF2) files.
|
||||
# 2007-09-08 SJM Avoid crash when zero-length Unicode string missing options byte.
|
||||
# 2007-04-22 SJM Remove experimental "trimming" facility.
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
from struct import unpack
|
||||
import sys
|
||||
from timemachine import *
|
||||
|
||||
class XLRDError(Exception):
|
||||
pass
|
||||
|
||||
##
|
||||
# Parent of almost all other classes in the package. Defines a common "dump" method
|
||||
# for debugging.
|
||||
|
||||
class BaseObject(object):
|
||||
|
||||
_repr_these = []
|
||||
|
||||
##
|
||||
# @param f open file object, to which the dump is written
|
||||
# @param header text to write before the dump
|
||||
# @param footer text to write after the dump
|
||||
# @param indent number of leading spaces (for recursive calls)
|
||||
|
||||
def dump(self, f=None, header=None, footer=None, indent=0):
|
||||
if f is None:
|
||||
f = sys.stderr
|
||||
alist = self.__dict__.items()
|
||||
alist.sort()
|
||||
pad = " " * indent
|
||||
if header is not None: print >> f, header
|
||||
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 >> f, "%s%s: %s, len = %d" % (pad, attr, type(value), len(value))
|
||||
else:
|
||||
print >> f, "%s%s: %r" % (pad, attr, value)
|
||||
if footer is not None: print >> f, footer
|
||||
|
||||
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",
|
||||
}
|
||||
|
||||
##
|
||||
# <p>This dictionary can be used to produce a text version of the internal codes
|
||||
# that Excel uses for error cells. Here are its contents:
|
||||
# <pre>
|
||||
# 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
|
||||
# </pre></p>
|
||||
|
||||
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_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_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_PALETTE = 0x92
|
||||
XL_RK = 0x27e
|
||||
XL_ROW = 0x208
|
||||
XL_ROW_B2 = 0x08
|
||||
XL_RSTRING = 0xd6
|
||||
XL_SHEETHDR = 0x8F # BIFF4W only
|
||||
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
|
||||
XL_TABLEOP = 0x236
|
||||
XL_TABLEOP2 = 0x37
|
||||
XL_TABLEOP_B2 = 0x36
|
||||
XL_TXO = 0x1b6
|
||||
XL_UNCALCED = 0x5e
|
||||
XL_UNKNOWN = 0xffff
|
||||
XL_WINDOW2 = 0x023E
|
||||
XL_WRITEACCESS = 0x5C
|
||||
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
|
||||
is_cell_opcode = _cell_opcode_dict.has_key
|
||||
|
||||
# def fprintf(f, fmt, *vargs): f.write(fmt % vargs)
|
||||
|
||||
def fprintf(f, fmt, *vargs):
|
||||
if fmt.endswith('\n'):
|
||||
print >> f, fmt[:-1] % vargs
|
||||
else:
|
||||
print >> f, fmt % vargs,
|
||||
|
||||
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 u""
|
||||
pos += lenlen
|
||||
options = ord(data[pos])
|
||||
pos += 1
|
||||
# phonetic = options & 0x04
|
||||
# richtext = options & 0x08
|
||||
if options & 0x08:
|
||||
# rt = unpack('<H', data[pos:pos+2])[0] # unused
|
||||
pos += 2
|
||||
if options & 0x04:
|
||||
# sz = unpack('<i', data[pos:pos+4])[0] # unused
|
||||
pos += 4
|
||||
if options & 0x01:
|
||||
# Uncompressed UTF-16-LE
|
||||
rawstrg = data[pos:pos+2*nchars]
|
||||
# if DEBUG: print "nchars=%d pos=%d rawstrg=%r" % (nchars, pos, rawstrg)
|
||||
strg = unicode(rawstrg, 'utf_16_le')
|
||||
# pos += 2*nchars
|
||||
else:
|
||||
# Note: this is COMPRESSED (not ASCII!) encoding!!!
|
||||
# Merely returning the raw bytes would work OK 99.99% of the time
|
||||
# if the local codepage was cp1252 -- however this would rapidly go pear-shaped
|
||||
# for other codepages so we grit our Anglocentric teeth and return Unicode :-)
|
||||
|
||||
strg = unicode(data[pos:pos+nchars], "latin_1")
|
||||
# pos += nchars
|
||||
# if richtext:
|
||||
# pos += 4 * rt
|
||||
# if phonetic:
|
||||
# pos += sz
|
||||
# return (strg, pos)
|
||||
return strg
|
||||
|
||||
def unpack_unicode_update_pos(data, pos, lenlen=2, known_len=None):
|
||||
"Return (unicode_strg, updated value of pos)"
|
||||
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
|
||||
if not nchars and not data[pos:]:
|
||||
# Zero-length string with no options byte
|
||||
return (u"", pos)
|
||||
options = ord(data[pos])
|
||||
pos += 1
|
||||
phonetic = options & 0x04
|
||||
richtext = options & 0x08
|
||||
if richtext:
|
||||
rt = unpack('<H', data[pos:pos+2])[0]
|
||||
pos += 2
|
||||
if phonetic:
|
||||
sz = unpack('<i', data[pos:pos+4])[0]
|
||||
pos += 4
|
||||
if options & 0x01:
|
||||
# Uncompressed UTF-16-LE
|
||||
strg = unicode(data[pos:pos+2*nchars], 'utf_16_le')
|
||||
pos += 2*nchars
|
||||
else:
|
||||
# Note: this is COMPRESSED (not ASCII!) encoding!!!
|
||||
strg = unicode(data[pos:pos+nchars], "latin_1")
|
||||
pos += nchars
|
||||
if richtext:
|
||||
pos += 4 * rt
|
||||
if phonetic:
|
||||
pos += sz
|
||||
return (strg, pos)
|
||||
|
||||
def unpack_cell_range_address_list_update_pos(
|
||||
output_list, data, pos, biff_version, addr_size=6):
|
||||
# output_list is updated in situ
|
||||
if biff_version < 80:
|
||||
assert addr_size == 6
|
||||
else:
|
||||
assert addr_size in (6, 8)
|
||||
n, = unpack("<H", data[pos:pos+2])
|
||||
pos += 2
|
||||
if n:
|
||||
if addr_size == 6:
|
||||
fmt = "<HHBB"
|
||||
else:
|
||||
fmt = "<HHHH"
|
||||
for _unused in xrange(n):
|
||||
ra, rb, ca, cb = unpack(fmt, data[pos:pos+addr_size])
|
||||
output_list.append((ra, rb+1, ca, cb+1))
|
||||
pos += addr_size
|
||||
return pos
|
||||
|
||||
_brecstrg = """\
|
||||
0000 DIMENSIONS_B2
|
||||
0001 BLANK_B2
|
||||
0002 INTEGER_B2_ONLY
|
||||
0003 NUMBER_B2
|
||||
0004 LABEL_B2
|
||||
0005 BOOLERR_B2
|
||||
0006 FORMULA
|
||||
0007 STRING_B2
|
||||
0008 ROW_B2
|
||||
0009 BOF_B2
|
||||
000A EOF
|
||||
000B INDEX_B2_ONLY
|
||||
000C CALCCOUNT
|
||||
000D CALCMODE
|
||||
000E PRECISION
|
||||
000F REFMODE
|
||||
0010 DELTA
|
||||
0011 ITERATION
|
||||
0012 PROTECT
|
||||
0013 PASSWORD
|
||||
0014 HEADER
|
||||
0015 FOOTER
|
||||
0016 EXTERNCOUNT
|
||||
0017 EXTERNSHEET
|
||||
0018 NAME_B2,5+
|
||||
0019 WINDOWPROTECT
|
||||
001A VERTICALPAGEBREAKS
|
||||
001B HORIZONTALPAGEBREAKS
|
||||
001C NOTE
|
||||
001D SELECTION
|
||||
001E FORMAT_B2-3
|
||||
001F BUILTINFMTCOUNT_B2
|
||||
0020 COLUMNDEFAULT_B2_ONLY
|
||||
0021 ARRAY_B2_ONLY
|
||||
0022 DATEMODE
|
||||
0023 EXTERNNAME
|
||||
0024 COLWIDTH_B2_ONLY
|
||||
0025 DEFAULTROWHEIGHT_B2_ONLY
|
||||
0026 LEFTMARGIN
|
||||
0027 RIGHTMARGIN
|
||||
0028 TOPMARGIN
|
||||
0029 BOTTOMMARGIN
|
||||
002A PRINTHEADERS
|
||||
002B PRINTGRIDLINES
|
||||
002F FILEPASS
|
||||
0031 FONT
|
||||
0032 FONT2_B2_ONLY
|
||||
0036 TABLEOP_B2
|
||||
0037 TABLEOP2_B2
|
||||
003C CONTINUE
|
||||
003D WINDOW1
|
||||
003E WINDOW2_B2
|
||||
0040 BACKUP
|
||||
0041 PANE
|
||||
0042 CODEPAGE
|
||||
0043 XF_B2
|
||||
0044 IXFE_B2_ONLY
|
||||
0045 EFONT_B2_ONLY
|
||||
004D PLS
|
||||
0051 DCONREF
|
||||
0055 DEFCOLWIDTH
|
||||
0056 BUILTINFMTCOUNT_B3-4
|
||||
0059 XCT
|
||||
005A CRN
|
||||
005B FILESHARING
|
||||
005C WRITEACCESS
|
||||
005D OBJECT
|
||||
005E UNCALCED
|
||||
005F SAVERECALC
|
||||
0063 OBJECTPROTECT
|
||||
007D COLINFO
|
||||
007E RK2_mythical_?
|
||||
0080 GUTS
|
||||
0081 WSBOOL
|
||||
0082 GRIDSET
|
||||
0083 HCENTER
|
||||
0084 VCENTER
|
||||
0085 BOUNDSHEET
|
||||
0086 WRITEPROT
|
||||
008C COUNTRY
|
||||
008D HIDEOBJ
|
||||
008E SHEETSOFFSET
|
||||
008F SHEETHDR
|
||||
0090 SORT
|
||||
0092 PALETTE
|
||||
0099 STANDARDWIDTH
|
||||
009B FILTERMODE
|
||||
009C FNGROUPCOUNT
|
||||
009D AUTOFILTERINFO
|
||||
009E AUTOFILTER
|
||||
00A0 SCL
|
||||
00A1 SETUP
|
||||
00AB GCW
|
||||
00BD MULRK
|
||||
00BE MULBLANK
|
||||
00C1 MMS
|
||||
00D6 RSTRING
|
||||
00D7 DBCELL
|
||||
00DA BOOKBOOL
|
||||
00DD SCENPROTECT
|
||||
00E0 XF
|
||||
00E1 INTERFACEHDR
|
||||
00E2 INTERFACEEND
|
||||
00E5 MERGEDCELLS
|
||||
00E9 BITMAP
|
||||
00EB MSO_DRAWING_GROUP
|
||||
00EC MSO_DRAWING
|
||||
00ED MSO_DRAWING_SELECTION
|
||||
00EF PHONETIC
|
||||
00FC SST
|
||||
00FD LABELSST
|
||||
00FF EXTSST
|
||||
013D TABID
|
||||
015F LABELRANGES
|
||||
0160 USESELFS
|
||||
0161 DSF
|
||||
01AE SUPBOOK
|
||||
01AF PROTECTIONREV4
|
||||
01B0 CONDFMT
|
||||
01B1 CF
|
||||
01B2 DVAL
|
||||
01B6 TXO
|
||||
01B7 REFRESHALL
|
||||
01B8 HLINK
|
||||
01BC PASSWORDREV4
|
||||
01BE DV
|
||||
01C0 XL9FILE
|
||||
01C1 RECALCID
|
||||
0200 DIMENSIONS
|
||||
0201 BLANK
|
||||
0203 NUMBER
|
||||
0204 LABEL
|
||||
0205 BOOLERR
|
||||
0206 FORMULA_B3
|
||||
0207 STRING
|
||||
0208 ROW
|
||||
0209 BOF
|
||||
020B INDEX_B3+
|
||||
0218 NAME
|
||||
0221 ARRAY
|
||||
0223 EXTERNNAME_B3-4
|
||||
0225 DEFAULTROWHEIGHT
|
||||
0231 FONT_B3B4
|
||||
0236 TABLEOP
|
||||
023E WINDOW2
|
||||
0243 XF_B3
|
||||
027E RK
|
||||
0293 STYLE
|
||||
0406 FORMULA_B4
|
||||
0409 BOF
|
||||
041E FORMAT
|
||||
0443 XF_B4
|
||||
04BC SHRFMLA
|
||||
0800 QUICKTIP
|
||||
0809 BOF
|
||||
0862 SHEETLAYOUT
|
||||
0867 SHEETPROTECTION
|
||||
0868 RANGEPROTECTION
|
||||
"""
|
||||
|
||||
biff_rec_name_dict = {}
|
||||
for _buff in _brecstrg.splitlines():
|
||||
_numh, _name = _buff.split()
|
||||
biff_rec_name_dict[int(_numh, 16)] = _name
|
||||
del _buff, _name, _brecstrg
|
||||
|
||||
def hex_char_dump(strg, ofs, dlen, base=0, fout=sys.stdout, unnumbered=False):
|
||||
endpos = min(ofs + dlen, len(strg))
|
||||
pos = ofs
|
||||
numbered = not unnumbered
|
||||
num_prefix = ''
|
||||
while pos < endpos:
|
||||
endsub = min(pos + 16, endpos)
|
||||
substrg = strg[pos:endsub]
|
||||
lensub = endsub - pos
|
||||
if lensub <= 0 or lensub != len(substrg):
|
||||
fprintf(
|
||||
sys.stdout,
|
||||
'??? hex_char_dump: ofs=%d dlen=%d base=%d -> endpos=%d pos=%d endsub=%d substrg=%r\n',
|
||||
ofs, dlen, base, endpos, pos, endsub, substrg)
|
||||
break
|
||||
hexd = ''.join(["%02x " % ord(c) for c in substrg])
|
||||
chard = ''
|
||||
for c in substrg:
|
||||
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('<HH', mem[pos:pos+4])
|
||||
if rc == 0 and length == 0:
|
||||
if mem[pos:] == '\0' * (stream_end - pos):
|
||||
dummies = stream_end - pos
|
||||
savpos = pos
|
||||
pos = stream_end
|
||||
break
|
||||
if dummies:
|
||||
dummies += 4
|
||||
else:
|
||||
savpos = pos
|
||||
dummies = 4
|
||||
pos += 4
|
||||
else:
|
||||
if dummies:
|
||||
if numbered:
|
||||
num_prefix = "%5d: " % (adj + savpos)
|
||||
fprintf(fout, "%s---- %d zero bytes skipped ----\n", num_prefix, dummies)
|
||||
dummies = 0
|
||||
recname = biff_rec_name_dict.get(rc, '<UNKNOWN>')
|
||||
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('<HH', mem[pos:pos+4])
|
||||
if rc == 0 and length == 0:
|
||||
if mem[pos:] == '\0' * (stream_end - pos):
|
||||
break
|
||||
recname = "<Dummy (zero)>"
|
||||
else:
|
||||
recname = biff_rec_name_dict.get(rc, None)
|
||||
if recname is None:
|
||||
recname = "Unknown_0x%04X" % rc
|
||||
if tally.has_key(recname):
|
||||
tally[recname] += 1
|
||||
else:
|
||||
tally[recname] = 1
|
||||
pos += length + 4
|
||||
slist = tally.items()
|
||||
slist.sort()
|
||||
for recname, count in slist:
|
||||
print >> fout, "%8d %s" % (count, recname)
|
||||
|
||||
encoding_from_codepage = {
|
||||
1200 : 'utf_16_le',
|
||||
10000: 'mac_roman',
|
||||
10006: 'mac_greek', # guess
|
||||
10007: 'mac_cyrillic', # guess
|
||||
10029: 'mac_latin2', # guess
|
||||
10079: 'mac_iceland', # guess
|
||||
10081: 'mac_turkish', # guess
|
||||
32768: 'mac_roman',
|
||||
32769: 'cp1252',
|
||||
}
|
||||
# some more guessing, for Indic scripts
|
||||
# codepage 57000 range:
|
||||
# 2 Devanagari [0]
|
||||
# 3 Bengali [1]
|
||||
# 4 Tamil [5]
|
||||
# 5 Telegu [6]
|
||||
# 6 Assamese [1] c.f. Bengali
|
||||
# 7 Oriya [4]
|
||||
# 8 Kannada [7]
|
||||
# 9 Malayalam [8]
|
||||
# 10 Gujarati [3]
|
||||
# 11 Gurmukhi [2]
|
||||
@@ -1,358 +0,0 @@
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
##
|
||||
# Implements the minimal functionality required
|
||||
# to extract a "Workbook" or "Book" stream (as one big string)
|
||||
# from an OLE2 Compound Document file.
|
||||
# <p>Copyright © 2005-2008 Stephen John Machin, Lingfo Pty Ltd</p>
|
||||
# <p>This module is part of the xlrd package, which is released under a BSD-style licence.</p>
|
||||
##
|
||||
|
||||
# No part of the content of this file was derived from the works of David Giffin.
|
||||
|
||||
# 2008-11-04 SJM Avoid assertion error when -1 used instead of -2 for first_SID of empty SCSS [Frank Hoffsuemmer]
|
||||
# 2007-09-08 SJM Warning message if sector sizes are extremely large.
|
||||
# 2007-05-07 SJM Meaningful exception instead of IndexError if a SAT (sector allocation table) is corrupted.
|
||||
# 2007-04-22 SJM Missing "<" in a struct.unpack call => can't open files on bigendian platforms.
|
||||
|
||||
|
||||
import sys
|
||||
from struct import unpack
|
||||
from timemachine import *
|
||||
|
||||
##
|
||||
# Magic cookie that should appear in the first 8 bytes of the file.
|
||||
SIGNATURE = "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
|
||||
|
||||
EOCSID = -2
|
||||
FREESID = -1
|
||||
SATSID = -3
|
||||
MSATSID = -4
|
||||
|
||||
class CompDocError(Exception):
|
||||
pass
|
||||
|
||||
class DirNode(object):
|
||||
|
||||
def __init__(self, DID, dent, DEBUG=0):
|
||||
# dent is the 128-byte directory entry
|
||||
self.DID = DID
|
||||
# (cbufsize, self.etype, self.colour, self.left_DID, self.right_DID,
|
||||
# self.root_DID,
|
||||
# self.first_SID,
|
||||
# self.tot_size) = \
|
||||
# unpack('<HBBiii16x4x8x8xii4x', dent[64:128])
|
||||
(cbufsize, self.etype, self.colour, self.left_DID, self.right_DID,
|
||||
self.root_DID) = \
|
||||
unpack('<HBBiii', dent[64:80])
|
||||
(self.first_SID, self.tot_size) = \
|
||||
unpack('<ii', dent[116:124])
|
||||
if cbufsize == 0:
|
||||
self.name = u''
|
||||
else:
|
||||
self.name = unicode(dent[0:cbufsize-2], 'utf_16_le') # omit the trailing U+0000
|
||||
self.children = [] # filled in later
|
||||
self.parent = -1 # indicates orphan; fixed up later
|
||||
self.tsinfo = unpack('<IIII', dent[100:116])
|
||||
if DEBUG:
|
||||
self.dump(DEBUG)
|
||||
|
||||
def dump(self, DEBUG=1):
|
||||
print "DID=%d name=%r etype=%d DIDs(left=%d right=%d root=%d parent=%d kids=%r) first_SID=%d tot_size=%d" \
|
||||
% (self.DID, self.name, self.etype, self.left_DID,
|
||||
self.right_DID, self.root_DID, self.parent, self.children, self.first_SID, self.tot_size)
|
||||
if DEBUG == 2:
|
||||
# cre_lo, cre_hi, mod_lo, mod_hi = tsinfo
|
||||
print "timestamp info", self.tsinfo
|
||||
|
||||
def _build_family_tree(dirlist, parent_DID, child_DID):
|
||||
if child_DID < 0: return
|
||||
_build_family_tree(dirlist, parent_DID, dirlist[child_DID].left_DID)
|
||||
dirlist[parent_DID].children.append(child_DID)
|
||||
dirlist[child_DID].parent = parent_DID
|
||||
_build_family_tree(dirlist, parent_DID, dirlist[child_DID].right_DID)
|
||||
if dirlist[child_DID].etype == 1: # storage
|
||||
_build_family_tree(dirlist, child_DID, dirlist[child_DID].root_DID)
|
||||
|
||||
##
|
||||
# Compound document handler.
|
||||
# @param mem The raw contents of the file, as a string, or as an mmap.mmap() object. The
|
||||
# only operation it needs to support is slicing.
|
||||
|
||||
class CompDoc(object):
|
||||
|
||||
def __init__(self, mem, logfile=sys.stdout, DEBUG=0):
|
||||
self.logfile = logfile
|
||||
if mem[0:8] != SIGNATURE:
|
||||
raise CompDocError('Not an OLE2 compound document')
|
||||
if mem[28:30] != '\xFE\xFF':
|
||||
raise CompDocError('Expected "little-endian" marker, found %r' % mem[28:30])
|
||||
revision, version = unpack('<HH', mem[24:28])
|
||||
if DEBUG:
|
||||
print >> logfile, "\nCompDoc format: version=0x%04x revision=0x%04x" % (version, revision)
|
||||
self.mem = mem
|
||||
ssz, sssz = unpack('<HH', mem[30:34])
|
||||
if ssz > 20: # allows for 2**20 bytes i.e. 1MB
|
||||
print >> logfile, \
|
||||
"WARNING: sector size (2**%d) is preposterous; assuming 512 and continuing ..." \
|
||||
% ssz
|
||||
ssz = 9
|
||||
if sssz > ssz:
|
||||
print >> logfile, \
|
||||
"WARNING: short stream sector size (2**%d) is preposterous; assuming 64 and continuing ..." \
|
||||
% sssz
|
||||
sssz = 6
|
||||
self.sec_size = sec_size = 1 << ssz
|
||||
self.short_sec_size = 1 << sssz
|
||||
(
|
||||
SAT_tot_secs, self.dir_first_sec_sid, _unused, self.min_size_std_stream,
|
||||
SSAT_first_sec_sid, SSAT_tot_secs,
|
||||
MSAT_first_sec_sid, MSAT_tot_secs,
|
||||
# ) = unpack('<ii4xiiiii', mem[44:76])
|
||||
) = unpack('<iiiiiiii', mem[44:76])
|
||||
mem_data_len = len(mem) - 512
|
||||
mem_data_secs, left_over = divmod(mem_data_len, sec_size)
|
||||
if left_over:
|
||||
#### raise CompDocError("Not a whole number of sectors")
|
||||
print >> logfile, \
|
||||
"WARNING *** file size (%d) not 512 + multiple of sector size (%d)" \
|
||||
% (len(mem), sec_size)
|
||||
if DEBUG:
|
||||
print >> logfile, 'sec sizes', ssz, sssz, sec_size, self.short_sec_size
|
||||
print >> logfile, "mem data: %d bytes == %d sectors" % (mem_data_len, mem_data_secs)
|
||||
print >> logfile, "SAT_tot_secs=%d, dir_first_sec_sid=%d, min_size_std_stream=%d" \
|
||||
% (SAT_tot_secs, self.dir_first_sec_sid, self.min_size_std_stream,)
|
||||
print >> logfile, "SSAT_first_sec_sid=%d, SSAT_tot_secs=%d" % (SSAT_first_sec_sid, SSAT_tot_secs,)
|
||||
print >> logfile, "MSAT_first_sec_sid=%d, MSAT_tot_secs=%d" % (MSAT_first_sec_sid, MSAT_tot_secs,)
|
||||
nent = int_floor_div(sec_size, 4) # number of SID entries in a sector
|
||||
fmt = "<%di" % nent
|
||||
trunc_warned = 0
|
||||
#
|
||||
# === build the MSAT ===
|
||||
#
|
||||
MSAT = list(unpack('<109i', mem[76:512]))
|
||||
sid = MSAT_first_sec_sid
|
||||
while sid >= 0:
|
||||
if sid >= mem_data_secs:
|
||||
raise CompDocError(
|
||||
"MSAT extension: accessing sector %d but only %d in file" % (sid, mem_data_secs)
|
||||
)
|
||||
offset = 512 + sec_size * sid
|
||||
news = list(unpack(fmt, mem[offset:offset+sec_size]))
|
||||
sid = news.pop()
|
||||
MSAT.extend(news)
|
||||
if DEBUG:
|
||||
print >> logfile, "MSAT: len =", len(MSAT)
|
||||
print >> logfile, MSAT
|
||||
#
|
||||
# === build the SAT ===
|
||||
#
|
||||
self.SAT = []
|
||||
for msid in MSAT:
|
||||
if msid == FREESID: continue
|
||||
if msid >= mem_data_secs:
|
||||
if not trunc_warned:
|
||||
print >> logfile, "WARNING *** File is truncated, or OLE2 MSAT is corrupt!!"
|
||||
print >> logfile, \
|
||||
"INFO: Trying to access sector %d but only %d available" \
|
||||
% (msid, mem_data_secs)
|
||||
trunc_warned = 1
|
||||
continue
|
||||
offset = 512 + sec_size * msid
|
||||
news = list(unpack(fmt, mem[offset:offset+sec_size]))
|
||||
self.SAT.extend(news)
|
||||
if DEBUG:
|
||||
print >> logfile, "SAT: len =", len(self.SAT)
|
||||
print >> logfile, self.SAT
|
||||
# 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
|
||||
|
||||
# === build the directory ===
|
||||
#
|
||||
dbytes = self._get_stream(
|
||||
self.mem, 512, self.SAT, self.sec_size, self.dir_first_sec_sid,
|
||||
name="directory")
|
||||
dirlist = []
|
||||
did = -1
|
||||
for pos in xrange(0, len(dbytes), 128):
|
||||
did += 1
|
||||
dirlist.append(DirNode(did, dbytes[pos:pos+128], 0))
|
||||
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 and 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")
|
||||
# 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 >> logfile, \
|
||||
"WARNING *** OLE2 inconsistency: SSCS size is 0 but SSAT size is non-zero"
|
||||
if sscs_dir.tot_size > 0:
|
||||
sid = SSAT_first_sec_sid
|
||||
nsecs = SSAT_tot_secs
|
||||
while sid >= 0 and nsecs > 0:
|
||||
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]
|
||||
# assert SSAT_tot_secs == 0 or sid == EOCSID
|
||||
if DEBUG: print >> logfile, "SSAT last sid %d; remaining sectors %d" % (sid, nsecs)
|
||||
assert nsecs == 0 and sid == EOCSID
|
||||
if DEBUG: print >> logfile, "SSAT", self.SSAT
|
||||
|
||||
def _get_stream(self, mem, base, sat, sec_size, start_sid, size=None, name=''):
|
||||
# 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:
|
||||
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:
|
||||
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:
|
||||
print >> self.logfile, \
|
||||
"WARNING *** OLE2 stream %r: expected size %d, actual size %d" \
|
||||
% (name, size, size - todo)
|
||||
return ''.join(sectors)
|
||||
|
||||
def _dir_search(self, path, storage_DID=0):
|
||||
# Return matching DirNode instance, or None
|
||||
head = path[0]
|
||||
tail = path[1:]
|
||||
dl = self.dirlist
|
||||
for child in dl[storage_DID].children:
|
||||
if dl[child].name.lower() == head.lower():
|
||||
et = dl[child].etype
|
||||
if et == 2:
|
||||
return dl[child]
|
||||
if et == 1:
|
||||
if not tail:
|
||||
raise CompDocError("Requested component is a 'storage'")
|
||||
return self._dir_search(tail, child)
|
||||
dl[child].dump(1)
|
||||
raise CompDocError("Requested stream is not a 'user stream'")
|
||||
return None
|
||||
|
||||
##
|
||||
# Interrogate the compound document's directory; return the stream as a string if found, otherwise
|
||||
# return None.
|
||||
# @param qname Name of the desired stream e.g. u'Workbook'. Should be in Unicode or convertible thereto.
|
||||
|
||||
def get_named_stream(self, qname):
|
||||
d = self._dir_search(qname.split("/"))
|
||||
if d is None:
|
||||
return None
|
||||
if d.tot_size >= self.min_size_std_stream:
|
||||
return self._get_stream(
|
||||
self.mem, 512, self.SAT, self.sec_size, d.first_SID,
|
||||
d.tot_size, name=qname)
|
||||
else:
|
||||
return self._get_stream(
|
||||
self.SSCS, 0, self.SSAT, self.short_sec_size, d.first_SID,
|
||||
d.tot_size, name=qname + " (from SSCS)")
|
||||
|
||||
##
|
||||
# Interrogate the compound document's directory.
|
||||
# If the named stream is not found, (None, 0, 0) will be returned.
|
||||
# If the named stream is found and is contiguous within the original byte sequence ("mem")
|
||||
# used when the document was opened,
|
||||
# then (mem, offset_to_start_of_stream, length_of_stream) is returned.
|
||||
# Otherwise a new string is built from the fragments and (new_string, 0, length_of_stream) is returned.
|
||||
# @param qname Name of the desired stream e.g. u'Workbook'. Should be in Unicode or convertible thereto.
|
||||
|
||||
def locate_named_stream(self, qname):
|
||||
d = self._dir_search(qname.split("/"))
|
||||
if d is None:
|
||||
return (None, 0, 0)
|
||||
if d.tot_size >= self.min_size_std_stream:
|
||||
return self._locate_stream(self.mem, 512, self.SAT, self.sec_size, d.first_SID, d.tot_size)
|
||||
else:
|
||||
return (
|
||||
self._get_stream(
|
||||
self.SSCS, 0, self.SSAT, self.short_sec_size, d.first_SID,
|
||||
d.tot_size, qname + " (from SSCS)"),
|
||||
0,
|
||||
d.tot_size
|
||||
)
|
||||
return (None, 0, 0) # not found
|
||||
|
||||
def _locate_stream(self, mem, base, sat, sec_size, start_sid, size):
|
||||
# print >> self.logfile, "_locate_stream", base, sec_size, start_sid, 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 = []
|
||||
while s >= 0:
|
||||
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
|
||||
# print >> self.logfile, len(slices) + 1, "slices"
|
||||
if not slices:
|
||||
# The stream is contiguous ... just what we like!
|
||||
return (mem, start_pos, size)
|
||||
slices.append((start_pos, end_pos))
|
||||
return (''.join([mem[start_pos:end_pos] for start_pos, end_pos in slices]), 0, size)
|
||||
|
||||
# ==========================================================================================
|
||||
@@ -1,69 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=us-ascii' />
|
||||
<title>The compdoc Module</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>The compdoc Module</h1>
|
||||
<p>Implements the minimal functionality required
|
||||
to extract a "Workbook" or "Book" stream (as one big string)
|
||||
from an OLE2 Compound Document file.
|
||||
</p><p>Copyright © 2005-2008 Stephen John Machin, Lingfo Pty Ltd</p>
|
||||
<p>This module is part of the xlrd package, which is released under a BSD-style licence.</p>
|
||||
<h2>Module Contents</h2>
|
||||
<dl>
|
||||
<dt><b>CompDoc(mem, logfile=sys.stdout, DEBUG=0)</b> (class) [<a href='#compdoc.CompDoc-class'>#</a>]</dt>
|
||||
<dd>
|
||||
<p>Compound document handler.</p>
|
||||
<dl>
|
||||
<dt><i>mem</i></dt>
|
||||
<dd>
|
||||
The raw contents of the file, as a string, or as an mmap.mmap() object. The
|
||||
only operation it needs to support is slicing.</dd>
|
||||
</dl><br />
|
||||
<p>For more information about this class, see <a href='#compdoc.CompDoc-class'><i>The CompDoc Class</i></a>.</p>
|
||||
</dd>
|
||||
<dt><a id='compdoc.SIGNATURE-variable' name='compdoc.SIGNATURE-variable'><b>SIGNATURE</b></a> (variable) [<a href='#compdoc.SIGNATURE-variable'>#</a>]</dt>
|
||||
<dd>
|
||||
<p>Magic cookie that should appear in the first 8 bytes of the file.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
<h2><a id='compdoc.CompDoc-class' name='compdoc.CompDoc-class'>The CompDoc Class</a></h2>
|
||||
<dl>
|
||||
<dt><b>CompDoc(mem, logfile=sys.stdout, DEBUG=0)</b> (class) [<a href='#compdoc.CompDoc-class'>#</a>]</dt>
|
||||
<dd>
|
||||
<p>Compound document handler.</p>
|
||||
<dl>
|
||||
<dt><i>mem</i></dt>
|
||||
<dd>
|
||||
The raw contents of the file, as a string, or as an mmap.mmap() object. The
|
||||
only operation it needs to support is slicing.</dd>
|
||||
</dl><br />
|
||||
</dd>
|
||||
<dt><a id='compdoc.CompDoc.get_named_stream-method' name='compdoc.CompDoc.get_named_stream-method'><b>get_named_stream(qname)</b></a> [<a href='#compdoc.CompDoc.get_named_stream-method'>#</a>]</dt>
|
||||
<dd>
|
||||
<p>Interrogate the compound document's directory; return the stream as a string if found, otherwise
|
||||
return None.</p>
|
||||
<dl>
|
||||
<dt><i>qname</i></dt>
|
||||
<dd>
|
||||
Name of the desired stream e.g. u'Workbook'. Should be in Unicode or convertible thereto.</dd>
|
||||
</dl><br />
|
||||
</dd>
|
||||
<dt><a id='compdoc.CompDoc.locate_named_stream-method' name='compdoc.CompDoc.locate_named_stream-method'><b>locate_named_stream(qname)</b></a> [<a href='#compdoc.CompDoc.locate_named_stream-method'>#</a>]</dt>
|
||||
<dd>
|
||||
<p>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.</p>
|
||||
<dl>
|
||||
<dt><i>qname</i></dt>
|
||||
<dd>
|
||||
Name of the desired stream e.g. u'Workbook'. Should be in Unicode or convertible thereto.</dd>
|
||||
</dl><br />
|
||||
</dd>
|
||||
</dl>
|
||||
</body></html>
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,178 +0,0 @@
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
##
|
||||
# Module/script example of the xlrd API for extracting information
|
||||
# about named references, named constants, etc.
|
||||
#
|
||||
# <p>Copyright © 2006 Stephen John Machin, Lingfo Pty Ltd</p>
|
||||
# <p>This module is part of the xlrd package, which is released under a BSD-style licence.</p>
|
||||
##
|
||||
|
||||
import xlrd
|
||||
import sys
|
||||
import glob
|
||||
|
||||
def scope_as_string(book, scope):
|
||||
if 0 <= scope < book.nsheets:
|
||||
return "sheet #%d (%r)" % (scope, book.sheet_names()[scope])
|
||||
if scope == -1:
|
||||
return "Global"
|
||||
if scope == -2:
|
||||
return "Macro/VBA"
|
||||
return "Unknown scope value (%r)" % 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 >> f, "%r => %d" % (scope_strg, qscope)
|
||||
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 >> f, "%r: unknown name" % name
|
||||
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 >> f, "%r => %d" % (scope_strg, scope)
|
||||
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 >> f, "Name %r not found in scope %d" % (name, scope)
|
||||
if scope == -1:
|
||||
return
|
||||
scope = -1 # Try again with global scope
|
||||
print >> f, "Name %r found in scope %d" % (name, scope)
|
||||
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:
|
||||
e1, e2 = sys.exc_info()[:2]
|
||||
showval = "%s:%s" % (e1.__name__, e2)
|
||||
elif celltype == xlrd.XL_CELL_ERROR:
|
||||
showval = xlrd.error_text_from_code.get(
|
||||
cellvalue, '<Unknown error code 0x%02x>' % cellvalue)
|
||||
else:
|
||||
showval = cellvalue
|
||||
return showval
|
||||
|
||||
def show_name_object(book, nobj, show_contents=0, f=sys.stdout):
|
||||
print >> f, "\nName: %r, scope: %r (%s)" \
|
||||
% (nobj.name, nobj.scope, scope_as_string(book, nobj.scope))
|
||||
res = nobj.result
|
||||
print >> f, "Formula eval result: %r" % res
|
||||
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 xrange(len(value)):
|
||||
ref3d = value[i]
|
||||
print >> f, "Range %d: %r ==> %s"% (i, ref3d.coords, xlrd.rangename3drel(book, ref3d))
|
||||
elif kind == xlrd.oREF:
|
||||
# A list of Ref3D objects
|
||||
for i in xrange(len(value)):
|
||||
ref3d = value[i]
|
||||
print >> f, "Range %d: %r ==> %s"% (i, ref3d.coords, xlrd.rangename3d(book, ref3d))
|
||||
if not show_contents:
|
||||
continue
|
||||
datemode = book.datemode
|
||||
for shx in xrange(ref3d.shtxlo, ref3d.shtxhi):
|
||||
sh = book.sheet_by_index(shx)
|
||||
print >> f, " Sheet #%d (%s)" % (shx, sh.name)
|
||||
rowlim = min(ref3d.rowxhi, sh.nrows)
|
||||
collim = min(ref3d.colxhi, sh.ncols)
|
||||
for rowx in xrange(ref3d.rowxlo, rowlim):
|
||||
for colx in xrange(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 >> f, " (%3d,%3d) %-5s: %r" \
|
||||
% (rowx, colx, xlrd.cellname(rowx, colx), sval)
|
||||
|
||||
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)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,77 +0,0 @@
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
Portions copyright © 2005-2009, Stephen John Machin, Lingfo Pty Ltd
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. None of the names of Stephen John Machin, Lingfo Pty Ltd and any
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
|
||||
"""
|
||||
/*-
|
||||
* Copyright (c) 2001 David Giffin.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Based on the the Java version: Andrew Khan Copyright (c) 2000.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgment:
|
||||
* "This product includes software developed by
|
||||
* David Giffin <david@giffin.org>."
|
||||
*
|
||||
* 4. Redistributions of any form whatsoever must retain the following
|
||||
* acknowledgment:
|
||||
* "This product includes software developed by
|
||||
* David Giffin <david@giffin.org>."
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
"""
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
##
|
||||
# <p>Copyright © 2006-2008 Stephen John Machin, Lingfo Pty Ltd</p>
|
||||
# <p>This module is part of the xlrd package, which is released under a BSD-style licence.</p>
|
||||
##
|
||||
|
||||
# timemachine.py -- adaptation for earlier Pythons e.g. 2.1
|
||||
# usage: from timemachine import *
|
||||
|
||||
# 2008-02-08 SJM Generalised method of detecting IronPython
|
||||
|
||||
import sys
|
||||
|
||||
python_version = sys.version_info[:2] # e.g. version 2.4 -> (2, 4)
|
||||
|
||||
CAN_PICKLE_ARRAY = python_version >= (2, 5)
|
||||
CAN_SUBCLASS_BUILTIN = python_version >= (2, 2)
|
||||
|
||||
if sys.version.find("IronPython") >= 0:
|
||||
array_array = None
|
||||
else:
|
||||
from array import array as array_array
|
||||
|
||||
if python_version < (2, 2):
|
||||
class object:
|
||||
pass
|
||||
False = 0
|
||||
True = 1
|
||||
|
||||
def int_floor_div(x, y):
|
||||
return divmod(x, y)[0]
|
||||
|
||||
def intbool(x):
|
||||
if x:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
if python_version < (2, 3):
|
||||
def sum(sequence, start=0):
|
||||
tot = start
|
||||
for item in aseq:
|
||||
tot += item
|
||||
return tot
|
||||
@@ -1,171 +0,0 @@
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
# No part of the content of this file was derived from the works of David Giffin.
|
||||
|
||||
##
|
||||
# <p>Copyright © 2005-2008 Stephen John Machin, Lingfo Pty Ltd</p>
|
||||
# <p>This module is part of the xlrd package, which is released under a BSD-style licence.</p>
|
||||
#
|
||||
# <p>Provides function(s) for dealing with Microsoft Excel ™ dates.</p>
|
||||
##
|
||||
|
||||
# 2008-10-18 SJM Fix bug in xldate_from_date_tuple (affected some years after 2099)
|
||||
|
||||
# The conversion from days to (year, month, day) starts with
|
||||
# an integral "julian day number" aka JDN.
|
||||
# FWIW, JDN 0 corresponds to noon on Monday November 24 in Gregorian year -4713.
|
||||
# More importantly:
|
||||
# Noon on Gregorian 1900-03-01 (day 61 in the 1900-based system) is JDN 2415080.0
|
||||
# Noon on Gregorian 1904-01-02 (day 1 in the 1904-based system) is JDN 2416482.0
|
||||
|
||||
from timemachine import int_floor_div as ifd
|
||||
|
||||
_JDN_delta = (2415080 - 61, 2416482 - 1)
|
||||
assert _JDN_delta[1] - _JDN_delta[0] == 1462
|
||||
|
||||
class XLDateError(ValueError): pass
|
||||
|
||||
class XLDateNegative(XLDateError): pass
|
||||
class XLDateAmbiguous(XLDateError): pass
|
||||
class XLDateTooLarge(XLDateError): pass
|
||||
class XLDateBadDatemode(XLDateError): pass
|
||||
class XLDateBadTuple(XLDateError): pass
|
||||
|
||||
_XLDAYS_TOO_LARGE = (2958466, 2958466 - 1462) # This is equivalent to 10000-01-01
|
||||
|
||||
##
|
||||
# Convert an Excel number (presumed to represent a date, a datetime or a time) into
|
||||
# a tuple suitable for feeding to datetime or mx.DateTime constructors.
|
||||
# @param xldate The Excel number
|
||||
# @param datemode 0: 1900-based, 1: 1904-based.
|
||||
# <br>WARNING: when using this function to
|
||||
# interpret the contents of a workbook, you should pass in the Book.datemode
|
||||
# attribute of that workbook. Whether
|
||||
# the workbook has ever been anywhere near a Macintosh is irrelevant.
|
||||
# @return Gregorian (year, month, day, hour, minute, nearest_second).
|
||||
# <br>Special case: if 0.0 <= xldate < 1.0, it is assumed to represent a time;
|
||||
# (0, 0, 0, hour, minute, second) will be returned.
|
||||
# <br>Note: 1904-01-01 is not regarded as a valid date in the datemode 1 system; its "serial number"
|
||||
# is zero.
|
||||
# @throws XLDateNegative xldate < 0.00
|
||||
# @throws XLDateAmbiguous The 1900 leap-year problem (datemode == 0 and 1.0 <= xldate < 61.0)
|
||||
# @throws XLDateTooLarge Gregorian year 10000 or later
|
||||
# @throws XLDateBadDatemode datemode arg is neither 0 nor 1
|
||||
# @throws XLDateError Covers the 4 specific errors
|
||||
|
||||
def xldate_as_tuple(xldate, datemode):
|
||||
if datemode not in (0, 1):
|
||||
raise XLDateBadDatemode(datemode)
|
||||
if xldate == 0.00:
|
||||
return (0, 0, 0, 0, 0, 0)
|
||||
if xldate < 0.00:
|
||||
raise XLDateNegative(xldate)
|
||||
xldays = int(xldate)
|
||||
frac = xldate - xldays
|
||||
seconds = int(round(frac * 86400.0))
|
||||
assert 0 <= seconds <= 86400
|
||||
if seconds == 86400:
|
||||
hour = minute = second = 0
|
||||
xldays += 1
|
||||
else:
|
||||
# second = seconds % 60; minutes = seconds // 60
|
||||
minutes, second = divmod(seconds, 60)
|
||||
# minute = minutes % 60; hour = minutes // 60
|
||||
hour, minute = divmod(minutes, 60)
|
||||
if xldays >= _XLDAYS_TOO_LARGE[datemode]:
|
||||
raise XLDateTooLarge(xldate)
|
||||
|
||||
if xldays == 0:
|
||||
return (0, 0, 0, hour, minute, second)
|
||||
|
||||
if xldays < 61 and datemode == 0:
|
||||
raise XLDateAmbiguous(xldate)
|
||||
|
||||
jdn = xldays + _JDN_delta[datemode]
|
||||
yreg = (ifd(ifd(jdn * 4 + 274277, 146097) * 3, 4) + jdn + 1363) * 4 + 3
|
||||
mp = ifd(yreg % 1461, 4) * 535 + 333
|
||||
d = ifd(mp % 16384, 535) + 1
|
||||
# mp /= 16384
|
||||
mp >>= 14
|
||||
if mp >= 10:
|
||||
return (ifd(yreg, 1461) - 4715, mp - 9, d, hour, minute, second)
|
||||
else:
|
||||
return (ifd(yreg, 1461) - 4716, mp + 3, d, hour, minute, second)
|
||||
|
||||
# === conversions from date/time to xl numbers
|
||||
|
||||
def _leap(y):
|
||||
if y % 4: return 0
|
||||
if y % 100: return 1
|
||||
if y % 400: return 0
|
||||
return 1
|
||||
|
||||
_days_in_month = (None, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
||||
|
||||
##
|
||||
# Convert a date tuple (year, month, day) to an Excel date.
|
||||
# @param year Gregorian year.
|
||||
# @param month 1 <= month <= 12
|
||||
# @param day 1 <= day <= last day of that (year, month)
|
||||
# @param datemode 0: 1900-based, 1: 1904-based.
|
||||
# @throws XLDateAmbiguous The 1900 leap-year problem (datemode == 0 and 1.0 <= xldate < 61.0)
|
||||
# @throws XLDateBadDatemode datemode arg is neither 0 nor 1
|
||||
# @throws XLDateBadTuple (year, month, day) is too early/late or has invalid component(s)
|
||||
# @throws XLDateError Covers the specific errors
|
||||
|
||||
def xldate_from_date_tuple((year, month, day), datemode):
|
||||
|
||||
if datemode not in (0, 1):
|
||||
raise XLDateBadDatemode(datemode)
|
||||
|
||||
if year == 0 and month == 0 and day == 0:
|
||||
return 0.00
|
||||
|
||||
if not (1900 <= year <= 9999):
|
||||
raise XLDateBadTuple("Invalid year: %r" % ((year, month, day),))
|
||||
if not (1 <= month <= 12):
|
||||
raise XLDateBadTuple("Invalid month: %r" % ((year, month, day),))
|
||||
if day < 1 \
|
||||
or (day > _days_in_month[month] and not(day == 29 and month == 2 and _leap(year))):
|
||||
raise XLDateBadTuple("Invalid day: %r" % ((year, month, day),))
|
||||
|
||||
Yp = year + 4716
|
||||
M = month
|
||||
if M <= 2:
|
||||
Yp = Yp - 1
|
||||
Mp = M + 9
|
||||
else:
|
||||
Mp = M - 3
|
||||
jdn = ifd(1461 * Yp, 4) + ifd(979 * Mp + 16, 32) + \
|
||||
day - 1364 - ifd(ifd(Yp + 184, 100) * 3, 4)
|
||||
xldays = jdn - _JDN_delta[datemode]
|
||||
if xldays <= 0:
|
||||
raise XLDateBadTuple("Invalid (year, month, day): %r" % ((year, month, day),))
|
||||
if xldays < 61 and datemode == 0:
|
||||
raise XLDateAmbiguous("Before 1900-03-01: %r" % ((year, month, day),))
|
||||
return float(xldays)
|
||||
|
||||
##
|
||||
# Convert a time tuple (hour, minute, second) to an Excel "date" value (fraction of a day).
|
||||
# @param hour 0 <= hour < 24
|
||||
# @param minute 0 <= minute < 60
|
||||
# @param second 0 <= second < 60
|
||||
# @throws XLDateBadTuple Out-of-range hour, minute, or second
|
||||
|
||||
def xldate_from_time_tuple((hour, minute, second)):
|
||||
if 0 <= hour < 24 and 0 <= minute < 60 and 0 <= second < 60:
|
||||
return ((second / 60.0 + minute) / 60.0 + hour) / 24.0
|
||||
raise XLDateBadTuple("Invalid (hour, minute, second): %r" % ((hour, minute, second),))
|
||||
|
||||
##
|
||||
# Convert a datetime tuple (year, month, day, hour, minute, second) to an Excel date value.
|
||||
# For more details, refer to other xldate_from_*_tuple functions.
|
||||
# @param datetime_tuple (year, month, day, hour, minute, second)
|
||||
# @param datemode 0: 1900-based, 1: 1904-based.
|
||||
|
||||
def xldate_from_datetime_tuple(datetime_tuple, datemode):
|
||||
return (
|
||||
xldate_from_date_tuple(datetime_tuple[:3], datemode)
|
||||
+
|
||||
xldate_from_time_tuple(datetime_tuple[3:])
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,620 +0,0 @@
|
||||
# Support module for the xlrd3 package.
|
||||
#
|
||||
# Portions copyright (c) 2005-2008 Stephen John Machin, Lingfo Pty Ltd
|
||||
# This module is part of the xlrd package, which is released under a
|
||||
# BSD-style licence.
|
||||
#
|
||||
# 2010-12-08 mozman refactoring for python 3
|
||||
# 2008-02-10 SJM BIFF2 BLANK record
|
||||
# 2008-02-08 SJM Preparation for Excel 2.0 support
|
||||
# 2008-02-02 SJM Added suffixes (_B2, _B2_ONLY, etc) on record names for
|
||||
# biff_dump & biff_count
|
||||
# 2007-12-04 SJM Added support for Excel 2.x (BIFF2) files.
|
||||
# 2007-09-08 SJM Avoid crash when zero-length Unicode string missing options byte.
|
||||
# 2007-04-22 SJM Remove experimental "trimming" facility.
|
||||
|
||||
import sys
|
||||
from struct import unpack
|
||||
|
||||
encoding_from_codepage = {
|
||||
1200 : 'utf_16_le',
|
||||
10000: 'mac_roman',
|
||||
10006: 'mac_greek', # guess
|
||||
10007: 'mac_cyrillic', # guess
|
||||
10029: 'mac_latin2', # guess
|
||||
10079: 'mac_iceland', # guess
|
||||
10081: 'mac_turkish', # guess
|
||||
32768: 'mac_roman',
|
||||
32769: 'cp1252',
|
||||
}
|
||||
|
||||
# some more guessing, for Indic scripts
|
||||
# codepage 57000 range:
|
||||
# 2 Devanagari [0]
|
||||
# 3 Bengali [1]
|
||||
# 4 Tamil [5]
|
||||
# 5 Telegu [6]
|
||||
# 6 Assamese [1] c.f. Bengali
|
||||
# 7 Oriya [4]
|
||||
# 8 Kannada [7]
|
||||
# 9 Malayalam [8]
|
||||
# 10 Gujarati [3]
|
||||
# 11 Gurmukhi [2]
|
||||
|
||||
FUN = 0 # unknown
|
||||
FDT = 1 # date
|
||||
FNU = 2 # number
|
||||
FGE = 3 # general
|
||||
FTX = 4 # text
|
||||
|
||||
DATEFORMAT = FDT
|
||||
NUMBERFORMAT = FNU
|
||||
|
||||
XL_CELL_EMPTY = 0
|
||||
XL_CELL_TEXT = 1
|
||||
XL_CELL_NUMBER = 2
|
||||
XL_CELL_DATE = 3
|
||||
XL_CELL_BOOLEAN = 4
|
||||
XL_CELL_ERROR = 5
|
||||
XL_CELL_BLANK = 6 # for use in debugging, gathering stats, etc
|
||||
|
||||
biff_text_from_num = {
|
||||
0: "(not BIFF)",
|
||||
20: "2.0",
|
||||
21: "2.1",
|
||||
30: "3",
|
||||
40: "4S",
|
||||
45: "4W",
|
||||
50: "5",
|
||||
70: "7",
|
||||
80: "8",
|
||||
85: "8X",
|
||||
}
|
||||
|
||||
# This dictionary can be used to produce a text version of the internal codes
|
||||
# that Excel uses for error cells. Here are its contents:
|
||||
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_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_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_PALETTE = 0x92
|
||||
XL_RK = 0x27e
|
||||
XL_ROW = 0x208
|
||||
XL_ROW_B2 = 0x08
|
||||
XL_RSTRING = 0xd6
|
||||
XL_SHEETHDR = 0x8F # BIFF4W only
|
||||
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
|
||||
XL_TABLEOP = 0x236
|
||||
XL_TABLEOP2 = 0x37
|
||||
XL_TABLEOP_B2 = 0x36
|
||||
XL_TXO = 0x1b6
|
||||
XL_UNCALCED = 0x5e
|
||||
XL_UNKNOWN = 0xffff
|
||||
XL_WINDOW2 = 0x023E
|
||||
XL_WRITEACCESS = 0x5C
|
||||
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,
|
||||
)
|
||||
|
||||
biff_rec_name_dict = {
|
||||
0x0000: 'DIMENSIONS_B2',
|
||||
0x0001: 'BLANK_B2',
|
||||
0x0002: 'INTEGER_B2_ONLY',
|
||||
0x0003: 'NUMBER_B2',
|
||||
0x0004: 'LABEL_B2',
|
||||
0x0005: 'BOOLERR_B2',
|
||||
0x0006: 'FORMULA',
|
||||
0x0007: 'STRING_B2',
|
||||
0x0008: 'ROW_B2',
|
||||
0x0009: 'BOF_B2',
|
||||
0x000A: 'EOF',
|
||||
0x000B: 'INDEX_B2_ONLY',
|
||||
0x000C: 'CALCCOUNT',
|
||||
0x000D: 'CALCMODE',
|
||||
0x000E: 'PRECISION',
|
||||
0x000F: 'REFMODE',
|
||||
0x0010: 'DELTA',
|
||||
0x0011: 'ITERATION',
|
||||
0x0012: 'PROTECT',
|
||||
0x0013: 'PASSWORD',
|
||||
0x0014: 'HEADER',
|
||||
0x0015: 'FOOTER',
|
||||
0x0016: 'EXTERNCOUNT',
|
||||
0x0017: 'EXTERNSHEET',
|
||||
0x0018: 'NAME_B2,5+',
|
||||
0x0019: 'WINDOWPROTECT',
|
||||
0x001A: 'VERTICALPAGEBREAKS',
|
||||
0x001B: 'HORIZONTALPAGEBREAKS',
|
||||
0x001C: 'NOTE',
|
||||
0x001D: 'SELECTION',
|
||||
0x001E: 'FORMAT_B2-3',
|
||||
0x001F: 'BUILTINFMTCOUNT_B2',
|
||||
0x0020: 'COLUMNDEFAULT_B2_ONLY',
|
||||
0x0021: 'ARRAY_B2_ONLY',
|
||||
0x0022: 'DATEMODE',
|
||||
0x0023: 'EXTERNNAME',
|
||||
0x0024: 'COLWIDTH_B2_ONLY',
|
||||
0x0025: 'DEFAULTROWHEIGHT_B2_ONLY',
|
||||
0x0026: 'LEFTMARGIN',
|
||||
0x0027: 'RIGHTMARGIN',
|
||||
0x0028: 'TOPMARGIN',
|
||||
0x0029: 'BOTTOMMARGIN',
|
||||
0x002A: 'PRINTHEADERS',
|
||||
0x002B: 'PRINTGRIDLINES',
|
||||
0x002F: 'FILEPASS',
|
||||
0x0031: 'FONT',
|
||||
0x0032: 'FONT2_B2_ONLY',
|
||||
0x0036: 'TABLEOP_B2',
|
||||
0x0037: 'TABLEOP2_B2',
|
||||
0x003C: 'CONTINUE',
|
||||
0x003D: 'WINDOW1',
|
||||
0x003E: 'WINDOW2_B2',
|
||||
0x0040: 'BACKUP',
|
||||
0x0041: 'PANE',
|
||||
0x0042: 'CODEPAGE',
|
||||
0x0043: 'XF_B2',
|
||||
0x0044: 'IXFE_B2_ONLY',
|
||||
0x0045: 'EFONT_B2_ONLY',
|
||||
0x004D: 'PLS',
|
||||
0x0051: 'DCONREF',
|
||||
0x0055: 'DEFCOLWIDTH',
|
||||
0x0056: 'BUILTINFMTCOUNT_B3-4',
|
||||
0x0059: 'XCT',
|
||||
0x005A: 'CRN',
|
||||
0x005B: 'FILESHARING',
|
||||
0x005C: 'WRITEACCESS',
|
||||
0x005D: 'OBJECT',
|
||||
0x005E: 'UNCALCED',
|
||||
0x005F: 'SAVERECALC',
|
||||
0x0063: 'OBJECTPROTECT',
|
||||
0x007D: 'COLINFO',
|
||||
0x007E: 'RK2_mythical_?',
|
||||
0x0080: 'GUTS',
|
||||
0x0081: 'WSBOOL',
|
||||
0x0082: 'GRIDSET',
|
||||
0x0083: 'HCENTER',
|
||||
0x0084: 'VCENTER',
|
||||
0x0085: 'BOUNDSHEET',
|
||||
0x0086: 'WRITEPROT',
|
||||
0x008C: 'COUNTRY',
|
||||
0x008D: 'HIDEOBJ',
|
||||
0x008E: 'SHEETSOFFSET',
|
||||
0x008F: 'SHEETHDR',
|
||||
0x0090: 'SORT',
|
||||
0x0092: 'PALETTE',
|
||||
0x0099: 'STANDARDWIDTH',
|
||||
0x009B: 'FILTERMODE',
|
||||
0x009C: 'FNGROUPCOUNT',
|
||||
0x009D: 'AUTOFILTERINFO',
|
||||
0x009E: 'AUTOFILTER',
|
||||
0x00A0: 'SCL',
|
||||
0x00A1: 'SETUP',
|
||||
0x00AB: 'GCW',
|
||||
0x00BD: 'MULRK',
|
||||
0x00BE: 'MULBLANK',
|
||||
0x00C1: 'MMS',
|
||||
0x00D6: 'RSTRING',
|
||||
0x00D7: 'DBCELL',
|
||||
0x00DA: 'BOOKBOOL',
|
||||
0x00DD: 'SCENPROTECT',
|
||||
0x00E0: 'XF',
|
||||
0x00E1: 'INTERFACEHDR',
|
||||
0x00E2: 'INTERFACEEND',
|
||||
0x00E5: 'MERGEDCELLS',
|
||||
0x00E9: 'BITMAP',
|
||||
0x00EB: 'MSO_DRAWING_GROUP',
|
||||
0x00EC: 'MSO_DRAWING',
|
||||
0x00ED: 'MSO_DRAWING_SELECTION',
|
||||
0x00EF: 'PHONETIC',
|
||||
0x00FC: 'SST',
|
||||
0x00FD: 'LABELSST',
|
||||
0x00FF: 'EXTSST',
|
||||
0x013D: 'TABID',
|
||||
0x015F: 'LABELRANGES',
|
||||
0x0160: 'USESELFS',
|
||||
0x0161: 'DSF',
|
||||
0x01AE: 'SUPBOOK',
|
||||
0x01AF: 'PROTECTIONREV4',
|
||||
0x01B0: 'CONDFMT',
|
||||
0x01B1: 'CF',
|
||||
0x01B2: 'DVAL',
|
||||
0x01B6: 'TXO',
|
||||
0x01B7: 'REFRESHALL',
|
||||
0x01B8: 'HLINK',
|
||||
0x01BC: 'PASSWORDREV4',
|
||||
0x01BE: 'DV',
|
||||
0x01C0: 'XL9FILE',
|
||||
0x01C1: 'RECALCID',
|
||||
0x0200: 'DIMENSIONS',
|
||||
0x0201: 'BLANK',
|
||||
0x0203: 'NUMBER',
|
||||
0x0204: 'LABEL',
|
||||
0x0205: 'BOOLERR',
|
||||
0x0206: 'FORMULA_B3',
|
||||
0x0207: 'STRING',
|
||||
0x0208: 'ROW',
|
||||
0x0209: 'BOF',
|
||||
0x020B: 'INDEX_B3+',
|
||||
0x0218: 'NAME',
|
||||
0x0221: 'ARRAY',
|
||||
0x0223: 'EXTERNNAME_B3-4',
|
||||
0x0225: 'DEFAULTROWHEIGHT',
|
||||
0x0231: 'FONT_B3B4',
|
||||
0x0236: 'TABLEOP',
|
||||
0x023E: 'WINDOW2',
|
||||
0x0243: 'XF_B3',
|
||||
0x027E: 'RK',
|
||||
0x0293: 'STYLE',
|
||||
0x0406: 'FORMULA_B4',
|
||||
0x0409: 'BOF',
|
||||
0x041E: 'FORMAT',
|
||||
0x0443: 'XF_B4',
|
||||
0x04BC: 'SHRFMLA',
|
||||
0x0800: 'QUICKTIP',
|
||||
0x0809: 'BOF',
|
||||
0x0862: 'SHEETLAYOUT',
|
||||
0x0867: 'SHEETPROTECTION',
|
||||
0x0868: 'RANGEPROTECTION',
|
||||
}
|
||||
|
||||
class XLRDError(Exception):
|
||||
pass
|
||||
|
||||
class BaseObject:
|
||||
"""
|
||||
Parent of almost all other classes in the package. Defines a common
|
||||
'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
|
||||
pad = " " * indent
|
||||
|
||||
if header is not None:
|
||||
print(header, file=f)
|
||||
|
||||
for attr, value in sorted(self.__dict__.items()):
|
||||
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) or
|
||||
isinstance(value, dict)):
|
||||
print("%s%s: %s, len = %d" % (pad, attr, type(value), len(value)), file=f)
|
||||
else:
|
||||
print("%s%s: %r" % (pad, attr, value), file=f)
|
||||
if footer is not None:
|
||||
print(footer, file=f)
|
||||
|
||||
def fprintf(f, fmt, *vargs):
|
||||
print(fmt.rstrip('\n') % vargs, file=f)
|
||||
|
||||
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 str(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 (str(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 ""
|
||||
pos += lenlen
|
||||
options = data[pos]
|
||||
pos += 1
|
||||
|
||||
if options & 0x08: # richtext
|
||||
pos += 2
|
||||
|
||||
if options & 0x04: # phonetic
|
||||
pos += 4
|
||||
|
||||
if options & 0x01:
|
||||
# Uncompressed UTF-16-LE
|
||||
rawstrg = data[pos:pos+2*nchars]
|
||||
strg = str(rawstrg, 'utf_16_le')
|
||||
else:
|
||||
# Note: this is COMPRESSED (not ASCII!) encoding!!!
|
||||
# Merely returning the raw bytes would work OK 99.99% of the time
|
||||
# if the local codepage was cp1252 -- however this would rapidly go pear-shaped
|
||||
# for other codepages so we grit our Anglocentric teeth and return Unicode :-)
|
||||
strg = str(data[pos:pos+nchars], "latin_1")
|
||||
return strg
|
||||
|
||||
def unpack_unicode_update_pos(data, pos, lenlen=2, known_len=None):
|
||||
""" Return (unicode_strg, updated value of pos) """
|
||||
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
|
||||
|
||||
if not nchars and not data[pos:]:
|
||||
# Zero-length string with no options byte
|
||||
return ("", pos)
|
||||
|
||||
options = data[pos]
|
||||
pos += 1
|
||||
phonetic = options & 0x04
|
||||
richtext = options & 0x08
|
||||
|
||||
if richtext:
|
||||
rt = unpack('<H', data[pos:pos+2])[0]
|
||||
pos += 2
|
||||
|
||||
if phonetic:
|
||||
sz = unpack('<i', data[pos:pos+4])[0]
|
||||
pos += 4
|
||||
|
||||
if options & 0x01:
|
||||
# Uncompressed UTF-16-LE
|
||||
strg = str(data[pos:pos+2*nchars], 'utf_16_le')
|
||||
pos += 2*nchars
|
||||
else:
|
||||
# Note: this is COMPRESSED (not ASCII!) encoding!!!
|
||||
strg = str(data[pos:pos+nchars], "latin_1")
|
||||
pos += nchars
|
||||
|
||||
if richtext:
|
||||
pos += 4 * rt
|
||||
|
||||
if phonetic:
|
||||
pos += sz
|
||||
|
||||
return (strg, pos)
|
||||
|
||||
def unpack_cell_range_address_list_update_pos(
|
||||
output_list, data, pos, biff_version, addr_size=6):
|
||||
# output_list is updated in situ
|
||||
if biff_version < 80:
|
||||
assert addr_size == 6
|
||||
else:
|
||||
assert addr_size in (6, 8)
|
||||
n, = unpack("<H", data[pos:pos+2])
|
||||
pos += 2
|
||||
if n:
|
||||
fmt = "<HHBB" if addr_size == 6 else "<HHHH"
|
||||
for _unused in range(n):
|
||||
ra, rb, ca, cb = unpack(fmt, data[pos:pos+addr_size])
|
||||
output_list.append((ra, rb+1, ca, cb+1))
|
||||
pos += addr_size
|
||||
return pos
|
||||
|
||||
def hex_char_dump(strg, ofs, dlen, base=0, fout=sys.stdout, unnumbered=False):
|
||||
endpos = min(ofs + dlen, len(strg))
|
||||
pos = ofs
|
||||
numbered = not unnumbered
|
||||
num_prefix = ''
|
||||
while pos < endpos:
|
||||
endsub = min(pos + 16, endpos)
|
||||
substrg = strg[pos:endsub]
|
||||
lensub = endsub - pos
|
||||
if lensub <= 0 or lensub != len(substrg):
|
||||
fprintf(
|
||||
sys.stdout,
|
||||
'??? hex_char_dump: ofs=%d dlen=%d base=%d -> endpos=%d pos=%d endsub=%d substrg=%r\n',
|
||||
ofs, dlen, base, endpos, pos, endsub, substrg)
|
||||
break
|
||||
hexd = ''.join(["%02x " % c for c in substrg])
|
||||
chard = ''
|
||||
for c in substrg:
|
||||
if c == ord('\0'):
|
||||
c = '~'
|
||||
elif not (' ' <= chr(c) <= '~'):
|
||||
c = '?'
|
||||
if isinstance(c, int):
|
||||
c = chr(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('<HH', mem[pos:pos+4])
|
||||
if rc == 0 and length == 0:
|
||||
if mem[pos:] == '\0' * (stream_end - pos):
|
||||
dummies = stream_end - pos
|
||||
savpos = pos
|
||||
pos = stream_end
|
||||
break
|
||||
|
||||
if dummies:
|
||||
dummies += 4
|
||||
else:
|
||||
savpos = pos
|
||||
dummies = 4
|
||||
pos += 4
|
||||
else:
|
||||
if dummies:
|
||||
if numbered:
|
||||
num_prefix = "%5d: " % (adj + savpos)
|
||||
fprintf(fout, "%s---- %d zero bytes skipped ----\n",
|
||||
num_prefix, dummies)
|
||||
dummies = 0
|
||||
|
||||
recname = biff_rec_name_dict.get(rc, '<UNKNOWN>')
|
||||
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('<HH', mem[pos:pos+4])
|
||||
if rc == 0 and length == 0:
|
||||
if mem[pos:] == '\0' * (stream_end - pos):
|
||||
break
|
||||
recname = "<Dummy (zero)>"
|
||||
else:
|
||||
recname = biff_rec_name_dict.get(rc, None)
|
||||
if recname is None:
|
||||
recname = "Unknown_0x%04X" % rc
|
||||
if recname in tally:
|
||||
tally[recname] += 1
|
||||
else:
|
||||
tally[recname] = 1
|
||||
pos += length + 4
|
||||
for recname, count in sorted(tally.items()):
|
||||
fprintf(fout, "%8d %s", count, recname)
|
||||
@@ -1,346 +0,0 @@
|
||||
# Implements the minimal functionality required
|
||||
# to extract a "Workbook" or "Book" stream (as one big string)
|
||||
# from an OLE2 Compound Document file.
|
||||
#
|
||||
# Copyright © 2005-2008 Stephen John Machin, Lingfo Pty Ltd
|
||||
# This module is part of the xlrd3 package, which is released under a BSD-style licence.
|
||||
|
||||
# No part of the content of this file was derived from the works of David Giffin.
|
||||
|
||||
# 2008-11-04 SJM Avoid assertion error when -1 used instead of -2 for first_SID of empty SCSS [Frank Hoffsuemmer]
|
||||
# 2007-09-08 SJM Warning message if sector sizes are extremely large.
|
||||
# 2007-05-07 SJM Meaningful exception instead of IndexError if a SAT (sector allocation table) is corrupted.
|
||||
# 2007-04-22 SJM Missing "<" in a struct.unpack call => can't open files on bigendian platforms.
|
||||
|
||||
|
||||
import sys
|
||||
from struct import unpack
|
||||
|
||||
# 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
|
||||
|
||||
def int_floor_div(x, y):
|
||||
return divmod(x, y)[0]
|
||||
|
||||
class CompDocError(Exception):
|
||||
pass
|
||||
|
||||
class DirNode(object):
|
||||
|
||||
def __init__(self, DID, dent, DEBUG=0):
|
||||
# dent is the 128-byte directory entry
|
||||
self.DID = DID
|
||||
# (cbufsize, self.etype, self.colour, self.left_DID, self.right_DID,
|
||||
# self.root_DID,
|
||||
# self.first_SID,
|
||||
# self.tot_size) = \
|
||||
# unpack('<HBBiii16x4x8x8xii4x', dent[64:128])
|
||||
(cbufsize, self.etype, self.colour, self.left_DID, self.right_DID,
|
||||
self.root_DID) = \
|
||||
unpack('<HBBiii', dent[64:80])
|
||||
(self.first_SID, self.tot_size) = \
|
||||
unpack('<ii', dent[116:124])
|
||||
if cbufsize == 0:
|
||||
self.name = ''
|
||||
else:
|
||||
self.name = str(dent[0:cbufsize-2], 'utf_16_le') # omit the trailing U+0000
|
||||
self.children = [] # filled in later
|
||||
self.parent = -1 # indicates orphan; fixed up later
|
||||
self.tsinfo = unpack('<IIII', dent[100:116])
|
||||
if DEBUG:
|
||||
self.dump(DEBUG)
|
||||
|
||||
def dump(self, DEBUG=1):
|
||||
print("DID=%d name=%r etype=%d DIDs(left=%d right=%d root=%d parent=%d kids=%r) first_SID=%d tot_size=%d" \
|
||||
% (self.DID, self.name, self.etype, self.left_DID,
|
||||
self.right_DID, self.root_DID, self.parent, self.children, self.first_SID, self.tot_size))
|
||||
if DEBUG == 2:
|
||||
# cre_lo, cre_hi, mod_lo, mod_hi = tsinfo
|
||||
print("timestamp info", self.tsinfo)
|
||||
|
||||
def _build_family_tree(dirlist, parent_DID, child_DID):
|
||||
if child_DID < 0: return
|
||||
_build_family_tree(dirlist, parent_DID, dirlist[child_DID].left_DID)
|
||||
dirlist[parent_DID].children.append(child_DID)
|
||||
dirlist[child_DID].parent = parent_DID
|
||||
_build_family_tree(dirlist, parent_DID, dirlist[child_DID].right_DID)
|
||||
if dirlist[child_DID].etype == 1: # storage
|
||||
_build_family_tree(dirlist, child_DID, dirlist[child_DID].root_DID)
|
||||
|
||||
# Compound document handler.
|
||||
# @param mem The raw contents of the file, as a string, or as an mmap.mmap() object. The
|
||||
# only operation it needs to support is slicing.
|
||||
|
||||
class CompDoc(object):
|
||||
|
||||
def __init__(self, mem, logfile=sys.stdout, DEBUG=0):
|
||||
self.logfile = logfile
|
||||
if mem[0:8] != SIGNATURE:
|
||||
raise CompDocError('Not an OLE2 compound document')
|
||||
if mem[28:30] != b'\xFE\xFF':
|
||||
raise CompDocError('Expected "little-endian" marker, found %r' % mem[28:30])
|
||||
revision, version = unpack('<HH', mem[24:28])
|
||||
if DEBUG:
|
||||
print("\nCompDoc format: version=0x%04x revision=0x%04x" % (version, revision), file=logfile)
|
||||
self.mem = mem
|
||||
ssz, sssz = unpack('<HH', mem[30:34])
|
||||
if ssz > 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
|
||||
(
|
||||
SAT_tot_secs, self.dir_first_sec_sid, _unused, self.min_size_std_stream,
|
||||
SSAT_first_sec_sid, SSAT_tot_secs,
|
||||
MSAT_first_sec_sid, MSAT_tot_secs,
|
||||
# ) = unpack('<ii4xiiiii', mem[44:76])
|
||||
) = unpack('<iiiiiiii', mem[44:76])
|
||||
mem_data_len = len(mem) - 512
|
||||
mem_data_secs, left_over = divmod(mem_data_len, sec_size)
|
||||
if left_over:
|
||||
#### raise CompDocError("Not a whole number of sectors")
|
||||
print("WARNING *** file size (%d) not 512 + multiple of sector size (%d)" \
|
||||
% (len(mem), sec_size), file=logfile)
|
||||
if DEBUG:
|
||||
print('sec sizes', ssz, sssz, sec_size, self.short_sec_size, file=logfile)
|
||||
print("mem data: %d bytes == %d sectors" % (mem_data_len, mem_data_secs), file=logfile)
|
||||
print("SAT_tot_secs=%d, dir_first_sec_sid=%d, min_size_std_stream=%d" \
|
||||
% (SAT_tot_secs, self.dir_first_sec_sid, self.min_size_std_stream,), file=logfile)
|
||||
print("SSAT_first_sec_sid=%d, SSAT_tot_secs=%d" % (SSAT_first_sec_sid, SSAT_tot_secs,), file=logfile)
|
||||
print("MSAT_first_sec_sid=%d, MSAT_tot_secs=%d" % (MSAT_first_sec_sid, MSAT_tot_secs,), file=logfile)
|
||||
nent = int_floor_div(sec_size, 4) # number of SID entries in a sector
|
||||
fmt = "<%di" % nent
|
||||
trunc_warned = 0
|
||||
#
|
||||
# === build the MSAT ===
|
||||
#
|
||||
MSAT = list(unpack('<109i', mem[76:512]))
|
||||
sid = MSAT_first_sec_sid
|
||||
while sid >= 0:
|
||||
if sid >= mem_data_secs:
|
||||
raise CompDocError(
|
||||
"MSAT extension: accessing sector %d but only %d in file" % (sid, mem_data_secs)
|
||||
)
|
||||
offset = 512 + sec_size * sid
|
||||
news = list(unpack(fmt, mem[offset:offset+sec_size]))
|
||||
sid = news.pop()
|
||||
MSAT.extend(news)
|
||||
if DEBUG:
|
||||
print("MSAT: len =", len(MSAT), file=logfile)
|
||||
print(MSAT, file=logfile)
|
||||
#
|
||||
# === build the SAT ===
|
||||
#
|
||||
self.SAT = []
|
||||
for msid in MSAT:
|
||||
if msid == FREESID: 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
|
||||
continue
|
||||
offset = 512 + sec_size * msid
|
||||
news = list(unpack(fmt, mem[offset:offset+sec_size]))
|
||||
self.SAT.extend(news)
|
||||
if DEBUG:
|
||||
print("SAT: len =", len(self.SAT), file=logfile)
|
||||
print(self.SAT, file=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()
|
||||
|
||||
# === build the directory ===
|
||||
#
|
||||
dbytes = self._get_stream(
|
||||
self.mem, 512, self.SAT, self.sec_size, self.dir_first_sec_sid,
|
||||
name="directory")
|
||||
dirlist = []
|
||||
did = -1
|
||||
for pos in range(0, len(dbytes), 128):
|
||||
did += 1
|
||||
dirlist.append(DirNode(did, dbytes[pos:pos+128], 0))
|
||||
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 and 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")
|
||||
# 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:
|
||||
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]
|
||||
# assert SSAT_tot_secs == 0 or sid == EOCSID
|
||||
if DEBUG: print("SSAT last sid %d; remaining sectors %d" % (sid, nsecs), file=logfile)
|
||||
assert nsecs == 0 and sid == EOCSID
|
||||
if DEBUG: print("SSAT", self.SSAT, file=logfile)
|
||||
|
||||
def _get_stream(self, mem, base, sat, sec_size, start_sid, size=None, name=''):
|
||||
# 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:
|
||||
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:
|
||||
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:
|
||||
print("WARNING *** OLE2 stream %r: expected size %d, actual size %d" \
|
||||
% (name, size, size - todo), file=self.logfile)
|
||||
return b''.join(sectors)
|
||||
|
||||
def _dir_search(self, path, storage_DID=0):
|
||||
# Return matching DirNode instance, or None
|
||||
head = path[0]
|
||||
tail = path[1:]
|
||||
dl = self.dirlist
|
||||
for child in dl[storage_DID].children:
|
||||
if dl[child].name.lower() == head.lower():
|
||||
et = dl[child].etype
|
||||
if et == 2:
|
||||
return dl[child]
|
||||
if et == 1:
|
||||
if not tail:
|
||||
raise CompDocError("Requested component is a 'storage'")
|
||||
return self._dir_search(tail, child)
|
||||
dl[child].dump(1)
|
||||
raise CompDocError("Requested stream is not a 'user stream'")
|
||||
return None
|
||||
|
||||
##
|
||||
# Interrogate the compound document's directory; return the stream as a string if found, otherwise
|
||||
# return None.
|
||||
# @param qname Name of the desired stream e.g. u'Workbook'. Should be in Unicode or convertible thereto.
|
||||
|
||||
def get_named_stream(self, qname):
|
||||
d = self._dir_search(qname.split("/"))
|
||||
if d is None:
|
||||
return None
|
||||
if d.tot_size >= self.min_size_std_stream:
|
||||
return self._get_stream(
|
||||
self.mem, 512, self.SAT, self.sec_size, d.first_SID,
|
||||
d.tot_size, name=qname)
|
||||
else:
|
||||
return self._get_stream(
|
||||
self.SSCS, 0, self.SSAT, self.short_sec_size, d.first_SID,
|
||||
d.tot_size, name=qname + " (from SSCS)")
|
||||
|
||||
# Interrogate the compound document's directory.
|
||||
# If the named stream is not found, (None, 0, 0) will be returned.
|
||||
# If the named stream is found and is contiguous within the original byte sequence ("mem")
|
||||
# used when the document was opened,
|
||||
# then (mem, offset_to_start_of_stream, length_of_stream) is returned.
|
||||
# Otherwise a new string is built from the fragments and (new_string, 0, length_of_stream) is returned.
|
||||
# @param qname Name of the desired stream e.g. u'Workbook'. Should be in Unicode or convertible thereto.
|
||||
|
||||
def locate_named_stream(self, qname):
|
||||
d = self._dir_search(qname.split("/"))
|
||||
if d is None:
|
||||
return (None, 0, 0)
|
||||
if d.tot_size >= self.min_size_std_stream:
|
||||
return self._locate_stream(self.mem, 512, self.SAT, self.sec_size, d.first_SID, d.tot_size)
|
||||
else:
|
||||
return (
|
||||
self._get_stream(
|
||||
self.SSCS, 0, self.SSAT, self.short_sec_size, d.first_SID,
|
||||
d.tot_size, qname + " (from SSCS)"),
|
||||
0,
|
||||
d.tot_size
|
||||
)
|
||||
return (None, 0, 0) # not found
|
||||
|
||||
def _locate_stream(self, mem, base, sat, sec_size, start_sid, size):
|
||||
# print >> self.logfile, "_locate_stream", base, sec_size, start_sid, 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 = []
|
||||
while s >= 0:
|
||||
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
|
||||
# print >> self.logfile, len(slices) + 1, "slices"
|
||||
if not slices:
|
||||
# The stream is contiguous ... just what we like!
|
||||
return (mem, start_pos, size)
|
||||
slices.append((start_pos, end_pos))
|
||||
return (b''.join([mem[start_pos:end_pos] for start_pos, end_pos in slices]), 0, size)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,276 +0,0 @@
|
||||
# Author: mozman <mozman@gmx.at>
|
||||
# Purpose: xfcell -- cell with convenient xf function
|
||||
# Created: 04.12.2010
|
||||
# Copyright (C) 2010, Manfred Moitzi
|
||||
# License: BSD-style licence
|
||||
|
||||
"""
|
||||
The XFCell() object contains the data for one cell.
|
||||
|
||||
WARNING: You don't call this class yourself. You access Cell objects
|
||||
via methods of the Sheet object(s) that you found in the Book object that
|
||||
was returned when you called xlrd.open_workbook("myfile.xls").
|
||||
|
||||
Cell objects have four attributes: `ctype` is an int, `value` (which depends
|
||||
on `ctype`), `xf_index` and `sheet`, a reference to the containing sheet. If
|
||||
**formatting_info** is not enabled when the workbook is opened, xf_index will
|
||||
be **None**.
|
||||
|
||||
The following table describes the types of cells and how their values
|
||||
are represented in Python.
|
||||
|
||||
=============== ===== ============ ==========================================
|
||||
Type symbol Const Python value Note
|
||||
=============== ===== ============ ==========================================
|
||||
XL_CELL_EMPTY 0 ""
|
||||
XL_CELL_TEXT 1 str
|
||||
XL_CELL_NUMBER 2 float
|
||||
XL_CELL_DATE 3 float
|
||||
XL_CELL_BOOLEAN 4 int 1 means TRUE, 0 means FALSE
|
||||
XL_CELL_ERROR 5 int representing internal Excel codes; for a
|
||||
text representation, refer to the supplied
|
||||
dictionary error_text_from_code
|
||||
XL_CELL_BLANK 6 "" this type will appear only when
|
||||
open_workbook(..., formatting_info=True)
|
||||
is used.
|
||||
=============== ===== ============ ==========================================
|
||||
"""
|
||||
|
||||
import datetime
|
||||
|
||||
from .xldate import xldate_as_tuple
|
||||
from .biffh import XL_CELL_DATE, BaseObject
|
||||
|
||||
class XFCell(BaseObject):
|
||||
""" Extended Cell() class with convenient methods for easy access of cell
|
||||
properties.
|
||||
"""
|
||||
__slots__ = ['sheet', 'ctype', 'value', 'xf']
|
||||
|
||||
def __init__(self, ctype, value, xf_index=None, sheet=None):
|
||||
self.sheet = sheet
|
||||
self.ctype = ctype
|
||||
self.value = value
|
||||
|
||||
if xf_index is not None:
|
||||
self.xf = self.book.xf_list[xf_index]
|
||||
else:
|
||||
self.xf = None
|
||||
|
||||
@property
|
||||
def book(self):
|
||||
return self.sheet.book
|
||||
|
||||
@property
|
||||
def has_xf(self):
|
||||
return (self.xf is not None)
|
||||
|
||||
@property
|
||||
def xf_index(self):
|
||||
if self.has_xf:
|
||||
return self.xf.xf_index
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def parent_style(self):
|
||||
return self.book.xf_list[self.xf.parent_style_index]
|
||||
|
||||
@property
|
||||
def is_datetime(self):
|
||||
return self.ctype == XL_CELL_DATE
|
||||
|
||||
@property
|
||||
def has_date(self):
|
||||
if self.is_datetime:
|
||||
return self.value > 1.
|
||||
return False
|
||||
|
||||
def get_color(self, index):
|
||||
return self.book.colour_map[index]
|
||||
|
||||
def datetime(self):
|
||||
""" Returns a datetime.datetime object if cell type is XL_CELL_DATE
|
||||
else raises a TypeError, and raises ValueError if the the cell has
|
||||
not date value (only time value is present).
|
||||
"""
|
||||
if self.is_datetime:
|
||||
if self.has_date:
|
||||
date = xldate_as_tuple(self.value, self.book.datemode)
|
||||
return datetime.datetime(*date)
|
||||
else:
|
||||
raise ValueError("Cell has no date value.")
|
||||
else:
|
||||
raise TypeError("Cell is not a XL_CELL_DATE.")
|
||||
|
||||
def date(self):
|
||||
""" Returns a datetime.date object if cell type is XL_CELL_DATE
|
||||
else raises a **TypeError**. Raises **ValueError** if the cell
|
||||
doesn't have a date value (only time value is present).
|
||||
"""
|
||||
dt = self.datetime()
|
||||
return dt.date()
|
||||
|
||||
def time(self):
|
||||
""" Returns a datetime.time object if cell type is XL_CELL_DATE else
|
||||
raises a TypeError.
|
||||
"""
|
||||
if self.is_datetime:
|
||||
date = xldate_as_tuple(self.value, self.book.datemode)
|
||||
return datetime.time(date[3], date[4], date[5])
|
||||
else:
|
||||
raise TypeError("Cell is not a XL_CELL_DATE.")
|
||||
|
||||
#
|
||||
# access the XFBackground() class
|
||||
#
|
||||
|
||||
@property
|
||||
def background(self):
|
||||
if self.xf.is_style and \
|
||||
self.xf._background_flag == 0:
|
||||
return self.xf.background
|
||||
elif self.xf._background_flag:
|
||||
return self.xf.background
|
||||
else:
|
||||
return self.parent_style.background
|
||||
|
||||
def background_color(self):
|
||||
""" Get cell background-color as 3-tuple. """
|
||||
color_index = self.xf.background.background_colour_index
|
||||
return self.get_color(color_index)
|
||||
|
||||
def fill_pattern(self):
|
||||
return self.xf.background.fill_pattern
|
||||
|
||||
def pattern_color(self):
|
||||
color_index = self.xf.background.pattern_colour_index
|
||||
return self.get_color(color_index)
|
||||
|
||||
#
|
||||
# access the Font() class
|
||||
#
|
||||
|
||||
@property
|
||||
def font_index(self):
|
||||
if self.xf.is_style and \
|
||||
self.xf._font_flag == 0:
|
||||
return self.xf.font_index
|
||||
elif self.xf._font_flag:
|
||||
return self.xf.font_index
|
||||
else:
|
||||
return self.parent_style.font_index
|
||||
|
||||
@property
|
||||
def font(self):
|
||||
""" Get the Font() class. """
|
||||
return self.book.font_list[self.xf.font_index]
|
||||
|
||||
def font_color(self):
|
||||
""" Get cell foreground-color as 3-tuple. """
|
||||
return self.get_color(self.font.colour_index)
|
||||
|
||||
#
|
||||
# access the Format() class
|
||||
#
|
||||
|
||||
@property
|
||||
def format_key(self):
|
||||
if self.xf.is_style and \
|
||||
self.xf._format_flag == 0:
|
||||
return self.xf.format_key
|
||||
elif self.xf._format_flag:
|
||||
return self.xf.format_key
|
||||
else:
|
||||
return self.parent_style.format_key
|
||||
|
||||
@property
|
||||
def format(self):
|
||||
""" Get the Format() class. """
|
||||
return self.book.format_map[self.format_key]
|
||||
|
||||
def format_str(self):
|
||||
""" Get the associated 'format_str'. """
|
||||
return self.format.format_str
|
||||
|
||||
#
|
||||
# access the XFAligment() class
|
||||
#
|
||||
|
||||
@property
|
||||
def alignment(self):
|
||||
if self.xf.is_style and \
|
||||
self.xf._alignment_flag == 0:
|
||||
return self.xf.alignment
|
||||
elif self.xf._alignment_flag:
|
||||
return self.xf.alignment
|
||||
else:
|
||||
return self.parent_style.alignment
|
||||
|
||||
#
|
||||
# access the XFBorder() class
|
||||
#
|
||||
|
||||
@property
|
||||
def border(self):
|
||||
if self.xf.is_style and \
|
||||
self.xf._border_flag == 0:
|
||||
return self.xf.border
|
||||
elif self.xf._border_flag:
|
||||
return self.xf.border
|
||||
else:
|
||||
return self.parent_style.border
|
||||
|
||||
def bordercolors(self):
|
||||
""" Get border color as dict of rgb-color-tuples. """
|
||||
border = self.border
|
||||
return {
|
||||
'top': self.get_color(border.top_colour_index),
|
||||
'bottom': self.get_color(border.bottom_colour_index),
|
||||
'left': self.get_color(border.left_colour_index),
|
||||
'right': self.get_color(border.right_colour_index),
|
||||
'diag': self.get_color(border.diag_colour_index),
|
||||
}
|
||||
|
||||
def borderstyles(self):
|
||||
""" Get border styles as dict of ints. """
|
||||
border = self.border
|
||||
return {
|
||||
'top': border.top_line_style,
|
||||
'bottom': border.bottom_line_style,
|
||||
'left': border.left_line_style,
|
||||
'right': border.right_line_style,
|
||||
'diag': border.diag_line_style,
|
||||
}
|
||||
|
||||
@property
|
||||
def has_up_diag(self):
|
||||
""" Draw a line across the cell from bottom left to top right. """
|
||||
return bool(self.border.diag_up)
|
||||
|
||||
@property
|
||||
def has_down_diag(self):
|
||||
""" Draw a line across the cell from top left to bottom right. """
|
||||
return bool(self.border.diag_down)
|
||||
|
||||
#
|
||||
# access the XFProtection() class
|
||||
#
|
||||
|
||||
@property
|
||||
def protection(self):
|
||||
if self.xf.is_style and \
|
||||
self.xf._protection_flag == 0:
|
||||
return self.xf.protection
|
||||
elif self.xf._protection_flag:
|
||||
return self.xf.protection
|
||||
else:
|
||||
return self.parent_style.protection
|
||||
|
||||
@property
|
||||
def is_cell_locked(self):
|
||||
return bool(self.protection.cell_locked)
|
||||
|
||||
@property
|
||||
def is_formula_hidden(self):
|
||||
return bool(self.protection.cell_locked)
|
||||
@@ -1,84 +0,0 @@
|
||||
# Author: mozman <mozman@gmx.at>
|
||||
# Purpose: xfconst -- xf constants
|
||||
# Created: 05.12.2010
|
||||
# Copyright (C) 2010, Manfred Moitzi
|
||||
# License: BSD-style licence
|
||||
|
||||
# excelfileformat section 5.115.1 pg. 219
|
||||
HOR_ALIGN_GENERAL = 0
|
||||
HOR_ALIGN_LEFT = 1
|
||||
HOR_ALIGN_CENTRED = 2
|
||||
HOR_ALIGN_RIGHT = 3
|
||||
HOR_ALIGN_FILLED = 4
|
||||
HOR_ALIGN_JUSTIFIED = 5
|
||||
HOR_ALIGN_CENTRED_ACROSS_SELECTION = 6
|
||||
HOR_ALIGN_DISTRIBUTED = 7
|
||||
|
||||
# excelfileformat section 5.115.1 pg. 220
|
||||
VERT_ALIGN_TOP = 0
|
||||
VERT_ALIGN_CENTRED = 1
|
||||
VERT_ALIGN_BOTTOM = 2
|
||||
VERT_ALIGN_JUSTIFIED = 3
|
||||
VERT_ALIGN_DISTRIBUTED = 4
|
||||
|
||||
# excelfileformat section 5.115.1 pg. 220
|
||||
ORIENTATION_NONE = 0
|
||||
ORIENTATION_STACKED = 1
|
||||
ORIENTATION_90_COUNTERCLOCKWISE = 2
|
||||
ORIENTATION_90_CLOCKWISE = 3
|
||||
|
||||
# excelfileformat section 5.115.1 pg. 220
|
||||
ROTATION_NONE = 0
|
||||
ROTATION_STACKED = 255
|
||||
# other values:
|
||||
# Value Description
|
||||
# ======= =================================
|
||||
# 1-90 1 to 90 degrees counterclockwise
|
||||
# 91-180 1 to 90 degrees clockwise
|
||||
|
||||
# excelfileformat section 2.5.11 Line Styles for Cell Borders pg. 24
|
||||
LS_NOLINE = 0
|
||||
|
||||
# solid line
|
||||
LS_THIN = 1
|
||||
|
||||
# solid line
|
||||
LS_MEDIUM = 2
|
||||
|
||||
# - - - - - - - - -
|
||||
LS_DASHED = 3
|
||||
|
||||
# .................
|
||||
LS_DOTTED = 4
|
||||
|
||||
# solid line
|
||||
LS_THICK = 5
|
||||
|
||||
# =================
|
||||
LS_DOUBLE = 6
|
||||
|
||||
# very thin dotted
|
||||
LS_HAIR = 7
|
||||
|
||||
# - - - - - - - - -
|
||||
LS_MEDIUM_DASHED = 8
|
||||
|
||||
# - . - . - . - . -
|
||||
LS_THIN_DASH_DOTTED = 9
|
||||
|
||||
# - . - . - . - . -
|
||||
LS_MEDIUM_DASH_DOTTED = 10
|
||||
|
||||
# - .. - .. - .. - .. -
|
||||
LS_THIN_DASH_DOT_DOTTED = 11
|
||||
|
||||
# - .. - .. - .. - .. -
|
||||
LS_MEDIUM_DASH_DOT_DOTTED = 12
|
||||
|
||||
# \\\ . \\\ . \\\ . \\\ . \\\
|
||||
LS_SLANTED_MEDIUM_DASH_DOTTED = 13
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
# No part of the content of this file was derived from the works of David Giffin.
|
||||
#
|
||||
# Copyright © 2005-2008 Stephen John Machin, Lingfo Pty Ltd
|
||||
# This module is part of the xlrd3 package, which is released under a
|
||||
# BSD-style licence.
|
||||
#
|
||||
# Provides function(s) for dealing with Microsoft Excel ™ dates.
|
||||
#
|
||||
# 2008-10-18 SJM Fix bug in xldate_from_date_tuple (affected some years after 2099)
|
||||
#
|
||||
# The conversion from days to (year, month, day) starts with
|
||||
# an integral "julian day number" aka JDN.
|
||||
# FWIW, JDN 0 corresponds to noon on Monday November 24 in Gregorian year -4713.
|
||||
# More importantly:
|
||||
# Noon on Gregorian 1900-03-01 (day 61 in the 1900-based system) is JDN 2415080.0
|
||||
# Noon on Gregorian 1904-01-02 (day 1 in the 1904-based system) is JDN 2416482.0
|
||||
|
||||
def ifd(x, y):
|
||||
return divmod(x, y)[0]
|
||||
|
||||
_JDN_delta = (2415080 - 61, 2416482 - 1)
|
||||
assert _JDN_delta[1] - _JDN_delta[0] == 1462
|
||||
|
||||
class XLDateError(ValueError): pass
|
||||
|
||||
class XLDateNegative(XLDateError): pass
|
||||
class XLDateAmbiguous(XLDateError): pass
|
||||
class XLDateTooLarge(XLDateError): pass
|
||||
class XLDateBadDatemode(XLDateError): pass
|
||||
class XLDateBadTuple(XLDateError): pass
|
||||
|
||||
_XLDAYS_TOO_LARGE = (2958466, 2958466 - 1462) # This is equivalent to 10000-01-01
|
||||
|
||||
# Convert an Excel number (presumed to represent a date, a datetime or a time) into
|
||||
# a tuple suitable for feeding to datetime or mx.DateTime constructors.
|
||||
# @param xldate The Excel number
|
||||
# @param datemode 0: 1900-based, 1: 1904-based.
|
||||
# <br>WARNING: when using this function to
|
||||
# interpret the contents of a workbook, you should pass in the Book.datemode
|
||||
# attribute of that workbook. Whether
|
||||
# the workbook has ever been anywhere near a Macintosh is irrelevant.
|
||||
# @return Gregorian (year, month, day, hour, minute, nearest_second).
|
||||
# <br>Special case: if 0.0 <= xldate < 1.0, it is assumed to represent a time;
|
||||
# (0, 0, 0, hour, minute, second) will be returned.
|
||||
# <br>Note: 1904-01-01 is not regarded as a valid date in the datemode 1 system; its "serial number"
|
||||
# is zero.
|
||||
# @throws XLDateNegative xldate < 0.00
|
||||
# @throws XLDateAmbiguous The 1900 leap-year problem (datemode == 0 and 1.0 <= xldate < 61.0)
|
||||
# @throws XLDateTooLarge Gregorian year 10000 or later
|
||||
# @throws XLDateBadDatemode datemode arg is neither 0 nor 1
|
||||
# @throws XLDateError Covers the 4 specific errors
|
||||
|
||||
def xldate_as_tuple(xldate, datemode):
|
||||
if datemode not in (0, 1):
|
||||
raise XLDateBadDatemode(datemode)
|
||||
if xldate == 0.00:
|
||||
return (0, 0, 0, 0, 0, 0)
|
||||
if xldate < 0.00:
|
||||
raise XLDateNegative(xldate)
|
||||
xldays = int(xldate)
|
||||
frac = xldate - xldays
|
||||
seconds = int(round(frac * 86400.0))
|
||||
assert 0 <= seconds <= 86400
|
||||
if seconds == 86400:
|
||||
hour = minute = second = 0
|
||||
xldays += 1
|
||||
else:
|
||||
# second = seconds % 60; minutes = seconds // 60
|
||||
minutes, second = divmod(seconds, 60)
|
||||
# minute = minutes % 60; hour = minutes // 60
|
||||
hour, minute = divmod(minutes, 60)
|
||||
if xldays >= _XLDAYS_TOO_LARGE[datemode]:
|
||||
raise XLDateTooLarge(xldate)
|
||||
|
||||
if xldays == 0:
|
||||
return (0, 0, 0, hour, minute, second)
|
||||
|
||||
if xldays < 61 and datemode == 0:
|
||||
raise XLDateAmbiguous(xldate)
|
||||
|
||||
jdn = xldays + _JDN_delta[datemode]
|
||||
yreg = (ifd(ifd(jdn * 4 + 274277, 146097) * 3, 4) + jdn + 1363) * 4 + 3
|
||||
mp = ifd(yreg % 1461, 4) * 535 + 333
|
||||
d = ifd(mp % 16384, 535) + 1
|
||||
# mp /= 16384
|
||||
mp >>= 14
|
||||
if mp >= 10:
|
||||
return (ifd(yreg, 1461) - 4715, mp - 9, d, hour, minute, second)
|
||||
else:
|
||||
return (ifd(yreg, 1461) - 4716, mp + 3, d, hour, minute, second)
|
||||
|
||||
# === conversions from date/time to xl numbers
|
||||
|
||||
def _leap(y):
|
||||
if y % 4: return 0
|
||||
if y % 100: return 1
|
||||
if y % 400: return 0
|
||||
return 1
|
||||
|
||||
_days_in_month = (None, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
||||
|
||||
# Convert a date tuple (year, month, day) to an Excel date.
|
||||
# @param year Gregorian year.
|
||||
# @param month 1 <= month <= 12
|
||||
# @param day 1 <= day <= last day of that (year, month)
|
||||
# @param datemode 0: 1900-based, 1: 1904-based.
|
||||
# @throws XLDateAmbiguous The 1900 leap-year problem (datemode == 0 and 1.0 <= xldate < 61.0)
|
||||
# @throws XLDateBadDatemode datemode arg is neither 0 nor 1
|
||||
# @throws XLDateBadTuple (year, month, day) is too early/late or has invalid component(s)
|
||||
# @throws XLDateError Covers the specific errors
|
||||
|
||||
def xldate_from_date_tuple(datetuple, datemode):
|
||||
|
||||
(year, month, day) = datetuple
|
||||
if datemode not in (0, 1):
|
||||
raise XLDateBadDatemode(datemode)
|
||||
|
||||
if year == 0 and month == 0 and day == 0:
|
||||
return 0.00
|
||||
|
||||
if not (1900 <= year <= 9999):
|
||||
raise XLDateBadTuple("Invalid year: %r" % ((year, month, day),))
|
||||
if not (1 <= month <= 12):
|
||||
raise XLDateBadTuple("Invalid month: %r" % ((year, month, day),))
|
||||
if day < 1 \
|
||||
or (day > _days_in_month[month] and not(day == 29 and month == 2 and _leap(year))):
|
||||
raise XLDateBadTuple("Invalid day: %r" % ((year, month, day),))
|
||||
|
||||
Yp = year + 4716
|
||||
M = month
|
||||
if M <= 2:
|
||||
Yp = Yp - 1
|
||||
Mp = M + 9
|
||||
else:
|
||||
Mp = M - 3
|
||||
jdn = ifd(1461 * Yp, 4) + ifd(979 * Mp + 16, 32) + \
|
||||
day - 1364 - ifd(ifd(Yp + 184, 100) * 3, 4)
|
||||
xldays = jdn - _JDN_delta[datemode]
|
||||
if xldays <= 0:
|
||||
raise XLDateBadTuple("Invalid (year, month, day): %r" % ((year, month, day),))
|
||||
if xldays < 61 and datemode == 0:
|
||||
raise XLDateAmbiguous("Before 1900-03-01: %r" % ((year, month, day),))
|
||||
return float(xldays)
|
||||
|
||||
# Convert a time tuple (hour, minute, second) to an Excel "date" value (fraction of a day).
|
||||
# @param hour 0 <= hour < 24
|
||||
# @param minute 0 <= minute < 60
|
||||
# @param second 0 <= second < 60
|
||||
# @throws XLDateBadTuple Out-of-range hour, minute, or second
|
||||
|
||||
def xldate_from_time_tuple(timetuple):
|
||||
(hour, minute, second) = timetuple
|
||||
if 0 <= hour < 24 and 0 <= minute < 60 and 0 <= second < 60:
|
||||
return ((second / 60.0 + minute) / 60.0 + hour) / 24.0
|
||||
raise XLDateBadTuple("Invalid (hour, minute, second): %r" % ((hour, minute, second),))
|
||||
|
||||
# Convert a datetime tuple (year, month, day, hour, minute, second) to an Excel date value.
|
||||
# For more details, refer to other xldate_from_*_tuple functions.
|
||||
# @param datetime_tuple (year, month, day, hour, minute, second)
|
||||
# @param datemode 0: 1900-based, 1: 1904-based.
|
||||
|
||||
def xldate_from_datetime_tuple(datetime_tuple, datemode):
|
||||
return (
|
||||
xldate_from_date_tuple(datetime_tuple[:3], datemode)
|
||||
+
|
||||
xldate_from_time_tuple(datetime_tuple[3:])
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,262 +0,0 @@
|
||||
# -*- coding: windows-1251 -*-
|
||||
|
||||
# Portions are Copyright (C) 2005 Roman V. Kiseliov
|
||||
# Portions are Copyright (c) 2004 Evgeny Filatov <fufff@users.sourceforge.net>
|
||||
# Portions are Copyright (c) 2002-2004 John McNamara (Perl Spreadsheet::WriteExcel)
|
||||
|
||||
from BIFFRecords import BiffRecord
|
||||
from struct import *
|
||||
|
||||
|
||||
def _size_col(sheet, col):
|
||||
return sheet.col_width(col)
|
||||
|
||||
|
||||
def _size_row(sheet, row):
|
||||
return sheet.row_height(row)
|
||||
|
||||
|
||||
def _position_image(sheet, row_start, col_start, x1, y1, width, height):
|
||||
"""Calculate the vertices that define the position of the image as required by
|
||||
the OBJ record.
|
||||
|
||||
+------------+------------+
|
||||
| A | B |
|
||||
+-----+------------+------------+
|
||||
| |(x1,y1) | |
|
||||
| 1 |(A1)._______|______ |
|
||||
| | | | |
|
||||
| | | | |
|
||||
+-----+----| BITMAP |-----+
|
||||
| | | | |
|
||||
| 2 | |______________. |
|
||||
| | | (B2)|
|
||||
| | | (x2,y2)|
|
||||
+---- +------------+------------+
|
||||
|
||||
Example of a bitmap that covers some of the area from cell A1 to cell B2.
|
||||
|
||||
Based on the width and height of the bitmap we need to calculate 8 vars:
|
||||
col_start, row_start, col_end, row_end, x1, y1, x2, y2.
|
||||
The width and height of the cells are also variable and have to be taken into
|
||||
account.
|
||||
The values of col_start and row_start are passed in from the calling
|
||||
function. The values of col_end and row_end are calculated by subtracting
|
||||
the width and height of the bitmap from the width and height of the
|
||||
underlying cells.
|
||||
The vertices are expressed as a percentage of the underlying cell width as
|
||||
follows (rhs values are in pixels):
|
||||
|
||||
x1 = X / W *1024
|
||||
y1 = Y / H *256
|
||||
x2 = (X-1) / W *1024
|
||||
y2 = (Y-1) / H *256
|
||||
|
||||
Where: X is distance from the left side of the underlying cell
|
||||
Y is distance from the top of the underlying cell
|
||||
W is the width of the cell
|
||||
H is the height of the cell
|
||||
|
||||
Note: the SDK incorrectly states that the height should be expressed as a
|
||||
percentage of 1024.
|
||||
|
||||
col_start - Col containing upper left corner of object
|
||||
row_start - Row containing top left corner of object
|
||||
x1 - Distance to left side of object
|
||||
y1 - Distance to top of object
|
||||
width - Width of image frame
|
||||
height - Height of image frame
|
||||
|
||||
"""
|
||||
# Adjust start column for offsets that are greater than the col width
|
||||
while x1 >= _size_col(sheet, col_start):
|
||||
x1 -= _size_col(sheet, col_start)
|
||||
col_start += 1
|
||||
# Adjust start row for offsets that are greater than the row height
|
||||
while y1 >= _size_row(sheet, row_start):
|
||||
y1 -= _size_row(sheet, row_start)
|
||||
row_start += 1
|
||||
# Initialise end cell to the same as the start cell
|
||||
row_end = row_start # Row containing bottom right corner of object
|
||||
col_end = col_start # Col containing lower right corner of object
|
||||
width = width + x1 - 1
|
||||
height = height + y1 - 1
|
||||
# Subtract the underlying cell widths to find the end cell of the image
|
||||
while (width >= _size_col(sheet, col_end)):
|
||||
width -= _size_col(sheet, col_end)
|
||||
col_end += 1
|
||||
# Subtract the underlying cell heights to find the end cell of the image
|
||||
while (height >= _size_row(sheet, row_end)):
|
||||
height -= _size_row(sheet, row_end)
|
||||
row_end += 1
|
||||
# Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
|
||||
# with zero height or width.
|
||||
if ((_size_col(sheet, col_start) == 0) or (_size_col(sheet, col_end) == 0)
|
||||
or (_size_row(sheet, row_start) == 0) or (_size_row(sheet, row_end) == 0)):
|
||||
return
|
||||
# Convert the pixel values to the percentage value expected by Excel
|
||||
x1 = int(float(x1) / _size_col(sheet, col_start) * 1024)
|
||||
y1 = int(float(y1) / _size_row(sheet, row_start) * 256)
|
||||
# Distance to right side of object
|
||||
x2 = int(float(width) / _size_col(sheet, col_end) * 1024)
|
||||
# Distance to bottom of object
|
||||
y2 = int(float(height) / _size_row(sheet, row_end) * 256)
|
||||
return (col_start, x1, row_start, y1, col_end, x2, row_end, y2)
|
||||
|
||||
|
||||
class ObjBmpRecord(BiffRecord):
|
||||
_REC_ID = 0x005D # Record identifier
|
||||
|
||||
def __init__(self, row, col, sheet, im_data_bmp, x, y, scale_x, scale_y):
|
||||
# Scale the frame of the image.
|
||||
width = im_data_bmp.width * scale_x
|
||||
height = im_data_bmp.height * scale_y
|
||||
|
||||
# Calculate the vertices of the image and write the OBJ record
|
||||
coordinates = _position_image(sheet, row, col, x, y, width, height)
|
||||
# print coordinates
|
||||
col_start, x1, row_start, y1, col_end, x2, row_end, y2 = coordinates
|
||||
|
||||
"""Store the OBJ record that precedes an IMDATA record. This could be generalise
|
||||
to support other Excel objects.
|
||||
|
||||
"""
|
||||
cObj = 0x0001 # Count of objects in file (set to 1)
|
||||
OT = 0x0008 # Object type. 8 = Picture
|
||||
id = 0x0001 # Object ID
|
||||
grbit = 0x0614 # Option flags
|
||||
colL = col_start # Col containing upper left corner of object
|
||||
dxL = x1 # Distance from left side of cell
|
||||
rwT = row_start # Row containing top left corner of object
|
||||
dyT = y1 # Distance from top of cell
|
||||
colR = col_end # Col containing lower right corner of object
|
||||
dxR = x2 # Distance from right of cell
|
||||
rwB = row_end # Row containing bottom right corner of object
|
||||
dyB = y2 # Distance from bottom of cell
|
||||
cbMacro = 0x0000 # Length of FMLA structure
|
||||
Reserved1 = 0x0000 # Reserved
|
||||
Reserved2 = 0x0000 # Reserved
|
||||
icvBack = 0x09 # Background colour
|
||||
icvFore = 0x09 # Foreground colour
|
||||
fls = 0x00 # Fill pattern
|
||||
fAuto = 0x00 # Automatic fill
|
||||
icv = 0x08 # Line colour
|
||||
lns = 0xff # Line style
|
||||
lnw = 0x01 # Line weight
|
||||
fAutoB = 0x00 # Automatic border
|
||||
frs = 0x0000 # Frame style
|
||||
cf = 0x0009 # Image format, 9 = bitmap
|
||||
Reserved3 = 0x0000 # Reserved
|
||||
cbPictFmla = 0x0000 # Length of FMLA structure
|
||||
Reserved4 = 0x0000 # Reserved
|
||||
grbit2 = 0x0001 # Option flags
|
||||
Reserved5 = 0x0000 # Reserved
|
||||
|
||||
data = pack("<L", cObj)
|
||||
data += pack("<H", OT)
|
||||
data += pack("<H", id)
|
||||
data += pack("<H", grbit)
|
||||
data += pack("<H", colL)
|
||||
data += pack("<H", dxL)
|
||||
data += pack("<H", rwT)
|
||||
data += pack("<H", dyT)
|
||||
data += pack("<H", colR)
|
||||
data += pack("<H", dxR)
|
||||
data += pack("<H", rwB)
|
||||
data += pack("<H", dyB)
|
||||
data += pack("<H", cbMacro)
|
||||
data += pack("<L", Reserved1)
|
||||
data += pack("<H", Reserved2)
|
||||
data += pack("<B", icvBack)
|
||||
data += pack("<B", icvFore)
|
||||
data += pack("<B", fls)
|
||||
data += pack("<B", fAuto)
|
||||
data += pack("<B", icv)
|
||||
data += pack("<B", lns)
|
||||
data += pack("<B", lnw)
|
||||
data += pack("<B", fAutoB)
|
||||
data += pack("<H", frs)
|
||||
data += pack("<L", cf)
|
||||
data += pack("<H", Reserved3)
|
||||
data += pack("<H", cbPictFmla)
|
||||
data += pack("<H", Reserved4)
|
||||
data += pack("<H", grbit2)
|
||||
data += pack("<L", Reserved5)
|
||||
|
||||
self._rec_data = data
|
||||
|
||||
def _process_bitmap(bitmap):
|
||||
"""Convert a 24 bit bitmap into the modified internal format used by Windows.
|
||||
This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the
|
||||
MSDN library.
|
||||
|
||||
"""
|
||||
# Open file and binmode the data in case the platform needs it.
|
||||
fh = file(bitmap, "rb")
|
||||
try:
|
||||
# Slurp the file into a string.
|
||||
data = fh.read()
|
||||
finally:
|
||||
fh.close()
|
||||
# Check that the file is big enough to be a bitmap.
|
||||
if len(data) <= 0x36:
|
||||
raise Exception("bitmap doesn't contain enough data.")
|
||||
# The first 2 bytes are used to identify the bitmap.
|
||||
if (data[:2] != "BM"):
|
||||
raise Exception("bitmap doesn't appear to to be a valid bitmap image.")
|
||||
# Remove bitmap data: ID.
|
||||
data = data[2:]
|
||||
# Read and remove the bitmap size. This is more reliable than reading
|
||||
# the data size at offset 0x22.
|
||||
#
|
||||
size = unpack("<L", data[:4])[0]
|
||||
size -= 0x36 # Subtract size of bitmap header.
|
||||
size += 0x0C # Add size of BIFF header.
|
||||
data = data[4:]
|
||||
# Remove bitmap data: reserved, offset, header length.
|
||||
data = data[12:]
|
||||
# Read and remove the bitmap width and height. Verify the sizes.
|
||||
width, height = unpack("<LL", data[:8])
|
||||
data = data[8:]
|
||||
if (width > 0xFFFF):
|
||||
raise Exception("bitmap: largest image width supported is 65k.")
|
||||
if (height > 0xFFFF):
|
||||
raise Exception("bitmap: largest image height supported is 65k.")
|
||||
# Read and remove the bitmap planes and bpp data. Verify them.
|
||||
planes, bitcount = unpack("<HH", data[:4])
|
||||
data = data[4:]
|
||||
if (bitcount != 24):
|
||||
raise Exception("bitmap isn't a 24bit true color bitmap.")
|
||||
if (planes != 1):
|
||||
raise Exception("bitmap: only 1 plane supported in bitmap image.")
|
||||
# Read and remove the bitmap compression. Verify compression.
|
||||
compression = unpack("<L", data[:4])[0]
|
||||
data = data[4:]
|
||||
if (compression != 0):
|
||||
raise Exception("bitmap: compression not supported in bitmap image.")
|
||||
# Remove bitmap data: data size, hres, vres, colours, imp. colours.
|
||||
data = data[20:]
|
||||
# Add the BITMAPCOREHEADER data
|
||||
header = pack("<LHHHH", 0x000c, width, height, 0x01, 0x18)
|
||||
data = header + data
|
||||
return (width, height, size, data)
|
||||
|
||||
|
||||
class ImDataBmpRecord(BiffRecord):
|
||||
_REC_ID = 0x007F
|
||||
|
||||
def __init__(self, filename):
|
||||
"""Insert a 24bit bitmap image in a worksheet. The main record required is
|
||||
IMDATA but it must be proceeded by a OBJ record to define its position.
|
||||
|
||||
"""
|
||||
BiffRecord.__init__(self)
|
||||
|
||||
self.width, self.height, self.size, data = _process_bitmap(filename)
|
||||
# Write the IMDATA record to store the bitmap data
|
||||
cf = 0x09
|
||||
env = 0x01
|
||||
lcb = self.size
|
||||
self._rec_data = pack("<HHL", cf, env, lcb) + data
|
||||
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
# -*- coding: windows-1252 -*-
|
||||
|
||||
from struct import unpack, pack
|
||||
import BIFFRecords
|
||||
|
||||
class StrCell(object):
|
||||
__slots__ = ["rowx", "colx", "xf_idx", "sst_idx"]
|
||||
|
||||
def __init__(self, rowx, colx, xf_idx, sst_idx):
|
||||
self.rowx = rowx
|
||||
self.colx = colx
|
||||
self.xf_idx = xf_idx
|
||||
self.sst_idx = sst_idx
|
||||
|
||||
def get_biff_data(self):
|
||||
# return BIFFRecords.LabelSSTRecord(self.rowx, self.colx, self.xf_idx, self.sst_idx).get()
|
||||
return pack('<5HL', 0x00FD, 10, self.rowx, self.colx, self.xf_idx, self.sst_idx)
|
||||
|
||||
class BlankCell(object):
|
||||
__slots__ = ["rowx", "colx", "xf_idx"]
|
||||
|
||||
def __init__(self, rowx, colx, xf_idx):
|
||||
self.rowx = rowx
|
||||
self.colx = colx
|
||||
self.xf_idx = xf_idx
|
||||
|
||||
def get_biff_data(self):
|
||||
# return BIFFRecords.BlankRecord(self.rowx, self.colx, self.xf_idx).get()
|
||||
return pack('<5H', 0x0201, 6, self.rowx, self.colx, self.xf_idx)
|
||||
|
||||
class MulBlankCell(object):
|
||||
__slots__ = ["rowx", "colx1", "colx2", "xf_idx"]
|
||||
|
||||
def __init__(self, rowx, colx1, colx2, xf_idx):
|
||||
self.rowx = rowx
|
||||
self.colx1 = colx1
|
||||
self.colx2 = colx2
|
||||
self.xf_idx = xf_idx
|
||||
|
||||
def get_biff_data(self):
|
||||
return BIFFRecords.MulBlankRecord(self.rowx,
|
||||
self.colx1, self.colx2, self.xf_idx).get()
|
||||
|
||||
class NumberCell(object):
|
||||
__slots__ = ["rowx", "colx", "xf_idx", "number"]
|
||||
|
||||
def __init__(self, rowx, colx, xf_idx, number):
|
||||
self.rowx = rowx
|
||||
self.colx = colx
|
||||
self.xf_idx = xf_idx
|
||||
self.number = float(number)
|
||||
|
||||
def get_encoded_data(self):
|
||||
rk_encoded = 0
|
||||
num = self.number
|
||||
|
||||
# The four possible kinds of RK encoding are *not* mutually exclusive.
|
||||
# The 30-bit integer variety picks up the most.
|
||||
# In the code below, the four varieties are checked in descending order
|
||||
# of bangs per buck, or not at all.
|
||||
# SJM 2007-10-01
|
||||
|
||||
if -0x20000000 <= num < 0x20000000: # fits in 30-bit *signed* int
|
||||
inum = int(num)
|
||||
if inum == num: # survives round-trip
|
||||
# print "30-bit integer RK", inum, hex(inum)
|
||||
rk_encoded = 2 | (inum << 2)
|
||||
return 1, rk_encoded
|
||||
|
||||
temp = num * 100
|
||||
|
||||
if -0x20000000 <= temp < 0x20000000:
|
||||
# That was step 1: the coded value will fit in
|
||||
# a 30-bit signed integer.
|
||||
itemp = int(round(temp, 0))
|
||||
# That was step 2: "itemp" is the best candidate coded value.
|
||||
# Now for step 3: simulate the decoding,
|
||||
# to check for round-trip correctness.
|
||||
if itemp / 100.0 == num:
|
||||
# print "30-bit integer RK*100", itemp, hex(itemp)
|
||||
rk_encoded = 3 | (itemp << 2)
|
||||
return 1, rk_encoded
|
||||
|
||||
if 0: # Cost of extra pack+unpack not justified by tiny yield.
|
||||
packed = pack('<d', num)
|
||||
w01, w23 = unpack('<2i', packed)
|
||||
if not w01 and not(w23 & 3):
|
||||
# 34 lsb are 0
|
||||
# print "float RK", w23, hex(w23)
|
||||
return 1, w23
|
||||
|
||||
packed100 = pack('<d', temp)
|
||||
w01, w23 = unpack('<2i', packed100)
|
||||
if not w01 and not(w23 & 3):
|
||||
# 34 lsb are 0
|
||||
# print "float RK*100", w23, hex(w23)
|
||||
return 1, w23 | 1
|
||||
|
||||
#print "Number"
|
||||
#print
|
||||
return 0, pack('<5Hd', 0x0203, 14, self.rowx, self.colx, self.xf_idx, num)
|
||||
|
||||
def get_biff_data(self):
|
||||
isRK, value = self.get_encoded_data()
|
||||
if isRK:
|
||||
return pack('<5Hi', 0x27E, 10, self.rowx, self.colx, self.xf_idx, value)
|
||||
return value # NUMBER record already packed
|
||||
|
||||
class BooleanCell(object):
|
||||
__slots__ = ["rowx", "colx", "xf_idx", "number"]
|
||||
|
||||
def __init__(self, rowx, colx, xf_idx, number):
|
||||
self.rowx = rowx
|
||||
self.colx = colx
|
||||
self.xf_idx = xf_idx
|
||||
self.number = number
|
||||
|
||||
def get_biff_data(self):
|
||||
return BIFFRecords.BoolErrRecord(self.rowx,
|
||||
self.colx, self.xf_idx, self.number, 0).get()
|
||||
|
||||
error_code_map = {
|
||||
0x00: 0, # Intersection of two cell ranges is empty
|
||||
0x07: 7, # Division by zero
|
||||
0x0F: 15, # Wrong type of operand
|
||||
0x17: 23, # Illegal or deleted cell reference
|
||||
0x1D: 29, # Wrong function or range name
|
||||
0x24: 36, # Value range overflow
|
||||
0x2A: 42, # Argument or function not available
|
||||
'#NULL!' : 0, # Intersection of two cell ranges is empty
|
||||
'#DIV/0!': 7, # Division by zero
|
||||
'#VALUE!': 36, # Wrong type of operand
|
||||
'#REF!' : 23, # Illegal or deleted cell reference
|
||||
'#NAME?' : 29, # Wrong function or range name
|
||||
'#NUM!' : 36, # Value range overflow
|
||||
'#N/A!' : 42, # Argument or function not available
|
||||
}
|
||||
|
||||
class ErrorCell(object):
|
||||
__slots__ = ["rowx", "colx", "xf_idx", "number"]
|
||||
|
||||
def __init__(self, rowx, colx, xf_idx, error_string_or_code):
|
||||
self.rowx = rowx
|
||||
self.colx = colx
|
||||
self.xf_idx = xf_idx
|
||||
try:
|
||||
self.number = error_code_map[error_string_or_code]
|
||||
except KeyError:
|
||||
raise Exception('Illegal error value (%r)' % error_string_or_code)
|
||||
|
||||
def get_biff_data(self):
|
||||
return BIFFRecords.BoolErrRecord(self.rowx,
|
||||
self.colx, self.xf_idx, self.number, 1).get()
|
||||
|
||||
class FormulaCell(object):
|
||||
__slots__ = ["rowx", "colx", "xf_idx", "frmla", "calc_flags"]
|
||||
|
||||
def __init__(self, rowx, colx, xf_idx, frmla, calc_flags=0):
|
||||
self.rowx = rowx
|
||||
self.colx = colx
|
||||
self.xf_idx = xf_idx
|
||||
self.frmla = frmla
|
||||
self.calc_flags = calc_flags
|
||||
|
||||
def get_biff_data(self):
|
||||
return BIFFRecords.FormulaRecord(self.rowx,
|
||||
self.colx, self.xf_idx, self.frmla.rpn(), self.calc_flags).get()
|
||||
|
||||
# module-level function for *internal* use by the Row module
|
||||
|
||||
def _get_cells_biff_data_mul(rowx, cell_items):
|
||||
# Return the BIFF data for all cell records in the row.
|
||||
# Adjacent BLANK|RK records are combined into MUL(BLANK|RK) records.
|
||||
pieces = []
|
||||
nitems = len(cell_items)
|
||||
i = 0
|
||||
while i < nitems:
|
||||
icolx, icell = cell_items[i]
|
||||
if isinstance(icell, NumberCell):
|
||||
isRK, value = icell.get_encoded_data()
|
||||
if not isRK:
|
||||
pieces.append(value) # pre-packed NUMBER record
|
||||
i += 1
|
||||
continue
|
||||
muldata = [(value, icell.xf_idx)]
|
||||
target = NumberCell
|
||||
elif isinstance(icell, BlankCell):
|
||||
muldata = [icell.xf_idx]
|
||||
target = BlankCell
|
||||
else:
|
||||
pieces.append(icell.get_biff_data())
|
||||
i += 1
|
||||
continue
|
||||
lastcolx = icolx
|
||||
j = i
|
||||
packed_record = ''
|
||||
for j in xrange(i+1, nitems):
|
||||
jcolx, jcell = cell_items[j]
|
||||
if jcolx != lastcolx + 1:
|
||||
nexti = j
|
||||
break
|
||||
if not isinstance(jcell, target):
|
||||
nexti = j
|
||||
break
|
||||
if target == NumberCell:
|
||||
isRK, value = jcell.get_encoded_data()
|
||||
if not isRK:
|
||||
packed_record = value
|
||||
nexti = j + 1
|
||||
break
|
||||
muldata.append((value, jcell.xf_idx))
|
||||
else:
|
||||
muldata.append(jcell.xf_idx)
|
||||
lastcolx = jcolx
|
||||
else:
|
||||
nexti = j + 1
|
||||
if target == NumberCell:
|
||||
if lastcolx == icolx:
|
||||
# RK record
|
||||
value, xf_idx = muldata[0]
|
||||
pieces.append(pack('<5Hi', 0x027E, 10, rowx, icolx, xf_idx, value))
|
||||
else:
|
||||
# MULRK record
|
||||
nc = lastcolx - icolx + 1
|
||||
pieces.append(pack('<4H', 0x00BD, 6 * nc + 6, rowx, icolx))
|
||||
pieces.append(''.join([pack('<Hi', xf_idx, value) for value, xf_idx in muldata]))
|
||||
pieces.append(pack('<H', lastcolx))
|
||||
else:
|
||||
if lastcolx == icolx:
|
||||
# BLANK record
|
||||
xf_idx = muldata[0]
|
||||
pieces.append(pack('<5H', 0x0201, 6, rowx, icolx, xf_idx))
|
||||
else:
|
||||
# MULBLANK record
|
||||
nc = lastcolx - icolx + 1
|
||||
pieces.append(pack('<4H', 0x00BE, 2 * nc + 6, rowx, icolx))
|
||||
pieces.append(''.join([pack('<H', xf_idx) for xf_idx in muldata]))
|
||||
pieces.append(pack('<H', lastcolx))
|
||||
if packed_record:
|
||||
pieces.append(packed_record)
|
||||
i = nexti
|
||||
return ''.join(pieces)
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
# -*- coding: windows-1252 -*-
|
||||
|
||||
from BIFFRecords import ColInfoRecord
|
||||
|
||||
class Column(object):
|
||||
def __init__(self, colx, parent_sheet):
|
||||
if not(isinstance(colx, int) and 0 <= colx <= 255):
|
||||
raise ValueError("column index (%r) not an int in range(256)" % colx)
|
||||
self._index = colx
|
||||
self._parent = parent_sheet
|
||||
self._parent_wb = parent_sheet.get_parent()
|
||||
self._xf_index = 0x0F
|
||||
|
||||
self.width = 0x0B92
|
||||
self.hidden = 0
|
||||
self.level = 0
|
||||
self.collapse = 0
|
||||
|
||||
def set_style(self, style):
|
||||
self._xf_index = self._parent_wb.add_style(style)
|
||||
|
||||
def width_in_pixels(self):
|
||||
# *** Approximation ****
|
||||
return int(round(self.width * 0.0272 + 0.446, 0))
|
||||
|
||||
def get_biff_record(self):
|
||||
options = (self.hidden & 0x01) << 0
|
||||
options |= (self.level & 0x07) << 8
|
||||
options |= (self.collapse & 0x01) << 12
|
||||
|
||||
return ColInfoRecord(self._index, self._index, self.width, self._xf_index, options).get()
|
||||
|
||||
|
||||
|
||||
@@ -1,516 +0,0 @@
|
||||
# -*- coding: windows-1252 -*-
|
||||
|
||||
import sys
|
||||
import struct
|
||||
|
||||
class Reader:
|
||||
def __init__(self, filename, dump = False):
|
||||
self.dump = dump
|
||||
self.STREAMS = {}
|
||||
|
||||
doc = file(filename, 'rb').read()
|
||||
self.header, self.data = doc[0:512], doc[512:]
|
||||
del doc
|
||||
|
||||
self.__build_header()
|
||||
self.__build_MSAT()
|
||||
self.__build_SAT()
|
||||
self.__build_directory()
|
||||
self.__build_short_sectors_data()
|
||||
|
||||
if len(self.short_sectors_data) > 0:
|
||||
self.__build_SSAT()
|
||||
else:
|
||||
if self.dump and (self.total_ssat_sectors != 0 or self.ssat_start_sid != -2):
|
||||
print 'NOTE: header says that must be', self.total_ssat_sectors, 'short sectors'
|
||||
print 'NOTE: starting at', self.ssat_start_sid, 'sector'
|
||||
print 'NOTE: but file does not contains data in short sectors'
|
||||
self.ssat_start_sid = -2
|
||||
self.total_ssat_sectors = 0
|
||||
self.SSAT = [-2]
|
||||
|
||||
for dentry in self.dir_entry_list[1:]:
|
||||
(did,
|
||||
sz, name,
|
||||
t, c,
|
||||
did_left, did_right, did_root,
|
||||
dentry_start_sid,
|
||||
stream_size
|
||||
) = dentry
|
||||
stream_data = ''
|
||||
if stream_size > 0:
|
||||
if stream_size >= self.min_stream_size:
|
||||
args = (self.data, self.SAT, dentry_start_sid, self.sect_size)
|
||||
else:
|
||||
args = (self.short_sectors_data, self.SSAT, dentry_start_sid, self.short_sect_size)
|
||||
stream_data = self.get_stream_data(*args)
|
||||
|
||||
if name != '':
|
||||
# BAD IDEA: names may be equal. NEED use full paths...
|
||||
self.STREAMS[name] = stream_data
|
||||
|
||||
|
||||
def __build_header(self):
|
||||
self.doc_magic = self.header[0:8]
|
||||
|
||||
if self.doc_magic != '\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1':
|
||||
raise Exception, 'Not an OLE file.'
|
||||
|
||||
self.file_uid = self.header[8:24]
|
||||
self.rev_num = self.header[24:26]
|
||||
self.ver_num = self.header[26:28]
|
||||
self.byte_order = self.header[28:30]
|
||||
self.log2_sect_size, = struct.unpack('<H', self.header[30:32])
|
||||
self.log2_short_sect_size, = struct.unpack('<H', self.header[32:34])
|
||||
self.total_sat_sectors, = struct.unpack('<L', self.header[44:48])
|
||||
self.dir_start_sid, = struct.unpack('<l', self.header[48:52])
|
||||
self.min_stream_size, = struct.unpack('<L', self.header[56:60])
|
||||
self.ssat_start_sid, = struct.unpack('<l', self.header[60:64])
|
||||
self.total_ssat_sectors, = struct.unpack('<L', self.header[64:68])
|
||||
self.msat_start_sid, = struct.unpack('<l', self.header[68:72])
|
||||
self.total_msat_sectors, = struct.unpack('<L', self.header[72:76])
|
||||
|
||||
self.sect_size = 1 << self.log2_sect_size
|
||||
self.short_sect_size = 1 << self.log2_short_sect_size
|
||||
|
||||
if self.dump:
|
||||
print 'file magic: '
|
||||
print_bin_data(self.doc_magic)
|
||||
|
||||
print 'file uid: '
|
||||
print_bin_data(self.file_uid)
|
||||
|
||||
print 'revision number: '
|
||||
print_bin_data(self.rev_num)
|
||||
|
||||
print 'version number: '
|
||||
print_bin_data(self.ver_num)
|
||||
|
||||
print 'byte order: '
|
||||
print_bin_data(self.byte_order)
|
||||
|
||||
print 'sector size :', hex(self.sect_size), self.sect_size
|
||||
#print 'total sectors in file :', hex(self.total_sectors), self.total_sectors
|
||||
print 'short sector size :', hex(self.short_sect_size), self.short_sect_size
|
||||
print 'Total number of sectors used for the SAT :', hex(self.total_sat_sectors), self.total_sat_sectors
|
||||
print 'SID of first sector of the directory stream:', hex(self.dir_start_sid), self.dir_start_sid
|
||||
print 'Minimum size of a standard stream :', hex(self.min_stream_size), self.min_stream_size
|
||||
print 'SID of first sector of the SSAT :', hex(self.ssat_start_sid), self.ssat_start_sid
|
||||
print 'Total number of sectors used for the SSAT :', hex(self.total_ssat_sectors), self.total_ssat_sectors
|
||||
print 'SID of first additional sector of the MSAT :', hex(self.msat_start_sid), self.msat_start_sid
|
||||
print 'Total number of sectors used for the MSAT :', hex(self.total_msat_sectors), self.total_msat_sectors
|
||||
|
||||
|
||||
def __build_MSAT(self):
|
||||
self.MSAT = list(struct.unpack('<109l', self.header[76:]))
|
||||
|
||||
next = self.msat_start_sid
|
||||
while next > 0:
|
||||
msat_sector = struct.unpack('<128l', self.data[next*self.sect_size:(next+1)*self.sect_size])
|
||||
self.MSAT.extend(msat_sector[:127])
|
||||
next = msat_sector[-1]
|
||||
|
||||
if self.dump:
|
||||
print 'MSAT (header part): \n', self.MSAT[:109]
|
||||
print 'additional MSAT sectors: \n', self.MSAT[109:]
|
||||
|
||||
|
||||
def __build_SAT(self):
|
||||
sat_stream = ''.join([self.data[i*self.sect_size:(i+1)*self.sect_size] for i in self.MSAT if i >= 0])
|
||||
|
||||
sat_sids_count = len(sat_stream) >> 2
|
||||
self.SAT = struct.unpack('<%dl' % sat_sids_count, sat_stream) # SIDs tuple
|
||||
|
||||
if self.dump:
|
||||
print 'SAT sid count:\n', sat_sids_count
|
||||
print 'SAT content:\n', self.SAT
|
||||
|
||||
|
||||
def __build_SSAT(self):
|
||||
ssat_stream = self.get_stream_data(self.data, self.SAT, self.ssat_start_sid, self.sect_size)
|
||||
|
||||
ssids_count = len(ssat_stream) >> 2
|
||||
self.SSAT = struct.unpack('<%dl' % ssids_count, ssat_stream)
|
||||
|
||||
if self.dump:
|
||||
print 'SSID count:', ssids_count
|
||||
print 'SSAT content:\n', self.SSAT
|
||||
|
||||
|
||||
def __build_directory(self):
|
||||
dir_stream = self.get_stream_data(self.data, self.SAT, self.dir_start_sid, self.sect_size)
|
||||
|
||||
self.dir_entry_list = []
|
||||
|
||||
i = 0
|
||||
while i < len(dir_stream):
|
||||
dentry = dir_stream[i:i+128] # 128 -- dir entry size
|
||||
i += 128
|
||||
|
||||
did = len(self.dir_entry_list)
|
||||
sz, = struct.unpack('<H', dentry[64:66])
|
||||
if sz > 0 :
|
||||
name = dentry[0:sz-2].decode('utf_16_le', 'replace')
|
||||
else:
|
||||
name = u''
|
||||
t, = struct.unpack('B', dentry[66])
|
||||
c, = struct.unpack('B', dentry[67])
|
||||
did_left , = struct.unpack('<l', dentry[68:72])
|
||||
did_right , = struct.unpack('<l', dentry[72:76])
|
||||
did_root , = struct.unpack('<l', dentry[76:80])
|
||||
dentry_start_sid , = struct.unpack('<l', dentry[116:120])
|
||||
stream_size , = struct.unpack('<L', dentry[120:124])
|
||||
|
||||
self.dir_entry_list.extend([(did, sz, name, t, c,
|
||||
did_left, did_right, did_root,
|
||||
dentry_start_sid, stream_size)])
|
||||
|
||||
if self.dump:
|
||||
dentry_types = {
|
||||
0x00: 'Empty',
|
||||
0x01: 'User storage',
|
||||
0x02: 'User stream',
|
||||
0x03: 'LockBytes',
|
||||
0x04: 'Property',
|
||||
0x05: 'Root storage'
|
||||
}
|
||||
node_colours = {
|
||||
0x00: 'Red',
|
||||
0x01: 'Black'
|
||||
}
|
||||
print 'total directory entries:', len(self.dir_entry_list)
|
||||
|
||||
for dentry in self.dir_entry_list:
|
||||
(did, sz, name, t, c,
|
||||
did_left, did_right, did_root,
|
||||
dentry_start_sid, stream_size) = dentry
|
||||
print 'DID', did
|
||||
print 'Size of the used area of the character buffer of the name:', sz
|
||||
print 'dir entry name:', repr(name)
|
||||
print 'type of entry:', t, dentry_types[t]
|
||||
print 'entry colour:', c, node_colours[c]
|
||||
print 'left child DID :', did_left
|
||||
print 'right child DID:', did_right
|
||||
print 'root DID :', did_root
|
||||
print 'start SID :', dentry_start_sid
|
||||
print 'stream size :', stream_size
|
||||
if stream_size == 0:
|
||||
print 'stream is empty'
|
||||
elif stream_size >= self.min_stream_size:
|
||||
print 'stream stored as normal stream'
|
||||
else:
|
||||
print 'stream stored as short-stream'
|
||||
|
||||
|
||||
def __build_short_sectors_data(self):
|
||||
(did, sz, name, t, c,
|
||||
did_left, did_right, did_root,
|
||||
dentry_start_sid, stream_size) = self.dir_entry_list[0]
|
||||
assert t == 0x05 # Short-Stream Container Stream (SSCS) resides in Root Storage
|
||||
if stream_size == 0:
|
||||
self.short_sectors_data = ''
|
||||
else:
|
||||
self.short_sectors_data = self.get_stream_data(self.data, self.SAT, dentry_start_sid, self.sect_size)
|
||||
|
||||
|
||||
def get_stream_data(self, data, SAT, start_sid, sect_size):
|
||||
sid = start_sid
|
||||
chunks = [(sid, sid)]
|
||||
stream_data = ''
|
||||
|
||||
while SAT[sid] >= 0:
|
||||
next_in_chain = SAT[sid]
|
||||
last_chunk_start, last_chunk_finish = chunks[-1]
|
||||
if next_in_chain == last_chunk_finish + 1:
|
||||
chunks[-1] = last_chunk_start, next_in_chain
|
||||
else:
|
||||
chunks.extend([(next_in_chain, next_in_chain)])
|
||||
sid = next_in_chain
|
||||
for s, f in chunks:
|
||||
stream_data += data[s*sect_size:(f+1)*sect_size]
|
||||
#print chunks
|
||||
return stream_data
|
||||
|
||||
|
||||
def print_bin_data(data):
|
||||
i = 0
|
||||
while i < len(data):
|
||||
j = 0
|
||||
while (i < len(data)) and (j < 16):
|
||||
c = '0x%02X' % ord(data[i])
|
||||
sys.stdout.write(c)
|
||||
sys.stdout.write(' ')
|
||||
i += 1
|
||||
j += 1
|
||||
print
|
||||
if i == 0:
|
||||
print '<NO DATA>'
|
||||
|
||||
|
||||
|
||||
# This implementation writes only 'Root Entry', 'Workbook' streams
|
||||
# and 2 empty streams for aligning directory stream on sector boundary
|
||||
#
|
||||
# LAYOUT:
|
||||
# 0 header
|
||||
# 76 MSAT (1st part: 109 SID)
|
||||
# 512 workbook stream
|
||||
# ... additional MSAT sectors if streams' size > about 7 Mb == (109*512 * 128)
|
||||
# ... SAT
|
||||
# ... directory stream
|
||||
#
|
||||
# NOTE: this layout is "ad hoc". It can be more general. RTFM
|
||||
|
||||
class XlsDoc:
|
||||
SECTOR_SIZE = 0x0200
|
||||
MIN_LIMIT = 0x1000
|
||||
|
||||
SID_FREE_SECTOR = -1
|
||||
SID_END_OF_CHAIN = -2
|
||||
SID_USED_BY_SAT = -3
|
||||
SID_USED_BY_MSAT = -4
|
||||
|
||||
def __init__(self):
|
||||
#self.book_stream = '' # padded
|
||||
self.book_stream_sect = []
|
||||
|
||||
self.dir_stream = ''
|
||||
self.dir_stream_sect = []
|
||||
|
||||
self.packed_SAT = ''
|
||||
self.SAT_sect = []
|
||||
|
||||
self.packed_MSAT_1st = ''
|
||||
self.packed_MSAT_2nd = ''
|
||||
self.MSAT_sect_2nd = []
|
||||
|
||||
self.header = ''
|
||||
|
||||
def __build_directory(self): # align on sector boundary
|
||||
self.dir_stream = ''
|
||||
|
||||
dentry_name = '\x00'.join('Root Entry\x00') + '\x00'
|
||||
dentry_name_sz = len(dentry_name)
|
||||
dentry_name_pad = '\x00'*(64 - dentry_name_sz)
|
||||
dentry_type = 0x05 # root storage
|
||||
dentry_colour = 0x01 # black
|
||||
dentry_did_left = -1
|
||||
dentry_did_right = -1
|
||||
dentry_did_root = 1
|
||||
dentry_start_sid = -2
|
||||
dentry_stream_sz = 0
|
||||
|
||||
self.dir_stream += struct.pack('<64s H 2B 3l 9L l L L',
|
||||
dentry_name + dentry_name_pad,
|
||||
dentry_name_sz,
|
||||
dentry_type,
|
||||
dentry_colour,
|
||||
dentry_did_left,
|
||||
dentry_did_right,
|
||||
dentry_did_root,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
dentry_start_sid,
|
||||
dentry_stream_sz,
|
||||
0
|
||||
)
|
||||
|
||||
dentry_name = '\x00'.join('Workbook\x00') + '\x00'
|
||||
dentry_name_sz = len(dentry_name)
|
||||
dentry_name_pad = '\x00'*(64 - dentry_name_sz)
|
||||
dentry_type = 0x02 # user stream
|
||||
dentry_colour = 0x01 # black
|
||||
dentry_did_left = -1
|
||||
dentry_did_right = -1
|
||||
dentry_did_root = -1
|
||||
dentry_start_sid = 0
|
||||
dentry_stream_sz = self.book_stream_len
|
||||
|
||||
self.dir_stream += struct.pack('<64s H 2B 3l 9L l L L',
|
||||
dentry_name + dentry_name_pad,
|
||||
dentry_name_sz,
|
||||
dentry_type,
|
||||
dentry_colour,
|
||||
dentry_did_left,
|
||||
dentry_did_right,
|
||||
dentry_did_root,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
dentry_start_sid,
|
||||
dentry_stream_sz,
|
||||
0
|
||||
)
|
||||
|
||||
# padding
|
||||
dentry_name = ''
|
||||
dentry_name_sz = len(dentry_name)
|
||||
dentry_name_pad = '\x00'*(64 - dentry_name_sz)
|
||||
dentry_type = 0x00 # empty
|
||||
dentry_colour = 0x01 # black
|
||||
dentry_did_left = -1
|
||||
dentry_did_right = -1
|
||||
dentry_did_root = -1
|
||||
dentry_start_sid = -2
|
||||
dentry_stream_sz = 0
|
||||
|
||||
self.dir_stream += struct.pack('<64s H 2B 3l 9L l L L',
|
||||
dentry_name + dentry_name_pad,
|
||||
dentry_name_sz,
|
||||
dentry_type,
|
||||
dentry_colour,
|
||||
dentry_did_left,
|
||||
dentry_did_right,
|
||||
dentry_did_root,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
dentry_start_sid,
|
||||
dentry_stream_sz,
|
||||
0
|
||||
) * 2
|
||||
|
||||
def __build_sat(self):
|
||||
# Build SAT
|
||||
book_sect_count = self.book_stream_len >> 9
|
||||
dir_sect_count = len(self.dir_stream) >> 9
|
||||
|
||||
total_sect_count = book_sect_count + dir_sect_count
|
||||
SAT_sect_count = 0
|
||||
MSAT_sect_count = 0
|
||||
SAT_sect_count_limit = 109
|
||||
while total_sect_count > 128*SAT_sect_count or SAT_sect_count > SAT_sect_count_limit:
|
||||
SAT_sect_count += 1
|
||||
total_sect_count += 1
|
||||
if SAT_sect_count > SAT_sect_count_limit:
|
||||
MSAT_sect_count += 1
|
||||
total_sect_count += 1
|
||||
SAT_sect_count_limit += 127
|
||||
|
||||
|
||||
SAT = [self.SID_FREE_SECTOR]*128*SAT_sect_count
|
||||
|
||||
sect = 0
|
||||
while sect < book_sect_count - 1:
|
||||
self.book_stream_sect.append(sect)
|
||||
SAT[sect] = sect + 1
|
||||
sect += 1
|
||||
self.book_stream_sect.append(sect)
|
||||
SAT[sect] = self.SID_END_OF_CHAIN
|
||||
sect += 1
|
||||
|
||||
while sect < book_sect_count + MSAT_sect_count:
|
||||
self.MSAT_sect_2nd.append(sect)
|
||||
SAT[sect] = self.SID_USED_BY_MSAT
|
||||
sect += 1
|
||||
|
||||
while sect < book_sect_count + MSAT_sect_count + SAT_sect_count:
|
||||
self.SAT_sect.append(sect)
|
||||
SAT[sect] = self.SID_USED_BY_SAT
|
||||
sect += 1
|
||||
|
||||
while sect < book_sect_count + MSAT_sect_count + SAT_sect_count + dir_sect_count - 1:
|
||||
self.dir_stream_sect.append(sect)
|
||||
SAT[sect] = sect + 1
|
||||
sect += 1
|
||||
self.dir_stream_sect.append(sect)
|
||||
SAT[sect] = self.SID_END_OF_CHAIN
|
||||
sect += 1
|
||||
|
||||
self.packed_SAT = struct.pack('<%dl' % (SAT_sect_count*128), *SAT)
|
||||
|
||||
MSAT_1st = [self.SID_FREE_SECTOR]*109
|
||||
for i, SAT_sect_num in zip(range(0, 109), self.SAT_sect):
|
||||
MSAT_1st[i] = SAT_sect_num
|
||||
self.packed_MSAT_1st = struct.pack('<109l', *MSAT_1st)
|
||||
|
||||
MSAT_2nd = [self.SID_FREE_SECTOR]*128*MSAT_sect_count
|
||||
if MSAT_sect_count > 0:
|
||||
MSAT_2nd[- 1] = self.SID_END_OF_CHAIN
|
||||
|
||||
i = 109
|
||||
msat_sect = 0
|
||||
sid_num = 0
|
||||
while i < SAT_sect_count:
|
||||
if (sid_num + 1) % 128 == 0:
|
||||
#print 'link: ',
|
||||
msat_sect += 1
|
||||
if msat_sect < len(self.MSAT_sect_2nd):
|
||||
MSAT_2nd[sid_num] = self.MSAT_sect_2nd[msat_sect]
|
||||
else:
|
||||
#print 'sid: ',
|
||||
MSAT_2nd[sid_num] = self.SAT_sect[i]
|
||||
i += 1
|
||||
#print sid_num, MSAT_2nd[sid_num]
|
||||
sid_num += 1
|
||||
|
||||
self.packed_MSAT_2nd = struct.pack('<%dl' % (MSAT_sect_count*128), *MSAT_2nd)
|
||||
|
||||
#print vars()
|
||||
#print zip(range(0, sect), SAT)
|
||||
#print self.book_stream_sect
|
||||
#print self.MSAT_sect_2nd
|
||||
#print MSAT_2nd
|
||||
#print self.SAT_sect
|
||||
#print self.dir_stream_sect
|
||||
|
||||
|
||||
def __build_header(self):
|
||||
doc_magic = '\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1'
|
||||
file_uid = '\x00'*16
|
||||
rev_num = '\x3E\x00'
|
||||
ver_num = '\x03\x00'
|
||||
byte_order = '\xFE\xFF'
|
||||
log_sect_size = struct.pack('<H', 9)
|
||||
log_short_sect_size = struct.pack('<H', 6)
|
||||
not_used0 = '\x00'*10
|
||||
total_sat_sectors = struct.pack('<L', len(self.SAT_sect))
|
||||
dir_start_sid = struct.pack('<l', self.dir_stream_sect[0])
|
||||
not_used1 = '\x00'*4
|
||||
min_stream_size = struct.pack('<L', 0x1000)
|
||||
ssat_start_sid = struct.pack('<l', -2)
|
||||
total_ssat_sectors = struct.pack('<L', 0)
|
||||
|
||||
if len(self.MSAT_sect_2nd) == 0:
|
||||
msat_start_sid = struct.pack('<l', -2)
|
||||
else:
|
||||
msat_start_sid = struct.pack('<l', self.MSAT_sect_2nd[0])
|
||||
|
||||
total_msat_sectors = struct.pack('<L', len(self.MSAT_sect_2nd))
|
||||
|
||||
self.header = ''.join([ doc_magic,
|
||||
file_uid,
|
||||
rev_num,
|
||||
ver_num,
|
||||
byte_order,
|
||||
log_sect_size,
|
||||
log_short_sect_size,
|
||||
not_used0,
|
||||
total_sat_sectors,
|
||||
dir_start_sid,
|
||||
not_used1,
|
||||
min_stream_size,
|
||||
ssat_start_sid,
|
||||
total_ssat_sectors,
|
||||
msat_start_sid,
|
||||
total_msat_sectors
|
||||
])
|
||||
|
||||
|
||||
def save(self, file_name_or_filelike_obj, stream):
|
||||
# 1. Align stream on 0x1000 boundary (and therefore on sector boundary)
|
||||
padding = '\x00' * (0x1000 - (len(stream) % 0x1000))
|
||||
self.book_stream_len = len(stream) + len(padding)
|
||||
|
||||
self.__build_directory()
|
||||
self.__build_sat()
|
||||
self.__build_header()
|
||||
|
||||
f = file_name_or_filelike_obj
|
||||
we_own_it = not hasattr(f, 'write')
|
||||
if we_own_it:
|
||||
f = open(file_name_or_filelike_obj, 'wb')
|
||||
f.write(self.header)
|
||||
f.write(self.packed_MSAT_1st)
|
||||
f.write(stream)
|
||||
f.write(padding)
|
||||
f.write(self.packed_MSAT_2nd)
|
||||
f.write(self.packed_SAT)
|
||||
f.write(self.dir_stream)
|
||||
if we_own_it:
|
||||
f.close()
|
||||
@@ -1,43 +0,0 @@
|
||||
# -*- coding: windows-1252 -*-
|
||||
|
||||
import ExcelFormulaParser, ExcelFormulaLexer
|
||||
import struct
|
||||
from antlr import ANTLRException
|
||||
|
||||
|
||||
class Formula(object):
|
||||
__slots__ = ["__init__", "__s", "__parser", "__sheet_refs", "__xcall_refs"]
|
||||
|
||||
|
||||
def __init__(self, s):
|
||||
try:
|
||||
self.__s = s
|
||||
lexer = ExcelFormulaLexer.Lexer(s)
|
||||
self.__parser = ExcelFormulaParser.Parser(lexer)
|
||||
self.__parser.formula()
|
||||
self.__sheet_refs = self.__parser.sheet_references
|
||||
self.__xcall_refs = self.__parser.xcall_references
|
||||
except ANTLRException, e:
|
||||
# print e
|
||||
raise ExcelFormulaParser.FormulaParseException, "can't parse formula " + s
|
||||
|
||||
def get_references(self):
|
||||
return self.__sheet_refs, self.__xcall_refs
|
||||
|
||||
def patch_references(self, patches):
|
||||
for offset, idx in patches:
|
||||
self.__parser.rpn = self.__parser.rpn[:offset] + struct.pack('<H', idx) + self.__parser.rpn[offset+2:]
|
||||
|
||||
def text(self):
|
||||
return self.__s
|
||||
|
||||
def rpn(self):
|
||||
'''
|
||||
Offset Size Contents
|
||||
0 2 Size of the following formula data (sz)
|
||||
2 sz Formula data (RPN token array)
|
||||
[2+sz] var. (optional) Additional data for specific tokens
|
||||
|
||||
'''
|
||||
return struct.pack("<H", len(self.__parser.rpn)) + self.__parser.rpn
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
# -*- coding: windows-1252 -*-
|
||||
|
||||
import sys
|
||||
from antlr import EOF, CommonToken as Tok, TokenStream, TokenStreamException
|
||||
import struct
|
||||
import ExcelFormulaParser
|
||||
from re import compile as recompile, match, UNICODE, IGNORECASE, VERBOSE
|
||||
|
||||
|
||||
int_const_pattern = r"\d+\b"
|
||||
flt_const_pattern = r"""
|
||||
(?:
|
||||
(?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc
|
||||
|
|
||||
(?: \d+ \. ) # 1. 12. 123. etc
|
||||
)
|
||||
# followed by optional exponent part
|
||||
(?: [Ee] [+-]? \d+ ) ?
|
||||
"""
|
||||
str_const_pattern = r'"(?:[^"]|"")*"'
|
||||
#range2d_pattern = recompile(r"\$?[A-I]?[A-Z]\$?\d+:\$?[A-I]?[A-Z]\$?\d+"
|
||||
ref2d_r1c1_pattern = r"[Rr]0*[1-9][0-9]*[Cc]0*[1-9][0-9]*"
|
||||
ref2d_pattern = r"\$?[A-I]?[A-Z]\$?0*[1-9][0-9]*"
|
||||
true_pattern = r"TRUE\b"
|
||||
false_pattern = r"FALSE\b"
|
||||
if_pattern = r"IF\b"
|
||||
choose_pattern = r"CHOOSE\b"
|
||||
name_pattern = r"\w[\.\w]*"
|
||||
quotename_pattern = r"'(?:[^']|'')*'" #### It's essential that this bracket be non-grouping.
|
||||
ne_pattern = r"<>"
|
||||
ge_pattern = r">="
|
||||
le_pattern = r"<="
|
||||
|
||||
pattern_type_tuples = (
|
||||
(flt_const_pattern, ExcelFormulaParser.NUM_CONST),
|
||||
(int_const_pattern, ExcelFormulaParser.INT_CONST),
|
||||
(str_const_pattern, ExcelFormulaParser.STR_CONST),
|
||||
# (range2d_pattern , ExcelFormulaParser.RANGE2D),
|
||||
(ref2d_r1c1_pattern, ExcelFormulaParser.REF2D_R1C1),
|
||||
(ref2d_pattern , ExcelFormulaParser.REF2D),
|
||||
(true_pattern , ExcelFormulaParser.TRUE_CONST),
|
||||
(false_pattern , ExcelFormulaParser.FALSE_CONST),
|
||||
(if_pattern , ExcelFormulaParser.FUNC_IF),
|
||||
(choose_pattern , ExcelFormulaParser.FUNC_CHOOSE),
|
||||
(name_pattern , ExcelFormulaParser.NAME),
|
||||
(quotename_pattern, ExcelFormulaParser.QUOTENAME),
|
||||
(ne_pattern, ExcelFormulaParser.NE),
|
||||
(ge_pattern, ExcelFormulaParser.GE),
|
||||
(le_pattern, ExcelFormulaParser.LE),
|
||||
)
|
||||
|
||||
_re = recompile(
|
||||
'(' + ')|('.join([i[0] for i in pattern_type_tuples]) + ')',
|
||||
VERBOSE+IGNORECASE)
|
||||
|
||||
_toktype = [None] + [i[1] for i in pattern_type_tuples]
|
||||
# need dummy at start because re.MatchObject.lastindex counts from 1
|
||||
|
||||
single_char_lookup = {
|
||||
'=': ExcelFormulaParser.EQ,
|
||||
'<': ExcelFormulaParser.LT,
|
||||
'>': ExcelFormulaParser.GT,
|
||||
'+': ExcelFormulaParser.ADD,
|
||||
'-': ExcelFormulaParser.SUB,
|
||||
'*': ExcelFormulaParser.MUL,
|
||||
'/': ExcelFormulaParser.DIV,
|
||||
':': ExcelFormulaParser.COLON,
|
||||
';': ExcelFormulaParser.SEMICOLON,
|
||||
',': ExcelFormulaParser.COMMA,
|
||||
'(': ExcelFormulaParser.LP,
|
||||
')': ExcelFormulaParser.RP,
|
||||
'&': ExcelFormulaParser.CONCAT,
|
||||
'%': ExcelFormulaParser.PERCENT,
|
||||
'^': ExcelFormulaParser.POWER,
|
||||
'!': ExcelFormulaParser.BANG,
|
||||
}
|
||||
|
||||
class Lexer(TokenStream):
|
||||
def __init__(self, text):
|
||||
self._text = text[:]
|
||||
self._pos = 0
|
||||
self._line = 0
|
||||
|
||||
def isEOF(self):
|
||||
return len(self._text) <= self._pos
|
||||
|
||||
def curr_ch(self):
|
||||
return self._text[self._pos]
|
||||
|
||||
def next_ch(self, n = 1):
|
||||
self._pos += n
|
||||
|
||||
def is_whitespace(self):
|
||||
return self.curr_ch() in " \t\n\r\f\v"
|
||||
|
||||
def match_pattern(self):
|
||||
m = _re.match(self._text, self._pos)
|
||||
if not m:
|
||||
return None
|
||||
self._pos = m.end(0)
|
||||
return Tok(type = _toktype[m.lastindex], text = m.group(0), col = m.start(0) + 1)
|
||||
|
||||
def nextToken(self):
|
||||
# skip whitespace
|
||||
while not self.isEOF() and self.is_whitespace():
|
||||
self.next_ch()
|
||||
if self.isEOF():
|
||||
return Tok(type = EOF)
|
||||
# first, try to match token with 2 or more chars
|
||||
t = self.match_pattern()
|
||||
if t:
|
||||
return t
|
||||
# second, we want 1-char tokens
|
||||
te = self.curr_ch()
|
||||
try:
|
||||
ty = single_char_lookup[te]
|
||||
except KeyError:
|
||||
raise TokenStreamException(
|
||||
"Unexpected char %r in column %u." % (self.curr_ch(), self._pos))
|
||||
self.next_ch()
|
||||
return Tok(type=ty, text=te, col=self._pos)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
for t in Lexer(""" 1.23 456 "abcd" R2C2 a1 iv65536 true false if choose a_name 'qname' <> >= <= """):
|
||||
print t
|
||||
except TokenStreamException, e:
|
||||
print "error:", e
|
||||
@@ -1,677 +0,0 @@
|
||||
### $ANTLR 2.7.7 (20060930): "xlwt/excel-formula.g" -> "ExcelFormulaParser.py"$
|
||||
### import antlr and other modules ..
|
||||
import sys
|
||||
import antlr
|
||||
|
||||
version = sys.version.split()[0]
|
||||
if version < '2.2.1':
|
||||
False = 0
|
||||
if version < '2.3':
|
||||
True = not False
|
||||
### header action >>>
|
||||
import struct
|
||||
import Utils
|
||||
from UnicodeUtils import upack1
|
||||
from ExcelMagic import *
|
||||
|
||||
_RVAdelta = {"R": 0, "V": 0x20, "A": 0x40}
|
||||
_RVAdeltaRef = {"R": 0, "V": 0x20, "A": 0x40, "D": 0x20}
|
||||
_RVAdeltaArea = {"R": 0, "V": 0x20, "A": 0x40, "D": 0}
|
||||
|
||||
|
||||
class FormulaParseException(Exception):
|
||||
"""
|
||||
An exception indicating that a Formula could not be successfully parsed.
|
||||
"""
|
||||
### header action <<<
|
||||
### preamble action>>>
|
||||
|
||||
### preamble action <<<
|
||||
|
||||
### import antlr.Token
|
||||
from antlr import Token
|
||||
### >>>The Known Token Types <<<
|
||||
SKIP = antlr.SKIP
|
||||
INVALID_TYPE = antlr.INVALID_TYPE
|
||||
EOF_TYPE = antlr.EOF_TYPE
|
||||
EOF = antlr.EOF
|
||||
NULL_TREE_LOOKAHEAD = antlr.NULL_TREE_LOOKAHEAD
|
||||
MIN_USER_TYPE = antlr.MIN_USER_TYPE
|
||||
TRUE_CONST = 4
|
||||
FALSE_CONST = 5
|
||||
STR_CONST = 6
|
||||
NUM_CONST = 7
|
||||
INT_CONST = 8
|
||||
FUNC_IF = 9
|
||||
FUNC_CHOOSE = 10
|
||||
NAME = 11
|
||||
QUOTENAME = 12
|
||||
EQ = 13
|
||||
NE = 14
|
||||
GT = 15
|
||||
LT = 16
|
||||
GE = 17
|
||||
LE = 18
|
||||
ADD = 19
|
||||
SUB = 20
|
||||
MUL = 21
|
||||
DIV = 22
|
||||
POWER = 23
|
||||
PERCENT = 24
|
||||
LP = 25
|
||||
RP = 26
|
||||
LB = 27
|
||||
RB = 28
|
||||
COLON = 29
|
||||
COMMA = 30
|
||||
SEMICOLON = 31
|
||||
REF2D = 32
|
||||
REF2D_R1C1 = 33
|
||||
BANG = 34
|
||||
CONCAT = 35
|
||||
|
||||
class Parser(antlr.LLkParser):
|
||||
### user action >>>
|
||||
### user action <<<
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
antlr.LLkParser.__init__(self, *args, **kwargs)
|
||||
self.tokenNames = _tokenNames
|
||||
### __init__ header action >>>
|
||||
self.rpn = ""
|
||||
self.sheet_references = []
|
||||
self.xcall_references = []
|
||||
### __init__ header action <<<
|
||||
|
||||
def formula(self):
|
||||
|
||||
pass
|
||||
self.expr("V")
|
||||
|
||||
def expr(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
pass
|
||||
self.prec0_expr(arg_type)
|
||||
while True:
|
||||
if ((self.LA(1) >= EQ and self.LA(1) <= LE)):
|
||||
pass
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [EQ]:
|
||||
pass
|
||||
self.match(EQ)
|
||||
op = struct.pack('B', ptgEQ)
|
||||
elif la1 and la1 in [NE]:
|
||||
pass
|
||||
self.match(NE)
|
||||
op = struct.pack('B', ptgNE)
|
||||
elif la1 and la1 in [GT]:
|
||||
pass
|
||||
self.match(GT)
|
||||
op = struct.pack('B', ptgGT)
|
||||
elif la1 and la1 in [LT]:
|
||||
pass
|
||||
self.match(LT)
|
||||
op = struct.pack('B', ptgLT)
|
||||
elif la1 and la1 in [GE]:
|
||||
pass
|
||||
self.match(GE)
|
||||
op = struct.pack('B', ptgGE)
|
||||
elif la1 and la1 in [LE]:
|
||||
pass
|
||||
self.match(LE)
|
||||
op = struct.pack('B', ptgLE)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
self.prec0_expr(arg_type)
|
||||
self.rpn += op
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
def prec0_expr(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
pass
|
||||
self.prec1_expr(arg_type)
|
||||
while True:
|
||||
if (self.LA(1)==CONCAT):
|
||||
pass
|
||||
pass
|
||||
self.match(CONCAT)
|
||||
op = struct.pack('B', ptgConcat)
|
||||
self.prec1_expr(arg_type)
|
||||
self.rpn += op
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
def prec1_expr(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
pass
|
||||
self.prec2_expr(arg_type)
|
||||
while True:
|
||||
if (self.LA(1)==ADD or self.LA(1)==SUB):
|
||||
pass
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [ADD]:
|
||||
pass
|
||||
self.match(ADD)
|
||||
op = struct.pack('B', ptgAdd)
|
||||
elif la1 and la1 in [SUB]:
|
||||
pass
|
||||
self.match(SUB)
|
||||
op = struct.pack('B', ptgSub)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
self.prec2_expr(arg_type)
|
||||
self.rpn += op;
|
||||
# print "**prec1_expr4 %s" % arg_type
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
def prec2_expr(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
pass
|
||||
self.prec3_expr(arg_type)
|
||||
while True:
|
||||
if (self.LA(1)==MUL or self.LA(1)==DIV):
|
||||
pass
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [MUL]:
|
||||
pass
|
||||
self.match(MUL)
|
||||
op = struct.pack('B', ptgMul)
|
||||
elif la1 and la1 in [DIV]:
|
||||
pass
|
||||
self.match(DIV)
|
||||
op = struct.pack('B', ptgDiv)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
self.prec3_expr(arg_type)
|
||||
self.rpn += op
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
def prec3_expr(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
pass
|
||||
self.prec4_expr(arg_type)
|
||||
while True:
|
||||
if (self.LA(1)==POWER):
|
||||
pass
|
||||
pass
|
||||
self.match(POWER)
|
||||
op = struct.pack('B', ptgPower)
|
||||
self.prec4_expr(arg_type)
|
||||
self.rpn += op
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
def prec4_expr(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
pass
|
||||
self.prec5_expr(arg_type)
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [PERCENT]:
|
||||
pass
|
||||
self.match(PERCENT)
|
||||
self.rpn += struct.pack('B', ptgPercent)
|
||||
elif la1 and la1 in [EOF,EQ,NE,GT,LT,GE,LE,ADD,SUB,MUL,DIV,POWER,RP,COMMA,SEMICOLON,CONCAT]:
|
||||
pass
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
|
||||
def prec5_expr(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,LP,REF2D]:
|
||||
pass
|
||||
self.primary(arg_type)
|
||||
elif la1 and la1 in [SUB]:
|
||||
pass
|
||||
self.match(SUB)
|
||||
self.primary(arg_type)
|
||||
self.rpn += struct.pack('B', ptgUminus)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
|
||||
def primary(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
str_tok = None
|
||||
int_tok = None
|
||||
num_tok = None
|
||||
ref2d_tok = None
|
||||
ref2d1_tok = None
|
||||
ref2d2_tok = None
|
||||
ref3d_ref2d = None
|
||||
ref3d_ref2d2 = None
|
||||
name_tok = None
|
||||
func_tok = None
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [TRUE_CONST]:
|
||||
pass
|
||||
self.match(TRUE_CONST)
|
||||
self.rpn += struct.pack("2B", ptgBool, 1)
|
||||
elif la1 and la1 in [FALSE_CONST]:
|
||||
pass
|
||||
self.match(FALSE_CONST)
|
||||
self.rpn += struct.pack("2B", ptgBool, 0)
|
||||
elif la1 and la1 in [STR_CONST]:
|
||||
pass
|
||||
str_tok = self.LT(1)
|
||||
self.match(STR_CONST)
|
||||
self.rpn += struct.pack("B", ptgStr) + upack1(str_tok.text[1:-1].replace("\"\"", "\""))
|
||||
elif la1 and la1 in [NUM_CONST]:
|
||||
pass
|
||||
num_tok = self.LT(1)
|
||||
self.match(NUM_CONST)
|
||||
self.rpn += struct.pack("<Bd", ptgNum, float(num_tok.text))
|
||||
elif la1 and la1 in [FUNC_IF]:
|
||||
pass
|
||||
self.match(FUNC_IF)
|
||||
self.match(LP)
|
||||
self.expr("V")
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [SEMICOLON]:
|
||||
pass
|
||||
self.match(SEMICOLON)
|
||||
elif la1 and la1 in [COMMA]:
|
||||
pass
|
||||
self.match(COMMA)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
self.rpn += struct.pack("<BBH", ptgAttr, 0x02, 0) # tAttrIf
|
||||
pos0 = len(self.rpn) - 2
|
||||
self.expr(arg_type)
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [SEMICOLON]:
|
||||
pass
|
||||
self.match(SEMICOLON)
|
||||
elif la1 and la1 in [COMMA]:
|
||||
pass
|
||||
self.match(COMMA)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
self.rpn += struct.pack("<BBH", ptgAttr, 0x08, 0) # tAttrSkip
|
||||
pos1 = len(self.rpn) - 2
|
||||
self.rpn = self.rpn[:pos0] + struct.pack("<H", pos1-pos0) + self.rpn[pos0+2:]
|
||||
self.expr(arg_type)
|
||||
self.match(RP)
|
||||
self.rpn += struct.pack("<BBH", ptgAttr, 0x08, 3) # tAttrSkip
|
||||
self.rpn += struct.pack("<BBH", ptgFuncVarR, 3, 1) # 3 = nargs, 1 = IF func
|
||||
pos2 = len(self.rpn)
|
||||
self.rpn = self.rpn[:pos1] + struct.pack("<H", pos2-(pos1+2)-1) + self.rpn[pos1+2:]
|
||||
elif la1 and la1 in [FUNC_CHOOSE]:
|
||||
pass
|
||||
self.match(FUNC_CHOOSE)
|
||||
arg_type = "R"
|
||||
rpn_chunks = []
|
||||
self.match(LP)
|
||||
self.expr("V")
|
||||
rpn_start = len(self.rpn)
|
||||
ref_markers = [len(self.sheet_references)]
|
||||
while True:
|
||||
if (self.LA(1)==COMMA or self.LA(1)==SEMICOLON):
|
||||
pass
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [SEMICOLON]:
|
||||
pass
|
||||
self.match(SEMICOLON)
|
||||
elif la1 and la1 in [COMMA]:
|
||||
pass
|
||||
self.match(COMMA)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
mark = len(self.rpn)
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,SUB,LP,REF2D]:
|
||||
pass
|
||||
self.expr(arg_type)
|
||||
elif la1 and la1 in [RP,COMMA,SEMICOLON]:
|
||||
pass
|
||||
self.rpn += struct.pack("B", ptgMissArg)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
rpn_chunks.append(self.rpn[mark:])
|
||||
ref_markers.append(len(self.sheet_references))
|
||||
else:
|
||||
break
|
||||
|
||||
self.match(RP)
|
||||
self.rpn = self.rpn[:rpn_start]
|
||||
nc = len(rpn_chunks)
|
||||
chunklens = [len(chunk) for chunk in rpn_chunks]
|
||||
skiplens = [0] * nc
|
||||
skiplens[-1] = 3
|
||||
for ic in xrange(nc-1, 0, -1):
|
||||
skiplens[ic-1] = skiplens[ic] + chunklens[ic] + 4
|
||||
jump_pos = [2 * nc + 2]
|
||||
for ic in xrange(nc):
|
||||
jump_pos.append(jump_pos[-1] + chunklens[ic] + 4)
|
||||
chunk_shift = 2 * nc + 6 # size of tAttrChoose
|
||||
for ic in xrange(nc):
|
||||
for refx in xrange(ref_markers[ic], ref_markers[ic+1]):
|
||||
ref = self.sheet_references[refx]
|
||||
self.sheet_references[refx] = (ref[0], ref[1], ref[2] + chunk_shift)
|
||||
chunk_shift += 4 # size of tAttrSkip
|
||||
choose_rpn = []
|
||||
choose_rpn.append(struct.pack("<BBH", ptgAttr, 0x04, nc)) # 0x04 is tAttrChoose
|
||||
choose_rpn.append(struct.pack("<%dH" % (nc+1), *jump_pos))
|
||||
for ic in xrange(nc):
|
||||
choose_rpn.append(rpn_chunks[ic])
|
||||
choose_rpn.append(struct.pack("<BBH", ptgAttr, 0x08, skiplens[ic])) # 0x08 is tAttrSkip
|
||||
choose_rpn.append(struct.pack("<BBH", ptgFuncVarV, nc+1, 100)) # 100 is CHOOSE fn
|
||||
self.rpn += "".join(choose_rpn)
|
||||
elif la1 and la1 in [LP]:
|
||||
pass
|
||||
self.match(LP)
|
||||
self.expr(arg_type)
|
||||
self.match(RP)
|
||||
self.rpn += struct.pack("B", ptgParen)
|
||||
else:
|
||||
if (self.LA(1)==INT_CONST) and (_tokenSet_0.member(self.LA(2))):
|
||||
pass
|
||||
int_tok = self.LT(1)
|
||||
self.match(INT_CONST)
|
||||
# print "**int_const", int_tok.text
|
||||
int_value = int(int_tok.text)
|
||||
if int_value <= 65535:
|
||||
self.rpn += struct.pack("<BH", ptgInt, int_value)
|
||||
else:
|
||||
self.rpn += struct.pack("<Bd", ptgNum, float(int_value))
|
||||
elif (self.LA(1)==REF2D) and (_tokenSet_0.member(self.LA(2))):
|
||||
pass
|
||||
ref2d_tok = self.LT(1)
|
||||
self.match(REF2D)
|
||||
# print "**ref2d %s %s" % (ref2d_tok.text, arg_type)
|
||||
r, c = Utils.cell_to_packed_rowcol(ref2d_tok.text)
|
||||
ptg = ptgRefR + _RVAdeltaRef[arg_type]
|
||||
self.rpn += struct.pack("<B2H", ptg, r, c)
|
||||
elif (self.LA(1)==REF2D) and (self.LA(2)==COLON):
|
||||
pass
|
||||
ref2d1_tok = self.LT(1)
|
||||
self.match(REF2D)
|
||||
self.match(COLON)
|
||||
ref2d2_tok = self.LT(1)
|
||||
self.match(REF2D)
|
||||
r1, c1 = Utils.cell_to_packed_rowcol(ref2d1_tok.text)
|
||||
r2, c2 = Utils.cell_to_packed_rowcol(ref2d2_tok.text)
|
||||
ptg = ptgAreaR + _RVAdeltaArea[arg_type]
|
||||
self.rpn += struct.pack("<B4H", ptg, r1, r2, c1, c2)
|
||||
elif (self.LA(1)==INT_CONST or self.LA(1)==NAME or self.LA(1)==QUOTENAME) and (self.LA(2)==COLON or self.LA(2)==BANG):
|
||||
pass
|
||||
sheet1=self.sheet()
|
||||
sheet2 = sheet1
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [COLON]:
|
||||
pass
|
||||
self.match(COLON)
|
||||
sheet2=self.sheet()
|
||||
elif la1 and la1 in [BANG]:
|
||||
pass
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
self.match(BANG)
|
||||
ref3d_ref2d = self.LT(1)
|
||||
self.match(REF2D)
|
||||
ptg = ptgRef3dR + _RVAdeltaRef[arg_type]
|
||||
rpn_ref2d = ""
|
||||
r1, c1 = Utils.cell_to_packed_rowcol(ref3d_ref2d.text)
|
||||
rpn_ref2d = struct.pack("<3H", 0x0000, r1, c1)
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [COLON]:
|
||||
pass
|
||||
self.match(COLON)
|
||||
ref3d_ref2d2 = self.LT(1)
|
||||
self.match(REF2D)
|
||||
ptg = ptgArea3dR + _RVAdeltaArea[arg_type]
|
||||
r2, c2 = Utils.cell_to_packed_rowcol(ref3d_ref2d2.text)
|
||||
rpn_ref2d = struct.pack("<5H", 0x0000, r1, r2, c1, c2)
|
||||
elif la1 and la1 in [EOF,EQ,NE,GT,LT,GE,LE,ADD,SUB,MUL,DIV,POWER,PERCENT,RP,COMMA,SEMICOLON,CONCAT]:
|
||||
pass
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
self.rpn += struct.pack("<B", ptg)
|
||||
self.sheet_references.append((sheet1, sheet2, len(self.rpn)))
|
||||
self.rpn += rpn_ref2d
|
||||
elif (self.LA(1)==NAME) and (_tokenSet_0.member(self.LA(2))):
|
||||
pass
|
||||
name_tok = self.LT(1)
|
||||
self.match(NAME)
|
||||
raise Exception("[formula] found unexpected NAME token (%r)" % name_tok.txt)
|
||||
# #### TODO: handle references to defined names here
|
||||
elif (self.LA(1)==NAME) and (self.LA(2)==LP):
|
||||
pass
|
||||
func_tok = self.LT(1)
|
||||
self.match(NAME)
|
||||
func_toku = func_tok.text.upper()
|
||||
if func_toku in all_funcs_by_name:
|
||||
(opcode,
|
||||
min_argc,
|
||||
max_argc,
|
||||
func_type,
|
||||
arg_type_str) = all_funcs_by_name[func_toku]
|
||||
arg_type_list = list(arg_type_str)
|
||||
else:
|
||||
raise Exception("[formula] unknown function (%s)" % func_tok.text)
|
||||
# print "**func_tok1 %s %s" % (func_toku, func_type)
|
||||
xcall = opcode < 0
|
||||
if xcall:
|
||||
# The name of the add-in function is passed as the 1st arg
|
||||
# of the hidden XCALL function
|
||||
self.xcall_references.append((func_toku, len(self.rpn) + 1))
|
||||
self.rpn += struct.pack("<BHHH",
|
||||
ptgNameXR,
|
||||
0xadde, # ##PATCHME## index to REF entry in EXTERNSHEET record
|
||||
0xefbe, # ##PATCHME## one-based index to EXTERNNAME record
|
||||
0x0000) # unused
|
||||
self.match(LP)
|
||||
arg_count=self.expr_list(arg_type_list, min_argc, max_argc)
|
||||
self.match(RP)
|
||||
if arg_count > max_argc or arg_count < min_argc:
|
||||
raise Exception, "%d parameters for function: %s" % (arg_count, func_tok.text)
|
||||
if xcall:
|
||||
func_ptg = ptgFuncVarR + _RVAdelta[func_type]
|
||||
self.rpn += struct.pack("<2BH", func_ptg, arg_count + 1, 255) # 255 is magic XCALL function
|
||||
elif min_argc == max_argc:
|
||||
func_ptg = ptgFuncR + _RVAdelta[func_type]
|
||||
self.rpn += struct.pack("<BH", func_ptg, opcode)
|
||||
elif arg_count == 1 and func_tok.text.upper() == "SUM":
|
||||
self.rpn += struct.pack("<BBH", ptgAttr, 0x10, 0) # tAttrSum
|
||||
else:
|
||||
func_ptg = ptgFuncVarR + _RVAdelta[func_type]
|
||||
self.rpn += struct.pack("<2BH", func_ptg, arg_count, opcode)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
|
||||
def sheet(self):
|
||||
ref = None
|
||||
|
||||
sheet_ref_name = None
|
||||
sheet_ref_int = None
|
||||
sheet_ref_quote = None
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [NAME]:
|
||||
pass
|
||||
sheet_ref_name = self.LT(1)
|
||||
self.match(NAME)
|
||||
ref = sheet_ref_name.text
|
||||
elif la1 and la1 in [INT_CONST]:
|
||||
pass
|
||||
sheet_ref_int = self.LT(1)
|
||||
self.match(INT_CONST)
|
||||
ref = sheet_ref_int.text
|
||||
elif la1 and la1 in [QUOTENAME]:
|
||||
pass
|
||||
sheet_ref_quote = self.LT(1)
|
||||
self.match(QUOTENAME)
|
||||
ref = sheet_ref_quote.text[1:-1].replace("''", "'")
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
return ref
|
||||
|
||||
def expr_list(self,
|
||||
arg_type_list, min_argc, max_argc
|
||||
):
|
||||
arg_cnt = None
|
||||
|
||||
arg_cnt = 0
|
||||
arg_type = arg_type_list[arg_cnt]
|
||||
# print "**expr_list1[%d] req=%s" % (arg_cnt, arg_type)
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,SUB,LP,REF2D]:
|
||||
pass
|
||||
self.expr(arg_type)
|
||||
arg_cnt += 1
|
||||
while True:
|
||||
if (self.LA(1)==COMMA or self.LA(1)==SEMICOLON):
|
||||
pass
|
||||
if arg_cnt < len(arg_type_list):
|
||||
arg_type = arg_type_list[arg_cnt]
|
||||
else:
|
||||
arg_type = arg_type_list[-1]
|
||||
if arg_type == "+":
|
||||
arg_type = arg_type_list[-2]
|
||||
# print "**expr_list2[%d] req=%s" % (arg_cnt, arg_type)
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [SEMICOLON]:
|
||||
pass
|
||||
self.match(SEMICOLON)
|
||||
elif la1 and la1 in [COMMA]:
|
||||
pass
|
||||
self.match(COMMA)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,SUB,LP,REF2D]:
|
||||
pass
|
||||
self.expr(arg_type)
|
||||
elif la1 and la1 in [RP,COMMA,SEMICOLON]:
|
||||
pass
|
||||
self.rpn += struct.pack("B", ptgMissArg)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
arg_cnt += 1
|
||||
else:
|
||||
break
|
||||
|
||||
elif la1 and la1 in [RP]:
|
||||
pass
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
return arg_cnt
|
||||
|
||||
|
||||
_tokenNames = [
|
||||
"<0>",
|
||||
"EOF",
|
||||
"<2>",
|
||||
"NULL_TREE_LOOKAHEAD",
|
||||
"TRUE_CONST",
|
||||
"FALSE_CONST",
|
||||
"STR_CONST",
|
||||
"NUM_CONST",
|
||||
"INT_CONST",
|
||||
"FUNC_IF",
|
||||
"FUNC_CHOOSE",
|
||||
"NAME",
|
||||
"QUOTENAME",
|
||||
"EQ",
|
||||
"NE",
|
||||
"GT",
|
||||
"LT",
|
||||
"GE",
|
||||
"LE",
|
||||
"ADD",
|
||||
"SUB",
|
||||
"MUL",
|
||||
"DIV",
|
||||
"POWER",
|
||||
"PERCENT",
|
||||
"LP",
|
||||
"RP",
|
||||
"LB",
|
||||
"RB",
|
||||
"COLON",
|
||||
"COMMA",
|
||||
"SEMICOLON",
|
||||
"REF2D",
|
||||
"REF2D_R1C1",
|
||||
"BANG",
|
||||
"CONCAT"
|
||||
]
|
||||
|
||||
|
||||
### generate bit set
|
||||
def mk_tokenSet_0():
|
||||
### var1
|
||||
data = [ 37681618946L, 0L]
|
||||
return data
|
||||
_tokenSet_0 = antlr.BitSet(mk_tokenSet_0())
|
||||
|
||||
@@ -1,862 +0,0 @@
|
||||
# -*- coding: ascii -*-
|
||||
"""
|
||||
lots of Excel Magic Numbers
|
||||
"""
|
||||
|
||||
# Boundaries BIFF8+
|
||||
|
||||
MAX_ROW = 65536
|
||||
MAX_COL = 256
|
||||
|
||||
|
||||
biff_records = {
|
||||
0x0000: "DIMENSIONS",
|
||||
0x0001: "BLANK",
|
||||
0x0002: "INTEGER",
|
||||
0x0003: "NUMBER",
|
||||
0x0004: "LABEL",
|
||||
0x0005: "BOOLERR",
|
||||
0x0006: "FORMULA",
|
||||
0x0007: "STRING",
|
||||
0x0008: "ROW",
|
||||
0x0009: "BOF",
|
||||
0x000A: "EOF",
|
||||
0x000B: "INDEX",
|
||||
0x000C: "CALCCOUNT",
|
||||
0x000D: "CALCMODE",
|
||||
0x000E: "PRECISION",
|
||||
0x000F: "REFMODE",
|
||||
0x0010: "DELTA",
|
||||
0x0011: "ITERATION",
|
||||
0x0012: "PROTECT",
|
||||
0x0013: "PASSWORD",
|
||||
0x0014: "HEADER",
|
||||
0x0015: "FOOTER",
|
||||
0x0016: "EXTERNCOUNT",
|
||||
0x0017: "EXTERNSHEET",
|
||||
0x0018: "NAME",
|
||||
0x0019: "WINDOWPROTECT",
|
||||
0x001A: "VERTICALPAGEBREAKS",
|
||||
0x001B: "HORIZONTALPAGEBREAKS",
|
||||
0x001C: "NOTE",
|
||||
0x001D: "SELECTION",
|
||||
0x001E: "FORMAT",
|
||||
0x001F: "FORMATCOUNT",
|
||||
0x0020: "COLUMNDEFAULT",
|
||||
0x0021: "ARRAY",
|
||||
0x0022: "1904",
|
||||
0x0023: "EXTERNNAME",
|
||||
0x0024: "COLWIDTH",
|
||||
0x0025: "DEFAULTROWHEIGHT",
|
||||
0x0026: "LEFTMARGIN",
|
||||
0x0027: "RIGHTMARGIN",
|
||||
0x0028: "TOPMARGIN",
|
||||
0x0029: "BOTTOMMARGIN",
|
||||
0x002A: "PRINTHEADERS",
|
||||
0x002B: "PRINTGRIDLINES",
|
||||
0x002F: "FILEPASS",
|
||||
0x0031: "FONT",
|
||||
0x0036: "TABLE",
|
||||
0x003C: "CONTINUE",
|
||||
0x003D: "WINDOW1",
|
||||
0x003E: "WINDOW2",
|
||||
0x0040: "BACKUP",
|
||||
0x0041: "PANE",
|
||||
0x0042: "CODEPAGE",
|
||||
0x0043: "XF",
|
||||
0x0044: "IXFE",
|
||||
0x0045: "EFONT",
|
||||
0x004D: "PLS",
|
||||
0x0050: "DCON",
|
||||
0x0051: "DCONREF",
|
||||
0x0053: "DCONNAME",
|
||||
0x0055: "DEFCOLWIDTH",
|
||||
0x0056: "BUILTINFMTCNT",
|
||||
0x0059: "XCT",
|
||||
0x005A: "CRN",
|
||||
0x005B: "FILESHARING",
|
||||
0x005C: "WRITEACCESS",
|
||||
0x005D: "OBJ",
|
||||
0x005E: "UNCALCED",
|
||||
0x005F: "SAFERECALC",
|
||||
0x0060: "TEMPLATE",
|
||||
0x0063: "OBJPROTECT",
|
||||
0x007D: "COLINFO",
|
||||
0x007E: "RK",
|
||||
0x007F: "IMDATA",
|
||||
0x0080: "GUTS",
|
||||
0x0081: "WSBOOL",
|
||||
0x0082: "GRIDSET",
|
||||
0x0083: "HCENTER",
|
||||
0x0084: "VCENTER",
|
||||
0x0085: "BOUNDSHEET",
|
||||
0x0086: "WRITEPROT",
|
||||
0x0087: "ADDIN",
|
||||
0x0088: "EDG",
|
||||
0x0089: "PUB",
|
||||
0x008C: "COUNTRY",
|
||||
0x008D: "HIDEOBJ",
|
||||
0x008E: "BUNDLESOFFSET",
|
||||
0x008F: "BUNDLEHEADER",
|
||||
0x0090: "SORT",
|
||||
0x0091: "SUB",
|
||||
0x0092: "PALETTE",
|
||||
0x0093: "STYLE",
|
||||
0x0094: "LHRECORD",
|
||||
0x0095: "LHNGRAPH",
|
||||
0x0096: "SOUND",
|
||||
0x0098: "LPR",
|
||||
0x0099: "STANDARDWIDTH",
|
||||
0x009A: "FNGROUPNAME",
|
||||
0x009B: "FILTERMODE",
|
||||
0x009C: "FNGROUPCOUNT",
|
||||
0x009D: "AUTOFILTERINFO",
|
||||
0x009E: "AUTOFILTER",
|
||||
0x00A0: "SCL",
|
||||
0x00A1: "SETUP",
|
||||
0x00A9: "COORDLIST",
|
||||
0x00AB: "GCW",
|
||||
0x00AE: "SCENMAN",
|
||||
0x00AF: "SCENARIO",
|
||||
0x00B0: "SXVIEW",
|
||||
0x00B1: "SXVD",
|
||||
0x00B2: "SXVI",
|
||||
0x00B4: "SXIVD",
|
||||
0x00B5: "SXLI",
|
||||
0x00B6: "SXPI",
|
||||
0x00B8: "DOCROUTE",
|
||||
0x00B9: "RECIPNAME",
|
||||
0x00BC: "SHRFMLA",
|
||||
0x00BD: "MULRK",
|
||||
0x00BE: "MULBLANK",
|
||||
0x00C1: "MMS",
|
||||
0x00C2: "ADDMENU",
|
||||
0x00C3: "DELMENU",
|
||||
0x00C5: "SXDI",
|
||||
0x00C6: "SXDB",
|
||||
0x00C7: "SXFIELD",
|
||||
0x00C8: "SXINDEXLIST",
|
||||
0x00C9: "SXDOUBLE",
|
||||
0x00CD: "SXSTRING",
|
||||
0x00CE: "SXDATETIME",
|
||||
0x00D0: "SXTBL",
|
||||
0x00D1: "SXTBRGITEM",
|
||||
0x00D2: "SXTBPG",
|
||||
0x00D3: "OBPROJ",
|
||||
0x00D5: "SXIDSTM",
|
||||
0x00D6: "RSTRING",
|
||||
0x00D7: "DBCELL",
|
||||
0x00DA: "BOOKBOOL",
|
||||
0x00DC: "SXEXT|PARAMQRY",
|
||||
0x00DD: "SCENPROTECT",
|
||||
0x00DE: "OLESIZE",
|
||||
0x00DF: "UDDESC",
|
||||
0x00E0: "XF",
|
||||
0x00E1: "INTERFACEHDR",
|
||||
0x00E2: "INTERFACEEND",
|
||||
0x00E3: "SXVS",
|
||||
0x00E5: "MERGEDCELLS",
|
||||
0x00E9: "BITMAP",
|
||||
0x00EB: "MSODRAWINGGROUP",
|
||||
0x00EC: "MSODRAWING",
|
||||
0x00ED: "MSODRAWINGSELECTION",
|
||||
0x00F0: "SXRULE",
|
||||
0x00F1: "SXEX",
|
||||
0x00F2: "SXFILT",
|
||||
0x00F6: "SXNAME",
|
||||
0x00F7: "SXSELECT",
|
||||
0x00F8: "SXPAIR",
|
||||
0x00F9: "SXFMLA",
|
||||
0x00FB: "SXFORMAT",
|
||||
0x00FC: "SST",
|
||||
0x00FD: "LABELSST",
|
||||
0x00FF: "EXTSST",
|
||||
0x0100: "SXVDEX",
|
||||
0x0103: "SXFORMULA",
|
||||
0x0122: "SXDBEX",
|
||||
0x0137: "CHTRINSERT",
|
||||
0x0138: "CHTRINFO",
|
||||
0x013B: "CHTRCELLCONTENT",
|
||||
0x013D: "TABID",
|
||||
0x0140: "CHTRMOVERANGE",
|
||||
0x014D: "CHTRINSERTTAB",
|
||||
0x015F: "LABELRANGES",
|
||||
0x0160: "USESELFS",
|
||||
0x0161: "DSF",
|
||||
0x0162: "XL5MODIFY",
|
||||
0x0196: "CHTRHEADER",
|
||||
0x01A9: "USERBVIEW",
|
||||
0x01AA: "USERSVIEWBEGIN",
|
||||
0x01AB: "USERSVIEWEND",
|
||||
0x01AD: "QSI",
|
||||
0x01AE: "SUPBOOK",
|
||||
0x01AF: "PROT4REV",
|
||||
0x01B0: "CONDFMT",
|
||||
0x01B1: "CF",
|
||||
0x01B2: "DVAL",
|
||||
0x01B5: "DCONBIN",
|
||||
0x01B6: "TXO",
|
||||
0x01B7: "REFRESHALL",
|
||||
0x01B8: "HLINK",
|
||||
0x01BA: "CODENAME",
|
||||
0x01BB: "SXFDBTYPE",
|
||||
0x01BC: "PROT4REVPASS",
|
||||
0x01BE: "DV",
|
||||
0x01C0: "XL9FILE",
|
||||
0x01C1: "RECALCID",
|
||||
0x0200: "DIMENSIONS",
|
||||
0x0201: "BLANK",
|
||||
0x0203: "NUMBER",
|
||||
0x0204: "LABEL",
|
||||
0x0205: "BOOLERR",
|
||||
0x0206: "FORMULA",
|
||||
0x0207: "STRING",
|
||||
0x0208: "ROW",
|
||||
0x0209: "BOF",
|
||||
0x020B: "INDEX",
|
||||
0x0218: "NAME",
|
||||
0x0221: "ARRAY",
|
||||
0x0223: "EXTERNNAME",
|
||||
0x0225: "DEFAULTROWHEIGHT",
|
||||
0x0231: "FONT",
|
||||
0x0236: "TABLE",
|
||||
0x023E: "WINDOW2",
|
||||
0x0243: "XF",
|
||||
0x027E: "RK",
|
||||
0x0293: "STYLE",
|
||||
0x0406: "FORMULA",
|
||||
0x0409: "BOF",
|
||||
0x041E: "FORMAT",
|
||||
0x0443: "XF",
|
||||
0x04BC: "SHRFMLA",
|
||||
0x0800: "SCREENTIP",
|
||||
0x0803: "WEBQRYSETTINGS",
|
||||
0x0804: "WEBQRYTABLES",
|
||||
0x0809: "BOF",
|
||||
0x0862: "SHEETLAYOUT",
|
||||
0x0867: "SHEETPROTECTION",
|
||||
0x1001: "UNITS",
|
||||
0x1002: "ChartChart",
|
||||
0x1003: "ChartSeries",
|
||||
0x1006: "ChartDataformat",
|
||||
0x1007: "ChartLineformat",
|
||||
0x1009: "ChartMarkerformat",
|
||||
0x100A: "ChartAreaformat",
|
||||
0x100B: "ChartPieformat",
|
||||
0x100C: "ChartAttachedlabel",
|
||||
0x100D: "ChartSeriestext",
|
||||
0x1014: "ChartChartformat",
|
||||
0x1015: "ChartLegend",
|
||||
0x1016: "ChartSerieslist",
|
||||
0x1017: "ChartBar",
|
||||
0x1018: "ChartLine",
|
||||
0x1019: "ChartPie",
|
||||
0x101A: "ChartArea",
|
||||
0x101B: "ChartScatter",
|
||||
0x101C: "ChartChartline",
|
||||
0x101D: "ChartAxis",
|
||||
0x101E: "ChartTick",
|
||||
0x101F: "ChartValuerange",
|
||||
0x1020: "ChartCatserrange",
|
||||
0x1021: "ChartAxislineformat",
|
||||
0x1022: "ChartFormatlink",
|
||||
0x1024: "ChartDefaulttext",
|
||||
0x1025: "ChartText",
|
||||
0x1026: "ChartFontx",
|
||||
0x1027: "ChartObjectLink",
|
||||
0x1032: "ChartFrame",
|
||||
0x1033: "BEGIN",
|
||||
0x1034: "END",
|
||||
0x1035: "ChartPlotarea",
|
||||
0x103A: "Chart3D",
|
||||
0x103C: "ChartPicf",
|
||||
0x103D: "ChartDropbar",
|
||||
0x103E: "ChartRadar",
|
||||
0x103F: "ChartSurface",
|
||||
0x1040: "ChartRadararea",
|
||||
0x1041: "ChartAxisparent",
|
||||
0x1043: "ChartLegendxn",
|
||||
0x1044: "ChartShtprops",
|
||||
0x1045: "ChartSertocrt",
|
||||
0x1046: "ChartAxesused",
|
||||
0x1048: "ChartSbaseref",
|
||||
0x104A: "ChartSerparent",
|
||||
0x104B: "ChartSerauxtrend",
|
||||
0x104E: "ChartIfmt",
|
||||
0x104F: "ChartPos",
|
||||
0x1050: "ChartAlruns",
|
||||
0x1051: "ChartAI",
|
||||
0x105B: "ChartSerauxerrbar",
|
||||
0x105D: "ChartSerfmt",
|
||||
0x105F: "Chart3DDataFormat",
|
||||
0x1060: "ChartFbi",
|
||||
0x1061: "ChartBoppop",
|
||||
0x1062: "ChartAxcext",
|
||||
0x1063: "ChartDat",
|
||||
0x1064: "ChartPlotgrowth",
|
||||
0x1065: "ChartSiindex",
|
||||
0x1066: "ChartGelframe",
|
||||
0x1067: "ChartBoppcustom",
|
||||
0xFFFF: ""
|
||||
}
|
||||
|
||||
|
||||
all_funcs_by_name = {
|
||||
# Includes Analysis ToolPak aka ATP aka add-in aka xcall functions,
|
||||
# distinguished by -ve opcode.
|
||||
# name: (opcode, min # args, max # args, func return type, func arg types)
|
||||
# + in func arg types means more of the same.
|
||||
'ABS' : ( 24, 1, 1, 'V', 'V'),
|
||||
'ACCRINT' : ( -1, 6, 7, 'V', 'VVVVVVV'),
|
||||
'ACCRINTM' : ( -1, 3, 5, 'V', 'VVVVV'),
|
||||
'ACOS' : ( 99, 1, 1, 'V', 'V'),
|
||||
'ACOSH' : (233, 1, 1, 'V', 'V'),
|
||||
'ADDRESS' : (219, 2, 5, 'V', 'VVVVV'),
|
||||
'AMORDEGRC' : ( -1, 7, 7, 'V', 'VVVVVVV'),
|
||||
'AMORLINC' : ( -1, 7, 7, 'V', 'VVVVVVV'),
|
||||
'AND' : ( 36, 1, 30, 'V', 'D+'),
|
||||
'AREAS' : ( 75, 1, 1, 'V', 'R'),
|
||||
'ASC' : (214, 1, 1, 'V', 'V'),
|
||||
'ASIN' : ( 98, 1, 1, 'V', 'V'),
|
||||
'ASINH' : (232, 1, 1, 'V', 'V'),
|
||||
'ATAN' : ( 18, 1, 1, 'V', 'V'),
|
||||
'ATAN2' : ( 97, 2, 2, 'V', 'VV'),
|
||||
'ATANH' : (234, 1, 1, 'V', 'V'),
|
||||
'AVEDEV' : (269, 1, 30, 'V', 'D+'),
|
||||
'AVERAGE' : ( 5, 1, 30, 'V', 'D+'),
|
||||
'AVERAGEA' : (361, 1, 30, 'V', 'D+'),
|
||||
'BAHTTEXT' : (368, 1, 1, 'V', 'V'),
|
||||
'BESSELI' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'BESSELJ' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'BESSELK' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'BESSELY' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'BETADIST' : (270, 3, 5, 'V', 'VVVVV'),
|
||||
'BETAINV' : (272, 3, 5, 'V', 'VVVVV'),
|
||||
'BIN2DEC' : ( -1, 1, 1, 'V', 'V'),
|
||||
'BIN2HEX' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'BIN2OCT' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'BINOMDIST' : (273, 4, 4, 'V', 'VVVV'),
|
||||
'CEILING' : (288, 2, 2, 'V', 'VV'),
|
||||
'CELL' : (125, 1, 2, 'V', 'VR'),
|
||||
'CHAR' : (111, 1, 1, 'V', 'V'),
|
||||
'CHIDIST' : (274, 2, 2, 'V', 'VV'),
|
||||
'CHIINV' : (275, 2, 2, 'V', 'VV'),
|
||||
'CHITEST' : (306, 2, 2, 'V', 'AA'),
|
||||
'CHOOSE' : (100, 2, 30, 'R', 'VR+'),
|
||||
'CLEAN' : (162, 1, 1, 'V', 'V'),
|
||||
'CODE' : (121, 1, 1, 'V', 'V'),
|
||||
'COLUMN' : ( 9, 0, 1, 'V', 'R'),
|
||||
'COLUMNS' : ( 77, 1, 1, 'V', 'R'),
|
||||
'COMBIN' : (276, 2, 2, 'V', 'VV'),
|
||||
'COMPLEX' : ( -1, 2, 3, 'V', 'VVV'),
|
||||
'CONCATENATE' : (336, 1, 30, 'V', 'V+'),
|
||||
'CONFIDENCE' : (277, 3, 3, 'V', 'VVV'),
|
||||
'CONVERT' : ( -1, 3, 3, 'V', 'VVV'),
|
||||
'CORREL' : (307, 2, 2, 'V', 'AA'),
|
||||
'COS' : ( 16, 1, 1, 'V', 'V'),
|
||||
'COSH' : (230, 1, 1, 'V', 'V'),
|
||||
'COUNT' : ( 0, 1, 30, 'V', 'D+'),
|
||||
'COUNTA' : (169, 1, 30, 'V', 'D+'),
|
||||
'COUNTBLANK' : (347, 1, 1, 'V', 'R'),
|
||||
'COUNTIF' : (346, 2, 2, 'V', 'RV'),
|
||||
'COUPDAYBS' : ( -1, 3, 5, 'V', 'VVVVV'),
|
||||
'COUPDAYS' : ( -1, 3, 5, 'V', 'VVVVV'),
|
||||
'COUPDAYSNC' : ( -1, 3, 5, 'V', 'VVVVV'),
|
||||
'COUPNCD' : ( -1, 3, 5, 'V', 'VVVVV'),
|
||||
'COUPNUM' : ( -1, 3, 5, 'V', 'VVVVV'),
|
||||
'COUPPCD' : ( -1, 3, 5, 'V', 'VVVVV'),
|
||||
'COVAR' : (308, 2, 2, 'V', 'AA'),
|
||||
'CRITBINOM' : (278, 3, 3, 'V', 'VVV'),
|
||||
'CUMIPMT' : ( -1, 6, 6, 'V', 'VVVVVV'),
|
||||
'CUMPRINC' : ( -1, 6, 6, 'V', 'VVVVVV'),
|
||||
'DATE' : ( 65, 3, 3, 'V', 'VVV'),
|
||||
'DATEDIF' : (351, 3, 3, 'V', 'VVV'),
|
||||
'DATEVALUE' : (140, 1, 1, 'V', 'V'),
|
||||
'DAVERAGE' : ( 42, 3, 3, 'V', 'RRR'),
|
||||
'DAY' : ( 67, 1, 1, 'V', 'V'),
|
||||
'DAYS360' : (220, 2, 3, 'V', 'VVV'),
|
||||
'DB' : (247, 4, 5, 'V', 'VVVVV'),
|
||||
'DBCS' : (215, 1, 1, 'V', 'V'),
|
||||
'DCOUNT' : ( 40, 3, 3, 'V', 'RRR'),
|
||||
'DCOUNTA' : (199, 3, 3, 'V', 'RRR'),
|
||||
'DDB' : (144, 4, 5, 'V', 'VVVVV'),
|
||||
'DEC2BIN' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'DEC2HEX' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'DEC2OCT' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'DEGREES' : (343, 1, 1, 'V', 'V'),
|
||||
'DELTA' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'DEVSQ' : (318, 1, 30, 'V', 'D+'),
|
||||
'DGET' : (235, 3, 3, 'V', 'RRR'),
|
||||
'DISC' : ( -1, 4, 5, 'V', 'VVVVV'),
|
||||
'DMAX' : ( 44, 3, 3, 'V', 'RRR'),
|
||||
'DMIN' : ( 43, 3, 3, 'V', 'RRR'),
|
||||
'DOLLAR' : ( 13, 1, 2, 'V', 'VV'),
|
||||
'DOLLARDE' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'DOLLARFR' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'DPRODUCT' : (189, 3, 3, 'V', 'RRR'),
|
||||
'DSTDEV' : ( 45, 3, 3, 'V', 'RRR'),
|
||||
'DSTDEVP' : (195, 3, 3, 'V', 'RRR'),
|
||||
'DSUM' : ( 41, 3, 3, 'V', 'RRR'),
|
||||
'DURATION' : ( -1, 5, 6, 'V', 'VVVVVV'),
|
||||
'DVAR' : ( 47, 3, 3, 'V', 'RRR'),
|
||||
'DVARP' : (196, 3, 3, 'V', 'RRR'),
|
||||
'EDATE' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'EFFECT' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'EOMONTH' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'ERF' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'ERFC' : ( -1, 1, 1, 'V', 'V'),
|
||||
'ERROR.TYPE' : (261, 1, 1, 'V', 'V'),
|
||||
'EVEN' : (279, 1, 1, 'V', 'V'),
|
||||
'EXACT' : (117, 2, 2, 'V', 'VV'),
|
||||
'EXP' : ( 21, 1, 1, 'V', 'V'),
|
||||
'EXPONDIST' : (280, 3, 3, 'V', 'VVV'),
|
||||
'FACT' : (184, 1, 1, 'V', 'V'),
|
||||
'FACTDOUBLE' : ( -1, 1, 1, 'V', 'V'),
|
||||
'FALSE' : ( 35, 0, 0, 'V', '-'),
|
||||
'FDIST' : (281, 3, 3, 'V', 'VVV'),
|
||||
'FIND' : (124, 2, 3, 'V', 'VVV'),
|
||||
'FINDB' : (205, 2, 3, 'V', 'VVV'),
|
||||
'FINV' : (282, 3, 3, 'V', 'VVV'),
|
||||
'FISHER' : (283, 1, 1, 'V', 'V'),
|
||||
'FISHERINV' : (284, 1, 1, 'V', 'V'),
|
||||
'FIXED' : ( 14, 2, 3, 'V', 'VVV'),
|
||||
'FLOOR' : (285, 2, 2, 'V', 'VV'),
|
||||
'FORECAST' : (309, 3, 3, 'V', 'VAA'),
|
||||
'FREQUENCY' : (252, 2, 2, 'A', 'RR'),
|
||||
'FTEST' : (310, 2, 2, 'V', 'AA'),
|
||||
'FV' : ( 57, 3, 5, 'V', 'VVVVV'),
|
||||
'FVSCHEDULE' : ( -1, 2, 2, 'V', 'VA'),
|
||||
'GAMMADIST' : (286, 4, 4, 'V', 'VVVV'),
|
||||
'GAMMAINV' : (287, 3, 3, 'V', 'VVV'),
|
||||
'GAMMALN' : (271, 1, 1, 'V', 'V'),
|
||||
'GCD' : ( -1, 1, 29, 'V', 'V+'),
|
||||
'GEOMEAN' : (319, 1, 30, 'V', 'D+'),
|
||||
'GESTEP' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'GETPIVOTDATA': (358, 2, 30, 'A', 'VAV+'),
|
||||
'GROWTH' : ( 52, 1, 4, 'A', 'RRRV'),
|
||||
'HARMEAN' : (320, 1, 30, 'V', 'D+'),
|
||||
'HEX2BIN' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'HEX2DEC' : ( -1, 1, 1, 'V', 'V'),
|
||||
'HEX2OCT' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'HLOOKUP' : (101, 3, 4, 'V', 'VRRV'),
|
||||
'HOUR' : ( 71, 1, 1, 'V', 'V'),
|
||||
'HYPERLINK' : (359, 1, 2, 'V', 'VV'),
|
||||
'HYPGEOMDIST' : (289, 4, 4, 'V', 'VVVV'),
|
||||
'IF' : ( 1, 2, 3, 'R', 'VRR'),
|
||||
'IMABS' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMAGINARY' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMARGUMENT' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMCONJUGATE' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMCOS' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMDIV' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'IMEXP' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMLN' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMLOG10' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMLOG2' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMPOWER' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'IMPRODUCT' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'IMREAL' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMSIN' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMSQRT' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMSUB' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'IMSUM' : ( -1, 1, 29, 'V', 'V+'),
|
||||
'INDEX' : ( 29, 2, 4, 'R', 'RVVV'),
|
||||
'INDIRECT' : (148, 1, 2, 'R', 'VV'),
|
||||
'INFO' : (244, 1, 1, 'V', 'V'),
|
||||
'INT' : ( 25, 1, 1, 'V', 'V'),
|
||||
'INTERCEPT' : (311, 2, 2, 'V', 'AA'),
|
||||
'INTRATE' : ( -1, 4, 5, 'V', 'VVVVV'),
|
||||
'IPMT' : (167, 4, 6, 'V', 'VVVVVV'),
|
||||
'IRR' : ( 62, 1, 2, 'V', 'RV'),
|
||||
'ISBLANK' : (129, 1, 1, 'V', 'V'),
|
||||
'ISERR' : (126, 1, 1, 'V', 'V'),
|
||||
'ISERROR' : ( 3, 1, 1, 'V', 'V'),
|
||||
'ISEVEN' : ( -1, 1, 1, 'V', 'V'),
|
||||
'ISLOGICAL' : (198, 1, 1, 'V', 'V'),
|
||||
'ISNA' : ( 2, 1, 1, 'V', 'V'),
|
||||
'ISNONTEXT' : (190, 1, 1, 'V', 'V'),
|
||||
'ISNUMBER' : (128, 1, 1, 'V', 'V'),
|
||||
'ISODD' : ( -1, 1, 1, 'V', 'V'),
|
||||
'ISPMT' : (350, 4, 4, 'V', 'VVVV'),
|
||||
'ISREF' : (105, 1, 1, 'V', 'R'),
|
||||
'ISTEXT' : (127, 1, 1, 'V', 'V'),
|
||||
'KURT' : (322, 1, 30, 'V', 'D+'),
|
||||
'LARGE' : (325, 2, 2, 'V', 'RV'),
|
||||
'LCM' : ( -1, 1, 29, 'V', 'V+'),
|
||||
'LEFT' : (115, 1, 2, 'V', 'VV'),
|
||||
'LEFTB' : (208, 1, 2, 'V', 'VV'),
|
||||
'LEN' : ( 32, 1, 1, 'V', 'V'),
|
||||
'LENB' : (211, 1, 1, 'V', 'V'),
|
||||
'LINEST' : ( 49, 1, 4, 'A', 'RRVV'),
|
||||
'LN' : ( 22, 1, 1, 'V', 'V'),
|
||||
'LOG' : (109, 1, 2, 'V', 'VV'),
|
||||
'LOG10' : ( 23, 1, 1, 'V', 'V'),
|
||||
'LOGEST' : ( 51, 1, 4, 'A', 'RRVV'),
|
||||
'LOGINV' : (291, 3, 3, 'V', 'VVV'),
|
||||
'LOGNORMDIST' : (290, 3, 3, 'V', 'VVV'),
|
||||
'LOOKUP' : ( 28, 2, 3, 'V', 'VRR'),
|
||||
'LOWER' : (112, 1, 1, 'V', 'V'),
|
||||
'MATCH' : ( 64, 2, 3, 'V', 'VRR'),
|
||||
'MAX' : ( 7, 1, 30, 'V', 'D+'),
|
||||
'MAXA' : (362, 1, 30, 'V', 'D+'),
|
||||
'MDETERM' : (163, 1, 1, 'V', 'A'),
|
||||
'MDURATION' : ( -1, 5, 6, 'V', 'VVVVVV'),
|
||||
'MEDIAN' : (227, 1, 30, 'V', 'D+'),
|
||||
'MID' : ( 31, 3, 3, 'V', 'VVV'),
|
||||
'MIDB' : (210, 3, 3, 'V', 'VVV'),
|
||||
'MIN' : ( 6, 1, 30, 'V', 'D+'),
|
||||
'MINA' : (363, 1, 30, 'V', 'D+'),
|
||||
'MINUTE' : ( 72, 1, 1, 'V', 'V'),
|
||||
'MINVERSE' : (164, 1, 1, 'A', 'A'),
|
||||
'MIRR' : ( 61, 3, 3, 'V', 'RVV'),
|
||||
'MMULT' : (165, 2, 2, 'A', 'AA'),
|
||||
'MOD' : ( 39, 2, 2, 'V', 'VV'),
|
||||
'MODE' : (330, 1, 30, 'V', 'A+'), ################ weird #################
|
||||
'MONTH' : ( 68, 1, 1, 'V', 'V'),
|
||||
'MROUND' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'MULTINOMIAL' : ( -1, 1, 29, 'V', 'V+'),
|
||||
'N' : (131, 1, 1, 'V', 'R'),
|
||||
'NA' : ( 10, 0, 0, 'V', '-'),
|
||||
'NEGBINOMDIST': (292, 3, 3, 'V', 'VVV'),
|
||||
'NETWORKDAYS' : ( -1, 2, 3, 'V', 'VVR'),
|
||||
'NOMINAL' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'NORMDIST' : (293, 4, 4, 'V', 'VVVV'),
|
||||
'NORMINV' : (295, 3, 3, 'V', 'VVV'),
|
||||
'NORMSDIST' : (294, 1, 1, 'V', 'V'),
|
||||
'NORMSINV' : (296, 1, 1, 'V', 'V'),
|
||||
'NOT' : ( 38, 1, 1, 'V', 'V'),
|
||||
'NOW' : ( 74, 0, 0, 'V', '-'),
|
||||
'NPER' : ( 58, 3, 5, 'V', 'VVVVV'),
|
||||
'NPV' : ( 11, 2, 30, 'V', 'VD+'),
|
||||
'OCT2BIN' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'OCT2DEC' : ( -1, 1, 1, 'V', 'V'),
|
||||
'OCT2HEX' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'ODD' : (298, 1, 1, 'V', 'V'),
|
||||
'ODDFPRICE' : ( -1, 9, 9, 'V', 'VVVVVVVVV'),
|
||||
'ODDFYIELD' : ( -1, 9, 9, 'V', 'VVVVVVVVV'),
|
||||
'ODDLPRICE' : ( -1, 8, 8, 'V', 'VVVVVVVV'),
|
||||
'ODDLYIELD' : ( -1, 8, 8, 'V', 'VVVVVVVV'),
|
||||
'OFFSET' : ( 78, 3, 5, 'R', 'RVVVV'),
|
||||
'OR' : ( 37, 1, 30, 'V', 'D+'),
|
||||
'PEARSON' : (312, 2, 2, 'V', 'AA'),
|
||||
'PERCENTILE' : (328, 2, 2, 'V', 'RV'),
|
||||
'PERCENTRANK' : (329, 2, 3, 'V', 'RVV'),
|
||||
'PERMUT' : (299, 2, 2, 'V', 'VV'),
|
||||
'PHONETIC' : (360, 1, 1, 'V', 'R'),
|
||||
'PI' : ( 19, 0, 0, 'V', '-'),
|
||||
'PMT' : ( 59, 3, 5, 'V', 'VVVVV'),
|
||||
'POISSON' : (300, 3, 3, 'V', 'VVV'),
|
||||
'POWER' : (337, 2, 2, 'V', 'VV'),
|
||||
'PPMT' : (168, 4, 6, 'V', 'VVVVVV'),
|
||||
'PRICE' : ( -1, 6, 7, 'V', 'VVVVVVV'),
|
||||
'PRICEDISC' : ( -1, 4, 5, 'V', 'VVVVV'),
|
||||
'PRICEMAT' : ( -1, 5, 6, 'V', 'VVVVVV'),
|
||||
'PROB' : (317, 3, 4, 'V', 'AAVV'),
|
||||
'PRODUCT' : (183, 1, 30, 'V', 'D+'),
|
||||
'PROPER' : (114, 1, 1, 'V', 'V'),
|
||||
'PV' : ( 56, 3, 5, 'V', 'VVVVV'),
|
||||
'QUARTILE' : (327, 2, 2, 'V', 'RV'),
|
||||
'QUOTIENT' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'RADIANS' : (342, 1, 1, 'V', 'V'),
|
||||
'RAND' : ( 63, 0, 0, 'V', '-'),
|
||||
'RANDBETWEEN' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'RANK' : (216, 2, 3, 'V', 'VRV'),
|
||||
'RATE' : ( 60, 3, 6, 'V', 'VVVVVV'),
|
||||
'RECEIVED' : ( -1, 4, 5, 'V', 'VVVVV'),
|
||||
'REPLACE' : (119, 4, 4, 'V', 'VVVV'),
|
||||
'REPLACEB' : (207, 4, 4, 'V', 'VVVV'),
|
||||
'REPT' : ( 30, 2, 2, 'V', 'VV'),
|
||||
'RIGHT' : (116, 1, 2, 'V', 'VV'),
|
||||
'RIGHTB' : (209, 1, 2, 'V', 'VV'),
|
||||
'ROMAN' : (354, 1, 2, 'V', 'VV'),
|
||||
'ROUND' : ( 27, 2, 2, 'V', 'VV'),
|
||||
'ROUNDDOWN' : (213, 2, 2, 'V', 'VV'),
|
||||
'ROUNDUP' : (212, 2, 2, 'V', 'VV'),
|
||||
'ROW' : ( 8, 0, 1, 'V', 'R'),
|
||||
'ROWS' : ( 76, 1, 1, 'V', 'R'),
|
||||
'RSQ' : (313, 2, 2, 'V', 'AA'),
|
||||
'RTD' : (379, 3, 30, 'A', 'VVV+'),
|
||||
'SEARCH' : ( 82, 2, 3, 'V', 'VVV'),
|
||||
'SEARCHB' : (206, 2, 3, 'V', 'VVV'),
|
||||
'SECOND' : ( 73, 1, 1, 'V', 'V'),
|
||||
'SERIESSUM' : ( -1, 4, 4, 'V', 'VVVA'),
|
||||
'SIGN' : ( 26, 1, 1, 'V', 'V'),
|
||||
'SIN' : ( 15, 1, 1, 'V', 'V'),
|
||||
'SINH' : (229, 1, 1, 'V', 'V'),
|
||||
'SKEW' : (323, 1, 30, 'V', 'D+'),
|
||||
'SLN' : (142, 3, 3, 'V', 'VVV'),
|
||||
'SLOPE' : (315, 2, 2, 'V', 'AA'),
|
||||
'SMALL' : (326, 2, 2, 'V', 'RV'),
|
||||
'SQRT' : ( 20, 1, 1, 'V', 'V'),
|
||||
'SQRTPI' : ( -1, 1, 1, 'V', 'V'),
|
||||
'STANDARDIZE' : (297, 3, 3, 'V', 'VVV'),
|
||||
'STDEV' : ( 12, 1, 30, 'V', 'D+'),
|
||||
'STDEVA' : (366, 1, 30, 'V', 'D+'),
|
||||
'STDEVP' : (193, 1, 30, 'V', 'D+'),
|
||||
'STDEVPA' : (364, 1, 30, 'V', 'D+'),
|
||||
'STEYX' : (314, 2, 2, 'V', 'AA'),
|
||||
'SUBSTITUTE' : (120, 3, 4, 'V', 'VVVV'),
|
||||
'SUBTOTAL' : (344, 2, 30, 'V', 'VR+'),
|
||||
'SUM' : ( 4, 1, 30, 'V', 'D+'),
|
||||
'SUMIF' : (345, 2, 3, 'V', 'RVR'),
|
||||
'SUMPRODUCT' : (228, 1, 30, 'V', 'A+'),
|
||||
'SUMSQ' : (321, 1, 30, 'V', 'D+'),
|
||||
'SUMX2MY2' : (304, 2, 2, 'V', 'AA'),
|
||||
'SUMX2PY2' : (305, 2, 2, 'V', 'AA'),
|
||||
'SUMXMY2' : (303, 2, 2, 'V', 'AA'),
|
||||
'SYD' : (143, 4, 4, 'V', 'VVVV'),
|
||||
'T' : (130, 1, 1, 'V', 'R'),
|
||||
'TAN' : ( 17, 1, 1, 'V', 'V'),
|
||||
'TANH' : (231, 1, 1, 'V', 'V'),
|
||||
'TBILLEQ' : ( -1, 3, 3, 'V', 'VVV'),
|
||||
'TBILLPRICE' : ( -1, 3, 3, 'V', 'VVV'),
|
||||
'TBILLYIELD' : ( -1, 3, 3, 'V', 'VVV'),
|
||||
'TDIST' : (301, 3, 3, 'V', 'VVV'),
|
||||
'TEXT' : ( 48, 2, 2, 'V', 'VV'),
|
||||
'TIME' : ( 66, 3, 3, 'V', 'VVV'),
|
||||
'TIMEVALUE' : (141, 1, 1, 'V', 'V'),
|
||||
'TINV' : (332, 2, 2, 'V', 'VV'),
|
||||
'TODAY' : (221, 0, 0, 'V', '-'),
|
||||
'TRANSPOSE' : ( 83, 1, 1, 'A', 'A'),
|
||||
'TREND' : ( 50, 1, 4, 'A', 'RRRV'),
|
||||
'TRIM' : (118, 1, 1, 'V', 'V'),
|
||||
'TRIMMEAN' : (331, 2, 2, 'V', 'RV'),
|
||||
'TRUE' : ( 34, 0, 0, 'V', '-'),
|
||||
'TRUNC' : (197, 1, 2, 'V', 'VV'),
|
||||
'TTEST' : (316, 4, 4, 'V', 'AAVV'),
|
||||
'TYPE' : ( 86, 1, 1, 'V', 'V'),
|
||||
'UPPER' : (113, 1, 1, 'V', 'V'),
|
||||
'USDOLLAR' : (204, 1, 2, 'V', 'VV'),
|
||||
'VALUE' : ( 33, 1, 1, 'V', 'V'),
|
||||
'VAR' : ( 46, 1, 30, 'V', 'D+'),
|
||||
'VARA' : (367, 1, 30, 'V', 'D+'),
|
||||
'VARP' : (194, 1, 30, 'V', 'D+'),
|
||||
'VARPA' : (365, 1, 30, 'V', 'D+'),
|
||||
'VDB' : (222, 5, 7, 'V', 'VVVVVVV'),
|
||||
'VLOOKUP' : (102, 3, 4, 'V', 'VRRV'),
|
||||
'WEEKDAY' : ( 70, 1, 2, 'V', 'VV'),
|
||||
'WEEKNUM' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'WEIBULL' : (302, 4, 4, 'V', 'VVVV'),
|
||||
'WORKDAY' : ( -1, 2, 3, 'V', 'VVR'),
|
||||
'XIRR' : ( -1, 2, 3, 'V', 'AAV'),
|
||||
'XNPV' : ( -1, 3, 3, 'V', 'VAA'),
|
||||
'YEAR' : ( 69, 1, 1, 'V', 'V'),
|
||||
'YEARFRAC' : ( -1, 2, 3, 'V', 'VVV'),
|
||||
'YIELD' : ( -1, 6, 7, 'V', 'VVVVVVV'),
|
||||
'YIELDDISC' : ( -1, 4, 5, 'V', 'VVVVV'),
|
||||
'YIELDMAT' : ( -1, 5, 6, 'V', 'VVVVVV'),
|
||||
'ZTEST' : (324, 2, 3, 'V', 'RVV'),
|
||||
}
|
||||
|
||||
# Formulas Parse things
|
||||
|
||||
ptgExp = 0x01
|
||||
ptgTbl = 0x02
|
||||
ptgAdd = 0x03
|
||||
ptgSub = 0x04
|
||||
ptgMul = 0x05
|
||||
ptgDiv = 0x06
|
||||
ptgPower = 0x07
|
||||
ptgConcat = 0x08
|
||||
ptgLT = 0x09
|
||||
ptgLE = 0x0a
|
||||
ptgEQ = 0x0b
|
||||
ptgGE = 0x0c
|
||||
ptgGT = 0x0d
|
||||
ptgNE = 0x0e
|
||||
ptgIsect = 0x0f
|
||||
ptgUnion = 0x10
|
||||
ptgRange = 0x11
|
||||
ptgUplus = 0x12
|
||||
ptgUminus = 0x13
|
||||
ptgPercent = 0x14
|
||||
ptgParen = 0x15
|
||||
ptgMissArg = 0x16
|
||||
ptgStr = 0x17
|
||||
ptgExtend = 0x18
|
||||
ptgAttr = 0x19
|
||||
ptgSheet = 0x1a
|
||||
ptgEndSheet = 0x1b
|
||||
ptgErr = 0x1c
|
||||
ptgBool = 0x1d
|
||||
ptgInt = 0x1e
|
||||
ptgNum = 0x1f
|
||||
|
||||
ptgArrayR = 0x20
|
||||
ptgFuncR = 0x21
|
||||
ptgFuncVarR = 0x22
|
||||
ptgNameR = 0x23
|
||||
ptgRefR = 0x24
|
||||
ptgAreaR = 0x25
|
||||
ptgMemAreaR = 0x26
|
||||
ptgMemErrR = 0x27
|
||||
ptgMemNoMemR = 0x28
|
||||
ptgMemFuncR = 0x29
|
||||
ptgRefErrR = 0x2a
|
||||
ptgAreaErrR = 0x2b
|
||||
ptgRefNR = 0x2c
|
||||
ptgAreaNR = 0x2d
|
||||
ptgMemAreaNR = 0x2e
|
||||
ptgMemNoMemNR = 0x2f
|
||||
ptgNameXR = 0x39
|
||||
ptgRef3dR = 0x3a
|
||||
ptgArea3dR = 0x3b
|
||||
ptgRefErr3dR = 0x3c
|
||||
ptgAreaErr3dR = 0x3d
|
||||
|
||||
ptgArrayV = 0x40
|
||||
ptgFuncV = 0x41
|
||||
ptgFuncVarV = 0x42
|
||||
ptgNameV = 0x43
|
||||
ptgRefV = 0x44
|
||||
ptgAreaV = 0x45
|
||||
ptgMemAreaV = 0x46
|
||||
ptgMemErrV = 0x47
|
||||
ptgMemNoMemV = 0x48
|
||||
ptgMemFuncV = 0x49
|
||||
ptgRefErrV = 0x4a
|
||||
ptgAreaErrV = 0x4b
|
||||
ptgRefNV = 0x4c
|
||||
ptgAreaNV = 0x4d
|
||||
ptgMemAreaNV = 0x4e
|
||||
ptgMemNoMemNV = 0x4f
|
||||
ptgFuncCEV = 0x58
|
||||
ptgNameXV = 0x59
|
||||
ptgRef3dV = 0x5a
|
||||
ptgArea3dV = 0x5b
|
||||
ptgRefErr3dV = 0x5c
|
||||
ptgAreaErr3dV = 0x5d
|
||||
|
||||
ptgArrayA = 0x60
|
||||
ptgFuncA = 0x61
|
||||
ptgFuncVarA = 0x62
|
||||
ptgNameA = 0x63
|
||||
ptgRefA = 0x64
|
||||
ptgAreaA = 0x65
|
||||
ptgMemAreaA = 0x66
|
||||
ptgMemErrA = 0x67
|
||||
ptgMemNoMemA = 0x68
|
||||
ptgMemFuncA = 0x69
|
||||
ptgRefErrA = 0x6a
|
||||
ptgAreaErrA = 0x6b
|
||||
ptgRefNA = 0x6c
|
||||
ptgAreaNA = 0x6d
|
||||
ptgMemAreaNA = 0x6e
|
||||
ptgMemNoMemNA = 0x6f
|
||||
ptgFuncCEA = 0x78
|
||||
ptgNameXA = 0x79
|
||||
ptgRef3dA = 0x7a
|
||||
ptgArea3dA = 0x7b
|
||||
ptgRefErr3dA = 0x7c
|
||||
ptgAreaErr3dA = 0x7d
|
||||
|
||||
|
||||
PtgNames = {
|
||||
ptgExp : "ptgExp",
|
||||
ptgTbl : "ptgTbl",
|
||||
ptgAdd : "ptgAdd",
|
||||
ptgSub : "ptgSub",
|
||||
ptgMul : "ptgMul",
|
||||
ptgDiv : "ptgDiv",
|
||||
ptgPower : "ptgPower",
|
||||
ptgConcat : "ptgConcat",
|
||||
ptgLT : "ptgLT",
|
||||
ptgLE : "ptgLE",
|
||||
ptgEQ : "ptgEQ",
|
||||
ptgGE : "ptgGE",
|
||||
ptgGT : "ptgGT",
|
||||
ptgNE : "ptgNE",
|
||||
ptgIsect : "ptgIsect",
|
||||
ptgUnion : "ptgUnion",
|
||||
ptgRange : "ptgRange",
|
||||
ptgUplus : "ptgUplus",
|
||||
ptgUminus : "ptgUminus",
|
||||
ptgPercent : "ptgPercent",
|
||||
ptgParen : "ptgParen",
|
||||
ptgMissArg : "ptgMissArg",
|
||||
ptgStr : "ptgStr",
|
||||
ptgExtend : "ptgExtend",
|
||||
ptgAttr : "ptgAttr",
|
||||
ptgSheet : "ptgSheet",
|
||||
ptgEndSheet : "ptgEndSheet",
|
||||
ptgErr : "ptgErr",
|
||||
ptgBool : "ptgBool",
|
||||
ptgInt : "ptgInt",
|
||||
ptgNum : "ptgNum",
|
||||
ptgArrayR : "ptgArrayR",
|
||||
ptgFuncR : "ptgFuncR",
|
||||
ptgFuncVarR : "ptgFuncVarR",
|
||||
ptgNameR : "ptgNameR",
|
||||
ptgRefR : "ptgRefR",
|
||||
ptgAreaR : "ptgAreaR",
|
||||
ptgMemAreaR : "ptgMemAreaR",
|
||||
ptgMemErrR : "ptgMemErrR",
|
||||
ptgMemNoMemR : "ptgMemNoMemR",
|
||||
ptgMemFuncR : "ptgMemFuncR",
|
||||
ptgRefErrR : "ptgRefErrR",
|
||||
ptgAreaErrR : "ptgAreaErrR",
|
||||
ptgRefNR : "ptgRefNR",
|
||||
ptgAreaNR : "ptgAreaNR",
|
||||
ptgMemAreaNR : "ptgMemAreaNR",
|
||||
ptgMemNoMemNR : "ptgMemNoMemNR",
|
||||
ptgNameXR : "ptgNameXR",
|
||||
ptgRef3dR : "ptgRef3dR",
|
||||
ptgArea3dR : "ptgArea3dR",
|
||||
ptgRefErr3dR : "ptgRefErr3dR",
|
||||
ptgAreaErr3dR : "ptgAreaErr3dR",
|
||||
ptgArrayV : "ptgArrayV",
|
||||
ptgFuncV : "ptgFuncV",
|
||||
ptgFuncVarV : "ptgFuncVarV",
|
||||
ptgNameV : "ptgNameV",
|
||||
ptgRefV : "ptgRefV",
|
||||
ptgAreaV : "ptgAreaV",
|
||||
ptgMemAreaV : "ptgMemAreaV",
|
||||
ptgMemErrV : "ptgMemErrV",
|
||||
ptgMemNoMemV : "ptgMemNoMemV",
|
||||
ptgMemFuncV : "ptgMemFuncV",
|
||||
ptgRefErrV : "ptgRefErrV",
|
||||
ptgAreaErrV : "ptgAreaErrV",
|
||||
ptgRefNV : "ptgRefNV",
|
||||
ptgAreaNV : "ptgAreaNV",
|
||||
ptgMemAreaNV : "ptgMemAreaNV",
|
||||
ptgMemNoMemNV : "ptgMemNoMemNV",
|
||||
ptgFuncCEV : "ptgFuncCEV",
|
||||
ptgNameXV : "ptgNameXV",
|
||||
ptgRef3dV : "ptgRef3dV",
|
||||
ptgArea3dV : "ptgArea3dV",
|
||||
ptgRefErr3dV : "ptgRefErr3dV",
|
||||
ptgAreaErr3dV : "ptgAreaErr3dV",
|
||||
ptgArrayA : "ptgArrayA",
|
||||
ptgFuncA : "ptgFuncA",
|
||||
ptgFuncVarA : "ptgFuncVarA",
|
||||
ptgNameA : "ptgNameA",
|
||||
ptgRefA : "ptgRefA",
|
||||
ptgAreaA : "ptgAreaA",
|
||||
ptgMemAreaA : "ptgMemAreaA",
|
||||
ptgMemErrA : "ptgMemErrA",
|
||||
ptgMemNoMemA : "ptgMemNoMemA",
|
||||
ptgMemFuncA : "ptgMemFuncA",
|
||||
ptgRefErrA : "ptgRefErrA",
|
||||
ptgAreaErrA : "ptgAreaErrA",
|
||||
ptgRefNA : "ptgRefNA",
|
||||
ptgAreaNA : "ptgAreaNA",
|
||||
ptgMemAreaNA : "ptgMemAreaNA",
|
||||
ptgMemNoMemNA : "ptgMemNoMemNA",
|
||||
ptgFuncCEA : "ptgFuncCEA",
|
||||
ptgNameXA : "ptgNameXA",
|
||||
ptgRef3dA : "ptgRef3dA",
|
||||
ptgArea3dA : "ptgArea3dA",
|
||||
ptgRefErr3dA : "ptgRefErr3dA",
|
||||
ptgAreaErr3dA : "ptgAreaErr3dA"
|
||||
}
|
||||
|
||||
|
||||
error_msg_by_code = {
|
||||
0x00: u"#NULL!", # intersection of two cell ranges is empty
|
||||
0x07: u"#DIV/0!", # division by zero
|
||||
0x0F: u"#VALUE!", # wrong type of operand
|
||||
0x17: u"#REF!", # illegal or deleted cell reference
|
||||
0x1D: u"#NAME?", # wrong function or range name
|
||||
0x24: u"#NUM!", # value range overflow
|
||||
0x2A: u"#N/A!" # argument or function not available
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
The XF record is able to store explicit cell formatting attributes or the
|
||||
attributes of a cell style. Explicit formatting includes the reference to
|
||||
a cell style XF record. This allows to extend a defined cell style with
|
||||
some explicit attributes. The formatting attributes are divided into
|
||||
6 groups:
|
||||
|
||||
Group Attributes
|
||||
-------------------------------------
|
||||
Number format Number format index (index to FORMAT record)
|
||||
Font Font index (index to FONT record)
|
||||
Alignment Horizontal and vertical alignment, text wrap, indentation,
|
||||
orientation/rotation, text direction
|
||||
Border Border line styles and colours
|
||||
Background Background area style and colours
|
||||
Protection Cell locked, formula hidden
|
||||
|
||||
For each group a flag in the cell XF record specifies whether to use the
|
||||
attributes contained in that XF record or in the referenced style
|
||||
XF record. In style XF records, these flags specify whether the attributes
|
||||
will overwrite explicit cell formatting when the style is applied to
|
||||
a cell. Changing a cell style (without applying this style to a cell) will
|
||||
change all cells which already use that style and do not contain explicit
|
||||
cell attributes for the changed style attributes. If a cell XF record does
|
||||
not contain explicit attributes in a group (if the attribute group flag
|
||||
is not set), it repeats the attributes of its style XF record.
|
||||
|
||||
'''
|
||||
|
||||
import BIFFRecords
|
||||
|
||||
class Font(object):
|
||||
|
||||
ESCAPEMENT_NONE = 0x00
|
||||
ESCAPEMENT_SUPERSCRIPT = 0x01
|
||||
ESCAPEMENT_SUBSCRIPT = 0x02
|
||||
|
||||
UNDERLINE_NONE = 0x00
|
||||
UNDERLINE_SINGLE = 0x01
|
||||
UNDERLINE_SINGLE_ACC = 0x21
|
||||
UNDERLINE_DOUBLE = 0x02
|
||||
UNDERLINE_DOUBLE_ACC = 0x22
|
||||
|
||||
FAMILY_NONE = 0x00
|
||||
FAMILY_ROMAN = 0x01
|
||||
FAMILY_SWISS = 0x02
|
||||
FAMILY_MODERN = 0x03
|
||||
FAMILY_SCRIPT = 0x04
|
||||
FAMILY_DECORATIVE = 0x05
|
||||
|
||||
CHARSET_ANSI_LATIN = 0x00
|
||||
CHARSET_SYS_DEFAULT = 0x01
|
||||
CHARSET_SYMBOL = 0x02
|
||||
CHARSET_APPLE_ROMAN = 0x4D
|
||||
CHARSET_ANSI_JAP_SHIFT_JIS = 0x80
|
||||
CHARSET_ANSI_KOR_HANGUL = 0x81
|
||||
CHARSET_ANSI_KOR_JOHAB = 0x82
|
||||
CHARSET_ANSI_CHINESE_GBK = 0x86
|
||||
CHARSET_ANSI_CHINESE_BIG5 = 0x88
|
||||
CHARSET_ANSI_GREEK = 0xA1
|
||||
CHARSET_ANSI_TURKISH = 0xA2
|
||||
CHARSET_ANSI_VIETNAMESE = 0xA3
|
||||
CHARSET_ANSI_HEBREW = 0xB1
|
||||
CHARSET_ANSI_ARABIC = 0xB2
|
||||
CHARSET_ANSI_BALTIC = 0xBA
|
||||
CHARSET_ANSI_CYRILLIC = 0xCC
|
||||
CHARSET_ANSI_THAI = 0xDE
|
||||
CHARSET_ANSI_LATIN_II = 0xEE
|
||||
CHARSET_OEM_LATIN_I = 0xFF
|
||||
|
||||
def __init__(self):
|
||||
# twip = 1/20 of a point = 1/1440 of a inch
|
||||
# usually resolution == 96 pixels per 1 inch
|
||||
# (rarely 120 pixels per 1 inch or another one)
|
||||
|
||||
self.height = 0x00C8 # 200: this is font with height 10 points
|
||||
self.italic = False
|
||||
self.struck_out = False
|
||||
self.outline = False
|
||||
self.shadow = False
|
||||
self.colour_index = 0x7FFF
|
||||
self.bold = False
|
||||
self._weight = 0x0190 # 0x02BC gives bold font
|
||||
self.escapement = self.ESCAPEMENT_NONE
|
||||
self.underline = self.UNDERLINE_NONE
|
||||
self.family = self.FAMILY_NONE
|
||||
self.charset = self.CHARSET_SYS_DEFAULT
|
||||
self.name = 'Arial'
|
||||
|
||||
def get_biff_record(self):
|
||||
height = self.height
|
||||
|
||||
options = 0x00
|
||||
if self.bold:
|
||||
options |= 0x01
|
||||
self._weight = 0x02BC
|
||||
if self.italic:
|
||||
options |= 0x02
|
||||
if self.underline != self.UNDERLINE_NONE:
|
||||
options |= 0x04
|
||||
if self.struck_out:
|
||||
options |= 0x08
|
||||
if self.outline:
|
||||
options |= 0x010
|
||||
if self.shadow:
|
||||
options |= 0x020
|
||||
|
||||
colour_index = self.colour_index
|
||||
weight = self._weight
|
||||
escapement = self.escapement
|
||||
underline = self.underline
|
||||
family = self.family
|
||||
charset = self.charset
|
||||
name = self.name
|
||||
|
||||
return BIFFRecords.FontRecord(height, options, colour_index, weight, escapement,
|
||||
underline, family, charset,
|
||||
name)
|
||||
|
||||
def _search_key(self):
|
||||
return (
|
||||
self.height,
|
||||
self.italic,
|
||||
self.struck_out,
|
||||
self.outline,
|
||||
self.shadow,
|
||||
self.colour_index,
|
||||
self.bold,
|
||||
self._weight,
|
||||
self.escapement,
|
||||
self.underline,
|
||||
self.family,
|
||||
self.charset,
|
||||
self.name,
|
||||
)
|
||||
|
||||
class Alignment(object):
|
||||
HORZ_GENERAL = 0x00
|
||||
HORZ_LEFT = 0x01
|
||||
HORZ_CENTER = 0x02
|
||||
HORZ_RIGHT = 0x03
|
||||
HORZ_FILLED = 0x04
|
||||
HORZ_JUSTIFIED = 0x05 # BIFF4-BIFF8X
|
||||
HORZ_CENTER_ACROSS_SEL = 0x06 # Centred across selection (BIFF4-BIFF8X)
|
||||
HORZ_DISTRIBUTED = 0x07 # Distributed (BIFF8X)
|
||||
|
||||
VERT_TOP = 0x00
|
||||
VERT_CENTER = 0x01
|
||||
VERT_BOTTOM = 0x02
|
||||
VERT_JUSTIFIED = 0x03 # Justified (BIFF5-BIFF8X)
|
||||
VERT_DISTRIBUTED = 0x04 # Distributed (BIFF8X)
|
||||
|
||||
DIRECTION_GENERAL = 0x00 # BIFF8X
|
||||
DIRECTION_LR = 0x01
|
||||
DIRECTION_RL = 0x02
|
||||
|
||||
ORIENTATION_NOT_ROTATED = 0x00
|
||||
ORIENTATION_STACKED = 0x01
|
||||
ORIENTATION_90_CC = 0x02
|
||||
ORIENTATION_90_CW = 0x03
|
||||
|
||||
ROTATION_0_ANGLE = 0x00
|
||||
ROTATION_STACKED = 0xFF
|
||||
|
||||
WRAP_AT_RIGHT = 0x01
|
||||
NOT_WRAP_AT_RIGHT = 0x00
|
||||
|
||||
SHRINK_TO_FIT = 0x01
|
||||
NOT_SHRINK_TO_FIT = 0x00
|
||||
|
||||
def __init__(self):
|
||||
self.horz = self.HORZ_GENERAL
|
||||
self.vert = self.VERT_BOTTOM
|
||||
self.dire = self.DIRECTION_GENERAL
|
||||
self.orie = self.ORIENTATION_NOT_ROTATED
|
||||
self.rota = self.ROTATION_0_ANGLE
|
||||
self.wrap = self.NOT_WRAP_AT_RIGHT
|
||||
self.shri = self.NOT_SHRINK_TO_FIT
|
||||
self.inde = 0
|
||||
self.merg = 0
|
||||
|
||||
def _search_key(self):
|
||||
return (
|
||||
self.horz, self.vert, self.dire, self.orie, self.rota,
|
||||
self.wrap, self.shri, self.inde, self.merg,
|
||||
)
|
||||
|
||||
class Borders(object):
|
||||
NO_LINE = 0x00
|
||||
THIN = 0x01
|
||||
MEDIUM = 0x02
|
||||
DASHED = 0x03
|
||||
DOTTED = 0x04
|
||||
THICK = 0x05
|
||||
DOUBLE = 0x06
|
||||
HAIR = 0x07
|
||||
#The following for BIFF8
|
||||
MEDIUM_DASHED = 0x08
|
||||
THIN_DASH_DOTTED = 0x09
|
||||
MEDIUM_DASH_DOTTED = 0x0A
|
||||
THIN_DASH_DOT_DOTTED = 0x0B
|
||||
MEDIUM_DASH_DOT_DOTTED = 0x0C
|
||||
SLANTED_MEDIUM_DASH_DOTTED = 0x0D
|
||||
|
||||
NEED_DIAG1 = 0x01
|
||||
NEED_DIAG2 = 0x01
|
||||
NO_NEED_DIAG1 = 0x00
|
||||
NO_NEED_DIAG2 = 0x00
|
||||
|
||||
def __init__(self):
|
||||
self.left = self.NO_LINE
|
||||
self.right = self.NO_LINE
|
||||
self.top = self.NO_LINE
|
||||
self.bottom = self.NO_LINE
|
||||
self.diag = self.NO_LINE
|
||||
|
||||
self.left_colour = 0x40
|
||||
self.right_colour = 0x40
|
||||
self.top_colour = 0x40
|
||||
self.bottom_colour = 0x40
|
||||
self.diag_colour = 0x40
|
||||
|
||||
self.need_diag1 = self.NO_NEED_DIAG1
|
||||
self.need_diag2 = self.NO_NEED_DIAG2
|
||||
|
||||
def _search_key(self):
|
||||
return (
|
||||
self.left, self.right, self.top, self.bottom, self.diag,
|
||||
self.left_colour, self.right_colour, self.top_colour,
|
||||
self.bottom_colour, self.diag_colour,
|
||||
self.need_diag1, self.need_diag2,
|
||||
)
|
||||
|
||||
class Pattern(object):
|
||||
# patterns 0x00 - 0x12
|
||||
NO_PATTERN = 0x00
|
||||
SOLID_PATTERN = 0x01
|
||||
|
||||
def __init__(self):
|
||||
self.pattern = self.NO_PATTERN
|
||||
self.pattern_fore_colour = 0x40
|
||||
self.pattern_back_colour = 0x41
|
||||
|
||||
def _search_key(self):
|
||||
return (
|
||||
self.pattern,
|
||||
self.pattern_fore_colour,
|
||||
self.pattern_back_colour,
|
||||
)
|
||||
|
||||
class Protection(object):
|
||||
def __init__(self):
|
||||
self.cell_locked = 1
|
||||
self.formula_hidden = 0
|
||||
|
||||
def _search_key(self):
|
||||
return (
|
||||
self.cell_locked,
|
||||
self.formula_hidden,
|
||||
)
|
||||
@@ -1,253 +0,0 @@
|
||||
# -*- coding: windows-1252 -*-
|
||||
|
||||
import BIFFRecords
|
||||
import Style
|
||||
from Cell import StrCell, BlankCell, NumberCell, FormulaCell, MulBlankCell, BooleanCell, ErrorCell, \
|
||||
_get_cells_biff_data_mul
|
||||
import ExcelFormula
|
||||
import datetime as dt
|
||||
try:
|
||||
from decimal import Decimal
|
||||
except ImportError:
|
||||
# Python 2.3: decimal not supported; create dummy Decimal class
|
||||
class Decimal(object):
|
||||
pass
|
||||
|
||||
|
||||
class Row(object):
|
||||
__slots__ = [# private variables
|
||||
"__idx",
|
||||
"__parent",
|
||||
"__parent_wb",
|
||||
"__cells",
|
||||
"__min_col_idx",
|
||||
"__max_col_idx",
|
||||
"__xf_index",
|
||||
"__has_default_xf_index",
|
||||
"__height_in_pixels",
|
||||
# public variables
|
||||
"height",
|
||||
"has_default_height",
|
||||
"height_mismatch",
|
||||
"level",
|
||||
"collapse",
|
||||
"hidden",
|
||||
"space_above",
|
||||
"space_below"]
|
||||
|
||||
def __init__(self, rowx, parent_sheet):
|
||||
if not (isinstance(rowx, int) and 0 <= rowx <= 65535):
|
||||
raise ValueError("row index (%r) not an int in range(65536)" % rowx)
|
||||
self.__idx = rowx
|
||||
self.__parent = parent_sheet
|
||||
self.__parent_wb = parent_sheet.get_parent()
|
||||
self.__cells = {}
|
||||
self.__min_col_idx = 0
|
||||
self.__max_col_idx = 0
|
||||
self.__xf_index = 0x0F
|
||||
self.__has_default_xf_index = 0
|
||||
self.__height_in_pixels = 0x11
|
||||
|
||||
self.height = 0x00FF
|
||||
self.has_default_height = 0x00
|
||||
self.height_mismatch = 0
|
||||
self.level = 0
|
||||
self.collapse = 0
|
||||
self.hidden = 0
|
||||
self.space_above = 0
|
||||
self.space_below = 0
|
||||
|
||||
|
||||
def __adjust_height(self, style):
|
||||
twips = style.font.height
|
||||
points = float(twips)/20.0
|
||||
# Cell height in pixels can be calcuted by following approx. formula:
|
||||
# cell height in pixels = font height in points * 83/50 + 2/5
|
||||
# It works when screen resolution is 96 dpi
|
||||
pix = int(round(points*83.0/50.0 + 2.0/5.0))
|
||||
if pix > self.__height_in_pixels:
|
||||
self.__height_in_pixels = pix
|
||||
|
||||
|
||||
def __adjust_bound_col_idx(self, *args):
|
||||
for arg in args:
|
||||
iarg = int(arg)
|
||||
if not ((0 <= iarg <= 255) and arg == iarg):
|
||||
raise ValueError("column index (%r) not an int in range(256)" % arg)
|
||||
sheet = self.__parent
|
||||
if iarg < self.__min_col_idx:
|
||||
self.__min_col_idx = iarg
|
||||
if iarg > self.__max_col_idx:
|
||||
self.__max_col_idx = iarg
|
||||
if iarg < sheet.first_used_col:
|
||||
sheet.first_used_col = iarg
|
||||
if iarg > sheet.last_used_col:
|
||||
sheet.last_used_col = iarg
|
||||
|
||||
def __excel_date_dt(self, date):
|
||||
if isinstance(date, dt.date) and (not isinstance(date, dt.datetime)):
|
||||
epoch = dt.date(1899, 12, 31)
|
||||
elif isinstance(date, dt.time):
|
||||
date = dt.datetime.combine(dt.datetime(1900, 1, 1), date)
|
||||
epoch = dt.datetime(1900, 1, 1, 0, 0, 0)
|
||||
else:
|
||||
epoch = dt.datetime(1899, 12, 31, 0, 0, 0)
|
||||
delta = date - epoch
|
||||
xldate = delta.days + float(delta.seconds) / (24*60*60)
|
||||
# Add a day for Excel's missing leap day in 1900
|
||||
if xldate > 59:
|
||||
xldate += 1
|
||||
return xldate
|
||||
|
||||
def get_height_in_pixels(self):
|
||||
return self.__height_in_pixels
|
||||
|
||||
|
||||
def set_style(self, style):
|
||||
self.__adjust_height(style)
|
||||
self.__xf_index = self.__parent_wb.add_style(style)
|
||||
self.__has_default_xf_index = 1
|
||||
|
||||
|
||||
def get_xf_index(self):
|
||||
return self.__xf_index
|
||||
|
||||
|
||||
def get_cells_count(self):
|
||||
return len(self.__cells)
|
||||
|
||||
|
||||
def get_min_col(self):
|
||||
return self.__min_col_idx
|
||||
|
||||
|
||||
def get_max_col(self):
|
||||
return self.__max_col_idx
|
||||
|
||||
|
||||
def get_row_biff_data(self):
|
||||
height_options = (self.height & 0x07FFF)
|
||||
height_options |= (self.has_default_height & 0x01) << 15
|
||||
|
||||
options = (self.level & 0x07) << 0
|
||||
options |= (self.collapse & 0x01) << 4
|
||||
options |= (self.hidden & 0x01) << 5
|
||||
options |= (self.height_mismatch & 0x01) << 6
|
||||
options |= (self.__has_default_xf_index & 0x01) << 7
|
||||
options |= (0x01 & 0x01) << 8
|
||||
options |= (self.__xf_index & 0x0FFF) << 16
|
||||
options |= (self.space_above & 1) << 28
|
||||
options |= (self.space_below & 1) << 29
|
||||
|
||||
return BIFFRecords.RowRecord(self.__idx, self.__min_col_idx,
|
||||
self.__max_col_idx, height_options, options).get()
|
||||
|
||||
def insert_cell(self, col_index, cell_obj):
|
||||
if col_index in self.__cells:
|
||||
if not self.__parent._cell_overwrite_ok:
|
||||
msg = "Attempt to overwrite cell: sheetname=%r rowx=%d colx=%d" \
|
||||
% (self.__parent.name, self.__idx, col_index)
|
||||
raise Exception(msg)
|
||||
prev_cell_obj = self.__cells[col_index]
|
||||
sst_idx = getattr(prev_cell_obj, 'sst_idx', None)
|
||||
if sst_idx is not None:
|
||||
self.__parent_wb.del_str(sst_idx)
|
||||
self.__cells[col_index] = cell_obj
|
||||
|
||||
def insert_mulcells(self, colx1, colx2, cell_obj):
|
||||
self.insert_cell(colx1, cell_obj)
|
||||
for col_index in xrange(colx1+1, colx2+1):
|
||||
self.insert_cell(col_index, None)
|
||||
|
||||
def get_cells_biff_data(self):
|
||||
cell_items = [item for item in self.__cells.iteritems() if item[1] is not None]
|
||||
cell_items.sort() # in column order
|
||||
return _get_cells_biff_data_mul(self.__idx, cell_items)
|
||||
# previously:
|
||||
# return ''.join([cell.get_biff_data() for colx, cell in cell_items])
|
||||
|
||||
def get_index(self):
|
||||
return self.__idx
|
||||
|
||||
def set_cell_text(self, colx, value, style=Style.default_style):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
self.insert_cell(colx, StrCell(self.__idx, colx, xf_index, self.__parent_wb.add_str(value)))
|
||||
|
||||
def set_cell_blank(self, colx, style=Style.default_style):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
self.insert_cell(colx, BlankCell(self.__idx, colx, xf_index))
|
||||
|
||||
def set_cell_mulblanks(self, first_colx, last_colx, style=Style.default_style):
|
||||
assert 0 <= first_colx <= last_colx <= 255
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(first_colx, last_colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
# ncols = last_colx - first_colx + 1
|
||||
self.insert_mulcells(first_colx, last_colx, MulBlankCell(self.__idx, first_colx, last_colx, xf_index))
|
||||
|
||||
def set_cell_number(self, colx, number, style=Style.default_style):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
self.insert_cell(colx, NumberCell(self.__idx, colx, xf_index, number))
|
||||
|
||||
def set_cell_date(self, colx, datetime_obj, style=Style.default_style):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
self.insert_cell(colx,
|
||||
NumberCell(self.__idx, colx, xf_index, self.__excel_date_dt(datetime_obj)))
|
||||
|
||||
def set_cell_formula(self, colx, formula, style=Style.default_style, calc_flags=0):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
self.__parent_wb.add_sheet_reference(formula)
|
||||
self.insert_cell(colx, FormulaCell(self.__idx, colx, xf_index, formula, calc_flags=0))
|
||||
|
||||
def set_cell_boolean(self, colx, value, style=Style.default_style):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
self.insert_cell(colx, BooleanCell(self.__idx, colx, xf_index, bool(value)))
|
||||
|
||||
def set_cell_error(self, colx, error_string_or_code, style=Style.default_style):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
self.insert_cell(colx, ErrorCell(self.__idx, colx, xf_index, error_string_or_code))
|
||||
|
||||
def write(self, col, label, style=Style.default_style):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(col)
|
||||
style_index = self.__parent_wb.add_style(style)
|
||||
if isinstance(label, basestring):
|
||||
if len(label) > 0:
|
||||
self.insert_cell(col,
|
||||
StrCell(self.__idx, col, style_index, self.__parent_wb.add_str(label))
|
||||
)
|
||||
else:
|
||||
self.insert_cell(col, BlankCell(self.__idx, col, style_index))
|
||||
elif isinstance(label, bool): # bool is subclass of int; test bool first
|
||||
self.insert_cell(col, BooleanCell(self.__idx, col, style_index, label))
|
||||
elif isinstance(label, (float, int, long, Decimal)):
|
||||
self.insert_cell(col, NumberCell(self.__idx, col, style_index, label))
|
||||
elif isinstance(label, (dt.datetime, dt.date, dt.time)):
|
||||
date_number = self.__excel_date_dt(label)
|
||||
self.insert_cell(col, NumberCell(self.__idx, col, style_index, date_number))
|
||||
elif label is None:
|
||||
self.insert_cell(col, BlankCell(self.__idx, col, style_index))
|
||||
elif isinstance(label, ExcelFormula.Formula):
|
||||
self.__parent_wb.add_sheet_reference(label)
|
||||
self.insert_cell(col, FormulaCell(self.__idx, col, style_index, label))
|
||||
else:
|
||||
raise Exception("Unexpected data type %r" % type(label))
|
||||
|
||||
write_blanks = set_cell_mulblanks
|
||||
|
||||
|
||||
|
||||
@@ -1,592 +0,0 @@
|
||||
# -*- coding: windows-1252 -*-
|
||||
|
||||
import Formatting
|
||||
from BIFFRecords import *
|
||||
|
||||
FIRST_USER_DEFINED_NUM_FORMAT_IDX = 164
|
||||
|
||||
class XFStyle(object):
|
||||
|
||||
def __init__(self):
|
||||
self.num_format_str = 'General'
|
||||
self.font = Formatting.Font()
|
||||
self.alignment = Formatting.Alignment()
|
||||
self.borders = Formatting.Borders()
|
||||
self.pattern = Formatting.Pattern()
|
||||
self.protection = Formatting.Protection()
|
||||
|
||||
default_style = XFStyle()
|
||||
|
||||
class StyleCollection(object):
|
||||
_std_num_fmt_list = [
|
||||
'general',
|
||||
'0',
|
||||
'0.00',
|
||||
'#,##0',
|
||||
'#,##0.00',
|
||||
'"$"#,##0_);("$"#,##',
|
||||
'"$"#,##0_);[Red]("$"#,##',
|
||||
'"$"#,##0.00_);("$"#,##',
|
||||
'"$"#,##0.00_);[Red]("$"#,##',
|
||||
'0%',
|
||||
'0.00%',
|
||||
'0.00E+00',
|
||||
'# ?/?',
|
||||
'# ??/??',
|
||||
'M/D/YY',
|
||||
'D-MMM-YY',
|
||||
'D-MMM',
|
||||
'MMM-YY',
|
||||
'h:mm AM/PM',
|
||||
'h:mm:ss AM/PM',
|
||||
'h:mm',
|
||||
'h:mm:ss',
|
||||
'M/D/YY h:mm',
|
||||
'_(#,##0_);(#,##0)',
|
||||
'_(#,##0_);[Red](#,##0)',
|
||||
'_(#,##0.00_);(#,##0.00)',
|
||||
'_(#,##0.00_);[Red](#,##0.00)',
|
||||
'_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)',
|
||||
'_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)',
|
||||
'_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)',
|
||||
'_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)',
|
||||
'mm:ss',
|
||||
'[h]:mm:ss',
|
||||
'mm:ss.0',
|
||||
'##0.0E+0',
|
||||
'@'
|
||||
]
|
||||
|
||||
def __init__(self, style_compression=0):
|
||||
self.style_compression = style_compression
|
||||
self.stats = [0, 0, 0, 0, 0, 0]
|
||||
self._font_id2x = {}
|
||||
self._font_x2id = {}
|
||||
self._font_val2x = {}
|
||||
|
||||
for x in (0, 1, 2, 3, 5): # The font with index 4 is omitted in all BIFF versions
|
||||
font = Formatting.Font()
|
||||
search_key = font._search_key()
|
||||
self._font_id2x[font] = x
|
||||
self._font_x2id[x] = font
|
||||
self._font_val2x[search_key] = x
|
||||
|
||||
self._xf_id2x = {}
|
||||
self._xf_x2id = {}
|
||||
self._xf_val2x = {}
|
||||
|
||||
self._num_formats = {}
|
||||
for fmtidx, fmtstr in zip(range(0, 23), StyleCollection._std_num_fmt_list[0:23]):
|
||||
self._num_formats[fmtstr] = fmtidx
|
||||
for fmtidx, fmtstr in zip(range(37, 50), StyleCollection._std_num_fmt_list[23:]):
|
||||
self._num_formats[fmtstr] = fmtidx
|
||||
|
||||
self.default_style = XFStyle()
|
||||
self._default_xf = self._add_style(self.default_style)[0]
|
||||
|
||||
def add(self, style):
|
||||
if style == None:
|
||||
return 0x10
|
||||
return self._add_style(style)[1]
|
||||
|
||||
def _add_style(self, style):
|
||||
num_format_str = style.num_format_str
|
||||
if num_format_str in self._num_formats:
|
||||
num_format_idx = self._num_formats[num_format_str]
|
||||
else:
|
||||
num_format_idx = (
|
||||
FIRST_USER_DEFINED_NUM_FORMAT_IDX
|
||||
+ len(self._num_formats)
|
||||
- len(StyleCollection._std_num_fmt_list)
|
||||
)
|
||||
self._num_formats[num_format_str] = num_format_idx
|
||||
|
||||
font = style.font
|
||||
if font in self._font_id2x:
|
||||
font_idx = self._font_id2x[font]
|
||||
self.stats[0] += 1
|
||||
elif self.style_compression:
|
||||
search_key = font._search_key()
|
||||
font_idx = self._font_val2x.get(search_key)
|
||||
if font_idx is not None:
|
||||
self._font_id2x[font] = font_idx
|
||||
self.stats[1] += 1
|
||||
else:
|
||||
font_idx = len(self._font_x2id) + 1 # Why plus 1? Font 4 is missing
|
||||
self._font_id2x[font] = font_idx
|
||||
self._font_val2x[search_key] = font_idx
|
||||
self._font_x2id[font_idx] = font
|
||||
self.stats[2] += 1
|
||||
else:
|
||||
font_idx = len(self._font_id2x) + 1
|
||||
self._font_id2x[font] = font_idx
|
||||
self.stats[2] += 1
|
||||
|
||||
gof = (style.alignment, style.borders, style.pattern, style.protection)
|
||||
xf = (font_idx, num_format_idx) + gof
|
||||
if xf in self._xf_id2x:
|
||||
xf_index = self._xf_id2x[xf]
|
||||
self.stats[3] += 1
|
||||
elif self.style_compression == 2:
|
||||
xf_key = (font_idx, num_format_idx) + tuple([obj._search_key() for obj in gof])
|
||||
xf_index = self._xf_val2x.get(xf_key)
|
||||
if xf_index is not None:
|
||||
self._xf_id2x[xf] = xf_index
|
||||
self.stats[4] += 1
|
||||
else:
|
||||
xf_index = 0x10 + len(self._xf_x2id)
|
||||
self._xf_id2x[xf] = xf_index
|
||||
self._xf_val2x[xf_key] = xf_index
|
||||
self._xf_x2id[xf_index] = xf
|
||||
self.stats[5] += 1
|
||||
else:
|
||||
xf_index = 0x10 + len(self._xf_id2x)
|
||||
self._xf_id2x[xf] = xf_index
|
||||
self.stats[5] += 1
|
||||
|
||||
if xf_index >= 0xFFF:
|
||||
# 12 bits allowed, 0xFFF is a sentinel value
|
||||
raise ValueError("More than 4094 XFs (styles)")
|
||||
|
||||
return xf, xf_index
|
||||
|
||||
def get_biff_data(self):
|
||||
result = ''
|
||||
result += self._all_fonts()
|
||||
result += self._all_num_formats()
|
||||
result += self._all_cell_styles()
|
||||
result += self._all_styles()
|
||||
return result
|
||||
|
||||
def _all_fonts(self):
|
||||
result = ''
|
||||
if self.style_compression:
|
||||
alist = self._font_x2id.items()
|
||||
else:
|
||||
alist = [(x, o) for o, x in self._font_id2x.items()]
|
||||
alist.sort()
|
||||
for font_idx, font in alist:
|
||||
result += font.get_biff_record().get()
|
||||
return result
|
||||
|
||||
def _all_num_formats(self):
|
||||
result = ''
|
||||
alist = [
|
||||
(v, k)
|
||||
for k, v in self._num_formats.items()
|
||||
if v >= FIRST_USER_DEFINED_NUM_FORMAT_IDX
|
||||
]
|
||||
alist.sort()
|
||||
for fmtidx, fmtstr in alist:
|
||||
result += NumberFormatRecord(fmtidx, fmtstr).get()
|
||||
return result
|
||||
|
||||
def _all_cell_styles(self):
|
||||
result = ''
|
||||
for i in range(0, 16):
|
||||
result += XFRecord(self._default_xf, 'style').get()
|
||||
if self.style_compression == 2:
|
||||
alist = self._xf_x2id.items()
|
||||
else:
|
||||
alist = [(x, o) for o, x in self._xf_id2x.items()]
|
||||
alist.sort()
|
||||
for xf_idx, xf in alist:
|
||||
result += XFRecord(xf).get()
|
||||
return result
|
||||
|
||||
def _all_styles(self):
|
||||
return StyleRecord().get()
|
||||
|
||||
# easyxf and its supporting objects ###################################
|
||||
|
||||
class EasyXFException(Exception):
|
||||
pass
|
||||
|
||||
class EasyXFCallerError(EasyXFException):
|
||||
pass
|
||||
|
||||
class EasyXFAuthorError(EasyXFException):
|
||||
pass
|
||||
|
||||
class IntULim(object):
|
||||
# If astring represents a valid unsigned integer ('123', '0xabcd', etc)
|
||||
# and it is <= limit, return the int value; otherwise return None.
|
||||
|
||||
def __init__(self, limit):
|
||||
self.limit = limit
|
||||
|
||||
def __call__(self, astring):
|
||||
try:
|
||||
value = int(astring, 0)
|
||||
except ValueError:
|
||||
return None
|
||||
if not 0 <= value <= self.limit:
|
||||
return None
|
||||
return value
|
||||
|
||||
bool_map = {
|
||||
# Text values for all Boolean attributes
|
||||
'1': 1, 'yes': 1, 'true': 1, 'on': 1,
|
||||
'0': 0, 'no': 0, 'false': 0, 'off': 0,
|
||||
}
|
||||
|
||||
border_line_map = {
|
||||
# Text values for these borders attributes:
|
||||
# left, right, top, bottom and diag
|
||||
'no_line': 0x00,
|
||||
'thin': 0x01,
|
||||
'medium': 0x02,
|
||||
'dashed': 0x03,
|
||||
'dotted': 0x04,
|
||||
'thick': 0x05,
|
||||
'double': 0x06,
|
||||
'hair': 0x07,
|
||||
'medium_dashed': 0x08,
|
||||
'thin_dash_dotted': 0x09,
|
||||
'medium_dash_dotted': 0x0a,
|
||||
'thin_dash_dot_dotted': 0x0b,
|
||||
'medium_dash_dot_dotted': 0x0c,
|
||||
'slanted_medium_dash_dotted': 0x0d,
|
||||
}
|
||||
|
||||
charset_map = {
|
||||
# Text values for font.charset
|
||||
'ansi_latin': 0x00,
|
||||
'sys_default': 0x01,
|
||||
'symbol': 0x02,
|
||||
'apple_roman': 0x4d,
|
||||
'ansi_jap_shift_jis': 0x80,
|
||||
'ansi_kor_hangul': 0x81,
|
||||
'ansi_kor_johab': 0x82,
|
||||
'ansi_chinese_gbk': 0x86,
|
||||
'ansi_chinese_big5': 0x88,
|
||||
'ansi_greek': 0xa1,
|
||||
'ansi_turkish': 0xa2,
|
||||
'ansi_vietnamese': 0xa3,
|
||||
'ansi_hebrew': 0xb1,
|
||||
'ansi_arabic': 0xb2,
|
||||
'ansi_baltic': 0xba,
|
||||
'ansi_cyrillic': 0xcc,
|
||||
'ansi_thai': 0xde,
|
||||
'ansi_latin_ii': 0xee,
|
||||
'oem_latin_i': 0xff,
|
||||
}
|
||||
|
||||
|
||||
# Text values for colour indices. "grey" is a synonym of "gray".
|
||||
# The names are those given by Microsoft Excel 2003 to the colours
|
||||
# in the default palette. There is no great correspondence with
|
||||
# any W3C name-to-RGB mapping.
|
||||
_colour_map_text = """\
|
||||
aqua 0x31
|
||||
black 0x08
|
||||
blue 0x0C
|
||||
blue_gray 0x36
|
||||
bright_green 0x0B
|
||||
brown 0x3C
|
||||
coral 0x1D
|
||||
cyan_ega 0x0F
|
||||
dark_blue 0x12
|
||||
dark_blue_ega 0x12
|
||||
dark_green 0x3A
|
||||
dark_green_ega 0x11
|
||||
dark_purple 0x1C
|
||||
dark_red 0x10
|
||||
dark_red_ega 0x10
|
||||
dark_teal 0x38
|
||||
dark_yellow 0x13
|
||||
gold 0x33
|
||||
gray_ega 0x17
|
||||
gray25 0x16
|
||||
gray40 0x37
|
||||
gray50 0x17
|
||||
gray80 0x3F
|
||||
green 0x11
|
||||
ice_blue 0x1F
|
||||
indigo 0x3E
|
||||
ivory 0x1A
|
||||
lavender 0x2E
|
||||
light_blue 0x30
|
||||
light_green 0x2A
|
||||
light_orange 0x34
|
||||
light_turquoise 0x29
|
||||
light_yellow 0x2B
|
||||
lime 0x32
|
||||
magenta_ega 0x0E
|
||||
ocean_blue 0x1E
|
||||
olive_ega 0x13
|
||||
olive_green 0x3B
|
||||
orange 0x35
|
||||
pale_blue 0x2C
|
||||
periwinkle 0x18
|
||||
pink 0x0E
|
||||
plum 0x3D
|
||||
purple_ega 0x14
|
||||
red 0x0A
|
||||
rose 0x2D
|
||||
sea_green 0x39
|
||||
silver_ega 0x16
|
||||
sky_blue 0x28
|
||||
tan 0x2F
|
||||
teal 0x15
|
||||
teal_ega 0x15
|
||||
turquoise 0x0F
|
||||
violet 0x14
|
||||
white 0x09
|
||||
yellow 0x0D"""
|
||||
|
||||
colour_map = {}
|
||||
for _line in _colour_map_text.splitlines():
|
||||
_name, _num = _line.split()
|
||||
_num = int(_num, 0)
|
||||
colour_map[_name] = _num
|
||||
if 'gray' in _name:
|
||||
colour_map[_name.replace('gray', 'grey')] = _num
|
||||
del _colour_map_text, _line, _name, _num
|
||||
|
||||
|
||||
pattern_map = {
|
||||
# Text values for pattern.pattern
|
||||
# xlwt/doc/pattern_examples.xls showcases all of these patterns.
|
||||
'no_fill': 0,
|
||||
'none': 0,
|
||||
'solid': 1,
|
||||
'solid_fill': 1,
|
||||
'solid_pattern': 1,
|
||||
'fine_dots': 2,
|
||||
'alt_bars': 3,
|
||||
'sparse_dots': 4,
|
||||
'thick_horz_bands': 5,
|
||||
'thick_vert_bands': 6,
|
||||
'thick_backward_diag': 7,
|
||||
'thick_forward_diag': 8,
|
||||
'big_spots': 9,
|
||||
'bricks': 10,
|
||||
'thin_horz_bands': 11,
|
||||
'thin_vert_bands': 12,
|
||||
'thin_backward_diag': 13,
|
||||
'thin_forward_diag': 14,
|
||||
'squares': 15,
|
||||
'diamonds': 16,
|
||||
}
|
||||
|
||||
def any_str_func(s):
|
||||
return s.strip()
|
||||
|
||||
def colour_index_func(s, maxval=0x7F):
|
||||
try:
|
||||
value = int(s, 0)
|
||||
except ValueError:
|
||||
return None
|
||||
if not (0 <= value <= maxval):
|
||||
return None
|
||||
return value
|
||||
|
||||
colour_index_func_7 = colour_index_func
|
||||
|
||||
def colour_index_func_15(s):
|
||||
return colour_index_func(s, maxval=0x7FFF)
|
||||
|
||||
def rotation_func(s):
|
||||
try:
|
||||
value = int(s, 0)
|
||||
except ValueError:
|
||||
return None
|
||||
if not (-90 <= value <= 90):
|
||||
raise EasyXFCallerError("rotation %d: should be -90 to +90 degrees" % value)
|
||||
if value < 0:
|
||||
value = 90 - value # encode as 91 to 180 (clockwise)
|
||||
return value
|
||||
|
||||
xf_dict = {
|
||||
'align': 'alignment', # synonym
|
||||
'alignment': {
|
||||
'dire': {
|
||||
'general': 0,
|
||||
'lr': 1,
|
||||
'rl': 2,
|
||||
},
|
||||
'direction': 'dire',
|
||||
'horiz': 'horz',
|
||||
'horizontal': 'horz',
|
||||
'horz': {
|
||||
'general': 0,
|
||||
'left': 1,
|
||||
'center': 2,
|
||||
'centre': 2, # "align: horiz centre" means xf.alignment.horz is set to 2
|
||||
'right': 3,
|
||||
'filled': 4,
|
||||
'justified': 5,
|
||||
'center_across_selection': 6,
|
||||
'centre_across_selection': 6,
|
||||
'distributed': 7,
|
||||
},
|
||||
'inde': IntULim(15), # restriction: 0 <= value <= 15
|
||||
'indent': 'inde',
|
||||
'rota': [{'stacked': 255, 'none': 0, }, rotation_func],
|
||||
'rotation': 'rota',
|
||||
'shri': bool_map,
|
||||
'shrink': 'shri',
|
||||
'shrink_to_fit': 'shri',
|
||||
'vert': {
|
||||
'top': 0,
|
||||
'center': 1,
|
||||
'centre': 1,
|
||||
'bottom': 2,
|
||||
'justified': 3,
|
||||
'distributed': 4,
|
||||
},
|
||||
'vertical': 'vert',
|
||||
'wrap': bool_map,
|
||||
},
|
||||
'border': 'borders',
|
||||
'borders': {
|
||||
'left': [border_line_map, IntULim(0x0d)],
|
||||
'right': [border_line_map, IntULim(0x0d)],
|
||||
'top': [border_line_map, IntULim(0x0d)],
|
||||
'bottom': [border_line_map, IntULim(0x0d)],
|
||||
'diag': [border_line_map, IntULim(0x0d)],
|
||||
'top_colour': [colour_map, colour_index_func_7],
|
||||
'bottom_colour': [colour_map, colour_index_func_7],
|
||||
'left_colour': [colour_map, colour_index_func_7],
|
||||
'right_colour': [colour_map, colour_index_func_7],
|
||||
'diag_colour': [colour_map, colour_index_func_7],
|
||||
'top_color': 'top_colour',
|
||||
'bottom_color': 'bottom_colour',
|
||||
'left_color': 'left_colour',
|
||||
'right_color': 'right_colour',
|
||||
'diag_color': 'diag-colour',
|
||||
'need_diag_1': bool_map,
|
||||
'need_diag_2': bool_map,
|
||||
},
|
||||
'font': {
|
||||
'bold': bool_map,
|
||||
'charset': charset_map,
|
||||
'color': 'colour_index',
|
||||
'color_index': 'colour_index',
|
||||
'colour': 'colour_index',
|
||||
'colour_index': [colour_map, colour_index_func_15],
|
||||
'escapement': {'none': 0, 'superscript': 1, 'subscript': 2},
|
||||
'family': {'none': 0, 'roman': 1, 'swiss': 2, 'modern': 3, 'script': 4, 'decorative': 5, },
|
||||
'height': IntULim(0xFFFF), # practical limits are much narrower e.g. 160 to 1440 (8pt to 72pt)
|
||||
'italic': bool_map,
|
||||
'name': any_str_func,
|
||||
'outline': bool_map,
|
||||
'shadow': bool_map,
|
||||
'struck_out': bool_map,
|
||||
'underline': [bool_map, {'none': 0, 'single': 1, 'single_acc': 0x21, 'double': 2, 'double_acc': 0x22, }],
|
||||
},
|
||||
'pattern': {
|
||||
'back_color': 'pattern_back_colour',
|
||||
'back_colour': 'pattern_back_colour',
|
||||
'fore_color': 'pattern_fore_colour',
|
||||
'fore_colour': 'pattern_fore_colour',
|
||||
'pattern': [pattern_map, IntULim(16)],
|
||||
'pattern_back_color': 'pattern_back_colour',
|
||||
'pattern_back_colour': [colour_map, colour_index_func_7],
|
||||
'pattern_fore_color': 'pattern_fore_colour',
|
||||
'pattern_fore_colour': [colour_map, colour_index_func_7],
|
||||
},
|
||||
'protection': {
|
||||
'cell_locked' : bool_map,
|
||||
'formula_hidden': bool_map,
|
||||
},
|
||||
}
|
||||
|
||||
def _esplit(s, split_char, esc_char="\\"):
|
||||
escaped = False
|
||||
olist = ['']
|
||||
for c in s:
|
||||
if escaped:
|
||||
olist[-1] += c
|
||||
escaped = False
|
||||
elif c == esc_char:
|
||||
escaped = True
|
||||
elif c == split_char:
|
||||
olist.append('')
|
||||
else:
|
||||
olist[-1] += c
|
||||
return olist
|
||||
|
||||
def _parse_strg_to_obj(strg, obj, parse_dict,
|
||||
field_sep=",", line_sep=";", intro_sep=":", esc_char="\\", debug=False):
|
||||
for line in _esplit(strg, line_sep, esc_char):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
break
|
||||
split_line = _esplit(line, intro_sep, esc_char)
|
||||
if len(split_line) != 2:
|
||||
raise EasyXFCallerError('line %r should have exactly 1 "%c"' % (line, intro_sep))
|
||||
section, item_str = split_line
|
||||
section = section.strip().lower()
|
||||
for counter in range(2):
|
||||
result = parse_dict.get(section)
|
||||
if result is None:
|
||||
raise EasyXFCallerError('section %r is unknown' % section)
|
||||
if isinstance(result, dict):
|
||||
break
|
||||
if not isinstance(result, str):
|
||||
raise EasyXFAuthorError(
|
||||
'section %r should map to dict or str object; found %r' % (section, type(result)))
|
||||
# synonym
|
||||
old_section = section
|
||||
section = result
|
||||
else:
|
||||
raise EasyXFAuthorError('Attempt to define synonym of synonym (%r: %r)' % (old_section, result))
|
||||
section_dict = result
|
||||
section_obj = getattr(obj, section, None)
|
||||
if section_obj is None:
|
||||
raise EasyXFAuthorError('instance of %s class has no attribute named %s' % (obj.__class__.__name__, section))
|
||||
for kv_str in _esplit(item_str, field_sep, esc_char):
|
||||
guff = kv_str.split()
|
||||
if not guff:
|
||||
continue
|
||||
k = guff[0].lower().replace('-', '_')
|
||||
v = ' '.join(guff[1:])
|
||||
if not v:
|
||||
raise EasyXFCallerError("no value supplied for %s.%s" % (section, k))
|
||||
for counter in xrange(2):
|
||||
result = section_dict.get(k)
|
||||
if result is None:
|
||||
raise EasyXFCallerError('%s.%s is not a known attribute' % (section, k))
|
||||
if not isinstance(result, basestring):
|
||||
break
|
||||
# synonym
|
||||
old_k = k
|
||||
k = result
|
||||
else:
|
||||
raise EasyXFAuthorError('Attempt to define synonym of synonym (%r: %r)' % (old_k, result))
|
||||
value_info = result
|
||||
if not isinstance(value_info, list):
|
||||
value_info = [value_info]
|
||||
for value_rule in value_info:
|
||||
if isinstance(value_rule, dict):
|
||||
# dict maps strings to integer field values
|
||||
vl = v.lower().replace('-', '_')
|
||||
if vl in value_rule:
|
||||
value = value_rule[vl]
|
||||
break
|
||||
elif callable(value_rule):
|
||||
value = value_rule(v)
|
||||
if value is not None:
|
||||
break
|
||||
else:
|
||||
raise EasyXFAuthorError("unknown value rule for attribute %r: %r" % (k, value_rule))
|
||||
else:
|
||||
raise EasyXFCallerError("unexpected value %r for %s.%s" % (v, section, k))
|
||||
try:
|
||||
orig = getattr(section_obj, k)
|
||||
except AttributeError:
|
||||
raise EasyXFAuthorError('%s.%s in dictionary but not in supplied object' % (section, k))
|
||||
if debug: print "+++ %s.%s = %r # %s; was %r" % (section, k, value, v, orig)
|
||||
setattr(section_obj, k, value)
|
||||
|
||||
def easyxf(strg_to_parse="", num_format_str=None,
|
||||
field_sep=",", line_sep=";", intro_sep=":", esc_char="\\", debug=False):
|
||||
xfobj = XFStyle()
|
||||
if num_format_str is not None:
|
||||
xfobj.num_format_str = num_format_str
|
||||
if strg_to_parse:
|
||||
_parse_strg_to_obj(strg_to_parse, xfobj, xf_dict,
|
||||
field_sep=field_sep, line_sep=line_sep, intro_sep=intro_sep, esc_char=esc_char, debug=debug)
|
||||
return xfobj
|
||||
@@ -1,81 +0,0 @@
|
||||
# -*- coding: windows-1252 -*-
|
||||
|
||||
'''
|
||||
From BIFF8 on, strings are always stored using UTF-16LE text encoding. The
|
||||
character array is a sequence of 16-bit values4. Additionally it is
|
||||
possible to use a compressed format, which omits the high bytes of all
|
||||
characters, if they are all zero.
|
||||
|
||||
The following tables describe the standard format of the entire string, but
|
||||
in many records the strings differ from this format. This will be mentioned
|
||||
separately. It is possible (but not required) to store Rich-Text formatting
|
||||
information and Asian phonetic information inside a Unicode string. This
|
||||
results in four different ways to store a string. The character array
|
||||
is not zero-terminated.
|
||||
|
||||
The string consists of the character count (as usual an 8-bit value or
|
||||
a 16-bit value), option flags, the character array and optional formatting
|
||||
information. If the string is empty, sometimes the option flags field will
|
||||
not occur. This is mentioned at the respective place.
|
||||
|
||||
Offset Size Contents
|
||||
0 1 or 2 Length of the string (character count, ln)
|
||||
1 or 2 1 Option flags:
|
||||
Bit Mask Contents
|
||||
0 01H Character compression (ccompr):
|
||||
0 = Compressed (8-bit characters)
|
||||
1 = Uncompressed (16-bit characters)
|
||||
2 04H Asian phonetic settings (phonetic):
|
||||
0 = Does not contain Asian phonetic settings
|
||||
1 = Contains Asian phonetic settings
|
||||
3 08H Rich-Text settings (richtext):
|
||||
0 = Does not contain Rich-Text settings
|
||||
1 = Contains Rich-Text settings
|
||||
[2 or 3] 2 (optional, only if richtext=1) Number of Rich-Text formatting runs (rt)
|
||||
[var.] 4 (optional, only if phonetic=1) Size of Asian phonetic settings block (in bytes, sz)
|
||||
var. ln or
|
||||
2·ln Character array (8-bit characters or 16-bit characters, dependent on ccompr)
|
||||
[var.] 4·rt (optional, only if richtext=1) List of rt formatting runs
|
||||
[var.] sz (optional, only if phonetic=1) Asian Phonetic Settings Block
|
||||
'''
|
||||
|
||||
|
||||
from struct import pack
|
||||
|
||||
def upack2(s, encoding='ascii'):
|
||||
# If not unicode, make it so.
|
||||
if isinstance(s, unicode):
|
||||
us = s
|
||||
else:
|
||||
us = unicode(s, encoding)
|
||||
# Limit is based on number of content characters
|
||||
# (not on number of bytes in packed result)
|
||||
len_us = len(us)
|
||||
if len_us > 65535:
|
||||
raise Exception('String longer than 65535 characters')
|
||||
try:
|
||||
encs = us.encode('latin1')
|
||||
# Success here means all chars are in U+0000 to U+00FF
|
||||
# inclusive, meaning that we can use "compressed format".
|
||||
flag = 0
|
||||
except UnicodeEncodeError:
|
||||
encs = us.encode('utf_16_le')
|
||||
flag = 1
|
||||
return pack('<HB', len_us, flag) + encs
|
||||
|
||||
def upack1(s, encoding='ascii'):
|
||||
# Same as upack2(), but with a one-byte length field.
|
||||
if isinstance(s, unicode):
|
||||
us = s
|
||||
else:
|
||||
us = unicode(s, encoding)
|
||||
len_us = len(us)
|
||||
if len_us > 255:
|
||||
raise Exception('String longer than 255 characters')
|
||||
try:
|
||||
encs = us.encode('latin1')
|
||||
flag = 0
|
||||
except UnicodeEncodeError:
|
||||
encs = us.encode('utf_16_le')
|
||||
flag = 1
|
||||
return pack('<BB', len_us, flag) + encs
|
||||
@@ -1,196 +0,0 @@
|
||||
# pyXLWriter: A library for generating Excel Spreadsheets
|
||||
# Copyright (c) 2004 Evgeny Filatov <fufff@users.sourceforge.net>
|
||||
# Copyright (c) 2002-2004 John McNamara (Perl Spreadsheet::WriteExcel)
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this library; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#----------------------------------------------------------------------------
|
||||
# This module was written/ported from PERL Spreadsheet::WriteExcel module
|
||||
# The author of the PERL Spreadsheet::WriteExcel module is John McNamara
|
||||
# <jmcnamara@cpan.org>
|
||||
#----------------------------------------------------------------------------
|
||||
# See the README.txt distributed with pyXLWriter for more details.
|
||||
|
||||
# Portions are (C) Roman V. Kiseliov, 2005
|
||||
|
||||
|
||||
# Utilities for work with reference to cells and with sheetnames
|
||||
|
||||
|
||||
__rev_id__ = """$Id: Utils.py 3844 2009-05-20 01:02:54Z sjmachin $"""
|
||||
|
||||
import re
|
||||
from struct import pack
|
||||
from ExcelMagic import MAX_ROW, MAX_COL
|
||||
|
||||
|
||||
_re_cell_ex = re.compile(r"(\$?)([A-I]?[A-Z])(\$?)(\d+)", re.IGNORECASE)
|
||||
_re_row_range = re.compile(r"\$?(\d+):\$?(\d+)")
|
||||
_re_col_range = re.compile(r"\$?([A-I]?[A-Z]):\$?([A-I]?[A-Z])", re.IGNORECASE)
|
||||
_re_cell_range = re.compile(r"\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)", re.IGNORECASE)
|
||||
_re_cell_ref = re.compile(r"\$?([A-I]?[A-Z]\$?\d+)", re.IGNORECASE)
|
||||
|
||||
|
||||
def col_by_name(colname):
|
||||
"""
|
||||
"""
|
||||
col = 0
|
||||
pow = 1
|
||||
for i in xrange(len(colname)-1, -1, -1):
|
||||
ch = colname[i]
|
||||
col += (ord(ch) - ord('A') + 1) * pow
|
||||
pow *= 26
|
||||
return col - 1
|
||||
|
||||
|
||||
def cell_to_rowcol(cell):
|
||||
"""Convert an Excel cell reference string in A1 notation
|
||||
to numeric row/col notation.
|
||||
|
||||
Returns: row, col, row_abs, col_abs
|
||||
|
||||
"""
|
||||
m = _re_cell_ex.match(cell)
|
||||
if not m:
|
||||
raise Exception("Ill-formed single_cell reference: %s" % cell)
|
||||
col_abs, col, row_abs, row = m.groups()
|
||||
row_abs = bool(row_abs)
|
||||
col_abs = bool(col_abs)
|
||||
row = int(row) - 1
|
||||
col = col_by_name(col.upper())
|
||||
return row, col, row_abs, col_abs
|
||||
|
||||
|
||||
def cell_to_rowcol2(cell):
|
||||
"""Convert an Excel cell reference string in A1 notation
|
||||
to numeric row/col notation.
|
||||
|
||||
Returns: row, col
|
||||
|
||||
"""
|
||||
m = _re_cell_ex.match(cell)
|
||||
if not m:
|
||||
raise Exception("Error in cell format")
|
||||
col_abs, col, row_abs, row = m.groups()
|
||||
# Convert base26 column string to number
|
||||
# All your Base are belong to us.
|
||||
row = int(row) - 1
|
||||
col = col_by_name(col.upper())
|
||||
return row, col
|
||||
|
||||
|
||||
def rowcol_to_cell(row, col, row_abs=False, col_abs=False):
|
||||
"""Convert numeric row/col notation to an Excel cell reference string in
|
||||
A1 notation.
|
||||
|
||||
"""
|
||||
assert 0 <= row < MAX_ROW # MAX_ROW counts from 1
|
||||
assert 0 <= col < MAX_COL # MAX_COL counts from 1
|
||||
d = col // 26
|
||||
m = col % 26
|
||||
chr1 = "" # Most significant character in AA1
|
||||
if row_abs:
|
||||
row_abs = '$'
|
||||
else:
|
||||
row_abs = ''
|
||||
if col_abs:
|
||||
col_abs = '$'
|
||||
else:
|
||||
col_abs = ''
|
||||
if d > 0:
|
||||
chr1 = chr(ord('A') + d - 1)
|
||||
chr2 = chr(ord('A') + m)
|
||||
# Zero index to 1-index
|
||||
return col_abs + chr1 + chr2 + row_abs + str(row + 1)
|
||||
|
||||
def rowcol_pair_to_cellrange(row1, col1, row2, col2,
|
||||
row1_abs=False, col1_abs=False, row2_abs=False, col2_abs=False):
|
||||
"""Convert two (row,column) pairs
|
||||
into a cell range string in A1:B2 notation.
|
||||
|
||||
Returns: cell range string
|
||||
"""
|
||||
assert row1 <= row2
|
||||
assert col1 <= col2
|
||||
return (
|
||||
rowcol_to_cell(row1, col1, row1_abs, col1_abs)
|
||||
+ ":"
|
||||
+ rowcol_to_cell(row2, col2, row2_abs, col2_abs)
|
||||
)
|
||||
|
||||
def cellrange_to_rowcol_pair(cellrange):
|
||||
"""Convert cell range string in A1 notation to numeric row/col
|
||||
pair.
|
||||
|
||||
Returns: row1, col1, row2, col2
|
||||
|
||||
"""
|
||||
cellrange = cellrange.upper()
|
||||
# Convert a row range: '1:3'
|
||||
res = _re_row_range.match(cellrange)
|
||||
if res:
|
||||
row1 = int(res.group(1)) - 1
|
||||
col1 = 0
|
||||
row2 = int(res.group(2)) - 1
|
||||
col2 = -1
|
||||
return row1, col1, row2, col2
|
||||
# Convert a column range: 'A:A' or 'B:G'.
|
||||
# A range such as A:A is equivalent to A1:A16384, so add rows as required
|
||||
res = _re_col_range.match(cellrange)
|
||||
if res:
|
||||
col1 = col_by_name(res.group(1).upper())
|
||||
row1 = 0
|
||||
col2 = col_by_name(res.group(2).upper())
|
||||
row2 = -1
|
||||
return row1, col1, row2, col2
|
||||
# Convert a cell range: 'A1:B7'
|
||||
res = _re_cell_range.match(cellrange)
|
||||
if res:
|
||||
row1, col1 = cell_to_rowcol2(res.group(1))
|
||||
row2, col2 = cell_to_rowcol2(res.group(2))
|
||||
return row1, col1, row2, col2
|
||||
# Convert a cell reference: 'A1' or 'AD2000'
|
||||
res = _re_cell_ref.match(cellrange)
|
||||
if res:
|
||||
row1, col1 = cell_to_rowcol2(res.group(1))
|
||||
return row1, col1, row1, col1
|
||||
raise Exception("Unknown cell reference %s" % (cell))
|
||||
|
||||
|
||||
def cell_to_packed_rowcol(cell):
|
||||
""" pack row and column into the required 4 byte format """
|
||||
row, col, row_abs, col_abs = cell_to_rowcol(cell)
|
||||
if col >= MAX_COL:
|
||||
raise Exception("Column %s greater than IV in formula" % cell)
|
||||
if row >= MAX_ROW: # this for BIFF8. for BIFF7 available 2^14
|
||||
raise Exception("Row %s greater than %d in formula" % (cell, MAX_ROW))
|
||||
col |= int(not row_abs) << 15
|
||||
col |= int(not col_abs) << 14
|
||||
return row, col
|
||||
|
||||
# === sheetname functions ===
|
||||
|
||||
def valid_sheet_name(sheet_name):
|
||||
if sheet_name == u"" or sheet_name[0] == u"'" or len(sheet_name) > 31:
|
||||
return False
|
||||
for c in sheet_name:
|
||||
if c in u"[]:\\?/*\x00":
|
||||
return False
|
||||
return True
|
||||
|
||||
def quote_sheet_name(unquoted_sheet_name):
|
||||
if not valid_sheet_name(unquoted_sheet_name):
|
||||
raise Exception(
|
||||
'attempt to quote an invalid worksheet name %r' % unquoted_sheet_name)
|
||||
return u"'" + unquoted_sheet_name.replace(u"'", u"''") + u"'"
|
||||
@@ -1,636 +0,0 @@
|
||||
# -*- coding: windows-1252 -*-
|
||||
'''
|
||||
Record Order in BIFF8
|
||||
Workbook Globals Substream
|
||||
BOF Type = workbook globals
|
||||
Interface Header
|
||||
MMS
|
||||
Interface End
|
||||
WRITEACCESS
|
||||
CODEPAGE
|
||||
DSF
|
||||
TABID
|
||||
FNGROUPCOUNT
|
||||
Workbook Protection Block
|
||||
WINDOWPROTECT
|
||||
PROTECT
|
||||
PASSWORD
|
||||
PROT4REV
|
||||
PROT4REVPASS
|
||||
BACKUP
|
||||
HIDEOBJ
|
||||
WINDOW1
|
||||
DATEMODE
|
||||
PRECISION
|
||||
REFRESHALL
|
||||
BOOKBOOL
|
||||
FONT +
|
||||
FORMAT *
|
||||
XF +
|
||||
STYLE +
|
||||
? PALETTE
|
||||
USESELFS
|
||||
|
||||
BOUNDSHEET +
|
||||
|
||||
COUNTRY
|
||||
? Link Table
|
||||
SST
|
||||
ExtSST
|
||||
EOF
|
||||
'''
|
||||
|
||||
import BIFFRecords
|
||||
import Style
|
||||
|
||||
class Workbook(object):
|
||||
|
||||
#################################################################
|
||||
## Constructor
|
||||
#################################################################
|
||||
def __init__(self, encoding='ascii', style_compression=0):
|
||||
self.encoding = encoding
|
||||
self.__owner = 'None'
|
||||
self.__country_code = None # 0x07 is Russia :-)
|
||||
self.__wnd_protect = 0
|
||||
self.__obj_protect = 0
|
||||
self.__protect = 0
|
||||
self.__backup_on_save = 0
|
||||
# for WINDOW1 record
|
||||
self.__hpos_twips = 0x01E0
|
||||
self.__vpos_twips = 0x005A
|
||||
self.__width_twips = 0x3FCF
|
||||
self.__height_twips = 0x2A4E
|
||||
|
||||
self.__active_sheet = 0
|
||||
self.__first_tab_index = 0
|
||||
self.__selected_tabs = 0x01
|
||||
self.__tab_width_twips = 0x0258
|
||||
|
||||
self.__wnd_hidden = 0
|
||||
self.__wnd_mini = 0
|
||||
self.__hscroll_visible = 1
|
||||
self.__vscroll_visible = 1
|
||||
self.__tabs_visible = 1
|
||||
|
||||
self.__styles = Style.StyleCollection(style_compression)
|
||||
|
||||
self.__dates_1904 = 0
|
||||
self.__use_cell_values = 1
|
||||
|
||||
self.__sst = BIFFRecords.SharedStringTable(self.encoding)
|
||||
|
||||
self.__worksheets = []
|
||||
self.__worksheet_idx_from_name = {}
|
||||
self.__sheet_refs = {}
|
||||
self._supbook_xref = {}
|
||||
self._xcall_xref = {}
|
||||
self._ownbook_supbookx = None
|
||||
self._ownbook_supbook_ref = None
|
||||
self._xcall_supbookx = None
|
||||
self._xcall_supbook_ref = None
|
||||
|
||||
|
||||
|
||||
#################################################################
|
||||
## Properties, "getters", "setters"
|
||||
#################################################################
|
||||
|
||||
def get_style_stats(self):
|
||||
return self.__styles.stats[:]
|
||||
|
||||
def set_owner(self, value):
|
||||
self.__owner = value
|
||||
|
||||
def get_owner(self):
|
||||
return self.__owner
|
||||
|
||||
owner = property(get_owner, set_owner)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_country_code(self, value):
|
||||
self.__country_code = value
|
||||
|
||||
def get_country_code(self):
|
||||
return self.__country_code
|
||||
|
||||
country_code = property(get_country_code, set_country_code)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_wnd_protect(self, value):
|
||||
self.__wnd_protect = int(value)
|
||||
|
||||
def get_wnd_protect(self):
|
||||
return bool(self.__wnd_protect)
|
||||
|
||||
wnd_protect = property(get_wnd_protect, set_wnd_protect)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_obj_protect(self, value):
|
||||
self.__obj_protect = int(value)
|
||||
|
||||
def get_obj_protect(self):
|
||||
return bool(self.__obj_protect)
|
||||
|
||||
obj_protect = property(get_obj_protect, set_obj_protect)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_protect(self, value):
|
||||
self.__protect = int(value)
|
||||
|
||||
def get_protect(self):
|
||||
return bool(self.__protect)
|
||||
|
||||
protect = property(get_protect, set_protect)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_backup_on_save(self, value):
|
||||
self.__backup_on_save = int(value)
|
||||
|
||||
def get_backup_on_save(self):
|
||||
return bool(self.__backup_on_save)
|
||||
|
||||
backup_on_save = property(get_backup_on_save, set_backup_on_save)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_hpos(self, value):
|
||||
self.__hpos_twips = value & 0xFFFF
|
||||
|
||||
def get_hpos(self):
|
||||
return self.__hpos_twips
|
||||
|
||||
hpos = property(get_hpos, set_hpos)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_vpos(self, value):
|
||||
self.__vpos_twips = value & 0xFFFF
|
||||
|
||||
def get_vpos(self):
|
||||
return self.__vpos_twips
|
||||
|
||||
vpos = property(get_vpos, set_vpos)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_width(self, value):
|
||||
self.__width_twips = value & 0xFFFF
|
||||
|
||||
def get_width(self):
|
||||
return self.__width_twips
|
||||
|
||||
width = property(get_width, set_width)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_height(self, value):
|
||||
self.__height_twips = value & 0xFFFF
|
||||
|
||||
def get_height(self):
|
||||
return self.__height_twips
|
||||
|
||||
height = property(get_height, set_height)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_active_sheet(self, value):
|
||||
self.__active_sheet = value & 0xFFFF
|
||||
self.__first_tab_index = self.__active_sheet
|
||||
|
||||
def get_active_sheet(self):
|
||||
return self.__active_sheet
|
||||
|
||||
active_sheet = property(get_active_sheet, set_active_sheet)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_tab_width(self, value):
|
||||
self.__tab_width_twips = value & 0xFFFF
|
||||
|
||||
def get_tab_width(self):
|
||||
return self.__tab_width_twips
|
||||
|
||||
tab_width = property(get_tab_width, set_tab_width)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_wnd_visible(self, value):
|
||||
self.__wnd_hidden = int(not value)
|
||||
|
||||
def get_wnd_visible(self):
|
||||
return not bool(self.__wnd_hidden)
|
||||
|
||||
wnd_visible = property(get_wnd_visible, set_wnd_visible)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_wnd_mini(self, value):
|
||||
self.__wnd_mini = int(value)
|
||||
|
||||
def get_wnd_mini(self):
|
||||
return bool(self.__wnd_mini)
|
||||
|
||||
wnd_mini = property(get_wnd_mini, set_wnd_mini)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_hscroll_visible(self, value):
|
||||
self.__hscroll_visible = int(value)
|
||||
|
||||
def get_hscroll_visible(self):
|
||||
return bool(self.__hscroll_visible)
|
||||
|
||||
hscroll_visible = property(get_hscroll_visible, set_hscroll_visible)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_vscroll_visible(self, value):
|
||||
self.__vscroll_visible = int(value)
|
||||
|
||||
def get_vscroll_visible(self):
|
||||
return bool(self.__vscroll_visible)
|
||||
|
||||
vscroll_visible = property(get_vscroll_visible, set_vscroll_visible)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_tabs_visible(self, value):
|
||||
self.__tabs_visible = int(value)
|
||||
|
||||
def get_tabs_visible(self):
|
||||
return bool(self.__tabs_visible)
|
||||
|
||||
tabs_visible = property(get_tabs_visible, set_tabs_visible)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_dates_1904(self, value):
|
||||
self.__dates_1904 = int(value)
|
||||
|
||||
def get_dates_1904(self):
|
||||
return bool(self.__dates_1904)
|
||||
|
||||
dates_1904 = property(get_dates_1904, set_dates_1904)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_use_cell_values(self, value):
|
||||
self.__use_cell_values = int(value)
|
||||
|
||||
def get_use_cell_values(self):
|
||||
return bool(self.__use_cell_values)
|
||||
|
||||
use_cell_values = property(get_use_cell_values, set_use_cell_values)
|
||||
|
||||
#################################################################
|
||||
|
||||
def get_default_style(self):
|
||||
return self.__styles.default_style
|
||||
|
||||
default_style = property(get_default_style)
|
||||
|
||||
##################################################################
|
||||
## Methods
|
||||
##################################################################
|
||||
|
||||
def add_style(self, style):
|
||||
return self.__styles.add(style)
|
||||
|
||||
def add_str(self, s):
|
||||
return self.__sst.add_str(s)
|
||||
|
||||
def del_str(self, sst_idx):
|
||||
self.__sst.del_str(sst_idx)
|
||||
|
||||
def str_index(self, s):
|
||||
return self.__sst.str_index(s)
|
||||
|
||||
def add_sheet(self, sheetname, cell_overwrite_ok=False):
|
||||
import Worksheet, Utils
|
||||
if not isinstance(sheetname, unicode):
|
||||
sheetname = sheetname.decode(self.encoding)
|
||||
if not Utils.valid_sheet_name(sheetname):
|
||||
raise Exception("invalid worksheet name %r" % sheetname)
|
||||
lower_name = sheetname.lower()
|
||||
if lower_name in self.__worksheet_idx_from_name:
|
||||
raise Exception("duplicate worksheet name %r" % sheetname)
|
||||
self.__worksheet_idx_from_name[lower_name] = len(self.__worksheets)
|
||||
self.__worksheets.append(Worksheet.Worksheet(sheetname, self, cell_overwrite_ok))
|
||||
return self.__worksheets[-1]
|
||||
|
||||
def get_sheet(self, sheetnum):
|
||||
return self.__worksheets[sheetnum]
|
||||
|
||||
def raise_bad_sheetname(self, sheetname):
|
||||
raise Exception("Formula: unknown sheet name %s" % sheetname)
|
||||
|
||||
def convert_sheetindex(self, strg_ref, n_sheets):
|
||||
idx = int(strg_ref)
|
||||
if 0 <= idx < n_sheets:
|
||||
return idx
|
||||
msg = "Formula: sheet index (%s) >= number of sheets (%d)" % (strg_ref, n_sheets)
|
||||
raise Exception(msg)
|
||||
|
||||
def _get_supbook_index(self, tag):
|
||||
if tag in self._supbook_xref:
|
||||
return self._supbook_xref[tag]
|
||||
self._supbook_xref[tag] = idx = len(self._supbook_xref)
|
||||
return idx
|
||||
|
||||
def setup_ownbook(self):
|
||||
self._ownbook_supbookx = self._get_supbook_index(('ownbook', 0))
|
||||
self._ownbook_supbook_ref = None
|
||||
reference = (self._ownbook_supbookx, 0xFFFE, 0xFFFE)
|
||||
if reference in self.__sheet_refs:
|
||||
raise Exception("can't happen")
|
||||
self.__sheet_refs[reference] = self._ownbook_supbook_ref = len(self.__sheet_refs)
|
||||
|
||||
def setup_xcall(self):
|
||||
self._xcall_supbookx = self._get_supbook_index(('xcall', 0))
|
||||
self._xcall_supbook_ref = None
|
||||
reference = (self._xcall_supbookx, 0xFFFE, 0xFFFE)
|
||||
if reference in self.__sheet_refs:
|
||||
raise Exception("can't happen")
|
||||
self.__sheet_refs[reference] = self._xcall_supbook_ref = len(self.__sheet_refs)
|
||||
|
||||
def add_sheet_reference(self, formula):
|
||||
patches = []
|
||||
n_sheets = len(self.__worksheets)
|
||||
sheet_refs, xcall_refs = formula.get_references()
|
||||
|
||||
for ref0, ref1, offset in sheet_refs:
|
||||
if not ref0.isdigit():
|
||||
try:
|
||||
ref0n = self.__worksheet_idx_from_name[ref0.lower()]
|
||||
except KeyError:
|
||||
self.raise_bad_sheetname(ref0)
|
||||
else:
|
||||
ref0n = self.convert_sheetindex(ref0, n_sheets)
|
||||
if ref1 == ref0:
|
||||
ref1n = ref0n
|
||||
elif not ref1.isdigit():
|
||||
try:
|
||||
ref1n = self.__worksheet_idx_from_name[ref1.lower()]
|
||||
except KeyError:
|
||||
self.raise_bad_sheetname(ref1)
|
||||
else:
|
||||
ref1n = self.convert_sheetindex(ref1, n_sheets)
|
||||
if ref1n < ref0n:
|
||||
msg = "Formula: sheets out of order; %r:%r -> (%d, %d)" \
|
||||
% (ref0, ref1, ref0n, ref1n)
|
||||
raise Exception(msg)
|
||||
if self._ownbook_supbookx is None:
|
||||
self.setup_ownbook()
|
||||
reference = (self._ownbook_supbookx, ref0n, ref1n)
|
||||
if reference in self.__sheet_refs:
|
||||
patches.append((offset, self.__sheet_refs[reference]))
|
||||
else:
|
||||
nrefs = len(self.__sheet_refs)
|
||||
if nrefs > 65535:
|
||||
raise Exception('More than 65536 inter-sheet references')
|
||||
self.__sheet_refs[reference] = nrefs
|
||||
patches.append((offset, nrefs))
|
||||
|
||||
for funcname, offset in xcall_refs:
|
||||
if self._ownbook_supbookx is None:
|
||||
self.setup_ownbook()
|
||||
if self._xcall_supbookx is None:
|
||||
self.setup_xcall()
|
||||
# print funcname, self._supbook_xref
|
||||
patches.append((offset, self._xcall_supbook_ref))
|
||||
if not isinstance(funcname, unicode):
|
||||
funcname = funcname.decode(self.encoding)
|
||||
if funcname in self._xcall_xref:
|
||||
idx = self._xcall_xref[funcname]
|
||||
else:
|
||||
self._xcall_xref[funcname] = idx = len(self._xcall_xref)
|
||||
patches.append((offset + 2, idx + 1))
|
||||
|
||||
formula.patch_references(patches)
|
||||
|
||||
##################################################################
|
||||
## BIFF records generation
|
||||
##################################################################
|
||||
|
||||
def __bof_rec(self):
|
||||
return BIFFRecords.Biff8BOFRecord(BIFFRecords.Biff8BOFRecord.BOOK_GLOBAL).get()
|
||||
|
||||
def __eof_rec(self):
|
||||
return BIFFRecords.EOFRecord().get()
|
||||
|
||||
def __intf_hdr_rec(self):
|
||||
return BIFFRecords.InteraceHdrRecord().get()
|
||||
|
||||
def __intf_end_rec(self):
|
||||
return BIFFRecords.InteraceEndRecord().get()
|
||||
|
||||
def __intf_mms_rec(self):
|
||||
return BIFFRecords.MMSRecord().get()
|
||||
|
||||
def __write_access_rec(self):
|
||||
return BIFFRecords.WriteAccessRecord(self.__owner).get()
|
||||
|
||||
def __wnd_protect_rec(self):
|
||||
return BIFFRecords.WindowProtectRecord(self.__wnd_protect).get()
|
||||
|
||||
def __obj_protect_rec(self):
|
||||
return BIFFRecords.ObjectProtectRecord(self.__obj_protect).get()
|
||||
|
||||
def __protect_rec(self):
|
||||
return BIFFRecords.ProtectRecord(self.__protect).get()
|
||||
|
||||
def __password_rec(self):
|
||||
return BIFFRecords.PasswordRecord().get()
|
||||
|
||||
def __prot4rev_rec(self):
|
||||
return BIFFRecords.Prot4RevRecord().get()
|
||||
|
||||
def __prot4rev_pass_rec(self):
|
||||
return BIFFRecords.Prot4RevPassRecord().get()
|
||||
|
||||
def __backup_rec(self):
|
||||
return BIFFRecords.BackupRecord(self.__backup_on_save).get()
|
||||
|
||||
def __hide_obj_rec(self):
|
||||
return BIFFRecords.HideObjRecord().get()
|
||||
|
||||
def __window1_rec(self):
|
||||
flags = 0
|
||||
flags |= (self.__wnd_hidden) << 0
|
||||
flags |= (self.__wnd_mini) << 1
|
||||
flags |= (self.__hscroll_visible) << 3
|
||||
flags |= (self.__vscroll_visible) << 4
|
||||
flags |= (self.__tabs_visible) << 5
|
||||
|
||||
return BIFFRecords.Window1Record(self.__hpos_twips, self.__vpos_twips,
|
||||
self.__width_twips, self.__height_twips,
|
||||
flags,
|
||||
self.__active_sheet, self.__first_tab_index,
|
||||
self.__selected_tabs, self.__tab_width_twips).get()
|
||||
|
||||
def __codepage_rec(self):
|
||||
return BIFFRecords.CodepageBiff8Record().get()
|
||||
|
||||
def __country_rec(self):
|
||||
if not self.__country_code:
|
||||
return ''
|
||||
return BIFFRecords.CountryRecord(self.__country_code, self.__country_code).get()
|
||||
|
||||
def __dsf_rec(self):
|
||||
return BIFFRecords.DSFRecord().get()
|
||||
|
||||
def __tabid_rec(self):
|
||||
return BIFFRecords.TabIDRecord(len(self.__worksheets)).get()
|
||||
|
||||
def __fngroupcount_rec(self):
|
||||
return BIFFRecords.FnGroupCountRecord().get()
|
||||
|
||||
def __datemode_rec(self):
|
||||
return BIFFRecords.DateModeRecord(self.__dates_1904).get()
|
||||
|
||||
def __precision_rec(self):
|
||||
return BIFFRecords.PrecisionRecord(self.__use_cell_values).get()
|
||||
|
||||
def __refresh_all_rec(self):
|
||||
return BIFFRecords.RefreshAllRecord().get()
|
||||
|
||||
def __bookbool_rec(self):
|
||||
return BIFFRecords.BookBoolRecord().get()
|
||||
|
||||
def __all_fonts_num_formats_xf_styles_rec(self):
|
||||
return self.__styles.get_biff_data()
|
||||
|
||||
def __palette_rec(self):
|
||||
result = ''
|
||||
return result
|
||||
|
||||
def __useselfs_rec(self):
|
||||
return BIFFRecords.UseSelfsRecord().get()
|
||||
|
||||
def __boundsheets_rec(self, data_len_before, data_len_after, sheet_biff_lens):
|
||||
# .................................
|
||||
# BOUNDSEHEET0
|
||||
# BOUNDSEHEET1
|
||||
# BOUNDSEHEET2
|
||||
# ..................................
|
||||
# WORKSHEET0
|
||||
# WORKSHEET1
|
||||
# WORKSHEET2
|
||||
boundsheets_len = 0
|
||||
for sheet in self.__worksheets:
|
||||
boundsheets_len += len(BIFFRecords.BoundSheetRecord(
|
||||
0x00L, sheet.visibility, sheet.name, self.encoding
|
||||
).get())
|
||||
|
||||
start = data_len_before + boundsheets_len + data_len_after
|
||||
|
||||
result = ''
|
||||
for sheet_biff_len, sheet in zip(sheet_biff_lens, self.__worksheets):
|
||||
result += BIFFRecords.BoundSheetRecord(
|
||||
start, sheet.visibility, sheet.name, self.encoding
|
||||
).get()
|
||||
start += sheet_biff_len
|
||||
return result
|
||||
|
||||
def __all_links_rec(self):
|
||||
pieces = []
|
||||
temp = [(idx, tag) for tag, idx in self._supbook_xref.items()]
|
||||
temp.sort()
|
||||
for idx, tag in temp:
|
||||
stype, snum = tag
|
||||
if stype == 'ownbook':
|
||||
rec = BIFFRecords.InternalReferenceSupBookRecord(len(self.__worksheets)).get()
|
||||
pieces.append(rec)
|
||||
elif stype == 'xcall':
|
||||
rec = BIFFRecords.XcallSupBookRecord().get()
|
||||
pieces.append(rec)
|
||||
temp = [(idx, name) for name, idx in self._xcall_xref.items()]
|
||||
temp.sort()
|
||||
for idx, name in temp:
|
||||
rec = BIFFRecords.ExternnameRecord(
|
||||
options=0, index=0, name=name, fmla='\x02\x00\x1c\x17').get()
|
||||
pieces.append(rec)
|
||||
else:
|
||||
raise Exception('unknown supbook stype %r' % stype)
|
||||
if len(self.__sheet_refs) > 0:
|
||||
# get references in index order
|
||||
temp = [(idx, ref) for ref, idx in self.__sheet_refs.items()]
|
||||
temp.sort()
|
||||
temp = [ref for idx, ref in temp]
|
||||
externsheet_record = BIFFRecords.ExternSheetRecord(temp).get()
|
||||
pieces.append(externsheet_record)
|
||||
return ''.join(pieces)
|
||||
|
||||
def __sst_rec(self):
|
||||
return self.__sst.get_biff_record()
|
||||
|
||||
def __ext_sst_rec(self, abs_stream_pos):
|
||||
return ''
|
||||
#return BIFFRecords.ExtSSTRecord(abs_stream_pos, self.sst_record.str_placement,
|
||||
#self.sst_record.portions_len).get()
|
||||
|
||||
def get_biff_data(self):
|
||||
before = ''
|
||||
before += self.__bof_rec()
|
||||
before += self.__intf_hdr_rec()
|
||||
before += self.__intf_mms_rec()
|
||||
before += self.__intf_end_rec()
|
||||
before += self.__write_access_rec()
|
||||
before += self.__codepage_rec()
|
||||
before += self.__dsf_rec()
|
||||
before += self.__tabid_rec()
|
||||
before += self.__fngroupcount_rec()
|
||||
before += self.__wnd_protect_rec()
|
||||
before += self.__protect_rec()
|
||||
before += self.__obj_protect_rec()
|
||||
before += self.__password_rec()
|
||||
before += self.__prot4rev_rec()
|
||||
before += self.__prot4rev_pass_rec()
|
||||
before += self.__backup_rec()
|
||||
before += self.__hide_obj_rec()
|
||||
before += self.__window1_rec()
|
||||
before += self.__datemode_rec()
|
||||
before += self.__precision_rec()
|
||||
before += self.__refresh_all_rec()
|
||||
before += self.__bookbool_rec()
|
||||
before += self.__all_fonts_num_formats_xf_styles_rec()
|
||||
before += self.__palette_rec()
|
||||
before += self.__useselfs_rec()
|
||||
|
||||
country = self.__country_rec()
|
||||
all_links = self.__all_links_rec()
|
||||
|
||||
shared_str_table = self.__sst_rec()
|
||||
after = country + all_links + shared_str_table
|
||||
|
||||
ext_sst = self.__ext_sst_rec(0) # need fake cause we need calc stream pos
|
||||
eof = self.__eof_rec()
|
||||
|
||||
self.__worksheets[self.__active_sheet].selected = True
|
||||
sheets = ''
|
||||
sheet_biff_lens = []
|
||||
for sheet in self.__worksheets:
|
||||
data = sheet.get_biff_data()
|
||||
sheets += data
|
||||
sheet_biff_lens.append(len(data))
|
||||
|
||||
bundlesheets = self.__boundsheets_rec(len(before), len(after)+len(ext_sst)+len(eof), sheet_biff_lens)
|
||||
|
||||
sst_stream_pos = len(before) + len(bundlesheets) + len(country) + len(all_links)
|
||||
ext_sst = self.__ext_sst_rec(sst_stream_pos)
|
||||
|
||||
return before + bundlesheets + after + ext_sst + eof + sheets
|
||||
|
||||
def save(self, filename):
|
||||
import CompoundDoc
|
||||
|
||||
doc = CompoundDoc.XlsDoc()
|
||||
doc.save(filename, self.get_biff_data())
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
# -*- coding: windows-1252 -*-
|
||||
|
||||
__VERSION__ = '0.7.2'
|
||||
|
||||
import sys
|
||||
|
||||
from Workbook import Workbook
|
||||
from Worksheet import Worksheet
|
||||
from Row import Row
|
||||
from Column import Column
|
||||
from Formatting import Font, Alignment, Borders, Pattern, Protection
|
||||
from Style import XFStyle, easyxf
|
||||
from ExcelFormula import *
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,199 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=us-ascii' />
|
||||
<title>The xlwt Module</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>The xlwt Module</h1>
|
||||
<p /><p><b>A Python package for generating Microsoft Excel ™ spreadsheet files.
|
||||
</b></p>
|
||||
|
||||
<h2>General information</h2>
|
||||
|
||||
<h3>State of Documentation</h3>
|
||||
|
||||
<p>
|
||||
This documentation is currently incomplete. There may be methods and
|
||||
classes not included and any item marked with a <em
|
||||
style="color:red;">[NC]</em> is not complete and may have further
|
||||
parameters, methods, attributes and functionality that are not
|
||||
documented. In these cases, you'll have to refer to the source if the
|
||||
documentation provided is insufficient.
|
||||
</p>
|
||||
|
||||
</p>
|
||||
|
||||
<h2>Module Contents <em style="color:red;">[NC]</em></h2>
|
||||
<dl>
|
||||
<dt><b>easyxf</b> (function)</dt>
|
||||
<dd>
|
||||
<p>
|
||||
This function is used to create and configure XFStyle objects
|
||||
for use with (for example) the Worksheet.write method.
|
||||
</p>
|
||||
<dl>
|
||||
<dt><i>strg_to_parse</i></dt>
|
||||
<dd>
|
||||
<p>
|
||||
A string to be parsed to obtain attribute values for Alignment, Borders, Font,
|
||||
Pattern and Protection objects. Refer to the examples
|
||||
in the file .../examples/xlwt_easyxf_simple_demo.py and to the xf_dict
|
||||
dictionary in Style.py. Various synonyms including color/colour, center/centre and gray/grey
|
||||
are allowed. Case is irrelevant (except maybe in font names). '-' may be used instead
|
||||
of '_'.<br />
|
||||
Example: "font: bold on; align: wrap on, vert centre, horiz center"
|
||||
</p>
|
||||
</dd>
|
||||
<dt><i>num_format_str</i></dt>
|
||||
<dd>
|
||||
<p>
|
||||
To get the "number format string" of an existing cell whose format you want to reproduce,
|
||||
select the cell and click on Format/Cells/Number/Custom. Otherwise, refer to Excel help.<br />
|
||||
Examples: "#,##0.00", "dd/mm/yyyy"
|
||||
</p>
|
||||
</dd>
|
||||
<dt>Returns:</dt>
|
||||
<dd>
|
||||
An object of the XFstyle class
|
||||
</dd>
|
||||
</dl>
|
||||
<br />
|
||||
|
||||
</dd>
|
||||
|
||||
<dt><b>Workbook</b> (class) [<a href='#xlwt.Workbook-class'>#</a>]</dt>
|
||||
<dd>
|
||||
<p>The class to instantiate to create a workbook</p>
|
||||
<p>For more information about this class, see <a href='#xlwt.Workbook-class'><i>The Workbook Class</i></a>.</p>
|
||||
</dd>
|
||||
<dt><b>Worksheet</b> (class) [<a href='#xlwt.Worksheet-class'>#</a>]</dt>
|
||||
<dd>
|
||||
<p>A class to represent the contents of a sheet in a workbook.</p>
|
||||
<p>For more information about this class, see <a href='#xlwt.Worksheet-class'><i>The Worksheet Class</i></a>.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<h2><a id='xlwt.Workbook-class' name='xlwt.Workbook-class'>The Workbook Class</a><em style="color:red;">[NC]</em></h2>
|
||||
<dl>
|
||||
<dt><b>Workbook(encoding='ascii',style_compression=0)</b> (class) [<a href='#xlwt.Workbook-class'>#</a>]</dt>
|
||||
<dd>
|
||||
<p>
|
||||
This is a class representing a workbook and all its contents.
|
||||
When creating Excel files with xlwt, you will normally start by
|
||||
instantiating an object of this class.
|
||||
</p>
|
||||
<dl>
|
||||
<dt><i>encoding</i></dt>
|
||||
<dd>
|
||||
<em style="color:red;">[NC]</em>
|
||||
</dd>
|
||||
<dt><i>style_compression</i></dt>
|
||||
<dd>
|
||||
<em style="color:red;">[NC]</em>
|
||||
</dd>
|
||||
<dt>Returns:</dt>
|
||||
<dd>
|
||||
An object of the <a href="#xlwt.Workbook-class">Workbook</a> class
|
||||
</dd>
|
||||
</dl>
|
||||
<br />
|
||||
</dd>
|
||||
<dt><a id='xlwt.Workbook.add_sheet-method' name='xlwt.Workbook.add_sheet-method'><b>add_sheet(sheetname)</b></a> [<a href='#xlwt.Workbook.add_sheet-method'>#</a>]</dt>
|
||||
<dd>
|
||||
<p>
|
||||
This method is used to create Worksheets in a Workbook.
|
||||
</p>
|
||||
<dl>
|
||||
<dt><i>sheetname</i></dt>
|
||||
<dd>
|
||||
The name to use for this sheet, as it will appear in the tabs at
|
||||
the bottom of the Excel application.
|
||||
</dd>
|
||||
<dt>Returns:</dt>
|
||||
<dd>
|
||||
An object of the <a href="#xlwt.Worksheet-class">Worksheet</a> class
|
||||
</dd>
|
||||
</dl>
|
||||
<br />
|
||||
</dd>
|
||||
<dt><a id='xlwt.Workbook.save-method' name='xlwt.Workbook.save-method'><b>save(filename_or_stream)</b></a> [<a href='#xlwt.Workbook.save-method'>#</a>]</dt>
|
||||
<dd>
|
||||
<p>
|
||||
This method is used to save Workbook to a file in native Excel format.
|
||||
</p>
|
||||
<dl>
|
||||
<dt><i>filename_or_stream</i></dt>
|
||||
<dd>
|
||||
<p>
|
||||
This can be a string containing a filename of the file, in which case
|
||||
the excel file is saved to disk using the name provided.
|
||||
</p>
|
||||
<p>
|
||||
It can also be a stream object with a write method, such as a
|
||||
StringIO, in which case the data for the excel file is written
|
||||
to the stream.
|
||||
</p>
|
||||
</dd>
|
||||
</dl>
|
||||
<br />
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<h2><a id='xlwt.Worksheet-class' name='xlwt.Worksheet-class'>The Worksheet Class</a><em style="color:red;">[NC]</em></h2>
|
||||
<dl>
|
||||
<dt><b>Worksheet(sheetname, parent_book)</b> (class) [<a href='#xlwt.Worksheet-class'>#</a>]</dt>
|
||||
<dd>
|
||||
<p>
|
||||
This is a class representing the contents of a sheet in a workbook.
|
||||
</p>
|
||||
<p>
|
||||
WARNING: You don't normally create instances of this class
|
||||
yourself. They are returned from calls to <a href="#xlwt.Workbook.add_sheet-method">Workbook.add_sheet</a>
|
||||
</p>
|
||||
</dd>
|
||||
<dt><a id='xlwt.Worksheet.write-method'
|
||||
name='xlwt.Worksheet.write-method'><b>write(r, c, label="", style=Style.default_style)</b></a> [<a href='#xlwt.Worksheet.write-method'>#</a>]</dt>
|
||||
<dd>
|
||||
<p>
|
||||
This method is used to write a cell to a Worksheet..
|
||||
</p>
|
||||
<dl>
|
||||
<dt><i>r</i></dt>
|
||||
<dd>
|
||||
The zero-relative number of the row in the worksheet to which the cell should be written.
|
||||
</dd>
|
||||
<dt><i>c</i></dt>
|
||||
<dd>
|
||||
The zero-relative number of the column in the worksheet to which the cell should be written.
|
||||
</dd>
|
||||
<dt><i>label</i></dt>
|
||||
<dd>
|
||||
The data value to be written.
|
||||
An int, long, or decimal.Decimal instance is converted to float.
|
||||
A unicode instance is written as is.
|
||||
A str instance is converted to unicode using the encoding (default: 'ascii') specified
|
||||
when the Workbook instance was created.
|
||||
A datetime.datetime, datetime.date, or datetime.time instance is converted into Excel date format
|
||||
(a float representing the number of days since (typically) 1899-12-31T00:00:00,
|
||||
under the pretence that 1900 was a leap year).
|
||||
A bool instance will show up as TRUE or FALSE in Excel.
|
||||
None causes the cell to be blank -- no data, only formatting.
|
||||
An xlwt.Formula instance causes an Excel formula to be written.
|
||||
<em style="color:red;">[NC]</em>
|
||||
</dd>
|
||||
<dt><i>style</i></dt>
|
||||
<dd>
|
||||
A style -- also known as an XF (extended format) -- is an XFStyle object, which encapsulates
|
||||
the formatting applied to the cell and its contents. XFStyle objects are best set up using the
|
||||
<i>easyxf</i> function. They may also be set up by setting attributes in
|
||||
Alignment, Borders, Pattern, Font and Protection objects
|
||||
then setting those objects and a format string as attributes of an XFStyle object.
|
||||
<em style="color:red;">[NC]</em>
|
||||
</dd>
|
||||
</dl>
|
||||
<br />
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
</body></html>
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# tries stress SST, SAT and MSAT
|
||||
|
||||
from time import *
|
||||
from xlwt.Workbook import *
|
||||
from xlwt.Style import *
|
||||
|
||||
style = XFStyle()
|
||||
|
||||
wb = Workbook()
|
||||
ws0 = wb.add_sheet('0')
|
||||
|
||||
colcount = 200 + 1
|
||||
rowcount = 6000 + 1
|
||||
|
||||
t0 = time()
|
||||
print "\nstart: %s" % ctime(t0)
|
||||
|
||||
print "Filling..."
|
||||
for col in xrange(colcount):
|
||||
print "[%d]" % col,
|
||||
for row in xrange(rowcount):
|
||||
#ws0.write(row, col, "BIG(%d, %d)" % (row, col))
|
||||
ws0.write(row, col, "BIG")
|
||||
|
||||
t1 = time() - t0
|
||||
print "\nsince starting elapsed %.2f s" % (t1)
|
||||
|
||||
print "Storing..."
|
||||
wb.save('big-16Mb.xls')
|
||||
|
||||
t2 = time() - t0
|
||||
print "since starting elapsed %.2f s" % (t2)
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# tries stress SST, SAT and MSAT
|
||||
|
||||
from time import *
|
||||
from xlwt import *
|
||||
|
||||
style = XFStyle()
|
||||
|
||||
wb = Workbook()
|
||||
ws0 = wb.add_sheet('0')
|
||||
|
||||
colcount = 200 + 1
|
||||
rowcount = 6000 + 1
|
||||
|
||||
t0 = time()
|
||||
print "\nstart: %s" % ctime(t0)
|
||||
|
||||
print "Filling..."
|
||||
for col in xrange(colcount):
|
||||
print "[%d]" % col,
|
||||
for row in xrange(rowcount):
|
||||
ws0.write(row, col, "BIG(%d, %d)" % (row, col))
|
||||
#ws0.write(row, col, "BIG")
|
||||
|
||||
t1 = time() - t0
|
||||
print "\nsince starting elapsed %.2f s" % (t1)
|
||||
|
||||
print "Storing..."
|
||||
wb.save('big-35Mb.xls')
|
||||
|
||||
t2 = time() - t0
|
||||
print "since starting elapsed %.2f s" % (t2)
|
||||
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
font0 = Font()
|
||||
font0.name = 'Times New Roman'
|
||||
font0.struck_out = True
|
||||
font0.bold = True
|
||||
|
||||
style0 = XFStyle()
|
||||
style0.font = font0
|
||||
|
||||
|
||||
wb = Workbook()
|
||||
ws0 = wb.add_sheet('0')
|
||||
|
||||
ws0.write(1, 1, 'Test', style0)
|
||||
|
||||
for i in range(0, 0x53):
|
||||
borders = Borders()
|
||||
borders.left = i
|
||||
borders.right = i
|
||||
borders.top = i
|
||||
borders.bottom = i
|
||||
|
||||
style = XFStyle()
|
||||
style.borders = borders
|
||||
|
||||
ws0.write(i, 2, '', style)
|
||||
ws0.write(i, 3, hex(i), style0)
|
||||
|
||||
ws0.write_merge(5, 8, 6, 10, "")
|
||||
|
||||
wb.save('blanks.xls')
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
__rev_id__ = """$Id: col_width.py 3315 2008-03-14 14:44:52Z chris $"""
|
||||
|
||||
|
||||
from xlwt import *
|
||||
|
||||
w = Workbook()
|
||||
ws = w.add_sheet('Hey, Dude')
|
||||
|
||||
for i in range(6, 80):
|
||||
fnt = Font()
|
||||
fnt.height = i*20
|
||||
style = XFStyle()
|
||||
style.font = fnt
|
||||
ws.write(1, i, 'Test')
|
||||
ws.col(i).width = 0x0d00 + i
|
||||
w.save('col_width.xls')
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1252 -*-
|
||||
# Copyright (C) 2007 John Machin
|
||||
|
||||
from xlwt import *
|
||||
|
||||
w = Workbook()
|
||||
w.country_code = 61
|
||||
ws = w.add_sheet('AU')
|
||||
w.save('country.xls')
|
||||
@@ -1,37 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
from datetime import datetime
|
||||
|
||||
w = Workbook()
|
||||
ws = w.add_sheet('Hey, Dude')
|
||||
|
||||
fmts = [
|
||||
'M/D/YY',
|
||||
'D-MMM-YY',
|
||||
'D-MMM',
|
||||
'MMM-YY',
|
||||
'h:mm AM/PM',
|
||||
'h:mm:ss AM/PM',
|
||||
'h:mm',
|
||||
'h:mm:ss',
|
||||
'M/D/YY h:mm',
|
||||
'mm:ss',
|
||||
'[h]:mm:ss',
|
||||
'mm:ss.0',
|
||||
]
|
||||
|
||||
i = 0
|
||||
for fmt in fmts:
|
||||
ws.write(i, 0, fmt)
|
||||
|
||||
style = XFStyle()
|
||||
style.num_format_str = fmt
|
||||
|
||||
ws.write(i, 4, datetime.now(), style)
|
||||
|
||||
i += 1
|
||||
|
||||
w.save('dates.xls')
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
font0 = Font()
|
||||
font0.name = 'Times New Roman'
|
||||
font0.struck_out = True
|
||||
font0.bold = True
|
||||
|
||||
style0 = XFStyle()
|
||||
style0.font = font0
|
||||
|
||||
|
||||
wb = Workbook()
|
||||
ws0 = wb.add_sheet('0')
|
||||
|
||||
ws0.write(1, 1, 'Test', style0)
|
||||
|
||||
for i in range(0, 0x53):
|
||||
fnt = Font()
|
||||
fnt.name = 'Arial'
|
||||
fnt.colour_index = i
|
||||
fnt.outline = True
|
||||
|
||||
borders = Borders()
|
||||
borders.left = i
|
||||
|
||||
style = XFStyle()
|
||||
style.font = fnt
|
||||
style.borders = borders
|
||||
|
||||
ws0.write(i, 2, 'colour', style)
|
||||
ws0.write(i, 3, hex(i), style0)
|
||||
|
||||
|
||||
wb.save('format.xls')
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
from xlwt.ExcelFormulaParser import FormulaParseException
|
||||
|
||||
w = Workbook()
|
||||
ws = w.add_sheet('F')
|
||||
|
||||
## This example is a little silly since the formula building is
|
||||
## so simplistic that it often fails because the generated text
|
||||
## has the wrong number of parameters for the function being
|
||||
## tested.
|
||||
|
||||
i = 0
|
||||
succeed_count = 0
|
||||
fail_count = 0
|
||||
for n in sorted(ExcelMagic.std_func_by_name):
|
||||
ws.write(i, 0, n)
|
||||
text = n + "($A$1)"
|
||||
try:
|
||||
formula = Formula(text)
|
||||
except FormulaParseException,e:
|
||||
print "Could not parse %r: %s" % (text,e.args[0])
|
||||
fail_count += 1
|
||||
else:
|
||||
ws.write(i, 3, formula)
|
||||
succeed_count += 1
|
||||
i += 1
|
||||
|
||||
w.save('formula_names.xls')
|
||||
|
||||
print "succeeded with %i functions, failed with %i" % (succeed_count,fail_count)
|
||||
@@ -1,47 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
w = Workbook()
|
||||
ws = w.add_sheet('F')
|
||||
|
||||
ws.write(0, 0, Formula("-(1+1)"))
|
||||
ws.write(1, 0, Formula("-(1+1)/(-2-2)"))
|
||||
ws.write(2, 0, Formula("-(134.8780789+1)"))
|
||||
ws.write(3, 0, Formula("-(134.8780789e-10+1)"))
|
||||
ws.write(4, 0, Formula("-1/(1+1)+9344"))
|
||||
|
||||
ws.write(0, 1, Formula("-(1+1)"))
|
||||
ws.write(1, 1, Formula("-(1+1)/(-2-2)"))
|
||||
ws.write(2, 1, Formula("-(134.8780789+1)"))
|
||||
ws.write(3, 1, Formula("-(134.8780789e-10+1)"))
|
||||
ws.write(4, 1, Formula("-1/(1+1)+9344"))
|
||||
|
||||
ws.write(0, 2, Formula("A1*B1"))
|
||||
ws.write(1, 2, Formula("A2*B2"))
|
||||
ws.write(2, 2, Formula("A3*B3"))
|
||||
ws.write(3, 2, Formula("A4*B4*sin(pi()/4)"))
|
||||
ws.write(4, 2, Formula("A5%*B5*pi()/1000"))
|
||||
|
||||
##############
|
||||
## NOTE: parameters are separated by semicolon!!!
|
||||
##############
|
||||
|
||||
|
||||
ws.write(5, 2, Formula("C1+C2+C3+C4+C5/(C1+C2+C3+C4/(C1+C2+C3+C4/(C1+C2+C3+C4)+C5)+C5)-20.3e-2"))
|
||||
ws.write(5, 3, Formula("C1^2"))
|
||||
ws.write(6, 2, Formula("SUM(C1;C2;;;;;C3;;;C4)"))
|
||||
ws.write(6, 3, Formula("SUM($A$1:$C$5)"))
|
||||
|
||||
ws.write(7, 0, Formula('"lkjljllkllkl"'))
|
||||
ws.write(7, 1, Formula('"yuyiyiyiyi"'))
|
||||
ws.write(7, 2, Formula('A8 & B8 & A8'))
|
||||
ws.write(8, 2, Formula('now()'))
|
||||
|
||||
ws.write(10, 2, Formula('TRUE'))
|
||||
ws.write(11, 2, Formula('FALSE'))
|
||||
ws.write(12, 3, Formula('IF(A1>A2;3;"hkjhjkhk")'))
|
||||
|
||||
w.save('formulas.xls')
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
f = Font()
|
||||
f.height = 20*72
|
||||
f.name = 'Verdana'
|
||||
f.bold = True
|
||||
f.underline = Font.UNDERLINE_DOUBLE
|
||||
f.colour_index = 4
|
||||
|
||||
h_style = XFStyle()
|
||||
h_style.font = f
|
||||
|
||||
w = Workbook()
|
||||
ws = w.add_sheet('F')
|
||||
|
||||
##############
|
||||
## NOTE: parameters are separated by semicolon!!!
|
||||
##############
|
||||
|
||||
n = "HYPERLINK"
|
||||
ws.write_merge(1, 1, 1, 10, Formula(n + '("http://www.irs.gov/pub/irs-pdf/f1000.pdf";"f1000.pdf")'), h_style)
|
||||
ws.write_merge(2, 2, 2, 25, Formula(n + '("mailto:roman.kiseliov@gmail.com?subject=pyExcelerator-feedback&Body=Hello,%20Roman!";"pyExcelerator-feedback")'), h_style)
|
||||
|
||||
w.save("hyperlinks.xls")
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
w = Workbook()
|
||||
ws = w.add_sheet('Image')
|
||||
ws.insert_bitmap('python.bmp', 2, 2)
|
||||
ws.insert_bitmap('python.bmp', 10, 2)
|
||||
|
||||
w.save('image.xls')
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
fnt = Font()
|
||||
fnt.name = 'Arial'
|
||||
fnt.colour_index = 4
|
||||
fnt.bold = True
|
||||
|
||||
borders = Borders()
|
||||
borders.left = 6
|
||||
borders.right = 6
|
||||
borders.top = 6
|
||||
borders.bottom = 6
|
||||
|
||||
al = Alignment()
|
||||
al.horz = Alignment.HORZ_CENTER
|
||||
al.vert = Alignment.VERT_CENTER
|
||||
|
||||
style = XFStyle()
|
||||
style.font = fnt
|
||||
style.borders = borders
|
||||
style.alignment = al
|
||||
|
||||
|
||||
wb = Workbook()
|
||||
ws0 = wb.add_sheet('sheet0')
|
||||
ws1 = wb.add_sheet('sheet1')
|
||||
ws2 = wb.add_sheet('sheet2')
|
||||
|
||||
for i in range(0, 0x200, 2):
|
||||
ws0.write_merge(i, i+1, 1, 5, 'test %d' % i, style)
|
||||
ws1.write_merge(i, i, 1, 7, 'test %d' % i, style)
|
||||
ws2.write_merge(i, i+1, 1, 7 + (i%10), 'test %d' % i, style)
|
||||
|
||||
|
||||
wb.save('merged.xls')
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
wb = Workbook()
|
||||
ws0 = wb.add_sheet('sheet0')
|
||||
|
||||
|
||||
fnt = Font()
|
||||
fnt.name = 'Arial'
|
||||
fnt.colour_index = 4
|
||||
fnt.bold = True
|
||||
|
||||
borders = Borders()
|
||||
borders.left = 6
|
||||
borders.right = 6
|
||||
borders.top = 6
|
||||
borders.bottom = 6
|
||||
|
||||
style = XFStyle()
|
||||
style.font = fnt
|
||||
style.borders = borders
|
||||
|
||||
ws0.write_merge(3, 3, 1, 5, 'test1', style)
|
||||
ws0.write_merge(4, 10, 1, 5, 'test2', style)
|
||||
ws0.col(1).width = 0x0d00
|
||||
|
||||
wb.save('merged0.xls')
|
||||
@@ -1,102 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
wb = Workbook()
|
||||
ws0 = wb.add_sheet('sheet0')
|
||||
|
||||
fnt1 = Font()
|
||||
fnt1.name = 'Verdana'
|
||||
fnt1.bold = True
|
||||
fnt1.height = 18*0x14
|
||||
|
||||
pat1 = Pattern()
|
||||
pat1.pattern = Pattern.SOLID_PATTERN
|
||||
pat1.pattern_fore_colour = 0x16
|
||||
|
||||
brd1 = Borders()
|
||||
brd1.left = 0x06
|
||||
brd1.right = 0x06
|
||||
brd1.top = 0x06
|
||||
brd1.bottom = 0x06
|
||||
|
||||
fnt2 = Font()
|
||||
fnt2.name = 'Verdana'
|
||||
fnt2.bold = True
|
||||
fnt2.height = 14*0x14
|
||||
|
||||
brd2 = Borders()
|
||||
brd2.left = 0x01
|
||||
brd2.right = 0x01
|
||||
brd2.top = 0x01
|
||||
brd2.bottom = 0x01
|
||||
|
||||
pat2 = Pattern()
|
||||
pat2.pattern = Pattern.SOLID_PATTERN
|
||||
pat2.pattern_fore_colour = 0x01F
|
||||
|
||||
fnt3 = Font()
|
||||
fnt3.name = 'Verdana'
|
||||
fnt3.bold = True
|
||||
fnt3.italic = True
|
||||
fnt3.height = 12*0x14
|
||||
|
||||
brd3 = Borders()
|
||||
brd3.left = 0x07
|
||||
brd3.right = 0x07
|
||||
brd3.top = 0x07
|
||||
brd3.bottom = 0x07
|
||||
|
||||
fnt4 = Font()
|
||||
|
||||
al1 = Alignment()
|
||||
al1.horz = Alignment.HORZ_CENTER
|
||||
al1.vert = Alignment.VERT_CENTER
|
||||
|
||||
al2 = Alignment()
|
||||
al2.horz = Alignment.HORZ_RIGHT
|
||||
al2.vert = Alignment.VERT_CENTER
|
||||
|
||||
al3 = Alignment()
|
||||
al3.horz = Alignment.HORZ_LEFT
|
||||
al3.vert = Alignment.VERT_CENTER
|
||||
|
||||
style1 = XFStyle()
|
||||
style1.font = fnt1
|
||||
style1.alignment = al1
|
||||
style1.pattern = pat1
|
||||
style1.borders = brd1
|
||||
|
||||
style2 = XFStyle()
|
||||
style2.font = fnt2
|
||||
style2.alignment = al1
|
||||
style2.pattern = pat2
|
||||
style2.borders = brd2
|
||||
|
||||
style3 = XFStyle()
|
||||
style3.font = fnt3
|
||||
style3.alignment = al1
|
||||
style3.pattern = pat2
|
||||
style3.borders = brd3
|
||||
|
||||
price_style = XFStyle()
|
||||
price_style.font = fnt4
|
||||
price_style.alignment = al2
|
||||
price_style.borders = brd3
|
||||
price_style.num_format_str = '_(#,##0.00_) "money"'
|
||||
|
||||
ware_style = XFStyle()
|
||||
ware_style.font = fnt4
|
||||
ware_style.alignment = al3
|
||||
ware_style.borders = brd3
|
||||
|
||||
|
||||
ws0.merge(3, 3, 1, 5, style1)
|
||||
ws0.merge(4, 10, 1, 6, style2)
|
||||
ws0.merge(14, 16, 1, 7, style3)
|
||||
ws0.col(1).width = 0x0d00
|
||||
|
||||
|
||||
wb.save('merged1.xls')
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
w = Workbook()
|
||||
ws = w.add_sheet('xlwt was here')
|
||||
w.save('mini.xls')
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
w = Workbook()
|
||||
ws = w.add_sheet('Hey, Dude')
|
||||
|
||||
fmts = [
|
||||
'general',
|
||||
'0',
|
||||
'0.00',
|
||||
'#,##0',
|
||||
'#,##0.00',
|
||||
'"$"#,##0_);("$"#,##',
|
||||
'"$"#,##0_);[Red]("$"#,##',
|
||||
'"$"#,##0.00_);("$"#,##',
|
||||
'"$"#,##0.00_);[Red]("$"#,##',
|
||||
'0%',
|
||||
'0.00%',
|
||||
'0.00E+00',
|
||||
'# ?/?',
|
||||
'# ??/??',
|
||||
'M/D/YY',
|
||||
'D-MMM-YY',
|
||||
'D-MMM',
|
||||
'MMM-YY',
|
||||
'h:mm AM/PM',
|
||||
'h:mm:ss AM/PM',
|
||||
'h:mm',
|
||||
'h:mm:ss',
|
||||
'M/D/YY h:mm',
|
||||
'_(#,##0_);(#,##0)',
|
||||
'_(#,##0_);[Red](#,##0)',
|
||||
'_(#,##0.00_);(#,##0.00)',
|
||||
'_(#,##0.00_);[Red](#,##0.00)',
|
||||
'_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)',
|
||||
'_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)',
|
||||
'_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)',
|
||||
'_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)',
|
||||
'mm:ss',
|
||||
'[h]:mm:ss',
|
||||
'mm:ss.0',
|
||||
'##0.0E+0',
|
||||
'@'
|
||||
]
|
||||
|
||||
i = 0
|
||||
for fmt in fmts:
|
||||
ws.write(i, 0, fmt)
|
||||
|
||||
style = XFStyle()
|
||||
style.num_format_str = fmt
|
||||
|
||||
ws.write(i, 4, -1278.9078, style)
|
||||
|
||||
i += 1
|
||||
|
||||
w.save('num_formats.xls')
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
w = Workbook()
|
||||
ws = w.add_sheet('Hey, Dude')
|
||||
|
||||
ws.write(0, 0, 1)
|
||||
ws.write(1, 0, 1.23)
|
||||
ws.write(2, 0, 12345678)
|
||||
ws.write(3, 0, 123456.78)
|
||||
|
||||
ws.write(0, 1, -1)
|
||||
ws.write(1, 1, -1.23)
|
||||
ws.write(2, 1, -12345678)
|
||||
ws.write(3, 1, -123456.78)
|
||||
|
||||
ws.write(0, 2, -17867868678687.0)
|
||||
ws.write(1, 2, -1.23e-5)
|
||||
ws.write(2, 2, -12345678.90780980)
|
||||
ws.write(3, 2, -123456.78)
|
||||
|
||||
w.save('numbers.xls')
|
||||
@@ -1,113 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
fnt = Font()
|
||||
fnt.name = 'Arial'
|
||||
fnt.colour_index = 4
|
||||
fnt.bold = True
|
||||
|
||||
borders = Borders()
|
||||
borders.left = 6
|
||||
borders.right = 6
|
||||
borders.top = 6
|
||||
borders.bottom = 6
|
||||
|
||||
style = XFStyle()
|
||||
style.font = fnt
|
||||
style.borders = borders
|
||||
|
||||
wb = Workbook()
|
||||
|
||||
ws0 = wb.add_sheet('Rows Outline')
|
||||
|
||||
ws0.write_merge(1, 1, 1, 5, 'test 1', style)
|
||||
ws0.write_merge(2, 2, 1, 4, 'test 1', style)
|
||||
ws0.write_merge(3, 3, 1, 3, 'test 2', style)
|
||||
ws0.write_merge(4, 4, 1, 4, 'test 1', style)
|
||||
ws0.write_merge(5, 5, 1, 4, 'test 3', style)
|
||||
ws0.write_merge(6, 6, 1, 5, 'test 1', style)
|
||||
ws0.write_merge(7, 7, 1, 5, 'test 4', style)
|
||||
ws0.write_merge(8, 8, 1, 4, 'test 1', style)
|
||||
ws0.write_merge(9, 9, 1, 3, 'test 5', style)
|
||||
|
||||
ws0.row(1).level = 1
|
||||
ws0.row(2).level = 1
|
||||
ws0.row(3).level = 2
|
||||
ws0.row(4).level = 2
|
||||
ws0.row(5).level = 2
|
||||
ws0.row(6).level = 2
|
||||
ws0.row(7).level = 2
|
||||
ws0.row(8).level = 1
|
||||
ws0.row(9).level = 1
|
||||
|
||||
|
||||
ws1 = wb.add_sheet('Columns Outline')
|
||||
|
||||
ws1.write_merge(1, 1, 1, 5, 'test 1', style)
|
||||
ws1.write_merge(2, 2, 1, 4, 'test 1', style)
|
||||
ws1.write_merge(3, 3, 1, 3, 'test 2', style)
|
||||
ws1.write_merge(4, 4, 1, 4, 'test 1', style)
|
||||
ws1.write_merge(5, 5, 1, 4, 'test 3', style)
|
||||
ws1.write_merge(6, 6, 1, 5, 'test 1', style)
|
||||
ws1.write_merge(7, 7, 1, 5, 'test 4', style)
|
||||
ws1.write_merge(8, 8, 1, 4, 'test 1', style)
|
||||
ws1.write_merge(9, 9, 1, 3, 'test 5', style)
|
||||
|
||||
ws1.col(1).level = 1
|
||||
ws1.col(2).level = 1
|
||||
ws1.col(3).level = 2
|
||||
ws1.col(4).level = 2
|
||||
ws1.col(5).level = 2
|
||||
ws1.col(6).level = 2
|
||||
ws1.col(7).level = 2
|
||||
ws1.col(8).level = 1
|
||||
ws1.col(9).level = 1
|
||||
|
||||
|
||||
ws2 = wb.add_sheet('Rows and Columns Outline')
|
||||
|
||||
ws2.write_merge(1, 1, 1, 5, 'test 1', style)
|
||||
ws2.write_merge(2, 2, 1, 4, 'test 1', style)
|
||||
ws2.write_merge(3, 3, 1, 3, 'test 2', style)
|
||||
ws2.write_merge(4, 4, 1, 4, 'test 1', style)
|
||||
ws2.write_merge(5, 5, 1, 4, 'test 3', style)
|
||||
ws2.write_merge(6, 6, 1, 5, 'test 1', style)
|
||||
ws2.write_merge(7, 7, 1, 5, 'test 4', style)
|
||||
ws2.write_merge(8, 8, 1, 4, 'test 1', style)
|
||||
ws2.write_merge(9, 9, 1, 3, 'test 5', style)
|
||||
|
||||
ws2.row(1).level = 1
|
||||
ws2.row(2).level = 1
|
||||
ws2.row(3).level = 2
|
||||
ws2.row(4).level = 2
|
||||
ws2.row(5).level = 2
|
||||
ws2.row(6).level = 2
|
||||
ws2.row(7).level = 2
|
||||
ws2.row(8).level = 1
|
||||
ws2.row(9).level = 1
|
||||
|
||||
ws2.write_merge(1, 1, 1, 5, 'test 1', style)
|
||||
ws2.write_merge(2, 2, 1, 4, 'test 1', style)
|
||||
ws2.write_merge(3, 3, 1, 3, 'test 2', style)
|
||||
ws2.write_merge(4, 4, 1, 4, 'test 1', style)
|
||||
ws2.write_merge(5, 5, 1, 4, 'test 3', style)
|
||||
ws2.write_merge(6, 6, 1, 5, 'test 1', style)
|
||||
ws2.write_merge(7, 7, 1, 5, 'test 4', style)
|
||||
ws2.write_merge(8, 8, 1, 4, 'test 1', style)
|
||||
ws2.write_merge(9, 9, 1, 3, 'test 5', style)
|
||||
|
||||
ws2.col(1).level = 1
|
||||
ws2.col(2).level = 1
|
||||
ws2.col(3).level = 2
|
||||
ws2.col(4).level = 2
|
||||
ws2.col(5).level = 2
|
||||
ws2.col(6).level = 2
|
||||
ws2.col(7).level = 2
|
||||
ws2.col(8).level = 1
|
||||
ws2.col(9).level = 1
|
||||
|
||||
|
||||
wb.save('outline.xls')
|
||||
@@ -1,58 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
w = Workbook()
|
||||
ws1 = w.add_sheet('sheet 1')
|
||||
ws2 = w.add_sheet('sheet 2')
|
||||
ws3 = w.add_sheet('sheet 3')
|
||||
ws4 = w.add_sheet('sheet 4')
|
||||
ws5 = w.add_sheet('sheet 5')
|
||||
ws6 = w.add_sheet('sheet 6')
|
||||
|
||||
for i in range(0x100):
|
||||
ws1.write(i/0x10, i%0x10, i)
|
||||
|
||||
for i in range(0x100):
|
||||
ws2.write(i/0x10, i%0x10, i)
|
||||
|
||||
for i in range(0x100):
|
||||
ws3.write(i/0x10, i%0x10, i)
|
||||
|
||||
for i in range(0x100):
|
||||
ws4.write(i/0x10, i%0x10, i)
|
||||
|
||||
for i in range(0x100):
|
||||
ws5.write(i/0x10, i%0x10, i)
|
||||
|
||||
for i in range(0x100):
|
||||
ws6.write(i/0x10, i%0x10, i)
|
||||
|
||||
ws1.panes_frozen = True
|
||||
ws1.horz_split_pos = 2
|
||||
|
||||
ws2.panes_frozen = True
|
||||
ws2.vert_split_pos = 2
|
||||
|
||||
ws3.panes_frozen = True
|
||||
ws3.horz_split_pos = 1
|
||||
ws3.vert_split_pos = 1
|
||||
|
||||
ws4.panes_frozen = False
|
||||
ws4.horz_split_pos = 12
|
||||
ws4.horz_split_first_visible = 2
|
||||
|
||||
ws5.panes_frozen = False
|
||||
ws5.vert_split_pos = 40
|
||||
ws4.vert_split_first_visible = 2
|
||||
|
||||
ws6.panes_frozen = False
|
||||
ws6.horz_split_pos = 12
|
||||
ws4.horz_split_first_visible = 2
|
||||
ws6.vert_split_pos = 40
|
||||
ws4.vert_split_first_visible = 2
|
||||
|
||||
w.save('panes.xls')
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
from xlwt import ExcelFormulaParser, ExcelFormula
|
||||
import sys
|
||||
|
||||
f = ExcelFormula.Formula(
|
||||
""" -((1.80 + 2.898 * 1)/(1.80 + 2.898))*
|
||||
AVERAGE((1.80 + 2.898 * 1)/(1.80 + 2.898);
|
||||
(1.80 + 2.898 * 1)/(1.80 + 2.898);
|
||||
(1.80 + 2.898 * 1)/(1.80 + 2.898)) +
|
||||
SIN(PI()/4)""")
|
||||
|
||||
#for t in f.rpn():
|
||||
# print "%15s %15s" % (ExcelFormulaParser.PtgNames[t[0]], t[1])
|
||||
@@ -1,122 +0,0 @@
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
fnt = Font()
|
||||
fnt.name = 'Arial'
|
||||
fnt.colour_index = 4
|
||||
fnt.bold = True
|
||||
|
||||
borders = Borders()
|
||||
borders.left = 6
|
||||
borders.right = 6
|
||||
borders.top = 6
|
||||
borders.bottom = 6
|
||||
|
||||
style = XFStyle()
|
||||
style.font = fnt
|
||||
style.borders = borders
|
||||
|
||||
wb = Workbook()
|
||||
|
||||
ws0 = wb.add_sheet('Rows Outline')
|
||||
|
||||
ws0.write_merge(1, 1, 1, 5, 'test 1', style)
|
||||
ws0.write_merge(2, 2, 1, 4, 'test 1', style)
|
||||
ws0.write_merge(3, 3, 1, 3, 'test 2', style)
|
||||
ws0.write_merge(4, 4, 1, 4, 'test 1', style)
|
||||
ws0.write_merge(5, 5, 1, 4, 'test 3', style)
|
||||
ws0.write_merge(6, 6, 1, 5, 'test 1', style)
|
||||
ws0.write_merge(7, 7, 1, 5, 'test 4', style)
|
||||
ws0.write_merge(8, 8, 1, 4, 'test 1', style)
|
||||
ws0.write_merge(9, 9, 1, 3, 'test 5', style)
|
||||
|
||||
ws0.row(1).level = 1
|
||||
ws0.row(2).level = 1
|
||||
ws0.row(3).level = 2
|
||||
ws0.row(4).level = 2
|
||||
ws0.row(5).level = 2
|
||||
ws0.row(6).level = 2
|
||||
ws0.row(7).level = 2
|
||||
ws0.row(8).level = 1
|
||||
ws0.row(9).level = 1
|
||||
|
||||
|
||||
ws1 = wb.add_sheet('Columns Outline')
|
||||
|
||||
ws1.write_merge(1, 1, 1, 5, 'test 1', style)
|
||||
ws1.write_merge(2, 2, 1, 4, 'test 1', style)
|
||||
ws1.write_merge(3, 3, 1, 3, 'test 2', style)
|
||||
ws1.write_merge(4, 4, 1, 4, 'test 1', style)
|
||||
ws1.write_merge(5, 5, 1, 4, 'test 3', style)
|
||||
ws1.write_merge(6, 6, 1, 5, 'test 1', style)
|
||||
ws1.write_merge(7, 7, 1, 5, 'test 4', style)
|
||||
ws1.write_merge(8, 8, 1, 4, 'test 1', style)
|
||||
ws1.write_merge(9, 9, 1, 3, 'test 5', style)
|
||||
|
||||
ws1.col(1).level = 1
|
||||
ws1.col(2).level = 1
|
||||
ws1.col(3).level = 2
|
||||
ws1.col(4).level = 2
|
||||
ws1.col(5).level = 2
|
||||
ws1.col(6).level = 2
|
||||
ws1.col(7).level = 2
|
||||
ws1.col(8).level = 1
|
||||
ws1.col(9).level = 1
|
||||
|
||||
|
||||
ws2 = wb.add_sheet('Rows and Columns Outline')
|
||||
|
||||
ws2.write_merge(1, 1, 1, 5, 'test 1', style)
|
||||
ws2.write_merge(2, 2, 1, 4, 'test 1', style)
|
||||
ws2.write_merge(3, 3, 1, 3, 'test 2', style)
|
||||
ws2.write_merge(4, 4, 1, 4, 'test 1', style)
|
||||
ws2.write_merge(5, 5, 1, 4, 'test 3', style)
|
||||
ws2.write_merge(6, 6, 1, 5, 'test 1', style)
|
||||
ws2.write_merge(7, 7, 1, 5, 'test 4', style)
|
||||
ws2.write_merge(8, 8, 1, 4, 'test 1', style)
|
||||
ws2.write_merge(9, 9, 1, 3, 'test 5', style)
|
||||
|
||||
ws2.row(1).level = 1
|
||||
ws2.row(2).level = 1
|
||||
ws2.row(3).level = 2
|
||||
ws2.row(4).level = 2
|
||||
ws2.row(5).level = 2
|
||||
ws2.row(6).level = 2
|
||||
ws2.row(7).level = 2
|
||||
ws2.row(8).level = 1
|
||||
ws2.row(9).level = 1
|
||||
|
||||
ws2.col(1).level = 1
|
||||
ws2.col(2).level = 1
|
||||
ws2.col(3).level = 2
|
||||
ws2.col(4).level = 2
|
||||
ws2.col(5).level = 2
|
||||
ws2.col(6).level = 2
|
||||
ws2.col(7).level = 2
|
||||
ws2.col(8).level = 1
|
||||
ws2.col(9).level = 1
|
||||
|
||||
|
||||
ws0.protect = True
|
||||
ws0.wnd_protect = True
|
||||
ws0.obj_protect = True
|
||||
ws0.scen_protect = True
|
||||
ws0.password = "123456"
|
||||
|
||||
ws1.protect = True
|
||||
ws1.wnd_protect = True
|
||||
ws1.obj_protect = True
|
||||
ws1.scen_protect = True
|
||||
ws1.password = "abcdefghij"
|
||||
|
||||
ws2.protect = True
|
||||
ws2.wnd_protect = True
|
||||
ws2.obj_protect = True
|
||||
ws2.scen_protect = True
|
||||
ws2.password = "ok"
|
||||
|
||||
wb.protect = True
|
||||
wb.wnd_protect = True
|
||||
wb.obj_protect = True
|
||||
wb.save('protection.xls')
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 37 KiB |
@@ -1,17 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
w = Workbook()
|
||||
ws = w.add_sheet('Hey, Dude')
|
||||
|
||||
for i in range(6, 80):
|
||||
fnt = Font()
|
||||
fnt.height = i*20
|
||||
style = XFStyle()
|
||||
style.font = fnt
|
||||
ws.write(i, 1, 'Test')
|
||||
ws.row(i).set_style(style)
|
||||
w.save('row_styles.xls')
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
__rev_id__ = """$Id: row_styles_empty.py 3309 2008-03-14 11:04:30Z chris $"""
|
||||
|
||||
|
||||
from pyExcelerator import *
|
||||
|
||||
w = Workbook()
|
||||
ws = w.add_sheet('Hey, Dude')
|
||||
|
||||
for i in range(6, 80):
|
||||
fnt = Font()
|
||||
fnt.height = i*20
|
||||
style = XFStyle()
|
||||
style.font = fnt
|
||||
ws.row(i).set_style(style)
|
||||
w.save('row_styles_empty.xls')
|
||||
@@ -1,24 +0,0 @@
|
||||
import xlwt
|
||||
from datetime import datetime
|
||||
|
||||
font0 = xlwt.Font()
|
||||
font0.name = 'Times New Roman'
|
||||
font0.colour_index = 2
|
||||
font0.bold = True
|
||||
|
||||
style0 = xlwt.XFStyle()
|
||||
style0.font = font0
|
||||
|
||||
style1 = xlwt.XFStyle()
|
||||
style1.num_format_str = 'D-MMM-YY'
|
||||
|
||||
wb = xlwt.Workbook()
|
||||
ws = wb.add_sheet('A Test Sheet')
|
||||
|
||||
ws.write(0, 0, 'Test', style0)
|
||||
ws.write(1, 0, datetime.now(), style1)
|
||||
ws.write(2, 0, 1)
|
||||
ws.write(2, 1, 1)
|
||||
ws.write(2, 2, xlwt.Formula("A3+B3"))
|
||||
|
||||
wb.save('example.xls')
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
font0 = Formatting.Font()
|
||||
font0.name = 'Arial'
|
||||
font1 = Formatting.Font()
|
||||
font1.name = 'Arial Cyr'
|
||||
font2 = Formatting.Font()
|
||||
font2.name = 'Times New Roman'
|
||||
font3 = Formatting.Font()
|
||||
font3.name = 'Courier New Cyr'
|
||||
|
||||
num_format0 = '0.00000'
|
||||
num_format1 = '0.000000'
|
||||
num_format2 = '0.0000000'
|
||||
num_format3 = '0.00000000'
|
||||
|
||||
st0 = XFStyle()
|
||||
st1 = XFStyle()
|
||||
st2 = XFStyle()
|
||||
st3 = XFStyle()
|
||||
st4 = XFStyle()
|
||||
|
||||
st0.font = font0
|
||||
st0.num_format = num_format0
|
||||
|
||||
st1.font = font1
|
||||
st1.num_format = num_format1
|
||||
|
||||
st2.font = font2
|
||||
st2.num_format = num_format2
|
||||
|
||||
st3.font = font3
|
||||
st3.num_format = num_format3
|
||||
|
||||
wb = Workbook()
|
||||
|
||||
wb.add_style(st0)
|
||||
wb.add_style(st1)
|
||||
wb.add_style(st2)
|
||||
wb.add_style(st3)
|
||||
|
||||
ws0 = wb.add_sheet('0')
|
||||
ws0.write(0, 0, 'Olya'*0x4000, st0)
|
||||
|
||||
#for i in range(0, 0x10):
|
||||
# ws0.write(i, 2, ('%d'%i)*0x4000, st1)
|
||||
|
||||
wb.save('sst.xls')
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
import xlwt
|
||||
|
||||
# Strings passed to (for example) Worksheet.write can be unicode objects,
|
||||
# or str (8-bit) objects, which are then decoded into unicode.
|
||||
# The encoding to be used defaults to 'ascii'. This can be overridden
|
||||
# when the Workbook instance is created:
|
||||
|
||||
book = xlwt.Workbook(encoding='cp1251')
|
||||
sheet = book.add_sheet('cp1251-demo')
|
||||
sheet.write(0, 0, '\xce\xeb\xff')
|
||||
book.save('unicode0.xls')
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
w = Workbook()
|
||||
ws1 = w.add_sheet(u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK SMALL LETTER BETA}\N{GREEK SMALL LETTER GAMMA}')
|
||||
|
||||
ws1.write(0, 0, u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK SMALL LETTER BETA}\N{GREEK SMALL LETTER GAMMA}')
|
||||
ws1.write(1, 1, u'\N{GREEK SMALL LETTER DELTA}x = 1 + \N{GREEK SMALL LETTER DELTA}')
|
||||
|
||||
ws1.write(2,0, u'A\u2262\u0391.') # RFC2152 example
|
||||
ws1.write(3,0, u'Hi Mom -\u263a-!') # RFC2152 example
|
||||
ws1.write(4,0, u'\u65E5\u672C\u8A9E') # RFC2152 example
|
||||
ws1.write(5,0, u'Item 3 is \u00a31.') # RFC2152 example
|
||||
ws1.write(8,0, u'\N{INTEGRAL}') # RFC2152 example
|
||||
|
||||
w.add_sheet(u'A\u2262\u0391.') # RFC2152 example
|
||||
w.add_sheet(u'Hi Mom -\u263a-!') # RFC2152 example
|
||||
one_more_ws = w.add_sheet(u'\u65E5\u672C\u8A9E') # RFC2152 example
|
||||
w.add_sheet(u'Item 3 is \u00a31.') # RFC2152 example
|
||||
|
||||
one_more_ws.write(0, 0, u'\u2665\u2665')
|
||||
|
||||
w.add_sheet(u'\N{GREEK SMALL LETTER ETA WITH TONOS}')
|
||||
w.save('unicode1.xls')
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: windows-1251 -*-
|
||||
# Copyright (C) 2005 Kiseliov Roman
|
||||
|
||||
from xlwt import *
|
||||
|
||||
w = Workbook()
|
||||
ws1 = w.add_sheet(u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK SMALL LETTER BETA}\N{GREEK SMALL LETTER GAMMA}\u2665\u041e\u041b\u042f\u2665')
|
||||
|
||||
fnt = Font()
|
||||
fnt.height = 26*20
|
||||
style = XFStyle()
|
||||
style.font = fnt
|
||||
|
||||
for i in range(0x10000):
|
||||
ws1.write(i/0x10, i%0x10, unichr(i), style)
|
||||
|
||||
w.save('unicode2.xls')
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
props = \
|
||||
[
|
||||
'name',
|
||||
'parent',
|
||||
'rows',
|
||||
'cols',
|
||||
'merged_ranges',
|
||||
'bmp_rec',
|
||||
'show_formulas',
|
||||
'show_grid',
|
||||
'show_headers',
|
||||
'panes_frozen',
|
||||
'show_empty_as_zero',
|
||||
'auto_colour_grid',
|
||||
'cols_right_to_left',
|
||||
'show_outline',
|
||||
'remove_splits',
|
||||
'selected',
|
||||
'hidden',
|
||||
'page_preview',
|
||||
'first_visible_row',
|
||||
'first_visible_col',
|
||||
'grid_colour',
|
||||
'preview_magn',
|
||||
'normal_magn',
|
||||
'row_gut_width',
|
||||
'col_gut_height',
|
||||
'show_auto_page_breaks',
|
||||
'dialogue_sheet',
|
||||
'auto_style_outline',
|
||||
'outline_below',
|
||||
'outline_right',
|
||||
'fit_num_pages',
|
||||
'show_row_outline',
|
||||
'show_col_outline',
|
||||
'alt_expr_eval',
|
||||
'alt_formula_entries',
|
||||
'row_default_height',
|
||||
'col_default_width',
|
||||
'calc_mode',
|
||||
'calc_count',
|
||||
'RC_ref_mode',
|
||||
'iterations_on',
|
||||
'delta',
|
||||
'save_recalc',
|
||||
'print_headers',
|
||||
'print_grid',
|
||||
'grid_set',
|
||||
'vert_page_breaks',
|
||||
'horz_page_breaks',
|
||||
'header_str',
|
||||
'footer_str',
|
||||
'print_centered_vert',
|
||||
'print_centered_horz',
|
||||
'left_margin',
|
||||
'right_margin',
|
||||
'top_margin',
|
||||
'bottom_margin',
|
||||
'paper_size_code',
|
||||
'print_scaling',
|
||||
'start_page_number',
|
||||
'fit_width_to_pages',
|
||||
'fit_height_to_pages',
|
||||
'print_in_rows',
|
||||
'portrait',
|
||||
'print_not_colour',
|
||||
'print_draft',
|
||||
'print_notes',
|
||||
'print_notes_at_end',
|
||||
'print_omit_errors',
|
||||
'print_hres',
|
||||
'print_vres',
|
||||
'header_margin',
|
||||
'footer_margin',
|
||||
'copies_num',
|
||||
]
|
||||
|
||||
from xlwt import *
|
||||
|
||||
wb = Workbook()
|
||||
ws = wb.add_sheet('sheet')
|
||||
|
||||
print ws.name
|
||||
print ws.parent
|
||||
print ws.rows
|
||||
print ws.cols
|
||||
print ws.merged_ranges
|
||||
print ws.bmp_rec
|
||||
print ws.show_formulas
|
||||
print ws.show_grid
|
||||
print ws.show_headers
|
||||
print ws.panes_frozen
|
||||
print ws.show_empty_as_zero
|
||||
print ws.auto_colour_grid
|
||||
print ws.cols_right_to_left
|
||||
print ws.show_outline
|
||||
print ws.remove_splits
|
||||
print ws.selected
|
||||
# print ws.hidden
|
||||
print ws.page_preview
|
||||
print ws.first_visible_row
|
||||
print ws.first_visible_col
|
||||
print ws.grid_colour
|
||||
print ws.preview_magn
|
||||
print ws.normal_magn
|
||||
#print ws.row_gut_width
|
||||
#print ws.col_gut_height
|
||||
print ws.show_auto_page_breaks
|
||||
print ws.dialogue_sheet
|
||||
print ws.auto_style_outline
|
||||
print ws.outline_below
|
||||
print ws.outline_right
|
||||
print ws.fit_num_pages
|
||||
print ws.show_row_outline
|
||||
print ws.show_col_outline
|
||||
print ws.alt_expr_eval
|
||||
print ws.alt_formula_entries
|
||||
print ws.row_default_height
|
||||
print ws.col_default_width
|
||||
print ws.calc_mode
|
||||
print ws.calc_count
|
||||
print ws.RC_ref_mode
|
||||
print ws.iterations_on
|
||||
print ws.delta
|
||||
print ws.save_recalc
|
||||
print ws.print_headers
|
||||
print ws.print_grid
|
||||
#print ws.grid_set
|
||||
print ws.vert_page_breaks
|
||||
print ws.horz_page_breaks
|
||||
print ws.header_str
|
||||
print ws.footer_str
|
||||
print ws.print_centered_vert
|
||||
print ws.print_centered_horz
|
||||
print ws.left_margin
|
||||
print ws.right_margin
|
||||
print ws.top_margin
|
||||
print ws.bottom_margin
|
||||
print ws.paper_size_code
|
||||
print ws.print_scaling
|
||||
print ws.start_page_number
|
||||
print ws.fit_width_to_pages
|
||||
print ws.fit_height_to_pages
|
||||
print ws.print_in_rows
|
||||
print ws.portrait
|
||||
print ws.print_colour
|
||||
print ws.print_draft
|
||||
print ws.print_notes
|
||||
print ws.print_notes_at_end
|
||||
print ws.print_omit_errors
|
||||
print ws.print_hres
|
||||
print ws.print_vres
|
||||
print ws.header_margin
|
||||
print ws.footer_margin
|
||||
print ws.copies_num
|
||||
@@ -1,46 +0,0 @@
|
||||
|
||||
# Write an XLS file with a single worksheet, containing
|
||||
# a heading row and some rows of data.
|
||||
|
||||
import xlwt
|
||||
import datetime
|
||||
ezxf = xlwt.easyxf
|
||||
|
||||
def write_xls(file_name, sheet_name, headings, data, heading_xf, data_xfs):
|
||||
book = xlwt.Workbook()
|
||||
sheet = book.add_sheet(sheet_name)
|
||||
rowx = 0
|
||||
for colx, value in enumerate(headings):
|
||||
sheet.write(rowx, colx, value, heading_xf)
|
||||
sheet.set_panes_frozen(True) # frozen headings instead of split panes
|
||||
sheet.set_horz_split_pos(rowx+1) # in general, freeze after last heading row
|
||||
sheet.set_remove_splits(True) # if user does unfreeze, don't leave a split there
|
||||
for row in data:
|
||||
rowx += 1
|
||||
for colx, value in enumerate(row):
|
||||
sheet.write(rowx, colx, value, data_xfs[colx])
|
||||
book.save(file_name)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
mkd = datetime.date
|
||||
hdngs = ['Date', 'Stock Code', 'Quantity', 'Unit Price', 'Value', 'Message']
|
||||
kinds = 'date text int price money text'.split()
|
||||
data = [
|
||||
[mkd(2007, 7, 1), 'ABC', 1000, 1.234567, 1234.57, ''],
|
||||
[mkd(2007, 12, 31), 'XYZ', -100, 4.654321, -465.43, 'Goods returned'],
|
||||
] + [
|
||||
[mkd(2008, 6, 30), 'PQRCD', 100, 2.345678, 234.57, ''],
|
||||
] * 100
|
||||
|
||||
heading_xf = ezxf('font: bold on; align: wrap on, vert centre, horiz center')
|
||||
kind_to_xf_map = {
|
||||
'date': ezxf(num_format_str='yyyy-mm-dd'),
|
||||
'int': ezxf(num_format_str='#,##0'),
|
||||
'money': ezxf('font: italic on; pattern: pattern solid, fore-colour grey25',
|
||||
num_format_str='$#,##0.00'),
|
||||
'price': ezxf(num_format_str='#0.000000'),
|
||||
'text': ezxf(),
|
||||
}
|
||||
data_xfs = [kind_to_xf_map[k] for k in kinds]
|
||||
write_xls('xlwt_easyxf_simple_demo.xls', 'Demo', hdngs, data, heading_xf, data_xfs)
|
||||
@@ -1,374 +0,0 @@
|
||||
header {
|
||||
import struct
|
||||
import Utils
|
||||
from UnicodeUtils import upack1
|
||||
from ExcelMagic import *
|
||||
|
||||
_RVAdelta = {"R": 0, "V": 0x20, "A": 0x40}
|
||||
_RVAdeltaRef = {"R": 0, "V": 0x20, "A": 0x40, "D": 0x20}
|
||||
_RVAdeltaArea = {"R": 0, "V": 0x20, "A": 0x40, "D": 0}
|
||||
|
||||
|
||||
class FormulaParseException(Exception):
|
||||
"""
|
||||
An exception indicating that a Formula could not be successfully parsed.
|
||||
"""
|
||||
}
|
||||
|
||||
header "ExcelFormulaParser.__init__" {
|
||||
self.rpn = ""
|
||||
self.sheet_references = []
|
||||
self.xcall_references = []
|
||||
}
|
||||
|
||||
options {
|
||||
language = "Python";
|
||||
}
|
||||
|
||||
class ExcelFormulaParser extends Parser;
|
||||
options {
|
||||
k = 2;
|
||||
defaultErrorHandler = false;
|
||||
buildAST = false;
|
||||
}
|
||||
|
||||
|
||||
tokens {
|
||||
TRUE_CONST;
|
||||
FALSE_CONST;
|
||||
STR_CONST;
|
||||
NUM_CONST;
|
||||
INT_CONST;
|
||||
|
||||
FUNC_IF;
|
||||
FUNC_CHOOSE;
|
||||
NAME;
|
||||
QUOTENAME;
|
||||
|
||||
EQ;
|
||||
NE;
|
||||
GT;
|
||||
LT;
|
||||
GE;
|
||||
LE;
|
||||
|
||||
ADD;
|
||||
SUB;
|
||||
MUL;
|
||||
DIV;
|
||||
|
||||
POWER;
|
||||
PERCENT;
|
||||
|
||||
LP;
|
||||
RP;
|
||||
|
||||
LB;
|
||||
RB;
|
||||
|
||||
COLON;
|
||||
COMMA;
|
||||
SEMICOLON;
|
||||
REF2D;
|
||||
REF2D_R1C1;
|
||||
BANG;
|
||||
}
|
||||
|
||||
formula
|
||||
: expr["V"]
|
||||
;
|
||||
|
||||
expr[arg_type]
|
||||
: // {print "\n**expr %s" % arg_type}
|
||||
prec0_expr[arg_type]
|
||||
(
|
||||
(
|
||||
EQ { op = struct.pack('B', ptgEQ) }
|
||||
| NE { op = struct.pack('B', ptgNE) }
|
||||
| GT { op = struct.pack('B', ptgGT) }
|
||||
| LT { op = struct.pack('B', ptgLT) }
|
||||
| GE { op = struct.pack('B', ptgGE) }
|
||||
| LE { op = struct.pack('B', ptgLE) }
|
||||
)
|
||||
prec0_expr[arg_type] { self.rpn += op }
|
||||
)*
|
||||
;
|
||||
|
||||
prec0_expr[arg_type]
|
||||
: prec1_expr[arg_type]
|
||||
(
|
||||
(
|
||||
CONCAT { op = struct.pack('B', ptgConcat) }
|
||||
)
|
||||
prec1_expr[arg_type] { self.rpn += op }
|
||||
)*
|
||||
;
|
||||
|
||||
prec1_expr[arg_type]
|
||||
: // {print "**prec1_expr1 %s" % arg_type}
|
||||
prec2_expr[arg_type]
|
||||
// {print "**prec1_expr2 %s" % arg_type}
|
||||
(
|
||||
(
|
||||
ADD { op = struct.pack('B', ptgAdd) }
|
||||
| SUB { op = struct.pack('B', ptgSub) }
|
||||
)
|
||||
// {print "**prec1_expr3 %s" % arg_type}
|
||||
prec2_expr[arg_type]
|
||||
{ self.rpn += op;
|
||||
// print "**prec1_expr4 %s" % arg_type
|
||||
}
|
||||
)*
|
||||
;
|
||||
|
||||
|
||||
prec2_expr[arg_type]
|
||||
: prec3_expr[arg_type]
|
||||
(
|
||||
(
|
||||
MUL { op = struct.pack('B', ptgMul) }
|
||||
| DIV { op = struct.pack('B', ptgDiv) }
|
||||
)
|
||||
prec3_expr[arg_type] { self.rpn += op }
|
||||
)*
|
||||
;
|
||||
|
||||
prec3_expr[arg_type]
|
||||
: prec4_expr[arg_type]
|
||||
(
|
||||
(
|
||||
POWER { op = struct.pack('B', ptgPower) }
|
||||
)
|
||||
prec4_expr[arg_type] { self.rpn += op }
|
||||
)*
|
||||
;
|
||||
|
||||
prec4_expr[arg_type]
|
||||
: prec5_expr[arg_type]
|
||||
(
|
||||
PERCENT { self.rpn += struct.pack('B', ptgPercent) }
|
||||
)?
|
||||
;
|
||||
|
||||
prec5_expr[arg_type]
|
||||
: primary[arg_type]
|
||||
| SUB primary[arg_type] { self.rpn += struct.pack('B', ptgUminus) }
|
||||
;
|
||||
|
||||
primary[arg_type]
|
||||
: TRUE_CONST
|
||||
{
|
||||
self.rpn += struct.pack("2B", ptgBool, 1)
|
||||
}
|
||||
| FALSE_CONST
|
||||
{
|
||||
self.rpn += struct.pack("2B", ptgBool, 0)
|
||||
}
|
||||
| str_tok:STR_CONST
|
||||
{
|
||||
self.rpn += struct.pack("B", ptgStr) + upack1(str_tok.text[1:-1].replace("\"\"", "\""))
|
||||
}
|
||||
| int_tok:INT_CONST
|
||||
{
|
||||
// print "**int_const", int_tok.text
|
||||
int_value = int(int_tok.text)
|
||||
if int_value <= 65535:
|
||||
self.rpn += struct.pack("<BH", ptgInt, int_value)
|
||||
else:
|
||||
self.rpn += struct.pack("<Bd", ptgNum, float(int_value))
|
||||
}
|
||||
| num_tok:NUM_CONST
|
||||
{
|
||||
self.rpn += struct.pack("<Bd", ptgNum, float(num_tok.text))
|
||||
}
|
||||
| ref2d_tok:REF2D
|
||||
{
|
||||
// print "**ref2d %s %s" % (ref2d_tok.text, arg_type)
|
||||
r, c = Utils.cell_to_packed_rowcol(ref2d_tok.text)
|
||||
ptg = ptgRefR + _RVAdeltaRef[arg_type]
|
||||
self.rpn += struct.pack("<B2H", ptg, r, c)
|
||||
}
|
||||
| ref2d1_tok:REF2D COLON ref2d2_tok:REF2D
|
||||
{
|
||||
r1, c1 = Utils.cell_to_packed_rowcol(ref2d1_tok.text)
|
||||
r2, c2 = Utils.cell_to_packed_rowcol(ref2d2_tok.text)
|
||||
ptg = ptgAreaR + _RVAdeltaArea[arg_type]
|
||||
self.rpn += struct.pack("<B4H", ptg, r1, r2, c1, c2)
|
||||
}
|
||||
| sheet1 = sheet
|
||||
{
|
||||
sheet2 = sheet1
|
||||
}
|
||||
( COLON sheet2 = sheet )? BANG ref3d_ref2d: REF2D
|
||||
{
|
||||
ptg = ptgRef3dR + _RVAdeltaRef[arg_type]
|
||||
rpn_ref2d = ""
|
||||
r1, c1 = Utils.cell_to_packed_rowcol(ref3d_ref2d.text)
|
||||
rpn_ref2d = struct.pack("<3H", 0x0000, r1, c1)
|
||||
}
|
||||
( COLON ref3d_ref2d2: REF2D
|
||||
{
|
||||
ptg = ptgArea3dR + _RVAdeltaArea[arg_type]
|
||||
r2, c2 = Utils.cell_to_packed_rowcol(ref3d_ref2d2.text)
|
||||
rpn_ref2d = struct.pack("<5H", 0x0000, r1, r2, c1, c2)
|
||||
}
|
||||
)?
|
||||
{
|
||||
self.rpn += struct.pack("<B", ptg)
|
||||
self.sheet_references.append((sheet1, sheet2, len(self.rpn)))
|
||||
self.rpn += rpn_ref2d
|
||||
}
|
||||
| FUNC_IF
|
||||
LP expr["V"] (SEMICOLON | COMMA)
|
||||
{
|
||||
self.rpn += struct.pack("<BBH", ptgAttr, 0x02, 0) // tAttrIf
|
||||
pos0 = len(self.rpn) - 2
|
||||
}
|
||||
expr[arg_type] (SEMICOLON | COMMA)
|
||||
{
|
||||
self.rpn += struct.pack("<BBH", ptgAttr, 0x08, 0) // tAttrSkip
|
||||
pos1 = len(self.rpn) - 2
|
||||
self.rpn = self.rpn[:pos0] + struct.pack("<H", pos1-pos0) + self.rpn[pos0+2:]
|
||||
}
|
||||
expr[arg_type] RP
|
||||
{
|
||||
self.rpn += struct.pack("<BBH", ptgAttr, 0x08, 3) // tAttrSkip
|
||||
self.rpn += struct.pack("<BBH", ptgFuncVarR, 3, 1) // 3 = nargs, 1 = IF func
|
||||
pos2 = len(self.rpn)
|
||||
self.rpn = self.rpn[:pos1] + struct.pack("<H", pos2-(pos1+2)-1) + self.rpn[pos1+2:]
|
||||
}
|
||||
| FUNC_CHOOSE
|
||||
{
|
||||
arg_type = "R"
|
||||
rpn_chunks = []
|
||||
}
|
||||
LP expr["V"] // first argument (the selector)
|
||||
{
|
||||
rpn_start = len(self.rpn)
|
||||
ref_markers = [len(self.sheet_references)]
|
||||
}
|
||||
(
|
||||
(SEMICOLON | COMMA)
|
||||
{ mark = len(self.rpn) }
|
||||
(
|
||||
expr[arg_type]
|
||||
| { self.rpn += struct.pack("B", ptgMissArg) }
|
||||
)
|
||||
{
|
||||
rpn_chunks.append(self.rpn[mark:])
|
||||
ref_markers.append(len(self.sheet_references))
|
||||
}
|
||||
)*
|
||||
RP
|
||||
{
|
||||
self.rpn = self.rpn[:rpn_start]
|
||||
nc = len(rpn_chunks)
|
||||
chunklens = [len(chunk) for chunk in rpn_chunks]
|
||||
skiplens = [0] * nc
|
||||
skiplens[-1] = 3
|
||||
for ic in xrange(nc-1, 0, -1):
|
||||
skiplens[ic-1] = skiplens[ic] + chunklens[ic] + 4
|
||||
jump_pos = [2 * nc + 2]
|
||||
for ic in xrange(nc):
|
||||
jump_pos.append(jump_pos[-1] + chunklens[ic] + 4)
|
||||
chunk_shift = 2 * nc + 6 // size of tAttrChoose
|
||||
for ic in xrange(nc):
|
||||
for refx in xrange(ref_markers[ic], ref_markers[ic+1]):
|
||||
ref = self.sheet_references[refx]
|
||||
self.sheet_references[refx] = (ref[0], ref[1], ref[2] + chunk_shift)
|
||||
chunk_shift += 4 // size of tAttrSkip
|
||||
choose_rpn = []
|
||||
choose_rpn.append(struct.pack("<BBH", ptgAttr, 0x04, nc)) // 0x04 is tAttrChoose
|
||||
choose_rpn.append(struct.pack("<%dH" % (nc+1), *jump_pos))
|
||||
for ic in xrange(nc):
|
||||
choose_rpn.append(rpn_chunks[ic])
|
||||
choose_rpn.append(struct.pack("<BBH", ptgAttr, 0x08, skiplens[ic])) // 0x08 is tAttrSkip
|
||||
choose_rpn.append(struct.pack("<BBH", ptgFuncVarV, nc+1, 100)) // 100 is CHOOSE fn
|
||||
self.rpn += "".join(choose_rpn)
|
||||
}
|
||||
| name_tok:NAME
|
||||
{
|
||||
raise Exception("[formula] found unexpected NAME token (%r)" % name_tok.txt)
|
||||
// #### TODO: handle references to defined names here
|
||||
}
|
||||
| func_tok:NAME
|
||||
{
|
||||
func_toku = func_tok.text.upper()
|
||||
if func_toku in all_funcs_by_name:
|
||||
(opcode,
|
||||
min_argc,
|
||||
max_argc,
|
||||
func_type,
|
||||
arg_type_str) = all_funcs_by_name[func_toku]
|
||||
arg_type_list = list(arg_type_str)
|
||||
else:
|
||||
raise Exception("[formula] unknown function (%s)" % func_tok.text)
|
||||
// print "**func_tok1 %s %s" % (func_toku, func_type)
|
||||
xcall = opcode < 0
|
||||
if xcall:
|
||||
// The name of the add-in function is passed as the 1st arg
|
||||
// of the hidden XCALL function
|
||||
self.xcall_references.append((func_toku, len(self.rpn) + 1))
|
||||
self.rpn += struct.pack("<BHHH",
|
||||
ptgNameXR,
|
||||
0xadde, // ##PATCHME## index to REF entry in EXTERNSHEET record
|
||||
0xefbe, // ##PATCHME## one-based index to EXTERNNAME record
|
||||
0x0000) // unused
|
||||
}
|
||||
LP arg_count = expr_list[arg_type_list, min_argc, max_argc] RP
|
||||
{
|
||||
if arg_count > max_argc or arg_count < min_argc:
|
||||
raise Exception, "%d parameters for function: %s" % (arg_count, func_tok.text)
|
||||
if xcall:
|
||||
func_ptg = ptgFuncVarR + _RVAdelta[func_type]
|
||||
self.rpn += struct.pack("<2BH", func_ptg, arg_count + 1, 255) // 255 is magic XCALL function
|
||||
elif min_argc == max_argc:
|
||||
func_ptg = ptgFuncR + _RVAdelta[func_type]
|
||||
self.rpn += struct.pack("<BH", func_ptg, opcode)
|
||||
elif arg_count == 1 and func_tok.text.upper() == "SUM":
|
||||
self.rpn += struct.pack("<BBH", ptgAttr, 0x10, 0) // tAttrSum
|
||||
else:
|
||||
func_ptg = ptgFuncVarR + _RVAdelta[func_type]
|
||||
self.rpn += struct.pack("<2BH", func_ptg, arg_count, opcode)
|
||||
}
|
||||
| LP expr[arg_type] RP
|
||||
{
|
||||
self.rpn += struct.pack("B", ptgParen)
|
||||
}
|
||||
;
|
||||
|
||||
expr_list[arg_type_list, min_argc, max_argc] returns [arg_cnt]
|
||||
{
|
||||
arg_cnt = 0
|
||||
arg_type = arg_type_list[arg_cnt]
|
||||
// print "**expr_list1[%d] req=%s" % (arg_cnt, arg_type)
|
||||
}
|
||||
: expr[arg_type] { arg_cnt += 1 }
|
||||
(
|
||||
{
|
||||
if arg_cnt < len(arg_type_list):
|
||||
arg_type = arg_type_list[arg_cnt]
|
||||
else:
|
||||
arg_type = arg_type_list[-1]
|
||||
if arg_type == "+":
|
||||
arg_type = arg_type_list[-2]
|
||||
// print "**expr_list2[%d] req=%s" % (arg_cnt, arg_type)
|
||||
}
|
||||
(SEMICOLON | COMMA)
|
||||
(
|
||||
expr[arg_type]
|
||||
| { self.rpn += struct.pack("B", ptgMissArg) }
|
||||
)
|
||||
{ arg_cnt += 1 }
|
||||
)*
|
||||
|
|
||||
;
|
||||
|
||||
sheet returns[ref]
|
||||
: sheet_ref_name: NAME
|
||||
{ ref = sheet_ref_name.text }
|
||||
| sheet_ref_int: INT_CONST
|
||||
{ ref = sheet_ref_int.text }
|
||||
| sheet_ref_quote: QUOTENAME
|
||||
{ ref = sheet_ref_quote.text[1:-1].replace("''", "'") }
|
||||
;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,258 +0,0 @@
|
||||
# Portions are Copyright (C) 2005 Roman V. Kiseliov
|
||||
# Portions are Copyright (c) 2004 Evgeny Filatov <fufff@users.sourceforge.net>
|
||||
# Portions are Copyright (c) 2002-2004 John McNamara (Perl Spreadsheet::WriteExcel)
|
||||
|
||||
from .BIFFRecords import BiffRecord
|
||||
from struct import *
|
||||
|
||||
|
||||
def _size_col(sheet, col):
|
||||
return sheet.col_width(col)
|
||||
|
||||
|
||||
def _size_row(sheet, row):
|
||||
return sheet.row_height(row)
|
||||
|
||||
|
||||
def _position_image(sheet, row_start, col_start, x1, y1, width, height):
|
||||
"""Calculate the vertices that define the position of the image as required by
|
||||
the OBJ record.
|
||||
|
||||
+------------+------------+
|
||||
| A | B |
|
||||
+-----+------------+------------+
|
||||
| |(x1,y1) | |
|
||||
| 1 |(A1)._______|______ |
|
||||
| | | | |
|
||||
| | | | |
|
||||
+-----+----| BITMAP |-----+
|
||||
| | | | |
|
||||
| 2 | |______________. |
|
||||
| | | (B2)|
|
||||
| | | (x2,y2)|
|
||||
+---- +------------+------------+
|
||||
|
||||
Example of a bitmap that covers some of the area from cell A1 to cell B2.
|
||||
|
||||
Based on the width and height of the bitmap we need to calculate 8 vars:
|
||||
col_start, row_start, col_end, row_end, x1, y1, x2, y2.
|
||||
The width and height of the cells are also variable and have to be taken into
|
||||
account.
|
||||
The values of col_start and row_start are passed in from the calling
|
||||
function. The values of col_end and row_end are calculated by subtracting
|
||||
the width and height of the bitmap from the width and height of the
|
||||
underlying cells.
|
||||
The vertices are expressed as a percentage of the underlying cell width as
|
||||
follows (rhs values are in pixels):
|
||||
|
||||
x1 = X / W *1024
|
||||
y1 = Y / H *256
|
||||
x2 = (X-1) / W *1024
|
||||
y2 = (Y-1) / H *256
|
||||
|
||||
Where: X is distance from the left side of the underlying cell
|
||||
Y is distance from the top of the underlying cell
|
||||
W is the width of the cell
|
||||
H is the height of the cell
|
||||
|
||||
Note: the SDK incorrectly states that the height should be expressed as a
|
||||
percentage of 1024.
|
||||
|
||||
col_start - Col containing upper left corner of object
|
||||
row_start - Row containing top left corner of object
|
||||
x1 - Distance to left side of object
|
||||
y1 - Distance to top of object
|
||||
width - Width of image frame
|
||||
height - Height of image frame
|
||||
|
||||
"""
|
||||
# Adjust start column for offsets that are greater than the col width
|
||||
while x1 >= _size_col(sheet, col_start):
|
||||
x1 -= _size_col(sheet, col_start)
|
||||
col_start += 1
|
||||
# Adjust start row for offsets that are greater than the row height
|
||||
while y1 >= _size_row(sheet, row_start):
|
||||
y1 -= _size_row(sheet, row_start)
|
||||
row_start += 1
|
||||
# Initialise end cell to the same as the start cell
|
||||
row_end = row_start # Row containing bottom right corner of object
|
||||
col_end = col_start # Col containing lower right corner of object
|
||||
width = width + x1 - 1
|
||||
height = height + y1 - 1
|
||||
# Subtract the underlying cell widths to find the end cell of the image
|
||||
while (width >= _size_col(sheet, col_end)):
|
||||
width -= _size_col(sheet, col_end)
|
||||
col_end += 1
|
||||
# Subtract the underlying cell heights to find the end cell of the image
|
||||
while (height >= _size_row(sheet, row_end)):
|
||||
height -= _size_row(sheet, row_end)
|
||||
row_end += 1
|
||||
# Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
|
||||
# with zero height or width.
|
||||
if ((_size_col(sheet, col_start) == 0) or (_size_col(sheet, col_end) == 0)
|
||||
or (_size_row(sheet, row_start) == 0) or (_size_row(sheet, row_end) == 0)):
|
||||
return
|
||||
# Convert the pixel values to the percentage value expected by Excel
|
||||
x1 = int(float(x1) / _size_col(sheet, col_start) * 1024)
|
||||
y1 = int(float(y1) / _size_row(sheet, row_start) * 256)
|
||||
# Distance to right side of object
|
||||
x2 = int(float(width) / _size_col(sheet, col_end) * 1024)
|
||||
# Distance to bottom of object
|
||||
y2 = int(float(height) / _size_row(sheet, row_end) * 256)
|
||||
return (col_start, x1, row_start, y1, col_end, x2, row_end, y2)
|
||||
|
||||
|
||||
class ObjBmpRecord(BiffRecord):
|
||||
_REC_ID = 0x005D # Record identifier
|
||||
|
||||
def __init__(self, row, col, sheet, im_data_bmp, x, y, scale_x, scale_y):
|
||||
# Scale the frame of the image.
|
||||
width = im_data_bmp.width * scale_x
|
||||
height = im_data_bmp.height * scale_y
|
||||
|
||||
# Calculate the vertices of the image and write the OBJ record
|
||||
coordinates = _position_image(sheet, row, col, x, y, width, height)
|
||||
# print coordinates
|
||||
col_start, x1, row_start, y1, col_end, x2, row_end, y2 = coordinates
|
||||
|
||||
"""Store the OBJ record that precedes an IMDATA record. This could be generalise
|
||||
to support other Excel objects.
|
||||
|
||||
"""
|
||||
cObj = 0x0001 # Count of objects in file (set to 1)
|
||||
OT = 0x0008 # Object type. 8 = Picture
|
||||
id = 0x0001 # Object ID
|
||||
grbit = 0x0614 # Option flags
|
||||
colL = col_start # Col containing upper left corner of object
|
||||
dxL = x1 # Distance from left side of cell
|
||||
rwT = row_start # Row containing top left corner of object
|
||||
dyT = y1 # Distance from top of cell
|
||||
colR = col_end # Col containing lower right corner of object
|
||||
dxR = x2 # Distance from right of cell
|
||||
rwB = row_end # Row containing bottom right corner of object
|
||||
dyB = y2 # Distance from bottom of cell
|
||||
cbMacro = 0x0000 # Length of FMLA structure
|
||||
Reserved1 = 0x0000 # Reserved
|
||||
Reserved2 = 0x0000 # Reserved
|
||||
icvBack = 0x09 # Background colour
|
||||
icvFore = 0x09 # Foreground colour
|
||||
fls = 0x00 # Fill pattern
|
||||
fAuto = 0x00 # Automatic fill
|
||||
icv = 0x08 # Line colour
|
||||
lns = 0xff # Line style
|
||||
lnw = 0x01 # Line weight
|
||||
fAutoB = 0x00 # Automatic border
|
||||
frs = 0x0000 # Frame style
|
||||
cf = 0x0009 # Image format, 9 = bitmap
|
||||
Reserved3 = 0x0000 # Reserved
|
||||
cbPictFmla = 0x0000 # Length of FMLA structure
|
||||
Reserved4 = 0x0000 # Reserved
|
||||
grbit2 = 0x0001 # Option flags
|
||||
Reserved5 = 0x0000 # Reserved
|
||||
|
||||
data = pack("<L", cObj)
|
||||
data += pack("<H", OT)
|
||||
data += pack("<H", id)
|
||||
data += pack("<H", grbit)
|
||||
data += pack("<H", colL)
|
||||
data += pack("<H", dxL)
|
||||
data += pack("<H", rwT)
|
||||
data += pack("<H", dyT)
|
||||
data += pack("<H", colR)
|
||||
data += pack("<H", dxR)
|
||||
data += pack("<H", rwB)
|
||||
data += pack("<H", dyB)
|
||||
data += pack("<H", cbMacro)
|
||||
data += pack("<L", Reserved1)
|
||||
data += pack("<H", Reserved2)
|
||||
data += pack("<B", icvBack)
|
||||
data += pack("<B", icvFore)
|
||||
data += pack("<B", fls)
|
||||
data += pack("<B", fAuto)
|
||||
data += pack("<B", icv)
|
||||
data += pack("<B", lns)
|
||||
data += pack("<B", lnw)
|
||||
data += pack("<B", fAutoB)
|
||||
data += pack("<H", frs)
|
||||
data += pack("<L", cf)
|
||||
data += pack("<H", Reserved3)
|
||||
data += pack("<H", cbPictFmla)
|
||||
data += pack("<H", Reserved4)
|
||||
data += pack("<H", grbit2)
|
||||
data += pack("<L", Reserved5)
|
||||
|
||||
self._rec_data = data
|
||||
|
||||
def _process_bitmap(bitmap):
|
||||
"""Convert a 24 bit bitmap into the modified internal format used by Windows.
|
||||
This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the
|
||||
MSDN library.
|
||||
|
||||
"""
|
||||
# Open file and binmode the data in case the platform needs it.
|
||||
fh = open(bitmap, 'rb')
|
||||
try:
|
||||
# Slurp the file into a string.
|
||||
data = fh.read()
|
||||
finally:
|
||||
fh.close()
|
||||
# Check that the file is big enough to be a bitmap.
|
||||
if len(data) <= 0x36:
|
||||
raise Exception("bitmap doesn't contain enough data.")
|
||||
# The first 2 bytes are used to identify the bitmap.
|
||||
if (data[:2] != b"BM"):
|
||||
raise Exception("bitmap doesn't appear to to be a valid bitmap image.")
|
||||
# Remove bitmap data: ID.
|
||||
data = data[2:]
|
||||
# Read and remove the bitmap size. This is more reliable than reading
|
||||
# the data size at offset 0x22.
|
||||
#
|
||||
size = unpack("<L", data[:4])[0]
|
||||
size -= 0x36 # Subtract size of bitmap header.
|
||||
size += 0x0C # Add size of BIFF header.
|
||||
data = data[4:]
|
||||
# Remove bitmap data: reserved, offset, header length.
|
||||
data = data[12:]
|
||||
# Read and remove the bitmap width and height. Verify the sizes.
|
||||
width, height = unpack("<LL", data[:8])
|
||||
data = data[8:]
|
||||
if (width > 0xFFFF):
|
||||
raise Exception("bitmap: largest image width supported is 65k.")
|
||||
if (height > 0xFFFF):
|
||||
raise Exception("bitmap: largest image height supported is 65k.")
|
||||
# Read and remove the bitmap planes and bpp data. Verify them.
|
||||
planes, bitcount = unpack("<HH", data[:4])
|
||||
data = data[4:]
|
||||
if (bitcount != 24):
|
||||
raise Exception("bitmap isn't a 24bit true color bitmap.")
|
||||
if (planes != 1):
|
||||
raise Exception("bitmap: only 1 plane supported in bitmap image.")
|
||||
# Read and remove the bitmap compression. Verify compression.
|
||||
compression = unpack("<L", data[:4])[0]
|
||||
data = data[4:]
|
||||
if (compression != 0):
|
||||
raise Exception("bitmap: compression not supported in bitmap image.")
|
||||
# Remove bitmap data: data size, hres, vres, colours, imp. colours.
|
||||
data = data[20:]
|
||||
# Add the BITMAPCOREHEADER data
|
||||
header = pack("<LHHHH", 0x000c, width, height, 0x01, 0x18)
|
||||
data = header + data
|
||||
return (width, height, size, data)
|
||||
|
||||
|
||||
class ImDataBmpRecord(BiffRecord):
|
||||
_REC_ID = 0x007F
|
||||
|
||||
def __init__(self, filename):
|
||||
"""Insert a 24bit bitmap image in a worksheet. The main record required is
|
||||
IMDATA but it must be proceeded by a OBJ record to define its position.
|
||||
|
||||
"""
|
||||
BiffRecord.__init__(self)
|
||||
|
||||
self.width, self.height, self.size, data = _process_bitmap(filename)
|
||||
# Write the IMDATA record to store the bitmap data
|
||||
cf = 0x09
|
||||
env = 0x01
|
||||
lcb = self.size
|
||||
self._rec_data = pack("<HHL", cf, env, lcb) + data
|
||||
@@ -1,233 +0,0 @@
|
||||
from struct import unpack, pack
|
||||
from . import BIFFRecords
|
||||
|
||||
class StrCell(object):
|
||||
__slots__ = ["rowx", "colx", "xf_idx", "sst_idx"]
|
||||
|
||||
def __init__(self, rowx, colx, xf_idx, sst_idx):
|
||||
self.rowx = rowx
|
||||
self.colx = colx
|
||||
self.xf_idx = xf_idx
|
||||
self.sst_idx = sst_idx
|
||||
|
||||
def get_biff_data(self):
|
||||
return pack('<5HL', 0x00FD, 10, self.rowx, self.colx, self.xf_idx, self.sst_idx)
|
||||
|
||||
class BlankCell(object):
|
||||
__slots__ = ["rowx", "colx", "xf_idx"]
|
||||
|
||||
def __init__(self, rowx, colx, xf_idx):
|
||||
self.rowx = rowx
|
||||
self.colx = colx
|
||||
self.xf_idx = xf_idx
|
||||
|
||||
def get_biff_data(self):
|
||||
return pack('<5H', 0x0201, 6, self.rowx, self.colx, self.xf_idx)
|
||||
|
||||
class MulBlankCell(object):
|
||||
__slots__ = ["rowx", "colx1", "colx2", "xf_idx"]
|
||||
|
||||
def __init__(self, rowx, colx1, colx2, xf_idx):
|
||||
self.rowx = rowx
|
||||
self.colx1 = colx1
|
||||
self.colx2 = colx2
|
||||
self.xf_idx = xf_idx
|
||||
|
||||
def get_biff_data(self):
|
||||
return BIFFRecords.MulBlankRecord(self.rowx,
|
||||
self.colx1, self.colx2, self.xf_idx).get()
|
||||
|
||||
class NumberCell(object):
|
||||
__slots__ = ["rowx", "colx", "xf_idx", "number"]
|
||||
|
||||
def __init__(self, rowx, colx, xf_idx, number):
|
||||
self.rowx = rowx
|
||||
self.colx = colx
|
||||
self.xf_idx = xf_idx
|
||||
self.number = float(number)
|
||||
|
||||
def get_encoded_data(self):
|
||||
rk_encoded = 0
|
||||
num = self.number
|
||||
|
||||
# The four possible kinds of RK encoding are *not* mutually exclusive.
|
||||
# The 30-bit integer variety picks up the most.
|
||||
# In the code below, the four varieties are checked in descending order
|
||||
# of bangs per buck, or not at all.
|
||||
# SJM 2007-10-01
|
||||
|
||||
if -0x20000000 <= num < 0x20000000: # fits in 30-bit *signed* int
|
||||
inum = int(num)
|
||||
if inum == num: # survives round-trip
|
||||
rk_encoded = 2 | (inum << 2)
|
||||
return 1, rk_encoded
|
||||
|
||||
temp = num * 100
|
||||
|
||||
if -0x20000000 <= temp < 0x20000000:
|
||||
# That was step 1: the coded value will fit in
|
||||
# a 30-bit signed integer.
|
||||
itemp = int(round(temp, 0))
|
||||
# That was step 2: "itemp" is the best candidate coded value.
|
||||
# Now for step 3: simulate the decoding,
|
||||
# to check for round-trip correctness.
|
||||
if itemp / 100.0 == num:
|
||||
rk_encoded = 3 | (itemp << 2)
|
||||
return 1, rk_encoded
|
||||
|
||||
if 0: # Cost of extra pack+unpack not justified by tiny yield.
|
||||
packed = pack('<d', num)
|
||||
w01, w23 = unpack('<2i', packed)
|
||||
if not w01 and not(w23 & 3):
|
||||
return 1, w23
|
||||
|
||||
packed100 = pack('<d', temp)
|
||||
w01, w23 = unpack('<2i', packed100)
|
||||
if not w01 and not(w23 & 3):
|
||||
return 1, w23 | 1
|
||||
|
||||
return 0, pack('<5Hd', 0x0203, 14, self.rowx, self.colx, self.xf_idx, num)
|
||||
|
||||
def get_biff_data(self):
|
||||
isRK, value = self.get_encoded_data()
|
||||
if isRK:
|
||||
return pack('<5Hi', 0x27E, 10, self.rowx, self.colx, self.xf_idx, value)
|
||||
return value # NUMBER record already packed
|
||||
|
||||
class BooleanCell(object):
|
||||
__slots__ = ["rowx", "colx", "xf_idx", "number"]
|
||||
|
||||
def __init__(self, rowx, colx, xf_idx, number):
|
||||
self.rowx = rowx
|
||||
self.colx = colx
|
||||
self.xf_idx = xf_idx
|
||||
self.number = number
|
||||
|
||||
def get_biff_data(self):
|
||||
return BIFFRecords.BoolErrRecord(self.rowx,
|
||||
self.colx, self.xf_idx, self.number, 0).get()
|
||||
|
||||
error_code_map = {
|
||||
0x00: 0, # Intersection of two cell ranges is empty
|
||||
0x07: 7, # Division by zero
|
||||
0x0F: 15, # Wrong type of operand
|
||||
0x17: 23, # Illegal or deleted cell reference
|
||||
0x1D: 29, # Wrong function or range name
|
||||
0x24: 36, # Value range overflow
|
||||
0x2A: 42, # Argument or function not available
|
||||
'#NULL!' : 0, # Intersection of two cell ranges is empty
|
||||
'#DIV/0!': 7, # Division by zero
|
||||
'#VALUE!': 36, # Wrong type of operand
|
||||
'#REF!' : 23, # Illegal or deleted cell reference
|
||||
'#NAME?' : 29, # Wrong function or range name
|
||||
'#NUM!' : 36, # Value range overflow
|
||||
'#N/A!' : 42, # Argument or function not available
|
||||
}
|
||||
|
||||
class ErrorCell(object):
|
||||
__slots__ = ["rowx", "colx", "xf_idx", "number"]
|
||||
|
||||
def __init__(self, rowx, colx, xf_idx, error_string_or_code):
|
||||
self.rowx = rowx
|
||||
self.colx = colx
|
||||
self.xf_idx = xf_idx
|
||||
try:
|
||||
self.number = error_code_map[error_string_or_code]
|
||||
except KeyError:
|
||||
raise Exception('Illegal error value (%r)' % error_string_or_code)
|
||||
|
||||
def get_biff_data(self):
|
||||
return BIFFRecords.BoolErrRecord(self.rowx,
|
||||
self.colx, self.xf_idx, self.number, 1).get()
|
||||
|
||||
class FormulaCell(object):
|
||||
__slots__ = ["rowx", "colx", "xf_idx", "frmla", "calc_flags"]
|
||||
|
||||
def __init__(self, rowx, colx, xf_idx, frmla, calc_flags=0):
|
||||
self.rowx = rowx
|
||||
self.colx = colx
|
||||
self.xf_idx = xf_idx
|
||||
self.frmla = frmla
|
||||
self.calc_flags = calc_flags
|
||||
|
||||
def get_biff_data(self):
|
||||
return BIFFRecords.FormulaRecord(self.rowx,
|
||||
self.colx, self.xf_idx, self.frmla.rpn(), self.calc_flags).get()
|
||||
|
||||
# module-level function for *internal* use by the Row module
|
||||
|
||||
def _get_cells_biff_data_mul(rowx, cell_items):
|
||||
# Return the BIFF data for all cell records in the row.
|
||||
# Adjacent BLANK|RK records are combined into MUL(BLANK|RK) records.
|
||||
pieces = []
|
||||
nitems = len(cell_items)
|
||||
i = 0
|
||||
while i < nitems:
|
||||
icolx, icell = cell_items[i]
|
||||
if isinstance(icell, NumberCell):
|
||||
isRK, value = icell.get_encoded_data()
|
||||
if not isRK:
|
||||
pieces.append(value) # pre-packed NUMBER record
|
||||
i += 1
|
||||
continue
|
||||
muldata = [(value, icell.xf_idx)]
|
||||
target = NumberCell
|
||||
elif isinstance(icell, BlankCell):
|
||||
muldata = [icell.xf_idx]
|
||||
target = BlankCell
|
||||
else:
|
||||
pieces.append(icell.get_biff_data())
|
||||
i += 1
|
||||
continue
|
||||
lastcolx = icolx
|
||||
j = i
|
||||
|
||||
packed_record = b'' # (to_py3): 'b' binary data
|
||||
for j in range(i+1, nitems):
|
||||
jcolx, jcell = cell_items[j]
|
||||
if jcolx != lastcolx + 1:
|
||||
nexti = j
|
||||
break
|
||||
if not isinstance(jcell, target):
|
||||
nexti = j
|
||||
break
|
||||
if target == NumberCell:
|
||||
isRK, value = jcell.get_encoded_data()
|
||||
if not isRK:
|
||||
packed_record = value
|
||||
nexti = j + 1
|
||||
break
|
||||
muldata.append((value, jcell.xf_idx))
|
||||
else:
|
||||
muldata.append(jcell.xf_idx)
|
||||
lastcolx = jcolx
|
||||
else:
|
||||
nexti = j + 1
|
||||
if target == NumberCell:
|
||||
if lastcolx == icolx:
|
||||
# RK record
|
||||
value, xf_idx = muldata[0]
|
||||
pieces.append(pack('<5Hi', 0x027E, 10, rowx, icolx, xf_idx, value))
|
||||
else:
|
||||
# MULRK record
|
||||
nc = lastcolx - icolx + 1
|
||||
pieces.append(pack('<4H', 0x00BD, 6 * nc + 6, rowx, icolx))
|
||||
# (to_py3): 'b' binary data
|
||||
pieces.append(b''.join([pack('<Hi', xf_idx, value) for value, xf_idx in muldata]))
|
||||
pieces.append(pack('<H', lastcolx))
|
||||
else:
|
||||
if lastcolx == icolx:
|
||||
# BLANK record
|
||||
xf_idx = muldata[0]
|
||||
pieces.append(pack('<5H', 0x0201, 6, rowx, icolx, xf_idx))
|
||||
else:
|
||||
# MULBLANK record
|
||||
nc = lastcolx - icolx + 1
|
||||
pieces.append(pack('<4H', 0x00BE, 2 * nc + 6, rowx, icolx))
|
||||
# (to_py3): 'b' binary data
|
||||
pieces.append(b''.join([pack('<H', xf_idx) for xf_idx in muldata]))
|
||||
pieces.append(pack('<H', lastcolx))
|
||||
if packed_record:
|
||||
pieces.append(packed_record)
|
||||
i = nexti
|
||||
return b''.join(pieces) # (to_py3): 'b' binary data
|
||||
@@ -1,34 +0,0 @@
|
||||
# -*- coding: windows-1252 -*-
|
||||
|
||||
from .BIFFRecords import ColInfoRecord
|
||||
|
||||
class Column(object):
|
||||
def __init__(self, colx, parent_sheet):
|
||||
if not(isinstance(colx, int) and 0 <= colx <= 255):
|
||||
raise ValueError("column index (%r) not an int in range(256)" % colx)
|
||||
self._index = colx
|
||||
self._parent = parent_sheet
|
||||
self._parent_wb = parent_sheet.get_parent()
|
||||
self._xf_index = 0x0F
|
||||
|
||||
self.width = 0x0B92
|
||||
self.hidden = 0
|
||||
self.level = 0
|
||||
self.collapse = 0
|
||||
|
||||
def set_style(self, style):
|
||||
self._xf_index = self._parent_wb.add_style(style)
|
||||
|
||||
def width_in_pixels(self):
|
||||
# *** Approximation ****
|
||||
return int(round(self.width * 0.0272 + 0.446, 0))
|
||||
|
||||
def get_biff_record(self):
|
||||
options = (self.hidden & 0x01) << 0
|
||||
options |= (self.level & 0x07) << 8
|
||||
options |= (self.collapse & 0x01) << 12
|
||||
|
||||
return ColInfoRecord(self._index, self._index, self.width, self._xf_index, options).get()
|
||||
|
||||
|
||||
|
||||
@@ -1,516 +0,0 @@
|
||||
import sys
|
||||
import struct
|
||||
|
||||
ENCODING = 'utf-16-le'
|
||||
|
||||
class Reader:
|
||||
def __init__(self, filename, dump = False):
|
||||
self.dump = dump
|
||||
self.STREAMS = {}
|
||||
|
||||
doc = file(filename, 'rb').read()
|
||||
self.header, self.data = doc[0:512], doc[512:]
|
||||
del doc
|
||||
|
||||
self.__build_header()
|
||||
self.__build_MSAT()
|
||||
self.__build_SAT()
|
||||
self.__build_directory()
|
||||
self.__build_short_sectors_data()
|
||||
|
||||
if len(self.short_sectors_data) > 0:
|
||||
self.__build_SSAT()
|
||||
else:
|
||||
if self.dump and (self.total_ssat_sectors != 0 or self.ssat_start_sid != -2):
|
||||
print('NOTE: header says that must be', self.total_ssat_sectors, 'short sectors')
|
||||
print('NOTE: starting at', self.ssat_start_sid, 'sector')
|
||||
print('NOTE: but file does not contains data in short sectors')
|
||||
self.ssat_start_sid = -2
|
||||
self.total_ssat_sectors = 0
|
||||
self.SSAT = [-2]
|
||||
|
||||
for dentry in self.dir_entry_list[1:]:
|
||||
(did,
|
||||
sz, name,
|
||||
t, c,
|
||||
did_left, did_right, did_root,
|
||||
dentry_start_sid,
|
||||
stream_size
|
||||
) = dentry
|
||||
stream_data = b''
|
||||
if stream_size > 0:
|
||||
if stream_size >= self.min_stream_size:
|
||||
args = (self.data, self.SAT, dentry_start_sid, self.sect_size)
|
||||
else:
|
||||
args = (self.short_sectors_data, self.SSAT, dentry_start_sid, self.short_sect_size)
|
||||
stream_data = self.get_stream_data(*args)
|
||||
|
||||
if name != b'':
|
||||
# BAD IDEA: names may be equal. NEED use full paths...
|
||||
self.STREAMS[name] = stream_data
|
||||
|
||||
|
||||
def __build_header(self):
|
||||
self.doc_magic = self.header[0:8]
|
||||
|
||||
if self.doc_magic != b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1':
|
||||
raise Exception('Not an OLE file.')
|
||||
|
||||
self.file_uid = self.header[8:24]
|
||||
self.rev_num = self.header[24:26]
|
||||
self.ver_num = self.header[26:28]
|
||||
self.byte_order = self.header[28:30]
|
||||
self.log2_sect_size, = struct.unpack('<H', self.header[30:32])
|
||||
self.log2_short_sect_size, = struct.unpack('<H', self.header[32:34])
|
||||
self.total_sat_sectors, = struct.unpack('<L', self.header[44:48])
|
||||
self.dir_start_sid, = struct.unpack('<l', self.header[48:52])
|
||||
self.min_stream_size, = struct.unpack('<L', self.header[56:60])
|
||||
self.ssat_start_sid, = struct.unpack('<l', self.header[60:64])
|
||||
self.total_ssat_sectors, = struct.unpack('<L', self.header[64:68])
|
||||
self.msat_start_sid, = struct.unpack('<l', self.header[68:72])
|
||||
self.total_msat_sectors, = struct.unpack('<L', self.header[72:76])
|
||||
|
||||
self.sect_size = 1 << self.log2_sect_size
|
||||
self.short_sect_size = 1 << self.log2_short_sect_size
|
||||
|
||||
if self.dump:
|
||||
print('file magic: ')
|
||||
print_bin_data(self.doc_magic)
|
||||
|
||||
print('file uid: ')
|
||||
print_bin_data(self.file_uid)
|
||||
|
||||
print('revision number: ')
|
||||
print_bin_data(self.rev_num)
|
||||
|
||||
print('version number: ')
|
||||
print_bin_data(self.ver_num)
|
||||
|
||||
print('byte order: ')
|
||||
print_bin_data(self.byte_order)
|
||||
|
||||
print('sector size :', hex(self.sect_size), self.sect_size)
|
||||
#print 'total sectors in file :', hex(self.total_sectors), self.total_sectors
|
||||
print('short sector size :', hex(self.short_sect_size), self.short_sect_size)
|
||||
print('Total number of sectors used for the SAT :', hex(self.total_sat_sectors), self.total_sat_sectors)
|
||||
print('SID of first sector of the directory stream:', hex(self.dir_start_sid), self.dir_start_sid)
|
||||
print('Minimum size of a standard stream :', hex(self.min_stream_size), self.min_stream_size)
|
||||
print('SID of first sector of the SSAT :', hex(self.ssat_start_sid), self.ssat_start_sid)
|
||||
print('Total number of sectors used for the SSAT :', hex(self.total_ssat_sectors), self.total_ssat_sectors)
|
||||
print('SID of first additional sector of the MSAT :', hex(self.msat_start_sid), self.msat_start_sid)
|
||||
print('Total number of sectors used for the MSAT :', hex(self.total_msat_sectors), self.total_msat_sectors)
|
||||
|
||||
|
||||
def __build_MSAT(self):
|
||||
self.MSAT = list(struct.unpack('<109l', self.header[76:]))
|
||||
|
||||
next = self.msat_start_sid
|
||||
while next > 0:
|
||||
msat_sector = struct.unpack('<128l', self.data[next*self.sect_size:(next+1)*self.sect_size])
|
||||
self.MSAT.extend(msat_sector[:127])
|
||||
next = msat_sector[-1]
|
||||
|
||||
if self.dump:
|
||||
print('MSAT (header part): \n', self.MSAT[:109])
|
||||
print('additional MSAT sectors: \n', self.MSAT[109:])
|
||||
|
||||
|
||||
def __build_SAT(self):
|
||||
sat_stream = b''.join([self.data[i*self.sect_size:(i+1)*self.sect_size] for i in self.MSAT if i >= 0])
|
||||
|
||||
sat_sids_count = len(sat_stream) >> 2
|
||||
self.SAT = struct.unpack('<%dl' % sat_sids_count, sat_stream) # SIDs tuple
|
||||
|
||||
if self.dump:
|
||||
print('SAT sid count:\n', sat_sids_count)
|
||||
print('SAT content:\n', self.SAT)
|
||||
|
||||
|
||||
def __build_SSAT(self):
|
||||
ssat_stream = self.get_stream_data(self.data, self.SAT, self.ssat_start_sid, self.sect_size)
|
||||
|
||||
ssids_count = len(ssat_stream) >> 2
|
||||
self.SSAT = struct.unpack('<%dl' % ssids_count, ssat_stream)
|
||||
|
||||
if self.dump:
|
||||
print('SSID count:', ssids_count)
|
||||
print('SSAT content:\n', self.SSAT)
|
||||
|
||||
|
||||
def __build_directory(self):
|
||||
dir_stream = self.get_stream_data(self.data, self.SAT, self.dir_start_sid, self.sect_size)
|
||||
|
||||
self.dir_entry_list = []
|
||||
|
||||
i = 0
|
||||
while i < len(dir_stream):
|
||||
dentry = dir_stream[i:i+128] # 128 -- dir entry size
|
||||
i += 128
|
||||
|
||||
did = len(self.dir_entry_list)
|
||||
sz, = struct.unpack('<H', dentry[64:66])
|
||||
if sz > 0 :
|
||||
name = dentry[0:sz-2].decode('utf_16_le', 'replace')
|
||||
else:
|
||||
name = b''
|
||||
t, = struct.unpack('B', dentry[66])
|
||||
c, = struct.unpack('B', dentry[67])
|
||||
did_left , = struct.unpack('<l', dentry[68:72])
|
||||
did_right , = struct.unpack('<l', dentry[72:76])
|
||||
did_root , = struct.unpack('<l', dentry[76:80])
|
||||
dentry_start_sid , = struct.unpack('<l', dentry[116:120])
|
||||
stream_size , = struct.unpack('<L', dentry[120:124])
|
||||
|
||||
self.dir_entry_list.extend([(did, sz, name, t, c,
|
||||
did_left, did_right, did_root,
|
||||
dentry_start_sid, stream_size)])
|
||||
|
||||
if self.dump:
|
||||
dentry_types = {
|
||||
0x00: 'Empty',
|
||||
0x01: 'User storage',
|
||||
0x02: 'User stream',
|
||||
0x03: 'LockBytes',
|
||||
0x04: 'Property',
|
||||
0x05: 'Root storage'
|
||||
}
|
||||
node_colours = {
|
||||
0x00: 'Red',
|
||||
0x01: 'Black'
|
||||
}
|
||||
print('total directory entries:', len(self.dir_entry_list))
|
||||
|
||||
for dentry in self.dir_entry_list:
|
||||
(did, sz, name, t, c,
|
||||
did_left, did_right, did_root,
|
||||
dentry_start_sid, stream_size) = dentry
|
||||
print('DID', did)
|
||||
print('Size of the used area of the character buffer of the name:', sz)
|
||||
print('dir entry name:', repr(name))
|
||||
print('type of entry:', t, dentry_types[t])
|
||||
print('entry colour:', c, node_colours[c])
|
||||
print('left child DID :', did_left)
|
||||
print('right child DID:', did_right)
|
||||
print('root DID :', did_root)
|
||||
print('start SID :', dentry_start_sid)
|
||||
print('stream size :', stream_size)
|
||||
if stream_size == 0:
|
||||
print('stream is empty')
|
||||
elif stream_size >= self.min_stream_size:
|
||||
print('stream stored as normal stream')
|
||||
else:
|
||||
print('stream stored as short-stream')
|
||||
|
||||
|
||||
def __build_short_sectors_data(self):
|
||||
(did, sz, name, t, c,
|
||||
did_left, did_right, did_root,
|
||||
dentry_start_sid, stream_size) = self.dir_entry_list[0]
|
||||
assert t == 0x05 # Short-Stream Container Stream (SSCS) resides in Root Storage
|
||||
if stream_size == 0:
|
||||
self.short_sectors_data = b''
|
||||
else:
|
||||
self.short_sectors_data = self.get_stream_data(self.data, self.SAT, dentry_start_sid, self.sect_size)
|
||||
|
||||
|
||||
def get_stream_data(self, data, SAT, start_sid, sect_size):
|
||||
sid = start_sid
|
||||
chunks = [(sid, sid)]
|
||||
stream_data = b''
|
||||
|
||||
while SAT[sid] >= 0:
|
||||
next_in_chain = SAT[sid]
|
||||
last_chunk_start, last_chunk_finish = chunks[-1]
|
||||
if next_in_chain == last_chunk_finish + 1:
|
||||
chunks[-1] = last_chunk_start, next_in_chain
|
||||
else:
|
||||
chunks.extend([(next_in_chain, next_in_chain)])
|
||||
sid = next_in_chain
|
||||
for s, f in chunks:
|
||||
stream_data += data[s*sect_size:(f+1)*sect_size]
|
||||
#print chunks
|
||||
return stream_data
|
||||
|
||||
|
||||
def print_bin_data(data):
|
||||
i = 0
|
||||
while i < len(data):
|
||||
j = 0
|
||||
while (i < len(data)) and (j < 16):
|
||||
c = b'0x%02X' % ord(data[i])
|
||||
sys.stdout.write(c)
|
||||
sys.stdout.write(' ')
|
||||
i += 1
|
||||
j += 1
|
||||
print()
|
||||
if i == 0:
|
||||
print('<NO DATA>')
|
||||
|
||||
|
||||
|
||||
# This implementation writes only 'Root Entry', 'Workbook' streams
|
||||
# and 2 empty streams for aligning directory stream on sector boundary
|
||||
#
|
||||
# LAYOUT:
|
||||
# 0 header
|
||||
# 76 MSAT (1st part: 109 SID)
|
||||
# 512 workbook stream
|
||||
# ... additional MSAT sectors if streams' size > about 7 Mb == (109*512 * 128)
|
||||
# ... SAT
|
||||
# ... directory stream
|
||||
#
|
||||
# NOTE: this layout is "ad hoc". It can be more general. RTFM
|
||||
|
||||
class XlsDoc:
|
||||
SECTOR_SIZE = 0x0200
|
||||
MIN_LIMIT = 0x1000
|
||||
|
||||
SID_FREE_SECTOR = -1
|
||||
SID_END_OF_CHAIN = -2
|
||||
SID_USED_BY_SAT = -3
|
||||
SID_USED_BY_MSAT = -4
|
||||
|
||||
def __init__(self):
|
||||
self.book_stream_sect = []
|
||||
|
||||
self.dir_stream = b''
|
||||
self.dir_stream_sect = []
|
||||
|
||||
self.packed_SAT = b''
|
||||
self.SAT_sect = []
|
||||
|
||||
self.packed_MSAT_1st = b''
|
||||
self.packed_MSAT_2nd = b''
|
||||
self.MSAT_sect_2nd = []
|
||||
|
||||
self.header = b''
|
||||
|
||||
def __build_directory(self): # align on sector boundary
|
||||
self.dir_stream = b''
|
||||
|
||||
#(to_py3): replaced = b'\x00'.join(b'Root Entry\x00') + b'\x00'
|
||||
dentry_name = 'Root Entry\x00'.encode(ENCODING)
|
||||
dentry_name_sz = len(dentry_name)
|
||||
dentry_name_pad = b'\x00'*(64 - dentry_name_sz)
|
||||
dentry_type = 0x05 # root storage
|
||||
dentry_colour = 0x01 # black
|
||||
dentry_did_left = -1
|
||||
dentry_did_right = -1
|
||||
dentry_did_root = 1
|
||||
dentry_start_sid = -2
|
||||
dentry_stream_sz = 0
|
||||
|
||||
self.dir_stream += struct.pack('<64s H 2B 3l 9L l L L',
|
||||
dentry_name + dentry_name_pad,
|
||||
dentry_name_sz,
|
||||
dentry_type,
|
||||
dentry_colour,
|
||||
dentry_did_left,
|
||||
dentry_did_right,
|
||||
dentry_did_root,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
dentry_start_sid,
|
||||
dentry_stream_sz,
|
||||
0
|
||||
)
|
||||
# (to_py3): replaced = b'\x00'.join(b'Workbook\x00') + b'\x00'
|
||||
dentry_name = 'Workbook\x00'.encode(ENCODING)
|
||||
dentry_name_sz = len(dentry_name)
|
||||
dentry_name_pad = b'\x00'*(64 - dentry_name_sz)
|
||||
dentry_type = 0x02 # user stream
|
||||
dentry_colour = 0x01 # black
|
||||
dentry_did_left = -1
|
||||
dentry_did_right = -1
|
||||
dentry_did_root = -1
|
||||
dentry_start_sid = 0
|
||||
dentry_stream_sz = self.book_stream_len
|
||||
|
||||
self.dir_stream += struct.pack('<64s H 2B 3l 9L l L L',
|
||||
dentry_name + dentry_name_pad,
|
||||
dentry_name_sz,
|
||||
dentry_type,
|
||||
dentry_colour,
|
||||
dentry_did_left,
|
||||
dentry_did_right,
|
||||
dentry_did_root,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
dentry_start_sid,
|
||||
dentry_stream_sz,
|
||||
0
|
||||
)
|
||||
|
||||
# padding
|
||||
dentry_name = b''
|
||||
dentry_name_sz = len(dentry_name)
|
||||
dentry_name_pad = b'\x00'*(64 - dentry_name_sz)
|
||||
dentry_type = 0x00 # empty
|
||||
dentry_colour = 0x01 # black
|
||||
dentry_did_left = -1
|
||||
dentry_did_right = -1
|
||||
dentry_did_root = -1
|
||||
dentry_start_sid = -2
|
||||
dentry_stream_sz = 0
|
||||
|
||||
self.dir_stream += struct.pack('<64s H 2B 3l 9L l L L',
|
||||
dentry_name + dentry_name_pad,
|
||||
dentry_name_sz,
|
||||
dentry_type,
|
||||
dentry_colour,
|
||||
dentry_did_left,
|
||||
dentry_did_right,
|
||||
dentry_did_root,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
dentry_start_sid,
|
||||
dentry_stream_sz,
|
||||
0
|
||||
) * 2
|
||||
|
||||
def __build_sat(self):
|
||||
# Build SAT
|
||||
book_sect_count = self.book_stream_len >> 9
|
||||
dir_sect_count = len(self.dir_stream) >> 9
|
||||
|
||||
total_sect_count = book_sect_count + dir_sect_count
|
||||
SAT_sect_count = 0
|
||||
MSAT_sect_count = 0
|
||||
SAT_sect_count_limit = 109
|
||||
while total_sect_count > 128*SAT_sect_count or SAT_sect_count > SAT_sect_count_limit:
|
||||
SAT_sect_count += 1
|
||||
total_sect_count += 1
|
||||
if SAT_sect_count > SAT_sect_count_limit:
|
||||
MSAT_sect_count += 1
|
||||
total_sect_count += 1
|
||||
SAT_sect_count_limit += 127
|
||||
|
||||
|
||||
SAT = [self.SID_FREE_SECTOR]*128*SAT_sect_count
|
||||
|
||||
sect = 0
|
||||
while sect < book_sect_count - 1:
|
||||
self.book_stream_sect.append(sect)
|
||||
SAT[sect] = sect + 1
|
||||
sect += 1
|
||||
self.book_stream_sect.append(sect)
|
||||
SAT[sect] = self.SID_END_OF_CHAIN
|
||||
sect += 1
|
||||
|
||||
while sect < book_sect_count + MSAT_sect_count:
|
||||
self.MSAT_sect_2nd.append(sect)
|
||||
SAT[sect] = self.SID_USED_BY_MSAT
|
||||
sect += 1
|
||||
|
||||
while sect < book_sect_count + MSAT_sect_count + SAT_sect_count:
|
||||
self.SAT_sect.append(sect)
|
||||
SAT[sect] = self.SID_USED_BY_SAT
|
||||
sect += 1
|
||||
|
||||
while sect < book_sect_count + MSAT_sect_count + SAT_sect_count + dir_sect_count - 1:
|
||||
self.dir_stream_sect.append(sect)
|
||||
SAT[sect] = sect + 1
|
||||
sect += 1
|
||||
self.dir_stream_sect.append(sect)
|
||||
SAT[sect] = self.SID_END_OF_CHAIN
|
||||
sect += 1
|
||||
|
||||
self.packed_SAT = struct.pack('<%dl' % (SAT_sect_count*128), *SAT)
|
||||
|
||||
MSAT_1st = [self.SID_FREE_SECTOR]*109
|
||||
for i, SAT_sect_num in zip(list(range(0, 109)), self.SAT_sect):
|
||||
MSAT_1st[i] = SAT_sect_num
|
||||
self.packed_MSAT_1st = struct.pack('<109l', *MSAT_1st)
|
||||
|
||||
MSAT_2nd = [self.SID_FREE_SECTOR]*128*MSAT_sect_count
|
||||
if MSAT_sect_count > 0:
|
||||
MSAT_2nd[- 1] = self.SID_END_OF_CHAIN
|
||||
|
||||
i = 109
|
||||
msat_sect = 0
|
||||
sid_num = 0
|
||||
while i < SAT_sect_count:
|
||||
if (sid_num + 1) % 128 == 0:
|
||||
#print 'link: ',
|
||||
msat_sect += 1
|
||||
if msat_sect < len(self.MSAT_sect_2nd):
|
||||
MSAT_2nd[sid_num] = self.MSAT_sect_2nd[msat_sect]
|
||||
else:
|
||||
#print 'sid: ',
|
||||
MSAT_2nd[sid_num] = self.SAT_sect[i]
|
||||
i += 1
|
||||
#print sid_num, MSAT_2nd[sid_num]
|
||||
sid_num += 1
|
||||
|
||||
self.packed_MSAT_2nd = struct.pack('<%dl' % (MSAT_sect_count*128), *MSAT_2nd)
|
||||
|
||||
#print vars()
|
||||
#print zip(range(0, sect), SAT)
|
||||
#print self.book_stream_sect
|
||||
#print self.MSAT_sect_2nd
|
||||
#print MSAT_2nd
|
||||
#print self.SAT_sect
|
||||
#print self.dir_stream_sect
|
||||
|
||||
|
||||
def __build_header(self):
|
||||
doc_magic = b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1'
|
||||
file_uid = b'\x00'*16
|
||||
rev_num = b'\x3E\x00'
|
||||
ver_num = b'\x03\x00'
|
||||
byte_order = b'\xFE\xFF'
|
||||
log_sect_size = struct.pack('<H', 9)
|
||||
log_short_sect_size = struct.pack('<H', 6)
|
||||
not_used0 = b'\x00'*10
|
||||
total_sat_sectors = struct.pack('<L', len(self.SAT_sect))
|
||||
dir_start_sid = struct.pack('<l', self.dir_stream_sect[0])
|
||||
not_used1 = b'\x00'*4
|
||||
min_stream_size = struct.pack('<L', 0x1000)
|
||||
ssat_start_sid = struct.pack('<l', -2)
|
||||
total_ssat_sectors = struct.pack('<L', 0)
|
||||
|
||||
if len(self.MSAT_sect_2nd) == 0:
|
||||
msat_start_sid = struct.pack('<l', -2)
|
||||
else:
|
||||
msat_start_sid = struct.pack('<l', self.MSAT_sect_2nd[0])
|
||||
|
||||
total_msat_sectors = struct.pack('<L', len(self.MSAT_sect_2nd))
|
||||
|
||||
self.header = b''.join([ doc_magic,
|
||||
file_uid,
|
||||
rev_num,
|
||||
ver_num,
|
||||
byte_order,
|
||||
log_sect_size,
|
||||
log_short_sect_size,
|
||||
not_used0,
|
||||
total_sat_sectors,
|
||||
dir_start_sid,
|
||||
not_used1,
|
||||
min_stream_size,
|
||||
ssat_start_sid,
|
||||
total_ssat_sectors,
|
||||
msat_start_sid,
|
||||
total_msat_sectors
|
||||
])
|
||||
|
||||
|
||||
def save(self, file_name_or_filelike_obj, stream):
|
||||
# 1. Align stream on 0x1000 boundary (and therefore on sector boundary)
|
||||
padding = b'\x00' * (0x1000 - (len(stream) % 0x1000))
|
||||
self.book_stream_len = len(stream) + len(padding)
|
||||
|
||||
self.__build_directory()
|
||||
self.__build_sat()
|
||||
self.__build_header()
|
||||
|
||||
f = file_name_or_filelike_obj
|
||||
we_own_it = not hasattr(f, 'write')
|
||||
if we_own_it:
|
||||
f = open(file_name_or_filelike_obj, 'wb')
|
||||
f.write(self.header)
|
||||
f.write(self.packed_MSAT_1st)
|
||||
f.write(stream)
|
||||
f.write(padding)
|
||||
f.write(self.packed_MSAT_2nd)
|
||||
f.write(self.packed_SAT)
|
||||
f.write(self.dir_stream)
|
||||
if we_own_it:
|
||||
f.close()
|
||||
@@ -1,41 +0,0 @@
|
||||
from . import ExcelFormulaParser, ExcelFormulaLexer
|
||||
import struct
|
||||
from .antlr import ANTLRException
|
||||
|
||||
|
||||
class Formula(object):
|
||||
__slots__ = ["__s", "__parser", "__sheet_refs", "__xcall_refs"]
|
||||
|
||||
|
||||
def __init__(self, s):
|
||||
try:
|
||||
self.__s = s
|
||||
lexer = ExcelFormulaLexer.Lexer(s)
|
||||
self.__parser = ExcelFormulaParser.Parser(lexer)
|
||||
self.__parser.formula()
|
||||
self.__sheet_refs = self.__parser.sheet_references
|
||||
self.__xcall_refs = self.__parser.xcall_references
|
||||
except ANTLRException as e:
|
||||
# print e
|
||||
raise ExcelFormulaParser.FormulaParseException("can't parse formula " + s)
|
||||
|
||||
def get_references(self):
|
||||
return self.__sheet_refs, self.__xcall_refs
|
||||
|
||||
def patch_references(self, patches):
|
||||
for offset, idx in patches:
|
||||
self.__parser.rpn = self.__parser.rpn[:offset] + struct.pack('<H', idx) + self.__parser.rpn[offset+2:]
|
||||
|
||||
def text(self):
|
||||
return self.__s
|
||||
|
||||
def rpn(self):
|
||||
'''
|
||||
Offset Size Contents
|
||||
0 2 Size of the following formula data (sz)
|
||||
2 sz Formula data (RPN token array)
|
||||
[2+sz] var. (optional) Additional data for specific tokens
|
||||
|
||||
'''
|
||||
return struct.pack("<H", len(self.__parser.rpn)) + self.__parser.rpn
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
import sys
|
||||
from .antlr import EOF, CommonToken as Tok, TokenStream, TokenStreamException
|
||||
import struct
|
||||
from . import ExcelFormulaParser
|
||||
from re import compile as recompile, match, UNICODE, IGNORECASE, VERBOSE
|
||||
|
||||
|
||||
int_const_pattern = r"\d+\b"
|
||||
flt_const_pattern = r"""
|
||||
(?:
|
||||
(?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc
|
||||
|
|
||||
(?: \d+ \. ) # 1. 12. 123. etc
|
||||
)
|
||||
# followed by optional exponent part
|
||||
(?: [Ee] [+-]? \d+ ) ?
|
||||
"""
|
||||
str_const_pattern = r'"(?:[^"]|"")*"'
|
||||
#range2d_pattern = recompile(r"\$?[A-I]?[A-Z]\$?\d+:\$?[A-I]?[A-Z]\$?\d+"
|
||||
ref2d_r1c1_pattern = r"[Rr]0*[1-9][0-9]*[Cc]0*[1-9][0-9]*"
|
||||
ref2d_pattern = r"\$?[A-I]?[A-Z]\$?0*[1-9][0-9]*"
|
||||
true_pattern = r"TRUE\b"
|
||||
false_pattern = r"FALSE\b"
|
||||
if_pattern = r"IF\b"
|
||||
choose_pattern = r"CHOOSE\b"
|
||||
name_pattern = r"\w[\.\w]*"
|
||||
quotename_pattern = r"'(?:[^']|'')*'" #### It's essential that this bracket be non-grouping.
|
||||
ne_pattern = r"<>"
|
||||
ge_pattern = r">="
|
||||
le_pattern = r"<="
|
||||
|
||||
pattern_type_tuples = (
|
||||
(flt_const_pattern, ExcelFormulaParser.NUM_CONST),
|
||||
(int_const_pattern, ExcelFormulaParser.INT_CONST),
|
||||
(str_const_pattern, ExcelFormulaParser.STR_CONST),
|
||||
# (range2d_pattern , ExcelFormulaParser.RANGE2D),
|
||||
(ref2d_r1c1_pattern, ExcelFormulaParser.REF2D_R1C1),
|
||||
(ref2d_pattern , ExcelFormulaParser.REF2D),
|
||||
(true_pattern , ExcelFormulaParser.TRUE_CONST),
|
||||
(false_pattern , ExcelFormulaParser.FALSE_CONST),
|
||||
(if_pattern , ExcelFormulaParser.FUNC_IF),
|
||||
(choose_pattern , ExcelFormulaParser.FUNC_CHOOSE),
|
||||
(name_pattern , ExcelFormulaParser.NAME),
|
||||
(quotename_pattern, ExcelFormulaParser.QUOTENAME),
|
||||
(ne_pattern, ExcelFormulaParser.NE),
|
||||
(ge_pattern, ExcelFormulaParser.GE),
|
||||
(le_pattern, ExcelFormulaParser.LE),
|
||||
)
|
||||
|
||||
_re = recompile(
|
||||
'(' + ')|('.join([i[0] for i in pattern_type_tuples]) + ')',
|
||||
VERBOSE+IGNORECASE)
|
||||
|
||||
_toktype = [None] + [i[1] for i in pattern_type_tuples]
|
||||
# need dummy at start because re.MatchObject.lastindex counts from 1
|
||||
|
||||
single_char_lookup = {
|
||||
'=': ExcelFormulaParser.EQ,
|
||||
'<': ExcelFormulaParser.LT,
|
||||
'>': ExcelFormulaParser.GT,
|
||||
'+': ExcelFormulaParser.ADD,
|
||||
'-': ExcelFormulaParser.SUB,
|
||||
'*': ExcelFormulaParser.MUL,
|
||||
'/': ExcelFormulaParser.DIV,
|
||||
':': ExcelFormulaParser.COLON,
|
||||
';': ExcelFormulaParser.SEMICOLON,
|
||||
',': ExcelFormulaParser.COMMA,
|
||||
'(': ExcelFormulaParser.LP,
|
||||
')': ExcelFormulaParser.RP,
|
||||
'&': ExcelFormulaParser.CONCAT,
|
||||
'%': ExcelFormulaParser.PERCENT,
|
||||
'^': ExcelFormulaParser.POWER,
|
||||
'!': ExcelFormulaParser.BANG,
|
||||
}
|
||||
|
||||
class Lexer(TokenStream):
|
||||
def __init__(self, text):
|
||||
self._text = text[:]
|
||||
self._pos = 0
|
||||
self._line = 0
|
||||
|
||||
def isEOF(self):
|
||||
return len(self._text) <= self._pos
|
||||
|
||||
def curr_ch(self):
|
||||
return self._text[self._pos]
|
||||
|
||||
def next_ch(self, n = 1):
|
||||
self._pos += n
|
||||
|
||||
def is_whitespace(self):
|
||||
return self.curr_ch() in " \t\n\r\f\v"
|
||||
|
||||
def match_pattern(self):
|
||||
m = _re.match(self._text, self._pos)
|
||||
if not m:
|
||||
return None
|
||||
self._pos = m.end(0)
|
||||
return Tok(type = _toktype[m.lastindex], text = m.group(0), col = m.start(0) + 1)
|
||||
|
||||
def nextToken(self):
|
||||
# skip whitespace
|
||||
while not self.isEOF() and self.is_whitespace():
|
||||
self.next_ch()
|
||||
if self.isEOF():
|
||||
return Tok(type = EOF)
|
||||
# first, try to match token with 2 or more chars
|
||||
t = self.match_pattern()
|
||||
if t:
|
||||
return t
|
||||
# second, we want 1-char tokens
|
||||
te = self.curr_ch()
|
||||
try:
|
||||
ty = single_char_lookup[te]
|
||||
except KeyError:
|
||||
raise TokenStreamException(
|
||||
"Unexpected char %r in column %u." % (self.curr_ch(), self._pos))
|
||||
self.next_ch()
|
||||
return Tok(type=ty, text=te, col=self._pos)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
for t in Lexer(""" 1.23 456 "abcd" R2C2 a1 iv65536 true false if choose a_name 'qname' <> >= <= """):
|
||||
print(t)
|
||||
except TokenStreamException as e:
|
||||
print("error:", e)
|
||||
@@ -1,659 +0,0 @@
|
||||
### $ANTLR 2.7.7 (20060930): "xlwt/excel-formula.g" -> "ExcelFormulaParser.py"$
|
||||
### import antlr and other modules ..
|
||||
import sys
|
||||
from . import antlr
|
||||
|
||||
### header action >>>
|
||||
import struct
|
||||
from . import Utils
|
||||
from .UnicodeUtils import upack1
|
||||
from .ExcelMagic import *
|
||||
|
||||
_RVAdelta = {"R": 0, "V": 0x20, "A": 0x40}
|
||||
_RVAdeltaRef = {"R": 0, "V": 0x20, "A": 0x40, "D": 0x20}
|
||||
_RVAdeltaArea = {"R": 0, "V": 0x20, "A": 0x40, "D": 0}
|
||||
|
||||
|
||||
class FormulaParseException(Exception):
|
||||
"""
|
||||
An exception indicating that a Formula could not be successfully parsed.
|
||||
"""
|
||||
### header action <<<
|
||||
### preamble action>>>
|
||||
|
||||
### preamble action <<<
|
||||
|
||||
### import antlr.Token
|
||||
from .antlr import Token
|
||||
### >>>The Known Token Types <<<
|
||||
SKIP = antlr.SKIP
|
||||
INVALID_TYPE = antlr.INVALID_TYPE
|
||||
EOF_TYPE = antlr.EOF_TYPE
|
||||
EOF = antlr.EOF
|
||||
NULL_TREE_LOOKAHEAD = antlr.NULL_TREE_LOOKAHEAD
|
||||
MIN_USER_TYPE = antlr.MIN_USER_TYPE
|
||||
TRUE_CONST = 4
|
||||
FALSE_CONST = 5
|
||||
STR_CONST = 6
|
||||
NUM_CONST = 7
|
||||
INT_CONST = 8
|
||||
FUNC_IF = 9
|
||||
FUNC_CHOOSE = 10
|
||||
NAME = 11
|
||||
QUOTENAME = 12
|
||||
EQ = 13
|
||||
NE = 14
|
||||
GT = 15
|
||||
LT = 16
|
||||
GE = 17
|
||||
LE = 18
|
||||
ADD = 19
|
||||
SUB = 20
|
||||
MUL = 21
|
||||
DIV = 22
|
||||
POWER = 23
|
||||
PERCENT = 24
|
||||
LP = 25
|
||||
RP = 26
|
||||
LB = 27
|
||||
RB = 28
|
||||
COLON = 29
|
||||
COMMA = 30
|
||||
SEMICOLON = 31
|
||||
REF2D = 32
|
||||
REF2D_R1C1 = 33
|
||||
BANG = 34
|
||||
CONCAT = 35
|
||||
|
||||
class Parser(antlr.LLkParser):
|
||||
### user action >>>
|
||||
### user action <<<
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
antlr.LLkParser.__init__(self, *args, **kwargs)
|
||||
self.tokenNames = _tokenNames
|
||||
### __init__ header action >>>
|
||||
self.rpn = b""
|
||||
self.sheet_references = []
|
||||
self.xcall_references = []
|
||||
### __init__ header action <<<
|
||||
|
||||
def formula(self):
|
||||
self.expr("V")
|
||||
|
||||
def expr(self, arg_type):
|
||||
self.prec0_expr(arg_type)
|
||||
while True:
|
||||
if ((self.LA(1) >= EQ and self.LA(1) <= LE)):
|
||||
pass
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [EQ]:
|
||||
pass
|
||||
self.match(EQ)
|
||||
op = struct.pack('B', ptgEQ)
|
||||
elif la1 and la1 in [NE]:
|
||||
pass
|
||||
self.match(NE)
|
||||
op = struct.pack('B', ptgNE)
|
||||
elif la1 and la1 in [GT]:
|
||||
pass
|
||||
self.match(GT)
|
||||
op = struct.pack('B', ptgGT)
|
||||
elif la1 and la1 in [LT]:
|
||||
pass
|
||||
self.match(LT)
|
||||
op = struct.pack('B', ptgLT)
|
||||
elif la1 and la1 in [GE]:
|
||||
pass
|
||||
self.match(GE)
|
||||
op = struct.pack('B', ptgGE)
|
||||
elif la1 and la1 in [LE]:
|
||||
pass
|
||||
self.match(LE)
|
||||
op = struct.pack('B', ptgLE)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
self.prec0_expr(arg_type)
|
||||
self.rpn += op
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
def prec0_expr(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
pass
|
||||
self.prec1_expr(arg_type)
|
||||
while True:
|
||||
if (self.LA(1)==CONCAT):
|
||||
pass
|
||||
pass
|
||||
self.match(CONCAT)
|
||||
op = struct.pack('B', ptgConcat)
|
||||
self.prec1_expr(arg_type)
|
||||
self.rpn += op
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
def prec1_expr(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
pass
|
||||
self.prec2_expr(arg_type)
|
||||
while True:
|
||||
if (self.LA(1)==ADD or self.LA(1)==SUB):
|
||||
pass
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [ADD]:
|
||||
pass
|
||||
self.match(ADD)
|
||||
op = struct.pack('B', ptgAdd)
|
||||
elif la1 and la1 in [SUB]:
|
||||
pass
|
||||
self.match(SUB)
|
||||
op = struct.pack('B', ptgSub)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
self.prec2_expr(arg_type)
|
||||
self.rpn += op;
|
||||
# print "**prec1_expr4 %s" % arg_type
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
def prec2_expr(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
pass
|
||||
self.prec3_expr(arg_type)
|
||||
while True:
|
||||
if (self.LA(1)==MUL or self.LA(1)==DIV):
|
||||
pass
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [MUL]:
|
||||
pass
|
||||
self.match(MUL)
|
||||
op = struct.pack('B', ptgMul)
|
||||
elif la1 and la1 in [DIV]:
|
||||
pass
|
||||
self.match(DIV)
|
||||
op = struct.pack('B', ptgDiv)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
self.prec3_expr(arg_type)
|
||||
self.rpn += op
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
def prec3_expr(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
pass
|
||||
self.prec4_expr(arg_type)
|
||||
while True:
|
||||
if (self.LA(1)==POWER):
|
||||
pass
|
||||
pass
|
||||
self.match(POWER)
|
||||
op = struct.pack('B', ptgPower)
|
||||
self.prec4_expr(arg_type)
|
||||
self.rpn += op
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
def prec4_expr(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
pass
|
||||
self.prec5_expr(arg_type)
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [PERCENT]:
|
||||
pass
|
||||
self.match(PERCENT)
|
||||
self.rpn += struct.pack('B', ptgPercent)
|
||||
elif la1 and la1 in [EOF,EQ,NE,GT,LT,GE,LE,ADD,SUB,MUL,DIV,POWER,RP,COMMA,SEMICOLON,CONCAT]:
|
||||
pass
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
|
||||
def prec5_expr(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,LP,REF2D]:
|
||||
pass
|
||||
self.primary(arg_type)
|
||||
elif la1 and la1 in [SUB]:
|
||||
pass
|
||||
self.match(SUB)
|
||||
self.primary(arg_type)
|
||||
self.rpn += struct.pack('B', ptgUminus)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
|
||||
def primary(self,
|
||||
arg_type
|
||||
):
|
||||
|
||||
str_tok = None
|
||||
int_tok = None
|
||||
num_tok = None
|
||||
ref2d_tok = None
|
||||
ref2d1_tok = None
|
||||
ref2d2_tok = None
|
||||
ref3d_ref2d = None
|
||||
ref3d_ref2d2 = None
|
||||
name_tok = None
|
||||
func_tok = None
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [TRUE_CONST]:
|
||||
pass
|
||||
self.match(TRUE_CONST)
|
||||
self.rpn += struct.pack("2B", ptgBool, 1)
|
||||
elif la1 and la1 in [FALSE_CONST]:
|
||||
pass
|
||||
self.match(FALSE_CONST)
|
||||
self.rpn += struct.pack("2B", ptgBool, 0)
|
||||
elif la1 and la1 in [STR_CONST]:
|
||||
pass
|
||||
str_tok = self.LT(1)
|
||||
self.match(STR_CONST)
|
||||
self.rpn += struct.pack("B", ptgStr) + upack1(str_tok.text[1:-1].replace("\"\"", "\""))
|
||||
elif la1 and la1 in [NUM_CONST]:
|
||||
pass
|
||||
num_tok = self.LT(1)
|
||||
self.match(NUM_CONST)
|
||||
self.rpn += struct.pack("<Bd", ptgNum, float(num_tok.text))
|
||||
elif la1 and la1 in [FUNC_IF]:
|
||||
pass
|
||||
self.match(FUNC_IF)
|
||||
self.match(LP)
|
||||
self.expr("V")
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [SEMICOLON]:
|
||||
pass
|
||||
self.match(SEMICOLON)
|
||||
elif la1 and la1 in [COMMA]:
|
||||
pass
|
||||
self.match(COMMA)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
self.rpn += struct.pack("<BBH", ptgAttr, 0x02, 0) # tAttrIf
|
||||
pos0 = len(self.rpn) - 2
|
||||
self.expr(arg_type)
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [SEMICOLON]:
|
||||
pass
|
||||
self.match(SEMICOLON)
|
||||
elif la1 and la1 in [COMMA]:
|
||||
pass
|
||||
self.match(COMMA)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
self.rpn += struct.pack("<BBH", ptgAttr, 0x08, 0) # tAttrSkip
|
||||
pos1 = len(self.rpn) - 2
|
||||
self.rpn = self.rpn[:pos0] + struct.pack("<H", pos1-pos0) + self.rpn[pos0+2:]
|
||||
self.expr(arg_type)
|
||||
self.match(RP)
|
||||
self.rpn += struct.pack("<BBH", ptgAttr, 0x08, 3) # tAttrSkip
|
||||
self.rpn += struct.pack("<BBH", ptgFuncVarR, 3, 1) # 3 = nargs, 1 = IF func
|
||||
pos2 = len(self.rpn)
|
||||
self.rpn = self.rpn[:pos1] + struct.pack("<H", pos2-(pos1+2)-1) + self.rpn[pos1+2:]
|
||||
elif la1 and la1 in [FUNC_CHOOSE]:
|
||||
pass
|
||||
self.match(FUNC_CHOOSE)
|
||||
arg_type = b"R"
|
||||
rpn_chunks = []
|
||||
self.match(LP)
|
||||
self.expr("V")
|
||||
rpn_start = len(self.rpn)
|
||||
ref_markers = [len(self.sheet_references)]
|
||||
while True:
|
||||
if (self.LA(1)==COMMA or self.LA(1)==SEMICOLON):
|
||||
pass
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [SEMICOLON]:
|
||||
pass
|
||||
self.match(SEMICOLON)
|
||||
elif la1 and la1 in [COMMA]:
|
||||
pass
|
||||
self.match(COMMA)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
mark = len(self.rpn)
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,SUB,LP,REF2D]:
|
||||
pass
|
||||
self.expr(arg_type)
|
||||
elif la1 and la1 in [RP,COMMA,SEMICOLON]:
|
||||
pass
|
||||
self.rpn += struct.pack("B", ptgMissArg)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
rpn_chunks.append(self.rpn[mark:])
|
||||
ref_markers.append(len(self.sheet_references))
|
||||
else:
|
||||
break
|
||||
|
||||
self.match(RP)
|
||||
self.rpn = self.rpn[:rpn_start]
|
||||
nc = len(rpn_chunks)
|
||||
chunklens = [len(chunk) for chunk in rpn_chunks]
|
||||
skiplens = [0] * nc
|
||||
skiplens[-1] = 3
|
||||
for ic in range(nc-1, 0, -1):
|
||||
skiplens[ic-1] = skiplens[ic] + chunklens[ic] + 4
|
||||
jump_pos = [2 * nc + 2]
|
||||
for ic in range(nc):
|
||||
jump_pos.append(jump_pos[-1] + chunklens[ic] + 4)
|
||||
chunk_shift = 2 * nc + 6 # size of tAttrChoose
|
||||
for ic in range(nc):
|
||||
for refx in range(ref_markers[ic], ref_markers[ic+1]):
|
||||
ref = self.sheet_references[refx]
|
||||
self.sheet_references[refx] = (ref[0], ref[1], ref[2] + chunk_shift)
|
||||
chunk_shift += 4 # size of tAttrSkip
|
||||
choose_rpn = []
|
||||
choose_rpn.append(struct.pack("<BBH", ptgAttr, 0x04, nc)) # 0x04 is tAttrChoose
|
||||
choose_rpn.append(struct.pack("<%dH" % (nc+1), *jump_pos))
|
||||
for ic in range(nc):
|
||||
choose_rpn.append(rpn_chunks[ic])
|
||||
choose_rpn.append(struct.pack("<BBH", ptgAttr, 0x08, skiplens[ic])) # 0x08 is tAttrSkip
|
||||
choose_rpn.append(struct.pack("<BBH", ptgFuncVarV, nc+1, 100)) # 100 is CHOOSE fn
|
||||
self.rpn += b"".join(choose_rpn)
|
||||
elif la1 and la1 in [LP]:
|
||||
pass
|
||||
self.match(LP)
|
||||
self.expr(arg_type)
|
||||
self.match(RP)
|
||||
self.rpn += struct.pack("B", ptgParen)
|
||||
else:
|
||||
if (self.LA(1)==INT_CONST) and (_tokenSet_0.member(self.LA(2))):
|
||||
pass
|
||||
int_tok = self.LT(1)
|
||||
self.match(INT_CONST)
|
||||
# print "**int_const", int_tok.text
|
||||
int_value = int(int_tok.text)
|
||||
if int_value <= 65535:
|
||||
self.rpn += struct.pack("<BH", ptgInt, int_value)
|
||||
else:
|
||||
self.rpn += struct.pack("<Bd", ptgNum, float(int_value))
|
||||
elif (self.LA(1)==REF2D) and (_tokenSet_0.member(self.LA(2))):
|
||||
pass
|
||||
ref2d_tok = self.LT(1)
|
||||
self.match(REF2D)
|
||||
# print "**ref2d %s %s" % (ref2d_tok.text, arg_type)
|
||||
r, c = Utils.cell_to_packed_rowcol(ref2d_tok.text)
|
||||
ptg = ptgRefR + _RVAdeltaRef[arg_type]
|
||||
self.rpn += struct.pack("<B2H", ptg, r, c)
|
||||
elif (self.LA(1)==REF2D) and (self.LA(2)==COLON):
|
||||
pass
|
||||
ref2d1_tok = self.LT(1)
|
||||
self.match(REF2D)
|
||||
self.match(COLON)
|
||||
ref2d2_tok = self.LT(1)
|
||||
self.match(REF2D)
|
||||
r1, c1 = Utils.cell_to_packed_rowcol(ref2d1_tok.text)
|
||||
r2, c2 = Utils.cell_to_packed_rowcol(ref2d2_tok.text)
|
||||
ptg = ptgAreaR + _RVAdeltaArea[arg_type]
|
||||
self.rpn += struct.pack("<B4H", ptg, r1, r2, c1, c2)
|
||||
elif (self.LA(1)==INT_CONST or self.LA(1)==NAME or self.LA(1)==QUOTENAME) and (self.LA(2)==COLON or self.LA(2)==BANG):
|
||||
pass
|
||||
sheet1=self.sheet()
|
||||
sheet2 = sheet1
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [COLON]:
|
||||
pass
|
||||
self.match(COLON)
|
||||
sheet2=self.sheet()
|
||||
elif la1 and la1 in [BANG]:
|
||||
pass
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
self.match(BANG)
|
||||
ref3d_ref2d = self.LT(1)
|
||||
self.match(REF2D)
|
||||
ptg = ptgRef3dR + _RVAdeltaRef[arg_type]
|
||||
rpn_ref2d = b""
|
||||
r1, c1 = Utils.cell_to_packed_rowcol(ref3d_ref2d.text)
|
||||
rpn_ref2d = struct.pack("<3H", 0x0000, r1, c1)
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [COLON]:
|
||||
pass
|
||||
self.match(COLON)
|
||||
ref3d_ref2d2 = self.LT(1)
|
||||
self.match(REF2D)
|
||||
ptg = ptgArea3dR + _RVAdeltaArea[arg_type]
|
||||
r2, c2 = Utils.cell_to_packed_rowcol(ref3d_ref2d2.text)
|
||||
rpn_ref2d = struct.pack("<5H", 0x0000, r1, r2, c1, c2)
|
||||
elif la1 and la1 in [EOF,EQ,NE,GT,LT,GE,LE,ADD,SUB,MUL,DIV,POWER,PERCENT,RP,COMMA,SEMICOLON,CONCAT]:
|
||||
pass
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
self.rpn += struct.pack("<B", ptg)
|
||||
self.sheet_references.append((sheet1, sheet2, len(self.rpn)))
|
||||
self.rpn += rpn_ref2d
|
||||
elif (self.LA(1)==NAME) and (_tokenSet_0.member(self.LA(2))):
|
||||
name_tok = self.LT(1)
|
||||
self.match(NAME)
|
||||
raise Exception("[formula] found unexpected NAME token (%r)" % name_tok.txt)
|
||||
# #### TODO: handle references to defined names here
|
||||
elif (self.LA(1)==NAME) and (self.LA(2)==LP):
|
||||
func_tok = self.LT(1)
|
||||
self.match(NAME)
|
||||
func_toku = func_tok.text.upper()
|
||||
if func_toku in all_funcs_by_name:
|
||||
(opcode,
|
||||
min_argc,
|
||||
max_argc,
|
||||
func_type,
|
||||
arg_type_str) = all_funcs_by_name[func_toku]
|
||||
arg_type_list = list(arg_type_str)
|
||||
else:
|
||||
raise Exception("[formula] unknown function (%s)" % func_tok.text)
|
||||
# print "**func_tok1 %s %s" % (func_toku, func_type)
|
||||
xcall = opcode < 0
|
||||
if xcall:
|
||||
# The name of the add-in function is passed as the 1st arg
|
||||
# of the hidden XCALL function
|
||||
self.xcall_references.append((func_toku, len(self.rpn) + 1))
|
||||
self.rpn += struct.pack("<BHHH",
|
||||
ptgNameXR,
|
||||
0xadde, # ##PATCHME## index to REF entry in EXTERNSHEET record
|
||||
0xefbe, # ##PATCHME## one-based index to EXTERNNAME record
|
||||
0x0000) # unused
|
||||
self.match(LP)
|
||||
arg_count=self.expr_list(arg_type_list, min_argc, max_argc)
|
||||
self.match(RP)
|
||||
if arg_count > max_argc or arg_count < min_argc:
|
||||
raise Exception("%d parameters for function: %s" % (arg_count, func_tok.text))
|
||||
if xcall:
|
||||
func_ptg = ptgFuncVarR + _RVAdelta[func_type]
|
||||
self.rpn += struct.pack("<2BH", func_ptg, arg_count + 1, 255) # 255 is magic XCALL function
|
||||
elif min_argc == max_argc:
|
||||
func_ptg = ptgFuncR + _RVAdelta[func_type]
|
||||
self.rpn += struct.pack("<BH", func_ptg, opcode)
|
||||
elif arg_count == 1 and func_tok.text.upper() == "SUM":
|
||||
self.rpn += struct.pack("<BBH", ptgAttr, 0x10, 0) # tAttrSum
|
||||
else:
|
||||
func_ptg = ptgFuncVarR + _RVAdelta[func_type]
|
||||
self.rpn += struct.pack("<2BH", func_ptg, arg_count, opcode)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
def sheet(self):
|
||||
ref = None
|
||||
sheet_ref_name = None
|
||||
sheet_ref_int = None
|
||||
sheet_ref_quote = None
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [NAME]:
|
||||
sheet_ref_name = self.LT(1)
|
||||
self.match(NAME)
|
||||
ref = sheet_ref_name.text
|
||||
elif la1 and la1 in [INT_CONST]:
|
||||
sheet_ref_int = self.LT(1)
|
||||
self.match(INT_CONST)
|
||||
ref = sheet_ref_int.text
|
||||
elif la1 and la1 in [QUOTENAME]:
|
||||
sheet_ref_quote = self.LT(1)
|
||||
self.match(QUOTENAME)
|
||||
ref = sheet_ref_quote.text[1:-1].replace("''", "'")
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
return ref
|
||||
|
||||
def expr_list(self,
|
||||
arg_type_list, min_argc, max_argc
|
||||
):
|
||||
arg_cnt = None
|
||||
|
||||
arg_cnt = 0
|
||||
arg_type = arg_type_list[arg_cnt]
|
||||
# print "**expr_list1[%d] req=%s" % (arg_cnt, arg_type)
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,SUB,LP,REF2D]:
|
||||
pass
|
||||
self.expr(arg_type)
|
||||
arg_cnt += 1
|
||||
while True:
|
||||
if (self.LA(1)==COMMA or self.LA(1)==SEMICOLON):
|
||||
pass
|
||||
if arg_cnt < len(arg_type_list):
|
||||
arg_type = arg_type_list[arg_cnt]
|
||||
else:
|
||||
arg_type = arg_type_list[-1]
|
||||
if arg_type == "+":
|
||||
arg_type = arg_type_list[-2]
|
||||
# print "**expr_list2[%d] req=%s" % (arg_cnt, arg_type)
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [SEMICOLON]:
|
||||
pass
|
||||
self.match(SEMICOLON)
|
||||
elif la1 and la1 in [COMMA]:
|
||||
pass
|
||||
self.match(COMMA)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
la1 = self.LA(1)
|
||||
if False:
|
||||
pass
|
||||
elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,SUB,LP,REF2D]:
|
||||
pass
|
||||
self.expr(arg_type)
|
||||
elif la1 and la1 in [RP,COMMA,SEMICOLON]:
|
||||
pass
|
||||
self.rpn += struct.pack("B", ptgMissArg)
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
arg_cnt += 1
|
||||
else:
|
||||
break
|
||||
|
||||
elif la1 and la1 in [RP]:
|
||||
pass
|
||||
else:
|
||||
raise antlr.NoViableAltException(self.LT(1), self.getFilename())
|
||||
|
||||
return arg_cnt
|
||||
|
||||
|
||||
_tokenNames = [
|
||||
"<0>",
|
||||
"EOF",
|
||||
"<2>",
|
||||
"NULL_TREE_LOOKAHEAD",
|
||||
"TRUE_CONST",
|
||||
"FALSE_CONST",
|
||||
"STR_CONST",
|
||||
"NUM_CONST",
|
||||
"INT_CONST",
|
||||
"FUNC_IF",
|
||||
"FUNC_CHOOSE",
|
||||
"NAME",
|
||||
"QUOTENAME",
|
||||
"EQ",
|
||||
"NE",
|
||||
"GT",
|
||||
"LT",
|
||||
"GE",
|
||||
"LE",
|
||||
"ADD",
|
||||
"SUB",
|
||||
"MUL",
|
||||
"DIV",
|
||||
"POWER",
|
||||
"PERCENT",
|
||||
"LP",
|
||||
"RP",
|
||||
"LB",
|
||||
"RB",
|
||||
"COLON",
|
||||
"COMMA",
|
||||
"SEMICOLON",
|
||||
"REF2D",
|
||||
"REF2D_R1C1",
|
||||
"BANG",
|
||||
"CONCAT"
|
||||
]
|
||||
|
||||
|
||||
### generate bit set
|
||||
def mk_tokenSet_0():
|
||||
### var1
|
||||
data = [ 37681618946, 0]
|
||||
return data
|
||||
_tokenSet_0 = antlr.BitSet(mk_tokenSet_0())
|
||||
|
||||
@@ -1,859 +0,0 @@
|
||||
""" lots of Excel Magic Numbers """
|
||||
|
||||
# Boundaries BIFF8+
|
||||
|
||||
MAX_ROW = 65536
|
||||
MAX_COL = 256
|
||||
|
||||
|
||||
biff_records = {
|
||||
0x0000: "DIMENSIONS",
|
||||
0x0001: "BLANK",
|
||||
0x0002: "INTEGER",
|
||||
0x0003: "NUMBER",
|
||||
0x0004: "LABEL",
|
||||
0x0005: "BOOLERR",
|
||||
0x0006: "FORMULA",
|
||||
0x0007: "STRING",
|
||||
0x0008: "ROW",
|
||||
0x0009: "BOF",
|
||||
0x000A: "EOF",
|
||||
0x000B: "INDEX",
|
||||
0x000C: "CALCCOUNT",
|
||||
0x000D: "CALCMODE",
|
||||
0x000E: "PRECISION",
|
||||
0x000F: "REFMODE",
|
||||
0x0010: "DELTA",
|
||||
0x0011: "ITERATION",
|
||||
0x0012: "PROTECT",
|
||||
0x0013: "PASSWORD",
|
||||
0x0014: "HEADER",
|
||||
0x0015: "FOOTER",
|
||||
0x0016: "EXTERNCOUNT",
|
||||
0x0017: "EXTERNSHEET",
|
||||
0x0018: "NAME",
|
||||
0x0019: "WINDOWPROTECT",
|
||||
0x001A: "VERTICALPAGEBREAKS",
|
||||
0x001B: "HORIZONTALPAGEBREAKS",
|
||||
0x001C: "NOTE",
|
||||
0x001D: "SELECTION",
|
||||
0x001E: "FORMAT",
|
||||
0x001F: "FORMATCOUNT",
|
||||
0x0020: "COLUMNDEFAULT",
|
||||
0x0021: "ARRAY",
|
||||
0x0022: "1904",
|
||||
0x0023: "EXTERNNAME",
|
||||
0x0024: "COLWIDTH",
|
||||
0x0025: "DEFAULTROWHEIGHT",
|
||||
0x0026: "LEFTMARGIN",
|
||||
0x0027: "RIGHTMARGIN",
|
||||
0x0028: "TOPMARGIN",
|
||||
0x0029: "BOTTOMMARGIN",
|
||||
0x002A: "PRINTHEADERS",
|
||||
0x002B: "PRINTGRIDLINES",
|
||||
0x002F: "FILEPASS",
|
||||
0x0031: "FONT",
|
||||
0x0036: "TABLE",
|
||||
0x003C: "CONTINUE",
|
||||
0x003D: "WINDOW1",
|
||||
0x003E: "WINDOW2",
|
||||
0x0040: "BACKUP",
|
||||
0x0041: "PANE",
|
||||
0x0042: "CODEPAGE",
|
||||
0x0043: "XF",
|
||||
0x0044: "IXFE",
|
||||
0x0045: "EFONT",
|
||||
0x004D: "PLS",
|
||||
0x0050: "DCON",
|
||||
0x0051: "DCONREF",
|
||||
0x0053: "DCONNAME",
|
||||
0x0055: "DEFCOLWIDTH",
|
||||
0x0056: "BUILTINFMTCNT",
|
||||
0x0059: "XCT",
|
||||
0x005A: "CRN",
|
||||
0x005B: "FILESHARING",
|
||||
0x005C: "WRITEACCESS",
|
||||
0x005D: "OBJ",
|
||||
0x005E: "UNCALCED",
|
||||
0x005F: "SAFERECALC",
|
||||
0x0060: "TEMPLATE",
|
||||
0x0063: "OBJPROTECT",
|
||||
0x007D: "COLINFO",
|
||||
0x007E: "RK",
|
||||
0x007F: "IMDATA",
|
||||
0x0080: "GUTS",
|
||||
0x0081: "WSBOOL",
|
||||
0x0082: "GRIDSET",
|
||||
0x0083: "HCENTER",
|
||||
0x0084: "VCENTER",
|
||||
0x0085: "BOUNDSHEET",
|
||||
0x0086: "WRITEPROT",
|
||||
0x0087: "ADDIN",
|
||||
0x0088: "EDG",
|
||||
0x0089: "PUB",
|
||||
0x008C: "COUNTRY",
|
||||
0x008D: "HIDEOBJ",
|
||||
0x008E: "BUNDLESOFFSET",
|
||||
0x008F: "BUNDLEHEADER",
|
||||
0x0090: "SORT",
|
||||
0x0091: "SUB",
|
||||
0x0092: "PALETTE",
|
||||
0x0093: "STYLE",
|
||||
0x0094: "LHRECORD",
|
||||
0x0095: "LHNGRAPH",
|
||||
0x0096: "SOUND",
|
||||
0x0098: "LPR",
|
||||
0x0099: "STANDARDWIDTH",
|
||||
0x009A: "FNGROUPNAME",
|
||||
0x009B: "FILTERMODE",
|
||||
0x009C: "FNGROUPCOUNT",
|
||||
0x009D: "AUTOFILTERINFO",
|
||||
0x009E: "AUTOFILTER",
|
||||
0x00A0: "SCL",
|
||||
0x00A1: "SETUP",
|
||||
0x00A9: "COORDLIST",
|
||||
0x00AB: "GCW",
|
||||
0x00AE: "SCENMAN",
|
||||
0x00AF: "SCENARIO",
|
||||
0x00B0: "SXVIEW",
|
||||
0x00B1: "SXVD",
|
||||
0x00B2: "SXVI",
|
||||
0x00B4: "SXIVD",
|
||||
0x00B5: "SXLI",
|
||||
0x00B6: "SXPI",
|
||||
0x00B8: "DOCROUTE",
|
||||
0x00B9: "RECIPNAME",
|
||||
0x00BC: "SHRFMLA",
|
||||
0x00BD: "MULRK",
|
||||
0x00BE: "MULBLANK",
|
||||
0x00C1: "MMS",
|
||||
0x00C2: "ADDMENU",
|
||||
0x00C3: "DELMENU",
|
||||
0x00C5: "SXDI",
|
||||
0x00C6: "SXDB",
|
||||
0x00C7: "SXFIELD",
|
||||
0x00C8: "SXINDEXLIST",
|
||||
0x00C9: "SXDOUBLE",
|
||||
0x00CD: "SXSTRING",
|
||||
0x00CE: "SXDATETIME",
|
||||
0x00D0: "SXTBL",
|
||||
0x00D1: "SXTBRGITEM",
|
||||
0x00D2: "SXTBPG",
|
||||
0x00D3: "OBPROJ",
|
||||
0x00D5: "SXIDSTM",
|
||||
0x00D6: "RSTRING",
|
||||
0x00D7: "DBCELL",
|
||||
0x00DA: "BOOKBOOL",
|
||||
0x00DC: "SXEXT|PARAMQRY",
|
||||
0x00DD: "SCENPROTECT",
|
||||
0x00DE: "OLESIZE",
|
||||
0x00DF: "UDDESC",
|
||||
0x00E0: "XF",
|
||||
0x00E1: "INTERFACEHDR",
|
||||
0x00E2: "INTERFACEEND",
|
||||
0x00E3: "SXVS",
|
||||
0x00E5: "MERGEDCELLS",
|
||||
0x00E9: "BITMAP",
|
||||
0x00EB: "MSODRAWINGGROUP",
|
||||
0x00EC: "MSODRAWING",
|
||||
0x00ED: "MSODRAWINGSELECTION",
|
||||
0x00F0: "SXRULE",
|
||||
0x00F1: "SXEX",
|
||||
0x00F2: "SXFILT",
|
||||
0x00F6: "SXNAME",
|
||||
0x00F7: "SXSELECT",
|
||||
0x00F8: "SXPAIR",
|
||||
0x00F9: "SXFMLA",
|
||||
0x00FB: "SXFORMAT",
|
||||
0x00FC: "SST",
|
||||
0x00FD: "LABELSST",
|
||||
0x00FF: "EXTSST",
|
||||
0x0100: "SXVDEX",
|
||||
0x0103: "SXFORMULA",
|
||||
0x0122: "SXDBEX",
|
||||
0x0137: "CHTRINSERT",
|
||||
0x0138: "CHTRINFO",
|
||||
0x013B: "CHTRCELLCONTENT",
|
||||
0x013D: "TABID",
|
||||
0x0140: "CHTRMOVERANGE",
|
||||
0x014D: "CHTRINSERTTAB",
|
||||
0x015F: "LABELRANGES",
|
||||
0x0160: "USESELFS",
|
||||
0x0161: "DSF",
|
||||
0x0162: "XL5MODIFY",
|
||||
0x0196: "CHTRHEADER",
|
||||
0x01A9: "USERBVIEW",
|
||||
0x01AA: "USERSVIEWBEGIN",
|
||||
0x01AB: "USERSVIEWEND",
|
||||
0x01AD: "QSI",
|
||||
0x01AE: "SUPBOOK",
|
||||
0x01AF: "PROT4REV",
|
||||
0x01B0: "CONDFMT",
|
||||
0x01B1: "CF",
|
||||
0x01B2: "DVAL",
|
||||
0x01B5: "DCONBIN",
|
||||
0x01B6: "TXO",
|
||||
0x01B7: "REFRESHALL",
|
||||
0x01B8: "HLINK",
|
||||
0x01BA: "CODENAME",
|
||||
0x01BB: "SXFDBTYPE",
|
||||
0x01BC: "PROT4REVPASS",
|
||||
0x01BE: "DV",
|
||||
0x01C0: "XL9FILE",
|
||||
0x01C1: "RECALCID",
|
||||
0x0200: "DIMENSIONS",
|
||||
0x0201: "BLANK",
|
||||
0x0203: "NUMBER",
|
||||
0x0204: "LABEL",
|
||||
0x0205: "BOOLERR",
|
||||
0x0206: "FORMULA",
|
||||
0x0207: "STRING",
|
||||
0x0208: "ROW",
|
||||
0x0209: "BOF",
|
||||
0x020B: "INDEX",
|
||||
0x0218: "NAME",
|
||||
0x0221: "ARRAY",
|
||||
0x0223: "EXTERNNAME",
|
||||
0x0225: "DEFAULTROWHEIGHT",
|
||||
0x0231: "FONT",
|
||||
0x0236: "TABLE",
|
||||
0x023E: "WINDOW2",
|
||||
0x0243: "XF",
|
||||
0x027E: "RK",
|
||||
0x0293: "STYLE",
|
||||
0x0406: "FORMULA",
|
||||
0x0409: "BOF",
|
||||
0x041E: "FORMAT",
|
||||
0x0443: "XF",
|
||||
0x04BC: "SHRFMLA",
|
||||
0x0800: "SCREENTIP",
|
||||
0x0803: "WEBQRYSETTINGS",
|
||||
0x0804: "WEBQRYTABLES",
|
||||
0x0809: "BOF",
|
||||
0x0862: "SHEETLAYOUT",
|
||||
0x0867: "SHEETPROTECTION",
|
||||
0x1001: "UNITS",
|
||||
0x1002: "ChartChart",
|
||||
0x1003: "ChartSeries",
|
||||
0x1006: "ChartDataformat",
|
||||
0x1007: "ChartLineformat",
|
||||
0x1009: "ChartMarkerformat",
|
||||
0x100A: "ChartAreaformat",
|
||||
0x100B: "ChartPieformat",
|
||||
0x100C: "ChartAttachedlabel",
|
||||
0x100D: "ChartSeriestext",
|
||||
0x1014: "ChartChartformat",
|
||||
0x1015: "ChartLegend",
|
||||
0x1016: "ChartSerieslist",
|
||||
0x1017: "ChartBar",
|
||||
0x1018: "ChartLine",
|
||||
0x1019: "ChartPie",
|
||||
0x101A: "ChartArea",
|
||||
0x101B: "ChartScatter",
|
||||
0x101C: "ChartChartline",
|
||||
0x101D: "ChartAxis",
|
||||
0x101E: "ChartTick",
|
||||
0x101F: "ChartValuerange",
|
||||
0x1020: "ChartCatserrange",
|
||||
0x1021: "ChartAxislineformat",
|
||||
0x1022: "ChartFormatlink",
|
||||
0x1024: "ChartDefaulttext",
|
||||
0x1025: "ChartText",
|
||||
0x1026: "ChartFontx",
|
||||
0x1027: "ChartObjectLink",
|
||||
0x1032: "ChartFrame",
|
||||
0x1033: "BEGIN",
|
||||
0x1034: "END",
|
||||
0x1035: "ChartPlotarea",
|
||||
0x103A: "Chart3D",
|
||||
0x103C: "ChartPicf",
|
||||
0x103D: "ChartDropbar",
|
||||
0x103E: "ChartRadar",
|
||||
0x103F: "ChartSurface",
|
||||
0x1040: "ChartRadararea",
|
||||
0x1041: "ChartAxisparent",
|
||||
0x1043: "ChartLegendxn",
|
||||
0x1044: "ChartShtprops",
|
||||
0x1045: "ChartSertocrt",
|
||||
0x1046: "ChartAxesused",
|
||||
0x1048: "ChartSbaseref",
|
||||
0x104A: "ChartSerparent",
|
||||
0x104B: "ChartSerauxtrend",
|
||||
0x104E: "ChartIfmt",
|
||||
0x104F: "ChartPos",
|
||||
0x1050: "ChartAlruns",
|
||||
0x1051: "ChartAI",
|
||||
0x105B: "ChartSerauxerrbar",
|
||||
0x105D: "ChartSerfmt",
|
||||
0x105F: "Chart3DDataFormat",
|
||||
0x1060: "ChartFbi",
|
||||
0x1061: "ChartBoppop",
|
||||
0x1062: "ChartAxcext",
|
||||
0x1063: "ChartDat",
|
||||
0x1064: "ChartPlotgrowth",
|
||||
0x1065: "ChartSiindex",
|
||||
0x1066: "ChartGelframe",
|
||||
0x1067: "ChartBoppcustom",
|
||||
0xFFFF: ""
|
||||
}
|
||||
|
||||
|
||||
all_funcs_by_name = {
|
||||
# Includes Analysis ToolPak aka ATP aka add-in aka xcall functions,
|
||||
# distinguished by -ve opcode.
|
||||
# name: (opcode, min # args, max # args, func return type, func arg types)
|
||||
# + in func arg types means more of the same.
|
||||
'ABS' : ( 24, 1, 1, 'V', 'V'),
|
||||
'ACCRINT' : ( -1, 6, 7, 'V', 'VVVVVVV'),
|
||||
'ACCRINTM' : ( -1, 3, 5, 'V', 'VVVVV'),
|
||||
'ACOS' : ( 99, 1, 1, 'V', 'V'),
|
||||
'ACOSH' : (233, 1, 1, 'V', 'V'),
|
||||
'ADDRESS' : (219, 2, 5, 'V', 'VVVVV'),
|
||||
'AMORDEGRC' : ( -1, 7, 7, 'V', 'VVVVVVV'),
|
||||
'AMORLINC' : ( -1, 7, 7, 'V', 'VVVVVVV'),
|
||||
'AND' : ( 36, 1, 30, 'V', 'D+'),
|
||||
'AREAS' : ( 75, 1, 1, 'V', 'R'),
|
||||
'ASC' : (214, 1, 1, 'V', 'V'),
|
||||
'ASIN' : ( 98, 1, 1, 'V', 'V'),
|
||||
'ASINH' : (232, 1, 1, 'V', 'V'),
|
||||
'ATAN' : ( 18, 1, 1, 'V', 'V'),
|
||||
'ATAN2' : ( 97, 2, 2, 'V', 'VV'),
|
||||
'ATANH' : (234, 1, 1, 'V', 'V'),
|
||||
'AVEDEV' : (269, 1, 30, 'V', 'D+'),
|
||||
'AVERAGE' : ( 5, 1, 30, 'V', 'D+'),
|
||||
'AVERAGEA' : (361, 1, 30, 'V', 'D+'),
|
||||
'BAHTTEXT' : (368, 1, 1, 'V', 'V'),
|
||||
'BESSELI' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'BESSELJ' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'BESSELK' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'BESSELY' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'BETADIST' : (270, 3, 5, 'V', 'VVVVV'),
|
||||
'BETAINV' : (272, 3, 5, 'V', 'VVVVV'),
|
||||
'BIN2DEC' : ( -1, 1, 1, 'V', 'V'),
|
||||
'BIN2HEX' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'BIN2OCT' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'BINOMDIST' : (273, 4, 4, 'V', 'VVVV'),
|
||||
'CEILING' : (288, 2, 2, 'V', 'VV'),
|
||||
'CELL' : (125, 1, 2, 'V', 'VR'),
|
||||
'CHAR' : (111, 1, 1, 'V', 'V'),
|
||||
'CHIDIST' : (274, 2, 2, 'V', 'VV'),
|
||||
'CHIINV' : (275, 2, 2, 'V', 'VV'),
|
||||
'CHITEST' : (306, 2, 2, 'V', 'AA'),
|
||||
'CHOOSE' : (100, 2, 30, 'R', 'VR+'),
|
||||
'CLEAN' : (162, 1, 1, 'V', 'V'),
|
||||
'CODE' : (121, 1, 1, 'V', 'V'),
|
||||
'COLUMN' : ( 9, 0, 1, 'V', 'R'),
|
||||
'COLUMNS' : ( 77, 1, 1, 'V', 'R'),
|
||||
'COMBIN' : (276, 2, 2, 'V', 'VV'),
|
||||
'COMPLEX' : ( -1, 2, 3, 'V', 'VVV'),
|
||||
'CONCATENATE' : (336, 1, 30, 'V', 'V+'),
|
||||
'CONFIDENCE' : (277, 3, 3, 'V', 'VVV'),
|
||||
'CONVERT' : ( -1, 3, 3, 'V', 'VVV'),
|
||||
'CORREL' : (307, 2, 2, 'V', 'AA'),
|
||||
'COS' : ( 16, 1, 1, 'V', 'V'),
|
||||
'COSH' : (230, 1, 1, 'V', 'V'),
|
||||
'COUNT' : ( 0, 1, 30, 'V', 'D+'),
|
||||
'COUNTA' : (169, 1, 30, 'V', 'D+'),
|
||||
'COUNTBLANK' : (347, 1, 1, 'V', 'R'),
|
||||
'COUNTIF' : (346, 2, 2, 'V', 'RV'),
|
||||
'COUPDAYBS' : ( -1, 3, 5, 'V', 'VVVVV'),
|
||||
'COUPDAYS' : ( -1, 3, 5, 'V', 'VVVVV'),
|
||||
'COUPDAYSNC' : ( -1, 3, 5, 'V', 'VVVVV'),
|
||||
'COUPNCD' : ( -1, 3, 5, 'V', 'VVVVV'),
|
||||
'COUPNUM' : ( -1, 3, 5, 'V', 'VVVVV'),
|
||||
'COUPPCD' : ( -1, 3, 5, 'V', 'VVVVV'),
|
||||
'COVAR' : (308, 2, 2, 'V', 'AA'),
|
||||
'CRITBINOM' : (278, 3, 3, 'V', 'VVV'),
|
||||
'CUMIPMT' : ( -1, 6, 6, 'V', 'VVVVVV'),
|
||||
'CUMPRINC' : ( -1, 6, 6, 'V', 'VVVVVV'),
|
||||
'DATE' : ( 65, 3, 3, 'V', 'VVV'),
|
||||
'DATEDIF' : (351, 3, 3, 'V', 'VVV'),
|
||||
'DATEVALUE' : (140, 1, 1, 'V', 'V'),
|
||||
'DAVERAGE' : ( 42, 3, 3, 'V', 'RRR'),
|
||||
'DAY' : ( 67, 1, 1, 'V', 'V'),
|
||||
'DAYS360' : (220, 2, 3, 'V', 'VVV'),
|
||||
'DB' : (247, 4, 5, 'V', 'VVVVV'),
|
||||
'DBCS' : (215, 1, 1, 'V', 'V'),
|
||||
'DCOUNT' : ( 40, 3, 3, 'V', 'RRR'),
|
||||
'DCOUNTA' : (199, 3, 3, 'V', 'RRR'),
|
||||
'DDB' : (144, 4, 5, 'V', 'VVVVV'),
|
||||
'DEC2BIN' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'DEC2HEX' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'DEC2OCT' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'DEGREES' : (343, 1, 1, 'V', 'V'),
|
||||
'DELTA' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'DEVSQ' : (318, 1, 30, 'V', 'D+'),
|
||||
'DGET' : (235, 3, 3, 'V', 'RRR'),
|
||||
'DISC' : ( -1, 4, 5, 'V', 'VVVVV'),
|
||||
'DMAX' : ( 44, 3, 3, 'V', 'RRR'),
|
||||
'DMIN' : ( 43, 3, 3, 'V', 'RRR'),
|
||||
'DOLLAR' : ( 13, 1, 2, 'V', 'VV'),
|
||||
'DOLLARDE' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'DOLLARFR' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'DPRODUCT' : (189, 3, 3, 'V', 'RRR'),
|
||||
'DSTDEV' : ( 45, 3, 3, 'V', 'RRR'),
|
||||
'DSTDEVP' : (195, 3, 3, 'V', 'RRR'),
|
||||
'DSUM' : ( 41, 3, 3, 'V', 'RRR'),
|
||||
'DURATION' : ( -1, 5, 6, 'V', 'VVVVVV'),
|
||||
'DVAR' : ( 47, 3, 3, 'V', 'RRR'),
|
||||
'DVARP' : (196, 3, 3, 'V', 'RRR'),
|
||||
'EDATE' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'EFFECT' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'EOMONTH' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'ERF' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'ERFC' : ( -1, 1, 1, 'V', 'V'),
|
||||
'ERROR.TYPE' : (261, 1, 1, 'V', 'V'),
|
||||
'EVEN' : (279, 1, 1, 'V', 'V'),
|
||||
'EXACT' : (117, 2, 2, 'V', 'VV'),
|
||||
'EXP' : ( 21, 1, 1, 'V', 'V'),
|
||||
'EXPONDIST' : (280, 3, 3, 'V', 'VVV'),
|
||||
'FACT' : (184, 1, 1, 'V', 'V'),
|
||||
'FACTDOUBLE' : ( -1, 1, 1, 'V', 'V'),
|
||||
'FALSE' : ( 35, 0, 0, 'V', '-'),
|
||||
'FDIST' : (281, 3, 3, 'V', 'VVV'),
|
||||
'FIND' : (124, 2, 3, 'V', 'VVV'),
|
||||
'FINDB' : (205, 2, 3, 'V', 'VVV'),
|
||||
'FINV' : (282, 3, 3, 'V', 'VVV'),
|
||||
'FISHER' : (283, 1, 1, 'V', 'V'),
|
||||
'FISHERINV' : (284, 1, 1, 'V', 'V'),
|
||||
'FIXED' : ( 14, 2, 3, 'V', 'VVV'),
|
||||
'FLOOR' : (285, 2, 2, 'V', 'VV'),
|
||||
'FORECAST' : (309, 3, 3, 'V', 'VAA'),
|
||||
'FREQUENCY' : (252, 2, 2, 'A', 'RR'),
|
||||
'FTEST' : (310, 2, 2, 'V', 'AA'),
|
||||
'FV' : ( 57, 3, 5, 'V', 'VVVVV'),
|
||||
'FVSCHEDULE' : ( -1, 2, 2, 'V', 'VA'),
|
||||
'GAMMADIST' : (286, 4, 4, 'V', 'VVVV'),
|
||||
'GAMMAINV' : (287, 3, 3, 'V', 'VVV'),
|
||||
'GAMMALN' : (271, 1, 1, 'V', 'V'),
|
||||
'GCD' : ( -1, 1, 29, 'V', 'V+'),
|
||||
'GEOMEAN' : (319, 1, 30, 'V', 'D+'),
|
||||
'GESTEP' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'GETPIVOTDATA': (358, 2, 30, 'A', 'VAV+'),
|
||||
'GROWTH' : ( 52, 1, 4, 'A', 'RRRV'),
|
||||
'HARMEAN' : (320, 1, 30, 'V', 'D+'),
|
||||
'HEX2BIN' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'HEX2DEC' : ( -1, 1, 1, 'V', 'V'),
|
||||
'HEX2OCT' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'HLOOKUP' : (101, 3, 4, 'V', 'VRRV'),
|
||||
'HOUR' : ( 71, 1, 1, 'V', 'V'),
|
||||
'HYPERLINK' : (359, 1, 2, 'V', 'VV'),
|
||||
'HYPGEOMDIST' : (289, 4, 4, 'V', 'VVVV'),
|
||||
'IF' : ( 1, 2, 3, 'R', 'VRR'),
|
||||
'IMABS' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMAGINARY' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMARGUMENT' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMCONJUGATE' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMCOS' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMDIV' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'IMEXP' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMLN' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMLOG10' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMLOG2' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMPOWER' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'IMPRODUCT' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'IMREAL' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMSIN' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMSQRT' : ( -1, 1, 1, 'V', 'V'),
|
||||
'IMSUB' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'IMSUM' : ( -1, 1, 29, 'V', 'V+'),
|
||||
'INDEX' : ( 29, 2, 4, 'R', 'RVVV'),
|
||||
'INDIRECT' : (148, 1, 2, 'R', 'VV'),
|
||||
'INFO' : (244, 1, 1, 'V', 'V'),
|
||||
'INT' : ( 25, 1, 1, 'V', 'V'),
|
||||
'INTERCEPT' : (311, 2, 2, 'V', 'AA'),
|
||||
'INTRATE' : ( -1, 4, 5, 'V', 'VVVVV'),
|
||||
'IPMT' : (167, 4, 6, 'V', 'VVVVVV'),
|
||||
'IRR' : ( 62, 1, 2, 'V', 'RV'),
|
||||
'ISBLANK' : (129, 1, 1, 'V', 'V'),
|
||||
'ISERR' : (126, 1, 1, 'V', 'V'),
|
||||
'ISERROR' : ( 3, 1, 1, 'V', 'V'),
|
||||
'ISEVEN' : ( -1, 1, 1, 'V', 'V'),
|
||||
'ISLOGICAL' : (198, 1, 1, 'V', 'V'),
|
||||
'ISNA' : ( 2, 1, 1, 'V', 'V'),
|
||||
'ISNONTEXT' : (190, 1, 1, 'V', 'V'),
|
||||
'ISNUMBER' : (128, 1, 1, 'V', 'V'),
|
||||
'ISODD' : ( -1, 1, 1, 'V', 'V'),
|
||||
'ISPMT' : (350, 4, 4, 'V', 'VVVV'),
|
||||
'ISREF' : (105, 1, 1, 'V', 'R'),
|
||||
'ISTEXT' : (127, 1, 1, 'V', 'V'),
|
||||
'KURT' : (322, 1, 30, 'V', 'D+'),
|
||||
'LARGE' : (325, 2, 2, 'V', 'RV'),
|
||||
'LCM' : ( -1, 1, 29, 'V', 'V+'),
|
||||
'LEFT' : (115, 1, 2, 'V', 'VV'),
|
||||
'LEFTB' : (208, 1, 2, 'V', 'VV'),
|
||||
'LEN' : ( 32, 1, 1, 'V', 'V'),
|
||||
'LENB' : (211, 1, 1, 'V', 'V'),
|
||||
'LINEST' : ( 49, 1, 4, 'A', 'RRVV'),
|
||||
'LN' : ( 22, 1, 1, 'V', 'V'),
|
||||
'LOG' : (109, 1, 2, 'V', 'VV'),
|
||||
'LOG10' : ( 23, 1, 1, 'V', 'V'),
|
||||
'LOGEST' : ( 51, 1, 4, 'A', 'RRVV'),
|
||||
'LOGINV' : (291, 3, 3, 'V', 'VVV'),
|
||||
'LOGNORMDIST' : (290, 3, 3, 'V', 'VVV'),
|
||||
'LOOKUP' : ( 28, 2, 3, 'V', 'VRR'),
|
||||
'LOWER' : (112, 1, 1, 'V', 'V'),
|
||||
'MATCH' : ( 64, 2, 3, 'V', 'VRR'),
|
||||
'MAX' : ( 7, 1, 30, 'V', 'D+'),
|
||||
'MAXA' : (362, 1, 30, 'V', 'D+'),
|
||||
'MDETERM' : (163, 1, 1, 'V', 'A'),
|
||||
'MDURATION' : ( -1, 5, 6, 'V', 'VVVVVV'),
|
||||
'MEDIAN' : (227, 1, 30, 'V', 'D+'),
|
||||
'MID' : ( 31, 3, 3, 'V', 'VVV'),
|
||||
'MIDB' : (210, 3, 3, 'V', 'VVV'),
|
||||
'MIN' : ( 6, 1, 30, 'V', 'D+'),
|
||||
'MINA' : (363, 1, 30, 'V', 'D+'),
|
||||
'MINUTE' : ( 72, 1, 1, 'V', 'V'),
|
||||
'MINVERSE' : (164, 1, 1, 'A', 'A'),
|
||||
'MIRR' : ( 61, 3, 3, 'V', 'RVV'),
|
||||
'MMULT' : (165, 2, 2, 'A', 'AA'),
|
||||
'MOD' : ( 39, 2, 2, 'V', 'VV'),
|
||||
'MODE' : (330, 1, 30, 'V', 'A+'), ################ weird #################
|
||||
'MONTH' : ( 68, 1, 1, 'V', 'V'),
|
||||
'MROUND' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'MULTINOMIAL' : ( -1, 1, 29, 'V', 'V+'),
|
||||
'N' : (131, 1, 1, 'V', 'R'),
|
||||
'NA' : ( 10, 0, 0, 'V', '-'),
|
||||
'NEGBINOMDIST': (292, 3, 3, 'V', 'VVV'),
|
||||
'NETWORKDAYS' : ( -1, 2, 3, 'V', 'VVR'),
|
||||
'NOMINAL' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'NORMDIST' : (293, 4, 4, 'V', 'VVVV'),
|
||||
'NORMINV' : (295, 3, 3, 'V', 'VVV'),
|
||||
'NORMSDIST' : (294, 1, 1, 'V', 'V'),
|
||||
'NORMSINV' : (296, 1, 1, 'V', 'V'),
|
||||
'NOT' : ( 38, 1, 1, 'V', 'V'),
|
||||
'NOW' : ( 74, 0, 0, 'V', '-'),
|
||||
'NPER' : ( 58, 3, 5, 'V', 'VVVVV'),
|
||||
'NPV' : ( 11, 2, 30, 'V', 'VD+'),
|
||||
'OCT2BIN' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'OCT2DEC' : ( -1, 1, 1, 'V', 'V'),
|
||||
'OCT2HEX' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'ODD' : (298, 1, 1, 'V', 'V'),
|
||||
'ODDFPRICE' : ( -1, 9, 9, 'V', 'VVVVVVVVV'),
|
||||
'ODDFYIELD' : ( -1, 9, 9, 'V', 'VVVVVVVVV'),
|
||||
'ODDLPRICE' : ( -1, 8, 8, 'V', 'VVVVVVVV'),
|
||||
'ODDLYIELD' : ( -1, 8, 8, 'V', 'VVVVVVVV'),
|
||||
'OFFSET' : ( 78, 3, 5, 'R', 'RVVVV'),
|
||||
'OR' : ( 37, 1, 30, 'V', 'D+'),
|
||||
'PEARSON' : (312, 2, 2, 'V', 'AA'),
|
||||
'PERCENTILE' : (328, 2, 2, 'V', 'RV'),
|
||||
'PERCENTRANK' : (329, 2, 3, 'V', 'RVV'),
|
||||
'PERMUT' : (299, 2, 2, 'V', 'VV'),
|
||||
'PHONETIC' : (360, 1, 1, 'V', 'R'),
|
||||
'PI' : ( 19, 0, 0, 'V', '-'),
|
||||
'PMT' : ( 59, 3, 5, 'V', 'VVVVV'),
|
||||
'POISSON' : (300, 3, 3, 'V', 'VVV'),
|
||||
'POWER' : (337, 2, 2, 'V', 'VV'),
|
||||
'PPMT' : (168, 4, 6, 'V', 'VVVVVV'),
|
||||
'PRICE' : ( -1, 6, 7, 'V', 'VVVVVVV'),
|
||||
'PRICEDISC' : ( -1, 4, 5, 'V', 'VVVVV'),
|
||||
'PRICEMAT' : ( -1, 5, 6, 'V', 'VVVVVV'),
|
||||
'PROB' : (317, 3, 4, 'V', 'AAVV'),
|
||||
'PRODUCT' : (183, 1, 30, 'V', 'D+'),
|
||||
'PROPER' : (114, 1, 1, 'V', 'V'),
|
||||
'PV' : ( 56, 3, 5, 'V', 'VVVVV'),
|
||||
'QUARTILE' : (327, 2, 2, 'V', 'RV'),
|
||||
'QUOTIENT' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'RADIANS' : (342, 1, 1, 'V', 'V'),
|
||||
'RAND' : ( 63, 0, 0, 'V', '-'),
|
||||
'RANDBETWEEN' : ( -1, 2, 2, 'V', 'VV'),
|
||||
'RANK' : (216, 2, 3, 'V', 'VRV'),
|
||||
'RATE' : ( 60, 3, 6, 'V', 'VVVVVV'),
|
||||
'RECEIVED' : ( -1, 4, 5, 'V', 'VVVVV'),
|
||||
'REPLACE' : (119, 4, 4, 'V', 'VVVV'),
|
||||
'REPLACEB' : (207, 4, 4, 'V', 'VVVV'),
|
||||
'REPT' : ( 30, 2, 2, 'V', 'VV'),
|
||||
'RIGHT' : (116, 1, 2, 'V', 'VV'),
|
||||
'RIGHTB' : (209, 1, 2, 'V', 'VV'),
|
||||
'ROMAN' : (354, 1, 2, 'V', 'VV'),
|
||||
'ROUND' : ( 27, 2, 2, 'V', 'VV'),
|
||||
'ROUNDDOWN' : (213, 2, 2, 'V', 'VV'),
|
||||
'ROUNDUP' : (212, 2, 2, 'V', 'VV'),
|
||||
'ROW' : ( 8, 0, 1, 'V', 'R'),
|
||||
'ROWS' : ( 76, 1, 1, 'V', 'R'),
|
||||
'RSQ' : (313, 2, 2, 'V', 'AA'),
|
||||
'RTD' : (379, 3, 30, 'A', 'VVV+'),
|
||||
'SEARCH' : ( 82, 2, 3, 'V', 'VVV'),
|
||||
'SEARCHB' : (206, 2, 3, 'V', 'VVV'),
|
||||
'SECOND' : ( 73, 1, 1, 'V', 'V'),
|
||||
'SERIESSUM' : ( -1, 4, 4, 'V', 'VVVA'),
|
||||
'SIGN' : ( 26, 1, 1, 'V', 'V'),
|
||||
'SIN' : ( 15, 1, 1, 'V', 'V'),
|
||||
'SINH' : (229, 1, 1, 'V', 'V'),
|
||||
'SKEW' : (323, 1, 30, 'V', 'D+'),
|
||||
'SLN' : (142, 3, 3, 'V', 'VVV'),
|
||||
'SLOPE' : (315, 2, 2, 'V', 'AA'),
|
||||
'SMALL' : (326, 2, 2, 'V', 'RV'),
|
||||
'SQRT' : ( 20, 1, 1, 'V', 'V'),
|
||||
'SQRTPI' : ( -1, 1, 1, 'V', 'V'),
|
||||
'STANDARDIZE' : (297, 3, 3, 'V', 'VVV'),
|
||||
'STDEV' : ( 12, 1, 30, 'V', 'D+'),
|
||||
'STDEVA' : (366, 1, 30, 'V', 'D+'),
|
||||
'STDEVP' : (193, 1, 30, 'V', 'D+'),
|
||||
'STDEVPA' : (364, 1, 30, 'V', 'D+'),
|
||||
'STEYX' : (314, 2, 2, 'V', 'AA'),
|
||||
'SUBSTITUTE' : (120, 3, 4, 'V', 'VVVV'),
|
||||
'SUBTOTAL' : (344, 2, 30, 'V', 'VR+'),
|
||||
'SUM' : ( 4, 1, 30, 'V', 'D+'),
|
||||
'SUMIF' : (345, 2, 3, 'V', 'RVR'),
|
||||
'SUMPRODUCT' : (228, 1, 30, 'V', 'A+'),
|
||||
'SUMSQ' : (321, 1, 30, 'V', 'D+'),
|
||||
'SUMX2MY2' : (304, 2, 2, 'V', 'AA'),
|
||||
'SUMX2PY2' : (305, 2, 2, 'V', 'AA'),
|
||||
'SUMXMY2' : (303, 2, 2, 'V', 'AA'),
|
||||
'SYD' : (143, 4, 4, 'V', 'VVVV'),
|
||||
'T' : (130, 1, 1, 'V', 'R'),
|
||||
'TAN' : ( 17, 1, 1, 'V', 'V'),
|
||||
'TANH' : (231, 1, 1, 'V', 'V'),
|
||||
'TBILLEQ' : ( -1, 3, 3, 'V', 'VVV'),
|
||||
'TBILLPRICE' : ( -1, 3, 3, 'V', 'VVV'),
|
||||
'TBILLYIELD' : ( -1, 3, 3, 'V', 'VVV'),
|
||||
'TDIST' : (301, 3, 3, 'V', 'VVV'),
|
||||
'TEXT' : ( 48, 2, 2, 'V', 'VV'),
|
||||
'TIME' : ( 66, 3, 3, 'V', 'VVV'),
|
||||
'TIMEVALUE' : (141, 1, 1, 'V', 'V'),
|
||||
'TINV' : (332, 2, 2, 'V', 'VV'),
|
||||
'TODAY' : (221, 0, 0, 'V', '-'),
|
||||
'TRANSPOSE' : ( 83, 1, 1, 'A', 'A'),
|
||||
'TREND' : ( 50, 1, 4, 'A', 'RRRV'),
|
||||
'TRIM' : (118, 1, 1, 'V', 'V'),
|
||||
'TRIMMEAN' : (331, 2, 2, 'V', 'RV'),
|
||||
'TRUE' : ( 34, 0, 0, 'V', '-'),
|
||||
'TRUNC' : (197, 1, 2, 'V', 'VV'),
|
||||
'TTEST' : (316, 4, 4, 'V', 'AAVV'),
|
||||
'TYPE' : ( 86, 1, 1, 'V', 'V'),
|
||||
'UPPER' : (113, 1, 1, 'V', 'V'),
|
||||
'USDOLLAR' : (204, 1, 2, 'V', 'VV'),
|
||||
'VALUE' : ( 33, 1, 1, 'V', 'V'),
|
||||
'VAR' : ( 46, 1, 30, 'V', 'D+'),
|
||||
'VARA' : (367, 1, 30, 'V', 'D+'),
|
||||
'VARP' : (194, 1, 30, 'V', 'D+'),
|
||||
'VARPA' : (365, 1, 30, 'V', 'D+'),
|
||||
'VDB' : (222, 5, 7, 'V', 'VVVVVVV'),
|
||||
'VLOOKUP' : (102, 3, 4, 'V', 'VRRV'),
|
||||
'WEEKDAY' : ( 70, 1, 2, 'V', 'VV'),
|
||||
'WEEKNUM' : ( -1, 1, 2, 'V', 'VV'),
|
||||
'WEIBULL' : (302, 4, 4, 'V', 'VVVV'),
|
||||
'WORKDAY' : ( -1, 2, 3, 'V', 'VVR'),
|
||||
'XIRR' : ( -1, 2, 3, 'V', 'AAV'),
|
||||
'XNPV' : ( -1, 3, 3, 'V', 'VAA'),
|
||||
'YEAR' : ( 69, 1, 1, 'V', 'V'),
|
||||
'YEARFRAC' : ( -1, 2, 3, 'V', 'VVV'),
|
||||
'YIELD' : ( -1, 6, 7, 'V', 'VVVVVVV'),
|
||||
'YIELDDISC' : ( -1, 4, 5, 'V', 'VVVVV'),
|
||||
'YIELDMAT' : ( -1, 5, 6, 'V', 'VVVVVV'),
|
||||
'ZTEST' : (324, 2, 3, 'V', 'RVV'),
|
||||
}
|
||||
|
||||
# Formulas Parse things
|
||||
|
||||
ptgExp = 0x01
|
||||
ptgTbl = 0x02
|
||||
ptgAdd = 0x03
|
||||
ptgSub = 0x04
|
||||
ptgMul = 0x05
|
||||
ptgDiv = 0x06
|
||||
ptgPower = 0x07
|
||||
ptgConcat = 0x08
|
||||
ptgLT = 0x09
|
||||
ptgLE = 0x0a
|
||||
ptgEQ = 0x0b
|
||||
ptgGE = 0x0c
|
||||
ptgGT = 0x0d
|
||||
ptgNE = 0x0e
|
||||
ptgIsect = 0x0f
|
||||
ptgUnion = 0x10
|
||||
ptgRange = 0x11
|
||||
ptgUplus = 0x12
|
||||
ptgUminus = 0x13
|
||||
ptgPercent = 0x14
|
||||
ptgParen = 0x15
|
||||
ptgMissArg = 0x16
|
||||
ptgStr = 0x17
|
||||
ptgExtend = 0x18
|
||||
ptgAttr = 0x19
|
||||
ptgSheet = 0x1a
|
||||
ptgEndSheet = 0x1b
|
||||
ptgErr = 0x1c
|
||||
ptgBool = 0x1d
|
||||
ptgInt = 0x1e
|
||||
ptgNum = 0x1f
|
||||
|
||||
ptgArrayR = 0x20
|
||||
ptgFuncR = 0x21
|
||||
ptgFuncVarR = 0x22
|
||||
ptgNameR = 0x23
|
||||
ptgRefR = 0x24
|
||||
ptgAreaR = 0x25
|
||||
ptgMemAreaR = 0x26
|
||||
ptgMemErrR = 0x27
|
||||
ptgMemNoMemR = 0x28
|
||||
ptgMemFuncR = 0x29
|
||||
ptgRefErrR = 0x2a
|
||||
ptgAreaErrR = 0x2b
|
||||
ptgRefNR = 0x2c
|
||||
ptgAreaNR = 0x2d
|
||||
ptgMemAreaNR = 0x2e
|
||||
ptgMemNoMemNR = 0x2f
|
||||
ptgNameXR = 0x39
|
||||
ptgRef3dR = 0x3a
|
||||
ptgArea3dR = 0x3b
|
||||
ptgRefErr3dR = 0x3c
|
||||
ptgAreaErr3dR = 0x3d
|
||||
|
||||
ptgArrayV = 0x40
|
||||
ptgFuncV = 0x41
|
||||
ptgFuncVarV = 0x42
|
||||
ptgNameV = 0x43
|
||||
ptgRefV = 0x44
|
||||
ptgAreaV = 0x45
|
||||
ptgMemAreaV = 0x46
|
||||
ptgMemErrV = 0x47
|
||||
ptgMemNoMemV = 0x48
|
||||
ptgMemFuncV = 0x49
|
||||
ptgRefErrV = 0x4a
|
||||
ptgAreaErrV = 0x4b
|
||||
ptgRefNV = 0x4c
|
||||
ptgAreaNV = 0x4d
|
||||
ptgMemAreaNV = 0x4e
|
||||
ptgMemNoMemNV = 0x4f
|
||||
ptgFuncCEV = 0x58
|
||||
ptgNameXV = 0x59
|
||||
ptgRef3dV = 0x5a
|
||||
ptgArea3dV = 0x5b
|
||||
ptgRefErr3dV = 0x5c
|
||||
ptgAreaErr3dV = 0x5d
|
||||
|
||||
ptgArrayA = 0x60
|
||||
ptgFuncA = 0x61
|
||||
ptgFuncVarA = 0x62
|
||||
ptgNameA = 0x63
|
||||
ptgRefA = 0x64
|
||||
ptgAreaA = 0x65
|
||||
ptgMemAreaA = 0x66
|
||||
ptgMemErrA = 0x67
|
||||
ptgMemNoMemA = 0x68
|
||||
ptgMemFuncA = 0x69
|
||||
ptgRefErrA = 0x6a
|
||||
ptgAreaErrA = 0x6b
|
||||
ptgRefNA = 0x6c
|
||||
ptgAreaNA = 0x6d
|
||||
ptgMemAreaNA = 0x6e
|
||||
ptgMemNoMemNA = 0x6f
|
||||
ptgFuncCEA = 0x78
|
||||
ptgNameXA = 0x79
|
||||
ptgRef3dA = 0x7a
|
||||
ptgArea3dA = 0x7b
|
||||
ptgRefErr3dA = 0x7c
|
||||
ptgAreaErr3dA = 0x7d
|
||||
|
||||
|
||||
PtgNames = {
|
||||
ptgExp : "ptgExp",
|
||||
ptgTbl : "ptgTbl",
|
||||
ptgAdd : "ptgAdd",
|
||||
ptgSub : "ptgSub",
|
||||
ptgMul : "ptgMul",
|
||||
ptgDiv : "ptgDiv",
|
||||
ptgPower : "ptgPower",
|
||||
ptgConcat : "ptgConcat",
|
||||
ptgLT : "ptgLT",
|
||||
ptgLE : "ptgLE",
|
||||
ptgEQ : "ptgEQ",
|
||||
ptgGE : "ptgGE",
|
||||
ptgGT : "ptgGT",
|
||||
ptgNE : "ptgNE",
|
||||
ptgIsect : "ptgIsect",
|
||||
ptgUnion : "ptgUnion",
|
||||
ptgRange : "ptgRange",
|
||||
ptgUplus : "ptgUplus",
|
||||
ptgUminus : "ptgUminus",
|
||||
ptgPercent : "ptgPercent",
|
||||
ptgParen : "ptgParen",
|
||||
ptgMissArg : "ptgMissArg",
|
||||
ptgStr : "ptgStr",
|
||||
ptgExtend : "ptgExtend",
|
||||
ptgAttr : "ptgAttr",
|
||||
ptgSheet : "ptgSheet",
|
||||
ptgEndSheet : "ptgEndSheet",
|
||||
ptgErr : "ptgErr",
|
||||
ptgBool : "ptgBool",
|
||||
ptgInt : "ptgInt",
|
||||
ptgNum : "ptgNum",
|
||||
ptgArrayR : "ptgArrayR",
|
||||
ptgFuncR : "ptgFuncR",
|
||||
ptgFuncVarR : "ptgFuncVarR",
|
||||
ptgNameR : "ptgNameR",
|
||||
ptgRefR : "ptgRefR",
|
||||
ptgAreaR : "ptgAreaR",
|
||||
ptgMemAreaR : "ptgMemAreaR",
|
||||
ptgMemErrR : "ptgMemErrR",
|
||||
ptgMemNoMemR : "ptgMemNoMemR",
|
||||
ptgMemFuncR : "ptgMemFuncR",
|
||||
ptgRefErrR : "ptgRefErrR",
|
||||
ptgAreaErrR : "ptgAreaErrR",
|
||||
ptgRefNR : "ptgRefNR",
|
||||
ptgAreaNR : "ptgAreaNR",
|
||||
ptgMemAreaNR : "ptgMemAreaNR",
|
||||
ptgMemNoMemNR : "ptgMemNoMemNR",
|
||||
ptgNameXR : "ptgNameXR",
|
||||
ptgRef3dR : "ptgRef3dR",
|
||||
ptgArea3dR : "ptgArea3dR",
|
||||
ptgRefErr3dR : "ptgRefErr3dR",
|
||||
ptgAreaErr3dR : "ptgAreaErr3dR",
|
||||
ptgArrayV : "ptgArrayV",
|
||||
ptgFuncV : "ptgFuncV",
|
||||
ptgFuncVarV : "ptgFuncVarV",
|
||||
ptgNameV : "ptgNameV",
|
||||
ptgRefV : "ptgRefV",
|
||||
ptgAreaV : "ptgAreaV",
|
||||
ptgMemAreaV : "ptgMemAreaV",
|
||||
ptgMemErrV : "ptgMemErrV",
|
||||
ptgMemNoMemV : "ptgMemNoMemV",
|
||||
ptgMemFuncV : "ptgMemFuncV",
|
||||
ptgRefErrV : "ptgRefErrV",
|
||||
ptgAreaErrV : "ptgAreaErrV",
|
||||
ptgRefNV : "ptgRefNV",
|
||||
ptgAreaNV : "ptgAreaNV",
|
||||
ptgMemAreaNV : "ptgMemAreaNV",
|
||||
ptgMemNoMemNV : "ptgMemNoMemNV",
|
||||
ptgFuncCEV : "ptgFuncCEV",
|
||||
ptgNameXV : "ptgNameXV",
|
||||
ptgRef3dV : "ptgRef3dV",
|
||||
ptgArea3dV : "ptgArea3dV",
|
||||
ptgRefErr3dV : "ptgRefErr3dV",
|
||||
ptgAreaErr3dV : "ptgAreaErr3dV",
|
||||
ptgArrayA : "ptgArrayA",
|
||||
ptgFuncA : "ptgFuncA",
|
||||
ptgFuncVarA : "ptgFuncVarA",
|
||||
ptgNameA : "ptgNameA",
|
||||
ptgRefA : "ptgRefA",
|
||||
ptgAreaA : "ptgAreaA",
|
||||
ptgMemAreaA : "ptgMemAreaA",
|
||||
ptgMemErrA : "ptgMemErrA",
|
||||
ptgMemNoMemA : "ptgMemNoMemA",
|
||||
ptgMemFuncA : "ptgMemFuncA",
|
||||
ptgRefErrA : "ptgRefErrA",
|
||||
ptgAreaErrA : "ptgAreaErrA",
|
||||
ptgRefNA : "ptgRefNA",
|
||||
ptgAreaNA : "ptgAreaNA",
|
||||
ptgMemAreaNA : "ptgMemAreaNA",
|
||||
ptgMemNoMemNA : "ptgMemNoMemNA",
|
||||
ptgFuncCEA : "ptgFuncCEA",
|
||||
ptgNameXA : "ptgNameXA",
|
||||
ptgRef3dA : "ptgRef3dA",
|
||||
ptgArea3dA : "ptgArea3dA",
|
||||
ptgRefErr3dA : "ptgRefErr3dA",
|
||||
ptgAreaErr3dA : "ptgAreaErr3dA"
|
||||
}
|
||||
|
||||
|
||||
error_msg_by_code = {
|
||||
0x00: "#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
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
The XF record is able to store explicit cell formatting attributes or the
|
||||
attributes of a cell style. Explicit formatting includes the reference to
|
||||
a cell style XF record. This allows to extend a defined cell style with
|
||||
some explicit attributes. The formatting attributes are divided into
|
||||
6 groups:
|
||||
|
||||
Group Attributes
|
||||
-------------------------------------
|
||||
Number format Number format index (index to FORMAT record)
|
||||
Font Font index (index to FONT record)
|
||||
Alignment Horizontal and vertical alignment, text wrap, indentation,
|
||||
orientation/rotation, text direction
|
||||
Border Border line styles and colours
|
||||
Background Background area style and colours
|
||||
Protection Cell locked, formula hidden
|
||||
|
||||
For each group a flag in the cell XF record specifies whether to use the
|
||||
attributes contained in that XF record or in the referenced style
|
||||
XF record. In style XF records, these flags specify whether the attributes
|
||||
will overwrite explicit cell formatting when the style is applied to
|
||||
a cell. Changing a cell style (without applying this style to a cell) will
|
||||
change all cells which already use that style and do not contain explicit
|
||||
cell attributes for the changed style attributes. If a cell XF record does
|
||||
not contain explicit attributes in a group (if the attribute group flag
|
||||
is not set), it repeats the attributes of its style XF record.
|
||||
|
||||
'''
|
||||
|
||||
from . import BIFFRecords
|
||||
|
||||
class Font(object):
|
||||
|
||||
ESCAPEMENT_NONE = 0x00
|
||||
ESCAPEMENT_SUPERSCRIPT = 0x01
|
||||
ESCAPEMENT_SUBSCRIPT = 0x02
|
||||
|
||||
UNDERLINE_NONE = 0x00
|
||||
UNDERLINE_SINGLE = 0x01
|
||||
UNDERLINE_SINGLE_ACC = 0x21
|
||||
UNDERLINE_DOUBLE = 0x02
|
||||
UNDERLINE_DOUBLE_ACC = 0x22
|
||||
|
||||
FAMILY_NONE = 0x00
|
||||
FAMILY_ROMAN = 0x01
|
||||
FAMILY_SWISS = 0x02
|
||||
FAMILY_MODERN = 0x03
|
||||
FAMILY_SCRIPT = 0x04
|
||||
FAMILY_DECORATIVE = 0x05
|
||||
|
||||
CHARSET_ANSI_LATIN = 0x00
|
||||
CHARSET_SYS_DEFAULT = 0x01
|
||||
CHARSET_SYMBOL = 0x02
|
||||
CHARSET_APPLE_ROMAN = 0x4D
|
||||
CHARSET_ANSI_JAP_SHIFT_JIS = 0x80
|
||||
CHARSET_ANSI_KOR_HANGUL = 0x81
|
||||
CHARSET_ANSI_KOR_JOHAB = 0x82
|
||||
CHARSET_ANSI_CHINESE_GBK = 0x86
|
||||
CHARSET_ANSI_CHINESE_BIG5 = 0x88
|
||||
CHARSET_ANSI_GREEK = 0xA1
|
||||
CHARSET_ANSI_TURKISH = 0xA2
|
||||
CHARSET_ANSI_VIETNAMESE = 0xA3
|
||||
CHARSET_ANSI_HEBREW = 0xB1
|
||||
CHARSET_ANSI_ARABIC = 0xB2
|
||||
CHARSET_ANSI_BALTIC = 0xBA
|
||||
CHARSET_ANSI_CYRILLIC = 0xCC
|
||||
CHARSET_ANSI_THAI = 0xDE
|
||||
CHARSET_ANSI_LATIN_II = 0xEE
|
||||
CHARSET_OEM_LATIN_I = 0xFF
|
||||
|
||||
def __init__(self):
|
||||
# twip = 1/20 of a point = 1/1440 of a inch
|
||||
# usually resolution == 96 pixels per 1 inch
|
||||
# (rarely 120 pixels per 1 inch or another one)
|
||||
|
||||
self.height = 0x00C8 # 200: this is font with height 10 points
|
||||
self.italic = False
|
||||
self.struck_out = False
|
||||
self.outline = False
|
||||
self.shadow = False
|
||||
self.colour_index = 0x7FFF
|
||||
self.bold = False
|
||||
self._weight = 0x0190 # 0x02BC gives bold font
|
||||
self.escapement = self.ESCAPEMENT_NONE
|
||||
self.underline = self.UNDERLINE_NONE
|
||||
self.family = self.FAMILY_NONE
|
||||
self.charset = self.CHARSET_SYS_DEFAULT
|
||||
self.name = b'Arial'
|
||||
|
||||
def get_biff_record(self):
|
||||
height = self.height
|
||||
|
||||
options = 0x00
|
||||
if self.bold:
|
||||
options |= 0x01
|
||||
self._weight = 0x02BC
|
||||
if self.italic:
|
||||
options |= 0x02
|
||||
if self.underline != self.UNDERLINE_NONE:
|
||||
options |= 0x04
|
||||
if self.struck_out:
|
||||
options |= 0x08
|
||||
if self.outline:
|
||||
options |= 0x010
|
||||
if self.shadow:
|
||||
options |= 0x020
|
||||
|
||||
colour_index = self.colour_index
|
||||
weight = self._weight
|
||||
escapement = self.escapement
|
||||
underline = self.underline
|
||||
family = self.family
|
||||
charset = self.charset
|
||||
name = self.name
|
||||
|
||||
return BIFFRecords.FontRecord(height, options, colour_index, weight, escapement,
|
||||
underline, family, charset,
|
||||
name)
|
||||
|
||||
def _search_key(self):
|
||||
return (
|
||||
self.height,
|
||||
self.italic,
|
||||
self.struck_out,
|
||||
self.outline,
|
||||
self.shadow,
|
||||
self.colour_index,
|
||||
self.bold,
|
||||
self._weight,
|
||||
self.escapement,
|
||||
self.underline,
|
||||
self.family,
|
||||
self.charset,
|
||||
self.name,
|
||||
)
|
||||
|
||||
class Alignment(object):
|
||||
HORZ_GENERAL = 0x00
|
||||
HORZ_LEFT = 0x01
|
||||
HORZ_CENTER = 0x02
|
||||
HORZ_RIGHT = 0x03
|
||||
HORZ_FILLED = 0x04
|
||||
HORZ_JUSTIFIED = 0x05 # BIFF4-BIFF8X
|
||||
HORZ_CENTER_ACROSS_SEL = 0x06 # Centred across selection (BIFF4-BIFF8X)
|
||||
HORZ_DISTRIBUTED = 0x07 # Distributed (BIFF8X)
|
||||
|
||||
VERT_TOP = 0x00
|
||||
VERT_CENTER = 0x01
|
||||
VERT_BOTTOM = 0x02
|
||||
VERT_JUSTIFIED = 0x03 # Justified (BIFF5-BIFF8X)
|
||||
VERT_DISTRIBUTED = 0x04 # Distributed (BIFF8X)
|
||||
|
||||
DIRECTION_GENERAL = 0x00 # BIFF8X
|
||||
DIRECTION_LR = 0x01
|
||||
DIRECTION_RL = 0x02
|
||||
|
||||
ORIENTATION_NOT_ROTATED = 0x00
|
||||
ORIENTATION_STACKED = 0x01
|
||||
ORIENTATION_90_CC = 0x02
|
||||
ORIENTATION_90_CW = 0x03
|
||||
|
||||
ROTATION_0_ANGLE = 0x00
|
||||
ROTATION_STACKED = 0xFF
|
||||
|
||||
WRAP_AT_RIGHT = 0x01
|
||||
NOT_WRAP_AT_RIGHT = 0x00
|
||||
|
||||
SHRINK_TO_FIT = 0x01
|
||||
NOT_SHRINK_TO_FIT = 0x00
|
||||
|
||||
def __init__(self):
|
||||
self.horz = self.HORZ_GENERAL
|
||||
self.vert = self.VERT_BOTTOM
|
||||
self.dire = self.DIRECTION_GENERAL
|
||||
self.orie = self.ORIENTATION_NOT_ROTATED
|
||||
self.rota = self.ROTATION_0_ANGLE
|
||||
self.wrap = self.NOT_WRAP_AT_RIGHT
|
||||
self.shri = self.NOT_SHRINK_TO_FIT
|
||||
self.inde = 0
|
||||
self.merg = 0
|
||||
|
||||
def _search_key(self):
|
||||
return (
|
||||
self.horz, self.vert, self.dire, self.orie, self.rota,
|
||||
self.wrap, self.shri, self.inde, self.merg,
|
||||
)
|
||||
|
||||
class Borders(object):
|
||||
NO_LINE = 0x00
|
||||
THIN = 0x01
|
||||
MEDIUM = 0x02
|
||||
DASHED = 0x03
|
||||
DOTTED = 0x04
|
||||
THICK = 0x05
|
||||
DOUBLE = 0x06
|
||||
HAIR = 0x07
|
||||
#The following for BIFF8
|
||||
MEDIUM_DASHED = 0x08
|
||||
THIN_DASH_DOTTED = 0x09
|
||||
MEDIUM_DASH_DOTTED = 0x0A
|
||||
THIN_DASH_DOT_DOTTED = 0x0B
|
||||
MEDIUM_DASH_DOT_DOTTED = 0x0C
|
||||
SLANTED_MEDIUM_DASH_DOTTED = 0x0D
|
||||
|
||||
NEED_DIAG1 = 0x01
|
||||
NEED_DIAG2 = 0x01
|
||||
NO_NEED_DIAG1 = 0x00
|
||||
NO_NEED_DIAG2 = 0x00
|
||||
|
||||
def __init__(self):
|
||||
self.left = self.NO_LINE
|
||||
self.right = self.NO_LINE
|
||||
self.top = self.NO_LINE
|
||||
self.bottom = self.NO_LINE
|
||||
self.diag = self.NO_LINE
|
||||
|
||||
self.left_colour = 0x40
|
||||
self.right_colour = 0x40
|
||||
self.top_colour = 0x40
|
||||
self.bottom_colour = 0x40
|
||||
self.diag_colour = 0x40
|
||||
|
||||
self.need_diag1 = self.NO_NEED_DIAG1
|
||||
self.need_diag2 = self.NO_NEED_DIAG2
|
||||
|
||||
def _search_key(self):
|
||||
return (
|
||||
self.left, self.right, self.top, self.bottom, self.diag,
|
||||
self.left_colour, self.right_colour, self.top_colour,
|
||||
self.bottom_colour, self.diag_colour,
|
||||
self.need_diag1, self.need_diag2,
|
||||
)
|
||||
|
||||
class Pattern(object):
|
||||
# patterns 0x00 - 0x12
|
||||
NO_PATTERN = 0x00
|
||||
SOLID_PATTERN = 0x01
|
||||
|
||||
def __init__(self):
|
||||
self.pattern = self.NO_PATTERN
|
||||
self.pattern_fore_colour = 0x40
|
||||
self.pattern_back_colour = 0x41
|
||||
|
||||
def _search_key(self):
|
||||
return (
|
||||
self.pattern,
|
||||
self.pattern_fore_colour,
|
||||
self.pattern_back_colour,
|
||||
)
|
||||
|
||||
class Protection(object):
|
||||
def __init__(self):
|
||||
self.cell_locked = 1
|
||||
self.formula_hidden = 0
|
||||
|
||||
def _search_key(self):
|
||||
return (
|
||||
self.cell_locked,
|
||||
self.formula_hidden,
|
||||
)
|
||||
@@ -1,253 +0,0 @@
|
||||
# -*- coding: windows-1252 -*-
|
||||
|
||||
from . import BIFFRecords
|
||||
from . import Style
|
||||
from .Cell import StrCell, BlankCell, NumberCell, FormulaCell, MulBlankCell, BooleanCell, ErrorCell, \
|
||||
_get_cells_biff_data_mul
|
||||
from . import ExcelFormula
|
||||
import datetime as dt
|
||||
try:
|
||||
from decimal import Decimal
|
||||
except ImportError:
|
||||
# Python 2.3: decimal not supported; create dummy Decimal class
|
||||
class Decimal(object):
|
||||
pass
|
||||
|
||||
|
||||
class Row(object):
|
||||
__slots__ = [# private variables
|
||||
"__idx",
|
||||
"__parent",
|
||||
"__parent_wb",
|
||||
"__cells",
|
||||
"__min_col_idx",
|
||||
"__max_col_idx",
|
||||
"__xf_index",
|
||||
"__has_default_xf_index",
|
||||
"__height_in_pixels",
|
||||
# public variables
|
||||
"height",
|
||||
"has_default_height",
|
||||
"height_mismatch",
|
||||
"level",
|
||||
"collapse",
|
||||
"hidden",
|
||||
"space_above",
|
||||
"space_below"]
|
||||
|
||||
def __init__(self, rowx, parent_sheet):
|
||||
if not (isinstance(rowx, int) and 0 <= rowx <= 65535):
|
||||
raise ValueError("row index (%r) not an int in range(65536)" % rowx)
|
||||
self.__idx = rowx
|
||||
self.__parent = parent_sheet
|
||||
self.__parent_wb = parent_sheet.get_parent()
|
||||
self.__cells = {}
|
||||
self.__min_col_idx = 0
|
||||
self.__max_col_idx = 0
|
||||
self.__xf_index = 0x0F
|
||||
self.__has_default_xf_index = 0
|
||||
self.__height_in_pixels = 0x11
|
||||
|
||||
self.height = 0x00FF
|
||||
self.has_default_height = 0x00
|
||||
self.height_mismatch = 0
|
||||
self.level = 0
|
||||
self.collapse = 0
|
||||
self.hidden = 0
|
||||
self.space_above = 0
|
||||
self.space_below = 0
|
||||
|
||||
|
||||
def __adjust_height(self, style):
|
||||
twips = style.font.height
|
||||
points = float(twips)/20.0
|
||||
# Cell height in pixels can be calcuted by following approx. formula:
|
||||
# cell height in pixels = font height in points * 83/50 + 2/5
|
||||
# It works when screen resolution is 96 dpi
|
||||
pix = int(round(points*83.0/50.0 + 2.0/5.0))
|
||||
if pix > self.__height_in_pixels:
|
||||
self.__height_in_pixels = pix
|
||||
|
||||
|
||||
def __adjust_bound_col_idx(self, *args):
|
||||
for arg in args:
|
||||
iarg = int(arg)
|
||||
if not ((0 <= iarg <= 255) and arg == iarg):
|
||||
raise ValueError("column index (%r) not an int in range(256)" % arg)
|
||||
sheet = self.__parent
|
||||
if iarg < self.__min_col_idx:
|
||||
self.__min_col_idx = iarg
|
||||
if iarg > self.__max_col_idx:
|
||||
self.__max_col_idx = iarg
|
||||
if iarg < sheet.first_used_col:
|
||||
sheet.first_used_col = iarg
|
||||
if iarg > sheet.last_used_col:
|
||||
sheet.last_used_col = iarg
|
||||
|
||||
def __excel_date_dt(self, date):
|
||||
if isinstance(date, dt.date) and (not isinstance(date, dt.datetime)):
|
||||
epoch = dt.date(1899, 12, 31)
|
||||
elif isinstance(date, dt.time):
|
||||
date = dt.datetime.combine(dt.datetime(1900, 1, 1), date)
|
||||
epoch = dt.datetime(1900, 1, 1, 0, 0, 0)
|
||||
else:
|
||||
epoch = dt.datetime(1899, 12, 31, 0, 0, 0)
|
||||
delta = date - epoch
|
||||
xldate = delta.days + float(delta.seconds) / (24*60*60)
|
||||
# Add a day for Excel's missing leap day in 1900
|
||||
if xldate > 59:
|
||||
xldate += 1
|
||||
return xldate
|
||||
|
||||
def get_height_in_pixels(self):
|
||||
return self.__height_in_pixels
|
||||
|
||||
|
||||
def set_style(self, style):
|
||||
self.__adjust_height(style)
|
||||
self.__xf_index = self.__parent_wb.add_style(style)
|
||||
self.__has_default_xf_index = 1
|
||||
|
||||
|
||||
def get_xf_index(self):
|
||||
return self.__xf_index
|
||||
|
||||
|
||||
def get_cells_count(self):
|
||||
return len(self.__cells)
|
||||
|
||||
|
||||
def get_min_col(self):
|
||||
return self.__min_col_idx
|
||||
|
||||
|
||||
def get_max_col(self):
|
||||
return self.__max_col_idx
|
||||
|
||||
|
||||
def get_row_biff_data(self):
|
||||
height_options = (self.height & 0x07FFF)
|
||||
height_options |= (self.has_default_height & 0x01) << 15
|
||||
|
||||
options = (self.level & 0x07) << 0
|
||||
options |= (self.collapse & 0x01) << 4
|
||||
options |= (self.hidden & 0x01) << 5
|
||||
options |= (self.height_mismatch & 0x01) << 6
|
||||
options |= (self.__has_default_xf_index & 0x01) << 7
|
||||
options |= (0x01 & 0x01) << 8
|
||||
options |= (self.__xf_index & 0x0FFF) << 16
|
||||
options |= (self.space_above & 1) << 28
|
||||
options |= (self.space_below & 1) << 29
|
||||
|
||||
return BIFFRecords.RowRecord(self.__idx, self.__min_col_idx,
|
||||
self.__max_col_idx, height_options, options).get()
|
||||
|
||||
def insert_cell(self, col_index, cell_obj):
|
||||
if col_index in self.__cells:
|
||||
if not self.__parent._cell_overwrite_ok:
|
||||
msg = "Attempt to overwrite cell: sheetname=%r rowx=%d colx=%d" \
|
||||
% (self.__parent.name, self.__idx, col_index)
|
||||
raise Exception(msg)
|
||||
prev_cell_obj = self.__cells[col_index]
|
||||
sst_idx = getattr(prev_cell_obj, 'sst_idx', None)
|
||||
if sst_idx is not None:
|
||||
self.__parent_wb.del_str(sst_idx)
|
||||
self.__cells[col_index] = cell_obj
|
||||
|
||||
def insert_mulcells(self, colx1, colx2, cell_obj):
|
||||
self.insert_cell(colx1, cell_obj)
|
||||
for col_index in range(colx1+1, colx2+1):
|
||||
self.insert_cell(col_index, None)
|
||||
|
||||
def get_cells_biff_data(self):
|
||||
cell_items = [item for item in self.__cells.items() if item[1] is not None]
|
||||
cell_items.sort() # in column order
|
||||
return _get_cells_biff_data_mul(self.__idx, cell_items)
|
||||
# previously:
|
||||
# return ''.join([cell.get_biff_data() for colx, cell in cell_items])
|
||||
|
||||
def get_index(self):
|
||||
return self.__idx
|
||||
|
||||
def set_cell_text(self, colx, value, style=Style.default_style):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
self.insert_cell(colx, StrCell(self.__idx, colx, xf_index, self.__parent_wb.add_str(value)))
|
||||
|
||||
def set_cell_blank(self, colx, style=Style.default_style):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
self.insert_cell(colx, BlankCell(self.__idx, colx, xf_index))
|
||||
|
||||
def set_cell_mulblanks(self, first_colx, last_colx, style=Style.default_style):
|
||||
assert 0 <= first_colx <= last_colx <= 255
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(first_colx, last_colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
# ncols = last_colx - first_colx + 1
|
||||
self.insert_mulcells(first_colx, last_colx, MulBlankCell(self.__idx, first_colx, last_colx, xf_index))
|
||||
|
||||
def set_cell_number(self, colx, number, style=Style.default_style):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
self.insert_cell(colx, NumberCell(self.__idx, colx, xf_index, number))
|
||||
|
||||
def set_cell_date(self, colx, datetime_obj, style=Style.default_style):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
self.insert_cell(colx,
|
||||
NumberCell(self.__idx, colx, xf_index, self.__excel_date_dt(datetime_obj)))
|
||||
|
||||
def set_cell_formula(self, colx, formula, style=Style.default_style, calc_flags=0):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
self.__parent_wb.add_sheet_reference(formula)
|
||||
self.insert_cell(colx, FormulaCell(self.__idx, colx, xf_index, formula, calc_flags=0))
|
||||
|
||||
def set_cell_boolean(self, colx, value, style=Style.default_style):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
self.insert_cell(colx, BooleanCell(self.__idx, colx, xf_index, bool(value)))
|
||||
|
||||
def set_cell_error(self, colx, error_string_or_code, style=Style.default_style):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(colx)
|
||||
xf_index = self.__parent_wb.add_style(style)
|
||||
self.insert_cell(colx, ErrorCell(self.__idx, colx, xf_index, error_string_or_code))
|
||||
|
||||
def write(self, col, label, style=Style.default_style):
|
||||
self.__adjust_height(style)
|
||||
self.__adjust_bound_col_idx(col)
|
||||
style_index = self.__parent_wb.add_style(style)
|
||||
if isinstance(label, str):
|
||||
if len(label) > 0:
|
||||
self.insert_cell(col,
|
||||
StrCell(self.__idx, col, style_index, self.__parent_wb.add_str(label))
|
||||
)
|
||||
else:
|
||||
self.insert_cell(col, BlankCell(self.__idx, col, style_index))
|
||||
elif isinstance(label, bool): # bool is subclass of int; test bool first
|
||||
self.insert_cell(col, BooleanCell(self.__idx, col, style_index, label))
|
||||
elif isinstance(label, (float, int, Decimal)):
|
||||
self.insert_cell(col, NumberCell(self.__idx, col, style_index, label))
|
||||
elif isinstance(label, (dt.datetime, dt.date, dt.time)):
|
||||
date_number = self.__excel_date_dt(label)
|
||||
self.insert_cell(col, NumberCell(self.__idx, col, style_index, date_number))
|
||||
elif label is None:
|
||||
self.insert_cell(col, BlankCell(self.__idx, col, style_index))
|
||||
elif isinstance(label, ExcelFormula.Formula):
|
||||
self.__parent_wb.add_sheet_reference(label)
|
||||
self.insert_cell(col, FormulaCell(self.__idx, col, style_index, label))
|
||||
else:
|
||||
raise Exception("Unexpected data type %r" % type(label))
|
||||
|
||||
write_blanks = set_cell_mulblanks
|
||||
|
||||
|
||||
|
||||
@@ -1,593 +0,0 @@
|
||||
# -*- coding: windows-1252 -*-
|
||||
|
||||
from . import Formatting
|
||||
from .BIFFRecords import *
|
||||
import collections
|
||||
|
||||
FIRST_USER_DEFINED_NUM_FORMAT_IDX = 164
|
||||
|
||||
class XFStyle(object):
|
||||
|
||||
def __init__(self):
|
||||
self.num_format_str = 'General'
|
||||
self.font = Formatting.Font()
|
||||
self.alignment = Formatting.Alignment()
|
||||
self.borders = Formatting.Borders()
|
||||
self.pattern = Formatting.Pattern()
|
||||
self.protection = Formatting.Protection()
|
||||
|
||||
default_style = XFStyle()
|
||||
|
||||
class StyleCollection(object):
|
||||
_std_num_fmt_list = [
|
||||
'general',
|
||||
'0',
|
||||
'0.00',
|
||||
'#,##0',
|
||||
'#,##0.00',
|
||||
'"$"#,##0_);("$"#,##',
|
||||
'"$"#,##0_);[Red]("$"#,##',
|
||||
'"$"#,##0.00_);("$"#,##',
|
||||
'"$"#,##0.00_);[Red]("$"#,##',
|
||||
'0%',
|
||||
'0.00%',
|
||||
'0.00E+00',
|
||||
'# ?/?',
|
||||
'# ??/??',
|
||||
'M/D/YY',
|
||||
'D-MMM-YY',
|
||||
'D-MMM',
|
||||
'MMM-YY',
|
||||
'h:mm AM/PM',
|
||||
'h:mm:ss AM/PM',
|
||||
'h:mm',
|
||||
'h:mm:ss',
|
||||
'M/D/YY h:mm',
|
||||
'_(#,##0_);(#,##0)',
|
||||
'_(#,##0_);[Red](#,##0)',
|
||||
'_(#,##0.00_);(#,##0.00)',
|
||||
'_(#,##0.00_);[Red](#,##0.00)',
|
||||
'_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)',
|
||||
'_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)',
|
||||
'_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)',
|
||||
'_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)',
|
||||
'mm:ss',
|
||||
'[h]:mm:ss',
|
||||
'mm:ss.0',
|
||||
'##0.0E+0',
|
||||
'@'
|
||||
]
|
||||
|
||||
def __init__(self, style_compression=0):
|
||||
self.style_compression = style_compression
|
||||
self.stats = [0, 0, 0, 0, 0, 0]
|
||||
self._font_id2x = {}
|
||||
self._font_x2id = {}
|
||||
self._font_val2x = {}
|
||||
|
||||
for x in (0, 1, 2, 3, 5): # The font with index 4 is omitted in all BIFF versions
|
||||
font = Formatting.Font()
|
||||
search_key = font._search_key()
|
||||
self._font_id2x[font] = x
|
||||
self._font_x2id[x] = font
|
||||
self._font_val2x[search_key] = x
|
||||
|
||||
self._xf_id2x = {}
|
||||
self._xf_x2id = {}
|
||||
self._xf_val2x = {}
|
||||
|
||||
self._num_formats = {}
|
||||
for fmtidx, fmtstr in zip(list(range(0, 23)), StyleCollection._std_num_fmt_list[0:23]):
|
||||
self._num_formats[fmtstr] = fmtidx
|
||||
for fmtidx, fmtstr in zip(list(range(37, 50)), StyleCollection._std_num_fmt_list[23:]):
|
||||
self._num_formats[fmtstr] = fmtidx
|
||||
|
||||
self.default_style = XFStyle()
|
||||
self._default_xf = self._add_style(self.default_style)[0]
|
||||
|
||||
def add(self, style):
|
||||
if style == None:
|
||||
return 0x10
|
||||
return self._add_style(style)[1]
|
||||
|
||||
def _add_style(self, style):
|
||||
num_format_str = style.num_format_str
|
||||
if num_format_str in self._num_formats:
|
||||
num_format_idx = self._num_formats[num_format_str]
|
||||
else:
|
||||
num_format_idx = (
|
||||
FIRST_USER_DEFINED_NUM_FORMAT_IDX
|
||||
+ len(self._num_formats)
|
||||
- len(StyleCollection._std_num_fmt_list)
|
||||
)
|
||||
self._num_formats[num_format_str] = num_format_idx
|
||||
|
||||
font = style.font
|
||||
if font in self._font_id2x:
|
||||
font_idx = self._font_id2x[font]
|
||||
self.stats[0] += 1
|
||||
elif self.style_compression:
|
||||
search_key = font._search_key()
|
||||
font_idx = self._font_val2x.get(search_key)
|
||||
if font_idx is not None:
|
||||
self._font_id2x[font] = font_idx
|
||||
self.stats[1] += 1
|
||||
else:
|
||||
font_idx = len(self._font_x2id) + 1 # Why plus 1? Font 4 is missing
|
||||
self._font_id2x[font] = font_idx
|
||||
self._font_val2x[search_key] = font_idx
|
||||
self._font_x2id[font_idx] = font
|
||||
self.stats[2] += 1
|
||||
else:
|
||||
font_idx = len(self._font_id2x) + 1
|
||||
self._font_id2x[font] = font_idx
|
||||
self.stats[2] += 1
|
||||
|
||||
gof = (style.alignment, style.borders, style.pattern, style.protection)
|
||||
xf = (font_idx, num_format_idx) + gof
|
||||
if xf in self._xf_id2x:
|
||||
xf_index = self._xf_id2x[xf]
|
||||
self.stats[3] += 1
|
||||
elif self.style_compression == 2:
|
||||
xf_key = (font_idx, num_format_idx) + tuple([obj._search_key() for obj in gof])
|
||||
xf_index = self._xf_val2x.get(xf_key)
|
||||
if xf_index is not None:
|
||||
self._xf_id2x[xf] = xf_index
|
||||
self.stats[4] += 1
|
||||
else:
|
||||
xf_index = 0x10 + len(self._xf_x2id)
|
||||
self._xf_id2x[xf] = xf_index
|
||||
self._xf_val2x[xf_key] = xf_index
|
||||
self._xf_x2id[xf_index] = xf
|
||||
self.stats[5] += 1
|
||||
else:
|
||||
xf_index = 0x10 + len(self._xf_id2x)
|
||||
self._xf_id2x[xf] = xf_index
|
||||
self.stats[5] += 1
|
||||
|
||||
if xf_index >= 0xFFF:
|
||||
# 12 bits allowed, 0xFFF is a sentinel value
|
||||
raise ValueError("More than 4094 XFs (styles)")
|
||||
|
||||
return xf, xf_index
|
||||
|
||||
def get_biff_data(self):
|
||||
result = b''
|
||||
result += self._all_fonts()
|
||||
result += self._all_num_formats()
|
||||
result += self._all_cell_styles()
|
||||
result += self._all_styles()
|
||||
return result
|
||||
|
||||
def _all_fonts(self):
|
||||
result = b''
|
||||
if self.style_compression:
|
||||
alist = list(self._font_x2id.items())
|
||||
else:
|
||||
alist = [(x, o) for o, x in list(self._font_id2x.items())]
|
||||
alist.sort()
|
||||
for font_idx, font in alist:
|
||||
result += font.get_biff_record().get()
|
||||
return result
|
||||
|
||||
def _all_num_formats(self):
|
||||
result = b''
|
||||
alist = [
|
||||
(v, k)
|
||||
for k, v in list(self._num_formats.items())
|
||||
if v >= FIRST_USER_DEFINED_NUM_FORMAT_IDX
|
||||
]
|
||||
alist.sort()
|
||||
for fmtidx, fmtstr in alist:
|
||||
result += NumberFormatRecord(fmtidx, fmtstr).get()
|
||||
return result
|
||||
|
||||
def _all_cell_styles(self):
|
||||
result = b''
|
||||
for i in range(0, 16):
|
||||
result += XFRecord(self._default_xf, 'style').get()
|
||||
if self.style_compression == 2:
|
||||
alist = list(self._xf_x2id.items())
|
||||
else:
|
||||
alist = [(x, o) for o, x in list(self._xf_id2x.items())]
|
||||
alist.sort()
|
||||
for xf_idx, xf in alist:
|
||||
result += XFRecord(xf).get()
|
||||
return result
|
||||
|
||||
def _all_styles(self):
|
||||
return StyleRecord().get()
|
||||
|
||||
# easyxf and its supporting objects ###################################
|
||||
|
||||
class EasyXFException(Exception):
|
||||
pass
|
||||
|
||||
class EasyXFCallerError(EasyXFException):
|
||||
pass
|
||||
|
||||
class EasyXFAuthorError(EasyXFException):
|
||||
pass
|
||||
|
||||
class IntULim(object):
|
||||
# If astring represents a valid unsigned integer ('123', '0xabcd', etc)
|
||||
# and it is <= limit, return the int value; otherwise return None.
|
||||
|
||||
def __init__(self, limit):
|
||||
self.limit = limit
|
||||
|
||||
def __call__(self, astring):
|
||||
try:
|
||||
value = int(astring, 0)
|
||||
except ValueError:
|
||||
return None
|
||||
if not 0 <= value <= self.limit:
|
||||
return None
|
||||
return value
|
||||
|
||||
bool_map = {
|
||||
# Text values for all Boolean attributes
|
||||
'1': 1, 'yes': 1, 'true': 1, 'on': 1,
|
||||
'0': 0, 'no': 0, 'false': 0, 'off': 0,
|
||||
}
|
||||
|
||||
border_line_map = {
|
||||
# Text values for these borders attributes:
|
||||
# left, right, top, bottom and diag
|
||||
'no_line': 0x00,
|
||||
'thin': 0x01,
|
||||
'medium': 0x02,
|
||||
'dashed': 0x03,
|
||||
'dotted': 0x04,
|
||||
'thick': 0x05,
|
||||
'double': 0x06,
|
||||
'hair': 0x07,
|
||||
'medium_dashed': 0x08,
|
||||
'thin_dash_dotted': 0x09,
|
||||
'medium_dash_dotted': 0x0a,
|
||||
'thin_dash_dot_dotted': 0x0b,
|
||||
'medium_dash_dot_dotted': 0x0c,
|
||||
'slanted_medium_dash_dotted': 0x0d,
|
||||
}
|
||||
|
||||
charset_map = {
|
||||
# Text values for font.charset
|
||||
'ansi_latin': 0x00,
|
||||
'sys_default': 0x01,
|
||||
'symbol': 0x02,
|
||||
'apple_roman': 0x4d,
|
||||
'ansi_jap_shift_jis': 0x80,
|
||||
'ansi_kor_hangul': 0x81,
|
||||
'ansi_kor_johab': 0x82,
|
||||
'ansi_chinese_gbk': 0x86,
|
||||
'ansi_chinese_big5': 0x88,
|
||||
'ansi_greek': 0xa1,
|
||||
'ansi_turkish': 0xa2,
|
||||
'ansi_vietnamese': 0xa3,
|
||||
'ansi_hebrew': 0xb1,
|
||||
'ansi_arabic': 0xb2,
|
||||
'ansi_baltic': 0xba,
|
||||
'ansi_cyrillic': 0xcc,
|
||||
'ansi_thai': 0xde,
|
||||
'ansi_latin_ii': 0xee,
|
||||
'oem_latin_i': 0xff,
|
||||
}
|
||||
|
||||
|
||||
# Text values for colour indices. "grey" is a synonym of "gray".
|
||||
# The names are those given by Microsoft Excel 2003 to the colours
|
||||
# in the default palette. There is no great correspondence with
|
||||
# any W3C name-to-RGB mapping.
|
||||
_colour_map_text = """\
|
||||
aqua 0x31
|
||||
black 0x08
|
||||
blue 0x0C
|
||||
blue_gray 0x36
|
||||
bright_green 0x0B
|
||||
brown 0x3C
|
||||
coral 0x1D
|
||||
cyan_ega 0x0F
|
||||
dark_blue 0x12
|
||||
dark_blue_ega 0x12
|
||||
dark_green 0x3A
|
||||
dark_green_ega 0x11
|
||||
dark_purple 0x1C
|
||||
dark_red 0x10
|
||||
dark_red_ega 0x10
|
||||
dark_teal 0x38
|
||||
dark_yellow 0x13
|
||||
gold 0x33
|
||||
gray_ega 0x17
|
||||
gray25 0x16
|
||||
gray40 0x37
|
||||
gray50 0x17
|
||||
gray80 0x3F
|
||||
green 0x11
|
||||
ice_blue 0x1F
|
||||
indigo 0x3E
|
||||
ivory 0x1A
|
||||
lavender 0x2E
|
||||
light_blue 0x30
|
||||
light_green 0x2A
|
||||
light_orange 0x34
|
||||
light_turquoise 0x29
|
||||
light_yellow 0x2B
|
||||
lime 0x32
|
||||
magenta_ega 0x0E
|
||||
ocean_blue 0x1E
|
||||
olive_ega 0x13
|
||||
olive_green 0x3B
|
||||
orange 0x35
|
||||
pale_blue 0x2C
|
||||
periwinkle 0x18
|
||||
pink 0x0E
|
||||
plum 0x3D
|
||||
purple_ega 0x14
|
||||
red 0x0A
|
||||
rose 0x2D
|
||||
sea_green 0x39
|
||||
silver_ega 0x16
|
||||
sky_blue 0x28
|
||||
tan 0x2F
|
||||
teal 0x15
|
||||
teal_ega 0x15
|
||||
turquoise 0x0F
|
||||
violet 0x14
|
||||
white 0x09
|
||||
yellow 0x0D"""
|
||||
|
||||
colour_map = {}
|
||||
for _line in _colour_map_text.splitlines():
|
||||
_name, _num = _line.split()
|
||||
_num = int(_num, 0)
|
||||
colour_map[_name] = _num
|
||||
if 'gray' in _name:
|
||||
colour_map[_name.replace('gray', 'grey')] = _num
|
||||
del _colour_map_text, _line, _name, _num
|
||||
|
||||
|
||||
pattern_map = {
|
||||
# Text values for pattern.pattern
|
||||
# xlwt/doc/pattern_examples.xls showcases all of these patterns.
|
||||
'no_fill': 0,
|
||||
'none': 0,
|
||||
'solid': 1,
|
||||
'solid_fill': 1,
|
||||
'solid_pattern': 1,
|
||||
'fine_dots': 2,
|
||||
'alt_bars': 3,
|
||||
'sparse_dots': 4,
|
||||
'thick_horz_bands': 5,
|
||||
'thick_vert_bands': 6,
|
||||
'thick_backward_diag': 7,
|
||||
'thick_forward_diag': 8,
|
||||
'big_spots': 9,
|
||||
'bricks': 10,
|
||||
'thin_horz_bands': 11,
|
||||
'thin_vert_bands': 12,
|
||||
'thin_backward_diag': 13,
|
||||
'thin_forward_diag': 14,
|
||||
'squares': 15,
|
||||
'diamonds': 16,
|
||||
}
|
||||
|
||||
def any_str_func(s):
|
||||
return s.strip()
|
||||
|
||||
def colour_index_func(s, maxval=0x7F):
|
||||
try:
|
||||
value = int(s, 0)
|
||||
except ValueError:
|
||||
return None
|
||||
if not (0 <= value <= maxval):
|
||||
return None
|
||||
return value
|
||||
|
||||
colour_index_func_7 = colour_index_func
|
||||
|
||||
def colour_index_func_15(s):
|
||||
return colour_index_func(s, maxval=0x7FFF)
|
||||
|
||||
def rotation_func(s):
|
||||
try:
|
||||
value = int(s, 0)
|
||||
except ValueError:
|
||||
return None
|
||||
if not (-90 <= value <= 90):
|
||||
raise EasyXFCallerError("rotation %d: should be -90 to +90 degrees" % value)
|
||||
if value < 0:
|
||||
value = 90 - value # encode as 91 to 180 (clockwise)
|
||||
return value
|
||||
|
||||
xf_dict = {
|
||||
'align': 'alignment', # synonym
|
||||
'alignment': {
|
||||
'dire': {
|
||||
'general': 0,
|
||||
'lr': 1,
|
||||
'rl': 2,
|
||||
},
|
||||
'direction': 'dire',
|
||||
'horiz': 'horz',
|
||||
'horizontal': 'horz',
|
||||
'horz': {
|
||||
'general': 0,
|
||||
'left': 1,
|
||||
'center': 2,
|
||||
'centre': 2, # "align: horiz centre" means xf.alignment.horz is set to 2
|
||||
'right': 3,
|
||||
'filled': 4,
|
||||
'justified': 5,
|
||||
'center_across_selection': 6,
|
||||
'centre_across_selection': 6,
|
||||
'distributed': 7,
|
||||
},
|
||||
'inde': IntULim(15), # restriction: 0 <= value <= 15
|
||||
'indent': 'inde',
|
||||
'rota': [{'stacked': 255, 'none': 0, }, rotation_func],
|
||||
'rotation': 'rota',
|
||||
'shri': bool_map,
|
||||
'shrink': 'shri',
|
||||
'shrink_to_fit': 'shri',
|
||||
'vert': {
|
||||
'top': 0,
|
||||
'center': 1,
|
||||
'centre': 1,
|
||||
'bottom': 2,
|
||||
'justified': 3,
|
||||
'distributed': 4,
|
||||
},
|
||||
'vertical': 'vert',
|
||||
'wrap': bool_map,
|
||||
},
|
||||
'border': 'borders',
|
||||
'borders': {
|
||||
'left': [border_line_map, IntULim(0x0d)],
|
||||
'right': [border_line_map, IntULim(0x0d)],
|
||||
'top': [border_line_map, IntULim(0x0d)],
|
||||
'bottom': [border_line_map, IntULim(0x0d)],
|
||||
'diag': [border_line_map, IntULim(0x0d)],
|
||||
'top_colour': [colour_map, colour_index_func_7],
|
||||
'bottom_colour': [colour_map, colour_index_func_7],
|
||||
'left_colour': [colour_map, colour_index_func_7],
|
||||
'right_colour': [colour_map, colour_index_func_7],
|
||||
'diag_colour': [colour_map, colour_index_func_7],
|
||||
'top_color': 'top_colour',
|
||||
'bottom_color': 'bottom_colour',
|
||||
'left_color': 'left_colour',
|
||||
'right_color': 'right_colour',
|
||||
'diag_color': 'diag-colour',
|
||||
'need_diag_1': bool_map,
|
||||
'need_diag_2': bool_map,
|
||||
},
|
||||
'font': {
|
||||
'bold': bool_map,
|
||||
'charset': charset_map,
|
||||
'color': 'colour_index',
|
||||
'color_index': 'colour_index',
|
||||
'colour': 'colour_index',
|
||||
'colour_index': [colour_map, colour_index_func_15],
|
||||
'escapement': {'none': 0, 'superscript': 1, 'subscript': 2},
|
||||
'family': {'none': 0, 'roman': 1, 'swiss': 2, 'modern': 3, 'script': 4, 'decorative': 5, },
|
||||
'height': IntULim(0xFFFF), # practical limits are much narrower e.g. 160 to 1440 (8pt to 72pt)
|
||||
'italic': bool_map,
|
||||
'name': any_str_func,
|
||||
'outline': bool_map,
|
||||
'shadow': bool_map,
|
||||
'struck_out': bool_map,
|
||||
'underline': [bool_map, {'none': 0, 'single': 1, 'single_acc': 0x21, 'double': 2, 'double_acc': 0x22, }],
|
||||
},
|
||||
'pattern': {
|
||||
'back_color': 'pattern_back_colour',
|
||||
'back_colour': 'pattern_back_colour',
|
||||
'fore_color': 'pattern_fore_colour',
|
||||
'fore_colour': 'pattern_fore_colour',
|
||||
'pattern': [pattern_map, IntULim(16)],
|
||||
'pattern_back_color': 'pattern_back_colour',
|
||||
'pattern_back_colour': [colour_map, colour_index_func_7],
|
||||
'pattern_fore_color': 'pattern_fore_colour',
|
||||
'pattern_fore_colour': [colour_map, colour_index_func_7],
|
||||
},
|
||||
'protection': {
|
||||
'cell_locked' : bool_map,
|
||||
'formula_hidden': bool_map,
|
||||
},
|
||||
}
|
||||
|
||||
def _esplit(s, split_char, esc_char="\\"):
|
||||
escaped = False
|
||||
olist = ['']
|
||||
for c in s:
|
||||
if escaped:
|
||||
olist[-1] += c
|
||||
escaped = False
|
||||
elif c == esc_char:
|
||||
escaped = True
|
||||
elif c == split_char:
|
||||
olist.append('')
|
||||
else:
|
||||
olist[-1] += c
|
||||
return olist
|
||||
|
||||
def _parse_strg_to_obj(strg, obj, parse_dict,
|
||||
field_sep=",", line_sep=";", intro_sep=":", esc_char="\\", debug=False):
|
||||
for line in _esplit(strg, line_sep, esc_char):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
break
|
||||
split_line = _esplit(line, intro_sep, esc_char)
|
||||
if len(split_line) != 2:
|
||||
raise EasyXFCallerError('line %r should have exactly 1 "%c"' % (line, intro_sep))
|
||||
section, item_str = split_line
|
||||
section = section.strip().lower()
|
||||
for counter in range(2):
|
||||
result = parse_dict.get(section)
|
||||
if result is None:
|
||||
raise EasyXFCallerError('section %r is unknown' % section)
|
||||
if isinstance(result, dict):
|
||||
break
|
||||
if not isinstance(result, str):
|
||||
raise EasyXFAuthorError(
|
||||
'section %r should map to dict or str object; found %r' % (section, type(result)))
|
||||
# synonym
|
||||
old_section = section
|
||||
section = result
|
||||
else:
|
||||
raise EasyXFAuthorError('Attempt to define synonym of synonym (%r: %r)' % (old_section, result))
|
||||
section_dict = result
|
||||
section_obj = getattr(obj, section, None)
|
||||
if section_obj is None:
|
||||
raise EasyXFAuthorError('instance of %s class has no attribute named %s' % (obj.__class__.__name__, section))
|
||||
for kv_str in _esplit(item_str, field_sep, esc_char):
|
||||
guff = kv_str.split()
|
||||
if not guff:
|
||||
continue
|
||||
k = guff[0].lower().replace('-', '_')
|
||||
v = ' '.join(guff[1:])
|
||||
if not v:
|
||||
raise EasyXFCallerError("no value supplied for %s.%s" % (section, k))
|
||||
for counter in range(2):
|
||||
result = section_dict.get(k)
|
||||
if result is None:
|
||||
raise EasyXFCallerError('%s.%s is not a known attribute' % (section, k))
|
||||
if not isinstance(result, str):
|
||||
break
|
||||
# synonym
|
||||
old_k = k
|
||||
k = result
|
||||
else:
|
||||
raise EasyXFAuthorError('Attempt to define synonym of synonym (%r: %r)' % (old_k, result))
|
||||
value_info = result
|
||||
if not isinstance(value_info, list):
|
||||
value_info = [value_info]
|
||||
for value_rule in value_info:
|
||||
if isinstance(value_rule, dict):
|
||||
# dict maps strings to integer field values
|
||||
vl = v.lower().replace('-', '_')
|
||||
if vl in value_rule:
|
||||
value = value_rule[vl]
|
||||
break
|
||||
elif isinstance(value_rule, collections.Callable):
|
||||
value = value_rule(v)
|
||||
if value is not None:
|
||||
break
|
||||
else:
|
||||
raise EasyXFAuthorError("unknown value rule for attribute %r: %r" % (k, value_rule))
|
||||
else:
|
||||
raise EasyXFCallerError("unexpected value %r for %s.%s" % (v, section, k))
|
||||
try:
|
||||
orig = getattr(section_obj, k)
|
||||
except AttributeError:
|
||||
raise EasyXFAuthorError('%s.%s in dictionary but not in supplied object' % (section, k))
|
||||
if debug: print("+++ %s.%s = %r # %s; was %r" % (section, k, value, v, orig))
|
||||
setattr(section_obj, k, value)
|
||||
|
||||
def easyxf(strg_to_parse="", num_format_str=None,
|
||||
field_sep=",", line_sep=";", intro_sep=":", esc_char="\\", debug=False):
|
||||
xfobj = XFStyle()
|
||||
if num_format_str is not None:
|
||||
xfobj.num_format_str = num_format_str
|
||||
if strg_to_parse:
|
||||
_parse_strg_to_obj(strg_to_parse, xfobj, xf_dict,
|
||||
field_sep=field_sep, line_sep=line_sep, intro_sep=intro_sep, esc_char=esc_char, debug=debug)
|
||||
return xfobj
|
||||
@@ -1,79 +0,0 @@
|
||||
'''
|
||||
From BIFF8 on, strings are always stored using UTF-16LE text encoding. The
|
||||
character array is a sequence of 16-bit values4. Additionally it is
|
||||
possible to use a compressed format, which omits the high bytes of all
|
||||
characters, if they are all zero.
|
||||
|
||||
The following tables describe the standard format of the entire string, but
|
||||
in many records the strings differ from this format. This will be mentioned
|
||||
separately. It is possible (but not required) to store Rich-Text formatting
|
||||
information and Asian phonetic information inside a Unicode string. This
|
||||
results in four different ways to store a string. The character array
|
||||
is not zero-terminated.
|
||||
|
||||
The string consists of the character count (as usual an 8-bit value or
|
||||
a 16-bit value), option flags, the character array and optional formatting
|
||||
information. If the string is empty, sometimes the option flags field will
|
||||
not occur. This is mentioned at the respective place.
|
||||
|
||||
Offset Size Contents
|
||||
0 1 or 2 Length of the string (character count, ln)
|
||||
1 or 2 1 Option flags:
|
||||
Bit Mask Contents
|
||||
0 01H Character compression (ccompr):
|
||||
0 = Compressed (8-bit characters)
|
||||
1 = Uncompressed (16-bit characters)
|
||||
2 04H Asian phonetic settings (phonetic):
|
||||
0 = Does not contain Asian phonetic settings
|
||||
1 = Contains Asian phonetic settings
|
||||
3 08H Rich-Text settings (richtext):
|
||||
0 = Does not contain Rich-Text settings
|
||||
1 = Contains Rich-Text settings
|
||||
[2 or 3] 2 (optional, only if richtext=1) Number of Rich-Text formatting runs (rt)
|
||||
[var.] 4 (optional, only if phonetic=1) Size of Asian phonetic settings block (in bytes, sz)
|
||||
var. ln or
|
||||
2·ln Character array (8-bit characters or 16-bit characters, dependent on ccompr)
|
||||
[var.] 4·rt (optional, only if richtext=1) List of rt formatting runs
|
||||
[var.] sz (optional, only if phonetic=1) Asian Phonetic Settings Block
|
||||
'''
|
||||
|
||||
|
||||
from struct import pack
|
||||
|
||||
def upack2(s, encoding='ascii'):
|
||||
# If not unicode, make it so.
|
||||
if isinstance(s, str):
|
||||
us = s
|
||||
else:
|
||||
us = str(s, encoding)
|
||||
# Limit is based on number of content characters
|
||||
# (not on number of bytes in packed result)
|
||||
len_us = len(us)
|
||||
if len_us > 65535:
|
||||
raise Exception('String longer than 65535 characters')
|
||||
try:
|
||||
encs = us.encode('latin1')
|
||||
# Success here means all chars are in U+0000 to U+00FF
|
||||
# inclusive, meaning that we can use "compressed format".
|
||||
flag = 0
|
||||
except UnicodeEncodeError:
|
||||
encs = us.encode('utf_16_le')
|
||||
flag = 1
|
||||
return pack('<HB', len_us, flag) + encs
|
||||
|
||||
def upack1(s, encoding='ascii'):
|
||||
# Same as upack2(), but with a one-byte length field.
|
||||
if isinstance(s, str):
|
||||
us = s
|
||||
else:
|
||||
us = str(s, encoding)
|
||||
len_us = len(us)
|
||||
if len_us > 255:
|
||||
raise Exception('String longer than 255 characters')
|
||||
try:
|
||||
encs = us.encode('latin1')
|
||||
flag = 0
|
||||
except UnicodeEncodeError:
|
||||
encs = us.encode('utf_16_le')
|
||||
flag = 1
|
||||
return pack('<BB', len_us, flag) + encs
|
||||
@@ -1,196 +0,0 @@
|
||||
# pyXLWriter: A library for generating Excel Spreadsheets
|
||||
# Copyright (c) 2004 Evgeny Filatov <fufff@users.sourceforge.net>
|
||||
# Copyright (c) 2002-2004 John McNamara (Perl Spreadsheet::WriteExcel)
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this library; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#----------------------------------------------------------------------------
|
||||
# This module was written/ported from PERL Spreadsheet::WriteExcel module
|
||||
# The author of the PERL Spreadsheet::WriteExcel module is John McNamara
|
||||
# <jmcnamara@cpan.org>
|
||||
#----------------------------------------------------------------------------
|
||||
# See the README.txt distributed with pyXLWriter for more details.
|
||||
|
||||
# Portions are (C) Roman V. Kiseliov, 2005
|
||||
|
||||
|
||||
# Utilities for work with reference to cells and with sheetnames
|
||||
|
||||
|
||||
__rev_id__ = """$Id: Utils.py 3844 2009-05-20 01:02:54Z sjmachin $"""
|
||||
|
||||
import re
|
||||
from struct import pack
|
||||
from .ExcelMagic import MAX_ROW, MAX_COL
|
||||
|
||||
|
||||
_re_cell_ex = re.compile(r"(\$?)([A-I]?[A-Z])(\$?)(\d+)", re.IGNORECASE)
|
||||
_re_row_range = re.compile(r"\$?(\d+):\$?(\d+)")
|
||||
_re_col_range = re.compile(r"\$?([A-I]?[A-Z]):\$?([A-I]?[A-Z])", re.IGNORECASE)
|
||||
_re_cell_range = re.compile(r"\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)", re.IGNORECASE)
|
||||
_re_cell_ref = re.compile(r"\$?([A-I]?[A-Z]\$?\d+)", re.IGNORECASE)
|
||||
|
||||
|
||||
def col_by_name(colname):
|
||||
"""
|
||||
"""
|
||||
col = 0
|
||||
pow = 1
|
||||
for i in range(len(colname)-1, -1, -1):
|
||||
ch = colname[i]
|
||||
col += (ord(ch) - ord('A') + 1) * pow
|
||||
pow *= 26
|
||||
return col - 1
|
||||
|
||||
|
||||
def cell_to_rowcol(cell):
|
||||
"""Convert an Excel cell reference string in A1 notation
|
||||
to numeric row/col notation.
|
||||
|
||||
Returns: row, col, row_abs, col_abs
|
||||
|
||||
"""
|
||||
m = _re_cell_ex.match(cell)
|
||||
if not m:
|
||||
raise Exception("Ill-formed single_cell reference: %s" % cell)
|
||||
col_abs, col, row_abs, row = m.groups()
|
||||
row_abs = bool(row_abs)
|
||||
col_abs = bool(col_abs)
|
||||
row = int(row) - 1
|
||||
col = col_by_name(col.upper())
|
||||
return row, col, row_abs, col_abs
|
||||
|
||||
|
||||
def cell_to_rowcol2(cell):
|
||||
"""Convert an Excel cell reference string in A1 notation
|
||||
to numeric row/col notation.
|
||||
|
||||
Returns: row, col
|
||||
|
||||
"""
|
||||
m = _re_cell_ex.match(cell)
|
||||
if not m:
|
||||
raise Exception("Error in cell format")
|
||||
col_abs, col, row_abs, row = m.groups()
|
||||
# Convert base26 column string to number
|
||||
# All your Base are belong to us.
|
||||
row = int(row) - 1
|
||||
col = col_by_name(col.upper())
|
||||
return row, col
|
||||
|
||||
|
||||
def rowcol_to_cell(row, col, row_abs=False, col_abs=False):
|
||||
"""Convert numeric row/col notation to an Excel cell reference string in
|
||||
A1 notation.
|
||||
|
||||
"""
|
||||
assert 0 <= row < MAX_ROW # MAX_ROW counts from 1
|
||||
assert 0 <= col < MAX_COL # MAX_COL counts from 1
|
||||
d = col // 26
|
||||
m = col % 26
|
||||
chr1 = "" # Most significant character in AA1
|
||||
if row_abs:
|
||||
row_abs = '$'
|
||||
else:
|
||||
row_abs = ''
|
||||
if col_abs:
|
||||
col_abs = '$'
|
||||
else:
|
||||
col_abs = ''
|
||||
if d > 0:
|
||||
chr1 = chr(ord('A') + d - 1)
|
||||
chr2 = chr(ord('A') + m)
|
||||
# Zero index to 1-index
|
||||
return col_abs + chr1 + chr2 + row_abs + str(row + 1)
|
||||
|
||||
def rowcol_pair_to_cellrange(row1, col1, row2, col2,
|
||||
row1_abs=False, col1_abs=False, row2_abs=False, col2_abs=False):
|
||||
"""Convert two (row,column) pairs
|
||||
into a cell range string in A1:B2 notation.
|
||||
|
||||
Returns: cell range string
|
||||
"""
|
||||
assert row1 <= row2
|
||||
assert col1 <= col2
|
||||
return (
|
||||
rowcol_to_cell(row1, col1, row1_abs, col1_abs)
|
||||
+ ":"
|
||||
+ rowcol_to_cell(row2, col2, row2_abs, col2_abs)
|
||||
)
|
||||
|
||||
def cellrange_to_rowcol_pair(cellrange):
|
||||
"""Convert cell range string in A1 notation to numeric row/col
|
||||
pair.
|
||||
|
||||
Returns: row1, col1, row2, col2
|
||||
|
||||
"""
|
||||
cellrange = cellrange.upper()
|
||||
# Convert a row range: '1:3'
|
||||
res = _re_row_range.match(cellrange)
|
||||
if res:
|
||||
row1 = int(res.group(1)) - 1
|
||||
col1 = 0
|
||||
row2 = int(res.group(2)) - 1
|
||||
col2 = -1
|
||||
return row1, col1, row2, col2
|
||||
# Convert a column range: 'A:A' or 'B:G'.
|
||||
# A range such as A:A is equivalent to A1:A16384, so add rows as required
|
||||
res = _re_col_range.match(cellrange)
|
||||
if res:
|
||||
col1 = col_by_name(res.group(1).upper())
|
||||
row1 = 0
|
||||
col2 = col_by_name(res.group(2).upper())
|
||||
row2 = -1
|
||||
return row1, col1, row2, col2
|
||||
# Convert a cell range: 'A1:B7'
|
||||
res = _re_cell_range.match(cellrange)
|
||||
if res:
|
||||
row1, col1 = cell_to_rowcol2(res.group(1))
|
||||
row2, col2 = cell_to_rowcol2(res.group(2))
|
||||
return row1, col1, row2, col2
|
||||
# Convert a cell reference: 'A1' or 'AD2000'
|
||||
res = _re_cell_ref.match(cellrange)
|
||||
if res:
|
||||
row1, col1 = cell_to_rowcol2(res.group(1))
|
||||
return row1, col1, row1, col1
|
||||
raise Exception("Unknown cell reference %s" % (cell))
|
||||
|
||||
|
||||
def cell_to_packed_rowcol(cell):
|
||||
""" pack row and column into the required 4 byte format """
|
||||
row, col, row_abs, col_abs = cell_to_rowcol(cell)
|
||||
if col >= MAX_COL:
|
||||
raise Exception("Column %s greater than IV in formula" % cell)
|
||||
if row >= MAX_ROW: # this for BIFF8. for BIFF7 available 2^14
|
||||
raise Exception("Row %s greater than %d in formula" % (cell, MAX_ROW))
|
||||
col |= int(not row_abs) << 15
|
||||
col |= int(not col_abs) << 14
|
||||
return row, col
|
||||
|
||||
# === sheetname functions ===
|
||||
|
||||
def valid_sheet_name(sheet_name):
|
||||
if sheet_name == "" or sheet_name[0] == "'" or len(sheet_name) > 31:
|
||||
return False
|
||||
for c in sheet_name:
|
||||
if c in "[]:\\?/*\x00":
|
||||
return False
|
||||
return True
|
||||
|
||||
def quote_sheet_name(unquoted_sheet_name):
|
||||
if not valid_sheet_name(unquoted_sheet_name):
|
||||
raise Exception(
|
||||
'attempt to quote an invalid worksheet name %r' % unquoted_sheet_name)
|
||||
return "'" + unquoted_sheet_name.replace("'", "''") + "'"
|
||||
@@ -1,635 +0,0 @@
|
||||
'''
|
||||
Record Order in BIFF8
|
||||
Workbook Globals Substream
|
||||
BOF Type = workbook globals
|
||||
Interface Header
|
||||
MMS
|
||||
Interface End
|
||||
WRITEACCESS
|
||||
CODEPAGE
|
||||
DSF
|
||||
TABID
|
||||
FNGROUPCOUNT
|
||||
Workbook Protection Block
|
||||
WINDOWPROTECT
|
||||
PROTECT
|
||||
PASSWORD
|
||||
PROT4REV
|
||||
PROT4REVPASS
|
||||
BACKUP
|
||||
HIDEOBJ
|
||||
WINDOW1
|
||||
DATEMODE
|
||||
PRECISION
|
||||
REFRESHALL
|
||||
BOOKBOOL
|
||||
FONT +
|
||||
FORMAT *
|
||||
XF +
|
||||
STYLE +
|
||||
? PALETTE
|
||||
USESELFS
|
||||
|
||||
BOUNDSHEET +
|
||||
|
||||
COUNTRY
|
||||
? Link Table
|
||||
SST
|
||||
ExtSST
|
||||
EOF
|
||||
'''
|
||||
|
||||
from . import BIFFRecords
|
||||
from . import Style
|
||||
|
||||
class Workbook(object):
|
||||
|
||||
#################################################################
|
||||
## Constructor
|
||||
#################################################################
|
||||
def __init__(self, encoding='ascii', style_compression=0):
|
||||
self.encoding = encoding
|
||||
self.__owner = 'None'
|
||||
self.__country_code = None # 0x07 is Russia :-)
|
||||
self.__wnd_protect = 0
|
||||
self.__obj_protect = 0
|
||||
self.__protect = 0
|
||||
self.__backup_on_save = 0
|
||||
# for WINDOW1 record
|
||||
self.__hpos_twips = 0x01E0
|
||||
self.__vpos_twips = 0x005A
|
||||
self.__width_twips = 0x3FCF
|
||||
self.__height_twips = 0x2A4E
|
||||
|
||||
self.__active_sheet = 0
|
||||
self.__first_tab_index = 0
|
||||
self.__selected_tabs = 0x01
|
||||
self.__tab_width_twips = 0x0258
|
||||
|
||||
self.__wnd_hidden = 0
|
||||
self.__wnd_mini = 0
|
||||
self.__hscroll_visible = 1
|
||||
self.__vscroll_visible = 1
|
||||
self.__tabs_visible = 1
|
||||
|
||||
self.__styles = Style.StyleCollection(style_compression)
|
||||
|
||||
self.__dates_1904 = 0
|
||||
self.__use_cell_values = 1
|
||||
|
||||
self.__sst = BIFFRecords.SharedStringTable(self.encoding)
|
||||
|
||||
self.__worksheets = []
|
||||
self.__worksheet_idx_from_name = {}
|
||||
self.__sheet_refs = {}
|
||||
self._supbook_xref = {}
|
||||
self._xcall_xref = {}
|
||||
self._ownbook_supbookx = None
|
||||
self._ownbook_supbook_ref = None
|
||||
self._xcall_supbookx = None
|
||||
self._xcall_supbook_ref = None
|
||||
|
||||
|
||||
|
||||
#################################################################
|
||||
## Properties, "getters", "setters"
|
||||
#################################################################
|
||||
|
||||
def get_style_stats(self):
|
||||
return self.__styles.stats[:]
|
||||
|
||||
def set_owner(self, value):
|
||||
self.__owner = value
|
||||
|
||||
def get_owner(self):
|
||||
return self.__owner
|
||||
|
||||
owner = property(get_owner, set_owner)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_country_code(self, value):
|
||||
self.__country_code = value
|
||||
|
||||
def get_country_code(self):
|
||||
return self.__country_code
|
||||
|
||||
country_code = property(get_country_code, set_country_code)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_wnd_protect(self, value):
|
||||
self.__wnd_protect = int(value)
|
||||
|
||||
def get_wnd_protect(self):
|
||||
return bool(self.__wnd_protect)
|
||||
|
||||
wnd_protect = property(get_wnd_protect, set_wnd_protect)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_obj_protect(self, value):
|
||||
self.__obj_protect = int(value)
|
||||
|
||||
def get_obj_protect(self):
|
||||
return bool(self.__obj_protect)
|
||||
|
||||
obj_protect = property(get_obj_protect, set_obj_protect)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_protect(self, value):
|
||||
self.__protect = int(value)
|
||||
|
||||
def get_protect(self):
|
||||
return bool(self.__protect)
|
||||
|
||||
protect = property(get_protect, set_protect)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_backup_on_save(self, value):
|
||||
self.__backup_on_save = int(value)
|
||||
|
||||
def get_backup_on_save(self):
|
||||
return bool(self.__backup_on_save)
|
||||
|
||||
backup_on_save = property(get_backup_on_save, set_backup_on_save)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_hpos(self, value):
|
||||
self.__hpos_twips = value & 0xFFFF
|
||||
|
||||
def get_hpos(self):
|
||||
return self.__hpos_twips
|
||||
|
||||
hpos = property(get_hpos, set_hpos)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_vpos(self, value):
|
||||
self.__vpos_twips = value & 0xFFFF
|
||||
|
||||
def get_vpos(self):
|
||||
return self.__vpos_twips
|
||||
|
||||
vpos = property(get_vpos, set_vpos)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_width(self, value):
|
||||
self.__width_twips = value & 0xFFFF
|
||||
|
||||
def get_width(self):
|
||||
return self.__width_twips
|
||||
|
||||
width = property(get_width, set_width)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_height(self, value):
|
||||
self.__height_twips = value & 0xFFFF
|
||||
|
||||
def get_height(self):
|
||||
return self.__height_twips
|
||||
|
||||
height = property(get_height, set_height)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_active_sheet(self, value):
|
||||
self.__active_sheet = value & 0xFFFF
|
||||
self.__first_tab_index = self.__active_sheet
|
||||
|
||||
def get_active_sheet(self):
|
||||
return self.__active_sheet
|
||||
|
||||
active_sheet = property(get_active_sheet, set_active_sheet)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_tab_width(self, value):
|
||||
self.__tab_width_twips = value & 0xFFFF
|
||||
|
||||
def get_tab_width(self):
|
||||
return self.__tab_width_twips
|
||||
|
||||
tab_width = property(get_tab_width, set_tab_width)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_wnd_visible(self, value):
|
||||
self.__wnd_hidden = int(not value)
|
||||
|
||||
def get_wnd_visible(self):
|
||||
return not bool(self.__wnd_hidden)
|
||||
|
||||
wnd_visible = property(get_wnd_visible, set_wnd_visible)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_wnd_mini(self, value):
|
||||
self.__wnd_mini = int(value)
|
||||
|
||||
def get_wnd_mini(self):
|
||||
return bool(self.__wnd_mini)
|
||||
|
||||
wnd_mini = property(get_wnd_mini, set_wnd_mini)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_hscroll_visible(self, value):
|
||||
self.__hscroll_visible = int(value)
|
||||
|
||||
def get_hscroll_visible(self):
|
||||
return bool(self.__hscroll_visible)
|
||||
|
||||
hscroll_visible = property(get_hscroll_visible, set_hscroll_visible)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_vscroll_visible(self, value):
|
||||
self.__vscroll_visible = int(value)
|
||||
|
||||
def get_vscroll_visible(self):
|
||||
return bool(self.__vscroll_visible)
|
||||
|
||||
vscroll_visible = property(get_vscroll_visible, set_vscroll_visible)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_tabs_visible(self, value):
|
||||
self.__tabs_visible = int(value)
|
||||
|
||||
def get_tabs_visible(self):
|
||||
return bool(self.__tabs_visible)
|
||||
|
||||
tabs_visible = property(get_tabs_visible, set_tabs_visible)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_dates_1904(self, value):
|
||||
self.__dates_1904 = int(value)
|
||||
|
||||
def get_dates_1904(self):
|
||||
return bool(self.__dates_1904)
|
||||
|
||||
dates_1904 = property(get_dates_1904, set_dates_1904)
|
||||
|
||||
#################################################################
|
||||
|
||||
def set_use_cell_values(self, value):
|
||||
self.__use_cell_values = int(value)
|
||||
|
||||
def get_use_cell_values(self):
|
||||
return bool(self.__use_cell_values)
|
||||
|
||||
use_cell_values = property(get_use_cell_values, set_use_cell_values)
|
||||
|
||||
#################################################################
|
||||
|
||||
def get_default_style(self):
|
||||
return self.__styles.default_style
|
||||
|
||||
default_style = property(get_default_style)
|
||||
|
||||
##################################################################
|
||||
## Methods
|
||||
##################################################################
|
||||
|
||||
def add_style(self, style):
|
||||
return self.__styles.add(style)
|
||||
|
||||
def add_str(self, s):
|
||||
return self.__sst.add_str(s)
|
||||
|
||||
def del_str(self, sst_idx):
|
||||
self.__sst.del_str(sst_idx)
|
||||
|
||||
def str_index(self, s):
|
||||
return self.__sst.str_index(s)
|
||||
|
||||
def add_sheet(self, sheetname, cell_overwrite_ok=False):
|
||||
from . import Worksheet, Utils
|
||||
if not isinstance(sheetname, str):
|
||||
sheetname = sheetname.decode(self.encoding)
|
||||
if not Utils.valid_sheet_name(sheetname):
|
||||
raise Exception("invalid worksheet name %r" % sheetname)
|
||||
lower_name = sheetname.lower()
|
||||
if lower_name in self.__worksheet_idx_from_name:
|
||||
raise Exception("duplicate worksheet name %r" % sheetname)
|
||||
self.__worksheet_idx_from_name[lower_name] = len(self.__worksheets)
|
||||
self.__worksheets.append(Worksheet(sheetname, self, cell_overwrite_ok))
|
||||
return self.__worksheets[-1]
|
||||
|
||||
def get_sheet(self, sheetnum):
|
||||
return self.__worksheets[sheetnum]
|
||||
|
||||
def raise_bad_sheetname(self, sheetname):
|
||||
raise Exception("Formula: unknown sheet name %s" % sheetname)
|
||||
|
||||
def convert_sheetindex(self, strg_ref, n_sheets):
|
||||
idx = int(strg_ref)
|
||||
if 0 <= idx < n_sheets:
|
||||
return idx
|
||||
msg = "Formula: sheet index (%s) >= number of sheets (%d)" % (strg_ref, n_sheets)
|
||||
raise Exception(msg)
|
||||
|
||||
def _get_supbook_index(self, tag):
|
||||
if tag in self._supbook_xref:
|
||||
return self._supbook_xref[tag]
|
||||
self._supbook_xref[tag] = idx = len(self._supbook_xref)
|
||||
return idx
|
||||
|
||||
def setup_ownbook(self):
|
||||
self._ownbook_supbookx = self._get_supbook_index(('ownbook', 0))
|
||||
self._ownbook_supbook_ref = None
|
||||
reference = (self._ownbook_supbookx, 0xFFFE, 0xFFFE)
|
||||
if reference in self.__sheet_refs:
|
||||
raise Exception("can't happen")
|
||||
self.__sheet_refs[reference] = self._ownbook_supbook_ref = len(self.__sheet_refs)
|
||||
|
||||
def setup_xcall(self):
|
||||
self._xcall_supbookx = self._get_supbook_index(('xcall', 0))
|
||||
self._xcall_supbook_ref = None
|
||||
reference = (self._xcall_supbookx, 0xFFFE, 0xFFFE)
|
||||
if reference in self.__sheet_refs:
|
||||
raise Exception("can't happen")
|
||||
self.__sheet_refs[reference] = self._xcall_supbook_ref = len(self.__sheet_refs)
|
||||
|
||||
def add_sheet_reference(self, formula):
|
||||
patches = []
|
||||
n_sheets = len(self.__worksheets)
|
||||
sheet_refs, xcall_refs = formula.get_references()
|
||||
|
||||
for ref0, ref1, offset in sheet_refs:
|
||||
if not ref0.isdigit():
|
||||
try:
|
||||
ref0n = self.__worksheet_idx_from_name[ref0.lower()]
|
||||
except KeyError:
|
||||
self.raise_bad_sheetname(ref0)
|
||||
else:
|
||||
ref0n = self.convert_sheetindex(ref0, n_sheets)
|
||||
if ref1 == ref0:
|
||||
ref1n = ref0n
|
||||
elif not ref1.isdigit():
|
||||
try:
|
||||
ref1n = self.__worksheet_idx_from_name[ref1.lower()]
|
||||
except KeyError:
|
||||
self.raise_bad_sheetname(ref1)
|
||||
else:
|
||||
ref1n = self.convert_sheetindex(ref1, n_sheets)
|
||||
if ref1n < ref0n:
|
||||
msg = "Formula: sheets out of order; %r:%r -> (%d, %d)" \
|
||||
% (ref0, ref1, ref0n, ref1n)
|
||||
raise Exception(msg)
|
||||
if self._ownbook_supbookx is None:
|
||||
self.setup_ownbook()
|
||||
reference = (self._ownbook_supbookx, ref0n, ref1n)
|
||||
if reference in self.__sheet_refs:
|
||||
patches.append((offset, self.__sheet_refs[reference]))
|
||||
else:
|
||||
nrefs = len(self.__sheet_refs)
|
||||
if nrefs > 65535:
|
||||
raise Exception('More than 65536 inter-sheet references')
|
||||
self.__sheet_refs[reference] = nrefs
|
||||
patches.append((offset, nrefs))
|
||||
|
||||
for funcname, offset in xcall_refs:
|
||||
if self._ownbook_supbookx is None:
|
||||
self.setup_ownbook()
|
||||
if self._xcall_supbookx is None:
|
||||
self.setup_xcall()
|
||||
# print funcname, self._supbook_xref
|
||||
patches.append((offset, self._xcall_supbook_ref))
|
||||
if not isinstance(funcname, str):
|
||||
funcname = funcname.decode(self.encoding)
|
||||
if funcname in self._xcall_xref:
|
||||
idx = self._xcall_xref[funcname]
|
||||
else:
|
||||
self._xcall_xref[funcname] = idx = len(self._xcall_xref)
|
||||
patches.append((offset + 2, idx + 1))
|
||||
|
||||
formula.patch_references(patches)
|
||||
|
||||
##################################################################
|
||||
## BIFF records generation
|
||||
##################################################################
|
||||
|
||||
def __bof_rec(self):
|
||||
return BIFFRecords.Biff8BOFRecord(BIFFRecords.Biff8BOFRecord.BOOK_GLOBAL).get()
|
||||
|
||||
def __eof_rec(self):
|
||||
return BIFFRecords.EOFRecord().get()
|
||||
|
||||
def __intf_hdr_rec(self):
|
||||
return BIFFRecords.InteraceHdrRecord().get()
|
||||
|
||||
def __intf_end_rec(self):
|
||||
return BIFFRecords.InteraceEndRecord().get()
|
||||
|
||||
def __intf_mms_rec(self):
|
||||
return BIFFRecords.MMSRecord().get()
|
||||
|
||||
def __write_access_rec(self):
|
||||
return BIFFRecords.WriteAccessRecord(self.__owner).get()
|
||||
|
||||
def __wnd_protect_rec(self):
|
||||
return BIFFRecords.WindowProtectRecord(self.__wnd_protect).get()
|
||||
|
||||
def __obj_protect_rec(self):
|
||||
return BIFFRecords.ObjectProtectRecord(self.__obj_protect).get()
|
||||
|
||||
def __protect_rec(self):
|
||||
return BIFFRecords.ProtectRecord(self.__protect).get()
|
||||
|
||||
def __password_rec(self):
|
||||
return BIFFRecords.PasswordRecord().get()
|
||||
|
||||
def __prot4rev_rec(self):
|
||||
return BIFFRecords.Prot4RevRecord().get()
|
||||
|
||||
def __prot4rev_pass_rec(self):
|
||||
return BIFFRecords.Prot4RevPassRecord().get()
|
||||
|
||||
def __backup_rec(self):
|
||||
return BIFFRecords.BackupRecord(self.__backup_on_save).get()
|
||||
|
||||
def __hide_obj_rec(self):
|
||||
return BIFFRecords.HideObjRecord().get()
|
||||
|
||||
def __window1_rec(self):
|
||||
flags = 0
|
||||
flags |= (self.__wnd_hidden) << 0
|
||||
flags |= (self.__wnd_mini) << 1
|
||||
flags |= (self.__hscroll_visible) << 3
|
||||
flags |= (self.__vscroll_visible) << 4
|
||||
flags |= (self.__tabs_visible) << 5
|
||||
|
||||
return BIFFRecords.Window1Record(self.__hpos_twips, self.__vpos_twips,
|
||||
self.__width_twips, self.__height_twips,
|
||||
flags,
|
||||
self.__active_sheet, self.__first_tab_index,
|
||||
self.__selected_tabs, self.__tab_width_twips).get()
|
||||
|
||||
def __codepage_rec(self):
|
||||
return BIFFRecords.CodepageBiff8Record().get()
|
||||
|
||||
def __country_rec(self):
|
||||
if not self.__country_code:
|
||||
return b''
|
||||
return BIFFRecords.CountryRecord(self.__country_code, self.__country_code).get()
|
||||
|
||||
def __dsf_rec(self):
|
||||
return BIFFRecords.DSFRecord().get()
|
||||
|
||||
def __tabid_rec(self):
|
||||
return BIFFRecords.TabIDRecord(len(self.__worksheets)).get()
|
||||
|
||||
def __fngroupcount_rec(self):
|
||||
return BIFFRecords.FnGroupCountRecord().get()
|
||||
|
||||
def __datemode_rec(self):
|
||||
return BIFFRecords.DateModeRecord(self.__dates_1904).get()
|
||||
|
||||
def __precision_rec(self):
|
||||
return BIFFRecords.PrecisionRecord(self.__use_cell_values).get()
|
||||
|
||||
def __refresh_all_rec(self):
|
||||
return BIFFRecords.RefreshAllRecord().get()
|
||||
|
||||
def __bookbool_rec(self):
|
||||
return BIFFRecords.BookBoolRecord().get()
|
||||
|
||||
def __all_fonts_num_formats_xf_styles_rec(self):
|
||||
return self.__styles.get_biff_data()
|
||||
|
||||
def __palette_rec(self):
|
||||
result = b''
|
||||
return result
|
||||
|
||||
def __useselfs_rec(self):
|
||||
return BIFFRecords.UseSelfsRecord().get()
|
||||
|
||||
def __boundsheets_rec(self, data_len_before, data_len_after, sheet_biff_lens):
|
||||
# .................................
|
||||
# BOUNDSEHEET0
|
||||
# BOUNDSEHEET1
|
||||
# BOUNDSEHEET2
|
||||
# ..................................
|
||||
# WORKSHEET0
|
||||
# WORKSHEET1
|
||||
# WORKSHEET2
|
||||
boundsheets_len = 0
|
||||
for sheet in self.__worksheets:
|
||||
boundsheets_len += len(BIFFRecords.BoundSheetRecord(
|
||||
0x00, sheet.visibility, sheet.name, self.encoding
|
||||
).get())
|
||||
|
||||
start = data_len_before + boundsheets_len + data_len_after
|
||||
|
||||
result = b''
|
||||
for sheet_biff_len, sheet in zip(sheet_biff_lens, self.__worksheets):
|
||||
result += BIFFRecords.BoundSheetRecord(
|
||||
start, sheet.visibility, sheet.name, self.encoding
|
||||
).get()
|
||||
start += sheet_biff_len
|
||||
return result
|
||||
|
||||
def __all_links_rec(self):
|
||||
pieces = []
|
||||
temp = [(idx, tag) for tag, idx in list(self._supbook_xref.items())]
|
||||
temp.sort()
|
||||
for idx, tag in temp:
|
||||
stype, snum = tag
|
||||
if stype == 'ownbook':
|
||||
rec = BIFFRecords.InternalReferenceSupBookRecord(len(self.__worksheets)).get()
|
||||
pieces.append(rec)
|
||||
elif stype == 'xcall':
|
||||
rec = BIFFRecords.XcallSupBookRecord().get()
|
||||
pieces.append(rec)
|
||||
temp = [(idx, name) for name, idx in list(self._xcall_xref.items())]
|
||||
temp.sort()
|
||||
for idx, name in temp:
|
||||
rec = BIFFRecords.ExternnameRecord(
|
||||
options=0, index=0, name=name, fmla='\x02\x00\x1c\x17').get()
|
||||
pieces.append(rec)
|
||||
else:
|
||||
raise Exception('unknown supbook stype %r' % stype)
|
||||
if len(self.__sheet_refs) > 0:
|
||||
# get references in index order
|
||||
temp = [(idx, ref) for ref, idx in list(self.__sheet_refs.items())]
|
||||
temp.sort()
|
||||
temp = [ref for idx, ref in temp]
|
||||
externsheet_record = BIFFRecords.ExternSheetRecord(temp).get()
|
||||
pieces.append(externsheet_record)
|
||||
return b''.join(pieces)
|
||||
|
||||
def __sst_rec(self):
|
||||
return self.__sst.get_biff_record()
|
||||
|
||||
def __ext_sst_rec(self, abs_stream_pos):
|
||||
return b''
|
||||
#return BIFFRecords.ExtSSTRecord(abs_stream_pos, self.sst_record.str_placement,
|
||||
#self.sst_record.portions_len).get()
|
||||
|
||||
def get_biff_data(self):
|
||||
before = b''
|
||||
before += self.__bof_rec()
|
||||
before += self.__intf_hdr_rec()
|
||||
before += self.__intf_mms_rec()
|
||||
before += self.__intf_end_rec()
|
||||
before += self.__write_access_rec()
|
||||
before += self.__codepage_rec()
|
||||
before += self.__dsf_rec()
|
||||
before += self.__tabid_rec()
|
||||
before += self.__fngroupcount_rec()
|
||||
before += self.__wnd_protect_rec()
|
||||
before += self.__protect_rec()
|
||||
before += self.__obj_protect_rec()
|
||||
before += self.__password_rec()
|
||||
before += self.__prot4rev_rec()
|
||||
before += self.__prot4rev_pass_rec()
|
||||
before += self.__backup_rec()
|
||||
before += self.__hide_obj_rec()
|
||||
before += self.__window1_rec()
|
||||
before += self.__datemode_rec()
|
||||
before += self.__precision_rec()
|
||||
before += self.__refresh_all_rec()
|
||||
before += self.__bookbool_rec()
|
||||
before += self.__all_fonts_num_formats_xf_styles_rec()
|
||||
before += self.__palette_rec()
|
||||
before += self.__useselfs_rec()
|
||||
|
||||
country = self.__country_rec()
|
||||
all_links = self.__all_links_rec()
|
||||
|
||||
shared_str_table = self.__sst_rec()
|
||||
after = country + all_links + shared_str_table
|
||||
|
||||
ext_sst = self.__ext_sst_rec(0) # need fake cause we need calc stream pos
|
||||
eof = self.__eof_rec()
|
||||
|
||||
self.__worksheets[self.__active_sheet].selected = True
|
||||
sheets = b''
|
||||
sheet_biff_lens = []
|
||||
for sheet in self.__worksheets:
|
||||
data = sheet.get_biff_data()
|
||||
sheets += data
|
||||
sheet_biff_lens.append(len(data))
|
||||
|
||||
bundlesheets = self.__boundsheets_rec(len(before), len(after)+len(ext_sst)+len(eof), sheet_biff_lens)
|
||||
|
||||
sst_stream_pos = len(before) + len(bundlesheets) + len(country) + len(all_links)
|
||||
ext_sst = self.__ext_sst_rec(sst_stream_pos)
|
||||
|
||||
return before + bundlesheets + after + ext_sst + eof + sheets
|
||||
|
||||
def save(self, filename):
|
||||
from . import CompoundDoc
|
||||
|
||||
doc = CompoundDoc.XlsDoc()
|
||||
doc.save(filename, self.get_biff_data())
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +0,0 @@
|
||||
import sys
|
||||
|
||||
from .Workbook import Workbook
|
||||
from .Worksheet import Worksheet
|
||||
from .Row import Row
|
||||
from .Column import Column
|
||||
from .Formatting import Font, Alignment, Borders, Pattern, Protection
|
||||
from .Style import XFStyle, easyxf
|
||||
from .ExcelFormula import *
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user