Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

changed the excel writing library to a more fully-featured, less buggy

one.
  • Loading branch information...
commit 275b48ac2bab58d5cab4697f8cf1e5fcc312b8e8 1 parent caf4bef
jungwirr authored
View
66 mod/attendance/viewall.php
@@ -77,40 +77,37 @@
//
//
-
if ($download == "xls") {
- require_once("../../lib/psxlsgen.php");
+ require_once("write_excel/Worksheet.php");
+ require_once("write_excel/Workbook.php");
+ // HTTP headers
+ attendance_HeaderingExcel($course->shortname."_Attendance.xls");
+ // Creating a workbook
+ $workbook = new Workbook("-");
+ // Creating the first worksheet
+ $myxls =& $workbook->add_worksheet('Grades');
-// add up the total columns that are required for the whole report
- $myxls = new PhpSimpleXlsGen();
-if ($dlsub== "all") {
- $myxls->totalcol=4+$numatt; // first,last,id ---...---> total
-} else {
- $myxls->totalcol=4; // first,last,id ---...---> total
-}
// print the date headings at the top of the table
// for each day of attendance
- $myxls->ChangePos(2,0);
- $myxls->InsertText(get_string("lastname"));
- $myxls->InsertText(get_string("firstname"));
- $myxls->InsertText(get_string("idnumber"));
+ $myxls->write_string(3,0,get_string("lastname"));
+ $myxls->write_string(3,1,get_string("firstname"));
+ $myxls->write_string(3,2,get_string("idnumber"));
$pos=3;
if ($dlsub== "all") {
for($k=0;$k<$numatt;$k++) {
// put notes for the date in the date heading
- $myxls->ChangePos(0,$pos);
- $myxls->InsertText(userdate($atts[$k]->attendance->day,"%m/%0d"));
- $myxls->ChangePos(1,$pos);
- $myxls->InsertText($atts[$k]->attendance->notes);
- $myxls->ChangePos(2,$pos);
+ $myxls->write_string(1,$pos,userdate($atts[$k]->attendance->day,"%m/%0d"));
+ $myxls->set_column($pos,$pos,5);
+ $myxls->write_string(2,$pos,$atts[$k]->attendance->notes);
for ($i=1;$i<=$atts[$k]->attendance->hours;$i++) {
- $myxls->InsertText($i);
+ $myxls->write_number(3,$pos,$i);
+ $myxls->set_column($pos,$pos,1);
$pos++;
}
}
} // if dlsub==all
- $myxls->ChangePos(2,$pos);
- $myxls->InsertText(get_string("total"));
+ $myxls->write_string(3,$pos,get_string("total"));
+ $myxls->set_column($pos,$pos,5);
/// generate the attendance rolls for the body of the spreadsheet
if (isstudent($course->id)) {
@@ -122,13 +119,13 @@
$A = get_string("absentshort","attendance");
$T = get_string("tardyshort","attendance");
$P = get_string("presentshort","attendance");
- $row=3;
+ $row=4;
foreach ($students as $student) {
- $myxls->ChangePos($row,0);
- $myxls->InsertText($student->lastname);
- $myxls->InsertText($student->firstname);
+ $myxls->write_string($row,0,$student->lastname);
+ $myxls->write_string($row,1,$student->firstname);
$studentid=(($student->idnumber != "") ? $student->idnumber : " ");
- $myxls->InsertText($studentid);
+ $myxls->write_string($row,2,$studentid);
+ $pos=3;
if ($dlsub== "all") {
for($k=0;$k<$numatt;$k++) { // for each day of attendance for the student
for($j=1;$j<=$atts[$k]->attendance->hours;$j++) {
@@ -136,7 +133,8 @@
if ($atts[$k]->sroll[$student->id][$j]->status == 1) {$status=$T;}
elseif ($atts[$k]->sroll[$student->id][$j]->status == 2) {$status=$A;}
else {$status=$P;}
- $myxls->InsertText($status);
+ $myxls->write_string($row,$pos,$status);
+ $pos++;
} /// for loop
}
}
@@ -149,14 +147,15 @@
} /// for loop
} // outer for for each day of attendance
$tot=tally_overall_absences_decimal($abs,$tar);
- $myxls->InsertNumber($tot);
+ $myxls->write_number($row,$pos,$tot);
$row++;
}
- $myxls->SendFileName($course->shortname."_Attendance");
+ $workbook->close();
exit;
}
+
if ($download == "txt") {
header("Content-Type: application/download\n");
@@ -513,4 +512,13 @@ function attendance_print_pagenav() {
echo "</tr></table></td></tr></table></center>\n";
}
}
+
+function attendance_HeaderingExcel($filename) {
+ header("Content-type: application/vnd.ms-excel");
+ header("Content-Disposition: attachment; filename=$filename" );
+ header("Expires: 0");
+ header("Cache-Control: must-revalidate, post-check=0,pre-check=0");
+ header("Pragma: public");
+}
+
?>
View
63 mod/attendance/viewweek.php
@@ -79,38 +79,36 @@
if ($download == "xls") {
- require_once("../../lib/psxlsgen.php");
+ require_once("write_excel/Worksheet.php");
+ require_once("write_excel/Workbook.php");
+ // HTTP headers
+ attendance_HeaderingExcel($course->shortname."_Attendance_Week.xls");
+ // Creating a workbook
+ $workbook = new Workbook("-");
+ // Creating the first worksheet
+ $myxls =& $workbook->add_worksheet('Grades');
-// add up the total columns that are required for the whole report
- $myxls = new PhpSimpleXlsGen();
-if ($dlsub== "all") {
- $myxls->totalcol=4+$numatt; // first,last,id ---...---> total
-} else {
- $myxls->totalcol=4; // first,last,id ---...---> total
-}
// print the date headings at the top of the table
// for each day of attendance
- $myxls->ChangePos(2,0);
- $myxls->InsertText(get_string("lastname"));
- $myxls->InsertText(get_string("firstname"));
- $myxls->InsertText(get_string("idnumber"));
+ $myxls->write_string(3,0,get_string("lastname"));
+ $myxls->write_string(3,1,get_string("firstname"));
+ $myxls->write_string(3,2,get_string("idnumber"));
$pos=3;
if ($dlsub== "all") {
for($k=0;$k<$numatt;$k++) {
// put notes for the date in the date heading
- $myxls->ChangePos(0,$pos);
- $myxls->InsertText(userdate($atts[$k]->attendance->day,"%m/%0d"));
- $myxls->ChangePos(1,$pos);
- $myxls->InsertText($atts[$k]->attendance->notes);
- $myxls->ChangePos(2,$pos);
+ $myxls->write_string(1,$pos,userdate($atts[$k]->attendance->day,"%m/%0d"));
+ $myxls->set_column($pos,$pos,5);
+ $myxls->write_string(2,$pos,$atts[$k]->attendance->notes);
for ($i=1;$i<=$atts[$k]->attendance->hours;$i++) {
- $myxls->InsertText($i);
+ $myxls->write_number(3,$pos,$i);
+ $myxls->set_column($pos,$pos,1);
$pos++;
}
}
} // if dlsub==all
- $myxls->ChangePos(2,$pos);
- $myxls->InsertText(get_string("total"));
+ $myxls->write_string(3,$pos,get_string("total"));
+ $myxls->set_column($pos,$pos,5);
/// generate the attendance rolls for the body of the spreadsheet
if (isstudent($course->id)) {
@@ -122,13 +120,13 @@
$A = get_string("absentshort","attendance");
$T = get_string("tardyshort","attendance");
$P = get_string("presentshort","attendance");
- $row=3;
+ $row=4;
foreach ($students as $student) {
- $myxls->ChangePos($row,0);
- $myxls->InsertText($student->lastname);
- $myxls->InsertText($student->firstname);
+ $myxls->write_string($row,0,$student->lastname);
+ $myxls->write_string($row,1,$student->firstname);
$studentid=(($student->idnumber != "") ? $student->idnumber : " ");
- $myxls->InsertText($studentid);
+ $myxls->write_string($row,2,$studentid);
+ $pos=3;
if ($dlsub== "all") {
for($k=0;$k<$numatt;$k++) { // for each day of attendance for the student
for($j=1;$j<=$atts[$k]->attendance->hours;$j++) {
@@ -136,7 +134,8 @@
if ($atts[$k]->sroll[$student->id][$j]->status == 1) {$status=$T;}
elseif ($atts[$k]->sroll[$student->id][$j]->status == 2) {$status=$A;}
else {$status=$P;}
- $myxls->InsertText($status);
+ $myxls->write_string($row,$pos,$status);
+ $pos++;
} /// for loop
}
}
@@ -149,10 +148,10 @@
} /// for loop
} // outer for for each day of attendance
$tot=tally_overall_absences_decimal($abs,$tar);
- $myxls->InsertNumber($tot);
+ $myxls->write_number($row,$pos,$tot);
$row++;
}
- $myxls->SendFileName($course->shortname."_Attendance_Week");
+ $workbook->close();
exit;
}
@@ -522,4 +521,12 @@ function attendance_print_header() {
navmenu($course, $cm));
}
+function attendance_HeaderingExcel($filename) {
+ header("Content-type: application/vnd.ms-excel");
+ header("Content-Disposition: attachment; filename=$filename" );
+ header("Expires: 0");
+ header("Cache-Control: must-revalidate, post-check=0,pre-check=0");
+ header("Pragma: public");
+}
+
?>
View
209 mod/attendance/write_excel/BIFFwriter.php
@@ -0,0 +1,209 @@
+<?php
+/*
+* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
+*
+* The majority of this is _NOT_ my code. I simply ported it from the
+* PERL Spreadsheet::WriteExcel module.
+*
+* The author of the Spreadsheet::WriteExcel module is John McNamara
+* <jmcnamara@cpan.org>
+*
+* I _DO_ maintain this code, and John McNamara has nothing to do with the
+* porting of this code to PHP. Any questions directly related to this
+* class library should be directed to me.
+*
+* License Information:
+*
+* Spreadsheet::WriteExcel: A library for generating Excel Spreadsheets
+* Copyright (C) 2002 Xavier Noguer xnoguer@rezebra.com
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/**
+* Class for writing Excel BIFF records.
+*
+* From "MICROSOFT EXCEL BINARY FILE FORMAT" by Mark O'Brien (Microsoft Corporation):
+*
+* BIFF (BInary File Format) is the file format in which Excel documents are
+* saved on disk. A BIFF file is a complete description of an Excel document.
+* BIFF files consist of sequences of variable-length records. There are many
+* different types of BIFF records. For example, one record type describes a
+* formula entered into a cell; one describes the size and location of a
+* window into a document; another describes a picture format.
+*
+* @author Xavier Noguer <xnoguer@rezebra.com>
+* @package Spreadsheet_WriteExcel
+*/
+
+class BIFFWriter
+{
+ var $_BIFF_version = 0x0500;
+
+/**
+* Constructor
+*
+* @access public
+*/
+ function BIFFwriter()
+ {
+ // The byte order of this architecture. 0 => little endian, 1 => big endian
+ $this->_byte_order = '';
+ // The string containing the data of the BIFF stream
+ $this->_data = '';
+ // Should be the same as strlen($this->_data)
+ $this->_datasize = 0;
+ // The maximun length for a BIFF record. See _add_continue()
+ $this->_limit = 2080;
+ // Set the byte order
+ $this->_set_byte_order();
+ }
+
+/**
+* Determine the byte order and store it as class data to avoid
+* recalculating it for each call to new().
+*
+* @access private
+*/
+ function _set_byte_order()
+ {
+ if ($this->_byte_order == '')
+ {
+ // Check if "pack" gives the required IEEE 64bit float
+ $teststr = pack("d", 1.2345);
+ $number = pack("C8", 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F);
+ if ($number == $teststr) {
+ $byte_order = 0; // Little Endian
+ }
+ elseif ($number == strrev($teststr)){
+ $byte_order = 1; // Big Endian
+ }
+ else {
+ // Give up. I'll fix this in a later version.
+ die("Required floating point format not supported ".
+ "on this platform. See the portability section ".
+ "of the documentation."
+ );
+ }
+ }
+ $this->_byte_order = $byte_order;
+ }
+
+/**
+* General storage function
+*
+* @param string $data binary data to prepend
+* @access private
+*/
+ function _prepend($data)
+ {
+ if (strlen($data) > $this->_limit) {
+ $data = $this->_add_continue($data);
+ }
+ $this->_data = $data.$this->_data;
+ $this->_datasize += strlen($data);
+ }
+
+/**
+* General storage function
+*
+* @param string $data binary data to append
+* @access private
+*/
+ function _append($data)
+ {
+ if (strlen($data) > $this->_limit) {
+ $data = $this->_add_continue($data);
+ }
+ $this->_data = $this->_data.$data;
+ $this->_datasize += strlen($data);
+ }
+
+/**
+* Writes Excel BOF record to indicate the beginning of a stream or
+* sub-stream in the BIFF file.
+*
+* @param integer $type type of BIFF file to write: 0x0005 Workbook, 0x0010 Worksheet.
+* @access private
+*/
+ function _store_bof($type)
+ {
+ $record = 0x0809; // Record identifier
+ $length = 0x0008; // Number of bytes to follow
+ $version = $this->_BIFF_version;
+
+ // According to the SDK $build and $year should be set to zero.
+ // However, this throws a warning in Excel 5. So, use these
+ // magic numbers.
+ $build = 0x096C;
+ $year = 0x07C9;
+
+ $header = pack("vv", $record, $length);
+ $data = pack("vvvv", $version, $type, $build, $year);
+ $this->_prepend($header.$data);
+ }
+
+/**
+* Writes Excel EOF record to indicate the end of a BIFF stream.
+*
+* @access private
+*/
+ function _store_eof()
+ {
+ $record = 0x000A; // Record identifier
+ $length = 0x0000; // Number of bytes to follow
+ $header = pack("vv", $record, $length);
+ $this->_append($header);
+ }
+
+/**
+* Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In
+* Excel 97 the limit is 8228 bytes. Records that are longer than these limits
+* must be split up into CONTINUE blocks.
+*
+* This function takes a long BIFF record and inserts CONTINUE records as
+* necessary.
+*
+* @param string $data The original binary data to be written
+* @return string A very convenient string of continue blocks
+* @access private
+*/
+ function _add_continue($data)
+ {
+ $limit = $this->_limit;
+ $record = 0x003C; // Record identifier
+
+ // The first 2080/8224 bytes remain intact. However, we have to change
+ // the length field of the record.
+ $tmp = substr($data, 0, 2).pack("v", $limit-4).substr($data, 4, $limit - 4);
+
+ $header = pack("vv", $record, $limit); // Headers for continue records
+
+ // Retrieve chunks of 2080/8224 bytes +4 for the header.
+ for($i = $limit; $i < strlen($data) - $limit; $i += $limit)
+ {
+ $tmp .= $header;
+ $tmp .= substr($data, $i, $limit);
+ }
+
+ // Retrieve the last chunk of data
+ $header = pack("vv", $record, strlen($data) - $i);
+ $tmp .= $header;
+ $tmp .= substr($data,$i,strlen($data) - $i);
+
+ return($tmp);
+ }
+}
+?>
View
637 mod/attendance/write_excel/Format.php
@@ -0,0 +1,637 @@
+<?php
+/*
+* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
+*
+* The majority of this is _NOT_ my code. I simply ported it from the
+* PERL Spreadsheet::WriteExcel module.
+*
+* The author of the Spreadsheet::WriteExcel module is John McNamara
+* <jmcnamara@cpan.org>
+*
+* I _DO_ maintain this code, and John McNamara has nothing to do with the
+* porting of this code to PHP. Any questions directly related to this
+* class library should be directed to me.
+*
+* License Information:
+*
+* Spreadsheet::WriteExcel: A library for generating Excel Spreadsheets
+* Copyright (C) 2002 Xavier Noguer xnoguer@rezebra.com
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/**
+* Class for generating Excel XF records (formats)
+*
+* @author Xavier Noguer <xnoguer@rezebra.com>
+* @package Spreadsheet_WriteExcel
+*/
+
+class Format
+{
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param integer $index the XF index for the format.
+ * @param array $properties array with properties to be set on initialization.
+ */
+ function Format($index = 0,$properties = array())
+ {
+ $this->xf_index = $index;
+
+ $this->font_index = 0;
+ $this->font = 'Arial';
+ $this->size = 10;
+ $this->bold = 0x0190;
+ $this->_italic = 0;
+ $this->color = 0x7FFF;
+ $this->_underline = 0;
+ $this->font_strikeout = 0;
+ $this->font_outline = 0;
+ $this->font_shadow = 0;
+ $this->font_script = 0;
+ $this->font_family = 0;
+ $this->font_charset = 0;
+
+ $this->_num_format = 0;
+
+ $this->hidden = 0;
+ $this->locked = 1;
+
+ $this->_text_h_align = 0;
+ $this->_text_wrap = 0;
+ $this->text_v_align = 2;
+ $this->text_justlast = 0;
+ $this->rotation = 0;
+
+ $this->fg_color = 0x40;
+ $this->bg_color = 0x41;
+
+ $this->pattern = 0;
+
+ $this->bottom = 0;
+ $this->top = 0;
+ $this->left = 0;
+ $this->right = 0;
+
+ $this->bottom_color = 0x40;
+ $this->top_color = 0x40;
+ $this->left_color = 0x40;
+ $this->right_color = 0x40;
+
+ // Set properties passed to Workbook::add_format()
+ foreach($properties as $property => $value)
+ {
+ if(method_exists($this,"set_$property"))
+ {
+ $aux = 'set_'.$property;
+ $this->$aux($value);
+ }
+ }
+ }
+
+ /**
+ * Generate an Excel BIFF XF record (style or cell).
+ *
+ * @param string $style The type of the XF record ('style' or 'cell').
+ * @return string The XF record
+ */
+ function get_xf($style)
+ {
+ // Set the type of the XF record and some of the attributes.
+ if ($style == "style") {
+ $style = 0xFFF5;
+ }
+ else {
+ $style = $this->locked;
+ $style |= $this->hidden << 1;
+ }
+
+ // Flags to indicate if attributes have been set.
+ $atr_num = ($this->_num_format != 0)?1:0;
+ $atr_fnt = ($this->font_index != 0)?1:0;
+ $atr_alc = ($this->_text_wrap)?1:0;
+ $atr_bdr = ($this->bottom ||
+ $this->top ||
+ $this->left ||
+ $this->right)?1:0;
+ $atr_pat = (($this->fg_color != 0x40) ||
+ ($this->bg_color != 0x41) ||
+ $this->pattern)?1:0;
+ $atr_prot = 0;
+
+ // Zero the default border colour if the border has not been set.
+ if ($this->bottom == 0) {
+ $this->bottom_color = 0;
+ }
+ if ($this->top == 0) {
+ $this->top_color = 0;
+ }
+ if ($this->right == 0) {
+ $this->right_color = 0;
+ }
+ if ($this->left == 0) {
+ $this->left_color = 0;
+ }
+
+ $record = 0x00E0; // Record identifier
+ $length = 0x0010; // Number of bytes to follow
+
+ $ifnt = $this->font_index; // Index to FONT record
+ $ifmt = $this->_num_format; // Index to FORMAT record
+
+ $align = $this->_text_h_align; // Alignment
+ $align |= $this->_text_wrap << 3;
+ $align |= $this->text_v_align << 4;
+ $align |= $this->text_justlast << 7;
+ $align |= $this->rotation << 8;
+ $align |= $atr_num << 10;
+ $align |= $atr_fnt << 11;
+ $align |= $atr_alc << 12;
+ $align |= $atr_bdr << 13;
+ $align |= $atr_pat << 14;
+ $align |= $atr_prot << 15;
+
+ $icv = $this->fg_color; // fg and bg pattern colors
+ $icv |= $this->bg_color << 7;
+
+ $fill = $this->pattern; // Fill and border line style
+ $fill |= $this->bottom << 6;
+ $fill |= $this->bottom_color << 9;
+
+ $border1 = $this->top; // Border line style and color
+ $border1 |= $this->left << 3;
+ $border1 |= $this->right << 6;
+ $border1 |= $this->top_color << 9;
+
+ $border2 = $this->left_color; // Border color
+ $border2 |= $this->right_color << 7;
+
+ $header = pack("vv", $record, $length);
+ $data = pack("vvvvvvvv", $ifnt, $ifmt, $style, $align,
+ $icv, $fill,
+ $border1, $border2);
+ return($header.$data);
+ }
+
+ /**
+ * Generate an Excel BIFF FONT record.
+ *
+ * @see Workbook::_store_all_fonts()
+ * @return string The FONT record
+ */
+ function get_font()
+ {
+ $dyHeight = $this->size * 20; // Height of font (1/20 of a point)
+ $icv = $this->color; // Index to color palette
+ $bls = $this->bold; // Bold style
+ $sss = $this->font_script; // Superscript/subscript
+ $uls = $this->_underline; // Underline
+ $bFamily = $this->font_family; // Font family
+ $bCharSet = $this->font_charset; // Character set
+ $rgch = $this->font; // Font name
+
+ $cch = strlen($rgch); // Length of font name
+ $record = 0x31; // Record identifier
+ $length = 0x0F + $cch; // Record length
+ $reserved = 0x00; // Reserved
+ $grbit = 0x00; // Font attributes
+ if ($this->_italic) {
+ $grbit |= 0x02;
+ }
+ if ($this->font_strikeout) {
+ $grbit |= 0x08;
+ }
+ if ($this->font_outline) {
+ $grbit |= 0x10;
+ }
+ if ($this->font_shadow) {
+ $grbit |= 0x20;
+ }
+
+ $header = pack("vv", $record, $length);
+ $data = pack("vvvvvCCCCC", $dyHeight, $grbit, $icv, $bls,
+ $sss, $uls, $bFamily,
+ $bCharSet, $reserved, $cch);
+ return($header . $data. $this->font);
+ }
+
+ /**
+ * Returns a unique hash key for a font. Used by Workbook->_store_all_fonts()
+ *
+ * The elements that form the key are arranged to increase the probability of
+ * generating a unique key. Elements that hold a large range of numbers
+ * (eg. _color) are placed between two binary elements such as _italic
+ *
+ * @return string A key for this font
+ */
+ function get_font_key()
+ {
+ $key = "$this->font$this->size";
+ $key .= "$this->font_script$this->_underline";
+ $key .= "$this->font_strikeout$this->bold$this->font_outline";
+ $key .= "$this->font_family$this->font_charset";
+ $key .= "$this->font_shadow$this->color$this->_italic";
+ $key = str_replace(" ","_",$key);
+ return ($key);
+ }
+
+ /**
+ * Returns the index used by Worksheet->_XF()
+ *
+ * @return integer The index for the XF record
+ */
+ function get_xf_index()
+ {
+ return($this->xf_index);
+ }
+
+ /**
+ * Used in conjunction with the set_xxx_color methods to convert a color
+ * string into a number. Color range is 0..63 but we will restrict it
+ * to 8..63 to comply with Gnumeric. Colors 0..7 are repeated in 8..15.
+ *
+ * @param string $name_color name of the color (i.e.: 'blue', 'red', etc..). Optional.
+ * @return integer The color index
+ */
+ function _get_color($name_color = '')
+ {
+ $colors = array(
+ 'aqua' => 0x0F,
+ 'cyan' => 0x0F,
+ 'black' => 0x08,
+ 'blue' => 0x0C,
+ 'brown' => 0x10,
+ 'magenta' => 0x0E,
+ 'fuchsia' => 0x0E,
+ 'gray' => 0x17,
+ 'grey' => 0x17,
+ 'green' => 0x11,
+ 'lime' => 0x0B,
+ 'navy' => 0x12,
+ 'orange' => 0x35,
+ 'purple' => 0x14,
+ 'red' => 0x0A,
+ 'silver' => 0x16,
+ 'white' => 0x09,
+ 'yellow' => 0x0D
+ );
+
+ // Return the default color, 0x7FFF, if undef,
+ if($name_color == '') {
+ return(0x7FFF);
+ }
+
+ // or the color string converted to an integer,
+ if(isset($colors[$name_color])) {
+ return($colors[$name_color]);
+ }
+
+ // or the default color if string is unrecognised,
+ if(preg_match("/\D/",$name_color)) {
+ return(0x7FFF);
+ }
+
+ // or an index < 8 mapped into the correct range,
+ if($name_color < 8) {
+ return($name_color + 8);
+ }
+
+ // or the default color if arg is outside range,
+ if($name_color > 63) {
+ return(0x7FFF);
+ }
+
+ // or an integer in the valid range
+ return($name_color);
+ }
+
+ /**
+ * Set cell alignment.
+ *
+ * @access public
+ * @param string $location alignment for the cell ('left', 'right', etc...).
+ */
+ function set_align($location)
+ {
+ if (preg_match("/\d/",$location)) {
+ return; // Ignore numbers
+ }
+
+ $location = strtolower($location);
+
+ if ($location == 'left')
+ $this->_text_h_align = 1;
+ if ($location == 'centre')
+ $this->_text_h_align = 2;
+ if ($location == 'center')
+ $this->_text_h_align = 2;
+ if ($location == 'right')
+ $this->_text_h_align = 3;
+ if ($location == 'fill')
+ $this->_text_h_align = 4;
+ if ($location == 'justify')
+ $this->_text_h_align = 5;
+ if ($location == 'merge')
+ $this->_text_h_align = 6;
+ if ($location == 'equal_space') // For T.K.
+ $this->_text_h_align = 7;
+ if ($location == 'top')
+ $this->text_v_align = 0;
+ if ($location == 'vcentre')
+ $this->text_v_align = 1;
+ if ($location == 'vcenter')
+ $this->text_v_align = 1;
+ if ($location == 'bottom')
+ $this->text_v_align = 2;
+ if ($location == 'vjustify')
+ $this->text_v_align = 3;
+ if ($location == 'vequal_space') // For T.K.
+ $this->text_v_align = 4;
+ }
+
+ /**
+ * This is an alias for the unintuitive set_align('merge')
+ *
+ * @access public
+ */
+ function set_merge()
+ {
+ $this->set_align('merge');
+ }
+
+ /**
+ * Bold has a range 0x64..0x3E8.
+ * 0x190 is normal. 0x2BC is bold.
+ *
+ * @access public
+ * @param integer $weight Weight for the text, 0 maps to 0x190, 1 maps to 0x2BC.
+ It's Optional, default is 1 (bold).
+ */
+ function set_bold($weight = 1)
+ {
+ if($weight == 1) {
+ $weight = 0x2BC; // Bold text
+ }
+ if($weight == 0) {
+ $weight = 0x190; // Normal text
+ }
+ if($weight < 0x064) {
+ $weight = 0x190; // Lower bound
+ }
+ if($weight > 0x3E8) {
+ $weight = 0x190; // Upper bound
+ }
+ $this->bold = $weight;
+ }
+
+
+ /************************************
+ * FUNCTIONS FOR SETTING CELLS BORDERS
+ */
+
+ /**
+ * Sets the bottom border of the cell
+ *
+ * @access public
+ * @param integer $style style of the cell border. 1 => thin, 2 => thick.
+ */
+ function set_bottom($style)
+ {
+ $this->bottom = $style;
+ }
+
+ /**
+ * Sets the top border of the cell
+ *
+ * @access public
+ * @param integer $style style of the cell top border. 1 => thin, 2 => thick.
+ */
+ function set_top($style)
+ {
+ $this->top = $style;
+ }
+
+ /**
+ * Sets the left border of the cell
+ *
+ * @access public
+ * @param integer $style style of the cell left border. 1 => thin, 2 => thick.
+ */
+ function set_left($style)
+ {
+ $this->left = $style;
+ }
+
+ /**
+ * Sets the right border of the cell
+ *
+ * @access public
+ * @param integer $style style of the cell right border. 1 => thin, 2 => thick.
+ */
+ function set_right($style)
+ {
+ $this->right = $style;
+ }
+
+
+ /**
+ * Set cells borders to the same style
+ *
+ * @access public
+ * @param integer $style style to apply for all cell borders. 1 => thin, 2 => thick.
+ */
+ function set_border($style)
+ {
+ $this->set_bottom($style);
+ $this->set_top($style);
+ $this->set_left($style);
+ $this->set_right($style);
+ }
+
+
+ /*******************************************
+ * FUNCTIONS FOR SETTING CELLS BORDERS COLORS
+ */
+
+ /**
+ * Sets all the cell's borders to the same color
+ *
+ * @access public
+ * @param mixed $color The color we are setting. Either a string (like 'blue'),
+ * or an integer (like 0x41).
+ */
+ function set_border_color($color)
+ {
+ $this->set_bottom_color($color);
+ $this->set_top_color($color);
+ $this->set_left_color($color);
+ $this->set_right_color($color);
+ }
+
+ /**
+ * Sets the cell's bottom border color
+ *
+ * @access public
+ * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
+ */
+ function set_bottom_color($color)
+ {
+ $value = $this->_get_color($color);
+ $this->bottom_color = $value;
+ }
+
+ /**
+ * Sets the cell's top border color
+ *
+ * @access public
+ * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
+ */
+ function set_top_color($color)
+ {
+ $value = $this->_get_color($color);
+ $this->top_color = $value;
+ }
+
+ /**
+ * Sets the cell's left border color
+ *
+ * @access public
+ * @param mixed $color either a string (like 'blue'), or an integer (like 0x41).
+ */
+ function set_left_color($color)
+ {
+ $value = $this->_get_color($color);
+ $this->left_color = $value;
+ }
+
+ /**
+ * Sets the cell's right border color
+ *
+ * @access public
+ * @param mixed $color either a string (like 'blue'), or an integer (like 0x41).
+ */
+ function set_right_color($color)
+ {
+ $value = $this->_get_color($color);
+ $this->right_color = $value;
+ }
+
+
+ /**
+ * Sets the cell's foreground color
+ *
+ * @access public
+ * @param mixed $color either a string (like 'blue'), or an integer (like 0x41).
+ */
+ function set_fg_color($color)
+ {
+ $value = $this->_get_color($color);
+ $this->fg_color = $value;
+ }
+
+ /**
+ * Sets the cell's background color
+ *
+ * @access public
+ * @param mixed $color either a string (like 'blue'), or an integer (like 0x41).
+ */
+ function set_bg_color($color)
+ {
+ $value = $this->_get_color($color);
+ $this->bg_color = $value;
+ }
+
+ /**
+ * Sets the cell's color
+ *
+ * @access public
+ * @param mixed $color either a string (like 'blue'), or an integer (like 0x41).
+ */
+ function set_color($color)
+ {
+ $value = $this->_get_color($color);
+ $this->color = $value;
+ }
+
+ /**
+ * Sets the pattern attribute of a cell
+ *
+ * @access public
+ * @param integer $arg Optional. Defaults to 1.
+ */
+ function set_pattern($arg = 1)
+ {
+ $this->pattern = $arg;
+ }
+
+ /**
+ * Sets the underline of the text
+ *
+ * @access public
+ * @param integer $underline The value for underline. Possible values are:
+ * 1 => underline, 2 => double underline.
+ */
+ function set_underline($underline)
+ {
+ $this->_underline = $underline;
+ }
+
+ /**
+ * Sets the font style as italic
+ *
+ * @access public
+ */
+ function set_italic()
+ {
+ $this->_italic = 1;
+ }
+
+ /**
+ * Sets the font size
+ *
+ * @access public
+ * @param integer $size The font size (in pixels I think).
+ */
+ function set_size($size)
+ {
+ $this->size = $size;
+ }
+
+ /**
+ * Sets the num format
+ *
+ * @access public
+ * @param integer $num_format The num format.
+ */
+ function set_num_format($num_format)
+ {
+ $this->_num_format = $num_format;
+ }
+
+ /**
+ * Sets text wrapping
+ *
+ * @access public
+ * @param integer $text_wrap Optional. 0 => no text wrapping, 1 => text wrapping.
+ * Defaults to 1.
+ */
+ function set_text_wrap($text_wrap = 1)
+ {
+ $this->_text_wrap = $text_wrap;
+ }
+}
+?>
View
414 mod/attendance/write_excel/OLEwriter.php
@@ -0,0 +1,414 @@
+<?php
+/*
+* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
+*
+* The majority of this is _NOT_ my code. I simply ported it from the
+* PERL Spreadsheet::WriteExcel module.
+*
+* The author of the Spreadsheet::WriteExcel module is John McNamara
+* <jmcnamara@cpan.org>
+*
+* I _DO_ maintain this code, and John McNamara has nothing to do with the
+* porting of this code to PHP. Any questions directly related to this
+* class library should be directed to me.
+*
+* License Information:
+*
+* Spreadsheet::WriteExcel: A library for generating Excel Spreadsheets
+* Copyright (C) 2002 Xavier Noguer xnoguer@rezebra.com
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/**
+* Class for creating OLE streams for Excel Spreadsheets
+*
+* @author Xavier Noguer <xnoguer@rezebra.com>
+* @package Spreadsheet_WriteExcel
+*/
+class OLEwriter
+{
+ /**
+ * Filename for the OLE stream
+ * @var string
+ * @see _initialize()
+ */
+ var $_OLEfilename;
+
+ /**
+ * Filehandle for the OLE stream
+ * @var resource
+ */
+ var $_filehandle;
+
+ /**
+ * Name of the temporal file in case OLE stream goes to stdout
+ * @var string
+ */
+ var $_tmp_filename;
+
+ /**
+ * Variable for preventing closing two times
+ * @var integer
+ */
+ var $_fileclosed;
+
+ /**
+ * Size of the data to be written to the OLE stream
+ * @var integer
+ */
+ var $_biffsize;
+
+ /**
+ * Real data size to be written to the OLE stream
+ * @var integer
+ */
+ var $_booksize;
+
+ /**
+ * Number of big blocks in the OLE stream
+ * @var integer
+ */
+ var $_big_blocks;
+
+ /**
+ * Number of list blocks in the OLE stream
+ * @var integer
+ */
+ var $_list_blocks;
+
+ /**
+ * Number of big blocks in the OLE stream
+ * @var integer
+ */
+ var $_root_start;
+
+ /**
+ * Class for creating an OLEwriter
+ *
+ * @param string $OLEfilename the name of the file for the OLE stream
+ */
+ function OLEwriter($OLEfilename)
+ {
+ $this->_OLEfilename = $OLEfilename;
+ $this->_filehandle = "";
+ $this->_tmp_filename = "";
+ $this->_fileclosed = 0;
+ //$this->_size_allowed = 0;
+ $this->_biffsize = 0;
+ $this->_booksize = 0;
+ $this->_big_blocks = 0;
+ $this->_list_blocks = 0;
+ $this->_root_start = 0;
+ //$this->_block_count = 4;
+ $this->_initialize();
+ }
+
+/**
+* Check for a valid filename and store the filehandle.
+* Filehandle "-" writes to STDOUT
+*/
+ function _initialize()
+ {
+ $OLEfile = $this->_OLEfilename;
+
+ if(($OLEfile == '-') or ($OLEfile == ''))
+ {
+ $this->_tmp_filename = tempnam("/tmp", "OLEwriter");
+ $fh = fopen($this->_tmp_filename,"wb");
+ if ($fh == false) {
+ die("Can't create temporary file.");
+ }
+ }
+ else
+ {
+ // Create a new file, open for writing (in binmode)
+ $fh = fopen($OLEfile,"wb");
+ if ($fh == false) {
+ die("Can't open $OLEfile. It may be in use or protected.");
+ }
+ }
+
+ // Store filehandle
+ $this->_filehandle = $fh;
+ }
+
+
+ /**
+ * Set the size of the data to be written to the OLE stream.
+ * The maximun size comes from this:
+ * $big_blocks = (109 depot block x (128 -1 marker word)
+ * - (1 x end words)) = 13842
+ * $maxsize = $big_blocks * 512 bytes = 7087104
+ *
+ * @access public
+ * @see Workbook::store_OLE_file()
+ * @param integer $biffsize The size of the data to be written to the OLE stream
+ * @return integer 1 for success
+ */
+ function set_size($biffsize)
+ {
+ $maxsize = 7087104; // TODO: extend max size
+
+ if ($biffsize > $maxsize) {
+ die("Maximum file size, $maxsize, exceeded.");
+ }
+
+ $this->_biffsize = $biffsize;
+ // Set the min file size to 4k to avoid having to use small blocks
+ if ($biffsize > 4096) {
+ $this->_booksize = $biffsize;
+ }
+ else {
+ $this->_booksize = 4096;
+ }
+ //$this->_size_allowed = 1;
+ return(1);
+ }
+
+
+ /**
+ * Calculate various sizes needed for the OLE stream
+ */
+ function _calculate_sizes()
+ {
+ $datasize = $this->_booksize;
+ if ($datasize % 512 == 0) {
+ $this->_big_blocks = $datasize/512;
+ }
+ else {
+ $this->_big_blocks = floor($datasize/512) + 1;
+ }
+ // There are 127 list blocks and 1 marker blocks for each big block
+ // depot + 1 end of chain block
+ $this->_list_blocks = floor(($this->_big_blocks)/127) + 1;
+ $this->_root_start = $this->_big_blocks;
+ }
+
+ /**
+ * Write root entry, big block list and close the filehandle.
+ * This routine is used to explicitly close the open filehandle without
+ * having to wait for DESTROY.
+ *
+ * @access public
+ * @see Workbook::store_OLE_file()
+ */
+ function close()
+ {
+ //return if not $this->{_size_allowed};
+ $this->_write_padding();
+ $this->_write_property_storage();
+ $this->_write_big_block_depot();
+ // Close the filehandle
+ fclose($this->_filehandle);
+ if(($this->_OLEfilename == '-') or ($this->_OLEfilename == ''))
+ {
+ $fh = fopen($this->_tmp_filename, "rb");
+ if ($fh == false) {
+ die("Can't read temporary file.");
+ }
+ fpassthru($fh);
+ // Delete the temporary file.
+ @unlink($this->_tmp_filename);
+ }
+ $this->_fileclosed = 1;
+ }
+
+
+ /**
+ * Write BIFF data to OLE file.
+ *
+ * @param string $data string of bytes to be written
+ */
+ function write($data) //por ahora sólo a STDOUT
+ {
+ fwrite($this->_filehandle,$data,strlen($data));
+ }
+
+
+ /**
+ * Write OLE header block.
+ */
+ function write_header()
+ {
+ $this->_calculate_sizes();
+ $root_start = $this->_root_start;
+ $num_lists = $this->_list_blocks;
+ $id = pack("nnnn", 0xD0CF, 0x11E0, 0xA1B1, 0x1AE1);
+ $unknown1 = pack("VVVV", 0x00, 0x00, 0x00, 0x00);
+ $unknown2 = pack("vv", 0x3E, 0x03);
+ $unknown3 = pack("v", -2);
+ $unknown4 = pack("v", 0x09);
+ $unknown5 = pack("VVV", 0x06, 0x00, 0x00);
+ $num_bbd_blocks = pack("V", $num_lists);
+ $root_startblock = pack("V", $root_start);
+ $unknown6 = pack("VV", 0x00, 0x1000);
+ $sbd_startblock = pack("V", -2);
+ $unknown7 = pack("VVV", 0x00, -2 ,0x00);
+ $unused = pack("V", -1);
+
+ fwrite($this->_filehandle,$id);
+ fwrite($this->_filehandle,$unknown1);
+ fwrite($this->_filehandle,$unknown2);
+ fwrite($this->_filehandle,$unknown3);
+ fwrite($this->_filehandle,$unknown4);
+ fwrite($this->_filehandle,$unknown5);
+ fwrite($this->_filehandle,$num_bbd_blocks);
+ fwrite($this->_filehandle,$root_startblock);
+ fwrite($this->_filehandle,$unknown6);
+ fwrite($this->_filehandle,$sbd_startblock);
+ fwrite($this->_filehandle,$unknown7);
+
+ for($i=1; $i <= $num_lists; $i++)
+ {
+ $root_start++;
+ fwrite($this->_filehandle,pack("V",$root_start));
+ }
+ for($i = $num_lists; $i <=108; $i++)
+ {
+ fwrite($this->_filehandle,$unused);
+ }
+ }
+
+
+ /**
+ * Write big block depot.
+ */
+ function _write_big_block_depot()
+ {
+ $num_blocks = $this->_big_blocks;
+ $num_lists = $this->_list_blocks;
+ $total_blocks = $num_lists *128;
+ $used_blocks = $num_blocks + $num_lists +2;
+
+ $marker = pack("V", -3);
+ $end_of_chain = pack("V", -2);
+ $unused = pack("V", -1);
+
+ for($i=1; $i < $num_blocks; $i++)
+ {
+ fwrite($this->_filehandle,pack("V",$i));
+ }
+ fwrite($this->_filehandle,$end_of_chain);
+ fwrite($this->_filehandle,$end_of_chain);
+ for($i=0; $i < $num_lists; $i++)
+ {
+ fwrite($this->_filehandle,$marker);
+ }
+ for($i=$used_blocks; $i <= $total_blocks; $i++)
+ {
+ fwrite($this->_filehandle,$unused);
+ }
+ }
+
+/**
+* Write property storage. TODO: add summary sheets
+*/
+ function _write_property_storage()
+ {
+ //$rootsize = -2;
+ /*************** name type dir start size */
+ $this->_write_pps("Root Entry", 0x05, 1, -2, 0x00);
+ $this->_write_pps("Book", 0x02, -1, 0x00, $this->_booksize);
+ $this->_write_pps('', 0x00, -1, 0x00, 0x0000);
+ $this->_write_pps('', 0x00, -1, 0x00, 0x0000);
+ }
+
+/**
+* Write property sheet in property storage
+*
+* @param string $name name of the property storage.
+* @param integer $type type of the property storage.
+* @param integer $dir dir of the property storage.
+* @param integer $start start of the property storage.
+* @param integer $size size of the property storage.
+* @access private
+*/
+ function _write_pps($name,$type,$dir,$start,$size)
+ {
+ $length = 0;
+ $rawname = '';
+
+ if ($name != '')
+ {
+ $name = $name . "\0";
+ for($i=0;$i<strlen($name);$i++)
+ {
+ // Simulate a Unicode string
+ $rawname .= pack("H*",dechex(ord($name{$i}))).pack("C",0);
+ }
+ $length = strlen($name) * 2;
+ }
+
+ $zero = pack("C", 0);
+ $pps_sizeofname = pack("v", $length); // 0x40
+ $pps_type = pack("v", $type); // 0x42
+ $pps_prev = pack("V", -1); // 0x44
+ $pps_next = pack("V", -1); // 0x48
+ $pps_dir = pack("V", $dir); // 0x4c
+
+ $unknown1 = pack("V", 0);
+
+ $pps_ts1s = pack("V", 0); // 0x64
+ $pps_ts1d = pack("V", 0); // 0x68
+ $pps_ts2s = pack("V", 0); // 0x6c
+ $pps_ts2d = pack("V", 0); // 0x70
+ $pps_sb = pack("V", $start); // 0x74
+ $pps_size = pack("V", $size); // 0x78
+
+
+ fwrite($this->_filehandle,$rawname);
+ for($i=0; $i < (64 -$length); $i++) {
+ fwrite($this->_filehandle,$zero);
+ }
+ fwrite($this->_filehandle,$pps_sizeofname);
+ fwrite($this->_filehandle,$pps_type);
+ fwrite($this->_filehandle,$pps_prev);
+ fwrite($this->_filehandle,$pps_next);
+ fwrite($this->_filehandle,$pps_dir);
+ for($i=0; $i < 5; $i++) {
+ fwrite($this->_filehandle,$unknown1);
+ }
+ fwrite($this->_filehandle,$pps_ts1s);
+ fwrite($this->_filehandle,$pps_ts1d);
+ fwrite($this->_filehandle,$pps_ts2d);
+ fwrite($this->_filehandle,$pps_ts2d);
+ fwrite($this->_filehandle,$pps_sb);
+ fwrite($this->_filehandle,$pps_size);
+ fwrite($this->_filehandle,$unknown1);
+ }
+
+ /**
+ * Pad the end of the file
+ */
+ function _write_padding()
+ {
+ $biffsize = $this->_biffsize;
+ if ($biffsize < 4096) {
+ $min_size = 4096;
+ }
+ else {
+ $min_size = 512;
+ }
+ if ($biffsize % $min_size != 0)
+ {
+ $padding = $min_size - ($biffsize % $min_size);
+ for($i=0; $i < $padding; $i++) {
+ fwrite($this->_filehandle,"\0");
+ }
+ }
+ }
+}
+?>
View
996 mod/attendance/write_excel/Parser.php
@@ -0,0 +1,996 @@
+<?php
+/**
+* Class for parsing Excel formulas
+*
+* License Information:
+*
+* Spreadsheet::WriteExcel: A library for generating Excel Spreadsheets
+* Copyright (C) 2002 Xavier Noguer xnoguer@rezebra.com
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/**
+* @const ADD token identifier for character "+"
+*/
+define('ADD',"+");
+
+/**
+* @const SUB token identifier for character "-"
+*/
+define('SUB',"-");
+
+/**
+* @const EQUAL token identifier for character "="
+*/
+define('EQUAL',"=");
+
+/**
+* @const MUL token identifier for character "*"
+*/
+define('MUL',"*");
+
+/**
+* @const DIV token identifier for character "/"
+*/
+define('DIV',"/");
+
+/**
+* @const OPEN token identifier for character "("
+*/
+define('OPEN',"(");
+
+/**
+* @const CLOSE token identifier for character ")"
+*/
+define('CLOSE',")");
+
+/**
+* @const COMA token identifier for character ","
+*/
+define('COMA',",");
+
+/**
+* Class for parsing Excel formulas
+*
+* @author Xavier Noguer <xnoguer@rezebra.com>
+* @package Spreadsheet_WriteExcel
+*/
+class Parser
+ {
+/**
+* The class constructor
+*
+* @param integer $byte_order The byte order (Little endian or Big endian) of the architecture
+ (optional). 1 => big endian, 0 (default) => little endian.
+*/
+ function Parser($byte_order = 0)
+ {
+ $this->_current_char = 0; // The index of the character we are currently looking at.
+ $this->_current_token = ''; // The token we are working on.
+ $this->_formula = ""; // The formula to parse.
+ $this->_lookahead = ''; // The character ahead of the current char.
+ $this->_parse_tree = ''; // The parse tree to be generated.
+ $this->_initialize_hashes(); // Initialize the hashes: ptg's and function's ptg's
+ $this->_byte_order = $byte_order; // Little Endian or Big Endian
+ $this->_func_args = 0; // Number of arguments for the current function
+ $this->_volatile = 0;
+ }
+
+/**
+* Initialize the ptg and function hashes.
+*/
+ function _initialize_hashes()
+ {
+ // The Excel ptg indices
+ $this->ptg = array(
+ 'ptgExp' => 0x01,
+ 'ptgTbl' => 0x02,
+ 'ptgAdd' => 0x03,
+ 'ptgSub' => 0x04,
+ 'ptgMul' => 0x05,
+ 'ptgDiv' => 0x06,
+ 'ptgPower' => 0x07,
+ 'ptgConcat' => 0x08,
+ 'ptgLT' => 0x09,
+ 'ptgLE' => 0x0A,
+ 'ptgEQ' => 0x0B,
+ 'ptgGE' => 0x0C,
+ 'ptgGT' => 0x0D,
+ 'ptgNE' => 0x0E,
+ 'ptgIsect' => 0x0F,
+ 'ptgUnion' => 0x10,
+ 'ptgRange' => 0x11,
+ 'ptgUplus' => 0x12,
+ 'ptgUminus' => 0x13,
+ 'ptgPercent' => 0x14,
+ 'ptgParen' => 0x15,
+ 'ptgMissArg' => 0x16,
+ 'ptgStr' => 0x17,
+ 'ptgAttr' => 0x19,
+ 'ptgSheet' => 0x1A,
+ 'ptgEndSheet' => 0x1B,
+ 'ptgErr' => 0x1C,
+ 'ptgBool' => 0x1D,
+ 'ptgInt' => 0x1E,
+ 'ptgNum' => 0x1F,
+ 'ptgArray' => 0x20,
+ 'ptgFunc' => 0x21,
+ 'ptgFuncVar' => 0x22,
+ 'ptgName' => 0x23,
+ 'ptgRef' => 0x24,
+ 'ptgArea' => 0x25,
+ 'ptgMemArea' => 0x26,
+ 'ptgMemErr' => 0x27,
+ 'ptgMemNoMem' => 0x28,
+ 'ptgMemFunc' => 0x29,
+ 'ptgRefErr' => 0x2A,
+ 'ptgAreaErr' => 0x2B,
+ 'ptgRefN' => 0x2C,
+ 'ptgAreaN' => 0x2D,
+ 'ptgMemAreaN' => 0x2E,
+ 'ptgMemNoMemN' => 0x2F,
+ 'ptgNameX' => 0x39,
+ 'ptgRef3d' => 0x3A,
+ 'ptgArea3d' => 0x3B,
+ 'ptgRefErr3d' => 0x3C,
+ 'ptgAreaErr3d' => 0x3D,
+ 'ptgArrayV' => 0x40,
+ 'ptgFuncV' => 0x41,
+ 'ptgFuncVarV' => 0x42,
+ 'ptgNameV' => 0x43,
+ 'ptgRefV' => 0x44,
+ 'ptgAreaV' => 0x45,
+ 'ptgMemAreaV' => 0x46,
+ 'ptgMemErrV' => 0x47,
+ 'ptgMemNoMemV' => 0x48,
+ 'ptgMemFuncV' => 0x49,
+ 'ptgRefErrV' => 0x4A,
+ 'ptgAreaErrV' => 0x4B,
+ 'ptgRefNV' => 0x4C,
+ 'ptgAreaNV' => 0x4D,
+ 'ptgMemAreaNV' => 0x4E,
+ 'ptgMemNoMemN' => 0x4F,
+ 'ptgFuncCEV' => 0x58,
+ 'ptgNameXV' => 0x59,
+ 'ptgRef3dV' => 0x5A,
+ 'ptgArea3dV' => 0x5B,
+ 'ptgRefErr3dV' => 0x5C,
+ 'ptgAreaErr3d' => 0x5D,
+ 'ptgArrayA' => 0x60,
+ 'ptgFuncA' => 0x61,
+ 'ptgFuncVarA' => 0x62,
+ 'ptgNameA' => 0x63,
+ 'ptgRefA' => 0x64,
+ 'ptgAreaA' => 0x65,
+ 'ptgMemAreaA' => 0x66,
+ 'ptgMemErrA' => 0x67,
+ 'ptgMemNoMemA' => 0x68,
+ 'ptgMemFuncA' => 0x69,
+ 'ptgRefErrA' => 0x6A,
+ 'ptgAreaErrA' => 0x6B,
+ 'ptgRefNA' => 0x6C,
+ 'ptgAreaNA' => 0x6D,
+ 'ptgMemAreaNA' => 0x6E,
+ 'ptgMemNoMemN' => 0x6F,
+ 'ptgFuncCEA' => 0x78,
+ 'ptgNameXA' => 0x79,
+ 'ptgRef3dA' => 0x7A,
+ 'ptgArea3dA' => 0x7B,
+ 'ptgRefErr3dA' => 0x7C,
+ 'ptgAreaErr3d' => 0x7D
+ );
+
+ // Thanks to Michael Meeks and Gnumeric for the initial arg values.
+ //
+ // The following hash was generated by "function_locale.pl" in the distro.
+ // Refer to function_locale.pl for non-English function names.
+ //
+ // The array elements are as follow:
+ // ptg: The Excel function ptg code.
+ // args: The number of arguments that the function takes:
+ // >=0 is a fixed number of arguments.
+ // -1 is a variable number of arguments.
+ // class: The reference, value or array class of the function args.
+ // vol: The function is volatile.
+ //
+ $this->_functions = array(
+ // function ptg args class vol
+ 'COUNT' => array( 0, -1, 0, 0 ),
+ 'IF' => array( 1, -1, 1, 0 ),
+ 'ISNA' => array( 2, 1, 1, 0 ),
+ 'ISERROR' => array( 3, 1, 1, 0 ),
+ 'SUM' => array( 4, -1, 0, 0 ),
+ 'AVERAGE' => array( 5, -1, 0, 0 ),
+ 'MIN' => array( 6, -1, 0, 0 ),
+ 'MAX' => array( 7, -1, 0, 0 ),
+ 'ROW' => array( 8, -1, 0, 0 ),
+ 'COLUMN' => array( 9, -1, 0, 0 ),
+ 'NA' => array( 10, 0, 0, 0 ),
+ 'NPV' => array( 11, -1, 1, 0 ),
+ 'STDEV' => array( 12, -1, 0, 0 ),
+ 'DOLLAR' => array( 13, -1, 1, 0 ),
+ 'FIXED' => array( 14, -1, 1, 0 ),
+ 'SIN' => array( 15, 1, 1, 0 ),
+ 'COS' => array( 16, 1, 1, 0 ),
+ 'TAN' => array( 17, 1, 1, 0 ),
+ 'ATAN' => array( 18, 1, 1, 0 ),
+ 'PI' => array( 19, 0, 1, 0 ),
+ 'SQRT' => array( 20, 1, 1, 0 ),
+ 'EXP' => array( 21, 1, 1, 0 ),
+ 'LN' => array( 22, 1, 1, 0 ),
+ 'LOG10' => array( 23, 1, 1, 0 ),
+ 'ABS' => array( 24, 1, 1, 0 ),
+ 'INT' => array( 25, 1, 1, 0 ),
+ 'SIGN' => array( 26, 1, 1, 0 ),
+ 'ROUND' => array( 27, 2, 1, 0 ),
+ 'LOOKUP' => array( 28, -1, 0, 0 ),
+ 'INDEX' => array( 29, -1, 0, 1 ),
+ 'REPT' => array( 30, 2, 1, 0 ),
+ 'MID' => array( 31, 3, 1, 0 ),
+ 'LEN' => array( 32, 1, 1, 0 ),
+ 'VALUE' => array( 33, 1, 1, 0 ),
+ 'TRUE' => array( 34, 0, 1, 0 ),
+ 'FALSE' => array( 35, 0, 1, 0 ),
+ 'AND' => array( 36, -1, 0, 0 ),
+ 'OR' => array( 37, -1, 0, 0 ),
+ 'NOT' => array( 38, 1, 1, 0 ),
+ 'MOD' => array( 39, 2, 1, 0 ),
+ 'DCOUNT' => array( 40, 3, 0, 0 ),
+ 'DSUM' => array( 41, 3, 0, 0 ),
+ 'DAVERAGE' => array( 42, 3, 0, 0 ),
+ 'DMIN' => array( 43, 3, 0, 0 ),
+ 'DMAX' => array( 44, 3, 0, 0 ),
+ 'DSTDEV' => array( 45, 3, 0, 0 ),
+ 'VAR' => array( 46, -1, 0, 0 ),
+ 'DVAR' => array( 47, 3, 0, 0 ),
+ 'TEXT' => array( 48, 2, 1, 0 ),
+ 'LINEST' => array( 49, -1, 0, 0 ),
+ 'TREND' => array( 50, -1, 0, 0 ),
+ 'LOGEST' => array( 51, -1, 0, 0 ),
+ 'GROWTH' => array( 52, -1, 0, 0 ),
+ 'PV' => array( 56, -1, 1, 0 ),
+ 'FV' => array( 57, -1, 1, 0 ),
+ 'NPER' => array( 58, -1, 1, 0 ),
+ 'PMT' => array( 59, -1, 1, 0 ),
+ 'RATE' => array( 60, -1, 1, 0 ),
+ 'MIRR' => array( 61, 3, 0, 0 ),
+ 'IRR' => array( 62, -1, 0, 0 ),
+ 'RAND' => array( 63, 0, 1, 1 ),
+ 'MATCH' => array( 64, -1, 0, 0 ),
+ 'DATE' => array( 65, 3, 1, 0 ),
+ 'TIME' => array( 66, 3, 1, 0 ),
+ 'DAY' => array( 67, 1, 1, 0 ),
+ 'MONTH' => array( 68, 1, 1, 0 ),
+ 'YEAR' => array( 69, 1, 1, 0 ),
+ 'WEEKDAY' => array( 70, -1, 1, 0 ),
+ 'HOUR' => array( 71, 1, 1, 0 ),
+ 'MINUTE' => array( 72, 1, 1, 0 ),
+ 'SECOND' => array( 73, 1, 1, 0 ),
+ 'NOW' => array( 74, 0, 1, 1 ),
+ 'AREAS' => array( 75, 1, 0, 1 ),
+ 'ROWS' => array( 76, 1, 0, 1 ),
+ 'COLUMNS' => array( 77, 1, 0, 1 ),
+ 'OFFSET' => array( 78, -1, 0, 1 ),
+ 'SEARCH' => array( 82, -1, 1, 0 ),
+ 'TRANSPOSE' => array( 83, 1, 1, 0 ),
+ 'TYPE' => array( 86, 1, 1, 0 ),
+ 'ATAN2' => array( 97, 2, 1, 0 ),
+ 'ASIN' => array( 98, 1, 1, 0 ),
+ 'ACOS' => array( 99, 1, 1, 0 ),
+ 'CHOOSE' => array( 100, -1, 1, 0 ),
+ 'HLOOKUP' => array( 101, -1, 0, 0 ),
+ 'VLOOKUP' => array( 102, -1, 0, 0 ),
+ 'ISREF' => array( 105, 1, 0, 0 ),
+ 'LOG' => array( 109, -1, 1, 0 ),
+ 'CHAR' => array( 111, 1, 1, 0 ),
+ 'LOWER' => array( 112, 1, 1, 0 ),
+ 'UPPER' => array( 113, 1, 1, 0 ),
+ 'PROPER' => array( 114, 1, 1, 0 ),
+ 'LEFT' => array( 115, -1, 1, 0 ),
+ 'RIGHT' => array( 116, -1, 1, 0 ),
+ 'EXACT' => array( 117, 2, 1, 0 ),
+ 'TRIM' => array( 118, 1, 1, 0 ),
+ 'REPLACE' => array( 119, 4, 1, 0 ),
+ 'SUBSTITUTE' => array( 120, -1, 1, 0 ),
+ 'CODE' => array( 121, 1, 1, 0 ),
+ 'FIND' => array( 124, -1, 1, 0 ),
+ 'CELL' => array( 125, -1, 0, 1 ),
+ 'ISERR' => array( 126, 1, 1, 0 ),
+ 'ISTEXT' => array( 127, 1, 1, 0 ),
+ 'ISNUMBER' => array( 128, 1, 1, 0 ),
+ 'ISBLANK' => array( 129, 1, 1, 0 ),
+ 'T' => array( 130, 1, 0, 0 ),
+ 'N' => array( 131, 1, 0, 0 ),
+ 'DATEVALUE' => array( 140, 1, 1, 0 ),
+ 'TIMEVALUE' => array( 141, 1, 1, 0 ),
+ 'SLN' => array( 142, 3, 1, 0 ),
+ 'SYD' => array( 143, 4, 1, 0 ),
+ 'DDB' => array( 144, -1, 1, 0 ),
+ 'INDIRECT' => array( 148, -1, 1, 1 ),
+ 'CALL' => array( 150, -1, 1, 0 ),
+ 'CLEAN' => array( 162, 1, 1, 0 ),
+ 'MDETERM' => array( 163, 1, 2, 0 ),
+ 'MINVERSE' => array( 164, 1, 2, 0 ),
+ 'MMULT' => array( 165, 2, 2, 0 ),
+ 'IPMT' => array( 167, -1, 1, 0 ),
+ 'PPMT' => array( 168, -1, 1, 0 ),
+ 'COUNTA' => array( 169, -1, 0, 0 ),
+ 'PRODUCT' => array( 183, -1, 0, 0 ),
+ 'FACT' => array( 184, 1, 1, 0 ),
+ 'DPRODUCT' => array( 189, 3, 0, 0 ),
+ 'ISNONTEXT' => array( 190, 1, 1, 0 ),
+ 'STDEVP' => array( 193, -1, 0, 0 ),
+ 'VARP' => array( 194, -1, 0, 0 ),
+ 'DSTDEVP' => array( 195, 3, 0, 0 ),
+ 'DVARP' => array( 196, 3, 0, 0 ),
+ 'TRUNC' => array( 197, -1, 1, 0 ),
+ 'ISLOGICAL' => array( 198, 1, 1, 0 ),
+ 'DCOUNTA' => array( 199, 3, 0, 0 ),
+ 'ROUNDUP' => array( 212, 2, 1, 0 ),
+ 'ROUNDDOWN' => array( 213, 2, 1, 0 ),
+ 'RANK' => array( 216, -1, 0, 0 ),
+ 'ADDRESS' => array( 219, -1, 1, 0 ),
+ 'DAYS360' => array( 220, -1, 1, 0 ),
+ 'TODAY' => array( 221, 0, 1, 1 ),
+ 'VDB' => array( 222, -1, 1, 0 ),
+ 'MEDIAN' => array( 227, -1, 0, 0 ),
+ 'SUMPRODUCT' => array( 228, -1, 2, 0 ),
+ 'SINH' => array( 229, 1, 1, 0 ),
+ 'COSH' => array( 230, 1, 1, 0 ),
+ 'TANH' => array( 231, 1, 1, 0 ),
+ 'ASINH' => array( 232, 1, 1, 0 ),
+ 'ACOSH' => array( 233, 1, 1, 0 ),
+ 'ATANH' => array( 234, 1, 1, 0 ),
+ 'DGET' => array( 235, 3, 0, 0 ),
+ 'INFO' => array( 244, 1, 1, 1 ),
+ 'DB' => array( 247, -1, 1, 0 ),
+ 'FREQUENCY' => array( 252, 2, 0, 0 ),
+ 'ERROR.TYPE' => array( 261, 1, 1, 0 ),
+ 'REGISTER.ID' => array( 267, -1, 1, 0 ),
+ 'AVEDEV' => array( 269, -1, 0, 0 ),
+ 'BETADIST' => array( 270, -1, 1, 0 ),
+ 'GAMMALN' => array( 271, 1, 1, 0 ),
+ 'BETAINV' => array( 272, -1, 1, 0 ),
+ 'BINOMDIST' => array( 273, 4, 1, 0 ),
+ 'CHIDIST' => array( 274, 2, 1, 0 ),
+ 'CHIINV' => array( 275, 2, 1, 0 ),
+ 'COMBIN' => array( 276, 2, 1, 0 ),
+ 'CONFIDENCE' => array( 277, 3, 1, 0 ),
+ 'CRITBINOM' => array( 278, 3, 1, 0 ),
+ 'EVEN' => array( 279, 1, 1, 0 ),
+ 'EXPONDIST' => array( 280, 3, 1, 0 ),
+ 'FDIST' => array( 281, 3, 1, 0 ),
+ 'FINV' => array( 282, 3, 1, 0 ),
+ 'FISHER' => array( 283, 1, 1, 0 ),
+ 'FISHERINV' => array( 284, 1, 1, 0 ),
+ 'FLOOR' => array( 285, 2, 1, 0 ),
+ 'GAMMADIST' => array( 286, 4, 1, 0 ),
+ 'GAMMAINV' => array( 287, 3, 1, 0 ),
+ 'CEILING' => array( 288, 2, 1, 0 ),
+ 'HYPGEOMDIST' => array( 289, 4, 1, 0 ),
+ 'LOGNORMDIST' => array( 290, 3, 1, 0 ),
+ 'LOGINV' => array( 291, 3, 1, 0 ),
+ 'NEGBINOMDIST' => array( 292, 3, 1, 0 ),
+ 'NORMDIST' => array( 293, 4, 1, 0 ),
+ 'NORMSDIST' => array( 294, 1, 1, 0 ),
+ 'NORMINV' => array( 295, 3, 1, 0 ),
+ 'NORMSINV' => array( 296, 1, 1, 0 ),
+ 'STANDARDIZE' => array( 297, 3, 1, 0 ),
+ 'ODD' => array( 298, 1, 1, 0 ),
+ 'PERMUT' => array( 299, 2, 1, 0 ),
+ 'POISSON' => array( 300, 3, 1, 0 ),
+ 'TDIST' => array( 301, 3, 1, 0 ),
+ 'WEIBULL' => array( 302, 4, 1, 0 ),
+ 'SUMXMY2' => array( 303, 2, 2, 0 ),
+ 'SUMX2MY2' => array( 304, 2, 2, 0 ),
+ 'SUMX2PY2' => array( 305, 2, 2, 0 ),
+ 'CHITEST' => array( 306, 2, 2, 0 ),
+ 'CORREL' => array( 307, 2, 2, 0 ),
+ 'COVAR' => array( 308, 2, 2, 0 ),
+ 'FORECAST' => array( 309, 3, 2, 0 ),
+ 'FTEST' => array( 310, 2, 2, 0 ),
+ 'INTERCEPT' => array( 311, 2, 2, 0 ),
+ 'PEARSON' => array( 312, 2, 2, 0 ),
+ 'RSQ' => array( 313, 2, 2, 0 ),
+ 'STEYX' => array( 314, 2, 2, 0 ),
+ 'SLOPE' => array( 315, 2, 2, 0 ),
+ 'TTEST' => array( 316, 4, 2, 0 ),
+ 'PROB' => array( 317, -1, 2, 0 ),
+ 'DEVSQ' => array( 318, -1, 0, 0 ),
+ 'GEOMEAN' => array( 319, -1, 0, 0 ),
+ 'HARMEAN' => array( 320, -1, 0, 0 ),
+ 'SUMSQ' => array( 321, -1, 0, 0 ),
+ 'KURT' => array( 322, -1, 0, 0 ),
+ 'SKEW' => array( 323, -1, 0, 0 ),
+ 'ZTEST' => array( 324, -1, 0, 0 ),
+ 'LARGE' => array( 325, 2, 0, 0 ),
+ 'SMALL' => array( 326, 2, 0, 0 ),
+ 'QUARTILE' => array( 327, 2, 0, 0 ),
+ 'PERCENTILE' => array( 328, 2, 0, 0 ),
+ 'PERCENTRANK' => array( 329, -1, 0, 0 ),
+ 'MODE' => array( 330, -1, 2, 0 ),
+ 'TRIMMEAN' => array( 331, 2, 0, 0 ),
+ 'TINV' => array( 332, 2, 1, 0 ),
+ 'CONCATENATE' => array( 336, -1, 1, 0 ),
+ 'POWER' => array( 337, 2, 1, 0 ),
+ 'RADIANS' => array( 342, 1, 1, 0 ),
+ 'DEGREES' => array( 343, 1, 1, 0 ),
+ 'SUBTOTAL' => array( 344, -1, 0, 0 ),
+ 'SUMIF' => array( 345, -1, 0, 0 ),
+ 'COUNTIF' => array( 346, 2, 0, 0 ),
+ 'COUNTBLANK' => array( 347, 1, 0, 0 ),
+ 'ROMAN' => array( 354, -1, 1, 0 )
+ );
+ }
+
+/**
+* Convert a token to the proper ptg value.
+*
+* @param mixed $token The token to convert.
+*/
+ function _convert($token)
+ {
+ if(is_numeric($token))
+ {
+ return($this->_convert_number($token));
+ }
+ // match references like A1
+ elseif(preg_match("/^([A-I]?[A-Z])(\d+)$/",$token))
+ {
+ return($this->_convert_ref2d($token));
+ }
+ // match ranges like A1:B2
+ elseif(preg_match("/^([A-I]?[A-Z])(\d+)\:([A-I]?[A-Z])(\d+)$/",$token))
+ {
+ return($this->_convert_range2d($token));
+ }
+ // match ranges like A1..B2
+ elseif(preg_match("/^([A-I]?[A-Z])(\d+)\.\.([A-I]?[A-Z])(\d+)$/",$token))
+ {
+ return($this->_convert_range2d($token));
+ }
+ elseif(isset($this->ptg[$token])) // operators (including parentheses)
+ {
+ return(pack("C", $this->ptg[$token]));
+ }
+ elseif(preg_match("/[A-Z0-9À-Ü\.]+/",$token))
+ {
+ return($this->_convert_function($token,$this->_func_args));
+ }
+ // if it's an argument, ignore the token (the argument remains)
+ elseif($token == 'arg')
+ {
+ $this->_func_args++;
+ return('');
+ }
+ die("Unknown token $token");
+ }
+
+/**
+* Convert a number token to ptgInt or ptgNum
+*
+* @param mixed $num an integer or double for conersion to its ptg value
+*/
+ function _convert_number($num)
+ {
+ // Integer in the range 0..2**16-1
+ if ((preg_match("/^\d+$/",$num)) and ($num <= 65535)) {
+ return pack("Cv", $this->ptg['ptgInt'], $num);
+ }
+ else // A float
+ {
+ if($this->_byte_order) // if it's Big Endian
+ {
+ $num = strrev($num);
+ }
+ return pack("Cd", $this->ptg['ptgNum'], $num);
+ }
+ }
+
+/**
+* Convert a function to a ptgFunc or ptgFuncVarV depending on the number of
+* args that it takes.
+*
+* @param string $token The name of the function for convertion to ptg value.
+* @param integer $num_args The number of arguments the function recieves.
+*/
+ function _convert_function($token, $num_args)
+ {
+ $this->_func_args = 0; // re initialize the number of arguments
+ $args = $this->_functions[$token][1];
+ $volatile = $this->_functions[$token][3];
+
+ if($volatile) {
+ $this->_volatile = 1;
+ }
+ // Fixed number of args eg. TIME($i,$j,$k).
+ if ($args >= 0)
+ {
+ return(pack("Cv", $this->ptg['ptgFuncV'], $this->_functions[$token][0]));
+ }
+ // Variable number of args eg. SUM($i,$j,$k, ..).
+ if ($args == -1) {
+ return(pack("CCv", $this->ptg['ptgFuncVarV'], $num_args, $this->_functions[$token][0]));
+ }
+ }
+
+/**
+* Convert an Excel range such as A1:D4 to a ptgRefV.
+*
+* @param string $range An Excel range in the A1:A2 or A1..A2 format.
+*/
+ function _convert_range2d($range)
+ {
+ $class = 2; // as far as I know, this is magick.
+
+ // Split the range into 2 cell refs
+ if(preg_match("/^([A-I]?[A-Z])(\d+)\:([A-I]?[A-Z])(\d+)$/",$range)) {
+ list($cell1, $cell2) = split(':', $range);
+ }
+ elseif(preg_match("/^([A-I]?[A-Z])(\d+)\.\.([A-I]?[A-Z])(\d+)$/",$range)) {
+ list($cell1, $cell2) = split('\.\.', $range);
+ }
+ else {
+ die("Unknown range separator");
+ }
+
+ // Convert the cell references
+ list($row1, $col1) = $this->_cell_to_packed_rowcol($cell1);
+ list($row2, $col2) = $this->_cell_to_packed_rowcol($cell2);
+
+ // The ptg value depends on the class of the ptg.
+ if ($class == 0) {
+ $ptgArea = pack("C", $this->ptg['ptgArea']);
+ }
+ elseif ($class == 1) {
+ $ptgArea = pack("C", $this->ptg['ptgAreaV']);
+ }
+ elseif ($class == 2) {
+ $ptgArea = pack("C", $this->ptg['ptgAreaA']);
+ }
+ else{
+ die("Unknown class ");
+ }
+
+ return($ptgArea . $row1 . $row2 . $col1. $col2);
+ }
+
+/**
+* Convert an Excel reference such as A1, $B2, C$3 or $D$4 to a ptgRefV.
+*
+* @param string $cell An Excel cell reference
+*/
+ function _convert_ref2d($cell)
+ {
+ $class = 2; // as far as I know, this is magick.
+
+ // Convert the cell reference
+ list($row, $col) = $this->_cell_to_packed_rowcol($cell);
+
+ // The ptg value depends on the class of the ptg.
+ if ($class == 0) {
+ $ptgRef = pack("C", $this->ptg['ptgRef']);
+ }
+ elseif ($class == 1) {
+ $ptgRef = pack("C", $this->ptg['ptgRefV']);
+ }
+ elseif ($class == 2) {
+ $ptgRef = pack("C", $this->ptg['ptgRefA']);
+ }
+ else{
+ die("Unknown class ");
+ }
+ return $ptgRef.$row.$col;
+ }
+
+/**
+* pack() row and column into the required 3 byte format.
+*
+* @param string $cell The Excel cell reference to be packed
+*/
+ function _cell_to_packed_rowcol($cell)
+ {
+ list($row, $col, $row_rel, $col_rel) = $this->_cell_to_rowcol($cell);
+ if ($col >= 256) {
+ die("Column in: $cell greater than 255 ");
+ }
+ if ($row >= 16384) {
+ die("Row in: $cell greater than 16384 ");
+ }
+
+ // Set the high bits to indicate if row or col are relative.
+ $row |= $col_rel << 14;
+ $row |= $row_rel << 15;
+
+ $row = pack('v', $row);
+ $col = pack('C', $col);
+
+ return (array($row, $col));
+ }
+
+/**
+* Convert an Excel cell reference such as A1 or $B2 or C$3 or $D$4 to a zero
+* indexed row and column number. Also returns two boolean values to indicate
+* whether the row or column are relative references.
+*
+* @param string $cell The Excel cell reference in A1 format.
+*/
+ function _cell_to_rowcol($cell)
+ {
+ preg_match('/(\$)?([A-I]?[A-Z])(\$)?(\d+)/',$cell,$match);
+ // return absolute column if there is a $ in the ref
+ $col_rel = empty($match[1]) ? 1 : 0;
+ $col_ref = $match[2];
+ $row_rel = empty($match[3]) ? 1 : 0;
+ $row = $match[4];
+
+ // Convert base26 column string to a number.
+ $expn = strlen($col_ref) - 1;
+ $col = 0;
+ for($i=0; $i < strlen($col_ref); $i++)
+ {
+ $col += (ord($col_ref{$i}) - ord('A') + 1) * pow(26, $expn);
+ $expn--;
+ }
+
+ // Convert 1-index to zero-index
+ $row--;
+ $col--;
+
+ return(array($row, $col, $row_rel, $col_rel));
+ }
+
+/**
+* Advance to the next valid token.
+*/
+ function _advance()
+ {
+ $i = $this->_current_char;
+ // eat up white spaces
+ if($i < strlen($this->_formula))
+ {
+ while($this->_formula{$i} == " ")
+ {
+ $i++;
+ }
+ if($i < strlen($this->_formula) - 1)
+ {
+ $this->_lookahead = $this->_formula{$i+1};
+ }
+ $token = "";
+ }
+ while($i < strlen($this->_formula))
+ {
+ $token .= $this->_formula{$i};
+ if($this->_match($token) != '')
+ {
+ if($i < strlen($this->_formula) - 1)
+ {
+ $this->_lookahead = $this->_formula{$i+1};
+ }
+ $this->_current_char = $i + 1;
+ $this->_current_token =