diff --git a/README.md b/README.md
index 26de6ae5a..1dee4a110 100644
--- a/README.md
+++ b/README.md
@@ -83,15 +83,17 @@ Simple cell formats map to more advanced cell formats
Basic cell styles have been available since version 0.30
-| style | allowed values |
-| ---------- | ---- |
-| font | Arial, Times New Roman, Courier New, Comic Sans MS |
-| font-size | 8,9,10,11,12 ... |
-| font-style | bold, italic, underline, strikethrough or multiple ie: 'bold,italic' |
-| border | left, right, top, bottom, or multiple ie: 'top,left' |
-| color | #RRGGBB, ie: #ff99cc or #f9c |
-| fill | #RRGGBB, ie: #eeffee or #efe |
-| halign | general, left, right, justify, center |
-| valign | bottom, center, distributed |
+| style | allowed values |
+| ------------ | ---- |
+| font | Arial, Times New Roman, Courier New, Comic Sans MS |
+| font-size | 8,9,10,11,12 ... |
+| font-style | bold, italic, underline, strikethrough or multiple ie: 'bold,italic' |
+| border | left, right, top, bottom, or multiple ie: 'top,left' |
+| border-style | thin, medium, thick, dashDot, dashDotDot, dashed, dotted, double, hair, mediumDashDot, mediumDashDotDot, mediumDashed, slantDashDot |
+| border-color | #RRGGBB, ie: #ff99cc or #f9c |
+| color | #RRGGBB, ie: #ff99cc or #f9c |
+| fill | #RRGGBB, ie: #eeffee or #efe |
+| halign | general, left, right, justify, center |
+| valign | bottom, center, distributed |
diff --git a/example-cli.php b/example-cli.php
index 2f0ca344d..e3245422e 100644
--- a/example-cli.php
+++ b/example-cli.php
@@ -10,8 +10,9 @@
array('2003','1','-50.5','2010-01-01 23:00:00','2012-12-31 23:00:00'),
array('2003','=B1', '23.5','2010-01-01 00:00:00','2012-12-31 00:00:00'),
);
+
$writer = new XLSXWriter();
-$writer->setAuthor('Some Author');
+$writer->setAuthor('Some Author');
foreach($rows as $row)
$writer->writeSheetRow('Sheet1', $row);
diff --git a/example.php b/example.php
index d76a8dc07..38831aea7 100644
--- a/example.php
+++ b/example.php
@@ -15,8 +15,9 @@
array('2003','1','-50.5','2010-01-01 23:00:00','2012-12-31 23:00:00'),
array('2003','=B1', '23.5','2010-01-01 00:00:00','2012-12-31 00:00:00'),
);
+
$writer = new XLSXWriter();
-$writer->setAuthor('Some Author');
+$writer->setAuthor('Some Author');
foreach($rows as $row)
$writer->writeSheetRow('Sheet1', $row);
$writer->writeToStdOut();
diff --git a/examples/ex08-advanced.php b/examples/ex08-advanced.php
index 33ed7c3eb..d7c358cb3 100644
--- a/examples/ex08-advanced.php
+++ b/examples/ex08-advanced.php
@@ -3,7 +3,14 @@
include_once("xlsxwriter.class.php");
$writer = new XLSXWriter();
+$keywords = array('some','interesting','keywords');
+
+$writer->setTitle('Some Title');
+$writer->setSubject('Some Subject');
$writer->setAuthor('Some Author');
+$writer->setCompany('Some Company');
+$writer->setKeywords($keywords);
+$writer->setDescription('Some interesting description');
$writer->setTempDir(sys_get_temp_dir());//set custom tempdir
//----
diff --git a/examples/ex09-autofilter.php b/examples/ex09-autofilter.php
new file mode 100644
index 000000000..c415e05a9
--- /dev/null
+++ b/examples/ex09-autofilter.php
@@ -0,0 +1,18 @@
+writeSheetHeader('Sheet1', array('col-string'=>'string','col-numbers'=>'integer','col-timestamps'=>'datetime'), ['auto_filter'=>true, 'widths'=>[15,15,30]] );
+for($i=0; $i<1000; $i++)
+{
+ $writer->writeSheetRow('Sheet1', array(
+ str_shuffle($chars),
+ rand()%10000,
+ date('Y-m-d H:i:s',time()-(rand()%31536000))
+ ));
+}
+$writer->writeToFile('xlsx-autofilter.xlsx');
+echo '#'.floor((memory_get_peak_usage())/1024/1024)."MB"."\n";
diff --git a/examples/ex10-freeze-rows-columns.php b/examples/ex10-freeze-rows-columns.php
new file mode 100644
index 000000000..196c78091
--- /dev/null
+++ b/examples/ex10-freeze-rows-columns.php
@@ -0,0 +1,20 @@
+writeSheetHeader('Sheet1', array('c1'=>'string','c2'=>'integer','c3'=>'integer','c4'=>'integer','c5'=>'integer'), ['freeze_rows'=>1, 'freeze_columns'=>1] );
+for($i=0; $i<250; $i++)
+{
+ $writer->writeSheetRow('Sheet1', array(
+ str_shuffle($chars),
+ rand()%10000,
+ rand()%10000,
+ rand()%10000,
+ rand()%10000
+ ));
+}
+$writer->writeToFile('xlsx-freeze-rows-columns.xlsx');
+echo '#'.floor((memory_get_peak_usage())/1024/1024)."MB"."\n";
diff --git a/testbench/pairs/formats.php b/testbench/pairs/formats.php
index e39e42052..697c7bca6 100644
--- a/testbench/pairs/formats.php
+++ b/testbench/pairs/formats.php
@@ -2,7 +2,14 @@
include_once("../../xlsxwriter.class.php");
$writer = new XLSXWriter();
+$keywords = array('some','interesting','keywords');
+
+$writer->setTitle('Some Title');
+$writer->setSubject('Some Subject');
$writer->setAuthor('Some Author');
+$writer->setCompany('Some Company');
+$writer->setKeywords($keywords);
+$writer->setDescription('Some interesting description');
$header = array(
'General'=>'string',
diff --git a/testbench/pairs/test.php b/testbench/pairs/test.php
index ab2c494fa..34b7befc4 100644
--- a/testbench/pairs/test.php
+++ b/testbench/pairs/test.php
@@ -2,7 +2,14 @@
include_once("../../xlsxwriter.class.php");
$writer = new XLSXWriter();
+$keywords = array('some','interesting','keywords');
+
+$writer->setTitle('Some Title');
+$writer->setSubject('Some Subject');
$writer->setAuthor('Some Author');
+$writer->setCompany('Some Company');
+$writer->setKeywords($keywords);
+$writer->setDescription('Some interesting description');
$header = array(
'General'=>'string',
diff --git a/testbench/test.php b/testbench/test.php
index ba7ca4c4d..088bd20fe 100644
--- a/testbench/test.php
+++ b/testbench/test.php
@@ -17,7 +17,14 @@
array('2003','02','345.12'),
);
$writer = new XLSXWriter();
+$keywords = array('some','interesting','keywords');
+
+$writer->setTitle('Some Title');
+$writer->setSubject('Some Subject');
$writer->setAuthor('Some Author');
+$writer->setCompany('Some Company');
+$writer->setKeywords($keywords);
+$writer->setDescription('Some interesting description');
$writer->writeSheet($data1,'Sheet1',$header);
$writer->writeSheet($data2,'Sheet2');
$writer->writeToFile('test.xlsx');
diff --git a/xlsxwriter.class.php b/xlsxwriter.class.php
index b1e8d04d0..a987735f8 100644
--- a/xlsxwriter.class.php
+++ b/xlsxwriter.class.php
@@ -12,15 +12,19 @@ class XLSXWriter
const EXCEL_2007_MAX_ROW=1048576;
const EXCEL_2007_MAX_COL=16384;
//------------------------------------------------------------------
- protected $title ='Doc Title';
- protected $author ='Doc Author';
+ protected $title;
+ protected $subject;
+ protected $author;
+ protected $company;
+ protected $description;
+ protected $keywords = array();
+
+ protected $current_sheet;
protected $sheets = array();
protected $temp_files = array();
protected $cell_styles = array();
protected $number_formats = array();
- protected $current_sheet = '';
-
public function __construct()
{
if(!ini_get('date.timezone'))
@@ -35,7 +39,11 @@ public function __construct()
}
public function setTitle($title='') { $this->title=$title; }
+ public function setSubject($subject='') { $this->subject=$subject; }
public function setAuthor($author='') { $this->author=$author; }
+ public function setCompany($company='') { $this->company=$company; }
+ public function setKeywords($keywords='') { $this->keywords=$keywords; }
+ public function setDescription($description='') { $this->description=$description; }
public function setTempDir($tempdir='') { $this->tempdir=$tempdir; }
public function __destruct()
@@ -108,7 +116,7 @@ public function writeToFile($filename)
$zip->close();
}
- protected function initializeSheet($sheet_name, $col_widths=array() )
+ protected function initializeSheet($sheet_name, $col_widths=array(), $auto_filter=false, $freeze_rows=false, $freeze_columns=false )
{
//if already initialized
if ($this->current_sheet==$sheet_name || isset($this->sheets[$sheet_name]))
@@ -126,6 +134,9 @@ protected function initializeSheet($sheet_name, $col_widths=array() )
'merge_cells' => array(),
'max_cell_tag_start' => 0,
'max_cell_tag_end' => 0,
+ 'auto_filter' => $auto_filter,
+ 'freeze_rows' => $freeze_rows,
+ 'freeze_columns' => $freeze_columns,
'finalized' => false,
);
$sheet = &$this->sheets[$sheet_name];
@@ -141,18 +152,34 @@ protected function initializeSheet($sheet_name, $col_widths=array() )
$sheet->max_cell_tag_end = $sheet->file_writer->ftell();
$sheet->file_writer->write( '');
$sheet->file_writer->write( '');
- $sheet->file_writer->write( '');
+ if ($sheet->freeze_rows && $sheet->freeze_columns) {
+ $sheet->file_writer->write( '');
+ $sheet->file_writer->write( '');
+ $sheet->file_writer->write( '');
+ $sheet->file_writer->write( '');
+ }
+ elseif ($sheet->freeze_rows) {
+ $sheet->file_writer->write( '');
+ $sheet->file_writer->write( '');
+ }
+ elseif ($sheet->freeze_columns) {
+ $sheet->file_writer->write( '');
+ $sheet->file_writer->write( '');
+ }
+ else { // not frozen
+ $sheet->file_writer->write( '');
+ }
$sheet->file_writer->write( '');
$sheet->file_writer->write( '');
$sheet->file_writer->write( '');
$i=0;
if (!empty($col_widths)) {
foreach($col_widths as $column_width) {
- $sheet->file_writer->write( '');
+ $sheet->file_writer->write( '');
$i++;
}
}
- $sheet->file_writer->write( '');
+ $sheet->file_writer->write( '');
$sheet->file_writer->write( '');
$sheet->file_writer->write( '');
}
@@ -195,7 +222,10 @@ public function writeSheetHeader($sheet_name, array $header_types, $col_options
$style = &$col_options;
$col_widths = isset($col_options['widths']) ? (array)$col_options['widths'] : array();
- self::initializeSheet($sheet_name, $col_widths);
+ $auto_filter = isset($col_options['auto_filter']) ? intval($col_options['auto_filter']) : false;
+ $freeze_rows = isset($col_options['freeze_rows']) ? intval($col_options['freeze_rows']) : false;
+ $freeze_columns = isset($col_options['freeze_columns']) ? intval($col_options['freeze_columns']) : false;
+ self::initializeSheet($sheet_name, $col_widths, $auto_filter, $freeze_rows, $freeze_columns);
$sheet = &$this->sheets[$sheet_name];
$sheet->columns = $this->initializeColumnTypes($header_types);
if (!$suppress_row)
@@ -229,8 +259,8 @@ public function writeSheetRow($sheet_name, array $row, $row_options=null)
{
$ht = isset($row_options['height']) ? floatval($row_options['height']) : 12.1;
$customHt = isset($row_options['height']) ? true : false;
- $hidden = isset($row_options['hidden']) ? boolval($row_options['hidden']) : false;
- $collapsed = isset($row_options['collapsed']) ? boolval($row_options['collapsed']) : false;
+ $hidden = isset($row_options['hidden']) ? (bool)($row_options['hidden']) : false;
+ $collapsed = isset($row_options['collapsed']) ? (bool)($row_options['collapsed']) : false;
$sheet->file_writer->write('');
}
else
@@ -275,6 +305,12 @@ protected function finalizeSheet($sheet_name)
$sheet->file_writer->write( '');
}
+ $max_cell = self::xlsCell($sheet->row_count - 1, count($sheet->columns) - 1);
+
+ if ($sheet->auto_filter) {
+ $sheet->file_writer->write( '');
+ }
+
$sheet->file_writer->write( '');
$sheet->file_writer->write( '');
$sheet->file_writer->write( '');
@@ -284,7 +320,6 @@ protected function finalizeSheet($sheet_name)
$sheet->file_writer->write( '');
$sheet->file_writer->write('');
- $max_cell = self::xlsCell($sheet->row_count - 1, count($sheet->columns) - 1);
$max_cell_tag = '';
$padding_length = $sheet->max_cell_tag_end - $sheet->max_cell_tag_start - strlen($max_cell_tag);
$sheet->file_writer->fseek($sheet->max_cell_tag_start);
@@ -338,7 +373,7 @@ protected function writeCell(XLSXWriter_BuffererWriter &$file, $row_number, $col
} elseif ($num_format_type=='n_string') {
$file->write(''.self::xmlspecialchars($value).'');
} elseif ($num_format_type=='n_auto' || 1) { //auto-detect unknown column types
- if (!is_string($value) || $value=='0' || ($value[0]!='0' && ctype_digit($value)) || preg_match("/^\-?[1-9][0-9]*(\.[0-9]+)?$/", $value)){
+ if (!is_string($value) || $value=='0' || ($value[0]!='0' && ctype_digit($value)) || preg_match("/^\-?(0|[1-9][0-9]*)(\.[0-9]+)?$/", $value)){
$file->write(''.self::xmlspecialchars($value).'');//int,float,currency
} else { //implied: ($cell_format=='string')
$file->write(''.self::xmlspecialchars($value).'');
@@ -349,8 +384,9 @@ protected function writeCell(XLSXWriter_BuffererWriter &$file, $row_number, $col
protected function styleFontIndexes()
{
static $border_allowed = array('left','right','top','bottom');
+ static $border_style_allowed = array('thin','medium','thick','dashDot','dashDotDot','dashed','dotted','double','hair','mediumDashDot','mediumDashDotDot','mediumDashed','slantDashDot');
static $horizontal_allowed = array('general','left','right','justify','center');
- static $vertical_allowed = array('bottom','center','distributed');
+ static $vertical_allowed = array('bottom','center','distributed','top');
$default_font = array('size'=>'10','name'=>'Arial','family'=>'2');
$fills = array('','');//2 placeholders for static xml later
$fonts = array('','','','');//4 placeholders for static xml later
@@ -364,12 +400,20 @@ protected function styleFontIndexes()
$style = @json_decode($style_json_string, $as_assoc=true);
$style_indexes[$i] = array('num_fmt_idx'=>$number_format_idx);//initialize entry
- if (isset($style['border']) && is_string($style['border']))
+ if (isset($style['border']) && is_string($style['border']))//border is a comma delimited str
{
- $border_input = explode(",", $style['border']);
- sort($border_input);
- $border_value = array_intersect($border_input, $border_allowed);
- $style_indexes[$i]['border_idx'] = self::add_to_list_get_index($borders, implode(",", $border_value) );
+ $border_value['side'] = array_intersect(explode(",", $style['border']), $border_allowed);
+ if (isset($style['border-style']) && in_array($style['border-style'],$border_style_allowed))
+ {
+ $border_value['style'] = $style['border-style'];
+ }
+ if (isset($style['border-color']) && is_string($style['border-color']) && $style['border-color'][0]=='#')
+ {
+ $v = substr($style['border-color'],1,6);
+ $v = strlen($v)==3 ? $v[0].$v[0].$v[1].$v[1].$v[2].$v[2] : $v;// expand cf0 => ccff00
+ $border_value['color'] = "FF".strtoupper($v);
+ }
+ $style_indexes[$i]['border_idx'] = self::add_to_list_get_index($borders, json_encode($border_value));
}
if (isset($style['fill']) && is_string($style['fill']) && $style['fill'][0]=='#')
{
@@ -390,7 +434,7 @@ protected function styleFontIndexes()
if (isset($style['wrap_text']))
{
$style_indexes[$i]['alignment'] = true;
- $style_indexes[$i]['wrap_text'] = $style['wrap_text'];
+ $style_indexes[$i]['wrap_text'] = (bool)$style['wrap_text'];
}
$font = $default_font;
@@ -484,12 +528,15 @@ protected function writeStylesXML()
$file->write( '');
foreach($borders as $border) {
if (!empty($border)) { //fonts have an empty placeholder in the array to offset the static xml entry above
- $pieces = explode(",", $border);
+ $pieces = json_decode($border,true);
+ $border_style = !empty($pieces['style']) ? $pieces['style'] : 'hair';
+ $border_color = !empty($pieces['color']) ? '' : '';
$file->write('');
- $file->write( '');
- $file->write( '');
- $file->write( '');
- $file->write( '');
+ foreach (array('left', 'right', 'top', 'bottom') as $side)
+ {
+ $show_side = in_array($side,$pieces['side']) ? true : false;
+ $file->write($show_side ? "<$side style=\"$border_style\">$border_color$side>" : "<$side/>");
+ }
$file->write( '');
$file->write('');
}
@@ -530,7 +577,7 @@ protected function writeStylesXML()
foreach($style_indexes as $v)
{
$applyAlignment = isset($v['alignment']) ? 'true' : 'false';
- $wrapText = isset($v['wrap_text']) ? boolval($v['wrap_text']) : 'false';
+ $wrapText = !empty($v['wrap_text']) ? 'true' : 'false';
$horizAlignment = isset($v['halign']) ? $v['halign'] : 'general';
$vertAlignment = isset($v['valign']) ? $v['valign'] : 'bottom';
$applyBorder = isset($v['border_idx']) ? 'true' : 'false';
@@ -562,7 +609,10 @@ protected function buildAppXML()
{
$app_xml="";
$app_xml.=''."\n";
- $app_xml.='0';
+ $app_xml.='';
+ $app_xml.='0';
+ $app_xml.=''.self::xmlspecialchars($this->company).'';
+ $app_xml.='';
return $app_xml;
}
@@ -573,7 +623,12 @@ protected function buildCoreXML()
$core_xml.='';
$core_xml.=''.date("Y-m-d\TH:i:s.00\Z").'';//$date_time = '2014-10-25T15:54:37.00Z';
$core_xml.=''.self::xmlspecialchars($this->title).'';
+ $core_xml.=''.self::xmlspecialchars($this->subject).'';
$core_xml.=''.self::xmlspecialchars($this->author).'';
+ if (!empty($this->keywords)) {
+ $core_xml.=''.self::xmlspecialchars(implode (", ", (array)$this->keywords)).'';
+ }
+ $core_xml.=''.self::xmlspecialchars($this->description).'';
$core_xml.='0';
$core_xml.='';
return $core_xml;
@@ -607,6 +662,15 @@ protected function buildWorkbookXML()
$i++;
}
$workbook_xml.='';
+ $workbook_xml.='';
+ foreach($this->sheets as $sheet_name=>$sheet) {
+ if ($sheet->auto_filter) {
+ $sheetname = self::sanitize_sheetname($sheet->sheetname);
+ $workbook_xml.='\''.self::xmlspecialchars($sheetname).'\'!$A$1:' . self::xlsCell($sheet->row_count - 1, count($sheet->columns) - 1, true) . '';
+ $i++;
+ }
+ }
+ $workbook_xml.='';
$workbook_xml.='';
return $workbook_xml;
}
@@ -650,14 +714,18 @@ protected function buildContentTypesXML()
/*
* @param $row_number int, zero based
* @param $column_number int, zero based
- * @return Cell label/coordinates, ex: A1, C3, AA42
+ * @param $absolute bool
+ * @return Cell label/coordinates, ex: A1, C3, AA42 (or if $absolute==true: $A$1, $C$3, $AA$42)
* */
- public static function xlsCell($row_number, $column_number)
+ public static function xlsCell($row_number, $column_number, $absolute=false)
{
$n = $column_number;
for($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
$r = chr($n%26 + 0x41) . $r;
}
+ if ($absolute) {
+ return '$' . $r . '$' . ($row_number+1);
+ }
return $r . ($row_number+1);
}
//------------------------------------------------------------------
@@ -705,11 +773,11 @@ private static function determineNumberFormatType($num_format)
if ($num_format=='GENERAL') return 'n_auto';
if ($num_format=='@') return 'n_string';
if ($num_format=='0') return 'n_numeric';
- if (preg_match("/[H]{1,2}:[M]{1,2}/", $num_format)) return 'n_datetime';
- if (preg_match("/[M]{1,2}:[S]{1,2}/", $num_format)) return 'n_datetime';
- if (preg_match("/[YY]{2,4}/", $num_format)) return 'n_date';
- if (preg_match("/[D]{1,2}/", $num_format)) return 'n_date';
- if (preg_match("/[M]{1,2}/", $num_format)) return 'n_date';
+ if (preg_match("/[H]{1,2}:[M]{1,2}/i", $num_format)) return 'n_datetime';
+ if (preg_match("/[M]{1,2}:[S]{1,2}/i", $num_format)) return 'n_datetime';
+ if (preg_match("/[Y]{2,4}/i", $num_format)) return 'n_date';
+ if (preg_match("/[D]{1,2}/i", $num_format)) return 'n_date';
+ if (preg_match("/[M]{1,2}/i", $num_format)) return 'n_date';
if (preg_match("/$/", $num_format)) return 'n_numeric';
if (preg_match("/%/", $num_format)) return 'n_numeric';
if (preg_match("/0/", $num_format)) return 'n_numeric';