Subversion Repositories Applications.bazar

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
468 mathias 1
<?php
2
/*
3
*  Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
4
*
5
*  The majority of this is _NOT_ my code.  I simply ported it from the
6
*  PERL Spreadsheet::WriteExcel module.
7
*
8
*  The author of the Spreadsheet::WriteExcel module is John McNamara
9
*  <jmcnamara@cpan.org>
10
*
11
*  I _DO_ maintain this code, and John McNamara has nothing to do with the
12
*  porting of this code to PHP.  Any questions directly related to this
13
*  class library should be directed to me.
14
*
15
*  License Information:
16
*
17
*    Spreadsheet_Excel_Writer:  A library for generating Excel Spreadsheets
18
*    Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
19
*
20
*    This library is free software; you can redistribute it and/or
21
*    modify it under the terms of the GNU Lesser General Public
22
*    License as published by the Free Software Foundation; either
23
*    version 2.1 of the License, or (at your option) any later version.
24
*
25
*    This library is distributed in the hope that it will be useful,
26
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
27
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
28
*    Lesser General Public License for more details.
29
*
30
*    You should have received a copy of the GNU Lesser General Public
31
*    License along with this library; if not, write to the Free Software
32
*    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
33
*/
34
 
35
require_once('Format.php');
36
require_once('OLEwriter.php');
37
require_once('BIFFwriter.php');
38
require_once('Worksheet.php');
39
require_once('Parser.php');
40
 
41
/**
42
* Class for generating Excel Spreadsheets
43
*
44
* @author   Xavier Noguer <xnoguer@rezebra.com>
45
* @category FileFormats
46
* @package  Spreadsheet_Excel_Writer
47
*/
48
 
49
class Spreadsheet_Excel_Writer_Workbook extends Spreadsheet_Excel_Writer_BIFFwriter
50
{
51
    /**
52
    * Filename for the Workbook
53
    * @var string
54
    */
55
    var $_filename;
56
 
57
    /**
58
    * Formula parser
59
    * @var object Parser
60
    */
61
    var $_parser;
62
 
63
    /**
64
    * Flag for 1904 date system
65
    * @var integer
66
    */
67
    var $_1904;
68
 
69
    /**
70
    * The active worksheet of the workbook (0 indexed)
71
    * @var integer
72
    */
73
    var $_activesheet;
74
 
75
    /**
76
    * 1st displayed worksheet in the workbook (0 indexed)
77
    * @var integer
78
    */
79
    var $_firstsheet;
80
 
81
    /**
82
    * Number of workbook tabs selected
83
    * @var integer
84
    */
85
    var $_selected;
86
 
87
    /**
88
    * Index for creating adding new formats to the workbook
89
    * @var integer
90
    */
91
    var $_xf_index;
92
 
93
    /**
94
    * Flag for preventing close from being called twice.
95
    * @var integer
96
    * @see close()
97
    */
98
    var $_fileclosed;
99
 
100
    /**
101
    * The BIFF file size for the workbook.
102
    * @var integer
103
    * @see _calcSheetOffsets()
104
    */
105
    var $_biffsize;
106
 
107
    /**
108
    * The default sheetname for all sheets created.
109
    * @var string
110
    */
111
    var $_sheetname;
112
 
113
    /**
114
    * The default XF format.
115
    * @var object Format
116
    */
117
    var $_tmp_format;
118
 
119
    /**
120
    * Array containing references to all of this workbook's worksheets
121
    * @var array
122
    */
123
    var $_worksheets;
124
 
125
    /**
126
    * Array of sheetnames for creating the EXTERNSHEET records
127
    * @var array
128
    */
129
    var $_sheetnames;
130
 
131
    /**
132
    * Array containing references to all of this workbook's formats
133
    * @var array
134
    */
135
    var $_formats;
136
 
137
    /**
138
    * Array containing the colour palette
139
    * @var array
140
    */
141
    var $_palette;
142
 
143
    /**
144
    * The default format for URLs.
145
    * @var object Format
146
    */
147
    var $_url_format;
148
 
149
    /**
150
    * Class constructor
151
    *
152
    * @param string filename for storing the workbook. "-" for writing to stdout.
153
    * @access public
154
    */
155
    function Spreadsheet_Excel_Writer_Workbook($filename)
156
    {
157
        // It needs to call its parent's constructor explicitly
158
        $this->Spreadsheet_Excel_Writer_BIFFwriter();
159
 
160
        $this->_filename         = $filename;
161
        $this->_parser           =& new Spreadsheet_Excel_Writer_Parser($this->_byte_order);
162
        $this->_1904             = 0;
163
        $this->_activesheet      = 0;
164
        $this->_firstsheet       = 0;
165
        $this->_selected         = 0;
166
        $this->_xf_index         = 16; // 15 style XF's and 1 cell XF.
167
        $this->_fileclosed       = 0;
168
        $this->_biffsize         = 0;
169
        $this->_sheetname        = "Sheet";
170
        $this->_tmp_format       =& new Spreadsheet_Excel_Writer_Format();
171
        $this->_worksheets       = array();
172
        $this->_sheetnames       = array();
173
        $this->_formats          = array();
174
        $this->_palette          = array();
175
 
176
        // Add the default format for hyperlinks
177
        $this->_url_format =& $this->addFormat(array('color' => 'blue', 'underline' => 1));
178
 
179
        $this->_setPaletteXl97();
180
    }
181
 
182
    /**
183
    * Calls finalization methods.
184
    * This method should always be the last one to be called on every workbook
185
    *
186
    * @access public
187
    */
188
    function close()
189
    {
190
        if ($this->_fileclosed) { // Prevent close() from being called twice.
191
            return;
192
        }
193
        $this->_storeWorkbook();
194
        $this->_fileclosed = 1;
195
    }
196
 
197
 
198
    /**
199
    * An accessor for the _worksheets[] array
200
    * Returns an array of the worksheet objects in a workbook
201
    * It actually calls to worksheets()
202
    *
203
    * @access public
204
    * @see worksheets()
205
    * @return array
206
    */
207
    function sheets()
208
    {
209
        return $this->worksheets();
210
    }
211
 
212
    /**
213
    * An accessor for the _worksheets[] array.
214
    * Returns an array of the worksheet objects in a workbook
215
    *
216
    * @access public
217
    * @return array
218
    */
219
    function worksheets()
220
    {
221
        return($this->_worksheets);
222
    }
223
 
224
    /**
225
    * Add a new worksheet to the Excel workbook.
226
    * If no name is given the name of the worksheet will be Sheeti$i, with
227
    * $i in [1..].
228
    *
229
    * @access public
230
    * @param string $name the optional name of the worksheet
231
    * @return &Spreadsheet_Excel_Writer_Worksheet reference to a worksheet object
232
    */
233
    function &addWorksheet($name = '')
234
    {
235
        $index     = count($this->_worksheets);
236
        $sheetname = $this->_sheetname;
237
 
238
        if($name == '') {
239
            $name = $sheetname.($index+1);
240
        }
241
 
242
        // Check that sheetname is <= 31 chars (Excel limit).
243
        if(strlen($name) > 31) {
244
            $this->raiseError("Sheetname $name must be <= 31 chars");
245
        }
246
 
247
        // Check that the worksheet name doesn't already exist: a fatal Excel error.
248
        for($i=0; $i < count($this->_worksheets); $i++)
249
        {
250
            if($name == $this->_worksheets[$i]->getName()) {
251
                $this->raiseError("Worksheet '$name' already exists");
252
            }
253
        }
254
 
255
        $worksheet = new Spreadsheet_Excel_Writer_Worksheet($name,$index,$this->_activesheet,
256
                                   $this->_firstsheet,$this->_url_format,
257
                                   $this->_parser);
258
 
259
        $this->_worksheets[$index] = &$worksheet;    // Store ref for iterator
260
        $this->_sheetnames[$index] = $name;          // Store EXTERNSHEET names
261
        $this->_parser->setExtSheet($name, $index);  // Register worksheet name with parser
262
        return($worksheet);
263
    }
264
 
265
    /**
266
    * Add a new format to the Excel workbook.
267
    * Also, pass any properties to the Format constructor.
268
    *
269
    * @access public
270
    * @param array $properties array with properties for initializing the format.
271
    * @return &Spreadsheet_Excel_Writer_Format reference to an Excel Format
272
    */
273
    function &addFormat($properties = array())
274
    {
275
        $format = new Spreadsheet_Excel_Writer_Format($this->_xf_index,$properties);
276
        $this->_xf_index += 1;
277
        $this->_formats[] = &$format;
278
        return($format);
279
    }
280
 
281
    /**
282
    * Change the RGB components of the elements in the colour palette.
283
    *
284
    * @access public
285
    * @param integer $index colour index
286
    * @param integer $red   red RGB value [0-255]
287
    * @param integer $green green RGB value [0-255]
288
    * @param integer $blue  blue RGB value [0-255]
289
    * @return integer The palette index for the custom color
290
    */
291
    function setCustomColor($index,$red,$green,$blue)
292
    {
293
        // Match a HTML #xxyyzz style parameter
294
        /*if (defined $_[1] and $_[1] =~ /^#(\w\w)(\w\w)(\w\w)/ ) {
295
            @_ = ($_[0], hex $1, hex $2, hex $3);
296
        }*/
297
 
298
        // Check that the colour index is the right range
299
        if ($index < 8 or $index > 64) {
300
            // TODO: assign real error codes
301
            $this->raiseError("Color index $index outside range: 8 <= index <= 64",0,PEAR_ERROR_DIE);
302
        }
303
 
304
        // Check that the colour components are in the right range
305
        if ( ($red   < 0 or $red   > 255) or
306
             ($green < 0 or $green > 255) or
307
             ($blue  < 0 or $blue  > 255) )
308
        {
309
            $this->raiseError("Color component outside range: 0 <= color <= 255");
310
        }
311
 
312
        $index -= 8; // Adjust colour index (wingless dragonfly)
313
 
314
        // Set the RGB value
315
        $this->_palette[$index] = array($red, $green, $blue, 0);
316
        return($index + 8);
317
    }
318
 
319
    /**
320
    * Sets the colour palette to the Excel 97+ default.
321
    *
322
    * @access private
323
    */
324
    function _setPaletteXl97()
325
    {
326
        $this->_palette = array(
327
                           array(0x00, 0x00, 0x00, 0x00),   // 8
328
                           array(0xff, 0xff, 0xff, 0x00),   // 9
329
                           array(0xff, 0x00, 0x00, 0x00),   // 10
330
                           array(0x00, 0xff, 0x00, 0x00),   // 11
331
                           array(0x00, 0x00, 0xff, 0x00),   // 12
332
                           array(0xff, 0xff, 0x00, 0x00),   // 13
333
                           array(0xff, 0x00, 0xff, 0x00),   // 14
334
                           array(0x00, 0xff, 0xff, 0x00),   // 15
335
                           array(0x80, 0x00, 0x00, 0x00),   // 16
336
                           array(0x00, 0x80, 0x00, 0x00),   // 17
337
                           array(0x00, 0x00, 0x80, 0x00),   // 18
338
                           array(0x80, 0x80, 0x00, 0x00),   // 19
339
                           array(0x80, 0x00, 0x80, 0x00),   // 20
340
                           array(0x00, 0x80, 0x80, 0x00),   // 21
341
                           array(0xc0, 0xc0, 0xc0, 0x00),   // 22
342
                           array(0x80, 0x80, 0x80, 0x00),   // 23
343
                           array(0x99, 0x99, 0xff, 0x00),   // 24
344
                           array(0x99, 0x33, 0x66, 0x00),   // 25
345
                           array(0xff, 0xff, 0xcc, 0x00),   // 26
346
                           array(0xcc, 0xff, 0xff, 0x00),   // 27
347
                           array(0x66, 0x00, 0x66, 0x00),   // 28
348
                           array(0xff, 0x80, 0x80, 0x00),   // 29
349
                           array(0x00, 0x66, 0xcc, 0x00),   // 30
350
                           array(0xcc, 0xcc, 0xff, 0x00),   // 31
351
                           array(0x00, 0x00, 0x80, 0x00),   // 32
352
                           array(0xff, 0x00, 0xff, 0x00),   // 33
353
                           array(0xff, 0xff, 0x00, 0x00),   // 34
354
                           array(0x00, 0xff, 0xff, 0x00),   // 35
355
                           array(0x80, 0x00, 0x80, 0x00),   // 36
356
                           array(0x80, 0x00, 0x00, 0x00),   // 37
357
                           array(0x00, 0x80, 0x80, 0x00),   // 38
358
                           array(0x00, 0x00, 0xff, 0x00),   // 39
359
                           array(0x00, 0xcc, 0xff, 0x00),   // 40
360
                           array(0xcc, 0xff, 0xff, 0x00),   // 41
361
                           array(0xcc, 0xff, 0xcc, 0x00),   // 42
362
                           array(0xff, 0xff, 0x99, 0x00),   // 43
363
                           array(0x99, 0xcc, 0xff, 0x00),   // 44
364
                           array(0xff, 0x99, 0xcc, 0x00),   // 45
365
                           array(0xcc, 0x99, 0xff, 0x00),   // 46
366
                           array(0xff, 0xcc, 0x99, 0x00),   // 47
367
                           array(0x33, 0x66, 0xff, 0x00),   // 48
368
                           array(0x33, 0xcc, 0xcc, 0x00),   // 49
369
                           array(0x99, 0xcc, 0x00, 0x00),   // 50
370
                           array(0xff, 0xcc, 0x00, 0x00),   // 51
371
                           array(0xff, 0x99, 0x00, 0x00),   // 52
372
                           array(0xff, 0x66, 0x00, 0x00),   // 53
373
                           array(0x66, 0x66, 0x99, 0x00),   // 54
374
                           array(0x96, 0x96, 0x96, 0x00),   // 55
375
                           array(0x00, 0x33, 0x66, 0x00),   // 56
376
                           array(0x33, 0x99, 0x66, 0x00),   // 57
377
                           array(0x00, 0x33, 0x00, 0x00),   // 58
378
                           array(0x33, 0x33, 0x00, 0x00),   // 59
379
                           array(0x99, 0x33, 0x00, 0x00),   // 60
380
                           array(0x99, 0x33, 0x66, 0x00),   // 61
381
                           array(0x33, 0x33, 0x99, 0x00),   // 62
382
                           array(0x33, 0x33, 0x33, 0x00),   // 63
383
                         );
384
    }
385
 
386
    /**
387
    * Assemble worksheets into a workbook and send the BIFF data to an OLE
388
    * storage.
389
    *
390
    * @access private
391
    */
392
    function _storeWorkbook()
393
    {
394
        // Ensure that at least one worksheet has been selected.
395
        if ($this->_activesheet == 0)
396
        {
397
            $this->_worksheets[0]->selected = 1;
398
        }
399
 
400
        // Calculate the number of selected worksheet tabs and call the finalization
401
        // methods for each worksheet
402
        for($i=0; $i < count($this->_worksheets); $i++)
403
        {
404
            if($this->_worksheets[$i]->selected) {
405
                $this->_selected++;
406
            }
407
            $this->_worksheets[$i]->close($this->_sheetnames);
408
        }
409
 
410
        // Add Workbook globals
411
        $this->_storeBof(0x0005);
412
        $this->_storeExterns();    // For print area and repeat rows
413
        $this->_storeNames();      // For print area and repeat rows
414
        $this->_storeWindow1();
415
        $this->_store1904();
416
        $this->_storeAllFonts();
417
        $this->_storeAllNumFormats();
418
        $this->_storeAllXfs();
419
        $this->_storeAllStyles();
420
        $this->_storePalette();
421
        $this->_calcSheetOffsets();
422
 
423
        // Add BOUNDSHEET records
424
        for($i=0; $i < count($this->_worksheets); $i++) {
425
            $this->_storeBoundsheet($this->_worksheets[$i]->name,$this->_worksheets[$i]->offset);
426
        }
427
 
428
        // End Workbook globals
429
        $this->_storeEof();
430
 
431
        // Store the workbook in an OLE container
432
        $this->_storeOLEFile();
433
    }
434
 
435
    /**
436
    * Store the workbook in an OLE container if the total size of the workbook data
437
    * is less than ~ 7MB.
438
    *
439
    * @access private
440
    */
441
    function _storeOLEFile()
442
    {
443
        $OLE  = new Spreadsheet_Excel_Writer_OLEwriter($this->_filename);
444
        $this->_tmp_filename = $OLE->_tmp_filename;
445
        // Write Worksheet data if data <~ 7MB
446
        if ($OLE->setSize($this->_biffsize))
447
        {
448
            $OLE->writeHeader();
449
            $OLE->write($this->_data);
450
            foreach($this->_worksheets as $sheet)
451
            {
452
                while ($tmp = $sheet->getData()) {
453
                    $OLE->write($tmp);
454
                }
455
            }
456
        }
457
        $OLE->close();
458
    }
459
 
460
    /**
461
    * Calculate offsets for Worksheet BOF records.
462
    *
463
    * @access private
464
    */
465
    function _calcSheetOffsets()
466
    {
467
        $BOF     = 11;
468
        $EOF     = 4;
469
        $offset  = $this->_datasize;
470
        for($i=0; $i < count($this->_worksheets); $i++) {
471
            $offset += $BOF + strlen($this->_worksheets[$i]->name);
472
        }
473
        $offset += $EOF;
474
        for($i=0; $i < count($this->_worksheets); $i++) {
475
            $this->_worksheets[$i]->offset = $offset;
476
            $offset += $this->_worksheets[$i]->_datasize;
477
        }
478
        $this->_biffsize = $offset;
479
    }
480
 
481
    /**
482
    * Store the Excel FONT records.
483
    *
484
    * @access private
485
    */
486
    function _storeAllFonts()
487
    {
488
        // tmp_format is added by the constructor. We use this to write the default XF's
489
        $format = $this->_tmp_format;
490
        $font   = $format->getFont();
491
 
492
        // Note: Fonts are 0-indexed. According to the SDK there is no index 4,
493
        // so the following fonts are 0, 1, 2, 3, 5
494
        //
495
        for($i=1; $i <= 5; $i++){
496
            $this->_append($font);
497
        }
498
 
499
        // Iterate through the XF objects and write a FONT record if it isn't the
500
        // same as the default FONT and if it hasn't already been used.
501
        //
502
        $fonts = array();
503
        $index = 6;                  // The first user defined FONT
504
 
505
        $key = $format->getFontKey(); // The default font from _tmp_format
506
        $fonts[$key] = 0;               // Index of the default font
507
 
508
        for($i=0; $i < count($this->_formats); $i++) {
509
            $key = $this->_formats[$i]->getFontKey();
510
            if (isset($fonts[$key])) {
511
                // FONT has already been used
512
                $this->_formats[$i]->font_index = $fonts[$key];
513
            }
514
            else {
515
                // Add a new FONT record
516
                $fonts[$key]        = $index;
517
                $this->_formats[$i]->font_index = $index;
518
                $index++;
519
                $font = $this->_formats[$i]->getFont();
520
                $this->_append($font);
521
            }
522
        }
523
    }
524
 
525
    /**
526
    * Store user defined numerical formats i.e. FORMAT records
527
    *
528
    * @access private
529
    */
530
    function _storeAllNumFormats()
531
    {
532
        // Leaning num_format syndrome
533
        $hash_num_formats = array();
534
        $num_formats      = array();
535
        $index = 164;
536
 
537
        // Iterate through the XF objects and write a FORMAT record if it isn't a
538
        // built-in format type and if the FORMAT string hasn't already been used.
539
        //
540
        for($i=0; $i < count($this->_formats); $i++)
541
        {
542
            $num_format = $this->_formats[$i]->_num_format;
543
 
544
            // Check if $num_format is an index to a built-in format.
545
            // Also check for a string of zeros, which is a valid format string
546
            // but would evaluate to zero.
547
            //
548
            if (!preg_match("/^0+\d/",$num_format))
549
            {
550
                if (preg_match("/^\d+$/",$num_format)) { // built-in format
551
                    continue;
552
                }
553
            }
554
 
555
            if (isset($hash_num_formats[$num_format])) {
556
                // FORMAT has already been used
557
                $this->_formats[$i]->_num_format = $hash_num_formats[$num_format];
558
            }
559
            else{
560
                // Add a new FORMAT
561
                $hash_num_formats[$num_format]  = $index;
562
                $this->_formats[$i]->_num_format = $index;
563
                array_push($num_formats,$num_format);
564
                $index++;
565
            }
566
        }
567
 
568
        // Write the new FORMAT records starting from 0xA4
569
        $index = 164;
570
        foreach ($num_formats as $num_format) {
571
            $this->_storeNumFormat($num_format,$index);
572
            $index++;
573
        }
574
    }
575
 
576
    /**
577
    * Write all XF records.
578
    *
579
    * @access private
580
    */
581
    function _storeAllXfs()
582
    {
583
        // _tmp_format is added by the constructor. We use this to write the default XF's
584
        // The default font index is 0
585
        //
586
        $format = $this->_tmp_format;
587
        for ($i=0; $i <= 14; $i++) {
588
            $xf = $format->getXf('style'); // Style XF
589
            $this->_append($xf);
590
        }
591
 
592
        $xf = $format->getXf('cell');      // Cell XF
593
        $this->_append($xf);
594
 
595
        // User defined XFs
596
        for($i=0; $i < count($this->_formats); $i++) {
597
            $xf = $this->_formats[$i]->getXf('cell');
598
            $this->_append($xf);
599
        }
600
    }
601
 
602
    /**
603
    * Write all STYLE records.
604
    *
605
    * @access private
606
    */
607
    function _storeAllStyles()
608
    {
609
        $this->_storeStyle();
610
    }
611
 
612
    /**
613
    * Write the EXTERNCOUNT and EXTERNSHEET records. These are used as indexes for
614
    * the NAME records.
615
    *
616
    * @access private
617
    */
618
    function _storeExterns()
619
    {
620
        // Create EXTERNCOUNT with number of worksheets
621
        $this->_storeExterncount(count($this->_worksheets));
622
 
623
        // Create EXTERNSHEET for each worksheet
624
        foreach ($this->_sheetnames as $sheetname) {
625
            $this->_storeExternsheet($sheetname);
626
        }
627
    }
628
 
629
    /**
630
    * Write the NAME record to define the print area and the repeat rows and cols.
631
    *
632
    * @access private
633
    */
634
    function _storeNames()
635
    {
636
        // Create the print area NAME records
637
        foreach ($this->_worksheets as $worksheet) {
638
            // Write a Name record if the print area has been defined
639
            if (isset($worksheet->print_rowmin))
640
            {
641
                $this->_storeNameShort(
642
                    $worksheet->index,
643
                    0x06, // NAME type
644
                    $worksheet->print_rowmin,
645
                    $worksheet->print_rowmax,
646
                    $worksheet->print_colmin,
647
                    $worksheet->print_colmax
648
                    );
649
            }
650
        }
651
 
652
        // Create the print title NAME records
653
        foreach ($this->_worksheets as $worksheet)
654
        {
655
            $rowmin = $worksheet->title_rowmin;
656
            $rowmax = $worksheet->title_rowmax;
657
            $colmin = $worksheet->title_colmin;
658
            $colmax = $worksheet->title_colmax;
659
 
660
            // Determine if row + col, row, col or nothing has been defined
661
            // and write the appropriate record
662
            //
663
            if (isset($rowmin) and isset($colmin)) {
664
                // Row and column titles have been defined.
665
                // Row title has been defined.
666
                $this->_storeNameLong(
667
                    $worksheet->index,
668
                    0x07, // NAME type
669
                    $rowmin,
670
                    $rowmax,
671
                    $colmin,
672
                    $colmax
673
                    );
674
            }
675
            elseif (isset($rowmin)) {
676
                // Row title has been defined.
677
                $this->_storeNameShort(
678
                    $worksheet->index,
679
                    0x07, // NAME type
680
                    $rowmin,
681
                    $rowmax,
682
                    0x00,
683
                    0xff
684
                    );
685
            }
686
            elseif (isset($colmin)) {
687
                // Column title has been defined.
688
                $this->_storeNameShort(
689
                    $worksheet->index,
690
                    0x07, // NAME type
691
                    0x0000,
692
                    0x3fff,
693
                    $colmin,
694
                    $colmax
695
                    );
696
            }
697
            else {
698
                // Print title hasn't been defined.
699
            }
700
        }
701
    }
702
 
703
 
704
 
705
 
706
    /******************************************************************************
707
    *
708
    * BIFF RECORDS
709
    *
710
    */
711
 
712
    /**
713
    * Write Excel BIFF WINDOW1 record.
714
    *
715
    * @access private
716
    */
717
    function _storeWindow1()
718
    {
719
        $record    = 0x003D;                 // Record identifier
720
        $length    = 0x0012;                 // Number of bytes to follow
721
 
722
        $xWn       = 0x0000;                 // Horizontal position of window
723
        $yWn       = 0x0000;                 // Vertical position of window
724
        $dxWn      = 0x25BC;                 // Width of window
725
        $dyWn      = 0x1572;                 // Height of window
726
 
727
        $grbit     = 0x0038;                 // Option flags
728
        $ctabsel   = $this->_selected;       // Number of workbook tabs selected
729
        $wTabRatio = 0x0258;                 // Tab to scrollbar ratio
730
 
731
        $itabFirst = $this->_firstsheet;     // 1st displayed worksheet
732
        $itabCur   = $this->_activesheet;    // Active worksheet
733
 
734
        $header    = pack("vv",        $record, $length);
735
        $data      = pack("vvvvvvvvv", $xWn, $yWn, $dxWn, $dyWn,
736
                                       $grbit,
737
                                       $itabCur, $itabFirst,
738
                                       $ctabsel, $wTabRatio);
739
        $this->_append($header.$data);
740
    }
741
 
742
    /**
743
    * Writes Excel BIFF BOUNDSHEET record.
744
    *
745
    * @param string  $sheetname Worksheet name
746
    * @param integer $offset    Location of worksheet BOF
747
    * @access private
748
    */
749
    function _storeBoundsheet($sheetname,$offset)
750
    {
751
        $record    = 0x0085;                    // Record identifier
752
        $length    = 0x07 + strlen($sheetname); // Number of bytes to follow
753
 
754
        $grbit     = 0x0000;                    // Sheet identifier
755
        $cch       = strlen($sheetname);        // Length of sheet name
756
 
757
        $header    = pack("vv",  $record, $length);
758
        $data      = pack("VvC", $offset, $grbit, $cch);
759
        $this->_append($header.$data.$sheetname);
760
    }
761
 
762
    /**
763
    * Write Excel BIFF STYLE records.
764
    *
765
    * @access private
766
    */
767
    function _storeStyle()
768
    {
769
        $record    = 0x0293;   // Record identifier
770
        $length    = 0x0004;   // Bytes to follow
771
 
772
        $ixfe      = 0x8000;   // Index to style XF
773
        $BuiltIn   = 0x00;     // Built-in style
774
        $iLevel    = 0xff;     // Outline style level
775
 
776
        $header    = pack("vv",  $record, $length);
777
        $data      = pack("vCC", $ixfe, $BuiltIn, $iLevel);
778
        $this->_append($header.$data);
779
    }
780
 
781
 
782
    /**
783
    * Writes Excel FORMAT record for non "built-in" numerical formats.
784
    *
785
    * @param string  $format Custom format string
786
    * @param integer $ifmt   Format index code
787
    * @access private
788
    */
789
    function _storeNumFormat($format,$ifmt)
790
    {
791
        $record    = 0x041E;                      // Record identifier
792
        $length    = 0x03 + strlen($format);      // Number of bytes to follow
793
 
794
        $cch       = strlen($format);             // Length of format string
795
 
796
        $header    = pack("vv", $record, $length);
797
        $data      = pack("vC", $ifmt, $cch);
798
        $this->_append($header.$data.$format);
799
    }
800
 
801
    /**
802
    * Write Excel 1904 record to indicate the date system in use.
803
    *
804
    * @access private
805
    */
806
    function _store1904()
807
    {
808
        $record    = 0x0022;         // Record identifier
809
        $length    = 0x0002;         // Bytes to follow
810
 
811
        $f1904     = $this->_1904;   // Flag for 1904 date system
812
 
813
        $header    = pack("vv", $record, $length);
814
        $data      = pack("v", $f1904);
815
        $this->_append($header.$data);
816
    }
817
 
818
 
819
    /**
820
    * Write BIFF record EXTERNCOUNT to indicate the number of external sheet
821
    * references in the workbook.
822
    *
823
    * Excel only stores references to external sheets that are used in NAME.
824
    * The workbook NAME record is required to define the print area and the repeat
825
    * rows and columns.
826
    *
827
    * A similar method is used in Worksheet.php for a slightly different purpose.
828
    *
829
    * @param integer $cxals Number of external references
830
    * @access private
831
    */
832
    function _storeExterncount($cxals)
833
    {
834
        $record   = 0x0016;          // Record identifier
835
        $length   = 0x0002;          // Number of bytes to follow
836
 
837
        $header   = pack("vv", $record, $length);
838
        $data     = pack("v",  $cxals);
839
        $this->_append($header.$data);
840
    }
841
 
842
 
843
    /**
844
    * Writes the Excel BIFF EXTERNSHEET record. These references are used by
845
    * formulas. NAME record is required to define the print area and the repeat
846
    * rows and columns.
847
    *
848
    * A similar method is used in Worksheet.php for a slightly different purpose.
849
    *
850
    * @param string $sheetname Worksheet name
851
    * @access private
852
    */
853
    function _storeExternsheet($sheetname)
854
    {
855
        $record      = 0x0017;                     // Record identifier
856
        $length      = 0x02 + strlen($sheetname);  // Number of bytes to follow
857
 
858
        $cch         = strlen($sheetname);         // Length of sheet name
859
        $rgch        = 0x03;                       // Filename encoding
860
 
861
        $header      = pack("vv",  $record, $length);
862
        $data        = pack("CC", $cch, $rgch);
863
        $this->_append($header.$data.$sheetname);
864
    }
865
 
866
 
867
    /**
868
    * Store the NAME record in the short format that is used for storing the print
869
    * area, repeat rows only and repeat columns only.
870
    *
871
    * @param integer $index  Sheet index
872
    * @param integer $type   Built-in name type
873
    * @param integer $rowmin Start row
874
    * @param integer $rowmax End row
875
    * @param integer $colmin Start colum
876
    * @param integer $colmax End column
877
    * @access private
878
    */
879
    function _storeNameShort($index,$type,$rowmin,$rowmax,$colmin,$colmax)
880
    {
881
        $record          = 0x0018;       // Record identifier
882
        $length          = 0x0024;       // Number of bytes to follow
883
 
884
        $grbit           = 0x0020;       // Option flags
885
        $chKey           = 0x00;         // Keyboard shortcut
886
        $cch             = 0x01;         // Length of text name
887
        $cce             = 0x0015;       // Length of text definition
888
        $ixals           = $index + 1;   // Sheet index
889
        $itab            = $ixals;       // Equal to ixals
890
        $cchCustMenu     = 0x00;         // Length of cust menu text
891
        $cchDescription  = 0x00;         // Length of description text
892
        $cchHelptopic    = 0x00;         // Length of help topic text
893
        $cchStatustext   = 0x00;         // Length of status bar text
894
        $rgch            = $type;        // Built-in name type
895
 
896
        $unknown03       = 0x3b;
897
        $unknown04       = 0xffff-$index;
898
        $unknown05       = 0x0000;
899
        $unknown06       = 0x0000;
900
        $unknown07       = 0x1087;
901
        $unknown08       = 0x8005;
902
 
903
        $header             = pack("vv", $record, $length);
904
        $data               = pack("v", $grbit);
905
        $data              .= pack("C", $chKey);
906
        $data              .= pack("C", $cch);
907
        $data              .= pack("v", $cce);
908
        $data              .= pack("v", $ixals);
909
        $data              .= pack("v", $itab);
910
        $data              .= pack("C", $cchCustMenu);
911
        $data              .= pack("C", $cchDescription);
912
        $data              .= pack("C", $cchHelptopic);
913
        $data              .= pack("C", $cchStatustext);
914
        $data              .= pack("C", $rgch);
915
        $data              .= pack("C", $unknown03);
916
        $data              .= pack("v", $unknown04);
917
        $data              .= pack("v", $unknown05);
918
        $data              .= pack("v", $unknown06);
919
        $data              .= pack("v", $unknown07);
920
        $data              .= pack("v", $unknown08);
921
        $data              .= pack("v", $index);
922
        $data              .= pack("v", $index);
923
        $data              .= pack("v", $rowmin);
924
        $data              .= pack("v", $rowmax);
925
        $data              .= pack("C", $colmin);
926
        $data              .= pack("C", $colmax);
927
        $this->_append($header.$data);
928
    }
929
 
930
 
931
    /**
932
    * Store the NAME record in the long format that is used for storing the repeat
933
    * rows and columns when both are specified. This shares a lot of code with
934
    * _storeNameShort() but we use a separate method to keep the code clean.
935
    * Code abstraction for reuse can be carried too far, and I should know. ;-)
936
    *
937
    * @param integer $index Sheet index
938
    * @param integer $type  Built-in name type
939
    * @param integer $rowmin Start row
940
    * @param integer $rowmax End row
941
    * @param integer $colmin Start colum
942
    * @param integer $colmax End column
943
    * @access private
944
    */
945
    function _storeNameLong($index,$type,$rowmin,$rowmax,$colmin,$colmax)
946
    {
947
        $record          = 0x0018;       // Record identifier
948
        $length          = 0x003d;       // Number of bytes to follow
949
        $grbit           = 0x0020;       // Option flags
950
        $chKey           = 0x00;         // Keyboard shortcut
951
        $cch             = 0x01;         // Length of text name
952
        $cce             = 0x002e;       // Length of text definition
953
        $ixals           = $index + 1;   // Sheet index
954
        $itab            = $ixals;       // Equal to ixals
955
        $cchCustMenu     = 0x00;         // Length of cust menu text
956
        $cchDescription  = 0x00;         // Length of description text
957
        $cchHelptopic    = 0x00;         // Length of help topic text
958
        $cchStatustext   = 0x00;         // Length of status bar text
959
        $rgch            = $type;        // Built-in name type
960
 
961
        $unknown01       = 0x29;
962
        $unknown02       = 0x002b;
963
        $unknown03       = 0x3b;
964
        $unknown04       = 0xffff-$index;
965
        $unknown05       = 0x0000;
966
        $unknown06       = 0x0000;
967
        $unknown07       = 0x1087;
968
        $unknown08       = 0x8008;
969
 
970
        $header             = pack("vv",  $record, $length);
971
        $data               = pack("v", $grbit);
972
        $data              .= pack("C", $chKey);
973
        $data              .= pack("C", $cch);
974
        $data              .= pack("v", $cce);
975
        $data              .= pack("v", $ixals);
976
        $data              .= pack("v", $itab);
977
        $data              .= pack("C", $cchCustMenu);
978
        $data              .= pack("C", $cchDescription);
979
        $data              .= pack("C", $cchHelptopic);
980
        $data              .= pack("C", $cchStatustext);
981
        $data              .= pack("C", $rgch);
982
        $data              .= pack("C", $unknown01);
983
        $data              .= pack("v", $unknown02);
984
        // Column definition
985
        $data              .= pack("C", $unknown03);
986
        $data              .= pack("v", $unknown04);
987
        $data              .= pack("v", $unknown05);
988
        $data              .= pack("v", $unknown06);
989
        $data              .= pack("v", $unknown07);
990
        $data              .= pack("v", $unknown08);
991
        $data              .= pack("v", $index);
992
        $data              .= pack("v", $index);
993
        $data              .= pack("v", 0x0000);
994
        $data              .= pack("v", 0x3fff);
995
        $data              .= pack("C", $colmin);
996
        $data              .= pack("C", $colmax);
997
        // Row definition
998
        $data              .= pack("C", $unknown03);
999
        $data              .= pack("v", $unknown04);
1000
        $data              .= pack("v", $unknown05);
1001
        $data              .= pack("v", $unknown06);
1002
        $data              .= pack("v", $unknown07);
1003
        $data              .= pack("v", $unknown08);
1004
        $data              .= pack("v", $index);
1005
        $data              .= pack("v", $index);
1006
        $data              .= pack("v", $rowmin);
1007
        $data              .= pack("v", $rowmax);
1008
        $data              .= pack("C", 0x00);
1009
        $data              .= pack("C", 0xff);
1010
        // End of data
1011
        $data              .= pack("C", 0x10);
1012
        $this->_append($header.$data);
1013
    }
1014
 
1015
 
1016
    /**
1017
    * Stores the PALETTE biff record.
1018
    *
1019
    * @access private
1020
    */
1021
    function _storePalette()
1022
    {
1023
        $aref            = $this->_palette;
1024
 
1025
        $record          = 0x0092;                 // Record identifier
1026
        $length          = 2 + 4 * count($aref);   // Number of bytes to follow
1027
        $ccv             =         count($aref);   // Number of RGB values to follow
1028
        $data = '';                                // The RGB data
1029
 
1030
        // Pack the RGB data
1031
        foreach($aref as $color)
1032
        {
1033
            foreach($color as $byte) {
1034
                $data .= pack("C",$byte);
1035
            }
1036
        }
1037
 
1038
        $header = pack("vvv",  $record, $length, $ccv);
1039
        $this->_append($header.$data);
1040
    }
1041
}
1042
?>