File Source for Workbook.php

PHPExcel_Writer_Excel5

# Source for file Workbook.php

Documentation is available at Workbook.php

1. <?php
2. /**
3. * PHPExcel
4. *
5. * Copyright (c) 2006 - 2011 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 - 2011 PHPExcel (http://www.codeplex.com/PHPExcel)
24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
25. * @version 1.7.6, 2011-02-27
26. */
28. // Original file header of PEAR::Spreadsheet_Excel_Writer_Workbook (used as the base for this class):
29. // -----------------------------------------------------------------------------------------
30. // /*
31. // * Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
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. // * <jmcnamara@cpan.org>
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 xnoguer@rezebra.com
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. // */
64. /**
65. * PHPExcel_Writer_Excel5_Workbook
66. *
67. * @category PHPExcel
68. * @package PHPExcel_Writer_Excel5
69. * @copyright Copyright (c) 2006 - 2011 PHPExcel (http://www.codeplex.com/PHPExcel)
70. */
71. class PHPExcel_Writer_Excel5_Workbook extends PHPExcel_Writer_Excel5_BIFFwriter
72. {
73. /**
74. * Formula parser
75. *
76. * @var PHPExcel_Writer_Excel5_Parser
77. */
78. private $_parser;
80. /**
81. * The BIFF file size for the workbook.
82. * @var integer
83. * @see _calcSheetOffsets()
84. */
85. public $_biffsize;
87. /**
88. * XF Writers
89. * @var PHPExcel_Writer_Excel5_Xf[]
90. */
91. private $_xfWriters = array();
93. /**
94. * Array containing the colour palette
95. * @var array
96. */
97. public $_palette;
99. /**
100. * The codepage indicates the text encoding used for strings
101. * @var integer
102. */
103. public $_codepage;
105. /**
106. * The country code used for localization
107. * @var integer
108. */
109. public $_country_code;
111. /**
112. * Workbook
113. * @var PHPExcel
114. */
115. private $_phpExcel;
117. /**
118. * Fonts writers
119. *
120. * @var PHPExcel_Writer_Excel5_Font[]
121. */
122. private $_fontWriters = array();
124. /**
125. * Added fonts. Maps from font's hash => index in workbook
126. *
127. * @var array
128. */
129. private $_addedFonts = array();
131. /**
132. * Shared number formats
133. *
134. * @var array
135. */
136. private $_numberFormats = array();
138. /**
139. * Added number formats. Maps from numberFormat's hash => index in workbook
140. *
141. * @var array
142. */
143. private $_addedNumberFormats = array();
145. /**
146. * Sizes of the binary worksheet streams
147. *
148. * @var array
149. */
150. private $_worksheetSizes = array();
152. /**
153. * Offsets of the binary worksheet streams relative to the start of the global workbook stream
154. *
155. * @var array
156. */
157. private $_worksheetOffsets = array();
159. /**
160. * Total number of shared strings in workbook
161. *
162. * @var int
163. */
164. private $_str_total;
166. /**
167. * Number of unique shared strings in workbook
168. *
169. * @var int
170. */
171. private $_str_unique;
173. /**
174. * Array of unique shared strings in workbook
175. *
176. * @var array
177. */
178. private $_str_table;
180. /**
181. * Color cache
182. */
183. private $_colors;
185. /**
186. * Escher object corresponding to MSODRAWINGGROUP
187. *
188. * @var PHPExcel_Shared_Escher
189. */
190. private $_escher;
193. /**
194. * Class constructor
195. *
196. * @param PHPExcel $phpExcel The Workbook
197. * @param int $BIFF_verions BIFF version
198. * @param int $str_total Total number of strings
199. * @param int $str_unique Total number of unique strings
200. * @param array $str_table
201. * @param mixed $parser The formula parser created for the Workbook
202. */
203. public function __construct(PHPExcel $phpExcel = null, $BIFF_version = 0x0600,
204. &$str_total,
205. &$str_unique, &$str_table, &$colors, $parser
206. )
207. {
208. // It needs to call its parent's constructor explicitly
209. parent::__construct();
211. $this->_parser = $parser;
212. $this->_biffsize = 0;
213. $this->_palette = array();
214. $this->_codepage = 0x04E4; // FIXME: should change for BIFF8
215. $this->_country_code = -1;
217. $this->_str_total = &$str_total;
218. $this->_str_unique = &$str_unique;
219. $this->_str_table = &$str_table;
220. $this->_colors = &$colors;
221. $this->_setPaletteXl97();
223. $this->_phpExcel = $phpExcel;
225. if ($BIFF_version == 0x0600) {
226. $this->_BIFF_version = 0x0600;
227. // change BIFFwriter limit for CONTINUE records
228. $this->_limit = 8224;
229. $this->_codepage = 0x04B0;
230. }
232. // Add empty sheets and Build color cache
233. $countSheets = $phpExcel->getSheetCount();
234. for ($i = 0; $i < $countSheets; ++$i) {
235. $phpSheet = $phpExcel->getSheet($i);
237. $this->_parser-> setExtSheet($phpSheet->getTitle(), $i); // Register worksheet name with parser
239. // for BIFF8
240. if ($this->_BIFF_version == 0x0600) {
241. $supbook_index = 0x00;
242. $ref = pack('vvv', $supbook_index, $i, $i);
243. $this->_parser->_references[] = $ref; // Register reference with parser
244. }
245. // Sheet tab colors?
246. if ($phpSheet->isTabColorSet()) {
247. $this->_addColor($phpSheet->getTabColor()->getRGB());
248. }
249. }
251. }
253. /**
254. * Add a new XF writer
255. *
256. * @param PHPExcel_Style
257. * @param boolean Is it a style XF?
258. * @return int Index to XF record
259. */
260. public function addXfWriter($style, $isStyleXf = false)
261. {
262. $xfWriter = new PHPExcel_Writer_Excel5_Xf($style);
263. $xfWriter->setBIFFVersion($this->_BIFF_version);
264. $xfWriter->setIsStyleXf($isStyleXf);
266. // Add the font if not already added
267. $fontHashCode = $style->getFont()->getHashCode();
269. if (isset($this->_addedFonts[$fontHashCode])) {
270. $fontIndex = $this->_addedFonts[$fontHashCode];
271. } else {
272. $countFonts = count($this->_fontWriters);
273. $fontIndex = ($countFonts < 4) ? $countFonts : $countFonts + 1;
275. $fontWriter = new PHPExcel_Writer_Excel5_Font($style->getFont());
276. $fontWriter->setBIFFVersion($this->_BIFF_version);
277. $fontWriter->setColorIndex($this->_addColor($style->getFont()->getColor()->getRGB()));
278. $this->_fontWriters[] = $fontWriter;
280. $this->_addedFonts[$fontHashCode] = $fontIndex;
281. }
283. // Assign the font index to the xf record
284. $xfWriter->setFontIndex($fontIndex);
286. // Background colors, best to treat these after the font so black will come after white in custom palette
287. $xfWriter->setFgColor($this->_addColor($style->getFill()->getStartColor()->getRGB()));
288. $xfWriter->setBgColor($this->_addColor($style->getFill()->getEndColor()->getRGB()));
289. $xfWriter->setBottomColor($this->_addColor($style->getBorders()->getBottom()->getColor()->getRGB()));
290. $xfWriter->setTopColor($this->_addColor($style->getBorders()->getTop()->getColor()->getRGB()));
291. $xfWriter->setRightColor($this->_addColor($style->getBorders()->getRight()->getColor()->getRGB()));
292. $xfWriter->setLeftColor($this->_addColor($style->getBorders()->getLeft()->getColor()->getRGB()));
293. $xfWriter->setDiagColor($this->_addColor($style->getBorders()->getDiagonal()->getColor()->getRGB()));
295. // Add the number format if it is not a built-in one and not already added
296. if ($style->getNumberFormat()->getBuiltInFormatCode() === false) {
297. $numberFormatHashCode = $style->getNumberFormat()->getHashCode();
299. if (isset($this->_addedNumberFormats[$numberFormatHashCode])) {
300. $numberFormatIndex = $this->_addedNumberFormats[$numberFormatHashCode];
301. } else {
302. $numberFormatIndex = 164 + count($this->_numberFormats);
303. $this->_numberFormats[$numberFormatIndex] = $style->getNumberFormat();
304. $this->_addedNumberFormats[$numberFormatHashCode] = $numberFormatIndex;
305. }
306. }
307. else {
308. $numberFormatIndex = (int) $style->getNumberFormat()->getBuiltInFormatCode();
309. }
311. // Assign the number format index to xf record
312. $xfWriter->setNumberFormatIndex($numberFormatIndex);
314. $this->_xfWriters[] = $xfWriter;
316. $xfIndex = count($this->_xfWriters) - 1;
317. return $xfIndex;
318. }
320. /**
321. * Alter color palette adding a custom color
322. *
323. * @param string $rgb E.g. 'FF00AA'
324. * @return int Color index
325. */
326. private function _addColor($rgb) {
327. if (!isset($this->_colors[$rgb])) {
328. if ( count($this->_colors) < 57) {
329. // then we add a custom color altering the palette
330. $colorIndex = 8 + count($this->_colors);
331. $this->_palette[$colorIndex] =
332. array(
333. hexdec( substr($rgb, 0, 2)),
334. hexdec( substr($rgb, 2, 2)),
335. hexdec( substr($rgb, 4)),
336. 0
337. );
338. $this->_colors[$rgb] = $colorIndex;
339. } else {
340. // no room for more custom colors, just map to black
341. $colorIndex = 0;
342. }
343. } else {
344. // fetch already added custom color
345. $colorIndex = $this->_colors[$rgb];
346. }
348. return $colorIndex;
349. }
351. /**
352. * Sets the colour palette to the Excel 97+ default.
353. *
354. * @access private
355. */
356. function _setPaletteXl97()
357. {
358. $this->_palette = array(
359. 0x08 => array(0x00, 0x00, 0x00, 0x00),
360. 0x09 => array(0xff, 0xff, 0xff, 0x00),
361. 0x0A => array(0xff, 0x00, 0x00, 0x00),
362. 0x0B => array(0x00, 0xff, 0x00, 0x00),
363. 0x0C => array(0x00, 0x00, 0xff, 0x00),
364. 0x0D => array(0xff, 0xff, 0x00, 0x00),
365. 0x0E => array(0xff, 0x00, 0xff, 0x00),
366. 0x0F => array(0x00, 0xff, 0xff, 0x00),
367. 0x10 => array(0x80, 0x00, 0x00, 0x00),
368. 0x11 => array(0x00, 0x80, 0x00, 0x00),
369. 0x12 => array(0x00, 0x00, 0x80, 0x00),
370. 0x13 => array(0x80, 0x80, 0x00, 0x00),
371. 0x14 => array(0x80, 0x00, 0x80, 0x00),
372. 0x15 => array(0x00, 0x80, 0x80, 0x00),
373. 0x16 => array(0xc0, 0xc0, 0xc0, 0x00),
374. 0x17 => array(0x80, 0x80, 0x80, 0x00),
375. 0x18 => array(0x99, 0x99, 0xff, 0x00),
376. 0x19 => array(0x99, 0x33, 0x66, 0x00),
377. 0x1A => array(0xff, 0xff, 0xcc, 0x00),
378. 0x1B => array(0xcc, 0xff, 0xff, 0x00),
379. 0x1C => array(0x66, 0x00, 0x66, 0x00),
380. 0x1D => array(0xff, 0x80, 0x80, 0x00),
381. 0x1E => array(0x00, 0x66, 0xcc, 0x00),
382. 0x1F => array(0xcc, 0xcc, 0xff, 0x00),
383. 0x20 => array(0x00, 0x00, 0x80, 0x00),
384. 0x21 => array(0xff, 0x00, 0xff, 0x00),
385. 0x22 => array(0xff, 0xff, 0x00, 0x00),
386. 0x23 => array(0x00, 0xff, 0xff, 0x00),
387. 0x24 => array(0x80, 0x00, 0x80, 0x00),
388. 0x25 => array(0x80, 0x00, 0x00, 0x00),
389. 0x26 => array(0x00, 0x80, 0x80, 0x00),
390. 0x27 => array(0x00, 0x00, 0xff, 0x00),
391. 0x28 => array(0x00, 0xcc, 0xff, 0x00),
392. 0x29 => array(0xcc, 0xff, 0xff, 0x00),
393. 0x2A => array(0xcc, 0xff, 0xcc, 0x00),
394. 0x2B => array(0xff, 0xff, 0x99, 0x00),
395. 0x2C => array(0x99, 0xcc, 0xff, 0x00),
396. 0x2D => array(0xff, 0x99, 0xcc, 0x00),
397. 0x2E => array(0xcc, 0x99, 0xff, 0x00),
398. 0x2F => array(0xff, 0xcc, 0x99, 0x00),
399. 0x30 => array(0x33, 0x66, 0xff, 0x00),
400. 0x31 => array(0x33, 0xcc, 0xcc, 0x00),
401. 0x32 => array(0x99, 0xcc, 0x00, 0x00),
402. 0x33 => array(0xff, 0xcc, 0x00, 0x00),
403. 0x34 => array(0xff, 0x99, 0x00, 0x00),
404. 0x35 => array(0xff, 0x66, 0x00, 0x00),
405. 0x36 => array(0x66, 0x66, 0x99, 0x00),
406. 0x37 => array(0x96, 0x96, 0x96, 0x00),
407. 0x38 => array(0x00, 0x33, 0x66, 0x00),
408. 0x39 => array(0x33, 0x99, 0x66, 0x00),
409. 0x3A => array(0x00, 0x33, 0x00, 0x00),
410. 0x3B => array(0x33, 0x33, 0x00, 0x00),
411. 0x3C => array(0x99, 0x33, 0x00, 0x00),
412. 0x3D => array(0x99, 0x33, 0x66, 0x00),
413. 0x3E => array(0x33, 0x33, 0x99, 0x00),
414. 0x3F => array(0x33, 0x33, 0x33, 0x00),
415. );
416. }
418. /**
419. * Assemble worksheets into a workbook and send the BIFF data to an OLE
420. * storage.
421. *
422. * @param array $worksheetSizes The sizes in bytes of the binary worksheet streams
423. * @return string Binary data for workbook stream
424. */
425. public function writeWorkbook($pWorksheetSizes = null)
426. {
427. $this->_worksheetSizes = $pWorksheetSizes;
429. // Calculate the number of selected worksheet tabs and call the finalization
430. // methods for each worksheet
431. $total_worksheets = $this->_phpExcel->getSheetCount();
433. // Add part 1 of the Workbook globals, what goes before the SHEET records
434. $this->_storeBof(0x0005);
435. $this->_writeCodepage();
436. if ($this->_BIFF_version == 0x0600) {
437. $this->_writeWindow1();
438. }
439. if ($this->_BIFF_version == 0x0500) {
440. $this->_writeExterns(); // For print area and repeat rows
441. $this->_writeNames(); // For print area and repeat rows
442. }
443. if ($this->_BIFF_version == 0x0500) {
444. $this->_writeWindow1();
445. }
446. $this->_writeDatemode();
447. $this->_writeAllFonts();
448. $this->_writeAllNumFormats();
449. $this->_writeAllXfs();
450. $this->_writeAllStyles();
451. $this->_writePalette();
453. // Prepare part 3 of the workbook global stream, what goes after the SHEET records
454. $part3 = '';
455. if ($this->_country_code != -1) {
456. $part3 .= $this->_writeCountry();
457. }
458. $part3 .= $this->_writeRecalcId();
460. if ($this->_BIFF_version == 0x0600) {
461. $part3 .= $this->_writeSupbookInternal();
462. /* TODO: store external SUPBOOK records and XCT and CRN records
463. in case of external references for BIFF8 */
464. $part3 .= $this->_writeExternsheetBiff8();
465. $part3 .= $this->_writeAllDefinedNamesBiff8();
466. $part3 .= $this->_writeMsoDrawingGroup();
467. $part3 .= $this->_writeSharedStringsTable();
468. }
470. $part3 .= $this-> writeEof();
472. // Add part 2 of the Workbook globals, the SHEET records
473. $this->_calcSheetOffsets();
474. for ($i = 0; $i < $total_worksheets; ++$i) {
475. $this->_writeBoundsheet($this->_phpExcel->getSheet($i), $this->_worksheetOffsets[$i]);
476. }
478. // Add part 3 of the Workbook globals
479. $this->_data .= $part3;
481. return $this->_data;
482. }
484. /**
485. * Calculate offsets for Worksheet BOF records.
486. *
487. * @access private
488. */
489. function _calcSheetOffsets()
490. {
491. if ($this->_BIFF_version == 0x0600) {
492. $boundsheet_length = 10; // fixed length for a BOUNDSHEET record
493. } else {
494. $boundsheet_length = 11;
495. }
497. // size of Workbook globals part 1 + 3
498. $offset = $this->_datasize;
500. // add size of Workbook globals part 2, the length of the SHEET records
501. $total_worksheets = count($this->_phpExcel->getAllSheets());
502. foreach ($this->_phpExcel->getWorksheetIterator() as $sheet) {
503. if ($this->_BIFF_version == 0x0600) {
504. $offset += $boundsheet_length + strlen( PHPExcel_Shared_String:: UTF8toBIFF8UnicodeShort($sheet->getTitle()));
505. } else {
506. $offset += $boundsheet_length + strlen($sheet->getTitle());
507. }
508. }
510. // add the sizes of each of the Sheet substreams, respectively
511. for ($i = 0; $i < $total_worksheets; ++$i) {
512. $this->_worksheetOffsets[$i] = $offset;
513. $offset += $this->_worksheetSizes[$i];
514. }
515. $this->_biffsize = $offset;
516. }
518. /**
519. * Store the Excel FONT records.
520. */
521. private function _writeAllFonts()
522. {
523. foreach ($this->_fontWriters as $fontWriter) {
524. $this->_append($fontWriter->writeFont());
525. }
526. }
528. /**
529. * Store user defined numerical formats i.e. FORMAT records
530. */
531. private function _writeAllNumFormats()
532. {
533. foreach ($this->_numberFormats as $numberFormatIndex => $numberFormat) {
534. $this->_writeNumFormat($numberFormat->getFormatCode(), $numberFormatIndex);
535. }
536. }
538. /**
539. * Write all XF records.
540. */
541. private function _writeAllXfs()
542. {
543. foreach ($this->_xfWriters as $xfWriter) {
544. $this->_append($xfWriter->writeXf());
545. }
546. }
548. /**
549. * Write all STYLE records.
550. */
551. private function _writeAllStyles()
552. {
553. $this->_writeStyle();
554. }
556. /**
557. * Write the EXTERNCOUNT and EXTERNSHEET records. These are used as indexes for
558. * the NAME records.
559. */
560. private function _writeExterns()
561. {
562. $countSheets = $this->_phpExcel->getSheetCount();
563. // Create EXTERNCOUNT with number of worksheets
564. $this->_writeExterncount($countSheets);
566. // Create EXTERNSHEET for each worksheet
567. for ($i = 0; $i < $countSheets; ++$i) {
568. $this->_writeExternsheet($phpExcel->getSheet($i)->getTitle());
569. }
570. }
572. /**
573. * Write the NAME record to define the print area and the repeat rows and cols.
574. */
575. private function _writeNames()
576. {
577. // total number of sheets
578. $total_worksheets = $this->_phpExcel->getSheetCount();
580. // Create the print area NAME records
581. for ($i = 0; $i < $total_worksheets; ++$i) {
582. $sheetSetup = $this->_phpExcel->getSheet($i)->getPageSetup();
583. // Write a Name record if the print area has been defined
584. if ($sheetSetup->isPrintAreaSet()) {
585. // Print area
586. $printArea = PHPExcel_Cell:: splitRange($sheetSetup->getPrintArea());
587. $printArea = $printArea[0];
588. $printArea[0] = PHPExcel_Cell:: coordinateFromString($printArea[0]);
589. $printArea[1] = PHPExcel_Cell:: coordinateFromString($printArea[1]);
591. $print_rowmin = $printArea[0][1] - 1;
592. $print_rowmax = $printArea[1][1] - 1;
593. $print_colmin = PHPExcel_Cell:: columnIndexFromString($printArea[0][0]) - 1;
594. $print_colmax = PHPExcel_Cell:: columnIndexFromString($printArea[1][0]) - 1;
596. $this->_writeNameShort(
597. $i, // sheet index
598. 0x06, // NAME type
599. $print_rowmin,
600. $print_rowmax,
601. $print_colmin,
602. $print_colmax
603. );
604. }
605. }
607. // Create the print title NAME records
608. for ($i = 0; $i < $total_worksheets; ++$i) {
609. $sheetSetup = $this->_phpExcel->getSheet($i)->getPageSetup();
611. // simultaneous repeatColumns repeatRows
612. if ($sheetSetup->isColumnsToRepeatAtLeftSet() && $sheetSetup->isRowsToRepeatAtTopSet()) {
613. $repeat = $sheetSetup->getColumnsToRepeatAtLeft();
614. $colmin = PHPExcel_Cell:: columnIndexFromString($repeat[0]) - 1;
615. $colmax = PHPExcel_Cell:: columnIndexFromString($repeat[1]) - 1;
617. $repeat = $sheetSetup->getRowsToRepeatAtTop();
618. $rowmin = $repeat[0] - 1;
619. $rowmax = $repeat[1] - 1;
621. $this->_writeNameLong(
622. $i, // sheet index
623. 0x07, // NAME type
624. $rowmin,
625. $rowmax,
626. $colmin,
627. $colmax
628. );
630. // (exclusive) either repeatColumns or repeatRows
631. } else if ($sheetSetup->isColumnsToRepeatAtLeftSet() || $sheetSetup->isRowsToRepeatAtTopSet()) {
633. // Columns to repeat
634. if ($sheetSetup->isColumnsToRepeatAtLeftSet()) {
635. $repeat = $sheetSetup->getColumnsToRepeatAtLeft();
636. $colmin = PHPExcel_Cell:: columnIndexFromString($repeat[0]) - 1;
637. $colmax = PHPExcel_Cell:: columnIndexFromString($repeat[1]) - 1;
638. } else {
639. $colmin = 0;
640. $colmax = 255;
641. }
643. // Rows to repeat
644. if ($sheetSetup->isRowsToRepeatAtTopSet()) {
645. $repeat = $sheetSetup->getRowsToRepeatAtTop();
646. $rowmin = $repeat[0] - 1;
647. $rowmax = $repeat[1] - 1;
648. } else {
649. $rowmin = 0;
650. $rowmax = 16383;
651. }
653. $this->_writeNameShort(
654. $i, // sheet index
655. 0x07, // NAME type
656. $rowmin,
657. $rowmax,
658. $colmin,
659. $colmax
660. );
661. }
662. }
663. }
666. /**
667. * Writes all the DEFINEDNAME records (BIFF8).
668. * So far this is only used for repeating rows/columns (print titles) and print areas
669. */
670. private function _writeAllDefinedNamesBiff8()
671. {
672. $chunk = '';
674. // Named ranges
675. if ( count($this->_phpExcel->getNamedRanges()) > 0) {
676. // Loop named ranges
677. $namedRanges = $this->_phpExcel->getNamedRanges();
678. foreach ($namedRanges as $namedRange) {
680. // Create absolute coordinate
681. $range = PHPExcel_Cell:: splitRange($namedRange->getRange());
682. for ($i = 0; $i < count($range); $i++) {
683. $range[$i][0] = '\'' . str_replace("'", "''", $namedRange->getWorksheet()->getTitle()) . '\'!' . PHPExcel_Cell:: absoluteCoordinate($range[$i][0]);
684. if (isset($range[$i][1])) {
685. $range[$i][1] = PHPExcel_Cell:: absoluteCoordinate($range[$i][1]);
686. }
687. }
688. $range = PHPExcel_Cell:: buildRange($range); // e.g. Sheet1!$A$1:$B$2
690. // parse formula
691. try {
692. $error = $this->_parser-> parse($range);
693. $formulaData = $this->_parser-> toReversePolish();
695. // make sure tRef3d is of type tRef3dR (0x3A)
696. if (isset($formulaData{0}) and ($formulaData{0} == "\x7A" or $formulaData{0} == "\x5A")) {
697. $formulaData = "\x3A" . substr($formulaData, 1);
698. }
700. if ($namedRange->getLocalOnly()) {
701. // local scope
702. $scope = $this->_phpExcel->getIndex($namedRange->getScope()) + 1;
703. } else {
704. // global scope
705. $scope = 0;
706. }
707. $chunk .= $this-> writeData($this->_writeDefinedNameBiff8($namedRange->getName(), $formulaData, $scope, false));
709. } catch(Exception $e) {
710. // do nothing
711. }
712. }
713. }
715. // total number of sheets
716. $total_worksheets = $this->_phpExcel->getSheetCount();
718. // write the print titles (repeating rows, columns), if any
719. for ($i = 0; $i < $total_worksheets; ++$i) {
720. $sheetSetup = $this->_phpExcel->getSheet($i)->getPageSetup();
721. // simultaneous repeatColumns repeatRows
722. if ($sheetSetup->isColumnsToRepeatAtLeftSet() && $sheetSetup->isRowsToRepeatAtTopSet()) {
723. $repeat = $sheetSetup->getColumnsToRepeatAtLeft();
724. $colmin = PHPExcel_Cell:: columnIndexFromString($repeat[0]) - 1;
725. $colmax = PHPExcel_Cell:: columnIndexFromString($repeat[1]) - 1;
727. $repeat = $sheetSetup->getRowsToRepeatAtTop();
728. $rowmin = $repeat[0] - 1;
729. $rowmax = $repeat[1] - 1;
731. // construct formula data manually
732. $formulaData = pack('Cv', 0x29, 0x17); // tMemFunc
733. $formulaData .= pack('Cvvvvv', 0x3B, $i, 0, 65535, $colmin, $colmax); // tArea3d
734. $formulaData .= pack('Cvvvvv', 0x3B, $i, $rowmin, $rowmax, 0, 255); // tArea3d
735. $formulaData .= pack('C', 0x10); // tList
737. // store the DEFINEDNAME record
738. $chunk .= $this-> writeData($this->_writeDefinedNameBiff8( pack('C', 0x07), $formulaData, $i + 1, true));
740. // (exclusive) either repeatColumns or repeatRows
741. } else if ($sheetSetup->isColumnsToRepeatAtLeftSet() || $sheetSetup->isRowsToRepeatAtTopSet()) {
743. // Columns to repeat
744. if ($sheetSetup->isColumnsToRepeatAtLeftSet()) {
745. $repeat = $sheetSetup->getColumnsToRepeatAtLeft();
746. $colmin = PHPExcel_Cell:: columnIndexFromString($repeat[0]) - 1;
747. $colmax = PHPExcel_Cell:: columnIndexFromString($repeat[1]) - 1;
748. } else {
749. $colmin = 0;
750. $colmax = 255;
751. }
752. // Rows to repeat
753. if ($sheetSetup->isRowsToRepeatAtTopSet()) {
754. $repeat = $sheetSetup->getRowsToRepeatAtTop();
755. $rowmin = $repeat[0] - 1;
756. $rowmax = $repeat[1] - 1;
757. } else {
758. $rowmin = 0;
759. $rowmax = 65535;
760. }
762. // construct formula data manually because parser does not recognize absolute 3d cell references
763. $formulaData = pack('Cvvvvv', 0x3B, $i, $rowmin, $rowmax, $colmin, $colmax);
765. // store the DEFINEDNAME record
766. $chunk .= $this-> writeData($this->_writeDefinedNameBiff8( pack('C', 0x07), $formulaData, $i + 1, true));
767. }
768. }
770. // write the print areas, if any
771. for ($i = 0; $i < $total_worksheets; ++$i) {
772. $sheetSetup = $this->_phpExcel->getSheet($i)->getPageSetup();
773. if ($sheetSetup->isPrintAreaSet()) {
774. // Print area, e.g. A3:J6,H1:X20
775. $printArea = PHPExcel_Cell:: splitRange($sheetSetup->getPrintArea());
776. $countPrintArea = count($printArea);
778. $formulaData = '';
779. for ($j = 0; $j < $countPrintArea; ++$j) {
780. $printAreaRect = $printArea[$j]; // e.g. A3:J6
781. $printAreaRect[0] = PHPExcel_Cell:: coordinateFromString($printAreaRect[0]);
782. $printAreaRect[1] = PHPExcel_Cell:: coordinateFromString($printAreaRect[1]);
784. $print_rowmin = $printAreaRect[0][1] - 1;
785. $print_rowmax = $printAreaRect[1][1] - 1;
786. $print_colmin = PHPExcel_Cell:: columnIndexFromString($printAreaRect[0][0]) - 1;
787. $print_colmax = PHPExcel_Cell:: columnIndexFromString($printAreaRect[1][0]) - 1;
789. // construct formula data manually because parser does not recognize absolute 3d cell references
790. $formulaData .= pack('Cvvvvv', 0x3B, $i, $print_rowmin, $print_rowmax, $print_colmin, $print_colmax);
792. if ($j > 0) {
793. $formulaData .= pack('C', 0x10); // list operator token ','
794. }
795. }
797. // store the DEFINEDNAME record
798. $chunk .= $this-> writeData($this->_writeDefinedNameBiff8( pack('C', 0x06), $formulaData, $i + 1, true));
799. }
800. }
802. return $chunk;
803. }
805. /**
806. * Write a DEFINEDNAME record for BIFF8 using explicit binary formula data
807. *
808. * @param string $name The name in UTF-8
809. * @param string $formulaData The binary formula data
810. * @param string $sheetIndex 1-based sheet index the defined name applies to. 0 = global
811. * @param boolean $isBuiltIn Built-in name?
812. * @return string Complete binary record data
813. */
814. private function _writeDefinedNameBiff8($name, $formulaData, $sheetIndex = 0, $isBuiltIn = false)
815. {
816. $record = 0x0018;
818. // option flags
819. $options = $isBuiltIn ? 0x20 : 0x00;
821. // length of the name, character count
822. $nlen = PHPExcel_Shared_String:: CountCharacters($name);
824. // name with stripped length field
825. $name = substr( PHPExcel_Shared_String:: UTF8toBIFF8UnicodeLong($name), 2);
827. // size of the formula (in bytes)
828. $sz = strlen($formulaData);
830. // combine the parts
831. $data = pack('vCCvvvCCCC', $options, 0, $nlen, $sz, 0, $sheetIndex, 0, 0, 0, 0)
832. . $name . $formulaData;
833. $length = strlen($data);
835. $header = pack('vv', $record, $length);
837. return $header . $data;
838. }
840. /**
841. * Stores the CODEPAGE biff record.
842. */
843. private function _writeCodepage()
844. {
845. $record = 0x0042; // Record identifier
846. $length = 0x0002; // Number of bytes to follow
847. $cv = $this->_codepage; // The code page
849. $header = pack('vv', $record, $length);
850. $data = pack('v', $cv);
852. $this->_append($header . $data);
853. }
855. /**
856. * Write Excel BIFF WINDOW1 record.
857. */
858. private function _writeWindow1()
859. {
860. $record = 0x003D; // Record identifier
861. $length = 0x0012; // Number of bytes to follow
863. $xWn = 0x0000; // Horizontal position of window
864. $yWn = 0x0000; // Vertical position of window
865. $dxWn = 0x25BC; // Width of window
866. $dyWn = 0x1572; // Height of window
868. $grbit = 0x0038; // Option flags
870. // not supported by PHPExcel, so there is only one selected sheet, the active
871. $ctabsel = 1; // Number of workbook tabs selected
873. $wTabRatio = 0x0258; // Tab to scrollbar ratio
875. // not supported by PHPExcel, set to 0
876. $itabFirst = 0; // 1st displayed worksheet
877. $itabCur = $this->_phpExcel->getActiveSheetIndex(); // Active worksheet
879. $header = pack("vv", $record, $length);
880. $data = pack("vvvvvvvvv", $xWn, $yWn, $dxWn, $dyWn,
881. $grbit,
882. $itabCur, $itabFirst,
883. $ctabsel, $wTabRatio);
884. $this->_append($header . $data);
885. }
887. /**
888. * Writes Excel BIFF BOUNDSHEET record.
889. *
890. * @param PHPExcel_Worksheet $sheet Worksheet name
891. * @param integer $offset Location of worksheet BOF
892. */
893. private function _writeBoundsheet($sheet, $offset)
894. {
895. $sheetname = $sheet->getTitle();
896. $record = 0x0085; // Record identifier
898. // sheet state
899. switch ($sheet->getSheetState()) {
900. case PHPExcel_Worksheet:: SHEETSTATE_VISIBLE: $ss = 0x00; break;
901. case PHPExcel_Worksheet:: SHEETSTATE_HIDDEN: $ss = 0x01; break;
902. case PHPExcel_Worksheet:: SHEETSTATE_VERYHIDDEN: $ss = 0x02; break;
903. default: $ss = 0x00; break;
904. }
906. // sheet type
907. $st = 0x00;
909. $grbit = 0x0000; // Visibility and sheet type
911. if ($this->_BIFF_version == 0x0600) {
912. $data = pack("VCC", $offset, $ss, $st);
913. $data .= PHPExcel_Shared_String:: UTF8toBIFF8UnicodeShort($sheetname);
914. } else {
915. $cch = strlen($sheetname); // Length of sheet name
916. $data = pack("VCCC", $offset, $ss, $st, $cch);
917. $data .= $sheetname;
918. }
920. $length = strlen($data);
921. $header = pack("vv", $record, $length);
922. $this->_append($header . $data);
923. }
925. /**
926. * Write Internal SUPBOOK record
927. */
928. private function _writeSupbookInternal()
929. {
930. $record = 0x01AE; // Record identifier
931. $length = 0x0004; // Bytes to follow
933. $header = pack("vv", $record, $length);
934. $data = pack("vv", $this->_phpExcel->getSheetCount(), 0x0401);
935. return $this-> writeData($header . $data);
936. }
938. /**
939. * Writes the Excel BIFF EXTERNSHEET record. These references are used by
940. * formulas.
941. *
942. */
943. private function _writeExternsheetBiff8()
944. {
945. $total_references = count($this->_parser->_references);
946. $record = 0x0017; // Record identifier
947. $length = 2 + 6 * $total_references; // Number of bytes to follow
949. $supbook_index = 0; // FIXME: only using internal SUPBOOK record
950. $header = pack("vv", $record, $length);
951. $data = pack('v', $total_references);
952. for ($i = 0; $i < $total_references; ++$i) {
953. $data .= $this->_parser->_references[$i];
954. }
955. return $this-> writeData($header . $data);
956. }
958. /**
959. * Write Excel BIFF STYLE records.
960. */
961. private function _writeStyle()
962. {
963. $record = 0x0293; // Record identifier
964. $length = 0x0004; // Bytes to follow
966. $ixfe = 0x8000; // Index to cell style XF
967. $BuiltIn = 0x00; // Built-in style
968. $iLevel = 0xff; // Outline style level
970. $header = pack("vv", $record, $length);
971. $data = pack("vCC", $ixfe, $BuiltIn, $iLevel);
972. $this->_append($header . $data);
973. }
976. /**
977. * Writes Excel FORMAT record for non "built-in" numerical formats.
978. *
979. * @param string $format Custom format string
980. * @param integer $ifmt Format index code
981. */
982. private function _writeNumFormat($format, $ifmt)
983. {
984. $record = 0x041E; // Record identifier
986. if ($this->_BIFF_version == 0x0600) {
987. $numberFormatString = PHPExcel_Shared_String:: UTF8toBIFF8UnicodeLong($format);
988. $length = 2 + strlen($numberFormatString); // Number of bytes to follow
989. } elseif ($this->_BIFF_version == 0x0500) {
990. $length = 3 + strlen($format); // Number of bytes to follow
991. }
994. $header = pack("vv", $record, $length);
995. if ($this->_BIFF_version == 0x0600) {
996. $data = pack("v", $ifmt) . $numberFormatString;
997. $this->_append($header . $data);
998. } elseif ($this->_BIFF_version == 0x0500) {
999. $cch = strlen($format); // Length of format string
1000. $data = pack("vC", $ifmt, $cch);
1001. $this->_append($header . $data . $format);
1002. }
1003. }
1005. /**
1006. * Write DATEMODE record to indicate the date system in use (1904 or 1900).
1007. */
1008. private function _writeDatemode()
1009. {
1010. $record = 0x0022; // Record identifier
1011. $length = 0x0002; // Bytes to follow
1013. $f1904 = ( PHPExcel_Shared_Date:: getExcelCalendar() == PHPExcel_Shared_Date:: CALENDAR_MAC_1904) ?
1014. 1 : 0; // Flag for 1904 date system
1016. $header = pack("vv", $record, $length);
1017. $data = pack("v", $f1904);
1018. $this->_append($header . $data);
1019. }
1022. /**
1023. * Write BIFF record EXTERNCOUNT to indicate the number of external sheet
1024. * references in the workbook.
1025. *
1026. * Excel only stores references to external sheets that are used in NAME.
1027. * The workbook NAME record is required to define the print area and the repeat
1028. * rows and columns.
1029. *
1030. * A similar method is used in Worksheet.php for a slightly different purpose.
1031. *
1032. * @param integer $cxals Number of external references
1033. */
1034. private function _writeExterncount($cxals)
1035. {
1036. $record = 0x0016; // Record identifier
1037. $length = 0x0002; // Number of bytes to follow
1039. $header = pack("vv", $record, $length);
1040. $data = pack("v", $cxals);
1041. $this->_append($header . $data);
1042. }
1045. /**
1046. * Writes the Excel BIFF EXTERNSHEET record. These references are used by
1047. * formulas. NAME record is required to define the print area and the repeat
1048. * rows and columns.
1049. *
1050. * A similar method is used in Worksheet.php for a slightly different purpose.
1051. *
1052. * @param string $sheetname Worksheet name
1053. */
1054. private function _writeExternsheet($sheetname)
1055. {
1056. $record = 0x0017; // Record identifier
1057. $length = 0x02 + strlen($sheetname); // Number of bytes to follow
1059. $cch = strlen($sheetname); // Length of sheet name
1060. $rgch = 0x03; // Filename encoding
1062. $header = pack("vv", $record, $length);
1063. $data = pack("CC", $cch, $rgch);
1064. $this->_append($header . $data . $sheetname);
1065. }
1068. /**
1069. * Store the NAME record in the short format that is used for storing the print
1070. * area, repeat rows only and repeat columns only.
1071. *
1072. * @param integer $index Sheet index
1073. * @param integer $type Built-in name type
1074. * @param integer $rowmin Start row
1075. * @param integer $rowmax End row
1076. * @param integer $colmin Start colum
1077. * @param integer $colmax End column
1078. */
1079. private function _writeNameShort($index, $type, $rowmin, $rowmax, $colmin, $colmax)
1080. {
1081. $record = 0x0018; // Record identifier
1082. $length = 0x0024; // Number of bytes to follow
1084. $grbit = 0x0020; // Option flags
1085. $chKey = 0x00; // Keyboard shortcut
1086. $cch = 0x01; // Length of text name
1087. $cce = 0x0015; // Length of text definition
1088. $ixals = $index + 1; // Sheet index
1089. $itab = $ixals; // Equal to ixals
1090. $cchCustMenu = 0x00; // Length of cust menu text
1091. $cchDescription = 0x00; // Length of description text
1092. $cchHelptopic = 0x00; // Length of help topic text
1093. $cchStatustext = 0x00; // Length of status bar text
1094. $rgch = $type; // Built-in name type
1096. $unknown03 = 0x3b;
1097. $unknown04 = 0xffff-$index;
1098. $unknown05 = 0x0000;
1099. $unknown06 = 0x0000;
1100. $unknown07 = 0x1087;
1101. $unknown08 = 0x8005;
1103. $header = pack("vv", $record, $length);
1104. $data = pack("v", $grbit);
1105. $data .= pack("C", $chKey);
1106. $data .= pack("C", $cch);
1107. $data .= pack("v", $cce);
1108. $data .= pack("v", $ixals);
1109. $data .= pack("v", $itab);
1110. $data .= pack("C", $cchCustMenu);
1111. $data .= pack("C", $cchDescription);
1112. $data .= pack("C", $cchHelptopic);
1113. $data .= pack("C", $cchStatustext);
1114. $data .= pack("C", $rgch);
1115. $data .= pack("C", $unknown03);
1116. $data .= pack("v", $unknown04);
1117. $data .= pack("v", $unknown05);
1118. $data .= pack("v", $unknown06);
1119. $data .= pack("v", $unknown07);
1120. $data .= pack("v", $unknown08);
1121. $data .= pack("v", $index);
1122. $data .= pack("v", $index);
1123. $data .= pack("v", $rowmin);
1124. $data .= pack("v", $rowmax);
1125. $data .= pack("C", $colmin);
1126. $data .= pack("C", $colmax);
1127. $this->_append($header . $data);
1128. }
1131. /**
1132. * Store the NAME record in the long format that is used for storing the repeat
1133. * rows and columns when both are specified. This shares a lot of code with
1134. * _writeNameShort() but we use a separate method to keep the code clean.
1135. * Code abstraction for reuse can be carried too far, and I should know. ;-)
1136. *
1137. * @param integer $index Sheet index
1138. * @param integer $type Built-in name type
1139. * @param integer $rowmin Start row
1140. * @param integer $rowmax End row
1141. * @param integer $colmin Start colum
1142. * @param integer $colmax End column
1143. */
1144. private function _writeNameLong($index, $type, $rowmin, $rowmax, $colmin, $colmax)
1145. {
1146. $record = 0x0018; // Record identifier
1147. $length = 0x003d; // Number of bytes to follow
1148. $grbit = 0x0020; // Option flags
1149. $chKey = 0x00; // Keyboard shortcut
1150. $cch = 0x01; // Length of text name
1151. $cce = 0x002e; // Length of text definition
1152. $ixals = $index + 1; // Sheet index
1153. $itab = $ixals; // Equal to ixals
1154. $cchCustMenu = 0x00; // Length of cust menu text
1155. $cchDescription = 0x00; // Length of description text
1156. $cchHelptopic = 0x00; // Length of help topic text
1157. $cchStatustext = 0x00; // Length of status bar text
1158. $rgch = $type; // Built-in name type
1160. $unknown01 = 0x29;
1161. $unknown02 = 0x002b;
1162. $unknown03 = 0x3b;
1163. $unknown04 = 0xffff-$index;
1164. $unknown05 = 0x0000;
1165. $unknown06 = 0x0000;
1166. $unknown07 = 0x1087;
1167. $unknown08 = 0x8008;
1169. $header = pack("vv", $record, $length);
1170. $data = pack("v", $grbit);
1171. $data .= pack("C", $chKey);
1172. $data .= pack("C", $cch);
1173. $data .= pack("v", $cce);
1174. $data .= pack("v", $ixals);
1175. $data .= pack("v", $itab);
1176. $data .= pack("C", $cchCustMenu);
1177. $data .= pack("C", $cchDescription);
1178. $data .= pack("C", $cchHelptopic);
1179. $data .= pack("C", $cchStatustext);
1180. $data .= pack("C", $rgch);
1181. $data .= pack("C", $unknown01);
1182. $data .= pack("v", $unknown02);
1183. // Column definition
1184. $data .= pack("C", $unknown03);
1185. $data .= pack("v", $unknown04);
1186. $data .= pack("v", $unknown05);
1187. $data .= pack("v", $unknown06);
1188. $data .= pack("v", $unknown07);
1189. $data .= pack("v", $unknown08);
1190. $data .= pack("v", $index);
1191. $data .= pack("v", $index);
1192. $data .= pack("v", 0x0000);
1193. $data .= pack("v", 0x3fff);
1194. $data .= pack("C", $colmin);
1195. $data .= pack("C", $colmax);
1196. // Row definition
1197. $data .= pack("C", $unknown03);
1198. $data .= pack("v", $unknown04);
1199. $data .= pack("v", $unknown05);
1200. $data .= pack("v", $unknown06);
1201. $data .= pack("v", $unknown07);
1202. $data .= pack("v", $unknown08);
1203. $data .= pack("v", $index);
1204. $data .= pack("v", $index);
1205. $data .= pack("v", $rowmin);
1206. $data .= pack("v", $rowmax);
1207. $data .= pack("C", 0x00);
1208. $data .= pack("C", 0xff);
1209. // End of data
1210. $data .= pack("C", 0x10);
1211. $this->_append($header . $data);
1212. }
1214. /**
1215. * Stores the COUNTRY record for localization
1216. *
1217. * @return string
1218. */
1219. private function _writeCountry()
1220. {
1221. $record = 0x008C; // Record identifier
1222. $length = 4; // Number of bytes to follow
1224. $header = pack('vv', $record, $length);
1225. /* using the same country code always for simplicity */
1226. $data = pack('vv', $this->_country_code, $this->_country_code);
1227. //$this->_append($header . $data);
1228. return $this-> writeData($header . $data);
1229. }
1231. /**
1232. * Write the RECALCID record
1233. *
1234. * @return string
1235. */
1236. private function _writeRecalcId()
1237. {
1238. $record = 0x01C1; // Record identifier
1239. $length = 8; // Number of bytes to follow
1241. $header = pack('vv', $record, $length);
1243. // by inspection of real Excel files, MS Office Excel 2007 writes this
1244. $data = pack('VV', 0x000001C1, 0x00001E667);
1246. return $this-> writeData($header . $data);
1247. }
1249. /**
1250. * Stores the PALETTE biff record.
1251. */
1252. private function _writePalette()
1253. {
1254. $aref = $this->_palette;
1256. $record = 0x0092; // Record identifier
1257. $length = 2 + 4 * count($aref); // Number of bytes to follow
1258. $ccv = count($aref); // Number of RGB values to follow
1259. $data = ''; // The RGB data
1261. // Pack the RGB data
1262. foreach ($aref as $color) {
1263. foreach ($color as $byte) {
1264. $data .= pack("C",$byte);
1265. }
1266. }
1268. $header = pack("vvv", $record, $length, $ccv);
1269. $this->_append($header . $data);
1270. }
1272. /**
1273. * Handling of the SST continue blocks is complicated by the need to include an
1274. * additional continuation byte depending on whether the string is split between
1275. * blocks or whether it starts at the beginning of the block. (There are also
1276. * additional complications that will arise later when/if Rich Strings are
1277. * supported).
1278. *
1279. * The Excel documentation says that the SST record should be followed by an
1280. * EXTSST record. The EXTSST record is a hash table that is used to optimise
1281. * access to SST. However, despite the documentation it doesn't seem to be
1282. * required so we will ignore it.
1283. *
1284. * @return string Binary data
1285. */
1286. private function _writeSharedStringsTable()
1287. {
1288. // maximum size of record data (excluding record header)
1289. $continue_limit = 8224;
1291. // initialize array of record data blocks
1292. $recordDatas = array();
1294. // start SST record data block with total number of strings, total number of unique strings
1295. $recordData = pack("VV", $this->_str_total, $this->_str_unique);
1297. // loop through all (unique) strings in shared strings table
1298. foreach ( array_keys($this->_str_table) as $string) {
1300. // here $string is a BIFF8 encoded string
1302. // length = character count
1303. $headerinfo = unpack("vlength/Cencoding", $string);
1305. // currently, this is always 1 = uncompressed
1306. $encoding = $headerinfo["encoding"];
1308. // initialize finished writing current $string
1309. $finished = false;
1311. while ($finished === false) {
1313. // normally, there will be only one cycle, but if string cannot immediately be written as is
1314. // there will be need for more than one cylcle, if string longer than one record data block, there
1315. // may be need for even more cycles
1317. if ( strlen($recordData) + strlen($string) < $continue_limit) {
1318. // then we can write the string (or remainder of string) without any problems
1319. $recordData .= $string;
1321. // we are finished writing this string
1322. $finished = true;
1324. } else if ( strlen($recordData) + strlen($string) == $continue_limit) {
1325. // then we can also write the string (or remainder of string)
1326. $recordData .= $string;
1328. // but we close the record data block, and initialize a new one
1329. $recordDatas[] = $recordData;
1330. $recordData = '';
1332. // we are finished writing this string
1333. $finished = true;
1335. } else {
1336. // special treatment writing the string (or remainder of the string)
1337. // If the string is very long it may need to be written in more than one CONTINUE record.
1339. // check how many bytes more there is room for in the current record
1340. $space_remaining = $continue_limit - strlen($recordData);
1342. // minimum space needed
1343. // uncompressed: 2 byte string length length field + 1 byte option flags + 2 byte character
1344. // compressed: 2 byte string length length field + 1 byte option flags + 1 byte character
1345. $min_space_needed = ($encoding == 1) ? 5 : 4;
1347. // We have two cases
1348. // 1. space remaining is less than minimum space needed
1349. // here we must waste the space remaining and move to next record data block
1350. // 2. space remaining is greater than or equal to minimum space needed
1351. // here we write as much as we can in the current block, then move to next record data block
1353. // 1. space remaining is less than minimum space needed
1354. if ($space_remaining < $min_space_needed) {
1355. // we close the block, store the block data
1356. $recordDatas[] = $recordData;
1358. // and start new record data block where we start writing the string
1359. $recordData = '';
1361. // 2. space remaining is greater than or equal to minimum space needed
1362. } else {
1363. // initialize effective remaining space, for Unicode strings this may need to be reduced by 1, see below
1364. $effective_space_remaining = $space_remaining;
1366. // for uncompressed strings, sometimes effective space remaining is reduced by 1
1367. if ( $encoding == 1 && ( strlen($string) - $space_remaining) % 2 == 1 ) {
1368. --$effective_space_remaining;
1369. }
1371. // one block fininshed, store the block data
1372. $recordData .= substr($string, 0, $effective_space_remaining);
1374. $string = substr($string, $effective_space_remaining); // for next cycle in while loop
1375. $recordDatas[] = $recordData;
1377. // start new record data block with the repeated option flags
1378. $recordData = pack('C', $encoding);
1379. }
1380. }
1381. }
1382. }
1384. // Store the last record data block unless it is empty
1385. // if there was no need for any continue records, this will be the for SST record data block itself
1386. if ( strlen($recordData) > 0) {
1387. $recordDatas[] = $recordData;
1388. }
1390. // combine into one chunk with all the blocks SST, CONTINUE,...
1391. $chunk = '';
1392. foreach ($recordDatas as $i => $recordData) {
1393. // first block should have the SST record header, remaing should have CONTINUE header
1394. $record = ($i == 0) ? 0x00FC : 0x003C;
1396. $header = pack("vv", $record, strlen($recordData));
1397. $data = $header . $recordData;
1399. $chunk .= $this-> writeData($data);
1400. }
1402. return $chunk;
1403. }
1405. /**
1406. * Writes the MSODRAWINGGROUP record if needed. Possibly split using CONTINUE records.
1407. */
1408. private function _writeMsoDrawingGroup()
1409. {
1410. // write the Escher stream if necessary
1411. if (isset($this->_escher)) {
1412. $writer = new PHPExcel_Writer_Excel5_Escher($this->_escher);
1413. $data = $writer->close();
1415. $record = 0x00EB;
1416. $length = strlen($data);
1417. $header = pack("vv", $record, $length);
1419. return $this-> writeData($header . $data);
1421. } else {
1422. return '';
1423. }
1424. }
1426. /**
1427. * Get Escher object
1428. *
1429. * @return PHPExcel_Shared_Escher
1430. */
1431. public function getEscher()
1432. {
1433. return $this->_escher;
1434. }
1436. /**
1437. * Set Escher object
1438. *
1439. * @param PHPExcel_Shared_Escher $pValue
1440. */
1441. public function setEscher(PHPExcel_Shared_Escher $pValue = null)
1442. {
1443. $this->_escher = $pValue;
1444. }
1446. }

Documentation generated on Sun, 27 Feb 2011 16:37:07 -0800 by phpDocumentor 1.4.3