├── 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"; 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"; 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"; 643 | } 644 | } 645 | $out .= "\n"; 646 | } 647 | $out .= "
 " . strtoupper($this->colindexes[$i]) . "
$row 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 .= "
"; 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 | --------------------------------------------------------------------------------