PHPExcel_Writer_Excel5_Worksheet::_writeLabel()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 14
c 0
b 0
f 0
nc 2
nop 4
dl 0
loc 20
rs 9.4285
1
<?php
2
/**
3
 * PHPExcel
4
 *
5
 * Copyright (c) 2006 - 2012 PHPExcel
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20
 *
21
 * @category   PHPExcel
22
 * @package    PHPExcel_Writer_Excel5
23
 * @copyright  Copyright (c) 2006 - 2012 PHPExcel (http://www.codeplex.com/PHPExcel)
24
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt	LGPL
25
 * @version    1.7.7, 2012-05-19
26
 */
27
28
// Original file header of PEAR::Spreadsheet_Excel_Writer_Worksheet (used as the base for this class):
29
// -----------------------------------------------------------------------------------------
30
// /*
31
// *  Module written/ported by Xavier Noguer <[email protected]>
32
// *
33
// *  The majority of this is _NOT_ my code.  I simply ported it from the
34
// *  PERL Spreadsheet::WriteExcel module.
35
// *
36
// *  The author of the Spreadsheet::WriteExcel module is John McNamara
37
// *  <[email protected]>
38
// *
39
// *  I _DO_ maintain this code, and John McNamara has nothing to do with the
40
// *  porting of this code to PHP.  Any questions directly related to this
41
// *  class library should be directed to me.
42
// *
43
// *  License Information:
44
// *
45
// *    Spreadsheet_Excel_Writer:  A library for generating Excel Spreadsheets
46
// *    Copyright (c) 2002-2003 Xavier Noguer [email protected]
47
// *
48
// *    This library is free software; you can redistribute it and/or
49
// *    modify it under the terms of the GNU Lesser General Public
50
// *    License as published by the Free Software Foundation; either
51
// *    version 2.1 of the License, or (at your option) any later version.
52
// *
53
// *    This library is distributed in the hope that it will be useful,
54
// *    but WITHOUT ANY WARRANTY; without even the implied warranty of
55
// *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
56
// *    Lesser General Public License for more details.
57
// *
58
// *    You should have received a copy of the GNU Lesser General Public
59
// *    License along with this library; if not, write to the Free Software
60
// *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
61
// */
62
63
64
/**
65
 * PHPExcel_Writer_Excel5_Worksheet
66
 *
67
 * @category   PHPExcel
68
 * @package    PHPExcel_Writer_Excel5
69
 * @copyright  Copyright (c) 2006 - 2012 PHPExcel (http://www.codeplex.com/PHPExcel)
70
 */
71
class PHPExcel_Writer_Excel5_Worksheet extends PHPExcel_Writer_Excel5_BIFFwriter
72
{
73
	/**
74
	 * Formula parser
75
	 *
76
	 * @var PHPExcel_Writer_Excel5_Parser
77
	 */
78
	private $_parser;
79
80
	/**
81
	 * Maximum number of characters for a string (LABEL record in BIFF5)
82
	 * @var integer
83
	 */
84
	public $_xls_strmax;
85
86
	/**
87
	 * Array containing format information for columns
88
	 * @var array
89
	 */
90
	public $_colinfo;
91
92
	/**
93
	 * Array containing the selected area for the worksheet
94
	 * @var array
95
	 */
96
	public $_selection;
97
98
	/**
99
	 * The active pane for the worksheet
100
	 * @var integer
101
	 */
102
	public $_active_pane;
103
104
	/**
105
	 * Whether to use outline.
106
	 * @var integer
107
	 */
108
	public $_outline_on;
109
110
	/**
111
	 * Auto outline styles.
112
	 * @var bool
113
	 */
114
	public $_outline_style;
115
116
	/**
117
	 * Whether to have outline summary below.
118
	 * @var bool
119
	 */
120
	public $_outline_below;
121
122
	/**
123
	 * Whether to have outline summary at the right.
124
	 * @var bool
125
	 */
126
	public $_outline_right;
127
128
	/**
129
	 * Reference to the total number of strings in the workbook
130
	 * @var integer
131
	 */
132
	public $_str_total;
133
134
	/**
135
	 * Reference to the number of unique strings in the workbook
136
	 * @var integer
137
	 */
138
	public $_str_unique;
139
140
	/**
141
	 * Reference to the array containing all the unique strings in the workbook
142
	 * @var array
143
	 */
144
	public $_str_table;
145
146
	/**
147
	 * Color cache
148
	 */
149
	private $_colors;
150
151
	/**
152
	 * Index of first used row (at least 0)
153
	 * @var int
154
	 */
155
	private $_firstRowIndex;
156
157
	/**
158
	 * Index of last used row. (no used rows means -1)
159
	 * @var int
160
	 */
161
	private $_lastRowIndex;
162
163
	/**
164
	 * Index of first used column (at least 0)
165
	 * @var int
166
	 */
167
	private $_firstColumnIndex;
168
169
	/**
170
	 * Index of last used column (no used columns means -1)
171
	 * @var int
172
	 */
173
	private $_lastColumnIndex;
174
175
	/**
176
	 * Sheet object
177
	 * @var PHPExcel_Worksheet
178
	 */
179
	public $_phpSheet;
180
181
	/**
182
	 * Count cell style Xfs
183
	 *
184
	 * @var int
185
	 */
186
	private $_countCellStyleXfs;
187
188
	/**
189
	 * Escher object corresponding to MSODRAWING
190
	 *
191
	 * @var PHPExcel_Shared_Escher
192
	 */
193
	private $_escher;
194
195
	/**
196
	 * Array of font hashes associated to FONT records index
197
	 *
198
	 * @var array
199
	 */
200
	public $_fntHashIndex;
201
202
	/**
203
	 * Constructor
204
	 *
205
	 * @param int		&$str_total		Total number of strings
206
	 * @param int		&$str_unique	Total number of unique strings
207
	 * @param array		&$str_table		String Table
208
	 * @param array		&$colors		Colour Table
209
	 * @param mixed		$parser			The formula parser created for the Workbook
210
	 * @param boolean	$preCalculateFormulas	Flag indicating whether formulas should be calculated or just written
211
	 * @param string	$phpSheet		The worksheet to write
212
	 * @param PHPExcel_Worksheet $phpSheet
213
	 */
214
	public function __construct(&$str_total, &$str_unique, &$str_table, &$colors,
215
								$parser, $preCalculateFormulas, $phpSheet)
216
	{
217
		// It needs to call its parent's constructor explicitly
218
		parent::__construct();
219
220
		// change BIFFwriter limit for CONTINUE records
221
//		$this->_limit = 8224;
222
223
224
		$this->_preCalculateFormulas = $preCalculateFormulas;
225
		$this->_str_total		= &$str_total;
226
		$this->_str_unique		= &$str_unique;
227
		$this->_str_table		= &$str_table;
228
		$this->_colors			= &$colors;
229
		$this->_parser			= $parser;
230
231
		$this->_phpSheet = $phpSheet;
0 ignored issues
show
Documentation Bug introduced by
It seems like $phpSheet of type string is incompatible with the declared type object<PHPExcel_Worksheet> of property $_phpSheet.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
232
233
		//$this->ext_sheets		= array();
234
		//$this->offset			= 0;
235
		$this->_xls_strmax		= 255;
236
		$this->_colinfo			= array();
237
		$this->_selection		= array(0,0,0,0);
238
		$this->_active_pane		= 3;
239
240
		$this->_print_headers		= 0;
241
242
		$this->_outline_style		= 0;
0 ignored issues
show
Documentation Bug introduced by
The property $_outline_style was declared of type boolean, but 0 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
243
		$this->_outline_below		= 1;
0 ignored issues
show
Documentation Bug introduced by
The property $_outline_below was declared of type boolean, but 1 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
244
		$this->_outline_right		= 1;
0 ignored issues
show
Documentation Bug introduced by
The property $_outline_right was declared of type boolean, but 1 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
245
		$this->_outline_on			= 1;
246
247
		$this->_fntHashIndex	= array();
248
249
		// calculate values for DIMENSIONS record
250
		$minR = 1;
251
		$minC = 'A';
252
253
		$maxR  = $this->_phpSheet->getHighestRow();
0 ignored issues
show
Bug introduced by
The method getHighestRow cannot be called on $this->_phpSheet (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
254
		$maxC = $this->_phpSheet->getHighestColumn();
0 ignored issues
show
Bug introduced by
The method getHighestColumn cannot be called on $this->_phpSheet (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
255
256
		// Determine lowest and highest column and row
257
//		$this->_firstRowIndex = ($minR > 65535) ? 65535 : $minR;
258
		$this->_lastRowIndex = ($maxR > 65535) ? 65535 : $maxR ;
259
260
		$this->_firstColumnIndex	= PHPExcel_Cell::columnIndexFromString($minC);
261
		$this->_lastColumnIndex		= PHPExcel_Cell::columnIndexFromString($maxC);
262
263
//		if ($this->_firstColumnIndex > 255) $this->_firstColumnIndex = 255;
264
		if ($this->_lastColumnIndex > 255) $this->_lastColumnIndex = 255;
265
266
		$this->_countCellStyleXfs = count($phpSheet->getParent()->getCellStyleXfCollection());
0 ignored issues
show
Bug introduced by
The method getParent cannot be called on $phpSheet (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
267
	}
268
269
	/**
270
	 * Add data to the beginning of the workbook (note the reverse order)
271
	 * and to the end of the workbook.
272
	 *
273
	 * @access public
274
	 * @see PHPExcel_Writer_Excel5_Workbook::storeWorkbook()
275
	 */
276
	function close()
277
	{
278
		$_phpSheet = $this->_phpSheet;
279
280
		$num_sheets = $_phpSheet->getParent()->getSheetCount();
281
282
		// Write BOF record
283
		$this->_storeBof(0x0010);
284
285
		// Write PRINTHEADERS
286
		$this->_writePrintHeaders();
287
288
		// Write PRINTGRIDLINES
289
		$this->_writePrintGridlines();
290
291
		// Write GRIDSET
292
		$this->_writeGridset();
293
294
		// Calculate column widths
295
		$_phpSheet->calculateColumnWidths();
296
297
		// Column dimensions
298
		if (($defaultWidth = $_phpSheet->getDefaultColumnDimension()->getWidth()) < 0) {
299
			$defaultWidth = PHPExcel_Shared_Font::getDefaultColumnWidthByFont($_phpSheet->getParent()->getDefaultStyle()->getFont());
300
		}
301
302
		$columnDimensions = $_phpSheet->getColumnDimensions();
303
		$maxCol = $this->_lastColumnIndex -1;
304
		for ($i = 0; $i <= $maxCol; ++$i) {
305
			$hidden = 0;
306
			$level = 0;
307
			$xfIndex = 15; // there are 15 cell style Xfs
308
309
			$width = $defaultWidth;
310
311
			$columnLetter = PHPExcel_Cell::stringFromColumnIndex($i);
312
			if (isset($columnDimensions[$columnLetter])) {
313
				$columnDimension = $columnDimensions[$columnLetter];
314
				if ($columnDimension->getWidth() >= 0) {
315
					$width = $columnDimension->getWidth();
316
				}
317
				$hidden = $columnDimension->getVisible() ? 0 : 1;
318
				$level = $columnDimension->getOutlineLevel();
319
				$xfIndex = $columnDimension->getXfIndex() + 15; // there are 15 cell style Xfs
320
			}
321
322
			// Components of _colinfo:
323
			// $firstcol first column on the range
324
			// $lastcol  last column on the range
325
			// $width	width to set
326
			// $xfIndex  The optional cell style Xf index to apply to the columns
327
			// $hidden   The optional hidden atribute
328
			// $level	The optional outline level
329
			$this->_colinfo[] = array($i, $i, $width, $xfIndex, $hidden, $level);
330
		}
331
332
		// Write GUTS
333
		$this->_writeGuts();
334
335
		// Write DEFAULTROWHEIGHT
336
		$this->_writeDefaultRowHeight();
337
338
		// Write WSBOOL
339
		$this->_writeWsbool();
340
341
		// Write horizontal and vertical page breaks
342
		$this->_writeBreaks();
343
344
		// Write page header
345
		$this->_writeHeader();
346
347
		// Write page footer
348
		$this->_writeFooter();
349
350
		// Write page horizontal centering
351
		$this->_writeHcenter();
352
353
		// Write page vertical centering
354
		$this->_writeVcenter();
355
356
		// Write left margin
357
		$this->_writeMarginLeft();
358
359
		// Write right margin
360
		$this->_writeMarginRight();
361
362
		// Write top margin
363
		$this->_writeMarginTop();
364
365
		// Write bottom margin
366
		$this->_writeMarginBottom();
367
368
		// Write page setup
369
		$this->_writeSetup();
370
371
		// Write sheet protection
372
		$this->_writeProtect();
373
374
		// Write SCENPROTECT
375
		$this->_writeScenProtect();
376
377
		// Write OBJECTPROTECT
378
		$this->_writeObjectProtect();
379
380
		// Write sheet password
381
		$this->_writePassword();
382
383
		// Write DEFCOLWIDTH record
384
		$this->_writeDefcol();
385
386
		// Write the COLINFO records if they exist
387
		if (!empty($this->_colinfo)) {
388
			$colcount = count($this->_colinfo);
389
			for ($i = 0; $i < $colcount; ++$i) {
390
				$this->_writeColinfo($this->_colinfo[$i]);
391
			}
392
		}
393
394
		// Write sheet dimensions
395
		$this->_writeDimensions();
396
397
		// Row dimensions
398
		foreach ($_phpSheet->getRowDimensions() as $rowDimension) {
399
			$xfIndex = $rowDimension->getXfIndex() + 15; // there are 15 cellXfs
400
			$this->_writeRow( $rowDimension->getRowIndex() - 1, $rowDimension->getRowHeight(), $xfIndex, ($rowDimension->getVisible() ? '0' : '1'), $rowDimension->getOutlineLevel() );
401
		}
402
403
		// Write Cells
404
		foreach ($_phpSheet->getCellCollection() as $cellID) {
405
			$cell = $_phpSheet->getCell($cellID);
406
			$row = $cell->getRow() - 1;
407
			$column = PHPExcel_Cell::columnIndexFromString($cell->getColumn()) - 1;
408
409
			// Don't break Excel!
410
//			if ($row + 1 > 65536 or $column + 1 > 256) {
411
			if ($row > 65535 || $column > 255) {
412
				break;
413
			}
414
415
			// Write cell value
416
			$xfIndex = $cell->getXfIndex() + 15; // there are 15 cell style Xfs
417
418
			$cVal = $cell->getValue();
419
			if ($cVal instanceof PHPExcel_RichText) {
420
				// $this->_writeString($row, $column, $cVal->getPlainText(), $xfIndex);
421
				$arrcRun = array();
422
				$str_len = strlen($cVal->getPlainText());
423
				$str_pos = 0;
424
				$elements = $cVal->getRichTextElements();
425
				foreach ($elements as $element) {
426
					// FONT Index
427
					if ($element instanceof PHPExcel_RichText_Run) {
428
						$str_fontidx = $this->_fntHashIndex[$element->getFont()->getHashCode()];
429
					}
430
					else {
431
						$str_fontidx = 0;
432
					}
433
					$arrcRun[] = array('strlen' => $str_pos, 'fontidx' => $str_fontidx);
434
					// Position FROM
435
					$str_pos += strlen($element->getText());
436
				}
437
				$this->_writeRichTextString($row, $column, $cVal->getPlainText(), $xfIndex, $arrcRun);
438
			} else {
439
				switch ($cell->getDatatype()) {
440
					case PHPExcel_Cell_DataType::TYPE_STRING:
441
					case PHPExcel_Cell_DataType::TYPE_NULL:
442
						if ($cVal === '' || $cVal === null) {
443
							$this->_writeBlank($row, $column, $xfIndex);
444
						} else {
445
							$this->_writeString($row, $column, $cVal, $xfIndex);
446
						}
447
						break;
448
449
					case PHPExcel_Cell_DataType::TYPE_NUMERIC:
450
						$this->_writeNumber($row, $column, $cVal, $xfIndex);
451
						break;
452
453
					case PHPExcel_Cell_DataType::TYPE_FORMULA:
454
						$calculatedValue = $this->_preCalculateFormulas ?
455
							$cell->getCalculatedValue() : null;
456
						$this->_writeFormula($row, $column, $cVal, $xfIndex, $calculatedValue);
457
						break;
458
459
					case PHPExcel_Cell_DataType::TYPE_BOOL:
460
						$this->_writeBoolErr($row, $column, $cVal, 0, $xfIndex);
461
						break;
462
463
					case PHPExcel_Cell_DataType::TYPE_ERROR:
464
						$this->_writeBoolErr($row, $column, self::_mapErrorCode($cVal), 1, $xfIndex);
465
						break;
466
467
				}
468
			}
469
		}
470
471
		// Append
472
		$this->_writeMsoDrawing();
473
474
		$this->_writeWindow2();
475
		$this->_writeZoom();
476
		if ($_phpSheet->getFreezePane()) {
477
			$this->_writePanes();
478
		}
479
		$this->_writeSelection();
480
		$this->_writeMergedCells();
481
482
		// Hyperlinks
483
		foreach ($_phpSheet->getHyperLinkCollection() as $coordinate => $hyperlink) {
484
			list($column, $row) = PHPExcel_Cell::coordinateFromString($coordinate);
485
486
			$url = $hyperlink->getUrl();
487
488
			if ( strpos($url, 'sheet://') !== false ) {
489
				// internal to current workbook
490
				$url = str_replace('sheet://', 'internal:', $url);
491
492
			} else if ( preg_match('/^(http:|https:|ftp:|mailto:)/', $url) ) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
493
				// URL
494
				// $url = $url;
495
496
			} else {
497
				// external (local file)
498
				$url = 'external:' . $url;
499
			}
500
501
			$this->_writeUrl($row - 1, PHPExcel_Cell::columnIndexFromString($column) - 1, $url);
502
		}
503
504
		$this->_writeDataValidity();
505
		$this->_writeSheetLayout();
506
		$this->_writeSheetProtection();
507
		$this->_writeRangeProtection();
508
509
		$this->_storeEof();
510
	}
511
512
	/**
513
	 * Write a cell range address in BIFF8
514
	 * always fixed range
515
	 * See section 2.5.14 in OpenOffice.org's Documentation of the Microsoft Excel File Format
516
	 *
517
	 * @param string $range E.g. 'A1' or 'A1:B6'
518
	 * @return string Binary data
519
	 */
520
	private function _writeBIFF8CellRangeAddressFixed($range = 'A1')
521
	{
522
		$explodes = explode(':', $range);
523
524
		// extract first cell, e.g. 'A1'
525
		$firstCell = $explodes[0];
526
527
		// extract last cell, e.g. 'B6'
528
		if (count($explodes) == 1) {
529
			$lastCell = $firstCell;
530
		} else {
531
			$lastCell = $explodes[1];
532
		}
533
534
		$firstCellCoordinates = PHPExcel_Cell::coordinateFromString($firstCell); // e.g. array(0, 1)
535
		$lastCellCoordinates  = PHPExcel_Cell::coordinateFromString($lastCell);  // e.g. array(1, 6)
536
537
		return(pack('vvvv',
538
			$firstCellCoordinates[1] - 1,
539
			$lastCellCoordinates[1] - 1,
540
			PHPExcel_Cell::columnIndexFromString($firstCellCoordinates[0]) - 1,
541
			PHPExcel_Cell::columnIndexFromString($lastCellCoordinates[0]) - 1
542
		));
543
	}
544
545
	/**
546
	 * Retrieves data from memory in one chunk, or from disk in $buffer
547
	 * sized chunks.
548
	 *
549
	 * @return string The data
550
	 */
551
	function getData()
552
	{
553
		$buffer = 4096;
554
555
		// Return data stored in memory
556
		if (isset($this->_data)) {
557
			$tmp   = $this->_data;
558
			unset($this->_data);
559
			return $tmp;
560
		}
561
		// No data to return
562
		return false;
563
	}
564
565
	/**
566
	 * Set the option to print the row and column headers on the printed page.
567
	 *
568
	 * @access public
569
	 * @param integer $print Whether to print the headers or not. Defaults to 1 (print).
570
	 */
571
	function printRowColHeaders($print = 1)
572
	{
573
		$this->_print_headers = $print;
574
	}
575
576
	/**
577
	 * This method sets the properties for outlining and grouping. The defaults
578
	 * correspond to Excel's defaults.
579
	 *
580
	 * @param bool $visible
581
	 * @param bool $symbols_below
582
	 * @param bool $symbols_right
583
	 * @param bool $auto_style
584
	 */
585
	function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false)
586
	{
587
		$this->_outline_on	= $visible;
0 ignored issues
show
Documentation Bug introduced by
The property $_outline_on was declared of type integer, but $visible is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
588
		$this->_outline_below = $symbols_below;
589
		$this->_outline_right = $symbols_right;
590
		$this->_outline_style = $auto_style;
591
592
		// Ensure this is a boolean vale for Window2
593
		if ($this->_outline_on) {
594
			$this->_outline_on = 1;
595
		}
596
	 }
597
598
	/**
599
	 * Write a double to the specified row and column (zero indexed).
600
	 * An integer can be written as a double. Excel will display an
601
	 * integer. $format is optional.
602
	 *
603
	 * Returns  0 : normal termination
604
	 *		 -2 : row or column out of range
605
	 *
606
	 * @param integer $row	Zero indexed row
607
	 * @param integer $col	Zero indexed column
608
	 * @param float   $num	The number to write
609
	 * @param mixed   $xfIndex The optional XF format
610
	 * @return integer
611
	 */
612
	private function _writeNumber($row, $col, $num, $xfIndex)
613
	{
614
		$record	= 0x0203;				 // Record identifier
615
		$length	= 0x000E;				 // Number of bytes to follow
616
617
		$header		= pack("vv",  $record, $length);
618
		$data		= pack("vvv", $row, $col, $xfIndex);
619
		$xl_double	= pack("d",   $num);
620
		if (self::getByteOrder()) { // if it's Big Endian
621
			$xl_double = strrev($xl_double);
622
		}
623
624
		$this->_append($header.$data.$xl_double);
625
		return(0);
626
	}
627
628
	/**
629
	 * Write a LABELSST record or a LABEL record. Which one depends on BIFF version
630
	 *
631
	 * @param int $row Row index (0-based)
632
	 * @param int $col Column index (0-based)
633
	 * @param string $str The string
634
	 * @param int $xfIndex Index to XF record
635
	 */
636
	private function _writeString($row, $col, $str, $xfIndex)
637
	{
638
		$this->_writeLabelSst($row, $col, $str, $xfIndex);
639
	}
640
	/**
641
	 * Write a LABELSST record or a LABEL record. Which one depends on BIFF version
642
	 * It differs from _writeString by the writing of rich text strings.
643
	 * @param int $row Row index (0-based)
644
	 * @param int $col Column index (0-based)
645
	 * @param string $str The string
646
	 * @param mixed   $xfIndex The XF format index for the cell
647
	 * @param array $arrcRun Index to Font record and characters beginning
648
	 */
649 View Code Duplication
	private function _writeRichTextString($row, $col, $str, $xfIndex, $arrcRun){
650
		$record	= 0x00FD;				   // Record identifier
651
		$length	= 0x000A;				   // Bytes to follow
652
653
		$str = PHPExcel_Shared_String::UTF8toBIFF8UnicodeShort($str, $arrcRun);
654
655
		/* check if string is already present */
656
		if (!isset($this->_str_table[$str])) {
657
			$this->_str_table[$str] = $this->_str_unique++;
658
		}
659
		$this->_str_total++;
660
661
		$header	= pack('vv',   $record, $length);
662
		$data	= pack('vvvV', $row, $col, $xfIndex, $this->_str_table[$str]);
663
		$this->_append($header.$data);
664
	}
665
666
	/**
667
	 * Write a string to the specified row and column (zero indexed).
668
	 * NOTE: there is an Excel 5 defined limit of 255 characters.
669
	 * $format is optional.
670
	 * Returns  0 : normal termination
671
	 *		 -2 : row or column out of range
672
	 *		 -3 : long string truncated to 255 chars
673
	 *
674
	 * @access public
675
	 * @param integer $row	Zero indexed row
676
	 * @param integer $col	Zero indexed column
677
	 * @param string  $str	The string to write
678
	 * @param mixed   $xfIndex The XF format index for the cell
679
	 * @return integer
680
	 */
681
	private function _writeLabel($row, $col, $str, $xfIndex)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
682
	{
683
		$strlen	= strlen($str);
684
		$record	= 0x0204;				   // Record identifier
685
		$length	= 0x0008 + $strlen;		 // Bytes to follow
686
687
		$str_error = 0;
688
689
		if ($strlen > $this->_xls_strmax) { // LABEL must be < 255 chars
690
			$str	= substr($str, 0, $this->_xls_strmax);
691
			$length	= 0x0008 + $this->_xls_strmax;
692
			$strlen	= $this->_xls_strmax;
693
			$str_error = -3;
694
		}
695
696
		$header	= pack("vv",   $record, $length);
697
		$data	= pack("vvvv", $row, $col, $xfIndex, $strlen);
698
		$this->_append($header . $data . $str);
699
		return($str_error);
700
	}
701
702
	/**
703
	 * Write a string to the specified row and column (zero indexed).
704
	 * This is the BIFF8 version (no 255 chars limit).
705
	 * $format is optional.
706
	 * Returns  0 : normal termination
707
	 *		 -2 : row or column out of range
708
	 *		 -3 : long string truncated to 255 chars
709
	 *
710
	 * @access public
711
	 * @param integer $row	Zero indexed row
712
	 * @param integer $col	Zero indexed column
713
	 * @param string  $str	The string to write
714
	 * @param mixed   $xfIndex The XF format index for the cell
715
	 * @return integer
716
	 */
717 View Code Duplication
	private function _writeLabelSst($row, $col, $str, $xfIndex)
718
	{
719
		$record	= 0x00FD;				   // Record identifier
720
		$length	= 0x000A;				   // Bytes to follow
721
722
		$str = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($str);
723
724
		/* check if string is already present */
725
		if (!isset($this->_str_table[$str])) {
726
			$this->_str_table[$str] = $this->_str_unique++;
727
		}
728
		$this->_str_total++;
729
730
		$header	= pack('vv',   $record, $length);
731
		$data	= pack('vvvV', $row, $col, $xfIndex, $this->_str_table[$str]);
732
		$this->_append($header.$data);
733
	}
734
735
	/**
736
	 * Writes a note associated with the cell given by the row and column.
737
	 * NOTE records don't have a length limit.
738
	 *
739
	 * @param integer $row	Zero indexed row
740
	 * @param integer $col	Zero indexed column
741
	 * @param string  $note   The note to write
742
	 */
743
	private function _writeNote($row, $col, $note)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
744
	{
745
		$note_length	= strlen($note);
746
		$record			= 0x001C;			// Record identifier
747
		$max_length		= 2048;				// Maximun length for a NOTE record
748
749
		// Length for this record is no more than 2048 + 6
750
		$length	= 0x0006 + min($note_length, 2048);
751
		$header	= pack("vv",   $record, $length);
752
		$data	= pack("vvv", $row, $col, $note_length);
753
		$this->_append($header . $data . substr($note, 0, 2048));
754
755
		for ($i = $max_length; $i < $note_length; $i += $max_length) {
756
			$chunk  = substr($note, $i, $max_length);
757
			$length = 0x0006 + strlen($chunk);
758
			$header = pack("vv",   $record, $length);
759
			$data   = pack("vvv", -1, 0, strlen($chunk));
760
			$this->_append($header.$data.$chunk);
761
		}
762
		return(0);
763
	}
764
765
	/**
766
	 * Write a blank cell to the specified row and column (zero indexed).
767
	 * A blank cell is used to specify formatting without adding a string
768
	 * or a number.
769
	 *
770
	 * A blank cell without a format serves no purpose. Therefore, we don't write
771
	 * a BLANK record unless a format is specified.
772
	 *
773
	 * Returns  0 : normal termination (including no format)
774
	 *		 -1 : insufficient number of arguments
775
	 *		 -2 : row or column out of range
776
	 *
777
	 * @param integer $row	Zero indexed row
778
	 * @param integer $col	Zero indexed column
779
	 * @param mixed   $xfIndex The XF format index
780
	 */
781 View Code Duplication
	function _writeBlank($row, $col, $xfIndex)
782
	{
783
		$record	= 0x0201;				 // Record identifier
784
		$length	= 0x0006;				 // Number of bytes to follow
785
786
		$header	= pack("vv",  $record, $length);
787
		$data	  = pack("vvv", $row, $col, $xfIndex);
788
		$this->_append($header . $data);
789
		return 0;
790
	}
791
792
	/**
793
	 * Write a boolean or an error type to the specified row and column (zero indexed)
794
	 *
795
	 * @param int $row Row index (0-based)
796
	 * @param int $col Column index (0-based)
797
	 * @param int $value
798
	 * @param boolean $isError Error or Boolean?
799
	 * @param int $xfIndex
800
	 */
801 View Code Duplication
	private function _writeBoolErr($row, $col, $value, $isError, $xfIndex)
802
	{
803
		$record = 0x0205;
804
		$length = 8;
805
806
		$header	= pack("vv",  $record, $length);
807
		$data	  = pack("vvvCC", $row, $col, $xfIndex, $value, $isError);
808
		$this->_append($header . $data);
809
		return 0;
810
	}
811
812
	/**
813
	 * Write a formula to the specified row and column (zero indexed).
814
	 * The textual representation of the formula is passed to the parser in
815
	 * Parser.php which returns a packed binary string.
816
	 *
817
	 * Returns  0 : normal termination
818
	 *		 -1 : formula errors (bad formula)
819
	 *		 -2 : row or column out of range
820
	 *
821
	 * @param integer $row	 Zero indexed row
822
	 * @param integer $col	 Zero indexed column
823
	 * @param string  $formula The formula text string
824
	 * @param mixed   $xfIndex  The XF format index
825
	 * @param mixed   $calculatedValue  Calculated value
826
	 * @return integer
827
	 */
828
	private function _writeFormula($row, $col, $formula, $xfIndex, $calculatedValue)
829
	{
830
		$record	= 0x0006;	 // Record identifier
831
832
		// Initialize possible additional value for STRING record that should be written after the FORMULA record?
833
		$stringValue = null;
834
835
		// calculated value
836
		if (isset($calculatedValue)) {
837
			// Since we can't yet get the data type of the calculated value,
838
			// we use best effort to determine data type
839
			if (is_bool($calculatedValue)) {
840
				// Boolean value
841
				$num = pack('CCCvCv', 0x01, 0x00, (int)$calculatedValue, 0x00, 0x00, 0xFFFF);
842
			} elseif (is_int($calculatedValue) || is_float($calculatedValue)) {
843
				// Numeric value
844
				$num = pack('d', $calculatedValue);
845
			} elseif (is_string($calculatedValue)) {
846
				if (array_key_exists($calculatedValue, PHPExcel_Cell_DataType::getErrorCodes())) {
847
					// Error value
848
					$num = pack('CCCvCv', 0x02, 0x00, self::_mapErrorCode($calculatedValue), 0x00, 0x00, 0xFFFF);
849
				} elseif ($calculatedValue === '') {
850
					// Empty string (and BIFF8)
851
					$num = pack('CCCvCv', 0x03, 0x00, 0x00, 0x00, 0x00, 0xFFFF);
852
				} else {
853
					// Non-empty string value (or empty string BIFF5)
854
					$stringValue = $calculatedValue;
855
					$num = pack('CCCvCv', 0x00, 0x00, 0x00, 0x00, 0x00, 0xFFFF);
856
				}
857
			} else {
858
				// We are really not supposed to reach here
859
				$num = pack('d', 0x00);
860
			}
861
		} else {
862
			$num = pack('d', 0x00);
863
		}
864
865
		$grbit		= 0x03;				// Option flags
866
		$unknown	= 0x0000;			// Must be zero
867
868
		// Strip the '=' or '@' sign at the beginning of the formula string
869
		if ($formula{0} == '=') {
870
			$formula = substr($formula,1);
871
		} else {
872
			// Error handling
873
			$this->_writeString($row, $col, 'Unrecognised character for formula');
0 ignored issues
show
Bug introduced by
The call to _writeString() misses a required argument $xfIndex.

This check looks for function calls that miss required arguments.

Loading history...
874
			return -1;
875
		}
876
877
		// Parse the formula using the parser in Parser.php
878
		try {
879
			$error = $this->_parser->parse($formula);
880
			$formula = $this->_parser->toReversePolish();
881
882
			$formlen	= strlen($formula);	// Length of the binary string
883
			$length	 = 0x16 + $formlen;	 // Length of the record data
884
885
			$header	= pack("vv",	  $record, $length);
886
887
			$data	  = pack("vvv", $row, $col, $xfIndex)
888
						. $num
889
						. pack("vVv", $grbit, $unknown, $formlen);
890
			$this->_append($header . $data . $formula);
891
892
			// Append also a STRING record if necessary
893
			if ($stringValue !== null) {
894
				$this->_writeStringRecord($stringValue);
895
			}
896
897
			return 0;
898
899
		} catch (Exception $e) {
900
			// do nothing
901
		}
902
903
	}
904
905
	/**
906
	 * Write a STRING record. This
907
	 *
908
	 * @param string $stringValue
909
	 */
910 View Code Duplication
	private function _writeStringRecord($stringValue)
911
	{
912
		$record = 0x0207;	 // Record identifier
913
		$data = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($stringValue);
914
915
		$length = strlen($data);
916
		$header = pack('vv', $record, $length);
917
918
		$this->_append($header . $data);
919
	}
920
921
	/**
922
	 * Write a hyperlink.
923
	 * This is comprised of two elements: the visible label and
924
	 * the invisible link. The visible label is the same as the link unless an
925
	 * alternative string is specified. The label is written using the
926
	 * _writeString() method. Therefore the 255 characters string limit applies.
927
	 * $string and $format are optional.
928
	 *
929
	 * The hyperlink can be to a http, ftp, mail, internal sheet (not yet), or external
930
	 * directory url.
931
	 *
932
	 * Returns  0 : normal termination
933
	 *		 -2 : row or column out of range
934
	 *		 -3 : long string truncated to 255 chars
935
	 *
936
	 * @param integer $row	Row
937
	 * @param integer $col	Column
938
	 * @param string  $url	URL string
939
	 * @return integer
940
	 */
941
	private function _writeUrl($row, $col, $url)
942
	{
943
		// Add start row and col to arg list
944
		return($this->_writeUrlRange($row, $col, $row, $col, $url));
945
	}
946
947
	/**
948
	 * This is the more general form of _writeUrl(). It allows a hyperlink to be
949
	 * written to a range of cells. This function also decides the type of hyperlink
950
	 * to be written. These are either, Web (http, ftp, mailto), Internal
951
	 * (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1').
952
	 *
953
	 * @access private
954
	 * @see _writeUrl()
955
	 * @param integer $row1   Start row
956
	 * @param integer $col1   Start column
957
	 * @param integer $row2   End row
958
	 * @param integer $col2   End column
959
	 * @param string  $url	URL string
960
	 * @return integer
961
	 */
962
	function _writeUrlRange($row1, $col1, $row2, $col2, $url)
963
	{
964
		// Check for internal/external sheet links or default to web link
965
		if (preg_match('[^internal:]', $url)) {
966
			return($this->_writeUrlInternal($row1, $col1, $row2, $col2, $url));
967
		}
968
		if (preg_match('[^external:]', $url)) {
969
			return($this->_writeUrlExternal($row1, $col1, $row2, $col2, $url));
970
		}
971
		return($this->_writeUrlWeb($row1, $col1, $row2, $col2, $url));
972
	}
973
974
	/**
975
	 * Used to write http, ftp and mailto hyperlinks.
976
	 * The link type ($options) is 0x03 is the same as absolute dir ref without
977
	 * sheet. However it is differentiated by the $unknown2 data stream.
978
	 *
979
	 * @access private
980
	 * @see _writeUrl()
981
	 * @param integer $row1   Start row
982
	 * @param integer $col1   Start column
983
	 * @param integer $row2   End row
984
	 * @param integer $col2   End column
985
	 * @param string  $url	URL string
986
	 * @return integer
987
	 */
988
	function _writeUrlWeb($row1, $col1, $row2, $col2, $url)
989
	{
990
		$record	  = 0x01B8;					   // Record identifier
991
		$length	  = 0x00000;					  // Bytes to follow
992
993
		// Pack the undocumented parts of the hyperlink stream
994
		$unknown1	= pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
995
		$unknown2	= pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B");
996
997
		// Pack the option flags
998
		$options	 = pack("V", 0x03);
999
1000
		// Convert URL to a null terminated wchar string
1001
		$url		 = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
1002
		$url		 = $url . "\0\0\0";
1003
1004
		// Pack the length of the URL
1005
		$url_len	 = pack("V", strlen($url));
1006
1007
		// Calculate the data length
1008
		$length	  = 0x34 + strlen($url);
1009
1010
		// Pack the header data
1011
		$header	  = pack("vv",   $record, $length);
1012
		$data		= pack("vvvv", $row1, $row2, $col1, $col2);
1013
1014
		// Write the packed data
1015
		$this->_append($header . $data .
1016
					   $unknown1 . $options .
1017
					   $unknown2 . $url_len . $url);
1018
		return 0;
1019
	}
1020
1021
	/**
1022
	 * Used to write internal reference hyperlinks such as "Sheet1!A1".
1023
	 *
1024
	 * @access private
1025
	 * @see _writeUrl()
1026
	 * @param integer $row1   Start row
1027
	 * @param integer $col1   Start column
1028
	 * @param integer $row2   End row
1029
	 * @param integer $col2   End column
1030
	 * @param string  $url	URL string
1031
	 * @return integer
1032
	 */
1033
	function _writeUrlInternal($row1, $col1, $row2, $col2, $url)
1034
	{
1035
		$record	  = 0x01B8;					   // Record identifier
1036
		$length	  = 0x00000;					  // Bytes to follow
1037
1038
		// Strip URL type
1039
		$url = preg_replace('/^internal:/', '', $url);
1040
1041
		// Pack the undocumented parts of the hyperlink stream
1042
		$unknown1	= pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
1043
1044
		// Pack the option flags
1045
		$options	 = pack("V", 0x08);
1046
1047
		// Convert the URL type and to a null terminated wchar string
1048
		$url .= "\0";
1049
1050
		// character count
1051
		$url_len = PHPExcel_Shared_String::CountCharacters($url);
1052
		$url_len = pack('V', $url_len);
1053
1054
		$url = PHPExcel_Shared_String::ConvertEncoding($url, 'UTF-16LE', 'UTF-8');
1055
1056
		// Calculate the data length
1057
		$length	  = 0x24 + strlen($url);
1058
1059
		// Pack the header data
1060
		$header	  = pack("vv",   $record, $length);
1061
		$data		= pack("vvvv", $row1, $row2, $col1, $col2);
1062
1063
		// Write the packed data
1064
		$this->_append($header . $data .
1065
					   $unknown1 . $options .
1066
					   $url_len . $url);
1067
		return 0;
1068
	}
1069
1070
	/**
1071
	 * Write links to external directory names such as 'c:\foo.xls',
1072
	 * c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'.
1073
	 *
1074
	 * Note: Excel writes some relative links with the $dir_long string. We ignore
1075
	 * these cases for the sake of simpler code.
1076
	 *
1077
	 * @access private
1078
	 * @see _writeUrl()
1079
	 * @param integer $row1   Start row
1080
	 * @param integer $col1   Start column
1081
	 * @param integer $row2   End row
1082
	 * @param integer $col2   End column
1083
	 * @param string  $url	URL string
1084
	 * @return integer
1085
	 */
1086
	function _writeUrlExternal($row1, $col1, $row2, $col2, $url)
1087
	{
1088
		// Network drives are different. We will handle them separately
1089
		// MS/Novell network drives and shares start with \\
1090
		if (preg_match('[^external:\\\\]', $url)) {
1091
			return; //($this->_writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format));
1092
		}
1093
1094
		$record	  = 0x01B8;					   // Record identifier
1095
		$length	  = 0x00000;					  // Bytes to follow
1096
1097
		// Strip URL type and change Unix dir separator to Dos style (if needed)
1098
		//
1099
		$url = preg_replace('/^external:/', '', $url);
1100
		$url = preg_replace('/\//', "\\", $url);
1101
1102
		// Determine if the link is relative or absolute:
1103
		//   relative if link contains no dir separator, "somefile.xls"
1104
		//   relative if link starts with up-dir, "..\..\somefile.xls"
1105
		//   otherwise, absolute
1106
1107
		$absolute = 0x00; // relative path
1108
		if ( preg_match('/^[A-Z]:/', $url) ) {
1109
			$absolute = 0x02; // absolute path on Windows, e.g. C:\...
1110
		}
1111
		$link_type			   = 0x01 | $absolute;
1112
1113
		// Determine if the link contains a sheet reference and change some of the
1114
		// parameters accordingly.
1115
		// Split the dir name and sheet name (if it exists)
1116
		$dir_long = $url;
1117
		if (preg_match("/\#/", $url)) {
1118
			$link_type |= 0x08;
1119
		}
1120
1121
1122
		// Pack the link type
1123
		$link_type   = pack("V", $link_type);
1124
1125
		// Calculate the up-level dir count e.g.. (..\..\..\ == 3)
1126
		$up_count	= preg_match_all("/\.\.\\\/", $dir_long, $useless);
1127
		$up_count	= pack("v", $up_count);
1128
1129
		// Store the short dos dir name (null terminated)
1130
		$dir_short   = preg_replace("/\.\.\\\/", '', $dir_long) . "\0";
1131
1132
		// Store the long dir name as a wchar string (non-null terminated)
1133
		$dir_long	   = $dir_long . "\0";
1134
1135
		// Pack the lengths of the dir strings
1136
		$dir_short_len = pack("V", strlen($dir_short)	  );
1137
		$dir_long_len  = pack("V", strlen($dir_long)	   );
1138
		$stream_len	= pack("V", 0);//strlen($dir_long) + 0x06);
1139
1140
		// Pack the undocumented parts of the hyperlink stream
1141
		$unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000'	   );
1142
		$unknown2 = pack("H*",'0303000000000000C000000000000046'			   );
1143
		$unknown3 = pack("H*",'FFFFADDE000000000000000000000000000000000000000');
1144
		$unknown4 = pack("v",  0x03											);
1145
1146
		// Pack the main data stream
1147
		$data		= pack("vvvv", $row1, $row2, $col1, $col2) .
1148
						  $unknown1	 .
1149
						  $link_type	.
1150
						  $unknown2	 .
1151
						  $up_count	 .
1152
						  $dir_short_len.
1153
						  $dir_short	.
1154
						  $unknown3	 .
1155
						  $stream_len   ;/*.
1156
						  $dir_long_len .
1157
						  $unknown4	 .
1158
						  $dir_long	 .
1159
						  $sheet_len	.
1160
						  $sheet		;*/
1161
1162
		// Pack the header data
1163
		$length   = strlen($data);
1164
		$header   = pack("vv", $record, $length);
1165
1166
		// Write the packed data
1167
		$this->_append($header. $data);
1168
		return 0;
1169
	}
1170
1171
	/**
1172
	 * This method is used to set the height and format for a row.
1173
	 *
1174
	 * @param integer $row	The row to set
1175
	 * @param integer $height Height we are giving to the row.
1176
	 *						Use null to set XF without setting height
1177
	 * @param integer $xfIndex  The optional cell style Xf index to apply to the columns
1178
	 * @param bool	$hidden The optional hidden attribute
1179
	 * @param integer $level  The optional outline level for row, in range [0,7]
1180
	 */
1181
	private function _writeRow($row, $height, $xfIndex, $hidden = false, $level = 0)
1182
	{
1183
		$record	  = 0x0208;			   // Record identifier
1184
		$length	  = 0x0010;			   // Number of bytes to follow
1185
1186
		$colMic	  = 0x0000;			   // First defined column
1187
		$colMac	  = 0x0000;			   // Last defined column
1188
		$irwMac	  = 0x0000;			   // Used by Excel to optimise loading
1189
		$reserved	= 0x0000;			   // Reserved
1190
		$grbit	   = 0x0000;			   // Option flags
1191
		$ixfe		= $xfIndex;
1192
1193
		if ( $height < 0 ){
1194
			$height = null;
1195
		}
1196
1197
		// Use _writeRow($row, null, $XF) to set XF format without setting height
1198
		if ($height != null) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $height of type null|integer against null; this is ambiguous if the integer can be zero. Consider using a strict comparison !== instead.
Loading history...
1199
			$miyRw = $height * 20;  // row height
1200
		} else {
1201
			$miyRw = 0xff;		  // default row height is 256
1202
		}
1203
1204
		// Set the options flags. fUnsynced is used to show that the font and row
1205
		// heights are not compatible. This is usually the case for WriteExcel.
1206
		// The collapsed flag 0x10 doesn't seem to be used to indicate that a row
1207
		// is collapsed. Instead it is used to indicate that the previous row is
1208
		// collapsed. The zero height flag, 0x20, is used to collapse a row.
1209
1210
		$grbit |= $level;
1211
		if ($hidden) {
1212
			$grbit |= 0x0020;
1213
		}
1214
		if ($height !== null) {
1215
			$grbit |= 0x0040; // fUnsynced
1216
		}
1217
		if ($xfIndex !== 0xF) {
1218
			$grbit |= 0x0080;
1219
		}
1220
		$grbit |= 0x0100;
1221
1222
		$header   = pack("vv",	   $record, $length);
1223
		$data	 = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw,
1224
									 $irwMac,$reserved, $grbit, $ixfe);
1225
		$this->_append($header.$data);
1226
	}
1227
1228
	/**
1229
	 * Writes Excel DIMENSIONS to define the area in which there is data.
1230
	 */
1231
	private function _writeDimensions()
1232
	{
1233
		$record = 0x0200; // Record identifier
1234
1235
		$length = 0x000E;
1236
		$data = pack('VVvvv'
1237
				, $this->_firstRowIndex
1238
				, $this->_lastRowIndex + 1
1239
				, $this->_firstColumnIndex
1240
				, $this->_lastColumnIndex + 1
1241
				, 0x0000 // reserved
1242
			);
1243
1244
		$header = pack("vv", $record, $length);
1245
		$this->_append($header.$data);
1246
	}
1247
1248
	/**
1249
	 * Write BIFF record Window2.
1250
	 */
1251
	private function _writeWindow2()
1252
	{
1253
		$record		 = 0x023E;	 // Record identifier
1254
		$length		 = 0x0012;
1255
1256
		$grbit		  = 0x00B6;	 // Option flags
1257
		$rwTop		  = 0x0000;	 // Top row visible in window
1258
		$colLeft		= 0x0000;	 // Leftmost column visible in window
1259
1260
1261
		// The options flags that comprise $grbit
1262
		$fDspFmla	   = 0;					 // 0 - bit
1263
		$fDspGrid	   = $this->_phpSheet->getShowGridlines() ? 1 : 0; // 1
1264
		$fDspRwCol	  = $this->_phpSheet->getShowRowColHeaders() ? 1 : 0; // 2
1265
		$fFrozen		= $this->_phpSheet->getFreezePane() ? 1 : 0;		// 3
1266
		$fDspZeros	  = 1;					 // 4
1267
		$fDefaultHdr	= 1;					 // 5
1268
		$fArabic		= $this->_phpSheet->getRightToLeft() ? 1 : 0; // 6
1269
		$fDspGuts	   = $this->_outline_on;	// 7
1270
		$fFrozenNoSplit = 0;					 // 0 - bit
1271
		// no support in PHPExcel for selected sheet, therefore sheet is only selected if it is the active sheet
1272
		$fSelected	  = ($this->_phpSheet === $this->_phpSheet->getParent()->getActiveSheet()) ? 1 : 0;
1273
		$fPaged		 = 1;					 // 2
1274
1275
		$grbit			 = $fDspFmla;
1276
		$grbit			|= $fDspGrid	   << 1;
1277
		$grbit			|= $fDspRwCol	  << 2;
1278
		$grbit			|= $fFrozen		<< 3;
1279
		$grbit			|= $fDspZeros	  << 4;
1280
		$grbit			|= $fDefaultHdr	<< 5;
1281
		$grbit			|= $fArabic		<< 6;
1282
		$grbit			|= $fDspGuts	   << 7;
1283
		$grbit			|= $fFrozenNoSplit << 8;
1284
		$grbit			|= $fSelected	  << 9;
1285
		$grbit			|= $fPaged		 << 10;
1286
1287
		$header  = pack("vv",   $record, $length);
1288
		$data	= pack("vvv", $grbit, $rwTop, $colLeft);
1289
1290
		// FIXME !!!
1291
		$rgbHdr	   = 0x0040; // Row/column heading and gridline color index
1292
		$zoom_factor_page_break = 0x0000;
1293
		$zoom_factor_normal	 = 0x0000;
1294
		$data .= pack("vvvvV", $rgbHdr, 0x0000, $zoom_factor_page_break, $zoom_factor_normal, 0x00000000);
1295
1296
		$this->_append($header.$data);
1297
	}
1298
1299
	/**
1300
	 * Write BIFF record DEFAULTROWHEIGHT.
1301
	 */
1302
	private function _writeDefaultRowHeight()
1303
	{
1304
		$defaultRowHeight = $this->_phpSheet->getDefaultRowDimension()->getRowHeight();
1305
1306
		if ($defaultRowHeight < 0) {
1307
			return;
1308
		}
1309
1310
		// convert to twips
1311
		$defaultRowHeight = (int) 20 * $defaultRowHeight;
1312
1313
		$record   = 0x0225;	  // Record identifier
1314
		$length   = 0x0004;	  // Number of bytes to follow
1315
1316
		$header   = pack("vv", $record, $length);
1317
		$data	 = pack("vv",  1, $defaultRowHeight);
1318
		$this->_append($header . $data);
1319
	}
1320
1321
	/**
1322
	 * Write BIFF record DEFCOLWIDTH if COLINFO records are in use.
1323
	 */
1324
	private function _writeDefcol()
1325
	{
1326
		$defaultColWidth = 8;
1327
1328
		$record   = 0x0055;	  // Record identifier
1329
		$length   = 0x0002;	  // Number of bytes to follow
1330
1331
		$header = pack("vv", $record, $length);
1332
		$data = pack("v", $defaultColWidth);
1333
		$this->_append($header . $data);
1334
	}
1335
1336
	/**
1337
	 * Write BIFF record COLINFO to define column widths
1338
	 *
1339
	 * Note: The SDK says the record length is 0x0B but Excel writes a 0x0C
1340
	 * length record.
1341
	 *
1342
	 * @param array $col_array This is the only parameter received and is composed of the following:
1343
	 *				0 => First formatted column,
1344
	 *				1 => Last formatted column,
1345
	 *				2 => Col width (8.43 is Excel default),
1346
	 *				3 => The optional XF format of the column,
1347
	 *				4 => Option flags.
1348
	 *				5 => Optional outline level
1349
	 */
1350
	private function _writeColinfo($col_array)
1351
	{
1352
		if (isset($col_array[0])) {
1353
			$colFirst = $col_array[0];
1354
		}
1355
		if (isset($col_array[1])) {
1356
			$colLast = $col_array[1];
1357
		}
1358
		if (isset($col_array[2])) {
1359
			$coldx = $col_array[2];
1360
		} else {
1361
			$coldx = 8.43;
1362
		}
1363
		if (isset($col_array[3])) {
1364
			$xfIndex = $col_array[3];
1365
		} else {
1366
			$xfIndex = 15;
1367
		}
1368
		if (isset($col_array[4])) {
1369
			$grbit = $col_array[4];
1370
		} else {
1371
			$grbit = 0;
1372
		}
1373
		if (isset($col_array[5])) {
1374
			$level = $col_array[5];
1375
		} else {
1376
			$level = 0;
1377
		}
1378
		$record   = 0x007D;		  // Record identifier
1379
		$length   = 0x000C;		  // Number of bytes to follow
1380
1381
		$coldx   *= 256;			 // Convert to units of 1/256 of a char
1382
1383
		$ixfe	 = $xfIndex;
1384
		$reserved = 0x0000;			// Reserved
1385
1386
		$level = max(0, min($level, 7));
1387
		$grbit |= $level << 8;
1388
1389
		$header   = pack("vv",	 $record, $length);
1390
		$data	 = pack("vvvvvv", $colFirst, $colLast, $coldx,
0 ignored issues
show
Bug introduced by
The variable $colFirst does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $colLast does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1391
								   $ixfe, $grbit, $reserved);
1392
		$this->_append($header.$data);
1393
	}
1394
1395
	/**
1396
	 * Write BIFF record SELECTION.
1397
	 */
1398
	private function _writeSelection()
1399
	{
1400
		// look up the selected cell range
1401
		$selectedCells = $this->_phpSheet->getSelectedCells();
1402
		$selectedCells = PHPExcel_Cell::splitRange($this->_phpSheet->getSelectedCells());
1403
		$selectedCells = $selectedCells[0];
1404
		if (count($selectedCells) == 2) {
1405
			list($first, $last) = $selectedCells;
1406
		} else {
1407
			$first = $selectedCells[0];
1408
			$last  = $selectedCells[0];
1409
		}
1410
1411
		list($colFirst, $rwFirst) = PHPExcel_Cell::coordinateFromString($first);
1412
		$colFirst = PHPExcel_Cell::columnIndexFromString($colFirst) - 1; // base 0 column index
1413
		--$rwFirst; // base 0 row index
1414
1415
		list($colLast, $rwLast) = PHPExcel_Cell::coordinateFromString($last);
1416
		$colLast = PHPExcel_Cell::columnIndexFromString($colLast) - 1; // base 0 column index
1417
		--$rwLast; // base 0 row index
1418
1419
		// make sure we are not out of bounds
1420
		$colFirst = min($colFirst, 255);
1421
		$colLast  = min($colLast,  255);
1422
1423
		$rwFirst = min($rwFirst, 65535);
1424
		$rwLast  = min($rwLast,  65535);
1425
1426
		$record   = 0x001D;				  // Record identifier
1427
		$length   = 0x000F;				  // Number of bytes to follow
1428
1429
		$pnn	  = $this->_active_pane;	 // Pane position
1430
		$rwAct	= $rwFirst;				// Active row
1431
		$colAct   = $colFirst;			   // Active column
1432
		$irefAct  = 0;					   // Active cell ref
1433
		$cref	 = 1;					   // Number of refs
1434
1435
		if (!isset($rwLast)) {
1436
			$rwLast   = $rwFirst;	   // Last  row in reference
1437
		}
1438
		if (!isset($colLast)) {
1439
			$colLast  = $colFirst;	  // Last  col in reference
1440
		}
1441
1442
		// Swap last row/col for first row/col as necessary
1443
		if ($rwFirst > $rwLast) {
1444
			list($rwFirst, $rwLast) = array($rwLast, $rwFirst);
1445
		}
1446
1447
		if ($colFirst > $colLast) {
1448
			list($colFirst, $colLast) = array($colLast, $colFirst);
1449
		}
1450
1451
		$header   = pack("vv",		 $record, $length);
1452
		$data	 = pack("CvvvvvvCC",  $pnn, $rwAct, $colAct,
1453
									   $irefAct, $cref,
1454
									   $rwFirst, $rwLast,
1455
									   $colFirst, $colLast);
1456
		$this->_append($header . $data);
1457
	}
1458
1459
	/**
1460
	 * Store the MERGEDCELLS records for all ranges of merged cells
1461
	 */
1462
	private function _writeMergedCells()
1463
	{
1464
		$mergeCells = $this->_phpSheet->getMergeCells();
1465
		$countMergeCells = count($mergeCells);
1466
1467
		if ($countMergeCells == 0) {
1468
			return;
1469
		}
1470
1471
		// maximum allowed number of merged cells per record
1472
		$maxCountMergeCellsPerRecord = 1027;
1473
1474
		// record identifier
1475
		$record = 0x00E5;
1476
1477
		// counter for total number of merged cells treated so far by the writer
1478
		$i = 0;
1479
1480
		// counter for number of merged cells written in record currently being written
1481
		$j = 0;
1482
1483
		// initialize record data
1484
		$recordData = '';
1485
1486
		// loop through the merged cells
1487
		foreach ($mergeCells as $mergeCell) {
1488
			++$i;
1489
			++$j;
1490
1491
			// extract the row and column indexes
1492
			$range = PHPExcel_Cell::splitRange($mergeCell);
1493
			list($first, $last) = $range[0];
1494
			list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($first);
1495
			list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($last);
1496
1497
			$recordData .= pack('vvvv', $firstRow - 1, $lastRow - 1, PHPExcel_Cell::columnIndexFromString($firstColumn) - 1, PHPExcel_Cell::columnIndexFromString($lastColumn) - 1);
1498
1499
			// flush record if we have reached limit for number of merged cells, or reached final merged cell
1500
			if ($j == $maxCountMergeCellsPerRecord or $i == $countMergeCells) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
1501
				$recordData = pack('v', $j) . $recordData;
1502
				$length = strlen($recordData);
1503
				$header = pack('vv', $record, $length);
1504
				$this->_append($header . $recordData);
1505
1506
				// initialize for next record, if any
1507
				$recordData = '';
1508
				$j = 0;
1509
			}
1510
		}
1511
	}
1512
1513
	/**
1514
	 * Write SHEETLAYOUT record
1515
	 */
1516
	private function _writeSheetLayout()
1517
	{
1518
		if (!$this->_phpSheet->isTabColorSet()) {
1519
			return;
1520
		}
1521
1522
		$recordData = pack(
1523
			'vvVVVvv'
1524
			, 0x0862
1525
			, 0x0000		// unused
1526
			, 0x00000000	// unused
1527
			, 0x00000000	// unused
1528
			, 0x00000014	// size of record data
1529
			, $this->_colors[$this->_phpSheet->getTabColor()->getRGB()]	// color index
1530
			, 0x0000		// unused
1531
		);
1532
1533
		$length = strlen($recordData);
1534
1535
		$record = 0x0862; // Record identifier
1536
		$header = pack('vv', $record, $length);
1537
		$this->_append($header . $recordData);
1538
	}
1539
1540
	/**
1541
	 * Write SHEETPROTECTION
1542
	 */
1543
	private function _writeSheetProtection()
1544
	{
1545
		// record identifier
1546
		$record = 0x0867;
1547
1548
		// prepare options
1549
		$options  =   (int) !$this->_phpSheet->getProtection()->getObjects()
1550
					| (int) !$this->_phpSheet->getProtection()->getScenarios()           << 1
1551
					| (int) !$this->_phpSheet->getProtection()->getFormatCells()         << 2
1552
					| (int) !$this->_phpSheet->getProtection()->getFormatColumns()       << 3
1553
					| (int) !$this->_phpSheet->getProtection()->getFormatRows()          << 4
1554
					| (int) !$this->_phpSheet->getProtection()->getInsertColumns()       << 5
1555
					| (int) !$this->_phpSheet->getProtection()->getInsertRows()          << 6
1556
					| (int) !$this->_phpSheet->getProtection()->getInsertHyperlinks()    << 7
1557
					| (int) !$this->_phpSheet->getProtection()->getDeleteColumns()       << 8
1558
					| (int) !$this->_phpSheet->getProtection()->getDeleteRows()          << 9
1559
					| (int) !$this->_phpSheet->getProtection()->getSelectLockedCells()   << 10
1560
					| (int) !$this->_phpSheet->getProtection()->getSort()                << 11
1561
					| (int) !$this->_phpSheet->getProtection()->getAutoFilter()          << 12
1562
					| (int) !$this->_phpSheet->getProtection()->getPivotTables()         << 13
1563
					| (int) !$this->_phpSheet->getProtection()->getSelectUnlockedCells() << 14 ;
1564
1565
		// record data
1566
		$recordData = pack(
1567
			'vVVCVVvv'
1568
			, 0x0867		// repeated record identifier
1569
			, 0x0000		// not used
1570
			, 0x0000		// not used
1571
			, 0x00			// not used
1572
			, 0x01000200	// unknown data
1573
			, 0xFFFFFFFF	// unknown data
1574
			, $options		// options
1575
			, 0x0000		// not used
1576
		);
1577
1578
		$length = strlen($recordData);
1579
		$header = pack('vv', $record, $length);
1580
1581
		$this->_append($header . $recordData);
1582
	}
1583
1584
	/**
1585
	 * Write BIFF record RANGEPROTECTION
1586
	 *
1587
	 * Openoffice.org's Documentaion of the Microsoft Excel File Format uses term RANGEPROTECTION for these records
1588
	 * Microsoft Office Excel 97-2007 Binary File Format Specification uses term FEAT for these records
1589
	 */
1590
	private function _writeRangeProtection()
1591
	{
1592
		foreach ($this->_phpSheet->getProtectedCells() as $range => $password) {
1593
			// number of ranges, e.g. 'A1:B3 C20:D25'
1594
			$cellRanges = explode(' ', $range);
1595
			$cref = count($cellRanges);
1596
1597
			$recordData = pack(
1598
				'vvVVvCVvVv',
1599
				0x0868,
1600
				0x00,
1601
				0x0000,
1602
				0x0000,
1603
				0x02,
1604
				0x0,
1605
				0x0000,
1606
				$cref,
1607
				0x0000,
1608
				0x00
1609
			);
1610
1611
			foreach ($cellRanges as $cellRange) {
1612
				$recordData .= $this->_writeBIFF8CellRangeAddressFixed($cellRange);
1613
			}
1614
1615
			// the rgbFeat structure
1616
			$recordData .= pack(
1617
				'VV',
1618
				0x0000,
1619
				hexdec($password)
1620
			);
1621
1622
			$recordData .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong('p' . md5($recordData));
1623
1624
			$length = strlen($recordData);
1625
1626
			$record = 0x0868;		// Record identifier
1627
			$header = pack("vv", $record, $length);
1628
			$this->_append($header . $recordData);
1629
		}
1630
	}
1631
1632
	/**
1633
	 * Write BIFF record EXTERNCOUNT to indicate the number of external sheet
1634
	 * references in a worksheet.
1635
	 *
1636
	 * Excel only stores references to external sheets that are used in formulas.
1637
	 * For simplicity we store references to all the sheets in the workbook
1638
	 * regardless of whether they are used or not. This reduces the overall
1639
	 * complexity and eliminates the need for a two way dialogue between the formula
1640
	 * parser the worksheet objects.
1641
	 *
1642
	 * @param integer $count The number of external sheet references in this worksheet
1643
	 */
1644 View Code Duplication
	private function _writeExterncount($count)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
1645
	{
1646
		$record = 0x0016;		  // Record identifier
1647
		$length = 0x0002;		  // Number of bytes to follow
1648
1649
		$header = pack("vv", $record, $length);
1650
		$data   = pack("v",  $count);
1651
		$this->_append($header . $data);
1652
	}
1653
1654
	/**
1655
	 * Writes the Excel BIFF EXTERNSHEET record. These references are used by
1656
	 * formulas. A formula references a sheet name via an index. Since we store a
1657
	 * reference to all of the external worksheets the EXTERNSHEET index is the same
1658
	 * as the worksheet index.
1659
	 *
1660
	 * @param string $sheetname The name of a external worksheet
1661
	 */
1662
	private function _writeExternsheet($sheetname)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
1663
	{
1664
		$record	= 0x0017;		 // Record identifier
1665
1666
		// References to the current sheet are encoded differently to references to
1667
		// external sheets.
1668
		//
1669
		if ($this->_phpSheet->getTitle() == $sheetname) {
1670
			$sheetname = '';
1671
			$length	= 0x02;  // The following 2 bytes
1672
			$cch	   = 1;	 // The following byte
1673
			$rgch	  = 0x02;  // Self reference
1674
		} else {
1675
			$length	= 0x02 + strlen($sheetname);
1676
			$cch	   = strlen($sheetname);
1677
			$rgch	  = 0x03;  // Reference to a sheet in the current workbook
1678
		}
1679
1680
		$header = pack("vv",  $record, $length);
1681
		$data   = pack("CC", $cch, $rgch);
1682
		$this->_append($header . $data . $sheetname);
1683
	}
1684
1685
	/**
1686
	 * Writes the Excel BIFF PANE record.
1687
	 * The panes can either be frozen or thawed (unfrozen).
1688
	 * Frozen panes are specified in terms of an integer number of rows and columns.
1689
	 * Thawed panes are specified in terms of Excel's units for rows and columns.
1690
	 */
1691
	private function _writePanes()
1692
	{
1693
		$panes = array();
1694
		if ($freezePane = $this->_phpSheet->getFreezePane()) {
1695
			list($column, $row) = PHPExcel_Cell::coordinateFromString($freezePane);
1696
			$panes[0] = $row - 1;
1697
			$panes[1] = PHPExcel_Cell::columnIndexFromString($column) - 1;
1698
		} else {
1699
			// thaw panes
1700
			return;
1701
		}
1702
1703
		$y	   = isset($panes[0]) ? $panes[0] : null;
1704
		$x	   = isset($panes[1]) ? $panes[1] : null;
1705
		$rwTop   = isset($panes[2]) ? $panes[2] : null;
1706
		$colLeft = isset($panes[3]) ? $panes[3] : null;
1707
		if (count($panes) > 4) { // if Active pane was received
1708
			$pnnAct = $panes[4];
1709
		} else {
1710
			$pnnAct = null;
1711
		}
1712
		$record  = 0x0041;	   // Record identifier
1713
		$length  = 0x000A;	   // Number of bytes to follow
1714
1715
		// Code specific to frozen or thawed panes.
1716
		if ($this->_phpSheet->getFreezePane()) {
1717
			// Set default values for $rwTop and $colLeft
1718
			if (!isset($rwTop)) {
1719
				$rwTop   = $y;
1720
			}
1721
			if (!isset($colLeft)) {
1722
				$colLeft = $x;
1723
			}
1724
		} else {
1725
			// Set default values for $rwTop and $colLeft
1726
			if (!isset($rwTop)) {
1727
				$rwTop   = 0;
1728
			}
1729
			if (!isset($colLeft)) {
1730
				$colLeft = 0;
1731
			}
1732
1733
			// Convert Excel's row and column units to the internal units.
1734
			// The default row height is 12.75
1735
			// The default column width is 8.43
1736
			// The following slope and intersection values were interpolated.
1737
			//
1738
			$y = 20*$y + 255;
1739
			$x = 113.879*$x + 390;
1740
		}
1741
1742
1743
		// Determine which pane should be active. There is also the undocumented
1744
		// option to override this should it be necessary: may be removed later.
1745
		//
1746
		if (!isset($pnnAct)) {
1747
			if ($x != 0 && $y != 0) {
1748
				$pnnAct = 0; // Bottom right
1749
			}
1750
			if ($x != 0 && $y == 0) {
1751
				$pnnAct = 1; // Top right
1752
			}
1753
			if ($x == 0 && $y != 0) {
1754
				$pnnAct = 2; // Bottom left
1755
			}
1756
			if ($x == 0 && $y == 0) {
1757
				$pnnAct = 3; // Top left
1758
			}
1759
		}
1760
1761
		$this->_active_pane = $pnnAct; // Used in _writeSelection
1762
1763
		$header	 = pack("vv",	$record, $length);
1764
		$data	   = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct);
1765
		$this->_append($header . $data);
1766
	}
1767
1768
	/**
1769
	 * Store the page setup SETUP BIFF record.
1770
	 */
1771
	private function _writeSetup()
1772
	{
1773
		$record	   = 0x00A1;				  // Record identifier
1774
		$length	   = 0x0022;				  // Number of bytes to follow
1775
1776
		$iPaperSize   = $this->_phpSheet->getPageSetup()->getPaperSize();	// Paper size
1777
1778
		$iScale = $this->_phpSheet->getPageSetup()->getScale() ?
1779
			$this->_phpSheet->getPageSetup()->getScale() : 100;   // Print scaling factor
1780
1781
		$iPageStart   = 0x01;				 // Starting page number
1782
		$iFitWidth	= (int) $this->_phpSheet->getPageSetup()->getFitToWidth();	// Fit to number of pages wide
1783
		$iFitHeight	= (int) $this->_phpSheet->getPageSetup()->getFitToHeight();	// Fit to number of pages high
1784
		$grbit		= 0x00;				 // Option flags
1785
		$iRes		 = 0x0258;			   // Print resolution
1786
		$iVRes		= 0x0258;			   // Vertical print resolution
1787
1788
		$numHdr	   = $this->_phpSheet->getPageMargins()->getHeader();  // Header Margin
1789
1790
		$numFtr	   = $this->_phpSheet->getPageMargins()->getFooter();   // Footer Margin
1791
		$iCopies	  = 0x01;				 // Number of copies
1792
1793
		$fLeftToRight = 0x0;					 // Print over then down
1794
1795
		// Page orientation
1796
		$fLandscape = ($this->_phpSheet->getPageSetup()->getOrientation() == PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE) ?
1797
			0x0 : 0x1;
1798
1799
		$fNoPls	   = 0x0;					 // Setup not read from printer
1800
		$fNoColor	 = 0x0;					 // Print black and white
1801
		$fDraft	   = 0x0;					 // Print draft quality
1802
		$fNotes	   = 0x0;					 // Print notes
1803
		$fNoOrient	= 0x0;					 // Orientation not set
1804
		$fUsePage	 = 0x0;					 // Use custom starting page
1805
1806
		$grbit		   = $fLeftToRight;
1807
		$grbit		  |= $fLandscape	<< 1;
1808
		$grbit		  |= $fNoPls		<< 2;
1809
		$grbit		  |= $fNoColor	  << 3;
1810
		$grbit		  |= $fDraft		<< 4;
1811
		$grbit		  |= $fNotes		<< 5;
1812
		$grbit		  |= $fNoOrient	 << 6;
1813
		$grbit		  |= $fUsePage	  << 7;
1814
1815
		$numHdr = pack("d", $numHdr);
1816
		$numFtr = pack("d", $numFtr);
1817
		if (self::getByteOrder()) { // if it's Big Endian
1818
			$numHdr = strrev($numHdr);
1819
			$numFtr = strrev($numFtr);
1820
		}
1821
1822
		$header = pack("vv", $record, $length);
1823
		$data1  = pack("vvvvvvvv", $iPaperSize,
1824
								   $iScale,
1825
								   $iPageStart,
1826
								   $iFitWidth,
1827
								   $iFitHeight,
1828
								   $grbit,
1829
								   $iRes,
1830
								   $iVRes);
1831
		$data2  = $numHdr.$numFtr;
1832
		$data3  = pack("v", $iCopies);
1833
		$this->_append($header . $data1 . $data2 . $data3);
1834
	}
1835
1836
	/**
1837
	 * Store the header caption BIFF record.
1838
	 */
1839 View Code Duplication
	private function _writeHeader()
1840
	{
1841
		$record  = 0x0014;			   // Record identifier
1842
1843
		/* removing for now
1844
		// need to fix character count (multibyte!)
1845
		if (strlen($this->_phpSheet->getHeaderFooter()->getOddHeader()) <= 255) {
1846
			$str	  = $this->_phpSheet->getHeaderFooter()->getOddHeader();	   // header string
1847
		} else {
1848
			$str = '';
1849
		}
1850
		*/
1851
1852
		$recordData = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($this->_phpSheet->getHeaderFooter()->getOddHeader());
1853
		$length = strlen($recordData);
1854
1855
		$header   = pack("vv", $record, $length);
1856
1857
		$this->_append($header . $recordData);
1858
	}
1859
1860
	/**
1861
	 * Store the footer caption BIFF record.
1862
	 */
1863 View Code Duplication
	private function _writeFooter()
1864
	{
1865
		$record  = 0x0015;			   // Record identifier
1866
1867
		/* removing for now
1868
		// need to fix character count (multibyte!)
1869
		if (strlen($this->_phpSheet->getHeaderFooter()->getOddFooter()) <= 255) {
1870
			$str = $this->_phpSheet->getHeaderFooter()->getOddFooter();
1871
		} else {
1872
			$str = '';
1873
		}
1874
		*/
1875
1876
		$recordData = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($this->_phpSheet->getHeaderFooter()->getOddFooter());
1877
		$length = strlen($recordData);
1878
1879
		$header	= pack("vv", $record, $length);
1880
1881
		$this->_append($header . $recordData);
1882
	}
1883
1884
	/**
1885
	 * Store the horizontal centering HCENTER BIFF record.
1886
	 *
1887
	 * @access private
1888
	 */
1889 View Code Duplication
	private function _writeHcenter()
1890
	{
1891
		$record   = 0x0083;			  // Record identifier
1892
		$length   = 0x0002;			  // Bytes to follow
1893
1894
		$fHCenter = $this->_phpSheet->getPageSetup()->getHorizontalCentered() ? 1 : 0;	 // Horizontal centering
1895
1896
		$header	= pack("vv", $record, $length);
1897
		$data	  = pack("v",  $fHCenter);
1898
1899
		$this->_append($header.$data);
1900
	}
1901
1902
	/**
1903
	 * Store the vertical centering VCENTER BIFF record.
1904
	 */
1905 View Code Duplication
	private function _writeVcenter()
1906
	{
1907
		$record   = 0x0084;			  // Record identifier
1908
		$length   = 0x0002;			  // Bytes to follow
1909
1910
		$fVCenter = $this->_phpSheet->getPageSetup()->getVerticalCentered() ? 1 : 0;	 // Horizontal centering
1911
1912
		$header	= pack("vv", $record, $length);
1913
		$data	  = pack("v",  $fVCenter);
1914
		$this->_append($header . $data);
1915
	}
1916
1917
	/**
1918
	 * Store the LEFTMARGIN BIFF record.
1919
	 */
1920 View Code Duplication
	private function _writeMarginLeft()
1921
	{
1922
		$record  = 0x0026;				   // Record identifier
1923
		$length  = 0x0008;				   // Bytes to follow
1924
1925
		$margin  = $this->_phpSheet->getPageMargins()->getLeft();	 // Margin in inches
1926
1927
		$header	= pack("vv",  $record, $length);
1928
		$data	  = pack("d",   $margin);
1929
		if (self::getByteOrder()) { // if it's Big Endian
1930
			$data = strrev($data);
1931
		}
1932
1933
		$this->_append($header . $data);
1934
	}
1935
1936
	/**
1937
	 * Store the RIGHTMARGIN BIFF record.
1938
	 */
1939 View Code Duplication
	private function _writeMarginRight()
1940
	{
1941
		$record  = 0x0027;				   // Record identifier
1942
		$length  = 0x0008;				   // Bytes to follow
1943
1944
		$margin  = $this->_phpSheet->getPageMargins()->getRight();	 // Margin in inches
1945
1946
		$header	= pack("vv",  $record, $length);
1947
		$data	  = pack("d",   $margin);
1948
		if (self::getByteOrder()) { // if it's Big Endian
1949
			$data = strrev($data);
1950
		}
1951
1952
		$this->_append($header . $data);
1953
	}
1954
1955
	/**
1956
	 * Store the TOPMARGIN BIFF record.
1957
	 */
1958 View Code Duplication
	private function _writeMarginTop()
1959
	{
1960
		$record  = 0x0028;				   // Record identifier
1961
		$length  = 0x0008;				   // Bytes to follow
1962
1963
		$margin  = $this->_phpSheet->getPageMargins()->getTop();	 // Margin in inches
1964
1965
		$header	= pack("vv",  $record, $length);
1966
		$data	  = pack("d",   $margin);
1967
		if (self::getByteOrder()) { // if it's Big Endian
1968
			$data = strrev($data);
1969
		}
1970
1971
		$this->_append($header . $data);
1972
	}
1973
1974
	/**
1975
	 * Store the BOTTOMMARGIN BIFF record.
1976
	 */
1977 View Code Duplication
	private function _writeMarginBottom()
1978
	{
1979
		$record  = 0x0029;				   // Record identifier
1980
		$length  = 0x0008;				   // Bytes to follow
1981
1982
		$margin  = $this->_phpSheet->getPageMargins()->getBottom();	 // Margin in inches
1983
1984
		$header	= pack("vv",  $record, $length);
1985
		$data	  = pack("d",   $margin);
1986
		if (self::getByteOrder()) { // if it's Big Endian
1987
			$data = strrev($data);
1988
		}
1989
1990
		$this->_append($header . $data);
1991
	}
1992
1993
	/**
1994
	 * Write the PRINTHEADERS BIFF record.
1995
	 */
1996
	private function _writePrintHeaders()
1997
	{
1998
		$record	  = 0x002a;				   // Record identifier
1999
		$length	  = 0x0002;				   // Bytes to follow
2000
2001
		$fPrintRwCol = $this->_print_headers;	 // Boolean flag
2002
2003
		$header	  = pack("vv", $record, $length);
2004
		$data		= pack("v", $fPrintRwCol);
2005
		$this->_append($header . $data);
2006
	}
2007
2008
	/**
2009
	 * Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the
2010
	 * GRIDSET record.
2011
	 */
2012 View Code Duplication
	private function _writePrintGridlines()
2013
	{
2014
		$record	  = 0x002b;					// Record identifier
2015
		$length	  = 0x0002;					// Bytes to follow
2016
2017
		$fPrintGrid  = $this->_phpSheet->getPrintGridlines() ? 1 : 0;	// Boolean flag
2018
2019
		$header	  = pack("vv", $record, $length);
2020
		$data		= pack("v", $fPrintGrid);
2021
		$this->_append($header . $data);
2022
	}
2023
2024
	/**
2025
	 * Write the GRIDSET BIFF record. Must be used in conjunction with the
2026
	 * PRINTGRIDLINES record.
2027
	 */
2028
	private function _writeGridset()
2029
	{
2030
		$record	  = 0x0082;						// Record identifier
2031
		$length	  = 0x0002;						// Bytes to follow
2032
2033
		$fGridSet	= !$this->_phpSheet->getPrintGridlines();	 // Boolean flag
2034
2035
		$header	  = pack("vv",  $record, $length);
2036
		$data		= pack("v",   $fGridSet);
2037
		$this->_append($header . $data);
2038
	}
2039
2040
	/**
2041
	 * Write the GUTS BIFF record. This is used to configure the gutter margins
2042
	 * where Excel outline symbols are displayed. The visibility of the gutters is
2043
	 * controlled by a flag in WSBOOL.
2044
	 *
2045
	 * @see _writeWsbool()
2046
	 */
2047
	private  function _writeGuts()
2048
	{
2049
		$record	  = 0x0080;   // Record identifier
2050
		$length	  = 0x0008;   // Bytes to follow
2051
2052
		$dxRwGut	 = 0x0000;   // Size of row gutter
2053
		$dxColGut	= 0x0000;   // Size of col gutter
2054
2055
		// determine maximum row outline level
2056
		$maxRowOutlineLevel = 0;
2057
		foreach ($this->_phpSheet->getRowDimensions() as $rowDimension) {
2058
			$maxRowOutlineLevel = max($maxRowOutlineLevel, $rowDimension->getOutlineLevel());
2059
		}
2060
2061
		$col_level   = 0;
2062
2063
		// Calculate the maximum column outline level. The equivalent calculation
2064
		// for the row outline level is carried out in _writeRow().
2065
		$colcount = count($this->_colinfo);
2066
		for ($i = 0; $i < $colcount; ++$i) {
2067
			$col_level = max($this->_colinfo[$i][5], $col_level);
2068
		}
2069
2070
		// Set the limits for the outline levels (0 <= x <= 7).
2071
		$col_level = max(0, min($col_level, 7));
2072
2073
		// The displayed level is one greater than the max outline levels
2074
		if ($maxRowOutlineLevel) {
2075
			++$maxRowOutlineLevel;
2076
		}
2077
		if ($col_level) {
2078
			++$col_level;
2079
		}
2080
2081
		$header	  = pack("vv",   $record, $length);
2082
		$data		= pack("vvvv", $dxRwGut, $dxColGut, $maxRowOutlineLevel, $col_level);
2083
2084
		$this->_append($header.$data);
2085
	}
2086
2087
	/**
2088
	 * Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction
2089
	 * with the SETUP record.
2090
	 */
2091
	private function _writeWsbool()
2092
	{
2093
		$record	  = 0x0081;   // Record identifier
2094
		$length	  = 0x0002;   // Bytes to follow
2095
		$grbit	   = 0x0000;
2096
2097
		// The only option that is of interest is the flag for fit to page. So we
2098
		// set all the options in one go.
2099
		//
2100
		// Set the option flags
2101
		$grbit |= 0x0001;						   // Auto page breaks visible
2102
		if ($this->_outline_style) {
2103
			$grbit |= 0x0020; // Auto outline styles
2104
		}
2105
		if ($this->_phpSheet->getShowSummaryBelow()) {
2106
			$grbit |= 0x0040; // Outline summary below
2107
		}
2108
		if ($this->_phpSheet->getShowSummaryRight()) {
2109
			$grbit |= 0x0080; // Outline summary right
2110
		}
2111
		if ($this->_phpSheet->getPageSetup()->getFitToPage()) {
2112
			$grbit |= 0x0100; // Page setup fit to page
2113
		}
2114
		if ($this->_outline_on) {
2115
			$grbit |= 0x0400; // Outline symbols displayed
2116
		}
2117
2118
		$header	  = pack("vv", $record, $length);
2119
		$data		= pack("v",  $grbit);
2120
		$this->_append($header . $data);
2121
	}
2122
2123
	/**
2124
	 * Write the HORIZONTALPAGEBREAKS and VERTICALPAGEBREAKS BIFF records.
2125
	 */
2126
	private function _writeBreaks()
2127
	{
2128
		// initialize
2129
		$vbreaks = array();
2130
		$hbreaks = array();
2131
2132
		foreach ($this->_phpSheet->getBreaks() as $cell => $breakType) {
2133
			// Fetch coordinates
2134
			$coordinates = PHPExcel_Cell::coordinateFromString($cell);
2135
2136
			// Decide what to do by the type of break
2137
			switch ($breakType) {
2138
				case PHPExcel_Worksheet::BREAK_COLUMN:
2139
					// Add to list of vertical breaks
2140
					$vbreaks[] = PHPExcel_Cell::columnIndexFromString($coordinates[0]) - 1;
2141
					break;
2142
2143
				case PHPExcel_Worksheet::BREAK_ROW:
2144
					// Add to list of horizontal breaks
2145
					$hbreaks[] = $coordinates[1];
2146
					break;
2147
2148
				case PHPExcel_Worksheet::BREAK_NONE:
2149
				default:
2150
					// Nothing to do
2151
					break;
2152
			}
2153
		}
2154
2155
		//horizontal page breaks
2156
		if (!empty($hbreaks)) {
2157
2158
			// Sort and filter array of page breaks
2159
			sort($hbreaks, SORT_NUMERIC);
2160
			if ($hbreaks[0] == 0) { // don't use first break if it's 0
2161
				array_shift($hbreaks);
2162
			}
2163
2164
			$record  = 0x001b;			   // Record identifier
2165
			$cbrk	= count($hbreaks);	   // Number of page breaks
2166
			$length  = 2 + 6 * $cbrk;	  // Bytes to follow
2167
2168
			$header  = pack("vv", $record, $length);
2169
			$data	= pack("v",  $cbrk);
2170
2171
			// Append each page break
2172
			foreach ($hbreaks as $hbreak) {
2173
				$data .= pack("vvv", $hbreak, 0x0000, 0x00ff);
2174
			}
2175
2176
			$this->_append($header . $data);
2177
		}
2178
2179
		// vertical page breaks
2180
		if (!empty($vbreaks)) {
2181
2182
			// 1000 vertical pagebreaks appears to be an internal Excel 5 limit.
2183
			// It is slightly higher in Excel 97/200, approx. 1026
2184
			$vbreaks = array_slice($vbreaks, 0, 1000);
2185
2186
			// Sort and filter array of page breaks
2187
			sort($vbreaks, SORT_NUMERIC);
2188
			if ($vbreaks[0] == 0) { // don't use first break if it's 0
2189
				array_shift($vbreaks);
2190
			}
2191
2192
			$record  = 0x001a;			   // Record identifier
2193
			$cbrk	= count($vbreaks);	   // Number of page breaks
2194
			$length  = 2 + 6 * $cbrk;	  // Bytes to follow
2195
2196
			$header  = pack("vv",  $record, $length);
2197
			$data	= pack("v",   $cbrk);
2198
2199
			// Append each page break
2200
			foreach ($vbreaks as $vbreak) {
2201
				$data .= pack("vvv", $vbreak, 0x0000, 0xffff);
2202
			}
2203
2204
			$this->_append($header . $data);
2205
		}
2206
	}
2207
2208
	/**
2209
	 * Set the Biff PROTECT record to indicate that the worksheet is protected.
2210
	 */
2211 View Code Duplication
	private function _writeProtect()
2212
	{
2213
		// Exit unless sheet protection has been specified
2214
		if (!$this->_phpSheet->getProtection()->getSheet()) {
2215
			return;
2216
		}
2217
2218
		$record	  = 0x0012;			 // Record identifier
2219
		$length	  = 0x0002;			 // Bytes to follow
2220
2221
		$fLock	   = 1;	// Worksheet is protected
2222
2223
		$header	  = pack("vv", $record, $length);
2224
		$data		= pack("v",  $fLock);
2225
2226
		$this->_append($header.$data);
2227
	}
2228
2229
	/**
2230
	 * Write SCENPROTECT
2231
	 */
2232 View Code Duplication
	private function _writeScenProtect()
2233
	{
2234
		// Exit if sheet protection is not active
2235
		if (!$this->_phpSheet->getProtection()->getSheet()) {
2236
			return;
2237
		}
2238
2239
		// Exit if scenarios are not protected
2240
		if (!$this->_phpSheet->getProtection()->getScenarios()) {
2241
			return;
2242
		}
2243
2244
		$record = 0x00DD; // Record identifier
2245
		$length = 0x0002; // Bytes to follow
2246
2247
		$header = pack('vv', $record, $length);
2248
		$data = pack('v', 1);
2249
2250
		$this->_append($header . $data);
2251
	}
2252
2253
	/**
2254
	 * Write OBJECTPROTECT
2255
	 */
2256 View Code Duplication
	private function _writeObjectProtect()
2257
	{
2258
		// Exit if sheet protection is not active
2259
		if (!$this->_phpSheet->getProtection()->getSheet()) {
2260
			return;
2261
		}
2262
2263
		// Exit if objects are not protected
2264
		if (!$this->_phpSheet->getProtection()->getObjects()) {
2265
			return;
2266
		}
2267
2268
		$record = 0x0063; // Record identifier
2269
		$length = 0x0002; // Bytes to follow
2270
2271
		$header = pack('vv', $record, $length);
2272
		$data = pack('v', 1);
2273
2274
		$this->_append($header . $data);
2275
	}
2276
2277
	/**
2278
	 * Write the worksheet PASSWORD record.
2279
	 */
2280
	private function _writePassword()
2281
	{
2282
		// Exit unless sheet protection and password have been specified
2283
		if (!$this->_phpSheet->getProtection()->getSheet() || !$this->_phpSheet->getProtection()->getPassword()) {
2284
			return;
2285
		}
2286
2287
		$record	  = 0x0013;			   // Record identifier
2288
		$length	  = 0x0002;			   // Bytes to follow
2289
2290
		$wPassword   = hexdec($this->_phpSheet->getProtection()->getPassword());	 // Encoded password
2291
2292
		$header	  = pack("vv", $record, $length);
2293
		$data		= pack("v",  $wPassword);
2294
2295
		$this->_append($header . $data);
2296
	}
2297
2298
	/**
2299
	 * Insert a 24bit bitmap image in a worksheet.
2300
	 *
2301
	 * @access public
2302
	 * @param integer $row	 The row we are going to insert the bitmap into
2303
	 * @param integer $col	 The column we are going to insert the bitmap into
2304
	 * @param mixed   $bitmap  The bitmap filename or GD-image resource
2305
	 * @param integer $x	   The horizontal position (offset) of the image inside the cell.
2306
	 * @param integer $y	   The vertical position (offset) of the image inside the cell.
2307
	 * @param float   $scale_x The horizontal scale
2308
	 * @param float   $scale_y The vertical scale
2309
	 */
2310
	function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1)
2311
	{
2312
		$bitmap_array = (is_resource($bitmap) ? $this->_processBitmapGd($bitmap) : $this->_processBitmap($bitmap));
2313
		list($width, $height, $size, $data) = $bitmap_array; //$this->_processBitmap($bitmap);
2314
2315
		// Scale the frame of the image.
2316
		$width  *= $scale_x;
2317
		$height *= $scale_y;
2318
2319
		// Calculate the vertices of the image and write the OBJ record
2320
		$this->_positionImage($col, $row, $x, $y, $width, $height);
2321
2322
		// Write the IMDATA record to store the bitmap data
2323
		$record	  = 0x007f;
2324
		$length	  = 8 + $size;
2325
		$cf		  = 0x09;
2326
		$env		 = 0x01;
2327
		$lcb		 = $size;
2328
2329
		$header	  = pack("vvvvV", $record, $length, $cf, $env, $lcb);
2330
		$this->_append($header.$data);
2331
	}
2332
2333
	/**
2334
	 * Calculate the vertices that define the position of the image as required by
2335
	 * the OBJ record.
2336
	 *
2337
	 *		 +------------+------------+
2338
	 *		 |	 A	  |	  B	 |
2339
	 *   +-----+------------+------------+
2340
	 *   |	 |(x1,y1)	 |			|
2341
	 *   |  1  |(A1)._______|______	  |
2342
	 *   |	 |	|			  |	 |
2343
	 *   |	 |	|			  |	 |
2344
	 *   +-----+----|	BITMAP	|-----+
2345
	 *   |	 |	|			  |	 |
2346
	 *   |  2  |	|______________.	 |
2347
	 *   |	 |			|		(B2)|
2348
	 *   |	 |			|	 (x2,y2)|
2349
	 *   +---- +------------+------------+
2350
	 *
2351
	 * Example of a bitmap that covers some of the area from cell A1 to cell B2.
2352
	 *
2353
	 * Based on the width and height of the bitmap we need to calculate 8 vars:
2354
	 *	 $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
2355
	 * The width and height of the cells are also variable and have to be taken into
2356
	 * account.
2357
	 * The values of $col_start and $row_start are passed in from the calling
2358
	 * function. The values of $col_end and $row_end are calculated by subtracting
2359
	 * the width and height of the bitmap from the width and height of the
2360
	 * underlying cells.
2361
	 * The vertices are expressed as a percentage of the underlying cell width as
2362
	 * follows (rhs values are in pixels):
2363
	 *
2364
	 *	   x1 = X / W *1024
2365
	 *	   y1 = Y / H *256
2366
	 *	   x2 = (X-1) / W *1024
2367
	 *	   y2 = (Y-1) / H *256
2368
	 *
2369
	 *	   Where:  X is distance from the left side of the underlying cell
2370
	 *			   Y is distance from the top of the underlying cell
2371
	 *			   W is the width of the cell
2372
	 *			   H is the height of the cell
2373
	 * The SDK incorrectly states that the height should be expressed as a
2374
	 *		percentage of 1024.
2375
	 *
2376
	 * @access private
2377
	 * @param integer $col_start Col containing upper left corner of object
2378
	 * @param integer $row_start Row containing top left corner of object
2379
	 * @param integer $x1		Distance to left side of object
2380
	 * @param integer $y1		Distance to top of object
2381
	 * @param integer $width	 Width of image frame
2382
	 * @param integer $height	Height of image frame
2383
	 */
2384
	function _positionImage($col_start, $row_start, $x1, $y1, $width, $height)
2385
	{
2386
		// Initialise end cell to the same as the start cell
2387
		$col_end	= $col_start;  // Col containing lower right corner of object
2388
		$row_end	= $row_start;  // Row containing bottom right corner of object
2389
2390
		// Zero the specified offset if greater than the cell dimensions
2391
		if ($x1 >= PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_start))) {
2392
			$x1 = 0;
2393
		}
2394
		if ($y1 >= PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_start + 1)) {
2395
			$y1 = 0;
2396
		}
2397
2398
		$width	  = $width  + $x1 -1;
2399
		$height	 = $height + $y1 -1;
2400
2401
		// Subtract the underlying cell widths to find the end cell of the image
2402
		while ($width >= PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end))) {
2403
			$width -= PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end));
2404
			++$col_end;
2405
		}
2406
2407
		// Subtract the underlying cell heights to find the end cell of the image
2408
		while ($height >= PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_end + 1)) {
2409
			$height -= PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_end + 1);
2410
			++$row_end;
2411
		}
2412
2413
		// Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
2414
		// with zero eight or width.
2415
		//
2416
		if (PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_start)) == 0) {
2417
			return;
2418
		}
2419
		if (PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end))   == 0) {
2420
			return;
2421
		}
2422
		if (PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_start + 1) == 0) {
2423
			return;
2424
		}
2425
		if (PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_end + 1)   == 0) {
2426
			return;
2427
		}
2428
2429
		// Convert the pixel values to the percentage value expected by Excel
2430
		$x1 = $x1	 / PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_start))   * 1024;
2431
		$y1 = $y1	 / PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_start + 1)   *  256;
2432
		$x2 = $width  / PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end))	 * 1024; // Distance to right side of object
2433
		$y2 = $height / PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_end + 1)	 *  256; // Distance to bottom of object
2434
2435
		$this->_writeObjPicture($col_start, $x1,
2436
								 $row_start, $y1,
2437
								 $col_end, $x2,
2438
								 $row_end, $y2);
2439
	}
2440
2441
	/**
2442
	 * Store the OBJ record that precedes an IMDATA record. This could be generalise
2443
	 * to support other Excel objects.
2444
	 *
2445
	 * @param integer $colL Column containing upper left corner of object
2446
	 * @param integer $dxL  Distance from left side of cell
2447
	 * @param integer $rwT  Row containing top left corner of object
2448
	 * @param integer $dyT  Distance from top of cell
2449
	 * @param integer $colR Column containing lower right corner of object
2450
	 * @param integer $dxR  Distance from right of cell
2451
	 * @param integer $rwB  Row containing bottom right corner of object
2452
	 * @param integer $dyB  Distance from bottom of cell
2453
	 */
2454
	private function _writeObjPicture($colL,$dxL,$rwT,$dyT,$colR,$dxR,$rwB,$dyB)
2455
	{
2456
		$record	  = 0x005d;   // Record identifier
2457
		$length	  = 0x003c;   // Bytes to follow
2458
2459
		$cObj		= 0x0001;   // Count of objects in file (set to 1)
2460
		$OT		  = 0x0008;   // Object type. 8 = Picture
2461
		$id		  = 0x0001;   // Object ID
2462
		$grbit	   = 0x0614;   // Option flags
2463
2464
		$cbMacro	 = 0x0000;   // Length of FMLA structure
2465
		$Reserved1   = 0x0000;   // Reserved
2466
		$Reserved2   = 0x0000;   // Reserved
2467
2468
		$icvBack	 = 0x09;	 // Background colour
2469
		$icvFore	 = 0x09;	 // Foreground colour
2470
		$fls		 = 0x00;	 // Fill pattern
2471
		$fAuto	   = 0x00;	 // Automatic fill
2472
		$icv		 = 0x08;	 // Line colour
2473
		$lns		 = 0xff;	 // Line style
2474
		$lnw		 = 0x01;	 // Line weight
2475
		$fAutoB	  = 0x00;	 // Automatic border
2476
		$frs		 = 0x0000;   // Frame style
2477
		$cf		  = 0x0009;   // Image format, 9 = bitmap
2478
		$Reserved3   = 0x0000;   // Reserved
2479
		$cbPictFmla  = 0x0000;   // Length of FMLA structure
2480
		$Reserved4   = 0x0000;   // Reserved
2481
		$grbit2	  = 0x0001;   // Option flags
2482
		$Reserved5   = 0x0000;   // Reserved
2483
2484
2485
		$header	  = pack("vv", $record, $length);
2486
		$data		= pack("V", $cObj);
2487
		$data	   .= pack("v", $OT);
2488
		$data	   .= pack("v", $id);
2489
		$data	   .= pack("v", $grbit);
2490
		$data	   .= pack("v", $colL);
2491
		$data	   .= pack("v", $dxL);
2492
		$data	   .= pack("v", $rwT);
2493
		$data	   .= pack("v", $dyT);
2494
		$data	   .= pack("v", $colR);
2495
		$data	   .= pack("v", $dxR);
2496
		$data	   .= pack("v", $rwB);
2497
		$data	   .= pack("v", $dyB);
2498
		$data	   .= pack("v", $cbMacro);
2499
		$data	   .= pack("V", $Reserved1);
2500
		$data	   .= pack("v", $Reserved2);
2501
		$data	   .= pack("C", $icvBack);
2502
		$data	   .= pack("C", $icvFore);
2503
		$data	   .= pack("C", $fls);
2504
		$data	   .= pack("C", $fAuto);
2505
		$data	   .= pack("C", $icv);
2506
		$data	   .= pack("C", $lns);
2507
		$data	   .= pack("C", $lnw);
2508
		$data	   .= pack("C", $fAutoB);
2509
		$data	   .= pack("v", $frs);
2510
		$data	   .= pack("V", $cf);
2511
		$data	   .= pack("v", $Reserved3);
2512
		$data	   .= pack("v", $cbPictFmla);
2513
		$data	   .= pack("v", $Reserved4);
2514
		$data	   .= pack("v", $grbit2);
2515
		$data	   .= pack("V", $Reserved5);
2516
2517
		$this->_append($header . $data);
2518
	}
2519
2520
	/**
2521
	 * Convert a GD-image into the internal format.
2522
	 *
2523
	 * @access private
2524
	 * @param resource $image The image to process
2525
	 * @return array Array with data and properties of the bitmap
2526
	 */
2527
	function _processBitmapGd($image) {
2528
		$width = imagesx($image);
2529
		$height = imagesy($image);
2530
2531
		$data = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18);
2532
		for ($j=$height; $j--; ) {
2533
			for ($i=0; $i < $width; ++$i) {
2534
				$color = imagecolorsforindex($image, imagecolorat($image, $i, $j));
2535
				foreach (array("red", "green", "blue") as $key) {
2536
					$color[$key] = $color[$key] + round((255 - $color[$key]) * $color["alpha"] / 127);
2537
				}
2538
				$data .= chr($color["blue"]) . chr($color["green"]) . chr($color["red"]);
2539
			}
2540
			if (3*$width % 4) {
2541
				$data .= str_repeat("\x00", 4 - 3*$width % 4);
2542
			}
2543
		}
2544
2545
		return array($width, $height, strlen($data), $data);
2546
	}
2547
2548
	/**
2549
	 * Convert a 24 bit bitmap into the modified internal format used by Windows.
2550
	 * This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the
2551
	 * MSDN library.
2552
	 *
2553
	 * @access private
2554
	 * @param string $bitmap The bitmap to process
2555
	 * @return array Array with data and properties of the bitmap
2556
	 */
2557
	function _processBitmap($bitmap)
2558
	{
2559
		// Open file.
2560
		$bmp_fd = @fopen($bitmap,"rb");
2561
		if (!$bmp_fd) {
2562
			throw new Exception("Couldn't import $bitmap");
2563
		}
2564
2565
		// Slurp the file into a string.
2566
		$data = fread($bmp_fd, filesize($bitmap));
2567
2568
		// Check that the file is big enough to be a bitmap.
2569
		if (strlen($data) <= 0x36) {
2570
			throw new Exception("$bitmap doesn't contain enough data.\n");
2571
		}
2572
2573
		// The first 2 bytes are used to identify the bitmap.
2574
		$identity = unpack("A2ident", $data);
2575
		if ($identity['ident'] != "BM") {
2576
			throw new Exception("$bitmap doesn't appear to be a valid bitmap image.\n");
2577
		}
2578
2579
		// Remove bitmap data: ID.
2580
		$data = substr($data, 2);
2581
2582
		// Read and remove the bitmap size. This is more reliable than reading
2583
		// the data size at offset 0x22.
2584
		//
2585
		$size_array   = unpack("Vsa", substr($data, 0, 4));
2586
		$size   = $size_array['sa'];
2587
		$data   = substr($data, 4);
2588
		$size  -= 0x36; // Subtract size of bitmap header.
2589
		$size  += 0x0C; // Add size of BIFF header.
2590
2591
		// Remove bitmap data: reserved, offset, header length.
2592
		$data = substr($data, 12);
2593
2594
		// Read and remove the bitmap width and height. Verify the sizes.
2595
		$width_and_height = unpack("V2", substr($data, 0, 8));
2596
		$width  = $width_and_height[1];
2597
		$height = $width_and_height[2];
2598
		$data   = substr($data, 8);
2599
		if ($width > 0xFFFF) {
2600
			throw new Exception("$bitmap: largest image width supported is 65k.\n");
2601
		}
2602
		if ($height > 0xFFFF) {
2603
			throw new Exception("$bitmap: largest image height supported is 65k.\n");
2604
		}
2605
2606
		// Read and remove the bitmap planes and bpp data. Verify them.
2607
		$planes_and_bitcount = unpack("v2", substr($data, 0, 4));
2608
		$data = substr($data, 4);
2609
		if ($planes_and_bitcount[2] != 24) { // Bitcount
2610
			throw new Exception("$bitmap isn't a 24bit true color bitmap.\n");
2611
		}
2612
		if ($planes_and_bitcount[1] != 1) {
2613
			throw new Exception("$bitmap: only 1 plane supported in bitmap image.\n");
2614
		}
2615
2616
		// Read and remove the bitmap compression. Verify compression.
2617
		$compression = unpack("Vcomp", substr($data, 0, 4));
2618
		$data = substr($data, 4);
2619
2620
		//$compression = 0;
2621
		if ($compression['comp'] != 0) {
2622
			throw new Exception("$bitmap: compression not supported in bitmap image.\n");
2623
		}
2624
2625
		// Remove bitmap data: data size, hres, vres, colours, imp. colours.
2626
		$data = substr($data, 20);
2627
2628
		// Add the BITMAPCOREHEADER data
2629
		$header  = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18);
2630
		$data	= $header . $data;
2631
2632
		return (array($width, $height, $size, $data));
2633
	}
2634
2635
	/**
2636
	 * Store the window zoom factor. This should be a reduced fraction but for
2637
	 * simplicity we will store all fractions with a numerator of 100.
2638
	 */
2639 View Code Duplication
	private function _writeZoom()
2640
	{
2641
		// If scale is 100 we don't need to write a record
2642
		if ($this->_phpSheet->getSheetView()->getZoomScale() == 100) {
2643
			return;
2644
		}
2645
2646
		$record	  = 0x00A0;			   // Record identifier
2647
		$length	  = 0x0004;			   // Bytes to follow
2648
2649
		$header	  = pack("vv", $record, $length);
2650
		$data		= pack("vv", $this->_phpSheet->getSheetView()->getZoomScale(), 100);
2651
		$this->_append($header . $data);
2652
	}
2653
2654
	/**
2655
	 * Get Escher object
2656
	 *
2657
	 * @return PHPExcel_Shared_Escher
2658
	 */
2659
	public function getEscher()
2660
	{
2661
		return $this->_escher;
2662
	}
2663
2664
	/**
2665
	 * Set Escher object
2666
	 *
2667
	 * @param PHPExcel_Shared_Escher $pValue
2668
	 */
2669
	public function setEscher(PHPExcel_Shared_Escher $pValue = null)
2670
	{
2671
		$this->_escher = $pValue;
2672
	}
2673
2674
	/**
2675
	 * Write MSODRAWING record
2676
	 */
2677
	private function _writeMsoDrawing()
2678
	{
2679
		// write the Escher stream if necessary
2680
		if (isset($this->_escher)) {
2681
			$writer = new PHPExcel_Writer_Excel5_Escher($this->_escher);
2682
			$data = $writer->close();
2683
			$spOffsets = $writer->getSpOffsets();
2684
2685
			// write the neccesary MSODRAWING, OBJ records
2686
2687
			// split the Escher stream
2688
			$spOffsets[0] = 0;
2689
			$nm = count($spOffsets) - 1; // number of shapes excluding first shape
2690
			for ($i = 1; $i <= $nm; ++$i) {
2691
				// MSODRAWING record
2692
				$record = 0x00EC;			// Record identifier
2693
2694
				// chunk of Escher stream for one shape
2695
2696
				$dataChunk = substr($data, $spOffsets[$i -1], $spOffsets[$i] - $spOffsets[$i - 1]);
2697
2698
				$length = strlen($dataChunk);
2699
				$header = pack("vv", $record, $length);
2700
2701
				$this->_append($header . $dataChunk);
2702
2703
				// OBJ record
2704
				$record = 0x005D; // record identifier
2705
				$objData = '';
2706
2707
				// ftCmo
2708
				$objData .=
2709
					pack('vvvvvVVV'
2710
						, 0x0015	// 0x0015 = ftCmo
2711
						, 0x0012	// length of ftCmo data
2712
						, 0x0008	// object type, 0x0008 = picture
2713
						, $i		// object id number, Excel seems to use 1-based index, local for the sheet
2714
						, 0x6011	// option flags, 0x6011 is what OpenOffice.org uses
2715
						, 0			// reserved
2716
						, 0			// reserved
2717
						, 0			// reserved
2718
					);
2719
				// ftEnd
2720
				$objData .=
2721
					pack('vv'
2722
						, 0x0000	// 0x0000 = ftEnd
2723
						, 0x0000	// length of ftEnd data
2724
					);
2725
2726
				$length = strlen($objData);
2727
				$header = pack('vv', $record, $length);
2728
				$this->_append($header . $objData);
2729
			}
2730
		}
2731
	}
2732
2733
	/**
2734
	 * Store the DATAVALIDATIONS and DATAVALIDATION records.
2735
	 */
2736
	private function _writeDataValidity()
2737
	{
2738
		// Datavalidation collection
2739
		$dataValidationCollection = $this->_phpSheet->getDataValidationCollection();
2740
2741
		// Write data validations?
2742
		if (!empty($dataValidationCollection)) {
2743
2744
			// DATAVALIDATIONS record
2745
			$record = 0x01B2;	  // Record identifier
2746
			$length	  = 0x0012;	  // Bytes to follow
2747
2748
			$grbit  = 0x0000;	   // Prompt box at cell, no cached validity data at DV records
2749
			$horPos	  = 0x00000000;  // Horizontal position of prompt box, if fixed position
2750
			$verPos	  = 0x00000000;  // Vertical position of prompt box, if fixed position
2751
			$objId  = 0xFFFFFFFF;  // Object identifier of drop down arrow object, or -1 if not visible
2752
2753
			$header	  = pack('vv', $record, $length);
2754
			$data		= pack('vVVVV', $grbit, $horPos, $verPos, $objId,
2755
										 count($dataValidationCollection));
2756
			$this->_append($header.$data);
2757
2758
			// DATAVALIDATION records
2759
			$record = 0x01BE;			  // Record identifier
2760
2761
			foreach ($dataValidationCollection as $cellCoordinate => $dataValidation) {
2762
				// initialize record data
2763
				$data = '';
2764
2765
				// options
2766
				$options = 0x00000000;
2767
2768
				// data type
2769
				$type = $dataValidation->getType();
2770 View Code Duplication
				switch ($type) {
2771
					case PHPExcel_Cell_DataValidation::TYPE_NONE:		$type = 0x00;	break;
2772
					case PHPExcel_Cell_DataValidation::TYPE_WHOLE:		$type = 0x01;	break;
2773
					case PHPExcel_Cell_DataValidation::TYPE_DECIMAL:	$type = 0x02;	break;
2774
					case PHPExcel_Cell_DataValidation::TYPE_LIST:		$type = 0x03;	break;
2775
					case PHPExcel_Cell_DataValidation::TYPE_DATE:		$type = 0x04;	break;
2776
					case PHPExcel_Cell_DataValidation::TYPE_TIME:		$type = 0x05;	break;
2777
					case PHPExcel_Cell_DataValidation::TYPE_TEXTLENGTH:	$type = 0x06;	break;
2778
					case PHPExcel_Cell_DataValidation::TYPE_CUSTOM:		$type = 0x07;	break;
2779
				}
2780
				$options |= $type << 0;
2781
2782
				// error style
2783
				$errorStyle = $dataValidation->getType();
2784 View Code Duplication
				switch ($errorStyle) {
2785
					case PHPExcel_Cell_DataValidation::STYLE_STOP:			$errorStyle = 0x00;		break;
2786
					case PHPExcel_Cell_DataValidation::STYLE_WARNING:		$errorStyle = 0x01;		break;
2787
					case PHPExcel_Cell_DataValidation::STYLE_INFORMATION:	$errorStyle = 0x02;		break;
2788
				}
2789
				$options |= $errorStyle << 4;
2790
2791
				// explicit formula?
2792
				if ($type == 0x03 && preg_match('/^\".*\"$/', $dataValidation->getFormula1())) {
2793
					$options |= 0x01				<< 7;
2794
				}
2795
2796
				// empty cells allowed
2797
				$options |= $dataValidation->getAllowBlank() << 8;
2798
2799
				// show drop down
2800
				$options |= (!$dataValidation->getShowDropDown()) << 9;
2801
2802
				// show input message
2803
				$options |= $dataValidation->getShowInputMessage() << 18;
2804
2805
				// show error message
2806
				$options |= $dataValidation->getShowErrorMessage() << 19;
2807
2808
				// condition operator
2809
				$operator = $dataValidation->getOperator();
2810 View Code Duplication
				switch ($operator) {
2811
					case PHPExcel_Cell_DataValidation::OPERATOR_BETWEEN: $operator = 0x00			;	break;
2812
					case PHPExcel_Cell_DataValidation::OPERATOR_NOTBETWEEN: $operator = 0x01		;	break;
2813
					case PHPExcel_Cell_DataValidation::OPERATOR_EQUAL: $operator = 0x02				;	break;
2814
					case PHPExcel_Cell_DataValidation::OPERATOR_NOTEQUAL: $operator = 0x03			;	break;
2815
					case PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHAN: $operator = 0x04		;	break;
2816
					case PHPExcel_Cell_DataValidation::OPERATOR_LESSTHAN: $operator = 0x05			;	break;
2817
					case PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHANOREQUAL: $operator = 0x06;	break;
2818
					case PHPExcel_Cell_DataValidation::OPERATOR_LESSTHANOREQUAL: $operator = 0x07	;	break;
2819
				}
2820
				$options |= $operator << 20;
2821
2822
				$data		= pack('V', $options);
2823
2824
				// prompt title
2825
				$promptTitle = $dataValidation->getPromptTitle() !== '' ?
2826
					$dataValidation->getPromptTitle() : chr(0);
2827
				$data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($promptTitle);
2828
2829
				// error title
2830
				$errorTitle = $dataValidation->getErrorTitle() !== '' ?
2831
					$dataValidation->getErrorTitle() : chr(0);
2832
				$data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($errorTitle);
2833
2834
				// prompt text
2835
				$prompt = $dataValidation->getPrompt() !== '' ?
2836
					$dataValidation->getPrompt() : chr(0);
2837
				$data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($prompt);
2838
2839
				// error text
2840
				$error = $dataValidation->getError() !== '' ?
2841
					$dataValidation->getError() : chr(0);
2842
				$data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($error);
2843
2844
				// formula 1
2845
				try {
2846
					$formula1 = $dataValidation->getFormula1();
2847
					if ($type == 0x03) { // list type
2848
						$formula1 = str_replace(',', chr(0), $formula1);
2849
					}
2850
					$this->_parser->parse($formula1);
2851
					$formula1 = $this->_parser->toReversePolish();
2852
					$sz1 = strlen($formula1);
2853
2854
				} catch(Exception $e) {
2855
					$sz1 = 0;
2856
					$formula1 = '';
2857
				}
2858
				$data .= pack('vv', $sz1, 0x0000);
2859
				$data .= $formula1;
2860
2861
				// formula 2
2862
				try {
2863
					$formula2 = $dataValidation->getFormula2();
2864
					if ($formula2 === '') {
2865
						throw new Exception('No formula2');
2866
					}
2867
					$this->_parser->parse($formula2);
2868
					$formula2 = $this->_parser->toReversePolish();
2869
					$sz2 = strlen($formula2);
2870
2871
				} catch(Exception $e) {
2872
					$sz2 = 0;
2873
					$formula2 = '';
2874
				}
2875
				$data .= pack('vv', $sz2, 0x0000);
2876
				$data .= $formula2;
2877
2878
				// cell range address list
2879
				$data .= pack('v', 0x0001);
2880
				$data .= $this->_writeBIFF8CellRangeAddressFixed($cellCoordinate);
2881
2882
				$length = strlen($data);
2883
			$header = pack("vv", $record, $length);
2884
2885
				$this->_append($header . $data);
2886
			}
2887
		}
2888
	}
2889
2890
	/**
2891
	 * Map Error code
2892
	 *
2893
	 * @param string $errorCode
2894
	 * @return int
2895
	 */
2896
	private static function _mapErrorCode($errorCode) {
2897
		switch ($errorCode) {
2898
			case '#NULL!':	return 0x00;
2899
			case '#DIV/0!':	return 0x07;
2900
			case '#VALUE!':	return 0x0F;
2901
			case '#REF!':	return 0x17;
2902
			case '#NAME?':	return 0x1D;
2903
			case '#NUM!':	return 0x24;
2904
			case '#N/A':	return 0x2A;
2905
		}
2906
2907
		return 0;
2908
	}
2909
2910
}