Subversion Repositories Applications.bazar

Compare Revisions

Ignore whitespace Rev 467 → Rev 468

/trunk/jrest/lib/DBAccessor.php
New file
0,0 → 1,34
<?php
require_once 'JrestService.php';
 
class DBAccessor extends JrestService {
public function connectDB($config, $base = 'database') {
require_once 'DB.php';
$dsn = $config[$base];
 
$DB =& DB::connect($dsn);
if (DB::isError($DB)) {
die($DB->getMessage());
}
$DB->query("SET NAMES 'utf8'");
return $DB;
}
public function connecterPDO($config, $base = 'database') {
$cfg = $config[$base];
$dsn = $cfg['phptype'].':dbname='.$cfg['database'].';host='.$cfg['hostspec'];
try {
$PDO = new PDO($dsn, $cfg['username'], $cfg['password']);
} catch (PDOException $e) {
echo 'La connexion à la base de donnée via PDO a échouée : ' . $e->getMessage();
}
// Passe en UTF-8 la connexion à la BDD
$PDO->exec("SET NAMES 'utf8'");
// Affiche les erreurs détectées par PDO (sinon mode silencieux => aucune erreur affiché)
$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $PDO;
}
}
?>
/trunk/jrest/lib/JrestService.php
New file
0,0 → 1,72
<?php
/**
* PHP Version 5
*
* @category PHP
* @package jrest
* @author aurelien <aurelien@tela-botanica.org>
* @copyright 2009 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @version $$id$$
* @link /doc/jrest/
*/
 
class JrestService {
 
protected $config;
protected $script_time;
protected $max_exec_time;
 
public function JrestService($config) {
$this->config = config;
$this->script_time = microtime(true);
$this->max_exec_time = ini_get('max_execution_time');
}
 
public function isAdmin($id) {
$admins = $this->config['jrest_admin']['admin'];
$admin_tab = split(',',$admins);
 
if (in_array($id,$admin_tab)) {
return true;
} else {
return false;
}
}
 
public function controleUtilisateur($id) {
if ($_SESSION['user']['name'] == '') {
//cas de la session temporaire, on ne fait rien de particulier
} else {
if (!$this->isAdmin($_SESSION['user']['name']) && $_SESSION['user']['name'] != $id) {
// cas d'usurpation d'identité
print 'Accès interdit';
exit();
}
}
}
 
public function logger($index,$chaine) {
if(!class_exists('Log')) {
include_once('Log.php');
Log::getInstance();
}
 
Log::setCheminLog($this->config['log']['cheminlog']);
Log::setTimeZone($this->config['log']['timezone']);
Log::setTailleMax($this->config['log']['taillemax']);
 
Log::ajouterEntree($index,$chaine);
}
 
public function verifierOuRelancerExecution() {
 
if((microtime(true) - $this->script_time) > ($this->max_exec_time - 5)*100) {
set_time_limit(2);
$this->logger('JRestService','Durée du script augmentée :'.microtime(true).' - '.$this->script_time.'.) > ('.$this->max_exec_time.' - 5)*1000000');
return true;
}
return false;
}
}
?>
/trunk/jrest/lib/JSON.php
New file
0,0 → 1,806
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* Converts to and from JSON format.
*
* JSON (JavaScript Object Notation) is a lightweight data-interchange
* format. It is easy for humans to read and write. It is easy for machines
* to parse and generate. It is based on a subset of the JavaScript
* Programming Language, Standard ECMA-262 3rd Edition - December 1999.
* This feature can also be found in Python. JSON is a text format that is
* completely language independent but uses conventions that are familiar
* to programmers of the C-family of languages, including C, C++, C#, Java,
* JavaScript, Perl, TCL, and many others. These properties make JSON an
* ideal data-interchange language.
*
* This package provides a simple encoder and decoder for JSON notation. It
* is intended for use with client-side Javascript applications that make
* use of HTTPRequest to perform server communication functions - data can
* be encoded into JSON notation for use in a client-side javascript, or
* decoded from incoming Javascript requests. JSON format is native to
* Javascript, and can be directly eval()'ed with no further parsing
* overhead
*
* All strings should be in ASCII or UTF-8 format!
*
* LICENSE: Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met: Redistributions of source code must retain the
* above copyright notice, this list of conditions and the following
* disclaimer. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* @category
* @package Services_JSON
* @author Michal Migurski <mike-json@teczno.com>
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
* @copyright 2005 Michal Migurski
* @version CVS: $Id$
* @license http://www.opensource.org/licenses/bsd-license.php
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
*/
 
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_SLICE', 1);
 
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_STR', 2);
 
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_ARR', 3);
 
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_OBJ', 4);
 
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_CMT', 5);
 
/**
* Behavior switch for Services_JSON::decode()
*/
define('SERVICES_JSON_LOOSE_TYPE', 16);
 
/**
* Behavior switch for Services_JSON::decode()
*/
define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
 
/**
* Converts to and from JSON format.
*
* Brief example of use:
*
* <code>
* // create a new instance of Services_JSON
* $json = new Services_JSON();
*
* // convert a complexe value to JSON notation, and send it to the browser
* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
* $output = $json->encode($value);
*
* print($output);
* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
*
* // accept incoming POST data, assumed to be in JSON notation
* $input = file_get_contents('php://input', 1000000);
* $value = $json->decode($input);
* </code>
*/
class Services_JSON
{
/**
* constructs a new JSON instance
*
* @param int $use object behavior flags; combine with boolean-OR
*
* possible values:
* - SERVICES_JSON_LOOSE_TYPE: loose typing.
* "{...}" syntax creates associative arrays
* instead of objects in decode().
* - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
* Values which can't be encoded (e.g. resources)
* appear as NULL instead of throwing errors.
* By default, a deeply-nested resource will
* bubble up with an error, so all return values
* from encode() should be checked with isError()
*/
function Services_JSON($use = 0)
{
$this->use = $use;
}
 
/**
* convert a string from one UTF-16 char to one UTF-8 char
*
* Normally should be handled by mb_convert_encoding, but
* provides a slower PHP-only method for installations
* that lack the multibye string extension.
*
* @param string $utf16 UTF-16 character
* @return string UTF-8 character
* @access private
*/
function utf162utf8($utf16)
{
// oh please oh please oh please oh please oh please
if(function_exists('mb_convert_encoding')) {
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
}
 
$bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
 
switch(true) {
case ((0x7F & $bytes) == $bytes):
// this case should never be reached, because we are in ASCII range
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x7F & $bytes);
 
case (0x07FF & $bytes) == $bytes:
// return a 2-byte UTF-8 character
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0xC0 | (($bytes >> 6) & 0x1F))
. chr(0x80 | ($bytes & 0x3F));
 
case (0xFFFF & $bytes) == $bytes:
// return a 3-byte UTF-8 character
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0xE0 | (($bytes >> 12) & 0x0F))
. chr(0x80 | (($bytes >> 6) & 0x3F))
. chr(0x80 | ($bytes & 0x3F));
}
 
// ignoring UTF-32 for now, sorry
return '';
}
 
/**
* convert a string from one UTF-8 char to one UTF-16 char
*
* Normally should be handled by mb_convert_encoding, but
* provides a slower PHP-only method for installations
* that lack the multibye string extension.
*
* @param string $utf8 UTF-8 character
* @return string UTF-16 character
* @access private
*/
function utf82utf16($utf8)
{
// oh please oh please oh please oh please oh please
if(function_exists('mb_convert_encoding')) {
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
}
 
switch(strlen($utf8)) {
case 1:
// this case should never be reached, because we are in ASCII range
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return $utf8;
 
case 2:
// return a UTF-16 character from a 2-byte UTF-8 char
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x07 & (ord($utf8{0}) >> 2))
. chr((0xC0 & (ord($utf8{0}) << 6))
| (0x3F & ord($utf8{1})));
 
case 3:
// return a UTF-16 character from a 3-byte UTF-8 char
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr((0xF0 & (ord($utf8{0}) << 4))
| (0x0F & (ord($utf8{1}) >> 2)))
. chr((0xC0 & (ord($utf8{1}) << 6))
| (0x7F & ord($utf8{2})));
}
 
// ignoring UTF-32 for now, sorry
return '';
}
 
/**
* encodes an arbitrary variable into JSON format
*
* @param mixed $var any number, boolean, string, array, or object to be encoded.
* see argument 1 to Services_JSON() above for array-parsing behavior.
* if var is a strng, note that encode() always expects it
* to be in ASCII or UTF-8 format!
*
* @return mixed JSON string representation of input var or an error if a problem occurs
* @access public
*/
function encode($var)
{
switch (gettype($var)) {
case 'boolean':
return $var ? 'true' : 'false';
 
case 'NULL':
return 'null';
 
case 'integer':
return (int) $var;
 
case 'double':
case 'float':
return (float) $var;
 
case 'string':
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
$ascii = '';
$strlen_var = strlen($var);
 
/*
* Iterate over every character in the string,
* escaping with a slash or encoding to UTF-8 where necessary
*/
for ($c = 0; $c < $strlen_var; ++$c) {
 
$ord_var_c = ord($var{$c});
 
switch (true) {
case $ord_var_c == 0x08:
$ascii .= '\b';
break;
case $ord_var_c == 0x09:
$ascii .= '\t';
break;
case $ord_var_c == 0x0A:
$ascii .= '\n';
break;
case $ord_var_c == 0x0C:
$ascii .= '\f';
break;
case $ord_var_c == 0x0D:
$ascii .= '\r';
break;
 
case $ord_var_c == 0x22:
case $ord_var_c == 0x2F:
case $ord_var_c == 0x5C:
// double quote, slash, slosh
$ascii .= '\\'.$var{$c};
break;
 
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
// characters U-00000000 - U-0000007F (same as ASCII)
$ascii .= $var{$c};
break;
 
case (($ord_var_c & 0xE0) == 0xC0):
// characters U-00000080 - U-000007FF, mask 110XXXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c, ord($var{$c + 1}));
$c += 1;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
 
case (($ord_var_c & 0xF0) == 0xE0):
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}));
$c += 2;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
 
case (($ord_var_c & 0xF8) == 0xF0):
// characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}));
$c += 3;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
 
case (($ord_var_c & 0xFC) == 0xF8):
// characters U-00200000 - U-03FFFFFF, mask 111110XX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}),
ord($var{$c + 4}));
$c += 4;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
 
case (($ord_var_c & 0xFE) == 0xFC):
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}),
ord($var{$c + 4}),
ord($var{$c + 5}));
$c += 5;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
}
}
 
return '"'.$ascii.'"';
 
case 'array':
/*
* As per JSON spec if any array key is not an integer
* we must treat the the whole array as an object. We
* also try to catch a sparsely populated associative
* array with numeric keys here because some JS engines
* will create an array with empty indexes up to
* max_index which can cause memory issues and because
* the keys, which may be relevant, will be remapped
* otherwise.
*
* As per the ECMA and JSON specification an object may
* have any string as a property. Unfortunately due to
* a hole in the ECMA specification if the key is a
* ECMA reserved word or starts with a digit the
* parameter is only accessible using ECMAScript's
* bracket notation.
*/
 
// treat as a JSON object
if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
$properties = array_map(array($this, 'name_value'),
array_keys($var),
array_values($var));
 
foreach($properties as $property) {
if(Services_JSON::isError($property)) {
return $property;
}
}
 
return '{' . join(',', $properties) . '}';
}
 
// treat it like a regular array
$elements = array_map(array($this, 'encode'), $var);
 
foreach($elements as $element) {
if(Services_JSON::isError($element)) {
return $element;
}
}
 
return '[' . join(',', $elements) . ']';
 
case 'object':
$vars = get_object_vars($var);
 
$properties = array_map(array($this, 'name_value'),
array_keys($vars),
array_values($vars));
 
foreach($properties as $property) {
if(Services_JSON::isError($property)) {
return $property;
}
}
 
return '{' . join(',', $properties) . '}';
 
default:
return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
? 'null'
: new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
}
}
 
/**
* array-walking function for use in generating JSON-formatted name-value pairs
*
* @param string $name name of key to use
* @param mixed $value reference to an array element to be encoded
*
* @return string JSON-formatted name-value pair, like '"name":value'
* @access private
*/
function name_value($name, $value)
{
$encoded_value = $this->encode($value);
 
if(Services_JSON::isError($encoded_value)) {
return $encoded_value;
}
 
return $this->encode(strval($name)) . ':' . $encoded_value;
}
 
/**
* reduce a string by removing leading and trailing comments and whitespace
*
* @param $str string string value to strip of comments and whitespace
*
* @return string string value stripped of comments and whitespace
* @access private
*/
function reduce_string($str)
{
$str = preg_replace(array(
 
// eliminate single line comments in '// ...' form
'#^\s*//(.+)$#m',
 
// eliminate multi-line comments in '/* ... */' form, at start of string
'#^\s*/\*(.+)\*/#Us',
 
// eliminate multi-line comments in '/* ... */' form, at end of string
'#/\*(.+)\*/\s*$#Us'
 
), '', $str);
 
// eliminate extraneous space
return trim($str);
}
 
/**
* decodes a JSON string into appropriate variable
*
* @param string $str JSON-formatted string
*
* @return mixed number, boolean, string, array, or object
* corresponding to given JSON input string.
* See argument 1 to Services_JSON() above for object-output behavior.
* Note that decode() always returns strings
* in ASCII or UTF-8 format!
* @access public
*/
function decode($str)
{
$str = $this->reduce_string($str);
 
switch (strtolower($str)) {
case 'true':
return true;
 
case 'false':
return false;
 
case 'null':
return null;
 
default:
$m = array();
 
if (is_numeric($str)) {
// Lookie-loo, it's a number
 
// This would work on its own, but I'm trying to be
// good about returning integers where appropriate:
// return (float)$str;
 
// Return float or int, as appropriate
return ((float)$str == (integer)$str)
? (integer)$str
: (float)$str;
 
} elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
// STRINGS RETURNED IN UTF-8 FORMAT
$delim = substr($str, 0, 1);
$chrs = substr($str, 1, -1);
$utf8 = '';
$strlen_chrs = strlen($chrs);
 
for ($c = 0; $c < $strlen_chrs; ++$c) {
 
$substr_chrs_c_2 = substr($chrs, $c, 2);
$ord_chrs_c = ord($chrs{$c});
 
switch (true) {
case $substr_chrs_c_2 == '\b':
$utf8 .= chr(0x08);
++$c;
break;
case $substr_chrs_c_2 == '\t':
$utf8 .= chr(0x09);
++$c;
break;
case $substr_chrs_c_2 == '\n':
$utf8 .= chr(0x0A);
++$c;
break;
case $substr_chrs_c_2 == '\f':
$utf8 .= chr(0x0C);
++$c;
break;
case $substr_chrs_c_2 == '\r':
$utf8 .= chr(0x0D);
++$c;
break;
 
case $substr_chrs_c_2 == '\\"':
case $substr_chrs_c_2 == '\\\'':
case $substr_chrs_c_2 == '\\\\':
case $substr_chrs_c_2 == '\\/':
if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
($delim == "'" && $substr_chrs_c_2 != '\\"')) {
$utf8 .= $chrs{++$c};
}
break;
 
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
// single, escaped unicode character
$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
. chr(hexdec(substr($chrs, ($c + 4), 2)));
$utf8 .= $this->utf162utf8($utf16);
$c += 5;
break;
 
case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
$utf8 .= $chrs{$c};
break;
 
case ($ord_chrs_c & 0xE0) == 0xC0:
// characters U-00000080 - U-000007FF, mask 110XXXXX
//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 2);
++$c;
break;
 
case ($ord_chrs_c & 0xF0) == 0xE0:
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 3);
$c += 2;
break;
 
case ($ord_chrs_c & 0xF8) == 0xF0:
// characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 4);
$c += 3;
break;
 
case ($ord_chrs_c & 0xFC) == 0xF8:
// characters U-00200000 - U-03FFFFFF, mask 111110XX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 5);
$c += 4;
break;
 
case ($ord_chrs_c & 0xFE) == 0xFC:
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 6);
$c += 5;
break;
 
}
 
}
 
return $utf8;
 
} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
// array, or object notation
 
if ($str{0} == '[') {
$stk = array(SERVICES_JSON_IN_ARR);
$arr = array();
} else {
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$stk = array(SERVICES_JSON_IN_OBJ);
$obj = array();
} else {
$stk = array(SERVICES_JSON_IN_OBJ);
$obj = new stdClass();
}
}
 
array_push($stk, array('what' => SERVICES_JSON_SLICE,
'where' => 0,
'delim' => false));
 
$chrs = substr($str, 1, -1);
$chrs = $this->reduce_string($chrs);
 
if ($chrs == '') {
if (reset($stk) == SERVICES_JSON_IN_ARR) {
return $arr;
 
} else {
return $obj;
 
}
}
 
//print("\nparsing {$chrs}\n");
 
$strlen_chrs = strlen($chrs);
 
for ($c = 0; $c <= $strlen_chrs; ++$c) {
 
$top = end($stk);
$substr_chrs_c_2 = substr($chrs, $c, 2);
 
if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
// found a comma that is not inside a string, array, etc.,
// OR we've reached the end of the character list
$slice = substr($chrs, $top['where'], ($c - $top['where']));
array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
//print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 
if (reset($stk) == SERVICES_JSON_IN_ARR) {
// we are in an array, so just push an element onto the stack
array_push($arr, $this->decode($slice));
 
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
// we are in an object, so figure
// out the property name and set an
// element in an associative array,
// for now
$parts = array();
if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
// "name":value pair
$key = $this->decode($parts[1]);
$val = $this->decode($parts[2]);
 
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$obj[$key] = $val;
} else {
$obj->$key = $val;
}
} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
// name:value pair, where name is unquoted
$key = $parts[1];
$val = $this->decode($parts[2]);
 
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$obj[$key] = $val;
} else {
$obj->$key = $val;
}
}
 
}
 
} elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
// found a quote, and we are not inside a string
array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
//print("Found start of string at {$c}\n");
 
} elseif (($chrs{$c} == $top['delim']) &&
($top['what'] == SERVICES_JSON_IN_STR) &&
((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
// found a quote, we're in a string, and it's not escaped
// we know that it's not escaped becase there is _not_ an
// odd number of backslashes at the end of the string so far
array_pop($stk);
//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
 
} elseif (($chrs{$c} == '[') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a left-bracket, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
//print("Found start of array at {$c}\n");
 
} elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
// found a right-bracket, and we're in an array
array_pop($stk);
//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 
} elseif (($chrs{$c} == '{') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a left-brace, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
//print("Found start of object at {$c}\n");
 
} elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
// found a right-brace, and we're in an object
array_pop($stk);
//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 
} elseif (($substr_chrs_c_2 == '/*') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a comment start, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
$c++;
//print("Found start of comment at {$c}\n");
 
} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
// found a comment end, and we're in one now
array_pop($stk);
$c++;
 
for ($i = $top['where']; $i <= $c; ++$i)
$chrs = substr_replace($chrs, ' ', $i, 1);
 
//print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 
}
 
}
 
if (reset($stk) == SERVICES_JSON_IN_ARR) {
return $arr;
 
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
return $obj;
 
}
 
}
}
}
 
/**
* @todo Ultimately, this should just call PEAR::isError()
*/
function isError($data, $code = null)
{
if (class_exists('pear')) {
return PEAR::isError($data, $code);
} elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
is_subclass_of($data, 'services_json_error'))) {
return true;
}
 
return false;
}
}
 
if (class_exists('PEAR_Error')) {
 
class Services_JSON_Error extends PEAR_Error
{
function Services_JSON_Error($message = 'unknown error', $code = null,
$mode = null, $options = null, $userinfo = null)
{
parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
}
}
 
} else {
 
/**
* @todo Ultimately, this class shall be descended from PEAR_Error
*/
class Services_JSON_Error
{
function Services_JSON_Error($message = 'unknown error', $code = null,
$mode = null, $options = null, $userinfo = null)
{
 
}
}
 
}
?>
/trunk/jrest/lib/PDF.php
New file
0,0 → 1,3001
<?php
/**
* File_PDF::
*
* The File_PDF:: class provides a PHP-only implementation of a PDF library.
* No external libs or PHP extensions are required.
*
* Based on the FPDF class by Olivier Plathey (http://www.fpdf.org).
*
* Copyright 2001-2003 Olivier Plathey <olivier@fpdf.org>
* Copyright 2003-2007 The Horde Project (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* $Horde: framework/File_PDF/PDF.php,v 1.48 2007/01/05 13:12:21 jan Exp $
*
* @author Olivier Plathey <olivier@fpdf.org>
* @author Marko Djukic <marko@oblo.com>
* @author Jan Schneider <jan@horde.org>
* @package File_PDF
* @category Fileformats
*/
class File_PDF {
 
/**
* Current page number.
*
* @var integer
*/
var $_page = 0;
 
/**
* Current object number.
*
* @var integer
*/
var $_n = 2;
 
/**
* Array of object offsets.
*
* @var array
*/
var $_offsets = array();
 
/**
* Buffer holding in-memory PDF.
*
* @var string
*/
var $_buffer = '';
 
/**
* Array containing the pages.
*
* @var array
*/
var $_pages = array();
 
/**
* Current document state.
* 0 - initial state
* 1 - document opened
* 2 - page opened
* 3 - document closed
*
* @var integer
*/
var $_state = 0;
 
/**
* Flag indicating if PDF file is to be compressed or not.
*
* @var boolean
*/
var $_compress;
 
/**
* The default page orientation.
*
* @var string
*/
var $_default_orientation;
 
/**
* The current page orientation.
*
* @var string
*/
var $_current_orientation;
 
/**
* Array indicating orientation changes.
*
* @var array
*/
var $_orientation_changes = array();
 
/**
* Current width of page format in points.
*
* @var float
*/
var $fwPt;
 
/**
* Current height of page format in points.
*
* @var float
*/
var $fhPt;
 
/**
* Current width of page format in user units.
*
* @var float
*/
var $fw;
 
/**
* Current height of page format in user units.
*
* @var float
*/
var $fh;
 
/**
* Current width of page in points.
*
* @var float
*/
var $wPt;
 
/**
* Current height of page in points.
*
* @var float
*/
var $hPt;
 
/**
* Current width of page in user units
*
* @var float
*/
var $w;
 
/**
* Current height of page in user units
*
* @var float
*/
var $h;
 
/**
* Scale factor (number of points in user units).
*
* @var float
*/
var $_scale;
 
/**
* Left page margin size.
*
* @var float
*/
var $_left_margin;
 
/**
* Top page margin size.
*
* @var float
*/
var $_top_margin;
 
/**
* Right page margin size.
*
* @var float
*/
var $_right_margin;
 
/**
* Break page margin size, the bottom margin which triggers a page break.
*
* @var float
*/
var $_break_margin;
 
/**
* Cell margin size.
*
* @var float
*/
var $_cell_margin;
 
/**
* The current horizontal position for cell positioning.
* Value is set in user units and is calculated from the top left corner
* as origin.
*
* @var float
*/
var $x;
 
/**
* The current vertical position for cell positioning.
* Value is set in user units and is calculated from the top left corner
* as origin.
*
* @var float
*/
var $y;
 
/**
* The height of the last cell printed.
*
* @var float
*/
var $_last_height;
 
/**
* Line width in user units.
*
* @var float
*/
var $_line_width;
 
/**
* An array of standard font names.
*
* @var array
*/
var $_core_fonts = array('courier' => 'Courier',
'courierB' => 'Courier-Bold',
'courierI' => 'Courier-Oblique',
'courierBI' => 'Courier-BoldOblique',
'helvetica' => 'Helvetica',
'helveticaB' => 'Helvetica-Bold',
'helveticaI' => 'Helvetica-Oblique',
'helveticaBI' => 'Helvetica-BoldOblique',
'times' => 'Times-Roman',
'timesB' => 'Times-Bold',
'timesI' => 'Times-Italic',
'timesBI' => 'Times-BoldItalic',
'symbol' => 'Symbol',
'zapfdingbats' => 'ZapfDingbats');
 
/**
* An array of used fonts.
*
* @var array
*/
var $_fonts = array();
 
/**
* An array of font files.
*
* @var array
*/
var $_font_files = array();
 
/**
* An array of encoding differences.
*
* @var array
*/
var $_diffs = array();
 
/**
* An array of used images.
*
* @var array
*/
var $_images = array();
 
/**
* An array of links in pages.
*
* @var array
*/
var $_page_links;
 
/**
* An array of internal links.
*
* @var array
*/
var $_links = array();
 
/**
* Current font family.
*
* @var string
*/
var $_font_family = '';
 
/**
* Current font style.
*
* @var string
*/
var $_font_style = '';
 
/**
* Underlining flag.
*
* @var boolean
*/
var $_underline = false;
 
/**
* An array containing current font info.
*
* @var array
*/
var $_current_font;
 
/**
* Current font size in points.
*
* @var float
*/
var $_font_size_pt = 12;
 
/**
* Current font size in user units.
*
* @var float
*/
var $_font_size;
 
/**
* Commands for filling color.
*
* @var string
*/
var $_fill_color = '0 g';
 
/**
* Commands for text color.
*
* @var string
*/
var $_text_color = '0 g';
 
/**
* Whether text color is different from fill color.
*
* @var boolean
*/
var $_color_flag = false;
 
/**
* Commands for drawing color.
*
* @var string
*/
var $_draw_color = '0 G';
 
/**
* Word spacing.
*
* @var integer
*/
var $_word_spacing = 0;
 
/**
* Automatic page breaking.
*
* @var boolean
*/
var $_auto_page_break;
 
/**
* Threshold used to trigger page breaks.
*
* @var float
*/
var $_page_break_trigger;
 
/**
* Flag set when processing footer.
*
* @var boolean
*/
var $_in_footer = false;
 
/**
* Zoom display mode.
*
* @var string
*/
var $_zoom_mode;
 
/**
* Layout display mode.
*
* @var string
*/
var $_layout_mode;
 
/**
* An array containing the document info, consisting of:
* - title
* - subject
* - author
* - keywords
* - creator
*
* @var array
*/
var $_info = array();
 
/**
* Alias for total number of pages.
*
* @var string
*/
var $_alias_nb_pages = '{nb}';
 
/**
* Attempts to return a conrete PDF instance. It allows to set up the page
* format, the orientation and the units of measurement used in all the
* methods (except for the font sizes).
*
* Example:<pre>
* $pdf = &File_PDF::factory(array('orientation' => 'P',
* 'unit' => 'mm',
* 'format' => 'A4'));</pre>
*
* @param array $params A hash with parameters for the created PDF object.
* Possible parameters are:
* orientation - Default page orientation. Possible
* values are (case insensitive):
* <pre>
* - P or Portrait (default)
* - L or Landscape
* </pre>
* unit - User measure units. Possible values values
* are:
* <pre>
* - pt: point
* - mm: millimeter (default)
* - cm: centimeter
* - in: inch
* </pre>
* A point equals 1/72 of inch, that is to say about
* 0.35 mm (an inch being 2.54 cm). This is a very
* common unit in typography; font sizes are
* expressed in that unit.
* format - The format used for pages. It can be
* either one of the following values (case
* insensitive):
* <pre>
* - A3
* - A4 (default)
* - A5
* - Letter
* - Legal
* </pre>
* or a custom format in the form of a two-element
* array containing the width and the height
* (expressed in the unit given by the unit
* parameter).
* @param string $class The concrete class name to return an instance of.
* Defaults to File_PDF.
*/
function &factory($params = array(), $class = 'File_PDF')
{
/* Check for PHP locale-related bug. */
if (1.1 == 1) {
$error = File_PDF::raiseError('Do not alter the locale before including the class file.');
return $error;
}
 
/* Default parameters. */
$defaults = array('orientation' => 'P', 'unit' => 'mm', 'format' => 'A4');
 
/* Backward compatibility with old method signature. */
/* Should be removed a few versions later. */
if (!is_array($params)) {
$class = 'File_PDF';
$params = $defaults;
$names = array_keys($defaults);
for ($i = 0; $i < func_num_args(); $i++) {
$params[$names[$i]] = func_get_arg($i);
}
} else {
$params = array_merge($defaults, $params);
}
 
/* Create the PDF object. */
$pdf = &new $class();
 
/* Scale factor. */
if ($params['unit'] == 'pt') {
$pdf->_scale = 1;
} elseif ($params['unit'] == 'mm') {
$pdf->_scale = 72 / 25.4;
} elseif ($params['unit'] == 'cm') {
$pdf->_scale = 72 / 2.54;
} elseif ($params['unit'] == 'in') {
$pdf->_scale = 72;
} else {
$error = File_PDF::raiseError(sprintf('Incorrect units: %s', $params['unit']));
return $error;
}
/* Page format. */
if (is_string($params['format'])) {
$params['format'] = strtolower($params['format']);
if ($params['format'] == 'a3') {
$params['format'] = array(841.89, 1190.55);
} elseif ($params['format'] == 'a4') {
$params['format'] = array(595.28, 841.89);
} elseif ($params['format'] == 'a5') {
$params['format'] = array(420.94, 595.28);
} elseif ($params['format'] == 'letter') {
$params['format'] = array(612, 792);
} elseif ($params['format'] == 'legal') {
$params['format'] = array(612, 1008);
} else {
$error = File_PDF::raiseError(sprintf('Unknown page format: %s', $params['format']));
return $error;
}
$pdf->fwPt = $params['format'][0];
$pdf->fhPt = $params['format'][1];
} else {
$pdf->fwPt = $params['format'][0] * $pdf->_scale;
$pdf->fhPt = $params['format'][1] * $pdf->_scale;
}
$pdf->fw = $pdf->fwPt / $pdf->_scale;
$pdf->fh = $pdf->fhPt / $pdf->_scale;
 
/* Page orientation. */
$params['orientation'] = strtolower($params['orientation']);
if ($params['orientation'] == 'p' || $params['orientation'] == 'portrait') {
$pdf->_default_orientation = 'P';
$pdf->wPt = $pdf->fwPt;
$pdf->hPt = $pdf->fhPt;
} elseif ($params['orientation'] == 'l' || $params['orientation'] == 'landscape') {
$pdf->_default_orientation = 'L';
$pdf->wPt = $pdf->fhPt;
$pdf->hPt = $pdf->fwPt;
} else {
$error = File_PDF::raiseError(sprintf('Incorrect orientation: %s', $params['orientation']));
return $error;
}
$pdf->_current_orientation = $pdf->_default_orientation;
$pdf->w = $pdf->wPt / $pdf->_scale;
$pdf->h = $pdf->hPt / $pdf->_scale;
 
/* Page margins (1 cm) */
$margin = 28.35 / $pdf->_scale;
$pdf->setMargins($margin, $margin);
 
/* Interior cell margin (1 mm) */
$pdf->_cell_margin = $margin / 10;
 
/* Line width (0.2 mm) */
$pdf->_line_width = .567 / $pdf->_scale;
 
/* Automatic page break */
$pdf->setAutoPageBreak(true, 2 * $margin);
 
/* Full width display mode */
$pdf->setDisplayMode('fullwidth');
 
/* Compression */
$pdf->setCompression(true);
 
return $pdf;
}
 
/**
* Returns a PEAR_Error object. Wraps around PEAR::raiseError() to
* avoid having to include PEAR.php unless an error occurs.
*
* @param mixed $error The error message.
*
* @return object PEAR_Error
*/
function raiseError($error)
{
require_once 'PEAR.php';
return PEAR::raiseError($error);
}
 
/**
* Defines the left, top and right margins. By default, they equal 1 cm.
* Call this method to change them.
*
* @param float $left Left margin.
* @param float $top Top margin.
* @param float $right Right margin. If not specified default to the value
* of the left one.
*
* @see File_PDF::setAutoPageBreak
* @see File_PDF::setLeftMargin
* @see File_PDF::setRightMargin
* @see File_PDF::setTopMargin
*/
function setMargins($left, $top, $right = null)
{
/* Set left and top margins. */
$this->_left_margin = $left;
$this->_top_margin = $top;
/* If no right margin set default to same as left. */
$this->_right_margin = (is_null($right) ? $left : $right);
}
 
/**
* Defines the left margin. The method can be called before creating the
* first page.
* If the current abscissa gets out of page, it is brought back to the
* margin.
*
* @param float $margin The margin.
*
* @see File_PDF::setAutoPageBreak
* @see File_PDF::setMargins
* @see File_PDF::setRightMargin
* @see File_PDF::setTopMargin
*/
function setLeftMargin($margin)
{
$this->_left_margin = $margin;
/* If there is a current page and the current X position is less than
* margin set the X position to the margin value. */
if ($this->_page > 0 && $this->x < $margin) {
$this->x = $margin;
}
}
 
/**
* Defines the top margin. The method can be called before creating the
* first page.
*
* @param float $margin The margin.
*/
function setTopMargin($margin)
{
$this->_top_margin = $margin;
}
 
/**
* Defines the right margin. The method can be called before creating the
* first page.
*
* @param float $margin The margin.
*/
function setRightMargin($margin)
{
$this->_right_margin = $margin;
}
 
/**
* Returns the actual page width.
*
* @since File_PDF 0.2.0
* @since Horde 3.2
*
* @return float The page width.
*/
function getPageWidth()
{
return ($this->w - $this->_right_margin - $this->_left_margin);
}
 
/**
* Returns the actual page height.
*
* @since File_PDF 0.2.0
* @since Horde 3.2
*
* @return float The page height.
*/
function getPageHeight()
{
return ($this->h - $this->_top_margin - $this->_break_margin);
}
 
/**
* Enables or disables the automatic page breaking mode. When enabling,
* the second parameter is the distance from the bottom of the page that
* defines the triggering limit. By default, the mode is on and the margin
* is 2 cm.
*
* @param boolean auto Boolean indicating if mode should be on or off.
* @param float $margin Distance from the bottom of the page.
*/
function setAutoPageBreak($auto, $margin = 0)
{
$this->_auto_page_break = $auto;
$this->_break_margin = $margin;
$this->_page_break_trigger = $this->h - $margin;
}
 
/**
* Defines the way the document is to be displayed by the viewer. The zoom
* level can be set: pages can be displayed entirely on screen, occupy the
* full width of the window, use real size, be scaled by a specific
* zooming factor or use viewer default (configured in the Preferences
* menu of Acrobat). The page layout can be specified too: single at once,
* continuous display, two columns or viewer default.
* By default, documents use the full width mode with continuous display.
*
* @param mixed $zoom The zoom to use. It can be one of the
* following string values:
* - fullpage: entire page on screen
* - fullwidth: maximum width of window
* - real: uses real size (100% zoom)
* - default: uses viewer default mode
* or a number indicating the zooming factor.
* @param string layout The page layout. Possible values are:
* - single: one page at once
* - continuous: pages in continuously
* - two: two pages on two columns
* - default: uses viewer default mode
* Default value is continuous.
*/
function setDisplayMode($zoom, $layout = 'continuous')
{
$zoom = strtolower($zoom);
if ($zoom == 'fullpage' || $zoom == 'fullwidth' || $zoom == 'real'
|| $zoom == 'default' || !is_string($zoom)) {
$this->_zoom_mode = $zoom;
} elseif ($zoom == 'zoom') {
$this->_zoom_mode = $layout;
} else {
return $this->raiseError(sprintf('Incorrect zoom display mode: %s', $zoom));
}
 
$layout = strtolower($layout);
if ($layout == 'single' || $layout == 'continuous' || $layout == 'two'
|| $layout == 'default') {
$this->_layout_mode = $layout;
} elseif ($zoom != 'zoom') {
return $this->raiseError(sprintf('Incorrect layout display mode: %s', $layout));
}
}
 
/**
* Activates or deactivates page compression. When activated, the internal
* representation of each page is compressed, which leads to a compression
* ratio of about 2 for the resulting document.
* Compression is on by default.
* Note: the Zlib extension is required for this feature. If not present,
* compression will be turned off.
*
* @param boolean $compress Boolean indicating if compression must be
* enabled or not.
*/
function setCompression($compress)
{
/* If no gzcompress function is available then default to false. */
$this->_compress = (function_exists('gzcompress') ? $compress : false);
}
 
/**
* Set the info to a document. Possible info settings are:
* - title
* - subject
* - author
* - keywords
* - creator
*
* @param mixed $info If passed as an array then the complete hash
* containing the info to be inserted into the
* document. Otherwise the name of setting to be set.
* @param string $value The value of the setting.
*/
function setInfo($info, $value = '')
{
if (is_array($info)) {
$this->_info = $info;
} else {
$this->_info[$info] = $value;
}
}
 
/**
* Defines an alias for the total number of pages. It will be substituted
* as the document is closed.
*
* Example:
* class My_File_PDF extends File_PDF {
* function footer()
* {
* // Go to 1.5 cm from bottom
* $this->setY(-15);
* // Select Arial italic 8
* $this->setFont('Arial', 'I', 8);
* // Print current and total page numbers
* $this->cell(0, 10, 'Page ' . $this->getPageNo() . '/{nb}', 0,
* 0, 'C');
* }
* }
* $pdf = &My_File_PDF::factory();
* $pdf->aliasNbPages();
*
* @param string $alias The alias. Default value: {nb}.
*
* @see File_PDF::getPageNo
* @see File_PDF::footer
*/
function aliasNbPages($alias = '{nb}')
{
$this->_alias_nb_pages = $alias;
}
 
/**
* This method begins the generation of the PDF document; it must be
* called before any output commands. No page is created by this method,
* therefore it is necessary to call File_PDF::addPage.
*
* @see File_PDF::addPage
* @see File_PDF::close
*/
function open()
{
$this->_beginDoc();
}
 
/**
* Terminates the PDF document. It is not necessary to call this method
* explicitly because File_PDF::output does it automatically.
* If the document contains no page, File_PDF::addPage is called to prevent
* from getting an invalid document.
*
* @see File_PDF::open
* @see File_PDF::output
*/
function close()
{
/* Terminate document */
if ($this->_page == 0) {
$this->addPage();
}
/* Page footer */
$this->_in_footer = true;
$this->footer();
$this->_in_footer = false;
/* Close page */
$this->_endPage();
/* Close document */
$this->_endDoc();
}
 
/**
* Adds a new page to the document. If a page is already present, the
* File_PDF::footer method is called first to output the footer. Then the
* page is added, the current position set to the top-left corner according
* to the left and top margins, and File_PDF::header is called to display
* the header.
* The font which was set before calling is automatically restored. There
* is no need to call File_PDF::setFont again if you want to continue with
* the same font. The same is true for colors and line width.
* The origin of the coordinate system is at the top-left corner and
* increasing ordinates go downwards.
*
* @param string $orientation Page orientation. Possible values
* are (case insensitive):
* - P or Portrait
* - L or Landscape
* The default value is the one passed to the
* constructor.
*
* @see File_PDF::PDF
* @see File_PDF::header
* @see File_PDF::footer
* @see File_PDF::setMargins
*/
function addPage($orientation = '')
{
/* For good measure make sure this is called. */
$this->_beginDoc();
 
/* Save style settings so that they are not overridden by footer(). */
$lw = $this->_line_width;
$dc = $this->_draw_color;
$fc = $this->_fill_color;
$tc = $this->_text_color;
$cf = $this->_color_flag;
if ($this->_page > 0) {
/* Page footer. */
$this->_in_footer = true;
$this->footer();
$this->_in_footer = false;
/* Close page. */
$this->_endPage();
}
/* Start new page. */
$this->_beginPage($orientation);
/* Set line cap style to square. */
$this->_out('2 J');
/* Set line width. */
$this->_line_width = $lw;
$this->_out(sprintf('%.2f w', $lw * $this->_scale));
/* Set font for the beginning of the page. */
$font_family = null;
if ($this->_font_family) {
$font_family = $this->_font_family;
$font_style = $this->_font_style . ($this->_underline ? 'U' : '');
$font_size = $this->_font_size_pt;
$this->setFont($font_family, $font_style, $font_size);
}
/* Set colors. */
$this->_fill_color = $fc;
/* Check if fill color has been set before this page. */
if ($this->_fill_color != '0 g') {
$this->_out($this->_fill_color);
}
$this->_draw_color = $dc;
/* Check if draw color has been set before this page. */
if ($this->_draw_color != '0 G') {
$this->_out($this->_draw_color);
}
$this->_text_color = $tc;
$this->_color_flag = $cf;
/* Page header. */
$this->header();
/* Restore line width. */
if ($this->_line_width != $lw) {
$this->_line_width = $lw;
$this->_out(sprintf('%.2f w', $lw * $this->_scale));
}
/* Make sure the font is set for this page as it was before the
* header. */
if ($font_family) {
$this->setFont($font_family, $font_style, $font_size, true);
}
/* Restore colors. */
if ($this->_draw_color != $dc) {
$this->_draw_color = $dc;
$this->_out($dc);
}
if ($this->_fill_color != $fc) {
$this->_fill_color = $fc;
$this->_out($fc);
}
$this->_text_color = $tc;
$this->_color_flag = $cf;
}
 
/**
* This method is used to render the page header. It is automatically
* called by File_PDF::addPage and should not be called directly by the
* application. The implementation in File_PDF:: is empty, so you have to
* subclass it and override the method if you want a specific processing.
*
* Example:
*
* class My_File_PDF extends File_PDF {
* function header()
* {
* // Select Arial bold 15
* $this->setFont('Arial', 'B', 15);
* // Move to the right
* $this->cell(80);
* // Framed title
* $this->cell(30, 10, 'Title', 1, 0, 'C');
* // Line break
* $this->newLine(20);
* }
* }
*
* @see File_PDF::footer
*/
function header()
{
/* To be implemented in your own inherited class. */
}
 
/**
* This method is used to render the page footer. It is automatically
* called by File_PDF::addPage and File_PDF::close and should not be called
* directly by the application. The implementation in File_PDF:: is empty,
* so you have to subclass it and override the method if you want a specific
* processing.
*
* Example:
*
* class My_File_PDF extends File_PDF {
* function footer()
* {
* // Go to 1.5 cm from bottom
* $this->setY(-15);
* // Select Arial italic 8
* $this->setFont('Arial', 'I', 8);
* // Print centered page number
* $this->cell(0, 10, 'Page ' . $this->getPageNo(), 0, 0, 'C');
* }
* }
*
* @see File_PDF::header
*/
function footer()
{
/* To be implemented in your own inherited class. */
}
 
/**
* Returns the current page number.
*
* @return integer
*
* @see File_PDF::aliasNbPages
*/
function getPageNo()
{
return $this->_page;
}
 
/**
* Sets the fill color.
*
* Depending on the colorspace called, the number of color component
* parameters required can be either 1, 3 or 4. The method can be called
* before the first page is created and the color is retained from page to
* page.
*
* @param string $cs Indicates the colorspace which can be either 'rgb',
* 'cmyk' or 'gray'. Defaults to 'rgb'.
* @param float $c1 First color component, floating point value between 0
* and 1. Required for gray, rgb and cmyk.
* @param float $c2 Second color component, floating point value between
* 0 and 1. Required for rgb and cmyk.
* @param float $c3 Third color component, floating point value between
* 0 and 1. Required for rgb and cmyk.
* @param float $c4 Fourth color component, floating point value between
* 0 and 1. Required for cmyk.
*
* @see File_PDF::setTextColor
* @see File_PDF::setDrawColor
* @see File_PDF::rect
* @see File_PDF::cell
* @see File_PDF::multiCell
*/
function setFillColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0)
{
$cs = strtolower($cs);
if ($cs == 'rgb') {
$this->_fill_color = sprintf('%.3f %.3f %.3f rg', $c1, $c2, $c3);
} elseif ($cs == 'cmyk') {
$this->_fill_color = sprintf('%.3f %.3f %.3f %.3f k', $c1, $c2, $c3, $c4);
} else {
$this->_fill_color = sprintf('%.3f g', $c1);
}
if ($this->_page > 0) {
$this->_out($this->_fill_color);
}
$this->_color_flag = $this->_fill_color != $this->_text_color;
}
 
/**
* Sets the text color.
*
* Depending on the colorspace called, the number of color component
* parameters required can be either 1, 3 or 4. The method can be called
* before the first page is created and the color is retained from page to
* page.
*
* @param string $cs Indicates the colorspace which can be either 'rgb',
* 'cmyk' or 'gray'. Defaults to 'rgb'.
* @param float $c1 First color component, floating point value between 0
* and 1. Required for gray, rgb and cmyk.
* @param float $c2 Second color component, floating point value between
* 0 and 1. Required for rgb and cmyk.
* @param float $c3 Third color component, floating point value between
* 0 and 1. Required for rgb and cmyk.
* @param float $c4 Fourth color component, floating point value between
* 0 and 1. Required for cmyk.
*
* @since File_PDF 0.2.0
* @since Horde 3.2
* @see File_PDF::setFillColor
* @see File_PDF::setDrawColor
* @see File_PDF::rect
* @see File_PDF::cell
* @see File_PDF::multiCell
*/
function setTextColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0)
{
$cs = strtolower($cs);
if ($cs == 'rgb') {
$this->_text_color = sprintf('%.3f %.3f %.3f rg', $c1, $c2, $c3);
} elseif ($cs == 'cmyk') {
$this->_text_color = sprintf('%.3f %.3f %.3f %.3f k', $c1, $c2, $c3, $c4);
} else {
$this->_text_color = sprintf('%.3f g', $c1);
}
if ($this->_page > 0) {
$this->_out($this->_text_color);
}
$this->_color_flag = $this->_fill_color != $this->_text_color;
}
 
/**
* Sets the draw color, used when drawing lines. Depending on the
* colorspace called, the number of color component parameters required
* can be either 1, 3 or 4. The method can be called before the first page
* is created and the color is retained from page to page.
*
* @param string $cs Indicates the colorspace which can be either 'rgb',
* 'cmyk' or 'gray'. Defaults to 'rgb'.
* @param float $c1 First color component, floating point value between 0
* and 1. Required for gray, rgb and cmyk.
* @param float $c2 Second color component, floating point value between
* 0 and 1. Required for rgb and cmyk.
* @param float $c3 Third color component, floating point value between 0
* and 1. Required for rgb and cmyk.
* @param float $c4 Fourth color component, floating point value between
* 0 and 1. Required for cmyk.
*
* @see File_PDF::setFillColor
* @see File_PDF::line
* @see File_PDF::rect
* @see File_PDF::cell
* @see File_PDF::multiCell
*/
function setDrawColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0)
{
$cs = strtolower($cs);
if ($cs == 'rgb') {
$this->_draw_color = sprintf('%.3f %.3f %.3f RG', $c1, $c2, $c3);
} elseif ($cs == 'cmyk') {
$this->_draw_color = sprintf('%.3f %.3f %.3f %.3f K', $c1, $c2, $c3, $c4);
} else {
$this->_draw_color = sprintf('%.3f G', $c1);
}
if ($this->_page > 0) {
$this->_out($this->_draw_color);
}
}
 
/**
* Returns the length of a text string. A font must be selected.
*
* @param string $text The text whose length is to be computed.
* @param boolean $pt Boolean to indicate if the width should be returned
* in points or user units. Default is 'false'.
*
* @return float
*/
function getStringWidth($text, $pt = false)
{
$text = (string)$text;
$width = 0;
$length = strlen($text);
for ($i = 0; $i < $length; $i++) {
$width += $this->_current_font['cw'][$text{$i}];
}
 
/* Adjust for word spacing. */
$width += $this->_word_spacing * substr_count($text, ' ') * $this->_current_font['cw'][' '];
 
if ($pt) {
return $width * $this->_font_size_pt / 1000;
} else {
return $width * $this->_font_size / 1000;
}
}
 
/**
* Defines the line width. By default, the value equals 0.2 mm. The method
* can be called before the first page is created and the value is
* retained from page to page.
*
* @param float $width The width.
*
* @see File_PDF::line
* @see File_PDF::rect
* @see File_PDF::cell
* @see File_PDF::multiCell
*/
function setLineWidth($width)
{
$this->_line_width = $width;
if ($this->_page > 0) {
$this->_out(sprintf('%.2f w', $width * $this->_scale));
}
}
 
/**
* Draws a line between two points.
*
* All coordinates can be negative to provide values from the right or
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
*
* @param float $x1 Abscissa of first point.
* @param float $y1 Ordinate of first point.
* @param float $x2 Abscissa of second point.
* @param float $y2 Ordinate of second point.
*
* @see File_PDF::setLineWidth
* @see File_PDF::setDrawColor.
*/
function line($x1, $y1, $x2, $y2)
{
if ($x1 < 0) {
$x1 += $this->w;
}
if ($y1 < 0) {
$y1 += $this->h;
}
if ($x2 < 0) {
$x2 += $this->w;
}
if ($y2 < 0) {
$y2 += $this->h;
}
 
$this->_out(sprintf('%.2f %.2f m %.2f %.2f l S', $x1 * $this->_scale, ($this->h - $y1) * $this->_scale, $x2 * $this->_scale, ($this->h - $y2) * $this->_scale));
}
 
/**
* Outputs a rectangle. It can be drawn (border only), filled (with no
* border) or both.
*
* All coordinates can be negative to provide values from the right or
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
*
* @param float $x Abscissa of upper-left corner.
* @param float $y Ordinate of upper-left corner.
* @param float $width Width.
* @param float $height Height.
* @param float $style Style of rendering. Possible values are:
* - D or empty string: draw (default)
* - F: fill
* - DF or FD: draw and fill
*
* @see File_PDF::setLineWidth
* @see File_PDF::setDrawColor
* @see File_PDF::setFillColor
*/
function rect($x, $y, $width, $height, $style = '')
{
if ($x < 0) {
$x += $this->w;
}
if ($y < 0) {
$y += $this->h;
}
 
$style = strtoupper($style);
if ($style == 'F') {
$op = 'f';
} elseif ($style == 'FD' || $style == 'DF') {
$op = 'B';
} else {
$op = 'S';
}
 
$x = $this->_toPt($x);
$y = $this->_toPt($y);
$width = $this->_toPt($width);
$height = $this->_toPt($height);
 
$this->_out(sprintf('%.2f %.2f %.2f %.2f re %s', $x, $this->hPt - $y, $width, -$height, $op));
}
 
/**
* Outputs a circle. It can be drawn (border only), filled (with no
* border) or both.
*
* All coordinates can be negative to provide values from the right or
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
*
* @param float $x Abscissa of the center of the circle.
* @param float $y Ordinate of the center of the circle.
* @param float $r Circle radius.
* @param string $style Style of rendering. Possible values are:
* - D or empty string: draw (default)
* - F: fill
* - DF or FD: draw and fill
*/
function circle($x, $y, $r, $style = '')
{
if ($x < 0) {
$x += $this->w;
}
if ($y < 0) {
$y += $this->h;
}
 
$style = strtolower($style);
if ($style == 'f') {
$op = 'f'; // Style is fill only.
} elseif ($style == 'fd' || $style == 'df') {
$op = 'B'; // Style is fill and stroke.
} else {
$op = 'S'; // Style is stroke only.
}
 
$x = $this->_toPt($x);
$y = $this->_toPt($y);
$r = $this->_toPt($r);
 
/* Invert the y scale. */
$y = $this->hPt - $y;
/* Length of the Bezier control. */
$b = $r * 0.552;
 
/* Move from the given origin and set the current point
* to the start of the first Bezier curve. */
$c = sprintf('%.2f %.2f m', $x - $r, $y);
$x = $x - $r;
/* First circle quarter. */
$c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c',
$x, $y + $b, // First control point.
$x + $r - $b, $y + $r, // Second control point.
$x + $r, $y + $r); // Final point.
/* Set x/y to the final point. */
$x = $x + $r;
$y = $y + $r;
/* Second circle quarter. */
$c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c',
$x + $b, $y,
$x + $r, $y - $r + $b,
$x + $r, $y - $r);
/* Set x/y to the final point. */
$x = $x + $r;
$y = $y - $r;
/* Third circle quarter. */
$c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c',
$x, $y - $b,
$x - $r + $b, $y - $r,
$x - $r, $y - $r);
/* Set x/y to the final point. */
$x = $x - $r;
$y = $y - $r;
/* Fourth circle quarter. */
$c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c %s',
$x - $b, $y,
$x - $r, $y + $r - $b,
$x - $r, $y + $r,
$op);
/* Output the whole string. */
$this->_out($c);
}
 
/**
* Imports a TrueType or Type1 font and makes it available. It is
* necessary to generate a font definition file first with the
* makefont.php utility.
* The location of the definition file (and the font file itself when
* embedding) must be found at the full path name included.
*
* Example:
* $pdf->addFont('Comic', 'I');
* is equivalent to:
* $pdf->addFont('Comic', 'I', 'comici.php');
*
* @param string $family Font family. The name can be chosen arbitrarily.
* If it is a standard family name, it will
* override the corresponding font.
* @param string $style Font style. Possible values are (case
* insensitive):
* - empty string: regular (default)
* - B: bold
* - I: italic
* - BI or IB: bold italic
* @param string $file The font definition file. By default, the name is
* built from the family and style, in lower case
* with no space.
*
* @see File_PDF::setFont
*/
function addFont($family, $style = '', $file = '')
{
$family = strtolower($family);
if ($family == 'arial') {
$family = 'helvetica';
}
 
$style = strtoupper($style);
if ($style == 'IB') {
$style = 'BI';
}
if (isset($this->_fonts[$family . $style])) {
return $this->raiseError(sprintf('Font already added: %s %s', $family, $style));
}
if ($file == '') {
$file = str_replace(' ', '', $family) . strtolower($style) . '.php';
}
include($file);
if (!isset($name)) {
return $this->raiseError('Could not include font definition file.');
}
$i = count($this->_fonts) + 1;
$this->_fonts[$family . $style] = array('i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'enc' => $enc, 'file' => $file);
if ($diff) {
/* Search existing encodings. */
$d = 0;
$nb = count($this->_diffs);
for ($i = 1; $i <= $nb; $i++) {
if ($this->_diffs[$i] == $diff) {
$d = $i;
break;
}
}
if ($d == 0) {
$d = $nb + 1;
$this->_diffs[$d] = $diff;
}
$this->_fonts[$family.$style]['diff'] = $d;
}
if ($file) {
if ($type == 'TrueType') {
$this->_font_files[$file] = array('length1' => $originalsize);
} else {
$this->_font_files[$file] = array('length1' => $size1, 'length2' => $size2);
}
}
}
 
/**
* Sets the font used to print character strings. It is mandatory to call
* this method at least once before printing text or the resulting
* document would not be valid. The font can be either a standard one or a
* font added via the File_PDF::addFont method. Standard fonts use Windows
* encoding cp1252 (Western Europe).
* The method can be called before the first page is created and the font
* is retained from page to page.
* If you just wish to change the current font size, it is simpler to call
* File_PDF::setFontSize.
*
* @param string $family Family font. It can be either a name defined by
* File_PDF::addFont or one of the standard families
* (case insensitive):
* - Courier (fixed-width)
* - Helvetica or Arial (sans serif)
* - Times (serif)
* - Symbol (symbolic)
* - ZapfDingbats (symbolic)
* It is also possible to pass an empty string. In
* that case, the current family is retained.
* @param string $style Font style. Possible values are (case
* insensitive):
* - empty string: regular
* - B: bold
* - I: italic
* - U: underline
* or any combination. The default value is regular.
* Bold and italic styles do not apply to Symbol and
* ZapfDingbats.
* @param integer $size Font size in points. The default value is the
* current size. If no size has been specified since
* the beginning of the document, the value taken
* is 12.
* @param boolean $force Force the setting of the font. Each new page will
* require a new call to File_PDF::setFont and
* settings this to true will make sure that the
* checks for same font calls will be skipped.
*
* @see File_PDF::addFont
* @see File_PDF::setFontSize
* @see File_PDF::cell
* @see File_PDF::multiCell
* @see File_PDF::Write
*/
function setFont($family, $style = '', $size = null, $force = false)
{
$family = strtolower($family);
if ($family == 'arial') {
/* Use helvetica instead of arial. */
$family = 'helvetica';
} elseif ($family == 'symbol' || $family == 'zapfdingbats') {
/* These two fonts do not have styles available. */
$style = '';
}
 
$style = strtoupper($style);
 
/* Underline is handled separately, if specified in the style var
* remove it from the style and set the underline flag. */
if (strpos($style, 'U') !== false) {
$this->_underline = true;
$style = str_replace('U', '', $style);
} else {
$this->_underline = false;
}
 
if ($style == 'IB') {
$style = 'BI';
}
 
/* If no size specified, use current size. */
if (is_null($size)) {
$size = $this->_font_size_pt;
}
 
/* If font requested is already the current font and no force setting
* of the font is requested (eg. when adding a new page) don't bother
* with the rest of the function and simply return. */
if ($this->_font_family == $family && $this->_font_style == $style &&
$this->_font_size_pt == $size && !$force) {
return;
}
 
/* Set the font key. */
$fontkey = $family . $style;
 
/* Test if already cached. */
if (!isset($this->_fonts[$fontkey])) {
/* Get the character width definition file. */
$font_widths = &File_PDF::_getFontFile($fontkey);
if (is_a($font_widths, 'PEAR_Error')) {
return $font_widths;
}
 
$i = count($this->_fonts) + 1;
$this->_fonts[$fontkey] = array(
'i' => $i,
'type' => 'core',
'name' => $this->_core_fonts[$fontkey],
'up' => -100,
'ut' => 50,
'cw' => $font_widths[$fontkey]);
}
 
/* Store font information as current font. */
$this->_font_family = $family;
$this->_font_style = $style;
$this->_font_size_pt = $size;
$this->_font_size = $size / $this->_scale;
$this->_current_font = &$this->_fonts[$fontkey];
 
/* Output font information if at least one page has been defined. */
if ($this->_page > 0) {
$this->_out(sprintf('BT /F%d %.2f Tf ET', $this->_current_font['i'], $this->_font_size_pt));
}
}
 
/**
* Defines the size of the current font.
*
* @param float $size The size (in points).
*
* @see File_PDF::setFont
*/
function setFontSize($size)
{
/* If the font size is already the current font size, just return. */
if ($this->_font_size_pt == $size) {
return;
}
/* Set the current font size, both in points and scaled to user
* units. */
$this->_font_size_pt = $size;
$this->_font_size = $size / $this->_scale;
 
/* Output font information if at least one page has been defined. */
if ($this->_page > 0) {
$this->_out(sprintf('BT /F%d %.2f Tf ET', $this->_current_font['i'], $this->_font_size_pt));
}
}
 
/**
* Defines the style of the current font.
*
* @param string $style The font style.
*
* @see File_PDF::setFont
* @since File_PDF 0.2.0
* @since Horde 3.2
*/
function setFontStyle($style)
{
$this->setFont($this->_font_family, $style);
}
 
/**
* Creates a new internal link and returns its identifier. An internal
* link is a clickable area which directs to another place within the
* document.
* The identifier can then be passed to File_PDF::cell, File_PDF::write,
* File_PDF::image or File_PDF::link. The destination is defined with
* File_PDF::setLink.
*
* @see File_PDF::cell
* @see File_PDF::Write
* @see File_PDF::image
* @see File_PDF::Link
* @see File_PDF::SetLink
*/
function addLink()
{
$n = count($this->_links) + 1;
$this->_links[$n] = array(0, 0);
return $n;
}
 
/**
* Defines the page and position a link points to.
*
* @param integer $link The link identifier returned by File_PDF::addLink.
* @param float $y Ordinate of target position; -1 indicates the
* current position. The default value is 0 (top of
* page).
* @param integer $page Number of target page; -1 indicates the current
* page. This is the default value.
*
* @see File_PDF::addLink
*/
function setLink($link, $y = 0, $page = -1)
{
if ($y == -1) {
$y = $this->y;
}
if ($page == -1) {
$page = $this->_page;
}
$this->_links[$link] = array($page, $y);
}
 
/**
* Puts a link on a rectangular area of the page. Text or image links are
* generally put via File_PDF::cell, File_PDF::Write or File_PDF::image,
* but this method can be useful for instance to define a clickable area
* inside an image.
*
* All coordinates can be negative to provide values from the right or
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
*
* @param float $x Abscissa of the upper-left corner of the rectangle.
* @param float $y Ordinate of the upper-left corner of the rectangle.
* @param float $width Width of the rectangle.
* @param float $height Height of the rectangle.
* @param mixed $link URL or identifier returned by File_PDF::addLink.
*
* @see File_PDF::addLink
* @see File_PDF::cell
* @see File_PDF::Write
* @see File_PDF::image
*/
function link($x, $y, $width, $height, $link)
{
if ($x < 0) {
$x += $this->w;
}
if ($y < 0) {
$y += $this->h;
}
 
/* Set up the coordinates with correct scaling in pt. */
$x = $this->_toPt($x);
$y = $this->hPt - $this->_toPt($y);
$width = $this->_toPt($width);
$height = $this->_toPt($height);
 
/* Save link to page links array. */
$this->_link($x, $y, $width, $height, $link);
}
 
/**
* Prints a character string. The origin is on the left of the first
* character, on the baseline. This method allows to place a string
* precisely on the page, but it is usually easier to use File_PDF::cell,
* File_PDF::multiCell or File_PDF::Write which are the standard methods to
* print text.
*
* All coordinates can be negative to provide values from the right or
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
*
* @param float $x Abscissa of the origin.
* @param float $y Ordinate of the origin.
* @param string $text String to print.
*
* @see File_PDF::setFont
* @see File_PDF::cell
* @see File_PDF::multiCell
* @see File_PDF::Write
*/
function text($x, $y, $text)
{
if ($x < 0) {
$x += $this->w;
}
if ($y < 0) {
$y += $this->h;
}
 
/* Scale coordinates into points and set correct Y position. */
$x = $this->_toPt($x);
$y = $this->hPt - $this->_toPt($y);
 
/* Escape any potentially harmful characters. */
$text = $this->_escape($text);
 
$out = sprintf('BT %.2f %.2f Td (%s) Tj ET', $x, $y, $text);
if ($this->_underline && $text != '') {
$out .= ' ' . $this->_doUnderline($x, $y, $text);
}
if ($this->_color_flag) {
$out = sprintf('q %s %s Q', $this->_text_color, $out);
}
$this->_out($out);
}
 
/**
* Whenever a page break condition is met, the method is called, and the
* break is issued or not depending on the returned value. The default
* implementation returns a value according to the mode selected by
* File_PDF:setAutoPageBreak.
* This method is called automatically and should not be called directly
* by the application.
*
* @return boolean
*
* @see File_PDF::setAutoPageBreak.
*/
function acceptPageBreak()
{
return $this->_auto_page_break;
}
 
/**
* Prints a cell (rectangular area) with optional borders, background
* color and character string. The upper-left corner of the cell
* corresponds to the current position. The text can be aligned or
* centered. After the call, the current position moves to the right or to
* the next line. It is possible to put a link on the text.
* If automatic page breaking is enabled and the cell goes beyond the
* limit, a page break is done before outputting.
*
* @param float $width Cell width. If 0, the cell extends up to the right
* margin.
* @param float $height Cell height. Default value: 0.
* @param string $text String to print. Default value: empty.
* @param mixed $border Indicates if borders must be drawn around the
* cell. The value can be either a number:
* - 0: no border (default)
* - 1: frame
* or a string containing some or all of the
* following characters (in any order):
* - L: left
* - T: top
* - R: right
* - B: bottom
* @param integer $ln Indicates where the current position should go
* after the call. Possible values are:
* - 0: to the right (default)
* - 1: to the beginning of the next line
* - 2: below
* Putting 1 is equivalent to putting 0 and calling
* File_PDF::newLine just after.
* @param string $align Allows to center or align the text. Possible
* values are:
* - L or empty string: left (default)
* - C: center
* - R: right
* @param integer $fill Indicates if the cell fill type. Possible values
* are:
* - 0: transparent (default)
* - 1: painted
* @param string $link URL or identifier returned by
* File_PDF:addLink.
*
* @see File_PDF::setFont
* @see File_PDF::setDrawColor
* @see File_PDF::setFillColor
* @see File_PDF::setLineWidth
* @see File_PDF::addLink
* @see File_PDF::newLine
* @see File_PDF::multiCell
* @see File_PDF::Write
* @see File_PDF::setAutoPageBreak
*/
function cell($width, $height = 0, $text = '', $border = 0, $ln = 0,
$align = '', $fill = 0, $link = '')
{
$k = $this->_scale;
if ($this->y + $height > $this->_page_break_trigger &&
!$this->_in_footer && $this->AcceptPageBreak()) {
$x = $this->x;
$ws = $this->_word_spacing;
if ($ws > 0) {
$this->_word_spacing = 0;
$this->_out('0 Tw');
}
$this->addPage($this->_current_orientation);
$this->x = $x;
if ($ws > 0) {
$this->_word_spacing = $ws;
$this->_out(sprintf('%.3f Tw', $ws * $k));
}
}
if ($width == 0) {
$width = $this->w - $this->_right_margin - $this->x;
}
$s = '';
if ($fill == 1 || $border == 1) {
if ($fill == 1) {
$op = ($border == 1) ? 'B' : 'f';
} else {
$op = 'S';
}
$s = sprintf('%.2f %.2f %.2f %.2f re %s ', $this->x * $k, ($this->h - $this->y) * $k, $width * $k, -$height * $k, $op);
}
if (is_string($border)) {
if (strpos($border, 'L') !== false) {
$s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $this->x * $k, ($this->h - $this->y) * $k, $this->x * $k, ($this->h - ($this->y + $height)) * $k);
}
if (strpos($border, 'T') !== false) {
$s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $this->x * $k, ($this->h - $this->y) * $k, ($this->x + $width) * $k, ($this->h - $this->y) * $k);
}
if (strpos($border, 'R') !== false) {
$s .= sprintf('%.2f %.2f m %.2f %.2f l S ', ($this->x + $width) * $k, ($this->h - $this->y) * $k, ($this->x + $width) * $k, ($this->h - ($this->y + $height)) * $k);
}
if (strpos($border, 'B') !== false) {
$s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $this->x * $k, ($this->h - ($this->y + $height)) * $k, ($this->x + $width) * $k, ($this->h - ($this->y + $height)) * $k);
}
}
if ($text != '') {
if ($align == 'R') {
$dx = $width - $this->_cell_margin - $this->getStringWidth($text);
} elseif ($align == 'C') {
$dx = ($width - $this->getStringWidth($text)) / 2;
} else {
$dx = $this->_cell_margin;
}
if ($this->_color_flag) {
$s .= 'q ' . $this->_text_color . ' ';
}
$text = str_replace(')', '\\)', str_replace('(', '\\(', str_replace('\\', '\\\\', $text)));
$test2 = ((.5 * $height) + (.3 * $this->_font_size));
$test1 = $this->fhPt - (($this->y + $test2) * $k);
$s .= sprintf('BT %.2f %.2f Td (%s) Tj ET', ($this->x + $dx) * $k, ($this->h - ($this->y + .5 * $height + .3 * $this->_font_size)) * $k, $text);
if ($this->_underline) {
$s .= ' ' . $this->_doUnderline($this->x + $dx, $this->y + .5 * $height + .3 * $this->_font_size, $text);
}
if ($this->_color_flag) {
$s .= ' Q';
}
if ($link) {
$this->link($this->x + $dx, $this->y + .5 * $height-.5 * $this->_font_size, $this->getStringWidth($text), $this->_font_size, $link);
}
}
if ($s) {
$this->_out($s);
}
$this->_last_height = $height;
if ($ln > 0) {
/* Go to next line. */
$this->y += $height;
if ($ln == 1) {
$this->x = $this->_left_margin;
}
} else {
$this->x += $width;
}
}
 
/**
* This method allows printing text with line breaks. They can be
* automatic (as soon as the text reaches the right border of the cell) or
* explicit (via the \n character). As many cells as necessary are output,
* one below the other.
* Text can be aligned, centered or justified. The cell block can be
* framed and the background painted.
*
* @param float $width Width of cells. If 0, they extend up to the right
* margin of the page.
* @param float $height Height of cells.
* @param string $text String to print.
* @param mixed $border Indicates if borders must be drawn around the cell
* block. The value can be either a number:
* - 0: no border (default)
* - 1: frame
* or a string containing some or all of the
* following characters (in any order):
* - L: left
* - T: top
* - R: right
* - B: bottom
* @param string $align Sets the text alignment. Possible values are:
* - L: left alignment
* - C: center
* - R: right alignment
* - J: justification (default value)
* @param integer $fill Indicates if the cell background must:
* - 0: transparent (default)
* - 1: painted
*
* @see File_PDF::setFont
* @see File_PDF::setDrawColor
* @see File_PDF::setFillColor
* @see File_PDF::setLineWidth
* @see File_PDF::cell
* @see File_PDF::write
* @see File_PDF::setAutoPageBreak
*/
function multiCell($width, $height, $text, $border = 0, $align = 'J',
$fill = 0)
{
$cw = &$this->_current_font['cw'];
if ($width == 0) {
$width = $this->w - $this->_right_margin - $this->x;
}
$wmax = ($width-2 * $this->_cell_margin) * 1000 / $this->_font_size;
$s = str_replace("\r", '', $text);
$nb = strlen($s);
if ($nb > 0 && $s[$nb-1] == "\n") {
$nb--;
}
$b = 0;
if ($border) {
if ($border == 1) {
$border = 'LTRB';
$b = 'LRT';
$b2 = 'LR';
} else {
$b2 = '';
if (strpos($border, 'L') !== false) {
$b2 .= 'L';
}
if (strpos($border, 'R') !== false) {
$b2 .= 'R';
}
$b = (strpos($border, 'T') !== false) ? $b2 . 'T' : $b2;
}
}
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$ns = 0;
$nl = 1;
while ($i < $nb) {
/* Get next character. */
$c = $s[$i];
if ($c == "\n") {
/* Explicit line break. */
if ($this->_word_spacing > 0) {
$this->_word_spacing = 0;
$this->_out('0 Tw');
}
$this->cell($width, $height, substr($s, $j, $i-$j), $b, 2, $align, $fill);
$i++;
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
$nl++;
if ($border && $nl == 2) {
$b = $b2;
}
continue;
}
if ($c == ' ') {
$sep = $i;
$ls = $l;
$ns++;
}
$l += $cw[$c];
if ($l > $wmax) {
/* Automatic line break. */
if ($sep == -1) {
if ($i == $j) {
$i++;
}
if ($this->_word_spacing > 0) {
$this->_word_spacing = 0;
$this->_out('0 Tw');
}
$this->cell($width, $height, substr($s, $j, $i - $j), $b, 2, $align, $fill);
} else {
if ($align == 'J') {
$this->_word_spacing = ($ns>1) ? ($wmax - $ls)/1000 * $this->_font_size / ($ns - 1) : 0;
$this->_out(sprintf('%.3f Tw', $this->_word_spacing * $this->_scale));
}
$this->cell($width, $height, substr($s, $j, $sep - $j), $b, 2, $align, $fill);
$i = $sep + 1;
}
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
$nl++;
if ($border && $nl == 2) {
$b = $b2;
}
} else {
$i++;
}
}
/* Last chunk. */
if ($this->_word_spacing > 0) {
$this->_word_spacing = 0;
$this->_out('0 Tw');
}
if ($border && strpos($border, 'B') !== false) {
$b .= 'B';
}
$this->cell($width, $height, substr($s, $j, $i), $b, 2, $align, $fill);
$this->x = $this->_left_margin;
}
 
/**
* This method prints text from the current position. When the right
* margin is reached (or the \n character is met) a line break occurs and
* text continues from the left margin. Upon method exit, the current
* position is left just at the end of the text.
* It is possible to put a link on the text.
*
* Example:
* //Begin with regular font
* $pdf->setFont('Arial','',14);
* $pdf->write(5,'Visit ');
* //Then put a blue underlined link
* $pdf->setTextColor(0,0,255);
* $pdf->setFont('','U');
* $pdf->write(5,'www.fpdf.org','http://www.fpdf.org');
*
* @param float $height Line height.
* @param string $text String to print.
* @param mixed $link URL or identifier returned by AddLink().
*
* @see File_PDF::setFont
* @see File_PDF::addLink
* @see File_PDF::multiCell
* @see File_PDF::setAutoPageBreak
*/
function write($height, $text, $link = '')
{
$cw = &$this->_current_font['cw'];
$width = $this->w - $this->_right_margin - $this->x;
$wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
$s = str_replace("\r", '', $text);
$nb = strlen($s);
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$nl = 1;
while ($i < $nb) {
/* Get next character. */
$c = $s{$i};
if ($c == "\n") {
/* Explicit line break. */
$this->cell($width, $height, substr($s, $j, $i - $j), 0, 2, '', 0, $link);
$i++;
$sep = -1;
$j = $i;
$l = 0;
if ($nl == 1) {
$this->x = $this->_left_margin;
$width = $this->w - $this->_right_margin - $this->x;
$wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
}
$nl++;
continue;
}
if ($c == ' ') {
$sep = $i;
$ls = $l;
}
$l += (isset($cw[$c]) ? $cw[$c] : 0);
if ($l > $wmax) {
/* Automatic line break. */
if ($sep == -1) {
if ($this->x > $this->_left_margin) {
/* Move to next line. */
$this->x = $this->_left_margin;
$this->y += $height;
$width = $this->w - $this->_right_margin - $this->x;
$wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
$i++;
$nl++;
continue;
}
if ($i == $j) {
$i++;
}
$this->cell($width, $height, substr($s, $j, $i - $j), 0, 2, '', 0, $link);
} else {
$this->cell($width, $height, substr($s, $j, $sep - $j), 0, 2, '', 0, $link);
$i = $sep + 1;
}
$sep = -1;
$j = $i;
$l = 0;
if ($nl == 1) {
$this->x = $this->_left_margin;
$width = $this->w - $this->_right_margin - $this->x;
$wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
}
$nl++;
} else {
$i++;
}
}
/* Last chunk. */
if ($i != $j) {
$this->cell($l / 1000 * $this->_font_size, $height, substr($s, $j, $i), 0, 0, '', 0, $link);
}
}
 
/**
* Writes text at an angle.
*
* All coordinates can be negative to provide values from the right or
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
*
* @param integer $x X coordinate.
* @param integer $y Y coordinate.
* @param string $text Text to write.
* @param float $text_angle Angle to rotate (Eg. 90 = bottom to top).
* @param float $font_angle Rotate characters as well as text.
*
* @see File_PDF::setFont
*/
function writeRotated($x, $y, $text, $text_angle, $font_angle = 0)
{
if ($x < 0) {
$x += $this->w;
}
if ($y < 0) {
$y += $this->h;
}
 
/* Escape text. */
$text = $this->_escape($text);
 
$font_angle += 90 + $text_angle;
$text_angle *= M_PI / 180;
$font_angle *= M_PI / 180;
 
$text_dx = cos($text_angle);
$text_dy = sin($text_angle);
$font_dx = cos($font_angle);
$font_dy = sin($font_angle);
 
$s= sprintf('BT %.2f %.2f %.2f %.2f %.2f %.2f Tm (%s) Tj ET',
$text_dx, $text_dy, $font_dx, $font_dy,
$x * $this->_scale, ($this->h-$y) * $this->_scale, $text);
 
if ($this->_draw_color) {
$s = 'q ' . $this->_draw_color . ' ' . $s . ' Q';
}
$this->_out($s);
}
 
/**
* Prints an image in the page. The upper-left corner and at least one of
* the dimensions must be specified; the height or the width can be
* calculated automatically in order to keep the image proportions.
* Supported formats are JPEG and PNG.
*
* All coordinates can be negative to provide values from the right or
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
*
* For JPEG, all flavors are allowed:
* - gray scales
* - true colors (24 bits)
* - CMYK (32 bits)
*
* For PNG, are allowed:
* - gray scales on at most 8 bits (256 levels)
* - indexed colors
* - true colors (24 bits)
* but are not supported:
* - Interlacing
* - Alpha channel
*
* If a transparent color is defined, it will be taken into account (but
* will be only interpreted by Acrobat 4 and above).
* The format can be specified explicitly or inferred from the file
* extension.
* It is possible to put a link on the image.
*
* Remark: if an image is used several times, only one copy will be
* embedded in the file.
*
* @param string $file Name of the file containing the image.
* @param float $x Abscissa of the upper-left corner.
* @param float $y Ordinate of the upper-left corner.
* @param float $width Width of the image in the page. If equal to zero,
* it is automatically calculated to keep the
* original proportions.
* @param float $height Height of the image in the page. If not specified
* or equal to zero, it is automatically calculated
* to keep the original proportions.
* @param string $type Image format. Possible values are (case
* insensitive) : JPG, JPEG, PNG. If not specified,
* the type is inferred from the file extension.
* @param mixed $link URL or identifier returned by File_PDF::addLink.
*
* @see File_PDF::addLink
*/
function image($file, $x, $y, $width = 0, $height = 0, $type = '',
$link = '')
{
if ($x < 0) {
$x += $this->w;
}
if ($y < 0) {
$y += $this->h;
}
 
if (!isset($this->_images[$file])) {
/* First use of image, get some file info. */
if ($type == '') {
$pos = strrpos($file, '.');
if ($pos === false) {
return $this->raiseError(sprintf('Image file has no extension and no type was specified: %s', $file));
}
$type = substr($file, $pos + 1);
}
$type = strtolower($type);
$mqr = get_magic_quotes_runtime();
set_magic_quotes_runtime(0);
if ($type == 'jpg' || $type == 'jpeg') {
$info = $this->_parseJPG($file);
} elseif ($type == 'png') {
$info = $this->_parsePNG($file);
} else {
return $this->raiseError(sprintf('Unsupported image file type: %s', $type));
}
if (is_a($info, 'PEAR_Error')) {
return $info;
}
set_magic_quotes_runtime($mqr);
$info['i'] = count($this->_images) + 1;
$this->_images[$file] = $info;
} else {
$info = $this->_images[$file];
}
 
/* Make sure all vars are converted to pt scale. */
$x = $this->_toPt($x);
$y = $this->_toPt($y);
$width = $this->_toPt($width);
$height = $this->_toPt($height);
 
/* If not specified do automatic width and height calculations. */
if (empty($width) && empty($height)) {
$width = $info['w'];
$height = $info['h'];
} elseif (empty($width)) {
$width = $height * $info['w'] / $info['h'];
} elseif (empty($height)) {
$height = $width * $info['h'] / $info['w'];
}
 
$this->_out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q', $width, $height, $x, $this->hPt - ($y + $height), $info['i']));
 
/* Set any link if requested. */
if ($link) {
$this->_link($x, $y, $width, $height, $link);
}
}
 
/**
* Performs a line break. The current abscissa goes back to the left
* margin and the ordinate increases by the amount passed in parameter.
*
* @param float $height The height of the break. By default, the value
* equals the height of the last printed cell.
*
* @see File_PDF::cell
*/
function newLine($height = '')
{
$this->x = $this->_left_margin;
if (is_string($height)) {
$this->y += $this->_last_height;
} else {
$this->y += $height;
}
}
 
/**
* Returns the abscissa of the current position in user units.
*
* @return float
*
* @see File_PDF::setX
* @see File_PDF::getY
* @see File_PDF::setY
*/
function getX()
{
return $this->x;
}
 
/**
* Defines the abscissa of the current position. If the passed value is
* negative, it is relative to the right of the page.
*
* @param float $x The value of the abscissa.
*
* @see File_PDF::getX
* @see File_PDF::getY
* @see File_PDF::setY
* @see File_PDF::setXY
*/
function setX($x)
{
if ($x >= 0) {
/* Absolute value. */
$this->x = $x;
} else {
/* Negative, so relative to right edge of the page. */
$this->x = $this->w + $x;
}
}
 
/**
* Returns the ordinate of the current position in user units.
*
* @return float
*
* @see File_PDF::setY
* @see File_PDF::getX
* @see File_PDF::setX
*/
function getY()
{
return $this->y;
}
 
/**
* Defines the ordinate of the current position. If the passed value is
* negative, it is relative to the bottom of the page.
*
* @param float $y The value of the ordinate.
*
* @see File_PDF::getX
* @see File_PDF::getY
* @see File_PDF::setY
* @see File_PDF::setXY
*/
function setY($y)
{
if ($y >= 0) {
/* Absolute value. */
$this->y = $y;
} else {
/* Negative, so relative to bottom edge of the page. */
$this->y = $this->h + $y;
}
}
 
/**
* Defines the abscissa and ordinate of the current position. If the
* passed values are negative, they are relative respectively to the right
* and bottom of the page.
*
* @param float $x The value of the abscissa.
* @param float $y The value of the ordinate.
*
* @see File_PDF::setX
* @see File_PDF::setY
*/
function setXY($x, $y)
{
$this->setY($y);
$this->setX($x);
}
 
/**
* Returns the raw PDF file.
*
* @see File_PDF::output
*/
function getOutput()
{
/* Check whether file has been closed. */
if ($this->_state < 3) {
$this->close();
}
 
return $this->_buffer;
}
 
/**
* Function to output the buffered data to the browser.
*
* @param string $filename The filename for the output file.
* @param boolean $inline True if inline, false if attachment.
*/
function output($filename = 'unknown.pdf', $inline = false)
{
/* Check whether file has been closed. */
if ($this->_state < 3) {
$this->close();
}
 
/* Check if headers have been sent. */
if (headers_sent()) {
return $this->raiseError('Unable to send PDF file, some data has already been output to browser.');
}
 
/* If HTTP_Download is not available return a PEAR_Error. */
if (!include_once 'HTTP/Download.php') {
return $this->raiseError('Missing PEAR package HTTP_Download.');
}
 
/* Params for the output. */
$disposition = !$inline ? HTTP_DOWNLOAD_ATTACHMENT : HTTP_DOWNLOAD_INLINE;
$params = array('data' => $this->_buffer,
'contenttype' => 'application/pdf',
'contentdisposition' => array($disposition, $filename));
/* Output the file. */
return HTTP_Download::staticSend($params);
}
 
/**
* Function to save the PDF file somewhere local to the server.
*
* @param string $filename The filename for the output file.
*/
function save($filename = 'unknown.pdf')
{
/* Check whether file has been closed. */
if ($this->_state < 3) {
$this->close();
}
 
$f = fopen($filename, 'wb');
if (!$f) {
return $this->raiseError(sprintf('Unable to save PDF file: %s', $filename));
}
fwrite($f, $this->_buffer, strlen($this->_buffer));
fclose($f);
}
 
function _toPt($val)
{
return $val * $this->_scale;
}
 
function &_getFontFile($fontkey, $path = '')
{
static $font_widths;
 
if (!isset($font_widths[$fontkey])) {
if (!empty($path)) {
$file = $path . strtolower($fontkey) . '.php';
} else {
$file = 'File/PDF/fonts/' . strtolower($fontkey) . '.php';
}
include $file;
if (!isset($font_widths[$fontkey])) {
return $this->raiseError(sprintf('Could not include font metric file: %s', $file));
}
}
 
return $font_widths;
}
 
function _link($x, $y, $width, $height, $link)
{
/* Save link to page links array. */
$this->_page_links[$this->_page][] = array($x, $y, $width, $height, $link);
}
 
function _beginDoc()
{
/* Start document, but only if not yet started. */
if ($this->_state < 1) {
$this->_state = 1;
$this->_out('%PDF-1.3');
}
}
 
function _putPages()
{
$nb = $this->_page;
if (!empty($this->_alias_nb_pages)) {
/* Replace number of pages. */
for ($n = 1; $n <= $nb; $n++) {
$this->_pages[$n] = str_replace($this->_alias_nb_pages, $nb, $this->_pages[$n]);
}
}
if ($this->_default_orientation == 'P') {
$wPt = $this->fwPt;
$hPt = $this->fhPt;
} else {
$wPt = $this->fhPt;
$hPt = $this->fwPt;
}
$filter = ($this->_compress) ? '/Filter /FlateDecode ' : '';
for ($n = 1; $n <= $nb; $n++) {
/* Page */
$this->_newobj();
$this->_out('<</Type /Page');
$this->_out('/Parent 1 0 R');
if (isset($this->_orientation_changes[$n])) {
$this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]', $hPt, $wPt));
}
$this->_out('/Resources 2 0 R');
if (isset($this->_page_links[$n])) {
/* Links */
$annots = '/Annots [';
foreach ($this->_page_links[$n] as $pl) {
$rect = sprintf('%.2f %.2f %.2f %.2f', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]);
$annots .= '<</Type /Annot /Subtype /Link /Rect [' . $rect . '] /Border [0 0 0] ';
if (is_string($pl[4])) {
$annots .= '/A <</S /URI /URI ' . $this->_textString($pl[4]) . '>>>>';
} else {
$l = $this->_links[$pl[4]];
$height = isset($this->_orientation_changes[$l[0]]) ? $wPt : $hPt;
$annots .= sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]>>', 1 + 2 * $l[0], $height - $l[1] * $this->_scale);
}
}
$this->_out($annots.']');
}
$this->_out('/Contents ' . ($this->_n + 1) . ' 0 R>>');
$this->_out('endobj');
/* Page content */
$p = ($this->_compress) ? gzcompress($this->_pages[$n]) : $this->_pages[$n];
$this->_newobj();
$this->_out('<<' . $filter . '/Length ' . strlen($p) . '>>');
$this->_putStream($p);
$this->_out('endobj');
}
/* Pages root */
$this->_offsets[1] = strlen($this->_buffer);
$this->_out('1 0 obj');
$this->_out('<</Type /Pages');
$kids = '/Kids [';
for ($i = 0; $i < $nb; $i++) {
$kids .= (3 + 2 * $i) . ' 0 R ';
}
$this->_out($kids . ']');
$this->_out('/Count ' . $nb);
$this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]', $wPt, $hPt));
$this->_out('>>');
$this->_out('endobj');
}
 
function _putFonts()
{
$nf = $this->_n;
foreach ($this->_diffs as $diff) {
/* Encodings */
$this->_newobj();
$this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [' . $diff . ']>>');
$this->_out('endobj');
}
$mqr = get_magic_quotes_runtime();
set_magic_quotes_runtime(0);
foreach ($this->_font_files as $file => $info) {
/* Font file embedding. */
$this->_newobj();
$this->_font_files[$file]['n'] = $this->_n;
$size = filesize($file);
if (!$size) {
return $this->raiseError('Font file not found.');
}
$this->_out('<</Length ' . $size);
if (substr($file, -2) == '.z') {
$this->_out('/Filter /FlateDecode');
}
$this->_out('/Length1 ' . $info['length1']);
if (isset($info['length2'])) {
$this->_out('/Length2 ' . $info['length2'] . ' /Length3 0');
}
$this->_out('>>');
$f = fopen($file, 'rb');
$this->_putStream(fread($f, $size));
fclose($f);
$this->_out('endobj');
}
set_magic_quotes_runtime($mqr);
foreach ($this->_fonts as $k => $font) {
/* Font objects */
$this->_newobj();
$this->_fonts[$k]['n'] = $this->_n;
$name = $font['name'];
$this->_out('<</Type /Font');
$this->_out('/BaseFont /' . $name);
if ($font['type'] == 'core') {
/* Standard font. */
$this->_out('/Subtype /Type1');
if ($name != 'Symbol' && $name != 'ZapfDingbats') {
$this->_out('/Encoding /WinAnsiEncoding');
}
} else {
/* Additional font. */
$this->_out('/Subtype /' . $font['type']);
$this->_out('/FirstChar 32');
$this->_out('/LastChar 255');
$this->_out('/Widths ' . ($this->_n + 1) . ' 0 R');
$this->_out('/FontDescriptor ' . ($this->_n + 2) . ' 0 R');
if ($font['enc']) {
if (isset($font['diff'])) {
$this->_out('/Encoding ' . ($nf + $font['diff']).' 0 R');
} else {
$this->_out('/Encoding /WinAnsiEncoding');
}
}
}
$this->_out('>>');
$this->_out('endobj');
if ($font['type'] != 'core') {
/* Widths. */
$this->_newobj();
$cw = &$font['cw'];
$s = '[';
for ($i = 32; $i <= 255; $i++) {
$s .= $cw[chr($i)] . ' ';
}
$this->_out($s . ']');
$this->_out('endobj');
/* Descriptor. */
$this->_newobj();
$s = '<</Type /FontDescriptor /FontName /' . $name;
foreach ($font['desc'] as $k => $v) {
$s .= ' /' . $k . ' ' . $v;
}
$file = $font['file'];
if ($file) {
$s .= ' /FontFile' . ($font['type'] == 'Type1' ? '' : '2') . ' ' . $this->_font_files[$file]['n'] . ' 0 R';
}
$this->_out($s . '>>');
$this->_out('endobj');
}
}
}
 
function _putImages()
{
$filter = ($this->_compress) ? '/Filter /FlateDecode ' : '';
foreach ($this->_images as $file => $info) {
$this->_newobj();
$this->_images[$file]['n'] = $this->_n;
$this->_out('<</Type /XObject');
$this->_out('/Subtype /Image');
$this->_out('/Width ' . $info['w']);
$this->_out('/Height ' . $info['h']);
if ($info['cs'] == 'Indexed') {
$this->_out('/ColorSpace [/Indexed /DeviceRGB ' . (strlen($info['pal'])/3 - 1) . ' ' . ($this->_n + 1).' 0 R]');
} else {
$this->_out('/ColorSpace /' . $info['cs']);
if ($info['cs'] == 'DeviceCMYK') {
$this->_out('/Decode [1 0 1 0 1 0 1 0]');
}
}
$this->_out('/BitsPerComponent ' . $info['bpc']);
$this->_out('/Filter /' . $info['f']);
if (isset($info['parms'])) {
$this->_out($info['parms']);
}
if (isset($info['trns']) && is_array($info['trns'])) {
$trns = '';
$i_max = count($info['trns']);
for ($i = 0; $i < $i_max; $i++) {
$trns .= $info['trns'][$i] . ' ' . $info['trns'][$i].' ';
}
$this->_out('/Mask [' . $trns . ']');
}
$this->_out('/Length ' . strlen($info['data']) . '>>');
$this->_putStream($info['data']);
$this->_out('endobj');
 
/* Palette. */
if ($info['cs'] == 'Indexed') {
$this->_newobj();
$pal = ($this->_compress) ? gzcompress($info['pal']) : $info['pal'];
$this->_out('<<' . $filter . '/Length ' . strlen($pal) . '>>');
$this->_putStream($pal);
$this->_out('endobj');
}
}
}
 
function _putResources()
{
$this->_putFonts();
$this->_putImages();
/* Resource dictionary */
$this->_offsets[2] = strlen($this->_buffer);
$this->_out('2 0 obj');
$this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
$this->_out('/Font <<');
foreach ($this->_fonts as $font) {
$this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
}
$this->_out('>>');
if (count($this->_images)) {
$this->_out('/XObject <<');
foreach ($this->_images as $image) {
$this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
}
$this->_out('>>');
}
$this->_out('>>');
$this->_out('endobj');
}
 
function _putInfo()
{
$this->_out('/Producer ' . $this->_textString('Horde PDF'));
if (!empty($this->_info['title'])) {
$this->_out('/Title ' . $this->_textString($this->_info['title']));
}
if (!empty($this->_info['subject'])) {
$this->_out('/Subject ' . $this->_textString($this->_info['subject']));
}
if (!empty($this->_info['author'])) {
$this->_out('/Author ' . $this->_textString($this->_info['author']));
}
if (!empty($this->keywords)) {
$this->_out('/Keywords ' . $this->_textString($this->keywords));
}
if (!empty($this->creator)) {
$this->_out('/Creator ' . $this->_textString($this->creator));
}
$this->_out('/CreationDate ' . $this->_textString('D:' . date('YmdHis')));
}
 
function _putCatalog()
{
$this->_out('/Type /Catalog');
$this->_out('/Pages 1 0 R');
if ($this->_zoom_mode == 'fullpage') {
$this->_out('/OpenAction [3 0 R /Fit]');
} elseif ($this->_zoom_mode == 'fullwidth') {
$this->_out('/OpenAction [3 0 R /FitH null]');
} elseif ($this->_zoom_mode == 'real') {
$this->_out('/OpenAction [3 0 R /XYZ null null 1]');
} elseif (!is_string($this->_zoom_mode)) {
$this->_out('/OpenAction [3 0 R /XYZ null null ' . ($this->_zoom_mode / 100).']');
}
if ($this->_layout_mode == 'single') {
$this->_out('/PageLayout /SinglePage');
} elseif ($this->_layout_mode == 'continuous') {
$this->_out('/PageLayout /OneColumn');
} elseif ($this->_layout_mode == 'two') {
$this->_out('/PageLayout /TwoColumnLeft');
}
}
 
function _putTrailer()
{
$this->_out('/Size ' . ($this->_n + 1));
$this->_out('/Root ' . $this->_n . ' 0 R');
$this->_out('/Info ' . ($this->_n - 1) . ' 0 R');
}
 
function _endDoc()
{
$this->_putPages();
$this->_putResources();
/* Info */
$this->_newobj();
$this->_out('<<');
$this->_putInfo();
$this->_out('>>');
$this->_out('endobj');
/* Catalog */
$this->_newobj();
$this->_out('<<');
$this->_putCatalog();
$this->_out('>>');
$this->_out('endobj');
/* Cross-ref */
$o = strlen($this->_buffer);
$this->_out('xref');
$this->_out('0 ' . ($this->_n + 1));
$this->_out('0000000000 65535 f ');
for ($i = 1; $i <= $this->_n; $i++) {
$this->_out(sprintf('%010d 00000 n ', $this->_offsets[$i]));
}
/* Trailer */
$this->_out('trailer');
$this->_out('<<');
$this->_putTrailer();
$this->_out('>>');
$this->_out('startxref');
$this->_out($o);
$this->_out('%%EOF');
$this->_state = 3;
}
 
function _beginPage($orientation)
{
$this->_page++;
$this->_pages[$this->_page] = '';
$this->_state = 2;
$this->x = $this->_left_margin;
$this->y = $this->_top_margin;
$this->_last_height = 0;
/* Page orientation */
if (!$orientation) {
$orientation = $this->_default_orientation;
} else {
$orientation = strtoupper($orientation[0]);
if ($orientation != $this->_default_orientation) {
$this->_orientation_changes[$this->_page] = true;
}
}
if ($orientation != $this->_current_orientation) {
/* Change orientation */
if ($orientation == 'P') {
$this->wPt = $this->fwPt;
$this->hPt = $this->fhPt;
$this->w = $this->fw;
$this->h = $this->fh;
} else {
$this->wPt = $this->fhPt;
$this->hPt = $this->fwPt;
$this->w = $this->fh;
$this->h = $this->fw;
}
$this->_page_break_trigger = $this->h - $this->_break_margin;
$this->_current_orientation = $orientation;
}
}
 
function _endPage()
{
/* End of page contents */
$this->_state = 1;
}
 
function _newobj()
{
/* Begin a new object */
$this->_n++;
$this->_offsets[$this->_n] = strlen($this->_buffer);
$this->_out($this->_n . ' 0 obj');
}
 
function _doUnderline($x, $y, $text)
{
/* Set the rectangle width according to text width. */
$width = $this->getStringWidth($text, true);
 
/* Set rectangle position and height, using underline position and
* thickness settings scaled by the font size. */
$y = $y + ($this->_current_font['up'] * $this->_font_size_pt / 1000);
$height = -$this->_current_font['ut'] * $this->_font_size_pt / 1000;
 
return sprintf('%.2f %.2f %.2f %.2f re f', $x, $y, $width, $height);
}
 
function _parseJPG($file)
{
/* Extract info from a JPEG file. */
$img = @getimagesize($file);
if (!$img) {
return $this->raiseError(sprintf('Missing or incorrect image file: %s', $file));
}
if ($img[2] != 2) {
return $this->raiseError(sprintf('Not a JPEG file: %s', $file));
}
if (!isset($img['channels']) || $img['channels'] == 3) {
$colspace = 'DeviceRGB';
} elseif ($img['channels'] == 4) {
$colspace = 'DeviceCMYK';
} else {
$colspace = 'DeviceGray';
}
$bpc = isset($img['bits']) ? $img['bits'] : 8;
 
/* Read whole file. */
$f = fopen($file, 'rb');
$data = fread($f, filesize($file));
fclose($f);
 
return array('w' => $img[0], 'h' => $img[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
}
 
function _parsePNG($file)
{
/* Extract info from a PNG file. */
$f = fopen($file, 'rb');
if (!$f) {
return $this->raiseError(sprintf('Unable to open image file: %s', $file));
}
 
/* Check signature. */
if (fread($f, 8) != chr(137) . 'PNG' . chr(13) . chr(10) . chr(26) . chr(10)) {
return $this->raiseError(sprintf('Not a PNG file: %s', $file));
}
 
/* Read header chunk. */
fread($f, 4);
if (fread($f, 4) != 'IHDR') {
return $this->raiseError(sprintf('Incorrect PNG file: %s', $file));
}
$width = $this->_freadInt($f);
$height = $this->_freadInt($f);
$bpc = ord(fread($f, 1));
if ($bpc > 8) {
return $this->raiseError(sprintf('16-bit depth not supported: %s', $file));
}
$ct = ord(fread($f, 1));
if ($ct == 0) {
$colspace = 'DeviceGray';
} elseif ($ct == 2) {
$colspace = 'DeviceRGB';
} elseif ($ct == 3) {
$colspace = 'Indexed';
} else {
return $this->raiseError(sprintf('Alpha channel not supported: %s', $file));
}
if (ord(fread($f, 1)) != 0) {
return $this->raiseError(sprintf('Unknown compression method: %s', $file));
}
if (ord(fread($f, 1)) != 0) {
return $this->raiseError(sprintf('Unknown filter method: %s', $file));
}
if (ord(fread($f, 1)) != 0) {
return $this->raiseError(sprintf('Interlacing not supported: %s', $file));
}
fread($f, 4);
$parms = '/DecodeParms <</Predictor 15 /Colors ' . ($ct == 2 ? 3 : 1).' /BitsPerComponent ' . $bpc . ' /Columns ' . $width.'>>';
/* Scan chunks looking for palette, transparency and image data. */
$pal = '';
$trns = '';
$data = '';
do {
$n = $this->_freadInt($f);
$type = fread($f, 4);
if ($type == 'PLTE') {
/* Read palette */
$pal = fread($f, $n);
fread($f, 4);
} elseif ($type == 'tRNS') {
/* Read transparency info */
$t = fread($f, $n);
if ($ct == 0) {
$trns = array(ord(substr($t, 1, 1)));
} elseif ($ct == 2) {
$trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
} else {
$pos = strpos($t, chr(0));
if (is_int($pos)) {
$trns = array($pos);
}
}
fread($f, 4);
} elseif ($type == 'IDAT') {
/* Read image data block */
$data .= fread($f, $n);
fread($f, 4);
} elseif ($type == 'IEND') {
break;
} else {
fread($f, $n + 4);
}
} while ($n);
 
if ($colspace == 'Indexed' && empty($pal)) {
return $this->raiseError(sprintf('Missing palette in: %s', $file));
}
fclose($f);
 
return array('w' => $width, 'h' => $height, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
}
 
function _freadInt($f)
{
/* Read a 4-byte integer from file. */
$i = ord(fread($f, 1)) << 24;
$i += ord(fread($f, 1)) << 16;
$i += ord(fread($f, 1)) << 8;
$i += ord(fread($f, 1));
return $i;
}
 
function _textString($s)
{
/* Format a text string */
return '(' . $this->_escape($s) . ')';
}
 
function _escape($s)
{
/* Add \ before \, ( and ) */
return str_replace(array(')','(','\\'),
array('\\)','\\(','\\\\'),
$s);
}
 
function _putStream($s)
{
$this->_out('stream');
$this->_out($s);
$this->_out('endstream');
}
 
function _out($s)
{
/* Add a line to the document. */
if ($this->_state == 2) {
$this->_pages[$this->_page] .= $s . "\n";
} else {
$this->_buffer .= $s . "\n";
}
}
 
}
/trunk/jrest/lib/SpreadsheetProductor.php
New file
0,0 → 1,15
<?php
 
 
Class SpreadsheetProductor {
function initSpreadsheet() {
require_once("Spreadsheet/Excel/Writer.php");
}
}
 
 
?>
/trunk/jrest/lib/PDFProductor.php
New file
0,0 → 1,14
<?php
 
 
Class PDFProductor {
function initPDF() {
require_once("PDF.php");
}
}
 
 
?>
/trunk/jrest/lib/WdHTMLParser.php
New file
0,0 → 1,144
<?php
/**
*
* @author Olivier Laviale
* @see http://www.weirdog.com/blog/php/un-parser-html-des-plus-leger.html
*
*/
class WdHTMLParser {
private $encoding;
private $matches;
private $escaped;
private $opened = array();
public $malformed;
 
public function parse($html, $namespace=NULL, $encoding='utf-8') {
$this->malformed = false;
$this->encoding = $encoding;
// we take care of escaping comments and processing options. they will not be parsed
// and will end as text nodes
$html = $this->escapeSpecials($html);
// in order to create a tree, we first need to split the HTML using the markups,
// creating a nice flat array of texts and opening and closing markups.
//
// the array can be read as follows :
//
// i+0 => some text
// i+1 => '/' for closing markups, nothing otherwise
// i+2 => the markup it self, without the '<' '>'
//
// note that i+2 might end with a '/' indicating an auto-closing markup
$this->matches = preg_split('#<(/?)' . $namespace . '([^>]*)>#', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
// the flat representation is now ready, we can create our tree
$tree = $this->buildTree();
 
// if comments or processing options where escaped, we can
// safely unescape them now
if ($this->escaped) {
$tree = $this->unescapeSpecials($tree);
}
return $tree;
}
private function escapeSpecials($html) {
// here we escape comments
$html = preg_replace_callback('#<\!--.+-->#sU', array($this, 'escapeSpecials_callback'), $html);
 
// and processing options
$html = preg_replace_callback('#<\?.+\?>#sU', array($this, 'escapeSpecials_callback'), $html);
return $html;
}
private function escapeSpecials_callback($m) {
$this->escaped = true;
$text = $m[0];
$text = str_replace(array('<', '>'), array("\x01", "\x02"), $text);
return $text;
}
 
private function unescapeSpecials($tree) {
return is_array($tree) ? array_map(array($this, 'unescapeSpecials'), $tree) : str_replace(array("\x01", "\x02"), array('<', '>'), $tree);
}
 
private function buildTree() {
$nodes = array();
$i = 0;
$text = NULL;
while (($value = array_shift($this->matches)) !== NULL) {
switch ($i++ % 3) {
case 0:
// if the trimed value is not empty we preserve the value,
// otherwise we discard it.
if (trim($value)){
$nodes[] = $value;
}
break;
case 1:
$closing = ($value == '/');
break;
case 2:
if (substr($value, -1, 1) == '/') {
// auto closing
$nodes[] = $this->parseMarkup(substr($value, 0, -1));
} else if ($closing) {
// closing markup
$open = array_pop($this->opened);
if ($value != $open) {
$this->error($value, $open);
}
 
return $nodes;
} else {
// this is an open markup with possible children
$node = $this->parseMarkup($value);
// push the markup name into the opened markups
$this->opened[] = $node['name'];
// create the node and parse its children
$node['children'] = $this->buildTree($this->matches);
$nodes[] = $node;
}
break;
}
}
return $nodes;
}
public function parseMarkup($markup) {
// get markup's name
preg_match('#^[^\s]+#', $markup, $matches);
$name = $matches[0];
 
// get markup's arguments
preg_match_all('#\s+([^=]+)\s*=\s*"([^"]+)"#', $markup, $matches, PREG_SET_ORDER);
// transform the matches into a nice key/value array
$args = array();
foreach ($matches as $m) {
// we unescape the html entities of the argument's value
$args[$m[1]] = html_entity_decode($m[2], ENT_QUOTES, $this->encoding);
}
 
return array('name' => $name, 'args' => $args);
}
public function error($markup, $expected) {
$this->malformed = true;
printf('unexpected closing markup "%s", should be "%s"', $markup, $expected);
}
}
 
?>
/trunk/jrest/lib/Writer.php
New file
0,0 → 1,104
<?php
/*
* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
*
* 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_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 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 'PEAR.php';
require_once 'Spreadsheet/Excel/Writer/Workbook.php';
 
/**
* Class for writing Excel Spreadsheets. This class should change COMPLETELY.
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer extends Spreadsheet_Excel_Writer_Workbook
{
/**
* The constructor. It just creates a Workbook
*
* @param string $filename The optional filename for the Workbook.
* @return Spreadsheet_Excel_Writer_Workbook The Workbook created
*/
function Spreadsheet_Excel_Writer($filename = '')
{
$this->_filename = $filename;
$this->Spreadsheet_Excel_Writer_Workbook($filename);
}
 
/**
* Send HTTP headers for the Excel file.
*
* @param string $filename The filename to use for HTTP headers
* @access public
*/
function send($filename)
{
header("Content-type: application/vnd.ms-excel");
header("Content-Disposition: attachment; filename=\"$filename\"");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0,pre-check=0");
header("Pragma: public");
}
 
/**
* Utility function for writing formulas
* Converts a cell's coordinates to the A1 format.
*
* @access public
* @static
* @param integer $row Row for the cell to convert (0-indexed).
* @param integer $col Column for the cell to convert (0-indexed).
* @return string The cell identifier in A1 format
*/
function rowcolToCell($row, $col)
{
if ($col > 255) { //maximum column value exceeded
return new PEAR_Error("Maximum column value exceeded: $col");
}
 
$int = (int)($col / 26);
$frac = $col % 26;
$chr1 = '';
 
if ($int > 0) {
$chr1 = chr(ord('A') + $int - 1);
}
 
$chr2 = chr(ord('A') + $frac);
$row++;
 
return $chr1 . $chr2 . $row;
}
}
?>
/trunk/jrest/lib/Log.php
New file
0,0 → 1,195
<?php
//declare(encoding='UTF-8');
/**
* Classe permettant de logger des messages dans les fichier situés dans le dossier de log
*
* PHP Version 5
*
* @category PHP
* @package Framework
* @author aurelien <aurelien@tela-botanica.org>
* @copyright 2009 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @version SVN: <svn_id>
* @link /doc/framework/
*/
 
class Log {
 
/**
* Tableau associatif stockant les descripteurs de fichiers
*/
private static $fichiersLog = array();
 
/**
* Chemin de base du dossier log de l'application
*/
private static $cheminLogs = '';
 
/**
* Booleen indiquant si l'on peut correctement écrire dans les fichiers de logs
*/
private static $droitLogger = true;
 
/**
* Zone horaire (pour éviter des avertissements dans les dates)
*/
private static $timeZone = '';
 
/**
* Taille maximum d'un fichier de log avant que celui ne soit archivé (en octets)
*/
private static $tailleMax = 10000;
 
/**
* séparateur de chemin
*/
private static $sd = DIRECTORY_SEPARATOR;
 
/**
* Extension des fichiers de log
*/
private static $ext = '.log';
 
/**
* La classe registre se contient elle-même, (pour le pattern singleton)
*/
private static $log;
 
/**
* Constructeur par défaut, privé, car on accède à la classe par le getInstance
*/
private function __construct() {
 
self::$sd = $sd;
// gestion de la timezone pour éviter des erreurs
if(function_exists("date_default_timezone_set") and function_exists("date_default_timezone_get")) {
date_default_timezone_set(self::$timeZone);
}
 
if(!is_dir(self::$cheminLogs) || !is_writable(self::$cheminLogs)) {
self::desactiverEcriture();
}
}
 
public static function setCheminLog($nouveauCheminLogs) {
self::$cheminLogs = $nouveauCheminLogs;
}
 
public static function getCheminLog() {
return self::$cheminLogs;
}
 
public static function setTimeZone($NouvelleTimeZone) {
self::$timeZone = $NouvelleTimeZone;
}
 
public static function setTailleMax($nouvelleTailleMax) {
self::$tailleMax = $nouvelleTailleMax;
}
 
/**
* Fonction qui renvoie l'instance de classe en assurant son unicité, c'est l'unique méthode qui doit être
* utilisée pour récupérer l'objet Registre
* @return Log le gestionnaire de log en cours
*/
public static function getInstance() {
if (self::$log instanceof Log) {
return self::$log;
}
self::$log = new Log();
return self::$log;
}
 
/**
* Ajoute une entrée au log spécifié par le paramètre $nomFichier
* @param string $nomFichier le nom du fichier dans lequel écrire
*/
public static function ajouterEntree($nomFichier,$entree,$mode='a+') {
if(self::$droitLogger) {
$date = "\n"."\n".date('d m Y H:i')."\n" ;
 
// si le fichier est déjà dans le tableau et qu'on peut y écrire
if(self::verifierOuvrirFichier($nomFichier,$mode)) {
// on y écrit le message de log
fwrite(self::$fichiersLog[$nomFichier],$date.$entree);
// on vérifie si le fichier ne dépasse pas la taille maximale
self::verifierTailleFichierOuArchiver($nomFichier);
} else {
// sinon on interdit l'écriture
self::desactiverEcriture($nomFichier);
}
}
}
 
/**
* Vide un fichier log indiqué
* @param string $nomFichier le nom du fichier à vider
*/
public static function viderLog($nomFichier) {
ajouterEntree($nomFichier,'','w');
}
 
/**
* Vérifie la présence d'un fichier dans le tableau, ses droits d'écriture,
* l'ouvre si nécessaire
* @param string $nomFichier le nom du fichier dont on doit vérifier la présence
* @return boolean true si le fichier est ouvert ou maintenant accessible, false sinon
*/
public static function verifierOuvrirFichier($nomFichier,$mode) {
// le fichier est il déjà ouvert ?
if(in_array($nomFichier,self::$fichiersLog)) {
// si oui peut on y écrire ?
if(is_writable(self::$cheminLogs.$nomFichier.self::$ext)) {
// si oui on renvoie le descripteur
return true;
}
return false;
} else {
// sinon on l'ouvre
$fp = @fopen(self::$cheminLogs.$nomFichier.self::$ext,$mode);
// si l'ouverture a réussi et si le fichier a les droits d'écriture
if($fp && is_writable(self::$cheminLogs.$nomFichier.self::$ext)) {
// si oui on renvoie le descripteur qu'on ajoute au tableau
self::$fichiersLog[$nomFichier] = $fp;
return true;
}
return false;
}
}
 
/**
* Vérifie la taille d'un fichier donné et si celle ci est trop importante
* archive le fichier de log
* @param string $nomFichier nom du fichier à vérifier
*/
private static function verifierTailleFichierOuArchiver($nomFichier) {
if(filesize(self::$cheminLogs.$nomFichier.self::$ext) > self::$tailleMax) {
rename(self::$cheminLogs.$nomFichier.self::$ext,self::$cheminLogs.$nomFichier.date('d_m_Y_H:i').self::$ext);
self::ajouterEntree($nomFichier,'');
}
}
 
/**
* Désactive l'écriture du log et envoie un message au gestionnaire d'erreurs
* @param string $nomFichier le nom du fichier qui a causé l'erreur
*/
private static function desactiverEcriture($nomFichier = '') {
self::$droitLogger = false;
if($nomFichier != '') {
$fichierDossier = 'fichier '.$nomFichier ;
} else {
$fichierDossier = 'dossier des logs';
}
}
 
/**
* destructeur de classe, ferme les descripteurs ouverts
*/
public function __destruct() {
foreach(self::$fichiersLog as $nomFichier => $fp) {
fclose($fp);
}
}
}
?>
/trunk/jrest/lib/Spreadsheet/Excel/Writer/BIFFwriter.php
New file
0,0 → 1,235
<?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_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 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('PEAR.php');
 
/**
* Class for writing Excel BIFF records.
*
* From "MICROSOFT EXCEL BINARY FILE FORMAT" by Mark O'Brien (Microsoft Corporation):
*
* BIFF (BInary File Format) is the file format in which Excel documents are
* saved on disk. A BIFF file is a complete description of an Excel document.
* BIFF files consist of sequences of variable-length records. There are many
* different types of BIFF records. For example, one record type describes a
* formula entered into a cell; one describes the size and location of a
* window into a document; another describes a picture format.
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer_BIFFwriter extends PEAR
{
/**
* The BIFF/Excel version (5).
* @var integer
*/
var $_BIFF_version = 0x0500;
 
/**
* The byte order of this architecture. 0 => little endian, 1 => big endian
* @var integer
*/
var $_byte_order;
 
/**
* The string containing the data of the BIFF stream
* @var string
*/
var $_data;
 
/**
* The size of the data in bytes. Should be the same as strlen($this->_data)
* @var integer
*/
var $_datasize;
 
/**
* The maximun length for a BIFF record. See _addContinue()
* @var integer
* @see _addContinue()
*/
var $_limit;
/**
* Constructor
*
* @access public
*/
function Spreadsheet_Excel_Writer_BIFFwriter()
{
$this->_byte_order = '';
$this->_data = '';
$this->_datasize = 0;
$this->_limit = 2080;
// Set the byte order
$this->_setByteOrder();
}
 
/**
* Determine the byte order and store it as class data to avoid
* recalculating it for each call to new().
*
* @access private
*/
function _setByteOrder()
{
if ($this->_byte_order == '')
{
// Check if "pack" gives the required IEEE 64bit float
$teststr = pack("d", 1.2345);
$number = pack("C8", 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F);
if ($number == $teststr) {
$byte_order = 0; // Little Endian
}
elseif ($number == strrev($teststr)){
$byte_order = 1; // Big Endian
}
else {
// Give up. I'll fix this in a later version.
$this->raiseError("Required floating point format not supported ".
"on this platform.");
}
}
$this->_byte_order = $byte_order;
}
 
/**
* General storage function
*
* @param string $data binary data to prepend
* @access private
*/
function _prepend($data)
{
if (strlen($data) > $this->_limit) {
$data = $this->_addContinue($data);
}
$this->_data = $data.$this->_data;
$this->_datasize += strlen($data);
}
 
/**
* General storage function
*
* @param string $data binary data to append
* @access private
*/
function _append($data)
{
if (strlen($data) > $this->_limit) {
$data = $this->_addContinue($data);
}
$this->_data = $this->_data.$data;
$this->_datasize += strlen($data);
}
 
/**
* Writes Excel BOF record to indicate the beginning of a stream or
* sub-stream in the BIFF file.
*
* @param integer $type type of BIFF file to write: 0x0005 Workbook, 0x0010 Worksheet.
* @access private
*/
function _storeBof($type)
{
$record = 0x0809; // Record identifier
$length = 0x0008; // Number of bytes to follow
$version = $this->_BIFF_version;
// According to the SDK $build and $year should be set to zero.
// However, this throws a warning in Excel 5. So, use these
// magic numbers.
$build = 0x096C;
$year = 0x07C9;
$header = pack("vv", $record, $length);
$data = pack("vvvv", $version, $type, $build, $year);
$this->_prepend($header.$data);
}
 
/**
* Writes Excel EOF record to indicate the end of a BIFF stream.
*
* @access private
*/
function _storeEof()
{
$record = 0x000A; // Record identifier
$length = 0x0000; // Number of bytes to follow
$header = pack("vv", $record, $length);
$this->_append($header);
}
 
/**
* Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In
* Excel 97 the limit is 8228 bytes. Records that are longer than these limits
* must be split up into CONTINUE blocks.
*
* This function takes a long BIFF record and inserts CONTINUE records as
* necessary.
*
* @param string $data The original binary data to be written
* @return string A very convenient string of continue blocks
* @access private
*/
function _addContinue($data)
{
$limit = $this->_limit;
$record = 0x003C; // Record identifier
// The first 2080/8224 bytes remain intact. However, we have to change
// the length field of the record.
$tmp = substr($data, 0, 2).pack("v", $limit-4).substr($data, 4, $limit - 4);
$header = pack("vv", $record, $limit); // Headers for continue records
// Retrieve chunks of 2080/8224 bytes +4 for the header.
for($i = $limit; $i < strlen($data) - $limit; $i += $limit)
{
$tmp .= $header;
$tmp .= substr($data, $i, $limit);
}
// Retrieve the last chunk of data
$header = pack("vv", $record, strlen($data) - $i);
$tmp .= $header;
$tmp .= substr($data,$i,strlen($data) - $i);
return($tmp);
}
}
?>
/trunk/jrest/lib/Spreadsheet/Excel/Writer/Format.php
New file
0,0 → 1,940
<?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_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 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('PEAR.php');
 
/**
* Class for generating Excel XF records (formats)
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer_Format extends PEAR
{
/**
* The index given by the workbook when creating a new format.
* @var integer
*/
var $_xf_index;
 
/**
* Index to the FONT record.
* @var integer
*/
var $font_index;
 
/**
* The font name (ASCII).
* @var string
*/
var $_font_name;
 
/**
* Height of font (1/20 of a point)
* @var integer
*/
var $_size;
 
/**
* Bold style
* @var integer
*/
var $_bold;
 
/**
* Bit specifiying if the font is italic.
* @var integer
*/
var $_italic;
 
/**
* Index to the cell's color
* @var integer
*/
var $_color;
 
/**
* The text underline property
* @var integer
*/
var $_underline;
 
/**
* Bit specifiying if the font has strikeout.
* @var integer
*/
var $_font_strikeout;
 
/**
* Bit specifiying if the font has outline.
* @var integer
*/
var $_font_outline;
 
/**
* Bit specifiying if the font has shadow.
* @var integer
*/
var $_font_shadow;
 
/**
* 2 bytes specifiying the script type for the font.
* @var integer
*/
var $_font_script;
 
/**
* Byte specifiying the font family.
* @var integer
*/
var $_font_family;
 
/**
* Byte specifiying the font charset.
* @var integer
*/
var $_font_charset;
 
/**
* An index (2 bytes) to a FORMAT record (number format).
* @var integer
*/
var $_num_format;
 
/**
* Bit specifying if formulas are hidden.
* @var integer
*/
var $_hidden;
 
/**
* Bit specifying if the cell is locked.
* @var integer
*/
var $_locked;
 
/**
* The three bits specifying the text horizontal alignment.
* @var integer
*/
var $_text_h_align;
 
/**
* Bit specifying if the text is wrapped at the right border.
* @var integer
*/
var $_text_wrap;
 
/**
* The three bits specifying the text vertical alignment.
* @var integer
*/
var $_text_v_align;
 
/**
* 1 bit, apparently not used.
* @var integer
*/
var $_text_justlast;
 
/**
* The two bits specifying the text rotation.
* @var integer
*/
var $_rotation;
 
/**
* The cell's foreground color.
* @var integer
*/
var $_fg_color;
 
/**
* The cell's background color.
* @var integer
*/
var $_bg_color;
 
/**
* The cell's background fill pattern.
* @var integer
*/
var $_pattern;
 
/**
* Style of the bottom border of the cell
* @var integer
*/
var $_bottom;
 
/**
* Color of the bottom border of the cell.
* @var integer
*/
var $_bottom_color;
 
/**
* Style of the top border of the cell
* @var integer
*/
var $_top;
 
/**
* Color of the top border of the cell.
* @var integer
*/
var $_top_color;
 
/**
* Style of the left border of the cell
* @var integer
*/
var $_left;
 
/**
* Color of the left border of the cell.
* @var integer
*/
var $_left_color;
 
/**
* Style of the right border of the cell
* @var integer
*/
var $_right;
 
/**
* Color of the right border of the cell.
* @var integer
*/
var $_right_color;
 
/**
* Constructor
*
* @access public
* @param integer $index the XF index for the format.
* @param array $properties array with properties to be set on initialization.
*/
function Spreadsheet_Excel_Writer_Format($index = 0,$properties = array())
{
$this->_xf_index = $index;
$this->font_index = 0;
$this->_font_name = 'Arial';
$this->_size = 10;
$this->_bold = 0x0190;
$this->_italic = 0;
$this->_color = 0x7FFF;
$this->_underline = 0;
$this->_font_strikeout = 0;
$this->_font_outline = 0;
$this->_font_shadow = 0;
$this->_font_script = 0;
$this->_font_family = 0;
$this->_font_charset = 0;
$this->_num_format = 0;
$this->_hidden = 0;
$this->_locked = 1;
 
$this->_text_h_align = 0;
$this->_text_wrap = 0;
$this->_text_v_align = 2;
$this->_text_justlast = 0;
$this->_rotation = 0;
 
$this->_fg_color = 0x40;
$this->_bg_color = 0x41;
 
$this->_pattern = 0;
$this->_bottom = 0;
$this->_top = 0;
$this->_left = 0;
$this->_right = 0;
$this->_bottom_color = 0x40;
$this->_top_color = 0x40;
$this->_left_color = 0x40;
$this->_right_color = 0x40;
// Set properties passed to Spreadsheet_Excel_Writer_Workbook::addFormat()
foreach($properties as $property => $value)
{
if(method_exists($this,'set'.ucwords($property)))
{
$method_name = 'set'.ucwords($property);
$this->$method_name($value);
}
}
}
 
 
/**
* Generate an Excel BIFF XF record (style or cell).
*
* @param string $style The type of the XF record ('style' or 'cell').
* @return string The XF record
*/
function getXf($style)
{
// Set the type of the XF record and some of the attributes.
if ($style == "style") {
$style = 0xFFF5;
}
else {
$style = $this->_locked;
$style |= $this->_hidden << 1;
}
// Flags to indicate if attributes have been set.
$atr_num = ($this->_num_format != 0)?1:0;
$atr_fnt = ($this->font_index != 0)?1:0;
$atr_alc = ($this->_text_wrap)?1:0;
$atr_bdr = ($this->_bottom ||
$this->_top ||
$this->_left ||
$this->_right)?1:0;
$atr_pat = (($this->_fg_color != 0x40) ||
($this->_bg_color != 0x41) ||
$this->_pattern)?1:0;
$atr_prot = 0;
// Zero the default border colour if the border has not been set.
if ($this->_bottom == 0) {
$this->_bottom_color = 0;
}
if ($this->_top == 0) {
$this->_top_color = 0;
}
if ($this->_right == 0) {
$this->_right_color = 0;
}
if ($this->_left == 0) {
$this->_left_color = 0;
}
$record = 0x00E0; // Record identifier
$length = 0x0010; // Number of bytes to follow
$ifnt = $this->font_index; // Index to FONT record
$ifmt = $this->_num_format; // Index to FORMAT record
$align = $this->_text_h_align; // Alignment
$align |= $this->_text_wrap << 3;
$align |= $this->_text_v_align << 4;
$align |= $this->_text_justlast << 7;
$align |= $this->_rotation << 8;
$align |= $atr_num << 10;
$align |= $atr_fnt << 11;
$align |= $atr_alc << 12;
$align |= $atr_bdr << 13;
$align |= $atr_pat << 14;
$align |= $atr_prot << 15;
$icv = $this->_fg_color; // fg and bg pattern colors
$icv |= $this->_bg_color << 7;
$fill = $this->_pattern; // Fill and border line style
$fill |= $this->_bottom << 6;
$fill |= $this->_bottom_color << 9;
$border1 = $this->_top; // Border line style and color
$border1 |= $this->_left << 3;
$border1 |= $this->_right << 6;
$border1 |= $this->_top_color << 9;
$border2 = $this->_left_color; // Border color
$border2 |= $this->_right_color << 7;
$header = pack("vv", $record, $length);
$data = pack("vvvvvvvv", $ifnt, $ifmt, $style, $align,
$icv, $fill,
$border1, $border2);
return($header.$data);
}
/**
* Generate an Excel BIFF FONT record.
*
* @return string The FONT record
*/
function getFont()
{
$dyHeight = $this->_size * 20; // Height of font (1/20 of a point)
$icv = $this->_color; // Index to color palette
$bls = $this->_bold; // Bold style
$sss = $this->_font_script; // Superscript/subscript
$uls = $this->_underline; // Underline
$bFamily = $this->_font_family; // Font family
$bCharSet = $this->_font_charset; // Character set
$rgch = $this->_font_name; // Font name
$cch = strlen($rgch); // Length of font name
$record = 0x31; // Record identifier
$length = 0x0F + $cch; // Record length
$reserved = 0x00; // Reserved
$grbit = 0x00; // Font attributes
if ($this->_italic) {
$grbit |= 0x02;
}
if ($this->_font_strikeout) {
$grbit |= 0x08;
}
if ($this->_font_outline) {
$grbit |= 0x10;
}
if ($this->_font_shadow) {
$grbit |= 0x20;
}
$header = pack("vv", $record, $length);
$data = pack("vvvvvCCCCC", $dyHeight, $grbit, $icv, $bls,
$sss, $uls, $bFamily,
$bCharSet, $reserved, $cch);
return($header . $data. $this->_font_name);
}
/**
* Returns a unique hash key for a font.
* Used by Spreadsheet_Excel_Writer_Workbook::_storeAllFonts()
*
* The elements that form the key are arranged to increase the probability of
* generating a unique key. Elements that hold a large range of numbers
* (eg. _color) are placed between two binary elements such as _italic
*
* @return string A key for this font
*/
function getFontKey()
{
$key = "$this->_font_name$this->_size";
$key .= "$this->_font_script$this->_underline";
$key .= "$this->_font_strikeout$this->_bold$this->_font_outline";
$key .= "$this->_font_family$this->_font_charset";
$key .= "$this->_font_shadow$this->_color$this->_italic";
$key = str_replace(" ","_",$key);
return ($key);
}
/**
* Returns the index used by Spreadsheet_Excel_Writer_Worksheet::_XF()
*
* @return integer The index for the XF record
*/
function getXfIndex()
{
return($this->_xf_index);
}
/**
* Used in conjunction with the set_xxx_color methods to convert a color
* string into a number. Color range is 0..63 but we will restrict it
* to 8..63 to comply with Gnumeric. Colors 0..7 are repeated in 8..15.
*
* @access private
* @param string $name_color name of the color (i.e.: 'blue', 'red', etc..). Optional.
* @return integer The color index
*/
function _getColor($name_color = '')
{
$colors = array(
'aqua' => 0x0F,
'cyan' => 0x0F,
'black' => 0x08,
'blue' => 0x0C,
'brown' => 0x10,
'magenta' => 0x0E,
'fuchsia' => 0x0E,
'gray' => 0x17,
'grey' => 0x17,
'green' => 0x11,
'lime' => 0x0B,
'navy' => 0x12,
'orange' => 0x35,
'purple' => 0x14,
'red' => 0x0A,
'silver' => 0x16,
'white' => 0x09,
'yellow' => 0x0D
);
// Return the default color, 0x7FFF, if undef,
if($name_color == '') {
return(0x7FFF);
}
// or the color string converted to an integer,
if(isset($colors[$name_color])) {
return($colors[$name_color]);
}
// or the default color if string is unrecognised,
if(preg_match("/\D/",$name_color)) {
return(0x7FFF);
}
// or an index < 8 mapped into the correct range,
if($name_color < 8) {
return($name_color + 8);
}
// or the default color if arg is outside range,
if($name_color > 63) {
return(0x7FFF);
}
// or an integer in the valid range
return($name_color);
}
/**
* Set cell alignment.
*
* @access public
* @param string $location alignment for the cell ('left', 'right', etc...).
*/
function setAlign($location)
{
if (preg_match("/\d/",$location)) {
return; // Ignore numbers
}
$location = strtolower($location);
if ($location == 'left') {
$this->_text_h_align = 1;
}
if ($location == 'centre') {
$this->_text_h_align = 2;
}
if ($location == 'center') {
$this->_text_h_align = 2;
}
if ($location == 'right') {
$this->_text_h_align = 3;
}
if ($location == 'fill') {
$this->_text_h_align = 4;
}
if ($location == 'justify') {
$this->_text_h_align = 5;
}
if ($location == 'merge') {
$this->_text_h_align = 6;
}
if ($location == 'equal_space') { // For T.K.
$this->_text_h_align = 7;
}
if ($location == 'top') {
$this->_text_v_align = 0;
}
if ($location == 'vcentre') {
$this->_text_v_align = 1;
}
if ($location == 'vcenter') {
$this->_text_v_align = 1;
}
if ($location == 'bottom') {
$this->_text_v_align = 2;
}
if ($location == 'vjustify') {
$this->_text_v_align = 3;
}
if ($location == 'vequal_space') { // For T.K.
$this->_text_v_align = 4;
}
}
/**
* This is an alias for the unintuitive setAlign('merge')
*
* @access public
*/
function setMerge()
{
$this->setAlign('merge');
}
/**
* Sets the boldness of the text.
* Bold has a range 100..1000.
* 0 (400) is normal. 1 (700) is bold.
*
* @access public
* @param integer $weight Weight for the text, 0 maps to 400 (normal text),
1 maps to 700 (bold text). Valid range is: 100-1000.
It's Optional, default is 1 (bold).
*/
function setBold($weight = 1)
{
if($weight == 1) {
$weight = 0x2BC; // Bold text
}
if($weight == 0) {
$weight = 0x190; // Normal text
}
if($weight < 0x064) {
$weight = 0x190; // Lower bound
}
if($weight > 0x3E8) {
$weight = 0x190; // Upper bound
}
$this->_bold = $weight;
}
/************************************
* FUNCTIONS FOR SETTING CELLS BORDERS
*/
/**
* Sets the width for the bottom border of the cell
*
* @access public
* @param integer $style style of the cell border. 1 => thin, 2 => thick.
*/
function setBottom($style)
{
$this->_bottom = $style;
}
/**
* Sets the width for the top border of the cell
*
* @access public
* @param integer $style style of the cell top border. 1 => thin, 2 => thick.
*/
function setTop($style)
{
$this->_top = $style;
}
/**
* Sets the width for the left border of the cell
*
* @access public
* @param integer $style style of the cell left border. 1 => thin, 2 => thick.
*/
function setLeft($style)
{
$this->_left = $style;
}
/**
* Sets the width for the right border of the cell
*
* @access public
* @param integer $style style of the cell right border. 1 => thin, 2 => thick.
*/
function setRight($style)
{
$this->_right = $style;
}
/**
* Set cells borders to the same style
*
* @access public
* @param integer $style style to apply for all cell borders. 1 => thin, 2 => thick.
*/
function setBorder($style)
{
$this->setBottom($style);
$this->setTop($style);
$this->setLeft($style);
$this->setRight($style);
}
/*******************************************
* FUNCTIONS FOR SETTING CELLS BORDERS COLORS
*/
/**
* Sets all the cell's borders to the same color
*
* @access public
* @param mixed $color The color we are setting. Either a string (like 'blue'),
* or an integer (range is [8...63]).
*/
function setBorderColor($color)
{
$this->setBottomColor($color);
$this->setTopColor($color);
$this->setLeftColor($color);
$this->setRightColor($color);
}
/**
* Sets the cell's bottom border color
*
* @access public
* @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
*/
function setBottomColor($color)
{
$value = $this->_getColor($color);
$this->_bottom_color = $value;
}
/**
* Sets the cell's top border color
*
* @access public
* @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
*/
function setTopColor($color)
{
$value = $this->_getColor($color);
$this->_top_color = $value;
}
/**
* Sets the cell's left border color
*
* @access public
* @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
*/
function setLeftColor($color)
{
$value = $this->_getColor($color);
$this->_left_color = $value;
}
/**
* Sets the cell's right border color
*
* @access public
* @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
*/
function setRightColor($color)
{
$value = $this->_getColor($color);
$this->_right_color = $value;
}
/**
* Sets the cell's foreground color
*
* @access public
* @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
*/
function setFgColor($color)
{
$value = $this->_getColor($color);
$this->_fg_color = $value;
}
/**
* Sets the cell's background color
*
* @access public
* @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
*/
function setBgColor($color)
{
$value = $this->_getColor($color);
$this->_bg_color = $value;
}
/**
* Sets the cell's color
*
* @access public
* @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
*/
function setColor($color)
{
$value = $this->_getColor($color);
$this->_color = $value;
}
/**
* Sets the fill pattern attribute of a cell
*
* @access public
* @param integer $arg Optional. Defaults to 1. Meaningful values are: 0-18,
* 0 meaning no background.
*/
function setPattern($arg = 1)
{
$this->_pattern = $arg;
}
/**
* Sets the underline of the text
*
* @access public
* @param integer $underline The value for underline. Possible values are:
* 1 => underline, 2 => double underline.
*/
function setUnderline($underline)
{
$this->_underline = $underline;
}
/**
* Sets the font style as italic
*
* @access public
*/
function setItalic()
{
$this->_italic = 1;
}
 
/**
* Sets the font size
*
* @access public
* @param integer $size The font size (in pixels I think).
*/
function setSize($size)
{
$this->_size = $size;
}
/**
* Sets text wrapping
*
* @access public
*/
function setTextWrap()
{
$this->_text_wrap = 1;
}
 
/**
* Sets the orientation of the text
*
* @access public
* @param integer $angle The rotation angle for the text (clockwise). Possible
values are: 0, 90, 270 and -1 for stacking top-to-bottom.
*/
function setTextRotation($angle)
{
switch ($angle)
{
case 0:
$this->_rotation = 0;
break;
case 90:
$this->_rotation = 3;
break;
case 270:
$this->_rotation = 2;
break;
case -1:
$this->_rotation = 1;
break;
default :
$this->raiseError("Invalid value for angle.".
" Possible values are: 0, 90, 270 and -1 ".
"for stacking top-to-bottom.");
$this->_rotation = 0;
break;
}
}
 
/**
* Sets the numeric format.
* It can be date, time, currency, etc...
*
* @access public
* @param integer $num_format The numeric format.
*/
function setNumFormat($num_format)
{
$this->_num_format = $num_format;
}
 
/**
* Sets font as strikeout.
*
* @access public
*/
function setStrikeOut()
{
$this->_font_strikeout = 1;
}
 
/**
* Sets outlining for a font.
*
* @access public
*/
function setOutLine()
{
$this->_font_outline = 1;
}
 
/**
* Sets font as shadow.
*
* @access public
*/
function setShadow()
{
$this->_font_shadow = 1;
}
 
/**
* Sets the script type of the text
*
* @access public
* @param integer $script The value for script type. Possible values are:
* 1 => superscript, 2 => subscript.
*/
function setScript($script)
{
$this->_font_script = $script;
}
 
/**
* Unlocks a cell. Useful for unprotecting particular cells of a protected sheet.
*
* @access public
*/
function setUnLocked()
{
$this->_locked = 0;
}
}
?>
/trunk/jrest/lib/Spreadsheet/Excel/Writer/Worksheet.php
New file
0,0 → 1,3095
<?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_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 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('Parser.php');
require_once('BIFFwriter.php');
 
/**
* Class for generating Excel Spreadsheets
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer_Worksheet extends Spreadsheet_Excel_Writer_BIFFwriter
{
/**
* Name of the Worksheet
* @var string
*/
var $name;
 
/**
* Index for the Worksheet
* @var integer
*/
var $index;
 
/**
* Reference to the (default) Format object for URLs
* @var object Format
*/
var $_url_format;
 
/**
* Reference to the parser used for parsing formulas
* @var object Format
*/
var $_parser;
 
/**
* Filehandle to the temporary file for storing data
* @var resource
*/
var $_filehandle;
 
/**
* Boolean indicating if we are using a temporary file for storing data
* @var bool
*/
var $_using_tmpfile;
 
/**
* Maximum number of rows for an Excel spreadsheet (BIFF5)
* @var integer
*/
var $_xls_rowmax;
 
/**
* Maximum number of columns for an Excel spreadsheet (BIFF5)
* @var integer
*/
var $_xls_colmax;
 
/**
* Maximum number of characters for a string (LABEL record in BIFF5)
* @var integer
*/
var $_xls_strmax;
 
/**
* First row for the DIMENSIONS record
* @var integer
* @see storeDimensions()
*/
var $_dim_rowmin;
 
/**
* Last row for the DIMENSIONS record
* @var integer
* @see storeDimensions()
*/
var $_dim_rowmax;
 
/**
* First column for the DIMENSIONS record
* @var integer
* @see storeDimensions()
*/
var $_dim_colmin;
 
/**
* Last column for the DIMENSIONS record
* @var integer
* @see storeDimensions()
*/
var $_dim_colmax;
 
/**
* Array containing format information for columns
* @var array
*/
var $_colinfo;
 
/**
* Array containing the selected area for the worksheet
* @var array
*/
var $_selection;
 
/**
* Array containing the panes for the worksheet
* @var array
*/
var $_panes;
 
/**
* The active pane for the worksheet
* @var integer
*/
var $_active_pane;
 
/**
* Bit specifying if panes are frozen
* @var integer
*/
var $_frozen;
 
/**
* Bit specifying if the worksheet is selected
* @var integer
*/
var $selected;
 
/**
* The paper size (for printing) (DOCUMENT!!!)
* @var integer
*/
var $_paper_size;
 
/**
* Bit specifying paper orientation (for printing). 0 => landscape, 1 => portrait
* @var integer
*/
var $_orientation;
 
/**
* The page header caption
* @var string
*/
var $_header;
 
/**
* The page footer caption
* @var string
*/
var $_footer;
 
/**
* The horizontal centering value for the page
* @var integer
*/
var $_hcenter;
 
/**
* The vertical centering value for the page
* @var integer
*/
var $_vcenter;
 
/**
* The margin for the header
* @var float
*/
var $_margin_head;
 
/**
* The margin for the footer
* @var float
*/
var $_margin_foot;
 
/**
* The left margin for the worksheet in inches
* @var float
*/
var $_margin_left;
 
/**
* The right margin for the worksheet in inches
* @var float
*/
var $_margin_right;
 
/**
* The top margin for the worksheet in inches
* @var float
*/
var $_margin_top;
 
/**
* The bottom margin for the worksheet in inches
* @var float
*/
var $_margin_bottom;
 
/**
* First row to reapeat on each printed page
* @var integer
*/
var $title_rowmin;
 
/**
* Last row to reapeat on each printed page
* @var integer
*/
var $title_rowmax;
 
/**
* First column to reapeat on each printed page
* @var integer
*/
var $title_colmin;
 
/**
* First row of the area to print
* @var integer
*/
var $print_rowmin;
 
/**
* Last row to of the area to print
* @var integer
*/
var $print_rowmax;
 
/**
* First column of the area to print
* @var integer
*/
var $print_colmin;
 
/**
* Last column of the area to print
* @var integer
*/
var $print_colmax;
 
/**
* Constructor
*
* @param string $name The name of the new worksheet
* @param integer $index The index of the new worksheet
* @param mixed &$activesheet The current activesheet of the workbook we belong to
* @param mixed &$firstsheet The first worksheet in the workbook we belong to
* @param mixed &$url_format The default format for hyperlinks
* @param mixed &$parser The formula parser created for the Workbook
*/
function Spreadsheet_Excel_Writer_Worksheet($name, $index, &$activesheet,
&$firstsheet, &$url_format,
&$parser)
{
// It needs to call its parent's constructor explicitly
$this->Spreadsheet_Excel_Writer_BIFFwriter();
$rowmax = 65536; // 16384 in Excel 5
$colmax = 256;
$this->name = $name;
$this->index = $index;
$this->activesheet = &$activesheet;
$this->firstsheet = &$firstsheet;
$this->_url_format = &$url_format;
$this->_parser = &$parser;
//$this->ext_sheets = array();
$this->_filehandle = "";
$this->_using_tmpfile = true;
//$this->fileclosed = 0;
//$this->offset = 0;
$this->_xls_rowmax = $rowmax;
$this->_xls_colmax = $colmax;
$this->_xls_strmax = 255;
$this->_dim_rowmin = $rowmax + 1;
$this->_dim_rowmax = 0;
$this->_dim_colmin = $colmax + 1;
$this->_dim_colmax = 0;
$this->_colinfo = array();
$this->_selection = array(0,0,0,0);
$this->_panes = array();
$this->_active_pane = 3;
$this->_frozen = 0;
$this->selected = 0;
$this->_paper_size = 0x0;
$this->_orientation = 0x1;
$this->_header = '';
$this->_footer = '';
$this->_hcenter = 0;
$this->_vcenter = 0;
$this->_margin_head = 0.50;
$this->_margin_foot = 0.50;
$this->_margin_left = 0.75;
$this->_margin_right = 0.75;
$this->_margin_top = 1.00;
$this->_margin_bottom = 1.00;
$this->title_rowmin = NULL;
$this->title_rowmax = NULL;
$this->title_colmin = NULL;
$this->title_colmax = NULL;
$this->print_rowmin = NULL;
$this->print_rowmax = NULL;
$this->print_colmin = NULL;
$this->print_colmax = NULL;
$this->_print_gridlines = 1;
$this->_print_headers = 0;
$this->_fit_page = 0;
$this->_fit_width = 0;
$this->_fit_height = 0;
$this->_hbreaks = array();
$this->_vbreaks = array();
$this->_protect = 0;
$this->_password = NULL;
$this->col_sizes = array();
$this->row_sizes = array();
$this->_zoom = 100;
$this->_print_scale = 100;
$this->_initialize();
}
/**
* Open a tmp file to store the majority of the Worksheet data. If this fails,
* for example due to write permissions, store the data in memory. This can be
* slow for large files.
*
* @access private
*/
function _initialize()
{
// Open tmp file for storing Worksheet data
$fh = tmpfile();
if ( $fh) {
// Store filehandle
$this->_filehandle = $fh;
}
else {
// If tmpfile() fails store data in memory
$this->_using_tmpfile = false;
}
}
/**
* Add data to the beginning of the workbook (note the reverse order)
* and to the end of the workbook.
*
* @access public
* @see Spreadsheet_Excel_Writer_Workbook::storeWorkbook()
* @param array $sheetnames The array of sheetnames from the Workbook this
* worksheet belongs to
*/
function close($sheetnames)
{
$num_sheets = count($sheetnames);
/***********************************************
* Prepend in reverse order!!
*/
// Prepend the sheet dimensions
$this->storeDimensions();
// Prepend the sheet password
$this->_storePassword();
// Prepend the sheet protection
$this->_storeProtect();
// Prepend the page setup
$this->_storeSetup();
// Prepend the bottom margin
$this->_storeMarginBottom();
// Prepend the top margin
$this->_storeMarginTop();
// Prepend the right margin
$this->_storeMarginRight();
// Prepend the left margin
$this->_storeMarginLeft();
// Prepend the page vertical centering
$this->_storeVcenter();
// Prepend the page horizontal centering
$this->_storeHcenter();
// Prepend the page footer
$this->_storeFooter();
// Prepend the page header
$this->_storeHeader();
// Prepend the vertical page breaks
$this->_storeVbreak();
// Prepend the horizontal page breaks
$this->_storeHbreak();
// Prepend WSBOOL
$this->_storeWsbool();
// Prepend GRIDSET
$this->_storeGridset();
// Prepend PRINTGRIDLINES
$this->_storePrintGridlines();
// Prepend PRINTHEADERS
$this->_storePrintHeaders();
// Prepend EXTERNSHEET references
for ($i = $num_sheets; $i > 0; $i--)
{
$sheetname = $sheetnames[$i-1];
$this->_storeExternsheet($sheetname);
}
// Prepend the EXTERNCOUNT of external references.
$this->_storeExterncount($num_sheets);
// Prepend the COLINFO records if they exist
if (!empty($this->_colinfo))
{
for($i=0; $i < count($this->_colinfo); $i++) {
$this->_storeColinfo($this->_colinfo[$i]);
}
$this->_storeDefcol();
}
// Prepend the BOF record
$this->_storeBof(0x0010);
/*
* End of prepend. Read upwards from here.
***********************************************/
// Append
$this->_storeWindow2();
$this->_storeZoom();
if (!empty($this->_panes)) {
$this->_storePanes($this->_panes);
}
$this->_storeSelection($this->_selection);
$this->_storeEof();
}
/**
* Retrieve the worksheet name.
* This is usefull when creating worksheets without a name.
*
* @access public
* @return string The worksheet's name
*/
function getName()
{
return $this->name;
}
/**
* Retrieves data from memory in one chunk, or from disk in $buffer
* sized chunks.
*
* @return string The data
*/
function getData()
{
$buffer = 4096;
// Return data stored in memory
if (isset($this->_data))
{
$tmp = $this->_data;
unset($this->_data);
$fh = $this->_filehandle;
if ($this->_using_tmpfile) {
fseek($fh, 0);
}
return($tmp);
}
// Return data stored on disk
if ($this->_using_tmpfile)
{
if ($tmp = fread($this->_filehandle, $buffer)) {
return($tmp);
}
}
// No data to return
return('');
}
/**
* Set this worksheet as a selected worksheet,
* i.e. the worksheet has its tab highlighted.
*
* @access public
*/
function select()
{
$this->selected = 1;
}
/**
* Set this worksheet as the active worksheet,
* i.e. the worksheet that is displayed when the workbook is opened.
* Also set it as selected.
*
* @access public
*/
function activate()
{
$this->selected = 1;
$this->activesheet = $this->index;
}
/**
* Set this worksheet as the first visible sheet.
* This is necessary when there are a large number of worksheets and the
* activated worksheet is not visible on the screen.
*
* @access public
*/
function setFirstSheet()
{
$this->firstsheet = $this->index;
}
/**
* Set the worksheet protection flag
* to prevent accidental modification and to
* hide formulas if the locked and hidden format properties have been set.
*
* @access public
* @param string $password The password to use for protecting the sheet.
*/
function protect($password)
{
$this->_protect = 1;
$this->_password = $this->_encodePassword($password);
}
/**
* Set the width of a single column or a range of columns.
*
* @access public
* @param integer $firstcol first column on the range
* @param integer $lastcol last column on the range
* @param integer $width width to set
* @param mixed $format The optional XF format to apply to the columns
* @param integer $hidden The optional hidden atribute
*/
function setColumn($firstcol, $lastcol, $width, $format = 0, $hidden = 0)
{
$this->_colinfo[] = array($firstcol, $lastcol, $width, &$format, $hidden);
// Set width to zero if column is hidden
$width = ($hidden) ? 0 : $width;
for($col = $firstcol; $col <= $lastcol; $col++) {
$this->col_sizes[$col] = $width;
}
}
/**
* Set which cell or cells are selected in a worksheet
*
* @access public
* @param integer $first_row first row in the selected quadrant
* @param integer $first_column first column in the selected quadrant
* @param integer $last_row last row in the selected quadrant
* @param integer $last_column last column in the selected quadrant
*/
function setSelection($first_row,$first_column,$last_row,$last_column)
{
$this->_selection = array($first_row,$first_column,$last_row,$last_column);
}
/**
* Set panes and mark them as frozen.
*
* @access public
* @param array $panes This is the only parameter received and is composed of the following:
* 0 => Vertical split position,
* 1 => Horizontal split position
* 2 => Top row visible
* 3 => Leftmost column visible
* 4 => Active pane
*/
function freezePanes($panes)
{
$this->_frozen = 1;
$this->_panes = $panes;
}
/**
* Set panes and mark them as unfrozen.
*
* @access public
* @param array $panes This is the only parameter received and is composed of the following:
* 0 => Vertical split position,
* 1 => Horizontal split position
* 2 => Top row visible
* 3 => Leftmost column visible
* 4 => Active pane
*/
function thawPanes($panes)
{
$this->_frozen = 0;
$this->_panes = $panes;
}
/**
* Set the page orientation as portrait.
*
* @access public
*/
function setPortrait()
{
$this->_orientation = 1;
}
/**
* Set the page orientation as landscape.
*
* @access public
*/
function setLandscape()
{
$this->_orientation = 0;
}
/**
* Set the paper type. Ex. 1 = US Letter, 9 = A4
*
* @access public
* @param integer $size The type of paper size to use
*/
function setPaper($size = 0)
{
$this->_paper_size = $size;
}
/**
* Set the page header caption and optional margin.
*
* @access public
* @param string $string The header text
* @param float $margin optional head margin in inches.
*/
function setHeader($string,$margin = 0.50)
{
if (strlen($string) >= 255) {
//carp 'Header string must be less than 255 characters';
return;
}
$this->_header = $string;
$this->_margin_head = $margin;
}
/**
* Set the page footer caption and optional margin.
*
* @access public
* @param string $string The footer text
* @param float $margin optional foot margin in inches.
*/
function setFooter($string,$margin = 0.50)
{
if (strlen($string) >= 255) {
//carp 'Footer string must be less than 255 characters';
return;
}
$this->_footer = $string;
$this->_margin_foot = $margin;
}
/**
* Center the page horinzontally.
*
* @access public
* @param integer $center the optional value for centering. Defaults to 1 (center).
*/
function centerHorizontally($center = 1)
{
$this->_hcenter = $center;
}
/**
* Center the page vertically.
*
* @access public
* @param integer $center the optional value for centering. Defaults to 1 (center).
*/
function centerVertically($center = 1)
{
$this->_vcenter = $center;
}
/**
* Set all the page margins to the same value in inches.
*
* @access public
* @param float $margin The margin to set in inches
*/
function setMargins($margin)
{
$this->setMarginLeft($margin);
$this->setMarginRight($margin);
$this->setMarginTop($margin);
$this->setMarginBottom($margin);
}
/**
* Set the left and right margins to the same value in inches.
*
* @access public
* @param float $margin The margin to set in inches
*/
function setMargins_LR($margin)
{
$this->setMarginLeft($margin);
$this->setMarginRight($margin);
}
/**
* Set the top and bottom margins to the same value in inches.
*
* @access public
* @param float $margin The margin to set in inches
*/
function setMargins_TB($margin)
{
$this->setMarginTop($margin);
$this->setMarginBottom($margin);
}
/**
* Set the left margin in inches.
*
* @access public
* @param float $margin The margin to set in inches
*/
function setMarginLeft($margin = 0.75)
{
$this->_margin_left = $margin;
}
/**
* Set the right margin in inches.
*
* @access public
* @param float $margin The margin to set in inches
*/
function setMarginRight($margin = 0.75)
{
$this->_margin_right = $margin;
}
/**
* Set the top margin in inches.
*
* @access public
* @param float $margin The margin to set in inches
*/
function setMarginTop($margin = 1.00)
{
$this->_margin_top = $margin;
}
/**
* Set the bottom margin in inches.
*
* @access public
* @param float $margin The margin to set in inches
*/
function setMarginBottom($margin = 1.00)
{
$this->_margin_bottom = $margin;
}
/**
* Set the rows to repeat at the top of each printed page.
*
* @access public
* @param integer $first_row First row to repeat
* @param integer $last_row Last row to repeat. Optional.
*/
function repeatRows($first_row, $last_row = NULL)
{
$this->title_rowmin = $first_row;
if (isset($last_row)) { //Second row is optional
$this->title_rowmax = $last_row;
}
else {
$this->title_rowmax = $first_row;
}
}
/**
* Set the columns to repeat at the left hand side of each printed page.
*
* @access public
* @param integer $first_col First column to repeat
* @param integer $last_col Last column to repeat. Optional.
*/
function repeatColumns($first_col, $last_col = NULL)
{
$this->title_colmin = $first_col;
if (isset($last_col)) { // Second col is optional
$this->title_colmax = $last_col;
}
else {
$this->title_colmax = $first_col;
}
}
/**
* Set the area of each worksheet that will be printed.
*
* @access public
* @param integer $first_row First row of the area to print
* @param integer $first_col First column of the area to print
* @param integer $last_row Last row of the area to print
* @param integer $last_col Last column of the area to print
*/
function printArea($first_row, $first_col, $last_row, $last_col)
{
$this->print_rowmin = $first_row;
$this->print_colmin = $first_col;
$this->print_rowmax = $last_row;
$this->print_colmax = $last_col;
}
/**
* Set the option to hide gridlines on the printed page.
*
* @access public
*/
function hideGridlines()
{
$this->_print_gridlines = 0;
}
/**
* Set the option to print the row and column headers on the printed page.
*
* @access public
* @param integer $print Whether to print the headers or not. Defaults to 1 (print).
*/
function printRowColHeaders($print = 1)
{
$this->_print_headers = $print;
}
/**
* Set the vertical and horizontal number of pages that will define the maximum area printed.
* It doesn't seem to work with OpenOffice.
*
* @access public
* @param integer $width Maximun width of printed area in pages
* @param integer $height Maximun heigth of printed area in pages
* @see setPrintScale()
*/
function fitToPages($width, $height)
{
$this->_fit_page = 1;
$this->_fit_width = $width;
$this->_fit_height = $height;
}
/**
* Store the horizontal page breaks on a worksheet (for printing).
* The breaks represent the row after which the break is inserted.
*
* @access public
* @param array $breaks Array containing the horizontal page breaks
*/
function setHPagebreaks($breaks)
{
foreach($breaks as $break) {
array_push($this->_hbreaks,$break);
}
}
/**
* Store the vertical page breaks on a worksheet (for printing).
* The breaks represent the column after which the break is inserted.
*
* @access public
* @param array $breaks Array containing the vertical page breaks
*/
function setVPagebreaks($breaks)
{
foreach($breaks as $break) {
array_push($this->_vbreaks,$break);
}
}
/**
* Set the worksheet zoom factor.
*
* @access public
* @param integer $scale The zoom factor
*/
function setZoom($scale = 100)
{
// Confine the scale to Excel's range
if ($scale < 10 or $scale > 400)
{
$this->raiseError("Zoom factor $scale outside range: 10 <= zoom <= 400");
$scale = 100;
}
$this->_zoom = floor($scale);
}
/**
* Set the scale factor for the printed page.
* It turns off the "fit to page" option
*
* @access public
* @param integer $scale The optional scale factor. Defaults to 100
*/
function setPrintScale($scale = 100)
{
// Confine the scale to Excel's range
if ($scale < 10 or $scale > 400)
{
$this->raiseError("Print scale $scale outside range: 10 <= zoom <= 400");
$scale = 100;
}
// Turn off "fit to page" option
$this->_fit_page = 0;
$this->_print_scale = floor($scale);
}
/**
* Map to the appropriate write method acording to the token recieved.
*
* @access public
* @param integer $row The row of the cell we are writing to
* @param integer $col The column of the cell we are writing to
* @param mixed $token What we are writing
* @param mixed $format The optional format to apply to the cell
*/
function write($row, $col, $token, $format = 0)
{
// Check for a cell reference in A1 notation and substitute row and column
/*if ($_[0] =~ /^\D/) {
@_ = $this->_substituteCellref(@_);
}*/
// Match number
if (preg_match("/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/",$token)) {
return $this->writeNumber($row,$col,$token,$format);
}
// Match http or ftp URL
elseif (preg_match("/^[fh]tt?p:\/\//",$token)) {
return $this->writeUrl($row, $col, $token, $format);
}
// Match mailto:
elseif (preg_match("/^mailto:/",$token)) {
return $this->writeUrl($row, $col, $token, $format);
}
// Match internal or external sheet link
elseif (preg_match("/^(?:in|ex)ternal:/",$token)) {
return $this->writeUrl($row, $col, $token, $format);
}
// Match formula
elseif (preg_match("/^=/",$token)) {
return $this->writeFormula($row, $col, $token, $format);
}
// Match formula
elseif (preg_match("/^@/",$token)) {
return $this->writeFormula($row, $col, $token, $format);
}
// Match blank
elseif ($token == '') {
return $this->writeBlank($row,$col,$format);
}
// Default: match string
else {
return $this->writeString($row,$col,$token,$format);
}
}
/**
* Write an array of values as a row
*
* @access public
* @param
* @return
*/
 
function writeRow($row, $col, $val, $format=0)
{
if (is_array($val)) {
foreach($val as $v) {
if (is_array($v)) {
$this->writeCol($row, $col, $v, $format);
} else {
$this->write($row, $col, $v, $format);
}
$col++;
}
} else {
$retval = new PEAR_Error('$val needs to be an array');
}
return($retval);
}
/**
* Write an array of values as a column
*
* @access public
* @param
* @return
*/
function writeCol($row, $col, $val, $format=0)
{
if (is_array($val)) {
foreach($val as $v) {
$this->write($row, $col, $v, $format);
$row++;
}
} else {
$retval = new PEAR_Error('$val needs to be an array');
}
return($retval);
}
/**
* Returns an index to the XF record in the workbook
*
* @access private
* @param mixed &$format The optional XF format
* @return integer The XF record index
*/
function _XF(&$format)
{
if ($format != 0) {
return($format->getXfIndex());
}
else {
return(0x0F);
}
}
/******************************************************************************
*******************************************************************************
*
* Internal methods
*/
/**
* Store Worksheet data in memory using the parent's class append() or to a
* temporary file, the default.
*
* @access private
* @param string $data The binary data to append
*/
function _append($data)
{
if ($this->_using_tmpfile)
{
// Add CONTINUE records if necessary
if (strlen($data) > $this->_limit) {
$data = $this->_addContinue($data);
}
fwrite($this->_filehandle,$data);
$this->_datasize += strlen($data);
}
else {
parent::_append($data);
}
}
/**
* Substitute an Excel cell reference in A1 notation for zero based row and
* column values in an argument list.
*
* Ex: ("A4", "Hello") is converted to (3, 0, "Hello").
*
* @access private
* @param string $cell The cell reference. Or range of cells.
* @return array
*/
function _substituteCellref($cell)
{
$cell = strtoupper($cell);
// Convert a column range: 'A:A' or 'B:G'
if (preg_match("/([A-I]?[A-Z]):([A-I]?[A-Z])/",$cell,$match)) {
list($no_use, $col1) = $this->_cellToRowcol($match[1] .'1'); // Add a dummy row
list($no_use, $col2) = $this->_cellToRowcol($match[2] .'1'); // Add a dummy row
return(array($col1, $col2));
}
// Convert a cell range: 'A1:B7'
if (preg_match("/\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)/",$cell,$match)) {
list($row1, $col1) = $this->_cellToRowcol($match[1]);
list($row2, $col2) = $this->_cellToRowcol($match[2]);
return(array($row1, $col1, $row2, $col2));
}
// Convert a cell reference: 'A1' or 'AD2000'
if (preg_match("/\$?([A-I]?[A-Z]\$?\d+)/",$cell)) {
list($row1, $col1) = $this->_cellToRowcol($match[1]);
return(array($row1, $col1));
}
// TODO use real error codes
$this->raiseError("Unknown cell reference $cell", 0, PEAR_ERROR_DIE);
}
/**
* Convert an Excel cell reference in A1 notation to a zero based row and column
* reference; converts C1 to (0, 2).
*
* @access private
* @param string $cell The cell reference.
* @return array containing (row, column)
*/
function _cellToRowcol($cell)
{
preg_match("/\$?([A-I]?[A-Z])\$?(\d+)/",$cell,$match);
$col = $match[1];
$row = $match[2];
// Convert base26 column string to number
$chars = split('', $col);
$expn = 0;
$col = 0;
while ($chars) {
$char = array_pop($chars); // LS char first
$col += (ord($char) -ord('A') +1) * pow(26,$expn);
$expn++;
}
// Convert 1-index to zero-index
$row--;
$col--;
return(array($row, $col));
}
/**
* Based on the algorithm provided by Daniel Rentz of OpenOffice.
*
* @access private
* @param string $plaintext The password to be encoded in plaintext.
* @return string The encoded password
*/
function _encodePassword($plaintext)
{
$password = 0x0000;
$i = 1; // char position
// split the plain text password in its component characters
$chars = preg_split('//', $plaintext, -1, PREG_SPLIT_NO_EMPTY);
foreach($chars as $char)
{
$value = ord($char) << $i; // shifted ASCII value
$rotated_bits = $value >> 15; // rotated bits beyond bit 15
$value &= 0x7fff; // first 15 bits
$password ^= ($value | $rotated_bits);
$i++;
}
$password ^= strlen($plaintext);
$password ^= 0xCE4B;
 
return($password);
}
 
/******************************************************************************
*******************************************************************************
*
* BIFF RECORDS
*/
/**
* Write a double to the specified row and column (zero indexed).
* An integer can be written as a double. Excel will display an
* integer. $format is optional.
*
* Returns 0 : normal termination
* -2 : row or column out of range
*
* @access public
* @param integer $row Zero indexed row
* @param integer $col Zero indexed column
* @param float $num The number to write
* @param mixed $format The optional XF format
* @return integer
*/
function writeNumber($row, $col, $num, $format = 0)
{
$record = 0x0203; // Record identifier
$length = 0x000E; // Number of bytes to follow
$xf = $this->_XF($format); // The cell format
// Check that row and col are valid and store max and min values
if ($row >= $this->_xls_rowmax)
{
return(-2);
}
if ($col >= $this->_xls_colmax)
{
return(-2);
}
if ($row < $this->_dim_rowmin)
{
$this->_dim_rowmin = $row;
}
if ($row > $this->_dim_rowmax)
{
$this->_dim_rowmax = $row;
}
if ($col < $this->_dim_colmin)
{
$this->_dim_colmin = $col;
}
if ($col > $this->_dim_colmax)
{
$this->_dim_colmax = $col;
}
$header = pack("vv", $record, $length);
$data = pack("vvv", $row, $col, $xf);
$xl_double = pack("d", $num);
if ($this->_byte_order) // if it's Big Endian
{
$xl_double = strrev($xl_double);
}
$this->_append($header.$data.$xl_double);
return(0);
}
/**
* Write a string to the specified row and column (zero indexed).
* NOTE: there is an Excel 5 defined limit of 255 characters.
* $format is optional.
* Returns 0 : normal termination
* -2 : row or column out of range
* -3 : long string truncated to 255 chars
*
* @access public
* @param integer $row Zero indexed row
* @param integer $col Zero indexed column
* @param string $str The string to write
* @param mixed $format The XF format for the cell
* @return integer
*/
function writeString($row, $col, $str, $format = 0)
{
$strlen = strlen($str);
$record = 0x0204; // Record identifier
$length = 0x0008 + $strlen; // Bytes to follow
$xf = $this->_XF($format); // The cell format
$str_error = 0;
// Check that row and col are valid and store max and min values
if ($row >= $this->_xls_rowmax)
{
return(-2);
}
if ($col >= $this->_xls_colmax)
{
return(-2);
}
if ($row < $this->_dim_rowmin)
{
$this->_dim_rowmin = $row;
}
if ($row > $this->_dim_rowmax)
{
$this->_dim_rowmax = $row;
}
if ($col < $this->_dim_colmin)
{
$this->_dim_colmin = $col;
}
if ($col > $this->_dim_colmax)
{
$this->_dim_colmax = $col;
}
if ($strlen > $this->_xls_strmax) // LABEL must be < 255 chars
{
$str = substr($str, 0, $this->_xls_strmax);
$length = 0x0008 + $this->_xls_strmax;
$strlen = $this->_xls_strmax;
$str_error = -3;
}
$header = pack("vv", $record, $length);
$data = pack("vvvv", $row, $col, $xf, $strlen);
$this->_append($header.$data.$str);
return($str_error);
}
 
/**
* Writes a note associated with the cell given by the row and column.
* NOTE records don't have a length limit.
*
* @access public
* @param integer $row Zero indexed row
* @param integer $col Zero indexed column
* @param string $note The note to write
*/
function writeNote($row, $col, $note)
{
$note_length = strlen($note);
$record = 0x001C; // Record identifier
$max_length = 2048; // Maximun length for a NOTE record
//$length = 0x0006 + $note_length; // Bytes to follow
 
// Check that row and col are valid and store max and min values
if ($row >= $this->_xls_rowmax)
{
return(-2);
}
if ($col >= $this->_xls_colmax)
{
return(-2);
}
if ($row < $this->_dim_rowmin)
{
$this->_dim_rowmin = $row;
}
if ($row > $this->_dim_rowmax)
{
$this->_dim_rowmax = $row;
}
if ($col < $this->_dim_colmin)
{
$this->_dim_colmin = $col;
}
if ($col > $this->_dim_colmax)
{
$this->_dim_colmax = $col;
}
// Length for this record is no more than 2048 + 6
$length = 0x0006 + min($note_length, 2048);
$header = pack("vv", $record, $length);
$data = pack("vvv", $row, $col, $note_length);
$this->_append($header.$data.substr($note, 0, 2048));
 
for($i = $max_length; $i < $note_length; $i += $max_length)
{
$chunk = substr($note, $i, $max_length);
$length = 0x0006 + strlen($chunk);
$header = pack("vv", $record, $length);
$data = pack("vvv", -1, 0, strlen($chunk));
$this->_append($header.$data.$chunk);
}
return(0);
}
 
/**
* Write a blank cell to the specified row and column (zero indexed).
* A blank cell is used to specify formatting without adding a string
* or a number.
*
* A blank cell without a format serves no purpose. Therefore, we don't write
* a BLANK record unless a format is specified.
*
* Returns 0 : normal termination (including no format)
* -1 : insufficient number of arguments
* -2 : row or column out of range
*
* @access public
* @param integer $row Zero indexed row
* @param integer $col Zero indexed column
* @param mixed $format The XF format
*/
function writeBlank($row, $col, $format)
{
// Don't write a blank cell unless it has a format
if ($format == 0)
{
return(0);
}
$record = 0x0201; // Record identifier
$length = 0x0006; // Number of bytes to follow
$xf = $this->_XF($format); // The cell format
// Check that row and col are valid and store max and min values
if ($row >= $this->_xls_rowmax)
{
return(-2);
}
if ($col >= $this->_xls_colmax)
{
return(-2);
}
if ($row < $this->_dim_rowmin)
{
$this->_dim_rowmin = $row;
}
if ($row > $this->_dim_rowmax)
{
$this->_dim_rowmax = $row;
}
if ($col < $this->_dim_colmin)
{
$this->_dim_colmin = $col;
}
if ($col > $this->_dim_colmax)
{
$this->_dim_colmax = $col;
}
$header = pack("vv", $record, $length);
$data = pack("vvv", $row, $col, $xf);
$this->_append($header.$data);
return 0;
}
 
/**
* Write a formula to the specified row and column (zero indexed).
* The textual representation of the formula is passed to the parser in
* Parser.php which returns a packed binary string.
*
* Returns 0 : normal termination
* -1 : formula errors (bad formula)
* -2 : row or column out of range
*
* @access public
* @param integer $row Zero indexed row
* @param integer $col Zero indexed column
* @param string $formula The formula text string
* @param mixed $format The optional XF format
* @return integer
*/
function writeFormula($row, $col, $formula, $format = 0)
{
$record = 0x0006; // Record identifier
// Excel normally stores the last calculated value of the formula in $num.
// Clearly we are not in a position to calculate this a priori. Instead
// we set $num to zero and set the option flags in $grbit to ensure
// automatic calculation of the formula when the file is opened.
//
$xf = $this->_XF($format); // The cell format
$num = 0x00; // Current value of formula
$grbit = 0x03; // Option flags
$chn = 0x0000; // Must be zero
// Check that row and col are valid and store max and min values
if ($row >= $this->_xls_rowmax)
{
return(-2);
}
if ($col >= $this->_xls_colmax)
{
return(-2);
}
if ($row < $this->_dim_rowmin)
{
$this->_dim_rowmin = $row;
}
if ($row > $this->_dim_rowmax)
{
$this->_dim_rowmax = $row;
}
if ($col < $this->_dim_colmin)
{
$this->_dim_colmin = $col;
}
if ($col > $this->_dim_colmax)
{
$this->_dim_colmax = $col;
}
// Strip the '=' or '@' sign at the beginning of the formula string
if (preg_match("/^=/",$formula)) {
$formula = preg_replace("/(^=)/","",$formula);
}
elseif (preg_match("/^@/",$formula)) {
$formula = preg_replace("/(^@)/","",$formula);
}
else
{
// Error handling
$this->writeString($row, $col, 'Unrecognised character for formula');
return -1;
}
// Parse the formula using the parser in Parser.php
$error = $this->_parser->parse($formula);
if ($this->isError($error))
{
$this->writeString($row, $col, $error->getMessage());
return -1;
}
$formula = $this->_parser->toReversePolish();
if ($this->isError($formula))
{
$this->writeString($row, $col, $formula->getMessage());
return -1;
}
$formlen = strlen($formula); // Length of the binary string
$length = 0x16 + $formlen; // Length of the record data
$header = pack("vv", $record, $length);
$data = pack("vvvdvVv", $row, $col, $xf, $num,
$grbit, $chn, $formlen);
$this->_append($header.$data.$formula);
return 0;
}
/**
* Write a hyperlink.
* This is comprised of two elements: the visible label and
* the invisible link. The visible label is the same as the link unless an
* alternative string is specified. The label is written using the
* writeString() method. Therefore the 255 characters string limit applies.
* $string and $format are optional.
*
* The hyperlink can be to a http, ftp, mail, internal sheet (not yet), or external
* directory url.
*
* Returns 0 : normal termination
* -2 : row or column out of range
* -3 : long string truncated to 255 chars
*
* @access public
* @param integer $row Row
* @param integer $col Column
* @param string $url URL string
* @param string $string Alternative label
* @param mixed $format The cell format
* @return integer
*/
function writeUrl($row, $col, $url, $string = '', $format = 0)
{
// Add start row and col to arg list
return($this->_writeUrl_range($row, $col, $row, $col, $url, $string, $format));
}
/**
* This is the more general form of writeUrl(). It allows a hyperlink to be
* written to a range of cells. This function also decides the type of hyperlink
* to be written. These are either, Web (http, ftp, mailto), Internal
* (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1').
*
* @access private
* @see writeUrl()
* @param integer $row1 Start row
* @param integer $col1 Start column
* @param integer $row2 End row
* @param integer $col2 End column
* @param string $url URL string
* @param string $string Alternative label
* @param mixed $format The cell format
* @return integer
*/
function _writeUrl_range($row1, $col1, $row2, $col2, $url, $string = '', $format = 0)
{
// Check for internal/external sheet links or default to web link
if (preg_match('[^internal:]', $url)) {
return($this->_writeUrlInternal($row1, $col1, $row2, $col2, $url, $string, $format));
}
if (preg_match('[^external:]', $url)) {
return($this->_writeUrlExternal($row1, $col1, $row2, $col2, $url, $string, $format));
}
return($this->_writeUrlWeb($row1, $col1, $row2, $col2, $url, $string, $format));
}
/**
* Used to write http, ftp and mailto hyperlinks.
* The link type ($options) is 0x03 is the same as absolute dir ref without
* sheet. However it is differentiated by the $unknown2 data stream.
*
* @access private
* @see writeUrl()
* @param integer $row1 Start row
* @param integer $col1 Start column
* @param integer $row2 End row
* @param integer $col2 End column
* @param string $url URL string
* @param string $str Alternative label
* @param mixed $format The cell format
* @return integer
*/
function _writeUrlWeb($row1, $col1, $row2, $col2, $url, $str, $format = 0)
{
$record = 0x01B8; // Record identifier
$length = 0x00000; // Bytes to follow
if ($format == 0) {
$format = $this->_url_format;
}
// Write the visible label using the writeString() method.
if ($str == '') {
$str = $url;
}
$str_error = $this->writeString($row1, $col1, $str, $format);
if (($str_error == -2) or ($str_error == -3)) {
return $str_error;
}
// Pack the undocumented parts of the hyperlink stream
$unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
$unknown2 = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B");
// Pack the option flags
$options = pack("V", 0x03);
// Convert URL to a null terminated wchar string
$url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
$url = $url . "\0\0\0";
// Pack the length of the URL
$url_len = pack("V", strlen($url));
// Calculate the data length
$length = 0x34 + strlen($url);
// Pack the header data
$header = pack("vv", $record, $length);
$data = pack("vvvv", $row1, $row2, $col1, $col2);
// Write the packed data
$this->_append( $header. $data.
$unknown1. $options.
$unknown2. $url_len. $url);
return($str_error);
}
/**
* Used to write internal reference hyperlinks such as "Sheet1!A1".
*
* @access private
* @see writeUrl()
* @param integer $row1 Start row
* @param integer $col1 Start column
* @param integer $row2 End row
* @param integer $col2 End column
* @param string $url URL string
* @param string $str Alternative label
* @param mixed $format The cell format
* @return integer
*/
function _writeUrlInternal($row1, $col1, $row2, $col2, $url, $str, $format = 0)
{
$record = 0x01B8; // Record identifier
$length = 0x00000; // Bytes to follow
if ($format == 0) {
$format = $this->_url_format;
}
// Strip URL type
$url = preg_replace('s[^internal:]', '', $url);
// Write the visible label
if ($str == '') {
$str = $url;
}
$str_error = $this->writeString($row1, $col1, $str, $format);
if (($str_error == -2) or ($str_error == -3)) {
return $str_error;
}
// Pack the undocumented parts of the hyperlink stream
$unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
// Pack the option flags
$options = pack("V", 0x08);
// Convert the URL type and to a null terminated wchar string
$url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
$url = $url . "\0\0\0";
// Pack the length of the URL as chars (not wchars)
$url_len = pack("V", floor(strlen($url)/2));
// Calculate the data length
$length = 0x24 + strlen($url);
// Pack the header data
$header = pack("vv", $record, $length);
$data = pack("vvvv", $row1, $row2, $col1, $col2);
// Write the packed data
$this->_append($header. $data.
$unknown1. $options.
$url_len. $url);
return($str_error);
}
/**
* Write links to external directory names such as 'c:\foo.xls',
* c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'.
*
* Note: Excel writes some relative links with the $dir_long string. We ignore
* these cases for the sake of simpler code.
*
* @access private
* @see writeUrl()
* @param integer $row1 Start row
* @param integer $col1 Start column
* @param integer $row2 End row
* @param integer $col2 End column
* @param string $url URL string
* @param string $str Alternative label
* @param mixed $format The cell format
* @return integer
*/
function _writeUrlExternal($row1, $col1, $row2, $col2, $url, $str, $format = 0)
{
// Network drives are different. We will handle them separately
// MS/Novell network drives and shares start with \\
if (preg_match('[^external:\\\\]', $url)) {
return; //($this->_writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format));
}
$record = 0x01B8; // Record identifier
$length = 0x00000; // Bytes to follow
if ($format == 0) {
$format = $this->_url_format;
}
// Strip URL type and change Unix dir separator to Dos style (if needed)
//
$url = preg_replace('[^external:]', '', $url);
$url = preg_replace('[/]', "\\", $url);
// Write the visible label
if ($str == '') {
$str = preg_replace('[\#]', ' - ', $url);
}
$str_error = $this->writeString($row1, $col1, $str, $format);
if (($str_error == -2) or ($str_error == -3)) {
return $str_error;
}
// Determine if the link is relative or absolute:
// relative if link contains no dir separator, "somefile.xls"
// relative if link starts with up-dir, "..\..\somefile.xls"
// otherwise, absolute
$absolute = 0x02; // Bit mask
if (!preg_match('[\\]', $url)) {
$absolute = 0x00;
}
if (preg_match('[^\.\.\\]', $url)) {
$absolute = 0x00;
}
// Determine if the link contains a sheet reference and change some of the
// parameters accordingly.
// Split the dir name and sheet name (if it exists)
list($dir_long , $sheet) = split('/\#/', $url);
$link_type = 0x01 | $absolute;
if (isset($sheet)) {
$link_type |= 0x08;
$sheet_len = pack("V", strlen($sheet) + 0x01);
$sheet = join("\0", split('', $sheet));
$sheet .= "\0\0\0";
}
else {
$sheet_len = '';
$sheet = '';
}
// Pack the link type
$link_type = pack("V", $link_type);
// Calculate the up-level dir count e.g.. (..\..\..\ == 3)
$up_count = preg_match_all("/\.\.\\/", $dir_long, $useless);
$up_count = pack("v", $up_count);
// Store the short dos dir name (null terminated)
$dir_short = preg_replace('/\.\.\\/', '', $dir_long) . "\0";
// Store the long dir name as a wchar string (non-null terminated)
$dir_long = join("\0", split('', $dir_long));
$dir_long = $dir_long . "\0";
// Pack the lengths of the dir strings
$dir_short_len = pack("V", strlen($dir_short) );
$dir_long_len = pack("V", strlen($dir_long) );
$stream_len = pack("V", strlen($dir_long) + 0x06);
// Pack the undocumented parts of the hyperlink stream
$unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000' );
$unknown2 = pack("H*",'0303000000000000C000000000000046' );
$unknown3 = pack("H*",'FFFFADDE000000000000000000000000000000000000000');
$unknown4 = pack("v", 0x03 );
// Pack the main data stream
$data = pack("vvvv", $row1, $row2, $col1, $col2) .
$unknown1 .
$link_type .
$unknown2 .
$up_count .
$dir_short_len.
$dir_short .
$unknown3 .
$stream_len .
$dir_long_len .
$unknown4 .
$dir_long .
$sheet_len .
$sheet ;
// Pack the header data
$length = strlen($data);
$header = pack("vv", $record, $length);
// Write the packed data
$this->_append($header. $data);
return($str_error);
}
/**
* This method is used to set the height and format for a row.
*
* @access public
* @param integer $row The row to set
* @param integer $height Height we are giving to the row.
* Use NULL to set XF without setting height
* @param mixed $format XF format we are giving to the row
*/
function setRow($row, $height, $format = 0)
{
$record = 0x0208; // Record identifier
$length = 0x0010; // Number of bytes to follow
$colMic = 0x0000; // First defined column
$colMac = 0x0000; // Last defined column
$irwMac = 0x0000; // Used by Excel to optimise loading
$reserved = 0x0000; // Reserved
$grbit = 0x01C0; // Option flags. (monkey) see $1 do
$ixfe = $this->_XF($format); // XF index
// Use setRow($row, NULL, $XF) to set XF format without setting height
if ($height != NULL) {
$miyRw = $height * 20; // row height
}
else {
$miyRw = 0xff; // default row height is 256
}
$header = pack("vv", $record, $length);
$data = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw,
$irwMac,$reserved, $grbit, $ixfe);
$this->_append($header.$data);
}
/**
* Writes Excel DIMENSIONS to define the area in which there is data.
*
* @access private
*/
function storeDimensions()
{
$record = 0x0000; // Record identifier
$length = 0x000A; // Number of bytes to follow
$row_min = $this->_dim_rowmin; // First row
$row_max = $this->_dim_rowmax; // Last row plus 1
$col_min = $this->_dim_colmin; // First column
$col_max = $this->_dim_colmax; // Last column plus 1
$reserved = 0x0000; // Reserved by Excel
$header = pack("vv", $record, $length);
$data = pack("vvvvv", $row_min, $row_max,
$col_min, $col_max, $reserved);
$this->_prepend($header.$data);
}
/**
* Write BIFF record Window2.
*
* @access private
*/
function _storeWindow2()
{
$record = 0x023E; // Record identifier
$length = 0x000A; // Number of bytes to follow
$grbit = 0x00B6; // Option flags
$rwTop = 0x0000; // Top row visible in window
$colLeft = 0x0000; // Leftmost column visible in window
$rgbHdr = 0x00000000; // Row/column heading and gridline color
// The options flags that comprise $grbit
$fDspFmla = 0; // 0 - bit
$fDspGrid = 1; // 1
$fDspRwCol = 1; // 2
$fFrozen = $this->_frozen; // 3
$fDspZeros = 1; // 4
$fDefaultHdr = 1; // 5
$fArabic = 0; // 6
$fDspGuts = 1; // 7
$fFrozenNoSplit = 0; // 0 - bit
$fSelected = $this->selected; // 1
$fPaged = 1; // 2
$grbit = $fDspFmla;
$grbit |= $fDspGrid << 1;
$grbit |= $fDspRwCol << 2;
$grbit |= $fFrozen << 3;
$grbit |= $fDspZeros << 4;
$grbit |= $fDefaultHdr << 5;
$grbit |= $fArabic << 6;
$grbit |= $fDspGuts << 7;
$grbit |= $fFrozenNoSplit << 8;
$grbit |= $fSelected << 9;
$grbit |= $fPaged << 10;
$header = pack("vv", $record, $length);
$data = pack("vvvV", $grbit, $rwTop, $colLeft, $rgbHdr);
$this->_append($header.$data);
}
/**
* Write BIFF record DEFCOLWIDTH if COLINFO records are in use.
*
* @access private
*/
function _storeDefcol()
{
$record = 0x0055; // Record identifier
$length = 0x0002; // Number of bytes to follow
$colwidth = 0x0008; // Default column width
$header = pack("vv", $record, $length);
$data = pack("v", $colwidth);
$this->_prepend($header.$data);
}
/**
* Write BIFF record COLINFO to define column widths
*
* Note: The SDK says the record length is 0x0B but Excel writes a 0x0C
* length record.
*
* @access private
* @param array $col_array This is the only parameter received and is composed of the following:
* 0 => First formatted column,
* 1 => Last formatted column,
* 2 => Col width (8.43 is Excel default),
* 3 => The optional XF format of the column,
* 4 => Option flags.
*/
function _storeColinfo($col_array)
{
if (isset($col_array[0])) {
$colFirst = $col_array[0];
}
if (isset($col_array[1])) {
$colLast = $col_array[1];
}
if (isset($col_array[2])) {
$coldx = $col_array[2];
}
else {
$coldx = 8.43;
}
if (isset($col_array[3])) {
$format = $col_array[3];
}
else {
$format = 0;
}
if (isset($col_array[4])) {
$grbit = $col_array[4];
}
else {
$grbit = 0;
}
$record = 0x007D; // Record identifier
$length = 0x000B; // Number of bytes to follow
$coldx += 0.72; // Fudge. Excel subtracts 0.72 !?
$coldx *= 256; // Convert to units of 1/256 of a char
$ixfe = $this->_XF($format);
$reserved = 0x00; // Reserved
$header = pack("vv", $record, $length);
$data = pack("vvvvvC", $colFirst, $colLast, $coldx,
$ixfe, $grbit, $reserved);
$this->_prepend($header.$data);
}
/**
* Write BIFF record SELECTION.
*
* @access private
* @param array $array array containing ($rwFirst,$colFirst,$rwLast,$colLast)
* @see setSelection()
*/
function _storeSelection($array)
{
list($rwFirst,$colFirst,$rwLast,$colLast) = $array;
$record = 0x001D; // Record identifier
$length = 0x000F; // Number of bytes to follow
$pnn = $this->_active_pane; // Pane position
$rwAct = $rwFirst; // Active row
$colAct = $colFirst; // Active column
$irefAct = 0; // Active cell ref
$cref = 1; // Number of refs
if (!isset($rwLast)) {
$rwLast = $rwFirst; // Last row in reference
}
if (!isset($colLast)) {
$colLast = $colFirst; // Last col in reference
}
// Swap last row/col for first row/col as necessary
if ($rwFirst > $rwLast)
{
list($rwFirst, $rwLast) = array($rwLast, $rwFirst);
}
if ($colFirst > $colLast)
{
list($colFirst, $colLast) = array($colLast, $colFirst);
}
$header = pack("vv", $record, $length);
$data = pack("CvvvvvvCC", $pnn, $rwAct, $colAct,
$irefAct, $cref,
$rwFirst, $rwLast,
$colFirst, $colLast);
$this->_append($header.$data);
}
/**
* Write BIFF record EXTERNCOUNT to indicate the number of external sheet
* references in a worksheet.
*
* Excel only stores references to external sheets that are used in formulas.
* For simplicity we store references to all the sheets in the workbook
* regardless of whether they are used or not. This reduces the overall
* complexity and eliminates the need for a two way dialogue between the formula
* parser the worksheet objects.
*
* @access private
* @param integer $count The number of external sheet references in this worksheet
*/
function _storeExterncount($count)
{
$record = 0x0016; // Record identifier
$length = 0x0002; // Number of bytes to follow
$header = pack("vv", $record, $length);
$data = pack("v", $count);
$this->_prepend($header.$data);
}
/**
* Writes the Excel BIFF EXTERNSHEET record. These references are used by
* formulas. A formula references a sheet name via an index. Since we store a
* reference to all of the external worksheets the EXTERNSHEET index is the same
* as the worksheet index.
*
* @access private
* @param string $sheetname The name of a external worksheet
*/
function _storeExternsheet($sheetname)
{
$record = 0x0017; // Record identifier
// References to the current sheet are encoded differently to references to
// external sheets.
//
if ($this->name == $sheetname) {
$sheetname = '';
$length = 0x02; // The following 2 bytes
$cch = 1; // The following byte
$rgch = 0x02; // Self reference
}
else {
$length = 0x02 + strlen($sheetname);
$cch = strlen($sheetname);
$rgch = 0x03; // Reference to a sheet in the current workbook
}
$header = pack("vv", $record, $length);
$data = pack("CC", $cch, $rgch);
$this->_prepend($header.$data.$sheetname);
}
/**
* Writes the Excel BIFF PANE record.
* The panes can either be frozen or thawed (unfrozen).
* Frozen panes are specified in terms of an integer number of rows and columns.
* Thawed panes are specified in terms of Excel's units for rows and columns.
*
* @access private
* @param array $panes This is the only parameter received and is composed of the following:
* 0 => Vertical split position,
* 1 => Horizontal split position
* 2 => Top row visible
* 3 => Leftmost column visible
* 4 => Active pane
*/
function _storePanes($panes)
{
$y = $panes[0];
$x = $panes[1];
$rwTop = $panes[2];
$colLeft = $panes[3];
if (count($panes) > 4) { // if Active pane was received
$pnnAct = $panes[4];
}
else {
$pnnAct = NULL;
}
$record = 0x0041; // Record identifier
$length = 0x000A; // Number of bytes to follow
// Code specific to frozen or thawed panes.
if ($this->_frozen)
{
// Set default values for $rwTop and $colLeft
if (!isset($rwTop)) {
$rwTop = $y;
}
if (!isset($colLeft)) {
$colLeft = $x;
}
}
else
{
// Set default values for $rwTop and $colLeft
if (!isset($rwTop)) {
$rwTop = 0;
}
if (!isset($colLeft)) {
$colLeft = 0;
}
// Convert Excel's row and column units to the internal units.
// The default row height is 12.75
// The default column width is 8.43
// The following slope and intersection values were interpolated.
//
$y = 20*$y + 255;
$x = 113.879*$x + 390;
}
// Determine which pane should be active. There is also the undocumented
// option to override this should it be necessary: may be removed later.
//
if (!isset($pnnAct))
{
if ($x != 0 and $y != 0)
$pnnAct = 0; // Bottom right
if ($x != 0 and $y == 0)
$pnnAct = 1; // Top right
if ($x == 0 and $y != 0)
$pnnAct = 2; // Bottom left
if ($x == 0 and $y == 0)
$pnnAct = 3; // Top left
}
$this->_active_pane = $pnnAct; // Used in _storeSelection
$header = pack("vv", $record, $length);
$data = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct);
$this->_append($header.$data);
}
/**
* Store the page setup SETUP BIFF record.
*
* @access private
*/
function _storeSetup()
{
$record = 0x00A1; // Record identifier
$length = 0x0022; // Number of bytes to follow
$iPaperSize = $this->_paper_size; // Paper size
$iScale = $this->_print_scale; // Print scaling factor
$iPageStart = 0x01; // Starting page number
$iFitWidth = $this->_fit_width; // Fit to number of pages wide
$iFitHeight = $this->_fit_height; // Fit to number of pages high
$grbit = 0x00; // Option flags
$iRes = 0x0258; // Print resolution
$iVRes = 0x0258; // Vertical print resolution
$numHdr = $this->_margin_head; // Header Margin
$numFtr = $this->_margin_foot; // Footer Margin
$iCopies = 0x01; // Number of copies
$fLeftToRight = 0x0; // Print over then down
$fLandscape = $this->_orientation; // Page orientation
$fNoPls = 0x0; // Setup not read from printer
$fNoColor = 0x0; // Print black and white
$fDraft = 0x0; // Print draft quality
$fNotes = 0x0; // Print notes
$fNoOrient = 0x0; // Orientation not set
$fUsePage = 0x0; // Use custom starting page
$grbit = $fLeftToRight;
$grbit |= $fLandscape << 1;
$grbit |= $fNoPls << 2;
$grbit |= $fNoColor << 3;
$grbit |= $fDraft << 4;
$grbit |= $fNotes << 5;
$grbit |= $fNoOrient << 6;
$grbit |= $fUsePage << 7;
$numHdr = pack("d", $numHdr);
$numFtr = pack("d", $numFtr);
if ($this->_byte_order) // if it's Big Endian
{
$numHdr = strrev($numHdr);
$numFtr = strrev($numFtr);
}
$header = pack("vv", $record, $length);
$data1 = pack("vvvvvvvv", $iPaperSize,
$iScale,
$iPageStart,
$iFitWidth,
$iFitHeight,
$grbit,
$iRes,
$iVRes);
$data2 = $numHdr.$numFtr;
$data3 = pack("v", $iCopies);
$this->_prepend($header.$data1.$data2.$data3);
}
/**
* Store the header caption BIFF record.
*
* @access private
*/
function _storeHeader()
{
$record = 0x0014; // Record identifier
$str = $this->_header; // header string
$cch = strlen($str); // Length of header string
$length = 1 + $cch; // Bytes to follow
$header = pack("vv", $record, $length);
$data = pack("C", $cch);
$this->_append($header.$data.$str);
}
/**
* Store the footer caption BIFF record.
*
* @access private
*/
function _storeFooter()
{
$record = 0x0015; // Record identifier
$str = $this->_footer; // Footer string
$cch = strlen($str); // Length of footer string
$length = 1 + $cch; // Bytes to follow
$header = pack("vv", $record, $length);
$data = pack("C", $cch);
$this->_append($header.$data.$str);
}
/**
* Store the horizontal centering HCENTER BIFF record.
*
* @access private
*/
function _storeHcenter()
{
$record = 0x0083; // Record identifier
$length = 0x0002; // Bytes to follow
$fHCenter = $this->_hcenter; // Horizontal centering
$header = pack("vv", $record, $length);
$data = pack("v", $fHCenter);
$this->_append($header.$data);
}
/**
* Store the vertical centering VCENTER BIFF record.
*
* @access private
*/
function _storeVcenter()
{
$record = 0x0084; // Record identifier
$length = 0x0002; // Bytes to follow
$fVCenter = $this->_vcenter; // Horizontal centering
$header = pack("vv", $record, $length);
$data = pack("v", $fVCenter);
$this->_append($header.$data);
}
/**
* Store the LEFTMARGIN BIFF record.
*
* @access private
*/
function _storeMarginLeft()
{
$record = 0x0026; // Record identifier
$length = 0x0008; // Bytes to follow
$margin = $this->_margin_left; // Margin in inches
$header = pack("vv", $record, $length);
$data = pack("d", $margin);
if ($this->_byte_order) // if it's Big Endian
{
$data = strrev($data);
}
$this->_append($header.$data);
}
/**
* Store the RIGHTMARGIN BIFF record.
*
* @access private
*/
function _storeMarginRight()
{
$record = 0x0027; // Record identifier
$length = 0x0008; // Bytes to follow
$margin = $this->_margin_right; // Margin in inches
$header = pack("vv", $record, $length);
$data = pack("d", $margin);
if ($this->_byte_order) // if it's Big Endian
{
$data = strrev($data);
}
$this->_append($header.$data);
}
/**
* Store the TOPMARGIN BIFF record.
*
* @access private
*/
function _storeMarginTop()
{
$record = 0x0028; // Record identifier
$length = 0x0008; // Bytes to follow
$margin = $this->_margin_top; // Margin in inches
$header = pack("vv", $record, $length);
$data = pack("d", $margin);
if ($this->_byte_order) // if it's Big Endian
{
$data = strrev($data);
}
$this->_append($header.$data);
}
/**
* Store the BOTTOMMARGIN BIFF record.
*
* @access private
*/
function _storeMarginBottom()
{
$record = 0x0029; // Record identifier
$length = 0x0008; // Bytes to follow
$margin = $this->_margin_bottom; // Margin in inches
$header = pack("vv", $record, $length);
$data = pack("d", $margin);
if ($this->_byte_order) // if it's Big Endian
{
$data = strrev($data);
}
$this->_append($header.$data);
}
 
/**
* Merges the area given by its arguments.
* This is an Excel97/2000 method. It is required to perform more complicated
* merging than the normal setAlign('merge').
*
* @access public
* @param integer $first_row First row of the area to merge
* @param integer $first_col First column of the area to merge
* @param integer $last_row Last row of the area to merge
* @param integer $last_col Last column of the area to merge
*/
function mergeCells($first_row, $first_col, $last_row, $last_col)
{
$record = 0x00E5; // Record identifier
$length = 0x000A; // Bytes to follow
$cref = 1; // Number of refs
 
// Swap last row/col for first row/col as necessary
if ($first_row > $last_row) {
list($first_row, $last_row) = array($last_row, $first_row);
}
if ($first_col > $last_col) {
list($first_col, $last_col) = array($last_col, $first_col);
}
$header = pack("vv", $record, $length);
$data = pack("vvvvv", $cref, $first_row, $last_row,
$first_col, $last_col);
$this->_append($header.$data);
}
 
/**
* Write the PRINTHEADERS BIFF record.
*
* @access private
*/
function _storePrintHeaders()
{
$record = 0x002a; // Record identifier
$length = 0x0002; // Bytes to follow
$fPrintRwCol = $this->_print_headers; // Boolean flag
$header = pack("vv", $record, $length);
$data = pack("v", $fPrintRwCol);
$this->_prepend($header.$data);
}
/**
* Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the
* GRIDSET record.
*
* @access private
*/
function _storePrintGridlines()
{
$record = 0x002b; // Record identifier
$length = 0x0002; // Bytes to follow
$fPrintGrid = $this->_print_gridlines; // Boolean flag
$header = pack("vv", $record, $length);
$data = pack("v", $fPrintGrid);
$this->_prepend($header.$data);
}
/**
* Write the GRIDSET BIFF record. Must be used in conjunction with the
* PRINTGRIDLINES record.
*
* @access private
*/
function _storeGridset()
{
$record = 0x0082; // Record identifier
$length = 0x0002; // Bytes to follow
$fGridSet = !($this->_print_gridlines); // Boolean flag
$header = pack("vv", $record, $length);
$data = pack("v", $fGridSet);
$this->_prepend($header.$data);
}
/**
* Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction
* with the SETUP record.
*
* @access private
*/
function _storeWsbool()
{
$record = 0x0081; // Record identifier
$length = 0x0002; // Bytes to follow
// The only option that is of interest is the flag for fit to page. So we
// set all the options in one go.
//
if ($this->_fit_page) {
$grbit = 0x05c1;
}
else {
$grbit = 0x04c1;
}
$header = pack("vv", $record, $length);
$data = pack("v", $grbit);
$this->_prepend($header.$data);
}
/**
* Write the HORIZONTALPAGEBREAKS BIFF record.
*
* @access private
*/
function _storeHbreak()
{
// Return if the user hasn't specified pagebreaks
if (empty($this->_hbreaks)) {
return;
}
// Sort and filter array of page breaks
$breaks = $this->_hbreaks;
sort($breaks,SORT_NUMERIC);
if ($breaks[0] == 0) { // don't use first break if it's 0
array_shift($breaks);
}
$record = 0x001b; // Record identifier
$cbrk = count($breaks); // Number of page breaks
$length = ($cbrk + 1) * 2; // Bytes to follow
$header = pack("vv", $record, $length);
$data = pack("v", $cbrk);
// Append each page break
foreach($breaks as $break) {
$data .= pack("v", $break);
}
$this->_prepend($header.$data);
}
/**
* Write the VERTICALPAGEBREAKS BIFF record.
*
* @access private
*/
function _storeVbreak()
{
// Return if the user hasn't specified pagebreaks
if (empty($this->_vbreaks)) {
return;
}
// 1000 vertical pagebreaks appears to be an internal Excel 5 limit.
// It is slightly higher in Excel 97/200, approx. 1026
$breaks = array_slice($this->_vbreaks,0,1000);
// Sort and filter array of page breaks
sort($breaks,SORT_NUMERIC);
if ($breaks[0] == 0) { // don't use first break if it's 0
array_shift($breaks);
}
$record = 0x001a; // Record identifier
$cbrk = count($breaks); // Number of page breaks
$length = ($cbrk + 1) * 2; // Bytes to follow
$header = pack("vv", $record, $length);
$data = pack("v", $cbrk);
// Append each page break
foreach ($breaks as $break) {
$data .= pack("v", $break);
}
$this->_prepend($header.$data);
}
/**
* Set the Biff PROTECT record to indicate that the worksheet is protected.
*
* @access private
*/
function _storeProtect()
{
// Exit unless sheet protection has been specified
if ($this->_protect == 0) {
return;
}
$record = 0x0012; // Record identifier
$length = 0x0002; // Bytes to follow
$fLock = $this->_protect; // Worksheet is protected
$header = pack("vv", $record, $length);
$data = pack("v", $fLock);
$this->_prepend($header.$data);
}
/**
* Write the worksheet PASSWORD record.
*
* @access private
*/
function _storePassword()
{
// Exit unless sheet protection and password have been specified
if (($this->_protect == 0) or (!isset($this->_password))) {
return;
}
$record = 0x0013; // Record identifier
$length = 0x0002; // Bytes to follow
$wPassword = $this->_password; // Encoded password
$header = pack("vv", $record, $length);
$data = pack("v", $wPassword);
$this->_prepend($header.$data);
}
 
/**
* Insert a 24bit bitmap image in a worksheet.
*
* @access public
* @param integer $row The row we are going to insert the bitmap into
* @param integer $col The column we are going to insert the bitmap into
* @param string $bitmap The bitmap filename
* @param integer $x The horizontal position (offset) of the image inside the cell.
* @param integer $y The vertical position (offset) of the image inside the cell.
* @param integer $scale_x The horizontal scale
* @param integer $scale_y The vertical scale
*/
function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1)
{
$bitmap_array = $this->_processBitmap($bitmap);
if ($this->isError($bitmap_array))
{
$this->writeString($row, $col, $bitmap_array->getMessage());
return;
}
list($width, $height, $size, $data) = $bitmap_array; //$this->_processBitmap($bitmap);
// Scale the frame of the image.
$width *= $scale_x;
$height *= $scale_y;
// Calculate the vertices of the image and write the OBJ record
$this->_positionImage($col, $row, $x, $y, $width, $height);
// Write the IMDATA record to store the bitmap data
$record = 0x007f;
$length = 8 + $size;
$cf = 0x09;
$env = 0x01;
$lcb = $size;
$header = pack("vvvvV", $record, $length, $cf, $env, $lcb);
$this->_append($header.$data);
}
/**
* Calculate the vertices that define the position of the image as required by
* the OBJ record.
*
* +------------+------------+
* | A | B |
* +-----+------------+------------+
* | |(x1,y1) | |
* | 1 |(A1)._______|______ |
* | | | | |
* | | | | |
* +-----+----| BITMAP |-----+
* | | | | |
* | 2 | |______________. |
* | | | (B2)|
* | | | (x2,y2)|
* +---- +------------+------------+
*
* Example of a bitmap that covers some of the area from cell A1 to cell B2.
*
* Based on the width and height of the bitmap we need to calculate 8 vars:
* $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
* The width and height of the cells are also variable and have to be taken into
* account.
* The values of $col_start and $row_start are passed in from the calling
* function. The values of $col_end and $row_end are calculated by subtracting
* the width and height of the bitmap from the width and height of the
* underlying cells.
* The vertices are expressed as a percentage of the underlying cell width as
* follows (rhs values are in pixels):
*
* x1 = X / W *1024
* y1 = Y / H *256
* x2 = (X-1) / W *1024
* y2 = (Y-1) / H *256
*
* Where: X is distance from the left side of the underlying cell
* Y is distance from the top of the underlying cell
* W is the width of the cell
* H is the height of the cell
*
* @access private
* @note the SDK incorrectly states that the height should be expressed as a
* percentage of 1024.
* @param integer $col_start Col containing upper left corner of object
* @param integer $row_start Row containing top left corner of object
* @param integer $x1 Distance to left side of object
* @param integer $y1 Distance to top of object
* @param integer $width Width of image frame
* @param integer $height Height of image frame
*/
function _positionImage($col_start, $row_start, $x1, $y1, $width, $height)
{
// Initialise end cell to the same as the start cell
$col_end = $col_start; // Col containing lower right corner of object
$row_end = $row_start; // Row containing bottom right corner of object
// Zero the specified offset if greater than the cell dimensions
if ($x1 >= $this->_sizeCol($col_start))
{
$x1 = 0;
}
if ($y1 >= $this->_sizeRow($row_start))
{
$y1 = 0;
}
$width = $width + $x1 -1;
$height = $height + $y1 -1;
// Subtract the underlying cell widths to find the end cell of the image
while ($width >= $this->_sizeCol($col_end)) {
$width -= $this->_sizeCol($col_end);
$col_end++;
}
// Subtract the underlying cell heights to find the end cell of the image
while ($height >= $this->_sizeRow($row_end)) {
$height -= $this->_sizeRow($row_end);
$row_end++;
}
// Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
// with zero eight or width.
//
if ($this->_sizeCol($col_start) == 0)
return;
if ($this->_sizeCol($col_end) == 0)
return;
if ($this->_sizeRow($row_start) == 0)
return;
if ($this->_sizeRow($row_end) == 0)
return;
// Convert the pixel values to the percentage value expected by Excel
$x1 = $x1 / $this->_sizeCol($col_start) * 1024;
$y1 = $y1 / $this->_sizeRow($row_start) * 256;
$x2 = $width / $this->_sizeCol($col_end) * 1024; // Distance to right side of object
$y2 = $height / $this->_sizeRow($row_end) * 256; // Distance to bottom of object
$this->_storeObjPicture( $col_start, $x1,
$row_start, $y1,
$col_end, $x2,
$row_end, $y2
);
}
/**
* Convert the width of a cell from user's units to pixels. By interpolation
* the relationship is: y = 7x +5. If the width hasn't been set by the user we
* use the default value. If the col is hidden we use a value of zero.
*
* @access private
* @param integer $col The column
* @return integer The width in pixels
*/
function _sizeCol($col)
{
// Look up the cell value to see if it has been changed
if (isset($this->col_sizes[$col])) {
if ($this->col_sizes[$col] == 0) {
return(0);
}
else {
return(floor(7 * $this->col_sizes[$col] + 5));
}
}
else {
return(64);
}
}
/**
* Convert the height of a cell from user's units to pixels. By interpolation
* the relationship is: y = 4/3x. If the height hasn't been set by the user we
* use the default value. If the row is hidden we use a value of zero. (Not
* possible to hide row yet).
*
* @access private
* @param integer $row The row
* @return integer The width in pixels
*/
function _sizeRow($row)
{
// Look up the cell value to see if it has been changed
if (isset($this->row_sizes[$row])) {
if ($this->row_sizes[$row] == 0) {
return(0);
}
else {
return(floor(4/3 * $this->row_sizes[$row]));
}
}
else {
return(17);
}
}
/**
* Store the OBJ record that precedes an IMDATA record. This could be generalise
* to support other Excel objects.
*
* @access private
* @param integer $colL Column containing upper left corner of object
* @param integer $dxL Distance from left side of cell
* @param integer $rwT Row containing top left corner of object
* @param integer $dyT Distance from top of cell
* @param integer $colR Column containing lower right corner of object
* @param integer $dxR Distance from right of cell
* @param integer $rwB Row containing bottom right corner of object
* @param integer $dyB Distance from bottom of cell
*/
function _storeObjPicture($colL,$dxL,$rwT,$dyT,$colR,$dxR,$rwB,$dyB)
{
$record = 0x005d; // Record identifier
$length = 0x003c; // Bytes to follow
$cObj = 0x0001; // Count of objects in file (set to 1)
$OT = 0x0008; // Object type. 8 = Picture
$id = 0x0001; // Object ID
$grbit = 0x0614; // Option flags
$cbMacro = 0x0000; // Length of FMLA structure
$Reserved1 = 0x0000; // Reserved
$Reserved2 = 0x0000; // Reserved
$icvBack = 0x09; // Background colour
$icvFore = 0x09; // Foreground colour
$fls = 0x00; // Fill pattern
$fAuto = 0x00; // Automatic fill
$icv = 0x08; // Line colour
$lns = 0xff; // Line style
$lnw = 0x01; // Line weight
$fAutoB = 0x00; // Automatic border
$frs = 0x0000; // Frame style
$cf = 0x0009; // Image format, 9 = bitmap
$Reserved3 = 0x0000; // Reserved
$cbPictFmla = 0x0000; // Length of FMLA structure
$Reserved4 = 0x0000; // Reserved
$grbit2 = 0x0001; // Option flags
$Reserved5 = 0x0000; // Reserved
$header = pack("vv", $record, $length);
$data = pack("V", $cObj);
$data .= pack("v", $OT);
$data .= pack("v", $id);
$data .= pack("v", $grbit);
$data .= pack("v", $colL);
$data .= pack("v", $dxL);
$data .= pack("v", $rwT);
$data .= pack("v", $dyT);
$data .= pack("v", $colR);
$data .= pack("v", $dxR);
$data .= pack("v", $rwB);
$data .= pack("v", $dyB);
$data .= pack("v", $cbMacro);
$data .= pack("V", $Reserved1);
$data .= pack("v", $Reserved2);
$data .= pack("C", $icvBack);
$data .= pack("C", $icvFore);
$data .= pack("C", $fls);
$data .= pack("C", $fAuto);
$data .= pack("C", $icv);
$data .= pack("C", $lns);
$data .= pack("C", $lnw);
$data .= pack("C", $fAutoB);
$data .= pack("v", $frs);
$data .= pack("V", $cf);
$data .= pack("v", $Reserved3);
$data .= pack("v", $cbPictFmla);
$data .= pack("v", $Reserved4);
$data .= pack("v", $grbit2);
$data .= pack("V", $Reserved5);
$this->_append($header.$data);
}
/**
* Convert a 24 bit bitmap into the modified internal format used by Windows.
* This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the
* MSDN library.
*
* @access private
* @param string $bitmap The bitmap to process
* @return array Array with data and properties of the bitmap
*/
function _processBitmap($bitmap)
{
// Open file.
$bmp_fd = @fopen($bitmap,"rb");
if (!$bmp_fd) {
return($this->raiseError("Couldn't import $bitmap"));
}
// Slurp the file into a string.
$data = fread($bmp_fd, filesize($bitmap));
// Check that the file is big enough to be a bitmap.
if (strlen($data) <= 0x36) {
return($this->raiseError("$bitmap doesn't contain enough data.\n"));
}
// The first 2 bytes are used to identify the bitmap.
$identity = unpack("A2", $data);
if ($identity[''] != "BM") {
return($this->raiseError("$bitmap doesn't appear to be a valid bitmap image.\n"));
}
// Remove bitmap data: ID.
$data = substr($data, 2);
// Read and remove the bitmap size. This is more reliable than reading
// the data size at offset 0x22.
//
$size_array = unpack("V", substr($data, 0, 4));
$size = $size_array[''];
$data = substr($data, 4);
$size -= 0x36; // Subtract size of bitmap header.
$size += 0x0C; // Add size of BIFF header.
// Remove bitmap data: reserved, offset, header length.
$data = substr($data, 12);
// Read and remove the bitmap width and height. Verify the sizes.
$width_and_height = unpack("V2", substr($data, 0, 8));
$width = $width_and_height[1];
$height = $width_and_height[2];
$data = substr($data, 8);
if ($width > 0xFFFF) {
return($this->raiseError("$bitmap: largest image width supported is 65k.\n"));
}
if ($height > 0xFFFF) {
return($this->raiseError("$bitmap: largest image height supported is 65k.\n"));
}
// Read and remove the bitmap planes and bpp data. Verify them.
$planes_and_bitcount = unpack("v2", substr($data, 0, 4));
$data = substr($data, 4);
if ($planes_and_bitcount[2] != 24) { // Bitcount
return($this->raiseError("$bitmap isn't a 24bit true color bitmap.\n"));
}
if ($planes_and_bitcount[1] != 1) {
return($this->raiseError("$bitmap: only 1 plane nupported in bitmap image.\n"));
}
// Read and remove the bitmap compression. Verify compression.
$compression = unpack("V", substr($data, 0, 4));
$data = substr($data, 4);
//$compression = 0;
if ($compression[""] != 0) {
return($this->raiseError("$bitmap: compression not supported in bitmap image.\n"));
}
// Remove bitmap data: data size, hres, vres, colours, imp. colours.
$data = substr($data, 20);
// Add the BITMAPCOREHEADER data
$header = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18);
$data = $header . $data;
return (array($width, $height, $size, $data));
}
/**
* Store the window zoom factor. This should be a reduced fraction but for
* simplicity we will store all fractions with a numerator of 100.
*
* @access private
*/
function _storeZoom()
{
// If scale is 100 we don't need to write a record
if ($this->_zoom == 100) {
return;
}
$record = 0x00A0; // Record identifier
$length = 0x0004; // Bytes to follow
$header = pack("vv", $record, $length);
$data = pack("vv", $this->_zoom, 100);
$this->_append($header.$data);
}
}
?>
/trunk/jrest/lib/Spreadsheet/Excel/Writer/OLEwriter.php
New file
0,0 → 1,428
<?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_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 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('PEAR.php');
 
/**
* Class for creating OLE streams for Excel Spreadsheets
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer_OLEwriter extends PEAR
{
/**
* Filename for the OLE stream
* @var string
* @see _initialize()
*/
var $_OLEfilename;
 
/**
* Filehandle for the OLE stream
* @var resource
*/
var $_filehandle;
 
/**
* Name of the temporal file in case OLE stream goes to stdout
* @var string
*/
var $_tmp_filename;
 
/**
* Variable for preventing closing two times
* @var integer
*/
var $_fileclosed;
 
/**
* Size of the data to be written to the OLE stream
* @var integer
*/
var $_biffsize;
 
/**
* Real data size to be written to the OLE stream
* @var integer
*/
var $_booksize;
 
/**
* Number of big blocks in the OLE stream
* @var integer
*/
var $_big_blocks;
 
/**
* Number of list blocks in the OLE stream
* @var integer
*/
var $_list_blocks;
 
/**
* Number of big blocks in the OLE stream
* @var integer
*/
var $_root_start;
 
/**
* Constructor for the OLEwriter class
*
* @param string $OLEfilename the name of the file for the OLE stream
*/
function Spreadsheet_Excel_Writer_OLEwriter($OLEfilename)
{
$this->_OLEfilename = $OLEfilename;
$this->_filehandle = "";
$this->_tmp_filename = "";
$this->_fileclosed = 0;
$this->_biff_only = 0;
//$this->_size_allowed = 0;
$this->_biffsize = 0;
$this->_booksize = 0;
$this->_big_blocks = 0;
$this->_list_blocks = 0;
$this->_root_start = 0;
//$this->_block_count = 4;
$this->_initialize();
}
 
/**
* Check for a valid filename and store the filehandle.
* Filehandle "-" writes to STDOUT
*
* @access private
*/
function _initialize()
{
$OLEfile = $this->_OLEfilename;
if(($OLEfile == '-') or ($OLEfile == ''))
{
$this->_tmp_filename = tempnam("/tmp", "OLEwriter");
$fh = fopen($this->_tmp_filename,"wb");
if ($fh == false) {
$this->raiseError("Can't create temporary file.");
}
}
else
{
// Create a new file, open for writing (in binmode)
$fh = fopen($OLEfile,"wb");
if ($fh == false) {
$this->raiseError("Can't open $OLEfile. It may be in use or protected.");
}
}
// Store filehandle
$this->_filehandle = $fh;
}
 
 
/**
* Set the size of the data to be written to the OLE stream.
* The maximun size comes from this:
* $big_blocks = (109 depot block x (128 -1 marker word)
* - (1 x end words)) = 13842
* $maxsize = $big_blocks * 512 bytes = 7087104
*
* @access public
* @see Spreadsheet_Excel_Writer_Workbook::store_OLE_file()
* @param integer $biffsize The size of the data to be written to the OLE stream
* @return integer 1 for success
*/
function setSize($biffsize)
{
$maxsize = 7087104; // TODO: extend max size
if ($biffsize > $maxsize) {
$this->raiseError("Maximum file size, $maxsize, exceeded.");
}
$this->_biffsize = $biffsize;
// Set the min file size to 4k to avoid having to use small blocks
if ($biffsize > 4096) {
$this->_booksize = $biffsize;
}
else {
$this->_booksize = 4096;
}
//$this->_size_allowed = 1;
return(1);
}
 
 
/**
* Calculate various sizes needed for the OLE stream
*
* @access private
*/
function _calculateSizes()
{
$datasize = $this->_booksize;
if ($datasize % 512 == 0) {
$this->_big_blocks = $datasize/512;
}
else {
$this->_big_blocks = floor($datasize/512) + 1;
}
// There are 127 list blocks and 1 marker blocks for each big block
// depot + 1 end of chain block
$this->_list_blocks = floor(($this->_big_blocks)/127) + 1;
$this->_root_start = $this->_big_blocks;
}
 
/**
* Write root entry, big block list and close the filehandle.
* This routine is used to explicitly close the open filehandle without
* having to wait for DESTROY.
*
* @access public
* @see Spreadsheet_Excel_Writer_Workbook::store_OLE_file()
*/
function close()
{
//return if not $this->{_size_allowed};
$this->_writePadding();
$this->_writePropertyStorage();
$this->_writeBigBlockDepot();
// Close the filehandle
fclose($this->_filehandle);
if(($this->_OLEfilename == '-') or ($this->_OLEfilename == ''))
{
$fh = fopen($this->_tmp_filename, "rb");
if ($fh == false) {
$this->raiseError("Can't read temporary file.");
}
fpassthru($fh);
@unlink($this->_tmp_filename);
}
$this->_fileclosed = 1;
}
 
 
/**
* Write BIFF data to OLE file.
*
* @param string $data string of bytes to be written
*/
function write($data)
{
fwrite($this->_filehandle,$data,strlen($data));
}
 
 
/**
* Write OLE header block.
*/
function writeHeader()
{
$this->_calculateSizes();
$root_start = $this->_root_start;
$num_lists = $this->_list_blocks;
$id = pack("nnnn", 0xD0CF, 0x11E0, 0xA1B1, 0x1AE1);
$unknown1 = pack("VVVV", 0x00, 0x00, 0x00, 0x00);
$unknown2 = pack("vv", 0x3E, 0x03);
$unknown3 = pack("v", -2);
$unknown4 = pack("v", 0x09);
$unknown5 = pack("VVV", 0x06, 0x00, 0x00);
$num_bbd_blocks = pack("V", $num_lists);
$root_startblock = pack("V", $root_start);
$unknown6 = pack("VV", 0x00, 0x1000);
$sbd_startblock = pack("V", -2);
$unknown7 = pack("VVV", 0x00, -2 ,0x00);
$unused = pack("V", -1);
fwrite($this->_filehandle,$id);
fwrite($this->_filehandle,$unknown1);
fwrite($this->_filehandle,$unknown2);
fwrite($this->_filehandle,$unknown3);
fwrite($this->_filehandle,$unknown4);
fwrite($this->_filehandle,$unknown5);
fwrite($this->_filehandle,$num_bbd_blocks);
fwrite($this->_filehandle,$root_startblock);
fwrite($this->_filehandle,$unknown6);
fwrite($this->_filehandle,$sbd_startblock);
fwrite($this->_filehandle,$unknown7);
for($i=1; $i <= $num_lists; $i++)
{
$root_start++;
fwrite($this->_filehandle,pack("V",$root_start));
}
for($i = $num_lists; $i <=108; $i++)
{
fwrite($this->_filehandle,$unused);
}
}
 
 
/**
* Write big block depot.
*
* @access private
*/
function _writeBigBlockDepot()
{
$num_blocks = $this->_big_blocks;
$num_lists = $this->_list_blocks;
$total_blocks = $num_lists *128;
$used_blocks = $num_blocks + $num_lists +2;
$marker = pack("V", -3);
$end_of_chain = pack("V", -2);
$unused = pack("V", -1);
for($i=1; $i < $num_blocks; $i++)
{
fwrite($this->_filehandle,pack("V",$i));
}
fwrite($this->_filehandle,$end_of_chain);
fwrite($this->_filehandle,$end_of_chain);
for($i=0; $i < $num_lists; $i++)
{
fwrite($this->_filehandle,$marker);
}
for($i=$used_blocks; $i <= $total_blocks; $i++)
{
fwrite($this->_filehandle,$unused);
}
}
 
/**
* Write property storage. TODO: add summary sheets
*
* @access private
*/
function _writePropertyStorage()
{
//$rootsize = -2;
/*************** name type dir start size */
$this->_writePps("Root Entry", 0x05, 1, -2, 0x00);
$this->_writePps("Book", 0x02, -1, 0x00, $this->_booksize);
$this->_writePps('', 0x00, -1, 0x00, 0x0000);
$this->_writePps('', 0x00, -1, 0x00, 0x0000);
}
 
/**
* Write property sheet in property storage
*
* @param string $name name of the property storage.
* @param integer $type type of the property storage.
* @param integer $dir dir of the property storage.
* @param integer $start start of the property storage.
* @param integer $size size of the property storage.
* @access private
*/
function _writePps($name,$type,$dir,$start,$size)
{
$length = 0;
$rawname = '';
if ($name != '')
{
$name = $name . "\0";
for($i=0;$i<strlen($name);$i++)
{
// Simulate a Unicode string
$rawname .= pack("H*",dechex(ord($name{$i}))).pack("C",0);
}
$length = strlen($name) * 2;
}
$zero = pack("C", 0);
$pps_sizeofname = pack("v", $length); // 0x40
$pps_type = pack("v", $type); // 0x42
$pps_prev = pack("V", -1); // 0x44
$pps_next = pack("V", -1); // 0x48
$pps_dir = pack("V", $dir); // 0x4c
$unknown1 = pack("V", 0);
$pps_ts1s = pack("V", 0); // 0x64
$pps_ts1d = pack("V", 0); // 0x68
$pps_ts2s = pack("V", 0); // 0x6c
$pps_ts2d = pack("V", 0); // 0x70
$pps_sb = pack("V", $start); // 0x74
$pps_size = pack("V", $size); // 0x78
fwrite($this->_filehandle,$rawname);
for($i=0; $i < (64 -$length); $i++) {
fwrite($this->_filehandle,$zero);
}
fwrite($this->_filehandle,$pps_sizeofname);
fwrite($this->_filehandle,$pps_type);
fwrite($this->_filehandle,$pps_prev);
fwrite($this->_filehandle,$pps_next);
fwrite($this->_filehandle,$pps_dir);
for($i=0; $i < 5; $i++) {
fwrite($this->_filehandle,$unknown1);
}
fwrite($this->_filehandle,$pps_ts1s);
fwrite($this->_filehandle,$pps_ts1d);
fwrite($this->_filehandle,$pps_ts2d);
fwrite($this->_filehandle,$pps_ts2d);
fwrite($this->_filehandle,$pps_sb);
fwrite($this->_filehandle,$pps_size);
fwrite($this->_filehandle,$unknown1);
}
 
/**
* Pad the end of the file
*
* @access private
*/
function _writePadding()
{
$biffsize = $this->_biffsize;
if ($biffsize < 4096) {
$min_size = 4096;
}
else {
$min_size = 512;
}
if ($biffsize % $min_size != 0)
{
$padding = $min_size - ($biffsize % $min_size);
for($i=0; $i < $padding; $i++) {
fwrite($this->_filehandle,"\0");
}
}
}
}
?>
/trunk/jrest/lib/Spreadsheet/Excel/Writer/Parser.php
New file
0,0 → 1,1551
<?php
/**
* Class for parsing Excel formulas
*
* License Information:
*
* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
 
/**
* @const SPREADSHEET_EXCEL_WRITER_ADD token identifier for character "+"
*/
define('SPREADSHEET_EXCEL_WRITER_ADD',"+");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_SUB token identifier for character "-"
*/
define('SPREADSHEET_EXCEL_WRITER_SUB',"-");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_MUL token identifier for character "*"
*/
define('SPREADSHEET_EXCEL_WRITER_MUL',"*");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_DIV token identifier for character "/"
*/
define('SPREADSHEET_EXCEL_WRITER_DIV',"/");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_OPEN token identifier for character "("
*/
define('SPREADSHEET_EXCEL_WRITER_OPEN',"(");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_CLOSE token identifier for character ")"
*/
define('SPREADSHEET_EXCEL_WRITER_CLOSE',")");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_COMA token identifier for character ","
*/
define('SPREADSHEET_EXCEL_WRITER_COMA',",");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_GT token identifier for character ">"
*/
define('SPREADSHEET_EXCEL_WRITER_GT',">");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_LT token identifier for character "<"
*/
define('SPREADSHEET_EXCEL_WRITER_LT',"<");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_LE token identifier for character "<="
*/
define('SPREADSHEET_EXCEL_WRITER_LE',"<=");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_GE token identifier for character ">="
*/
define('SPREADSHEET_EXCEL_WRITER_GE',">=");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_EQ token identifier for character "="
*/
define('SPREADSHEET_EXCEL_WRITER_EQ',"=");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_NE token identifier for character "<>"
*/
define('SPREADSHEET_EXCEL_WRITER_NE',"<>");
 
 
require_once('PEAR.php');
 
/**
* Class for parsing Excel formulas
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer_Parser extends PEAR
{
/**
* The index of the character we are currently looking at
* @var integer
*/
var $_current_char;
 
/**
* The token we are working on.
* @var string
*/
var $_current_token;
 
/**
* The formula to parse
* @var string
*/
var $_formula;
 
/**
* The character ahead of the current char
* @var string
*/
var $_lookahead;
 
/**
* The parse tree to be generated
* @var string
*/
var $_parse_tree;
 
/**
* The byte order. 1 => big endian, 0 => little endian.
* @var integer
*/
var $_byte_order;
 
/**
* Number of arguments for the current function
* @var integer
*/
var $_func_args;
 
/**
* Array of external sheets
* @var array
*/
var $_ext_sheets;
 
/**
* The class constructor
*
* @param integer $byte_order The byte order (Little endian or Big endian) of the architecture
(optional). 1 => big endian, 0 (default) => little endian.
*/
function Spreadsheet_Excel_Writer_Parser($byte_order = 0)
{
$this->_current_char = 0;
$this->_current_token = ''; // The token we are working on.
$this->_formula = ""; // The formula to parse.
$this->_lookahead = ''; // The character ahead of the current char.
$this->_parse_tree = ''; // The parse tree to be generated.
$this->_initializeHashes(); // Initialize the hashes: ptg's and function's ptg's
$this->_byte_order = $byte_order; // Little Endian or Big Endian
$this->_func_args = 0; // Number of arguments for the current function
$this->_ext_sheets = array();
}
/**
* Initialize the ptg and function hashes.
*
* @access private
*/
function _initializeHashes()
{
// The Excel ptg indices
$this->ptg = array(
'ptgExp' => 0x01,
'ptgTbl' => 0x02,
'ptgAdd' => 0x03,
'ptgSub' => 0x04,
'ptgMul' => 0x05,
'ptgDiv' => 0x06,
'ptgPower' => 0x07,
'ptgConcat' => 0x08,
'ptgLT' => 0x09,
'ptgLE' => 0x0A,
'ptgEQ' => 0x0B,
'ptgGE' => 0x0C,
'ptgGT' => 0x0D,
'ptgNE' => 0x0E,
'ptgIsect' => 0x0F,
'ptgUnion' => 0x10,
'ptgRange' => 0x11,
'ptgUplus' => 0x12,
'ptgUminus' => 0x13,
'ptgPercent' => 0x14,
'ptgParen' => 0x15,
'ptgMissArg' => 0x16,
'ptgStr' => 0x17,
'ptgAttr' => 0x19,
'ptgSheet' => 0x1A,
'ptgEndSheet' => 0x1B,
'ptgErr' => 0x1C,
'ptgBool' => 0x1D,
'ptgInt' => 0x1E,
'ptgNum' => 0x1F,
'ptgArray' => 0x20,
'ptgFunc' => 0x21,
'ptgFuncVar' => 0x22,
'ptgName' => 0x23,
'ptgRef' => 0x24,
'ptgArea' => 0x25,
'ptgMemArea' => 0x26,
'ptgMemErr' => 0x27,
'ptgMemNoMem' => 0x28,
'ptgMemFunc' => 0x29,
'ptgRefErr' => 0x2A,
'ptgAreaErr' => 0x2B,
'ptgRefN' => 0x2C,
'ptgAreaN' => 0x2D,
'ptgMemAreaN' => 0x2E,
'ptgMemNoMemN' => 0x2F,
'ptgNameX' => 0x39,
'ptgRef3d' => 0x3A,
'ptgArea3d' => 0x3B,
'ptgRefErr3d' => 0x3C,
'ptgAreaErr3d' => 0x3D,
'ptgArrayV' => 0x40,
'ptgFuncV' => 0x41,
'ptgFuncVarV' => 0x42,
'ptgNameV' => 0x43,
'ptgRefV' => 0x44,
'ptgAreaV' => 0x45,
'ptgMemAreaV' => 0x46,
'ptgMemErrV' => 0x47,
'ptgMemNoMemV' => 0x48,
'ptgMemFuncV' => 0x49,
'ptgRefErrV' => 0x4A,
'ptgAreaErrV' => 0x4B,
'ptgRefNV' => 0x4C,
'ptgAreaNV' => 0x4D,
'ptgMemAreaNV' => 0x4E,
'ptgMemNoMemN' => 0x4F,
'ptgFuncCEV' => 0x58,
'ptgNameXV' => 0x59,
'ptgRef3dV' => 0x5A,
'ptgArea3dV' => 0x5B,
'ptgRefErr3dV' => 0x5C,
'ptgAreaErr3d' => 0x5D,
'ptgArrayA' => 0x60,
'ptgFuncA' => 0x61,
'ptgFuncVarA' => 0x62,
'ptgNameA' => 0x63,
'ptgRefA' => 0x64,
'ptgAreaA' => 0x65,
'ptgMemAreaA' => 0x66,
'ptgMemErrA' => 0x67,
'ptgMemNoMemA' => 0x68,
'ptgMemFuncA' => 0x69,
'ptgRefErrA' => 0x6A,
'ptgAreaErrA' => 0x6B,
'ptgRefNA' => 0x6C,
'ptgAreaNA' => 0x6D,
'ptgMemAreaNA' => 0x6E,
'ptgMemNoMemN' => 0x6F,
'ptgFuncCEA' => 0x78,
'ptgNameXA' => 0x79,
'ptgRef3dA' => 0x7A,
'ptgArea3dA' => 0x7B,
'ptgRefErr3dA' => 0x7C,
'ptgAreaErr3d' => 0x7D
);
// Thanks to Michael Meeks and Gnumeric for the initial arg values.
//
// The following hash was generated by "function_locale.pl" in the distro.
// Refer to function_locale.pl for non-English function names.
//
// The array elements are as follow:
// ptg: The Excel function ptg code.
// args: The number of arguments that the function takes:
// >=0 is a fixed number of arguments.
// -1 is a variable number of arguments.
// class: The reference, value or array class of the function args.
// vol: The function is volatile.
//
$this->_functions = array(
// function ptg args class vol
'COUNT' => array( 0, -1, 0, 0 ),
'IF' => array( 1, -1, 1, 0 ),
'ISNA' => array( 2, 1, 1, 0 ),
'ISERROR' => array( 3, 1, 1, 0 ),
'SUM' => array( 4, -1, 0, 0 ),
'AVERAGE' => array( 5, -1, 0, 0 ),
'MIN' => array( 6, -1, 0, 0 ),
'MAX' => array( 7, -1, 0, 0 ),
'ROW' => array( 8, -1, 0, 0 ),
'COLUMN' => array( 9, -1, 0, 0 ),
'NA' => array( 10, 0, 0, 0 ),
'NPV' => array( 11, -1, 1, 0 ),
'STDEV' => array( 12, -1, 0, 0 ),
'DOLLAR' => array( 13, -1, 1, 0 ),
'FIXED' => array( 14, -1, 1, 0 ),
'SIN' => array( 15, 1, 1, 0 ),
'COS' => array( 16, 1, 1, 0 ),
'TAN' => array( 17, 1, 1, 0 ),
'ATAN' => array( 18, 1, 1, 0 ),
'PI' => array( 19, 0, 1, 0 ),
'SQRT' => array( 20, 1, 1, 0 ),
'EXP' => array( 21, 1, 1, 0 ),
'LN' => array( 22, 1, 1, 0 ),
'LOG10' => array( 23, 1, 1, 0 ),
'ABS' => array( 24, 1, 1, 0 ),
'INT' => array( 25, 1, 1, 0 ),
'SIGN' => array( 26, 1, 1, 0 ),
'ROUND' => array( 27, 2, 1, 0 ),
'LOOKUP' => array( 28, -1, 0, 0 ),
'INDEX' => array( 29, -1, 0, 1 ),
'REPT' => array( 30, 2, 1, 0 ),
'MID' => array( 31, 3, 1, 0 ),
'LEN' => array( 32, 1, 1, 0 ),
'VALUE' => array( 33, 1, 1, 0 ),
'TRUE' => array( 34, 0, 1, 0 ),
'FALSE' => array( 35, 0, 1, 0 ),
'AND' => array( 36, -1, 0, 0 ),
'OR' => array( 37, -1, 0, 0 ),
'NOT' => array( 38, 1, 1, 0 ),
'MOD' => array( 39, 2, 1, 0 ),
'DCOUNT' => array( 40, 3, 0, 0 ),
'DSUM' => array( 41, 3, 0, 0 ),
'DAVERAGE' => array( 42, 3, 0, 0 ),
'DMIN' => array( 43, 3, 0, 0 ),
'DMAX' => array( 44, 3, 0, 0 ),
'DSTDEV' => array( 45, 3, 0, 0 ),
'VAR' => array( 46, -1, 0, 0 ),
'DVAR' => array( 47, 3, 0, 0 ),
'TEXT' => array( 48, 2, 1, 0 ),
'LINEST' => array( 49, -1, 0, 0 ),
'TREND' => array( 50, -1, 0, 0 ),
'LOGEST' => array( 51, -1, 0, 0 ),
'GROWTH' => array( 52, -1, 0, 0 ),
'PV' => array( 56, -1, 1, 0 ),
'FV' => array( 57, -1, 1, 0 ),
'NPER' => array( 58, -1, 1, 0 ),
'PMT' => array( 59, -1, 1, 0 ),
'RATE' => array( 60, -1, 1, 0 ),
'MIRR' => array( 61, 3, 0, 0 ),
'IRR' => array( 62, -1, 0, 0 ),
'RAND' => array( 63, 0, 1, 1 ),
'MATCH' => array( 64, -1, 0, 0 ),
'DATE' => array( 65, 3, 1, 0 ),
'TIME' => array( 66, 3, 1, 0 ),
'DAY' => array( 67, 1, 1, 0 ),
'MONTH' => array( 68, 1, 1, 0 ),
'YEAR' => array( 69, 1, 1, 0 ),
'WEEKDAY' => array( 70, -1, 1, 0 ),
'HOUR' => array( 71, 1, 1, 0 ),
'MINUTE' => array( 72, 1, 1, 0 ),
'SECOND' => array( 73, 1, 1, 0 ),
'NOW' => array( 74, 0, 1, 1 ),
'AREAS' => array( 75, 1, 0, 1 ),
'ROWS' => array( 76, 1, 0, 1 ),
'COLUMNS' => array( 77, 1, 0, 1 ),
'OFFSET' => array( 78, -1, 0, 1 ),
'SEARCH' => array( 82, -1, 1, 0 ),
'TRANSPOSE' => array( 83, 1, 1, 0 ),
'TYPE' => array( 86, 1, 1, 0 ),
'ATAN2' => array( 97, 2, 1, 0 ),
'ASIN' => array( 98, 1, 1, 0 ),
'ACOS' => array( 99, 1, 1, 0 ),
'CHOOSE' => array( 100, -1, 1, 0 ),
'HLOOKUP' => array( 101, -1, 0, 0 ),
'VLOOKUP' => array( 102, -1, 0, 0 ),
'ISREF' => array( 105, 1, 0, 0 ),
'LOG' => array( 109, -1, 1, 0 ),
'CHAR' => array( 111, 1, 1, 0 ),
'LOWER' => array( 112, 1, 1, 0 ),
'UPPER' => array( 113, 1, 1, 0 ),
'PROPER' => array( 114, 1, 1, 0 ),
'LEFT' => array( 115, -1, 1, 0 ),
'RIGHT' => array( 116, -1, 1, 0 ),
'EXACT' => array( 117, 2, 1, 0 ),
'TRIM' => array( 118, 1, 1, 0 ),
'REPLACE' => array( 119, 4, 1, 0 ),
'SUBSTITUTE' => array( 120, -1, 1, 0 ),
'CODE' => array( 121, 1, 1, 0 ),
'FIND' => array( 124, -1, 1, 0 ),
'CELL' => array( 125, -1, 0, 1 ),
'ISERR' => array( 126, 1, 1, 0 ),
'ISTEXT' => array( 127, 1, 1, 0 ),
'ISNUMBER' => array( 128, 1, 1, 0 ),
'ISBLANK' => array( 129, 1, 1, 0 ),
'T' => array( 130, 1, 0, 0 ),
'N' => array( 131, 1, 0, 0 ),
'DATEVALUE' => array( 140, 1, 1, 0 ),
'TIMEVALUE' => array( 141, 1, 1, 0 ),
'SLN' => array( 142, 3, 1, 0 ),
'SYD' => array( 143, 4, 1, 0 ),
'DDB' => array( 144, -1, 1, 0 ),
'INDIRECT' => array( 148, -1, 1, 1 ),
'CALL' => array( 150, -1, 1, 0 ),
'CLEAN' => array( 162, 1, 1, 0 ),
'MDETERM' => array( 163, 1, 2, 0 ),
'MINVERSE' => array( 164, 1, 2, 0 ),
'MMULT' => array( 165, 2, 2, 0 ),
'IPMT' => array( 167, -1, 1, 0 ),
'PPMT' => array( 168, -1, 1, 0 ),
'COUNTA' => array( 169, -1, 0, 0 ),
'PRODUCT' => array( 183, -1, 0, 0 ),
'FACT' => array( 184, 1, 1, 0 ),
'DPRODUCT' => array( 189, 3, 0, 0 ),
'ISNONTEXT' => array( 190, 1, 1, 0 ),
'STDEVP' => array( 193, -1, 0, 0 ),
'VARP' => array( 194, -1, 0, 0 ),
'DSTDEVP' => array( 195, 3, 0, 0 ),
'DVARP' => array( 196, 3, 0, 0 ),
'TRUNC' => array( 197, -1, 1, 0 ),
'ISLOGICAL' => array( 198, 1, 1, 0 ),
'DCOUNTA' => array( 199, 3, 0, 0 ),
'ROUNDUP' => array( 212, 2, 1, 0 ),
'ROUNDDOWN' => array( 213, 2, 1, 0 ),
'RANK' => array( 216, -1, 0, 0 ),
'ADDRESS' => array( 219, -1, 1, 0 ),
'DAYS360' => array( 220, -1, 1, 0 ),
'TODAY' => array( 221, 0, 1, 1 ),
'VDB' => array( 222, -1, 1, 0 ),
'MEDIAN' => array( 227, -1, 0, 0 ),
'SUMPRODUCT' => array( 228, -1, 2, 0 ),
'SINH' => array( 229, 1, 1, 0 ),
'COSH' => array( 230, 1, 1, 0 ),
'TANH' => array( 231, 1, 1, 0 ),
'ASINH' => array( 232, 1, 1, 0 ),
'ACOSH' => array( 233, 1, 1, 0 ),
'ATANH' => array( 234, 1, 1, 0 ),
'DGET' => array( 235, 3, 0, 0 ),
'INFO' => array( 244, 1, 1, 1 ),
'DB' => array( 247, -1, 1, 0 ),
'FREQUENCY' => array( 252, 2, 0, 0 ),
'ERROR.TYPE' => array( 261, 1, 1, 0 ),
'REGISTER.ID' => array( 267, -1, 1, 0 ),
'AVEDEV' => array( 269, -1, 0, 0 ),
'BETADIST' => array( 270, -1, 1, 0 ),
'GAMMALN' => array( 271, 1, 1, 0 ),
'BETAINV' => array( 272, -1, 1, 0 ),
'BINOMDIST' => array( 273, 4, 1, 0 ),
'CHIDIST' => array( 274, 2, 1, 0 ),
'CHIINV' => array( 275, 2, 1, 0 ),
'COMBIN' => array( 276, 2, 1, 0 ),
'CONFIDENCE' => array( 277, 3, 1, 0 ),
'CRITBINOM' => array( 278, 3, 1, 0 ),
'EVEN' => array( 279, 1, 1, 0 ),
'EXPONDIST' => array( 280, 3, 1, 0 ),
'FDIST' => array( 281, 3, 1, 0 ),
'FINV' => array( 282, 3, 1, 0 ),
'FISHER' => array( 283, 1, 1, 0 ),
'FISHERINV' => array( 284, 1, 1, 0 ),
'FLOOR' => array( 285, 2, 1, 0 ),
'GAMMADIST' => array( 286, 4, 1, 0 ),
'GAMMAINV' => array( 287, 3, 1, 0 ),
'CEILING' => array( 288, 2, 1, 0 ),
'HYPGEOMDIST' => array( 289, 4, 1, 0 ),
'LOGNORMDIST' => array( 290, 3, 1, 0 ),
'LOGINV' => array( 291, 3, 1, 0 ),
'NEGBINOMDIST' => array( 292, 3, 1, 0 ),
'NORMDIST' => array( 293, 4, 1, 0 ),
'NORMSDIST' => array( 294, 1, 1, 0 ),
'NORMINV' => array( 295, 3, 1, 0 ),
'NORMSINV' => array( 296, 1, 1, 0 ),
'STANDARDIZE' => array( 297, 3, 1, 0 ),
'ODD' => array( 298, 1, 1, 0 ),
'PERMUT' => array( 299, 2, 1, 0 ),
'POISSON' => array( 300, 3, 1, 0 ),
'TDIST' => array( 301, 3, 1, 0 ),
'WEIBULL' => array( 302, 4, 1, 0 ),
'SUMXMY2' => array( 303, 2, 2, 0 ),
'SUMX2MY2' => array( 304, 2, 2, 0 ),
'SUMX2PY2' => array( 305, 2, 2, 0 ),
'CHITEST' => array( 306, 2, 2, 0 ),
'CORREL' => array( 307, 2, 2, 0 ),
'COVAR' => array( 308, 2, 2, 0 ),
'FORECAST' => array( 309, 3, 2, 0 ),
'FTEST' => array( 310, 2, 2, 0 ),
'INTERCEPT' => array( 311, 2, 2, 0 ),
'PEARSON' => array( 312, 2, 2, 0 ),
'RSQ' => array( 313, 2, 2, 0 ),
'STEYX' => array( 314, 2, 2, 0 ),
'SLOPE' => array( 315, 2, 2, 0 ),
'TTEST' => array( 316, 4, 2, 0 ),
'PROB' => array( 317, -1, 2, 0 ),
'DEVSQ' => array( 318, -1, 0, 0 ),
'GEOMEAN' => array( 319, -1, 0, 0 ),
'HARMEAN' => array( 320, -1, 0, 0 ),
'SUMSQ' => array( 321, -1, 0, 0 ),
'KURT' => array( 322, -1, 0, 0 ),
'SKEW' => array( 323, -1, 0, 0 ),
'ZTEST' => array( 324, -1, 0, 0 ),
'LARGE' => array( 325, 2, 0, 0 ),
'SMALL' => array( 326, 2, 0, 0 ),
'QUARTILE' => array( 327, 2, 0, 0 ),
'PERCENTILE' => array( 328, 2, 0, 0 ),
'PERCENTRANK' => array( 329, -1, 0, 0 ),
'MODE' => array( 330, -1, 2, 0 ),
'TRIMMEAN' => array( 331, 2, 0, 0 ),
'TINV' => array( 332, 2, 1, 0 ),
'CONCATENATE' => array( 336, -1, 1, 0 ),
'POWER' => array( 337, 2, 1, 0 ),
'RADIANS' => array( 342, 1, 1, 0 ),
'DEGREES' => array( 343, 1, 1, 0 ),
'SUBTOTAL' => array( 344, -1, 0, 0 ),
'SUMIF' => array( 345, -1, 0, 0 ),
'COUNTIF' => array( 346, 2, 0, 0 ),
'COUNTBLANK' => array( 347, 1, 0, 0 ),
'ROMAN' => array( 354, -1, 1, 0 )
);
}
/**
* Convert a token to the proper ptg value.
*
* @access private
* @param mixed $token The token to convert.
*/
function _convert($token)
{
if (preg_match("/^\"[^\"]{0,255}\"$/", $token))
{
return $this->_convertString($token);
}
elseif (is_numeric($token))
{
return $this->_convertNumber($token);
}
// match references like A1 or $A$1
elseif(preg_match('/^\$?([A-I]?[A-Z])\$?(\d+)$/',$token))
{
return($this->_convertRef2d($token));
}
// match external references like Sheet1:Sheet2!A1
elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\![A-I]?[A-Z](\d+)$/",$token))
{
return $this->_convertRef3d($token);
}
// match ranges like A1:B2
elseif(preg_match("/^(\$)?[A-I]?[A-Z](\$)?(\d+)\:(\$)?[A-I]?[A-Z](\$)?(\d+)$/",$token))
{
return($this->_convertRange2d($token));
}
// match ranges like A1..B2
elseif(preg_match("/^(\$)?[A-I]?[A-Z](\$)?(\d+)\.\.(\$)?[A-I]?[A-Z](\$)?(\d+)$/",$token))
{
return($this->_convertRange2d($token));
}
// match external ranges like Sheet1:Sheet2!A1:B2
elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\!([A-I]?[A-Z])?(\d+)\:([A-I]?[A-Z])?(\d+)$/",$token))
{
return $this->_convertRange3d($token);
}
elseif(isset($this->ptg[$token])) // operators (including parentheses)
{
return(pack("C", $this->ptg[$token]));
}
elseif(preg_match("/[A-Z0-9\xc0-\xdc\.]+/",$token))
{
return($this->_convertFunction($token,$this->_func_args));
}
// if it's an argument, ignore the token (the argument remains)
elseif($token == 'arg')
{
$this->_func_args++;
return('');
}
// TODO: use real error codes
$this->raiseError("Unknown token $token", 0, PEAR_ERROR_DIE);
}
/**
* Convert a number token to ptgInt or ptgNum
*
* @access private
* @param mixed $num an integer or double for conversion to its ptg value
*/
function _convertNumber($num)
{
// Integer in the range 0..2**16-1
if ((preg_match("/^\d+$/",$num)) and ($num <= 65535)) {
return(pack("Cv", $this->ptg['ptgInt'], $num));
}
else // A float
{
if($this->_byte_order) // if it's Big Endian
{
$num = strrev($num);
}
return(pack("Cd", $this->ptg['ptgNum'], $num));
}
}
/**
* Convert a string token to ptgStr
*
* @access private
* @param string $string A string for conversion to its ptg value
*/
function _convertString($string)
{
// chop away beggining and ending quotes
$string = substr($string, 1, strlen($string) - 2);
return pack("CC", $this->ptg['ptgStr'], strlen($string)).$string;
}
/**
* Convert a function to a ptgFunc or ptgFuncVarV depending on the number of
* args that it takes.
*
* @access private
* @param string $token The name of the function for convertion to ptg value.
* @param integer $num_args The number of arguments the function recieves.
*/
function _convertFunction($token, $num_args)
{
$this->_func_args = 0; // re initialize the number of arguments
$args = $this->_functions[$token][1];
$volatile = $this->_functions[$token][3];
// Fixed number of args eg. TIME($i,$j,$k).
if ($args >= 0) {
return(pack("Cv", $this->ptg['ptgFuncV'], $this->_functions[$token][0]));
}
// Variable number of args eg. SUM($i,$j,$k, ..).
if ($args == -1) {
return(pack("CCv", $this->ptg['ptgFuncVarV'], $num_args, $this->_functions[$token][0]));
}
}
/**
* Convert an Excel range such as A1:D4 to a ptgRefV.
*
* @access private
* @param string $range An Excel range in the A1:A2 or A1..A2 format.
*/
function _convertRange2d($range)
{
$class = 2; // as far as I know, this is magick.
// Split the range into 2 cell refs
if(preg_match("/^([A-I]?[A-Z])(\d+)\:([A-I]?[A-Z])(\d+)$/",$range)) {
list($cell1, $cell2) = split(':', $range);
}
elseif(preg_match("/^([A-I]?[A-Z])(\d+)\.\.([A-I]?[A-Z])(\d+)$/",$range)) {
list($cell1, $cell2) = split('\.\.', $range);
}
else {
// TODO: use real error codes
$this->raiseError("Unknown range separator", 0, PEAR_ERROR_DIE);
}
// Convert the cell references
$cell_array1 = $this->_cellToPackedRowcol($cell1);
if($this->isError($cell_array1)) {
return($cell_array1);
}
list($row1, $col1) = $cell_array1; //$this->_cellToPackedRowcol($cell1);
$cell_array2 = $this->_cellToPackedRowcol($cell2);
if($this->isError($cell_array2)) {
return($cell_array2);
}
list($row2, $col2) = $cell_array2; //$this->_cellToPackedRowcol($cell2);
// The ptg value depends on the class of the ptg.
if ($class == 0) {
$ptgArea = pack("C", $this->ptg['ptgArea']);
}
elseif ($class == 1) {
$ptgArea = pack("C", $this->ptg['ptgAreaV']);
}
elseif ($class == 2) {
$ptgArea = pack("C", $this->ptg['ptgAreaA']);
}
else {
// TODO: use real error codes
$this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE);
}
return($ptgArea . $row1 . $row2 . $col1. $col2);
}
/**
* Convert an Excel 3d range such as "Sheet1!A1:D4" or "Sheet1:Sheet2!A1:D4" to
* a ptgArea3dV.
*
* @access private
* @param string $token An Excel range in the Sheet1!A1:A2 format.
*/
function _convertRange3d($token)
{
$class = 2; // as far as I know, this is magick.
// Split the ref at the ! symbol
list($ext_ref, $range) = split('!', $token);
// Convert the external reference part
$ext_ref = $this->_packExtRef($ext_ref);
if ($this->isError($ext_ref)) {
return $ext_ref;
}
// Split the range into 2 cell refs
list($cell1, $cell2) = split(':', $range);
// Convert the cell references
if (preg_match("/^(\$)?[A-I]?[A-Z](\$)?(\d+)$/", $cell1))
{
$cell_array1 = $this->_cellToPackedRowcol($cell1);
if (PEAR::isError($cell_array1)) {
return $cell_array1;
}
list($row1, $col1) = $cell_array1;
$cell_array2 = $this->_cellToPackedRowcol($cell2);
if (PEAR::isError($cell_array2)) {
return $cell_array2;
}
list($row2, $col2) = $cell_array2;
}
else { // It's a columns range (like 26:27)
$cells_array = $this->_rangeToPackedRange($cell1.':'.$cell2);
if (PEAR::isError($cells_array)) {
return $cells_array;
}
list($row1, $col1, $row2, $col2) = $cells_array;
}
// The ptg value depends on the class of the ptg.
if ($class == 0) {
$ptgArea = pack("C", $this->ptg['ptgArea3d']);
}
elseif ($class == 1) {
$ptgArea = pack("C", $this->ptg['ptgArea3dV']);
}
elseif ($class == 2) {
$ptgArea = pack("C", $this->ptg['ptgArea3dA']);
}
else {
$this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE);
}
return $ptgArea . $ext_ref . $row1 . $row2 . $col1. $col2;
}
 
/**
* Convert an Excel reference such as A1, $B2, C$3 or $D$4 to a ptgRefV.
*
* @access private
* @param string $cell An Excel cell reference
* @return string The cell in packed() format with the corresponding ptg
*/
function _convertRef2d($cell)
{
$class = 2; // as far as I know, this is magick.
// Convert the cell reference
$cell_array = $this->_cellToPackedRowcol($cell);
if($this->isError($cell_array)) {
return($cell_array);
}
list($row, $col) = $cell_array;
// The ptg value depends on the class of the ptg.
if ($class == 0) {
$ptgRef = pack("C", $this->ptg['ptgRef']);
}
elseif ($class == 1) {
$ptgRef = pack("C", $this->ptg['ptgRefV']);
}
elseif ($class == 2) {
$ptgRef = pack("C", $this->ptg['ptgRefA']);
}
else {
// TODO: use real error codes
$this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE);
}
return($ptgRef.$row.$col);
}
/**
* Convert an Excel 3d reference such as "Sheet1!A1" or "Sheet1:Sheet2!A1" to a
* ptgRef3dV.
*
* @access private
* @param string $cell An Excel cell reference
* @return string The cell in packed() format with the corresponding ptg
*/
function _convertRef3d($cell)
{
$class = 2; // as far as I know, this is magick.
// Split the ref at the ! symbol
list($ext_ref, $cell) = split('!', $cell);
// Convert the external reference part
$ext_ref = $this->_packExtRef($ext_ref);
if ($this->isError($ext_ref)) {
return $ext_ref;
}
// Convert the cell reference part
list($row, $col) = $this->_cellToPackedRowcol($cell);
// The ptg value depends on the class of the ptg.
if ($class == 0) {
$ptgRef = pack("C", $this->ptg['ptgRef3d']);
}
elseif ($class == 1) {
$ptgRef = pack("C", $this->ptg['ptgRef3dV']);
}
elseif ($class == 2) {
$ptgRef = pack("C", $this->ptg['ptgRef3dA']);
}
else {
$this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE);
}
 
return $ptgRef . $ext_ref. $row . $col;
}
 
/**
* Convert the sheet name part of an external reference, for example "Sheet1" or
* "Sheet1:Sheet2", to a packed structure.
*
* @access private
* @param string $ext_ref The name of the external reference
* @return string The reference index in packed() format
*/
function _packExtRef($ext_ref)
{
$ext_ref = preg_replace("/^'/", '', $ext_ref); // Remove leading ' if any.
$ext_ref = preg_replace("/'$/", '', $ext_ref); // Remove trailing ' if any.
// Check if there is a sheet range eg., Sheet1:Sheet2.
if (preg_match("/:/", $ext_ref))
{
list($sheet_name1, $sheet_name2) = split(':', $ext_ref);
$sheet1 = $this->_getSheetIndex($sheet_name1);
if ($sheet1 == -1) {
return $this->raiseError("Unknown sheet name $sheet_name1 in formula");
}
$sheet2 = $this->_getSheetIndex($sheet_name2);
if ($sheet2 == -1) {
return $this->raiseError("Unknown sheet name $sheet_name2 in formula");
}
// Reverse max and min sheet numbers if necessary
if ($sheet1 > $sheet2) {
list($sheet1, $sheet2) = array($sheet2, $sheet1);
}
}
else // Single sheet name only.
{
$sheet1 = $this->_getSheetIndex($ext_ref);
if ($sheet1 == -1) {
return $this->raiseError("Unknown sheet name $ext_ref in formula");
}
$sheet2 = $sheet1;
}
// References are stored relative to 0xFFFF.
$offset = -1 - $sheet1;
 
return pack('vdvv', $offset, 0x00, $sheet1, $sheet2);
}
 
/**
* Look up the index that corresponds to an external sheet name. The hash of
* sheet names is updated by the addworksheet() method of the
* Spreadsheet_Excel_Writer_Workbook class.
*
* @access private
* @return integer
*/
function _getSheetIndex($sheet_name)
{
if (!isset($this->_ext_sheets[$sheet_name])) {
return -1;
}
else {
return $this->_ext_sheets[$sheet_name];
}
}
 
/**
* This method is used to update the array of sheet names. It is
* called by the addWorksheet() method of the Spreadsheet_Excel_Writer_Workbook class.
*
* @access private
* @param string $name The name of the worksheet being added
* @param integer $index The index of the worksheet being added
*/
function setExtSheet($name, $index)
{
$this->_ext_sheets[$name] = $index;
}
 
/**
* pack() row and column into the required 3 byte format.
*
* @access private
* @param string $cell The Excel cell reference to be packed
* @return array Array containing the row and column in packed() format
*/
function _cellToPackedRowcol($cell)
{
list($row, $col, $row_rel, $col_rel) = $this->_cellToRowcol($cell);
if ($col >= 256) {
return($this->raiseError("Column in: $cell greater than 255"));
}
if ($row >= 16384) {
return($this->raiseError("Row in: $cell greater than 16384 "));
}
// Set the high bits to indicate if row or col are relative.
$row |= $col_rel << 14;
$row |= $row_rel << 15;
$row = pack('v', $row);
$col = pack('C', $col);
return(array($row, $col));
}
/**
* pack() row range into the required 3 byte format.
* Just using maximun col/rows, which is probably not the correct solution
*
* @access private
* @param string $range The Excel range to be packed
* @return array Array containing (row1,col1,row2,col2) in packed() format
*/
function _rangeToPackedRange($range)
{
preg_match('/(\$)?(\d+)\:(\$)?(\d+)/', $range, $match);
// return absolute rows if there is a $ in the ref
$row1_rel = empty($match[1]) ? 1 : 0;
$row1 = $match[2];
$row2_rel = empty($match[3]) ? 1 : 0;
$row2 = $match[4];
// Convert 1-index to zero-index
$row1--;
$row2--;
// Trick poor inocent Excel
$col1 = 0;
$col2 = 16383; // maximum possible value for Excel 5 (change this!!!)
 
//list($row, $col, $row_rel, $col_rel) = $this->_cellToRowcol($cell);
if (($row1 >= 16384) or ($row2 >= 16384)) {
return new PEAR_Error("Row in: $range greater than 16384 ");
}
// Set the high bits to indicate if rows are relative.
$row1 |= $row1_rel << 14;
$row2 |= $row2_rel << 15;
$row1 = pack('v', $row1);
$row2 = pack('v', $row2);
$col1 = pack('C', $col1);
$col2 = pack('C', $col2);
return array($row1, $col1, $row2, $col2);
}
 
/**
* Convert an Excel cell reference such as A1 or $B2 or C$3 or $D$4 to a zero
* indexed row and column number. Also returns two (0,1) values to indicate
* whether the row or column are relative references.
*
* @access private
* @param string $cell The Excel cell reference in A1 format.
* @return array
*/
function _cellToRowcol($cell)
{
preg_match('/(\$)?([A-I]?[A-Z])(\$)?(\d+)/',$cell,$match);
// return absolute column if there is a $ in the ref
$col_rel = empty($match[1]) ? 1 : 0;
$col_ref = $match[2];
$row_rel = empty($match[3]) ? 1 : 0;
$row = $match[4];
// Convert base26 column string to a number.
$expn = strlen($col_ref) - 1;
$col = 0;
for($i=0; $i < strlen($col_ref); $i++)
{
$col += (ord($col_ref{$i}) - ord('A') + 1) * pow(26, $expn);
$expn--;
}
// Convert 1-index to zero-index
$row--;
$col--;
return(array($row, $col, $row_rel, $col_rel));
}
/**
* Advance to the next valid token.
*
* @access private
*/
function _advance()
{
$i = $this->_current_char;
// eat up white spaces
if($i < strlen($this->_formula))
{
while($this->_formula{$i} == " ") {
$i++;
}
if($i < strlen($this->_formula) - 1) {
$this->_lookahead = $this->_formula{$i+1};
}
$token = "";
}
while($i < strlen($this->_formula))
{
$token .= $this->_formula{$i};
if($this->_match($token) != '')
{
if($i < strlen($this->_formula) - 1) {
$this->_lookahead = $this->_formula{$i+1};
}
$this->_current_char = $i + 1;
$this->_current_token = $token;
return(1);
}
if ($i < strlen($this->_formula) - 2) {
$this->_lookahead = $this->_formula{$i+2};
}
// if we run out of characters _lookahead becomes empty
else {
$this->_lookahead = '';
}
$i++;
}
//die("Lexical error ".$this->_current_char);
}
/**
* Checks if it's a valid token.
*
* @access private
* @param mixed $token The token to check.
* @return mixed The checked token or false on failure
*/
function _match($token)
{
switch($token)
{
case SPREADSHEET_EXCEL_WRITER_ADD:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_SUB:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_MUL:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_DIV:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_OPEN:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_CLOSE:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_COMA:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_GT:
if ($this->_lookahead == '=') { // it's a GE token
break;
}
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_LT:
// it's a LE or a NE token
if (($this->_lookahead == '=') or ($this->_lookahead == '>')) {
break;
}
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_GE:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_LE:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_EQ:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_NE:
return($token);
break;
default:
// if it's a reference
if (preg_match('/^\$?[A-I]?[A-Z]\$?[0-9]+$/',$token) and
!ereg("[0-9]",$this->_lookahead) and
($this->_lookahead != ':') and ($this->_lookahead != '.') and
($this->_lookahead != '!'))
{
return $token;
}
// If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1)
elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\![A-I]?[A-Z][0-9]+$/",$token) and
!ereg("[0-9]",$this->_lookahead) and
($this->_lookahead != ':') and ($this->_lookahead != '.'))
{
return $token;
}
// if it's a range (A1:A2)
elseif (preg_match("/^(\$)?[A-I]?[A-Z](\$)?[0-9]+:(\$)?[A-I]?[A-Z](\$)?[0-9]+$/",$token) and
!ereg("[0-9]",$this->_lookahead))
{
return $token;
}
// if it's a range (A1..A2)
elseif (preg_match("/^(\$)?[A-I]?[A-Z](\$)?[0-9]+\.\.(\$)?[A-I]?[A-Z](\$)?[0-9]+$/",$token) and
!ereg("[0-9]",$this->_lookahead))
{
return $token;
}
// If it's an external range
elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\!([A-I]?[A-Z])?[0-9]+:([A-I]?[A-Z])?[0-9]+$/",$token) and
!ereg("[0-9]",$this->_lookahead))
{
return $token;
}
// If it's a number (check that it's not a sheet name or range)
elseif (is_numeric($token) and !is_numeric($token.$this->_lookahead) and
($this->_lookahead != '!') and (($this->_lookahead != ':')))
{
return $token;
}
// If it's a string (of maximum 255 characters)
elseif(ereg("^\"[^\"]{0,255}\"$",$token))
{
return($token);
}
// if it's a function call
elseif(eregi("^[A-Z0-9\xc0-\xdc\.]+$",$token) and ($this->_lookahead == "("))
{
return($token);
}
return '';
}
}
/**
* The parsing method. It parses a formula.
*
* @access public
* @param string $formula The formula to parse, without the initial equal sign (=).
*/
function parse($formula)
{
$this->_current_char = 0;
$this->_formula = $formula;
$this->_lookahead = $formula{1};
$this->_advance();
$this->_parse_tree = $this->_condition();
if ($this->isError($this->_parse_tree)) {
return $this->_parse_tree;
}
}
/**
* It parses a condition. It assumes the following rule:
* Cond -> Expr [(">" | "<") Expr]
*
* @access private
* @return mixed The parsed ptg'd tree
*/
function _condition()
{
$result = $this->_expression();
if($this->isError($result)) {
return $result;
}
if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_LT)
{
$this->_advance();
$result2 = $this->_expression();
if($this->isError($result2)) {
return $result2;
}
$result = $this->_createTree('ptgLT', $result, $result2);
}
elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_GT)
{
$this->_advance();
$result2 = $this->_expression();
if($this->isError($result2)) {
return $result2;
}
$result = $this->_createTree('ptgGT', $result, $result2);
}
elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_LE)
{
$this->_advance();
$result2 = $this->_expression();
if($this->isError($result2)) {
return $result2;
}
$result = $this->_createTree('ptgLE', $result, $result2);
}
elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_GE)
{
$this->_advance();
$result2 = $this->_expression();
if($this->isError($result2)) {
return $result2;
}
$result = $this->_createTree('ptgGE', $result, $result2);
}
elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_EQ)
{
$this->_advance();
$result2 = $this->_expression();
if($this->isError($result2)) {
return $result2;
}
$result = $this->_createTree('ptgEQ', $result, $result2);
}
elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_NE)
{
$this->_advance();
$result2 = $this->_expression();
if($this->isError($result2)) {
return $result2;
}
$result = $this->_createTree('ptgNE', $result, $result2);
}
return $result;
}
/**
* It parses a expression. It assumes the following rule:
* Expr -> Term [("+" | "-") Term]
*
* @access private
* @return mixed The parsed ptg'd tree
*/
function _expression()
{
// If it's a string return a string node
if (ereg("^\"[^\"]{0,255}\"$", $this->_current_token))
{
$result = $this->_createTree($this->_current_token, '', '');
$this->_advance();
return($result);
}
$result = $this->_term();
if($this->isError($result)) {
return($result);
}
while (($this->_current_token == SPREADSHEET_EXCEL_WRITER_ADD) or
($this->_current_token == SPREADSHEET_EXCEL_WRITER_SUB))
{
if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_ADD)
{
$this->_advance();
$result2 = $this->_term();
if($this->isError($result2)) {
return($result2);
}
$result = $this->_createTree('ptgAdd', $result, $result2);
}
else
{
$this->_advance();
$result2 = $this->_term();
if($this->isError($result2)) {
return($result2);
}
$result = $this->_createTree('ptgSub', $result, $result2);
}
}
return($result);
}
/**
* This function just introduces a ptgParen element in the tree, so that Excel
* doesn't get confused when working with a parenthesized formula afterwards.
*
* @access private
* @see _fact()
* @return mixed The parsed ptg'd tree
*/
function _parenthesizedExpression()
{
$result = $this->_createTree('ptgParen', $this->_expression(), '');
return($result);
}
/**
* It parses a term. It assumes the following rule:
* Term -> Fact [("*" | "/") Fact]
*
* @access private
* @return mixed The parsed ptg'd tree
*/
function _term()
{
$result = $this->_fact();
if($this->isError($result)) {
return($result);
}
while (($this->_current_token == SPREADSHEET_EXCEL_WRITER_MUL) or
($this->_current_token == SPREADSHEET_EXCEL_WRITER_DIV))
{
if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_MUL)
{
$this->_advance();
$result2 = $this->_fact();
if($this->isError($result2)) {
return($result2);
}
$result = $this->_createTree('ptgMul', $result, $result2);
}
else
{
$this->_advance();
$result2 = $this->_fact();
if($this->isError($result2)) {
return($result2);
}
$result = $this->_createTree('ptgDiv', $result, $result2);
}
}
return($result);
}
/**
* It parses a factor. It assumes the following rule:
* Fact -> ( Expr )
* | CellRef
* | CellRange
* | Number
* | Function
*
* @access private
* @return mixed The parsed ptg'd tree
*/
function _fact()
{
if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_OPEN)
{
$this->_advance(); // eat the "("
$result = $this->_parenthesizedExpression();
if ($this->_current_token != SPREADSHEET_EXCEL_WRITER_CLOSE) {
return($this->raiseError("')' token expected."));
}
$this->_advance(); // eat the ")"
return $result;
}
// if it's a reference
if (preg_match('/^\$?[A-I]?[A-Z]\$?[0-9]+$/',$this->_current_token))
{
$result = $this->_createTree($this->_current_token, '', '');
$this->_advance();
return $result;
}
// If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1)
elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\![A-I]?[A-Z][0-9]+$/",$this->_current_token))
{
$result = $this->_createTree($this->_current_token, '', '');
$this->_advance();
return $result;
}
// if it's a range
elseif (preg_match("/^(\$)?[A-I]?[A-Z](\$)?[0-9]+:(\$)?[A-I]?[A-Z](\$)?[0-9]+$/",$this->_current_token) or
preg_match("/^(\$)?[A-I]?[A-Z](\$)?[0-9]+\.\.(\$)?[A-I]?[A-Z](\$)?[0-9]+$/",$this->_current_token))
{
$result = $this->_current_token;
$this->_advance();
return $result;
}
// If it's an external range (Sheet1!A1:B2)
elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\!([A-I]?[A-Z])?[0-9]+:([A-I]?[A-Z])?[0-9]+$/",$this->_current_token))
{
$result = $this->_current_token;
$this->_advance();
return($result);
}
elseif (is_numeric($this->_current_token))
{
$result = $this->_createTree($this->_current_token, '', '');
$this->_advance();
return($result);
}
// if it's a function call
elseif (eregi("^[A-Z0-9\xc0-\xdc\.]+$",$this->_current_token))
{
$result = $this->_func();
return($result);
}
return($this->raiseError("Sintactic error: ".$this->_current_token.", lookahead: ".
$this->_lookahead.", current char: ".$this->_current_char));
}
/**
* It parses a function call. It assumes the following rule:
* Func -> ( Expr [,Expr]* )
*
* @access private
*/
function _func()
{
$num_args = 0; // number of arguments received
$function = $this->_current_token;
$this->_advance();
$this->_advance(); // eat the "("
while($this->_current_token != ')')
{
if($num_args > 0)
{
if($this->_current_token == SPREADSHEET_EXCEL_WRITER_COMA) {
$this->_advance(); // eat the ","
}
else {
return new PEAR_Error("Sintactic error: coma expected in ".
"function $function, {$num_args}º arg");
}
$result2 = $this->_condition();
if($this->isError($result2)) {
return($result2);
}
$result = $this->_createTree('arg', $result, $result2);
}
else // first argument
{
$result2 = $this->_condition();
if($this->isError($result2)) {
return($result2);
}
$result = $this->_createTree('arg', '', $result2);
}
$num_args++;
}
$args = $this->_functions[$function][1];
// If fixed number of args eg. TIME($i,$j,$k). Check that the number of args is valid.
if (($args >= 0) and ($args != $num_args))
{
return($this->raiseError("Incorrect number of arguments in function $function() "));
}
$result = $this->_createTree($function, $result, '');
$this->_advance(); // eat the ")"
return($result);
}
/**
* Creates a tree. In fact an array which may have one or two arrays (sub-trees)
* as elements.
*
* @access private
* @param mixed $value The value of this node.
* @param mixed $left The left array (sub-tree) or a final node.
* @param mixed $right The right array (sub-tree) or a final node.
*/
function _createTree($value, $left, $right)
{
return(array('value' => $value, 'left' => $left, 'right' => $right));
}
/**
* Builds a string containing the tree in reverse polish notation (What you
* would use in a HP calculator stack).
* The following tree:
*
* +
* / \
* 2 3
*
* produces: "23+"
*
* The following tree:
*
* +
* / \
* 3 *
* / \
* 6 A1
*
* produces: "36A1*+"
*
* In fact all operands, functions, references, etc... are written as ptg's
*
* @access public
* @param array $tree The optional tree to convert.
* @return string The tree in reverse polish notation
*/
function toReversePolish($tree = array())
{
$polish = ""; // the string we are going to return
if (empty($tree)) // If it's the first call use _parse_tree
{
$tree = $this->_parse_tree;
}
if (is_array($tree['left']))
{
$converted_tree = $this->toReversePolish($tree['left']);
if($this->isError($converted_tree)) {
return($converted_tree);
}
$polish .= $converted_tree;
}
elseif($tree['left'] != '') // It's a final node
{
$converted_tree = $this->_convert($tree['left']);
if($this->isError($converted_tree)) {
return($converted_tree);
}
$polish .= $converted_tree;
}
if (is_array($tree['right']))
{
$converted_tree = $this->toReversePolish($tree['right']);
if($this->isError($converted_tree)) {
return($converted_tree);
}
$polish .= $converted_tree;
}
elseif($tree['right'] != '') // It's a final node
{
$converted_tree = $this->_convert($tree['right']);
if($this->isError($converted_tree)) {
return($converted_tree);
}
$polish .= $converted_tree;
}
$converted_tree = $this->_convert($tree['value']);
if($this->isError($converted_tree)) {
return($converted_tree);
}
$polish .= $converted_tree;
return($polish);
}
}
?>
/trunk/jrest/lib/Spreadsheet/Excel/Writer/Workbook.php
New file
0,0 → 1,1042
<?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_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 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');
require_once('Worksheet.php');
require_once('Parser.php');
 
/**
* Class for generating Excel Spreadsheets
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer_Workbook extends Spreadsheet_Excel_Writer_BIFFwriter
{
/**
* Filename for the Workbook
* @var string
*/
var $_filename;
 
/**
* Formula parser
* @var object Parser
*/
var $_parser;
 
/**
* Flag for 1904 date system
* @var integer
*/
var $_1904;
 
/**
* The active worksheet of the workbook (0 indexed)
* @var integer
*/
var $_activesheet;
 
/**
* 1st displayed worksheet in the workbook (0 indexed)
* @var integer
*/
var $_firstsheet;
 
/**
* Number of workbook tabs selected
* @var integer
*/
var $_selected;
 
/**
* Index for creating adding new formats to the workbook
* @var integer
*/
var $_xf_index;
 
/**
* Flag for preventing close from being called twice.
* @var integer
* @see close()
*/
var $_fileclosed;
 
/**
* The BIFF file size for the workbook.
* @var integer
* @see _calcSheetOffsets()
*/
var $_biffsize;
 
/**
* The default sheetname for all sheets created.
* @var string
*/
var $_sheetname;
 
/**
* The default XF format.
* @var object Format
*/
var $_tmp_format;
 
/**
* Array containing references to all of this workbook's worksheets
* @var array
*/
var $_worksheets;
 
/**
* Array of sheetnames for creating the EXTERNSHEET records
* @var array
*/
var $_sheetnames;
 
/**
* Array containing references to all of this workbook's formats
* @var array
*/
var $_formats;
 
/**
* Array containing the colour palette
* @var array
*/
var $_palette;
 
/**
* The default format for URLs.
* @var object Format
*/
var $_url_format;
 
/**
* Class constructor
*
* @param string filename for storing the workbook. "-" for writing to stdout.
* @access public
*/
function Spreadsheet_Excel_Writer_Workbook($filename)
{
// It needs to call its parent's constructor explicitly
$this->Spreadsheet_Excel_Writer_BIFFwriter();
$this->_filename = $filename;
$this->_parser =& new Spreadsheet_Excel_Writer_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 Spreadsheet_Excel_Writer_Format();
$this->_worksheets = array();
$this->_sheetnames = array();
$this->_formats = array();
$this->_palette = array();
// Add the default format for hyperlinks
$this->_url_format =& $this->addFormat(array('color' => 'blue', 'underline' => 1));
$this->_setPaletteXl97();
}
/**
* Calls finalization methods.
* This method should always be the last one to be called on every workbook
*
* @access public
*/
function close()
{
if ($this->_fileclosed) { // Prevent close() from being called twice.
return;
}
$this->_storeWorkbook();
$this->_fileclosed = 1;
}
/**
* An accessor for the _worksheets[] array
* Returns an array of the worksheet objects in a workbook
* It actually calls to worksheets()
*
* @access public
* @see worksheets()
* @return array
*/
function sheets()
{
return $this->worksheets();
}
/**
* An accessor for the _worksheets[] array.
* Returns an array of the worksheet objects in a workbook
*
* @access public
* @return array
*/
function worksheets()
{
return($this->_worksheets);
}
/**
* Add a new worksheet to the Excel workbook.
* If no name is given the name of the worksheet will be Sheeti$i, with
* $i in [1..].
*
* @access public
* @param string $name the optional name of the worksheet
* @return &Spreadsheet_Excel_Writer_Worksheet reference to a worksheet object
*/
function &addWorksheet($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) {
$this->raiseError("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]->getName()) {
$this->raiseError("Worksheet '$name' already exists");
}
}
$worksheet = new Spreadsheet_Excel_Writer_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->setExtSheet($name, $index); // Register worksheet name with parser
return($worksheet);
}
/**
* Add a new format to the Excel workbook.
* Also, pass any properties to the Format constructor.
*
* @access public
* @param array $properties array with properties for initializing the format.
* @return &Spreadsheet_Excel_Writer_Format reference to an Excel Format
*/
function &addFormat($properties = array())
{
$format = new Spreadsheet_Excel_Writer_Format($this->_xf_index,$properties);
$this->_xf_index += 1;
$this->_formats[] = &$format;
return($format);
}
/**
* 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 setCustomColor($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) {
// TODO: assign real error codes
$this->raiseError("Color index $index outside range: 8 <= index <= 64",0,PEAR_ERROR_DIE);
}
// Check that the colour components are in the right range
if ( ($red < 0 or $red > 255) or
($green < 0 or $green > 255) or
($blue < 0 or $blue > 255) )
{
$this->raiseError("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.
*
* @access private
*/
function _setPaletteXl97()
{
$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
);
}
/**
* Assemble worksheets into a workbook and send the BIFF data to an OLE
* storage.
*
* @access private
*/
function _storeWorkbook()
{
// 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->_storeBof(0x0005);
$this->_storeExterns(); // For print area and repeat rows
$this->_storeNames(); // For print area and repeat rows
$this->_storeWindow1();
$this->_store1904();
$this->_storeAllFonts();
$this->_storeAllNumFormats();
$this->_storeAllXfs();
$this->_storeAllStyles();
$this->_storePalette();
$this->_calcSheetOffsets();
// Add BOUNDSHEET records
for($i=0; $i < count($this->_worksheets); $i++) {
$this->_storeBoundsheet($this->_worksheets[$i]->name,$this->_worksheets[$i]->offset);
}
// End Workbook globals
$this->_storeEof();
// Store the workbook in an OLE container
$this->_storeOLEFile();
}
/**
* Store the workbook in an OLE container if the total size of the workbook data
* is less than ~ 7MB.
*
* @access private
*/
function _storeOLEFile()
{
$OLE = new Spreadsheet_Excel_Writer_OLEwriter($this->_filename);
$this->_tmp_filename = $OLE->_tmp_filename;
// Write Worksheet data if data <~ 7MB
if ($OLE->setSize($this->_biffsize))
{
$OLE->writeHeader();
$OLE->write($this->_data);
foreach($this->_worksheets as $sheet)
{
while ($tmp = $sheet->getData()) {
$OLE->write($tmp);
}
}
}
$OLE->close();
}
/**
* Calculate offsets for Worksheet BOF records.
*
* @access private
*/
function _calcSheetOffsets()
{
$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.
*
* @access private
*/
function _storeAllFonts()
{
// tmp_format is added by the constructor. We use this to write the default XF's
$format = $this->_tmp_format;
$font = $format->getFont();
// 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->getFontKey(); // 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]->getFontKey();
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]->getFont();
$this->_append($font);
}
}
}
/**
* Store user defined numerical formats i.e. FORMAT records
*
* @access private
*/
function _storeAllNumFormats()
{
// 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->_storeNumFormat($num_format,$index);
$index++;
}
}
/**
* Write all XF records.
*
* @access private
*/
function _storeAllXfs()
{
// _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->getXf('style'); // Style XF
$this->_append($xf);
}
$xf = $format->getXf('cell'); // Cell XF
$this->_append($xf);
// User defined XFs
for($i=0; $i < count($this->_formats); $i++) {
$xf = $this->_formats[$i]->getXf('cell');
$this->_append($xf);
}
}
/**
* Write all STYLE records.
*
* @access private
*/
function _storeAllStyles()
{
$this->_storeStyle();
}
/**
* Write the EXTERNCOUNT and EXTERNSHEET records. These are used as indexes for
* the NAME records.
*
* @access private
*/
function _storeExterns()
{
// Create EXTERNCOUNT with number of worksheets
$this->_storeExterncount(count($this->_worksheets));
// Create EXTERNSHEET for each worksheet
foreach ($this->_sheetnames as $sheetname) {
$this->_storeExternsheet($sheetname);
}
}
/**
* Write the NAME record to define the print area and the repeat rows and cols.
*
* @access private
*/
function _storeNames()
{
// 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->_storeNameShort(
$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) and isset($colmin)) {
// Row and column titles have been defined.
// Row title has been defined.
$this->_storeNameLong(
$worksheet->index,
0x07, // NAME type
$rowmin,
$rowmax,
$colmin,
$colmax
);
}
elseif (isset($rowmin)) {
// Row title has been defined.
$this->_storeNameShort(
$worksheet->index,
0x07, // NAME type
$rowmin,
$rowmax,
0x00,
0xff
);
}
elseif (isset($colmin)) {
// Column title has been defined.
$this->_storeNameShort(
$worksheet->index,
0x07, // NAME type
0x0000,
0x3fff,
$colmin,
$colmax
);
}
else {
// Print title hasn't been defined.
}
}
}
/******************************************************************************
*
* BIFF RECORDS
*
*/
/**
* Write Excel BIFF WINDOW1 record.
*
* @access private
*/
function _storeWindow1()
{
$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
* @access private
*/
function _storeBoundsheet($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.
*
* @access private
*/
function _storeStyle()
{
$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
* @access private
*/
function _storeNumFormat($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.
*
* @access private
*/
function _store1904()
{
$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
* @access private
*/
function _storeExterncount($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
* @access private
*/
function _storeExternsheet($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
* @access private
*/
function _storeNameShort($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 shares a lot of code with
* _storeNameShort() 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
* @access private
*/
function _storeNameLong($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.
*
* @access private
*/
function _storePalette()
{
$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);
}
}
?>
/trunk/jrest/lib/Spreadsheet/Excel/Writer.php
New file
0,0 → 1,75
<?php
/*
* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
*
* 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_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 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('PEAR.php');
require_once('Writer/Workbook.php');
 
/**
* Class for writing Excel Spreadsheets. This class should change COMPLETELY.
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer extends Spreadsheet_Excel_Writer_Workbook
{
/**
* The constructor. It just creates a Workbook
*
* @param string $filename The optional filename for the Workbook.
* @return Spreadsheet_Excel_Writer_Workbook The Workbook created
*/
function Spreadsheet_Excel_Writer($filename = '')
{
$this->_filename = $filename;
$this->Spreadsheet_Excel_Writer_Workbook($filename);
}
 
/**
* Send HTTP headers for the Excel file.
*
* @param string $filename The filename to use for HTTP headers
* @access public
*/
function send($filename)
{
header("Content-type: application/vnd.ms-excel");
header("Content-Disposition: attachment; filename=$filename");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0,pre-check=0");
header("Pragma: public");
}
 
}
?>
/trunk/jrest/lib/DB.php
New file
0,0 → 1,1114
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Stig Bakken <ssb@php.net> |
// | Tomas V.V.Cox <cox@idecnet.com> |
// | Maintainer: Daniel Convissor <danielc@php.net> |
// +----------------------------------------------------------------------+
//
// $Id$
//
// Database independent query interface.
 
 
require_once 'PEAR.php';
 
// {{{ constants
// {{{ error codes
 
/*
* The method mapErrorCode in each DB_dbtype implementation maps
* native error codes to one of these.
*
* If you add an error code here, make sure you also add a textual
* version of it in DB::errorMessage().
*/
define('DB_OK', 1);
define('DB_ERROR', -1);
define('DB_ERROR_SYNTAX', -2);
define('DB_ERROR_CONSTRAINT', -3);
define('DB_ERROR_NOT_FOUND', -4);
define('DB_ERROR_ALREADY_EXISTS', -5);
define('DB_ERROR_UNSUPPORTED', -6);
define('DB_ERROR_MISMATCH', -7);
define('DB_ERROR_INVALID', -8);
define('DB_ERROR_NOT_CAPABLE', -9);
define('DB_ERROR_TRUNCATED', -10);
define('DB_ERROR_INVALID_NUMBER', -11);
define('DB_ERROR_INVALID_DATE', -12);
define('DB_ERROR_DIVZERO', -13);
define('DB_ERROR_NODBSELECTED', -14);
define('DB_ERROR_CANNOT_CREATE', -15);
define('DB_ERROR_CANNOT_DELETE', -16);
define('DB_ERROR_CANNOT_DROP', -17);
define('DB_ERROR_NOSUCHTABLE', -18);
define('DB_ERROR_NOSUCHFIELD', -19);
define('DB_ERROR_NEED_MORE_DATA', -20);
define('DB_ERROR_NOT_LOCKED', -21);
define('DB_ERROR_VALUE_COUNT_ON_ROW', -22);
define('DB_ERROR_INVALID_DSN', -23);
define('DB_ERROR_CONNECT_FAILED', -24);
define('DB_ERROR_EXTENSION_NOT_FOUND',-25);
define('DB_ERROR_ACCESS_VIOLATION', -26);
define('DB_ERROR_NOSUCHDB', -27);
define('DB_ERROR_CONSTRAINT_NOT_NULL',-29);
 
 
// }}}
// {{{ prepared statement-related
 
 
/*
* These constants are used when storing information about prepared
* statements (using the "prepare" method in DB_dbtype).
*
* The prepare/execute model in DB is mostly borrowed from the ODBC
* extension, in a query the "?" character means a scalar parameter.
* There are two extensions though, a "&" character means an opaque
* parameter. An opaque parameter is simply a file name, the real
* data are in that file (useful for putting uploaded files into your
* database and such). The "!" char means a parameter that must be
* left as it is.
* They modify the quote behavoir:
* DB_PARAM_SCALAR (?) => 'original string quoted'
* DB_PARAM_OPAQUE (&) => 'string from file quoted'
* DB_PARAM_MISC (!) => original string
*/
define('DB_PARAM_SCALAR', 1);
define('DB_PARAM_OPAQUE', 2);
define('DB_PARAM_MISC', 3);
 
 
// }}}
// {{{ binary data-related
 
 
/*
* These constants define different ways of returning binary data
* from queries. Again, this model has been borrowed from the ODBC
* extension.
*
* DB_BINMODE_PASSTHRU sends the data directly through to the browser
* when data is fetched from the database.
* DB_BINMODE_RETURN lets you return data as usual.
* DB_BINMODE_CONVERT returns data as well, only it is converted to
* hex format, for example the string "123" would become "313233".
*/
define('DB_BINMODE_PASSTHRU', 1);
define('DB_BINMODE_RETURN', 2);
define('DB_BINMODE_CONVERT', 3);
 
 
// }}}
// {{{ fetch modes
 
 
/**
* This is a special constant that tells DB the user hasn't specified
* any particular get mode, so the default should be used.
*/
define('DB_FETCHMODE_DEFAULT', 0);
 
/**
* Column data indexed by numbers, ordered from 0 and up
*/
define('DB_FETCHMODE_ORDERED', 1);
 
/**
* Column data indexed by column names
*/
define('DB_FETCHMODE_ASSOC', 2);
 
/**
* Column data as object properties
*/
define('DB_FETCHMODE_OBJECT', 3);
 
/**
* For multi-dimensional results: normally the first level of arrays
* is the row number, and the second level indexed by column number or name.
* DB_FETCHMODE_FLIPPED switches this order, so the first level of arrays
* is the column name, and the second level the row number.
*/
define('DB_FETCHMODE_FLIPPED', 4);
 
/* for compatibility */
define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED);
define('DB_GETMODE_ASSOC', DB_FETCHMODE_ASSOC);
define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED);
 
 
// }}}
// {{{ tableInfo() && autoPrepare()-related
 
 
/**
* these are constants for the tableInfo-function
* they are bitwised or'ed. so if there are more constants to be defined
* in the future, adjust DB_TABLEINFO_FULL accordingly
*/
define('DB_TABLEINFO_ORDER', 1);
define('DB_TABLEINFO_ORDERTABLE', 2);
define('DB_TABLEINFO_FULL', 3);
 
/*
* Used by autoPrepare()
*/
define('DB_AUTOQUERY_INSERT', 1);
define('DB_AUTOQUERY_UPDATE', 2);
 
 
// }}}
// {{{ portability modes
 
 
/**
* Portability: turn off all portability features.
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_NONE', 0);
 
/**
* Portability: convert names of tables and fields to lower case
* when using the get*(), fetch*() and tableInfo() methods.
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_LOWERCASE', 1);
 
/**
* Portability: right trim the data output by get*() and fetch*().
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_RTRIM', 2);
 
/**
* Portability: force reporting the number of rows deleted.
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_DELETE_COUNT', 4);
 
/**
* Portability: enable hack that makes numRows() work in Oracle.
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_NUMROWS', 8);
 
/**
* Portability: makes certain error messages in certain drivers compatible
* with those from other DBMS's.
*
* + mysql, mysqli: change unique/primary key constraints
* DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT
*
* + odbc(access): MS's ODBC driver reports 'no such field' as code
* 07001, which means 'too few parameters.' When this option is on
* that code gets mapped to DB_ERROR_NOSUCHFIELD.
*
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_ERRORS', 16);
 
/**
* Portability: convert null values to empty strings in data output by
* get*() and fetch*().
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_NULL_TO_EMPTY', 32);
 
/**
* Portability: turn on all portability features.
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_ALL', 63);
 
// }}}
 
 
// }}}
// {{{ class DB
 
/**
* The main "DB" class is simply a container class with some static
* methods for creating DB objects as well as some utility functions
* common to all parts of DB.
*
* The object model of DB is as follows (indentation means inheritance):
*
* DB The main DB class. This is simply a utility class
* with some "static" methods for creating DB objects as
* well as common utility functions for other DB classes.
*
* DB_common The base for each DB implementation. Provides default
* | implementations (in OO lingo virtual methods) for
* | the actual DB implementations as well as a bunch of
* | query utility functions.
* |
* +-DB_mysql The DB implementation for MySQL. Inherits DB_common.
* When calling DB::factory or DB::connect for MySQL
* connections, the object returned is an instance of this
* class.
*
* @package DB
* @author Stig Bakken <ssb@php.net>
* @author Tomas V.V.Cox <cox@idecnet.com>
* @since PHP 4.0
* @version $Id$
* @category Database
*/
class DB
{
// {{{ &factory()
 
/**
* Create a new DB object for the specified database type.
*
* Allows creation of a DB_<driver> object from which the object's
* methods can be utilized without actually connecting to a database.
*
* @param string $type database type, for example "mysql"
* @param array $options associative array of option names and values
*
* @return object a new DB object. On error, an error object.
*
* @see DB_common::setOption()
* @access public
*/
function &factory($type, $options = false)
{
if (!is_array($options)) {
$options = array('persistent' => $options);
}
 
if (isset($options['debug']) && $options['debug'] >= 2) {
// expose php errors with sufficient debug level
include_once "DB/{$type}.php";
} else {
@include_once "DB/{$type}.php";
}
 
$classname = "DB_${type}";
 
if (!class_exists($classname)) {
$tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null,
"Unable to include the DB/{$type}.php file",
'DB_Error', true);
return $tmp;
}
 
@$obj =& new $classname;
 
foreach ($options as $option => $value) {
$test = $obj->setOption($option, $value);
if (DB::isError($test)) {
return $test;
}
}
 
return $obj;
}
 
// }}}
// {{{ &connect()
 
/**
* Create a new DB object and connect to the specified database.
*
* Example 1.
* <code> <?php
* require_once 'DB.php';
*
* $dsn = 'mysql://user:password@host/database'
* $options = array(
* 'debug' => 2,
* 'portability' => DB_PORTABILITY_ALL,
* );
*
* $dbh =& DB::connect($dsn, $options);
* if (DB::isError($dbh)) {
* die($dbh->getMessage());
* }
* ?></code>
*
* @param mixed $dsn string "data source name" or an array in the
* format returned by DB::parseDSN()
*
* @param array $options an associative array of option names and
* their values
*
* @return object a newly created DB connection object, or a DB
* error object on error
*
* @see DB::parseDSN(), DB_common::setOption(), DB::isError()
* @access public
*/
function &connect($dsn, $options = array())
{
$dsninfo = DB::parseDSN($dsn);
$type = $dsninfo['phptype'];
 
if (!is_array($options)) {
/*
* For backwards compatibility. $options used to be boolean,
* indicating whether the connection should be persistent.
*/
$options = array('persistent' => $options);
}
 
if (isset($options['debug']) && $options['debug'] >= 2) {
// expose php errors with sufficient debug level
include_once "DB/${type}.php";
} else {
@include_once "DB/${type}.php";
}
 
$classname = "DB_${type}";
if (!class_exists($classname)) {
$tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null,
"Unable to include the DB/{$type}.php file for `$dsn'",
'DB_Error', true);
return $tmp;
}
 
@$obj =& new $classname;
 
foreach ($options as $option => $value) {
$test = $obj->setOption($option, $value);
if (DB::isError($test)) {
return $test;
}
}
 
$err = $obj->connect($dsninfo, $obj->getOption('persistent'));
if (DB::isError($err)) {
$err->addUserInfo($dsn);
return $err;
}
 
return $obj;
}
 
// }}}
// {{{ apiVersion()
 
/**
* Return the DB API version
*
* @return int the DB API version number
*
* @access public
*/
function apiVersion()
{
return 2;
}
 
// }}}
// {{{ isError()
 
/**
* Tell whether a result code from a DB method is an error
*
* @param int $value result code
*
* @return bool whether $value is an error
*
* @access public
*/
function isError($value)
{
return is_a($value, 'DB_Error');
}
 
// }}}
// {{{ isConnection()
 
/**
* Tell whether a value is a DB connection
*
* @param mixed $value value to test
*
* @return bool whether $value is a DB connection
*
* @access public
*/
function isConnection($value)
{
return (is_object($value) &&
is_subclass_of($value, 'db_common') &&
method_exists($value, 'simpleQuery'));
}
 
// }}}
// {{{ isManip()
 
/**
* Tell whether a query is a data manipulation query (insert,
* update or delete) or a data definition query (create, drop,
* alter, grant, revoke).
*
* @access public
*
* @param string $query the query
*
* @return boolean whether $query is a data manipulation query
*/
function isManip($query)
{
$manips = 'INSERT|UPDATE|DELETE|LOAD DATA|'.'REPLACE|CREATE|DROP|'.
'ALTER|GRANT|REVOKE|'.'LOCK|UNLOCK';
if (preg_match('/^\s*"?('.$manips.')\s+/i', $query)) {
return true;
}
return false;
}
 
// }}}
// {{{ errorMessage()
 
/**
* Return a textual error message for a DB error code
*
* @param integer $value error code
*
* @return string error message, or false if the error code was
* not recognized
*/
function errorMessage($value)
{
static $errorMessages;
if (!isset($errorMessages)) {
$errorMessages = array(
DB_ERROR => 'unknown error',
DB_ERROR_ALREADY_EXISTS => 'already exists',
DB_ERROR_CANNOT_CREATE => 'can not create',
DB_ERROR_CANNOT_DELETE => 'can not delete',
DB_ERROR_CANNOT_DROP => 'can not drop',
DB_ERROR_CONSTRAINT => 'constraint violation',
DB_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
DB_ERROR_DIVZERO => 'division by zero',
DB_ERROR_INVALID => 'invalid',
DB_ERROR_INVALID_DATE => 'invalid date or time',
DB_ERROR_INVALID_NUMBER => 'invalid number',
DB_ERROR_MISMATCH => 'mismatch',
DB_ERROR_NODBSELECTED => 'no database selected',
DB_ERROR_NOSUCHFIELD => 'no such field',
DB_ERROR_NOSUCHTABLE => 'no such table',
DB_ERROR_NOT_CAPABLE => 'DB backend not capable',
DB_ERROR_NOT_FOUND => 'not found',
DB_ERROR_NOT_LOCKED => 'not locked',
DB_ERROR_SYNTAX => 'syntax error',
DB_ERROR_UNSUPPORTED => 'not supported',
DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row',
DB_ERROR_INVALID_DSN => 'invalid DSN',
DB_ERROR_CONNECT_FAILED => 'connect failed',
DB_OK => 'no error',
DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied',
DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found',
DB_ERROR_NOSUCHDB => 'no such database',
DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions',
DB_ERROR_TRUNCATED => 'truncated'
);
}
 
if (DB::isError($value)) {
$value = $value->getCode();
}
 
return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[DB_ERROR];
}
 
// }}}
// {{{ parseDSN()
 
/**
* Parse a data source name.
*
* Additional keys can be added by appending a URI query string to the
* end of the DSN.
*
* The format of the supplied DSN is in its fullest form:
* <code>
* phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true
* </code>
*
* Most variations are allowed:
* <code>
* phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644
* phptype://username:password@hostspec/database_name
* phptype://username:password@hostspec
* phptype://username@hostspec
* phptype://hostspec/database
* phptype://hostspec
* phptype(dbsyntax)
* phptype
* </code>
*
* @param string $dsn Data Source Name to be parsed
*
* @return array an associative array with the following keys:
* + phptype: Database backend used in PHP (mysql, odbc etc.)
* + dbsyntax: Database used with regards to SQL syntax etc.
* + protocol: Communication protocol to use (tcp, unix etc.)
* + hostspec: Host specification (hostname[:port])
* + database: Database to use on the DBMS server
* + username: User name for login
* + password: Password for login
*
* @author Tomas V.V.Cox <cox@idecnet.com>
*/
function parseDSN($dsn)
{
$parsed = array(
'phptype' => false,
'dbsyntax' => false,
'username' => false,
'password' => false,
'protocol' => false,
'hostspec' => false,
'port' => false,
'socket' => false,
'database' => false,
);
 
if (is_array($dsn)) {
$dsn = array_merge($parsed, $dsn);
if (!$dsn['dbsyntax']) {
$dsn['dbsyntax'] = $dsn['phptype'];
}
return $dsn;
}
 
// Find phptype and dbsyntax
if (($pos = strpos($dsn, '://')) !== false) {
$str = substr($dsn, 0, $pos);
$dsn = substr($dsn, $pos + 3);
} else {
$str = $dsn;
$dsn = null;
}
 
// Get phptype and dbsyntax
// $str => phptype(dbsyntax)
if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {
$parsed['phptype'] = $arr[1];
$parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2];
} else {
$parsed['phptype'] = $str;
$parsed['dbsyntax'] = $str;
}
 
if (!count($dsn)) {
return $parsed;
}
 
// Get (if found): username and password
// $dsn => username:password@protocol+hostspec/database
if (($at = strrpos($dsn,'@')) !== false) {
$str = substr($dsn, 0, $at);
$dsn = substr($dsn, $at + 1);
if (($pos = strpos($str, ':')) !== false) {
$parsed['username'] = rawurldecode(substr($str, 0, $pos));
$parsed['password'] = rawurldecode(substr($str, $pos + 1));
} else {
$parsed['username'] = rawurldecode($str);
}
}
 
// Find protocol and hostspec
 
// $dsn => proto(proto_opts)/database
if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) {
$proto = $match[1];
$proto_opts = $match[2] ? $match[2] : false;
$dsn = $match[3];
 
// $dsn => protocol+hostspec/database (old format)
} else {
if (strpos($dsn, '+') !== false) {
list($proto, $dsn) = explode('+', $dsn, 2);
}
if (strpos($dsn, '/') !== false) {
list($proto_opts, $dsn) = explode('/', $dsn, 2);
} else {
$proto_opts = $dsn;
$dsn = null;
}
}
 
// process the different protocol options
$parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp';
$proto_opts = rawurldecode($proto_opts);
if ($parsed['protocol'] == 'tcp') {
if (strpos($proto_opts, ':') !== false) {
list($parsed['hostspec'], $parsed['port']) = explode(':', $proto_opts);
} else {
$parsed['hostspec'] = $proto_opts;
}
} elseif ($parsed['protocol'] == 'unix') {
$parsed['socket'] = $proto_opts;
}
 
// Get dabase if any
// $dsn => database
if ($dsn) {
// /database
if (($pos = strpos($dsn, '?')) === false) {
$parsed['database'] = $dsn;
// /database?param1=value1&param2=value2
} else {
$parsed['database'] = substr($dsn, 0, $pos);
$dsn = substr($dsn, $pos + 1);
if (strpos($dsn, '&') !== false) {
$opts = explode('&', $dsn);
} else { // database?param1=value1
$opts = array($dsn);
}
foreach ($opts as $opt) {
list($key, $value) = explode('=', $opt);
if (!isset($parsed[$key])) {
// don't allow params overwrite
$parsed[$key] = rawurldecode($value);
}
}
}
}
 
return $parsed;
}
 
// }}}
// {{{ assertExtension()
 
/**
* Load a PHP database extension if it is not loaded already.
*
* @access public
*
* @param string $name the base name of the extension (without the .so or
* .dll suffix)
*
* @return boolean true if the extension was already or successfully
* loaded, false if it could not be loaded
*/
function assertExtension($name)
{
if (!extension_loaded($name)) {
$dlext = OS_WINDOWS ? '.dll' : '.so';
$dlprefix = OS_WINDOWS ? 'php_' : '';
@dl($dlprefix . $name . $dlext);
return extension_loaded($name);
}
return true;
}
// }}}
}
 
// }}}
// {{{ class DB_Error
 
/**
* DB_Error implements a class for reporting portable database error
* messages.
*
* @package DB
* @author Stig Bakken <ssb@php.net>
*/
class DB_Error extends PEAR_Error
{
// {{{ constructor
 
/**
* DB_Error constructor.
*
* @param mixed $code DB error code, or string with error message.
* @param integer $mode what "error mode" to operate in
* @param integer $level what error level to use for $mode & PEAR_ERROR_TRIGGER
* @param mixed $debuginfo additional debug info, such as the last query
*
* @access public
*
* @see PEAR_Error
*/
function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
$level = E_USER_NOTICE, $debuginfo = null)
{
if (is_int($code)) {
$this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code, $mode, $level, $debuginfo);
} else {
$this->PEAR_Error("DB Error: $code", DB_ERROR, $mode, $level, $debuginfo);
}
}
// }}}
}
 
// }}}
// {{{ class DB_result
 
/**
* This class implements a wrapper for a DB result set.
* A new instance of this class will be returned by the DB implementation
* after processing a query that returns data.
*
* @package DB
* @author Stig Bakken <ssb@php.net>
*/
class DB_result
{
// {{{ properties
 
var $dbh;
var $result;
var $row_counter = null;
 
/**
* for limit queries, the row to start fetching
* @var integer
*/
var $limit_from = null;
 
/**
* for limit queries, the number of rows to fetch
* @var integer
*/
var $limit_count = null;
 
// }}}
// {{{ constructor
 
/**
* DB_result constructor.
* @param resource &$dbh DB object reference
* @param resource $result result resource id
* @param array $options assoc array with optional result options
*/
function DB_result(&$dbh, $result, $options = array())
{
$this->dbh = &$dbh;
$this->result = $result;
foreach ($options as $key => $value) {
$this->setOption($key, $value);
}
$this->limit_type = $dbh->features['limit'];
$this->autofree = $dbh->options['autofree'];
$this->fetchmode = $dbh->fetchmode;
$this->fetchmode_object_class = $dbh->fetchmode_object_class;
}
 
function setOption($key, $value = null)
{
switch ($key) {
case 'limit_from':
$this->limit_from = $value; break;
case 'limit_count':
$this->limit_count = $value; break;
}
}
 
// }}}
// {{{ fetchRow()
 
/**
* Fetch a row of data and return it by reference into an array.
*
* The type of array returned can be controlled either by setting this
* method's <var>$fetchmode</var> parameter or by changing the default
* fetch mode setFetchMode() before calling this method.
*
* There are two options for standardizing the information returned
* from databases, ensuring their values are consistent when changing
* DBMS's. These portability options can be turned on when creating a
* new DB object or by using setOption().
*
* + <samp>DB_PORTABILITY_LOWERCASE</samp>
* convert names of fields to lower case
*
* + <samp>DB_PORTABILITY_RTRIM</samp>
* right trim the data
*
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch
*
* @return array a row of data, null on no more rows or PEAR_Error
* object on error
*
* @see DB_common::setOption(), DB_common::setFetchMode()
* @access public
*/
function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null)
{
if ($fetchmode === DB_FETCHMODE_DEFAULT) {
$fetchmode = $this->fetchmode;
}
if ($fetchmode === DB_FETCHMODE_OBJECT) {
$fetchmode = DB_FETCHMODE_ASSOC;
$object_class = $this->fetchmode_object_class;
}
if ($this->limit_from !== null) {
if ($this->row_counter === null) {
$this->row_counter = $this->limit_from;
// Skip rows
if ($this->limit_type == false) {
$i = 0;
while ($i++ < $this->limit_from) {
$this->dbh->fetchInto($this->result, $arr, $fetchmode);
}
}
}
if ($this->row_counter >= (
$this->limit_from + $this->limit_count))
{
if ($this->autofree) {
$this->free();
}
$tmp = null;
return $tmp;
}
if ($this->limit_type == 'emulate') {
$rownum = $this->row_counter;
}
$this->row_counter++;
}
$res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum);
if ($res === DB_OK) {
if (isset($object_class)) {
// default mode specified in DB_common::fetchmode_object_class property
if ($object_class == 'stdClass') {
$arr = (object) $arr;
} else {
$arr = &new $object_class($arr);
}
}
return $arr;
}
if ($res == null && $this->autofree) {
$this->free();
}
return $res;
}
 
// }}}
// {{{ fetchInto()
 
/**
* Fetch a row of data into an array which is passed by reference.
*
* The type of array returned can be controlled either by setting this
* method's <var>$fetchmode</var> parameter or by changing the default
* fetch mode setFetchMode() before calling this method.
*
* There are two options for standardizing the information returned
* from databases, ensuring their values are consistent when changing
* DBMS's. These portability options can be turned on when creating a
* new DB object or by using setOption().
*
* + <samp>DB_PORTABILITY_LOWERCASE</samp>
* convert names of fields to lower case
*
* + <samp>DB_PORTABILITY_RTRIM</samp>
* right trim the data
*
* @param array &$arr (reference) array where data from the row
* should be placed
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch
*
* @return mixed DB_OK on success, null on no more rows or
* a DB_Error object on error
*
* @see DB_common::setOption(), DB_common::setFetchMode()
* @access public
*/
function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null)
{
if ($fetchmode === DB_FETCHMODE_DEFAULT) {
$fetchmode = $this->fetchmode;
}
if ($fetchmode === DB_FETCHMODE_OBJECT) {
$fetchmode = DB_FETCHMODE_ASSOC;
$object_class = $this->fetchmode_object_class;
}
if ($this->limit_from !== null) {
if ($this->row_counter === null) {
$this->row_counter = $this->limit_from;
// Skip rows
if ($this->limit_type == false) {
$i = 0;
while ($i++ < $this->limit_from) {
$this->dbh->fetchInto($this->result, $arr, $fetchmode);
}
}
}
if ($this->row_counter >= (
$this->limit_from + $this->limit_count))
{
if ($this->autofree) {
$this->free();
}
return null;
}
if ($this->limit_type == 'emulate') {
$rownum = $this->row_counter;
}
 
$this->row_counter++;
}
$res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum);
if ($res === DB_OK) {
if (isset($object_class)) {
// default mode specified in DB_common::fetchmode_object_class property
if ($object_class == 'stdClass') {
$arr = (object) $arr;
} else {
$arr = new $object_class($arr);
}
}
return DB_OK;
}
if ($res == null && $this->autofree) {
$this->free();
}
return $res;
}
 
// }}}
// {{{ numCols()
 
/**
* Get the the number of columns in a result set.
*
* @return int the number of columns, or a DB error
*
* @access public
*/
function numCols()
{
return $this->dbh->numCols($this->result);
}
 
// }}}
// {{{ numRows()
 
/**
* Get the number of rows in a result set.
*
* @return int the number of rows, or a DB error
*
* @access public
*/
function numRows()
{
return $this->dbh->numRows($this->result);
}
 
// }}}
// {{{ nextResult()
 
/**
* Get the next result if a batch of queries was executed.
*
* @return bool true if a new result is available or false if not.
*
* @access public
*/
function nextResult()
{
return $this->dbh->nextResult($this->result);
}
 
// }}}
// {{{ free()
 
/**
* Frees the resources allocated for this result set.
* @return int error code
*
* @access public
*/
function free()
{
$err = $this->dbh->freeResult($this->result);
if (DB::isError($err)) {
return $err;
}
$this->result = false;
return true;
}
 
// }}}
// {{{ tableInfo()
 
/**
* @deprecated
* @internal
* @see DB_common::tableInfo()
*/
function tableInfo($mode = null)
{
if (is_string($mode)) {
return $this->dbh->raiseError(DB_ERROR_NEED_MORE_DATA);
}
return $this->dbh->tableInfo($this, $mode);
}
 
// }}}
// {{{ getRowCounter()
 
/**
* returns the actual row number
* @return integer
*/
function getRowCounter()
{
return $this->row_counter;
}
// }}}
}
 
// }}}
// {{{ class DB_row
 
/**
* Pear DB Row Object
* @see DB_common::setFetchMode()
*/
class DB_row
{
// {{{ constructor
 
/**
* constructor
*
* @param resource row data as array
*/
function DB_row(&$arr)
{
foreach ($arr as $key => $value) {
$this->$key = &$arr[$key];
}
}
 
// }}}
}
 
// }}}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/jrest/lib/PEAR.php
New file
0,0 → 1,971
<?php
//
// +----------------------------------------------------------------------+
// | PEAR, the PHP Extension and Application Repository |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available through the world-wide-web at the following url: |
// | http://www.php.net/license/3_0.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Sterling Hughes <sterling@php.net> |
// | Stig Bakken <ssb@php.net> |
// | Tomas V.V.Cox <cox@idecnet.com> |
// +----------------------------------------------------------------------+
//
// $Id$
//
 
define('PEAR_ERROR_RETURN', 1);
define('PEAR_ERROR_PRINT', 2);
define('PEAR_ERROR_TRIGGER', 4);
define('PEAR_ERROR_DIE', 8);
define('PEAR_ERROR_CALLBACK', 16);
define('PEAR_ERROR_EXCEPTION', 32);
define('PEAR_ZE2', (function_exists('version_compare') &&
version_compare(zend_version(), "2-dev", "ge")));
 
if (substr(PHP_OS, 0, 3) == 'WIN') {
define('OS_WINDOWS', true);
define('OS_UNIX', false);
define('PEAR_OS', 'Windows');
} else {
define('OS_WINDOWS', false);
define('OS_UNIX', true);
define('PEAR_OS', 'Unix'); // blatant assumption
}
 
// instant backwards compatibility
if (!defined('PATH_SEPARATOR')) {
if (OS_WINDOWS) {
define('PATH_SEPARATOR', ';');
} else {
define('PATH_SEPARATOR', ':');
}
}
 
$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
$GLOBALS['_PEAR_destructor_object_list'] = array();
$GLOBALS['_PEAR_shutdown_funcs'] = array();
$GLOBALS['_PEAR_error_handler_stack'] = array();
 
ini_set('track_errors', true);
 
/**
* Base class for other PEAR classes. Provides rudimentary
* emulation of destructors.
*
* If you want a destructor in your class, inherit PEAR and make a
* destructor method called _yourclassname (same name as the
* constructor, but with a "_" prefix). Also, in your constructor you
* have to call the PEAR constructor: $this->PEAR();.
* The destructor method will be called without parameters. Note that
* at in some SAPI implementations (such as Apache), any output during
* the request shutdown (in which destructors are called) seems to be
* discarded. If you need to get any debug information from your
* destructor, use error_log(), syslog() or something similar.
*
* IMPORTANT! To use the emulated destructors you need to create the
* objects by reference: $obj =& new PEAR_child;
*
* @since PHP 4.0.2
* @author Stig Bakken <ssb@php.net>
* @see http://pear.php.net/manual/
*/
class PEAR
{
// {{{ properties
 
/**
* Whether to enable internal debug messages.
*
* @var bool
* @access private
*/
var $_debug = false;
 
/**
* Default error mode for this object.
*
* @var int
* @access private
*/
var $_default_error_mode = null;
 
/**
* Default error options used for this object when error mode
* is PEAR_ERROR_TRIGGER.
*
* @var int
* @access private
*/
var $_default_error_options = null;
 
/**
* Default error handler (callback) for this object, if error mode is
* PEAR_ERROR_CALLBACK.
*
* @var string
* @access private
*/
var $_default_error_handler = '';
 
/**
* Which class to use for error objects.
*
* @var string
* @access private
*/
var $_error_class = 'PEAR_Error';
 
/**
* An array of expected errors.
*
* @var array
* @access private
*/
var $_expected_errors = array();
 
// }}}
 
// {{{ constructor
 
/**
* Constructor. Registers this object in
* $_PEAR_destructor_object_list for destructor emulation if a
* destructor object exists.
*
* @param string $error_class (optional) which class to use for
* error objects, defaults to PEAR_Error.
* @access public
* @return void
*/
function PEAR($error_class = null)
{
$classname = get_class($this);
if ($this->_debug) {
print "PEAR constructor called, class=$classname\n";
}
if ($error_class !== null) {
$this->_error_class = $error_class;
}
while ($classname) {
$destructor = "_$classname";
if (method_exists($this, $destructor)) {
global $_PEAR_destructor_object_list;
$_PEAR_destructor_object_list[] = &$this;
break;
} else {
$classname = get_parent_class($classname);
}
}
}
 
// }}}
// {{{ destructor
 
/**
* Destructor (the emulated type of...). Does nothing right now,
* but is included for forward compatibility, so subclass
* destructors should always call it.
*
* See the note in the class desciption about output from
* destructors.
*
* @access public
* @return void
*/
function _PEAR() {
if ($this->_debug) {
printf("PEAR destructor called, class=%s\n", get_class($this));
}
}
 
// }}}
// {{{ getStaticProperty()
 
/**
* If you have a class that's mostly/entirely static, and you need static
* properties, you can use this method to simulate them. Eg. in your method(s)
* do this: $myVar = &PEAR::getStaticProperty('myVar');
* You MUST use a reference, or they will not persist!
*
* @access public
* @param string $class The calling classname, to prevent clashes
* @param string $var The variable to retrieve.
* @return mixed A reference to the variable. If not set it will be
* auto initialised to NULL.
*/
function &getStaticProperty($class, $var)
{
static $properties;
return $properties[$class][$var];
}
 
// }}}
// {{{ registerShutdownFunc()
 
/**
* Use this function to register a shutdown method for static
* classes.
*
* @access public
* @param mixed $func The function name (or array of class/method) to call
* @param mixed $args The arguments to pass to the function
* @return void
*/
function registerShutdownFunc($func, $args = array())
{
$GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
}
 
// }}}
// {{{ isError()
 
/**
* Tell whether a value is a PEAR error.
*
* @param mixed $data the value to test
* @param int $code if $data is an error object, return true
* only if $code is a string and
* $obj->getMessage() == $code or
* $code is an integer and $obj->getCode() == $code
* @access public
* @return bool true if parameter is an error
*/
 
function isError($data, $code = null)
{
if (is_a($data, 'PEAR_Error')) {
if (is_null($code)) {
return true;
} elseif (is_string($code)) {
return $data->getMessage() == $code;
} else {
return $data->getCode() == $code;
}
}
return false;
}
 
// }}}
// {{{ setErrorHandling()
 
/**
* Sets how errors generated by this object should be handled.
* Can be invoked both in objects and statically. If called
* statically, setErrorHandling sets the default behaviour for all
* PEAR objects. If called in an object, setErrorHandling sets
* the default behaviour for that object.
*
* @param int $mode
* One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
* PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
* PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
*
* @param mixed $options
* When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
* of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
*
* When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
* to be the callback function or method. A callback
* function is a string with the name of the function, a
* callback method is an array of two elements: the element
* at index 0 is the object, and the element at index 1 is
* the name of the method to call in the object.
*
* When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
* a printf format string used when printing the error
* message.
*
* @access public
* @return void
* @see PEAR_ERROR_RETURN
* @see PEAR_ERROR_PRINT
* @see PEAR_ERROR_TRIGGER
* @see PEAR_ERROR_DIE
* @see PEAR_ERROR_CALLBACK
* @see PEAR_ERROR_EXCEPTION
*
* @since PHP 4.0.5
*/
 
function setErrorHandling($mode = null, $options = null)
{
if (isset($this) && is_a($this, 'PEAR')) {
$setmode = &$this->_default_error_mode;
$setoptions = &$this->_default_error_options;
} else {
$setmode = &$GLOBALS['_PEAR_default_error_mode'];
$setoptions = &$GLOBALS['_PEAR_default_error_options'];
}
 
switch ($mode) {
case PEAR_ERROR_RETURN:
case PEAR_ERROR_PRINT:
case PEAR_ERROR_TRIGGER:
case PEAR_ERROR_DIE:
case PEAR_ERROR_EXCEPTION:
case null:
$setmode = $mode;
$setoptions = $options;
break;
 
case PEAR_ERROR_CALLBACK:
$setmode = $mode;
// class/object method callback
if (is_callable($options)) {
$setoptions = $options;
} else {
trigger_error("invalid error callback", E_USER_WARNING);
}
break;
 
default:
trigger_error("invalid error mode", E_USER_WARNING);
break;
}
}
 
// }}}
// {{{ expectError()
 
/**
* This method is used to tell which errors you expect to get.
* Expected errors are always returned with error mode
* PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
* and this method pushes a new element onto it. The list of
* expected errors are in effect until they are popped off the
* stack with the popExpect() method.
*
* Note that this method can not be called statically
*
* @param mixed $code a single error code or an array of error codes to expect
*
* @return int the new depth of the "expected errors" stack
* @access public
*/
function expectError($code = '*')
{
if (is_array($code)) {
array_push($this->_expected_errors, $code);
} else {
array_push($this->_expected_errors, array($code));
}
return sizeof($this->_expected_errors);
}
 
// }}}
// {{{ popExpect()
 
/**
* This method pops one element off the expected error codes
* stack.
*
* @return array the list of error codes that were popped
*/
function popExpect()
{
return array_pop($this->_expected_errors);
}
 
// }}}
// {{{ _checkDelExpect()
 
/**
* This method checks unsets an error code if available
*
* @param mixed error code
* @return bool true if the error code was unset, false otherwise
* @access private
* @since PHP 4.3.0
*/
function _checkDelExpect($error_code)
{
$deleted = false;
 
foreach ($this->_expected_errors AS $key => $error_array) {
if (in_array($error_code, $error_array)) {
unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
$deleted = true;
}
 
// clean up empty arrays
if (0 == count($this->_expected_errors[$key])) {
unset($this->_expected_errors[$key]);
}
}
return $deleted;
}
 
// }}}
// {{{ delExpect()
 
/**
* This method deletes all occurences of the specified element from
* the expected error codes stack.
*
* @param mixed $error_code error code that should be deleted
* @return mixed list of error codes that were deleted or error
* @access public
* @since PHP 4.3.0
*/
function delExpect($error_code)
{
$deleted = false;
 
if ((is_array($error_code) && (0 != count($error_code)))) {
// $error_code is a non-empty array here;
// we walk through it trying to unset all
// values
foreach($error_code as $key => $error) {
if ($this->_checkDelExpect($error)) {
$deleted = true;
} else {
$deleted = false;
}
}
return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
} elseif (!empty($error_code)) {
// $error_code comes alone, trying to unset it
if ($this->_checkDelExpect($error_code)) {
return true;
} else {
return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
}
} else {
// $error_code is empty
return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
}
}
 
// }}}
// {{{ raiseError()
 
/**
* This method is a wrapper that returns an instance of the
* configured error class with this object's default error
* handling applied. If the $mode and $options parameters are not
* specified, the object's defaults are used.
*
* @param mixed $message a text error message or a PEAR error object
*
* @param int $code a numeric error code (it is up to your class
* to define these if you want to use codes)
*
* @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
* PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
* PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
*
* @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
* specifies the PHP-internal error level (one of
* E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
* If $mode is PEAR_ERROR_CALLBACK, this
* parameter specifies the callback function or
* method. In other error modes this parameter
* is ignored.
*
* @param string $userinfo If you need to pass along for example debug
* information, this parameter is meant for that.
*
* @param string $error_class The returned error object will be
* instantiated from this class, if specified.
*
* @param bool $skipmsg If true, raiseError will only pass error codes,
* the error message parameter will be dropped.
*
* @access public
* @return object a PEAR error object
* @see PEAR::setErrorHandling
* @since PHP 4.0.5
*/
function raiseError($message = null,
$code = null,
$mode = null,
$options = null,
$userinfo = null,
$error_class = null,
$skipmsg = false)
{
// The error is yet a PEAR error object
if (is_object($message)) {
$code = $message->getCode();
$userinfo = $message->getUserInfo();
$error_class = $message->getType();
$message->error_message_prefix = '';
$message = $message->getMessage();
}
 
if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
if ($exp[0] == "*" ||
(is_int(reset($exp)) && in_array($code, $exp)) ||
(is_string(reset($exp)) && in_array($message, $exp))) {
$mode = PEAR_ERROR_RETURN;
}
}
// No mode given, try global ones
if ($mode === null) {
// Class error handler
if (isset($this) && isset($this->_default_error_mode)) {
$mode = $this->_default_error_mode;
$options = $this->_default_error_options;
// Global error handler
} elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
$mode = $GLOBALS['_PEAR_default_error_mode'];
$options = $GLOBALS['_PEAR_default_error_options'];
}
}
 
if ($error_class !== null) {
$ec = $error_class;
} elseif (isset($this) && isset($this->_error_class)) {
$ec = $this->_error_class;
} else {
$ec = 'PEAR_Error';
}
if ($skipmsg) {
return new $ec($code, $mode, $options, $userinfo);
} else {
return new $ec($message, $code, $mode, $options, $userinfo);
}
}
 
// }}}
// {{{ throwError()
 
/**
* Simpler form of raiseError with fewer options. In most cases
* message, code and userinfo are enough.
*
* @param string $message
*
*/
function throwError($message = null,
$code = null,
$userinfo = null)
{
if (isset($this) && is_subclass_of($this, 'PEAR_Error')) {
return $this->raiseError($message, $code, null, null, $userinfo);
} else {
return PEAR::raiseError($message, $code, null, null, $userinfo);
}
}
 
// }}}
// {{{ pushErrorHandling()
 
/**
* Push a new error handler on top of the error handler options stack. With this
* you can easily override the actual error handler for some code and restore
* it later with popErrorHandling.
*
* @param mixed $mode (same as setErrorHandling)
* @param mixed $options (same as setErrorHandling)
*
* @return bool Always true
*
* @see PEAR::setErrorHandling
*/
function pushErrorHandling($mode, $options = null)
{
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
if (isset($this) && is_a($this, 'PEAR')) {
$def_mode = &$this->_default_error_mode;
$def_options = &$this->_default_error_options;
} else {
$def_mode = &$GLOBALS['_PEAR_default_error_mode'];
$def_options = &$GLOBALS['_PEAR_default_error_options'];
}
$stack[] = array($def_mode, $def_options);
 
if (isset($this) && is_a($this, 'PEAR')) {
$this->setErrorHandling($mode, $options);
} else {
PEAR::setErrorHandling($mode, $options);
}
$stack[] = array($mode, $options);
return true;
}
 
// }}}
// {{{ popErrorHandling()
 
/**
* Pop the last error handler used
*
* @return bool Always true
*
* @see PEAR::pushErrorHandling
*/
function popErrorHandling()
{
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
array_pop($stack);
list($mode, $options) = $stack[sizeof($stack) - 1];
array_pop($stack);
if (isset($this) && is_a($this, 'PEAR')) {
$this->setErrorHandling($mode, $options);
} else {
PEAR::setErrorHandling($mode, $options);
}
return true;
}
 
// }}}
// {{{ loadExtension()
 
/**
* OS independant PHP extension load. Remember to take care
* on the correct extension name for case sensitive OSes.
*
* @param string $ext The extension name
* @return bool Success or not on the dl() call
*/
function loadExtension($ext)
{
if (!extension_loaded($ext)) {
// if either returns true dl() will produce a FATAL error, stop that
if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
return false;
}
if (OS_WINDOWS) {
$suffix = '.dll';
} elseif (PHP_OS == 'HP-UX') {
$suffix = '.sl';
} elseif (PHP_OS == 'AIX') {
$suffix = '.a';
} elseif (PHP_OS == 'OSX') {
$suffix = '.bundle';
} else {
$suffix = '.so';
}
return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
}
return true;
}
 
// }}}
}
 
// {{{ _PEAR_call_destructors()
 
function _PEAR_call_destructors()
{
global $_PEAR_destructor_object_list;
if (is_array($_PEAR_destructor_object_list) &&
sizeof($_PEAR_destructor_object_list))
{
reset($_PEAR_destructor_object_list);
while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
$classname = get_class($objref);
while ($classname) {
$destructor = "_$classname";
if (method_exists($objref, $destructor)) {
$objref->$destructor();
break;
} else {
$classname = get_parent_class($classname);
}
}
}
// Empty the object list to ensure that destructors are
// not called more than once.
$_PEAR_destructor_object_list = array();
}
 
// Now call the shutdown functions
if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
call_user_func_array($value[0], $value[1]);
}
}
}
 
// }}}
 
class PEAR_Error
{
// {{{ properties
 
var $error_message_prefix = '';
var $mode = PEAR_ERROR_RETURN;
var $level = E_USER_NOTICE;
var $code = -1;
var $message = '';
var $userinfo = '';
var $backtrace = null;
 
// }}}
// {{{ constructor
 
/**
* PEAR_Error constructor
*
* @param string $message message
*
* @param int $code (optional) error code
*
* @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
* PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
* PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
*
* @param mixed $options (optional) error level, _OR_ in the case of
* PEAR_ERROR_CALLBACK, the callback function or object/method
* tuple.
*
* @param string $userinfo (optional) additional user/debug info
*
* @access public
*
*/
function PEAR_Error($message = 'unknown error', $code = null,
$mode = null, $options = null, $userinfo = null)
{
if ($mode === null) {
$mode = PEAR_ERROR_RETURN;
}
$this->message = $message;
$this->code = $code;
$this->mode = $mode;
$this->userinfo = $userinfo;
if (function_exists("debug_backtrace")) {
$this->backtrace = debug_backtrace();
}
if ($mode & PEAR_ERROR_CALLBACK) {
$this->level = E_USER_NOTICE;
$this->callback = $options;
} else {
if ($options === null) {
$options = E_USER_NOTICE;
}
$this->level = $options;
$this->callback = null;
}
if ($this->mode & PEAR_ERROR_PRINT) {
if (is_null($options) || is_int($options)) {
$format = "%s";
} else {
$format = $options;
}
printf($format, $this->getMessage());
}
if ($this->mode & PEAR_ERROR_TRIGGER) {
trigger_error($this->getMessage(), $this->level);
}
if ($this->mode & PEAR_ERROR_DIE) {
$msg = $this->getMessage();
if (is_null($options) || is_int($options)) {
$format = "%s";
if (substr($msg, -1) != "\n") {
$msg .= "\n";
}
} else {
$format = $options;
}
die(sprintf($format, $msg));
}
if ($this->mode & PEAR_ERROR_CALLBACK) {
if (is_callable($this->callback)) {
call_user_func($this->callback, $this);
}
}
if (PEAR_ZE2 && $this->mode & PEAR_ERROR_EXCEPTION) {
eval('throw $this;');
}
}
 
// }}}
// {{{ getMode()
 
/**
* Get the error mode from an error object.
*
* @return int error mode
* @access public
*/
function getMode() {
return $this->mode;
}
 
// }}}
// {{{ getCallback()
 
/**
* Get the callback function/method from an error object.
*
* @return mixed callback function or object/method array
* @access public
*/
function getCallback() {
return $this->callback;
}
 
// }}}
// {{{ getMessage()
 
 
/**
* Get the error message from an error object.
*
* @return string full error message
* @access public
*/
function getMessage()
{
return ($this->error_message_prefix . $this->message);
}
 
 
// }}}
// {{{ getCode()
 
/**
* Get error code from an error object
*
* @return int error code
* @access public
*/
function getCode()
{
return $this->code;
}
 
// }}}
// {{{ getType()
 
/**
* Get the name of this error/exception.
*
* @return string error/exception name (type)
* @access public
*/
function getType()
{
return get_class($this);
}
 
// }}}
// {{{ getUserInfo()
 
/**
* Get additional user-supplied information.
*
* @return string user-supplied information
* @access public
*/
function getUserInfo()
{
return $this->userinfo;
}
 
// }}}
// {{{ getDebugInfo()
 
/**
* Get additional debug information supplied by the application.
*
* @return string debug information
* @access public
*/
function getDebugInfo()
{
return $this->getUserInfo();
}
 
// }}}
// {{{ getBacktrace()
 
/**
* Get the call backtrace from where the error was generated.
* Supported with PHP 4.3.0 or newer.
*
* @param int $frame (optional) what frame to fetch
* @return array Backtrace, or NULL if not available.
* @access public
*/
function getBacktrace($frame = null)
{
if ($frame === null) {
return $this->backtrace;
}
return $this->backtrace[$frame];
}
 
// }}}
// {{{ addUserInfo()
 
function addUserInfo($info)
{
if (empty($this->userinfo)) {
$this->userinfo = $info;
} else {
$this->userinfo .= " ** $info";
}
}
 
// }}}
// {{{ toString()
 
/**
* Make a string representation of this object.
*
* @return string a string with an object summary
* @access public
*/
function toString() {
$modes = array();
$levels = array(E_USER_NOTICE => 'notice',
E_USER_WARNING => 'warning',
E_USER_ERROR => 'error');
if ($this->mode & PEAR_ERROR_CALLBACK) {
if (is_array($this->callback)) {
$callback = get_class($this->callback[0]) . '::' .
$this->callback[1];
} else {
$callback = $this->callback;
}
return sprintf('[%s: message="%s" code=%d mode=callback '.
'callback=%s prefix="%s" info="%s"]',
get_class($this), $this->message, $this->code,
$callback, $this->error_message_prefix,
$this->userinfo);
}
if ($this->mode & PEAR_ERROR_PRINT) {
$modes[] = 'print';
}
if ($this->mode & PEAR_ERROR_TRIGGER) {
$modes[] = 'trigger';
}
if ($this->mode & PEAR_ERROR_DIE) {
$modes[] = 'die';
}
if ($this->mode & PEAR_ERROR_RETURN) {
$modes[] = 'return';
}
return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
'prefix="%s" info="%s"]',
get_class($this), $this->message, $this->code,
implode("|", $modes), $levels[$this->level],
$this->error_message_prefix,
$this->userinfo);
}
 
// }}}
}
 
register_shutdown_function("_PEAR_call_destructors");
 
/*
* Local Variables:
* mode: php
* tab-width: 4
* c-basic-offset: 4
* End:
*/
?>
/trunk/jrest/lib/File/PDF/fonts/helveticai.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['helveticaI'] = array(
chr(0) => 278,
chr(1) => 278,
chr(2) => 278,
chr(3) => 278,
chr(4) => 278,
chr(5) => 278,
chr(6) => 278,
chr(7) => 278,
chr(8) => 278,
chr(9) => 278,
chr(10) => 278,
chr(11) => 278,
chr(12) => 278,
chr(13) => 278,
chr(14) => 278,
chr(15) => 278,
chr(16) => 278,
chr(17) => 278,
chr(18) => 278,
chr(19) => 278,
chr(20) => 278,
chr(21) => 278,
chr(22) => 278,
chr(23) => 278,
chr(24) => 278,
chr(25) => 278,
chr(26) => 278,
chr(27) => 278,
chr(28) => 278,
chr(29) => 278,
chr(30) => 278,
chr(31) => 278,
' ' => 278,
'!' => 278,
'"' => 355,
'#' => 556,
'$' => 556,
'%' => 889,
'&' => 667,
'\'' => 191,
'(' => 333,
')' => 333,
'*' => 389,
'+' => 584,
',' => 278,
'-' => 333,
'.' => 278,
'/' => 278,
'0' => 556,
'1' => 556,
'2' => 556,
'3' => 556,
'4' => 556,
'5' => 556,
'6' => 556,
'7' => 556,
'8' => 556,
'9' => 556,
':' => 278,
';' => 278,
'<' => 584,
'=' => 584,
'>' => 584,
'?' => 556,
'@' => 1015,
'A' => 667,
'B' => 667,
'C' => 722,
'D' => 722,
'E' => 667,
'F' => 611,
'G' => 778,
'H' => 722,
'I' => 278,
'J' => 500,
'K' => 667,
'L' => 556,
'M' => 833,
'N' => 722,
'O' => 778,
'P' => 667,
'Q' => 778,
'R' => 722,
'S' => 667,
'T' => 611,
'U' => 722,
'V' => 667,
'W' => 944,
'X' => 667,
'Y' => 667,
'Z' => 611,
'[' => 278,
'\\' => 278,
']' => 278,
'^' => 469,
'_' => 556,
'`' => 333,
'a' => 556,
'b' => 556,
'c' => 500,
'd' => 556,
'e' => 556,
'f' => 278,
'g' => 556,
'h' => 556,
'i' => 222,
'j' => 222,
'k' => 500,
'l' => 222,
'm' => 833,
'n' => 556,
'o' => 556,
'p' => 556,
'q' => 556,
'r' => 333,
's' => 500,
't' => 278,
'u' => 556,
'v' => 500,
'w' => 722,
'x' => 500,
'y' => 500,
'z' => 500,
'{' => 334,
'|' => 260,
'}' => 334,
'~' => 584,
chr(127) => 350,
chr(128) => 556,
chr(129) => 350,
chr(130) => 222,
chr(131) => 556,
chr(132) => 333,
chr(133) => 1000,
chr(134) => 556,
chr(135) => 556,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 667,
chr(139) => 333,
chr(140) => 1000,
chr(141) => 350,
chr(142) => 611,
chr(143) => 350,
chr(144) => 350,
chr(145) => 222,
chr(146) => 222,
chr(147) => 333,
chr(148) => 333,
chr(149) => 350,
chr(150) => 556,
chr(151) => 1000,
chr(152) => 333,
chr(153) => 1000,
chr(154) => 500,
chr(155) => 333,
chr(156) => 944,
chr(157) => 350,
chr(158) => 500,
chr(159) => 667,
chr(160) => 278,
chr(161) => 333,
chr(162) => 556,
chr(163) => 556,
chr(164) => 556,
chr(165) => 556,
chr(166) => 260,
chr(167) => 556,
chr(168) => 333,
chr(169) => 737,
chr(170) => 370,
chr(171) => 556,
chr(172) => 584,
chr(173) => 333,
chr(174) => 737,
chr(175) => 333,
chr(176) => 400,
chr(177) => 584,
chr(178) => 333,
chr(179) => 333,
chr(180) => 333,
chr(181) => 556,
chr(182) => 537,
chr(183) => 278,
chr(184) => 333,
chr(185) => 333,
chr(186) => 365,
chr(187) => 556,
chr(188) => 834,
chr(189) => 834,
chr(190) => 834,
chr(191) => 611,
chr(192) => 667,
chr(193) => 667,
chr(194) => 667,
chr(195) => 667,
chr(196) => 667,
chr(197) => 667,
chr(198) => 1000,
chr(199) => 722,
chr(200) => 667,
chr(201) => 667,
chr(202) => 667,
chr(203) => 667,
chr(204) => 278,
chr(205) => 278,
chr(206) => 278,
chr(207) => 278,
chr(208) => 722,
chr(209) => 722,
chr(210) => 778,
chr(211) => 778,
chr(212) => 778,
chr(213) => 778,
chr(214) => 778,
chr(215) => 584,
chr(216) => 778,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 667,
chr(222) => 667,
chr(223) => 611,
chr(224) => 556,
chr(225) => 556,
chr(226) => 556,
chr(227) => 556,
chr(228) => 556,
chr(229) => 556,
chr(230) => 889,
chr(231) => 500,
chr(232) => 556,
chr(233) => 556,
chr(234) => 556,
chr(235) => 556,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 556,
chr(241) => 556,
chr(242) => 556,
chr(243) => 556,
chr(244) => 556,
chr(245) => 556,
chr(246) => 556,
chr(247) => 584,
chr(248) => 611,
chr(249) => 556,
chr(250) => 556,
chr(251) => 556,
chr(252) => 556,
chr(253) => 500,
chr(254) => 556,
chr(255) => 500);
/trunk/jrest/lib/File/PDF/fonts/helveticabi.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['helveticaBI'] = array(
chr(0) => 278,
chr(1) => 278,
chr(2) => 278,
chr(3) => 278,
chr(4) => 278,
chr(5) => 278,
chr(6) => 278,
chr(7) => 278,
chr(8) => 278,
chr(9) => 278,
chr(10) => 278,
chr(11) => 278,
chr(12) => 278,
chr(13) => 278,
chr(14) => 278,
chr(15) => 278,
chr(16) => 278,
chr(17) => 278,
chr(18) => 278,
chr(19) => 278,
chr(20) => 278,
chr(21) => 278,
chr(22) => 278,
chr(23) => 278,
chr(24) => 278,
chr(25) => 278,
chr(26) => 278,
chr(27) => 278,
chr(28) => 278,
chr(29) => 278,
chr(30) => 278,
chr(31) => 278,
' ' => 278,
'!' => 333,
'"' => 474,
'#' => 556,
'$' => 556,
'%' => 889,
'&' => 722,
'\'' => 238,
'(' => 333,
')' => 333,
'*' => 389,
'+' => 584,
',' => 278,
'-' => 333,
'.' => 278,
'/' => 278,
'0' => 556,
'1' => 556,
'2' => 556,
'3' => 556,
'4' => 556,
'5' => 556,
'6' => 556,
'7' => 556,
'8' => 556,
'9' => 556,
':' => 333,
';' => 333,
'<' => 584,
'=' => 584,
'>' => 584,
'?' => 611,
'@' => 975,
'A' => 722,
'B' => 722,
'C' => 722,
'D' => 722,
'E' => 667,
'F' => 611,
'G' => 778,
'H' => 722,
'I' => 278,
'J' => 556,
'K' => 722,
'L' => 611,
'M' => 833,
'N' => 722,
'O' => 778,
'P' => 667,
'Q' => 778,
'R' => 722,
'S' => 667,
'T' => 611,
'U' => 722,
'V' => 667,
'W' => 944,
'X' => 667,
'Y' => 667,
'Z' => 611,
'[' => 333,
'\\' => 278,
']' => 333,
'^' => 584,
'_' => 556,
'`' => 333,
'a' => 556,
'b' => 611,
'c' => 556,
'd' => 611,
'e' => 556,
'f' => 333,
'g' => 611,
'h' => 611,
'i' => 278,
'j' => 278,
'k' => 556,
'l' => 278,
'm' => 889,
'n' => 611,
'o' => 611,
'p' => 611,
'q' => 611,
'r' => 389,
's' => 556,
't' => 333,
'u' => 611,
'v' => 556,
'w' => 778,
'x' => 556,
'y' => 556,
'z' => 500,
'{' => 389,
'|' => 280,
'}' => 389,
'~' => 584,
chr(127) => 350,
chr(128) => 556,
chr(129) => 350,
chr(130) => 278,
chr(131) => 556,
chr(132) => 500,
chr(133) => 1000,
chr(134) => 556,
chr(135) => 556,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 667,
chr(139) => 333,
chr(140) => 1000,
chr(141) => 350,
chr(142) => 611,
chr(143) => 350,
chr(144) => 350,
chr(145) => 278,
chr(146) => 278,
chr(147) => 500,
chr(148) => 500,
chr(149) => 350,
chr(150) => 556,
chr(151) => 1000,
chr(152) => 333,
chr(153) => 1000,
chr(154) => 556,
chr(155) => 333,
chr(156) => 944,
chr(157) => 350,
chr(158) => 500,
chr(159) => 667,
chr(160) => 278,
chr(161) => 333,
chr(162) => 556,
chr(163) => 556,
chr(164) => 556,
chr(165) => 556,
chr(166) => 280,
chr(167) => 556,
chr(168) => 333,
chr(169) => 737,
chr(170) => 370,
chr(171) => 556,
chr(172) => 584,
chr(173) => 333,
chr(174) => 737,
chr(175) => 333,
chr(176) => 400,
chr(177) => 584,
chr(178) => 333,
chr(179) => 333,
chr(180) => 333,
chr(181) => 611,
chr(182) => 556,
chr(183) => 278,
chr(184) => 333,
chr(185) => 333,
chr(186) => 365,
chr(187) => 556,
chr(188) => 834,
chr(189) => 834,
chr(190) => 834,
chr(191) => 611,
chr(192) => 722,
chr(193) => 722,
chr(194) => 722,
chr(195) => 722,
chr(196) => 722,
chr(197) => 722,
chr(198) => 1000,
chr(199) => 722,
chr(200) => 667,
chr(201) => 667,
chr(202) => 667,
chr(203) => 667,
chr(204) => 278,
chr(205) => 278,
chr(206) => 278,
chr(207) => 278,
chr(208) => 722,
chr(209) => 722,
chr(210) => 778,
chr(211) => 778,
chr(212) => 778,
chr(213) => 778,
chr(214) => 778,
chr(215) => 584,
chr(216) => 778,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 667,
chr(222) => 667,
chr(223) => 611,
chr(224) => 556,
chr(225) => 556,
chr(226) => 556,
chr(227) => 556,
chr(228) => 556,
chr(229) => 556,
chr(230) => 889,
chr(231) => 556,
chr(232) => 556,
chr(233) => 556,
chr(234) => 556,
chr(235) => 556,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 611,
chr(241) => 611,
chr(242) => 611,
chr(243) => 611,
chr(244) => 611,
chr(245) => 611,
chr(246) => 611,
chr(247) => 584,
chr(248) => 611,
chr(249) => 611,
chr(250) => 611,
chr(251) => 611,
chr(252) => 611,
chr(253) => 556,
chr(254) => 611,
chr(255) => 556);
/trunk/jrest/lib/File/PDF/fonts/times.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['times'] = array(
chr(0) => 250,
chr(1) => 250,
chr(2) => 250,
chr(3) => 250,
chr(4) => 250,
chr(5) => 250,
chr(6) => 250,
chr(7) => 250,
chr(8) => 250,
chr(9) => 250,
chr(10) => 250,
chr(11) => 250,
chr(12) => 250,
chr(13) => 250,
chr(14) => 250,
chr(15) => 250,
chr(16) => 250,
chr(17) => 250,
chr(18) => 250,
chr(19) => 250,
chr(20) => 250,
chr(21) => 250,
chr(22) => 250,
chr(23) => 250,
chr(24) => 250,
chr(25) => 250,
chr(26) => 250,
chr(27) => 250,
chr(28) => 250,
chr(29) => 250,
chr(30) => 250,
chr(31) => 250,
' ' => 250,
'!' => 333,
'"' => 408,
'#' => 500,
'$' => 500,
'%' => 833,
'&' => 778,
'\'' => 180,
'(' => 333,
')' => 333,
'*' => 500,
'+' => 564,
',' => 250,
'-' => 333,
'.' => 250,
'/' => 278,
'0' => 500,
'1' => 500,
'2' => 500,
'3' => 500,
'4' => 500,
'5' => 500,
'6' => 500,
'7' => 500,
'8' => 500,
'9' => 500,
':' => 278,
';' => 278,
'<' => 564,
'=' => 564,
'>' => 564,
'?' => 444,
'@' => 921,
'A' => 722,
'B' => 667,
'C' => 667,
'D' => 722,
'E' => 611,
'F' => 556,
'G' => 722,
'H' => 722,
'I' => 333,
'J' => 389,
'K' => 722,
'L' => 611,
'M' => 889,
'N' => 722,
'O' => 722,
'P' => 556,
'Q' => 722,
'R' => 667,
'S' => 556,
'T' => 611,
'U' => 722,
'V' => 722,
'W' => 944,
'X' => 722,
'Y' => 722,
'Z' => 611,
'[' => 333,
'\\' => 278,
']' => 333,
'^' => 469,
'_' => 500,
'`' => 333,
'a' => 444,
'b' => 500,
'c' => 444,
'd' => 500,
'e' => 444,
'f' => 333,
'g' => 500,
'h' => 500,
'i' => 278,
'j' => 278,
'k' => 500,
'l' => 278,
'm' => 778,
'n' => 500,
'o' => 500,
'p' => 500,
'q' => 500,
'r' => 333,
's' => 389,
't' => 278,
'u' => 500,
'v' => 500,
'w' => 722,
'x' => 500,
'y' => 500,
'z' => 444,
'{' => 480,
'|' => 200,
'}' => 480,
'~' => 541,
chr(127) => 350,
chr(128) => 500,
chr(129) => 350,
chr(130) => 333,
chr(131) => 500,
chr(132) => 444,
chr(133) => 1000,
chr(134) => 500,
chr(135) => 500,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 556,
chr(139) => 333,
chr(140) => 889,
chr(141) => 350,
chr(142) => 611,
chr(143) => 350,
chr(144) => 350,
chr(145) => 333,
chr(146) => 333,
chr(147) => 444,
chr(148) => 444,
chr(149) => 350,
chr(150) => 500,
chr(151) => 1000,
chr(152) => 333,
chr(153) => 980,
chr(154) => 389,
chr(155) => 333,
chr(156) => 722,
chr(157) => 350,
chr(158) => 444,
chr(159) => 722,
chr(160) => 250,
chr(161) => 333,
chr(162) => 500,
chr(163) => 500,
chr(164) => 500,
chr(165) => 500,
chr(166) => 200,
chr(167) => 500,
chr(168) => 333,
chr(169) => 760,
chr(170) => 276,
chr(171) => 500,
chr(172) => 564,
chr(173) => 333,
chr(174) => 760,
chr(175) => 333,
chr(176) => 400,
chr(177) => 564,
chr(178) => 300,
chr(179) => 300,
chr(180) => 333,
chr(181) => 500,
chr(182) => 453,
chr(183) => 250,
chr(184) => 333,
chr(185) => 300,
chr(186) => 310,
chr(187) => 500,
chr(188) => 750,
chr(189) => 750,
chr(190) => 750,
chr(191) => 444,
chr(192) => 722,
chr(193) => 722,
chr(194) => 722,
chr(195) => 722,
chr(196) => 722,
chr(197) => 722,
chr(198) => 889,
chr(199) => 667,
chr(200) => 611,
chr(201) => 611,
chr(202) => 611,
chr(203) => 611,
chr(204) => 333,
chr(205) => 333,
chr(206) => 333,
chr(207) => 333,
chr(208) => 722,
chr(209) => 722,
chr(210) => 722,
chr(211) => 722,
chr(212) => 722,
chr(213) => 722,
chr(214) => 722,
chr(215) => 564,
chr(216) => 722,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 722,
chr(222) => 556,
chr(223) => 500,
chr(224) => 444,
chr(225) => 444,
chr(226) => 444,
chr(227) => 444,
chr(228) => 444,
chr(229) => 444,
chr(230) => 667,
chr(231) => 444,
chr(232) => 444,
chr(233) => 444,
chr(234) => 444,
chr(235) => 444,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 500,
chr(241) => 500,
chr(242) => 500,
chr(243) => 500,
chr(244) => 500,
chr(245) => 500,
chr(246) => 500,
chr(247) => 564,
chr(248) => 500,
chr(249) => 500,
chr(250) => 500,
chr(251) => 500,
chr(252) => 500,
chr(253) => 500,
chr(254) => 500,
chr(255) => 500);
/trunk/jrest/lib/File/PDF/fonts/timesb.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['timesB'] = array(
chr(0) => 250,
chr(1) => 250,
chr(2) => 250,
chr(3) => 250,
chr(4) => 250,
chr(5) => 250,
chr(6) => 250,
chr(7) => 250,
chr(8) => 250,
chr(9) => 250,
chr(10) => 250,
chr(11) => 250,
chr(12) => 250,
chr(13) => 250,
chr(14) => 250,
chr(15) => 250,
chr(16) => 250,
chr(17) => 250,
chr(18) => 250,
chr(19) => 250,
chr(20) => 250,
chr(21) => 250,
chr(22) => 250,
chr(23) => 250,
chr(24) => 250,
chr(25) => 250,
chr(26) => 250,
chr(27) => 250,
chr(28) => 250,
chr(29) => 250,
chr(30) => 250,
chr(31) => 250,
' ' => 250,
'!' => 333,
'"' => 555,
'#' => 500,
'$' => 500,
'%' => 1000,
'&' => 833,
'\'' => 278,
'(' => 333,
')' => 333,
'*' => 500,
'+' => 570,
',' => 250,
'-' => 333,
'.' => 250,
'/' => 278,
'0' => 500,
'1' => 500,
'2' => 500,
'3' => 500,
'4' => 500,
'5' => 500,
'6' => 500,
'7' => 500,
'8' => 500,
'9' => 500,
':' => 333,
';' => 333,
'<' => 570,
'=' => 570,
'>' => 570,
'?' => 500,
'@' => 930,
'A' => 722,
'B' => 667,
'C' => 722,
'D' => 722,
'E' => 667,
'F' => 611,
'G' => 778,
'H' => 778,
'I' => 389,
'J' => 500,
'K' => 778,
'L' => 667,
'M' => 944,
'N' => 722,
'O' => 778,
'P' => 611,
'Q' => 778,
'R' => 722,
'S' => 556,
'T' => 667,
'U' => 722,
'V' => 722,
'W' => 1000,
'X' => 722,
'Y' => 722,
'Z' => 667,
'[' => 333,
'\\' => 278,
']' => 333,
'^' => 581,
'_' => 500,
'`' => 333,
'a' => 500,
'b' => 556,
'c' => 444,
'd' => 556,
'e' => 444,
'f' => 333,
'g' => 500,
'h' => 556,
'i' => 278,
'j' => 333,
'k' => 556,
'l' => 278,
'm' => 833,
'n' => 556,
'o' => 500,
'p' => 556,
'q' => 556,
'r' => 444,
's' => 389,
't' => 333,
'u' => 556,
'v' => 500,
'w' => 722,
'x' => 500,
'y' => 500,
'z' => 444,
'{' => 394,
'|' => 220,
'}' => 394,
'~' => 520,
chr(127) => 350,
chr(128) => 500,
chr(129) => 350,
chr(130) => 333,
chr(131) => 500,
chr(132) => 500,
chr(133) => 1000,
chr(134) => 500,
chr(135) => 500,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 556,
chr(139) => 333,
chr(140) => 1000,
chr(141) => 350,
chr(142) => 667,
chr(143) => 350,
chr(144) => 350,
chr(145) => 333,
chr(146) => 333,
chr(147) => 500,
chr(148) => 500,
chr(149) => 350,
chr(150) => 500,
chr(151) => 1000,
chr(152) => 333,
chr(153) => 1000,
chr(154) => 389,
chr(155) => 333,
chr(156) => 722,
chr(157) => 350,
chr(158) => 444,
chr(159) => 722,
chr(160) => 250,
chr(161) => 333,
chr(162) => 500,
chr(163) => 500,
chr(164) => 500,
chr(165) => 500,
chr(166) => 220,
chr(167) => 500,
chr(168) => 333,
chr(169) => 747,
chr(170) => 300,
chr(171) => 500,
chr(172) => 570,
chr(173) => 333,
chr(174) => 747,
chr(175) => 333,
chr(176) => 400,
chr(177) => 570,
chr(178) => 300,
chr(179) => 300,
chr(180) => 333,
chr(181) => 556,
chr(182) => 540,
chr(183) => 250,
chr(184) => 333,
chr(185) => 300,
chr(186) => 330,
chr(187) => 500,
chr(188) => 750,
chr(189) => 750,
chr(190) => 750,
chr(191) => 500,
chr(192) => 722,
chr(193) => 722,
chr(194) => 722,
chr(195) => 722,
chr(196) => 722,
chr(197) => 722,
chr(198) => 1000,
chr(199) => 722,
chr(200) => 667,
chr(201) => 667,
chr(202) => 667,
chr(203) => 667,
chr(204) => 389,
chr(205) => 389,
chr(206) => 389,
chr(207) => 389,
chr(208) => 722,
chr(209) => 722,
chr(210) => 778,
chr(211) => 778,
chr(212) => 778,
chr(213) => 778,
chr(214) => 778,
chr(215) => 570,
chr(216) => 778,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 722,
chr(222) => 611,
chr(223) => 556,
chr(224) => 500,
chr(225) => 500,
chr(226) => 500,
chr(227) => 500,
chr(228) => 500,
chr(229) => 500,
chr(230) => 722,
chr(231) => 444,
chr(232) => 444,
chr(233) => 444,
chr(234) => 444,
chr(235) => 444,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 500,
chr(241) => 556,
chr(242) => 500,
chr(243) => 500,
chr(244) => 500,
chr(245) => 500,
chr(246) => 500,
chr(247) => 570,
chr(248) => 500,
chr(249) => 556,
chr(250) => 556,
chr(251) => 556,
chr(252) => 556,
chr(253) => 500,
chr(254) => 556,
chr(255) => 500);
/trunk/jrest/lib/File/PDF/fonts/helvetica.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['helvetica'] = array(
chr(0) => 278,
chr(1) => 278,
chr(2) => 278,
chr(3) => 278,
chr(4) => 278,
chr(5) => 278,
chr(6) => 278,
chr(7) => 278,
chr(8) => 278,
chr(9) => 278,
chr(10) => 278,
chr(11) => 278,
chr(12) => 278,
chr(13) => 278,
chr(14) => 278,
chr(15) => 278,
chr(16) => 278,
chr(17) => 278,
chr(18) => 278,
chr(19) => 278,
chr(20) => 278,
chr(21) => 278,
chr(22) => 278,
chr(23) => 278,
chr(24) => 278,
chr(25) => 278,
chr(26) => 278,
chr(27) => 278,
chr(28) => 278,
chr(29) => 278,
chr(30) => 278,
chr(31) => 278,
' ' => 278,
'!' => 278,
'"' => 355,
'#' => 556,
'$' => 556,
'%' => 889,
'&' => 667,
'\'' => 191,
'(' => 333,
')' => 333,
'*' => 389,
'+' => 584,
',' => 278,
'-' => 333,
'.' => 278,
'/' => 278,
'0' => 556,
'1' => 556,
'2' => 556,
'3' => 556,
'4' => 556,
'5' => 556,
'6' => 556,
'7' => 556,
'8' => 556,
'9' => 556,
':' => 278,
';' => 278,
'<' => 584,
'=' => 584,
'>' => 584,
'?' => 556,
'@' => 1015,
'A' => 667,
'B' => 667,
'C' => 722,
'D' => 722,
'E' => 667,
'F' => 611,
'G' => 778,
'H' => 722,
'I' => 278,
'J' => 500,
'K' => 667,
'L' => 556,
'M' => 833,
'N' => 722,
'O' => 778,
'P' => 667,
'Q' => 778,
'R' => 722,
'S' => 667,
'T' => 611,
'U' => 722,
'V' => 667,
'W' => 944,
'X' => 667,
'Y' => 667,
'Z' => 611,
'[' => 278,
'\\' => 278,
']' => 278,
'^' => 469,
'_' => 556,
'`' => 333,
'a' => 556,
'b' => 556,
'c' => 500,
'd' => 556,
'e' => 556,
'f' => 278,
'g' => 556,
'h' => 556,
'i' => 222,
'j' => 222,
'k' => 500,
'l' => 222,
'm' => 833,
'n' => 556,
'o' => 556,
'p' => 556,
'q' => 556,
'r' => 333,
's' => 500,
't' => 278,
'u' => 556,
'v' => 500,
'w' => 722,
'x' => 500,
'y' => 500,
'z' => 500,
'{' => 334,
'|' => 260,
'}' => 334,
'~' => 584,
chr(127) => 350,
chr(128) => 556,
chr(129) => 350,
chr(130) => 222,
chr(131) => 556,
chr(132) => 333,
chr(133) => 1000,
chr(134) => 556,
chr(135) => 556,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 667,
chr(139) => 333,
chr(140) => 1000,
chr(141) => 350,
chr(142) => 611,
chr(143) => 350,
chr(144) => 350,
chr(145) => 222,
chr(146) => 222,
chr(147) => 333,
chr(148) => 333,
chr(149) => 350,
chr(150) => 556,
chr(151) => 1000,
chr(152) => 333,
chr(153) => 1000,
chr(154) => 500,
chr(155) => 333,
chr(156) => 944,
chr(157) => 350,
chr(158) => 500,
chr(159) => 667,
chr(160) => 278,
chr(161) => 333,
chr(162) => 556,
chr(163) => 556,
chr(164) => 556,
chr(165) => 556,
chr(166) => 260,
chr(167) => 556,
chr(168) => 333,
chr(169) => 737,
chr(170) => 370,
chr(171) => 556,
chr(172) => 584,
chr(173) => 333,
chr(174) => 737,
chr(175) => 333,
chr(176) => 400,
chr(177) => 584,
chr(178) => 333,
chr(179) => 333,
chr(180) => 333,
chr(181) => 556,
chr(182) => 537,
chr(183) => 278,
chr(184) => 333,
chr(185) => 333,
chr(186) => 365,
chr(187) => 556,
chr(188) => 834,
chr(189) => 834,
chr(190) => 834,
chr(191) => 611,
chr(192) => 667,
chr(193) => 667,
chr(194) => 667,
chr(195) => 667,
chr(196) => 667,
chr(197) => 667,
chr(198) => 1000,
chr(199) => 722,
chr(200) => 667,
chr(201) => 667,
chr(202) => 667,
chr(203) => 667,
chr(204) => 278,
chr(205) => 278,
chr(206) => 278,
chr(207) => 278,
chr(208) => 722,
chr(209) => 722,
chr(210) => 778,
chr(211) => 778,
chr(212) => 778,
chr(213) => 778,
chr(214) => 778,
chr(215) => 584,
chr(216) => 778,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 667,
chr(222) => 667,
chr(223) => 611,
chr(224) => 556,
chr(225) => 556,
chr(226) => 556,
chr(227) => 556,
chr(228) => 556,
chr(229) => 556,
chr(230) => 889,
chr(231) => 500,
chr(232) => 556,
chr(233) => 556,
chr(234) => 556,
chr(235) => 556,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 556,
chr(241) => 556,
chr(242) => 556,
chr(243) => 556,
chr(244) => 556,
chr(245) => 556,
chr(246) => 556,
chr(247) => 584,
chr(248) => 611,
chr(249) => 556,
chr(250) => 556,
chr(251) => 556,
chr(252) => 556,
chr(253) => 500,
chr(254) => 556,
chr(255) => 500);
/trunk/jrest/lib/File/PDF/fonts/symbol.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['symbol'] = array(
chr(0) => 250,
chr(1) => 250,
chr(2) => 250,
chr(3) => 250,
chr(4) => 250,
chr(5) => 250,
chr(6) => 250,
chr(7) => 250,
chr(8) => 250,
chr(9) => 250,
chr(10) => 250,
chr(11) => 250,
chr(12) => 250,
chr(13) => 250,
chr(14) => 250,
chr(15) => 250,
chr(16) => 250,
chr(17) => 250,
chr(18) => 250,
chr(19) => 250,
chr(20) => 250,
chr(21) => 250,
chr(22) => 250,
chr(23) => 250,
chr(24) => 250,
chr(25) => 250,
chr(26) => 250,
chr(27) => 250,
chr(28) => 250,
chr(29) => 250,
chr(30) => 250,
chr(31) => 250,
' ' => 250,
'!' => 333,
'"' => 713,
'#' => 500,
'$' => 549,
'%' => 833,
'&' => 778,
'\'' => 439,
'(' => 333,
')' => 333,
'*' => 500,
'+' => 549,
',' => 250,
'-' => 549,
'.' => 250,
'/' => 278,
'0' => 500,
'1' => 500,
'2' => 500,
'3' => 500,
'4' => 500,
'5' => 500,
'6' => 500,
'7' => 500,
'8' => 500,
'9' => 500,
':' => 278,
';' => 278,
'<' => 549,
'=' => 549,
'>' => 549,
'?' => 444,
'@' => 549,
'A' => 722,
'B' => 667,
'C' => 722,
'D' => 612,
'E' => 611,
'F' => 763,
'G' => 603,
'H' => 722,
'I' => 333,
'J' => 631,
'K' => 722,
'L' => 686,
'M' => 889,
'N' => 722,
'O' => 722,
'P' => 768,
'Q' => 741,
'R' => 556,
'S' => 592,
'T' => 611,
'U' => 690,
'V' => 439,
'W' => 768,
'X' => 645,
'Y' => 795,
'Z' => 611,
'[' => 333,
'\\' => 863,
']' => 333,
'^' => 658,
'_' => 500,
'`' => 500,
'a' => 631,
'b' => 549,
'c' => 549,
'd' => 494,
'e' => 439,
'f' => 521,
'g' => 411,
'h' => 603,
'i' => 329,
'j' => 603,
'k' => 549,
'l' => 549,
'm' => 576,
'n' => 521,
'o' => 549,
'p' => 549,
'q' => 521,
'r' => 549,
's' => 603,
't' => 439,
'u' => 576,
'v' => 713,
'w' => 686,
'x' => 493,
'y' => 686,
'z' => 494,
'{' => 480,
'|' => 200,
'}' => 480,
'~' => 549,
chr(127) => 0,
chr(128) => 0,
chr(129) => 0,
chr(130) => 0,
chr(131) => 0,
chr(132) => 0,
chr(133) => 0,
chr(134) => 0,
chr(135) => 0,
chr(136) => 0,
chr(137) => 0,
chr(138) => 0,
chr(139) => 0,
chr(140) => 0,
chr(141) => 0,
chr(142) => 0,
chr(143) => 0,
chr(144) => 0,
chr(145) => 0,
chr(146) => 0,
chr(147) => 0,
chr(148) => 0,
chr(149) => 0,
chr(150) => 0,
chr(151) => 0,
chr(152) => 0,
chr(153) => 0,
chr(154) => 0,
chr(155) => 0,
chr(156) => 0,
chr(157) => 0,
chr(158) => 0,
chr(159) => 0,
chr(160) => 750,
chr(161) => 620,
chr(162) => 247,
chr(163) => 549,
chr(164) => 167,
chr(165) => 713,
chr(166) => 500,
chr(167) => 753,
chr(168) => 753,
chr(169) => 753,
chr(170) => 753,
chr(171) => 1042,
chr(172) => 987,
chr(173) => 603,
chr(174) => 987,
chr(175) => 603,
chr(176) => 400,
chr(177) => 549,
chr(178) => 411,
chr(179) => 549,
chr(180) => 549,
chr(181) => 713,
chr(182) => 494,
chr(183) => 460,
chr(184) => 549,
chr(185) => 549,
chr(186) => 549,
chr(187) => 549,
chr(188) => 1000,
chr(189) => 603,
chr(190) => 1000,
chr(191) => 658,
chr(192) => 823,
chr(193) => 686,
chr(194) => 795,
chr(195) => 987,
chr(196) => 768,
chr(197) => 768,
chr(198) => 823,
chr(199) => 768,
chr(200) => 768,
chr(201) => 713,
chr(202) => 713,
chr(203) => 713,
chr(204) => 713,
chr(205) => 713,
chr(206) => 713,
chr(207) => 713,
chr(208) => 768,
chr(209) => 713,
chr(210) => 790,
chr(211) => 790,
chr(212) => 890,
chr(213) => 823,
chr(214) => 549,
chr(215) => 250,
chr(216) => 713,
chr(217) => 603,
chr(218) => 603,
chr(219) => 1042,
chr(220) => 987,
chr(221) => 603,
chr(222) => 987,
chr(223) => 603,
chr(224) => 494,
chr(225) => 329,
chr(226) => 790,
chr(227) => 790,
chr(228) => 786,
chr(229) => 713,
chr(230) => 384,
chr(231) => 384,
chr(232) => 384,
chr(233) => 384,
chr(234) => 384,
chr(235) => 384,
chr(236) => 494,
chr(237) => 494,
chr(238) => 494,
chr(239) => 494,
chr(240) => 0,
chr(241) => 329,
chr(242) => 274,
chr(243) => 686,
chr(244) => 686,
chr(245) => 686,
chr(246) => 384,
chr(247) => 384,
chr(248) => 384,
chr(249) => 384,
chr(250) => 384,
chr(251) => 384,
chr(252) => 494,
chr(253) => 494,
chr(254) => 494,
chr(255) => 0);
/trunk/jrest/lib/File/PDF/fonts/helveticab.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['helveticaB'] = array(
chr(0) => 278,
chr(1) => 278,
chr(2) => 278,
chr(3) => 278,
chr(4) => 278,
chr(5) => 278,
chr(6) => 278,
chr(7) => 278,
chr(8) => 278,
chr(9) => 278,
chr(10) => 278,
chr(11) => 278,
chr(12) => 278,
chr(13) => 278,
chr(14) => 278,
chr(15) => 278,
chr(16) => 278,
chr(17) => 278,
chr(18) => 278,
chr(19) => 278,
chr(20) => 278,
chr(21) => 278,
chr(22) => 278,
chr(23) => 278,
chr(24) => 278,
chr(25) => 278,
chr(26) => 278,
chr(27) => 278,
chr(28) => 278,
chr(29) => 278,
chr(30) => 278,
chr(31) => 278,
' ' => 278,
'!' => 333,
'"' => 474,
'#' => 556,
'$' => 556,
'%' => 889,
'&' => 722,
'\'' => 238,
'(' => 333,
')' => 333,
'*' => 389,
'+' => 584,
',' => 278,
'-' => 333,
'.' => 278,
'/' => 278,
'0' => 556,
'1' => 556,
'2' => 556,
'3' => 556,
'4' => 556,
'5' => 556,
'6' => 556,
'7' => 556,
'8' => 556,
'9' => 556,
':' => 333,
';' => 333,
'<' => 584,
'=' => 584,
'>' => 584,
'?' => 611,
'@' => 975,
'A' => 722,
'B' => 722,
'C' => 722,
'D' => 722,
'E' => 667,
'F' => 611,
'G' => 778,
'H' => 722,
'I' => 278,
'J' => 556,
'K' => 722,
'L' => 611,
'M' => 833,
'N' => 722,
'O' => 778,
'P' => 667,
'Q' => 778,
'R' => 722,
'S' => 667,
'T' => 611,
'U' => 722,
'V' => 667,
'W' => 944,
'X' => 667,
'Y' => 667,
'Z' => 611,
'[' => 333,
'\\' => 278,
']' => 333,
'^' => 584,
'_' => 556,
'`' => 333,
'a' => 556,
'b' => 611,
'c' => 556,
'd' => 611,
'e' => 556,
'f' => 333,
'g' => 611,
'h' => 611,
'i' => 278,
'j' => 278,
'k' => 556,
'l' => 278,
'm' => 889,
'n' => 611,
'o' => 611,
'p' => 611,
'q' => 611,
'r' => 389,
's' => 556,
't' => 333,
'u' => 611,
'v' => 556,
'w' => 778,
'x' => 556,
'y' => 556,
'z' => 500,
'{' => 389,
'|' => 280,
'}' => 389,
'~' => 584,
chr(127) => 350,
chr(128) => 556,
chr(129) => 350,
chr(130) => 278,
chr(131) => 556,
chr(132) => 500,
chr(133) => 1000,
chr(134) => 556,
chr(135) => 556,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 667,
chr(139) => 333,
chr(140) => 1000,
chr(141) => 350,
chr(142) => 611,
chr(143) => 350,
chr(144) => 350,
chr(145) => 278,
chr(146) => 278,
chr(147) => 500,
chr(148) => 500,
chr(149) => 350,
chr(150) => 556,
chr(151) => 1000,
chr(152) => 333,
chr(153) => 1000,
chr(154) => 556,
chr(155) => 333,
chr(156) => 944,
chr(157) => 350,
chr(158) => 500,
chr(159) => 667,
chr(160) => 278,
chr(161) => 333,
chr(162) => 556,
chr(163) => 556,
chr(164) => 556,
chr(165) => 556,
chr(166) => 280,
chr(167) => 556,
chr(168) => 333,
chr(169) => 737,
chr(170) => 370,
chr(171) => 556,
chr(172) => 584,
chr(173) => 333,
chr(174) => 737,
chr(175) => 333,
chr(176) => 400,
chr(177) => 584,
chr(178) => 333,
chr(179) => 333,
chr(180) => 333,
chr(181) => 611,
chr(182) => 556,
chr(183) => 278,
chr(184) => 333,
chr(185) => 333,
chr(186) => 365,
chr(187) => 556,
chr(188) => 834,
chr(189) => 834,
chr(190) => 834,
chr(191) => 611,
chr(192) => 722,
chr(193) => 722,
chr(194) => 722,
chr(195) => 722,
chr(196) => 722,
chr(197) => 722,
chr(198) => 1000,
chr(199) => 722,
chr(200) => 667,
chr(201) => 667,
chr(202) => 667,
chr(203) => 667,
chr(204) => 278,
chr(205) => 278,
chr(206) => 278,
chr(207) => 278,
chr(208) => 722,
chr(209) => 722,
chr(210) => 778,
chr(211) => 778,
chr(212) => 778,
chr(213) => 778,
chr(214) => 778,
chr(215) => 584,
chr(216) => 778,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 667,
chr(222) => 667,
chr(223) => 611,
chr(224) => 556,
chr(225) => 556,
chr(226) => 556,
chr(227) => 556,
chr(228) => 556,
chr(229) => 556,
chr(230) => 889,
chr(231) => 556,
chr(232) => 556,
chr(233) => 556,
chr(234) => 556,
chr(235) => 556,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 611,
chr(241) => 611,
chr(242) => 611,
chr(243) => 611,
chr(244) => 611,
chr(245) => 611,
chr(246) => 611,
chr(247) => 584,
chr(248) => 611,
chr(249) => 611,
chr(250) => 611,
chr(251) => 611,
chr(252) => 611,
chr(253) => 556,
chr(254) => 611,
chr(255) => 556);
/trunk/jrest/lib/File/PDF/fonts/courier.php
New file
0,0 → 1,10
<?php
/**
* @package File_PDF
*/
for ($i = 0; $i <= 255; $i++) {
$font_widths['courier'][chr($i)] = 600;
}
$font_widths['courierB'] = $font_widths['courier'];
$font_widths['courierI'] = $font_widths['courier'];
$font_widths['courierBI'] = $font_widths['courier'];
/trunk/jrest/lib/File/PDF/fonts/timesi.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['timesI'] = array(
chr(0) => 250,
chr(1) => 250,
chr(2) => 250,
chr(3) => 250,
chr(4) => 250,
chr(5) => 250,
chr(6) => 250,
chr(7) => 250,
chr(8) => 250,
chr(9) => 250,
chr(10) => 250,
chr(11) => 250,
chr(12) => 250,
chr(13) => 250,
chr(14) => 250,
chr(15) => 250,
chr(16) => 250,
chr(17) => 250,
chr(18) => 250,
chr(19) => 250,
chr(20) => 250,
chr(21) => 250,
chr(22) => 250,
chr(23) => 250,
chr(24) => 250,
chr(25) => 250,
chr(26) => 250,
chr(27) => 250,
chr(28) => 250,
chr(29) => 250,
chr(30) => 250,
chr(31) => 250,
' ' => 250,
'!' => 333,
'"' => 420,
'#' => 500,
'$' => 500,
'%' => 833,
'&' => 778,
'\'' => 214,
'(' => 333,
')' => 333,
'*' => 500,
'+' => 675,
',' => 250,
'-' => 333,
'.' => 250,
'/' => 278,
'0' => 500,
'1' => 500,
'2' => 500,
'3' => 500,
'4' => 500,
'5' => 500,
'6' => 500,
'7' => 500,
'8' => 500,
'9' => 500,
':' => 333,
';' => 333,
'<' => 675,
'=' => 675,
'>' => 675,
'?' => 500,
'@' => 920,
'A' => 611,
'B' => 611,
'C' => 667,
'D' => 722,
'E' => 611,
'F' => 611,
'G' => 722,
'H' => 722,
'I' => 333,
'J' => 444,
'K' => 667,
'L' => 556,
'M' => 833,
'N' => 667,
'O' => 722,
'P' => 611,
'Q' => 722,
'R' => 611,
'S' => 500,
'T' => 556,
'U' => 722,
'V' => 611,
'W' => 833,
'X' => 611,
'Y' => 556,
'Z' => 556,
'[' => 389,
'\\' => 278,
']' => 389,
'^' => 422,
'_' => 500,
'`' => 333,
'a' => 500,
'b' => 500,
'c' => 444,
'd' => 500,
'e' => 444,
'f' => 278,
'g' => 500,
'h' => 500,
'i' => 278,
'j' => 278,
'k' => 444,
'l' => 278,
'm' => 722,
'n' => 500,
'o' => 500,
'p' => 500,
'q' => 500,
'r' => 389,
's' => 389,
't' => 278,
'u' => 500,
'v' => 444,
'w' => 667,
'x' => 444,
'y' => 444,
'z' => 389,
'{' => 400,
'|' => 275,
'}' => 400,
'~' => 541,
chr(127) => 350,
chr(128) => 500,
chr(129) => 350,
chr(130) => 333,
chr(131) => 500,
chr(132) => 556,
chr(133) => 889,
chr(134) => 500,
chr(135) => 500,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 500,
chr(139) => 333,
chr(140) => 944,
chr(141) => 350,
chr(142) => 556,
chr(143) => 350,
chr(144) => 350,
chr(145) => 333,
chr(146) => 333,
chr(147) => 556,
chr(148) => 556,
chr(149) => 350,
chr(150) => 500,
chr(151) => 889,
chr(152) => 333,
chr(153) => 980,
chr(154) => 389,
chr(155) => 333,
chr(156) => 667,
chr(157) => 350,
chr(158) => 389,
chr(159) => 556,
chr(160) => 250,
chr(161) => 389,
chr(162) => 500,
chr(163) => 500,
chr(164) => 500,
chr(165) => 500,
chr(166) => 275,
chr(167) => 500,
chr(168) => 333,
chr(169) => 760,
chr(170) => 276,
chr(171) => 500,
chr(172) => 675,
chr(173) => 333,
chr(174) => 760,
chr(175) => 333,
chr(176) => 400,
chr(177) => 675,
chr(178) => 300,
chr(179) => 300,
chr(180) => 333,
chr(181) => 500,
chr(182) => 523,
chr(183) => 250,
chr(184) => 333,
chr(185) => 300,
chr(186) => 310,
chr(187) => 500,
chr(188) => 750,
chr(189) => 750,
chr(190) => 750,
chr(191) => 500,
chr(192) => 611,
chr(193) => 611,
chr(194) => 611,
chr(195) => 611,
chr(196) => 611,
chr(197) => 611,
chr(198) => 889,
chr(199) => 667,
chr(200) => 611,
chr(201) => 611,
chr(202) => 611,
chr(203) => 611,
chr(204) => 333,
chr(205) => 333,
chr(206) => 333,
chr(207) => 333,
chr(208) => 722,
chr(209) => 667,
chr(210) => 722,
chr(211) => 722,
chr(212) => 722,
chr(213) => 722,
chr(214) => 722,
chr(215) => 675,
chr(216) => 722,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 556,
chr(222) => 611,
chr(223) => 500,
chr(224) => 500,
chr(225) => 500,
chr(226) => 500,
chr(227) => 500,
chr(228) => 500,
chr(229) => 500,
chr(230) => 667,
chr(231) => 444,
chr(232) => 444,
chr(233) => 444,
chr(234) => 444,
chr(235) => 444,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 500,
chr(241) => 500,
chr(242) => 500,
chr(243) => 500,
chr(244) => 500,
chr(245) => 500,
chr(246) => 500,
chr(247) => 675,
chr(248) => 500,
chr(249) => 500,
chr(250) => 500,
chr(251) => 500,
chr(252) => 500,
chr(253) => 444,
chr(254) => 500,
chr(255) => 444);
/trunk/jrest/lib/File/PDF/fonts/timesbi.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['timesBI'] = array(
chr(0) => 250,
chr(1) => 250,
chr(2) => 250,
chr(3) => 250,
chr(4) => 250,
chr(5) => 250,
chr(6) => 250,
chr(7) => 250,
chr(8) => 250,
chr(9) => 250,
chr(10) => 250,
chr(11) => 250,
chr(12) => 250,
chr(13) => 250,
chr(14) => 250,
chr(15) => 250,
chr(16) => 250,
chr(17) => 250,
chr(18) => 250,
chr(19) => 250,
chr(20) => 250,
chr(21) => 250,
chr(22) => 250,
chr(23) => 250,
chr(24) => 250,
chr(25) => 250,
chr(26) => 250,
chr(27) => 250,
chr(28) => 250,
chr(29) => 250,
chr(30) => 250,
chr(31) => 250,
' ' => 250,
'!' => 389,
'"' => 555,
'#' => 500,
'$' => 500,
'%' => 833,
'&' => 778,
'\'' => 278,
'(' => 333,
')' => 333,
'*' => 500,
'+' => 570,
',' => 250,
'-' => 333,
'.' => 250,
'/' => 278,
'0' => 500,
'1' => 500,
'2' => 500,
'3' => 500,
'4' => 500,
'5' => 500,
'6' => 500,
'7' => 500,
'8' => 500,
'9' => 500,
':' => 333,
';' => 333,
'<' => 570,
'=' => 570,
'>' => 570,
'?' => 500,
'@' => 832,
'A' => 667,
'B' => 667,
'C' => 667,
'D' => 722,
'E' => 667,
'F' => 667,
'G' => 722,
'H' => 778,
'I' => 389,
'J' => 500,
'K' => 667,
'L' => 611,
'M' => 889,
'N' => 722,
'O' => 722,
'P' => 611,
'Q' => 722,
'R' => 667,
'S' => 556,
'T' => 611,
'U' => 722,
'V' => 667,
'W' => 889,
'X' => 667,
'Y' => 611,
'Z' => 611,
'[' => 333,
'\\' => 278,
']' => 333,
'^' => 570,
'_' => 500,
'`' => 333,
'a' => 500,
'b' => 500,
'c' => 444,
'd' => 500,
'e' => 444,
'f' => 333,
'g' => 500,
'h' => 556,
'i' => 278,
'j' => 278,
'k' => 500,
'l' => 278,
'm' => 778,
'n' => 556,
'o' => 500,
'p' => 500,
'q' => 500,
'r' => 389,
's' => 389,
't' => 278,
'u' => 556,
'v' => 444,
'w' => 667,
'x' => 500,
'y' => 444,
'z' => 389,
'{' => 348,
'|' => 220,
'}' => 348,
'~' => 570,
chr(127) => 350,
chr(128) => 500,
chr(129) => 350,
chr(130) => 333,
chr(131) => 500,
chr(132) => 500,
chr(133) => 1000,
chr(134) => 500,
chr(135) => 500,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 556,
chr(139) => 333,
chr(140) => 944,
chr(141) => 350,
chr(142) => 611,
chr(143) => 350,
chr(144) => 350,
chr(145) => 333,
chr(146) => 333,
chr(147) => 500,
chr(148) => 500,
chr(149) => 350,
chr(150) => 500,
chr(151) => 1000,
chr(152) => 333,
chr(153) => 1000,
chr(154) => 389,
chr(155) => 333,
chr(156) => 722,
chr(157) => 350,
chr(158) => 389,
chr(159) => 611,
chr(160) => 250,
chr(161) => 389,
chr(162) => 500,
chr(163) => 500,
chr(164) => 500,
chr(165) => 500,
chr(166) => 220,
chr(167) => 500,
chr(168) => 333,
chr(169) => 747,
chr(170) => 266,
chr(171) => 500,
chr(172) => 606,
chr(173) => 333,
chr(174) => 747,
chr(175) => 333,
chr(176) => 400,
chr(177) => 570,
chr(178) => 300,
chr(179) => 300,
chr(180) => 333,
chr(181) => 576,
chr(182) => 500,
chr(183) => 250,
chr(184) => 333,
chr(185) => 300,
chr(186) => 300,
chr(187) => 500,
chr(188) => 750,
chr(189) => 750,
chr(190) => 750,
chr(191) => 500,
chr(192) => 667,
chr(193) => 667,
chr(194) => 667,
chr(195) => 667,
chr(196) => 667,
chr(197) => 667,
chr(198) => 944,
chr(199) => 667,
chr(200) => 667,
chr(201) => 667,
chr(202) => 667,
chr(203) => 667,
chr(204) => 389,
chr(205) => 389,
chr(206) => 389,
chr(207) => 389,
chr(208) => 722,
chr(209) => 722,
chr(210) => 722,
chr(211) => 722,
chr(212) => 722,
chr(213) => 722,
chr(214) => 722,
chr(215) => 570,
chr(216) => 722,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 611,
chr(222) => 611,
chr(223) => 500,
chr(224) => 500,
chr(225) => 500,
chr(226) => 500,
chr(227) => 500,
chr(228) => 500,
chr(229) => 500,
chr(230) => 722,
chr(231) => 444,
chr(232) => 444,
chr(233) => 444,
chr(234) => 444,
chr(235) => 444,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 500,
chr(241) => 556,
chr(242) => 500,
chr(243) => 500,
chr(244) => 500,
chr(245) => 500,
chr(246) => 500,
chr(247) => 570,
chr(248) => 500,
chr(249) => 556,
chr(250) => 556,
chr(251) => 556,
chr(252) => 556,
chr(253) => 444,
chr(254) => 500,
chr(255) => 444);
/trunk/jrest/lib/File/PDF/fonts/zapfdingbats.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['zapfdingbats'] = array(
chr(0) => 0,
chr(1) => 0,
chr(2) => 0,
chr(3) => 0,
chr(4) => 0,
chr(5) => 0,
chr(6) => 0,
chr(7) => 0,
chr(8) => 0,
chr(9) => 0,
chr(10) => 0,
chr(11) => 0,
chr(12) => 0,
chr(13) => 0,
chr(14) => 0,
chr(15) => 0,
chr(16) => 0,
chr(17) => 0,
chr(18) => 0,
chr(19) => 0,
chr(20) => 0,
chr(21) => 0,
chr(22) => 0,
chr(23) => 0,
chr(24) => 0,
chr(25) => 0,
chr(26) => 0,
chr(27) => 0,
chr(28) => 0,
chr(29) => 0,
chr(30) => 0,
chr(31) => 0,
' ' => 278,
'!' => 974,
'"' => 961,
'#' => 974,
'$' => 980,
'%' => 719,
'&' => 789,
'\'' => 790,
'(' => 791,
')' => 690,
'*' => 960,
'+' => 939,
',' => 549,
'-' => 855,
'.' => 911,
'/' => 933,
'0' => 911,
'1' => 945,
'2' => 974,
'3' => 755,
'4' => 846,
'5' => 762,
'6' => 761,
'7' => 571,
'8' => 677,
'9' => 763,
':' => 760,
';' => 759,
'<' => 754,
'=' => 494,
'>' => 552,
'?' => 537,
'@' => 577,
'A' => 692,
'B' => 786,
'C' => 788,
'D' => 788,
'E' => 790,
'F' => 793,
'G' => 794,
'H' => 816,
'I' => 823,
'J' => 789,
'K' => 841,
'L' => 823,
'M' => 833,
'N' => 816,
'O' => 831,
'P' => 923,
'Q' => 744,
'R' => 723,
'S' => 749,
'T' => 790,
'U' => 792,
'V' => 695,
'W' => 776,
'X' => 768,
'Y' => 792,
'Z' => 759,
'[' => 707,
'\\' => 708,
']' => 682,
'^' => 701,
'_' => 826,
'`' => 815,
'a' => 789,
'b' => 789,
'c' => 707,
'd' => 687,
'e' => 696,
'f' => 689,
'g' => 786,
'h' => 787,
'i' => 713,
'j' => 791,
'k' => 785,
'l' => 791,
'm' => 873,
'n' => 761,
'o' => 762,
'p' => 762,
'q' => 759,
'r' => 759,
's' => 892,
't' => 892,
'u' => 788,
'v' => 784,
'w' => 438,
'x' => 138,
'y' => 277,
'z' => 415,
'{' => 392,
'|' => 392,
'}' => 668,
'~' => 668,
chr(127) => 0,
chr(128) => 390,
chr(129) => 390,
chr(130) => 317,
chr(131) => 317,
chr(132) => 276,
chr(133) => 276,
chr(134) => 509,
chr(135) => 509,
chr(136) => 410,
chr(137) => 410,
chr(138) => 234,
chr(139) => 234,
chr(140) => 334,
chr(141) => 334,
chr(142) => 0,
chr(143) => 0,
chr(144) => 0,
chr(145) => 0,
chr(146) => 0,
chr(147) => 0,
chr(148) => 0,
chr(149) => 0,
chr(150) => 0,
chr(151) => 0,
chr(152) => 0,
chr(153) => 0,
chr(154) => 0,
chr(155) => 0,
chr(156) => 0,
chr(157) => 0,
chr(158) => 0,
chr(159) => 0,
chr(160) => 0,
chr(161) => 732,
chr(162) => 544,
chr(163) => 544,
chr(164) => 910,
chr(165) => 667,
chr(166) => 760,
chr(167) => 760,
chr(168) => 776,
chr(169) => 595,
chr(170) => 694,
chr(171) => 626,
chr(172) => 788,
chr(173) => 788,
chr(174) => 788,
chr(175) => 788,
chr(176) => 788,
chr(177) => 788,
chr(178) => 788,
chr(179) => 788,
chr(180) => 788,
chr(181) => 788,
chr(182) => 788,
chr(183) => 788,
chr(184) => 788,
chr(185) => 788,
chr(186) => 788,
chr(187) => 788,
chr(188) => 788,
chr(189) => 788,
chr(190) => 788,
chr(191) => 788,
chr(192) => 788,
chr(193) => 788,
chr(194) => 788,
chr(195) => 788,
chr(196) => 788,
chr(197) => 788,
chr(198) => 788,
chr(199) => 788,
chr(200) => 788,
chr(201) => 788,
chr(202) => 788,
chr(203) => 788,
chr(204) => 788,
chr(205) => 788,
chr(206) => 788,
chr(207) => 788,
chr(208) => 788,
chr(209) => 788,
chr(210) => 788,
chr(211) => 788,
chr(212) => 894,
chr(213) => 838,
chr(214) => 1016,
chr(215) => 458,
chr(216) => 748,
chr(217) => 924,
chr(218) => 748,
chr(219) => 918,
chr(220) => 927,
chr(221) => 928,
chr(222) => 928,
chr(223) => 834,
chr(224) => 873,
chr(225) => 828,
chr(226) => 924,
chr(227) => 924,
chr(228) => 917,
chr(229) => 930,
chr(230) => 931,
chr(231) => 463,
chr(232) => 883,
chr(233) => 836,
chr(234) => 836,
chr(235) => 867,
chr(236) => 867,
chr(237) => 696,
chr(238) => 696,
chr(239) => 874,
chr(240) => 0,
chr(241) => 874,
chr(242) => 760,
chr(243) => 946,
chr(244) => 771,
chr(245) => 865,
chr(246) => 771,
chr(247) => 888,
chr(248) => 967,
chr(249) => 888,
chr(250) => 831,
chr(251) => 873,
chr(252) => 927,
chr(253) => 970,
chr(254) => 918,
chr(255) => 0);
/trunk/jrest/lib/HTTP/Download.php
New file
0,0 → 1,1034
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* HTTP::Download
*
* PHP versions 4 and 5
*
* @category HTTP
* @package HTTP_Download
* @author Michael Wallner <mike@php.net>
* @copyright 2003-2005 Michael Wallner
* @license BSD, revised
* @version CVS: $Id$
* @link http://pear.php.net/package/HTTP_Download
*/
 
// {{{ includes
/**
* Requires PEAR
*/
require_once 'PEAR.php';
 
/**
* Requires HTTP_Header
*/
require_once 'HTTP/Header.php';
// }}}
 
// {{{ constants
/**#@+ Use with HTTP_Download::setContentDisposition() **/
/**
* Send data as attachment
*/
define('HTTP_DOWNLOAD_ATTACHMENT', 'attachment');
/**
* Send data inline
*/
define('HTTP_DOWNLOAD_INLINE', 'inline');
/**#@-**/
 
/**#@+ Use with HTTP_Download::sendArchive() **/
/**
* Send as uncompressed tar archive
*/
define('HTTP_DOWNLOAD_TAR', 'TAR');
/**
* Send as gzipped tar archive
*/
define('HTTP_DOWNLOAD_TGZ', 'TGZ');
/**
* Send as bzip2 compressed tar archive
*/
define('HTTP_DOWNLOAD_BZ2', 'BZ2');
/**
* Send as zip archive
*/
define('HTTP_DOWNLOAD_ZIP', 'ZIP');
/**#@-**/
 
/**#@+
* Error constants
*/
define('HTTP_DOWNLOAD_E_HEADERS_SENT', -1);
define('HTTP_DOWNLOAD_E_NO_EXT_ZLIB', -2);
define('HTTP_DOWNLOAD_E_NO_EXT_MMAGIC', -3);
define('HTTP_DOWNLOAD_E_INVALID_FILE', -4);
define('HTTP_DOWNLOAD_E_INVALID_PARAM', -5);
define('HTTP_DOWNLOAD_E_INVALID_RESOURCE', -6);
define('HTTP_DOWNLOAD_E_INVALID_REQUEST', -7);
define('HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE', -8);
define('HTTP_DOWNLOAD_E_INVALID_ARCHIVE_TYPE', -9);
/**#@-**/
// }}}
 
/**
* Send HTTP Downloads/Responses.
*
* With this package you can handle (hidden) downloads.
* It supports partial downloads, resuming and sending
* raw data ie. from database BLOBs.
*
* <i>ATTENTION:</i>
* You shouldn't use this package together with ob_gzhandler or
* zlib.output_compression enabled in your php.ini, especially
* if you want to send already gzipped data!
*
* @access public
* @version $Revision$
*/
class HTTP_Download
{
// {{{ protected member variables
/**
* Path to file for download
*
* @see HTTP_Download::setFile()
* @access protected
* @var string
*/
var $file = '';
/**
* Data for download
*
* @see HTTP_Download::setData()
* @access protected
* @var string
*/
var $data = null;
/**
* Resource handle for download
*
* @see HTTP_Download::setResource()
* @access protected
* @var int
*/
var $handle = null;
/**
* Whether to gzip the download
*
* @access protected
* @var bool
*/
var $gzip = false;
/**
* Whether to allow caching of the download on the clients side
*
* @access protected
* @var bool
*/
var $cache = true;
/**
* Size of download
*
* @access protected
* @var int
*/
var $size = 0;
/**
* Last modified
*
* @access protected
* @var int
*/
var $lastModified = 0;
/**
* HTTP headers
*
* @access protected
* @var array
*/
var $headers = array(
'Content-Type' => 'application/x-octetstream',
'Pragma' => 'cache',
'Cache-Control' => 'public, must-revalidate, max-age=0',
'Accept-Ranges' => 'bytes',
'X-Sent-By' => 'PEAR::HTTP::Download'
);
/**
* HTTP_Header
*
* @access protected
* @var object
*/
var $HTTP = null;
/**
* ETag
*
* @access protected
* @var string
*/
var $etag = '';
/**
* Buffer Size
*
* @access protected
* @var int
*/
var $bufferSize = 2097152;
/**
* Throttle Delay
*
* @access protected
* @var float
*/
var $throttleDelay = 0;
/**
* Sent Bytes
*
* @access public
* @var int
*/
var $sentBytes = 0;
// }}}
// {{{ constructor
/**
* Constructor
*
* Set supplied parameters.
*
* @access public
* @param array $params associative array of parameters
*
* <b>one of:</b>
* o 'file' => path to file for download
* o 'data' => raw data for download
* o 'resource' => resource handle for download
* <br/>
* <b>and any of:</b>
* o 'cache' => whether to allow cs caching
* o 'gzip' => whether to gzip the download
* o 'lastmodified' => unix timestamp
* o 'contenttype' => content type of download
* o 'contentdisposition' => content disposition
* o 'buffersize' => amount of bytes to buffer
* o 'throttledelay' => amount of secs to sleep
* o 'cachecontrol' => cache privacy and validity
*
* <br />
* 'Content-Disposition' is not HTTP compliant, but most browsers
* follow this header, so it was borrowed from MIME standard.
*
* It looks like this: <br />
* "Content-Disposition: attachment; filename=example.tgz".
*
* @see HTTP_Download::setContentDisposition()
*/
function HTTP_Download($params = array())
{
$this->HTTP = &new HTTP_Header;
$this->setParams($params);
}
// }}}
// {{{ public methods
/**
* Set parameters
*
* Set supplied parameters through its accessor methods.
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param array $params associative array of parameters
*
* @see HTTP_Download::HTTP_Download()
*/
function setParams($params)
{
foreach((array) $params as $param => $value){
$method = 'set'. $param;
if (!method_exists($this, $method)) {
return PEAR::raiseError(
"Method '$method' doesn't exist.",
HTTP_DOWNLOAD_E_INVALID_PARAM
);
}
$e = call_user_func_array(array(&$this, $method), (array) $value);
if (PEAR::isError($e)) {
return $e;
}
}
return true;
}
/**
* Set path to file for download
*
* The Last-Modified header will be set to files filemtime(), actually.
* Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_FILE) if file doesn't exist.
* Sends HTTP 404 status if $send_404 is set to true.
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param string $file path to file for download
* @param bool $send_404 whether to send HTTP/404 if
* the file wasn't found
*/
function setFile($file, $send_404 = true)
{
$file = realpath($file);
if (!is_file($file)) {
if ($send_404) {
$this->HTTP->sendStatusCode(404);
}
return PEAR::raiseError(
"File '$file' not found.",
HTTP_DOWNLOAD_E_INVALID_FILE
);
}
$this->setLastModified(filemtime($file));
$this->file = $file;
$this->size = filesize($file);
return true;
}
/**
* Set data for download
*
* Set $data to null if you want to unset this.
*
* @access public
* @return void
* @param $data raw data to send
*/
function setData($data = null)
{
$this->data = $data;
$this->size = strlen($data);
}
/**
* Set resource for download
*
* The resource handle supplied will be closed after sending the download.
* Returns a PEAR_Error (HTTP_DOWNLOAD_E_INVALID_RESOURCE) if $handle
* is no valid resource. Set $handle to null if you want to unset this.
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param int $handle resource handle
*/
function setResource($handle = null)
{
if (!isset($handle)) {
$this->handle = null;
$this->size = 0;
return true;
}
if (is_resource($handle)) {
$this->handle = $handle;
$filestats = fstat($handle);
$this->size = $filestats['size'];
return true;
}
 
return PEAR::raiseError(
"Handle '$handle' is no valid resource.",
HTTP_DOWNLOAD_E_INVALID_RESOURCE
);
}
/**
* Whether to gzip the download
*
* Returns a PEAR_Error (HTTP_DOWNLOAD_E_NO_EXT_ZLIB)
* if ext/zlib is not available/loadable.
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param bool $gzip whether to gzip the download
*/
function setGzip($gzip = false)
{
if ($gzip && !PEAR::loadExtension('zlib')){
return PEAR::raiseError(
'GZIP compression (ext/zlib) not available.',
HTTP_DOWNLOAD_E_NO_EXT_ZLIB
);
}
$this->gzip = (bool) $gzip;
return true;
}
 
/**
* Whether to allow caching
*
* If set to true (default) we'll send some headers that are commonly
* used for caching purposes like ETag, Cache-Control and Last-Modified.
*
* If caching is disabled, we'll send the download no matter if it
* would actually be cached at the client side.
*
* @access public
* @return void
* @param bool $cache whether to allow caching
*/
function setCache($cache = true)
{
$this->cache = (bool) $cache;
}
/**
* Whether to allow proxies to cache
*
* If set to 'private' proxies shouldn't cache the response.
* This setting defaults to 'public' and affects only cached responses.
*
* @access public
* @return bool
* @param string $cache private or public
* @param int $maxage maximum age of the client cache entry
*/
function setCacheControl($cache = 'public', $maxage = 0)
{
switch ($cache = strToLower($cache))
{
case 'private':
case 'public':
$this->headers['Cache-Control'] =
$cache .', must-revalidate, max-age='. abs($maxage);
return true;
break;
}
return false;
}
/**
* Set ETag
*
* Sets a user-defined ETag for cache-validation. The ETag is usually
* generated by HTTP_Download through its payload information.
*
* @access public
* @return void
* @param string $etag Entity tag used for strong cache validation.
*/
function setETag($etag = null)
{
$this->etag = (string) $etag;
}
/**
* Set Size of Buffer
*
* The amount of bytes specified as buffer size is the maximum amount
* of data read at once from resources or files. The default size is 2M
* (2097152 bytes). Be aware that if you enable gzip compression and
* you set a very low buffer size that the actual file size may grow
* due to added gzip headers for each sent chunk of the specified size.
*
* Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_PARAM) if $size is not
* greater than 0 bytes.
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param int $bytes Amount of bytes to use as buffer.
*/
function setBufferSize($bytes = 2097152)
{
if (0 >= $bytes) {
return PEAR::raiseError(
'Buffer size must be greater than 0 bytes ('. $bytes .' given)',
HTTP_DOWNLOAD_E_INVALID_PARAM);
}
$this->bufferSize = abs($bytes);
return true;
}
/**
* Set Throttle Delay
*
* Set the amount of seconds to sleep after each chunck that has been
* sent. One can implement some sort of throttle through adjusting the
* buffer size and the throttle delay. With the following settings
* HTTP_Download will sleep a second after each 25 K of data sent.
*
* <code>
* Array(
* 'throttledelay' => 1,
* 'buffersize' => 1024 * 25,
* )
* </code>
*
* Just be aware that if gzipp'ing is enabled, decreasing the chunk size
* too much leads to proportionally increased network traffic due to added
* gzip header and bottom bytes around each chunk.
*
* @access public
* @return void
* @param float $seconds Amount of seconds to sleep after each
* chunk that has been sent.
*/
function setThrottleDelay($seconds = 0)
{
$this->throttleDelay = abs($seconds) * 1000;
}
/**
* Set "Last-Modified"
*
* This is usually determined by filemtime() in HTTP_Download::setFile()
* If you set raw data for download with HTTP_Download::setData() and you
* want do send an appropiate "Last-Modified" header, you should call this
* method.
*
* @access public
* @return void
* @param int unix timestamp
*/
function setLastModified($last_modified)
{
$this->lastModified = $this->headers['Last-Modified'] = (int) $last_modified;
}
/**
* Set Content-Disposition header
*
* @see HTTP_Download::HTTP_Download
*
* @access public
* @return void
* @param string $disposition whether to send the download
* inline or as attachment
* @param string $file_name the filename to display in
* the browser's download window
*
* <b>Example:</b>
* <code>
* $HTTP_Download->setContentDisposition(
* HTTP_DOWNLOAD_ATTACHMENT,
* 'download.tgz'
* );
* </code>
*/
function setContentDisposition( $disposition = HTTP_DOWNLOAD_ATTACHMENT,
$file_name = null)
{
$cd = $disposition;
if (isset($file_name)) {
$cd .= '; filename="' . $file_name . '"';
} elseif ($this->file) {
$cd .= '; filename="' . basename($this->file) . '"';
}
$this->headers['Content-Disposition'] = $cd;
}
/**
* Set content type of the download
*
* Default content type of the download will be 'application/x-octetstream'.
* Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) if
* $content_type doesn't seem to be valid.
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param string $content_type content type of file for download
*/
function setContentType($content_type = 'application/x-octetstream')
{
if (!preg_match('/^[a-z]+\w*\/[a-z]+[\w.;= -]*$/', $content_type)) {
return PEAR::raiseError(
"Invalid content type '$content_type' supplied.",
HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE
);
}
$this->headers['Content-Type'] = $content_type;
return true;
}
/**
* Guess content type of file
*
* First we try to use PEAR::MIME_Type, if installed, to detect the content
* type, else we check if ext/mime_magic is loaded and properly configured.
*
* Returns PEAR_Error if:
* o if PEAR::MIME_Type failed to detect a proper content type
* (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE)
* o ext/magic.mime is not installed, or not properly configured
* (HTTP_DOWNLOAD_E_NO_EXT_MMAGIC)
* o mime_content_type() couldn't guess content type or returned
* a content type considered to be bogus by setContentType()
* (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE)
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
*/
function guessContentType()
{
if (class_exists('MIME_Type') || @include_once 'MIME/Type.php') {
if (PEAR::isError($mime_type = MIME_Type::autoDetect($this->file))) {
return PEAR::raiseError($mime_type->getMessage(),
HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE);
}
return $this->setContentType($mime_type);
}
if (!function_exists('mime_content_type')) {
return PEAR::raiseError(
'This feature requires ext/mime_magic!',
HTTP_DOWNLOAD_E_NO_EXT_MMAGIC
);
}
if (!is_file(ini_get('mime_magic.magicfile'))) {
return PEAR::raiseError(
'ext/mime_magic is loaded but not properly configured!',
HTTP_DOWNLOAD_E_NO_EXT_MMAGIC
);
}
if (!$content_type = @mime_content_type($this->file)) {
return PEAR::raiseError(
'Couldn\'t guess content type with mime_content_type().',
HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE
);
}
return $this->setContentType($content_type);
}
 
/**
* Send
*
* Returns PEAR_Error if:
* o HTTP headers were already sent (HTTP_DOWNLOAD_E_HEADERS_SENT)
* o HTTP Range was invalid (HTTP_DOWNLOAD_E_INVALID_REQUEST)
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param bool $autoSetContentDisposition Whether to set the
* Content-Disposition header if it isn't already.
*/
function send($autoSetContentDisposition = true)
{
if (headers_sent()) {
return PEAR::raiseError(
'Headers already sent.',
HTTP_DOWNLOAD_E_HEADERS_SENT
);
}
if (!ini_get('safe_mode')) {
@set_time_limit(0);
}
if ($autoSetContentDisposition &&
!isset($this->headers['Content-Disposition'])) {
$this->setContentDisposition();
}
if ($this->cache) {
$this->headers['ETag'] = $this->generateETag();
if ($this->isCached()) {
$this->HTTP->sendStatusCode(304);
$this->sendHeaders();
return true;
}
} else {
unset($this->headers['Last-Modified']);
}
if (ob_get_level()) {
while (@ob_end_clean());
}
if ($this->gzip) {
@ob_start('ob_gzhandler');
} else {
ob_start();
}
$this->sentBytes = 0;
if ($this->isRangeRequest()) {
$this->HTTP->sendStatusCode(206);
$chunks = $this->getChunks();
} else {
$this->HTTP->sendStatusCode(200);
$chunks = array(array(0, $this->size));
if (!$this->gzip && count(ob_list_handlers()) < 2) {
$this->headers['Content-Length'] = $this->size;
}
}
 
if (PEAR::isError($e = $this->sendChunks($chunks))) {
ob_end_clean();
$this->HTTP->sendStatusCode(416);
return $e;
}
ob_end_flush();
flush();
return true;
}
 
/**
* Static send
*
* @see HTTP_Download::HTTP_Download()
* @see HTTP_Download::send()
*
* @static
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param array $params associative array of parameters
* @param bool $guess whether HTTP_Download::guessContentType()
* should be called
*/
function staticSend($params, $guess = false)
{
$d = &new HTTP_Download();
$e = $d->setParams($params);
if (PEAR::isError($e)) {
return $e;
}
if ($guess) {
$e = $d->guessContentType();
if (PEAR::isError($e)) {
return $e;
}
}
return $d->send();
}
/**
* Send a bunch of files or directories as an archive
*
* Example:
* <code>
* require_once 'HTTP/Download.php';
* HTTP_Download::sendArchive(
* 'myArchive.tgz',
* '/var/ftp/pub/mike',
* HTTP_DOWNLOAD_TGZ,
* '',
* '/var/ftp/pub'
* );
* </code>
*
* @see Archive_Tar::createModify()
* @deprecated use HTTP_Download_Archive::send()
* @static
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param string $name name the sent archive should have
* @param mixed $files files/directories
* @param string $type archive type
* @param string $add_path path that should be prepended to the files
* @param string $strip_path path that should be stripped from the files
*/
function sendArchive( $name,
$files,
$type = HTTP_DOWNLOAD_TGZ,
$add_path = '',
$strip_path = '')
{
require_once 'HTTP/Download/Archive.php';
return HTTP_Download_Archive::send($name, $files, $type,
$add_path, $strip_path);
}
// }}}
// {{{ protected methods
/**
* Generate ETag
*
* @access protected
* @return string
*/
function generateETag()
{
if (!$this->etag) {
if ($this->data) {
$md5 = md5($this->data);
} else {
$fst = is_resource($this->handle) ?
fstat($this->handle) : stat($this->file);
$md5 = md5($fst['mtime'] .'='. $fst['ino'] .'='. $fst['size']);
}
$this->etag = '"' . $md5 . '-' . crc32($md5) . '"';
}
return $this->etag;
}
/**
* Send multiple chunks
*
* @access protected
* @return mixed Returns true on success or PEAR_Error on failure.
* @param array $chunks
*/
function sendChunks($chunks)
{
if (count($chunks) == 1) {
return $this->sendChunk(current($chunks));
}
 
$bound = uniqid('HTTP_DOWNLOAD-', true);
$cType = $this->headers['Content-Type'];
$this->headers['Content-Type'] =
'multipart/byteranges; boundary=' . $bound;
$this->sendHeaders();
foreach ($chunks as $chunk){
if (PEAR::isError($e = $this->sendChunk($chunk, $cType, $bound))) {
return $e;
}
}
#echo "\r\n--$bound--\r\n";
return true;
}
/**
* Send chunk of data
*
* @access protected
* @return mixed Returns true on success or PEAR_Error on failure.
* @param array $chunk start and end offset of the chunk to send
* @param string $cType actual content type
* @param string $bound boundary for multipart/byteranges
*/
function sendChunk($chunk, $cType = null, $bound = null)
{
list($offset, $lastbyte) = $chunk;
$length = ($lastbyte - $offset) + 1;
if ($length < 1) {
return PEAR::raiseError(
"Error processing range request: $offset-$lastbyte/$length",
HTTP_DOWNLOAD_E_INVALID_REQUEST
);
}
$range = $offset . '-' . $lastbyte . '/' . $this->size;
if (isset($cType, $bound)) {
echo "\r\n--$bound\r\n",
"Content-Type: $cType\r\n",
"Content-Range: bytes $range\r\n\r\n";
} else {
if ($this->isRangeRequest()) {
$this->headers['Content-Length'] = $length;
$this->headers['Content-Range'] = 'bytes '. $range;
}
$this->sendHeaders();
}
 
if ($this->data) {
while (($length -= $this->bufferSize) > 0) {
$this->flush(substr($this->data, $offset, $this->bufferSize));
$this->throttleDelay and $this->sleep();
$offset += $this->bufferSize;
}
if ($length) {
$this->flush(substr($this->data, $offset, $this->bufferSize + $length));
}
} else {
if (!is_resource($this->handle)) {
$this->handle = fopen($this->file, 'rb');
}
fseek($this->handle, $offset);
while (($length -= $this->bufferSize) > 0) {
$this->flush(fread($this->handle, $this->bufferSize));
$this->throttleDelay and $this->sleep();
}
if ($length) {
$this->flush(fread($this->handle, $this->bufferSize + $length));
}
}
return true;
}
/**
* Get chunks to send
*
* @access protected
* @return array
*/
function getChunks()
{
$parts = array();
foreach (explode(',', $this->getRanges()) as $chunk){
list($o, $e) = explode('-', $chunk);
if ($e >= $this->size || (empty($e) && $e !== 0 && $e !== '0')) {
$e = $this->size - 1;
}
if (empty($o) && $o !== 0 && $o !== '0') {
$o = $this->size - $e;
$e = $this->size - 1;
}
$parts[] = array($o, $e);
}
return $parts;
}
/**
* Check if range is requested
*
* @access protected
* @return bool
*/
function isRangeRequest()
{
if (!isset($_SERVER['HTTP_RANGE'])) {
return false;
}
return $this->isValidRange();
}
/**
* Get range request
*
* @access protected
* @return array
*/
function getRanges()
{
return preg_match('/^bytes=((\d*-\d*,? ?)+)$/',
@$_SERVER['HTTP_RANGE'], $matches) ? $matches[1] : array();
}
/**
* Check if entity is cached
*
* @access protected
* @return bool
*/
function isCached()
{
return (
(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
$this->lastModified == strtotime(current($a = explode(
';', $_SERVER['HTTP_IF_MODIFIED_SINCE'])))) ||
(isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
$this->compareAsterisk('HTTP_IF_NONE_MATCH', $this->etag))
);
}
/**
* Check if entity hasn't changed
*
* @access protected
* @return bool
*/
function isValidRange()
{
if (isset($_SERVER['HTTP_IF_MATCH']) &&
!$this->compareAsterisk('HTTP_IF_MATCH', $this->etag)) {
return false;
}
if (isset($_SERVER['HTTP_IF_RANGE']) &&
$_SERVER['HTTP_IF_RANGE'] !== $this->etag &&
strtotime($_SERVER['HTTP_IF_RANGE']) !== $this->lastModified) {
return false;
}
if (isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE'])) {
$lm = current($a = explode(';', $_SERVER['HTTP_IF_UNMODIFIED_SINCE']));
if (strtotime($lm) !== $this->lastModified) {
return false;
}
}
if (isset($_SERVER['HTTP_UNLESS_MODIFIED_SINCE'])) {
$lm = current($a = explode(';', $_SERVER['HTTP_UNLESS_MODIFIED_SINCE']));
if (strtotime($lm) !== $this->lastModified) {
return false;
}
}
return true;
}
/**
* Compare against an asterisk or check for equality
*
* @access protected
* @return bool
* @param string key for the $_SERVER array
* @param string string to compare
*/
function compareAsterisk($svar, $compare)
{
foreach (array_map('trim', explode(',', $_SERVER[$svar])) as $request) {
if ($request === '*' || $request === $compare) {
return true;
}
}
return false;
}
/**
* Send HTTP headers
*
* @access protected
* @return void
*/
function sendHeaders()
{
foreach ($this->headers as $header => $value) {
$this->HTTP->setHeader($header, $value);
}
$this->HTTP->sendHeaders();
/* NSAPI won't output anything if we did this */
if (strncasecmp(PHP_SAPI, 'nsapi', 5)) {
ob_flush();
flush();
}
}
/**
* Flush
*
* @access protected
* @return void
* @param string $data
*/
function flush($data = '')
{
if ($dlen = strlen($data)) {
$this->sentBytes += $dlen;
echo $data;
}
ob_flush();
flush();
}
/**
* Sleep
*
* @access protected
* @return void
*/
function sleep()
{
if (OS_WINDOWS) {
com_message_pump($this->throttleDelay);
} else {
usleep($this->throttleDelay * 1000);
}
}
// }}}
}
?>
/trunk/jrest/lib/HTTP/HTTP/Header.php
New file
0,0 → 1,531
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* HTTP::Header
*
* PHP versions 4 and 5
*
* @category HTTP
* @package HTTP_Header
* @author Wolfram Kriesing <wk@visionp.de>
* @author Davey Shafik <davey@php.net>
* @author Michael Wallner <mike@php.net>
* @copyright 2003-2005 The Authors
* @license BSD, revised
* @version CVS: $Id$
* @link http://pear.php.net/package/HTTP_Header
*/
 
/**
* Requires HTTP
*/
require_once 'HTTP.php';
 
/**#@+
* Information Codes
*/
define('HTTP_HEADER_STATUS_100', '100 Continue');
define('HTTP_HEADER_STATUS_101', '101 Switching Protocols');
define('HTTP_HEADER_STATUS_102', '102 Processing');
define('HTTP_HEADER_STATUS_INFORMATIONAL',1);
/**#@-*/
 
/**#+
* Success Codes
*/
define('HTTP_HEADER_STATUS_200', '200 OK');
define('HTTP_HEADER_STATUS_201', '201 Created');
define('HTTP_HEADER_STATUS_202', '202 Accepted');
define('HTTP_HEADER_STATUS_203', '203 Non-Authoritative Information');
define('HTTP_HEADER_STATUS_204', '204 No Content');
define('HTTP_HEADER_STATUS_205', '205 Reset Content');
define('HTTP_HEADER_STATUS_206', '206 Partial Content');
define('HTTP_HEADER_STATUS_207', '207 Multi-Status');
define('HTTP_HEADER_STATUS_SUCCESSFUL',2);
/**#@-*/
 
/**#@+
* Redirection Codes
*/
define('HTTP_HEADER_STATUS_300', '300 Multiple Choices');
define('HTTP_HEADER_STATUS_301', '301 Moved Permanently');
define('HTTP_HEADER_STATUS_302', '302 Found');
define('HTTP_HEADER_STATUS_303', '303 See Other');
define('HTTP_HEADER_STATUS_304', '304 Not Modified');
define('HTTP_HEADER_STATUS_305', '305 Use Proxy');
define('HTTP_HEADER_STATUS_306', '306 (Unused)');
define('HTTP_HEADER_STATUS_307', '307 Temporary Redirect');
define('HTTP_HEADER_STATUS_REDIRECT',3);
/**#@-*/
 
/**#@+
* Error Codes
*/
define('HTTP_HEADER_STATUS_400', '400 Bad Request');
define('HTTP_HEADER_STATUS_401', '401 Unauthorized');
define('HTTP_HEADER_STATUS_402', '402 Payment Granted');
define('HTTP_HEADER_STATUS_403', '403 Forbidden');
define('HTTP_HEADER_STATUS_404', '404 File Not Found');
define('HTTP_HEADER_STATUS_405', '405 Method Not Allowed');
define('HTTP_HEADER_STATUS_406', '406 Not Acceptable');
define('HTTP_HEADER_STATUS_407', '407 Proxy Authentication Required');
define('HTTP_HEADER_STATUS_408', '408 Request Time-out');
define('HTTP_HEADER_STATUS_409', '409 Conflict');
define('HTTP_HEADER_STATUS_410', '410 Gone');
define('HTTP_HEADER_STATUS_411', '411 Length Required');
define('HTTP_HEADER_STATUS_412', '412 Precondition Failed');
define('HTTP_HEADER_STATUS_413', '413 Request Entity Too Large');
define('HTTP_HEADER_STATUS_414', '414 Request-URI Too Large');
define('HTTP_HEADER_STATUS_415', '415 Unsupported Media Type');
define('HTTP_HEADER_STATUS_416', '416 Requested range not satisfiable');
define('HTTP_HEADER_STATUS_417', '417 Expectation Failed');
define('HTTP_HEADER_STATUS_422', '422 Unprocessable Entity');
define('HTTP_HEADER_STATUS_423', '423 Locked');
define('HTTP_HEADER_STATUS_424', '424 Failed Dependency');
define('HTTP_HEADER_STATUS_CLIENT_ERROR',4);
/**#@-*/
 
/**#@+
* Server Errors
*/
define('HTTP_HEADER_STATUS_500', '500 Internal Server Error');
define('HTTP_HEADER_STATUS_501', '501 Not Implemented');
define('HTTP_HEADER_STATUS_502', '502 Bad Gateway');
define('HTTP_HEADER_STATUS_503', '503 Service Unavailable');
define('HTTP_HEADER_STATUS_504', '504 Gateway Time-out');
define('HTTP_HEADER_STATUS_505', '505 HTTP Version not supported');
define('HTTP_HEADER_STATUS_507', '507 Insufficient Storage');
define('HTTP_HEADER_STATUS_SERVER_ERROR',5);
/**#@-*/
 
/**
* HTTP_Header
*
* @package HTTP_Header
* @category HTTP
* @access public
* @version $Revision$
*/
class HTTP_Header extends HTTP
{
/**
* Default Headers
*
* The values that are set as default, are the same as PHP sends by default.
*
* @var array
* @access private
*/
var $_headers = array(
'content-type' => 'text/html',
'pragma' => 'no-cache',
'cache-control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
);
 
/**
* HTTP version
*
* @var string
* @access private
*/
var $_httpVersion = '1.0';
 
/**
* Constructor
*
* Sets HTTP version.
*
* @access public
* @return object HTTP_Header
*/
function HTTP_Header()
{
if (isset($_SERVER['SERVER_PROTOCOL'])) {
$this->setHttpVersion(substr($_SERVER['SERVER_PROTOCOL'], -3));
}
}
/**
* Set HTTP version
*
* @access public
* @return bool Returns true on success or false if version doesn't
* match 1.0 or 1.1 (note: 1 will result in 1.0)
* @param mixed $version HTTP version, either 1.0 or 1.1
*/
function setHttpVersion($version)
{
$version = round((float) $version, 1);
if ($version < 1.0 || $version > 1.1) {
return false;
}
$this->_httpVersion = sprintf('%0.1f', $version);
return true;
}
/**
* Get HTTP version
*
* @access public
* @return string
*/
function getHttpVersion()
{
return $this->_httpVersion;
}
/**
* Set Header
*
* The default value for the Last-Modified header will be current
* date and atime if $value is omitted.
*
* @access public
* @return bool Returns true on success or false if $key was empty or
* $value was not of an scalar type.
* @param string $key The name of the header.
* @param string $value The value of the header. (NULL to unset header)
*/
function setHeader($key, $value = null)
{
if (empty($key) || (isset($value) && !is_scalar($value))) {
return false;
}
$key = strToLower($key);
if ($key == 'last-modified') {
if (!isset($value)) {
$value = HTTP::Date(time());
} elseif (is_numeric($value)) {
$value = HTTP::Date($value);
}
}
if (isset($value)) {
$this->_headers[$key] = $value;
} else {
unset($this->_headers[$key]);
}
return true;
}
 
/**
* Get Header
*
* If $key is omitted, all stored headers will be returned.
*
* @access public
* @return mixed Returns string value of the requested header,
* array values of all headers or false if header $key
* is not set.
* @param string $key The name of the header to fetch.
*/
function getHeader($key = null)
{
if (!isset($key)) {
return $this->_headers;
}
$key = strToLower($key);
if (!isset($this->_headers[$key])) {
return false;
}
return $this->_headers[$key];
}
 
/**
* Send Headers
*
* Send out the header that you set via setHeader().
*
* @access public
* @return bool Returns true on success or false if headers are already
* sent.
* @param array $keys Headers to (not) send, see $include.
* @param array $include If true only $keys matching headers will be
* sent, if false only header not matching $keys will be
* sent.
*/
function sendHeaders($keys = array(), $include = true)
{
if (headers_sent()) {
return false;
}
if (count($keys)) {
array_change_key_case($keys, CASE_LOWER);
foreach ($this->_headers as $key => $value) {
if ($include ? in_array($key, $keys) : !in_array($key, $keys)) {
header($key .': '. $value);
}
}
} else {
foreach ($this->_headers as $header => $value) {
header($header .': '. $value);
}
}
return true;
}
 
/**
* Send Satus Code
*
* Send out the given HTTP-Status code. Use this for example when you
* want to tell the client this page is cached, then you would call
* sendStatusCode(304).
*
* @see HTTP_Header_Cache::exitIfCached()
*
* @access public
* @return bool Returns true on success or false if headers are already
* sent.
* @param int $code The status code to send, i.e. 404, 304, 200, etc.
*/
function sendStatusCode($code)
{
if (headers_sent()) {
return false;
}
if ($code == (int) $code && defined('HTTP_HEADER_STATUS_'. $code)) {
$code = constant('HTTP_HEADER_STATUS_'. $code);
}
if (strncasecmp(PHP_SAPI, 'cgi', 3)) {
header('HTTP/'. $this->_httpVersion .' '. $code);
} else {
header('Status: '. $code);
}
return true;
}
 
/**
* Date to Timestamp
*
* Converts dates like
* Mon, 31 Mar 2003 15:26:34 GMT
* Tue, 15 Nov 1994 12:45:26 GMT
* into a timestamp, strtotime() didn't do it in older versions.
*
* @deprecated Use PHPs strtotime() instead.
* @access public
* @return mixed Returns int unix timestamp or false if the date doesn't
* seem to be a valid GMT date.
* @param string $date The GMT date.
*/
function dateToTimestamp($date)
{
static $months = array(
null => 0, 'Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4,
'May' => 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8, 'Sep' => 9,
'Oct' => 10, 'Nov' => 11, 'Dec' => 12
);
if (-1 < $timestamp = strToTime($date)) {
return $timestamp;
}
if (!preg_match('~[^,]*,\s(\d+)\s(\w+)\s(\d+)\s(\d+):(\d+):(\d+).*~',
$date, $m)) {
return false;
}
// [0] => Mon, 31 Mar 2003 15:42:55 GMT
// [1] => 31 [2] => Mar [3] => 2003 [4] => 15 [5] => 42 [6] => 55
return mktime($m[4], $m[5], $m[6], $months[$m[2]], $m[1], $m[3]);
}
 
/**
* Redirect
*
* This function redirects the client. This is done by issuing a Location
* header and exiting. Additionally to HTTP::redirect() you can also add
* parameters to the url.
*
* If you dont need parameters to be added, simply use HTTP::redirect()
* otherwise use HTTP_Header::redirect().
*
* @see HTTP::redirect()
* @author Wolfram Kriesing <wk@visionp.de>
* @access public
* @return void
* @param string $url The URL to redirect to, if none is given it
* redirects to the current page.
* @param array $param Array of query string parameters to add; usually
* a set of key => value pairs; if an array entry consists
* only of an value it is used as key and the respective
* value is fetched from $GLOBALS[$value]
* @param bool $session Whether the session name/id should be added
*/
function redirect($url = null, $param = array(), $session = false)
{
if (!isset($url)) {
$url = $_SERVER['PHP_SELF'];
}
$qs = array();
 
if ($session) {
$qs[] = session_name() .'='. session_id();
}
 
if (is_array($param) && count($param)) {
if (count($param)) {
foreach ($param as $key => $val) {
if (is_string($key)) {
$qs[] = urlencode($key) .'='. urlencode($val);
} else {
$qs[] = urlencode($val) .'='. urlencode(@$GLOBALS[$val]);
}
}
}
}
if ($qstr = implode('&', $qs)) {
$purl = parse_url($url);
$url .= (isset($purl['query']) ? '&' : '?') . $qstr;
}
 
parent::redirect($url);
}
 
/**#@+
* @author Davey Shafik <davey@php.net>
* @param int $http_code HTTP Code to check
* @access public
*/
 
/**
* Return HTTP Status Code Type
*
* @return int|false
*/
function getStatusType($http_code)
{
if(is_int($http_code) && defined('HTTP_HEADER_STATUS_' .$http_code) || defined($http_code)) {
$type = substr($http_code,0,1);
switch ($type) {
case HTTP_HEADER_STATUS_INFORMATIONAL:
case HTTP_HEADER_STATUS_SUCCESSFUL:
case HTTP_HEADER_STATUS_REDIRECT:
case HTTP_HEADER_STATUS_CLIENT_ERROR:
case HTTP_HEADER_STATUS_SERVER_ERROR:
return $type;
break;
default:
return false;
break;
}
} else {
return false;
}
}
 
/**
* Return Status Code Message
*
* @return string|false
*/
function getStatusText($http_code)
{
if ($this->getStatusType($http_code)) {
if (is_int($http_code) && defined('HTTP_HEADER_STATUS_' .$http_code)) {
return substr(constant('HTTP_HEADER_STATUS_' .$http_code),4);
} else {
return substr($http_code,4);
}
} else {
return false;
}
}
 
/**
* Checks if HTTP Status code is Information (1xx)
*
* @return boolean
*/
function isInformational($http_code)
{
if ($status_type = $this->getStatusType($http_code)) {
return $status_type{0} == HTTP_HEADER_STATUS_INFORMATIONAL;
} else {
return false;
}
}
 
/**
* Checks if HTTP Status code is Successful (2xx)
*
* @return boolean
*/
function isSuccessful($http_code)
{
if ($status_type = $this->getStatusType($http_code)) {
return $status_type{0} == HTTP_HEADER_STATUS_SUCCESSFUL;
} else {
return false;
}
}
 
/**
* Checks if HTTP Status code is a Redirect (3xx)
*
* @return boolean
*/
function isRedirect($http_code)
{
if ($status_type = $this->getStatusType($http_code)) {
return $status_type{0} == HTTP_HEADER_STATUS_REDIRECT;
} else {
return false;
}
}
 
/**
* Checks if HTTP Status code is a Client Error (4xx)
*
* @return boolean
*/
function isClientError($http_code)
{
if ($status_type = $this->getStatusType($http_code)) {
return $status_type{0} == HTTP_HEADER_STATUS_CLIENT_ERROR;
} else {
return false;
}
}
 
/**
* Checks if HTTP Status code is Server Error (5xx)
*
* @return boolean
*/
function isServerError($http_code)
{
if ($status_type = $this->getStatusType($http_code)) {
return $status_type{0} == HTTP_HEADER_STATUS_SERVER_ERROR;
} else {
return false;
}
}
 
/**
* Checks if HTTP Status code is Server OR Client Error (4xx or 5xx)
*
* @return boolean
*/
function isError($http_code)
{
if ($status_type = $this->getStatusType($http_code)) {
return (($status_type == HTTP_HEADER_STATUS_CLIENT_ERROR) || ($status_type == HTTP_HEADER_STATUS_SERVER_ERROR)) ? true : false;
} else {
return false;
}
}
/**#@-*/
}
?>
/trunk/jrest/lib/DB/common.php
New file
0,0 → 1,2040
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Stig Bakken <ssb@php.net> |
// | Tomas V.V.Cox <cox@idecnet.com> |
// | Maintainer: Daniel Convissor <danielc@php.net> |
// +----------------------------------------------------------------------+
//
// $Id$
 
require_once 'PEAR.php';
 
/**
* DB_common is a base class for DB implementations, and must be
* inherited by all such
*
* @package DB
* @version $Id$
* @category Database
* @author Stig Bakken <ssb@php.net>
* @author Tomas V.V.Cox <cox@idecnet.com>
*/
class DB_common extends PEAR
{
// {{{ properties
 
/**
* assoc of capabilities for this DB implementation
* $features['limit'] => 'emulate' => emulate with fetch row by number
* 'alter' => alter the query
* false => skip rows
* @var array
*/
var $features = array();
 
/**
* assoc mapping native error codes to DB ones
* @var array
*/
var $errorcode_map = array();
 
/**
* DB type (mysql, oci8, odbc etc.)
* @var string
*/
var $phptype;
 
/**
* @var string
*/
var $prepare_tokens;
 
/**
* @var string
*/
var $prepare_types;
 
/**
* @var string
*/
var $prepared_queries;
 
/**
* @var integer
*/
var $prepare_maxstmt = 0;
 
/**
* @var string
*/
var $last_query = '';
 
/**
* @var integer
*/
var $fetchmode = DB_FETCHMODE_ORDERED;
 
/**
* @var string
*/
var $fetchmode_object_class = 'stdClass';
 
/**
* Run-time configuration options.
*
* The 'optimize' option has been deprecated. Use the 'portability'
* option instead.
*
* @see DB_common::setOption()
* @var array
*/
var $options = array(
'persistent' => false,
'ssl' => false,
'debug' => 0,
'seqname_format' => '%s_seq',
'autofree' => false,
'portability' => DB_PORTABILITY_NONE,
'optimize' => 'performance', // Deprecated. Use 'portability'.
);
 
/**
* DB handle
* @var resource
*/
var $dbh;
 
// }}}
// {{{ toString()
 
/**
* String conversation
*
* @return string
* @access private
*/
function toString()
{
$info = strtolower(get_class($this));
$info .= ': (phptype=' . $this->phptype .
', dbsyntax=' . $this->dbsyntax .
')';
 
if ($this->connection) {
$info .= ' [connected]';
}
 
return $info;
}
 
// }}}
// {{{ constructor
 
/**
* Constructor
*/
function DB_common()
{
$this->PEAR('DB_Error');
}
 
// }}}
// {{{ quoteString()
 
/**
* DEPRECATED: Quotes a string so it can be safely used within string
* delimiters in a query
*
* @return string quoted string
*
* @see DB_common::quoteSmart(), DB_common::escapeSimple()
* @deprecated Deprecated in release 1.2 or lower
* @internal
*/
function quoteString($string)
{
$string = $this->quote($string);
if ($string{0} == "'") {
return substr($string, 1, -1);
}
return $string;
}
 
// }}}
// {{{ quote()
 
/**
* DEPRECATED: Quotes a string so it can be safely used in a query
*
* @param string $string the input string to quote
*
* @return string The NULL string or the string quotes
* in magic_quote_sybase style
*
* @see DB_common::quoteSmart(), DB_common::escapeSimple()
* @deprecated Deprecated in release 1.6.0
* @internal
*/
function quote($string = null)
{
return ($string === null) ? 'NULL' : "'".str_replace("'", "''", $string)."'";
}
 
// }}}
// {{{ quoteIdentifier()
 
/**
* Quote a string so it can be safely used as a table or column name
*
* Delimiting style depends on which database driver is being used.
*
* NOTE: just because you CAN use delimited identifiers doesn't mean
* you SHOULD use them. In general, they end up causing way more
* problems than they solve.
*
* Portability is broken by using the following characters inside
* delimited identifiers:
* + backtick (<kbd>`</kbd>) -- due to MySQL
* + double quote (<kbd>"</kbd>) -- due to Oracle
* + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
*
* Delimited identifiers are known to generally work correctly under
* the following drivers:
* + mssql
* + mysql
* + mysqli
* + oci8
* + odbc(access)
* + odbc(db2)
* + pgsql
* + sqlite
* + sybase
*
* InterBase doesn't seem to be able to use delimited identifiers
* via PHP 4. They work fine under PHP 5.
*
* @param string $str identifier name to be quoted
*
* @return string quoted identifier string
*
* @since 1.6.0
* @access public
*/
function quoteIdentifier($str)
{
return '"' . str_replace('"', '""', $str) . '"';
}
 
// }}}
// {{{ quoteSmart()
 
/**
* Format input so it can be safely used in a query
*
* The output depends on the PHP data type of input and the database
* type being used.
*
* @param mixed $in data to be quoted
*
* @return mixed the format of the results depends on the input's
* PHP type:
*
* <ul>
* <li>
* <kbd>input</kbd> -> <samp>returns</samp>
* </li>
* <li>
* <kbd>null</kbd> -> the string <samp>NULL</samp>
* </li>
* <li>
* <kbd>integer</kbd> or <kbd>double</kbd> -> the unquoted number
* </li>
* <li>
* &type.bool; -> output depends on the driver in use
* Most drivers return integers: <samp>1</samp> if
* <kbd>true</kbd> or <samp>0</samp> if
* <kbd>false</kbd>.
* Some return strings: <samp>TRUE</samp> if
* <kbd>true</kbd> or <samp>FALSE</samp> if
* <kbd>false</kbd>.
* Finally one returns strings: <samp>T</samp> if
* <kbd>true</kbd> or <samp>F</samp> if
* <kbd>false</kbd>. Here is a list of each DBMS,
* the values returned and the suggested column type:
* <ul>
* <li>
* <kbd>dbase</kbd> -> <samp>T/F</samp>
* (<kbd>Logical</kbd>)
* </li>
* <li>
* <kbd>fbase</kbd> -> <samp>TRUE/FALSE</samp>
* (<kbd>BOOLEAN</kbd>)
* </li>
* <li>
* <kbd>ibase</kbd> -> <samp>1/0</samp>
* (<kbd>SMALLINT</kbd>) [1]
* </li>
* <li>
* <kbd>ifx</kbd> -> <samp>1/0</samp>
* (<kbd>SMALLINT</kbd>) [1]
* </li>
* <li>
* <kbd>msql</kbd> -> <samp>1/0</samp>
* (<kbd>INTEGER</kbd>)
* </li>
* <li>
* <kbd>mssql</kbd> -> <samp>1/0</samp>
* (<kbd>BIT</kbd>)
* </li>
* <li>
* <kbd>mysql</kbd> -> <samp>1/0</samp>
* (<kbd>TINYINT(1)</kbd>)
* </li>
* <li>
* <kbd>mysqli</kbd> -> <samp>1/0</samp>
* (<kbd>TINYINT(1)</kbd>)
* </li>
* <li>
* <kbd>oci8</kbd> -> <samp>1/0</samp>
* (<kbd>NUMBER(1)</kbd>)
* </li>
* <li>
* <kbd>odbc</kbd> -> <samp>1/0</samp>
* (<kbd>SMALLINT</kbd>) [1]
* </li>
* <li>
* <kbd>pgsql</kbd> -> <samp>TRUE/FALSE</samp>
* (<kbd>BOOLEAN</kbd>)
* </li>
* <li>
* <kbd>sqlite</kbd> -> <samp>1/0</samp>
* (<kbd>INTEGER</kbd>)
* </li>
* <li>
* <kbd>sybase</kbd> -> <samp>1/0</samp>
* (<kbd>TINYINT(1)</kbd>)
* </li>
* </ul>
* [1] Accommodate the lowest common denominator because not all
* versions of have <kbd>BOOLEAN</kbd>.
* </li>
* <li>
* other (including strings and numeric strings) ->
* the data with single quotes escaped by preceeding
* single quotes, backslashes are escaped by preceeding
* backslashes, then the whole string is encapsulated
* between single quotes
* </li>
* </ul>
*
* @since 1.6.0
* @see DB_common::escapeSimple()
* @access public
*/
function quoteSmart($in)
{
if (is_int($in) || is_double($in)) {
return $in;
} elseif (is_bool($in)) {
return $in ? 1 : 0;
} elseif (is_null($in)) {
return 'NULL';
} else {
return "'" . $this->escapeSimple($in) . "'";
}
}
 
// }}}
// {{{ escapeSimple()
 
/**
* Escape a string according to the current DBMS's standards
*
* In SQLite, this makes things safe for inserts/updates, but may
* cause problems when performing text comparisons against columns
* containing binary data. See the
* {@link http://php.net/sqlite_escape_string PHP manual} for more info.
*
* @param string $str the string to be escaped
*
* @return string the escaped string
*
* @since 1.6.0
* @see DB_common::quoteSmart()
* @access public
*/
function escapeSimple($str) {
return str_replace("'", "''", $str);
}
 
// }}}
// {{{ provides()
 
/**
* Tell whether a DB implementation or its backend extension
* supports a given feature
*
* @param array $feature name of the feature (see the DB class doc)
* @return bool whether this DB implementation supports $feature
* @access public
*/
function provides($feature)
{
return $this->features[$feature];
}
 
// }}}
// {{{ errorCode()
 
/**
* Map native error codes to DB's portable ones
*
* Requires that the DB implementation's constructor fills
* in the <var>$errorcode_map</var> property.
*
* @param mixed $nativecode the native error code, as returned by the
* backend database extension (string or integer)
*
* @return int a portable DB error code, or DB_ERROR if this DB
* implementation has no mapping for the given error code.
*
* @access public
*/
function errorCode($nativecode)
{
if (isset($this->errorcode_map[$nativecode])) {
return $this->errorcode_map[$nativecode];
}
// Fall back to DB_ERROR if there was no mapping.
return DB_ERROR;
}
 
// }}}
// {{{ errorMessage()
 
/**
* Map a DB error code to a textual message. This is actually
* just a wrapper for DB::errorMessage()
*
* @param integer $dbcode the DB error code
*
* @return string the corresponding error message, of false
* if the error code was unknown
*
* @access public
*/
function errorMessage($dbcode)
{
return DB::errorMessage($this->errorcode_map[$dbcode]);
}
 
// }}}
// {{{ raiseError()
 
/**
* Communicate an error and invoke error callbacks, etc
*
* Basically a wrapper for PEAR::raiseError without the message string.
*
* @param mixed integer error code, or a PEAR error object (all
* other parameters are ignored if this parameter is
* an object
*
* @param int error mode, see PEAR_Error docs
*
* @param mixed If error mode is PEAR_ERROR_TRIGGER, this is the
* error level (E_USER_NOTICE etc). If error mode is
* PEAR_ERROR_CALLBACK, this is the callback function,
* either as a function name, or as an array of an
* object and method name. For other error modes this
* parameter is ignored.
*
* @param string Extra debug information. Defaults to the last
* query and native error code.
*
* @param mixed Native error code, integer or string depending the
* backend.
*
* @return object a PEAR error object
*
* @access public
* @see PEAR_Error
*/
function &raiseError($code = DB_ERROR, $mode = null, $options = null,
$userinfo = null, $nativecode = null)
{
// The error is yet a DB error object
if (is_object($code)) {
// because we the static PEAR::raiseError, our global
// handler should be used if it is set
if ($mode === null && !empty($this->_default_error_mode)) {
$mode = $this->_default_error_mode;
$options = $this->_default_error_options;
}
$tmp = PEAR::raiseError($code, null, $mode, $options, null, null, true);
return $tmp;
}
 
if ($userinfo === null) {
$userinfo = $this->last_query;
}
 
if ($nativecode) {
$userinfo .= ' [nativecode=' . trim($nativecode) . ']';
}
 
$tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo,
'DB_Error', true);
return $tmp;
}
 
// }}}
// {{{ setFetchMode()
 
/**
* Sets which fetch mode should be used by default on queries
* on this connection
*
* @param integer $fetchmode DB_FETCHMODE_ORDERED or
* DB_FETCHMODE_ASSOC, possibly bit-wise OR'ed with
* DB_FETCHMODE_FLIPPED.
*
* @param string $object_class The class of the object
* to be returned by the fetch methods when
* the DB_FETCHMODE_OBJECT mode is selected.
* If no class is specified by default a cast
* to object from the assoc array row will be done.
* There is also the posibility to use and extend the
* 'DB_row' class.
*
* @see DB_FETCHMODE_ORDERED
* @see DB_FETCHMODE_ASSOC
* @see DB_FETCHMODE_FLIPPED
* @see DB_FETCHMODE_OBJECT
* @see DB_row::DB_row()
* @access public
*/
function setFetchMode($fetchmode, $object_class = 'stdClass')
{
switch ($fetchmode) {
case DB_FETCHMODE_OBJECT:
$this->fetchmode_object_class = $object_class;
case DB_FETCHMODE_ORDERED:
case DB_FETCHMODE_ASSOC:
$this->fetchmode = $fetchmode;
break;
default:
return $this->raiseError('invalid fetchmode mode');
}
}
 
// }}}
// {{{ setOption()
 
/**
* Set run-time configuration options for PEAR DB
*
* Options, their data types, default values and description:
* <ul>
* <li>
* <var>autofree</var> <kbd>boolean</kbd> = <samp>false</samp>
* <br />should results be freed automatically when there are no
* more rows?
* </li><li>
* <var>debug</var> <kbd>integer</kbd> = <samp>0</samp>
* <br />debug level
* </li><li>
* <var>persistent</var> <kbd>boolean</kbd> = <samp>false</samp>
* <br />should the connection be persistent?
* </li><li>
* <var>portability</var> <kbd>integer</kbd> = <samp>DB_PORTABILITY_NONE</samp>
* <br />portability mode constant (see below)
* </li><li>
* <var>seqname_format</var> <kbd>string</kbd> = <samp>%s_seq</samp>
* <br />the sprintf() format string used on sequence names. This
* format is applied to sequence names passed to
* createSequence(), nextID() and dropSequence().
* </li><li>
* <var>ssl</var> <kbd>boolean</kbd> = <samp>false</samp>
* <br />use ssl to connect?
* </li>
* </ul>
*
* -----------------------------------------
*
* PORTABILITY MODES
*
* These modes are bitwised, so they can be combined using <kbd>|</kbd>
* and removed using <kbd>^</kbd>. See the examples section below on how
* to do this.
*
* <samp>DB_PORTABILITY_NONE</samp>
* turn off all portability features
*
* This mode gets automatically turned on if the deprecated
* <var>optimize</var> option gets set to <samp>performance</samp>.
*
*
* <samp>DB_PORTABILITY_LOWERCASE</samp>
* convert names of tables and fields to lower case when using
* <kbd>get*()</kbd>, <kbd>fetch*()</kbd> and <kbd>tableInfo()</kbd>
*
* This mode gets automatically turned on in the following databases
* if the deprecated option <var>optimize</var> gets set to
* <samp>portability</samp>:
* + oci8
*
*
* <samp>DB_PORTABILITY_RTRIM</samp>
* right trim the data output by <kbd>get*()</kbd> <kbd>fetch*()</kbd>
*
*
* <samp>DB_PORTABILITY_DELETE_COUNT</samp>
* force reporting the number of rows deleted
*
* Some DBMS's don't count the number of rows deleted when performing
* simple <kbd>DELETE FROM tablename</kbd> queries. This portability
* mode tricks such DBMS's into telling the count by adding
* <samp>WHERE 1=1</samp> to the end of <kbd>DELETE</kbd> queries.
*
* This mode gets automatically turned on in the following databases
* if the deprecated option <var>optimize</var> gets set to
* <samp>portability</samp>:
* + fbsql
* + mysql
* + mysqli
* + sqlite
*
*
* <samp>DB_PORTABILITY_NUMROWS</samp>
* enable hack that makes <kbd>numRows()</kbd> work in Oracle
*
* This mode gets automatically turned on in the following databases
* if the deprecated option <var>optimize</var> gets set to
* <samp>portability</samp>:
* + oci8
*
*
* <samp>DB_PORTABILITY_ERRORS</samp>
* makes certain error messages in certain drivers compatible
* with those from other DBMS's
*
* + mysql, mysqli: change unique/primary key constraints
* DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT
*
* + odbc(access): MS's ODBC driver reports 'no such field' as code
* 07001, which means 'too few parameters.' When this option is on
* that code gets mapped to DB_ERROR_NOSUCHFIELD.
* DB_ERROR_MISMATCH -> DB_ERROR_NOSUCHFIELD
*
*
* <samp>DB_PORTABILITY_NULL_TO_EMPTY</samp>
* convert null values to empty strings in data output by get*() and
* fetch*(). Needed because Oracle considers empty strings to be null,
* while most other DBMS's know the difference between empty and null.
*
*
* <samp>DB_PORTABILITY_ALL</samp>
* turn on all portability features
*
* -----------------------------------------
*
* Example 1. Simple setOption() example
* <code> <?php
* $dbh->setOption('autofree', true);
* ?></code>
*
* Example 2. Portability for lowercasing and trimming
* <code> <?php
* $dbh->setOption('portability',
* DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM);
* ?></code>
*
* Example 3. All portability options except trimming
* <code> <?php
* $dbh->setOption('portability',
* DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM);
* ?></code>
*
* @param string $option option name
* @param mixed $value value for the option
*
* @return int DB_OK on success. DB_Error object on failure.
*
* @see DB_common::$options
*/
function setOption($option, $value)
{
if (isset($this->options[$option])) {
$this->options[$option] = $value;
 
/*
* Backwards compatibility check for the deprecated 'optimize'
* option. Done here in case settings change after connecting.
*/
if ($option == 'optimize') {
if ($value == 'portability') {
switch ($this->phptype) {
case 'oci8':
$this->options['portability'] =
DB_PORTABILITY_LOWERCASE |
DB_PORTABILITY_NUMROWS;
break;
case 'fbsql':
case 'mysql':
case 'mysqli':
case 'sqlite':
$this->options['portability'] =
DB_PORTABILITY_DELETE_COUNT;
break;
}
} else {
$this->options['portability'] = DB_PORTABILITY_NONE;
}
}
 
return DB_OK;
}
return $this->raiseError("unknown option $option");
}
 
// }}}
// {{{ getOption()
 
/**
* Returns the value of an option
*
* @param string $option option name
*
* @return mixed the option value
*/
function getOption($option)
{
if (isset($this->options[$option])) {
return $this->options[$option];
}
return $this->raiseError("unknown option $option");
}
 
// }}}
// {{{ prepare()
 
/**
* Prepares a query for multiple execution with execute()
*
* Creates a query that can be run multiple times. Each time it is run,
* the placeholders, if any, will be replaced by the contents of
* execute()'s $data argument.
*
* Three types of placeholders can be used:
* + <kbd>?</kbd> scalar value (i.e. strings, integers). The system
* will automatically quote and escape the data.
* + <kbd>!</kbd> value is inserted 'as is'
* + <kbd>&</kbd> requires a file name. The file's contents get
* inserted into the query (i.e. saving binary
* data in a db)
*
* Example 1.
* <code> <?php
* $sth = $dbh->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');
* $data = array(
* "John's text",
* "'it''s good'",
* 'filename.txt'
* );
* $res = $dbh->execute($sth, $data);
* ?></code>
*
* Use backslashes to escape placeholder characters if you don't want
* them to be interpreted as placeholders:
* <pre>
* "UPDATE foo SET col=? WHERE col='over \& under'"
* </pre>
*
* With some database backends, this is emulated.
*
* {@internal ibase and oci8 have their own prepare() methods.}}
*
* @param string $query query to be prepared
*
* @return mixed DB statement resource on success. DB_Error on failure.
*
* @see DB_common::execute()
* @access public
*/
function prepare($query)
{
$tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1,
PREG_SPLIT_DELIM_CAPTURE);
$token = 0;
$types = array();
$newtokens = array();
 
foreach ($tokens as $val) {
switch ($val) {
case '?':
$types[$token++] = DB_PARAM_SCALAR;
break;
case '&':
$types[$token++] = DB_PARAM_OPAQUE;
break;
case '!':
$types[$token++] = DB_PARAM_MISC;
break;
default:
$newtokens[] = preg_replace('/\\\([&?!])/', "\\1", $val);
}
}
 
$this->prepare_tokens[] = &$newtokens;
end($this->prepare_tokens);
 
$k = key($this->prepare_tokens);
$this->prepare_types[$k] = $types;
$this->prepared_queries[$k] = implode(' ', $newtokens);
 
return $k;
}
 
// }}}
// {{{ autoPrepare()
 
/**
* Automaticaly generate an insert or update query and pass it to prepare()
*
* @param string $table name of the table
* @param array $table_fields ordered array containing the fields names
* @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE)
* @param string $where in case of update queries, this string will be put after the sql WHERE statement
* @return resource handle for the query
* @see DB_common::prepare(), DB_common::buildManipSQL()
* @access public
*/
function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT, $where = false)
{
$query = $this->buildManipSQL($table, $table_fields, $mode, $where);
return $this->prepare($query);
}
 
// }}}
// {{{ autoExecute()
 
/**
* Automaticaly generate an insert or update query and call prepare()
* and execute() with it
*
* @param string $table name of the table
* @param array $fields_values assoc ($key=>$value) where $key is a field name and $value its value
* @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE)
* @param string $where in case of update queries, this string will be put after the sql WHERE statement
* @return mixed a new DB_Result or a DB_Error when fail
* @see DB_common::autoPrepare(), DB_common::buildManipSQL()
* @access public
*/
function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT, $where = false)
{
$sth = $this->autoPrepare($table, array_keys($fields_values), $mode, $where);
$ret =& $this->execute($sth, array_values($fields_values));
$this->freePrepared($sth);
return $ret;
 
}
 
// }}}
// {{{ buildManipSQL()
 
/**
* Make automaticaly an sql query for prepare()
*
* Example : buildManipSQL('table_sql', array('field1', 'field2', 'field3'), DB_AUTOQUERY_INSERT)
* will return the string : INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?)
* NB : - This belongs more to a SQL Builder class, but this is a simple facility
* - Be carefull ! If you don't give a $where param with an UPDATE query, all
* the records of the table will be updated !
*
* @param string $table name of the table
* @param array $table_fields ordered array containing the fields names
* @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE)
* @param string $where in case of update queries, this string will be put after the sql WHERE statement
* @return string sql query for prepare()
* @access public
*/
function buildManipSQL($table, $table_fields, $mode, $where = false)
{
if (count($table_fields) == 0) {
$this->raiseError(DB_ERROR_NEED_MORE_DATA);
}
$first = true;
switch ($mode) {
case DB_AUTOQUERY_INSERT:
$values = '';
$names = '';
foreach ($table_fields as $value) {
if ($first) {
$first = false;
} else {
$names .= ',';
$values .= ',';
}
$names .= $value;
$values .= '?';
}
return "INSERT INTO $table ($names) VALUES ($values)";
case DB_AUTOQUERY_UPDATE:
$set = '';
foreach ($table_fields as $value) {
if ($first) {
$first = false;
} else {
$set .= ',';
}
$set .= "$value = ?";
}
$sql = "UPDATE $table SET $set";
if ($where) {
$sql .= " WHERE $where";
}
return $sql;
default:
$this->raiseError(DB_ERROR_SYNTAX);
}
}
 
// }}}
// {{{ execute()
 
/**
* Executes a DB statement prepared with prepare()
*
* Example 1.
* <code> <?php
* $sth = $dbh->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');
* $data = array(
* "John's text",
* "'it''s good'",
* 'filename.txt'
* );
* $res =& $dbh->execute($sth, $data);
* ?></code>
*
* @param resource $stmt a DB statement resource returned from prepare()
* @param mixed $data array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return object a new DB_Result or a DB_Error when fail
*
* {@internal ibase and oci8 have their own execute() methods.}}
*
* @see DB_common::prepare()
* @access public
*/
function &execute($stmt, $data = array())
{
$realquery = $this->executeEmulateQuery($stmt, $data);
if (DB::isError($realquery)) {
return $realquery;
}
$result = $this->simpleQuery($realquery);
 
if (DB::isError($result) || $result === DB_OK) {
return $result;
} else {
$tmp =& new DB_result($this, $result);
return $tmp;
}
}
 
// }}}
// {{{ executeEmulateQuery()
 
/**
* Emulates the execute statement, when not supported
*
* @param resource $stmt a DB statement resource returned from execute()
* @param mixed $data array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return mixed a string containing the real query run when emulating
* prepare/execute. A DB error code is returned on failure.
*
* @see DB_common::execute()
* @access private
*/
function executeEmulateQuery($stmt, $data = array())
{
if (!is_array($data)) {
$data = array($data);
}
 
if (count($this->prepare_types[$stmt]) != count($data)) {
$this->last_query = $this->prepared_queries[$stmt];
return $this->raiseError(DB_ERROR_MISMATCH);
}
 
$realquery = $this->prepare_tokens[$stmt][0];
 
$i = 0;
foreach ($data as $value) {
if ($this->prepare_types[$stmt][$i] == DB_PARAM_SCALAR) {
$realquery .= $this->quoteSmart($value);
} elseif ($this->prepare_types[$stmt][$i] == DB_PARAM_OPAQUE) {
$fp = @fopen($value, 'rb');
if (!$fp) {
return $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
}
$realquery .= $this->quoteSmart(fread($fp, filesize($value)));
fclose($fp);
} else {
$realquery .= $value;
}
 
$realquery .= $this->prepare_tokens[$stmt][++$i];
}
 
return $realquery;
}
 
// }}}
// {{{ executeMultiple()
 
/**
* This function does several execute() calls on the same
* statement handle
*
* $data must be an array indexed numerically
* from 0, one execute call is done for every "row" in the array.
*
* If an error occurs during execute(), executeMultiple() does not
* execute the unfinished rows, but rather returns that error.
*
* @param resource $stmt query handle from prepare()
* @param array $data numeric array containing the
* data to insert into the query
*
* @return mixed DB_OK or DB_Error
*
* @see DB_common::prepare(), DB_common::execute()
* @access public
*/
function executeMultiple($stmt, $data)
{
foreach ($data as $value) {
$res =& $this->execute($stmt, $value);
if (DB::isError($res)) {
return $res;
}
}
return DB_OK;
}
 
// }}}
// {{{ freePrepared()
 
/**
* Free the resource used in a prepared query
*
* @param $stmt The resurce returned by the prepare() function
* @see DB_common::prepare()
*/
function freePrepared($stmt)
{
// Free the internal prepared vars
if (isset($this->prepare_tokens[$stmt])) {
unset($this->prepare_tokens[$stmt]);
unset($this->prepare_types[$stmt]);
unset($this->prepared_queries[$stmt]);
return true;
}
return false;
}
 
// }}}
// {{{ modifyQuery()
 
/**
* This method is used by backends to alter queries for various
* reasons
*
* It is defined here to assure that all implementations
* have this method defined.
*
* @param string $query query to modify
*
* @return the new (modified) query
*
* @access private
*/
function modifyQuery($query) {
return $query;
}
 
// }}}
// {{{ modifyLimitQuery()
 
/**
* This method is used by backends to alter limited queries
*
* @param string $query query to modify
* @param integer $from the row to start to fetching
* @param integer $count the numbers of rows to fetch
*
* @return the new (modified) query
*
* @access private
*/
function modifyLimitQuery($query, $from, $count, $params = array())
{
return $query;
}
 
// }}}
// {{{ query()
 
/**
* Send a query to the database and return any results with a
* DB_result object
*
* The query string can be either a normal statement to be sent directly
* to the server OR if <var>$params</var> are passed the query can have
* placeholders and it will be passed through prepare() and execute().
*
* @param string $query the SQL query or the statement to prepare
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return mixed a DB_result object or DB_OK on success, a DB
* error on failure
*
* @see DB_result, DB_common::prepare(), DB_common::execute()
* @access public
*/
function &query($query, $params = array())
{
if (sizeof($params) > 0) {
$sth = $this->prepare($query);
if (DB::isError($sth)) {
return $sth;
}
$ret =& $this->execute($sth, $params);
$this->freePrepared($sth);
return $ret;
} else {
$result = $this->simpleQuery($query);
if (DB::isError($result) || $result === DB_OK) {
return $result;
} else {
$tmp =& new DB_result($this, $result);
return $tmp;
}
}
}
 
// }}}
// {{{ limitQuery()
 
/**
* Generates a limited query
*
* @param string $query query
* @param integer $from the row to start to fetching
* @param integer $count the numbers of rows to fetch
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return mixed a DB_Result object, DB_OK or a DB_Error
*
* @access public
*/
function &limitQuery($query, $from, $count, $params = array())
{
$query = $this->modifyLimitQuery($query, $from, $count, $params);
if (DB::isError($query)){
return $query;
}
$result =& $this->query($query, $params);
if (is_a($result, 'DB_result')) {
$result->setOption('limit_from', $from);
$result->setOption('limit_count', $count);
}
return $result;
}
 
// }}}
// {{{ getOne()
 
/**
* Fetch the first column of the first row of data returned from
* a query
*
* Takes care of doing the query and freeing the results when finished.
*
* @param string $query the SQL query
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return mixed the returned value of the query. DB_Error on failure.
*
* @access public
*/
function &getOne($query, $params = array())
{
settype($params, 'array');
if (sizeof($params) > 0) {
$sth = $this->prepare($query);
if (DB::isError($sth)) {
return $sth;
}
$res =& $this->execute($sth, $params);
$this->freePrepared($sth);
} else {
$res =& $this->query($query);
}
 
if (DB::isError($res)) {
return $res;
}
 
$err = $res->fetchInto($row, DB_FETCHMODE_ORDERED);
$res->free();
 
if ($err !== DB_OK) {
return $err;
}
 
return $row[0];
}
 
// }}}
// {{{ getRow()
 
/**
* Fetch the first row of data returned from a query
*
* Takes care of doing the query and freeing the results when finished.
*
* @param string $query the SQL query
* @param array $params array to be used in execution of the statement.
* Quantity of array elements must match quantity
* of placeholders in query. This function does
* NOT support scalars.
* @param int $fetchmode the fetch mode to use
*
* @return array the first row of results as an array indexed from
* 0, or a DB error code.
*
* @access public
*/
function &getRow($query,
$params = array(),
$fetchmode = DB_FETCHMODE_DEFAULT)
{
// compat check, the params and fetchmode parameters used to
// have the opposite order
if (!is_array($params)) {
if (is_array($fetchmode)) {
if ($params === null) {
$tmp = DB_FETCHMODE_DEFAULT;
} else {
$tmp = $params;
}
$params = $fetchmode;
$fetchmode = $tmp;
} elseif ($params !== null) {
$fetchmode = $params;
$params = array();
}
}
 
if (sizeof($params) > 0) {
$sth = $this->prepare($query);
if (DB::isError($sth)) {
return $sth;
}
$res =& $this->execute($sth, $params);
$this->freePrepared($sth);
} else {
$res =& $this->query($query);
}
 
if (DB::isError($res)) {
return $res;
}
 
$err = $res->fetchInto($row, $fetchmode);
 
$res->free();
 
if ($err !== DB_OK) {
return $err;
}
 
return $row;
}
 
// }}}
// {{{ getCol()
 
/**
* Fetch a single column from a result set and return it as an
* indexed array
*
* @param string $query the SQL query
* @param mixed $col which column to return (integer [column number,
* starting at 0] or string [column name])
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return array an indexed array with the data from the first
* row at index 0, or a DB error code
*
* @see DB_common::query()
* @access public
*/
function &getCol($query, $col = 0, $params = array())
{
settype($params, 'array');
if (sizeof($params) > 0) {
$sth = $this->prepare($query);
 
if (DB::isError($sth)) {
return $sth;
}
 
$res =& $this->execute($sth, $params);
$this->freePrepared($sth);
} else {
$res =& $this->query($query);
}
 
if (DB::isError($res)) {
return $res;
}
 
$fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC;
 
if (!is_array($row = $res->fetchRow($fetchmode))) {
$ret = array();
} else {
if (!array_key_exists($col, $row)) {
$ret =& $this->raiseError(DB_ERROR_NOSUCHFIELD);
} else {
$ret = array($row[$col]);
while (is_array($row = $res->fetchRow($fetchmode))) {
$ret[] = $row[$col];
}
}
}
 
$res->free();
 
if (DB::isError($row)) {
$ret = $row;
}
 
return $ret;
}
 
// }}}
// {{{ getAssoc()
 
/**
* Fetch the entire result set of a query and return it as an
* associative array using the first column as the key
*
* If the result set contains more than two columns, the value
* will be an array of the values from column 2-n. If the result
* set contains only two columns, the returned value will be a
* scalar with the value of the second column (unless forced to an
* array with the $force_array parameter). A DB error code is
* returned on errors. If the result set contains fewer than two
* columns, a DB_ERROR_TRUNCATED error is returned.
*
* For example, if the table "mytable" contains:
*
* <pre>
* ID TEXT DATE
* --------------------------------
* 1 'one' 944679408
* 2 'two' 944679408
* 3 'three' 944679408
* </pre>
*
* Then the call getAssoc('SELECT id,text FROM mytable') returns:
* <pre>
* array(
* '1' => 'one',
* '2' => 'two',
* '3' => 'three',
* )
* </pre>
*
* ...while the call getAssoc('SELECT id,text,date FROM mytable') returns:
* <pre>
* array(
* '1' => array('one', '944679408'),
* '2' => array('two', '944679408'),
* '3' => array('three', '944679408')
* )
* </pre>
*
* If the more than one row occurs with the same value in the
* first column, the last row overwrites all previous ones by
* default. Use the $group parameter if you don't want to
* overwrite like this. Example:
*
* <pre>
* getAssoc('SELECT category,id,name FROM mytable', false, null,
* DB_FETCHMODE_ASSOC, true) returns:
*
* array(
* '1' => array(array('id' => '4', 'name' => 'number four'),
* array('id' => '6', 'name' => 'number six')
* ),
* '9' => array(array('id' => '4', 'name' => 'number four'),
* array('id' => '6', 'name' => 'number six')
* )
* )
* </pre>
*
* Keep in mind that database functions in PHP usually return string
* values for results regardless of the database's internal type.
*
* @param string $query the SQL query
* @param boolean $force_array used only when the query returns
* exactly two columns. If true, the values
* of the returned array will be one-element
* arrays instead of scalars.
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
* @param int $fetchmode the fetch mode to use
* @param boolean $group if true, the values of the returned array
* is wrapped in another array. If the same
* key value (in the first column) repeats
* itself, the values will be appended to
* this array instead of overwriting the
* existing values.
*
* @return array associative array with results from the query.
* DB Error on failure.
*
* @access public
*/
function &getAssoc($query, $force_array = false, $params = array(),
$fetchmode = DB_FETCHMODE_DEFAULT, $group = false)
{
settype($params, 'array');
if (sizeof($params) > 0) {
$sth = $this->prepare($query);
 
if (DB::isError($sth)) {
return $sth;
}
 
$res =& $this->execute($sth, $params);
$this->freePrepared($sth);
} else {
$res =& $this->query($query);
}
 
if (DB::isError($res)) {
return $res;
}
if ($fetchmode == DB_FETCHMODE_DEFAULT) {
$fetchmode = $this->fetchmode;
}
$cols = $res->numCols();
 
if ($cols < 2) {
$tmp =& $this->raiseError(DB_ERROR_TRUNCATED);
return $tmp;
}
 
$results = array();
 
if ($cols > 2 || $force_array) {
// return array values
// XXX this part can be optimized
if ($fetchmode == DB_FETCHMODE_ASSOC) {
while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) {
reset($row);
$key = current($row);
unset($row[key($row)]);
if ($group) {
$results[$key][] = $row;
} else {
$results[$key] = $row;
}
}
} elseif ($fetchmode == DB_FETCHMODE_OBJECT) {
while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) {
$arr = get_object_vars($row);
$key = current($arr);
if ($group) {
$results[$key][] = $row;
} else {
$results[$key] = $row;
}
}
} else {
while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
// we shift away the first element to get
// indices running from 0 again
$key = array_shift($row);
if ($group) {
$results[$key][] = $row;
} else {
$results[$key] = $row;
}
}
}
if (DB::isError($row)) {
$results = $row;
}
} else {
// return scalar values
while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
if ($group) {
$results[$row[0]][] = $row[1];
} else {
$results[$row[0]] = $row[1];
}
}
if (DB::isError($row)) {
$results = $row;
}
}
 
$res->free();
 
return $results;
}
 
// }}}
// {{{ getAll()
 
/**
* Fetch all the rows returned from a query
*
* @param string $query the SQL query
* @param array $params array to be used in execution of the statement.
* Quantity of array elements must match quantity
* of placeholders in query. This function does
* NOT support scalars.
* @param int $fetchmode the fetch mode to use
*
* @return array an nested array. DB error on failure.
*
* @access public
*/
function &getAll($query,
$params = array(),
$fetchmode = DB_FETCHMODE_DEFAULT)
{
// compat check, the params and fetchmode parameters used to
// have the opposite order
if (!is_array($params)) {
if (is_array($fetchmode)) {
if ($params === null) {
$tmp = DB_FETCHMODE_DEFAULT;
} else {
$tmp = $params;
}
$params = $fetchmode;
$fetchmode = $tmp;
} elseif ($params !== null) {
$fetchmode = $params;
$params = array();
}
}
 
if (sizeof($params) > 0) {
$sth = $this->prepare($query);
 
if (DB::isError($sth)) {
return $sth;
}
 
$res =& $this->execute($sth, $params);
$this->freePrepared($sth);
} else {
$res =& $this->query($query);
}
 
if (DB::isError($res) || $res === DB_OK) {
return $res;
}
 
$results = array();
while (DB_OK === $res->fetchInto($row, $fetchmode)) {
if ($fetchmode & DB_FETCHMODE_FLIPPED) {
foreach ($row as $key => $val) {
$results[$key][] = $val;
}
} else {
$results[] = $row;
}
}
 
$res->free();
 
if (DB::isError($row)) {
$tmp =& $this->raiseError($row);
return $tmp;
}
return $results;
}
 
// }}}
// {{{ autoCommit()
 
/**
* enable automatic Commit
*
* @param boolean $onoff
* @return mixed DB_Error
*
* @access public
*/
function autoCommit($onoff=false)
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ commit()
 
/**
* starts a Commit
*
* @return mixed DB_Error
*
* @access public
*/
function commit()
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ rollback()
 
/**
* starts a rollback
*
* @return mixed DB_Error
*
* @access public
*/
function rollback()
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ numRows()
 
/**
* Returns the number of rows in a result object
*
* @param object DB_Result the result object to check
*
* @return mixed DB_Error or the number of rows
*
* @access public
*/
function numRows($result)
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ affectedRows()
 
/**
* Returns the affected rows of a query
*
* @return mixed DB_Error or number of rows
*
* @access public
*/
function affectedRows()
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ errorNative()
 
/**
* Returns an errormessage, provides by the database
*
* @return mixed DB_Error or message
*
* @access public
*/
function errorNative()
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ getSequenceName()
 
/**
* Generate the name used inside the database for a sequence
*
* The createSequence() docblock contains notes about storing sequence
* names.
*
* @param string $sqn the sequence's public name
*
* @return string the sequence's name in the backend
*
* @see DB_common::createSequence(), DB_common::dropSequence(),
* DB_common::nextID(), DB_common::setOption()
* @access private
*/
function getSequenceName($sqn)
{
return sprintf($this->getOption('seqname_format'),
preg_replace('/[^a-z0-9_.]/i', '_', $sqn));
}
 
// }}}
// {{{ nextId()
 
/**
* Returns the next free id in a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true, the seqence is automatically
* created if it does not exist
*
* @return int the next id number in the sequence. DB_Error if problem.
*
* @see DB_common::createSequence(), DB_common::dropSequence(),
* DB_common::getSequenceName()
* @access public
*/
function nextId($seq_name, $ondemand = true)
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ createSequence()
 
/**
* Creates a new sequence
*
* The name of a given sequence is determined by passing the string
* provided in the <var>$seq_name</var> argument through PHP's sprintf()
* function using the value from the <var>seqname_format</var> option as
* the sprintf()'s format argument.
*
* <var>seqname_format</var> is set via setOption().
*
* @param string $seq_name name of the new sequence
*
* @return int DB_OK on success. A DB_Error object is returned if
* problems arise.
*
* @see DB_common::dropSequence(), DB_common::getSequenceName(),
* DB_common::nextID()
* @access public
*/
function createSequence($seq_name)
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ dropSequence()
 
/**
* Deletes a sequence
*
* @param string $seq_name name of the sequence to be deleted
*
* @return int DB_OK on success. DB_Error if problems.
*
* @see DB_common::createSequence(), DB_common::getSequenceName(),
* DB_common::nextID()
* @access public
*/
function dropSequence($seq_name)
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table or a result set
*
* The format of the resulting array depends on which <var>$mode</var>
* you select. The sample output below is based on this query:
* <pre>
* SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
* FROM tblFoo
* JOIN tblBar ON tblFoo.fldId = tblBar.fldId
* </pre>
*
* <ul>
* <li>
*
* <kbd>null</kbd> (default)
* <pre>
* [0] => Array (
* [table] => tblFoo
* [name] => fldId
* [type] => int
* [len] => 11
* [flags] => primary_key not_null
* )
* [1] => Array (
* [table] => tblFoo
* [name] => fldPhone
* [type] => string
* [len] => 20
* [flags] =>
* )
* [2] => Array (
* [table] => tblBar
* [name] => fldId
* [type] => int
* [len] => 11
* [flags] => primary_key not_null
* )
* </pre>
*
* </li><li>
*
* <kbd>DB_TABLEINFO_ORDER</kbd>
*
* <p>In addition to the information found in the default output,
* a notation of the number of columns is provided by the
* <samp>num_fields</samp> element while the <samp>order</samp>
* element provides an array with the column names as the keys and
* their location index number (corresponding to the keys in the
* the default output) as the values.</p>
*
* <p>If a result set has identical field names, the last one is
* used.</p>
*
* <pre>
* [num_fields] => 3
* [order] => Array (
* [fldId] => 2
* [fldTrans] => 1
* )
* </pre>
*
* </li><li>
*
* <kbd>DB_TABLEINFO_ORDERTABLE</kbd>
*
* <p>Similar to <kbd>DB_TABLEINFO_ORDER</kbd> but adds more
* dimensions to the array in which the table names are keys and
* the field names are sub-keys. This is helpful for queries that
* join tables which have identical field names.</p>
*
* <pre>
* [num_fields] => 3
* [ordertable] => Array (
* [tblFoo] => Array (
* [fldId] => 0
* [fldPhone] => 1
* )
* [tblBar] => Array (
* [fldId] => 2
* )
* )
* </pre>
*
* </li>
* </ul>
*
* The <samp>flags</samp> element contains a space separated list
* of extra information about the field. This data is inconsistent
* between DBMS's due to the way each DBMS works.
* + <samp>primary_key</samp>
* + <samp>unique_key</samp>
* + <samp>multiple_key</samp>
* + <samp>not_null</samp>
*
* Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp>
* elements if <var>$result</var> is a table name. The following DBMS's
* provide full information from queries:
* + fbsql
* + mysql
*
* If the 'portability' option has <samp>DB_PORTABILITY_LOWERCASE</samp>
* turned on, the names of tables and fields will be lowercased.
*
* @param object|string $result DB_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode either unused or one of the tableInfo modes:
* <kbd>DB_TABLEINFO_ORDERTABLE</kbd>,
* <kbd>DB_TABLEINFO_ORDER</kbd> or
* <kbd>DB_TABLEINFO_FULL</kbd> (which does both).
* These are bitwise, so the first two can be
* combined using <kbd>|</kbd>.
* @return array an associative array with the information requested.
* If something goes wrong an error object is returned.
*
* @see DB_common::setOption()
* @access public
*/
function tableInfo($result, $mode = null)
{
/*
* If the DB_<driver> class has a tableInfo() method, that one
* overrides this one. But, if the driver doesn't have one,
* this method runs and tells users about that fact.
*/
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ getTables()
 
/**
* @deprecated Deprecated in release 1.2 or lower
*/
function getTables()
{
return $this->getListOf('tables');
}
 
// }}}
// {{{ getListOf()
 
/**
* list internal DB info
* valid values for $type are db dependent,
* often: databases, users, view, functions
*
* @param string $type type of requested info
*
* @return mixed DB_Error or the requested data
*
* @access public
*/
function getListOf($type)
{
$sql = $this->getSpecialQuery($type);
if ($sql === null) { // No support
return $this->raiseError(DB_ERROR_UNSUPPORTED);
} elseif (is_int($sql) || DB::isError($sql)) { // Previous error
return $this->raiseError($sql);
} elseif (is_array($sql)) { // Already the result
return $sql;
}
return $this->getCol($sql); // Launch this query
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Returns the query needed to get some backend info
*
* @param string $type What kind of info you want to retrieve
*
* @return string The SQL query string
*
* @access public
*/
function getSpecialQuery($type)
{
return $this->raiseError(DB_ERROR_UNSUPPORTED);
}
 
// }}}
// {{{ _rtrimArrayValues()
 
/**
* Right trim all strings in an array
*
* @param array $array the array to be trimmed (passed by reference)
* @return void
* @access private
*/
function _rtrimArrayValues(&$array)
{
foreach ($array as $key => $value) {
if (is_string($value)) {
$array[$key] = rtrim($value);
}
}
}
 
// }}}
// {{{ _convertNullArrayValuesToEmpty()
 
/**
* Convert all null values in an array to empty strings
*
* @param array $array the array to be de-nullified (passed by reference)
* @return void
* @access private
*/
function _convertNullArrayValuesToEmpty(&$array)
{
foreach ($array as $key => $value) {
if (is_null($value)) {
$array[$key] = '';
}
}
}
 
// }}}
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/jrest/lib/DB/mysql.php
New file
0,0 → 1,916
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Stig Bakken <ssb@php.net> |
// | Maintainer: Daniel Convissor <danielc@php.net> |
// +----------------------------------------------------------------------+
//
// $Id$
 
 
// XXX legend:
//
// XXX ERRORMSG: The error message from the mysql function should
// be registered here.
//
// TODO/wishlist:
// longReadlen
// binmode
 
 
require_once 'DB/common.php';
 
/**
* Database independent query interface definition for PHP's MySQL
* extension.
*
* This is for MySQL versions 4.0 and below.
*
* @package DB
* @version $Id$
* @category Database
* @author Stig Bakken <ssb@php.net>
*/
class DB_mysql extends DB_common
{
// {{{ properties
 
var $connection;
var $phptype, $dbsyntax;
var $prepare_tokens = array();
var $prepare_types = array();
var $num_rows = array();
var $transaction_opcount = 0;
var $autocommit = true;
var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */
var $_db = false;
 
// }}}
// {{{ constructor
 
/**
* DB_mysql constructor.
*
* @access public
*/
function DB_mysql()
{
$this->DB_common();
$this->phptype = 'mysql';
$this->dbsyntax = 'mysql';
$this->features = array(
'prepare' => false,
'pconnect' => true,
'transactions' => true,
'limit' => 'alter'
);
$this->errorcode_map = array(
1004 => DB_ERROR_CANNOT_CREATE,
1005 => DB_ERROR_CANNOT_CREATE,
1006 => DB_ERROR_CANNOT_CREATE,
1007 => DB_ERROR_ALREADY_EXISTS,
1008 => DB_ERROR_CANNOT_DROP,
1022 => DB_ERROR_ALREADY_EXISTS,
1046 => DB_ERROR_NODBSELECTED,
1048 => DB_ERROR_CONSTRAINT,
1050 => DB_ERROR_ALREADY_EXISTS,
1051 => DB_ERROR_NOSUCHTABLE,
1054 => DB_ERROR_NOSUCHFIELD,
1062 => DB_ERROR_ALREADY_EXISTS,
1064 => DB_ERROR_SYNTAX,
1100 => DB_ERROR_NOT_LOCKED,
1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
1146 => DB_ERROR_NOSUCHTABLE,
1216 => DB_ERROR_CONSTRAINT,
1217 => DB_ERROR_CONSTRAINT,
);
}
 
// }}}
// {{{ connect()
 
/**
* Connect to a database and log in as the specified user.
*
* @param $dsn the data source name (see DB::parseDSN for syntax)
* @param $persistent (optional) whether the connection should
* be persistent
* @access public
* @return int DB_OK on success, a DB error on failure
*/
function connect($dsninfo, $persistent = false)
{
if (!DB::assertExtension('mysql')) {
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
}
$this->dsn = $dsninfo;
if ($dsninfo['protocol'] && $dsninfo['protocol'] == 'unix') {
$dbhost = ':' . $dsninfo['socket'];
} else {
$dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
if ($dsninfo['port']) {
$dbhost .= ':' . $dsninfo['port'];
}
}
 
$connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
 
if ($dbhost && $dsninfo['username'] && isset($dsninfo['password'])) {
$conn = @$connect_function($dbhost, $dsninfo['username'],
$dsninfo['password']);
} elseif ($dbhost && $dsninfo['username']) {
$conn = @$connect_function($dbhost, $dsninfo['username']);
} elseif ($dbhost) {
$conn = @$connect_function($dbhost);
} else {
$conn = false;
}
if (!$conn) {
if (($err = @mysql_error()) != '') {
return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
null, $err);
} elseif (empty($php_errormsg)) {
return $this->raiseError(DB_ERROR_CONNECT_FAILED);
} else {
return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
null, $php_errormsg);
}
}
 
if ($dsninfo['database']) {
if (!@mysql_select_db($dsninfo['database'], $conn)) {
switch(mysql_errno($conn)) {
case 1049:
return $this->raiseError(DB_ERROR_NOSUCHDB, null, null,
null, @mysql_error($conn));
case 1044:
return $this->raiseError(DB_ERROR_ACCESS_VIOLATION, null, null,
null, @mysql_error($conn));
default:
return $this->raiseError(DB_ERROR, null, null,
null, @mysql_error($conn));
}
}
// fix to allow calls to different databases in the same script
$this->_db = $dsninfo['database'];
}
 
$this->connection = $conn;
return DB_OK;
}
 
// }}}
// {{{ disconnect()
 
/**
* Log out and disconnect from the database.
*
* @access public
*
* @return bool true on success, false if not connected.
*/
function disconnect()
{
$ret = @mysql_close($this->connection);
$this->connection = null;
return $ret;
}
 
// }}}
// {{{ simpleQuery()
 
/**
* Send a query to MySQL and return the results as a MySQL resource
* identifier.
*
* @param the SQL query
*
* @access public
*
* @return mixed returns a valid MySQL result for successful SELECT
* queries, DB_OK for other successful queries. A DB error is
* returned on failure.
*/
function simpleQuery($query)
{
$ismanip = DB::isManip($query);
$this->last_query = $query;
$query = $this->modifyQuery($query);
if ($this->_db) {
if (!@mysql_select_db($this->_db, $this->connection)) {
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
}
}
if (!$this->autocommit && $ismanip) {
if ($this->transaction_opcount == 0) {
$result = @mysql_query('SET AUTOCOMMIT=0', $this->connection);
$result = @mysql_query('BEGIN', $this->connection);
if (!$result) {
return $this->mysqlRaiseError();
}
}
$this->transaction_opcount++;
}
$result = @mysql_query($query, $this->connection);
if (!$result) {
return $this->mysqlRaiseError();
}
if (is_resource($result)) {
$numrows = $this->numrows($result);
if (is_object($numrows)) {
return $numrows;
}
$this->num_rows[(int)$result] = $numrows;
return $result;
}
return DB_OK;
}
 
// }}}
// {{{ nextResult()
 
/**
* Move the internal mysql result pointer to the next available result
*
* This method has not been implemented yet.
*
* @param a valid sql result resource
*
* @access public
*
* @return false
*/
function nextResult($result)
{
return false;
}
 
// }}}
// {{{ fetchInto()
 
/**
* Fetch a row and insert the data into an existing array.
*
* Formating of the array and the data therein are configurable.
* See DB_result::fetchInto() for more information.
*
* @param resource $result query result identifier
* @param array $arr (reference) array where data from the row
* should be placed
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch
*
* @return mixed DB_OK on success, null when end of result set is
* reached or on failure
*
* @see DB_result::fetchInto()
* @access private
*/
function fetchInto($result, &$arr, $fetchmode, $rownum=null)
{
if ($rownum !== null) {
if (!@mysql_data_seek($result, $rownum)) {
return null;
}
}
if ($fetchmode & DB_FETCHMODE_ASSOC) {
$arr = @mysql_fetch_array($result, MYSQL_ASSOC);
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
$arr = array_change_key_case($arr, CASE_LOWER);
}
} else {
$arr = @mysql_fetch_row($result);
}
if (!$arr) {
// See: http://bugs.php.net/bug.php?id=22328
// for why we can't check errors on fetching
return null;
/*
$errno = @mysql_errno($this->connection);
if (!$errno) {
return null;
}
return $this->mysqlRaiseError($errno);
*/
}
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
/*
* Even though this DBMS already trims output, we do this because
* a field might have intentional whitespace at the end that
* gets removed by DB_PORTABILITY_RTRIM under another driver.
*/
$this->_rtrimArrayValues($arr);
}
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
$this->_convertNullArrayValuesToEmpty($arr);
}
return DB_OK;
}
 
// }}}
// {{{ freeResult()
 
/**
* Free the internal resources associated with $result.
*
* @param $result MySQL result identifier
*
* @access public
*
* @return bool true on success, false if $result is invalid
*/
function freeResult($result)
{
unset($this->num_rows[(int)$result]);
return @mysql_free_result($result);
}
 
// }}}
// {{{ numCols()
 
/**
* Get the number of columns in a result set.
*
* @param $result MySQL result identifier
*
* @access public
*
* @return int the number of columns per row in $result
*/
function numCols($result)
{
$cols = @mysql_num_fields($result);
 
if (!$cols) {
return $this->mysqlRaiseError();
}
 
return $cols;
}
 
// }}}
// {{{ numRows()
 
/**
* Get the number of rows in a result set.
*
* @param $result MySQL result identifier
*
* @access public
*
* @return int the number of rows in $result
*/
function numRows($result)
{
$rows = @mysql_num_rows($result);
if ($rows === null) {
return $this->mysqlRaiseError();
}
return $rows;
}
 
// }}}
// {{{ autoCommit()
 
/**
* Enable/disable automatic commits
*/
function autoCommit($onoff = false)
{
// XXX if $this->transaction_opcount > 0, we should probably
// issue a warning here.
$this->autocommit = $onoff ? true : false;
return DB_OK;
}
 
// }}}
// {{{ commit()
 
/**
* Commit the current transaction.
*/
function commit()
{
if ($this->transaction_opcount > 0) {
if ($this->_db) {
if (!@mysql_select_db($this->_db, $this->connection)) {
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
}
}
$result = @mysql_query('COMMIT', $this->connection);
$result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
$this->transaction_opcount = 0;
if (!$result) {
return $this->mysqlRaiseError();
}
}
return DB_OK;
}
 
// }}}
// {{{ rollback()
 
/**
* Roll back (undo) the current transaction.
*/
function rollback()
{
if ($this->transaction_opcount > 0) {
if ($this->_db) {
if (!@mysql_select_db($this->_db, $this->connection)) {
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
}
}
$result = @mysql_query('ROLLBACK', $this->connection);
$result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
$this->transaction_opcount = 0;
if (!$result) {
return $this->mysqlRaiseError();
}
}
return DB_OK;
}
 
// }}}
// {{{ affectedRows()
 
/**
* Gets the number of rows affected by the data manipulation
* query. For other queries, this function returns 0.
*
* @return number of rows affected by the last query
*/
function affectedRows()
{
if (DB::isManip($this->last_query)) {
return @mysql_affected_rows($this->connection);
} else {
return 0;
}
}
 
// }}}
// {{{ errorNative()
 
/**
* Get the native error code of the last error (if any) that
* occured on the current connection.
*
* @access public
*
* @return int native MySQL error code
*/
function errorNative()
{
return @mysql_errno($this->connection);
}
 
// }}}
// {{{ nextId()
 
/**
* Returns the next free id in a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true, the seqence is automatically
* created if it does not exist
*
* @return int the next id number in the sequence. DB_Error if problem.
*
* @internal
* @see DB_common::nextID()
* @access public
*/
function nextId($seq_name, $ondemand = true)
{
$seqname = $this->getSequenceName($seq_name);
do {
$repeat = 0;
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->query("UPDATE ${seqname} ".
'SET id=LAST_INSERT_ID(id+1)');
$this->popErrorHandling();
if ($result === DB_OK) {
/** COMMON CASE **/
$id = @mysql_insert_id($this->connection);
if ($id != 0) {
return $id;
}
/** EMPTY SEQ TABLE **/
// Sequence table must be empty for some reason, so fill it and return 1
// Obtain a user-level lock
$result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
if (DB::isError($result)) {
return $this->raiseError($result);
}
if ($result == 0) {
// Failed to get the lock, bail with a DB_ERROR_NOT_LOCKED error
return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
}
 
// add the default value
$result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)");
if (DB::isError($result)) {
return $this->raiseError($result);
}
 
// Release the lock
$result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
if (DB::isError($result)) {
return $this->raiseError($result);
}
// We know what the result will be, so no need to try again
return 1;
 
/** ONDEMAND TABLE CREATION **/
} elseif ($ondemand && DB::isError($result) &&
$result->getCode() == DB_ERROR_NOSUCHTABLE)
{
$result = $this->createSequence($seq_name);
if (DB::isError($result)) {
return $this->raiseError($result);
} else {
$repeat = 1;
}
 
/** BACKWARDS COMPAT **/
} elseif (DB::isError($result) &&
$result->getCode() == DB_ERROR_ALREADY_EXISTS)
{
// see _BCsequence() comment
$result = $this->_BCsequence($seqname);
if (DB::isError($result)) {
return $this->raiseError($result);
}
$repeat = 1;
}
} while ($repeat);
 
return $this->raiseError($result);
}
 
// }}}
// {{{ createSequence()
 
/**
* Creates a new sequence
*
* @param string $seq_name name of the new sequence
*
* @return int DB_OK on success. A DB_Error object is returned if
* problems arise.
*
* @internal
* @see DB_common::createSequence()
* @access public
*/
function createSequence($seq_name)
{
$seqname = $this->getSequenceName($seq_name);
$res = $this->query("CREATE TABLE ${seqname} ".
'(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'.
' PRIMARY KEY(id))');
if (DB::isError($res)) {
return $res;
}
// insert yields value 1, nextId call will generate ID 2
$res = $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
if (DB::isError($res)) {
return $res;
}
// so reset to zero
return $this->query("UPDATE ${seqname} SET id = 0;");
}
 
// }}}
// {{{ dropSequence()
 
/**
* Deletes a sequence
*
* @param string $seq_name name of the sequence to be deleted
*
* @return int DB_OK on success. DB_Error if problems.
*
* @internal
* @see DB_common::dropSequence()
* @access public
*/
function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
}
 
// }}}
// {{{ _BCsequence()
 
/**
* Backwards compatibility with old sequence emulation implementation
* (clean up the dupes)
*
* @param string $seqname The sequence name to clean up
* @return mixed DB_Error or true
*/
function _BCsequence($seqname)
{
// Obtain a user-level lock... this will release any previous
// application locks, but unlike LOCK TABLES, it does not abort
// the current transaction and is much less frequently used.
$result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
if (DB::isError($result)) {
return $result;
}
if ($result == 0) {
// Failed to get the lock, can't do the conversion, bail
// with a DB_ERROR_NOT_LOCKED error
return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
}
 
$highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
if (DB::isError($highest_id)) {
return $highest_id;
}
// This should kill all rows except the highest
// We should probably do something if $highest_id isn't
// numeric, but I'm at a loss as how to handle that...
$result = $this->query("DELETE FROM ${seqname} WHERE id <> $highest_id");
if (DB::isError($result)) {
return $result;
}
 
// If another thread has been waiting for this lock,
// it will go thru the above procedure, but will have no
// real effect
$result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
if (DB::isError($result)) {
return $result;
}
return true;
}
 
// }}}
// {{{ quoteIdentifier()
 
/**
* Quote a string so it can be safely used as a table or column name
*
* Quoting style depends on which database driver is being used.
*
* MySQL can't handle the backtick character (<kbd>`</kbd>) in
* table or column names.
*
* @param string $str identifier name to be quoted
*
* @return string quoted identifier string
*
* @since 1.6.0
* @access public
* @internal
*/
function quoteIdentifier($str)
{
return '`' . $str . '`';
}
 
// }}}
// {{{ quote()
 
/**
* @deprecated Deprecated in release 1.6.0
* @internal
*/
function quote($str) {
return $this->quoteSmart($str);
}
 
// }}}
// {{{ escapeSimple()
 
/**
* Escape a string according to the current DBMS's standards
*
* @param string $str the string to be escaped
*
* @return string the escaped string
*
* @internal
*/
function escapeSimple($str) {
if (function_exists('mysql_real_escape_string')) {
return @mysql_real_escape_string($str, $this->connection);
} else {
return @mysql_escape_string($str);
}
}
 
// }}}
// {{{ modifyQuery()
 
function modifyQuery($query)
{
if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
// "DELETE FROM table" gives 0 affected rows in MySQL.
// This little hack lets you know how many rows were deleted.
if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
$query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
'DELETE FROM \1 WHERE 1=1', $query);
}
}
return $query;
}
 
// }}}
// {{{ modifyLimitQuery()
 
function modifyLimitQuery($query, $from, $count, $params = array())
{
if (DB::isManip($query)) {
return $query . " LIMIT $count";
} else {
return $query . " LIMIT $from, $count";
}
}
 
// }}}
// {{{ mysqlRaiseError()
 
/**
* Gather information about an error, then use that info to create a
* DB error object and finally return that object.
*
* @param integer $errno PEAR error number (usually a DB constant) if
* manually raising an error
* @return object DB error object
* @see DB_common::errorCode()
* @see DB_common::raiseError()
*/
function mysqlRaiseError($errno = null)
{
if ($errno === null) {
if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
$this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
$this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
$this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
} else {
// Doing this in case mode changes during runtime.
$this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
$this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
$this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
}
$errno = $this->errorCode(mysql_errno($this->connection));
}
return $this->raiseError($errno, null, null, null,
@mysql_errno($this->connection) . ' ** ' .
@mysql_error($this->connection));
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table or a result set.
*
* @param object|string $result DB_result object from a query or a
* string containing the name of a table
* @param int $mode a valid tableInfo mode
* @return array an associative array with the information requested
* or an error object if something is wrong
* @access public
* @internal
* @see DB_common::tableInfo()
*/
function tableInfo($result, $mode = null) {
if (isset($result->result)) {
/*
* Probably received a result object.
* Extract the result resource identifier.
*/
$id = $result->result;
$got_string = false;
} elseif (is_string($result)) {
/*
* Probably received a table name.
* Create a result resource identifier.
*/
$id = @mysql_list_fields($this->dsn['database'],
$result, $this->connection);
$got_string = true;
} else {
/*
* Probably received a result resource identifier.
* Copy it.
* Deprecated. Here for compatibility only.
*/
$id = $result;
$got_string = false;
}
 
if (!is_resource($id)) {
return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA);
}
 
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$case_func = 'strtolower';
} else {
$case_func = 'strval';
}
 
$count = @mysql_num_fields($id);
 
// made this IF due to performance (one if is faster than $count if's)
if (!$mode) {
for ($i=0; $i<$count; $i++) {
$res[$i]['table'] = $case_func(@mysql_field_table($id, $i));
$res[$i]['name'] = $case_func(@mysql_field_name($id, $i));
$res[$i]['type'] = @mysql_field_type($id, $i);
$res[$i]['len'] = @mysql_field_len($id, $i);
$res[$i]['flags'] = @mysql_field_flags($id, $i);
}
} else { // full
$res['num_fields']= $count;
 
for ($i=0; $i<$count; $i++) {
$res[$i]['table'] = $case_func(@mysql_field_table($id, $i));
$res[$i]['name'] = $case_func(@mysql_field_name($id, $i));
$res[$i]['type'] = @mysql_field_type($id, $i);
$res[$i]['len'] = @mysql_field_len($id, $i);
$res[$i]['flags'] = @mysql_field_flags($id, $i);
 
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
}
 
// free the result only if we were called on a table
if ($got_string) {
@mysql_free_result($id);
}
return $res;
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Returns the query needed to get some backend info
* @param string $type What kind of info you want to retrieve
* @return string The SQL query string
*/
function getSpecialQuery($type)
{
switch ($type) {
case 'tables':
return 'SHOW TABLES';
case 'views':
return DB_ERROR_NOT_CAPABLE;
case 'users':
$sql = 'select distinct User from user';
if ($this->dsn['database'] != 'mysql') {
$dsn = $this->dsn;
$dsn['database'] = 'mysql';
if (DB::isError($db = DB::connect($dsn))) {
return $db;
}
$sql = $db->getCol($sql);
$db->disconnect();
// XXX Fixme the mysql driver should take care of this
if (!@mysql_select_db($this->dsn['database'], $this->connection)) {
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
}
}
return $sql;
case 'databases':
return 'SHOW DATABASES';
default:
return null;
}
}
 
// }}}
 
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/jrest/lib/zip.php
New file
0,0 → 1,190
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
*
* @version $Id: zip.lib.php 10240 2007-04-01 11:02:46Z cybot_tm $
*/
 
/**
* Zip file creation class.
* Makes zip files.
*
* Based on :
*
* http://www.zend.com/codex.php?id=535&single=1
* By Eric Mueller <eric@themepark.com>
*
* http://www.zend.com/codex.php?id=470&single=1
* by Denis125 <webmaster@atlant.ru>
*
* a patch from Peter Listiak <mlady@users.sourceforge.net> for last modified
* date and time of the compressed file
*
* Official ZIP file format: http://www.pkware.com/appnote.txt
*
* @access public
*/
class zipfile
{
/**
* Array to store compressed data
*
* @var array $datasec
*/
var $datasec = array();
 
/**
* Central directory
*
* @var array $ctrl_dir
*/
var $ctrl_dir = array();
 
/**
* End of central directory record
*
* @var string $eof_ctrl_dir
*/
var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
 
/**
* Last offset position
*
* @var integer $old_offset
*/
var $old_offset = 0;
 
 
/**
* Converts an Unix timestamp to a four byte DOS date and time format (date
* in high two bytes, time in low two bytes allowing magnitude comparison).
*
* @param integer the current Unix timestamp
*
* @return integer the current date in a four byte DOS format
*
* @access private
*/
function unix2DosTime($unixtime = 0) {
$timearray = ($unixtime == 0) ? getdate() : getdate($unixtime);
 
if ($timearray['year'] < 1980) {
$timearray['year'] = 1980;
$timearray['mon'] = 1;
$timearray['mday'] = 1;
$timearray['hours'] = 0;
$timearray['minutes'] = 0;
$timearray['seconds'] = 0;
} // end if
 
return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) |
($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
} // end of the 'unix2DosTime()' method
 
 
/**
* Adds "file" to archive
*
* @param string file contents
* @param string name of the file in the archive (may contains the path)
* @param integer the current timestamp
*
* @access public
*/
function addFile($data, $name, $time = 0)
{
$name = str_replace('\\', '/', $name);
 
$dtime = dechex($this->unix2DosTime($time));
$hexdtime = '\x' . $dtime[6] . $dtime[7]
. '\x' . $dtime[4] . $dtime[5]
. '\x' . $dtime[2] . $dtime[3]
. '\x' . $dtime[0] . $dtime[1];
eval('$hexdtime = "' . $hexdtime . '";');
 
$fr = "\x50\x4b\x03\x04";
$fr .= "\x14\x00"; // ver needed to extract
$fr .= "\x00\x00"; // gen purpose bit flag
$fr .= "\x08\x00"; // compression method
$fr .= $hexdtime; // last mod time and date
 
// "local file header" segment
$unc_len = strlen($data);
$crc = crc32($data);
$zdata = gzcompress($data);
$zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug
$c_len = strlen($zdata);
$fr .= pack('V', $crc); // crc32
$fr .= pack('V', $c_len); // compressed filesize
$fr .= pack('V', $unc_len); // uncompressed filesize
$fr .= pack('v', strlen($name)); // length of filename
$fr .= pack('v', 0); // extra field length
$fr .= $name;
 
// "file data" segment
$fr .= $zdata;
 
// "data descriptor" segment (optional but necessary if archive is not
// served as file)
// nijel(2004-10-19): this seems not to be needed at all and causes
// problems in some cases (bug #1037737)
//$fr .= pack('V', $crc); // crc32
//$fr .= pack('V', $c_len); // compressed filesize
//$fr .= pack('V', $unc_len); // uncompressed filesize
 
// add this entry to array
$this -> datasec[] = $fr;
 
// now add to central directory record
$cdrec = "\x50\x4b\x01\x02";
$cdrec .= "\x00\x00"; // version made by
$cdrec .= "\x14\x00"; // version needed to extract
$cdrec .= "\x00\x00"; // gen purpose bit flag
$cdrec .= "\x08\x00"; // compression method
$cdrec .= $hexdtime; // last mod time & date
$cdrec .= pack('V', $crc); // crc32
$cdrec .= pack('V', $c_len); // compressed filesize
$cdrec .= pack('V', $unc_len); // uncompressed filesize
$cdrec .= pack('v', strlen($name)); // length of filename
$cdrec .= pack('v', 0); // extra field length
$cdrec .= pack('v', 0); // file comment length
$cdrec .= pack('v', 0); // disk number start
$cdrec .= pack('v', 0); // internal file attributes
$cdrec .= pack('V', 32); // external file attributes - 'archive' bit set
 
$cdrec .= pack('V', $this -> old_offset); // relative offset of local header
$this -> old_offset += strlen($fr);
 
$cdrec .= $name;
 
// optional extra field, file comment goes here
// save to central directory
$this -> ctrl_dir[] = $cdrec;
} // end of the 'addFile()' method
 
 
/**
* Dumps out file
*
* @return string the zipped file
*
* @access public
*/
function file()
{
$data = implode('', $this -> datasec);
$ctrldir = implode('', $this -> ctrl_dir);
 
return
$data .
$ctrldir .
$this -> eof_ctrl_dir .
pack('v', sizeof($this -> ctrl_dir)) . // total # of entries "on this disk"
pack('v', sizeof($this -> ctrl_dir)) . // total # of entries overall
pack('V', strlen($ctrldir)) . // size of central dir
pack('V', strlen($data)) . // offset to start of central dir
"\x00\x00"; // .zip file comment length
} // end of the 'file()' method
 
} // end of the 'zipfile' class
?>