Cell.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. # -*- coding: windows-1252 -*-
  2. from struct import unpack, pack
  3. from . import BIFFRecords
  4. from .compat import xrange
  5. class StrCell(object):
  6. __slots__ = ["rowx", "colx", "xf_idx", "sst_idx"]
  7. def __init__(self, rowx, colx, xf_idx, sst_idx):
  8. self.rowx = rowx
  9. self.colx = colx
  10. self.xf_idx = xf_idx
  11. self.sst_idx = sst_idx
  12. def get_biff_data(self):
  13. # return BIFFRecords.LabelSSTRecord(self.rowx, self.colx, self.xf_idx, self.sst_idx).get()
  14. return pack('<5HL', 0x00FD, 10, self.rowx, self.colx, self.xf_idx, self.sst_idx)
  15. class BlankCell(object):
  16. __slots__ = ["rowx", "colx", "xf_idx"]
  17. def __init__(self, rowx, colx, xf_idx):
  18. self.rowx = rowx
  19. self.colx = colx
  20. self.xf_idx = xf_idx
  21. def get_biff_data(self):
  22. # return BIFFRecords.BlankRecord(self.rowx, self.colx, self.xf_idx).get()
  23. return pack('<5H', 0x0201, 6, self.rowx, self.colx, self.xf_idx)
  24. class MulBlankCell(object):
  25. __slots__ = ["rowx", "colx1", "colx2", "xf_idx"]
  26. def __init__(self, rowx, colx1, colx2, xf_idx):
  27. self.rowx = rowx
  28. self.colx1 = colx1
  29. self.colx2 = colx2
  30. self.xf_idx = xf_idx
  31. def get_biff_data(self):
  32. return BIFFRecords.MulBlankRecord(self.rowx,
  33. self.colx1, self.colx2, self.xf_idx).get()
  34. class NumberCell(object):
  35. __slots__ = ["rowx", "colx", "xf_idx", "number"]
  36. def __init__(self, rowx, colx, xf_idx, number):
  37. self.rowx = rowx
  38. self.colx = colx
  39. self.xf_idx = xf_idx
  40. self.number = float(number)
  41. def get_encoded_data(self):
  42. rk_encoded = 0
  43. num = self.number
  44. # The four possible kinds of RK encoding are *not* mutually exclusive.
  45. # The 30-bit integer variety picks up the most.
  46. # In the code below, the four varieties are checked in descending order
  47. # of bangs per buck, or not at all.
  48. # SJM 2007-10-01
  49. if -0x20000000 <= num < 0x20000000: # fits in 30-bit *signed* int
  50. inum = int(num)
  51. if inum == num: # survives round-trip
  52. # print "30-bit integer RK", inum, hex(inum)
  53. rk_encoded = 2 | (inum << 2)
  54. return 1, rk_encoded
  55. temp = num * 100
  56. if -0x20000000 <= temp < 0x20000000:
  57. # That was step 1: the coded value will fit in
  58. # a 30-bit signed integer.
  59. itemp = int(round(temp, 0))
  60. # That was step 2: "itemp" is the best candidate coded value.
  61. # Now for step 3: simulate the decoding,
  62. # to check for round-trip correctness.
  63. if itemp / 100.0 == num:
  64. # print "30-bit integer RK*100", itemp, hex(itemp)
  65. rk_encoded = 3 | (itemp << 2)
  66. return 1, rk_encoded
  67. if 0: # Cost of extra pack+unpack not justified by tiny yield.
  68. packed = pack('<d', num)
  69. w01, w23 = unpack('<2i', packed)
  70. if not w01 and not(w23 & 3):
  71. # 34 lsb are 0
  72. # print "float RK", w23, hex(w23)
  73. return 1, w23
  74. packed100 = pack('<d', temp)
  75. w01, w23 = unpack('<2i', packed100)
  76. if not w01 and not(w23 & 3):
  77. # 34 lsb are 0
  78. # print "float RK*100", w23, hex(w23)
  79. return 1, w23 | 1
  80. #print "Number"
  81. #print
  82. return 0, pack('<5Hd', 0x0203, 14, self.rowx, self.colx, self.xf_idx, num)
  83. def get_biff_data(self):
  84. isRK, value = self.get_encoded_data()
  85. if isRK:
  86. return pack('<5Hi', 0x27E, 10, self.rowx, self.colx, self.xf_idx, value)
  87. return value # NUMBER record already packed
  88. class BooleanCell(object):
  89. __slots__ = ["rowx", "colx", "xf_idx", "number"]
  90. def __init__(self, rowx, colx, xf_idx, number):
  91. self.rowx = rowx
  92. self.colx = colx
  93. self.xf_idx = xf_idx
  94. self.number = number
  95. def get_biff_data(self):
  96. return BIFFRecords.BoolErrRecord(self.rowx,
  97. self.colx, self.xf_idx, self.number, 0).get()
  98. error_code_map = {
  99. 0x00: 0, # Intersection of two cell ranges is empty
  100. 0x07: 7, # Division by zero
  101. 0x0F: 15, # Wrong type of operand
  102. 0x17: 23, # Illegal or deleted cell reference
  103. 0x1D: 29, # Wrong function or range name
  104. 0x24: 36, # Value range overflow
  105. 0x2A: 42, # Argument or function not available
  106. '#NULL!' : 0, # Intersection of two cell ranges is empty
  107. '#DIV/0!': 7, # Division by zero
  108. '#VALUE!': 36, # Wrong type of operand
  109. '#REF!' : 23, # Illegal or deleted cell reference
  110. '#NAME?' : 29, # Wrong function or range name
  111. '#NUM!' : 36, # Value range overflow
  112. '#N/A!' : 42, # Argument or function not available
  113. }
  114. class ErrorCell(object):
  115. __slots__ = ["rowx", "colx", "xf_idx", "number"]
  116. def __init__(self, rowx, colx, xf_idx, error_string_or_code):
  117. self.rowx = rowx
  118. self.colx = colx
  119. self.xf_idx = xf_idx
  120. try:
  121. self.number = error_code_map[error_string_or_code]
  122. except KeyError:
  123. raise Exception('Illegal error value (%r)' % error_string_or_code)
  124. def get_biff_data(self):
  125. return BIFFRecords.BoolErrRecord(self.rowx,
  126. self.colx, self.xf_idx, self.number, 1).get()
  127. class FormulaCell(object):
  128. __slots__ = ["rowx", "colx", "xf_idx", "frmla", "calc_flags"]
  129. def __init__(self, rowx, colx, xf_idx, frmla, calc_flags=0):
  130. self.rowx = rowx
  131. self.colx = colx
  132. self.xf_idx = xf_idx
  133. self.frmla = frmla
  134. self.calc_flags = calc_flags
  135. def get_biff_data(self):
  136. return BIFFRecords.FormulaRecord(self.rowx,
  137. self.colx, self.xf_idx, self.frmla.rpn(), self.calc_flags).get()
  138. # module-level function for *internal* use by the Row module
  139. def _get_cells_biff_data_mul(rowx, cell_items):
  140. # Return the BIFF data for all cell records in the row.
  141. # Adjacent BLANK|RK records are combined into MUL(BLANK|RK) records.
  142. pieces = []
  143. nitems = len(cell_items)
  144. i = 0
  145. while i < nitems:
  146. icolx, icell = cell_items[i]
  147. if isinstance(icell, NumberCell):
  148. isRK, value = icell.get_encoded_data()
  149. if not isRK:
  150. pieces.append(value) # pre-packed NUMBER record
  151. i += 1
  152. continue
  153. muldata = [(value, icell.xf_idx)]
  154. target = NumberCell
  155. elif isinstance(icell, BlankCell):
  156. muldata = [icell.xf_idx]
  157. target = BlankCell
  158. else:
  159. pieces.append(icell.get_biff_data())
  160. i += 1
  161. continue
  162. lastcolx = icolx
  163. j = i
  164. packed_record = ''
  165. for j in xrange(i+1, nitems):
  166. jcolx, jcell = cell_items[j]
  167. if jcolx != lastcolx + 1:
  168. nexti = j
  169. break
  170. if not isinstance(jcell, target):
  171. nexti = j
  172. break
  173. if target == NumberCell:
  174. isRK, value = jcell.get_encoded_data()
  175. if not isRK:
  176. packed_record = value
  177. nexti = j + 1
  178. break
  179. muldata.append((value, jcell.xf_idx))
  180. else:
  181. muldata.append(jcell.xf_idx)
  182. lastcolx = jcolx
  183. else:
  184. nexti = j + 1
  185. if target == NumberCell:
  186. if lastcolx == icolx:
  187. # RK record
  188. value, xf_idx = muldata[0]
  189. pieces.append(pack('<5Hi', 0x027E, 10, rowx, icolx, xf_idx, value))
  190. else:
  191. # MULRK record
  192. nc = lastcolx - icolx + 1
  193. pieces.append(pack('<4H', 0x00BD, 6 * nc + 6, rowx, icolx))
  194. pieces.append(b''.join(pack('<Hi', xf_idx, value) for value, xf_idx in muldata))
  195. pieces.append(pack('<H', lastcolx))
  196. else:
  197. if lastcolx == icolx:
  198. # BLANK record
  199. xf_idx = muldata[0]
  200. pieces.append(pack('<5H', 0x0201, 6, rowx, icolx, xf_idx))
  201. else:
  202. # MULBLANK record
  203. nc = lastcolx - icolx + 1
  204. pieces.append(pack('<4H', 0x00BE, 2 * nc + 6, rowx, icolx))
  205. pieces.append(b''.join(pack('<H', xf_idx) for xf_idx in muldata))
  206. pieces.append(pack('<H', lastcolx))
  207. if packed_record:
  208. pieces.append(packed_record)
  209. i = nexti
  210. return b''.join(pieces)