PageRenderTime 68ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/include/php_writeexcel/class.writeexcel_worksheet.inc.php

https://bitbucket.org/yousef_fadila/vtiger
PHP | 2954 lines | 1615 code | 510 blank | 829 comment | 223 complexity | 73aa872ec571c908ea15f910fb28d549 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  1. <?php
  2. /*
  3. * Copyleft 2002 Johann Hanne
  4. *
  5. * This is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU Lesser General Public
  7. * License as published by the Free Software Foundation; either
  8. * version 2.1 of the License, or (at your option) any later version.
  9. *
  10. * This software is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with this software; if not, write to the
  17. * Free Software Foundation, Inc., 59 Temple Place,
  18. * Suite 330, Boston, MA 02111-1307 USA
  19. */
  20. /*
  21. * This is the Spreadsheet::WriteExcel Perl package ported to PHP
  22. * Spreadsheet::WriteExcel was written by John McNamara, jmcnamara@cpan.org
  23. */
  24. require_once "class.writeexcel_biffwriter.inc.php";
  25. class writeexcel_worksheet extends writeexcel_biffwriter {
  26. var $_name;
  27. var $_index;
  28. var $_activesheet;
  29. var $_firstsheet;
  30. var $_url_format;
  31. var $_parser;
  32. var $_tempdir;
  33. var $_ext_sheets;
  34. var $_using_tmpfile;
  35. var $_filehandle;
  36. var $_fileclosed;
  37. var $_offset;
  38. var $_xls_rowmax;
  39. var $_xls_colmax;
  40. var $_xls_strmax;
  41. var $_dim_rowmin;
  42. var $_dim_rowmax;
  43. var $_dim_colmin;
  44. var $_dim_colmax;
  45. var $_colinfo;
  46. var $_selection;
  47. var $_panes;
  48. var $_active_pane;
  49. var $_frozen;
  50. var $_selected;
  51. var $_paper_size;
  52. var $_orientation;
  53. var $_header;
  54. var $_footer;
  55. var $_hcenter;
  56. var $_vcenter;
  57. var $_margin_head;
  58. var $_margin_foot;
  59. var $_margin_left;
  60. var $_margin_right;
  61. var $_margin_top;
  62. var $_margin_bottom;
  63. var $_title_rowmin;
  64. var $_title_rowmax;
  65. var $_title_colmin;
  66. var $_title_colmax;
  67. var $_print_rowmin;
  68. var $_print_rowmax;
  69. var $_print_colmin;
  70. var $_print_colmax;
  71. var $_print_gridlines;
  72. var $_screen_gridlines;
  73. var $_print_headers;
  74. var $_fit_page;
  75. var $_fit_width;
  76. var $_fit_height;
  77. var $_hbreaks;
  78. var $_vbreaks;
  79. var $_protect;
  80. var $_password;
  81. var $_col_sizes;
  82. var $_row_sizes;
  83. var $_col_formats;
  84. var $_row_formats;
  85. var $_zoom;
  86. var $_print_scale;
  87. //added to get the tmp file name
  88. var $_tmp_file_name;
  89. /*
  90. * Constructor. Creates a new Worksheet object from a BIFFwriter object
  91. */
  92. function writeexcel_worksheet($name, $index, &$activesheet, &$firstsheet,
  93. &$url_format, &$parser, $tempdir) {
  94. $this->writeexcel_biffwriter();
  95. $rowmax = 65536; // 16384 in Excel 5
  96. $colmax = 256;
  97. $strmax = 20000;
  98. $this->_name = $name;
  99. $this->_index = $index;
  100. $this->_activesheet = &$activesheet;
  101. $this->_firstsheet = &$firstsheet;
  102. $this->_url_format = &$url_format;
  103. $this->_parser = &$parser;
  104. $this->_tempdir = $tempdir;
  105. $this->_ext_sheets = array();
  106. $this->_using_tmpfile = 1;
  107. $this->_filehandle = false;
  108. $this->_fileclosed = 0;
  109. $this->_offset = 0;
  110. $this->_xls_rowmax = $rowmax;
  111. $this->_xls_colmax = $colmax;
  112. $this->_xls_strmax = $strmax;
  113. $this->_dim_rowmin = $rowmax +1;
  114. $this->_dim_rowmax = 0;
  115. $this->_dim_colmin = $colmax +1;
  116. $this->_dim_colmax = 0;
  117. $this->_colinfo = array();
  118. $this->_selection = array(0, 0);
  119. $this->_panes = array();
  120. $this->_active_pane = 3;
  121. $this->_frozen = 0;
  122. $this->_selected = 0;
  123. $this->_paper_size = 0x0;
  124. $this->_orientation = 0x1;
  125. $this->_header = '';
  126. $this->_footer = '';
  127. $this->_hcenter = 0;
  128. $this->_vcenter = 0;
  129. $this->_margin_head = 0.50;
  130. $this->_margin_foot = 0.50;
  131. $this->_margin_left = 0.75;
  132. $this->_margin_right = 0.75;
  133. $this->_margin_top = 1.00;
  134. $this->_margin_bottom = 1.00;
  135. $this->_title_rowmin = false;
  136. $this->_title_rowmax = false;
  137. $this->_title_colmin = false;
  138. $this->_title_colmax = false;
  139. $this->_print_rowmin = false;
  140. $this->_print_rowmax = false;
  141. $this->_print_colmin = false;
  142. $this->_print_colmax = false;
  143. $this->_print_gridlines = 1;
  144. $this->_screen_gridlines = 1;
  145. $this->_print_headers = 0;
  146. $this->_fit_page = 0;
  147. $this->_fit_width = 0;
  148. $this->_fit_height = 0;
  149. $this->_hbreaks = array();
  150. $this->_vbreaks = array();
  151. $this->_protect = 0;
  152. $this->_password = false;
  153. $this->_col_sizes = array();
  154. $this->_row_sizes = array();
  155. $this->_col_formats = array();
  156. $this->_row_formats = array();
  157. $this->_zoom = 100;
  158. $this->_print_scale = 100;
  159. $this->_initialize();
  160. }
  161. ###############################################################################
  162. #
  163. # _initialize()
  164. #
  165. # Open a tmp file to store the majority of the Worksheet data. If this fails,
  166. # for example due to write permissions, store the data in memory. This can be
  167. # slow for large files.
  168. #
  169. function _initialize() {
  170. # Open tmp file for storing Worksheet data.
  171. $this->_tmp_file_name = tempnam($this->_tempdir, "php_writeexcel");
  172. $fh=fopen($this->_tmp_file_name, "w+b");
  173. if ($fh) {
  174. # Store filehandle
  175. $this->_filehandle = $fh;
  176. } else {
  177. # If tempfile() failed store data in memory
  178. $this->_using_tmpfile = 0;
  179. if ($this->_index == 0) {
  180. $dir = $this->_tempdir;
  181. //todo warn "Unable to create temp files in $dir. Refer to set_tempdir()".
  182. // " in the Spreadsheet::WriteExcel documentation.\n" ;
  183. }
  184. }
  185. }
  186. /*
  187. * Add data to the beginning of the workbook (note the reverse order)
  188. * and to the end of the workbook.
  189. */
  190. function _close($sheetnames) {
  191. ///////////////////////////////
  192. // Prepend in reverse order!!
  193. //
  194. $this->_store_dimensions(); // Prepend the sheet dimensions
  195. $this->_store_password(); // Prepend the sheet password
  196. $this->_store_protect(); // Prepend the sheet protection
  197. $this->_store_setup(); // Prepend the page setup
  198. $this->_store_margin_bottom(); // Prepend the bottom margin
  199. $this->_store_margin_top(); // Prepend the top margin
  200. $this->_store_margin_right(); // Prepend the right margin
  201. $this->_store_margin_left(); // Prepend the left margin
  202. $this->_store_vcenter(); // Prepend the page vertical
  203. // centering
  204. $this->_store_hcenter(); // Prepend the page horizontal
  205. // centering
  206. $this->_store_footer(); // Prepend the page footer
  207. $this->_store_header(); // Prepend the page header
  208. $this->_store_vbreak(); // Prepend the vertical page breaks
  209. $this->_store_hbreak(); // Prepend the horizontal
  210. // page breaks
  211. $this->_store_wsbool(); // Prepend WSBOOL
  212. $this->_store_gridset(); // Prepend GRIDSET
  213. $this->_store_print_gridlines(); // Prepend PRINTGRIDLINES
  214. $this->_store_print_headers(); // Prepend PRINTHEADERS
  215. // Prepend EXTERNSHEET references
  216. $num_sheets = sizeof($sheetnames);
  217. for ($i = $num_sheets; $i > 0; $i--) {
  218. $sheetname = $sheetnames[$i-1];
  219. $this->_store_externsheet($sheetname);
  220. }
  221. $this->_store_externcount($num_sheets); // Prepend the EXTERNCOUNT
  222. // of external references.
  223. // Prepend the COLINFO records if they exist
  224. if (sizeof($this->_colinfo)>0){
  225. while (sizeof($this->_colinfo)>0) {
  226. $arrayref = array_pop ($this->_colinfo);
  227. $this->_store_colinfo($arrayref);
  228. }
  229. $this->_store_defcol();
  230. }
  231. $this->_store_bof(0x0010); // Prepend the BOF record
  232. //
  233. // End of prepend. Read upwards from here.
  234. ////////////////////////////////////////////
  235. // Append
  236. $this->_store_window2();
  237. $this->_store_zoom();
  238. if (sizeof($this->_panes)>0) {
  239. $this->_store_panes($this->_panes);
  240. }
  241. $this->_store_selection($this->_selection);
  242. $this->_store_eof();
  243. }
  244. /*
  245. * Retrieve the worksheet name.
  246. */
  247. function get_name() {
  248. return $this->_name;
  249. }
  250. ###############################################################################
  251. #
  252. # get_data().
  253. #
  254. # Retrieves data from memory in one chunk, or from disk in $buffer
  255. # sized chunks.
  256. #
  257. function get_data() {
  258. $buffer = 4096;
  259. # Return data stored in memory
  260. if ($this->_data!==false) {
  261. $tmp = $this->_data;
  262. $this->_data=false;
  263. $fh = $this->_filehandle;
  264. if ($this->_using_tmpfile) {
  265. fseek($fh, 0, SEEK_SET);
  266. }
  267. if ($this->_debug) {
  268. print "*** worksheet::get_data() called (1):";
  269. for ($c=0;$c<strlen($tmp);$c++) {
  270. if ($c%16==0) {
  271. print "\n";
  272. }
  273. printf("%02X ", ord($tmp[$c]));
  274. }
  275. print "\n";
  276. }
  277. return $tmp;
  278. }
  279. # Return data stored on disk
  280. if ($this->_using_tmpfile) {
  281. if ($tmp=fread($this->_filehandle, $buffer)) {
  282. if ($this->_debug) {
  283. print "*** worksheet::get_data() called (2):";
  284. for ($c=0;$c<strlen($tmp);$c++) {
  285. if ($c%16==0) {
  286. print "\n";
  287. }
  288. printf("%02X ", ord($tmp[$c]));
  289. }
  290. print "\n";
  291. }
  292. return $tmp;
  293. }
  294. }
  295. # No data to return
  296. return false;
  297. }
  298. /*
  299. * Set this worksheet as a selected worksheet, i.e. the worksheet has
  300. * its tab highlighted.
  301. */
  302. function select() {
  303. $this->_selected = 1;
  304. }
  305. /*
  306. * Set this worksheet as the active worksheet, i.e. the worksheet
  307. * that is displayed when the workbook is opened. Also set it as
  308. * selected.
  309. */
  310. function activate() {
  311. $this->_selected = 1;
  312. $this->_activesheet = $this->_index;
  313. }
  314. /*
  315. * Set this worksheet as the first visible sheet. This is necessary
  316. * when there are a large number of worksheets and the activated
  317. * worksheet is not visible on the screen.
  318. */
  319. function set_first_sheet() {
  320. $this->_firstsheet = $this->_index;
  321. }
  322. /*
  323. * Set the worksheet protection flag to prevent accidental modification
  324. * and to hide formulas if the locked and hidden format properties have
  325. * been set.
  326. */
  327. function protect($password) {
  328. $this->_protect = 1;
  329. $this->_password = $this->_encode_password($password);
  330. }
  331. ###############################################################################
  332. #
  333. # set_column($firstcol, $lastcol, $width, $format, $hidden)
  334. #
  335. # Set the width of a single column or a range of column.
  336. # See also: _store_colinfo
  337. #
  338. function set_column() {
  339. $_=func_get_args();
  340. $cell = $_[0];
  341. # Check for a cell reference in A1 notation and substitute row and column
  342. if (preg_match('/^\D/', $cell)) {
  343. $_ = $this->_substitute_cellref($_);
  344. }
  345. array_push($this->_colinfo, $_);
  346. # Store the col sizes for use when calculating image vertices taking
  347. # hidden columns into account. Also store the column formats.
  348. #
  349. if (sizeof($_)<3) {
  350. # Ensure at least $firstcol, $lastcol and $width
  351. return;
  352. }
  353. $width = $_[4] ? 0 : $_[2]; # Set width to zero if column is hidden
  354. $format = $_[3];
  355. list($firstcol, $lastcol) = $_;
  356. for ($col=$firstcol;$col<=$lastcol;$col++) {
  357. $this->_col_sizes[$col] = $width;
  358. if ($format) {
  359. $this->_col_formats[$col] = $format;
  360. }
  361. }
  362. }
  363. ###############################################################################
  364. #
  365. # set_selection()
  366. #
  367. # Set which cell or cells are selected in a worksheet: see also the
  368. # function _store_selection
  369. #
  370. function set_selection() {
  371. $_=func_get_args();
  372. # Check for a cell reference in A1 notation and substitute row and column
  373. if (preg_match('/^\D/', $_[0])) {
  374. $_ = $this->_substitute_cellref($_);
  375. }
  376. $this->_selection = $_;
  377. }
  378. ###############################################################################
  379. #
  380. # freeze_panes()
  381. #
  382. # Set panes and mark them as frozen. See also _store_panes().
  383. #
  384. function freeze_panes() {
  385. $_=func_get_args();
  386. # Check for a cell reference in A1 notation and substitute row and column
  387. if (preg_match('/^\D/', $_[0])) {
  388. $_ = $this->_substitute_cellref($_);
  389. }
  390. $this->_frozen = 1;
  391. $this->_panes = $_;
  392. }
  393. ###############################################################################
  394. #
  395. # thaw_panes()
  396. #
  397. # Set panes and mark them as unfrozen. See also _store_panes().
  398. #
  399. function thaw_panes() {
  400. $_=func_get_args();
  401. $this->_frozen = 0;
  402. $this->_panes = $_;
  403. }
  404. /*
  405. * Set the page orientation as portrait.
  406. */
  407. function set_portrait() {
  408. $this->_orientation = 1;
  409. }
  410. /*
  411. * Set the page orientation as landscape.
  412. */
  413. function set_landscape() {
  414. $this->_orientation = 0;
  415. }
  416. /*
  417. * Set the paper type. Ex. 1 = US Letter, 9 = A4
  418. */
  419. function set_paper($type) {
  420. $this->_paper_size = $type;
  421. }
  422. /*
  423. * Set the page header caption and optional margin.
  424. */
  425. function set_header($string, $margin) {
  426. if (strlen($string) >= 255) {
  427. trigger_error("Header string must be less than 255 characters",
  428. E_USER_WARNING);
  429. return;
  430. }
  431. $this->_header = $string;
  432. $this->_margin_head = $margin;
  433. }
  434. /*
  435. * Set the page footer caption and optional margin.
  436. */
  437. function set_footer($string, $margin) {
  438. if (strlen($string) >= 255) {
  439. trigger_error("Footer string must be less than 255 characters",
  440. E_USER_WARNING);
  441. return;
  442. }
  443. $this->_footer = $string;
  444. $this->_margin_foot = $margin;
  445. }
  446. /*
  447. * Center the page horizontally.
  448. */
  449. function center_horizontally($hcenter=1) {
  450. $this->_hcenter = $hcenter;
  451. }
  452. /*
  453. * Center the page horizontally.
  454. */
  455. function center_vertically($vcenter=1) {
  456. $this->_vcenter = $vcenter;
  457. }
  458. /*
  459. * Set all the page margins to the same value in inches.
  460. */
  461. function set_margins($margin) {
  462. $this->set_margin_left($margin);
  463. $this->set_margin_right($margin);
  464. $this->set_margin_top($margin);
  465. $this->set_margin_bottom($margin);
  466. }
  467. /*
  468. * Set the left and right margins to the same value in inches.
  469. */
  470. function set_margins_LR($margin) {
  471. $this->set_margin_left($margin);
  472. $this->set_margin_right($margin);
  473. }
  474. /*
  475. * Set the top and bottom margins to the same value in inches.
  476. */
  477. function set_margins_TB($margin) {
  478. $this->set_margin_top($margin);
  479. $this->set_margin_bottom($margin);
  480. }
  481. /*
  482. * Set the left margin in inches.
  483. */
  484. function set_margin_left($margin=0.75) {
  485. $this->_margin_left = $margin;
  486. }
  487. /*
  488. * Set the right margin in inches.
  489. */
  490. function set_margin_right($margin=0.75) {
  491. $this->_margin_right = $margin;
  492. }
  493. /*
  494. * Set the top margin in inches.
  495. */
  496. function set_margin_top($margin=1.00) {
  497. $this->_margin_top = $margin;
  498. }
  499. /*
  500. * Set the bottom margin in inches.
  501. */
  502. function set_margin_bottom($margin=1.00) {
  503. $this->_margin_bottom = $margin;
  504. }
  505. ###############################################################################
  506. #
  507. # repeat_rows($first_row, $last_row)
  508. #
  509. # Set the rows to repeat at the top of each printed page. See also the
  510. # _store_name_xxxx() methods in Workbook.pm.
  511. #
  512. function repeat_rows() {
  513. $_=func_get_args();
  514. $this->_title_rowmin = $_[0];
  515. $this->_title_rowmax = isset($_[1]) ? $_[1] : $_[0]; # Second row is optional
  516. }
  517. ###############################################################################
  518. #
  519. # repeat_columns($first_col, $last_col)
  520. #
  521. # Set the columns to repeat at the left hand side of each printed page.
  522. # See also the _store_names() methods in Workbook.pm.
  523. #
  524. function repeat_columns() {
  525. $_=func_get_args();
  526. # Check for a cell reference in A1 notation and substitute row and column
  527. if (preg_match('/^\D/', $_[0])) {
  528. $_ = $this->_substitute_cellref($_);
  529. }
  530. $this->_title_colmin = $_[0];
  531. $this->_title_colmax = isset($_[1]) ? $_[1] : $_[0]; # Second col is optional
  532. }
  533. ###############################################################################
  534. #
  535. # print_area($first_row, $first_col, $last_row, $last_col)
  536. #
  537. # Set the area of each worksheet that will be printed. See also the
  538. # _store_names() methods in Workbook.pm.
  539. #
  540. function print_area() {
  541. $_=func_get_args();
  542. # Check for a cell reference in A1 notation and substitute row and column
  543. if (preg_match('/^\D/', $_[0])) {
  544. $_ = $this->_substitute_cellref($_);
  545. }
  546. if (sizeof($_) != 4) {
  547. # Require 4 parameters
  548. return;
  549. }
  550. $this->_print_rowmin = $_[0];
  551. $this->_print_colmin = $_[1];
  552. $this->_print_rowmax = $_[2];
  553. $this->_print_colmax = $_[3];
  554. }
  555. /*
  556. * Set the option to hide gridlines on the screen and the printed page.
  557. * There are two ways of doing this in the Excel BIFF format: The first
  558. * is by setting the DspGrid field of the WINDOW2 record, this turns off
  559. * the screen and subsequently the print gridline. The second method is
  560. * to via the PRINTGRIDLINES and GRIDSET records, this turns off the
  561. * printed gridlines only. The first method is probably sufficient for
  562. * most cases. The second method is supported for backwards compatibility.
  563. */
  564. function hide_gridlines($option=1) {
  565. if ($option == 0) {
  566. $this->_print_gridlines = 1; # 1 = display, 0 = hide
  567. $this->_screen_gridlines = 1;
  568. } elseif ($option == 1) {
  569. $this->_print_gridlines = 0;
  570. $this->_screen_gridlines = 1;
  571. } else {
  572. $this->_print_gridlines = 0;
  573. $this->_screen_gridlines = 0;
  574. }
  575. }
  576. /*
  577. * Set the option to print the row and column headers on the printed page.
  578. * See also the _store_print_headers() method below.
  579. */
  580. function print_row_col_headers($headers=1) {
  581. $this->_print_headers = $headers;
  582. }
  583. /*
  584. * Store the vertical and horizontal number of pages that will define
  585. * the maximum area printed. See also _store_setup() and _store_wsbool()
  586. * below.
  587. */
  588. function fit_to_pages($width, $height) {
  589. $this->_fit_page = 1;
  590. $this->_fit_width = $width;
  591. $this->_fit_height = $height;
  592. }
  593. /*
  594. * Store the horizontal page breaks on a worksheet.
  595. */
  596. function set_h_pagebreaks($breaks) {
  597. $this->_hbreaks=array_merge($this->_hbreaks, $breaks);
  598. }
  599. /*
  600. * Store the vertical page breaks on a worksheet.
  601. */
  602. function set_v_pagebreaks($breaks) {
  603. $this->_vbreaks=array_merge($this->_vbreaks, $breaks);
  604. }
  605. /*
  606. * Set the worksheet zoom factor.
  607. */
  608. function set_zoom($scale=100) {
  609. // Confine the scale to Excel's range
  610. if ($scale < 10 || $scale > 400) {
  611. trigger_error("Zoom factor $scale outside range: ".
  612. "10 <= zoom <= 400", E_USER_WARNING);
  613. $scale = 100;
  614. }
  615. $this->_zoom = $scale;
  616. }
  617. /*
  618. * Set the scale factor for the printed page.
  619. */
  620. function set_print_scale($scale=100) {
  621. // Confine the scale to Excel's range
  622. if ($scale < 10 || $scale > 400) {
  623. trigger_error("Print scale $scale outside range: ".
  624. "10 <= zoom <= 400", E_USER_WARNING);
  625. $scale = 100;
  626. }
  627. // Turn off "fit to page" option
  628. $this->_fit_page = 0;
  629. $this->_print_scale = $scale;
  630. }
  631. ###############################################################################
  632. #
  633. # write($row, $col, $token, $format)
  634. #
  635. # Parse $token call appropriate write method. $row and $column are zero
  636. # indexed. $format is optional.
  637. #
  638. # Returns: return value of called subroutine
  639. #
  640. function write() {
  641. $_=func_get_args();
  642. # Check for a cell reference in A1 notation and substitute row and column
  643. if (preg_match('/^\D/', $_[0])) {
  644. $_ = $this->_substitute_cellref($_);
  645. }
  646. $token = $_[2];
  647. # Match an array ref.
  648. if (is_array($token)) {
  649. return call_user_method_array('write_row', $this, $_);
  650. }
  651. # Match number
  652. if (preg_match('/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/', $token)) {
  653. return call_user_method_array('write_number', $this, $_);
  654. }
  655. # Match http, https or ftp URL
  656. elseif (preg_match('|^[fh]tt?ps?://|', $token)) {
  657. return call_user_method_array('write_url', $this, $_);
  658. }
  659. # Match mailto:
  660. elseif (preg_match('/^mailto:/', $token)) {
  661. return call_user_method_array('write_url', $this, $_);
  662. }
  663. # Match internal or external sheet link
  664. elseif (preg_match('[^(?:in|ex)ternal:]', $token)) {
  665. return call_user_method_array('write_url', $this, $_);
  666. }
  667. # Match formula
  668. elseif (preg_match('/^=/', $token)) {
  669. return call_user_method_array('write_formula', $this, $_);
  670. }
  671. # Match blank
  672. elseif ($token == '') {
  673. array_splice($_, 2, 1); # remove the empty string from the parameter list
  674. return call_user_method_array('write_blank', $this, $_);
  675. }
  676. # Default: match string
  677. else {
  678. return call_user_method_array('write_string', $this, $_);
  679. }
  680. }
  681. ###############################################################################
  682. #
  683. # write_row($row, $col, $array_ref, $format)
  684. #
  685. # Write a row of data starting from ($row, $col). Call write_col() if any of
  686. # the elements of the array ref are in turn array refs. This allows the writing
  687. # of 1D or 2D arrays of data in one go.
  688. #
  689. # Returns: the first encountered error value or zero for no errors
  690. #
  691. function write_row() {
  692. $_=func_get_args();
  693. # Check for a cell reference in A1 notation and substitute row and column
  694. if (preg_match('/^\D/', $_[0])) {
  695. $_ = $this->_substitute_cellref($_);
  696. }
  697. # Catch non array refs passed by user.
  698. if (!is_array($_[2])) {
  699. trigger_error("Not an array ref in call to write_row()!", E_USER_ERROR);
  700. }
  701. list($row, $col, $tokens)=array_splice($_, 0, 3);
  702. $options = $_[0];
  703. $error = 0;
  704. foreach ($tokens as $token) {
  705. # Check for nested arrays
  706. if (is_array($token)) {
  707. $ret = $this->write_col($row, $col, $token, $options);
  708. } else {
  709. $ret = $this->write ($row, $col, $token, $options);
  710. }
  711. # Return only the first error encountered, if any.
  712. $error = $error || $ret;
  713. $col++;
  714. }
  715. return $error;
  716. }
  717. ###############################################################################
  718. #
  719. # _XF()
  720. #
  721. # Returns an index to the XF record in the workbook.
  722. # TODO
  723. #
  724. # Note: this is a function, not a method.
  725. #
  726. function _XF($row=false, $col=false, $format=false) {
  727. if ($format) {
  728. return $format->get_xf_index();
  729. } elseif (isset($this->_row_formats[$row])) {
  730. return $this->_row_formats[$row]->get_xf_index();
  731. } elseif (isset($this->_col_formats[$col])) {
  732. return $this->_col_formats[$col]->get_xf_index();
  733. } else {
  734. return 0x0F;
  735. }
  736. }
  737. ###############################################################################
  738. #
  739. # write_col($row, $col, $array_ref, $format)
  740. #
  741. # Write a column of data starting from ($row, $col). Call write_row() if any of
  742. # the elements of the array ref are in turn array refs. This allows the writing
  743. # of 1D or 2D arrays of data in one go.
  744. #
  745. # Returns: the first encountered error value or zero for no errors
  746. #
  747. function write_col() {
  748. $_=func_get_args();
  749. # Check for a cell reference in A1 notation and substitute row and column
  750. if (preg_match('/^\D/', $_[0])) {
  751. $_ = $this->_substitute_cellref($_);
  752. }
  753. # Catch non array refs passed by user.
  754. if (!is_array($_[2])) {
  755. trigger_error("Not an array ref in call to write_row()!", E_USER_ERROR);
  756. }
  757. $row = array_shift($_);
  758. $col = array_shift($_);
  759. $tokens = array_shift($_);
  760. $options = $_;
  761. $error = 0;
  762. foreach ($tokens as $token) {
  763. # write() will deal with any nested arrays
  764. $ret = $this->write($row, $col, $token, $options);
  765. # Return only the first error encountered, if any.
  766. $error = $error || $ret;
  767. $row++;
  768. }
  769. return $error;
  770. }
  771. ###############################################################################
  772. ###############################################################################
  773. #
  774. # Internal methods
  775. #
  776. ###############################################################################
  777. #
  778. # _append(), overloaded.
  779. #
  780. # Store Worksheet data in memory using the base class _append() or to a
  781. # temporary file, the default.
  782. #
  783. function _append($data) {
  784. if (func_num_args()>1) {
  785. trigger_error("writeexcel_worksheet::_append() ".
  786. "called with more than one argument", E_USER_ERROR);
  787. }
  788. if ($this->_using_tmpfile) {
  789. if ($this->_debug) {
  790. print "worksheet::_append() called:";
  791. for ($c=0;$c<strlen($data);$c++) {
  792. if ($c%16==0) {
  793. print "\n";
  794. }
  795. printf("%02X ", ord($data[$c]));
  796. }
  797. print "\n";
  798. }
  799. # Add CONTINUE records if necessary
  800. if (strlen($data) > $this->_limit) {
  801. $data = $this->_add_continue($data);
  802. }
  803. fputs($this->_filehandle, $data);
  804. $this->_datasize += strlen($data);
  805. } else {
  806. parent::_append($data);
  807. }
  808. }
  809. ###############################################################################
  810. #
  811. # _substitute_cellref()
  812. #
  813. # Substitute an Excel cell reference in A1 notation for zero based row and
  814. # column values in an argument list.
  815. #
  816. # Ex: ("A4", "Hello") is converted to (3, 0, "Hello").
  817. #
  818. // Exactly one array must be passed!
  819. function _substitute_cellref($_) {
  820. $cell = strtoupper(array_shift($_));
  821. # Convert a column range: 'A:A' or 'B:G'
  822. if (preg_match('/([A-I]?[A-Z]):([A-I]?[A-Z])/', $cell, $reg)) {
  823. list($dummy, $col1) = $this->_cell_to_rowcol($reg[1] .'1'); # Add a dummy row
  824. list($dummy, $col2) = $this->_cell_to_rowcol($reg[2] .'1'); # Add a dummy row
  825. return array_merge(array($col1, $col2), $_);
  826. }
  827. # Convert a cell range: 'A1:B7'
  828. if (preg_match('/\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)/', $cell, $reg)) {
  829. list($row1, $col1) = $this->_cell_to_rowcol($reg[1]);
  830. list($row2, $col2) = $this->_cell_to_rowcol($reg[2]);
  831. return array_merge(array($row1, $col1, $row2, $col2), $_);
  832. }
  833. # Convert a cell reference: 'A1' or 'AD2000'
  834. if (preg_match('/\$?([A-I]?[A-Z]\$?\d+)/', $cell, $reg)) {
  835. list($row1, $col1) = $this->_cell_to_rowcol($reg[1]);
  836. return array_merge(array($row1, $col1), $_);
  837. }
  838. trigger_error("Unknown cell reference $cell", E_USER_ERROR);
  839. }
  840. ###############################################################################
  841. #
  842. # _cell_to_rowcol($cell_ref)
  843. #
  844. # Convert an Excel cell reference in A1 notation to a zero based row and column
  845. # reference; converts C1 to (0, 2).
  846. #
  847. # Returns: row, column
  848. #
  849. # TODO use functions in Utility.pm
  850. #
  851. function _cell_to_rowcol($cell) {
  852. preg_match('/\$?([A-I]?[A-Z])\$?(\d+)/', $cell, $reg);
  853. $col = $reg[1];
  854. $row = $reg[2];
  855. # Convert base26 column string to number
  856. # All your Base are belong to us.
  857. $chars = preg_split('//', $col, -1, PREG_SPLIT_NO_EMPTY);
  858. $expn = 0;
  859. $col = 0;
  860. while (sizeof($chars)) {
  861. $char = array_pop($chars); # LS char first
  862. $col += (ord($char) -ord('A') +1) * pow(26, $expn);
  863. $expn++;
  864. }
  865. # Convert 1-index to zero-index
  866. $row--;
  867. $col--;
  868. return array($row, $col);
  869. }
  870. /*
  871. * This is an internal method that is used to filter elements of the
  872. * array of pagebreaks used in the _store_hbreak() and _store_vbreak()
  873. * methods. It:
  874. * 1. Removes duplicate entries from the list.
  875. * 2. Sorts the list.
  876. * 3. Removes 0 from the list if present.
  877. */
  878. function _sort_pagebreaks($breaks) {
  879. // Hash slice to remove duplicates
  880. foreach ($breaks as $break) {
  881. $hash["$break"]=1;
  882. }
  883. // Numerical sort
  884. $breaks=array_keys($hash);
  885. sort($breaks, SORT_NUMERIC);
  886. // Remove zero
  887. if ($breaks[0] == 0) {
  888. array_shift($breaks);
  889. }
  890. // 1000 vertical pagebreaks appears to be an internal Excel 5 limit.
  891. // It is slightly higher in Excel 97/200, approx. 1026
  892. if (sizeof($breaks) > 1000) {
  893. array_splice($breaks, 1000);
  894. }
  895. return $breaks;
  896. }
  897. /*
  898. * Based on the algorithm provided by Daniel Rentz of OpenOffice.
  899. */
  900. function _encode_password($plaintext) {
  901. $chars=preg_split('//', $plaintext, -1, PREG_SPLIT_NO_EMPTY);
  902. $count=sizeof($chars);
  903. for ($c=0;$c<sizeof($chars);$c++) {
  904. $char=&$chars[$c];
  905. $char = ord($char) << ++$i;
  906. $low_15 = $char & 0x7fff;
  907. $high_15 = $char & 0x7fff << 15;
  908. $high_15 = $high_15 >> 15;
  909. $char = $low_15 | $high_15;
  910. }
  911. $password = 0x0000;
  912. foreach ($chars as $char) {
  913. $password ^= $char;
  914. }
  915. $password ^= $count;
  916. $password ^= 0xCE4B;
  917. return $password;
  918. }
  919. ###############################################################################
  920. ###############################################################################
  921. #
  922. # BIFF RECORDS
  923. #
  924. ###############################################################################
  925. #
  926. # write_number($row, $col, $num, $format)
  927. #
  928. # Write a double to the specified row and column (zero indexed).
  929. # An integer can be written as a double. Excel will display an
  930. # integer. $format is optional.
  931. #
  932. # Returns 0 : normal termination
  933. # -1 : insufficient number of arguments
  934. # -2 : row or column out of range
  935. #
  936. function write_number() {
  937. $_=func_get_args();
  938. # Check for a cell reference in A1 notation and substitute row and column
  939. if (preg_match('/^\D/', $_[0])) {
  940. $_ = $this->_substitute_cellref($_);
  941. }
  942. # Check the number of args
  943. if (sizeof($_) < 3) {
  944. return -1;
  945. }
  946. $record = 0x0203; # Record identifier
  947. $length = 0x000E; # Number of bytes to follow
  948. $row = $_[0]; # Zero indexed row
  949. $col = $_[1]; # Zero indexed column
  950. $num = $_[2];
  951. //!!!
  952. $xf = $this->_XF($row, $col, $_[3]); # The cell format
  953. # Check that row and col are valid and store max and min values
  954. if ($row >= $this->_xls_rowmax) { return -2; }
  955. if ($col >= $this->_xls_colmax) { return -2; }
  956. if ($row < $this->_dim_rowmin) { $this->_dim_rowmin = $row; }
  957. if ($row > $this->_dim_rowmax) { $this->_dim_rowmax = $row; }
  958. if ($col < $this->_dim_colmin) { $this->_dim_colmin = $col; }
  959. if ($col > $this->_dim_colmax) { $this->_dim_colmax = $col; }
  960. $header = pack("vv", $record, $length);
  961. $data = pack("vvv", $row, $col, $xf);
  962. $xl_double = pack("d", $num);
  963. if ($this->_byte_order) {
  964. //TODO
  965. $xl_double = strrev($xl_double);
  966. }
  967. $this->_append($header . $data . $xl_double);
  968. return 0;
  969. }
  970. ###############################################################################
  971. #
  972. # write_string ($row, $col, $string, $format)
  973. #
  974. # Write a string to the specified row and column (zero indexed).
  975. # NOTE: there is an Excel 5 defined limit of 255 characters.
  976. # $format is optional.
  977. # Returns 0 : normal termination
  978. # -1 : insufficient number of arguments
  979. # -2 : row or column out of range
  980. # -3 : long string truncated to 255 chars
  981. #
  982. function write_string() {
  983. $_=func_get_args();
  984. # Check for a cell reference in A1 notation and substitute row and column
  985. if (preg_match('/^\D/', $_[0])) {
  986. $_ = $this->_substitute_cellref($_);
  987. }
  988. # Check the number of args
  989. if (sizeof($_) < 3) {
  990. return -1;
  991. }
  992. $record = 0x0204; # Record identifier
  993. $length = 0x0008 + strlen($_[2]); # Bytes to follow
  994. $row = $_[0]; # Zero indexed row
  995. $col = $_[1]; # Zero indexed column
  996. $strlen = strlen($_[2]);
  997. $str = $_[2];
  998. $xf = $this->_XF($row, $col, $_[3]); # The cell format
  999. $str_error = 0;
  1000. # Check that row and col are valid and store max and min values
  1001. if ($row >= $this->_xls_rowmax) { return -2; }
  1002. if ($col >= $this->_xls_colmax) { return -2; }
  1003. if ($row < $this->_dim_rowmin) { $this->_dim_rowmin = $row; }
  1004. if ($row > $this->_dim_rowmax) { $this->_dim_rowmax = $row; }
  1005. if ($col < $this->_dim_colmin) { $this->_dim_colmin = $col; }
  1006. if ($col > $this->_dim_colmax) { $this->_dim_colmax = $col; }
  1007. if ($strlen > $this->_xls_strmax) { # LABEL must be < 255 chars
  1008. $str = substr($str, 0, $this->_xls_strmax);
  1009. $length = 0x0008 + $this->_xls_strmax;
  1010. $strlen = $this->_xls_strmax;
  1011. $str_error = -3;
  1012. }
  1013. $header = pack("vv", $record, $length);
  1014. $data = pack("vvvv", $row, $col, $xf, $strlen);
  1015. $this->_append($header . $data . $str);
  1016. return $str_error;
  1017. }
  1018. ###############################################################################
  1019. #
  1020. # write_blank($row, $col, $format)
  1021. #
  1022. # Write a blank cell to the specified row and column (zero indexed).
  1023. # A blank cell is used to specify formatting without adding a string
  1024. # or a number.
  1025. #
  1026. # A blank cell without a format serves no purpose. Therefore, we don't write
  1027. # a BLANK record unless a format is specified. This is mainly an optimisation
  1028. # for the write_row() and write_col() methods.
  1029. #
  1030. # Returns 0 : normal termination (including no format)
  1031. # -1 : insufficient number of arguments
  1032. # -2 : row or column out of range
  1033. #
  1034. function write_blank() {
  1035. $_=func_get_args();
  1036. # Check for a cell reference in A1 notation and substitute row and column
  1037. if (preg_match('/^\D/', $_[0])) {
  1038. $_ = $this->_substitute_cellref($_);
  1039. }
  1040. # Check the number of args
  1041. if (sizeof($_) < 2) {
  1042. return -1;
  1043. }
  1044. # Don't write a blank cell unless it has a format
  1045. if (!isset($_[2])) {
  1046. return 0;
  1047. }
  1048. $record = 0x0201; # Record identifier
  1049. $length = 0x0006; # Number of bytes to follow
  1050. $row = $_[0]; # Zero indexed row
  1051. $col = $_[1]; # Zero indexed column
  1052. $xf = $this->_XF($row, $col, $_[2]); # The cell format
  1053. # Check that row and col are valid and store max and min values
  1054. if ($row >= $this->_xls_rowmax) { return -2; }
  1055. if ($col >= $this->_xls_colmax) { return -2; }
  1056. if ($row < $this->_dim_rowmin) { $this->_dim_rowmin = $row; }
  1057. if ($row > $this->_dim_rowmax) { $this->_dim_rowmax = $row; }
  1058. if ($col < $this->_dim_colmin) { $this->_dim_colmin = $col; }
  1059. if ($col > $this->_dim_colmax) { $this->_dim_colmax = $col; }
  1060. $header = pack("vv", $record, $length);
  1061. $data = pack("vvv", $row, $col, $xf);
  1062. $this->_append($header . $data);
  1063. return 0;
  1064. }
  1065. ###############################################################################
  1066. #
  1067. # write_formula($row, $col, $formula, $format)
  1068. #
  1069. # Write a formula to the specified row and column (zero indexed).
  1070. # The textual representation of the formula is passed to the parser in
  1071. # Formula.pm which returns a packed binary string.
  1072. #
  1073. # $format is optional.
  1074. #
  1075. # Returns 0 : normal termination
  1076. # -1 : insufficient number of arguments
  1077. # -2 : row or column out of range
  1078. #
  1079. function write_formula() {
  1080. $_=func_get_args();
  1081. # Check for a cell reference in A1 notation and substitute row and column
  1082. if (preg_match('/^\D/', $_[0])) {
  1083. $_ = $this->_substitute_cellref($_);
  1084. }
  1085. # Check the number of args
  1086. if (sizeof($_) < 3) {
  1087. return -1;
  1088. }
  1089. $record = 0x0006; # Record identifier
  1090. $length=0; # Bytes to follow
  1091. $row = $_[0]; # Zero indexed row
  1092. $col = $_[1]; # Zero indexed column
  1093. $formula = $_[2]; # The formula text string
  1094. # Excel normally stores the last calculated value of the formula in $num.
  1095. # Clearly we are not in a position to calculate this a priori. Instead
  1096. # we set $num to zero and set the option flags in $grbit to ensure
  1097. # automatic calculation of the formula when the file is opened.
  1098. #
  1099. $xf = $this->_XF($row, $col, $_[3]); # The cell format
  1100. $num = 0x00; # Current value of formula
  1101. $grbit = 0x03; # Option flags
  1102. $chn = 0x0000; # Must be zero
  1103. # Check that row and col are valid and store max and min values
  1104. if ($row >= $this->_xls_rowmax) { return -2; }
  1105. if ($col >= $this->_xls_colmax) { return -2; }
  1106. if ($row < $this->_dim_rowmin) { $this->_dim_rowmin = $row; }
  1107. if ($row > $this->_dim_rowmax) { $this->_dim_rowmax = $row; }
  1108. if ($col < $this->_dim_colmin) { $this->_dim_colmin = $col; }
  1109. if ($col > $this->_dim_colmax) { $this->_dim_colmax = $col; }
  1110. # Strip the = sign at the beginning of the formula string
  1111. $formula = preg_replace('/^=/', "", $formula);
  1112. # Parse the formula using the parser in Formula.pm
  1113. $parser =& $this->_parser;
  1114. $formula = $parser->parse_formula($formula);
  1115. $formlen = strlen($formula); # Length of the binary string
  1116. $length = 0x16 + $formlen; # Length of the record data
  1117. $header = pack("vv", $record, $length);
  1118. $data = pack("vvvdvVv", $row, $col, $xf, $num,
  1119. $grbit, $chn, $formlen);
  1120. $this->_append($header . $data . $formula);
  1121. return 0;
  1122. }
  1123. ###############################################################################
  1124. #
  1125. # write_url($row, $col, $url, $string, $format)
  1126. #
  1127. # Write a hyperlink. This is comprised of two elements: the visible label and
  1128. # the invisible link. The visible label is the same as the link unless an
  1129. # alternative string is specified. The label is written using the
  1130. # write_string() method. Therefore the 255 characters string limit applies.
  1131. # $string and $format are optional and their order is interchangeable.
  1132. #
  1133. # The hyperlink can be to a http, ftp, mail, internal sheet, or external
  1134. # directory url.
  1135. #
  1136. # Returns 0 : normal termination
  1137. # -1 : insufficient number of arguments
  1138. # -2 : row or column out of range
  1139. # -3 : long string truncated to 255 chars
  1140. #
  1141. function write_url() {
  1142. $_=func_get_args();
  1143. # Check for a cell reference in A1 notation and substitute row and column
  1144. if (preg_match('/^\D/', $_[0])) {
  1145. $_ = $this->_substitute_cellref($_);
  1146. }
  1147. # Check the number of args
  1148. if (sizeof($_) < 3) {
  1149. return -1;
  1150. }
  1151. # Add start row and col to arg list
  1152. return call_user_method_array('write_url_range', $this,
  1153. array_merge(array($_[0], $_[1]), $_));
  1154. }
  1155. ###############################################################################
  1156. #
  1157. # write_url_range($row1, $col1, $row2, $col2, $url, $string, $format)
  1158. #
  1159. # This is the more general form of write_url(). It allows a hyperlink to be
  1160. # written to a range of cells. This function also decides the type of hyperlink
  1161. # to be written. These are either, Web (http, ftp, mailto), Internal
  1162. # (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1').
  1163. #
  1164. # See also write_url() above for a general description and return values.
  1165. #
  1166. function write_url_range() {
  1167. $_=func_get_args();
  1168. # Check for a cell reference in A1 notation and substitute row and column
  1169. if (preg_match('/^\D/', $_[0])) {
  1170. $_ = $this->_substitute_cellref($_);
  1171. }
  1172. # Check the number of args
  1173. if (sizeof($_) < 5) {
  1174. return -1;
  1175. }
  1176. # Reverse the order of $string and $format if necessary.
  1177. //TODO ($_[5], $_[6]) = ($_[6], $_[5]) if (ref $_[5]);
  1178. $url = $_[4];
  1179. # Check for internal/external sheet links or default to web link
  1180. if (preg_match('[^internal:]', $url)) {
  1181. return call_user_method_array('_write_url_internal', $this, $_);
  1182. }
  1183. if (preg_match('[^external:]', $url)) {
  1184. return call_user_method_array('_write_url_external', $this, $_);
  1185. }
  1186. return call_user_method_array('_write_url_web', $this, $_);
  1187. }
  1188. ###############################################################################
  1189. #
  1190. # _write_url_web($row1, $col1, $row2, $col2, $url, $string, $format)
  1191. #
  1192. # Used to write http, ftp and mailto hyperlinks.
  1193. # The link type ($options) is 0x03 is the same as absolute dir ref without
  1194. # sheet. However it is differentiated by the $unknown2 data stream.
  1195. #
  1196. # See also write_url() above for a general description and return values.
  1197. #
  1198. function _write_url_web() {
  1199. $_=func_get_args();
  1200. $record = 0x01B8; # Record identifier
  1201. $length = 0x00000; # Bytes to follow
  1202. $row1 = $_[0]; # Start row
  1203. $col1 = $_[1]; # Start column
  1204. $row2 = $_[2]; # End row
  1205. $col2 = $_[3]; # End column
  1206. $url = $_[4]; # URL string
  1207. if (isset($_[5])) {
  1208. $str = $_[5]; # Alternative label
  1209. }
  1210. $xf = $_[6] ? $_[6] : $this->_url_format; # The cell format
  1211. # Write the visible label using the write_string() method.
  1212. if(!isset($str)) {
  1213. $str = $url;
  1214. }
  1215. $str_error = $this->write_string($row1, $col1, $str, $xf);
  1216. if ($str_error == -2) {
  1217. return $str_error;
  1218. }
  1219. # Pack the undocumented parts of the hyperlink stream
  1220. $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
  1221. $unknown2 = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B");
  1222. # Pack the option flags
  1223. $options = pack("V", 0x03);
  1224. # Convert URL to a null terminated wchar string
  1225. $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
  1226. $url = $url . "\0\0\0";
  1227. # Pack the length of the URL
  1228. $url_len = pack("V", strlen($url));
  1229. # Calculate the data length
  1230. $length = 0x34 + strlen($url);
  1231. # Pack the header data
  1232. $header = pack("vv", $record, $length);
  1233. $data = pack("vvvv", $row1, $row2, $col1, $col2);
  1234. # Write the packed data
  1235. $this->_append($header.
  1236. $data.
  1237. $unknown1.
  1238. $options.
  1239. $unknown2.
  1240. $url_len.
  1241. $url);
  1242. return $str_error;
  1243. }
  1244. ###############################################################################
  1245. #
  1246. # _write_url_internal($row1, $col1, $row2, $col2, $url, $string, $format)
  1247. #
  1248. # Used to write internal reference hyperlinks such as "Sheet1!A1".
  1249. #
  1250. # See also write_url() above for a general description and return values.
  1251. #
  1252. function _write_url_internal() {
  1253. $_=func_get_args();
  1254. $record = 0x01B8; # Record identifier
  1255. $length = 0x00000; # Bytes to follow
  1256. $row1 = $_[0]; # Start row
  1257. $col1 = $_[1]; # Start column
  1258. $row2 = $_[2]; # End row
  1259. $col2 = $_[3]; # End column
  1260. $url = $_[4]; # URL string
  1261. if (isset($_[5])) {
  1262. $str = $_[5]; # Alternative label
  1263. }
  1264. $xf = $_[6] ? $_[6] : $this->_url_format; # The cell format
  1265. # Strip URL type
  1266. $url = preg_replace('s[^internal:]', '', $url);
  1267. # Write the visible label
  1268. if (!isset($str)) {
  1269. $str = $url;
  1270. }
  1271. $str_error = $this->write_string($row1, $col1, $str, $xf);
  1272. if ($str_error == -2) {
  1273. return $str_error;
  1274. }
  1275. # Pack the undocumented parts of the hyperlink stream
  1276. $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
  1277. # Pack the option flags
  1278. $options = pack("V", 0x08);
  1279. # Convert the URL type and to a null terminated wchar string
  1280. $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
  1281. $url = $url . "\0\0\0";
  1282. # Pack the length of the URL as chars (not wchars)
  1283. $url_len = pack("V", int(strlen($url)/2));
  1284. # Calculate the data length
  1285. $length = 0x24 + strlen($url);
  1286. # Pack the header data
  1287. $header = pack("vv", $record, $length);
  1288. $data = pack("vvvv", $row1, $row2, $col1, $col2);
  1289. # Write the packed data
  1290. $this->_append($header.
  1291. $data.
  1292. $unknown1.
  1293. $options.
  1294. $url_len.
  1295. $url);
  1296. return $str_error;
  1297. }
  1298. ###############################################################################
  1299. #
  1300. # _write_url_external($row1, $col1, $row2, $col2, $url, $string, $format)
  1301. #
  1302. # Write links to external directory names such as 'c:\foo.xls',
  1303. # c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'.
  1304. #
  1305. # Note: Excel writes some relative links with the $dir_long string. We ignore
  1306. # these cases for the sake of simpler code.
  1307. #
  1308. # See also write_url() above for a general description and return values.
  1309. #
  1310. function _write_url_external() {
  1311. $_=func_get_args();
  1312. # Network drives are different. We will handle them separately
  1313. # MS/Novell network drives and shares start with \\
  1314. if (preg_match('[^external:\\\\]', $_[4])) {
  1315. return call_user_method_array('_write_url_external_net', $this, $_);
  1316. }
  1317. $record = 0x01B8; # Record identifier
  1318. $length = 0x00000; # Bytes to follow
  1319. $row1 = $_[0]; # Start row
  1320. $col1 = $_[1]; # Start column
  1321. $row2 = $_[2]; # End row
  1322. $col2 = $_[3]; # End column
  1323. $url = $_[4]; # URL string
  1324. if (isset($_[5])) {
  1325. $str = $_[5]; # Alternative label
  1326. }
  1327. $xf = $_[6] ? $_[6] : $this->_url_format; # The cell format
  1328. # Strip URL type and change Unix dir separator to Dos style (if needed)
  1329. #
  1330. $url = preg_replace('[^external:]', '', $url);
  1331. $url = preg_replace('[/]', "\\", $url);
  1332. # Write the visible label
  1333. if (!isset($str)) {
  1334. $str = preg_replace('[\#]', ' - ', $url);
  1335. }
  1336. $str_error = $this->write_string($row1, $col1, $str, $xf);
  1337. if ($str_error == -2) {
  1338. return $str_error;
  1339. }
  1340. # Determine if the link is relative or absolute:
  1341. # relative if link contains no dir separator, "somefile.xls"
  1342. # relative if link starts with up-dir, "..\..\somefile.xls"
  1343. # otherwise, absolute
  1344. #
  1345. $absolute = 0x02; # Bit mask
  1346. if (!preg_match('[\\]', $url)) {
  1347. $absolute = 0x00;
  1348. }
  1349. if (preg_match('[^\.\.\\]', $url)) {
  1350. $absolute = 0x00;
  1351. }
  1352. # Determine if the link contains a sheet reference and change some of the
  1353. # parameters accordingly.
  1354. # Split the dir name and sheet name (if it exists)
  1355. #
  1356. list($dir_long, $sheet) = preg_split('/\#/', $url);
  1357. $link_type = 0x01 | $absolute;
  1358. //!!!
  1359. if (isset($sheet)) {
  1360. $link_type |= 0x08;
  1361. $sheet_len = pack("V", length($sheet) + 0x01);
  1362. $sheet = join("\0", split('', $sheet));
  1363. $sheet .= "\0\0\0";
  1364. } else {
  1365. $sheet_len = '';
  1366. $sheet = '';
  1367. }
  1368. # Pack the link type
  1369. $link_type = pack("V", $link_type);
  1370. # Calculate the up-level dir count e.g.. (..\..\..\ == 3)
  1371. /* TODO
  1372. $up_count = 0;
  1373. $up_count++ while $dir_long =~ s[^\.\.\\][];
  1374. $up_count = pack("v", $up_count);
  1375. */
  1376. # Store the short dos dir name (null terminated)
  1377. $dir_short = $dir_long . "\0";
  1378. # Store the long dir name as a wchar string (non-null terminated)
  1379. $dir_long = join("\0", preg_split('', $dir_long, -1, PREG_SPLIT_NO_EMPTY));
  1380. $dir_long = $dir_long . "\0";
  1381. # Pack the lengths of the dir strings
  1382. $dir_short_len = pack("V", strlen($dir_short) );
  1383. $dir_long_len = pack("V", strlen($dir_long) );
  1384. $stream_len = pack("V", strlen($dir_long) + 0x06);
  1385. # Pack the undocumented parts of the hyperlink stream
  1386. $unknown1 =pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000' );
  1387. $unknown2 =pack("H*",'0303000000000000C000000000000046' );
  1388. $unknown3 =pack("H*",'FFFFADDE000000000000000000000000000000000000000');
  1389. $unknown4 =pack("v", 0x03 );
  1390. # Pack the main data stream
  1391. $data = pack("vvvv", $row1, $row2, $col1, $col2) .
  1392. $unknown1 .
  1393. $link_type .
  1394. $unknown2 .
  1395. $up_count .
  1396. $dir_short_len.
  1397. $dir_short .
  1398. $unknown3 .
  1399. $stream_len .
  1400. $dir_long_len .
  1401. $unknown4 .
  1402. $dir_long .
  1403. $sheet_len .
  1404. $sheet ;
  1405. # Pack the header data
  1406. $length = strlen($data);
  1407. $header = pack("vv", $record, $length);
  1408. # Write the packed data
  1409. $this->_append($header . $data);
  1410. return $str_error;
  1411. }
  1412. ###############################################################################
  1413. #
  1414. # write_url_xxx($row1, $col1, $row2, $col2, $url, $string, $format)
  1415. #
  1416. # Write links to external MS/Novell network drives and shares such as
  1417. # '//NETWORK/share/foo.xls' and '//NETWORK/share/foo.xls#Sheet1!A1'.
  1418. #
  1419. # See also write_url() above for a general description and return values.
  1420. #
  1421. function _write_url_external_net() {
  1422. $_=func_get_args();
  1423. $record = 0x01B8; # Record identifier
  1424. $length = 0x00000; # Bytes to follow
  1425. $row1 = $_[0]; # Start row
  1426. $col1 = $_[1]; # Start column
  1427. $row2 = $_[2]; # End row
  1428. $col2 = $_[3]; # End column
  1429. $url = $_[4]; # URL string
  1430. if(isset($_[5])) {
  1431. $str = $_[5]; # Alternative label
  1432. }
  1433. $xf = $_[6] ? $_[6] : $this->_url_format; # The cell format
  1434. # Strip URL type and change Unix dir separator to Dos style (if needed)
  1435. #
  1436. $url = preg_replace('[^external:]', "", $url);
  1437. $url = preg_replace('[/]', "\\");
  1438. # Write the visible label
  1439. if (!isset($str)) {
  1440. $str = preg_replace('[\#]', " - ", $url);
  1441. }
  1442. $str_error = $this->write_string($row1, $col1, $str, $xf);
  1443. if ($str_error == -2) {
  1444. return $str_error;
  1445. }
  1446. # Determine if the link contains a sheet reference and change some of the
  1447. # parameters accordingly.
  1448. # Split the dir name and sheet name (if it exists)
  1449. #
  1450. list($dir_long , $sheet) = preg_split('\#', $url);
  1451. $link_type = 0x0103; # Always absolute
  1452. //!!!
  1453. if (isset($sheet)) {
  1454. $link_type |= 0x08;
  1455. $sheet_len = pack("V", strlen($sheet) + 0x01);
  1456. $sheet = join("\0", preg_split("''", $sheet, -1, PREG_SPLIT_NO_EMPTY));
  1457. $sheet .= "\0\0\0";
  1458. } else {
  1459. $sheet_len = '';
  1460. $sheet = '';
  1461. }
  1462. # Pack the link type
  1463. $link_type = pack("V", $link_type);
  1464. # Make the string null terminated
  1465. $dir_long = $dir_long . "\0";
  1466. # Pack the lengths of the dir string
  1467. $dir_long_len = pack("V", strlen($dir_long));
  1468. # Store the long dir name as a wchar string (non-null terminated)
  1469. $dir_long = join("\0", preg_split("''", $dir_long, -1, PREG_SPLIT_NO_EMPTY));
  1470. $dir_long = $dir_long . "\0";
  1471. # Pack the undocumented part of the hyperlink stream
  1472. $unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000');
  1473. # Pack the main data stream
  1474. $data = pack("vvvv", $row1, $row2, $col1, $col2) .
  1475. $unknown1 .
  1476. $link_type .
  1477. $dir_long_len .
  1478. $dir_long .
  1479. $sheet_len .
  1480. $sheet ;
  1481. # Pack the header data
  1482. $length = strlen($data);
  1483. $header = pack("vv", $record, $length);
  1484. # Write the packed data
  1485. $this->_append($header . $data);
  1486. return $str_error;
  1487. }
  1488. ###############################################################################
  1489. #
  1490. # set_row($row, $height, $XF)
  1491. #
  1492. # This method is used to set the height and XF format for a row.
  1493. # Writes the BIFF record ROW.
  1494. #
  1495. function set_row() {
  1496. $_=func_get_args();
  1497. $record = 0x0208; # Record identifier
  1498. $length = 0x0010; # Number of bytes to follow
  1499. $rw = $_[0]; # Row Number
  1500. $colMic = 0x0000; # First defined column
  1501. $colMac = 0x0000; # Last defined column
  1502. //$miyRw; # Row height
  1503. $irwMac = 0x0000; # Used by Excel to optimise loading
  1504. $reserved = 0x0000; # Reserved
  1505. $grbit = 0x01C0; # Option flags. (monkey) see $1 do
  1506. //$ixfe; # XF index
  1507. if (isset($_[2])) {
  1508. $format = $_[2]; # Format object
  1509. }
  1510. # Check for a format object
  1511. if (isset($_[2])) {
  1512. $ixfe = $format->get_xf_index();
  1513. } else {
  1514. $ixfe = 0x0F;
  1515. }
  1516. # Use set_row($row, undef, $XF) to set XF without setting height
  1517. if (isset($_[1])) {
  1518. $miyRw = $_[1] *20;
  1519. } else {
  1520. $miyRw = 0xff;
  1521. }
  1522. $header = pack("vv", $record, $length);
  1523. $data = pack("vvvvvvvv", $rw, $colMic, $colMac, $miyRw,
  1524. $irwMac,$reserved, $grbit, $ixfe);
  1525. $this->_append($header . $data);
  1526. # Store the row sizes for use when calculating image vertices.
  1527. # Also store the column formats.
  1528. #
  1529. # Ensure at least $row and $height
  1530. if (sizeof($_) < 2) {
  1531. return;
  1532. }
  1533. $this->_row_sizes[$_[0]] = $_[1];
  1534. if (isset($_[2])) {
  1535. $this->_row_formats[$_[0]] = $_[2];
  1536. }
  1537. }
  1538. /*
  1539. * Writes Excel DIMENSIONS to define the area in which there is data.
  1540. */
  1541. function _store_dimensions() {
  1542. $record = 0x0000; // Record identifier
  1543. $length = 0x000A; // Number of bytes to follow
  1544. $row_min = $this->_dim_rowmin; // First row
  1545. $row_max = $this->_dim_rowmax; // Last row plus 1
  1546. $col_min = $this->_dim_colmin; // First column
  1547. $col_max = $this->_dim_colmax; // Last column plus 1
  1548. $reserved = 0x0000; // Reserved by Excel
  1549. $header = pack("vv", $record, $length);
  1550. $data = pack("vvvvv", $row_min, $row_max,
  1551. $col_min, $col_max, $reserved);
  1552. $this->_prepend($header . $data);
  1553. }
  1554. /*
  1555. * Write BIFF record Window2.
  1556. */
  1557. function _store_window2() {
  1558. $record = 0x023E; // Record identifier
  1559. $length = 0x000A; // Number of bytes to follow
  1560. $grbit = 0x00B6; // Option flags
  1561. $rwTop = 0x0000; // Top row visible in window
  1562. $colLeft = 0x0000; // Leftmost column visible in window
  1563. $rgbHdr = 0x00000000; // Row/column heading and gridline
  1564. // color
  1565. // The options flags that comprise $grbit
  1566. $fDspFmla = 0; // 0 - bit
  1567. $fDspGrid = $this->_screen_gridlines; // 1
  1568. $fDspRwCol = 1; // 2
  1569. $fFrozen = $this->_frozen; // 3
  1570. $fDspZeros = 1; // 4
  1571. $fDefaultHdr = 1; // 5
  1572. $fArabic = 0; // 6
  1573. $fDspGuts = 1; // 7
  1574. $fFrozenNoSplit = 0; // 0 - bit
  1575. $fSelected = $this->_selected; // 1
  1576. $fPaged = 1; // 2
  1577. $grbit = $fDspFmla;
  1578. $grbit |= $fDspGrid << 1;
  1579. $grbit |= $fDspRwCol << 2;
  1580. $grbit |= $fFrozen << 3;
  1581. $grbit |= $fDspZeros << 4;
  1582. $grbit |= $fDefaultHdr << 5;
  1583. $grbit |= $fArabic << 6;
  1584. $grbit |= $fDspGuts << 7;
  1585. $grbit |= $fFrozenNoSplit << 8;
  1586. $grbit |= $fSelected << 9;
  1587. $grbit |= $fPaged << 10;
  1588. $header = pack("vv", $record, $length);
  1589. $data = pack("vvvV", $grbit, $rwTop, $colLeft, $rgbHdr);
  1590. $this->_append($header . $data);
  1591. }
  1592. /*
  1593. * Write BIFF record DEFCOLWIDTH if COLINFO records are in use.
  1594. */
  1595. function _store_defcol() {
  1596. $record = 0x0055; // Record identifier
  1597. $length = 0x0002; // Number of bytes to follow
  1598. $colwidth = 0x0008; // Default column width
  1599. $header = pack("vv", $record, $length);
  1600. $data = pack("v", $colwidth);
  1601. $this->_prepend($header . $data);
  1602. }
  1603. ###############################################################################
  1604. #
  1605. # _store_colinfo($firstcol, $lastcol, $width, $format, $hidden)
  1606. #
  1607. # Write BIFF record COLINFO to define column widths
  1608. #
  1609. # Note: The SDK says the record length is 0x0B but Excel writes a 0x0C
  1610. # length record.
  1611. #
  1612. function _store_colinfo($_) {
  1613. $record = 0x007D; # Record identifier
  1614. $length = 0x000B; # Number of bytes to follow
  1615. $colFirst = $_[0] ? $_[0] : 0; # First formatted column
  1616. $colLast = $_[1] ? $_[1] : 0; # Last formatted column
  1617. $coldx = $_[2] ? $_[2] : 8.43; # Col width, 8.43 is Excel default
  1618. $coldx += 0.72; # Fudge. Excel subtracts 0.72 !?
  1619. $coldx *= 256; # Convert to units of 1/256 of a char
  1620. //$ixfe; # XF index
  1621. $grbit = $_[4] || 0; # Option flags
  1622. $reserved = 0x00; # Reserved
  1623. $format = $_[3]; # Format object
  1624. # Check for a format object
  1625. if (isset($_[3])) {
  1626. $ixfe = $format->get_xf_index();
  1627. } else {
  1628. $ixfe = 0x0F;
  1629. }
  1630. $header = pack("vv", $record, $length);
  1631. $data = pack("vvvvvC", $colFirst, $colLast, $coldx,
  1632. $ixfe, $grbit, $reserved);
  1633. $this->_prepend($header . $data);
  1634. }
  1635. ###############################################################################
  1636. #
  1637. # _store_selection($first_row, $first_col, $last_row, $last_col)
  1638. #
  1639. # Write BIFF record SELECTION.
  1640. #
  1641. function _store_selection($_) {
  1642. $record = 0x001D; # Record identifier
  1643. $length = 0x000F; # Number of bytes to follow
  1644. $pnn = $this->_active_pane; # Pane position
  1645. $rwAct = $_[0]; # Active row
  1646. $colAct = $_[1]; # Active column
  1647. $irefAct = 0; # Active cell ref
  1648. $cref = 1; # Number of refs
  1649. $rwFirst = $_[0]; # First row in reference
  1650. $colFirst = $_[1]; # First col in reference
  1651. $rwLast = $_[2] ? $_[2] : $rwFirst; # Last row in reference
  1652. $colLast = $_[3] ? $_[3] : $colFirst; # Last col in reference
  1653. # Swap last row/col for first row/col as necessary
  1654. if ($rwFirst > $rwLast) {
  1655. list($rwFirst, $rwLast) = array($rwLast, $rwFirst);
  1656. }
  1657. if ($colFirst > $colLast) {
  1658. list($colFirst, $colLast) = array($colLast, $colFirst);
  1659. }
  1660. $header = pack("vv", $record, $length);
  1661. $data = pack("CvvvvvvCC", $pnn, $rwAct, $colAct,
  1662. $irefAct, $cref,
  1663. $rwFirst, $rwLast,
  1664. $colFirst, $colLast);
  1665. $this->_append($header . $data);
  1666. }
  1667. /*
  1668. * Write BIFF record EXTERNCOUNT to indicate the number of external
  1669. * sheet references in a worksheet.
  1670. *
  1671. * Excel only stores references to external sheets that are used in
  1672. * formulas. For simplicity we store references to all the sheets in
  1673. * the workbook regardless of whether they are used or not. This reduces
  1674. * the overall complexity and eliminates the need for a two way dialogue
  1675. * between the formula parser the worksheet objects.
  1676. */
  1677. function _store_externcount($cxals) {
  1678. // $cxals Number of external references
  1679. $record = 0x0016; // Record identifier
  1680. $length = 0x0002; // Number of bytes to follow
  1681. $header = pack("vv", $record, $length);
  1682. $data = pack("v", $cxals);
  1683. $this->_prepend($header . $data);
  1684. }
  1685. /*
  1686. * Writes the Excel BIFF EXTERNSHEET record. These references are used
  1687. * by formulas. A formula references a sheet name via an index. Since we
  1688. * store a reference to all of the external worksheets the EXTERNSHEET
  1689. * index is the same as the worksheet index.
  1690. */
  1691. function _store_externsheet($sheetname) {
  1692. $record = 0x0017; # Record identifier
  1693. // $length Number of bytes to follow
  1694. // $cch Length of sheet name
  1695. // $rgch Filename encoding
  1696. // References to the current sheet are encoded differently to
  1697. // references to external sheets.
  1698. if ($this->_name == $sheetname) {
  1699. $sheetname = '';
  1700. $length = 0x02; // The following 2 bytes
  1701. $cch = 1; // The following byte
  1702. $rgch = 0x02; // Self reference
  1703. } else {
  1704. $length = 0x02 + strlen($sheetname);
  1705. $cch = strlen($sheetname);
  1706. $rgch = 0x03; // Reference to a sheet in the current
  1707. // workbook
  1708. }
  1709. $header = pack("vv", $record, $length);
  1710. $data = pack("CC", $cch, $rgch);
  1711. $this->_prepend($header . $data . $sheetname);
  1712. }
  1713. ###############################################################################
  1714. #
  1715. # _store_panes()
  1716. #
  1717. #
  1718. # Writes the Excel BIFF PANE record.
  1719. # The panes can either be frozen or thawed (unfrozen).
  1720. # Frozen panes are specified in terms of a integer number of rows and columns.
  1721. # Thawed panes are specified in terms of Excel's units for rows and columns.
  1722. #
  1723. function _store_panes() {
  1724. $_=func_get_args();
  1725. $record = 0x0041; # Record identifier
  1726. $length = 0x000A; # Number of bytes to follow
  1727. $y = $_[0] || 0; # Vertical split position
  1728. $x = $_[1] || 0; # Horizontal split position
  1729. if (isset($_[2])) {
  1730. $rwTop = $_[2]; # Top row visible
  1731. }
  1732. if (isset($_[3])) {
  1733. $colLeft = $_[3]; # Leftmost column visible
  1734. }
  1735. if (isset($_[4])) {
  1736. $pnnAct = $_[4]; # Active pane
  1737. }
  1738. # Code specific to frozen or thawed panes.
  1739. if ($this->_frozen) {
  1740. # Set default values for $rwTop and $colLeft
  1741. if (!isset($rwTop)) {
  1742. $rwTop = $y;
  1743. }
  1744. if (!isset($colLeft)) {
  1745. $colLeft = $x;
  1746. }
  1747. } else {
  1748. # Set default values for $rwTop and $colLeft
  1749. if (!isset($rwTop)) {
  1750. $rwTop = 0;
  1751. }
  1752. if (!isset($colLeft)) {
  1753. $colLeft = 0;
  1754. }
  1755. # Convert Excel's row and column units to the internal units.
  1756. # The default row height is 12.75
  1757. # The default column width is 8.43
  1758. # The following slope and intersection values were interpolated.
  1759. #
  1760. $y = 20*$y + 255;
  1761. $x = 113.879*$x + 390;
  1762. }
  1763. # Determine which pane should be active. There is also the undocumented
  1764. # option to override this should it be necessary: may be removed later.
  1765. #
  1766. if (!isset($pnnAct)) {
  1767. # Bottom right
  1768. if ($x != 0 && $y != 0) {
  1769. $pnnAct = 0;
  1770. }
  1771. # Top right
  1772. if ($x != 0 && $y == 0) {
  1773. $pnnAct = 1;
  1774. }
  1775. # Bottom left
  1776. if ($x == 0 && $y != 0) {
  1777. $pnnAct = 2;
  1778. }
  1779. # Top left
  1780. if ($x == 0 && $y == 0) {
  1781. $pnnAct = 3;
  1782. }
  1783. }
  1784. $this->_active_pane = $pnnAct; # Used in _store_selection
  1785. $header = pack("vv", $record, $length);
  1786. $data = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct);
  1787. $this->_append($header . $data);
  1788. }
  1789. /*
  1790. * Store the page setup SETUP BIFF record.
  1791. */
  1792. function _store_setup() {
  1793. $record = 0x00A1; // Record identifier
  1794. $length = 0x0022; // Number of bytes to follow
  1795. $iPaperSize = $this->_paper_size; // Paper size
  1796. $iScale = $this->_print_scale; // Print scaling factor
  1797. $iPageStart = 0x01; // Starting page number
  1798. $iFitWidth = $this->_fit_width; // Fit to number of pages wide
  1799. $iFitHeight = $this->_fit_height; // Fit to number of pages high
  1800. $grbit = 0x00; // Option flags
  1801. $iRes = 0x0258; // Print resolution
  1802. $iVRes = 0x0258; // Vertical print resolution
  1803. $numHdr = $this->_margin_head; // Header Margin
  1804. $numFtr = $this->_margin_foot; // Footer Margin
  1805. $iCopies = 0x01; // Number of copies
  1806. $fLeftToRight = 0x0; // Print over then down
  1807. $fLandscape = $this->_orientation; // Page orientation
  1808. $fNoPls = 0x0; // Setup not read from printer
  1809. $fNoColor = 0x0; // Print black and white
  1810. $fDraft = 0x0; // Print draft quality
  1811. $fNotes = 0x0; // Print notes
  1812. $fNoOrient = 0x0; // Orientation not set
  1813. $fUsePage = 0x0; // Use custom starting page
  1814. $grbit = $fLeftToRight;
  1815. $grbit |= $fLandscape << 1;
  1816. $grbit |= $fNoPls << 2;
  1817. $grbit |= $fNoColor << 3;
  1818. $grbit |= $fDraft << 4;
  1819. $grbit |= $fNotes << 5;
  1820. $grbit |= $fNoOrient << 6;
  1821. $grbit |= $fUsePage << 7;
  1822. $numHdr = pack("d", $numHdr);
  1823. $numFtr = pack("d", $numFtr);
  1824. if ($this->_byte_order) {
  1825. $numHdr = strrev($numHdr);
  1826. $numFtr = strrev($numFtr);
  1827. }
  1828. $header = pack("vv", $record, $length);
  1829. $data1 = pack("vvvvvvvv", $iPaperSize,
  1830. $iScale,
  1831. $iPageStart,
  1832. $iFitWidth,
  1833. $iFitHeight,
  1834. $grbit,
  1835. $iRes,
  1836. $iVRes);
  1837. $data2 = $numHdr . $numFtr;
  1838. $data3 = pack("v", $iCopies);
  1839. $this->_prepend($header . $data1 . $data2 . $data3);
  1840. }
  1841. /*
  1842. * Store the header caption BIFF record.
  1843. */
  1844. function _store_header() {
  1845. $record = 0x0014; // Record identifier
  1846. $str = $this->_header; // header string
  1847. $cch = strlen($str); // Length of header string
  1848. $length = 1 + $cch; // Bytes to follow
  1849. $header = pack("vv", $record, $length);
  1850. $data = pack("C", $cch);
  1851. $this->_append($header . $data . $str);
  1852. }
  1853. /*
  1854. * Store the footer caption BIFF record.
  1855. */
  1856. function _store_footer() {
  1857. $record = 0x0015; // Record identifier
  1858. $str = $this->_footer; // Footer string
  1859. $cch = strlen($str); // Length of footer string
  1860. $length = 1 + $cch; // Bytes to follow
  1861. $header = pack("vv", $record, $length);
  1862. $data = pack("C", $cch);
  1863. $this->_append($header . $data . $str);
  1864. }
  1865. /*
  1866. * Store the horizontal centering HCENTER BIFF record.
  1867. */
  1868. function _store_hcenter() {
  1869. $record = 0x0083; // Record identifier
  1870. $length = 0x0002; // Bytes to follow
  1871. $fHCenter = $this->_hcenter; // Horizontal centering
  1872. $header = pack("vv", $record, $length);
  1873. $data = pack("v", $fHCenter);
  1874. $this->_append($header . $data);
  1875. }
  1876. /*
  1877. * Store the vertical centering VCENTER BIFF record.
  1878. */
  1879. function _store_vcenter() {
  1880. $record = 0x0084; // Record identifier
  1881. $length = 0x0002; // Bytes to follow
  1882. $fVCenter = $this->_vcenter; // Horizontal centering
  1883. $header = pack("vv", $record, $length);
  1884. $data = pack("v", $fVCenter);
  1885. $this->_append($header . $data);
  1886. }
  1887. /*
  1888. * Store the LEFTMARGIN BIFF record.
  1889. */
  1890. function _store_margin_left() {
  1891. $record = 0x0026; // Record identifier
  1892. $length = 0x0008; // Bytes to follow
  1893. $margin = $this->_margin_left; // Margin in inches
  1894. $header = pack("vv", $record, $length);
  1895. $data = pack("d", $margin);
  1896. if ($this->_byte_order) {
  1897. $data = strrev($data);
  1898. }
  1899. $this->_append($header . $data);
  1900. }
  1901. /*
  1902. * Store the RIGHTMARGIN BIFF record.
  1903. */
  1904. function _store_margin_right() {
  1905. $record = 0x0027; // Record identifier
  1906. $length = 0x0008; // Bytes to follow
  1907. $margin = $this->_margin_right; // Margin in inches
  1908. $header = pack("vv", $record, $length);
  1909. $data = pack("d", $margin);
  1910. if ($this->_byte_order) {
  1911. $data = strrev($data);
  1912. }
  1913. $this->_append($header . $data);
  1914. }
  1915. /*
  1916. * Store the TOPMARGIN BIFF record.
  1917. */
  1918. function _store_margin_top() {
  1919. $record = 0x0028; // Record identifier
  1920. $length = 0x0008; // Bytes to follow
  1921. $margin = $this->_margin_top; // Margin in inches
  1922. $header = pack("vv", $record, $length);
  1923. $data = pack("d", $margin);
  1924. if ($this->_byte_order) {
  1925. $data = strrev($data);
  1926. }
  1927. $this->_append($header . $data);
  1928. }
  1929. /*
  1930. * Store the BOTTOMMARGIN BIFF record.
  1931. */
  1932. function _store_margin_bottom() {
  1933. $record = 0x0029; // Record identifier
  1934. $length = 0x0008; // Bytes to follow
  1935. $margin = $this->_margin_bottom; // Margin in inches
  1936. $header = pack("vv", $record, $length);
  1937. $data = pack("d", $margin);
  1938. if ($this->_byte_order) {
  1939. $data = strrev($data);
  1940. }
  1941. $this->_append($header . $data);
  1942. }
  1943. ###############################################################################
  1944. #
  1945. # merge_cells($first_row, $first_col, $last_row, $last_col)
  1946. #
  1947. # This is an Excel97/2000 method. It is required to perform more complicated
  1948. # merging than the normal align merge in Format.pm
  1949. #
  1950. function merge_cells() {
  1951. $_=func_get_args();
  1952. // Check for a cell reference in A1 notation and substitute row and column
  1953. if (preg_match('/^\D/', $_[0])) {
  1954. $_ = $this->_substitute_cellref($_);
  1955. }
  1956. $record = 0x00E5; # Record identifier
  1957. $length = 0x000A; # Bytes to follow
  1958. $cref = 1; # Number of refs
  1959. $rwFirst = $_[0]; # First row in reference
  1960. $colFirst = $_[1]; # First col in reference
  1961. $rwLast = $_[2] || $rwFirst; # Last row in reference
  1962. $colLast = $_[3] || $colFirst; # Last col in reference
  1963. // Swap last row/col for first row/col as necessary
  1964. if ($rwFirst > $rwLast) {
  1965. list($rwFirst, $rwLast) = array($rwLast, $rwFirst);
  1966. }
  1967. if ($colFirst > $colLast) {
  1968. list($colFirst, $colLast) = array($colLast, $colFirst);
  1969. }
  1970. $header = pack("vv", $record, $length);
  1971. $data = pack("vvvvv", $cref,
  1972. $rwFirst, $rwLast,
  1973. $colFirst, $colLast);
  1974. $this->_append($header . $data);
  1975. }
  1976. /*
  1977. * Write the PRINTHEADERS BIFF record.
  1978. */
  1979. function _store_print_headers() {
  1980. $record = 0x002a; // Record identifier
  1981. $length = 0x0002; // Bytes to follow
  1982. $fPrintRwCol = $this->_print_headers; // Boolean flag
  1983. $header = pack("vv", $record, $length);
  1984. $data = pack("v", $fPrintRwCol);
  1985. $this->_prepend($header . $data);
  1986. }
  1987. /*
  1988. * Write the PRINTGRIDLINES BIFF record. Must be used in conjunction
  1989. * with the GRIDSET record.
  1990. */
  1991. function _store_print_gridlines() {
  1992. $record = 0x002b; // Record identifier
  1993. $length = 0x0002; // Bytes to follow
  1994. $fPrintGrid = $this->_print_gridlines; // Boolean flag
  1995. $header = pack("vv", $record, $length);
  1996. $data = pack("v", $fPrintGrid);
  1997. $this->_prepend($header . $data);
  1998. }
  1999. /*
  2000. * Write the GRIDSET BIFF record. Must be used in conjunction with the
  2001. * PRINTGRIDLINES record.
  2002. */
  2003. function _store_gridset() {
  2004. $record = 0x0082; // Record identifier
  2005. $length = 0x0002; // Bytes to follow
  2006. $fGridSet = !$this->_print_gridlines; // Boolean flag
  2007. $header = pack("vv", $record, $length);
  2008. $data = pack("v", $fGridSet);
  2009. $this->_prepend($header . $data);
  2010. }
  2011. /*
  2012. * Write the WSBOOL BIFF record, mainly for fit-to-page. Used in
  2013. * conjunction with the SETUP record.
  2014. */
  2015. function _store_wsbool() {
  2016. $record = 0x0081; # Record identifier
  2017. $length = 0x0002; # Bytes to follow
  2018. // $grbit Option flags
  2019. // The only option that is of interest is the flag for fit to page.
  2020. // So we set all the options in one go.
  2021. if ($this->_fit_page) {
  2022. $grbit = 0x05c1;
  2023. } else {
  2024. $grbit = 0x04c1;
  2025. }
  2026. $header = pack("vv", $record, $length);
  2027. $data = pack("v", $grbit);
  2028. $this->_prepend($header . $data);
  2029. }
  2030. /*
  2031. * Write the HORIZONTALPAGEBREAKS BIFF record.
  2032. */
  2033. function _store_hbreak() {
  2034. // Return if the user hasn't specified pagebreaks
  2035. if(sizeof($this->_hbreaks)==0) {
  2036. return;
  2037. }
  2038. # Sort and filter array of page breaks
  2039. $breaks = $this->_sort_pagebreaks($this->_hbreaks);
  2040. $record = 0x001b; // Record identifier
  2041. $cbrk = sizeof($breaks); // Number of page breaks
  2042. $length = ($cbrk + 1) * 2; // Bytes to follow
  2043. $header = pack("vv", $record, $length);
  2044. $data = pack("v", $cbrk);
  2045. // Append each page break
  2046. foreach ($breaks as $break) {
  2047. $data .= pack("v", $break);
  2048. }
  2049. $this->_prepend($header . $data);
  2050. }
  2051. /*
  2052. * Write the VERTICALPAGEBREAKS BIFF record.
  2053. */
  2054. function _store_vbreak() {
  2055. // Return if the user hasn't specified pagebreaks
  2056. if(sizeof($this->_vbreaks)==0) {
  2057. return;
  2058. }
  2059. // Sort and filter array of page breaks
  2060. $breaks = $this->_sort_pagebreaks($this->_vbreaks);
  2061. $record = 0x001a; // Record identifier
  2062. $cbrk = sizeof($breaks); // Number of page breaks
  2063. $length = ($cbrk + 1) * 2; // Bytes to follow
  2064. $header = pack("vv", $record, $length);
  2065. $data = pack("v", $cbrk);
  2066. // Append each page break
  2067. foreach ($breaks as $break) {
  2068. $data .= pack("v", $break);
  2069. }
  2070. $this->_prepend($header . $data);
  2071. }
  2072. /*
  2073. * Set the Biff PROTECT record to indicate that the worksheet is
  2074. * protected.
  2075. */
  2076. function _store_protect() {
  2077. // Exit unless sheet protection has been specified
  2078. if (!$this->_protect) {
  2079. return;
  2080. }
  2081. $record = 0x0012; // Record identifier
  2082. $length = 0x0002; // Bytes to follow
  2083. $fLock = $this->_protect; // Worksheet is protected
  2084. $header = pack("vv", $record, $length);
  2085. $data = pack("v", $fLock);
  2086. $this->_prepend($header . $data);
  2087. }
  2088. /*
  2089. * Write the worksheet PASSWORD record.
  2090. */
  2091. function _store_password() {
  2092. // Exit unless sheet protection and password have been specified
  2093. if (!$this->_protect || !$this->_password) {
  2094. return;
  2095. }
  2096. $record = 0x0013; // Record identifier
  2097. $length = 0x0002; // Bytes to follow
  2098. $wPassword = $this->_password; // Encoded password
  2099. $header = pack("vv", $record, $length);
  2100. $data = pack("v", $wPassword);
  2101. $this->_prepend($header . $data);
  2102. }
  2103. ###############################################################################
  2104. #
  2105. # insert_bitmap($row, $col, $filename, $x, $y, $scale_x, $scale_y)
  2106. #
  2107. # Insert a 24bit bitmap image in a worksheet. The main record required is
  2108. # IMDATA but it must be proceeded by a OBJ record to define its position.
  2109. #
  2110. function insert_bitmap() {
  2111. $_=func_get_args();
  2112. # Check for a cell reference in A1 notation and substitute row and column
  2113. if (preg_match('/^\D/', $_[0])) {
  2114. $_ = $this->_substitute_cellref($_);
  2115. }
  2116. $row = $_[0];
  2117. $col = $_[1];
  2118. $bitmap = $_[2];
  2119. $x = $_[3] ? $_[3] : 0;
  2120. $y = $_[4] ? $_[4] : 0;
  2121. $scale_x = $_[5] ? $_[5] : 1;
  2122. $scale_y = $_[6] ? $_[6] : 1;
  2123. list($width, $height, $size, $data) = $this->_process_bitmap($bitmap);
  2124. # Scale the frame of the image.
  2125. $width *= $scale_x;
  2126. $height *= $scale_y;
  2127. # Calculate the vertices of the image and write the OBJ record
  2128. $this->_position_image($col, $row, $x, $y, $width, $height);
  2129. # Write the IMDATA record to store the bitmap data
  2130. $record = 0x007f;
  2131. $length = 8 + $size;
  2132. $cf = 0x09;
  2133. $env = 0x01;
  2134. $lcb = $size;
  2135. $header = pack("vvvvV", $record, $length, $cf, $env, $lcb);
  2136. $this->_append($header . $data);
  2137. }
  2138. /*
  2139. * Calculate the vertices that define the position of the image as
  2140. * required by the OBJ record.
  2141. *
  2142. * +------------+------------+
  2143. * | A | B |
  2144. * +-----+------------+------------+
  2145. * | |(x1,y1) | |
  2146. * | 1 |(A1)._______|______ |
  2147. * | | | | |
  2148. * | | | | |
  2149. * +-----+----| BITMAP |-----+
  2150. * | | | | |
  2151. * | 2 | |______________. |
  2152. * | | | (B2)|
  2153. * | | | (x2,y2)|
  2154. * +---- +------------+------------+
  2155. *
  2156. * Example of a bitmap that covers some of the area from cell A1 to
  2157. * cell B2.
  2158. *
  2159. * Based on the width and height of the bitmap we need to calculate 8
  2160. *vars:
  2161. * $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
  2162. * The width and height of the cells are also variable and have to be
  2163. * taken into account.
  2164. * The values of $col_start and $row_start are passed in from the calling
  2165. * function. The values of $col_end and $row_end are calculated by
  2166. * subtracting the width and height of the bitmap from the width and
  2167. * height of the underlying cells.
  2168. * The vertices are expressed as a percentage of the underlying cell
  2169. * width as follows (rhs values are in pixels):
  2170. *
  2171. * x1 = X / W *1024
  2172. * y1 = Y / H *256
  2173. * x2 = (X-1) / W *1024
  2174. * y2 = (Y-1) / H *256
  2175. *
  2176. * Where: X is distance from the left side of the underlying cell
  2177. * Y is distance from the top of the underlying cell
  2178. * W is the width of the cell
  2179. * H is the height of the cell
  2180. *
  2181. * Note: the SDK incorrectly states that the height should be expressed
  2182. * as a percentage of 1024.
  2183. */
  2184. function _position_image($col_start, $row_start, $x1, $y1,
  2185. $width, $height) {
  2186. // $col_start Col containing upper left corner of object
  2187. // $x1 Distance to left side of object
  2188. // $row_start Row containing top left corner of object
  2189. // $y1 Distance to top of object
  2190. // $col_end Col containing lower right corner of object
  2191. // $x2 Distance to right side of object
  2192. // $row_end Row containing bottom right corner of object
  2193. // $y2 Distance to bottom of object
  2194. // $width Width of image frame
  2195. // $height Height of image frame
  2196. // Initialise end cell to the same as the start cell
  2197. $col_end = $col_start;
  2198. $row_end = $row_start;
  2199. // Zero the specified offset if greater than the cell dimensions
  2200. if ($x1 >= $this->_size_col($col_start)) {
  2201. $x1 = 0;
  2202. }
  2203. if ($y1 >= $this->_size_row($row_start)) {
  2204. $y1 = 0;
  2205. }
  2206. $width = $width + $x1 -1;
  2207. $height = $height + $y1 -1;
  2208. // Subtract the underlying cell widths to find the end cell of the
  2209. // image
  2210. while ($width >= $this->_size_col($col_end)) {
  2211. $width -= $this->_size_col($col_end);
  2212. $col_end++;
  2213. }
  2214. // Subtract the underlying cell heights to find the end cell of the
  2215. // image
  2216. while ($height >= $this->_size_row($row_end)) {
  2217. $height -= $this->_size_row($row_end);
  2218. $row_end++;
  2219. }
  2220. // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a
  2221. // cell with zero height or width.
  2222. if ($this->_size_col($col_start) == 0) { return; }
  2223. if ($this->_size_col($col_end) == 0) { return; }
  2224. if ($this->_size_row($row_start) == 0) { return; }
  2225. if ($this->_size_row($row_end) == 0) { return; }
  2226. // Convert the pixel values to the percentage value expected by Excel
  2227. $x1 = $x1 / $this->_size_col($col_start) * 1024;
  2228. $y1 = $y1 / $this->_size_row($row_start) * 256;
  2229. $x2 = $width / $this->_size_col($col_end) * 1024;
  2230. $y2 = $height / $this->_size_row($row_end) * 256;
  2231. $this->_store_obj_picture($col_start, $x1, $row_start, $y1,
  2232. $col_end, $x2, $row_end, $y2);
  2233. }
  2234. /*
  2235. * Convert the width of a cell from user's units to pixels. By
  2236. * interpolation the relationship is: y = 7x +5. If the width
  2237. * hasn't been set by the user we use the default value. If the
  2238. * col is hidden we use a value of zero.
  2239. */
  2240. function _size_col($col) {
  2241. // Look up the cell value to see if it has been changed
  2242. if (isset($this->_col_sizes[$col])) {
  2243. if ($this->_col_sizes[$col] == 0) {
  2244. return 0;
  2245. } else {
  2246. return floor(7 * $this->_col_sizes[$col] + 5);
  2247. }
  2248. } else {
  2249. return 64;
  2250. }
  2251. }
  2252. /*
  2253. * Convert the height of a cell from user's units to pixels. By
  2254. * interpolation # the relationship is: y = 4/3x. If the height
  2255. * hasn't been set by the user we use the default value. If the
  2256. * row is hidden we use a value of zero. (Not possible to hide row
  2257. * yet).
  2258. */
  2259. function _size_row($row) {
  2260. // Look up the cell value to see if it has been changed
  2261. if (isset($this->_row_sizes[$row])) {
  2262. if ($this->_row_sizes[$row] == 0) {
  2263. return 0;
  2264. } else {
  2265. return floor(4/3 * $this->_row_sizes[$row]);
  2266. }
  2267. } else {
  2268. return 17;
  2269. }
  2270. }
  2271. /*
  2272. * Store the OBJ record that precedes an IMDATA record. This could
  2273. * be generalized to support other Excel objects.
  2274. */
  2275. function _store_obj_picture($col_start, $x1, $row_start, $y1,
  2276. $col_end, $x2, $row_end, $y2) {
  2277. $record = 0x005d; // Record identifier
  2278. $length = 0x003c; // Bytes to follow
  2279. $cObj = 0x0001; // Count of objects in file (set to 1)
  2280. $OT = 0x0008; // Object type. 8 = Picture
  2281. $id = 0x0001; // Object ID
  2282. $grbit = 0x0614; // Option flags
  2283. $colL = $col_start; // Col containing upper left corner of
  2284. // object
  2285. $dxL = $x1; // Distance from left side of cell
  2286. $rwT = $row_start; // Row containing top left corner of
  2287. // object
  2288. $dyT = $y1; // Distance from top of cell
  2289. $colR = $col_end; // Col containing lower right corner of
  2290. // object
  2291. $dxR = $x2; // Distance from right of cell
  2292. $rwB = $row_end; // Row containing bottom right corner of
  2293. // object
  2294. $dyB = $y2; // Distance from bottom of cell
  2295. $cbMacro = 0x0000; // Length of FMLA structure
  2296. $Reserved1 = 0x0000; // Reserved
  2297. $Reserved2 = 0x0000; // Reserved
  2298. $icvBack = 0x09; // Background colour
  2299. $icvFore = 0x09; // Foreground colour
  2300. $fls = 0x00; // Fill pattern
  2301. $fAuto = 0x00; // Automatic fill
  2302. $icv = 0x08; // Line colour
  2303. $lns = 0xff; // Line style
  2304. $lnw = 0x01; // Line weight
  2305. $fAutoB = 0x00; // Automatic border
  2306. $frs = 0x0000; // Frame style
  2307. $cf = 0x0009; // Image format, 9 = bitmap
  2308. $Reserved3 = 0x0000; // Reserved
  2309. $cbPictFmla = 0x0000; // Length of FMLA structure
  2310. $Reserved4 = 0x0000; // Reserved
  2311. $grbit2 = 0x0001; // Option flags
  2312. $Reserved5 = 0x0000; // Reserved
  2313. $header = pack("vv", $record, $length);
  2314. $data = pack("V", $cObj);
  2315. $data .= pack("v", $OT);
  2316. $data .= pack("v", $id);
  2317. $data .= pack("v", $grbit);
  2318. $data .= pack("v", $colL);
  2319. $data .= pack("v", $dxL);
  2320. $data .= pack("v", $rwT);
  2321. $data .= pack("v", $dyT);
  2322. $data .= pack("v", $colR);
  2323. $data .= pack("v", $dxR);
  2324. $data .= pack("v", $rwB);
  2325. $data .= pack("v", $dyB);
  2326. $data .= pack("v", $cbMacro);
  2327. $data .= pack("V", $Reserved1);
  2328. $data .= pack("v", $Reserved2);
  2329. $data .= pack("C", $icvBack);
  2330. $data .= pack("C", $icvFore);
  2331. $data .= pack("C", $fls);
  2332. $data .= pack("C", $fAuto);
  2333. $data .= pack("C", $icv);
  2334. $data .= pack("C", $lns);
  2335. $data .= pack("C", $lnw);
  2336. $data .= pack("C", $fAutoB);
  2337. $data .= pack("v", $frs);
  2338. $data .= pack("V", $cf);
  2339. $data .= pack("v", $Reserved3);
  2340. $data .= pack("v", $cbPictFmla);
  2341. $data .= pack("v", $Reserved4);
  2342. $data .= pack("v", $grbit2);
  2343. $data .= pack("V", $Reserved5);
  2344. $this->_append($header . $data);
  2345. }
  2346. /*
  2347. * Convert a 24 bit bitmap into the modified internal format used by
  2348. * Windows. This is described in BITMAPCOREHEADER and BITMAPCOREINFO
  2349. * structures in the MSDN library.
  2350. */
  2351. function _process_bitmap($bitmap) {
  2352. // Open file and binmode the data in case the platform needs it.
  2353. $bmp=fopen($bitmap, "rb");
  2354. if (!$bmp) {
  2355. trigger_error("Could not open file '$bitmap'.", E_USER_ERROR);
  2356. }
  2357. $data=fread($bmp, filesize($bitmap));
  2358. // Check that the file is big enough to be a bitmap.
  2359. if (strlen($data) <= 0x36) {
  2360. trigger_error("$bitmap doesn't contain enough data.",
  2361. E_USER_ERROR);
  2362. }
  2363. // The first 2 bytes are used to identify the bitmap.
  2364. if (substr($data, 0, 2) != "BM") {
  2365. trigger_error("$bitmap doesn't appear to to be a ".
  2366. "valid bitmap image.", E_USER_ERROR);
  2367. }
  2368. // Remove bitmap data: ID.
  2369. $data = substr($data, 2);
  2370. // Read and remove the bitmap size. This is more reliable than reading
  2371. // the data size at offset 0x22.
  2372. $array = unpack("Vsize", $data);
  2373. $data = substr($data, 4);
  2374. $size = $array["size"];
  2375. $size -= 0x36; # Subtract size of bitmap header.
  2376. $size += 0x0C; # Add size of BIFF header.
  2377. // Remove bitmap data: reserved, offset, header length.
  2378. $data = substr($data, 12);
  2379. // Read and remove the bitmap width and height. Verify the sizes.
  2380. $array = unpack("Vwidth/Vheight", $data);
  2381. $data = substr($data, 8);
  2382. $width = $array["width"];
  2383. $height = $array["height"];
  2384. if ($width > 0xFFFF) {
  2385. trigger_error("$bitmap: largest image width supported is 64k.",
  2386. E_USER_ERROR);
  2387. }
  2388. if ($height > 0xFFFF) {
  2389. trigger_error("$bitmap: largest image height supported is 64k.",
  2390. E_USER_ERROR);
  2391. }
  2392. // Read and remove the bitmap planes and bpp data. Verify them.
  2393. $array = unpack("vplanes/vbitcount", $data);
  2394. $data = substr($data, 4);
  2395. $planes = $array["planes"];
  2396. $bitcount = $array["bitcount"];
  2397. if ($bitcount != 24) {
  2398. trigger_error("$bitmap isn't a 24bit true color bitmap.",
  2399. E_USER_ERROR);
  2400. }
  2401. if ($planes != 1) {
  2402. trigger_error("$bitmap: only 1 plane supported in bitmap image.",
  2403. E_USER_ERROR);
  2404. }
  2405. // Read and remove the bitmap compression. Verify compression.
  2406. $array = unpack("Vcompression", $data);
  2407. $data = substr($data, 4);
  2408. $compression = $array["compression"];
  2409. if ($compression != 0) {
  2410. trigger_error("$bitmap: compression not supported in bitmap image.",
  2411. E_USER_ERROR);
  2412. }
  2413. // Remove bitmap data: data size, hres, vres, colours, imp. colours.
  2414. $data = substr($data, 20);
  2415. // Add the BITMAPCOREHEADER data
  2416. $header = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18);
  2417. $data = $header . $data;
  2418. return array($width, $height, $size, $data);
  2419. }
  2420. /*
  2421. * Store the window zoom factor. This should be a reduced fraction but for
  2422. * simplicity we will store all fractions with a numerator of 100.
  2423. */
  2424. function _store_zoom() {
  2425. // If scale is 100% we don't need to write a record
  2426. if ($this->_zoom == 100) {
  2427. return;
  2428. }
  2429. $record = 0x00A0; // Record identifier
  2430. $length = 0x0004; // Bytes to follow
  2431. $header = pack("vv", $record, $length);
  2432. $data = pack("vv", $this->_zoom, 100);
  2433. $this->_append($header . $data);
  2434. }
  2435. }
  2436. ?>