mirror of
https://github.com/moodle/moodle.git
synced 2025-08-05 08:56:36 +02:00

more compatible with non-Excel spreadsheets. It also has more features than the old system that we might take advantage of later.
953 lines
No EOL
35 KiB
PHP
953 lines
No EOL
35 KiB
PHP
<?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
|
|
*/
|
|
|
|
require_once('Format.php');
|
|
require_once('OLEwriter.php');
|
|
require_once('BIFFwriter.php');
|
|
|
|
/**
|
|
* Class for generating Excel Spreadsheets
|
|
*
|
|
* @author Xavier Noguer <xnoguer@rezebra.com>
|
|
* @package Spreadsheet_WriteExcel
|
|
*/
|
|
|
|
class Workbook extends BIFFwriter
|
|
{
|
|
/**
|
|
* Class constructor
|
|
*
|
|
* @param string filename for storing the workbook. "-" for writing to stdout.
|
|
*/
|
|
function Workbook($filename)
|
|
{
|
|
$this->BIFFwriter(); // It needs to call its parent's constructor explicitly
|
|
|
|
$this->_filename = $filename;
|
|
$this->parser = new Parser($this->_byte_order);
|
|
$this->_1904 = 0;
|
|
$this->activesheet = 0;
|
|
$this->firstsheet = 0;
|
|
$this->selected = 0;
|
|
$this->xf_index = 16; // 15 style XF's and 1 cell XF.
|
|
$this->_fileclosed = 0;
|
|
$this->_biffsize = 0;
|
|
$this->sheetname = "Sheet";
|
|
$this->tmp_format = new Format();
|
|
$this->worksheets = array();
|
|
$this->sheetnames = array();
|
|
$this->formats = array();
|
|
$this->palette = array();
|
|
|
|
// Add the default format for hyperlinks
|
|
$this->url_format =& $this->add_format(array('color' => 'blue', 'underline' => 1));
|
|
|
|
// Check for a filename
|
|
//if ($this->_filename == '') {
|
|
// die('Filename required by Spreadsheet::WriteExcel->new()');
|
|
//}
|
|
|
|
# Warn if tmpfiles can't be used.
|
|
//$this->tmpfile_warning();
|
|
$this->_set_palette_xl97();
|
|
}
|
|
|
|
/**
|
|
* Calls finalization methods and explicitly close the OLEwriter file
|
|
* handle.
|
|
*/
|
|
function close()
|
|
{
|
|
if ($this->_fileclosed) { // Prevent close() from being called twice.
|
|
return;
|
|
}
|
|
$this->store_workbook();
|
|
$this->_fileclosed = 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* An accessor for the _worksheets[] array
|
|
* Returns an array of the worksheet objects in a workbook
|
|
*
|
|
* @return array
|
|
*/
|
|
function sheets()
|
|
{
|
|
return($this->worksheets());
|
|
}
|
|
|
|
/**
|
|
* An accessor for the _worksheets[] array.
|
|
*
|
|
* @return array
|
|
*/
|
|
function worksheets()
|
|
{
|
|
return($this->worksheets);
|
|
}
|
|
|
|
/**
|
|
* Add a new worksheet to the Excel workbook.
|
|
* TODO: Add accessor for $this->{_sheetname} for international Excel versions.
|
|
*
|
|
* @access public
|
|
* @param string $name the optional name of the worksheet
|
|
* @return &object reference to a worksheet object
|
|
*/
|
|
function &add_worksheet($name = '')
|
|
{
|
|
$index = count($this->worksheets);
|
|
$sheetname = $this->sheetname;
|
|
|
|
if($name == '') {
|
|
$name = $sheetname.($index+1);
|
|
}
|
|
|
|
// Check that sheetname is <= 31 chars (Excel limit).
|
|
if(strlen($name) > 31) {
|
|
die("Sheetname $name must be <= 31 chars");
|
|
}
|
|
|
|
// Check that the worksheet name doesn't already exist: a fatal Excel error.
|
|
for($i=0; $i < count($this->worksheets); $i++)
|
|
{
|
|
if($name == $this->worksheets[$i]->get_name()) {
|
|
die("Worksheet '$name' already exists");
|
|
}
|
|
}
|
|
|
|
$worksheet = new Worksheet($name,$index,$this->activesheet,
|
|
$this->firstsheet,$this->url_format,
|
|
$this->parser);
|
|
$this->worksheets[$index] = &$worksheet; // Store ref for iterator
|
|
$this->sheetnames[$index] = $name; // Store EXTERNSHEET names
|
|
//$this->parser->set_ext_sheet($name,$index); // Store names in Formula.php
|
|
return($worksheet);
|
|
}
|
|
|
|
/**
|
|
* DEPRECATED!! Use add_worksheet instead
|
|
*
|
|
* @access public
|
|
* @deprecated Use add_worksheet instead
|
|
* @param string $name the optional name of the worksheet
|
|
* @return &object reference to a worksheet object
|
|
*/
|
|
function &addworksheet($name = '')
|
|
{
|
|
return($this->add_worksheet($name));
|
|
}
|
|
|
|
/**
|
|
* Add a new format to the Excel workbook. This adds an XF record and
|
|
* a FONT record. Also, pass any properties to the Format constructor.
|
|
*
|
|
* @access public
|
|
* @param array $properties array with properties for initializing the format (see Format.php)
|
|
* @return &object reference to an XF format
|
|
*/
|
|
function &add_format($properties = array())
|
|
{
|
|
$format = new Format($this->xf_index,$properties);
|
|
$this->xf_index += 1;
|
|
$this->formats[] = &$format;
|
|
return($format);
|
|
}
|
|
|
|
/**
|
|
* DEPRECATED!! Use add_format instead
|
|
*
|
|
* @access public
|
|
* @deprecated Use add_format instead
|
|
* @param array $properties array with properties for initializing the format (see Format.php)
|
|
* @return &object reference to an XF format
|
|
*/
|
|
function &addformat($properties = array())
|
|
{
|
|
return($this->add_format($properties));
|
|
}
|
|
|
|
|
|
/**
|
|
* Change the RGB components of the elements in the colour palette.
|
|
*
|
|
* @access public
|
|
* @param integer $index colour index
|
|
* @param integer $red red RGB value [0-255]
|
|
* @param integer $green green RGB value [0-255]
|
|
* @param integer $blue blue RGB value [0-255]
|
|
* @return integer The palette index for the custom color
|
|
*/
|
|
function set_custom_color($index,$red,$green,$blue)
|
|
{
|
|
// Match a HTML #xxyyzz style parameter
|
|
/*if (defined $_[1] and $_[1] =~ /^#(\w\w)(\w\w)(\w\w)/ ) {
|
|
@_ = ($_[0], hex $1, hex $2, hex $3);
|
|
}*/
|
|
|
|
// Check that the colour index is the right range
|
|
if ($index < 8 or $index > 64) {
|
|
die("Color index $index outside range: 8 <= index <= 64");
|
|
}
|
|
|
|
// Check that the colour components are in the right range
|
|
if ( ($red < 0 or $red > 255) ||
|
|
($green < 0 or $green > 255) ||
|
|
($blue < 0 or $blue > 255) )
|
|
{
|
|
die("Color component outside range: 0 <= color <= 255");
|
|
}
|
|
|
|
$index -= 8; // Adjust colour index (wingless dragonfly)
|
|
|
|
// Set the RGB value
|
|
$this->palette[$index] = array($red, $green, $blue, 0);
|
|
return($index + 8);
|
|
}
|
|
|
|
/**
|
|
* Sets the colour palette to the Excel 97+ default.
|
|
*/
|
|
function _set_palette_xl97()
|
|
{
|
|
$this->palette = array(
|
|
array(0x00, 0x00, 0x00, 0x00), // 8
|
|
array(0xff, 0xff, 0xff, 0x00), // 9
|
|
array(0xff, 0x00, 0x00, 0x00), // 10
|
|
array(0x00, 0xff, 0x00, 0x00), // 11
|
|
array(0x00, 0x00, 0xff, 0x00), // 12
|
|
array(0xff, 0xff, 0x00, 0x00), // 13
|
|
array(0xff, 0x00, 0xff, 0x00), // 14
|
|
array(0x00, 0xff, 0xff, 0x00), // 15
|
|
array(0x80, 0x00, 0x00, 0x00), // 16
|
|
array(0x00, 0x80, 0x00, 0x00), // 17
|
|
array(0x00, 0x00, 0x80, 0x00), // 18
|
|
array(0x80, 0x80, 0x00, 0x00), // 19
|
|
array(0x80, 0x00, 0x80, 0x00), // 20
|
|
array(0x00, 0x80, 0x80, 0x00), // 21
|
|
array(0xc0, 0xc0, 0xc0, 0x00), // 22
|
|
array(0x80, 0x80, 0x80, 0x00), // 23
|
|
array(0x99, 0x99, 0xff, 0x00), // 24
|
|
array(0x99, 0x33, 0x66, 0x00), // 25
|
|
array(0xff, 0xff, 0xcc, 0x00), // 26
|
|
array(0xcc, 0xff, 0xff, 0x00), // 27
|
|
array(0x66, 0x00, 0x66, 0x00), // 28
|
|
array(0xff, 0x80, 0x80, 0x00), // 29
|
|
array(0x00, 0x66, 0xcc, 0x00), // 30
|
|
array(0xcc, 0xcc, 0xff, 0x00), // 31
|
|
array(0x00, 0x00, 0x80, 0x00), // 32
|
|
array(0xff, 0x00, 0xff, 0x00), // 33
|
|
array(0xff, 0xff, 0x00, 0x00), // 34
|
|
array(0x00, 0xff, 0xff, 0x00), // 35
|
|
array(0x80, 0x00, 0x80, 0x00), // 36
|
|
array(0x80, 0x00, 0x00, 0x00), // 37
|
|
array(0x00, 0x80, 0x80, 0x00), // 38
|
|
array(0x00, 0x00, 0xff, 0x00), // 39
|
|
array(0x00, 0xcc, 0xff, 0x00), // 40
|
|
array(0xcc, 0xff, 0xff, 0x00), // 41
|
|
array(0xcc, 0xff, 0xcc, 0x00), // 42
|
|
array(0xff, 0xff, 0x99, 0x00), // 43
|
|
array(0x99, 0xcc, 0xff, 0x00), // 44
|
|
array(0xff, 0x99, 0xcc, 0x00), // 45
|
|
array(0xcc, 0x99, 0xff, 0x00), // 46
|
|
array(0xff, 0xcc, 0x99, 0x00), // 47
|
|
array(0x33, 0x66, 0xff, 0x00), // 48
|
|
array(0x33, 0xcc, 0xcc, 0x00), // 49
|
|
array(0x99, 0xcc, 0x00, 0x00), // 50
|
|
array(0xff, 0xcc, 0x00, 0x00), // 51
|
|
array(0xff, 0x99, 0x00, 0x00), // 52
|
|
array(0xff, 0x66, 0x00, 0x00), // 53
|
|
array(0x66, 0x66, 0x99, 0x00), // 54
|
|
array(0x96, 0x96, 0x96, 0x00), // 55
|
|
array(0x00, 0x33, 0x66, 0x00), // 56
|
|
array(0x33, 0x99, 0x66, 0x00), // 57
|
|
array(0x00, 0x33, 0x00, 0x00), // 58
|
|
array(0x33, 0x33, 0x00, 0x00), // 59
|
|
array(0x99, 0x33, 0x00, 0x00), // 60
|
|
array(0x99, 0x33, 0x66, 0x00), // 61
|
|
array(0x33, 0x33, 0x99, 0x00), // 62
|
|
array(0x33, 0x33, 0x33, 0x00), // 63
|
|
);
|
|
}
|
|
|
|
|
|
###############################################################################
|
|
#
|
|
# _tmpfile_warning()
|
|
#
|
|
# Check that tmp files can be created for use in Worksheet.pm. A CGI, mod_perl
|
|
# or IIS might not have permission to create tmp files. The test is here rather
|
|
# than in Worksheet.pm so that only one warning is given.
|
|
#
|
|
/*sub _tmpfile_warning {
|
|
|
|
my $fh = IO::File->new_tmpfile();
|
|
|
|
if ((not defined $fh) && ($^W)) {
|
|
carp("Unable to create tmp files via IO::File->new_tmpfile(). " .
|
|
"Storing data in memory")
|
|
}
|
|
}*/
|
|
|
|
/**
|
|
* Assemble worksheets into a workbook and send the BIFF data to an OLE
|
|
* storage.
|
|
*/
|
|
function store_workbook()
|
|
{
|
|
// Ensure that at least one worksheet has been selected.
|
|
if ($this->activesheet == 0) {
|
|
$this->worksheets[0]->selected = 1;
|
|
}
|
|
|
|
// Calculate the number of selected worksheet tabs and call the finalization
|
|
// methods for each worksheet
|
|
for($i=0; $i < count($this->worksheets); $i++)
|
|
{
|
|
if($this->worksheets[$i]->selected)
|
|
$this->selected++;
|
|
$this->worksheets[$i]->close($this->sheetnames);
|
|
}
|
|
|
|
// Add Workbook globals
|
|
$this->_store_bof(0x0005);
|
|
$this->_store_externs(); // For print area and repeat rows
|
|
$this->_store_names(); // For print area and repeat rows
|
|
$this->_store_window1();
|
|
$this->_store_1904();
|
|
$this->_store_all_fonts();
|
|
$this->_store_all_num_formats();
|
|
$this->_store_all_xfs();
|
|
$this->_store_all_styles();
|
|
$this->_store_palette();
|
|
$this->_calc_sheet_offsets();
|
|
|
|
// Add BOUNDSHEET records
|
|
for($i=0; $i < count($this->worksheets); $i++) {
|
|
$this->_store_boundsheet($this->worksheets[$i]->name,$this->worksheets[$i]->offset);
|
|
}
|
|
|
|
// End Workbook globals
|
|
$this->_store_eof();
|
|
|
|
// Store the workbook in an OLE container
|
|
$this->_store_OLE_file();
|
|
}
|
|
|
|
/**
|
|
* Store the workbook in an OLE container if the total size of the workbook data
|
|
* is less than ~ 7MB.
|
|
*/
|
|
function _store_OLE_file()
|
|
{
|
|
$OLE = new OLEwriter($this->_filename);
|
|
// Write Worksheet data if data <~ 7MB
|
|
if ($OLE->set_size($this->_biffsize))
|
|
{
|
|
$OLE->write_header();
|
|
$OLE->write($this->_data);
|
|
foreach($this->worksheets as $sheet)
|
|
{
|
|
while ($tmp = $sheet->get_data()) {
|
|
$OLE->write($tmp);
|
|
}
|
|
}
|
|
}
|
|
$OLE->close();
|
|
}
|
|
|
|
/**
|
|
* Calculate offsets for Worksheet BOF records.
|
|
*/
|
|
function _calc_sheet_offsets()
|
|
{
|
|
$BOF = 11;
|
|
$EOF = 4;
|
|
$offset = $this->_datasize;
|
|
for($i=0; $i < count($this->worksheets); $i++) {
|
|
$offset += $BOF + strlen($this->worksheets[$i]->name);
|
|
}
|
|
$offset += $EOF;
|
|
for($i=0; $i < count($this->worksheets); $i++) {
|
|
$this->worksheets[$i]->offset = $offset;
|
|
$offset += $this->worksheets[$i]->_datasize;
|
|
}
|
|
$this->_biffsize = $offset;
|
|
}
|
|
|
|
/**
|
|
* Store the Excel FONT records.
|
|
*/
|
|
function _store_all_fonts()
|
|
{
|
|
// tmp_format is added by new(). We use this to write the default XF's
|
|
$format = $this->tmp_format;
|
|
$font = $format->get_font();
|
|
|
|
// Note: Fonts are 0-indexed. According to the SDK there is no index 4,
|
|
// so the following fonts are 0, 1, 2, 3, 5
|
|
//
|
|
for($i=1; $i <= 5; $i++){
|
|
$this->_append($font);
|
|
}
|
|
|
|
// Iterate through the XF objects and write a FONT record if it isn't the
|
|
// same as the default FONT and if it hasn't already been used.
|
|
//
|
|
$fonts = array();
|
|
$index = 6; // The first user defined FONT
|
|
|
|
$key = $format->get_font_key(); // The default font from _tmp_format
|
|
$fonts[$key] = 0; // Index of the default font
|
|
|
|
for($i=0; $i < count($this->formats); $i++) {
|
|
$key = $this->formats[$i]->get_font_key();
|
|
if (isset($fonts[$key])) {
|
|
// FONT has already been used
|
|
$this->formats[$i]->font_index = $fonts[$key];
|
|
}
|
|
else {
|
|
// Add a new FONT record
|
|
$fonts[$key] = $index;
|
|
$this->formats[$i]->font_index = $index;
|
|
$index++;
|
|
$font = $this->formats[$i]->get_font();
|
|
$this->_append($font);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Store user defined numerical formats i.e. FORMAT records
|
|
*/
|
|
function _store_all_num_formats()
|
|
{
|
|
// Leaning num_format syndrome
|
|
$hash_num_formats = array();
|
|
$num_formats = array();
|
|
$index = 164;
|
|
|
|
// Iterate through the XF objects and write a FORMAT record if it isn't a
|
|
// built-in format type and if the FORMAT string hasn't already been used.
|
|
//
|
|
for($i=0; $i < count($this->formats); $i++)
|
|
{
|
|
$num_format = $this->formats[$i]->_num_format;
|
|
|
|
// Check if $num_format is an index to a built-in format.
|
|
// Also check for a string of zeros, which is a valid format string
|
|
// but would evaluate to zero.
|
|
//
|
|
if (!preg_match("/^0+\d/",$num_format))
|
|
{
|
|
if (preg_match("/^\d+$/",$num_format)) { // built-in format
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (isset($hash_num_formats[$num_format])) {
|
|
// FORMAT has already been used
|
|
$this->formats[$i]->_num_format = $hash_num_formats[$num_format];
|
|
}
|
|
else
|
|
{
|
|
// Add a new FORMAT
|
|
$hash_num_formats[$num_format] = $index;
|
|
$this->formats[$i]->_num_format = $index;
|
|
array_push($num_formats,$num_format);
|
|
$index++;
|
|
}
|
|
}
|
|
|
|
// Write the new FORMAT records starting from 0xA4
|
|
$index = 164;
|
|
foreach ($num_formats as $num_format)
|
|
{
|
|
$this->_store_num_format($num_format,$index);
|
|
$index++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write all XF records.
|
|
*/
|
|
function _store_all_xfs()
|
|
{
|
|
// tmp_format is added by the constructor. We use this to write the default XF's
|
|
// The default font index is 0
|
|
//
|
|
$format = $this->tmp_format;
|
|
for ($i=0; $i <= 14; $i++)
|
|
{
|
|
$xf = $format->get_xf('style'); // Style XF
|
|
$this->_append($xf);
|
|
}
|
|
|
|
$xf = $format->get_xf('cell'); // Cell XF
|
|
$this->_append($xf);
|
|
|
|
// User defined XFs
|
|
for($i=0; $i < count($this->formats); $i++)
|
|
{
|
|
$xf = $this->formats[$i]->get_xf('cell');
|
|
$this->_append($xf);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write all STYLE records.
|
|
*/
|
|
function _store_all_styles()
|
|
{
|
|
$this->_store_style();
|
|
}
|
|
|
|
/**
|
|
* Write the EXTERNCOUNT and EXTERNSHEET records. These are used as indexes for
|
|
* the NAME records.
|
|
*/
|
|
function _store_externs()
|
|
{
|
|
// Create EXTERNCOUNT with number of worksheets
|
|
$this->_store_externcount(count($this->worksheets));
|
|
|
|
// Create EXTERNSHEET for each worksheet
|
|
foreach ($this->sheetnames as $sheetname) {
|
|
$this->_store_externsheet($sheetname);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write the NAME record to define the print area and the repeat rows and cols.
|
|
*/
|
|
function _store_names()
|
|
{
|
|
// Create the print area NAME records
|
|
foreach ($this->worksheets as $worksheet)
|
|
{
|
|
// Write a Name record if the print area has been defined
|
|
if (isset($worksheet->_print_rowmin))
|
|
{
|
|
$this->store_name_short(
|
|
$worksheet->index,
|
|
0x06, // NAME type
|
|
$worksheet->_print_rowmin,
|
|
$worksheet->_print_rowmax,
|
|
$worksheet->_print_colmin,
|
|
$worksheet->_print_colmax
|
|
);
|
|
}
|
|
}
|
|
|
|
// Create the print title NAME records
|
|
foreach ($this->worksheets as $worksheet)
|
|
{
|
|
$rowmin = $worksheet->_title_rowmin;
|
|
$rowmax = $worksheet->_title_rowmax;
|
|
$colmin = $worksheet->_title_colmin;
|
|
$colmax = $worksheet->_title_colmax;
|
|
|
|
// Determine if row + col, row, col or nothing has been defined
|
|
// and write the appropriate record
|
|
//
|
|
if (isset($rowmin) && isset($colmin))
|
|
{
|
|
// Row and column titles have been defined.
|
|
// Row title has been defined.
|
|
$this->store_name_long(
|
|
$worksheet->index,
|
|
0x07, // NAME type
|
|
$rowmin,
|
|
$rowmax,
|
|
$colmin,
|
|
$colmax
|
|
);
|
|
}
|
|
elseif (isset($rowmin))
|
|
{
|
|
// Row title has been defined.
|
|
$this->store_name_short(
|
|
$worksheet->index,
|
|
0x07, // NAME type
|
|
$rowmin,
|
|
$rowmax,
|
|
0x00,
|
|
0xff
|
|
);
|
|
}
|
|
elseif (isset($colmin))
|
|
{
|
|
// Column title has been defined.
|
|
$this->store_name_short(
|
|
$worksheet->index,
|
|
0x07, // NAME type
|
|
0x0000,
|
|
0x3fff,
|
|
$colmin,
|
|
$colmax
|
|
);
|
|
}
|
|
else {
|
|
// Print title hasn't been defined.
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* BIFF RECORDS
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* Write Excel BIFF WINDOW1 record.
|
|
*/
|
|
function _store_window1()
|
|
{
|
|
$record = 0x003D; // Record identifier
|
|
$length = 0x0012; // Number of bytes to follow
|
|
|
|
$xWn = 0x0000; // Horizontal position of window
|
|
$yWn = 0x0000; // Vertical position of window
|
|
$dxWn = 0x25BC; // Width of window
|
|
$dyWn = 0x1572; // Height of window
|
|
|
|
$grbit = 0x0038; // Option flags
|
|
$ctabsel = $this->selected; // Number of workbook tabs selected
|
|
$wTabRatio = 0x0258; // Tab to scrollbar ratio
|
|
|
|
$itabFirst = $this->firstsheet; // 1st displayed worksheet
|
|
$itabCur = $this->activesheet; // Active worksheet
|
|
|
|
$header = pack("vv", $record, $length);
|
|
$data = pack("vvvvvvvvv", $xWn, $yWn, $dxWn, $dyWn,
|
|
$grbit,
|
|
$itabCur, $itabFirst,
|
|
$ctabsel, $wTabRatio);
|
|
$this->_append($header.$data);
|
|
}
|
|
|
|
/**
|
|
* Writes Excel BIFF BOUNDSHEET record.
|
|
*
|
|
* @param string $sheetname Worksheet name
|
|
* @param integer $offset Location of worksheet BOF
|
|
*/
|
|
function _store_boundsheet($sheetname,$offset)
|
|
{
|
|
$record = 0x0085; // Record identifier
|
|
$length = 0x07 + strlen($sheetname); // Number of bytes to follow
|
|
|
|
$grbit = 0x0000; // Sheet identifier
|
|
$cch = strlen($sheetname); // Length of sheet name
|
|
|
|
$header = pack("vv", $record, $length);
|
|
$data = pack("VvC", $offset, $grbit, $cch);
|
|
$this->_append($header.$data.$sheetname);
|
|
}
|
|
|
|
/**
|
|
* Write Excel BIFF STYLE records.
|
|
*/
|
|
function _store_style()
|
|
{
|
|
$record = 0x0293; // Record identifier
|
|
$length = 0x0004; // Bytes to follow
|
|
|
|
$ixfe = 0x8000; // Index to style XF
|
|
$BuiltIn = 0x00; // Built-in style
|
|
$iLevel = 0xff; // Outline style level
|
|
|
|
$header = pack("vv", $record, $length);
|
|
$data = pack("vCC", $ixfe, $BuiltIn, $iLevel);
|
|
$this->_append($header.$data);
|
|
}
|
|
|
|
|
|
/**
|
|
* Writes Excel FORMAT record for non "built-in" numerical formats.
|
|
*
|
|
* @param string $format Custom format string
|
|
* @param integer $ifmt Format index code
|
|
*/
|
|
function _store_num_format($format,$ifmt)
|
|
{
|
|
$record = 0x041E; // Record identifier
|
|
$length = 0x03 + strlen($format); // Number of bytes to follow
|
|
|
|
$cch = strlen($format); // Length of format string
|
|
|
|
$header = pack("vv", $record, $length);
|
|
$data = pack("vC", $ifmt, $cch);
|
|
$this->_append($header.$data.$format);
|
|
}
|
|
|
|
/**
|
|
* Write Excel 1904 record to indicate the date system in use.
|
|
*/
|
|
function _store_1904()
|
|
{
|
|
$record = 0x0022; // Record identifier
|
|
$length = 0x0002; // Bytes to follow
|
|
|
|
$f1904 = $this->_1904; // Flag for 1904 date system
|
|
|
|
$header = pack("vv", $record, $length);
|
|
$data = pack("v", $f1904);
|
|
$this->_append($header.$data);
|
|
}
|
|
|
|
|
|
/**
|
|
* Write BIFF record EXTERNCOUNT to indicate the number of external sheet
|
|
* references in the workbook.
|
|
*
|
|
* Excel only stores references to external sheets that are used in NAME.
|
|
* The workbook NAME record is required to define the print area and the repeat
|
|
* rows and columns.
|
|
*
|
|
* A similar method is used in Worksheet.php for a slightly different purpose.
|
|
*
|
|
* @param integer $cxals Number of external references
|
|
*/
|
|
function _store_externcount($cxals)
|
|
{
|
|
$record = 0x0016; // Record identifier
|
|
$length = 0x0002; // Number of bytes to follow
|
|
|
|
$header = pack("vv", $record, $length);
|
|
$data = pack("v", $cxals);
|
|
$this->_append($header.$data);
|
|
}
|
|
|
|
|
|
/**
|
|
* Writes the Excel BIFF EXTERNSHEET record. These references are used by
|
|
* formulas. NAME record is required to define the print area and the repeat
|
|
* rows and columns.
|
|
*
|
|
* A similar method is used in Worksheet.php for a slightly different purpose.
|
|
*
|
|
* @param string $sheetname Worksheet name
|
|
*/
|
|
function _store_externsheet($sheetname)
|
|
{
|
|
$record = 0x0017; // Record identifier
|
|
$length = 0x02 + strlen($sheetname); // Number of bytes to follow
|
|
|
|
$cch = strlen($sheetname); // Length of sheet name
|
|
$rgch = 0x03; // Filename encoding
|
|
|
|
$header = pack("vv", $record, $length);
|
|
$data = pack("CC", $cch, $rgch);
|
|
$this->_append($header.$data.$sheetname);
|
|
}
|
|
|
|
|
|
/**
|
|
* Store the NAME record in the short format that is used for storing the print
|
|
* area, repeat rows only and repeat columns only.
|
|
*
|
|
* @param integer $index Sheet index
|
|
* @param integer $type Built-in name type
|
|
* @param integer $rowmin Start row
|
|
* @param integer $rowmax End row
|
|
* @param integer $colmin Start colum
|
|
* @param integer $colmax End column
|
|
*/
|
|
function store_name_short($index,$type,$rowmin,$rowmax,$colmin,$colmax)
|
|
{
|
|
$record = 0x0018; // Record identifier
|
|
$length = 0x0024; // Number of bytes to follow
|
|
|
|
$grbit = 0x0020; // Option flags
|
|
$chKey = 0x00; // Keyboard shortcut
|
|
$cch = 0x01; // Length of text name
|
|
$cce = 0x0015; // Length of text definition
|
|
$ixals = $index + 1; // Sheet index
|
|
$itab = $ixals; // Equal to ixals
|
|
$cchCustMenu = 0x00; // Length of cust menu text
|
|
$cchDescription = 0x00; // Length of description text
|
|
$cchHelptopic = 0x00; // Length of help topic text
|
|
$cchStatustext = 0x00; // Length of status bar text
|
|
$rgch = $type; // Built-in name type
|
|
|
|
$unknown03 = 0x3b;
|
|
$unknown04 = 0xffff-$index;
|
|
$unknown05 = 0x0000;
|
|
$unknown06 = 0x0000;
|
|
$unknown07 = 0x1087;
|
|
$unknown08 = 0x8005;
|
|
|
|
$header = pack("vv", $record, $length);
|
|
$data = pack("v", $grbit);
|
|
$data .= pack("C", $chKey);
|
|
$data .= pack("C", $cch);
|
|
$data .= pack("v", $cce);
|
|
$data .= pack("v", $ixals);
|
|
$data .= pack("v", $itab);
|
|
$data .= pack("C", $cchCustMenu);
|
|
$data .= pack("C", $cchDescription);
|
|
$data .= pack("C", $cchHelptopic);
|
|
$data .= pack("C", $cchStatustext);
|
|
$data .= pack("C", $rgch);
|
|
$data .= pack("C", $unknown03);
|
|
$data .= pack("v", $unknown04);
|
|
$data .= pack("v", $unknown05);
|
|
$data .= pack("v", $unknown06);
|
|
$data .= pack("v", $unknown07);
|
|
$data .= pack("v", $unknown08);
|
|
$data .= pack("v", $index);
|
|
$data .= pack("v", $index);
|
|
$data .= pack("v", $rowmin);
|
|
$data .= pack("v", $rowmax);
|
|
$data .= pack("C", $colmin);
|
|
$data .= pack("C", $colmax);
|
|
$this->_append($header.$data);
|
|
}
|
|
|
|
|
|
/**
|
|
* Store the NAME record in the long format that is used for storing the repeat
|
|
* rows and columns when both are specified. This share a lot of code with
|
|
* _store_name_short() but we use a separate method to keep the code clean.
|
|
* Code abstraction for reuse can be carried too far, and I should know. ;-)
|
|
*
|
|
* @param integer $index Sheet index
|
|
* @param integer $type Built-in name type
|
|
* @param integer $rowmin Start row
|
|
* @param integer $rowmax End row
|
|
* @param integer $colmin Start colum
|
|
* @param integer $colmax End column
|
|
*/
|
|
function store_name_long($index,$type,$rowmin,$rowmax,$colmin,$colmax)
|
|
{
|
|
$record = 0x0018; // Record identifier
|
|
$length = 0x003d; // Number of bytes to follow
|
|
$grbit = 0x0020; // Option flags
|
|
$chKey = 0x00; // Keyboard shortcut
|
|
$cch = 0x01; // Length of text name
|
|
$cce = 0x002e; // Length of text definition
|
|
$ixals = $index + 1; // Sheet index
|
|
$itab = $ixals; // Equal to ixals
|
|
$cchCustMenu = 0x00; // Length of cust menu text
|
|
$cchDescription = 0x00; // Length of description text
|
|
$cchHelptopic = 0x00; // Length of help topic text
|
|
$cchStatustext = 0x00; // Length of status bar text
|
|
$rgch = $type; // Built-in name type
|
|
|
|
$unknown01 = 0x29;
|
|
$unknown02 = 0x002b;
|
|
$unknown03 = 0x3b;
|
|
$unknown04 = 0xffff-$index;
|
|
$unknown05 = 0x0000;
|
|
$unknown06 = 0x0000;
|
|
$unknown07 = 0x1087;
|
|
$unknown08 = 0x8008;
|
|
|
|
$header = pack("vv", $record, $length);
|
|
$data = pack("v", $grbit);
|
|
$data .= pack("C", $chKey);
|
|
$data .= pack("C", $cch);
|
|
$data .= pack("v", $cce);
|
|
$data .= pack("v", $ixals);
|
|
$data .= pack("v", $itab);
|
|
$data .= pack("C", $cchCustMenu);
|
|
$data .= pack("C", $cchDescription);
|
|
$data .= pack("C", $cchHelptopic);
|
|
$data .= pack("C", $cchStatustext);
|
|
$data .= pack("C", $rgch);
|
|
$data .= pack("C", $unknown01);
|
|
$data .= pack("v", $unknown02);
|
|
// Column definition
|
|
$data .= pack("C", $unknown03);
|
|
$data .= pack("v", $unknown04);
|
|
$data .= pack("v", $unknown05);
|
|
$data .= pack("v", $unknown06);
|
|
$data .= pack("v", $unknown07);
|
|
$data .= pack("v", $unknown08);
|
|
$data .= pack("v", $index);
|
|
$data .= pack("v", $index);
|
|
$data .= pack("v", 0x0000);
|
|
$data .= pack("v", 0x3fff);
|
|
$data .= pack("C", $colmin);
|
|
$data .= pack("C", $colmax);
|
|
// Row definition
|
|
$data .= pack("C", $unknown03);
|
|
$data .= pack("v", $unknown04);
|
|
$data .= pack("v", $unknown05);
|
|
$data .= pack("v", $unknown06);
|
|
$data .= pack("v", $unknown07);
|
|
$data .= pack("v", $unknown08);
|
|
$data .= pack("v", $index);
|
|
$data .= pack("v", $index);
|
|
$data .= pack("v", $rowmin);
|
|
$data .= pack("v", $rowmax);
|
|
$data .= pack("C", 0x00);
|
|
$data .= pack("C", 0xff);
|
|
// End of data
|
|
$data .= pack("C", 0x10);
|
|
$this->_append($header.$data);
|
|
}
|
|
|
|
|
|
/**
|
|
* Stores the PALETTE biff record.
|
|
*/
|
|
function _store_palette()
|
|
{
|
|
$aref = $this->palette;
|
|
|
|
$record = 0x0092; // Record identifier
|
|
$length = 2 + 4 * count($aref); // Number of bytes to follow
|
|
$ccv = count($aref); // Number of RGB values to follow
|
|
$data = ''; // The RGB data
|
|
|
|
// Pack the RGB data
|
|
foreach($aref as $color)
|
|
{
|
|
foreach($color as $byte) {
|
|
$data .= pack("C",$byte);
|
|
}
|
|
}
|
|
|
|
$header = pack("vvv", $record, $length, $ccv);
|
|
$this->_append($header.$data);
|
|
}
|
|
}
|
|
?>
|