Subversion Repositories eFlore/Applications.cel

Rev

Rev 2388 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2388 jpm 1
<?php
2
/**
3
 * PHPExcel
4
 *
5
 * Copyright (c) 2006 - 2013 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 - 2013 PHPExcel (http://www.codeplex.com/PHPExcel)
24
 * @license	http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt	LGPL
25
 * @version	##VERSION##, ##DATE##
26
 */
27
 
28
 
29
/**
30
 * PHPExcel_Writer_Excel5
31
 *
32
 * @category   PHPExcel
33
 * @package    PHPExcel_Writer_Excel5
34
 * @copyright  Copyright (c) 2006 - 2013 PHPExcel (http://www.codeplex.com/PHPExcel)
35
 */
36
class PHPExcel_Writer_Excel5 extends PHPExcel_Writer_Abstract implements PHPExcel_Writer_IWriter
37
{
38
	/**
39
	 * PHPExcel object
40
	 *
41
	 * @var PHPExcel
42
	 */
43
	private $_phpExcel;
44
 
45
	/**
46
	 * Total number of shared strings in workbook
47
	 *
48
	 * @var int
49
	 */
50
	private $_str_total		= 0;
51
 
52
	/**
53
	 * Number of unique shared strings in workbook
54
	 *
55
	 * @var int
56
	 */
57
	private $_str_unique	= 0;
58
 
59
	/**
60
	 * Array of unique shared strings in workbook
61
	 *
62
	 * @var array
63
	 */
64
	private $_str_table		= array();
65
 
66
	/**
67
	 * Color cache. Mapping between RGB value and color index.
68
	 *
69
	 * @var array
70
	 */
71
	private $_colors;
72
 
73
	/**
74
	 * Formula parser
75
	 *
76
	 * @var PHPExcel_Writer_Excel5_Parser
77
	 */
78
	private $_parser;
79
 
80
	/**
81
	 * Identifier clusters for drawings. Used in MSODRAWINGGROUP record.
82
	 *
83
	 * @var array
84
	 */
85
	private $_IDCLs;
86
 
87
	/**
88
	 * Basic OLE object summary information
89
	 *
90
	 * @var array
91
	 */
92
	private $_summaryInformation;
93
 
94
	/**
95
	 * Extended OLE object document summary information
96
	 *
97
	 * @var array
98
	 */
99
	private $_documentSummaryInformation;
100
 
101
	/**
102
	 * Create a new PHPExcel_Writer_Excel5
103
	 *
104
	 * @param	PHPExcel	$phpExcel	PHPExcel object
105
	 */
106
	public function __construct(PHPExcel $phpExcel) {
107
		$this->_phpExcel	= $phpExcel;
108
 
109
		$this->_parser		= new PHPExcel_Writer_Excel5_Parser();
110
	}
111
 
112
	/**
113
	 * Save PHPExcel to file
114
	 *
115
	 * @param	string		$pFilename
116
	 * @throws	PHPExcel_Writer_Exception
117
	 */
118
	public function save($pFilename = null) {
119
 
120
		// garbage collect
121
		$this->_phpExcel->garbageCollect();
122
 
123
		$saveDebugLog = PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->getWriteDebugLog();
124
		PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog(FALSE);
125
		$saveDateReturnType = PHPExcel_Calculation_Functions::getReturnDateType();
126
		PHPExcel_Calculation_Functions::setReturnDateType(PHPExcel_Calculation_Functions::RETURNDATE_EXCEL);
127
 
128
		// initialize colors array
129
		$this->_colors          = array();
130
 
131
		// Initialise workbook writer
132
		$this->_writerWorkbook = new PHPExcel_Writer_Excel5_Workbook($this->_phpExcel,
133
																	 $this->_str_total, $this->_str_unique, $this->_str_table,
134
																	 $this->_colors, $this->_parser);
135
 
136
		// Initialise worksheet writers
137
		$countSheets = $this->_phpExcel->getSheetCount();
138
		for ($i = 0; $i < $countSheets; ++$i) {
139
			$this->_writerWorksheets[$i] = new PHPExcel_Writer_Excel5_Worksheet($this->_str_total, $this->_str_unique,
140
																			   $this->_str_table, $this->_colors,
141
																			   $this->_parser,
142
																			   $this->_preCalculateFormulas,
143
																			   $this->_phpExcel->getSheet($i));
144
		}
145
 
146
		// build Escher objects. Escher objects for workbooks needs to be build before Escher object for workbook.
147
		$this->_buildWorksheetEschers();
148
		$this->_buildWorkbookEscher();
149
 
150
		// add 15 identical cell style Xfs
151
		// for now, we use the first cellXf instead of cellStyleXf
152
		$cellXfCollection = $this->_phpExcel->getCellXfCollection();
153
		for ($i = 0; $i < 15; ++$i) {
154
			$this->_writerWorkbook->addXfWriter($cellXfCollection[0], true);
155
		}
156
 
157
		// add all the cell Xfs
158
		foreach ($this->_phpExcel->getCellXfCollection() as $style) {
159
			$this->_writerWorkbook->addXfWriter($style, false);
160
		}
161
 
162
		// add fonts from rich text eleemnts
163
		for ($i = 0; $i < $countSheets; ++$i) {
164
			foreach ($this->_writerWorksheets[$i]->_phpSheet->getCellCollection() as $cellID) {
165
				$cell = $this->_writerWorksheets[$i]->_phpSheet->getCell($cellID);
166
				$cVal = $cell->getValue();
167
				if ($cVal instanceof PHPExcel_RichText) {
168
					$elements = $cVal->getRichTextElements();
169
					foreach ($elements as $element) {
170
						if ($element instanceof PHPExcel_RichText_Run) {
171
							$font = $element->getFont();
172
							$this->_writerWorksheets[$i]->_fntHashIndex[$font->getHashCode()] = $this->_writerWorkbook->_addFont($font);
173
						}
174
					}
175
				}
176
			}
177
		}
178
 
179
		// initialize OLE file
180
		$workbookStreamName = 'Workbook';
181
		$OLE = new PHPExcel_Shared_OLE_PPS_File(PHPExcel_Shared_OLE::Asc2Ucs($workbookStreamName));
182
 
183
		// Write the worksheet streams before the global workbook stream,
184
		// because the byte sizes of these are needed in the global workbook stream
185
		$worksheetSizes = array();
186
		for ($i = 0; $i < $countSheets; ++$i) {
187
			$this->_writerWorksheets[$i]->close();
188
			$worksheetSizes[] = $this->_writerWorksheets[$i]->_datasize;
189
		}
190
 
191
		// add binary data for global workbook stream
192
		$OLE->append($this->_writerWorkbook->writeWorkbook($worksheetSizes));
193
 
194
		// add binary data for sheet streams
195
		for ($i = 0; $i < $countSheets; ++$i) {
196
			$OLE->append($this->_writerWorksheets[$i]->getData());
197
		}
198
 
199
		$this->_documentSummaryInformation = $this->_writeDocumentSummaryInformation();
200
		// initialize OLE Document Summary Information
201
		if(isset($this->_documentSummaryInformation) && !empty($this->_documentSummaryInformation)){
202
			$OLE_DocumentSummaryInformation = new PHPExcel_Shared_OLE_PPS_File(PHPExcel_Shared_OLE::Asc2Ucs(chr(5) . 'DocumentSummaryInformation'));
203
			$OLE_DocumentSummaryInformation->append($this->_documentSummaryInformation);
204
		}
205
 
206
		$this->_summaryInformation = $this->_writeSummaryInformation();
207
		// initialize OLE Summary Information
208
		if(isset($this->_summaryInformation) && !empty($this->_summaryInformation)){
209
		  $OLE_SummaryInformation = new PHPExcel_Shared_OLE_PPS_File(PHPExcel_Shared_OLE::Asc2Ucs(chr(5) . 'SummaryInformation'));
210
		  $OLE_SummaryInformation->append($this->_summaryInformation);
211
		}
212
 
213
		// define OLE Parts
214
		$arrRootData = array($OLE);
215
		// initialize OLE Properties file
216
		if(isset($OLE_SummaryInformation)){
217
			$arrRootData[] = $OLE_SummaryInformation;
218
		}
219
		// initialize OLE Extended Properties file
220
		if(isset($OLE_DocumentSummaryInformation)){
221
			$arrRootData[] = $OLE_DocumentSummaryInformation;
222
		}
223
 
224
		$root = new PHPExcel_Shared_OLE_PPS_Root(time(), time(), $arrRootData);
225
		// save the OLE file
226
		$res = $root->save($pFilename);
227
 
228
		PHPExcel_Calculation_Functions::setReturnDateType($saveDateReturnType);
229
		PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog($saveDebugLog);
230
	}
231
 
232
	/**
233
	 * Set temporary storage directory
234
	 *
235
	 * @deprecated
236
	 * @param	string	$pValue		Temporary storage directory
237
	 * @throws	PHPExcel_Writer_Exception	when directory does not exist
238
	 * @return PHPExcel_Writer_Excel5
239
	 */
240
	public function setTempDir($pValue = '') {
241
		return $this;
242
	}
243
 
244
	/**
245
	 * Build the Worksheet Escher objects
246
	 *
247
	 */
248
	private function _buildWorksheetEschers()
249
	{
250
		// 1-based index to BstoreContainer
251
		$blipIndex = 0;
252
		$lastReducedSpId = 0;
253
		$lastSpId = 0;
254
 
255
		foreach ($this->_phpExcel->getAllsheets() as $sheet) {
256
			// sheet index
257
			$sheetIndex = $sheet->getParent()->getIndex($sheet);
258
 
259
			$escher = null;
260
 
261
			// check if there are any shapes for this sheet
262
			$filterRange = $sheet->getAutoFilter()->getRange();
263
			if (count($sheet->getDrawingCollection()) == 0 && empty($filterRange)) {
264
				continue;
265
			}
266
 
267
			// create intermediate Escher object
268
			$escher = new PHPExcel_Shared_Escher();
269
 
270
			// dgContainer
271
			$dgContainer = new PHPExcel_Shared_Escher_DgContainer();
272
 
273
			// set the drawing index (we use sheet index + 1)
274
			$dgId = $sheet->getParent()->getIndex($sheet) + 1;
275
			$dgContainer->setDgId($dgId);
276
			$escher->setDgContainer($dgContainer);
277
 
278
			// spgrContainer
279
			$spgrContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer();
280
			$dgContainer->setSpgrContainer($spgrContainer);
281
 
282
			// add one shape which is the group shape
283
			$spContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer();
284
			$spContainer->setSpgr(true);
285
			$spContainer->setSpType(0);
286
			$spContainer->setSpId(($sheet->getParent()->getIndex($sheet) + 1) << 10);
287
			$spgrContainer->addChild($spContainer);
288
 
289
			// add the shapes
290
 
291
			$countShapes[$sheetIndex] = 0; // count number of shapes (minus group shape), in sheet
292
 
293
			foreach ($sheet->getDrawingCollection() as $drawing) {
294
				++$blipIndex;
295
 
296
				++$countShapes[$sheetIndex];
297
 
298
				// add the shape
299
				$spContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer();
300
 
301
				// set the shape type
302
				$spContainer->setSpType(0x004B);
303
				// set the shape flag
304
				$spContainer->setSpFlag(0x02);
305
 
306
				// set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index)
307
				$reducedSpId = $countShapes[$sheetIndex];
308
				$spId = $reducedSpId
309
					| ($sheet->getParent()->getIndex($sheet) + 1) << 10;
310
				$spContainer->setSpId($spId);
311
 
312
				// keep track of last reducedSpId
313
				$lastReducedSpId = $reducedSpId;
314
 
315
				// keep track of last spId
316
				$lastSpId = $spId;
317
 
318
				// set the BLIP index
319
				$spContainer->setOPT(0x4104, $blipIndex);
320
 
321
				// set coordinates and offsets, client anchor
322
				$coordinates = $drawing->getCoordinates();
323
				$offsetX = $drawing->getOffsetX();
324
				$offsetY = $drawing->getOffsetY();
325
				$width = $drawing->getWidth();
326
				$height = $drawing->getHeight();
327
 
328
				$twoAnchor = PHPExcel_Shared_Excel5::oneAnchor2twoAnchor($sheet, $coordinates, $offsetX, $offsetY, $width, $height);
329
 
330
				$spContainer->setStartCoordinates($twoAnchor['startCoordinates']);
331
				$spContainer->setStartOffsetX($twoAnchor['startOffsetX']);
332
				$spContainer->setStartOffsetY($twoAnchor['startOffsetY']);
333
				$spContainer->setEndCoordinates($twoAnchor['endCoordinates']);
334
				$spContainer->setEndOffsetX($twoAnchor['endOffsetX']);
335
				$spContainer->setEndOffsetY($twoAnchor['endOffsetY']);
336
 
337
				$spgrContainer->addChild($spContainer);
338
			}
339
 
340
			// AutoFilters
341
			if(!empty($filterRange)){
342
				$rangeBounds = PHPExcel_Cell::rangeBoundaries($filterRange);
343
				$iNumColStart = $rangeBounds[0][0];
344
				$iNumColEnd = $rangeBounds[1][0];
345
 
346
				$iInc = $iNumColStart;
347
				while($iInc <= $iNumColEnd){
348
					++$countShapes[$sheetIndex];
349
 
350
					// create an Drawing Object for the dropdown
351
					$oDrawing  = new PHPExcel_Worksheet_BaseDrawing();
352
					// get the coordinates of drawing
353
					$cDrawing   = PHPExcel_Cell::stringFromColumnIndex($iInc - 1) . $rangeBounds[0][1];
354
					$oDrawing->setCoordinates($cDrawing);
355
					$oDrawing->setWorksheet($sheet);
356
 
357
					// add the shape
358
					$spContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer();
359
					// set the shape type
360
					$spContainer->setSpType(0x00C9);
361
					// set the shape flag
362
					$spContainer->setSpFlag(0x01);
363
 
364
					// set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index)
365
					$reducedSpId = $countShapes[$sheetIndex];
366
					$spId = $reducedSpId
367
						| ($sheet->getParent()->getIndex($sheet) + 1) << 10;
368
					$spContainer->setSpId($spId);
369
 
370
					// keep track of last reducedSpId
371
					$lastReducedSpId = $reducedSpId;
372
 
373
					// keep track of last spId
374
					$lastSpId = $spId;
375
 
376
					$spContainer->setOPT(0x007F, 0x01040104); // Protection -> fLockAgainstGrouping
377
					$spContainer->setOPT(0x00BF, 0x00080008); // Text -> fFitTextToShape
378
					$spContainer->setOPT(0x01BF, 0x00010000); // Fill Style -> fNoFillHitTest
379
					$spContainer->setOPT(0x01FF, 0x00080000); // Line Style -> fNoLineDrawDash
380
					$spContainer->setOPT(0x03BF, 0x000A0000); // Group Shape -> fPrint
381
 
382
					// set coordinates and offsets, client anchor
383
					$endCoordinates = PHPExcel_Cell::stringFromColumnIndex(PHPExcel_Cell::stringFromColumnIndex($iInc - 1));
384
					$endCoordinates .= $rangeBounds[0][1] + 1;
385
 
386
					$spContainer->setStartCoordinates($cDrawing);
387
					$spContainer->setStartOffsetX(0);
388
					$spContainer->setStartOffsetY(0);
389
					$spContainer->setEndCoordinates($endCoordinates);
390
					$spContainer->setEndOffsetX(0);
391
					$spContainer->setEndOffsetY(0);
392
 
393
					$spgrContainer->addChild($spContainer);
394
					$iInc++;
395
				}
396
			}
397
 
398
			// identifier clusters, used for workbook Escher object
399
			$this->_IDCLs[$dgId] = $lastReducedSpId;
400
 
401
			// set last shape index
402
			$dgContainer->setLastSpId($lastSpId);
403
 
404
			// set the Escher object
405
			$this->_writerWorksheets[$sheetIndex]->setEscher($escher);
406
		}
407
	}
408
 
409
	/**
410
	 * Build the Escher object corresponding to the MSODRAWINGGROUP record
411
	 */
412
	private function _buildWorkbookEscher()
413
	{
414
		$escher = null;
415
 
416
		// any drawings in this workbook?
417
		$found = false;
418
		foreach ($this->_phpExcel->getAllSheets() as $sheet) {
419
			if (count($sheet->getDrawingCollection()) > 0) {
420
				$found = true;
421
				break;
422
			}
423
		}
424
 
425
		// nothing to do if there are no drawings
426
		if (!$found) {
427
			return;
428
		}
429
 
430
		// if we reach here, then there are drawings in the workbook
431
		$escher = new PHPExcel_Shared_Escher();
432
 
433
		// dggContainer
434
		$dggContainer = new PHPExcel_Shared_Escher_DggContainer();
435
		$escher->setDggContainer($dggContainer);
436
 
437
		// set IDCLs (identifier clusters)
438
		$dggContainer->setIDCLs($this->_IDCLs);
439
 
440
		// this loop is for determining maximum shape identifier of all drawing
441
		$spIdMax = 0;
442
		$totalCountShapes = 0;
443
		$countDrawings = 0;
444
 
445
		foreach ($this->_phpExcel->getAllsheets() as $sheet) {
446
			$sheetCountShapes = 0; // count number of shapes (minus group shape), in sheet
447
 
448
			if (count($sheet->getDrawingCollection()) > 0) {
449
				++$countDrawings;
450
 
451
				foreach ($sheet->getDrawingCollection() as $drawing) {
452
					++$sheetCountShapes;
453
					++$totalCountShapes;
454
 
455
					$spId = $sheetCountShapes
456
						| ($this->_phpExcel->getIndex($sheet) + 1) << 10;
457
					$spIdMax = max($spId, $spIdMax);
458
				}
459
			}
460
		}
461
 
462
		$dggContainer->setSpIdMax($spIdMax + 1);
463
		$dggContainer->setCDgSaved($countDrawings);
464
		$dggContainer->setCSpSaved($totalCountShapes + $countDrawings); // total number of shapes incl. one group shapes per drawing
465
 
466
		// bstoreContainer
467
		$bstoreContainer = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer();
468
		$dggContainer->setBstoreContainer($bstoreContainer);
469
 
470
		// the BSE's (all the images)
471
		foreach ($this->_phpExcel->getAllsheets() as $sheet) {
472
			foreach ($sheet->getDrawingCollection() as $drawing) {
473
				if ($drawing instanceof PHPExcel_Worksheet_Drawing) {
474
 
475
					$filename = $drawing->getPath();
476
 
477
					list($imagesx, $imagesy, $imageFormat) = getimagesize($filename);
478
 
479
					switch ($imageFormat) {
480
 
481
					case 1: // GIF, not supported by BIFF8, we convert to PNG
482
						$blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
483
						ob_start();
484
						imagepng(imagecreatefromgif($filename));
485
						$blipData = ob_get_contents();
486
						ob_end_clean();
487
						break;
488
 
489
					case 2: // JPEG
490
						$blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG;
491
						$blipData = file_get_contents($filename);
492
						break;
493
 
494
					case 3: // PNG
495
						$blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
496
						$blipData = file_get_contents($filename);
497
						break;
498
 
499
					case 6: // Windows DIB (BMP), we convert to PNG
500
						$blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
501
						ob_start();
502
						imagepng(PHPExcel_Shared_Drawing::imagecreatefrombmp($filename));
503
						$blipData = ob_get_contents();
504
						ob_end_clean();
505
						break;
506
 
507
					default: continue 2;
508
 
509
					}
510
 
511
					$blip = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip();
512
					$blip->setData($blipData);
513
 
514
					$BSE = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE();
515
					$BSE->setBlipType($blipType);
516
					$BSE->setBlip($blip);
517
 
518
					$bstoreContainer->addBSE($BSE);
519
 
520
				} else if ($drawing instanceof PHPExcel_Worksheet_MemoryDrawing) {
521
 
522
					switch ($drawing->getRenderingFunction()) {
523
 
524
					case PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG:
525
						$blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG;
526
						$renderingFunction = 'imagejpeg';
527
						break;
528
 
529
					case PHPExcel_Worksheet_MemoryDrawing::RENDERING_GIF:
530
					case PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG:
531
					case PHPExcel_Worksheet_MemoryDrawing::RENDERING_DEFAULT:
532
						$blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
533
						$renderingFunction = 'imagepng';
534
						break;
535
 
536
					}
537
 
538
					ob_start();
539
					call_user_func($renderingFunction, $drawing->getImageResource());
540
					$blipData = ob_get_contents();
541
					ob_end_clean();
542
 
543
					$blip = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip();
544
					$blip->setData($blipData);
545
 
546
					$BSE = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE();
547
					$BSE->setBlipType($blipType);
548
					$BSE->setBlip($blip);
549
 
550
					$bstoreContainer->addBSE($BSE);
551
				}
552
			}
553
		}
554
 
555
		// Set the Escher object
556
		$this->_writerWorkbook->setEscher($escher);
557
	}
558
 
559
	/**
560
	 * Build the OLE Part for DocumentSummary Information
561
	 * @return string
562
	 */
563
	private function _writeDocumentSummaryInformation(){
564
 
565
		// offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
566
		$data = pack('v', 0xFFFE);
567
		// offset: 2; size: 2;
568
		$data .= pack('v', 0x0000);
569
		// offset: 4; size: 2; OS version
570
		$data .= pack('v', 0x0106);
571
		// offset: 6; size: 2; OS indicator
572
		$data .= pack('v', 0x0002);
573
		// offset: 8; size: 16
574
		$data .= pack('VVVV', 0x00, 0x00, 0x00, 0x00);
575
		// offset: 24; size: 4; section count
576
		$data .= pack('V', 0x0001);
577
 
578
		// offset: 28; size: 16; first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae
579
		$data .= pack('vvvvvvvv', 0xD502, 0xD5CD, 0x2E9C, 0x101B, 0x9793, 0x0008, 0x2C2B, 0xAEF9);
580
		// offset: 44; size: 4; offset of the start
581
		$data .= pack('V', 0x30);
582
 
583
		// SECTION
584
		$dataSection = array();
585
		$dataSection_NumProps = 0;
586
		$dataSection_Summary = '';
587
		$dataSection_Content = '';
588
 
589
		// GKPIDDSI_CODEPAGE: CodePage
590
		$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x01),
591
							   'offset' => array('pack' => 'V'),
592
							   'type' 	=> array('pack' => 'V', 'data' => 0x02), // 2 byte signed integer
593
							   'data'	=> array('data' => 1252));
594
		$dataSection_NumProps++;
595
 
596
		// GKPIDDSI_CATEGORY : Category
597
		if($this->_phpExcel->getProperties()->getCategory()){
598
			$dataProp = $this->_phpExcel->getProperties()->getCategory();
599
			$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x02),
600
								   'offset' => array('pack' => 'V'),
601
								   'type' 	=> array('pack' => 'V', 'data' => 0x1E),
602
								   'data'	=> array('data' => $dataProp, 'length' => strlen($dataProp)));
603
			$dataSection_NumProps++;
604
		}
605
		// GKPIDDSI_VERSION :Version of the application that wrote the property storage
606
		$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x17),
607
							   'offset' => array('pack' => 'V'),
608
							   'type' 	=> array('pack' => 'V', 'data' => 0x03),
609
							   'data'	=> array('pack' => 'V', 'data' => 0x000C0000));
610
		$dataSection_NumProps++;
611
		// GKPIDDSI_SCALE : FALSE
612
		$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x0B),
613
							   'offset' => array('pack' => 'V'),
614
							   'type' 	=> array('pack' => 'V', 'data' => 0x0B),
615
							   'data'	=> array('data' => false));
616
		$dataSection_NumProps++;
617
		// GKPIDDSI_LINKSDIRTY : True if any of the values for the linked properties have changed outside of the application
618
		$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x10),
619
							   'offset' => array('pack' => 'V'),
620
							   'type' 	=> array('pack' => 'V', 'data' => 0x0B),
621
							   'data'	=> array('data' => false));
622
		$dataSection_NumProps++;
623
		// GKPIDDSI_SHAREDOC : FALSE
624
		$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x13),
625
							   'offset' => array('pack' => 'V'),
626
							   'type' 	=> array('pack' => 'V', 'data' => 0x0B),
627
							   'data'	=> array('data' => false));
628
		$dataSection_NumProps++;
629
		// GKPIDDSI_HYPERLINKSCHANGED : True if any of the values for the _PID_LINKS (hyperlink text) have changed outside of the application
630
		$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x16),
631
							   'offset' => array('pack' => 'V'),
632
							   'type' 	=> array('pack' => 'V', 'data' => 0x0B),
633
							   'data'	=> array('data' => false));
634
		$dataSection_NumProps++;
635
 
636
		// GKPIDDSI_DOCSPARTS
637
		// MS-OSHARED p75 (2.3.3.2.2.1)
638
		// Structure is VtVecUnalignedLpstrValue (2.3.3.1.9)
639
		// cElements
640
		$dataProp = pack('v', 0x0001);
641
		$dataProp .= pack('v', 0x0000);
642
		// array of UnalignedLpstr
643
		  // cch
644
		  $dataProp .= pack('v', 0x000A);
645
		  $dataProp .= pack('v', 0x0000);
646
		  // value
647
		  $dataProp .= 'Worksheet'.chr(0);
648
 
649
		$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x0D),
650
							   'offset' => array('pack' => 'V'),
651
							   'type' 	=> array('pack' => 'V', 'data' => 0x101E),
652
							   'data'	=> array('data' => $dataProp, 'length' => strlen($dataProp)));
653
		$dataSection_NumProps++;
654
 
655
		// GKPIDDSI_HEADINGPAIR
656
		// VtVecHeadingPairValue
657
		  // cElements
658
		  $dataProp = pack('v', 0x0002);
659
		  $dataProp .= pack('v', 0x0000);
660
		  // Array of vtHeadingPair
661
		    // vtUnalignedString - headingString
662
		      // stringType
663
		      $dataProp .= pack('v', 0x001E);
664
		      // padding
665
		      $dataProp .= pack('v', 0x0000);
666
		      // UnalignedLpstr
667
		        // cch
668
		        $dataProp .= pack('v', 0x0013);
669
		        $dataProp .= pack('v', 0x0000);
670
		        // value
671
		        $dataProp .= 'Feuilles de calcul';
672
		    // vtUnalignedString - headingParts
673
		      // wType : 0x0003 = 32 bit signed integer
674
		      $dataProp .= pack('v', 0x0300);
675
		      // padding
676
		      $dataProp .= pack('v', 0x0000);
677
		      // value
678
		      $dataProp .= pack('v', 0x0100);
679
		      $dataProp .= pack('v', 0x0000);
680
			  $dataProp .= pack('v', 0x0000);
681
		      $dataProp .= pack('v', 0x0000);
682
 
683
        $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x0C),
684
        					   'offset' => array('pack' => 'V'),
685
        					   'type' 	=> array('pack' => 'V', 'data' => 0x100C),
686
        					   'data'	=> array('data' => $dataProp, 'length' => strlen($dataProp)));
687
        $dataSection_NumProps++;
688
 
689
		// 		4 	Section Length
690
		//		4 	Property count
691
		//		8 * $dataSection_NumProps (8 =  ID (4) + OffSet(4))
692
		$dataSection_Content_Offset = 8 + $dataSection_NumProps * 8;
693
		foreach ($dataSection as $dataProp){
694
			// Summary
695
			$dataSection_Summary .= pack($dataProp['summary']['pack'], $dataProp['summary']['data']);
696
			// Offset
697
			$dataSection_Summary .= pack($dataProp['offset']['pack'], $dataSection_Content_Offset);
698
			// DataType
699
			$dataSection_Content .= pack($dataProp['type']['pack'], $dataProp['type']['data']);
700
			// Data
701
			if($dataProp['type']['data'] == 0x02){ // 2 byte signed integer
702
				$dataSection_Content .= pack('V', $dataProp['data']['data']);
703
 
704
				$dataSection_Content_Offset += 4 + 4;
705
			}
706
			elseif($dataProp['type']['data'] == 0x03){ // 4 byte signed integer
707
				$dataSection_Content .= pack('V', $dataProp['data']['data']);
708
 
709
				$dataSection_Content_Offset += 4 + 4;
710
			}
711
			elseif($dataProp['type']['data'] == 0x0B){ // Boolean
712
				if($dataProp['data']['data'] == false){
713
					$dataSection_Content .= pack('V', 0x0000);
714
				} else {
715
					$dataSection_Content .= pack('V', 0x0001);
716
				}
717
				$dataSection_Content_Offset += 4 + 4;
718
			}
719
			elseif($dataProp['type']['data'] == 0x1E){ // null-terminated string prepended by dword string length
720
				// Null-terminated string
721
				$dataProp['data']['data'] .= chr(0);
722
				$dataProp['data']['length'] += 1;
723
				// Complete the string with null string for being a %4
724
				$dataProp['data']['length'] = $dataProp['data']['length'] + ((4 - $dataProp['data']['length'] % 4)==4 ? 0 : (4 - $dataProp['data']['length'] % 4));
725
				$dataProp['data']['data'] = str_pad($dataProp['data']['data'], $dataProp['data']['length'], chr(0), STR_PAD_RIGHT);
726
 
727
				$dataSection_Content .= pack('V', $dataProp['data']['length']);
728
				$dataSection_Content .= $dataProp['data']['data'];
729
 
730
				$dataSection_Content_Offset += 4 + 4 + strlen($dataProp['data']['data']);
731
			}
732
			elseif($dataProp['type']['data'] == 0x40){ // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
733
				$dataSection_Content .= $dataProp['data']['data'];
734
 
735
				$dataSection_Content_Offset += 4 + 8;
736
			}
737
			else {
738
				// Data Type Not Used at the moment
739
				$dataSection_Content .= $dataProp['data']['data'];
740
 
741
				$dataSection_Content_Offset += 4 + $dataProp['data']['length'];
742
			}
743
		}
744
		// Now $dataSection_Content_Offset contains the size of the content
745
 
746
		// section header
747
		// offset: $secOffset; size: 4; section length
748
		// 		+ x  Size of the content (summary + content)
749
		$data .= pack('V', $dataSection_Content_Offset);
750
		// offset: $secOffset+4; size: 4; property count
751
		$data .= pack('V', $dataSection_NumProps);
752
		// Section Summary
753
		$data .= $dataSection_Summary;
754
		// Section Content
755
		$data .= $dataSection_Content;
756
 
757
		return $data;
758
	}
759
 
760
	/**
761
	 * Build the OLE Part for Summary Information
762
	 * @return string
763
	 */
764
	private function _writeSummaryInformation(){
765
		// offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
766
		$data = pack('v', 0xFFFE);
767
		// offset: 2; size: 2;
768
		$data .= pack('v', 0x0000);
769
		// offset: 4; size: 2; OS version
770
		$data .= pack('v', 0x0106);
771
		// offset: 6; size: 2; OS indicator
772
		$data .= pack('v', 0x0002);
773
		// offset: 8; size: 16
774
		$data .= pack('VVVV', 0x00, 0x00, 0x00, 0x00);
775
		// offset: 24; size: 4; section count
776
		$data .= pack('V', 0x0001);
777
 
778
		// offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
779
		$data .= pack('vvvvvvvv', 0x85E0, 0xF29F, 0x4FF9, 0x1068, 0x91AB, 0x0008, 0x272B, 0xD9B3);
780
		// offset: 44; size: 4; offset of the start
781
		$data .= pack('V', 0x30);
782
 
783
		// SECTION
784
		$dataSection = array();
785
		$dataSection_NumProps = 0;
786
		$dataSection_Summary = '';
787
		$dataSection_Content = '';
788
 
789
		// CodePage : CP-1252
790
		$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x01),
791
							   'offset' => array('pack' => 'V'),
792
							   'type' 	=> array('pack' => 'V', 'data' => 0x02), // 2 byte signed integer
793
							   'data'	=> array('data' => 1252));
794
		$dataSection_NumProps++;
795
 
796
		//	Title
797
		if($this->_phpExcel->getProperties()->getTitle()){
798
			$dataProp = $this->_phpExcel->getProperties()->getTitle();
799
			$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x02),
800
								   'offset' => array('pack' => 'V'),
801
								   'type' 	=> array('pack' => 'V', 'data' => 0x1E), // null-terminated string prepended by dword string length
802
								   'data'	=> array('data' => $dataProp, 'length' => strlen($dataProp)));
803
			$dataSection_NumProps++;
804
		}
805
		//	Subject
806
		if($this->_phpExcel->getProperties()->getSubject()){
807
			$dataProp = $this->_phpExcel->getProperties()->getSubject();
808
			$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x03),
809
								   'offset' => array('pack' => 'V'),
810
								   'type' 	=> array('pack' => 'V', 'data' => 0x1E), // null-terminated string prepended by dword string length
811
								   'data'	=> array('data' => $dataProp, 'length' => strlen($dataProp)));
812
			$dataSection_NumProps++;
813
		}
814
		//	Author (Creator)
815
		if($this->_phpExcel->getProperties()->getCreator()){
816
			$dataProp = $this->_phpExcel->getProperties()->getCreator();
817
			$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x04),
818
								   'offset' => array('pack' => 'V'),
819
								   'type' 	=> array('pack' => 'V', 'data' => 0x1E), // null-terminated string prepended by dword string length
820
								   'data'	=> array('data' => $dataProp, 'length' => strlen($dataProp)));
821
			$dataSection_NumProps++;
822
		}
823
		//	Keywords
824
		if($this->_phpExcel->getProperties()->getKeywords()){
825
			$dataProp = $this->_phpExcel->getProperties()->getKeywords();
826
			$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x05),
827
								   'offset' => array('pack' => 'V'),
828
								   'type' 	=> array('pack' => 'V', 'data' => 0x1E), // null-terminated string prepended by dword string length
829
								   'data'	=> array('data' => $dataProp, 'length' => strlen($dataProp)));
830
			$dataSection_NumProps++;
831
		}
832
		//	Comments (Description)
833
		if($this->_phpExcel->getProperties()->getDescription()){
834
			$dataProp = $this->_phpExcel->getProperties()->getDescription();
835
			$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x06),
836
								   'offset' => array('pack' => 'V'),
837
								   'type' 	=> array('pack' => 'V', 'data' => 0x1E), // null-terminated string prepended by dword string length
838
								   'data'	=> array('data' => $dataProp, 'length' => strlen($dataProp)));
839
			$dataSection_NumProps++;
840
		}
841
		//	Last Saved By (LastModifiedBy)
842
		if($this->_phpExcel->getProperties()->getLastModifiedBy()){
843
			$dataProp = $this->_phpExcel->getProperties()->getLastModifiedBy();
844
			$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x08),
845
								   'offset' => array('pack' => 'V'),
846
								   'type' 	=> array('pack' => 'V', 'data' => 0x1E), // null-terminated string prepended by dword string length
847
								   'data'	=> array('data' => $dataProp, 'length' => strlen($dataProp)));
848
			$dataSection_NumProps++;
849
		}
850
		//	Created Date/Time
851
		if($this->_phpExcel->getProperties()->getCreated()){
852
			$dataProp = $this->_phpExcel->getProperties()->getCreated();
853
			$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x0C),
854
								   'offset' => array('pack' => 'V'),
855
								   'type' 	=> array('pack' => 'V', 'data' => 0x40), // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
856
								   'data'	=> array('data' => PHPExcel_Shared_OLE::LocalDate2OLE($dataProp)));
857
			$dataSection_NumProps++;
858
		}
859
		//	Modified Date/Time
860
		if($this->_phpExcel->getProperties()->getModified()){
861
			$dataProp = $this->_phpExcel->getProperties()->getModified();
862
			$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x0D),
863
								   'offset' => array('pack' => 'V'),
864
								   'type' 	=> array('pack' => 'V', 'data' => 0x40), // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
865
								   'data'	=> array('data' => PHPExcel_Shared_OLE::LocalDate2OLE($dataProp)));
866
			$dataSection_NumProps++;
867
		}
868
		//	Security
869
		$dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x13),
870
							   'offset' => array('pack' => 'V'),
871
							   'type' 	=> array('pack' => 'V', 'data' => 0x03), // 4 byte signed integer
872
							   'data'	=> array('data' => 0x00));
873
		$dataSection_NumProps++;
874
 
875
 
876
		// 		4 	Section Length
877
		//		4 	Property count
878
		//		8 * $dataSection_NumProps (8 =  ID (4) + OffSet(4))
879
		$dataSection_Content_Offset = 8 + $dataSection_NumProps * 8;
880
		foreach ($dataSection as $dataProp){
881
			// Summary
882
			$dataSection_Summary .= pack($dataProp['summary']['pack'], $dataProp['summary']['data']);
883
			// Offset
884
			$dataSection_Summary .= pack($dataProp['offset']['pack'], $dataSection_Content_Offset);
885
			// DataType
886
			$dataSection_Content .= pack($dataProp['type']['pack'], $dataProp['type']['data']);
887
			// Data
888
			if($dataProp['type']['data'] == 0x02){ // 2 byte signed integer
889
				$dataSection_Content .= pack('V', $dataProp['data']['data']);
890
 
891
				$dataSection_Content_Offset += 4 + 4;
892
			}
893
			elseif($dataProp['type']['data'] == 0x03){ // 4 byte signed integer
894
				$dataSection_Content .= pack('V', $dataProp['data']['data']);
895
 
896
				$dataSection_Content_Offset += 4 + 4;
897
			}
898
			elseif($dataProp['type']['data'] == 0x1E){ // null-terminated string prepended by dword string length
899
				// Null-terminated string
900
				$dataProp['data']['data'] .= chr(0);
901
				$dataProp['data']['length'] += 1;
902
				// Complete the string with null string for being a %4
903
				$dataProp['data']['length'] = $dataProp['data']['length'] + ((4 - $dataProp['data']['length'] % 4)==4 ? 0 : (4 - $dataProp['data']['length'] % 4));
904
				$dataProp['data']['data'] = str_pad($dataProp['data']['data'], $dataProp['data']['length'], chr(0), STR_PAD_RIGHT);
905
 
906
				$dataSection_Content .= pack('V', $dataProp['data']['length']);
907
				$dataSection_Content .= $dataProp['data']['data'];
908
 
909
				$dataSection_Content_Offset += 4 + 4 + strlen($dataProp['data']['data']);
910
			}
911
			elseif($dataProp['type']['data'] == 0x40){ // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
912
				$dataSection_Content .= $dataProp['data']['data'];
913
 
914
				$dataSection_Content_Offset += 4 + 8;
915
			}
916
			else {
917
				// Data Type Not Used at the moment
918
			}
919
		}
920
		// Now $dataSection_Content_Offset contains the size of the content
921
 
922
		// section header
923
		// offset: $secOffset; size: 4; section length
924
		// 		+ x  Size of the content (summary + content)
925
		$data .= pack('V', $dataSection_Content_Offset);
926
		// offset: $secOffset+4; size: 4; property count
927
		$data .= pack('V', $dataSection_NumProps);
928
		// Section Summary
929
		$data .= $dataSection_Summary;
930
		// Section Content
931
		$data .= $dataSection_Content;
932
 
933
		return $data;
934
	}
935
}