| |
1
| +<?php |
| |
2
| +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| |
3
| + |
| |
4
| +/** |
| |
5
| +* A class for reading Microsoft Excel Spreadsheets. |
| |
6
| +* |
| |
7
| +* Originally developed by Vadim Tkachenko under the name PHPExcelReader. |
| |
8
| +* (http://sourceforge.net/projects/phpexcelreader) |
| |
9
| +* Based on the Java version by Andy Khan (http://www.andykhan.com). Now |
| |
10
| +* maintained by David Sanders. Reads only Biff 7 and Biff 8 formats. |
| |
11
| +* |
| |
12
| +* PHP versions 4 and 5 |
| |
13
| +* |
| |
14
| +* LICENSE: This source file is subject to version 3.0 of the PHP license |
| |
15
| +* that is available through the world-wide-web at the following URI: |
| |
16
| +* http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| |
17
| +* the PHP License and are unable to obtain it through the web, please |
| |
18
| +* send a note to license@php.net so we can mail you a copy immediately. |
| |
19
| +* |
| |
20
| +* @category Spreadsheet |
| |
21
| +* @package Spreadsheet_Excel_Reader |
| |
22
| +* @author Vadim Tkachenko <vt@apachephp.com> |
| |
23
| +* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| |
24
| +* @version CVS: $Id: reader.php 19 2007-03-13 12:42:41Z shangxiao $ |
| |
25
| +* @link http://pear.php.net/package/Spreadsheet_Excel_Reader |
| |
26
| +* @see OLE, Spreadsheet_Excel_Writer |
| |
27
| +*/ |
| |
28
| + |
| |
29
| + |
| |
30
| +//require_once 'PEAR.php'; |
| |
31
| +//require_once 'Spreadsheet/Excel/Reader/OLERead.php'; |
| |
32
| +require_once 'oleread.inc'; |
| |
33
| +//require_once 'OLE.php'; |
| |
34
| + |
| |
35
| +define('SPREADSHEET_EXCEL_READER_BIFF8', 0x600); |
| |
36
| +define('SPREADSHEET_EXCEL_READER_BIFF7', 0x500); |
| |
37
| +define('SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS', 0x5); |
| |
38
| +define('SPREADSHEET_EXCEL_READER_WORKSHEET', 0x10); |
| |
39
| + |
| |
40
| +define('SPREADSHEET_EXCEL_READER_TYPE_BOF', 0x809); |
| |
41
| +define('SPREADSHEET_EXCEL_READER_TYPE_EOF', 0x0a); |
| |
42
| +define('SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET', 0x85); |
| |
43
| +define('SPREADSHEET_EXCEL_READER_TYPE_DIMENSION', 0x200); |
| |
44
| +define('SPREADSHEET_EXCEL_READER_TYPE_ROW', 0x208); |
| |
45
| +define('SPREADSHEET_EXCEL_READER_TYPE_DBCELL', 0xd7); |
| |
46
| +define('SPREADSHEET_EXCEL_READER_TYPE_FILEPASS', 0x2f); |
| |
47
| +define('SPREADSHEET_EXCEL_READER_TYPE_NOTE', 0x1c); |
| |
48
| +define('SPREADSHEET_EXCEL_READER_TYPE_TXO', 0x1b6); |
| |
49
| +define('SPREADSHEET_EXCEL_READER_TYPE_RK', 0x7e); |
| |
50
| +define('SPREADSHEET_EXCEL_READER_TYPE_RK2', 0x27e); |
| |
51
| +define('SPREADSHEET_EXCEL_READER_TYPE_MULRK', 0xbd); |
| |
52
| +define('SPREADSHEET_EXCEL_READER_TYPE_MULBLANK', 0xbe); |
| |
53
| +define('SPREADSHEET_EXCEL_READER_TYPE_INDEX', 0x20b); |
| |
54
| +define('SPREADSHEET_EXCEL_READER_TYPE_SST', 0xfc); |
| |
55
| +define('SPREADSHEET_EXCEL_READER_TYPE_EXTSST', 0xff); |
| |
56
| +define('SPREADSHEET_EXCEL_READER_TYPE_CONTINUE', 0x3c); |
| |
57
| +define('SPREADSHEET_EXCEL_READER_TYPE_LABEL', 0x204); |
| |
58
| +define('SPREADSHEET_EXCEL_READER_TYPE_LABELSST', 0xfd); |
| |
59
| +define('SPREADSHEET_EXCEL_READER_TYPE_NUMBER', 0x203); |
| |
60
| +define('SPREADSHEET_EXCEL_READER_TYPE_NAME', 0x18); |
| |
61
| +define('SPREADSHEET_EXCEL_READER_TYPE_ARRAY', 0x221); |
| |
62
| +define('SPREADSHEET_EXCEL_READER_TYPE_STRING', 0x207); |
| |
63
| +define('SPREADSHEET_EXCEL_READER_TYPE_FORMULA', 0x406); |
| |
64
| +define('SPREADSHEET_EXCEL_READER_TYPE_FORMULA2', 0x6); |
| |
65
| +define('SPREADSHEET_EXCEL_READER_TYPE_FORMAT', 0x41e); |
| |
66
| +define('SPREADSHEET_EXCEL_READER_TYPE_XF', 0xe0); |
| |
67
| +define('SPREADSHEET_EXCEL_READER_TYPE_BOOLERR', 0x205); |
| |
68
| +define('SPREADSHEET_EXCEL_READER_TYPE_UNKNOWN', 0xffff); |
| |
69
| +define('SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR', 0x22); |
| |
70
| +define('SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS', 0xE5); |
| |
71
| + |
| |
72
| +define('SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS' , 25569); |
| |
73
| +define('SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904', 24107); |
| |
74
| +define('SPREADSHEET_EXCEL_READER_MSINADAY', 86400); |
| |
75
| +//define('SPREADSHEET_EXCEL_READER_MSINADAY', 24 * 60 * 60); |
| |
76
| + |
| |
77
| +//define('SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT', "%.2f"); |
| |
78
| +define('SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT', "%s"); |
| |
79
| + |
| |
80
| + |
| |
81
| +/* |
| |
82
| +* Place includes, constant defines and $_GLOBAL settings here. |
| |
83
| +* Make sure they have appropriate docblocks to avoid phpDocumentor |
| |
84
| +* construing they are documented by the page-level docblock. |
| |
85
| +*/ |
| |
86
| + |
| |
87
| +/** |
| |
88
| +* A class for reading Microsoft Excel Spreadsheets. |
| |
89
| +* |
| |
90
| +* Originally developed by Vadim Tkachenko under the name PHPExcelReader. |
| |
91
| +* (http://sourceforge.net/projects/phpexcelreader) |
| |
92
| +* Based on the Java version by Andy Khan (http://www.andykhan.com). Now |
| |
93
| +* maintained by David Sanders. Reads only Biff 7 and Biff 8 formats. |
| |
94
| +* |
| |
95
| +* @category Spreadsheet |
| |
96
| +* @package Spreadsheet_Excel_Reader |
| |
97
| +* @author Vadim Tkachenko <vt@phpapache.com> |
| |
98
| +* @copyright 1997-2005 The PHP Group |
| |
99
| +* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| |
100
| +* @version Release: @package_version@ |
| |
101
| +* @link http://pear.php.net/package/PackageName |
| |
102
| +* @see OLE, Spreadsheet_Excel_Writer |
| |
103
| +*/ |
| |
104
| +class Spreadsheet_Excel_Reader |
| |
105
| +{ |
| |
106
| + /** |
| |
107
| + * Array of worksheets found |
| |
108
| + * |
| |
109
| + * @var array |
| |
110
| + * @access public |
| |
111
| + */ |
| |
112
| + var $boundsheets = array(); |
| |
113
| + |
| |
114
| + /** |
| |
115
| + * Array of format records found |
| |
116
| + * |
| |
117
| + * @var array |
| |
118
| + * @access public |
| |
119
| + */ |
| |
120
| + var $formatRecords = array(); |
| |
121
| + |
| |
122
| + /** |
| |
123
| + * todo |
| |
124
| + * |
| |
125
| + * @var array |
| |
126
| + * @access public |
| |
127
| + */ |
| |
128
| + var $sst = array(); |
| |
129
| + |
| |
130
| + /** |
| |
131
| + * Array of worksheets |
| |
132
| + * |
| |
133
| + * The data is stored in 'cells' and the meta-data is stored in an array |
| |
134
| + * called 'cellsInfo' |
| |
135
| + * |
| |
136
| + * Example: |
| |
137
| + * |
| |
138
| + * $sheets --> 'cells' --> row --> column --> Interpreted value |
| |
139
| + * --> 'cellsInfo' --> row --> column --> 'type' - Can be 'date', 'number', or 'unknown' |
| |
140
| + * --> 'raw' - The raw data that Excel stores for that data cell |
| |
141
| + * |
| |
142
| + * @var array |
| |
143
| + * @access public |
| |
144
| + */ |
| |
145
| + var $sheets = array(); |
| |
146
| + |
| |
147
| + /** |
| |
148
| + * The data returned by OLE |
| |
149
| + * |
| |
150
| + * @var string |
| |
151
| + * @access public |
| |
152
| + */ |
| |
153
| + var $data; |
| |
154
| + |
| |
155
| + /** |
| |
156
| + * OLE object for reading the file |
| |
157
| + * |
| |
158
| + * @var OLE object |
| |
159
| + * @access private |
| |
160
| + */ |
| |
161
| + var $_ole; |
| |
162
| + |
| |
163
| + /** |
| |
164
| + * Default encoding |
| |
165
| + * |
| |
166
| + * @var string |
| |
167
| + * @access private |
| |
168
| + */ |
| |
169
| + var $_defaultEncoding; |
| |
170
| + |
| |
171
| + /** |
| |
172
| + * Default number format |
| |
173
| + * |
| |
174
| + * @var integer |
| |
175
| + * @access private |
| |
176
| + */ |
| |
177
| + var $_defaultFormat = SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT; |
| |
178
| + |
| |
179
| + /** |
| |
180
| + * todo |
| |
181
| + * List of formats to use for each column |
| |
182
| + * |
| |
183
| + * @var array |
| |
184
| + * @access private |
| |
185
| + */ |
| |
186
| + var $_columnsFormat = array(); |
| |
187
| + |
| |
188
| + /** |
| |
189
| + * todo |
| |
190
| + * |
| |
191
| + * @var integer |
| |
192
| + * @access private |
| |
193
| + */ |
| |
194
| + var $_rowoffset = 1; |
| |
195
| + |
| |
196
| + /** |
| |
197
| + * todo |
| |
198
| + * |
| |
199
| + * @var integer |
| |
200
| + * @access private |
| |
201
| + */ |
| |
202
| + var $_coloffset = 1; |
| |
203
| + |
| |
204
| + /** |
| |
205
| + * List of default date formats used by Excel |
| |
206
| + * |
| |
207
| + * @var array |
| |
208
| + * @access public |
| |
209
| + */ |
| |
210
| + var $dateFormats = array ( |
| |
211
| + 0xe => "d/m/Y", |
| |
212
| + 0xf => "d-M-Y", |
| |
213
| + 0x10 => "d-M", |
| |
214
| + 0x11 => "M-Y", |
| |
215
| + 0x12 => "h:i a", |
| |
216
| + 0x13 => "h:i:s a", |
| |
217
| + 0x14 => "H:i", |
| |
218
| + 0x15 => "H:i:s", |
| |
219
| + 0x16 => "d/m/Y H:i", |
| |
220
| + 0x2d => "i:s", |
| |
221
| + 0x2e => "H:i:s", |
| |
222
| + 0x2f => "i:s.S"); |
| |
223
| + |
| |
224
| + /** |
| |
225
| + * Default number formats used by Excel |
| |
226
| + * |
| |
227
| + * @var array |
| |
228
| + * @access public |
| |
229
| + */ |
| |
230
| + var $numberFormats = array( |
| |
231
| + 0x1 => "%1.0f", // "0" |
| |
232
| + 0x2 => "%1.2f", // "0.00", |
| |
233
| + 0x3 => "%1.0f", //"#,##0", |
| |
234
| + 0x4 => "%1.2f", //"#,##0.00", |
| |
235
| + 0x5 => "%1.0f", /*"$#,##0;($#,##0)",*/ |
| |
236
| + 0x6 => '$%1.0f', /*"$#,##0;($#,##0)",*/ |
| |
237
| + 0x7 => '$%1.2f', //"$#,##0.00;($#,##0.00)", |
| |
238
| + 0x8 => '$%1.2f', //"$#,##0.00;($#,##0.00)", |
| |
239
| + 0x9 => '%1.0f%%', // "0%" |
| |
240
| + 0xa => '%1.2f%%', // "0.00%" |
| |
241
| + 0xb => '%1.2f', // 0.00E00", |
| |
242
| + 0x25 => '%1.0f', // "#,##0;(#,##0)", |
| |
243
| + 0x26 => '%1.0f', //"#,##0;(#,##0)", |
| |
244
| + 0x27 => '%1.2f', //"#,##0.00;(#,##0.00)", |
| |
245
| + 0x28 => '%1.2f', //"#,##0.00;(#,##0.00)", |
| |
246
| + 0x29 => '%1.0f', //"#,##0;(#,##0)", |
| |
247
| + 0x2a => '$%1.0f', //"$#,##0;($#,##0)", |
| |
248
| + 0x2b => '%1.2f', //"#,##0.00;(#,##0.00)", |
| |
249
| + 0x2c => '$%1.2f', //"$#,##0.00;($#,##0.00)", |
| |
250
| + 0x30 => '%1.0f'); //"##0.0E0"; |
| |
251
| + |
| |
252
| + // }}} |
| |
253
| + // {{{ Spreadsheet_Excel_Reader() |
| |
254
| + |
| |
255
| + /** |
| |
256
| + * Constructor |
| |
257
| + * |
| |
258
| + * Some basic initialisation |
| |
259
| + */ |
| |
260
| + function Spreadsheet_Excel_Reader() |
| |
261
| + { |
| |
262
| + $this->_ole =& new OLERead(); |
| |
263
| + $this->setUTFEncoder('iconv'); |
| |
264
| + } |
| |
265
| + |
| |
266
| + // }}} |
| |
267
| + // {{{ setOutputEncoding() |
| |
268
| + |
| |
269
| + /** |
| |
270
| + * Set the encoding method |
| |
271
| + * |
| |
272
| + * @param string Encoding to use |
| |
273
| + * @access public |
| |
274
| + */ |
| |
275
| + function setOutputEncoding($encoding) |
| |
276
| + { |
| |
277
| + $this->_defaultEncoding = $encoding; |
| |
278
| + } |
| |
279
| + |
| |
280
| + // }}} |
| |
281
| + // {{{ setUTFEncoder() |
| |
282
| + |
| |
283
| + /** |
| |
284
| + * $encoder = 'iconv' or 'mb' |
| |
285
| + * set iconv if you would like use 'iconv' for encode UTF-16LE to your encoding |
| |
286
| + * set mb if you would like use 'mb_convert_encoding' for encode UTF-16LE to your encoding |
| |
287
| + * |
| |
288
| + * @access public |
| |
289
| + * @param string Encoding type to use. Either 'iconv' or 'mb' |
| |
290
| + */ |
| |
291
| + function setUTFEncoder($encoder = 'iconv') |
| |
292
| + { |
| |
293
| + $this->_encoderFunction = ''; |
| |
294
| + |
| |
295
| + if ($encoder == 'iconv') { |
| |
296
| + $this->_encoderFunction = function_exists('iconv') ? 'iconv' : ''; |
| |
297
| + } elseif ($encoder == 'mb') { |
| |
298
| + $this->_encoderFunction = function_exists('mb_convert_encoding') ? |
| |
299
| + 'mb_convert_encoding' : |
| |
300
| + ''; |
| |
301
| + } |
| |
302
| + } |
| |
303
| + |
| |
304
| + // }}} |
| |
305
| + // {{{ setRowColOffset() |
| |
306
| + |
| |
307
| + /** |
| |
308
| + * todo |
| |
309
| + * |
| |
310
| + * @access public |
| |
311
| + * @param offset |
| |
312
| + */ |
| |
313
| + function setRowColOffset($iOffset) |
| |
314
| + { |
| |
315
| + $this->_rowoffset = $iOffset; |
| |
316
| + $this->_coloffset = $iOffset; |
| |
317
| + } |
| |
318
| + |
| |
319
| + // }}} |
| |
320
| + // {{{ setDefaultFormat() |
| |
321
| + |
| |
322
| + /** |
| |
323
| + * Set the default number format |
| |
324
| + * |
| |
325
| + * @access public |
| |
326
| + * @param Default format |
| |
327
| + */ |
| |
328
| + function setDefaultFormat($sFormat) |
| |
329
| + { |
| |
330
| + $this->_defaultFormat = $sFormat; |
| |
331
| + } |
| |
332
| + |
| |
333
| + // }}} |
| |
334
| + // {{{ setColumnFormat() |
| |
335
| + |
| |
336
| + /** |
| |
337
| + * Force a column to use a certain format |
| |
338
| + * |
| |
339
| + * @access public |
| |
340
| + * @param integer Column number |
| |
341
| + * @param string Format |
| |
342
| + */ |
| |
343
| + function setColumnFormat($column, $sFormat) |
| |
344
| + { |
| |
345
| + $this->_columnsFormat[$column] = $sFormat; |
| |
346
| + } |
| |
347
| + |
| |
348
| + |
| |
349
| + // }}} |
| |
350
| + // {{{ read() |
| |
351
| + |
| |
352
| + /** |
| |
353
| + * Read the spreadsheet file using OLE, then parse |
| |
354
| + * |
| |
355
| + * @access public |
| |
356
| + * @param filename |
| |
357
| + * @todo return a valid value |
| |
358
| + */ |
| |
359
| + function read($sFileName) |
| |
360
| + { |
| |
361
| + /* |
| |
362
| + require_once 'OLE.php'; |
| |
363
| + $ole = new OLE(); |
| |
364
| + $ole->read($sFileName); |
| |
365
| + |
| |
366
| + foreach ($ole->_list as $i => $pps) { |
| |
367
| + if (($pps->Name == 'Workbook' || $pps->Name == 'Book') && |
| |
368
| + $pps->Size >= SMALL_BLOCK_THRESHOLD) { |
| |
369
| + |
| |
370
| + $this->data = $ole->getData($i, 0, $ole->getDataLength($i)); |
| |
371
| + } elseif ($pps->Name == 'Root Entry') { |
| |
372
| + $this->data = $ole->getData($i, 0, $ole->getDataLength($i)); |
| |
373
| + } |
| |
374
| + //var_dump(strlen($ole->getData($i, 0, $ole->getDataLength($i))), $pps->Name, md5($this->data), $ole->getDataLength($i)); |
| |
375
| + } |
| |
376
| +//exit; |
| |
377
| + $this->_parse(); |
| |
378
| + |
| |
379
| + return sizeof($this->sheets) > 0; |
| |
380
| + */ |
| |
381
| + |
| |
382
| + $res = $this->_ole->read($sFileName); |
| |
383
| + |
| |
384
| + // oops, something goes wrong (Darko Miljanovic) |
| |
385
| + if($res === false) { |
| |
386
| + // check error code |
| |
387
| + if($this->_ole->error == 1) { |
| |
388
| + // bad file |
| |
389
| + die('The filename ' . $sFileName . ' is not readable'); |
| |
390
| + } |
| |
391
| + // check other error codes here (eg bad fileformat, etc...) |
| |
392
| + } |
| |
393
| + |
| |
394
| + $this->data = $this->_ole->getWorkBook(); |
| |
395
| + |
| |
396
| + |
| |
397
| + /* |
| |
398
| + $res = $this->_ole->read($sFileName); |
| |
399
| + |
| |
400
| + if ($this->isError($res)) { |
| |
401
| +// var_dump($res); |
| |
402
| + return $this->raiseError($res); |
| |
403
| + } |
| |
404
| + |
| |
405
| + $total = $this->_ole->ppsTotal(); |
| |
406
| + for ($i = 0; $i < $total; $i++) { |
| |
407
| + if ($this->_ole->isFile($i)) { |
| |
408
| + $type = unpack("v", $this->_ole->getData($i, 0, 2)); |
| |
409
| + if ($type[''] == 0x0809) { // check if it's a BIFF stream |
| |
410
| + $this->_index = $i; |
| |
411
| + $this->data = $this->_ole->getData($i, 0, $this->_ole->getDataLength($i)); |
| |
412
| + break; |
| |
413
| + } |
| |
414
| + } |
| |
415
| + } |
| |
416
| + |
| |
417
| + if ($this->_index === null) { |
| |
418
| + return $this->raiseError("$file doesn't seem to be an Excel file"); |
| |
419
| + } |
| |
420
| + |
| |
421
| + */ |
| |
422
| + |
| |
423
| + //echo "data =".$this->data; |
| |
424
| + //$this->readRecords(); |
| |
425
| + $this->_parse(); |
| |
426
| + } |
| |
427
| + |
| |
428
| + |
| |
429
| + // }}} |
| |
430
| + // {{{ _parse() |
| |
431
| + |
| |
432
| + /** |
| |
433
| + * Parse a workbook |
| |
434
| + * |
| |
435
| + * @access private |
| |
436
| + * @return bool |
| |
437
| + */ |
| |
438
| + function _parse() |
| |
439
| + { |
| |
440
| + $pos = 0; |
| |
441
| + |
| |
442
| + $code = ord($this->data[$pos]) | ord($this->data[$pos+1])<<8; |
| |
443
| + $length = ord($this->data[$pos+2]) | ord($this->data[$pos+3])<<8; |
| |
444
| + |
| |
445
| + $version = ord($this->data[$pos + 4]) | ord($this->data[$pos + 5])<<8; |
| |
446
| + $substreamType = ord($this->data[$pos + 6]) | ord($this->data[$pos + 7])<<8; |
| |
447
| + //echo "Start parse code=".base_convert($code,10,16)." version=".base_convert($version,10,16)." substreamType=".base_convert($substreamType,10,16).""."\n"; |
| |
448
| + |
| |
449
| + if (($version != SPREADSHEET_EXCEL_READER_BIFF8) && |
| |
450
| + ($version != SPREADSHEET_EXCEL_READER_BIFF7)) { |
| |
451
| + return false; |
| |
452
| + } |
| |
453
| + |
| |
454
| + if ($substreamType != SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS){ |
| |
455
| + return false; |
| |
456
| + } |
| |
457
| + |
| |
458
| + //print_r($rec); |
| |
459
| + $pos += $length + 4; |
| |
460
| + |
| |
461
| + $code = ord($this->data[$pos]) | ord($this->data[$pos+1])<<8; |
| |
462
| + $length = ord($this->data[$pos+2]) | ord($this->data[$pos+3])<<8; |
| |
463
| + |
| |
464
| + while ($code != SPREADSHEET_EXCEL_READER_TYPE_EOF) { |
| |
465
| + switch ($code) { |
| |
466
| + case SPREADSHEET_EXCEL_READER_TYPE_SST: |
| |
467
| + //echo "Type_SST\n"; |
| |
468
| + $spos = $pos + 4; |
| |
469
| + $limitpos = $spos + $length; |
| |
470
| + $uniqueStrings = $this->_GetInt4d($this->data, $spos+4); |
| |
471
| + $spos += 8; |
| |
472
| + for ($i = 0; $i < $uniqueStrings; $i++) { |
| |
473
| + // Read in the number of characters |
| |
474
| + if ($spos == $limitpos) { |
| |
475
| + $opcode = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| |
476
| + $conlength = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| |
477
| + if ($opcode != 0x3c) { |
| |
478
| + return -1; |
| |
479
| + } |
| |
480
| + $spos += 4; |
| |
481
| + $limitpos = $spos + $conlength; |
| |
482
| + } |
| |
483
| + $numChars = ord($this->data[$spos]) | (ord($this->data[$spos+1]) << 8); |
| |
484
| + //echo "i = $i pos = $pos numChars = $numChars "; |
| |
485
| + $spos += 2; |
| |
486
| + $optionFlags = ord($this->data[$spos]); |
| |
487
| + $spos++; |
| |
488
| + $asciiEncoding = (($optionFlags & 0x01) == 0) ; |
| |
489
| + $extendedString = ( ($optionFlags & 0x04) != 0); |
| |
490
| + |
| |
491
| + // See if string contains formatting information |
| |
492
| + $richString = ( ($optionFlags & 0x08) != 0); |
| |
493
| + |
| |
494
| + if ($richString) { |
| |
495
| + // Read in the crun |
| |
496
| + $formattingRuns = ord($this->data[$spos]) | (ord($this->data[$spos+1]) << 8); |
| |
497
| + $spos += 2; |
| |
498
| + } |
| |
499
| + |
| |
500
| + if ($extendedString) { |
| |
501
| + // Read in cchExtRst |
| |
502
| + $extendedRunLength = $this->_GetInt4d($this->data, $spos); |
| |
503
| + $spos += 4; |
| |
504
| + } |
| |
505
| + |
| |
506
| + $len = ($asciiEncoding)? $numChars : $numChars*2; |
| |
507
| + if ($spos + $len < $limitpos) { |
| |
508
| + $retstr = substr($this->data, $spos, $len); |
| |
509
| + $spos += $len; |
| |
510
| + }else{ |
| |
511
| + // found countinue |
| |
512
| + $retstr = substr($this->data, $spos, $limitpos - $spos); |
| |
513
| + $bytesRead = $limitpos - $spos; |
| |
514
| + $charsLeft = $numChars - (($asciiEncoding) ? $bytesRead : ($bytesRead / 2)); |
| |
515
| + $spos = $limitpos; |
| |
516
| + |
| |
517
| + while ($charsLeft > 0){ |
| |
518
| + $opcode = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| |
519
| + $conlength = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| |
520
| + if ($opcode != 0x3c) { |
| |
521
| + return -1; |
| |
522
| + } |
| |
523
| + $spos += 4; |
| |
524
| + $limitpos = $spos + $conlength; |
| |
525
| + $option = ord($this->data[$spos]); |
| |
526
| + $spos += 1; |
| |
527
| + if ($asciiEncoding && ($option == 0)) { |
| |
528
| + $len = min($charsLeft, $limitpos - $spos); // min($charsLeft, $conlength); |
| |
529
| + $retstr .= substr($this->data, $spos, $len); |
| |
530
| + $charsLeft -= $len; |
| |
531
| + $asciiEncoding = true; |
| |
532
| + }elseif (!$asciiEncoding && ($option != 0)){ |
| |
533
| + $len = min($charsLeft * 2, $limitpos - $spos); // min($charsLeft, $conlength); |
| |
534
| + $retstr .= substr($this->data, $spos, $len); |
| |
535
| + $charsLeft -= $len/2; |
| |
536
| + $asciiEncoding = false; |
| |
537
| + }elseif (!$asciiEncoding && ($option == 0)) { |
| |
538
| + // Bummer - the string starts off as Unicode, but after the |
| |
539
| + // continuation it is in straightforward ASCII encoding |
| |
540
| + $len = min($charsLeft, $limitpos - $spos); // min($charsLeft, $conlength); |
| |
541
| + for ($j = 0; $j < $len; $j++) { |
| |
542
| + $retstr .= $this->data[$spos + $j].chr(0); |
| |
543
| + } |
| |
544
| + $charsLeft -= $len; |
| |
545
| + $asciiEncoding = false; |
| |
546
| + }else{ |
| |
547
| + $newstr = ''; |
| |
548
| + for ($j = 0; $j < strlen($retstr); $j++) { |
| |
549
| + $newstr = $retstr[$j].chr(0); |
| |
550
| + } |
| |
551
| + $retstr = $newstr; |
| |
552
| + $len = min($charsLeft * 2, $limitpos - $spos); // min($charsLeft, $conlength); |
| |
553
| + $retstr .= substr($this->data, $spos, $len); |
| |
554
| + $charsLeft -= $len/2; |
| |
555
| + $asciiEncoding = false; |
| |
556
| + //echo "Izavrat\n"; |
| |
557
| + } |
| |
558
| + $spos += $len; |
| |
559
| + |
| |
560
| + } |
| |
561
| + } |
| |
562
| + $retstr = ($asciiEncoding) ? $retstr : $this->_encodeUTF16($retstr); |
| |
563
| +// echo "Str $i = $retstr\n"; |
| |
564
| + if ($richString){ |
| |
565
| + $spos += 4 * $formattingRuns; |
| |
566
| + } |
| |
567
| + |
| |
568
| + // For extended strings, skip over the extended string data |
| |
569
| + if ($extendedString) { |
| |
570
| + $spos += $extendedRunLength; |
| |
571
| + } |
| |
572
| + //if ($retstr == 'Derby'){ |
| |
573
| + // echo "bb\n"; |
| |
574
| + //} |
| |
575
| + $this->sst[]=$retstr; |
| |
576
| + } |
| |
577
| + /*$continueRecords = array(); |
| |
578
| + while ($this->getNextCode() == Type_CONTINUE) { |
| |
579
| + $continueRecords[] = &$this->nextRecord(); |
| |
580
| + } |
| |
581
| + //echo " 1 Type_SST\n"; |
| |
582
| + $this->shareStrings = new SSTRecord($r, $continueRecords); |
| |
583
| + //print_r($this->shareStrings->strings); |
| |
584
| + */ |
| |
585
| + // echo 'SST read: '.($time_end-$time_start)."\n"; |
| |
586
| + break; |
| |
587
| + |
| |
588
| + case SPREADSHEET_EXCEL_READER_TYPE_FILEPASS: |
| |
589
| + return false; |
| |
590
| + break; |
| |
591
| + case SPREADSHEET_EXCEL_READER_TYPE_NAME: |
| |
592
| + //echo "Type_NAME\n"; |
| |
593
| + break; |
| |
594
| + case SPREADSHEET_EXCEL_READER_TYPE_FORMAT: |
| |
595
| + $indexCode = ord($this->data[$pos+4]) | ord($this->data[$pos+5]) << 8; |
| |
596
| + |
| |
597
| + if ($version == SPREADSHEET_EXCEL_READER_BIFF8) { |
| |
598
| + $numchars = ord($this->data[$pos+6]) | ord($this->data[$pos+7]) << 8; |
| |
599
| + if (ord($this->data[$pos+8]) == 0){ |
| |
600
| + $formatString = substr($this->data, $pos+9, $numchars); |
| |
601
| + } else { |
| |
602
| + $formatString = substr($this->data, $pos+9, $numchars*2); |
| |
603
| + } |
| |
604
| + } else { |
| |
605
| + $numchars = ord($this->data[$pos+6]); |
| |
606
| + $formatString = substr($this->data, $pos+7, $numchars*2); |
| |
607
| + } |
| |
608
| + |
| |
609
| + $this->formatRecords[$indexCode] = $formatString; |
| |
610
| + // echo "Type.FORMAT\n"; |
| |
611
| + break; |
| |
612
| + case SPREADSHEET_EXCEL_READER_TYPE_XF: |
| |
613
| + //global $dateFormats, $numberFormats; |
| |
614
| + $indexCode = ord($this->data[$pos+6]) | ord($this->data[$pos+7]) << 8; |
| |
615
| + //echo "\nType.XF ".count($this->formatRecords['xfrecords'])." $indexCode "; |
| |
616
| + if (array_key_exists($indexCode, $this->dateFormats)) { |
| |
617
| + //echo "isdate ".$dateFormats[$indexCode]; |
| |
618
| + $this->formatRecords['xfrecords'][] = array( |
| |
619
| + 'type' => 'date', |
| |
620
| + 'format' => $this->dateFormats[$indexCode] |
| |
621
| + ); |
| |
622
| + }elseif (array_key_exists($indexCode, $this->numberFormats)) { |
| |
623
| + //echo "isnumber ".$this->numberFormats[$indexCode]; |
| |
624
| + $this->formatRecords['xfrecords'][] = array( |
| |
625
| + 'type' => 'number', |
| |
626
| + 'format' => $this->numberFormats[$indexCode] |
| |
627
| + ); |
| |
628
| + }else{ |
| |
629
| + $isdate = FALSE; |
| |
630
| + if ($indexCode > 0){ |
| |
631
| + if (isset($this->formatRecords[$indexCode])) |
| |
632
| + $formatstr = $this->formatRecords[$indexCode]; |
| |
633
| + //echo '.other.'; |
| |
634
| + //echo "\ndate-time=$formatstr=\n"; |
| |
635
| + if ($formatstr) |
| |
636
| + if (preg_match("/[^hmsday\/\-:\s]/i", $formatstr) == 0) { // found day and time format |
| |
637
| + $isdate = TRUE; |
| |
638
| + $formatstr = str_replace('mm', 'i', $formatstr); |
| |
639
| + $formatstr = str_replace('h', 'H', $formatstr); |
| |
640
| + //echo "\ndate-time $formatstr \n"; |
| |
641
| + } |
| |
642
| + } |
| |
643
| + |
| |
644
| + if ($isdate){ |
| |
645
| + $this->formatRecords['xfrecords'][] = array( |
| |
646
| + 'type' => 'date', |
| |
647
| + 'format' => $formatstr, |
| |
648
| + ); |
| |
649
| + }else{ |
| |
650
| + $this->formatRecords['xfrecords'][] = array( |
| |
651
| + 'type' => 'other', |
| |
652
| + 'format' => '', |
| |
653
| + 'code' => $indexCode |
| |
654
| + ); |
| |
655
| + } |
| |
656
| + } |
| |
657
| + //echo "\n"; |
| |
658
| + break; |
| |
659
| + case SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR: |
| |
660
| + //echo "Type.NINETEENFOUR\n"; |
| |
661
| + $this->nineteenFour = (ord($this->data[$pos+4]) == 1); |
| |
662
| + break; |
| |
663
| + case SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET: |
| |
664
| + //echo "Type.BOUNDSHEET\n"; |
| |
665
| + $rec_offset = $this->_GetInt4d($this->data, $pos+4); |
| |
666
| + $rec_typeFlag = ord($this->data[$pos+8]); |
| |
667
| + $rec_visibilityFlag = ord($this->data[$pos+9]); |
| |
668
| + $rec_length = ord($this->data[$pos+10]); |
| |
669
| + |
| |
670
| + if ($version == SPREADSHEET_EXCEL_READER_BIFF8){ |
| |
671
| + $chartype = ord($this->data[$pos+11]); |
| |
672
| + if ($chartype == 0){ |
| |
673
| + $rec_name = substr($this->data, $pos+12, $rec_length); |
| |
674
| + } else { |
| |
675
| + $rec_name = $this->_encodeUTF16(substr($this->data, $pos+12, $rec_length*2)); |
| |
676
| + } |
| |
677
| + }elseif ($version == SPREADSHEET_EXCEL_READER_BIFF7){ |
| |
678
| + $rec_name = substr($this->data, $pos+11, $rec_length); |
| |
679
| + } |
| |
680
| + $this->boundsheets[] = array('name'=>$rec_name, |
| |
681
| + 'offset'=>$rec_offset); |
| |
682
| + |
| |
683
| + break; |
| |
684
| + |
| |
685
| + } |
| |
686
| + |
| |
687
| + //echo "Code = ".base_convert($r['code'],10,16)."\n"; |
| |
688
| + $pos += $length + 4; |
| |
689
| + $code = ord($this->data[$pos]) | ord($this->data[$pos+1])<<8; |
| |
690
| + $length = ord($this->data[$pos+2]) | ord($this->data[$pos+3])<<8; |
| |
691
| + |
| |
692
| + //$r = &$this->nextRecord(); |
| |
693
| + //echo "1 Code = ".base_convert($r['code'],10,16)."\n"; |
| |
694
| + } |
| |
695
| + |
| |
696
| + foreach ($this->boundsheets as $key=>$val){ |
| |
697
| + $this->sn = $key; |
| |
698
| + $this->_parsesheet($val['offset']); |
| |
699
| + } |
| |
700
| + return true; |
| |
701
| + |
| |
702
| + } |
| |
703
| + |
| |
704
| + /** |
| |
705
| + * Parse a worksheet |
| |
706
| + * |
| |
707
| + * @access private |
| |
708
| + * @param todo |
| |
709
| + * @todo fix return codes |
| |
710
| + */ |
| |
711
| + function _parsesheet($spos) |
| |
712
| + { |
| |
713
| + $cont = true; |
| |
714
| + // read BOF |
| |
715
| + $code = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| |
716
| + $length = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| |
717
| + |
| |
718
| + $version = ord($this->data[$spos + 4]) | ord($this->data[$spos + 5])<<8; |
| |
719
| + $substreamType = ord($this->data[$spos + 6]) | ord($this->data[$spos + 7])<<8; |
| |
720
| + |
| |
721
| + if (($version != SPREADSHEET_EXCEL_READER_BIFF8) && ($version != SPREADSHEET_EXCEL_READER_BIFF7)) { |
| |
722
| + return -1; |
| |
723
| + } |
| |
724
| + |
| |
725
| + if ($substreamType != SPREADSHEET_EXCEL_READER_WORKSHEET){ |
| |
726
| + return -2; |
| |
727
| + } |
| |
728
| + //echo "Start parse code=".base_convert($code,10,16)." version=".base_convert($version,10,16)." substreamType=".base_convert($substreamType,10,16).""."\n"; |
| |
729
| + $spos += $length + 4; |
| |
730
| + //var_dump($this->formatRecords); |
| |
731
| + //echo "code $code $length"; |
| |
732
| + while($cont) { |
| |
733
| + //echo "mem= ".memory_get_usage()."\n"; |
| |
734
| +// $r = &$this->file->nextRecord(); |
| |
735
| + $lowcode = ord($this->data[$spos]); |
| |
736
| + if ($lowcode == SPREADSHEET_EXCEL_READER_TYPE_EOF) break; |
| |
737
| + $code = $lowcode | ord($this->data[$spos+1])<<8; |
| |
738
| + $length = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| |
739
| + $spos += 4; |
| |
740
| + $this->sheets[$this->sn]['maxrow'] = $this->_rowoffset - 1; |
| |
741
| + $this->sheets[$this->sn]['maxcol'] = $this->_coloffset - 1; |
| |
742
| + //echo "Code=".base_convert($code,10,16)." $code\n"; |
| |
743
| + unset($this->rectype); |
| |
744
| + $this->multiplier = 1; // need for format with % |
| |
745
| + switch ($code) { |
| |
746
| + case SPREADSHEET_EXCEL_READER_TYPE_DIMENSION: |
| |
747
| + //echo 'Type_DIMENSION '; |
| |
748
| + if (!isset($this->numRows)) { |
| |
749
| + if (($length == 10) || ($version == SPREADSHEET_EXCEL_READER_BIFF7)){ |
| |
750
| + $this->sheets[$this->sn]['numRows'] = ord($this->data[$spos+2]) | ord($this->data[$spos+3]) << 8; |
| |
751
| + $this->sheets[$this->sn]['numCols'] = ord($this->data[$spos+6]) | ord($this->data[$spos+7]) << 8; |
| |
752
| + } else { |
| |
753
| + $this->sheets[$this->sn]['numRows'] = ord($this->data[$spos+4]) | ord($this->data[$spos+5]) << 8; |
| |
754
| + $this->sheets[$this->sn]['numCols'] = ord($this->data[$spos+10]) | ord($this->data[$spos+11]) << 8; |
| |
755
| + } |
| |
756
| + } |
| |
757
| + //echo 'numRows '.$this->numRows.' '.$this->numCols."\n"; |
| |
758
| + break; |
| |
759
| + case SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS: |
| |
760
| + $cellRanges = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| |
761
| + for ($i = 0; $i < $cellRanges; $i++) { |
| |
762
| + $fr = ord($this->data[$spos + 8*$i + 2]) | ord($this->data[$spos + 8*$i + 3])<<8; |
| |
763
| + $lr = ord($this->data[$spos + 8*$i + 4]) | ord($this->data[$spos + 8*$i + 5])<<8; |
| |
764
| + $fc = ord($this->data[$spos + 8*$i + 6]) | ord($this->data[$spos + 8*$i + 7])<<8; |
| |
765
| + $lc = ord($this->data[$spos + 8*$i + 8]) | ord($this->data[$spos + 8*$i + 9])<<8; |
| |
766
| + //$this->sheets[$this->sn]['mergedCells'][] = array($fr + 1, $fc + 1, $lr + 1, $lc + 1); |
| |
767
| + if ($lr - $fr > 0) { |
| |
768
| + $this->sheets[$this->sn]['cellsInfo'][$fr+1][$fc+1]['rowspan'] = $lr - $fr + 1; |
| |
769
| + } |
| |
770
| + if ($lc - $fc > 0) { |
| |
771
| + $this->sheets[$this->sn]['cellsInfo'][$fr+1][$fc+1]['colspan'] = $lc - $fc + 1; |
| |
772
| + } |
| |
773
| + } |
| |
774
| + //echo "Merged Cells $cellRanges $lr $fr $lc $fc\n"; |
| |
775
| + break; |
| |
776
| + case SPREADSHEET_EXCEL_READER_TYPE_RK: |
| |
777
| + case SPREADSHEET_EXCEL_READER_TYPE_RK2: |
| |
778
| + //echo 'SPREADSHEET_EXCEL_READER_TYPE_RK'."\n"; |
| |
779
| + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| |
780
| + $column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| |
781
| + $rknum = $this->_GetInt4d($this->data, $spos + 6); |
| |
782
| + $numValue = $this->_GetIEEE754($rknum); |
| |
783
| + //echo $numValue." "; |
| |
784
| + if ($this->isDate($spos)) { |
| |
785
| + list($string, $raw) = $this->createDate($numValue); |
| |
786
| + }else{ |
| |
787
| + $raw = $numValue; |
| |
788
| + if (isset($this->_columnsFormat[$column + 1])){ |
| |
789
| + $this->curformat = $this->_columnsFormat[$column + 1]; |
| |
790
| + } |
| |
791
| + $string = sprintf($this->curformat, $numValue * $this->multiplier); |
| |
792
| + //$this->addcell(RKRecord($r)); |
| |
793
| + } |
| |
794
| + $this->addcell($row, $column, $string, $raw); |
| |
795
| + //echo "Type_RK $row $column $string $raw {$this->curformat}\n"; |
| |
796
| + break; |
| |
797
| + case SPREADSHEET_EXCEL_READER_TYPE_LABELSST: |
| |
798
| + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| |
799
| + $column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| |
800
| + $xfindex = ord($this->data[$spos+4]) | ord($this->data[$spos+5])<<8; |
| |
801
| + $index = $this->_GetInt4d($this->data, $spos + 6); |
| |
802
| + //var_dump($this->sst); |
| |
803
| + $this->addcell($row, $column, $this->sst[$index]); |
| |
804
| + //echo "LabelSST $row $column $string\n"; |
| |
805
| + break; |
| |
806
| + case SPREADSHEET_EXCEL_READER_TYPE_MULRK: |
| |
807
| + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| |
808
| + $colFirst = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| |
809
| + $colLast = ord($this->data[$spos + $length - 2]) | ord($this->data[$spos + $length - 1])<<8; |
| |
810
| + $columns = $colLast - $colFirst + 1; |
| |
811
| + $tmppos = $spos+4; |
| |
812
| + for ($i = 0; $i < $columns; $i++) { |
| |
813
| + $numValue = $this->_GetIEEE754($this->_GetInt4d($this->data, $tmppos + 2)); |
| |
814
| + if ($this->isDate($tmppos-4)) { |
| |
815
| + list($string, $raw) = $this->createDate($numValue); |
| |
816
| + }else{ |
| |
817
| + $raw = $numValue; |
| |
818
| + if (isset($this->_columnsFormat[$colFirst + $i + 1])){ |
| |
819
| + $this->curformat = $this->_columnsFormat[$colFirst + $i + 1]; |
| |
820
| + } |
| |
821
| + $string = sprintf($this->curformat, $numValue * $this->multiplier); |
| |
822
| + } |
| |
823
| + //$rec['rknumbers'][$i]['xfindex'] = ord($rec['data'][$pos]) | ord($rec['data'][$pos+1]) << 8; |
| |
824
| + $tmppos += 6; |
| |
825
| + $this->addcell($row, $colFirst + $i, $string, $raw); |
| |
826
| + //echo "MULRK $row ".($colFirst + $i)." $string\n"; |
| |
827
| + } |
| |
828
| + //MulRKRecord($r); |
| |
829
| + // Get the individual cell records from the multiple record |
| |
830
| + //$num = ; |
| |
831
| + |
| |
832
| + break; |
| |
833
| + case SPREADSHEET_EXCEL_READER_TYPE_NUMBER: |
| |
834
| + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| |
835
| + $column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| |
836
| + $tmp = unpack("ddouble", substr($this->data, $spos + 6, 8)); // It machine machine dependent |
| |
837
| + if ($this->isDate($spos)) { |
| |
838
| + list($string, $raw) = $this->createDate($tmp['double']); |
| |
839
| + // $this->addcell(DateRecord($r, 1)); |
| |
840
| + }else{ |
| |
841
| + //$raw = $tmp['']; |
| |
842
| + if (isset($this->_columnsFormat[$column + 1])){ |
| |
843
| + $this->curformat = $this->_columnsFormat[$column + 1]; |
| |
844
| + } |
| |
845
| + $raw = $this->createNumber($spos); |
| |
846
| + $string = sprintf($this->curformat, $raw * $this->multiplier); |
| |
847
| + |
| |
848
| + // $this->addcell(NumberRecord($r)); |
| |
849
| + } |
| |
850
| + $this->addcell($row, $column, $string, $raw); |
| |
851
| + //echo "Number $row $column $string\n"; |
| |
852
| + break; |
| |
853
| + case SPREADSHEET_EXCEL_READER_TYPE_FORMULA: |
| |
854
| + case SPREADSHEET_EXCEL_READER_TYPE_FORMULA2: |
| |
855
| + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| |
856
| + $column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| |
857
| + if ((ord($this->data[$spos+6])==0) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) { |
| |
858
| + //String formula. Result follows in a STRING record |
| |
859
| + //echo "FORMULA $row $column Formula with a string<br>\n"; |
| |
860
| + } elseif ((ord($this->data[$spos+6])==1) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) { |
| |
861
| + //Boolean formula. Result is in +2; 0=false,1=true |
| |
862
| + } elseif ((ord($this->data[$spos+6])==2) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) { |
| |
863
| + //Error formula. Error code is in +2; |
| |
864
| + } elseif ((ord($this->data[$spos+6])==3) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) { |
| |
865
| + //Formula result is a null string. |
| |
866
| + } else { |
| |
867
| + // result is a number, so first 14 bytes are just like a _NUMBER record |
| |
868
| + $tmp = unpack("ddouble", substr($this->data, $spos + 6, 8)); // It machine machine dependent |
| |
869
| + if ($this->isDate($spos)) { |
| |
870
| + list($string, $raw) = $this->createDate($tmp['double']); |
| |
871
| + // $this->addcell(DateRecord($r, 1)); |
| |
872
| + }else{ |
| |
873
| + //$raw = $tmp['']; |
| |
874
| + if (isset($this->_columnsFormat[$column + 1])){ |
| |
875
| + $this->curformat = $this->_columnsFormat[$column + 1]; |
| |
876
| + } |
| |
877
| + $raw = $this->createNumber($spos); |
| |
878
| + $string = sprintf($this->curformat, $raw * $this->multiplier); |
| |
879
| + |
| |
880
| + // $this->addcell(NumberRecord($r)); |
| |
881
| + } |
| |
882
| + $this->addcell($row, $column, $string, $raw); |
| |
883
| + //echo "Number $row $column $string\n"; |
| |
884
| + } |
| |
885
| + break; |
| |
886
| + case SPREADSHEET_EXCEL_READER_TYPE_BOOLERR: |
| |
887
| + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| |
888
| + $column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| |
889
| + $string = ord($this->data[$spos+6]); |
| |
890
| + $this->addcell($row, $column, $string); |
| |
891
| + //echo 'Type_BOOLERR '."\n"; |
| |
892
| + break; |
| |
893
| + case SPREADSHEET_EXCEL_READER_TYPE_ROW: |
| |
894
| + case SPREADSHEET_EXCEL_READER_TYPE_DBCELL: |
| |
895
| + case SPREADSHEET_EXCEL_READER_TYPE_MULBLANK: |
| |
896
| + break; |
| |
897
| + case SPREADSHEET_EXCEL_READER_TYPE_LABEL: |
| |
898
| + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| |
899
| + $column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| |
900
| + $this->addcell($row, $column, substr($this->data, $spos + 8, ord($this->data[$spos + 6]) | ord($this->data[$spos + 7])<<8)); |
| |
901
| + |
| |
902
| + // $this->addcell(LabelRecord($r)); |
| |
903
| + break; |
| |
904
| + |
| |
905
| + case SPREADSHEET_EXCEL_READER_TYPE_EOF: |
| |
906
| + $cont = false; |
| |
907
| + break; |
| |
908
| + default: |
| |
909
| + //echo ' unknown :'.base_convert($r['code'],10,16)."\n"; |
| |
910
| + break; |
| |
911
| + |
| |
912
| + } |
| |
913
| + $spos += $length; |
| |
914
| + } |
| |
915
| + |
| |
916
| + if (!isset($this->sheets[$this->sn]['numRows'])) |
| |
917
| + $this->sheets[$this->sn]['numRows'] = $this->sheets[$this->sn]['maxrow']; |
| |
918
| + if (!isset($this->sheets[$this->sn]['numCols'])) |
| |
919
| + $this->sheets[$this->sn]['numCols'] = $this->sheets[$this->sn]['maxcol']; |
| |
920
| + |
| |
921
| + } |
| |
922
| + |
| |
923
| + /** |
| |
924
| + * Check whether the current record read is a date |
| |
925
| + * |
| |
926
| + * @param todo |
| |
927
| + * @return boolean True if date, false otherwise |
| |
928
| + */ |
| |
929
| + function isDate($spos) |
| |
930
| + { |
| |
931
| + //$xfindex = GetInt2d(, 4); |
| |
932
| + $xfindex = ord($this->data[$spos+4]) | ord($this->data[$spos+5]) << 8; |
| |
933
| + //echo 'check is date '.$xfindex.' '.$this->formatRecords['xfrecords'][$xfindex]['type']."\n"; |
| |
934
| + //var_dump($this->formatRecords['xfrecords'][$xfindex]); |
| |
935
| + if ($this->formatRecords['xfrecords'][$xfindex]['type'] == 'date') { |
| |
936
| + $this->curformat = $this->formatRecords['xfrecords'][$xfindex]['format']; |
| |
937
| + $this->rectype = 'date'; |
| |
938
| + return true; |
| |
939
| + } else { |
| |
940
| + if ($this->formatRecords['xfrecords'][$xfindex]['type'] == 'number') { |
| |
941
| + $this->curformat = $this->formatRecords['xfrecords'][$xfindex]['format']; |
| |
942
| + $this->rectype = 'number'; |
| |
943
| + if (($xfindex == 0x9) || ($xfindex == 0xa)){ |
| |
944
| + $this->multiplier = 100; |
| |
945
| + } |
| |
946
| + }else{ |
| |
947
| + $this->curformat = $this->_defaultFormat; |
| |
948
| + $this->rectype = 'unknown'; |
| |
949
| + } |
| |
950
| + return false; |
| |
951
| + } |
| |
952
| + } |
| |
953
| + |
| |
954
| + //}}} |
| |
955
| + //{{{ createDate() |
| |
956
| + |
| |
957
| + /** |
| |
958
| + * Convert the raw Excel date into a human readable format |
| |
959
| + * |
| |
960
| + * Dates in Excel are stored as number of seconds from an epoch. On |
| |
961
| + * Windows, the epoch is 30/12/1899 and on Mac it's 01/01/1904 |
| |
962
| + * |
| |
963
| + * @access private |
| |
964
| + * @param integer The raw Excel value to convert |
| |
965
| + * @return array First element is the converted date, the second element is number a unix timestamp |
| |
966
| + */ |
| |
967
| + function createDate($numValue) |
| |
968
| + { |
| |
969
| + if ($numValue > 1) { |
| |
970
| + $utcDays = $numValue - ($this->nineteenFour ? SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904 : SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS); |
| |
971
| + $utcValue = round(($utcDays+1) * SPREADSHEET_EXCEL_READER_MSINADAY); |
| |
972
| + $string = date ($this->curformat, $utcValue); |
| |
973
| + $raw = $utcValue; |
| |
974
| + } else { |
| |
975
| + $raw = $numValue; |
| |
976
| + $hours = floor($numValue * 24); |
| |
977
| + $mins = floor($numValue * 24 * 60) - $hours * 60; |
| |
978
| + $secs = floor($numValue * SPREADSHEET_EXCEL_READER_MSINADAY) - $hours * 60 * 60 - $mins * 60; |
| |
979
| + $string = date ($this->curformat, mktime($hours, $mins, $secs)); |
| |
980
| + } |
| |
981
| + |
| |
982
| + return array($string, $raw); |
| |
983
| + } |
| |
984
| + |
| |
985
| + function createNumber($spos) |
| |
986
| + { |
| |
987
| + $rknumhigh = $this->_GetInt4d($this->data, $spos + 10); |
| |
988
| + $rknumlow = $this->_GetInt4d($this->data, $spos + 6); |
| |
989
| + //for ($i=0; $i<8; $i++) { echo ord($this->data[$i+$spos+6]) . " "; } echo "<br>"; |
| |
990
| + $sign = ($rknumhigh & 0x80000000) >> 31; |
| |
991
| + $exp = ($rknumhigh & 0x7ff00000) >> 20; |
| |
992
| + $mantissa = (0x100000 | ($rknumhigh & 0x000fffff)); |
| |
993
| + $mantissalow1 = ($rknumlow & 0x80000000) >> 31; |
| |
994
| + $mantissalow2 = ($rknumlow & 0x7fffffff); |
| |
995
| + $value = $mantissa / pow( 2 , (20- ($exp - 1023))); |
| |
996
| + if ($mantissalow1 != 0) $value += 1 / pow (2 , (21 - ($exp - 1023))); |
| |
997
| + $value += $mantissalow2 / pow (2 , (52 - ($exp - 1023))); |
| |
998
| + //echo "Sign = $sign, Exp = $exp, mantissahighx = $mantissa, mantissalow1 = $mantissalow1, mantissalow2 = $mantissalow2<br>\n"; |
| |
999
| + if ($sign) {$value = -1 * $value;} |
| |
1000
| + return $value; |
| |
1001
| + } |
| |
1002
| + |
| |
1003
| + function addcell($row, $col, $string, $raw = '') |
| |
1004
| + { |
| |
1005
| + //echo "ADD cel $row-$col $string\n"; |
| |
1006
| + $this->sheets[$this->sn]['maxrow'] = max($this->sheets[$this->sn]['maxrow'], $row + $this->_rowoffset); |
| |
1007
| + $this->sheets[$this->sn]['maxcol'] = max($this->sheets[$this->sn]['maxcol'], $col + $this->_coloffset); |
| |
1008
| + $this->sheets[$this->sn]['cells'][$row + $this->_rowoffset][$col + $this->_coloffset] = $string; |
| |
1009
| + if ($raw) |
| |
1010
| + $this->sheets[$this->sn]['cellsInfo'][$row + $this->_rowoffset][$col + $this->_coloffset]['raw'] = $raw; |
| |
1011
| + if (isset($this->rectype)) |
| |
1012
| + $this->sheets[$this->sn]['cellsInfo'][$row + $this->_rowoffset][$col + $this->_coloffset]['type'] = $this->rectype; |
| |
1013
| + |
| |
1014
| + } |
| |
1015
| + |
| |
1016
| + |
| |
1017
| + function _GetIEEE754($rknum) |
| |
1018
| + { |
| |
1019
| + if (($rknum & 0x02) != 0) { |
| |
1020
| + $value = $rknum >> 2; |
| |
1021
| + } else { |
| |
1022
| +//mmp |
| |
1023
| +// first comment out the previously existing 7 lines of code here |
| |
1024
| +// $tmp = unpack("d", pack("VV", 0, ($rknum & 0xfffffffc))); |
| |
1025
| +// //$value = $tmp['']; |
| |
1026
| +// if (array_key_exists(1, $tmp)) { |
| |
1027
| +// $value = $tmp[1]; |
| |
1028
| +// } else { |
| |
1029
| +// $value = $tmp['']; |
| |
1030
| +// } |
| |
1031
| +// I got my info on IEEE754 encoding from |
| |
1032
| +// http://research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html |
| |
1033
| +// The RK format calls for using only the most significant 30 bits of the |
| |
1034
| +// 64 bit floating point value. The other 34 bits are assumed to be 0 |
| |
1035
| +// So, we use the upper 30 bits of $rknum as follows... |
| |
1036
| + $sign = ($rknum & 0x80000000) >> 31; |
| |
1037
| + $exp = ($rknum & 0x7ff00000) >> 20; |
| |
1038
| + $mantissa = (0x100000 | ($rknum & 0x000ffffc)); |
| |
1039
| + $value = $mantissa / pow( 2 , (20- ($exp - 1023))); |
| |
1040
| + if ($sign) {$value = -1 * $value;} |
| |
1041
| +//end of changes by mmp |
| |
1042
| + |
| |
1043
| + } |
| |
1044
| + |
| |
1045
| + if (($rknum & 0x01) != 0) { |
| |
1046
| + $value /= 100; |
| |
1047
| + } |
| |
1048
| + return $value; |
| |
1049
| + } |
| |
1050
| + |
| |
1051
| + function _encodeUTF16($string) |
| |
1052
| + { |
| |
1053
| + $result = $string; |
| |
1054
| + if ($this->_defaultEncoding){ |
| |
1055
| + switch ($this->_encoderFunction){ |
| |
1056
| + case 'iconv' : $result = iconv('UTF-16LE', $this->_defaultEncoding, $string); |
| |
1057
| + break; |
| |
1058
| + case 'mb_convert_encoding' : $result = mb_convert_encoding($string, $this->_defaultEncoding, 'UTF-16LE' ); |
| |
1059
| + break; |
| |
1060
| + } |
| |
1061
| + } |
| |
1062
| + return $result; |
| |
1063
| + } |
| |
1064
| + |
| |
1065
| + function _GetInt4d($data, $pos) |
| |
1066
| + { |
| |
1067
| + $value = ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | (ord($data[$pos+3]) << 24); |
| |
1068
| + if ($value>=4294967294) |
| |
1069
| + { |
| |
1070
| + $value=-2; |
| |
1071
| + } |
| |
1072
| + return $value; |
| |
1073
| + } |
| |
1074
| + |
| |
1075
| +} |
| |
1076
| + |
| |
1077
| +/* |
| |
1078
| + * Local variables: |
| |
1079
| + * tab-width: 4 |
| |
1080
| + * c-basic-offset: 4 |
| |
1081
| + * c-hanging-comment-ender-p: nil |
| |
1082
| + * End: |
| |
1083
| + */ |
| |
1084
| + |
| |
1085
| +?> |