├── kirby-spreadsheet.php
├── package.json
├── readme.md
├── tags
└── spreadsheet.php
└── vendor
└── spreadsheet-reader
├── .gitignore
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── SpreadsheetReader.php
├── SpreadsheetReader_CSV.php
├── SpreadsheetReader_ODS.php
├── SpreadsheetReader_XLS.php
├── SpreadsheetReader_XLSX.php
├── composer.json
├── php-excel-reader
└── excel_reader2.php
└── test.php
/kirby-spreadsheet.php:
--------------------------------------------------------------------------------
1 | ",
5 | "license": "MIT",
6 | "version": "0.1.0",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/texnixe/kirby-spreadsheet"
10 | },
11 | "type": "kirby-plugin"
12 | }
13 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Kirby Spreadsheet
2 |
3 | Version 0.1.0
4 |
5 | Include a table inside your textarea field from an .xlsx spreadsheet or comma separated text file. This plugin provides a Kirbytag, which lets you reference a spreadsheet. The columns and rows of the spreadsheet are then inserted as a table.
6 |
7 | ## Installation
8 |
9 | ### Download
10 |
11 | [Download the files](https://github.com/texnixe/kirby-spreadsheet/archive/master.zip) and place them inside `site/plugins/kirby-spreadsheet`.
12 |
13 | ### Kirby CLI
14 | Installing via Kirby's [command line interface](https://github.com/getkirby/cli):
15 |
16 | $ kirby plugin:install texnixe/kirby-spreadsheet
17 |
18 | To update the plugin, run:
19 |
20 | $ kirby plugin:update texnixe/kirby-spreadsheet
21 |
22 | ### Git Submodule
23 | You can add the Kirby Spreadsheet plugin as a Git submodule.
24 |
25 | $ cd your/project/root
26 | $ git submodule add https://github.com/texnixe/kirby-spreadsheet.git site/plugins/kirby-spreadsheet
27 | $ git submodule update --init --recursive
28 | $ git commit -am "Add Kirby Spreadsheet"
29 |
30 | Run these commands to update the plugin:
31 |
32 | $ cd your/project/root
33 | $ git submodule foreach git checkout master
34 | $ git submodule foreach git pull
35 | $ git commit -am "Update submodules"
36 | $ git submodule update --init --recursive
37 |
38 | ### How to use
39 |
40 | Insert the tag into a textarea field.
41 |
42 | **Example**:
43 |
44 | ```
45 | (spreadsheet: pricelist.xlxs class: pricelist)
46 | ```
47 |
48 | ### Options:
49 |
50 | #### class
51 |
52 | The class you want to use for the table.
53 |
54 | ### header
55 |
56 | Set to `false` if the table does not contain a header, default is `true`
57 |
58 | ```
59 | (spreadsheet: pricelist.xlxs header: false)
60 | ```
61 |
62 | #### sheet
63 |
64 | Optional, the name of the worksheet.
65 |
66 | ```
67 | (spreadsheet: pricelist.xlsx sheet: Table1)
68 | ```
69 |
70 | ## License
71 |
72 | Kirby Spreadsheet is open-sourced software licensed under the MIT license.
73 |
74 | Copyright © 2016 Sonja Broda info@texniq.de https://www.texniq.de
75 |
--------------------------------------------------------------------------------
/tags/spreadsheet.php:
--------------------------------------------------------------------------------
1 | set('tag', 'spreadsheet', array(
3 | 'attr' => array(
4 | 'header',
5 | 'class',
6 | 'sheet'
7 | ),
8 | 'html' => function($tag) {
9 |
10 | $file = $tag->file($tag->attr('spreadsheet'));
11 | $class = $tag->attr('class');
12 | $sheet = $tag->attr('sheet');
13 | $header = $tag->attr('header');
14 | $header = (is_null($header) || $header === 'true') ? true : false;
15 |
16 | if(! $file) return '';
17 |
18 | $reader = new SpreadsheetReader(kirby()->roots()->content() . DS . $file->diruri());
19 |
20 | if($sheet) {
21 | $sheets = $reader->sheets();
22 | if($index = array_search($sheet, $sheets)){
23 | $reader->changeSheet($index);
24 | }
25 | }
26 |
27 | if($header) {
28 | $tablehead = $reader->current();
29 | }
30 |
31 | $table = new Brick('table', null, array(
32 | 'class' => $class,
33 | ));
34 |
35 | if(isset($tablehead)) {
36 |
37 | $thead = new Brick('thead');
38 | $htr = new Brick('tr');
39 |
40 | foreach($tablehead as $colname) {
41 | $htr->append('
' . $colname . ' | ');
42 | }
43 | $thead->append($htr);
44 | $table->append($thead);
45 |
46 | }
47 |
48 | $tbody = new Brick('tbody');
49 |
50 | while ($row = $reader->next()) {
51 | $btr = new Brick('tr');
52 | foreach($row as $key => $cell) {
53 | $btr->append('' . $cell . ' | ');
54 | }
55 | $tbody->append($btr);
56 | }
57 |
58 | $table->append($tbody);
59 |
60 | return $table;
61 | }
62 | ));
63 |
--------------------------------------------------------------------------------
/vendor/spreadsheet-reader/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | test
3 | materials
--------------------------------------------------------------------------------
/vendor/spreadsheet-reader/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### v.0.5.11 2015-04-30
2 |
3 | - Added a special case for cells formatted as text in XLSX. Previously leading zeros would get truncated if a text cell contained only numbers.
4 |
5 | ### v.0.5.10 2015-04-18
6 |
7 | - Implemented SeekableIterator. Thanks to [paales](https://github.com/paales) for suggestion ([Issue #54](https://github.com/nuovo/spreadsheet-reader/issues/54) and [Pull request #55](https://github.com/nuovo/spreadsheet-reader/pull/55)).
8 | - Fixed a bug in CSV and ODS reading where reading position 0 multiple times in a row would result in internal pointer being advanced and reading the next line. (E.g. reading row #0 three times would result in rows #0, #1, and #2.). This could have happened on multiple calls to `current()` while in #0 position, or calls to `seek(0)` and `current()`.
9 |
10 | ### v.0.5.9 2015-04-18
11 |
12 | - [Pull request #85](https://github.com/nuovo/spreadsheet-reader/pull/85): Fixed an index check. (Thanks to [pa-m](https://github.com/pa-m)).
13 |
14 | ### v.0.5.8 2015-01-31
15 |
16 | - [Issue #50](https://github.com/nuovo/spreadsheet-reader/issues/50): Fixed an XLSX rewind issue. (Thanks to [osuwariboy](https://github.com/osuwariboy))
17 | - [Issue #52](https://github.com/nuovo/spreadsheet-reader/issues/52), [#53](https://github.com/nuovo/spreadsheet-reader/issues/53): Apache POI compatibility for XLSX. (Thanks to [dimapashkov](https://github.com/dimapashkov))
18 | - [Issue #61](https://github.com/nuovo/spreadsheet-reader/issues/61): Autoload fix in the main class. (Thanks to [i-bash](https://github.com/i-bash))
19 | - [Issue #60](https://github.com/nuovo/spreadsheet-reader/issues/60), [#69](https://github.com/nuovo/spreadsheet-reader/issues/69), [#72](https://github.com/nuovo/spreadsheet-reader/issues/72): Fixed an issue where XLSX ChangeSheet may not work. (Thanks to [jtresponse](https://github.com/jtresponse), [osuwariboy](https://github.com/osuwariboy))
20 | - [Issue #70](https://github.com/nuovo/spreadsheet-reader/issues/70): Added a check for constructor parameter correctness.
21 |
22 |
23 | ### v.0.5.7 2013-10-29
24 |
25 | - Attempt to replicate Excel's "General" format in XLSX files that is applied to otherwise unformatted cells.
26 | Currently only decimal number values are converted to PHP's floats.
27 |
28 | ### v.0.5.6 2013-09-04
29 |
30 | - Fix for formulas being returned along with values in XLSX files. (Thanks to [marktag](https://github.com/marktag))
31 |
32 | ### v.0.5.5 2013-08-23
33 |
34 | - Fix for macro sheets appearing when parsing XLS files. (Thanks to [osuwariboy](https://github.com/osuwariboy))
35 |
36 | ### v.0.5.4 2013-08-22
37 |
38 | - Fix for a PHP warning that occurs with completely empty sheets in XLS files.
39 | - XLSM (macro-enabled XLSX) files are recognized and read, too.
40 | - composer.json file is added to the repository (thanks to [matej116](https://github.com/matej116))
41 |
42 | ### v.0.5.3 2013-08-12
43 |
44 | - Fix for repeated columns in ODS files not reading correctly (thanks to [etfb](https://github.com/etfb))
45 | - Fix for filename extension reading (Thanks to [osuwariboy](https://github.com/osuwariboy))
46 |
47 | ### v.0.5.2 2013-06-28
48 |
49 | - A fix for the case when row count wasn't read correctly from the sheet in a XLS file.
50 |
51 | ### v.0.5.1 2013-06-27
52 |
53 | - Fixed file type choice when using mime-types (previously there were problems with
54 | XLSX and ODS mime-types) (Thanks to [incratec](https://github.com/incratec))
55 |
56 | - Fixed an error in XLSX iterator where `current()` would advance the iterator forward
57 | with each call. (Thanks to [osuwariboy](https://github.com/osuwariboy))
58 |
59 | ### v.0.5.0 2013-06-17
60 |
61 | - Multiple sheet reading is now supported:
62 | - The `Sheets()` method lets you retrieve a list of all sheets present in the file.
63 | - `ChangeSheet($Index)` method changes the sheet in the reader to the one specified.
64 |
65 | - Previously temporary files that were extracted, were deleted after the SpreadsheetReader
66 | was destroyed but the empty directories remained. Now those are cleaned up as well.
67 |
68 | ### v.0.4.3 2013-06-14
69 |
70 | - Bugfix for shared string caching in XLSX files. When the shared string count was larger
71 | than the caching limit, instead of them being read from file, empty strings were returned.
72 |
73 | ### v.0.4.2 2013-06-02
74 |
75 | - XLS file reading relies on the external Spreadsheet_Excel_Reader class which, by default,
76 | reads additional information about cells like fonts, styles, etc. Now that is disabled
77 | to save some memory since the style data is unnecessary anyway.
78 | (Thanks to [ChALkeR](https://github.com/ChALkeR) for the tip.)
79 |
80 | Martins Pilsetnieks
--------------------------------------------------------------------------------
/vendor/spreadsheet-reader/LICENSE.md:
--------------------------------------------------------------------------------
1 | ### spreadsheet-reader is licensed under the MIT License
2 |
3 | Copyright (C) 2012-2015 Martins Pilsetnieks
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
22 | Spreadsheet_Excel_Reader is licensed under the PHP license as noted in the excel_reader2.php file
23 |
24 | > LICENSE: This source file is subject to version 3.0 of the PHP license
25 | > that is available through the world-wide-web at the following URI:
26 | > http://www.php.net/license/3_0.txt. If you did not receive a copy of
27 | > the PHP License and are unable to obtain it through the web, please
28 | > send a note to license@php.net so we can mail you a copy immediately.
29 |
--------------------------------------------------------------------------------
/vendor/spreadsheet-reader/README.md:
--------------------------------------------------------------------------------
1 | **spreadsheet-reader** is a PHP spreadsheet reader that differs from others in that the main goal for it was efficient
2 | data extraction that could handle large (as in really large) files. So far it may not definitely be CPU, time
3 | or I/O-efficient but at least it won't run out of memory (except maybe for XLS files).
4 |
5 | So far XLSX, ODS and text/CSV file parsing should be memory-efficient. XLS file parsing is done with php-excel-reader
6 | from http://code.google.com/p/php-excel-reader/ which, sadly, has memory issues with bigger spreadsheets, as it reads the
7 | data all at once and keeps it all in memory.
8 |
9 | ### Requirements:
10 | * PHP 5.3.0 or newer
11 | * PHP must have Zip file support (see http://php.net/manual/en/zip.installation.php)
12 |
13 | ### Usage:
14 |
15 | All data is read from the file sequentially, with each row being returned as a numeric array.
16 | This is about the easiest way to read a file:
17 |
18 |
30 |
31 | However, now also multiple sheet reading is supported for file formats where it is possible. (In case of CSV, it is handled as if
32 | it only has one sheet.)
33 |
34 | You can retrieve information about sheets contained in the file by calling the `Sheets()` method which returns an array with
35 | sheet indexes as keys and sheet names as values. Then you can change the sheet that's currently being read by passing that index
36 | to the `ChangeSheet($Index)` method.
37 |
38 | Example:
39 |
40 | Sheets();
43 |
44 | foreach ($Sheets as $Index => $Name)
45 | {
46 | echo 'Sheet #'.$Index.': '.$Name;
47 |
48 | $Reader -> ChangeSheet($Index);
49 |
50 | foreach ($Reader as $Row)
51 | {
52 | print_r($Row);
53 | }
54 | }
55 | ?>
56 |
57 | If a sheet is changed to the same that is currently open, the position in the file still reverts to the beginning, so as to conform
58 | to the same behavior as when changed to a different sheet.
59 |
60 | ### Testing
61 |
62 | From the command line:
63 |
64 | php test.php path-to-spreadsheet.xls
65 |
66 | In the browser:
67 |
68 | http://path-to-library/test.php?File=/path/to/spreadsheet.xls
69 |
70 | ### Notes about library performance
71 | * CSV and text files are read strictly sequentially so performance should be O(n);
72 | * When parsing XLS files, all of the file content is read into memory so large XLS files can lead to "out of memory" errors;
73 | * XLSX files use so called "shared strings" internally to optimize for cases where the same string is repeated multiple times.
74 | Internally XLSX is an XML text that is parsed sequentially to extract data from it, however, in some cases these shared strings are a problem -
75 | sometimes Excel may put all, or nearly all of the strings from the spreadsheet in the shared string file (which is a separate XML text), and not necessarily in the same
76 | order. Worst case scenario is when it is in reverse order - for each string we need to parse the shared string XML from the beginning, if we want to avoid keeping the data in memory.
77 | To that end, the XLSX parser has a cache for shared strings that is used if the total shared string count is not too high. In case you get out of memory errors, you can
78 | try adjusting the *SHARED_STRING_CACHE_LIMIT* constant in SpreadsheetReader_XLSX to a lower one.
79 |
80 | ### TODOs:
81 | * ODS date formats;
82 |
83 | ### Licensing
84 | All of the code in this library is licensed under the MIT license as included in the LICENSE file, however, for now the library
85 | relies on php-excel-reader library for XLS file parsing which is licensed under the PHP license.
--------------------------------------------------------------------------------
/vendor/spreadsheet-reader/SpreadsheetReader.php:
--------------------------------------------------------------------------------
1 | '',
17 | 'Enclosure' => '"'
18 | );
19 |
20 | /**
21 | * @var int Current row in the file
22 | */
23 | private $Index = 0;
24 |
25 | /**
26 | * @var SpreadsheetReader_* Handle for the reader object
27 | */
28 | private $Handle = array();
29 |
30 | /**
31 | * @var TYPE_* Type of the contained spreadsheet
32 | */
33 | private $Type = false;
34 |
35 | /**
36 | * @param string Path to file
37 | * @param string Original filename (in case of an uploaded file), used to determine file type, optional
38 | * @param string MIME type from an upload, used to determine file type, optional
39 | */
40 | public function __construct($Filepath, $OriginalFilename = false, $MimeType = false)
41 | {
42 | if (!is_readable($Filepath))
43 | {
44 | throw new Exception('SpreadsheetReader: File ('.$Filepath.') not readable');
45 | }
46 |
47 | // To avoid timezone warnings and exceptions for formatting dates retrieved from files
48 | $DefaultTZ = @date_default_timezone_get();
49 | if ($DefaultTZ)
50 | {
51 | date_default_timezone_set($DefaultTZ);
52 | }
53 |
54 | // Checking the other parameters for correctness
55 |
56 | // This should be a check for string but we're lenient
57 | if (!empty($OriginalFilename) && !is_scalar($OriginalFilename))
58 | {
59 | throw new Exception('SpreadsheetReader: Original file (2nd parameter) path is not a string or a scalar value.');
60 | }
61 | if (!empty($MimeType) && !is_scalar($MimeType))
62 | {
63 | throw new Exception('SpreadsheetReader: Mime type (3nd parameter) path is not a string or a scalar value.');
64 | }
65 |
66 | // 1. Determine type
67 | if (!$OriginalFilename)
68 | {
69 | $OriginalFilename = $Filepath;
70 | }
71 |
72 | $Extension = strtolower(pathinfo($OriginalFilename, PATHINFO_EXTENSION));
73 |
74 | switch ($MimeType)
75 | {
76 | case 'text/csv':
77 | case 'text/comma-separated-values':
78 | case 'text/plain':
79 | $this -> Type = self::TYPE_CSV;
80 | break;
81 | case 'application/vnd.ms-excel':
82 | case 'application/msexcel':
83 | case 'application/x-msexcel':
84 | case 'application/x-ms-excel':
85 | case 'application/vnd.ms-excel':
86 | case 'application/x-excel':
87 | case 'application/x-dos_ms_excel':
88 | case 'application/xls':
89 | case 'application/xlt':
90 | case 'application/x-xls':
91 | // Excel does weird stuff
92 | if (in_array($Extension, array('csv', 'tsv', 'txt')))
93 | {
94 | $this -> Type = self::TYPE_CSV;
95 | }
96 | else
97 | {
98 | $this -> Type = self::TYPE_XLS;
99 | }
100 | break;
101 | case 'application/vnd.oasis.opendocument.spreadsheet':
102 | case 'application/vnd.oasis.opendocument.spreadsheet-template':
103 | $this -> Type = self::TYPE_ODS;
104 | break;
105 | case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
106 | case 'application/vnd.openxmlformats-officedocument.spreadsheetml.template':
107 | case 'application/xlsx':
108 | case 'application/xltx':
109 | $this -> Type = self::TYPE_XLSX;
110 | break;
111 | case 'application/xml':
112 | // Excel 2004 xml format uses this
113 | break;
114 | }
115 |
116 | if (!$this -> Type)
117 | {
118 | switch ($Extension)
119 | {
120 | case 'xlsx':
121 | case 'xltx': // XLSX template
122 | case 'xlsm': // Macro-enabled XLSX
123 | case 'xltm': // Macro-enabled XLSX template
124 | $this -> Type = self::TYPE_XLSX;
125 | break;
126 | case 'xls':
127 | case 'xlt':
128 | $this -> Type = self::TYPE_XLS;
129 | break;
130 | case 'ods':
131 | case 'odt':
132 | $this -> Type = self::TYPE_ODS;
133 | break;
134 | default:
135 | $this -> Type = self::TYPE_CSV;
136 | break;
137 | }
138 | }
139 |
140 | // Pre-checking XLS files, in case they are renamed CSV or XLSX files
141 | if ($this -> Type == self::TYPE_XLS)
142 | {
143 | self::Load(self::TYPE_XLS);
144 | $this -> Handle = new SpreadsheetReader_XLS($Filepath);
145 | if ($this -> Handle -> Error)
146 | {
147 | $this -> Handle -> __destruct();
148 |
149 | if (is_resource($ZipHandle = zip_open($Filepath)))
150 | {
151 | $this -> Type = self::TYPE_XLSX;
152 | zip_close($ZipHandle);
153 | }
154 | else
155 | {
156 | $this -> Type = self::TYPE_CSV;
157 | }
158 | }
159 | }
160 |
161 | // 2. Create handle
162 | switch ($this -> Type)
163 | {
164 | case self::TYPE_XLSX:
165 | self::Load(self::TYPE_XLSX);
166 | $this -> Handle = new SpreadsheetReader_XLSX($Filepath);
167 | break;
168 | case self::TYPE_CSV:
169 | self::Load(self::TYPE_CSV);
170 | $this -> Handle = new SpreadsheetReader_CSV($Filepath, $this -> Options);
171 | break;
172 | case self::TYPE_XLS:
173 | // Everything already happens above
174 | break;
175 | case self::TYPE_ODS:
176 | self::Load(self::TYPE_ODS);
177 | $this -> Handle = new SpreadsheetReader_ODS($Filepath, $this -> Options);
178 | break;
179 | }
180 | }
181 |
182 | /**
183 | * Gets information about separate sheets in the given file
184 | *
185 | * @return array Associative array where key is sheet index and value is sheet name
186 | */
187 | public function Sheets()
188 | {
189 | return $this -> Handle -> Sheets();
190 | }
191 |
192 | /**
193 | * Changes the current sheet to another from the file.
194 | * Note that changing the sheet will rewind the file to the beginning, even if
195 | * the current sheet index is provided.
196 | *
197 | * @param int Sheet index
198 | *
199 | * @return bool True if sheet could be changed to the specified one,
200 | * false if not (for example, if incorrect index was provided.
201 | */
202 | public function ChangeSheet($Index)
203 | {
204 | return $this -> Handle -> ChangeSheet($Index);
205 | }
206 |
207 | /**
208 | * Autoloads the required class for the particular spreadsheet type
209 | *
210 | * @param TYPE_* Spreadsheet type, one of TYPE_* constants of this class
211 | */
212 | private static function Load($Type)
213 | {
214 | if (!in_array($Type, array(self::TYPE_XLSX, self::TYPE_XLS, self::TYPE_CSV, self::TYPE_ODS)))
215 | {
216 | throw new Exception('SpreadsheetReader: Invalid type ('.$Type.')');
217 | }
218 |
219 | // 2nd parameter is to prevent autoloading for the class.
220 | // If autoload works, the require line is unnecessary, if it doesn't, it ends badly.
221 | if (!class_exists('SpreadsheetReader_'.$Type, false))
222 | {
223 | require(dirname(__FILE__).DIRECTORY_SEPARATOR.'SpreadsheetReader_'.$Type.'.php');
224 | }
225 | }
226 |
227 | // !Iterator interface methods
228 |
229 | /**
230 | * Rewind the Iterator to the first element.
231 | * Similar to the reset() function for arrays in PHP
232 | */
233 | public function rewind()
234 | {
235 | $this -> Index = 0;
236 | if ($this -> Handle)
237 | {
238 | $this -> Handle -> rewind();
239 | }
240 | }
241 |
242 | /**
243 | * Return the current element.
244 | * Similar to the current() function for arrays in PHP
245 | *
246 | * @return mixed current element from the collection
247 | */
248 | public function current()
249 | {
250 | if ($this -> Handle)
251 | {
252 | return $this -> Handle -> current();
253 | }
254 | return null;
255 | }
256 |
257 | /**
258 | * Move forward to next element.
259 | * Similar to the next() function for arrays in PHP
260 | */
261 | public function next()
262 | {
263 | if ($this -> Handle)
264 | {
265 | $this -> Index++;
266 |
267 | return $this -> Handle -> next();
268 | }
269 | return null;
270 | }
271 |
272 | /**
273 | * Return the identifying key of the current element.
274 | * Similar to the key() function for arrays in PHP
275 | *
276 | * @return mixed either an integer or a string
277 | */
278 | public function key()
279 | {
280 | if ($this -> Handle)
281 | {
282 | return $this -> Handle -> key();
283 | }
284 | return null;
285 | }
286 |
287 | /**
288 | * Check if there is a current element after calls to rewind() or next().
289 | * Used to check if we've iterated to the end of the collection
290 | *
291 | * @return boolean FALSE if there's nothing more to iterate over
292 | */
293 | public function valid()
294 | {
295 | if ($this -> Handle)
296 | {
297 | return $this -> Handle -> valid();
298 | }
299 | return false;
300 | }
301 |
302 | // !Countable interface method
303 | public function count()
304 | {
305 | if ($this -> Handle)
306 | {
307 | return $this -> Handle -> count();
308 | }
309 | return 0;
310 | }
311 |
312 | /**
313 | * Method for SeekableIterator interface. Takes a posiiton and traverses the file to that position
314 | * The value can be retrieved with a `current()` call afterwards.
315 | *
316 | * @param int Position in file
317 | */
318 | public function seek($Position)
319 | {
320 | if (!$this -> Handle)
321 | {
322 | throw new OutOfBoundsException('SpreadsheetReader: No file opened');
323 | }
324 |
325 | $CurrentIndex = $this -> Handle -> key();
326 |
327 | if ($CurrentIndex != $Position)
328 | {
329 | if ($Position < $CurrentIndex || is_null($CurrentIndex) || $Position == 0)
330 | {
331 | $this -> rewind();
332 | }
333 |
334 | while ($this -> Handle -> valid() && ($Position > $this -> Handle -> key()))
335 | {
336 | $this -> Handle -> next();
337 | }
338 |
339 | if (!$this -> Handle -> valid())
340 | {
341 | throw new OutOfBoundsException('SpreadsheetError: Position '.$Position.' not found');
342 | }
343 | }
344 |
345 | return null;
346 | }
347 | }
348 | ?>
349 |
--------------------------------------------------------------------------------
/vendor/spreadsheet-reader/SpreadsheetReader_CSV.php:
--------------------------------------------------------------------------------
1 | ';',
14 | 'Enclosure' => '"'
15 | );
16 |
17 | private $Encoding = 'UTF-8';
18 | private $BOMLength = 0;
19 |
20 | /**
21 | * @var resource File handle
22 | */
23 | private $Handle = false;
24 |
25 | private $Filepath = '';
26 |
27 | private $Index = 0;
28 |
29 | private $CurrentRow = null;
30 |
31 | /**
32 | * @param string Path to file
33 | * @param array Options:
34 | * Enclosure => string CSV enclosure
35 | * Separator => string CSV separator
36 | */
37 | public function __construct($Filepath, array $Options = null)
38 | {
39 | $this -> Filepath = $Filepath;
40 |
41 | if (!is_readable($Filepath))
42 | {
43 | throw new Exception('SpreadsheetReader_CSV: File not readable ('.$Filepath.')');
44 | }
45 |
46 | // For safety's sake
47 | @ini_set('auto_detect_line_endings', true);
48 |
49 | $this -> Options = array_merge($this -> Options, $Options);
50 | $this -> Handle = fopen($Filepath, 'r');
51 |
52 | // Checking the file for byte-order mark to determine encoding
53 | $BOM16 = bin2hex(fread($this -> Handle, 2));
54 | if ($BOM16 == 'fffe')
55 | {
56 | $this -> Encoding = 'UTF-16LE';
57 | //$this -> Encoding = 'UTF-16';
58 | $this -> BOMLength = 2;
59 | }
60 | elseif ($BOM16 == 'feff')
61 | {
62 | $this -> Encoding = 'UTF-16BE';
63 | //$this -> Encoding = 'UTF-16';
64 | $this -> BOMLength = 2;
65 | }
66 |
67 | if (!$this -> BOMLength)
68 | {
69 | fseek($this -> Handle, 0);
70 | $BOM32 = bin2hex(fread($this -> Handle, 4));
71 | if ($BOM32 == '0000feff')
72 | {
73 | //$this -> Encoding = 'UTF-32BE';
74 | $this -> Encoding = 'UTF-32';
75 | $this -> BOMLength = 4;
76 | }
77 | elseif ($BOM32 == 'fffe0000')
78 | {
79 | //$this -> Encoding = 'UTF-32LE';
80 | $this -> Encoding = 'UTF-32';
81 | $this -> BOMLength = 4;
82 | }
83 | }
84 |
85 | fseek($this -> Handle, 0);
86 | $BOM8 = bin2hex(fread($this -> Handle, 3));
87 | if ($BOM8 == 'efbbbf')
88 | {
89 | $this -> Encoding = 'UTF-8';
90 | $this -> BOMLength = 3;
91 | }
92 |
93 | // Seeking the place right after BOM as the start of the real content
94 | if ($this -> BOMLength)
95 | {
96 | fseek($this -> Handle, $this -> BOMLength);
97 | }
98 |
99 | // Checking for the delimiter if it should be determined automatically
100 | if (!$this -> Options['Delimiter'])
101 | {
102 | // fgetcsv needs single-byte separators
103 | $Semicolon = ';';
104 | $Tab = "\t";
105 | $Comma = ',';
106 |
107 | // Reading the first row and checking if a specific separator character
108 | // has more columns than others (it means that most likely that is the delimiter).
109 | $SemicolonCount = count(fgetcsv($this -> Handle, null, $Semicolon));
110 | fseek($this -> Handle, $this -> BOMLength);
111 | $TabCount = count(fgetcsv($this -> Handle, null, $Tab));
112 | fseek($this -> Handle, $this -> BOMLength);
113 | $CommaCount = count(fgetcsv($this -> Handle, null, $Comma));
114 | fseek($this -> Handle, $this -> BOMLength);
115 |
116 | $Delimiter = $Semicolon;
117 | if ($TabCount > $SemicolonCount || $CommaCount > $SemicolonCount)
118 | {
119 | $Delimiter = $CommaCount > $TabCount ? $Comma : $Tab;
120 | }
121 |
122 | $this -> Options['Delimiter'] = $Delimiter;
123 | }
124 | }
125 |
126 | /**
127 | * Returns information about sheets in the file.
128 | * Because CSV doesn't have any, it's just a single entry.
129 | *
130 | * @return array Sheet data
131 | */
132 | public function Sheets()
133 | {
134 | return array(0 => basename($this -> Filepath));
135 | }
136 |
137 | /**
138 | * Changes sheet to another. Because CSV doesn't have any sheets
139 | * it just rewinds the file so the behaviour is compatible with other
140 | * sheet readers. (If an invalid index is given, it doesn't do anything.)
141 | *
142 | * @param bool Status
143 | */
144 | public function ChangeSheet($Index)
145 | {
146 | if ($Index == 0)
147 | {
148 | $this -> rewind();
149 | return true;
150 | }
151 | return false;
152 | }
153 |
154 | // !Iterator interface methods
155 | /**
156 | * Rewind the Iterator to the first element.
157 | * Similar to the reset() function for arrays in PHP
158 | */
159 | public function rewind()
160 | {
161 | fseek($this -> Handle, $this -> BOMLength);
162 | $this -> CurrentRow = null;
163 | $this -> Index = 0;
164 | }
165 |
166 | /**
167 | * Return the current element.
168 | * Similar to the current() function for arrays in PHP
169 | *
170 | * @return mixed current element from the collection
171 | */
172 | public function current()
173 | {
174 | if ($this -> Index == 0 && is_null($this -> CurrentRow))
175 | {
176 | $this -> next();
177 | $this -> Index--;
178 | }
179 | return $this -> CurrentRow;
180 | }
181 |
182 | /**
183 | * Move forward to next element.
184 | * Similar to the next() function for arrays in PHP
185 | */
186 | public function next()
187 | {
188 | $this -> CurrentRow = array();
189 |
190 | // Finding the place the next line starts for UTF-16 encoded files
191 | // Line breaks could be 0x0D 0x00 0x0A 0x00 and PHP could split lines on the
192 | // first or the second linebreak leaving unnecessary \0 characters that mess up
193 | // the output.
194 | if ($this -> Encoding == 'UTF-16LE' || $this -> Encoding == 'UTF-16BE')
195 | {
196 | while (!feof($this -> Handle))
197 | {
198 | // While bytes are insignificant whitespace, do nothing
199 | $Char = ord(fgetc($this -> Handle));
200 | if (!$Char || $Char == 10 || $Char == 13)
201 | {
202 | continue;
203 | }
204 | else
205 | {
206 | // When significant bytes are found, step back to the last place before them
207 | if ($this -> Encoding == 'UTF-16LE')
208 | {
209 | fseek($this -> Handle, ftell($this -> Handle) - 1);
210 | }
211 | else
212 | {
213 | fseek($this -> Handle, ftell($this -> Handle) - 2);
214 | }
215 | break;
216 | }
217 | }
218 | }
219 |
220 | $this -> Index++;
221 | $this -> CurrentRow = fgetcsv($this -> Handle, null, $this -> Options['Delimiter'], $this -> Options['Enclosure']);
222 |
223 | if ($this -> CurrentRow)
224 | {
225 | // Converting multi-byte unicode strings
226 | // and trimming enclosure symbols off of them because those aren't recognized
227 | // in the relevan encodings.
228 | if ($this -> Encoding != 'ASCII' && $this -> Encoding != 'UTF-8')
229 | {
230 | $Encoding = $this -> Encoding;
231 | foreach ($this -> CurrentRow as $Key => $Value)
232 | {
233 | $this -> CurrentRow[$Key] = trim(trim(
234 | mb_convert_encoding($Value, 'UTF-8', $this -> Encoding),
235 | $this -> Options['Enclosure']
236 | ));
237 | }
238 |
239 | }
240 | }
241 |
242 | return $this -> CurrentRow;
243 | }
244 |
245 | /**
246 | * Return the identifying key of the current element.
247 | * Similar to the key() function for arrays in PHP
248 | *
249 | * @return mixed either an integer or a string
250 | */
251 | public function key()
252 | {
253 | return $this -> Index;
254 | }
255 |
256 | /**
257 | * Check if there is a current element after calls to rewind() or next().
258 | * Used to check if we've iterated to the end of the collection
259 | *
260 | * @return boolean FALSE if there's nothing more to iterate over
261 | */
262 | public function valid()
263 | {
264 | return ($this -> CurrentRow || !feof($this -> Handle));
265 | }
266 |
267 | // !Countable interface method
268 | /**
269 | * Ostensibly should return the count of the contained items but this just returns the number
270 | * of rows read so far. It's not really correct but at least coherent.
271 | */
272 | public function count()
273 | {
274 | return $this -> Index + 1;
275 | }
276 | }
277 | ?>
--------------------------------------------------------------------------------
/vendor/spreadsheet-reader/SpreadsheetReader_ODS.php:
--------------------------------------------------------------------------------
1 | '',
11 | 'ReturnDateTimeObjects' => false
12 | );
13 |
14 | /**
15 | * @var string Path to temporary content file
16 | */
17 | private $ContentPath = '';
18 | /**
19 | * @var XMLReader XML reader object
20 | */
21 | private $Content = false;
22 |
23 | /**
24 | * @var array Data about separate sheets in the file
25 | */
26 | private $Sheets = false;
27 |
28 | private $CurrentRow = null;
29 |
30 | /**
31 | * @var int Number of the sheet we're currently reading
32 | */
33 | private $CurrentSheet = 0;
34 |
35 | private $Index = 0;
36 |
37 | private $TableOpen = false;
38 | private $RowOpen = false;
39 |
40 | /**
41 | * @param string Path to file
42 | * @param array Options:
43 | * TempDir => string Temporary directory path
44 | * ReturnDateTimeObjects => bool True => dates and times will be returned as PHP DateTime objects, false => as strings
45 | */
46 | public function __construct($Filepath, array $Options = null)
47 | {
48 | if (!is_readable($Filepath))
49 | {
50 | throw new Exception('SpreadsheetReader_ODS: File not readable ('.$Filepath.')');
51 | }
52 |
53 | $this -> TempDir = isset($Options['TempDir']) && is_writable($Options['TempDir']) ?
54 | $Options['TempDir'] :
55 | sys_get_temp_dir();
56 |
57 | $this -> TempDir = rtrim($this -> TempDir, DIRECTORY_SEPARATOR);
58 | $this -> TempDir = $this -> TempDir.DIRECTORY_SEPARATOR.uniqid().DIRECTORY_SEPARATOR;
59 |
60 | $Zip = new ZipArchive;
61 | $Status = $Zip -> open($Filepath);
62 |
63 | if ($Status !== true)
64 | {
65 | throw new Exception('SpreadsheetReader_ODS: File not readable ('.$Filepath.') (Error '.$Status.')');
66 | }
67 |
68 | if ($Zip -> locateName('content.xml') !== false)
69 | {
70 | $Zip -> extractTo($this -> TempDir, 'content.xml');
71 | $this -> ContentPath = $this -> TempDir.'content.xml';
72 | }
73 |
74 | $Zip -> close();
75 |
76 | if ($this -> ContentPath && is_readable($this -> ContentPath))
77 | {
78 | $this -> Content = new XMLReader;
79 | $this -> Content -> open($this -> ContentPath);
80 | $this -> Valid = true;
81 | }
82 | }
83 |
84 | /**
85 | * Destructor, destroys all that remains (closes and deletes temp files)
86 | */
87 | public function __destruct()
88 | {
89 | if ($this -> Content && $this -> Content instanceof XMLReader)
90 | {
91 | $this -> Content -> close();
92 | unset($this -> Content);
93 | }
94 | if (file_exists($this -> ContentPath))
95 | {
96 | @unlink($this -> ContentPath);
97 | unset($this -> ContentPath);
98 | }
99 | }
100 |
101 | /**
102 | * Retrieves an array with information about sheets in the current file
103 | *
104 | * @return array List of sheets (key is sheet index, value is name)
105 | */
106 | public function Sheets()
107 | {
108 | if ($this -> Sheets === false)
109 | {
110 | $this -> Sheets = array();
111 |
112 | if ($this -> Valid)
113 | {
114 | $this -> SheetReader = new XMLReader;
115 | $this -> SheetReader -> open($this -> ContentPath);
116 |
117 | while ($this -> SheetReader -> read())
118 | {
119 | if ($this -> SheetReader -> name == 'table:table')
120 | {
121 | $this -> Sheets[] = $this -> SheetReader -> getAttribute('table:name');
122 | $this -> SheetReader -> next();
123 | }
124 | }
125 |
126 | $this -> SheetReader -> close();
127 | }
128 | }
129 | return $this -> Sheets;
130 | }
131 |
132 | /**
133 | * Changes the current sheet in the file to another
134 | *
135 | * @param int Sheet index
136 | *
137 | * @return bool True if sheet was successfully changed, false otherwise.
138 | */
139 | public function ChangeSheet($Index)
140 | {
141 | $Index = (int)$Index;
142 |
143 | $Sheets = $this -> Sheets();
144 | if (isset($Sheets[$Index]))
145 | {
146 | $this -> CurrentSheet = $Index;
147 | $this -> rewind();
148 |
149 | return true;
150 | }
151 |
152 | return false;
153 | }
154 |
155 | // !Iterator interface methods
156 | /**
157 | * Rewind the Iterator to the first element.
158 | * Similar to the reset() function for arrays in PHP
159 | */
160 | public function rewind()
161 | {
162 | if ($this -> Index > 0)
163 | {
164 | // If the worksheet was already iterated, XML file is reopened.
165 | // Otherwise it should be at the beginning anyway
166 | $this -> Content -> close();
167 | $this -> Content -> open($this -> ContentPath);
168 | $this -> Valid = true;
169 |
170 | $this -> TableOpen = false;
171 | $this -> RowOpen = false;
172 |
173 | $this -> CurrentRow = null;
174 | }
175 |
176 | $this -> Index = 0;
177 | }
178 |
179 | /**
180 | * Return the current element.
181 | * Similar to the current() function for arrays in PHP
182 | *
183 | * @return mixed current element from the collection
184 | */
185 | public function current()
186 | {
187 | if ($this -> Index == 0 && is_null($this -> CurrentRow))
188 | {
189 | $this -> next();
190 | $this -> Index--;
191 | }
192 | return $this -> CurrentRow;
193 | }
194 |
195 | /**
196 | * Move forward to next element.
197 | * Similar to the next() function for arrays in PHP
198 | */
199 | public function next()
200 | {
201 | $this -> Index++;
202 |
203 | $this -> CurrentRow = array();
204 |
205 | if (!$this -> TableOpen)
206 | {
207 | $TableCounter = 0;
208 | $SkipRead = false;
209 |
210 | while ($this -> Valid = ($SkipRead || $this -> Content -> read()))
211 | {
212 | if ($SkipRead)
213 | {
214 | $SkipRead = false;
215 | }
216 |
217 | if ($this -> Content -> name == 'table:table' && $this -> Content -> nodeType != XMLReader::END_ELEMENT)
218 | {
219 | if ($TableCounter == $this -> CurrentSheet)
220 | {
221 | $this -> TableOpen = true;
222 | break;
223 | }
224 |
225 | $TableCounter++;
226 | $this -> Content -> next();
227 | $SkipRead = true;
228 | }
229 | }
230 | }
231 |
232 | if ($this -> TableOpen && !$this -> RowOpen)
233 | {
234 | while ($this -> Valid = $this -> Content -> read())
235 | {
236 | switch ($this -> Content -> name)
237 | {
238 | case 'table:table':
239 | $this -> TableOpen = false;
240 | $this -> Content -> next('office:document-content');
241 | $this -> Valid = false;
242 | break 2;
243 | case 'table:table-row':
244 | if ($this -> Content -> nodeType != XMLReader::END_ELEMENT)
245 | {
246 | $this -> RowOpen = true;
247 | break 2;
248 | }
249 | break;
250 | }
251 | }
252 | }
253 |
254 | if ($this -> RowOpen)
255 | {
256 | $LastCellContent = '';
257 |
258 | while ($this -> Valid = $this -> Content -> read())
259 | {
260 | switch ($this -> Content -> name)
261 | {
262 | case 'table:table-cell':
263 | if ($this -> Content -> nodeType == XMLReader::END_ELEMENT || $this -> Content -> isEmptyElement)
264 | {
265 | if ($this -> Content -> nodeType == XMLReader::END_ELEMENT)
266 | {
267 | $CellValue = $LastCellContent;
268 | }
269 | elseif ($this -> Content -> isEmptyElement)
270 | {
271 | $LastCellContent = '';
272 | $CellValue = $LastCellContent;
273 | }
274 |
275 | $this -> CurrentRow[] = $LastCellContent;
276 |
277 | if ($this -> Content -> getAttribute('table:number-columns-repeated') !== null)
278 | {
279 | $RepeatedColumnCount = $this -> Content -> getAttribute('table:number-columns-repeated');
280 | // Checking if larger than one because the value is already added to the row once before
281 | if ($RepeatedColumnCount > 1)
282 | {
283 | $this -> CurrentRow = array_pad($this -> CurrentRow, count($this -> CurrentRow) + $RepeatedColumnCount - 1, $LastCellContent);
284 | }
285 | }
286 | }
287 | else
288 | {
289 | $LastCellContent = '';
290 | }
291 | case 'text:p':
292 | if ($this -> Content -> nodeType != XMLReader::END_ELEMENT)
293 | {
294 | $LastCellContent = $this -> Content -> readString();
295 | }
296 | break;
297 | case 'table:table-row':
298 | $this -> RowOpen = false;
299 | break 2;
300 | }
301 | }
302 | }
303 |
304 | return $this -> CurrentRow;
305 | }
306 |
307 | /**
308 | * Return the identifying key of the current element.
309 | * Similar to the key() function for arrays in PHP
310 | *
311 | * @return mixed either an integer or a string
312 | */
313 | public function key()
314 | {
315 | return $this -> Index;
316 | }
317 |
318 | /**
319 | * Check if there is a current element after calls to rewind() or next().
320 | * Used to check if we've iterated to the end of the collection
321 | *
322 | * @return boolean FALSE if there's nothing more to iterate over
323 | */
324 | public function valid()
325 | {
326 | return $this -> Valid;
327 | }
328 |
329 | // !Countable interface method
330 | /**
331 | * Ostensibly should return the count of the contained items but this just returns the number
332 | * of rows read so far. It's not really correct but at least coherent.
333 | */
334 | public function count()
335 | {
336 | return $this -> Index + 1;
337 | }
338 | }
339 | ?>
--------------------------------------------------------------------------------
/vendor/spreadsheet-reader/SpreadsheetReader_XLS.php:
--------------------------------------------------------------------------------
1 | Handle = new Spreadsheet_Excel_Reader($Filepath, false, 'UTF-8');
72 |
73 | if (function_exists('mb_convert_encoding'))
74 | {
75 | $this -> Handle -> setUTFEncoder('mb');
76 | }
77 |
78 | if (empty($this -> Handle -> sheets))
79 | {
80 | $this -> Error = true;
81 | return null;
82 | }
83 |
84 | $this -> ChangeSheet(0);
85 | }
86 |
87 | public function __destruct()
88 | {
89 | unset($this -> Handle);
90 | }
91 |
92 | /**
93 | * Retrieves an array with information about sheets in the current file
94 | *
95 | * @return array List of sheets (key is sheet index, value is name)
96 | */
97 | public function Sheets()
98 | {
99 | if ($this -> Sheets === false)
100 | {
101 | $this -> Sheets = array();
102 | $this -> SheetIndexes = array_keys($this -> Handle -> sheets);
103 |
104 | foreach ($this -> SheetIndexes as $SheetIndex)
105 | {
106 | $this -> Sheets[] = $this -> Handle -> boundsheets[$SheetIndex]['name'];
107 | }
108 | }
109 | return $this -> Sheets;
110 | }
111 |
112 | /**
113 | * Changes the current sheet in the file to another
114 | *
115 | * @param int Sheet index
116 | *
117 | * @return bool True if sheet was successfully changed, false otherwise.
118 | */
119 | public function ChangeSheet($Index)
120 | {
121 | $Index = (int)$Index;
122 | $Sheets = $this -> Sheets();
123 |
124 | if (isset($this -> Sheets[$Index]))
125 | {
126 | $this -> rewind();
127 | $this -> CurrentSheet = $this -> SheetIndexes[$Index];
128 |
129 | $this -> ColumnCount = $this -> Handle -> sheets[$this -> CurrentSheet]['numCols'];
130 | $this -> RowCount = $this -> Handle -> sheets[$this -> CurrentSheet]['numRows'];
131 |
132 | // For the case when Spreadsheet_Excel_Reader doesn't have the row count set correctly.
133 | if (!$this -> RowCount && count($this -> Handle -> sheets[$this -> CurrentSheet]['cells']))
134 | {
135 | end($this -> Handle -> sheets[$this -> CurrentSheet]['cells']);
136 | $this -> RowCount = (int)key($this -> Handle -> sheets[$this -> CurrentSheet]['cells']);
137 | }
138 |
139 | if ($this -> ColumnCount)
140 | {
141 | $this -> EmptyRow = array_fill(1, $this -> ColumnCount, '');
142 | }
143 | else
144 | {
145 | $this -> EmptyRow = array();
146 | }
147 | }
148 |
149 | return false;
150 | }
151 |
152 | public function __get($Name)
153 | {
154 | switch ($Name)
155 | {
156 | case 'Error':
157 | return $this -> Error;
158 | break;
159 | }
160 | return null;
161 | }
162 |
163 | // !Iterator interface methods
164 | /**
165 | * Rewind the Iterator to the first element.
166 | * Similar to the reset() function for arrays in PHP
167 | */
168 | public function rewind()
169 | {
170 | $this -> Index = 0;
171 | }
172 |
173 | /**
174 | * Return the current element.
175 | * Similar to the current() function for arrays in PHP
176 | *
177 | * @return mixed current element from the collection
178 | */
179 | public function current()
180 | {
181 | if ($this -> Index == 0)
182 | {
183 | $this -> next();
184 | }
185 |
186 | return $this -> CurrentRow;
187 | }
188 |
189 | /**
190 | * Move forward to next element.
191 | * Similar to the next() function for arrays in PHP
192 | */
193 | public function next()
194 | {
195 | // Internal counter is advanced here instead of the if statement
196 | // because apparently it's fully possible that an empty row will not be
197 | // present at all
198 | $this -> Index++;
199 |
200 | if ($this -> Error)
201 | {
202 | return array();
203 | }
204 | elseif (isset($this -> Handle -> sheets[$this -> CurrentSheet]['cells'][$this -> Index]))
205 | {
206 | $this -> CurrentRow = $this -> Handle -> sheets[$this -> CurrentSheet]['cells'][$this -> Index];
207 | if (!$this -> CurrentRow)
208 | {
209 | return array();
210 | }
211 |
212 | $this -> CurrentRow = $this -> CurrentRow + $this -> EmptyRow;
213 | ksort($this -> CurrentRow);
214 |
215 | $this -> CurrentRow = array_values($this -> CurrentRow);
216 | return $this -> CurrentRow;
217 | }
218 | else
219 | {
220 | $this -> CurrentRow = $this -> EmptyRow;
221 | return $this -> CurrentRow;
222 | }
223 | }
224 |
225 | /**
226 | * Return the identifying key of the current element.
227 | * Similar to the key() function for arrays in PHP
228 | *
229 | * @return mixed either an integer or a string
230 | */
231 | public function key()
232 | {
233 | return $this -> Index;
234 | }
235 |
236 | /**
237 | * Check if there is a current element after calls to rewind() or next().
238 | * Used to check if we've iterated to the end of the collection
239 | *
240 | * @return boolean FALSE if there's nothing more to iterate over
241 | */
242 | public function valid()
243 | {
244 | if ($this -> Error)
245 | {
246 | return false;
247 | }
248 | return ($this -> Index <= $this -> RowCount);
249 | }
250 |
251 | // !Countable interface method
252 | /**
253 | * Ostensibly should return the count of the contained items but this just returns the number
254 | * of rows read so far. It's not really correct but at least coherent.
255 | */
256 | public function count()
257 | {
258 | if ($this -> Error)
259 | {
260 | return 0;
261 | }
262 |
263 | return $this -> RowCount;
264 | }
265 | }
266 | ?>
--------------------------------------------------------------------------------
/vendor/spreadsheet-reader/SpreadsheetReader_XLSX.php:
--------------------------------------------------------------------------------
1 | '',
27 | 'ReturnDateTimeObjects' => false
28 | );
29 |
30 | private static $RuntimeInfo = array(
31 | 'GMPSupported' => false
32 | );
33 |
34 | private $Valid = false;
35 |
36 | /**
37 | * @var SpreadsheetReader_* Handle for the reader object
38 | */
39 | private $Handle = false;
40 |
41 | // Worksheet file
42 | /**
43 | * @var string Path to the worksheet XML file
44 | */
45 | private $WorksheetPath = false;
46 | /**
47 | * @var XMLReader XML reader object for the worksheet XML file
48 | */
49 | private $Worksheet = false;
50 |
51 | // Shared strings file
52 | /**
53 | * @var string Path to shared strings XML file
54 | */
55 | private $SharedStringsPath = false;
56 | /**
57 | * @var XMLReader XML reader object for the shared strings XML file
58 | */
59 | private $SharedStrings = false;
60 | /**
61 | * @var array Shared strings cache, if the number of shared strings is low enough
62 | */
63 | private $SharedStringCache = array();
64 |
65 | // Workbook data
66 | /**
67 | * @var SimpleXMLElement XML object for the workbook XML file
68 | */
69 | private $WorkbookXML = false;
70 |
71 | // Style data
72 | /**
73 | * @var SimpleXMLElement XML object for the styles XML file
74 | */
75 | private $StylesXML = false;
76 | /**
77 | * @var array Container for cell value style data
78 | */
79 | private $Styles = array();
80 |
81 | private $TempDir = '';
82 | private $TempFiles = array();
83 |
84 | private $CurrentRow = false;
85 |
86 | // Runtime parsing data
87 | /**
88 | * @var int Current row in the file
89 | */
90 | private $Index = 0;
91 |
92 | /**
93 | * @var array Data about separate sheets in the file
94 | */
95 | private $Sheets = false;
96 |
97 | private $SharedStringCount = 0;
98 | private $SharedStringIndex = 0;
99 | private $LastSharedStringValue = null;
100 |
101 | private $RowOpen = false;
102 |
103 | private $SSOpen = false;
104 | private $SSForwarded = false;
105 |
106 | private static $BuiltinFormats = array(
107 | 0 => '',
108 | 1 => '0',
109 | 2 => '0.00',
110 | 3 => '#,##0',
111 | 4 => '#,##0.00',
112 |
113 | 9 => '0%',
114 | 10 => '0.00%',
115 | 11 => '0.00E+00',
116 | 12 => '# ?/?',
117 | 13 => '# ??/??',
118 | 14 => 'mm-dd-yy',
119 | 15 => 'd-mmm-yy',
120 | 16 => 'd-mmm',
121 | 17 => 'mmm-yy',
122 | 18 => 'h:mm AM/PM',
123 | 19 => 'h:mm:ss AM/PM',
124 | 20 => 'h:mm',
125 | 21 => 'h:mm:ss',
126 | 22 => 'm/d/yy h:mm',
127 |
128 | 37 => '#,##0 ;(#,##0)',
129 | 38 => '#,##0 ;[Red](#,##0)',
130 | 39 => '#,##0.00;(#,##0.00)',
131 | 40 => '#,##0.00;[Red](#,##0.00)',
132 |
133 | 45 => 'mm:ss',
134 | 46 => '[h]:mm:ss',
135 | 47 => 'mmss.0',
136 | 48 => '##0.0E+0',
137 | 49 => '@',
138 |
139 | // CHT & CHS
140 | 27 => '[$-404]e/m/d',
141 | 30 => 'm/d/yy',
142 | 36 => '[$-404]e/m/d',
143 | 50 => '[$-404]e/m/d',
144 | 57 => '[$-404]e/m/d',
145 |
146 | // THA
147 | 59 => 't0',
148 | 60 => 't0.00',
149 | 61 =>'t#,##0',
150 | 62 => 't#,##0.00',
151 | 67 => 't0%',
152 | 68 => 't0.00%',
153 | 69 => 't# ?/?',
154 | 70 => 't# ??/??'
155 | );
156 | private $Formats = array();
157 |
158 | private static $DateReplacements = array(
159 | 'All' => array(
160 | '\\' => '',
161 | 'am/pm' => 'A',
162 | 'yyyy' => 'Y',
163 | 'yy' => 'y',
164 | 'mmmmm' => 'M',
165 | 'mmmm' => 'F',
166 | 'mmm' => 'M',
167 | ':mm' => ':i',
168 | 'mm' => 'm',
169 | 'm' => 'n',
170 | 'dddd' => 'l',
171 | 'ddd' => 'D',
172 | 'dd' => 'd',
173 | 'd' => 'j',
174 | 'ss' => 's',
175 | '.s' => ''
176 | ),
177 | '24H' => array(
178 | 'hh' => 'H',
179 | 'h' => 'G'
180 | ),
181 | '12H' => array(
182 | 'hh' => 'h',
183 | 'h' => 'G'
184 | )
185 | );
186 |
187 | private static $BaseDate = false;
188 | private static $DecimalSeparator = '.';
189 | private static $ThousandSeparator = '';
190 | private static $CurrencyCode = '';
191 |
192 | /**
193 | * @var array Cache for already processed format strings
194 | */
195 | private $ParsedFormatCache = array();
196 |
197 | /**
198 | * @param string Path to file
199 | * @param array Options:
200 | * TempDir => string Temporary directory path
201 | * ReturnDateTimeObjects => bool True => dates and times will be returned as PHP DateTime objects, false => as strings
202 | */
203 | public function __construct($Filepath, array $Options = null)
204 | {
205 | if (!is_readable($Filepath))
206 | {
207 | throw new Exception('SpreadsheetReader_XLSX: File not readable ('.$Filepath.')');
208 | }
209 |
210 | $this -> TempDir = isset($Options['TempDir']) && is_writable($Options['TempDir']) ?
211 | $Options['TempDir'] :
212 | sys_get_temp_dir();
213 |
214 | $this -> TempDir = rtrim($this -> TempDir, DIRECTORY_SEPARATOR);
215 | $this -> TempDir = $this -> TempDir.DIRECTORY_SEPARATOR.uniqid().DIRECTORY_SEPARATOR;
216 |
217 | $Zip = new ZipArchive;
218 | $Status = $Zip -> open($Filepath);
219 |
220 | if ($Status !== true)
221 | {
222 | throw new Exception('SpreadsheetReader_XLSX: File not readable ('.$Filepath.') (Error '.$Status.')');
223 | }
224 |
225 | // Getting the general workbook information
226 | if ($Zip -> locateName('xl/workbook.xml') !== false)
227 | {
228 | $this -> WorkbookXML = new SimpleXMLElement($Zip -> getFromName('xl/workbook.xml'));
229 | }
230 |
231 | // Extracting the XMLs from the XLSX zip file
232 | if ($Zip -> locateName('xl/sharedStrings.xml') !== false)
233 | {
234 | $this -> SharedStringsPath = $this -> TempDir.'xl'.DIRECTORY_SEPARATOR.'sharedStrings.xml';
235 | $Zip -> extractTo($this -> TempDir, 'xl/sharedStrings.xml');
236 | $this -> TempFiles[] = $this -> TempDir.'xl'.DIRECTORY_SEPARATOR.'sharedStrings.xml';
237 |
238 | if (is_readable($this -> SharedStringsPath))
239 | {
240 | $this -> SharedStrings = new XMLReader;
241 | $this -> SharedStrings -> open($this -> SharedStringsPath);
242 | $this -> PrepareSharedStringCache();
243 | }
244 | }
245 |
246 | $Sheets = $this -> Sheets();
247 |
248 | foreach ($this -> Sheets as $Index => $Name)
249 | {
250 | if ($Zip -> locateName('xl/worksheets/sheet'.$Index.'.xml') !== false)
251 | {
252 | $Zip -> extractTo($this -> TempDir, 'xl/worksheets/sheet'.$Index.'.xml');
253 | $this -> TempFiles[] = $this -> TempDir.'xl'.DIRECTORY_SEPARATOR.'worksheets'.DIRECTORY_SEPARATOR.'sheet'.$Index.'.xml';
254 | }
255 | }
256 |
257 | $this -> ChangeSheet(0);
258 |
259 | // If worksheet is present and is OK, parse the styles already
260 | if ($Zip -> locateName('xl/styles.xml') !== false)
261 | {
262 | $this -> StylesXML = new SimpleXMLElement($Zip -> getFromName('xl/styles.xml'));
263 | if ($this -> StylesXML && $this -> StylesXML -> cellXfs && $this -> StylesXML -> cellXfs -> xf)
264 | {
265 | foreach ($this -> StylesXML -> cellXfs -> xf as $Index => $XF)
266 | {
267 | // Format #0 is a special case - it is the "General" format that is applied regardless of applyNumberFormat
268 | if ($XF -> attributes() -> applyNumberFormat || (0 == (int)$XF -> attributes() -> numFmtId))
269 | {
270 | $FormatId = (int)$XF -> attributes() -> numFmtId;
271 | // If format ID >= 164, it is a custom format and should be read from styleSheet\numFmts
272 | $this -> Styles[] = $FormatId;
273 | }
274 | else
275 | {
276 | // 0 for "General" format
277 | $this -> Styles[] = 0;
278 | }
279 | }
280 | }
281 |
282 | if ($this -> StylesXML -> numFmts && $this -> StylesXML -> numFmts -> numFmt)
283 | {
284 | foreach ($this -> StylesXML -> numFmts -> numFmt as $Index => $NumFmt)
285 | {
286 | $this -> Formats[(int)$NumFmt -> attributes() -> numFmtId] = (string)$NumFmt -> attributes() -> formatCode;
287 | }
288 | }
289 |
290 | unset($this -> StylesXML);
291 | }
292 |
293 | $Zip -> close();
294 |
295 | // Setting base date
296 | if (!self::$BaseDate)
297 | {
298 | self::$BaseDate = new DateTime;
299 | self::$BaseDate -> setTimezone(new DateTimeZone('UTC'));
300 | self::$BaseDate -> setDate(1900, 1, 0);
301 | self::$BaseDate -> setTime(0, 0, 0);
302 | }
303 |
304 | // Decimal and thousand separators
305 | if (!self::$DecimalSeparator && !self::$ThousandSeparator && !self::$CurrencyCode)
306 | {
307 | $Locale = localeconv();
308 | self::$DecimalSeparator = $Locale['decimal_point'];
309 | self::$ThousandSeparator = $Locale['thousands_sep'];
310 | self::$CurrencyCode = $Locale['int_curr_symbol'];
311 | }
312 |
313 | if (function_exists('gmp_gcd'))
314 | {
315 | self::$RuntimeInfo['GMPSupported'] = true;
316 | }
317 | }
318 |
319 | /**
320 | * Destructor, destroys all that remains (closes and deletes temp files)
321 | */
322 | public function __destruct()
323 | {
324 | foreach ($this -> TempFiles as $TempFile)
325 | {
326 | @unlink($TempFile);
327 | }
328 |
329 | // Better safe than sorry - shouldn't try deleting '.' or '/', or '..'.
330 | if (strlen($this -> TempDir) > 2)
331 | {
332 | @rmdir($this -> TempDir.'xl'.DIRECTORY_SEPARATOR.'worksheets');
333 | @rmdir($this -> TempDir.'xl');
334 | @rmdir($this -> TempDir);
335 | }
336 |
337 | if ($this -> Worksheet && $this -> Worksheet instanceof XMLReader)
338 | {
339 | $this -> Worksheet -> close();
340 | unset($this -> Worksheet);
341 | }
342 | unset($this -> WorksheetPath);
343 |
344 | if ($this -> SharedStrings && $this -> SharedStrings instanceof XMLReader)
345 | {
346 | $this -> SharedStrings -> close();
347 | unset($this -> SharedStrings);
348 | }
349 | unset($this -> SharedStringsPath);
350 |
351 | if (isset($this -> StylesXML))
352 | {
353 | unset($this -> StylesXML);
354 | }
355 | if ($this -> WorkbookXML)
356 | {
357 | unset($this -> WorkbookXML);
358 | }
359 | }
360 |
361 | /**
362 | * Retrieves an array with information about sheets in the current file
363 | *
364 | * @return array List of sheets (key is sheet index, value is name)
365 | */
366 | public function Sheets()
367 | {
368 | if ($this -> Sheets === false)
369 | {
370 | $this -> Sheets = array();
371 | foreach ($this -> WorkbookXML -> sheets -> sheet as $Index => $Sheet)
372 | {
373 | $Attributes = $Sheet -> attributes('r', true);
374 | foreach ($Attributes as $Name => $Value)
375 | {
376 | if ($Name == 'id')
377 | {
378 | $SheetID = (int)str_replace('rId', '', (string)$Value);
379 | break;
380 | }
381 | }
382 |
383 | $this -> Sheets[$SheetID] = (string)$Sheet['name'];
384 | }
385 | ksort($this -> Sheets);
386 | }
387 | return array_values($this -> Sheets);
388 | }
389 |
390 | /**
391 | * Changes the current sheet in the file to another
392 | *
393 | * @param int Sheet index
394 | *
395 | * @return bool True if sheet was successfully changed, false otherwise.
396 | */
397 | public function ChangeSheet($Index)
398 | {
399 | $RealSheetIndex = false;
400 | $Sheets = $this -> Sheets();
401 | if (isset($Sheets[$Index]))
402 | {
403 | $SheetIndexes = array_keys($this -> Sheets);
404 | $RealSheetIndex = $SheetIndexes[$Index];
405 | }
406 |
407 | $TempWorksheetPath = $this -> TempDir.'xl/worksheets/sheet'.$RealSheetIndex.'.xml';
408 |
409 | if ($RealSheetIndex !== false && is_readable($TempWorksheetPath))
410 | {
411 | $this -> WorksheetPath = $TempWorksheetPath;
412 |
413 | $this -> rewind();
414 | return true;
415 | }
416 |
417 | return false;
418 | }
419 |
420 | /**
421 | * Creating shared string cache if the number of shared strings is acceptably low (or there is no limit on the amount
422 | */
423 | private function PrepareSharedStringCache()
424 | {
425 | while ($this -> SharedStrings -> read())
426 | {
427 | if ($this -> SharedStrings -> name == 'sst')
428 | {
429 | $this -> SharedStringCount = $this -> SharedStrings -> getAttribute('count');
430 | break;
431 | }
432 | }
433 |
434 | if (!$this -> SharedStringCount || (self::SHARED_STRING_CACHE_LIMIT < $this -> SharedStringCount && self::SHARED_STRING_CACHE_LIMIT !== null))
435 | {
436 | return false;
437 | }
438 |
439 | $CacheIndex = 0;
440 | $CacheValue = '';
441 | while ($this -> SharedStrings -> read())
442 | {
443 | switch ($this -> SharedStrings -> name)
444 | {
445 | case 'si':
446 | if ($this -> SharedStrings -> nodeType == XMLReader::END_ELEMENT)
447 | {
448 | $this -> SharedStringCache[$CacheIndex] = $CacheValue;
449 | $CacheIndex++;
450 | $CacheValue = '';
451 | }
452 | break;
453 | case 't':
454 | if ($this -> SharedStrings -> nodeType == XMLReader::END_ELEMENT)
455 | {
456 | continue;
457 | }
458 | $CacheValue .= $this -> SharedStrings -> readString();
459 | break;
460 | }
461 | }
462 |
463 | $this -> SharedStrings -> close();
464 | return true;
465 | }
466 |
467 | /**
468 | * Retrieves a shared string value by its index
469 | *
470 | * @param int Shared string index
471 | *
472 | * @return string Value
473 | */
474 | private function GetSharedString($Index)
475 | {
476 | if ((self::SHARED_STRING_CACHE_LIMIT === null || self::SHARED_STRING_CACHE_LIMIT > 0) && !empty($this -> SharedStringCache))
477 | {
478 | if (isset($this -> SharedStringCache[$Index]))
479 | {
480 | return $this -> SharedStringCache[$Index];
481 | }
482 | else
483 | {
484 | return '';
485 | }
486 | }
487 |
488 | // If the desired index is before the current, rewind the XML
489 | if ($this -> SharedStringIndex > $Index)
490 | {
491 | $this -> SSOpen = false;
492 | $this -> SharedStrings -> close();
493 | $this -> SharedStrings -> open($this -> SharedStringsPath);
494 | $this -> SharedStringIndex = 0;
495 | $this -> LastSharedStringValue = null;
496 | $this -> SSForwarded = false;
497 | }
498 |
499 | // Finding the unique string count (if not already read)
500 | if ($this -> SharedStringIndex == 0 && !$this -> SharedStringCount)
501 | {
502 | while ($this -> SharedStrings -> read())
503 | {
504 | if ($this -> SharedStrings -> name == 'sst')
505 | {
506 | $this -> SharedStringCount = $this -> SharedStrings -> getAttribute('uniqueCount');
507 | break;
508 | }
509 | }
510 | }
511 |
512 | // If index of the desired string is larger than possible, don't even bother.
513 | if ($this -> SharedStringCount && ($Index >= $this -> SharedStringCount))
514 | {
515 | return '';
516 | }
517 |
518 | // If an index with the same value as the last already fetched is requested
519 | // (any further traversing the tree would get us further away from the node)
520 | if (($Index == $this -> SharedStringIndex) && ($this -> LastSharedStringValue !== null))
521 | {
522 | return $this -> LastSharedStringValue;
523 | }
524 |
525 | // Find the correct node with the desired index
526 | while ($this -> SharedStringIndex <= $Index)
527 | {
528 | // SSForwarded is set further to avoid double reading in case nodes are skipped.
529 | if ($this -> SSForwarded)
530 | {
531 | $this -> SSForwarded = false;
532 | }
533 | else
534 | {
535 | $ReadStatus = $this -> SharedStrings -> read();
536 | if (!$ReadStatus)
537 | {
538 | break;
539 | }
540 | }
541 |
542 | if ($this -> SharedStrings -> name == 'si')
543 | {
544 | if ($this -> SharedStrings -> nodeType == XMLReader::END_ELEMENT)
545 | {
546 | $this -> SSOpen = false;
547 | $this -> SharedStringIndex++;
548 | }
549 | else
550 | {
551 | $this -> SSOpen = true;
552 |
553 | if ($this -> SharedStringIndex < $Index)
554 | {
555 | $this -> SSOpen = false;
556 | $this -> SharedStrings -> next('si');
557 | $this -> SSForwarded = true;
558 | $this -> SharedStringIndex++;
559 | continue;
560 | }
561 | else
562 | {
563 | break;
564 | }
565 | }
566 | }
567 | }
568 |
569 | $Value = '';
570 |
571 | // Extract the value from the shared string
572 | if ($this -> SSOpen && ($this -> SharedStringIndex == $Index))
573 | {
574 | while ($this -> SharedStrings -> read())
575 | {
576 | switch ($this -> SharedStrings -> name)
577 | {
578 | case 't':
579 | if ($this -> SharedStrings -> nodeType == XMLReader::END_ELEMENT)
580 | {
581 | continue;
582 | }
583 | $Value .= $this -> SharedStrings -> readString();
584 | break;
585 | case 'si':
586 | if ($this -> SharedStrings -> nodeType == XMLReader::END_ELEMENT)
587 | {
588 | $this -> SSOpen = false;
589 | $this -> SSForwarded = true;
590 | break 2;
591 | }
592 | break;
593 | }
594 | }
595 | }
596 |
597 | if ($Value)
598 | {
599 | $this -> LastSharedStringValue = $Value;
600 | }
601 | return $Value;
602 | }
603 |
604 | /**
605 | * Formats the value according to the index
606 | *
607 | * @param string Cell value
608 | * @param int Format index
609 | *
610 | * @return string Formatted cell value
611 | */
612 | private function FormatValue($Value, $Index)
613 | {
614 | if (!is_numeric($Value))
615 | {
616 | return $Value;
617 | }
618 |
619 | if (isset($this -> Styles[$Index]) && ($this -> Styles[$Index] !== false))
620 | {
621 | $Index = $this -> Styles[$Index];
622 | }
623 | else
624 | {
625 | return $Value;
626 | }
627 |
628 | // A special case for the "General" format
629 | if ($Index == 0)
630 | {
631 | return $this -> GeneralFormat($Value);
632 | }
633 |
634 | $Format = array();
635 |
636 | if (isset($this -> ParsedFormatCache[$Index]))
637 | {
638 | $Format = $this -> ParsedFormatCache[$Index];
639 | }
640 |
641 | if (!$Format)
642 | {
643 | $Format = array(
644 | 'Code' => false,
645 | 'Type' => false,
646 | 'Scale' => 1,
647 | 'Thousands' => false,
648 | 'Currency' => false
649 | );
650 |
651 | if (isset(self::$BuiltinFormats[$Index]))
652 | {
653 | $Format['Code'] = self::$BuiltinFormats[$Index];
654 | }
655 | elseif (isset($this -> Formats[$Index]))
656 | {
657 | $Format['Code'] = $this -> Formats[$Index];
658 | }
659 |
660 | // Format code found, now parsing the format
661 | if ($Format['Code'])
662 | {
663 | $Sections = explode(';', $Format['Code']);
664 | $Format['Code'] = $Sections[0];
665 |
666 | switch (count($Sections))
667 | {
668 | case 2:
669 | if ($Value < 0)
670 | {
671 | $Format['Code'] = $Sections[1];
672 | }
673 | break;
674 | case 3:
675 | case 4:
676 | if ($Value < 0)
677 | {
678 | $Format['Code'] = $Sections[1];
679 | }
680 | elseif ($Value == 0)
681 | {
682 | $Format['Code'] = $Sections[2];
683 | }
684 | break;
685 | }
686 | }
687 |
688 | // Stripping colors
689 | $Format['Code'] = trim(preg_replace('{^\[[[:alpha:]]+\]}i', '', $Format['Code']));
690 |
691 | // Percentages
692 | if (substr($Format['Code'], -1) == '%')
693 | {
694 | $Format['Type'] = 'Percentage';
695 | }
696 | elseif (preg_match('{^(\[\$[[:alpha:]]*-[0-9A-F]*\])*[hmsdy]}i', $Format['Code']))
697 | {
698 | $Format['Type'] = 'DateTime';
699 |
700 | $Format['Code'] = trim(preg_replace('{^(\[\$[[:alpha:]]*-[0-9A-F]*\])}i', '', $Format['Code']));
701 | $Format['Code'] = strtolower($Format['Code']);
702 |
703 | $Format['Code'] = strtr($Format['Code'], self::$DateReplacements['All']);
704 | if (strpos($Format['Code'], 'A') === false)
705 | {
706 | $Format['Code'] = strtr($Format['Code'], self::$DateReplacements['24H']);
707 | }
708 | else
709 | {
710 | $Format['Code'] = strtr($Format['Code'], self::$DateReplacements['12H']);
711 | }
712 | }
713 | elseif ($Format['Code'] == '[$EUR ]#,##0.00_-')
714 | {
715 | $Format['Type'] = 'Euro';
716 | }
717 | else
718 | {
719 | // Removing skipped characters
720 | $Format['Code'] = preg_replace('{_.}', '', $Format['Code']);
721 | // Removing unnecessary escaping
722 | $Format['Code'] = preg_replace("{\\\\}", '', $Format['Code']);
723 | // Removing string quotes
724 | $Format['Code'] = str_replace(array('"', '*'), '', $Format['Code']);
725 | // Removing thousands separator
726 | if (strpos($Format['Code'], '0,0') !== false || strpos($Format['Code'], '#,#') !== false)
727 | {
728 | $Format['Thousands'] = true;
729 | }
730 | $Format['Code'] = str_replace(array('0,0', '#,#'), array('00', '##'), $Format['Code']);
731 |
732 | // Scaling (Commas indicate the power)
733 | $Scale = 1;
734 | $Matches = array();
735 | if (preg_match('{(0|#)(,+)}', $Format['Code'], $Matches))
736 | {
737 | $Scale = pow(1000, strlen($Matches[2]));
738 | // Removing the commas
739 | $Format['Code'] = preg_replace(array('{0,+}', '{#,+}'), array('0', '#'), $Format['Code']);
740 | }
741 |
742 | $Format['Scale'] = $Scale;
743 |
744 | if (preg_match('{#?.*\?\/\?}', $Format['Code']))
745 | {
746 | $Format['Type'] = 'Fraction';
747 | }
748 | else
749 | {
750 | $Format['Code'] = str_replace('#', '', $Format['Code']);
751 |
752 | $Matches = array();
753 | if (preg_match('{(0+)(\.?)(0*)}', preg_replace('{\[[^\]]+\]}', '', $Format['Code']), $Matches))
754 | {
755 | $Integer = $Matches[1];
756 | $DecimalPoint = $Matches[2];
757 | $Decimals = $Matches[3];
758 |
759 | $Format['MinWidth'] = strlen($Integer) + strlen($DecimalPoint) + strlen($Decimals);
760 | $Format['Decimals'] = $Decimals;
761 | $Format['Precision'] = strlen($Format['Decimals']);
762 | $Format['Pattern'] = '%0'.$Format['MinWidth'].'.'.$Format['Precision'].'f';
763 | }
764 | }
765 |
766 | $Matches = array();
767 | if (preg_match('{\[\$(.*)\]}u', $Format['Code'], $Matches))
768 | {
769 | $CurrFormat = $Matches[0];
770 | $CurrCode = $Matches[1];
771 | $CurrCode = explode('-', $CurrCode);
772 | if ($CurrCode)
773 | {
774 | $CurrCode = $CurrCode[0];
775 | }
776 |
777 | if (!$CurrCode)
778 | {
779 | $CurrCode = self::$CurrencyCode;
780 | }
781 |
782 | $Format['Currency'] = $CurrCode;
783 | }
784 | $Format['Code'] = trim($Format['Code']);
785 | }
786 |
787 | $this -> ParsedFormatCache[$Index] = $Format;
788 | }
789 |
790 | // Applying format to value
791 | if ($Format)
792 | {
793 | if ($Format['Code'] == '@')
794 | {
795 | return (string)$Value;
796 | }
797 | // Percentages
798 | elseif ($Format['Type'] == 'Percentage')
799 | {
800 | if ($Format['Code'] === '0%')
801 | {
802 | $Value = round(100 * $Value, 0).'%';
803 | }
804 | else
805 | {
806 | $Value = sprintf('%.2f%%', round(100 * $Value, 2));
807 | }
808 | }
809 | // Dates and times
810 | elseif ($Format['Type'] == 'DateTime')
811 | {
812 | $Days = (int)$Value;
813 | // Correcting for Feb 29, 1900
814 | if ($Days > 60)
815 | {
816 | $Days--;
817 | }
818 |
819 | // At this point time is a fraction of a day
820 | $Time = ($Value - (int)$Value);
821 | $Seconds = 0;
822 | if ($Time)
823 | {
824 | // Here time is converted to seconds
825 | // Some loss of precision will occur
826 | $Seconds = (int)($Time * 86400);
827 | }
828 |
829 | $Value = clone self::$BaseDate;
830 | $Value -> add(new DateInterval('P'.$Days.'D'.($Seconds ? 'T'.$Seconds.'S' : '')));
831 |
832 | if (!$this -> Options['ReturnDateTimeObjects'])
833 | {
834 | $Value = $Value -> format($Format['Code']);
835 | }
836 | else
837 | {
838 | // A DateTime object is returned
839 | }
840 | }
841 | elseif ($Format['Type'] == 'Euro')
842 | {
843 | $Value = 'EUR '.sprintf('%1.2f', $Value);
844 | }
845 | else
846 | {
847 | // Fractional numbers
848 | if ($Format['Type'] == 'Fraction' && ($Value != (int)$Value))
849 | {
850 | $Integer = floor(abs($Value));
851 | $Decimal = fmod(abs($Value), 1);
852 | // Removing the integer part and decimal point
853 | $Decimal *= pow(10, strlen($Decimal) - 2);
854 | $DecimalDivisor = pow(10, strlen($Decimal));
855 |
856 | if (self::$RuntimeInfo['GMPSupported'])
857 | {
858 | $GCD = gmp_strval(gmp_gcd($Decimal, $DecimalDivisor));
859 | }
860 | else
861 | {
862 | $GCD = self::GCD($Decimal, $DecimalDivisor);
863 | }
864 |
865 | $AdjDecimal = $DecimalPart/$GCD;
866 | $AdjDecimalDivisor = $DecimalDivisor/$GCD;
867 |
868 | if (
869 | strpos($Format['Code'], '0') !== false ||
870 | strpos($Format['Code'], '#') !== false ||
871 | substr($Format['Code'], 0, 3) == '? ?'
872 | )
873 | {
874 | // The integer part is shown separately apart from the fraction
875 | $Value = ($Value < 0 ? '-' : '').
876 | $Integer ? $Integer.' ' : ''.
877 | $AdjDecimal.'/'.
878 | $AdjDecimalDivisor;
879 | }
880 | else
881 | {
882 | // The fraction includes the integer part
883 | $AdjDecimal += $Integer * $AdjDecimalDivisor;
884 | $Value = ($Value < 0 ? '-' : '').
885 | $AdjDecimal.'/'.
886 | $AdjDecimalDivisor;
887 | }
888 | }
889 | else
890 | {
891 | // Scaling
892 | $Value = $Value / $Format['Scale'];
893 |
894 | if (!empty($Format['MinWidth']) && $Format['Decimals'])
895 | {
896 | if ($Format['Thousands'])
897 | {
898 | $Value = number_format($Value, $Format['Precision'],
899 | self::$DecimalSeparator, self::$ThousandSeparator);
900 | }
901 | else
902 | {
903 | $Value = sprintf($Format['Pattern'], $Value);
904 | }
905 |
906 | $Value = preg_replace('{(0+)(\.?)(0*)}', $Value, $Format['Code']);
907 | }
908 | }
909 |
910 | // Currency/Accounting
911 | if ($Format['Currency'])
912 | {
913 | $Value = preg_replace('', $Format['Currency'], $Value);
914 | }
915 | }
916 |
917 | }
918 |
919 | return $Value;
920 | }
921 |
922 | /**
923 | * Attempts to approximate Excel's "general" format.
924 | *
925 | * @param mixed Value
926 | *
927 | * @return mixed Result
928 | */
929 | public function GeneralFormat($Value)
930 | {
931 | // Numeric format
932 | if (is_numeric($Value))
933 | {
934 | $Value = (float)$Value;
935 | }
936 | return $Value;
937 | }
938 |
939 | // !Iterator interface methods
940 | /**
941 | * Rewind the Iterator to the first element.
942 | * Similar to the reset() function for arrays in PHP
943 | */
944 | public function rewind()
945 | {
946 | // Removed the check whether $this -> Index == 0 otherwise ChangeSheet doesn't work properly
947 |
948 | // If the worksheet was already iterated, XML file is reopened.
949 | // Otherwise it should be at the beginning anyway
950 | if ($this -> Worksheet instanceof XMLReader)
951 | {
952 | $this -> Worksheet -> close();
953 | }
954 | else
955 | {
956 | $this -> Worksheet = new XMLReader;
957 | }
958 |
959 | $this -> Worksheet -> open($this -> WorksheetPath);
960 |
961 | $this -> Valid = true;
962 | $this -> RowOpen = false;
963 | $this -> CurrentRow = false;
964 | $this -> Index = 0;
965 | }
966 |
967 | /**
968 | * Return the current element.
969 | * Similar to the current() function for arrays in PHP
970 | *
971 | * @return mixed current element from the collection
972 | */
973 | public function current()
974 | {
975 | if ($this -> Index == 0 && $this -> CurrentRow === false)
976 | {
977 | $this -> next();
978 | $this -> Index--;
979 | }
980 | return $this -> CurrentRow;
981 | }
982 |
983 | /**
984 | * Move forward to next element.
985 | * Similar to the next() function for arrays in PHP
986 | */
987 | public function next()
988 | {
989 | $this -> Index++;
990 |
991 | $this -> CurrentRow = array();
992 |
993 | if (!$this -> RowOpen)
994 | {
995 | while ($this -> Valid = $this -> Worksheet -> read())
996 | {
997 | if ($this -> Worksheet -> name == 'row')
998 | {
999 | // Getting the row spanning area (stored as e.g., 1:12)
1000 | // so that the last cells will be present, even if empty
1001 | $RowSpans = $this -> Worksheet -> getAttribute('spans');
1002 | if ($RowSpans)
1003 | {
1004 | $RowSpans = explode(':', $RowSpans);
1005 | $CurrentRowColumnCount = $RowSpans[1];
1006 | }
1007 | else
1008 | {
1009 | $CurrentRowColumnCount = 0;
1010 | }
1011 |
1012 | if ($CurrentRowColumnCount > 0)
1013 | {
1014 | $this -> CurrentRow = array_fill(0, $CurrentRowColumnCount, '');
1015 | }
1016 |
1017 | $this -> RowOpen = true;
1018 | break;
1019 | }
1020 | }
1021 | }
1022 |
1023 | // Reading the necessary row, if found
1024 | if ($this -> RowOpen)
1025 | {
1026 | // These two are needed to control for empty cells
1027 | $MaxIndex = 0;
1028 | $CellCount = 0;
1029 |
1030 | $CellHasSharedString = false;
1031 |
1032 | while ($this -> Valid = $this -> Worksheet -> read())
1033 | {
1034 | switch ($this -> Worksheet -> name)
1035 | {
1036 | // End of row
1037 | case 'row':
1038 | if ($this -> Worksheet -> nodeType == XMLReader::END_ELEMENT)
1039 | {
1040 | $this -> RowOpen = false;
1041 | break 2;
1042 | }
1043 | break;
1044 | // Cell
1045 | case 'c':
1046 | // If it is a closing tag, skip it
1047 | if ($this -> Worksheet -> nodeType == XMLReader::END_ELEMENT)
1048 | {
1049 | continue;
1050 | }
1051 |
1052 | $StyleId = (int)$this -> Worksheet -> getAttribute('s');
1053 |
1054 | // Get the index of the cell
1055 | $Index = $this -> Worksheet -> getAttribute('r');
1056 | $Letter = preg_replace('{[^[:alpha:]]}S', '', $Index);
1057 | $Index = self::IndexFromColumnLetter($Letter);
1058 |
1059 | // Determine cell type
1060 | if ($this -> Worksheet -> getAttribute('t') == self::CELL_TYPE_SHARED_STR)
1061 | {
1062 | $CellHasSharedString = true;
1063 | }
1064 | else
1065 | {
1066 | $CellHasSharedString = false;
1067 | }
1068 |
1069 | $this -> CurrentRow[$Index] = '';
1070 |
1071 | $CellCount++;
1072 | if ($Index > $MaxIndex)
1073 | {
1074 | $MaxIndex = $Index;
1075 | }
1076 |
1077 | break;
1078 | // Cell value
1079 | case 'v':
1080 | case 'is':
1081 | if ($this -> Worksheet -> nodeType == XMLReader::END_ELEMENT)
1082 | {
1083 | continue;
1084 | }
1085 |
1086 | $Value = $this -> Worksheet -> readString();
1087 |
1088 | if ($CellHasSharedString)
1089 | {
1090 | $Value = $this -> GetSharedString($Value);
1091 | }
1092 |
1093 | // Format value if necessary
1094 | if ($Value !== '' && $StyleId && isset($this -> Styles[$StyleId]))
1095 | {
1096 | $Value = $this -> FormatValue($Value, $StyleId);
1097 | }
1098 | elseif ($Value)
1099 | {
1100 | $Value = $this -> GeneralFormat($Value);
1101 | }
1102 |
1103 | $this -> CurrentRow[$Index] = $Value;
1104 | break;
1105 | }
1106 | }
1107 |
1108 | // Adding empty cells, if necessary
1109 | // Only empty cells inbetween and on the left side are added
1110 | if ($MaxIndex + 1 > $CellCount)
1111 | {
1112 | $this -> CurrentRow = $this -> CurrentRow + array_fill(0, $MaxIndex + 1, '');
1113 | ksort($this -> CurrentRow);
1114 | }
1115 | }
1116 |
1117 | return $this -> CurrentRow;
1118 | }
1119 |
1120 | /**
1121 | * Return the identifying key of the current element.
1122 | * Similar to the key() function for arrays in PHP
1123 | *
1124 | * @return mixed either an integer or a string
1125 | */
1126 | public function key()
1127 | {
1128 | return $this -> Index;
1129 | }
1130 |
1131 | /**
1132 | * Check if there is a current element after calls to rewind() or next().
1133 | * Used to check if we've iterated to the end of the collection
1134 | *
1135 | * @return boolean FALSE if there's nothing more to iterate over
1136 | */
1137 | public function valid()
1138 | {
1139 | return $this -> Valid;
1140 | }
1141 |
1142 | // !Countable interface method
1143 | /**
1144 | * Ostensibly should return the count of the contained items but this just returns the number
1145 | * of rows read so far. It's not really correct but at least coherent.
1146 | */
1147 | public function count()
1148 | {
1149 | return $this -> Index + 1;
1150 | }
1151 |
1152 | /**
1153 | * Takes the column letter and converts it to a numerical index (0-based)
1154 | *
1155 | * @param string Letter(s) to convert
1156 | *
1157 | * @return mixed Numeric index (0-based) or boolean false if it cannot be calculated
1158 | */
1159 | public static function IndexFromColumnLetter($Letter)
1160 | {
1161 | $Powers = array();
1162 |
1163 | $Letter = strtoupper($Letter);
1164 |
1165 | $Result = 0;
1166 | for ($i = strlen($Letter) - 1, $j = 0; $i >= 0; $i--, $j++)
1167 | {
1168 | $Ord = ord($Letter[$i]) - 64;
1169 | if ($Ord > 26)
1170 | {
1171 | // Something is very, very wrong
1172 | return false;
1173 | }
1174 | $Result += $Ord * pow(26, $j);
1175 | }
1176 | return $Result - 1;
1177 | }
1178 |
1179 | /**
1180 | * Helper function for greatest common divisor calculation in case GMP extension is
1181 | * not enabled
1182 | *
1183 | * @param int Number #1
1184 | * @param int Number #2
1185 | *
1186 | * @param int Greatest common divisor
1187 | */
1188 | public static function GCD($A, $B)
1189 | {
1190 | $A = abs($A);
1191 | $B = abs($B);
1192 | if ($A + $B == 0)
1193 | {
1194 | return 0;
1195 | }
1196 | else
1197 | {
1198 | $C = 1;
1199 |
1200 | while ($A > 0)
1201 | {
1202 | $C = $A;
1203 | $A = $B % $A;
1204 | $B = $C;
1205 | }
1206 |
1207 | return $C;
1208 | }
1209 | }
1210 | }
1211 | ?>
1212 |
--------------------------------------------------------------------------------
/vendor/spreadsheet-reader/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nuovo/spreadsheet-reader",
3 | "description": "Spreadsheet reader library for Excel, OpenOffice and structured text files",
4 | "keywords": ["spreadsheet", "xls", "xlsx", "ods", "csv", "excel", "openoffice"],
5 | "homepage": "https://github.com/nuovo/spreadsheet-reader",
6 | "version": "0.5.11",
7 | "time": "2015-04-30",
8 | "type": "library",
9 | "license": ["MIT"],
10 | "authors": [
11 | {
12 | "name": "Martins Pilsetnieks",
13 | "email": "pilsetnieks@gmail.com",
14 | "homepage": "http://www.nuovo.lv/"
15 | }
16 | ],
17 | "support": {
18 | "email": "spreadsheet-reader@nuovo.lv"
19 | },
20 | "require": {
21 | "php": ">= 5.3.0",
22 | "ext-zip": "*"
23 | },
24 | "autoload": {
25 | "classmap": ["./"]
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/vendor/spreadsheet-reader/php-excel-reader/excel_reader2.php:
--------------------------------------------------------------------------------
1 |
8 | * Maintained at http://code.google.com/p/php-excel-reader/
9 | *
10 | * Format parsing and MUCH more contributed by:
11 | * Matt Roxburgh < http://www.roxburgh.me.uk >
12 | *
13 | * DOCUMENTATION
14 | * =============
15 | * http://code.google.com/p/php-excel-reader/wiki/Documentation
16 | *
17 | * CHANGE LOG
18 | * ==========
19 | * http://code.google.com/p/php-excel-reader/wiki/ChangeHistory
20 | *
21 | * DISCUSSION/SUPPORT
22 | * ==================
23 | * http://groups.google.com/group/php-excel-reader-discuss/topics
24 | *
25 | * --------------------------------------------------------------------------
26 | *
27 | * Originally developed by Vadim Tkachenko under the name PHPExcelReader.
28 | * (http://sourceforge.net/projects/phpexcelreader)
29 | * Based on the Java version by Andy Khan (http://www.andykhan.com). Now
30 | * maintained by David Sanders. Reads only Biff 7 and Biff 8 formats.
31 | *
32 | * PHP versions 4 and 5
33 | *
34 | * LICENSE: This source file is subject to version 3.0 of the PHP license
35 | * that is available through the world-wide-web at the following URI:
36 | * http://www.php.net/license/3_0.txt. If you did not receive a copy of
37 | * the PHP License and are unable to obtain it through the web, please
38 | * send a note to license@php.net so we can mail you a copy immediately.
39 | *
40 | * @category Spreadsheet
41 | * @package Spreadsheet_Excel_Reader
42 | * @author Vadim Tkachenko
43 | * @license http://www.php.net/license/3_0.txt PHP License 3.0
44 | * @version CVS: $Id: reader.php 19 2007-03-13 12:42:41Z shangxiao $
45 | * @link http://pear.php.net/package/Spreadsheet_Excel_Reader
46 | * @see OLE, Spreadsheet_Excel_Writer
47 | * --------------------------------------------------------------------------
48 | */
49 |
50 | define('NUM_BIG_BLOCK_DEPOT_BLOCKS_POS', 0x2c);
51 | define('SMALL_BLOCK_DEPOT_BLOCK_POS', 0x3c);
52 | define('ROOT_START_BLOCK_POS', 0x30);
53 | define('BIG_BLOCK_SIZE', 0x200);
54 | define('SMALL_BLOCK_SIZE', 0x40);
55 | define('EXTENSION_BLOCK_POS', 0x44);
56 | define('NUM_EXTENSION_BLOCK_POS', 0x48);
57 | define('PROPERTY_STORAGE_BLOCK_SIZE', 0x80);
58 | define('BIG_BLOCK_DEPOT_BLOCKS_POS', 0x4c);
59 | define('SMALL_BLOCK_THRESHOLD', 0x1000);
60 | // property storage offsets
61 | define('SIZE_OF_NAME_POS', 0x40);
62 | define('TYPE_POS', 0x42);
63 | define('START_BLOCK_POS', 0x74);
64 | define('SIZE_POS', 0x78);
65 | define('IDENTIFIER_OLE', pack("CCCCCCCC",0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1));
66 |
67 |
68 | function GetInt4d($data, $pos) {
69 | $value = ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | (ord($data[$pos+3]) << 24);
70 | if ($value>=4294967294) {
71 | $value=-2;
72 | }
73 | return $value;
74 | }
75 |
76 | // http://uk.php.net/manual/en/function.getdate.php
77 | function gmgetdate($ts = null){
78 | $k = array('seconds','minutes','hours','mday','wday','mon','year','yday','weekday','month',0);
79 | return(array_comb($k,explode(":",gmdate('s:i:G:j:w:n:Y:z:l:F:U',is_null($ts)?time():$ts))));
80 | }
81 |
82 | // Added for PHP4 compatibility
83 | function array_comb($array1, $array2) {
84 | $out = array();
85 | foreach ($array1 as $key => $value) {
86 | $out[$value] = $array2[$key];
87 | }
88 | return $out;
89 | }
90 |
91 | function v($data,$pos) {
92 | return ord($data[$pos]) | ord($data[$pos+1])<<8;
93 | }
94 |
95 | class OLERead {
96 | var $data = '';
97 | function OLERead(){ }
98 |
99 | function read($sFileName){
100 | // check if file exist and is readable (Darko Miljanovic)
101 | if(!is_readable($sFileName)) {
102 | $this->error = 1;
103 | return false;
104 | }
105 | $this->data = @file_get_contents($sFileName);
106 | if (!$this->data) {
107 | $this->error = 1;
108 | return false;
109 | }
110 | if (substr($this->data, 0, 8) != IDENTIFIER_OLE) {
111 | $this->error = 1;
112 | return false;
113 | }
114 | $this->numBigBlockDepotBlocks = GetInt4d($this->data, NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
115 | $this->sbdStartBlock = GetInt4d($this->data, SMALL_BLOCK_DEPOT_BLOCK_POS);
116 | $this->rootStartBlock = GetInt4d($this->data, ROOT_START_BLOCK_POS);
117 | $this->extensionBlock = GetInt4d($this->data, EXTENSION_BLOCK_POS);
118 | $this->numExtensionBlocks = GetInt4d($this->data, NUM_EXTENSION_BLOCK_POS);
119 |
120 | $bigBlockDepotBlocks = array();
121 | $pos = BIG_BLOCK_DEPOT_BLOCKS_POS;
122 | $bbdBlocks = $this->numBigBlockDepotBlocks;
123 | if ($this->numExtensionBlocks != 0) {
124 | $bbdBlocks = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS)/4;
125 | }
126 |
127 | for ($i = 0; $i < $bbdBlocks; $i++) {
128 | $bigBlockDepotBlocks[$i] = GetInt4d($this->data, $pos);
129 | $pos += 4;
130 | }
131 |
132 |
133 | for ($j = 0; $j < $this->numExtensionBlocks; $j++) {
134 | $pos = ($this->extensionBlock + 1) * BIG_BLOCK_SIZE;
135 | $blocksToRead = min($this->numBigBlockDepotBlocks - $bbdBlocks, BIG_BLOCK_SIZE / 4 - 1);
136 |
137 | for ($i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; $i++) {
138 | $bigBlockDepotBlocks[$i] = GetInt4d($this->data, $pos);
139 | $pos += 4;
140 | }
141 |
142 | $bbdBlocks += $blocksToRead;
143 | if ($bbdBlocks < $this->numBigBlockDepotBlocks) {
144 | $this->extensionBlock = GetInt4d($this->data, $pos);
145 | }
146 | }
147 |
148 | // readBigBlockDepot
149 | $pos = 0;
150 | $index = 0;
151 | $this->bigBlockChain = array();
152 |
153 | for ($i = 0; $i < $this->numBigBlockDepotBlocks; $i++) {
154 | $pos = ($bigBlockDepotBlocks[$i] + 1) * BIG_BLOCK_SIZE;
155 | //echo "pos = $pos";
156 | for ($j = 0 ; $j < BIG_BLOCK_SIZE / 4; $j++) {
157 | $this->bigBlockChain[$index] = GetInt4d($this->data, $pos);
158 | $pos += 4 ;
159 | $index++;
160 | }
161 | }
162 |
163 | // readSmallBlockDepot();
164 | $pos = 0;
165 | $index = 0;
166 | $sbdBlock = $this->sbdStartBlock;
167 | $this->smallBlockChain = array();
168 |
169 | while ($sbdBlock != -2) {
170 | $pos = ($sbdBlock + 1) * BIG_BLOCK_SIZE;
171 | for ($j = 0; $j < BIG_BLOCK_SIZE / 4; $j++) {
172 | $this->smallBlockChain[$index] = GetInt4d($this->data, $pos);
173 | $pos += 4;
174 | $index++;
175 | }
176 | $sbdBlock = $this->bigBlockChain[$sbdBlock];
177 | }
178 |
179 |
180 | // readData(rootStartBlock)
181 | $block = $this->rootStartBlock;
182 | $pos = 0;
183 | $this->entry = $this->__readData($block);
184 | $this->__readPropertySets();
185 | }
186 |
187 | function __readData($bl) {
188 | $block = $bl;
189 | $pos = 0;
190 | $data = '';
191 | while ($block != -2) {
192 | $pos = ($block + 1) * BIG_BLOCK_SIZE;
193 | $data = $data.substr($this->data, $pos, BIG_BLOCK_SIZE);
194 | $block = $this->bigBlockChain[$block];
195 | }
196 | return $data;
197 | }
198 |
199 | function __readPropertySets(){
200 | $offset = 0;
201 | while ($offset < strlen($this->entry)) {
202 | $d = substr($this->entry, $offset, PROPERTY_STORAGE_BLOCK_SIZE);
203 | $nameSize = ord($d[SIZE_OF_NAME_POS]) | (ord($d[SIZE_OF_NAME_POS+1]) << 8);
204 | $type = ord($d[TYPE_POS]);
205 | $startBlock = GetInt4d($d, START_BLOCK_POS);
206 | $size = GetInt4d($d, SIZE_POS);
207 | $name = '';
208 | for ($i = 0; $i < $nameSize ; $i++) {
209 | $name .= $d[$i];
210 | }
211 | $name = str_replace("\x00", "", $name);
212 | $this->props[] = array (
213 | 'name' => $name,
214 | 'type' => $type,
215 | 'startBlock' => $startBlock,
216 | 'size' => $size);
217 | if ((strtolower($name) == "workbook") || ( strtolower($name) == "book")) {
218 | $this->wrkbook = count($this->props) - 1;
219 | }
220 | if ($name == "Root Entry") {
221 | $this->rootentry = count($this->props) - 1;
222 | }
223 | $offset += PROPERTY_STORAGE_BLOCK_SIZE;
224 | }
225 |
226 | }
227 |
228 |
229 | function getWorkBook(){
230 | if ($this->props[$this->wrkbook]['size'] < SMALL_BLOCK_THRESHOLD){
231 | $rootdata = $this->__readData($this->props[$this->rootentry]['startBlock']);
232 | $streamData = '';
233 | $block = $this->props[$this->wrkbook]['startBlock'];
234 | $pos = 0;
235 | while ($block != -2) {
236 | $pos = $block * SMALL_BLOCK_SIZE;
237 | $streamData .= substr($rootdata, $pos, SMALL_BLOCK_SIZE);
238 | $block = $this->smallBlockChain[$block];
239 | }
240 | return $streamData;
241 | }else{
242 | $numBlocks = $this->props[$this->wrkbook]['size'] / BIG_BLOCK_SIZE;
243 | if ($this->props[$this->wrkbook]['size'] % BIG_BLOCK_SIZE != 0) {
244 | $numBlocks++;
245 | }
246 |
247 | if ($numBlocks == 0) return '';
248 | $streamData = '';
249 | $block = $this->props[$this->wrkbook]['startBlock'];
250 | $pos = 0;
251 | while ($block != -2) {
252 | $pos = ($block + 1) * BIG_BLOCK_SIZE;
253 | $streamData .= substr($this->data, $pos, BIG_BLOCK_SIZE);
254 | $block = $this->bigBlockChain[$block];
255 | }
256 | return $streamData;
257 | }
258 | }
259 |
260 | }
261 |
262 | define('SPREADSHEET_EXCEL_READER_BIFF8', 0x600);
263 | define('SPREADSHEET_EXCEL_READER_BIFF7', 0x500);
264 | define('SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS', 0x5);
265 | define('SPREADSHEET_EXCEL_READER_WORKSHEET', 0x10);
266 | define('SPREADSHEET_EXCEL_READER_TYPE_BOF', 0x809);
267 | define('SPREADSHEET_EXCEL_READER_TYPE_EOF', 0x0a);
268 | define('SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET', 0x85);
269 | define('SPREADSHEET_EXCEL_READER_TYPE_DIMENSION', 0x200);
270 | define('SPREADSHEET_EXCEL_READER_TYPE_ROW', 0x208);
271 | define('SPREADSHEET_EXCEL_READER_TYPE_DBCELL', 0xd7);
272 | define('SPREADSHEET_EXCEL_READER_TYPE_FILEPASS', 0x2f);
273 | define('SPREADSHEET_EXCEL_READER_TYPE_NOTE', 0x1c);
274 | define('SPREADSHEET_EXCEL_READER_TYPE_TXO', 0x1b6);
275 | define('SPREADSHEET_EXCEL_READER_TYPE_RK', 0x7e);
276 | define('SPREADSHEET_EXCEL_READER_TYPE_RK2', 0x27e);
277 | define('SPREADSHEET_EXCEL_READER_TYPE_MULRK', 0xbd);
278 | define('SPREADSHEET_EXCEL_READER_TYPE_MULBLANK', 0xbe);
279 | define('SPREADSHEET_EXCEL_READER_TYPE_INDEX', 0x20b);
280 | define('SPREADSHEET_EXCEL_READER_TYPE_SST', 0xfc);
281 | define('SPREADSHEET_EXCEL_READER_TYPE_EXTSST', 0xff);
282 | define('SPREADSHEET_EXCEL_READER_TYPE_CONTINUE', 0x3c);
283 | define('SPREADSHEET_EXCEL_READER_TYPE_LABEL', 0x204);
284 | define('SPREADSHEET_EXCEL_READER_TYPE_LABELSST', 0xfd);
285 | define('SPREADSHEET_EXCEL_READER_TYPE_NUMBER', 0x203);
286 | define('SPREADSHEET_EXCEL_READER_TYPE_NAME', 0x18);
287 | define('SPREADSHEET_EXCEL_READER_TYPE_ARRAY', 0x221);
288 | define('SPREADSHEET_EXCEL_READER_TYPE_STRING', 0x207);
289 | define('SPREADSHEET_EXCEL_READER_TYPE_FORMULA', 0x406);
290 | define('SPREADSHEET_EXCEL_READER_TYPE_FORMULA2', 0x6);
291 | define('SPREADSHEET_EXCEL_READER_TYPE_FORMAT', 0x41e);
292 | define('SPREADSHEET_EXCEL_READER_TYPE_XF', 0xe0);
293 | define('SPREADSHEET_EXCEL_READER_TYPE_BOOLERR', 0x205);
294 | define('SPREADSHEET_EXCEL_READER_TYPE_FONT', 0x0031);
295 | define('SPREADSHEET_EXCEL_READER_TYPE_PALETTE', 0x0092);
296 | define('SPREADSHEET_EXCEL_READER_TYPE_UNKNOWN', 0xffff);
297 | define('SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR', 0x22);
298 | define('SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS', 0xE5);
299 | define('SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS' , 25569);
300 | define('SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904', 24107);
301 | define('SPREADSHEET_EXCEL_READER_MSINADAY', 86400);
302 | define('SPREADSHEET_EXCEL_READER_TYPE_HYPER', 0x01b8);
303 | define('SPREADSHEET_EXCEL_READER_TYPE_COLINFO', 0x7d);
304 | define('SPREADSHEET_EXCEL_READER_TYPE_DEFCOLWIDTH', 0x55);
305 | define('SPREADSHEET_EXCEL_READER_TYPE_STANDARDWIDTH', 0x99);
306 | define('SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT', "%s");
307 |
308 |
309 | /*
310 | * Main Class
311 | */
312 | class Spreadsheet_Excel_Reader {
313 |
314 | // MK: Added to make data retrieval easier
315 | var $colnames = array();
316 | var $colindexes = array();
317 | var $standardColWidth = 0;
318 | var $defaultColWidth = 0;
319 |
320 | function myHex($d) {
321 | if ($d < 16) return "0" . dechex($d);
322 | return dechex($d);
323 | }
324 |
325 | function dumpHexData($data, $pos, $length) {
326 | $info = "";
327 | for ($i = 0; $i <= $length; $i++) {
328 | $info .= ($i==0?"":" ") . $this->myHex(ord($data[$pos + $i])) . (ord($data[$pos + $i])>31? "[" . $data[$pos + $i] . "]":'');
329 | }
330 | return $info;
331 | }
332 |
333 | function getCol($col) {
334 | if (is_string($col)) {
335 | $col = strtolower($col);
336 | if (array_key_exists($col,$this->colnames)) {
337 | $col = $this->colnames[$col];
338 | }
339 | }
340 | return $col;
341 | }
342 |
343 | // PUBLIC API FUNCTIONS
344 | // --------------------
345 |
346 | function val($row,$col,$sheet=0) {
347 | $col = $this->getCol($col);
348 | if (array_key_exists($row,$this->sheets[$sheet]['cells']) && array_key_exists($col,$this->sheets[$sheet]['cells'][$row])) {
349 | return $this->sheets[$sheet]['cells'][$row][$col];
350 | }
351 | return "";
352 | }
353 | function value($row,$col,$sheet=0) {
354 | return $this->val($row,$col,$sheet);
355 | }
356 | function info($row,$col,$type='',$sheet=0) {
357 | $col = $this->getCol($col);
358 | if (array_key_exists('cellsInfo',$this->sheets[$sheet])
359 | && array_key_exists($row,$this->sheets[$sheet]['cellsInfo'])
360 | && array_key_exists($col,$this->sheets[$sheet]['cellsInfo'][$row])
361 | && array_key_exists($type,$this->sheets[$sheet]['cellsInfo'][$row][$col])) {
362 | return $this->sheets[$sheet]['cellsInfo'][$row][$col][$type];
363 | }
364 | return "";
365 | }
366 | function type($row,$col,$sheet=0) {
367 | return $this->info($row,$col,'type',$sheet);
368 | }
369 | function raw($row,$col,$sheet=0) {
370 | return $this->info($row,$col,'raw',$sheet);
371 | }
372 | function rowspan($row,$col,$sheet=0) {
373 | $val = $this->info($row,$col,'rowspan',$sheet);
374 | if ($val=="") { return 1; }
375 | return $val;
376 | }
377 | function colspan($row,$col,$sheet=0) {
378 | $val = $this->info($row,$col,'colspan',$sheet);
379 | if ($val=="") { return 1; }
380 | return $val;
381 | }
382 | function hyperlink($row,$col,$sheet=0) {
383 | $link = $this->sheets[$sheet]['cellsInfo'][$row][$col]['hyperlink'];
384 | if ($link) {
385 | return $link['link'];
386 | }
387 | return '';
388 | }
389 | function rowcount($sheet=0) {
390 | return $this->sheets[$sheet]['numRows'];
391 | }
392 | function colcount($sheet=0) {
393 | return $this->sheets[$sheet]['numCols'];
394 | }
395 | function colwidth($col,$sheet=0) {
396 | // Col width is actually the width of the number 0. So we have to estimate and come close
397 | return $this->colInfo[$sheet][$col]['width']/9142*200;
398 | }
399 | function colhidden($col,$sheet=0) {
400 | return !!$this->colInfo[$sheet][$col]['hidden'];
401 | }
402 | function rowheight($row,$sheet=0) {
403 | return $this->rowInfo[$sheet][$row]['height'];
404 | }
405 | function rowhidden($row,$sheet=0) {
406 | return !!$this->rowInfo[$sheet][$row]['hidden'];
407 | }
408 |
409 | // GET THE CSS FOR FORMATTING
410 | // ==========================
411 | function style($row,$col,$sheet=0,$properties='') {
412 | $css = "";
413 | $font=$this->font($row,$col,$sheet);
414 | if ($font!="") {
415 | $css .= "font-family:$font;";
416 | }
417 | $align=$this->align($row,$col,$sheet);
418 | if ($align!="") {
419 | $css .= "text-align:$align;";
420 | }
421 | $height=$this->height($row,$col,$sheet);
422 | if ($height!="") {
423 | $css .= "font-size:$height"."px;";
424 | }
425 | $bgcolor=$this->bgColor($row,$col,$sheet);
426 | if ($bgcolor!="") {
427 | $bgcolor = $this->colors[$bgcolor];
428 | $css .= "background-color:$bgcolor;";
429 | }
430 | $color=$this->color($row,$col,$sheet);
431 | if ($color!="") {
432 | $css .= "color:$color;";
433 | }
434 | $bold=$this->bold($row,$col,$sheet);
435 | if ($bold) {
436 | $css .= "font-weight:bold;";
437 | }
438 | $italic=$this->italic($row,$col,$sheet);
439 | if ($italic) {
440 | $css .= "font-style:italic;";
441 | }
442 | $underline=$this->underline($row,$col,$sheet);
443 | if ($underline) {
444 | $css .= "text-decoration:underline;";
445 | }
446 | // Borders
447 | $bLeft = $this->borderLeft($row,$col,$sheet);
448 | $bRight = $this->borderRight($row,$col,$sheet);
449 | $bTop = $this->borderTop($row,$col,$sheet);
450 | $bBottom = $this->borderBottom($row,$col,$sheet);
451 | $bLeftCol = $this->borderLeftColor($row,$col,$sheet);
452 | $bRightCol = $this->borderRightColor($row,$col,$sheet);
453 | $bTopCol = $this->borderTopColor($row,$col,$sheet);
454 | $bBottomCol = $this->borderBottomColor($row,$col,$sheet);
455 | // Try to output the minimal required style
456 | if ($bLeft!="" && $bLeft==$bRight && $bRight==$bTop && $bTop==$bBottom) {
457 | $css .= "border:" . $this->lineStylesCss[$bLeft] .";";
458 | }
459 | else {
460 | if ($bLeft!="") { $css .= "border-left:" . $this->lineStylesCss[$bLeft] .";"; }
461 | if ($bRight!="") { $css .= "border-right:" . $this->lineStylesCss[$bRight] .";"; }
462 | if ($bTop!="") { $css .= "border-top:" . $this->lineStylesCss[$bTop] .";"; }
463 | if ($bBottom!="") { $css .= "border-bottom:" . $this->lineStylesCss[$bBottom] .";"; }
464 | }
465 | // Only output border colors if there is an actual border specified
466 | if ($bLeft!="" && $bLeftCol!="") { $css .= "border-left-color:" . $bLeftCol .";"; }
467 | if ($bRight!="" && $bRightCol!="") { $css .= "border-right-color:" . $bRightCol .";"; }
468 | if ($bTop!="" && $bTopCol!="") { $css .= "border-top-color:" . $bTopCol . ";"; }
469 | if ($bBottom!="" && $bBottomCol!="") { $css .= "border-bottom-color:" . $bBottomCol .";"; }
470 |
471 | return $css;
472 | }
473 |
474 | // FORMAT PROPERTIES
475 | // =================
476 | function format($row,$col,$sheet=0) {
477 | return $this->info($row,$col,'format',$sheet);
478 | }
479 | function formatIndex($row,$col,$sheet=0) {
480 | return $this->info($row,$col,'formatIndex',$sheet);
481 | }
482 | function formatColor($row,$col,$sheet=0) {
483 | return $this->info($row,$col,'formatColor',$sheet);
484 | }
485 |
486 | // CELL (XF) PROPERTIES
487 | // ====================
488 | function xfRecord($row,$col,$sheet=0) {
489 | $xfIndex = $this->info($row,$col,'xfIndex',$sheet);
490 | if ($xfIndex!="") {
491 | return $this->xfRecords[$xfIndex];
492 | }
493 | return null;
494 | }
495 | function xfProperty($row,$col,$sheet,$prop) {
496 | $xfRecord = $this->xfRecord($row,$col,$sheet);
497 | if ($xfRecord!=null) {
498 | return $xfRecord[$prop];
499 | }
500 | return "";
501 | }
502 | function align($row,$col,$sheet=0) {
503 | return $this->xfProperty($row,$col,$sheet,'align');
504 | }
505 | function bgColor($row,$col,$sheet=0) {
506 | return $this->xfProperty($row,$col,$sheet,'bgColor');
507 | }
508 | function borderLeft($row,$col,$sheet=0) {
509 | return $this->xfProperty($row,$col,$sheet,'borderLeft');
510 | }
511 | function borderRight($row,$col,$sheet=0) {
512 | return $this->xfProperty($row,$col,$sheet,'borderRight');
513 | }
514 | function borderTop($row,$col,$sheet=0) {
515 | return $this->xfProperty($row,$col,$sheet,'borderTop');
516 | }
517 | function borderBottom($row,$col,$sheet=0) {
518 | return $this->xfProperty($row,$col,$sheet,'borderBottom');
519 | }
520 | function borderLeftColor($row,$col,$sheet=0) {
521 | return $this->colors[$this->xfProperty($row,$col,$sheet,'borderLeftColor')];
522 | }
523 | function borderRightColor($row,$col,$sheet=0) {
524 | return $this->colors[$this->xfProperty($row,$col,$sheet,'borderRightColor')];
525 | }
526 | function borderTopColor($row,$col,$sheet=0) {
527 | return $this->colors[$this->xfProperty($row,$col,$sheet,'borderTopColor')];
528 | }
529 | function borderBottomColor($row,$col,$sheet=0) {
530 | return $this->colors[$this->xfProperty($row,$col,$sheet,'borderBottomColor')];
531 | }
532 |
533 | // FONT PROPERTIES
534 | // ===============
535 | function fontRecord($row,$col,$sheet=0) {
536 | $xfRecord = $this->xfRecord($row,$col,$sheet);
537 | if ($xfRecord!=null) {
538 | $font = $xfRecord['fontIndex'];
539 | if ($font!=null) {
540 | return $this->fontRecords[$font];
541 | }
542 | }
543 | return null;
544 | }
545 | function fontProperty($row,$col,$sheet=0,$prop) {
546 | $font = $this->fontRecord($row,$col,$sheet);
547 | if ($font!=null) {
548 | return $font[$prop];
549 | }
550 | return false;
551 | }
552 | function fontIndex($row,$col,$sheet=0) {
553 | return $this->xfProperty($row,$col,$sheet,'fontIndex');
554 | }
555 | function color($row,$col,$sheet=0) {
556 | $formatColor = $this->formatColor($row,$col,$sheet);
557 | if ($formatColor!="") {
558 | return $formatColor;
559 | }
560 | $ci = $this->fontProperty($row,$col,$sheet,'color');
561 | return $this->rawColor($ci);
562 | }
563 | function rawColor($ci) {
564 | if (($ci <> 0x7FFF) && ($ci <> '')) {
565 | return $this->colors[$ci];
566 | }
567 | return "";
568 | }
569 | function bold($row,$col,$sheet=0) {
570 | return $this->fontProperty($row,$col,$sheet,'bold');
571 | }
572 | function italic($row,$col,$sheet=0) {
573 | return $this->fontProperty($row,$col,$sheet,'italic');
574 | }
575 | function underline($row,$col,$sheet=0) {
576 | return $this->fontProperty($row,$col,$sheet,'under');
577 | }
578 | function height($row,$col,$sheet=0) {
579 | return $this->fontProperty($row,$col,$sheet,'height');
580 | }
581 | function font($row,$col,$sheet=0) {
582 | return $this->fontProperty($row,$col,$sheet,'font');
583 | }
584 |
585 | // DUMP AN HTML TABLE OF THE ENTIRE XLS DATA
586 | // =========================================
587 | function dump($row_numbers=false,$col_letters=false,$sheet=0,$table_class='excel') {
588 | $out = "";
589 | if ($col_letters) {
590 | $out .= "\n\t";
591 | if ($row_numbers) {
592 | $out .= "\n\t\t  | ";
593 | }
594 | for($i=1;$i<=$this->colcount($sheet);$i++) {
595 | $style = "width:" . ($this->colwidth($i,$sheet)*1) . "px;";
596 | if ($this->colhidden($i,$sheet)) {
597 | $style .= "display:none;";
598 | }
599 | $out .= "\n\t\t" . strtoupper($this->colindexes[$i]) . " | ";
600 | }
601 | $out .= "
\n";
602 | }
603 |
604 | $out .= "\n";
605 | for($row=1;$row<=$this->rowcount($sheet);$row++) {
606 | $rowheight = $this->rowheight($row,$sheet);
607 | $style = "height:" . ($rowheight*(4/3)) . "px;";
608 | if ($this->rowhidden($row,$sheet)) {
609 | $style .= "display:none;";
610 | }
611 | $out .= "\n\t";
612 | if ($row_numbers) {
613 | $out .= "\n\t\t$row | ";
614 | }
615 | for($col=1;$col<=$this->colcount($sheet);$col++) {
616 | // Account for Rowspans/Colspans
617 | $rowspan = $this->rowspan($row,$col,$sheet);
618 | $colspan = $this->colspan($row,$col,$sheet);
619 | for($i=0;$i<$rowspan;$i++) {
620 | for($j=0;$j<$colspan;$j++) {
621 | if ($i>0 || $j>0) {
622 | $this->sheets[$sheet]['cellsInfo'][$row+$i][$col+$j]['dontprint']=1;
623 | }
624 | }
625 | }
626 | if(!$this->sheets[$sheet]['cellsInfo'][$row][$col]['dontprint']) {
627 | $style = $this->style($row,$col,$sheet);
628 | if ($this->colhidden($col,$sheet)) {
629 | $style .= "display:none;";
630 | }
631 | $out .= "\n\t\t 1?" colspan=$colspan":"") . ($rowspan > 1?" rowspan=$rowspan":"") . ">";
632 | $val = $this->val($row,$col,$sheet);
633 | if ($val=='') { $val=" "; }
634 | else {
635 | $val = htmlentities($val);
636 | $link = $this->hyperlink($row,$col,$sheet);
637 | if ($link!='') {
638 | $val = "$val";
639 | }
640 | }
641 | $out .= "".nl2br($val)."";
642 | $out .= " | ";
643 | }
644 | }
645 | $out .= "
\n";
646 | }
647 | $out .= "
";
648 | return $out;
649 | }
650 |
651 | // --------------
652 | // END PUBLIC API
653 |
654 |
655 | var $boundsheets = array();
656 | var $formatRecords = array();
657 | var $fontRecords = array();
658 | var $xfRecords = array();
659 | var $colInfo = array();
660 | var $rowInfo = array();
661 |
662 | var $sst = array();
663 | var $sheets = array();
664 |
665 | var $data;
666 | var $_ole;
667 | var $_defaultEncoding = "UTF-8";
668 | var $_defaultFormat = SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT;
669 | var $_columnsFormat = array();
670 | var $_rowoffset = 1;
671 | var $_coloffset = 1;
672 |
673 | /**
674 | * List of default date formats used by Excel
675 | */
676 | var $dateFormats = array (
677 | 0xe => "m/d/Y",
678 | 0xf => "M-d-Y",
679 | 0x10 => "d-M",
680 | 0x11 => "M-Y",
681 | 0x12 => "h:i a",
682 | 0x13 => "h:i:s a",
683 | 0x14 => "H:i",
684 | 0x15 => "H:i:s",
685 | 0x16 => "d/m/Y H:i",
686 | 0x2d => "i:s",
687 | 0x2e => "H:i:s",
688 | 0x2f => "i:s.S"
689 | );
690 |
691 | /**
692 | * Default number formats used by Excel
693 | */
694 | var $numberFormats = array(
695 | 0x1 => "0",
696 | 0x2 => "0.00",
697 | 0x3 => "#,##0",
698 | 0x4 => "#,##0.00",
699 | 0x5 => "\$#,##0;(\$#,##0)",
700 | 0x6 => "\$#,##0;[Red](\$#,##0)",
701 | 0x7 => "\$#,##0.00;(\$#,##0.00)",
702 | 0x8 => "\$#,##0.00;[Red](\$#,##0.00)",
703 | 0x9 => "0%",
704 | 0xa => "0.00%",
705 | 0xb => "0.00E+00",
706 | 0x25 => "#,##0;(#,##0)",
707 | 0x26 => "#,##0;[Red](#,##0)",
708 | 0x27 => "#,##0.00;(#,##0.00)",
709 | 0x28 => "#,##0.00;[Red](#,##0.00)",
710 | 0x29 => "#,##0;(#,##0)", // Not exactly
711 | 0x2a => "\$#,##0;(\$#,##0)", // Not exactly
712 | 0x2b => "#,##0.00;(#,##0.00)", // Not exactly
713 | 0x2c => "\$#,##0.00;(\$#,##0.00)", // Not exactly
714 | 0x30 => "##0.0E+0"
715 | );
716 |
717 | var $colors = Array(
718 | 0x00 => "#000000",
719 | 0x01 => "#FFFFFF",
720 | 0x02 => "#FF0000",
721 | 0x03 => "#00FF00",
722 | 0x04 => "#0000FF",
723 | 0x05 => "#FFFF00",
724 | 0x06 => "#FF00FF",
725 | 0x07 => "#00FFFF",
726 | 0x08 => "#000000",
727 | 0x09 => "#FFFFFF",
728 | 0x0A => "#FF0000",
729 | 0x0B => "#00FF00",
730 | 0x0C => "#0000FF",
731 | 0x0D => "#FFFF00",
732 | 0x0E => "#FF00FF",
733 | 0x0F => "#00FFFF",
734 | 0x10 => "#800000",
735 | 0x11 => "#008000",
736 | 0x12 => "#000080",
737 | 0x13 => "#808000",
738 | 0x14 => "#800080",
739 | 0x15 => "#008080",
740 | 0x16 => "#C0C0C0",
741 | 0x17 => "#808080",
742 | 0x18 => "#9999FF",
743 | 0x19 => "#993366",
744 | 0x1A => "#FFFFCC",
745 | 0x1B => "#CCFFFF",
746 | 0x1C => "#660066",
747 | 0x1D => "#FF8080",
748 | 0x1E => "#0066CC",
749 | 0x1F => "#CCCCFF",
750 | 0x20 => "#000080",
751 | 0x21 => "#FF00FF",
752 | 0x22 => "#FFFF00",
753 | 0x23 => "#00FFFF",
754 | 0x24 => "#800080",
755 | 0x25 => "#800000",
756 | 0x26 => "#008080",
757 | 0x27 => "#0000FF",
758 | 0x28 => "#00CCFF",
759 | 0x29 => "#CCFFFF",
760 | 0x2A => "#CCFFCC",
761 | 0x2B => "#FFFF99",
762 | 0x2C => "#99CCFF",
763 | 0x2D => "#FF99CC",
764 | 0x2E => "#CC99FF",
765 | 0x2F => "#FFCC99",
766 | 0x30 => "#3366FF",
767 | 0x31 => "#33CCCC",
768 | 0x32 => "#99CC00",
769 | 0x33 => "#FFCC00",
770 | 0x34 => "#FF9900",
771 | 0x35 => "#FF6600",
772 | 0x36 => "#666699",
773 | 0x37 => "#969696",
774 | 0x38 => "#003366",
775 | 0x39 => "#339966",
776 | 0x3A => "#003300",
777 | 0x3B => "#333300",
778 | 0x3C => "#993300",
779 | 0x3D => "#993366",
780 | 0x3E => "#333399",
781 | 0x3F => "#333333",
782 | 0x40 => "#000000",
783 | 0x41 => "#FFFFFF",
784 |
785 | 0x43 => "#000000",
786 | 0x4D => "#000000",
787 | 0x4E => "#FFFFFF",
788 | 0x4F => "#000000",
789 | 0x50 => "#FFFFFF",
790 | 0x51 => "#000000",
791 |
792 | 0x7FFF => "#000000"
793 | );
794 |
795 | var $lineStyles = array(
796 | 0x00 => "",
797 | 0x01 => "Thin",
798 | 0x02 => "Medium",
799 | 0x03 => "Dashed",
800 | 0x04 => "Dotted",
801 | 0x05 => "Thick",
802 | 0x06 => "Double",
803 | 0x07 => "Hair",
804 | 0x08 => "Medium dashed",
805 | 0x09 => "Thin dash-dotted",
806 | 0x0A => "Medium dash-dotted",
807 | 0x0B => "Thin dash-dot-dotted",
808 | 0x0C => "Medium dash-dot-dotted",
809 | 0x0D => "Slanted medium dash-dotted"
810 | );
811 |
812 | var $lineStylesCss = array(
813 | "Thin" => "1px solid",
814 | "Medium" => "2px solid",
815 | "Dashed" => "1px dashed",
816 | "Dotted" => "1px dotted",
817 | "Thick" => "3px solid",
818 | "Double" => "double",
819 | "Hair" => "1px solid",
820 | "Medium dashed" => "2px dashed",
821 | "Thin dash-dotted" => "1px dashed",
822 | "Medium dash-dotted" => "2px dashed",
823 | "Thin dash-dot-dotted" => "1px dashed",
824 | "Medium dash-dot-dotted" => "2px dashed",
825 | "Slanted medium dash-dotte" => "2px dashed"
826 | );
827 |
828 | function read16bitstring($data, $start) {
829 | $len = 0;
830 | while (ord($data[$start + $len]) + ord($data[$start + $len + 1]) > 0) $len++;
831 | return substr($data, $start, $len);
832 | }
833 |
834 | // ADDED by Matt Kruse for better formatting
835 | function _format_value($format,$num,$f) {
836 | // 49==TEXT format
837 | // http://code.google.com/p/php-excel-reader/issues/detail?id=7
838 | if ( (!$f && $format=="%s") || ($f==49) || ($format=="GENERAL") ) {
839 | return array('string'=>$num, 'formatColor'=>null);
840 | }
841 |
842 | // Custom pattern can be POSITIVE;NEGATIVE;ZERO
843 | // The "text" option as 4th parameter is not handled
844 | $parts = explode(";",$format);
845 | $pattern = $parts[0];
846 | // Negative pattern
847 | if (count($parts)>2 && $num==0) {
848 | $pattern = $parts[2];
849 | }
850 | // Zero pattern
851 | if (count($parts)>1 && $num<0) {
852 | $pattern = $parts[1];
853 | $num = abs($num);
854 | }
855 |
856 | $color = "";
857 | $matches = array();
858 | $color_regex = "/^\[(BLACK|BLUE|CYAN|GREEN|MAGENTA|RED|WHITE|YELLOW)\]/i";
859 | if (preg_match($color_regex,$pattern,$matches)) {
860 | $color = strtolower($matches[1]);
861 | $pattern = preg_replace($color_regex,"",$pattern);
862 | }
863 |
864 | // In Excel formats, "_" is used to add spacing, which we can't do in HTML
865 | $pattern = preg_replace("/_./","",$pattern);
866 |
867 | // Some non-number characters are escaped with \, which we don't need
868 | $pattern = preg_replace("/\\\/","",$pattern);
869 |
870 | // Some non-number strings are quoted, so we'll get rid of the quotes
871 | $pattern = preg_replace("/\"/","",$pattern);
872 |
873 | // TEMPORARY - Convert # to 0
874 | $pattern = preg_replace("/\#/","0",$pattern);
875 |
876 | // Find out if we need comma formatting
877 | $has_commas = preg_match("/,/",$pattern);
878 | if ($has_commas) {
879 | $pattern = preg_replace("/,/","",$pattern);
880 | }
881 |
882 | // Handle Percentages
883 | if (preg_match("/\d(\%)([^\%]|$)/",$pattern,$matches)) {
884 | $num = $num * 100;
885 | $pattern = preg_replace("/(\d)(\%)([^\%]|$)/","$1%$3",$pattern);
886 | }
887 |
888 | // Handle the number itself
889 | $number_regex = "/(\d+)(\.?)(\d*)/";
890 | if (preg_match($number_regex,$pattern,$matches)) {
891 | $left = $matches[1];
892 | $dec = $matches[2];
893 | $right = $matches[3];
894 | if ($has_commas) {
895 | $formatted = number_format($num,strlen($right));
896 | }
897 | else {
898 | $sprintf_pattern = "%1.".strlen($right)."f";
899 | $formatted = sprintf($sprintf_pattern, $num);
900 | }
901 | $pattern = preg_replace($number_regex, $formatted, $pattern);
902 | }
903 |
904 | return array(
905 | 'string'=>$pattern,
906 | 'formatColor'=>$color
907 | );
908 | }
909 |
910 | /**
911 | * Constructor
912 | *
913 | * Some basic initialisation
914 | */
915 | function Spreadsheet_Excel_Reader($file='',$store_extended_info=true,$outputEncoding='') {
916 | $this->_ole = new OLERead();
917 | $this->setUTFEncoder('iconv');
918 | if ($outputEncoding != '') {
919 | $this->setOutputEncoding($outputEncoding);
920 | }
921 | for ($i=1; $i<245; $i++) {
922 | $name = strtolower(( (($i-1)/26>=1)?chr(($i-1)/26+64):'') . chr(($i-1)%26+65));
923 | $this->colnames[$name] = $i;
924 | $this->colindexes[$i] = $name;
925 | }
926 | $this->store_extended_info = $store_extended_info;
927 | if ($file!="") {
928 | $this->read($file);
929 | }
930 | }
931 |
932 | /**
933 | * Set the encoding method
934 | */
935 | function setOutputEncoding($encoding) {
936 | $this->_defaultEncoding = $encoding;
937 | }
938 |
939 | /**
940 | * $encoder = 'iconv' or 'mb'
941 | * set iconv if you would like use 'iconv' for encode UTF-16LE to your encoding
942 | * set mb if you would like use 'mb_convert_encoding' for encode UTF-16LE to your encoding
943 | */
944 | function setUTFEncoder($encoder = 'iconv') {
945 | $this->_encoderFunction = '';
946 | if ($encoder == 'iconv') {
947 | $this->_encoderFunction = function_exists('iconv') ? 'iconv' : '';
948 | } elseif ($encoder == 'mb') {
949 | $this->_encoderFunction = function_exists('mb_convert_encoding') ? 'mb_convert_encoding' : '';
950 | }
951 | }
952 |
953 | function setRowColOffset($iOffset) {
954 | $this->_rowoffset = $iOffset;
955 | $this->_coloffset = $iOffset;
956 | }
957 |
958 | /**
959 | * Set the default number format
960 | */
961 | function setDefaultFormat($sFormat) {
962 | $this->_defaultFormat = $sFormat;
963 | }
964 |
965 | /**
966 | * Force a column to use a certain format
967 | */
968 | function setColumnFormat($column, $sFormat) {
969 | $this->_columnsFormat[$column] = $sFormat;
970 | }
971 |
972 | /**
973 | * Read the spreadsheet file using OLE, then parse
974 | */
975 | function read($sFileName) {
976 | $res = $this->_ole->read($sFileName);
977 |
978 | // oops, something goes wrong (Darko Miljanovic)
979 | if($res === false) {
980 | // check error code
981 | if($this->_ole->error == 1) {
982 | // bad file
983 | die('The filename ' . $sFileName . ' is not readable');
984 | }
985 | // check other error codes here (eg bad fileformat, etc...)
986 | }
987 | $this->data = $this->_ole->getWorkBook();
988 | $this->_parse();
989 | }
990 |
991 | /**
992 | * Parse a workbook
993 | *
994 | * @access private
995 | * @return bool
996 | */
997 | function _parse() {
998 | $pos = 0;
999 | $data = $this->data;
1000 |
1001 | $code = v($data,$pos);
1002 | $length = v($data,$pos+2);
1003 | $version = v($data,$pos+4);
1004 | $substreamType = v($data,$pos+6);
1005 |
1006 | $this->version = $version;
1007 |
1008 | if (($version != SPREADSHEET_EXCEL_READER_BIFF8) &&
1009 | ($version != SPREADSHEET_EXCEL_READER_BIFF7)) {
1010 | return false;
1011 | }
1012 |
1013 | if ($substreamType != SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS){
1014 | return false;
1015 | }
1016 |
1017 | $pos += $length + 4;
1018 |
1019 | $code = v($data,$pos);
1020 | $length = v($data,$pos+2);
1021 |
1022 | while ($code != SPREADSHEET_EXCEL_READER_TYPE_EOF) {
1023 | switch ($code) {
1024 | case SPREADSHEET_EXCEL_READER_TYPE_SST:
1025 | $spos = $pos + 4;
1026 | $limitpos = $spos + $length;
1027 | $uniqueStrings = $this->_GetInt4d($data, $spos+4);
1028 | $spos += 8;
1029 | for ($i = 0; $i < $uniqueStrings; $i++) {
1030 | // Read in the number of characters
1031 | if ($spos == $limitpos) {
1032 | $opcode = v($data,$spos);
1033 | $conlength = v($data,$spos+2);
1034 | if ($opcode != 0x3c) {
1035 | return -1;
1036 | }
1037 | $spos += 4;
1038 | $limitpos = $spos + $conlength;
1039 | }
1040 | $numChars = ord($data[$spos]) | (ord($data[$spos+1]) << 8);
1041 | $spos += 2;
1042 | $optionFlags = ord($data[$spos]);
1043 | $spos++;
1044 | $asciiEncoding = (($optionFlags & 0x01) == 0) ;
1045 | $extendedString = ( ($optionFlags & 0x04) != 0);
1046 |
1047 | // See if string contains formatting information
1048 | $richString = ( ($optionFlags & 0x08) != 0);
1049 |
1050 | if ($richString) {
1051 | // Read in the crun
1052 | $formattingRuns = v($data,$spos);
1053 | $spos += 2;
1054 | }
1055 |
1056 | if ($extendedString) {
1057 | // Read in cchExtRst
1058 | $extendedRunLength = $this->_GetInt4d($data, $spos);
1059 | $spos += 4;
1060 | }
1061 |
1062 | $len = ($asciiEncoding)? $numChars : $numChars*2;
1063 | if ($spos + $len < $limitpos) {
1064 | $retstr = substr($data, $spos, $len);
1065 | $spos += $len;
1066 | }
1067 | else{
1068 | // found countinue
1069 | $retstr = substr($data, $spos, $limitpos - $spos);
1070 | $bytesRead = $limitpos - $spos;
1071 | $charsLeft = $numChars - (($asciiEncoding) ? $bytesRead : ($bytesRead / 2));
1072 | $spos = $limitpos;
1073 |
1074 | while ($charsLeft > 0){
1075 | $opcode = v($data,$spos);
1076 | $conlength = v($data,$spos+2);
1077 | if ($opcode != 0x3c) {
1078 | return -1;
1079 | }
1080 | $spos += 4;
1081 | $limitpos = $spos + $conlength;
1082 | $option = ord($data[$spos]);
1083 | $spos += 1;
1084 | if ($asciiEncoding && ($option == 0)) {
1085 | $len = min($charsLeft, $limitpos - $spos); // min($charsLeft, $conlength);
1086 | $retstr .= substr($data, $spos, $len);
1087 | $charsLeft -= $len;
1088 | $asciiEncoding = true;
1089 | }
1090 | elseif (!$asciiEncoding && ($option != 0)) {
1091 | $len = min($charsLeft * 2, $limitpos - $spos); // min($charsLeft, $conlength);
1092 | $retstr .= substr($data, $spos, $len);
1093 | $charsLeft -= $len/2;
1094 | $asciiEncoding = false;
1095 | }
1096 | elseif (!$asciiEncoding && ($option == 0)) {
1097 | // Bummer - the string starts off as Unicode, but after the
1098 | // continuation it is in straightforward ASCII encoding
1099 | $len = min($charsLeft, $limitpos - $spos); // min($charsLeft, $conlength);
1100 | for ($j = 0; $j < $len; $j++) {
1101 | $retstr .= $data[$spos + $j].chr(0);
1102 | }
1103 | $charsLeft -= $len;
1104 | $asciiEncoding = false;
1105 | }
1106 | else{
1107 | $newstr = '';
1108 | for ($j = 0; $j < strlen($retstr); $j++) {
1109 | $newstr = $retstr[$j].chr(0);
1110 | }
1111 | $retstr = $newstr;
1112 | $len = min($charsLeft * 2, $limitpos - $spos); // min($charsLeft, $conlength);
1113 | $retstr .= substr($data, $spos, $len);
1114 | $charsLeft -= $len/2;
1115 | $asciiEncoding = false;
1116 | }
1117 | $spos += $len;
1118 | }
1119 | }
1120 | $retstr = ($asciiEncoding) ? $retstr : $this->_encodeUTF16($retstr);
1121 |
1122 | if ($richString){
1123 | $spos += 4 * $formattingRuns;
1124 | }
1125 |
1126 | // For extended strings, skip over the extended string data
1127 | if ($extendedString) {
1128 | $spos += $extendedRunLength;
1129 | }
1130 | $this->sst[]=$retstr;
1131 | }
1132 | break;
1133 | case SPREADSHEET_EXCEL_READER_TYPE_FILEPASS:
1134 | return false;
1135 | break;
1136 | case SPREADSHEET_EXCEL_READER_TYPE_NAME:
1137 | break;
1138 | case SPREADSHEET_EXCEL_READER_TYPE_FORMAT:
1139 | $indexCode = v($data,$pos+4);
1140 | if ($version == SPREADSHEET_EXCEL_READER_BIFF8) {
1141 | $numchars = v($data,$pos+6);
1142 | if (ord($data[$pos+8]) == 0){
1143 | $formatString = substr($data, $pos+9, $numchars);
1144 | } else {
1145 | $formatString = substr($data, $pos+9, $numchars*2);
1146 | }
1147 | } else {
1148 | $numchars = ord($data[$pos+6]);
1149 | $formatString = substr($data, $pos+7, $numchars*2);
1150 | }
1151 | $this->formatRecords[$indexCode] = $formatString;
1152 | break;
1153 | case SPREADSHEET_EXCEL_READER_TYPE_FONT:
1154 | $height = v($data,$pos+4);
1155 | $option = v($data,$pos+6);
1156 | $color = v($data,$pos+8);
1157 | $weight = v($data,$pos+10);
1158 | $under = ord($data[$pos+14]);
1159 | $font = "";
1160 | // Font name
1161 | $numchars = ord($data[$pos+18]);
1162 | if ((ord($data[$pos+19]) & 1) == 0){
1163 | $font = substr($data, $pos+20, $numchars);
1164 | } else {
1165 | $font = substr($data, $pos+20, $numchars*2);
1166 | $font = $this->_encodeUTF16($font);
1167 | }
1168 | $this->fontRecords[] = array(
1169 | 'height' => $height / 20,
1170 | 'italic' => !!($option & 2),
1171 | 'color' => $color,
1172 | 'under' => !($under==0),
1173 | 'bold' => ($weight==700),
1174 | 'font' => $font,
1175 | 'raw' => $this->dumpHexData($data, $pos+3, $length)
1176 | );
1177 | break;
1178 |
1179 | case SPREADSHEET_EXCEL_READER_TYPE_PALETTE:
1180 | $colors = ord($data[$pos+4]) | ord($data[$pos+5]) << 8;
1181 | for ($coli = 0; $coli < $colors; $coli++) {
1182 | $colOff = $pos + 2 + ($coli * 4);
1183 | $colr = ord($data[$colOff]);
1184 | $colg = ord($data[$colOff+1]);
1185 | $colb = ord($data[$colOff+2]);
1186 | $this->colors[0x07 + $coli] = '#' . $this->myhex($colr) . $this->myhex($colg) . $this->myhex($colb);
1187 | }
1188 | break;
1189 |
1190 | case SPREADSHEET_EXCEL_READER_TYPE_XF:
1191 | $fontIndexCode = (ord($data[$pos+4]) | ord($data[$pos+5]) << 8) - 1;
1192 | $fontIndexCode = max(0,$fontIndexCode);
1193 | $indexCode = ord($data[$pos+6]) | ord($data[$pos+7]) << 8;
1194 | $alignbit = ord($data[$pos+10]) & 3;
1195 | $bgi = (ord($data[$pos+22]) | ord($data[$pos+23]) << 8) & 0x3FFF;
1196 | $bgcolor = ($bgi & 0x7F);
1197 | // $bgcolor = ($bgi & 0x3f80) >> 7;
1198 | $align = "";
1199 | if ($alignbit==3) { $align="right"; }
1200 | if ($alignbit==2) { $align="center"; }
1201 |
1202 | $fillPattern = (ord($data[$pos+21]) & 0xFC) >> 2;
1203 | if ($fillPattern == 0) {
1204 | $bgcolor = "";
1205 | }
1206 |
1207 | $xf = array();
1208 | $xf['formatIndex'] = $indexCode;
1209 | $xf['align'] = $align;
1210 | $xf['fontIndex'] = $fontIndexCode;
1211 | $xf['bgColor'] = $bgcolor;
1212 | $xf['fillPattern'] = $fillPattern;
1213 |
1214 | $border = ord($data[$pos+14]) | (ord($data[$pos+15]) << 8) | (ord($data[$pos+16]) << 16) | (ord($data[$pos+17]) << 24);
1215 | $xf['borderLeft'] = $this->lineStyles[($border & 0xF)];
1216 | $xf['borderRight'] = $this->lineStyles[($border & 0xF0) >> 4];
1217 | $xf['borderTop'] = $this->lineStyles[($border & 0xF00) >> 8];
1218 | $xf['borderBottom'] = $this->lineStyles[($border & 0xF000) >> 12];
1219 |
1220 | $xf['borderLeftColor'] = ($border & 0x7F0000) >> 16;
1221 | $xf['borderRightColor'] = ($border & 0x3F800000) >> 23;
1222 | $border = (ord($data[$pos+18]) | ord($data[$pos+19]) << 8);
1223 |
1224 | $xf['borderTopColor'] = ($border & 0x7F);
1225 | $xf['borderBottomColor'] = ($border & 0x3F80) >> 7;
1226 |
1227 | if (array_key_exists($indexCode, $this->dateFormats)) {
1228 | $xf['type'] = 'date';
1229 | $xf['format'] = $this->dateFormats[$indexCode];
1230 | if ($align=='') { $xf['align'] = 'right'; }
1231 | }elseif (array_key_exists($indexCode, $this->numberFormats)) {
1232 | $xf['type'] = 'number';
1233 | $xf['format'] = $this->numberFormats[$indexCode];
1234 | if ($align=='') { $xf['align'] = 'right'; }
1235 | }else{
1236 | $isdate = FALSE;
1237 | $formatstr = '';
1238 | if ($indexCode > 0){
1239 | if (isset($this->formatRecords[$indexCode]))
1240 | $formatstr = $this->formatRecords[$indexCode];
1241 | if ($formatstr!="") {
1242 | $tmp = preg_replace("/\;.*/","",$formatstr);
1243 | $tmp = preg_replace("/^\[[^\]]*\]/","",$tmp);
1244 | if (preg_match("/[^hmsday\/\-:\s\\\,AMP]/i", $tmp) == 0) { // found day and time format
1245 | $isdate = TRUE;
1246 | $formatstr = $tmp;
1247 | $formatstr = str_replace(array('AM/PM','mmmm','mmm'), array('a','F','M'), $formatstr);
1248 | // m/mm are used for both minutes and months - oh SNAP!
1249 | // This mess tries to fix for that.
1250 | // 'm' == minutes only if following h/hh or preceding s/ss
1251 | $formatstr = preg_replace("/(h:?)mm?/","$1i", $formatstr);
1252 | $formatstr = preg_replace("/mm?(:?s)/","i$1", $formatstr);
1253 | // A single 'm' = n in PHP
1254 | $formatstr = preg_replace("/(^|[^m])m([^m]|$)/", '$1n$2', $formatstr);
1255 | $formatstr = preg_replace("/(^|[^m])m([^m]|$)/", '$1n$2', $formatstr);
1256 | // else it's months
1257 | $formatstr = str_replace('mm', 'm', $formatstr);
1258 | // Convert single 'd' to 'j'
1259 | $formatstr = preg_replace("/(^|[^d])d([^d]|$)/", '$1j$2', $formatstr);
1260 | $formatstr = str_replace(array('dddd','ddd','dd','yyyy','yy','hh','h'), array('l','D','d','Y','y','H','g'), $formatstr);
1261 | $formatstr = preg_replace("/ss?/", 's', $formatstr);
1262 | }
1263 | }
1264 | }
1265 | if ($isdate){
1266 | $xf['type'] = 'date';
1267 | $xf['format'] = $formatstr;
1268 | if ($align=='') { $xf['align'] = 'right'; }
1269 | }else{
1270 | // If the format string has a 0 or # in it, we'll assume it's a number
1271 | if (preg_match("/[0#]/", $formatstr)) {
1272 | $xf['type'] = 'number';
1273 | if ($align=='') { $xf['align']='right'; }
1274 | }
1275 | else {
1276 | $xf['type'] = 'other';
1277 | }
1278 | $xf['format'] = $formatstr;
1279 | $xf['code'] = $indexCode;
1280 | }
1281 | }
1282 | $this->xfRecords[] = $xf;
1283 | break;
1284 | case SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR:
1285 | $this->nineteenFour = (ord($data[$pos+4]) == 1);
1286 | break;
1287 | case SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET:
1288 | $rec_offset = $this->_GetInt4d($data, $pos+4);
1289 | $rec_typeFlag = ord($data[$pos+8]);
1290 | $rec_visibilityFlag = ord($data[$pos+9]);
1291 | $rec_length = ord($data[$pos+10]);
1292 |
1293 | if ($version == SPREADSHEET_EXCEL_READER_BIFF8){
1294 | $chartype = ord($data[$pos+11]);
1295 | if ($chartype == 0){
1296 | $rec_name = substr($data, $pos+12, $rec_length);
1297 | } else {
1298 | $rec_name = $this->_encodeUTF16(substr($data, $pos+12, $rec_length*2));
1299 | }
1300 | }elseif ($version == SPREADSHEET_EXCEL_READER_BIFF7){
1301 | $rec_name = substr($data, $pos+11, $rec_length);
1302 | }
1303 | $this->boundsheets[] = array('name'=>$rec_name,'offset'=>$rec_offset);
1304 | break;
1305 |
1306 | }
1307 |
1308 | $pos += $length + 4;
1309 | $code = ord($data[$pos]) | ord($data[$pos+1])<<8;
1310 | $length = ord($data[$pos+2]) | ord($data[$pos+3])<<8;
1311 | }
1312 |
1313 | foreach ($this->boundsheets as $key=>$val){
1314 | $this->sn = $key;
1315 | $this->_parsesheet($val['offset']);
1316 | }
1317 | return true;
1318 | }
1319 |
1320 | /**
1321 | * Parse a worksheet
1322 | */
1323 | function _parsesheet($spos) {
1324 | $cont = true;
1325 | $data = $this->data;
1326 | // read BOF
1327 | $code = ord($data[$spos]) | ord($data[$spos+1])<<8;
1328 | $length = ord($data[$spos+2]) | ord($data[$spos+3])<<8;
1329 |
1330 | $version = ord($data[$spos + 4]) | ord($data[$spos + 5])<<8;
1331 | $substreamType = ord($data[$spos + 6]) | ord($data[$spos + 7])<<8;
1332 |
1333 | if (($version != SPREADSHEET_EXCEL_READER_BIFF8) && ($version != SPREADSHEET_EXCEL_READER_BIFF7)) {
1334 | return -1;
1335 | }
1336 |
1337 | if ($substreamType != SPREADSHEET_EXCEL_READER_WORKSHEET){
1338 | return -2;
1339 | }
1340 | $spos += $length + 4;
1341 | while($cont) {
1342 | $lowcode = ord($data[$spos]);
1343 | if ($lowcode == SPREADSHEET_EXCEL_READER_TYPE_EOF) break;
1344 | $code = $lowcode | ord($data[$spos+1])<<8;
1345 | $length = ord($data[$spos+2]) | ord($data[$spos+3])<<8;
1346 | $spos += 4;
1347 | $this->sheets[$this->sn]['maxrow'] = $this->_rowoffset - 1;
1348 | $this->sheets[$this->sn]['maxcol'] = $this->_coloffset - 1;
1349 | unset($this->rectype);
1350 | switch ($code) {
1351 | case SPREADSHEET_EXCEL_READER_TYPE_DIMENSION:
1352 | if (!isset($this->numRows)) {
1353 | if (($length == 10) || ($version == SPREADSHEET_EXCEL_READER_BIFF7)){
1354 | $this->sheets[$this->sn]['numRows'] = ord($data[$spos+2]) | ord($data[$spos+3]) << 8;
1355 | $this->sheets[$this->sn]['numCols'] = ord($data[$spos+6]) | ord($data[$spos+7]) << 8;
1356 | } else {
1357 | $this->sheets[$this->sn]['numRows'] = ord($data[$spos+4]) | ord($data[$spos+5]) << 8;
1358 | $this->sheets[$this->sn]['numCols'] = ord($data[$spos+10]) | ord($data[$spos+11]) << 8;
1359 | }
1360 | }
1361 | break;
1362 | case SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS:
1363 | $cellRanges = ord($data[$spos]) | ord($data[$spos+1])<<8;
1364 | for ($i = 0; $i < $cellRanges; $i++) {
1365 | $fr = ord($data[$spos + 8*$i + 2]) | ord($data[$spos + 8*$i + 3])<<8;
1366 | $lr = ord($data[$spos + 8*$i + 4]) | ord($data[$spos + 8*$i + 5])<<8;
1367 | $fc = ord($data[$spos + 8*$i + 6]) | ord($data[$spos + 8*$i + 7])<<8;
1368 | $lc = ord($data[$spos + 8*$i + 8]) | ord($data[$spos + 8*$i + 9])<<8;
1369 | if ($lr - $fr > 0) {
1370 | $this->sheets[$this->sn]['cellsInfo'][$fr+1][$fc+1]['rowspan'] = $lr - $fr + 1;
1371 | }
1372 | if ($lc - $fc > 0) {
1373 | $this->sheets[$this->sn]['cellsInfo'][$fr+1][$fc+1]['colspan'] = $lc - $fc + 1;
1374 | }
1375 | }
1376 | break;
1377 | case SPREADSHEET_EXCEL_READER_TYPE_RK:
1378 | case SPREADSHEET_EXCEL_READER_TYPE_RK2:
1379 | $row = ord($data[$spos]) | ord($data[$spos+1])<<8;
1380 | $column = ord($data[$spos+2]) | ord($data[$spos+3])<<8;
1381 | $rknum = $this->_GetInt4d($data, $spos + 6);
1382 | $numValue = $this->_GetIEEE754($rknum);
1383 | $info = $this->_getCellDetails($spos,$numValue,$column);
1384 | $this->addcell($row, $column, $info['string'],$info);
1385 | break;
1386 | case SPREADSHEET_EXCEL_READER_TYPE_LABELSST:
1387 | $row = ord($data[$spos]) | ord($data[$spos+1])<<8;
1388 | $column = ord($data[$spos+2]) | ord($data[$spos+3])<<8;
1389 | $xfindex = ord($data[$spos+4]) | ord($data[$spos+5])<<8;
1390 | $index = $this->_GetInt4d($data, $spos + 6);
1391 | $this->addcell($row, $column, $this->sst[$index], array('xfIndex'=>$xfindex) );
1392 | break;
1393 | case SPREADSHEET_EXCEL_READER_TYPE_MULRK:
1394 | $row = ord($data[$spos]) | ord($data[$spos+1])<<8;
1395 | $colFirst = ord($data[$spos+2]) | ord($data[$spos+3])<<8;
1396 | $colLast = ord($data[$spos + $length - 2]) | ord($data[$spos + $length - 1])<<8;
1397 | $columns = $colLast - $colFirst + 1;
1398 | $tmppos = $spos+4;
1399 | for ($i = 0; $i < $columns; $i++) {
1400 | $numValue = $this->_GetIEEE754($this->_GetInt4d($data, $tmppos + 2));
1401 | $info = $this->_getCellDetails($tmppos-4,$numValue,$colFirst + $i + 1);
1402 | $tmppos += 6;
1403 | $this->addcell($row, $colFirst + $i, $info['string'], $info);
1404 | }
1405 | break;
1406 | case SPREADSHEET_EXCEL_READER_TYPE_NUMBER:
1407 | $row = ord($data[$spos]) | ord($data[$spos+1])<<8;
1408 | $column = ord($data[$spos+2]) | ord($data[$spos+3])<<8;
1409 | $tmp = unpack("ddouble", substr($data, $spos + 6, 8)); // It machine machine dependent
1410 | if ($this->isDate($spos)) {
1411 | $numValue = $tmp['double'];
1412 | }
1413 | else {
1414 | $numValue = $this->createNumber($spos);
1415 | }
1416 | $info = $this->_getCellDetails($spos,$numValue,$column);
1417 | $this->addcell($row, $column, $info['string'], $info);
1418 | break;
1419 |
1420 | case SPREADSHEET_EXCEL_READER_TYPE_FORMULA:
1421 | case SPREADSHEET_EXCEL_READER_TYPE_FORMULA2:
1422 | $row = ord($data[$spos]) | ord($data[$spos+1])<<8;
1423 | $column = ord($data[$spos+2]) | ord($data[$spos+3])<<8;
1424 | if ((ord($data[$spos+6])==0) && (ord($data[$spos+12])==255) && (ord($data[$spos+13])==255)) {
1425 | //String formula. Result follows in a STRING record
1426 | // This row/col are stored to be referenced in that record
1427 | // http://code.google.com/p/php-excel-reader/issues/detail?id=4
1428 | $previousRow = $row;
1429 | $previousCol = $column;
1430 | } elseif ((ord($data[$spos+6])==1) && (ord($data[$spos+12])==255) && (ord($data[$spos+13])==255)) {
1431 | //Boolean formula. Result is in +2; 0=false,1=true
1432 | // http://code.google.com/p/php-excel-reader/issues/detail?id=4
1433 | if (ord($this->data[$spos+8])==1) {
1434 | $this->addcell($row, $column, "TRUE");
1435 | } else {
1436 | $this->addcell($row, $column, "FALSE");
1437 | }
1438 | } elseif ((ord($data[$spos+6])==2) && (ord($data[$spos+12])==255) && (ord($data[$spos+13])==255)) {
1439 | //Error formula. Error code is in +2;
1440 | } elseif ((ord($data[$spos+6])==3) && (ord($data[$spos+12])==255) && (ord($data[$spos+13])==255)) {
1441 | //Formula result is a null string.
1442 | $this->addcell($row, $column, '');
1443 | } else {
1444 | // result is a number, so first 14 bytes are just like a _NUMBER record
1445 | $tmp = unpack("ddouble", substr($data, $spos + 6, 8)); // It machine machine dependent
1446 | if ($this->isDate($spos)) {
1447 | $numValue = $tmp['double'];
1448 | }
1449 | else {
1450 | $numValue = $this->createNumber($spos);
1451 | }
1452 | $info = $this->_getCellDetails($spos,$numValue,$column);
1453 | $this->addcell($row, $column, $info['string'], $info);
1454 | }
1455 | break;
1456 | case SPREADSHEET_EXCEL_READER_TYPE_BOOLERR:
1457 | $row = ord($data[$spos]) | ord($data[$spos+1])<<8;
1458 | $column = ord($data[$spos+2]) | ord($data[$spos+3])<<8;
1459 | $string = ord($data[$spos+6]);
1460 | $this->addcell($row, $column, $string);
1461 | break;
1462 | case SPREADSHEET_EXCEL_READER_TYPE_STRING:
1463 | // http://code.google.com/p/php-excel-reader/issues/detail?id=4
1464 | if ($version == SPREADSHEET_EXCEL_READER_BIFF8){
1465 | // Unicode 16 string, like an SST record
1466 | $xpos = $spos;
1467 | $numChars =ord($data[$xpos]) | (ord($data[$xpos+1]) << 8);
1468 | $xpos += 2;
1469 | $optionFlags =ord($data[$xpos]);
1470 | $xpos++;
1471 | $asciiEncoding = (($optionFlags &0x01) == 0) ;
1472 | $extendedString = (($optionFlags & 0x04) != 0);
1473 | // See if string contains formatting information
1474 | $richString = (($optionFlags & 0x08) != 0);
1475 | if ($richString) {
1476 | // Read in the crun
1477 | $formattingRuns =ord($data[$xpos]) | (ord($data[$xpos+1]) << 8);
1478 | $xpos += 2;
1479 | }
1480 | if ($extendedString) {
1481 | // Read in cchExtRst
1482 | $extendedRunLength =$this->_GetInt4d($this->data, $xpos);
1483 | $xpos += 4;
1484 | }
1485 | $len = ($asciiEncoding)?$numChars : $numChars*2;
1486 | $retstr =substr($data, $xpos, $len);
1487 | $xpos += $len;
1488 | $retstr = ($asciiEncoding)? $retstr : $this->_encodeUTF16($retstr);
1489 | }
1490 | elseif ($version == SPREADSHEET_EXCEL_READER_BIFF7){
1491 | // Simple byte string
1492 | $xpos = $spos;
1493 | $numChars =ord($data[$xpos]) | (ord($data[$xpos+1]) << 8);
1494 | $xpos += 2;
1495 | $retstr =substr($data, $xpos, $numChars);
1496 | }
1497 | $this->addcell($previousRow, $previousCol, $retstr);
1498 | break;
1499 | case SPREADSHEET_EXCEL_READER_TYPE_ROW:
1500 | $row = ord($data[$spos]) | ord($data[$spos+1])<<8;
1501 | $rowInfo = ord($data[$spos + 6]) | ((ord($data[$spos+7]) << 8) & 0x7FFF);
1502 | if (($rowInfo & 0x8000) > 0) {
1503 | $rowHeight = -1;
1504 | } else {
1505 | $rowHeight = $rowInfo & 0x7FFF;
1506 | }
1507 | $rowHidden = (ord($data[$spos + 12]) & 0x20) >> 5;
1508 | $this->rowInfo[$this->sn][$row+1] = Array('height' => $rowHeight / 20, 'hidden'=>$rowHidden );
1509 | break;
1510 | case SPREADSHEET_EXCEL_READER_TYPE_DBCELL:
1511 | break;
1512 | case SPREADSHEET_EXCEL_READER_TYPE_MULBLANK:
1513 | $row = ord($data[$spos]) | ord($data[$spos+1])<<8;
1514 | $column = ord($data[$spos+2]) | ord($data[$spos+3])<<8;
1515 | $cols = ($length / 2) - 3;
1516 | for ($c = 0; $c < $cols; $c++) {
1517 | $xfindex = ord($data[$spos + 4 + ($c * 2)]) | ord($data[$spos + 5 + ($c * 2)])<<8;
1518 | $this->addcell($row, $column + $c, "", array('xfIndex'=>$xfindex));
1519 | }
1520 | break;
1521 | case SPREADSHEET_EXCEL_READER_TYPE_LABEL:
1522 | $row = ord($data[$spos]) | ord($data[$spos+1])<<8;
1523 | $column = ord($data[$spos+2]) | ord($data[$spos+3])<<8;
1524 | $this->addcell($row, $column, substr($data, $spos + 8, ord($data[$spos + 6]) | ord($data[$spos + 7])<<8));
1525 | break;
1526 | case SPREADSHEET_EXCEL_READER_TYPE_EOF:
1527 | $cont = false;
1528 | break;
1529 | case SPREADSHEET_EXCEL_READER_TYPE_HYPER:
1530 | // Only handle hyperlinks to a URL
1531 | $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8;
1532 | $row2 = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8;
1533 | $column = ord($this->data[$spos+4]) | ord($this->data[$spos+5])<<8;
1534 | $column2 = ord($this->data[$spos+6]) | ord($this->data[$spos+7])<<8;
1535 | $linkdata = Array();
1536 | $flags = ord($this->data[$spos + 28]);
1537 | $udesc = "";
1538 | $ulink = "";
1539 | $uloc = 32;
1540 | $linkdata['flags'] = $flags;
1541 | if (($flags & 1) > 0 ) { // is a type we understand
1542 | // is there a description ?
1543 | if (($flags & 0x14) == 0x14 ) { // has a description
1544 | $uloc += 4;
1545 | $descLen = ord($this->data[$spos + 32]) | ord($this->data[$spos + 33]) << 8;
1546 | $udesc = substr($this->data, $spos + $uloc, $descLen * 2);
1547 | $uloc += 2 * $descLen;
1548 | }
1549 | $ulink = $this->read16bitstring($this->data, $spos + $uloc + 20);
1550 | if ($udesc == "") {
1551 | $udesc = $ulink;
1552 | }
1553 | }
1554 | $linkdata['desc'] = $udesc;
1555 | $linkdata['link'] = $this->_encodeUTF16($ulink);
1556 | for ($r=$row; $r<=$row2; $r++) {
1557 | for ($c=$column; $c<=$column2; $c++) {
1558 | $this->sheets[$this->sn]['cellsInfo'][$r+1][$c+1]['hyperlink'] = $linkdata;
1559 | }
1560 | }
1561 | break;
1562 | case SPREADSHEET_EXCEL_READER_TYPE_DEFCOLWIDTH:
1563 | $this->defaultColWidth = ord($data[$spos+4]) | ord($data[$spos+5]) << 8;
1564 | break;
1565 | case SPREADSHEET_EXCEL_READER_TYPE_STANDARDWIDTH:
1566 | $this->standardColWidth = ord($data[$spos+4]) | ord($data[$spos+5]) << 8;
1567 | break;
1568 | case SPREADSHEET_EXCEL_READER_TYPE_COLINFO:
1569 | $colfrom = ord($data[$spos+0]) | ord($data[$spos+1]) << 8;
1570 | $colto = ord($data[$spos+2]) | ord($data[$spos+3]) << 8;
1571 | $cw = ord($data[$spos+4]) | ord($data[$spos+5]) << 8;
1572 | $cxf = ord($data[$spos+6]) | ord($data[$spos+7]) << 8;
1573 | $co = ord($data[$spos+8]);
1574 | for ($coli = $colfrom; $coli <= $colto; $coli++) {
1575 | $this->colInfo[$this->sn][$coli+1] = Array('width' => $cw, 'xf' => $cxf, 'hidden' => ($co & 0x01), 'collapsed' => ($co & 0x1000) >> 12);
1576 | }
1577 | break;
1578 |
1579 | default:
1580 | break;
1581 | }
1582 | $spos += $length;
1583 | }
1584 |
1585 | if (!isset($this->sheets[$this->sn]['numRows']))
1586 | $this->sheets[$this->sn]['numRows'] = $this->sheets[$this->sn]['maxrow'];
1587 | if (!isset($this->sheets[$this->sn]['numCols']))
1588 | $this->sheets[$this->sn]['numCols'] = $this->sheets[$this->sn]['maxcol'];
1589 | }
1590 |
1591 | function isDate($spos) {
1592 | $xfindex = ord($this->data[$spos+4]) | ord($this->data[$spos+5]) << 8;
1593 | return ($this->xfRecords[$xfindex]['type'] == 'date');
1594 | }
1595 |
1596 | // Get the details for a particular cell
1597 | function _getCellDetails($spos,$numValue,$column) {
1598 | $xfindex = ord($this->data[$spos+4]) | ord($this->data[$spos+5]) << 8;
1599 | $xfrecord = $this->xfRecords[$xfindex];
1600 | $type = $xfrecord['type'];
1601 |
1602 | $format = $xfrecord['format'];
1603 | $formatIndex = $xfrecord['formatIndex'];
1604 | $fontIndex = $xfrecord['fontIndex'];
1605 | $formatColor = "";
1606 | $rectype = '';
1607 | $string = '';
1608 | $raw = '';
1609 |
1610 | if (isset($this->_columnsFormat[$column + 1])){
1611 | $format = $this->_columnsFormat[$column + 1];
1612 | }
1613 |
1614 | if ($type == 'date') {
1615 | // See http://groups.google.com/group/php-excel-reader-discuss/browse_frm/thread/9c3f9790d12d8e10/f2045c2369ac79de
1616 | $rectype = 'date';
1617 | // Convert numeric value into a date
1618 | $utcDays = floor($numValue - ($this->nineteenFour ? SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904 : SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS));
1619 | $utcValue = ($utcDays) * SPREADSHEET_EXCEL_READER_MSINADAY;
1620 | $dateinfo = gmgetdate($utcValue);
1621 |
1622 | $raw = $numValue;
1623 | $fractionalDay = $numValue - floor($numValue) + .0000001; // The .0000001 is to fix for php/excel fractional diffs
1624 |
1625 | $totalseconds = floor(SPREADSHEET_EXCEL_READER_MSINADAY * $fractionalDay);
1626 | $secs = $totalseconds % 60;
1627 | $totalseconds -= $secs;
1628 | $hours = floor($totalseconds / (60 * 60));
1629 | $mins = floor($totalseconds / 60) % 60;
1630 | $string = date ($format, mktime($hours, $mins, $secs, $dateinfo["mon"], $dateinfo["mday"], $dateinfo["year"]));
1631 | } else if ($type == 'number') {
1632 | $rectype = 'number';
1633 | $formatted = $this->_format_value($format, $numValue, $formatIndex);
1634 | $string = $formatted['string'];
1635 | $formatColor = $formatted['formatColor'];
1636 | $raw = $numValue;
1637 | } else{
1638 | if ($format=="") {
1639 | $format = $this->_defaultFormat;
1640 | }
1641 | $rectype = 'unknown';
1642 | $formatted = $this->_format_value($format, $numValue, $formatIndex);
1643 | $string = $formatted['string'];
1644 | $formatColor = $formatted['formatColor'];
1645 | $raw = $numValue;
1646 | }
1647 |
1648 | return array(
1649 | 'string'=>$string,
1650 | 'raw'=>$raw,
1651 | 'rectype'=>$rectype,
1652 | 'format'=>$format,
1653 | 'formatIndex'=>$formatIndex,
1654 | 'fontIndex'=>$fontIndex,
1655 | 'formatColor'=>$formatColor,
1656 | 'xfIndex'=>$xfindex
1657 | );
1658 |
1659 | }
1660 |
1661 |
1662 | function createNumber($spos) {
1663 | $rknumhigh = $this->_GetInt4d($this->data, $spos + 10);
1664 | $rknumlow = $this->_GetInt4d($this->data, $spos + 6);
1665 | $sign = ($rknumhigh & 0x80000000) >> 31;
1666 | $exp = ($rknumhigh & 0x7ff00000) >> 20;
1667 | $mantissa = (0x100000 | ($rknumhigh & 0x000fffff));
1668 | $mantissalow1 = ($rknumlow & 0x80000000) >> 31;
1669 | $mantissalow2 = ($rknumlow & 0x7fffffff);
1670 | $value = $mantissa / pow( 2 , (20- ($exp - 1023)));
1671 | if ($mantissalow1 != 0) $value += 1 / pow (2 , (21 - ($exp - 1023)));
1672 | $value += $mantissalow2 / pow (2 , (52 - ($exp - 1023)));
1673 | if ($sign) {$value = -1 * $value;}
1674 | return $value;
1675 | }
1676 |
1677 | function addcell($row, $col, $string, $info=null) {
1678 | $this->sheets[$this->sn]['maxrow'] = max($this->sheets[$this->sn]['maxrow'], $row + $this->_rowoffset);
1679 | $this->sheets[$this->sn]['maxcol'] = max($this->sheets[$this->sn]['maxcol'], $col + $this->_coloffset);
1680 | $this->sheets[$this->sn]['cells'][$row + $this->_rowoffset][$col + $this->_coloffset] = $string;
1681 | if ($this->store_extended_info && $info) {
1682 | foreach ($info as $key=>$val) {
1683 | $this->sheets[$this->sn]['cellsInfo'][$row + $this->_rowoffset][$col + $this->_coloffset][$key] = $val;
1684 | }
1685 | }
1686 | }
1687 |
1688 |
1689 | function _GetIEEE754($rknum) {
1690 | if (($rknum & 0x02) != 0) {
1691 | $value = $rknum >> 2;
1692 | } else {
1693 | //mmp
1694 | // I got my info on IEEE754 encoding from
1695 | // http://research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
1696 | // The RK format calls for using only the most significant 30 bits of the
1697 | // 64 bit floating point value. The other 34 bits are assumed to be 0
1698 | // So, we use the upper 30 bits of $rknum as follows...
1699 | $sign = ($rknum & 0x80000000) >> 31;
1700 | $exp = ($rknum & 0x7ff00000) >> 20;
1701 | $mantissa = (0x100000 | ($rknum & 0x000ffffc));
1702 | $value = $mantissa / pow( 2 , (20- ($exp - 1023)));
1703 | if ($sign) {
1704 | $value = -1 * $value;
1705 | }
1706 | //end of changes by mmp
1707 | }
1708 | if (($rknum & 0x01) != 0) {
1709 | $value /= 100;
1710 | }
1711 | return $value;
1712 | }
1713 |
1714 | function _encodeUTF16($string) {
1715 | $result = $string;
1716 | if ($this->_defaultEncoding){
1717 | switch ($this->_encoderFunction){
1718 | case 'iconv' : $result = iconv('UTF-16LE', $this->_defaultEncoding, $string);
1719 | break;
1720 | case 'mb_convert_encoding' : $result = mb_convert_encoding($string, $this->_defaultEncoding, 'UTF-16LE' );
1721 | break;
1722 | }
1723 | }
1724 | return $result;
1725 | }
1726 |
1727 | function _GetInt4d($data, $pos) {
1728 | $value = ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | (ord($data[$pos+3]) << 24);
1729 | if ($value>=4294967294) {
1730 | $value=-2;
1731 | }
1732 | return $value;
1733 | }
1734 |
1735 | }
1736 |
1737 | ?>
--------------------------------------------------------------------------------
/vendor/spreadsheet-reader/test.php:
--------------------------------------------------------------------------------
1 | Sheets();
45 |
46 | echo '---------------------------------'.PHP_EOL;
47 | echo 'Spreadsheets:'.PHP_EOL;
48 | print_r($Sheets);
49 | echo '---------------------------------'.PHP_EOL;
50 | echo '---------------------------------'.PHP_EOL;
51 |
52 | foreach ($Sheets as $Index => $Name)
53 | {
54 | echo '---------------------------------'.PHP_EOL;
55 | echo '*** Sheet '.$Name.' ***'.PHP_EOL;
56 | echo '---------------------------------'.PHP_EOL;
57 |
58 | $Time = microtime(true);
59 |
60 | $Spreadsheet -> ChangeSheet($Index);
61 |
62 | foreach ($Spreadsheet as $Key => $Row)
63 | {
64 | echo $Key.': ';
65 | if ($Row)
66 | {
67 | print_r($Row);
68 | }
69 | else
70 | {
71 | var_dump($Row);
72 | }
73 | $CurrentMem = memory_get_usage();
74 |
75 | echo 'Memory: '.($CurrentMem - $BaseMem).' current, '.$CurrentMem.' base'.PHP_EOL;
76 | echo '---------------------------------'.PHP_EOL;
77 |
78 | if ($Key && ($Key % 500 == 0))
79 | {
80 | echo '---------------------------------'.PHP_EOL;
81 | echo 'Time: '.(microtime(true) - $Time);
82 | echo '---------------------------------'.PHP_EOL;
83 | }
84 | }
85 |
86 | echo PHP_EOL.'---------------------------------'.PHP_EOL;
87 | echo 'Time: '.(microtime(true) - $Time);
88 | echo PHP_EOL;
89 |
90 | echo '---------------------------------'.PHP_EOL;
91 | echo '*** End of sheet '.$Name.' ***'.PHP_EOL;
92 | echo '---------------------------------'.PHP_EOL;
93 | }
94 |
95 | }
96 | catch (Exception $E)
97 | {
98 | echo $E -> getMessage();
99 | }
100 | ?>
101 |
--------------------------------------------------------------------------------