From fd2a3773d8dc326654696f6ad83af58aa81dd2e9 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Mon, 6 May 2013 23:39:49 +0100 Subject: [PATCH] Refactoring of canRead function in Readers, and minor fixes to Examples and documentation updates --- Classes/PHPExcel/Reader/Abstract.php | 51 ++++- Classes/PHPExcel/Reader/CSV.php | 204 +++++++----------- Classes/PHPExcel/Reader/Excel2003XML.php | 14 +- Classes/PHPExcel/Reader/HTML.php | 38 ++-- Classes/PHPExcel/Reader/SYLK.php | 68 +++--- .../PHPExcel developer documentation.doc | Bin 817152 -> 819200 bytes ...-chart.php => 33chartcreate-composite.php} | 0 Examples/33chartcreate-scatter.php | 138 ++++++++++++ Examples/34chartupdate.php | 5 +- 9 files changed, 299 insertions(+), 219 deletions(-) rename Examples/{33chartcreate-composite-chart.php => 33chartcreate-composite.php} (100%) create mode 100644 Examples/33chartcreate-scatter.php diff --git a/Classes/PHPExcel/Reader/Abstract.php b/Classes/PHPExcel/Reader/Abstract.php index 224136a5..1c61197d 100644 --- a/Classes/PHPExcel/Reader/Abstract.php +++ b/Classes/PHPExcel/Reader/Abstract.php @@ -67,6 +67,8 @@ abstract class PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader */ protected $_readFilter = NULL; + protected $_fileHandle = NULL; + /** * Read data only? @@ -79,7 +81,6 @@ abstract class PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader return $this->_readDataOnly; } - /** * Set read data only * Set to true, to advise the Reader only to read data values for cells, and to ignore any formatting information. @@ -94,7 +95,6 @@ abstract class PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader return $this; } - /** * Read charts in workbook? * If this is true, then the Reader will include any charts that exist in the workbook. @@ -107,7 +107,6 @@ abstract class PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader return $this->_includeCharts; } - /** * Set read charts in workbook * Set to true, to advise the Reader to include any charts that exist in the workbook. @@ -123,7 +122,6 @@ abstract class PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader return $this; } - /** * Get which sheets to load * Returns either an array of worksheet names (the list of worksheets that should be loaded), or a null @@ -136,7 +134,6 @@ abstract class PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader return $this->_loadSheetsOnly; } - /** * Set which sheets to load * @@ -153,7 +150,6 @@ abstract class PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader return $this; } - /** * Set all sheets to load * Tells the Reader to load all worksheets from the workbook. @@ -166,7 +162,6 @@ abstract class PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader return $this; } - /** * Read filter * @@ -176,7 +171,6 @@ abstract class PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader return $this->_readFilter; } - /** * Set read filter * @@ -188,5 +182,46 @@ abstract class PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader return $this; } + /** + * Open file for reading + * + * @param string $pFilename + * @throws PHPExcel_Reader_Exception + * @return resource + */ + protected function _openFile($pFilename) + { + // Check if file exists + if (!file_exists($pFilename) || !is_readable($pFilename)) { + throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist."); + } + + // Open file + $this->_fileHandle = fopen($pFilename, 'r'); + if ($this->_fileHandle === FALSE) { + throw new PHPExcel_Reader_Exception("Could not open file " . $pFilename . " for reading."); + } + } + + /** + * Can the current PHPExcel_Reader_IReader read the file? + * + * @param string $pFilename + * @return boolean + * @throws PHPExcel_Reader_Exception + */ + public function canRead($pFilename) + { + // Check if file exists + try { + $this->_openFile($pFilename); + } catch (Exception $e) { + return FALSE; + } + + $readable = $this->_isValidFormat(); + fclose ($this->_fileHandle); + return $readable; + } } diff --git a/Classes/PHPExcel/Reader/CSV.php b/Classes/PHPExcel/Reader/CSV.php index 4678f7dc..a0fac383 100644 --- a/Classes/PHPExcel/Reader/CSV.php +++ b/Classes/PHPExcel/Reader/CSV.php @@ -92,112 +92,104 @@ class PHPExcel_Reader_CSV extends PHPExcel_Reader_Abstract implements PHPExcel_R */ private $_contiguous = false; - /** * Row counter for loading rows contiguously * - * @access private * @var int */ private $_contiguousRow = -1; + /** * Create a new PHPExcel_Reader_CSV */ public function __construct() { $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter(); - } // function __construct() - + } /** - * Can the current PHPExcel_Reader_IReader read the file? + * Validate that the current file is a CSV file * - * @access public - * @param string $pFilename * @return boolean - * @throws PHPExcel_Reader_Exception */ - public function canRead($pFilename) + protected function _isValidFormat() { - // Check if file exists - if (!file_exists($pFilename)) { - throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist."); - } - - return true; - } // function canRead() - + return TRUE; + } /** * Set input encoding * - * @access public * @param string $pValue Input encoding */ public function setInputEncoding($pValue = 'UTF-8') { $this->_inputEncoding = $pValue; return $this; - } // function setInputEncoding() - + } /** * Get input encoding * - * @access public * @return string */ public function getInputEncoding() { return $this->_inputEncoding; - } // function getInputEncoding() + } + /** + * Move filepointer past any BOM marker + * + */ + protected function _skipBOM() + { + rewind($fileHandle); + + switch ($this->_inputEncoding) { + case 'UTF-8': + fgets($this->_fileHandle, 4) == "\xEF\xBB\xBF" ? + fseek($this->_fileHandle, 3) : fseek($this->_fileHandle, 0); + break; + case 'UTF-16LE': + fgets($this->_fileHandle, 3) == "\xFF\xFE" ? + fseek($this->_fileHandle, 2) : fseek($this->_fileHandle, 0); + break; + case 'UTF-16BE': + fgets($this->_fileHandle, 3) == "\xFE\xFF" ? + fseek($this->_fileHandle, 2) : fseek($this->_fileHandle, 0); + break; + case 'UTF-32LE': + fgets($this->_fileHandle, 5) == "\xFF\xFE\x00\x00" ? + fseek($this->_fileHandle, 4) : fseek($this->_fileHandle, 0); + break; + case 'UTF-32BE': + fgets($this->_fileHandle, 5) == "\x00\x00\xFE\xFF" ? + fseek($this->_fileHandle, 4) : fseek($this->_fileHandle, 0); + break; + default: + break; + } + } /** * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns) * - * @access public * @param string $pFilename * @throws PHPExcel_Reader_Exception */ public function listWorksheetInfo($pFilename) { - // Check if file exists - if (!file_exists($pFilename)) { - throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist."); - } - // Open file - $fileHandle = fopen($pFilename, 'r'); - if ($fileHandle === false) { - throw new PHPExcel_Reader_Exception("Could not open file " . $pFilename . " for reading."); + $this->_openFile($pFilename); + if (!$this->_isValidFormat()) { + fclose ($this->_fileHandle); + throw new PHPExcel_Reader_Exception($pFilename . " is an Invalid Spreadsheet file."); } - + $fileHandle = $this->_fileHandle; + // Skip BOM, if any - switch ($this->_inputEncoding) { - case 'UTF-8': - fgets($fileHandle, 4) == "\xEF\xBB\xBF" ? - fseek($fileHandle, 3) : fseek($fileHandle, 0); - break; - case 'UTF-16LE': - fgets($fileHandle, 3) == "\xFF\xFE" ? - fseek($fileHandle, 2) : fseek($fileHandle, 0); - break; - case 'UTF-16BE': - fgets($fileHandle, 3) == "\xFE\xFF" ? - fseek($fileHandle, 2) : fseek($fileHandle, 0); - break; - case 'UTF-32LE': - fgets($fileHandle, 5) == "\xFF\xFE\x00\x00" ? - fseek($fileHandle, 4) : fseek($fileHandle, 0); - break; - case 'UTF-32BE': - fgets($fileHandle, 5) == "\x00\x00\xFE\xFF" ? - fseek($fileHandle, 4) : fseek($fileHandle, 0); - break; - default: - break; - } + $this->_skipBOM(); $escapeEnclosures = array( "\\" . $this->_enclosure, $this->_enclosure . $this->_enclosure ); @@ -223,11 +215,9 @@ class PHPExcel_Reader_CSV extends PHPExcel_Reader_Abstract implements PHPExcel_R return $worksheetInfo; } - /** * Loads PHPExcel from file * - * @access public * @param string $pFilename * @return PHPExcel * @throws PHPExcel_Reader_Exception @@ -239,13 +229,11 @@ class PHPExcel_Reader_CSV extends PHPExcel_Reader_Abstract implements PHPExcel_R // Load into this instance return $this->loadIntoExisting($pFilename, $objPHPExcel); - } // function load() - + } /** * Loads PHPExcel from file into PHPExcel instance * - * @access public * @param string $pFilename * @param PHPExcel $objPHPExcel * @return PHPExcel @@ -253,51 +241,25 @@ class PHPExcel_Reader_CSV extends PHPExcel_Reader_Abstract implements PHPExcel_R */ public function loadIntoExisting($pFilename, PHPExcel $objPHPExcel) { - // Check if file exists - if (!file_exists($pFilename)) { - throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist."); - } - - // Create new PHPExcel - while ($objPHPExcel->getSheetCount() <= $this->_sheetIndex) { - $objPHPExcel->createSheet(); - } - $sheet = $objPHPExcel->setActiveSheetIndex( $this->_sheetIndex ); - $lineEnding = ini_get('auto_detect_line_endings'); ini_set('auto_detect_line_endings', true); // Open file - $fileHandle = fopen($pFilename, 'r'); - if ($fileHandle === false) { - throw new PHPExcel_Reader_Exception("Could not open file $pFilename for reading."); + $this->_openFile($pFilename); + if (!$this->_isValidFormat()) { + fclose ($this->_fileHandle); + throw new PHPExcel_Reader_Exception($pFilename . " is an Invalid Spreadsheet file."); } + $fileHandle = $this->_fileHandle; // Skip BOM, if any - switch ($this->_inputEncoding) { - case 'UTF-8': - fgets($fileHandle, 4) == "\xEF\xBB\xBF" ? - fseek($fileHandle, 3) : fseek($fileHandle, 0); - break; - case 'UTF-16LE': - fgets($fileHandle, 3) == "\xFF\xFE" ? - fseek($fileHandle, 2) : fseek($fileHandle, 0); - break; - case 'UTF-16BE': - fgets($fileHandle, 3) == "\xFE\xFF" ? - fseek($fileHandle, 2) : fseek($fileHandle, 0); - break; - case 'UTF-32LE': - fgets($fileHandle, 5) == "\xFF\xFE\x00\x00" ? - fseek($fileHandle, 4) : fseek($fileHandle, 0); - break; - case 'UTF-32BE': - fgets($fileHandle, 5) == "\x00\x00\xFE\xFF" ? - fseek($fileHandle, 4) : fseek($fileHandle, 0); - break; - default: - break; + $this->_skipBOM(); + + // Create new PHPExcel object + while ($objPHPExcel->getSheetCount() <= $this->_sheetIndex) { + $objPHPExcel->createSheet(); } + $sheet = $objPHPExcel->setActiveSheetIndex($this->_sheetIndex); $escapeEnclosures = array( "\\" . $this->_enclosure, $this->_enclosure . $this->_enclosure @@ -341,48 +303,40 @@ class PHPExcel_Reader_CSV extends PHPExcel_Reader_Abstract implements PHPExcel_R // Return return $objPHPExcel; - } // function loadIntoExisting() - + } /** * Get delimiter * - * @access public * @return string */ public function getDelimiter() { return $this->_delimiter; - } // function getDelimiter() - + } /** * Set delimiter * - * @access public * @param string $pValue Delimiter, defaults to , * @return PHPExcel_Reader_CSV */ public function setDelimiter($pValue = ',') { $this->_delimiter = $pValue; return $this; - } // function setDelimiter() - + } /** * Get enclosure * - * @access public * @return string */ public function getEnclosure() { return $this->_enclosure; - } // function getEnclosure() - + } /** * Set enclosure * - * @access public * @param string $pValue Enclosure, defaults to " * @return PHPExcel_Reader_CSV */ @@ -392,82 +346,70 @@ class PHPExcel_Reader_CSV extends PHPExcel_Reader_Abstract implements PHPExcel_R } $this->_enclosure = $pValue; return $this; - } // function setEnclosure() - + } /** * Get line ending * - * @access public * @return string */ public function getLineEnding() { return $this->_lineEnding; - } // function getLineEnding() - + } /** * Set line ending * - * @access public * @param string $pValue Line ending, defaults to OS line ending (PHP_EOL) * @return PHPExcel_Reader_CSV */ public function setLineEnding($pValue = PHP_EOL) { $this->_lineEnding = $pValue; return $this; - } // function setLineEnding() - + } /** * Get sheet index * - * @access public - * @return int + * @return integer */ public function getSheetIndex() { return $this->_sheetIndex; - } // function getSheetIndex() - + } /** * Set sheet index * - * @access public * @param integer $pValue Sheet index * @return PHPExcel_Reader_CSV */ public function setSheetIndex($pValue = 0) { $this->_sheetIndex = $pValue; return $this; - } // function setSheetIndex() - + } /** * Set Contiguous * - * @access public * @param boolean $contiguous */ public function setContiguous($contiguous = FALSE) { $this->_contiguous = (bool) $contiguous; if (!$contiguous) { - $this->_contiguousRow = -1; + $this->_contiguousRow = -1; } return $this; - } // function setInputEncoding() - + } /** * Get Contiguous * - * @access public * @return boolean */ public function getContiguous() { return $this->_contiguous; - } // function getSheetIndex() + } } diff --git a/Classes/PHPExcel/Reader/Excel2003XML.php b/Classes/PHPExcel/Reader/Excel2003XML.php index 58496855..ec910f78 100644 --- a/Classes/PHPExcel/Reader/Excel2003XML.php +++ b/Classes/PHPExcel/Reader/Excel2003XML.php @@ -92,15 +92,13 @@ class PHPExcel_Reader_Excel2003XML extends PHPExcel_Reader_Abstract implements P '' ); - // Check if file exists - if (!file_exists($pFilename)) { - throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist."); - } - + // Open file + $this->_openFile($pFilename); + $fileHandle = $this->_fileHandle; + // Read sample data (first 2 KB will do) - $fh = fopen($pFilename, 'r'); - $data = fread($fh, 2048); - fclose($fh); + $data = fread($fileHandle, 2048); + fclose($fileHandle); $valid = true; foreach($signature as $match) { diff --git a/Classes/PHPExcel/Reader/HTML.php b/Classes/PHPExcel/Reader/HTML.php index d8c5821b..3ff0a172 100644 --- a/Classes/PHPExcel/Reader/HTML.php +++ b/Classes/PHPExcel/Reader/HTML.php @@ -109,24 +109,14 @@ class PHPExcel_Reader_HTML extends PHPExcel_Reader_Abstract implements PHPExcel_ } /** - * Can the current PHPExcel_Reader_IReader read the file? + * Validate that the current file is an HTML file * - * @param string $pFilename - * @return boolean - * @throws PHPExcel_Reader_Exception + * @return boolean */ - public function canRead($pFilename) + protected function _isValidFormat() { - // Check if file exists - if (!file_exists($pFilename)) { - throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist."); - } - - // Read sample data (first 2 KB will do) - $fh = fopen($pFilename, 'r'); - $data = fread($fh, 2048); - fclose($fh); - + // Reading 2048 bytes should be enough to validate that the format is HTML + $data = fread($this->_fileHandle, 2048); if ((strpos('<',$data) !== FALSE) && (strlen($data) !== strlen(strip_tags($data)))) { return TRUE; @@ -416,14 +406,14 @@ class PHPExcel_Reader_HTML extends PHPExcel_Reader_Abstract implements PHPExcel_ */ public function loadIntoExisting($pFilename, PHPExcel $objPHPExcel) { - // Check if file exists - if (!file_exists($pFilename)) { - throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist."); - } - - if (!is_file($pFilename)) { - throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! The given file is not a regular file."); + // Open file to validate + $this->_openFile($pFilename); + if (!$this->_isValidFormat()) { + fclose ($this->_fileHandle); + throw new PHPExcel_Reader_Exception($pFilename . " is an Invalid HTML file."); } + // Close after validating + fclose ($this->_fileHandle); // Create new PHPExcel while ($objPHPExcel->getSheetCount() <= $this->_sheetIndex) { @@ -433,9 +423,9 @@ class PHPExcel_Reader_HTML extends PHPExcel_Reader_Abstract implements PHPExcel_ // Create a new DOM object $dom = new domDocument; - // Load the HTML file into the DOM object + // Reload the HTML file into the DOM object $loaded = $dom->loadHTMLFile($pFilename); - if ($loaded === false) { + if ($loaded === FALSE) { throw new PHPExcel_Reader_Exception('Failed to load ',$pFilename,' as a DOM Document'); } diff --git a/Classes/PHPExcel/Reader/SYLK.php b/Classes/PHPExcel/Reader/SYLK.php index d3a99357..a6e22e6d 100644 --- a/Classes/PHPExcel/Reader/SYLK.php +++ b/Classes/PHPExcel/Reader/SYLK.php @@ -79,42 +79,31 @@ class PHPExcel_Reader_SYLK extends PHPExcel_Reader_Abstract implements PHPExcel_ $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter(); } - /** - * Can the current PHPExcel_Reader_IReader read the file? + * Validate that the current file is a SYLK file * - * @param string $pFilename - * @return boolean - * @throws PHPExcel_Reader_Exception + * @return boolean */ - public function canRead($pFilename) + protected function _isValidFormat() { - // Check if file exists - if (!file_exists($pFilename)) { - throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist."); - } - // Read sample data (first 2 KB will do) - $fh = fopen($pFilename, 'r'); - $data = fread($fh, 2048); - fclose($fh); + $data = fread($this->_fileHandle, 2048); // Count delimiters in file $delimiterCount = substr_count($data, ';'); if ($delimiterCount < 1) { - return false; + return FALSE; } // Analyze first line looking for ID; signature $lines = explode("\n", $data); if (substr($lines[0],0,4) != 'ID;P') { - return false; + return FALSE; } - return true; + return TRUE; } - /** * Set input encoding * @@ -126,7 +115,6 @@ class PHPExcel_Reader_SYLK extends PHPExcel_Reader_Abstract implements PHPExcel_ return $this; } - /** * Get input encoding * @@ -137,7 +125,6 @@ class PHPExcel_Reader_SYLK extends PHPExcel_Reader_Abstract implements PHPExcel_ return $this->_inputEncoding; } - /** * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns) * @@ -146,16 +133,13 @@ class PHPExcel_Reader_SYLK extends PHPExcel_Reader_Abstract implements PHPExcel_ */ public function listWorksheetInfo($pFilename) { - // Check if file exists - if (!file_exists($pFilename)) { - throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist."); - } - // Open file - $fileHandle = fopen($pFilename, 'r'); - if ($fileHandle === false) { - throw new PHPExcel_Reader_Exception("Could not open file " . $pFilename . " for reading."); + $this->_openFile($pFilename); + if (!$this->_isValidFormat()) { + fclose ($this->_fileHandle); + throw new PHPExcel_Reader_Exception($pFilename . " is an Invalid Spreadsheet file."); } + $fileHandle = $this->_fileHandle; $worksheetInfo = array(); $worksheetInfo[0]['worksheetName'] = 'Worksheet'; @@ -177,7 +161,7 @@ class PHPExcel_Reader_SYLK extends PHPExcel_Reader_Abstract implements PHPExcel_ // explode each row at semicolons while taking into account that literal semicolon (;) // is escaped like this (;;) - $rowData = explode("\t",str_replace('?',';',str_replace(';',"\t",str_replace(';;','?',rtrim($rowData))))); + $rowData = explode("\t",str_replace('¤',';',str_replace(';',"\t",str_replace(';;','¤',rtrim($rowData))))); $dataType = array_shift($rowData); if ($dataType == 'C') { @@ -209,7 +193,6 @@ class PHPExcel_Reader_SYLK extends PHPExcel_Reader_Abstract implements PHPExcel_ return $worksheetInfo; } - /** * Loads PHPExcel from file * @@ -226,7 +209,6 @@ class PHPExcel_Reader_SYLK extends PHPExcel_Reader_Abstract implements PHPExcel_ return $this->loadIntoExisting($pFilename, $objPHPExcel); } - /** * Loads PHPExcel from file into PHPExcel instance * @@ -237,11 +219,15 @@ class PHPExcel_Reader_SYLK extends PHPExcel_Reader_Abstract implements PHPExcel_ */ public function loadIntoExisting($pFilename, PHPExcel $objPHPExcel) { - // Check if file exists - if (!file_exists($pFilename)) { - throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist."); + // Open file + $this->_openFile($pFilename); + if (!$this->_isValidFormat()) { + fclose ($this->_fileHandle); + throw new PHPExcel_Reader_Exception($pFilename . " is an Invalid Spreadsheet file."); } - + $fileHandle = $this->_fileHandle; + rewind($fileHandle); + // Create new PHPExcel while ($objPHPExcel->getSheetCount() <= $this->_sheetIndex) { $objPHPExcel->createSheet(); @@ -251,12 +237,6 @@ class PHPExcel_Reader_SYLK extends PHPExcel_Reader_Abstract implements PHPExcel_ $fromFormats = array('\-', '\ '); $toFormats = array('-', ' '); - // Open file - $fileHandle = fopen($pFilename, 'r'); - if ($fileHandle === false) { - throw new PHPExcel_Reader_Exception("Could not open file $pFilename for reading."); - } - // Loop through file $rowData = array(); $column = $row = ''; @@ -404,7 +384,9 @@ class PHPExcel_Reader_SYLK extends PHPExcel_Reader_Abstract implements PHPExcel_ } if (($formatStyle > '') && ($column > '') && ($row > '')) { $columnLetter = PHPExcel_Cell::stringFromColumnIndex($column-1); - $objPHPExcel->getActiveSheet()->getStyle($columnLetter.$row)->applyFromArray($this->_formats[$formatStyle]); + if (isset($this->_formats[$formatStyle])) { + $objPHPExcel->getActiveSheet()->getStyle($columnLetter.$row)->applyFromArray($this->_formats[$formatStyle]); + } } if ((!empty($styleData)) && ($column > '') && ($row > '')) { $columnLetter = PHPExcel_Cell::stringFromColumnIndex($column-1); @@ -444,7 +426,6 @@ class PHPExcel_Reader_SYLK extends PHPExcel_Reader_Abstract implements PHPExcel_ return $objPHPExcel; } - /** * Get sheet index * @@ -454,7 +435,6 @@ class PHPExcel_Reader_SYLK extends PHPExcel_Reader_Abstract implements PHPExcel_ return $this->_sheetIndex; } - /** * Set sheet index * diff --git a/Documentation/PHPExcel developer documentation.doc b/Documentation/PHPExcel developer documentation.doc index 30600f63257b1de167940293d8a205b0db6f7780..6eb230c93b96f8c59553e0922607e6f79ea36134 100644 GIT binary patch delta 32396 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 N@2s0JNi#$Be*piUI!yoo diff --git a/Examples/33chartcreate-composite-chart.php b/Examples/33chartcreate-composite.php similarity index 100% rename from Examples/33chartcreate-composite-chart.php rename to Examples/33chartcreate-composite.php diff --git a/Examples/33chartcreate-scatter.php b/Examples/33chartcreate-scatter.php new file mode 100644 index 00000000..9d62e916 --- /dev/null +++ b/Examples/33chartcreate-scatter.php @@ -0,0 +1,138 @@ +'); + +date_default_timezone_set('Europe/London'); + +/** + * PHPExcel + * + * Copyright (C) 2006 - 2012 PHPExcel + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category PHPExcel + * @package PHPExcel + * @copyright Copyright (c) 2006 - 2012 PHPExcel (http://www.codeplex.com/PHPExcel) + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @version ##VERSION##, ##DATE## + */ + +/** PHPExcel */ +require_once '../Classes/PHPExcel.php'; + + +$objPHPExcel = new PHPExcel(); +$objWorksheet = $objPHPExcel->getActiveSheet(); +$objWorksheet->fromArray( + array( + array('', 2010, 2011, 2012), + array('Q1', 12, 15, 21), + array('Q2', 56, 73, 86), + array('Q3', 52, 61, 69), + array('Q4', 30, 32, 0), + ) +); + +// Set the Labels for each data series we want to plot +// Datatype +// Cell reference for data +// Format Code +// Number of datapoints in series +// Data values +// Data Marker +$dataseriesLabels = array( + new PHPExcel_Chart_DataSeriesValues('String', 'Worksheet!$B$1', NULL, 1), // 2010 + new PHPExcel_Chart_DataSeriesValues('String', 'Worksheet!$C$1', NULL, 1), // 2011 + new PHPExcel_Chart_DataSeriesValues('String', 'Worksheet!$D$1', NULL, 1), // 2012 +); +// Set the X-Axis Labels +$xAxisTickValues = array( + new PHPExcel_Chart_DataSeriesValues('String', 'Worksheet!$A$2:$A$5', NULL, 4), // Q1 to Q4 +); +// Set the Data values for each data series we want to plot +// Datatype +// Cell reference for data +// Format Code +// Number of datapoints in series +// Data values +// Data Marker +$dataSeriesValues = array( + new PHPExcel_Chart_DataSeriesValues('Number', 'Worksheet!$B$2:$B$5', NULL, 4), + new PHPExcel_Chart_DataSeriesValues('Number', 'Worksheet!$C$2:$C$5', NULL, 4), + new PHPExcel_Chart_DataSeriesValues('Number', 'Worksheet!$D$2:$D$5', NULL, 4), +); + +// Build the dataseries +$series = new PHPExcel_Chart_DataSeries( + PHPExcel_Chart_DataSeries::TYPE_SCATTERCHART, // plotType + NULL, // plotGrouping (Scatter charts don't have any grouping) + range(0, count($dataSeriesValues)-1), // plotOrder + $dataseriesLabels, // plotLabel + $xAxisTickValues, // plotCategory + $dataSeriesValues, // plotValues + NULL, // smooth line + PHPExcel_Chart_DataSeries::STYLE_LINEMARKER // plotStyle +); + +// Set the series in the plot area +$plotarea = new PHPExcel_Chart_PlotArea(NULL, array($series)); +// Set the chart legend +$legend = new PHPExcel_Chart_Legend(PHPExcel_Chart_Legend::POSITION_TOPRIGHT, NULL, false); + +$title = new PHPExcel_Chart_Title('Test Scatter Chart'); +$yAxisLabel = new PHPExcel_Chart_Title('Value ($k)'); + + +// Create the chart +$chart = new PHPExcel_Chart( + 'chart1', // name + $title, // title + $legend, // legend + $plotarea, // plotArea + true, // plotVisibleOnly + 0, // displayBlanksAs + NULL, // xAxisLabel + $yAxisLabel // yAxisLabel +); + +// Set the position where the chart should appear in the worksheet +$chart->setTopLeftPosition('A7'); +$chart->setBottomRightPosition('H20'); + +// Add the chart to the worksheet +$objWorksheet->addChart($chart); + + +// Save Excel 2007 file +echo date('H:i:s') , " Write to Excel2007 format" , EOL; +$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007'); +$objWriter->setIncludeCharts(TRUE); +$objWriter->save(str_replace('.php', '.xlsx', __FILE__)); +echo date('H:i:s') , " File written to " , str_replace('.php', '.xlsx', pathinfo(__FILE__, PATHINFO_BASENAME)) , EOL; + + +// Echo memory peak usage +echo date('H:i:s') , " Peak memory usage: " , (memory_get_peak_usage(true) / 1024 / 1024) , " MB" , EOL; + +// Echo done +echo date('H:i:s') , " Done writing file" , EOL; +echo 'File has been created in ' , getcwd() , EOL; diff --git a/Examples/34chartupdate.php b/Examples/34chartupdate.php index c6021723..569e3169 100644 --- a/Examples/34chartupdate.php +++ b/Examples/34chartupdate.php @@ -36,11 +36,8 @@ date_default_timezone_set('Europe/London'); * @version ##VERSION##, ##DATE## */ -/** Include path **/ -set_include_path(get_include_path() . PATH_SEPARATOR . '../Classes/'); - /** PHPExcel */ -include 'PHPExcel.php'; +include '../Classes/PHPExcel.php'; if (!file_exists("33chartcreate-bar.xlsx")) { exit("Please run 33chartcreate-bar.php first." . EOL);