1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace PhpOffice\PhpSpreadsheet\Writer\Xls; |
4
|
|
|
|
5
|
|
|
use PhpOffice\PhpSpreadsheet\Cell\Coordinate; |
6
|
|
|
use PhpOffice\PhpSpreadsheet\Cell\DataType; |
7
|
|
|
use PhpOffice\PhpSpreadsheet\Cell\DataValidation; |
8
|
|
|
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; |
9
|
|
|
use PhpOffice\PhpSpreadsheet\RichText\RichText; |
10
|
|
|
use PhpOffice\PhpSpreadsheet\RichText\Run; |
11
|
|
|
use PhpOffice\PhpSpreadsheet\Shared\StringHelper; |
12
|
|
|
use PhpOffice\PhpSpreadsheet\Shared\Xls; |
13
|
|
|
use PhpOffice\PhpSpreadsheet\Style\Alignment; |
14
|
|
|
use PhpOffice\PhpSpreadsheet\Style\Border; |
15
|
|
|
use PhpOffice\PhpSpreadsheet\Style\Color; |
16
|
|
|
use PhpOffice\PhpSpreadsheet\Style\Conditional; |
17
|
|
|
use PhpOffice\PhpSpreadsheet\Style\Fill; |
18
|
|
|
use PhpOffice\PhpSpreadsheet\Style\Protection; |
19
|
|
|
use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; |
20
|
|
|
use PhpOffice\PhpSpreadsheet\Worksheet\SheetView; |
21
|
|
|
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException; |
22
|
|
|
|
23
|
|
|
// Original file header of PEAR::Spreadsheet_Excel_Writer_Worksheet (used as the base for this class): |
24
|
|
|
// ----------------------------------------------------------------------------------------- |
25
|
|
|
// /* |
26
|
|
|
// * Module written/ported by Xavier Noguer <[email protected]> |
27
|
|
|
// * |
28
|
|
|
// * The majority of this is _NOT_ my code. I simply ported it from the |
29
|
|
|
// * PERL Spreadsheet::WriteExcel module. |
30
|
|
|
// * |
31
|
|
|
// * The author of the Spreadsheet::WriteExcel module is John McNamara |
32
|
|
|
// * <[email protected]> |
33
|
|
|
// * |
34
|
|
|
// * I _DO_ maintain this code, and John McNamara has nothing to do with the |
35
|
|
|
// * porting of this code to PHP. Any questions directly related to this |
36
|
|
|
// * class library should be directed to me. |
37
|
|
|
// * |
38
|
|
|
// * License Information: |
39
|
|
|
// * |
40
|
|
|
// * Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets |
41
|
|
|
// * Copyright (c) 2002-2003 Xavier Noguer [email protected] |
42
|
|
|
// * |
43
|
|
|
// * This library is free software; you can redistribute it and/or |
44
|
|
|
// * modify it under the terms of the GNU Lesser General Public |
45
|
|
|
// * License as published by the Free Software Foundation; either |
46
|
|
|
// * version 2.1 of the License, or (at your option) any later version. |
47
|
|
|
// * |
48
|
|
|
// * This library is distributed in the hope that it will be useful, |
49
|
|
|
// * but WITHOUT ANY WARRANTY; without even the implied warranty of |
50
|
|
|
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
51
|
|
|
// * Lesser General Public License for more details. |
52
|
|
|
// * |
53
|
|
|
// * You should have received a copy of the GNU Lesser General Public |
54
|
|
|
// * License along with this library; if not, write to the Free Software |
55
|
|
|
// * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
56
|
|
|
// */ |
57
|
|
|
class Worksheet extends BIFFwriter |
58
|
|
|
{ |
59
|
|
|
/** |
60
|
|
|
* Formula parser. |
61
|
|
|
* |
62
|
|
|
* @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Parser |
63
|
|
|
*/ |
64
|
|
|
private $parser; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Maximum number of characters for a string (LABEL record in BIFF5). |
68
|
|
|
* |
69
|
|
|
* @var int |
70
|
|
|
*/ |
71
|
|
|
private $xlsStringMaxLength; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Array containing format information for columns. |
75
|
|
|
* |
76
|
|
|
* @var array |
77
|
|
|
*/ |
78
|
|
|
private $columnInfo; |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Array containing the selected area for the worksheet. |
82
|
|
|
* |
83
|
|
|
* @var array |
84
|
|
|
*/ |
85
|
|
|
private $selection; |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* The active pane for the worksheet. |
89
|
|
|
* |
90
|
|
|
* @var int |
91
|
|
|
*/ |
92
|
|
|
private $activePane; |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Whether to use outline. |
96
|
|
|
* |
97
|
|
|
* @var int |
98
|
|
|
*/ |
99
|
|
|
private $outlineOn; |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Auto outline styles. |
103
|
|
|
* |
104
|
|
|
* @var bool |
105
|
|
|
*/ |
106
|
|
|
private $outlineStyle; |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Whether to have outline summary below. |
110
|
|
|
* |
111
|
|
|
* @var bool |
112
|
|
|
*/ |
113
|
|
|
private $outlineBelow; |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* Whether to have outline summary at the right. |
117
|
|
|
* |
118
|
|
|
* @var bool |
119
|
|
|
*/ |
120
|
|
|
private $outlineRight; |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Reference to the total number of strings in the workbook. |
124
|
|
|
* |
125
|
|
|
* @var int |
126
|
|
|
*/ |
127
|
|
|
private $stringTotal; |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Reference to the number of unique strings in the workbook. |
131
|
|
|
* |
132
|
|
|
* @var int |
133
|
|
|
*/ |
134
|
|
|
private $stringUnique; |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Reference to the array containing all the unique strings in the workbook. |
138
|
|
|
* |
139
|
|
|
* @var array |
140
|
|
|
*/ |
141
|
|
|
private $stringTable; |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Color cache. |
145
|
|
|
*/ |
146
|
|
|
private $colors; |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Index of first used row (at least 0). |
150
|
|
|
* |
151
|
|
|
* @var int |
152
|
|
|
*/ |
153
|
|
|
private $firstRowIndex; |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Index of last used row. (no used rows means -1). |
157
|
|
|
* |
158
|
|
|
* @var int |
159
|
|
|
*/ |
160
|
|
|
private $lastRowIndex; |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* Index of first used column (at least 0). |
164
|
|
|
* |
165
|
|
|
* @var int |
166
|
|
|
*/ |
167
|
|
|
private $firstColumnIndex; |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* Index of last used column (no used columns means -1). |
171
|
|
|
* |
172
|
|
|
* @var int |
173
|
|
|
*/ |
174
|
|
|
private $lastColumnIndex; |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* Sheet object. |
178
|
|
|
* |
179
|
|
|
* @var \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet |
180
|
|
|
*/ |
181
|
|
|
public $phpSheet; |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* Count cell style Xfs. |
185
|
|
|
* |
186
|
|
|
* @var int |
187
|
|
|
*/ |
188
|
|
|
private $countCellStyleXfs; |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* Escher object corresponding to MSODRAWING. |
192
|
|
|
* |
193
|
|
|
* @var \PhpOffice\PhpSpreadsheet\Shared\Escher |
194
|
|
|
*/ |
195
|
|
|
private $escher; |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* Array of font hashes associated to FONT records index. |
199
|
|
|
* |
200
|
|
|
* @var array |
201
|
|
|
*/ |
202
|
|
|
public $fontHashIndex; |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* @var bool |
206
|
|
|
*/ |
207
|
|
|
private $preCalculateFormulas; |
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* @var int |
211
|
|
|
*/ |
212
|
|
|
private $printHeaders; |
213
|
|
|
|
214
|
|
|
/** |
215
|
|
|
* Constructor. |
216
|
|
|
* |
217
|
|
|
* @param int $str_total Total number of strings |
218
|
|
|
* @param int $str_unique Total number of unique strings |
219
|
|
|
* @param array &$str_table String Table |
220
|
|
|
* @param array &$colors Colour Table |
221
|
|
|
* @param Parser $parser The formula parser created for the Workbook |
222
|
|
|
* @param bool $preCalculateFormulas Flag indicating whether formulas should be calculated or just written |
223
|
|
|
* @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $phpSheet The worksheet to write |
224
|
|
|
*/ |
225
|
39 |
|
public function __construct(&$str_total, &$str_unique, &$str_table, &$colors, Parser $parser, $preCalculateFormulas, \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $phpSheet) |
226
|
|
|
{ |
227
|
|
|
// It needs to call its parent's constructor explicitly |
228
|
39 |
|
parent::__construct(); |
229
|
|
|
|
230
|
39 |
|
$this->preCalculateFormulas = $preCalculateFormulas; |
231
|
39 |
|
$this->stringTotal = &$str_total; |
232
|
39 |
|
$this->stringUnique = &$str_unique; |
233
|
39 |
|
$this->stringTable = &$str_table; |
234
|
39 |
|
$this->colors = &$colors; |
235
|
39 |
|
$this->parser = $parser; |
236
|
|
|
|
237
|
39 |
|
$this->phpSheet = $phpSheet; |
238
|
|
|
|
239
|
39 |
|
$this->xlsStringMaxLength = 255; |
240
|
39 |
|
$this->columnInfo = []; |
241
|
39 |
|
$this->selection = [0, 0, 0, 0]; |
242
|
39 |
|
$this->activePane = 3; |
243
|
|
|
|
244
|
39 |
|
$this->printHeaders = 0; |
245
|
|
|
|
246
|
39 |
|
$this->outlineStyle = 0; |
247
|
39 |
|
$this->outlineBelow = 1; |
248
|
39 |
|
$this->outlineRight = 1; |
249
|
39 |
|
$this->outlineOn = 1; |
250
|
|
|
|
251
|
39 |
|
$this->fontHashIndex = []; |
252
|
|
|
|
253
|
|
|
// calculate values for DIMENSIONS record |
254
|
39 |
|
$minR = 1; |
255
|
39 |
|
$minC = 'A'; |
256
|
|
|
|
257
|
39 |
|
$maxR = $this->phpSheet->getHighestRow(); |
258
|
39 |
|
$maxC = $this->phpSheet->getHighestColumn(); |
259
|
|
|
|
260
|
|
|
// Determine lowest and highest column and row |
261
|
39 |
|
$this->lastRowIndex = ($maxR > 65535) ? 65535 : $maxR; |
262
|
|
|
|
263
|
39 |
|
$this->firstColumnIndex = Coordinate::columnIndexFromString($minC); |
264
|
39 |
|
$this->lastColumnIndex = Coordinate::columnIndexFromString($maxC); |
265
|
|
|
|
266
|
|
|
// if ($this->firstColumnIndex > 255) $this->firstColumnIndex = 255; |
267
|
39 |
|
if ($this->lastColumnIndex > 255) { |
268
|
|
|
$this->lastColumnIndex = 255; |
269
|
|
|
} |
270
|
|
|
|
271
|
39 |
|
$this->countCellStyleXfs = count($phpSheet->getParent()->getCellStyleXfCollection()); |
272
|
39 |
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* Add data to the beginning of the workbook (note the reverse order) |
276
|
|
|
* and to the end of the workbook. |
277
|
|
|
* |
278
|
|
|
* @see \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook::storeWorkbook() |
279
|
|
|
*/ |
280
|
39 |
|
public function close() |
281
|
|
|
{ |
282
|
39 |
|
$phpSheet = $this->phpSheet; |
283
|
|
|
|
284
|
|
|
// Write BOF record |
285
|
39 |
|
$this->storeBof(0x0010); |
286
|
|
|
|
287
|
|
|
// Write PRINTHEADERS |
288
|
39 |
|
$this->writePrintHeaders(); |
289
|
|
|
|
290
|
|
|
// Write PRINTGRIDLINES |
291
|
39 |
|
$this->writePrintGridlines(); |
292
|
|
|
|
293
|
|
|
// Write GRIDSET |
294
|
39 |
|
$this->writeGridset(); |
295
|
|
|
|
296
|
|
|
// Calculate column widths |
297
|
39 |
|
$phpSheet->calculateColumnWidths(); |
298
|
|
|
|
299
|
|
|
// Column dimensions |
300
|
39 |
|
if (($defaultWidth = $phpSheet->getDefaultColumnDimension()->getWidth()) < 0) { |
301
|
38 |
|
$defaultWidth = \PhpOffice\PhpSpreadsheet\Shared\Font::getDefaultColumnWidthByFont($phpSheet->getParent()->getDefaultStyle()->getFont()); |
302
|
|
|
} |
303
|
|
|
|
304
|
39 |
|
$columnDimensions = $phpSheet->getColumnDimensions(); |
305
|
39 |
|
$maxCol = $this->lastColumnIndex - 1; |
306
|
39 |
|
for ($i = 0; $i <= $maxCol; ++$i) { |
307
|
39 |
|
$hidden = 0; |
308
|
39 |
|
$level = 0; |
309
|
39 |
|
$xfIndex = 15; // there are 15 cell style Xfs |
310
|
|
|
|
311
|
39 |
|
$width = $defaultWidth; |
312
|
|
|
|
313
|
39 |
|
$columnLetter = Coordinate::stringFromColumnIndex($i + 1); |
314
|
39 |
|
if (isset($columnDimensions[$columnLetter])) { |
315
|
19 |
|
$columnDimension = $columnDimensions[$columnLetter]; |
316
|
19 |
|
if ($columnDimension->getWidth() >= 0) { |
317
|
19 |
|
$width = $columnDimension->getWidth(); |
318
|
|
|
} |
319
|
19 |
|
$hidden = $columnDimension->getVisible() ? 0 : 1; |
320
|
19 |
|
$level = $columnDimension->getOutlineLevel(); |
321
|
19 |
|
$xfIndex = $columnDimension->getXfIndex() + 15; // there are 15 cell style Xfs |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
// Components of columnInfo: |
325
|
|
|
// $firstcol first column on the range |
326
|
|
|
// $lastcol last column on the range |
327
|
|
|
// $width width to set |
328
|
|
|
// $xfIndex The optional cell style Xf index to apply to the columns |
329
|
|
|
// $hidden The optional hidden atribute |
330
|
|
|
// $level The optional outline level |
331
|
39 |
|
$this->columnInfo[] = [$i, $i, $width, $xfIndex, $hidden, $level]; |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
// Write GUTS |
335
|
39 |
|
$this->writeGuts(); |
336
|
|
|
|
337
|
|
|
// Write DEFAULTROWHEIGHT |
338
|
39 |
|
$this->writeDefaultRowHeight(); |
339
|
|
|
// Write WSBOOL |
340
|
39 |
|
$this->writeWsbool(); |
341
|
|
|
// Write horizontal and vertical page breaks |
342
|
39 |
|
$this->writeBreaks(); |
343
|
|
|
// Write page header |
344
|
39 |
|
$this->writeHeader(); |
345
|
|
|
// Write page footer |
346
|
39 |
|
$this->writeFooter(); |
347
|
|
|
// Write page horizontal centering |
348
|
39 |
|
$this->writeHcenter(); |
349
|
|
|
// Write page vertical centering |
350
|
39 |
|
$this->writeVcenter(); |
351
|
|
|
// Write left margin |
352
|
39 |
|
$this->writeMarginLeft(); |
353
|
|
|
// Write right margin |
354
|
39 |
|
$this->writeMarginRight(); |
355
|
|
|
// Write top margin |
356
|
39 |
|
$this->writeMarginTop(); |
357
|
|
|
// Write bottom margin |
358
|
39 |
|
$this->writeMarginBottom(); |
359
|
|
|
// Write page setup |
360
|
39 |
|
$this->writeSetup(); |
361
|
|
|
// Write sheet protection |
362
|
39 |
|
$this->writeProtect(); |
363
|
|
|
// Write SCENPROTECT |
364
|
39 |
|
$this->writeScenProtect(); |
365
|
|
|
// Write OBJECTPROTECT |
366
|
39 |
|
$this->writeObjectProtect(); |
367
|
|
|
// Write sheet password |
368
|
39 |
|
$this->writePassword(); |
369
|
|
|
// Write DEFCOLWIDTH record |
370
|
39 |
|
$this->writeDefcol(); |
371
|
|
|
|
372
|
|
|
// Write the COLINFO records if they exist |
373
|
39 |
|
if (!empty($this->columnInfo)) { |
374
|
39 |
|
$colcount = count($this->columnInfo); |
375
|
39 |
|
for ($i = 0; $i < $colcount; ++$i) { |
376
|
39 |
|
$this->writeColinfo($this->columnInfo[$i]); |
377
|
|
|
} |
378
|
|
|
} |
379
|
39 |
|
$autoFilterRange = $phpSheet->getAutoFilter()->getRange(); |
380
|
39 |
|
if (!empty($autoFilterRange)) { |
381
|
|
|
// Write AUTOFILTERINFO |
382
|
3 |
|
$this->writeAutoFilterInfo(); |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
// Write sheet dimensions |
386
|
39 |
|
$this->writeDimensions(); |
387
|
|
|
|
388
|
|
|
// Row dimensions |
389
|
39 |
|
foreach ($phpSheet->getRowDimensions() as $rowDimension) { |
390
|
38 |
|
$xfIndex = $rowDimension->getXfIndex() + 15; // there are 15 cellXfs |
391
|
38 |
|
$this->writeRow($rowDimension->getRowIndex() - 1, $rowDimension->getRowHeight(), $xfIndex, ($rowDimension->getVisible() ? '0' : '1'), $rowDimension->getOutlineLevel()); |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
// Write Cells |
395
|
39 |
|
foreach ($phpSheet->getCoordinates() as $coordinate) { |
396
|
39 |
|
$cell = $phpSheet->getCell($coordinate); |
397
|
39 |
|
$row = $cell->getRow() - 1; |
398
|
39 |
|
$column = Coordinate::columnIndexFromString($cell->getColumn()) - 1; |
399
|
|
|
|
400
|
|
|
// Don't break Excel break the code! |
401
|
39 |
|
if ($row > 65535 || $column > 255) { |
402
|
|
|
throw new WriterException('Rows or columns overflow! Excel5 has limit to 65535 rows and 255 columns. Use XLSX instead.'); |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
// Write cell value |
406
|
39 |
|
$xfIndex = $cell->getXfIndex() + 15; // there are 15 cell style Xfs |
407
|
|
|
|
408
|
39 |
|
$cVal = $cell->getValue(); |
409
|
39 |
|
if ($cVal instanceof RichText) { |
410
|
9 |
|
$arrcRun = []; |
411
|
9 |
|
$str_len = StringHelper::countCharacters($cVal->getPlainText(), 'UTF-8'); |
412
|
9 |
|
$str_pos = 0; |
413
|
9 |
|
$elements = $cVal->getRichTextElements(); |
414
|
9 |
|
foreach ($elements as $element) { |
415
|
|
|
// FONT Index |
416
|
9 |
|
if ($element instanceof Run) { |
417
|
9 |
|
$str_fontidx = $this->fontHashIndex[$element->getFont()->getHashCode()]; |
418
|
|
|
} else { |
419
|
8 |
|
$str_fontidx = 0; |
420
|
|
|
} |
421
|
9 |
|
$arrcRun[] = ['strlen' => $str_pos, 'fontidx' => $str_fontidx]; |
422
|
|
|
// Position FROM |
423
|
9 |
|
$str_pos += StringHelper::countCharacters($element->getText(), 'UTF-8'); |
424
|
|
|
} |
425
|
9 |
|
$this->writeRichTextString($row, $column, $cVal->getPlainText(), $xfIndex, $arrcRun); |
426
|
|
|
} else { |
427
|
38 |
|
switch ($cell->getDatatype()) { |
428
|
38 |
|
case DataType::TYPE_STRING: |
429
|
31 |
|
case DataType::TYPE_NULL: |
430
|
36 |
|
if ($cVal === '' || $cVal === null) { |
431
|
19 |
|
$this->writeBlank($row, $column, $xfIndex); |
432
|
|
|
} else { |
433
|
34 |
|
$this->writeString($row, $column, $cVal, $xfIndex); |
434
|
|
|
} |
435
|
|
|
|
436
|
36 |
|
break; |
437
|
26 |
|
case DataType::TYPE_NUMERIC: |
438
|
23 |
|
$this->writeNumber($row, $column, $cVal, $xfIndex); |
439
|
|
|
|
440
|
23 |
|
break; |
441
|
18 |
|
case DataType::TYPE_FORMULA: |
442
|
16 |
|
$calculatedValue = $this->preCalculateFormulas ? |
443
|
16 |
|
$cell->getCalculatedValue() : null; |
444
|
16 |
|
$this->writeFormula($row, $column, $cVal, $xfIndex, $calculatedValue); |
445
|
|
|
|
446
|
16 |
|
break; |
447
|
8 |
|
case DataType::TYPE_BOOL: |
448
|
8 |
|
$this->writeBoolErr($row, $column, $cVal, 0, $xfIndex); |
449
|
|
|
|
450
|
8 |
|
break; |
451
|
|
|
case DataType::TYPE_ERROR: |
452
|
|
|
$this->writeBoolErr($row, $column, self::mapErrorCode($cVal), 1, $xfIndex); |
453
|
|
|
|
454
|
39 |
|
break; |
455
|
|
|
} |
456
|
|
|
} |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
// Append |
460
|
39 |
|
$this->writeMsoDrawing(); |
461
|
|
|
|
462
|
|
|
// Write WINDOW2 record |
463
|
39 |
|
$this->writeWindow2(); |
464
|
|
|
|
465
|
|
|
// Write PLV record |
466
|
39 |
|
$this->writePageLayoutView(); |
467
|
|
|
|
468
|
|
|
// Write ZOOM record |
469
|
39 |
|
$this->writeZoom(); |
470
|
39 |
|
if ($phpSheet->getFreezePane()) { |
471
|
3 |
|
$this->writePanes(); |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
// Write SELECTION record |
475
|
39 |
|
$this->writeSelection(); |
476
|
|
|
|
477
|
|
|
// Write MergedCellsTable Record |
478
|
39 |
|
$this->writeMergedCells(); |
479
|
|
|
|
480
|
|
|
// Hyperlinks |
481
|
39 |
|
foreach ($phpSheet->getHyperLinkCollection() as $coordinate => $hyperlink) { |
482
|
9 |
|
list($column, $row) = Coordinate::coordinateFromString($coordinate); |
483
|
|
|
|
484
|
9 |
|
$url = $hyperlink->getUrl(); |
485
|
|
|
|
486
|
9 |
|
if (strpos($url, 'sheet://') !== false) { |
487
|
|
|
// internal to current workbook |
488
|
6 |
|
$url = str_replace('sheet://', 'internal:', $url); |
489
|
9 |
|
} elseif (preg_match('/^(http:|https:|ftp:|mailto:)/', $url)) { |
490
|
|
|
// URL |
491
|
|
|
} else { |
492
|
|
|
// external (local file) |
493
|
|
|
$url = 'external:' . $url; |
494
|
|
|
} |
495
|
|
|
|
496
|
9 |
|
$this->writeUrl($row - 1, Coordinate::columnIndexFromString($column) - 1, $url); |
497
|
|
|
} |
498
|
|
|
|
499
|
39 |
|
$this->writeDataValidity(); |
500
|
39 |
|
$this->writeSheetLayout(); |
501
|
|
|
|
502
|
|
|
// Write SHEETPROTECTION record |
503
|
39 |
|
$this->writeSheetProtection(); |
504
|
39 |
|
$this->writeRangeProtection(); |
505
|
|
|
|
506
|
39 |
|
$arrConditionalStyles = $phpSheet->getConditionalStylesCollection(); |
507
|
39 |
|
if (!empty($arrConditionalStyles)) { |
508
|
2 |
|
$arrConditional = []; |
509
|
|
|
// @todo CFRule & CFHeader |
510
|
|
|
// Write CFHEADER record |
511
|
2 |
|
$this->writeCFHeader(); |
512
|
|
|
// Write ConditionalFormattingTable records |
513
|
2 |
|
foreach ($arrConditionalStyles as $cellCoordinate => $conditionalStyles) { |
514
|
2 |
|
foreach ($conditionalStyles as $conditional) { |
515
|
2 |
|
if ($conditional->getConditionType() == Conditional::CONDITION_EXPRESSION |
516
|
2 |
|
|| $conditional->getConditionType() == Conditional::CONDITION_CELLIS) { |
517
|
2 |
|
if (!isset($arrConditional[$conditional->getHashCode()])) { |
518
|
|
|
// This hash code has been handled |
519
|
2 |
|
$arrConditional[$conditional->getHashCode()] = true; |
520
|
|
|
|
521
|
|
|
// Write CFRULE record |
522
|
2 |
|
$this->writeCFRule($conditional); |
523
|
|
|
} |
524
|
|
|
} |
525
|
|
|
} |
526
|
|
|
} |
527
|
|
|
} |
528
|
|
|
|
529
|
39 |
|
$this->storeEof(); |
530
|
39 |
|
} |
531
|
|
|
|
532
|
|
|
/** |
533
|
|
|
* Write a cell range address in BIFF8 |
534
|
|
|
* always fixed range |
535
|
|
|
* See section 2.5.14 in OpenOffice.org's Documentation of the Microsoft Excel File Format. |
536
|
|
|
* |
537
|
|
|
* @param string $range E.g. 'A1' or 'A1:B6' |
538
|
|
|
* |
539
|
|
|
* @return string Binary data |
540
|
|
|
*/ |
541
|
7 |
|
private function writeBIFF8CellRangeAddressFixed($range) |
542
|
|
|
{ |
543
|
7 |
|
$explodes = explode(':', $range); |
544
|
|
|
|
545
|
|
|
// extract first cell, e.g. 'A1' |
546
|
7 |
|
$firstCell = $explodes[0]; |
547
|
|
|
|
548
|
|
|
// extract last cell, e.g. 'B6' |
549
|
7 |
|
if (count($explodes) == 1) { |
550
|
2 |
|
$lastCell = $firstCell; |
551
|
|
|
} else { |
552
|
5 |
|
$lastCell = $explodes[1]; |
553
|
|
|
} |
554
|
|
|
|
555
|
7 |
|
$firstCellCoordinates = Coordinate::coordinateFromString($firstCell); // e.g. array(0, 1) |
556
|
7 |
|
$lastCellCoordinates = Coordinate::coordinateFromString($lastCell); // e.g. array(1, 6) |
557
|
|
|
|
558
|
7 |
|
return pack('vvvv', $firstCellCoordinates[1] - 1, $lastCellCoordinates[1] - 1, Coordinate::columnIndexFromString($firstCellCoordinates[0]) - 1, Coordinate::columnIndexFromString($lastCellCoordinates[0]) - 1); |
559
|
|
|
} |
560
|
|
|
|
561
|
|
|
/** |
562
|
|
|
* Retrieves data from memory in one chunk, or from disk in $buffer |
563
|
|
|
* sized chunks. |
564
|
|
|
* |
565
|
|
|
* @return string The data |
566
|
|
|
*/ |
567
|
39 |
|
public function getData() |
568
|
|
|
{ |
569
|
39 |
|
$buffer = 4096; |
570
|
|
|
|
571
|
|
|
// Return data stored in memory |
572
|
39 |
|
if (isset($this->_data)) { |
573
|
39 |
|
$tmp = $this->_data; |
574
|
39 |
|
unset($this->_data); |
575
|
|
|
|
576
|
39 |
|
return $tmp; |
577
|
|
|
} |
578
|
|
|
// No data to return |
579
|
|
|
return false; |
580
|
|
|
} |
581
|
|
|
|
582
|
|
|
/** |
583
|
|
|
* Set the option to print the row and column headers on the printed page. |
584
|
|
|
* |
585
|
|
|
* @param int $print Whether to print the headers or not. Defaults to 1 (print). |
586
|
|
|
*/ |
587
|
|
|
public function printRowColHeaders($print = 1) |
588
|
|
|
{ |
589
|
|
|
$this->printHeaders = $print; |
590
|
|
|
} |
591
|
|
|
|
592
|
|
|
/** |
593
|
|
|
* This method sets the properties for outlining and grouping. The defaults |
594
|
|
|
* correspond to Excel's defaults. |
595
|
|
|
* |
596
|
|
|
* @param bool $visible |
597
|
|
|
* @param bool $symbols_below |
598
|
|
|
* @param bool $symbols_right |
599
|
|
|
* @param bool $auto_style |
600
|
|
|
*/ |
601
|
|
|
public function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false) |
602
|
|
|
{ |
603
|
|
|
$this->outlineOn = $visible; |
604
|
|
|
$this->outlineBelow = $symbols_below; |
605
|
|
|
$this->outlineRight = $symbols_right; |
606
|
|
|
$this->outlineStyle = $auto_style; |
607
|
|
|
|
608
|
|
|
// Ensure this is a boolean vale for Window2 |
609
|
|
|
if ($this->outlineOn) { |
610
|
|
|
$this->outlineOn = 1; |
611
|
|
|
} |
612
|
|
|
} |
613
|
|
|
|
614
|
|
|
/** |
615
|
|
|
* Write a double to the specified row and column (zero indexed). |
616
|
|
|
* An integer can be written as a double. Excel will display an |
617
|
|
|
* integer. $format is optional. |
618
|
|
|
* |
619
|
|
|
* Returns 0 : normal termination |
620
|
|
|
* -2 : row or column out of range |
621
|
|
|
* |
622
|
|
|
* @param int $row Zero indexed row |
623
|
|
|
* @param int $col Zero indexed column |
624
|
|
|
* @param float $num The number to write |
625
|
|
|
* @param mixed $xfIndex The optional XF format |
626
|
|
|
* |
627
|
|
|
* @return int |
628
|
|
|
*/ |
629
|
23 |
|
private function writeNumber($row, $col, $num, $xfIndex) |
630
|
|
|
{ |
631
|
23 |
|
$record = 0x0203; // Record identifier |
632
|
23 |
|
$length = 0x000E; // Number of bytes to follow |
633
|
|
|
|
634
|
23 |
|
$header = pack('vv', $record, $length); |
635
|
23 |
|
$data = pack('vvv', $row, $col, $xfIndex); |
636
|
23 |
|
$xl_double = pack('d', $num); |
637
|
23 |
|
if (self::getByteOrder()) { // if it's Big Endian |
638
|
|
|
$xl_double = strrev($xl_double); |
639
|
|
|
} |
640
|
|
|
|
641
|
23 |
|
$this->append($header . $data . $xl_double); |
642
|
|
|
|
643
|
23 |
|
return 0; |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
/** |
647
|
|
|
* Write a LABELSST record or a LABEL record. Which one depends on BIFF version. |
648
|
|
|
* |
649
|
|
|
* @param int $row Row index (0-based) |
650
|
|
|
* @param int $col Column index (0-based) |
651
|
|
|
* @param string $str The string |
652
|
|
|
* @param int $xfIndex Index to XF record |
653
|
|
|
*/ |
654
|
34 |
|
private function writeString($row, $col, $str, $xfIndex) |
655
|
|
|
{ |
656
|
34 |
|
$this->writeLabelSst($row, $col, $str, $xfIndex); |
657
|
34 |
|
} |
658
|
|
|
|
659
|
|
|
/** |
660
|
|
|
* Write a LABELSST record or a LABEL record. Which one depends on BIFF version |
661
|
|
|
* It differs from writeString by the writing of rich text strings. |
662
|
|
|
* |
663
|
|
|
* @param int $row Row index (0-based) |
664
|
|
|
* @param int $col Column index (0-based) |
665
|
|
|
* @param string $str The string |
666
|
|
|
* @param int $xfIndex The XF format index for the cell |
667
|
|
|
* @param array $arrcRun Index to Font record and characters beginning |
668
|
|
|
*/ |
669
|
9 |
View Code Duplication |
private function writeRichTextString($row, $col, $str, $xfIndex, $arrcRun) |
|
|
|
|
670
|
|
|
{ |
671
|
9 |
|
$record = 0x00FD; // Record identifier |
672
|
9 |
|
$length = 0x000A; // Bytes to follow |
673
|
9 |
|
$str = StringHelper::UTF8toBIFF8UnicodeShort($str, $arrcRun); |
674
|
|
|
|
675
|
|
|
// check if string is already present |
676
|
9 |
|
if (!isset($this->stringTable[$str])) { |
677
|
9 |
|
$this->stringTable[$str] = $this->stringUnique++; |
678
|
|
|
} |
679
|
9 |
|
++$this->stringTotal; |
680
|
|
|
|
681
|
9 |
|
$header = pack('vv', $record, $length); |
682
|
9 |
|
$data = pack('vvvV', $row, $col, $xfIndex, $this->stringTable[$str]); |
683
|
9 |
|
$this->append($header . $data); |
684
|
9 |
|
} |
685
|
|
|
|
686
|
|
|
/** |
687
|
|
|
* Write a string to the specified row and column (zero indexed). |
688
|
|
|
* This is the BIFF8 version (no 255 chars limit). |
689
|
|
|
* $format is optional. |
690
|
|
|
* Returns 0 : normal termination |
691
|
|
|
* -2 : row or column out of range |
692
|
|
|
* -3 : long string truncated to 255 chars. |
693
|
|
|
* |
694
|
|
|
* @param int $row Zero indexed row |
695
|
|
|
* @param int $col Zero indexed column |
696
|
|
|
* @param string $str The string to write |
697
|
|
|
* @param mixed $xfIndex The XF format index for the cell |
698
|
|
|
* |
699
|
|
|
* @return int |
700
|
|
|
*/ |
701
|
34 |
View Code Duplication |
private function writeLabelSst($row, $col, $str, $xfIndex) |
|
|
|
|
702
|
|
|
{ |
703
|
34 |
|
$record = 0x00FD; // Record identifier |
704
|
34 |
|
$length = 0x000A; // Bytes to follow |
705
|
|
|
|
706
|
34 |
|
$str = StringHelper::UTF8toBIFF8UnicodeLong($str); |
707
|
|
|
|
708
|
|
|
// check if string is already present |
709
|
34 |
|
if (!isset($this->stringTable[$str])) { |
710
|
34 |
|
$this->stringTable[$str] = $this->stringUnique++; |
711
|
|
|
} |
712
|
34 |
|
++$this->stringTotal; |
713
|
|
|
|
714
|
34 |
|
$header = pack('vv', $record, $length); |
715
|
34 |
|
$data = pack('vvvV', $row, $col, $xfIndex, $this->stringTable[$str]); |
716
|
34 |
|
$this->append($header . $data); |
717
|
34 |
|
} |
718
|
|
|
|
719
|
|
|
/** |
720
|
|
|
* Write a blank cell to the specified row and column (zero indexed). |
721
|
|
|
* A blank cell is used to specify formatting without adding a string |
722
|
|
|
* or a number. |
723
|
|
|
* |
724
|
|
|
* A blank cell without a format serves no purpose. Therefore, we don't write |
725
|
|
|
* a BLANK record unless a format is specified. |
726
|
|
|
* |
727
|
|
|
* Returns 0 : normal termination (including no format) |
728
|
|
|
* -1 : insufficient number of arguments |
729
|
|
|
* -2 : row or column out of range |
730
|
|
|
* |
731
|
|
|
* @param int $row Zero indexed row |
732
|
|
|
* @param int $col Zero indexed column |
733
|
|
|
* @param mixed $xfIndex The XF format index |
734
|
|
|
*/ |
735
|
19 |
View Code Duplication |
public function writeBlank($row, $col, $xfIndex) |
|
|
|
|
736
|
|
|
{ |
737
|
19 |
|
$record = 0x0201; // Record identifier |
738
|
19 |
|
$length = 0x0006; // Number of bytes to follow |
739
|
|
|
|
740
|
19 |
|
$header = pack('vv', $record, $length); |
741
|
19 |
|
$data = pack('vvv', $row, $col, $xfIndex); |
742
|
19 |
|
$this->append($header . $data); |
743
|
|
|
|
744
|
19 |
|
return 0; |
745
|
|
|
} |
746
|
|
|
|
747
|
|
|
/** |
748
|
|
|
* Write a boolean or an error type to the specified row and column (zero indexed). |
749
|
|
|
* |
750
|
|
|
* @param int $row Row index (0-based) |
751
|
|
|
* @param int $col Column index (0-based) |
752
|
|
|
* @param int $value |
753
|
|
|
* @param bool $isError Error or Boolean? |
754
|
|
|
* @param int $xfIndex |
755
|
|
|
*/ |
756
|
8 |
View Code Duplication |
private function writeBoolErr($row, $col, $value, $isError, $xfIndex) |
|
|
|
|
757
|
|
|
{ |
758
|
8 |
|
$record = 0x0205; |
759
|
8 |
|
$length = 8; |
760
|
|
|
|
761
|
8 |
|
$header = pack('vv', $record, $length); |
762
|
8 |
|
$data = pack('vvvCC', $row, $col, $xfIndex, $value, $isError); |
763
|
8 |
|
$this->append($header . $data); |
764
|
|
|
|
765
|
8 |
|
return 0; |
766
|
|
|
} |
767
|
|
|
|
768
|
|
|
/** |
769
|
|
|
* Write a formula to the specified row and column (zero indexed). |
770
|
|
|
* The textual representation of the formula is passed to the parser in |
771
|
|
|
* Parser.php which returns a packed binary string. |
772
|
|
|
* |
773
|
|
|
* Returns 0 : normal termination |
774
|
|
|
* -1 : formula errors (bad formula) |
775
|
|
|
* -2 : row or column out of range |
776
|
|
|
* |
777
|
|
|
* @param int $row Zero indexed row |
778
|
|
|
* @param int $col Zero indexed column |
779
|
|
|
* @param string $formula The formula text string |
780
|
|
|
* @param mixed $xfIndex The XF format index |
781
|
|
|
* @param mixed $calculatedValue Calculated value |
782
|
|
|
* |
783
|
|
|
* @return int |
784
|
|
|
*/ |
785
|
16 |
|
private function writeFormula($row, $col, $formula, $xfIndex, $calculatedValue) |
786
|
|
|
{ |
787
|
16 |
|
$record = 0x0006; // Record identifier |
788
|
|
|
|
789
|
|
|
// Initialize possible additional value for STRING record that should be written after the FORMULA record? |
790
|
16 |
|
$stringValue = null; |
791
|
|
|
|
792
|
|
|
// calculated value |
793
|
16 |
|
if (isset($calculatedValue)) { |
794
|
|
|
// Since we can't yet get the data type of the calculated value, |
795
|
|
|
// we use best effort to determine data type |
796
|
16 |
|
if (is_bool($calculatedValue)) { |
797
|
|
|
// Boolean value |
798
|
3 |
|
$num = pack('CCCvCv', 0x01, 0x00, (int) $calculatedValue, 0x00, 0x00, 0xFFFF); |
799
|
16 |
|
} elseif (is_int($calculatedValue) || is_float($calculatedValue)) { |
800
|
|
|
// Numeric value |
801
|
14 |
|
$num = pack('d', $calculatedValue); |
802
|
12 |
|
} elseif (is_string($calculatedValue)) { |
803
|
12 |
|
$errorCodes = DataType::getErrorCodes(); |
804
|
12 |
|
if (isset($errorCodes[$calculatedValue])) { |
805
|
|
|
// Error value |
806
|
4 |
|
$num = pack('CCCvCv', 0x02, 0x00, self::mapErrorCode($calculatedValue), 0x00, 0x00, 0xFFFF); |
807
|
12 |
|
} elseif ($calculatedValue === '') { |
808
|
|
|
// Empty string (and BIFF8) |
809
|
5 |
|
$num = pack('CCCvCv', 0x03, 0x00, 0x00, 0x00, 0x00, 0xFFFF); |
810
|
|
|
} else { |
811
|
|
|
// Non-empty string value (or empty string BIFF5) |
812
|
7 |
|
$stringValue = $calculatedValue; |
813
|
12 |
|
$num = pack('CCCvCv', 0x00, 0x00, 0x00, 0x00, 0x00, 0xFFFF); |
814
|
|
|
} |
815
|
|
|
} else { |
816
|
|
|
// We are really not supposed to reach here |
817
|
16 |
|
$num = pack('d', 0x00); |
818
|
|
|
} |
819
|
|
|
} else { |
820
|
|
|
$num = pack('d', 0x00); |
821
|
|
|
} |
822
|
|
|
|
823
|
16 |
|
$grbit = 0x03; // Option flags |
824
|
16 |
|
$unknown = 0x0000; // Must be zero |
825
|
|
|
|
826
|
|
|
// Strip the '=' or '@' sign at the beginning of the formula string |
827
|
16 |
|
if ($formula[0] == '=') { |
828
|
16 |
|
$formula = substr($formula, 1); |
829
|
|
|
} else { |
830
|
|
|
// Error handling |
831
|
|
|
$this->writeString($row, $col, 'Unrecognised character for formula'); |
832
|
|
|
|
833
|
|
|
return -1; |
834
|
|
|
} |
835
|
|
|
|
836
|
|
|
// Parse the formula using the parser in Parser.php |
837
|
|
|
try { |
838
|
16 |
|
$error = $this->parser->parse($formula); |
839
|
16 |
|
$formula = $this->parser->toReversePolish(); |
840
|
|
|
|
841
|
16 |
|
$formlen = strlen($formula); // Length of the binary string |
842
|
16 |
|
$length = 0x16 + $formlen; // Length of the record data |
843
|
|
|
|
844
|
16 |
|
$header = pack('vv', $record, $length); |
845
|
|
|
|
846
|
16 |
|
$data = pack('vvv', $row, $col, $xfIndex) |
847
|
16 |
|
. $num |
848
|
16 |
|
. pack('vVv', $grbit, $unknown, $formlen); |
849
|
16 |
|
$this->append($header . $data . $formula); |
850
|
|
|
|
851
|
|
|
// Append also a STRING record if necessary |
852
|
16 |
|
if ($stringValue !== null) { |
853
|
7 |
|
$this->writeStringRecord($stringValue); |
854
|
|
|
} |
855
|
|
|
|
856
|
16 |
|
return 0; |
857
|
5 |
|
} catch (PhpSpreadsheetException $e) { |
858
|
|
|
// do nothing |
859
|
|
|
} |
860
|
5 |
|
} |
861
|
|
|
|
862
|
|
|
/** |
863
|
|
|
* Write a STRING record. This. |
864
|
|
|
* |
865
|
|
|
* @param string $stringValue |
866
|
|
|
*/ |
867
|
7 |
View Code Duplication |
private function writeStringRecord($stringValue) |
|
|
|
|
868
|
|
|
{ |
869
|
7 |
|
$record = 0x0207; // Record identifier |
870
|
7 |
|
$data = StringHelper::UTF8toBIFF8UnicodeLong($stringValue); |
871
|
|
|
|
872
|
7 |
|
$length = strlen($data); |
873
|
7 |
|
$header = pack('vv', $record, $length); |
874
|
|
|
|
875
|
7 |
|
$this->append($header . $data); |
876
|
7 |
|
} |
877
|
|
|
|
878
|
|
|
/** |
879
|
|
|
* Write a hyperlink. |
880
|
|
|
* This is comprised of two elements: the visible label and |
881
|
|
|
* the invisible link. The visible label is the same as the link unless an |
882
|
|
|
* alternative string is specified. The label is written using the |
883
|
|
|
* writeString() method. Therefore the 255 characters string limit applies. |
884
|
|
|
* $string and $format are optional. |
885
|
|
|
* |
886
|
|
|
* The hyperlink can be to a http, ftp, mail, internal sheet (not yet), or external |
887
|
|
|
* directory url. |
888
|
|
|
* |
889
|
|
|
* Returns 0 : normal termination |
890
|
|
|
* -2 : row or column out of range |
891
|
|
|
* -3 : long string truncated to 255 chars |
892
|
|
|
* |
893
|
|
|
* @param int $row Row |
894
|
|
|
* @param int $col Column |
895
|
|
|
* @param string $url URL string |
896
|
|
|
* |
897
|
|
|
* @return int |
898
|
|
|
*/ |
899
|
9 |
|
private function writeUrl($row, $col, $url) |
900
|
|
|
{ |
901
|
|
|
// Add start row and col to arg list |
902
|
9 |
|
return $this->writeUrlRange($row, $col, $row, $col, $url); |
903
|
|
|
} |
904
|
|
|
|
905
|
|
|
/** |
906
|
|
|
* This is the more general form of writeUrl(). It allows a hyperlink to be |
907
|
|
|
* written to a range of cells. This function also decides the type of hyperlink |
908
|
|
|
* to be written. These are either, Web (http, ftp, mailto), Internal |
909
|
|
|
* (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1'). |
910
|
|
|
* |
911
|
|
|
* @see writeUrl() |
912
|
|
|
* |
913
|
|
|
* @param int $row1 Start row |
914
|
|
|
* @param int $col1 Start column |
915
|
|
|
* @param int $row2 End row |
916
|
|
|
* @param int $col2 End column |
917
|
|
|
* @param string $url URL string |
918
|
|
|
* |
919
|
|
|
* @return int |
920
|
|
|
*/ |
921
|
9 |
|
public function writeUrlRange($row1, $col1, $row2, $col2, $url) |
922
|
|
|
{ |
923
|
|
|
// Check for internal/external sheet links or default to web link |
924
|
9 |
|
if (preg_match('[^internal:]', $url)) { |
925
|
6 |
|
return $this->writeUrlInternal($row1, $col1, $row2, $col2, $url); |
926
|
|
|
} |
927
|
9 |
|
if (preg_match('[^external:]', $url)) { |
928
|
|
|
return $this->writeUrlExternal($row1, $col1, $row2, $col2, $url); |
929
|
|
|
} |
930
|
|
|
|
931
|
9 |
|
return $this->writeUrlWeb($row1, $col1, $row2, $col2, $url); |
932
|
|
|
} |
933
|
|
|
|
934
|
|
|
/** |
935
|
|
|
* Used to write http, ftp and mailto hyperlinks. |
936
|
|
|
* The link type ($options) is 0x03 is the same as absolute dir ref without |
937
|
|
|
* sheet. However it is differentiated by the $unknown2 data stream. |
938
|
|
|
* |
939
|
|
|
* @see writeUrl() |
940
|
|
|
* |
941
|
|
|
* @param int $row1 Start row |
942
|
|
|
* @param int $col1 Start column |
943
|
|
|
* @param int $row2 End row |
944
|
|
|
* @param int $col2 End column |
945
|
|
|
* @param string $url URL string |
946
|
|
|
* |
947
|
|
|
* @return int |
948
|
|
|
*/ |
949
|
9 |
|
public function writeUrlWeb($row1, $col1, $row2, $col2, $url) |
950
|
|
|
{ |
951
|
9 |
|
$record = 0x01B8; // Record identifier |
952
|
9 |
|
$length = 0x00000; // Bytes to follow |
953
|
|
|
|
954
|
|
|
// Pack the undocumented parts of the hyperlink stream |
955
|
9 |
|
$unknown1 = pack('H*', 'D0C9EA79F9BACE118C8200AA004BA90B02000000'); |
956
|
9 |
|
$unknown2 = pack('H*', 'E0C9EA79F9BACE118C8200AA004BA90B'); |
957
|
|
|
|
958
|
|
|
// Pack the option flags |
959
|
9 |
|
$options = pack('V', 0x03); |
960
|
|
|
|
961
|
|
|
// Convert URL to a null terminated wchar string |
962
|
9 |
|
$url = implode("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY)); |
963
|
9 |
|
$url = $url . "\0\0\0"; |
964
|
|
|
|
965
|
|
|
// Pack the length of the URL |
966
|
9 |
|
$url_len = pack('V', strlen($url)); |
967
|
|
|
|
968
|
|
|
// Calculate the data length |
969
|
9 |
|
$length = 0x34 + strlen($url); |
970
|
|
|
|
971
|
|
|
// Pack the header data |
972
|
9 |
|
$header = pack('vv', $record, $length); |
973
|
9 |
|
$data = pack('vvvv', $row1, $row2, $col1, $col2); |
974
|
|
|
|
975
|
|
|
// Write the packed data |
976
|
9 |
|
$this->append($header . $data . $unknown1 . $options . $unknown2 . $url_len . $url); |
977
|
|
|
|
978
|
9 |
|
return 0; |
979
|
|
|
} |
980
|
|
|
|
981
|
|
|
/** |
982
|
|
|
* Used to write internal reference hyperlinks such as "Sheet1!A1". |
983
|
|
|
* |
984
|
|
|
* @see writeUrl() |
985
|
|
|
* |
986
|
|
|
* @param int $row1 Start row |
987
|
|
|
* @param int $col1 Start column |
988
|
|
|
* @param int $row2 End row |
989
|
|
|
* @param int $col2 End column |
990
|
|
|
* @param string $url URL string |
991
|
|
|
* |
992
|
|
|
* @return int |
993
|
|
|
*/ |
994
|
6 |
|
public function writeUrlInternal($row1, $col1, $row2, $col2, $url) |
995
|
|
|
{ |
996
|
6 |
|
$record = 0x01B8; // Record identifier |
997
|
6 |
|
$length = 0x00000; // Bytes to follow |
998
|
|
|
|
999
|
|
|
// Strip URL type |
1000
|
6 |
|
$url = preg_replace('/^internal:/', '', $url); |
1001
|
|
|
|
1002
|
|
|
// Pack the undocumented parts of the hyperlink stream |
1003
|
6 |
|
$unknown1 = pack('H*', 'D0C9EA79F9BACE118C8200AA004BA90B02000000'); |
1004
|
|
|
|
1005
|
|
|
// Pack the option flags |
1006
|
6 |
|
$options = pack('V', 0x08); |
1007
|
|
|
|
1008
|
|
|
// Convert the URL type and to a null terminated wchar string |
1009
|
6 |
|
$url .= "\0"; |
1010
|
|
|
|
1011
|
|
|
// character count |
1012
|
6 |
|
$url_len = StringHelper::countCharacters($url); |
1013
|
6 |
|
$url_len = pack('V', $url_len); |
1014
|
|
|
|
1015
|
6 |
|
$url = StringHelper::convertEncoding($url, 'UTF-16LE', 'UTF-8'); |
1016
|
|
|
|
1017
|
|
|
// Calculate the data length |
1018
|
6 |
|
$length = 0x24 + strlen($url); |
1019
|
|
|
|
1020
|
|
|
// Pack the header data |
1021
|
6 |
|
$header = pack('vv', $record, $length); |
1022
|
6 |
|
$data = pack('vvvv', $row1, $row2, $col1, $col2); |
1023
|
|
|
|
1024
|
|
|
// Write the packed data |
1025
|
6 |
|
$this->append($header . $data . $unknown1 . $options . $url_len . $url); |
1026
|
|
|
|
1027
|
6 |
|
return 0; |
1028
|
|
|
} |
1029
|
|
|
|
1030
|
|
|
/** |
1031
|
|
|
* Write links to external directory names such as 'c:\foo.xls', |
1032
|
|
|
* c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'. |
1033
|
|
|
* |
1034
|
|
|
* Note: Excel writes some relative links with the $dir_long string. We ignore |
1035
|
|
|
* these cases for the sake of simpler code. |
1036
|
|
|
* |
1037
|
|
|
* @see writeUrl() |
1038
|
|
|
* |
1039
|
|
|
* @param int $row1 Start row |
1040
|
|
|
* @param int $col1 Start column |
1041
|
|
|
* @param int $row2 End row |
1042
|
|
|
* @param int $col2 End column |
1043
|
|
|
* @param string $url URL string |
1044
|
|
|
* |
1045
|
|
|
* @return int |
1046
|
|
|
*/ |
1047
|
|
|
public function writeUrlExternal($row1, $col1, $row2, $col2, $url) |
1048
|
|
|
{ |
1049
|
|
|
// Network drives are different. We will handle them separately |
1050
|
|
|
// MS/Novell network drives and shares start with \\ |
1051
|
|
|
if (preg_match('[^external:\\\\]', $url)) { |
1052
|
|
|
return; //($this->writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format)); |
1053
|
|
|
} |
1054
|
|
|
|
1055
|
|
|
$record = 0x01B8; // Record identifier |
1056
|
|
|
$length = 0x00000; // Bytes to follow |
1057
|
|
|
|
1058
|
|
|
// Strip URL type and change Unix dir separator to Dos style (if needed) |
1059
|
|
|
// |
1060
|
|
|
$url = preg_replace('/^external:/', '', $url); |
1061
|
|
|
$url = preg_replace('/\//', '\\', $url); |
1062
|
|
|
|
1063
|
|
|
// Determine if the link is relative or absolute: |
1064
|
|
|
// relative if link contains no dir separator, "somefile.xls" |
1065
|
|
|
// relative if link starts with up-dir, "..\..\somefile.xls" |
1066
|
|
|
// otherwise, absolute |
1067
|
|
|
|
1068
|
|
|
$absolute = 0x00; // relative path |
1069
|
|
|
if (preg_match('/^[A-Z]:/', $url)) { |
1070
|
|
|
$absolute = 0x02; // absolute path on Windows, e.g. C:\... |
1071
|
|
|
} |
1072
|
|
|
$link_type = 0x01 | $absolute; |
1073
|
|
|
|
1074
|
|
|
// Determine if the link contains a sheet reference and change some of the |
1075
|
|
|
// parameters accordingly. |
1076
|
|
|
// Split the dir name and sheet name (if it exists) |
1077
|
|
|
$dir_long = $url; |
1078
|
|
|
if (preg_match("/\#/", $url)) { |
1079
|
|
|
$link_type |= 0x08; |
1080
|
|
|
} |
1081
|
|
|
|
1082
|
|
|
// Pack the link type |
1083
|
|
|
$link_type = pack('V', $link_type); |
1084
|
|
|
|
1085
|
|
|
// Calculate the up-level dir count e.g.. (..\..\..\ == 3) |
1086
|
|
|
$up_count = preg_match_all("/\.\.\\\/", $dir_long, $useless); |
1087
|
|
|
$up_count = pack('v', $up_count); |
1088
|
|
|
|
1089
|
|
|
// Store the short dos dir name (null terminated) |
1090
|
|
|
$dir_short = preg_replace("/\.\.\\\/", '', $dir_long) . "\0"; |
1091
|
|
|
|
1092
|
|
|
// Store the long dir name as a wchar string (non-null terminated) |
1093
|
|
|
$dir_long = $dir_long . "\0"; |
1094
|
|
|
|
1095
|
|
|
// Pack the lengths of the dir strings |
1096
|
|
|
$dir_short_len = pack('V', strlen($dir_short)); |
1097
|
|
|
$dir_long_len = pack('V', strlen($dir_long)); |
1098
|
|
|
$stream_len = pack('V', 0); //strlen($dir_long) + 0x06); |
1099
|
|
|
|
1100
|
|
|
// Pack the undocumented parts of the hyperlink stream |
1101
|
|
|
$unknown1 = pack('H*', 'D0C9EA79F9BACE118C8200AA004BA90B02000000'); |
1102
|
|
|
$unknown2 = pack('H*', '0303000000000000C000000000000046'); |
1103
|
|
|
$unknown3 = pack('H*', 'FFFFADDE000000000000000000000000000000000000000'); |
1104
|
|
|
$unknown4 = pack('v', 0x03); |
1105
|
|
|
|
1106
|
|
|
// Pack the main data stream |
1107
|
|
|
$data = pack('vvvv', $row1, $row2, $col1, $col2) . |
1108
|
|
|
$unknown1 . |
1109
|
|
|
$link_type . |
1110
|
|
|
$unknown2 . |
1111
|
|
|
$up_count . |
1112
|
|
|
$dir_short_len . |
1113
|
|
|
$dir_short . |
1114
|
|
|
$unknown3 . |
1115
|
|
|
$stream_len; /*. |
1116
|
|
|
$dir_long_len . |
1117
|
|
|
$unknown4 . |
1118
|
|
|
$dir_long . |
1119
|
|
|
$sheet_len . |
1120
|
|
|
$sheet ;*/ |
1121
|
|
|
|
1122
|
|
|
// Pack the header data |
1123
|
|
|
$length = strlen($data); |
1124
|
|
|
$header = pack('vv', $record, $length); |
1125
|
|
|
|
1126
|
|
|
// Write the packed data |
1127
|
|
|
$this->append($header . $data); |
1128
|
|
|
|
1129
|
|
|
return 0; |
1130
|
|
|
} |
1131
|
|
|
|
1132
|
|
|
/** |
1133
|
|
|
* This method is used to set the height and format for a row. |
1134
|
|
|
* |
1135
|
|
|
* @param int $row The row to set |
1136
|
|
|
* @param int $height Height we are giving to the row. |
1137
|
|
|
* Use null to set XF without setting height |
1138
|
|
|
* @param int $xfIndex The optional cell style Xf index to apply to the columns |
1139
|
|
|
* @param bool $hidden The optional hidden attribute |
1140
|
|
|
* @param int $level The optional outline level for row, in range [0,7] |
1141
|
|
|
*/ |
1142
|
38 |
|
private function writeRow($row, $height, $xfIndex, $hidden = false, $level = 0) |
1143
|
|
|
{ |
1144
|
38 |
|
$record = 0x0208; // Record identifier |
1145
|
38 |
|
$length = 0x0010; // Number of bytes to follow |
1146
|
|
|
|
1147
|
38 |
|
$colMic = 0x0000; // First defined column |
1148
|
38 |
|
$colMac = 0x0000; // Last defined column |
1149
|
38 |
|
$irwMac = 0x0000; // Used by Excel to optimise loading |
1150
|
38 |
|
$reserved = 0x0000; // Reserved |
1151
|
38 |
|
$grbit = 0x0000; // Option flags |
1152
|
38 |
|
$ixfe = $xfIndex; |
1153
|
|
|
|
1154
|
38 |
|
if ($height < 0) { |
1155
|
37 |
|
$height = null; |
1156
|
|
|
} |
1157
|
|
|
|
1158
|
|
|
// Use writeRow($row, null, $XF) to set XF format without setting height |
1159
|
38 |
|
if ($height != null) { |
1160
|
6 |
|
$miyRw = $height * 20; // row height |
1161
|
|
|
} else { |
1162
|
37 |
|
$miyRw = 0xff; // default row height is 256 |
1163
|
|
|
} |
1164
|
|
|
|
1165
|
|
|
// Set the options flags. fUnsynced is used to show that the font and row |
1166
|
|
|
// heights are not compatible. This is usually the case for WriteExcel. |
1167
|
|
|
// The collapsed flag 0x10 doesn't seem to be used to indicate that a row |
1168
|
|
|
// is collapsed. Instead it is used to indicate that the previous row is |
1169
|
|
|
// collapsed. The zero height flag, 0x20, is used to collapse a row. |
1170
|
|
|
|
1171
|
38 |
|
$grbit |= $level; |
1172
|
38 |
|
if ($hidden) { |
1173
|
2 |
|
$grbit |= 0x0030; |
1174
|
|
|
} |
1175
|
38 |
|
if ($height !== null) { |
1176
|
6 |
|
$grbit |= 0x0040; // fUnsynced |
1177
|
|
|
} |
1178
|
38 |
|
if ($xfIndex !== 0xF) { |
1179
|
|
|
$grbit |= 0x0080; |
1180
|
|
|
} |
1181
|
38 |
|
$grbit |= 0x0100; |
1182
|
|
|
|
1183
|
38 |
|
$header = pack('vv', $record, $length); |
1184
|
38 |
|
$data = pack('vvvvvvvv', $row, $colMic, $colMac, $miyRw, $irwMac, $reserved, $grbit, $ixfe); |
1185
|
38 |
|
$this->append($header . $data); |
1186
|
38 |
|
} |
1187
|
|
|
|
1188
|
|
|
/** |
1189
|
|
|
* Writes Excel DIMENSIONS to define the area in which there is data. |
1190
|
|
|
*/ |
1191
|
39 |
|
private function writeDimensions() |
1192
|
|
|
{ |
1193
|
39 |
|
$record = 0x0200; // Record identifier |
1194
|
|
|
|
1195
|
39 |
|
$length = 0x000E; |
1196
|
39 |
|
$data = pack('VVvvv', $this->firstRowIndex, $this->lastRowIndex + 1, $this->firstColumnIndex, $this->lastColumnIndex + 1, 0x0000); // reserved |
1197
|
|
|
|
1198
|
39 |
|
$header = pack('vv', $record, $length); |
1199
|
39 |
|
$this->append($header . $data); |
1200
|
39 |
|
} |
1201
|
|
|
|
1202
|
|
|
/** |
1203
|
|
|
* Write BIFF record Window2. |
1204
|
|
|
*/ |
1205
|
39 |
|
private function writeWindow2() |
1206
|
|
|
{ |
1207
|
39 |
|
$record = 0x023E; // Record identifier |
1208
|
39 |
|
$length = 0x0012; |
1209
|
|
|
|
1210
|
39 |
|
$grbit = 0x00B6; // Option flags |
1211
|
39 |
|
$rwTop = 0x0000; // Top row visible in window |
1212
|
39 |
|
$colLeft = 0x0000; // Leftmost column visible in window |
1213
|
|
|
|
1214
|
|
|
// The options flags that comprise $grbit |
1215
|
39 |
|
$fDspFmla = 0; // 0 - bit |
1216
|
39 |
|
$fDspGrid = $this->phpSheet->getShowGridlines() ? 1 : 0; // 1 |
1217
|
39 |
|
$fDspRwCol = $this->phpSheet->getShowRowColHeaders() ? 1 : 0; // 2 |
1218
|
39 |
|
$fFrozen = $this->phpSheet->getFreezePane() ? 1 : 0; // 3 |
1219
|
39 |
|
$fDspZeros = 1; // 4 |
1220
|
39 |
|
$fDefaultHdr = 1; // 5 |
1221
|
39 |
|
$fArabic = $this->phpSheet->getRightToLeft() ? 1 : 0; // 6 |
1222
|
39 |
|
$fDspGuts = $this->outlineOn; // 7 |
1223
|
39 |
|
$fFrozenNoSplit = 0; // 0 - bit |
1224
|
|
|
// no support in PhpSpreadsheet for selected sheet, therefore sheet is only selected if it is the active sheet |
1225
|
39 |
|
$fSelected = ($this->phpSheet === $this->phpSheet->getParent()->getActiveSheet()) ? 1 : 0; |
1226
|
39 |
|
$fPaged = 1; // 2 |
1227
|
39 |
|
$fPageBreakPreview = $this->phpSheet->getSheetView()->getView() === SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW; |
1228
|
|
|
|
1229
|
39 |
|
$grbit = $fDspFmla; |
1230
|
39 |
|
$grbit |= $fDspGrid << 1; |
1231
|
39 |
|
$grbit |= $fDspRwCol << 2; |
1232
|
39 |
|
$grbit |= $fFrozen << 3; |
1233
|
39 |
|
$grbit |= $fDspZeros << 4; |
1234
|
39 |
|
$grbit |= $fDefaultHdr << 5; |
1235
|
39 |
|
$grbit |= $fArabic << 6; |
1236
|
39 |
|
$grbit |= $fDspGuts << 7; |
1237
|
39 |
|
$grbit |= $fFrozenNoSplit << 8; |
1238
|
39 |
|
$grbit |= $fSelected << 9; |
1239
|
39 |
|
$grbit |= $fPaged << 10; |
1240
|
39 |
|
$grbit |= $fPageBreakPreview << 11; |
1241
|
|
|
|
1242
|
39 |
|
$header = pack('vv', $record, $length); |
1243
|
39 |
|
$data = pack('vvv', $grbit, $rwTop, $colLeft); |
1244
|
|
|
|
1245
|
|
|
// FIXME !!! |
1246
|
39 |
|
$rgbHdr = 0x0040; // Row/column heading and gridline color index |
1247
|
39 |
|
$zoom_factor_page_break = ($fPageBreakPreview ? $this->phpSheet->getSheetView()->getZoomScale() : 0x0000); |
1248
|
39 |
|
$zoom_factor_normal = $this->phpSheet->getSheetView()->getZoomScaleNormal(); |
1249
|
|
|
|
1250
|
39 |
|
$data .= pack('vvvvV', $rgbHdr, 0x0000, $zoom_factor_page_break, $zoom_factor_normal, 0x00000000); |
1251
|
|
|
|
1252
|
39 |
|
$this->append($header . $data); |
1253
|
39 |
|
} |
1254
|
|
|
|
1255
|
|
|
/** |
1256
|
|
|
* Write BIFF record DEFAULTROWHEIGHT. |
1257
|
|
|
*/ |
1258
|
39 |
|
private function writeDefaultRowHeight() |
1259
|
|
|
{ |
1260
|
39 |
|
$defaultRowHeight = $this->phpSheet->getDefaultRowDimension()->getRowHeight(); |
1261
|
|
|
|
1262
|
39 |
|
if ($defaultRowHeight < 0) { |
1263
|
36 |
|
return; |
1264
|
|
|
} |
1265
|
|
|
|
1266
|
|
|
// convert to twips |
1267
|
3 |
|
$defaultRowHeight = (int) 20 * $defaultRowHeight; |
1268
|
|
|
|
1269
|
3 |
|
$record = 0x0225; // Record identifier |
1270
|
3 |
|
$length = 0x0004; // Number of bytes to follow |
1271
|
|
|
|
1272
|
3 |
|
$header = pack('vv', $record, $length); |
1273
|
3 |
|
$data = pack('vv', 1, $defaultRowHeight); |
1274
|
3 |
|
$this->append($header . $data); |
1275
|
3 |
|
} |
1276
|
|
|
|
1277
|
|
|
/** |
1278
|
|
|
* Write BIFF record DEFCOLWIDTH if COLINFO records are in use. |
1279
|
|
|
*/ |
1280
|
39 |
View Code Duplication |
private function writeDefcol() |
|
|
|
|
1281
|
|
|
{ |
1282
|
39 |
|
$defaultColWidth = 8; |
1283
|
|
|
|
1284
|
39 |
|
$record = 0x0055; // Record identifier |
1285
|
39 |
|
$length = 0x0002; // Number of bytes to follow |
1286
|
|
|
|
1287
|
39 |
|
$header = pack('vv', $record, $length); |
1288
|
39 |
|
$data = pack('v', $defaultColWidth); |
1289
|
39 |
|
$this->append($header . $data); |
1290
|
39 |
|
} |
1291
|
|
|
|
1292
|
|
|
/** |
1293
|
|
|
* Write BIFF record COLINFO to define column widths. |
1294
|
|
|
* |
1295
|
|
|
* Note: The SDK says the record length is 0x0B but Excel writes a 0x0C |
1296
|
|
|
* length record. |
1297
|
|
|
* |
1298
|
|
|
* @param array $col_array This is the only parameter received and is composed of the following: |
1299
|
|
|
* 0 => First formatted column, |
1300
|
|
|
* 1 => Last formatted column, |
1301
|
|
|
* 2 => Col width (8.43 is Excel default), |
1302
|
|
|
* 3 => The optional XF format of the column, |
1303
|
|
|
* 4 => Option flags. |
1304
|
|
|
* 5 => Optional outline level |
1305
|
|
|
*/ |
1306
|
39 |
|
private function writeColinfo($col_array) |
1307
|
|
|
{ |
1308
|
39 |
|
if (isset($col_array[0])) { |
1309
|
39 |
|
$colFirst = $col_array[0]; |
1310
|
|
|
} |
1311
|
39 |
|
if (isset($col_array[1])) { |
1312
|
39 |
|
$colLast = $col_array[1]; |
1313
|
|
|
} |
1314
|
39 |
|
if (isset($col_array[2])) { |
1315
|
39 |
|
$coldx = $col_array[2]; |
1316
|
|
|
} else { |
1317
|
|
|
$coldx = 8.43; |
1318
|
|
|
} |
1319
|
39 |
|
if (isset($col_array[3])) { |
1320
|
39 |
|
$xfIndex = $col_array[3]; |
1321
|
|
|
} else { |
1322
|
|
|
$xfIndex = 15; |
1323
|
|
|
} |
1324
|
39 |
|
if (isset($col_array[4])) { |
1325
|
39 |
|
$grbit = $col_array[4]; |
1326
|
|
|
} else { |
1327
|
|
|
$grbit = 0; |
1328
|
|
|
} |
1329
|
39 |
|
if (isset($col_array[5])) { |
1330
|
39 |
|
$level = $col_array[5]; |
1331
|
|
|
} else { |
1332
|
|
|
$level = 0; |
1333
|
|
|
} |
1334
|
39 |
|
$record = 0x007D; // Record identifier |
1335
|
39 |
|
$length = 0x000C; // Number of bytes to follow |
1336
|
|
|
|
1337
|
39 |
|
$coldx *= 256; // Convert to units of 1/256 of a char |
1338
|
|
|
|
1339
|
39 |
|
$ixfe = $xfIndex; |
1340
|
39 |
|
$reserved = 0x0000; // Reserved |
1341
|
|
|
|
1342
|
39 |
|
$level = max(0, min($level, 7)); |
1343
|
39 |
|
$grbit |= $level << 8; |
1344
|
|
|
|
1345
|
39 |
|
$header = pack('vv', $record, $length); |
1346
|
39 |
|
$data = pack('vvvvvv', $colFirst, $colLast, $coldx, $ixfe, $grbit, $reserved); |
1347
|
39 |
|
$this->append($header . $data); |
1348
|
39 |
|
} |
1349
|
|
|
|
1350
|
|
|
/** |
1351
|
|
|
* Write BIFF record SELECTION. |
1352
|
|
|
*/ |
1353
|
39 |
|
private function writeSelection() |
1354
|
|
|
{ |
1355
|
|
|
// look up the selected cell range |
1356
|
39 |
|
$selectedCells = Coordinate::splitRange($this->phpSheet->getSelectedCells()); |
1357
|
39 |
|
$selectedCells = $selectedCells[0]; |
1358
|
39 |
|
if (count($selectedCells) == 2) { |
1359
|
13 |
|
list($first, $last) = $selectedCells; |
1360
|
|
|
} else { |
1361
|
32 |
|
$first = $selectedCells[0]; |
1362
|
32 |
|
$last = $selectedCells[0]; |
1363
|
|
|
} |
1364
|
|
|
|
1365
|
39 |
|
list($colFirst, $rwFirst) = Coordinate::coordinateFromString($first); |
1366
|
39 |
|
$colFirst = Coordinate::columnIndexFromString($colFirst) - 1; // base 0 column index |
1367
|
39 |
|
--$rwFirst; // base 0 row index |
1368
|
|
|
|
1369
|
39 |
|
list($colLast, $rwLast) = Coordinate::coordinateFromString($last); |
1370
|
39 |
|
$colLast = Coordinate::columnIndexFromString($colLast) - 1; // base 0 column index |
1371
|
39 |
|
--$rwLast; // base 0 row index |
1372
|
|
|
|
1373
|
|
|
// make sure we are not out of bounds |
1374
|
39 |
|
$colFirst = min($colFirst, 255); |
1375
|
39 |
|
$colLast = min($colLast, 255); |
1376
|
|
|
|
1377
|
39 |
|
$rwFirst = min($rwFirst, 65535); |
1378
|
39 |
|
$rwLast = min($rwLast, 65535); |
1379
|
|
|
|
1380
|
39 |
|
$record = 0x001D; // Record identifier |
1381
|
39 |
|
$length = 0x000F; // Number of bytes to follow |
1382
|
|
|
|
1383
|
39 |
|
$pnn = $this->activePane; // Pane position |
1384
|
39 |
|
$rwAct = $rwFirst; // Active row |
1385
|
39 |
|
$colAct = $colFirst; // Active column |
1386
|
39 |
|
$irefAct = 0; // Active cell ref |
1387
|
39 |
|
$cref = 1; // Number of refs |
1388
|
|
|
|
1389
|
39 |
|
if (!isset($rwLast)) { |
1390
|
|
|
$rwLast = $rwFirst; // Last row in reference |
1391
|
|
|
} |
1392
|
39 |
|
if (!isset($colLast)) { |
1393
|
|
|
$colLast = $colFirst; // Last col in reference |
1394
|
|
|
} |
1395
|
|
|
|
1396
|
|
|
// Swap last row/col for first row/col as necessary |
1397
|
39 |
|
if ($rwFirst > $rwLast) { |
1398
|
|
|
list($rwFirst, $rwLast) = [$rwLast, $rwFirst]; |
1399
|
|
|
} |
1400
|
|
|
|
1401
|
39 |
|
if ($colFirst > $colLast) { |
1402
|
|
|
list($colFirst, $colLast) = [$colLast, $colFirst]; |
1403
|
|
|
} |
1404
|
|
|
|
1405
|
39 |
|
$header = pack('vv', $record, $length); |
1406
|
39 |
|
$data = pack('CvvvvvvCC', $pnn, $rwAct, $colAct, $irefAct, $cref, $rwFirst, $rwLast, $colFirst, $colLast); |
1407
|
39 |
|
$this->append($header . $data); |
1408
|
39 |
|
} |
1409
|
|
|
|
1410
|
|
|
/** |
1411
|
|
|
* Store the MERGEDCELLS records for all ranges of merged cells. |
1412
|
|
|
*/ |
1413
|
39 |
|
private function writeMergedCells() |
1414
|
|
|
{ |
1415
|
39 |
|
$mergeCells = $this->phpSheet->getMergeCells(); |
1416
|
39 |
|
$countMergeCells = count($mergeCells); |
1417
|
|
|
|
1418
|
39 |
|
if ($countMergeCells == 0) { |
1419
|
38 |
|
return; |
1420
|
|
|
} |
1421
|
|
|
|
1422
|
|
|
// maximum allowed number of merged cells per record |
1423
|
9 |
|
$maxCountMergeCellsPerRecord = 1027; |
1424
|
|
|
|
1425
|
|
|
// record identifier |
1426
|
9 |
|
$record = 0x00E5; |
1427
|
|
|
|
1428
|
|
|
// counter for total number of merged cells treated so far by the writer |
1429
|
9 |
|
$i = 0; |
1430
|
|
|
|
1431
|
|
|
// counter for number of merged cells written in record currently being written |
1432
|
9 |
|
$j = 0; |
1433
|
|
|
|
1434
|
|
|
// initialize record data |
1435
|
9 |
|
$recordData = ''; |
1436
|
|
|
|
1437
|
|
|
// loop through the merged cells |
1438
|
9 |
|
foreach ($mergeCells as $mergeCell) { |
1439
|
9 |
|
++$i; |
1440
|
9 |
|
++$j; |
1441
|
|
|
|
1442
|
|
|
// extract the row and column indexes |
1443
|
9 |
|
$range = Coordinate::splitRange($mergeCell); |
1444
|
9 |
|
list($first, $last) = $range[0]; |
1445
|
9 |
|
list($firstColumn, $firstRow) = Coordinate::coordinateFromString($first); |
1446
|
9 |
|
list($lastColumn, $lastRow) = Coordinate::coordinateFromString($last); |
1447
|
|
|
|
1448
|
9 |
|
$recordData .= pack('vvvv', $firstRow - 1, $lastRow - 1, Coordinate::columnIndexFromString($firstColumn) - 1, Coordinate::columnIndexFromString($lastColumn) - 1); |
1449
|
|
|
|
1450
|
|
|
// flush record if we have reached limit for number of merged cells, or reached final merged cell |
1451
|
9 |
|
if ($j == $maxCountMergeCellsPerRecord or $i == $countMergeCells) { |
1452
|
9 |
|
$recordData = pack('v', $j) . $recordData; |
1453
|
9 |
|
$length = strlen($recordData); |
1454
|
9 |
|
$header = pack('vv', $record, $length); |
1455
|
9 |
|
$this->append($header . $recordData); |
1456
|
|
|
|
1457
|
|
|
// initialize for next record, if any |
1458
|
9 |
|
$recordData = ''; |
1459
|
9 |
|
$j = 0; |
1460
|
|
|
} |
1461
|
|
|
} |
1462
|
9 |
|
} |
1463
|
|
|
|
1464
|
|
|
/** |
1465
|
|
|
* Write SHEETLAYOUT record. |
1466
|
|
|
*/ |
1467
|
39 |
|
private function writeSheetLayout() |
1468
|
|
|
{ |
1469
|
39 |
|
if (!$this->phpSheet->isTabColorSet()) { |
1470
|
39 |
|
return; |
1471
|
|
|
} |
1472
|
|
|
|
1473
|
5 |
|
$recordData = pack( |
1474
|
5 |
|
'vvVVVvv', |
1475
|
5 |
|
0x0862, |
1476
|
5 |
|
0x0000, // unused |
1477
|
5 |
|
0x00000000, // unused |
1478
|
5 |
|
0x00000000, // unused |
1479
|
5 |
|
0x00000014, // size of record data |
1480
|
5 |
|
$this->colors[$this->phpSheet->getTabColor()->getRGB()], // color index |
1481
|
5 |
|
0x0000 // unused |
1482
|
|
|
); |
1483
|
|
|
|
1484
|
5 |
|
$length = strlen($recordData); |
1485
|
|
|
|
1486
|
5 |
|
$record = 0x0862; // Record identifier |
1487
|
5 |
|
$header = pack('vv', $record, $length); |
1488
|
5 |
|
$this->append($header . $recordData); |
1489
|
5 |
|
} |
1490
|
|
|
|
1491
|
|
|
/** |
1492
|
|
|
* Write SHEETPROTECTION. |
1493
|
|
|
*/ |
1494
|
39 |
|
private function writeSheetProtection() |
1495
|
|
|
{ |
1496
|
|
|
// record identifier |
1497
|
39 |
|
$record = 0x0867; |
1498
|
|
|
|
1499
|
|
|
// prepare options |
1500
|
39 |
|
$options = (int) !$this->phpSheet->getProtection()->getObjects() |
1501
|
39 |
|
| (int) !$this->phpSheet->getProtection()->getScenarios() << 1 |
1502
|
39 |
|
| (int) !$this->phpSheet->getProtection()->getFormatCells() << 2 |
1503
|
39 |
|
| (int) !$this->phpSheet->getProtection()->getFormatColumns() << 3 |
1504
|
39 |
|
| (int) !$this->phpSheet->getProtection()->getFormatRows() << 4 |
1505
|
39 |
|
| (int) !$this->phpSheet->getProtection()->getInsertColumns() << 5 |
1506
|
39 |
|
| (int) !$this->phpSheet->getProtection()->getInsertRows() << 6 |
1507
|
39 |
|
| (int) !$this->phpSheet->getProtection()->getInsertHyperlinks() << 7 |
1508
|
39 |
|
| (int) !$this->phpSheet->getProtection()->getDeleteColumns() << 8 |
1509
|
39 |
|
| (int) !$this->phpSheet->getProtection()->getDeleteRows() << 9 |
1510
|
39 |
|
| (int) !$this->phpSheet->getProtection()->getSelectLockedCells() << 10 |
1511
|
39 |
|
| (int) !$this->phpSheet->getProtection()->getSort() << 11 |
1512
|
39 |
|
| (int) !$this->phpSheet->getProtection()->getAutoFilter() << 12 |
1513
|
39 |
|
| (int) !$this->phpSheet->getProtection()->getPivotTables() << 13 |
1514
|
39 |
|
| (int) !$this->phpSheet->getProtection()->getSelectUnlockedCells() << 14; |
1515
|
|
|
|
1516
|
|
|
// record data |
1517
|
39 |
|
$recordData = pack( |
1518
|
39 |
|
'vVVCVVvv', |
1519
|
39 |
|
0x0867, // repeated record identifier |
1520
|
39 |
|
0x0000, // not used |
1521
|
39 |
|
0x0000, // not used |
1522
|
39 |
|
0x00, // not used |
1523
|
39 |
|
0x01000200, // unknown data |
1524
|
39 |
|
0xFFFFFFFF, // unknown data |
1525
|
39 |
|
$options, // options |
1526
|
39 |
|
0x0000 // not used |
1527
|
|
|
); |
1528
|
|
|
|
1529
|
39 |
|
$length = strlen($recordData); |
1530
|
39 |
|
$header = pack('vv', $record, $length); |
1531
|
|
|
|
1532
|
39 |
|
$this->append($header . $recordData); |
1533
|
39 |
|
} |
1534
|
|
|
|
1535
|
|
|
/** |
1536
|
|
|
* Write BIFF record RANGEPROTECTION. |
1537
|
|
|
* |
1538
|
|
|
* Openoffice.org's Documentaion of the Microsoft Excel File Format uses term RANGEPROTECTION for these records |
1539
|
|
|
* Microsoft Office Excel 97-2007 Binary File Format Specification uses term FEAT for these records |
1540
|
|
|
*/ |
1541
|
39 |
|
private function writeRangeProtection() |
1542
|
|
|
{ |
1543
|
39 |
|
foreach ($this->phpSheet->getProtectedCells() as $range => $password) { |
1544
|
|
|
// number of ranges, e.g. 'A1:B3 C20:D25' |
1545
|
5 |
|
$cellRanges = explode(' ', $range); |
1546
|
5 |
|
$cref = count($cellRanges); |
1547
|
|
|
|
1548
|
5 |
|
$recordData = pack( |
1549
|
5 |
|
'vvVVvCVvVv', |
1550
|
5 |
|
0x0868, |
1551
|
5 |
|
0x00, |
1552
|
5 |
|
0x0000, |
1553
|
5 |
|
0x0000, |
1554
|
5 |
|
0x02, |
1555
|
5 |
|
0x0, |
1556
|
5 |
|
0x0000, |
1557
|
5 |
|
$cref, |
1558
|
5 |
|
0x0000, |
1559
|
5 |
|
0x00 |
1560
|
|
|
); |
1561
|
|
|
|
1562
|
5 |
|
foreach ($cellRanges as $cellRange) { |
1563
|
5 |
|
$recordData .= $this->writeBIFF8CellRangeAddressFixed($cellRange); |
1564
|
|
|
} |
1565
|
|
|
|
1566
|
|
|
// the rgbFeat structure |
1567
|
5 |
|
$recordData .= pack( |
1568
|
5 |
|
'VV', |
1569
|
5 |
|
0x0000, |
1570
|
5 |
|
hexdec($password) |
1571
|
|
|
); |
1572
|
|
|
|
1573
|
5 |
|
$recordData .= StringHelper::UTF8toBIFF8UnicodeLong('p' . md5($recordData)); |
1574
|
|
|
|
1575
|
5 |
|
$length = strlen($recordData); |
1576
|
|
|
|
1577
|
5 |
|
$record = 0x0868; // Record identifier |
1578
|
5 |
|
$header = pack('vv', $record, $length); |
1579
|
5 |
|
$this->append($header . $recordData); |
1580
|
|
|
} |
1581
|
39 |
|
} |
1582
|
|
|
|
1583
|
|
|
/** |
1584
|
|
|
* Writes the Excel BIFF PANE record. |
1585
|
|
|
* The panes can either be frozen or thawed (unfrozen). |
1586
|
|
|
* Frozen panes are specified in terms of an integer number of rows and columns. |
1587
|
|
|
* Thawed panes are specified in terms of Excel's units for rows and columns. |
1588
|
|
|
*/ |
1589
|
3 |
|
private function writePanes() |
1590
|
|
|
{ |
1591
|
3 |
|
$panes = []; |
1592
|
3 |
|
if ($freezePane = $this->phpSheet->getFreezePane()) { |
1593
|
3 |
|
list($column, $row) = Coordinate::coordinateFromString($freezePane); |
1594
|
3 |
|
$panes[0] = $row - 1; |
1595
|
3 |
|
$panes[1] = Coordinate::columnIndexFromString($column) - 1; |
1596
|
|
|
} else { |
1597
|
|
|
// thaw panes |
1598
|
|
|
return; |
1599
|
|
|
} |
1600
|
|
|
|
1601
|
3 |
|
$y = isset($panes[0]) ? $panes[0] : null; |
1602
|
3 |
|
$x = isset($panes[1]) ? $panes[1] : null; |
1603
|
3 |
|
$rwTop = isset($panes[2]) ? $panes[2] : null; |
1604
|
3 |
|
$colLeft = isset($panes[3]) ? $panes[3] : null; |
1605
|
3 |
|
if (count($panes) > 4) { // if Active pane was received |
1606
|
|
|
$pnnAct = $panes[4]; |
1607
|
|
|
} else { |
1608
|
3 |
|
$pnnAct = null; |
1609
|
|
|
} |
1610
|
3 |
|
$record = 0x0041; // Record identifier |
1611
|
3 |
|
$length = 0x000A; // Number of bytes to follow |
1612
|
|
|
|
1613
|
|
|
// Code specific to frozen or thawed panes. |
1614
|
3 |
|
if ($this->phpSheet->getFreezePane()) { |
1615
|
|
|
// Set default values for $rwTop and $colLeft |
1616
|
3 |
|
if (!isset($rwTop)) { |
1617
|
3 |
|
$rwTop = $y; |
1618
|
|
|
} |
1619
|
3 |
|
if (!isset($colLeft)) { |
1620
|
3 |
|
$colLeft = $x; |
1621
|
|
|
} |
1622
|
|
|
} else { |
1623
|
|
|
// Set default values for $rwTop and $colLeft |
1624
|
|
|
if (!isset($rwTop)) { |
1625
|
|
|
$rwTop = 0; |
1626
|
|
|
} |
1627
|
|
|
if (!isset($colLeft)) { |
1628
|
|
|
$colLeft = 0; |
1629
|
|
|
} |
1630
|
|
|
|
1631
|
|
|
// Convert Excel's row and column units to the internal units. |
1632
|
|
|
// The default row height is 12.75 |
1633
|
|
|
// The default column width is 8.43 |
1634
|
|
|
// The following slope and intersection values were interpolated. |
1635
|
|
|
// |
1636
|
|
|
$y = 20 * $y + 255; |
1637
|
|
|
$x = 113.879 * $x + 390; |
1638
|
|
|
} |
1639
|
|
|
|
1640
|
|
|
// Determine which pane should be active. There is also the undocumented |
1641
|
|
|
// option to override this should it be necessary: may be removed later. |
1642
|
|
|
// |
1643
|
3 |
|
if (!isset($pnnAct)) { |
1644
|
3 |
|
if ($x != 0 && $y != 0) { |
1645
|
|
|
$pnnAct = 0; // Bottom right |
1646
|
|
|
} |
1647
|
3 |
|
if ($x != 0 && $y == 0) { |
1648
|
|
|
$pnnAct = 1; // Top right |
1649
|
|
|
} |
1650
|
3 |
|
if ($x == 0 && $y != 0) { |
1651
|
3 |
|
$pnnAct = 2; // Bottom left |
1652
|
|
|
} |
1653
|
3 |
|
if ($x == 0 && $y == 0) { |
1654
|
|
|
$pnnAct = 3; // Top left |
1655
|
|
|
} |
1656
|
|
|
} |
1657
|
|
|
|
1658
|
3 |
|
$this->activePane = $pnnAct; // Used in writeSelection |
1659
|
|
|
|
1660
|
3 |
|
$header = pack('vv', $record, $length); |
1661
|
3 |
|
$data = pack('vvvvv', $x, $y, $rwTop, $colLeft, $pnnAct); |
1662
|
3 |
|
$this->append($header . $data); |
1663
|
3 |
|
} |
1664
|
|
|
|
1665
|
|
|
/** |
1666
|
|
|
* Store the page setup SETUP BIFF record. |
1667
|
|
|
*/ |
1668
|
39 |
|
private function writeSetup() |
1669
|
|
|
{ |
1670
|
39 |
|
$record = 0x00A1; // Record identifier |
1671
|
39 |
|
$length = 0x0022; // Number of bytes to follow |
1672
|
|
|
|
1673
|
39 |
|
$iPaperSize = $this->phpSheet->getPageSetup()->getPaperSize(); // Paper size |
1674
|
|
|
|
1675
|
39 |
|
$iScale = $this->phpSheet->getPageSetup()->getScale() ? |
1676
|
39 |
|
$this->phpSheet->getPageSetup()->getScale() : 100; // Print scaling factor |
1677
|
|
|
|
1678
|
39 |
|
$iPageStart = 0x01; // Starting page number |
1679
|
39 |
|
$iFitWidth = (int) $this->phpSheet->getPageSetup()->getFitToWidth(); // Fit to number of pages wide |
1680
|
39 |
|
$iFitHeight = (int) $this->phpSheet->getPageSetup()->getFitToHeight(); // Fit to number of pages high |
1681
|
39 |
|
$grbit = 0x00; // Option flags |
1682
|
39 |
|
$iRes = 0x0258; // Print resolution |
1683
|
39 |
|
$iVRes = 0x0258; // Vertical print resolution |
1684
|
|
|
|
1685
|
39 |
|
$numHdr = $this->phpSheet->getPageMargins()->getHeader(); // Header Margin |
1686
|
|
|
|
1687
|
39 |
|
$numFtr = $this->phpSheet->getPageMargins()->getFooter(); // Footer Margin |
1688
|
39 |
|
$iCopies = 0x01; // Number of copies |
1689
|
|
|
|
1690
|
39 |
|
$fLeftToRight = 0x0; // Print over then down |
1691
|
|
|
|
1692
|
|
|
// Page orientation |
1693
|
39 |
|
$fLandscape = ($this->phpSheet->getPageSetup()->getOrientation() == PageSetup::ORIENTATION_LANDSCAPE) ? |
1694
|
39 |
|
0x0 : 0x1; |
1695
|
|
|
|
1696
|
39 |
|
$fNoPls = 0x0; // Setup not read from printer |
1697
|
39 |
|
$fNoColor = 0x0; // Print black and white |
1698
|
39 |
|
$fDraft = 0x0; // Print draft quality |
1699
|
39 |
|
$fNotes = 0x0; // Print notes |
1700
|
39 |
|
$fNoOrient = 0x0; // Orientation not set |
1701
|
39 |
|
$fUsePage = 0x0; // Use custom starting page |
1702
|
|
|
|
1703
|
39 |
|
$grbit = $fLeftToRight; |
1704
|
39 |
|
$grbit |= $fLandscape << 1; |
1705
|
39 |
|
$grbit |= $fNoPls << 2; |
1706
|
39 |
|
$grbit |= $fNoColor << 3; |
1707
|
39 |
|
$grbit |= $fDraft << 4; |
1708
|
39 |
|
$grbit |= $fNotes << 5; |
1709
|
39 |
|
$grbit |= $fNoOrient << 6; |
1710
|
39 |
|
$grbit |= $fUsePage << 7; |
1711
|
|
|
|
1712
|
39 |
|
$numHdr = pack('d', $numHdr); |
1713
|
39 |
|
$numFtr = pack('d', $numFtr); |
1714
|
39 |
|
if (self::getByteOrder()) { // if it's Big Endian |
1715
|
|
|
$numHdr = strrev($numHdr); |
1716
|
|
|
$numFtr = strrev($numFtr); |
1717
|
|
|
} |
1718
|
|
|
|
1719
|
39 |
|
$header = pack('vv', $record, $length); |
1720
|
39 |
|
$data1 = pack('vvvvvvvv', $iPaperSize, $iScale, $iPageStart, $iFitWidth, $iFitHeight, $grbit, $iRes, $iVRes); |
1721
|
39 |
|
$data2 = $numHdr . $numFtr; |
1722
|
39 |
|
$data3 = pack('v', $iCopies); |
1723
|
39 |
|
$this->append($header . $data1 . $data2 . $data3); |
1724
|
39 |
|
} |
1725
|
|
|
|
1726
|
|
|
/** |
1727
|
|
|
* Store the header caption BIFF record. |
1728
|
|
|
*/ |
1729
|
39 |
View Code Duplication |
private function writeHeader() |
|
|
|
|
1730
|
|
|
{ |
1731
|
39 |
|
$record = 0x0014; // Record identifier |
1732
|
|
|
|
1733
|
|
|
/* removing for now |
1734
|
|
|
// need to fix character count (multibyte!) |
1735
|
|
|
if (strlen($this->phpSheet->getHeaderFooter()->getOddHeader()) <= 255) { |
1736
|
|
|
$str = $this->phpSheet->getHeaderFooter()->getOddHeader(); // header string |
1737
|
|
|
} else { |
1738
|
|
|
$str = ''; |
1739
|
|
|
} |
1740
|
|
|
*/ |
1741
|
|
|
|
1742
|
39 |
|
$recordData = StringHelper::UTF8toBIFF8UnicodeLong($this->phpSheet->getHeaderFooter()->getOddHeader()); |
1743
|
39 |
|
$length = strlen($recordData); |
1744
|
|
|
|
1745
|
39 |
|
$header = pack('vv', $record, $length); |
1746
|
|
|
|
1747
|
39 |
|
$this->append($header . $recordData); |
1748
|
39 |
|
} |
1749
|
|
|
|
1750
|
|
|
/** |
1751
|
|
|
* Store the footer caption BIFF record. |
1752
|
|
|
*/ |
1753
|
39 |
View Code Duplication |
private function writeFooter() |
|
|
|
|
1754
|
|
|
{ |
1755
|
39 |
|
$record = 0x0015; // Record identifier |
1756
|
|
|
|
1757
|
|
|
/* removing for now |
1758
|
|
|
// need to fix character count (multibyte!) |
1759
|
|
|
if (strlen($this->phpSheet->getHeaderFooter()->getOddFooter()) <= 255) { |
1760
|
|
|
$str = $this->phpSheet->getHeaderFooter()->getOddFooter(); |
1761
|
|
|
} else { |
1762
|
|
|
$str = ''; |
1763
|
|
|
} |
1764
|
|
|
*/ |
1765
|
|
|
|
1766
|
39 |
|
$recordData = StringHelper::UTF8toBIFF8UnicodeLong($this->phpSheet->getHeaderFooter()->getOddFooter()); |
1767
|
39 |
|
$length = strlen($recordData); |
1768
|
|
|
|
1769
|
39 |
|
$header = pack('vv', $record, $length); |
1770
|
|
|
|
1771
|
39 |
|
$this->append($header . $recordData); |
1772
|
39 |
|
} |
1773
|
|
|
|
1774
|
|
|
/** |
1775
|
|
|
* Store the horizontal centering HCENTER BIFF record. |
1776
|
|
|
*/ |
1777
|
39 |
View Code Duplication |
private function writeHcenter() |
|
|
|
|
1778
|
|
|
{ |
1779
|
39 |
|
$record = 0x0083; // Record identifier |
1780
|
39 |
|
$length = 0x0002; // Bytes to follow |
1781
|
|
|
|
1782
|
39 |
|
$fHCenter = $this->phpSheet->getPageSetup()->getHorizontalCentered() ? 1 : 0; // Horizontal centering |
1783
|
|
|
|
1784
|
39 |
|
$header = pack('vv', $record, $length); |
1785
|
39 |
|
$data = pack('v', $fHCenter); |
1786
|
|
|
|
1787
|
39 |
|
$this->append($header . $data); |
1788
|
39 |
|
} |
1789
|
|
|
|
1790
|
|
|
/** |
1791
|
|
|
* Store the vertical centering VCENTER BIFF record. |
1792
|
|
|
*/ |
1793
|
39 |
View Code Duplication |
private function writeVcenter() |
|
|
|
|
1794
|
|
|
{ |
1795
|
39 |
|
$record = 0x0084; // Record identifier |
1796
|
39 |
|
$length = 0x0002; // Bytes to follow |
1797
|
|
|
|
1798
|
39 |
|
$fVCenter = $this->phpSheet->getPageSetup()->getVerticalCentered() ? 1 : 0; // Horizontal centering |
1799
|
|
|
|
1800
|
39 |
|
$header = pack('vv', $record, $length); |
1801
|
39 |
|
$data = pack('v', $fVCenter); |
1802
|
39 |
|
$this->append($header . $data); |
1803
|
39 |
|
} |
1804
|
|
|
|
1805
|
|
|
/** |
1806
|
|
|
* Store the LEFTMARGIN BIFF record. |
1807
|
|
|
*/ |
1808
|
39 |
View Code Duplication |
private function writeMarginLeft() |
|
|
|
|
1809
|
|
|
{ |
1810
|
39 |
|
$record = 0x0026; // Record identifier |
1811
|
39 |
|
$length = 0x0008; // Bytes to follow |
1812
|
|
|
|
1813
|
39 |
|
$margin = $this->phpSheet->getPageMargins()->getLeft(); // Margin in inches |
1814
|
|
|
|
1815
|
39 |
|
$header = pack('vv', $record, $length); |
1816
|
39 |
|
$data = pack('d', $margin); |
1817
|
39 |
|
if (self::getByteOrder()) { // if it's Big Endian |
1818
|
|
|
$data = strrev($data); |
1819
|
|
|
} |
1820
|
|
|
|
1821
|
39 |
|
$this->append($header . $data); |
1822
|
39 |
|
} |
1823
|
|
|
|
1824
|
|
|
/** |
1825
|
|
|
* Store the RIGHTMARGIN BIFF record. |
1826
|
|
|
*/ |
1827
|
39 |
View Code Duplication |
private function writeMarginRight() |
|
|
|
|
1828
|
|
|
{ |
1829
|
39 |
|
$record = 0x0027; // Record identifier |
1830
|
39 |
|
$length = 0x0008; // Bytes to follow |
1831
|
|
|
|
1832
|
39 |
|
$margin = $this->phpSheet->getPageMargins()->getRight(); // Margin in inches |
1833
|
|
|
|
1834
|
39 |
|
$header = pack('vv', $record, $length); |
1835
|
39 |
|
$data = pack('d', $margin); |
1836
|
39 |
|
if (self::getByteOrder()) { // if it's Big Endian |
1837
|
|
|
$data = strrev($data); |
1838
|
|
|
} |
1839
|
|
|
|
1840
|
39 |
|
$this->append($header . $data); |
1841
|
39 |
|
} |
1842
|
|
|
|
1843
|
|
|
/** |
1844
|
|
|
* Store the TOPMARGIN BIFF record. |
1845
|
|
|
*/ |
1846
|
39 |
View Code Duplication |
private function writeMarginTop() |
|
|
|
|
1847
|
|
|
{ |
1848
|
39 |
|
$record = 0x0028; // Record identifier |
1849
|
39 |
|
$length = 0x0008; // Bytes to follow |
1850
|
|
|
|
1851
|
39 |
|
$margin = $this->phpSheet->getPageMargins()->getTop(); // Margin in inches |
1852
|
|
|
|
1853
|
39 |
|
$header = pack('vv', $record, $length); |
1854
|
39 |
|
$data = pack('d', $margin); |
1855
|
39 |
|
if (self::getByteOrder()) { // if it's Big Endian |
1856
|
|
|
$data = strrev($data); |
1857
|
|
|
} |
1858
|
|
|
|
1859
|
39 |
|
$this->append($header . $data); |
1860
|
39 |
|
} |
1861
|
|
|
|
1862
|
|
|
/** |
1863
|
|
|
* Store the BOTTOMMARGIN BIFF record. |
1864
|
|
|
*/ |
1865
|
39 |
View Code Duplication |
private function writeMarginBottom() |
|
|
|
|
1866
|
|
|
{ |
1867
|
39 |
|
$record = 0x0029; // Record identifier |
1868
|
39 |
|
$length = 0x0008; // Bytes to follow |
1869
|
|
|
|
1870
|
39 |
|
$margin = $this->phpSheet->getPageMargins()->getBottom(); // Margin in inches |
1871
|
|
|
|
1872
|
39 |
|
$header = pack('vv', $record, $length); |
1873
|
39 |
|
$data = pack('d', $margin); |
1874
|
39 |
|
if (self::getByteOrder()) { // if it's Big Endian |
1875
|
|
|
$data = strrev($data); |
1876
|
|
|
} |
1877
|
|
|
|
1878
|
39 |
|
$this->append($header . $data); |
1879
|
39 |
|
} |
1880
|
|
|
|
1881
|
|
|
/** |
1882
|
|
|
* Write the PRINTHEADERS BIFF record. |
1883
|
|
|
*/ |
1884
|
39 |
View Code Duplication |
private function writePrintHeaders() |
|
|
|
|
1885
|
|
|
{ |
1886
|
39 |
|
$record = 0x002a; // Record identifier |
1887
|
39 |
|
$length = 0x0002; // Bytes to follow |
1888
|
|
|
|
1889
|
39 |
|
$fPrintRwCol = $this->printHeaders; // Boolean flag |
1890
|
|
|
|
1891
|
39 |
|
$header = pack('vv', $record, $length); |
1892
|
39 |
|
$data = pack('v', $fPrintRwCol); |
1893
|
39 |
|
$this->append($header . $data); |
1894
|
39 |
|
} |
1895
|
|
|
|
1896
|
|
|
/** |
1897
|
|
|
* Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the |
1898
|
|
|
* GRIDSET record. |
1899
|
|
|
*/ |
1900
|
39 |
View Code Duplication |
private function writePrintGridlines() |
|
|
|
|
1901
|
|
|
{ |
1902
|
39 |
|
$record = 0x002b; // Record identifier |
1903
|
39 |
|
$length = 0x0002; // Bytes to follow |
1904
|
|
|
|
1905
|
39 |
|
$fPrintGrid = $this->phpSheet->getPrintGridlines() ? 1 : 0; // Boolean flag |
1906
|
|
|
|
1907
|
39 |
|
$header = pack('vv', $record, $length); |
1908
|
39 |
|
$data = pack('v', $fPrintGrid); |
1909
|
39 |
|
$this->append($header . $data); |
1910
|
39 |
|
} |
1911
|
|
|
|
1912
|
|
|
/** |
1913
|
|
|
* Write the GRIDSET BIFF record. Must be used in conjunction with the |
1914
|
|
|
* PRINTGRIDLINES record. |
1915
|
|
|
*/ |
1916
|
39 |
|
private function writeGridset() |
1917
|
|
|
{ |
1918
|
39 |
|
$record = 0x0082; // Record identifier |
1919
|
39 |
|
$length = 0x0002; // Bytes to follow |
1920
|
|
|
|
1921
|
39 |
|
$fGridSet = !$this->phpSheet->getPrintGridlines(); // Boolean flag |
1922
|
|
|
|
1923
|
39 |
|
$header = pack('vv', $record, $length); |
1924
|
39 |
|
$data = pack('v', $fGridSet); |
1925
|
39 |
|
$this->append($header . $data); |
1926
|
39 |
|
} |
1927
|
|
|
|
1928
|
|
|
/** |
1929
|
|
|
* Write the AUTOFILTERINFO BIFF record. This is used to configure the number of autofilter select used in the sheet. |
1930
|
|
|
*/ |
1931
|
3 |
|
private function writeAutoFilterInfo() |
1932
|
|
|
{ |
1933
|
3 |
|
$record = 0x009D; // Record identifier |
1934
|
3 |
|
$length = 0x0002; // Bytes to follow |
1935
|
|
|
|
1936
|
3 |
|
$rangeBounds = Coordinate::rangeBoundaries($this->phpSheet->getAutoFilter()->getRange()); |
1937
|
3 |
|
$iNumFilters = 1 + $rangeBounds[1][0] - $rangeBounds[0][0]; |
1938
|
|
|
|
1939
|
3 |
|
$header = pack('vv', $record, $length); |
1940
|
3 |
|
$data = pack('v', $iNumFilters); |
1941
|
3 |
|
$this->append($header . $data); |
1942
|
3 |
|
} |
1943
|
|
|
|
1944
|
|
|
/** |
1945
|
|
|
* Write the GUTS BIFF record. This is used to configure the gutter margins |
1946
|
|
|
* where Excel outline symbols are displayed. The visibility of the gutters is |
1947
|
|
|
* controlled by a flag in WSBOOL. |
1948
|
|
|
* |
1949
|
|
|
* @see writeWsbool() |
1950
|
|
|
*/ |
1951
|
39 |
|
private function writeGuts() |
1952
|
|
|
{ |
1953
|
39 |
|
$record = 0x0080; // Record identifier |
1954
|
39 |
|
$length = 0x0008; // Bytes to follow |
1955
|
|
|
|
1956
|
39 |
|
$dxRwGut = 0x0000; // Size of row gutter |
1957
|
39 |
|
$dxColGut = 0x0000; // Size of col gutter |
1958
|
|
|
|
1959
|
|
|
// determine maximum row outline level |
1960
|
39 |
|
$maxRowOutlineLevel = 0; |
1961
|
39 |
|
foreach ($this->phpSheet->getRowDimensions() as $rowDimension) { |
1962
|
38 |
|
$maxRowOutlineLevel = max($maxRowOutlineLevel, $rowDimension->getOutlineLevel()); |
1963
|
|
|
} |
1964
|
|
|
|
1965
|
39 |
|
$col_level = 0; |
1966
|
|
|
|
1967
|
|
|
// Calculate the maximum column outline level. The equivalent calculation |
1968
|
|
|
// for the row outline level is carried out in writeRow(). |
1969
|
39 |
|
$colcount = count($this->columnInfo); |
1970
|
39 |
|
for ($i = 0; $i < $colcount; ++$i) { |
1971
|
39 |
|
$col_level = max($this->columnInfo[$i][5], $col_level); |
1972
|
|
|
} |
1973
|
|
|
|
1974
|
|
|
// Set the limits for the outline levels (0 <= x <= 7). |
1975
|
39 |
|
$col_level = max(0, min($col_level, 7)); |
1976
|
|
|
|
1977
|
|
|
// The displayed level is one greater than the max outline levels |
1978
|
39 |
|
if ($maxRowOutlineLevel) { |
1979
|
|
|
++$maxRowOutlineLevel; |
1980
|
|
|
} |
1981
|
39 |
|
if ($col_level) { |
1982
|
1 |
|
++$col_level; |
1983
|
|
|
} |
1984
|
|
|
|
1985
|
39 |
|
$header = pack('vv', $record, $length); |
1986
|
39 |
|
$data = pack('vvvv', $dxRwGut, $dxColGut, $maxRowOutlineLevel, $col_level); |
1987
|
|
|
|
1988
|
39 |
|
$this->append($header . $data); |
1989
|
39 |
|
} |
1990
|
|
|
|
1991
|
|
|
/** |
1992
|
|
|
* Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction |
1993
|
|
|
* with the SETUP record. |
1994
|
|
|
*/ |
1995
|
39 |
|
private function writeWsbool() |
1996
|
|
|
{ |
1997
|
39 |
|
$record = 0x0081; // Record identifier |
1998
|
39 |
|
$length = 0x0002; // Bytes to follow |
1999
|
39 |
|
$grbit = 0x0000; |
2000
|
|
|
|
2001
|
|
|
// The only option that is of interest is the flag for fit to page. So we |
2002
|
|
|
// set all the options in one go. |
2003
|
|
|
// |
2004
|
|
|
// Set the option flags |
2005
|
39 |
|
$grbit |= 0x0001; // Auto page breaks visible |
2006
|
39 |
|
if ($this->outlineStyle) { |
2007
|
|
|
$grbit |= 0x0020; // Auto outline styles |
2008
|
|
|
} |
2009
|
39 |
|
if ($this->phpSheet->getShowSummaryBelow()) { |
2010
|
39 |
|
$grbit |= 0x0040; // Outline summary below |
2011
|
|
|
} |
2012
|
39 |
|
if ($this->phpSheet->getShowSummaryRight()) { |
2013
|
39 |
|
$grbit |= 0x0080; // Outline summary right |
2014
|
|
|
} |
2015
|
39 |
|
if ($this->phpSheet->getPageSetup()->getFitToPage()) { |
2016
|
|
|
$grbit |= 0x0100; // Page setup fit to page |
2017
|
|
|
} |
2018
|
39 |
|
if ($this->outlineOn) { |
2019
|
39 |
|
$grbit |= 0x0400; // Outline symbols displayed |
2020
|
|
|
} |
2021
|
|
|
|
2022
|
39 |
|
$header = pack('vv', $record, $length); |
2023
|
39 |
|
$data = pack('v', $grbit); |
2024
|
39 |
|
$this->append($header . $data); |
2025
|
39 |
|
} |
2026
|
|
|
|
2027
|
|
|
/** |
2028
|
|
|
* Write the HORIZONTALPAGEBREAKS and VERTICALPAGEBREAKS BIFF records. |
2029
|
|
|
*/ |
2030
|
39 |
|
private function writeBreaks() |
2031
|
|
|
{ |
2032
|
|
|
// initialize |
2033
|
39 |
|
$vbreaks = []; |
2034
|
39 |
|
$hbreaks = []; |
2035
|
|
|
|
2036
|
39 |
|
foreach ($this->phpSheet->getBreaks() as $cell => $breakType) { |
2037
|
|
|
// Fetch coordinates |
2038
|
1 |
|
$coordinates = Coordinate::coordinateFromString($cell); |
2039
|
|
|
|
2040
|
|
|
// Decide what to do by the type of break |
2041
|
|
|
switch ($breakType) { |
2042
|
1 |
|
case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_COLUMN: |
2043
|
|
|
// Add to list of vertical breaks |
2044
|
|
|
$vbreaks[] = Coordinate::columnIndexFromString($coordinates[0]) - 1; |
2045
|
|
|
|
2046
|
|
|
break; |
2047
|
1 |
|
case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW: |
2048
|
|
|
// Add to list of horizontal breaks |
2049
|
1 |
|
$hbreaks[] = $coordinates[1]; |
2050
|
|
|
|
2051
|
1 |
|
break; |
2052
|
|
|
case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_NONE: |
2053
|
|
|
default: |
2054
|
|
|
// Nothing to do |
2055
|
1 |
|
break; |
2056
|
|
|
} |
2057
|
|
|
} |
2058
|
|
|
|
2059
|
|
|
//horizontal page breaks |
2060
|
39 |
|
if (!empty($hbreaks)) { |
2061
|
|
|
// Sort and filter array of page breaks |
2062
|
1 |
|
sort($hbreaks, SORT_NUMERIC); |
2063
|
1 |
|
if ($hbreaks[0] == 0) { // don't use first break if it's 0 |
2064
|
|
|
array_shift($hbreaks); |
2065
|
|
|
} |
2066
|
|
|
|
2067
|
1 |
|
$record = 0x001b; // Record identifier |
2068
|
1 |
|
$cbrk = count($hbreaks); // Number of page breaks |
2069
|
1 |
|
$length = 2 + 6 * $cbrk; // Bytes to follow |
2070
|
|
|
|
2071
|
1 |
|
$header = pack('vv', $record, $length); |
2072
|
1 |
|
$data = pack('v', $cbrk); |
2073
|
|
|
|
2074
|
|
|
// Append each page break |
2075
|
1 |
|
foreach ($hbreaks as $hbreak) { |
2076
|
1 |
|
$data .= pack('vvv', $hbreak, 0x0000, 0x00ff); |
2077
|
|
|
} |
2078
|
|
|
|
2079
|
1 |
|
$this->append($header . $data); |
2080
|
|
|
} |
2081
|
|
|
|
2082
|
|
|
// vertical page breaks |
2083
|
39 |
|
if (!empty($vbreaks)) { |
2084
|
|
|
// 1000 vertical pagebreaks appears to be an internal Excel 5 limit. |
2085
|
|
|
// It is slightly higher in Excel 97/200, approx. 1026 |
2086
|
|
|
$vbreaks = array_slice($vbreaks, 0, 1000); |
2087
|
|
|
|
2088
|
|
|
// Sort and filter array of page breaks |
2089
|
|
|
sort($vbreaks, SORT_NUMERIC); |
2090
|
|
|
if ($vbreaks[0] == 0) { // don't use first break if it's 0 |
2091
|
|
|
array_shift($vbreaks); |
2092
|
|
|
} |
2093
|
|
|
|
2094
|
|
|
$record = 0x001a; // Record identifier |
2095
|
|
|
$cbrk = count($vbreaks); // Number of page breaks |
2096
|
|
|
$length = 2 + 6 * $cbrk; // Bytes to follow |
2097
|
|
|
|
2098
|
|
|
$header = pack('vv', $record, $length); |
2099
|
|
|
$data = pack('v', $cbrk); |
2100
|
|
|
|
2101
|
|
|
// Append each page break |
2102
|
|
|
foreach ($vbreaks as $vbreak) { |
2103
|
|
|
$data .= pack('vvv', $vbreak, 0x0000, 0xffff); |
2104
|
|
|
} |
2105
|
|
|
|
2106
|
|
|
$this->append($header . $data); |
2107
|
|
|
} |
2108
|
39 |
|
} |
2109
|
|
|
|
2110
|
|
|
/** |
2111
|
|
|
* Set the Biff PROTECT record to indicate that the worksheet is protected. |
2112
|
|
|
*/ |
2113
|
39 |
View Code Duplication |
private function writeProtect() |
|
|
|
|
2114
|
|
|
{ |
2115
|
|
|
// Exit unless sheet protection has been specified |
2116
|
39 |
|
if (!$this->phpSheet->getProtection()->getSheet()) { |
2117
|
37 |
|
return; |
2118
|
|
|
} |
2119
|
|
|
|
2120
|
7 |
|
$record = 0x0012; // Record identifier |
2121
|
7 |
|
$length = 0x0002; // Bytes to follow |
2122
|
|
|
|
2123
|
7 |
|
$fLock = 1; // Worksheet is protected |
2124
|
|
|
|
2125
|
7 |
|
$header = pack('vv', $record, $length); |
2126
|
7 |
|
$data = pack('v', $fLock); |
2127
|
|
|
|
2128
|
7 |
|
$this->append($header . $data); |
2129
|
7 |
|
} |
2130
|
|
|
|
2131
|
|
|
/** |
2132
|
|
|
* Write SCENPROTECT. |
2133
|
|
|
*/ |
2134
|
39 |
View Code Duplication |
private function writeScenProtect() |
|
|
|
|
2135
|
|
|
{ |
2136
|
|
|
// Exit if sheet protection is not active |
2137
|
39 |
|
if (!$this->phpSheet->getProtection()->getSheet()) { |
2138
|
37 |
|
return; |
2139
|
|
|
} |
2140
|
|
|
|
2141
|
|
|
// Exit if scenarios are not protected |
2142
|
7 |
|
if (!$this->phpSheet->getProtection()->getScenarios()) { |
2143
|
7 |
|
return; |
2144
|
|
|
} |
2145
|
|
|
|
2146
|
|
|
$record = 0x00DD; // Record identifier |
2147
|
|
|
$length = 0x0002; // Bytes to follow |
2148
|
|
|
|
2149
|
|
|
$header = pack('vv', $record, $length); |
2150
|
|
|
$data = pack('v', 1); |
2151
|
|
|
|
2152
|
|
|
$this->append($header . $data); |
2153
|
|
|
} |
2154
|
|
|
|
2155
|
|
|
/** |
2156
|
|
|
* Write OBJECTPROTECT. |
2157
|
|
|
*/ |
2158
|
39 |
View Code Duplication |
private function writeObjectProtect() |
|
|
|
|
2159
|
|
|
{ |
2160
|
|
|
// Exit if sheet protection is not active |
2161
|
39 |
|
if (!$this->phpSheet->getProtection()->getSheet()) { |
2162
|
37 |
|
return; |
2163
|
|
|
} |
2164
|
|
|
|
2165
|
|
|
// Exit if objects are not protected |
2166
|
7 |
|
if (!$this->phpSheet->getProtection()->getObjects()) { |
2167
|
7 |
|
return; |
2168
|
|
|
} |
2169
|
|
|
|
2170
|
|
|
$record = 0x0063; // Record identifier |
2171
|
|
|
$length = 0x0002; // Bytes to follow |
2172
|
|
|
|
2173
|
|
|
$header = pack('vv', $record, $length); |
2174
|
|
|
$data = pack('v', 1); |
2175
|
|
|
|
2176
|
|
|
$this->append($header . $data); |
2177
|
|
|
} |
2178
|
|
|
|
2179
|
|
|
/** |
2180
|
|
|
* Write the worksheet PASSWORD record. |
2181
|
|
|
*/ |
2182
|
39 |
|
private function writePassword() |
2183
|
|
|
{ |
2184
|
|
|
// Exit unless sheet protection and password have been specified |
2185
|
39 |
|
if (!$this->phpSheet->getProtection()->getSheet() || !$this->phpSheet->getProtection()->getPassword()) { |
2186
|
38 |
|
return; |
2187
|
|
|
} |
2188
|
|
|
|
2189
|
1 |
|
$record = 0x0013; // Record identifier |
2190
|
1 |
|
$length = 0x0002; // Bytes to follow |
2191
|
|
|
|
2192
|
1 |
|
$wPassword = hexdec($this->phpSheet->getProtection()->getPassword()); // Encoded password |
2193
|
|
|
|
2194
|
1 |
|
$header = pack('vv', $record, $length); |
2195
|
1 |
|
$data = pack('v', $wPassword); |
2196
|
|
|
|
2197
|
1 |
|
$this->append($header . $data); |
2198
|
1 |
|
} |
2199
|
|
|
|
2200
|
|
|
/** |
2201
|
|
|
* Insert a 24bit bitmap image in a worksheet. |
2202
|
|
|
* |
2203
|
|
|
* @param int $row The row we are going to insert the bitmap into |
2204
|
|
|
* @param int $col The column we are going to insert the bitmap into |
2205
|
|
|
* @param mixed $bitmap The bitmap filename or GD-image resource |
2206
|
|
|
* @param int $x the horizontal position (offset) of the image inside the cell |
2207
|
|
|
* @param int $y the vertical position (offset) of the image inside the cell |
2208
|
|
|
* @param float $scale_x The horizontal scale |
2209
|
|
|
* @param float $scale_y The vertical scale |
2210
|
|
|
*/ |
2211
|
|
|
public function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1) |
2212
|
|
|
{ |
2213
|
|
|
$bitmap_array = (is_resource($bitmap) ? $this->processBitmapGd($bitmap) : $this->processBitmap($bitmap)); |
2214
|
|
|
list($width, $height, $size, $data) = $bitmap_array; |
2215
|
|
|
|
2216
|
|
|
// Scale the frame of the image. |
2217
|
|
|
$width *= $scale_x; |
2218
|
|
|
$height *= $scale_y; |
2219
|
|
|
|
2220
|
|
|
// Calculate the vertices of the image and write the OBJ record |
2221
|
|
|
$this->positionImage($col, $row, $x, $y, $width, $height); |
2222
|
|
|
|
2223
|
|
|
// Write the IMDATA record to store the bitmap data |
2224
|
|
|
$record = 0x007f; |
2225
|
|
|
$length = 8 + $size; |
2226
|
|
|
$cf = 0x09; |
2227
|
|
|
$env = 0x01; |
2228
|
|
|
$lcb = $size; |
2229
|
|
|
|
2230
|
|
|
$header = pack('vvvvV', $record, $length, $cf, $env, $lcb); |
2231
|
|
|
$this->append($header . $data); |
2232
|
|
|
} |
2233
|
|
|
|
2234
|
|
|
/** |
2235
|
|
|
* Calculate the vertices that define the position of the image as required by |
2236
|
|
|
* the OBJ record. |
2237
|
|
|
* |
2238
|
|
|
* +------------+------------+ |
2239
|
|
|
* | A | B | |
2240
|
|
|
* +-----+------------+------------+ |
2241
|
|
|
* | |(x1,y1) | | |
2242
|
|
|
* | 1 |(A1)._______|______ | |
2243
|
|
|
* | | | | | |
2244
|
|
|
* | | | | | |
2245
|
|
|
* +-----+----| BITMAP |-----+ |
2246
|
|
|
* | | | | | |
2247
|
|
|
* | 2 | |______________. | |
2248
|
|
|
* | | | (B2)| |
2249
|
|
|
* | | | (x2,y2)| |
2250
|
|
|
* +---- +------------+------------+ |
2251
|
|
|
* |
2252
|
|
|
* Example of a bitmap that covers some of the area from cell A1 to cell B2. |
2253
|
|
|
* |
2254
|
|
|
* Based on the width and height of the bitmap we need to calculate 8 vars: |
2255
|
|
|
* $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2. |
2256
|
|
|
* The width and height of the cells are also variable and have to be taken into |
2257
|
|
|
* account. |
2258
|
|
|
* The values of $col_start and $row_start are passed in from the calling |
2259
|
|
|
* function. The values of $col_end and $row_end are calculated by subtracting |
2260
|
|
|
* the width and height of the bitmap from the width and height of the |
2261
|
|
|
* underlying cells. |
2262
|
|
|
* The vertices are expressed as a percentage of the underlying cell width as |
2263
|
|
|
* follows (rhs values are in pixels): |
2264
|
|
|
* |
2265
|
|
|
* x1 = X / W *1024 |
2266
|
|
|
* y1 = Y / H *256 |
2267
|
|
|
* x2 = (X-1) / W *1024 |
2268
|
|
|
* y2 = (Y-1) / H *256 |
2269
|
|
|
* |
2270
|
|
|
* Where: X is distance from the left side of the underlying cell |
2271
|
|
|
* Y is distance from the top of the underlying cell |
2272
|
|
|
* W is the width of the cell |
2273
|
|
|
* H is the height of the cell |
2274
|
|
|
* The SDK incorrectly states that the height should be expressed as a |
2275
|
|
|
* percentage of 1024. |
2276
|
|
|
* |
2277
|
|
|
* @param int $col_start Col containing upper left corner of object |
2278
|
|
|
* @param int $row_start Row containing top left corner of object |
2279
|
|
|
* @param int $x1 Distance to left side of object |
2280
|
|
|
* @param int $y1 Distance to top of object |
2281
|
|
|
* @param int $width Width of image frame |
2282
|
|
|
* @param int $height Height of image frame |
2283
|
|
|
*/ |
2284
|
|
|
public function positionImage($col_start, $row_start, $x1, $y1, $width, $height) |
2285
|
|
|
{ |
2286
|
|
|
// Initialise end cell to the same as the start cell |
2287
|
|
|
$col_end = $col_start; // Col containing lower right corner of object |
2288
|
|
|
$row_end = $row_start; // Row containing bottom right corner of object |
2289
|
|
|
|
2290
|
|
|
// Zero the specified offset if greater than the cell dimensions |
2291
|
|
|
if ($x1 >= Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_start + 1))) { |
2292
|
|
|
$x1 = 0; |
2293
|
|
|
} |
2294
|
|
|
if ($y1 >= Xls::sizeRow($this->phpSheet, $row_start + 1)) { |
2295
|
|
|
$y1 = 0; |
2296
|
|
|
} |
2297
|
|
|
|
2298
|
|
|
$width = $width + $x1 - 1; |
2299
|
|
|
$height = $height + $y1 - 1; |
2300
|
|
|
|
2301
|
|
|
// Subtract the underlying cell widths to find the end cell of the image |
2302
|
|
|
while ($width >= Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_end + 1))) { |
2303
|
|
|
$width -= Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_end + 1)); |
2304
|
|
|
++$col_end; |
2305
|
|
|
} |
2306
|
|
|
|
2307
|
|
|
// Subtract the underlying cell heights to find the end cell of the image |
2308
|
|
|
while ($height >= Xls::sizeRow($this->phpSheet, $row_end + 1)) { |
2309
|
|
|
$height -= Xls::sizeRow($this->phpSheet, $row_end + 1); |
2310
|
|
|
++$row_end; |
2311
|
|
|
} |
2312
|
|
|
|
2313
|
|
|
// Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell |
2314
|
|
|
// with zero eight or width. |
2315
|
|
|
// |
2316
|
|
|
if (Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_start + 1)) == 0) { |
2317
|
|
|
return; |
2318
|
|
|
} |
2319
|
|
|
if (Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_end + 1)) == 0) { |
2320
|
|
|
return; |
2321
|
|
|
} |
2322
|
|
|
if (Xls::sizeRow($this->phpSheet, $row_start + 1) == 0) { |
2323
|
|
|
return; |
2324
|
|
|
} |
2325
|
|
|
if (Xls::sizeRow($this->phpSheet, $row_end + 1) == 0) { |
2326
|
|
|
return; |
2327
|
|
|
} |
2328
|
|
|
|
2329
|
|
|
// Convert the pixel values to the percentage value expected by Excel |
2330
|
|
|
$x1 = $x1 / Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_start + 1)) * 1024; |
2331
|
|
|
$y1 = $y1 / Xls::sizeRow($this->phpSheet, $row_start + 1) * 256; |
2332
|
|
|
$x2 = $width / Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_end + 1)) * 1024; // Distance to right side of object |
2333
|
|
|
$y2 = $height / Xls::sizeRow($this->phpSheet, $row_end + 1) * 256; // Distance to bottom of object |
2334
|
|
|
|
2335
|
|
|
$this->writeObjPicture($col_start, $x1, $row_start, $y1, $col_end, $x2, $row_end, $y2); |
2336
|
|
|
} |
2337
|
|
|
|
2338
|
|
|
/** |
2339
|
|
|
* Store the OBJ record that precedes an IMDATA record. This could be generalise |
2340
|
|
|
* to support other Excel objects. |
2341
|
|
|
* |
2342
|
|
|
* @param int $colL Column containing upper left corner of object |
2343
|
|
|
* @param int $dxL Distance from left side of cell |
2344
|
|
|
* @param int $rwT Row containing top left corner of object |
2345
|
|
|
* @param int $dyT Distance from top of cell |
2346
|
|
|
* @param int $colR Column containing lower right corner of object |
2347
|
|
|
* @param int $dxR Distance from right of cell |
2348
|
|
|
* @param int $rwB Row containing bottom right corner of object |
2349
|
|
|
* @param int $dyB Distance from bottom of cell |
2350
|
|
|
*/ |
2351
|
|
|
private function writeObjPicture($colL, $dxL, $rwT, $dyT, $colR, $dxR, $rwB, $dyB) |
2352
|
|
|
{ |
2353
|
|
|
$record = 0x005d; // Record identifier |
2354
|
|
|
$length = 0x003c; // Bytes to follow |
2355
|
|
|
|
2356
|
|
|
$cObj = 0x0001; // Count of objects in file (set to 1) |
2357
|
|
|
$OT = 0x0008; // Object type. 8 = Picture |
2358
|
|
|
$id = 0x0001; // Object ID |
2359
|
|
|
$grbit = 0x0614; // Option flags |
2360
|
|
|
|
2361
|
|
|
$cbMacro = 0x0000; // Length of FMLA structure |
2362
|
|
|
$Reserved1 = 0x0000; // Reserved |
2363
|
|
|
$Reserved2 = 0x0000; // Reserved |
2364
|
|
|
|
2365
|
|
|
$icvBack = 0x09; // Background colour |
2366
|
|
|
$icvFore = 0x09; // Foreground colour |
2367
|
|
|
$fls = 0x00; // Fill pattern |
2368
|
|
|
$fAuto = 0x00; // Automatic fill |
2369
|
|
|
$icv = 0x08; // Line colour |
2370
|
|
|
$lns = 0xff; // Line style |
2371
|
|
|
$lnw = 0x01; // Line weight |
2372
|
|
|
$fAutoB = 0x00; // Automatic border |
2373
|
|
|
$frs = 0x0000; // Frame style |
2374
|
|
|
$cf = 0x0009; // Image format, 9 = bitmap |
2375
|
|
|
$Reserved3 = 0x0000; // Reserved |
2376
|
|
|
$cbPictFmla = 0x0000; // Length of FMLA structure |
2377
|
|
|
$Reserved4 = 0x0000; // Reserved |
2378
|
|
|
$grbit2 = 0x0001; // Option flags |
2379
|
|
|
$Reserved5 = 0x0000; // Reserved |
2380
|
|
|
|
2381
|
|
|
$header = pack('vv', $record, $length); |
2382
|
|
|
$data = pack('V', $cObj); |
2383
|
|
|
$data .= pack('v', $OT); |
2384
|
|
|
$data .= pack('v', $id); |
2385
|
|
|
$data .= pack('v', $grbit); |
2386
|
|
|
$data .= pack('v', $colL); |
2387
|
|
|
$data .= pack('v', $dxL); |
2388
|
|
|
$data .= pack('v', $rwT); |
2389
|
|
|
$data .= pack('v', $dyT); |
2390
|
|
|
$data .= pack('v', $colR); |
2391
|
|
|
$data .= pack('v', $dxR); |
2392
|
|
|
$data .= pack('v', $rwB); |
2393
|
|
|
$data .= pack('v', $dyB); |
2394
|
|
|
$data .= pack('v', $cbMacro); |
2395
|
|
|
$data .= pack('V', $Reserved1); |
2396
|
|
|
$data .= pack('v', $Reserved2); |
2397
|
|
|
$data .= pack('C', $icvBack); |
2398
|
|
|
$data .= pack('C', $icvFore); |
2399
|
|
|
$data .= pack('C', $fls); |
2400
|
|
|
$data .= pack('C', $fAuto); |
2401
|
|
|
$data .= pack('C', $icv); |
2402
|
|
|
$data .= pack('C', $lns); |
2403
|
|
|
$data .= pack('C', $lnw); |
2404
|
|
|
$data .= pack('C', $fAutoB); |
2405
|
|
|
$data .= pack('v', $frs); |
2406
|
|
|
$data .= pack('V', $cf); |
2407
|
|
|
$data .= pack('v', $Reserved3); |
2408
|
|
|
$data .= pack('v', $cbPictFmla); |
2409
|
|
|
$data .= pack('v', $Reserved4); |
2410
|
|
|
$data .= pack('v', $grbit2); |
2411
|
|
|
$data .= pack('V', $Reserved5); |
2412
|
|
|
|
2413
|
|
|
$this->append($header . $data); |
2414
|
|
|
} |
2415
|
|
|
|
2416
|
|
|
/** |
2417
|
|
|
* Convert a GD-image into the internal format. |
2418
|
|
|
* |
2419
|
|
|
* @param resource $image The image to process |
2420
|
|
|
* |
2421
|
|
|
* @return array Array with data and properties of the bitmap |
2422
|
|
|
*/ |
2423
|
|
|
public function processBitmapGd($image) |
2424
|
|
|
{ |
2425
|
|
|
$width = imagesx($image); |
2426
|
|
|
$height = imagesy($image); |
2427
|
|
|
|
2428
|
|
|
$data = pack('Vvvvv', 0x000c, $width, $height, 0x01, 0x18); |
2429
|
|
|
for ($j = $height; --$j;) { |
2430
|
|
|
for ($i = 0; $i < $width; ++$i) { |
2431
|
|
|
$color = imagecolorsforindex($image, imagecolorat($image, $i, $j)); |
2432
|
|
|
foreach (['red', 'green', 'blue'] as $key) { |
2433
|
|
|
$color[$key] = $color[$key] + round((255 - $color[$key]) * $color['alpha'] / 127); |
2434
|
|
|
} |
2435
|
|
|
$data .= chr($color['blue']) . chr($color['green']) . chr($color['red']); |
2436
|
|
|
} |
2437
|
|
|
if (3 * $width % 4) { |
2438
|
|
|
$data .= str_repeat("\x00", 4 - 3 * $width % 4); |
2439
|
|
|
} |
2440
|
|
|
} |
2441
|
|
|
|
2442
|
|
|
return [$width, $height, strlen($data), $data]; |
2443
|
|
|
} |
2444
|
|
|
|
2445
|
|
|
/** |
2446
|
|
|
* Convert a 24 bit bitmap into the modified internal format used by Windows. |
2447
|
|
|
* This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the |
2448
|
|
|
* MSDN library. |
2449
|
|
|
* |
2450
|
|
|
* @param string $bitmap The bitmap to process |
2451
|
|
|
* |
2452
|
|
|
* @return array Array with data and properties of the bitmap |
2453
|
|
|
*/ |
2454
|
|
|
public function processBitmap($bitmap) |
2455
|
|
|
{ |
2456
|
|
|
// Open file. |
2457
|
|
|
$bmp_fd = @fopen($bitmap, 'rb'); |
2458
|
|
|
if (!$bmp_fd) { |
2459
|
|
|
throw new WriterException("Couldn't import $bitmap"); |
2460
|
|
|
} |
2461
|
|
|
|
2462
|
|
|
// Slurp the file into a string. |
2463
|
|
|
$data = fread($bmp_fd, filesize($bitmap)); |
2464
|
|
|
|
2465
|
|
|
// Check that the file is big enough to be a bitmap. |
2466
|
|
|
if (strlen($data) <= 0x36) { |
2467
|
|
|
throw new WriterException("$bitmap doesn't contain enough data.\n"); |
2468
|
|
|
} |
2469
|
|
|
|
2470
|
|
|
// The first 2 bytes are used to identify the bitmap. |
2471
|
|
|
$identity = unpack('A2ident', $data); |
2472
|
|
|
if ($identity['ident'] != 'BM') { |
2473
|
|
|
throw new WriterException("$bitmap doesn't appear to be a valid bitmap image.\n"); |
2474
|
|
|
} |
2475
|
|
|
|
2476
|
|
|
// Remove bitmap data: ID. |
2477
|
|
|
$data = substr($data, 2); |
2478
|
|
|
|
2479
|
|
|
// Read and remove the bitmap size. This is more reliable than reading |
2480
|
|
|
// the data size at offset 0x22. |
2481
|
|
|
// |
2482
|
|
|
$size_array = unpack('Vsa', substr($data, 0, 4)); |
2483
|
|
|
$size = $size_array['sa']; |
2484
|
|
|
$data = substr($data, 4); |
2485
|
|
|
$size -= 0x36; // Subtract size of bitmap header. |
2486
|
|
|
$size += 0x0C; // Add size of BIFF header. |
2487
|
|
|
|
2488
|
|
|
// Remove bitmap data: reserved, offset, header length. |
2489
|
|
|
$data = substr($data, 12); |
2490
|
|
|
|
2491
|
|
|
// Read and remove the bitmap width and height. Verify the sizes. |
2492
|
|
|
$width_and_height = unpack('V2', substr($data, 0, 8)); |
2493
|
|
|
$width = $width_and_height[1]; |
2494
|
|
|
$height = $width_and_height[2]; |
2495
|
|
|
$data = substr($data, 8); |
2496
|
|
|
if ($width > 0xFFFF) { |
2497
|
|
|
throw new WriterException("$bitmap: largest image width supported is 65k.\n"); |
2498
|
|
|
} |
2499
|
|
|
if ($height > 0xFFFF) { |
2500
|
|
|
throw new WriterException("$bitmap: largest image height supported is 65k.\n"); |
2501
|
|
|
} |
2502
|
|
|
|
2503
|
|
|
// Read and remove the bitmap planes and bpp data. Verify them. |
2504
|
|
|
$planes_and_bitcount = unpack('v2', substr($data, 0, 4)); |
2505
|
|
|
$data = substr($data, 4); |
2506
|
|
|
if ($planes_and_bitcount[2] != 24) { // Bitcount |
2507
|
|
|
throw new WriterException("$bitmap isn't a 24bit true color bitmap.\n"); |
2508
|
|
|
} |
2509
|
|
|
if ($planes_and_bitcount[1] != 1) { |
2510
|
|
|
throw new WriterException("$bitmap: only 1 plane supported in bitmap image.\n"); |
2511
|
|
|
} |
2512
|
|
|
|
2513
|
|
|
// Read and remove the bitmap compression. Verify compression. |
2514
|
|
|
$compression = unpack('Vcomp', substr($data, 0, 4)); |
2515
|
|
|
$data = substr($data, 4); |
2516
|
|
|
|
2517
|
|
|
if ($compression['comp'] != 0) { |
2518
|
|
|
throw new WriterException("$bitmap: compression not supported in bitmap image.\n"); |
2519
|
|
|
} |
2520
|
|
|
|
2521
|
|
|
// Remove bitmap data: data size, hres, vres, colours, imp. colours. |
2522
|
|
|
$data = substr($data, 20); |
2523
|
|
|
|
2524
|
|
|
// Add the BITMAPCOREHEADER data |
2525
|
|
|
$header = pack('Vvvvv', 0x000c, $width, $height, 0x01, 0x18); |
2526
|
|
|
$data = $header . $data; |
2527
|
|
|
|
2528
|
|
|
return [$width, $height, $size, $data]; |
2529
|
|
|
} |
2530
|
|
|
|
2531
|
|
|
/** |
2532
|
|
|
* Store the window zoom factor. This should be a reduced fraction but for |
2533
|
|
|
* simplicity we will store all fractions with a numerator of 100. |
2534
|
|
|
*/ |
2535
|
39 |
View Code Duplication |
private function writeZoom() |
|
|
|
|
2536
|
|
|
{ |
2537
|
|
|
// If scale is 100 we don't need to write a record |
2538
|
39 |
|
if ($this->phpSheet->getSheetView()->getZoomScale() == 100) { |
2539
|
39 |
|
return; |
2540
|
|
|
} |
2541
|
|
|
|
2542
|
|
|
$record = 0x00A0; // Record identifier |
2543
|
|
|
$length = 0x0004; // Bytes to follow |
2544
|
|
|
|
2545
|
|
|
$header = pack('vv', $record, $length); |
2546
|
|
|
$data = pack('vv', $this->phpSheet->getSheetView()->getZoomScale(), 100); |
2547
|
|
|
$this->append($header . $data); |
2548
|
|
|
} |
2549
|
|
|
|
2550
|
|
|
/** |
2551
|
|
|
* Get Escher object. |
2552
|
|
|
* |
2553
|
|
|
* @return \PhpOffice\PhpSpreadsheet\Shared\Escher |
2554
|
|
|
*/ |
2555
|
|
|
public function getEscher() |
2556
|
|
|
{ |
2557
|
|
|
return $this->escher; |
2558
|
|
|
} |
2559
|
|
|
|
2560
|
|
|
/** |
2561
|
|
|
* Set Escher object. |
2562
|
|
|
* |
2563
|
|
|
* @param \PhpOffice\PhpSpreadsheet\Shared\Escher $pValue |
2564
|
|
|
*/ |
2565
|
10 |
|
public function setEscher(\PhpOffice\PhpSpreadsheet\Shared\Escher $pValue = null) |
2566
|
|
|
{ |
2567
|
10 |
|
$this->escher = $pValue; |
2568
|
10 |
|
} |
2569
|
|
|
|
2570
|
|
|
/** |
2571
|
|
|
* Write MSODRAWING record. |
2572
|
|
|
*/ |
2573
|
39 |
|
private function writeMsoDrawing() |
2574
|
|
|
{ |
2575
|
|
|
// write the Escher stream if necessary |
2576
|
39 |
|
if (isset($this->escher)) { |
2577
|
10 |
|
$writer = new Escher($this->escher); |
2578
|
10 |
|
$data = $writer->close(); |
2579
|
10 |
|
$spOffsets = $writer->getSpOffsets(); |
2580
|
10 |
|
$spTypes = $writer->getSpTypes(); |
2581
|
|
|
// write the neccesary MSODRAWING, OBJ records |
2582
|
|
|
|
2583
|
|
|
// split the Escher stream |
2584
|
10 |
|
$spOffsets[0] = 0; |
2585
|
10 |
|
$nm = count($spOffsets) - 1; // number of shapes excluding first shape |
2586
|
10 |
|
for ($i = 1; $i <= $nm; ++$i) { |
2587
|
|
|
// MSODRAWING record |
2588
|
10 |
|
$record = 0x00EC; // Record identifier |
2589
|
|
|
|
2590
|
|
|
// chunk of Escher stream for one shape |
2591
|
10 |
|
$dataChunk = substr($data, $spOffsets[$i - 1], $spOffsets[$i] - $spOffsets[$i - 1]); |
2592
|
|
|
|
2593
|
10 |
|
$length = strlen($dataChunk); |
2594
|
10 |
|
$header = pack('vv', $record, $length); |
2595
|
|
|
|
2596
|
10 |
|
$this->append($header . $dataChunk); |
2597
|
|
|
|
2598
|
|
|
// OBJ record |
2599
|
10 |
|
$record = 0x005D; // record identifier |
2600
|
10 |
|
$objData = ''; |
2601
|
|
|
|
2602
|
|
|
// ftCmo |
2603
|
10 |
|
if ($spTypes[$i] == 0x00C9) { |
2604
|
|
|
// Add ftCmo (common object data) subobject |
2605
|
|
|
$objData .= |
2606
|
3 |
|
pack( |
2607
|
3 |
|
'vvvvvVVV', |
2608
|
3 |
|
0x0015, // 0x0015 = ftCmo |
2609
|
3 |
|
0x0012, // length of ftCmo data |
2610
|
3 |
|
0x0014, // object type, 0x0014 = filter |
2611
|
3 |
|
$i, // object id number, Excel seems to use 1-based index, local for the sheet |
2612
|
3 |
|
0x2101, // option flags, 0x2001 is what OpenOffice.org uses |
2613
|
3 |
|
0, // reserved |
2614
|
3 |
|
0, // reserved |
2615
|
3 |
|
0 // reserved |
2616
|
|
|
); |
2617
|
|
|
|
2618
|
|
|
// Add ftSbs Scroll bar subobject |
2619
|
3 |
|
$objData .= pack('vv', 0x00C, 0x0014); |
2620
|
3 |
|
$objData .= pack('H*', '0000000000000000640001000A00000010000100'); |
2621
|
|
|
// Add ftLbsData (List box data) subobject |
2622
|
3 |
|
$objData .= pack('vv', 0x0013, 0x1FEE); |
2623
|
3 |
|
$objData .= pack('H*', '00000000010001030000020008005700'); |
2624
|
|
|
} else { |
2625
|
|
|
// Add ftCmo (common object data) subobject |
2626
|
|
|
$objData .= |
2627
|
7 |
|
pack( |
2628
|
7 |
|
'vvvvvVVV', |
2629
|
7 |
|
0x0015, // 0x0015 = ftCmo |
2630
|
7 |
|
0x0012, // length of ftCmo data |
2631
|
7 |
|
0x0008, // object type, 0x0008 = picture |
2632
|
7 |
|
$i, // object id number, Excel seems to use 1-based index, local for the sheet |
2633
|
7 |
|
0x6011, // option flags, 0x6011 is what OpenOffice.org uses |
2634
|
7 |
|
0, // reserved |
2635
|
7 |
|
0, // reserved |
2636
|
7 |
|
0 // reserved |
2637
|
|
|
); |
2638
|
|
|
} |
2639
|
|
|
|
2640
|
|
|
// ftEnd |
2641
|
|
|
$objData .= |
2642
|
10 |
|
pack( |
2643
|
10 |
|
'vv', |
2644
|
10 |
|
0x0000, // 0x0000 = ftEnd |
2645
|
10 |
|
0x0000 // length of ftEnd data |
2646
|
|
|
); |
2647
|
|
|
|
2648
|
10 |
|
$length = strlen($objData); |
2649
|
10 |
|
$header = pack('vv', $record, $length); |
2650
|
10 |
|
$this->append($header . $objData); |
2651
|
|
|
} |
2652
|
|
|
} |
2653
|
39 |
|
} |
2654
|
|
|
|
2655
|
|
|
/** |
2656
|
|
|
* Store the DATAVALIDATIONS and DATAVALIDATION records. |
2657
|
|
|
* |
2658
|
|
|
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception |
2659
|
|
|
*/ |
2660
|
39 |
|
private function writeDataValidity() |
2661
|
|
|
{ |
2662
|
|
|
// Datavalidation collection |
2663
|
39 |
|
$dataValidationCollection = $this->phpSheet->getDataValidationCollection(); |
2664
|
|
|
|
2665
|
|
|
// Write data validations? |
2666
|
39 |
|
if (!empty($dataValidationCollection)) { |
2667
|
|
|
// DATAVALIDATIONS record |
2668
|
2 |
|
$record = 0x01B2; // Record identifier |
2669
|
2 |
|
$length = 0x0012; // Bytes to follow |
2670
|
|
|
|
2671
|
2 |
|
$grbit = 0x0000; // Prompt box at cell, no cached validity data at DV records |
2672
|
2 |
|
$horPos = 0x00000000; // Horizontal position of prompt box, if fixed position |
2673
|
2 |
|
$verPos = 0x00000000; // Vertical position of prompt box, if fixed position |
2674
|
2 |
|
$objId = 0xFFFFFFFF; // Object identifier of drop down arrow object, or -1 if not visible |
2675
|
|
|
|
2676
|
2 |
|
$header = pack('vv', $record, $length); |
2677
|
2 |
|
$data = pack('vVVVV', $grbit, $horPos, $verPos, $objId, count($dataValidationCollection)); |
2678
|
2 |
|
$this->append($header . $data); |
2679
|
|
|
|
2680
|
|
|
// DATAVALIDATION records |
2681
|
2 |
|
$record = 0x01BE; // Record identifier |
2682
|
|
|
|
2683
|
2 |
|
foreach ($dataValidationCollection as $cellCoordinate => $dataValidation) { |
2684
|
|
|
// initialize record data |
2685
|
2 |
|
$data = ''; |
2686
|
|
|
|
2687
|
|
|
// options |
2688
|
2 |
|
$options = 0x00000000; |
2689
|
|
|
|
2690
|
|
|
// data type |
2691
|
2 |
|
$type = 0x00; |
2692
|
2 |
View Code Duplication |
switch ($dataValidation->getType()) { |
|
|
|
|
2693
|
2 |
|
case DataValidation::TYPE_NONE: |
2694
|
|
|
$type = 0x00; |
2695
|
|
|
|
2696
|
|
|
break; |
2697
|
2 |
|
case DataValidation::TYPE_WHOLE: |
2698
|
|
|
$type = 0x01; |
2699
|
|
|
|
2700
|
|
|
break; |
2701
|
2 |
|
case DataValidation::TYPE_DECIMAL: |
2702
|
|
|
$type = 0x02; |
2703
|
|
|
|
2704
|
|
|
break; |
2705
|
2 |
|
case DataValidation::TYPE_LIST: |
2706
|
|
|
$type = 0x03; |
2707
|
|
|
|
2708
|
|
|
break; |
2709
|
2 |
|
case DataValidation::TYPE_DATE: |
2710
|
|
|
$type = 0x04; |
2711
|
|
|
|
2712
|
|
|
break; |
2713
|
2 |
|
case DataValidation::TYPE_TIME: |
2714
|
|
|
$type = 0x05; |
2715
|
|
|
|
2716
|
|
|
break; |
2717
|
2 |
|
case DataValidation::TYPE_TEXTLENGTH: |
2718
|
|
|
$type = 0x06; |
2719
|
|
|
|
2720
|
|
|
break; |
2721
|
2 |
|
case DataValidation::TYPE_CUSTOM: |
2722
|
|
|
$type = 0x07; |
2723
|
|
|
|
2724
|
|
|
break; |
2725
|
|
|
} |
2726
|
|
|
|
2727
|
2 |
|
$options |= $type << 0; |
2728
|
|
|
|
2729
|
|
|
// error style |
2730
|
2 |
|
$errorStyle = 0x00; |
2731
|
2 |
View Code Duplication |
switch ($dataValidation->getErrorStyle()) { |
|
|
|
|
2732
|
2 |
|
case DataValidation::STYLE_STOP: |
2733
|
|
|
$errorStyle = 0x00; |
2734
|
|
|
|
2735
|
|
|
break; |
2736
|
2 |
|
case DataValidation::STYLE_WARNING: |
2737
|
|
|
$errorStyle = 0x01; |
2738
|
|
|
|
2739
|
|
|
break; |
2740
|
2 |
|
case DataValidation::STYLE_INFORMATION: |
2741
|
|
|
$errorStyle = 0x02; |
2742
|
|
|
|
2743
|
|
|
break; |
2744
|
|
|
} |
2745
|
|
|
|
2746
|
2 |
|
$options |= $errorStyle << 4; |
2747
|
|
|
|
2748
|
|
|
// explicit formula? |
2749
|
2 |
|
if ($type == 0x03 && preg_match('/^\".*\"$/', $dataValidation->getFormula1())) { |
2750
|
|
|
$options |= 0x01 << 7; |
2751
|
|
|
} |
2752
|
|
|
|
2753
|
|
|
// empty cells allowed |
2754
|
2 |
|
$options |= $dataValidation->getAllowBlank() << 8; |
2755
|
|
|
|
2756
|
|
|
// show drop down |
2757
|
2 |
|
$options |= (!$dataValidation->getShowDropDown()) << 9; |
2758
|
|
|
|
2759
|
|
|
// show input message |
2760
|
2 |
|
$options |= $dataValidation->getShowInputMessage() << 18; |
2761
|
|
|
|
2762
|
|
|
// show error message |
2763
|
2 |
|
$options |= $dataValidation->getShowErrorMessage() << 19; |
2764
|
|
|
|
2765
|
|
|
// condition operator |
2766
|
2 |
|
$operator = 0x00; |
2767
|
2 |
View Code Duplication |
switch ($dataValidation->getOperator()) { |
|
|
|
|
2768
|
2 |
|
case DataValidation::OPERATOR_BETWEEN: |
2769
|
2 |
|
$operator = 0x00; |
2770
|
|
|
|
2771
|
2 |
|
break; |
2772
|
|
|
case DataValidation::OPERATOR_NOTBETWEEN: |
2773
|
|
|
$operator = 0x01; |
2774
|
|
|
|
2775
|
|
|
break; |
2776
|
|
|
case DataValidation::OPERATOR_EQUAL: |
2777
|
|
|
$operator = 0x02; |
2778
|
|
|
|
2779
|
|
|
break; |
2780
|
|
|
case DataValidation::OPERATOR_NOTEQUAL: |
2781
|
|
|
$operator = 0x03; |
2782
|
|
|
|
2783
|
|
|
break; |
2784
|
|
|
case DataValidation::OPERATOR_GREATERTHAN: |
2785
|
|
|
$operator = 0x04; |
2786
|
|
|
|
2787
|
|
|
break; |
2788
|
|
|
case DataValidation::OPERATOR_LESSTHAN: |
2789
|
|
|
$operator = 0x05; |
2790
|
|
|
|
2791
|
|
|
break; |
2792
|
|
|
case DataValidation::OPERATOR_GREATERTHANOREQUAL: |
2793
|
|
|
$operator = 0x06; |
2794
|
|
|
|
2795
|
|
|
break; |
2796
|
|
|
case DataValidation::OPERATOR_LESSTHANOREQUAL: |
2797
|
|
|
$operator = 0x07; |
2798
|
|
|
|
2799
|
|
|
break; |
2800
|
|
|
} |
2801
|
|
|
|
2802
|
2 |
|
$options |= $operator << 20; |
2803
|
|
|
|
2804
|
2 |
|
$data = pack('V', $options); |
2805
|
|
|
|
2806
|
|
|
// prompt title |
2807
|
2 |
|
$promptTitle = $dataValidation->getPromptTitle() !== '' ? |
2808
|
2 |
|
$dataValidation->getPromptTitle() : chr(0); |
2809
|
2 |
|
$data .= StringHelper::UTF8toBIFF8UnicodeLong($promptTitle); |
2810
|
|
|
|
2811
|
|
|
// error title |
2812
|
2 |
|
$errorTitle = $dataValidation->getErrorTitle() !== '' ? |
2813
|
2 |
|
$dataValidation->getErrorTitle() : chr(0); |
2814
|
2 |
|
$data .= StringHelper::UTF8toBIFF8UnicodeLong($errorTitle); |
2815
|
|
|
|
2816
|
|
|
// prompt text |
2817
|
2 |
|
$prompt = $dataValidation->getPrompt() !== '' ? |
2818
|
2 |
|
$dataValidation->getPrompt() : chr(0); |
2819
|
2 |
|
$data .= StringHelper::UTF8toBIFF8UnicodeLong($prompt); |
2820
|
|
|
|
2821
|
|
|
// error text |
2822
|
2 |
|
$error = $dataValidation->getError() !== '' ? |
2823
|
2 |
|
$dataValidation->getError() : chr(0); |
2824
|
2 |
|
$data .= StringHelper::UTF8toBIFF8UnicodeLong($error); |
2825
|
|
|
|
2826
|
|
|
// formula 1 |
2827
|
|
|
try { |
2828
|
2 |
|
$formula1 = $dataValidation->getFormula1(); |
2829
|
2 |
|
if ($type == 0x03) { // list type |
2830
|
|
|
$formula1 = str_replace(',', chr(0), $formula1); |
2831
|
|
|
} |
2832
|
2 |
|
$this->parser->parse($formula1); |
2833
|
1 |
|
$formula1 = $this->parser->toReversePolish(); |
2834
|
1 |
|
$sz1 = strlen($formula1); |
2835
|
2 |
|
} catch (PhpSpreadsheetException $e) { |
2836
|
2 |
|
$sz1 = 0; |
2837
|
2 |
|
$formula1 = ''; |
2838
|
|
|
} |
2839
|
2 |
|
$data .= pack('vv', $sz1, 0x0000); |
2840
|
2 |
|
$data .= $formula1; |
2841
|
|
|
|
2842
|
|
|
// formula 2 |
2843
|
|
|
try { |
2844
|
2 |
|
$formula2 = $dataValidation->getFormula2(); |
2845
|
2 |
|
if ($formula2 === '') { |
2846
|
2 |
|
throw new WriterException('No formula2'); |
2847
|
|
|
} |
2848
|
1 |
|
$this->parser->parse($formula2); |
2849
|
|
|
$formula2 = $this->parser->toReversePolish(); |
2850
|
|
|
$sz2 = strlen($formula2); |
2851
|
2 |
|
} catch (PhpSpreadsheetException $e) { |
2852
|
2 |
|
$sz2 = 0; |
2853
|
2 |
|
$formula2 = ''; |
2854
|
|
|
} |
2855
|
2 |
|
$data .= pack('vv', $sz2, 0x0000); |
2856
|
2 |
|
$data .= $formula2; |
2857
|
|
|
|
2858
|
|
|
// cell range address list |
2859
|
2 |
|
$data .= pack('v', 0x0001); |
2860
|
2 |
|
$data .= $this->writeBIFF8CellRangeAddressFixed($cellCoordinate); |
2861
|
|
|
|
2862
|
2 |
|
$length = strlen($data); |
2863
|
2 |
|
$header = pack('vv', $record, $length); |
2864
|
|
|
|
2865
|
2 |
|
$this->append($header . $data); |
2866
|
|
|
} |
2867
|
|
|
} |
2868
|
39 |
|
} |
2869
|
|
|
|
2870
|
|
|
/** |
2871
|
|
|
* Map Error code. |
2872
|
|
|
* |
2873
|
|
|
* @param string $errorCode |
2874
|
|
|
* |
2875
|
|
|
* @return int |
2876
|
|
|
*/ |
2877
|
4 |
|
private static function mapErrorCode($errorCode) |
2878
|
|
|
{ |
2879
|
|
|
switch ($errorCode) { |
2880
|
4 |
|
case '#NULL!': |
2881
|
|
|
return 0x00; |
2882
|
4 |
|
case '#DIV/0!': |
2883
|
3 |
|
return 0x07; |
2884
|
2 |
|
case '#VALUE!': |
2885
|
1 |
|
return 0x0F; |
2886
|
1 |
|
case '#REF!': |
2887
|
|
|
return 0x17; |
2888
|
1 |
|
case '#NAME?': |
2889
|
|
|
return 0x1D; |
2890
|
1 |
|
case '#NUM!': |
2891
|
|
|
return 0x24; |
2892
|
1 |
|
case '#N/A': |
2893
|
1 |
|
return 0x2A; |
2894
|
|
|
} |
2895
|
|
|
|
2896
|
|
|
return 0; |
2897
|
|
|
} |
2898
|
|
|
|
2899
|
|
|
/** |
2900
|
|
|
* Write PLV Record. |
2901
|
|
|
*/ |
2902
|
39 |
|
private function writePageLayoutView() |
2903
|
|
|
{ |
2904
|
39 |
|
$record = 0x088B; // Record identifier |
2905
|
39 |
|
$length = 0x0010; // Bytes to follow |
2906
|
|
|
|
2907
|
39 |
|
$rt = 0x088B; // 2 |
2908
|
39 |
|
$grbitFrt = 0x0000; // 2 |
2909
|
39 |
|
$reserved = 0x0000000000000000; // 8 |
2910
|
39 |
|
$wScalvePLV = $this->phpSheet->getSheetView()->getZoomScale(); // 2 |
2911
|
|
|
|
2912
|
|
|
// The options flags that comprise $grbit |
2913
|
39 |
|
if ($this->phpSheet->getSheetView()->getView() == SheetView::SHEETVIEW_PAGE_LAYOUT) { |
2914
|
1 |
|
$fPageLayoutView = 1; |
2915
|
|
|
} else { |
2916
|
38 |
|
$fPageLayoutView = 0; |
2917
|
|
|
} |
2918
|
39 |
|
$fRulerVisible = 0; |
2919
|
39 |
|
$fWhitespaceHidden = 0; |
2920
|
|
|
|
2921
|
39 |
|
$grbit = $fPageLayoutView; // 2 |
2922
|
39 |
|
$grbit |= $fRulerVisible << 1; |
2923
|
39 |
|
$grbit |= $fWhitespaceHidden << 3; |
2924
|
|
|
|
2925
|
39 |
|
$header = pack('vv', $record, $length); |
2926
|
39 |
|
$data = pack('vvVVvv', $rt, $grbitFrt, 0x00000000, 0x00000000, $wScalvePLV, $grbit); |
2927
|
39 |
|
$this->append($header . $data); |
2928
|
39 |
|
} |
2929
|
|
|
|
2930
|
|
|
/** |
2931
|
|
|
* Write CFRule Record. |
2932
|
|
|
* |
2933
|
|
|
* @param Conditional $conditional |
2934
|
|
|
*/ |
2935
|
2 |
|
private function writeCFRule(Conditional $conditional) |
2936
|
|
|
{ |
2937
|
2 |
|
$record = 0x01B1; // Record identifier |
2938
|
|
|
|
2939
|
|
|
// $type : Type of the CF |
2940
|
|
|
// $operatorType : Comparison operator |
2941
|
2 |
|
if ($conditional->getConditionType() == Conditional::CONDITION_EXPRESSION) { |
2942
|
|
|
$type = 0x02; |
2943
|
|
|
$operatorType = 0x00; |
2944
|
2 |
|
} elseif ($conditional->getConditionType() == Conditional::CONDITION_CELLIS) { |
2945
|
2 |
|
$type = 0x01; |
2946
|
|
|
|
2947
|
2 |
|
switch ($conditional->getOperatorType()) { |
2948
|
2 |
|
case Conditional::OPERATOR_NONE: |
2949
|
|
|
$operatorType = 0x00; |
2950
|
|
|
|
2951
|
|
|
break; |
2952
|
2 |
|
case Conditional::OPERATOR_EQUAL: |
2953
|
|
|
$operatorType = 0x03; |
2954
|
|
|
|
2955
|
|
|
break; |
2956
|
2 |
|
case Conditional::OPERATOR_GREATERTHAN: |
2957
|
|
|
$operatorType = 0x05; |
2958
|
|
|
|
2959
|
|
|
break; |
2960
|
2 |
|
case Conditional::OPERATOR_GREATERTHANOREQUAL: |
2961
|
2 |
|
$operatorType = 0x07; |
2962
|
|
|
|
2963
|
2 |
|
break; |
2964
|
2 |
|
case Conditional::OPERATOR_LESSTHAN: |
2965
|
2 |
|
$operatorType = 0x06; |
2966
|
|
|
|
2967
|
2 |
|
break; |
2968
|
1 |
|
case Conditional::OPERATOR_LESSTHANOREQUAL: |
2969
|
|
|
$operatorType = 0x08; |
2970
|
|
|
|
2971
|
|
|
break; |
2972
|
1 |
|
case Conditional::OPERATOR_NOTEQUAL: |
2973
|
|
|
$operatorType = 0x04; |
2974
|
|
|
|
2975
|
|
|
break; |
2976
|
1 |
|
case Conditional::OPERATOR_BETWEEN: |
2977
|
1 |
|
$operatorType = 0x01; |
2978
|
|
|
|
2979
|
1 |
|
break; |
2980
|
|
|
// not OPERATOR_NOTBETWEEN 0x02 |
2981
|
|
|
} |
2982
|
|
|
} |
2983
|
|
|
|
2984
|
|
|
// $szValue1 : size of the formula data for first value or formula |
2985
|
|
|
// $szValue2 : size of the formula data for second value or formula |
2986
|
2 |
|
$arrConditions = $conditional->getConditions(); |
2987
|
2 |
|
$numConditions = count($arrConditions); |
2988
|
2 |
|
if ($numConditions == 1) { |
2989
|
2 |
|
$szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000); |
2990
|
2 |
|
$szValue2 = 0x0000; |
2991
|
2 |
|
$operand1 = pack('Cv', 0x1E, $arrConditions[0]); |
2992
|
2 |
|
$operand2 = null; |
2993
|
1 |
|
} elseif ($numConditions == 2 && ($conditional->getOperatorType() == Conditional::OPERATOR_BETWEEN)) { |
2994
|
1 |
|
$szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000); |
2995
|
1 |
|
$szValue2 = ($arrConditions[1] <= 65535 ? 3 : 0x0000); |
2996
|
1 |
|
$operand1 = pack('Cv', 0x1E, $arrConditions[0]); |
2997
|
1 |
|
$operand2 = pack('Cv', 0x1E, $arrConditions[1]); |
2998
|
|
|
} else { |
2999
|
|
|
$szValue1 = 0x0000; |
3000
|
|
|
$szValue2 = 0x0000; |
3001
|
|
|
$operand1 = null; |
3002
|
|
|
$operand2 = null; |
3003
|
|
|
} |
3004
|
|
|
|
3005
|
|
|
// $flags : Option flags |
3006
|
|
|
// Alignment |
3007
|
2 |
|
$bAlignHz = ($conditional->getStyle()->getAlignment()->getHorizontal() == null ? 1 : 0); |
3008
|
2 |
|
$bAlignVt = ($conditional->getStyle()->getAlignment()->getVertical() == null ? 1 : 0); |
3009
|
2 |
|
$bAlignWrapTx = ($conditional->getStyle()->getAlignment()->getWrapText() == false ? 1 : 0); |
3010
|
2 |
|
$bTxRotation = ($conditional->getStyle()->getAlignment()->getTextRotation() == null ? 1 : 0); |
3011
|
2 |
|
$bIndent = ($conditional->getStyle()->getAlignment()->getIndent() == 0 ? 1 : 0); |
3012
|
2 |
|
$bShrinkToFit = ($conditional->getStyle()->getAlignment()->getShrinkToFit() == false ? 1 : 0); |
3013
|
2 |
|
if ($bAlignHz == 0 || $bAlignVt == 0 || $bAlignWrapTx == 0 || $bTxRotation == 0 || $bIndent == 0 || $bShrinkToFit == 0) { |
3014
|
|
|
$bFormatAlign = 1; |
3015
|
|
|
} else { |
3016
|
2 |
|
$bFormatAlign = 0; |
3017
|
|
|
} |
3018
|
|
|
// Protection |
3019
|
2 |
|
$bProtLocked = ($conditional->getStyle()->getProtection()->getLocked() == null ? 1 : 0); |
3020
|
2 |
|
$bProtHidden = ($conditional->getStyle()->getProtection()->getHidden() == null ? 1 : 0); |
3021
|
2 |
|
if ($bProtLocked == 0 || $bProtHidden == 0) { |
3022
|
|
|
$bFormatProt = 1; |
3023
|
|
|
} else { |
3024
|
2 |
|
$bFormatProt = 0; |
3025
|
|
|
} |
3026
|
|
|
// Border |
3027
|
2 |
|
$bBorderLeft = ($conditional->getStyle()->getBorders()->getLeft()->getColor()->getARGB() == Color::COLOR_BLACK |
3028
|
2 |
|
&& $conditional->getStyle()->getBorders()->getLeft()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0); |
3029
|
2 |
|
$bBorderRight = ($conditional->getStyle()->getBorders()->getRight()->getColor()->getARGB() == Color::COLOR_BLACK |
3030
|
2 |
|
&& $conditional->getStyle()->getBorders()->getRight()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0); |
3031
|
2 |
|
$bBorderTop = ($conditional->getStyle()->getBorders()->getTop()->getColor()->getARGB() == Color::COLOR_BLACK |
3032
|
2 |
|
&& $conditional->getStyle()->getBorders()->getTop()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0); |
3033
|
2 |
|
$bBorderBottom = ($conditional->getStyle()->getBorders()->getBottom()->getColor()->getARGB() == Color::COLOR_BLACK |
3034
|
2 |
|
&& $conditional->getStyle()->getBorders()->getBottom()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0); |
3035
|
2 |
|
if ($bBorderLeft == 0 || $bBorderRight == 0 || $bBorderTop == 0 || $bBorderBottom == 0) { |
3036
|
|
|
$bFormatBorder = 1; |
3037
|
|
|
} else { |
3038
|
2 |
|
$bFormatBorder = 0; |
3039
|
|
|
} |
3040
|
|
|
// Pattern |
3041
|
2 |
|
$bFillStyle = ($conditional->getStyle()->getFill()->getFillType() == null ? 0 : 1); |
3042
|
2 |
|
$bFillColor = ($conditional->getStyle()->getFill()->getStartColor()->getARGB() == null ? 0 : 1); |
3043
|
2 |
|
$bFillColorBg = ($conditional->getStyle()->getFill()->getEndColor()->getARGB() == null ? 0 : 1); |
3044
|
2 |
|
if ($bFillStyle == 0 || $bFillColor == 0 || $bFillColorBg == 0) { |
3045
|
2 |
|
$bFormatFill = 1; |
3046
|
|
|
} else { |
3047
|
|
|
$bFormatFill = 0; |
3048
|
|
|
} |
3049
|
|
|
// Font |
3050
|
2 |
|
if ($conditional->getStyle()->getFont()->getName() != null |
3051
|
2 |
|
|| $conditional->getStyle()->getFont()->getSize() != null |
3052
|
2 |
|
|| $conditional->getStyle()->getFont()->getBold() != null |
3053
|
2 |
|
|| $conditional->getStyle()->getFont()->getItalic() != null |
3054
|
1 |
|
|| $conditional->getStyle()->getFont()->getSuperscript() != null |
3055
|
1 |
|
|| $conditional->getStyle()->getFont()->getSubscript() != null |
3056
|
1 |
|
|| $conditional->getStyle()->getFont()->getUnderline() != null |
3057
|
1 |
|
|| $conditional->getStyle()->getFont()->getStrikethrough() != null |
3058
|
2 |
|
|| $conditional->getStyle()->getFont()->getColor()->getARGB() != null) { |
3059
|
2 |
|
$bFormatFont = 1; |
3060
|
|
|
} else { |
3061
|
|
|
$bFormatFont = 0; |
3062
|
|
|
} |
3063
|
|
|
// Alignment |
3064
|
2 |
|
$flags = 0; |
3065
|
2 |
|
$flags |= (1 == $bAlignHz ? 0x00000001 : 0); |
3066
|
2 |
|
$flags |= (1 == $bAlignVt ? 0x00000002 : 0); |
3067
|
2 |
|
$flags |= (1 == $bAlignWrapTx ? 0x00000004 : 0); |
3068
|
2 |
|
$flags |= (1 == $bTxRotation ? 0x00000008 : 0); |
3069
|
|
|
// Justify last line flag |
3070
|
2 |
|
$flags |= (1 == 1 ? 0x00000010 : 0); |
3071
|
2 |
|
$flags |= (1 == $bIndent ? 0x00000020 : 0); |
3072
|
2 |
|
$flags |= (1 == $bShrinkToFit ? 0x00000040 : 0); |
3073
|
|
|
// Default |
3074
|
2 |
|
$flags |= (1 == 1 ? 0x00000080 : 0); |
3075
|
|
|
// Protection |
3076
|
2 |
|
$flags |= (1 == $bProtLocked ? 0x00000100 : 0); |
3077
|
2 |
|
$flags |= (1 == $bProtHidden ? 0x00000200 : 0); |
3078
|
|
|
// Border |
3079
|
2 |
|
$flags |= (1 == $bBorderLeft ? 0x00000400 : 0); |
3080
|
2 |
|
$flags |= (1 == $bBorderRight ? 0x00000800 : 0); |
3081
|
2 |
|
$flags |= (1 == $bBorderTop ? 0x00001000 : 0); |
3082
|
2 |
|
$flags |= (1 == $bBorderBottom ? 0x00002000 : 0); |
3083
|
2 |
|
$flags |= (1 == 1 ? 0x00004000 : 0); // Top left to Bottom right border |
3084
|
2 |
|
$flags |= (1 == 1 ? 0x00008000 : 0); // Bottom left to Top right border |
3085
|
|
|
// Pattern |
3086
|
2 |
|
$flags |= (1 == $bFillStyle ? 0x00010000 : 0); |
3087
|
2 |
|
$flags |= (1 == $bFillColor ? 0x00020000 : 0); |
3088
|
2 |
|
$flags |= (1 == $bFillColorBg ? 0x00040000 : 0); |
3089
|
2 |
|
$flags |= (1 == 1 ? 0x00380000 : 0); |
3090
|
|
|
// Font |
3091
|
2 |
|
$flags |= (1 == $bFormatFont ? 0x04000000 : 0); |
3092
|
|
|
// Alignment: |
3093
|
2 |
|
$flags |= (1 == $bFormatAlign ? 0x08000000 : 0); |
3094
|
|
|
// Border |
3095
|
2 |
|
$flags |= (1 == $bFormatBorder ? 0x10000000 : 0); |
3096
|
|
|
// Pattern |
3097
|
2 |
|
$flags |= (1 == $bFormatFill ? 0x20000000 : 0); |
3098
|
|
|
// Protection |
3099
|
2 |
|
$flags |= (1 == $bFormatProt ? 0x40000000 : 0); |
3100
|
|
|
// Text direction |
3101
|
2 |
|
$flags |= (1 == 0 ? 0x80000000 : 0); |
3102
|
|
|
|
3103
|
|
|
// Data Blocks |
3104
|
2 |
|
if ($bFormatFont == 1) { |
3105
|
|
|
// Font Name |
3106
|
2 |
|
if ($conditional->getStyle()->getFont()->getName() == null) { |
3107
|
2 |
|
$dataBlockFont = pack('VVVVVVVV', 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000); |
3108
|
2 |
|
$dataBlockFont .= pack('VVVVVVVV', 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000); |
3109
|
|
|
} else { |
3110
|
|
|
$dataBlockFont = StringHelper::UTF8toBIFF8UnicodeLong($conditional->getStyle()->getFont()->getName()); |
3111
|
|
|
} |
3112
|
|
|
// Font Size |
3113
|
2 |
|
if ($conditional->getStyle()->getFont()->getSize() == null) { |
3114
|
2 |
|
$dataBlockFont .= pack('V', 20 * 11); |
3115
|
|
|
} else { |
3116
|
|
|
$dataBlockFont .= pack('V', 20 * $conditional->getStyle()->getFont()->getSize()); |
3117
|
|
|
} |
3118
|
|
|
// Font Options |
3119
|
2 |
|
$dataBlockFont .= pack('V', 0); |
3120
|
|
|
// Font weight |
3121
|
2 |
|
if ($conditional->getStyle()->getFont()->getBold() == true) { |
3122
|
1 |
|
$dataBlockFont .= pack('v', 0x02BC); |
3123
|
|
|
} else { |
3124
|
2 |
|
$dataBlockFont .= pack('v', 0x0190); |
3125
|
|
|
} |
3126
|
|
|
// Escapement type |
3127
|
2 |
|
if ($conditional->getStyle()->getFont()->getSubscript() == true) { |
3128
|
|
|
$dataBlockFont .= pack('v', 0x02); |
3129
|
|
|
$fontEscapement = 0; |
3130
|
2 |
|
} elseif ($conditional->getStyle()->getFont()->getSuperscript() == true) { |
3131
|
|
|
$dataBlockFont .= pack('v', 0x01); |
3132
|
|
|
$fontEscapement = 0; |
3133
|
|
|
} else { |
3134
|
2 |
|
$dataBlockFont .= pack('v', 0x00); |
3135
|
2 |
|
$fontEscapement = 1; |
3136
|
|
|
} |
3137
|
|
|
// Underline type |
3138
|
2 |
|
switch ($conditional->getStyle()->getFont()->getUnderline()) { |
3139
|
2 |
|
case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_NONE: |
3140
|
|
|
$dataBlockFont .= pack('C', 0x00); |
3141
|
|
|
$fontUnderline = 0; |
3142
|
|
|
|
3143
|
|
|
break; |
3144
|
2 |
|
case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE: |
3145
|
|
|
$dataBlockFont .= pack('C', 0x02); |
3146
|
|
|
$fontUnderline = 0; |
3147
|
|
|
|
3148
|
|
|
break; |
3149
|
2 |
|
case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLEACCOUNTING: |
3150
|
|
|
$dataBlockFont .= pack('C', 0x22); |
3151
|
|
|
$fontUnderline = 0; |
3152
|
|
|
|
3153
|
|
|
break; |
3154
|
2 |
|
case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE: |
3155
|
|
|
$dataBlockFont .= pack('C', 0x01); |
3156
|
|
|
$fontUnderline = 0; |
3157
|
|
|
|
3158
|
|
|
break; |
3159
|
2 |
|
case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLEACCOUNTING: |
3160
|
|
|
$dataBlockFont .= pack('C', 0x21); |
3161
|
|
|
$fontUnderline = 0; |
3162
|
|
|
|
3163
|
|
|
break; |
3164
|
|
|
default: |
3165
|
2 |
|
$dataBlockFont .= pack('C', 0x00); |
3166
|
2 |
|
$fontUnderline = 1; |
3167
|
|
|
|
3168
|
2 |
|
break; |
3169
|
|
|
} |
3170
|
|
|
// Not used (3) |
3171
|
2 |
|
$dataBlockFont .= pack('vC', 0x0000, 0x00); |
3172
|
|
|
// Font color index |
3173
|
2 |
View Code Duplication |
switch ($conditional->getStyle()->getFont()->getColor()->getRGB()) { |
|
|
|
|
3174
|
2 |
|
case '000000': |
3175
|
|
|
$colorIdx = 0x08; |
3176
|
|
|
|
3177
|
|
|
break; |
3178
|
2 |
|
case 'FFFFFF': |
3179
|
|
|
$colorIdx = 0x09; |
3180
|
|
|
|
3181
|
|
|
break; |
3182
|
2 |
|
case 'FF0000': |
3183
|
2 |
|
$colorIdx = 0x0A; |
3184
|
|
|
|
3185
|
2 |
|
break; |
3186
|
2 |
|
case '00FF00': |
3187
|
2 |
|
$colorIdx = 0x0B; |
3188
|
|
|
|
3189
|
2 |
|
break; |
3190
|
1 |
|
case '0000FF': |
3191
|
|
|
$colorIdx = 0x0C; |
3192
|
|
|
|
3193
|
|
|
break; |
3194
|
1 |
|
case 'FFFF00': |
3195
|
1 |
|
$colorIdx = 0x0D; |
3196
|
|
|
|
3197
|
1 |
|
break; |
3198
|
|
|
case 'FF00FF': |
3199
|
|
|
$colorIdx = 0x0E; |
3200
|
|
|
|
3201
|
|
|
break; |
3202
|
|
|
case '00FFFF': |
3203
|
|
|
$colorIdx = 0x0F; |
3204
|
|
|
|
3205
|
|
|
break; |
3206
|
|
|
case '800000': |
3207
|
|
|
$colorIdx = 0x10; |
3208
|
|
|
|
3209
|
|
|
break; |
3210
|
|
|
case '008000': |
3211
|
|
|
$colorIdx = 0x11; |
3212
|
|
|
|
3213
|
|
|
break; |
3214
|
|
|
case '000080': |
3215
|
|
|
$colorIdx = 0x12; |
3216
|
|
|
|
3217
|
|
|
break; |
3218
|
|
|
case '808000': |
3219
|
|
|
$colorIdx = 0x13; |
3220
|
|
|
|
3221
|
|
|
break; |
3222
|
|
|
case '800080': |
3223
|
|
|
$colorIdx = 0x14; |
3224
|
|
|
|
3225
|
|
|
break; |
3226
|
|
|
case '008080': |
3227
|
|
|
$colorIdx = 0x15; |
3228
|
|
|
|
3229
|
|
|
break; |
3230
|
|
|
case 'C0C0C0': |
3231
|
|
|
$colorIdx = 0x16; |
3232
|
|
|
|
3233
|
|
|
break; |
3234
|
|
|
case '808080': |
3235
|
|
|
$colorIdx = 0x17; |
3236
|
|
|
|
3237
|
|
|
break; |
3238
|
|
|
case '9999FF': |
3239
|
|
|
$colorIdx = 0x18; |
3240
|
|
|
|
3241
|
|
|
break; |
3242
|
|
|
case '993366': |
3243
|
|
|
$colorIdx = 0x19; |
3244
|
|
|
|
3245
|
|
|
break; |
3246
|
|
|
case 'FFFFCC': |
3247
|
|
|
$colorIdx = 0x1A; |
3248
|
|
|
|
3249
|
|
|
break; |
3250
|
|
|
case 'CCFFFF': |
3251
|
|
|
$colorIdx = 0x1B; |
3252
|
|
|
|
3253
|
|
|
break; |
3254
|
|
|
case '660066': |
3255
|
|
|
$colorIdx = 0x1C; |
3256
|
|
|
|
3257
|
|
|
break; |
3258
|
|
|
case 'FF8080': |
3259
|
|
|
$colorIdx = 0x1D; |
3260
|
|
|
|
3261
|
|
|
break; |
3262
|
|
|
case '0066CC': |
3263
|
|
|
$colorIdx = 0x1E; |
3264
|
|
|
|
3265
|
|
|
break; |
3266
|
|
|
case 'CCCCFF': |
3267
|
|
|
$colorIdx = 0x1F; |
3268
|
|
|
|
3269
|
|
|
break; |
3270
|
|
|
case '000080': |
3271
|
|
|
$colorIdx = 0x20; |
3272
|
|
|
|
3273
|
|
|
break; |
3274
|
|
|
case 'FF00FF': |
3275
|
|
|
$colorIdx = 0x21; |
3276
|
|
|
|
3277
|
|
|
break; |
3278
|
|
|
case 'FFFF00': |
3279
|
|
|
$colorIdx = 0x22; |
3280
|
|
|
|
3281
|
|
|
break; |
3282
|
|
|
case '00FFFF': |
3283
|
|
|
$colorIdx = 0x23; |
3284
|
|
|
|
3285
|
|
|
break; |
3286
|
|
|
case '800080': |
3287
|
|
|
$colorIdx = 0x24; |
3288
|
|
|
|
3289
|
|
|
break; |
3290
|
|
|
case '800000': |
3291
|
|
|
$colorIdx = 0x25; |
3292
|
|
|
|
3293
|
|
|
break; |
3294
|
|
|
case '008080': |
3295
|
|
|
$colorIdx = 0x26; |
3296
|
|
|
|
3297
|
|
|
break; |
3298
|
|
|
case '0000FF': |
3299
|
|
|
$colorIdx = 0x27; |
3300
|
|
|
|
3301
|
|
|
break; |
3302
|
|
|
case '00CCFF': |
3303
|
|
|
$colorIdx = 0x28; |
3304
|
|
|
|
3305
|
|
|
break; |
3306
|
|
|
case 'CCFFFF': |
3307
|
|
|
$colorIdx = 0x29; |
3308
|
|
|
|
3309
|
|
|
break; |
3310
|
|
|
case 'CCFFCC': |
3311
|
|
|
$colorIdx = 0x2A; |
3312
|
|
|
|
3313
|
|
|
break; |
3314
|
|
|
case 'FFFF99': |
3315
|
|
|
$colorIdx = 0x2B; |
3316
|
|
|
|
3317
|
|
|
break; |
3318
|
|
|
case '99CCFF': |
3319
|
|
|
$colorIdx = 0x2C; |
3320
|
|
|
|
3321
|
|
|
break; |
3322
|
|
|
case 'FF99CC': |
3323
|
|
|
$colorIdx = 0x2D; |
3324
|
|
|
|
3325
|
|
|
break; |
3326
|
|
|
case 'CC99FF': |
3327
|
|
|
$colorIdx = 0x2E; |
3328
|
|
|
|
3329
|
|
|
break; |
3330
|
|
|
case 'FFCC99': |
3331
|
|
|
$colorIdx = 0x2F; |
3332
|
|
|
|
3333
|
|
|
break; |
3334
|
|
|
case '3366FF': |
3335
|
|
|
$colorIdx = 0x30; |
3336
|
|
|
|
3337
|
|
|
break; |
3338
|
|
|
case '33CCCC': |
3339
|
|
|
$colorIdx = 0x31; |
3340
|
|
|
|
3341
|
|
|
break; |
3342
|
|
|
case '99CC00': |
3343
|
|
|
$colorIdx = 0x32; |
3344
|
|
|
|
3345
|
|
|
break; |
3346
|
|
|
case 'FFCC00': |
3347
|
|
|
$colorIdx = 0x33; |
3348
|
|
|
|
3349
|
|
|
break; |
3350
|
|
|
case 'FF9900': |
3351
|
|
|
$colorIdx = 0x34; |
3352
|
|
|
|
3353
|
|
|
break; |
3354
|
|
|
case 'FF6600': |
3355
|
|
|
$colorIdx = 0x35; |
3356
|
|
|
|
3357
|
|
|
break; |
3358
|
|
|
case '666699': |
3359
|
|
|
$colorIdx = 0x36; |
3360
|
|
|
|
3361
|
|
|
break; |
3362
|
|
|
case '969696': |
3363
|
|
|
$colorIdx = 0x37; |
3364
|
|
|
|
3365
|
|
|
break; |
3366
|
|
|
case '003366': |
3367
|
|
|
$colorIdx = 0x38; |
3368
|
|
|
|
3369
|
|
|
break; |
3370
|
|
|
case '339966': |
3371
|
|
|
$colorIdx = 0x39; |
3372
|
|
|
|
3373
|
|
|
break; |
3374
|
|
|
case '003300': |
3375
|
|
|
$colorIdx = 0x3A; |
3376
|
|
|
|
3377
|
|
|
break; |
3378
|
|
|
case '333300': |
3379
|
|
|
$colorIdx = 0x3B; |
3380
|
|
|
|
3381
|
|
|
break; |
3382
|
|
|
case '993300': |
3383
|
|
|
$colorIdx = 0x3C; |
3384
|
|
|
|
3385
|
|
|
break; |
3386
|
|
|
case '993366': |
3387
|
|
|
$colorIdx = 0x3D; |
3388
|
|
|
|
3389
|
|
|
break; |
3390
|
|
|
case '333399': |
3391
|
|
|
$colorIdx = 0x3E; |
3392
|
|
|
|
3393
|
|
|
break; |
3394
|
|
|
case '333333': |
3395
|
|
|
$colorIdx = 0x3F; |
3396
|
|
|
|
3397
|
|
|
break; |
3398
|
|
|
default: |
3399
|
|
|
$colorIdx = 0x00; |
3400
|
|
|
|
3401
|
|
|
break; |
3402
|
|
|
} |
3403
|
2 |
|
$dataBlockFont .= pack('V', $colorIdx); |
3404
|
|
|
// Not used (4) |
3405
|
2 |
|
$dataBlockFont .= pack('V', 0x00000000); |
3406
|
|
|
// Options flags for modified font attributes |
3407
|
2 |
|
$optionsFlags = 0; |
3408
|
2 |
|
$optionsFlagsBold = ($conditional->getStyle()->getFont()->getBold() == null ? 1 : 0); |
3409
|
2 |
|
$optionsFlags |= (1 == $optionsFlagsBold ? 0x00000002 : 0); |
3410
|
2 |
|
$optionsFlags |= (1 == 1 ? 0x00000008 : 0); |
3411
|
2 |
|
$optionsFlags |= (1 == 1 ? 0x00000010 : 0); |
3412
|
2 |
|
$optionsFlags |= (1 == 0 ? 0x00000020 : 0); |
3413
|
2 |
|
$optionsFlags |= (1 == 1 ? 0x00000080 : 0); |
3414
|
2 |
|
$dataBlockFont .= pack('V', $optionsFlags); |
3415
|
|
|
// Escapement type |
3416
|
2 |
|
$dataBlockFont .= pack('V', $fontEscapement); |
3417
|
|
|
// Underline type |
3418
|
2 |
|
$dataBlockFont .= pack('V', $fontUnderline); |
3419
|
|
|
// Always |
3420
|
2 |
|
$dataBlockFont .= pack('V', 0x00000000); |
3421
|
|
|
// Always |
3422
|
2 |
|
$dataBlockFont .= pack('V', 0x00000000); |
3423
|
|
|
// Not used (8) |
3424
|
2 |
|
$dataBlockFont .= pack('VV', 0x00000000, 0x00000000); |
3425
|
|
|
// Always |
3426
|
2 |
|
$dataBlockFont .= pack('v', 0x0001); |
3427
|
|
|
} |
3428
|
2 |
|
if ($bFormatAlign == 1) { |
3429
|
|
|
$blockAlign = 0; |
3430
|
|
|
// Alignment and text break |
3431
|
|
|
switch ($conditional->getStyle()->getAlignment()->getHorizontal()) { |
3432
|
|
|
case Alignment::HORIZONTAL_GENERAL: |
3433
|
|
|
$blockAlign = 0; |
3434
|
|
|
|
3435
|
|
|
break; |
3436
|
|
|
case Alignment::HORIZONTAL_LEFT: |
3437
|
|
|
$blockAlign = 1; |
3438
|
|
|
|
3439
|
|
|
break; |
3440
|
|
|
case Alignment::HORIZONTAL_RIGHT: |
3441
|
|
|
$blockAlign = 3; |
3442
|
|
|
|
3443
|
|
|
break; |
3444
|
|
|
case Alignment::HORIZONTAL_CENTER: |
3445
|
|
|
$blockAlign = 2; |
3446
|
|
|
|
3447
|
|
|
break; |
3448
|
|
|
case Alignment::HORIZONTAL_CENTER_CONTINUOUS: |
3449
|
|
|
$blockAlign = 6; |
3450
|
|
|
|
3451
|
|
|
break; |
3452
|
|
|
case Alignment::HORIZONTAL_JUSTIFY: |
3453
|
|
|
$blockAlign = 5; |
3454
|
|
|
|
3455
|
|
|
break; |
3456
|
|
|
} |
3457
|
|
|
if ($conditional->getStyle()->getAlignment()->getWrapText() == true) { |
3458
|
|
|
$blockAlign |= 1 << 3; |
3459
|
|
|
} else { |
3460
|
|
|
$blockAlign |= 0 << 3; |
3461
|
|
|
} |
3462
|
|
|
switch ($conditional->getStyle()->getAlignment()->getVertical()) { |
3463
|
|
|
case Alignment::VERTICAL_BOTTOM: |
3464
|
|
|
$blockAlign = 2 << 4; |
3465
|
|
|
|
3466
|
|
|
break; |
3467
|
|
|
case Alignment::VERTICAL_TOP: |
3468
|
|
|
$blockAlign = 0 << 4; |
3469
|
|
|
|
3470
|
|
|
break; |
3471
|
|
|
case Alignment::VERTICAL_CENTER: |
3472
|
|
|
$blockAlign = 1 << 4; |
3473
|
|
|
|
3474
|
|
|
break; |
3475
|
|
|
case Alignment::VERTICAL_JUSTIFY: |
3476
|
|
|
$blockAlign = 3 << 4; |
3477
|
|
|
|
3478
|
|
|
break; |
3479
|
|
|
} |
3480
|
|
|
$blockAlign |= 0 << 7; |
3481
|
|
|
|
3482
|
|
|
// Text rotation angle |
3483
|
|
|
$blockRotation = $conditional->getStyle()->getAlignment()->getTextRotation(); |
3484
|
|
|
|
3485
|
|
|
// Indentation |
3486
|
|
|
$blockIndent = $conditional->getStyle()->getAlignment()->getIndent(); |
3487
|
|
|
if ($conditional->getStyle()->getAlignment()->getShrinkToFit() == true) { |
3488
|
|
|
$blockIndent |= 1 << 4; |
3489
|
|
|
} else { |
3490
|
|
|
$blockIndent |= 0 << 4; |
3491
|
|
|
} |
3492
|
|
|
$blockIndent |= 0 << 6; |
3493
|
|
|
|
3494
|
|
|
// Relative indentation |
3495
|
|
|
$blockIndentRelative = 255; |
3496
|
|
|
|
3497
|
|
|
$dataBlockAlign = pack('CCvvv', $blockAlign, $blockRotation, $blockIndent, $blockIndentRelative, 0x0000); |
3498
|
|
|
} |
3499
|
2 |
|
if ($bFormatBorder == 1) { |
3500
|
|
|
$blockLineStyle = 0; |
3501
|
|
|
switch ($conditional->getStyle()->getBorders()->getLeft()->getBorderStyle()) { |
3502
|
|
|
case Border::BORDER_NONE: |
3503
|
|
|
$blockLineStyle |= 0x00; |
3504
|
|
|
|
3505
|
|
|
break; |
3506
|
|
|
case Border::BORDER_THIN: |
3507
|
|
|
$blockLineStyle |= 0x01; |
3508
|
|
|
|
3509
|
|
|
break; |
3510
|
|
|
case Border::BORDER_MEDIUM: |
3511
|
|
|
$blockLineStyle |= 0x02; |
3512
|
|
|
|
3513
|
|
|
break; |
3514
|
|
|
case Border::BORDER_DASHED: |
3515
|
|
|
$blockLineStyle |= 0x03; |
3516
|
|
|
|
3517
|
|
|
break; |
3518
|
|
|
case Border::BORDER_DOTTED: |
3519
|
|
|
$blockLineStyle |= 0x04; |
3520
|
|
|
|
3521
|
|
|
break; |
3522
|
|
|
case Border::BORDER_THICK: |
3523
|
|
|
$blockLineStyle |= 0x05; |
3524
|
|
|
|
3525
|
|
|
break; |
3526
|
|
|
case Border::BORDER_DOUBLE: |
3527
|
|
|
$blockLineStyle |= 0x06; |
3528
|
|
|
|
3529
|
|
|
break; |
3530
|
|
|
case Border::BORDER_HAIR: |
3531
|
|
|
$blockLineStyle |= 0x07; |
3532
|
|
|
|
3533
|
|
|
break; |
3534
|
|
|
case Border::BORDER_MEDIUMDASHED: |
3535
|
|
|
$blockLineStyle |= 0x08; |
3536
|
|
|
|
3537
|
|
|
break; |
3538
|
|
|
case Border::BORDER_DASHDOT: |
3539
|
|
|
$blockLineStyle |= 0x09; |
3540
|
|
|
|
3541
|
|
|
break; |
3542
|
|
|
case Border::BORDER_MEDIUMDASHDOT: |
3543
|
|
|
$blockLineStyle |= 0x0A; |
3544
|
|
|
|
3545
|
|
|
break; |
3546
|
|
|
case Border::BORDER_DASHDOTDOT: |
3547
|
|
|
$blockLineStyle |= 0x0B; |
3548
|
|
|
|
3549
|
|
|
break; |
3550
|
|
|
case Border::BORDER_MEDIUMDASHDOTDOT: |
3551
|
|
|
$blockLineStyle |= 0x0C; |
3552
|
|
|
|
3553
|
|
|
break; |
3554
|
|
|
case Border::BORDER_SLANTDASHDOT: |
3555
|
|
|
$blockLineStyle |= 0x0D; |
3556
|
|
|
|
3557
|
|
|
break; |
3558
|
|
|
} |
3559
|
|
View Code Duplication |
switch ($conditional->getStyle()->getBorders()->getRight()->getBorderStyle()) { |
|
|
|
|
3560
|
|
|
case Border::BORDER_NONE: |
3561
|
|
|
$blockLineStyle |= 0x00 << 4; |
3562
|
|
|
|
3563
|
|
|
break; |
3564
|
|
|
case Border::BORDER_THIN: |
3565
|
|
|
$blockLineStyle |= 0x01 << 4; |
3566
|
|
|
|
3567
|
|
|
break; |
3568
|
|
|
case Border::BORDER_MEDIUM: |
3569
|
|
|
$blockLineStyle |= 0x02 << 4; |
3570
|
|
|
|
3571
|
|
|
break; |
3572
|
|
|
case Border::BORDER_DASHED: |
3573
|
|
|
$blockLineStyle |= 0x03 << 4; |
3574
|
|
|
|
3575
|
|
|
break; |
3576
|
|
|
case Border::BORDER_DOTTED: |
3577
|
|
|
$blockLineStyle |= 0x04 << 4; |
3578
|
|
|
|
3579
|
|
|
break; |
3580
|
|
|
case Border::BORDER_THICK: |
3581
|
|
|
$blockLineStyle |= 0x05 << 4; |
3582
|
|
|
|
3583
|
|
|
break; |
3584
|
|
|
case Border::BORDER_DOUBLE: |
3585
|
|
|
$blockLineStyle |= 0x06 << 4; |
3586
|
|
|
|
3587
|
|
|
break; |
3588
|
|
|
case Border::BORDER_HAIR: |
3589
|
|
|
$blockLineStyle |= 0x07 << 4; |
3590
|
|
|
|
3591
|
|
|
break; |
3592
|
|
|
case Border::BORDER_MEDIUMDASHED: |
3593
|
|
|
$blockLineStyle |= 0x08 << 4; |
3594
|
|
|
|
3595
|
|
|
break; |
3596
|
|
|
case Border::BORDER_DASHDOT: |
3597
|
|
|
$blockLineStyle |= 0x09 << 4; |
3598
|
|
|
|
3599
|
|
|
break; |
3600
|
|
|
case Border::BORDER_MEDIUMDASHDOT: |
3601
|
|
|
$blockLineStyle |= 0x0A << 4; |
3602
|
|
|
|
3603
|
|
|
break; |
3604
|
|
|
case Border::BORDER_DASHDOTDOT: |
3605
|
|
|
$blockLineStyle |= 0x0B << 4; |
3606
|
|
|
|
3607
|
|
|
break; |
3608
|
|
|
case Border::BORDER_MEDIUMDASHDOTDOT: |
3609
|
|
|
$blockLineStyle |= 0x0C << 4; |
3610
|
|
|
|
3611
|
|
|
break; |
3612
|
|
|
case Border::BORDER_SLANTDASHDOT: |
3613
|
|
|
$blockLineStyle |= 0x0D << 4; |
3614
|
|
|
|
3615
|
|
|
break; |
3616
|
|
|
} |
3617
|
|
View Code Duplication |
switch ($conditional->getStyle()->getBorders()->getTop()->getBorderStyle()) { |
|
|
|
|
3618
|
|
|
case Border::BORDER_NONE: |
3619
|
|
|
$blockLineStyle |= 0x00 << 8; |
3620
|
|
|
|
3621
|
|
|
break; |
3622
|
|
|
case Border::BORDER_THIN: |
3623
|
|
|
$blockLineStyle |= 0x01 << 8; |
3624
|
|
|
|
3625
|
|
|
break; |
3626
|
|
|
case Border::BORDER_MEDIUM: |
3627
|
|
|
$blockLineStyle |= 0x02 << 8; |
3628
|
|
|
|
3629
|
|
|
break; |
3630
|
|
|
case Border::BORDER_DASHED: |
3631
|
|
|
$blockLineStyle |= 0x03 << 8; |
3632
|
|
|
|
3633
|
|
|
break; |
3634
|
|
|
case Border::BORDER_DOTTED: |
3635
|
|
|
$blockLineStyle |= 0x04 << 8; |
3636
|
|
|
|
3637
|
|
|
break; |
3638
|
|
|
case Border::BORDER_THICK: |
3639
|
|
|
$blockLineStyle |= 0x05 << 8; |
3640
|
|
|
|
3641
|
|
|
break; |
3642
|
|
|
case Border::BORDER_DOUBLE: |
3643
|
|
|
$blockLineStyle |= 0x06 << 8; |
3644
|
|
|
|
3645
|
|
|
break; |
3646
|
|
|
case Border::BORDER_HAIR: |
3647
|
|
|
$blockLineStyle |= 0x07 << 8; |
3648
|
|
|
|
3649
|
|
|
break; |
3650
|
|
|
case Border::BORDER_MEDIUMDASHED: |
3651
|
|
|
$blockLineStyle |= 0x08 << 8; |
3652
|
|
|
|
3653
|
|
|
break; |
3654
|
|
|
case Border::BORDER_DASHDOT: |
3655
|
|
|
$blockLineStyle |= 0x09 << 8; |
3656
|
|
|
|
3657
|
|
|
break; |
3658
|
|
|
case Border::BORDER_MEDIUMDASHDOT: |
3659
|
|
|
$blockLineStyle |= 0x0A << 8; |
3660
|
|
|
|
3661
|
|
|
break; |
3662
|
|
|
case Border::BORDER_DASHDOTDOT: |
3663
|
|
|
$blockLineStyle |= 0x0B << 8; |
3664
|
|
|
|
3665
|
|
|
break; |
3666
|
|
|
case Border::BORDER_MEDIUMDASHDOTDOT: |
3667
|
|
|
$blockLineStyle |= 0x0C << 8; |
3668
|
|
|
|
3669
|
|
|
break; |
3670
|
|
|
case Border::BORDER_SLANTDASHDOT: |
3671
|
|
|
$blockLineStyle |= 0x0D << 8; |
3672
|
|
|
|
3673
|
|
|
break; |
3674
|
|
|
} |
3675
|
|
View Code Duplication |
switch ($conditional->getStyle()->getBorders()->getBottom()->getBorderStyle()) { |
|
|
|
|
3676
|
|
|
case Border::BORDER_NONE: |
3677
|
|
|
$blockLineStyle |= 0x00 << 12; |
3678
|
|
|
|
3679
|
|
|
break; |
3680
|
|
|
case Border::BORDER_THIN: |
3681
|
|
|
$blockLineStyle |= 0x01 << 12; |
3682
|
|
|
|
3683
|
|
|
break; |
3684
|
|
|
case Border::BORDER_MEDIUM: |
3685
|
|
|
$blockLineStyle |= 0x02 << 12; |
3686
|
|
|
|
3687
|
|
|
break; |
3688
|
|
|
case Border::BORDER_DASHED: |
3689
|
|
|
$blockLineStyle |= 0x03 << 12; |
3690
|
|
|
|
3691
|
|
|
break; |
3692
|
|
|
case Border::BORDER_DOTTED: |
3693
|
|
|
$blockLineStyle |= 0x04 << 12; |
3694
|
|
|
|
3695
|
|
|
break; |
3696
|
|
|
case Border::BORDER_THICK: |
3697
|
|
|
$blockLineStyle |= 0x05 << 12; |
3698
|
|
|
|
3699
|
|
|
break; |
3700
|
|
|
case Border::BORDER_DOUBLE: |
3701
|
|
|
$blockLineStyle |= 0x06 << 12; |
3702
|
|
|
|
3703
|
|
|
break; |
3704
|
|
|
case Border::BORDER_HAIR: |
3705
|
|
|
$blockLineStyle |= 0x07 << 12; |
3706
|
|
|
|
3707
|
|
|
break; |
3708
|
|
|
case Border::BORDER_MEDIUMDASHED: |
3709
|
|
|
$blockLineStyle |= 0x08 << 12; |
3710
|
|
|
|
3711
|
|
|
break; |
3712
|
|
|
case Border::BORDER_DASHDOT: |
3713
|
|
|
$blockLineStyle |= 0x09 << 12; |
3714
|
|
|
|
3715
|
|
|
break; |
3716
|
|
|
case Border::BORDER_MEDIUMDASHDOT: |
3717
|
|
|
$blockLineStyle |= 0x0A << 12; |
3718
|
|
|
|
3719
|
|
|
break; |
3720
|
|
|
case Border::BORDER_DASHDOTDOT: |
3721
|
|
|
$blockLineStyle |= 0x0B << 12; |
3722
|
|
|
|
3723
|
|
|
break; |
3724
|
|
|
case Border::BORDER_MEDIUMDASHDOTDOT: |
3725
|
|
|
$blockLineStyle |= 0x0C << 12; |
3726
|
|
|
|
3727
|
|
|
break; |
3728
|
|
|
case Border::BORDER_SLANTDASHDOT: |
3729
|
|
|
$blockLineStyle |= 0x0D << 12; |
3730
|
|
|
|
3731
|
|
|
break; |
3732
|
|
|
} |
3733
|
|
|
//@todo writeCFRule() => $blockLineStyle => Index Color for left line |
3734
|
|
|
//@todo writeCFRule() => $blockLineStyle => Index Color for right line |
3735
|
|
|
//@todo writeCFRule() => $blockLineStyle => Top-left to bottom-right on/off |
3736
|
|
|
//@todo writeCFRule() => $blockLineStyle => Bottom-left to top-right on/off |
3737
|
|
|
$blockColor = 0; |
3738
|
|
|
//@todo writeCFRule() => $blockColor => Index Color for top line |
3739
|
|
|
//@todo writeCFRule() => $blockColor => Index Color for bottom line |
3740
|
|
|
//@todo writeCFRule() => $blockColor => Index Color for diagonal line |
3741
|
|
View Code Duplication |
switch ($conditional->getStyle()->getBorders()->getDiagonal()->getBorderStyle()) { |
|
|
|
|
3742
|
|
|
case Border::BORDER_NONE: |
3743
|
|
|
$blockColor |= 0x00 << 21; |
3744
|
|
|
|
3745
|
|
|
break; |
3746
|
|
|
case Border::BORDER_THIN: |
3747
|
|
|
$blockColor |= 0x01 << 21; |
3748
|
|
|
|
3749
|
|
|
break; |
3750
|
|
|
case Border::BORDER_MEDIUM: |
3751
|
|
|
$blockColor |= 0x02 << 21; |
3752
|
|
|
|
3753
|
|
|
break; |
3754
|
|
|
case Border::BORDER_DASHED: |
3755
|
|
|
$blockColor |= 0x03 << 21; |
3756
|
|
|
|
3757
|
|
|
break; |
3758
|
|
|
case Border::BORDER_DOTTED: |
3759
|
|
|
$blockColor |= 0x04 << 21; |
3760
|
|
|
|
3761
|
|
|
break; |
3762
|
|
|
case Border::BORDER_THICK: |
3763
|
|
|
$blockColor |= 0x05 << 21; |
3764
|
|
|
|
3765
|
|
|
break; |
3766
|
|
|
case Border::BORDER_DOUBLE: |
3767
|
|
|
$blockColor |= 0x06 << 21; |
3768
|
|
|
|
3769
|
|
|
break; |
3770
|
|
|
case Border::BORDER_HAIR: |
3771
|
|
|
$blockColor |= 0x07 << 21; |
3772
|
|
|
|
3773
|
|
|
break; |
3774
|
|
|
case Border::BORDER_MEDIUMDASHED: |
3775
|
|
|
$blockColor |= 0x08 << 21; |
3776
|
|
|
|
3777
|
|
|
break; |
3778
|
|
|
case Border::BORDER_DASHDOT: |
3779
|
|
|
$blockColor |= 0x09 << 21; |
3780
|
|
|
|
3781
|
|
|
break; |
3782
|
|
|
case Border::BORDER_MEDIUMDASHDOT: |
3783
|
|
|
$blockColor |= 0x0A << 21; |
3784
|
|
|
|
3785
|
|
|
break; |
3786
|
|
|
case Border::BORDER_DASHDOTDOT: |
3787
|
|
|
$blockColor |= 0x0B << 21; |
3788
|
|
|
|
3789
|
|
|
break; |
3790
|
|
|
case Border::BORDER_MEDIUMDASHDOTDOT: |
3791
|
|
|
$blockColor |= 0x0C << 21; |
3792
|
|
|
|
3793
|
|
|
break; |
3794
|
|
|
case Border::BORDER_SLANTDASHDOT: |
3795
|
|
|
$blockColor |= 0x0D << 21; |
3796
|
|
|
|
3797
|
|
|
break; |
3798
|
|
|
} |
3799
|
|
|
$dataBlockBorder = pack('vv', $blockLineStyle, $blockColor); |
3800
|
|
|
} |
3801
|
2 |
|
if ($bFormatFill == 1) { |
3802
|
|
|
// Fill Patern Style |
3803
|
2 |
|
$blockFillPatternStyle = 0; |
3804
|
2 |
|
switch ($conditional->getStyle()->getFill()->getFillType()) { |
3805
|
2 |
|
case Fill::FILL_NONE: |
3806
|
|
|
$blockFillPatternStyle = 0x00; |
3807
|
|
|
|
3808
|
|
|
break; |
3809
|
2 |
|
case Fill::FILL_SOLID: |
3810
|
|
|
$blockFillPatternStyle = 0x01; |
3811
|
|
|
|
3812
|
|
|
break; |
3813
|
2 |
|
case Fill::FILL_PATTERN_MEDIUMGRAY: |
3814
|
|
|
$blockFillPatternStyle = 0x02; |
3815
|
|
|
|
3816
|
|
|
break; |
3817
|
2 |
|
case Fill::FILL_PATTERN_DARKGRAY: |
3818
|
|
|
$blockFillPatternStyle = 0x03; |
3819
|
|
|
|
3820
|
|
|
break; |
3821
|
2 |
|
case Fill::FILL_PATTERN_LIGHTGRAY: |
3822
|
|
|
$blockFillPatternStyle = 0x04; |
3823
|
|
|
|
3824
|
|
|
break; |
3825
|
2 |
|
case Fill::FILL_PATTERN_DARKHORIZONTAL: |
3826
|
|
|
$blockFillPatternStyle = 0x05; |
3827
|
|
|
|
3828
|
|
|
break; |
3829
|
2 |
|
case Fill::FILL_PATTERN_DARKVERTICAL: |
3830
|
|
|
$blockFillPatternStyle = 0x06; |
3831
|
|
|
|
3832
|
|
|
break; |
3833
|
2 |
|
case Fill::FILL_PATTERN_DARKDOWN: |
3834
|
|
|
$blockFillPatternStyle = 0x07; |
3835
|
|
|
|
3836
|
|
|
break; |
3837
|
2 |
|
case Fill::FILL_PATTERN_DARKUP: |
3838
|
|
|
$blockFillPatternStyle = 0x08; |
3839
|
|
|
|
3840
|
|
|
break; |
3841
|
2 |
|
case Fill::FILL_PATTERN_DARKGRID: |
3842
|
|
|
$blockFillPatternStyle = 0x09; |
3843
|
|
|
|
3844
|
|
|
break; |
3845
|
2 |
|
case Fill::FILL_PATTERN_DARKTRELLIS: |
3846
|
|
|
$blockFillPatternStyle = 0x0A; |
3847
|
|
|
|
3848
|
|
|
break; |
3849
|
2 |
|
case Fill::FILL_PATTERN_LIGHTHORIZONTAL: |
3850
|
|
|
$blockFillPatternStyle = 0x0B; |
3851
|
|
|
|
3852
|
|
|
break; |
3853
|
2 |
|
case Fill::FILL_PATTERN_LIGHTVERTICAL: |
3854
|
|
|
$blockFillPatternStyle = 0x0C; |
3855
|
|
|
|
3856
|
|
|
break; |
3857
|
2 |
|
case Fill::FILL_PATTERN_LIGHTDOWN: |
3858
|
|
|
$blockFillPatternStyle = 0x0D; |
3859
|
|
|
|
3860
|
|
|
break; |
3861
|
2 |
|
case Fill::FILL_PATTERN_LIGHTUP: |
3862
|
|
|
$blockFillPatternStyle = 0x0E; |
3863
|
|
|
|
3864
|
|
|
break; |
3865
|
2 |
|
case Fill::FILL_PATTERN_LIGHTGRID: |
3866
|
|
|
$blockFillPatternStyle = 0x0F; |
3867
|
|
|
|
3868
|
|
|
break; |
3869
|
2 |
|
case Fill::FILL_PATTERN_LIGHTTRELLIS: |
3870
|
|
|
$blockFillPatternStyle = 0x10; |
3871
|
|
|
|
3872
|
|
|
break; |
3873
|
2 |
|
case Fill::FILL_PATTERN_GRAY125: |
3874
|
|
|
$blockFillPatternStyle = 0x11; |
3875
|
|
|
|
3876
|
|
|
break; |
3877
|
2 |
|
case Fill::FILL_PATTERN_GRAY0625: |
3878
|
|
|
$blockFillPatternStyle = 0x12; |
3879
|
|
|
|
3880
|
|
|
break; |
3881
|
2 |
|
case Fill::FILL_GRADIENT_LINEAR: |
3882
|
|
|
$blockFillPatternStyle = 0x00; |
3883
|
|
|
|
3884
|
|
|
break; // does not exist in BIFF8 |
3885
|
2 |
|
case Fill::FILL_GRADIENT_PATH: |
3886
|
|
|
$blockFillPatternStyle = 0x00; |
3887
|
|
|
|
3888
|
|
|
break; // does not exist in BIFF8 |
3889
|
|
|
default: |
3890
|
2 |
|
$blockFillPatternStyle = 0x00; |
3891
|
|
|
|
3892
|
2 |
|
break; |
3893
|
|
|
} |
3894
|
|
|
// Color |
3895
|
2 |
View Code Duplication |
switch ($conditional->getStyle()->getFill()->getStartColor()->getRGB()) { |
|
|
|
|
3896
|
2 |
|
case '000000': |
3897
|
|
|
$colorIdxBg = 0x08; |
3898
|
|
|
|
3899
|
|
|
break; |
3900
|
2 |
|
case 'FFFFFF': |
3901
|
|
|
$colorIdxBg = 0x09; |
3902
|
|
|
|
3903
|
|
|
break; |
3904
|
2 |
|
case 'FF0000': |
3905
|
|
|
$colorIdxBg = 0x0A; |
3906
|
|
|
|
3907
|
|
|
break; |
3908
|
2 |
|
case '00FF00': |
3909
|
|
|
$colorIdxBg = 0x0B; |
3910
|
|
|
|
3911
|
|
|
break; |
3912
|
2 |
|
case '0000FF': |
3913
|
|
|
$colorIdxBg = 0x0C; |
3914
|
|
|
|
3915
|
|
|
break; |
3916
|
2 |
|
case 'FFFF00': |
3917
|
|
|
$colorIdxBg = 0x0D; |
3918
|
|
|
|
3919
|
|
|
break; |
3920
|
2 |
|
case 'FF00FF': |
3921
|
|
|
$colorIdxBg = 0x0E; |
3922
|
|
|
|
3923
|
|
|
break; |
3924
|
2 |
|
case '00FFFF': |
3925
|
|
|
$colorIdxBg = 0x0F; |
3926
|
|
|
|
3927
|
|
|
break; |
3928
|
2 |
|
case '800000': |
3929
|
|
|
$colorIdxBg = 0x10; |
3930
|
|
|
|
3931
|
|
|
break; |
3932
|
2 |
|
case '008000': |
3933
|
|
|
$colorIdxBg = 0x11; |
3934
|
|
|
|
3935
|
|
|
break; |
3936
|
2 |
|
case '000080': |
3937
|
|
|
$colorIdxBg = 0x12; |
3938
|
|
|
|
3939
|
|
|
break; |
3940
|
2 |
|
case '808000': |
3941
|
|
|
$colorIdxBg = 0x13; |
3942
|
|
|
|
3943
|
|
|
break; |
3944
|
2 |
|
case '800080': |
3945
|
|
|
$colorIdxBg = 0x14; |
3946
|
|
|
|
3947
|
|
|
break; |
3948
|
2 |
|
case '008080': |
3949
|
|
|
$colorIdxBg = 0x15; |
3950
|
|
|
|
3951
|
|
|
break; |
3952
|
2 |
|
case 'C0C0C0': |
3953
|
|
|
$colorIdxBg = 0x16; |
3954
|
|
|
|
3955
|
|
|
break; |
3956
|
2 |
|
case '808080': |
3957
|
|
|
$colorIdxBg = 0x17; |
3958
|
|
|
|
3959
|
|
|
break; |
3960
|
2 |
|
case '9999FF': |
3961
|
|
|
$colorIdxBg = 0x18; |
3962
|
|
|
|
3963
|
|
|
break; |
3964
|
2 |
|
case '993366': |
3965
|
|
|
$colorIdxBg = 0x19; |
3966
|
|
|
|
3967
|
|
|
break; |
3968
|
2 |
|
case 'FFFFCC': |
3969
|
|
|
$colorIdxBg = 0x1A; |
3970
|
|
|
|
3971
|
|
|
break; |
3972
|
2 |
|
case 'CCFFFF': |
3973
|
|
|
$colorIdxBg = 0x1B; |
3974
|
|
|
|
3975
|
|
|
break; |
3976
|
2 |
|
case '660066': |
3977
|
|
|
$colorIdxBg = 0x1C; |
3978
|
|
|
|
3979
|
|
|
break; |
3980
|
2 |
|
case 'FF8080': |
3981
|
|
|
$colorIdxBg = 0x1D; |
3982
|
|
|
|
3983
|
|
|
break; |
3984
|
2 |
|
case '0066CC': |
3985
|
|
|
$colorIdxBg = 0x1E; |
3986
|
|
|
|
3987
|
|
|
break; |
3988
|
2 |
|
case 'CCCCFF': |
3989
|
|
|
$colorIdxBg = 0x1F; |
3990
|
|
|
|
3991
|
|
|
break; |
3992
|
2 |
|
case '000080': |
3993
|
|
|
$colorIdxBg = 0x20; |
3994
|
|
|
|
3995
|
|
|
break; |
3996
|
2 |
|
case 'FF00FF': |
3997
|
|
|
$colorIdxBg = 0x21; |
3998
|
|
|
|
3999
|
|
|
break; |
4000
|
2 |
|
case 'FFFF00': |
4001
|
|
|
$colorIdxBg = 0x22; |
4002
|
|
|
|
4003
|
|
|
break; |
4004
|
2 |
|
case '00FFFF': |
4005
|
|
|
$colorIdxBg = 0x23; |
4006
|
|
|
|
4007
|
|
|
break; |
4008
|
2 |
|
case '800080': |
4009
|
|
|
$colorIdxBg = 0x24; |
4010
|
|
|
|
4011
|
|
|
break; |
4012
|
2 |
|
case '800000': |
4013
|
|
|
$colorIdxBg = 0x25; |
4014
|
|
|
|
4015
|
|
|
break; |
4016
|
2 |
|
case '008080': |
4017
|
|
|
$colorIdxBg = 0x26; |
4018
|
|
|
|
4019
|
|
|
break; |
4020
|
2 |
|
case '0000FF': |
4021
|
|
|
$colorIdxBg = 0x27; |
4022
|
|
|
|
4023
|
|
|
break; |
4024
|
2 |
|
case '00CCFF': |
4025
|
|
|
$colorIdxBg = 0x28; |
4026
|
|
|
|
4027
|
|
|
break; |
4028
|
2 |
|
case 'CCFFFF': |
4029
|
|
|
$colorIdxBg = 0x29; |
4030
|
|
|
|
4031
|
|
|
break; |
4032
|
2 |
|
case 'CCFFCC': |
4033
|
|
|
$colorIdxBg = 0x2A; |
4034
|
|
|
|
4035
|
|
|
break; |
4036
|
2 |
|
case 'FFFF99': |
4037
|
|
|
$colorIdxBg = 0x2B; |
4038
|
|
|
|
4039
|
|
|
break; |
4040
|
2 |
|
case '99CCFF': |
4041
|
|
|
$colorIdxBg = 0x2C; |
4042
|
|
|
|
4043
|
|
|
break; |
4044
|
2 |
|
case 'FF99CC': |
4045
|
|
|
$colorIdxBg = 0x2D; |
4046
|
|
|
|
4047
|
|
|
break; |
4048
|
2 |
|
case 'CC99FF': |
4049
|
|
|
$colorIdxBg = 0x2E; |
4050
|
|
|
|
4051
|
|
|
break; |
4052
|
2 |
|
case 'FFCC99': |
4053
|
|
|
$colorIdxBg = 0x2F; |
4054
|
|
|
|
4055
|
|
|
break; |
4056
|
2 |
|
case '3366FF': |
4057
|
|
|
$colorIdxBg = 0x30; |
4058
|
|
|
|
4059
|
|
|
break; |
4060
|
2 |
|
case '33CCCC': |
4061
|
|
|
$colorIdxBg = 0x31; |
4062
|
|
|
|
4063
|
|
|
break; |
4064
|
2 |
|
case '99CC00': |
4065
|
|
|
$colorIdxBg = 0x32; |
4066
|
|
|
|
4067
|
|
|
break; |
4068
|
2 |
|
case 'FFCC00': |
4069
|
|
|
$colorIdxBg = 0x33; |
4070
|
|
|
|
4071
|
|
|
break; |
4072
|
2 |
|
case 'FF9900': |
4073
|
|
|
$colorIdxBg = 0x34; |
4074
|
|
|
|
4075
|
|
|
break; |
4076
|
2 |
|
case 'FF6600': |
4077
|
|
|
$colorIdxBg = 0x35; |
4078
|
|
|
|
4079
|
|
|
break; |
4080
|
2 |
|
case '666699': |
4081
|
|
|
$colorIdxBg = 0x36; |
4082
|
|
|
|
4083
|
|
|
break; |
4084
|
2 |
|
case '969696': |
4085
|
|
|
$colorIdxBg = 0x37; |
4086
|
|
|
|
4087
|
|
|
break; |
4088
|
2 |
|
case '003366': |
4089
|
|
|
$colorIdxBg = 0x38; |
4090
|
|
|
|
4091
|
|
|
break; |
4092
|
2 |
|
case '339966': |
4093
|
|
|
$colorIdxBg = 0x39; |
4094
|
|
|
|
4095
|
|
|
break; |
4096
|
2 |
|
case '003300': |
4097
|
|
|
$colorIdxBg = 0x3A; |
4098
|
|
|
|
4099
|
|
|
break; |
4100
|
2 |
|
case '333300': |
4101
|
|
|
$colorIdxBg = 0x3B; |
4102
|
|
|
|
4103
|
|
|
break; |
4104
|
2 |
|
case '993300': |
4105
|
|
|
$colorIdxBg = 0x3C; |
4106
|
|
|
|
4107
|
|
|
break; |
4108
|
2 |
|
case '993366': |
4109
|
|
|
$colorIdxBg = 0x3D; |
4110
|
|
|
|
4111
|
|
|
break; |
4112
|
2 |
|
case '333399': |
4113
|
|
|
$colorIdxBg = 0x3E; |
4114
|
|
|
|
4115
|
|
|
break; |
4116
|
2 |
|
case '333333': |
4117
|
|
|
$colorIdxBg = 0x3F; |
4118
|
|
|
|
4119
|
|
|
break; |
4120
|
|
|
default: |
4121
|
2 |
|
$colorIdxBg = 0x41; |
4122
|
|
|
|
4123
|
2 |
|
break; |
4124
|
|
|
} |
4125
|
|
|
// Fg Color |
4126
|
2 |
View Code Duplication |
switch ($conditional->getStyle()->getFill()->getEndColor()->getRGB()) { |
|
|
|
|
4127
|
2 |
|
case '000000': |
4128
|
|
|
$colorIdxFg = 0x08; |
4129
|
|
|
|
4130
|
|
|
break; |
4131
|
2 |
|
case 'FFFFFF': |
4132
|
|
|
$colorIdxFg = 0x09; |
4133
|
|
|
|
4134
|
|
|
break; |
4135
|
2 |
|
case 'FF0000': |
4136
|
|
|
$colorIdxFg = 0x0A; |
4137
|
|
|
|
4138
|
|
|
break; |
4139
|
2 |
|
case '00FF00': |
4140
|
|
|
$colorIdxFg = 0x0B; |
4141
|
|
|
|
4142
|
|
|
break; |
4143
|
2 |
|
case '0000FF': |
4144
|
|
|
$colorIdxFg = 0x0C; |
4145
|
|
|
|
4146
|
|
|
break; |
4147
|
2 |
|
case 'FFFF00': |
4148
|
|
|
$colorIdxFg = 0x0D; |
4149
|
|
|
|
4150
|
|
|
break; |
4151
|
2 |
|
case 'FF00FF': |
4152
|
|
|
$colorIdxFg = 0x0E; |
4153
|
|
|
|
4154
|
|
|
break; |
4155
|
2 |
|
case '00FFFF': |
4156
|
|
|
$colorIdxFg = 0x0F; |
4157
|
|
|
|
4158
|
|
|
break; |
4159
|
2 |
|
case '800000': |
4160
|
|
|
$colorIdxFg = 0x10; |
4161
|
|
|
|
4162
|
|
|
break; |
4163
|
2 |
|
case '008000': |
4164
|
|
|
$colorIdxFg = 0x11; |
4165
|
|
|
|
4166
|
|
|
break; |
4167
|
2 |
|
case '000080': |
4168
|
|
|
$colorIdxFg = 0x12; |
4169
|
|
|
|
4170
|
|
|
break; |
4171
|
2 |
|
case '808000': |
4172
|
|
|
$colorIdxFg = 0x13; |
4173
|
|
|
|
4174
|
|
|
break; |
4175
|
2 |
|
case '800080': |
4176
|
|
|
$colorIdxFg = 0x14; |
4177
|
|
|
|
4178
|
|
|
break; |
4179
|
2 |
|
case '008080': |
4180
|
|
|
$colorIdxFg = 0x15; |
4181
|
|
|
|
4182
|
|
|
break; |
4183
|
2 |
|
case 'C0C0C0': |
4184
|
|
|
$colorIdxFg = 0x16; |
4185
|
|
|
|
4186
|
|
|
break; |
4187
|
2 |
|
case '808080': |
4188
|
|
|
$colorIdxFg = 0x17; |
4189
|
|
|
|
4190
|
|
|
break; |
4191
|
2 |
|
case '9999FF': |
4192
|
|
|
$colorIdxFg = 0x18; |
4193
|
|
|
|
4194
|
|
|
break; |
4195
|
2 |
|
case '993366': |
4196
|
|
|
$colorIdxFg = 0x19; |
4197
|
|
|
|
4198
|
|
|
break; |
4199
|
2 |
|
case 'FFFFCC': |
4200
|
|
|
$colorIdxFg = 0x1A; |
4201
|
|
|
|
4202
|
|
|
break; |
4203
|
2 |
|
case 'CCFFFF': |
4204
|
|
|
$colorIdxFg = 0x1B; |
4205
|
|
|
|
4206
|
|
|
break; |
4207
|
2 |
|
case '660066': |
4208
|
|
|
$colorIdxFg = 0x1C; |
4209
|
|
|
|
4210
|
|
|
break; |
4211
|
2 |
|
case 'FF8080': |
4212
|
|
|
$colorIdxFg = 0x1D; |
4213
|
|
|
|
4214
|
|
|
break; |
4215
|
2 |
|
case '0066CC': |
4216
|
|
|
$colorIdxFg = 0x1E; |
4217
|
|
|
|
4218
|
|
|
break; |
4219
|
2 |
|
case 'CCCCFF': |
4220
|
|
|
$colorIdxFg = 0x1F; |
4221
|
|
|
|
4222
|
|
|
break; |
4223
|
2 |
|
case '000080': |
4224
|
|
|
$colorIdxFg = 0x20; |
4225
|
|
|
|
4226
|
|
|
break; |
4227
|
2 |
|
case 'FF00FF': |
4228
|
|
|
$colorIdxFg = 0x21; |
4229
|
|
|
|
4230
|
|
|
break; |
4231
|
2 |
|
case 'FFFF00': |
4232
|
|
|
$colorIdxFg = 0x22; |
4233
|
|
|
|
4234
|
|
|
break; |
4235
|
2 |
|
case '00FFFF': |
4236
|
|
|
$colorIdxFg = 0x23; |
4237
|
|
|
|
4238
|
|
|
break; |
4239
|
2 |
|
case '800080': |
4240
|
|
|
$colorIdxFg = 0x24; |
4241
|
|
|
|
4242
|
|
|
break; |
4243
|
2 |
|
case '800000': |
4244
|
|
|
$colorIdxFg = 0x25; |
4245
|
|
|
|
4246
|
|
|
break; |
4247
|
2 |
|
case '008080': |
4248
|
|
|
$colorIdxFg = 0x26; |
4249
|
|
|
|
4250
|
|
|
break; |
4251
|
2 |
|
case '0000FF': |
4252
|
|
|
$colorIdxFg = 0x27; |
4253
|
|
|
|
4254
|
|
|
break; |
4255
|
2 |
|
case '00CCFF': |
4256
|
|
|
$colorIdxFg = 0x28; |
4257
|
|
|
|
4258
|
|
|
break; |
4259
|
2 |
|
case 'CCFFFF': |
4260
|
|
|
$colorIdxFg = 0x29; |
4261
|
|
|
|
4262
|
|
|
break; |
4263
|
2 |
|
case 'CCFFCC': |
4264
|
|
|
$colorIdxFg = 0x2A; |
4265
|
|
|
|
4266
|
|
|
break; |
4267
|
2 |
|
case 'FFFF99': |
4268
|
|
|
$colorIdxFg = 0x2B; |
4269
|
|
|
|
4270
|
|
|
break; |
4271
|
2 |
|
case '99CCFF': |
4272
|
|
|
$colorIdxFg = 0x2C; |
4273
|
|
|
|
4274
|
|
|
break; |
4275
|
2 |
|
case 'FF99CC': |
4276
|
|
|
$colorIdxFg = 0x2D; |
4277
|
|
|
|
4278
|
|
|
break; |
4279
|
2 |
|
case 'CC99FF': |
4280
|
|
|
$colorIdxFg = 0x2E; |
4281
|
|
|
|
4282
|
|
|
break; |
4283
|
2 |
|
case 'FFCC99': |
4284
|
|
|
$colorIdxFg = 0x2F; |
4285
|
|
|
|
4286
|
|
|
break; |
4287
|
2 |
|
case '3366FF': |
4288
|
|
|
$colorIdxFg = 0x30; |
4289
|
|
|
|
4290
|
|
|
break; |
4291
|
2 |
|
case '33CCCC': |
4292
|
|
|
$colorIdxFg = 0x31; |
4293
|
|
|
|
4294
|
|
|
break; |
4295
|
2 |
|
case '99CC00': |
4296
|
|
|
$colorIdxFg = 0x32; |
4297
|
|
|
|
4298
|
|
|
break; |
4299
|
2 |
|
case 'FFCC00': |
4300
|
|
|
$colorIdxFg = 0x33; |
4301
|
|
|
|
4302
|
|
|
break; |
4303
|
2 |
|
case 'FF9900': |
4304
|
|
|
$colorIdxFg = 0x34; |
4305
|
|
|
|
4306
|
|
|
break; |
4307
|
2 |
|
case 'FF6600': |
4308
|
|
|
$colorIdxFg = 0x35; |
4309
|
|
|
|
4310
|
|
|
break; |
4311
|
2 |
|
case '666699': |
4312
|
|
|
$colorIdxFg = 0x36; |
4313
|
|
|
|
4314
|
|
|
break; |
4315
|
2 |
|
case '969696': |
4316
|
|
|
$colorIdxFg = 0x37; |
4317
|
|
|
|
4318
|
|
|
break; |
4319
|
2 |
|
case '003366': |
4320
|
|
|
$colorIdxFg = 0x38; |
4321
|
|
|
|
4322
|
|
|
break; |
4323
|
2 |
|
case '339966': |
4324
|
|
|
$colorIdxFg = 0x39; |
4325
|
|
|
|
4326
|
|
|
break; |
4327
|
2 |
|
case '003300': |
4328
|
|
|
$colorIdxFg = 0x3A; |
4329
|
|
|
|
4330
|
|
|
break; |
4331
|
2 |
|
case '333300': |
4332
|
|
|
$colorIdxFg = 0x3B; |
4333
|
|
|
|
4334
|
|
|
break; |
4335
|
2 |
|
case '993300': |
4336
|
|
|
$colorIdxFg = 0x3C; |
4337
|
|
|
|
4338
|
|
|
break; |
4339
|
2 |
|
case '993366': |
4340
|
|
|
$colorIdxFg = 0x3D; |
4341
|
|
|
|
4342
|
|
|
break; |
4343
|
2 |
|
case '333399': |
4344
|
|
|
$colorIdxFg = 0x3E; |
4345
|
|
|
|
4346
|
|
|
break; |
4347
|
2 |
|
case '333333': |
4348
|
|
|
$colorIdxFg = 0x3F; |
4349
|
|
|
|
4350
|
|
|
break; |
4351
|
|
|
default: |
4352
|
2 |
|
$colorIdxFg = 0x40; |
4353
|
|
|
|
4354
|
2 |
|
break; |
4355
|
|
|
} |
4356
|
2 |
|
$dataBlockFill = pack('v', $blockFillPatternStyle); |
4357
|
2 |
|
$dataBlockFill .= pack('v', $colorIdxFg | ($colorIdxBg << 7)); |
4358
|
|
|
} |
4359
|
2 |
|
if ($bFormatProt == 1) { |
4360
|
|
|
$dataBlockProtection = 0; |
4361
|
|
|
if ($conditional->getStyle()->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED) { |
4362
|
|
|
$dataBlockProtection = 1; |
4363
|
|
|
} |
4364
|
|
|
if ($conditional->getStyle()->getProtection()->getHidden() == Protection::PROTECTION_PROTECTED) { |
4365
|
|
|
$dataBlockProtection = 1 << 1; |
4366
|
|
|
} |
4367
|
|
|
} |
4368
|
|
|
|
4369
|
2 |
|
$data = pack('CCvvVv', $type, $operatorType, $szValue1, $szValue2, $flags, 0x0000); |
4370
|
2 |
|
if ($bFormatFont == 1) { // Block Formatting : OK |
4371
|
2 |
|
$data .= $dataBlockFont; |
4372
|
|
|
} |
4373
|
2 |
|
if ($bFormatAlign == 1) { |
4374
|
|
|
$data .= $dataBlockAlign; |
4375
|
|
|
} |
4376
|
2 |
|
if ($bFormatBorder == 1) { |
4377
|
|
|
$data .= $dataBlockBorder; |
4378
|
|
|
} |
4379
|
2 |
|
if ($bFormatFill == 1) { // Block Formatting : OK |
4380
|
2 |
|
$data .= $dataBlockFill; |
4381
|
|
|
} |
4382
|
2 |
|
if ($bFormatProt == 1) { |
4383
|
|
|
$data .= $dataBlockProtection; |
4384
|
|
|
} |
4385
|
2 |
|
if ($operand1 !== null) { |
4386
|
2 |
|
$data .= $operand1; |
4387
|
|
|
} |
4388
|
2 |
|
if ($operand2 !== null) { |
4389
|
1 |
|
$data .= $operand2; |
4390
|
|
|
} |
4391
|
2 |
|
$header = pack('vv', $record, strlen($data)); |
4392
|
2 |
|
$this->append($header . $data); |
4393
|
2 |
|
} |
4394
|
|
|
|
4395
|
|
|
/** |
4396
|
|
|
* Write CFHeader record. |
4397
|
|
|
*/ |
4398
|
2 |
|
private function writeCFHeader() |
4399
|
|
|
{ |
4400
|
2 |
|
$record = 0x01B0; // Record identifier |
4401
|
2 |
|
$length = 0x0016; // Bytes to follow |
4402
|
|
|
|
4403
|
2 |
|
$numColumnMin = null; |
4404
|
2 |
|
$numColumnMax = null; |
4405
|
2 |
|
$numRowMin = null; |
4406
|
2 |
|
$numRowMax = null; |
4407
|
2 |
|
$arrConditional = []; |
4408
|
2 |
|
foreach ($this->phpSheet->getConditionalStylesCollection() as $cellCoordinate => $conditionalStyles) { |
4409
|
2 |
|
foreach ($conditionalStyles as $conditional) { |
4410
|
2 |
|
if ($conditional->getConditionType() == Conditional::CONDITION_EXPRESSION |
4411
|
2 |
|
|| $conditional->getConditionType() == Conditional::CONDITION_CELLIS) { |
4412
|
2 |
|
if (!in_array($conditional->getHashCode(), $arrConditional)) { |
4413
|
2 |
|
$arrConditional[] = $conditional->getHashCode(); |
4414
|
|
|
} |
4415
|
|
|
// Cells |
4416
|
2 |
|
$arrCoord = Coordinate::coordinateFromString($cellCoordinate); |
4417
|
2 |
|
if (!is_numeric($arrCoord[0])) { |
4418
|
2 |
|
$arrCoord[0] = Coordinate::columnIndexFromString($arrCoord[0]); |
4419
|
|
|
} |
4420
|
2 |
|
if ($numColumnMin === null || ($numColumnMin > $arrCoord[0])) { |
4421
|
2 |
|
$numColumnMin = $arrCoord[0]; |
4422
|
|
|
} |
4423
|
2 |
|
if ($numColumnMax === null || ($numColumnMax < $arrCoord[0])) { |
4424
|
2 |
|
$numColumnMax = $arrCoord[0]; |
4425
|
|
|
} |
4426
|
2 |
|
if ($numRowMin === null || ($numRowMin > $arrCoord[1])) { |
4427
|
2 |
|
$numRowMin = $arrCoord[1]; |
4428
|
|
|
} |
4429
|
2 |
|
if ($numRowMax === null || ($numRowMax < $arrCoord[1])) { |
4430
|
2 |
|
$numRowMax = $arrCoord[1]; |
4431
|
|
|
} |
4432
|
|
|
} |
4433
|
|
|
} |
4434
|
|
|
} |
4435
|
2 |
|
$needRedraw = 1; |
4436
|
2 |
|
$cellRange = pack('vvvv', $numRowMin - 1, $numRowMax - 1, $numColumnMin - 1, $numColumnMax - 1); |
4437
|
|
|
|
4438
|
2 |
|
$header = pack('vv', $record, $length); |
4439
|
2 |
|
$data = pack('vv', count($arrConditional), $needRedraw); |
4440
|
2 |
|
$data .= $cellRange; |
4441
|
2 |
|
$data .= pack('v', 0x0001); |
4442
|
2 |
|
$data .= $cellRange; |
4443
|
2 |
|
$this->append($header . $data); |
4444
|
2 |
|
} |
4445
|
|
|
} |
4446
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.