PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
tablepress / libraries / vendor / PhpSpreadsheet / Reader / XlsBase.php

XlsBase.php in TablePress – Tables in WordPress made easy 3.4, at libraries/vendor/PhpSpreadsheet/Reader/XlsBase.php

423 lines 12.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace TablePress\PhpOffice\PhpSpreadsheet\Reader;
4
5 use TablePress\PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
6 use TablePress\PhpOffice\PhpSpreadsheet\Shared\CodePage;
7 use TablePress\PhpOffice\PhpSpreadsheet\Shared\File;
8 use TablePress\PhpOffice\PhpSpreadsheet\Shared\OLERead;
9 use TablePress\PhpOffice\PhpSpreadsheet\Shared\StringHelper;
10 use TablePress\PhpOffice\PhpSpreadsheet\Style\Border;
11
12 class XlsBase extends BaseReader
13 {
14 protected const HIGH_ORDER_BIT = 0x80 << 24;
15 protected const FC000000 = 0xFC << 24;
16 protected const FE000000 = 0xFE << 24;
17
18 // ParseXL definitions
19 const XLS_BIFF8 = 0x0600;
20 const XLS_BIFF7 = 0x0500;
21 const XLS_WORKBOOKGLOBALS = 0x0005;
22 const XLS_WORKSHEET = 0x0010;
23
24 // record identifiers
25 const XLS_TYPE_FORMULA = 0x0006;
26 const XLS_TYPE_EOF = 0x000A;
27 const XLS_TYPE_PROTECT = 0x0012;
28 const XLS_TYPE_PASSWORD = 0x0013;
29 const XLS_TYPE_HEADER = 0x0014;
30 const XLS_TYPE_FOOTER = 0x0015;
31 const XLS_TYPE_EXTERNSHEET = 0x0017;
32 const XLS_TYPE_DEFINEDNAME = 0x0018;
33 const XLS_TYPE_VERTICALPAGEBREAKS = 0x001A;
34 const XLS_TYPE_HORIZONTALPAGEBREAKS = 0x001B;
35 const XLS_TYPE_NOTE = 0x001C;
36 const XLS_TYPE_SELECTION = 0x001D;
37 const XLS_TYPE_DATEMODE = 0x0022;
38 const XLS_TYPE_EXTERNNAME = 0x0023;
39 const XLS_TYPE_LEFTMARGIN = 0x0026;
40 const XLS_TYPE_RIGHTMARGIN = 0x0027;
41 const XLS_TYPE_TOPMARGIN = 0x0028;
42 const XLS_TYPE_BOTTOMMARGIN = 0x0029;
43 const XLS_TYPE_PRINTGRIDLINES = 0x002B;
44 const XLS_TYPE_FILEPASS = 0x002F;
45 const XLS_TYPE_FONT = 0x0031;
46 const XLS_TYPE_CONTINUE = 0x003C;
47 const XLS_TYPE_PANE = 0x0041;
48 const XLS_TYPE_CODEPAGE = 0x0042;
49 const XLS_TYPE_DEFCOLWIDTH = 0x0055;
50 const XLS_TYPE_OBJ = 0x005D;
51 const XLS_TYPE_OBJECTPROTECT = 0x0063;
52 const XLS_TYPE_COLINFO = 0x007D;
53 const XLS_TYPE_IMDATA = 0x007F;
54 const XLS_TYPE_SHEETPR = 0x0081;
55 const XLS_TYPE_HCENTER = 0x0083;
56 const XLS_TYPE_VCENTER = 0x0084;
57 const XLS_TYPE_SHEET = 0x0085;
58 const XLS_TYPE_PALETTE = 0x0092;
59 const XLS_TYPE_SCL = 0x00A0;
60 const XLS_TYPE_PAGESETUP = 0x00A1;
61 const XLS_TYPE_MULRK = 0x00BD;
62 const XLS_TYPE_MULBLANK = 0x00BE;
63 const XLS_TYPE_DBCELL = 0x00D7;
64 const XLS_TYPE_SCENPROTECT = 0x00DD;
65 const XLS_TYPE_XF = 0x00E0;
66 const XLS_TYPE_MERGEDCELLS = 0x00E5;
67 const XLS_TYPE_MSODRAWINGGROUP = 0x00EB;
68 const XLS_TYPE_MSODRAWING = 0x00EC;
69 const XLS_TYPE_SST = 0x00FC;
70 const XLS_TYPE_LABELSST = 0x00FD;
71 const XLS_TYPE_EXTSST = 0x00FF;
72 const XLS_TYPE_EXTERNALBOOK = 0x01AE;
73 const XLS_TYPE_CFHEADER = 0x01B0;
74 const XLS_TYPE_CFRULE = 0x01B1;
75 const XLS_TYPE_DATAVALIDATIONS = 0x01B2;
76 const XLS_TYPE_TXO = 0x01B6;
77 const XLS_TYPE_HYPERLINK = 0x01B8;
78 const XLS_TYPE_DATAVALIDATION = 0x01BE;
79 const XLS_TYPE_DIMENSION = 0x0200;
80 const XLS_TYPE_BLANK = 0x0201;
81 const XLS_TYPE_NUMBER = 0x0203;
82 const XLS_TYPE_LABEL = 0x0204;
83 const XLS_TYPE_BOOLERR = 0x0205;
84 const XLS_TYPE_STRING = 0x0207;
85 const XLS_TYPE_ROW = 0x0208;
86 const XLS_TYPE_INDEX = 0x020B;
87 const XLS_TYPE_ARRAY = 0x0221;
88 const XLS_TYPE_DEFAULTROWHEIGHT = 0x0225;
89 const XLS_TYPE_WINDOW2 = 0x023E;
90 const XLS_TYPE_RK = 0x027E;
91 const XLS_TYPE_STYLE = 0x0293;
92 const XLS_TYPE_FORMAT = 0x041E;
93 const XLS_TYPE_SHAREDFMLA = 0x04BC;
94 const XLS_TYPE_BOF = 0x0809;
95 const XLS_TYPE_SHEETLAYOUT = 0x0862;
96 const XLS_TYPE_SHEETPROTECTION = 0x0867;
97 const XLS_TYPE_RANGEPROTECTION = 0x0868;
98 const XLS_TYPE_XFEXT = 0x087D;
99 const XLS_TYPE_PAGELAYOUTVIEW = 0x088B;
100 const XLS_TYPE_UNKNOWN = 0xFFFF;
101
102 // Encryption type
103 const MS_BIFF_CRYPTO_NONE = 0;
104 const MS_BIFF_CRYPTO_XOR = 1;
105 const MS_BIFF_CRYPTO_RC4 = 2;
106
107 // Size of stream blocks when using RC4 encryption
108 const REKEY_BLOCK = 0x400;
109
110 // should be consistent with Writer\Xls\Style\CellBorder
111 const BORDER_STYLE_MAP = [
112 Border::BORDER_NONE, // => 0x00,
113 Border::BORDER_THIN, // => 0x01,
114 Border::BORDER_MEDIUM, // => 0x02,
115 Border::BORDER_DASHED, // => 0x03,
116 Border::BORDER_DOTTED, // => 0x04,
117 Border::BORDER_THICK, // => 0x05,
118 Border::BORDER_DOUBLE, // => 0x06,
119 Border::BORDER_HAIR, // => 0x07,
120 Border::BORDER_MEDIUMDASHED, // => 0x08,
121 Border::BORDER_DASHDOT, // => 0x09,
122 Border::BORDER_MEDIUMDASHDOT, // => 0x0A,
123 Border::BORDER_DASHDOTDOT, // => 0x0B,
124 Border::BORDER_MEDIUMDASHDOTDOT, // => 0x0C,
125 Border::BORDER_SLANTDASHDOT, // => 0x0D,
126 Border::BORDER_OMIT, // => 0x0E,
127 Border::BORDER_OMIT, // => 0x0F,
128 ];
129
130 /**
131 * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
132 * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'.
133 */
134 protected string $codepage = '';
135
136 public function setCodepage(string $codepage): void
137 {
138 if (CodePage::validate($codepage) === false) {
139 throw new PhpSpreadsheetException('Unknown codepage: ' . $codepage);
140 }
141
142 $this->codepage = $codepage;
143 }
144
145 public function getCodepage(): string
146 {
147 return $this->codepage;
148 }
149
150 /**
151 * Can the current IReader read the file?
152 */
153 public function canRead(string $filename): bool
154 {
155 if (File::testFileNoThrow($filename) === false) {
156 return false;
157 }
158
159 try {
160 // Use ParseXL for the hard work.
161 $ole = new OLERead();
162
163 // get excel data
164 $ole->read($filename);
165 if ($ole->wrkbook === null) {
166 throw new Exception('The filename ' . $filename . ' is not recognised as a Spreadsheet file');
167 }
168
169 return true;
170 } catch (PhpSpreadsheetException $exception) {
171 return false;
172 }
173 }
174
175 /**
176 * Extract RGB color
177 * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4.
178 *
179 * @param string $rgb Encoded RGB value (4 bytes)
180 *
181 * @return array{rgb: string}
182 */
183 protected static function readRGB(string $rgb): array
184 {
185 // offset: 0; size 1; Red component
186 $r = ord($rgb[0]);
187
188 // offset: 1; size: 1; Green component
189 $g = ord($rgb[1]);
190
191 // offset: 2; size: 1; Blue component
192 $b = ord($rgb[2]);
193
194 // HEX notation, e.g. 'FF00FC'
195 $rgb = sprintf('%02X%02X%02X', $r, $g, $b);
196
197 return ['rgb' => $rgb];
198 }
199
200 /**
201 * Extracts an Excel Unicode short string (8-bit string length)
202 * OpenOffice documentation: 2.5.3
203 * function will automatically find out where the Unicode string ends.
204 *
205 * @return array{value: string, size: int}
206 */
207 protected static function readUnicodeStringShort(string $subData): array
208 {
209 // offset: 0: size: 1; length of the string (character count)
210 $characterCount = ord($subData[0]);
211
212 $string = self::readUnicodeString((string) substr($subData, 1), $characterCount);
213
214 // add 1 for the string length
215 ++$string['size'];
216
217 return $string;
218 }
219
220 /**
221 * Extracts an Excel Unicode long string (16-bit string length)
222 * OpenOffice documentation: 2.5.3
223 * this function is under construction, needs to support rich text, and Asian phonetic settings.
224 *
225 * @return array{value: string, size: int}
226 */
227 protected static function readUnicodeStringLong(string $subData): array
228 {
229 // offset: 0: size: 2; length of the string (character count)
230 $characterCount = self::getUInt2d($subData, 0);
231
232 $string = self::readUnicodeString((string) substr($subData, 2), $characterCount);
233
234 // add 2 for the string length
235 $string['size'] += 2;
236
237 return $string;
238 }
239
240 /**
241 * Read Unicode string with no string length field, but with known character count
242 * this function is under construction, needs to support rich text, and Asian phonetic settings
243 * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3.
244 *
245 * @return array{value: string, size: int}
246 */
247 protected static function readUnicodeString(string $subData, int $characterCount): array
248 {
249 // offset: 0: size: 1; option flags
250 // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
251 $isCompressed = !((0x01 & ord($subData[0])) >> 0);
252
253 // bit: 2; mask: 0x04; Asian phonetic settings
254 //$hasAsian = (0x04) & ord($subData[0]) >> 2;
255
256 // bit: 3; mask: 0x08; Rich-Text settings
257 //$hasRichText = (0x08) & ord($subData[0]) >> 3;
258
259 // offset: 1: size: var; character array
260 // this offset assumes richtext and Asian phonetic settings are off which is generally wrong
261 // needs to be fixed
262 $value = self::encodeUTF16((string) substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed);
263
264 return [
265 'value' => $value,
266 'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags
267 ];
268 }
269
270 /**
271 * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas.
272 * Example: hello"world --> "hello""world".
273 *
274 * @param string $value UTF-8 encoded string
275 */
276 protected static function UTF8toExcelDoubleQuoted(string $value): string
277 {
278 return '"' . str_replace('"', '""', $value) . '"';
279 }
280
281 /**
282 * Reads first 8 bytes of a string and return IEEE 754 float.
283 *
284 * @param string $data Binary string that is at least 8 bytes long
285 * @return int|float
286 */
287 protected static function extractNumber(string $data)
288 {
289 $rknumhigh = self::getInt4d($data, 4);
290 $rknumlow = self::getInt4d($data, 0);
291 $sign = ($rknumhigh & self::HIGH_ORDER_BIT) >> 31;
292 $exp = (($rknumhigh & 0x7FF00000) >> 20) - 1023;
293 $mantissa = (0x100000 | ($rknumhigh & 0x000FFFFF));
294 $mantissalow1 = ($rknumlow & self::HIGH_ORDER_BIT) >> 31;
295 $mantissalow2 = ($rknumlow & 0x7FFFFFFF);
296 $value = $mantissa / 2 ** (20 - $exp);
297
298 if ($mantissalow1 != 0) {
299 $value += 1 / 2 ** (21 - $exp);
300 }
301
302 if ($mantissalow2 != 0) {
303 $value += $mantissalow2 / 2 ** (52 - $exp);
304 }
305 if ($sign) {
306 $value *= -1;
307 }
308
309 return $value;
310 }
311
312 /**
313 * @return float|int
314 */
315 protected static function getIEEE754(int $rknum)
316 {
317 if (($rknum & 0x02) != 0) {
318 $value = $rknum >> 2;
319 } else {
320 // changes by mmp, info on IEEE754 encoding from
321 // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
322 // The RK format calls for using only the most significant 30 bits
323 // of the 64 bit floating point value. The other 34 bits are assumed
324 // to be 0 so we use the upper 30 bits of $rknum as follows...
325 $sign = ($rknum & self::HIGH_ORDER_BIT) >> 31;
326 $exp = ($rknum & 0x7FF00000) >> 20;
327 $mantissa = (0x100000 | ($rknum & 0x000FFFFC));
328 $value = $mantissa / 2 ** (20 - ($exp - 1023));
329 if ($sign) {
330 $value = -1 * $value;
331 }
332 //end of changes by mmp
333 }
334 if (($rknum & 0x01) != 0) {
335 $value /= 100;
336 }
337
338 return $value;
339 }
340
341 /**
342 * Get UTF-8 string from (compressed or uncompressed) UTF-16 string.
343 */
344 protected static function encodeUTF16(string $string, bool $compressed = false): string
345 {
346 if ($compressed) {
347 $string = self::uncompressByteString($string);
348 }
349
350 return StringHelper::convertEncoding($string, 'UTF-8', 'UTF-16LE');
351 }
352
353 /**
354 * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
355 */
356 protected static function uncompressByteString(string $string): string
357 {
358 $uncompressedString = '';
359 $strLen = strlen($string);
360 for ($i = 0; $i < $strLen; ++$i) {
361 $uncompressedString .= $string[$i] . "\0";
362 }
363
364 return $uncompressedString;
365 }
366
367 /**
368 * Convert string to UTF-8. Only used for BIFF5.
369 */
370 protected function decodeCodepage(string $string): string
371 {
372 return StringHelper::convertEncoding($string, 'UTF-8', $this->codepage);
373 }
374
375 protected static function confirmPos(string $data, int $pos): void
376 {
377 if ($pos >= strlen($data)) {
378 throw new PhpSpreadsheetException('File appears to be corrupt'); // @codeCoverageIgnore
379 }
380 }
381
382 /**
383 * Read 16-bit unsigned integer.
384 */
385 public static function getUInt2d(string $data, int $pos): int
386 {
387 self::confirmPos($data, $pos + 1);
388
389 return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
390 }
391
392 /**
393 * Read 16-bit signed integer.
394 */
395 public static function getInt2d(string $data, int $pos): int
396 {
397 self::confirmPos($data, $pos + 1);
398
399 return unpack('s', $data[$pos] . $data[$pos + 1])[1]; // @phpstan-ignore offsetAccess.nonOffsetAccessible (I don't know how this statement works), return.type (ditto)
400 }
401
402 /**
403 * Read 32-bit signed integer.
404 */
405 public static function getInt4d(string $data, int $pos): int
406 {
407 self::confirmPos($data, $pos + 3);
408
409 // FIX: represent numbers correctly on 64-bit system
410 // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
411 // Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
412 $_or_24 = ord($data[$pos + 3]);
413 if ($_or_24 >= 128) {
414 // negative number
415 $_ord_24 = -abs((256 - $_or_24) << 24);
416 } else {
417 $_ord_24 = ($_or_24 & 127) << 24;
418 }
419
420 return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
421 }
422 }
423