PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.0.3
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.0.3
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
visualizer / vendor / phpoffice / phpexcel / Classes / PHPExcel / Writer / Excel5.php

Excel5.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.0.3, at vendor/phpoffice/phpexcel/Classes/PHPExcel/Writer/Excel5.php

936 lines 33.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * PHPExcel
4 *
5 * Copyright (c) 2006 - 2014 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 - 2014 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 - 2014 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 }
936