--- /dev/null
+<?php /* vim: set binary expandtab tabstop=4 shiftwidth=4 textwidth=80:
+-------------------------------------------------------------------------------
+ Ministerio de EconomÃa
+ meconlib
+-------------------------------------------------------------------------------
+This file is part of meconlib.
+
+meconlib is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your option)
+any later version.
+
+meconlib 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License; if not,
+write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+Boston, MA 02111-1307 USA
+-------------------------------------------------------------------------------
+Creado: Tue Nov 26 12:45:31 2003
+Autor: Manuel Nazar Anchorena <manazar@mecon.gov.ar>
+-------------------------------------------------------------------------------
+$Id: Graph.php 428 2003-11-18 14:30:30Z mmarre $
+-----------------------------------------------------------------------------*/
+
+require_once 'MECON/Graph/external/jpgraph/src/jpgraph.php';
+
+/**
+ * Liberia base para el manejo de graficos.
+ */
+class MECON_Graph {
+
+
+ /**
+ * Tipo del grafico (xy, torta, gantt)
+ * @var int $ancho
+ * @access private
+ */
+ var $_tipo;
+
+
+ /**
+ * Ancho del grafico.
+ * @var int $ancho
+ * @access private
+ */
+ var $_ancho;
+
+ /**
+ * Alto del grafico.
+ * @var int $alto
+ * @access private
+ */
+ var $_alto;
+
+ /**
+ * El grafico propiamente dicho.
+ * @var Graph $grafico
+ * @access private
+ */
+ var $_grafico;
+
+ /**
+ * Booleano que define si se muestran o no los valores
+ * @var bool $verValores
+ * @access private
+ */
+ var $_verValores;
+
+ /**
+ * Class constructor.
+ *
+ * @param string $tipo Tipo de grafico(xy,torta,torta3D,gantt)
+ * @param int $ancho Ancho del grafico
+ * @param int $alto Alto del grafico
+ * @param string $titulo Titulo del grafico.
+ * @param array $attrib_gral Contiene opciones generales para el grafico.
+ *
+ * @return void
+ * @access public
+ */
+ function MECON_graph($tipo, $ancho=300, $alto=200,$titulo,$attrib_gral=NULL)
+ {
+ $this->_tipo=$tipo;
+ $this->_ancho= $ancho;
+ $this->_alto= $alto;
+
+ if (isset($attrib_gral['verValores']))
+ $this->_verValores= $attrib_gral['verValores'];
+
+ if($this->_tipo=="xy")
+ {
+ $this->_grafico= new Graph($ancho,$alto,"auto");
+ $this->_grafico->SetScale("textlin");
+
+ if (isset($attrib_gral['Xtitulo']))
+ $this->_grafico->xaxis->title->Set($attrib_gral['Xtitulo']);
+
+ if (isset($attrib_gral['Ytitulo']))
+ $this->_grafico->yaxis->title->Set($attrib_gral['Ytitulo']);
+
+ if (isset($attrib_gral['XEtiquetas']))
+ $this->_grafico->xaxis->SetTickLabels($attrib_gral['XEtiquetas']);
+ }
+
+ if(($this->_tipo=="torta")||($this->_tipo=="torta3D"))
+ {
+ require_once 'MECON/Graph/external/jpgraph/src/jpgraph_pie.php';
+ require_once 'MECON/Graph/external/jpgraph/src/jpgraph_pie3d.php';
+
+ $this->_grafico= new PieGraph($ancho,$alto);
+
+ }
+
+ //Atributos en comun
+
+ if (isset($titulo))
+ $this->_grafico->title-> Set($titulo);
+ if (isset($attrib_gral['subTitulo']))
+ $this->_grafico->subtitle->Set($attrib_gral['subTitulo']);
+ if ($attrib_gral['verSombra']==true)
+ {
+ $this->_grafico->SetShadow();
+ }
+ }
+
+
+ /**
+ * Agrega Secuencia de datos.
+ *
+ * @param array $tipo Tipo de grafico para la secuencia
+ * @param array $secuencia Datos del arreglo
+ * @param array $atributos Atributos especiales para la secuencia
+ *
+ * @return void
+ * @access public
+ */
+ function agregarSecuencia($tipo,$secuencia,$atributos=NULL)
+ {
+ if($this->_tipo=="xy")
+ {
+ $valido=false;
+
+ if ($tipo=="lineas")
+ {
+ $valido=true;
+ require_once 'MECON/Graph/external/jpgraph/src/jpgraph_line.php';
+ $plot= new LinePlot($secuencia);
+ if ($this->_verValores)
+ $plot->value->Show();
+
+ if (isset($atributos['colorRelleno']))
+ {
+ $plot->SetFillColor($atributos['colorRelleno']);
+ }
+
+ }
+
+
+ if ($tipo=="barras")
+ {
+ $valido=true;
+ require_once 'MECON/Graph/external/jpgraph/src/jpgraph_bar.php';
+ $plot= new BarPlot($secuencia);
+ if ($this->_verValores)
+ $plot->value->Show();
+
+ }
+
+ if ($tipo=="puntos")
+ {
+ $valido=true;
+ require_once 'MECON/Graph/external/jpgraph/src/jpgraph_scatter.php';
+ $plot= new ScatterPlot($secuencia);
+ if ($this->_verValores)
+ $plot->value->Show();
+
+ if (isset($atributos['impulso']))
+ {
+ if ($atributos['impulso']=="si")
+ $plot->SetImpuls();
+ }
+ }
+
+ // Seteo opciones generales
+ if (isset($atributos['color']))
+ {
+ $plot->SetColor($atributos['color']);
+ }
+ if (isset($atributos['leyenda']))
+ {
+ $plot->SetLegend($atributos['leyenda']);
+ //$this->_grafico->legend->SetLayout(LEGEND_HOR);
+ //$this->_grafico->legend->Pos(0.05,0.5,"bottom","center");
+ }
+
+
+ //Si hubo error
+ if ($valido==false)
+ {
+ die ("Error: Tipo de grafico $tipo no valido (aun)");
+ }
+ }//del if tipo xy
+
+
+ if(($this->_tipo=="torta")||($this->_tipo=="torta3D"))
+ {
+ if($this->_tipo=="torta")
+ $plot= new PiePlot($secuencia);
+
+ if($this->_tipo=="torta3D")
+ $plot= new PiePlot3D($secuencia);
+
+ if ($this->_verValores)
+ $plot->value->Show();
+
+
+ if (isset($atributos['leyendas']))
+ {
+ $plot->SetLegends($atributos['leyendas']);
+ }
+
+ if (isset($atributos['centro']))
+ {
+ $x=$atributos['centro'][0];
+ $y=$atributos['centro'][1];
+ $plot->SetCenter($x,$y);
+ }
+
+ }
+
+ $this->_grafico->Add($plot);
+
+ }
+
+
+
+ /**
+ * Genera el grafico y lo dibuja.
+ *
+ * @return void
+ * @access public
+ */
+ function generar()
+ {
+ $this->_grafico->Stroke();
+ }
+
+
+}
--- /dev/null
+THE Q PUBLIC LICENSE version 1.0
+
+Copyright (C) 1999 Trolltech AS, Norway.
+ Everyone is permitted to copy and
+ distribute this license document.
+
+The intent of this license is to establish freedom to share and change
+the software regulated by this license under the open source model.
+
+This license applies to any software containing a notice placed by the
+copyright holder saying that it may be distributed under the terms of
+the Q Public License version 1.0. Such software is herein referred to
+as the Software. This license covers modification and distribution of
+the Software, use of third-party application programs based on the
+Software, and development of free software which uses the Software.
+
+
+Granted Rights
+
+1. You are granted the non-exclusive rights set forth in this license
+ provided you agree to and comply with any and all conditions in
+ this license. Whole or partial distribution of the Software, or
+ software items that link with the Software, in any form signifies
+ acceptance of this license.
+
+
+2. You may copy and distribute the Software in unmodified form
+ provided that the entire package, including - but not restricted to
+ - copyright, trademark notices and disclaimers, as released by the
+ initial developer of the Software, is distributed.
+
+
+3. You may make modifications to the Software and distribute your
+ modifications, in a form that is separate from the Software, such
+ as patches. The following restrictions apply to modifications:
+
+ a. Modifications must not alter or remove any copyright notices in the
+ Software.
+
+ b. When modifications to the Software are released under this license,
+ a non-exclusive royalty-free right is granted to the initial developer
+ of the Software to distribute your modification in future versions of
+ the Software provided such versions remain available under these terms
+ in addition to any other license(s) of the initial developer.
+
+
+4. You may distribute machine-executable forms of the Software or
+ machine-executable forms of modified versions of the Software,
+ provided that you meet these restrictions:
+
+ a. You must include this license document in the distribution.
+
+ b. You must ensure that all recipients of the machine-executable forms
+ are also able to receive the complete machine-readable source code to
+ the distributed Software, including all modifications, without any
+ charge beyond the costs of data transfer, and place prominent notices
+ in the distribution explaining this.
+
+ c. You must ensure that all modifications included in the
+ machine-executable forms are available under the terms of this
+ license.
+
+
+5. You may use the original or modified versions of the Software to
+ compile, link and run application programs legally developed by you
+ or by others.
+
+
+6. You may develop application programs, reusable components and other
+ software items that link with the original or modified versions of
+ the Software. These items, when distributed, are subject to the
+ following requirements:
+
+
+
+ a. You must ensure that all recipients of machine-executable forms of
+ these items are also able to receive and use the complete
+ machine-readable source code to the items without any charge beyond
+ the costs of data transfer.
+
+
+ b. You must explicitly license all recipients of your items to use and
+ re-distribute original and modified versions of the items in both
+ machine-executable and source code forms. The recipients must be able
+ to do so without any charges whatsoever, and they must be able to
+ re-distribute to anyone they choose.
+
+
+ c. If the items are not available to the general public, and the
+ initial developer of the Software requests a copy of the items, then
+ you must supply one.
+
+
+Limitations of Liability
+
+In no event shall the initial developers or copyright holders be
+liable for any damages whatsoever, including - but not restricted to -
+lost revenue or profits or other direct, indirect, special, incidental
+or consequential damages, even if they have been advised of the
+possibility of such damages, except to the extent invariable law, if
+any, provides otherwise.
+
+
+No Warranty
+
+The Software and this license document are provided AS IS with NO
+WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN,
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+
+Choice of Law
+
+This license is governed by the Laws of Norway. Disputes shall be
+settled by Oslo City Court.
+
+
+
+
+
--- /dev/null
+README FOR JPGRAPH
+==================
+
+This is JpGraph 1.13 an Object Oriented PHP4 Graph Plotting library.
+
+The libray is Copyright (C) 2001,2002,2003 Johan Persson and
+released under dual license QPL 1.0 for open source and educational
+use amd JpGraph Professional License for commercial use.
+
+Please see full license details at http://www.aditus.nu/jpgraph/
+
+Included files
+--------------
+README This file
+QPL.txt QPL 1.0 Licensee
+
+/src
+ Changelog Changelog
+ jpgraph.php Base library
+ jpgraph_log.php Extension: logarithmic scales
+ jpgraph_line.php Extension: line plots
+ jpgraph_bar.php Extension: bar plots
+ jpgraph_error.php Extension: error plots
+ jpgraph_scatter.php Extension: scatter/impuls plots
+ jpgraph_radar.php Extension: radar plots
+ jpgraph_pie.php Extension: pie plots
+ jpgraph_canvas.php Extension: drawing canvas
+ jpgraph_canvtools.php Extension: utility classes for working with canvas
+ jpgraph_pie3d.php Extension: 3D pie plots
+ jpgraph_gantt.php Extension: Gantt chart
+ jpgraph_regstat.php Extension: Statistics and cubic splines.
+ jpgraph_stock.php Extension: Stock and box plots.
+ jpgraph_gradient.php Extension: Color gradient class
+ jpgraph_gb2312.php Extension: Chinese GB2312 to Unicode translation
+ jpgraph_plotmark.php Extension: Handle plotmarks in plots
+ jpgraph_polar.php Extension: Polar plot extension
+ jpgraph_flags.php Extension: Country flags
+ imgdata_*.inc Extension: Encoded images for plot marks
+ flags*.dat Image data: Pre-compiled data for country flags.
+
+/src/utils/misc Various _unsupported_ small utilities to do
+ image manipulation, create background images
+ and help create graphical DB schema.
+
+/src/utils/jpdocgen DDDA architecture to produce JpGraph class reference
+
+/src/Examples A directory with around 215 example graphs with source.
+ Run jpgraph_testsuit.php to get a list of all
+ files and you can easily click on a file to
+ see the code and the resulting image.
+
+/docs/index.html Documentation portal
+
+
+Requirements:
+-------------
+Miminum:
+* PHP 4.1 or higher
+* GD 1.8.x or higher
+
+Recommended:
+* PHP 4.3.2
+* GD 2.12 or builtin GD
+
+CAVEAT:
+To get background images working with GD 2.0.1 you MUST enable
+Truecolor images by setting the constant USE_TRUECOLOR to true. If you
+don't fo this the background images will just be a black rectangle.
+The problem with this is that the antialias for Truetype font
+is broken using truecolor images in GD 2.0.1. This means you can't have
+background and TTF fonts in the same image with GD 2.01.
+
+By upgrading to the latest GD (2.08 as of this writing)y
+ou will fix this problem.
+
+Installation
+------------
+0. Make sure your PHP is AT LEAST 4.1 (preferrable 4.3.1)
+ and that you have compiled support for GD library.
+ You must make aboslutely sure that you have GD working.
+ Please run phpinfo() to check if GD library
+ is supported in your installation. If you don't have the GD library
+ please consult the PHP manual under section "Image" for
+ instructions on where to find this library.
+
+1. Unzip and copy the files to a directory of your choice.
+
+2. Check that the default directory paths in jpgraph.php
+ for cache directory and TTF directory suits your installation.
+ Note1: The default directories are different depending on if
+ the library is running on Windows or UNIX.
+ Note2: Apache/PHP must have write permission to your cache
+ directory.
+
+3. Check that all rest of the DEFINE in the top of JpGraph.php
+ is setup to your preference. The default should be fine
+ for most users. (See also Note 5. below)
+
+4. Make sure PHP have write privileges to your cache directory if
+ you plan on using the cache feature.
+
+5. Read (really!) the FAQ on http://www.aditus.nu/jpgraph/jpg_faq.php.
+
+
+Troubleshooting
+---------------
+1. Any error about "parent::" undefined means that you are not using
+ PHP 4.02 or above. You _NEED_ PHP 4.02 or higher.
+ This problem has also been
+ reported to sometimes occur under Windows. This problem has also
+ been reported by people running Zend-cache and is a bug in Zend. A
+ workaround is to move all files into one single file.
+
+2. If you don't get any background images (but rather a solid black
+ box) you are using GD 2.x but have forgotten to enable truecolor
+ support. Correct this by enabling the USE_TRUECOLOR define.
+
+3. If background images does not work make sure the settings of
+ USE_GD2_LIBRARY corresponds to your installation, i.e. If you
+ don't have GD2 then this define must be false!
+
+4. If you are running PHP 4.06 and get an error saying "GD was not
+ built with truetype support" you should know that this is a known
+ problem with GD+PHP 4.06. There are some workarounds (search the
+ net!) but it is really recommended that you instead upgrade to at least
+ PHP 4.1.1 and configure PHP with --with-gd-native-ttf
+ (Please also note that the built in TTF uses point size for fonts
+ whereas Truetype 2 uses pixels.)
+ Please DON't ask me how to resolve the GD Font problem. All mail
+ regarding this will go straight to /dev/null. Upgrade to 4.1.1!
+
+5. If you are running IIS and Win2k and get the error "Can't find
+ font' when trying to use TTF fonts then try to change you paths
+ to UNIX style, i.e. "/usr/local/fonts/ttf/". Remember that the
+ path is absolute and not relative to the htdocs catalogue. Some
+ versions of GD for Windows also need you to set the environment
+ variable GDFONTPATH for GD to find the fonts.
+
+6. If you are using the cache please make sure that you have
+ set the permissions correctly for the cache directory so that
+ Apache/PHP can write to that directory.
+
+7. If you have problem building GD 2.0.1 for PHP you might want
+ to try the following tip from Rasmus L.
+
+ ----< QUOTE >----
+
+ Build GD 2.0.1 with these two lines in your GD2 Makefile
+
+ CFLAGS=-g -DHAVE_LIBPNG -DHAVE_LIBJPEG -DHAVE_LIBFREETYPE
+ LIBS=libgd.a -lpng -lz -ljpeg -freetype -lm
+
+ Don't install the lib anywhere, just leave it in the GD-2.1.1
+ directory.
+
+ Then build PHP using a minimum of:
+
+ --with-gd=/home/<your_dir>/gd-2.0.1
+ --with-freetype-dir=/use
+ --enable-gd-native-ttf
+ --enable-gd-imgstrttf
+ --with-jpeg-dir=/usr
+ --with-png-dir=/usr
+ --with-xpm-dir=/usr/X11R6
+
+ The above assumes you have freetype2 installed along with the
+ libjpeg and libpng libs under /usr
+
+ ----< END QUOTE >----
+
+8. Some windows installations seems to have a problem with a PHP
+ script ending in a newline (This newline seems to be sent to the
+ browser and will cause a Header already sent error).
+ If you have this problem try remove all trailing newlines in the
+ jpgraph* files
+
+
+Bug reports and suggestions
+---------------------------
+Should be sent to (jpgraph aditus nu) [insert at and dot]
+
+Change history:
+------------------------------------------------------------------------
+Date Ver Comment
+------------------------------------------------------------------------
+2003-08-24 1.13BETA2 Very Minor update before official test release
+2003-08-19 1.13BETA Improved Gantt graphs various other changes and bug fixes
+2003-02-02 1.11 Stock charts and some minor oter changes.
+2002-12-16 1.10 Alpha blending, cubic splines and field plots
+2002-10-30 1.9.1 Fixed two stupid mistakes in 1.9
+2002-10-25 1.9 TTF font change. Many small additional improvements
+2002-09-17 1.8 Documentation update.
+2002-09-09 1.8BETA Functional improvements.
+2002-07-05 1.7 Functional improvements. Rotated bar graphs.
+2002-06-17 1.7BETA Functional improvements.
+2002-05-15 1.6.3 Enhancements to Gantt graphs anbd minor bug fixes.
+2002-04-19 1.6.2 Addition of image maps for line and scatter plot
+2002-04-07 1.6.1 Bug fixes for 3D pies and image maps for pie's
+2002-04-01 1.6 Functional improvments
+2002-04-01 1.6BETA Beta candidate for 1.6
+2002-03-01 1.5.3 Fix minor release problem in 1.5.2
+2002-02-29 1.5.2 Minor bug fixes.
+2002-02-11 1.5.1 Minor bug fixes.
+2002-01-27 1.5 Functional improvements. Gantt charts.
+2002-01-17 1.5BETA2 Functional improvements, bug fixes
+2001-12-16 1.5BETA Functional improvements, gantt-charts, bug fixes.
+2001-11-12 1.4 Functional improvements, bug fixes.
+2001-09-23 1.3.1 Minor bug fixes
+2001-09-13 1.3 Major functional enhancements and minor bugfixes
+2001-04-29 1.2.2 Minor bug fixes. Addded background image support
+2001-03-29 1.2.1 Minor bug fixes. Experimental support for 3D pie plots
+2001-03-18 1.2 Second release see changes.txt
+2001-02-18 1.1 Second release see changes.txt
+2001-02-04 1.0 First public release
+
+-------------------------------------------------------------------------
+
+Stockholm/London 2003-08-24
+Johan Persson (jpgraph aditus nu) [insert at and dot]
+
+<EOF>
--- /dev/null
+23 Aug 2003 JpGraph 1.13_BETA2
+==============================
+* [me] Added Gradient line fill for line plots. See gradlinefillex1.php
+
+* [me] Fix bug #140. A rounding problem in Gradient fill which
+ in some circumstances could cause a 2 pixels overflow in the filling.
+
+* [me] Added option to let the user setup if the PHP error handler should
+ give an image with the error message. Useful during development.
+ Controlled by the define INSTALL_PHP_ERR_HANDLER
+
+* [me] Added method Legend::SetReverse() to reverse the order of plots in
+ the Legend.
+
+* [me] Since the example directory is now getting to big the example directory
+ is now split into several subdirectories depending on Graph type.
+
+* [me] Added more Gantt chart examples.
+
+* [me] Adjusted size for filled circles (as used for example in markers). By a
+ type they were previously 2 pixels to large copared to what they were
+ specified to be
+
+* [me] Fix for annoyance #147, #150. Fine tuning of legend layout and positioning of
+ markers. It should now look even better with some better calculated margins
+ and general positioning that depends on the actual markers use for the plots.
+
+* [me] Fix (bug?) #134. Don't output any coordinates for bar CSIM if the target is an
+ empty string.
+
+* [me] Fix bug #133. Fill colors for individual bars was not honoured when
+ used together with an accumulated bar.
+
+* [me] Fix bug #136. Labels for 2D pie does not follow start label when
+ anti-alias is enabled.
+
+* [me] Feature request #154. Make it possible to use object methods as
+ callbacks wherever callbacks can be specified in the library
+
+* [me] Fix problem #152. Values for PiePlotC wasn't parsed for
+ printf-formatting. (A bug that was introduced with AntiAlising in 1.12)
+
+* [me] Fix problem #148. Explode array was specified clockwise while slices
+ are specified anti-clockwise.
+ NOTE: THIS GIVES THE WRONG LOOK ON EXISTING 2D PIE PLOTS WITH EXPLODEDED
+ SLICES SINCE THE WRONG SLICE WIll NOW BE EXPLODED
+
+* [me] Updated class reference
+
+* [me] Make colors have the same order in both 2D and 3D pie plots
+
+* [me] Added possibility to make PiePlot percentage adjustment so that
+ integer percetage will add up to 100%. This is done by the Hare/Niemeyer
+ method. To activate the percetage adjustment you need to specify labeltype
+ of 2, as in $pieplot->SetLabeltype(2),
+
+* [me] Y2 scale can now add lines, texts and band through a single call to
+ Graph::AddY2() in exactly the same way as Graph::Add()
+
+* [me] Make sure there are no direct calls to GD except through the Image class
+
+* [me] Make it possible to rotate polar graphs 90 degrees with Set90AndMargin()
+
+* [me] Major improvements in the Gantt charts.
+ 1. Now support for minute and hour scale as well
+ 2. Suport for multiple columns in the title field, you can now have (for
+ example) Start date, end date, duration etc
+ 3. Improved sizing to make the overall image as small as possible
+ 4. Make it possible to use builtin or file based icons in the title columns
+ 5. Improved 3D look & feel
+
+* [me] Added support for using country flags as image markers. This
+ is added by jpgraph_flags.php together with the image files *.dat
+ At the moment 229 countries are supported. See Example/listallflags.php
+ for a current list of supported countries. All flags are available in 4
+ standard original sizes, as markers they may also be arbitrary scaled.
+ For a simple example see Examples/markflagex1.php
+
+* [ryleyb] Multiple constrains for Gantt graphs
+
+* [me] Added possibility to use the Free Vera TTF font. The font is available
+ from http://www.gnome.org/fonts/
+
+* [me] Make PHP deault error handler show image if possible
+
+* [me] Improved look for image error handler (added icon)
+
+* [me] Fixed problem with gradient backgrounds for horizontal
+ bar graphs.
+
+* [me] Added possibility of 'max' value position for bar graphs. This
+ positions the value just inside tha bar at the max value of the bar.
+
+* [me] Font files are now fully specified in the setup and doesn't assume an
+ extension of ttf.
+
+30 May 2003 JpGraph 1.12.2
+===========================
+* [me & A. Tang] Added support for Chinese font and character handling.
+ This requires you to install the "simhei.ttf" Chinese font.
+
+* [J Sweat] Fix bug [#124] Make step line style ignore NULL values in data
+
+* [J Sweat] Fix bug [#123] Divide by zero for line length of zero for dashed lines
+
+* [me] Fix bug [#109]. Problem with pie color when a supplied color array
+ contains more colors than data. For anti-aliased images that meant that
+ the last n colors was used and not the first n colors.
+
+* [me] For plotmarks that use the ALT string the wrong value was in due
+ to the recent addition of both Y and X value to control the plot callback.
+ This has now been fixed so that it will work as before and that previous
+ code that assumes one parameter in their ALT srting will see the Y-value at
+ the plotmar. New is also that the X-value is given as a second
+ parameter. This means that the ALT string can display both the Y and X value
+ for a specific point.
+
+* [me] Make it possible to disable individual plot legends through the method
+ Plot::HideLegend()
+
+* [me] Fixed problem with SetTextTickInterval() together with grouped bars
+ (bug #116)
+
+* [me] Fixed a problem with shadows on accumulated bar plots (bug #111)
+
+* [me] Fixed Legend::SetMarkAbsSize()
+
+* [me] Add error check for the case that the server (Windows)
+ doesn't support SystemRoot and TEMP server variables.
+
+* [me] Fix short-open-tags bug that made it's way into 1.12.1 (bug #121)
+
+* [me] Make sure we only pass integers into GD when positioning texts.
+
+* [me] Always treat "" (empty string) as NULL for bar plots
+
+* [me] Allow links from vertical position 0 on gantt charts
+
+
+12 Apr 2003 JpGraph 1.12.1
+==========================
+
+* [me] Added Legend::SetLineSpacing() to control the vertical line
+ distcance (in pixels) between legend texts.
+
+* [me] Compensate for bug in imagecopyresampled() in GD 2.01.
+ This made clipping unusable with GD 2.01
+
+* [me] Added LinePlot::SetFillFromYMin() to make the fill start from
+ the bottom of a Y-scale with negative values rather than the 0-value
+ which is now the case.
+
+* [me] Fix a problem with legend box layout in the case of TTF fonts
+ and different lengths of individual texts. The layout engine now
+ work out each column individually. This will also give the legend
+ box the minimum possible size.
+
+* [me] Compensate for possible bug in strtok() when parsing color
+ specification as a string with adjustment factors.
+
+
+06 Apr 2003 JpGraph 1.12
+========================
+* [me] Added possibility to specify the position of the legend box
+ with absolute pixels coordinates not only as fractinos as before.
+
+* [me] Improved legend layout. It is now possible to specify
+ the number of columns in the legend box. The preferred way of
+ specifyin the layout is now Legend::SetColumns(). The oild
+ way with SetLayout() is still available for backwards compatibility
+ reasons. The old vertical layout corresponds to a 1 column
+ layout (SetColumns(1) which is also the default).
+
+* [me] Added possibility to specify text string position
+ using scale coordinates onto the plot by adding
+ Text::SetScalePos(). The autoscaling has been modifyied to
+ be aware of the text scale positions.
+
+* [me] Added builtin images for plotmarks. This also meant a
+ refactoring exercise for the PlotMark class and it has now been
+ split in sevaral files.
+
+* [me] Added footer capability to Radar, Pie and Gantt charts
+
+* [me] Improved upon rounding problem with GD2 and pie slices
+ (defect #88)
+
+* [me] Take shadow color as specified with Graph::SetShadow() into account.
+
+* [me] Don't rotate the footer if the graph is rotated.
+
+* [me] Added Axis::HideLabels()
+
+* [me] Adjusted of the automatic margin calculation so that it is
+ aware of when the x-axis have been hidden.
+
+* [me] Added PlotMark::SetCallbackYX(), This allows for the
+ specification of a callback function which is given both X and Y
+ coordinates. Also extended the returned values from the callback so
+ that image in a marker can be dynamically modified both size and
+ actual image file used. This will enable the use of advanced
+ "push-pin" graphs. For example adding markers to a background image
+ map of certain size and type.
+
+ For backwards compatibility any callback specified with
+ PlotMark::SetCallback() will not be given the extra parameter. So
+ old code does not need to be changed.
+
+* [me] Fixed handling of PHP array URL parameters with CSIM. [Bug #82]
+
+* [me] Added Legend::SetFrameWeight() which adjusts the frame around
+ the legend. Legend::SetLineWeight() now works as documented and sets
+ the line weight for the markers instead.
+
+* [me] Autoscaling will now also take the value of added lines into account.
+
+* [me] Fixed problem with horizontal legend box becomming too small
+ for many legends.
+
+* [me] Radar plot. SetColor() always sets a fill color. Fixed (Bug #83)
+
+* [me] Added SetWidth() for tab-title. The tab can now easily extend
+ over the entire plot width. Added SetPos() as synonym in tab title
+ for SetTabAlign().
+
+* [me] Gradient bar plots now show a gradient mark in the legend
+
+* [me] PiePlot3D::SetSliceColors() now more correctly specifies the
+ color anti-clockwise. The same order that slices are drawn.
+
+* [me] Pie position can now also be specified in absolute pixels
+ in PiePlot::SetCenter()
+
+* [me] Added Bevel frame for entire graph
+
+* [me] Added possibility for some more advanced Graph title
+ formatting through the Graph::SetTitleBackground() method.
+
+* [me] Type safe comparison in auto-detection of GD version
+
+* [me] Added Graph::SetCanvasColor() to specify the background color
+ the canvas is initialized with.
+
+* [me] Fine tuned label positiong for pie labels and made 3D pies
+ take the specified labels into account.
+
+* [me] Added Anti-aliasing to Pie charts. Looks good but it really
+ slows down the pie generation. Enable by acalling
+ PieGraph::SetAntiAliasing()
+
+* [me] Ignore NULL values in Stock and bar plots
+
+* [me] Some minor internal adjustments for rounding in log scales.
+
+
+2 Feb 2003 JpGraph 1.11
+=======================
+* [Martijn] Compensate for GD 2.x bug in filledarc() which doesn't
+ like negative angles.
+
+* [me] Added Graph::SetBackgroundGradient() . This allows
+ a gradient to be used as a background. The gradient can either fill
+ the whole frame, only the margin or just the plot area. See the
+ class reference or the manual for details regarding this method.
+
+* [me] Modified adjustment factor for colors so that it now
+ is linear between dark and light, i.e. a factor of 1 is the original
+ color, a factor of 0 is black and a factor of 2 is white.
+
+* [me] Added two new gradient style GRAD_LEFT_REFLECTION and
+ GRAD_RIGHT_REFLECTION
+
+* [me] Some fine tuning of text layout in legend box
+
+* [me] Radar plots can now have a background image and multiple
+ titles (sub-title, and subsub title).
+ Added RadarGraph::SetPos() as alias for
+ RadarGraph::SetCenter().
+
+* [me] Fixed a small pre-read-loop error which could casue the
+ X-gridline to be outside the right edge of the plotarea if the
+ X-scale was indented.
+
+* [me] Added possibility to use (scaled) images as markers by
+ introductin the new mark type MARK_IMG.
+
+* [me] Added tab titles for graphs. See class reference and Example
+ tabtitleex1.php.
+
+* [me] Added filled grids by adding method Grid::SetFill(). This new
+ feature is illustrated in example filledgridex1.php. Documented in
+ section 6.2.11 in the manual.
+
+* [me] Improved automatic margins so they adapt to one or more title
+ lines automatically.
+
+* [me] Fixed a class hierachy bug. RotImage::DashedLine() should not be
+ implemented but rather rely on the Image::DashedLine() since the
+ rotation will happen in the RotImage::Line() which gets called in
+ Image::DashedLine() by any istance of RotImage().
+ You could see the ffect of this by trying to create a 90 degrees
+ rotated graph with dashed gridlines, they would appear not to be
+ rotated at all where in fact they were rotated twice.
+
+* [me] Added automatic default margins when using Set90AndMargin()
+
+* [me] Added Box plots which is a variant of Stock charts with the
+ possibility to add the median as a horizontal line in the box.
+
+* [me] Added Stock charts, (aka candle-bars) as new class StockPlot().
+ added example stockplotex1.php in the Example directory.
+
+* [me] Added two new axis styles AXSTYLE_YBOXOUT, AXSTYLE_BOXIN which
+ suplicates the Y-axis on both sides but not the X-axis.
+
+* [me] Set90AndMargin() now also adjusts the label alignment on the
+ axis to suit the rotation.
+
+* [me] Added class LineErrorPlot which alolows a line plot to
+ have error bars (+/- delta error).
+
+* [me] Added Ticks::SetSize()
+
+* [me] Modified size for plot mark circle so it will have the
+ specified width as radius and not as diameter.
+
+* [me] Added Ticks::SetSize() to be able to set the absolute size
+ in pixels of the tick marks.
+
+* [me] Default directories for Fonts and Cache now gets set
+ automatically.
+
+
+13-Jan 2003 JpGraph 1.10.1
+==========================
+
+* [me] Fix bug in Band pattern Left diagonal and Right Diagonal where
+ the drawing was incorrect when width < height
+
+* [me] Take the border on/off switch into account for line AddArea()
+
+* [me] Added color parameter to Axis::SetTickLabels() to make it
+ possible to set individual colors on labels.
+
+* [me] Fixed PiePlot::SetSliceColor() so that colors are now specified
+ in the same order as slices.
+
+* [me] Gantt, Radar and Canvas graph now also considers the special file
+ _IMG_HANDLER (returns the actual image handler and doesn't stream
+ picture back)
+
+* [Christian Reiser] Pie graphs can now also return the IMG_HANDLER
+
+* [Richard Black] Removed case sensitivity for extension of
+ background images. Added support for handling file names with
+ multiple '.' characters in the path for background images.
+
+* [me] Added error check for imagettfbbox() since it can give
+ a file not found error even though both file_exists() and
+ is_readable() reports that the file exists and is readable
+ in the case when PHP basedir restriction is in place.
+
+* [me] Corrected CSIM handling in Gantt for non-file based
+ Gantt charts.
+
+* [me] Note to self: 1.10p
+
+* [me] Gracefully handle the abnormal case where no valid data points
+ is included in the Y-data array when creating a line plot.
+
+16-Dec 2002 JpGraph 1.10
+========================
+* [me] Added GanttGraph::CreateSimple() whihc is a utility method to
+ help create simple gantt charts with a minimum of code.
+
+* [me] Added new plot (FieldPlot) type which handles field plots. This is
+ basically a variant of scatter plots where each point is represented
+ as an arrow which a specific direction (0-360 degrees).
+
+* [me] Added Graph::SetY2OrderBack() to control of plots on Y2 should
+ be under (in z-order) te plots on Y. By default they are on top.
+
+* [me] Avoided a potential 1-pixel off rounding problem if a user
+ supplies a non integer canvas width and height in the Graph() call.
+
+* [me] Added two new formats for week scale in Gantt
+ charts. WEEKSTYLE_FIRSTDAYWNBR, WEEKSTYLE_FIRSTDAY2WNBR These
+ formats adds the weeknumber as well to displaying the date of the
+ start of the week. Also tweaked the auto-sizing of the Gantt charts
+ to adjust for the size of the text in the week scale.
+
+* [me] Fixed wrong define in jpgraph_gantt.php for WEEKSTYLE_DAY2
+ (Bug #45)
+
+* [me] Improvements on manual scaling. If a manual scale is specifed
+ (in the Graph::SetScale()) you are now longer required to specify a
+ major and minior tick interval (but of course you can still do it
+ with a call to Ticks::Set() ).
+ A suitable intervall will be automatically
+ caclulated in the same way as for autoscaling. By default the exact
+ limits for the scale as specified will be used. This means that
+ depending on the scale and tick intervall chosen the max value of
+ the scale may or may not fall on a major tick mark. If you want that
+ behaviour you need to call Scale::SetAutoTicks(). By using this call
+ you give the algorithm permission to slightly adjust the specified
+ min and max values (if needed) so that they wil fall on a even
+ major step.
+
+* [me] Added error check if user tries to use SetTextLabelStart()
+ with a logarithmic scale (where it is undefined).
+
+* [me] Default marker for scatter plots are now a filled square
+
+* [me] Adjusted the x-axis labelling for a 1-pixel off limit. Which
+ sometimes (depending on the image size and scale) could cause a
+ linear scale's last label not to be shown.
+
+* [me] Fixed bug report #40
+
+* [me] Fixed positioning of caption for full odometers. (Bug #34)
+
+* [me] Fixed spelling mistake in jpgraph_odo.php which casued
+ Odometer::SetLabelPos() to have no affect. (Bug #33)
+
+* [me] Fixed an overzeoulous optimization which skipped both rotation
+ and translation for angle=0. Addresses (Bug #36)
+
+* [me] Colors can now include an alpha channel specification as well.
+ The alpha channel is specified in the text string separated by a
+ a '@' , for example "red@0.5". The alphachannel is specified as a
+ fraction between 0.0 and 1.0.
+
+* [me] Improved filled line plots so they now handle negative values
+ in a more standard way.
+
+* [me] Added automatic detection of what version of GD is installed.
+
+* [me] Aligned the order of legends and slices for pie and 3D pies so
+ that they now follow the more common order of clockwise slices with
+ legends from top to bottom.
+
+* [me] Changed call to deprecated function SetLinePos() to SetLineSide()
+
+* [me] Added clipping to generated plots. This means that if you have
+ plots (for example lines) which extends outside the scale range it
+ will be clipped. This might come in handy in for example balloon
+ plots. Clipping is enabled by default but can be controlled by a
+ call to Graph::SetClipping()
+
+* [me] Fixed a problem with circles and filled circles when they were
+ rotated twice in a rotated graph. (Bugtraq #26)
+
+* [me] Added Graph::Set90AndMargin() which helps for doing a 90
+ degrees rotation and getting the margins right. (Otherwise you have
+ to adjust them manually since height becomes width and width becomes
+ height when you rotate 90 degrees)
+ Changed the horizbar* examples to use the Set90AndMargin()
+
+* [me] Changed the behaviour of plot marks in legends. The possible
+ format callback is no longer called. Instead the plotmarks are
+ set to a default size.
+
+* [me] Added three more plotmarks MARK_LEFTTRIANGLE,
+ MARK_RIGHTTRIANGLE, MARK_FLASH. These new types are primarily meant
+ to be used with GanttCharts.
+
+* [me] Added constrain lines to GanttCharts. It is now possible to
+ illustrate start-end, start-start, end-start and end-end
+ constrains. This is illustrated by adding lines with arrows,
+ Use GanttPlotObject::SetConstrain()
+
+* [me] Added Image map support for Gantt chart. Bar and titles can now
+ have associated image map targets. Use GanttBar::SetCSIMTarget()
+ GanttBar::SetCSIMAlt() and for titles use
+ GanttBar::title::SetCSIMTarget() and GanttBar::title::SetCSIMAlt()
+
+* [me] Fix problem in Gantt graph whereby the progress bar indicator
+ could extend over the end date if an explicit date scale was set.
+
+* [me] By default logarithmic scale now print the labels as 10^3
+ instead of 1000. You can change the behaviour with the new
+ method for logarithmic ticks, SetLabelLogType().
+ Also added the possibility to have a callback for log scales
+
+* NOTE TO SELF 1.9p
+
+* [me] Added the possibility to specify a value > 1 as the position
+ for labels in PieGraph::SetLabel() to increase the margin between
+ the label and the edge of the pie.
+
+* [Olaf Kandel] Fixed subtle bug in the printing of the month scale for the
+ case where the start and end month hjave the same number but
+ are on different year. This would cause the width for the
+ first month to be wrong.
+
+
+Jpgraph 1.9.1
+=============
+* [me] Fixed defects #20 and #21 (spelling mistake and and one
+overzelous last minute error check I added). I have to eat a lot
+of humble pie over these ones. Sorry to all who have been affected.
+What can I say? Human error........
+
+JpGraph 1.9
+===========
+* [me] Changed the way TTF fonts are handled. The font files are no
+ longer distributed with JpGraph due to potential copyright problem.
+ This change also has the advantage that it is now very
+ straigthforward to install new fonts whatever their naming
+ conventions are.
+
+ 1. If you are running on a standard Windows system just point the
+ TTF directory to your standard font directory and use the available
+ fonts in windows.
+
+ 2. If you are on a Unix system you can download the MS Core TTF
+ files from http://corefonts.sourceforge.net/. The old MS EULA allows
+ distribution of the fonts in unaltered form (Win EXE) and this
+ project exercises this right and provide tools to download and
+ install the fonts on a UNIX system.
+
+* [me] Renamed this release from 1.8.1 to 1.9 due to the big
+ change in the handling of TTF fonts which breaks backward
+ compatibility due to the copyright problem with TTF fonts.
+
+* [me] Improved the TTF pargagraph layout engine for consistency
+ in handling "\n\r" vs "\n". It is now always sufficient to
+ use just "\n".
+
+* [me] Adjusted the text positioning for StrokeBoxedText()
+ to use the new GetBBoxHeigh() and GetBBoxWidth().
+
+* [me] Added Image::GetBBoxHeight(), Image::getBBoxWidth()
+
+* [me] Once and for all fixed the handling of rotated TTF text by
+ adding Image::GetBBoxTTF() which returns the bounding rectangle
+ (which is not the same as the bounding box except for angle==0)
+
+* [me] Added argument to Legend::SetColor() to make it possible
+ to specify both frame and font color.
+
+* [me] Cosmetic adjustment of markers in legend so that they are
+ positioned exactly in the middle of the text string.
+
+* [me] Corrected the positioning of TTF fonts when defining the
+ hotspot as 'bottom'. It could be up to 4 pixel off when using
+ texts that contained character that extended below the TTF
+ baseline e.g. '('.
+
+* [me] Added possibility to use a cache system with CSIM images
+ as well. The cache system is used slightly, slightly different
+ and require a call to the new method Graph::CheckCSIMCache()
+ This is unfortunately unavoidable due to the nature of CSIM in
+ HTML. The documentation is updated to deal with this. This also
+ added two more defines CSIMCACHE_DIR and CSIMCACHE_HTTP_DIR
+
+* [me] Added CSIM (Client Side Image Maps) to legends, added
+ target and alt arguments to all Plot::SetLegend()
+
+* [me] Added functionlity to pass on any URL arguments when using
+ StrokeCSIM() (Bug: #006)
+
+* [me] Fixed so that Gantt charts can again have just the width
+ specified and have the height automtically sized. (This was a
+ regression introduced in 1.8) (Bug #005)
+
+* [me] Added error check for return value from imaagecolorsforindex()
+ in Anti-aliased lines. In combination with GD2, no-true-color, a
+ true color background (ala JPG) this can fail since it gets a color
+ which doesn't exist (since it uses imagecolorat()).
+
+* [me] Improved edges support in 3D Pies.
+
+* [me] Changed Image::Polygon() so i now draws an open polygon and not
+ a closed one.
+
+* [me] Legends for scatter plots no displays the mark used for
+ each scatter plot and not just a square with the correct
+ color.
+
+* [me] Fixed a small cosmetic problem in that horizontal legend
+ box was sometimes not wide enough and made a small adjustment
+ of the positioning of marks in the legend box for better
+ apperance. (Bug #0017)
+
+* [me] Improved manual by adding linked TOC.
+
+* [me] Added Bezier curve to canvas Shape class. Added Bezier canvas
+ example canvasbezierex1.php
+
+* [me] Added Paragraph Alignment setting in Text::Align() as well to
+ avoid seprate call to Text::SetParagraphAlign()
+
+* [me] Added Image::SetExpired() . By default the image now sends
+ back a couple of variants of "no-cache" headers to force the browser
+ to reload.
+
+* [me] Some serious speed improvments for 3D Pie plots. It can now be
+ up to 6000% faster (yes that's right 6000%!). This is the way I had
+ planned on doing it but for time reasons were pushed out of 1.8.
+ I have completely changed the algorithm that handles the individual
+ slices on the expense of a slightly more complicated implementation.
+ This means that there is no performance difference between 2D and
+ 3D Pie Plots.
+
+* [me] Fix bug for rotation of paragraphs (multi-line) texts.
+ A limitation for now is that rotated pargraphs always gets
+ paragraphs align "left". (Bug: #008)
+
+* NOTE TO SELF: 1.8p
+
+* [me] Fixed a typo that had the strange affect that paragraph strings
+ which started with a number would have larger distance between the
+ individual lines since that number would be interpretated as the
+ angle for the text!
+
+* [me] Fixed a problem with marks getting out of sync if NULL values
+ are present in the line plot. (Bug #0018)
+
+* [me] Scatterplot no skips NULL values instead of treating them
+ as zero.
+
+* [me] Added LinearScale::SetAutoMax() as complement to SetAutoMin()
+
+* [me] Added LinearScale::SetAutoTicks() this makes it possible to
+ manually set a scale but still have the tick marks automatically
+ determined. By default this feature is off to keep old code
+ behave exactly the same as before. If this is enabled any
+ Ticks::Set() calls are ignored.
+
+* [me] Added possibility to adjust the position of the displayed
+ value for bars via the BarPlot::SetValuePos() method. Added new
+ example example20.5 to demonstrate this.
+
+* [me] Bugfix: Fixed color themes for 3D pie plot. The color array
+ was not wrapped around in the case where more slices (data points)
+ than colors is used. (Bug: #001)
+
+* [me] Documentation is now 100% pure HTML and does not require
+ any PHP. Previously a PHP script was used to generate the
+ highlighted source used in the image/source frames. All this
+ is now pre-generated in the docs.
+
+* [me] Position the values in a scatter plot in the center of the
+ plot mark by default (Setting align to 'center','center' and margin=0
+
+* [me] Added LinePlot::SetBarCenter to make it possible to center the
+ line on the bars when combining a plot with both bars and lines.
+ The new example 'ceneterdlinebarex1.php' demonstrates this.
+
+
+
+[19-Sep-2002] JpGraph 1.8
+=========================
+* [me] Added Text::SetPos() as alias for Text::Pos() to harmonize with
+ other setting methods.
+
+* [me] Made Graph::Add() polymorphic. You can now use Graph::Add() to
+ add all plot objects and you don't longer have to use AddText(),
+ AddLine(), AddBand() and so on.
+
+* [me] Added PiePlot::SetLabelPos(), added some more Pie examples.
+
+* [me] Improved the API for class Text by adding SetShadow() to make
+ it more like the other objects. Extended Text::SetBox() with shadow
+ width and corner radius.
+
+* [me] Added the possibility for Text object to use the new rounded
+ rectangles as the filled background.
+
+* [me] Added possibility to add arbitrary labels inside the pie plot
+
+* [me] Added special filename __IMG_HANDLER to make it possible to
+ have Graph::Stroke() return the image handler instead of stroking
+ the image to file (or stream it back).
+
+* [me] Improve FilledCircle() to avoid moire-patterns in circle.
+
+* [me] Added middle type pie plot
+
+* [me] User specified values for the band position is truncated to the
+ min and max values of the axis.
+
+* [me] Added possibility to use Text::SetMargin() for all the titles
+ in a graph
+
+* [me] Added SetSize() as synonym for SetWidth() for plot marks.
+
+* [me] Updated some of the examples with more comments.
+
+* [me] Fixed problem with with jpg background images wrongly being
+ loaded with imagecreatefromjpg instad of imagecreatefromjpeg
+
+* [me] Corrected the positioning of individual titles for 3D
+ plots.
+
+* [me] Added Text::SetMargin() to make it easy to have context
+ sensitive margins.
+
+* [me] Added capability to have background images on Pie and 3D
+ Pie plots
+
+* [me] Added method Axis::HideLine() which hides the axis line but
+ nothing else.
+
+* [me] Fixed bug in Stroke::Text() where negative coordinates
+ wasn't properly handled. We must allow negative absolute
+ coordinates since these might be rotated/translated into
+ "real" screen coordinates. This fixes the bug, for example
+ in bar_csimex1.php where the first value of the bar wasn't
+ displayed.
+
+* [me] More adjustment of how the rounding takes place with
+ coordinates. This should now avoid all potential difference
+ between different graphic objects.
+
+* [me] When you use automatic labelling on text scale it now starts
+ at 1 and not at 0. This makes more sense since most people (except
+ mathematicians) start counting from 1.
+
+* [me] Example directory:
+ - Added more advanced canvas examples
+ - Updated all CSIM examples
+ - ~60 new example script which are used in the new documentation
+
+* [me] Compensate for bug in imagettfbox() which return to small size for
+ large fonts.
+
+* [me] Added jpgraph_canvtools.php which contain some utility classes for
+ use with canvas graphs.
+
+* [me] DOCUMENTATION: The class reference is now included in the release.
+
+* [me] DOCUMENTATION: Completely rewritten the original
+ Word-introduction to JpGraph to pure HTML. This will be a living
+ document that will be updated as time moves on. This is currently
+ considered "Beta" and more chapters need to be added but this is
+ a good start!!
+
+* [me] Added full support for multiline legends. (See for example
+ nullvalueex01.php. Also improved the way lineplot with marks
+ are illustrated in the legend by adding the line underneth the
+ plotmark.
+
+* [me] Added some utility classes to be able to draw DB schema for
+ DDDA. This is tailored for my DB abstraction layer (jpdb.php)
+ in DDDA but is generic enough to work in other circumstances as well.
+ The classes can be found in utils/misc/imgdbschema.inc
+ Please see (run) dbschema_ddda.php which shows how it looks
+ in a real life example.
+
+* [me] Tweaked the positioning of TTF fonts and how the width and
+ height are determined. Making more carefull assumptions about
+ how the baseline is handled for individual lines of text.
+
+* [me] Added class RectangleText() which makes it easier to work
+ with text inside a rectangle with rounded corner.
+
+* [me] Added support for multi-line TTF paragraphs with paragraph
+ formatting. This was previously only supported for the builtin
+ fonts. All text object should now support fully formatted paragraphs
+ of text.
+
+* [me] Added Image::RoundedRectangle() and
+ Image::FilledRoundedRectangle()
+
+* [me] Added format callback to plot marks. This makes it
+ possible to individually adjust the plot marks depending
+ on it's specific data value. See balloonex1.php
+
+* [me] Added some initial support for super script used for
+ scientific numbering, i.e properly print for example 10^2
+ where the 2 is in superscript. This is all handled by the
+ new class SuperScriptText(). Not yet used in the actual
+ labels drawing but will be added shortly.
+
+* [me] Added a small tool mkgrad.php in the utils directory to
+ make it a breeze to create gradient squares which may be
+ used as background images.
+
+* [me] Added footer class to graph. This makes it easy to have
+ left,center and right footers in your graphs.
+
+* [me] Added subsubtitle to graphs
+
+* [me] Improved/Changed the way you work with CSIM. This new method
+ makes it possible to generate CSIM without having to create an
+ intermediate image on disk.
+ Added Graph::StrokeCSIM(). Added new chapter in the docs on how to
+ work with CSIM
+
+* [me] Changed naming of Spider* to Radar* since SpiderGraph is a
+ copyrighted name.
+
+* [me] Modified the automatic suppression of the first value of the
+ X-axis when the Y-axis have negative value so it don't happen
+ in the case where the X-axis is positioned at the minimum of
+ the Y-axis.
+
+* [me] Fixed spelling error of BK* instead of the correct BG*
+ for the default settings of background image style.
+
+* [me] Some internal code changes to RGB() class to make it easier
+ to use directly for external scripts.
+
+* [me] Changed the behaviour of filled line plot so that they
+ don't go down to 0 for null values in the plot but instead
+ behaves like '-' for non-filed plots, just ignore them and
+ connect the previous and next point instead.
+
+* [me] Fixed problem in integer scaling where it certain scale
+ combinations could generate floating point labels! Ooops..
+ (Affects IntCalcTicks())
+
+
+[5-July-2002] JpGraph 1.7
+==========================
+* [me] Removed the APACHE_CACHE_DIRECTORY define since it is
+ not used.
+
+* [me] Added Image map functionlity for rotated bar graphs,
+ also added new example for this (bar_csimex3.php)
+
+* [me] Made Ticks::SetPrecision() deprecated. Use SetFormatString()
+ instead
+
+* [me] Fixed problem with incorrect string being returned from
+ ToCyrillic()
+
+* [me] Allow Y2 axis to use "int" scale
+
+* [me] Fixed problem with wrongly names framework function for
+ patterns
+
+* [me] Fixed small problems with margins being slightly off for
+ roated images.
+
+* [me] Changed name of PlotMark::SetMarkColor() to PlotMark::SetColor()
+ for consistency
+
+* [me] Added example with "hidden" Y-axis.
+
+[17-June-2002] JpGraph 1.7-BETA
+===============================
+* [me] Added DisplayValue::SetFormatCallback() to make it possible to use
+ a user specified formatting callback routine.
+ (Note:: Yes, this is very nono-OO but the proper OO way, by forcing the
+ user to define a subclass of the plot class where the formatting method
+ is overridden will probably not fly with to many non-OO PHP peoples since
+ a callback is conceptually simpler.)
+
+* [me] Added DisplayValue::HideZero(). Makes it possible to hide zero values
+ in labels, for example zero slices in pie's, (e.g. $pieplot->value->HideZero())
+
+* [me] Added manual scaling to Gantt graphs, added example to illustrate
+ this, fixscale_gantt.php
+
+* [me] Adding image maps for Y2 scale as well.
+
+* [me] Fixed a small bug which made it impossible to use BRAND_TIMING with
+ Gantt charts. Enabling timestamping of graphs would give a "PlotArea to small"
+ error.
+
+* [me] Made a very small change to the AutoScaling algorithm to yield tighter
+ boundaries, i.e with the existing algorithm, depending on rounding problems
+ a plot which had a maximum value of, say 4, could get a autoscaling max
+ value of 5. Not a big problem (or hardly a bug) but not what I wanted.
+ All my 111 test cases works fine with the modification but I'm still
+ not convinced that this (very minor) adjustment is 100% (I can distinctly
+ remember that I had a good reason to do it the original way, I just can't
+ remember what :-)
+
+* [me] Graph titles may now have multiple rows. By default the lines will
+ be centered, but this may of course be adjusted by a call to, for
+ example, $graph->subtitle->ParagraphAlign('left');
+
+* [me] Added class FuncGenerator() to make it easy to generate
+ function plots (both normal and polar). To illustrate this I have
+ added three new examples funcex1.php,funcex2.php,funcex3.php.
+
+* [me] Added Axis::HideFirstLastLabel() to hide first and last label on
+ axis. This is usefull if we have a box around the plot area and we don't
+ want the labels to owerwrite the box (see funcex1.php)
+
+* [me] Make sure GD2 variables is registred in $GLOABALS[]
+
+* [me] Fixed bug in image maps for marks. Alt strings was incorrectly
+ indexed so it was only using values 0,2,4.. instead of 0,1,2,...
+
+* [me] Added Graph::SetAxisStyle(). This makes it easy to use
+ a few number of preset axis setup. For example duplicating the axis
+ as a border around the plot. Preset axis position availabel are
+ AXSTYLE_SIMPLE, AXSTYLE_BOXIN, AXSTYLE_BOXOUT
+
+* [me] Made Axis::SetTickLabelMargin() deprecated. Use
+ Axis::SetLabelMargin() instead. Name change to make it more
+ consistent with the other margin settings.
+
+* [me] Made Ticks::SetSide() a synonym for Ticks::SetDirection()
+ to make it consistent with side setting for labels and values.
+
+* [me] Added Axis::HideZeroLabel() as a shortcut for
+ axis->scale->ticks->SupressZeroLabel()
+
+* [me] Internal code changed. Replace the use of FontProp in SpiderGraph
+ with Text() and some additoinal code cleanup.
+
+* [me] Adjusted Image::Arc() so that no negative angles are
+ passed on to GD imagearc() since it barfs at negative angles.
+
+* [me] Added PiePlot3D::SetHeight() which is used to specify the
+ height in pixels of the 3D pies instead of using the default
+ height.
+
+* [me] Pie plots (both 2D and 3D) now displays the slice labels
+ by default.
+
+* [me] Added two more Examples. barintex1.php shows how to use
+ integer scale and barscalecallbackex1.php shows how to use
+ a callback function for formatting the scale.
+
+* [me] Modified grace calculation for Integer scale to have higher
+ sensitivity.
+
+* [me] Restructured code so class DateLocale is now in jpgraph.php
+ rather than in jpgraph_gantt.php. Also added $gDateLocale as a
+ global instance of the class DateLocale
+
+* [me] Modified integer autoscale to correctly handle very small values
+ (needs special handling of max==1 to avoid fraction)
+
+* [me] Changed the way min value is extracted when finding the baseline
+ for bar plots. Now uses the function Scale::GetMinVal() instead of
+ reading the value directly fomr the instance variable. This makes it
+ work better with logscales so that the bar starts from the min value
+ of the log scale. Also added check in bar PreStrokeAdjust() to see if
+ we are using a log Y-scale to automatically adjust the Y-base.
+
+* [me] Optimized Stroke() loop for LinePlot so calls to PlotMark::Stroke()
+ will only be done if the marks are really to be shown.
+
+* [me] Added Axis::SetLabelAlign().
+
+* [me] Made some internal modifiaction on how Axis title gets aligned
+ to make it possible to specify it manually without having it
+ automtically set. This makes it possible to generate nice
+ horizontal bar graphs.
+
+* [me] Added three examples in the Example directory on how to make
+ horizontal bar graphs.
+
+* [me] Don't always set Y-axis position automtically
+
+* [me] Add Text::SetAngle() as synonym for SetOrientation()
+ Added SetLabelSide() as synonym for SetlabelPos()
+
+* [me ]Make it possible to manually specify the alignment of the textstring
+ for the title position. This makes title looks nicer with horizontal
+ bar graphs.
+
+* [me] Allow Y-axis labels to be at an angle
+
+[14-May-2002] JpGraph 1.6.3
+===========================
+Changes/Additions:
+* [me] Added functionality to make AccBarPlot::SetAbsWidth() work in
+ jpgraph_bar.phps
+
+* [me] LinePlot::SetColor() no longer sets the color of the plotmark.
+ The problem that some people had was that if you set the mark color
+ specifically first and then unsuspecting called SetColor() for the
+ line the color for the plotmark circumference would also be set making
+ it look like the PlotMark::SetColor() didn't work.
+
+* [me] Improved the functionality of GanntGraph::SetDateRange() to make
+ it possible to display a section of the graph.
+ (In previous version it was a checked error if you tried to plot
+ a graph where any activity bar was outside the specified area.
+ This is now changed to only cap the bars to the area.
+ This makes it more suitable to handle big gantt charts
+ which now can be split into several images to show different
+ parts of the projects)
+
+* [me] Added GanttScale::SetWeekStart() to set the start of week to an
+ arbitrary day. For example SetWeekStart(0) will make the weeks start
+ on Sunday, SetWeekStart(1), Monday and so on.
+
+* [me] Fixed a rounding error in Gantt chart that could cause the week
+ start day to off by one.
+
+* [me] Modified the Min() and Max() methods for AccLineBar's to avoid
+ them overestimating the max value. (Basically now using the same
+ methods as in AccBarPlot)
+
+* [me] Added missing code to do percentage calculation for 3D Pies so
+ they work the same way as the 2D Pies.
+
+* [me] Added error check for the case where the image is so small that
+ the available plot area becomes to small.
+
+* [me] Compensate for a PHP bug (#12474) in strtotime() which can affect
+ Gantt chart so that the same month name is shown twice.
+ See http://bugs.php.net/bug.php?id=12474
+
+* [me] Compensate for the documented shortcomming in my
+ 3D Pie algortitm whereby a small exploded slice close to 270 degrees
+ get's slighthly nagged at tha base due to the fact that the z-order
+ stroking algorithm is only perfect if the slices are not exploded.
+
+
+[20-April-2002] JpGraph 1.6.2
+=============================
+Changes/Additions:
+* [me] Slightly adjusted LinearTicks::Stroke() to better handle som
+ potential rounding problems.
+
+* [Thomas George] Added LinePlot::AddArea() which makes it possible
+ to just fill part of a line plot (a part that is specified with a
+ start and end x-point)
+
+* [Thomas George] Fixed bug with missing coordinate when using filled
+ line plot with step style.
+
+* [me] Added client side image maps to scatter and line plots
+
+* [me] Fixed problem with warning "IMG_ARC_CIRCLE not defined" when
+ running on GD 1.x
+
+* [me] Removed test with is_null() in jpgraph.php to make JpGraph 1.6.2
+ work with older versions of PHP (especially 4.02).
+
+
+[07-April-2002] JpGraph 1.6.1
+=============================
+Changes/Additions:
+* [me] Modified autoscaling for the case where all values are equal to
+ make the min/max difference larger.
+
+* [me] Fixed bug in 3D pies for certain data values
+
+* [me] Fixed bug with images maps assuming that slices
+ where order anti-clockwise and the new pie algorithm did them
+ clockwise.
+
+* [me] Added subtitle to Pie and Pie3D graphs.
+
+[02-April-2002] JpGraph 1.6
+===========================
+Changes/Additions:
+* [me] Changed license to QPL
+
+* [me] Completely rewritten algorithm for 3D pies which gives MUCH better result. Will
+ also allow exploded 3D pies.
+
+* [D. Reinbach] Better handling of filling line plot with negative
+ values
+
+* [me] Added LinePlot::StrokeDataValue() for easy handling of plotting
+ data point values.
+* [Gyozo Papp,me] Real locales for Gantt chart. The locales now take a
+ proper locale string (e.g. "en_UK", or "sv_SE") to get the real
+ locales from the system. Note that this breaks the sc compatibility
+
+* [me] Removed jpgraph_dir.php and moved the directory settings back in
+ jpgraph.php due to problems in PHP with nested includes and search
+ paths.
+
+* [me] Added possibility to show values for scatter plot by
+ refactoring value support back into jpgraph.php Plot() class. This
+ makes it easy for all subclasses that uses Plot() to add display
+ value support.
+
+* [me] Added possibility to use X-coordinates in Error plots
+
+* [me] Fixed missing LogScale::SetXLabelOffset(). This made it
+ impossible to use linear plots with a log x-scale !!!
+
+* [me] Fixed bug so possible Y2 plots are also taken into account when auto
+ scaling the X-axis. This could otherwise couse plots outside the
+ scale when using Y and Y2 plots where Y2 plots had more data values.
+
+* [me] **********!!SC BREAK!!**************
+ Aligned the way values are displayed on Bar graphs with line and
+ scatter plots. The values to be displayed for these plots are now
+ all accessed through the "value" property of the plot. For example
+ $myBarPlot->value->Show();
+ instead of the previous
+ $myBarPlot->ShowValue();
+
+* [me] **********!!SC BREAK!!**************
+ Aligned the way values are displayed in Pie plots with line and
+ bar plots. You know use the "value" property in exactly the same way
+ as in bars and lines.
+
+* [me] **********!!SC BREAK!!**************
+ Aligned the way values are displayed in 3D Pie plots with line and
+ bar plots. You know use the "value" property in exactly the same way
+ as in bars and lines.
+
+* [me] Added possibility to display individual values in an
+ accumulated bar graph in addition to the overall accumulated
+ value. This is done as usual through setting the individual bars
+ value property to what you want (size, color, format and so on)
+
+* [me] Added another default error handler which is image based to
+ make it easier to see error messages when images are included
+ in a page. This is now the default error handler.
+
+* [me] Added possibility to specify the location of the X-Axis
+ to the top of the graph by using xaxis->SetPos("max") also
+ added new Example topxaxisex1.php to demonstrate this feature.
+
+* [me] Added more 3D pie examples to demonstrate new functionlity.
+
+[01-March-2002] JpGraph 1.5.3
+=============================
+Changes/Additions:
+* [me] Make it possible to use JpGraph 1.5.3 with GD 1.8.x. In 1.5.2 I was
+ using a function only available in GD 2.x.
+
+
+[29-Feb-2002] JpGraph 1.5.2
+===========================
+
+Bug fixes:
+----------
+* [me] Released the correct 3D pie file (By mistake I mixed the stable version with
+ the development branch for 2.0 in the 1.5.1 release just to prove
+ that I'm still human :-)
+
+* [me] Fixed out of bounds error when using shadows for bar graphs
+ with 0 value
+
+* [me] Fixed typo in DisplayValue::SetFormat()
+
+* [me] Fixed potential array out of bounds error in Plot::Min() and
+ Plot::Max()
+
+* [me] Fixed misplaced ')' in string construction in call to
+ JpGraph error.
+
+Changes/Additions:
+------------------
+* [me] Added possibility to use "auto" file name when stroking
+ directly to a file.
+
+
+* [me] Added tag 'title' to the image maps to make for better
+ compatibility with Mozilla.
+
+* [me] Moved class LineProprty to jpgraph.php from jpgraph_gantt.php
+ since it can be used in other modules as well.
+
+* [me]Added Text::GetTextHeight() which differs from
+ Text::GetFontHeight() in that it takes the total text height
+ (possible multiple lines) into account.
+
+* [me] Added Image::FilledArc()
+
+* [me] Added RotImage::SetTranslation()
+ Added RotImage::Circle()
+ Added RotImage::FilledCircle()
+ Added RotImage::Arc()
+ Added RotImage::FilledArc()
+
+
+[11-Feb-2002] JpGraph 1.5.1
+===========================
+* [me] No longer treat a not set $y[0] value as an explicit error. Instead
+ silently set $y[0] to 0.
+
+* [me] Changed "include" to "require" in jpgraph.php when including
+ jpgraph_dir.php. This seems to fix the strange problem whereby
+ the constants defined in jpgraph_dir.php didn't have a correct
+ value in jpgraph.php.
+
+* [me] Fixed problem with correctly drawing of bars when all bars
+ are negative. (Previously the baseline wasn't correctly handled
+ since min/max values needs to be mirrored for negative bars)
+ Note: Funny this problem has gone unnoticed so long.
+
+* [me] Fixed problem with doing gradient fill for negative bars.
+
+* [me] Fixed problem with autoscaling when there is a very small
+ difference (< 0.00001) between min and max and when both min and max are
+ negative. This might happen if you have a single negative value.
+
+* [me] Added labels for line graph. Each point my now also optionally
+ display it's value on a line graph. Value are controlled trough
+ class DisplayValue and it's instance in LinePlot.
+ LinePlot->value->Show(),
+ LinePlot->value->SetColor(),
+ LinePlot->value->SetFont(),
+ LinePlot->value->SetMargin(),
+ LinePlot->value->SetFormat(),
+
+
+[27-Jan-2002] JpGraph 1.5
+=========================
+* [me] Fixed misnamed xmin variable in Min() for acc bars
+
+* [me] Added possibility to specify paragraph alignment for
+ multi line text, i.e the text can be left,right or center aligned
+ within it's bounded box. The lignment is set by the new method
+ Text::ParagraphAlign()
+
+* [me] Changed position specification for text so that if the position
+ is specified in range [0,1] it is interpreted as fraction of image
+ height width and otherwise as absolute pixels.
+
+* [me] Added possibility to specify pie size (radius) both as a fraction and
+ absolute size in pixels.
+
+* [me] Added possibility to tweak color specification by adding a
+ saturation percentage, i.e you can specify color as "red:0.65"
+ which means 65% of the red value (in other words 35% darker).
+
+* [me] Tweaked the estimation of number of segments to approximate an
+ arc in Image::CakeSlice.
+
+[17-Jan-2002] JpGraph 1.5beta2a
+===============================
+* [me] Fixed typo in Max() calculation for accumulated bar graphs
+ which caused it to return wrong value.
+
+* [me] For Min() calculation for acc bars graphs take the user
+ specified ymin into account.
+
+* [Peter Svistunov, me] Added Cyrillic unicode support through the
+ global define LANGUAGE_CYRILLIC. This is also the start of a
+ generic support for Unicode languages through the LanguageConv
+ class.
+
+
+[15-Jan-2002] JpGraph 1.5beta2
+==============================
+* [me] Rewrite of the pie-chart drawing algorithm. This version
+ is slightly slower but avoids using GD floodfill function. This
+ fixes problems with small slices not being filled completely. As
+ an added bonus it is now possible to explode (and specify the
+ explode radius) for all slices. It is also possible to draw the
+ pie without having internal borders between the slices as well
+ as turning on/off the border surronding the pie.
+
+* [me] Added graphic primitive CakeSlice() to the Image class.
+ Used to simplify pie-drawing.
+
+* [me] Added more generic error handling. It is now possible to
+ install your own error object with
+ JpGraphError::Install(<name of error object>)
+ The default error handler will just abort execution.
+
+* [me] Security whole in utility script for displaying source
+ and image in frames fixed.
+
+* [Cedric Scheyder] Actively ignore hours in calculating Max/Min
+ values for dates in Gantt charts to avoid problems in autoscaling
+ when users specify start and end dates down to hours.
+
+* [Rick Widmer] Fixed problem in calculating min/max value when first
+ Y-value was non-numeric.
+
+* [me] Fixed calculation of width and height of text with multiple lines.
+ Multiple text lines (text blocks) should now be almost fully supported.
+
+
+[16-Dec-2001] JpGraph 1.5-BETA
+==============================
+* Take "\n" into (some) account when calculating string height and width
+ Text output now supports multiple lines better.
+
+* [me] Removed reference to global PHP_SELF, use HTTP_SERVER_VARS array
+ instead. This makes JpGraph working with 'register_globals' turned
+ off.
+
+* [me] Added some rudimentary check that GD is enabled before trying to
+ run JpGraph.
+
+* [me] Fixed silly cut-and-paste bug in AccBarPlot::Max() which could
+ cause to small max values to be returned
+
+* [me] Added property 'title' to PlotMark. The title is printed in the center
+ of the plotmark.
+
+* [DanNY (dulcis28@hotmail.com)] Added JpGraph Logo. (Thanks!)
+
+* [me] Added jplintphp.php, jpgendoc.php, jplintdriver.php which are
+ parsing, documentation and pretty printing tools I use to help
+ me to documentation and find unused code.
+ Tthat does some simple static analysis on PHP for silly mistakes
+ that I do (Like using instance variables without qualifying them
+ with "$this->" ) The tool also warns about unused instance variables.
+ Since it also parses the classes and methods I use it to generate
+ the documentation skeleton for the reference section.
+
+* [me] Added Gantt chart funcionality, jpgraph_gantt.php . See
+ Gantt tutorial for help on usage.
+
+* [me] Added new Gantt tutorial
+
+* [me] Added BKIMG_CENTER, possibility to center the background image.
+
+* [me] Timeout function now also aplies when you generate an image
+ off-line, i.e if the imagefile exist and is within the time window
+ now file will be written.
+
+* [Rasmus Lerdorf] Added patch to make background images work with GD 2.x
+ N.B GD 2.01 has bugs in handling TTF font with truecolor images which
+ are fixed in 2.02 but that has (of this writing) still not been officially
+ released. GD 2.x support in JpGraph is still to be considered experimental.
+
+* [me] Fixed some potential rounding errors in 2D Pie plot which in some
+ circumstances might have caused a flood fill to escape. Also added check
+ to better handle very small slices so we don't try to do a fill
+ on the border edge.
+
+* [me] Slightly changed the internal design for band patterns.
+ They are now created by a factory class instead. This makes it much easier
+ to re-use and have maximum code reuse.
+
+* [me] Factored out some functionlity from Graph::Stroke() to make it possible
+ to re-use some code in GanttGraph::Stroke()
+
+* [me] One more instance when PHP surprised me by thinking 0 == "-" aarrghhh
+ Now I think (knock on wood) all problems with data points=0 should be solved
+
+* [me] Added '&' to the assignment in Graph::Add() This now lets you specify changes
+ for lines, plots even after they been added to a Graph. But I'm not sure if this
+ is really such a good ide because you will have problem if you write code
+ like
+
+ for( .... ) {
+ $myplot = new LinePlot(.....);
+ $graph->Add($myplot);
+ }
+
+ Since this will not work the way you think (why?) Because the same
+ reference to the variable $myplot will be used for all additions and
+ hence the graph will only have multiple instances of the last added
+ LinePlot(). Have to think of this and what is the best way. (I will
+ probably never get used to PHP references....)
+
+
+[11-Nov-2001] JpGraph 1.4
+==========================
+Bug Fixes:
+-------------------------------------------------------------------------------------
+* [me] Enabled E_NOTICE warnings in my PHP installation and found a couple of
+ glitches (harmeless out-of-bound in two loops) which is now corrected.
+* [me] Fixed problem in line plots where y-value==0 wasn't drawn. (Once again
+ bitten by the fact that in PHP the following is true: ""==0)
+* [me] Fixed color specification for bars when initialized as an array of colors
+* [me] Fixed color of bars using shadow which was advertedly set to shadow color
+* [me] Fixing incorrect color specification for legends using bars with color arrays
+* [me] Add check for sum=0 in pie plots to avoid divide by 0 problem
+* [me] Changed the stroke orderd for FilledRectangle() so that the shadow doesn't
+ overwrite a line with weight > 1
+* [me] Fixed incorrect calculation of font height for titles which cause the
+ subtitle to be to close to the title.
+
+Additions:
+-------------------------------------------------------------------------------------
+* [me] Added depth parameter (DEPTH_FRONT, DEPTH_BACK) for gridlines
+ Graph::SetGridDepth()
+
+* [me] Added possibility to add static horizontal and vertical band with a
+ pattern in the plot area. Graph::AddBand() Supports solid, diagonal,
+ stright line, 3D-plane crosses (both diagonal/vertical) patterns.
+ Graph::AddBand()
+
+* [me] Added possibility to have static lines in the plot. This is now implemented
+ with new class PlotLine and two new functions Graph::AddLine(),
+
+* [me] Added symbolic constants to specify background image types
+ BGIMG_COPY, BGIMG_FILLPLOT, BGIMG_FILLFRAME
+
+* [me] Added extended label formatting through either
+ Axis::SetLabelFormatString() C-style formatting string
+ or
+ Axis::SetLabelFormatCallback() A callback function that are given the value to be displayed
+ and should manipulate the value as it likes
+
+ The use of the old style digit precision variable will be deprecated from next version.
+
+* [me] Added error check if supplied Y-data has unspecified value at index 0 to avoid
+ out of bounds error when corrupted data is sent in to JpGraph.
+
+* [me] Added possibility to specify image depth for gridlines. You can now specify
+ Graph::SetGridDepth() as DEPTH_FRONT or DEPTH_BACK. Depending on if you want
+ the gridlines at the back of in front of your plots.
+
+* [me] Added possibility to specify Cache file name as "auto". In this case the
+ image file name will have the same name as the scriupt that generates the
+ image with the extension of the automtically generated graphic format.
+ NOTE: You will get the wrong extension using this feature at the same
+ time as specifying a different graphic format thne the automatically choosen
+ one.
+
+* [me] Added some more advanced examples for impuls drawings impulsex3.php and
+ impulsex4.php
+
+* [me] The values on top of bars can now be at an angle and the horisontal
+ position now account for if a shadow is present.
+
+* [me] Added filename to Graph::Stroke() which makes it easy to output an image
+ directly to a file _without_ streaming it back to the browser. Usefull for
+ batch processing
+
+* [me] Added new tutorial for bar graphs
+
+* [me] New scale type "int". This gives a normal linear scale but restricts
+ ticks (and labels) to integer values. Can be used for both X and Y scales.
+
+* [me] Added subtitle to graph
+
+* [me] Added possibility to specify an array of X-coordinates with bar plots to
+ position individual bars on the X-scale. Makes it much easier to combine
+ bar plots with other types of plots, (line, scatter etc)
+
+* [me] Extended utility adjimage.php to allow it to manipulate color saturation.
+
+* [me] Added Image::AdjSat() to allow manipulation of the color
+ saturation in the image. This also makes it very easy to change the image
+ to greyscale (just set saturation=-1).
+ Extended Graph::AdjImage() and Graph::AdjBackgroundImage()
+ to include new parameter for saturation value.
+
+* [me] Added BarPlot::SetAlign() which makes it possible to align
+ bars relative the tickmark for linear and int scale
+
+* [me] Added possibility to absolutely set the width of bars. This will
+ override the automatically decided with based on the scale and tick distance
+
+Changes:
+-------------------------------------------------------------------------------------
+* [me] Legend color for scatter plots now becomes the fill color of the mark
+
+* [me] Impulsplots now anchors at Y-value = 0 if the y-axis contains both negative
+ and positive values. Otherwise it will go to the lowest value on the Y-axis.
+
+* [me] Bar shadows is now connected to bars to make for a stronger 3D effect.
+
+* [me] Removed all deprecated references and usage of FONT0, FONT1, FONT2. Note
+ You can no longer use these. Instead use FF_FONT0, FF_FONT1, FF_FONT2 etc
+
+* [me] Text scale now starts default labelling at 0
+
+* [me] All plots internally adjusted to start at default X-coordinate 0 to harmonize better
+ with X-scale.
+
+* [me] Graph->Stroke() now just returns instead of exit()
+
+* [me] Removed flag argument for BarPlot::SetShadow()
+
+* [me] Bar plots are know by default filled with lightblue
+ unless otherwise specified.
+
+* [me] Changed the way bars are positioned for linear and integer
+ scale for maximum flexibility when mixing barplot with other
+ plots.
+
+* [me] The ALT strings for all image maps now uses printf() syntax to display
+ the actual value. If you have used this feature you must update your
+ ALT strings, for example "val=%v" must become "val=%d" or whatever format
+ you choose.
+
+* [me] Changed the formatting of bar graphs by changing the framework
+ so that labels on the x-axis using a text scale is now independent
+ on the tick marks.
+ This makes it possible to have the labels centered under each bar but have
+ the tickmarks between the bars as most other commercial graph packages
+ format these typw of graphs. Introduced Axis::SetTextLabelsInterval()
+ Effects jpgraph.php, jpgraph_log.php and jpgraph_bar.php
+
+* [me] Renamed Axis::SetTextTicks() to Axis::SetTextTicksInterval() to make the name
+ more descriptive. The old name is deprecated from this version on but will still
+ work until 2.0 to keep compatibility.
+
+* [me] Cleaned up implementation of pie3d so it now inherits from pie to avoid
+ a lot of duplicated code and easier maintainance. (A good example of the real
+ life OO programming where you might not see the similarities between two classes
+ until you fully implement them.) File size of jpgraph_pie3d.php has shrunk by
+ roughly 40%
+
+[23-Sep-2001] JpGraph 1.3.1
+===========================
+Additions:
+* [Michael Anthon] Added client side image maps to 3D pie plots
+* [me] Added possibility to better control the labeling of pie graphs with an optional
+ label formatting string PiePlot::SetLabelFormat(), PiePlot::SetLabelType()
+ This also makes it possible to have labels with the absolute value and not only the
+ percentage. (same with pie3d)
+* [me] Added formatting capabilities for legends on pie and pie3d graphs. Can have the value
+ automtically included (Se Examples/pieex6.php)
+* [me] Added some more examples, negbarvalueex1.php, scatterlinkex2.php, pie3d_csimex1.php
+ pieex6.php
+
+
+Known bugs and omissions:
+- Client side image maps does not currently handle exploded pie graphs (just
+ normal pie graphs.
+
+Bug fixes:
+* [me] Fixed problem with positioning the displayed values for negative bars
+* [me] Fixed shadow for negative bars
+* [me] Fixed problem in Plot::Min(), PlotMax::Max() whereby zeros where not counted
+ towards minimum since in PHP 0=="". This could sometimes cause strange autoscaling.
+* [Vitaly E. Ashmarin] Wrong arguments to imagejpeg() for combination of specified
+ image quality and filename = ""
+* [John Milne] BarPlot::SetFillColor() fixed glitch in loop variable
+
+
+[13-Sep-2001] JpGraph 1.3
+=========================
+Note: I have to the best of my memory tried to give credit where
+credit is due. Howevever, since ver 1.2.2 I have received over 2000 mail about
+JpGraph with Questions/Suggestions/Improvments (and a few bug fixes) etc and
+I might have missed some in the process of going through these mails. The
+fault is completely my own and if you recognize some idea I have implemented
+something which you think you should have credit for please drop me a note and
+I will adjust the add you to the change notes.
+
+Known bugs and omissions:
+- The automatic value on bar graphs does not work well with
+ negative bar graphs. (Quite easy to fix though..)
+- Client side image maps does not currently handle exploded pie graphs (just
+ normal pie graphs.
+
+IMPORTANT NOTICE:
+* SC (SOURCE COMPATIBILITY) BREAK !: If you want to rotate the image the parameter
+ is no longer supplied directly in the call to Graph() but rather as a method
+ call Graph::SetAngle(). I decided to make this SC break since I judged it more
+ natural to supply the timeout value just efter the specified cache-file name
+ in the Graph() call. Hopefully this will not hit to many people since my hunch is
+ that few people uses the rotate function and it's very easy to fix those script
+ that does so.
+
+Bug Fixes:
+* [michael@anthon.net] Corrected behviour of accumulated bar graph so it now
+ diffrentiate between the accumulated positive and negative values.
+
+* [marko@fly.srk.fer.hr] Fixed Max() calculations for accumulated bar graphs
+ where my original code was a little bit sloppy. (I simply took the max for
+ each plot and added those and this might be more then the real maximum.)
+
+* [m.purgar@extracom.de] Spotting an incorrect "jpg" in the image where it should
+ have been "jpeg"
+
+* [adam.blomberg@euroseek.net,patrik.johansson@euroseek.net] 3D-Pie filling
+ problem for small slices due to the discrapency between GD arc() and the
+ JpGraphs purely mathematical definition of an ellipse.
+
+* [delorme.maxime@free.fr] 2D-Pie Floodfill might "escape" for very small values of slices.
+
+* [et.al] Improved handling of 0:s vs "" values. This was necessary since PHP treats
+ 0 as "" the same in a number of situations and JpGraph needs to diffrentiate these
+ two cases. This caused 0 data values to be treated as null values and not be inluded
+ in (for example) autoscaling consideration.
+
+Additions:
+* [me] Added new global constant USE_CACHE which makes it possible to disable
+ writing to the cache even if a filename is supplied in th Graph::Graph() method.
+
+* [me] Added LinePlot::SetStyle() to make it possible to make dashed, dotted etc
+ lineplots. (See example1.2). Also adjusted legend to display the same style in
+ the legend.
+
+* [michael@anthon.net, me] Added client side image maps for all types of bars
+ and 2D pies with possibility to have the actual value shown in an ALT-tag.
+
+* [me] Added drop shadow (or rather "right-up" shadow) to bar graphs BarPlot::SetShadow()
+
+* [me] Added possibility to have the actual value of bar graphs displayed at top of graphs
+ via BarPlot::ShowValue(), BarPlot::SetValueFont(), SetValueMargin(), SetValueColor()
+ The format of the value is specified according to standard C printf() string formatting
+ i.e. "val=%d" will for example print a string "val=13"
+
+* [me] Added possibility to adjust brightness and contrast for background image
+ via the Graph::AdjBackgroundImage() method.
+
+* [me] Added possibility to adjust brightness and contrast in the finished image
+ via the Image::AdjBrightContrast() method.
+
+* [me] Added possibility to just generate an image to a file and not stream
+ it directly back to the browser. Added parameter $aInline to Graph::Graph()
+
+* [me] Added timout for cache, i.e if the image in the cache is older then
+ the specified number of minutes (=0 never re-generate, -1 always regenerate)
+ then re-generate the image. Just specify a timeout in minutes with a call:
+ $mygraph->cache->SetTimeOut(10)
+
+* [me] Added possibility to use logarithmic scale for SpiderPlots. This also resulted
+ in a little bit of internal cleanup in jpgraph_spider.php.
+
+* [me] Added SetWeight() method to class Ticks() for control of line thickness for ticks
+
+* [me] Added SetMarkColor method to class TIcks() which lets you specify different
+ colors for major and minor tick marks.
+
+* [me] Added method RelTranslate() to LinearScale() and LogScale() classes which is used
+ in Spider class. This makes a world to screen relative translation.
+
+* [me] Added possibility to link data points in a scatter plot with lines
+ by the addition of method ScatterPlot::SetLinkPoints()
+
+* [me,john.milne@one2one.co.uk] Added possibility to have individual colors of
+ bars in bar graphs by having BarPlot::SetColor() accept an array as argument.
+
+* [sergio@alsernet.es] Make it possibly to have x-tick labels to consist of two lines
+ by inserting a "\n" separator in the text.
+
+* [CK1@wwwtech.de] Adding JPEG quality setting. This is done by adding
+ a method SetQuality() to the img class. After creating a graph this can then
+ be used as $mygraph->img->SetQuality($some_qvalue).
+ Currently this only affects JPEG images.
+
+* [delorme.maxime@free.fr] Possibility to use different grace value for top and bottom
+ of graph and not just a single value as in 1.2.2
+
+Changes:
+* [me] Changed group/permission handling for created files to better adjust itself
+ to the way the local Apache user is setup
+
+* [me] The default for spider graphs is now NOT to be filled
+
+* [me] Allow all datapoints to be 0 which will set the scale to [0,1]
+
+* [me] Removed the need for a DIR_BASE variable
+
+
+[29-Apr-2001] JpGraph 1.2.2
+===========================
+Bug fixes:
+* Removed reference to non-existent property 'bypass' in class RotImg
+* Changed to allow the last X-gridline to be drawn (comparison with limit was '<'
+ where it should have been '<=' )
+
+Additions:
+* Added possibility to use background image
+* Added possibility to use approximate color through the "USE_APPROX_COLORS"
+ constant
+* Handling of diffrent form of "null values" for line graphs. An y-value
+ can now be "" or "-". In the first case (a true null value) this mark
+ will cause a disruption in the line graph to indicate that the value
+ is undefined. In the other case specifying the value as "-" will cause
+ the value to be ignored bu the line will still be drawn between the
+ previous data point and the following. Neither of these cases will have
+ any mark drawn.
+* Added a fatal warning when no more colors can be allocated for the chosen
+ graphic format.
+
+Changes:
+* "Downgraded" anti-aliasing algorithm to use fewer color levels
+ in the transition to avoid using up to much of the color palette
+* Cleaned up some of the code to not rely on PHP default initialization
+ of variables. This will help those who have got warnings when they
+ have had all error reporting enabled in php.ini (E_ALL)
+* Legends now show the plot mark (if defined) instead of just a square of
+ the right color.
+
+Acknowledgements:
+* Thanks to kevin@pricetrak.com for suggesting the treatment of
+ pure NULL values.
+* Thanks to luca_n@hotmail.com for getting my attention to the warnings
+ caused by relying on PHP default initialization of variables.
+ (My installation had E_NOTICE disabled, hence I never got those warning myself.)
+
+
+[29-Mar-2001] JpGraph 1.2.1
+===========================
+Bug fixes:
+* When min and max values for autoscaling was equal this resulted in trying to calculate
+ log10(0) = INF resulting in an infinite loop in LinearTicks::Stroke()
+* When autoscaling was used with only one data point it tried to calculate log10(0) = INF
+ resulting in an infinite loop
+* fopen("xxx","r") should be fopen("xxx","rb") causes problems on windows system with IIS5
+* JPG streaming function was incorrectly called "imagejpg" should be "imagejpeg" (see the difference?)
+* Non existent color name in jpgraph_pie.php
+* Removed all deprecated forced references in function calls
+* The width of the surrunding box was not calculated correctly for internal bold font
+
+Additions:
+* Color themes for pie graphs
+* Beta release of 3D pie graphs
+* Added DIR_BASE as constant to make it possible to use one system wide copy
+of JpGraph. NOTE: This must be set to the directory where JpGraph is installed.
+
+Acknowledgements:
+* (ck1@wwwtech.de) (and a russian guy which I unfortunately lost the name of)
+ for reporting the imagejpg spelling mistake
+* (ales.gregor@zps.skoda-auto.cz) for finding the binary file problem with IIS
+* Several people have reported the problem with autoscaling when all Y-values
+ were equal. Thanks to you all for acting as my privet QA team!
+
+
+[18-Mar-2001] JpGraph 1.2
+=========================
+Additions:
+----------
+* Added "BRAND_TIMING" which give possibility to brand each image generated with
+the time (in ms) it took to generate the image.
+* Added Gradient Fill for bar graphs, The gradient fill allows 6 different styles.
+* Added Anti-aliasing for lines. Note drawing anti-aliased lines is 7-10 times
+slower then "normal" lines!
+* Added full TTF support. this means an internal SC break (Source Code Break). Normal JpGraph
+script should not be affected if you have used only publized "public" frunctions. The SC break is
+to allow uniform treatment of both internal fonts and TTF. This has been achieved by changing
+the parameter list for SetFont(). However, backward compatibility with the old naming conventions
+for internal fonts are kepot, e.g. SetFont(FONT1_BOLD) still works but is deprecated and will not
+be valid from 2.0
+* Added possibility to control font for title of Spider Axis through the added
+property 'title' to the axis in the spider graph.
+* Added 'SetColor()' in pie graphs as a shortcut to SetMarginColor() to set the background color
+* Added possibility to draw X-axis labels at arbitrary angle. Internal fonts only supports
+horizontal and verical.
+* Added possibility to draw boxed text at an angle
+* Added possibility to have different colors for axis and the labels on the axis by the addition
+of extra parameter to SetColor()
+* Added 350 more named colors.
+* Added canvas "fake" graph to make it easy to draw arbitrary graphics
+* Added SetCenter() method for line graphs.
+* Added SetGrace() for autoscaling purposes.
+
+Changes:
+--------
+* If you use a text-scale for X-axis the default labels now start at 1 instead of 0 since this is
+actually a counting scale and it makes more sense to start at 1.
+* Improved handling of bar-graphs with negative values.
+* Updated existing examples to use new format for SetFont()
+* Made it a critical error to specify a non existant font
+* Changed so that orientation for Text() is now given as an angle, i.e. SetOrientation(45). Old
+style of using SetOrientation("v") (for vertical) is deprecated. But will work until V2
+
+Bug fixes:
+----------
+* Improved handling of computational effects on small values in scaling where a rounding error might cause the
+last label not to be drawn.
+* The position for the title of X-axis could in some cases be slightly different in first
+and second pass due to incorrectly determination of the X-axis labels font height.
+* Specifying an minimum Y-value for Y-axis on bar graphs could on rare occasions (combination
+of scale values and specified density of autoscaling ticks) cause a gap between the bottom of
+each bar and the X-axis.
+
+
+[18-Feb-2001] JpGraph 1.1
+=========================
+Additions/Changes:
+------------------
+* Added Spider graphs
+* Added Pie graph
+* Added Scatter (and impuls) graphs
+* Added possibility to rotate plots an arbitrary angle
+* Added step style rendering to lineplot
+* Added Breseham circle drawing which gives better visual apperance
+ then the built in Arc() in GD (on the expense of CPU and performance)
+* Improved Polygon and Rectangle drawing so that it will use line weight settings
+* Improved the visual appearance of lines with weight>1 by correctly calculating
+ (angle wise) the endpoints and using a filled polygon to draw the line.
+ Unfortunately it would require some real improvements in the GD library to really improve
+ the visual appeance of lines with weight>1. This is as good as it gets without writing
+ a low level pixe-by-pixel correct plotting. With my method you get it to look visually
+ aestethic right in 90% of the cases with just 10% work.
+* Added MARK_FILLEDCIRCLE as new mark type. Note that due too the poor performance of the
+ basic Arc() image primitive in PHP4 the circles isn't perfect circles, they tend to be a
+ a little bit flat at multiples on PI/2.
+* Added the possibility to have a separate fill- and line- color for marks
+* Moved Class PlotMark from jpgraph_line.php to base in jpgraph.php since this class is also used
+ with scatter plots.
+* Made it a critical error to try to use Text X-scale with specified X-data points.
+ Previously no warning was given for this non-defined state. (Using text scale with specified
+ points really doesn't make sense.)
+* Made it a critical error to use unknown color rather than silently replacing it with black.
+* Updated documentation to reflect added capability
+
+
+Bug fixes:
+----------
+* Fixed minor bug in Line() in which it didn't update the last point for use with
+ subsequent LineTo() calls (forgotten $this->)
+* Eliminated assumptions in DashedLine() that x1<=x2 and y1<=y2 eliminates the
+ potential pixel overshoot.
+* Fixed a serious bug in Line plot when used with a specified X-scale (not the normal default)
+ since the X-coordinate wasn't read from the correct vector!
+* Fixed a serious bug whe using as specified X-scale since the maximum value for a plot
+ wasn't correctly passed to the autoscaling.
+ Note: The reason that these two bugs haven't been discovered previously is the fact
+ that my test specs didn't include test cases to do with specified X-axis (I almost never ever use that).
+ This has now been corrected and added to the test suit.
+* Fixed so the possible box around the plot area correctly honours the weight specified for it.
+
+
+[05-Feb-2001] JpGraph 1.0
+=========================
+* Initial release.
+
+[EOF]
--- /dev/null
+<?php
+//=======================================================================
+// File: IMGDATA_ROUNDBALLS.INC
+// Description: Base64 encoded images for small round markers
+// Created: 2003-03-20
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: imgdata_balls.inc,v 1.4 2003/04/26 10:03:50 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+class ImgData_Balls extends ImgData {
+ var $name = 'Round Balls';
+ var $an = array(MARK_IMG_LBALL => 'imgdata_large',
+ MARK_IMG_MBALL => 'imgdata_small',
+ MARK_IMG_SBALL => 'imgdata_xsmall',
+ MARK_IMG_BALL => 'imgdata_xsmall');
+ var $colors_1 = array('blue','lightblue','brown','darkgreen',
+ 'green','purple','red','gray','yellow','silver','gray');
+ var $index_1 = array('blue'=>9,'lightblue'=>1,'brown'=>6,'darkgreen'=>7,
+ 'green'=>8,'purple'=>4,'red'=>0,'gray'=>5,'silver'=>3,'yellow'=>2);
+ var $maxidx_1 = 9 ;
+
+ var $colors_2 = array('blue','bluegreen','brown','cyan',
+ 'darkgray','greengray','gray','green',
+ 'greenblue','lightblue','lightred',
+ 'purple','red','white','yellow');
+
+
+ var $index_2 = array('blue'=>9,'bluegreen'=>13,'brown'=>8,'cyan'=>12,
+ 'darkgray'=>5,'greengray'=>6,'gray'=>2,'green'=>10,
+ 'greenblue'=>3,'lightblue'=>1,'lightred'=>14,
+ 'purple'=>7,'red'=>0,'white'=>11,'yellow'=>4);
+
+ var $maxidx_2 = 14 ;
+
+
+ var $colors_3 = array('bluegreen','cyan','darkgray','greengray',
+ 'gray','graypurple','green','greenblue','lightblue',
+ 'lightred','navy','orange','purple','red','yellow');
+
+ var $index_3 = array('bluegreen'=>1,'cyan'=>11,'darkgray'=>14,'greengray'=>10,
+ 'gray'=>3,'graypurple'=>4,'green'=>9,'greenblue'=>7,
+ 'lightblue'=>13,'lightred'=>0,'navy'=>2,'orange'=>12,
+ 'purple'=>8,'red'=>5,'yellow'=>6);
+ var $maxidx_3 = 14 ;
+
+ var $colors,$index,$maxidx;
+ var $imgdata_large ;
+ var $imgdata_small ;
+ var $imgdata_xsmall ;
+
+
+ function GetImg($aMark,$aIdx) {
+ switch( $aMark ) {
+ case MARK_IMG_SBALL:
+ case MARK_IMG_BALL:
+ $this->colors = $this->colors_3;
+ $this->index = $this->index_3 ;
+ $this->maxidx = $this->maxidx_3 ;
+ break;
+ case MARK_IMG_MBALL:
+ $this->colors = $this->colors_2;
+ $this->index = $this->index_2 ;
+ $this->maxidx = $this->maxidx_2 ;
+ break;
+ default:
+ $this->colors = $this->colors_1;
+ $this->index = $this->index_1 ;
+ $this->maxidx = $this->maxidx_1 ;
+ break;
+ }
+ return parent::GetImg($aMark,$aIdx);
+ }
+
+ function ImgData_Balls() {
+
+//==========================================================
+// File: bl_red.png
+//==========================================================
+ $this->imgdata_large[0][0]= 1072 ;
+ $this->imgdata_large[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAByF'.
+ 'BMVEX/////////xsb/vb3/lIz/hIT/e3v/c3P/c2v/a2v/Y2P/'.
+ 'UlL/Skr/SkL/Qjn/MTH/MSn/KSn/ISH/IRj/GBj/GBD/EBD/EA'.
+ 'j/CAj/CAD/AAD3QkL3MTH3KSn3KSH3GBj3EBD3CAj3AAD1zMzv'.
+ 'QkLvISHvIRjvGBjvEBDvEAjvAADnUlLnSkrnMTnnKSnnIRjnGB'.
+ 'DnEBDnCAjnAADec3PeSkreISHeGBjeGBDeEAjWhITWa2vWUlLW'.
+ 'SkrWISnWGBjWEBDWEAjWCAjWAADOnp7Oa2vOGCHOGBjOGBDOEB'.
+ 'DOCAjOAADJrq7Gt7fGGBjGEBDGCAjGAADEpKS/v7+9QkK9GBC9'.
+ 'EBC9CAi9AAC1e3u1a2u1Skq1KSm1EBC1CAi1AACtEBCtCBCtCA'.
+ 'itAACngYGlCAilAACghIScOTmcCAicAACYgYGUGAiUCAiUAAiU'.
+ 'AACMKSmMEACMAACEa2uEGAiEAAB7GBh7CAB7AABzOTlzGBBzCA'.
+ 'BzAABrSkprOTlrGBhrAABjOTljAABaQkJaOTlaCABaAABSKSlS'.
+ 'GBhSAABKKSlKGBhKAABCGBhCCABCAAA5CAA5AAAxCAAxAAApCA'.
+ 'ApAAAhAAAYAACc9eRyAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF'.
+ 'HUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkRFD'.
+ 'UHLytKAAAB4UlEQVR4nGNgIAK4mGjrmNq6BmFIWMmISUpKSmk5'.
+ 'B8ZEokj4qoiLiQCBgqald3xaBpKMj6y4sLCQkJCIvIaFV0RaUR'.
+ 'lCSk5cWEiAn19ASN7QwisuraihHiajKyEixM/NwckjoKrvEACU'.
+ 'qumpg7pAUlREiJdNmZmLT9/cMzwps7Smc3I2WEpGUkxYkJuFiY'.
+ 'lTxszePzY1v7Shc2oX2D+K4iLCgjzsrOw8embuYUmZeTVtPVOn'.
+ 'gqSslYAOF+Ln4ZHWtXMPTcjMrWno7J82rRgoZWOsqaCgrqaqqm'.
+ 'fn5peQmlsK1DR52vRaoFSIs5GRoYG5ub27n19CYm5pdVPnxKnT'.
+ 'pjWDpLydnZwcHTz8QxMSEnJLgDL9U6dNnQ6Sio4PDAgICA+PTU'.
+ 'zNzSkph8hADIxKS46Pj0tKTc3MLSksqWrtmQySAjuDIT8rKy0r'.
+ 'Kz+vtLSmur6jb9JUIJgGdjxDQUVRUVFpaUVNQ1NrZ9+kKVOmTZ'.
+ 'k6vR0sldJUAwQNTU2dnX0TgOJTQLrSIYFY2dPW1NbW2TNxwtQp'.
+ 'U6ZMmjJt2rRGWNB3TO7vnzh5MsgSoB6gy7sREdY7bRrQEDAGOb'.
+ 'wXOQW0TJsOEpwClmxBTTbZ7UDVIPkp7dkYaYqhuLa5trYYUxwL'.
+ 'AADzm6uekAAcXAAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bl_bluegreen.png
+//==========================================================
+ $this->imgdata_large[1][0]= 1368 ;
+ $this->imgdata_large[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMMFi8hE9b2uAAABOVJREFUeNq9lk2sJFUVx3+3qv'.
+ 'tW95t57zFvhiFxmCFRUJRoNCQiJARMhiFx/Igxii5goTG6ZDAu'.
+ '/EhcSCIrTAgLEiKsJ8ywABNZEMJXEDYCukAmjgjzBkK/j35V1d'.
+ '333FtV97io97pfzwxfG86qcu/N+Z3zP+fcW/Apmfk4hx57+R/6'.
+ 'Rqmc9ykhsWjlsUngAA1fXIQ7b73pI/186IGHnn9dH/8frC8v4I'.
+ 'PiG53uaerR4GmKkv31mB8cyfjd946ZTwR66qVX9OTWIi8UKUv9'.
+ 'BOrZXpYZvFeiBvzI0fgSUSFKwbVG+Pl1V3HH0VvNR4KeeukV/f'.
+ 'PmMmdHhst76aXD64AbeVQ9bjNHaiGOC2o3wLrAb2/4LL/84ffn'.
+ 'fCdzkOdayKpLppBemrBsU5Y1Zdmm9LJdGU6E/t4M24Q26jRDRL'.
+ 'j3mdc49cSTekFsMzs5XuTsyLDUNSDQ25NwKOly9YIl22MYhJr/'.
+ 'uoDtBBoT0CxBRGYOAhibIaOCe//2MpfM6KHnX9cXipSlbkKWmS'.
+ 'nk9iv38J0jixw7vJfrTMYBOvhSoQHJBS09ANELloAGDxW8tfoW'.
+ 'J+5/UC8CPS0LU7r3SpYarr7M8rmFjMPLXT6/33L4si7Z2GCrQC'.
+ '+0ctlOaNs9DReV8vSLr85ndPLpZ/WNvHW+01kAVFBOGvJx0wYg'.
+ 'Sp47RIQ4Emwa8FGJXlDxSCFo5YlVgAo2hwPue/hRndboTV3EW2'.
+ 'Wp3k6wBp8q56QiWzecW6vwQfnPRkAWhFgILnq08jQ+R2nlUzzN'.
+ 'uES9Q7Vd+9fba7NmWJW61db2247qACmcjxXr45psYphsFGSLBu'.
+ 'kIajxqtjNwHkvAjQt0sg3crhPA2+fPz0CuyNFOghsGsr19mnFg'.
+ 'DGwrRm8UoAtNmQPQtRXDgdC4HImCFEKcCE0oieUWUYq2LtbiGp'.
+ 'mBQmppfIkjw45DK0QNNkvQ0jMBtPL0UnDRM1rN+cxKwzvOo2NP'.
+ 'tykR9a1kfpZNDLMG6QDYJqCTBvUe1+uxs+YKyPoGrTwY2HhvC4'.
+ 'CDWQd5d4xNApNQEEMgjgLdUCLBQ5cprL/trwNwKG2IUmDqDFd5'.
+ 'sr5BWrlxuSdLDFEFlqAzXGc4zFjupqh6uqYihpxJcEgp026l2w'.
+ '7wFUv7Z6AvrfRo/n0OYzPwIKE3HUKAJg2otMBiElnsF7wngis9'.
+ '3ZDjNnLi7huCWUZfueZKTu/M0V3HvmkOFDVxVKDG04ScejSgW5'.
+ 'V0q5JYFEghuDLHlTmToqDeGOCKIVtrW9hsdmXufEcNLPSXuPHa'.
+ 'a+bvuh9df5AH/v5PDFmbWQC3Mx+TVvfGVTRB2CodNgT2JBX003'.
+ 'aANZAYS/BxCv32TV/l2C03G7jgmfjGiT/qmeEmibEYm7XzAO2k'.
+ 'A+pbgHhBgydqu54YO5eRiLCy7yDvPP6Xqf+5Z+Lu277OYuOpiw'.
+ 'H15oBmlNOMcmK5RbP+PrEscGU+DSAxdg4CICIkxnLP8aNz63Og'.
+ 'H3/rdvOb795GVhuaYo0oBc3GGrEsUPVTwO6a7LYd+X51x3Hu/t'.
+ 'lP5tS65FN+6okn9U+n/sqb596dTvhOF+02myXTmkQNrOw7yD3H'.
+ 'j14E+UDQjp24/0E9/eKrbA4HH3aMK1b2ccvXvswjv//1J/s5ud'.
+ 'Due/hRPfP+OmfOrk7vrn7a48ihA3zh8CH+8Iuffiw/n4r9H1ZZ'.
+ '0zz7G56hAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bl_yellow.png
+//==========================================================
+ $this->imgdata_large[2][0]= 1101 ;
+ $this->imgdata_large[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAB2l'.
+ 'BMVEX//////////+///+f//9b//8b//73//7X//63//6X//5T/'.
+ '/4z//4T//3P//2v//1r//0r//0L//zH//yn//yH//xj//xD//w'.
+ 'j//wD/90L/9zn/9zH/9xj/9xD/9wj/9wD39yn37zn37zH37yH3'.
+ '7xD37wj37wDv70Lv50rv50Lv5znv5yHv5xjv5wjv5wDn51Ln5x'.
+ 'Dn3jHn3iHn3hjn3hDn3gje3oze3nPe3lLe1oze1nPe1lLe1ine'.
+ '1iHe1hje1hDe1gje1gDW1qXW1mvWzqXWzkLWzhjWzhDWzgjWzg'.
+ 'DOzrXOzq3OzpzOzgDOxkrOxinOxhjOxhDOxgjOxgDGxqXGxnvG'.
+ 'xmvGvRjGvRDGvQjGvQDFxbnAvr6/v7+9vaW9vZS9vQi9vQC9tR'.
+ 'C9tQi9tQC7u7W1tZS1tXu1tTG1tQi1rRC1rQi1rQCtrYytrSGt'.
+ 'rQitrQCtpYStpSGtpQitpQClpYSlpXulpQClnBClnAilnACcnG'.
+ 'ucnAicnACclAiclACUlFqUlCmUlAiUlACUjFKUjAiUjACMjFKM'.
+ 'jEqMjACMhACEhACEewB7ezF7exB7ewB7cwBzcylzcwBzaxBzaw'.
+ 'BraxhrawhrawBrYxBrYwBjYwBjWgBaWgBaUgCXBwRMAAAAAXRS'.
+ 'TlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAd'.
+ 'LdfvwAAAAHdElNRQfTAwkRFBKiJZ4hAAAB7ElEQVR4nI3S+1vS'.
+ 'UBgHcB67WJmIMWAVdDHEDLBC6Go0slj3Ft0m9RRBWQEmFZFDEM'.
+ 'Qgt0EMFBY7p/+198hj1kM/9N1+++x73rOd6XT/kStnTx4fPzd9'.
+ 'uwfOjFhomj7smAhwj/6Cm2O0xUwy6g7cCL99uCW3jtBmE7lsdr'.
+ 'fvejgpzP7uEDFRRoqy2k8xQPnypo2BUMP6waF9Vpf3ciiSzErL'.
+ 'XTkPc0zDe3bsHDAcc00yoVgqL3UWN2iENpspff+2vn6D0+NnZ9'.
+ '6lC5K6RuSqBTZn1O/a3rd7v/MSez+WyIpVFX8GuuCA9SjD4N6B'.
+ 'oRNTfo5PCAVR0fBXoIuOQzab1XjwwNHx00GOj8/nKtV1DdeArk'.
+ '24R+0ul9PjmbrHPYl+EipyU0OoQSjg8/m83kl/MMhx0fjCkqio'.
+ 'SMOE7t4JMAzDsizH81AqSdW2hroLPg4/CEF4PhKNx98vlevrbY'.
+ 'QQXgV6kXwVfjkTiSXmhYVcSa7DIE1DOENe7GM6lUym0l+EXKks'.
+ 'K20VAeH2M0JvVgrZfL5Qqkiy0lRVaMBd7H7EZUmsiJJcrTdVja'.
+ 'wGpdbTLj3/3qwrUOjAfGgg4LnNA5tdQx14Hm00QFBm65hfNzAm'.
+ '+yIFhFtzuj+z2MI/MQn6Uez5pz4Ua41G7VumB/6RX4zMr1TKBr'.
+ 'SXAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bl_silver.png
+//==========================================================
+ $this->imgdata_large[3][0]= 1481 ;
+ $this->imgdata_large[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAMAAAAM7l6QAAADAF'.
+ 'BMVEUAAADOzs7Gxsa9vb21tbXOxsbOzsbGzsb3///O1ta1vb2c'.
+ 'paVSWlpKWlpSY2ve5+97hIze7/9aY2vO5/9zhJRaa3tSY3PGzt'.
+ 'aMlJxrc3tja3NKUlpCSlK1vcZze4RSWmPW5/+Upb3G3v9zhJxS'.
+ 'Y3t7jKVaa4TO3veltc6ElK1re5Rjc4ycpbV7hJRaY3M5QlLn7/'.
+ '/Gzt6lrb2EjJzO3v9ja3vG1ve9zu+1xueltdacrc6UpcaMnL1C'.
+ 'SlqElLV7jK1zhKVre5zW3u/O1ue1vc6ttcaMlKVze4xrc4RSWm'.
+ 'tKUmPG1v+9zve1xu+tveeltd6crdbe5/+9xt6cpb17hJxaY3s5'.
+ 'QlrW3vfO1u/Gzue1vdattc6lrcaUnLWMlK2EjKVze5Rrc4xja4'.
+ 'RSWnNKUmtCSmO9xuecpcZ7hKVaY4TW3v/O1vfGzu+1vd6ttdal'.
+ 'rc69xu+UnL2MlLWEjK1ze5xrc5R7hK1ja4zO1v+1veettd6lrd'.
+ 'aMlL3Gzv/39//W1t7Gxs61tb29vcatrbWlpa2cnKWUlJyEhIx7'.
+ 'e4TW1ufGxta1tcZSUlqcnK3W1u+UlKW9vda1tc57e4ytrcalpb'.
+ '1ra3vOzu9jY3OUlK29vd6MjKWEhJxaWmtSUmNzc4xKSlpjY3tK'.
+ 'SmNCQlqUjJzOxs7///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
+ 'AAAAAAAAAAAAAAAAAAAAAAAAD///9fnkWVAAAAAnRSTlP/AOW3'.
+ 'MEoAAAABYktHRP+lB/LFAAAACXBIWXMAAABFAAAARQBP+QatAA'.
+ 'AB/klEQVR42mNgxAsYqCdd3+lcb4hLmj8wMMvEu8DCMqYbU9op'.
+ 'UEFB2MTb26eyysomFl06XEEhUCHLpAKo2z/fujikEUVaXUFBMB'.
+ 'BouLePuV+VVWGRciIXknSEsImCQd3//xwmPr65llaFcSFJHkjS'.
+ '3iYmWUDZ//8NfCr989NjNUMSUyTg0jneSiaCINn/gmlVQM12qg'.
+ 'lJnp5waTMTE5NAkCyHWZW/lXWNfUlikmdYK0zax7siS4EDKJtd'.
+ 'mQeU1XRwLBdLkRGASucWmGVnZ4dnhZvn5lmm29iVOWpnJqcuko'.
+ 'JKR1Wm5eTkRKYF5eblp9sU2ZeUJiV7zbfVg0pH56UFBQXNjIqK'.
+ 'jgkujItX1koKTVmYajsdKu2qETVhwgSXiUDZ2Bn9xqUeoZ5e0t'.
+ 'LzYYZ3B092ndjtOnmKTmycW1s7SHa+l5dtB8zlccE6RlN0dGbM'.
+ 'mDVbd5KupNBcL6+F82XgHouLj5vRP2PWLGNdd4+ppnxe8tJec6'.
+ 'XnNsKkm0uVQ5RDRHQTPTym68nPlZbvkfYCexsa5rpJ2qXa5Umm'.
+ 'ocmec3m8vHjmSs+fgxyhC5JDQ8WSPT2lvbzm8vDIe0nbtiBLN8'.
+ '8BigNdu1B6Lsje+fPbUFMLi5TMfGmvHi/puUAv23q2YCTFNqH5'.
+ 'MvPnSwPh3HasCbm3XUpv+nS5VtrkEkwAANSTpGHdye9PAAAASn'.
+ 'RFWHRzaWduYXR1cmUANGJkODkyYmE4MWZhNTk4MTIyNDJjNjUx'.
+ 'NzZhY2UxMDAzOGFhZjdhZWIyNzliNTM2ZGFmZDlkM2RiNDU3Zm'.
+ 'NlNT9CliMAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bl_purple.png
+//==========================================================
+ $this->imgdata_large[4][0]= 1149 ;
+ $this->imgdata_large[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAACAV'.
+ 'BMVEX/////////7///5///1v//xv//rf//pf//lP//jP//hP//'.
+ 'c///a///Wv//Wvf/Uv//Sv//Qv//Qvf/Off/Mf//Kf//If//If'.
+ 'f/GP//GPf/EP//EPf/CP//CPf/CO//AP//APf3Oe/3Kff3Ke/3'.
+ 'Ie/3GO/3EO/3AO/vSu/vSufvOefvMefvIefvGOfvEOfvCOfvAO'.
+ 'fnUufnSufnMd7nId7nGN7nGNbnEN7nCN7nAN7ejN7ejNbec97e'.
+ 'c9beUtbeQtbeIdbeGNbeENbeCNbeANbWpdbWa9bWQs7WGM7WEM'.
+ '7WCM7WAM7Otc7Orc7OnM7OSsbOIb3OGMbOEMbOCMbOAM7OAMbG'.
+ 'pcbGnMbGe8bGa8bGKbXGEL3GCL3GAL3FucXBu73AvsC/v7+9pb'.
+ '29Ka29GLW9ELW9CLW9AL29ALW5rrm1lLW1e7W1MbW1GKW1EK21'.
+ 'CLW1CK21AK2tjK2thKWtMaWtIaWtGJytCK2tCKWtAK2tAKWlhK'.
+ 'Wle6WlEJylCJylAKWlAJyca5ycGJScEJScCJScAJycAJSUWpSU'.
+ 'UoyUKZSUEIyUCIyUAJSUAIyMUoyMSoyMIYSMEISMCISMAIyMAI'.
+ 'SECHuEAISEAHt7MXt7EHt7CHt7AHt7AHNzKXNzEGtzAHNzAGtr'.
+ 'GGtrEGNrCGtrAGtrAGNjCFpjAGNjAFpaAFpaAFIpZn4bAAAAAX'.
+ 'RSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsS'.
+ 'AdLdfvwAAAAHdElNRQfTAwkRFB0ymoOwAAAB9UlEQVR4nGNgIA'.
+ 'K42hhqGtm5+WFIWClKycvLK6gbuARGoEj4aMjLSElISUir6Tt7'.
+ 'x+aEIWR8leQlwEBSTc/CK7awLguuR0lGQkJMVFRUTFJVzwko1d'.
+ 'oFk9OQl5IQE+Dh5hVR0TV3CkkvbJgyASJjDZIR5GBl5eRX0TH1'.
+ 'DEqrbJ2ypBEspSgvJSXKw8bMxMavbOLoGZNf1TZlybw4oIyfLN'.
+ 'BxotxsLEzsQiaOHkFpBQ2905esrAZK2SpIAaUEuDm5+LTNPAKj'.
+ 'C+pbps1evrIDKGWnLictKSkuLKyoZQyUya9o7Z2+YMXKGUApew'.
+ 'M9PTVdXR0TEwf3wOjUirruafOXL18xFyjl72Kpb25qaurg4REU'.
+ 'EFVe2zJ5zpLlK1aCpbydnZ2dnDwDA6NTopLLeiZNXbB8BcTAyP'.
+ 'TQ0JDg4KCY1NS83JKmiVOBepYvX9UPlAovzEiPSU/LLyior2vq'.
+ 'mjZr3vLlIF01IC+XVhUWFlZW1Lc290ycOGfxohVATSsXx4Oksn'.
+ 'vaWlsb2tq6J0+bM2/RohVA81asbIcEYueU3t7JU6ZNnwNyGkhm'.
+ '+cp5CRCppJnzZ8+ZM3/JUogECBbBIixr8Yqly8FCy8F6ltUgoj'.
+ 'lz7sqVK2ByK+cVMSCDxoUrwWDVysXt8WhJKqG4Y8bcuTP6qrGk'.
+ 'QwwAABiMu7T4HMi4AAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bl_gray.png
+//==========================================================
+ $this->imgdata_large[5][0]= 905 ;
+ $this->imgdata_large[5][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAABO1'.
+ 'BMVEX////////3///39/fv7+/e5+fW3t7Wzs7WxsbG1tbGzsbG'.
+ 'xsbDxMS/v7++wMC+v7+9zsa9xsa9vb29tbW9ra29pa24uLi1xs'.
+ 'a1vb21tbWxtrattbWmpqalra2cra2cpaWcnJycjIyUpaWUnJyU'.
+ 'lJSUjIyMnJyMnJSMlJSMlIyMjJSMjIyElJSElIyEjIyEhIR7jI'.
+ 'x7hIR7hHt7e3t7e3N7e2tzhIRze3tze3Nzc3Nre3trc3Nrc2tr'.
+ 'a2tjc3Njc2tja3Nja2tjY2NjWlpaa2taY2taY2NaY1paWlpaUl'.
+ 'JSY2NSY1pSWlpSWlJSUlJSUkpKWlpKWlJKUlpKUlJKUkpKSkpK'.
+ 'SkJCUlJCUkJCSkpCSkJCQkI5Sko5QkI5Qjk5OUI5OTkxQkIxOT'.
+ 'kxMTkxMTEpMTEhMTEhKSkYISEpy7AFAAAAAXRSTlMAQObYZgAA'.
+ 'AAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdE'.
+ 'lNRQfTAwkRFQfW40uLAAABx0lEQVR4nI3SbXfSMBQA4NV3nce5'.
+ 'TecAHUywRMHSgFuBCFsQUqwBS1OsWQh0GTj//y8wZUzdwQ/efM'.
+ 'tzcm/uuXdj4z9ic/PR9k4qk1qDnf0X2/uZzKt8GaRvSubg4LVp'.
+ 'mkWzCGAT/i3Zsm2XNQHLsm2n2937LaaNnGoJFAEo27B50qN0ay'.
+ 'Wg26lXsw8fP8nmzcJb2CbsnF5JmmCE8ncN404KvLfsYwd7/MdV'.
+ 'Pdgl/VbKMIzbuwVgVZw2JlSKJTVJ3609vWUY957lgAUd1KNcqr'.
+ 'yWnOcOPn8q7d5/8PywAqsOOiVDrn42NFk+HQ7dVuXNYeFdBTpN'.
+ 'nY5JdZl8xI5Y+HXYaTVqEDp1hAnRohZM03EUjMdhn5wghOoNnD'.
+ 'wSK7KiiDPqEtz+iD4ctdyAifNYzUnScBSxwPd6GLfRURW7Ay5i'.
+ 'pS5bmrY8348C5vvUI+TLiIVSJrVA0heK/GDkJxYMRoyfCSmk4s'.
+ 'uWc3yic/oBo4yF374LGQs5Xw0GyQljI8bYmEsxVUoKxa6HMpAT'.
+ 'vgyhU2mR8uU1pXmsa8ezqb6U4mwWF/5MeY8uLtQ0nmmQ8UWYvb'.
+ 'EcJaYWar7QhztrO5Wr4Q4hDbAG/4hfTAF2iCiWrCEAAAAASUVO'.
+ 'RK5CYII=' ;
+
+//==========================================================
+// File: bl_brown.png
+//==========================================================
+ $this->imgdata_large[6][0]= 1053 ;
+ $this->imgdata_large[6][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAABoV'.
+ 'BMVEX////Gzs7GvbXGrZTGpXu9nHO1nHO1nIy9taXGxs7GtaXO'.
+ 'nHPGlFrGjEq9hEq1hEqte0Klczmcazmce1KtnIzGxsbGvb3OlF'.
+ 'LOlFq9hFKte0qcc0KUYzGEWimMc1K9ta3OnGvOnGPWnGO9jFq9'.
+ 'jFKlc0KUazmMYzl7UilzUjGtpZzGxr3GnGPWpWvepXO1hFJ7Wj'.
+ 'FrSiFjUjG1ra3GnHPvxpT/5733zpythFKUa0KEYzlzUilaOSF7'.
+ 'Wjm9jErvvYz/99b///f/78bnrYS1hFqle0p7UjFrSiljQiFCMR'.
+ 'iMhHO9lGvGjFLWnGv/3q3////erXuthEqlc0paQiFKMRhSQin/'.
+ '1qX/997//++cc0pjSilaQilKORhCKRiclIy9pYzGlGPntYT33q'.
+ '3vvZSEWjlSOSE5KRB7c2O1lHutczmthFqte1JrWkqtjGtCKRBa'.
+ 'SjmljGuca0KMYzGMaznOztaclISUYzmEWjFKOSF7a1qEYzFaSi'.
+ 'GUjISEa0pKOSm9vb2llIxaQhg5IQiEc2tzY0paORilnJy1raVS'.
+ 'OSljUkJjWkKTpvQWAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHU'.
+ 'gAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkREiei'.
+ 'zP2EAAAB9UlEQVR4nGWS/VfSUBjHL5QluhhBxtwyWcCus5Blpm'.
+ 'wDC4ONaWXCyBi7RMZmpQ2Bypm9W/byV3cHHo/W88s95/s5z/d5'.
+ 'uwCcCh/4L3zAf+bs0NC588On9QAYGSUuBINk6GI4cmnsBLk8Go'.
+ '1SFEGMkzRzZeLq5JE8FvDHouw1lqXiCZJOcnCKnx4AcP0GBqmZ'.
+ 'mRgRT9MMB4Wbs7cGSXNRik3dnp9fiMUzNCNKgpzN9bsaWaQo9s'.
+ '7dfH7pXiFTZCBU1JK27LmtBO8TDx7mV1eXHqXXyiIUFLWiVzHx'.
+ 'BxcJIvV4/cn6wkqmWOOwmVE3UQOAp6HxRKL5bGPj+VwhUhalFq'.
+ '8alm5vAt+LlySZTsebzcKrraIIW4JqZC3N3ga+1+EQTZKZta1M'.
+ 'pCZCSeDViqVrThsEdsLJZLJYLpZrHVGScrKBvTQNtQHY6XIM02'.
+ 'E6Ik7odRW1Dzy3N28n3kGuB3tQagm7UMBFXI/sATAs7L5vdbEs'.
+ '8Lycm923NB0j5wMe6KOsKIIyxcuqauxbrmlqyEWfPmPy5assY1'.
+ 'U1SvWKZWom9nK/HfQ3+v2HYZSMStayTNN0PYKqg11P1nWsWq7u'.
+ '4gJeY8g9PLrddNXRdW8Iryv86I3ja/9s26gvukhDdvUQnIjlKr'.
+ 'IdZCNH+3Xw779qbG63f//ZOzb6C4+ofdbzERrSAAAAAElFTkSu'.
+ 'QmCC' ;
+
+//==========================================================
+// File: bl_darkgreen.png
+//==========================================================
+ $this->imgdata_large[7][0]= 1113 ;
+ $this->imgdata_large[7][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAB2l'.
+ 'BMVEX////////3///v///n/+/e99bW/+/W99bO786/v7++vr69'.
+ '/96999a7wb24vbu1/9a1zqW1u7itxrWosq6l772l1qWlxrWlxq'.
+ '2lva2cxpSU562U3q2UxqWUvaWUpZyM77WM57WMvYyMtZyMrZyM'.
+ 'pZSMnJSEvZyEtYyErZSElIx7zpR7xpx7xpR7vZR7jIRz1pRzxp'.
+ 'RzjIRrzpRrzoxrxoxrtYRrrYxrrXtrpYRrhHNjzoxjxoxjxoRj'.
+ 'vYRjtYRjrXtjpXtjlGNje2tazoxazoRaxoxaxoRavYRatYRatX'.
+ 'tarXtapXNanHNajFpae2tSzoRSxoRSvXtStXtSrXtSrXNSpXNS'.
+ 'nHNSnGtSlGtSlGNSjGtSjGNKvXtKtXNKrXNKpWtKnGtKlGNKjG'.
+ 'NKhGNKhFJKc1pKa1JCrWtCpWtCnGtClGNCjGNCjFpChFpCe1JC'.
+ 'a1JCY1I5pWs5nGM5lGM5jFo5hFo5e1o5c0o5WkoxjFoxhFoxhF'.
+ 'Ixe1Ixc1Ixc0oxa0ophFIpe0opc0opa0opa0IpY0IpWkIpWjkp'.
+ 'UkIpUjkhc0oha0IhY0IhWjkhWjEhUjkhUjEhSjEhSikhQjEhQi'.
+ 'kYWjkYSjEYSikYQjEYQikQSikQQikQQiEQOSExf8saAAAAAXRS'.
+ 'TlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAd'.
+ 'LdfvwAAAAHdElNRQfTAwkRFCaDkWqUAAAB+ElEQVR4nI3S+1vS'.
+ 'UBgHcGZlPV0ks/vFrmQWFimJjiwiYUJWjFBWFhClyZCy5hLrwA'.
+ 'x2EIwJC1w7zf2vnU0re+iHvs9++7x7zznvORbLf+TA6ct9fYMX'.
+ 'jrfAUYefpp+/iM1ykxf/lmuhUZ/PTwXC8dml5Wcd23o5H5Mk6b'.
+ '5NUU8icXbhS67rNzn9JDnguOEYGQtEEtwC+Crs3RJ76P5A/znr'.
+ 'vsNX7wQnEiwHCtK7TTkW8rvdZ9uJtvZTLkxpHhSrP66bNEj7/P'.
+ '3WNoLYeeSWQQCIpe9lQw7RNEU5rDsIYtcJ14Nocg7kRUlBNkxn'.
+ 'YmGKcp7cv3vPwR7XOJPmc0VYU3Sv0e9NOBAYG7Hbz/cMjTMveZ'.
+ 'CHkqxuTBv0PhYJB4N3XR6PJ5rMAPMnpGUxDX1IxSeMTEaZp1OZ'.
+ 'nGAIQiYtsalUIhFlmGTy3sO3AizJCKn6DKYryxzHsWyaneMzr6'.
+ 'cWxRVZVlFTe4SpE3zm+U/4+whyiwJcWVMQNr3XONirVWAklxcE'.
+ 'EdbqchPhjhVzGpeqhUKhWBQhLElr9fo3pDaQPrw5xOl1CGG1JE'.
+ 'k1uYEBIVkrb02+o6RItfq6rBhbw/tuINT96766KhuqYpY3UFPF'.
+ 'BbY/19yZ1XF1U0UNBa9T7rZsz80K0jWk6bpWGW55UzbvTHZ+3t'.
+ 'vbAv/IT+K1uCmhIrKJAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bl_green.png
+//==========================================================
+ $this->imgdata_large[8][0]= 1484 ;
+ $this->imgdata_large[8][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMMFjM4kcoDJQAABVlJREFUeNq9ll2MJFUVx3/11V'.
+ 'Vd/TE9vU0v4zLDwJIF16jBqLAPhsRXEiDqg0QTJiQSjcSNvCzw'.
+ 'sBEDDxizhvAAxBgf1oR9QF9NiE9ESFZkQyZB5WtddmdnZ3qqqr'.
+ 'uqbt367Cofqu3ZZpWVaDzJfbkf53//55z/PVdZXV3l/2H6f7Lp'.
+ '5VdOV/4Nb+GmHpUeA7AdBNxc3kafNb73jRPK9Xwon8ToxVefqU'.
+ 'b91wibH5EkCQBCizFihTSviHUHR0hWws9xe3wvJ7/7nPKpgX5y'.
+ '9oFqt3eOgWniRBoAbUBGGqZUibSYaeoT2B5bnkdaSA6793Cv/S'.
+ 'QPPbihXBfo5VdOV+8dfgnvwAU62YH5fCZ12sDujFkwyegCqTrB'.
+ 'iUOKTOJKj8jr88jS8zy6cXwBTP048nuHX0I0nDlIp7RpTG7kM0'.
+ 'sdyAYsTVukUuWGhlWHMq0ITL92lnUp9R1Obz/GmTNnqn9bDD8/'.
+ '+0D1oX0O0zQZZDYCsK2j3Gl9jQqDfHiei8GfiKVLlsZkJaBAN1'.
+ '0i6PgwUbB0GxG5/PrtE/xLRr959Znqw9452oVNI+jiJhnr1pe4'.
+ 'k29zB1/nFr5Kj7tpt1YYhJ0FJ7nUYbcJQBgahN2MzeCP/OipR6'.
+ 'prgN6Qr6ELFQFUWoRpNVjlKwxZB8DCpE+PtfEKqV1cUzxpVudu'.
+ 'GTBHA5Y1g99e+dUio9O/P1Vpq+/WE5GGjDSMoAtAQjrf3C52IP'.
+ 'QxpY4WK2hpReka9Gfrhqgz0bACRoCWjDh56kQ1z9FeuUUQxVhK'.
+ 'B92sD1VahM+bAJgcoJhGjP/6Ln8rAgDiRCVRKiIzxMkkodBJ85'.
+ 'im1IlEHbE4k1xyNveL4YP8HarmGJIOpqyjeQmfNHmTvnqZTWBt'.
+ 'vIJXpPwlukJSuSTKGK3pEwtJmiX00ZlInTyNscImO6XBITvH1c'.
+ '8vVt2OucdKvIyeKRTNCivsEMgcpg6taYs30nfq0Gqg6hOSSFJ4'.
+ 'BSnJPht0IqEjWmOGocEI6F0J94F0qaL6BntTF0MtUfweKQKAPU'.
+ 'Wwp4OcVnQAmVb0p9DLOzjEhEKnGRmoRc7EzRGlwA6NujAKG4yP'.
+ '6Sjwc4aVznZ7DK0xXdkDoJf0kGmFBniFBOBGcZSCCSKd0IwN0k'.
+ 'IS+QZWCGVZex4BnUxya3+Zt9iugQbcRFpIAtuHvAZulPUdLhUJ'.
+ 'RqegI3WcqaSXddlT3idsWMSRRGkEtNwmyTifAwyBo7LP+11J0e'.
+ '7tM7pZOYblHkBLcqZ5LcYtw6Wbd4CM3SpE9foYZsIHoqDKCrbz'.
+ 'mLSQtPwmuhXgtBLs0GBdbXOhFGB7WBKO2F8GXt9/VO97Ya3atF'.
+ '7nUHnwGjGGQqcPxFEdFqURkEidiZszAERoYIsGju1hq21kWee3'.
+ 'bw15+8WpsvAy3K1+i3JkkhZyPpxxjjPOsfOYiZ+TFhLPzQnHOU'.
+ 'tpzGB2dgA4tscIkKIx19Cxg/fPL7vQJu47eXt1VvsDK8pwPueZ'.
+ 'PuZoQMOqhRoJHSs0kKLBWjvjYinmeQGw1TaX1RFdfZ3LMzYLjA'.
+ 'C++dkn6AaH2Nobk6cxEzdnuG0TdC8zvdJkN0hqkFkO/jwL0fxa'.
+ 'so8sBcuFzQ+/+MRC+BeAHnpwQzn++ee5KT9Eshuy46dcKAXm32'.
+ '0uzPQhS4GttkH2GQID2Wc0Y4LtAbDxhZ/x5A+e/uTG9+jGceXH'.
+ '9/ySnnIXnUzOxXe1038mW3ZynNmam4yYWkO+f9cv+Oljz16/lV'.
+ '9tDz/9nerc1hm8ZEScSRK7VvtYl1i1dklsOKyvc+zg/bzw1O8+'.
+ '/efkajt56kR1ydlEJBc5H46xzbrJ3dY9wrB7hGcff+6/+279L+'.
+ '0fHxyiE8XMLl4AAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bl_blue.png
+//==========================================================
+ $this->imgdata_large[9][0]= 1169 ;
+ $this->imgdata_large[9][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAACEF'.
+ 'BMVEX/////////7//35//v1v/exv/Wvf/Wrf/Wpf/Orf+/v7+9'.
+ 'tc69jP+9hP+5ucW1tc6tlP+rq7Wlpdalpcalpb2cnM6cnMacc/'.
+ '+cWv+UlLWUjN6UjK2Uc/+Ma/+MUv+EhKWEa/+EQvd7e8Z7e7V7'.
+ 'e6V7c957Wv9za9Zza8ZzSv9ra5xrSv9rOf9rMe9jUudjQv9jOe'.
+ '9aWpRaUt5aUpRaSu9aSudSUoxSSs5SSoxSMf9KQtZKOfdKMedK'.
+ 'Kf9KKe9CKf9CKb1CKa1CIfdCIedCId45MXs5Kfc5If85Iec5Id'.
+ 'Y5GP8xMbUxMXsxKc4xKZQxIf8xGP8xGO8xGN4xGNYxGL0xGK0p'.
+ 'KXMpIYwpGP8pGO8pGOcpGNYpGM4pEP8pEPcpEOcpEN4pENYpEM'.
+ 'YpEL0hGKUhEP8hEPchEO8hEOchEN4hENYhEM4hEMYhELUhCP8h'.
+ 'CO8hCN4YGJwYGGsYEL0YEK0YEHMYCN4YCM4YCMYYCL0YCKUYAP'.
+ '8QEJQQEIwQEHsQEGsQCM4QCLUQCK0QCKUQCJwQCJQQCIwQCHMQ'.
+ 'CGsQAP8QAPcQAO8QAOcQAN4QANYQAM4QAMYQAL0QALUQAKUQAJ'.
+ 'QQAIQICGsICGMIAO8IANYIAL0IALUIAK0IAKUIAJwIAJQIAIwI'.
+ 'AIQIAHsIAHMIAGsIAGMAAN4AAMYAAK0AAJQAAIwAAIQAAHMAAG'.
+ 'sAAGMAAFrR1dDlAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkRFRPMOZ'.
+ '/2AAAB+klEQVR4nGNgIAIIqeqZmBqpi2JISNml5lVXV3d198Yo'.
+ 'oUjwm1SnxsbGRsSm5ZfNXO4tjCTjVh0ABhFx6QV9E1Y0S8JkuN'.
+ '3yAgLc7W3t/QPi4jPKJ8ye1yoIlTKpjvVy15eVUbN0i4zKLJ8w'.
+ 'ae6qcKgLqmMj3PUFWFl5NJ0CExLLJzbNW7BWCyxlXR0ba6/Axs'.
+ 'zELmfnkRBT0QiSKgXJCOflxUbYy3KyMHEoOrtEZ1c2TZ6/cMl6'.
+ 'eaCUamdsbIC7tjgPr4SBS3BMMVDTwkXr1hsDpYy6UmMj/O0tdX'.
+ 'QNbDxjknJLWqYsXLx0vStQynxGflpkZGCgs7Onp29SbtNkoMy6'.
+ 'pevCgFJWy3oyMuKjgoKCPWNCvEuqWhcsWrJ06XqQlPnMvrKyrM'.
+ 'TomJjkZAfHlNa2qdOWrlu63gcopbG8v7+hvLwip7g4JdSxsLZu'.
+ '8dKlS9ettwBKic2eNXHChIkTG5tKqgpr2uo6loLAehWQx0LnzJ'.
+ '49p6mpeXLLlNq6RUvqly6dvnR9Bx9ISnnlvLmT582bMr9t4aL2'.
+ '+vrp60GaDCGB6Ld6wfwFCxYCJZYsXQ+SmL6+FBryInVrFi1atH'.
+ 'jJkqVQsH6pNCzCJNvXrQW6CmQJREYFEc2CYevXrwMLAyXXl0oz'.
+ 'IAOt0vVQUGSIkabkDV3DwlzNVDAksAAAfUbNQRCwr88AAAAASU'.
+ 'VORK5CYII=' ;
+
+//==========================================================
+// File: bs_red.png
+//==========================================================
+ $this->imgdata_small[0][0]= 437 ;
+ $this->imgdata_small[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAk1'.
+ 'BMVEX////////GxsbGra3/xsbOhITWhIT/hIT/e3v/c3P/a2vG'.
+ 'UlK1SkrOUlL/Y2PWUlLGSkrnUlLeSkrnSkr/SkqEGBj/KSmlGB'.
+ 'jeGBjvGBj3GBj/EBD/CAj/AAD3AADvAADnAADeAADWAADOAADG'.
+ 'AAC9AAC1AACtAAClAACcAACUAACMAACEAAB7AABzAABrAABjAA'.
+ 'BuukXBAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
+ 'cwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGDNEMgOYAAAAm0'.
+ 'lEQVR4nI3Q3RKCIBAFYGZMy9RKzX7MVUAUlQTe/+kS0K49d3wD'.
+ '7JlFaG+CvIR3FvzPXgpLatxevVVS+Jzv0BDGk/UJwOkQ1ph2g/'.
+ 'Ct5ACX4wNT1o/zzUoJUFUGBiGfVnDTYGJgmrWy8iKEtp0Bpd2d'.
+ 'jLGu56MB7f4JOOfDJAwoNwslk/jOUi+Jts6RVNrC1hkhPy50Ef'.
+ 'u79/ADQMQSGQ8bBywAAAAASUVORK5CYII=' ;
+
+
+//==========================================================
+// File: bs_lightblue.png
+//==========================================================
+ $this->imgdata_small[1][0]= 657 ;
+ $this->imgdata_small[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAABVl'.
+ 'BMVEX////////d///AwMC7wcS08P+y+P+xxdCwxM+uws2twMur'.
+ 'vsinzNynytylzuKhyN6e5v6d5P+d1fOcwNWcu8ub4f+at8iZ3v'.
+ '+ZvdGY2/yW2f+VscGU1vuT1fqTr72Sx+SSxeKR0fWRz/GPz/OP'.
+ 'rr+OyeqMy+6Myu2LyeyKxueJudSGw+SGorGDvt+Cvd6CvN2Aud'.
+ 'p+uNd+t9Z9tdV8tdR8tNN6sc94r813rct2q8h0qcZ0qMVzp8Rx'.
+ 'o8Bwor5tn7ptnrptnrlsnbhqmbRpmbNpi51ol7Flkqtkkqtkka'.
+ 'pjj6hijaRhjaZgi6NfiqJfiaFdh55bhJtag5pZgphYgJZYf5VX'.
+ 'cn9Ve5FSeI1RdopRdYlQdYlPc4dPcoZPcoVNcINLboBLbH9GZn'.
+ 'hGZXdFZHZEY3RDYnJCXW4/W2s/WWg+Wmo7VmU7VGM7U2E6VGM6'.
+ 'VGI5UV82T1wGxheQAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHU'.
+ 'gAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGTok'.
+ '9Yp9AAAAtElEQVR4nGNgIBaw8wkpKghzwvksPAKiUsraprYiLF'.
+ 'ARXkE2JiZ1PXMHXzGIAIekOFBE08TGLTCOCyzCLyvDxsZqZOnk'.
+ 'E56kAhaRV9NQUjW2tPcMjs9wBYsY6Oobmlk7egRGpxZmgkW0zC'.
+ '2s7Jy9giKT8gohaiQcnVzc/UNjkrMLCyHmcHr7BYREJKTlFxbm'.
+ 'QOxiEIuKTUzJKgQCaZibpdOzQfwCOZibGRi4dcJyw3S4iQ4HAL'.
+ 'qvIlIAMH7YAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bs_gray.png
+//==========================================================
+ $this->imgdata_small[2][0]= 550 ;
+ $this->imgdata_small[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAAQCAMAAADH72RtAAABI1'.
+ 'BMVEX///8AAAD8EAD8IAD8NAD8RAD8VAAYGBi/v7+goKCCgoJk'.
+ 'ZGRGRkb8yAD83AD87AD8/AD4+ADo+ADY+ADI+AC0+ACk+ACU+A'.
+ 'CE+AB0/ABk/ABU/ABE/AAw/AAg/AAQ/AAA/AAA+AAA6BAA2CAA'.
+ 'yDQAtEQApFQAlGQAhHQAdIgAZJgAVKgARLgAMMgAINwAEOwAAP'.
+ 'wAAPgIAPAQAOgYAOAkANgsANA0AMg8AMBEALhMALBUAKhcAKBo'.
+ 'AJhwAJB4AIiAAID////4+Pjy8vLs7Ozm5ubg4ODa2trT09PNzc'.
+ '3Hx8fBwcG7u7u1tbWurq6oqKiioqKcnJyWlpaQkJCJiYmDg4N9'.
+ 'fX13d3dxcXFra2tkZGReXl5YWFhSUlJMTExGRkZAQEA1BLn4AA'.
+ 'AAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIA'.
+ 'AAsSAdLdfvwAAAAHdElNRQfTAwkUGiIctEHoAAAAfElEQVR4nI'.
+ '2N2xKDIAwF+bZ2kAa8cNFosBD//yvKWGh9dN+yk9kjxH28R7ze'.
+ 'wzBOYSX6CaNB927Z9qZ66KTSNmBM7UU9Hx2c5qjmJaWCaV5j4t'.
+ 'o1ANr40sn5a+x4biElrqHgrXMeac/c1nEpFHG0LSFoo/jO/BeF'.
+ 'lJnFbT58ayUf0BpA8wAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bs_greenblue.png
+//==========================================================
+ $this->imgdata_small[3][0]= 503 ;
+ $this->imgdata_small[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAxl'.
+ 'BMVEX///////+/v79znJQhSkJ7raU5hHtjraVKnJRCjIRClIyU'.
+ '9++E595avbVaxr2/v7+ctbWcvb17nJxrjIx7paUxQkK9//+Mvb'.
+ '17ra2Evb17tbVCY2MQGBiU5+ec9/eM5+d71tZanJxjra1rvb1j'.
+ 'tbVSnJxara1rzs5jxsZKlJRChIQpUlIhQkJatbVSpaU5c3MxY2'.
+ 'MYMTEQISFavb1Sra1KnJxCjIw5e3sxa2spWlpClJQhSkoYOTkp'.
+ 'Y2MhUlIQKSkIGBgQMTH+e30mAAAAAXRSTlMAQObYZgAAAAFiS0'.
+ 'dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfT'.
+ 'AwkUGTIqLgJPAAAAqklEQVR4nI2QVxOCMBCEM6Mi2OiCvSslJB'.
+ 'CUoqjn//9TYgCfubf9Zu9uZxFqO+rscO7b6l/LljMZX29J2pNr'.
+ 'YjmX4ZaIEs2NeiWO19NNacl8rHAyD4LR6jjw6PMRdTjZE0JOiU'.
+ 'dDv2ALTlzRvSdCCfAHGCc7yRPSrAQRQOWxKc3C/IUjBlDdUcM8'.
+ '97vFGwBY9QsZGBc/A4DWZNbeXIPWZEZI0c2lqSute/gCO9MXGY'.
+ '4/IOkAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bs_yellow.png
+//==========================================================
+ $this->imgdata_small[4][0]= 507 ;
+ $this->imgdata_small[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAzF'.
+ 'BMVEX///////+/v79zYwCMewDOxoTWzoTezkr/5wj/5wDnzgDe'.
+ 'xgC1pQCtnACllACcjACUhABjWgDGvVK1rUrOxlLGvUqEexilnB'.
+ 'jv3hj35xj/7wj/7wD35wDv3gDn1gDezgDWxgDOvQDGtQC9rQCE'.
+ 'ewB7cwBzawBrYwDWzlLn3lLe1krn3kre1hi9tQC1rQCtpQClnA'.
+ 'CclACUjACMhAD/9wC/v7///8bOzoT//4T//3v//3P//2v//2Pn'.
+ '50r//0r//yn39xj//xD//wBjYwDO8noaAAAAAXRSTlMAQObYZg'.
+ 'AAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAH'.
+ 'dElNRQfTAwkUGSDZl3MHAAAAqElEQVR4nI3QWRNDMBAA4My09E'.
+ 'IF1SME0VT1okXvM/3//6kEfbZv+81eswA0DfHxRpOV+M+zkDGG'.
+ 'rL63zCoJ2ef2RLZDIqNqYexyvFrY9ePkxGWdpvfzC7tEGtIRly'.
+ 'nqzboFKMlizAXbNnZyiFUKAy4bZ+B6W0lRaQDLmg4h/k7eFwDL'.
+ 'OWIky8qhXUBQ7gKGmsxpC+ah1TdriwByqG8GQNDNr6kLjf/wAx'.
+ 'KgEq+FpPbfAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bs_darkgray.png
+//==========================================================
+ $this->imgdata_small[5][0]= 611 ;
+ $this->imgdata_small[5][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAMAAAAMCGV4AAABJl'.
+ 'BMVEX////////o8v/f6O7W4OnR3PXL1OTL0evEyLvCzePAwMC/'.
+ 'v7a8wsq7t7C1xum1vtS1q6GzopmyxeKsrsOqvNWoq7anvN+nsb'.
+ 'qhrcGgqbGfpq6cp7+bqMuVmJKRm7yPlKKMnL6FkKWFipOEkLSE'.
+ 'j6qEhoqAiaB+jqd8haF7hZR4iJt4g5l3hZl2gIt2cod1hJVzeY'.
+ 'VzboJvhp9sfJJsb41peY1pd5xpdoVod4xndI5lcHxka4BjcYVg'.
+ 'Z3BfboFbb4lbZnZbYntaZ4laZYVZV3JYYWpXX3JWWm5VX4RVW2'.
+ 'NUYX9SXHxPWn5OVFxNWWtNVXVMVWFKV3xHUGZGU3dGTldFSlxE'.
+ 'Sk9ESXBCRlNBS3k/SGs/RU4+R1k9R2U6RFU2PUg0PEQxNU0ECL'.
+ 'QWAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAA'.
+ 'CxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGQmbJetrAAAAtklEQV'.
+ 'R4nGNgwAK4JZTNNOWlYDxhMT4ZDTOzQE1uMF9CiJWVU0LbxDlS'.
+ 'G8QVF+FnZ2KRNHAIiPUHaZGSlmZj5lH19A1KjLUA8lXU5MWllF'.
+ 'yjo30TYr2BfG19G11b37CEeN84H38gX1HbwTUkOjo+zjfG3hLI'.
+ 'l1exCvCNCwnxjfMz0gTyRdXNHXx9fUNCQu2MwU6SN3ZwD42LCH'.
+ 'W30IK4T8vUJSAkNMhDiwPqYiktXWN9JZj7UQAAjWEfhlG+kScA'.
+ 'AAAASUVORK5CYII=' ;
+
+
+//==========================================================
+// File: bs_darkgreen.png
+//==========================================================
+ $this->imgdata_small[6][0]= 666 ;
+ $this->imgdata_small[6][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAABX1'.
+ 'BMVEX////////l/+nAwMC86r+8wb28wby8wLy78sCzw7SywrSx'.
+ 'wLKwvrGuvK+syK+ryq2rx62n36ym3aumxKmk2qij0Keh16ahva'.
+ 'Og1aSguKKe06KeuaCetZ+d0KGdtZ+bz6Cay56ZyZ2Zwp2Zr5qZ'.
+ 'rpqYwJuXyZuXrJmVw5mUxZiTxJeTw5eTq5WRwJWPtJKOvZKKuI'.
+ '6Kt42Kn4yJt42ItIuGsomFsYmEsIiEr4eDr4eBrIR/qoN+qIJ8'.
+ 'poB7pH56o356on14nnt2nXl0mndzmnZzmXZymHVwlXNvlHJukn'.
+ 'FtiHBqjm1qjW1oi2toiWpniWplh2hlhmdkhWdig2VggGNgf2Je'.
+ 'fmFdfGBde19bbl1aeFxXdFpWclhVclhVcVdUcFZTb1VSbVRQal'.
+ 'JPaVFKY0xKYkxJYUtIYEpHX0lEWkZCWERCV0NCVkM/U0A+U0A+'.
+ 'UUA+UEA9Uj89UT48Tj45TDvewfrHAAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
+ 'RQfTAwkUGRjxlcuZAAAAtElEQVR4nGNgIBZw8osqqIpzw/msfI'.
+ 'IiUmr6lo6SbFARASEOJiYtQ2uXADmIAJeEGFBE18LBMySBBywi'.
+ 'LC/LwcFiZuvmH5WiAxZR0tRW1DC3dfYJS8zyAouYGBibWtm7+o'.
+ 'TEpZfkgEX0rG3snNx9Q2NSCksgaqRd3Ty8gyLiU/NKSiDmcPsF'.
+ 'BodHJ2UUlZTkQ+xikIlNSE7LLgECZagL2VQyc0H8YnV2uD94jS'.
+ 'ILIo14iQ4HALarJBNwbJVNAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bs_purple.png
+//==========================================================
+ $this->imgdata_small[7][0]= 447 ;
+ $this->imgdata_small[7][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAnF'.
+ 'BMVEX///////+/v7/Gvca9rb3Grcb/xv+1hLWte629hL21e7XG'.
+ 'hMbWhNbOe87We9b/hP//e/97OXv/c///a///Y/+cOZz/Sv/WOd'.
+ 'bnOefvOe//Kf9jCGNrCGv/EP//CP/nCOf/AP/3APfvAO/nAOfe'.
+ 'AN7WANbOAM7GAMa9AL21ALWtAK2lAKWcAJyUAJSMAIyEAIR7AH'.
+ 'tzAHNrAGtjAGPP1sZnAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF'.
+ 'HUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGS'.
+ 'o5QpoZAAAAnElEQVR4nI3Q2xJDMBAG4MyQokWrZz3oSkJISJH3'.
+ 'f7dK0Gv/Xb7J7vyzCK0NjtPsHuH/2wlhTE7LnTNLCO/TFQjjIp'.
+ 'hHAA6bY06LSqppMAY47x+04HXTba2kAFlmQKr+YuVDCGUG2k6/'.
+ 'rNwYK8rKwKCnPxHnVS0aA3rag4UQslUGhrlk0Kpv1+sx3tLZ6w'.
+ 'dtYemMkOsnz8R3V9/hB87DEu2Wos5+AAAAAElFTkSuQmCC' ;
+
+
+//==========================================================
+// File: bs_brown.png
+//==========================================================
+ $this->imgdata_small[8][0]= 677 ;
+ $this->imgdata_small[8][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAABaF'.
+ 'BMVEX//////////8X/3oD/3nj/1HX/0Gr/xGP/rkv/gBf+iS/2'.
+ 'bAL1agDxaQDuZwDrZwLpZQDmZQLlZADjcx7gZATeYQDdZgraXw'.
+ 'DZXwHYXgDXiEvXZAvUjlfUXwXTjVfTbR7ShUvRbR7RWwDMWQDL'.
+ 'WADKooLKWADJoYLJgkvHWATGoILFn4LFgEvFVgDEZx7EVQDDt6'.
+ '/DVQDCt6/CnoLChlfCVADAwMC+hFe+UgC8UgC6UQC4gVe4UAC3'.
+ 'gVe3UAC1gFe1eUu1TwC1TgCzTgCwTQKuTACrSgCqSgCpSgCpSQ'.
+ 'CodEulSACkRwCiRgCdRACcRACaQwCYQgCWQgKVQQCVQACUQACS'.
+ 'UR6RPwCOPgCNPQCLPACKPACJOwCEOQCBOAB+NwB9NgB8NgB7NQ'.
+ 'B6NwJ4NAB3RR52MwB0MgBuLwBtLwBsLwBqLgBpLQBkLQJiKgBh'.
+ 'KgBgKwRcKABbKQJbJwBaKQRaJwBYKAJVJQDZvdIYAAAAAXRSTl'.
+ 'MAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLd'.
+ 'fvwAAAAHdElNRQfTAwkUGho0tvl2AAAAtklEQVR4nGNgIBaoSg'.
+ 'mLKGpowfkGMty8AqJKpi4mRlAROR5ONg4JFUv3YHOIgDo/HwsT'.
+ 'q6yps29EsjZYREFIkJ2ZS9/OMzA20wEsIi8uKSZtaOPmH5WSFw'.
+ 'YW0VRW07Vw8vCLSMguLwCL6FlaObp6B0TGZxSXQ9TouHv6+IXG'.
+ 'JGYWlpdDzNEKCgmPjkvLKS0vL4LYxWAen5SelV8OBNZQFxrZ5h'.
+ 'aC+GX2MDczMBh7pZakehkTHQ4AA0Am/jsB5gkAAAAASUVORK5C'.
+ 'YII=' ;
+
+//==========================================================
+// File: bs_blue.png
+//==========================================================
+ $this->imgdata_small[9][0]= 436 ;
+ $this->imgdata_small[9][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAk1'.
+ 'BMVEX///////+/v7+trcbGxv+EhM6EhNaEhP97e/9zc/9ra/9S'.
+ 'UsZKSrVSUs5jY/9SUtZKSsZSUudKSt5KSudKSv8YGIQpKf8YGK'.
+ 'UYGN4YGO8YGPcQEP8ICP8AAP8AAPcAAO8AAOcAAN4AANYAAM4A'.
+ 'AMYAAL0AALUAAK0AAKUAAJwAAJQAAIwAAIQAAHsAAHMAAGsAAG'.
+ 'ONFkFbAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
+ 'cwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGhNNakHSAAAAmk'.
+ 'lEQVR4nI3P2xKCIBAGYGfM6SBWo1nauIqogaDA+z9dK9Lhrv47'.
+ 'vtl/2A2CfxNlJRRp9IETYGraJeEb7ocLNKznia8A7Db7umWDUG'.
+ 'sxAzhurxRHxok4KQGqCuEhlL45oU1D2w5BztY4KRhj/bCAsetM'.
+ '2uObjwvY8/oX50JItYDxSyZSTrO2mNhvGMbaWAevnbFIcpuTr7'.
+ 't+5AkyfBIKSJHdSQAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bs_green.png
+//==========================================================
+ $this->imgdata_small[10][0]= 452 ;
+ $this->imgdata_small[10][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAn1'.
+ 'BMVEX///////+/v7+/v7/G/8aUxpSMvYyUzpSMzoyM1oxarVqE'.
+ '/4R7/3tavVpKnEpaxlpz/3Nr/2tKtUpj/2Na51pKzkpK1kpK50'.
+ 'pK/0oYcxgp/ykYlBgY3hgY7xgY9xgQ/xAI/wgA/wAA9wAA7wAA'.
+ '5wAA3gAA1gAAzgAAxgAAvQAAtQAArQAApQAAnAAAlAAAjAAAhA'.
+ 'AAewAAcwAAawAAYwA0tyxUAAAAAXRSTlMAQObYZgAAAAFiS0dE'.
+ 'AIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAw'.
+ 'kUGgW5vvSDAAAAnklEQVR4nI3QSxKCMAwA0M4gqCgoiiJ+kEAL'.
+ 'LQUq0PufzX7ENdnlJZNkgtDS2CYZvK6bf+7EoKLA9cH5SQzv6A'.
+ 'YloTywsAbYr44FrlgrXCMJwHl3xxVtuuFkJAPIcw2tGB9GcFli'.
+ 'oqEf5GTkSUhVMw2TtD0XSlnDOw3SznE5520vNEi7CwW9+Ayjyq'.
+ 'U/3+yPuq5gvhkhL0xlGnqL//AFf14UIh4mkEkAAAAASUVORK5C'.
+ 'YII=' ;
+
+
+//==========================================================
+// File: bs_white.png
+//==========================================================
+ $this->imgdata_small[11][0]= 480 ;
+ $this->imgdata_small[11][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAAQCAYAAADwMZRfAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMLFTsY/ewvBQAAAW1JREFUeJytkz2u4jAUhT/jic'.
+ 'gfBUKiZhE0bIKeVbCWrIKenp6eDiGlCEEEBArIxvzGU4xeZjLk'.
+ 'jWb05lRXuvbx+exr4bouX1Xjyw7Atz81F4uFBYjjGIDhcCjq1o'.
+ 'k6nN1uZwFerxfP55Msy1itVmRZBsB4PK6YveHkeW5d18XzPIIg'.
+ 'wPd9Wq0WnU6HMAxJkoQoiuynOIfDwUopkVIihKAoCgAcx6Hdbm'.
+ 'OMIU1T5vN55eBKEikljUYDIX6kFUKU9e8aDAZlmjcca+1b7TgO'.
+ '1+uVy+VS9nzfr8e53++VzdZaiqIgz3OMMWitOZ/PaK0JgqDeRC'.
+ 'mF53lIKYGfr3O73TDGoJQiTVO01nS73XqT4/FIs9kkCAIej0eZ'.
+ 'brPZEMcxSZKgtQZgMpmIWpN+vy+m06n1PK9yTx8Gy+WS/X5Pr9'.
+ 'er9GuHLYoiG4YhSilOpxPr9Zrtdlti/JriU5MPjUYjq7UuEWaz'.
+ '2d+P/b/qv/zi75oetJcv7QQXAAAAAElFTkSuQmCC' ;
+
+
+//==========================================================
+// File: bs_cyan.png
+//==========================================================
+ $this->imgdata_small[12][0]= 633 ;
+ $this->imgdata_small[12][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAABPl'.
+ 'BMVEX////////F///AwMCvxsaC1NSC0dGCz8+CzMyA//94//91'.
+ '//9q//9j//9X4uJX09NXz89Xx8dXxMRL//9L5uZL3d1L2NhLxs'.
+ 'ZLt7cv//8e9fUe8fEe7u4e398epqYehoYX//8L+PgK//8F9fUE'.
+ '/v4E5+cEb28EZ2cC//8C/v4C/f0CzMwCrq4Cjo4CdXUCaWkCZW'.
+ 'UB/PwA//8A/f0A+/sA8/MA7e0A7OwA6+sA5eUA5OQA4uIA4eEA'.
+ '3NwA2toA2NgA1dUA09MA0tIA0NAAysoAxsYAxcUAxMQAv78Avr'.
+ '4AvLwAtrYAtbUAs7MAsLAAra0Aq6sAqKgApaUApKQAoqIAoKAA'.
+ 'n58AmpoAlZUAk5MAkpIAkJAAj48AjIwAiYkAh4cAf38AfX0Ae3'.
+ 'sAenoAcnIAcHAAa2sAaWkAaGgAYmIUPEuTAAAAAXRSTlMAQObY'.
+ 'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
+ 'AHdElNRQfTAwkUGQDi+VPPAAAAtElEQVR4nGNgIBawikipyIiy'.
+ 'wfksfJpGRkamNtr8LFARPiMFHmFDcztXfwGoFi0jLiZuZRtnry'.
+ 'BddrCIiJEGL6eklYO7X3iCOFhE2thESdHawdUnJDZFDiyiamZh'.
+ 'aevk5h0UlZSpBhaRtbN3dPHwDY5MSM+EqBFzc/f0DgiLTkjLzI'.
+ 'SYw6bjHxgaEZeckZmpD7GLQSAqJj4xNRMIBGFuFtRLA/ENhGBu'.
+ 'ZmDgkJBXl5fgIDocAAKcINaFePT4AAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bs_bluegreen.png
+//==========================================================
+ $this->imgdata_small[13][0]= 493 ;
+ $this->imgdata_small[13][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAvV'.
+ 'BMVEX///////+/v79j//855/8x3v851v9Spb1C1v8AOUqEtcZK'.
+ 'lK1StdYxzv8hxv8AY4QASmNSlK1KpcZKtd4YQlIYnM4YrecIvf'.
+ '8AtfcAre8AjL0AhLUAc5wAa5QAWnsAQloAKTkAGCFKhJxKrdYY'.
+ 'jL0Ypd4Atf8ArfcApecAnN4AlM4AjMYAe60Ac6UAY4wAUnNSnL'.
+ '0AlNYAWoQASmsAOVIAITGEtc4YWnsAUnsAMUqtvcaErcYAKUIA'.
+ 'GCkAECHUyVh/AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAA'.
+ 'AJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGxNUcXCT'.
+ 'AAAAqUlEQVR4nI2Q1xKCMBREM2NHLCCogAGCjd6SqLT8/2cZKT'.
+ '6zb3tm987OBWCsXoejp8rC35fi4+l6gXFZlD0Rz6fZ1tdDmKR9'.
+ 'RdOmkzmP7DDpilfX3SzvRgQ/Vr1uiZplfsCBiVf03RJd140wgj'.
+ 'kmNqMtuYXcxyYmNWJdRoYwzpM9qRvGujuCmSR7q7ARY00/MiWk'.
+ 'sCnjkobNEm1+HknDZgAqR0GKU43+wxdu2hYzbsHU6AAAAABJRU'.
+ '5ErkJggg==' ;
+
+//==========================================================
+// File: bs_lightred.png
+//==========================================================
+ $this->imgdata_small[14][0]= 532 ;
+ $this->imgdata_small[14][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAA3l'.
+ 'BMVEX///////+/v7/Gvb0hGBj/5///3v//zu//1u//xucpGCG9'.
+ 'nK21lKVSQkp7Wms5KTExISlaOUpjQlIhEBj/tdbOhKXnrcbGjK'.
+ 'Wla4TetcbGnK2EWmv/rc73pcZ7UmOcY3vOpbW1jJzenLW9e5Rz'.
+ 'Slq1c4xrQlJSOULGhJz/pcb3nL2chIzOnK33rcbelK3WjKWMWm'.
+ 'vGe5SEUmM5ISnOtb3GrbXerb3vpb2ca3v/rcaUY3POhJxCKTF7'.
+ 'SlrWnK21e4ytc4TvnLXnlK2la3taOUK1lJxrSlLGhJRjQkpSMT'.
+ 'lw+q2nAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
+ 'cwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGjoP2Nm+AAAAr0'.
+ 'lEQVR4nGNgIBaYiOk62imYwPnMkiIyso76yhJSzFARMxkRNk49'.
+ 'a3t5OW6oFk1LVkYOfWUHKxUXiEYzLS12DnN3VXkjIRtFsIiSk5'.
+ '6evqGqhYGKugAfWMRa1FpD2UHeQEXQRlgALCJur+rgbCUNFOAS'.
+ 'hqjRkZe3MpBTcwEKCEPMMTGSs3Xz8OQHCnBBHckt6OJpIyAMBD'.
+ 'wwN/MYc4H4LK4wNzMwmGrzcvFqmxIdDgDiHRT6VVQkrAAAAABJ'.
+ 'RU5ErkJggg==' ;
+
+//==========================================================
+// File: bxs_lightred.png
+//==========================================================
+ $this->imgdata_xsmall[0][0]= 432 ;
+ $this->imgdata_xsmall[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAA3l'.
+ 'BMVEX///////+/v7/Gvb0hGBj/5///3v//zu//1u//xucpGCG9'.
+ 'nK21lKVSQkp7Wms5KTExISlaOUpjQlIhEBj/tdbOhKXnrcbGjK'.
+ 'Wla4TetcbGnK2EWmv/rc73pcZ7UmOcY3vOpbW1jJzenLW9e5Rz'.
+ 'Slq1c4xrQlJSOULGhJz/pcb3nL2chIzOnK33rcbelK3WjKWMWm'.
+ 'vGe5SEUmM5ISnOtb3GrbXerb3vpb2ca3v/rcaUY3POhJxCKTF7'.
+ 'SlrWnK21e4ytc4TvnLXnlK2la3taOUK1lJxrSlLGhJRjQkpSMT'.
+ 'lw+q2nAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
+ 'cwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUKBOgGhWjAAAAS0'.
+ 'lEQVR4nGNgQAEmunYmEJaMCKe1vBxYzJKVQ9lKBSSupKdnaKGi'.
+ 'zgdkiqs6WKnYcIGYJnK2HvzCwmCNgi42wsLCECNMeXlNUY0HAL'.
+ 'DaB7Du8MiEAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bxs_bluegreen.png
+//==========================================================
+ $this->imgdata_xsmall[1][0]= 397 ;
+ $this->imgdata_xsmall[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAvV'.
+ 'BMVEX///////+/v79j//855/8x3v851v9Spb1C1v8AOUqEtcZK'.
+ 'lK1StdYxzv8hxv8AY4QASmNSlK1KpcZKtd4YQlIYnM4YrecIvf'.
+ '8AtfcAre8AjL0AhLUAc5wAa5QAWnsAQloAKTkAGCFKhJxKrdYY'.
+ 'jL0Ypd4Atf8ArfcApecAnN4AlM4AjMYAe60Ac6UAY4wAUnNSnL'.
+ '0AlNYAWoQASmsAOVIAITGEtc4YWnsAUnsAMUqtvcaErcYAKUIA'.
+ 'GCkAECHUyVh/AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAA'.
+ 'AJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUKDVyF5Be'.
+ 'AAAASUlEQVR4nGNgQAFmYqJcEJaEOJ+UrD5YTJKFTZrfGCQuaq'.
+ 'glLWvMaQ5kqujo6hnbKIKYXPr68gp2dmCNJiZAlh3ECGsREWtU'.
+ '4wF1kwdpAHfnSwAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bxs_navy.png
+//==========================================================
+ $this->imgdata_xsmall[2][0]= 353 ;
+ $this->imgdata_xsmall[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAk1'.
+ 'BMVEX///////+/v7+trcbGxv+EhM6EhNaEhP97e/9zc/9ra/9S'.
+ 'UsZKSrVSUs5jY/9SUtZKSsZSUudKSt5KSudKSv8YGIQpKf8YGK'.
+ 'UYGN4YGO8YGPcQEP8ICP8AAP8AAPcAAO8AAOcAAN4AANYAAM4A'.
+ 'AMYAAL0AALUAAK0AAKUAAJwAAJQAAIwAAIQAAHsAAHMAAGsAAG'.
+ 'ONFkFbAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
+ 'cwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUJxXO4axZAAAAR0'.
+ 'lEQVR4nGNgQAGskhKsEJaslIi8ijpYTJaDU1FVAyQuKSujoKKh'.
+ 'LQ5kSigpqWro6oOYrOoaWroGBmCNWiCWAdQwUVFWVOMBOp4GCJ'.
+ 's5S60AAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bxs_gray.png
+//==========================================================
+ $this->imgdata_xsmall[3][0]= 492 ;
+ $this->imgdata_xsmall[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABI1'.
+ 'BMVEX///8AAAD8EAD8IAD8NAD8RAD8VAAYGBi/v7+goKCCgoJk'.
+ 'ZGRGRkb8yAD83AD87AD8/AD4+ADo+ADY+ADI+AC0+ACk+ACU+A'.
+ 'CE+AB0/ABk/ABU/ABE/AAw/AAg/AAQ/AAA/AAA+AAA6BAA2CAA'.
+ 'yDQAtEQApFQAlGQAhHQAdIgAZJgAVKgARLgAMMgAINwAEOwAAP'.
+ 'wAAPgIAPAQAOgYAOAkANgsANA0AMg8AMBEALhMALBUAKhcAKBo'.
+ 'AJhwAJB4AIiAAID////4+Pjy8vLs7Ozm5ubg4ODa2trT09PNzc'.
+ '3Hx8fBwcG7u7u1tbWurq6oqKiioqKcnJyWlpaQkJCJiYmDg4N9'.
+ 'fX13d3dxcXFra2tkZGReXl5YWFhSUlJMTExGRkZAQEA1BLn4AA'.
+ 'AAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxEA'.
+ 'AAsRAX9kX5EAAAAHdElNRQfTAwkUKC74clmyAAAAQklEQVR4nG'.
+ 'NgQAVBYVCGt5dXYEQ0mOnp5h4QFgVmeri6+4dHxYMVeHoFRUTH'.
+ 'gTUFBIZBWAwMkZEx8bFQM2Lj0UwHANc/DV6yq/BiAAAAAElFTk'.
+ 'SuQmCC' ;
+
+//==========================================================
+// File: bxs_graypurple.png
+//==========================================================
+ $this->imgdata_xsmall[4][0]= 542 ;
+ $this->imgdata_xsmall[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABSl'.
+ 'BMVEX////////11P/MqdvKrNfAwMC+u7+9u7+4rr24lsi3rby3'.
+ 'lMe1rLq1o720q7i0oL20ksSzoryyqbaykMGxlb2wkL+vnbiujb'.
+ '2sjLuri7qpl7GoirWoibenmK2mla6mjLKmhrSllauki7CjhrCj'.
+ 'hLGihLChg6+ggq2fkqadkKOcfqqai6Gag6WYe6WXeqSWeaOTd6'.
+ 'CTd5+Rdp6RdZ6RdZ2Qg5eOc5qMcpiLcZeJb5WIbpOHbZKGbJGE'.
+ 'a4+CaY2AZ4t/Z4p/Zop/Zol+Zol7ZIZ6Y4V5YoR1ZH11X391Xn'.
+ '9zXX1yXXtxXHtvWnluWXhsV3VqVnNpVXJoVHFnU3BmUm9jUGth'.
+ 'VGdgTmheTGZcS2RcSmRaSWJYR19XRl5SQllRQlhQQVdPQFZOP1'.
+ 'VLPlFJO09IPE5IOk5FOEtEN0lDOEpDOElDNklCNkc/M0XhbrfD'.
+ 'AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACx'.
+ 'EAAAsRAX9kX5EAAAAHdElNRQfTAwkUKCgREfyHAAAATUlEQVR4'.
+ 'nGNgQAEcIko8EBY3M5Ougy+IxSXMwmTsFsAHZMqrSRvZB0W7A5'.
+ 'k6FlYugXEZICaPr394Um4uSAFDRFRCbm4uxAihsDAhVOMBHT0L'.
+ 'hkeRpo8AAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bxs_red.png
+//==========================================================
+ $this->imgdata_xsmall[5][0]= 357 ;
+ $this->imgdata_xsmall[5][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAk1'.
+ 'BMVEX////////GxsbGra3/xsbOhITWhIT/hIT/e3v/c3P/a2vG'.
+ 'UlK1SkrOUlL/Y2PWUlLGSkrnUlLeSkrnSkr/SkqEGBj/KSmlGB'.
+ 'jeGBjvGBj3GBj/EBD/CAj/AAD3AADvAADnAADeAADWAADOAADG'.
+ 'AAC9AAC1AACtAAClAACcAACUAACMAACEAAB7AABzAABrAABjAA'.
+ 'BuukXBAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
+ 'cwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUIyjy5SVMAAAAS0'.
+ 'lEQVR4nGNgQAFsUpJsEJastIi8ijpYTJaDU0FVgxXIlJKVUVDR'.
+ '0BYHMiUUlVQ1dPVBTDZ1dS1dAwOQAgYtbSDLAGIEq6goK6rxAD'.
+ 'yXBg73lwGUAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bxs_yellow.png
+//==========================================================
+ $this->imgdata_xsmall[6][0]= 414 ;
+ $this->imgdata_xsmall[6][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAzF'.
+ 'BMVEX///////+/v79zYwCMewDOxoTWzoTezkr/5wj/5wDnzgDe'.
+ 'xgC1pQCtnACllACcjACUhABjWgDGvVK1rUrOxlLGvUqEexilnB'.
+ 'jv3hj35xj/7wj/7wD35wDv3gDn1gDezgDWxgDOvQDGtQC9rQCE'.
+ 'ewB7cwBzawBrYwDWzlLn3lLe1krn3kre1hi9tQC1rQCtpQClnA'.
+ 'CclACUjACMhAD/9wC/v7///8bOzoT//4T//3v//3P//2v//2Pn'.
+ '50r//0r//yn39xj//xD//wBjYwDO8noaAAAAAXRSTlMAQObYZg'.
+ 'AAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAH'.
+ 'dElNRQfTAwkUIzoBXFQEAAAAS0lEQVR4nGNgQAFsDhJsEJaTo5'.
+ '2skj5YzMnSSk7ZwBzIlOSUklPiMxYHMnW4FXT5VNVBTDZeXiNV'.
+ 'QUGQAgYBYyBLEGIEq5gYK6rxAH4kBmHBaMQQAAAAAElFTkSuQm'.
+ 'CC' ;
+
+//==========================================================
+// File: bxs_greenblue.png
+//==========================================================
+ $this->imgdata_xsmall[7][0]= 410 ;
+ $this->imgdata_xsmall[7][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAxl'.
+ 'BMVEX///////+/v79znJQhSkJ7raU5hHtjraVKnJRCjIRClIyU'.
+ '9++E595avbVaxr2/v7+ctbWcvb17nJxrjIx7paUxQkK9//+Mvb'.
+ '17ra2Evb17tbVCY2MQGBiU5+ec9/eM5+d71tZanJxjra1rvb1j'.
+ 'tbVSnJxara1rzs5jxsZKlJRChIQpUlIhQkJatbVSpaU5c3MxY2'.
+ 'MYMTEQISFavb1Sra1KnJxCjIw5e3sxa2spWlpClJQhSkoYOTkp'.
+ 'Y2MhUlIQKSkIGBgQMTH+e30mAAAAAXRSTlMAQObYZgAAAAFiS0'.
+ 'dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfT'.
+ 'AwkUJy5/6kV9AAAATUlEQVR4nGNgQAGCyuyCEJaGugKHviVYzF'.
+ 'hO3sxCWwDIVNLTM9PXtpEGMhW12Cy0DR1ATEFLSxZ7BweQAgYd'.
+ 'HUMHBweIEQKiogKoxgMAo/4H5AfSehsAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bxs_purple.png
+//==========================================================
+ $this->imgdata_xsmall[8][0]= 364 ;
+ $this->imgdata_xsmall[8][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAnF'.
+ 'BMVEX///////+/v7/Gvca9rb3Grcb/xv+1hLWte629hL21e7XG'.
+ 'hMbWhNbOe87We9b/hP//e/97OXv/c///a///Y/+cOZz/Sv/WOd'.
+ 'bnOefvOe//Kf9jCGNrCGv/EP//CP/nCOf/AP/3APfvAO/nAOfe'.
+ 'AN7WANbOAM7GAMa9AL21ALWtAK2lAKWcAJyUAJSMAIyEAIR7AH'.
+ 'tzAHNrAGtjAGPP1sZnAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF'.
+ 'HUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUIj'.
+ 'mBTjT/AAAASUlEQVR4nGNgQAGskhKsEJaCrJiSuhZYTEFASFlD'.
+ 'GyQuqSCnrK6tJwpkiquoamgbGIGYrFpaugbGxmCNunpAljHECB'.
+ 'ZBQRZU4wFSMAZsXeM71AAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bxs_green.png
+//==========================================================
+ $this->imgdata_xsmall[9][0]= 370 ;
+ $this->imgdata_xsmall[9][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAn1'.
+ 'BMVEX///////+/v7+/v7/G/8aUxpSMvYyUzpSMzoyM1oxarVqE'.
+ '/4R7/3tavVpKnEpaxlpz/3Nr/2tKtUpj/2Na51pKzkpK1kpK50'.
+ 'pK/0oYcxgp/ykYlBgY3hgY7xgY9xgQ/xAI/wgA/wAA9wAA7wAA'.
+ '5wAA3gAA1gAAzgAAxgAAvQAAtQAArQAApQAAnAAAlAAAjAAAhA'.
+ 'AAewAAcwAAawAAYwA0tyxUAAAAAXRSTlMAQObYZgAAAAFiS0dE'.
+ 'AIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAw'.
+ 'kUKBrZxq0HAAAATElEQVR4nGNgQAGccrIcEJaivISyhjaIxa7I'.
+ 'I6CiqcMKZMopKqho6OhLA5kyqmqaOobGICartraeoYkJSAGDnj'.
+ '6QZQIxgk1Skg3VeABlVgbItqEBUwAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bxs_darkgreen.png
+//==========================================================
+ $this->imgdata_xsmall[10][0]= 563 ;
+ $this->imgdata_xsmall[10][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABX1'.
+ 'BMVEX////////l/+nAwMC86r+8wb28wby8wLy78sCzw7SywrSx'.
+ 'wLKwvrGuvK+syK+ryq2rx62n36ym3aumxKmk2qij0Keh16ahva'.
+ 'Og1aSguKKe06KeuaCetZ+d0KGdtZ+bz6Cay56ZyZ2Zwp2Zr5qZ'.
+ 'rpqYwJuXyZuXrJmVw5mUxZiTxJeTw5eTq5WRwJWPtJKOvZKKuI'.
+ '6Kt42Kn4yJt42ItIuGsomFsYmEsIiEr4eDr4eBrIR/qoN+qIJ8'.
+ 'poB7pH56o356on14nnt2nXl0mndzmnZzmXZymHVwlXNvlHJukn'.
+ 'FtiHBqjm1qjW1oi2toiWpniWplh2hlhmdkhWdig2VggGNgf2Je'.
+ 'fmFdfGBde19bbl1aeFxXdFpWclhVclhVcVdUcFZTb1VSbVRQal'.
+ 'JPaVFKY0xKYkxJYUtIYEpHX0lEWkZCWERCV0NCVkM/U0A+U0A+'.
+ 'UUA+UEA9Uj89UT48Tj45TDvewfrHAAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElN'.
+ 'RQfTAwkUKCFozUQjAAAATUlEQVR4nGNgQAGcoqrcEJYQB5OhSw'.
+ 'CIxSXGwWThGcIDZCppK5o7hyV6AZl6NnbuoSmFICZ3YHB0RkkJ'.
+ 'SAFDbEJaSUkJxAjeyEheVOMBQj4MOEkWew4AAAAASUVORK5CYI'.
+ 'I=' ;
+
+//==========================================================
+// File: bxs_cyan.png
+//==========================================================
+ $this->imgdata_xsmall[11][0]= 530 ;
+ $this->imgdata_xsmall[11][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABPl'.
+ 'BMVEX////////F///AwMCvxsaC1NSC0dGCz8+CzMyA//94//91'.
+ '//9q//9j//9X4uJX09NXz89Xx8dXxMRL//9L5uZL3d1L2NhLxs'.
+ 'ZLt7cv//8e9fUe8fEe7u4e398epqYehoYX//8L+PgK//8F9fUE'.
+ '/v4E5+cEb28EZ2cC//8C/v4C/f0CzMwCrq4Cjo4CdXUCaWkCZW'.
+ 'UB/PwA//8A/f0A+/sA8/MA7e0A7OwA6+sA5eUA5OQA4uIA4eEA'.
+ '3NwA2toA2NgA1dUA09MA0tIA0NAAysoAxsYAxcUAxMQAv78Avr'.
+ '4AvLwAtrYAtbUAs7MAsLAAra0Aq6sAqKgApaUApKQAoqIAoKAA'.
+ 'n58AmpoAlZUAk5MAkpIAkJAAj48AjIwAiYkAh4cAf38AfX0Ae3'.
+ 'sAenoAcnIAcHAAa2sAaWkAaGgAYmIUPEuTAAAAAXRSTlMAQObY'.
+ 'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAA'.
+ 'AHdElNRQfTAwkUKQFKuFWqAAAATUlEQVR4nGNgQAGsUjJsEJaR'.
+ 'grC5qz9YzIiL28YriB3IlDZRsnYNiZUDMmXtHT2CE9JBTDb/wI'.
+ 'jkzEyQAoaomMTMzEyIERzy8hyoxgMAN2MLVPW0f4gAAAAASUVO'.
+ 'RK5CYII=' ;
+
+//==========================================================
+// File: bxs_orange.png
+//==========================================================
+ $this->imgdata_xsmall[12][0]= 572 ;
+ $this->imgdata_xsmall[12][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABaF'.
+ 'BMVEX//////////8X/3oD/3nj/1HX/0Gr/xGP/rkv/gBf+iS/2'.
+ 'bAL1agDxaQDuZwDrZwLpZQDmZQLlZADjcx7gZATeYQDdZgraXw'.
+ 'DZXwHYXgDXiEvXZAvUjlfUXwXTjVfTbR7ShUvRbR7RWwDMWQDL'.
+ 'WADKooLKWADJoYLJgkvHWATGoILFn4LFgEvFVgDEZx7EVQDDt6'.
+ '/DVQDCt6/CnoLChlfCVADAwMC+hFe+UgC8UgC6UQC4gVe4UAC3'.
+ 'gVe3UAC1gFe1eUu1TwC1TgCzTgCwTQKuTACrSgCqSgCpSgCpSQ'.
+ 'CodEulSACkRwCiRgCdRACcRACaQwCYQgCWQgKVQQCVQACUQACS'.
+ 'UR6RPwCOPgCNPQCLPACKPACJOwCEOQCBOAB+NwB9NgB8NgB7NQ'.
+ 'B6NwJ4NAB3RR52MwB0MgBuLwBtLwBsLwBqLgBpLQBkLQJiKgBh'.
+ 'KgBgKwRcKABbKQJbJwBaKQRaJwBYKAJVJQDZvdIYAAAAAXRSTl'.
+ 'MAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9k'.
+ 'X5EAAAAHdElNRQfTAwkUJBSSy88MAAAATUlEQVR4nGNgQAGqwo'.
+ 'paEBYPJ4eKezCIpc7HwmrqG6ENZMpLihm6RaWEAZl6Vo7ekRnF'.
+ 'IKZWSHhcTnk5SAFDfFJWeXk5xAjj1FRjVOMBeFwNcWYSLjsAAA'.
+ 'AASUVORK5CYII=' ;
+
+//==========================================================
+// File: bxs_lightblue.png
+//==========================================================
+ $this->imgdata_xsmall[13][0]= 554 ;
+ $this->imgdata_xsmall[13][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABVl'.
+ 'BMVEX////////d///AwMC7wcS08P+y+P+xxdCwxM+uws2twMur'.
+ 'vsinzNynytylzuKhyN6e5v6d5P+d1fOcwNWcu8ub4f+at8iZ3v'.
+ '+ZvdGY2/yW2f+VscGU1vuT1fqTr72Sx+SSxeKR0fWRz/GPz/OP'.
+ 'rr+OyeqMy+6Myu2LyeyKxueJudSGw+SGorGDvt+Cvd6CvN2Aud'.
+ 'p+uNd+t9Z9tdV8tdR8tNN6sc94r813rct2q8h0qcZ0qMVzp8Rx'.
+ 'o8Bwor5tn7ptnrptnrlsnbhqmbRpmbNpi51ol7Flkqtkkqtkka'.
+ 'pjj6hijaRhjaZgi6NfiqJfiaFdh55bhJtag5pZgphYgJZYf5VX'.
+ 'cn9Ve5FSeI1RdopRdYlQdYlPc4dPcoZPcoVNcINLboBLbH9GZn'.
+ 'hGZXdFZHZEY3RDYnJCXW4/W2s/WWg+Wmo7VmU7VGM7U2E6VGM6'.
+ 'VGI5UV82T1wGxheQAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHU'.
+ 'gAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUJziL'.
+ 'PvAsAAAATUlEQVR4nGNgQAHsQgqcEJYgG5Oegy+IxSHOxmTiFs'.
+ 'gFZMprKBnbB8e7AplaFlbOQUl5ICanX0BEWmEhSAFDVGxKYWEh'.
+ 'xAjusDBuVOMBJO8LrFHRAykAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bxs_darkgray.png
+//==========================================================
+ $this->imgdata_xsmall[14][0]= 574 ;
+ $this->imgdata_xsmall[14][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABm'.
+ 'JLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsRAAALEQF/ZF+RAAAB'.
+ 'iElEQVR42k3QPU8TYRwA8P//ebkXrgdIColXRAOEkJqbaExMut'.
+ 'DBhE1GNjYHPg+DG6ODiU6QOLjVxITBcFKBYCstlAC2Bz17fe76'.
+ 'vLD6+wg/1FpTRFR5lpaub/u1eGBGaAT4HneD4OlXx7avtDYUjT'.
+ 'HQabd2Ti8e3vVSKzxrtHS32wIpFVldno22Nqvvg2Bhl0gp/aNm'.
+ 'vJ3qqXAtLIva+ks1H0wqlSXi4+d6+OFTfRsAfHJx2d1od24rZP'.
+ 'xP2HzopINr1mkesX7ccojqif0v9crxWXODZTno3+dNGA7uWLsd'.
+ 'mUYU4fHJCViMG9umLBmM4L6fagZGg9QKfjZ+Qfy3C3G/B3mugF'.
+ 'IHHNcDf64E3KJALApk2p8CSolUUqLjFkyxOGMsTtFyJ+Wz57NQ'.
+ '8DghS4sLB0svioeZZo7nPhFoUKZDIVFbglkTTnl5/rC8snjAkJ'.
+ 'Bk/XV5LxHC/v7tR8jzTFPbg8LENK9WX0Vv31T2AEmCSmlKCCoh'.
+ 'ROnP1U1tPFYjJBRcbtzSf+GPsFTAQBq1n4AAAABKdEVYdHNpZ2'.
+ '5hdHVyZQBiYzYyMDIyNjgwYThjODMyMmUxNjk0NWUzZjljOGFh'.
+ 'N2VmZWFhMjA4OTE2ZjkwOTdhZWE1MzYyMjk0MWRkM2I5EqaPDA'.
+ 'AAAABJRU5ErkJggg==' ;
+ }
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//=======================================================================
+// File: IMGDATA_BEVELS.INC
+// Description: Base64 encoded images for round bevels
+// Created: 2003-03-20
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: imgdata_bevels.inc,v 1.1 2003/03/23 13:37:36 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+class ImgData_Bevels extends ImgData {
+ var $name = 'Round Bevels';
+ var $an = array(MARK_IMG_BEVEL => 'imgdata');
+
+ var $colors = array('green','purple','orange','red','yellow');
+ var $index = array('green'=>1,'purple'=>4,'orange'=>2,'red'=>0,'yellow'=>3);
+ var $maxidx = 4 ;
+
+ var $imgdata ;
+
+ function ImgData_Bevels() {
+//==========================================================
+// File: bullets_balls_red_013.png
+//==========================================================
+ $this->imgdata[0][0]= 337 ;
+ $this->imgdata[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
+ 'BMVEX////////27t/f3+LFwcmNxMuxm62DmqKth1VpZmIWg6fv'.
+ 'HCa7K0BwMEytCjFnIyUlEBg9vhQvAAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
+ 'RQfTAxcBNhk+pYJVAAAAl0lEQVR4nE2Q2xLDIAgFHUWBKJf//9'.
+ 'oekmbafVDZARRbK/pYTKP9WNcNv64zzUdd9BjmrgnsVXRNSzO3'.
+ 'CJ5ahdhy0XKQkxld1kxb45j7dp0x2lBNOyVgQpMaoadX7Hs7zr'.
+ 'P1yKj47DKBnKaBKiSAkNss7O6PkMx6kIgYXISQJpcZCqdY6KR+'.
+ 'J1PkS5Xob/h7MNz8x6D3fz5DKQjpkZOBYAAAAABJRU5ErkJggg'.
+ '==' ;
+
+//==========================================================
+// File: bullets_balls_green_013.png
+//==========================================================
+ $this->imgdata[1][0]= 344 ;
+ $this->imgdata[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
+ 'BMVEX////////27t/e3+K3vriUub/Dm18j4xc3ob10k0ItqQlU'.
+ 'e5JBmwpxY1ENaKBgUh0iHgwsSre9AAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
+ 'RQfTAxcBNTfJXtxZAAAAnklEQVR4nE2QWY4EMQhDUVhSIRC4/2'.
+ 'kbaqLp9p+f2AxAayAzDfiK9znPORuvH0x8Ss9z6I9sHp6tcxE9'.
+ 'nLmWmebmt5F5p2AR0+C9AWpLBjXRaZsCAT3SqklVp0YkAWaGtd'.
+ 'c5Z41/STYpPzW7BjyiRrwkVmQto/Cw9tNEMvsgcekyCyFPboIu'.
+ 'IsuXiKffYB4NK4r/h6d4g9HPPwCR7i8+GscIiiaonUAAAAAASU'.
+ 'VORK5CYII=' ;
+
+//==========================================================
+// File: bullets_balls_oy_035.png
+//==========================================================
+ $this->imgdata[2][0]= 341 ;
+ $this->imgdata[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
+ 'BMVEX////////27t/f3+K5tbqNwcjnkjXjbxR2i5anfEoNkbis'.
+ 'PBxpU0sZbZejKgdqIRIlERIwYtkYAAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
+ 'RQfTAxcBNgK0wEu5AAAAm0lEQVR4nE3QVxIEIQgEUErAgTHA/U'.
+ '+7zbipf9RXgoGo0liMmX6RdSPLPtZM9F4LuuSIaZtZWffiU6Iz'.
+ 'Y8SOMF0NogBj30ioGRGLZgiPvce1TbIRz6oBQEbOFGK0rIoxrn'.
+ '5hDomMA1cfGRCaRVhjS3gkzheM+4HtnlkXcvdZhWG4qZawewe6'.
+ '9Jnz/TKLB/ML6HUepn//QczazuwFO/0Ivpolhi4AAAAASUVORK'.
+ '5CYII=' ;
+
+//==========================================================
+// File: bullets_balls_oy_036.png
+//==========================================================
+ $this->imgdata[3][0]= 340 ;
+ $this->imgdata[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
+ 'BMVEX////////27t/e3+LO3hfYzz65ubiNwci6uQ12ipadgVGa'.
+ 'fwsNkbhnVkcaZ5dwSA8lFg7CEepmAAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElN'.
+ 'RQfTAxcCBySi1nevAAAAjElEQVR4nFXPWw7EIAgFUNMoCMhj/6'.
+ 'staKczc/2RkwjS2glQ+w3YytgXCXCZpRo8gJdGxZadJws13CUP'.
+ '4SZI4MYiUxypeiGGw1XShVBTNN9kLXP2GRrZPFvKgd7z/sqGGV'.
+ '7C7r7r3l09alYN3iA8Yn+ImdVrNoEeSRqJPAaHfhZzLYwXstdZ'.
+ 'rP3n2bvdAI4INwtihiwAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bullets_balls_pp_019.png
+//==========================================================
+ $this->imgdata[4][0]= 334 ;
+ $this->imgdata[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
+ 'BMVEX////+/v7i4eO/w8eHxcvKroNVormtfkjrMN2BeXQrepPc'.
+ 'Esy4IL+OFaR7F25LHF8mFRh5XXtUAAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
+ 'RQfTAxcBNgkjEpIxAAAAlElEQVR4nE2QAQ7FIAhDDTAVndL7n3'.
+ 'ZV/7JfEwMvFIWUlkTMVNInbVv5ZeJqG7Smh2QTBwJBpsdizAZP'.
+ '5NyW0awhK8kYodnZxS6ECvPRp2sI+y7PBv1mN02KH7h77QCJ8D'.
+ '4VvY5NUgEmCwj6ZMzHtJRgRSXwC1gfcqJJH0GBnSnK1kUQ72DY'.
+ 'CPBv+MCS/e0jib77eQAJxwiEWm7hFwAAAABJRU5ErkJggg==' ;
+
+ }
+}
+
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//=======================================================================
+// File: IMGDATA_DIAMONDS.INC
+// Description: Base64 encoded images for diamonds
+// Created: 2003-03-20
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: imgdata_diamonds.inc,v 1.1 2003/03/23 13:37:36 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+class ImgData_Diamonds extends ImgData {
+ var $name = 'Diamonds';
+ var $an = array(MARK_IMG_DIAMOND =>'imgdata');
+ var $colors = array('lightblue','darkblue','gray',
+ 'blue','pink','purple','red','yellow');
+ var $index = array('lightblue' =>7,'darkblue'=>2,'gray'=>6,
+ 'blue'=>4,'pink'=>1,'purple'=>5,'red'=>0,'yellow'=>3);
+
+ var $maxidx = 7 ;
+ var $imgdata ;
+
+ function ImgData_Diamonds() {
+//==========================================================
+// File: diam_red.png
+//==========================================================
+ $this->imgdata[0][0]= 668 ;
+ $this->imgdata[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA/F'.
+ 'BMVEX///////+cAAD/AADOAABjAABrAADWGBjOCAj/CAj/GBj/'.
+ 'EBCcCAiMOTl7KSl7ISFzGBilGBjOEBBrCAjv5+eMQkK1QkKtMT'.
+ 'GtKSnWKSn/KSlzEBCcEBDexsb/tbXOe3ucWlqcUlKUSkr/e3vn'.
+ 'a2u9UlL/a2uEMTHeUlLeSkqtOTn/UlL/SkrWOTn/QkL/OTmlIS'.
+ 'H/MTH/ISH39/f/9/f35+fezs7/5+fvzs7WtbXOra3nvb3/zs7G'.
+ 'nJzvtbXGlJTepaW9jIy1hITWlJS1e3uta2ulY2P/lJTnhITne3'.
+ 'vGY2O9Wlr/c3PeY2O1Skr/Y2P/WlreQkLWISGlEBCglEUaAAAA'.
+ 'AXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAA'.
+ 'sSAdLdfvwAAAAHdElNRQfTAwsWEw5WI4qnAAABGUlEQVR4nHXQ'.
+ '1XLDMBAFUKUCM1NiO8zcpIxpp8z0//9SWY7b2LHv6EU6s1qtAN'.
+ 'iMBAojLPkigpJvogKC4pxDuQipjanlICXof1RQDkYEF21mKIfg'.
+ '/GGKtjAmOKt9oSyuCU7OhyiDCQnjowGfRnooCJIkiWJvv8NxnG'.
+ 'nyNAwFcekvZpPP3mu7Vrp8fOq8DYbTyjdnAvBj7Jbd7nP95urs'.
+ '+MC2D6unF+Cu0VJULQBAlsOQuueN3Hrp2nGUvqppemBZ0aU7Se'.
+ 'SXvYZFMKaLJn7MH3btJmZEMEmGSOreqy0SI/4ffo3uiUOYEACy'.
+ 'OFopmNWlP5uZd9uPWmUoxvK9ilO9NtBo6mS7KkZD0fOJYqgGBU'.
+ 'S/T7OKCAA9tfsFOicXcbxt29cAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: diam_pink.png
+//==========================================================
+ $this->imgdata[1][0]= 262 ;
+ $this->imgdata[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
+ 'BMVEX///+AgID/M5n/Zpn/zMz/mZn1xELhAAAAAXRSTlMAQObY'.
+ 'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
+ 'AHdElNRQfTAwsWEi3tX8qUAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
+ 'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
+ '6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
+ 'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
+ '==' ;
+
+//==========================================================
+// File: diam_blue.png
+//==========================================================
+ $this->imgdata[2][0]= 662 ;
+ $this->imgdata[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA+V'.
+ 'BMVEX///+AgIAAAJwAAP8AAM4AAGMAAGsQEP8YGHMQEHMYGP8Q'.
+ 'EKUICJwICM5KSpQxMYQpKXsYGNYQEM4ICGsICP97e85aWpw5OY'.
+ 'xSUv85ObVCQt4xMa0pKa0hIaUpKf+9vd6EhLVra+dzc/9SUr1r'.
+ 'a/9aWt5SUt5CQrVaWv9KSv8hIXs5Of8xMf8pKdYhIdYYGKUhIf'.
+ '/Ozs739//v7/fn5+/v7//n5/fW1ufOzufOzu/W1v+trc69veel'.
+ 'pc6trd6UlMa9vf+MjL21tfe1tf+UlNZzc61ra6Wlpf+EhOeMjP'.
+ '9ra8ZSUpyEhP9CQoxKSrVCQv85Od4xMdYQENZnJhlWAAAAAXRS'.
+ 'TlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAd'.
+ 'LdfvwAAAAHdElNRQfTAwsWEx3Snct5AAABFklEQVR4nHXR5XbD'.
+ 'IBgGYM6AuHsaqbvOfeuknev9X8xISbplSd5/8JyXwwcA/I0AKm'.
+ 'PFchVBdvKNKggKQx2VIoRwMZihMiQE49YUlWBCcPL0hYq4ITh+'.
+ 'qKECUoLDZWqoQNA766F/mJHlHXblPJJNiyURhM5eU9cNw5BlmS'.
+ 'IrLOLxhzfotF7vwO2j3ez2ap/TmW4AIM7DoN9+tu+vLk6Pdg9O'.
+ '6ufXjfXLm6pxPACSJIpRFAa+/26DhuK6qjbiON40k0N3skjOvm'.
+ 'NijBmchF5mi+1jhQqDmWyIzPp1hUlrv8On5l+6mMm1tigFNyrt'.
+ '5R97g+FKKyGKkTNKesXPJTZXOFIrUoKiypcTQVHjK4g8H2dWEQ'.
+ 'B8bvUDLSQXSr41rmEAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: diam_yellow.png
+//==========================================================
+ $this->imgdata[3][0]= 262 ;
+ $this->imgdata[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
+ 'BMVEX///+AgIBmMwCZZgD/zADMmQD/QLMZAAAAAXRSTlMAQObY'.
+ 'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
+ 'AHdElNRQfTAwsWEwcv/zIDAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
+ 'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
+ '6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
+ 'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
+ '==' ;
+
+//==========================================================
+// File: diam_lightblue.png
+//==========================================================
+ $this->imgdata[4][0]= 671 ;
+ $this->imgdata[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA/1'.
+ 'BMVEX///+AgIAAnP8A//8Azv8AY/8Aa/8I//8Y1v8Izv8Y//8Q'.
+ '//8InP8Qzv8Ypf85jP8he/8Yc/8Ia/8pe/8p//8p1v9Ctf8xrf'.
+ '8prf8QnP8Qc/9CjP+1//97//9r//9S//9K//9C//85//8x//8h'.
+ '//9r5/9K3v9S3v851v97zv9Svf85rf8hpf/G3v9SnP9anP9KlP'.
+ '8xhP/n7//v7+f3///n///O//+U//9z//9j//9a//975/9C3v8h'.
+ '1v+E5/+17/9j3v/O7//n9/+95/+l3v9jxv+U1v8Qpf9avf9Ktf'.
+ '+Uxv+11v97tf9rrf+cxv+Mvf9jpf+tzv+Etf/O3v/39/8Akkxr'.
+ 'AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACx'.
+ 'IAAAsSAdLdfvwAAAAHdElNRQfTAwsWEiHk6Ya/AAABGUlEQVR4'.
+ 'nHXQ13KDMBAF0J2o0E01GHDvJa7p3em95/+/JQJMYjDc0Yt0Zr'.
+ 'VaAaxHgtxwbSGPkGQpOIeQ2ORxJiJmNWYZyAhZR0WcgQGhViU0'.
+ 'nEGoedDHGxgRapRPcRpXhOr7XZzCmLjaXk9IIjvkOEmSRLG62+'.
+ 'F5XlEElhA5sW21GvXj6mGlDBfnJ51lr9svnvEKwH1hu2QPbwd3'.
+ 'N9eXVzuL7/Hn29frdKaamgcgy67L3HFG9gDefV+dm5qme4YRXL'.
+ 'oVR374mRqUELZYosf84XAxISFRQuMh4rrH8YxGSP6HX6H97NNQ'.
+ 'KEAaR08qCeuSnx2a8zIPWqUowtKHSRK91rAw0elmVYQFVc8mhq'.
+ '7p5RD7Ps3IIwA9sfsFxFUX6eZ4Zh4AAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: diam_purple.png
+//==========================================================
+ $this->imgdata[5][0]= 657 ;
+ $this->imgdata[5][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA/F'.
+ 'BMVEX///////8xAP/OAP+cAP9jAP9rAP+cCP85CP/OEP9SKf/O'.
+ 'CP9CEP9zGP9rCP+lGP/WOf/WIf9KIf9jOf+MQv+EMf97If9zEP'.
+ '+1Sv+lIf/ne//eUv/na//n5//Oxv/Wzv+chP9zUv97Wv9rQv9a'.
+ 'Mf9KGP/v5/+te/97Kf+9Y/+tOf+tKf+lEP/vtf/WMf/WKf/v7+'.
+ 'f39/+tnP+9rf9rSv9jQv9CGP+ljP+EY//Gtf+tlP+Ma/9zSv/e'.
+ 'zv+UUv+9lP+cWv+lY/+cUv+MOf+EKf+UQv/Opf/OhP/Ga/+1Qv'.
+ '/Oe/+9Uv/ntf/eWv/eSv/WGP/3zv/vlP/WEP//9/+pL4oHAAAA'.
+ 'AXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAA'.
+ 'sSAdLdfvwAAAAHdElNRQfTAwsWEjX+M1LCAAABDklEQVR4nHXQ'.
+ '1bLDIBAGYFqIEW+ksbr7cXd3ff93OUCamdOE/Mxw882yywLwPz'.
+ '+gNKotlRFUVnNUQlCxTMRFCKEdE+MgpJaEiIOU4DKaoSIygtb3'.
+ 'FBUQrm3xjPK4JvXjK0A5hFniYSBtIilQVYUm+X0KTVNiYah+2q'.
+ 'ulFb8nUbSovD2+TCavwXQWmnMA6ro+di+uR5cPzfPhVqPV3N1p'.
+ 'n3b3+rimAWAYhP3xnXd7P6oc9vadPsa1wYEs00dFQRAFehlX21'.
+ '25Sg9NOgwF5jeNTjVL9om0TjDc1lmeCKZ17nFPzhPtSRt6J06R'.
+ 'WKUoeG3MoXRa/wjLHGLodwZcotPqjsYngnWslRBZH91hWTbpD2'.
+ 'EdF1ECWW1SAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: diam_gray.png
+//==========================================================
+ $this->imgdata[6][0]= 262 ;
+ $this->imgdata[6][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
+ 'BMVEX//////wAzMzNmZmbMzMyZmZlq4Qo5AAAAAXRSTlMAQObY'.
+ 'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
+ 'AHdElNRQfTAwsWExZFTxLxAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
+ 'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
+ '6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
+ 'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
+ '==' ;
+
+//==========================================================
+// File: diam_blgr.png
+//==========================================================
+ $this->imgdata[7][0]= 262 ;
+ $this->imgdata[7][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
+ 'BMVEX///+AgIBmzP9m///M//+Z//8hMmBVAAAAAXRSTlMAQObY'.
+ 'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
+ 'AHdElNRQfTAwsWEwCxm6egAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
+ 'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
+ '6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
+ 'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
+ '==' ;
+ }
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//=======================================================================
+// File: IMGDATA_PUSHPINS.INC
+// Description: Base64 encoded images for pushpins
+// Created: 2003-03-20
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: imgdata_pushpins.inc,v 1.2 2003/03/23 16:23:32 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+class ImgData_PushPins extends ImgData {
+ var $name = 'Push pins';
+ var $an = array(MARK_IMG_PUSHPIN => 'imgdata_small',
+ MARK_IMG_SPUSHPIN => 'imgdata_small',
+ MARK_IMG_LPUSHPIN => 'imgdata_large');
+
+ var $colors = array('blue','green','orange','pink','red');
+ var $index = array('red' => 0, 'orange' => 1, 'pink' => 2, 'blue' => 3, 'green' => 4 ) ;
+ var $maxidx = 4 ;
+ var $imgdata_large, $imgdata_small ;
+
+ function ImgData_PushPins() {
+
+ // The anchor should be where the needle "hits" the paper
+ // (bottom left corner)
+ $this->anchor_x = 0;
+ $this->anchor_y = 1;
+
+//==========================================================
+// File: ppl_red.png
+//==========================================================
+ $this->imgdata_large[0][0]= 2490 ;
+ $this->imgdata_large[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMKBh4Ryh89CgAACUdJREFUeJy9mNtTFFcexz+/7p'.
+ '4Lw1wZJKDGCAwmDAqUySamcCq1ed6k9mn3UfMP7F+1T3nYqn2J'.
+ 'lZdoDEjpbq0KG8EBFBFBEJye6Zmenkv32Ydu5GYiUMmeqq6uqT'.
+ '6Xz3zP73aOcIKmAQkIFyD3N/jrBPwlKjLQEglVlJKyUjR3u7cc'.
+ 'WLoP3/4dvv03LNrQ8I6x1rFbDML9kOmHvh7IRHU9JKmUSG8vpF'.
+ 'IoXX/TV0AiEM5A5jT0noFMFMJHXUt/d5f9TUAbhtQ3cPFruDog'.
+ '8klHMnmO0dGYe/myOJGINEwTz3F2higFXgy8PpAkOC+h8hoaCt'.
+ '4ppHFcQAWSgOQlyI/p+lUjmRxWAwNJd3xca/f34yoFi4tgmjtD'.
+ 'NIFkJ4xcgBCgVqEBFJ9DqcZea/gNAAVEg7AOGYnHe9XoaJd3+X'.
+ 'LISSSwnz6lsbKCZ9sHh4UVdBkwdA6cPwNnIfJPmC3Ctgft3wwQ'.
+ 'QPkvTZJJnbExzfvsM2nMzVG7e5fG48d4lnXwTwEYCjJxuHQBog'.
+ 'BHUfKkgAIIhiGk06hTp/Dm5qS1uYlXLvtWd4gPgIiCrAEcVckT'.
+ 'Ab5p7TaYJrK1hQaEenrwSiVfQdc91P0kSp7Ii89D5ksY/kAkLy'.
+ 'IZXFdXkQjS1YUSEbdcRu168V6+HTUNIKJDRwdE+sBIQmP9Ld59'.
+ 'bEBA3of4F/D+uXb7rGaaCSmXI3pPj64PDaHCYfEqFVSjgWo2D2'.
+ '73XlJNQTgCyQykIuBWoNKEeh1aLXBPBCggGdBOgxZVSjoajVhH'.
+ 'o5HWlIpq4bCQSgm9vXhK4ZZKh5SUYygp4J1EQVUD9xlU18BJQD'.
+ 'bUbJ5T5XJStyxN9fSI099P3baxV1dRloW2h2ivx/yakg2ot6F1'.
+ 'EkCa4G1D+zVEq5ArKTWM42Q6HUczQV7U66w9e0ZpdRXlOIQ5vF'.
+ 'VHUXILKify4jiEzkOqC3peQMoBQymFlMt4Dx6wUSxSsm2UZXEK'.
+ 'P30QvOUt8/2Sd78CdWwFDTA+gsw3cOlPcPUD+CQB52oQ21RKXM'.
+ 'eRhGXhOg7VoKrx8KuS4ygZhVg3ZI8FGIfwR9BVgAtfwxdXdP3L'.
+ '86nUR91dXelNXTeWWy10paQHX602YAP1ADASAL7LJvFtMpOCc0'.
+ 'cG3FHuGlz6Gr4YEpnoTCbzsdHRbOzy5RCRiLRMk5rjyOtAimwA'.
+ 'U4U3SurBN/0wnAASBCVDIKpB4kiAB5Ub0/UvO9LpPAMDGfn005'.
+ 'AxPCzxep3Q6iqPLUseBoufCZRsAE6g5g5kKIDfKUj3wnpAG8QB'.
+ '/Z1OIqANQuI65AtwNScyYXR2XlAXL2YZHzcklRKWl5GVFXFtGx'.
+ 'MoAiV/EQaAGH6BUQNWgQpwFngv+Ca8KUAQEBcwgTJHyMV7679R'.
+ 'XS8YqdSI6u/PMD5ukMtJY3GR2uQkr5aXeWVZOEALmA8WsIAxfL'.
+ 'd0goVLAdCOd+/YpgqeVtBv4yiA++q/RKKXixe7GB8PSyoljcVF'.
+ 'yg8fyubyMpulEk2lyAIfAAvAC+B+oOQFoAt/+0rAejB/EzjNri'.
+ 'vvqNnCd64jxcE39V8spnP+vMbAgDSePKE2NcXm06dslMuUlcID'.
+ 'TuFvqwXMBU8N39bGgRR+ki0Dz4L5DSAe9NGD7zq+6kcN1L6H2b'.
+ 'ao5WWaQHllRTafPmWrVMJUimoAQrBYJFjQwre7B6A8YAi8LCgD'.
+ '5DVo6/hbb/iHK1KggvFeD3hHziQKEMuiNTNDbXGRTdtmw7Iwla'.
+ 'KGH0oqwbscLOoG46rAY6AOzRhY74PT6QuUKEN4PegXxd/yEDTT'.
+ 'YMWOk+oEaLkuFdNk0zTZwjfkavDUArXWgGXgFb4dEShXhfYqlI'.
+ 'ow3w9rg3B6ED60IOOA5oEYQBrcpG+mj9bg0VG8GMJhVDZLyzAo'.
+ 'VSq8rFYxXXefcjVgG9+uisDrXUCApoKSBcUHMBmHhfcgNwhtD3'.
+ 'q9IG6Lr15b4OUTmPwBJt8JqGuapp05o0mhoHnptLQfPsR+8IBK'.
+ 'uYyNH3yr+B77LHheA3tK1Ta+IrMeTL2C6Xl48TOsNWDDgAz7s5'.
+ '/r+krP/eddCsbj8fDQ4GBm9MqVvvRXX2VULBayRGRzaYn1SoWa'.
+ 'UjgB4PIB5QK4ZgBXBKaAHxQsrED1H7CRgCUPwgHZDqACmhWwXv'.
+ '2aDRqGYeRyufS169cvThQKV88PDuYbW1vJ5VRK+5euqxWlPMdX'.
+ 'SRqgreHbZGN3ijfKBXBTAeh2Fdwi2MofshP/dvKwCmKhp4m83Y'.
+ 'vj8Xg4l8tlCoXC0MTExMTFkZE/1m37wvLGRvKRacoD1209E7Fc'.
+ 'pZwYREOQqEJ4z3HskHLsz4AoXykPIBSN0t3dTTQafROoHdumXC'.
+ '4fjoMiog0ODiauX7+eLxQKV3O53ETdti88nJnJ3rl505ifmWm3'.
+ 'arWSodR8GNbycDoNHy5C5jFold1k8d+DyvELNwg93d18/vnn9P'.
+ 'X1oes6nufx/Plz7t+/fxhQKSWJRCI5NjaWHxkZKdj1+sjSwkJm'.
+ '+uZN/dZ337VqCwullGUVdZjsgIUC5LqhrUPvCugWuApeApPAzY'.
+ 'PKHWyaphGNRunt7WVwcBARwfM8Ojo6sCzrMKBhGLphGFEF2Wq1'.
+ '2jc7M5OZ/vHH0MPbt93awkJJmeZsC6ZaMK3DCwvWdNioQUb5B6'.
+ 'AdBR+9SzkAz/NwHIeXL18iIui6TjgcJplMMjY2th8wHo+Hh4aG'.
+ 'MsPDw6fddru7+Phxx51bt/RbN260qwsLpZhlFZsw9QJ+2Pbrga'.
+ 'oJG2FY2oKwuTtVEz9uV34NbqdtbW0xPT1NNBoF4MyZM1y5coWu'.
+ 'rq5dQBHRcrlc4tq1a/l8Pj9RMs38ndu3Ez//9JNXLRZNyuXZJk'.
+ 'xVYKoExQpsK/+IaAuYb7no8zjC/R+A4zisrq7u+53NZjl16tQ+'.
+ 'QIlEIslsNpuPRCJXZ2dnh2/duNFRW1oy07a96MKd575yxRqU1B'.
+ '5vPMpF5HHa1tYW9+7do7Ozc/eQpZTSQ6FQt1Lq8pMnT/5w7969'.
+ 'nuLcXE1rNufO9fRMhlKpOyvt9qPtVmvb25fFfvvWbrepVCqHwo'.
+ 'xaX19vff/996ZhGC8qlkW9Wt1Onz073fXxxz+6MB+9e9dUjuO+'.
+ '7ebq9wLdB9hoNCrr6+s/4wf3FCJW3fPmTZhXsNWCprjuW66Dfr'.
+ '928KAfBhJAEgiJSLuzs7OSTqctoFkqlZRt26j/I+L/AGjPTN4d'.
+ 'Nqn4AAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: ppl_orange.png
+//==========================================================
+ $this->imgdata_large[1][0]= 2753 ;
+ $this->imgdata_large[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMLFQ0VCkHCzQAACk5JREFUeJytmGtzG0d2hp8zNw'.
+ 'AEcRdJ6EJK9FL0CqZUm9jWbkwq3vhDstl8dmLvz8rP2H8Q75ZT'.
+ 'pkRfpLgqsS6WIFEKGYkiSBCDO+banQ8DUpRWEkklXQUUqlCDfv'.
+ 'rp857pgfAOQ4AMOJdg4R/hX96Hf06bvDc5iT07i8yeg8ksiIAI'.
+ '4TBi/ds9/vivD/njapNHvRBfHXMu410AM+BUoVSF05NQsi1sO4'.
+ '8402AXwLQTuP31OAZO2aG0MEn14iSlnI1z3LnMk8IZYJyBwjIs'.
+ '/TWsVIWPJkvMFS4zMfMhUp5BsoCpAAEBLYKaMFGn00jBxnvu02'.
+ '35+JHmSJEnBpQEcPo38MmCxd/nS9Ry71Ga/g1W9a8gn0GsHkgA'.
+ '6DGjxkqb5CoO+YxF3A3p+jGjQUzoK+L/V0ADzFMwtSR8eLbAr8'.
+ 'uXOTf9NzhTc0geSLUQcYHgYEH786RMg0zWJHV2Aitv4x/HpHVS'.
+ 'QA2YBqTTGIUq5qkPMWaWkVwPnPtAA/BevmZcjxaaUtHh8pJJGu'.
+ 'DpCB9FvT7A7YT7S3p5vFMNzmWo/O0MSx/Ms3TqI8r59zFTfUQe'.
+ 'I7SBODE3tnfoIxYnNHligwik0zAzDdVpyKbA8sff5YAeMEwgkV'.
+ 'cufQeTJzZoCsaFLKXPTnNpoUTNsSgJmNoGsuNQjIDwYD2HlnZy'.
+ 'k++yxTKXZfKTU8zOpjhneeQYkorSmGERtIlICBKRbLX+y98YN3'.
+ 'ADcNIm+bJD4U3pPnmbEaRgYVRTGBkDSSsmxKfY7ZLuDJA4hdjl'.
+ 'JEgyBB2SJOvQ9RzTpNKoEwNq0CNFvOXR3/HxMgYVPObaz8kPmh'.
+ 'hkEWMatAfRONGGvLizyOE9P8KkpwhPDAgQKJQbELUD0oOIhbbH'.
+ 'JeVTmowxjAgZutB5AoOngA+2DdYrcTyOyYZP9+QpBvI29vwEhb'.
+ 'It042BVQgDy9KTMfkwQG1A9ACCLlgBBGUwxxoc52WDh2ATyEPp'.
+ '1hoaPvrEBh0Dq5an9OUsl/9hylk5b5c+mowLc4E2Jtw4Eoljyf'.
+ 'ogA/AGEAagNRjGyUxOmEycyVA5EWDBxrmUp3ytLIv/NJP69Goh'.
+ '+9mFydIvS5PZYkvH1oY/RFtKymlwBFQAgQd+kAA6qSQ8pvn2mp'.
+ 'SkJkuVFHPHBnQMrEt5Sl+e4/Lvp51PF1PF5Xy6WMvOWZXMom8z'.
+ 'OZTQ8+j5sbQiMEwopsCIwRtBGIJSCdzbTGo9NimkDcgdC7Bg49'.
+ 'TG5n4/nfr0Si77WdYp1YzyZEkWPdteaEnB7pPqBTxuIf/VgciE'.
+ 'SgasCPwh+GNIkaNNag1RiPge5pEhMQVjfoLcF+eoXSvbKxedwn'.
+ 'LKzC3KWbOi5/sW5a44/SHFUSgVA7SCzRG0AvA9mPOgFIETgu4n'.
+ 'Ww0wNQWFAqRSL6D2ZQYBdDrQ7R7jXiwgRcvIL02makuTmWtpM/'.
+ '+BlLMl5vuWzLVEuwH6oYnR1KS8kJINGXMM2YdfRlALoQoQQKeb'.
+ 'bDVwoMdxQMaLCwLo96HZTF5HbrEhmOftianfZisfzueKv7ZmrX'.
+ 'MsjhxKXZGBjzyeEHmSE3oWiggtyVGmE8DTIXTC5NxgAxOAGUM8'.
+ 'fun9mnSSLQ/CxNzOTgJ3LIMgoGwkKBiiMyaVviHVkdCO4FEKNv'.
+ 'LQzWBYHfITPa4UBVM0LR/WB7ARJsdDDTjA6deYFIFUOimJ3d0E'.
+ 'sNdLavYYgBpthqKcjiiJRO8K6CK0CsJTjfQAGaJtD9vQFAxNNQ'.
+ '1FB0yBAfA8gdMAIagLoCVAen0M00zMOTYShNDtoHs9CAIUoI4E'.
+ '1IBihCdNhsMhsj6NuV7BCC2IBpBqQaaFOENCCeiEsO1BO4RQgy'.
+ 'I5Hm4k4oIU9MrgZSAdBeTabZz+ODxKQRRBFBJo6IUc51anYRQo'.
+ 'dto+24FNxYCiaWKkQsj00KkO4gxRRkAngJ868M0u3OkkM+hxQA'.
+ 'cQ7YD7GO5XYSsPZybh/TCkFIYY+kWniTW4Q7jXgHvHMhiRpmuW'.
+ 'ca08GZkkZ/nY6TZMNhCnf2CuPoDVJvxpB+q9BHA8Ag1uH+oP4c'.
+ 'YEPCzDwmzSLquShHW/E0YRbG/BjZtw40hAy7aNzJlzRn75E6N0'.
+ 'qiwTzafI7kOU3gWrhzZC2iHcbsPqLlxvJnCt4KC1RYAL3I5hzY'.
+ 'Xv/huePYCtITQMKEnyB4KQvMURuJvw889HGSwUCs7CwkLpo6tX'.
+ 'Ty/+7nel6VLGDn/8N9m+eZuo1UP8iNhLau6b3RfmOsHBGTUYw9'.
+ 'WBNeDrGB4+h/4qNLKwTnLbHj9CJw/6GoIh9Jpvq0HHcayFhYXi'.
+ 'l3/4w9LK8vLKexfma3G/mb/3n1njTivS7tNQaaU1grQDjJ868D'.
+ 'Axx6vmxnBrY9C9IcSbSXbavNjb/S3eN6/0m1JcKBScixcvllZW'.
+ 'Vi6uLC8v12q1v/M8b/HxVjP//YYr32yE4dYWvShO0ogi14xwxq'.
+ 'F4rbnxZ3cMjtpvEEeMvwA0TdOYn5/PffHFF7Vr166tvPeLXyx7'.
+ 'nrd4+/btyg/frFo//Xgncnd67qCn78earQqcmYD3fSi1wPCTSV'.
+ '3gzqvm9uFOMl5nUAqFQn5paal26dKla57vf7D+6FHph9VV88af'.
+ 'vgq79bo70e3VT2l9A3hYg4UiRALVHTCHSZvYBm4A//6quf8zoG'.
+ '3bpuM4acMwKr1+//SDe/dK31+/bv90/Xrcq9fduNW6rbVeC+E7'.
+ 'gWdD2DKg4UEpBmPcm10RuScida31ntb62HAigoigDw6Gh0axWH'.
+ 'QWFhZKi4uLZ+I4PrVer2e+u37dXPvqq6hbr7tOp1NXWq89h6/b'.
+ '8FBB34WGBesdcPrj38lkMkGlUuml0+mu53nR3t4eo9HoSLhMJk'.
+ 'OlUiGdTuN5Hq7rvgA0TdO4cOFC7vPPP6/VarXldqdTu7m2lrv7'.
+ '7beq++BBO263b/tKrfWSXlbvwJ6CuAtDgTYiaBFMw6BSqfDxxx'.
+ '+rarWqGo0GN2/eZGtrC6XenAkRoVKpcPXqVWZmZmg0Gty6desF'.
+ 'oIhIOp3Ol8vlmmVZK3fv3Lm09uc/Zwbr653ccPgoNIzvnmn99Z'.
+ '7W9QG46lAaM5mM2l95GIYUi0VOnz7N7OwsWmsymQzyuse5Q8Mw'.
+ 'DNLpNDMzM5w/f/7A6AGgUkoajYa9urpayOXzUz/fvZutr68Pim'.
+ 'F4/2y1+n2o9Q/ru7uPesPhXnyo4A+vfHp6mmazybNnz9jZ2UFr'.
+ 'TbPZJAhe+8/aS0Mphed5NBoNABqNBqPR6MWBVWstvu/nnj9/Pv'.
+ 'vo0aPq5uZmPBgM/qcwPf39xV/9ajU1M3Nvq9PZaw8GoT50PjdN'.
+ 'k6mpKa5cucL58+eJ45j19XWePHnCzs4OnudhmiaWZRGGIVH05r'.
+ 'yEYYjrumxubrKxsfFyDQJ6NBp1Pc+7C4jWumBaVm+kVL2l1H2l'.
+ '1G6otS+H6V6z8u3tbVzXpdFooJRicXGRqakptre3uXXr1ltrcT'.
+ 'Qa8ezZszemWAE9rfUdYBOwtVLRbrPZ+48ff+wDvuu6Sr3MB4Dr'.
+ 'uty6desgfa1WC3iRyrNnz4pSSmezWUzTfGtYtNYcdvC/9sMlgP'.
+ 'n5N4cAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: ppl_pink.png
+//==========================================================
+ $this->imgdata_large[2][0]= 2779 ;
+ $this->imgdata_large[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMLFQolY9lkpgAACmhJREFUeJy9mOtzFNl5h5+3b9'.
+ 'Mz0kzPBWmEVtIiWYhIiC0HCDhB8lb8ISk7nzdZ5+/zJ/8BTmpT'.
+ '660CZLwG1pVFgBkgGIHECEaa+/T9nHzQCCQuRpCNz6mp6g893U'.
+ '8/c37ve3qEjxiC4OA4n/Lp/EUu/tsMM/+aEWduVBx7WhdkShcY'.
+ 'xUH2zo0Dwod/5N6vf8V//PoGdx8M8EOFPtK9jI8BdHCcMuVSmf'.
+ 'LxHLmSZdm2U8xIbmKETDGDZZnIy4dBbCynyGhphurEDBOlHFnn'.
+ 'qPcyPxTOwDCOccw7w5nlBRZWylI+ny/mZ6rL1dzUZ5/IWGZU3D'.
+ 'ZIOMQDDaJcHDVGWUbJBi9odVr0QoVSPzigIEaZ8vgSS/8wZU3/'.
+ 'k1fylipz5dLM2WlrZqHKaGCKbEbontq3KAKWQyZfZKTgYqc9Bp'.
+ '2I2PcJ4ogk/UEBQcwipbFZmT13vDBx8fhnE1Ofnp9yJopFyT3X'.
+ 'yANfks0QHSQMDaL37pOxMLIu2UyVkjVKLjyKSeuD8dAYCFkso1'.
+ 'gYMaeWJ40T56cl8yAi/O4FSa2P6kYczIDsgVpAqcDImZPMuAB1'.
+ 'dkLQtcc8a/bwox8IUHAxZVxGZMouSLVYwKuMkD5IxN+JSdsRJB'.
+ 'pexuTVgYYM6EoGmxkmg3/hEhNUMr/hd7dqbOzExMn/GRDAxWZc'.
+ 'j3I8HiXfMjF2FQowKw7pjoN6E/Llw/GBJj8qxVOMlX4ipxc/lY'.
+ 'kl2zBLkmrTcEzMkoNoRLVidLi/9g+Z3I+1xRHX5EcAihxnbPRv'.
+ 'OTU9kZSmpKPy9FTGrLimPZ1H+UiyGaF67w6n7E1DwMngFDxGvc'.
+ 'w70v0xZUby5IxjlIyMssUJrJwVWkXBdbXvSvwEibcSdKCAFI16'.
+ '4/sc0SRo9cGAGq1DwvQFzV6DVuBiV4zYnlEts6A2TSPcSiXoxo'.
+ 'QqJCEEFMbQ2b69o5qMiOOPqIMQkagu/aSL7waE8101WFShLjk9'.
+ 'yxgEvjRUiyYd+gwAjY2J9VpXfZ/JEXLhDp3OR6U4T97+hEnPwx'.
+ 'tv4HsRjy2tTQSFzQgDUnwSLBQRI+x1ZgcH87Vcv4SF19Kt0ezS'.
+ '1h9s0Ma25pgr/YJfnLnEysok0+ezjM6EBLldGqKIJYuDRhOQEJ'.
+ 'Oih8X9Q0xmcXNjlCofBJgn78wxVz7L2YWf8tPPz1hnfjbjzfxN'.
+ 'qVwutq2etZXUQSXikcXGIgUiUkJSDIQMJgYGJsaB3c7b1qQ4GZ'.
+ 'xSkdGZIwMeNLfK6uezMnvJK3pLxeVixfvMsyVjSNSO6IV9adPG'.
+ 'AArkEEz8oUkFmBjYGO80qfd6pCWIayD59wIKcsjcKqufn7JO/S'.
+ 'xfyi+5c24pey5rZ09mJRNkiDdT/tzbkBr3SYkpMYpgEaIJSYhI'.
+ 'kSOY1GhilAQk5ntDIojxCZ/kf87Pl85xbuWEnLiUy+cW3NNuJX'.
+ 'MmY5meKf6mT7wZS+THdOjxlG06tIlIOMZxchSxcFFEGAwAGGME'.
+ 'jwyZYSnWL3cXWiIUbUI6hO/vxXuFOV84ycmlBWthNeflTjuzTi'.
+ 'lzJmM5s46Ej0J63/ZoPmoy6PYxtYVNhmfs0mbAND1mmKVMBY1L'.
+ 'mxA1LN7WgXQbCApNhKJHRIM+DQbv7yQGhjnJ5NgFuXBuxpu5mD'.
+ 'udm3LPuY7pmZLUE6L1SIJaIPFuDAqyw9lnwDYv6NFHkWJh4ZDB'.
+ 'wCBFD3uMxsTAwcBAiElpE/KcPg36dIiOvpsRxDCyhmlP2YY9ZU'.
+ 'v8NMb/1id+FGO0DTztkSXLOONUqeITsMkW2zwnJEIDFhYGx+A1'.
+ 'kwK4mASkvKDPc3p0iYhRRwYUhZLUTyV6Eu0t4s1Y4kcx6W6KaM'.
+ 'EZThcXH59RRhGEgIAddnBwNEBKqqpUtWBIF22YDIhJsbEkJqFN'.
+ 'qLtERHs7GnUkwISEQAf0uj30bY39PzbiC6qrDu2cExJ69Nhhhz'.
+ '59UlIUipCQOnVi4sjG7ubJBy6um0C+he/0iDHQKIQERYyKFLqr'.
+ 'SI/W6kJCnvOcrWSLSquC1/Jw9Ks3R0FQKHr0uMc9bnCDGjX69A'.
+ 'H0XlcJkibN5jOe/alCZStHbjJL9lSMLkXExvCXRiDV6GZEeGeX'.
+ '3TvvBVQoEjfBL/v0rT75Th7VU5C8gktI6NLlMY+5yU3WWGODDf'.
+ 'r098tHpNFNH7/2lKdXXdz7efLzVaqJIBOCmK8AJUlI6g0aV+9y'.
+ '9+p7AR3bMQpTBWPy7yeN6fy0jNwewfpvC9Xe+3kFoUuXe9zj5n'.
+ 'BusEGHjh6GIAGawC2FWuvSvbbF1maFylZAsC1ISZADBiVNSJrP'.
+ 'eX73MY//skHP85z5+fnSxQsXj//4n39cmnPn7LbZlsajBmEnBL'.
+ '1nuEGDG9x4aa5Ldz+h0RCuBqwBv1Wo+7vs9r7n++0MmYeAM+zB'.
+ '+61EK1QUEnbbtN+9Bh3Hsebn54u//PdfLq9eWl2ZnZ1dSnaSwu'.
+ 'Pin40b9g3doKE0WoNIl65xj3v75njd3BBubQi6ExKmDWkMRKSl'.
+ 'tSbVKQcMao1Go5Ugb0+x53nOyZMnSysrKydXLq1cWlxa/McgCB'.
+ 'Yev3hU+GPrD3I5/q94k3pXYQY58q6B5Bs0HB//neaGx00gyWaz'.
+ 'VCoV7bquCoKAnZ0dfN/f03egLGj0m3XQNE1jdnY2/+WXXy6trq'.
+ '6uzP3oR5eCIFi4detW5feXL1vr679Let37zVB3/mQytjXJwmSB'.
+ 'wikHp9ShY0RESqObwPrr5oBERKhUKly4cIFqtUq9XufmzZtsbW'.
+ '2hXvuDwTTNtxZq8TyvsLy8vLS4uLgahOHphw8elL69fNlc++qr'.
+ 'uFOrNXPddm1cczVL5f5P+Lv5MuOJgTGxwYbZpZsCdeAq8M1Bcw'.
+ 'CGYeC6LtVqlRMnTjAyMkKn0yGXyx0N0LZt03Ec1zCMSrfXO37v'.
+ 'zp3S769csb+/ciXt1mrNdHf3ltZ6Lca8ZpJsduhtCdb2gEFJoQ'.
+ 'xADYHuHDS3f32lFEEQUK/XGRkZoVAocP78eZaXl9FaI/Jq25Uk'.
+ 'yWHAYrHozM/PlxYWFibTND32sFbLXrtyxVz76qukXas1M61WTW'.
+ 'm99gx+20TdN9jqtfjP7QzOwwYNp037Zd0DukDnIByA1pqdnR2+'.
+ '++472u02Z8+eZWJiAsMwDsEBRNGBzYJpmsaJEyfyX3zxxdLS0t'.
+ 'KlVqu1dP3q1cLta9ekU6u1dat1J9b6Sk9kraV1rYXegW7apDYw'.
+ 'kFY6fPc4MNTw88bwfZ/NzU2UUnieRxAEiAiGcXiXfcigiIjruo'.
+ 'VyubxkWdbK7fX1xWvffFMInjzBM82uMT5+p++6V1UUrSe7u03t'.
+ '+8lezlKt3gHyl0aSJDQaDa5fv876+vo+w6FzDq1BpZRsb2/bly'.
+ '9f9vL5/Njdu3fzG0+eMJHNxsfn532vXN5NPG/7abPZal6/Hvfe'.
+ 'kroPHfsm98f7AHW9Xo+//vrrlmVZm71+37QNw3JnZ9PK4uJGpV'.
+ 'pt4Dh+vLGhsrmcfv1iHzu01m89HjIdCon2fb8TBMHtvYeRUn50'.
+ '1Oj4vqp3Ok1f5LYSadfr9dQfDN642P/XeF2DA+SBAuA4jkOhUK'.
+ 'BQKESO43S11p3BYBDt7u4y+CtB/i/q7jp1GMiw2AAAAABJRU5E'.
+ 'rkJggg==' ;
+
+//==========================================================
+// File: ppl_blue.png
+//==========================================================
+ $this->imgdata_large[3][0]= 2284 ;
+ $this->imgdata_large[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMLFRAiTZAL3gAACHlJREFUeJy9mGtv29YZgJ9zKF'.
+ 'F3y/Q9jh05tuQkarKgbYasde0UBdZgwNou/Vqga/sD9mP2B4a1'.
+ 'BbZ9atFPxb5sqOtmXbI19bqsluPYiR3HN90vFEWRZx/IJI5zqa'.
+ 'x0OwBBSgR5Hj7v+55zSEFXTUgIJyA9C6/9RsjMjAyFIxxJCDc7'.
+ 'iBqKgyZACGg3G2x9+xXf/fG33P3mC9qNKsp1O+1JdkEnQTdgIO'.
+ 'ttCSMUi8gj072MnugllAyB9G8rBGi6RsToJTF6iuRoFi1kHKZf'.
+ '7fB8Iggj0/Dy23D2dakNTR3JDsXPvzstxmZGRMER1EwHhQAEgE'.
+ 'CLhIkPD6InY9S3djGLJVBtQP1Qb4HDAyoJYQOOZkPx49nhTH9i'.
+ '7MUBGT7egxkJgd70wZS/CUkoZtA/fRoE1DZ2ACiv52ibReCp4e'.
+ '7CIEHomxDiuVdGTqUnf/ZeOjR8fpiVXZul5ZrY3bWwbdcLr/dA'.
+ 'AAIpAwQjUWIjQ+g9HZvswiCgBVF9/SI6OSLGzo0i+oLi6+Utbq'.
+ '+bKEftgwOE/0Ohocf66M+cBjo22U2RQLIHMhmYnvaOpR9S8bSU'.
+ 'UqCURGpRkuMZMm9cIvPGJZLj0yBjT2LprkiSkykx9cuXIhOnUs'.
+ 'm+QNC2XdG02ggBTcvFabsPWwTPpBAChSCgh4kYBpoeplWp47Qs'.
+ '7EYDt21xINzd5GCAxLExRl89Z+nHjpbKMmjbmkgfDzI0JEW53K'.
+ 'Jaa6NcAOEX8v52uJzsBlAS6u0hcnTIccPRqhWPCUcLD+s1EaUp'.
+ 'HCEhEMCyHNpt9SjgIU12A6iw6xb123vYhaaKjB9tlgMD5X+uBp'.
+ 'zdkpg6azA8EaNQtKlVba+Xez4eCntnJrsDdFsW5nYFpxlFN846'.
+ 'DXe8utkM4mhi+EgQmjYbS2WqexZKk6BpjwJ2YlK5VjeA3pNDiH'.
+ 'YjRWPzPE7tmBo8EWwGhkXx+z3uXL7D3rU97LIF8RBEAl6lK/Uo'.
+ '6JNM1rZ2aTcr3eUgIQOGTgbdwXMGyRejenLYTvQGbAdRuetSud'.
+ 'OivVuFZgtCEgICghICnZoMhmlVTPR49LCAEkQUhk/B7KXe0MWf'.
+ 'nxj8xVR/cDheK14WZmtVMJSBnlGoN6FmQq0FLfdwJgORKPHRo/'.
+ 'Snzx4G0F/FjJ4KiOdmjPCrrx8bffnMybMv9MQGNG3rzlVqtR1B'.
+ 'sh/CYXCD4Aag1oCW7ZnUOjSp6WFi/QNEB8Y7BfTNjZyCmUvJ0I'.
+ 'XXT47MTp98Ybon9VZCk8cVazfqlNargsY34G7ByAlIjkHd9CCr'.
+ 'LbBdiHViUgiECuDKYCdz8b2cywREdiYZOj8zNnLuzOTzx6ODp+'.
+ 'OaGaqwVzBFqz0Idhz2loE7YEwBLaAJLQcKbW8qjAcBF5Jh0AMP'.
+ 'IOHe6kxgtb3UMO2OxkF//ffK28nQqxfvm3szrtnDVa799Qb/+v'.
+ 'NtsbNSpm3tAv8B+w7Ub0FhAyoBcMPec9oK6raXk48ziQBXQcmC'.
+ 'pT3YqHa0mpEBkTR6wz/Jjo2cy04+fzwxdDquNfQKO7sFUbpu0c'.
+ 'wp3JoAYsA42Bbkl4GCryUNDEM7Avm6Z/CgSYBWG8pNuFuDu1Wo'.
+ 'tjoxKIJGeHIiM/jmK9NnX5ycuJQMtUcqXPvLDTa+qIie4hAJ1U'.
+ 'vdrmO2HaDfB931twJgAn1A4lGT96obPHPLBbhVgUoTHHWo9aAA'.
+ 'JVAKpyKEmQNzWRENAsL18ycKjAFN/9gCNvzLB/390MMmE7pnDi'.
+ 'Bvwt0K5Jv3O+0oB22nJ1Vvjb/UMhOpcKknqN1OiMB2DNHU2G5s'.
+ 'sVndpGJVcZXjX1IAlvw9PmhRQcOFPhsSDkiBrQR1G7brgs0a7D'.
+ 'ag3FK4rguqBXarI4Nt1SJv5gls7TEWtJDRBO2GwnIs8maevFnA'.
+ 'Gx6awLZvzeTBu4kFbLigijC47pscpx0xyDfkvtUEnlarCDtrUC'.
+ 't2HGIhvPHVdVwqjTIrxRU2a5uUrYoP0QZ2gMvACl7+3V/LuKDq'.
+ 'sJuDy597516+CEezIHXv7vcgXQu2l+Bvn8He9Y4AE4kgk5P9DE'.
+ 'R6aFdq5Et5Nit3yTf3m9sBcsAN3+D98c0Fit5JawE25r1zg1Fo'.
+ '5B8GFD7g+nVYnu8EUEop9XTa0N/9dUbqcphP/rDJzbUClVbpgR'.
+ 'y2fXM3fND95qj75J8AC6BWPINfVSBieK+x+6cS5UCzCLu3oFV9'.
+ 'GqCMx2NGOp2Znpv7aXZudsool3T5J/179sxVlHJ4kGPrP2COBX'.
+ '/7DmiApWCjxIMXpYNznYuXM+6TAKWUMppOZzLvv//ery5cuDCT'.
+ 'SqVS336bCwr1JfAPB9r+2KAFwJS+OcETzZHz/7v3etl6ipz77X'.
+ 'GAMh6PG+l0OjM3NzczOzs3k0pNnFlbW43+e/GKtMqrblSsF03V'.
+ 'WHcJA0PjIAzvg9JTze2H67g9DjAwOTmZ+uCDD96anZ2dnZiYmF'.
+ '5dW41++Lvfa1fnr7qllVK9103mXNTnJgPA+YugsvB3HTaEl+Qs'.
+ 'AZ/yeHPPDCiTyaRx5syZbGoilV1dW00szC9oV+avusuLy0Xd0X'.
+ 'MgFkDM+zkYBZEHV8f7wwKu84zmngQoNU0LaZoWUa4K31y5qX/8'.
+ '4cfyyvwVN5/L10NOKNeg8UmDxoKF5Vfj1xXAgD0JrgAcvBDfel'.
+ 'a4g4AykUgY6XR6emJiIru2ttZXq9S0K19eUcuLy8WQE8o5OAsN'.
+ 'Ggsmpl+NpoL1g9X4UBU+C9xDgEKIwNTUVOqdd955M9mbnJ3/cj'.
+ '6Vu5aTheXCQXNdVeMzAwJSCGEA2XKpnF1cXIzlFnOVhJPIKdR+'.
+ 'c88ctq4AlVKsrKzw0UcfKcC5uXqzXnNqSzb2pwLxOHP/l7Z/BN'.
+ 'eB01LKt4HTrusKvGr8jB+hGn8MQAkYQMrfw4Nq/MFPtf+rdvDb'.
+ 'k8QL+/5Z4Uepxm7bfwHuTAVUWpWaqAAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: ppl_green.png
+//==========================================================
+ $this->imgdata_large[4][0]= 2854 ;
+ $this->imgdata_large[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMLFQ4hANhluwAACrNJREFUeJytmF1zE1eagJ+3u9'.
+ 'XdkvUty2AbmLEtEzDBgZ0UpDBOalNTUzU3czl7tct/2n+wt3M/'.
+ 'NVM12SSTQQSyW2TA+QAJQogtYYFtyfrqL3WfvWj5g8AEjzfvhS'.
+ 'SXjk8//Zz3Pf3qCMcJAWxMKlT4kH+jwu/FknnJSUItKFHzCrKA'.
+ 'BggBQx5ziz/wn/yBz3hED4/oaJfSjgVoYjJJgTLTZCjohp7IGT'.
+ 'k5aZ4kb+bRTR30Q7djj8f/kpPMUSCFedRL6W8e8qMQNE6S4xpv'.
+ 'c5HrTPFubiJ3ZnlyOXV59rJYU5Z00h1c3d0brxAiUkScRijisk'.
+ '6XLTyiN3s8HuAJpniXa/q8/pt8Or+0kF8oXJm5YiydWcIpOrJu'.
+ 'rjOQwd54AQwsMpTJYhPSoYuLQ58An/DnBQSdImXO8avsTPbqpc'.
+ 'lLp67OXDVzMznZLGxSs2qyIRu4at8gKHQEC50kE1icxqCAdxST'.
+ 'xjEA44tqaJlERl8uLWvvnX5PHuQfcCdxh5qq0aX76vj4WgWyXO'.
+ 'QiNgBP8IAaddr08X8+wHFmJSQhBbPAZGoSZSt5wQs6qoNC7UEd'.
+ '4AEoLIQSCaCCy78Dv8Tiv1hjjW1CRj8XIAgEKqDtt9keboMJZa'.
+ 'vMjuzQVd3Xr9prTJo+GF/jKZea95R25Lxs8jg5qFGiwDnOS0mW'.
+ 'NE0rjNRIt3WbklUCA9mV3Zdz8OBT/JfCQLB0SKYVVjGFYSfx/E'.
+ '26ow4e6uDujlPFQpE0FU6P8qNTHdXJdEdda0qf0itWBVM3pa/3'.
+ 'ccUlIECJet0cAJoeYk5EZCeS5IwEoerSxccJBwRqFFf38QCTaO'.
+ 'TRVFKJm3NTbtLNSyh2IkhIXsvLCesEGNCWdmwyruSD/z9kUlRc'.
+ '3bqNlSxhJNJ43p5JITrOEis8Qtr0cXEpU/JT/pmO18n2vb42pU'.
+ '3JnDnHMBqyPlpnoAaxhr2llv1ZUBqEGlqYwDQMsskMOcMgVL3Y'.
+ 'ZOQTHAcQQiIGjHCwCaiovjrv4hbcpKuJJjIcDHm685RGr4GLCx'.
+ 'YHkAcrLoAoDSLBiAQrMkjqybHJCbxgh+7xAC1MpsgzwRwD3qHL'.
+ 'WyTIBdlAa6u2rHfXaew06PV78ZZjAwleNnkolECoH5i090wOcY'.
+ '+TgwYzFHiPi1zkOkXexeAMASnVU+LiyiA1wFUuaqggACLizeWw'.
+ 'ycMzyssmVYKkbpGyC5T+OUALk2mKLHKWf+ED/az+YW42d66YL+'.
+ 'aNrmEEzQCFEnKw368EgEvcN1m80eTIQIt0TFOjMJHkzNEBBYPp'.
+ 'sblf8QHzrORO5JaWZ5ZLl6cuJyyxpNPv4PZdoT+GyIxBfI5uUg'.
+ 'eJMCwP2/bIHO1JEudcgUUWOceKNq99mCvnzs5PzRcuTV4y5mRO'.
+ 'SMIjo47z5S7a94oQCNKgJsZwO7D/IDNg3/LLhRNXt4JohBb4aG'.
+ '82GLdXcf93mQ+Y43r2RHZp+cRy6cqJK4l8MS+tdItaqiYtc0Mm'.
+ 'QpfJARh98HYh9IiXVcaAo58wGb+LBAjbSPgCOcoSa0wzxXtc08'.
+ '/pv8mfyL+9MLVQvDJ1JVHJV6SZbFI1qtTsB+KlehRtRTGE8Afo'.
+ 'P4DRcAxiEudhAHjjzz+ubgX4oHowakHQOlqzICQwyVPITGVOXi'.
+ 'xfLF6aumzmczl5lHzMff2+fCdPaGttEkXoLQAO9B7C6EugPYby'.
+ 'gVPjGXc5eIbNAJPjGwiAbaAJUQv8wVG7GROkJFpyOqn/ovgLba'.
+ '44L0+sDaraXb6jzq7aBQWjBOyUoHcaopOgmaA3IRyNDZnA1HjO'.
+ 'HSBkr7eEFDAEngHrQCf+/s2A8cSiSkqcKUeeTjwFy2Jd78t3+L'.
+ 'TR4itIiBLwLQhzkJyB5Cx4HXDaENVQCBAQcRqFIHTRaBIvuYXg'.
+ 'AdsouuNxEL0ZUBHnSQp66R73zYfUtQ6OytKT8RckQAJQoLtgO5'.
+ 'BJgj0D/WfgdyHaAHx8THoUcbGx8ciwhUl3bDEiToURPooeI7pH'.
+ 'MziK9Yd9nU5a6GgKjOH41vsgI4hAcyC5AZkapF+AoYNrjjsuhx'.
+ 'FbtPmeB5ykyQQzTPAWAQWC8S9oAI0QRRuPb9jkmyMZNAOTklvC'.
+ 'GGYZaFkGmkVAh8h4DtKFMIBunG+pB5B5AIkGBDsQ+qBiL20caj'.
+ 'zhJknq5KlgMkLjJHJos4kYEbFJi5vc5eYbATVN02bNWe19+32t'.
+ 'aJWlFm3wbf8Rz5NbDFJdlOFBF/g7cBf0JkrbBb+F6j1DOduEkU'.
+ '8bWCOiSofPWadBnSZDWmgUkEMGhZCINut8S/0NBtPptFlZrBSu'.
+ 'vnt1+ndnflfIp9OJ/279Ubbbd+lP7KBKPoEBsgnqLph/BRzwdS'.
+ 'LnBUFvHcfdpRsGPAGqwMco6jynz+e0SPKYCHMfLX5VKHwcenR+'.
+ 'Igd1XTcqlUr+xn/cePv91fevzy8sLO2OtrOpWkqL7gXKSAVRdh'.
+ 'ZFEmEXoYkwBNqovoc/3GHH3aUR+jwC1oD/AWrANi4hGwyBzqEG'.
+ 'Vvb77Dgi0eT1VZzJZMxKpVJYXV1dXF1dXVm6sPSvruue3Xzcyj'.
+ '6/syvDzwj0lNazK6Fj5LFCRZouZpBABj6jXouu3+Np6HNvDHaf'.
+ 'g91t74msbMuOJicnSSaTKKUQEUQEpRSO69But1/dB0VEm5uby9'.
+ 'y4cWNpdXX1+sLCworrume//PuXpeqnVeOban0U1PW2kcx+O9L7'.
+ 'Te9sUB4lWFR9SqNtNGcHx+/RDD2+Am4D94CnQA8OjjlEhMnyJC'.
+ 'srK8zOzu7BiYioMAzZ2Njg9u3brwIqpSSXy2WXl5eXLly4sOo4'.
+ 'zoV6vV6oflrVP/7Tx8Hmw1Zb6ydqmpWp7ha8h4O3gjOhzVANmF'.
+ 'XPMNQWvdDnCXCXuHR+APqH4fbCtm2mp6eZn59H13WJuYXRaKSU'.
+ 'UiSTyVcBdV3XDcOwRaTU7/en19bWCn/79G+JL/76RbhZ22y7u+'.
+ '6ahl71nPDz/nO17m7wAxlabFOihy4+DvAcqAMbPzZ3OFzX5dmz'.
+ 'Z2iahoiosUUVhiGNRgPHcV4GzGQy5uLiYuH8+fMzo9FoslarJW'.
+ '9+elP75E+fBJu1zY7qqpqBUW3T/niohnVvy+1zm5aVtp+WE2XT'.
+ 'nrHFzbjh1tYLz3XdPjD4R3BKKba2tqhWq4dzUO3noBPn4H5PKy'.
+ 'LaO++8U7hx48byhQsXVne7u6tf3/v64t3P7mbq9+odt+OuaWi3'.
+ 'PLxbW2ytubjbQCgiMnt6VlaurWgz0zM0m02q1WrUaDSUUuqI56'.
+ 'ivDxE5MCgiYllWtlwuL5mmufLV/a/O/uXPf9Ff1F+80Lv6Yx29'.
+ '2qHzyZBh3cdvc7gaTZuZkzPh/Py8ACqVSv1/uPZDKXUAGEWRtF'.
+ 'qtxEcffZTL5XLF+2v39fqjeivshA/TpP83JLwzYFBzcA4370Cc'.
+ 'S81nTRBUs9lkOByi1GuOPI4Rh3+26JZlnSkWi781DOPXvV4v3+'.
+ '/2G0R8kSBxB/jew+tERK+c49m2TblcxrZtXNfl+fPneJ6HZVmU'.
+ 'y2VJJpNyaJ9TSinlOA5bW1u4rntkQA0oAG8D54gb9W3ianxM3A'.
+ 'e/cn73U3Hq1Cm5du2aPjs7a+ztcSIShmE4ajQa6tatWzQajZ+0'.
+ 'fbiKI+It4SvijVUj7kL2qvGfgkskEqTTaZmcnDROnTplJhIJTU'.
+ 'QiwPd9P/Q8T6XTaQzDIAiCfzjP/wFVfszuFqdHXgAAAABJRU5E'.
+ 'rkJggg==' ;
+
+
+//==========================================================
+// File: pp_red.png
+//==========================================================
+ $this->imgdata_small[0][0]= 384 ;
+ $this->imgdata_small[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
+ 'B3RJTUUH0wMJFhouFobZrQAAAQ1JREFUeJyV1dFtwyAQBuD/og'.
+ 'xQdYxa8gRY6hJ0jK6QdohMkTEuE5wUj5ERen05IoLvID7Jkn2G'.
+ 'j8MgTMyMXqRlUQBYq9ydmaL2h1cwqD7l30t+L1iwlbYFRegY7I'.
+ 'SHjkEifGg4ww3aBa/l4+9AhxWWr/dLhEunXUGHq6yGniw3QkOw'.
+ '3jJ7UBd82n/VVAlAtvsfp98lAj2sAJOhU4AeQ7DC1ubVBODWDJ'.
+ 'TtCsEWa6u5M1NeFs1NzgdtuhHGtj+9Q2IDppQUAL6Cyrlz0gDN'.
+ 'ohSMiJCt861672EiAhEhESG3woJ9V9OKTkwRKbdqz4cHmFLSFg'.
+ 's69+LvAZKdeZ/n89uLnd2g0S+gjd5g8zzjH5Y/eLLi+NPEAAAA'.
+ 'AElFTkSuQmCC' ;
+
+//==========================================================
+// File: pp_orange.png
+//==========================================================
+ $this->imgdata_small[1][0]= 403 ;
+ $this->imgdata_small[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
+ 'B3RJTUUH0wMJFhwAnApz5AAAASBJREFUeJyN1dFthDAMBuDf7S'.
+ '3BCm2VCRKpS4QxbhikW6IewzcBqm6Fm6JyH7iEEByCn5AJH38g'.
+ 'BBIRHNUzBAWAGNfe/SrUGv92CtNt309BrfFdMGPjvt9CD8Fyml'.
+ 'ZZaDchRgA/59FDMD18pvNoNyHxMnUmgLmPHoJ+CqqfMaNAH22C'.
+ 'fgqKRwR+GRpxGjXBEiuXDBWQhTK3plxijyWWvtKVS5KNG1xM8I'.
+ 'OBr7geV1WupDqpmTAPKjCqLhxk/z0PImQmjKrAuI6vMXlhFroD'.
+ 'vfdqITXWqg2YMSJEAFcReoag6UXU2DzPG8w5t09YYsAyLWvHrL'.
+ 'HUy6D3XmvMAAhAay8kAJpBosX4vt0G4+4Jam6s6Rz1fgFG0ncA'.
+ 'f3XfOQcA+Acv5IUSdQw9hgAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: pp_pink.png
+//==========================================================
+ $this->imgdata_small[2][0]= 419 ;
+ $this->imgdata_small[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
+ 'B3RJTUUH0wMJFhsQzvz1RwAAATBJREFUeJyd1MFthDAQheF/oi'.
+ 'gF+JYWQKICkCJRA1vGtrDbxFbhGvY0HVjCLeS2BeTiHFgTB2wg'.
+ 'eRISstCnmcG2qCpbuXf3ADBQzWsPfZfS9y9HsEu4/Fo33Wf4Fx'.
+ 'gxL3a1XkI3wbTNXHLoboVeLFUYDqObYBy+Fw/Uh9DdCmtOwIjF'.
+ 'YvG76CZoOhNGRmpO8zz30CJoOhMAqlDxFzQLppgXj2XaNlP7FF'.
+ 'GLL7ccMYCBgZERgCvXLBrfi2DEclmiKZwFY4tp6sW26bVfnede'.
+ 'e5Hc5dC2bUgrXGKqWrwcXnNYDjmCrcCIiQgDcFYV05kQ8SXmnB'.
+ 'NgPiVN06wrTDGAhz5EWY/FOccTk+cTnHM/YNu2YYllgFxCWuUM'.
+ 'ikzGx+2Gc+4N+CoJW8n+5a2UKm2aBoBvGA6L7wfl8aoAAAAASU'.
+ 'VORK5CYII=' ;
+
+
+//==========================================================
+// File: pp_blue.png
+//==========================================================
+ $this->imgdata_small[3][0]= 883 ;
+ $this->imgdata_small[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAACi1'.
+ 'BMVEX///8AAAAAADMAAGYAAJkAAMwAAP8zAAAzADMzAGYzAJkz'.
+ 'AMwzAP9mAABmADNmAGZmAJlmAMxmAP+ZAACZADOZAGaZAJmZAM'.
+ 'yZAP/MAADMADPMAGbMAJnMAMzMAP//AAD/ADP/AGb/AJn/AMz/'.
+ 'AP8AMwAAMzMAM2YAM5kAM8wAM/8zMwAzMzMzM2YzM5kzM8wzM/'.
+ '9mMwBmMzNmM2ZmM5lmM8xmM/+ZMwCZMzOZM2aZM5mZM8yZM//M'.
+ 'MwDMMzPMM2bMM5nMM8zMM///MwD/MzP/M2b/M5n/M8z/M/8AZg'.
+ 'AAZjMAZmYAZpkAZswAZv8zZgAzZjMzZmYzZpkzZswzZv9mZgBm'.
+ 'ZjNmZmZmZplmZsxmZv+ZZgCZZjOZZmaZZpmZZsyZZv/MZgDMZj'.
+ 'PMZmbMZpnMZszMZv//ZgD/ZjP/Zmb/Zpn/Zsz/Zv8AmQAAmTMA'.
+ 'mWYAmZkAmcwAmf8zmQAzmTMzmWYzmZkzmcwzmf9mmQBmmTNmmW'.
+ 'ZmmZlmmcxmmf+ZmQCZmTOZmWaZmZmZmcyZmf/MmQDMmTPMmWbM'.
+ 'mZnMmczMmf//mQD/mTP/mWb/mZn/mcz/mf8AzAAAzDMAzGYAzJ'.
+ 'kAzMwAzP8zzAAzzDMzzGYzzJkzzMwzzP9mzABmzDNmzGZmzJlm'.
+ 'zMxmzP+ZzACZzDOZzGaZzJmZzMyZzP/MzADMzDPMzGbMzJnMzM'.
+ 'zMzP//zAD/zDP/zGb/zJn/zMz/zP8A/wAA/zMA/2YA/5kA/8wA'.
+ '//8z/wAz/zMz/2Yz/5kz/8wz//9m/wBm/zNm/2Zm/5lm/8xm//'.
+ '+Z/wCZ/zOZ/2aZ/5mZ/8yZ///M/wDM/zPM/2bM/5nM/8zM////'.
+ '/wD//zP//2b//5n//8z///9jJVUgAAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
+ 'RQfTAwkWGTNerea3AAAAYUlEQVR4nHXNwQ3AIAxDUUfyoROxRZ'.
+ 'icARin0EBTIP3Hp1gBRqSqYo0seqjZpnngojlWBir5+b8o06lM'.
+ 'ha5uFKEpDZulV8l52axhVzqaCdxQp32qVSSwC1wN3fYiw7b76w'.
+ 'bN4SMue4/KbwAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: pp_green.png
+//==========================================================
+ $this->imgdata_small[4][0]= 447 ;
+ $this->imgdata_small[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
+ 'B3RJTUUH0wMJFhkLdq9eKQAAAUxJREFUeJyN1LFVwzAQxvH/8f'.
+ 'IeDS0FLKABlN6eIwPYAzCHB0gWYI2jj+i1ABUTQN4TRSQ7iiWZ'.
+ 'qxLn9Mt9ydmiqrSq930AYFiu6YdKrf/hP1gYQn6960PxwBaYMG'.
+ 'E9UA3dBFtVQjdBOQmBakLennK0CapRwbZRZ3N0O/IeEsqp3HKL'.
+ 'Smtt5pUZgTPg4gdDud+6xoS97wM2rsxxmRSoTgoVcMZsXJkBho'.
+ 'SmKqCuOuEtls6nmGMFPTUmxBKx/MeyNfQGLoOOiC2ddsxb1Kzv'.
+ 'ZzUqu5IXbGDvBJf+hDisi77qFSuhq7Xpuu66TyJLRGbsXVUPxV'.
+ 'SxsgkzDMt0mKT3/RcjL8C5hHnvJToXY0xYRZ4xnVKsV/S+a8YA'.
+ 'AvCb3s9g13UhYj+TTo93B3fApRV1FVlEAD6H42DjN9/WvzDYuJ'.
+ 'dL5b1/ji+/IX8EGWP4AwRii8PdFHTqAAAAAElFTkSuQmCC' ;
+ }
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//=======================================================================
+// File: IMGDATA_SQUARES.INC
+// Description: Base64 encoded images for squares
+// Created: 2003-03-20
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: imgdata_squares.inc,v 1.1 2003/03/23 13:37:37 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+class ImgData_Squares extends ImgData {
+ var $name = 'Squares';
+ var $an = array(MARK_IMG_SQUARE =>'imgdata');
+
+ var $colors = array('bluegreen','blue','green',
+ 'lightblue','orange','purple','red','yellow');
+ var $index = array('bluegreen' =>2,'blue'=>5,'green'=>6,
+ 'lightblue'=>0,'orange'=>7,'purple'=>4,'red'=>3,'yellow'=>1);
+ var $maxidx = 7 ;
+ var $imgdata ;
+
+ function ImgData_Squares () {
+//==========================================================
+//sq_lblue.png
+//==========================================================
+ $this->imgdata[0][0]= 362 ;
+ $this->imgdata[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAIAAADZrBkAAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMLFgojiPx/ygAAAPdJREFUeNpj/P377+kzHx89/c'.
+ 'VAHNBQ5VBX52HavPWWjg6nnDQbkXoUFTnnL7zD9PPXrz17HxCj'.
+ 'E6Jn6fL7H7/+ZWJgYCBGJ7IeBgYGJogofp1oehDa8OjE1IOiDa'.
+ 'tOrHoYGBhY0NwD0enirMDAwMDFxYRVD7ptyDrNTAU0NXix6sGu'.
+ 'jYGBgZOT9e/f/0xMjFyczFgVsGAKCfBza2kKzpl3hIuT1c9Xb/'.
+ 'PW58/foKchJqx6tmy98vbjj8cvPm/afMnXW1JShA2fNmQ9EBFc'.
+ 'Opnw6MGjkwm/Hlw6mQjqwaqTiRg9mDoZv//4M2/+UYJ64EBWgj'.
+ 'cm2hwA8l24oNDl+DMAAAAASUVORK5CYII=' ;
+
+//==========================================================
+//sq_yellow.png
+//==========================================================
+ $this->imgdata[1][0]= 338 ;
+ $this->imgdata[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAWl'.
+ 'BMVEX////+/+H+/9/9/9v8/8P8/8H8/7v8/7n6/4P5/335/3n5'.
+ '/3X4/1f4/1P3/031/w30/wn0/wPt+ADp9ADm8ADk7gDc5gDa5A'.
+ 'DL1ADFzgCwuACqsgClrABzeAC9M0MzAAAAAWJLR0QAiAUdSAAA'.
+ 'AAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9MDCxYEDlOgDj'.
+ 'EAAAB+SURBVHjaVcpbCsQgDEDRGERGKopjDa2a/W9zfLWj9/Nw'.
+ 'Ac21ZRBOtZlRN9ApzSYFaDUj79KIorRDbJNO9bN/GUSh2ZRJFJ'.
+ 'S18iorURBiyksO8buT0zkfYaUqzI91ckfhWhoGXTLzsDjI68Sz'.
+ 'pGMjrzPzauA/iXk1AtykmvgBC8UcWUdc9HkAAAAASUVORK5CYI'.
+ 'I=' ;
+
+//==========================================================
+//sq_blgr.png
+//==========================================================
+ $this->imgdata[2][0]= 347 ;
+ $this->imgdata[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAZl'.
+ 'BMVEX////0+vv0+vrz+fry+frv+Png7e/d7e/a6+zY6+250tSz'.
+ '0tSyztCtztGM0NWIz9SDzdNfsLVcrrRZrbJOp61MpqtIr7dHn6'.
+ 'RErrZArLQ6q7M2g4kygYcsp68npa4ctr8QZ20JnqepKsl4AAAA'.
+ 'AWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU'.
+ '1FB9MDCxYEByp8tpUAAAB7SURBVHjaVcjRFoIgDADQWZpWJpjY'.
+ 'MsnG//9kzIFn3McLzfArDA3MndFjrhvgfDHFBEB9pt0CVzwrY3'.
+ 'n2yicjhY4vTSp0nbXtN+hCV53SHDWe61dZY+/9463r2XuifHAM'.
+ '0SoH+6xEcovUlCfefeFSIwfTTQ3fB+pi4lV/bTIgvmaA7a0AAA'.
+ 'AASUVORK5CYII=' ;
+
+//==========================================================
+//sq_red.png
+//==========================================================
+ $this->imgdata[3][0]= 324 ;
+ $this->imgdata[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAXV'.
+ 'BMVEX////++Pn99/j99ff99fb98/X98/T98PL55uj43+P24+bw'.
+ 'kKPvjaHviJ3teJHpxMnoL2Pjs73WW3rWNljVWXnUVnbUK1DTJk'.
+ '3SUHPOBz/KQmmxPVmuOFasNFOeIkWVka/fAAAAAWJLR0QAiAUd'.
+ 'SAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9MDCxYEHd'.
+ 'ceT+8AAABtSURBVHjaVchbAkMwEAXQq6i3VrQiQfa/zDYTw8z5'.
+ 'PCjGt9JVWFt1XWPh1fWNdfDy+tq6WPfRUPENNKnSnXNWPB4uv2'.
+ 'b54nSZ8jHrMtOxvWZZZtpD4KP6xLkO9/AhzhaCOMhJh68cOjzV'.
+ '/K/4Ac2cG+nBcaRuAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+//sq_pink.png
+//==========================================================
+ $this->imgdata[4][0]= 445 ;
+ $this->imgdata[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAApV'.
+ 'BMVEX////6+Pz69/v49Pr38/r17/jr4+/l3Onj2efh1ua/L+i+'.
+ 'q8m+Lue9Lua8qsS8LuW8LeS7pca5LOG4LN+2Y9O2YNW1ZdO1Kt'.
+ 'y0atC0aNGzb82zbc6zKtuzKdqycsuwa8qtJtOISZ2GRpuFN6GE'.
+ 'NqCDQpmCMZ+BPpd/LJ1/K519S5B9Jpx9Jpt9JZt6RY11BJZ1BJ'.
+ 'V0BJV0BJRzBJNvNoRtIoJUEmdZ/XbrAAAAAWJLR0QAiAUdSAAA'.
+ 'AAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9MDCxYDF3iKMD'.
+ 'YAAACeSURBVHjaVczbEoIgGARgCiMtrexoWpaa2FHUgvd/tH4Y'.
+ 'BnEvv9ldhNPradPnnGBUTtPDzMRPSIF46SaBoR25dYjz3I20Lb'.
+ 'ek6BgQz73Il7KKpSgCO0pTHU0886J1sCe0ZYbALjGhjFnEM2es'.
+ 'VhZVI4d+B1QtfnV47ywCEaKeP/p7JdLejSYt0j6NIiOq1wJZIs'.
+ 'QTDA0ELHwhPBCwyR/Cni9cOmzJtwAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+//sq_blue.png
+//==========================================================
+ $this->imgdata[5][0]= 283 ;
+ $this->imgdata[5][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAQl'.
+ 'BMVEX////4+fz39/z19vvy8vru7/ni4+7g4fHW1ue8vteXmt6B'.
+ 'hdhiZ7FQVaZETcxCSJo1Oq4zNoMjKakhJHcKFaMEC2jRVYdWAA'.
+ 'AAAWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0'.
+ 'SU1FB9MDCxYDN0PkEP4AAABfSURBVHjaVchHAoAgDATAVcCCIF'.
+ 'j4/1elJEjmOFDHKVgDv4iz640gLs+LMF6ZUv/VqcXXplU7Gqpy'.
+ 'PFzBT5qml9NzlOX259riWHlS4kOffviHD8PQYZx2EFMPRkw+9Q'.
+ 'FSnRPeWEDzKAAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+//sq_green.png
+//==========================================================
+ $this->imgdata[6][0]= 325 ;
+ $this->imgdata[6][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAXV'.
+ 'BMVEX////2+vX1+vX1+fT0+fPz+PPx9/Dv9u7u9e3h7uHe697a'.
+ '6dnO2s3I1sa10LOvza2ay5aEwYBWlE9TqE5Tkk1RkEpMrUJMg0'.
+ 'hKiUNGpEFBojw8oTcsbScaYBMWlwmMT0NtAAAAAWJLR0QAiAUd'.
+ 'SAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9MDCxYEFd'.
+ 'nFx90AAABuSURBVHjaVc9HAoAgDADB2HuJWLDx/2cKBITscW4L'.
+ '5byzMIWtZobNDZIZtrcCGZsRQ8GwvRSRNxIiMuysODKG3alikl'.
+ 'ueOPlpKTLBaRmOZxQxaXlfb5ZWI9om4WntrXiDSJzp7SBkwMQa'.
+ 'FEy0VR/NAB2kNuj7rgAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+//sq_orange.png
+//==========================================================
+ $this->imgdata[7][0]= 321 ;
+ $this->imgdata[7][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAUV'.
+ 'BMVEX/////8+n/8uf/8OP/59H/5Mv/zqH/zJ3/ypv/yJf/vYH/'.
+ 'u33/uXn/n0n/nUX/m0H/lzn/ljf/lDP/kS3/kCv/iR//hxv/fg'.
+ 'n/fAX/eQDYZgDW6ia5AAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAL'.
+ 'EgAACxIB0t1+/AAAAAd0SU1FB9MDCxYEJIgbx+cAAAB2SURBVH'.
+ 'jaVczRCoQwDETRbLAWLZSGUA35/w/dVI0283i4DODew3YESmWW'.
+ 'kg5gWkoQAe6TleUQI/66Sy7i56+kLk7cht2N0+hcnJgQu0SqiC'.
+ '1SzSIbzWSi6gavqJ63wSduRi2f+kwyD5rEukwCdZ1kGAMGMfv9'.
+ 'AbWuGMOr5COSAAAAAElFTkSuQmCC' ;
+ }
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//=======================================================================
+// File: IMGDATA_STARS.INC
+// Description: Base64 encoded images for stars
+// Created: 2003-03-20
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: imgdata_stars.inc,v 1.1 2003/03/23 13:37:37 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+
+class ImgData_Stars extends ImgData {
+ var $name = 'Stars';
+ var $an = array(MARK_IMG_STAR => 'imgdata');
+
+ var $colors = array('bluegreen','lightblue','purple','blue','green','pink','red','yellow');
+ var $index = array('bluegreen'=>3,'lightblue'=>4,'purple'=>1,
+ 'blue'=>5,'green'=>0,'pink'=>7,'red'=>2,'yellow'=>6);
+ var $maxidx = 7 ;
+ var $imgdata ;
+
+ function ImgData_Stars() {
+//==========================================================
+// File: bstar_green_001.png
+//==========================================================
+ $this->imgdata[0][0]= 329 ;
+ $this->imgdata[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAAUV'.
+ 'BMVEX///////+/v7+83rqcyY2Q/4R7/15y/1tp/05p/0lg/zdX'.
+ '/zdX/zVV/zdO/zFJ9TFJvDFD4yg+8Bw+3iU68hwurhYotxYosx'.
+ 'YokBoTfwANgQFUp7DWAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF'.
+ 'HUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJj'.
+ 'CRyxgTAAAAcUlEQVR4nH3MSw6AIAwEUBL/IKBWwXL/g0pLojUS'.
+ 'ZzGLl8ko9Zumhr5iy66/GH0dp49llNPB5sTotDY5PVuLG6tnM9'.
+ 'CVKSIe1joSgPsAKSuANNaENFQvTAGzmheSkUpMBWeJZwqBT8wo'.
+ 'hmysD4bnnPsC/x8ItUdGPfAAAAAASUVORK5CYII=' ;
+//==========================================================
+// File: bstar_blred.png
+//==========================================================
+ $this->imgdata[1][0]= 325 ;
+ $this->imgdata[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
+ 'BMVEX///+/v79uRJ6jWPOSUtKrb+ejWO+gWPaGTruJTr6rZvF2'.
+ 'RqC2ocqdVuCeV+egV/GsnLuIXL66rMSpcOyATbipY/OdWOp+VK'.
+ 'aTU9WhV+yJKBoLAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJwynv1'.
+ 'XVAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
+ 'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
+ 'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
+ 'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bstar_red_001.png
+//==========================================================
+ $this->imgdata[2][0]= 325 ;
+ $this->imgdata[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
+ 'BMVEX///+/v7+eRFHzWG3SUmHnb37vWGr2WHG7Tlm+TljxZneg'.
+ 'Rk3KoaXgVmXnV2nxV227nJ++XGzErK3scIS4TVzzY3fqWG2mVF'.
+ 'zVU2PsV2rJFw9VAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJzCI0C'.
+ 'lSAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
+ 'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
+ 'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
+ 'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bstar_blgr_001.png
+//==========================================================
+ $this->imgdata[3][0]= 325 ;
+ $this->imgdata[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
+ 'BMVEX///+/v79Ehp5Yx/NSq9Jvw+dYwu9YzfZOmbtOmb5myPFG'.
+ 'gqChvcpWteBXvedXxvGcsbtcpb6su8RwzOxNmrhjyvNYwupUjK'.
+ 'ZTr9VXwOyJhmWNAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJTC65k'.
+ 'vQAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
+ 'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
+ 'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
+ 'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bstar_blgr_002.png
+//==========================================================
+ $this->imgdata[4][0]= 325 ;
+ $this->imgdata[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
+ 'BMVEX///+/v79EnpxY8/FS0dJv5+dY7+9Y9vBOubtOur5m8fFG'.
+ 'nKChycpW3uBX5+ZX8e2curtcvrqswsRw7OdNuLZj8/BY6udUpK'.
+ 'ZT1dRX7OtNkrW5AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJgXHeN'.
+ 'wwAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
+ 'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
+ 'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
+ 'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bstar_blue_001.png
+//==========================================================
+ $this->imgdata[5][0]= 325 ;
+ $this->imgdata[5][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
+ 'BMVEX///+/v79EY55Yi/NSetJvledYiO9YkPZOb7tObr5mkvFG'.
+ 'X6ChrcpWgOBXhedXi/Gcpbtcf76sssRwnOxNcbhjk/NYiepUbK'.
+ 'ZTfdVXh+ynNEzzAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJhStyP'.
+ 'zCAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
+ 'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
+ 'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
+ 'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bstar_oy_007.png
+//==========================================================
+ $this->imgdata[6][0]= 325 ;
+ $this->imgdata[6][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
+ 'BMVEX///+/v7+ejUTz11jSvVLn02/v1lj21li7q06+r07x2mag'.
+ 'lUbKxKHgy1bnz1fx1Ve7t5y+qlzEwqzs03C4pE3z2WPqz1imml'.
+ 'TVv1Ps01dGRjeyAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJjsGGc'.
+ 'GbAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
+ 'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
+ 'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
+ 'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bstar_lred.png
+//==========================================================
+ $this->imgdata[7][0]= 325 ;
+ $this->imgdata[7][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
+ 'BMVEX///+/v7+eRJPzWN3SUr7nb9TvWNj2WOS7Tqi+TqnxZtyg'.
+ 'Ro/KocPgVsjnV9LxV927nLa+XLTErL7scN24TarzY9/qWNemVJ'.
+ 'jVU8LsV9VCwcc9AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJxi9ZY'.
+ 'GoAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
+ 'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
+ 'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
+ 'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
+ }
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//=======================================================================
+// File: JPGRAPH.PHP
+// Description: PHP4 Graph Plotting library. Base module.
+// Created: 2001-01-08
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph.php,v 1.242.2.21 2003/08/22 23:56:11 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2001,2002,2003 Johan Persson
+//========================================================================
+
+//------------------------------------------------------------------------
+// Directories for cache and font directory.
+// Leave them undefined to use default values.
+//
+// Default values used if these defines are left commented out are:
+//
+// UNIX:
+// CACHE_DIR = /tmp/jpgraph_cache/
+// TTF_DIR = /usr/X11R6/lib/X11/fonts/truetype/
+//
+// WINDOWS:
+// CACHE_DIR = $SERVER_TEMP/jpgraph_cache/
+// TTF_DIR = $SERVER_SYSTEMROOT/fonts/
+//
+//
+//------------------------------------------------------------------------
+
+// The full absolute name of the directory to be used to store the
+// cached image files. This directory will not be used if the USE_CACHE
+// define (further down) is false. If you enable the cache please note that
+// this directory MUST be readable and writable for the process running PHP.
+// Must end with '/'
+// DEFINE("CACHE_DIR","/tmp/jpgraph_cache/");
+
+// Directory for jpGraph TTF fonts. Must end with '/'
+// DEFINE("TTF_DIR","/usr/X11R6/lib/X11/fonts/truetype/");
+
+
+//-------------------------------------------------------------------------
+// Cache directory specification for use with CSIM graphs that are
+// using the cache.
+// The directory must be the filesysystem name as seen by PHP
+// and the 'http' version must be the same directory but as
+// seen by the HTTP server relative to the 'htdocs' ddirectory.
+// If a relative path is specified it is taken to be relative from where
+// the image script is executed.
+// Note: The default setting is to create a subdirectory in the
+// directory from where the image script is executed and store all files
+// there. As ususal this directory must be writeable by the PHP process.
+DEFINE("CSIMCACHE_DIR","csimcache/");
+DEFINE("CSIMCACHE_HTTP_DIR","csimcache/");
+
+//------------------------------------------------------------------------
+// Various JpGraph Settings. Adjust accordingly to your
+// preferences. Note that cache functionality is turned off by
+// default (Enable by setting USE_CACHE to true)
+//------------------------------------------------------------------------
+
+// Deafult graphic format set to "auto" which will automatically
+// choose the best available format in the order png,gif,jpg
+// (The supported format depends on what your PHP installation supports)
+DEFINE("DEFAULT_GFORMAT","auto");
+
+// Should the image be a truecolor image?
+// Note 1: Has only effect with GD 2.0.1 and above.
+// Note 2: GD 2.0.1 + PHP 4.0.6 on Win32 crashes when trying to use
+// trucolor. Truecolor support is to be considered alpha since GD 2.x
+// is still not considered stable (especially on Win32).
+// Note 3: MUST be enabled to get background images working with GD2
+// Note 4: If enabled then truetype fonts will look very ugly with GD 2.0.1
+// => You can't have both background images and truetype fonts in the same
+// image until these bugs has been fixed in GD 2.01. There is a patch
+// available for GD 2.0.1 though. See the README file.
+DEFINE('USE_TRUECOLOR',true);
+
+// Specify what version of the GD library is installed.
+// If this is set to 'auto' the version will be automatically
+// determined.
+// However since determining the library takes ~1ms you can also
+// manually specify the version if you know what version you have.
+// This means that you should
+// set this define to true if you have GD 2.x installed to save 1ms.
+DEFINE("USE_LIBRARY_GD2",'auto');
+
+// Should the cache be used at all? By setting this to false no
+// files will be generated in the cache directory.
+// The difference from READ_CACHE being that setting READ_CACHE to
+// false will still create the image in the cache directory
+// just not use it. By setting USE_CACHE=false no files will even
+// be generated in the cache directory.
+DEFINE("USE_CACHE",false);
+
+// Should we try to find an image in the cache before generating it?
+// Set this define to false to bypass the reading of the cache and always
+// regenerate the image. Note that even if reading the cache is
+// disabled the cached will still be updated with the newly generated
+// image. Set also "USE_CACHE" below.
+DEFINE("READ_CACHE",true);
+
+// Determine if the error handler should be image based or purely
+// text based. Image based makes it easier since the script will
+// always return an image even in case of errors.
+DEFINE("USE_IMAGE_ERROR_HANDLER",true);
+
+// Determine if the library should also setup the default PHP
+// error handler to generate a graphic error mesage. This is useful
+// during development to be able to see the error message as an image
+// instead as a "red-cross" in a page where an image is expected.
+DEFINE("INSTALL_PHP_ERR_HANDLER",false);
+
+// If the color palette is full should JpGraph try to allocate
+// the closest match? If you plan on using background images or
+// gradient fills it might be a good idea to enable this.
+// If not you will otherwise get an error saying that the color palette is
+// exhausted. The drawback of using approximations is that the colors
+// might not be exactly what you specified.
+// Note1: This does only apply to paletted images, not truecolor
+// images since they don't have the limitations of maximum number
+// of colors.
+DEFINE("USE_APPROX_COLORS",true);
+
+// Special unicode cyrillic language support
+DEFINE("LANGUAGE_CYRILLIC",false);
+
+// If you are setting this config to true the conversion
+// will assume that the input text is windows 1251, if
+// false it will assume koi8-r
+DEFINE("CYRILLIC_FROM_WINDOWS",false);
+
+// Should usage of deprecated functions and parameters give a fatal error?
+// (Useful to check if code is future proof.)
+DEFINE("ERR_DEPRECATED",true);
+
+// Should the time taken to generate each picture be branded to the lower
+// left in corner in each generated image? Useful for performace measurements
+// generating graphs
+DEFINE("BRAND_TIMING",false);
+
+// What format should be used for the timing string?
+DEFINE("BRAND_TIME_FORMAT","(%01.3fs)");
+
+//------------------------------------------------------------------------
+// The following constants should rarely have to be changed !
+//------------------------------------------------------------------------
+
+// What group should the cached file belong to
+// (Set to "" will give the default group for the "PHP-user")
+// Please note that the Apache user must be a member of the
+// specified group since otherwise it is impossible for Apache
+// to set the specified group.
+DEFINE("CACHE_FILE_GROUP","wwwadmin");
+
+// What permissions should the cached file have
+// (Set to "" will give the default persmissions for the "PHP-user")
+DEFINE("CACHE_FILE_MOD",0664);
+
+// Decide if we should use the bresenham circle algorithm or the
+// built in Arc(). Bresenham gives better visual apperance of circles
+// but is more CPU intensive and slower then the built in Arc() function
+// in GD. Turned off by default for speed
+DEFINE("USE_BRESENHAM",false);
+
+// Special file name to indicate that we only want to calc
+// the image map in the call to Graph::Stroke() used
+// internally from the GetHTMLCSIM() method.
+DEFINE("_CSIM_SPECIALFILE","_csim_special_");
+
+// HTTP GET argument that is used with image map
+// to indicate to the script to just generate the image
+// and not the full CSIM HTML page.
+DEFINE("_CSIM_DISPLAY","_jpg_csimd");
+
+// Special filename for Graph::Stroke(). If this filename is given
+// then the image will NOT be streamed to browser of file. Instead the
+// Stroke call will return the handler for the created GD image.
+DEFINE("_IMG_HANDLER","__handle");
+
+// DON'T SET THIS FLAG YORSELF THIS IS ONLY FOR INTERNAL TESTING
+// PURPOSES. ENABLING THIS FLAG WILL MAKE SOME OF YOUR SCRIPT
+// STOP WORKING
+// Enable some extra debug information for CSIM etc to be shown.
+DEFINE("JPG_DEBUG",false);
+
+// Version info
+DEFINE('JPG_VERSION','1.12');
+
+//------------------------------------------------------------------------
+// Automatic settings of path for cache and font directory
+// if they have not been previously specified
+//------------------------------------------------------------------------
+if (!defined('CACHE_DIR')) {
+ if ( strstr( PHP_OS, 'WIN') ) {
+ if( empty($_SERVER['TEMP']) ) {
+ die('JpGraph Error: No path specified for CACHE_DIR. Please specify a path for that DEFINE in jpgraph.php');
+ }
+ else {
+ DEFINE('CACHE_DIR', $_SERVER['TEMP'] . '/');
+ }
+ } else {
+ DEFINE('CACHE_DIR','/tmp/jpgraph_cache/');
+ }
+}
+
+if (!defined('TTF_DIR')) {
+ if (strstr( PHP_OS, 'WIN') ) {
+ if( empty($_SERVER['SystemRoot']) ) {
+ die('JpGraph Error: No path specified for TTF_DIR. Please specify a path for that DEFINE in jpgraph.php');
+ }
+ else {
+ DEFINE('TTF_DIR', $_SERVER['SystemRoot'] . '/fonts/');
+ }
+ } else {
+ DEFINE('TTF_DIR','/usr/X11R6/lib/X11/fonts/truetype/');
+ }
+}
+
+//------------------------------------------------------------------
+// Constants which are used as parameters for the method calls
+//------------------------------------------------------------------
+
+// TTF Font families
+DEFINE("FF_COURIER",10);
+DEFINE("FF_VERDANA",11);
+DEFINE("FF_TIMES",12);
+DEFINE("FF_COMIC",14);
+DEFINE("FF_ARIAL",15);
+DEFINE("FF_GEORGIA",16);
+DEFINE("FF_TREBUCHE",17);
+
+// Chinese font
+DEFINE("FF_SIMSUN",18);
+
+// Gnome Vera font
+// Available from http://www.gnome.org/fonts/
+DEFINE("FF_VERA",19);
+DEFINE("FF_VERAMONO",20);
+DEFINE("FF_VERASERIF",21);
+
+
+// Older deprecated fonts
+DEFINE("FF_BOOK",91); // Deprecated fonts from 1.9
+DEFINE("FF_HANDWRT",92); // Deprecated fonts from 1.9
+
+// TTF Font styles
+DEFINE("FS_NORMAL",9001);
+DEFINE("FS_BOLD",9002);
+DEFINE("FS_ITALIC",9003);
+DEFINE("FS_BOLDIT",9004);
+DEFINE("FS_BOLDITALIC",9004);
+
+//Definitions for internal font, new style
+DEFINE("FF_FONT0",1);
+DEFINE("FF_FONT1",2);
+DEFINE("FF_FONT2",4);
+
+//Definitions for internal font, old style
+// (Only defined here to be able to generate an error mesage
+// when used)
+DEFINE("FONT0",99); // Deprecated from 1.2
+DEFINE("FONT1",98); // Deprecated from 1.2
+DEFINE("FONT1_BOLD",97); // Deprecated from 1.2
+DEFINE("FONT2",96); // Deprecated from 1.2
+DEFINE("FONT2_BOLD",95); // Deprecated from 1.2
+
+// Tick density
+DEFINE("TICKD_DENSE",1);
+DEFINE("TICKD_NORMAL",2);
+DEFINE("TICKD_SPARSE",3);
+DEFINE("TICKD_VERYSPARSE",4);
+
+// Side for ticks and labels.
+DEFINE("SIDE_LEFT",-1);
+DEFINE("SIDE_RIGHT",1);
+DEFINE("SIDE_DOWN",-1);
+DEFINE("SIDE_BOTTOM",-1);
+DEFINE("SIDE_UP",1);
+DEFINE("SIDE_TOP",1);
+
+// Legend type stacked vertical or horizontal
+DEFINE("LEGEND_VERT",0);
+DEFINE("LEGEND_HOR",1);
+
+// Mark types for plot marks
+DEFINE("MARK_SQUARE",1);
+DEFINE("MARK_UTRIANGLE",2);
+DEFINE("MARK_DTRIANGLE",3);
+DEFINE("MARK_DIAMOND",4);
+DEFINE("MARK_CIRCLE",5);
+DEFINE("MARK_FILLEDCIRCLE",6);
+DEFINE("MARK_CROSS",7);
+DEFINE("MARK_STAR",8);
+DEFINE("MARK_X",9);
+DEFINE("MARK_LEFTTRIANGLE",10);
+DEFINE("MARK_RIGHTTRIANGLE",11);
+DEFINE("MARK_FLASH",12);
+DEFINE("MARK_IMG",13);
+DEFINE("MARK_FLAG1",14);
+DEFINE("MARK_FLAG2",15);
+DEFINE("MARK_FLAG3",16);
+DEFINE("MARK_FLAG4",17);
+
+// Builtin images
+DEFINE("MARK_IMG_PUSHPIN",50);
+DEFINE("MARK_IMG_SPUSHPIN",50);
+DEFINE("MARK_IMG_LPUSHPIN",51);
+DEFINE("MARK_IMG_DIAMOND",52);
+DEFINE("MARK_IMG_SQUARE",53);
+DEFINE("MARK_IMG_STAR",54);
+DEFINE("MARK_IMG_BALL",55);
+DEFINE("MARK_IMG_SBALL",55);
+DEFINE("MARK_IMG_MBALL",56);
+DEFINE("MARK_IMG_LBALL",57);
+DEFINE("MARK_IMG_BEVEL",58);
+
+// Styles for gradient color fill
+DEFINE("GRAD_VER",1);
+DEFINE("GRAD_VERT",1);
+DEFINE("GRAD_HOR",2);
+DEFINE("GRAD_MIDHOR",3);
+DEFINE("GRAD_MIDVER",4);
+DEFINE("GRAD_CENTER",5);
+DEFINE("GRAD_WIDE_MIDVER",6);
+DEFINE("GRAD_WIDE_MIDHOR",7);
+DEFINE("GRAD_LEFT_REFLECTION",8);
+DEFINE("GRAD_RIGHT_REFLECTION",9);
+
+// Inline defines
+DEFINE("INLINE_YES",1);
+DEFINE("INLINE_NO",0);
+
+// Format for background images
+DEFINE("BGIMG_FILLPLOT",1);
+DEFINE("BGIMG_FILLFRAME",2);
+DEFINE("BGIMG_COPY",3);
+DEFINE("BGIMG_CENTER",4);
+
+// Depth of objects
+DEFINE("DEPTH_BACK",0);
+DEFINE("DEPTH_FRONT",1);
+
+// Direction
+DEFINE("VERTICAL",1);
+DEFINE("HORIZONTAL",0);
+
+// Constants for types of static bands in plot area
+DEFINE("BAND_RDIAG",1); // Right diagonal lines
+DEFINE("BAND_LDIAG",2); // Left diagonal lines
+DEFINE("BAND_SOLID",3); // Solid one color
+DEFINE("BAND_VLINE",4); // Vertical lines
+DEFINE("BAND_HLINE",5); // Horizontal lines
+DEFINE("BAND_3DPLANE",6); // "3D" Plane
+DEFINE("BAND_HVCROSS",7); // Vertical/Hor crosses
+DEFINE("BAND_DIAGCROSS",8); // Diagonal crosses
+
+// Axis styles for scientific style axis
+DEFINE('AXSTYLE_SIMPLE',1);
+DEFINE('AXSTYLE_BOXIN',2);
+DEFINE('AXSTYLE_BOXOUT',3);
+DEFINE('AXSTYLE_YBOXIN',4);
+DEFINE('AXSTYLE_YBOXOUT',5);
+
+// Style for title backgrounds
+DEFINE('TITLEBKG_STYLE1',1);
+DEFINE('TITLEBKG_STYLE2',2);
+DEFINE('TITLEBKG_STYLE3',3);
+DEFINE('TITLEBKG_FRAME_NONE',0);
+DEFINE('TITLEBKG_FRAME_FULL',1);
+DEFINE('TITLEBKG_FRAME_BOTTOM',2);
+DEFINE('TITLEBKG_FRAME_BEVEL',3);
+DEFINE('TITLEBKG_FILLSTYLE_HSTRIPED',1);
+DEFINE('TITLEBKG_FILLSTYLE_VSTRIPED',2);
+DEFINE('TITLEBKG_FILLSTYLE_SOLID',3);
+
+// Width of tab titles
+DEFINE('TABTITLE_WIDTHFIT',0);
+DEFINE('TABTITLE_WIDTHFULL',-1);
+
+//
+// Get hold of gradient class (In Version 2.x)
+// A client of the library has to manually include this
+//
+include "jpgraph_gradient.php";
+
+//
+// First of all set up a default error handler
+//
+
+//=============================================================
+// The default trivial text error handler.
+//=============================================================
+class JpGraphErrObject {
+ function JpGraphErrObject() {
+ // Empty. Reserved for future use
+ }
+
+ // If aHalt is true then execution can't continue. Typical used for
+ // fatal errors
+ function Raise($aMsg,$aHalt=true) {
+ $aMsg = "<b>JpGraph Error:</b> ".$aMsg;
+ if( $aHalt )
+ die($aMsg);
+ else
+ echo $aMsg."<p>";
+ }
+}
+
+//==============================================================
+// An image based error handler
+//==============================================================
+class JpGraphErrObjectImg {
+
+ function Raise($aMsg,$aHalt=true) {
+ $img_iconerror =
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAAAaV'.
+ 'BMVEX//////2Xy8mLl5V/Z2VvMzFi/v1WyslKlpU+ZmUyMjEh/'.
+ 'f0VyckJlZT9YWDxMTDjAwMDy8sLl5bnY2K/MzKW/v5yyspKlpY'.
+ 'iYmH+MjHY/PzV/f2xycmJlZVlZWU9MTEXY2Ms/PzwyMjLFTjea'.
+ 'AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACx'.
+ 'IAAAsSAdLdfvwAAAAHdElNRQfTBgISOCqusfs5AAABLUlEQVR4'.
+ '2tWV3XKCMBBGWfkranCIVClKLd/7P2Q3QsgCxjDTq+6FE2cPH+'.
+ 'xJ0Ogn2lQbsT+Wrs+buAZAV4W5T6Bs0YXBBwpKgEuIu+JERAX6'.
+ 'wM2rHjmDdEITmsQEEmWADgZm6rAjhXsoMGY9B/NZBwJzBvn+e3'.
+ 'wHntCAJdGu9SviwIwoZVDxPB9+Rc0TSEbQr0j3SA1gwdSn6Db0'.
+ '6Tm1KfV6yzWGQO7zdpvyKLKBDmRFjzeB3LYgK7r6A/noDAfjtS'.
+ 'IXaIzbJSv6WgUebTMV4EoRB8a2mQiQjgtF91HdKDKZ1gtFtQjk'.
+ 'YcWaR5OKOhkYt+ZsTFdJRfPAApOpQYJTNHvCRSJR6SJngQadfc'.
+ 'vd69OLMddVOPCGVnmrFD8bVYd3JXfxXPtLR/+mtv59/ALWiiMx'.
+ 'qL72fwAAAABJRU5ErkJggg==' ;
+
+ if( headers_sent() ) {
+ // Special case for headers already sent error. Dont
+ // return an image since it can't be displayed
+ die("<b>JpGraph Error:</b> ".$aMsg);
+ }
+
+ // Create the error icon GD
+ $erricon = Image::CreateFromString(base64_decode($img_iconerror));
+
+ // Create an image that contains the error text.
+ $w=380; $h=100;
+ $img = new Image($w,$h);
+
+
+ // Drop shadow
+ $img->SetColor("gray");
+ $img->FilledRectangle(5,5,$w-1,$h-1,10);
+ $img->SetColor("gray:0.7");
+ $img->FilledRectangle(5,5,$w-3,$h-3,10);
+
+ // Window background
+ $img->SetColor("lightblue");
+ $img->FilledRectangle(1,1,$w-5,$h-5);
+ $img->CopyCanvasH($img->img,$erricon,5,30,0,0,40,40);
+
+ // Window border
+ $img->SetColor("black");
+ $img->Rectangle(1,1,$w-5,$h-5);
+ $img->Rectangle(0,0,$w-4,$h-4);
+
+ // Window top row
+ $img->SetColor("darkred");
+ for($y=3; $y < 18; $y += 2 )
+ $img->Line(1,$y,$w-25,$y);
+ $img->Line(1,17,$w-5,17);
+ $img->Line($w-25,1,$w-25,16);
+
+ // "Close button"
+ $img->Line($w-18,6,$w-12,12);
+ $img->Line($w-18,7,$w-12,13);
+ $img->Line($w-18,12,$w-12,6);
+ $img->Line($w-18,13,$w-12,7);
+
+ // "White shadow"
+ $img->SetColor("white");
+ // "Button"
+ $img->Line($w-24,3,$w-24,15);
+ $img->Line($w-24,2,$w-7,2);
+
+ // Left window edge
+ $img->Line(2,2,2,$h-5);
+ $img->Line(2,2,$w-27,2);
+
+ // "Gray button shadow"
+ $img->SetColor("darkgray");
+ $img->Line($w-6,3,$w-6,16);
+ $img->Line($w-24,16,$w-7,16);
+
+ // Gray window shadow
+ $img->Line(2,$h-6,$w-5,$h-6);
+ $img->Line(3,$h-7,$w-5,$h-7);
+
+ // Window title
+ $m = floor($w/2-5);
+ $l = 100;
+ $img->SetColor("lightgray:1.3");
+ $img->FilledRectangle($m-$l,2,$m+$l,16);
+
+ // Stroke text
+ $img->SetColor("darkred");
+ $img->SetFont(FF_FONT2,FS_BOLD);
+ $img->StrokeText($m-50,15,"JpGraph Error");
+ $img->SetColor("black");
+ $img->SetFont(FF_FONT1,FS_NORMAL);
+ $txt = new Text(wordwrap($aMsg,52),52,25);
+ $txt->Align("left","top");
+ $txt->Stroke($img);
+ $img->Headers();
+ $img->Stream();
+ die();
+ }
+}
+
+//
+// A wrapper class that is used to access the specified error object
+// (to hide the global error parameter and avoid having a GLOBAL directive
+// in all methods.
+//
+class JpGraphError {
+ function Install($aErrObject) {
+ GLOBAL $__jpg_err;
+ $__jpg_err = $aErrObject;
+ }
+ function Raise($aMsg,$aHalt=true){
+ GLOBAL $__jpg_err;
+ $tmp = new $__jpg_err;
+ $tmp->Raise($aMsg,$aHalt);
+ }
+}
+
+//
+// ... and install the default error handler
+//
+if( USE_IMAGE_ERROR_HANDLER ) {
+ JpGraphError::Install("JpGraphErrObjectImg");
+}
+else {
+ JpGraphError::Install("JpGraphErrObject");
+}
+
+//
+// Setup PHP error handler
+//
+
+function _phpErrorHandler($errno,$errmsg,$filename, $linenum, $vars) {
+ JpGraphError::Raise('In '.basename($filename).'#'.$linenum."\n".$errmsg);
+}
+
+if( INSTALL_PHP_ERR_HANDLER ) {
+ set_error_handler("_phpErrorHandler");
+ error_reporting(1);
+}
+
+//
+//Check if there were any warnings, perhaps some wrong includes by the
+//user
+//
+if( isset($GLOBALS['php_errormsg']) ) {
+ JpGraphError::Raise("<b>General PHP error:</b><br>".$GLOBALS['php_errormsg']);
+}
+
+
+//
+// Routine to determine if GD1 or GD2 is installed
+//
+function CheckGDVersion() {
+ ob_start();
+ phpinfo(8); // Just get the modules loaded
+ $a = ob_get_contents();
+ ob_end_clean();
+ if( preg_match('/.*GD Version.*(1\.).*/',$a,$m) ) {
+ $r=1;$v=$m[1];
+ }
+ elseif( preg_match('/.*GD Version.*(2\.).*/',$a,$m) ) {
+ $r=2;$v=$m[1];
+ }
+ else {
+ $r=0;$v=$m[1];
+ }
+ return $r;
+}
+
+//
+// Check what version of the GD library is installed.
+//
+if( USE_LIBRARY_GD2 === 'auto' ) {
+ $gdversion = CheckGDVersion();
+ if( $gdversion == 2 ) {
+ $GLOBALS['gd2'] = true;
+ $GLOBALS['copyfunc'] = 'imagecopyresampled';
+ }
+ elseif( $gdversion == 1 ) {
+ $GLOBALS['gd2'] = false;
+ $GLOBALS['copyfunc'] = 'imagecopyresized';
+ }
+ else {
+ JpGraphError::Raise(" Your PHP installation does not seem to
+ have the required GD library.
+ Please see the PHP documentation on how to install and enable the GD library.");
+ }
+}
+else {
+ $GLOBALS['gd2'] = USE_LIBRARY_GD2;
+ $GLOBALS['copyfunc'] = USE_LIBRARY_GD2 ? 'imagecopyresampled' : 'imagecopyresized';
+}
+
+// Usefull mathematical function
+function sign($a) {return $a >= 0 ? 1 : -1;}
+
+// Utility function to generate an image name based on the filename we
+// are running from and assuming we use auto detection of graphic format
+// (top level), i.e it is safe to call this function
+// from a script that uses JpGraph
+function GenImgName() {
+ global $HTTP_SERVER_VARS;
+ $supported = imagetypes();
+ if( $supported & IMG_PNG )
+ $img_format="png";
+ elseif( $supported & IMG_GIF )
+ $img_format="gif";
+ elseif( $supported & IMG_JPG )
+ $img_format="jpeg";
+ if( !isset($HTTP_SERVER_VARS['PHP_SELF']) )
+ JpGraphError::Raise(" Can't access PHP_SELF, PHP global variable. You can't run PHP from command line
+ if you want to use the 'auto' naming of cache or image files.");
+ $fname=basename($HTTP_SERVER_VARS['PHP_SELF']);
+ // Replace the ".php" extension with the image format extension
+ return substr($fname,0,strlen($fname)-4).".".$img_format;
+}
+
+class LanguageConv {
+ var $g2312 = null ;
+
+ function Convert($aTxt,$aFF) {
+ if( LANGUAGE_CYRILLIC ) {
+ if( CYRILLIC_FROM_WINDOWS ) {
+ $aTxt = convert_cyr_string($aTxt, "w", "k");
+ }
+ $isostring = convert_cyr_string($aTxt, "k", "i");
+ $unistring = LanguageConv::iso2uni($isostring);
+ return $unistring;
+ }
+ elseif( $aFF === FF_SIMSUN ) {
+ // Do Chinese conversion
+ if( $this->g2312 == null ) {
+ include_once 'jpgraph_gb2312.php' ;
+ $this->g2312 = new GB2312toUTF8();
+ }
+ return $this->g2312->gb2utf8($aTxt);
+ }
+ else
+ return $aTxt;
+ }
+
+ // Translate iso encoding to unicode
+ function iso2uni ($isoline){
+ for ($i=0; $i < strlen($isoline); $i++){
+ $thischar=substr($isoline,$i,1);
+ $charcode=ord($thischar);
+ $uniline.=($charcode>175) ? "&#" . (1040+($charcode-176)). ";" : $thischar;
+ }
+ return $uniline;
+ }
+}
+
+//===================================================
+// CLASS JpgTimer
+// Description: General timing utility class to handle
+// timne measurement of generating graphs. Multiple
+// timers can be started by pushing new on a stack.
+//===================================================
+class JpgTimer {
+ var $start;
+ var $idx;
+//---------------
+// CONSTRUCTOR
+ function JpgTimer() {
+ $this->idx=0;
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ // Push a new timer start on stack
+ function Push() {
+ list($ms,$s)=explode(" ",microtime());
+ $this->start[$this->idx++]=floor($ms*1000) + 1000*$s;
+ }
+
+ // Pop the latest timer start and return the diff with the
+ // current time
+ function Pop() {
+ assert($this->idx>0);
+ list($ms,$s)=explode(" ",microtime());
+ $etime=floor($ms*1000) + (1000*$s);
+ $this->idx--;
+ return $etime-$this->start[$this->idx];
+ }
+} // Class
+
+$gJpgBrandTiming = BRAND_TIMING;
+//===================================================
+// CLASS DateLocale
+// Description: Hold localized text used in dates
+// ToDOo: Rewrite this to use the real local locale
+// instead.
+//===================================================
+class DateLocale {
+
+ var $iLocale = 'C'; // environmental locale be used by default
+
+ var $iDayAbb = null;
+ var $iShortDay = null;
+ var $iShortMonth = null;
+ var $iMonthName = null;
+
+//---------------
+// CONSTRUCTOR
+ function DateLocale() {
+ settype($this->iDayAbb, 'array');
+ settype($this->iShortDay, 'array');
+ settype($this->iShortMonth, 'array');
+ settype($this->iMonthName, 'array');
+
+
+ $this->Set('C');
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Set($aLocale) {
+ if ( in_array($aLocale, array_keys($this->iDayAbb)) ){
+ $this->iLocale = $aLocale;
+ return TRUE; // already cached nothing else to do!
+ }
+
+ $pLocale = setlocale(LC_TIME, 0); // get current locale for LC_TIME
+ $res = setlocale(LC_TIME, $aLocale);
+ if ( ! $res ){
+ JpGraphError::Raise("You are trying to use the locale ($aLocale) which your PHP installation does not support. Hint: Use '' to indicate the default locale for this geographic region.");
+ return FALSE;
+ }
+
+ $this->iLocale = $aLocale;
+
+ for ( $i = 0, $ofs = 0 - strftime('%w'); $i < 7; $i++, $ofs++ ){
+ $day = strftime('%a', strtotime("$ofs day"));
+ $day{0} = strtoupper($day{0});
+ $this->iDayAbb[$aLocale][]= $day{0};
+ $this->iShortDay[$aLocale][]= $day;
+ }
+
+ for($i=1; $i<=12; ++$i) {
+ list($short ,$full) = explode('|', strftime("%b|%B",strtotime("2001-$i-01")));
+ $this->iShortMonth[$aLocale][] = ucfirst($short);
+ $this->iMonthName [$aLocale][] = ucfirst($full);
+ }
+
+
+ setlocale(LC_TIME, $pLocale);
+
+ return TRUE;
+ }
+
+
+ function GetDayAbb() {
+ return $this->iDayAbb[$this->iLocale];
+ }
+
+ function GetShortDay() {
+ return $this->iShortDay[$this->iLocale];
+ }
+
+ function GetShortMonth() {
+ return $this->iShortMonth[$this->iLocale];
+ }
+
+ function GetShortMonthName($aNbr) {
+ return $this->iShortMonth[$this->iLocale][$aNbr];
+ }
+
+ function GetLongMonthName($aNbr) {
+ return $this->iMonthName[$this->iLocale][$aNbr];
+ }
+
+ function GetMonth() {
+ return $this->iMonthName[$this->iLocale];
+ }
+}
+
+$gDateLocale = new DateLocale();
+$gJpgDateLocale = new DateLocale();
+
+
+//===================================================
+// CLASS FuncGenerator
+// Description: Utility class to help generate data for function plots.
+// The class supports both parametric and regular functions.
+//===================================================
+class FuncGenerator {
+ var $iFunc='',$iXFunc='',$iMin,$iMax,$iStepSize;
+
+ function FuncGenerator($aFunc,$aXFunc='') {
+ $this->iFunc = $aFunc;
+ $this->iXFunc = $aXFunc;
+ }
+
+ function E($aXMin,$aXMax,$aSteps=50) {
+ $this->iMin = $aXMin;
+ $this->iMax = $aXMax;
+ $this->iStepSize = ($aXMax-$aXMin)/$aSteps;
+
+ if( $this->iXFunc != '' )
+ $t = 'for($i='.$aXMin.'; $i<='.$aXMax.'; $i += '.$this->iStepSize.') {$ya[]='.$this->iFunc.';$xa[]='.$this->iXFunc.';}';
+ elseif( $this->iFunc != '' )
+ $t = 'for($x='.$aXMin.'; $x<='.$aXMax.'; $x += '.$this->iStepSize.') {$ya[]='.$this->iFunc.';$xa[]=$x;} $x='.$aXMax.';$ya[]='.$this->iFunc.';$xa[]=$x;';
+ else
+ JpGraphError::Raise('FuncGenerator : No function specified. ');
+
+ @eval($t);
+
+ // If there is an error in the function specifcation this is the only
+ // way we can discover that.
+ if( empty($xa) || empty($ya) )
+ JpGraphError::Raise('FuncGenerator : Syntax error in function specification ');
+
+ return array($xa,$ya);
+ }
+}
+
+
+//=======================================================
+// CLASS Footer
+// Description: Encapsulates the footer line in the Graph
+//
+//=======================================================
+class Footer {
+ var $left,$center,$right;
+ var $iLeftMargin = 3;
+ var $iRightMargin = 3;
+ var $iBottomMargin = 3;
+
+ function Footer() {
+ $this->left = new Text();
+ $this->left->ParagraphAlign('left');
+ $this->center = new Text();
+ $this->center->ParagraphAlign('center');
+ $this->right = new Text();
+ $this->right->ParagraphAlign('right');
+ }
+
+ function Stroke($aImg) {
+ $y = $aImg->height - $this->iBottomMargin;
+ $x = $this->iLeftMargin;
+ $this->left->Align('left','bottom');
+ $this->left->Stroke($aImg,$x,$y);
+
+ $x = ($aImg->width - $this->iLeftMargin - $this->iRightMargin)/2;
+ $this->center->Align('center','bottom');
+ $this->center->Stroke($aImg,$x,$y);
+
+ $x = $aImg->width - $this->iRightMargin;
+ $this->right->Align('right','bottom');
+ $this->right->Stroke($aImg,$x,$y);
+ }
+}
+
+ DEFINE('BGRAD_FRAME',1);
+ DEFINE('BGRAD_MARGIN',2);
+ DEFINE('BGRAD_PLOT',3);
+
+
+
+//===================================================
+// CLASS Graph
+// Description: Main class to handle graphs
+//===================================================
+class Graph {
+ var $cache=null; // Cache object (singleton)
+ var $img=null; // Img object (singleton)
+ var $plots=array(); // Array of all plot object in the graph (for Y 1 axis)
+ var $y2plots=array();// Array of all plot object in the graph (for Y 2 axis)
+ var $xscale=null; // X Scale object (could be instance of LinearScale or LogScale
+ var $yscale=null,$y2scale=null;
+ var $cache_name; // File name to be used for the current graph in the cache directory
+ var $xgrid=null; // X Grid object (linear or logarithmic)
+ var $ygrid=null,$y2grid=null; //dito for Y
+ var $doframe=true,$frame_color=array(0,0,0), $frame_weight=1; // Frame around graph
+ var $boxed=false, $box_color=array(0,0,0), $box_weight=1; // Box around plot area
+ var $doshadow=false,$shadow_width=4,$shadow_color=array(102,102,102); // Shadow for graph
+ var $xaxis=null; // X-axis (instane of Axis class)
+ var $yaxis=null, $y2axis=null; // Y axis (instance of Axis class)
+ var $margin_color=array(200,200,200); // Margin color of graph
+ var $plotarea_color=array(255,255,255); // Plot area color
+ var $title,$subtitle,$subsubtitle; // Title and subtitle(s) text object
+ var $axtype="linlin"; // Type of axis
+ var $xtick_factor; // Factot to determine the maximum number of ticks depending on the plot with
+ var $texts=null, $y2texts=null; // Text object to ge shown in the graph
+ var $lines=null, $y2lines=null;
+ var $bands=null, $y2bands=null;
+ var $text_scale_off=0; // Text scale offset in world coordinates
+ var $background_image="",$background_image_type=-1,$background_image_format="png";
+ var $background_image_bright=0,$background_image_contr=0,$background_image_sat=0;
+ var $image_bright=0, $image_contr=0, $image_sat=0;
+ var $inline;
+ var $showcsim=0,$csimcolor="red"; //debug stuff, draw the csim boundaris on the image if <>0
+ var $grid_depth=DEPTH_BACK; // Draw grid under all plots as default
+ var $iAxisStyle = AXSTYLE_SIMPLE;
+ var $iCSIMdisplay=false,$iHasStroked = false;
+ var $footer;
+ var $csimcachename = '', $csimcachetimeout = 0;
+ var $iDoClipping = false;
+ var $y2orderback=true;
+ var $tabtitle;
+ var $bkg_gradtype=-1,$bkg_gradstyle=BGRAD_MARGIN;
+ var $bkg_gradfrom='navy', $bkg_gradto='silver';
+ var $titlebackground = false;
+ var $titlebackground_color = 'lightblue',
+ $titlebackground_style = 1,
+ $titlebackground_framecolor = 'blue',
+ $titlebackground_framestyle = 2,
+ $titlebackground_frameweight = 1,
+ $titlebackground_bevelheight = 3 ;
+ var $titlebkg_fillstyle=TITLEBKG_FILLSTYLE_SOLID;
+ var $titlebkg_scolor1='black',$titlebkg_scolor2='white';
+ var $framebevel = false, $framebeveldepth = 2 ;
+ var $framebevelborder = false, $framebevelbordercolor='black';
+ var $framebevelcolor1='white@0.4', $framebevelcolor2='black@0.4';
+
+//---------------
+// CONSTRUCTOR
+
+ // aWIdth Width in pixels of image
+ // aHeight Height in pixels of image
+ // aCachedName Name for image file in cache directory
+ // aTimeOut Timeout in minutes for image in cache
+ // aInline If true the image is streamed back in the call to Stroke()
+ // If false the image is just created in the cache
+ function Graph($aWidth=300,$aHeight=200,$aCachedName="",$aTimeOut=0,$aInline=true) {
+ GLOBAL $gJpgBrandTiming;
+ // If timing is used create a new timing object
+ if( $gJpgBrandTiming ) {
+ global $tim;
+ $tim = new JpgTimer();
+ $tim->Push();
+ }
+
+ // Automatically generate the image file name based on the name of the script that
+ // generates the graph
+ if( $aCachedName=="auto" )
+ $aCachedName=GenImgName();
+
+ // Should the image be streamed back to the browser or only to the cache?
+ $this->inline=$aInline;
+
+ $this->img = new RotImage($aWidth,$aHeight);
+
+ $this->cache = new ImgStreamCache($this->img);
+ $this->cache->SetTimeOut($aTimeOut);
+
+ $this->title = new Text();
+ $this->title->ParagraphAlign('center');
+ $this->title->SetFont(FF_FONT2,FS_BOLD);
+ $this->title->SetMargin(3);
+
+ $this->subtitle = new Text();
+ $this->subtitle->ParagraphAlign('center');
+
+ $this->subsubtitle = new Text();
+ $this->subsubtitle->ParagraphAlign('center');
+
+ $this->legend = new Legend();
+ $this->footer = new Footer();
+
+ // If the cached version exist just read it directly from the
+ // cache, stream it back to browser and exit
+ if( $aCachedName!="" && READ_CACHE && $aInline )
+ if( $this->cache->GetAndStream($aCachedName) ) {
+ exit();
+ }
+
+ $this->cache_name = $aCachedName;
+ $this->SetTickDensity(); // Normal density
+
+ $this->tabtitle = new GraphTabTitle();
+ }
+//---------------
+// PUBLIC METHODS
+
+ // Should the grid be in front or back of the plot?
+ function SetGridDepth($aDepth) {
+ $this->grid_depth=$aDepth;
+ }
+
+ // Specify graph angle 0-360 degrees.
+ function SetAngle($aAngle) {
+ $this->img->SetAngle($aAngle);
+ }
+
+ function SetAlphaBlending($aFlg=true) {
+ $this->img->SetAlphaBlending($aFlg);
+ }
+
+ // Shortcut to image margin
+ function SetMargin($lm,$rm,$tm,$bm) {
+ $this->img->SetMargin($lm,$rm,$tm,$bm);
+ }
+
+ function SetY2OrderBack($aBack=true) {
+ $this->y2orderback = $aBack;
+ }
+
+ // Rotate the graph 90 degrees and set the margin
+ // when we have done a 90 degree rotation
+ function Set90AndMargin($lm=0,$rm=0,$tm=0,$bm=0) {
+ $lm = $lm ==0 ? floor(0.2 * $this->img->width) : $lm ;
+ $rm = $rm ==0 ? floor(0.1 * $this->img->width) : $rm ;
+ $tm = $tm ==0 ? floor(0.2 * $this->img->height) : $tm ;
+ $bm = $bm ==0 ? floor(0.1 * $this->img->height) : $bm ;
+
+ $adj = ($this->img->height - $this->img->width)/2;
+ $this->img->SetMargin($tm-$adj,$bm-$adj,$rm+$adj,$lm+$adj);
+ $this->img->SetCenter(floor($this->img->width/2),floor($this->img->height/2));
+ $this->SetAngle(90);
+ $this->xaxis->SetLabelAlign('right','center');
+ $this->yaxis->SetLabelAlign('center','bottom');
+ }
+
+ function SetClipping($aFlg=true) {
+ $this->iDoClipping = $aFlg ;
+ }
+
+ // Add a plot object to the graph
+ function Add(&$aPlot) {
+ if( $aPlot == null )
+ JpGraphError::Raise("<b></b> Graph::Add() You tried to add a null plot to the graph.");
+ if( is_array($aPlot) && count($aPlot) > 0 )
+ $cl = get_class($aPlot[0]);
+ else
+ $cl = get_class($aPlot);
+
+ if( $cl == 'text' )
+ $this->AddText($aPlot);
+ elseif( $cl == 'plotline' )
+ $this->AddLine($aPlot);
+ elseif( $cl == 'plotband' )
+ $this->AddBand($aPlot);
+ else
+ $this->plots[] = &$aPlot;
+ }
+
+ // Add plot to second Y-scale
+ function AddY2(&$aPlot) {
+ if( $aPlot == null )
+ JpGraphError::Raise("<b></b> Graph::AddY2() You tried to add a null plot to the graph.");
+
+ if( is_array($aPlot) && count($aPlot) > 0 )
+ $cl = get_class($aPlot[0]);
+ else
+ $cl = get_class($aPlot);
+
+ if( $cl == 'text' )
+ $this->AddText($aPlot,true);
+ elseif( $cl == 'plotline' )
+ $this->AddLine($aPlot,true);
+ elseif( $cl == 'plotband' )
+ $this->AddBand($aPlot,true);
+ else
+ $this->y2plots[] = &$aPlot;
+ }
+
+ // Add text object to the graph
+ function AddText(&$aTxt,$aToY2=false) {
+ if( $aTxt == null )
+ JpGraphError::Raise("<b></b> Graph::AddText() You tried to add a null text to the graph.");
+ if( $aToY2 ) {
+ if( is_array($aTxt) ) {
+ for($i=0; $i < count($aTxt); ++$i )
+ $this->y2texts[]=&$aTxt[$i];
+ }
+ else
+ $this->y2texts[] = &$aTxt;
+ }
+ else {
+ if( is_array($aTxt) ) {
+ for($i=0; $i < count($aTxt); ++$i )
+ $this->texts[]=&$aTxt[$i];
+ }
+ else
+ $this->texts[] = &$aTxt;
+ }
+ }
+
+ // Add a line object (class PlotLine) to the graph
+ function AddLine(&$aLine,$aToY2=false) {
+ if( $aLine == null )
+ JpGraphError::Raise("<b></b> Graph::AddLine() You tried to add a null line to the graph.");
+
+ if( $aToY2 ) {
+ if( is_array($aLine) ) {
+ for($i=0; $i < count($aLine); ++$i )
+ $this->y2lines[]=&$aLine[$i];
+ }
+ else
+ $this->y2lines[] = &$aLine;
+ }
+ else {
+ if( is_array($aLine) ) {
+ for($i=0; $i<count($aLine); ++$i )
+ $this->lines[]=&$aLine[$i];
+ }
+ else
+ $this->lines[] = &$aLine;
+ }
+ }
+
+ // Add vertical or horizontal band
+ function AddBand(&$aBand,$aToY2=false) {
+ if( $aBand == null )
+ JpGraphError::Raise(" Graph::AddBand() You tried to add a null band to the graph.");
+
+ if( $aToY2 ) {
+ if( is_array($aBand) ) {
+ for($i=0; $i < count($aBand); ++$i )
+ $this->y2bands[] = &$aBand[$i];
+ }
+ else
+ $this->y2bands[] = &$aBand;
+ }
+ else {
+ if( is_array($aBand) ) {
+ for($i=0; $i < count($aBand); ++$i )
+ $this->bands[] = &$aBand[$i];
+ }
+ else
+ $this->bands[] = &$aBand;
+ }
+ }
+
+ function SetBackgroundGradient($aFrom='navy',$aTo='silver',$aGradType=GRAD_HOR,$aStyle=BGRAD_FRAME) {
+ $this->bkg_gradtype=$aGradType;
+ $this->bkg_gradstyle=$aStyle;
+ $this->bkg_gradfrom = $aFrom;
+ $this->bkg_gradto = $aTo;
+ }
+
+ // Specify a background image
+ function SetBackgroundImage($aFileName,$aBgType=BGIMG_FILLPLOT,$aImgFormat="auto") {
+
+ if( $GLOBALS['gd2'] && !USE_TRUECOLOR ) {
+ JpGraphError::Raise("You are using GD 2.x and are trying to use a background images on a non truecolor image. To use background images with GD 2.x you <b>must</b> enable truecolor by setting the USE_TRUECOLOR constant to TRUE. Due to a bug in GD 2.0.1 using any truetype fonts with truecolor images will result in very poor quality fonts.");
+ }
+
+ // Get extension to determine image type
+ if( $aImgFormat == "auto" ) {
+ $e = explode('.',$aFileName);
+ if( !$e ) {
+ JpGraphError::Raise('Incorrect file name for Graph::SetBackgroundImage() : '.$aFileName.' Must have a valid image extension (jpg,gif,png) when using autodetection of image type');
+ }
+
+ $valid_formats = array('png', 'jpg', 'gif');
+ $aImgFormat = strtolower($e[count($e)-1]);
+ if ($aImgFormat == 'jpeg') {
+ $aImgFormat = 'jpg';
+ }
+ elseif (!in_array($aImgFormat, $valid_formats) ) {
+ JpGraphError::Raise('Unknown file extension ($aImgFormat) in Graph::SetBackgroundImage() for filename: '.$aFileName);
+ }
+ }
+
+ $this->background_image = $aFileName;
+ $this->background_image_type=$aBgType;
+ $this->background_image_format=$aImgFormat;
+ }
+
+ // Adjust brightness and constrast for background image
+ function AdjBackgroundImage($aBright,$aContr=0,$aSat=0) {
+ $this->background_image_bright=$aBright;
+ $this->background_image_contr=$aContr;
+ $this->background_image_sat=$aSat;
+ }
+
+ // Adjust brightness and constrast for image
+ function AdjImage($aBright,$aContr=0,$aSat=0) {
+ $this->image_bright=$aBright;
+ $this->image_contr=$aContr;
+ $this->image_sat=$aSat;
+ }
+
+ // Specify axis style (boxed or single)
+ function SetAxisStyle($aStyle) {
+ $this->iAxisStyle = $aStyle ;
+ }
+
+ // Set a frame around the plot area
+ function SetBox($aDrawPlotFrame=true,$aPlotFrameColor=array(0,0,0),$aPlotFrameWeight=1) {
+ $this->boxed = $aDrawPlotFrame;
+ $this->box_weight = $aPlotFrameWeight;
+ $this->box_color = $aPlotFrameColor;
+ }
+
+ // Specify color for the plotarea (not the margins)
+ function SetColor($aColor) {
+ $this->plotarea_color=$aColor;
+ }
+
+ // Specify color for the margins (all areas outside the plotarea)
+ function SetMarginColor($aColor) {
+ $this->margin_color=$aColor;
+ }
+
+ // Set a frame around the entire image
+ function SetFrame($aDrawImgFrame=true,$aImgFrameColor=array(0,0,0),$aImgFrameWeight=1) {
+ $this->doframe = $aDrawImgFrame;
+ $this->frame_color = $aImgFrameColor;
+ $this->frame_weight = $aImgFrameWeight;
+ }
+
+ function SetFrameBevel($aDepth=3,$aBorder=false,$aBorderColor='black',$aColor1='white@0.4',$aColor2='darkgray@0.4',$aFlg=true) {
+ $this->framebevel = $aFlg ;
+ $this->framebeveldepth = $aDepth ;
+ $this->framebevelborder = $aBorder ;
+ $this->framebevelbordercolor = $aBorderColor ;
+ $this->framebevelcolor1 = $aColor1 ;
+ $this->framebevelcolor2 = $aColor2 ;
+
+ $this->doshadow = false ;
+ }
+
+ // Set the shadow around the whole image
+ function SetShadow($aShowShadow=true,$aShadowWidth=5,$aShadowColor=array(102,102,102)) {
+ $this->doshadow = $aShowShadow;
+ $this->shadow_color = $aShadowColor;
+ $this->shadow_width = $aShadowWidth;
+ $this->footer->iBottomMargin += $aShadowWidth;
+ $this->footer->iRightMargin += $aShadowWidth;
+ }
+
+ // Specify x,y scale. Note that if you manually specify the scale
+ // you must also specify the tick distance with a call to Ticks::Set()
+ function SetScale($aAxisType,$aYMin=1,$aYMax=1,$aXMin=1,$aXMax=1) {
+ $this->axtype = $aAxisType;
+
+ if( $aYMax < $aYMin || $aXMax < $aXMin )
+ JpGraphError::Raise('Graph::SetScale(): Specified Max value must be larger than the specified Min value.');
+
+ $yt=substr($aAxisType,-3,3);
+ if( $yt=="lin" )
+ $this->yscale = new LinearScale($aYMin,$aYMax);
+ elseif( $yt == "int" ) {
+ $this->yscale = new LinearScale($aYMin,$aYMax);
+ $this->yscale->SetIntScale();
+ }
+ elseif( $yt=="log" )
+ $this->yscale = new LogScale($aYMin,$aYMax);
+ else
+ JpGraphError::Raise("Unknown scale specification for Y-scale. ($aAxisType)");
+
+ $xt=substr($aAxisType,0,3);
+ if( $xt == "lin" || $xt == "tex" ) {
+ $this->xscale = new LinearScale($aXMin,$aXMax,"x");
+ $this->xscale->textscale = ($xt == "tex");
+ }
+ elseif( $xt == "int" ) {
+ $this->xscale = new LinearScale($aXMin,$aXMax,"x");
+ $this->xscale->SetIntScale();
+ }
+ elseif( $xt == "log" )
+ $this->xscale = new LogScale($aXMin,$aXMax,"x");
+ else
+ JpGraphError::Raise(" Unknown scale specification for X-scale. ($aAxisType)");
+
+ $this->xscale->Init($this->img);
+ $this->yscale->Init($this->img);
+
+ $this->xaxis = new Axis($this->img,$this->xscale);
+ $this->yaxis = new Axis($this->img,$this->yscale);
+ $this->xgrid = new Grid($this->xaxis);
+ $this->ygrid = new Grid($this->yaxis);
+ $this->ygrid->Show();
+ }
+
+ // Specify secondary Y scale
+ function SetY2Scale($aAxisType="lin",$aY2Min=1,$aY2Max=1) {
+ if( $aAxisType=="lin" )
+ $this->y2scale = new LinearScale($aY2Min,$aY2Max);
+ elseif( $aAxisType == "int" ) {
+ $this->y2scale = new LinearScale($aY2Min,$aY2Max);
+ $this->y2scale->SetIntScale();
+ }
+ elseif( $aAxisType=="log" ) {
+ $this->y2scale = new LogScale($aY2Min,$aY2Max);
+ }
+ else JpGraphError::Raise("JpGraph: Unsupported Y2 axis type: $axtype<br>");
+
+ $this->y2scale->Init($this->img);
+ $this->y2axis = new Axis($this->img,$this->y2scale);
+ $this->y2axis->scale->ticks->SetDirection(SIDE_LEFT);
+ $this->y2axis->SetLabelSide(SIDE_RIGHT);
+
+ // Deafult position is the max x-value
+ $this->y2grid = new Grid($this->y2axis);
+ }
+
+ // Specify density of ticks when autoscaling 'normal', 'dense', 'sparse', 'verysparse'
+ // The dividing factor have been determined heuristically according to my aesthetic
+ // sense (or lack off) y.m.m.v !
+ function SetTickDensity($aYDensity=TICKD_NORMAL,$aXDensity=TICKD_NORMAL) {
+ $this->xtick_factor=30;
+ $this->ytick_factor=25;
+ switch( $aYDensity ) {
+ case TICKD_DENSE:
+ $this->ytick_factor=12;
+ break;
+ case TICKD_NORMAL:
+ $this->ytick_factor=25;
+ break;
+ case TICKD_SPARSE:
+ $this->ytick_factor=40;
+ break;
+ case TICKD_VERYSPARSE:
+ $this->ytick_factor=100;
+ break;
+ default:
+ JpGraphError::Raise("JpGraph: Unsupported Tick density: $densy");
+ }
+ switch( $aXDensity ) {
+ case TICKD_DENSE:
+ $this->xtick_factor=15;
+ break;
+ case TICKD_NORMAL:
+ $this->xtick_factor=30;
+ break;
+ case TICKD_SPARSE:
+ $this->xtick_factor=45;
+ break;
+ case TICKD_VERYSPARSE:
+ $this->xtick_factor=60;
+ break;
+ default:
+ JpGraphError::Raise("JpGraph: Unsupported Tick density: $densx");
+ }
+ }
+
+
+ // Get a string of all image map areas
+ function GetCSIMareas() {
+ if( !$this->iHasStroked )
+ $this->Stroke(_CSIM_SPECIALFILE);
+ $csim=$this->legend->GetCSIMAreas();
+
+ $n = count($this->plots);
+ for( $i=0; $i<$n; ++$i )
+ $csim .= $this->plots[$i]->GetCSIMareas();
+
+ $n = count($this->y2plots);
+ for( $i=0; $i<$n; ++$i )
+ $csim .= $this->y2plots[$i]->GetCSIMareas();
+
+ return $csim;
+ }
+
+ // Get a complete <MAP>..</MAP> tag for the final image map
+ function GetHTMLImageMap($aMapName) {
+ $im = "<MAP NAME=\"$aMapName\">\n";
+ $im .= $this->GetCSIMareas();
+ $im .= "</MAP>";
+ return $im;
+ }
+
+ function CheckCSIMCache($aCacheName,$aTimeOut=60) {
+ global $HTTP_SERVER_VARS;
+
+ if( $aCacheName=='auto' )
+ $aCacheName=basename($HTTP_SERVER_VARS['PHP_SELF']);
+
+ $this->csimcachename = CSIMCACHE_DIR.$aCacheName;
+ $this->csimcachetimeout = $aTimeOut;
+
+ // First determine if we need to check for a cached version
+ // This differs from the standard cache in the sense that the
+ // image and CSIM map HTML file is written relative to the directory
+ // the script executes in and not the specified cache directory.
+ // The reason for this is that the cache directory is not necessarily
+ // accessible from the HTTP server.
+ if( $this->csimcachename != '' ) {
+ $dir = dirname($this->csimcachename);
+ $base = basename($this->csimcachename);
+ $base = strtok($base,'.');
+ $suffix = strtok('.');
+ $basecsim = $dir.'/'.$base.'_csim_.html';
+ $baseimg = $dir.'/'.$base.'.'.$this->img->img_format;
+
+ $timedout=false;
+
+ // Does it exist at all ?
+
+ if( file_exists($basecsim) && file_exists($baseimg) ) {
+ // Check that it hasn't timed out
+ $diff=time()-filemtime($basecsim);
+ if( $this->csimcachetimeout>0 && ($diff > $this->csimcachetimeout*60) ) {
+ $timedout=true;
+ @unlink($basecsim);
+ @unlink($baseimg);
+ }
+ else {
+ if ($fh = @fopen($basecsim, "r")) {
+ fpassthru($fh);
+ exit();
+ }
+ else
+ JpGraphError::Raise(" Can't open cached CSIM \"$basecsim\" for reading.");
+ }
+ }
+ }
+ return false;
+ }
+
+ function StrokeCSIM($aScriptName='',$aCSIMName='',$aBorder=0) {
+ GLOBAL $HTTP_GET_VARS;
+
+ if( $aCSIMName=='' ) {
+ // create a random map name
+ srand ((double) microtime() * 1000000);
+ $r = rand(0,100000);
+ $aCSIMName='__mapname'.$r.'__';
+ }
+ if( empty($HTTP_GET_VARS[_CSIM_DISPLAY]) ) {
+ // First determine if we need to check for a cached version
+ // This differs from the standard cache in the sense that the
+ // image and CSIM map HTML file is written relative to the directory
+ // the script executes in and not the specified cache directory.
+ // The reason for this is that the cache directory is not necessarily
+ // accessible from the HTTP server.
+ if( $this->csimcachename != '' ) {
+ $dir = dirname($this->csimcachename);
+ $base = basename($this->csimcachename);
+ $base = strtok($base,'.');
+ $suffix = strtok('.');
+ $basecsim = $dir.'/'.$base.'_csim_.html';
+ $baseimg = $base.'.'.$this->img->img_format;
+
+ // Check that apache can write to directory specified
+
+ if( file_exists($dir) && !is_writeable($dir) ) {
+ JpgraphError::Raise('Apache/PHP does not have permission to write to the CSIM cache directory ('.$dir.'). Check permissions.');
+ }
+
+ // Make sure directory exists
+ $this->cache->MakeDirs($dir);
+
+ // Write the image file
+ $this->Stroke(CSIMCACHE_DIR.$baseimg);
+
+ // Construct wrapper HTML and write to file and send it back to browser
+ $htmlwrap = $this->GetHTMLImageMap($aCSIMName)."\n".
+ '<img src="'.CSIMCACHE_HTTP_DIR.$baseimg.'" ISMAP USEMAP="#'.$aCSIMName.'" border='.$aBorder.'>'."\n";
+ if($fh = @fopen($basecsim,'w') ) {
+ fwrite($fh,$htmlwrap);
+ fclose($fh);
+ echo $htmlwrap;
+ }
+ else
+ JpGraphError::Raise(" Can't write CSIM \"$basecsim\" for writing. Check free space and permissions.");
+ }
+ else {
+
+ if( $aScriptName=='' ) {
+ JpGraphError::Raise('Missing script name in call to StrokeCSIM(). You must specify the name of the actual image script as the first parameter to StrokeCSIM().');
+ exit();
+ }
+
+ // Construct the HTML wrapper page
+ // Get all user defined URL arguments
+ reset($HTTP_GET_VARS);
+
+ // This is a JPGRAPH internal defined that prevents
+ // us from recursively coming here again
+ $urlarg='?'._CSIM_DISPLAY.'=1';
+
+ while( list($key,$value) = each($HTTP_GET_VARS) ) {
+ if( is_array($value) ) {
+ $n = count($value);
+ for( $i=0; $i < $n; ++$i ) {
+ $urlarg .= '&'.$key.'%5B%5D='.urlencode($value[$i]);
+ }
+ }
+ else {
+ $urlarg .= '&'.$key.'='.urlencode($value);
+ }
+ }
+
+ echo $this->GetHTMLImageMap($aCSIMName);
+
+ echo "<img src='".$aScriptName.$urlarg."' ISMAP USEMAP='#".$aCSIMName."' border=$aBorder>";
+ }
+ }
+ else {
+ $this->Stroke();
+ }
+ }
+
+ function GetTextsYMinMax($aY2=false) {
+ if( $aY2 )
+ $txts = $this->y2texts;
+ else
+ $txts = $this->texts;
+ $n = count($txts);
+ $min=null;
+ $max=null;
+ for( $i=0; $i < $n; ++$i ) {
+ if( $txts[$i]->iScalePosY !== null &&
+ $txts[$i]->iScalePosX !== null ) {
+ if( $min === null ) {
+ $min = $max = $txts[$i]->iScalePosY ;
+ }
+ else {
+ $min = min($min,$txts[$i]->iScalePosY);
+ $max = max($max,$txts[$i]->iScalePosY);
+ }
+ }
+ }
+ if( $min !== null ) {
+ return array($min,$max);
+ }
+ else
+ return null;
+ }
+
+ function GetTextsXMinMax($aY2=false) {
+ if( $aY2 )
+ $txts = $this->y2texts;
+ else
+ $txts = $this->texts;
+ $n = count($txts);
+ $min=null;
+ $max=null;
+ for( $i=0; $i < $n; ++$i ) {
+ if( $txts[$i]->iScalePosY !== null &&
+ $txts[$i]->iScalePosX !== null ) {
+ if( $min === null ) {
+ $min = $max = $txts[$i]->iScalePosX ;
+ }
+ else {
+ $min = min($min,$txts[$i]->iScalePosX);
+ $max = max($max,$txts[$i]->iScalePosX);
+ }
+ }
+ }
+ if( $min !== null ) {
+ return array($min,$max);
+ }
+ else
+ return null;
+ }
+
+ function GetXMinMax() {
+ list($min,$ymin) = $this->plots[0]->Min();
+ list($max,$ymax) = $this->plots[0]->Max();
+ foreach( $this->plots as $p ) {
+ list($xmin,$ymin) = $p->Min();
+ list($xmax,$ymax) = $p->Max();
+ $min = Min($xmin,$min);
+ $max = Max($xmax,$max);
+ }
+ if( $this->y2axis != null ) {
+ foreach( $this->y2plots as $p ) {
+ list($xmin,$ymin) = $p->Min();
+ list($xmax,$ymax) = $p->Max();
+ $min = Min($xmin,$min);
+ $max = Max($xmax,$max);
+ }
+ }
+ return array($min,$max);
+ }
+
+ function AdjustMarginsForTitles() {
+ $totrequired =
+ ($this->title->t != '' ?
+ $this->title->GetTextHeight($this->img) + $this->title->margin + 5 : 0 ) +
+ ($this->subtitle->t != '' ?
+ $this->subtitle->GetTextHeight($this->img) + $this->subtitle->margin + 5 : 0 ) +
+ ($this->subsubtitle->t != '' ?
+ $this->subsubtitle->GetTextHeight($this->img) + $this->subsubtitle->margin + 5 : 0 ) ;
+
+
+ $btotrequired = 0;
+ if( !$this->xaxis->hide && !$this->xaxis->hide_labels ) {
+ // Minimum bottom margin
+ if( $this->xaxis->title->t != '' ) {
+ if( $this->img->a == 90 )
+ $btotrequired = $this->yaxis->title->GetTextHeight($this->img) + 5 ;
+ else
+ $btotrequired = $this->xaxis->title->GetTextHeight($this->img) + 5 ;
+ }
+ else
+ $btotrequired = 0;
+
+ if( $this->img->a == 90 ) {
+ $this->img->SetFont($this->yaxis->font_family,$this->yaxis->font_style,
+ $this->yaxis->font_size);
+ $lh = $this->img->GetTextHeight('Mg',$this->yaxis->label_angle);
+ }
+ else {
+ $this->img->SetFont($this->xaxis->font_family,$this->xaxis->font_style,
+ $this->xaxis->font_size);
+ $lh = $this->img->GetTextHeight('Mg',$this->xaxis->label_angle);
+ }
+
+ $btotrequired += $lh + 5;
+ }
+
+ if( $this->img->a == 90 ) {
+ // DO Nothing. It gets too messy to do this properly for 90 deg...
+ }
+ else{
+ if( $this->img->top_margin < $totrequired ) {
+ $this->SetMargin($this->img->left_margin,$this->img->right_margin,
+ $totrequired,$this->img->bottom_margin);
+ }
+ if( $this->img->bottom_margin < $btotrequired ) {
+ $this->SetMargin($this->img->left_margin,$this->img->right_margin,
+ $this->img->top_margin,$btotrequired);
+ }
+ }
+ }
+
+ // Stroke the graph
+ // $aStrokeFileName If != "" the image will be written to this file and NOT
+ // streamed back to the browser
+ function Stroke($aStrokeFileName="") {
+
+ // Start by adjusting the margin so that potential titles will fit.
+ $this->AdjustMarginsForTitles();
+
+ // If the filename is the predefined value = '_csim_special_'
+ // we assume that the call to stroke only needs to do enough
+ // to correctly generate the CSIM maps.
+ // We use this variable to skip things we don't strictly need
+ // to do to generate the image map to improve performance
+ // a best we can. Therefor you will see a lot of tests !$_csim in the
+ // code below.
+ $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
+
+ // We need to know if we have stroked the plot in the
+ // GetCSIMareas. Otherwise the CSIM hasn't been generated
+ // and in the case of GetCSIM called before stroke to generate
+ // CSIM without storing an image to disk GetCSIM must call Stroke.
+ $this->iHasStroked = true;
+
+ // Do any pre-stroke adjustment that is needed by the different plot types
+ // (i.e bar plots want's to add an offset to the x-labels etc)
+ for($i=0; $i<count($this->plots) ; ++$i ) {
+ $this->plots[$i]->PreStrokeAdjust($this);
+ $this->plots[$i]->DoLegend($this);
+ }
+
+ // Any plots on the second Y scale?
+ if( $this->y2scale != null ) {
+ for($i=0; $i<count($this->y2plots) ; ++$i ) {
+ $this->y2plots[$i]->PreStrokeAdjust($this);
+ $this->y2plots[$i]->DoLegend($this);
+ }
+ }
+
+ // Bail out if any of the Y-axis not been specified and
+ // has no plots. (This means it is impossible to do autoscaling and
+ // no other scale was given so we can't possible draw anything). If you use manual
+ // scaling you also have to supply the tick steps as well.
+ if( (!$this->yscale->IsSpecified() && count($this->plots)==0) ||
+ ($this->y2scale!=null && !$this->y2scale->IsSpecified() && count($this->y2plots)==0) ) {
+ //$e = "n=".count($this->y2plots)."\n";
+ $e = "Can't draw unspecified Y-scale.<br>\nYou have either:<br>\n";
+ $e .= "1. Specified an Y axis for autoscaling but have not supplied any plots<br>\n";
+ $e .= "2. Specified a scale manually but have forgot to specify the tick steps";
+ JpGraphError::Raise($e);
+ }
+
+ // Bail out if no plots and no specified X-scale
+ if( (!$this->xscale->IsSpecified() && count($this->plots)==0 && count($this->y2plots)==0) )
+ JpGraphError::Raise("<strong>JpGraph: Can't draw unspecified X-scale.</strong><br>No plots.<br>");
+
+ //Check if we should autoscale y-axis
+ if( !$this->yscale->IsSpecified() && count($this->plots)>0 ) {
+ list($min,$max) = $this->GetPlotsYMinMax($this->plots);
+ $lres = $this->GetLinesYMinMax($this->lines);
+ if( $lres ) {
+ list($linmin,$linmax) = $lres ;
+ $min = min($min,$linmin);
+ $max = max($max,$linmax);
+ }
+ $tres = $this->GetTextsYMinMax();
+ if( $tres ) {
+ list($tmin,$tmax) = $tres ;
+ $min = min($min,$tmin);
+ $max = max($max,$tmax);
+ }
+ $this->yscale->AutoScale($this->img,$min,$max,
+ $this->img->plotheight/$this->ytick_factor);
+ }
+ elseif( $this->yscale->IsSpecified() &&
+ ( $this->yscale->auto_ticks || !$this->yscale->ticks->IsSpecified()) ) {
+ // The tick calculation will use the user suplied min/max values to determine
+ // the ticks. If auto_ticks is false the exact user specifed min and max
+ // values will be used for the scale.
+ // If auto_ticks is true then the scale might be slightly adjusted
+ // so that the min and max values falls on an even major step.
+ $min = $this->yscale->scale[0];
+ $max = $this->yscale->scale[1];
+ $this->yscale->AutoScale($this->img,$min,$max,
+ $this->img->plotheight/$this->ytick_factor,
+ $this->yscale->auto_ticks);
+ }
+
+ if( $this->y2scale != null) {
+ if( !$this->y2scale->IsSpecified() && count($this->y2plots)>0 ) {
+ list($min,$max) = $this->GetPlotsYMinMax($this->y2plots);
+
+ $lres = $this->GetLinesYMinMax($this->y2lines);
+ if( $lres ) {
+ list($linmin,$linmax) = $lres ;
+ $min = min($min,$linmin);
+ $max = max($max,$linmax);
+ }
+ $tres = $this->GetTextsYMinMax(true);
+ if( $tres ) {
+ list($tmin,$tmax) = $tres ;
+ $min = min($min,$tmin);
+ $max = max($max,$tmax);
+ }
+ $this->y2scale->AutoScale($this->img,$min,$max,$this->img->plotheight/$this->ytick_factor);
+ }
+ elseif( $this->y2scale->IsSpecified() &&
+ ( $this->y2scale->auto_ticks || !$this->y2scale->ticks->IsSpecified()) ) {
+ // The tick calculation will use the user suplied min/max values to determine
+ // the ticks. If auto_ticks is false the exact user specifed min and max
+ // values will be used for the scale.
+ // If auto_ticks is true then the scale might be slightly adjusted
+ // so that the min and max values falls on an even major step.
+ $min = $this->y2scale->scale[0];
+ $max = $this->y2scale->scale[1];
+ $this->y2scale->AutoScale($this->img,$min,$max,
+ $this->img->plotheight/$this->ytick_factor,
+ $this->y2scale->auto_ticks);
+ }
+ }
+
+ //Check if we should autoscale x-axis
+ if( !$this->xscale->IsSpecified() ) {
+ if( substr($this->axtype,0,4) == "text" ) {
+ $max=0;
+ foreach( $this->plots as $p ) {
+ $max=max($max,$p->numpoints-1);
+ }
+ $min=0;
+ if( $this->y2axis != null ) {
+ foreach( $this->y2plots as $p ) {
+ $max=max($max,$p->numpoints-1);
+ }
+ }
+ $this->xscale->Update($this->img,$min,$max);
+ $this->xscale->ticks->Set($this->xaxis->tick_step,1);
+ $this->xscale->ticks->SupressMinorTickMarks();
+ }
+ else {
+ list($min,$max) = $this->GetXMinMax();
+ $lres = $this->GetLinesXMinMax($this->lines);
+ if( $lres ) {
+ list($linmin,$linmax) = $lres ;
+ $min = min($min,$linmin);
+ $max = max($max,$linmax);
+ }
+
+ $lres = $this->GetLinesXMinMax($this->y2lines);
+ if( $lres ) {
+ list($linmin,$linmax) = $lres ;
+ $min = min($min,$linmin);
+ $max = max($max,$linmax);
+ }
+
+ $tres = $this->GetTextsXMinMax();
+ if( $tres ) {
+ list($tmin,$tmax) = $tres ;
+ $min = min($min,$tmin);
+ $max = max($max,$tmax);
+ }
+
+ $tres = $this->GetTextsXMinMax(true);
+ if( $tres ) {
+ list($tmin,$tmax) = $tres ;
+ $min = min($min,$tmin);
+ $max = max($max,$tmax);
+ }
+
+ $this->xscale->AutoScale($this->img,$min,$max,$this->img->plotwidth/$this->xtick_factor);
+ }
+
+ //Adjust position of y-axis and y2-axis to minimum/maximum of x-scale
+ if( !is_numeric($this->yaxis->pos) && !is_string($this->yaxis->pos) )
+ $this->yaxis->SetPos($this->xscale->GetMinVal());
+ if( $this->y2axis != null ) {
+ if( !is_numeric($this->y2axis->pos) && !is_string($this->y2axis->pos) )
+ $this->y2axis->SetPos($this->xscale->GetMaxVal());
+ $this->y2axis->SetTitleSide(SIDE_RIGHT);
+ }
+ }
+ elseif( $this->xscale->IsSpecified() &&
+ ( $this->xscale->auto_ticks || !$this->xscale->ticks->IsSpecified()) ) {
+ // The tick calculation will use the user suplied min/max values to determine
+ // the ticks. If auto_ticks is false the exact user specifed min and max
+ // values will be used for the scale.
+ // If auto_ticks is true then the scale might be slightly adjusted
+ // so that the min and max values falls on an even major step.
+ $min = $this->xscale->scale[0];
+ $max = $this->xscale->scale[1];
+ $this->xscale->AutoScale($this->img,$min,$max,
+ $this->img->plotwidth/$this->xtick_factor,
+ false);
+
+ if( $this->y2axis != null ) {
+ if( !is_numeric($this->y2axis->pos) && !is_string($this->y2axis->pos) )
+ $this->y2axis->SetPos($this->xscale->GetMaxVal());
+ $this->y2axis->SetTitleSide(SIDE_RIGHT);
+ }
+
+ }
+
+ // If we have a negative values and x-axis position is at 0
+ // we need to supress the first and possible the last tick since
+ // they will be drawn on top of the y-axis (and possible y2 axis)
+ // The test below might seem strange the reasone being that if
+ // the user hasn't specified a value for position this will not
+ // be set until we do the stroke for the axis so as of now it
+ // is undefined.
+ // For X-text scale we ignore all this since the tick are usually
+ // much further in and not close to the Y-axis. Hence the test
+ // for 'text'
+
+ if( ($this->yaxis->pos==$this->xscale->GetMinVal() ||
+ (is_string($this->yaxis->pos) && $this->yaxis->pos=='min')) &&
+ !is_numeric($this->xaxis->pos) && $this->yscale->GetMinVal() < 0 &&
+ substr($this->axtype,0,4) != 'text' && $this->xaxis->pos!="min" ) {
+
+ //$this->yscale->ticks->SupressZeroLabel(false);
+ $this->xscale->ticks->SupressFirst();
+ if( $this->y2axis != null ) {
+ $this->xscale->ticks->SupressLast();
+ }
+ }
+ elseif( !is_numeric($this->yaxis->pos) && $this->yaxis->pos=='max' ) {
+ $this->xscale->ticks->SupressLast();
+ }
+
+
+ if( !$_csim ) {
+ $this->StrokePlotArea();
+ $this->StrokeAxis();
+ }
+
+ // Stroke bands
+ if( $this->bands != null && !$_csim)
+ for($i=0; $i < count($this->bands); ++$i) {
+ // Stroke all bands that asks to be in the background
+ if( $this->bands[$i]->depth == DEPTH_BACK )
+ $this->bands[$i]->Stroke($this->img,$this->xscale,$this->yscale);
+ }
+
+ if( $this->y2bands != null && $this->y2scale != null && !$_csim )
+ for($i=0; $i < count($this->y2bands); ++$i) {
+ // Stroke all bands that asks to be in the foreground
+ if( $this->y2bands[$i]->depth == DEPTH_BACK )
+ $this->y2bands[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
+ }
+
+
+ if( $this->grid_depth == DEPTH_BACK && !$_csim) {
+ $this->ygrid->Stroke();
+ $this->xgrid->Stroke();
+ }
+
+ // Stroke Y2-axis
+ if( $this->y2axis != null && !$_csim) {
+ $this->y2axis->Stroke($this->xscale);
+ $this->y2grid->Stroke();
+ }
+
+ $oldoff=$this->xscale->off;
+ if(substr($this->axtype,0,4)=="text") {
+ $this->xscale->off +=
+ ceil($this->xscale->scale_factor*$this->text_scale_off*$this->xscale->ticks->minor_step);
+ }
+
+ if( $this->iDoClipping ) {
+ $oldimage = $this->img->CloneCanvasH();
+ }
+
+ if( ! $this->y2orderback ) {
+ // Stroke all plots for Y1 axis
+ for($i=0; $i < count($this->plots); ++$i) {
+ $this->plots[$i]->Stroke($this->img,$this->xscale,$this->yscale);
+ $this->plots[$i]->StrokeMargin($this->img);
+ }
+ }
+
+ // Stroke all plots for Y2 axis
+ if( $this->y2scale != null )
+ for($i=0; $i< count($this->y2plots); ++$i ) {
+ $this->y2plots[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
+ }
+
+ if( $this->y2orderback ) {
+ // Stroke all plots for Y1 axis
+ for($i=0; $i < count($this->plots); ++$i) {
+ $this->plots[$i]->Stroke($this->img,$this->xscale,$this->yscale);
+ $this->plots[$i]->StrokeMargin($this->img);
+ }
+ }
+
+
+ if( $this->iDoClipping ) {
+ // Clipping only supports graphs at 0 and 90 degrees
+ if( $this->img->a == 0 ) {
+ $this->img->CopyCanvasH($oldimage,$this->img->img,
+ $this->img->left_margin,$this->img->top_margin,
+ $this->img->left_margin,$this->img->top_margin,
+ $this->img->plotwidth+1,$this->img->plotheight);
+ }
+ elseif( $this->img->a == 90 ) {
+ $adj = ($this->img->height - $this->img->width)/2;
+ $this->img->CopyCanvasH($oldimage,$this->img->img,
+ $this->img->bottom_margin-$adj,$this->img->left_margin+$adj,
+ $this->img->bottom_margin-$adj,$this->img->left_margin+$adj,
+ $this->img->plotheight+1,$this->img->plotwidth);
+ }
+ else {
+ JpGraphError::Raise('You have enabled clipping. Cliping is only supported for graphs at 0 or 90 degrees rotation. Please adjust you current angle (='.$this->img->a.' degrees) or disable clipping.');
+ }
+ $this->img->Destroy();
+ $this->img->SetCanvasH($oldimage);
+ }
+
+ $this->xscale->off=$oldoff;
+
+ if( $this->grid_depth == DEPTH_FRONT && !$_csim ) {
+ $this->ygrid->Stroke();
+ $this->xgrid->Stroke();
+ }
+
+ // Stroke bands
+ if( $this->bands!= null )
+ for($i=0; $i < count($this->bands); ++$i) {
+ // Stroke all bands that asks to be in the foreground
+ if( $this->bands[$i]->depth == DEPTH_FRONT )
+ $this->bands[$i]->Stroke($this->img,$this->xscale,$this->yscale);
+ }
+
+ if( $this->y2bands!= null && $this->y2scale != null )
+ for($i=0; $i < count($this->y2bands); ++$i) {
+ // Stroke all bands that asks to be in the foreground
+ if( $this->y2bands[$i]->depth == DEPTH_FRONT )
+ $this->y2bands[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
+ }
+
+
+ // Stroke any lines added
+ if( $this->lines != null ) {
+ for($i=0; $i < count($this->lines); ++$i) {
+ $this->lines[$i]->Stroke($this->img,$this->xscale,$this->yscale);
+ }
+ }
+
+ if( $this->y2lines != null && $this->y2scale != null ) {
+ for($i=0; $i < count($this->y2lines); ++$i) {
+ $this->y2lines[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
+ }
+ }
+
+
+ // Finally draw the axis again since some plots may have nagged
+ // the axis in the edges.
+ if( !$_csim )
+ $this->StrokeAxis();
+
+ if( $this->y2scale != null && !$_csim )
+ $this->y2axis->Stroke($this->xscale);
+
+ if( !$_csim ) {
+ $this->StrokePlotBox();
+ }
+
+ if( !$_csim ) {
+ // The titles and legends never gets rotated so make sure
+ // that the angle is 0 before stroking them
+ $aa = $this->img->SetAngle(0);
+ $this->StrokeTitles();
+ $this->footer->Stroke($this->img);
+ }
+
+ $this->legend->Stroke($this->img);
+
+ if( !$_csim ) {
+
+ $this->StrokeTexts();
+ $this->img->SetAngle($aa);
+
+ // Draw an outline around the image map
+ if(JPG_DEBUG)
+ $this->DisplayClientSideaImageMapAreas();
+
+ // Adjust the appearance of the image
+ $this->AdjustSaturationBrightnessContrast();
+
+ // If the filename is given as the special "__handle"
+ // then the image handler is returned and the image is NOT
+ // streamed back
+ if( $aStrokeFileName == _IMG_HANDLER ) {
+ return $this->img->img;
+ }
+ else {
+ // Finally stream the generated picture
+ $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
+ $aStrokeFileName);
+ }
+ }
+ }
+
+//---------------
+// PRIVATE METHODS
+ function StrokeAxis() {
+
+ // Stroke axis
+ if( $this->iAxisStyle != AXSTYLE_SIMPLE ) {
+ switch( $this->iAxisStyle ) {
+ case AXSTYLE_BOXIN :
+ $toppos = SIDE_DOWN;
+ $bottompos = SIDE_UP;
+ $leftpos = SIDE_RIGHT;
+ $rightpos = SIDE_LEFT;
+ break;
+ case AXSTYLE_BOXOUT :
+ $toppos = SIDE_UP;
+ $bottompos = SIDE_DOWN;
+ $leftpos = SIDE_LEFT;
+ $rightpos = SIDE_RIGHT;
+ break;
+ case AXSTYLE_YBOXIN:
+ $toppos = -100;
+ $bottompos = SIDE_UP;
+ $leftpos = SIDE_RIGHT;
+ $rightpos = SIDE_LEFT;
+ break;
+ case AXSTYLE_YBOXOUT:
+ $toppos = -100;
+ $bottompos = SIDE_DOWN;
+ $leftpos = SIDE_LEFT;
+ $rightpos = SIDE_RIGHT;
+ break;
+ default:
+ JpGRaphError::Raise('Unknown AxisStyle() : '.$this->iAxisStyle);
+ break;
+ }
+ $this->xaxis->SetPos('min');
+
+ // By default we hide the first label so it doesn't cross the
+ // Y-axis in case the positon hasn't been set by the user.
+ // However, if we use a box we always want the first value
+ // displayed so we make sure it will be displayed.
+ $this->xscale->ticks->SupressFirst(false);
+
+ $this->xaxis->SetLabelSide(SIDE_DOWN);
+ $this->xaxis->scale->ticks->SetSide($bottompos);
+ $this->xaxis->Stroke($this->yscale);
+
+ if( $toppos != -100 ) {
+ // To avoid side effects we work on a new copy
+ $maxis = $this->xaxis;
+ $maxis->SetPos('max');
+ $maxis->SetLabelSide(SIDE_UP);
+ $maxis->SetLabelMargin(7);
+ $this->xaxis->scale->ticks->SetSide($toppos);
+ $maxis->Stroke($this->yscale);
+ }
+
+ $this->yaxis->SetPos('min');
+ $this->yaxis->SetLabelMargin(10);
+ $this->yaxis->SetLabelSide(SIDE_LEFT);
+ $this->yaxis->scale->ticks->SetSide($leftpos);
+ $this->yaxis->Stroke($this->xscale);
+
+ $myaxis = $this->yaxis;
+ $myaxis->SetPos('max');
+ $myaxis->SetLabelMargin(10);
+ $myaxis->SetLabelSide(SIDE_RIGHT);
+ $myaxis->title->Set('');
+ $myaxis->scale->ticks->SetSide($rightpos);
+ $myaxis->Stroke($this->xscale);
+
+ }
+ else {
+ $this->xaxis->Stroke($this->yscale);
+ $this->yaxis->Stroke($this->xscale);
+ }
+ }
+
+
+ // Private helper function for backgound image
+ function LoadBkgImage($aImgFormat='',$aFile='') {
+ if( $aFile == '' )
+ $aFile = $this->background_image;
+ // Remove case sensitivity and setup appropriate function to create image
+ // Get file extension. This should be the LAST '.' separated part of the filename
+ $e = explode('.',$aFile);
+ $ext = strtolower($e[count($e)-1]);
+ if ($ext == "jpeg") {
+ $ext = "jpg";
+ }
+
+ if( trim($ext) == '' )
+ $ext = 'png'; // Assume PNG if no extension specified
+
+ if( $aImgFormat == '' )
+ $imgtag = $ext;
+ else
+ $imgtag = $aImgFormat;
+
+ if( $imgtag == "jpg" || $imgtag == "jpeg")
+ {
+ $f = "imagecreatefromjpeg";
+ $imgtag = "jpg";
+ }
+ else
+ {
+ $f = "imagecreatefrom".$imgtag;
+ }
+
+ // Compare specified image type and file extension
+ if( $imgtag != $ext ) {
+ $t = " Background image seems to be of different type (has different file extension)".
+ " than specified imagetype. Specified: '".
+ $aImgFormat."'File: '".$aFile."'";
+ JpGraphError::Raise($t);
+ }
+
+ $img = @$f($aFile);
+ if( !$img ) {
+ JpGraphError::Raise(" Can't read background image: '".$aFile."'");
+ }
+ return $img;
+ }
+
+ function StrokeBackgroundGrad() {
+ if( $this->bkg_gradtype < 0 )
+ return;
+ $grad = new Gradient($this->img);
+ if( $this->bkg_gradstyle == BGRAD_PLOT ) {
+ $xl = $this->img->left_margin;
+ $yt = $this->img->top_margin;
+ $xr = $xl + $this->img->plotwidth ;
+ $yb = $yt + $this->img->plotheight ;
+ }
+ else {
+ $xl = 0;
+ $yt = 0;
+ $xr = $xl + $this->img->width - 1;
+ $yb = $yt + $this->img->height - 1;
+ }
+ if( $this->doshadow ) {
+ $xr -= $this->shadow_width;
+ $yb -= $this->shadow_width;
+ }
+ if( $this->doframe ) {
+
+ $xl += $this->frame_weight;
+ $xr -= $this->frame_weight;
+ }
+ $grad->FilledRectangle($xl,$yt,$xr,$yb,
+ $this->bkg_gradfrom,$this->bkg_gradto,
+ $this->bkg_gradtype);
+ }
+
+ function StrokeFrameBackground() {
+ if( $this->background_image == "" )
+ return;
+
+ $bkgimg = $this->LoadBkgImage($this->background_image_format);
+ $this->img->_AdjBrightContrast($bkgimg,$this->background_image_bright,
+ $this->background_image_contr);
+ $this->img->_AdjSat($bkgimg,$this->background_image_sat);
+ $bw = ImageSX($bkgimg);
+ $bh = ImageSY($bkgimg);
+
+ // No matter what the angle is we always stroke the image and frame
+ // assuming it is 0 degree
+ $aa = $this->img->SetAngle(0);
+
+ switch( $this->background_image_type ) {
+ case BGIMG_FILLPLOT: // Resize to just fill the plotarea
+ $this->StrokeFrame();
+ $GLOBALS['copyfunc']($this->img->img,$bkgimg,
+ $this->img->left_margin,$this->img->top_margin,
+ 0,0,$this->img->plotwidth,$this->img->plotheight,
+ $bw,$bh);
+ break;
+ case BGIMG_FILLFRAME: // Fill the whole area from upper left corner, resize to just fit
+ $GLOBALS['copyfunc']($this->img->img,$bkgimg,
+ 0,0,0,0,
+ $this->img->width,$this->img->height,
+ $bw,$bh);
+ $this->StrokeFrame();
+ break;
+ case BGIMG_COPY: // Just copy the image from left corner, no resizing
+ $GLOBALS['copyfunc']($this->img->img,$bkgimg,
+ 0,0,0,0,
+ $bw,$bh,
+ $bw,$bh);
+ $this->StrokeFrame();
+ break;
+ case BGIMG_CENTER: // Center original image in the plot area
+ $centerx = round($this->img->plotwidth/2+$this->img->left_margin-$bw/2);
+ $centery = round($this->img->plotheight/2+$this->img->top_margin-$bh/2);
+ $GLOBALS['copyfunc']($this->img->img,$bkgimg,
+ $centerx,$centery,
+ 0,0,
+ $bw,$bh,
+ $bw,$bh);
+ $this->StrokeFrame();
+ break;
+ default:
+ JpGraphError::Raise(" Unknown background image layout");
+ }
+ $this->img->SetAngle($aa);
+ }
+
+ // Private
+ // Draw a frame around the image
+ function StrokeFrame() {
+ if( !$this->doframe ) return;
+ if( $this->background_image_type <= 1 &&
+ ($this->bkg_gradtype < 0 ||
+ ($this->bkg_gradtype > 0 && $this->bkg_gradstyle==BGRAD_PLOT) ) )
+ $c = $this->margin_color;
+ else
+ $c = false;
+
+ if( $this->doshadow ) {
+ $this->img->SetColor($this->frame_color);
+ $this->img->ShadowRectangle(0,0,$this->img->width,$this->img->height,
+ $c,$this->shadow_width,$this->shadow_color);
+ }
+ elseif( $this->framebevel ) {
+ if( $c ) {
+ $this->img->SetColor($this->margin_color);
+ $this->img->FilledRectangle(0,0,$this->img->width-1,$this->img->height-1);
+ }
+ $this->img->Bevel(1,1,$this->img->width-2,$this->img->height-2,
+ $this->framebeveldepth,
+ $this->framebevelcolor1,$this->framebevelcolor2);
+ if( $this->framebevelborder ) {
+ $this->img->SetColor($this->framebevelbordercolor);
+ $this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1);
+ }
+ }
+ else {
+ $this->img->SetLineWeight($this->frame_weight);
+ if( $c ) {
+ $this->img->SetColor($this->margin_color);
+ $this->img->FilledRectangle(0,0,$this->img->width-1,$this->img->height-1);
+ }
+ $this->img->SetColor($this->frame_color);
+ $this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1);
+ }
+ }
+
+ // Stroke the plot area with either a solid color or a background image
+ function StrokePlotArea() {
+ // Note: To be consistent we really should take a possible shadow
+ // into account. However, that causes some problem for the LinearScale class
+ // since in the current design it does not have any links to class Graph which
+ // means it has no way of compensating for the adjusted plotarea in case of a
+ // shadow. So, until I redesign LinearScale we can't compensate for this.
+ // So just set the two adjustment parameters to zero for now.
+ $boxadj = 0; //$this->doframe ? $this->frame_weight : 0 ;
+ $adj = 0; //$this->doshadow ? $this->shadow_width : 0 ;
+
+ if( $this->background_image != "" ) {
+ $this->StrokeFrameBackground();
+ }
+ else {
+ $aa = $this->img->SetAngle(0);
+ $this->StrokeFrame();
+ $aa = $this->img->SetAngle($aa);
+ $this->StrokeBackgroundGrad();
+ if( $this->bkg_gradtype < 0 ||
+ ($this->bkg_gradtype > 0 && $this->bkg_gradstyle==BGRAD_MARGIN ) ) {
+ $this->img->PushColor($this->plotarea_color);
+ $this->img->FilledRectangle($this->img->left_margin+$boxadj,
+ $this->img->top_margin+$boxadj,
+ $this->img->width-$this->img->right_margin-$adj-2*$boxadj,
+ $this->img->height-$this->img->bottom_margin-$adj-2*$boxadj);
+ $this->img->PopColor();
+ }
+ }
+ }
+
+
+ function StrokePlotBox() {
+ // Should we draw a box around the plot area?
+ if( $this->boxed ) {
+ $this->img->SetLineWeight(1);
+ $this->img->SetColor($this->box_color);
+ for($i=0; $i < $this->box_weight; ++$i ) {
+ $this->img->Rectangle(
+ $this->img->left_margin-$i,$this->img->top_margin-$i,
+ $this->img->width-$this->img->right_margin+$i,
+ $this->img->height-$this->img->bottom_margin+$i);
+ }
+ }
+ }
+
+ function SetTitleBackgroundFillStyle($aStyle,$aColor1='black',$aColor2='white') {
+ $this->titlebkg_fillstyle = $aStyle;
+ $this->titlebkg_scolor1 = $aColor1;
+ $this->titlebkg_scolor2 = $aColor2;
+ }
+
+ function SetTitleBackground($aBackColor='gray', $aStyle=TITLEBKG_STYLE1, $aFrameStyle=TITLEBKG_FRAME_NONE, $aFrameColor='black', $aFrameWeight=1, $aBevelHeight=3, $aEnable=true) {
+ $this->titlebackground = $aEnable;
+ $this->titlebackground_color = $aBackColor;
+ $this->titlebackground_style = $aStyle;
+ $this->titlebackground_framecolor = $aFrameColor;
+ $this->titlebackground_framestyle = $aFrameStyle;
+ $this->titlebackground_frameweight = $aFrameWeight;
+ $this->titlebackground_bevelheight = $aBevelHeight ;
+ }
+
+
+ function StrokeTitles() {
+
+ $margin=3;
+
+ if( $this->titlebackground ) {
+
+ // Find out height
+ $this->title->margin += 2 ;
+ $h = $this->title->GetTextHeight($this->img)+$this->title->margin+$margin;
+ if( $this->subtitle->t != "" && !$this->subtitle->hide ) {
+ $h += $this->subtitle->GetTextHeight($this->img)+$margin+
+ $this->subtitle->margin;
+ }
+ if( $this->subsubtitle->t != "" && !$this->subsubtitle->hide ) {
+ $h += $this->subsubtitle->GetTextHeight($this->img)+$margin+
+ $this->subsubtitle->margin;
+ }
+ $this->img->PushColor($this->titlebackground_color);
+ if( $this->titlebackground_style === 1 ) {
+ // Inside the frame
+ if( $this->framebevel ) {
+ $x1 = $y1 = $this->framebeveldepth + 1 ;
+ $x2 = $this->img->width - $this->framebeveldepth - 2 ;
+ $this->title->margin += $this->framebeveldepth + 1 ;
+ $h += $y1 ;
+ }
+ else {
+ $x1 = $y1 = $this->frame_weight;
+ $x2 = $this->img->width - 2*$x1;
+ }
+ }
+ elseif( $this->titlebackground_style === 2 ) {
+ // Cover the frame as well
+ $x1 = $y1 = 0;
+ $x2 = $this->img->width - 1 ;
+ }
+ elseif( $this->titlebackground_style === 3) {
+ // Cover the frame as well (the difference is that
+ // for style==3 a bevel frame border is on top
+ // of the title background)
+ $x1 = $y1 = 0;
+ $x2 = $this->img->width - 1 ;
+ $h += $this->framebeveldepth ;
+ $this->title->margin += $this->framebeveldepth ;
+ }
+ else {
+ JpGraphError::Raise('Unknown title background style.');
+ }
+
+ if( $this->titlebackground_framestyle === 3 ) {
+ $h += $this->titlebackground_bevelheight*2 + 1 ;
+ $this->title->margin += $this->titlebackground_bevelheight ;
+ }
+
+ if( $this->doshadow ) {
+ $x2 -= $this->shadow_width ;
+ }
+
+ if( $this->titlebkg_fillstyle==TITLEBKG_FILLSTYLE_HSTRIPED ) {
+ $this->img->FilledRectangle2($x1,$y1,$x2,$h,
+ $this->titlebkg_scolor1,
+ $this->titlebkg_scolor2);
+ }
+ elseif( $this->titlebkg_fillstyle==TITLEBKG_FILLSTYLE_VSTRIPED ) {
+ $this->img->FilledRectangle2($x1,$y1,$x2,$h,
+ $this->titlebkg_scolor1,
+ $this->titlebkg_scolor2,2);
+ }
+ else {
+ // Solid fill
+ $this->img->FilledRectangle($x1,$y1,$x2,$h);
+ }
+ $this->img->PopColor();
+
+ $this->img->PushColor($this->titlebackground_framecolor);
+ $this->img->SetLineWeight($this->titlebackground_frameweight);
+ if( $this->titlebackground_framestyle == 1 ) {
+ // Frame background
+ $this->img->Rectangle($x1,$y1,$x2,$h);
+ }
+ elseif( $this->titlebackground_framestyle == 2 ) {
+ // Bottom line only
+ $this->img->Line($x1,$h,$x2,$h);
+ }
+ elseif( $this->titlebackground_framestyle == 3 ) {
+ $this->img->Bevel($x1,$y1,$x2,$h,$this->titlebackground_bevelheight);
+ }
+ $this->img->PopColor();
+
+ // This is clumsy. But we neeed to stroke the whole graph frame if it is
+ // set to bevel to get the bevel shading on top of the text background
+ if( $this->framebevel && $this->doframe &&
+ $this->titlebackground_style === 3 ) {
+ $this->img->Bevel(1,1,$this->img->width-2,$this->img->height-2,
+ $this->framebeveldepth,
+ $this->framebevelcolor1,$this->framebevelcolor2);
+ if( $this->framebevelborder ) {
+ $this->img->SetColor($this->framebevelbordercolor);
+ $this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1);
+ }
+ }
+ }
+
+ // Stroke title
+ $y = $this->title->margin;
+ $this->title->Center(0,$this->img->width,$y);
+ $this->title->Stroke($this->img);
+
+ // ... and subtitle
+ $y += $this->title->GetTextHeight($this->img) + $margin + $this->subtitle->margin;
+ $this->subtitle->Center(0,$this->img->width,$y);
+ $this->subtitle->Stroke($this->img);
+
+ // ... and subsubtitle
+ $y += $this->subtitle->GetTextHeight($this->img) + $margin + $this->subsubtitle->margin;
+ $this->subsubtitle->Center(0,$this->img->width,$y);
+ $this->subsubtitle->Stroke($this->img);
+
+ // ... and fancy title
+ $this->tabtitle->Stroke($this->img);
+
+ }
+
+ function StrokeTexts() {
+ // Stroke any user added text objects
+ if( $this->texts != null ) {
+ for($i=0; $i < count($this->texts); ++$i) {
+ $this->texts[$i]->StrokeWithScale($this->img,$this->xscale,$this->yscale);
+ }
+ }
+
+ if( $this->y2texts != null && $this->y2scale != null ) {
+ for($i=0; $i < count($this->y2texts); ++$i) {
+ $this->y2texts[$i]->StrokeWithScale($this->img,$this->xscale,$this->y2scale);
+ }
+ }
+
+ }
+
+ function DisplayClientSideaImageMapAreas() {
+ // Debug stuff - display the outline of the image map areas
+ foreach ($this->plots as $p) {
+ $csim.= $p->GetCSIMareas();
+ }
+ $csim .= $this->legend->GetCSIMareas();
+ if (preg_match_all("/area shape=\"(\w+)\" coords=\"([0-9\, ]+)\"/", $csim, $coords)) {
+ $this->img->SetColor($this->csimcolor);
+ for ($i=0; $i<count($coords[0]); $i++) {
+ if ($coords[1][$i]=="poly") {
+ preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/',$coords[2][$i],$pts);
+ $this->img->SetStartPoint($pts[1][count($pts[0])-1],$pts[2][count($pts[0])-1]);
+ for ($j=0; $j<count($pts[0]); $j++) {
+ $this->img->LineTo($pts[1][$j],$pts[2][$j]);
+ }
+ } else if ($coords[1][$i]=="rect") {
+ $pts = preg_split('/,/', $coords[2][$i]);
+ $this->img->SetStartPoint($pts[0],$pts[1]);
+ $this->img->LineTo($pts[2],$pts[1]);
+ $this->img->LineTo($pts[2],$pts[3]);
+ $this->img->LineTo($pts[0],$pts[3]);
+ $this->img->LineTo($pts[0],$pts[1]);
+ }
+ }
+ }
+ }
+
+ function AdjustSaturationBrightnessContrast() {
+ // Adjust the brightness and contrast of the image
+ if( $this->image_contr || $this->image_bright )
+ $this->img->AdjBrightContrast($this->image_bright,$this->image_contr);
+ if( $this->image_sat )
+ $this->img->AdjSat($this->image_sat);
+ }
+
+ // Text scale offset in world coordinates
+ function SetTextScaleOff($aOff) {
+ $this->text_scale_off = $aOff;
+ $this->xscale->text_scale_off = $aOff;
+ }
+
+ // Get Y min and max values for added lines
+ function GetLinesYMinMax( $aLines ) {
+ $n = count($aLines);
+ if( $n == 0 ) return false;
+ $min = $aLines[0]->scaleposition ;
+ $max = $min ;
+ $flg = false;
+ for( $i=0; $i < $n; ++$i ) {
+ if( $aLines[$i]->direction == HORIZONTAL ) {
+ $flg = true ;
+ $v = $aLines[$i]->scaleposition ;
+ if( $min > $v ) $min = $v ;
+ if( $max < $v ) $max = $v ;
+ }
+ }
+ return $flg ? array($min,$max) : false ;
+ }
+
+ // Get X min and max values for added lines
+ function GetLinesXMinMax( $aLines ) {
+ $n = count($aLines);
+ if( $n == 0 ) return false ;
+ $min = $aLines[0]->scaleposition ;
+ $max = $min ;
+ $flg = false;
+ for( $i=0; $i < $n; ++$i ) {
+ if( $aLines[$i]->direction == VERTICAL ) {
+ $flg = true ;
+ $v = $aLines[$i]->scaleposition ;
+ if( $min > $v ) $min = $v ;
+ if( $max < $v ) $max = $v ;
+ }
+ }
+ return $flg ? array($min,$max) : false ;
+ }
+
+ // Get min and max values for all included plots
+ function GetPlotsYMinMax(&$aPlots) {
+ list($xmax,$max) = $aPlots[0]->Max();
+ list($xmin,$min) = $aPlots[0]->Min();
+ for($i=0; $i<count($aPlots); ++$i ) {
+ list($xmax,$ymax)=$aPlots[$i]->Max();
+ list($xmin,$ymin)=$aPlots[$i]->Min();
+ if (!is_string($ymax) || $ymax != "") $max=max($max,$ymax);
+ if (!is_string($ymin) || $ymin != "") $min=min($min,$ymin);
+ }
+ if( $min == "" ) $min = 0;
+ if( $max == "" ) $max = 0;
+ if( $min == 0 && $max == 0 ) {
+ // Special case if all values are 0
+ $min=0;$max=1;
+ }
+ return array($min,$max);
+ }
+
+} // Class
+
+
+//===================================================
+// CLASS TTF
+// Description: Handle TTF font names
+//===================================================
+class TTF {
+ var $font_files,$style_names;
+//---------------
+// CONSTRUCTOR
+ function TTF() {
+ $this->style_names=array(FS_NORMAL=>'normal',FS_BOLD=>'bold',FS_ITALIC=>'italic',FS_BOLDITALIC=>'bolditalic');
+ // File names for available fonts
+ $this->font_files=array(
+ FF_COURIER => array(FS_NORMAL=>'cour.ttf', FS_BOLD=>'courbd.ttf', FS_ITALIC=>'couri.ttf', FS_BOLDITALIC=>'courbi.ttf' ),
+ FF_GEORGIA => array(FS_NORMAL=>'georgia.ttf', FS_BOLD=>'georgiab.ttf', FS_ITALIC=>'georgiai.ttf', FS_BOLDITALIC=>'' ),
+ FF_TREBUCHE =>array(FS_NORMAL=>'trebuc.ttf', FS_BOLD=>'trebucbd.ttf', FS_ITALIC=>'trebucit.ttf', FS_BOLDITALIC=>'trebucbi.ttf' ),
+ FF_VERDANA => array(FS_NORMAL=>'verdana.ttf', FS_BOLD=>'verdanab.ttf', FS_ITALIC=>'verdanai.ttf', FS_BOLDITALIC=>'' ),
+ FF_TIMES => array(FS_NORMAL=>'times.ttf', FS_BOLD=>'timesbd.ttf', FS_ITALIC=>'timesi.ttf', FS_BOLDITALIC=>'timesbi.ttf' ),
+ FF_COMIC => array(FS_NORMAL=>'comic.ttf', FS_BOLD=>'comicbd.ttf', FS_ITALIC=>'.ttf', FS_BOLDITALIC=>'' ),
+ FF_ARIAL => array(FS_NORMAL=>'arial.ttf', FS_BOLD=>'arialbd.ttf', FS_ITALIC=>'ariali.ttf', FS_BOLDITALIC=>'arialbi.ttf' ) ,
+ FF_SIMSUN => array(FS_NORMAL=>'simsun.ttc', FS_BOLD=>'simhei.ttf', FS_ITALIC=>'', FS_BOLDITALIC=>'' ),
+ FF_VERA => array(FS_NORMAL=>'Vera.ttf', FS_BOLD=>'VeraBd.ttf', FS_ITALIC=>'VeraIt.ttf', FS_BOLDITALIC=>'VeraBI.ttf' ),
+ FF_VERAMONO => array(FS_NORMAL=>'VeraMono.ttf', FS_BOLD=>'VeraMoBd.ttf', FS_ITALIC=>'VeraMoIt.ttf', FS_BOLDITALIC=>'VeraMoBI.ttf' ),
+ FF_VERASERIF => array(FS_NORMAL=>'VeraSe.ttf', FS_BOLD=>'VeraSeBd.ttf', FS_ITALIC=>'', FS_BOLDITALIC=>'' )
+);
+ }
+
+//---------------
+// PUBLIC METHODS
+ // Create the TTF file from the font specification
+ function File($family,$style=FS_NORMAL) {
+
+ if( $family == FF_HANDWRT || $family==FF_BOOK ) {
+ JpGraphError::Raise('Font families FF_HANDWRT and FF_BOOK are no longer available due to copyright problem with these fonts. Fonts can no longer be distributed with JpGraph. Please download fonts from http://corefonts.sourceforge.net/');
+ }
+
+ $fam = @$this->font_files[$family];
+ if( !$fam ) {
+ JpGraphError::Raise(
+ "Specified TTF font family (id=$family) is unknown or does not exist. ".
+ "Please note that TTF fonts are not distributed with JpGraph for copyright reasons.".
+ " You can find the MS TTF WEB-fonts (arial, courier etc) for download at ".
+ " http://corefonts.sourceforge.net/");
+ }
+ $f = @$fam[$style];
+
+ if( $f==='' )
+ JpGraphError::Raise('Style "'.$this->style_names[$style].'" is not available for font family '.$this->font_files[$family][FS_NORMAL].'.');
+ if( !$f )
+ JpGraphError::Raise("Unknown font style specification [$fam].");
+ $f = TTF_DIR.$f;
+ if( file_exists($f) === false || is_readable($f) === false ) {
+ JpGraphError::Raise("Font file \"$f\" is not readable or does not exist.");
+ }
+ return $f;
+ }
+} // Class
+
+//===================================================
+// CLASS LineProperty
+// Description: Holds properties for a line
+//===================================================
+class LineProperty {
+ var $iWeight=1, $iColor="black",$iStyle="solid";
+ var $iShow=true;
+
+//---------------
+// PUBLIC METHODS
+ function SetColor($aColor) {
+ $this->iColor = $aColor;
+ }
+
+ function SetWeight($aWeight) {
+ $this->iWeight = $aWeight;
+ }
+
+ function SetStyle($aStyle) {
+ $this->iStyle = $aStyle;
+ }
+
+ function Show($aShow=true) {
+ $this->iShow=$aShow;
+ }
+
+ function Stroke($aImg,$aX1,$aY1,$aX2,$aY2) {
+ if( $this->iShow ) {
+ $aImg->SetColor($this->iColor);
+ $aImg->SetLineWeight($this->iWeight);
+ $aImg->SetLineStyle($this->iStyle);
+ $aImg->StyleLine($aX1,$aY1,$aX2,$aY2);
+ }
+ }
+}
+
+
+//===================================================
+// CLASS Text
+// Description: Arbitrary text object that can be added to the graph
+//===================================================
+class Text {
+ var $t,$x=0,$y=0,$halign="left",$valign="top",$color=array(0,0,0);
+ var $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12;
+ var $hide=false, $dir=0;
+ var $boxed=false; // Should the text be boxed
+ var $paragraph_align="left";
+ var $margin;
+ var $icornerradius=0,$ishadowwidth=3;
+ var $iScalePosY=null,$iScalePosX=null;
+
+//---------------
+// CONSTRUCTOR
+
+ // Create new text at absolute pixel coordinates
+ function Text($aTxt="",$aXAbsPos=0,$aYAbsPos=0) {
+ $this->t = $aTxt;
+ $this->x = round($aXAbsPos);
+ $this->y = round($aYAbsPos);
+ $this->margin = 0;
+ }
+//---------------
+// PUBLIC METHODS
+ // Set the string in the text object
+ function Set($aTxt) {
+ $this->t = $aTxt;
+ }
+
+ // Alias for Pos()
+ function SetPos($aXAbsPos=0,$aYAbsPos=0,$aHAlign="left",$aVAlign="top") {
+ $this->Pos($aXAbsPos,$aYAbsPos,$aHAlign,$aVAlign);
+ }
+
+ // Specify the position and alignment for the text object
+ function Pos($aXAbsPos=0,$aYAbsPos=0,$aHAlign="left",$aVAlign="top") {
+ $this->x = $aXAbsPos;
+ $this->y = $aYAbsPos;
+ $this->halign = $aHAlign;
+ $this->valign = $aVAlign;
+ }
+
+ function SetScalePos($aX,$aY) {
+ $this->iScalePosX = $aX;
+ $this->iScalePosY = $aY;
+ }
+
+ // Specify alignment for the text
+ function Align($aHAlign,$aVAlign="top",$aParagraphAlign="") {
+ $this->halign = $aHAlign;
+ $this->valign = $aVAlign;
+ if( $aParagraphAlign != "" )
+ $this->paragraph_align = $aParagraphAlign;
+ }
+
+ // Alias
+ function SetAlign($aHAlign,$aVAlign="top",$aParagraphAlign="") {
+ $this->Align($aHAlign,$aVAlign,$aParagraphAlign);
+ }
+
+ // Specifies the alignment for a multi line text
+ function ParagraphAlign($aAlign) {
+ $this->paragraph_align = $aAlign;
+ }
+
+ function SetShadow($aShadowColor='gray',$aShadowWidth=3) {
+ $this->ishadowwidth=$aShadowWidth;
+ $this->shadow=$aShadowColor;
+ $this->boxed=true;
+ }
+
+ // Specify that the text should be boxed. fcolor=frame color, bcolor=border color,
+ // $shadow=drop shadow should be added around the text.
+ function SetBox($aFrameColor=array(255,255,255),$aBorderColor=array(0,0,0),$aShadowColor=false,$aCornerRadius=4,$aShadowWidth=3) {
+ if( $aFrameColor==false )
+ $this->boxed=false;
+ else
+ $this->boxed=true;
+ $this->fcolor=$aFrameColor;
+ $this->bcolor=$aBorderColor;
+ // For backwards compatibility when shadow was just true or false
+ if( $aShadowColor === true )
+ $aShadowColor = 'gray';
+ $this->shadow=$aShadowColor;
+ $this->icornerradius=$aCornerRadius;
+ $this->ishadowwidth=$aShadowWidth;
+ }
+
+ // Hide the text
+ function Hide($aHide=true) {
+ $this->hide=$aHide;
+ }
+
+ // This looks ugly since it's not a very orthogonal design
+ // but I added this "inverse" of Hide() to harmonize
+ // with some classes which I designed more recently (especially)
+ // jpgraph_gantt
+ function Show($aShow=true) {
+ $this->hide=!$aShow;
+ }
+
+ // Specify font
+ function SetFont($aFamily,$aStyle=FS_NORMAL,$aSize=10) {
+ $this->font_family=$aFamily;
+ $this->font_style=$aStyle;
+ $this->font_size=$aSize;
+ }
+
+ // Center the text between $left and $right coordinates
+ function Center($aLeft,$aRight,$aYAbsPos=false) {
+ $this->x = $aLeft + ($aRight-$aLeft )/2;
+ $this->halign = "center";
+ if( is_numeric($aYAbsPos) )
+ $this->y = $aYAbsPos;
+ }
+
+ // Set text color
+ function SetColor($aColor) {
+ $this->color = $aColor;
+ }
+
+ function SetAngle($aAngle) {
+ $this->SetOrientation($aAngle);
+ }
+
+ // Orientation of text. Note only TTF fonts can have an arbitrary angle
+ function SetOrientation($aDirection=0) {
+ if( is_numeric($aDirection) )
+ $this->dir=$aDirection;
+ elseif( $aDirection=="h" )
+ $this->dir = 0;
+ elseif( $aDirection=="v" )
+ $this->dir = 90;
+ else JpGraphError::Raise(" Invalid direction specified for text.");
+ }
+
+ // Total width of text
+ function GetWidth($aImg) {
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $w = $aImg->GetTextWidth($this->t,$this->dir);
+ return $w;
+ }
+
+ // Hight of font
+ function GetFontHeight($aImg) {
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $h = $aImg->GetFontHeight();
+ return $h;
+
+ }
+
+ function GetTextHeight($aImg) {
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $h = $aImg->GetTextHeight($this->t,$this->dir);
+ return $h;
+ }
+
+ // Set the margin which will be interpretated differently depending
+ // on the context.
+ function SetMargin($aMarg) {
+ $this->margin = $aMarg;
+ }
+
+ function StrokeWithScale($aImg,$axscale,$ayscale) {
+ if( $this->iScalePosX === null ||
+ $this->iScalePosY === null ) {
+ $this->Stroke($aImg);
+ }
+ else {
+ $this->Stroke($aImg,
+ round($axscale->Translate($this->iScalePosX)),
+ round($ayscale->Translate($this->iScalePosY)));
+ }
+ }
+
+ // Display text in image
+ function Stroke($aImg,$x=null,$y=null) {
+
+ if( !empty($x) ) $this->x = round($x);
+ if( !empty($y) ) $this->y = round($y);
+
+ // If position been given as a fraction of the image size
+ // calculate the absolute position
+ if( $this->x < 1 && $this->x > 0 ) $this->x *= $aImg->width;
+ if( $this->y < 1 && $this->y > 0 ) $this->y *= $aImg->height;
+
+ $aImg->PushColor($this->color);
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $aImg->SetTextAlign($this->halign,$this->valign);
+ if( $this->boxed ) {
+ if( $this->fcolor=="nofill" )
+ $this->fcolor=false;
+ $aImg->SetLineWeight(1);
+ $aImg->StrokeBoxedText($this->x,$this->y,$this->t,
+ $this->dir,$this->fcolor,$this->bcolor,$this->shadow,
+ $this->paragraph_align,6,2,$this->icornerradius,
+ $this->ishadowwidth);
+ }
+ else {
+ $aImg->StrokeText($this->x,$this->y,$this->t,$this->dir,
+ $this->paragraph_align);
+ }
+ $aImg->PopColor($this->color);
+ }
+} // Class
+
+class GraphTabTitle extends Text{
+ var $corner = 6 , $posx = 7, $posy = 4;
+ var $color='darkred',$fillcolor='lightyellow',$bordercolor='black';
+ var $align = 'left', $width=TABTITLE_WIDTHFIT;
+ function GraphTabTitle() {
+ $this->t = '';
+ $this->font_style = FS_BOLD;
+ $this->hide = true;
+ }
+
+ function SetColor($aTxtColor,$aFillColor='lightyellow',$aBorderColor='black') {
+ $this->color = $aTxtColor;
+ $this->fillcolor = $aFillColor;
+ $this->bordercolor = $aBorderColor;
+ }
+
+ function SetFillColor($aFillColor) {
+ $this->fillcolor = $aFillColor;
+ }
+
+ function SetTabAlign($aAlign) {
+ // Synonym for SetPos
+ $this->align = $aAlign;
+ }
+
+ function SetPos($aAlign) {
+ $this->align = $aAlign;
+ }
+
+ function SetWidth($aWidth) {
+ $this->width = $aWidth ;
+ }
+
+ function Set($t) {
+ $this->t = $t;
+ $this->hide = false;
+ }
+
+ function SetCorner($aD) {
+ $this->corner = $aD ;
+ }
+
+ function Stroke($aImg) {
+ if( $this->hide )
+ return;
+ $this->boxed = false;
+ $w = $this->GetWidth($aImg) + 2*$this->posx;
+ $h = $this->GetTextHeight($aImg) + 2*$this->posy;
+
+ $x = $aImg->left_margin;
+ $y = $aImg->top_margin;
+
+ if( $this->width === TABTITLE_WIDTHFIT ) {
+ if( $this->align == 'left' ) {
+ $p = array($x, $y,
+ $x, $y-$h+$this->corner,
+ $x + $this->corner,$y-$h,
+ $x + $w - $this->corner, $y-$h,
+ $x + $w, $y-$h+$this->corner,
+ $x + $w, $y);
+ }
+ elseif( $this->align == 'center' ) {
+ $x += round($aImg->plotwidth/2) - round($w/2);
+ $p = array($x, $y,
+ $x, $y-$h+$this->corner,
+ $x + $this->corner, $y-$h,
+ $x + $w - $this->corner, $y-$h,
+ $x + $w, $y-$h+$this->corner,
+ $x + $w, $y);
+ }
+ else {
+ $x += $aImg->plotwidth -$w;
+ $p = array($x, $y,
+ $x, $y-$h+$this->corner,
+ $x + $this->corner,$y-$h,
+ $x + $w - $this->corner, $y-$h,
+ $x + $w, $y-$h+$this->corner,
+ $x + $w, $y);
+ }
+ }
+ else {
+ if( $this->width === TABTITLE_WIDTHFULL )
+ $w = $aImg->plotwidth ;
+ else
+ $w = $this->width ;
+
+ // Make the tab fit the width of the plot area
+ $p = array($x, $y,
+ $x, $y-$h+$this->corner,
+ $x + $this->corner,$y-$h,
+ $x + $w - $this->corner, $y-$h,
+ $x + $w, $y-$h+$this->corner,
+ $x + $w, $y);
+
+ }
+ $aImg->SetTextAlign('left','bottom');
+ $x += $this->posx;
+ $y -= $this->posy;
+
+ $aImg->SetColor($this->fillcolor);
+ $aImg->FilledPolygon($p);
+
+ $aImg->SetColor($this->bordercolor);
+ $aImg->Polygon($p,true);
+
+ $aImg->SetColor($this->color);
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $aImg->StrokeText($x,$y,$this->t,0,'center');
+ }
+
+}
+
+//===================================================
+// CLASS SuperScriptText
+// Description: Format a superscript text
+//===================================================
+class SuperScriptText extends Text {
+ var $iSuper="";
+ var $sfont_family="",$sfont_style="",$sfont_size=8;
+ var $iSuperMargin=2,$iVertOverlap=4,$iSuperScale=0.65;
+ var $iSDir=0;
+ var $iSimple=false;
+
+ function SuperScriptText($aTxt="",$aSuper="",$aXAbsPos=0,$aYAbsPos=0) {
+ parent::Text($aTxt,$aXAbsPos,$aYAbsPos);
+ $this->iSuper = $aSuper;
+ }
+
+ function FromReal($aVal,$aPrecision=2) {
+ // Convert a floating point number to scientific notation
+ $neg=1.0;
+ if( $aVal < 0 ) {
+ $neg = -1.0;
+ $aVal = -$aVal;
+ }
+
+ $l = floor(log10($aVal));
+ $a = sprintf("%0.".$aPrecision."f",round($aVal / pow(10,$l),$aPrecision));
+ $a *= $neg;
+ if( $this->iSimple && ($a == 1 || $a==-1) ) $a = '';
+
+ if( $a != '' )
+ $this->t = $a.' * 10';
+ else {
+ if( $neg == 1 )
+ $this->t = '10';
+ else
+ $this->t = '-10';
+ }
+ $this->iSuper = $l;
+ }
+
+ function Set($aTxt,$aSuper="") {
+ $this->t = $aTxt;
+ $this->iSuper = $aSuper;
+ }
+
+ function SetSuperFont($aFontFam,$aFontStyle=FS_NORMAL,$aFontSize=8) {
+ $this->sfont_family = $aFontFam;
+ $this->sfont_style = $aFontStyle;
+ $this->sfont_size = $aFontSize;
+ }
+
+ // Total width of text
+ function GetWidth(&$aImg) {
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $w = $aImg->GetTextWidth($this->t);
+ $aImg->SetFont($this->sfont_family,$this->sfont_style,$this->sfont_size);
+ $w += $aImg->GetTextWidth($this->iSuper);
+ $w += $this->iSuperMargin;
+ return $w;
+ }
+
+ // Hight of font (approximate the height of the text)
+ function GetFontHeight(&$aImg) {
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $h = $aImg->GetFontHeight();
+ $aImg->SetFont($this->sfont_family,$this->sfont_style,$this->sfont_size);
+ $h += $aImg->GetFontHeight();
+ return $h;
+ }
+
+ // Hight of text
+ function GetTextHeight(&$aImg) {
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $h = $aImg->GetTextHeight($this->t);
+ $aImg->SetFont($this->sfont_family,$this->sfont_style,$this->sfont_size);
+ $h += $aImg->GetTextHeight($this->iSuper);
+ return $h;
+ }
+
+ function Stroke($aImg,$ax=-1,$ay=-1) {
+
+ // To position the super script correctly we need different
+ // cases to handle the alignmewnt specified since that will
+ // determine how we can interpret the x,y coordinates
+
+ $w = parent::GetWidth($aImg);
+ $h = parent::GetTextHeight($aImg);
+ switch( $this->valign ) {
+ case 'top':
+ $sy = $this->y;
+ break;
+ case 'center':
+ $sy = $this->y - $h/2;
+ break;
+ case 'bottom':
+ $sy = $this->y - $h;
+ break;
+ default:
+ JpGraphError::Raise('PANIC: Internal error in SuperScript::Stroke(). Unknown vertical alignment for text');
+ exit();
+ }
+
+ switch( $this->halign ) {
+ case 'left':
+ $sx = $this->x + $w;
+ break;
+ case 'center':
+ $sx = $this->x + $w/2;
+ break;
+ case 'right':
+ $sx = $this->x;
+ break;
+ default:
+ JpGraphError::Raise('PANIC: Internal error in SuperScript::Stroke(). Unknown horizontal alignment for text');
+ exit();
+ }
+
+ $sx += $this->iSuperMargin;
+ $sy += $this->iVertOverlap;
+
+ // Should we automatically determine the font or
+ // has the user specified it explicetly?
+ if( $this->sfont_family == "" ) {
+ if( $this->font_family <= FF_FONT2 ) {
+ if( $this->font_family == FF_FONT0 ) {
+ $sff = FF_FONT0;
+ }
+ elseif( $this->font_family == FF_FONT1 ) {
+ if( $this->font_style == FS_NORMAL )
+ $sff = FF_FONT0;
+ else
+ $sff = FF_FONT1;
+ }
+ else {
+ $sff = FF_FONT1;
+ }
+ $sfs = $this->font_style;
+ $sfz = $this->font_size;
+ }
+ else {
+ // TTF fonts
+ $sff = $this->font_family;
+ $sfs = $this->font_style;
+ $sfz = floor($this->font_size*$this->iSuperScale);
+ if( $sfz < 8 ) $sfz = 8;
+ }
+ $this->sfont_family = $sff;
+ $this->sfont_style = $sfs;
+ $this->sfont_size = $sfz;
+ }
+ else {
+ $sff = $this->sfont_family;
+ $sfs = $this->sfont_style;
+ $sfz = $this->sfont_size;
+ }
+
+ parent::Stroke($aImg,$ax,$ay);
+
+
+ // For the builtin fonts we need to reduce the margins
+ // since the bounding bx reported for the builtin fonts
+ // are much larger than for the TTF fonts.
+ if( $sff <= FF_FONT2 ) {
+ $sx -= 2;
+ $sy += 3;
+ }
+
+ $aImg->SetTextAlign('left','bottom');
+ $aImg->SetFont($sff,$sfs,$sfz);
+ $aImg->PushColor($this->color);
+ $aImg->StrokeText($sx,$sy,$this->iSuper,$this->iSDir,'left');
+ $aImg->PopColor();
+ }
+}
+
+
+//===================================================
+// CLASS Grid
+// Description: responsible for drawing grid lines in graph
+//===================================================
+class Grid {
+ var $img;
+ var $scale;
+ var $grid_color='#DDDDDD',$grid_mincolor='#DDDDDD';
+ var $type="solid";
+ var $show=false, $showMinor=false,$weight=1;
+ var $fill=false,$fillcolor=array('#EFEFEF','#BBCCFF');
+//---------------
+// CONSTRUCTOR
+ function Grid(&$aAxis) {
+ $this->scale = &$aAxis->scale;
+ $this->img = &$aAxis->img;
+ }
+//---------------
+// PUBLIC METHODS
+ function SetColor($aMajColor,$aMinColor=false) {
+ $this->grid_color=$aMajColor;
+ if( $aMinColor === false )
+ $aMinColor = $aMajColor ;
+ $this->grid_mincolor = $aMinColor;
+ }
+
+ function SetWeight($aWeight) {
+ $this->weight=$aWeight;
+ }
+
+ // Specify if grid should be dashed, dotted or solid
+ function SetLineStyle($aType) {
+ $this->type = $aType;
+ }
+
+ // Decide if both major and minor grid should be displayed
+ function Show($aShowMajor=true,$aShowMinor=false) {
+ $this->show=$aShowMajor;
+ $this->showMinor=$aShowMinor;
+ }
+
+ function SetFill($aFlg=true,$aColor1='lightgray',$aColor2='lightblue') {
+ $this->fill = $aFlg;
+ $this->fillcolor = array( $aColor1, $aColor2 );
+ }
+
+ // Display the grid
+ function Stroke() {
+ if( $this->showMinor ) {
+ $tmp = $this->grid_color;
+ $this->grid_color = $this->grid_mincolor;
+ $this->DoStroke($this->scale->ticks->ticks_pos);
+
+ $this->grid_color = $tmp;
+ $this->DoStroke($this->scale->ticks->maj_ticks_pos);
+ }
+ else {
+ $this->DoStroke($this->scale->ticks->maj_ticks_pos);
+ }
+ }
+
+//--------------
+// Private methods
+ // Draw the grid
+ function DoStroke(&$aTicksPos) {
+ if( !$this->show )
+ return;
+ $nbrgrids = count($aTicksPos);
+
+ if( $this->scale->type=="y" ) {
+ $xl=$this->img->left_margin;
+ $xr=$this->img->width-$this->img->right_margin;
+
+ if( $this->fill ) {
+ // Draw filled areas
+ $y2 = $aTicksPos[0];
+ $i=1;
+ while( $i < $nbrgrids ) {
+ $y1 = $y2;
+ $y2 = $aTicksPos[$i++];
+ $this->img->SetColor($this->fillcolor[$i & 1]);
+ $this->img->FilledRectangle($xl,$y1,$xr,$y2);
+ }
+ }
+
+ $this->img->SetColor($this->grid_color);
+ $this->img->SetLineWeight($this->weight);
+
+ // Draw grid lines
+ for($i=0; $i<$nbrgrids; ++$i) {
+ $y=$aTicksPos[$i];
+ if( $this->type == "solid" )
+ $this->img->Line($xl,$y,$xr,$y);
+ elseif( $this->type == "dotted" )
+ $this->img->DashedLine($xl,$y,$xr,$y,1,6);
+ elseif( $this->type == "dashed" )
+ $this->img->DashedLine($xl,$y,$xr,$y,2,4);
+ elseif( $this->type == "longdashed" )
+ $this->img->DashedLine($xl,$y,$xr,$y,8,6);
+ }
+ }
+ elseif( $this->scale->type=="x" ) {
+ $yu=$this->img->top_margin;
+ $yl=$this->img->height-$this->img->bottom_margin;
+ $limit=$this->img->width-$this->img->right_margin;
+
+ if( $this->fill ) {
+ // Draw filled areas
+ $x2 = $aTicksPos[0];
+ $i=1;
+ while( $i < $nbrgrids ) {
+ $x1 = $x2;
+ $x2 = min($aTicksPos[$i++],$limit) ;
+ $this->img->SetColor($this->fillcolor[$i & 1]);
+ $this->img->FilledRectangle($x1,$yu,$x2,$yl);
+ }
+ }
+
+ $this->img->SetColor($this->grid_color);
+ $this->img->SetLineWeight($this->weight);
+
+ // We must also test for limit since we might have
+ // an offset and the number of ticks is calculated with
+ // assumption offset==0 so we might end up drawing one
+ // to many gridlines
+ $i=0;
+ $x=$aTicksPos[$i];
+ while( $i<count($aTicksPos) && ($x=$aTicksPos[$i]) <= $limit ) {
+ if( $this->type == "solid" )
+ $this->img->Line($x,$yl,$x,$yu);
+ elseif( $this->type == "dotted" )
+ $this->img->DashedLine($x,$yl,$x,$yu,1,6);
+ elseif( $this->type == "dashed" )
+ $this->img->DashedLine($x,$yl,$x,$yu,2,4);
+ elseif( $this->type == "longdashed" )
+ $this->img->DashedLine($x,$yl,$x,$yu,8,6);
+ ++$i;
+ }
+ }
+ else {
+ JpGraphError::Raise('Internal error: Unknown grid axis ['.$this->scale->type.']');
+ }
+ return true;
+ }
+} // Class
+
+//===================================================
+// CLASS Axis
+// Description: Defines X and Y axis. Notes that at the
+// moment the code is not really good since the axis on
+// several occasion must know wheter it's an X or Y axis.
+// This was a design decision to make the code easier to
+// follow.
+//===================================================
+class Axis {
+ var $pos = false;
+ var $weight=1;
+ var $color=array(0,0,0),$label_color=array(0,0,0);
+ var $img=null,$scale=null;
+ var $hide=false;
+ var $ticks_label=false, $ticks_label_colors=null;
+ var $show_first_label=true,$show_last_label=true;
+ var $label_step=1; // Used by a text axis to specify what multiple of major steps
+ // should be labeled.
+ var $tick_step=1;
+ var $labelPos=0; // Which side of the axis should the labels be?
+ var $title=null,$title_adjust,$title_margin,$title_side=SIDE_LEFT;
+ var $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12,$label_angle=0;
+ var $tick_label_margin=5;
+ var $label_halign = '',$label_valign = '', $label_para_align='left';
+ var $hide_line=false,$hide_labels=false;
+ //var $hide_zero_label=false;
+
+//---------------
+// CONSTRUCTOR
+ function Axis(&$img,&$aScale,$color=array(0,0,0)) {
+ $this->img = &$img;
+ $this->scale = &$aScale;
+ $this->color = $color;
+ $this->title=new Text("");
+
+ if( $aScale->type=="y" ) {
+ $this->title_margin = 25;
+ $this->title_adjust="middle";
+ $this->title->SetOrientation(90);
+ $this->tick_label_margin=7;
+ $this->labelPos=SIDE_LEFT;
+ //$this->SetLabelFormat('%.1f');
+ }
+ else {
+ $this->title_margin = 5;
+ $this->title_adjust="high";
+ $this->title->SetOrientation(0);
+ $this->tick_label_margin=3;
+ $this->labelPos=SIDE_DOWN;
+ //$this->SetLabelFormat('%.0f');
+ }
+ }
+//---------------
+// PUBLIC METHODS
+
+ function SetLabelFormat($aFormStr) {
+ $this->scale->ticks->SetLabelFormat($aFormStr);
+ }
+
+ function SetLabelFormatString($aFormStr) {
+ $this->scale->ticks->SetLabelFormat($aFormStr);
+ }
+
+ function SetLabelFormatCallback($aFuncName) {
+ $this->scale->ticks->SetFormatCallback($aFuncName);
+ }
+
+ function SetLabelAlign($aHAlign,$aVAlign="top",$aParagraphAlign='left') {
+ $this->label_halign = $aHAlign;
+ $this->label_valign = $aVAlign;
+ $this->label_para_align = $aParagraphAlign;
+ }
+
+ // Don't display the first label
+ function HideFirstTickLabel($aShow=false) {
+ $this->show_first_label=$aShow;
+ }
+
+ function HideLastTickLabel($aShow=false) {
+ $this->show_last_label=$aShow;
+ }
+
+ function HideTicks($aHideMinor=true,$aHideMajor=true) {
+ $this->scale->ticks->SupressMinorTickMarks($aHideMinor);
+ $this->scale->ticks->SupressTickMarks($aHideMajor);
+ }
+
+ // Hide zero label
+ function HideZeroLabel($aFlag=true) {
+ $this->scale->ticks->SupressZeroLabel();
+ //$this->hide_zero_label = $aFlag;
+ }
+
+ function HideFirstLastLabel() {
+ // The two first calls to ticks method will supress
+ // automatically generated scale values. However, that
+ // will not affect manually specified value, e.g text-scales.
+ // therefor we also make a kludge here to supress manually
+ // specified scale labels.
+ $this->scale->ticks->SupressLast();
+ $this->scale->ticks->SupressFirst();
+ $this->show_first_label = false;
+ $this->show_last_label = false;
+ }
+
+ // Hide the axis
+ function Hide($aHide=true) {
+ $this->hide=$aHide;
+ }
+
+ // Hide the actual axis-line, but still print the labels
+ function HideLine($aHide=true) {
+ $this->hide_line = $aHide;
+ }
+
+ function HideLabels($aHide=true) {
+ $this->hide_labels = $aHide;
+ }
+
+
+ // Weight of axis
+ function SetWeight($aWeight) {
+ $this->weight = $aWeight;
+ }
+
+ // Axis color
+ function SetColor($aColor,$aLabelColor=false) {
+ $this->color = $aColor;
+ if( !$aLabelColor ) $this->label_color = $aColor;
+ else $this->label_color = $aLabelColor;
+ }
+
+ // Title on axis
+ function SetTitle($aTitle,$aAdjustAlign="high") {
+ $this->title->Set($aTitle);
+ $this->title_adjust=$aAdjustAlign;
+ }
+
+ // Specify distance from the axis
+ function SetTitleMargin($aMargin) {
+ $this->title_margin=$aMargin;
+ }
+
+ // Which side of the axis should the axis title be?
+ function SetTitleSide($aSideOfAxis) {
+ $this->title_side = $aSideOfAxis;
+ }
+
+ // Utility function to set the direction for tick marks
+ function SetTickDirection($aDir) {
+ // Will be deprecated from 1.7
+ if( ERR_DEPRECATED )
+ JpGraphError::Raise('Axis::SetTickDirection() is deprecated. Use Axis::SetTickSide() instead');
+ $this->scale->ticks->SetSide($aDir);
+ }
+
+ function SetTickSide($aDir) {
+ $this->scale->ticks->SetSide($aDir);
+ }
+
+ // Specify text labels for the ticks. One label for each data point
+ function SetTickLabels($aLabelArray,$aLabelColorArray=null) {
+ $this->ticks_label = $aLabelArray;
+ $this->ticks_label_colors = $aLabelColorArray;
+ }
+
+ // How far from the axis should the labels be drawn
+ function SetTickLabelMargin($aMargin) {
+ if( ERR_DEPRECATED )
+ JpGraphError::Raise('SetTickLabelMargin() is deprecated. Use Axis::SetLabelMargin() instead.');
+ $this->tick_label_margin=$aMargin;
+ }
+
+ function SetLabelMargin($aMargin) {
+ $this->tick_label_margin=$aMargin;
+ }
+
+ // Specify that every $step of the ticks should be displayed starting
+ // at $start
+ // DEPRECATED FUNCTION: USE SetTextTickInterval() INSTEAD
+ function SetTextTicks($step,$start=0) {
+ JpGraphError::Raise(" SetTextTicks() is deprecated. Use SetTextTickInterval() instead.");
+ }
+
+ // Specify that every $step of the ticks should be displayed starting
+ // at $start
+ function SetTextTickInterval($aStep,$aStart=0) {
+ $this->scale->ticks->SetTextLabelStart($aStart);
+ $this->tick_step=$aStep;
+ }
+
+ // Specify that every $step tick mark should have a label
+ // should be displayed starting
+ function SetTextLabelInterval($aStep) {
+ if( $aStep < 1 )
+ JpGraphError::Raise(" Text label interval must be specified >= 1.");
+ $this->label_step=$aStep;
+ }
+
+ // Which side of the axis should the labels be on?
+ function SetLabelPos($aSidePos) {
+ // This will be deprecated from 1.7
+ if( ERR_DEPRECATED )
+ JpGraphError::Raise('SetLabelPos() is deprecated. Use Axis::SetLabelSide() instead.');
+ $this->labelPos=$aSidePos;
+ }
+
+ function SetLabelSide($aSidePos) {
+ $this->labelPos=$aSidePos;
+ }
+
+ // Set the font
+ function SetFont($aFamily,$aStyle=FS_NORMAL,$aSize=10) {
+ $this->font_family = $aFamily;
+ $this->font_style = $aStyle;
+ $this->font_size = $aSize;
+ }
+
+ // Position for axis line on the "other" scale
+ function SetPos($aPosOnOtherScale) {
+ $this->pos=$aPosOnOtherScale;
+ }
+
+ // Specify the angle for the tick labels
+ function SetLabelAngle($aAngle) {
+ $this->label_angle = $aAngle;
+ }
+
+ // Stroke the axis.
+ function Stroke($aOtherAxisScale) {
+ if( $this->hide ) return;
+ if( is_numeric($this->pos) ) {
+ $pos=$aOtherAxisScale->Translate($this->pos);
+ }
+ else { // Default to minimum of other scale if pos not set
+ if( ($aOtherAxisScale->GetMinVal() >= 0 && $this->pos==false) || $this->pos=="min" ) {
+ $pos = $aOtherAxisScale->scale_abs[0];
+ }
+ elseif($this->pos == "max") {
+ $pos = $aOtherAxisScale->scale_abs[1];
+ }
+ else { // If negative set x-axis at 0
+ $this->pos=0;
+ $pos=$aOtherAxisScale->Translate(0);
+ }
+ }
+ $this->img->SetLineWeight($this->weight);
+ $this->img->SetColor($this->color);
+ $this->img->SetFont($this->font_family,$this->font_style,$this->font_size);
+ if( $this->scale->type == "x" ) {
+ if( !$this->hide_line )
+ $this->img->FilledRectangle($this->img->left_margin,$pos,
+ $this->img->width-$this->img->right_margin,$pos+$this->weight-1);
+ $y=$pos+$this->img->GetFontHeight()+$this->title_margin+$this->title->margin;
+ if( $this->title_adjust=="high" )
+ $this->title->Pos($this->img->width-$this->img->right_margin,$y,"right","top");
+ elseif( $this->title_adjust=="middle" || $this->title_adjust=="center" )
+ $this->title->Pos(($this->img->width-$this->img->left_margin-$this->img->right_margin)/2+$this->img->left_margin,$y,"center","top");
+ elseif($this->title_adjust=="low")
+ $this->title->Pos($this->img->left_margin,$y,"left","top");
+ else {
+ JpGraphError::Raise('Unknown alignment specified for X-axis title. ('.$this->title_adjust.')');
+ }
+ }
+ elseif( $this->scale->type == "y" ) {
+ // Add line weight to the height of the axis since
+ // the x-axis could have a width>1 and we want the axis to fit nicely together.
+ if( !$this->hide_line )
+ $this->img->FilledRectangle($pos-$this->weight+1,$this->img->top_margin,
+ $pos,$this->img->height-$this->img->bottom_margin+$this->weight-1);
+ $x=$pos ;
+ if( $this->title_side == SIDE_LEFT ) {
+ $x -= $this->title_margin;
+ $x -= $this->title->margin;
+ $halign="right";
+ }
+ else {
+ $x += $this->title_margin;
+ $x += $this->title->margin;
+ $halign="left";
+ }
+ // If the user has manually specified an hor. align
+ // then we override the automatic settings with this
+ // specifed setting. Since default is 'left' we compare
+ // with that. (This means a manually set 'left' align
+ // will have no effect.)
+ if( $this->title->halign != 'left' )
+ $halign = $this->title->halign;
+ if( $this->title_adjust=="high" )
+ $this->title->Pos($x,$this->img->top_margin,$halign,"top");
+ elseif($this->title_adjust=="middle" || $this->title_adjust=="center")
+ $this->title->Pos($x,($this->img->height-$this->img->top_margin-$this->img->bottom_margin)/2+$this->img->top_margin,$halign,"center");
+ elseif($this->title_adjust=="low")
+ $this->title->Pos($x,$this->img->height-$this->img->bottom_margin,$halign,"bottom");
+ else
+ JpGraphError::Raise('Unknown alignment specified for Y-axis title. ('.$this->title_adjust.')');
+
+ }
+ $this->scale->ticks->Stroke($this->img,$this->scale,$pos);
+ if( !$this->hide_labels ) {
+ $this->StrokeLabels($pos);
+ }
+ $this->title->Stroke($this->img);
+ }
+
+//---------------
+// PRIVATE METHODS
+ // Draw all the tick labels on major tick marks
+ function StrokeLabels($aPos,$aMinor=false,$aAbsLabel=false) {
+
+ $this->img->SetColor($this->label_color);
+ $this->img->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $yoff=$this->img->GetFontHeight()/2;
+
+ // Only draw labels at major tick marks
+ $nbr = count($this->scale->ticks->maj_ticks_label);
+
+ // We have the option to not-display the very first mark
+ // (Usefull when the first label might interfere with another
+ // axis.)
+ $i = $this->show_first_label ? 0 : 1 ;
+ if( !$this->show_last_label ) --$nbr;
+ // Now run through all labels making sure we don't overshoot the end
+ // of the scale.
+ $ncolor=0;
+ if( isset($this->ticks_label_colors) )
+ $ncolor=count($this->ticks_label_colors);
+
+ while( $i<$nbr ) {
+ // $tpos holds the absolute text position for the label
+ $tpos=$this->scale->ticks->maj_ticklabels_pos[$i];
+
+ // Note. the $limit is only used for the x axis since we
+ // might otherwise overshoot if the scale has been centered
+ // This is due to us "loosing" the last tick mark if we center.
+ if( $this->scale->type=="x" && $tpos > $this->img->width-$this->img->right_margin+1 ) {
+ return;
+ }
+ // we only draw every $label_step label
+ if( ($i % $this->label_step)==0 ) {
+
+ // Set specific label color if specified
+ if( $ncolor > 0 )
+ $this->img->SetColor($this->ticks_label_colors[$i % $ncolor]);
+
+ // If the label has been specified use that and in other case
+ // just label the mark with the actual scale value
+ $m=$this->scale->ticks->GetMajor();
+
+ // ticks_label has an entry for each data point and is the array
+ // that holds the labels set by the user. If the user hasn't
+ // specified any values we use whats in the automatically asigned
+ // labels in the maj_ticks_label
+ if( isset($this->ticks_label[$i*$m]) )
+ $label=$this->ticks_label[$i*$m];
+ else {
+ if( $aAbsLabel )
+ $label=abs($this->scale->ticks->maj_ticks_label[$i]);
+ else
+ $label=$this->scale->ticks->maj_ticks_label[$i];
+ if( $this->scale->textscale && $this->scale->ticks->label_formfunc == '' ) {
+ ++$label;
+ }
+ }
+
+ //if( $this->hide_zero_label && $label==0.0 ) {
+ // ++$i;
+ // continue;
+ //}
+
+ if( $this->scale->type == "x" ) {
+ if( $this->labelPos == SIDE_DOWN ) {
+ if( $this->label_angle==0 || $this->label_angle==90 ) {
+ if( $this->label_halign=='' && $this->label_valign=='')
+ $this->img->SetTextAlign('center','top');
+ else
+ $this->img->SetTextAlign($this->label_halign,$this->label_valign);
+
+ }
+ else {
+ if( $this->label_halign=='' && $this->label_valign=='')
+ $this->img->SetTextAlign("right","top");
+ else
+ $this->img->SetTextAlign($this->label_halign,$this->label_valign);
+ }
+
+ $this->img->StrokeText($tpos,$aPos+$this->tick_label_margin,$label,
+ $this->label_angle,$this->label_para_align);
+ }
+ else {
+ if( $this->label_angle==0 || $this->label_angle==90 ) {
+ if( $this->label_halign=='' && $this->label_valign=='')
+ $this->img->SetTextAlign("center","bottom");
+ else
+ $this->img->SetTextAlign($this->label_halign,$this->label_valign);
+ }
+ else {
+ if( $this->label_halign=='' && $this->label_valign=='')
+ $this->img->SetTextAlign("right","bottom");
+ else
+ $this->img->SetTextAlign($this->label_halign,$this->label_valign);
+ }
+ $this->img->StrokeText($tpos,$aPos-$this->tick_label_margin,$label,
+ $this->label_angle,$this->label_para_align);
+ }
+ }
+ else {
+ // scale->type == "y"
+ //if( $this->label_angle!=0 )
+ //JpGraphError::Raise(" Labels at an angle are not supported on Y-axis");
+ if( $this->labelPos == SIDE_LEFT ) { // To the left of y-axis
+ if( $this->label_halign=='' && $this->label_valign=='')
+ $this->img->SetTextAlign("right","center");
+ else
+ $this->img->SetTextAlign($this->label_halign,$this->label_valign);
+ $this->img->StrokeText($aPos-$this->tick_label_margin,$tpos,$label,$this->label_angle,$this->label_para_align);
+ }
+ else { // To the right of the y-axis
+ if( $this->label_halign=='' && $this->label_valign=='')
+ $this->img->SetTextAlign("left","center");
+ else
+ $this->img->SetTextAlign($this->label_halign,$this->label_valign);
+ $this->img->StrokeText($aPos+$this->tick_label_margin,$tpos,$label,$this->label_angle,$this->label_para_align);
+ }
+ }
+ }
+ ++$i;
+ }
+ }
+
+} // Class
+
+//===================================================
+// CLASS Ticks
+// Description: Abstract base class for drawing linear and logarithmic
+// tick marks on axis
+//===================================================
+class Ticks {
+ var $minor_abs_size=3, $major_abs_size=5;
+ var $direction=1; // Should ticks be in(=1) the plot area or outside (=-1)?
+ var $scale;
+ var $is_set=false;
+ var $precision;
+ var $supress_zerolabel=false,$supress_first=false;
+ var $supress_last=false,$supress_tickmarks=false,$supress_minor_tickmarks=false;
+ var $mincolor="",$majcolor="";
+ var $weight=1;
+ var $label_formatstr=''; // C-style format string to use for labels
+ var $label_formfunc='';
+
+
+//---------------
+// CONSTRUCTOR
+ function Ticks(&$aScale) {
+ $this->scale=&$aScale;
+ $this->precision = -1;
+ }
+
+//---------------
+// PUBLIC METHODS
+ // Set format string for automatic labels
+ function SetLabelFormat($aFormatString) {
+ $this->label_formatstr=$aFormatString;
+ }
+
+ function SetFormatCallback($aCallbackFuncName) {
+ $this->label_formfunc = $aCallbackFuncName;
+ }
+
+ // Don't display the first zero label
+ function SupressZeroLabel($aFlag=true) {
+ $this->supress_zerolabel=$aFlag;
+ }
+
+ // Don't display minor tick marks
+ function SupressMinorTickMarks($aHide=true) {
+ $this->supress_minor_tickmarks=$aHide;
+ }
+
+ // Don't display major tick marks
+ function SupressTickMarks($aHide=true) {
+ $this->supress_tickmarks=$aHide;
+ }
+
+ // Hide the first tick mark
+ function SupressFirst($aHide=true) {
+ $this->supress_first=$aHide;
+ }
+
+ // Hide the last tick mark
+ function SupressLast($aHide=true) {
+ $this->supress_last=$aHide;
+ }
+
+ // Size (in pixels) of minor tick marks
+ function GetMinTickAbsSize() {
+ return $this->minor_abs_size;
+ }
+
+ // Size (in pixels) of major tick marks
+ function GetMajTickAbsSize() {
+ return $this->major_abs_size;
+ }
+
+ function SetSize($aMajSize,$aMinSize=3) {
+ $this->major_abs_size = $aMajSize;
+ $this->minor_abs_size = $aMinSize;
+ }
+
+ // Have the ticks been specified
+ function IsSpecified() {
+ return $this->is_set;
+ }
+
+ // Set the distance between major and minor tick marks
+ function Set($aMaj,$aMin) {
+ // "Virtual method"
+ // Should be implemented by the concrete subclass
+ // if any action is wanted.
+ }
+
+ // Specify number of decimals in automatic labels
+ // Deprecated from 1.4. Use SetFormatString() instead
+ function SetPrecision($aPrecision) {
+ if( ERR_DEPRECATED )
+ JpGraphError::Raise('Ticks::SetPrecision() is deprecated. Use Ticks::SetLabelFormat() (or Ticks::SetFormatCallback()) instead');
+ $this->precision=$aPrecision;
+ }
+
+ function SetSide($aSide) {
+ $this->direction=$aSide;
+ }
+
+ // Which side of the axis should the ticks be on
+ function SetDirection($aSide=SIDE_RIGHT) {
+ $this->direction=$aSide;
+ }
+
+ // Set colors for major and minor tick marks
+ function SetMarkColor($aMajorColor,$aMinorColor="") {
+ $this->SetColor($aMajorColor,$aMinorColor);
+ }
+
+ function SetColor($aMajorColor,$aMinorColor="") {
+ $this->majcolor=$aMajorColor;
+
+ // If not specified use same as major
+ if( $aMinorColor=="" )
+ $this->mincolor=$aMajorColor;
+ else
+ $this->mincolor=$aMinorColor;
+ }
+
+ function SetWeight($aWeight) {
+ $this->weight=$aWeight;
+ }
+
+} // Class
+
+//===================================================
+// CLASS LinearTicks
+// Description: Draw linear ticks on axis
+//===================================================
+class LinearTicks extends Ticks {
+ var $minor_step=1, $major_step=2;
+ var $xlabel_offset=0,$xtick_offset=0;
+ var $label_offset=0; // What offset should the displayed label have
+ // i.e should we display 0,1,2 or 1,2,3,4 or 2,3,4 etc
+ var $text_label_start=0;
+//---------------
+// CONSTRUCTOR
+ function LinearTicks() {
+ $this->precision = -1;
+ }
+
+//---------------
+// PUBLIC METHODS
+
+
+ // Return major step size in world coordinates
+ function GetMajor() {
+ return $this->major_step;
+ }
+
+ // Return minor step size in world coordinates
+ function GetMinor() {
+ return $this->minor_step;
+ }
+
+ // Set Minor and Major ticks (in world coordinates)
+ function Set($aMajStep,$aMinStep=false) {
+ if( $aMinStep==false )
+ $aMinStep=$aMajStep;
+
+ if( $aMajStep <= 0 || $aMinStep <= 0 ) {
+ JpGraphError::Raise(" Minor or major step size is 0. Check that you haven't
+ got an accidental SetTextTicks(0) in your code.<p>
+ If this is not the case you might have stumbled upon a bug in JpGraph.
+ Please report this and if possible include the data that caused the
+ problem.");
+ }
+
+ $this->major_step=$aMajStep;
+ $this->minor_step=$aMinStep;
+ $this->is_set = true;
+ }
+
+ // Draw linear ticks
+ function Stroke(&$img,&$scale,$pos) {
+ $maj_step_abs = $scale->scale_factor*$this->major_step;
+ $min_step_abs = $scale->scale_factor*$this->minor_step;
+
+ if( $min_step_abs==0 || $maj_step_abs==0 )
+ JpGraphError::Raise(" A plot has an illegal scale. This could for example be that you are trying to use text autoscaling to draw a line plot with only one point or that the plot area is too small. Try increasing the graph size or correct the lineplot.");
+ $limit = $scale->scale_abs[1];
+ $nbrmajticks=floor(1.000001*(($scale->GetMaxVal()-$scale->GetMinVal())/$this->major_step))+1;
+ $first=0;
+
+ // If precision hasn't been specified set it to a sensible value
+ if( $this->precision==-1 ) {
+ $t = log10($this->minor_step);
+ if( $t > 0 )
+ $precision = 0;
+ else
+ $precision = -floor($t);
+ }
+ else
+ $precision = $this->precision;
+
+ $img->SetLineWeight($this->weight);
+
+ // Handle ticks on X-axis
+ if( $scale->type == "x" ) {
+
+ // Draw the minor tick marks
+
+ $yu = $pos - $this->direction*$this->GetMinTickAbsSize();
+ $label = $scale->GetMinVal();
+ $x=$scale->scale_abs[0];
+ $i=0;
+ $j=0;
+ $step = round($maj_step_abs/$min_step_abs);
+ while( $x < $limit ) {
+ $this->ticks_pos[]=$x;
+ $this->ticks_label[]=$label;
+ $label+=$this->minor_step;
+ if( !$this->supress_tickmarks && !$this->supress_minor_tickmarks) {
+ if( $this->mincolor!="" ) $img->PushColor($this->mincolor);
+ $img->Line($x,$pos,$x,$yu);
+ if( $this->mincolor!="" ) $img->PopColor();
+ }
+
+ if( $i % $step == 0 ) {
+ $this->maj_ticks_pos[$j]=round($x);//$xtick;
+ ++$j;
+ }
+
+ ++$i;
+ $x += $min_step_abs;
+
+ }
+ $this->maj_ticks_pos[$j]=$x;
+
+ // Draw the major tick marks
+
+ $yu = $pos - $this->direction*$this->GetMajTickAbsSize();
+
+ // TODO: Add logic to set label_offset for text labels
+ $label = (float)$scale->GetMinVal()+$this->text_label_start+$this->label_offset;
+
+ $start_abs=$scale->scale_factor*$this->text_label_start;
+
+ $nbrmajticks=ceil(($scale->GetMaxVal()-$scale->GetMinVal()-$this->text_label_start )/$this->major_step)+1;
+
+
+ $x = $scale->scale_abs[0]+$start_abs+$this->xlabel_offset*$min_step_abs;
+ for( $i=0; $label <= $scale->GetMaxVal()+$this->label_offset; ++$i ) {
+
+ // Apply format
+ if( $this->label_formfunc != "" ) {
+ $f=$this->label_formfunc;
+ $l = call_user_func($f,$label);
+ }
+ elseif( $this->label_formatstr != '' )
+ $l = sprintf($this->label_formatstr,$label);
+ else {
+ $v = round($label,$precision);
+ $l = sprintf("%01.".$precision."f",$v);
+ }
+
+ if( ($this->supress_zerolabel && $l==0) ||
+ ($this->supress_first && $i==0) ||
+ ($this->supress_last && $i==$nbrmajticks-1) ) {
+ $l="";
+ }
+
+ $this->maj_ticks_label[$i]=$l;
+ $label+=$this->major_step;
+ $this->maj_ticklabels_pos[$i] = $x;
+// $this->maj_ticklabels_pos[$i] = $this->maj_ticks_pos[$i];
+
+ // The x-position of the tick marks can be different from the labels.
+ // Note that we record the tick position (not the label) so that the grid
+ // happen upon tick marks and not labels.
+ $xtick=$scale->scale_abs[0]+$start_abs+$this->xtick_offset*$min_step_abs+$i*$maj_step_abs;
+ $this->maj_ticks_pos[$i]=$xtick;
+ if(!($this->xtick_offset > 0 && $i==$nbrmajticks-1) &&
+ !$this->supress_tickmarks) {
+ if( $this->majcolor!="" ) $img->PushColor($this->majcolor);
+ $img->Line($this->maj_ticks_pos[$i],$pos,$this->maj_ticks_pos[$i],$yu);
+ if( $this->majcolor!="" ) $img->PopColor();
+ }
+
+ $x += $maj_step_abs;
+ }
+
+
+ }
+ elseif( $scale->type == "y" ) {
+
+ // Draw the major tick marks
+ $xr = $pos + $this->direction*$this->GetMajTickAbsSize();
+ $label = $scale->GetMinVal();
+
+ $tmpmaj=array();
+ $tmpmin=array();
+
+ for( $i=0; $i<$nbrmajticks; ++$i) {
+ $y=$scale->scale_abs[0]+$i*$maj_step_abs;
+
+ $tmpmaj[]=$y;
+
+
+ // THe following two lines might seem to be unecessary but they are not!
+ // The reason being that for X-axis we separate the position of the labels
+ // and the tick marks which we don't do for the Y-axis.
+ // We therefore need to make sure both arrays are corcectly filled
+ // since Axis::StrokeLabels() uses the label positions and Grid::Stroke() uses
+ // the tick positions.
+ $this->maj_ticklabels_pos[$i]=$y;
+ $this->maj_ticks_pos[$i]=$y;
+
+ if( $this->label_formfunc != "" ) {
+ $f=$this->label_formfunc;
+ $l = call_user_func($f,$label);
+ }
+ elseif( $this->label_formatstr != "" )
+ $l = sprintf($this->label_formatstr,$label);
+ else
+ $l = sprintf("%01.".$precision."f",round($label,$precision));
+
+ if( ($this->supress_zerolabel && ($l + 0)==0) || ($this->supress_first && $i==0) ||
+ ($this->supress_last && $i==$nbrmajticks-1) ) {
+ $l="";
+ }
+
+ $this->maj_ticks_label[$i]=$l;
+ $label+=$this->major_step;
+ if( !$this->supress_tickmarks ) {
+ if( $this->majcolor!="" ) $img->PushColor($this->majcolor);
+ $img->Line($pos,$y,$xr,$y);
+ if( $this->majcolor!="" ) $img->PopColor();
+ }
+ }
+
+ // Draw the minor tick marks
+ $xr = $pos + $this->direction*$this->GetMinTickAbsSize();
+ $label = $scale->GetMinVal();
+ for( $i=0,$y=$scale->scale_abs[0]; $y>=$limit; ) {
+
+ $tmpmin[]=$y;
+
+ $this->ticks_pos[$i]=$y;
+ $this->ticks_label[$i]=$label;
+ $label+=$this->minor_step;
+ if( !$this->supress_tickmarks && !$this->supress_minor_tickmarks) {
+ if( $this->mincolor!="" ) $img->PushColor($this->mincolor);
+ $img->Line($pos,$y,$xr,$y);
+ if( $this->mincolor!="" ) $img->PopColor();
+ }
+ ++$i;
+ $y=$scale->scale_abs[0]+$i*$min_step_abs;
+ }
+ }
+ }
+//---------------
+// PRIVATE METHODS
+ // Spoecify the offset of the displayed tick mark with the tick "space"
+ // Legal values for $o is [0,1] used to adjust where the tick marks and label
+ // should be positioned within the major tick-size
+ // $lo specifies the label offset and $to specifies the tick offset
+ // this comes in handy for example in bar graphs where we wont no offset for the
+ // tick but have the labels displayed halfway under the bars.
+ function SetXLabelOffset($aLabelOff,$aTickOff=-1) {
+ $this->xlabel_offset=$aLabelOff;
+ if( $aTickOff==-1 ) // Same as label offset
+ $this->xtick_offset=$aLabelOff;
+ else
+ $this->xtick_offset=$aTickOff;
+ if( $aLabelOff>0 )
+ $this->SupressLast(); // The last tick wont fit
+ }
+
+ // Which tick label should we start with?
+ function SetTextLabelStart($aTextLabelOff) {
+ $this->text_label_start=$aTextLabelOff;
+ }
+
+} // Class
+
+//===================================================
+// CLASS LinearScale
+// Description: Handle linear scaling between screen and world
+//===================================================
+class LinearScale {
+ var $scale=array(0,0);
+ var $scale_abs=array(0,0);
+ var $scale_factor; // Scale factor between world and screen
+ var $world_size; // Plot area size in world coordinates
+ var $world_abs_size; // Plot area size in pixels
+ var $off; // Offset between image edge and plot area
+ var $type; // is this x or y scale ?
+ var $ticks=null; // Store ticks
+ var $autoscale_min=false; // Forced minimum value, auto determine max
+ var $autoscale_max=false; // Forced maximum value, auto determine min
+ var $gracetop=0,$gracebottom=0;
+ var $intscale=false; // Restrict autoscale to integers
+ var $textscale=false; // Just a flag to let the Plot class find out if
+ // we are a textscale or not. This is a cludge since
+ // this ionformatyion is availabale in Graph::axtype but
+ // we don't have access to the graph object in the Plots
+ // stroke method. So we let graph store the status here
+ // when the linear scale is created. A real cludge...
+ var $text_scale_off = 0;
+ var $auto_ticks=false; // When using manual scale should the ticks be automatically set?
+ var $name = 'lin';
+//---------------
+// CONSTRUCTOR
+ function LinearScale($aMin=0,$aMax=0,$aType="y") {
+ assert($aType=="x" || $aType=="y" );
+ assert($aMin<=$aMax);
+
+ $this->type=$aType;
+ $this->scale=array($aMin,$aMax);
+ $this->world_size=$aMax-$aMin;
+ $this->ticks = new LinearTicks();
+ }
+
+//---------------
+// PUBLIC METHODS
+ // Second phase constructor
+ function Init(&$aImg) {
+ $this->InitConstants($aImg);
+ // We want image to notify us when the margins changes so we
+ // can recalculate the constants.
+ // PHP <= 4.04 BUGWARNING: IT IS IMPOSSIBLE TO DO THIS IN THE CONSTRUCTOR
+ // SINCE (FOR SOME REASON) IT IS IMPOSSIBLE TO PASS A REFERENCE
+ // TO 'this' INSTEAD IT WILL ADD AN ANONYMOUS COPY OF THIS OBJECT WHICH WILL
+ // GET ALL THE NOTIFICATIONS. (This took a while to track down...)
+
+ // Add us as an observer to class Image
+ $aImg->AddObserver("InitConstants",$this);
+ }
+
+ // Check if scale is set or if we should autoscale
+ // We should do this is either scale or ticks has not been set
+ function IsSpecified() {
+ if( $this->GetMinVal()==$this->GetMaxVal() ) { // Scale not set
+ return false;
+ }
+ return true;
+ }
+
+ // Set the minimum data value when the autoscaling is used.
+ // Usefull if you want a fix minimum (like 0) but have an
+ // automatic maximum
+ function SetAutoMin($aMin) {
+ $this->autoscale_min=$aMin;
+ }
+
+ // Set the minimum data value when the autoscaling is used.
+ // Usefull if you want a fix minimum (like 0) but have an
+ // automatic maximum
+ function SetAutoMax($aMax) {
+ $this->autoscale_max=$aMax;
+ }
+
+ // If the user manually specifies a scale should the ticks
+ // still be set automatically?
+ function SetAutoTicks($aFlag=true) {
+ $this->auto_ticks = $aFlag;
+ }
+
+ // Specify scale "grace" value (top and bottom)
+ function SetGrace($aGraceTop,$aGraceBottom=0) {
+ if( $aGraceTop<0 || $aGraceBottom < 0 )
+ JpGraphError::Raise(" Grace must be larger then 0");
+ $this->gracetop=$aGraceTop;
+ $this->gracebottom=$aGraceBottom;
+ }
+
+ // Get the minimum value in the scale
+ function GetMinVal() {
+ return $this->scale[0];
+ }
+
+ // get maximum value for scale
+ function GetMaxVal() {
+ return $this->scale[1];
+ }
+
+ // Specify a new min/max value for sclae
+ function Update(&$aImg,$aMin,$aMax) {
+ $this->scale=array($aMin,$aMax);
+ $this->world_size=$aMax-$aMin;
+ $this->InitConstants($aImg);
+ }
+
+ // Translate between world and screen
+ function Translate($aCoord) {
+ return $this->off+($aCoord - $this->GetMinVal()) * $this->scale_factor;
+ }
+
+ // Relative translate (don't include offset) usefull when we just want
+ // to know the relative position (in pixels) on the axis
+ function RelTranslate($aCoord) {
+ return ($aCoord - $this->GetMinVal()) * $this->scale_factor;
+ }
+
+ // Restrict autoscaling to only use integers
+ function SetIntScale($aIntScale=true) {
+ $this->intscale=$aIntScale;
+ }
+
+ // Calculate an integer autoscale
+ function IntAutoScale(&$img,$min,$max,$maxsteps,$majend=true) {
+ // Make sure limits are integers
+ $min=floor($min);
+ $max=ceil($max);
+ if( abs($min-$max)==0 ) {
+ --$min; ++$max;
+ }
+ $maxsteps = floor($maxsteps);
+
+ $gracetop=round(($this->gracetop/100.0)*abs($max-$min));
+ $gracebottom=round(($this->gracebottom/100.0)*abs($max-$min));
+ if( is_numeric($this->autoscale_min) ) {
+ $min = ceil($this->autoscale_min);
+ if( $min >= $max ) {
+ JpGraphError::Raise('You have specified a min value with SetAutoMin() which is larger than the maximum value used for the scale. This is not possible.');
+ die();
+ }
+ }
+
+ if( is_numeric($this->autoscale_max) ) {
+ $max = ceil($this->autoscale_max);
+ if( $min >= $max ) {
+ JpGraphError::Raise('You have specified a max value with SetAutoMax() which is smaller than the miminum value used for the scale. This is not possible.');
+ die();
+ }
+ }
+
+ if( abs($min-$max ) == 0 ) {
+ ++$max;
+ --$min;
+ }
+
+ $min -= $gracebottom;
+ $max += $gracetop;
+
+ // First get tickmarks as multiples of 1, 10, ...
+ if( $majend ) {
+ list($num1steps,$adj1min,$adj1max,$maj1step) =
+ $this->IntCalcTicks($maxsteps,$min,$max,1);
+ }
+ else {
+ $adj1min = $min;
+ $adj1max = $max;
+ list($num1steps,$maj1step) =
+ $this->IntCalcTicksFreeze($maxsteps,$min,$max,1);
+ }
+
+ if( abs($min-$max) > 2 ) {
+ // Then get tick marks as 2:s 2, 20, ...
+ if( $majend ) {
+ list($num2steps,$adj2min,$adj2max,$maj2step) =
+ $this->IntCalcTicks($maxsteps,$min,$max,5);
+ }
+ else {
+ $adj2min = $min;
+ $adj2max = $max;
+ list($num2steps,$maj2step) =
+ $this->IntCalcTicksFreeze($maxsteps,$min,$max,5);
+ }
+ }
+ else {
+ $num2steps = 10000; // Dummy high value so we don't choose this
+ }
+
+ if( abs($min-$max) > 5 ) {
+ // Then get tickmarks as 5:s 5, 50, 500, ...
+ if( $majend ) {
+ list($num5steps,$adj5min,$adj5max,$maj5step) =
+ $this->IntCalcTicks($maxsteps,$min,$max,2);
+ }
+ else {
+ $adj5min = $min;
+ $adj5max = $max;
+ list($num5steps,$maj5step) =
+ $this->IntCalcTicksFreeze($maxsteps,$min,$max,2);
+ }
+ }
+ else {
+ $num5steps = 10000; // Dummy high value so we don't choose this
+ }
+
+ // Check to see whichof 1:s, 2:s or 5:s fit better with
+ // the requested number of major ticks
+ $match1=abs($num1steps-$maxsteps);
+ $match2=abs($num2steps-$maxsteps);
+ if( !empty($maj5step) && $maj5step > 1 )
+ $match5=abs($num5steps-$maxsteps);
+ else
+ $match5=10000; // Dummy high value
+
+ // Compare these three values and see which is the closest match
+ // We use a 0.6 weight to gravitate towards multiple of 5:s
+ if( $match1 < $match2 ) {
+ if( $match1 < $match5 )
+ $r=1;
+ else
+ $r=3;
+ }
+ else {
+ if( $match2 < $match5 )
+ $r=2;
+ else
+ $r=3;
+ }
+ // Minsteps are always the same as maxsteps for integer scale
+ switch( $r ) {
+ case 1:
+ $this->Update($img,$adj1min,$adj1max);
+ $this->ticks->Set($maj1step,$maj1step);
+ break;
+ case 2:
+ $this->Update($img,$adj2min,$adj2max);
+ $this->ticks->Set($maj2step,$maj2step);
+ break;
+ case 3:
+ $this->Update($img,$adj5min,$adj5max);
+ $this->ticks->Set($maj5step,$maj2step);
+ break;
+ }
+ }
+
+
+ // Calculate autoscale. Used if user hasn't given a scale and ticks
+ // $maxsteps is the maximum number of major tickmarks allowed.
+ function AutoScale(&$img,$min,$max,$maxsteps,$majend=true) {
+ if( $this->intscale ) {
+ $this->IntAutoScale($img,$min,$max,$maxsteps,$majend);
+ return;
+ }
+ if( abs($min-$max) < 0.00001 ) {
+ // We need some difference to be able to autoscale
+ // make it 5% above and 5% below value
+ if( $min==0 && $max==0 ) { // Special case
+ $min=-1; $max=1;
+ }
+ else {
+ $delta = (abs($max)+abs($min))*0.005;
+ $min -= $delta;
+ $max += $delta;
+ }
+ }
+
+ $gracetop=($this->gracetop/100.0)*abs($max-$min);
+ $gracebottom=($this->gracebottom/100.0)*abs($max-$min);
+ if( is_numeric($this->autoscale_min) ) {
+ $min = $this->autoscale_min;
+ if( $min >= $max ) {
+ JpGraphError::Raise('You have specified a min value with SetAutoMin() which is larger than the maximum value used for the scale. This is not possible.');
+ die();
+ }
+ if( abs($min-$max ) < 0.00001 )
+ $max *= 1.2;
+ }
+
+ if( is_numeric($this->autoscale_max) ) {
+ $max = $this->autoscale_max;
+ if( $min >= $max ) {
+ JpGraphError::Raise('You have specified a max value with SetAutoMax() which is smaller than the miminum value used for the scale. This is not possible.');
+ die();
+ }
+ if( abs($min-$max ) < 0.00001 )
+ $min *= 0.8;
+ }
+
+
+ $min -= $gracebottom;
+ $max += $gracetop;
+
+ // First get tickmarks as multiples of 0.1, 1, 10, ...
+ if( $majend ) {
+ list($num1steps,$adj1min,$adj1max,$min1step,$maj1step) =
+ $this->CalcTicks($maxsteps,$min,$max,1,2);
+ }
+ else {
+ $adj1min=$min;
+ $adj1max=$max;
+ list($num1steps,$min1step,$maj1step) =
+ $this->CalcTicksFreeze($maxsteps,$min,$max,1,2,false);
+ }
+
+ // Then get tick marks as 2:s 0.2, 2, 20, ...
+ if( $majend ) {
+ list($num2steps,$adj2min,$adj2max,$min2step,$maj2step) =
+ $this->CalcTicks($maxsteps,$min,$max,5,2);
+ }
+ else {
+ $adj2min=$min;
+ $adj2max=$max;
+ list($num2steps,$min2step,$maj2step) =
+ $this->CalcTicksFreeze($maxsteps,$min,$max,5,2,false);
+ }
+
+ // Then get tickmarks as 5:s 0.05, 0.5, 5, 50, ...
+ if( $majend ) {
+ list($num5steps,$adj5min,$adj5max,$min5step,$maj5step) =
+ $this->CalcTicks($maxsteps,$min,$max,2,5);
+ }
+ else {
+ $adj5min=$min;
+ $adj5max=$max;
+ list($num5steps,$min5step,$maj5step) =
+ $this->CalcTicksFreeze($maxsteps,$min,$max,2,5,false);
+ }
+
+ // Check to see whichof 1:s, 2:s or 5:s fit better with
+ // the requested number of major ticks
+ $match1=abs($num1steps-$maxsteps);
+ $match2=abs($num2steps-$maxsteps);
+ $match5=abs($num5steps-$maxsteps);
+ // Compare these three values and see which is the closest match
+ // We use a 0.8 weight to gravitate towards multiple of 5:s
+ $r=$this->MatchMin3($match1,$match2,$match5,0.8);
+ switch( $r ) {
+ case 1:
+ $this->Update($img,$adj1min,$adj1max);
+ $this->ticks->Set($maj1step,$min1step);
+ break;
+ case 2:
+ $this->Update($img,$adj2min,$adj2max);
+ $this->ticks->Set($maj2step,$min2step);
+ break;
+ case 3:
+ $this->Update($img,$adj5min,$adj5max);
+ $this->ticks->Set($maj5step,$min5step);
+ break;
+ }
+ }
+
+//---------------
+// PRIVATE METHODS
+
+ // This method recalculates all constants that are depending on the
+ // margins in the image. If the margins in the image are changed
+ // this method should be called for every scale that is registred with
+ // that image. Should really be installed as an observer of that image.
+ function InitConstants(&$img) {
+ if( $this->type=="x" ) {
+ $this->world_abs_size=$img->width - $img->left_margin - $img->right_margin;
+ $this->off=$img->left_margin;
+ $this->scale_factor = 0;
+ if( $this->world_size > 0 )
+ $this->scale_factor=$this->world_abs_size/($this->world_size*1.0);
+ }
+ else { // y scale
+ $this->world_abs_size=$img->height - $img->top_margin - $img->bottom_margin;
+ $this->off=$img->top_margin+$this->world_abs_size;
+ $this->scale_factor = 0;
+ if( $this->world_size > 0 )
+ $this->scale_factor=-$this->world_abs_size/($this->world_size*1.0);
+ }
+ $size = $this->world_size * $this->scale_factor;
+ $this->scale_abs=array($this->off,$this->off + $size);
+ }
+
+ // Initialize the conversion constants for this scale
+ // This tries to pre-calculate as much as possible to speed up the
+ // actual conversion (with Translate()) later on
+ // $start =scale start in absolute pixels (for x-scale this is an y-position
+ // and for an y-scale this is an x-position
+ // $len =absolute length in pixels of scale
+ function SetConstants($aStart,$aLen) {
+ $this->world_abs_size=$aLen;
+ $this->off=$aStart;
+
+ if( $this->world_size<=0 ) {
+ JpGraphError::Raise("<b>JpGraph Fatal Error</b>:<br>
+ You have unfortunately stumbled upon a bug in JpGraph. <br>
+ It seems like the scale range is ".$this->world_size." [for ".
+ $this->type." scale] <br>
+ Please report Bug #01 to jpgraph@aditus.nu and include the script
+ that gave this error. <br>
+ This problem could potentially be caused by trying to use \"illegal\"
+ values in the input data arrays (like trying to send in strings or
+ only NULL values) which causes the autoscaling to fail.");
+ }
+
+ // scale_factor = number of pixels per world unit
+ $this->scale_factor=$this->world_abs_size/($this->world_size*1.0);
+
+ // scale_abs = start and end points of scale in absolute pixels
+ $this->scale_abs=array($this->off,$this->off+$this->world_size*$this->scale_factor);
+ }
+
+
+ // Calculate number of ticks steps with a specific division
+ // $a is the divisor of 10**x to generate the first maj tick intervall
+ // $a=1, $b=2 give major ticks with multiple of 10, ...,0.1,1,10,...
+ // $a=5, $b=2 give major ticks with multiple of 2:s ...,0.2,2,20,...
+ // $a=2, $b=5 give major ticks with multiple of 5:s ...,0.5,5,50,...
+ // We return a vector of
+ // [$numsteps,$adjmin,$adjmax,$minstep,$majstep]
+ // If $majend==true then the first and last marks on the axis will be major
+ // labeled tick marks otherwise it will be adjusted to the closest min tick mark
+ function CalcTicks($maxsteps,$min,$max,$a,$b,$majend=true) {
+ $diff=$max-$min;
+ if( $diff==0 )
+ $ld=0;
+ else
+ $ld=floor(log10($diff));
+
+ // Gravitate min towards zero if we are close
+ if( $min>0 && $min < pow(10,$ld) ) $min=0;
+
+ //$majstep=pow(10,$ld-1)/$a;
+ $majstep=pow(10,$ld)/$a;
+ $minstep=$majstep/$b;
+
+ $adjmax=ceil($max/$minstep)*$minstep;
+ $adjmin=floor($min/$minstep)*$minstep;
+ $adjdiff = $adjmax-$adjmin;
+ $numsteps=$adjdiff/$majstep;
+
+ while( $numsteps>$maxsteps ) {
+ $majstep=pow(10,$ld)/$a;
+ $numsteps=$adjdiff/$majstep;
+ ++$ld;
+ }
+
+ $minstep=$majstep/$b;
+ $adjmin=floor($min/$minstep)*$minstep;
+ $adjdiff = $adjmax-$adjmin;
+ if( $majend ) {
+ $adjmin = floor($min/$majstep)*$majstep;
+ $adjdiff = $adjmax-$adjmin;
+ $adjmax = ceil($adjdiff/$majstep)*$majstep+$adjmin;
+ }
+ else
+ $adjmax=ceil($max/$minstep)*$minstep;
+
+ return array($numsteps,$adjmin,$adjmax,$minstep,$majstep);
+ }
+
+ function CalcTicksFreeze($maxsteps,$min,$max,$a,$b) {
+ // Same as CalcTicks but don't adjust min/max values
+ $diff=$max-$min;
+ if( $diff==0 )
+ $ld=0;
+ else
+ $ld=floor(log10($diff));
+
+ //$majstep=pow(10,$ld-1)/$a;
+ $majstep=pow(10,$ld)/$a;
+ $minstep=$majstep/$b;
+ $numsteps=floor($diff/$majstep);
+
+ while( $numsteps > $maxsteps ) {
+ $majstep=pow(10,$ld)/$a;
+ $numsteps=floor($diff/$majstep);
+ ++$ld;
+ }
+ $minstep=$majstep/$b;
+ return array($numsteps,$minstep,$majstep);
+ }
+
+
+ function IntCalcTicks($maxsteps,$min,$max,$a,$majend=true) {
+ $diff=$max-$min;
+ if( $diff==0 )
+ JpGraphError::Raise('Can\'t automatically determine ticks since min==max.');
+ else
+ $ld=floor(log10($diff));
+
+ // Gravitate min towards zero if we are close
+ if( $min>0 && $min < pow(10,$ld) ) $min=0;
+
+ if( $ld == 0 ) $ld=1;
+
+ if( $a == 1 )
+ $majstep = 1;
+ else
+ $majstep=pow(10,$ld)/$a;
+ $adjmax=ceil($max/$majstep)*$majstep;
+
+ $adjmin=floor($min/$majstep)*$majstep;
+ $adjdiff = $adjmax-$adjmin;
+ $numsteps=$adjdiff/$majstep;
+ while( $numsteps>$maxsteps ) {
+ $majstep=pow(10,$ld)/$a;
+ $numsteps=$adjdiff/$majstep;
+ ++$ld;
+ }
+
+ $adjmin=floor($min/$majstep)*$majstep;
+ $adjdiff = $adjmax-$adjmin;
+ if( $majend ) {
+ $adjmin = floor($min/$majstep)*$majstep;
+ $adjdiff = $adjmax-$adjmin;
+ $adjmax = ceil($adjdiff/$majstep)*$majstep+$adjmin;
+ }
+ else
+ $adjmax=ceil($max/$majstep)*$majstep;
+
+ return array($numsteps,$adjmin,$adjmax,$majstep);
+ }
+
+
+ function IntCalcTicksFreeze($maxsteps,$min,$max,$a) {
+ // Same as IntCalcTick but don't change min/max values
+ $diff=$max-$min;
+ if( $diff==0 )
+ JpGraphError::Raise('Can\'t automatically determine ticks since min==max.');
+ else
+ $ld=floor(log10($diff));
+
+ if( $ld == 0 ) $ld=1;
+
+ if( $a == 1 )
+ $majstep = 1;
+ else
+ $majstep=pow(10,$ld)/$a;
+
+ $numsteps=floor($diff/$majstep);
+ while( $numsteps > $maxsteps ) {
+ $majstep=pow(10,$ld)/$a;
+ $numsteps=floor($diff/$majstep);
+ ++$ld;
+ }
+
+ return array($numsteps,$majstep);
+ }
+
+
+
+ // Determine the minimum of three values witha weight for last value
+ function MatchMin3($a,$b,$c,$weight) {
+ if( $a < $b ) {
+ if( $a < ($c*$weight) )
+ return 1; // $a smallest
+ else
+ return 3; // $c smallest
+ }
+ elseif( $b < ($c*$weight) )
+ return 2; // $b smallest
+ return 3; // $c smallest
+ }
+} // Class
+
+//===================================================
+// CLASS RGB
+// Description: Color definitions as RGB triples
+//===================================================
+class RGB {
+ var $rgb_table;
+ var $img;
+ function RGB($aImg=null) {
+ $this->img = $aImg;
+
+ // Conversion array between color names and RGB
+ $this->rgb_table = array(
+ "aqua"=> array(0,255,255),
+ "lime"=> array(0,255,0),
+ "teal"=> array(0,128,128),
+ "whitesmoke"=>array(245,245,245),
+ "gainsboro"=>array(220,220,220),
+ "oldlace"=>array(253,245,230),
+ "linen"=>array(250,240,230),
+ "antiquewhite"=>array(250,235,215),
+ "papayawhip"=>array(255,239,213),
+ "blanchedalmond"=>array(255,235,205),
+ "bisque"=>array(255,228,196),
+ "peachpuff"=>array(255,218,185),
+ "navajowhite"=>array(255,222,173),
+ "moccasin"=>array(255,228,181),
+ "cornsilk"=>array(255,248,220),
+ "ivory"=>array(255,255,240),
+ "lemonchiffon"=>array(255,250,205),
+ "seashell"=>array(255,245,238),
+ "mintcream"=>array(245,255,250),
+ "azure"=>array(240,255,255),
+ "aliceblue"=>array(240,248,255),
+ "lavender"=>array(230,230,250),
+ "lavenderblush"=>array(255,240,245),
+ "mistyrose"=>array(255,228,225),
+ "white"=>array(255,255,255),
+ "black"=>array(0,0,0),
+ "darkslategray"=>array(47,79,79),
+ "dimgray"=>array(105,105,105),
+ "slategray"=>array(112,128,144),
+ "lightslategray"=>array(119,136,153),
+ "gray"=>array(190,190,190),
+ "lightgray"=>array(211,211,211),
+ "midnightblue"=>array(25,25,112),
+ "navy"=>array(0,0,128),
+ "cornflowerblue"=>array(100,149,237),
+ "darkslateblue"=>array(72,61,139),
+ "slateblue"=>array(106,90,205),
+ "mediumslateblue"=>array(123,104,238),
+ "lightslateblue"=>array(132,112,255),
+ "mediumblue"=>array(0,0,205),
+ "royalblue"=>array(65,105,225),
+ "blue"=>array(0,0,255),
+ "dodgerblue"=>array(30,144,255),
+ "deepskyblue"=>array(0,191,255),
+ "skyblue"=>array(135,206,235),
+ "lightskyblue"=>array(135,206,250),
+ "steelblue"=>array(70,130,180),
+ "lightred"=>array(211,167,168),
+ "lightsteelblue"=>array(176,196,222),
+ "lightblue"=>array(173,216,230),
+ "powderblue"=>array(176,224,230),
+ "paleturquoise"=>array(175,238,238),
+ "darkturquoise"=>array(0,206,209),
+ "mediumturquoise"=>array(72,209,204),
+ "turquoise"=>array(64,224,208),
+ "cyan"=>array(0,255,255),
+ "lightcyan"=>array(224,255,255),
+ "cadetblue"=>array(95,158,160),
+ "mediumaquamarine"=>array(102,205,170),
+ "aquamarine"=>array(127,255,212),
+ "darkgreen"=>array(0,100,0),
+ "darkolivegreen"=>array(85,107,47),
+ "darkseagreen"=>array(143,188,143),
+ "seagreen"=>array(46,139,87),
+ "mediumseagreen"=>array(60,179,113),
+ "lightseagreen"=>array(32,178,170),
+ "palegreen"=>array(152,251,152),
+ "springgreen"=>array(0,255,127),
+ "lawngreen"=>array(124,252,0),
+ "green"=>array(0,255,0),
+ "chartreuse"=>array(127,255,0),
+ "mediumspringgreen"=>array(0,250,154),
+ "greenyellow"=>array(173,255,47),
+ "limegreen"=>array(50,205,50),
+ "yellowgreen"=>array(154,205,50),
+ "forestgreen"=>array(34,139,34),
+ "olivedrab"=>array(107,142,35),
+ "darkkhaki"=>array(189,183,107),
+ "khaki"=>array(240,230,140),
+ "palegoldenrod"=>array(238,232,170),
+ "lightgoldenrodyellow"=>array(250,250,210),
+ "lightyellow"=>array(255,255,200),
+ "yellow"=>array(255,255,0),
+ "gold"=>array(255,215,0),
+ "lightgoldenrod"=>array(238,221,130),
+ "goldenrod"=>array(218,165,32),
+ "darkgoldenrod"=>array(184,134,11),
+ "rosybrown"=>array(188,143,143),
+ "indianred"=>array(205,92,92),
+ "saddlebrown"=>array(139,69,19),
+ "sienna"=>array(160,82,45),
+ "peru"=>array(205,133,63),
+ "burlywood"=>array(222,184,135),
+ "beige"=>array(245,245,220),
+ "wheat"=>array(245,222,179),
+ "sandybrown"=>array(244,164,96),
+ "tan"=>array(210,180,140),
+ "chocolate"=>array(210,105,30),
+ "firebrick"=>array(178,34,34),
+ "brown"=>array(165,42,42),
+ "darksalmon"=>array(233,150,122),
+ "salmon"=>array(250,128,114),
+ "lightsalmon"=>array(255,160,122),
+ "orange"=>array(255,165,0),
+ "darkorange"=>array(255,140,0),
+ "coral"=>array(255,127,80),
+ "lightcoral"=>array(240,128,128),
+ "tomato"=>array(255,99,71),
+ "orangered"=>array(255,69,0),
+ "red"=>array(255,0,0),
+ "hotpink"=>array(255,105,180),
+ "deeppink"=>array(255,20,147),
+ "pink"=>array(255,192,203),
+ "lightpink"=>array(255,182,193),
+ "palevioletred"=>array(219,112,147),
+ "maroon"=>array(176,48,96),
+ "mediumvioletred"=>array(199,21,133),
+ "violetred"=>array(208,32,144),
+ "magenta"=>array(255,0,255),
+ "violet"=>array(238,130,238),
+ "plum"=>array(221,160,221),
+ "orchid"=>array(218,112,214),
+ "mediumorchid"=>array(186,85,211),
+ "darkorchid"=>array(153,50,204),
+ "darkviolet"=>array(148,0,211),
+ "blueviolet"=>array(138,43,226),
+ "purple"=>array(160,32,240),
+ "mediumpurple"=>array(147,112,219),
+ "thistle"=>array(216,191,216),
+ "snow1"=>array(255,250,250),
+ "snow2"=>array(238,233,233),
+ "snow3"=>array(205,201,201),
+ "snow4"=>array(139,137,137),
+ "seashell1"=>array(255,245,238),
+ "seashell2"=>array(238,229,222),
+ "seashell3"=>array(205,197,191),
+ "seashell4"=>array(139,134,130),
+ "AntiqueWhite1"=>array(255,239,219),
+ "AntiqueWhite2"=>array(238,223,204),
+ "AntiqueWhite3"=>array(205,192,176),
+ "AntiqueWhite4"=>array(139,131,120),
+ "bisque1"=>array(255,228,196),
+ "bisque2"=>array(238,213,183),
+ "bisque3"=>array(205,183,158),
+ "bisque4"=>array(139,125,107),
+ "peachPuff1"=>array(255,218,185),
+ "peachpuff2"=>array(238,203,173),
+ "peachpuff3"=>array(205,175,149),
+ "peachpuff4"=>array(139,119,101),
+ "navajowhite1"=>array(255,222,173),
+ "navajowhite2"=>array(238,207,161),
+ "navajowhite3"=>array(205,179,139),
+ "navajowhite4"=>array(139,121,94),
+ "lemonchiffon1"=>array(255,250,205),
+ "lemonchiffon2"=>array(238,233,191),
+ "lemonchiffon3"=>array(205,201,165),
+ "lemonchiffon4"=>array(139,137,112),
+ "ivory1"=>array(255,255,240),
+ "ivory2"=>array(238,238,224),
+ "ivory3"=>array(205,205,193),
+ "ivory4"=>array(139,139,131),
+ "honeydew"=>array(193,205,193),
+ "lavenderblush1"=>array(255,240,245),
+ "lavenderblush2"=>array(238,224,229),
+ "lavenderblush3"=>array(205,193,197),
+ "lavenderblush4"=>array(139,131,134),
+ "mistyrose1"=>array(255,228,225),
+ "mistyrose2"=>array(238,213,210),
+ "mistyrose3"=>array(205,183,181),
+ "mistyrose4"=>array(139,125,123),
+ "azure1"=>array(240,255,255),
+ "azure2"=>array(224,238,238),
+ "azure3"=>array(193,205,205),
+ "azure4"=>array(131,139,139),
+ "slateblue1"=>array(131,111,255),
+ "slateblue2"=>array(122,103,238),
+ "slateblue3"=>array(105,89,205),
+ "slateblue4"=>array(71,60,139),
+ "royalblue1"=>array(72,118,255),
+ "royalblue2"=>array(67,110,238),
+ "royalblue3"=>array(58,95,205),
+ "royalblue4"=>array(39,64,139),
+ "dodgerblue1"=>array(30,144,255),
+ "dodgerblue2"=>array(28,134,238),
+ "dodgerblue3"=>array(24,116,205),
+ "dodgerblue4"=>array(16,78,139),
+ "steelblue1"=>array(99,184,255),
+ "steelblue2"=>array(92,172,238),
+ "steelblue3"=>array(79,148,205),
+ "steelblue4"=>array(54,100,139),
+ "deepskyblue1"=>array(0,191,255),
+ "deepskyblue2"=>array(0,178,238),
+ "deepskyblue3"=>array(0,154,205),
+ "deepskyblue4"=>array(0,104,139),
+ "skyblue1"=>array(135,206,255),
+ "skyblue2"=>array(126,192,238),
+ "skyblue3"=>array(108,166,205),
+ "skyblue4"=>array(74,112,139),
+ "lightskyblue1"=>array(176,226,255),
+ "lightskyblue2"=>array(164,211,238),
+ "lightskyblue3"=>array(141,182,205),
+ "lightskyblue4"=>array(96,123,139),
+ "slategray1"=>array(198,226,255),
+ "slategray2"=>array(185,211,238),
+ "slategray3"=>array(159,182,205),
+ "slategray4"=>array(108,123,139),
+ "lightsteelblue1"=>array(202,225,255),
+ "lightsteelblue2"=>array(188,210,238),
+ "lightsteelblue3"=>array(162,181,205),
+ "lightsteelblue4"=>array(110,123,139),
+ "lightblue1"=>array(191,239,255),
+ "lightblue2"=>array(178,223,238),
+ "lightblue3"=>array(154,192,205),
+ "lightblue4"=>array(104,131,139),
+ "lightcyan1"=>array(224,255,255),
+ "lightcyan2"=>array(209,238,238),
+ "lightcyan3"=>array(180,205,205),
+ "lightcyan4"=>array(122,139,139),
+ "paleturquoise1"=>array(187,255,255),
+ "paleturquoise2"=>array(174,238,238),
+ "paleturquoise3"=>array(150,205,205),
+ "paleturquoise4"=>array(102,139,139),
+ "cadetblue1"=>array(152,245,255),
+ "cadetblue2"=>array(142,229,238),
+ "cadetblue3"=>array(122,197,205),
+ "cadetblue4"=>array(83,134,139),
+ "turquoise1"=>array(0,245,255),
+ "turquoise2"=>array(0,229,238),
+ "turquoise3"=>array(0,197,205),
+ "turquoise4"=>array(0,134,139),
+ "cyan1"=>array(0,255,255),
+ "cyan2"=>array(0,238,238),
+ "cyan3"=>array(0,205,205),
+ "cyan4"=>array(0,139,139),
+ "darkslategray1"=>array(151,255,255),
+ "darkslategray2"=>array(141,238,238),
+ "darkslategray3"=>array(121,205,205),
+ "darkslategray4"=>array(82,139,139),
+ "aquamarine1"=>array(127,255,212),
+ "aquamarine2"=>array(118,238,198),
+ "aquamarine3"=>array(102,205,170),
+ "aquamarine4"=>array(69,139,116),
+ "darkseagreen1"=>array(193,255,193),
+ "darkseagreen2"=>array(180,238,180),
+ "darkseagreen3"=>array(155,205,155),
+ "darkseagreen4"=>array(105,139,105),
+ "seagreen1"=>array(84,255,159),
+ "seagreen2"=>array(78,238,148),
+ "seagreen3"=>array(67,205,128),
+ "seagreen4"=>array(46,139,87),
+ "palegreen1"=>array(154,255,154),
+ "palegreen2"=>array(144,238,144),
+ "palegreen3"=>array(124,205,124),
+ "palegreen4"=>array(84,139,84),
+ "springgreen1"=>array(0,255,127),
+ "springgreen2"=>array(0,238,118),
+ "springgreen3"=>array(0,205,102),
+ "springgreen4"=>array(0,139,69),
+ "chartreuse1"=>array(127,255,0),
+ "chartreuse2"=>array(118,238,0),
+ "chartreuse3"=>array(102,205,0),
+ "chartreuse4"=>array(69,139,0),
+ "olivedrab1"=>array(192,255,62),
+ "olivedrab2"=>array(179,238,58),
+ "olivedrab3"=>array(154,205,50),
+ "olivedrab4"=>array(105,139,34),
+ "darkolivegreen1"=>array(202,255,112),
+ "darkolivegreen2"=>array(188,238,104),
+ "darkolivegreen3"=>array(162,205,90),
+ "darkolivegreen4"=>array(110,139,61),
+ "khaki1"=>array(255,246,143),
+ "khaki2"=>array(238,230,133),
+ "khaki3"=>array(205,198,115),
+ "khaki4"=>array(139,134,78),
+ "lightgoldenrod1"=>array(255,236,139),
+ "lightgoldenrod2"=>array(238,220,130),
+ "lightgoldenrod3"=>array(205,190,112),
+ "lightgoldenrod4"=>array(139,129,76),
+ "yellow1"=>array(255,255,0),
+ "yellow2"=>array(238,238,0),
+ "yellow3"=>array(205,205,0),
+ "yellow4"=>array(139,139,0),
+ "gold1"=>array(255,215,0),
+ "gold2"=>array(238,201,0),
+ "gold3"=>array(205,173,0),
+ "gold4"=>array(139,117,0),
+ "goldenrod1"=>array(255,193,37),
+ "goldenrod2"=>array(238,180,34),
+ "goldenrod3"=>array(205,155,29),
+ "goldenrod4"=>array(139,105,20),
+ "darkgoldenrod1"=>array(255,185,15),
+ "darkgoldenrod2"=>array(238,173,14),
+ "darkgoldenrod3"=>array(205,149,12),
+ "darkgoldenrod4"=>array(139,101,8),
+ "rosybrown1"=>array(255,193,193),
+ "rosybrown2"=>array(238,180,180),
+ "rosybrown3"=>array(205,155,155),
+ "rosybrown4"=>array(139,105,105),
+ "indianred1"=>array(255,106,106),
+ "indianred2"=>array(238,99,99),
+ "indianred3"=>array(205,85,85),
+ "indianred4"=>array(139,58,58),
+ "sienna1"=>array(255,130,71),
+ "sienna2"=>array(238,121,66),
+ "sienna3"=>array(205,104,57),
+ "sienna4"=>array(139,71,38),
+ "burlywood1"=>array(255,211,155),
+ "burlywood2"=>array(238,197,145),
+ "burlywood3"=>array(205,170,125),
+ "burlywood4"=>array(139,115,85),
+ "wheat1"=>array(255,231,186),
+ "wheat2"=>array(238,216,174),
+ "wheat3"=>array(205,186,150),
+ "wheat4"=>array(139,126,102),
+ "tan1"=>array(255,165,79),
+ "tan2"=>array(238,154,73),
+ "tan3"=>array(205,133,63),
+ "tan4"=>array(139,90,43),
+ "chocolate1"=>array(255,127,36),
+ "chocolate2"=>array(238,118,33),
+ "chocolate3"=>array(205,102,29),
+ "chocolate4"=>array(139,69,19),
+ "firebrick1"=>array(255,48,48),
+ "firebrick2"=>array(238,44,44),
+ "firebrick3"=>array(205,38,38),
+ "firebrick4"=>array(139,26,26),
+ "brown1"=>array(255,64,64),
+ "brown2"=>array(238,59,59),
+ "brown3"=>array(205,51,51),
+ "brown4"=>array(139,35,35),
+ "salmon1"=>array(255,140,105),
+ "salmon2"=>array(238,130,98),
+ "salmon3"=>array(205,112,84),
+ "salmon4"=>array(139,76,57),
+ "lightsalmon1"=>array(255,160,122),
+ "lightsalmon2"=>array(238,149,114),
+ "lightsalmon3"=>array(205,129,98),
+ "lightsalmon4"=>array(139,87,66),
+ "orange1"=>array(255,165,0),
+ "orange2"=>array(238,154,0),
+ "orange3"=>array(205,133,0),
+ "orange4"=>array(139,90,0),
+ "darkorange1"=>array(255,127,0),
+ "darkorange2"=>array(238,118,0),
+ "darkorange3"=>array(205,102,0),
+ "darkorange4"=>array(139,69,0),
+ "coral1"=>array(255,114,86),
+ "coral2"=>array(238,106,80),
+ "coral3"=>array(205,91,69),
+ "coral4"=>array(139,62,47),
+ "tomato1"=>array(255,99,71),
+ "tomato2"=>array(238,92,66),
+ "tomato3"=>array(205,79,57),
+ "tomato4"=>array(139,54,38),
+ "orangered1"=>array(255,69,0),
+ "orangered2"=>array(238,64,0),
+ "orangered3"=>array(205,55,0),
+ "orangered4"=>array(139,37,0),
+ "deeppink1"=>array(255,20,147),
+ "deeppink2"=>array(238,18,137),
+ "deeppink3"=>array(205,16,118),
+ "deeppink4"=>array(139,10,80),
+ "hotpink1"=>array(255,110,180),
+ "hotpink2"=>array(238,106,167),
+ "hotpink3"=>array(205,96,144),
+ "hotpink4"=>array(139,58,98),
+ "pink1"=>array(255,181,197),
+ "pink2"=>array(238,169,184),
+ "pink3"=>array(205,145,158),
+ "pink4"=>array(139,99,108),
+ "lightpink1"=>array(255,174,185),
+ "lightpink2"=>array(238,162,173),
+ "lightpink3"=>array(205,140,149),
+ "lightpink4"=>array(139,95,101),
+ "palevioletred1"=>array(255,130,171),
+ "palevioletred2"=>array(238,121,159),
+ "palevioletred3"=>array(205,104,137),
+ "palevioletred4"=>array(139,71,93),
+ "maroon1"=>array(255,52,179),
+ "maroon2"=>array(238,48,167),
+ "maroon3"=>array(205,41,144),
+ "maroon4"=>array(139,28,98),
+ "violetred1"=>array(255,62,150),
+ "violetred2"=>array(238,58,140),
+ "violetred3"=>array(205,50,120),
+ "violetred4"=>array(139,34,82),
+ "magenta1"=>array(255,0,255),
+ "magenta2"=>array(238,0,238),
+ "magenta3"=>array(205,0,205),
+ "magenta4"=>array(139,0,139),
+ "mediumred"=>array(140,34,34),
+ "orchid1"=>array(255,131,250),
+ "orchid2"=>array(238,122,233),
+ "orchid3"=>array(205,105,201),
+ "orchid4"=>array(139,71,137),
+ "plum1"=>array(255,187,255),
+ "plum2"=>array(238,174,238),
+ "plum3"=>array(205,150,205),
+ "plum4"=>array(139,102,139),
+ "mediumorchid1"=>array(224,102,255),
+ "mediumorchid2"=>array(209,95,238),
+ "mediumorchid3"=>array(180,82,205),
+ "mediumorchid4"=>array(122,55,139),
+ "darkorchid1"=>array(191,62,255),
+ "darkorchid2"=>array(178,58,238),
+ "darkorchid3"=>array(154,50,205),
+ "darkorchid4"=>array(104,34,139),
+ "purple1"=>array(155,48,255),
+ "purple2"=>array(145,44,238),
+ "purple3"=>array(125,38,205),
+ "purple4"=>array(85,26,139),
+ "mediumpurple1"=>array(171,130,255),
+ "mediumpurple2"=>array(159,121,238),
+ "mediumpurple3"=>array(137,104,205),
+ "mediumpurple4"=>array(93,71,139),
+ "thistle1"=>array(255,225,255),
+ "thistle2"=>array(238,210,238),
+ "thistle3"=>array(205,181,205),
+ "thistle4"=>array(139,123,139),
+ "gray1"=>array(10,10,10),
+ "gray2"=>array(40,40,30),
+ "gray3"=>array(70,70,70),
+ "gray4"=>array(100,100,100),
+ "gray5"=>array(130,130,130),
+ "gray6"=>array(160,160,160),
+ "gray7"=>array(190,190,190),
+ "gray8"=>array(210,210,210),
+ "gray9"=>array(240,240,240),
+ "darkgray"=>array(100,100,100),
+ "darkblue"=>array(0,0,139),
+ "darkcyan"=>array(0,139,139),
+ "darkmagenta"=>array(139,0,139),
+ "darkred"=>array(139,0,0),
+ "silver"=>array(192, 192, 192),
+ "eggplant"=>array(144,176,168),
+ "lightgreen"=>array(144,238,144));
+ }
+//----------------
+// PUBLIC METHODS
+ // Colors can be specified as either
+ // 1. #xxxxxx HTML style
+ // 2. "colorname" as a named color
+ // 3. array(r,g,b) RGB triple
+ // This function translates this to a native RGB format and returns an
+ // RGB triple.
+ function Color($aColor) {
+ if (is_string($aColor)) {
+ // Strip of any alpha factor
+ $pos = strpos($aColor,'@');
+ if( $pos === false ) {
+ $alpha = 0;
+ }
+ else {
+ $pos2 = strpos($aColor,':');
+ if( $pos2===false )
+ $pos2 = $pos-1; // Sentinel
+ if( $pos > $pos2 ) {
+ $alpha = substr($aColor,$pos+1);
+ $aColor = substr($aColor,0,$pos);
+ }
+ else {
+ $alpha = substr($aColor,$pos+1,$pos2-$pos-1);
+ $aColor = substr($aColor,0,$pos).substr($aColor,$pos2);
+ }
+ }
+
+ // Extract potential adjustment figure at end of color
+ // specification
+ $pos = strpos($aColor,":");
+ if( $pos === false ) {
+ $adj = 1.0;
+ }
+ else {
+ $adj = 0.0 + substr($aColor,$pos+1);
+ $aColor = substr($aColor,0,$pos);
+ }
+ if( $adj < 0 )
+ JpGraphError::Raise('Adjustment factor for color must be > 0');
+
+ if (substr($aColor, 0, 1) == "#") {
+ $r = hexdec(substr($aColor, 1, 2));
+ $g = hexdec(substr($aColor, 3, 2));
+ $b = hexdec(substr($aColor, 5, 2));
+ } else {
+ if(!isset($this->rgb_table[$aColor]) )
+ JpGraphError::Raise(" Unknown color: <strong>$aColor</strong>");
+ $tmp=$this->rgb_table[$aColor];
+ $r = $tmp[0];
+ $g = $tmp[1];
+ $b = $tmp[2];
+ }
+ // Scale adj so that an adj=2 always
+ // makes the color 100% white (i.e. 255,255,255.
+ // and adj=1 neutral and adj=0 black.
+ if( $adj > 1 ) {
+ $m = ($adj-1.0)*(255-min(255,min($r,min($g,$b))));
+ return array(min(255,$r+$m), min(255,$g+$m), min(255,$b+$m),$alpha);
+ }
+ elseif( $adj < 1 ) {
+ $m = ($adj-1.0)*max(255,max($r,max($g,$b)));
+ return array(max(0,$r+$m), max(0,$g+$m), max(0,$b+$m),$alpha);
+ }
+ else {
+ return array($r,$g,$b,$alpha);
+ }
+
+ } elseif( is_array($aColor) ) {
+ if( count($aColor)==3 ) {
+ $aColor[3]=0;
+ return $aColor;
+ }
+ else
+ return $aColor;
+ }
+ else
+ JpGraphError::Raise(" Unknown color specification: $aColor , size=".count($aColor));
+ }
+
+ // Compare two colors
+ // return true if equal
+ function Equal($aCol1,$aCol2) {
+ $c1 = $this->Color($aCol1);
+ $c2 = $this->Color($aCol2);
+ if( $c1[0]==$c2[0] && $c1[1]==$c2[1] && $c1[2]==$c2[2] )
+ return true;
+ else
+ return false;
+ }
+
+ // Allocate a new color in the current image
+ // Return new color index, -1 if no more colors could be allocated
+ function Allocate($aColor,$aAlpha=0.0) {
+ list ($r, $g, $b, $a) = $this->color($aColor);
+ // If alpha is specified in the color string then this
+ // takes precedence over the second argument
+ if( $a > 0 )
+ $aAlpha = $a;
+ if(@$GLOBALS['gd2']==true) {
+ if( $aAlpha < 0 || $aAlpha > 1 ) {
+ JpGraphError::Raise('Alpha parameter for color must be between 0.0 and 1.0');
+ exit(1);
+ }
+ return imagecolorresolvealpha($this->img, $r, $g, $b, round($aAlpha * 127));
+ } else {
+ $index = imagecolorexact($this->img, $r, $g, $b);
+ if ($index == -1) {
+ $index = imagecolorallocate($this->img, $r, $g, $b);
+ if( USE_APPROX_COLORS && $index == -1 )
+ $index = imagecolorresolve($this->img, $r, $g, $b);
+ }
+ return $index;
+ }
+ }
+} // Class
+
+
+//===================================================
+// CLASS Image
+// Description: Wrapper class with some goodies to form the
+// Interface to low level image drawing routines.
+//===================================================
+class Image {
+ var $img_format;
+ var $expired=true;
+ var $img;
+ var $left_margin=30,$right_margin=30,$top_margin=20,$bottom_margin=30;
+ var $plotwidth=0,$plotheight=0;
+ var $rgb=null;
+ var $current_color,$current_color_name;
+ var $lastx=0, $lasty=0;
+ var $width, $height;
+ var $line_weight=1;
+ var $line_style=1; // Default line style is solid
+ var $obs_list=array();
+ var $font_size=12,$font_family=FF_FONT1, $font_style=FS_NORMAL;
+ var $font_file='';
+ var $text_halign="left",$text_valign="bottom";
+ var $ttf=null;
+ var $use_anti_aliasing=false;
+ var $quality=null;
+ var $colorstack=array(),$colorstackidx=0;
+ var $canvascolor = 'white' ;
+ var $langconv = null ;
+
+ //---------------
+ // CONSTRUCTOR
+ function Image($aWidth,$aHeight,$aFormat=DEFAULT_GFORMAT) {
+ $this->CreateImgCanvas($aWidth,$aHeight);
+ $this->SetAutoMargin();
+
+ if( !$this->SetImgFormat($aFormat) ) {
+ JpGraphError::Raise("JpGraph: Selected graphic format is either not supported or unknown [$aFormat]");
+ }
+ $this->ttf = new TTF();
+ $this->langconv = new LanguageConv();
+ }
+
+ // Should we use anti-aliasing. Note: This really slows down graphics!
+ function SetAntiAliasing() {
+ $this->use_anti_aliasing=true;
+ }
+
+ function CreateRawCanvas($aWidth=0,$aHeight=0) {
+ if( @$GLOBALS['gd2']==true && USE_TRUECOLOR ) {
+ $this->img = @imagecreatetruecolor($aWidth, $aHeight);
+ if( $this->img < 1 ) {
+ die("<font color=red><b>JpGraph Error:</b></font> Can't create truecolor image. Check that you really have GD2 library installed.");
+ }
+ $this->SetAlphaBlending();
+ } else {
+ $this->img = @imagecreate($aWidth, $aHeight);
+ if( $this->img < 1 ) {
+ die("<font color=red><b>JpGraph Error:</b></font> Can't create image. Check that you really have the GD library installed.");
+ }
+ }
+ if( $this->rgb != null )
+ $this->rgb->img = $this->img ;
+ else
+ $this->rgb = new RGB($this->img);
+ }
+
+ function CloneCanvasH() {
+ $oldimage = $this->img;
+ $this->CreateRawCanvas($this->width,$this->height);
+ imagecopy($this->img,$oldimage,0,0,0,0,$this->width,$this->height);
+ return $oldimage;
+ }
+
+ function CreateImgCanvas($aWidth=0,$aHeight=0) {
+
+ $old = array($this->img,$this->width,$this->height);
+
+ $aWidth = round($aWidth);
+ $aHeight = round($aHeight);
+
+ $this->width=$aWidth;
+ $this->height=$aHeight;
+
+
+ if( $aWidth==0 || $aHeight==0 ) {
+ // We will set the final size later.
+ // Note: The size must be specified before any other
+ // img routines that stroke anything are called.
+ $this->img = null;
+ $this->rgb = null;
+ return $old;
+ }
+
+ $this->CreateRawCanvas($aWidth,$aHeight);
+
+ // Set canvas color (will also be the background color for a
+ // a pallett image
+ $this->SetColor($this->canvascolor);
+ $this->FilledRectangle(0,0,$aWidth,$aHeight);
+
+ return $old ;
+ }
+
+
+ function CopyCanvasH($aToHdl,$aFromHdl,$aToX,$aToY,$aFromX,$aFromY,$aWidth,$aHeight,$aw=-1,$ah=-1) {
+ if( $aw === -1 ) {
+ $aw = $aWidth;
+ $ah = $aHeight;
+ $f = 'imagecopyresized';
+ }
+ else {
+ $f = $GLOBALS['copyfunc'] ;
+ }
+ $f($aToHdl,$aFromHdl,
+ $aToX,$aToY,$aFromX,$aFromY, $aWidth,$aHeight,$aw,$ah);
+ }
+
+ function Copy($fromImg,$toX,$toY,$fromX,$fromY,
+ $toWidth,$toHeight,$fromWidth=-1,$fromHeight=-1) {
+ $this->CopyCanvasH($this->img,$fromImg,$toX,$toY,$fromX,$fromY,
+ $toWidth,$toHeight,$fromWidth,$fromHeight);
+ }
+
+ function GetWidth($aImg=null) {
+ if( $aImg === null )
+ $aImg = $this->img;
+ return imagesx($aImg);
+ }
+
+ function GetHeight($aImg=null) {
+ if( $aImg === null )
+ $aImg = $this->img;
+ return imagesy($aImg);
+ }
+
+ function CreateFromString($aStr) {
+ return imagecreatefromstring($aStr);
+ }
+
+ function SetCanvasH($aHdl) {
+ $this->img = $aHdl;
+ $this->rgb->img = $aHdl;
+ }
+
+ function SetCanvasColor($aColor) {
+ $this->canvascolor = $aColor ;
+ }
+
+ function SetAlphaBlending($aFlg=true) {
+ if( $GLOBALS['gd2'] )
+ ImageAlphaBlending($this->img,$aFlg);
+ else
+ JpGraphError::Raise('You only seem to have GD 1.x installed. To enable Alphablending requires GD 2.x or higher. Please install GD or make sure the constant USE_GD2 is specified correctly to reflect your installation. By default it tries to autodetect what version of GD you have installed. On some very rare occasions it may falsely detect GD2 where only GD1 is installed. You must then set USE_GD2 to false.');
+ }
+
+
+ function SetAutoMargin() {
+ GLOBAL $gJpgBrandTiming;
+ $min_bm=0;
+ /*
+ if( $gJpgBrandTiming )
+ $min_bm=15;
+ */
+ $lm = max(0,$this->width/7);
+ $rm = max(0,$this->width/10);
+ $tm = max(0,$this->height/7);
+ $bm = max($min_bm,$this->height/7);
+ $this->SetMargin($lm,$rm,$tm,$bm);
+ }
+
+
+ //---------------
+ // PUBLIC METHODS
+
+ // Add observer. The observer will be notified when
+ // the margin changes
+ function AddObserver($aMethod,&$aObject) {
+ $this->obs_list[]=array($aMethod,&$aObject);
+ }
+
+ // Call all observers
+ function NotifyObservers() {
+ // foreach($this->obs_list as $o)
+ // $o[1]->$o[0]($this);
+ for($i=0; $i < count($this->obs_list); ++$i) {
+ $obj = & $this->obs_list[$i][1];
+ $method = $this->obs_list[$i][0];
+ $obj->$method($this);
+ }
+ }
+
+ function SetFont($family,$style=FS_NORMAL,$size=10) {
+ if($family==FONT1_BOLD || $family==FONT2_BOLD || $family==FONT0 || $family==FONT1 || $family==FONT2 )
+ JpGraphError::Raise(" Usage of FONT0, FONT1, FONT2 is deprecated. Use FF_xxx instead.");
+
+
+ $this->font_family=$family;
+ $this->font_style=$style;
+ $this->font_size=$size;
+ $this->font_file='';
+ if( ($this->font_family==FF_FONT1 || $this->font_family==FF_FONT2) && $this->font_style==FS_BOLD ){
+ ++$this->font_family;
+ }
+ if( $this->font_family > FF_FONT2+1 ) { // A TTF font so get the font file
+
+ // Check that this PHP has support for TTF fonts
+ if( !function_exists('imagettfbbox') ) {
+ JpGraphError::Raise('This PHP build has not been configured with TTF support. You need to recompile your PHP installation with FreeType support.');
+ exit();
+ }
+ $this->font_file = $this->ttf->File($this->font_family,$this->font_style);
+ }
+ }
+
+ // Get the specific height for a text string
+ function GetTextHeight($txt="",$angle=0) {
+ $tmp = split("\n",$txt);
+ $n = count($tmp);
+ $m=0;
+ for($i=0; $i< $n; ++$i)
+ $m = max($m,strlen($tmp[$i]));
+
+ if( $this->font_family <= FF_FONT2+1 ) {
+ if( $angle==0 )
+ return $n*imagefontheight($this->font_family);
+ else
+ return $m*imagefontwidth($this->font_family);
+ }
+ else {
+ $bbox = $this->GetTTFBBox($txt,$angle);
+ return $bbox[1]-$bbox[5];
+ }
+ }
+
+ // Estimate font height
+ function GetFontHeight($angle=0) {
+ $txt = "XOMg";
+ return $this->GetTextHeight($txt,$angle);
+ }
+
+ // Approximate font width with width of letter "O"
+ function GetFontWidth($angle=0) {
+ $txt = 'O';
+ return $this->GetTextWidth($txt,$angle);
+ }
+
+ // Get actual width of text in absolute pixels
+ function GetTextWidth($txt,$angle=0) {
+
+ $tmp = split("\n",$txt);
+ $n = count($tmp);
+ if( $this->font_family <= FF_FONT2+1 ) {
+
+ $m=0;
+ for($i=0; $i < $n; ++$i) {
+ $l=strlen($tmp[$i]);
+ if( $l > $m ) {
+ $m = $l;
+ }
+ }
+
+ if( $angle==0 ) {
+ $width=$m*imagefontwidth($this->font_family);
+ return $width;
+ }
+ else {
+ // 90 degrees internal so height becomes width
+ return $n*imagefontheight($this->font_family);
+ }
+ }
+ else {
+ // For TTF fonts we must walk through a lines and find the
+ // widest one which we use as the width of the multi-line
+ // paragraph
+ $m=0;
+ for( $i=0; $i < $n; ++$i ) {
+ $bbox = $this->GetTTFBBox($tmp[$i],$angle);
+ $mm = $bbox[2] - $bbox[0];
+ if( $mm > $m )
+ $m = $mm;
+ }
+ return $m;
+ }
+ }
+
+ // Draw text with a box around it
+ function StrokeBoxedText($x,$y,$txt,$dir=0,$fcolor="white",$bcolor="black",
+ $shadowcolor=false,$paragraph_align="left",
+ $xmarg=6,$ymarg=4,$cornerradius=0,$dropwidth=3) {
+
+ if( !is_numeric($dir) ) {
+ if( $dir=="h" ) $dir=0;
+ elseif( $dir=="v" ) $dir=90;
+ else JpGraphError::Raise(" Unknown direction specified in call to StrokeBoxedText() [$dir]");
+ }
+
+ if( $this->font_family >= FF_FONT0 && $this->font_family <= FF_FONT2+1) {
+ $width=$this->GetTextWidth($txt,$dir) ;
+ $height=$this->GetTextHeight($txt,$dir) ;
+ }
+ else {
+ $width=$this->GetBBoxWidth($txt,$dir) ;
+ $height=$this->GetBBoxHeight($txt,$dir) ;
+ }
+
+ $height += 2*$ymarg;
+ $width += 2*$xmarg;
+
+ if( $this->text_halign=="right" ) $x -= $width;
+ elseif( $this->text_halign=="center" ) $x -= $width/2;
+ if( $this->text_valign=="bottom" ) $y -= $height;
+ elseif( $this->text_valign=="center" ) $y -= $height/2;
+
+ if( $shadowcolor ) {
+ $this->PushColor($shadowcolor);
+ $this->FilledRoundedRectangle($x-$xmarg+$dropwidth,$y-$ymarg+$dropwidth,
+ $x+$width+$dropwidth,$y+$height+$dropwidth,
+ $cornerradius);
+ $this->PopColor();
+ $this->PushColor($fcolor);
+ $this->FilledRoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height,$cornerradius);
+ $this->PopColor();
+ $this->PushColor($bcolor);
+ $this->RoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height,$cornerradius);
+ $this->PopColor();
+ }
+ else {
+ if( $fcolor ) {
+ $oc=$this->current_color;
+ $this->SetColor($fcolor);
+ $this->FilledRoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height,$cornerradius);
+ $this->current_color=$oc;
+ }
+ if( $bcolor ) {
+ $oc=$this->current_color;
+ $this->SetColor($bcolor);
+ $this->RoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height,$cornerradius);
+ $this->current_color=$oc;
+ }
+ }
+
+ $h=$this->text_halign;
+ $v=$this->text_valign;
+ $this->SetTextAlign("left","top");
+ $this->StrokeText($x, $y, $txt, $dir, $paragraph_align);
+ $this->SetTextAlign($h,$v);
+ }
+
+ // Set text alignment
+ function SetTextAlign($halign,$valign="bottom") {
+ $this->text_halign=$halign;
+ $this->text_valign=$valign;
+ }
+
+
+ function _StrokeBuiltinFont($x,$y,$txt,$dir=0,$paragraph_align="left") {
+
+ if( is_numeric($dir) && $dir!=90 && $dir!=0)
+ JpGraphError::Raise(" Internal font does not support drawing text at arbitrary angle. Use TTF fonts instead.");
+
+ $h=$this->GetTextHeight($txt);
+ $fh=$this->GetFontHeight();
+ $w=$this->GetTextWidth($txt);
+
+ if( $this->text_halign=="right")
+ $x -= $dir==0 ? $w : $h;
+ elseif( $this->text_halign=="center" ) {
+ // For center we subtract 1 pixel since this makes the middle
+ // be prefectly in the middle
+ $x -= $dir==0 ? $w/2-1 : $h/2;
+ }
+ if( $this->text_valign=="top" )
+ $y += $dir==0 ? $h : $w;
+ elseif( $this->text_valign=="center" )
+ $y += $dir==0 ? $h/2 : $w/2;
+
+ if( $dir==90 )
+ imagestringup($this->img,$this->font_family,$x,$y,$txt,$this->current_color);
+ else {
+ if( ereg("\n",$txt) ) {
+ $tmp = split("\n",$txt);
+ for($i=0; $i < count($tmp); ++$i) {
+ $w1 = $this->GetTextWidth($tmp[$i]);
+ if( $paragraph_align=="left" ) {
+ imagestring($this->img,$this->font_family,$x,$y-$h+1+$i*$fh,$tmp[$i],$this->current_color);
+ }
+ elseif( $paragraph_align=="right" ) {
+ imagestring($this->img,$this->font_family,$x+($w-$w1),
+ $y-$h+1+$i*$fh,$tmp[$i],$this->current_color);
+ }
+ else {
+ imagestring($this->img,$this->font_family,$x+$w/2-$w1/2,
+ $y-$h+1+$i*$fh,$tmp[$i],$this->current_color);
+ }
+ }
+ }
+ else {
+ //Put the text
+ imagestring($this->img,$this->font_family,$x,$y-$h+1,$txt,$this->current_color);
+ }
+ }
+ }
+
+ function AddTxtCR($aTxt) {
+ // If the user has just specified a '\n'
+ // instead of '\n\t' we have to add '\r' since
+ // the width will be too muchy otherwise since when
+ // we print we stroke the individually lines by hand.
+ $e = explode("\n",$aTxt);
+ $n = count($e);
+ for($i=0; $i<$n; ++$i) {
+ $e[$i]=str_replace("\r","",$e[$i]);
+ }
+ return implode("\n\r",$e);
+ }
+
+ function GetTTFBBox($aTxt,$aAngle=0) {
+ $bbox = @ImageTTFBBox($this->font_size,$aAngle,$this->font_file,$aTxt);
+ if( $bbox === false ) {
+ JpGraphError::Raise("There is either a configuration problem with TrueType or a problem reading font file (".$this->font_file."). Make sure file exists and is in a readable place for the HTTP process. (If 'basedir' restriction is enabled in PHP then the font file must be located in the document root.). It might also be a wrongly installed FreeType library. Try uppgrading to at least FreeType 2.1.13 and recompile GD with the correct setup so it can find the new FT library.");
+ }
+ return $bbox;
+ }
+
+ function GetBBoxTTF($aTxt,$aAngle=0) {
+ // Normalize the bounding box to become a minimum
+ // enscribing rectangle
+
+ $aTxt = $this->AddTxtCR($aTxt);
+
+ if( !is_readable($this->font_file) ) {
+ JpGraphError::Raise('Can not read font file ('.$this->font_file.') in call to Image::GetBBoxTTF. Please make sure that you have set a font before calling this method and that the font is installed in the TTF directory.');
+ }
+ $bbox = $this->GetTTFBBox($aTxt,$aAngle);
+
+ if( $aAngle==0 )
+ return $bbox;
+ if( $aAngle >= 0 ) {
+ if( $aAngle <= 90 ) { //<=0
+ $bbox = array($bbox[6],$bbox[1],$bbox[2],$bbox[1],
+ $bbox[2],$bbox[5],$bbox[6],$bbox[5]);
+ }
+ elseif( $aAngle <= 180 ) { //<= 2
+ $bbox = array($bbox[4],$bbox[7],$bbox[0],$bbox[7],
+ $bbox[0],$bbox[3],$bbox[4],$bbox[3]);
+ }
+ elseif( $aAngle <= 270 ) { //<= 3
+ $bbox = array($bbox[2],$bbox[5],$bbox[6],$bbox[5],
+ $bbox[6],$bbox[1],$bbox[2],$bbox[1]);
+ }
+ else {
+ $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3],
+ $bbox[4],$bbox[7],$bbox[0],$bbox[7]);
+ }
+ }
+ elseif( $aAngle < 0 ) {
+ if( $aAngle <= -270 ) { // <= -3
+ $bbox = array($bbox[6],$bbox[1],$bbox[2],$bbox[1],
+ $bbox[2],$bbox[5],$bbox[6],$bbox[5]);
+ }
+ elseif( $aAngle <= -180 ) { // <= -2
+ $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3],
+ $bbox[4],$bbox[7],$bbox[0],$bbox[7]);
+ }
+ elseif( $aAngle <= -90 ) { // <= -1
+ $bbox = array($bbox[2],$bbox[5],$bbox[6],$bbox[5],
+ $bbox[6],$bbox[1],$bbox[2],$bbox[1]);
+ }
+ else {
+ $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3],
+ $bbox[4],$bbox[7],$bbox[0],$bbox[7]);
+ }
+ }
+ return $bbox;
+ }
+
+ function GetBBoxHeight($aTxt,$aAngle=0) {
+ $box = $this->GetBBoxTTF($aTxt,$aAngle);
+ return $box[1]-$box[7]+1;
+ }
+
+ function GetBBoxWidth($aTxt,$aAngle=0) {
+ $box = $this->GetBBoxTTF($aTxt,$aAngle);
+ return $box[2]-$box[0]+1;
+ }
+
+ function _StrokeTTF($x,$y,$txt,$dir=0,$paragraph_align="left",$debug=false) {
+
+ // Remember the anchor point before adjustment
+ if( $debug ) {
+ $ox=$x;
+ $oy=$y;
+ }
+
+ if( !ereg("\n",$txt) || ($dir>0 && ereg("\n",$txt)) ) {
+ // Format a single line
+
+ $txt = $this->AddTxtCR($txt);
+
+ $bbox=$this->GetBBoxTTF($txt,$dir);
+
+ // Align x,y ot lower left corner of bbox
+ $x -= $bbox[0];
+ $y -= $bbox[1];
+
+ // Note to self: "topanchor" is deprecated after we changed the
+ // bopunding box stuff.
+ if( $this->text_halign=="right" || $this->text_halign=="topanchor" )
+ $x -= $bbox[2]-$bbox[0];
+ elseif( $this->text_halign=="center" ) $x -= ($bbox[2]-$bbox[0])/2;
+
+ if( $this->text_valign=="top" ) $y += abs($bbox[5])+$bbox[1];
+ elseif( $this->text_valign=="center" ) $y -= ($bbox[5]-$bbox[1])/2;
+
+ ImageTTFText ($this->img, $this->font_size, $dir, $x, $y,
+ $this->current_color,$this->font_file,$txt);
+
+ if( $debug ) {
+ // Draw the bounding rectangle and the bounding box
+ $box=ImageTTFBBox($this->font_size,$dir,$this->font_file,$txt);
+ $p = array();
+ $p1 = array();
+ for($i=0; $i < 4; ++$i) {
+ $p[] = $bbox[$i*2]+$x;
+ $p[] = $bbox[$i*2+1]+$y;
+ $p1[] = $box[$i*2]+$x;
+ $p1[] = $box[$i*2+1]+$y;
+ }
+
+ // Draw bounding box
+ $this->PushColor('green');
+ $this->Polygon($p1,true);
+ $this->PopColor();
+
+ // Draw bounding rectangle
+ $this->PushColor('darkgreen');
+ $this->Polygon($p,true);
+ $this->PopColor();
+
+ // Draw a cross at the anchor point
+ $this->PushColor('red');
+ $this->Line($ox-15,$oy,$ox+15,$oy);
+ $this->Line($ox,$oy-15,$ox,$oy+15);
+ $this->PopColor();
+ }
+ }
+ else {
+ // Format a text paragraph
+ $fh=$this->GetFontHeight();
+
+ // Line margin is 15% of font height
+ $linemargin=round($fh*0.15);
+ $fh += $linemargin;
+ $w=$this->GetTextWidth($txt);
+
+ $y -= $linemargin/2;
+ $tmp = split("\n",$txt);
+ $nl = count($tmp);
+ $h = $nl * $fh;
+
+ if( $this->text_halign=="right")
+ $x -= $dir==0 ? $w : $h;
+ elseif( $this->text_halign=="center" ) {
+ $x -= $dir==0 ? $w/2 : $h/2;
+ }
+
+ if( $this->text_valign=="top" )
+ $y += $dir==0 ? $h : $w;
+ elseif( $this->text_valign=="center" )
+ $y += $dir==0 ? $h/2 : $w/2;
+
+ // Here comes a tricky bit.
+ // Since we have to give the position for the string at the
+ // baseline this means thaht text will move slightly up
+ // and down depending on any of it's character descend below
+ // the baseline, for example a 'g'. To adjust the Y-position
+ // we therefore adjust the text with the baseline Y-offset
+ // as used for the current font and size. This will keep the
+ // baseline at a fixed positoned disregarding the actual
+ // characters in the string.
+ $standardbox = $this->GetTTFBBox('Gg',$dir);
+ $yadj = $standardbox[1];
+ $xadj = $standardbox[0];
+ for($i=0; $i < $nl; ++$i) {
+ $wl = $this->GetTextWidth($tmp[$i]);
+ $bbox = $this->GetTTFBBox($tmp[$i],$dir);
+ if( $paragraph_align=="left" ) {
+ $xl = $x;
+ }
+ elseif( $paragraph_align=="right" ) {
+ $xl = $x + ($w-$wl);
+ }
+ else {
+ // Center
+ $xl = $x + $w/2 - $wl/2 ;
+ }
+
+ $xl -= $bbox[0];
+ $yl = $y - $yadj;
+ $xl = $xl - $xadj;
+ ImageTTFText ($this->img, $this->font_size, $dir,
+ $xl, $yl-($h-$fh)+$fh*$i,
+ $this->current_color,$this->font_file,$tmp[$i]);
+
+
+ if( $debug ) {
+ // Draw the bounding rectangle around each line
+ $box=ImageTTFBBox($this->font_size,$dir,$this->font_file,$tmp[$i]);
+ $p = array();
+ for($j=0; $j < 4; ++$j) {
+ $p[] = $bbox[$j*2]+$xl;
+ $p[] = $bbox[$j*2+1]+$yl-($h-$fh)+$fh*$i;
+ }
+
+ // Draw bounding rectangle
+ $this->PushColor('darkgreen');
+ $this->Polygon($p,true);
+ $this->PopColor();
+ }
+ }
+
+ if( $debug ) {
+
+ // Draw a cross at the anchor point
+ $this->PushColor('red');
+ $this->Line($ox-25,$oy,$ox+25,$oy);
+ $this->Line($ox,$oy-25,$ox,$oy+25);
+ $this->PopColor();
+ }
+
+ }
+ }
+
+ function StrokeText($x,$y,$txt,$dir=0,$paragraph_align="left",$debug=false) {
+
+ $x = round($x);
+ $y = round($y);
+
+ // Do special language encoding
+ $txt = $this->langconv->Convert($txt,$this->font_family);
+
+ if( !is_numeric($dir) )
+ JpGraphError::Raise(" Direction for text most be given as an angle between 0 and 90.");
+
+ if( $this->font_family >= FF_FONT0 && $this->font_family <= FF_FONT2+1) {
+ $this->_StrokeBuiltinFont($x,$y,$txt,$dir,$paragraph_align,$debug);
+ }
+ elseif($this->font_family >= FF_COURIER && $this->font_family <= FF_BOOK) {
+ $this->_StrokeTTF($x,$y,$txt,$dir,$paragraph_align,$debug);
+ }
+ else
+ JpGraphError::Raise(" Unknown font font family specification. ");
+ }
+
+ function SetMargin($lm,$rm,$tm,$bm) {
+ $this->left_margin=$lm;
+ $this->right_margin=$rm;
+ $this->top_margin=$tm;
+ $this->bottom_margin=$bm;
+ $this->plotwidth=$this->width - $this->left_margin-$this->right_margin ;
+ $this->plotheight=$this->height - $this->top_margin-$this->bottom_margin ;
+ if( $this->plotwidth < 0 || $this->plotheight < 0 )
+ JpGraphError::raise("To small plot area. ($lm,$rm,$tm,$bm : $this->plotwidth x $this->plotheight). With the given image size and margins there is to little space left for the plot. Increase the plot size or reduce the margins.");
+ $this->NotifyObservers();
+ }
+
+ function SetTransparent($color) {
+ imagecolortransparent ($this->img,$this->rgb->allocate($color));
+ }
+
+ function SetColor($color,$aAlpha=0) {
+ $this->current_color_name = $color;
+ $this->current_color=$this->rgb->allocate($color,$aAlpha);
+ if( $this->current_color == -1 ) {
+ $tc=imagecolorstotal($this->img);
+ JpGraphError::Raise("Can't allocate any more colors.
+ Image has already allocated maximum of <b>$tc colors</b>.
+ This might happen if you have anti-aliasing turned on
+ together with a background image or perhaps gradient fill
+ since this requires many, many colors. Try to turn off
+ anti-aliasing.<p>
+ If there is still a problem try downgrading the quality of
+ the background image to use a smaller pallete to leave some
+ entries for your graphs. You should try to limit the number
+ of colors in your background image to 64.<p>
+ If there is still problem set the constant
+<pre>
+DEFINE(\"USE_APPROX_COLORS\",true);
+</pre>
+ in jpgraph.php This will use approximative colors
+ when the palette is full.
+ <p>
+ Unfortunately there is not much JpGraph can do about this
+ since the palette size is a limitation of current graphic format and
+ what the underlying GD library suppports.");
+ }
+ return $this->current_color;
+ }
+
+ function PushColor($color) {
+ if( $color != "" ) {
+ $this->colorstack[$this->colorstackidx]=$this->current_color_name;
+ $this->colorstack[$this->colorstackidx+1]=$this->current_color;
+ $this->colorstackidx+=2;
+ $this->SetColor($color);
+ }
+ else {
+ JpGraphError::Raise("Color specified as empty string in PushColor().");
+ }
+ }
+
+ function PopColor() {
+ if($this->colorstackidx<1)
+ JpGraphError::Raise(" Negative Color stack index. Unmatched call to PopColor()");
+ $this->current_color=$this->colorstack[--$this->colorstackidx];
+ $this->current_color_name=$this->colorstack[--$this->colorstackidx];
+ }
+
+
+ // Why this duplication? Because this way we can call this method
+ // for any image and not only the current objsct
+ function AdjSat($sat) { $this->_AdjSat($this->img,$sat); }
+
+ function _AdjSat($img,$sat) {
+ $nbr = imagecolorstotal ($img);
+ for( $i=0; $i<$nbr; ++$i ) {
+ $colarr = imagecolorsforindex ($img,$i);
+ $rgb[0]=$colarr["red"];
+ $rgb[1]=$colarr["green"];
+ $rgb[2]=$colarr["blue"];
+ $rgb = $this->AdjRGBSat($rgb,$sat);
+ imagecolorset ($img, $i, $rgb[0], $rgb[1], $rgb[2]);
+ }
+ }
+
+ function AdjBrightContrast($bright,$contr=0) {
+ $this->_AdjBrightContrast($this->img,$bright,$contr);
+ }
+ function _AdjBrightContrast($img,$bright,$contr=0) {
+ if( $bright < -1 || $bright > 1 || $contr < -1 || $contr > 1 )
+ JpGraphError::Raise(" Parameters for brightness and Contrast out of range [-1,1]");
+ $nbr = imagecolorstotal ($img);
+ for( $i=0; $i<$nbr; ++$i ) {
+ $colarr = imagecolorsforindex ($img,$i);
+ $r = $this->AdjRGBBrightContrast($colarr["red"],$bright,$contr);
+ $g = $this->AdjRGBBrightContrast($colarr["green"],$bright,$contr);
+ $b = $this->AdjRGBBrightContrast($colarr["blue"],$bright,$contr);
+ imagecolorset ($img, $i, $r, $g, $b);
+ }
+ }
+
+ // Private helper function for adj sat
+ // Adjust saturation for RGB array $u. $sat is a value between -1 and 1
+ // Note: Due to GD inability to handle true color the RGB values are only between
+ // 8 bit. This makes saturation quite sensitive for small increases in parameter sat.
+ //
+ // Tip: To get a grayscale picture set sat=-100, values <-100 changes the colors
+ // to it's complement.
+ //
+ // Implementation note: The saturation is implemented directly in the RGB space
+ // by adjusting the perpendicular distance between the RGB point and the "grey"
+ // line (1,1,1). Setting $sat>0 moves the point away from the line along the perp.
+ // distance and a negative value moves the point closer to the line.
+ // The values are truncated when the color point hits the bounding box along the
+ // RGB axis.
+ // DISCLAIMER: I'm not 100% sure this is he correct way to implement a color
+ // saturation function in RGB space. However, it looks ok and has the expected effect.
+ function AdjRGBSat($rgb,$sat) {
+ // TODO: Should be moved to the RGB class
+ // Grey vector
+ $v=array(1,1,1);
+
+ // Dot product
+ $dot = $rgb[0]*$v[0]+$rgb[1]*$v[1]+$rgb[2]*$v[2];
+
+ // Normalize dot product
+ $normdot = $dot/3; // dot/|v|^2
+
+ // Direction vector between $u and its projection onto $v
+ for($i=0; $i<3; ++$i)
+ $r[$i] = $rgb[$i] - $normdot*$v[$i];
+
+ // Adjustment factor so that sat==1 sets the highest RGB value to 255
+ if( $sat > 0 ) {
+ $m=0;
+ for( $i=0; $i<3; ++$i) {
+ if( sign($r[$i]) == 1 && $r[$i]>0)
+ $m=max($m,(255-$rgb[$i])/$r[$i]);
+ }
+ $tadj=$m;
+ }
+ else
+ $tadj=1;
+
+ $tadj = $tadj*$sat;
+ for($i=0; $i<3; ++$i) {
+ $un[$i] = round($rgb[$i] + $tadj*$r[$i]);
+ if( $un[$i]<0 ) $un[$i]=0; // Truncate color when they reach 0
+ if( $un[$i]>255 ) $un[$i]=255;// Avoid potential rounding error
+ }
+ return $un;
+ }
+
+ // Private helper function for AdjBrightContrast
+ function AdjRGBBrightContrast($rgb,$bright,$contr) {
+ // TODO: Should be moved to the RGB class
+ // First handle contrast, i.e change the dynamic range around grey
+ if( $contr <= 0 ) {
+ // Decrease contrast
+ $adj = abs($rgb-128) * (-$contr);
+ if( $rgb < 128 ) $rgb += $adj;
+ else $rgb -= $adj;
+ }
+ else { // $contr > 0
+ // Increase contrast
+ if( $rgb < 128 ) $rgb = $rgb - ($rgb * $contr);
+ else $rgb = $rgb + ((255-$rgb) * $contr);
+ }
+
+ // Add (or remove) various amount of white
+ $rgb += $bright*255;
+ $rgb=min($rgb,255);
+ $rgb=max($rgb,0);
+ return $rgb;
+ }
+
+ function SetLineWeight($weight) {
+ $this->line_weight = $weight;
+ }
+
+ function SetStartPoint($x,$y) {
+ $this->lastx=round($x);
+ $this->lasty=round($y);
+ }
+
+ function Arc($cx,$cy,$w,$h,$s,$e) {
+ // GD Arc doesn't like negative angles
+ while( $s < 0) $s += 360;
+ while( $e < 0) $e += 360;
+
+ imagearc($this->img,round($cx),round($cy),round($w),round($h),
+ $s,$e,$this->current_color);
+ }
+
+ function FilledArc($xc,$yc,$w,$h,$s,$e,$style="") {
+
+ if( $GLOBALS['gd2'] ) {
+ while( $s < 0 ) $s += 360;
+ while( $e < 0 ) $e += 360;
+ if( $style=="" )
+ $style=IMG_ARC_PIE;
+ imagefilledarc($this->img,round($xc),round($yc),round($w),round($h),
+ round($s),round($e),$this->current_color,$style);
+ return;
+ }
+
+
+ // In GD 1.x we have to do it ourself interesting enough there is surprisingly
+ // little difference in time between doing it PHP and using the optimised GD
+ // library (roughly ~20%) I had expected it to be at least 100% slower doing it
+ // manually with a polygon approximation in PHP.....
+ $fillcolor = $this->current_color_name;
+
+ $w /= 2; // We use radius in our calculations instead
+ $h /= 2;
+
+ // Setup the angles so we have the same conventions as the builtin
+ // FilledArc() which is a little bit strange if you ask me....
+
+ $s = 360-$s;
+ $e = 360-$e;
+
+ if( $e > $s ) {
+ $e = $e - 360;
+ $da = $s - $e;
+ }
+ $da = $s-$e;
+
+ // We use radians
+ $s *= M_PI/180;
+ $e *= M_PI/180;
+ $da *= M_PI/180;
+
+ // Calculate a polygon approximation
+ $p[0] = $xc;
+ $p[1] = $yc;
+
+ // Heuristic on how many polygons we need to make the
+ // arc look good
+ $numsteps = round(8 * abs($da) * ($w+$h)*($w+$h)/1500);
+
+ if( $numsteps == 0 ) return;
+ if( $numsteps < 7 ) $numsteps=7;
+ $delta = abs($da)/$numsteps;
+
+ $pa=array();
+ $a = $s;
+ for($i=1; $i<=$numsteps; ++$i ) {
+ $p[2*$i] = round($xc + $w*cos($a));
+ $p[2*$i+1] = round($yc - $h*sin($a));
+ //$a = $s + $i*$delta;
+ $a -= $delta;
+ $pa[2*($i-1)] = $p[2*$i];
+ $pa[2*($i-1)+1] = $p[2*$i+1];
+ }
+
+ // Get the last point at the exact ending angle to avoid
+ // any rounding errors.
+ $p[2*$i] = round($xc + $w*cos($e));
+ $p[2*$i+1] = round($yc - $h*sin($e));
+ $pa[2*($i-1)] = $p[2*$i];
+ $pa[2*($i-1)+1] = $p[2*$i+1];
+ $i++;
+
+ $p[2*$i] = $xc;
+ $p[2*$i+1] = $yc;
+ if( $fillcolor != "" ) {
+ $this->PushColor($fillcolor);
+ imagefilledpolygon($this->img,$p,count($p)/2,$this->current_color);
+ $this->PopColor();
+ }
+ }
+
+ function FilledCakeSlice($cx,$cy,$w,$h,$s,$e) {
+ $this->CakeSlice($cx,$cy,$w,$h,$s,$e,$this->current_color_name);
+ }
+
+ function CakeSlice($xc,$yc,$w,$h,$s,$e,$fillcolor="",$arccolor="") {
+ $s = round($s); $e = round($e);
+ $w = round($w); $h = round($h);
+ $xc = round($xc); $yc = round($yc);
+ $this->PushColor($fillcolor);
+ $this->FilledArc($xc,$yc,2*$w,2*$h,$s,$e);
+ $this->PopColor();
+ if( $arccolor != "" ) {
+ $this->PushColor($arccolor);
+ // We add 2 pixels to make the Arc() better aligned with
+ // the filled arc.
+ if( $GLOBALS['gd2'] ) {
+ imagefilledarc($this->img,$xc,$yc,2*$w,2*$h,$s,$e,$this->current_color_name,
+ IMG_ARC_NOFILL | IMG_ARC_EDGED ) ;
+ }
+ else {
+ $this->Arc($xc,$yc,2*$w+2,2*$h+2,$s,$e);
+ $xx = $w * cos(2*M_PI - $s*M_PI/180) + $xc;
+ $yy = $yc - $h * sin(2*M_PI - $s*M_PI/180);
+ $this->Line($xc,$yc,$xx,$yy);
+ $xx = $w * cos(2*M_PI - $e*M_PI/180) + $xc;
+ $yy = $yc - $h * sin(2*M_PI - $e*M_PI/180);
+ $this->Line($xc,$yc,$xx,$yy);
+ }
+ $this->PopColor();
+ }
+ }
+
+ function Ellipse($xc,$yc,$w,$h) {
+ $this->Arc($xc,$yc,$w,$h,0,360);
+ }
+
+ // Breseham circle gives visually better result then using GD
+ // built in arc(). It takes some more time but gives better
+ // accuracy.
+ function BresenhamCircle($xc,$yc,$r) {
+ $d = 3-2*$r;
+ $x = 0;
+ $y = $r;
+ while($x<=$y) {
+ $this->Point($xc+$x,$yc+$y);
+ $this->Point($xc+$x,$yc-$y);
+ $this->Point($xc-$x,$yc+$y);
+ $this->Point($xc-$x,$yc-$y);
+
+ $this->Point($xc+$y,$yc+$x);
+ $this->Point($xc+$y,$yc-$x);
+ $this->Point($xc-$y,$yc+$x);
+ $this->Point($xc-$y,$yc-$x);
+
+ if( $d<0 ) $d += 4*$x+6;
+ else {
+ $d += 4*($x-$y)+10;
+ --$y;
+ }
+ ++$x;
+ }
+ }
+
+ function Circle($xc,$yc,$r) {
+ if( USE_BRESENHAM )
+ $this->BresenhamCircle($xc,$yc,$r);
+ else {
+
+ /*
+ // Some experimental code snippet to see if we can get a decent
+ // result doing a trig-circle
+ // Create an approximated circle with 0.05 rad resolution
+ $end = 2*M_PI;
+ $l = $r/10;
+ if( $l < 3 ) $l=3;
+ $step_size = 2*M_PI/(2*$r*M_PI/$l);
+ $pts = array();
+ $pts[] = $r + $xc;
+ $pts[] = $yc;
+ for( $a=$step_size; $a <= $end; $a += $step_size ) {
+ $pts[] = round($xc + $r*cos($a));
+ $pts[] = round($yc - $r*sin($a));
+ }
+ imagepolygon($this->img,$pts,count($pts)/2,$this->current_color);
+ */
+
+ $this->Arc($xc,$yc,$r*2,$r*2,0,360);
+
+ // For some reason imageellipse() isn't in GD 2.0.1, PHP 4.1.1
+ //imageellipse($this->img,$xc,$yc,$r,$r,$this->current_color);
+ }
+ }
+
+ function FilledCircle($xc,$yc,$r) {
+ if( $GLOBALS['gd2'] ) {
+ imagefilledellipse($this->img,round($xc),round($yc),
+ 2*$r,2*$r,$this->current_color);
+ }
+ else {
+ for( $i=1; $i < 2*$r; $i += 2 ) {
+ // To avoid moire patterns we have to draw some
+ // 1 extra "skewed" filled circles
+ $this->Arc($xc,$yc,$i,$i,0,360);
+ $this->Arc($xc,$yc,$i+1,$i,0,360);
+ $this->Arc($xc,$yc,$i+1,$i+1,0,360);
+ }
+ }
+ }
+
+ // Linear Color InterPolation
+ function lip($f,$t,$p) {
+ $p = round($p,1);
+ $r = $f[0] + ($t[0]-$f[0])*$p;
+ $g = $f[1] + ($t[1]-$f[1])*$p;
+ $b = $f[2] + ($t[2]-$f[2])*$p;
+ return array($r,$g,$b);
+ }
+
+ // Anti-aliased line.
+ // Note that this is roughly 8 times slower then a normal line!
+ function WuLine($x1,$y1,$x2,$y2) {
+ // Get foreground line color
+ $lc = imagecolorsforindex($this->img,$this->current_color);
+ $lc = array($lc["red"],$lc["green"],$lc["blue"]);
+
+ $dx = $x2-$x1;
+ $dy = $y2-$y1;
+
+ if( abs($dx) > abs($dy) ) {
+ if( $dx<0 ) {
+ $dx = -$dx;$dy = -$dy;
+ $tmp=$x2;$x2=$x1;$x1=$tmp;
+ $tmp=$y2;$y2=$y1;$y1=$tmp;
+ }
+ $x=$x1<<16; $y=$y1<<16;
+ $yinc = ($dy*65535)/$dx;
+ while( ($x >> 16) < $x2 ) {
+
+ $bc = @imagecolorsforindex($this->img,imagecolorat($this->img,$x>>16,$y>>16));
+ if( $bc <= 0 ) {
+ JpGraphError::Raise('Problem with color palette and your GD setup. Please disable anti-aliasing or use GD2 with true-color. If you have GD2 library installed please make sure that you have set the USE_GD2 constant to true and that truecolor is enabled.');
+ }
+ $bc=array($bc["red"],$bc["green"],$bc["blue"]);
+
+ $this->SetColor($this->lip($lc,$bc,($y & 0xFFFF)/65535));
+ imagesetpixel($this->img,$x>>16,$y>>16,$this->current_color);
+ $this->SetColor($this->lip($lc,$bc,(~$y & 0xFFFF)/65535));
+ imagesetpixel($this->img,$x>>16,($y>>16)+1,$this->current_color);
+ $x += 65536; $y += $yinc;
+ }
+ }
+ else {
+ if( $dy<0 ) {
+ $dx = -$dx;$dy = -$dy;
+ $tmp=$x2;$x2=$x1;$x1=$tmp;
+ $tmp=$y2;$y2=$y1;$y1=$tmp;
+ }
+ $x=$x1<<16; $y=$y1<<16;
+ $xinc = ($dx*65535)/$dy;
+ while( ($y >> 16) < $y2 ) {
+
+ $bc = @imagecolorsforindex($this->img,imagecolorat($this->img,$x>>16,$y>>16));
+ if( $bc <= 0 ) {
+ JpGraphError::Raise('Problem with color palette and your GD setup. Please disable anti-aliasing or use GD2 with true-color. If you have GD2 library installed please make sure that you have set the USE_GD2 constant to true and truecolor is enabled.');
+
+ }
+
+ $bc=array($bc["red"],$bc["green"],$bc["blue"]);
+
+ $this->SetColor($this->lip($lc,$bc,($x & 0xFFFF)/65535));
+ imagesetpixel($this->img,$x>>16,$y>>16,$this->current_color);
+ $this->SetColor($this->lip($lc,$bc,(~$x & 0xFFFF)/65535));
+ imagesetpixel($this->img,($x>>16)+1,$y>>16,$this->current_color);
+ $y += 65536; $x += $xinc;
+ }
+ }
+ $this->SetColor($lc);
+ imagesetpixel($this->img,$x2,$y2,$this->current_color);
+ imagesetpixel($this->img,$x1,$y1,$this->current_color);
+ }
+
+ // Set line style dashed, dotted etc
+ function SetLineStyle($s) {
+ if( is_numeric($s) ) {
+ if( $s<1 || $s>4 )
+ JpGraphError::Raise(" Illegal numeric argument to SetLineStyle(): ($s)");
+ }
+ elseif( is_string($s) ) {
+ if( $s == "solid" ) $s=1;
+ elseif( $s == "dotted" ) $s=2;
+ elseif( $s == "dashed" ) $s=3;
+ elseif( $s == "longdashed" ) $s=4;
+ else JpGraphError::Raise(" Illegal string argument to SetLineStyle(): $s");
+ }
+ else JpGraphError::Raise(" Illegal argument to SetLineStyle $s");
+ $this->line_style=$s;
+ }
+
+ // Same as Line but take the line_style into account
+ function StyleLine($x1,$y1,$x2,$y2) {
+ switch( $this->line_style ) {
+ case 1:// Solid
+ $this->Line($x1,$y1,$x2,$y2);
+ break;
+ case 2: // Dotted
+ $this->DashedLine($x1,$y1,$x2,$y2,1,6);
+ break;
+ case 3: // Dashed
+ $this->DashedLine($x1,$y1,$x2,$y2,2,4);
+ break;
+ case 4: // Longdashes
+ $this->DashedLine($x1,$y1,$x2,$y2,8,6);
+ break;
+ default:
+ JpGraphError::Raise(" Unknown line style: $this->line_style ");
+ break;
+ }
+ }
+
+ function Line($x1,$y1,$x2,$y2) {
+
+ $x1 = round($x1);
+ $x2 = round($x2);
+ $y1 = round($y1);
+ $y2 = round($y2);
+
+ if( $this->line_weight==0 ) return;
+ if( $this->use_anti_aliasing ) {
+ $dx = $x2-$x1;
+ $dy = $y2-$y1;
+ // Vertical, Horizontal or 45 lines don't need anti-aliasing
+ if( $dx!=0 && $dy!=0 && $dx!=$dy ) {
+ $this->WuLine($x1,$y1,$x2,$y2);
+ return;
+ }
+ }
+ if( $this->line_weight==1 ) {
+ imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color);
+ }
+ elseif( $x1==$x2 ) { // Special case for vertical lines
+ imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color);
+ $w1=floor($this->line_weight/2);
+ $w2=floor(($this->line_weight-1)/2);
+ for($i=1; $i<=$w1; ++$i)
+ imageline($this->img,$x1+$i,$y1,$x2+$i,$y2,$this->current_color);
+ for($i=1; $i<=$w2; ++$i)
+ imageline($this->img,$x1-$i,$y1,$x2-$i,$y2,$this->current_color);
+ }
+ elseif( $y1==$y2 ) { // Special case for horizontal lines
+ imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color);
+ $w1=floor($this->line_weight/2);
+ $w2=floor(($this->line_weight-1)/2);
+ for($i=1; $i<=$w1; ++$i)
+ imageline($this->img,$x1,$y1+$i,$x2,$y2+$i,$this->current_color);
+ for($i=1; $i<=$w2; ++$i)
+ imageline($this->img,$x1,$y1-$i,$x2,$y2-$i,$this->current_color);
+ }
+ else { // General case with a line at an angle
+ $a = atan2($y1-$y2,$x2-$x1);
+ // Now establish some offsets from the center. This gets a little
+ // bit involved since we are dealing with integer functions and we
+ // want the apperance to be as smooth as possible and never be thicker
+ // then the specified width.
+
+ // We do the trig stuff to make sure that the endpoints of the line
+ // are perpendicular to the line itself.
+ $dx=(sin($a)*$this->line_weight/2);
+ $dy=(cos($a)*$this->line_weight/2);
+
+ $pnts = array($x2+$dx,$y2+$dy,$x2-$dx,$y2-$dy,$x1-$dx,$y1-$dy,$x1+$dx,$y1+$dy);
+ imagefilledpolygon($this->img,$pnts,count($pnts)/2,$this->current_color);
+ }
+ $this->lastx=$x2; $this->lasty=$y2;
+ }
+
+ function Polygon($p,$closed=FALSE) {
+ if( $this->line_weight==0 ) return;
+ $n=count($p);
+ $oldx = $p[0];
+ $oldy = $p[1];
+ for( $i=2; $i < $n; $i+=2 ) {
+ $this->Line($oldx,$oldy,$p[$i],$p[$i+1]);
+ $oldx = $p[$i];
+ $oldy = $p[$i+1];
+ }
+ if( $closed )
+ $this->Line($oldx,$oldy,$p[0],$p[1]);
+ }
+
+ function FilledPolygon($pts) {
+ $n=count($pts);
+ if( $n == 0 )
+ JpGraphError::Raise('Internal error: Calling Image::FilledPolygon() with no polygon');
+ for($i=0; $i < $n; ++$i)
+ $pts[$i] = round($pts[$i]);
+ imagefilledpolygon($this->img,$pts,count($pts)/2,$this->current_color);
+ }
+
+ function Rectangle($xl,$yu,$xr,$yl) {
+ $this->Polygon(array($xl,$yu,$xr,$yu,$xr,$yl,$xl,$yl,$xl,$yu));
+ }
+
+ function FilledRectangle($xl,$yu,$xr,$yl) {
+ $this->FilledPolygon(array($xl,$yu,$xr,$yu,$xr,$yl,$xl,$yl));
+ }
+
+ function FilledRectangle2($xl,$yu,$xr,$yl,$color1,$color2,$style=1) {
+ // Fill a rectangle with lines of two colors
+ if( $style===1 ) {
+ // Horizontal stripe
+ if( $yl < $yu ) {
+ $t = $yl; $yl=$yu; $yu=$t;
+ }
+ for( $y=$yu; $y <= $yl; ++$y) {
+ $this->SetColor($color1);
+ $this->Line($xl,$y,$xr,$y);
+ ++$y;
+ $this->SetColor($color2);
+ $this->Line($xl,$y,$xr,$y);
+ }
+ }
+ else {
+ if( $xl < $xl ) {
+ $t = $xl; $xl=$xr; $xr=$t;
+ }
+ for( $x=$xl; $x <= $xr; ++$x) {
+ $this->SetColor($color1);
+ $this->Line($x,$yu,$x,$yl);
+ ++$x;
+ $this->SetColor($color2);
+ $this->Line($x,$yu,$x,$yl);
+ }
+ }
+ }
+
+ function ShadowRectangle($xl,$yu,$xr,$yl,$fcolor=false,$shadow_width=3,$shadow_color=array(102,102,102)) {
+ // This is complicated by the fact that we must also handle the case where
+ // the reactangle has no fill color
+ $this->PushColor($shadow_color);
+ $this->FilledRectangle($xr-$shadow_width,$yu+$shadow_width,$xr,$yl-$shadow_width-1);
+ $this->FilledRectangle($xl+$shadow_width,$yl-$shadow_width,$xr,$yl);
+ //$this->FilledRectangle($xl+$shadow_width,$yu+$shadow_width,$xr,$yl);
+ $this->PopColor();
+ if( $fcolor==false )
+ $this->Rectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1);
+ else {
+ $this->PushColor($fcolor);
+ $this->FilledRectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1);
+ $this->PopColor();
+ $this->Rectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1);
+ }
+ }
+
+ function FilledRoundedRectangle($xt,$yt,$xr,$yl,$r=5) {
+ if( $r==0 ) {
+ $this->FilledRectangle($xt,$yt,$xr,$yl);
+ return;
+ }
+
+ // To avoid overlapping fillings (which will look strange
+ // when alphablending is enabled) we have no choice but
+ // to fill the five distinct areas one by one.
+
+ // Center square
+ $this->FilledRectangle($xt+$r,$yt+$r,$xr-$r,$yl-$r);
+ // Top band
+ $this->FilledRectangle($xt+$r,$yt,$xr-$r,$yt+$r-1);
+ // Bottom band
+ $this->FilledRectangle($xt+$r,$yl-$r+1,$xr-$r,$yl);
+ // Left band
+ $this->FilledRectangle($xt,$yt+$r+1,$xt+$r-1,$yl-$r);
+ // Right band
+ $this->FilledRectangle($xr-$r+1,$yt+$r,$xr,$yl-$r);
+
+ // Topleft & Topright arc
+ $this->FilledArc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
+ $this->FilledArc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
+
+ // Bottomleft & Bottom right arc
+ $this->FilledArc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
+ $this->FilledArc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
+
+ }
+
+ function RoundedRectangle($xt,$yt,$xr,$yl,$r=5) {
+
+ if( $r==0 ) {
+ $this->Rectangle($xt,$yt,$xr,$yl);
+ return;
+ }
+
+ // Top & Bottom line
+ $this->Line($xt+$r,$yt,$xr-$r,$yt);
+ $this->Line($xt+$r,$yl,$xr-$r,$yl);
+
+ // Left & Right line
+ $this->Line($xt,$yt+$r,$xt,$yl-$r);
+ $this->Line($xr,$yt+$r,$xr,$yl-$r);
+
+ // Topleft & Topright arc
+ $this->Arc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
+ $this->Arc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
+
+ // Bottomleft & Bottomright arc
+ $this->Arc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
+ $this->Arc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
+ }
+
+ function FilledBevel($x1,$y1,$x2,$y2,$depth=2,$color1='white@0.4',$color2='darkgray@0.4') {
+ $this->FilledRectangle($x1,$y1,$x2,$y2);
+ $this->Bevel($x1,$y1,$x2,$y2,$depth,$color1,$color2);
+ }
+
+ function Bevel($x1,$y1,$x2,$y2,$depth=2,$color1='white@0.4',$color2='black@0.5') {
+ $this->PushColor($color1);
+ for( $i=0; $i < $depth; ++$i ) {
+ $this->Line($x1+$i,$y1+$i,$x1+$i,$y2-$i);
+ $this->Line($x1+$i,$y1+$i,$x2-$i,$y1+$i);
+ }
+ $this->PopColor();
+
+ $this->PushColor($color2);
+ for( $i=0; $i < $depth; ++$i ) {
+ $this->Line($x1+$i,$y2-$i,$x2-$i,$y2-$i);
+ $this->Line($x2-$i,$y1+$i,$x2-$i,$y2-$i-1);
+ }
+ $this->PopColor();
+ }
+
+ function StyleLineTo($x,$y) {
+ $this->StyleLine($this->lastx,$this->lasty,$x,$y);
+ $this->lastx=$x;
+ $this->lasty=$y;
+ }
+
+ function LineTo($x,$y) {
+ $this->Line($this->lastx,$this->lasty,$x,$y);
+ $this->lastx=$x;
+ $this->lasty=$y;
+ }
+
+ function Point($x,$y) {
+ imagesetpixel($this->img,round($x),round($y),$this->current_color);
+ }
+
+ function Fill($x,$y) {
+ imagefill($this->img,round($x),round($y),$this->current_color);
+ }
+
+ function FillToBorder($x,$y,$aBordColor) {
+ $bc = $this->rgb->allocate($aBordColor);
+ if( $bc == -1 ) {
+ JpGraphError::Raise('Image::FillToBorder : Can not allocate more colors');
+ exit();
+ }
+ imagefilltoborder($this->img,round($x),round($y),$bc,$this->current_color);
+ }
+
+ function DashedLine($x1,$y1,$x2,$y2,$dash_length=1,$dash_space=4) {
+ // Code based on, but not identical to, work by Ariel Garza and James Pine
+ $line_length = ceil (sqrt(pow(($x2 - $x1),2) + pow(($y2 - $y1),2)) );
+ $dx = ($line_length) ? ($x2 - $x1) / $line_length : 0;
+ $dy = ($line_length) ? ($y2 - $y1) / $line_length : 0;
+ $lastx = $x1; $lasty = $y1;
+ $xmax = max($x1,$x2);
+ $xmin = min($x1,$x2);
+ $ymax = max($y1,$y2);
+ $ymin = min($y1,$y2);
+ for ($i = 0; $i < $line_length; $i += ($dash_length + $dash_space)) {
+ $x = ($dash_length * $dx) + $lastx;
+ $y = ($dash_length * $dy) + $lasty;
+
+ // The last section might overshoot so we must take a computational hit
+ // and check this.
+ if( $x>$xmax ) $x=$xmax;
+ if( $y>$ymax ) $y=$ymax;
+
+ if( $x<$xmin ) $x=$xmin;
+ if( $y<$ymin ) $y=$ymin;
+
+ $this->Line($lastx,$lasty,$x,$y);
+ $lastx = $x + ($dash_space * $dx);
+ $lasty = $y + ($dash_space * $dy);
+ }
+ }
+
+ function SetExpired($aFlg=true) {
+ $this->expired = $aFlg;
+ }
+
+ // Generate image header
+ function Headers() {
+ if ($this->expired) {
+ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
+ header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
+ header("Cache-Control: no-cache, must-revalidate");
+ header("Pragma: no-cache");
+ }
+ header("Content-type: image/$this->img_format");
+ }
+
+ // Adjust image quality for formats that allow this
+ function SetQuality($q) {
+ $this->quality = $q;
+ }
+
+ // Stream image to browser or to file
+ function Stream($aFile="") {
+ $func="image".$this->img_format;
+ if( $this->img_format=="jpeg" && $this->quality != null ) {
+ $res = @$func($this->img,$aFile,$this->quality);
+ }
+ else {
+ if( $aFile != "" ) {
+ $res = @$func($this->img,$aFile);
+ }
+ else
+ $res = @$func($this->img);
+ }
+ if( !$res )
+ JpGraphError::Raise("Can't create or stream image to file $aFile Check that PHP has enough permission to write a file to the current directory.");
+ }
+
+ // Clear resource tide up by image
+ function Destroy() {
+ imagedestroy($this->img);
+ }
+
+ // Specify image format. Note depending on your installation
+ // of PHP not all formats may be supported.
+ function SetImgFormat($aFormat) {
+ $aFormat = strtolower($aFormat);
+ $tst = true;
+ $supported = imagetypes();
+ if( $aFormat=="auto" ) {
+ if( $supported & IMG_PNG )
+ $this->img_format="png";
+ elseif( $supported & IMG_JPG )
+ $this->img_format="jpeg";
+ elseif( $supported & IMG_GIF )
+ $this->img_format="gif";
+ else
+ JpGraphError::Raise(" Your PHP (and GD-lib) installation does not appear to support any known graphic formats.".
+ "You need to first make sure GD is compiled as a module to PHP. If you also want to use JPEG images".
+ "you must get the JPEG library. Please see the PHP docs for details.");
+
+ return true;
+ }
+ else {
+ if( $aFormat=="jpeg" || $aFormat=="png" || $aFormat=="gif" ) {
+ if( $aFormat=="jpeg" && !($supported & IMG_JPG) )
+ $tst=false;
+ elseif( $aFormat=="png" && !($supported & IMG_PNG) )
+ $tst=false;
+ elseif( $aFormat=="gif" && !($supported & IMG_GIF) )
+ $tst=false;
+ else {
+ $this->img_format=$aFormat;
+ return true;
+ }
+ }
+ else
+ $tst=false;
+ if( !$tst )
+ JpGraphError::Raise(" Your PHP installation does not support the chosen graphic format: $aFormat");
+ }
+ }
+} // CLASS
+
+//===================================================
+// CLASS RotImage
+// Description: Exactly as Image but draws the image at
+// a specified angle around a specified rotation point.
+//===================================================
+class RotImage extends Image {
+ var $m=array();
+ var $a=0;
+ var $dx=0,$dy=0,$transx=0,$transy=0;
+
+ function RotImage($aWidth,$aHeight,$a=0,$aFormat=DEFAULT_GFORMAT) {
+ $this->Image($aWidth,$aHeight,$aFormat);
+ $this->dx=$this->left_margin+$this->plotwidth/2;
+ $this->dy=$this->top_margin+$this->plotheight/2;
+ $this->SetAngle($a);
+ }
+
+ function SetCenter($dx,$dy) {
+ $old_dx = $this->dx;
+ $old_dy = $this->dy;
+ $this->dx=$dx;
+ $this->dy=$dy;
+ $this->SetAngle($this->a);
+ return array($old_dx,$old_dy);
+ }
+
+ function SetTranslation($dx,$dy) {
+ $old = array($this->transx,$this->transy);
+ $this->transx = $dx;
+ $this->transy = $dy;
+ return $old;
+ }
+
+ function UpdateRotMatrice() {
+ $a = $this->a;
+ $a *= M_PI/180;
+ $sa=sin($a); $ca=cos($a);
+ // Create the rotation matrix
+ $this->m[0][0] = $ca;
+ $this->m[0][1] = -$sa;
+ $this->m[0][2] = $this->dx*(1-$ca) + $sa*$this->dy ;
+ $this->m[1][0] = $sa;
+ $this->m[1][1] = $ca;
+ $this->m[1][2] = $this->dy*(1-$ca) - $sa*$this->dx ;
+ }
+
+ function SetAngle($a) {
+ $tmp = $this->a;
+ $this->a = $a;
+ $this->UpdateRotMatrice();
+ return $tmp;
+ }
+
+ function Circle($xc,$yc,$r) {
+ // Circle get's rotated through the Arc() call
+ // made in the parent class
+ parent::Circle($xc,$yc,$r);
+ }
+
+ function FilledCircle($xc,$yc,$r) {
+ // If we use GD1 then Image::FilledCircle will use a
+ // call to Arc so it will get rotated through the Arc
+ // call.
+ if( $GLOBALS['gd2'] ) {
+ list($xc,$yc) = $this->Rotate($xc,$yc);
+ }
+ parent::FilledCircle($xc,$yc,$r);
+ }
+
+
+ function Arc($xc,$yc,$w,$h,$s,$e) {
+ list($xc,$yc) = $this->Rotate($xc,$yc);
+ $s += $this->a;
+ $e += $this->a;
+ parent::Arc($xc,$yc,$w,$h,$s,$e);
+ }
+
+ function FilledArc($xc,$yc,$w,$h,$s,$e) {
+ list($xc,$yc) = $this->Rotate($xc,$yc);
+ $s += $this->a;
+ $e += $this->a;
+ parent::FilledArc($xc,$yc,$w,$h,$s,$e);
+ }
+
+ function SetMargin($lm,$rm,$tm,$bm) {
+ parent::SetMargin($lm,$rm,$tm,$bm);
+ $this->dx=$this->left_margin+$this->plotwidth/2;
+ $this->dy=$this->top_margin+$this->plotheight/2;
+ $this->UpdateRotMatrice();
+ }
+
+ function Rotate($x,$y) {
+ // Optimization. Ignore rotation if Angle==0 || ANgle==360
+ if( $this->a == 0 || $this->a == 360 ) {
+ return array($x + $this->transx, $y + $this->transy );
+ }
+ else {
+ $x1=round($this->m[0][0]*$x + $this->m[0][1]*$y,1) + $this->m[0][2] + $this->transx;
+ $y1=round($this->m[1][0]*$x + $this->m[1][1]*$y,1) + $this->m[1][2] + $this->transy;
+ return array($x1,$y1);
+ }
+ }
+
+ function ArrRotate($pnts) {
+ for($i=0; $i < count($pnts)-1; $i+=2)
+ list($pnts[$i],$pnts[$i+1]) = $this->Rotate($pnts[$i],$pnts[$i+1]);
+ return $pnts;
+ }
+
+ function Line($x1,$y1,$x2,$y2) {
+ list($x1,$y1) = $this->Rotate($x1,$y1);
+ list($x2,$y2) = $this->Rotate($x2,$y2);
+ parent::Line($x1,$y1,$x2,$y2);
+ }
+
+ function Rectangle($x1,$y1,$x2,$y2) {
+ // Rectangle uses Line() so it will be rotated through that call
+ parent::Rectangle($x1,$y1,$x2,$y2);
+ }
+
+ function FilledRectangle($x1,$y1,$x2,$y2) {
+ if( $y1==$y2 || $x1==$x2 )
+ $this->Line($x1,$y1,$x2,$y2);
+ else
+ $this->FilledPolygon(array($x1,$y1,$x2,$y1,$x2,$y2,$x1,$y2));
+ }
+
+ function Polygon($pnts,$closed=FALSE) {
+ //Polygon uses Line() so it will be rotated through that call
+ parent::Polygon($pnts,$closed);
+ }
+
+ function FilledPolygon($pnts) {
+ parent::FilledPolygon($this->ArrRotate($pnts));
+ }
+
+ function Point($x,$y) {
+ list($xp,$yp) = $this->Rotate($x,$y);
+ parent::Point($xp,$yp);
+ }
+
+ function StrokeText($x,$y,$txt,$dir=0,$paragraph_align="left",$debug=false) {
+ list($xp,$yp) = $this->Rotate($x,$y);
+ parent::StrokeText($xp,$yp,$txt,$dir,$paragraph_align,$debug);
+ }
+}
+
+//===================================================
+// CLASS ImgStreamCache
+// Description: Handle caching of graphs to files
+//===================================================
+class ImgStreamCache {
+ var $cache_dir;
+ var $img=null;
+ var $timeout=0; // Infinite timeout
+ //---------------
+ // CONSTRUCTOR
+ function ImgStreamCache(&$aImg, $aCacheDir=CACHE_DIR) {
+ $this->img = &$aImg;
+ $this->cache_dir = $aCacheDir;
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ // Specify a timeout (in minutes) for the file. If the file is older then the
+ // timeout value it will be overwritten with a newer version.
+ // If timeout is set to 0 this is the same as infinite large timeout and if
+ // timeout is set to -1 this is the same as infinite small timeout
+ function SetTimeout($aTimeout) {
+ $this->timeout=$aTimeout;
+ }
+
+ // Output image to browser and also write it to the cache
+ function PutAndStream(&$aImage,$aCacheFileName,$aInline,$aStrokeFileName) {
+ // Some debugging code to brand the image with numbe of colors
+ // used
+ GLOBAL $gJpgBrandTiming;
+
+ if( $gJpgBrandTiming ) {
+ global $tim;
+ $t=$tim->Pop()/1000.0;
+ $c=$aImage->SetColor("black");
+ $t=sprintf(BRAND_TIME_FORMAT,round($t,3));
+ imagestring($this->img->img,2,5,$this->img->height-20,$t,$c);
+ }
+
+ // Check if we should stroke the image to an arbitrary file
+ if( $aStrokeFileName!="" ) {
+ if( $aStrokeFileName == "auto" )
+ $aStrokeFileName = GenImgName();
+ if( file_exists($aStrokeFileName) ) {
+ // Delete the old file
+ if( !@unlink($aStrokeFileName) )
+ JpGraphError::Raise(" Can't delete cached image $aStrokeFileName. Permission problem?");
+ }
+ $aImage->Stream($aStrokeFileName);
+ return;
+ }
+
+ if( $aCacheFileName != "" && USE_CACHE) {
+
+ $aCacheFileName = $this->cache_dir . $aCacheFileName;
+ if( file_exists($aCacheFileName) ) {
+ if( !$aInline ) {
+ // If we are generating image off-line (just writing to the cache)
+ // and the file exists and is still valid (no timeout)
+ // then do nothing, just return.
+ $diff=time()-filemtime($aCacheFileName);
+ if( $diff < 0 )
+ JpGraphError::Raise(" Cached imagefile ($aCacheFileName) has file date in the future!!");
+ if( $this->timeout>0 && ($diff <= $this->timeout*60) )
+ return;
+ }
+ if( !@unlink($aCacheFileName) )
+ JpGraphError::Raise(" Can't delete cached image $aStrokeFileName. Permission problem?");
+ $aImage->Stream($aCacheFileName);
+ }
+ else {
+ $this->MakeDirs(dirname($aCacheFileName));
+ if( !is_writeable(dirname($aCacheFileName)) ) {
+ JpGraphError::Raise('PHP has not enough permissions to write to the cache file '.$aCacheFileName.'. Please make sure that the user running PHP has write permission for this file if you wan to use the cache system with JpGraph.');
+ }
+ $aImage->Stream($aCacheFileName);
+ }
+
+ $res=true;
+ // Set group to specified
+ if( CACHE_FILE_GROUP != "" )
+ $res = @chgrp($aCacheFileName,CACHE_FILE_GROUP);
+ if( CACHE_FILE_MOD != "" )
+ $res = @chmod($aCacheFileName,CACHE_FILE_MOD);
+ if( !$res )
+ JpGraphError::Raise(" Can't set permission for cached image $aStrokeFileName. Permission problem?");
+
+ $aImage->Destroy();
+ if( $aInline ) {
+ if ($fh = @fopen($aCacheFileName, "rb") ) {
+ $this->img->Headers();
+ fpassthru($fh);
+ return;
+ }
+ else
+ JpGraphError::Raise(" Cant open file from cache [$aFile]");
+ }
+ }
+ elseif( $aInline ) {
+ $this->img->Headers();
+ $aImage->Stream();
+ return;
+ }
+ }
+
+ // Check if a given image is in cache and in that case
+ // pass it directly on to web browser. Return false if the
+ // image file doesn't exist or exists but is to old
+ function GetAndStream($aCacheFileName) {
+ $aCacheFileName = $this->cache_dir.$aCacheFileName;
+ if ( USE_CACHE && file_exists($aCacheFileName) && $this->timeout>=0 ) {
+ $diff=time()-filemtime($aCacheFileName);
+ if( $this->timeout>0 && ($diff > $this->timeout*60) ) {
+ return false;
+ }
+ else {
+ if ($fh = @fopen($aCacheFileName, "rb")) {
+ $this->img->Headers();
+ fpassthru($fh);
+ return true;
+ }
+ else
+ JpGraphError::Raise(" Can't open cached image \"$aCacheFileName\" for reading.");
+ }
+ }
+ return false;
+ }
+
+ //---------------
+ // PRIVATE METHODS
+ // Create all necessary directories in a path
+ function MakeDirs($aFile) {
+ $dirs = array();
+ while ( !(file_exists($aFile)) ) {
+ $dirs[] = $aFile;
+ $aFile = dirname($aFile);
+ }
+ for ($i = sizeof($dirs)-1; $i>=0; $i--) {
+ if(! @mkdir($dirs[$i],0777) )
+ JpGraphError::Raise(" Can't create directory $aFile. Make sure PHP has write permission to this directory.");
+ // We also specify mode here after we have changed group.
+ // This is necessary if Apache user doesn't belong the
+ // default group and hence can't specify group permission
+ // in the previous mkdir() call
+ if( CACHE_FILE_GROUP != "" ) {
+ $res=true;
+ $res =@chgrp($dirs[$i],CACHE_FILE_GROUP);
+ $res &= @chmod($dirs[$i],0777);
+ if( !$res )
+ JpGraphError::Raise(" Can't set permissions for $aFile. Permission problems?");
+ }
+ }
+ return true;
+ }
+} // CLASS Cache
+
+//===================================================
+// CLASS Legend
+// Description: Responsible for drawing the box containing
+// all the legend text for the graph
+//===================================================
+DEFINE('_DEFAULT_LPM_SIZE',8);
+class Legend {
+ var $color=array(0,0,0); // Default fram color
+ var $fill_color=array(235,235,235); // Default fill color
+ var $shadow=true; // Shadow around legend "box"
+ var $shadow_color='gray';
+ var $txtcol=array();
+ var $mark_abs_size=_DEFAULT_LPM_SIZE;
+ var $xmargin=10,$ymargin=6,$shadow_width=2;
+ var $xpos=0.05, $ypos=0.15, $xabspos=-1, $yabspos=-1;
+ var $halign="right", $valign="top";
+ var $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12;
+ var $font_color='black';
+ var $hide=false,$layout_n=1;
+ var $weight=1,$frameweight=1;
+ var $csimareas='';
+ var $reverse = false ;
+//---------------
+// CONSTRUCTOR
+ function Legend() {
+ // Empty
+ }
+//---------------
+// PUBLIC METHODS
+ function Hide($aHide=true) {
+ $this->hide=$aHide;
+ }
+
+ function SetShadow($aShow='gray',$aWidth=2) {
+ if( is_string($aShow) ) {
+ $this->shadow_color = $aShow;
+ $this->shadow=true;
+ }
+ else
+ $this->shadow=$aShow;
+ $this->shadow_width=$aWidth;
+ }
+
+ function SetMarkAbsSize($aSize) {
+ $this->mark_abs_size = $aSize ;
+ }
+
+ function SetLineWeight($aWeight) {
+ $this->weight = $aWeight;
+ }
+
+ function SetFrameWeight($aWeight) {
+ $this->frameweight = $aWeight;
+ }
+
+ function SetLayout($aDirection=LEGEND_VERT) {
+ $this->layout_n = $aDirection==LEGEND_VERT ? 1 : 99 ;
+ }
+
+ function SetColumns($aCols) {
+ $this->layout_n = $aCols ;
+ }
+
+ function SetLineSpacing($aSpacing) {
+ $this->ymargin = $aSpacing ;
+ }
+
+ function SetReverse($f=true) {
+ $this->reverse = $f ;
+ }
+
+ // Set color on frame around box
+ function SetColor($aFontColor,$aColor='black') {
+ $this->font_color=$aFontColor;
+ $this->color=$aColor;
+ }
+
+ function SetFont($aFamily,$aStyle=FS_NORMAL,$aSize=10) {
+ $this->font_family = $aFamily;
+ $this->font_style = $aStyle;
+ $this->font_size = $aSize;
+ }
+
+ function SetPos($aX,$aY,$aHAlign="right",$aVAlign="top") {
+ $this->Pos($aX,$aY,$aHAlign,$aVAlign);
+ }
+
+ function SetAbsPos($aX,$aY,$aHAlign="right",$aVAlign="top") {
+ $this->xabspos=$aX;
+ $this->yabspos=$aY;
+ $this->halign=$aHAlign;
+ $this->valign=$aVAlign;
+ }
+
+
+ function Pos($aX,$aY,$aHAlign="right",$aVAlign="top") {
+ if( !($aX<1 && $aY<1) )
+ JpGraphError::Raise(" Position for legend must be given as percentage in range 0-1");
+ $this->xpos=$aX;
+ $this->ypos=$aY;
+ $this->halign=$aHAlign;
+ $this->valign=$aVAlign;
+ }
+
+ function SetFillColor($aColor) {
+ $this->fill_color=$aColor;
+ }
+
+ function Add($aTxt,$aColor,$aPlotmark="",$aLinestyle=0,$csimtarget="",$csimalt="") {
+ $this->txtcol[]=array($aTxt,$aColor,$aPlotmark,$aLinestyle,$csimtarget,$csimalt);
+ }
+
+ function GetCSIMAreas() {
+ return $this->csimareas;
+ }
+
+ function Stroke(&$aImg) {
+ if( $this->hide ) return;
+
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+
+ if( $this->reverse ) {
+ $this->txtcol = array_reverse($this->txtcol);
+ }
+
+ $n=count($this->txtcol);
+ if( $n == 0 ) return;
+
+ // Find out the max width and height of each column to be able
+ // to size the legend box.
+ $numcolumns = ($n > $this->layout_n ? $this->layout_n : $n);
+ for( $i=0; $i < $numcolumns; ++$i ) {
+ $colwidth[$i] = $aImg->GetTextWidth($this->txtcol[$i][0]) +
+ 2*$this->xmargin + 2*$this->mark_abs_size;
+ $colheight[$i] = 0;
+ }
+
+ // Find our maximum height in each row
+ $rows = -1 ;
+ for( $i=0; $i < $n; ++$i ) {
+ $h = max($this->mark_abs_size,$aImg->GetTextHeight($this->txtcol[$i][0]))+$this->ymargin;
+ if( $i % $numcolumns == 0 ) {
+ $rows++;
+ $rowheight[$rows] = 0;
+ }
+ $rowheight[$rows] = max($rowheight[$rows],$h);
+ }
+
+ $abs_height = 0;
+ for( $i=0; $i <= $rows; ++$i ) {
+ $abs_height += $rowheight[$i] ;
+ }
+
+ // Make sure that the height is at least as high as mark size + ymargin
+ $abs_height = max($abs_height,$this->mark_abs_size+$this->ymargin);
+ $abs_height += 2*$this->ymargin;
+
+ // Find out the maximum width in each column
+ for( $i=$numcolumns; $i < $n; ++$i ) {
+ $colwidth[$i % $numcolumns] = max(
+ $aImg->GetTextWidth($this->txtcol[$i][0])+2*$this->xmargin+2*$this->mark_abs_size,
+ $colwidth[$i % $numcolumns]);
+ }
+
+ // Get the total width
+ $mtw = 0;
+ for( $i=0; $i < $numcolumns; ++$i ) {
+ $mtw += $colwidth[$i] + $this->xmargin;
+ }
+
+ // Find out maximum width we need for legend box
+ $abs_width = $mtw+$this->xmargin;
+
+ if( $this->xabspos === -1 && $this->yabspos === -1 ) {
+ $this->xabspos = $this->xpos*$aImg->width ;
+ $this->yabspos = $this->ypos*$aImg->height ;
+ }
+
+ // Positioning of the legend box
+ if( $this->halign=="left" )
+ $xp = $this->xabspos;
+ elseif( $this->halign=="center" )
+ $xp = $this->xabspos - $abs_width/2;
+ else
+ $xp = $aImg->width - $this->xabspos - $abs_width;
+
+ $yp=$this->yabspos;
+ if( $this->valign=="center" )
+ $yp-=$abs_height/2;
+ elseif( $this->valign=="bottom" )
+ $yp-=$abs_height;
+
+ // Stroke legend box
+ $aImg->SetColor($this->color);
+ $aImg->SetLineWeight($this->frameweight);
+
+ if( $this->shadow )
+ $aImg->ShadowRectangle($xp,$yp,$xp+$abs_width+$this->shadow_width,
+ $yp+$abs_height+$this->shadow_width,
+ $this->fill_color,$this->shadow_width,$this->shadow_color);
+ else {
+ $aImg->SetColor($this->fill_color);
+ $aImg->FilledRectangle($xp,$yp,$xp+$abs_width,$yp+$abs_height);
+ $aImg->SetColor($this->color);
+ $aImg->Rectangle($xp,$yp,$xp+$abs_width,$yp+$abs_height);
+ }
+
+ // x1,y1 is the position for the legend mark
+ $aImg->SetLineWeight($this->weight);
+ $x1=$xp+$this->mark_abs_size+8;
+ $y1=$yp + $this->mark_abs_size/2 + $this->ymargin/2;
+
+ $f2 = round($aImg->GetTextHeight('X')/2);
+
+ $grad = new Gradient($aImg);
+
+ // Now stroke each legend in turn
+ $i = 1 ; $row = 0;
+ foreach($this->txtcol as $p) {
+ $x1 = round($x1); $y1=round($y1);
+ if ( $p[2] != "" && $p[2]->GetType() > -1 ) {
+ // Make a plot mark legend
+ $aImg->SetColor($p[1]);
+ if( $p[3] > 0 ) {
+ $aImg->SetLineStyle($p[3]);
+ $aImg->StyleLine($x1-$this->mark_abs_size,$y1+$f2,$x1+$this->mark_abs_size,$y1+$f2);
+ }
+ // Stroke a mark with the standard size
+ // (As long as it is not an image mark )
+ if( $p[2]->GetType() != MARK_IMG ) {
+ $p[2]->iFormatCallback = '';
+
+ // Since size for circles is specified as the radius
+ // this means that we must half the size to make the total
+ // width behave as the other marks
+
+ if( $p[2]->GetType() == MARK_FILLEDCIRCLE ||
+ $p[2]->GetType() == MARK_CIRCLE ) {
+ $p[2]->SetSize($this->mark_abs_size/2);
+ $p[2]->Stroke($aImg,$x1,$y1+$f2);
+
+ }
+ else {
+ $p[2]->SetSize($this->mark_abs_size);
+ $p[2]->Stroke($aImg,$x1,$y1+$f2);
+ }
+ }
+ }
+ elseif ( $p[2] != "" && (is_string($p[3]) || $p[3]>0 ) ) {
+ // Draw a styled line
+ $aImg->SetColor($p[1]);
+ $aImg->SetLineStyle($p[3]);
+ $aImg->StyleLine($x1-1,$y1+$f2,$x1+$this->mark_abs_size,$y1+$f2);
+ $aImg->StyleLine($x1-1,$y1+$f2+1,$x1+$this->mark_abs_size,$y1+$f2+1);
+ }
+ else {
+ // Draw a colored box
+ $color = $p[1] ;
+ $ym = round($y1 + $f2 - $this->mark_abs_size/2);
+ if( is_array($color) && count($color)==3 ) {
+ // The client want a gradient color
+ $grad->FilledRectangle($x1,$ym,
+ $x1+$this->mark_abs_size,$ym+$this->mark_abs_size,
+ $color[0],$color[1],$color[2]);
+ }
+ else {
+ $aImg->SetColor($p[1]);
+ $aImg->FilledRectangle($x1,$ym,$x1+$this->mark_abs_size,$ym+$this->mark_abs_size);
+ }
+ $aImg->SetColor($this->color);
+ $aImg->SetLineWeight($this->weight);
+ $aImg->Rectangle($x1,$ym,$x1+$this->mark_abs_size,$ym+$this->mark_abs_size);
+ }
+ $aImg->SetColor($this->font_color);
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $aImg->SetTextAlign("left","top");
+ $aImg->StrokeText(round($x1+$this->mark_abs_size+$this->xmargin),$y1,$p[0]);
+
+ // Add CSIM for Legend if defined
+ if( $p[4] != "" ) {
+ $xe = $x1 + $this->xmargin+$this->mark_abs_size+$aImg->GetTextWidth($p[0]);
+ $ye = $y1 + max($this->mark_abs_size,$aImg->GetTextHeight($p[0]));
+ $coords = "$x1,$y1,$xe,$y1,$xe,$ye,$x1,$ye";
+ if( ! empty($p[4]) ) {
+ $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$p[4]."\"";
+ if( !empty($p[5]) ) {
+ $tmp=sprintf($p[5],$p[0]);
+ $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimareas .= ">\n";
+ }
+ }
+ if( $i >= $this->layout_n ) {
+ $x1 = $xp+$this->mark_abs_size+8;
+ //$y1 += max($aImg->GetTextHeight($p[0]),$this->mark_abs_size)+$this->ymargin;
+ $y1 += $rowheight[$row++];
+ $i = 1;
+ }
+ else {
+ $x1 += $colwidth[($i-1) % $numcolumns] + $this->xmargin;
+ ++$i;
+ }
+ }
+ }
+} // Class
+
+
+//===================================================
+// CLASS DisplayValue
+// Description: Used to print data values at data points
+//===================================================
+class DisplayValue {
+ var $show=false,$format="%.1f",$negformat="";
+ var $iFormCallback='';
+ var $angle=0;
+ var $ff=FF_FONT1,$fs=FS_NORMAL,$fsize=10;
+ var $color="navy",$negcolor="";
+ var $margin=5,$valign="",$halign="center";
+ var $iHideZero=false;
+
+ function Show($aFlag=true) {
+ $this->show=$aFlag;
+ }
+
+ function SetColor($aColor,$aNegcolor="") {
+ $this->color = $aColor;
+ $this->negcolor = $aNegcolor;
+ }
+
+ function SetFont($aFontFamily,$aFontStyle=FS_NORMAL,$aFontSize=10) {
+ $this->ff=$aFontFamily;
+ $this->fs=$aFontStyle;
+ $this->fsize=$aFontSize;
+ }
+
+ function SetMargin($aMargin) {
+ $this->margin = $aMargin;
+ }
+
+ function SetAngle($aAngle) {
+ $this->angle = $aAngle;
+ }
+
+ function SetAlign($aHAlign,$aVAlign='') {
+ $this->halign = $aHAlign;
+ $this->valign = $aVAlign;
+ }
+
+ function SetFormat($aFormat,$aNegFormat="") {
+ $this->format= $aFormat;
+ $this->negformat= $aNegFormat;
+ }
+
+ function SetFormatCallback($aFunc) {
+ $this->iFormCallback = $aFunc;
+ }
+
+ function HideZero($aFlag=true) {
+ $this->iHideZero=$aFlag;
+ }
+
+ function Stroke($img,$aVal,$x,$y) {
+
+ if( $this->show )
+ {
+ if( $this->negformat=="" ) $this->negformat=$this->format;
+ if( $this->negcolor=="" ) $this->negcolor=$this->color;
+
+ if( $aVal===NULL || (is_string($aVal) && ($aVal=="" || $aVal=="-" || $aVal=="x" ) ) )
+ return;
+
+ if( is_numeric($aVal) && $aVal==0 && $this->iHideZero ) {
+ return;
+ }
+
+ // Since the value is used in different cirumstances we need to check what
+ // kind of formatting we shall use. For example, to display values in a line
+ // graph we simply display the formatted value, but in the case where the user
+ // has already specified a text string we don't fo anything.
+ if( $this->iFormCallback != '' ) {
+ $f = $this->iFormCallback;
+ $sval = call_user_func($f,$aVal);
+ }
+ elseif( is_numeric($aVal) ) {
+ if( $aVal >= 0 )
+ $sval=sprintf($this->format,$aVal);
+ else
+ $sval=sprintf($this->negformat,$aVal);
+ }
+ else
+ $sval=$aVal;
+
+ $y = $y-sign($aVal)*$this->margin;
+
+ $txt = new Text($sval,$x,$y);
+ $txt->SetFont($this->ff,$this->fs,$this->fsize);
+ if( $this->valign == "" ) {
+ if( $aVal >= 0 )
+ $valign = "bottom";
+ else
+ $valign = "top";
+ }
+ else
+ $valign = $this->valign;
+ $txt->Align($this->halign,$valign);
+
+ $txt->SetOrientation($this->angle);
+ if( $aVal > 0 )
+ $txt->SetColor($this->color);
+ else
+ $txt->SetColor($this->negcolor);
+ $txt->Stroke($img);
+ }
+ }
+}
+
+
+//===================================================
+// CLASS Plot
+// Description: Abstract base class for all concrete plot classes
+//===================================================
+class Plot {
+ var $line_weight=1;
+ var $coords=array();
+ var $legend='',$hidelegend=false;
+ var $csimtargets=array(); // Array of targets for CSIM
+ var $csimareas=""; // Resultant CSIM area tags
+ var $csimalts=null; // ALT:s for corresponding target
+ var $color="black";
+ var $numpoints=0;
+ var $weight=1;
+ var $value;
+ var $center=false;
+ var $legendcsimtarget='';
+ var $legendcsimalt='';
+//---------------
+// CONSTRUCTOR
+ function Plot(&$aDatay,$aDatax=false) {
+ $this->numpoints = count($aDatay);
+ if( $this->numpoints==0 )
+ JpGraphError::Raise(" Empty data array specified for plot. Must have at least one data point.");
+ $this->coords[0]=$aDatay;
+ if( is_array($aDatax) )
+ $this->coords[1]=$aDatax;
+ $this->value = new DisplayValue();
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ // Stroke the plot
+ // "virtual" function which must be implemented by
+ // the subclasses
+ function Stroke(&$aImg,&$aXScale,&$aYScale) {
+ JpGraphError::Raise("JpGraph: Stroke() must be implemented by concrete subclass to class Plot");
+ }
+
+ function HideLegend($f=true) {
+ $this->hidelegend = $f;
+ }
+
+ function DoLegend(&$graph) {
+ if( !$this->hidelegend )
+ $this->Legend($graph);
+ }
+
+ function StrokeDataValue($img,$aVal,$x,$y) {
+ $this->value->Stroke($img,$aVal,$x,$y);
+ }
+
+ // Set href targets for CSIM
+ function SetCSIMTargets($aTargets,$aAlts=null) {
+ $this->csimtargets=$aTargets;
+ $this->csimalts=$aAlts;
+ }
+
+ // Get all created areas
+ function GetCSIMareas() {
+ return $this->csimareas;
+ }
+
+ // "Virtual" function which gets called before any scale
+ // or axis are stroked used to do any plot specific adjustment
+ function PreStrokeAdjust(&$aGraph) {
+ if( substr($aGraph->axtype,0,4) == "text" && (isset($this->coords[1])) )
+ JpGraphError::Raise("JpGraph: You can't use a text X-scale with specified X-coords. Use a \"int\" or \"lin\" scale instead.");
+ return true;
+ }
+
+ // Get minimum values in plot
+ function Min() {
+ if( isset($this->coords[1]) )
+ $x=$this->coords[1];
+ else
+ $x="";
+ if( $x != "" && count($x) > 0 )
+ $xm=min($x);
+ else
+ $xm=0;
+ $y=$this->coords[0];
+ if( count($y) > 0 ) {
+ $ym = $y[0];
+ $cnt = count($y);
+ $i=0;
+ while( $i<$cnt && !is_numeric($ym=$y[$i]) )
+ $i++;
+ while( $i < $cnt) {
+ if( is_numeric($y[$i]) )
+ $ym=min($ym,$y[$i]);
+ ++$i;
+ }
+ }
+ else
+ $ym="";
+ return array($xm,$ym);
+ }
+
+ // Get maximum value in plot
+ function Max() {
+ if( isset($this->coords[1]) )
+ $x=$this->coords[1];
+ else
+ $x="";
+
+ if( $x!="" && count($x) > 0 )
+ $xm=max($x);
+ else {
+ //$xm=count($this->coords[0])-1; // We count from 0..(n-1)
+ $xm = $this->numpoints-1;
+ }
+ $y=$this->coords[0];
+ if( count($y) > 0 ) {
+ if( !isset($y[0]) ) {
+ $y[0] = 0;
+// Change in 1.5.1 Don't treat this as an error any more. Just silently concert to 0
+// JpGraphError::Raise(" You have not specified a y[0] value!!");
+ }
+ $cnt = count($y);
+ $i=0;
+ while( $i<$cnt && !is_numeric($ym=$y[$i]) )
+ $i++;
+ while( $i < $cnt ) {
+ if( is_numeric($y[$i]) ) $ym=max($ym,$y[$i]);
+ ++$i;
+ }
+ }
+ else
+ $ym="";
+ return array($xm,$ym);
+ }
+
+ function SetColor($aColor) {
+ $this->color=$aColor;
+ }
+
+ function SetLegend($aLegend,$aCSIM="",$aCSIMAlt="") {
+ $this->legend = $aLegend;
+ $this->legendcsimtarget = $aCSIM;
+ $this->legendcsimalt = $aCSIMAlt;
+ }
+
+ function SetWeight($aWeight) {
+ $this->weight=$aWeight;
+ }
+
+ function SetLineWeight($aWeight=1) {
+ $this->line_weight=$aWeight;
+ }
+
+ function SetCenter($aCenter=true) {
+ $this->center = $aCenter;
+ }
+
+ // This method gets called by Graph class to plot anything that should go
+ // into the margin after the margin color has been set.
+ function StrokeMargin(&$aImg) {
+ return true;
+ }
+
+ // Framework function the chance for each plot class to set a legend
+ function Legend(&$aGraph) {
+ if( $this->legend != "" )
+ $aGraph->legend->Add($this->legend,$this->color,"",0,$this->legendcsimtarget,$this->legendcsimalt);
+ }
+
+} // Class
+
+require_once "jpgraph_plotmark.inc" ;
+
+//==============================================================================
+// The following section contains classes to implement the "band" functionality
+//==============================================================================
+
+// Utility class to hold coordinates for a rectangle
+class Rectangle {
+ var $x,$y,$w,$h;
+ var $xe, $ye;
+ function Rectangle($aX,$aY,$aWidth,$aHeight) {
+ $this->x=$aX;
+ $this->y=$aY;
+ $this->w=$aWidth;
+ $this->h=$aHeight;
+ $this->xe=$aX+$aWidth-1;
+ $this->ye=$aY+$aHeight-1;
+ }
+}
+
+//=====================================================================
+// Class RectPattern
+// Base class for pattern hierarchi that is used to display patterned
+// bands on the graph. Any subclass that doesn't override Stroke()
+// must at least implement method DoPattern(&$aImg) which is responsible
+// for drawing the pattern onto the graph.
+//=====================================================================
+class RectPattern {
+ var $color;
+ var $weight;
+ var $rect=null;
+ var $doframe=true;
+ var $linespacing; // Line spacing in pixels
+ var $iBackgroundColor=-1; // Default is no background fill
+
+ function RectPattern($aColor,$aWeight=1) {
+ $this->color = $aColor;
+ $this->weight = $aWeight;
+ }
+
+ function SetBackground($aBackgroundColor) {
+ $this->iBackgroundColor=$aBackgroundColor;
+ }
+
+ function SetPos(&$aRect) {
+ $this->rect = $aRect;
+ }
+
+ function ShowFrame($aShow=true) {
+ $this->doframe=$aShow;
+ }
+
+ function SetDensity($aDens) {
+ if( $aDens <1 || $aDens > 100 )
+ JpGraphError::Raise(" Desity for pattern must be between 1 and 100. (You tried $aDens)");
+ // 1% corresponds to linespacing=50
+ // 100 % corresponds to linespacing 1
+ $this->linespacing = floor(((100-$aDens)/100.0)*50)+1;
+
+ }
+
+ function Stroke(&$aImg) {
+ if( $this->rect == null )
+ JpGraphError::Raise(" No positions specified for pattern.");
+
+ if( !(is_numeric($this->iBackgroundColor) && $this->iBackgroundColor==-1) ) {
+ $aImg->SetColor($this->iBackgroundColor);
+ $aImg->FilledRectangle($this->rect->x,$this->rect->y,$this->rect->xe,$this->rect->ye);
+ }
+
+ $aImg->SetColor($this->color);
+ $aImg->SetLineWeight($this->weight);
+
+ // Virtual function implemented by subclass
+ $this->DoPattern($aImg);
+
+ // Frame around the pattern area
+ if( $this->doframe )
+ $aImg->Rectangle($this->rect->x,$this->rect->y,$this->rect->xe,$this->rect->ye);
+ }
+
+}
+
+
+//=====================================================================
+// Class RectPatternSolid
+// Implements a solid band
+//=====================================================================
+class RectPatternSolid extends RectPattern {
+
+ function RectPatternSolid($aColor="black",$aWeight=1) {
+ parent::RectPattern($aColor,$aWeight);
+ }
+
+ function DoPattern(&$aImg) {
+ $aImg->SetColor($this->color);
+ $aImg->FilledRectangle($this->rect->x,$this->rect->y,
+ $this->rect->xe,$this->rect->ye);
+ }
+}
+
+//=====================================================================
+// Class RectPatternHor
+// Implements horizontal line pattern
+//=====================================================================
+class RectPatternHor extends RectPattern {
+
+ function RectPatternHor($aColor="black",$aWeight=1,$aLineSpacing=7) {
+ parent::RectPattern($aColor,$aWeight);
+ $this->linespacing = $aLineSpacing;
+ }
+
+ function DoPattern(&$aImg) {
+ $x0 = $this->rect->x;
+ $x1 = $this->rect->xe;
+ $y = $this->rect->y;
+ while( $y < $this->rect->ye ) {
+ $aImg->Line($x0,$y,$x1,$y);
+ $y += $this->linespacing;
+ }
+ }
+}
+
+//=====================================================================
+// Class RectPatternVert
+// Implements vertical line pattern
+//=====================================================================
+class RectPatternVert extends RectPattern {
+ var $linespacing=10; // Line spacing in pixels
+
+ function RectPatternVert($aColor="black",$aWeight=1,$aLineSpacing=7) {
+ parent::RectPattern($aColor,$aWeight);
+ $this->linespacing = $aLineSpacing;
+ }
+
+ //--------------------
+ // Private methods
+ //
+ function DoPattern(&$aImg) {
+ $x = $this->rect->x;
+ $y0 = $this->rect->y;
+ $y1 = $this->rect->ye;
+ while( $x < $this->rect->xe ) {
+ $aImg->Line($x,$y0,$x,$y1);
+ $x += $this->linespacing;
+ }
+ }
+}
+
+
+//=====================================================================
+// Class RectPatternRDiag
+// Implements right diagonal pattern
+//=====================================================================
+class RectPatternRDiag extends RectPattern {
+ var $linespacing; // Line spacing in pixels
+
+ function RectPatternRDiag($aColor="black",$aWeight=1,$aLineSpacing=12) {
+ parent::RectPattern($aColor,$aWeight);
+ $this->linespacing = $aLineSpacing;
+ }
+
+ function DoPattern(&$aImg) {
+ // --------------------
+ // | / / / / /|
+ // |/ / / / / |
+ // | / / / / |
+ // --------------------
+ $xe = $this->rect->xe;
+ $ye = $this->rect->ye;
+ $x0 = $this->rect->x + round($this->linespacing/2);
+ $y0 = $this->rect->y;
+ $x1 = $this->rect->x;
+ $y1 = $this->rect->y + round($this->linespacing/2);
+
+ while($x0<=$xe && $y1<=$ye) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $x0 += $this->linespacing;
+ $y1 += $this->linespacing;
+ }
+
+ if( $xe-$x1 > $ye-$y0 ) {
+ // Width larger than height
+ $x1 = $this->rect->x + ($y1-$ye);
+ $y1 = $ye;
+ $y0 = $this->rect->y;
+ while( $x0 <= $xe ) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $x0 += $this->linespacing;
+ $x1 += $this->linespacing;
+ }
+
+ $y0=$this->rect->y + ($x0-$xe);
+ $x0=$xe;
+ }
+ else {
+ // Height larger than width
+ $diff = $x0-$xe;
+ $y0 = $diff+$this->rect->y;
+ $x0 = $xe;
+ $x1 = $this->rect->x;
+ while( $y1 <= $ye ) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $y1 += $this->linespacing;
+ $y0 += $this->linespacing;
+ }
+
+ $diff = $y1-$ye;
+ $y1 = $ye;
+ $x1 = $diff + $this->rect->x;
+ }
+
+ while( $y0 <= $ye ) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $y0 += $this->linespacing;
+ $x1 += $this->linespacing;
+ }
+ }
+}
+
+//=====================================================================
+// Class RectPatternLDiag
+// Implements left diagonal pattern
+//=====================================================================
+class RectPatternLDiag extends RectPattern {
+ var $linespacing; // Line spacing in pixels
+
+ function RectPatternLDiag($aColor="black",$aWeight=1,$aLineSpacing=12) {
+ $this->linespacing = $aLineSpacing;
+ parent::RectPattern($aColor,$aWeight);
+ }
+
+ function DoPattern(&$aImg) {
+ // --------------------
+ // |\ \ \ \ \ |
+ // | \ \ \ \ \|
+ // | \ \ \ \ |
+ // |------------------|
+ $xe = $this->rect->xe;
+ $ye = $this->rect->ye;
+ $x0 = $this->rect->x + round($this->linespacing/2);
+ $y0 = $this->rect->ye;
+ $x1 = $this->rect->x;
+ $y1 = $this->rect->ye - round($this->linespacing/2);
+
+ while($x0<=$xe && $y1>=$this->rect->y) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $x0 += $this->linespacing;
+ $y1 -= $this->linespacing;
+ }
+ if( $xe-$x1 > $ye-$this->rect->y ) {
+ // Width larger than height
+ $x1 = $this->rect->x + ($this->rect->y-$y1);
+ $y0=$ye; $y1=$this->rect->y;
+ while( $x0 <= $xe ) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $x0 += $this->linespacing;
+ $x1 += $this->linespacing;
+ }
+
+ $y0=$this->rect->ye - ($x0-$xe);
+ $x0=$xe;
+ }
+ else {
+ // Height larger than width
+ $diff = $x0-$xe;
+ $y0 = $ye-$diff;
+ $x0 = $xe;
+ while( $y1 >= $this->rect->y ) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $y0 -= $this->linespacing;
+ $y1 -= $this->linespacing;
+ }
+ $diff = $this->rect->y - $y1;
+ $x1 = $this->rect->x + $diff;
+ $y1 = $this->rect->y;
+ }
+ while( $y0 >= $this->rect->y ) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $y0 -= $this->linespacing;
+ $x1 += $this->linespacing;
+ }
+ }
+}
+
+//=====================================================================
+// Class RectPattern3DPlane
+// Implements "3D" plane pattern
+//=====================================================================
+class RectPattern3DPlane extends RectPattern {
+ var $alpha=50; // Parameter that specifies the distance
+ // to "simulated" horizon in pixel from the
+ // top of the band. Specifies how fast the lines
+ // converge.
+
+ function RectPattern3DPlane($aColor="black",$aWeight=1) {
+ parent::RectPattern($aColor,$aWeight);
+ $this->SetDensity(10); // Slightly larger default
+ }
+
+ function SetHorizon($aHorizon) {
+ $this->alpha=$aHorizon;
+ }
+
+ function DoPattern(&$aImg) {
+ // "Fake" a nice 3D grid-effect.
+ $x0 = $this->rect->x + $this->rect->w/2;
+ $y0 = $this->rect->y;
+ $x1 = $x0;
+ $y1 = $this->rect->ye;
+ $x0_right = $x0;
+ $x1_right = $x1;
+
+ // BTW "apa" means monkey in Swedish but is really a shortform for
+ // "alpha+a" which was the labels I used on paper when I derived the
+ // geometric to get the 3D perspective right.
+ // $apa is the height of the bounding rectangle plus the distance to the
+ // artifical horizon (alpha)
+ $apa = $this->rect->h + $this->alpha;
+
+ // Three cases and three loops
+ // 1) The endpoint of the line ends on the bottom line
+ // 2) The endpoint ends on the side
+ // 3) Horizontal lines
+
+ // Endpoint falls on bottom line
+ $middle=$this->rect->x + $this->rect->w/2;
+ $dist=$this->linespacing;
+ $factor=$this->alpha /($apa);
+ while($x1>$this->rect->x) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $aImg->Line($x0_right,$y0,$x1_right,$y1);
+ $x1 = $middle - $dist;
+ $x0 = $middle - $dist * $factor;
+ $x1_right = $middle + $dist;
+ $x0_right = $middle + $dist * $factor;
+ $dist += $this->linespacing;
+ }
+
+ // Endpoint falls on sides
+ $dist -= $this->linespacing;
+ $d=$this->rect->w/2;
+ $c = $apa - $d*$apa/$dist;
+ while( $x0>$this->rect->x ) {
+ $aImg->Line($x0,$y0,$this->rect->x,$this->rect->ye-$c);
+ $aImg->Line($x0_right,$y0,$this->rect->xe,$this->rect->ye-$c);
+ $dist += $this->linespacing;
+ $x0 = $middle - $dist * $factor;
+ $x1 = $middle - $dist;
+ $x0_right = $middle + $dist * $factor;
+ $c = $apa - $d*$apa/$dist;
+ }
+
+ // Horizontal lines
+ // They need some serious consideration since they are a function
+ // of perspective depth (alpha) and density (linespacing)
+ $x0=$this->rect->x;
+ $x1=$this->rect->xe;
+ $y=$this->rect->ye;
+
+ // The first line is drawn directly. Makes the loop below slightly
+ // more readable.
+ $aImg->Line($x0,$y,$x1,$y);
+ $hls = $this->linespacing;
+
+ // A correction factor for vertical "brick" line spacing to account for
+ // a) the difference in number of pixels hor vs vert
+ // b) visual apperance to make the first layer of "bricks" look more
+ // square.
+ $vls = $this->linespacing*0.6;
+
+ $ds = $hls*($apa-$vls)/$apa;
+ // Get the slope for the "perspective line" going from bottom right
+ // corner to top left corner of the "first" brick.
+
+ // Uncomment the following lines if you want to get a visual understanding
+ // of what this helpline does. BTW this mimics the way you would get the
+ // perspective right when drawing on paper.
+ /*
+ $x0 = $middle;
+ $y0 = $this->rect->ye;
+ $len=floor(($this->rect->ye-$this->rect->y)/$vls);
+ $x1 = $middle-round($len*$ds);
+ $y1 = $this->rect->ye-$len*$vls;
+ $aImg->PushColor("red");
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $aImg->PopColor();
+ */
+
+ $y -= $vls;
+ $k=($this->rect->ye-($this->rect->ye-$vls))/($middle-($middle-$ds));
+ $dist = $hls;
+ while( $y>$this->rect->y ) {
+ $aImg->Line($this->rect->x,$y,$this->rect->xe,$y);
+ $adj = $k*$dist/(1+$dist*$k/$apa);
+ if( $adj < 2 ) $adj=2;
+ $y = $this->rect->ye - round($adj);
+ $dist += $hls;
+ }
+ }
+}
+
+//=====================================================================
+// Class RectPatternCross
+// Vert/Hor crosses
+//=====================================================================
+class RectPatternCross extends RectPattern {
+ var $vert=null;
+ var $hor=null;
+ function RectPatternCross($aColor="black",$aWeight=1) {
+ parent::RectPattern($aColor,$aWeight);
+ $this->vert = new RectPatternVert($aColor,$aWeight);
+ $this->hor = new RectPatternHor($aColor,$aWeight);
+ }
+
+ function SetOrder($aDepth) {
+ $this->vert->SetOrder($aDepth);
+ $this->hor->SetOrder($aDepth);
+ }
+
+ function SetPos(&$aRect) {
+ parent::SetPos($aRect);
+ $this->vert->SetPos($aRect);
+ $this->hor->SetPos($aRect);
+ }
+
+ function SetDensity($aDens) {
+ $this->vert->SetDensity($aDens);
+ $this->hor->SetDensity($aDens);
+ }
+
+ function DoPattern(&$aImg) {
+ $this->vert->DoPattern($aImg);
+ $this->hor->DoPattern($aImg);
+ }
+}
+
+//=====================================================================
+// Class RectPatternDiagCross
+// Vert/Hor crosses
+//=====================================================================
+
+class RectPatternDiagCross extends RectPattern {
+ var $left=null;
+ var $right=null;
+ function RectPatternDiagCross($aColor="black",$aWeight=1) {
+ parent::RectPattern($aColor,$aWeight);
+ $this->right = new RectPatternRDiag($aColor,$aWeight);
+ $this->left = new RectPatternLDiag($aColor,$aWeight);
+ }
+
+ function SetOrder($aDepth) {
+ $this->left->SetOrder($aDepth);
+ $this->right->SetOrder($aDepth);
+ }
+
+ function SetPos(&$aRect) {
+ parent::SetPos($aRect);
+ $this->left->SetPos($aRect);
+ $this->right->SetPos($aRect);
+ }
+
+ function SetDensity($aDens) {
+ $this->left->SetDensity($aDens);
+ $this->right->SetDensity($aDens);
+ }
+
+ function DoPattern(&$aImg) {
+ $this->left->DoPattern($aImg);
+ $this->right->DoPattern($aImg);
+ }
+
+}
+
+//=====================================================================
+// Class RectPatternFactory
+// Factory class for rectangular pattern
+//=====================================================================
+class RectPatternFactory {
+ function RectPatternFactory() {
+ // Empty
+ }
+ function Create($aPattern,$aColor,$aWeight=1) {
+ switch($aPattern) {
+ case BAND_RDIAG:
+ $obj = new RectPatternRDiag($aColor,$aWeight);
+ break;
+ case BAND_LDIAG:
+ $obj = new RectPatternLDiag($aColor,$aWeight);
+ break;
+ case BAND_SOLID:
+ $obj = new RectPatternSolid($aColor,$aWeight);
+ break;
+ case BAND_VLINE:
+ $obj = new RectPatternVert($aColor,$aWeight);
+ break;
+ case BAND_HLINE:
+ $obj = new RectPatternHor($aColor,$aWeight);
+ break;
+ case BAND_3DPLANE:
+ $obj = new RectPattern3DPlane($aColor,$aWeight);
+ break;
+ case BAND_HVCROSS:
+ $obj = new RectPatternCross($aColor,$aWeight);
+ break;
+ case BAND_DIAGCROSS:
+ $obj = new RectPatternDiagCross($aColor,$aWeight);
+ break;
+ default:
+ JpGraphError::Raise(" Unknown pattern specification ($aPattern)");
+ }
+ return $obj;
+ }
+}
+
+
+//=====================================================================
+// Class PlotBand
+// Factory class which is used by the client.
+// It is responsible for factoring the corresponding pattern
+// concrete class.
+//=====================================================================
+class PlotBand {
+ var $prect=null;
+ var $depth;
+ var $dir, $min, $max;
+
+ function PlotBand($aDir,$aPattern,$aMin,$aMax,$aColor="black",$aWeight=1,$aDepth=DEPTH_BACK) {
+ $f = new RectPatternFactory();
+ $this->prect = $f->Create($aPattern,$aColor,$aWeight);
+ if( is_numeric($aMin) && is_numeric($aMax) && ($aMin > $aMax) )
+ JpGraphError::Raise('Min value for plotband is larger than specified max value. Please correct.');
+ $this->dir = $aDir;
+ $this->min = $aMin;
+ $this->max = $aMax;
+ $this->depth=$aDepth;
+ }
+
+ // Set position. aRect contains absolute image coordinates
+ function SetPos(&$aRect) {
+ assert( $this->prect != null ) ;
+ $this->prect->SetPos($aRect);
+ }
+
+ function ShowFrame($aFlag=true) {
+ $this->prect->ShowFrame($aFlag);
+ }
+
+ // Set z-order. In front of pplot or in the back
+ function SetOrder($aDepth) {
+ $this->depth=$aDepth;
+ }
+
+ function SetDensity($aDens) {
+ $this->prect->SetDensity($aDens);
+ }
+
+ function GetDir() {
+ return $this->dir;
+ }
+
+ function GetMin() {
+ return $this->min;
+ }
+
+ function GetMax() {
+ return $this->max;
+ }
+
+ // Display band
+ function Stroke(&$aImg,&$aXScale,&$aYScale) {
+ assert( $this->prect != null ) ;
+ if( $this->dir == HORIZONTAL ) {
+ if( $this->min === 'min' ) $this->min = $aYScale->GetMinVal();
+ if( $this->max === 'max' ) $this->max = $aYScale->GetMaxVal();
+
+ // Only draw the bar if it actually appears in the range
+ if ($this->min < $aYScale->GetMaxVal() && $this->max > $aYScale->GetMinVal()) {
+
+ // Trucate to limit of axis
+ $this->min = max($this->min, $aYScale->GetMinVal());
+ $this->max = min($this->max, $aYScale->GetMaxVal());
+
+ $x=$aXScale->scale_abs[0];
+ $y=$aYScale->Translate($this->max);
+ $width=$aXScale->scale_abs[1]-$aXScale->scale_abs[0]+1;
+ $height=abs($y-$aYScale->Translate($this->min))+1;
+ $this->prect->SetPos(new Rectangle($x,$y,$width,$height));
+ $this->prect->Stroke($aImg);
+ }
+ }
+ else { // VERTICAL
+ if( $this->min === 'min' ) $this->min = $aXScale->GetMinVal();
+ if( $this->max === 'max' ) $this->max = $aXScale->GetMaxVal();
+
+ // Only draw the bar if it actually appears in the range
+ if ($this->min < $aXScale->GetMaxVal() && $this->max > $aXScale->GetMinVal()) {
+
+ // Trucate to limit of axis
+ $this->min = max($this->min, $aXScale->GetMinVal());
+ $this->max = min($this->max, $aXScale->GetMaxVal());
+
+ $y=$aYScale->scale_abs[1];
+ $x=$aXScale->Translate($this->min);
+ $height=abs($aYScale->scale_abs[1]-$aYScale->scale_abs[0]);
+ $width=abs($x-$aXScale->Translate($this->max));
+ $this->prect->SetPos(new Rectangle($x,$y,$width,$height));
+ $this->prect->Stroke($aImg);
+ }
+ }
+ }
+}
+
+//===================================================
+// CLASS PlotLine
+// Description:
+// Data container class to hold properties for a static
+// line that is drawn directly in the plot area.
+// Usefull to add static borders inside a plot to show
+// for example set-values
+//===================================================
+class PlotLine {
+ var $weight=1;
+ var $color="black";
+ var $direction=-1;
+ var $scaleposition;
+
+//---------------
+// CONSTRUCTOR
+ function PlotLine($aDir=HORIZONTAL,$aPos=0,$aColor="black",$aWeight=1) {
+ $this->direction = $aDir;
+ $this->color=$aColor;
+ $this->weight=$aWeight;
+ $this->scaleposition=$aPos;
+ }
+
+//---------------
+// PUBLIC METHODS
+ function SetPosition($aScalePosition) {
+ $this->scaleposition=$aScalePosition;
+ }
+
+ function SetDirection($aDir) {
+ $this->direction = $aDir;
+ }
+
+ function SetColor($aColor) {
+ $this->color=$aColor;
+ }
+
+ function SetWeight($aWeight) {
+ $this->weight=$aWeight;
+ }
+
+ function Stroke(&$aImg,&$aXScale,&$aYScale) {
+ $aImg->SetColor($this->color);
+ $aImg->SetLineWeight($this->weight);
+ if( $this->direction == VERTICAL ) {
+ $ymin_abs=$aYScale->Translate($aYScale->GetMinVal());
+ $ymax_abs=$aYScale->Translate($aYScale->GetMaxVal());
+ $xpos_abs=$aXScale->Translate($this->scaleposition);
+ $aImg->Line($xpos_abs, $ymin_abs, $xpos_abs, $ymax_abs);
+ }
+ elseif( $this->direction == HORIZONTAL ) {
+ $xmin_abs=$aXScale->Translate($aXScale->GetMinVal());
+ $xmax_abs=$aXScale->Translate($aXScale->GetMaxVal());
+ $ypos_abs=$aYScale->Translate($this->scaleposition);
+ $aImg->Line($xmin_abs, $ypos_abs, $xmax_abs, $ypos_abs);
+ }
+ else
+ JpGraphError::Raise(" Illegal direction for static line");
+ }
+}
+
+// <EOF>
+?>
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_BAR.PHP
+// Description: Bar plot extension for JpGraph
+// Created: 2001-01-08
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_bar.php,v 1.54.2.5 2003/08/15 19:06:15 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+//===================================================
+// CLASS BarPlot
+// Description: Main code to produce a bar plot
+//===================================================
+class BarPlot extends Plot {
+ var $width=0.4; // in percent of major ticks
+ var $abswidth=-1; // Width in absolute pixels
+ var $fill=false,$fill_color="lightblue"; // Default is to fill with light blue
+ var $ybase=0; // Bars start at 0
+ var $align="center";
+ var $grad=false,$grad_style=1;
+ var $grad_fromcolor=array(50,50,200),$grad_tocolor=array(255,255,255);
+ var $bar_shadow=false;
+ var $bar_shadow_color="black";
+ var $bar_shadow_hsize=3,$bar_shadow_vsize=3;
+ var $valuepos='top';
+
+//---------------
+// CONSTRUCTOR
+ function BarPlot(&$datay,$datax=false) {
+ $this->Plot($datay,$datax);
+ ++$this->numpoints;
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ // Set a drop shadow for the bar (or rather an "up-right" shadow)
+ function SetShadow($color="black",$hsize=3,$vsize=3) {
+ $this->bar_shadow=true;
+ $this->bar_shadow_color=$color;
+ $this->bar_shadow_vsize=$vsize;
+ $this->bar_shadow_hsize=$hsize;
+
+ // Adjust the value margin to compensate for shadow
+ $this->value->margin += $vsize;
+ }
+
+ // DEPRECATED use SetYBase instead
+ function SetYMin($aYStartValue) {
+ //die("JpGraph Error: Deprecated function SetYMin. Use SetYBase() instead.");
+ $this->ybase=$aYStartValue;
+ }
+
+ // Specify the base value for the bars
+ function SetYBase($aYStartValue) {
+ $this->ybase=$aYStartValue;
+ }
+
+ function Legend(&$graph) {
+ if( $this->grad && $this->legend!="" && !$this->fill ) {
+ $color=array($this->grad_fromcolor,$this->grad_tocolor,$this->grad_style);
+ $graph->legend->Add($this->legend,$color,"",0,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ }
+ elseif( $this->fill_color && $this->legend!="" ) {
+ if( is_array($this->fill_color) )
+ $graph->legend->Add($this->legend,$this->fill_color[0],"",0,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ else
+ $graph->legend->Add($this->legend,$this->fill_color,"",0,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ }
+ }
+
+ // Gets called before any axis are stroked
+ function PreStrokeAdjust(&$graph) {
+ parent::PreStrokeAdjust($graph);
+
+ // If we are using a log Y-scale we want the base to be at the
+ // minimum Y-value unless the user have specifically set some other
+ // value than the default.
+ if( substr($graph->axtype,-3,3)=="log" && $this->ybase==0 )
+ $this->ybase = $graph->yaxis->scale->GetMinVal();
+
+ // For a "text" X-axis scale we will adjust the
+ // display of the bars a little bit.
+ if( substr($graph->axtype,0,3)=="tex" ) {
+ // Position the ticks between the bars
+ $graph->xaxis->scale->ticks->SetXLabelOffset(0.5,0);
+
+ // Center the bars
+ if( $this->align == "center" )
+ $graph->SetTextScaleOff(0.5-$this->width/2);
+ elseif( $this->align == "right" )
+ $graph->SetTextScaleOff(1-$this->width);
+
+ }
+ else {
+ // We only set an absolute width for linear and int scale
+ // for text scale the width will be set to a fraction of
+ // the majstep width.
+ if( $this->abswidth == -1 ) {
+ // Not set
+ // set width to a visuable sensible default
+ $this->abswidth = $graph->img->plotwidth/(2*count($this->coords[0]));
+ }
+ }
+ }
+
+ function Min() {
+ $m = parent::Min();
+ if( $m[1] >= $this->ybase )
+ $m[1] = $this->ybase;
+ return $m;
+ }
+
+ function Max() {
+ $m = parent::Max();
+ if( $m[1] <= $this->ybase )
+ $m[1] = $this->ybase;
+ return $m;
+ }
+
+ // Specify width as fractions of the major stepo size
+ function SetWidth($aFractionWidth) {
+ $this->width=$aFractionWidth;
+ }
+
+ // Specify width in absolute pixels. If specified this
+ // overrides SetWidth()
+ function SetAbsWidth($aWidth) {
+ $this->abswidth=$aWidth;
+ }
+
+ function SetAlign($aAlign) {
+ $this->align=$aAlign;
+ }
+
+ function SetNoFill() {
+ $this->grad = false;
+ $this->fill_color=false;
+ $this->fill=false;
+ }
+
+ function SetFillColor($aColor) {
+ $this->fill = true ;
+ $this->fill_color=$aColor;
+ }
+
+ function SetFillGradient($from_color,$to_color,$style) {
+ $this->grad=true;
+ $this->grad_fromcolor=$from_color;
+ $this->grad_tocolor=$to_color;
+ $this->grad_style=$style;
+ }
+
+ function SetValuePos($aPos) {
+ $this->valuepos = $aPos;
+ }
+
+ function Stroke(&$img,&$xscale,&$yscale) {
+
+ $numpoints = count($this->coords[0]);
+ if( isset($this->coords[1]) ) {
+ if( count($this->coords[1])!=$numpoints )
+ die("JpGraph Error: Number of X and Y points are not equal.<br>
+ Number of X-points:".count($this->coords[1])."<br>
+ Number of Y-points:$numpoints");
+ else
+ $exist_x = true;
+ }
+ else
+ $exist_x = false;
+
+
+ $numbars=count($this->coords[0]);
+
+ // Use GetMinVal() instead of scale[0] directly since in the case
+ // of log scale we get a correct value. Log scales will have negative
+ // values for values < 1 while still not representing negative numbers.
+ if( $yscale->GetMinVal() >= 0 )
+ $zp=$yscale->scale_abs[0];
+ else {
+ $zp=$yscale->Translate(0);
+ }
+
+ if( $this->abswidth > -1 ) {
+ $abswidth=$this->abswidth;
+ }
+ else
+ $abswidth=round($this->width*$xscale->scale_factor,0);
+
+ for($i=0; $i<$numbars; $i++) {
+
+ // If value is NULL, or 0 then don't draw a bar at all
+ if ($this->coords[0][$i] === null ||
+ $this->coords[0][$i] === '' ||
+ $this->coords[0][$i] === 0 ) continue;
+
+ if( $exist_x ) $x=$this->coords[1][$i];
+ else $x=$i;
+
+ $x=$xscale->Translate($x);
+
+ if( !$xscale->textscale ) {
+ if($this->align=="center")
+ $x -= $abswidth/2;
+ elseif($this->align=="right")
+ $x -= $abswidth;
+ }
+
+ $pts=array(
+ $x,$zp,
+ $x,$yscale->Translate($this->coords[0][$i]),
+ $x+$abswidth,$yscale->Translate($this->coords[0][$i]),
+ $x+$abswidth,$zp);
+ if( $this->grad ) {
+ $grad = new Gradient($img);
+ $grad->FilledRectangle($pts[2],$pts[3],
+ $pts[6],$pts[7],
+ $this->grad_fromcolor,$this->grad_tocolor,$this->grad_style);
+ }
+ elseif( !empty($this->fill_color) ) {
+ if(is_array($this->fill_color)) {
+ $img->PushColor($this->fill_color[$i % count($this->fill_color)]);
+ } else {
+ $img->PushColor($this->fill_color);
+ }
+ $img->FilledPolygon($pts);
+ $img->PopColor();
+ }
+
+ // Remember value of this bar
+ $val=$this->coords[0][$i];
+
+ if( $this->bar_shadow && $val !== 0 && $val !== 0.0 ) {
+ $ssh = $this->bar_shadow_hsize;
+ $ssv = $this->bar_shadow_vsize;
+ // Create points to create a "upper-right" shadow
+ if( $val > 0 ) {
+ $sp[0]=$pts[6]; $sp[1]=$pts[7];
+ $sp[2]=$pts[4]; $sp[3]=$pts[5];
+ $sp[4]=$pts[2]; $sp[5]=$pts[3];
+ $sp[6]=$pts[2]+$ssh; $sp[7]=$pts[3]-$ssv;
+ $sp[8]=$pts[4]+$ssh; $sp[9]=$pts[5]-$ssv;
+ $sp[10]=$pts[6]+$ssh; $sp[11]=$pts[7]-$ssv;
+ }
+ elseif( $val < 0 ) {
+ $sp[0]=$pts[4]; $sp[1]=$pts[5];
+ $sp[2]=$pts[6]; $sp[3]=$pts[7];
+ $sp[4]=$pts[0]; $sp[5]=$pts[1];
+ $sp[6]=$pts[0]+$ssh; $sp[7]=$pts[1]-$ssv;
+ $sp[8]=$pts[6]+$ssh; $sp[9]=$pts[7]-$ssv;
+ $sp[10]=$pts[4]+$ssh; $sp[11]=$pts[5]-$ssv;
+ }
+
+ $img->PushColor($this->bar_shadow_color);
+ $img->FilledPolygon($sp);
+ $img->PopColor();
+ }
+
+ // Stroke the outline of the bar
+ if( is_array($this->color) )
+ $img->SetColor($this->color[$i % count($this->color)]);
+ else
+ $img->SetColor($this->color);
+
+ $pts[] = $pts[0];
+ $pts[] = $pts[1];
+
+ if( $this->weight > 0 ) {
+ $img->SetLineWeight($this->weight);
+ $img->Polygon($pts);
+ }
+
+ $x=$pts[2]+($pts[4]-$pts[2])/2;
+ if( $this->valuepos=='top' ) {
+ $y=$pts[3];
+ $this->value->Stroke($img,$val,$x,$y);
+ }
+ elseif( $this->valuepos=='max' ) {
+ $y=$pts[3];
+ if( $img->a === 90 ) {
+ $this->value->SetAlign('right','center');
+ }
+ else {
+ $this->value->SetAlign('center','top');
+ }
+ $this->value->SetMargin(-2);
+ $this->value->Stroke($img,$val,$x,$y);
+ }
+ elseif( $this->valuepos=='center' ) {
+ $y = ($pts[3] + $pts[1])/2;
+ $this->value->SetAlign('center','center');
+ $this->value->SetMargin(0);
+ $this->value->Stroke($img,$val,$x,$y);
+ }
+ elseif( $this->valuepos=='bottom' || $this->valuepos=='min' ) {
+ $y=$pts[1];
+ $this->value->SetMargin(0);
+ $this->value->Stroke($img,$val,$x,$y);
+ }
+ else {
+ JpGraphError::Raise('Unknown position for values on bars :'.$this->valuepos);
+ die();
+ }
+ // Create the client side image map
+ $rpts = $img->ArrRotate($pts);
+ $csimcoord=round($rpts[0]).", ".round($rpts[1]);
+ for( $j=1; $j < 4; ++$j){
+ $csimcoord .= ", ".round($rpts[2*$j]).", ".round($rpts[2*$j+1]);
+ }
+ if( !empty($this->csimtargets[$i]) ) {
+ $this->csimareas .= '<area shape="poly" coords="'.$csimcoord.'" ';
+ $this->csimareas .= " href=\"".$this->csimtargets[$i]."\"";
+ if( !empty($this->csimalts[$i]) ) {
+ $sval=sprintf($this->csimalts[$i],$this->coords[0][$i]);
+ $this->csimareas .= " alt=\"$sval\" title=\"$sval\" ";
+ }
+ $this->csimareas .= ">\n";
+ }
+ }
+ return true;
+ }
+} // Class
+
+//===================================================
+// CLASS GroupBarPlot
+// Description: Produce grouped bar plots
+//===================================================
+class GroupBarPlot extends BarPlot {
+ var $plots;
+ var $width=0.7;
+ var $nbrplots=0;
+ var $numpoints;
+//---------------
+// CONSTRUCTOR
+ function GroupBarPlot($plots) {
+ $this->plots = $plots;
+ $this->nbrplots = count($plots);
+ $this->numpoints = $plots[0]->numpoints;
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Legend(&$graph) {
+ $n = count($this->plots);
+ for($i=0; $i<$n; ++$i)
+ $this->plots[$i]->DoLegend($graph);
+ }
+
+ function Min() {
+ list($xmin,$ymin) = $this->plots[0]->Min();
+ $n = count($this->plots);
+ for($i=0; $i<$n; ++$i) {
+ list($xm,$ym) = $this->plots[$i]->Min();
+ $xmin = max($xmin,$xm);
+ $ymin = min($ymin,$ym);
+ }
+ return array($xmin,$ymin);
+ }
+
+ function Max() {
+ list($xmax,$ymax) = $this->plots[0]->Max();
+ $n = count($this->plots);
+ for($i=0; $i<$n; ++$i) {
+ list($xm,$ym) = $this->plots[$i]->Max();
+ $xmax = max($xmax,$xm);
+ $ymax = max($ymax,$ym);
+ }
+ return array($xmax,$ymax);
+ }
+
+ function GetCSIMareas() {
+ $n = count($this->plots);
+ $csimareas='';
+ for($i=0; $i < $n; ++$i) {
+ $csimareas .= $this->plots[$i]->csimareas;
+ }
+ return $csimareas;
+ }
+
+ // Stroke all the bars next to each other
+ function Stroke(&$img,&$xscale,&$yscale) {
+ $tmp=$xscale->off;
+ $n = count($this->plots);
+ $subwidth = $this->width/$this->nbrplots ;
+ for( $i=0; $i < $n; ++$i ) {
+ $this->plots[$i]->ymin=$this->ybase;
+ $this->plots[$i]->SetWidth($subwidth);
+
+ // If the client have used SetTextTickInterval() then
+ // major_step will be > 1 and the positioning will fail.
+ // If we assume it is always one the positioning will work
+ // fine with a text scale but this will not work with
+ // arbitrary linear scale
+ $xscale->off = $tmp+$i*round(/*$xscale->ticks->major_step* */
+ $xscale->scale_factor*$subwidth);
+ $this->plots[$i]->Stroke($img,$xscale,$yscale);
+ }
+ $xscale->off=$tmp;
+ }
+} // Class
+
+//===================================================
+// CLASS AccBarPlot
+// Description: Produce accumulated bar plots
+//===================================================
+class AccBarPlot extends BarPlot {
+ var $plots=null,$nbrplots=0,$numpoints=0;
+//---------------
+// CONSTRUCTOR
+ function AccBarPlot($plots) {
+ $this->plots = $plots;
+ $this->nbrplots = count($plots);
+ $this->numpoints = $plots[0]->numpoints;
+ $this->value = new DisplayValue();
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Legend(&$graph) {
+ $n = count($this->plots);
+ for( $i=$n-1; $i>=0; --$i )
+ $this->plots[$i]->DoLegend($graph);
+ }
+
+ function Max() {
+ list($xmax) = $this->plots[0]->Max();
+ $nmax=0;
+ for($i=0; $i<count($this->plots); ++$i) {
+ $n = count($this->plots[$i]->coords[0]);
+ $nmax = max($nmax,$n);
+ list($x) = $this->plots[$i]->Max();
+ $xmax = max($xmax,$x);
+ }
+ for( $i = 0; $i < $nmax; $i++ ) {
+ // Get y-value for bar $i by adding the
+ // individual bars from all the plots added.
+ // It would be wrong to just add the
+ // individual plots max y-value since that
+ // would in most cases give to large y-value.
+ $y=0;
+ if( $this->plots[0]->coords[0][$i] > 0 )
+ $y=$this->plots[0]->coords[0][$i];
+ for( $j = 1; $j < $this->nbrplots; $j++ ) {
+ if( $this->plots[$j]->coords[0][$i] > 0 )
+ $y += $this->plots[$j]->coords[0][$i];
+ }
+ $ymax[$i] = $y;
+ }
+ $ymax = max($ymax);
+
+ // Bar always start at baseline
+ if( $ymax <= $this->ybase )
+ $ymax = $this->ybase;
+ return array($xmax,$ymax);
+ }
+
+ function Min() {
+ $nmax=0;
+ list($xmin,$ysetmin) = $this->plots[0]->Min();
+ for($i=0; $i<count($this->plots); ++$i) {
+ $n = count($this->plots[$i]->coords[0]);
+ $nmax = max($nmax,$n);
+ list($x,$y) = $this->plots[$i]->Min();
+ $xmin = Min($xmin,$x);
+ $ysetmin = Min($y,$ysetmin);
+ }
+ for( $i = 0; $i < $nmax; $i++ ) {
+ // Get y-value for bar $i by adding the
+ // individual bars from all the plots added.
+ // It would be wrong to just add the
+ // individual plots max y-value since that
+ // would in most cases give to large y-value.
+ $y=$this->plots[0]->coords[0][$i];
+ for( $j = 1; $j < $this->nbrplots; $j++ ) {
+ $y += $this->plots[ $j ]->coords[0][$i];
+ }
+ $ymin[$i] = $y;
+ }
+ $ymin = Min($ysetmin,Min($ymin));
+ // Bar always start at baseline
+ if( $ymin >= $this->ybase )
+ $ymin = $this->ybase;
+ return array($xmin,$ymin);
+ }
+
+ // Stroke acc bar plot
+ function Stroke(&$img,&$xscale,&$yscale) {
+ $img->SetLineWeight($this->weight);
+ for($i=0; $i<$this->numpoints-1; $i++) {
+ $accy = 0;
+ $accy_neg = 0;
+ for($j=0; $j < $this->nbrplots; ++$j ) {
+
+ $img->SetColor($this->plots[$j]->color);
+
+ if ( $this->plots[$j]->coords[0][$i] >= 0) {
+ $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy);
+ $accyt=$yscale->Translate($accy);
+ $accy+=$this->plots[$j]->coords[0][$i];
+ }
+ else {
+ //if ( $this->plots[$j]->coords[0][$i] < 0 || $accy_neg < 0 ) {
+ $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy_neg);
+ $accyt=$yscale->Translate($accy_neg);
+ $accy_neg+=$this->plots[$j]->coords[0][$i];
+ }
+
+ $xt=$xscale->Translate($i);
+
+ if( $this->abswidth > -1 )
+ $abswidth=$this->abswidth;
+ else
+ $abswidth=round($this->width*$xscale->scale_factor,0);
+
+ $pts=array($xt,$accyt,$xt,$yt,$xt+$abswidth,$yt,$xt+$abswidth,$accyt);
+
+ if( $this->bar_shadow ) {
+ $ssh = $this->bar_shadow_hsize;
+ $ssv = $this->bar_shadow_vsize;
+
+ // We must also differ if we are a positive or negative bar.
+ if( $j === 0 ) {
+ // This gets extra complicated since we have to
+ // see all plots to see if we are negative. It could
+ // for example be that all plots are 0 until the very
+ // last one. We therefore need to save the initial setup
+ // for both the negative and positive case
+
+ // In case the final bar is positive
+ $sp[0]=$pts[6]+1; $sp[1]=$pts[7];
+ $sp[2]=$pts[6]+$ssh; $sp[3]=$pts[7]-$ssv;
+
+ // In case the final bar is negative
+ $nsp[0]=$pts[0]; $nsp[1]=$pts[1];
+ $nsp[2]=$pts[0]+$ssh; $nsp[3]=$pts[1]-$ssv;
+ $nsp[4]=$pts[6]+$ssh; $nsp[5]=$pts[7]-$ssv;
+ $nsp[10]=$pts[6]+1; $nsp[11]=$pts[7];
+ }
+
+ if( $j === $this->nbrplots-1 ) {
+ // If this is the last plot of the bar and
+ // the total value is larger than 0 then we
+ // add the shadow.
+ if( $accy > 0 ) {
+ $sp[4]=$pts[4]+$ssh; $sp[5]=$pts[5]-$ssv;
+ $sp[6]=$pts[2]+$ssh; $sp[7]=$pts[3]-$ssv;
+ $sp[8]=$pts[2]; $sp[9]=$pts[3]-1;
+ $sp[10]=$pts[4]+1; $sp[11]=$pts[5];
+ $img->PushColor($this->bar_shadow_color);
+ $img->FilledPolygon($sp,4);
+ $img->PopColor();
+ }
+ elseif( $accy_neg < 0 ) {
+ $nsp[6]=$pts[4]+$ssh; $nsp[7]=$pts[5]-$ssv;
+ $nsp[8]=$pts[4]+1; $nsp[9]=$pts[5];
+ $img->PushColor($this->bar_shadow_color);
+ $img->FilledPolygon($nsp,4);
+ $img->PopColor();
+ }
+ }
+ }
+
+ // If value is NULL or 0, then don't draw a bar at all
+ if ($this->plots[$j]->coords[0][$i] == 0 ) continue;
+
+ if( $this->plots[$j]->grad ) {
+ $grad = new Gradient($img);
+ $grad->FilledRectangle(
+ $pts[2],$pts[3],
+ $pts[6],$pts[7],
+ $this->plots[$j]->grad_fromcolor,
+ $this->plots[$j]->grad_tocolor,
+ $this->plots[$j]->grad_style);
+ } else {
+ if (is_array($this->plots[$j]->fill_color) ) {
+ $numcolors = count($this->plots[$j]->fill_color);
+ $img->SetColor($this->plots[$j]->fill_color[$i % $numcolors]);
+ }
+ else {
+ $img->SetColor($this->plots[$j]->fill_color);
+ }
+ $img->FilledPolygon($pts);
+ $img->SetColor($this->plots[$j]->color);
+ }
+
+
+ // CSIM array
+
+ if( $i < count($this->plots[$j]->csimtargets) ) {
+ // Create the client side image map
+ $rpts = $img->ArrRotate($pts);
+ $csimcoord=round($rpts[0]).", ".round($rpts[1]);
+ for( $k=1; $k < 4; ++$k){
+ $csimcoord .= ", ".round($rpts[2*$k]).", ".round($rpts[2*$k+1]);
+ }
+ if( ! empty($this->plots[$j]->csimtargets[$i]) ) {
+ $this->csimareas.= '<area shape="poly" coords="'.$csimcoord.'" ';
+ $this->csimareas.= " href=\"".$this->plots[$j]->csimtargets[$i]."\"";
+ if( !empty($this->plots[$j]->csimalts[$i]) ) {
+ $sval=sprintf($this->plots[$j]->csimalts[$i],$this->plots[$j]->coords[0][$i]);
+ $this->csimareas .= " alt=\"$sval\" title=\"$sval\" ";
+ }
+ $this->csimareas .= ">\n";
+ }
+ }
+
+ $pts[] = $pts[0];
+ $pts[] = $pts[1];
+ $img->Polygon($pts);
+ }
+
+ // Draw labels for each acc.bar
+
+ $x=$pts[2]+($pts[4]-$pts[2])/2;
+ $y=$yscale->Translate($accy);
+ if($this->bar_shadow) $x += $ssh;
+ $this->value->Stroke($img,$accy,$x,$y);
+
+ $accy = 0;
+ $accy_neg = 0;
+ for($j=0; $j<$this->nbrplots; ++$j ) {
+ if ($this->plots[$j]->coords[0][$i] > 0) {
+ $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy);
+ $accyt=$yscale->Translate($accy);
+ $y = $accyt-($accyt-$yt)/2;
+ $accy+=$this->plots[$j]->coords[0][$i];
+ } else {
+ $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy_neg);
+ $accyt=$yscale->Translate($accy_neg);
+ //$y=0;
+ $accy_neg+=$this->plots[$j]->coords[0][$i];
+ $y = $accyt-($accyt-$yt)/2; // TODO : Check this fix
+ }
+ $this->plots[$j]->value->SetAlign("center","center");
+ $this->plots[$j]->value->SetMargin(0);
+ $this->plots[$j]->value->Stroke($img,$this->plots[$j]->coords[0][$i],$x,$y);
+ }
+
+ }
+ return true;
+ }
+} // Class
+
+/* EOF */
+?>
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_CANVAS.PHP
+// Description: Canvas drawing extension for JpGraph
+// Created: 2001-01-08
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_canvas.php,v 1.12 2003/01/09 12:56:56 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+//===================================================
+// CLASS CanvasGraph
+// Description: Creates a simple canvas graph which
+// might be used together with the basic Image drawing
+// primitives. Useful to auickoly produce some arbitrary
+// graphic which benefits from all the functionality in the
+// graph liek caching for example.
+//===================================================
+class CanvasGraph extends Graph {
+//---------------
+// CONSTRUCTOR
+ function CanvasGraph($aWidth=300,$aHeight=200,$aCachedName="",$timeout=0,$inline=1) {
+ $this->Graph($aWidth,$aHeight,$aCachedName,$timeout,$inline);
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ function InitFrame() {
+ $this->StrokePlotArea();
+ }
+
+ // Method description
+ function Stroke($aStrokeFileName="") {
+ if( $this->texts != null ) {
+ for($i=0; $i<count($this->texts); ++$i) {
+ $this->texts[$i]->Stroke($this->img);
+ }
+ }
+ $this->StrokeTitles();
+
+ // If the filename is given as the special _IMG_HANDLER
+ // then the image handler is returned and the image is NOT
+ // streamed back
+ if( $aStrokeFileName == _IMG_HANDLER ) {
+ return $this->img->img;
+ }
+ else {
+ // Finally stream the generated picture
+ $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
+ $aStrokeFileName);
+ }
+ }
+} // Class
+/* EOF */
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_CANVTOOLS.PHP
+// Description: Some utilities for text and shape drawing on a canvas
+// Created: 2002-08-23
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_canvtools.php,v 1.9 2002/12/01 10:00:40 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+DEFINE('CORNER_TOPLEFT',0);
+DEFINE('CORNER_TOPRIGHT',1);
+DEFINE('CORNER_BOTTOMRIGHT',2);
+DEFINE('CORNER_BOTTOMLEFT',3);
+
+
+//===================================================
+// CLASS CanvasScale
+// Description: Define a scale for canvas so we
+// can abstract away with absolute pixels
+//===================================================
+
+class CanvasScale {
+ var $g;
+ var $w,$h;
+ var $ixmin=0,$ixmax=10,$iymin=0,$iymax=10;
+
+ function CanvasScale(&$graph,$xmin=0,$xmax=10,$ymin=0,$ymax=10) {
+ $this->g = &$graph;
+ $this->w = $graph->img->width;
+ $this->h = $graph->img->height;
+ $this->ixmin = $xmin;
+ $this->ixmax = $xmax;
+ $this->iymin = $ymin;
+ $this->iymax = $ymax;
+ }
+
+ function Set($xmin=0,$xmax=10,$ymin=0,$ymax=10) {
+ $this->ixmin = $xmin;
+ $this->ixmax = $xmax;
+ $this->iymin = $ymin;
+ $this->iymax = $ymax;
+ }
+
+ function Translate($x,$y) {
+ $xp = round(($x-$this->ixmin)/($this->ixmax - $this->ixmin) * $this->w);
+ $yp = round(($y-$this->iymin)/($this->iymax - $this->iymin) * $this->h);
+ return array($xp,$yp);
+ }
+
+ function TranslateX($x) {
+ $xp = round(($x-$this->ixmin)/($this->ixmax - $this->ixmin) * $this->w);
+ return $xp;
+ }
+
+ function TranslateY($y) {
+ $yp = round(($y-$this->iymin)/($this->iymax - $this->iymin) * $this->h);
+ return $yp;
+ }
+
+}
+
+
+//===================================================
+// CLASS Shape
+// Description: Methods to draw shapes on canvas
+//===================================================
+class Shape {
+ var $img,$scale;
+
+ function Shape(&$aGraph,&$scale) {
+ $this->img = &$aGraph->img;
+ $this->img->SetColor('black');
+ $this->scale = &$scale;
+ }
+
+ function SetColor($aColor) {
+ $this->img->SetColor($aColor);
+ }
+
+ function Line($x1,$y1,$x2,$y2) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ list($x2,$y2) = $this->scale->Translate($x2,$y2);
+ $this->img->Line($x1,$y1,$x2,$y2);
+ }
+
+ function Polygon($p,$aClosed=false) {
+ $n=count($p);
+ for($i=0; $i < $n; $i+=2 ) {
+ $p[$i] = $this->scale->TranslateX($p[$i]);
+ $p[$i+1] = $this->scale->TranslateY($p[$i+1]);
+ }
+ $this->img->Polygon($p,$aClosed);
+ }
+
+ function FilledPolygon($p) {
+ $n=count($p);
+ for($i=0; $i < $n; $i+=2 ) {
+ $p[$i] = $this->scale->TranslateX($p[$i]);
+ $p[$i+1] = $this->scale->TranslateY($p[$i+1]);
+ }
+ $this->img->FilledPolygon($p);
+ }
+
+
+ // Draw a bezier curve with defining points in the $aPnts array
+ // using $aSteps steps.
+ // 0=x0, 1=y0
+ // 2=x1, 3=y1
+ // 4=x2, 5=y2
+ // 6=x3, 7=y3
+ function Bezier($p,$aSteps=40) {
+ $x0 = $p[0];
+ $y0 = $p[1];
+ // Calculate coefficients
+ $cx = 3*($p[2]-$p[0]);
+ $bx = 3*($p[4]-$p[2])-$cx;
+ $ax = $p[6]-$p[0]-$cx-$bx;
+ $cy = 3*($p[3]-$p[1]);
+ $by = 3*($p[5]-$p[3])-$cy;
+ $ay = $p[7]-$p[1]-$cy-$by;
+
+ // Step size
+ $delta = 1.0/$aSteps;
+
+ $x_old = $x0;
+ $y_old = $y0;
+ for($t=$delta; $t<=1.0; $t+=$delta) {
+ $tt = $t*$t; $ttt=$tt*$t;
+ $x = $ax*$ttt + $bx*$tt + $cx*$t + $x0;
+ $y = $ay*$ttt + $by*$tt + $cy*$t + $y0;
+ $this->Line($x_old,$y_old,$x,$y);
+ $x_old = $x;
+ $y_old = $y;
+ }
+ $this->Line($x_old,$y_old,$p[6],$p[7]);
+ }
+
+ function Rectangle($x1,$y1,$x2,$y2) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ list($x2,$y2) = $this->scale->Translate($x2,$y2);
+ $this->img->Rectangle($x1,$y1,$x2,$y2);
+ }
+
+ function FilledRectangle($x1,$y1,$x2,$y2) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ list($x2,$y2) = $this->scale->Translate($x2,$y2);
+ $this->img->FilledRectangle($x1,$y1,$x2,$y2);
+ }
+
+ function Circle($x1,$y1,$r) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ if( $r >= 0 )
+ $r = $this->scale->TranslateX($r);
+ else
+ $r = -$r;
+ $this->img->Circle($x1,$y1,$r);
+ }
+
+ function FilledCircle($x1,$y1,$r) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ if( $r >= 0 )
+ $r = $this->scale->TranslateX($r);
+ else
+ $r = -$r;
+ $this->img->FilledCircle($x1,$y1,$r);
+ }
+
+ function RoundedRectangle($x1,$y1,$x2,$y2,$r=null) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ list($x2,$y2) = $this->scale->Translate($x2,$y2);
+
+ if( $r == null )
+ $r = 5;
+ elseif( $r >= 0 )
+ $r = $this->scale->TranslateX($r);
+ else
+ $r = -$r;
+ $this->img->RoundedRectangle($x1,$y1,$x2,$y2,$r);
+ }
+
+ function FilledRoundedRectangle($x1,$y1,$x2,$y2,$r=null) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ list($x2,$y2) = $this->scale->Translate($x2,$y2);
+
+ if( $r == null )
+ $r = 5;
+ elseif( $r > 0 )
+ $r = $this->scale->TranslateX($r);
+ else
+ $r = -$r;
+ $this->img->FilledRoundedRectangle($x1,$y1,$x2,$y2,$r);
+ }
+
+ function ShadowRectangle($x1,$y1,$x2,$y2,$fcolor=false,$shadow_width=null,$shadow_color=array(102,102,102)) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ list($x2,$y2) = $this->scale->Translate($x2,$y2);
+ if( $shadow_width == null )
+ $shadow_width=4;
+ else
+ $shadow_width=$this->scale->TranslateX($shadow_width);
+ $this->img->ShadowRectangle($x1,$y1,$x2,$y2,$fcolor,$shadow_width,$shadow_color);
+ }
+
+ function SetTextAlign($halign,$valign="bottom") {
+ $this->img->SetTextAlign($halign,$valign="bottom");
+ }
+
+ function StrokeText($x1,$y1,$txt,$dir=0,$paragraph_align="left") {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ $this->img->StrokeText($x1,$y1,$txt,$dir,$paragraph_align);
+ }
+
+ // A rounded rectangle where one of the corner has been moved "into" the
+ // rectangle 'iw' width and 'ih' height. Corners:
+ // 0=Top left, 1=top right, 2=bottom right, 3=bottom left
+ function IndentedRectangle($xt,$yt,$w,$h,$iw=0,$ih=0,$aCorner=3,$aFillColor="",$r=4) {
+
+ list($xt,$yt) = $this->scale->Translate($xt,$yt);
+ list($w,$h) = $this->scale->Translate($w,$h);
+ list($iw,$ih) = $this->scale->Translate($iw,$ih);
+
+ $xr = $xt + $w - 0;
+ $yl = $yt + $h - 0;
+
+ switch( $aCorner ) {
+ case 0: // Upper left
+
+ // Bottom line, left & right arc
+ $this->img->Line($xt+$r,$yl,$xr-$r,$yl);
+ $this->img->Arc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
+ $this->img->Arc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
+
+ // Right line, Top right arc
+ $this->img->Line($xr,$yt+$r,$xr,$yl-$r);
+ $this->img->Arc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
+
+ // Top line, Top left arc
+ $this->img->Line($xt+$iw+$r,$yt,$xr-$r,$yt);
+ $this->img->Arc($xt+$iw+$r,$yt+$r,$r*2,$r*2,180,270);
+
+ // Left line
+ $this->img->Line($xt,$yt+$ih+$r,$xt,$yl-$r);
+
+ // Indent horizontal, Lower left arc
+ $this->img->Line($xt+$r,$yt+$ih,$xt+$iw-$r,$yt+$ih);
+ $this->img->Arc($xt+$r,$yt+$ih+$r,$r*2,$r*2,180,270);
+
+ // Indent vertical, Indent arc
+ $this->img->Line($xt+$iw,$yt+$r,$xt+$iw,$yt+$ih-$r);
+ $this->img->Arc($xt+$iw-$r,$yt+$ih-$r,$r*2,$r*2,0,90);
+
+ if( $aFillColor != '' ) {
+ $bc = $this->img->current_color_name;
+ $this->img->PushColor($aFillColor);
+ $this->img->FillToBorder($xr-$r,$yl-$r,$bc);
+ $this->img->PopColor();
+ }
+
+ break;
+
+ case 1: // Upper right
+
+ // Bottom line, left & right arc
+ $this->img->Line($xt+$r,$yl,$xr-$r,$yl);
+ $this->img->Arc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
+ $this->img->Arc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
+
+ // Left line, Top left arc
+ $this->img->Line($xt,$yt+$r,$xt,$yl-$r);
+ $this->img->Arc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
+
+ // Top line, Top right arc
+ $this->img->Line($xt+$r,$yt,$xr-$iw-$r,$yt);
+ $this->img->Arc($xr-$iw-$r,$yt+$r,$r*2,$r*2,270,360);
+
+ // Right line
+ $this->img->Line($xr,$yt+$ih+$r,$xr,$yl-$r);
+
+ // Indent horizontal, Lower right arc
+ $this->img->Line($xr-$iw+$r,$yt+$ih,$xr-$r,$yt+$ih);
+ $this->img->Arc($xr-$r,$yt+$ih+$r,$r*2,$r*2,270,360);
+
+ // Indent vertical, Indent arc
+ $this->img->Line($xr-$iw,$yt+$r,$xr-$iw,$yt+$ih-$r);
+ $this->img->Arc($xr-$iw+$r,$yt+$ih-$r,$r*2,$r*2,90,180);
+
+ if( $aFillColor != '' ) {
+ $bc = $this->img->current_color_name;
+ $this->img->PushColor($aFillColor);
+ $this->img->FillToBorder($xt+$r,$yl-$r,$bc);
+ $this->img->PopColor();
+ }
+
+ break;
+
+ case 2: // Lower right
+ // Top line, Top left & Top right arc
+ $this->img->Line($xt+$r,$yt,$xr-$r,$yt);
+ $this->img->Arc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
+ $this->img->Arc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
+
+ // Left line, Bottom left arc
+ $this->img->Line($xt,$yt+$r,$xt,$yl-$r);
+ $this->img->Arc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
+
+ // Bottom line, Bottom right arc
+ $this->img->Line($xt+$r,$yl,$xr-$iw-$r,$yl);
+ $this->img->Arc($xr-$iw-$r,$yl-$r,$r*2,$r*2,0,90);
+
+ // Right line
+ $this->img->Line($xr,$yt+$r,$xr,$yl-$ih-$r);
+
+ // Indent horizontal, Lower right arc
+ $this->img->Line($xr-$r,$yl-$ih,$xr-$iw+$r,$yl-$ih);
+ $this->img->Arc($xr-$r,$yl-$ih-$r,$r*2,$r*2,0,90);
+
+ // Indent vertical, Indent arc
+ $this->img->Line($xr-$iw,$yl-$r,$xr-$iw,$yl-$ih+$r);
+ $this->img->Arc($xr-$iw+$r,$yl-$ih+$r,$r*2,$r*2,180,270);
+
+ if( $aFillColor != '' ) {
+ $bc = $this->img->current_color_name;
+ $this->img->PushColor($aFillColor);
+ $this->img->FillToBorder($xt+$r,$yt+$r,$bc);
+ $this->img->PopColor();
+ }
+
+ break;
+
+ case 3: // Lower left
+ // Top line, Top left & Top right arc
+ $this->img->Line($xt+$r,$yt,$xr-$r,$yt);
+ $this->img->Arc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
+ $this->img->Arc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
+
+ // Right line, Bottom right arc
+ $this->img->Line($xr,$yt+$r,$xr,$yl-$r);
+ $this->img->Arc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
+
+ // Bottom line, Bottom left arc
+ $this->img->Line($xt+$iw+$r,$yl,$xr-$r,$yl);
+ $this->img->Arc($xt+$iw+$r,$yl-$r,$r*2,$r*2,90,180);
+
+ // Left line
+ $this->img->Line($xt,$yt+$r,$xt,$yl-$ih-$r);
+
+ // Indent horizontal, Lower left arc
+ $this->img->Line($xt+$r,$yl-$ih,$xt+$iw-$r,$yl-$ih);
+ $this->img->Arc($xt+$r,$yl-$ih-$r,$r*2,$r*2,90,180);
+
+ // Indent vertical, Indent arc
+ $this->img->Line($xt+$iw,$yl-$ih+$r,$xt+$iw,$yl-$r);
+ $this->img->Arc($xt+$iw-$r,$yl-$ih+$r,$r*2,$r*2,270,360);
+
+ if( $aFillColor != '' ) {
+ $bc = $this->img->current_color_name;
+ $this->img->PushColor($aFillColor);
+ $this->img->FillToBorder($xr-$r,$yt+$r,$bc);
+ $this->img->PopColor();
+ }
+
+ break;
+ }
+ }
+}
+
+
+//===================================================
+// CLASS RectangleText
+// Description: Draws a text paragraph inside a
+// rounded, possible filled, rectangle.
+//===================================================
+class CanvasRectangleText {
+ var $ix,$iy,$iw,$ih,$ir=4;
+ var $iTxt,$iColor='black',$iFillColor='',$iFontColor='black';
+ var $iParaAlign='center';
+ var $iAutoBoxMargin=5;
+ var $iShadowWidth=3,$iShadowColor='';
+
+ function CanvasRectangleText($aTxt='',$xl=0,$yt=0,$w=0,$h=0) {
+ $this->iTxt = new Text($aTxt);
+ $this->ix = $xl;
+ $this->iy = $yt;
+ $this->iw = $w;
+ $this->ih = $h;
+ }
+
+ function SetShadow($aColor='gray',$aWidth=3) {
+ $this->iShadowColor = $aColor;
+ $this->iShadowWidth = $aWidth;
+ }
+
+ function SetFont($FontFam,$aFontStyle,$aFontSize=12) {
+ $this->iTxt->SetFont($FontFam,$aFontStyle,$aFontSize);
+ }
+
+ function SetTxt($aTxt) {
+ $this->iTxt->Set($aTxt);
+ }
+
+ function ParagraphAlign($aParaAlign) {
+ $this->iParaAlign = $aParaAlign;
+ }
+
+ function SetFillColor($aFillColor) {
+ $this->iFillColor = $aFillColor;
+ }
+
+ function SetAutoMargin($aMargin) {
+ $this->iAutoBoxMargin=$aMargin;
+ }
+
+ function SetColor($aColor) {
+ $this->iColor = $aColor;
+ }
+
+ function SetFontColor($aColor) {
+ $this->iFontColor = $aColor;
+ }
+
+ function SetPos($xl=0,$yt=0,$w=0,$h=0) {
+ $this->ix = $xl;
+ $this->iy = $yt;
+ $this->iw = $w;
+ $this->ih = $h;
+ }
+
+ function Pos($xl=0,$yt=0,$w=0,$h=0) {
+ $this->ix = $xl;
+ $this->iy = $yt;
+ $this->iw = $w;
+ $this->ih = $h;
+ }
+
+ function Set($aTxt,$xl,$yt,$w=0,$h=0) {
+ $this->iTxt->Set($aTxt);
+ $this->ix = $xl;
+ $this->iy = $yt;
+ $this->iw = $w;
+ $this->ih = $h;
+ }
+
+ function SetCornerRadius($aRad=5) {
+ $this->ir = $aRad;
+ }
+
+ function Stroke($aImg,$scale) {
+
+ // If coordinates are specifed as negative this means we should
+ // treat them as abolsute (pixels) coordinates
+ if( $this->ix > 0 ) {
+ $this->ix = $scale->TranslateX($this->ix) ;
+ }
+ else {
+ $this->ix = -$this->ix;
+ }
+
+ if( $this->iy > 0 ) {
+ $this->iy = $scale->TranslateY($this->iy) ;
+ }
+ else {
+ $this->iy = -$this->iy;
+ }
+
+ list($this->iw,$this->ih) = $scale->Translate($this->iw,$this->ih) ;
+
+ if( $this->iw == 0 )
+ $this->iw = round($this->iTxt->GetWidth($aImg) + $this->iAutoBoxMargin);
+ if( $this->ih == 0 ) {
+ $this->ih = round($this->iTxt->GetTextHeight($aImg) + $this->iAutoBoxMargin);
+ }
+
+ if( $this->iShadowColor != '' ) {
+ $aImg->PushColor($this->iShadowColor);
+ $aImg->FilledRoundedRectangle($this->ix+$this->iShadowWidth,
+ $this->iy+$this->iShadowWidth,
+ $this->ix+$this->iw-1+$this->iShadowWidth,
+ $this->iy+$this->ih-1+$this->iShadowWidth,
+ $this->ir);
+ $aImg->PopColor();
+ }
+
+ if( $this->iFillColor != '' ) {
+ $aImg->PushColor($this->iFillColor);
+ $aImg->FilledRoundedRectangle($this->ix,$this->iy,
+ $this->ix+$this->iw-1,
+ $this->iy+$this->ih-1,
+ $this->ir);
+ $aImg->PopColor();
+ }
+
+ if( $this->iColor != '' ) {
+ $aImg->PushColor($this->iColor);
+ $aImg->RoundedRectangle($this->ix,$this->iy,
+ $this->ix+$this->iw-1,
+ $this->iy+$this->ih-1,
+ $this->ir);
+ $aImg->PopColor();
+ }
+
+ $this->iTxt->Align('center','center');
+ $this->iTxt->ParagraphAlign($this->iParaAlign);
+ $this->iTxt->SetColor($this->iFontColor);
+ $this->iTxt->Stroke($aImg, $this->ix+$this->iw/2, $this->iy+$this->ih/2);
+
+ return array($this->iw, $this->ih);
+
+ }
+
+}
+
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_ERROR.PHP
+// Description: Error plot extension for JpGraph
+// Created: 2001-01-08
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_error.php,v 1.16 2003/02/04 22:47:16 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+//===================================================
+// CLASS ErrorPlot
+// Description: Error plot with min/max value for
+// each datapoint
+//===================================================
+class ErrorPlot extends Plot {
+ var $errwidth=2;
+//---------------
+// CONSTRUCTOR
+ function ErrorPlot(&$datay,$datax=false) {
+ $this->Plot($datay,$datax);
+ $this->numpoints /= 2;
+ }
+//---------------
+// PUBLIC METHODS
+
+ // Gets called before any axis are stroked
+ function PreStrokeAdjust(&$graph) {
+ if( $this->center ) {
+ $a=0.5; $b=0.5;
+ ++$this->numpoints;
+ } else {
+ $a=0; $b=0;
+ }
+ $graph->xaxis->scale->ticks->SetXLabelOffset($a);
+ $graph->SetTextScaleOff($b);
+ //$graph->xaxis->scale->ticks->SupressMinorTickMarks();
+ }
+
+ // Method description
+ function Stroke(&$img,&$xscale,&$yscale) {
+ $numpoints=count($this->coords[0])/2;
+ $img->SetColor($this->color);
+ $img->SetLineWeight($this->weight);
+
+ if( isset($this->coords[1]) ) {
+ if( count($this->coords[1])!=$numpoints )
+ JpGraphError::Raise("Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])." Number of Y-points:$numpoints");
+ else
+ $exist_x = true;
+ }
+ else
+ $exist_x = false;
+
+ if( $exist_x )
+ $xs=$this->coords[1][0];
+ else
+ $xs=0;
+
+
+ for( $i=0; $i<$numpoints; ++$i) {
+ if( $exist_x ) $x=$this->coords[1][$i];
+ else $x=$i;
+ $xt = $xscale->Translate($x);
+ $yt1 = $yscale->Translate($this->coords[0][$i*2]);
+ $yt2 = $yscale->Translate($this->coords[0][$i*2+1]);
+ $img->Line($xt,$yt1,$xt,$yt2);
+ $img->Line($xt-$this->errwidth,$yt1,$xt+$this->errwidth,$yt1);
+ $img->Line($xt-$this->errwidth,$yt2,$xt+$this->errwidth,$yt2);
+ }
+ return true;
+ }
+} // Class
+
+
+//===================================================
+// CLASS ErrorLinePlot
+// Description: Combine a line and error plot
+// THIS IS A DEPRECATED PLOT TYPE JUST KEPT FOR
+// BACKWARD COMPATIBILITY
+//===================================================
+class ErrorLinePlot extends ErrorPlot {
+ var $line=null;
+//---------------
+// CONSTRUCTOR
+ function ErrorLinePlot(&$datay,$datax=false) {
+ $this->ErrorPlot($datay,$datax);
+ // Calculate line coordinates as the average of the error limits
+ for($i=0; $i < count($datay); $i+=2 ) {
+ $ly[]=($datay[$i]+$datay[$i+1])/2;
+ }
+ $this->line=new LinePlot($ly,$datax);
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Legend(&$graph) {
+ if( $this->legend != "" )
+ $graph->legend->Add($this->legend,$this->color);
+ $this->line->Legend($graph);
+ }
+
+ function Stroke(&$img,&$xscale,&$yscale) {
+ parent::Stroke($img,$xscale,$yscale);
+ $this->line->Stroke($img,$xscale,$yscale);
+ }
+} // Class
+
+
+//===================================================
+// CLASS LineErrorPlot
+// Description: Combine a line and error plot
+//===================================================
+class LineErrorPlot extends ErrorPlot {
+ var $line=null;
+//---------------
+// CONSTRUCTOR
+ // Data is (val, errdeltamin, errdeltamax)
+ function LineErrorPlot(&$datay,$datax=false) {
+ $ly=array(); $ey=array();
+ $n = count($datay);
+ if( $n % 3 != 0 ) {
+ JpGraphError::Raise('Error in input data to LineErrorPlot.'.
+ 'Number of data points must be a multiple of 3');
+ }
+ for($i=0; $i < count($datay); $i+=3 ) {
+ $ly[]=$datay[$i];
+ $ey[]=$datay[$i]+$datay[$i+1];
+ $ey[]=$datay[$i]+$datay[$i+2];
+ }
+ $this->ErrorPlot($ey,$datax);
+ $this->line=new LinePlot($ly,$datax);
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Legend(&$graph) {
+ if( $this->legend != "" )
+ $graph->legend->Add($this->legend,$this->color);
+ $this->line->Legend($graph);
+ }
+
+ function Stroke(&$img,&$xscale,&$yscale) {
+ parent::Stroke($img,$xscale,$yscale);
+ $this->line->Stroke($img,$xscale,$yscale);
+ }
+} // Class
+
+
+/* EOF */
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//=======================================================================
+// File: JPGRAPH_FLAGS.PHP
+// Description: Class Jpfile. Handles plotmarks
+// Created: 2003-06-28
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_flags.php,v 1.2.2.5 2003/08/24 11:15:02 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+//------------------------------------------------------------
+// Defines for the different basic sizes of flags
+//------------------------------------------------------------
+DEFINE('FLAGSIZE1',1);
+DEFINE('FLAGSIZE2',2);
+DEFINE('FLAGSIZE3',3);
+DEFINE('FLAGSIZE4',4);
+
+class FlagImages {
+
+ var $iCountryNameMap = array(
+ 'Afghanistan' => 'afgh',
+ 'Republic of Angola' => 'agla',
+ 'Republic of Albania' => 'alba',
+ 'Alderney' => 'alde',
+ 'Democratic and Popular Republic of Algeria' => 'alge',
+ 'Territory of American Samoa' => 'amsa',
+ 'Principality of Andorra' => 'andr',
+ 'British Overseas Territory of Anguilla' => 'angu',
+ 'Antarctica' => 'anta',
+ 'Argentine Republic' => 'arge',
+ 'League of Arab States' => 'arle',
+ 'Republic of Armenia' => 'arme',
+ 'Aruba' => 'arub',
+ 'Commonwealth of Australia' => 'astl',
+ 'Republic of Austria' => 'aust',
+ 'Azerbaijani Republic' => 'azer',
+ 'British Antarctic Territory' => 'bant',
+ 'Kingdom of Belgium' => 'belg',
+ 'British Overseas Territory of Bermuda' => 'berm',
+ 'Commonwealth of the Bahamas' => 'bhms',
+ 'Kingdom of Bahrain' => 'bhrn',
+ 'Republic of Belarus' => 'blru',
+ 'Republic of Bolivia' => 'blva',
+ 'Belize' => 'blze',
+ 'Republic of Benin' => 'bnin',
+ 'Republic of Botswana' => 'bots',
+ 'Federative Republic of Brazil' => 'braz',
+ 'Barbados' => 'brbd',
+ 'British Indian Ocean Territory' => 'brin',
+ 'Brunei Darussalam' => 'brun',
+ 'Republic of Burkina' => 'bufa',
+ 'Republic of Bulgaria' => 'bulg',
+ 'Republic of Burundi' => 'buru',
+ 'Overseas Territory of the British Virgin Islands' => 'bvis',
+ 'Central African Republic' => 'cafr',
+ 'Kingdom of Cambodia' => 'camb',
+ 'Republic of Cameroon' => 'came',
+ 'Dominion of Canada' => 'cana',
+ 'Caribbean Community' => 'cari',
+ 'Republic of Cape Verde' => 'cave',
+ 'Republic of Chad' => 'chad',
+ 'Republic of Chile' => 'chil',
+ 'Territory of Christmas Island' => 'chms',
+ 'Commonwealth of Independent States' => 'cins',
+ 'Cook Islands' => 'ckis',
+ 'Republic of Colombia' => 'clmb',
+ 'Territory of Cocos Islands' => 'cois',
+ 'Commonwealth' => 'comn',
+ 'Union of the Comoros' => 'como',
+ 'Republic of the Congo' => 'cong',
+ 'Republic of Costa Rica' => 'corc',
+ 'Republic of Croatia' => 'croa',
+ 'Republic of Cuba' => 'cuba',
+ 'British Overseas Territory of the Cayman Islands' => 'cyis',
+ 'Republic of Cyprus' => 'cypr',
+ 'The Czech Republic' => 'czec',
+ 'Kingdom of Denmark' => 'denm',
+ 'Republic of Djibouti' => 'djib',
+ 'Commonwealth of Dominica' => 'domn',
+ 'Dominican Republic' => 'dore',
+ 'Republic of Ecuador' => 'ecua',
+ 'Arab Republic of Egypt' => 'egyp',
+ 'Republic of El Salvador' => 'elsa',
+ 'England' => 'engl',
+ 'Republic of Equatorial Guinea' => 'eqgu',
+ 'State of Eritrea' => 'erit',
+ 'Republic of Estonia' => 'estn',
+ 'Ethiopia' => 'ethp',
+ 'European Union' => 'euun',
+ 'British Overseas Territory of the Falkland Islands' => 'fais',
+ 'International Federation of Vexillological Associations' => 'fiav',
+ 'Republic of Fiji' => 'fiji',
+ 'Republic of Finland' => 'finl',
+ 'Territory of French Polynesia' => 'fpol',
+ 'French Republic' => 'fran',
+ 'Overseas Department of French Guiana' => 'frgu',
+ 'Gabonese Republic' => 'gabn',
+ 'Republic of the Gambia' => 'gamb',
+ 'Republic of Georgia' => 'geor',
+ 'Federal Republic of Germany' => 'germ',
+ 'Republic of Ghana' => 'ghan',
+ 'Gibraltar' => 'gibr',
+ 'Hellenic Republic' => 'grec',
+ 'State of Grenada' => 'gren',
+ 'Overseas Department of Guadeloupe' => 'guad',
+ 'Territory of Guam' => 'guam',
+ 'Republic of Guatemala' => 'guat',
+ 'The Bailiwick of Guernsey' => 'guer',
+ 'Republic of Guinea' => 'guin',
+ 'Republic of Haiti' => 'hait',
+ 'Hong Kong Special Administrative Region' => 'hokn',
+ 'Republic of Honduras' => 'hond',
+ 'Republic of Hungary' => 'hung',
+ 'Republic of Iceland' => 'icel',
+ 'International Committee of the Red Cross' => 'icrc',
+ 'Republic of India' => 'inda',
+ 'Republic of Indonesia' => 'indn',
+ 'Republic of Iraq' => 'iraq',
+ 'Republic of Ireland' => 'irel',
+ 'Organization of the Islamic Conference' => 'isco',
+ 'Isle of Man' => 'isma',
+ 'State of Israel' => 'isra',
+ 'Italian Republic' => 'ital',
+ 'Jamaica' => 'jama',
+ 'Japan' => 'japa',
+ 'The Bailiwick of Jersey' => 'jers',
+ 'Hashemite Kingdom of Jordan' => 'jord',
+ 'Republic of Kazakhstan' => 'kazk',
+ 'Republic of Kenya' => 'keny',
+ 'Republic of Kiribati' => 'kirb',
+ 'State of Kuwait' => 'kuwa',
+ 'Kyrgyz Republic' => 'kyrg',
+ 'Republic of Latvia' => 'latv',
+ 'Lebanese Republic' => 'leba',
+ 'Kingdom of Lesotho' => 'lest',
+ 'Republic of Liberia' => 'libe',
+ 'Principality of Liechtenstein' => 'liec',
+ 'Republic of Lithuania' => 'lith',
+ 'Grand Duchy of Luxembourg' => 'luxe',
+ 'Macao Special Administrative Region' => 'maca',
+ 'Republic of Macedonia' => 'mace',
+ 'Republic of Madagascar' => 'mada',
+ 'Republic of the Marshall Islands' => 'mais',
+ 'Republic of Mali' => 'mali',
+ 'Federation of Malaysia' => 'mals',
+ 'Republic of Malta' => 'malt',
+ 'Republic of Malawi' => 'malw',
+ 'Overseas Department of Martinique' => 'mart',
+ 'Islamic Republic of Mauritania' => 'maur',
+ 'Territorial Collectivity of Mayotte' => 'mayt',
+ 'United Mexican States' => 'mexc',
+ 'Federated States of Micronesia' => 'micr',
+ 'Midway Islands' => 'miis',
+ 'Republic of Moldova' => 'mold',
+ 'Principality of Monaco' => 'mona',
+ 'Republic of Mongolia' => 'mong',
+ 'British Overseas Territory of Montserrat' => 'mont',
+ 'Kingdom of Morocco' => 'morc',
+ 'Republic of Mozambique' => 'moza',
+ 'Republic of Mauritius' => 'mrts',
+ 'Union of Myanmar' => 'myan',
+ 'Republic of Namibia' => 'namb',
+ 'North Atlantic Treaty Organization' => 'nato',
+ 'Republic of Nauru' => 'naur',
+ 'Turkish Republic of Northern Cyprus' => 'ncyp',
+ 'Netherlands Antilles' => 'nean',
+ 'Kingdom of Nepal' => 'nepa',
+ 'Kingdom of the Netherlands' => 'neth',
+ 'Territory of Norfolk Island' => 'nfis',
+ 'Federal Republic of Nigeria' => 'ngra',
+ 'Republic of Nicaragua' => 'nica',
+ 'Republic of Niger' => 'nigr',
+ 'Niue' => 'niue',
+ 'Commonwealth of the Northern Mariana Islands' => 'nmar',
+ 'Province of Northern Ireland' => 'noir',
+ 'Nordic Council' => 'nord',
+ 'Kingdom of Norway' => 'norw',
+ 'Territory of New Caledonia and Dependencies' => 'nwca',
+ 'New Zealand' => 'nwze',
+ 'Organization of American States' => 'oast',
+ 'Organization of African Unity' => 'oaun',
+ 'International Olympic Committee' => 'olym',
+ 'Sultanate of Oman' => 'oman',
+ 'Islamic Republic of Pakistan' => 'paks',
+ 'Republic of Palau' => 'pala',
+ 'Independent State of Papua New Guinea' => 'pang',
+ 'Republic of Paraguay' => 'para',
+ 'Republic of the Philippines' => 'phil',
+ 'British Overseas Territory of the Pitcairn Islands' => 'piis',
+ 'Republic of Poland' => 'pola',
+ 'Republic of Portugal' => 'port',
+ 'Commonwealth of Puerto Rico' => 'purc',
+ 'State of Qatar' => 'qata',
+ 'Russian Federation' => 'russ',
+ 'Republic of Rwanda' => 'rwan',
+ 'Kingdom of Saudi Arabia' => 'saar',
+ 'Republic of San Marino' => 'sama',
+ 'Nordic Sami Conference' => 'sami',
+ 'Sark' => 'sark',
+ 'Scotland' => 'scot',
+ 'Principality of Seborga' => 'sebo',
+ 'Republic of Sierra Leone' => 'sile',
+ 'Republic of Singapore' => 'sing',
+ 'Republic of Korea' => 'skor',
+ 'Republic of Slovenia' => 'slva',
+ 'Somali Republic' => 'smla',
+ 'Republic of Somaliland' => 'smld',
+ 'Republic of South Africa' => 'soaf',
+ 'Solomon Islands' => 'sois',
+ 'Kingdom of Spain' => 'span',
+ 'Secretariat of the Pacific Community' => 'spco',
+ 'Democratic Socialist Republic of Sri Lanka' => 'srla',
+ 'Saint Lucia' => 'stlu',
+ 'Republic of the Sudan' => 'suda',
+ 'Republic of Suriname' => 'surn',
+ 'Slovak Republic' => 'svka',
+ 'Kingdom of Sweden' => 'swdn',
+ 'Swiss Confederation' => 'swit',
+ 'Syrian Arab Republic' => 'syra',
+ 'Kingdom of Swaziland' => 'szld',
+ 'Republic of China' => 'taiw',
+ 'Republic of Tajikistan' => 'tajk',
+ 'United Republic of Tanzania' => 'tanz',
+ 'Kingdom of Thailand' => 'thal',
+ 'Autonomous Region of Tibet' => 'tibe',
+ 'Turkmenistan' => 'tkst',
+ 'Togolese Republic' => 'togo',
+ 'Tokelau' => 'toke',
+ 'Kingdom of Tonga' => 'tong',
+ 'Tristan da Cunha' => 'trdc',
+ 'Tromelin' => 'tris',
+ 'Republic of Tunisia' => 'tuns',
+ 'Republic of Turkey' => 'turk',
+ 'Tuvalu' => 'tuva',
+ 'United Arab Emirates' => 'uaem',
+ 'Republic of Uganda' => 'ugan',
+ 'Ukraine' => 'ukrn',
+ 'United Kingdom of Great Britain' => 'unkg',
+ 'United Nations' => 'unna',
+ 'United States of America' => 'unst',
+ 'Oriental Republic of Uruguay' => 'urgy',
+ 'Virgin Islands of the United States' => 'usvs',
+ 'Republic of Uzbekistan' => 'uzbk',
+ 'State of the Vatican City' => 'vacy',
+ 'Republic of Vanuatu' => 'vant',
+ 'Bolivarian Republic of Venezuela' => 'venz',
+ 'Republic of Yemen' => 'yemn',
+ 'Democratic Republic of Congo' => 'zare',
+ 'Republic of Zimbabwe' => 'zbwe' ) ;
+
+
+ var $iFlagCount = -1;
+ var $iFlagSetMap = array(
+ FLAGSIZE1 => 'flags_thumb35x35',
+ FLAGSIZE2 => 'flags_thumb60x60',
+ FLAGSIZE3 => 'flags_thumb100x100',
+ FLAGSIZE4 => 'flags'
+ );
+
+ var $iFlagData ;
+ var $iOrdIdx=array();
+
+ function FlagImages($aSize=FLAGSIZE1) {
+ switch($aSize) {
+ case FLAGSIZE1 :
+ case FLAGSIZE2 :
+ case FLAGSIZE3 :
+ case FLAGSIZE4 :
+ $file = dirname(__FILE__).'/'.$this->iFlagSetMap[$aSize].'.dat';
+ $fp = fopen($file,'rb');
+ $rawdata = fread($fp,filesize($file));
+ $this->iFlagData = unserialize($rawdata);
+ break;
+ default:
+ JpGraphError::Raise('Unknown flag size. ('.$aSize.')');
+ die();
+ }
+ $this->iFlagCount = count($this->iCountryNameMap);
+ }
+
+ function GetNum() {
+ return $this->iFlagCount;
+ }
+
+ function GetImgByIdx($aIdx) {
+ if( array_key_exists($aIdx,$this->iFlagData) ) {
+ $d = $this->iFlagData[$aIdx][1];
+ return Image::CreateFromString($d);
+ }
+ else {
+ JpGraphError::Raise("Flag index \"Â $aIdx\" does not exist.");
+ }
+ }
+
+ function GetIdxByOrdinal($aOrd,&$outFullName) {
+ $aOrd--;
+ $n = count($this->iOrdIdx);
+ if( $n == 0 ) {
+ reset($this->iCountryNameMap);
+ $this->iOrdIdx=array();
+ $i=0;
+ while( list($key,$val) = each($this->iCountryNameMap) ) {
+ $this->iOrdIdx[$i++] = array($val,$key);
+ }
+ $tmp=$this->iOrdIdx[$aOrd];
+ $outFullName = $tmp[1];
+ return $tmp[0];
+
+ }
+ elseif( $aOrd >= 0 && $aOrd < $n ) {
+ $tmp=$this->iOrdIdx[$aOrd];
+ $outFullName = $tmp[1];
+ return $tmp[0];
+ }
+ else {
+ JpGraphError::Raise('Invalid ordinal number specified for flag index.');
+ }
+ }
+
+ function GetIdxByName($aName,&$outFullName) {
+
+ if( is_integer($aName) ) {
+ $idx = $this->GetIdxByOrdinal($aName,$outFullName);
+ return $idx;
+ }
+
+ $found=false;
+ $aName = strtolower($aName);
+ $nlen = strlen($aName);
+ reset($this->iCountryNameMap);
+ // Match partial full country name or exact idx name
+ while( list($key,$val) = each($this->iCountryNameMap) ) {
+ if( strpos(strtolower($key), $aName) !== false ||
+ ($nlen == strlen($val) && $val == $aName) ) {
+ $found=true;
+ break;
+ }
+ }
+ if( $found ) {
+ $outFullName = $key;
+ return $val;
+ }
+ else {
+ JpGraphError::Raise("The (partial) country name \"$aName\" does not have a cooresponding flag image. The flag may still exist but under another name, e.g. insted of \"usa\" try \"united states\".");
+ }
+ }
+}
+
+
+
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_GANTT.PHP
+// Description: JpGraph Gantt plot extension
+// Created: 2001-11-12
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_gantt.php,v 1.46.2.28 2003/08/19 21:46:37 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (c) 2002 Johan Persson
+//========================================================================
+*/
+
+// Scale Header types
+DEFINE("GANTT_HDAY",1);
+DEFINE("GANTT_HWEEK",2);
+DEFINE("GANTT_HMONTH",4);
+DEFINE("GANTT_HYEAR",8);
+DEFINE("GANTT_HHOUR",16);
+DEFINE("GANTT_HMIN",32);
+
+// Bar patterns
+DEFINE("GANTT_RDIAG",BAND_RDIAG); // Right diagonal lines
+DEFINE("GANTT_LDIAG",BAND_LDIAG); // Left diagonal lines
+DEFINE("GANTT_SOLID",BAND_SOLID); // Solid one color
+DEFINE("GANTT_VLINE",BAND_VLINE); // Vertical lines
+DEFINE("GANTT_HLINE",BAND_HLINE); // Horizontal lines
+DEFINE("GANTT_3DPLANE",BAND_3DPLANE); // "3D" Plane
+DEFINE("GANTT_HVCROSS",BAND_HVCROSS); // Vertical/Hor crosses
+DEFINE("GANTT_DIAGCROSS",BAND_DIAGCROSS); // Diagonal crosses
+
+// Conversion constant
+DEFINE("SECPERDAY",3600*24);
+
+// Locales. ONLY KEPT FOR BACKWARDS COMPATIBILITY
+// You should use the proper locale strings directly
+// from now on.
+DEFINE("LOCALE_EN","en_UK");
+DEFINE("LOCALE_SV","sv_SE");
+
+// Layout of bars
+DEFINE("GANTT_EVEN",1);
+DEFINE("GANTT_FROMTOP",2);
+
+// Style for minute header
+DEFINE("MINUTESTYLE_MM",0); // 15
+DEFINE("MINUTESTYLE_CUSTOM",2); // Custom format
+
+
+// Style for hour header
+DEFINE("HOURSTYLE_HM24",0); // 13:10
+DEFINE("HOURSTYLE_HMAMPM",1); // 1:10pm
+DEFINE("HOURSTYLE_H24",2); // 13
+DEFINE("HOURSTYLE_HAMPM",3); // 1pm
+DEFINE("HOURSTYLE_CUSTOM",4); // User defined
+
+// Style for day header
+DEFINE("DAYSTYLE_ONELETTER",0); // "M"
+DEFINE("DAYSTYLE_LONG",1); // "Monday"
+DEFINE("DAYSTYLE_LONGDAYDATE1",2); // "Monday 23 Jun"
+DEFINE("DAYSTYLE_LONGDAYDATE2",3); // "Monday 23 Jun 2003"
+DEFINE("DAYSTYLE_SHORT",4); // "Mon"
+DEFINE("DAYSTYLE_SHORTDAYDATE1",5); // "Mon 23/6"
+DEFINE("DAYSTYLE_SHORTDAYDATE2",6); // "Mon 23 Jun"
+DEFINE("DAYSTYLE_SHORTDAYDATE3",7); // "Mon 23"
+DEFINE("DAYSTYLE_SHORTDATE1",8); // "23/6"
+DEFINE("DAYSTYLE_SHORTDATE2",9); // "23 Jun"
+DEFINE("DAYSTYLE_SHORTDATE3",10); // "Mon 23"
+DEFINE("DAYSTYLE_CUSTOM",11); // "M"
+
+// Styles for week header
+DEFINE("WEEKSTYLE_WNBR",0);
+DEFINE("WEEKSTYLE_FIRSTDAY",1);
+DEFINE("WEEKSTYLE_FIRSTDAY2",2);
+DEFINE("WEEKSTYLE_FIRSTDAYWNBR",3);
+DEFINE("WEEKSTYLE_FIRSTDAY2WNBR",4);
+
+// Styles for month header
+DEFINE("MONTHSTYLE_SHORTNAME",0);
+DEFINE("MONTHSTYLE_LONGNAME",1);
+DEFINE("MONTHSTYLE_LONGNAMEYEAR2",2);
+DEFINE("MONTHSTYLE_SHORTNAMEYEAR2",3);
+DEFINE("MONTHSTYLE_LONGNAMEYEAR4",4);
+DEFINE("MONTHSTYLE_SHORTNAMEYEAR4",5);
+
+
+// Types of constrain links
+DEFINE('CONSTRAIN_STARTSTART',0);
+DEFINE('CONSTRAIN_STARTEND',1);
+DEFINE('CONSTRAIN_ENDSTART',2);
+DEFINE('CONSTRAIN_ENDEND',3);
+
+// Arrow direction for constrain links
+DEFINE('ARROW_DOWN',0);
+DEFINE('ARROW_UP',1);
+DEFINE('ARROW_LEFT',2);
+DEFINE('ARROW_RIGHT',3);
+
+// Arrow type for constrain type
+DEFINE('ARROWT_SOLID',0);
+DEFINE('ARROWT_OPEN',1);
+
+// Arrow size for constrain lines
+DEFINE('ARROW_S1',0);
+DEFINE('ARROW_S2',1);
+DEFINE('ARROW_S3',2);
+DEFINE('ARROW_S4',3);
+DEFINE('ARROW_S5',4);
+
+// Activity types for use with utility method CreateSimple()
+DEFINE('ACTYPE_NORMAL',0);
+DEFINE('ACTYPE_GROUP',1);
+DEFINE('ACTYPE_MILESTONE',2);
+
+
+DEFINE('ACTINFO_3D',1);
+DEFINE('ACTINFO_2D',0);
+
+//===================================================
+// CLASS GanttActivityInfo
+// Description:
+//===================================================
+class GanttActivityInfo {
+ var $iColor='black';
+ var $iBackgroundColor='lightgray';
+ var $iFFamily=FF_FONT1,$iFStyle=FS_NORMAL,$iFSize=10,$iFontColor='black';
+ var $iTitles=array();
+ var $iWidth=array(),$iHeight=-1;
+ var $iLeftColMargin=4,$iRightColMargin=1,$iTopColMargin=1,$iBottomColMargin=3;
+ var $iTopHeaderMargin = 4;
+ var $vgrid = null;
+ var $iStyle=1;
+ var $iShow=true;
+ var $iHeaderAlign='center';
+
+ function GanttActivityInfo() {
+ $this->vgrid = new LineProperty();
+ }
+
+ function Hide($aF=true) {
+ $this->iShow=!$aF;
+ }
+
+ function Show($aF=true) {
+ $this->iShow=$aF;
+ }
+
+ // Specify font
+ function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
+ $this->iFFamily = $aFFamily;
+ $this->iFStyle = $aFStyle;
+ $this->iFSize = $aFSize;
+ }
+
+ function SetStyle($aStyle) {
+ $this->iStyle = $aStyle;
+ }
+
+ function SetColumnMargin($aLeft,$aRight) {
+ $this->iLeftColMargin = $aLeft;
+ $this->iRightColMargin = $aRight;
+ }
+
+ function SetFontColor($aFontColor) {
+ $this->iFontColor = $aFontColor;
+ }
+
+ function SetColor($aColor) {
+ $this->iColor = $aColor;
+ }
+
+ function SetBackgroundColor($aColor) {
+ $this->iBackgroundColor = $aColor;
+ }
+
+ function SetColTitles($aTitles,$aWidth=null) {
+ $this->iTitles = $aTitles;
+ $this->iWidth = $aWidth;
+ }
+
+ function SetMinColWidth($aWidths) {
+ $n = min(count($this->iTitles),count($aWidths));
+ for($i=0; $i < $n; ++$i ) {
+ if( !empty($aWidths[$i]) ) {
+ if( empty($this->iWidth[$i]) ) {
+ $this->iWidth[$i] = $aWidths[$i];
+ }
+ else {
+ $this->iWidth[$i] = max($this->iWidth[$i],$aWidths[$i]);
+ }
+ }
+ }
+ }
+
+ function GetWidth($aImg) {
+ $txt = new TextProperty();
+ $txt->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ $n = count($this->iTitles) ;
+ $rm=$this->iRightColMargin;
+ $w = 0;
+ for($h=0, $i=0; $i < $n; ++$i ) {
+ $w += $this->iLeftColMargin;
+ $txt->Set($this->iTitles[$i]);
+ if( !empty($this->iWidth[$i]) ) {
+ $w1 = max($txt->GetWidth($aImg)+$rm,$this->iWidth[$i]);
+ }
+ else {
+ $w1 = $txt->GetWidth($aImg)+$rm;
+ }
+ $this->iWidth[$i] = $w1;
+ $w += $w1;
+ $h = max($h,$txt->GetHeight($aImg));
+ }
+ $this->iHeight = $h+$this->iTopHeaderMargin;
+ $txt='';
+ return $w;
+ }
+
+ function GetColStart($aImg,&$ioStart,$aAddLeftMargin=false) {
+ $n = count($this->iTitles) ;
+ $adj = $aAddLeftMargin ? $this->iLeftColMargin : 0;
+ $ioStart=array($aImg->left_margin+$adj);
+ for( $i=1; $i < $n; ++$i ) {
+ $ioStart[$i] = $ioStart[$i-1]+$this->iLeftColMargin+$this->iWidth[$i-1];
+ }
+ }
+
+ // Adjust headers left, right or centered
+ function SetHeaderAlign($aAlign) {
+ $this->iHeaderAlign=$aAlign;
+ }
+
+ function Stroke($aImg,$aXLeft,$aYTop,$aXRight,$aYBottom,$aUseTextHeight=false) {
+
+ if( !$this->iShow ) return;
+
+ $txt = new TextProperty();
+ $txt->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ $txt->SetColor($this->iFontColor);
+ $txt->SetAlign($this->iHeaderAlign,'top');
+ $n=count($this->iTitles);
+
+ if( $n == 0 )
+ return;
+
+ $x = $aXLeft;
+ $h = $this->iHeight;
+ $yTop = $aUseTextHeight ? $aYBottom-$h-$this->iTopColMargin-$this->iBottomColMargin : $aYTop ;
+
+ if( $h < 0 ) {
+ JpGraphError::Raise('Internal error. Height for ActivityTitles is < 0');
+ }
+
+ $aImg->SetLineWeight(1);
+ // Set background color
+ $aImg->SetColor($this->iBackgroundColor);
+ $aImg->FilledRectangle($aXLeft,$yTop,$aXRight,$aYBottom-1);
+
+ if( $this->iStyle == 1 ) {
+ // Make a 3D effect
+ $aImg->SetColor('white');
+ $aImg->Line($aXLeft,$yTop+1,
+ $aXRight,$yTop+1);
+ }
+
+ for($i=0; $i < $n; ++$i ) {
+ if( $this->iStyle == 1 ) {
+ // Make a 3D effect
+ $aImg->SetColor('white');
+ $aImg->Line($x+1,$yTop,$x+1,$aYBottom);
+ }
+ $x += $this->iLeftColMargin;
+ $txt->Set($this->iTitles[$i]);
+
+ // Adjust the text anchor position according to the choosen alignment
+ $xp = $x;
+ if( $this->iHeaderAlign == 'center' ) {
+ $xp = (($x-$this->iLeftColMargin)+($x+$this->iWidth[$i]))/2;
+ }
+ elseif( $this->iHeaderAlign == 'right' ) {
+ $xp = $x +$this->iWidth[$i]-$this->iRightColMargin;
+ }
+
+ $txt->Stroke($aImg,$xp,$yTop+$this->iTopHeaderMargin);
+ $x += $this->iWidth[$i];
+ if( $i < $n-1 ) {
+ $aImg->SetColor($this->iColor);
+ $aImg->Line($x,$yTop,$x,$aYBottom);
+ }
+ }
+
+ $aImg->SetColor($this->iColor);
+ $aImg->Line($aXLeft,$yTop, $aXRight,$yTop);
+
+ // Stroke vertical column dividers
+ $cols=array();
+ $this->GetColStart($aImg,$cols);
+ $n=count($cols);
+ for( $i=1; $i < $n; ++$i ) {
+ $this->vgrid->Stroke($aImg,$cols[$i],$aYBottom,$cols[$i],
+ $aImg->height - $aImg->bottom_margin);
+ }
+ }
+}
+
+
+//===================================================
+// CLASS GanttGraph
+// Description: Main class to handle gantt graphs
+//===================================================
+class GanttGraph extends Graph {
+ var $scale; // Public accessible
+ var $iObj=array(); // Gantt objects
+ var $iLabelHMarginFactor=0.2; // 10% margin on each side of the labels
+ var $iLabelVMarginFactor=0.4; // 40% margin on top and bottom of label
+ var $iLayout=GANTT_FROMTOP; // Could also be GANTT_EVEN
+ var $iSimpleFont = FF_FONT1,$iSimpleFontSize=11;
+ var $iSimpleStyle=GANTT_RDIAG,$iSimpleColor='yellow',$iSimpleBkgColor='red';
+ var $iSimpleProgressBkgColor='gray',$iSimpleProgressColor='darkgreen';
+ var $iSimpleProgressStyle=GANTT_SOLID;
+//---------------
+// CONSTRUCTOR
+ // Create a new gantt graph
+ function GanttGraph($aWidth=0,$aHeight=0,$aCachedName="",$aTimeOut=0,$aInline=true) {
+ Graph::Graph($aWidth,$aHeight,$aCachedName,$aTimeOut,$aInline);
+ $this->scale = new GanttScale($this->img);
+ if( $aWidth > 0 )
+ $this->img->SetMargin($aWidth/17,$aWidth/17,$aHeight/7,$aHeight/10);
+
+ $this->scale->ShowHeaders(GANTT_HWEEK|GANTT_HDAY);
+ $this->SetBox();
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ //
+
+ function SetSimpleFont($aFont,$aSize) {
+ $this->iSimpleFont = $aFont;
+ $this->iSimpleFontSize = $aSize;
+ }
+
+ function SetSimpleStyle($aBand,$aColor,$aBkgColor) {
+ $this->iSimpleStyle = $aBand;
+ $this->iSimpleColor = $aColor;
+ $this->iSimpleBkgColor = $aSimpleBkgColor;
+ }
+
+ // A utility function to help create basic Gantt charts
+ function CreateSimple($data,$constrains=array(),$progress=array()) {
+
+ for( $i=0; $i < count($data); ++$i) {
+ switch( $data[$i][1] ) {
+ case ACTYPE_GROUP:
+ // Create a slightly smaller height bar since the
+ // "wings" at the end will make it look taller
+ $a = new GanttBar($data[$i][0],$data[$i][2],$data[$i][3],$data[$i][4],'',8);
+ $a->title->SetFont($this->iSimpleFont,FS_BOLD,$this->iSimpleFontSize);
+ $a->rightMark->Show();
+ $a->rightMark->SetType(MARK_RIGHTTRIANGLE);
+ $a->rightMark->SetWidth(8);
+ $a->rightMark->SetColor('black');
+ $a->rightMark->SetFillColor('black');
+
+ $a->leftMark->Show();
+ $a->leftMark->SetType(MARK_LEFTTRIANGLE);
+ $a->leftMark->SetWidth(8);
+ $a->leftMark->SetColor('black');
+ $a->leftMark->SetFillColor('black');
+
+ $a->SetPattern(BAND_SOLID,'black');
+ $csimpos = 6;
+ break;
+
+ case ACTYPE_NORMAL:
+ $a = new GanttBar($data[$i][0],$data[$i][2],$data[$i][3],$data[$i][4],'',10);
+ $a->title->SetFont($this->iSimpleFont,FS_NORMAL,$this->iSimpleFontSize);
+ $a->SetPattern($this->iSimpleStyle,$this->iSimpleColor);
+ $a->SetFillColor($this->iSimpleBkgColor);
+ // Check if this activity should have a constrain line
+ $n = count($constrains);
+ for( $j=0; $j < $n; ++$j ) {
+ if( $constrains[$j][0]==$data[$i][0] ) {
+ $a->SetConstrain($constrains[$j][1],$constrains[$j][2],'black',ARROW_S2,ARROWT_SOLID);
+ break;
+ }
+ }
+
+ // Check if this activity have a progress bar
+ $n = count($progress);
+ for( $j=0; $j < $n; ++$j ) {
+ if( $progress[$j][0]==$data[$i][0] ) {
+ $a->progress->Set($progress[$j][1]);
+ $a->progress->SetPattern($this->iSimpleProgressStyle,
+ $this->iSimpleProgressColor);
+ $a->progress->SetFillColor($this->iSimpleProgressBkgColor);
+ //$a->progress->SetPattern($progress[$j][2],$progress[$j][3]);
+ break;
+ }
+ }
+ $csimpos = 6;
+ break;
+
+ case ACTYPE_MILESTONE:
+ $a = new MileStone($data[$i][0],$data[$i][2],$data[$i][3]);
+ $a->title->SetFont($this->iSimpleFont,FS_NORMAL,$this->iSimpleFontSize);
+ $csimpos = 5;
+ break;
+ default:
+ die('Unknown activity type');
+ break;
+ }
+
+ // Setup caption
+ $a->caption->Set($data[$i][$csimpos-1]);
+
+ // Check if this activity should have a CSIM target ?
+ if( !empty($data[$i][$csimpos]) ) {
+ $a->SetCSIMTarget($data[$i][$csimpos]);
+ $a->SetCSIMAlt($data[$i][$csimpos+1]);
+ }
+ if( !empty($data[$i][$csimpos+2]) ) {
+ $a->title->SetCSIMTarget($data[$i][$csimpos+2]);
+ $a->title->SetCSIMAlt($data[$i][$csimpos+3]);
+ }
+
+ $this->Add($a);
+ }
+ }
+
+
+ // Set what headers should be shown
+ function ShowHeaders($aFlg) {
+ $this->scale->ShowHeaders($aFlg);
+ }
+
+ // Specify the fraction of the font height that should be added
+ // as vertical margin
+ function SetLabelVMarginFactor($aVal) {
+ $this->iLabelVMarginFactor = $aVal;
+ }
+
+ // Synonym to the method above
+ function SetVMarginFactor($aVal) {
+ $this->iLabelVMarginFactor = $aVal;
+ }
+
+
+ // Add a new Gantt object
+ function Add($aObject) {
+ if( is_array($aObject) ) {
+ for($i=0; $i<count($aObject); ++$i)
+ $this->iObj[] = $aObject[$i];
+ }
+ else
+ $this->iObj[] = $aObject;
+ }
+
+ // Override inherit method from Graph and give a warning message
+ function SetScale() {
+ JpGraphError::Raise("SetScale() is not meaningfull with Gantt charts.");
+ }
+
+ // Specify the date range for Gantt graphs (if this is not set it will be
+ // automtically determined from the input data)
+ function SetDateRange($aStart,$aEnd) {
+ // Adjust the start and end so that the indicate the
+ // begining and end of respective start and end days
+ if( strpos($aStart,':') === false )
+ $aStart = date('Y-m-d 00:00',strtotime($aStart));
+ if( strpos($aEnd,':') === false )
+ $aEnd = date('Y-m-d 23:59',strtotime($aEnd));
+ $this->scale->SetRange($aStart,$aEnd);
+ }
+
+ // Get the maximum width of the activity titles columns for the bars
+ // The name is lightly misleading since we from now on can have
+ // multiple columns in the label section. When this was first written
+ // it only supported a single label, hence the name.
+ function GetMaxLabelWidth() {
+ $m=50;
+ if( $this->iObj != null ) {
+ $marg = $this->scale->actinfo->iLeftColMargin+$this->scale->actinfo->iRightColMargin;
+ $m = $this->iObj[0]->title->GetWidth($this->img)+$marg;
+ for($i=1; $i < count($this->iObj); ++$i) {
+ if( $this->iObj[$i]->title->HasTabs() ) {
+ list($tot,$w) = $this->iObj[$i]->title->GetWidth($this->img,true);
+ $m=max($m,$tot);
+ }
+ else
+ $m=max($m,$this->iObj[$i]->title->GetWidth($this->img));
+ }
+ }
+ return $m;
+ }
+
+ // Get the maximum height of the titles for the bars
+ function GetMaxLabelHeight() {
+ $m=0;
+ if( $this->iObj != null ) {
+ $m = $this->iObj[0]->title->GetHeight($this->img);
+ for($i=1; $i<count($this->iObj); ++$i) {
+ $m=max($m,$this->iObj[$i]->title->GetHeight($this->img));
+ }
+ }
+ return $m;
+ }
+
+ function GetMaxBarAbsHeight() {
+ $m=0;
+ if( $this->iObj != null ) {
+ $m = $this->iObj[0]->GetAbsHeight($this->img);
+ for($i=1; $i<count($this->iObj); ++$i) {
+ $m=max($m,$this->iObj[$i]->GetAbsHeight($this->img));
+ }
+ }
+ return $m;
+ }
+
+ // Get the maximum used line number (vertical position) for bars
+ function GetBarMaxLineNumber() {
+ $m=0;
+ if( $this->iObj != null ) {
+ $m = $this->iObj[0]->GetLineNbr();
+ for($i=1; $i<count($this->iObj); ++$i) {
+ $m=max($m,$this->iObj[$i]->GetLineNbr());
+ }
+ }
+ return $m;
+ }
+
+ // Get the minumum and maximum used dates for all bars
+ function GetBarMinMax() {
+ $max=$this->scale->NormalizeDate($this->iObj[0]->GetMaxDate());
+ $min=$this->scale->NormalizeDate($this->iObj[0]->GetMinDate());
+ for($i=1; $i < count($this->iObj); ++$i) {
+ $max=Max($max,$this->scale->NormalizeDate($this->iObj[$i]->GetMaxDate()));
+ $min=Min($min,$this->scale->NormalizeDate($this->iObj[$i]->GetMinDate()));
+ }
+ $minDate = date("Y-m-d",$min);
+ $min = strtotime($minDate);
+ $maxDate = date("Y-m-d 23:59",$max);
+ $max = strtotime($maxDate);
+ return array($min,$max);
+ }
+
+ // Create a new auto sized canvas if the user hasn't specified a size
+ // The size is determined by what scale the user has choosen and hence
+ // the minimum width needed to display the headers. Some margins are
+ // also added to make it better looking.
+ function AutoSize() {
+ if( $this->img->img == null ) {
+ // The predefined left, right, top, bottom margins.
+ // Note that the top margin might incease depending on
+ // the title.
+ $lm=30;$rm=30;$tm=20;$bm=30;
+ if( BRAND_TIMING ) $bm += 10;
+
+ // First find out the height
+ $n=$this->GetBarMaxLineNumber()+1;
+ $m=max($this->GetMaxLabelHeight(),$this->GetMaxBarAbsHeight());
+ $height=$n*((1+$this->iLabelVMarginFactor)*$m);
+
+ // Add the height of the scale titles
+ $h=$this->scale->GetHeaderHeight();
+ $height += $h;
+
+ // Calculate the top margin needed for title and subtitle
+ if( $this->title->t != "" ) {
+ $tm += $this->title->GetFontHeight($this->img);
+ }
+ if( $this->subtitle->t != "" ) {
+ $tm += $this->subtitle->GetFontHeight($this->img);
+ }
+
+ // ...and then take the bottom and top plot margins into account
+ $height += $tm + $bm + $this->scale->iTopPlotMargin + $this->scale->iBottomPlotMargin;
+ // Now find the minimum width for the chart required
+
+ // If day scale or smaller is shown then we use the day font width
+ // as the base size unit.
+ // If only weeks or above is displayed we use a modified unit to
+ // get a smaller image.
+
+ if( $this->scale->IsDisplayDay() || $this->scale->IsDisplayHour() ||
+ $this->scale->IsDisplayMinute() ) {
+ // Add 2 pixel margin on each side
+ $fw=$this->scale->day->GetFontWidth($this->img)+4;
+ }
+ elseif( $this->scale->IsDisplayWeek() ) {
+ $fw = 8;
+ }
+ elseif( $this->scale->IsDisplayMonth() ) {
+ $fw = 4;
+ }
+ else {
+ $fw = 2;
+ }
+ $nd=$this->scale->GetNumberOfDays();
+
+ if( $this->scale->IsDisplayDay() ) {
+ // If the days are displayed we also need to figure out
+ // how much space each day's title will require.
+ switch( $this->scale->day->iStyle ) {
+ case DAYSTYLE_LONG :
+ $txt = "Monday";
+ break;
+ case DAYSTYLE_LONGDAYDATE1 :
+ $txt = "Monday 23 Jun";
+ break;
+ case DAYSTYLE_LONGDAYDATE2 :
+ $txt = "Monday 23 Jun 2003";
+ break;
+ case DAYSTYLE_SHORT :
+ $txt = "Mon";
+ break;
+ case DAYSTYLE_SHORTDAYDATE1 :
+ $txt = "Mon 23/6";
+ break;
+ case DAYSTYLE_SHORTDAYDATE2 :
+ $txt = "Mon 23 Jun";
+ break;
+ case DAYSTYLE_SHORTDAYDATE3 :
+ $txt = "Mon 23";
+ break;
+ case DAYSTYLE_SHORTDATE1 :
+ $txt = "23/6";
+ break;
+ case DAYSTYLE_SHORTDATE2 :
+ $txt = "23 Jun";
+ break;
+ case DAYSTYLE_SHORTDATE3 :
+ $txt = "Mon 23";
+ break;
+ case DAYSTYLE_CUSTOM :
+ $txt = date($this->scale->day->iLabelFormStr,
+ strtotime('2003-12-20 18:00'));
+ break;
+ case DAYSTYLE_ONELETTER :
+ default:
+ $txt = "M";
+ break;
+ }
+ $fw = $this->scale->day->GetStrWidth($this->img,$txt)+6;
+ }
+
+ // If we have hours enabled we must make sure that each day has enough
+ // space to fit the number of hours to be displayed.
+ if( $this->scale->IsDisplayHour() ) {
+ // Depending on what format the user has choose we need different amount
+ // of space. We therefore create a typical string for the choosen format
+ // and determine the length of that string.
+ switch( $this->scale->hour->iStyle ) {
+ case HOURSTYLE_HMAMPM:
+ $txt = '12:00pm';
+ break;
+ case HOURSTYLE_H24:
+ // 13
+ $txt = '24';
+ break;
+ case HOURSTYLE_HAMPM:
+ $txt = '12pm';
+ break;
+ case HOURSTYLE_CUSTOM:
+ $txt = date($this->scale->hour->iLabelFormStr,strtotime('2003-12-20 18:00'));
+ break;
+ case HOURSTYLE_HM24:
+ default:
+ $txt = '24:00';
+ break;
+ }
+
+ $hfw = $this->scale->hour->GetStrWidth($this->img,$txt)+6;
+ $mw = $hfw;
+ if( $this->scale->IsDisplayMinute() ) {
+ // Depending on what format the user has choose we need different amount
+ // of space. We therefore create a typical string for the choosen format
+ // and determine the length of that string.
+ switch( $this->scale->minute->iStyle ) {
+ case HOURSTYLE_CUSTOM:
+ $txt2 = date($this->scale->minute->iLabelFormStr,strtotime('2005-05-15 18:55'));
+ break;
+ case MINUTESTYLE_MM:
+ default:
+ $txt2 = '15';
+ break;
+ }
+
+ $mfw = $this->scale->minute->GetStrWidth($this->img,$txt2)+6;
+ $n2 = ceil(60 / $this->scale->minute->GetIntervall() );
+ $mw = $n2 * $mfw;
+ }
+ $hfw = $hfw < $mw ? $mw : $hfw ;
+ $n = ceil(24*60 / $this->scale->TimeToMinutes($this->scale->hour->GetIntervall()) );
+ $hw = $n * $hfw;
+ $fw = $fw < $hw ? $hw : $fw ;
+ }
+
+ // We need to repeat this code block here as well.
+ // THIS iS NOT A MISTAKE !
+ // We really need it since we need to adjust for minutes both in the case
+ // where hour scale is shown and when it is not shown.
+
+ if( $this->scale->IsDisplayMinute() ) {
+ // Depending on what format the user has choose we need different amount
+ // of space. We therefore create a typical string for the choosen format
+ // and determine the length of that string.
+ switch( $this->scale->minute->iStyle ) {
+ case HOURSTYLE_CUSTOM:
+ $txt = date($this->scale->minute->iLabelFormStr,strtotime('2005-05-15 18:55'));
+ break;
+ case MINUTESTYLE_MM:
+ default:
+ $txt = '15';
+ break;
+ }
+
+ $mfw = $this->scale->minute->GetStrWidth($this->img,$txt)+6;
+ $n = ceil(60 / $this->scale->TimeToMinutes($this->scale->minute->GetIntervall()) );
+ $mw = $n * $mfw;
+ $fw = $fw < $mw ? $mw : $fw ;
+ }
+
+ // If we display week we must make sure that 7*$fw is enough
+ // to fit up to 10 characters of the week font (if the week is enabled)
+ if( $this->scale->IsDisplayWeek() ) {
+ // Depending on what format the user has choose we need different amount
+ // of space
+ $fsw = strlen($this->scale->week->iLabelFormStr);
+ if( $this->scale->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
+ $fsw += 8;
+ }
+ elseif( $this->scale->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ) {
+ $fsw += 7;
+ }
+ else {
+ $fsw += 4;
+ }
+
+ $ww = $fsw*$this->scale->week->GetFontWidth($this->img);
+ if( 7*$fw < $ww ) {
+ $fw = ceil($ww/7);
+ }
+ }
+
+ if( !$this->scale->IsDisplayDay() && !$this->scale->IsDisplayHour() &&
+ !( ($this->scale->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ||
+ $this->scale->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR) && $this->scale->IsDisplayWeek() ) ) {
+ // If we don't display the individual days we can shrink the
+ // scale a little bit. This is a little bit pragmatic at the
+ // moment and should be re-written to take into account
+ // a) What scales exactly are shown and
+ // b) what format do they use so we know how wide we need to
+ // make each scale text space at minimum.
+ $fw /= 2;
+ if( !$this->scale->IsDisplayWeek() ) {
+ $fw /= 1.8;
+ }
+ }
+
+ // Has the user specified a width or do we need to
+ // determine it?
+ if( $this->img->width <= 0 ) {
+ // Now determine the width for the activity titles column
+
+ // Firdst find out the maximum width of each object column
+ $cw = $this->GetMaxActInfoColWidth() ;
+ $this->scale->actinfo->SetMinColWidth($cw);
+ $titlewidth = max(max($this->GetMaxLabelWidth(),
+ $this->scale->tableTitle->GetWidth($this->img)),
+ $this->scale->actinfo->GetWidth($this->img));
+
+ // Add the width of the vertivcal divider line
+ $titlewidth += $this->scale->divider->iWeight*2;
+
+
+ // Now get the total width taking
+ // titlewidth, left and rigt margin, dayfont size
+ // into account
+ $width = $titlewidth + $nd*$fw + $lm+$rm;
+ }
+ else
+ $width = $this->img->width;
+
+ $this->img->CreateImgCanvas($width,$height);
+ $this->img->SetMargin($lm,$rm,$tm,$bm);
+ }
+ }
+
+ // Return an array width the maximum width for each activity
+ // column. This is used when we autosize the columns where we need
+ // to find out the maximum width of each column. In order to do that we
+ // must walk through all the objects, sigh...
+ function GetMaxActInfoColWidth() {
+ $n = count($this->iObj);
+ if( $n == 0 ) return;
+ $w = array();
+ $m = $this->scale->actinfo->iLeftColMargin + $this->scale->actinfo->iRightColMargin;
+
+ for( $i=0; $i < $n; ++$i ) {
+ $tmp = $this->iObj[$i]->title->GetColWidth($this->img,$m);
+ $nn = count($tmp);
+ for( $j=0; $j < $nn; ++$j ) {
+ if( empty($w[$j]) )
+ $w[$j] = $tmp[$j];
+ else
+ $w[$j] = max($w[$j],$tmp[$j]);
+ }
+ }
+ return $w;
+ }
+
+ // Stroke the gantt chart
+ function Stroke($aStrokeFileName="") {
+
+ // If the filename is the predefined value = '_csim_special_'
+ // we assume that the call to stroke only needs to do enough
+ // to correctly generate the CSIM maps.
+ // We use this variable to skip things we don't strictly need
+ // to do to generate the image map to improve performance
+ // a best we can. Therefor you will see a lot of tests !$_csim in the
+ // code below.
+ $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
+
+ // Should we autoscale dates?
+ if( !$this->scale->IsRangeSet() ) {
+ list($min,$max) = $this->GetBarMinMax();
+ $this->scale->SetRange($min,$max);
+ }
+
+ $this->scale->AdjustStartEndDay();
+
+ // Check if we should autoscale the image
+ $this->AutoSize();
+
+ // Should we start from the top or just spread the bars out even over the
+ // available height
+ $this->scale->SetVertLayout($this->iLayout);
+ if( $this->iLayout == GANTT_FROMTOP ) {
+ $maxheight=max($this->GetMaxLabelHeight(),$this->GetMaxBarAbsHeight());
+ $this->scale->SetVertSpacing($maxheight*(1+$this->iLabelVMarginFactor));
+ }
+ // If it hasn't been set find out the maximum line number
+ if( $this->scale->iVertLines == -1 )
+ $this->scale->iVertLines = $this->GetBarMaxLineNumber()+1;
+
+ $maxwidth=max($this->scale->actinfo->GetWidth($this->img),
+ max($this->GetMaxLabelWidth(),
+ $this->scale->tableTitle->GetWidth($this->img)));
+
+ $this->scale->SetLabelWidth($maxwidth+$this->scale->divider->iWeight);//*(1+$this->iLabelHMarginFactor));
+
+ if( !$_csim )
+ $this->StrokePlotArea();
+
+ $this->scale->Stroke();
+
+ if( !$_csim ) {
+ // Due to rounding we need to draw the box + pixel to the right
+ $this->img->right_margin--;
+ $this->StrokePlotBox();
+ $this->img->right_margin++;
+ }
+
+ $n = count($this->iObj);
+ for($i=0; $i < $n; ++$i) {
+ //$this->iObj[$i]->SetLabelLeftMargin(round($maxwidth*$this->iLabelHMarginFactor/2));
+ $this->iObj[$i]->Stroke($this->img,$this->scale);
+ }
+
+ if( !$_csim ) {
+ $this->StrokeConstrains();
+ $this->StrokeTitles();
+ $this->footer->Stroke($this->img);
+
+ // If the filename is given as the special "__handle"
+ // then the image handler is returned and the image is NOT
+ // streamed back
+ if( $aStrokeFileName == _IMG_HANDLER ) {
+ return $this->img->img;
+ }
+ else {
+ // Finally stream the generated picture
+ $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
+ $aStrokeFileName);
+ }
+ }
+ }
+
+ function StrokeConstrains() {
+ $n = count($this->iObj);
+
+ // Stroke all constrains
+ for($i=0; $i < $n; ++$i) {
+
+ $numConstrains = count($this->iObj[$i]->constraints);
+
+ for( $k = 0; $k < $numConstrains; $k++ ) {
+ $vpos = $this->iObj[$i]->constraints[$k]->iConstrainRow;
+ if( $vpos >= 0 ) {
+ $c1 = $this->iObj[$i]->iConstrainPos;
+
+ // Find out which object is on the target row
+ $targetobj = -1;
+ for( $j=0; $j < $n && $targetobj == -1; ++$j ) {
+ if( $this->iObj[$j]->iVPos == $vpos ) {
+ $targetobj = $j;
+ }
+ }
+ if( $targetobj == -1 ) {
+ JpGraphError::Raise('You have specifed a constrain from row='.
+ $this->iObj[$i]->iVPos.
+ ' to row='.$vpos.' which does not have any activity.');
+ exit();
+ }
+ $c2 = $this->iObj[$targetobj]->iConstrainPos;
+ if( count($c1) == 4 && count($c2 ) == 4) {
+ switch( $this->iObj[$i]->constraints[$k]->iConstrainType ) {
+ case CONSTRAIN_ENDSTART:
+ if( $c1[1] < $c2[1] ) {
+ $link = new GanttLink($c1[2],$c1[3],$c2[0],$c2[1]);
+ }
+ else {
+ $link = new GanttLink($c1[2],$c1[1],$c2[0],$c2[3]);
+ }
+ $link->SetPath(3);
+ break;
+ case CONSTRAIN_STARTEND:
+ if( $c1[1] < $c2[1] ) {
+ $link = new GanttLink($c1[0],$c1[3],$c2[2],$c2[1]);
+ }
+ else {
+ $link = new GanttLink($c1[0],$c1[1],$c2[2],$c2[3]);
+ }
+ $link->SetPath(0);
+ break;
+ case CONSTRAIN_ENDEND:
+ if( $c1[1] < $c2[1] ) {
+ $link = new GanttLink($c1[2],$c1[3],$c2[2],$c2[1]);
+ }
+ else {
+ $link = new GanttLink($c1[2],$c1[1],$c2[2],$c2[3]);
+ }
+ $link->SetPath(1);
+ break;
+ case CONSTRAIN_STARTSTART:
+ if( $c1[1] < $c2[1] ) {
+ $link = new GanttLink($c1[0],$c1[3],$c2[0],$c2[1]);
+ }
+ else {
+ $link = new GanttLink($c1[0],$c1[1],$c2[0],$c2[3]);
+ }
+ $link->SetPath(3);
+ break;
+ default:
+ JpGraphError::Raise('Unknown constrain type specified from row='.
+ $this->iObj[$i]->iVPos.
+ ' to row='.$vpos);
+ break;
+ }
+
+ $link->SetColor($this->iObj[$i]->constraints[$k]->iConstrainColor);
+ $link->SetArrow($this->iObj[$i]->constraints[$k]->iConstrainArrowSize,
+ $this->iObj[$i]->constraints[$k]->iConstrainArrowType);
+
+ $link->Stroke($this->img);
+ }
+ }
+ }
+ }
+ }
+
+ function GetCSIMAreas() {
+ if( !$this->iHasStroked )
+ $this->Stroke(_CSIM_SPECIALFILE);
+ $csim='';
+ $n = count($this->iObj);
+ for( $i=$n-1; $i >= 0; --$i )
+ $csim .= $this->iObj[$i]->GetCSIMArea();
+ return $csim;
+ }
+}
+
+//===================================================
+// CLASS PredefIcons
+// Description: Predefined icons for use with Gantt charts
+//===================================================
+DEFINE('GICON_WARNINGRED',0);
+DEFINE('GICON_TEXT',1);
+DEFINE('GICON_ENDCONS',2);
+DEFINE('GICON_MAIL',3);
+DEFINE('GICON_STARTCONS',4);
+DEFINE('GICON_CALC',5);
+DEFINE('GICON_MAGNIFIER',6);
+DEFINE('GICON_LOCK',7);
+DEFINE('GICON_STOP',8);
+DEFINE('GICON_WARNINGYELLOW',9);
+DEFINE('GICON_FOLDEROPEN',10);
+DEFINE('GICON_FOLDER',11);
+DEFINE('GICON_TEXTIMPORTANT',12);
+
+class PredefIcons {
+ var $iBuiltinIcon = null;
+ var $iLen = -1 ;
+
+ function GetLen() {
+ return $this->iLen ;
+ }
+
+ function GetImg($aIdx) {
+ if( $aIdx < 0 || $aIdx >= $this->iLen ) {
+ JpGraphError::Raise('Illegal icon index for Gantt builtin icon ['.$aIdx.']');
+ }
+ return Image::CreateFromString(base64_decode($this->iBuiltinIcon[$aIdx][1]));
+ }
+
+ function PredefIcons() {
+ //==========================================================
+ // warning.png
+ //==========================================================
+ $this->iBuiltinIcon[0][0]= 1043 ;
+ $this->iBuiltinIcon[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
+ 'B3RJTUUH0wgKFSgilWPhUQAAA6BJREFUeNrtl91rHFUYh5/3zMx+Z5JNUoOamCZNaqTZ6IWIkqRiQWmi1IDetHfeiCiltgXBP8AL'.
+ '0SIUxf/AvfRSBS9EKILFFqyIH9CEmFZtPqrBJLs7c+b1YneT3WTTbNsUFPLCcAbmzPt73o9zzgzs2Z793231UOdv3w9k9Z2uzOdA'.
+ '5+2+79yNeL7Hl7hw7oeixRMZ6PJM26W18DNAm/Vh7lR8fqh97NmMF11es1iFpMATqdirwMNA/J4DpIzkr5YsAF1PO6gIMYHRdPwl'.
+ 'oO2elmB+qH3sm7XozbkgYvy8SzYnZPtcblyM6I+5z3jQ+0vJfgpEu56BfI9vUkbyi2HZd1QJoeWRiAjBd4SDCW8SSAOy6wBHMzF7'.
+ 'YdV2A+ROuvRPLfHoiSU0EMY/cDAIhxJeGngKaN1VgHyPL7NBxI1K9P4QxBzw3K1zJ/zkG8B9uwaQ7/HNsRZv9kohBGD0o7JqMYS/'.
+ '/ynPidQw/LrBiPBcS/yFCT95DvB2BWAy4575PaQbQKW+tPd3GCItu2odKI++YxiKu0d26oWmAD7paZU/rLz37VqIijD2YbnzNBBE'.
+ 'IBHf8K8qjL7vYhCGErEU8CTg3xXAeMp96GrJEqkyXkm9Bhui1xfsunjdGhcYLq+IzjsGmBt5YH/cmJkFq6gIqlon3u4LxdKGuCIo'.
+ 'Qu41g0E41po+2R33Xt5uz9kRIB2UTle7PnfKrROP1HD4sRjZlq0lzhwoZ6rDNeTi3nEg1si/7FT7kYQbXS6E5E65tA5uRF9tutq0'.
+ 'K/VwAF+/FbIYWt6+tjQM/AqUms7A4Wy6d7YSfSNxgMmzi0ycWWworio4QJvj4LpuL5BqugTnXzzqJsJwurrlNhJXFaavW67NRw3F'.
+ 'q+aJcCQVe9fzvJGmAY7/dPH0gi0f64OveGxa+usCuQMeZ0+kt8BVrX+qPO9Bzx0MgqBvs+a2PfDdYIf+WAjXU1ub4tqNaPPzRs8A'.
+ 'blrli+WVn79cXn0cWKl+tGx7HLc7pu3CSmnfitL+l1UihAhwjFkPQev4K/fSABjBM8JCaFuurJU+rgW41SroA8aNMVNAFtgHJCsn'.
+ 'XGy/58QVxAC9MccJtZ5kIzNlW440WrJ2ea4YPA9cAooA7i0A/gS+iqLoOpB1HOegqrYB3UBmJrAtQAJwpwPr1Ry92wVlgZsiYlW1'.
+ 'uX1gU36dymgqYxJIJJNJT1W9QqHgNwFQBGYqo94OwHZQUuPD7ACglSvc+5n5T9m/wfJJX4U9qzEAAAAASUVORK5CYII=' ;
+
+ //==========================================================
+ // edit.png
+ //==========================================================
+ $this->iBuiltinIcon[1][0]= 959 ;
+ $this->iBuiltinIcon[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAFgAWABY9j+ZuwAAAAlwSFlz'.
+ 'AAALEAAACxABrSO9dQAAAAd0SU1FB9AKDAwbIEXOA6AAAAM8SURBVHicpdRPaBxlHMbx76ZvsmOTmm1dsEqQSIIsEmGVBAQjivEQ'.
+ 'PAUJngpWsAWlBw8egpQepKwplN4ULEG9CjkEyUFKlSJrWTG0IU51pCsdYW2ncUPjdtp9Z+f3vuNhu8nKbmhaf5cZeGc+PO8zf1Lc'.
+ 'm0KhkACICCKCMeaBjiLC0tLSnjNvPmuOHRpH0TZTU1M8zBi9wakzn7OFTs5sw8YYACYmJrre7HkeuVyu69qPF77hlT1XmZ0eQ03O'.
+ 'wOLJTvhBx1rLz18VmJ0eY+jVd2FxDkKXnvYLHgb97OgLzE4ON9Hzc1B1QaQzsed5O0Lta3Ec89OnR5h5McfQ+Mw2qgQUnfBOPbZ3'.
+ 'bK3l+xOvMT0+3ERLp5FNF6UEjcL32+DdVmGt5WLhDYYPZrbRqreFumXwql0S3w9tnDvLWD5PZigPpdOwuYpSCo3C8wU3UHxQdHbf'.
+ 'cZIkNM6dxcnlUM4k1eUFMlUPpUADbpkttFarHe6oYqeOr6yt4RzMQHYUcUsQVtGicHDwKprViuLDkkOtVnsHCHZVRVy/zcj1i5Af'.
+ 'h8AjdIts+hUcGcYPK3iBtKM3gD/uAzf/AdY2mmmVgy6X8YNNKmGIvyloPcB8SUin07RQ4EZHFdsdG0wkJEnEaHAJxvKEpSLeaokV'.
+ 'r4zWmhUZYLlY4b1D03y5eIEWCtS7vsciAgiIxkQRabWOrlQor66y4pUphoJb1jiO4uO5o0S3q6RSqVbiOmC7VCEgAhLSaDQ48dH7'.
+ 'vD46REY0iysegSjKQciRt99ib7qXwX0O+pG4teM6YKHLB9JMq4mTmF9/+AKA4wvLZByH7OgYL7+UY2qvw/7Bfg5kHiXjJFyv3CGO'.
+ 'Y1rof+BW4t/XLiPG0DCGr79d4XzRxRnIMn98huXSTYyJ6et1UNYQhRvcinpJq86H3wGPPPM0iBDd+QffD1g4eZjLvuG7S1Wef26E'.
+ 'J7L7eSx7gAHVg7V3MSbi6m/r93baBd6qQjerAJg/9Ql/XrvG0ON1+vv7GH3qSfY5fahUnSTpwZgIEQesaVXRPbHRG/xyJSAxMYlp'.
+ 'EOm71HUINiY7mGb95l/8jZCyQmJjMDGJjUmsdCROtZ0n/P/Z8v4Fs2MTUUf7vYoAAAAASUVORK5CYII=' ;
+
+ //==========================================================
+ // endconstrain.png
+ //==========================================================
+ $this->iBuiltinIcon[2][0]= 666 ;
+ $this->iBuiltinIcon[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlz'.
+ 'AAALDwAACw8BkvkDpQAAAAd0SU1FB9ALEREILkh0+eQAAAIXSURBVHictZU9aFNRFMd/N81HX77aptJUWmp1LHRpIcWhg5sIDlUQ'.
+ 'LAXB4t7RRUpwEhy7iQ46CCIoSHcl0CFaoVARU2MFMYktadLXJNok7x2HtCExvuYFmnO4w/3gx+Gc/z1HKRTdMEdXqHbB/sgc/sic'.
+ 'nDoYAI8XwDa8o1RMLT+2hAsigtTvbIGVqhX46szUifBGswUeCPgAGB7QeLk0X4Ork+HOxo1VgSqGASjMqkn8W4r4vVtEgI/RRQEL'.
+ 'vaoGD85cl5V3nySR/S1mxWxab7f35PnntNyMJeRr9kCMqiHTy09EoeToLwggx6ymiMOD/VwcD7Oa/MHkcIiQx026WGYto5P/U+ZZ'.
+ '7gD0QwDuT5z9N3LrVPi0Xs543eQPKkRzaS54eviJIp4tMFQFMllAWN2qcRZHBnixNM8NYD162xq8u7ePSQ+GX2Pjwxc2dB2cLtB8'.
+ '7GgamCb0anBYBeChMtl8855CarclxU1gvViiUK4w2OMkNDnGeJ8bt9fH90yOnOkCwLFTwhzykhvtYzOWoBBbY//R3dbaNTYhf2RO'.
+ 'QpeuUMzv188MlwuHy0H13HnE48UzMcL0WAtUHX8OxZHoG1URiFw7rnLLCswuSPD1ulze/iWjT2PSf+dBXRFtVVGIvzqph0pQL7VE'.
+ 'avXYaXXxPwsnt0imdttCocMmZBdK7YU9D8wuNOW0nXc6QWzPsSa5naZ1beb9BbGB6dxGtMnXAAAAAElFTkSuQmCC' ;
+
+ //==========================================================
+ // mail.png
+ //==========================================================
+ $this->iBuiltinIcon[3][0]= 1122 ;
+ $this->iBuiltinIcon[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlz'.
+ 'AAALEAAACxABrSO9dQAAAAd0SU1FB9AJHAMfFvL9OU8AAAPfSURBVHictZRdaBRXFMd/987H7tbNx8aYtGCrEexDsOBDaKHFxirb'.
+ 'h0qhsiY0ykppKq1osI99C4H2WSiFFMHWUhXBrjRi0uCmtSEUGgP1QWqhWjGkoW7M1kTX3WRn5p4+TJJNGolQ6IXDnDtz+N0z/3PP'.
+ 'UWBIpdpYa23b9g09PZ2kUrOrvmUyGVKp1Ao/mUyi56YnVgWfO/P1CihAd/dJMpmaNROIRq8BkM1m0bH6TasC3j6QXgFdXI+DR6PR'.
+ 'JX/Pno8B+KLnMKqlpUU8z8MYs2RBEDzWf9J+0RcRbMdxGBsbw/fmCXwPMUEYID4iAVp8wIRmDIHMo4yHSIBSASKC+CWE0C/PF9jU'.
+ '3B6Cp+4M07C5FUtKGNvGwQJctPgIsgD2wRhEIqAMGB+UQYkHJgYYZD7P1HwVlmWhHcfhyk83KeRGUW4t6CgoG5SNUS4KBWgQDUov'.
+ '7AGlwYASBVqH0Bk49dXpCviVV3dw/tI1Bvr7kMIIlh0NYUpjlF0BAYvcxSXmEVLKceHSCJm+PnbueBHbtkNwTXUNBzo6aGpq4sSZ'.
+ 'GwT5H7BsF6Wdf1GWHQAoM0upeI9PT1yioS7B7tdaSdSuw7KsUGMAy7HYsmUztTW1nMwM0txssX1rlHjjS5jy/Uq2YkK/eJuLl6/z'.
+ 'x+1xkslW6mrixGIODx8EFSlEBC0+tmXT0NhA2763iEUjnLv4C8XpUbSbAB1mKkGJ3J83Od77HW5EszvZSqK2iljMIeJaRGNuJePF'.
+ '6mspY7BJ1DXwQnCd2fxGRq5OUCz8xt72dyhMZcn++Cu3xu9SKhdp2b4ZHWnAtTSxmIWlhcIjlksR3lNBYzlxZsb7+f7ne+xtSzOd'.
+ 'u83szH1OnThOPp/n+a0beeP1l4mvq+PU2Qyd+5PY1RuwlAqLYFaBfbTbyPSdfgaH77A//QF4f1O/vpr6RJyq+C5Kc/M8FbFxXItY'.
+ 'xOHDrvfo/fxLDnbsJBp5BowBReVWYAzabeTh5ABDw7cWoNNL3YYYNtSv57lnn6Z+Qx01VeuIuBa2DV1HD3H63BAPZu4u1WGpeLHq'.
+ 'Rh7+NcjA0O+0p4+CNwXigwnbWlQQdpuEpli+n+PIkcOc//YKuckJJFh2K2anrjFw+QZt6S6kPImIF/b+cqAJD1LihWAxC61twBTo'.
+ 'fPcQF/oGsVW5ovHQlavs2/8+uYnRVSOUgHAmmAClBIOBwKC0gPjhIRgEIX2wg7NnwpZW3d3d4vs+vu8TBMGK51rvPM9b8hdteZxd'.
+ 'LBbVR8feJDs0Rlv6GFKeXJ21rNRXESxMPR+CBUl0nN7PjtO+dye7Up/8v1I88bf/ixT/AO1/hZsqW+C6AAAAAElFTkSuQmCC' ;
+
+ //==========================================================
+ // startconstrain.png
+ //==========================================================
+ $this->iBuiltinIcon[4][0]= 725 ;
+ $this->iBuiltinIcon[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlz'.
+ 'AAALDgAACw4BQL7hQQAAAAd0SU1FB9ALEREICJp5fBkAAAJSSURBVHic3dS9a1NRGMfx77kxtS+xqS9FG6p1ER3qVJpBQUUc3CRU'.
+ 'BwURVLB1EAuKIP0THJQiiNRJBK3iJl18AyeltRZa0bbaJMbUNmlNSm5e7s25j0NqpSSmyag/OMM9POdzDuflwn8djz8gClVRrVEV'.
+ 'ur4Bl1FTNSzLrSS6vbml0jUUwSXj8Qfk3PkLtLW2AeBIybmrgz3+gFzpucjlE4f4btuFTuWuCF5XDr3a3UPf6cM8GQvxzbsRAJdh'.
+ 'ScfxSywml5j7mVypN0eGEJ0tebIre+zxB6Tv7jPReS2hREpOvpmUXU+H5eC913JnNCSRVE60pUVbWoZjprR39Yq70bdqj4pW7PEH'.
+ '5FpvL9e79jOTTHM7ssDL6CJZ08LbvAGnrpZg2mI2Z/MlZfN8IkxuSwu4V9+WIrj7zFlOHfXzKrLIi2SGh5ECKjnNVNxkQEc55vOw'.
+ 'rb6O8JLFdHyJ+ayFElUeHvjwkfteL/V7fKTSkFvIQE4DoLI2Mz/muTkTApcBKIwaN8pwIUrKw+ajWwDknAO0d/r4zFaMuRS63sWm'.
+ 'RoOdm+vRIriUYjKexrQV+t1o0YEVwfZSVJmD/dIABJuO0LG3lRFx0GOfiAELE9OgCrfU0XnIp5FwGLEy5WEAOxlR5uN+ARhP7GN3'.
+ '5w7Gv4bQI2+xpt4jjv2nWBmIlcExE2vDAHYioszBZXw6CPE4ADoWVHmd/tuwlZR9eXYyoszBfpiNQqaAOU5+TXRN+DeeenADPT9b'.
+ 'EVgKVsutKPl0TGWGhwofoquaoKK4apsq/tH/e/kFwBMXLgAEKK4AAAAASUVORK5CYII=' ;
+
+ //==========================================================
+ // calc.png
+ //==========================================================
+ $this->iBuiltinIcon[5][0]= 589 ;
+ $this->iBuiltinIcon[5][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAA4AIwBbgMF12wAAAAlwSFlz'.
+ 'AAALEQAACxEBf2RfkQAAAAd0SU1FB9AHBxQeFsqn0wQAAAHKSURBVHicnZWff+RAGIef3U/gcOEgUAgUCgcLhYXCwsHBQeGgUDgs'.
+ 'FgMHB4VA/4Bg4XChWFgIFIqBwkJhsRAYeOGF+TQHmWSTTbKd9pU37/x45jvfTDITXEynAbdWKVQB0NazcVm0alcL4rJaRVzm+w/e'.
+ '3iwAkzbYRcnnYgI04GCvsxxSPabYaEdt2Ra6D0atcvvvDmyrMWBX1zPq2ircP/Tk98DiJtjV/fim6ziOCL6dDHZNhxQ3arIMsox4'.
+ 'vejleL2Ay9+jaw6A+4OSICG2cacGKhsGxg+CxeqAQS0Y7BYJvowq7iGMOhXHEfzpvpQkA9bLKgOgWKt+4Lo1mM9hs9m17QNsJ70P'.
+ 'Fjc/O52joogoX8MZKiBiAFxd9Z1vcj9wfSpUlDRNMcYQxzFpmnJ0FPH8nDe1MQaWSz9woQpWSZKEojDkeaWoKAyr1tlu+s48wfVx'.
+ 'u7n5i7jthmGIiEGcT+36PP+gFeJrxWLhb0UA/lb4ggGs1T0rZs0zwM/ZjNfilcIY5tutPxgOW3F6dUX464LrKILLiw+A7WErrl+2'.
+ 'rABG1EL/BilZP8DjU2uR4U+2E49P1Z8QJmNXUzl24A9GBT0IruCfi86d9x+D12RGzt+pNAAAAABJRU5ErkJggg==' ;
+
+ //==========================================================
+ // mag.png
+ //==========================================================
+ $this->iBuiltinIcon[6][0]= 1415 ;
+ $this->iBuiltinIcon[6][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlz'.
+ 'AAALDAAACwwBP0AiyAAAAAd0SU1FB9ALDxEWDY6Ul+UAAAUESURBVHicdZVrbFRFGIafsyyF0nalV1R6WiggaAptlzsr1OgEogmC'.
+ '0IgoBAsBgkIrBAPEhBj/AP6xRTCUFEwRI4jcgsitXMrFCJptJWvBNpXYbbXtbtttt6e7e86ec/yxadlCfZPJZDIz73zzzjfvR2VL'.
+ 'F7U+hf0HD2JduIzTFy6SlJRkPtkcDgdCCE65OxFC8NPV6wghyM7OptankJ2dzbSC5QghEEIgCSHog9PpNAF27dlN6miZuPgElB4/'.
+ 'nmY3O7ZtByA1NVUCkGWZweD1eklJScESTbqxuIjrd+/x6uIl5M19hSy7nfGOeUxf+g7VjU1sKi7C4/GYsiyz7tAJAD4/cRaA1tZW'.
+ 'AHIPnECUVGD1+/3U19ebG4uLeHf1akamjsIwoVnVCOvQEdLoVILYYmMo3PIxSBJflpSaDX5FAmju1QAYv/8k/s8+wLVxOU0jR2LZ'.
+ '8sMFAApWrCApbRRDrRZirBYSLBKaoRPQw3SFernf2sav7T0Ubt4KwL4FMwF4Vu8FoHBCKgCzDhwHwLIhZ7y5a89u4m2JhA0wTdDC'.
+ 'OrphEjJMNElCHxKDEjaobmvlfo/Krj27CQQCJsCGJW8C0KXqAMxMiosQA8hZWcTFx9OsaniDKh1qmG7VoFsL0x0K06kbeAMhWpRe'.
+ '/KpG+gwHAKUnz7Dz3BUMw6DK18nuw99wt0Nh6VdHI8RJicmETQgFg7SFwjSrGv+oKp6ghldV6dZ0ugJBlF6FmCESQ2w2AIqXLsan'.
+ 'BrFYLJTnTCBrdBqveeopWZiPFaBHUegJhegMqGgxEkHDwB/UaQ9rdIV06v0+TD2EEQjQFtAY0dsNgNvt5sialQAIIXh7wQKuVf6J'.
+ 'gTsSccPDWlQstClBGjr9eHpVWvUQncEwdYEedF8noQ4vmYmpZMTH0nTvDn25vLbrNmu7bvfnsYEbAMnhcPDgwQPzUo2LJusw/mhp'.
+ 'QwlHNO0KBAnoIfxtrcQMT2De1Mm891wyUzNlUlJSpIyMDBobGzlzr5rFM/Koq6vrP8ASGxsLwPmKcvIShjPGZiPOakE3VFB8hHwd'.
+ 'vJAxhrk5L7Ly+RQuH/sWgPdXrwFg/6HDFBUsIj09nehfbAWwPWOT9n5RYhqGwarNWxkRM5TRCfF4U1PQsDDJFk9uYhwXvzvKjm3b'.
+ 'KSsro3DJInNW5RXp7u2bAKSlpeH1esnPz6eqqgqLpmmcr3Fht9ulfaV7mZk1Bs+lM6T1djM9fhg5egDPpTNMy5TZsW07kydPYdWM'.
+ 'aXx96ixOp9O8cfUa80srmDpjOgAulytiQqZpMnvObLbt/JTtHxXj9/tRVdU0DGOAufRpevPDTeac0hJyc3NxOOawfv161lVWS6eX'.
+ 'z+9/UOCxu1VWVvaTRGv16NFfjB2bNeAQp9NpTpmSM4DcbrdL0WsGDKLRR+52uwe1yP8jb2lpYfikyY9t80n03UCWZeaXVjw1f+zs'.
+ 'Oen+/d+pqanhzp2fKSsrw+l0mi6XiyPl5ZGITdN8fAVJwjRNJEmi1qfw1kw7siyTnJxMe3s71dXV3GpoZO64DG41NPJylvxU5D/e'.
+ 'qJKsfWQD9IkaZ2RmUvr9aV4aGYcQgjfO3aWoYBF5eXm4ewIsu/CbdPz1aWb0/p1bNoOrQxlUiuiaFo3c3FyEEOx9+C9CCD6paaTW'.
+ 'p/TXyYkTJ0Xe59jf7QOyAKDWp/QXxcFQ61P4pT3ShBBcvnUHIQTjxmX19/8BCeVg+/GPpskAAAAASUVORK5CYII=' ;
+
+ //==========================================================
+ // lock.png
+ //==========================================================
+ $this->iBuiltinIcon[7][0]= 963 ;
+ $this->iBuiltinIcon[7][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlz'.
+ 'AAALCwAACwsBbQSEtwAAAAd0SU1FB9AKAw0XDmwMOwIAAANASURBVHic7ZXfS1t3GMY/3+PprI7aisvo2YU6h6ATA8JW4rrlsF4U'.
+ 'qiAsF9mhl0N2cYTRy9G/wptAYWPD9iJtRy5asDe7cYFmyjaXOLaMImOrmkRrjL9yTmIS3120JybWQgfb3R74wuc8Lzw858vLOUpE'.
+ 'OK6pqSm2trbY39+nu7tbPHYch7m5OcLhMIA67kWj0aMQEWk6tm17rNm2LSIie3t7ksvlJJ1OSyqVkls3Z8SyLMnlcqTTaVKpFLdu'.
+ 'zmBZVj1HeY2VUti2TSQSQSml2bZdi0QirK2tMT09zerqKtlslqGhISYnJ4nHv2N+foFsNquOe9FotLlxOBwmk8lgWRbhcFgymYxY'.
+ 'liUi0mqaJoAuIi2macrdO7fFsizx3to0Te7euV1vrXtXEgqFmJmZYWVlhXK5LB4/U9kwDL784kYV0A3DYHd3m4sXRymXywKoRi8U'.
+ 'Ch01DgQCJBIJLMsiEAhIIpHw2uLz+eqtYrEYIqKZpimxWEyCwaCMjY01zYPBIJpXqVQqsby8TLVabWKA/v5+RkZGMAyDrq4ulFKH'.
+ 'HsfjcWZnZ+ns7KTRqwcnk0mKxSKFQqGJlVKtruuSTCYB6O3trW9UI/v9/iZPB/j8s2HOnX0FgHfeXpeffnzK+fWf+fijvhLs0PtG'.
+ 'D/n1OJ9+MsrlSwb3733DwMCAt1EyPj6uACYmJp56168NU6nUqFSE9nZdPE7+WqC/r4NKTagcCJVqDaUUB5VDAA4Pa9x7sMLlSwan'.
+ 'WjRmv13D7/erpaWlo604qOp88OF7LC48rPNosMq5Th+Dgxd4/XyA1rbzADi7j8jnf2P++wdcvSr8MJ/i8eomAKlUqn41OsDAQDeD'.
+ 'g++yuPCwzm/2vU8+n2a7sMFfj79mp7BBuVzioFSiXHJx3SKuW2Rzy0Up9dxnQVvODALQerqNRn4ZKe0Mvtc6TpzpmqbxalcY9Ato'.
+ '2v06t515C73YQftZB9GLnDrt4LoujuPgOA4Ui+C6yOpXJwZrJ7r/gv4P/u+D9W7fLxTz+1ScQxrZ3atRLaVxdjbY2d184R6/sLHe'.
+ 'opHP7/Do90Ua+WWUyezzZHObP/7cfX54/dowE1d66s8TV3oE+Mfn+L/zb4XmHPjRG9YjAAAAAElFTkSuQmCC' ;
+
+ //==========================================================
+ // stop.png
+ //==========================================================
+ $this->iBuiltinIcon[8][0]= 889 ;
+ $this->iBuiltinIcon[8][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlz'.
+ 'AAALDwAACw8BkvkDpQAAAAd0SU1FB9AJDwEvNyD6M/0AAAL2SURBVHic1ZTLaxVnGIefb2bO5OScHJN4oWrFNqcUJYoUEgU3/Qf6'.
+ 'F7gwCkIrvdBLUtqqiLhSg9bgBduFSHZdiG5ctkJ3xRDbUFwUmghNzBDanPGMkzOX79LFJGPMOSd204U/+Bbzvd/78F4H/ieJdoad'.
+ 'pZKxRFszAI/DcP0HazXY22v+HB01kee1PA/v3zfnjx4xgGnHcNZe7OvuNj+cOEF1ZATv5nUA4jhBSgmADCVWo8Ge2Of9wb18P/G7'.
+ 'oUXmYi30zqlTVEdGWLh1g2D6MYlKkXGE0Vl8aa2GEB149+4xXSzyoOIw/mimiZV/DPb25pFOj13A9gOMEChhUEqhVYqWKUk9QAUp'.
+ 'sT/P4s8PmKlUmNhQaIJbkDVqBbpw6wZ2zUc4Nm+ePku5p4eOrgpueQOFUoVCVxcD4+N07dpF9+5tVJeWGPBjhvr7WF1zC8ASgtcP'.
+ 'H8a7eZ1odh4sh50nzwCw9ZNh3M4Stutiu0X2nB/LyjZ6lcIbVTpdQU/jWVPzLADM8+ZGBRdtC7wrF/O7bR99iu26VL86iU4SAH4b'.
+ 'Po5d6AQhstMSvGyI4wS5FJBKSRwnzF8byx/u+PjzzMF1mfryQ1K/jnCahqp1xEopjFLoNEFJSRJHzF799gWHqa+/QKcSUXBI609f'.
+ 'Al5W4teQSiHDOipNUKnMI13RvnOXAIEKQixvGWya98SC560MFwPiqEG86JM8q79Q06lvhnOndy5/B6GPCUOMUu3BQgg8z0M3GmBZ'.
+ 'iGJn3v2VmsqnfzNx7FDueODuj8ROCFpjtG5TCmOYv32bJ09msP0ISydMfnAUgF8/O45RAA6WTPjlvXcB+Gn7FuRf/zAnNX6x3ARe'.
+ 'PSdmqL+P/YHkwMGDOGWDZTlQcNBRhPEComgB/YeHfq2InF1kLlXUOkpMbio1bd7aATRD/X0M1lPeSlM2vt2X1XBZjZnpLG2tmZO6'.
+ 'LbQVOIcP+HG2UauH3xgwBqOz9Cc3l1tC24Fz+MvUDroeGNb5if9H/1dM/wLPCYMw9fryKgAAAABJRU5ErkJggg==' ;
+
+ //==========================================================
+ // error.png
+ //==========================================================
+ $this->iBuiltinIcon[9][0]= 541 ;
+ $this->iBuiltinIcon[9][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAAAaVBMVEX//////2Xy8mLl5V/Z2VvMzFi/v1WyslKlpU+ZmUyMjEh/'.
+ 'f0VyckJlZT9YWDxMTDjAwMDy8sLl5bnY2K/MzKW/v5yyspKlpYiYmH+MjHY/PzV/f2xycmJlZVlZWU9MTEXY2Ms/PzwyMjLFTjea'.
+ 'AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTCAkUMSj9wWSOAAABLUlEQVR4'.
+ '2s2U3ZKCMAxGjfzJanFAXFkUle/9H9JUKA1gKTN7Yy6YMjl+kNPK5rlZVSuxf1ZRnlZxFYAm93NnIKvR+MEHUgqBXx93wZGIUrSe'.
+ 'h+ctEgbpiMo3iQ4kioHCGxir/ZYUbr7AgPXs9bX0BCYM8vN/cPe8oQYzom3tVsSBMVHEoOJ5dm5F1RsIe9CtqGgRacCAkUvRtevT'.
+ 'e2pd6vOWF+gCuc/brcuhyARakBU9FgK5bUBWdHEH8tHpDsZnRTZQGzdLVvQ3CzyYZiTAmSIODEwzFCAdJopuvbpeZDisJ4pKEcjD'.
+ 'ijWPJhU1MjCo9dkYfiUVjQNTDKY6CVbR6A0niUSZjRwFanR0l9i/TyvGnFdqwStq5axMfDbyBksld/FUumvxS/Bd9VyJvQDWiiMx'.
+ 'iOsCHgAAAABJRU5ErkJggg==' ;
+
+ //==========================================================
+ // openfolder.png
+ //==========================================================
+ $this->iBuiltinIcon[10][0]= 2040 ;
+ $this->iBuiltinIcon[10][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEANAAtwClFht71AAAAAlwSFlz'.
+ 'AAALEAAACxABrSO9dQAAAAd0SU1FB9AKDQ4RIXMeaLcAAAd1SURBVHicxZd7jBXVHcc/58zcvTNzH8vusqw8FsTsKiCUUh5WBZXG'.
+ 'GkOptmqwNWsWLKXFGlEpzZI0AWNKSy0WhDS22gJKtWlTsSRqzYIuLGB2WVvDIwQMZQMsy2OFfdzde+/OnHP6x907vJaFpjb9JZM5'.
+ 'c85Mfp/f9/s7Jxn4P4e41gtSyp78WGvtfdEAcqDFYUOH9HS0NhGk9tPb/ilSyp789UUB2AMuqhQy3Uzm7HGkE6W3dTNZMRI3EcWO'.
+ 'jf9ClLmWBT3dzW8jUsevWHCG3UpWl+IkHSxnbDh/Mcz12NevBcuWXTmf6TjnXvJ88gDmVB3pw3+nt3UzHa1NqMzBS2zqPLGFjtMN'.
+ 'ZNr3XdW+qyqwZcFk76HX/tHWfuQvyO4W7qhaHwL8efkMRlRUpPv7rqD0RrJ+FgAjLy1a20OIxZJEEuNCRfIApj+om4bGM3u2/sYU'.
+ '9J41d8973f3Dhg1pISTV1dXXBRNJxPGFCzhou+DCQrScZOkktNaeDZjamgeZ9MgiYmVDccvHhjAzJw0NTh8/alyZMaVJicp0iTHj'.
+ 'JpgNv38tjWUhhGROdbUL9W5/MH5XCkjlcibi+KIop5LVHLKEu8A/f4r286doa9pGrGwYAAsfqbbH3b8MgO/Nqgy6WvdbbXHMkEFJ'.
+ '4xUOMVEvaTZu3BgmvF4Yk4hz9rO/Ulr5cE9owae/rcGxohSOuiWkC2IjcIqKyPZm+OmCH7GhoZEF077EEzVVweAbJ+riEeO0Ey8y'.
+ 'UubqOHn0AOgMwvf59txnBrSp9dgxKmf/+kIP1NY8SFk0jh5ajmNHAWg5b2E5EexojGHjbiVRMoRMNs0LC+Yz46vTuH3enN7BI8fr'.
+ 'qFdo0BoVZNC9aVSQ4fNjBzEmQJiARxb+/AqYPMAVB5FsPU5v37g9OxgLhe14ZM5/ju052E6MNZvf5pmHHuLmmWOkEysxUtpGAtme'.
+ 'dtHTflJkezqQto3jFRnLssyf1jydxiiM7zNnye/c3ZsqLu2BN5fcMfzrv/hby1tPzmRUoihcTJ87CwQI2yLtDcIqsIjYUf51qBlf'.
+ 'OnScOSrdQUOMURkiXsLUzJnvbGhoBGDHH5cGyZLhOpYoNl5hqYnYEXOu5fDl9eYAHntx98n8hFHZcPHUuTSxSASAeK/CGIOxJJ0f'.
+ 'bOGNPU280dgkq6Y2yu8vfjCIlwwzr+/ZQ/PHO0gOLuO5qsftDQ2NbN+4OCgqG6WTxWVaq6zpF+DiSHWnicdylp3r6aZTWthIOrNp'.
+ 'ktHcvBu0sHX1Sm6ozB3B42d90zZA9bQp7PvgPSzXZfnqX/HS4DKKK2+x69Y/HURs26iBAN5ccsfw7774UcumF37C6f07KSt2OHji'.
+ 'DEUJD0tISjyPrrSPlAKvN0JP/U4O1NfjuhG2rvklN1SOpfXwftpbTqAyKRrff5fb7rs9V1R7m4wlz2ihA3HpmXflUWyOH2umpLiY'.
+ 'ui3v8M+6bWzfsRNbSgqkxaCkiy0simMuEWEhpcRzIhQWOIAh6tiAwS4owInFiTou5dOnMnl2NR++ujBwXEc9terD6M43nrj6LgAB'.
+ 'QnDPA9/irtkP8JRS7Hr/3T6YekDQ1pEiEXOwpUVJzCVlZZFS4mZtkpEo9ChAkDp/jtLMBACy6S4RiQghLyv5cgBRPnKUOX6smUGF'.
+ 'hSil0MYw9d77mPy1e5mnFE3batm3czvb6nYgEJztSFGU9LCRlMRdUjIH0+lnEMIwPNXD3NumoVJnrMCJaiciMUZfvQnz4QcBSvV1'.
+ 'vjE5GK358t0zmXDnDB79saLpo20c+aSRD+t25JTp7GZQwsEWFiVxl6hlUf/WO9z32CxmL1rOe6u/I2KuwGhzLQCB7/sYY9Bah3el'.
+ 'FKbvrrVm4vS7GH/7ncx+chEHGz7myCeNbPtoO0JI2jq78WIRLGkzsqs7V5SfFV5EovXACoiqqsfNpk2vo5VCWtYFBfoU0VoTBAFa'.
+ 'a7TRaK2p+MoURk+cxMzq+Rzbv49DDbuo27UTW9h0dedssPxuK+kIfN8XxhgDYPVXf2Fh4XKtFIl4AiklAlBKAYRKKK36wHIweTCt'.
+ 'NfHiEkaOn8j0+7/BmDFjaT30GbHywSxcuZkpFfFg+m1jjZ/NmnVvNfRvwd69e8WBA/uNFAIh4JVXXmHsmDHE4vEQQgjQ2lxQIm9N'.
+ 'nz35q3BEOZOHzaG2thaA4mRU+L29It+IV21CpbRQfeMFC35gRB/M2rVrubnyZmLxWJhECBEmz/eHyo/7lMlH3LFFujsthNFCCGOu'.
+ '+WNyeUgpjSVzMKtWraKyshLPdcPEeYWCIEBdpIxSivr6eta8vI7d6+cGnhdV06pe1QP+F/QXWmuRL+jZZ58LlVmxYgUVFRV4rhtu'.
+ '4TzMxXAA6XRaRAtsYUkx8I/JtSJQOlSwpmZpCLN8+fPcdNNoHMfB9/0QJgRoP295TlR7UVv8xxZcHMuWIZ9/Hn35vG3JEGZpzVJG'.
+ 'jx5N1IlitKahsZE1L69j69qHgx+urFX/lQL9JYdLlfnZihUhzOLFi8N3Ml1dthOxVH/f/8/CtqSJ2JaJ2JZ59J7RPsC/AViJsQS/'.
+ 'dBntAAAAAElFTkSuQmCC' ;
+
+ //==========================================================
+ // folder.png
+ //==========================================================
+ $this->iBuiltinIcon[11][0]= 1824 ;
+ $this->iBuiltinIcon[11][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACIAAAAiCAYAAAA6RwvCAAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlz'.
+ 'AAALEAAACxABrSO9dQAAAAd0SU1FB9ECAQgFFyd9cRUAAAadSURBVHiczdhvbBP3Hcfx9/2xfefEOA5JoCNNnIT8AdtZmYBETJsI'.
+ '6+jQOlQihT1AYgytqzZpD1atfyYqlT1h0lRpT7aRJ4NQpRvZGELVuo5Ua9jEJDIETQsNQyPBsUJMWGPnj//e+e72wNg4xElMR6ed'.
+ 'ZNln3933dZ/f93f6yfB/sgmrHdDV1WXlPg8NDZUDScD8LFFFEZZlWYZhWMFg0Orq6sq/gDJAfFy1iiZy9OjrVnj4JzQ1rMWqfxm/'.
+ '309jYyNtbW0kEgnu3bvH4cOH88c/jqSKQl4/XGkd+eVtAN46up1LH92ktqYS++ZX8Pv9NDQ0sGnTJlKpFOFwmO7u7vy5IyMjeVRd'.
+ 'XV1+WEOh0IrY4pDnq6wXX/sTiCJaMkFZdRNqxefoe7VtCSqXVDqdZnZ2ltraWkzTpKqqijt3JpFlG7dvj7NzZ1f++qFQyA3EClHL'.
+ 'Ql743nFkhxPDtJAd5eTaYSVUfX09lZWVlJWVIUnSg7sVQMBCUcu4ceMGe/bsIRQK1QAzOcyykIM9P0KyudAyCWyqG8nhwqa4SkLt'.
+ '3r0bVVVxu924XC40TUOWZUQxe97CwgIdHR2LMHIxSCaVInVvFElxE0vMY1Pd2NUKJMWNTXHlUfF//4vETJCelwbpFm3MjP2dt37x'.
+ 'AlN+PzU1NViWRSwW4+7du3g8HjweD4qi5EFAJzAExIpCANbooxhplfB0FJvTg6xWIqsVRVF6MopkU3FXPcnkJxGU0VEAdF2noqKC'.
+ 'W3/8DpnqLjzep2lubsblcjE8PExHR8fboVDID9xYFpLBDpJF0jDQIncQpWlkm31FlFLtp9PfyuW/vYQj1kPSuRW/38+lj27S2Q7v'.
+ '/aWXUBVUffVNtm3blivVCEwsC5Eyc5iiApEpDEAXMqQdldhSiWVQHjJagud+8Fuexck/zv+K82dfoSbSCsDe75/km+4GVPd6+l5t'.
+ '4zJHcqVUYN2yEEtZQDCSJCueRAYsPY49HsFIZVG6p25JUumFafT4DKJN4amtT7Nz38sk5+5A70HMtEYyMkFiZhxzjQ/poXrLQrRU'.
+ 'DFGEeFpAlkQkm4pRiCpIKodKzk0T/2QMh+piPjxKZPwiSkUtu/b9mNnJEWS7E8nhAmvpM60oJDkXJxqNozxRRUxPIesispBBlsXV'.
+ 'UaKEFo8gzoaJhz8s2lOmrpUG+WBhJ9/60g+Z+fDXTAXfxllRjl1VkO0OFATsYhYliiK21ZKKhhHnFveUqSdKgwAEOp7F2v51vvw8'.
+ 'XH7/N1wd/BlTweuUV65BdtgfoLTSkipsdD3tRi0VYpommUwGwzDwdT5HYEc3giAwcvH3jLz3BlPB67jWeZBEKYsSBWwpHZtNKo4q'.
+ 'aHTDsJeeiGEYWJaFZVmYpommaRiGQdPnv0bb1m8gSRL/vPIOV979aR4lmAJ2p4qCgCxksNuKJ6VNpx4NYhgGpmkuQhmGQTqdxjAM'.
+ 'qr2d7HtxEEEQuH1tkKvvvkF44tqDnrIcKJKAPf1g+LAUElq8dIiu60sApmnm93Pfzc7OYhgGrie+wFe++ztcLhcT1wf54PzPCU9c'.
+ 'w7XWjWS3IdsdOAUBWZAxrRJnTQ6SG5bce2FCpmkughmGQSqVYm5uDtnj44sH38TtdhP6+Dwf//V4ttHXrkGURZJaic8RgHQ6jWma'.
+ 'SJKUL5RLKNfIOczDKF3XSSaTRCIRhLJWntp3nGfWrSMxc5OLf3iNP4+68T9Ub9nF76lTpxgfHycajZJKpdA0LZ9GbjYV7hcDWZaF'.
+ 'pmnMz88Ti8UYunSLmu1HFi2aVkxkaGjINTY2ttDb24vX6+XQoUNs3ryZ8vJyIDu1BUFYkkxhgxeiWlpaOHPmDE1NTdTX1xe98eWG'.
+ 'JnF/9dQZCoXUYDA4AOD1ejlw4ACtra2Ul5fniwmCkEcUJiUIAoFAgL6+Pnw+H21tbfT39z8SxCS7hHsfWH9/8dL4MKqnp4eWlhac'.
+ 'TmcekEvMNE2am5s5ceIEgUCA9vZ2Tp48ic/nY3j4UsmQHCYOjJHtpeBKqL1799Lc3IzT6UTXdRobGxkYGKC9vZ3W1tZ8Ko86NJ8a'.
+ 'tXHjRo4dO8bp06fZsmULGzZsoL+/n0AggNfr5ezZs/8VpGTU5OSkc//+/acBfD4f1dXV7Nq1i4aGBs6dO4fP5+Pq1SuPBbIiyjTN'.
+ 'RUnV1dUNXLhwAa/Xy44dO4jFYgBEo9FFF1r134BPuYlk16LrAYXsAlmtq6sbKDwoFAp9m+ykuP5ZQVZF3f8tCdwCov8LyHIoAANI'.
+ 'AXf/A1TI0XCDh7OWAAAAAElFTkSuQmCC' ;
+
+ //==========================================================
+ // file_important.png
+ //==========================================================
+ $this->iBuiltinIcon[12][0]= 1785 ;
+ $this->iBuiltinIcon[12][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACIAAAAiCAYAAAA6RwvCAAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlz'.
+ 'AAALDwAACw8BkvkDpQAAAAd0SU1FB9ECDAcjDeD3lKsAAAZ2SURBVHicrZhPaFzHHcc/897s7lutJCsr2VHsOHWMk0MPbsBUrcnF'.
+ 'OFRdSo6FNhdB6SGHlpDmYtJCDyoxyKe6EBxKQkt7KKL0T6ABo0NbciqigtC6PhWKI2NFqqxdSd7V2/dmftPDvPd212t55dCBYfbN'.
+ 'zpvfZ77z+/1mdhUjytWrV93Hf/24eD5z9gwiMlDjOKbb7dLtdhER2u02u7u73Lp1CxEZBw4AeZwdNQqkMd9wbziFGINJUt6rRbz5'.
+ '1ptUq1XK5TJBEAAUMHt7e+zu7gKwvLzMysoKwAng/uNg9CgQgFKlgg1DUJ67Vqtx6tQpZmdniaIIpRTOOZRSdDoddnZ2aLfbLC8v'.
+ 's7S0xJUrV7ZGwQSj1PhhfRodVdDlMrpc5vup5Z2fvMPdu3fZ29vDWjvwztjYGPV6nVqtRqVS4dKlSywtLQFsAdOH2XwsCEApg3jl'.
+ 'w98Rak2gvYjNZpNms0mSJDjnHgkDMDc3dySYQ0Ea8w139YUX0OUKulzyg7UmCEO+l1huvHuDra0t9vf3h1TJYSqVypFhHquIrlQI'.
+ 'S5qv/uIDAC7/4bcEQYAKvK+0Wq1DVQGIoog7d+4cCeaRII35hrt+8SsEOkRlUaEyR0UpFIrXHxyMVKVUKnHv3r0jwRwaNelBjBjL'.
+ 'Sz/7KYuLiwAsLi7y4z/9kY9e+TpkCuSqjI+Po7XuAWeKXLt2DWNMUZMkwRjDhQsXWFtbK6JpCCT3jfQgxomPtPX19YHWicM5x3c2'.
+ '73Pj3Ru8/aO3mZqaolKpoHVvyuvXr/Ppnf/Q7uzz380NPtu4y/qnG+ztd1hfX2dtbQ3gIvDnRyqSxl1UoPjyz98D4PTp0wPtq39Z'.
+ '4fdzLxegrVaLVqvF5OQkYRgWqpRKJZ77wvNsbW1RG5tgfKLOTH2G7Z1twqBQrgrMDvhInjfSOCY5iIv+hYWFgRZArEWsZWF941Bf'.
+ 'SdMUgMnJCWpjVU4cn+HUyePM1Gc4+fRUPkzBI5w1jbukcczLv/5l0XfmzJmBFuCba38r/CRXpT+CrDUoZ0jjB4RYonJAOYRobJKT'.
+ 'z5zgqfqxAbsFSH6mpHFM2qdGXh4VnoViD6mSJF2cTQeqDqBaKVHWmonJCWpZjhkC6anR5WsffTgwaHV1FaUUq6urA/2v3f5k4LnV'.
+ 'arG9tUn3oI2YBCcWHYAxMVYs1qZEZY2SFB2aYZDGfMN9d7uJiWPSeFiNo5Rclc3NTXZbO6RpF7EJVixYA9agwwDnUiqlEPdQ3imi'.
+ 'Jo27BGHIt/7x9yEjc3Nzh27Na7c/4TdffKl4bja3ae5MUIu0T/HOEIaOpJt4gwoSsVTK4SBIY77hFtY3ABBjBiZ90rKwvsH77/+K'.
+ 't37wOhO1iPpTk4SBw1mLsz6CnKQ4l3qV+kE+t9XHlNZOk+bUJLVIE1VCcIJWQmJ6qjj30NbcXLkZMt8YPig+Z3n1G5fZ39/j/vY2'.
+ '9ckqZT2Ochbn0p4qNkU/dDfUADdXbh4HXgRO4zNdEU0XL1784PLly5w9e7Z4SazFOfGrEotDcOKrcoJPmrYIXf/Zop3QNd1skuGt'.
+ 'cUAb2MgAxvHZTgFUq1Wmp6eZnZ0F8JlTjDduDThBnDeECEoJtbGIp6enqEblzCcEZ1PECU4yVRiOGgd0gc+AB0CZvkv1sWPHOHfu'.
+ 'HOfPn8da41cpkkltEBEPJhYnBkTQJcdYVKGkgRxCfBsq5xXNgAa2Bn+hjTOgHEKBP8pzRUxykIH4ifLJRTJAl+UMBJzPHQ6bfe/f'.
+ 'cWIzPxlUpD+zugzIZtVk1d8znBAqRxgoQuVQgSJQ3h9C5QhDRYgjUILCAzlnEdsHYTKfMTEBcP7F54YUGVmc2GLlIn6ve6v0ahSt'.
+ '8X25TzjJ+rIx1grKpQPWR4LkGVVsMgghvS0qjPdvm5OeceOTWA5Evo2mFzkjQfL7hZPUy5yvvF/uPFQL3+nbDmsLCEmT3sTmCTNr'.
+ 'rogT6yFsOix3ftw7OwQhkvSU6CuinhCk0+kAkFoBazEEICHaHHiPVmU0gnUp4EAc1mYrF0EBVpwPi34VrBkwPxKk3W5ju/e5/c+d'.
+ 'bGUHIAIuydTIE5zfc5Wr4lJcahHnHTP3CVGm78DrgY38N+DEibp7dmYKdAQmBh1hjEFjis+9CTWYGK21H6PxPyOI0DobYwzZF/z7'.
+ '7jadTvJtYG0kCD7lfwl49ijgT1gc0AH+dZSJA/xB+Mz/GSIvFoj/B7H1mAd8CO/zAAAAAElFTkSuQmCC' ;
+
+ $this->iLen = count($this->iBuiltinIcon);
+ }
+}
+
+//===================================================
+// Global cache for builtin images
+//===================================================
+$_gPredefIcons = new PredefIcons();
+
+//===================================================
+// CLASS IconImage
+// Description: Holds properties for an icon image
+//===================================================
+class IconImage {
+ var $iGDImage=null;
+ var $iWidth,$iHeight;
+ var $ixalign='left',$iyalign='center';
+ var $iScale=1.0;
+
+ function IconImage($aIcon,$aScale=1) {
+ GLOBAL $_gPredefIcons ;
+ if( is_string($aIcon) ) {
+ $this->iGDImage = Graph::LoadBkgImage('',$aIcon);
+ }
+ elseif( is_integer($aIcon) ) {
+ // Builtin image
+ $this->iGDImage = $_gPredefIcons->GetImg($aIcon);
+ }
+ else {
+ JpGraphError::Raise('Argument to IconImage must be string or integer');
+ }
+ $this->iScale = $aScale;
+ $this->iWidth = Image::GetWidth($this->iGDImage);
+ $this->iHeight = Image::GetHeight($this->iGDImage);
+ }
+
+ function GetWidth() {
+ return round($this->iScale*$this->iWidth);
+ }
+
+ function GetHeight() {
+ return round($this->iScale*$this->iHeight);
+ }
+
+ function SetAlign($aX='left',$aY='center') {
+
+ $this->ixalign = $aX;
+ $this->iyalign = $aY;
+
+ }
+
+ function Stroke($aImg,$x,$y) {
+
+ if( $this->ixalign == 'right' ) {
+ $x -= $this->iWidth;
+ }
+ elseif( $this->ixalign == 'center' ) {
+ $x -= round($this->iWidth/2*$this->iScale);
+ }
+
+ if( $this->iyalign == 'bottom' ) {
+ $y -= $this->iHeight;
+ }
+ elseif( $this->iyalign == 'center' ) {
+ $y -= round($this->iHeight/2*$this->iScale);
+ }
+
+ $aImg->Copy($this->iGDImage,
+ $x,$y,0,0,
+ round($this->iWidth*$this->iScale),round($this->iHeight*$this->iScale),
+ $this->iWidth,$this->iHeight);
+ }
+}
+
+
+//===================================================
+// CLASS TextProperty
+// Description: Holds properties for a text
+//===================================================
+class TextProperty {
+ var $iFFamily=FF_FONT1,$iFStyle=FS_NORMAL,$iFSize=10;
+ var $iColor="black";
+ var $iShow=true;
+ var $iText="";
+ var $iHAlign="left",$iVAlign="bottom";
+ var $csimtarget='',$csimalt='';
+
+//---------------
+// CONSTRUCTOR
+ function TextProperty($aTxt='') {
+ $this->iText = $aTxt;
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Set($aTxt) {
+ $this->iText = $aTxt;
+ }
+
+ function SetCSIMTarget($aTarget,$aAltText='') {
+ if( is_string($aTarget) )
+ $aTarget = array($aTarget);
+ $this->csimtarget=$aTarget;
+ if( is_string($aAltText) )
+ $aAltText = array($aAltText);
+ $this->csimalt=$aAltText;
+ }
+
+ function SetCSIMAlt($aAlt) {
+ $this->csimalt=$aAlt;
+ }
+
+ // Set text color
+ function SetColor($aColor) {
+ $this->iColor = $aColor;
+ }
+
+ function HasTabs() {
+ if( is_string($this->iText) ) {
+ return substr_count($this->iText,"\t") > 0;
+ }
+ elseif( is_array($this->iText) ) {
+ return false;
+ }
+ }
+
+ // Get number of tabs in string
+ function GetNbrTabs() {
+ if( is_string($this->iText) ) {
+ return substr_count($this->iText,"\t") ;
+ }
+ else{
+ return 0;
+ }
+ }
+
+ // Set alignment
+ function Align($aHAlign,$aVAlign="bottom") {
+ $this->iHAlign=$aHAlign;
+ $this->iVAlign=$aVAlign;
+ }
+
+ // Synonym
+ function SetAlign($aHAlign,$aVAlign="bottom") {
+ $this->iHAlign=$aHAlign;
+ $this->iVAlign=$aVAlign;
+ }
+
+ // Specify font
+ function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
+ $this->iFFamily = $aFFamily;
+ $this->iFStyle = $aFStyle;
+ $this->iFSize = $aFSize;
+ }
+
+ function IsColumns() {
+ return is_array($this->iText) ;
+ }
+
+ // Get width of text. If text contains several columns separated by
+ // tabs then return both the total width as well as an array with a
+ // width for each column.
+ function GetWidth($aImg,$aUseTabs=false,$aTabExtraMargin=1.1) {
+ $errmsg = 'Unknown type in Gantt object title specification';
+ $extra_margin=4;
+ $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ if( is_string($this->iText) ) {
+ if( strlen($this->iText) == 0 ) return 0;
+ $tmp = split("\t",$this->iText);
+ if( count($tmp) <= 1 || !$aUseTabs ) {
+ return $aImg->GetTextWidth($this->iText)+2*$extra_margin;
+ }
+ else {
+ $tot=0;
+ for($i=0; $i < count($tmp); ++$i) {
+ $res[$i] = $aImg->GetTextWidth($tmp[$i]);
+ $tot += $res[$i]*$aTabExtraMargin;
+ }
+ return array(round($tot),$res);
+ }
+ }
+ elseif( is_object($this->iText) ) {
+ // A single icon
+ return $this->iText->GetWidth()+2*$extra_margin;
+ }
+ elseif( is_array($this->iText) ) {
+ // Must be an array of texts. In this case we return the sum of the
+ // length + a fixed margin of 4 pixels on each text string
+ $n = count($this->iText);
+ for( $i=0, $w=0; $i < $n; ++$i ) {
+ $tmp = $this->iText[$i];
+ if( is_string($tmp) ) {
+ $w += $aImg->GetTextWidth($tmp)+$extra_margin;
+ }
+ else {
+ if( is_object($tmp) === false ) {
+ JpGraphError::Raise($errmsg);
+ }
+ $w += $tmp->GetWidth()+$extra_margin;
+ }
+ }
+ return $w;
+ }
+ else {
+ JpGraphError::Raise($errmsg);
+ }
+ }
+
+ // for the case where we have multiple columns this function returns the width of each
+ // column individually. If there is no columns just return the width of the single
+ // column as an array of one
+ function GetColWidth($aImg,$aMargin=0) {
+ $errmsg = 'Unknown type in Gantt object title specification';
+ $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ if( is_array($this->iText) ) {
+ $n = count($this->iText);
+ for( $i=0, $w=array(); $i < $n; ++$i ) {
+ $tmp = $this->iText[$i];
+ if( is_string($tmp) ) {
+ $w[$i] = $aImg->GetTextWidth($this->iText[$i])+$aMargin;
+ }
+ else {
+ if( is_object($tmp) === false ) {
+ JpGraphError::Raise($errmsg);
+ }
+ $w[$i] = $tmp->GetWidth()+$aMargin;
+ }
+ }
+ return $w;
+ }
+ else {
+ return array($this->GetWidth($aImg));
+ }
+ }
+
+ // Get total height of text
+ function GetHeight($aImg) {
+ $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ return $aImg->GetFontHeight();
+ }
+
+ // Unhide/hide the text
+ function Show($aShow=true) {
+ $this->iShow=$aShow;
+ }
+
+ // Stroke text at (x,y) coordinates. If the text contains tabs then the
+ // x parameter should be an array of positions to be used for each successive
+ // tab mark. If no array is supplied then the tabs will be ignored.
+ function Stroke($aImg,$aX,$aY) {
+ if( $this->iShow ) {
+ $aImg->SetColor($this->iColor);
+ $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ $aImg->SetTextAlign($this->iHAlign,$this->iVAlign);
+ if( $this->GetNbrTabs() <= 1 ) {
+ if( is_string($this->iText) ) {
+ // Get rid of any "\t" characters and stroke string
+ if( is_array($aX) ) $aX=$aX[0];
+ if( is_array($aY) ) $aY=$aY[0];
+ $aImg->StrokeText($aX,$aY,str_replace("\t"," ",$this->iText));
+ }
+ else {
+ $n = count($this->iText);
+ $ax = is_array($aX) ;
+ $ay = is_array($aY) ;
+ if( $ax && $ay ) {
+ // Nothing; both are already arrays
+ }
+ elseif( $ax ) {
+ $aY = array_fill(0,$n,$aY);
+ }
+ elseif( $ay ) {
+ $aX = array_fill(0,$n,$aX);
+ }
+ else {
+ $aX = array_fill(0,$n,$aX);
+ $aY = array_fill(0,$n,$aY);
+ }
+ $n = min($n, count($aX) ) ;
+ $n = min($n, count($aY) ) ;
+ for($i=0; $i < $n; ++$i ) {
+ $tmp = $this->iText[$i];
+ if( is_object($tmp) ) {
+ $tmp->Stroke($aImg,$aX[$i],$aY[$i]);
+ }
+ else
+ $aImg->StrokeText($aX[$i],$aY[$i],str_replace("\t"," ",$tmp));
+ }
+ }
+ }
+ else {
+ $tmp = split("\t",$this->iText);
+ $n = min(count($tmp),count($aX));
+ for($i=0; $i < $n; ++$i) {
+ $aImg->StrokeText($aX[$i],$aY,$tmp[$i]);
+ }
+ }
+ }
+ }
+}
+
+//===================================================
+// CLASS HeaderProperty
+// Description: Data encapsulating class to hold property
+// for each type of the scale headers
+//===================================================
+class HeaderProperty {
+ var $iTitleVertMargin=3,$iFFamily=FF_FONT0,$iFStyle=FS_NORMAL,$iFSize=8;
+ var $iFrameColor="black",$iFrameWeight=1;
+ var $iShowLabels=true,$iShowGrid=true;
+ var $iBackgroundColor="white";
+ var $iWeekendBackgroundColor="lightgray",$iSundayTextColor="red"; // these are only used with day scale
+ var $iTextColor="black";
+ var $iLabelFormStr="%d";
+ var $grid,$iStyle=0;
+ var $iIntervall = 1;
+
+//---------------
+// CONSTRUCTOR
+ function HeaderProperty() {
+ $this->grid = new LineProperty();
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Show($aShow=true) {
+ $this->iShowLabels = $aShow;
+ }
+
+ function SetIntervall($aInt) {
+ $this->iIntervall = $aInt;
+ }
+
+ function GetIntervall() {
+ return $this->iIntervall ;
+ }
+
+ function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
+ $this->iFFamily = $aFFamily;
+ $this->iFStyle = $aFStyle;
+ $this->iFSize = $aFSize;
+ }
+
+ function SetFontColor($aColor) {
+ $this->iTextColor = $aColor;
+ }
+
+ function GetFontHeight($aImg) {
+ $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ return $aImg->GetFontHeight();
+ }
+
+ function GetFontWidth($aImg) {
+ $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ return $aImg->GetFontWidth();
+ }
+
+ function GetStrWidth($aImg,$aStr) {
+ $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ return $aImg->GetTextWidth($aStr);
+ }
+
+ function SetStyle($aStyle) {
+ $this->iStyle = $aStyle;
+ }
+
+ function SetBackgroundColor($aColor) {
+ $this->iBackgroundColor=$aColor;
+ }
+
+ function SetFrameWeight($aWeight) {
+ $this->iFrameWeight=$aWeight;
+ }
+
+ function SetFrameColor($aColor) {
+ $this->iFrameColor=$aColor;
+ }
+
+ // Only used by day scale
+ function SetWeekendColor($aColor) {
+ $this->iWeekendBackgroundColor=$aColor;
+ }
+
+ // Only used by day scale
+ function SetSundayFontColor($aColor) {
+ $this->iSundayTextColor=$aColor;
+ }
+
+ function SetTitleVertMargin($aMargin) {
+ $this->iTitleVertMargin=$aMargin;
+ }
+
+ function SetLabelFormatString($aStr) {
+ $this->iLabelFormStr=$aStr;
+ }
+
+ function SetFormatString($aStr) {
+ $this->SetLabelFormatString($aStr);
+ }
+
+
+}
+
+//===================================================
+// CLASS GanttScale
+// Description: Responsible for calculating and showing
+// the scale in a gantt chart. This includes providing methods for
+// converting dates to position in the chart as well as stroking the
+// date headers (days, week, etc).
+//===================================================
+class GanttScale {
+ var $minute,$hour,$day,$week,$month,$year;
+ var $divider,$dividerh,$tableTitle;
+ var $iStartDate=-1,$iEndDate=-1;
+ // Number of gantt bar position (n.b not necessariliy the same as the number of bars)
+ // we could have on bar in position 1, and one bar in position 5 then there are two
+ // bars but the number of bar positions is 5
+ var $iVertLines=-1;
+ // The width of the labels (defaults to the widest of all labels)
+ var $iLabelWidth;
+ // Out image to stroke the scale to
+ var $iImg;
+ var $iTableHeaderBackgroundColor="white",$iTableHeaderFrameColor="black";
+ var $iTableHeaderFrameWeight=1;
+ var $iAvailableHeight=-1,$iVertSpacing=-1,$iVertHeaderSize=-1;
+ var $iDateLocale;
+ var $iVertLayout=GANTT_EVEN;
+ var $iTopPlotMargin=10,$iBottomPlotMargin=15;
+ var $iUsePlotWeekendBackground=true;
+ var $iWeekStart = 1; // Default to have weekends start on Monday
+ var $actinfo;
+
+//---------------
+// CONSTRUCTOR
+ function GanttScale(&$aImg) {
+ $this->iImg = &$aImg;
+ $this->iDateLocale = new DateLocale();
+
+ $this->minute = new HeaderProperty();
+ $this->minute->SetIntervall(15);
+ $this->minute->SetLabelFormatString('i');
+ $this->minute->SetFont(FF_FONT0);
+ $this->minute->grid->SetColor("gray");
+
+ $this->hour = new HeaderProperty();
+ $this->hour->SetFont(FF_FONT0);
+ $this->hour->SetIntervall(6);
+ $this->hour->SetStyle(HOURSTYLE_HM24);
+ $this->hour->SetLabelFormatString('H:i');
+ $this->hour->grid->SetColor("gray");
+
+ $this->day = new HeaderProperty();
+ $this->day->grid->SetColor("gray");
+ $this->day->SetLabelFormatString('l');
+
+ $this->week = new HeaderProperty();
+ $this->week->SetLabelFormatString("w%d");
+ $this->week->SetFont(FF_FONT1);
+
+ $this->month = new HeaderProperty();
+ $this->month->SetFont(FF_FONT1,FS_BOLD);
+
+ $this->year = new HeaderProperty();
+ $this->year->SetFont(FF_FONT1,FS_BOLD);
+
+ $this->divider=new LineProperty();
+ $this->dividerh=new LineProperty();
+ $this->dividerh->SetWeight(2);
+ $this->divider->SetWeight(6);
+ $this->divider->SetColor('gray');
+ $this->divider->SetStyle('fancy');
+
+ $this->tableTitle=new TextProperty();
+ $this->tableTitle->Show(false);
+ $this->actinfo = new GanttActivityInfo();
+ }
+
+//---------------
+// PUBLIC METHODS
+ // Specify what headers should be visible
+ function ShowHeaders($aFlg) {
+ $this->day->Show($aFlg & GANTT_HDAY);
+ $this->week->Show($aFlg & GANTT_HWEEK);
+ $this->month->Show($aFlg & GANTT_HMONTH);
+ $this->year->Show($aFlg & GANTT_HYEAR);
+ $this->hour->Show($aFlg & GANTT_HHOUR);
+ $this->minute->Show($aFlg & GANTT_HMIN);
+
+ // Make some default settings of gridlines whihc makes sense
+ if( $aFlg & GANTT_HWEEK ) {
+ $this->month->grid->Show(false);
+ $this->year->grid->Show(false);
+ }
+ if( $aFlg & GANTT_HHOUR ) {
+ $this->day->grid->SetColor("black");
+ }
+ }
+
+ // Should the weekend background stretch all the way down in the plotarea
+ function UseWeekendBackground($aShow) {
+ $this->iUsePlotWeekendBackground = $aShow;
+ }
+
+ // Have a range been specified?
+ function IsRangeSet() {
+ return $this->iStartDate!=-1 && $this->iEndDate!=-1;
+ }
+
+ // Should the layout be from top or even?
+ function SetVertLayout($aLayout) {
+ $this->iVertLayout = $aLayout;
+ }
+
+ // Which locale should be used?
+ function SetDateLocale($aLocale) {
+ $this->iDateLocale->Set($aLocale);
+ }
+
+ // Number of days we are showing
+ function GetNumberOfDays() {
+ return round(($this->iEndDate-$this->iStartDate)/SECPERDAY);
+ }
+
+ // The width of the actual plot area
+ function GetPlotWidth() {
+ $img=$this->iImg;
+ return $img->width - $img->left_margin - $img->right_margin;
+ }
+
+ // Specify the width of the titles(labels) for the activities
+ // (This is by default set to the minimum width enought for the
+ // widest title)
+ function SetLabelWidth($aLabelWidth) {
+ $this->iLabelWidth=$aLabelWidth;
+ }
+
+ // Which day should the week start?
+ // 0==Sun, 1==Monday, 2==Tuesday etc
+ function SetWeekStart($aStartDay) {
+ $this->iWeekStart = $aStartDay % 7;
+
+ //Recalculate the startday since this will change the week start
+ $this->SetRange($this->iStartDate,$this->iEndDate);
+ }
+
+ // Do we show min scale?
+ function IsDisplayMinute() {
+ return $this->minute->iShowLabels;
+ }
+
+ // Do we show day scale?
+ function IsDisplayHour() {
+ return $this->hour->iShowLabels;
+ }
+
+
+ // Do we show day scale?
+ function IsDisplayDay() {
+ return $this->day->iShowLabels;
+ }
+
+ // Do we show week scale?
+ function IsDisplayWeek() {
+ return $this->week->iShowLabels;
+ }
+
+ // Do we show month scale?
+ function IsDisplayMonth() {
+ return $this->month->iShowLabels;
+ }
+
+ // Do we show year scale?
+ function IsDisplayYear() {
+ return $this->year->iShowLabels;
+ }
+
+ // Specify spacing (in percent of bar height) between activity bars
+ function SetVertSpacing($aSpacing) {
+ $this->iVertSpacing = $aSpacing;
+ }
+
+ // Specify scale min and max date either as timestamp or as date strings
+ // Always round to the nearest week boundary
+ function SetRange($aMin,$aMax) {
+ $this->iStartDate = $this->NormalizeDate($aMin);
+ $this->iEndDate = $this->NormalizeDate($aMax);
+ }
+
+
+ // Adjust the start and end date so they fit to beginning/ending
+ // of the week taking the specified week start day into account.
+ function AdjustStartEndDay() {
+
+ if( !($this->IsDisplayYear() ||$this->IsDisplayMonth() || $this->IsDisplayWeek()) ) {
+ // Don't adjust
+ return;
+ }
+
+ // Get day in week for start and ending date (Sun==0)
+ $ds=strftime("%w",$this->iStartDate);
+ $de=strftime("%w",$this->iEndDate);
+
+ // We want to start on iWeekStart day. But first we subtract a week
+ // if the startdate is "behind" the day the week start at.
+ // This way we ensure that the given start date is always included
+ // in the range. If we don't do this the nearest correct weekday in the week
+ // to start at might be later than the start date.
+ if( $ds < $this->iWeekStart )
+ $d = strtotime('-7 day',$this->iStartDate);
+ else
+ $d = $this->iStartDate;
+ $adjdate = strtotime(($this->iWeekStart-$ds).' day',$d /*$this->iStartDate*/ );
+ $this->iStartDate = $adjdate;
+
+ // We want to end on the last day of the week
+ $preferredEndDay = ($this->iWeekStart+6)%7;
+ if( $preferredEndDay != $de ) {
+ // Solve equivalence eq: $de + x ~ $preferredDay (mod 7)
+ $adj = (7+($preferredEndDay - $de)) % 7;
+ $adjdate = strtotime("+$adj day",$this->iEndDate);
+ $this->iEndDate = $adjdate;
+ }
+ }
+
+ // Specify background for the table title area (upper left corner of the table)
+ function SetTableTitleBackground($aColor) {
+ $this->iTableHeaderBackgroundColor = $aColor;
+ }
+
+///////////////////////////////////////
+// PRIVATE Methods
+
+ // Determine the height of all the scale headers combined
+ function GetHeaderHeight() {
+ $img=$this->iImg;
+ $height=1;
+ if( $this->minute->iShowLabels ) {
+ $height += $this->minute->GetFontHeight($img);
+ $height += $this->minute->iTitleVertMargin;
+ }
+ if( $this->hour->iShowLabels ) {
+ $height += $this->hour->GetFontHeight($img);
+ $height += $this->hour->iTitleVertMargin;
+ }
+ if( $this->day->iShowLabels ) {
+ $height += $this->day->GetFontHeight($img);
+ $height += $this->day->iTitleVertMargin;
+ }
+ if( $this->week->iShowLabels ) {
+ $height += $this->week->GetFontHeight($img);
+ $height += $this->week->iTitleVertMargin;
+ }
+ if( $this->month->iShowLabels ) {
+ $height += $this->month->GetFontHeight($img);
+ $height += $this->month->iTitleVertMargin;
+ }
+ if( $this->year->iShowLabels ) {
+ $height += $this->year->GetFontHeight($img);
+ $height += $this->year->iTitleVertMargin;
+ }
+ return $height;
+ }
+
+ // Get width (in pixels) for a single day
+ function GetDayWidth() {
+ return ($this->GetPlotWidth()-$this->iLabelWidth+1)/$this->GetNumberOfDays();
+ }
+
+ // Get width (in pixels) for a single hour
+ function GetHourWidth() {
+ return $this->GetDayWidth() / 24 ;
+ }
+
+ function GetMinuteWidth() {
+ return $this->GetHourWidth() / 60 ;
+ }
+
+ // Nuber of days in a year
+ function GetNumDaysInYear($aYear) {
+ if( $this->IsLeap($aYear) )
+ return 366;
+ else
+ return 365;
+ }
+
+ // Get week number
+ function GetWeekNbr($aDate) {
+ // We can't use the internal strftime() since it gets the weeknumber
+ // wrong since it doesn't follow ISO on all systems since this is
+ // system linrary dependent.
+ // Even worse is that this works differently if we are on a Windows
+ // or UNIX box (it even differs between UNIX boxes how strftime()
+ // is natively implemented)
+ //
+ // Credit to Nicolas Hoizey <nhoizey@phpheaven.net> for this elegant
+ // version of Week Nbr calculation.
+
+ $day = $this->NormalizeDate($aDate);
+
+ /*-------------------------------------------------------------------------
+ According to ISO-8601 :
+ "Week 01 of a year is per definition the first week that has the Thursday in this year,
+ which is equivalent to the week that contains the fourth day of January.
+ In other words, the first week of a new year is the week that has the majority of its
+ days in the new year."
+
+ Be carefull, with PHP, -3 % 7 = -3, instead of 4 !!!
+
+ day of year = date("z", $day) + 1
+ offset to thursday = 3 - (date("w", $day) + 6) % 7
+ first thursday of year = 1 + (11 - date("w", mktime(0, 0, 0, 1, 1, date("Y", $day)))) % 7
+ week number = (thursday's day of year - first thursday's day of year) / 7 + 1
+ ---------------------------------------------------------------------------*/
+
+ $thursday = $day + 60 * 60 * 24 * (3 - (date("w", $day) + 6) % 7); // take week's thursday
+ $week = 1 + (date("z", $thursday) - (11 - date("w", mktime(0, 0, 0, 1, 1, date("Y", $thursday)))) % 7) / 7;
+
+ return $week;
+ }
+
+ // Is year a leap year?
+ function IsLeap($aYear) {
+ // Is the year a leap year?
+ //$year = 0+date("Y",$aDate);
+ if( $aYear % 4 == 0)
+ if( !($aYear % 100 == 0) || ($aYear % 400 == 0) )
+ return true;
+ return false;
+ }
+
+ // Get current year
+ function GetYear($aDate) {
+ return 0+Date("Y",$aDate);
+ }
+
+ // Return number of days in a year
+ function GetNumDaysInMonth($aMonth,$aYear) {
+ $days=array(31,28,31,30,31,30,31,31,30,31,30,31);
+ $daysl=array(31,29,31,30,31,30,31,31,30,31,30,31);
+ if( $this->IsLeap($aYear))
+ return $daysl[$aMonth];
+ else
+ return $days[$aMonth];
+ }
+
+ // Get day in month
+ function GetMonthDayNbr($aDate) {
+ return 0+strftime("%d",$aDate);
+ }
+
+ // Get day in year
+ function GetYearDayNbr($aDate) {
+ return 0+strftime("%j",$aDate);
+ }
+
+ // Get month number
+ function GetMonthNbr($aDate) {
+ return 0+strftime("%m",$aDate);
+ }
+
+ // Translate a date to screen coordinates (horizontal scale)
+ function TranslateDate($aDate) {
+ $aDate = $this->NormalizeDate($aDate);
+ $img=$this->iImg;
+ return ($aDate-$this->iStartDate)/SECPERDAY*$this->GetDayWidth()+$img->left_margin+$this->iLabelWidth;;
+ }
+
+ // Get screen coordinatesz for the vertical position for a bar
+ function TranslateVertPos($aPos) {
+ $img=$this->iImg;
+ $ph=$this->iAvailableHeight;
+ if( $aPos > $this->iVertLines )
+ JpGraphError::Raise("Illegal vertical position $aPos");
+ if( $this->iVertLayout == GANTT_EVEN ) {
+ // Position the top bar at 1 vert spacing from the scale
+ return round($img->top_margin + $this->iVertHeaderSize + ($aPos+1)*$this->iVertSpacing);
+ }
+ else {
+ // position the top bar at 1/2 a vert spacing from the scale
+ return round($img->top_margin + $this->iVertHeaderSize + $this->iTopPlotMargin + ($aPos+1)*$this->iVertSpacing);
+ }
+ }
+
+ // What is the vertical spacing?
+ function GetVertSpacing() {
+ return $this->iVertSpacing;
+ }
+
+ // Convert a date to timestamp
+ function NormalizeDate($aDate) {
+ if( is_string($aDate) )
+ return strtotime($aDate);
+ elseif( is_int($aDate) || is_float($aDate) )
+ return $aDate;
+ else
+ JpGraphError::Raise("Unknown date format in GanttScale ($aDate).");
+ }
+
+
+ // Convert a time string to minutes
+
+ function TimeToMinutes($aTimeString) {
+ // Split in hours and minutes
+ $pos=strpos($aTimeString,':');
+ $minint=60;
+ if( $pos === false ) {
+ $hourint = $aTimeString;
+ $minint = 0;
+ }
+ else {
+ $hourint = floor(substr($aTimeString,0,$pos));
+ $minint = floor(substr($aTimeString,$pos+1));
+ }
+ $minint += 60 * $hourint;
+ return $minint;
+ }
+
+ // Stroke the day scale (including gridlines)
+ function StrokeMinutes($aYCoord,$getHeight=false) {
+ $img=$this->iImg;
+ $xt=$img->left_margin+$this->iLabelWidth;
+ $yt=$aYCoord+$img->top_margin;
+ if( $this->minute->iShowLabels ) {
+ $img->SetFont($this->minute->iFFamily,$this->minute->iFStyle,$this->minute->iFSize);
+ $yb = $yt + $img->GetFontHeight() +
+ $this->minute->iTitleVertMargin + $this->minute->iFrameWeight;
+ if( $getHeight ) {
+ return $yb - $img->top_margin;
+ }
+ $xb = $img->width-$img->right_margin+1;
+ $img->SetColor($this->minute->iBackgroundColor);
+ $img->FilledRectangle($xt,$yt,$xb,$yb);
+
+ $x = $xt;
+ $img->SetTextAlign("center");
+ $day = date('w',$this->iStartDate);
+ $minint = $this->minute->GetIntervall() ;
+
+ if( 60 % $minint !== 0 ) {
+ JpGraphError::Raise('Intervall for minutes must divide the hour evenly, e.g. 1,5,10,12,15,20,30 etc You have specified an intervall of '.$minint.' minutes.');
+ }
+
+
+ $n = 60 / $minint;
+ $datestamp = $this->iStartDate;
+ $width = $this->GetHourWidth() / $n ;
+ if( $width < 8 ) {
+ // TO small width to draw minute scale
+ JpGraphError::Raise('The available width ('.$width.') for minutes are to small for this scale to be displayed. Please use auto-sizing or increase the width of the graph.');
+ }
+
+ $nh = ceil(24*60 / $this->TimeToMinutes($this->hour->GetIntervall()) );
+ $nd = $this->GetNumberOfDays();
+ // Convert to intervall to seconds
+ $minint *= 60;
+ for($j=0; $j < $nd; ++$j, $day += 1, $day %= 7) {
+ for( $k=0; $k < $nh; ++$k ) {
+ for($i=0; $i < $n ;++$i, $x+=$width, $datestamp += $minint ) {
+ if( $day==6 || $day==0 ) {
+
+ $img->PushColor($this->day->iWeekendBackgroundColor);
+ if( $this->iUsePlotWeekendBackground )
+ $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$img->height-$img->bottom_margin);
+ else
+ $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$yb-$this->day->iFrameWeight);
+ $img->PopColor();
+
+ }
+
+ if( $day==0 )
+ $img->SetColor($this->day->iSundayTextColor);
+ else
+ $img->SetColor($this->day->iTextColor);
+
+ switch( $this->minute->iStyle ) {
+ case MINUTESTYLE_CUSTOM:
+ $txt = date($this->minute->iLabelFormStr,$datestamp);
+ break;
+ case MINUTESTYLE_MM:
+ default:
+ // 15
+ $txt = date('i',$datestamp);
+ break;
+ }
+ $img->StrokeText(round($x+$width/2),round($yb-$this->minute->iTitleVertMargin),$txt);
+
+ // FIXME: The rounding problem needs to be solved properly ...
+ //
+ // Fix a rounding problem the wrong way ..
+ // If we also have hour scale then don't draw the firsta or last
+ // gridline since that will be overwritten by the hour scale gridline if such exists.
+ // However, due to the propagation of rounding of the 'x+=width' term in the loop
+ // this might sometimes be one pixel of so we fix this by not drawing it.
+ // The proper way to fix it would be to re-calculate the scale for each step and
+ // not using the additive term.
+ if( !(($i == $n || $i==0) && $this->hour->iShowLabels && $this->hour->grid->iShow) ) {
+ $img->SetColor($this->minute->grid->iColor);
+ $img->Line($x,$yt,$x,$yb);
+ $this->minute->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
+ }
+ }
+ }
+ }
+ $img->SetColor($this->minute->iFrameColor);
+ $img->SetLineWeight($this->minute->iFrameWeight);
+ $img->Rectangle($xt,$yt,$xb,$yb);
+ return $yb - $img->top_margin;
+ }
+ return $aYCoord;
+ }
+
+ // Stroke the day scale (including gridlines)
+ function StrokeHours($aYCoord,$getHeight=false) {
+ $img=$this->iImg;
+ $xt=$img->left_margin+$this->iLabelWidth;
+ $yt=$aYCoord+$img->top_margin;
+ if( $this->hour->iShowLabels ) {
+ $img->SetFont($this->hour->iFFamily,$this->hour->iFStyle,$this->hour->iFSize);
+ $yb = $yt + $img->GetFontHeight() +
+ $this->hour->iTitleVertMargin + $this->hour->iFrameWeight;
+ if( $getHeight ) {
+ return $yb - $img->top_margin;
+ }
+ $xb = $img->width-$img->right_margin+1;
+ $img->SetColor($this->hour->iBackgroundColor);
+ $img->FilledRectangle($xt,$yt,$xb,$yb);
+
+ $x = $xt;
+ $img->SetTextAlign("center");
+ $tmp = $this->hour->GetIntervall() ;
+ $minint = $this->TimeToMinutes($tmp);
+ if( 1440 % $minint !== 0 ) {
+ JpGraphError::Raise('Intervall for hours must divide the day evenly, e.g. 0:30, 1:00, 1:30, 4:00 etc. You have specified an intervall of '.$tmp);
+ }
+
+ $n = ceil(24*60 / $minint );
+ $datestamp = $this->iStartDate;
+ $day = date('w',$this->iStartDate);
+ $doback = !$this->minute->iShowLabels;
+ $width = $this->GetDayWidth() / $n ;
+ for($j=0; $j < $this->GetNumberOfDays(); ++$j, $day += 1,$day %= 7) {
+ for($i=0; $i < $n ;++$i, $x+=$width, $datestamp += $minint*60) {
+ if( $day==6 || $day==0 ) {
+
+ $img->PushColor($this->day->iWeekendBackgroundColor);
+ if( $this->iUsePlotWeekendBackground && $doback )
+ $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$img->height-$img->bottom_margin);
+ else
+ $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$yb-$this->day->iFrameWeight);
+ $img->PopColor();
+
+ }
+
+ if( $day==0 )
+ $img->SetColor($this->day->iSundayTextColor);
+ else
+ $img->SetColor($this->day->iTextColor);
+
+ switch( $this->hour->iStyle ) {
+ case HOURSTYLE_HMAMPM:
+ // 1:35pm
+ $txt = date('g:ia',$datestamp);
+ break;
+ case HOURSTYLE_H24:
+ // 13
+ $txt = date('H',$datestamp);
+ break;
+ case HOURSTYLE_HAMPM:
+ $txt = date('ga',$datestamp);
+ break;
+ case HOURSTYLE_CUSTOM:
+ $txt = date($this->hour->iLabelFormStr,$datestamp);
+ break;
+ case HOURSTYLE_HM24:
+ default:
+ $txt = date('H:i',$datestamp);
+ break;
+ }
+ $img->StrokeText(round($x+$width/2),round($yb-$this->hour->iTitleVertMargin),$txt);
+ $img->SetColor($this->hour->grid->iColor);
+ $img->Line($x,$yt,$x,$yb);
+ $this->hour->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
+ }
+ }
+ $img->SetColor($this->hour->iFrameColor);
+ $img->SetLineWeight($this->hour->iFrameWeight);
+ $img->Rectangle($xt,$yt,$xb,$yb);
+ return $yb - $img->top_margin;
+ }
+ return $aYCoord;
+ }
+
+
+ // Stroke the day scale (including gridlines)
+ function StrokeDays($aYCoord,$getHeight=false) {
+ $wdays=$this->iDateLocale->GetDayAbb();
+ $img=$this->iImg;
+ $daywidth=$this->GetDayWidth();
+ $xt=$img->left_margin+$this->iLabelWidth;
+ $yt=$aYCoord+$img->top_margin;
+ if( $this->day->iShowLabels ) {
+ $img->SetFont($this->day->iFFamily,$this->day->iFStyle,$this->day->iFSize);
+ $yb=$yt + $img->GetFontHeight() + $this->day->iTitleVertMargin + $this->day->iFrameWeight;
+ if( $getHeight ) {
+ return $yb - $img->top_margin;
+ }
+ $xb=$img->width-$img->right_margin+1;
+ $img->SetColor($this->day->iBackgroundColor);
+ $img->FilledRectangle($xt,$yt,$xb,$yb);
+
+ $x = $xt;
+ $img->SetTextAlign("center");
+ $day = date('w',$this->iStartDate);
+ $datestamp = $this->iStartDate;
+
+ $doback = !($this->hour->iShowLabels || $this->minute->iShowLabels);
+
+ for($i=0; $i < $this->GetNumberOfDays(); ++$i, $x+=$daywidth, $day += 1,$day %= 7) {
+ if( $day==6 || $day==0 ) {
+ $img->SetColor($this->day->iWeekendBackgroundColor);
+ if( $this->iUsePlotWeekendBackground && $doback)
+ $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,
+ $x+$daywidth,$img->height-$img->bottom_margin);
+ else
+ $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,
+ $x+$daywidth,$yb-$this->day->iFrameWeight);
+ }
+ switch( $this->day->iStyle ) {
+ case DAYSTYLE_LONG:
+ // "Monday"
+ $txt = date('l',$datestamp);
+ break;
+ case DAYSTYLE_SHORT:
+ // "Mon"
+ $txt = date('D',$datestamp);
+ break;
+ case DAYSTYLE_SHORTDAYDATE1:
+ // "Mon 23/6"
+ $txt = date('D j/n',$datestamp);
+ break;
+ case DAYSTYLE_SHORTDAYDATE2:
+ // "Mon 23 Jun"
+ $txt = date('D j M',$datestamp);
+ break;
+ case DAYSTYLE_SHORTDAYDATE3:
+ // "Mon 23 Jun 2003"
+ $txt = date('D j M Y',$datestamp);
+ break;
+ case DAYSTYLE_LONGDAYDATE1:
+ // "Monday 23 Jun"
+ $txt = date('l j M',$datestamp);
+ break;
+ case DAYSTYLE_LONGDAYDATE2:
+ // "Monday 23 Jun 2003"
+ $txt = date('l j M Y',$datestamp);
+ break;
+ case DAYSTYLE_SHORTDATE1:
+ // "23/6"
+ $txt = date('j/n',$datestamp);
+ break;
+ case DAYSTYLE_SHORTDATE2:
+ // "23 Jun"
+ $txt = date('j M',$datestamp);
+ break;
+ case DAYSTYLE_SHORTDATE3:
+ // "Mon 23"
+ $txt = date('D j',$datestamp);
+ break;
+ case DAYSTYLE_CUSTOM:
+ // Custom format
+ $txt = date($this->day->iLabelFormStr,$datestamp);
+ break;
+ case DAYSTYLE_ONELETTER:
+ default:
+ // "M"
+ $txt = substr(date('D',$datestamp),0,1);
+ break;
+ }
+
+ if( $day==0 )
+ $img->SetColor($this->day->iSundayTextColor);
+ else
+ $img->SetColor($this->day->iTextColor);
+ $img->StrokeText(round($x+$daywidth/2+1),
+ round($yb-$this->day->iTitleVertMargin),$txt);
+ $img->SetColor($this->day->grid->iColor);
+ $img->Line($x,$yt,$x,$yb);
+ $this->day->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
+
+ $datestamp += SECPERDAY;
+ }
+ $img->SetColor($this->day->iFrameColor);
+ $img->SetLineWeight($this->day->iFrameWeight);
+ $img->Rectangle($xt,$yt,$xb,$yb);
+ return $yb - $img->top_margin;
+ }
+ return $aYCoord;
+ }
+
+ // Stroke week header and grid
+ function StrokeWeeks($aYCoord,$getHeight=false) {
+ if( $this->week->iShowLabels ) {
+ $img=$this->iImg;
+ $yt=$aYCoord+$img->top_margin;
+ $img->SetFont($this->week->iFFamily,$this->week->iFStyle,$this->week->iFSize);
+ $yb=$yt + $img->GetFontHeight() + $this->week->iTitleVertMargin + $this->week->iFrameWeight;
+
+ if( $getHeight ) {
+ return $yb - $img->top_margin;
+ }
+
+ $xt=$img->left_margin+$this->iLabelWidth;
+ $weekwidth=$this->GetDayWidth()*7;
+ $wdays=$this->iDateLocale->GetDayAbb();
+ $xb=$img->width-$img->right_margin+1;
+ $week = $this->iStartDate;
+ $weeknbr=$this->GetWeekNbr($week);
+ $img->SetColor($this->week->iBackgroundColor);
+ $img->FilledRectangle($xt,$yt,$xb,$yb);
+ $img->SetColor($this->week->grid->iColor);
+ $x = $xt;
+ if( $this->week->iStyle==WEEKSTYLE_WNBR ) {
+ $img->SetTextAlign("center");
+ $txtOffset = $weekwidth/2+1;
+ }
+ elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY ||
+ $this->week->iStyle==WEEKSTYLE_FIRSTDAY2 ||
+ $this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ||
+ $this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
+ $img->SetTextAlign("left");
+ $txtOffset = 3;
+ }
+ else
+ JpGraphError::Raise("Unknown formatting style for week.");
+
+ for($i=0; $i<$this->GetNumberOfDays()/7; ++$i, $x+=$weekwidth) {
+ $img->PushColor($this->week->iTextColor);
+
+ if( $this->week->iStyle==WEEKSTYLE_WNBR )
+ $txt = sprintf($this->week->iLabelFormStr,$weeknbr);
+ elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY ||
+ $this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR )
+ $txt = date("j/n",$week);
+ elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY2 ||
+ $this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
+ $monthnbr = date("n",$week)-1;
+ $shortmonth = $this->iDateLocale->GetShortMonthName($monthnbr);
+ $txt = Date("j",$week)." ".$shortmonth;
+ }
+
+ if( $this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ||
+ $this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
+ $w = sprintf($this->week->iLabelFormStr,$weeknbr);
+ $txt .= ' '.$w;
+ }
+
+ $img->StrokeText(round($x+$txtOffset),
+ round($yb-$this->week->iTitleVertMargin),$txt);
+
+ $week = strtotime('+7 day',$week);
+ $weeknbr = $this->GetWeekNbr($week);
+ $img->PopColor();
+ $img->Line($x,$yt,$x,$yb);
+ $this->week->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
+ }
+ $img->SetColor($this->week->iFrameColor);
+ $img->SetLineWeight($this->week->iFrameWeight);
+ $img->Rectangle($xt,$yt,$xb,$yb);
+ return $yb-$img->top_margin;
+ }
+ return $aYCoord;
+ }
+
+ // Format the mont scale header string
+ function GetMonthLabel($aMonthNbr,$year) {
+ $sn = $this->iDateLocale->GetShortMonthName($aMonthNbr);
+ $ln = $this->iDateLocale->GetLongMonthName($aMonthNbr);
+ switch($this->month->iStyle) {
+ case MONTHSTYLE_SHORTNAME:
+ $m=$sn;
+ break;
+ case MONTHSTYLE_LONGNAME:
+ $m=$ln;
+ break;
+ case MONTHSTYLE_SHORTNAMEYEAR2:
+ $m=$sn." '".substr("".$year,2);
+ break;
+ case MONTHSTYLE_SHORTNAMEYEAR4:
+ $m=$sn." ".$year;
+ break;
+ case MONTHSTYLE_LONGNAMEYEAR2:
+ $m=$ln." '".substr("".$year,2);
+ break;
+ case MONTHSTYLE_LONGNAMEYEAR4:
+ $m=$ln." ".$year;
+ break;
+ }
+ return $m;
+ }
+
+ // Stroke month scale and gridlines
+ function StrokeMonths($aYCoord,$getHeight=false) {
+ if( $this->month->iShowLabels ) {
+ $img=$this->iImg;
+ $img->SetFont($this->month->iFFamily,$this->month->iFStyle,$this->month->iFSize);
+ $yt=$aYCoord+$img->top_margin;
+ $yb=$yt + $img->GetFontHeight() + $this->month->iTitleVertMargin + $this->month->iFrameWeight;
+ if( $getHeight ) {
+ return $yb - $img->top_margin;
+ }
+ $monthnbr = $this->GetMonthNbr($this->iStartDate)-1;
+ $xt=$img->left_margin+$this->iLabelWidth;
+ $xb=$img->width-$img->right_margin+1;
+
+ $img->SetColor($this->month->iBackgroundColor);
+ $img->FilledRectangle($xt,$yt,$xb,$yb);
+
+ $img->SetLineWeight($this->month->grid->iWeight);
+ $img->SetColor($this->month->iTextColor);
+ $year = 0+strftime("%Y",$this->iStartDate);
+ $img->SetTextAlign("center");
+ if( $this->GetMonthNbr($this->iStartDate) == $this->GetMonthNbr($this->iEndDate)
+ && $this->GetYear($this->iStartDate)==$this->GetYear($this->iEndDate) ) {
+ $monthwidth=$this->GetDayWidth()*($this->GetMonthDayNbr($this->iEndDate) - $this->GetMonthDayNbr($this->iStartDate) + 1);
+ }
+ else {
+ $monthwidth=$this->GetDayWidth()*($this->GetNumDaysInMonth($monthnbr,$year)-$this->GetMonthDayNbr($this->iStartDate)+1);
+ }
+ // Is it enough space to stroke the first month?
+ $monthName = $this->GetMonthLabel($monthnbr,$year);
+ if( $monthwidth >= 1.2*$img->GetTextWidth($monthName) ) {
+ $img->SetColor($this->month->iTextColor);
+ $img->StrokeText(round($xt+$monthwidth/2+1),
+ round($yb-$this->month->iTitleVertMargin),
+ $monthName);
+ }
+ $x = $xt + $monthwidth;
+ while( $x < $xb ) {
+ $img->SetColor($this->month->grid->iColor);
+ $img->Line($x,$yt,$x,$yb);
+ $this->month->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
+ $monthnbr++;
+ if( $monthnbr==12 ) {
+ $monthnbr=0;
+ $year++;
+ }
+ $monthName = $this->GetMonthLabel($monthnbr,$year);
+ $monthwidth=$this->GetDayWidth()*$this->GetNumDaysInMonth($monthnbr,$year);
+ if( $x + $monthwidth < $xb )
+ $w = $monthwidth;
+ else
+ $w = $xb-$x;
+ if( $w >= 1.2*$img->GetTextWidth($monthName) ) {
+ $img->SetColor($this->month->iTextColor);
+ $img->StrokeText(round($x+$w/2+1),
+ round($yb-$this->month->iTitleVertMargin),$monthName);
+ }
+ $x += $monthwidth;
+ }
+ $img->SetColor($this->month->iFrameColor);
+ $img->SetLineWeight($this->month->iFrameWeight);
+ $img->Rectangle($xt,$yt,$xb,$yb);
+ return $yb-$img->top_margin;
+ }
+ return $aYCoord;
+ }
+
+ // Stroke year scale and gridlines
+ function StrokeYears($aYCoord,$getHeight=false) {
+ if( $this->year->iShowLabels ) {
+ $img=$this->iImg;
+ $yt=$aYCoord+$img->top_margin;
+ $img->SetFont($this->year->iFFamily,$this->year->iFStyle,$this->year->iFSize);
+ $yb=$yt + $img->GetFontHeight() + $this->year->iTitleVertMargin + $this->year->iFrameWeight;
+
+ if( $getHeight ) {
+ return $yb - $img->top_margin;
+ }
+
+ $xb=$img->width-$img->right_margin+1;
+ $xt=$img->left_margin+$this->iLabelWidth;
+ $year = $this->GetYear($this->iStartDate);
+ $img->SetColor($this->year->iBackgroundColor);
+ $img->FilledRectangle($xt,$yt,$xb,$yb);
+ $img->SetLineWeight($this->year->grid->iWeight);
+ $img->SetTextAlign("center");
+ if( $year == $this->GetYear($this->iEndDate) )
+ $yearwidth=$this->GetDayWidth()*($this->GetYearDayNbr($this->iEndDate)-$this->GetYearDayNbr($this->iStartDate)+1);
+ else
+ $yearwidth=$this->GetDayWidth()*($this->GetNumDaysInYear($year)-$this->GetYearDayNbr($this->iStartDate)+1);
+
+ // The space for a year must be at least 20% bigger than the actual text
+ // so we allow 10% margin on each side
+ if( $yearwidth >= 1.20*$img->GetTextWidth("".$year) ) {
+ $img->SetColor($this->year->iTextColor);
+ $img->StrokeText(round($xt+$yearwidth/2+1),
+ round($yb-$this->year->iTitleVertMargin),
+ $year);
+ }
+ $x = $xt + $yearwidth;
+ while( $x < $xb ) {
+ $img->SetColor($this->year->grid->iColor);
+ $img->Line($x,$yt,$x,$yb);
+ $this->year->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
+ $year += 1;
+ $yearwidth=$this->GetDayWidth()*$this->GetNumDaysInYear($year);
+ if( $x + $yearwidth < $xb )
+ $w = $yearwidth;
+ else
+ $w = $xb-$x;
+ if( $w >= 1.2*$img->GetTextWidth("".$year) ) {
+ $img->SetColor($this->year->iTextColor);
+ $img->StrokeText(round($x+$w/2+1),
+ round($yb-$this->year->iTitleVertMargin),
+ $year);
+ }
+ $x += $yearwidth;
+ }
+ $img->SetColor($this->year->iFrameColor);
+ $img->SetLineWeight($this->year->iFrameWeight);
+ $img->Rectangle($xt,$yt,$xb,$yb);
+ return $yb-$img->top_margin;
+ }
+ return $aYCoord;
+ }
+
+ // Stroke table title (upper left corner)
+ function StrokeTableHeaders($aYBottom) {
+ $img=$this->iImg;
+ $xt=$img->left_margin;
+ $yt=$img->top_margin;
+ $xb=$xt+$this->iLabelWidth;
+ $yb=$aYBottom+$img->top_margin;
+
+ if( $this->tableTitle->iShow ) {
+ $img->SetColor($this->iTableHeaderBackgroundColor);
+ $img->FilledRectangle($xt,$yt,$xb,$yb);
+ $this->tableTitle->Align("center","top");
+ $this->tableTitle->Stroke($img,$xt+($xb-$xt)/2+1,$yt+2);
+ $img->SetColor($this->iTableHeaderFrameColor);
+ $img->SetLineWeight($this->iTableHeaderFrameWeight);
+ $img->Rectangle($xt,$yt,$xb,$yb);
+ }
+
+ $this->actinfo->Stroke($img,$xt,$yt,$xb,$yb,$this->tableTitle->iShow);
+
+
+ // Draw the horizontal dividing line
+ $this->dividerh->Stroke($img,$xt,$yb,$img->width-$img->right_margin,$yb);
+
+ // Draw the vertical dividing line
+ // We do the width "manually" since we want the line only to grow
+ // to the left
+ $fancy = $this->divider->iStyle == 'fancy' ;
+ if( $fancy ) {
+ $this->divider->iStyle = 'solid';
+ }
+
+ $tmp = $this->divider->iWeight;
+ $this->divider->iWeight=1;
+ $y = $img->height-$img->bottom_margin;
+ for($i=0; $i < $tmp; ++$i ) {
+ $this->divider->Stroke($img,$xb-$i,$yt,$xb-$i,$y);
+ }
+
+ // Should we draw "fancy" divider
+ if( $fancy ) {
+ $img->SetLineWeight(1);
+ $img->SetColor($this->iTableHeaderFrameColor);
+ $img->Line($xb,$yt,$xb,$y);
+ $img->Line($xb-$tmp+1,$yt,$xb-$tmp+1,$y);
+ $img->SetColor('white');
+ $img->Line($xb-$tmp+2,$yt,$xb-$tmp+2,$y);
+ }
+
+
+ }
+
+ // Main entry point to stroke scale
+ function Stroke() {
+ if( !$this->IsRangeSet() )
+ JpGraphError::Raise("Gantt scale has not been specified.");
+ $img=$this->iImg;
+
+ // If minutes are displayed then hour interval must be 1
+ if( $this->IsDisplayMinute() && $this->hour->GetIntervall() > 1 ) {
+ JpGraphError::Raise('If you display both hour and minutes the hour intervall must be 1 (Otherwise it doesn\' make sense to display minutes).');
+ }
+
+ // Stroke all headers. As argument we supply the offset from the
+ // top which depends on any previous headers
+
+ // First find out the height of each header
+ $offy=$this->StrokeYears(0,true);
+ $offm=$this->StrokeMonths($offy,true);
+ $offw=$this->StrokeWeeks($offm,true);
+ $offd=$this->StrokeDays($offw,true);
+ $offh=$this->StrokeHours($offd,true);
+ $offmin=$this->StrokeMinutes($offh,true);
+
+ // ... then we can stroke them in the "backwards order to ensure that
+ // the larger scale gridlines is stroked over the smaller scale gridline
+ $this->StrokeMinutes($offh);
+ $this->StrokeHours($offd);
+ $this->StrokeDays($offw);
+ $this->StrokeWeeks($offm);
+ $this->StrokeMonths($offy);
+ $this->StrokeYears(0);
+
+ // Now when we now the oaverall size of the scale headers
+ // we can stroke the overall table headers
+ $this->StrokeTableHeaders($offmin);
+
+ // Now we can calculate the correct scaling factor for each vertical position
+ $this->iAvailableHeight = $img->height - $img->top_margin - $img->bottom_margin - $offd;
+ $this->iVertHeaderSize = $offmin;
+ if( $this->iVertSpacing == -1 )
+ $this->iVertSpacing = $this->iAvailableHeight / $this->iVertLines;
+ }
+}
+
+
+//===================================================
+// CLASS GanttConstraint
+// Just a structure to store all the values for a constraint
+//===================================================
+class GanttConstraint {
+ var $iConstrainType;
+ var $iConstrainRow;
+ var $iConstrainColor;
+ var $iConstrainArrowSize;
+ var $iConstrainArrowType;
+
+//---------------
+// CONSTRUCTOR
+ function GanttConstraint($aRow,$aType,$aColor,$aArrowSize,$aArrowType){
+ $this->iConstrainType = $aType;
+ $this->iConstrainRow = $aRow;
+ $this->iConstrainColor=$aColor;
+ $this->iConstrainArrowSize=$aArrowSize;
+ $this->iConstrainArrowType=$aArrowType;
+ }
+}
+
+
+//===================================================
+// CLASS GanttPlotObject
+// The common signature for a Gantt object
+//===================================================
+class GanttPlotObject {
+ var $iVPos=0; // Vertical position
+ var $iLabelLeftMargin=2; // Title margin
+ var $iStart=""; // Start date
+ var $title,$caption;
+ var $iCaptionMargin=5;
+ var $csimarea='',$csimtarget='',$csimalt='';
+
+ var $constraints = array();
+ var $iConstrainPos=array();
+
+ function GanttPlotObject() {
+ $this->title = new TextProperty();
+ $this->title->Align("left","center");
+ $this->caption = new TextProperty();
+ }
+
+ function GetCSIMArea() {
+ return $this->csimarea;
+ }
+
+ function SetCSIMTarget($aTarget,$aAltText='') {
+ $this->csimtarget=$aTarget;
+ $this->csimalt=$aAltText;
+ }
+
+ function SetCSIMAlt($aAlt) {
+ $this->csimalt=$aAlt;
+ }
+
+ function SetConstrain($aRow,$aType,$aColor='black',$aArrowSize=ARROW_S2,$aArrowType=ARROWT_SOLID) {
+ $this->constraints[] = new GanttConstraint($aRow, $aType, $aColor, $aArrowSize, $aArrowType);
+ }
+
+ function SetConstrainPos($xt,$yt,$xb,$yb) {
+ $this->iConstrainPos = array($xt,$yt,$xb,$yb);
+ }
+
+ /*
+ function GetConstrain() {
+ return array($this->iConstrainRow,$this->iConstrainType);
+ }
+ */
+
+ function GetMinDate() {
+ return $this->iStart;
+ }
+
+ function GetMaxDate() {
+ return $this->iStart;
+ }
+
+ function SetCaptionMargin($aMarg) {
+ $this->iCaptionMargin=$aMarg;
+ }
+
+ function GetAbsHeight($aImg) {
+ return 0;
+ }
+
+ function GetLineNbr() {
+ return $this->iVPos;
+ }
+
+ function SetLabelLeftMargin($aOff) {
+ $this->iLabelLeftMargin=$aOff;
+ }
+
+ function StrokeActInfo($aImg,$aScale,$aYPos) {
+ $cols=array();
+ $aScale->actinfo->GetColStart($aImg,$cols,true);
+ $this->title->Stroke($aImg,$cols,$aYPos);
+ }
+}
+
+//===================================================
+// CLASS Progress
+// Holds parameters for the progress indicator
+// displyed within a bar
+//===================================================
+class Progress {
+ var $iProgress=-1, $iColor="black", $iFillColor='black';
+ var $iPattern=GANTT_SOLID;
+ var $iDensity=98, $iHeight=0.65;
+
+ function Set($aProg) {
+ if( $aProg < 0.0 || $aProg > 1.0 )
+ JpGraphError::Raise("Progress value must in range [0, 1]");
+ $this->iProgress = $aProg;
+ }
+
+ function SetPattern($aPattern,$aColor="blue",$aDensity=98) {
+ $this->iPattern = $aPattern;
+ $this->iColor = $aColor;
+ $this->iDensity = $aDensity;
+ }
+
+ function SetFillColor($aColor) {
+ $this->iFillColor = $aColor;
+ }
+
+ function SetHeight($aHeight) {
+ $this->iHeight = $aHeight;
+ }
+}
+
+//===================================================
+// CLASS GanttBar
+// Responsible for formatting individual gantt bars
+//===================================================
+class GanttBar extends GanttPlotObject {
+ var $iEnd;
+ var $iHeightFactor=0.5;
+ var $iFillColor="white",$iFrameColor="black";
+ var $iShadow=false,$iShadowColor="darkgray",$iShadowWidth=1,$iShadowFrame="black";
+ var $iPattern=GANTT_RDIAG,$iPatternColor="blue",$iPatternDensity=95;
+ var $leftMark,$rightMark;
+ var $progress;
+//---------------
+// CONSTRUCTOR
+ function GanttBar($aPos,$aLabel,$aStart,$aEnd,$aCaption="",$aHeightFactor=0.6) {
+ parent::GanttPlotObject();
+ $this->iStart = $aStart;
+ // Is the end date given as a date or as number of days added to start date?
+ if( is_string($aEnd) ) {
+ // If end date has been specified without a time we will asssume
+ // end date is at the end of that date
+ if( strpos($aEnd,':') === false )
+ $this->iEnd = strtotime($aEnd)+SECPERDAY-1;
+ else
+ $this->iEnd = $aEnd;
+ }
+ elseif(is_int($aEnd) || is_float($aEnd) )
+ $this->iEnd = strtotime($aStart)+round($aEnd*SECPERDAY);
+ $this->iVPos = $aPos;
+ $this->iHeightFactor = $aHeightFactor;
+ $this->title->Set($aLabel);
+ $this->caption = new TextProperty($aCaption);
+ $this->caption->Align("left","center");
+ $this->leftMark =new PlotMark();
+ $this->leftMark->Hide();
+ $this->rightMark=new PlotMark();
+ $this->rightMark->Hide();
+ $this->progress = new Progress();
+ }
+
+//---------------
+// PUBLIC METHODS
+ function SetShadow($aShadow=true,$aColor="gray") {
+ $this->iShadow=$aShadow;
+ $this->iShadowColor=$aColor;
+ }
+
+ function GetMaxDate() {
+ return $this->iEnd;
+ }
+
+ function SetHeight($aHeight) {
+ $this->iHeightFactor = $aHeight;
+ }
+
+ function SetColor($aColor) {
+ $this->iFrameColor = $aColor;
+ }
+
+ function SetFillColor($aColor) {
+ $this->iFillColor = $aColor;
+ }
+
+ function GetAbsHeight($aImg) {
+ if( is_int($this->iHeightFactor) || $this->leftMark->show || $this->rightMark->show ) {
+ $m=-1;
+ if( is_int($this->iHeightFactor) )
+ $m = $this->iHeightFactor;
+ if( $this->leftMark->show )
+ $m = max($m,$this->leftMark->width*2);
+ if( $this->rightMark->show )
+ $m = max($m,$this->rightMark->width*2);
+ return $m;
+ }
+ else
+ return -1;
+ }
+
+ function SetPattern($aPattern,$aColor="blue",$aDensity=95) {
+ $this->iPattern = $aPattern;
+ $this->iPatternColor = $aColor;
+ $this->iPatternDensity = $aDensity;
+ }
+
+ function Stroke($aImg,$aScale) {
+ $factory = new RectPatternFactory();
+ $prect = $factory->Create($this->iPattern,$this->iPatternColor);
+ $prect->SetDensity($this->iPatternDensity);
+
+ // If height factor is specified as a float between 0,1 then we take it as meaning
+ // percetage of the scale width between horizontal line.
+ // If it is an integer > 1 we take it to mean the absolute height in pixels
+ if( $this->iHeightFactor > -0.0 && $this->iHeightFactor <= 1.1)
+ $vs = $aScale->GetVertSpacing()*$this->iHeightFactor;
+ elseif(is_int($this->iHeightFactor) && $this->iHeightFactor>2 && $this->iHeightFactor<200)
+ $vs = $this->iHeightFactor;
+ else
+ JpGraphError::Raise("Specified height (".$this->iHeightFactor.") for gantt bar is out of range.");
+
+ // Clip date to min max dates to show
+ $st = $aScale->NormalizeDate($this->iStart);
+ $en = $aScale->NormalizeDate($this->iEnd);
+
+
+ $limst = max($st,$aScale->iStartDate);
+ $limen = min($en,$aScale->iEndDate);
+
+ $xt = round($aScale->TranslateDate($limst));
+ $xb = round($aScale->TranslateDate($limen));
+ $yt = round($aScale->TranslateVertPos($this->iVPos)-$vs-($aScale->GetVertSpacing()/2-$vs/2));
+ $yb = round($aScale->TranslateVertPos($this->iVPos)-($aScale->GetVertSpacing()/2-$vs/2));
+ $middle = round($yt+($yb-$yt)/2);
+ $this->StrokeActInfo($aImg,$aScale,$middle);
+
+ // CSIM for title
+ if( ! empty($this->title->csimtarget) ) {
+ $colwidth = $this->title->GetColWidth($aImg);
+ $colstarts=array();
+ $aScale->actinfo->GetColStart($aImg,$colstarts,true);
+ $n = min(count($colwidth),count($this->title->csimtarget));
+ for( $i=0; $i < $n; ++$i ) {
+ $title_xt = $colstarts[$i];
+ $title_xb = $title_xt + $colwidth[$i];
+ $coords = "$title_xt,$yt,$title_xb,$yt,$title_xb,$yb,$title_xt,$yb";
+ $this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->title->csimtarget[$i]."\"";
+ if( ! empty($this->title->csimalt[$i]) ) {
+ $tmp = $this->title->csimalt[$i];
+ $this->csimarea .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimarea .= ">\n";
+ }
+ }
+
+ // Check if the bar is totally outside the current scale range
+ if( $en < $aScale->iStartDate || $st > $aScale->iEndDate )
+ return;
+
+
+ // Remember the positions for the bar
+ $this->SetConstrainPos($xt,$yt,$xb,$yb);
+
+ $prect->ShowFrame(false);
+ $prect->SetBackground($this->iFillColor);
+ if( $this->iShadow ) {
+ $aImg->SetColor($this->iFrameColor);
+ $aImg->ShadowRectangle($xt,$yt,$xb,$yb,$this->iFillColor,$this->iShadowWidth,$this->iShadowColor);
+ $prect->SetPos(new Rectangle($xt+1,$yt+1,$xb-$xt-$this->iShadowWidth-2,$yb-$yt-$this->iShadowWidth-2));
+ $prect->Stroke($aImg);
+ }
+ else {
+ $prect->SetPos(new Rectangle($xt,$yt,$xb-$xt+1,$yb-$yt+1));
+ $prect->Stroke($aImg);
+ $aImg->SetColor($this->iFrameColor);
+ $aImg->Rectangle($xt,$yt,$xb,$yb);
+ }
+
+ // CSIM for bar
+ if( $this->csimtarget != '' ) {
+
+ $coords = "$xt,$yt,$xb,$yt,$xb,$yb,$xt,$yb";
+ $this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
+ $this->csimtarget."\"";
+ if( $this->csimalt != '' ) {
+ $tmp = $this->csimalt;
+ $this->csimarea .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimarea .= ">\n";
+ }
+
+ // Draw progress bar inside activity bar
+ if( $this->progress->iProgress > 0 ) {
+
+ $xtp = $aScale->TranslateDate($st);
+ $xbp = $aScale->TranslateDate($en);
+ $len = ($xbp-$xtp)*$this->progress->iProgress;
+
+ $endpos = $xtp+$len;
+ if( $endpos > $xt ) {
+ $len -= ($xt-$xtp);
+
+ // Make sure that the progess bar doesn't extend over the end date
+ if( $xtp+$len-1 > $xb )
+ $len = $xb - $xtp + 1;
+
+ if( $xtp < $xt )
+ $xtp = $xt;
+
+ $prog = $factory->Create($this->progress->iPattern,$this->progress->iColor);
+ $prog->SetDensity($this->progress->iDensity);
+ $prog->SetBackground($this->progress->iFillColor);
+ $barheight = ($yb-$yt+1);
+ if( $this->iShadow )
+ $barheight -= $this->iShadowWidth;
+ $progressheight = floor($barheight*$this->progress->iHeight);
+ $marg = ceil(($barheight-$progressheight)/2);
+ $pos = new Rectangle($xtp,$yt + $marg, $len,$barheight-2*$marg);
+ $prog->SetPos($pos);
+ $prog->Stroke($aImg);
+ }
+ }
+
+ // We don't plot the end mark if the bar has been capped
+ if( $limst == $st ) {
+ $y = $middle;
+ // We treat the RIGHT and LEFT triangle mark a little bi
+ // special so that these marks are placed right under the
+ // bar.
+ if( $this->leftMark->GetType() == MARK_LEFTTRIANGLE ) {
+ $y = $yb ;
+ }
+ $this->leftMark->Stroke($aImg,$xt,$y);
+ }
+ if( $limen == $en ) {
+ $y = $middle;
+ // We treat the RIGHT and LEFT triangle mark a little bi
+ // special so that these marks are placed right under the
+ // bar.
+ if( $this->rightMark->GetType() == MARK_RIGHTTRIANGLE ) {
+ $y = $yb ;
+ }
+ $this->rightMark->Stroke($aImg,$xb,$y);
+
+ $margin = $this->iCaptionMargin;
+ if( $this->rightMark->show )
+ $margin += $this->rightMark->GetWidth();
+ $this->caption->Stroke($aImg,$xb+$margin,$middle);
+ }
+ }
+}
+
+//===================================================
+// CLASS MileStone
+// Responsible for formatting individual milestones
+//===================================================
+class MileStone extends GanttPlotObject {
+ var $mark;
+
+//---------------
+// CONSTRUCTOR
+ function MileStone($aVPos,$aLabel,$aDate,$aCaption="") {
+ GanttPlotObject::GanttPlotObject();
+ $this->caption->Set($aCaption);
+ $this->caption->Align("left","center");
+ $this->caption->SetFont(FF_FONT1,FS_BOLD);
+ $this->title->Set($aLabel);
+ $this->title->SetColor("darkred");
+ $this->mark = new PlotMark();
+ $this->mark->SetWidth(10);
+ $this->mark->SetType(MARK_DIAMOND);
+ $this->mark->SetColor("darkred");
+ $this->mark->SetFillColor("darkred");
+ $this->iVPos = $aVPos;
+ $this->iStart = $aDate;
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ function GetAbsHeight($aImg) {
+ return max($this->title->GetHeight($aImg),$this->mark->GetWidth());
+ }
+
+ function Stroke($aImg,$aScale) {
+ // Put the mark in the middle at the middle of the day
+ $d = $aScale->NormalizeDate($this->iStart)+SECPERDAY/2;
+ $x = $aScale->TranslateDate($d);
+ $y = $aScale->TranslateVertPos($this->iVPos)-($aScale->GetVertSpacing()/2);
+
+ $this->StrokeActInfo($aImg,$aScale,$y);
+
+ // CSIM for title
+ if( $this->title->csimtarget != '' ) {
+ $title_xt = $aImg->left_margin+$this->iLabelLeftMargin;
+ $title_xb = $title_xt + $this->title->GetWidth($aImg);
+ $yt = round($y - $this->title->GetHeight($aImg)/2);
+ $yb = round($y + $this->title->GetHeight($aImg)/2);
+ $coords = "$title_xt,$yt,$title_xb,$yt,$title_xb,$yb,$title_xt,$yb";
+ $this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->title->csimtarget."\"";
+ if( $this->title->csimalt != '' ) {
+ $tmp = $this->title->csimalt;
+ $this->csimarea .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimarea .= ">\n";
+ }
+
+
+ if( $d < $aScale->iStartDate || $d > $aScale->iEndDate )
+ return;
+
+ // Remember the coordinates for any constrains linking to
+ // this milestone
+ $w = $this->mark->GetWidth()/2;
+ $this->SetConstrainPos($x,round($y-$w),$x,round($y+$w));
+
+ // Setup CSIM
+ if( $this->csimtarget != '' ) {
+ $this->mark->SetCSIMTarget( $this->csimtarget );
+ $this->mark->SetCSIMAlt( $this->csimalt );
+ }
+
+ $this->mark->Stroke($aImg,$x,$y);
+ $this->caption->Stroke($aImg,$x+$this->mark->width/2+$this->iCaptionMargin,$y);
+
+ $this->csimarea .= $this->mark->GetCSIMAreas();
+ }
+}
+
+
+//===================================================
+// CLASS GanttVLine
+// Responsible for formatting individual milestones
+//===================================================
+
+class TextPropertyBelow extends TextProperty {
+ function TextPropertyBelow($aTxt='') {
+ parent::TextProperty($aTxt);
+ }
+
+ function GetColWidth($aImg,$margin) {
+ // Since we are not stroking the title in the columns
+ // but rather under the graph we want this to return 0.
+ return array(0);
+ }
+}
+
+class GanttVLine extends GanttPlotObject {
+
+ var $iLine,$title_margin=3;
+ var $iDayOffset=1; // Defult to right edge of day
+
+//---------------
+// CONSTRUCTOR
+ function GanttVLine($aDate,$aTitle="",$aColor="black",$aWeight=3,$aStyle="dashed") {
+ GanttPlotObject::GanttPlotObject();
+ $this->iLine = new LineProperty();
+ $this->iLine->SetColor($aColor);
+ $this->iLine->SetWeight($aWeight);
+ $this->iLine->SetStyle($aStyle);
+ $this->iStart = $aDate;
+ $this->title = new TextPropertyBelow();
+ $this->title->Set($aTitle);
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ function SetDayOffset($aOff=0.5) {
+ if( $aOff < 0.0 || $aOff > 1.0 )
+ JpGraphError::Raise("Offset for vertical line must be in range [0,1]");
+ $this->iDayOffset = $aOff;
+ }
+
+ function SetTitleMargin($aMarg) {
+ $this->title_margin = $aMarg;
+ }
+
+ function Stroke($aImg,$aScale) {
+ $d = $aScale->NormalizeDate($this->iStart);
+ if( $d < $aScale->iStartDate || $d > $aScale->iEndDate )
+ return;
+ $x = $aScale->TranslateDate($d);
+ $y1 = $aScale->iVertHeaderSize+$aImg->top_margin;
+ $y2 = $aImg->height - $aImg->bottom_margin;
+ $this->iLine->Stroke($aImg,$x,$y1,$x,$y2);
+ $this->title->Align("center","top");
+ $this->title->Stroke($aImg,$x,$y2+$this->title_margin);
+ }
+}
+
+//===================================================
+// CLASS LinkArrow
+// Handles the drawing of a an arrow
+//===================================================
+class LinkArrow {
+ var $ix,$iy;
+ var $isizespec = array(
+ array(2,3),array(3,5),array(3,8),array(6,15),array(8,22));
+ var $iDirection=ARROW_DOWN,$iType=ARROWT_SOLID,$iSize=ARROW_S2;
+ var $iColor='black';
+
+ function LinkArrow($x,$y,$aDirection,$aType=ARROWT_SOLID,$aSize=ARROW_S2) {
+ $this->iDirection = $aDirection;
+ $this->iType = $aType;
+ $this->iSize = $aSize;
+ $this->ix = $x;
+ $this->iy = $y;
+ }
+
+ function SetColor($aColor) {
+ $this->iColor = $aColor;
+ }
+
+ function SetSize($aSize) {
+ $this->iSize = $aSize;
+ }
+
+ function SetType($aType) {
+ $this->iType = $aType;
+ }
+
+ function Stroke($aImg) {
+ list($dx,$dy) = $this->isizespec[$this->iSize];
+ $x = $this->ix;
+ $y = $this->iy;
+ switch ( $this->iDirection ) {
+ case ARROW_DOWN:
+ $c = array($x,$y,$x-$dx,$y-$dy,$x+$dx,$y-$dy,$x,$y);
+ break;
+ case ARROW_UP:
+ $c = array($x,$y,$x-$dx,$y+$dy,$x+$dx,$y+$dy,$x,$y);
+ break;
+ case ARROW_LEFT:
+ $c = array($x,$y,$x+$dy,$y-$dx,$x+$dy,$y+$dx,$x,$y);
+ break;
+ case ARROW_RIGHT:
+ $c = array($x,$y,$x-$dy,$y-$dx,$x-$dy,$y+$dx,$x,$y);
+ break;
+ default:
+ JpGraphError::Raise('Unknown arrow direction for link.');
+ die();
+ break;
+ }
+ $aImg->SetColor($this->iColor);
+ switch( $this->iType ) {
+ case ARROWT_SOLID:
+ $aImg->FilledPolygon($c);
+ break;
+ case ARROWT_OPEN:
+ $aImg->Polygon($c);
+ break;
+ default:
+ JpGraphError::Raise('Unknown arrow type for link.');
+ die();
+ break;
+ }
+ }
+}
+
+//===================================================
+// CLASS GanttLink
+// Handles the drawing of a link line between 2 points
+//===================================================
+
+class GanttLink {
+ var $iArrowType='';
+ var $ix1,$ix2,$iy1,$iy2;
+ var $iPathType=2,$iPathExtend=15;
+ var $iColor='black',$iWeight=1;
+ var $iArrowSize=ARROW_S2,$iArrowType=ARROWT_SOLID;
+
+ function GanttLink($x1=0,$y1=0,$x2=0,$y2=0) {
+ $this->ix1 = $x1;
+ $this->ix2 = $x2;
+ $this->iy1 = $y1;
+ $this->iy2 = $y2;
+ }
+
+ function SetPos($x1,$y1,$x2,$y2) {
+ $this->ix1 = $x1;
+ $this->ix2 = $x2;
+ $this->iy1 = $y1;
+ $this->iy2 = $y2;
+ }
+
+ function SetPath($aPath) {
+ $this->iPathType = $aPath;
+ }
+
+ function SetColor($aColor) {
+ $this->iColor = $aColor;
+ }
+
+ function SetArrow($aSize,$aType=ARROWT_SOLID) {
+ $this->iArrowSize = $aSize;
+ $this->iArrowType = $aType;
+ }
+
+ function SetWeight($aWeight) {
+ $this->iWeight = $aWeight;
+ }
+
+ function Stroke($aImg) {
+ // The way the path for the arrow is constructed is partly based
+ // on some heuristics. This is not an exact science but draws the
+ // path in a way that, for me, makes esthetic sence. For example
+ // if the start and end activities are very close we make a small
+ // detour to endter the target horixontally. If there are more
+ // space between axctivities then no suh detour is made and the
+ // target is "hit" directly vertical. I have tried to keep this
+ // simple. no doubt this could become almost infinitive complex
+ // and have some real AI. Feel free to modify this.
+ // This will no-doubt be tweaked as times go by. One design aim
+ // is to avoid having the user choose what types of arrow
+ // he wants.
+
+ // The arrow is drawn between (x1,y1) to (x2,y2)
+ $x1 = $this->ix1 ;
+ $x2 = $this->ix2 ;
+ $y1 = $this->iy1 ;
+ $y2 = $this->iy2 ;
+
+ // Depending on if the target is below or above we have to
+ // handle thi different.
+ if( $y2 > $y1 ) {
+ $arrowtype = ARROW_DOWN;
+ $midy = round(($y2-$y1)/2+$y1);
+ if( $x2 > $x1 ) {
+ switch ( $this->iPathType ) {
+ case 0:
+ $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ $c = array($x1,$y1,$x2,$y1,$x2,$y2);
+ break;
+ default:
+ JpGraphError::Raise('Internal error: Unknown path type (='.$this->iPathType .') specified for link.');
+ exit(1);
+ break;
+ }
+ }
+ else {
+ switch ( $this->iPathType ) {
+ case 0:
+ case 1:
+ $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
+ break;
+ case 2:
+ // Always extend out horizontally a bit from the first point
+ // If we draw a link back in time (end to start) and the bars
+ // are very close we also change the path so it comes in from
+ // the left on the activity
+ $c = array($x1,$y1,$x1+$this->iPathExtend,$y1,
+ $x1+$this->iPathExtend,$midy,
+ $x2,$midy,$x2,$y2);
+ break;
+ case 3:
+ if( $y2-$midy < 6 ) {
+ $c = array($x1,$y1,$x1,$midy,
+ $x2-$this->iPathExtend,$midy,
+ $x2-$this->iPathExtend,$y2,
+ $x2,$y2);
+ $arrowtype = ARROW_RIGHT;
+ }
+ else {
+ $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
+ }
+ break;
+ default:
+ JpGraphError::Raise('Internal error: Unknown path type specified for link.');
+ exit(1);
+ break;
+ }
+ }
+ $arrow = new LinkArrow($x2,$y2,$arrowtype);
+ }
+ else {
+ // Y2 < Y1
+ $arrowtype = ARROW_UP;
+ $midy = round(($y1-$y2)/2+$y2);
+ if( $x2 > $x1 ) {
+ switch ( $this->iPathType ) {
+ case 0:
+ case 1:
+ $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
+ break;
+ case 3:
+ if( $midy-$y2 < 8 ) {
+ $arrowtype = ARROW_RIGHT;
+ $c = array($x1,$y1,$x1,$y2,$x2,$y2);
+ }
+ else {
+ $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
+ }
+ break;
+ default:
+ JpGraphError::Raise('Internal error: Unknown path type specified for link.');
+ break;
+ }
+ }
+ else {
+ switch ( $this->iPathType ) {
+ case 0:
+ case 1:
+ $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
+ break;
+ case 2:
+ // Always extend out horizontally a bit from the first point
+ $c = array($x1,$y1,$x1+$this->iPathExtend,$y1,
+ $x1+$this->iPathExtend,$midy,
+ $x2,$midy,$x2,$y2);
+ break;
+ case 3:
+ if( $midy-$y2 < 16 ) {
+ $arrowtype = ARROW_RIGHT;
+ $c = array($x1,$y1,$x1,$midy,$x2-$this->iPathExtend,$midy,
+ $x2-$this->iPathExtend,$y2,
+ $x2,$y2);
+ }
+ else {
+ $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
+ }
+ break;
+ default:
+ JpGraphError::Raise('Internal error: Unknown path type specified for link.');
+ exit(1);
+ break;
+ }
+ }
+ $arrow = new LinkArrow($x2,$y2,$arrowtype);
+ }
+ $aImg->SetColor($this->iColor);
+ $aImg->SetLineWeight($this->iWeight);
+ $aImg->Polygon($c);
+ $aImg->SetLineWeight(1);
+ $arrow->SetColor($this->iColor);
+ $arrow->SetSize($this->iArrowSize);
+ $arrow->SetType($this->iArrowType);
+ $arrow->Stroke($aImg);
+ }
+}
+
+// <EOF>
+?>
--- /dev/null
+<?php
+//=======================================================================
+// File: JPGRAPH_GB2312.PHP
+// Description: PHP4 Graph Plotting library. Chinese font conversions
+// Created: 2003-05-30
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_gb2312.php,v 1.1 2003/05/29 15:59:18 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2001,2002,2003 Johan Persson
+//========================================================================
+
+
+class GB2312toUTF8 {
+// --------------------------------------------------------------------
+// This code table is used to translate GB2312 code (key) to
+// it's corresponding Unicode value (data)
+// --------------------------------------------------------------------
+ var $codetable = array(
+ 8481 => 12288, 8482 => 12289, 8483 => 12290, 8484 => 12539, 8485 => 713,
+ 8486 => 711, 8487 => 168, 8488 => 12291, 8489 => 12293, 8490 => 8213,
+ 8491 => 65374, 8492 => 8214, 8493 => 8230, 8494 => 8216, 8495 => 8217,
+ 8496 => 8220, 8497 => 8221, 8498 => 12308, 8499 => 12309, 8500 => 12296,
+ 8501 => 12297, 8502 => 12298, 8503 => 12299, 8504 => 12300, 8505 => 12301,
+ 8506 => 12302, 8507 => 12303, 8508 => 12310, 8509 => 12311, 8510 => 12304,
+ 8511 => 12305, 8512 => 177, 8513 => 215, 8514 => 247, 8515 => 8758,
+ 8516 => 8743, 8517 => 8744, 8518 => 8721, 8519 => 8719, 8520 => 8746,
+ 8521 => 8745, 8522 => 8712, 8523 => 8759, 8524 => 8730, 8525 => 8869,
+ 8526 => 8741, 8527 => 8736, 8528 => 8978, 8529 => 8857, 8530 => 8747,
+ 8531 => 8750, 8532 => 8801, 8533 => 8780, 8534 => 8776, 8535 => 8765,
+ 8536 => 8733, 8537 => 8800, 8538 => 8814, 8539 => 8815, 8540 => 8804,
+ 8541 => 8805, 8542 => 8734, 8543 => 8757, 8544 => 8756, 8545 => 9794,
+ 8546 => 9792, 8547 => 176, 8548 => 8242, 8549 => 8243, 8550 => 8451,
+ 8551 => 65284, 8552 => 164, 8553 => 65504, 8554 => 65505, 8555 => 8240,
+ 8556 => 167, 8557 => 8470, 8558 => 9734, 8559 => 9733, 8560 => 9675,
+ 8561 => 9679, 8562 => 9678, 8563 => 9671, 8564 => 9670, 8565 => 9633,
+ 8566 => 9632, 8567 => 9651, 8568 => 9650, 8569 => 8251, 8570 => 8594,
+ 8571 => 8592, 8572 => 8593, 8573 => 8595, 8574 => 12307, 8753 => 9352,
+ 8754 => 9353, 8755 => 9354, 8756 => 9355, 8757 => 9356, 8758 => 9357,
+ 8759 => 9358, 8760 => 9359, 8761 => 9360, 8762 => 9361, 8763 => 9362,
+ 8764 => 9363, 8765 => 9364, 8766 => 9365, 8767 => 9366, 8768 => 9367,
+ 8769 => 9368, 8770 => 9369, 8771 => 9370, 8772 => 9371, 8773 => 9332,
+ 8774 => 9333, 8775 => 9334, 8776 => 9335, 8777 => 9336, 8778 => 9337,
+ 8779 => 9338, 8780 => 9339, 8781 => 9340, 8782 => 9341, 8783 => 9342,
+ 8784 => 9343, 8785 => 9344, 8786 => 9345, 8787 => 9346, 8788 => 9347,
+ 8789 => 9348, 8790 => 9349, 8791 => 9350, 8792 => 9351, 8793 => 9312,
+ 8794 => 9313, 8795 => 9314, 8796 => 9315, 8797 => 9316, 8798 => 9317,
+ 8799 => 9318, 8800 => 9319, 8801 => 9320, 8802 => 9321, 8805 => 12832,
+ 8806 => 12833, 8807 => 12834, 8808 => 12835, 8809 => 12836, 8810 => 12837,
+ 8811 => 12838, 8812 => 12839, 8813 => 12840, 8814 => 12841, 8817 => 8544,
+ 8818 => 8545, 8819 => 8546, 8820 => 8547, 8821 => 8548, 8822 => 8549,
+ 8823 => 8550, 8824 => 8551, 8825 => 8552, 8826 => 8553, 8827 => 8554,
+ 8828 => 8555, 8993 => 65281, 8994 => 65282, 8995 => 65283, 8996 => 65509,
+ 8997 => 65285, 8998 => 65286, 8999 => 65287, 9000 => 65288, 9001 => 65289,
+ 9002 => 65290, 9003 => 65291, 9004 => 65292, 9005 => 65293, 9006 => 65294,
+ 9007 => 65295, 9008 => 65296, 9009 => 65297, 9010 => 65298, 9011 => 65299,
+ 9012 => 65300, 9013 => 65301, 9014 => 65302, 9015 => 65303, 9016 => 65304,
+ 9017 => 65305, 9018 => 65306, 9019 => 65307, 9020 => 65308, 9021 => 65309,
+ 9022 => 65310, 9023 => 65311, 9024 => 65312, 9025 => 65313, 9026 => 65314,
+ 9027 => 65315, 9028 => 65316, 9029 => 65317, 9030 => 65318, 9031 => 65319,
+ 9032 => 65320, 9033 => 65321, 9034 => 65322, 9035 => 65323, 9036 => 65324,
+ 9037 => 65325, 9038 => 65326, 9039 => 65327, 9040 => 65328, 9041 => 65329,
+ 9042 => 65330, 9043 => 65331, 9044 => 65332, 9045 => 65333, 9046 => 65334,
+ 9047 => 65335, 9048 => 65336, 9049 => 65337, 9050 => 65338, 9051 => 65339,
+ 9052 => 65340, 9053 => 65341, 9054 => 65342, 9055 => 65343, 9056 => 65344,
+ 9057 => 65345, 9058 => 65346, 9059 => 65347, 9060 => 65348, 9061 => 65349,
+ 9062 => 65350, 9063 => 65351, 9064 => 65352, 9065 => 65353, 9066 => 65354,
+ 9067 => 65355, 9068 => 65356, 9069 => 65357, 9070 => 65358, 9071 => 65359,
+ 9072 => 65360, 9073 => 65361, 9074 => 65362, 9075 => 65363, 9076 => 65364,
+ 9077 => 65365, 9078 => 65366, 9079 => 65367, 9080 => 65368, 9081 => 65369,
+ 9082 => 65370, 9083 => 65371, 9084 => 65372, 9085 => 65373, 9086 => 65507,
+ 9249 => 12353, 9250 => 12354, 9251 => 12355, 9252 => 12356, 9253 => 12357,
+ 9254 => 12358, 9255 => 12359, 9256 => 12360, 9257 => 12361, 9258 => 12362,
+ 9259 => 12363, 9260 => 12364, 9261 => 12365, 9262 => 12366, 9263 => 12367,
+ 9264 => 12368, 9265 => 12369, 9266 => 12370, 9267 => 12371, 9268 => 12372,
+ 9269 => 12373, 9270 => 12374, 9271 => 12375, 9272 => 12376, 9273 => 12377,
+ 9274 => 12378, 9275 => 12379, 9276 => 12380, 9277 => 12381, 9278 => 12382,
+ 9279 => 12383, 9280 => 12384, 9281 => 12385, 9282 => 12386, 9283 => 12387,
+ 9284 => 12388, 9285 => 12389, 9286 => 12390, 9287 => 12391, 9288 => 12392,
+ 9289 => 12393, 9290 => 12394, 9291 => 12395, 9292 => 12396, 9293 => 12397,
+ 9294 => 12398, 9295 => 12399, 9296 => 12400, 9297 => 12401, 9298 => 12402,
+ 9299 => 12403, 9300 => 12404, 9301 => 12405, 9302 => 12406, 9303 => 12407,
+ 9304 => 12408, 9305 => 12409, 9306 => 12410, 9307 => 12411, 9308 => 12412,
+ 9309 => 12413, 9310 => 12414, 9311 => 12415, 9312 => 12416, 9313 => 12417,
+ 9314 => 12418, 9315 => 12419, 9316 => 12420, 9317 => 12421, 9318 => 12422,
+ 9319 => 12423, 9320 => 12424, 9321 => 12425, 9322 => 12426, 9323 => 12427,
+ 9324 => 12428, 9325 => 12429, 9326 => 12430, 9327 => 12431, 9328 => 12432,
+ 9329 => 12433, 9330 => 12434, 9331 => 12435, 9505 => 12449, 9506 => 12450,
+ 9507 => 12451, 9508 => 12452, 9509 => 12453, 9510 => 12454, 9511 => 12455,
+ 9512 => 12456, 9513 => 12457, 9514 => 12458, 9515 => 12459, 9516 => 12460,
+ 9517 => 12461, 9518 => 12462, 9519 => 12463, 9520 => 12464, 9521 => 12465,
+ 9522 => 12466, 9523 => 12467, 9524 => 12468, 9525 => 12469, 9526 => 12470,
+ 9527 => 12471, 9528 => 12472, 9529 => 12473, 9530 => 12474, 9531 => 12475,
+ 9532 => 12476, 9533 => 12477, 9534 => 12478, 9535 => 12479, 9536 => 12480,
+ 9537 => 12481, 9538 => 12482, 9539 => 12483, 9540 => 12484, 9541 => 12485,
+ 9542 => 12486, 9543 => 12487, 9544 => 12488, 9545 => 12489, 9546 => 12490,
+ 9547 => 12491, 9548 => 12492, 9549 => 12493, 9550 => 12494, 9551 => 12495,
+ 9552 => 12496, 9553 => 12497, 9554 => 12498, 9555 => 12499, 9556 => 12500,
+ 9557 => 12501, 9558 => 12502, 9559 => 12503, 9560 => 12504, 9561 => 12505,
+ 9562 => 12506, 9563 => 12507, 9564 => 12508, 9565 => 12509, 9566 => 12510,
+ 9567 => 12511, 9568 => 12512, 9569 => 12513, 9570 => 12514, 9571 => 12515,
+ 9572 => 12516, 9573 => 12517, 9574 => 12518, 9575 => 12519, 9576 => 12520,
+ 9577 => 12521, 9578 => 12522, 9579 => 12523, 9580 => 12524, 9581 => 12525,
+ 9582 => 12526, 9583 => 12527, 9584 => 12528, 9585 => 12529, 9586 => 12530,
+ 9587 => 12531, 9588 => 12532, 9589 => 12533, 9590 => 12534, 9761 => 913,
+ 9762 => 914, 9763 => 915, 9764 => 916, 9765 => 917, 9766 => 918,
+ 9767 => 919, 9768 => 920, 9769 => 921, 9770 => 922, 9771 => 923,
+ 9772 => 924, 9773 => 925, 9774 => 926, 9775 => 927, 9776 => 928,
+ 9777 => 929, 9778 => 931, 9779 => 932, 9780 => 933, 9781 => 934,
+ 9782 => 935, 9783 => 936, 9784 => 937, 9793 => 945, 9794 => 946,
+ 9795 => 947, 9796 => 948, 9797 => 949, 9798 => 950, 9799 => 951,
+ 9800 => 952, 9801 => 953, 9802 => 954, 9803 => 955, 9804 => 956,
+ 9805 => 957, 9806 => 958, 9807 => 959, 9808 => 960, 9809 => 961,
+ 9810 => 963, 9811 => 964, 9812 => 965, 9813 => 966, 9814 => 967,
+ 9815 => 968, 9816 => 969, 10017 => 1040, 10018 => 1041, 10019 => 1042,
+ 10020 => 1043, 10021 => 1044, 10022 => 1045, 10023 => 1025, 10024 => 1046,
+ 10025 => 1047, 10026 => 1048, 10027 => 1049, 10028 => 1050, 10029 => 1051,
+ 10030 => 1052, 10031 => 1053, 10032 => 1054, 10033 => 1055, 10034 => 1056,
+ 10035 => 1057, 10036 => 1058, 10037 => 1059, 10038 => 1060, 10039 => 1061,
+ 10040 => 1062, 10041 => 1063, 10042 => 1064, 10043 => 1065, 10044 => 1066,
+ 10045 => 1067, 10046 => 1068, 10047 => 1069, 10048 => 1070, 10049 => 1071,
+ 10065 => 1072, 10066 => 1073, 10067 => 1074, 10068 => 1075, 10069 => 1076,
+ 10070 => 1077, 10071 => 1105, 10072 => 1078, 10073 => 1079, 10074 => 1080,
+ 10075 => 1081, 10076 => 1082, 10077 => 1083, 10078 => 1084, 10079 => 1085,
+ 10080 => 1086, 10081 => 1087, 10082 => 1088, 10083 => 1089, 10084 => 1090,
+ 10085 => 1091, 10086 => 1092, 10087 => 1093, 10088 => 1094, 10089 => 1095,
+ 10090 => 1096, 10091 => 1097, 10092 => 1098, 10093 => 1099, 10094 => 1100,
+ 10095 => 1101, 10096 => 1102, 10097 => 1103, 10273 => 257, 10274 => 225,
+ 10275 => 462, 10276 => 224, 10277 => 275, 10278 => 233, 10279 => 283,
+ 10280 => 232, 10281 => 299, 10282 => 237, 10283 => 464, 10284 => 236,
+ 10285 => 333, 10286 => 243, 10287 => 466, 10288 => 242, 10289 => 363,
+ 10290 => 250, 10291 => 468, 10292 => 249, 10293 => 470, 10294 => 472,
+ 10295 => 474, 10296 => 476, 10297 => 252, 10298 => 234, 10309 => 12549,
+ 10310 => 12550, 10311 => 12551, 10312 => 12552, 10313 => 12553, 10314 => 12554,
+ 10315 => 12555, 10316 => 12556, 10317 => 12557, 10318 => 12558, 10319 => 12559,
+ 10320 => 12560, 10321 => 12561, 10322 => 12562, 10323 => 12563, 10324 => 12564,
+ 10325 => 12565, 10326 => 12566, 10327 => 12567, 10328 => 12568, 10329 => 12569,
+ 10330 => 12570, 10331 => 12571, 10332 => 12572, 10333 => 12573, 10334 => 12574,
+ 10335 => 12575, 10336 => 12576, 10337 => 12577, 10338 => 12578, 10339 => 12579,
+ 10340 => 12580, 10341 => 12581, 10342 => 12582, 10343 => 12583, 10344 => 12584,
+ 10345 => 12585, 10532 => 9472, 10533 => 9473, 10534 => 9474, 10535 => 9475,
+ 10536 => 9476, 10537 => 9477, 10538 => 9478, 10539 => 9479, 10540 => 9480,
+ 10541 => 9481, 10542 => 9482, 10543 => 9483, 10544 => 9484, 10545 => 9485,
+ 10546 => 9486, 10547 => 9487, 10548 => 9488, 10549 => 9489, 10550 => 9490,
+ 10551 => 9491, 10552 => 9492, 10553 => 9493, 10554 => 9494, 10555 => 9495,
+ 10556 => 9496, 10557 => 9497, 10558 => 9498, 10559 => 9499, 10560 => 9500,
+ 10561 => 9501, 10562 => 9502, 10563 => 9503, 10564 => 9504, 10565 => 9505,
+ 10566 => 9506, 10567 => 9507, 10568 => 9508, 10569 => 9509, 10570 => 9510,
+ 10571 => 9511, 10572 => 9512, 10573 => 9513, 10574 => 9514, 10575 => 9515,
+ 10576 => 9516, 10577 => 9517, 10578 => 9518, 10579 => 9519, 10580 => 9520,
+ 10581 => 9521, 10582 => 9522, 10583 => 9523, 10584 => 9524, 10585 => 9525,
+ 10586 => 9526, 10587 => 9527, 10588 => 9528, 10589 => 9529, 10590 => 9530,
+ 10591 => 9531, 10592 => 9532, 10593 => 9533, 10594 => 9534, 10595 => 9535,
+ 10596 => 9536, 10597 => 9537, 10598 => 9538, 10599 => 9539, 10600 => 9540,
+ 10601 => 9541, 10602 => 9542, 10603 => 9543, 10604 => 9544, 10605 => 9545,
+ 10606 => 9546, 10607 => 9547, 12321 => 21834, 12322 => 38463, 12323 => 22467,
+ 12324 => 25384, 12325 => 21710, 12326 => 21769, 12327 => 21696, 12328 => 30353,
+ 12329 => 30284, 12330 => 34108, 12331 => 30702, 12332 => 33406, 12333 => 30861,
+ 12334 => 29233, 12335 => 38552, 12336 => 38797, 12337 => 27688, 12338 => 23433,
+ 12339 => 20474, 12340 => 25353, 12341 => 26263, 12342 => 23736, 12343 => 33018,
+ 12344 => 26696, 12345 => 32942, 12346 => 26114, 12347 => 30414, 12348 => 20985,
+ 12349 => 25942, 12350 => 29100, 12351 => 32753, 12352 => 34948, 12353 => 20658,
+ 12354 => 22885, 12355 => 25034, 12356 => 28595, 12357 => 33453, 12358 => 25420,
+ 12359 => 25170, 12360 => 21485, 12361 => 21543, 12362 => 31494, 12363 => 20843,
+ 12364 => 30116, 12365 => 24052, 12366 => 25300, 12367 => 36299, 12368 => 38774,
+ 12369 => 25226, 12370 => 32793, 12371 => 22365, 12372 => 38712, 12373 => 32610,
+ 12374 => 29240, 12375 => 30333, 12376 => 26575, 12377 => 30334, 12378 => 25670,
+ 12379 => 20336, 12380 => 36133, 12381 => 25308, 12382 => 31255, 12383 => 26001,
+ 12384 => 29677, 12385 => 25644, 12386 => 25203, 12387 => 33324, 12388 => 39041,
+ 12389 => 26495, 12390 => 29256, 12391 => 25198, 12392 => 25292, 12393 => 20276,
+ 12394 => 29923, 12395 => 21322, 12396 => 21150, 12397 => 32458, 12398 => 37030,
+ 12399 => 24110, 12400 => 26758, 12401 => 27036, 12402 => 33152, 12403 => 32465,
+ 12404 => 26834, 12405 => 30917, 12406 => 34444, 12407 => 38225, 12408 => 20621,
+ 12409 => 35876, 12410 => 33502, 12411 => 32990, 12412 => 21253, 12413 => 35090,
+ 12414 => 21093, 12577 => 34180, 12578 => 38649, 12579 => 20445, 12580 => 22561,
+ 12581 => 39281, 12582 => 23453, 12583 => 25265, 12584 => 25253, 12585 => 26292,
+ 12586 => 35961, 12587 => 40077, 12588 => 29190, 12589 => 26479, 12590 => 30865,
+ 12591 => 24754, 12592 => 21329, 12593 => 21271, 12594 => 36744, 12595 => 32972,
+ 12596 => 36125, 12597 => 38049, 12598 => 20493, 12599 => 29384, 12600 => 22791,
+ 12601 => 24811, 12602 => 28953, 12603 => 34987, 12604 => 22868, 12605 => 33519,
+ 12606 => 26412, 12607 => 31528, 12608 => 23849, 12609 => 32503, 12610 => 29997,
+ 12611 => 27893, 12612 => 36454, 12613 => 36856, 12614 => 36924, 12615 => 40763,
+ 12616 => 27604, 12617 => 37145, 12618 => 31508, 12619 => 24444, 12620 => 30887,
+ 12621 => 34006, 12622 => 34109, 12623 => 27605, 12624 => 27609, 12625 => 27606,
+ 12626 => 24065, 12627 => 24199, 12628 => 30201, 12629 => 38381, 12630 => 25949,
+ 12631 => 24330, 12632 => 24517, 12633 => 36767, 12634 => 22721, 12635 => 33218,
+ 12636 => 36991, 12637 => 38491, 12638 => 38829, 12639 => 36793, 12640 => 32534,
+ 12641 => 36140, 12642 => 25153, 12643 => 20415, 12644 => 21464, 12645 => 21342,
+ 12646 => 36776, 12647 => 36777, 12648 => 36779, 12649 => 36941, 12650 => 26631,
+ 12651 => 24426, 12652 => 33176, 12653 => 34920, 12654 => 40150, 12655 => 24971,
+ 12656 => 21035, 12657 => 30250, 12658 => 24428, 12659 => 25996, 12660 => 28626,
+ 12661 => 28392, 12662 => 23486, 12663 => 25672, 12664 => 20853, 12665 => 20912,
+ 12666 => 26564, 12667 => 19993, 12668 => 31177, 12669 => 39292, 12670 => 28851,
+ 12833 => 30149, 12834 => 24182, 12835 => 29627, 12836 => 33760, 12837 => 25773,
+ 12838 => 25320, 12839 => 38069, 12840 => 27874, 12841 => 21338, 12842 => 21187,
+ 12843 => 25615, 12844 => 38082, 12845 => 31636, 12846 => 20271, 12847 => 24091,
+ 12848 => 33334, 12849 => 33046, 12850 => 33162, 12851 => 28196, 12852 => 27850,
+ 12853 => 39539, 12854 => 25429, 12855 => 21340, 12856 => 21754, 12857 => 34917,
+ 12858 => 22496, 12859 => 19981, 12860 => 24067, 12861 => 27493, 12862 => 31807,
+ 12863 => 37096, 12864 => 24598, 12865 => 25830, 12866 => 29468, 12867 => 35009,
+ 12868 => 26448, 12869 => 25165, 12870 => 36130, 12871 => 30572, 12872 => 36393,
+ 12873 => 37319, 12874 => 24425, 12875 => 33756, 12876 => 34081, 12877 => 39184,
+ 12878 => 21442, 12879 => 34453, 12880 => 27531, 12881 => 24813, 12882 => 24808,
+ 12883 => 28799, 12884 => 33485, 12885 => 33329, 12886 => 20179, 12887 => 27815,
+ 12888 => 34255, 12889 => 25805, 12890 => 31961, 12891 => 27133, 12892 => 26361,
+ 12893 => 33609, 12894 => 21397, 12895 => 31574, 12896 => 20391, 12897 => 20876,
+ 12898 => 27979, 12899 => 23618, 12900 => 36461, 12901 => 25554, 12902 => 21449,
+ 12903 => 33580, 12904 => 33590, 12905 => 26597, 12906 => 30900, 12907 => 25661,
+ 12908 => 23519, 12909 => 23700, 12910 => 24046, 12911 => 35815, 12912 => 25286,
+ 12913 => 26612, 12914 => 35962, 12915 => 25600, 12916 => 25530, 12917 => 34633,
+ 12918 => 39307, 12919 => 35863, 12920 => 32544, 12921 => 38130, 12922 => 20135,
+ 12923 => 38416, 12924 => 39076, 12925 => 26124, 12926 => 29462, 13089 => 22330,
+ 13090 => 23581, 13091 => 24120, 13092 => 38271, 13093 => 20607, 13094 => 32928,
+ 13095 => 21378, 13096 => 25950, 13097 => 30021, 13098 => 21809, 13099 => 20513,
+ 13100 => 36229, 13101 => 25220, 13102 => 38046, 13103 => 26397, 13104 => 22066,
+ 13105 => 28526, 13106 => 24034, 13107 => 21557, 13108 => 28818, 13109 => 36710,
+ 13110 => 25199, 13111 => 25764, 13112 => 25507, 13113 => 24443, 13114 => 28552,
+ 13115 => 37108, 13116 => 33251, 13117 => 36784, 13118 => 23576, 13119 => 26216,
+ 13120 => 24561, 13121 => 27785, 13122 => 38472, 13123 => 36225, 13124 => 34924,
+ 13125 => 25745, 13126 => 31216, 13127 => 22478, 13128 => 27225, 13129 => 25104,
+ 13130 => 21576, 13131 => 20056, 13132 => 31243, 13133 => 24809, 13134 => 28548,
+ 13135 => 35802, 13136 => 25215, 13137 => 36894, 13138 => 39563, 13139 => 31204,
+13140 => 21507, 13141 => 30196, 13142 => 25345, 13143 => 21273, 13144 => 27744,
+13145 => 36831, 13146 => 24347, 13147 => 39536, 13148 => 32827, 13149 => 40831,
+13150 => 20360, 13151 => 23610, 13152 => 36196, 13153 => 32709, 13154 => 26021,
+13155 => 28861, 13156 => 20805, 13157 => 20914, 13158 => 34411, 13159 => 23815,
+13160 => 23456, 13161 => 25277, 13162 => 37228, 13163 => 30068, 13164 => 36364,
+13165 => 31264, 13166 => 24833, 13167 => 31609, 13168 => 20167, 13169 => 32504,
+13170 => 30597, 13171 => 19985, 13172 => 33261, 13173 => 21021, 13174 => 20986,
+13175 => 27249, 13176 => 21416, 13177 => 36487, 13178 => 38148, 13179 => 38607,
+13180 => 28353, 13181 => 38500, 13182 => 26970, 13345 => 30784, 13346 => 20648,
+13347 => 30679, 13348 => 25616, 13349 => 35302, 13350 => 22788, 13351 => 25571,
+13352 => 24029, 13353 => 31359, 13354 => 26941, 13355 => 20256, 13356 => 33337,
+13357 => 21912, 13358 => 20018, 13359 => 30126, 13360 => 31383, 13361 => 24162,
+13362 => 24202, 13363 => 38383, 13364 => 21019, 13365 => 21561, 13366 => 28810,
+13367 => 25462, 13368 => 38180, 13369 => 22402, 13370 => 26149, 13371 => 26943,
+13372 => 37255, 13373 => 21767, 13374 => 28147, 13375 => 32431, 13376 => 34850,
+13377 => 25139, 13378 => 32496, 13379 => 30133, 13380 => 33576, 13381 => 30913,
+13382 => 38604, 13383 => 36766, 13384 => 24904, 13385 => 29943, 13386 => 35789,
+13387 => 27492, 13388 => 21050, 13389 => 36176, 13390 => 27425, 13391 => 32874,
+13392 => 33905, 13393 => 22257, 13394 => 21254, 13395 => 20174, 13396 => 19995,
+13397 => 20945, 13398 => 31895, 13399 => 37259, 13400 => 31751, 13401 => 20419,
+13402 => 36479, 13403 => 31713, 13404 => 31388, 13405 => 25703, 13406 => 23828,
+13407 => 20652, 13408 => 33030, 13409 => 30209, 13410 => 31929, 13411 => 28140,
+13412 => 32736, 13413 => 26449, 13414 => 23384, 13415 => 23544, 13416 => 30923,
+13417 => 25774, 13418 => 25619, 13419 => 25514, 13420 => 25387, 13421 => 38169,
+13422 => 25645, 13423 => 36798, 13424 => 31572, 13425 => 30249, 13426 => 25171,
+13427 => 22823, 13428 => 21574, 13429 => 27513, 13430 => 20643, 13431 => 25140,
+13432 => 24102, 13433 => 27526, 13434 => 20195, 13435 => 36151, 13436 => 34955,
+13437 => 24453, 13438 => 36910, 13601 => 24608, 13602 => 32829, 13603 => 25285,
+13604 => 20025, 13605 => 21333, 13606 => 37112, 13607 => 25528, 13608 => 32966,
+13609 => 26086, 13610 => 27694, 13611 => 20294, 13612 => 24814, 13613 => 28129,
+13614 => 35806, 13615 => 24377, 13616 => 34507, 13617 => 24403, 13618 => 25377,
+13619 => 20826, 13620 => 33633, 13621 => 26723, 13622 => 20992, 13623 => 25443,
+13624 => 36424, 13625 => 20498, 13626 => 23707, 13627 => 31095, 13628 => 23548,
+13629 => 21040, 13630 => 31291, 13631 => 24764, 13632 => 36947, 13633 => 30423,
+13634 => 24503, 13635 => 24471, 13636 => 30340, 13637 => 36460, 13638 => 28783,
+13639 => 30331, 13640 => 31561, 13641 => 30634, 13642 => 20979, 13643 => 37011,
+13644 => 22564, 13645 => 20302, 13646 => 28404, 13647 => 36842, 13648 => 25932,
+13649 => 31515, 13650 => 29380, 13651 => 28068, 13652 => 32735, 13653 => 23265,
+13654 => 25269, 13655 => 24213, 13656 => 22320, 13657 => 33922, 13658 => 31532,
+13659 => 24093, 13660 => 24351, 13661 => 36882, 13662 => 32532, 13663 => 39072,
+13664 => 25474, 13665 => 28359, 13666 => 30872, 13667 => 28857, 13668 => 20856,
+13669 => 38747, 13670 => 22443, 13671 => 30005, 13672 => 20291, 13673 => 30008,
+13674 => 24215, 13675 => 24806, 13676 => 22880, 13677 => 28096, 13678 => 27583,
+13679 => 30857, 13680 => 21500, 13681 => 38613, 13682 => 20939, 13683 => 20993,
+13684 => 25481, 13685 => 21514, 13686 => 38035, 13687 => 35843, 13688 => 36300,
+13689 => 29241, 13690 => 30879, 13691 => 34678, 13692 => 36845, 13693 => 35853,
+13694 => 21472, 13857 => 19969, 13858 => 30447, 13859 => 21486, 13860 => 38025,
+13861 => 39030, 13862 => 40718, 13863 => 38189, 13864 => 23450, 13865 => 35746,
+13866 => 20002, 13867 => 19996, 13868 => 20908, 13869 => 33891, 13870 => 25026,
+13871 => 21160, 13872 => 26635, 13873 => 20375, 13874 => 24683, 13875 => 20923,
+13876 => 27934, 13877 => 20828, 13878 => 25238, 13879 => 26007, 13880 => 38497,
+13881 => 35910, 13882 => 36887, 13883 => 30168, 13884 => 37117, 13885 => 30563,
+13886 => 27602, 13887 => 29322, 13888 => 29420, 13889 => 35835, 13890 => 22581,
+13891 => 30585, 13892 => 36172, 13893 => 26460, 13894 => 38208, 13895 => 32922,
+13896 => 24230, 13897 => 28193, 13898 => 22930, 13899 => 31471, 13900 => 30701,
+13901 => 38203, 13902 => 27573, 13903 => 26029, 13904 => 32526, 13905 => 22534,
+13906 => 20817, 13907 => 38431, 13908 => 23545, 13909 => 22697, 13910 => 21544,
+13911 => 36466, 13912 => 25958, 13913 => 39039, 13914 => 22244, 13915 => 38045,
+13916 => 30462, 13917 => 36929, 13918 => 25479, 13919 => 21702, 13920 => 22810,
+13921 => 22842, 13922 => 22427, 13923 => 36530, 13924 => 26421, 13925 => 36346,
+13926 => 33333, 13927 => 21057, 13928 => 24816, 13929 => 22549, 13930 => 34558,
+13931 => 23784, 13932 => 40517, 13933 => 20420, 13934 => 39069, 13935 => 35769,
+13936 => 23077, 13937 => 24694, 13938 => 21380, 13939 => 25212, 13940 => 36943,
+13941 => 37122, 13942 => 39295, 13943 => 24681, 13944 => 32780, 13945 => 20799,
+13946 => 32819, 13947 => 23572, 13948 => 39285, 13949 => 27953, 13950 => 20108,
+14113 => 36144, 14114 => 21457, 14115 => 32602, 14116 => 31567, 14117 => 20240,
+14118 => 20047, 14119 => 38400, 14120 => 27861, 14121 => 29648, 14122 => 34281,
+14123 => 24070, 14124 => 30058, 14125 => 32763, 14126 => 27146, 14127 => 30718,
+14128 => 38034, 14129 => 32321, 14130 => 20961, 14131 => 28902, 14132 => 21453,
+14133 => 36820, 14134 => 33539, 14135 => 36137, 14136 => 29359, 14137 => 39277,
+14138 => 27867, 14139 => 22346, 14140 => 33459, 14141 => 26041, 14142 => 32938,
+14143 => 25151, 14144 => 38450, 14145 => 22952, 14146 => 20223, 14147 => 35775,
+14148 => 32442, 14149 => 25918, 14150 => 33778, 14151 => 38750, 14152 => 21857,
+14153 => 39134, 14154 => 32933, 14155 => 21290, 14156 => 35837, 14157 => 21536,
+14158 => 32954, 14159 => 24223, 14160 => 27832, 14161 => 36153, 14162 => 33452,
+14163 => 37210, 14164 => 21545, 14165 => 27675, 14166 => 20998, 14167 => 32439,
+14168 => 22367, 14169 => 28954, 14170 => 27774, 14171 => 31881, 14172 => 22859,
+14173 => 20221, 14174 => 24575, 14175 => 24868, 14176 => 31914, 14177 => 20016,
+14178 => 23553, 14179 => 26539, 14180 => 34562, 14181 => 23792, 14182 => 38155,
+14183 => 39118, 14184 => 30127, 14185 => 28925, 14186 => 36898, 14187 => 20911,
+14188 => 32541, 14189 => 35773, 14190 => 22857, 14191 => 20964, 14192 => 20315,
+14193 => 21542, 14194 => 22827, 14195 => 25975, 14196 => 32932, 14197 => 23413,
+14198 => 25206, 14199 => 25282, 14200 => 36752, 14201 => 24133, 14202 => 27679,
+14203 => 31526, 14204 => 20239, 14205 => 20440, 14206 => 26381, 14369 => 28014,
+14370 => 28074, 14371 => 31119, 14372 => 34993, 14373 => 24343, 14374 => 29995,
+14375 => 25242, 14376 => 36741, 14377 => 20463, 14378 => 37340, 14379 => 26023,
+14380 => 33071, 14381 => 33105, 14382 => 24220, 14383 => 33104, 14384 => 36212,
+14385 => 21103, 14386 => 35206, 14387 => 36171, 14388 => 22797, 14389 => 20613,
+14390 => 20184, 14391 => 38428, 14392 => 29238, 14393 => 33145, 14394 => 36127,
+14395 => 23500, 14396 => 35747, 14397 => 38468, 14398 => 22919, 14399 => 32538,
+14400 => 21648, 14401 => 22134, 14402 => 22030, 14403 => 35813, 14404 => 25913,
+14405 => 27010, 14406 => 38041, 14407 => 30422, 14408 => 28297, 14409 => 24178,
+14410 => 29976, 14411 => 26438, 14412 => 26577, 14413 => 31487, 14414 => 32925,
+14415 => 36214, 14416 => 24863, 14417 => 31174, 14418 => 25954, 14419 => 36195,
+14420 => 20872, 14421 => 21018, 14422 => 38050, 14423 => 32568, 14424 => 32923,
+14425 => 32434, 14426 => 23703, 14427 => 28207, 14428 => 26464, 14429 => 31705,
+14430 => 30347, 14431 => 39640, 14432 => 33167, 14433 => 32660, 14434 => 31957,
+14435 => 25630, 14436 => 38224, 14437 => 31295, 14438 => 21578, 14439 => 21733,
+14440 => 27468, 14441 => 25601, 14442 => 25096, 14443 => 40509, 14444 => 33011,
+14445 => 30105, 14446 => 21106, 14447 => 38761, 14448 => 33883, 14449 => 26684,
+14450 => 34532, 14451 => 38401, 14452 => 38548, 14453 => 38124, 14454 => 20010,
+14455 => 21508, 14456 => 32473, 14457 => 26681, 14458 => 36319, 14459 => 32789,
+14460 => 26356, 14461 => 24218, 14462 => 32697, 14625 => 22466, 14626 => 32831,
+14627 => 26775, 14628 => 24037, 14629 => 25915, 14630 => 21151, 14631 => 24685,
+14632 => 40858, 14633 => 20379, 14634 => 36524, 14635 => 20844, 14636 => 23467,
+14637 => 24339, 14638 => 24041, 14639 => 27742, 14640 => 25329, 14641 => 36129,
+14642 => 20849, 14643 => 38057, 14644 => 21246, 14645 => 27807, 14646 => 33503,
+14647 => 29399, 14648 => 22434, 14649 => 26500, 14650 => 36141, 14651 => 22815,
+14652 => 36764, 14653 => 33735, 14654 => 21653, 14655 => 31629, 14656 => 20272,
+14657 => 27837, 14658 => 23396, 14659 => 22993, 14660 => 40723, 14661 => 21476,
+14662 => 34506, 14663 => 39592, 14664 => 35895, 14665 => 32929, 14666 => 25925,
+14667 => 39038, 14668 => 22266, 14669 => 38599, 14670 => 21038, 14671 => 29916,
+14672 => 21072, 14673 => 23521, 14674 => 25346, 14675 => 35074, 14676 => 20054,
+14677 => 25296, 14678 => 24618, 14679 => 26874, 14680 => 20851, 14681 => 23448,
+14682 => 20896, 14683 => 35266, 14684 => 31649, 14685 => 39302, 14686 => 32592,
+14687 => 24815, 14688 => 28748, 14689 => 36143, 14690 => 20809, 14691 => 24191,
+14692 => 36891, 14693 => 29808, 14694 => 35268, 14695 => 22317, 14696 => 30789,
+14697 => 24402, 14698 => 40863, 14699 => 38394, 14700 => 36712, 14701 => 39740,
+14702 => 35809, 14703 => 30328, 14704 => 26690, 14705 => 26588, 14706 => 36330,
+14707 => 36149, 14708 => 21053, 14709 => 36746, 14710 => 28378, 14711 => 26829,
+14712 => 38149, 14713 => 37101, 14714 => 22269, 14715 => 26524, 14716 => 35065,
+14717 => 36807, 14718 => 21704, 14881 => 39608, 14882 => 23401, 14883 => 28023,
+14884 => 27686, 14885 => 20133, 14886 => 23475, 14887 => 39559, 14888 => 37219,
+14889 => 25000, 14890 => 37039, 14891 => 38889, 14892 => 21547, 14893 => 28085,
+14894 => 23506, 14895 => 20989, 14896 => 21898, 14897 => 32597, 14898 => 32752,
+14899 => 25788, 14900 => 25421, 14901 => 26097, 14902 => 25022, 14903 => 24717,
+14904 => 28938, 14905 => 27735, 14906 => 27721, 14907 => 22831, 14908 => 26477,
+14909 => 33322, 14910 => 22741, 14911 => 22158, 14912 => 35946, 14913 => 27627,
+14914 => 37085, 14915 => 22909, 14916 => 32791, 14917 => 21495, 14918 => 28009,
+14919 => 21621, 14920 => 21917, 14921 => 33655, 14922 => 33743, 14923 => 26680,
+14924 => 31166, 14925 => 21644, 14926 => 20309, 14927 => 21512, 14928 => 30418,
+14929 => 35977, 14930 => 38402, 14931 => 27827, 14932 => 28088, 14933 => 36203,
+14934 => 35088, 14935 => 40548, 14936 => 36154, 14937 => 22079, 14938 => 40657,
+14939 => 30165, 14940 => 24456, 14941 => 29408, 14942 => 24680, 14943 => 21756,
+14944 => 20136, 14945 => 27178, 14946 => 34913, 14947 => 24658, 14948 => 36720,
+14949 => 21700, 14950 => 28888, 14951 => 34425, 14952 => 40511, 14953 => 27946,
+14954 => 23439, 14955 => 24344, 14956 => 32418, 14957 => 21897, 14958 => 20399,
+14959 => 29492, 14960 => 21564, 14961 => 21402, 14962 => 20505, 14963 => 21518,
+14964 => 21628, 14965 => 20046, 14966 => 24573, 14967 => 29786, 14968 => 22774,
+14969 => 33899, 14970 => 32993, 14971 => 34676, 14972 => 29392, 14973 => 31946,
+14974 => 28246, 15137 => 24359, 15138 => 34382, 15139 => 21804, 15140 => 25252,
+15141 => 20114, 15142 => 27818, 15143 => 25143, 15144 => 33457, 15145 => 21719,
+15146 => 21326, 15147 => 29502, 15148 => 28369, 15149 => 30011, 15150 => 21010,
+15151 => 21270, 15152 => 35805, 15153 => 27088, 15154 => 24458, 15155 => 24576,
+15156 => 28142, 15157 => 22351, 15158 => 27426, 15159 => 29615, 15160 => 26707,
+15161 => 36824, 15162 => 32531, 15163 => 25442, 15164 => 24739, 15165 => 21796,
+15166 => 30186, 15167 => 35938, 15168 => 28949, 15169 => 28067, 15170 => 23462,
+15171 => 24187, 15172 => 33618, 15173 => 24908, 15174 => 40644, 15175 => 30970,
+15176 => 34647, 15177 => 31783, 15178 => 30343, 15179 => 20976, 15180 => 24822,
+15181 => 29004, 15182 => 26179, 15183 => 24140, 15184 => 24653, 15185 => 35854,
+15186 => 28784, 15187 => 25381, 15188 => 36745, 15189 => 24509, 15190 => 24674,
+15191 => 34516, 15192 => 22238, 15193 => 27585, 15194 => 24724, 15195 => 24935,
+15196 => 21321, 15197 => 24800, 15198 => 26214, 15199 => 36159, 15200 => 31229,
+15201 => 20250, 15202 => 28905, 15203 => 27719, 15204 => 35763, 15205 => 35826,
+15206 => 32472, 15207 => 33636, 15208 => 26127, 15209 => 23130, 15210 => 39746,
+15211 => 27985, 15212 => 28151, 15213 => 35905, 15214 => 27963, 15215 => 20249,
+15216 => 28779, 15217 => 33719, 15218 => 25110, 15219 => 24785, 15220 => 38669,
+15221 => 36135, 15222 => 31096, 15223 => 20987, 15224 => 22334, 15225 => 22522,
+15226 => 26426, 15227 => 30072, 15228 => 31293, 15229 => 31215, 15230 => 31637,
+15393 => 32908, 15394 => 39269, 15395 => 36857, 15396 => 28608, 15397 => 35749,
+15398 => 40481, 15399 => 23020, 15400 => 32489, 15401 => 32521, 15402 => 21513,
+15403 => 26497, 15404 => 26840, 15405 => 36753, 15406 => 31821, 15407 => 38598,
+15408 => 21450, 15409 => 24613, 15410 => 30142, 15411 => 27762, 15412 => 21363,
+15413 => 23241, 15414 => 32423, 15415 => 25380, 15416 => 20960, 15417 => 33034,
+15418 => 24049, 15419 => 34015, 15420 => 25216, 15421 => 20864, 15422 => 23395,
+15423 => 20238, 15424 => 31085, 15425 => 21058, 15426 => 24760, 15427 => 27982,
+15428 => 23492, 15429 => 23490, 15430 => 35745, 15431 => 35760, 15432 => 26082,
+15433 => 24524, 15434 => 38469, 15435 => 22931, 15436 => 32487, 15437 => 32426,
+15438 => 22025, 15439 => 26551, 15440 => 22841, 15441 => 20339, 15442 => 23478,
+15443 => 21152, 15444 => 33626, 15445 => 39050, 15446 => 36158, 15447 => 30002,
+15448 => 38078, 15449 => 20551, 15450 => 31292, 15451 => 20215, 15452 => 26550,
+15453 => 39550, 15454 => 23233, 15455 => 27516, 15456 => 30417, 15457 => 22362,
+15458 => 23574, 15459 => 31546, 15460 => 38388, 15461 => 29006, 15462 => 20860,
+15463 => 32937, 15464 => 33392, 15465 => 22904, 15466 => 32516, 15467 => 33575,
+15468 => 26816, 15469 => 26604, 15470 => 30897, 15471 => 30839, 15472 => 25315,
+15473 => 25441, 15474 => 31616, 15475 => 20461, 15476 => 21098, 15477 => 20943,
+15478 => 33616, 15479 => 27099, 15480 => 37492, 15481 => 36341, 15482 => 36145,
+15483 => 35265, 15484 => 38190, 15485 => 31661, 15486 => 20214, 15649 => 20581,
+15650 => 33328, 15651 => 21073, 15652 => 39279, 15653 => 28176, 15654 => 28293,
+15655 => 28071, 15656 => 24314, 15657 => 20725, 15658 => 23004, 15659 => 23558,
+15660 => 27974, 15661 => 27743, 15662 => 30086, 15663 => 33931, 15664 => 26728,
+15665 => 22870, 15666 => 35762, 15667 => 21280, 15668 => 37233, 15669 => 38477,
+15670 => 34121, 15671 => 26898, 15672 => 30977, 15673 => 28966, 15674 => 33014,
+15675 => 20132, 15676 => 37066, 15677 => 27975, 15678 => 39556, 15679 => 23047,
+15680 => 22204, 15681 => 25605, 15682 => 38128, 15683 => 30699, 15684 => 20389,
+15685 => 33050, 15686 => 29409, 15687 => 35282, 15688 => 39290, 15689 => 32564,
+15690 => 32478, 15691 => 21119, 15692 => 25945, 15693 => 37237, 15694 => 36735,
+15695 => 36739, 15696 => 21483, 15697 => 31382, 15698 => 25581, 15699 => 25509,
+15700 => 30342, 15701 => 31224, 15702 => 34903, 15703 => 38454, 15704 => 25130,
+15705 => 21163, 15706 => 33410, 15707 => 26708, 15708 => 26480, 15709 => 25463,
+15710 => 30571, 15711 => 31469, 15712 => 27905, 15713 => 32467, 15714 => 35299,
+15715 => 22992, 15716 => 25106, 15717 => 34249, 15718 => 33445, 15719 => 30028,
+15720 => 20511, 15721 => 20171, 15722 => 30117, 15723 => 35819, 15724 => 23626,
+15725 => 24062, 15726 => 31563, 15727 => 26020, 15728 => 37329, 15729 => 20170,
+15730 => 27941, 15731 => 35167, 15732 => 32039, 15733 => 38182, 15734 => 20165,
+15735 => 35880, 15736 => 36827, 15737 => 38771, 15738 => 26187, 15739 => 31105,
+15740 => 36817, 15741 => 28908, 15742 => 28024, 15905 => 23613, 15906 => 21170,
+15907 => 33606, 15908 => 20834, 15909 => 33550, 15910 => 30555, 15911 => 26230,
+15912 => 40120, 15913 => 20140, 15914 => 24778, 15915 => 31934, 15916 => 31923,
+15917 => 32463, 15918 => 20117, 15919 => 35686, 15920 => 26223, 15921 => 39048,
+15922 => 38745, 15923 => 22659, 15924 => 25964, 15925 => 38236, 15926 => 24452,
+15927 => 30153, 15928 => 38742, 15929 => 31455, 15930 => 31454, 15931 => 20928,
+15932 => 28847, 15933 => 31384, 15934 => 25578, 15935 => 31350, 15936 => 32416,
+15937 => 29590, 15938 => 38893, 15939 => 20037, 15940 => 28792, 15941 => 20061,
+15942 => 37202, 15943 => 21417, 15944 => 25937, 15945 => 26087, 15946 => 33276,
+15947 => 33285, 15948 => 21646, 15949 => 23601, 15950 => 30106, 15951 => 38816,
+15952 => 25304, 15953 => 29401, 15954 => 30141, 15955 => 23621, 15956 => 39545,
+15957 => 33738, 15958 => 23616, 15959 => 21632, 15960 => 30697, 15961 => 20030,
+15962 => 27822, 15963 => 32858, 15964 => 25298, 15965 => 25454, 15966 => 24040,
+15967 => 20855, 15968 => 36317, 15969 => 36382, 15970 => 38191, 15971 => 20465,
+15972 => 21477, 15973 => 24807, 15974 => 28844, 15975 => 21095, 15976 => 25424,
+15977 => 40515, 15978 => 23071, 15979 => 20518, 15980 => 30519, 15981 => 21367,
+15982 => 32482, 15983 => 25733, 15984 => 25899, 15985 => 25225, 15986 => 25496,
+15987 => 20500, 15988 => 29237, 15989 => 35273, 15990 => 20915, 15991 => 35776,
+15992 => 32477, 15993 => 22343, 15994 => 33740, 15995 => 38055, 15996 => 20891,
+15997 => 21531, 15998 => 23803, 16161 => 20426, 16162 => 31459, 16163 => 27994,
+16164 => 37089, 16165 => 39567, 16166 => 21888, 16167 => 21654, 16168 => 21345,
+16169 => 21679, 16170 => 24320, 16171 => 25577, 16172 => 26999, 16173 => 20975,
+16174 => 24936, 16175 => 21002, 16176 => 22570, 16177 => 21208, 16178 => 22350,
+16179 => 30733, 16180 => 30475, 16181 => 24247, 16182 => 24951, 16183 => 31968,
+16184 => 25179, 16185 => 25239, 16186 => 20130, 16187 => 28821, 16188 => 32771,
+16189 => 25335, 16190 => 28900, 16191 => 38752, 16192 => 22391, 16193 => 33499,
+16194 => 26607, 16195 => 26869, 16196 => 30933, 16197 => 39063, 16198 => 31185,
+16199 => 22771, 16200 => 21683, 16201 => 21487, 16202 => 28212, 16203 => 20811,
+16204 => 21051, 16205 => 23458, 16206 => 35838, 16207 => 32943, 16208 => 21827,
+16209 => 22438, 16210 => 24691, 16211 => 22353, 16212 => 21549, 16213 => 31354,
+16214 => 24656, 16215 => 23380, 16216 => 25511, 16217 => 25248, 16218 => 21475,
+16219 => 25187, 16220 => 23495, 16221 => 26543, 16222 => 21741, 16223 => 31391,
+16224 => 33510, 16225 => 37239, 16226 => 24211, 16227 => 35044, 16228 => 22840,
+16229 => 22446, 16230 => 25358, 16231 => 36328, 16232 => 33007, 16233 => 22359,
+16234 => 31607, 16235 => 20393, 16236 => 24555, 16237 => 23485, 16238 => 27454,
+16239 => 21281, 16240 => 31568, 16241 => 29378, 16242 => 26694, 16243 => 30719,
+16244 => 30518, 16245 => 26103, 16246 => 20917, 16247 => 20111, 16248 => 30420,
+16249 => 23743, 16250 => 31397, 16251 => 33909, 16252 => 22862, 16253 => 39745,
+16254 => 20608, 16417 => 39304, 16418 => 24871, 16419 => 28291, 16420 => 22372,
+16421 => 26118, 16422 => 25414, 16423 => 22256, 16424 => 25324, 16425 => 25193,
+16426 => 24275, 16427 => 38420, 16428 => 22403, 16429 => 25289, 16430 => 21895,
+16431 => 34593, 16432 => 33098, 16433 => 36771, 16434 => 21862, 16435 => 33713,
+16436 => 26469, 16437 => 36182, 16438 => 34013, 16439 => 23146, 16440 => 26639,
+16441 => 25318, 16442 => 31726, 16443 => 38417, 16444 => 20848, 16445 => 28572,
+16446 => 35888, 16447 => 25597, 16448 => 35272, 16449 => 25042, 16450 => 32518,
+16451 => 28866, 16452 => 28389, 16453 => 29701, 16454 => 27028, 16455 => 29436,
+16456 => 24266, 16457 => 37070, 16458 => 26391, 16459 => 28010, 16460 => 25438,
+16461 => 21171, 16462 => 29282, 16463 => 32769, 16464 => 20332, 16465 => 23013,
+16466 => 37226, 16467 => 28889, 16468 => 28061, 16469 => 21202, 16470 => 20048,
+16471 => 38647, 16472 => 38253, 16473 => 34174, 16474 => 30922, 16475 => 32047,
+16476 => 20769, 16477 => 22418, 16478 => 25794, 16479 => 32907, 16480 => 31867,
+16481 => 27882, 16482 => 26865, 16483 => 26974, 16484 => 20919, 16485 => 21400,
+16486 => 26792, 16487 => 29313, 16488 => 40654, 16489 => 31729, 16490 => 29432,
+16491 => 31163, 16492 => 28435, 16493 => 29702, 16494 => 26446, 16495 => 37324,
+16496 => 40100, 16497 => 31036, 16498 => 33673, 16499 => 33620, 16500 => 21519,
+16501 => 26647, 16502 => 20029, 16503 => 21385, 16504 => 21169, 16505 => 30782,
+16506 => 21382, 16507 => 21033, 16508 => 20616, 16509 => 20363, 16510 => 20432,
+16673 => 30178, 16674 => 31435, 16675 => 31890, 16676 => 27813, 16677 => 38582,
+16678 => 21147, 16679 => 29827, 16680 => 21737, 16681 => 20457, 16682 => 32852,
+16683 => 33714, 16684 => 36830, 16685 => 38256, 16686 => 24265, 16687 => 24604,
+16688 => 28063, 16689 => 24088, 16690 => 25947, 16691 => 33080, 16692 => 38142,
+16693 => 24651, 16694 => 28860, 16695 => 32451, 16696 => 31918, 16697 => 20937,
+16698 => 26753, 16699 => 31921, 16700 => 33391, 16701 => 20004, 16702 => 36742,
+16703 => 37327, 16704 => 26238, 16705 => 20142, 16706 => 35845, 16707 => 25769,
+16708 => 32842, 16709 => 20698, 16710 => 30103, 16711 => 29134, 16712 => 23525,
+16713 => 36797, 16714 => 28518, 16715 => 20102, 16716 => 25730, 16717 => 38243,
+16718 => 24278, 16719 => 26009, 16720 => 21015, 16721 => 35010, 16722 => 28872,
+16723 => 21155, 16724 => 29454, 16725 => 29747, 16726 => 26519, 16727 => 30967,
+16728 => 38678, 16729 => 20020, 16730 => 37051, 16731 => 40158, 16732 => 28107,
+16733 => 20955, 16734 => 36161, 16735 => 21533, 16736 => 25294, 16737 => 29618,
+16738 => 33777, 16739 => 38646, 16740 => 40836, 16741 => 38083, 16742 => 20278,
+16743 => 32666, 16744 => 20940, 16745 => 28789, 16746 => 38517, 16747 => 23725,
+16748 => 39046, 16749 => 21478, 16750 => 20196, 16751 => 28316, 16752 => 29705,
+16753 => 27060, 16754 => 30827, 16755 => 39311, 16756 => 30041, 16757 => 21016,
+16758 => 30244, 16759 => 27969, 16760 => 26611, 16761 => 20845, 16762 => 40857,
+16763 => 32843, 16764 => 21657, 16765 => 31548, 16766 => 31423, 16929 => 38534,
+16930 => 22404, 16931 => 25314, 16932 => 38471, 16933 => 27004, 16934 => 23044,
+16935 => 25602, 16936 => 31699, 16937 => 28431, 16938 => 38475, 16939 => 33446,
+16940 => 21346, 16941 => 39045, 16942 => 24208, 16943 => 28809, 16944 => 25523,
+16945 => 21348, 16946 => 34383, 16947 => 40065, 16948 => 40595, 16949 => 30860,
+16950 => 38706, 16951 => 36335, 16952 => 36162, 16953 => 40575, 16954 => 28510,
+16955 => 31108, 16956 => 24405, 16957 => 38470, 16958 => 25134, 16959 => 39540,
+16960 => 21525, 16961 => 38109, 16962 => 20387, 16963 => 26053, 16964 => 23653,
+16965 => 23649, 16966 => 32533, 16967 => 34385, 16968 => 27695, 16969 => 24459,
+16970 => 29575, 16971 => 28388, 16972 => 32511, 16973 => 23782, 16974 => 25371,
+16975 => 23402, 16976 => 28390, 16977 => 21365, 16978 => 20081, 16979 => 25504,
+16980 => 30053, 16981 => 25249, 16982 => 36718, 16983 => 20262, 16984 => 20177,
+16985 => 27814, 16986 => 32438, 16987 => 35770, 16988 => 33821, 16989 => 34746,
+16990 => 32599, 16991 => 36923, 16992 => 38179, 16993 => 31657, 16994 => 39585,
+16995 => 35064, 16996 => 33853, 16997 => 27931, 16998 => 39558, 16999 => 32476,
+17000 => 22920, 17001 => 40635, 17002 => 29595, 17003 => 30721, 17004 => 34434,
+17005 => 39532, 17006 => 39554, 17007 => 22043, 17008 => 21527, 17009 => 22475,
+17010 => 20080, 17011 => 40614, 17012 => 21334, 17013 => 36808, 17014 => 33033,
+17015 => 30610, 17016 => 39314, 17017 => 34542, 17018 => 28385, 17019 => 34067,
+17020 => 26364, 17021 => 24930, 17022 => 28459, 17185 => 35881, 17186 => 33426,
+17187 => 33579, 17188 => 30450, 17189 => 27667, 17190 => 24537, 17191 => 33725,
+17192 => 29483, 17193 => 33541, 17194 => 38170, 17195 => 27611, 17196 => 30683,
+17197 => 38086, 17198 => 21359, 17199 => 33538, 17200 => 20882, 17201 => 24125,
+17202 => 35980, 17203 => 36152, 17204 => 20040, 17205 => 29611, 17206 => 26522,
+17207 => 26757, 17208 => 37238, 17209 => 38665, 17210 => 29028, 17211 => 27809,
+17212 => 30473, 17213 => 23186, 17214 => 38209, 17215 => 27599, 17216 => 32654,
+17217 => 26151, 17218 => 23504, 17219 => 22969, 17220 => 23194, 17221 => 38376,
+17222 => 38391, 17223 => 20204, 17224 => 33804, 17225 => 33945, 17226 => 27308,
+17227 => 30431, 17228 => 38192, 17229 => 29467, 17230 => 26790, 17231 => 23391,
+17232 => 30511, 17233 => 37274, 17234 => 38753, 17235 => 31964, 17236 => 36855,
+17237 => 35868, 17238 => 24357, 17239 => 31859, 17240 => 31192, 17241 => 35269,
+17242 => 27852, 17243 => 34588, 17244 => 23494, 17245 => 24130, 17246 => 26825,
+17247 => 30496, 17248 => 32501, 17249 => 20885, 17250 => 20813, 17251 => 21193,
+17252 => 23081, 17253 => 32517, 17254 => 38754, 17255 => 33495, 17256 => 25551,
+17257 => 30596, 17258 => 34256, 17259 => 31186, 17260 => 28218, 17261 => 24217,
+17262 => 22937, 17263 => 34065, 17264 => 28781, 17265 => 27665, 17266 => 25279,
+17267 => 30399, 17268 => 25935, 17269 => 24751, 17270 => 38397, 17271 => 26126,
+17272 => 34719, 17273 => 40483, 17274 => 38125, 17275 => 21517, 17276 => 21629,
+17277 => 35884, 17278 => 25720, 17441 => 25721, 17442 => 34321, 17443 => 27169,
+17444 => 33180, 17445 => 30952, 17446 => 25705, 17447 => 39764, 17448 => 25273,
+17449 => 26411, 17450 => 33707, 17451 => 22696, 17452 => 40664, 17453 => 27819,
+17454 => 28448, 17455 => 23518, 17456 => 38476, 17457 => 35851, 17458 => 29279,
+17459 => 26576, 17460 => 25287, 17461 => 29281, 17462 => 20137, 17463 => 22982,
+17464 => 27597, 17465 => 22675, 17466 => 26286, 17467 => 24149, 17468 => 21215,
+17469 => 24917, 17470 => 26408, 17471 => 30446, 17472 => 30566, 17473 => 29287,
+17474 => 31302, 17475 => 25343, 17476 => 21738, 17477 => 21584, 17478 => 38048,
+17479 => 37027, 17480 => 23068, 17481 => 32435, 17482 => 27670, 17483 => 20035,
+17484 => 22902, 17485 => 32784, 17486 => 22856, 17487 => 21335, 17488 => 30007,
+17489 => 38590, 17490 => 22218, 17491 => 25376, 17492 => 33041, 17493 => 24700,
+17494 => 38393, 17495 => 28118, 17496 => 21602, 17497 => 39297, 17498 => 20869,
+17499 => 23273, 17500 => 33021, 17501 => 22958, 17502 => 38675, 17503 => 20522,
+17504 => 27877, 17505 => 23612, 17506 => 25311, 17507 => 20320, 17508 => 21311,
+17509 => 33147, 17510 => 36870, 17511 => 28346, 17512 => 34091, 17513 => 25288,
+17514 => 24180, 17515 => 30910, 17516 => 25781, 17517 => 25467, 17518 => 24565,
+17519 => 23064, 17520 => 37247, 17521 => 40479, 17522 => 23615, 17523 => 25423,
+17524 => 32834, 17525 => 23421, 17526 => 21870, 17527 => 38218, 17528 => 38221,
+17529 => 28037, 17530 => 24744, 17531 => 26592, 17532 => 29406, 17533 => 20957,
+17534 => 23425, 17697 => 25319, 17698 => 27870, 17699 => 29275, 17700 => 25197,
+17701 => 38062, 17702 => 32445, 17703 => 33043, 17704 => 27987, 17705 => 20892,
+17706 => 24324, 17707 => 22900, 17708 => 21162, 17709 => 24594, 17710 => 22899,
+17711 => 26262, 17712 => 34384, 17713 => 30111, 17714 => 25386, 17715 => 25062,
+17716 => 31983, 17717 => 35834, 17718 => 21734, 17719 => 27431, 17720 => 40485,
+17721 => 27572, 17722 => 34261, 17723 => 21589, 17724 => 20598, 17725 => 27812,
+17726 => 21866, 17727 => 36276, 17728 => 29228, 17729 => 24085, 17730 => 24597,
+17731 => 29750, 17732 => 25293, 17733 => 25490, 17734 => 29260, 17735 => 24472,
+17736 => 28227, 17737 => 27966, 17738 => 25856, 17739 => 28504, 17740 => 30424,
+17741 => 30928, 17742 => 30460, 17743 => 30036, 17744 => 21028, 17745 => 21467,
+17746 => 20051, 17747 => 24222, 17748 => 26049, 17749 => 32810, 17750 => 32982,
+17751 => 25243, 17752 => 21638, 17753 => 21032, 17754 => 28846, 17755 => 34957,
+17756 => 36305, 17757 => 27873, 17758 => 21624, 17759 => 32986, 17760 => 22521,
+17761 => 35060, 17762 => 36180, 17763 => 38506, 17764 => 37197, 17765 => 20329,
+17766 => 27803, 17767 => 21943, 17768 => 30406, 17769 => 30768, 17770 => 25256,
+17771 => 28921, 17772 => 28558, 17773 => 24429, 17774 => 34028, 17775 => 26842,
+17776 => 30844, 17777 => 31735, 17778 => 33192, 17779 => 26379, 17780 => 40527,
+17781 => 25447, 17782 => 30896, 17783 => 22383, 17784 => 30738, 17785 => 38713,
+17786 => 25209, 17787 => 25259, 17788 => 21128, 17789 => 29749, 17790 => 27607,
+17953 => 21860, 17954 => 33086, 17955 => 30130, 17956 => 30382, 17957 => 21305,
+17958 => 30174, 17959 => 20731, 17960 => 23617, 17961 => 35692, 17962 => 31687,
+17963 => 20559, 17964 => 29255, 17965 => 39575, 17966 => 39128, 17967 => 28418,
+17968 => 29922, 17969 => 31080, 17970 => 25735, 17971 => 30629, 17972 => 25340,
+17973 => 39057, 17974 => 36139, 17975 => 21697, 17976 => 32856, 17977 => 20050,
+17978 => 22378, 17979 => 33529, 17980 => 33805, 17981 => 24179, 17982 => 20973,
+17983 => 29942, 17984 => 35780, 17985 => 23631, 17986 => 22369, 17987 => 27900,
+17988 => 39047, 17989 => 23110, 17990 => 30772, 17991 => 39748, 17992 => 36843,
+17993 => 31893, 17994 => 21078, 17995 => 25169, 17996 => 38138, 17997 => 20166,
+17998 => 33670, 17999 => 33889, 18000 => 33769, 18001 => 33970, 18002 => 22484,
+18003 => 26420, 18004 => 22275, 18005 => 26222, 18006 => 28006, 18007 => 35889,
+18008 => 26333, 18009 => 28689, 18010 => 26399, 18011 => 27450, 18012 => 26646,
+18013 => 25114, 18014 => 22971, 18015 => 19971, 18016 => 20932, 18017 => 28422,
+18018 => 26578, 18019 => 27791, 18020 => 20854, 18021 => 26827, 18022 => 22855,
+18023 => 27495, 18024 => 30054, 18025 => 23822, 18026 => 33040, 18027 => 40784,
+18028 => 26071, 18029 => 31048, 18030 => 31041, 18031 => 39569, 18032 => 36215,
+18033 => 23682, 18034 => 20062, 18035 => 20225, 18036 => 21551, 18037 => 22865,
+18038 => 30732, 18039 => 22120, 18040 => 27668, 18041 => 36804, 18042 => 24323,
+18043 => 27773, 18044 => 27875, 18045 => 35755, 18046 => 25488, 18209 => 24688,
+18210 => 27965, 18211 => 29301, 18212 => 25190, 18213 => 38030, 18214 => 38085,
+18215 => 21315, 18216 => 36801, 18217 => 31614, 18218 => 20191, 18219 => 35878,
+18220 => 20094, 18221 => 40660, 18222 => 38065, 18223 => 38067, 18224 => 21069,
+18225 => 28508, 18226 => 36963, 18227 => 27973, 18228 => 35892, 18229 => 22545,
+18230 => 23884, 18231 => 27424, 18232 => 27465, 18233 => 26538, 18234 => 21595,
+18235 => 33108, 18236 => 32652, 18237 => 22681, 18238 => 34103, 18239 => 24378,
+18240 => 25250, 18241 => 27207, 18242 => 38201, 18243 => 25970, 18244 => 24708,
+18245 => 26725, 18246 => 30631, 18247 => 20052, 18248 => 20392, 18249 => 24039,
+18250 => 38808, 18251 => 25772, 18252 => 32728, 18253 => 23789, 18254 => 20431,
+18255 => 31373, 18256 => 20999, 18257 => 33540, 18258 => 19988, 18259 => 24623,
+18260 => 31363, 18261 => 38054, 18262 => 20405, 18263 => 20146, 18264 => 31206,
+18265 => 29748, 18266 => 21220, 18267 => 33465, 18268 => 25810, 18269 => 31165,
+18270 => 23517, 18271 => 27777, 18272 => 38738, 18273 => 36731, 18274 => 27682,
+18275 => 20542, 18276 => 21375, 18277 => 28165, 18278 => 25806, 18279 => 26228,
+18280 => 27696, 18281 => 24773, 18282 => 39031, 18283 => 35831, 18284 => 24198,
+18285 => 29756, 18286 => 31351, 18287 => 31179, 18288 => 19992, 18289 => 37041,
+18290 => 29699, 18291 => 27714, 18292 => 22234, 18293 => 37195, 18294 => 27845,
+18295 => 36235, 18296 => 21306, 18297 => 34502, 18298 => 26354, 18299 => 36527,
+18300 => 23624, 18301 => 39537, 18302 => 28192, 18465 => 21462, 18466 => 23094,
+18467 => 40843, 18468 => 36259, 18469 => 21435, 18470 => 22280, 18471 => 39079,
+18472 => 26435, 18473 => 37275, 18474 => 27849, 18475 => 20840, 18476 => 30154,
+18477 => 25331, 18478 => 29356, 18479 => 21048, 18480 => 21149, 18481 => 32570,
+18482 => 28820, 18483 => 30264, 18484 => 21364, 18485 => 40522, 18486 => 27063,
+18487 => 30830, 18488 => 38592, 18489 => 35033, 18490 => 32676, 18491 => 28982,
+18492 => 29123, 18493 => 20873, 18494 => 26579, 18495 => 29924, 18496 => 22756,
+18497 => 25880, 18498 => 22199, 18499 => 35753, 18500 => 39286, 18501 => 25200,
+18502 => 32469, 18503 => 24825, 18504 => 28909, 18505 => 22764, 18506 => 20161,
+18507 => 20154, 18508 => 24525, 18509 => 38887, 18510 => 20219, 18511 => 35748,
+18512 => 20995, 18513 => 22922, 18514 => 32427, 18515 => 25172, 18516 => 20173,
+18517 => 26085, 18518 => 25102, 18519 => 33592, 18520 => 33993, 18521 => 33635,
+18522 => 34701, 18523 => 29076, 18524 => 28342, 18525 => 23481, 18526 => 32466,
+18527 => 20887, 18528 => 25545, 18529 => 26580, 18530 => 32905, 18531 => 33593,
+18532 => 34837, 18533 => 20754, 18534 => 23418, 18535 => 22914, 18536 => 36785,
+18537 => 20083, 18538 => 27741, 18539 => 20837, 18540 => 35109, 18541 => 36719,
+18542 => 38446, 18543 => 34122, 18544 => 29790, 18545 => 38160, 18546 => 38384,
+18547 => 28070, 18548 => 33509, 18549 => 24369, 18550 => 25746, 18551 => 27922,
+18552 => 33832, 18553 => 33134, 18554 => 40131, 18555 => 22622, 18556 => 36187,
+18557 => 19977, 18558 => 21441, 18721 => 20254, 18722 => 25955, 18723 => 26705,
+18724 => 21971, 18725 => 20007, 18726 => 25620, 18727 => 39578, 18728 => 25195,
+18729 => 23234, 18730 => 29791, 18731 => 33394, 18732 => 28073, 18733 => 26862,
+18734 => 20711, 18735 => 33678, 18736 => 30722, 18737 => 26432, 18738 => 21049,
+18739 => 27801, 18740 => 32433, 18741 => 20667, 18742 => 21861, 18743 => 29022,
+18744 => 31579, 18745 => 26194, 18746 => 29642, 18747 => 33515, 18748 => 26441,
+18749 => 23665, 18750 => 21024, 18751 => 29053, 18752 => 34923, 18753 => 38378,
+18754 => 38485, 18755 => 25797, 18756 => 36193, 18757 => 33203, 18758 => 21892,
+18759 => 27733, 18760 => 25159, 18761 => 32558, 18762 => 22674, 18763 => 20260,
+18764 => 21830, 18765 => 36175, 18766 => 26188, 18767 => 19978, 18768 => 23578,
+18769 => 35059, 18770 => 26786, 18771 => 25422, 18772 => 31245, 18773 => 28903,
+18774 => 33421, 18775 => 21242, 18776 => 38902, 18777 => 23569, 18778 => 21736,
+18779 => 37045, 18780 => 32461, 18781 => 22882, 18782 => 36170, 18783 => 34503,
+18784 => 33292, 18785 => 33293, 18786 => 36198, 18787 => 25668, 18788 => 23556,
+18789 => 24913, 18790 => 28041, 18791 => 31038, 18792 => 35774, 18793 => 30775,
+18794 => 30003, 18795 => 21627, 18796 => 20280, 18797 => 36523, 18798 => 28145,
+18799 => 23072, 18800 => 32453, 18801 => 31070, 18802 => 27784, 18803 => 23457,
+18804 => 23158, 18805 => 29978, 18806 => 32958, 18807 => 24910, 18808 => 28183,
+18809 => 22768, 18810 => 29983, 18811 => 29989, 18812 => 29298, 18813 => 21319,
+18814 => 32499, 18977 => 30465, 18978 => 30427, 18979 => 21097, 18980 => 32988,
+18981 => 22307, 18982 => 24072, 18983 => 22833, 18984 => 29422, 18985 => 26045,
+18986 => 28287, 18987 => 35799, 18988 => 23608, 18989 => 34417, 18990 => 21313,
+18991 => 30707, 18992 => 25342, 18993 => 26102, 18994 => 20160, 18995 => 39135,
+18996 => 34432, 18997 => 23454, 18998 => 35782, 18999 => 21490, 19000 => 30690,
+19001 => 20351, 19002 => 23630, 19003 => 39542, 19004 => 22987, 19005 => 24335,
+19006 => 31034, 19007 => 22763, 19008 => 19990, 19009 => 26623, 19010 => 20107,
+19011 => 25325, 19012 => 35475, 19013 => 36893, 19014 => 21183, 19015 => 26159,
+19016 => 21980, 19017 => 22124, 19018 => 36866, 19019 => 20181, 19020 => 20365,
+19021 => 37322, 19022 => 39280, 19023 => 27663, 19024 => 24066, 19025 => 24643,
+19026 => 23460, 19027 => 35270, 19028 => 35797, 19029 => 25910, 19030 => 25163,
+19031 => 39318, 19032 => 23432, 19033 => 23551, 19034 => 25480, 19035 => 21806,
+19036 => 21463, 19037 => 30246, 19038 => 20861, 19039 => 34092, 19040 => 26530,
+19041 => 26803, 19042 => 27530, 19043 => 25234, 19044 => 36755, 19045 => 21460,
+19046 => 33298, 19047 => 28113, 19048 => 30095, 19049 => 20070, 19050 => 36174,
+19051 => 23408, 19052 => 29087, 19053 => 34223, 19054 => 26257, 19055 => 26329,
+19056 => 32626, 19057 => 34560, 19058 => 40653, 19059 => 40736, 19060 => 23646,
+19061 => 26415, 19062 => 36848, 19063 => 26641, 19064 => 26463, 19065 => 25101,
+19066 => 31446, 19067 => 22661, 19068 => 24246, 19069 => 25968, 19070 => 28465,
+19233 => 24661, 19234 => 21047, 19235 => 32781, 19236 => 25684, 19237 => 34928,
+19238 => 29993, 19239 => 24069, 19240 => 26643, 19241 => 25332, 19242 => 38684,
+19243 => 21452, 19244 => 29245, 19245 => 35841, 19246 => 27700, 19247 => 30561,
+19248 => 31246, 19249 => 21550, 19250 => 30636, 19251 => 39034, 19252 => 33308,
+19253 => 35828, 19254 => 30805, 19255 => 26388, 19256 => 28865, 19257 => 26031,
+19258 => 25749, 19259 => 22070, 19260 => 24605, 19261 => 31169, 19262 => 21496,
+19263 => 19997, 19264 => 27515, 19265 => 32902, 19266 => 23546, 19267 => 21987,
+19268 => 22235, 19269 => 20282, 19270 => 20284, 19271 => 39282, 19272 => 24051,
+19273 => 26494, 19274 => 32824, 19275 => 24578, 19276 => 39042, 19277 => 36865,
+19278 => 23435, 19279 => 35772, 19280 => 35829, 19281 => 25628, 19282 => 33368,
+19283 => 25822, 19284 => 22013, 19285 => 33487, 19286 => 37221, 19287 => 20439,
+19288 => 32032, 19289 => 36895, 19290 => 31903, 19291 => 20723, 19292 => 22609,
+19293 => 28335, 19294 => 23487, 19295 => 35785, 19296 => 32899, 19297 => 37240,
+19298 => 33948, 19299 => 31639, 19300 => 34429, 19301 => 38539, 19302 => 38543,
+19303 => 32485, 19304 => 39635, 19305 => 30862, 19306 => 23681, 19307 => 31319,
+19308 => 36930, 19309 => 38567, 19310 => 31071, 19311 => 23385, 19312 => 25439,
+19313 => 31499, 19314 => 34001, 19315 => 26797, 19316 => 21766, 19317 => 32553,
+19318 => 29712, 19319 => 32034, 19320 => 38145, 19321 => 25152, 19322 => 22604,
+19323 => 20182, 19324 => 23427, 19325 => 22905, 19326 => 22612, 19489 => 29549,
+19490 => 25374, 19491 => 36427, 19492 => 36367, 19493 => 32974, 19494 => 33492,
+19495 => 25260, 19496 => 21488, 19497 => 27888, 19498 => 37214, 19499 => 22826,
+19500 => 24577, 19501 => 27760, 19502 => 22349, 19503 => 25674, 19504 => 36138,
+19505 => 30251, 19506 => 28393, 19507 => 22363, 19508 => 27264, 19509 => 30192,
+19510 => 28525, 19511 => 35885, 19512 => 35848, 19513 => 22374, 19514 => 27631,
+19515 => 34962, 19516 => 30899, 19517 => 25506, 19518 => 21497, 19519 => 28845,
+19520 => 27748, 19521 => 22616, 19522 => 25642, 19523 => 22530, 19524 => 26848,
+19525 => 33179, 19526 => 21776, 19527 => 31958, 19528 => 20504, 19529 => 36538,
+19530 => 28108, 19531 => 36255, 19532 => 28907, 19533 => 25487, 19534 => 28059,
+19535 => 28372, 19536 => 32486, 19537 => 33796, 19538 => 26691, 19539 => 36867,
+19540 => 28120, 19541 => 38518, 19542 => 35752, 19543 => 22871, 19544 => 29305,
+19545 => 34276, 19546 => 33150, 19547 => 30140, 19548 => 35466, 19549 => 26799,
+19550 => 21076, 19551 => 36386, 19552 => 38161, 19553 => 25552, 19554 => 39064,
+19555 => 36420, 19556 => 21884, 19557 => 20307, 19558 => 26367, 19559 => 22159,
+19560 => 24789, 19561 => 28053, 19562 => 21059, 19563 => 23625, 19564 => 22825,
+19565 => 28155, 19566 => 22635, 19567 => 30000, 19568 => 29980, 19569 => 24684,
+19570 => 33300, 19571 => 33094, 19572 => 25361, 19573 => 26465, 19574 => 36834,
+19575 => 30522, 19576 => 36339, 19577 => 36148, 19578 => 38081, 19579 => 24086,
+19580 => 21381, 19581 => 21548, 19582 => 28867, 19745 => 27712, 19746 => 24311,
+19747 => 20572, 19748 => 20141, 19749 => 24237, 19750 => 25402, 19751 => 33351,
+19752 => 36890, 19753 => 26704, 19754 => 37230, 19755 => 30643, 19756 => 21516,
+19757 => 38108, 19758 => 24420, 19759 => 31461, 19760 => 26742, 19761 => 25413,
+19762 => 31570, 19763 => 32479, 19764 => 30171, 19765 => 20599, 19766 => 25237,
+19767 => 22836, 19768 => 36879, 19769 => 20984, 19770 => 31171, 19771 => 31361,
+19772 => 22270, 19773 => 24466, 19774 => 36884, 19775 => 28034, 19776 => 23648,
+19777 => 22303, 19778 => 21520, 19779 => 20820, 19780 => 28237, 19781 => 22242,
+19782 => 25512, 19783 => 39059, 19784 => 33151, 19785 => 34581, 19786 => 35114,
+19787 => 36864, 19788 => 21534, 19789 => 23663, 19790 => 33216, 19791 => 25302,
+19792 => 25176, 19793 => 33073, 19794 => 40501, 19795 => 38464, 19796 => 39534,
+19797 => 39548, 19798 => 26925, 19799 => 22949, 19800 => 25299, 19801 => 21822,
+19802 => 25366, 19803 => 21703, 19804 => 34521, 19805 => 27964, 19806 => 23043,
+19807 => 29926, 19808 => 34972, 19809 => 27498, 19810 => 22806, 19811 => 35916,
+19812 => 24367, 19813 => 28286, 19814 => 29609, 19815 => 39037, 19816 => 20024,
+19817 => 28919, 19818 => 23436, 19819 => 30871, 19820 => 25405, 19821 => 26202,
+19822 => 30358, 19823 => 24779, 19824 => 23451, 19825 => 23113, 19826 => 19975,
+19827 => 33109, 19828 => 27754, 19829 => 29579, 19830 => 20129, 19831 => 26505,
+19832 => 32593, 19833 => 24448, 19834 => 26106, 19835 => 26395, 19836 => 24536,
+19837 => 22916, 19838 => 23041, 20001 => 24013, 20002 => 24494, 20003 => 21361,
+20004 => 38886, 20005 => 36829, 20006 => 26693, 20007 => 22260, 20008 => 21807,
+20009 => 24799, 20010 => 20026, 20011 => 28493, 20012 => 32500, 20013 => 33479,
+20014 => 33806, 20015 => 22996, 20016 => 20255, 20017 => 20266, 20018 => 23614,
+20019 => 32428, 20020 => 26410, 20021 => 34074, 20022 => 21619, 20023 => 30031,
+20024 => 32963, 20025 => 21890, 20026 => 39759, 20027 => 20301, 20028 => 28205,
+20029 => 35859, 20030 => 23561, 20031 => 24944, 20032 => 21355, 20033 => 30239,
+20034 => 28201, 20035 => 34442, 20036 => 25991, 20037 => 38395, 20038 => 32441,
+20039 => 21563, 20040 => 31283, 20041 => 32010, 20042 => 38382, 20043 => 21985,
+20044 => 32705, 20045 => 29934, 20046 => 25373, 20047 => 34583, 20048 => 28065,
+20049 => 31389, 20050 => 25105, 20051 => 26017, 20052 => 21351, 20053 => 25569,
+20054 => 27779, 20055 => 24043, 20056 => 21596, 20057 => 38056, 20058 => 20044,
+20059 => 27745, 20060 => 35820, 20061 => 23627, 20062 => 26080, 20063 => 33436,
+20064 => 26791, 20065 => 21566, 20066 => 21556, 20067 => 27595, 20068 => 27494,
+20069 => 20116, 20070 => 25410, 20071 => 21320, 20072 => 33310, 20073 => 20237,
+20074 => 20398, 20075 => 22366, 20076 => 25098, 20077 => 38654, 20078 => 26212,
+20079 => 29289, 20080 => 21247, 20081 => 21153, 20082 => 24735, 20083 => 35823,
+20084 => 26132, 20085 => 29081, 20086 => 26512, 20087 => 35199, 20088 => 30802,
+20089 => 30717, 20090 => 26224, 20091 => 22075, 20092 => 21560, 20093 => 38177,
+20094 => 29306, 20257 => 31232, 20258 => 24687, 20259 => 24076, 20260 => 24713,
+20261 => 33181, 20262 => 22805, 20263 => 24796, 20264 => 29060, 20265 => 28911,
+20266 => 28330, 20267 => 27728, 20268 => 29312, 20269 => 27268, 20270 => 34989,
+20271 => 24109, 20272 => 20064, 20273 => 23219, 20274 => 21916, 20275 => 38115,
+20276 => 27927, 20277 => 31995, 20278 => 38553, 20279 => 25103, 20280 => 32454,
+20281 => 30606, 20282 => 34430, 20283 => 21283, 20284 => 38686, 20285 => 36758,
+20286 => 26247, 20287 => 23777, 20288 => 20384, 20289 => 29421, 20290 => 19979,
+20291 => 21414, 20292 => 22799, 20293 => 21523, 20294 => 25472, 20295 => 38184,
+20296 => 20808, 20297 => 20185, 20298 => 40092, 20299 => 32420, 20300 => 21688,
+20301 => 36132, 20302 => 34900, 20303 => 33335, 20304 => 38386, 20305 => 28046,
+20306 => 24358, 20307 => 23244, 20308 => 26174, 20309 => 38505, 20310 => 29616,
+20311 => 29486, 20312 => 21439, 20313 => 33146, 20314 => 39301, 20315 => 32673,
+20316 => 23466, 20317 => 38519, 20318 => 38480, 20319 => 32447, 20320 => 30456,
+20321 => 21410, 20322 => 38262, 20323 => 39321, 20324 => 31665, 20325 => 35140,
+20326 => 28248, 20327 => 20065, 20328 => 32724, 20329 => 31077, 20330 => 35814,
+20331 => 24819, 20332 => 21709, 20333 => 20139, 20334 => 39033, 20335 => 24055,
+20336 => 27233, 20337 => 20687, 20338 => 21521, 20339 => 35937, 20340 => 33831,
+20341 => 30813, 20342 => 38660, 20343 => 21066, 20344 => 21742, 20345 => 22179,
+20346 => 38144, 20347 => 28040, 20348 => 23477, 20349 => 28102, 20350 => 26195,
+20513 => 23567, 20514 => 23389, 20515 => 26657, 20516 => 32918, 20517 => 21880,
+20518 => 31505, 20519 => 25928, 20520 => 26964, 20521 => 20123, 20522 => 27463,
+20523 => 34638, 20524 => 38795, 20525 => 21327, 20526 => 25375, 20527 => 25658,
+20528 => 37034, 20529 => 26012, 20530 => 32961, 20531 => 35856, 20532 => 20889,
+20533 => 26800, 20534 => 21368, 20535 => 34809, 20536 => 25032, 20537 => 27844,
+20538 => 27899, 20539 => 35874, 20540 => 23633, 20541 => 34218, 20542 => 33455,
+20543 => 38156, 20544 => 27427, 20545 => 36763, 20546 => 26032, 20547 => 24571,
+20548 => 24515, 20549 => 20449, 20550 => 34885, 20551 => 26143, 20552 => 33125,
+20553 => 29481, 20554 => 24826, 20555 => 20852, 20556 => 21009, 20557 => 22411,
+20558 => 24418, 20559 => 37026, 20560 => 34892, 20561 => 37266, 20562 => 24184,
+20563 => 26447, 20564 => 24615, 20565 => 22995, 20566 => 20804, 20567 => 20982,
+20568 => 33016, 20569 => 21256, 20570 => 27769, 20571 => 38596, 20572 => 29066,
+20573 => 20241, 20574 => 20462, 20575 => 32670, 20576 => 26429, 20577 => 21957,
+20578 => 38152, 20579 => 31168, 20580 => 34966, 20581 => 32483, 20582 => 22687,
+20583 => 25100, 20584 => 38656, 20585 => 34394, 20586 => 22040, 20587 => 39035,
+20588 => 24464, 20589 => 35768, 20590 => 33988, 20591 => 37207, 20592 => 21465,
+20593 => 26093, 20594 => 24207, 20595 => 30044, 20596 => 24676, 20597 => 32110,
+20598 => 23167, 20599 => 32490, 20600 => 32493, 20601 => 36713, 20602 => 21927,
+20603 => 23459, 20604 => 24748, 20605 => 26059, 20606 => 29572, 20769 => 36873,
+20770 => 30307, 20771 => 30505, 20772 => 32474, 20773 => 38772, 20774 => 34203,
+20775 => 23398, 20776 => 31348, 20777 => 38634, 20778 => 34880, 20779 => 21195,
+20780 => 29071, 20781 => 24490, 20782 => 26092, 20783 => 35810, 20784 => 23547,
+20785 => 39535, 20786 => 24033, 20787 => 27529, 20788 => 27739, 20789 => 35757,
+20790 => 35759, 20791 => 36874, 20792 => 36805, 20793 => 21387, 20794 => 25276,
+20795 => 40486, 20796 => 40493, 20797 => 21568, 20798 => 20011, 20799 => 33469,
+20800 => 29273, 20801 => 34460, 20802 => 23830, 20803 => 34905, 20804 => 28079,
+20805 => 38597, 20806 => 21713, 20807 => 20122, 20808 => 35766, 20809 => 28937,
+20810 => 21693, 20811 => 38409, 20812 => 28895, 20813 => 28153, 20814 => 30416,
+20815 => 20005, 20816 => 30740, 20817 => 34578, 20818 => 23721, 20819 => 24310,
+20820 => 35328, 20821 => 39068, 20822 => 38414, 20823 => 28814, 20824 => 27839,
+20825 => 22852, 20826 => 25513, 20827 => 30524, 20828 => 34893, 20829 => 28436,
+20830 => 33395, 20831 => 22576, 20832 => 29141, 20833 => 21388, 20834 => 30746,
+20835 => 38593, 20836 => 21761, 20837 => 24422, 20838 => 28976, 20839 => 23476,
+20840 => 35866, 20841 => 39564, 20842 => 27523, 20843 => 22830, 20844 => 40495,
+20845 => 31207, 20846 => 26472, 20847 => 25196, 20848 => 20335, 20849 => 30113,
+20850 => 32650, 20851 => 27915, 20852 => 38451, 20853 => 27687, 20854 => 20208,
+20855 => 30162, 20856 => 20859, 20857 => 26679, 20858 => 28478, 20859 => 36992,
+20860 => 33136, 20861 => 22934, 20862 => 29814, 21025 => 25671, 21026 => 23591,
+21027 => 36965, 21028 => 31377, 21029 => 35875, 21030 => 23002, 21031 => 21676,
+21032 => 33280, 21033 => 33647, 21034 => 35201, 21035 => 32768, 21036 => 26928,
+21037 => 22094, 21038 => 32822, 21039 => 29239, 21040 => 37326, 21041 => 20918,
+21042 => 20063, 21043 => 39029, 21044 => 25494, 21045 => 19994, 21046 => 21494,
+21047 => 26355, 21048 => 33099, 21049 => 22812, 21050 => 28082, 21051 => 19968,
+21052 => 22777, 21053 => 21307, 21054 => 25558, 21055 => 38129, 21056 => 20381,
+21057 => 20234, 21058 => 34915, 21059 => 39056, 21060 => 22839, 21061 => 36951,
+21062 => 31227, 21063 => 20202, 21064 => 33008, 21065 => 30097, 21066 => 27778,
+21067 => 23452, 21068 => 23016, 21069 => 24413, 21070 => 26885, 21071 => 34433,
+21072 => 20506, 21073 => 24050, 21074 => 20057, 21075 => 30691, 21076 => 20197,
+21077 => 33402, 21078 => 25233, 21079 => 26131, 21080 => 37009, 21081 => 23673,
+21082 => 20159, 21083 => 24441, 21084 => 33222, 21085 => 36920, 21086 => 32900,
+21087 => 30123, 21088 => 20134, 21089 => 35028, 21090 => 24847, 21091 => 27589,
+21092 => 24518, 21093 => 20041, 21094 => 30410, 21095 => 28322, 21096 => 35811,
+21097 => 35758, 21098 => 35850, 21099 => 35793, 21100 => 24322, 21101 => 32764,
+21102 => 32716, 21103 => 32462, 21104 => 33589, 21105 => 33643, 21106 => 22240,
+21107 => 27575, 21108 => 38899, 21109 => 38452, 21110 => 23035, 21111 => 21535,
+21112 => 38134, 21113 => 28139, 21114 => 23493, 21115 => 39278, 21116 => 23609,
+21117 => 24341, 21118 => 38544, 21281 => 21360, 21282 => 33521, 21283 => 27185,
+21284 => 23156, 21285 => 40560, 21286 => 24212, 21287 => 32552, 21288 => 33721,
+21289 => 33828, 21290 => 33829, 21291 => 33639, 21292 => 34631, 21293 => 36814,
+21294 => 36194, 21295 => 30408, 21296 => 24433, 21297 => 39062, 21298 => 30828,
+21299 => 26144, 21300 => 21727, 21301 => 25317, 21302 => 20323, 21303 => 33219,
+21304 => 30152, 21305 => 24248, 21306 => 38605, 21307 => 36362, 21308 => 34553,
+21309 => 21647, 21310 => 27891, 21311 => 28044, 21312 => 27704, 21313 => 24703,
+21314 => 21191, 21315 => 29992, 21316 => 24189, 21317 => 20248, 21318 => 24736,
+21319 => 24551, 21320 => 23588, 21321 => 30001, 21322 => 37038, 21323 => 38080,
+21324 => 29369, 21325 => 27833, 21326 => 28216, 21327 => 37193, 21328 => 26377,
+21329 => 21451, 21330 => 21491, 21331 => 20305, 21332 => 37321, 21333 => 35825,
+21334 => 21448, 21335 => 24188, 21336 => 36802, 21337 => 28132, 21338 => 20110,
+21339 => 30402, 21340 => 27014, 21341 => 34398, 21342 => 24858, 21343 => 33286,
+21344 => 20313, 21345 => 20446, 21346 => 36926, 21347 => 40060, 21348 => 24841,
+21349 => 28189, 21350 => 28180, 21351 => 38533, 21352 => 20104, 21353 => 23089,
+21354 => 38632, 21355 => 19982, 21356 => 23679, 21357 => 31161, 21358 => 23431,
+21359 => 35821, 21360 => 32701, 21361 => 29577, 21362 => 22495, 21363 => 33419,
+21364 => 37057, 21365 => 21505, 21366 => 36935, 21367 => 21947, 21368 => 23786,
+21369 => 24481, 21370 => 24840, 21371 => 27442, 21372 => 29425, 21373 => 32946,
+21374 => 35465, 21537 => 28020, 21538 => 23507, 21539 => 35029, 21540 => 39044,
+21541 => 35947, 21542 => 39533, 21543 => 40499, 21544 => 28170, 21545 => 20900,
+21546 => 20803, 21547 => 22435, 21548 => 34945, 21549 => 21407, 21550 => 25588,
+21551 => 36757, 21552 => 22253, 21553 => 21592, 21554 => 22278, 21555 => 29503,
+21556 => 28304, 21557 => 32536, 21558 => 36828, 21559 => 33489, 21560 => 24895,
+21561 => 24616, 21562 => 38498, 21563 => 26352, 21564 => 32422, 21565 => 36234,
+21566 => 36291, 21567 => 38053, 21568 => 23731, 21569 => 31908, 21570 => 26376,
+21571 => 24742, 21572 => 38405, 21573 => 32792, 21574 => 20113, 21575 => 37095,
+21576 => 21248, 21577 => 38504, 21578 => 20801, 21579 => 36816, 21580 => 34164,
+21581 => 37213, 21582 => 26197, 21583 => 38901, 21584 => 23381, 21585 => 21277,
+21586 => 30776, 21587 => 26434, 21588 => 26685, 21589 => 21705, 21590 => 28798,
+21591 => 23472, 21592 => 36733, 21593 => 20877, 21594 => 22312, 21595 => 21681,
+21596 => 25874, 21597 => 26242, 21598 => 36190, 21599 => 36163, 21600 => 33039,
+21601 => 33900, 21602 => 36973, 21603 => 31967, 21604 => 20991, 21605 => 34299,
+21606 => 26531, 21607 => 26089, 21608 => 28577, 21609 => 34468, 21610 => 36481,
+21611 => 22122, 21612 => 36896, 21613 => 30338, 21614 => 28790, 21615 => 29157,
+21616 => 36131, 21617 => 25321, 21618 => 21017, 21619 => 27901, 21620 => 36156,
+21621 => 24590, 21622 => 22686, 21623 => 24974, 21624 => 26366, 21625 => 36192,
+21626 => 25166, 21627 => 21939, 21628 => 28195, 21629 => 26413, 21630 => 36711,
+21793 => 38113, 21794 => 38392, 21795 => 30504, 21796 => 26629, 21797 => 27048,
+21798 => 21643, 21799 => 20045, 21800 => 28856, 21801 => 35784, 21802 => 25688,
+21803 => 25995, 21804 => 23429, 21805 => 31364, 21806 => 20538, 21807 => 23528,
+21808 => 30651, 21809 => 27617, 21810 => 35449, 21811 => 31896, 21812 => 27838,
+21813 => 30415, 21814 => 26025, 21815 => 36759, 21816 => 23853, 21817 => 23637,
+21818 => 34360, 21819 => 26632, 21820 => 21344, 21821 => 25112, 21822 => 31449,
+21823 => 28251, 21824 => 32509, 21825 => 27167, 21826 => 31456, 21827 => 24432,
+21828 => 28467, 21829 => 24352, 21830 => 25484, 21831 => 28072, 21832 => 26454,
+21833 => 19976, 21834 => 24080, 21835 => 36134, 21836 => 20183, 21837 => 32960,
+21838 => 30260, 21839 => 38556, 21840 => 25307, 21841 => 26157, 21842 => 25214,
+21843 => 27836, 21844 => 36213, 21845 => 29031, 21846 => 32617, 21847 => 20806,
+21848 => 32903, 21849 => 21484, 21850 => 36974, 21851 => 25240, 21852 => 21746,
+21853 => 34544, 21854 => 36761, 21855 => 32773, 21856 => 38167, 21857 => 34071,
+21858 => 36825, 21859 => 27993, 21860 => 29645, 21861 => 26015, 21862 => 30495,
+21863 => 29956, 21864 => 30759, 21865 => 33275, 21866 => 36126, 21867 => 38024,
+21868 => 20390, 21869 => 26517, 21870 => 30137, 21871 => 35786, 21872 => 38663,
+21873 => 25391, 21874 => 38215, 21875 => 38453, 21876 => 33976, 21877 => 25379,
+21878 => 30529, 21879 => 24449, 21880 => 29424, 21881 => 20105, 21882 => 24596,
+21883 => 25972, 21884 => 25327, 21885 => 27491, 21886 => 25919, 22049 => 24103,
+22050 => 30151, 22051 => 37073, 22052 => 35777, 22053 => 33437, 22054 => 26525,
+22055 => 25903, 22056 => 21553, 22057 => 34584, 22058 => 30693, 22059 => 32930,
+22060 => 33026, 22061 => 27713, 22062 => 20043, 22063 => 32455, 22064 => 32844,
+22065 => 30452, 22066 => 26893, 22067 => 27542, 22068 => 25191, 22069 => 20540,
+22070 => 20356, 22071 => 22336, 22072 => 25351, 22073 => 27490, 22074 => 36286,
+22075 => 21482, 22076 => 26088, 22077 => 32440, 22078 => 24535, 22079 => 25370,
+22080 => 25527, 22081 => 33267, 22082 => 33268, 22083 => 32622, 22084 => 24092,
+22085 => 23769, 22086 => 21046, 22087 => 26234, 22088 => 31209, 22089 => 31258,
+22090 => 36136, 22091 => 28825, 22092 => 30164, 22093 => 28382, 22094 => 27835,
+22095 => 31378, 22096 => 20013, 22097 => 30405, 22098 => 24544, 22099 => 38047,
+22100 => 34935, 22101 => 32456, 22102 => 31181, 22103 => 32959, 22104 => 37325,
+22105 => 20210, 22106 => 20247, 22107 => 33311, 22108 => 21608, 22109 => 24030,
+22110 => 27954, 22111 => 35788, 22112 => 31909, 22113 => 36724, 22114 => 32920,
+22115 => 24090, 22116 => 21650, 22117 => 30385, 22118 => 23449, 22119 => 26172,
+22120 => 39588, 22121 => 29664, 22122 => 26666, 22123 => 34523, 22124 => 26417,
+22125 => 29482, 22126 => 35832, 22127 => 35803, 22128 => 36880, 22129 => 31481,
+22130 => 28891, 22131 => 29038, 22132 => 25284, 22133 => 30633, 22134 => 22065,
+22135 => 20027, 22136 => 33879, 22137 => 26609, 22138 => 21161, 22139 => 34496,
+22140 => 36142, 22141 => 38136, 22142 => 31569, 22305 => 20303, 22306 => 27880,
+22307 => 31069, 22308 => 39547, 22309 => 25235, 22310 => 29226, 22311 => 25341,
+22312 => 19987, 22313 => 30742, 22314 => 36716, 22315 => 25776, 22316 => 36186,
+22317 => 31686, 22318 => 26729, 22319 => 24196, 22320 => 35013, 22321 => 22918,
+22322 => 25758, 22323 => 22766, 22324 => 29366, 22325 => 26894, 22326 => 38181,
+22327 => 36861, 22328 => 36184, 22329 => 22368, 22330 => 32512, 22331 => 35846,
+22332 => 20934, 22333 => 25417, 22334 => 25305, 22335 => 21331, 22336 => 26700,
+22337 => 29730, 22338 => 33537, 22339 => 37196, 22340 => 21828, 22341 => 30528,
+22342 => 28796, 22343 => 27978, 22344 => 20857, 22345 => 21672, 22346 => 36164,
+22347 => 23039, 22348 => 28363, 22349 => 28100, 22350 => 23388, 22351 => 32043,
+22352 => 20180, 22353 => 31869, 22354 => 28371, 22355 => 23376, 22356 => 33258,
+22357 => 28173, 22358 => 23383, 22359 => 39683, 22360 => 26837, 22361 => 36394,
+22362 => 23447, 22363 => 32508, 22364 => 24635, 22365 => 32437, 22366 => 37049,
+22367 => 36208, 22368 => 22863, 22369 => 25549, 22370 => 31199, 22371 => 36275,
+22372 => 21330, 22373 => 26063, 22374 => 31062, 22375 => 35781, 22376 => 38459,
+22377 => 32452, 22378 => 38075, 22379 => 32386, 22380 => 22068, 22381 => 37257,
+22382 => 26368, 22383 => 32618, 22384 => 23562, 22385 => 36981, 22386 => 26152,
+22387 => 24038, 22388 => 20304, 22389 => 26590, 22390 => 20570, 22391 => 20316,
+22392 => 22352, 22393 => 24231, 22561 => 20109, 22562 => 19980, 22563 => 20800,
+22564 => 19984, 22565 => 24319, 22566 => 21317, 22567 => 19989, 22568 => 20120,
+22569 => 19998, 22570 => 39730, 22571 => 23404, 22572 => 22121, 22573 => 20008,
+22574 => 31162, 22575 => 20031, 22576 => 21269, 22577 => 20039, 22578 => 22829,
+22579 => 29243, 22580 => 21358, 22581 => 27664, 22582 => 22239, 22583 => 32996,
+22584 => 39319, 22585 => 27603, 22586 => 30590, 22587 => 40727, 22588 => 20022,
+22589 => 20127, 22590 => 40720, 22591 => 20060, 22592 => 20073, 22593 => 20115,
+22594 => 33416, 22595 => 23387, 22596 => 21868, 22597 => 22031, 22598 => 20164,
+22599 => 21389, 22600 => 21405, 22601 => 21411, 22602 => 21413, 22603 => 21422,
+22604 => 38757, 22605 => 36189, 22606 => 21274, 22607 => 21493, 22608 => 21286,
+22609 => 21294, 22610 => 21310, 22611 => 36188, 22612 => 21350, 22613 => 21347,
+22614 => 20994, 22615 => 21000, 22616 => 21006, 22617 => 21037, 22618 => 21043,
+22619 => 21055, 22620 => 21056, 22621 => 21068, 22622 => 21086, 22623 => 21089,
+22624 => 21084, 22625 => 33967, 22626 => 21117, 22627 => 21122, 22628 => 21121,
+22629 => 21136, 22630 => 21139, 22631 => 20866, 22632 => 32596, 22633 => 20155,
+22634 => 20163, 22635 => 20169, 22636 => 20162, 22637 => 20200, 22638 => 20193,
+22639 => 20203, 22640 => 20190, 22641 => 20251, 22642 => 20211, 22643 => 20258,
+22644 => 20324, 22645 => 20213, 22646 => 20261, 22647 => 20263, 22648 => 20233,
+22649 => 20267, 22650 => 20318, 22651 => 20327, 22652 => 25912, 22653 => 20314,
+22654 => 20317, 22817 => 20319, 22818 => 20311, 22819 => 20274, 22820 => 20285,
+22821 => 20342, 22822 => 20340, 22823 => 20369, 22824 => 20361, 22825 => 20355,
+22826 => 20367, 22827 => 20350, 22828 => 20347, 22829 => 20394, 22830 => 20348,
+22831 => 20396, 22832 => 20372, 22833 => 20454, 22834 => 20456, 22835 => 20458,
+22836 => 20421, 22837 => 20442, 22838 => 20451, 22839 => 20444, 22840 => 20433,
+22841 => 20447, 22842 => 20472, 22843 => 20521, 22844 => 20556, 22845 => 20467,
+22846 => 20524, 22847 => 20495, 22848 => 20526, 22849 => 20525, 22850 => 20478,
+22851 => 20508, 22852 => 20492, 22853 => 20517, 22854 => 20520, 22855 => 20606,
+22856 => 20547, 22857 => 20565, 22858 => 20552, 22859 => 20558, 22860 => 20588,
+22861 => 20603, 22862 => 20645, 22863 => 20647, 22864 => 20649, 22865 => 20666,
+22866 => 20694, 22867 => 20742, 22868 => 20717, 22869 => 20716, 22870 => 20710,
+22871 => 20718, 22872 => 20743, 22873 => 20747, 22874 => 20189, 22875 => 27709,
+22876 => 20312, 22877 => 20325, 22878 => 20430, 22879 => 40864, 22880 => 27718,
+22881 => 31860, 22882 => 20846, 22883 => 24061, 22884 => 40649, 22885 => 39320,
+22886 => 20865, 22887 => 22804, 22888 => 21241, 22889 => 21261, 22890 => 35335,
+22891 => 21264, 22892 => 20971, 22893 => 22809, 22894 => 20821, 22895 => 20128,
+22896 => 20822, 22897 => 20147, 22898 => 34926, 22899 => 34980, 22900 => 20149,
+22901 => 33044, 22902 => 35026, 22903 => 31104, 22904 => 23348, 22905 => 34819,
+22906 => 32696, 22907 => 20907, 22908 => 20913, 22909 => 20925, 22910 => 20924,
+23073 => 20935, 23074 => 20886, 23075 => 20898, 23076 => 20901, 23077 => 35744,
+23078 => 35750, 23079 => 35751, 23080 => 35754, 23081 => 35764, 23082 => 35765,
+23083 => 35767, 23084 => 35778, 23085 => 35779, 23086 => 35787, 23087 => 35791,
+23088 => 35790, 23089 => 35794, 23090 => 35795, 23091 => 35796, 23092 => 35798,
+23093 => 35800, 23094 => 35801, 23095 => 35804, 23096 => 35807, 23097 => 35808,
+23098 => 35812, 23099 => 35816, 23100 => 35817, 23101 => 35822, 23102 => 35824,
+23103 => 35827, 23104 => 35830, 23105 => 35833, 23106 => 35836, 23107 => 35839,
+23108 => 35840, 23109 => 35842, 23110 => 35844, 23111 => 35847, 23112 => 35852,
+23113 => 35855, 23114 => 35857, 23115 => 35858, 23116 => 35860, 23117 => 35861,
+23118 => 35862, 23119 => 35865, 23120 => 35867, 23121 => 35864, 23122 => 35869,
+23123 => 35871, 23124 => 35872, 23125 => 35873, 23126 => 35877, 23127 => 35879,
+23128 => 35882, 23129 => 35883, 23130 => 35886, 23131 => 35887, 23132 => 35890,
+23133 => 35891, 23134 => 35893, 23135 => 35894, 23136 => 21353, 23137 => 21370,
+23138 => 38429, 23139 => 38434, 23140 => 38433, 23141 => 38449, 23142 => 38442,
+23143 => 38461, 23144 => 38460, 23145 => 38466, 23146 => 38473, 23147 => 38484,
+23148 => 38495, 23149 => 38503, 23150 => 38508, 23151 => 38514, 23152 => 38516,
+23153 => 38536, 23154 => 38541, 23155 => 38551, 23156 => 38576, 23157 => 37015,
+23158 => 37019, 23159 => 37021, 23160 => 37017, 23161 => 37036, 23162 => 37025,
+23163 => 37044, 23164 => 37043, 23165 => 37046, 23166 => 37050, 23329 => 37048,
+23330 => 37040, 23331 => 37071, 23332 => 37061, 23333 => 37054, 23334 => 37072,
+23335 => 37060, 23336 => 37063, 23337 => 37075, 23338 => 37094, 23339 => 37090,
+23340 => 37084, 23341 => 37079, 23342 => 37083, 23343 => 37099, 23344 => 37103,
+23345 => 37118, 23346 => 37124, 23347 => 37154, 23348 => 37150, 23349 => 37155,
+23350 => 37169, 23351 => 37167, 23352 => 37177, 23353 => 37187, 23354 => 37190,
+23355 => 21005, 23356 => 22850, 23357 => 21154, 23358 => 21164, 23359 => 21165,
+23360 => 21182, 23361 => 21759, 23362 => 21200, 23363 => 21206, 23364 => 21232,
+23365 => 21471, 23366 => 29166, 23367 => 30669, 23368 => 24308, 23369 => 20981,
+23370 => 20988, 23371 => 39727, 23372 => 21430, 23373 => 24321, 23374 => 30042,
+23375 => 24047, 23376 => 22348, 23377 => 22441, 23378 => 22433, 23379 => 22654,
+23380 => 22716, 23381 => 22725, 23382 => 22737, 23383 => 22313, 23384 => 22316,
+23385 => 22314, 23386 => 22323, 23387 => 22329, 23388 => 22318, 23389 => 22319,
+23390 => 22364, 23391 => 22331, 23392 => 22338, 23393 => 22377, 23394 => 22405,
+23395 => 22379, 23396 => 22406, 23397 => 22396, 23398 => 22395, 23399 => 22376,
+23400 => 22381, 23401 => 22390, 23402 => 22387, 23403 => 22445, 23404 => 22436,
+23405 => 22412, 23406 => 22450, 23407 => 22479, 23408 => 22439, 23409 => 22452,
+23410 => 22419, 23411 => 22432, 23412 => 22485, 23413 => 22488, 23414 => 22490,
+23415 => 22489, 23416 => 22482, 23417 => 22456, 23418 => 22516, 23419 => 22511,
+23420 => 22520, 23421 => 22500, 23422 => 22493, 23585 => 22539, 23586 => 22541,
+23587 => 22525, 23588 => 22509, 23589 => 22528, 23590 => 22558, 23591 => 22553,
+23592 => 22596, 23593 => 22560, 23594 => 22629, 23595 => 22636, 23596 => 22657,
+23597 => 22665, 23598 => 22682, 23599 => 22656, 23600 => 39336, 23601 => 40729,
+23602 => 25087, 23603 => 33401, 23604 => 33405, 23605 => 33407, 23606 => 33423,
+23607 => 33418, 23608 => 33448, 23609 => 33412, 23610 => 33422, 23611 => 33425,
+23612 => 33431, 23613 => 33433, 23614 => 33451, 23615 => 33464, 23616 => 33470,
+23617 => 33456, 23618 => 33480, 23619 => 33482, 23620 => 33507, 23621 => 33432,
+23622 => 33463, 23623 => 33454, 23624 => 33483, 23625 => 33484, 23626 => 33473,
+23627 => 33449, 23628 => 33460, 23629 => 33441, 23630 => 33450, 23631 => 33439,
+23632 => 33476, 23633 => 33486, 23634 => 33444, 23635 => 33505, 23636 => 33545,
+23637 => 33527, 23638 => 33508, 23639 => 33551, 23640 => 33543, 23641 => 33500,
+23642 => 33524, 23643 => 33490, 23644 => 33496, 23645 => 33548, 23646 => 33531,
+23647 => 33491, 23648 => 33553, 23649 => 33562, 23650 => 33542, 23651 => 33556,
+23652 => 33557, 23653 => 33504, 23654 => 33493, 23655 => 33564, 23656 => 33617,
+23657 => 33627, 23658 => 33628, 23659 => 33544, 23660 => 33682, 23661 => 33596,
+23662 => 33588, 23663 => 33585, 23664 => 33691, 23665 => 33630, 23666 => 33583,
+23667 => 33615, 23668 => 33607, 23669 => 33603, 23670 => 33631, 23671 => 33600,
+23672 => 33559, 23673 => 33632, 23674 => 33581, 23675 => 33594, 23676 => 33587,
+23677 => 33638, 23678 => 33637, 23841 => 33640, 23842 => 33563, 23843 => 33641,
+23844 => 33644, 23845 => 33642, 23846 => 33645, 23847 => 33646, 23848 => 33712,
+23849 => 33656, 23850 => 33715, 23851 => 33716, 23852 => 33696, 23853 => 33706,
+23854 => 33683, 23855 => 33692, 23856 => 33669, 23857 => 33660, 23858 => 33718,
+23859 => 33705, 23860 => 33661, 23861 => 33720, 23862 => 33659, 23863 => 33688,
+23864 => 33694, 23865 => 33704, 23866 => 33722, 23867 => 33724, 23868 => 33729,
+23869 => 33793, 23870 => 33765, 23871 => 33752, 23872 => 22535, 23873 => 33816,
+23874 => 33803, 23875 => 33757, 23876 => 33789, 23877 => 33750, 23878 => 33820,
+23879 => 33848, 23880 => 33809, 23881 => 33798, 23882 => 33748, 23883 => 33759,
+23884 => 33807, 23885 => 33795, 23886 => 33784, 23887 => 33785, 23888 => 33770,
+23889 => 33733, 23890 => 33728, 23891 => 33830, 23892 => 33776, 23893 => 33761,
+23894 => 33884, 23895 => 33873, 23896 => 33882, 23897 => 33881, 23898 => 33907,
+23899 => 33927, 23900 => 33928, 23901 => 33914, 23902 => 33929, 23903 => 33912,
+23904 => 33852, 23905 => 33862, 23906 => 33897, 23907 => 33910, 23908 => 33932,
+23909 => 33934, 23910 => 33841, 23911 => 33901, 23912 => 33985, 23913 => 33997,
+23914 => 34000, 23915 => 34022, 23916 => 33981, 23917 => 34003, 23918 => 33994,
+23919 => 33983, 23920 => 33978, 23921 => 34016, 23922 => 33953, 23923 => 33977,
+23924 => 33972, 23925 => 33943, 23926 => 34021, 23927 => 34019, 23928 => 34060,
+23929 => 29965, 23930 => 34104, 23931 => 34032, 23932 => 34105, 23933 => 34079,
+23934 => 34106, 24097 => 34134, 24098 => 34107, 24099 => 34047, 24100 => 34044,
+24101 => 34137, 24102 => 34120, 24103 => 34152, 24104 => 34148, 24105 => 34142,
+24106 => 34170, 24107 => 30626, 24108 => 34115, 24109 => 34162, 24110 => 34171,
+24111 => 34212, 24112 => 34216, 24113 => 34183, 24114 => 34191, 24115 => 34169,
+24116 => 34222, 24117 => 34204, 24118 => 34181, 24119 => 34233, 24120 => 34231,
+24121 => 34224, 24122 => 34259, 24123 => 34241, 24124 => 34268, 24125 => 34303,
+24126 => 34343, 24127 => 34309, 24128 => 34345, 24129 => 34326, 24130 => 34364,
+24131 => 24318, 24132 => 24328, 24133 => 22844, 24134 => 22849, 24135 => 32823,
+24136 => 22869, 24137 => 22874, 24138 => 22872, 24139 => 21263, 24140 => 23586,
+24141 => 23589, 24142 => 23596, 24143 => 23604, 24144 => 25164, 24145 => 25194,
+24146 => 25247, 24147 => 25275, 24148 => 25290, 24149 => 25306, 24150 => 25303,
+24151 => 25326, 24152 => 25378, 24153 => 25334, 24154 => 25401, 24155 => 25419,
+24156 => 25411, 24157 => 25517, 24158 => 25590, 24159 => 25457, 24160 => 25466,
+24161 => 25486, 24162 => 25524, 24163 => 25453, 24164 => 25516, 24165 => 25482,
+24166 => 25449, 24167 => 25518, 24168 => 25532, 24169 => 25586, 24170 => 25592,
+24171 => 25568, 24172 => 25599, 24173 => 25540, 24174 => 25566, 24175 => 25550,
+24176 => 25682, 24177 => 25542, 24178 => 25534, 24179 => 25669, 24180 => 25665,
+24181 => 25611, 24182 => 25627, 24183 => 25632, 24184 => 25612, 24185 => 25638,
+24186 => 25633, 24187 => 25694, 24188 => 25732, 24189 => 25709, 24190 => 25750,
+24353 => 25722, 24354 => 25783, 24355 => 25784, 24356 => 25753, 24357 => 25786,
+24358 => 25792, 24359 => 25808, 24360 => 25815, 24361 => 25828, 24362 => 25826,
+24363 => 25865, 24364 => 25893, 24365 => 25902, 24366 => 24331, 24367 => 24530,
+24368 => 29977, 24369 => 24337, 24370 => 21343, 24371 => 21489, 24372 => 21501,
+24373 => 21481, 24374 => 21480, 24375 => 21499, 24376 => 21522, 24377 => 21526,
+24378 => 21510, 24379 => 21579, 24380 => 21586, 24381 => 21587, 24382 => 21588,
+24383 => 21590, 24384 => 21571, 24385 => 21537, 24386 => 21591, 24387 => 21593,
+24388 => 21539, 24389 => 21554, 24390 => 21634, 24391 => 21652, 24392 => 21623,
+24393 => 21617, 24394 => 21604, 24395 => 21658, 24396 => 21659, 24397 => 21636,
+24398 => 21622, 24399 => 21606, 24400 => 21661, 24401 => 21712, 24402 => 21677,
+24403 => 21698, 24404 => 21684, 24405 => 21714, 24406 => 21671, 24407 => 21670,
+24408 => 21715, 24409 => 21716, 24410 => 21618, 24411 => 21667, 24412 => 21717,
+24413 => 21691, 24414 => 21695, 24415 => 21708, 24416 => 21721, 24417 => 21722,
+24418 => 21724, 24419 => 21673, 24420 => 21674, 24421 => 21668, 24422 => 21725,
+24423 => 21711, 24424 => 21726, 24425 => 21787, 24426 => 21735, 24427 => 21792,
+24428 => 21757, 24429 => 21780, 24430 => 21747, 24431 => 21794, 24432 => 21795,
+24433 => 21775, 24434 => 21777, 24435 => 21799, 24436 => 21802, 24437 => 21863,
+24438 => 21903, 24439 => 21941, 24440 => 21833, 24441 => 21869, 24442 => 21825,
+24443 => 21845, 24444 => 21823, 24445 => 21840, 24446 => 21820, 24609 => 21815,
+24610 => 21846, 24611 => 21877, 24612 => 21878, 24613 => 21879, 24614 => 21811,
+24615 => 21808, 24616 => 21852, 24617 => 21899, 24618 => 21970, 24619 => 21891,
+24620 => 21937, 24621 => 21945, 24622 => 21896, 24623 => 21889, 24624 => 21919,
+24625 => 21886, 24626 => 21974, 24627 => 21905, 24628 => 21883, 24629 => 21983,
+24630 => 21949, 24631 => 21950, 24632 => 21908, 24633 => 21913, 24634 => 21994,
+24635 => 22007, 24636 => 21961, 24637 => 22047, 24638 => 21969, 24639 => 21995,
+24640 => 21996, 24641 => 21972, 24642 => 21990, 24643 => 21981, 24644 => 21956,
+24645 => 21999, 24646 => 21989, 24647 => 22002, 24648 => 22003, 24649 => 21964,
+24650 => 21965, 24651 => 21992, 24652 => 22005, 24653 => 21988, 24654 => 36756,
+24655 => 22046, 24656 => 22024, 24657 => 22028, 24658 => 22017, 24659 => 22052,
+24660 => 22051, 24661 => 22014, 24662 => 22016, 24663 => 22055, 24664 => 22061,
+24665 => 22104, 24666 => 22073, 24667 => 22103, 24668 => 22060, 24669 => 22093,
+24670 => 22114, 24671 => 22105, 24672 => 22108, 24673 => 22092, 24674 => 22100,
+24675 => 22150, 24676 => 22116, 24677 => 22129, 24678 => 22123, 24679 => 22139,
+24680 => 22140, 24681 => 22149, 24682 => 22163, 24683 => 22191, 24684 => 22228,
+24685 => 22231, 24686 => 22237, 24687 => 22241, 24688 => 22261, 24689 => 22251,
+24690 => 22265, 24691 => 22271, 24692 => 22276, 24693 => 22282, 24694 => 22281,
+24695 => 22300, 24696 => 24079, 24697 => 24089, 24698 => 24084, 24699 => 24081,
+24700 => 24113, 24701 => 24123, 24702 => 24124, 24865 => 24119, 24866 => 24132,
+24867 => 24148, 24868 => 24155, 24869 => 24158, 24870 => 24161, 24871 => 23692,
+24872 => 23674, 24873 => 23693, 24874 => 23696, 24875 => 23702, 24876 => 23688,
+24877 => 23704, 24878 => 23705, 24879 => 23697, 24880 => 23706, 24881 => 23708,
+24882 => 23733, 24883 => 23714, 24884 => 23741, 24885 => 23724, 24886 => 23723,
+24887 => 23729, 24888 => 23715, 24889 => 23745, 24890 => 23735, 24891 => 23748,
+24892 => 23762, 24893 => 23780, 24894 => 23755, 24895 => 23781, 24896 => 23810,
+24897 => 23811, 24898 => 23847, 24899 => 23846, 24900 => 23854, 24901 => 23844,
+24902 => 23838, 24903 => 23814, 24904 => 23835, 24905 => 23896, 24906 => 23870,
+24907 => 23860, 24908 => 23869, 24909 => 23916, 24910 => 23899, 24911 => 23919,
+24912 => 23901, 24913 => 23915, 24914 => 23883, 24915 => 23882, 24916 => 23913,
+24917 => 23924, 24918 => 23938, 24919 => 23961, 24920 => 23965, 24921 => 35955,
+24922 => 23991, 24923 => 24005, 24924 => 24435, 24925 => 24439, 24926 => 24450,
+24927 => 24455, 24928 => 24457, 24929 => 24460, 24930 => 24469, 24931 => 24473,
+24932 => 24476, 24933 => 24488, 24934 => 24493, 24935 => 24501, 24936 => 24508,
+24937 => 34914, 24938 => 24417, 24939 => 29357, 24940 => 29360, 24941 => 29364,
+24942 => 29367, 24943 => 29368, 24944 => 29379, 24945 => 29377, 24946 => 29390,
+24947 => 29389, 24948 => 29394, 24949 => 29416, 24950 => 29423, 24951 => 29417,
+24952 => 29426, 24953 => 29428, 24954 => 29431, 24955 => 29441, 24956 => 29427,
+24957 => 29443, 24958 => 29434, 25121 => 29435, 25122 => 29463, 25123 => 29459,
+25124 => 29473, 25125 => 29450, 25126 => 29470, 25127 => 29469, 25128 => 29461,
+25129 => 29474, 25130 => 29497, 25131 => 29477, 25132 => 29484, 25133 => 29496,
+25134 => 29489, 25135 => 29520, 25136 => 29517, 25137 => 29527, 25138 => 29536,
+25139 => 29548, 25140 => 29551, 25141 => 29566, 25142 => 33307, 25143 => 22821,
+25144 => 39143, 25145 => 22820, 25146 => 22786, 25147 => 39267, 25148 => 39271,
+25149 => 39272, 25150 => 39273, 25151 => 39274, 25152 => 39275, 25153 => 39276,
+25154 => 39284, 25155 => 39287, 25156 => 39293, 25157 => 39296, 25158 => 39300,
+25159 => 39303, 25160 => 39306, 25161 => 39309, 25162 => 39312, 25163 => 39313,
+25164 => 39315, 25165 => 39316, 25166 => 39317, 25167 => 24192, 25168 => 24209,
+25169 => 24203, 25170 => 24214, 25171 => 24229, 25172 => 24224, 25173 => 24249,
+25174 => 24245, 25175 => 24254, 25176 => 24243, 25177 => 36179, 25178 => 24274,
+25179 => 24273, 25180 => 24283, 25181 => 24296, 25182 => 24298, 25183 => 33210,
+25184 => 24516, 25185 => 24521, 25186 => 24534, 25187 => 24527, 25188 => 24579,
+25189 => 24558, 25190 => 24580, 25191 => 24545, 25192 => 24548, 25193 => 24574,
+25194 => 24581, 25195 => 24582, 25196 => 24554, 25197 => 24557, 25198 => 24568,
+25199 => 24601, 25200 => 24629, 25201 => 24614, 25202 => 24603, 25203 => 24591,
+25204 => 24589, 25205 => 24617, 25206 => 24619, 25207 => 24586, 25208 => 24639,
+25209 => 24609, 25210 => 24696, 25211 => 24697, 25212 => 24699, 25213 => 24698,
+25214 => 24642, 25377 => 24682, 25378 => 24701, 25379 => 24726, 25380 => 24730,
+25381 => 24749, 25382 => 24733, 25383 => 24707, 25384 => 24722, 25385 => 24716,
+25386 => 24731, 25387 => 24812, 25388 => 24763, 25389 => 24753, 25390 => 24797,
+25391 => 24792, 25392 => 24774, 25393 => 24794, 25394 => 24756, 25395 => 24864,
+25396 => 24870, 25397 => 24853, 25398 => 24867, 25399 => 24820, 25400 => 24832,
+25401 => 24846, 25402 => 24875, 25403 => 24906, 25404 => 24949, 25405 => 25004,
+25406 => 24980, 25407 => 24999, 25408 => 25015, 25409 => 25044, 25410 => 25077,
+25411 => 24541, 25412 => 38579, 25413 => 38377, 25414 => 38379, 25415 => 38385,
+25416 => 38387, 25417 => 38389, 25418 => 38390, 25419 => 38396, 25420 => 38398,
+25421 => 38403, 25422 => 38404, 25423 => 38406, 25424 => 38408, 25425 => 38410,
+25426 => 38411, 25427 => 38412, 25428 => 38413, 25429 => 38415, 25430 => 38418,
+25431 => 38421, 25432 => 38422, 25433 => 38423, 25434 => 38425, 25435 => 38426,
+25436 => 20012, 25437 => 29247, 25438 => 25109, 25439 => 27701, 25440 => 27732,
+25441 => 27740, 25442 => 27722, 25443 => 27811, 25444 => 27781, 25445 => 27792,
+25446 => 27796, 25447 => 27788, 25448 => 27752, 25449 => 27753, 25450 => 27764,
+25451 => 27766, 25452 => 27782, 25453 => 27817, 25454 => 27856, 25455 => 27860,
+25456 => 27821, 25457 => 27895, 25458 => 27896, 25459 => 27889, 25460 => 27863,
+25461 => 27826, 25462 => 27872, 25463 => 27862, 25464 => 27898, 25465 => 27883,
+25466 => 27886, 25467 => 27825, 25468 => 27859, 25469 => 27887, 25470 => 27902,
+25633 => 27961, 25634 => 27943, 25635 => 27916, 25636 => 27971, 25637 => 27976,
+25638 => 27911, 25639 => 27908, 25640 => 27929, 25641 => 27918, 25642 => 27947,
+25643 => 27981, 25644 => 27950, 25645 => 27957, 25646 => 27930, 25647 => 27983,
+25648 => 27986, 25649 => 27988, 25650 => 27955, 25651 => 28049, 25652 => 28015,
+25653 => 28062, 25654 => 28064, 25655 => 27998, 25656 => 28051, 25657 => 28052,
+25658 => 27996, 25659 => 28000, 25660 => 28028, 25661 => 28003, 25662 => 28186,
+25663 => 28103, 25664 => 28101, 25665 => 28126, 25666 => 28174, 25667 => 28095,
+25668 => 28128, 25669 => 28177, 25670 => 28134, 25671 => 28125, 25672 => 28121,
+25673 => 28182, 25674 => 28075, 25675 => 28172, 25676 => 28078, 25677 => 28203,
+25678 => 28270, 25679 => 28238, 25680 => 28267, 25681 => 28338, 25682 => 28255,
+25683 => 28294, 25684 => 28243, 25685 => 28244, 25686 => 28210, 25687 => 28197,
+25688 => 28228, 25689 => 28383, 25690 => 28337, 25691 => 28312, 25692 => 28384,
+25693 => 28461, 25694 => 28386, 25695 => 28325, 25696 => 28327, 25697 => 28349,
+25698 => 28347, 25699 => 28343, 25700 => 28375, 25701 => 28340, 25702 => 28367,
+25703 => 28303, 25704 => 28354, 25705 => 28319, 25706 => 28514, 25707 => 28486,
+25708 => 28487, 25709 => 28452, 25710 => 28437, 25711 => 28409, 25712 => 28463,
+25713 => 28470, 25714 => 28491, 25715 => 28532, 25716 => 28458, 25717 => 28425,
+25718 => 28457, 25719 => 28553, 25720 => 28557, 25721 => 28556, 25722 => 28536,
+25723 => 28530, 25724 => 28540, 25725 => 28538, 25726 => 28625, 25889 => 28617,
+25890 => 28583, 25891 => 28601, 25892 => 28598, 25893 => 28610, 25894 => 28641,
+25895 => 28654, 25896 => 28638, 25897 => 28640, 25898 => 28655, 25899 => 28698,
+25900 => 28707, 25901 => 28699, 25902 => 28729, 25903 => 28725, 25904 => 28751,
+25905 => 28766, 25906 => 23424, 25907 => 23428, 25908 => 23445, 25909 => 23443,
+25910 => 23461, 25911 => 23480, 25912 => 29999, 25913 => 39582, 25914 => 25652,
+25915 => 23524, 25916 => 23534, 25917 => 35120, 25918 => 23536, 25919 => 36423,
+25920 => 35591, 25921 => 36790, 25922 => 36819, 25923 => 36821, 25924 => 36837,
+25925 => 36846, 25926 => 36836, 25927 => 36841, 25928 => 36838, 25929 => 36851,
+25930 => 36840, 25931 => 36869, 25932 => 36868, 25933 => 36875, 25934 => 36902,
+25935 => 36881, 25936 => 36877, 25937 => 36886, 25938 => 36897, 25939 => 36917,
+25940 => 36918, 25941 => 36909, 25942 => 36911, 25943 => 36932, 25944 => 36945,
+25945 => 36946, 25946 => 36944, 25947 => 36968, 25948 => 36952, 25949 => 36962,
+25950 => 36955, 25951 => 26297, 25952 => 36980, 25953 => 36989, 25954 => 36994,
+25955 => 37000, 25956 => 36995, 25957 => 37003, 25958 => 24400, 25959 => 24407,
+25960 => 24406, 25961 => 24408, 25962 => 23611, 25963 => 21675, 25964 => 23632,
+25965 => 23641, 25966 => 23409, 25967 => 23651, 25968 => 23654, 25969 => 32700,
+25970 => 24362, 25971 => 24361, 25972 => 24365, 25973 => 33396, 25974 => 24380,
+25975 => 39739, 25976 => 23662, 25977 => 22913, 25978 => 22915, 25979 => 22925,
+25980 => 22953, 25981 => 22954, 25982 => 22947, 26145 => 22935, 26146 => 22986,
+26147 => 22955, 26148 => 22942, 26149 => 22948, 26150 => 22994, 26151 => 22962,
+26152 => 22959, 26153 => 22999, 26154 => 22974, 26155 => 23045, 26156 => 23046,
+26157 => 23005, 26158 => 23048, 26159 => 23011, 26160 => 23000, 26161 => 23033,
+26162 => 23052, 26163 => 23049, 26164 => 23090, 26165 => 23092, 26166 => 23057,
+26167 => 23075, 26168 => 23059, 26169 => 23104, 26170 => 23143, 26171 => 23114,
+26172 => 23125, 26173 => 23100, 26174 => 23138, 26175 => 23157, 26176 => 33004,
+26177 => 23210, 26178 => 23195, 26179 => 23159, 26180 => 23162, 26181 => 23230,
+26182 => 23275, 26183 => 23218, 26184 => 23250, 26185 => 23252, 26186 => 23224,
+26187 => 23264, 26188 => 23267, 26189 => 23281, 26190 => 23254, 26191 => 23270,
+26192 => 23256, 26193 => 23260, 26194 => 23305, 26195 => 23319, 26196 => 23318,
+26197 => 23346, 26198 => 23351, 26199 => 23360, 26200 => 23573, 26201 => 23580,
+26202 => 23386, 26203 => 23397, 26204 => 23411, 26205 => 23377, 26206 => 23379,
+26207 => 23394, 26208 => 39541, 26209 => 39543, 26210 => 39544, 26211 => 39546,
+26212 => 39551, 26213 => 39549, 26214 => 39552, 26215 => 39553, 26216 => 39557,
+26217 => 39560, 26218 => 39562, 26219 => 39568, 26220 => 39570, 26221 => 39571,
+26222 => 39574, 26223 => 39576, 26224 => 39579, 26225 => 39580, 26226 => 39581,
+26227 => 39583, 26228 => 39584, 26229 => 39586, 26230 => 39587, 26231 => 39589,
+26232 => 39591, 26233 => 32415, 26234 => 32417, 26235 => 32419, 26236 => 32421,
+26237 => 32424, 26238 => 32425, 26401 => 32429, 26402 => 32432, 26403 => 32446,
+26404 => 32448, 26405 => 32449, 26406 => 32450, 26407 => 32457, 26408 => 32459,
+26409 => 32460, 26410 => 32464, 26411 => 32468, 26412 => 32471, 26413 => 32475,
+26414 => 32480, 26415 => 32481, 26416 => 32488, 26417 => 32491, 26418 => 32494,
+26419 => 32495, 26420 => 32497, 26421 => 32498, 26422 => 32525, 26423 => 32502,
+26424 => 32506, 26425 => 32507, 26426 => 32510, 26427 => 32513, 26428 => 32514,
+26429 => 32515, 26430 => 32519, 26431 => 32520, 26432 => 32523, 26433 => 32524,
+26434 => 32527, 26435 => 32529, 26436 => 32530, 26437 => 32535, 26438 => 32537,
+26439 => 32540, 26440 => 32539, 26441 => 32543, 26442 => 32545, 26443 => 32546,
+26444 => 32547, 26445 => 32548, 26446 => 32549, 26447 => 32550, 26448 => 32551,
+26449 => 32554, 26450 => 32555, 26451 => 32556, 26452 => 32557, 26453 => 32559,
+26454 => 32560, 26455 => 32561, 26456 => 32562, 26457 => 32563, 26458 => 32565,
+26459 => 24186, 26460 => 30079, 26461 => 24027, 26462 => 30014, 26463 => 37013,
+26464 => 29582, 26465 => 29585, 26466 => 29614, 26467 => 29602, 26468 => 29599,
+26469 => 29647, 26470 => 29634, 26471 => 29649, 26472 => 29623, 26473 => 29619,
+26474 => 29632, 26475 => 29641, 26476 => 29640, 26477 => 29669, 26478 => 29657,
+26479 => 39036, 26480 => 29706, 26481 => 29673, 26482 => 29671, 26483 => 29662,
+26484 => 29626, 26485 => 29682, 26486 => 29711, 26487 => 29738, 26488 => 29787,
+26489 => 29734, 26490 => 29733, 26491 => 29736, 26492 => 29744, 26493 => 29742,
+26494 => 29740, 26657 => 29723, 26658 => 29722, 26659 => 29761, 26660 => 29788,
+26661 => 29783, 26662 => 29781, 26663 => 29785, 26664 => 29815, 26665 => 29805,
+26666 => 29822, 26667 => 29852, 26668 => 29838, 26669 => 29824, 26670 => 29825,
+26671 => 29831, 26672 => 29835, 26673 => 29854, 26674 => 29864, 26675 => 29865,
+26676 => 29840, 26677 => 29863, 26678 => 29906, 26679 => 29882, 26680 => 38890,
+26681 => 38891, 26682 => 38892, 26683 => 26444, 26684 => 26451, 26685 => 26462,
+26686 => 26440, 26687 => 26473, 26688 => 26533, 26689 => 26503, 26690 => 26474,
+26691 => 26483, 26692 => 26520, 26693 => 26535, 26694 => 26485, 26695 => 26536,
+26696 => 26526, 26697 => 26541, 26698 => 26507, 26699 => 26487, 26700 => 26492,
+26701 => 26608, 26702 => 26633, 26703 => 26584, 26704 => 26634, 26705 => 26601,
+26706 => 26544, 26707 => 26636, 26708 => 26585, 26709 => 26549, 26710 => 26586,
+26711 => 26547, 26712 => 26589, 26713 => 26624, 26714 => 26563, 26715 => 26552,
+26716 => 26594, 26717 => 26638, 26718 => 26561, 26719 => 26621, 26720 => 26674,
+26721 => 26675, 26722 => 26720, 26723 => 26721, 26724 => 26702, 26725 => 26722,
+26726 => 26692, 26727 => 26724, 26728 => 26755, 26729 => 26653, 26730 => 26709,
+26731 => 26726, 26732 => 26689, 26733 => 26727, 26734 => 26688, 26735 => 26686,
+26736 => 26698, 26737 => 26697, 26738 => 26665, 26739 => 26805, 26740 => 26767,
+26741 => 26740, 26742 => 26743, 26743 => 26771, 26744 => 26731, 26745 => 26818,
+26746 => 26990, 26747 => 26876, 26748 => 26911, 26749 => 26912, 26750 => 26873,
+26913 => 26916, 26914 => 26864, 26915 => 26891, 26916 => 26881, 26917 => 26967,
+26918 => 26851, 26919 => 26896, 26920 => 26993, 26921 => 26937, 26922 => 26976,
+26923 => 26946, 26924 => 26973, 26925 => 27012, 26926 => 26987, 26927 => 27008,
+26928 => 27032, 26929 => 27000, 26930 => 26932, 26931 => 27084, 26932 => 27015,
+26933 => 27016, 26934 => 27086, 26935 => 27017, 26936 => 26982, 26937 => 26979,
+26938 => 27001, 26939 => 27035, 26940 => 27047, 26941 => 27067, 26942 => 27051,
+26943 => 27053, 26944 => 27092, 26945 => 27057, 26946 => 27073, 26947 => 27082,
+26948 => 27103, 26949 => 27029, 26950 => 27104, 26951 => 27021, 26952 => 27135,
+26953 => 27183, 26954 => 27117, 26955 => 27159, 26956 => 27160, 26957 => 27237,
+26958 => 27122, 26959 => 27204, 26960 => 27198, 26961 => 27296, 26962 => 27216,
+26963 => 27227, 26964 => 27189, 26965 => 27278, 26966 => 27257, 26967 => 27197,
+26968 => 27176, 26969 => 27224, 26970 => 27260, 26971 => 27281, 26972 => 27280,
+26973 => 27305, 26974 => 27287, 26975 => 27307, 26976 => 29495, 26977 => 29522,
+26978 => 27521, 26979 => 27522, 26980 => 27527, 26981 => 27524, 26982 => 27538,
+26983 => 27539, 26984 => 27533, 26985 => 27546, 26986 => 27547, 26987 => 27553,
+26988 => 27562, 26989 => 36715, 26990 => 36717, 26991 => 36721, 26992 => 36722,
+26993 => 36723, 26994 => 36725, 26995 => 36726, 26996 => 36728, 26997 => 36727,
+26998 => 36729, 26999 => 36730, 27000 => 36732, 27001 => 36734, 27002 => 36737,
+27003 => 36738, 27004 => 36740, 27005 => 36743, 27006 => 36747, 27169 => 36749,
+27170 => 36750, 27171 => 36751, 27172 => 36760, 27173 => 36762, 27174 => 36558,
+27175 => 25099, 27176 => 25111, 27177 => 25115, 27178 => 25119, 27179 => 25122,
+27180 => 25121, 27181 => 25125, 27182 => 25124, 27183 => 25132, 27184 => 33255,
+27185 => 29935, 27186 => 29940, 27187 => 29951, 27188 => 29967, 27189 => 29969,
+27190 => 29971, 27191 => 25908, 27192 => 26094, 27193 => 26095, 27194 => 26096,
+27195 => 26122, 27196 => 26137, 27197 => 26482, 27198 => 26115, 27199 => 26133,
+27200 => 26112, 27201 => 28805, 27202 => 26359, 27203 => 26141, 27204 => 26164,
+27205 => 26161, 27206 => 26166, 27207 => 26165, 27208 => 32774, 27209 => 26207,
+27210 => 26196, 27211 => 26177, 27212 => 26191, 27213 => 26198, 27214 => 26209,
+27215 => 26199, 27216 => 26231, 27217 => 26244, 27218 => 26252, 27219 => 26279,
+27220 => 26269, 27221 => 26302, 27222 => 26331, 27223 => 26332, 27224 => 26342,
+27225 => 26345, 27226 => 36146, 27227 => 36147, 27228 => 36150, 27229 => 36155,
+27230 => 36157, 27231 => 36160, 27232 => 36165, 27233 => 36166, 27234 => 36168,
+27235 => 36169, 27236 => 36167, 27237 => 36173, 27238 => 36181, 27239 => 36185,
+27240 => 35271, 27241 => 35274, 27242 => 35275, 27243 => 35276, 27244 => 35278,
+27245 => 35279, 27246 => 35280, 27247 => 35281, 27248 => 29294, 27249 => 29343,
+27250 => 29277, 27251 => 29286, 27252 => 29295, 27253 => 29310, 27254 => 29311,
+27255 => 29316, 27256 => 29323, 27257 => 29325, 27258 => 29327, 27259 => 29330,
+27260 => 25352, 27261 => 25394, 27262 => 25520, 27425 => 25663, 27426 => 25816,
+27427 => 32772, 27428 => 27626, 27429 => 27635, 27430 => 27645, 27431 => 27637,
+27432 => 27641, 27433 => 27653, 27434 => 27655, 27435 => 27654, 27436 => 27661,
+27437 => 27669, 27438 => 27672, 27439 => 27673, 27440 => 27674, 27441 => 27681,
+27442 => 27689, 27443 => 27684, 27444 => 27690, 27445 => 27698, 27446 => 25909,
+27447 => 25941, 27448 => 25963, 27449 => 29261, 27450 => 29266, 27451 => 29270,
+27452 => 29232, 27453 => 34402, 27454 => 21014, 27455 => 32927, 27456 => 32924,
+27457 => 32915, 27458 => 32956, 27459 => 26378, 27460 => 32957, 27461 => 32945,
+27462 => 32939, 27463 => 32941, 27464 => 32948, 27465 => 32951, 27466 => 32999,
+27467 => 33000, 27468 => 33001, 27469 => 33002, 27470 => 32987, 27471 => 32962,
+27472 => 32964, 27473 => 32985, 27474 => 32973, 27475 => 32983, 27476 => 26384,
+27477 => 32989, 27478 => 33003, 27479 => 33009, 27480 => 33012, 27481 => 33005,
+27482 => 33037, 27483 => 33038, 27484 => 33010, 27485 => 33020, 27486 => 26389,
+27487 => 33042, 27488 => 35930, 27489 => 33078, 27490 => 33054, 27491 => 33068,
+27492 => 33048, 27493 => 33074, 27494 => 33096, 27495 => 33100, 27496 => 33107,
+27497 => 33140, 27498 => 33113, 27499 => 33114, 27500 => 33137, 27501 => 33120,
+27502 => 33129, 27503 => 33148, 27504 => 33149, 27505 => 33133, 27506 => 33127,
+27507 => 22605, 27508 => 23221, 27509 => 33160, 27510 => 33154, 27511 => 33169,
+27512 => 28373, 27513 => 33187, 27514 => 33194, 27515 => 33228, 27516 => 26406,
+27517 => 33226, 27518 => 33211, 27681 => 33217, 27682 => 33190, 27683 => 27428,
+27684 => 27447, 27685 => 27449, 27686 => 27459, 27687 => 27462, 27688 => 27481,
+27689 => 39121, 27690 => 39122, 27691 => 39123, 27692 => 39125, 27693 => 39129,
+27694 => 39130, 27695 => 27571, 27696 => 24384, 27697 => 27586, 27698 => 35315,
+27699 => 26000, 27700 => 40785, 27701 => 26003, 27702 => 26044, 27703 => 26054,
+27704 => 26052, 27705 => 26051, 27706 => 26060, 27707 => 26062, 27708 => 26066,
+27709 => 26070, 27710 => 28800, 27711 => 28828, 27712 => 28822, 27713 => 28829,
+27714 => 28859, 27715 => 28864, 27716 => 28855, 27717 => 28843, 27718 => 28849,
+27719 => 28904, 27720 => 28874, 27721 => 28944, 27722 => 28947, 27723 => 28950,
+27724 => 28975, 27725 => 28977, 27726 => 29043, 27727 => 29020, 27728 => 29032,
+27729 => 28997, 27730 => 29042, 27731 => 29002, 27732 => 29048, 27733 => 29050,
+27734 => 29080, 27735 => 29107, 27736 => 29109, 27737 => 29096, 27738 => 29088,
+27739 => 29152, 27740 => 29140, 27741 => 29159, 27742 => 29177, 27743 => 29213,
+27744 => 29224, 27745 => 28780, 27746 => 28952, 27747 => 29030, 27748 => 29113,
+27749 => 25150, 27750 => 25149, 27751 => 25155, 27752 => 25160, 27753 => 25161,
+27754 => 31035, 27755 => 31040, 27756 => 31046, 27757 => 31049, 27758 => 31067,
+27759 => 31068, 27760 => 31059, 27761 => 31066, 27762 => 31074, 27763 => 31063,
+27764 => 31072, 27765 => 31087, 27766 => 31079, 27767 => 31098, 27768 => 31109,
+27769 => 31114, 27770 => 31130, 27771 => 31143, 27772 => 31155, 27773 => 24529,
+27774 => 24528, 27937 => 24636, 27938 => 24669, 27939 => 24666, 27940 => 24679,
+27941 => 24641, 27942 => 24665, 27943 => 24675, 27944 => 24747, 27945 => 24838,
+27946 => 24845, 27947 => 24925, 27948 => 25001, 27949 => 24989, 27950 => 25035,
+27951 => 25041, 27952 => 25094, 27953 => 32896, 27954 => 32895, 27955 => 27795,
+27956 => 27894, 27957 => 28156, 27958 => 30710, 27959 => 30712, 27960 => 30720,
+27961 => 30729, 27962 => 30743, 27963 => 30744, 27964 => 30737, 27965 => 26027,
+27966 => 30765, 27967 => 30748, 27968 => 30749, 27969 => 30777, 27970 => 30778,
+27971 => 30779, 27972 => 30751, 27973 => 30780, 27974 => 30757, 27975 => 30764,
+27976 => 30755, 27977 => 30761, 27978 => 30798, 27979 => 30829, 27980 => 30806,
+27981 => 30807, 27982 => 30758, 27983 => 30800, 27984 => 30791, 27985 => 30796,
+27986 => 30826, 27987 => 30875, 27988 => 30867, 27989 => 30874, 27990 => 30855,
+27991 => 30876, 27992 => 30881, 27993 => 30883, 27994 => 30898, 27995 => 30905,
+27996 => 30885, 27997 => 30932, 27998 => 30937, 27999 => 30921, 28000 => 30956,
+28001 => 30962, 28002 => 30981, 28003 => 30964, 28004 => 30995, 28005 => 31012,
+28006 => 31006, 28007 => 31028, 28008 => 40859, 28009 => 40697, 28010 => 40699,
+28011 => 40700, 28012 => 30449, 28013 => 30468, 28014 => 30477, 28015 => 30457,
+28016 => 30471, 28017 => 30472, 28018 => 30490, 28019 => 30498, 28020 => 30489,
+28021 => 30509, 28022 => 30502, 28023 => 30517, 28024 => 30520, 28025 => 30544,
+28026 => 30545, 28027 => 30535, 28028 => 30531, 28029 => 30554, 28030 => 30568,
+28193 => 30562, 28194 => 30565, 28195 => 30591, 28196 => 30605, 28197 => 30589,
+28198 => 30592, 28199 => 30604, 28200 => 30609, 28201 => 30623, 28202 => 30624,
+28203 => 30640, 28204 => 30645, 28205 => 30653, 28206 => 30010, 28207 => 30016,
+28208 => 30030, 28209 => 30027, 28210 => 30024, 28211 => 30043, 28212 => 30066,
+28213 => 30073, 28214 => 30083, 28215 => 32600, 28216 => 32609, 28217 => 32607,
+28218 => 35400, 28219 => 32616, 28220 => 32628, 28221 => 32625, 28222 => 32633,
+28223 => 32641, 28224 => 32638, 28225 => 30413, 28226 => 30437, 28227 => 34866,
+28228 => 38021, 28229 => 38022, 28230 => 38023, 28231 => 38027, 28232 => 38026,
+28233 => 38028, 28234 => 38029, 28235 => 38031, 28236 => 38032, 28237 => 38036,
+28238 => 38039, 28239 => 38037, 28240 => 38042, 28241 => 38043, 28242 => 38044,
+28243 => 38051, 28244 => 38052, 28245 => 38059, 28246 => 38058, 28247 => 38061,
+28248 => 38060, 28249 => 38063, 28250 => 38064, 28251 => 38066, 28252 => 38068,
+28253 => 38070, 28254 => 38071, 28255 => 38072, 28256 => 38073, 28257 => 38074,
+28258 => 38076, 28259 => 38077, 28260 => 38079, 28261 => 38084, 28262 => 38088,
+28263 => 38089, 28264 => 38090, 28265 => 38091, 28266 => 38092, 28267 => 38093,
+28268 => 38094, 28269 => 38096, 28270 => 38097, 28271 => 38098, 28272 => 38101,
+28273 => 38102, 28274 => 38103, 28275 => 38105, 28276 => 38104, 28277 => 38107,
+28278 => 38110, 28279 => 38111, 28280 => 38112, 28281 => 38114, 28282 => 38116,
+28283 => 38117, 28284 => 38119, 28285 => 38120, 28286 => 38122, 28449 => 38121,
+28450 => 38123, 28451 => 38126, 28452 => 38127, 28453 => 38131, 28454 => 38132,
+28455 => 38133, 28456 => 38135, 28457 => 38137, 28458 => 38140, 28459 => 38141,
+28460 => 38143, 28461 => 38147, 28462 => 38146, 28463 => 38150, 28464 => 38151,
+28465 => 38153, 28466 => 38154, 28467 => 38157, 28468 => 38158, 28469 => 38159,
+28470 => 38162, 28471 => 38163, 28472 => 38164, 28473 => 38165, 28474 => 38166,
+28475 => 38168, 28476 => 38171, 28477 => 38173, 28478 => 38174, 28479 => 38175,
+28480 => 38178, 28481 => 38186, 28482 => 38187, 28483 => 38185, 28484 => 38188,
+28485 => 38193, 28486 => 38194, 28487 => 38196, 28488 => 38198, 28489 => 38199,
+28490 => 38200, 28491 => 38204, 28492 => 38206, 28493 => 38207, 28494 => 38210,
+28495 => 38197, 28496 => 38212, 28497 => 38213, 28498 => 38214, 28499 => 38217,
+28500 => 38220, 28501 => 38222, 28502 => 38223, 28503 => 38226, 28504 => 38227,
+28505 => 38228, 28506 => 38230, 28507 => 38231, 28508 => 38232, 28509 => 38233,
+28510 => 38235, 28511 => 38238, 28512 => 38239, 28513 => 38237, 28514 => 38241,
+28515 => 38242, 28516 => 38244, 28517 => 38245, 28518 => 38246, 28519 => 38247,
+28520 => 38248, 28521 => 38249, 28522 => 38250, 28523 => 38251, 28524 => 38252,
+28525 => 38255, 28526 => 38257, 28527 => 38258, 28528 => 38259, 28529 => 38202,
+28530 => 30695, 28531 => 30700, 28532 => 38601, 28533 => 31189, 28534 => 31213,
+28535 => 31203, 28536 => 31211, 28537 => 31238, 28538 => 23879, 28539 => 31235,
+28540 => 31234, 28541 => 31262, 28542 => 31252, 28705 => 31289, 28706 => 31287,
+28707 => 31313, 28708 => 40655, 28709 => 39333, 28710 => 31344, 28711 => 30344,
+28712 => 30350, 28713 => 30355, 28714 => 30361, 28715 => 30372, 28716 => 29918,
+28717 => 29920, 28718 => 29996, 28719 => 40480, 28720 => 40482, 28721 => 40488,
+28722 => 40489, 28723 => 40490, 28724 => 40491, 28725 => 40492, 28726 => 40498,
+28727 => 40497, 28728 => 40502, 28729 => 40504, 28730 => 40503, 28731 => 40505,
+28732 => 40506, 28733 => 40510, 28734 => 40513, 28735 => 40514, 28736 => 40516,
+28737 => 40518, 28738 => 40519, 28739 => 40520, 28740 => 40521, 28741 => 40523,
+28742 => 40524, 28743 => 40526, 28744 => 40529, 28745 => 40533, 28746 => 40535,
+28747 => 40538, 28748 => 40539, 28749 => 40540, 28750 => 40542, 28751 => 40547,
+28752 => 40550, 28753 => 40551, 28754 => 40552, 28755 => 40553, 28756 => 40554,
+28757 => 40555, 28758 => 40556, 28759 => 40561, 28760 => 40557, 28761 => 40563,
+28762 => 30098, 28763 => 30100, 28764 => 30102, 28765 => 30112, 28766 => 30109,
+28767 => 30124, 28768 => 30115, 28769 => 30131, 28770 => 30132, 28771 => 30136,
+28772 => 30148, 28773 => 30129, 28774 => 30128, 28775 => 30147, 28776 => 30146,
+28777 => 30166, 28778 => 30157, 28779 => 30179, 28780 => 30184, 28781 => 30182,
+28782 => 30180, 28783 => 30187, 28784 => 30183, 28785 => 30211, 28786 => 30193,
+28787 => 30204, 28788 => 30207, 28789 => 30224, 28790 => 30208, 28791 => 30213,
+28792 => 30220, 28793 => 30231, 28794 => 30218, 28795 => 30245, 28796 => 30232,
+28797 => 30229, 28798 => 30233, 28961 => 30235, 28962 => 30268, 28963 => 30242,
+28964 => 30240, 28965 => 30272, 28966 => 30253, 28967 => 30256, 28968 => 30271,
+28969 => 30261, 28970 => 30275, 28971 => 30270, 28972 => 30259, 28973 => 30285,
+28974 => 30302, 28975 => 30292, 28976 => 30300, 28977 => 30294, 28978 => 30315,
+28979 => 30319, 28980 => 32714, 28981 => 31462, 28982 => 31352, 28983 => 31353,
+28984 => 31360, 28985 => 31366, 28986 => 31368, 28987 => 31381, 28988 => 31398,
+28989 => 31392, 28990 => 31404, 28991 => 31400, 28992 => 31405, 28993 => 31411,
+28994 => 34916, 28995 => 34921, 28996 => 34930, 28997 => 34941, 28998 => 34943,
+28999 => 34946, 29000 => 34978, 29001 => 35014, 29002 => 34999, 29003 => 35004,
+29004 => 35017, 29005 => 35042, 29006 => 35022, 29007 => 35043, 29008 => 35045,
+29009 => 35057, 29010 => 35098, 29011 => 35068, 29012 => 35048, 29013 => 35070,
+29014 => 35056, 29015 => 35105, 29016 => 35097, 29017 => 35091, 29018 => 35099,
+29019 => 35082, 29020 => 35124, 29021 => 35115, 29022 => 35126, 29023 => 35137,
+29024 => 35174, 29025 => 35195, 29026 => 30091, 29027 => 32997, 29028 => 30386,
+29029 => 30388, 29030 => 30684, 29031 => 32786, 29032 => 32788, 29033 => 32790,
+29034 => 32796, 29035 => 32800, 29036 => 32802, 29037 => 32805, 29038 => 32806,
+29039 => 32807, 29040 => 32809, 29041 => 32808, 29042 => 32817, 29043 => 32779,
+29044 => 32821, 29045 => 32835, 29046 => 32838, 29047 => 32845, 29048 => 32850,
+29049 => 32873, 29050 => 32881, 29051 => 35203, 29052 => 39032, 29053 => 39040,
+29054 => 39043, 29217 => 39049, 29218 => 39052, 29219 => 39053, 29220 => 39055,
+29221 => 39060, 29222 => 39066, 29223 => 39067, 29224 => 39070, 29225 => 39071,
+29226 => 39073, 29227 => 39074, 29228 => 39077, 29229 => 39078, 29230 => 34381,
+29231 => 34388, 29232 => 34412, 29233 => 34414, 29234 => 34431, 29235 => 34426,
+29236 => 34428, 29237 => 34427, 29238 => 34472, 29239 => 34445, 29240 => 34443,
+29241 => 34476, 29242 => 34461, 29243 => 34471, 29244 => 34467, 29245 => 34474,
+29246 => 34451, 29247 => 34473, 29248 => 34486, 29249 => 34500, 29250 => 34485,
+29251 => 34510, 29252 => 34480, 29253 => 34490, 29254 => 34481, 29255 => 34479,
+29256 => 34505, 29257 => 34511, 29258 => 34484, 29259 => 34537, 29260 => 34545,
+29261 => 34546, 29262 => 34541, 29263 => 34547, 29264 => 34512, 29265 => 34579,
+29266 => 34526, 29267 => 34548, 29268 => 34527, 29269 => 34520, 29270 => 34513,
+29271 => 34563, 29272 => 34567, 29273 => 34552, 29274 => 34568, 29275 => 34570,
+29276 => 34573, 29277 => 34569, 29278 => 34595, 29279 => 34619, 29280 => 34590,
+29281 => 34597, 29282 => 34606, 29283 => 34586, 29284 => 34622, 29285 => 34632,
+29286 => 34612, 29287 => 34609, 29288 => 34601, 29289 => 34615, 29290 => 34623,
+29291 => 34690, 29292 => 34594, 29293 => 34685, 29294 => 34686, 29295 => 34683,
+29296 => 34656, 29297 => 34672, 29298 => 34636, 29299 => 34670, 29300 => 34699,
+29301 => 34643, 29302 => 34659, 29303 => 34684, 29304 => 34660, 29305 => 34649,
+29306 => 34661, 29307 => 34707, 29308 => 34735, 29309 => 34728, 29310 => 34770,
+29473 => 34758, 29474 => 34696, 29475 => 34693, 29476 => 34733, 29477 => 34711,
+29478 => 34691, 29479 => 34731, 29480 => 34789, 29481 => 34732, 29482 => 34741,
+29483 => 34739, 29484 => 34763, 29485 => 34771, 29486 => 34749, 29487 => 34769,
+29488 => 34752, 29489 => 34762, 29490 => 34779, 29491 => 34794, 29492 => 34784,
+29493 => 34798, 29494 => 34838, 29495 => 34835, 29496 => 34814, 29497 => 34826,
+29498 => 34843, 29499 => 34849, 29500 => 34873, 29501 => 34876, 29502 => 32566,
+29503 => 32578, 29504 => 32580, 29505 => 32581, 29506 => 33296, 29507 => 31482,
+29508 => 31485, 29509 => 31496, 29510 => 31491, 29511 => 31492, 29512 => 31509,
+29513 => 31498, 29514 => 31531, 29515 => 31503, 29516 => 31559, 29517 => 31544,
+29518 => 31530, 29519 => 31513, 29520 => 31534, 29521 => 31537, 29522 => 31520,
+29523 => 31525, 29524 => 31524, 29525 => 31539, 29526 => 31550, 29527 => 31518,
+29528 => 31576, 29529 => 31578, 29530 => 31557, 29531 => 31605, 29532 => 31564,
+29533 => 31581, 29534 => 31584, 29535 => 31598, 29536 => 31611, 29537 => 31586,
+29538 => 31602, 29539 => 31601, 29540 => 31632, 29541 => 31654, 29542 => 31655,
+29543 => 31672, 29544 => 31660, 29545 => 31645, 29546 => 31656, 29547 => 31621,
+29548 => 31658, 29549 => 31644, 29550 => 31650, 29551 => 31659, 29552 => 31668,
+29553 => 31697, 29554 => 31681, 29555 => 31692, 29556 => 31709, 29557 => 31706,
+29558 => 31717, 29559 => 31718, 29560 => 31722, 29561 => 31756, 29562 => 31742,
+29563 => 31740, 29564 => 31759, 29565 => 31766, 29566 => 31755, 29729 => 31775,
+29730 => 31786, 29731 => 31782, 29732 => 31800, 29733 => 31809, 29734 => 31808,
+29735 => 33278, 29736 => 33281, 29737 => 33282, 29738 => 33284, 29739 => 33260,
+29740 => 34884, 29741 => 33313, 29742 => 33314, 29743 => 33315, 29744 => 33325,
+29745 => 33327, 29746 => 33320, 29747 => 33323, 29748 => 33336, 29749 => 33339,
+29750 => 33331, 29751 => 33332, 29752 => 33342, 29753 => 33348, 29754 => 33353,
+29755 => 33355, 29756 => 33359, 29757 => 33370, 29758 => 33375, 29759 => 33384,
+29760 => 34942, 29761 => 34949, 29762 => 34952, 29763 => 35032, 29764 => 35039,
+29765 => 35166, 29766 => 32669, 29767 => 32671, 29768 => 32679, 29769 => 32687,
+29770 => 32688, 29771 => 32690, 29772 => 31868, 29773 => 25929, 29774 => 31889,
+29775 => 31901, 29776 => 31900, 29777 => 31902, 29778 => 31906, 29779 => 31922,
+29780 => 31932, 29781 => 31933, 29782 => 31937, 29783 => 31943, 29784 => 31948,
+29785 => 31949, 29786 => 31944, 29787 => 31941, 29788 => 31959, 29789 => 31976,
+29790 => 33390, 29791 => 26280, 29792 => 32703, 29793 => 32718, 29794 => 32725,
+29795 => 32741, 29796 => 32737, 29797 => 32742, 29798 => 32745, 29799 => 32750,
+29800 => 32755, 29801 => 31992, 29802 => 32119, 29803 => 32166, 29804 => 32174,
+29805 => 32327, 29806 => 32411, 29807 => 40632, 29808 => 40628, 29809 => 36211,
+29810 => 36228, 29811 => 36244, 29812 => 36241, 29813 => 36273, 29814 => 36199,
+29815 => 36205, 29816 => 35911, 29817 => 35913, 29818 => 37194, 29819 => 37200,
+29820 => 37198, 29821 => 37199, 29822 => 37220, 29985 => 37218, 29986 => 37217,
+29987 => 37232, 29988 => 37225, 29989 => 37231, 29990 => 37245, 29991 => 37246,
+29992 => 37234, 29993 => 37236, 29994 => 37241, 29995 => 37260, 29996 => 37253,
+29997 => 37264, 29998 => 37261, 29999 => 37265, 30000 => 37282, 30001 => 37283,
+30002 => 37290, 30003 => 37293, 30004 => 37294, 30005 => 37295, 30006 => 37301,
+30007 => 37300, 30008 => 37306, 30009 => 35925, 30010 => 40574, 30011 => 36280,
+30012 => 36331, 30013 => 36357, 30014 => 36441, 30015 => 36457, 30016 => 36277,
+30017 => 36287, 30018 => 36284, 30019 => 36282, 30020 => 36292, 30021 => 36310,
+30022 => 36311, 30023 => 36314, 30024 => 36318, 30025 => 36302, 30026 => 36303,
+30027 => 36315, 30028 => 36294, 30029 => 36332, 30030 => 36343, 30031 => 36344,
+30032 => 36323, 30033 => 36345, 30034 => 36347, 30035 => 36324, 30036 => 36361,
+30037 => 36349, 30038 => 36372, 30039 => 36381, 30040 => 36383, 30041 => 36396,
+30042 => 36398, 30043 => 36387, 30044 => 36399, 30045 => 36410, 30046 => 36416,
+30047 => 36409, 30048 => 36405, 30049 => 36413, 30050 => 36401, 30051 => 36425,
+30052 => 36417, 30053 => 36418, 30054 => 36433, 30055 => 36434, 30056 => 36426,
+30057 => 36464, 30058 => 36470, 30059 => 36476, 30060 => 36463, 30061 => 36468,
+30062 => 36485, 30063 => 36495, 30064 => 36500, 30065 => 36496, 30066 => 36508,
+30067 => 36510, 30068 => 35960, 30069 => 35970, 30070 => 35978, 30071 => 35973,
+30072 => 35992, 30073 => 35988, 30074 => 26011, 30075 => 35286, 30076 => 35294,
+30077 => 35290, 30078 => 35292, 30241 => 35301, 30242 => 35307, 30243 => 35311,
+30244 => 35390, 30245 => 35622, 30246 => 38739, 30247 => 38633, 30248 => 38643,
+30249 => 38639, 30250 => 38662, 30251 => 38657, 30252 => 38664, 30253 => 38671,
+30254 => 38670, 30255 => 38698, 30256 => 38701, 30257 => 38704, 30258 => 38718,
+30259 => 40832, 30260 => 40835, 30261 => 40837, 30262 => 40838, 30263 => 40839,
+30264 => 40840, 30265 => 40841, 30266 => 40842, 30267 => 40844, 30268 => 40702,
+30269 => 40715, 30270 => 40717, 30271 => 38585, 30272 => 38588, 30273 => 38589,
+30274 => 38606, 30275 => 38610, 30276 => 30655, 30277 => 38624, 30278 => 37518,
+30279 => 37550, 30280 => 37576, 30281 => 37694, 30282 => 37738, 30283 => 37834,
+30284 => 37775, 30285 => 37950, 30286 => 37995, 30287 => 40063, 30288 => 40066,
+30289 => 40069, 30290 => 40070, 30291 => 40071, 30292 => 40072, 30293 => 31267,
+30294 => 40075, 30295 => 40078, 30296 => 40080, 30297 => 40081, 30298 => 40082,
+30299 => 40084, 30300 => 40085, 30301 => 40090, 30302 => 40091, 30303 => 40094,
+30304 => 40095, 30305 => 40096, 30306 => 40097, 30307 => 40098, 30308 => 40099,
+30309 => 40101, 30310 => 40102, 30311 => 40103, 30312 => 40104, 30313 => 40105,
+30314 => 40107, 30315 => 40109, 30316 => 40110, 30317 => 40112, 30318 => 40113,
+30319 => 40114, 30320 => 40115, 30321 => 40116, 30322 => 40117, 30323 => 40118,
+30324 => 40119, 30325 => 40122, 30326 => 40123, 30327 => 40124, 30328 => 40125,
+30329 => 40132, 30330 => 40133, 30331 => 40134, 30332 => 40135, 30333 => 40138,
+30334 => 40139, 30497 => 40140, 30498 => 40141, 30499 => 40142, 30500 => 40143,
+30501 => 40144, 30502 => 40147, 30503 => 40148, 30504 => 40149, 30505 => 40151,
+30506 => 40152, 30507 => 40153, 30508 => 40156, 30509 => 40157, 30510 => 40159,
+30511 => 40162, 30512 => 38780, 30513 => 38789, 30514 => 38801, 30515 => 38802,
+30516 => 38804, 30517 => 38831, 30518 => 38827, 30519 => 38819, 30520 => 38834,
+30521 => 38836, 30522 => 39601, 30523 => 39600, 30524 => 39607, 30525 => 40536,
+30526 => 39606, 30527 => 39610, 30528 => 39612, 30529 => 39617, 30530 => 39616,
+30531 => 39621, 30532 => 39618, 30533 => 39627, 30534 => 39628, 30535 => 39633,
+30536 => 39749, 30537 => 39747, 30538 => 39751, 30539 => 39753, 30540 => 39752,
+30541 => 39757, 30542 => 39761, 30543 => 39144, 30544 => 39181, 30545 => 39214,
+30546 => 39253, 30547 => 39252, 30548 => 39647, 30549 => 39649, 30550 => 39654,
+30551 => 39663, 30552 => 39659, 30553 => 39675, 30554 => 39661, 30555 => 39673,
+30556 => 39688, 30557 => 39695, 30558 => 39699, 30559 => 39711, 30560 => 39715,
+30561 => 40637, 30562 => 40638, 30563 => 32315, 30564 => 40578, 30565 => 40583,
+30566 => 40584, 30567 => 40587, 30568 => 40594, 30569 => 37846, 30570 => 40605,
+30571 => 40607, 30572 => 40667, 30573 => 40668, 30574 => 40669, 30575 => 40672,
+30576 => 40671, 30577 => 40674, 30578 => 40681, 30579 => 40679, 30580 => 40677,
+30581 => 40682, 30582 => 40687, 30583 => 40738, 30584 => 40748, 30585 => 40751,
+30586 => 40761, 30587 => 40759, 30588 => 40765, 30589 => 40766, 30590 => 40772,
+0 => 0 );
+
+ function gb2utf8($gb) {
+ if( !trim($gb) ) return $gb;
+ $utf8='';
+ while($gb) {
+ if( ord(substr($gb,0,1)) > 127 ) {
+ $t=substr($gb,0,2);
+ $gb=substr($gb,2);
+ $utf8 .= $this->u2utf8($this->codetable[hexdec(bin2hex($t))-0x8080]);
+ }
+ else {
+ $t=substr($gb,0,1);
+ $gb=substr($gb,1);
+ $utf8 .= $this->u2utf8($t);
+ }
+ }
+ return $utf8;
+ }
+
+ function u2utf8($c) {
+ $str='';
+ if ($c < 0x80) {
+ $str.=$c;
+ }
+ else if ($c < 0x800) {
+ $str.=chr(0xC0 | $c>>6);
+ $str.=chr(0x80 | $c & 0x3F);
+ }
+ else if ($c < 0x10000) {
+ $str.=chr(0xE0 | $c>>12);
+ $str.=chr(0x80 | $c>>6 & 0x3F);
+ $str.=chr(0x80 | $c & 0x3F);
+ }
+ else if ($c < 0x200000) {
+ $str.=chr(0xF0 | $c>>18);
+ $str.=chr(0x80 | $c>>12 & 0x3F);
+ $str.=chr(0x80 | $c>>6 & 0x3F);
+ $str.=chr(0x80 | $c & 0x3F);
+ }
+ return $str;
+ }
+
+} // END Class
+
+?>
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_GRADIENT.PHP
+// Description: Create a color gradient
+// Created: 2003-02-01
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_gradient.php,v 1.1.2.5 2003/08/23 23:15:06 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+*/
+
+
+//===================================================
+// CLASS Gradient
+// Description: Handles gradient fills. This is to be
+// considered a "friend" class of Class Image.
+//===================================================
+class Gradient {
+ var $img=null;
+ var $numcolors=100;
+//---------------
+// CONSTRUCTOR
+ function Gradient(&$img) {
+ $this->img = $img;
+ }
+
+
+ function SetNumColors($aNum) {
+ $this->numcolors=$aNum;
+ }
+//---------------
+// PUBLIC METHODS
+ // Produce a gradient filled rectangle with a smooth transition between
+ // two colors.
+ // ($xl,$yt) Top left corner
+ // ($xr,$yb) Bottom right
+ // $from_color Starting color in gradient
+ // $to_color End color in the gradient
+ // $style Which way is the gradient oriented?
+ function FilledRectangle($xl,$yt,$xr,$yb,$from_color,$to_color,$style=1) {
+ switch( $style ) {
+ case 1: // HORIZONTAL
+ $steps = abs($xr-$xl);
+ $delta = $xr>=$xl ? 1 : -1;
+ $this->GetColArray($from_color,$to_color,$steps,$colors,$this->numcolors);
+ for( $i=0, $x=$xl; $i < $steps; ++$i ) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yt,$x,$yb);
+ $x += $delta;
+ }
+ break;
+
+ case 2: // VERTICAL
+ $steps = abs($yb-$yt);
+ $delta = $yb>=$yt ? 1 : -1;
+ $this->GetColArray($from_color,$to_color,$steps,$colors,$this->numcolors);
+ for($i=0,$y=$yt; $i < $steps; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($xl,$y,$xr,$y);
+ $y += $delta;
+ }
+ break;
+
+ case 3: // VERTICAL FROM MIDDLE
+ $steps = abs($yb-$yt)/2;
+ $delta = $yb >= $yt ? 1 : -1;
+ $this->GetColArray($from_color,$to_color,$steps,$colors,$this->numcolors);
+ for($y=$yt, $i=0; $i < $steps; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($xl,$y,$xr,$y);
+ $y += $delta;
+ }
+ --$i;
+ for($j=0; $j < $steps; ++$j, --$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($xl,$y,$xr,$y);
+ $y += $delta;
+ }
+ $this->img->Line($xl,$y,$xr,$y);
+ break;
+
+ case 4: // HORIZONTAL FROM MIDDLE
+ $steps = abs($xr-$xl)/2;
+ $delta = $xr>=$xl ? 1 : -1;
+ $this->GetColArray($from_color,$to_color,$steps,$colors,$this->numcolors);
+ for($x=$xl, $i=0; $i < $steps; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ --$i;
+ for($j=0; $j < $steps; ++$j, --$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ $this->img->Line($x,$yb,$x,$yt);
+ break;
+
+ case 6: // HORIZONTAL WIDER MIDDLE
+ $diff = round(abs($xr-$xl));
+ $steps = floor(abs($diff)/3);
+ $firststep = $diff - 2*$steps ;
+ // $steps = abs($xr-$xl)/3;
+ $delta = $xr >= $xl ? 1 : -1;
+ $this->GetColArray($from_color,$to_color,$firststep,$colors,$this->numcolors);
+ for($x=$xl, $i=0; $i < $firststep; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ --$i;
+ $this->img->current_color = $colors[$i];
+ for($j=0; $j< $steps; ++$j) {
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+
+ for($j=0; $j < $steps; ++$j, --$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ break;
+
+ case 7: // VERTICAL WIDER MIDDLE
+ $diff = round(abs($yb-$yt));
+ $steps = floor(abs($diff)/3);
+ $firststep = $diff - 2*$steps ;
+ $delta = $yb >= $yt? 1 : -1;
+ $this->GetColArray($from_color,$to_color,$firststep,$colors,$this->numcolors);
+ for($y=$yt, $i=0; $i < $firststep; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($xl,$y,$xr,$y);
+ $y += $delta;
+ }
+ --$i;
+ $this->img->current_color = $colors[$i];
+ for($j=0; $j < $steps; ++$j) {
+ $this->img->Line($xl,$y,$xr,$y);
+ $y += $delta;
+ }
+ for($j=0; $j < $steps; ++$j, --$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($xl,$y,$xr,$y);
+ $y += $delta;
+ }
+ break;
+
+ case 8: // LEFT REFLECTION
+ $steps1 = round(0.3*abs($xr-$xl));
+ $delta = $xr>=$xl ? 1 : -1;
+
+ $this->GetColArray($from_color.':1.3',$to_color,$steps1,$colors,$this->numcolors);
+ for($x=$xl, $i=0; $i < $steps1; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ $steps2 = max(1,round(0.08*abs($xr-$xl)));
+ $this->img->SetColor($to_color);
+ for($j=0; $j< $steps2; ++$j) {
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ $steps = abs($xr-$xl)-$steps1-$steps2;
+ $this->GetColArray($to_color,$from_color,$steps,$colors,$this->numcolors);
+ for($i=0; $i < $steps; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ break;
+
+ case 9: // RIGHT REFLECTION
+ $steps1 = round(0.7*abs($xr-$xl));
+ $delta = $xr>=$xl ? 1 : -1;
+
+ $this->GetColArray($from_color,$to_color,$steps1,$colors,$this->numcolors);
+ for($x=$xl, $i=0; $i < $steps1; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ $steps2 = max(1,round(0.08*abs($xr-$xl)));
+ $this->img->SetColor($to_color);
+ for($j=0; $j< $steps2; ++$j) {
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ $steps = abs($xr-$xl)-$steps1-$steps2;
+ $this->GetColArray($to_color,$from_color.':1.3',$steps,$colors,$this->numcolors);
+ for($i=0; $i < $steps; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ break;
+
+
+ case 5: // Rectangle
+ $steps = floor(min(($yb-$yt)+1,($xr-$xl)+1)/2);
+ $this->GetColArray($from_color,$to_color,$steps,$colors,$this->numcolors);
+ $dx = ($xr-$xl)/2;
+ $dy = ($yb-$yt)/2;
+ $x=$xl;$y=$yt;$x2=$xr;$y2=$yb;
+ for($x=$xl, $i=0; $x < $xl+$dx && $y < $yt+$dy ; ++$x, ++$y, --$x2, --$y2, ++$i) {
+ assert( $i < count($colors));
+ $this->img->current_color = $colors[$i];
+ $this->img->Rectangle($x,$y,$x2,$y2);
+ }
+ $this->img->Line($x,$y,$x2,$y2);
+ break;
+
+ default:
+ die("JpGraph Error: Unknown gradient style (=$style).");
+ break;
+ }
+ }
+
+ // Fill a special case of a polygon with a flat bottom
+ // with a gradient. Can be used for filled line plots.
+ // Please note that this is NOT a generic gradient polygon fill
+ // routine. It assumes that the bottom is flat (like a drawing
+ // of a mountain)
+ function FilledFlatPolygon($pts,$from_color,$to_color) {
+ if( count($pts) == 0 ) return;
+
+ $maxy=$pts[1];
+ $miny=$pts[1];
+ $n = count($pts) ;
+ for( $i=0, $idx=0; $i < $n; $i += 2) {
+ $x = round($pts[$i]);
+ $y = round($pts[$i+1]);
+ $miny = min($miny,$y);
+ $maxy = max($maxy,$y);
+ }
+
+ $colors = array();
+ $this->GetColArray($from_color,$to_color,abs($maxy-$miny)+1,$colors,$this->numcolors);
+ for($i=$miny, $idx=0; $i <= $maxy; ++$i ) {
+ $colmap[$i] = $colors[$idx++];
+ }
+
+ $n = count($pts)/2 ;
+ $idx = 0 ;
+ while( $idx < $n-1 ) {
+ $p1 = array(round($pts[$idx*2]),round($pts[$idx*2+1]));
+ $p2 = array(round($pts[++$idx*2]),round($pts[$idx*2+1]));
+
+ // Find the largest rectangle we can fill
+ $y = max($p1[1],$p2[1]) ;
+ for($yy=$maxy; $yy >= $y; --$yy) {
+ $this->img->current_color = $colmap[$yy];
+ $this->img->Line($p1[0],$yy,$p2[0],$yy);
+ }
+
+ if( $p1[1] == $p2[1] ) continue;
+
+ // Fill the rest using lines (slow...)
+ $slope = ($p2[0]-$p1[0])/($p1[1]-$p2[1]);
+ $x1 = $p1[0];
+ $x2 = $p2[0];
+ $start = $y;
+ if( $p1[1] > $p2[1] ) {
+ while( $y >= $p2[1] ) {
+ $x1=$slope*($start-$y)+$p1[0];
+ $this->img->current_color = $colmap[$y];
+ $this->img->Line($x1,$y,$x2,$y);
+ --$y;
+ }
+ }
+ else {
+ while( $y >= $p1[1] ) {
+ $x2=$p2[0]+$slope*($start-$y);
+ $this->img->current_color = $colmap[$y];
+ $this->img->Line($x1,$y,$x2,$y);
+ --$y;
+ }
+ }
+ }
+ }
+
+//---------------
+// PRIVATE METHODS
+ // Add to the image color map the necessary colors to do the transition
+ // between the two colors using $numcolors intermediate colors
+ function GetColArray($from_color,$to_color,$arr_size,&$colors,$numcols=100) {
+ if( $arr_size==0 ) return;
+ // If color is given as text get it's corresponding r,g,b values
+ $from_color = $this->img->rgb->Color($from_color);
+ $to_color = $this->img->rgb->Color($to_color);
+
+ $rdelta=($to_color[0]-$from_color[0])/$numcols;
+ $gdelta=($to_color[1]-$from_color[1])/$numcols;
+ $bdelta=($to_color[2]-$from_color[2])/$numcols;
+ $colorsperstep = $numcols/$arr_size;
+ $prevcolnum = -1;
+ for ($i=0; $i < $arr_size; ++$i) {
+ $colnum = floor($colorsperstep*$i);
+ if ( $colnum == $prevcolnum )
+ $colors[$i] = $colidx;
+ else {
+ $r = floor($from_color[0] + $colnum*$rdelta);
+ $g = floor($from_color[1] + $colnum*$gdelta);
+ $b = floor($from_color[2] + $colnum*$bdelta);
+ $colidx = $this->img->rgb->Allocate(sprintf("#%02x%02x%02x",$r,$g,$b));
+ $colors[$i] = $colidx;
+ }
+ $prevcolnum = $colnum;
+ }
+ }
+} // Class
+
+?>
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_LINE.PHP
+// Description: Line plot extension for JpGraph
+// Created: 2001-01-08
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_line.php,v 1.48.2.3 2003/08/23 22:01:56 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+// constants for the (filled) area
+DEFINE("LP_AREA_FILLED", true);
+DEFINE("LP_AREA_NOT_FILLED", false);
+DEFINE("LP_AREA_BORDER",false);
+DEFINE("LP_AREA_NO_BORDER",true);
+
+//===================================================
+// CLASS LinePlot
+// Description:
+//===================================================
+class LinePlot extends Plot{
+ var $filled=false;
+ var $fill_color='blue';
+ var $mark=null;
+ var $step_style=false, $center=false;
+ var $line_style=1; // Default to solid
+ var $filledAreas = array(); // array of arrays(with min,max,col,filled in them)
+ var $barcenter=false; // When we mix line and bar. Should we center the line in the bar.
+ var $fillFromMin = false ;
+ var $fillgrad=false,$fillgrad_fromcolor='navy',$fillgrad_tocolor='silver',$fillgrad_numcolors=100;
+
+//---------------
+// CONSTRUCTOR
+ function LinePlot(&$datay,$datax=false) {
+ $this->Plot($datay,$datax);
+ $this->mark = new PlotMark();
+ }
+//---------------
+// PUBLIC METHODS
+
+ // Set style, filled or open
+ function SetFilled($aFlag=true) {
+ JpGraphError::Raise('LinePlot::SetFilled() is deprecated. Use SetFillColor()');
+ }
+
+ function SetBarCenter($aFlag=true) {
+ $this->barcenter=$aFlag;
+ }
+
+ function SetStyle($aStyle) {
+ $this->line_style=$aStyle;
+ }
+
+ function SetStepStyle($aFlag=true) {
+ $this->step_style = $aFlag;
+ }
+
+ function SetColor($aColor) {
+ parent::SetColor($aColor);
+ }
+
+ function SetFillFromYMin($f=true) {
+ $this->fillFromMin = $f ;
+ }
+
+ function SetFillColor($aColor,$aFilled=true) {
+ $this->fill_color=$aColor;
+ $this->filled=$aFilled;
+ }
+
+ function SetFillGradient($aFromColor,$aToColor,$aNumColors=100,$aFilled=true) {
+ $this->fillgrad_fromcolor = $aFromColor;
+ $this->fillgrad_tocolor = $aToColor;
+ $this->fillgrad_numcolors = $aNumColors;
+ $this->filled = $aFilled;
+ $this->fillgrad = true;
+ }
+
+ function Legend(&$graph) {
+ if( $this->legend!="" ) {
+ if( $this->filled ) {
+ $graph->legend->Add($this->legend,
+ $this->fill_color,$this->mark,0,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ } else {
+ $graph->legend->Add($this->legend,
+ $this->color,$this->mark,$this->line_style,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ }
+ }
+ }
+
+ function AddArea($aMin=0,$aMax=0,$aFilled=LP_AREA_NOT_FILLED,$aColor="gray9",$aBorder=LP_AREA_BORDER) {
+ if($aMin > $aMax) {
+ // swap
+ $tmp = $aMin;
+ $aMin = $aMax;
+ $aMax = $tmp;
+ }
+ $this->filledAreas[] = array($aMin,$aMax,$aColor,$aFilled,$aBorder);
+ }
+
+ // Gets called before any axis are stroked
+ function PreStrokeAdjust(&$graph) {
+
+ // If another plot type have already adjusted the
+ // offset we don't touch it.
+ // (We check for empty in case the scale is a log scale
+ // and hence doesn't contain any xlabel_offset)
+ if( empty($graph->xaxis->scale->ticks->xlabel_offset) ||
+ $graph->xaxis->scale->ticks->xlabel_offset == 0 ) {
+ if( $this->center ) {
+ ++$this->numpoints;
+ $a=0.5; $b=0.5;
+ } else {
+ $a=0; $b=0;
+ }
+ $graph->xaxis->scale->ticks->SetXLabelOffset($a);
+ $graph->SetTextScaleOff($b);
+ //$graph->xaxis->scale->ticks->SupressMinorTickMarks();
+ }
+ }
+
+ function Stroke(&$img,&$xscale,&$yscale) {
+ $numpoints=count($this->coords[0]);
+ if( isset($this->coords[1]) ) {
+ if( count($this->coords[1])!=$numpoints )
+ JpGraphError::Raise("Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])." Number of Y-points:$numpoints");
+ else
+ $exist_x = true;
+ }
+ else
+ $exist_x = false;
+
+ if( $this->barcenter )
+ $textadj = 0.5-$xscale->text_scale_off;
+ else
+ $textadj = 0;
+
+ // Find the first numeric data point
+ $startpoint=0;
+ while( $startpoint < $numpoints && !is_numeric($this->coords[0][$startpoint]) )
+ ++$startpoint;
+
+ // Bail out if no data points
+ if( $startpoint == $numpoints )
+ return;
+
+ if( $exist_x )
+ $xs=$this->coords[1][$startpoint];
+ else
+ $xs= $textadj+$startpoint;
+
+ $img->SetStartPoint($xscale->Translate($xs),
+ $yscale->Translate($this->coords[0][$startpoint]));
+
+
+ if( $this->filled ) {
+ $cord[] = $xscale->Translate($xs);
+ $min = $yscale->GetMinVal();
+ if( $min > 0 || $this->fillFromMin )
+ $cord[] = $yscale->Translate($min);
+ else
+ $cord[] = $yscale->Translate(0);
+ }
+ $xt = $xscale->Translate($xs);
+ $yt = $yscale->Translate($this->coords[0][$startpoint]);
+ $cord[] = $xt;
+ $cord[] = $yt;
+ $yt_old = $yt;
+
+ $this->value->Stroke($img,$this->coords[0][$startpoint],$xt,$yt);
+
+ $img->SetColor($this->color);
+ $img->SetLineWeight($this->weight);
+ $img->SetLineStyle($this->line_style);
+ for( $pnts=$startpoint+1; $pnts<$numpoints; ++$pnts) {
+
+ if( $exist_x ) $x=$this->coords[1][$pnts];
+ else $x=$pnts+$textadj;
+ $xt = $xscale->Translate($x);
+ $yt = $yscale->Translate($this->coords[0][$pnts]);
+
+ $y=$this->coords[0][$pnts];
+ if( $this->step_style && is_numeric($y) ) {
+ $img->StyleLineTo($xt,$yt_old);
+ $img->StyleLineTo($xt,$yt);
+
+ $cord[] = $xt;
+ $cord[] = $yt_old;
+
+ $cord[] = $xt;
+ $cord[] = $yt;
+
+ }
+ else {
+ if( is_numeric($y) || (is_string($y) && $y != "-") ) {
+ $tmp1=$this->coords[0][$pnts];
+ $tmp2=$this->coords[0][$pnts-1];
+ if( is_numeric($tmp1) && (is_numeric($tmp2) || $tmp2=="-" ) ) {
+ $img->StyleLineTo($xt,$yt);
+ }
+ else {
+ $img->SetStartPoint($xt,$yt);
+ }
+ if( is_numeric($tmp1) &&
+ (is_numeric($tmp2) || $tmp2=="-" || ($this->filled && $tmp2=='') ) ) {
+ $cord[] = $xt;
+ $cord[] = $yt;
+ }
+ }
+ }
+ $yt_old = $yt;
+
+ $this->StrokeDataValue($img,$this->coords[0][$pnts],$xt,$yt);
+ }
+
+ if( $this->filled ) {
+ $cord[] = $xt;
+ if( $min > 0 || $this->fillFromMin )
+ $cord[] = $yscale->Translate($min);
+ else
+ $cord[] = $yscale->Translate(0);
+ if( $this->fillgrad ) {
+ $img->SetLineWeight(1);
+ $grad = new Gradient($img);
+ $grad->SetNumColors($this->fillgrad_numcolors);
+ $grad->FilledFlatPolygon($cord,$this->fillgrad_fromcolor,$this->fillgrad_tocolor);
+ $img->SetLineWeight($this->weight);
+ }
+ else {
+ $img->SetColor($this->fill_color);
+ $img->FilledPolygon($cord);
+ }
+ if( $this->line_weight > 0 ) {
+ $img->SetColor($this->color);
+ $img->Polygon($cord);
+ }
+ }
+
+ if(!empty($this->filledAreas)) {
+
+ $minY = $yscale->Translate($yscale->GetMinVal());
+ $factor = ($this->step_style ? 4 : 2);
+
+ for($i = 0; $i < sizeof($this->filledAreas); ++$i) {
+ // go through all filled area elements ordered by insertion
+ // fill polygon array
+ $areaCoords[] = $cord[$this->filledAreas[$i][0] * $factor];
+ $areaCoords[] = $minY;
+
+ $areaCoords =
+ array_merge($areaCoords,
+ array_slice($cord,
+ $this->filledAreas[$i][0] * $factor,
+ ($this->filledAreas[$i][1] - $this->filledAreas[$i][0] + ($this->step_style ? 0 : 1)) * $factor));
+ $areaCoords[] = $areaCoords[sizeof($areaCoords)-2]; // last x
+ $areaCoords[] = $minY; // last y
+
+ if($this->filledAreas[$i][3]) {
+ $img->SetColor($this->filledAreas[$i][2]);
+ $img->FilledPolygon($areaCoords);
+ $img->SetColor($this->color);
+ }
+ // Check if we should draw the frame.
+ // If not we still re-draw the line since it might have been
+ // partially overwritten by the filled area and it doesn't look
+ // very good.
+ // TODO: The behaviour is undefined if the line does not have
+ // any line at the position of the area.
+ if( $this->filledAreas[$i][4] )
+ $img->Polygon($areaCoords);
+ else
+ $img->Polygon($cord);
+
+ $areaCoords = array();
+ }
+ }
+
+ if( $this->mark->type == -1 || $this->mark->show == false )
+ return;
+
+ for( $pnts=0; $pnts<$numpoints; ++$pnts) {
+
+ if( $exist_x ) $x=$this->coords[1][$pnts];
+ else $x=$pnts+$textadj;
+ $xt = $xscale->Translate($x);
+ $yt = $yscale->Translate($this->coords[0][$pnts]);
+
+ if( is_numeric($this->coords[0][$pnts]) ) {
+ if( !empty($this->csimtargets[$pnts]) ) {
+ $this->mark->SetCSIMTarget($this->csimtargets[$pnts]);
+ $this->mark->SetCSIMAlt($this->csimalts[$pnts]);
+ }
+ if( $exist_x )
+ $x=$this->coords[1][$pnts];
+ else
+ $x=$pnts;
+ $this->mark->SetCSIMAltVal($this->coords[0][$pnts],$x);
+ $this->mark->Stroke($img,$xt,$yt);
+ $this->csimareas .= $this->mark->GetCSIMAreas();
+ $this->StrokeDataValue($img,$this->coords[0][$pnts],$xt,$yt);
+ }
+ }
+
+
+ }
+} // Class
+
+
+//===================================================
+// CLASS AccLinePlot
+// Description:
+//===================================================
+class AccLinePlot extends Plot {
+ var $plots=null,$nbrplots=0,$numpoints=0;
+//---------------
+// CONSTRUCTOR
+ function AccLinePlot($plots) {
+ $this->plots = $plots;
+ $this->nbrplots = count($plots);
+ $this->numpoints = $plots[0]->numpoints;
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Legend(&$graph) {
+ foreach( $this->plots as $p )
+ $p->DoLegend($graph);
+ }
+
+ function Max() {
+ list($xmax) = $this->plots[0]->Max();
+ $nmax=0;
+ for($i=0; $i<count($this->plots); ++$i) {
+ $n = count($this->plots[$i]->coords[0]);
+ $nmax = max($nmax,$n);
+ list($x) = $this->plots[$i]->Max();
+ $xmax = Max($xmax,$x);
+ }
+ for( $i = 0; $i < $nmax; $i++ ) {
+ // Get y-value for line $i by adding the
+ // individual bars from all the plots added.
+ // It would be wrong to just add the
+ // individual plots max y-value since that
+ // would in most cases give to large y-value.
+ $y=$this->plots[0]->coords[0][$i];
+ for( $j = 1; $j < $this->nbrplots; $j++ ) {
+ $y += $this->plots[ $j ]->coords[0][$i];
+ }
+ $ymax[$i] = $y;
+ }
+ $ymax = max($ymax);
+ return array($xmax,$ymax);
+ }
+
+ function Min() {
+ $nmax=0;
+ list($xmin,$ysetmin) = $this->plots[0]->Min();
+ for($i=0; $i<count($this->plots); ++$i) {
+ $n = count($this->plots[$i]->coords[0]);
+ $nmax = max($nmax,$n);
+ list($x,$y) = $this->plots[$i]->Min();
+ $xmin = Min($xmin,$x);
+ $ysetmin = Min($y,$ysetmin);
+ }
+ for( $i = 0; $i < $nmax; $i++ ) {
+ // Get y-value for line $i by adding the
+ // individual bars from all the plots added.
+ // It would be wrong to just add the
+ // individual plots min y-value since that
+ // would in most cases give to small y-value.
+ $y=$this->plots[0]->coords[0][$i];
+ for( $j = 1; $j < $this->nbrplots; $j++ ) {
+ $y += $this->plots[ $j ]->coords[0][$i];
+ }
+ $ymin[$i] = $y;
+ }
+ $ymin = Min($ysetmin,Min($ymin));
+ return array($xmin,$ymin);
+ }
+
+ // Gets called before any axis are stroked
+ function PreStrokeAdjust(&$graph) {
+
+ // If another plot type have already adjusted the
+ // offset we don't touch it.
+ // (We check for empty in case the scale is a log scale
+ // and hence doesn't contain any xlabel_offset)
+
+ if( empty($graph->xaxis->scale->ticks->xlabel_offset) ||
+ $graph->xaxis->scale->ticks->xlabel_offset == 0 ) {
+ if( $this->center ) {
+ ++$this->numpoints;
+ $a=0.5; $b=0.5;
+ } else {
+ $a=0; $b=0;
+ }
+ $graph->xaxis->scale->ticks->SetXLabelOffset($a);
+ $graph->SetTextScaleOff($b);
+ $graph->xaxis->scale->ticks->SupressMinorTickMarks();
+ }
+
+ }
+
+ // To avoid duplicate of line drawing code here we just
+ // change the y-values for each plot and then restore it
+ // after we have made the stroke. We must do this copy since
+ // it wouldn't be possible to create an acc line plot
+ // with the same graphs, i.e AccLinePlot(array($pl,$pl,$pl));
+ // since this method would have a side effect.
+ function Stroke(&$img,&$xscale,&$yscale) {
+ $img->SetLineWeight($this->weight);
+ $this->numpoints = count($this->plots[0]->coords[0]);
+ // Allocate array
+ $coords[$this->nbrplots][$this->numpoints]=0;
+ for($i=0; $i<$this->numpoints; $i++) {
+ $coords[0][$i]=$this->plots[0]->coords[0][$i];
+ $accy=$coords[0][$i];
+ for($j=1; $j<$this->nbrplots; ++$j ) {
+ $coords[$j][$i] = $this->plots[$j]->coords[0][$i]+$accy;
+ $accy = $coords[$j][$i];
+ }
+ }
+ for($j=$this->nbrplots-1; $j>=0; --$j) {
+ $p=$this->plots[$j];
+ for( $i=0; $i<$this->numpoints; ++$i) {
+ $tmp[$i]=$p->coords[0][$i];
+ $p->coords[0][$i]=$coords[$j][$i];
+ }
+ $p->Stroke($img,$xscale,$yscale);
+ for( $i=0; $i<$this->numpoints; ++$i)
+ $p->coords[0][$i]=$tmp[$i];
+ $p->coords[0][]=$tmp;
+ }
+ }
+} // Class
+
+
+/* EOF */
+?>
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_LOG.PHP
+// Description: Log scale plot extension for JpGraph
+// Created: 2001-01-08
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_log.php,v 1.15.2.1 2003/08/15 15:27:40 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+
+DEFINE('LOGLABELS_PLAIN',0);
+DEFINE('LOGLABELS_MAGNITUDE',1);
+
+//===================================================
+// CLASS LogScale
+// Description: Logarithmic scale between world and screen
+//===================================================
+class LogScale extends LinearScale {
+//---------------
+// CONSTRUCTOR
+
+ // Log scale is specified using the log of min and max
+ function LogScale($min,$max,$type="y") {
+ $this->LinearScale($min,$max,$type);
+ $this->ticks = new LogTicks();
+ $this->name = 'log';
+ }
+
+//----------------
+// PUBLIC METHODS
+
+ // Translate between world and screen
+ function Translate($a) {
+ if( $a < 0 ) {
+ JpGraphError::Raise("Negative data values can not be used in a log scale.");
+ exit(1);
+ }
+ if( $a==0 ) $a=1;
+ $a=log10($a);
+ return ceil($this->off + ($a*1.0 - $this->scale[0]) * $this->scale_factor);
+ }
+
+ // Relative translate (don't include offset) usefull when we just want
+ // to know the relative position (in pixels) on the axis
+ function RelTranslate($a) {
+ if( $a==0 ) $a=1;
+ $a=log10($a);
+ return round(($a*1.0 - $this->scale[0]) * $this->scale_factor);
+ }
+
+ // Use bcpow() for increased precision
+ function GetMinVal() {
+ if( function_exists("bcpow") )
+ return round(bcpow(10,$this->scale[0],15),14);
+ else
+ return round(pow(10,$this->scale[0]));
+ }
+
+ function GetMaxVal() {
+ if( function_exists("bcpow") )
+ return round(bcpow(10,$this->scale[1],15),14);
+ else
+ return round(pow(10,$this->scale[1]));
+ }
+
+ // Logarithmic autoscaling is much simplier since we just
+ // set the min and max to logs of the min and max values.
+ // Note that for log autoscale the "maxstep" the fourth argument
+ // isn't used. This is just included to give the method the same
+ // signature as the linear counterpart.
+ function AutoScale(&$img,$min,$max,$dummy) {
+ if( $min==0 ) $min=1;
+ assert($max>0);
+ $smin = floor(log10($min));
+ $smax = ceil(log10($max));
+ $this->Update($img,$smin,$smax);
+ }
+//---------------
+// PRIVATE METHODS
+} // Class
+
+//===================================================
+// CLASS LogTicks
+// Description:
+//===================================================
+class LogTicks extends Ticks{
+ var $label_logtype=LOGLABELS_MAGNITUDE;
+//---------------
+// CONSTRUCTOR
+ function LogTicks() {
+ }
+//---------------
+// PUBLIC METHODS
+ function IsSpecified() {
+ return true;
+ }
+
+ function SetLabelLogType($aType) {
+ $this->label_logtype = $aType;
+ }
+
+ // For log scale it's meaningless to speak about a major step
+ // We just return -1 to make the framework happy (specifically
+ // StrokeLabels() )
+ function GetMajor() {
+ return -1;
+ }
+
+ function SetTextLabelStart($aStart) {
+ JpGraphError::Raise('Specifying tick interval for a logarithmic scale is undefined. Remove any calls to SetTextLabelStart() or SetTextTickInterval() on the logarithmic scale.');
+ }
+
+ function SetXLabelOffset($dummy) {
+ // For log scales we dont care about XLabel offset
+ }
+
+ // Draw ticks on image "img" using scale "scale". The axis absolute
+ // position in the image is specified in pos, i.e. for an x-axis
+ // it specifies the absolute y-coord and for Y-ticks it specified the
+ // absolute x-position.
+ function Stroke(&$img,&$scale,$pos) {
+ $start = $scale->GetMinVal();
+ $limit = $scale->GetMaxVal();
+ $nextMajor = 10*$start;
+ $step = $nextMajor / 10.0;
+
+
+ $img->SetLineWeight($this->weight);
+
+ if( $scale->type == "y" ) {
+ // member direction specified if the ticks should be on
+ // left or right side.
+ $a=$pos + $this->direction*$this->GetMinTickAbsSize();
+ $a2=$pos + $this->direction*$this->GetMajTickAbsSize();
+
+ $count=1;
+ $this->maj_ticks_pos[0]=$scale->Translate($start);
+ $this->maj_ticklabels_pos[0]=$scale->Translate($start);
+ if( $this->supress_first )
+ $this->maj_ticks_label[0]="";
+ else {
+ if( $this->label_formfunc != '' ) {
+ $f = $this->label_formfunc;
+ $this->maj_ticks_label[0]=call_user_func($f,$start);
+ }
+ elseif( $this->label_logtype == LOGLABELS_PLAIN )
+ $this->maj_ticks_label[0]=$start;
+ else
+ $this->maj_ticks_label[0]='10^'.round(log10($start));
+ }
+ $i=1;
+ for($y=$start; $y<=$limit; $y+=$step,++$count ) {
+ $ys=$scale->Translate($y);
+ $this->ticks_pos[]=$ys;
+ $this->ticklabels_pos[]=$ys;
+ if( $count % 10 == 0 ) {
+ if( $this->majcolor!="" ) {
+ $img->PushColor($this->majcolor);
+ $img->Line($pos,$ys,$a2,$ys);
+ $img->PopColor();
+ }
+ else
+ $img->Line($pos,$ys,$a2,$ys);
+
+ $this->maj_ticks_pos[$i]=$ys;
+ $this->maj_ticklabels_pos[$i]=$ys;
+
+ if( $this->label_formfunc != '' ) {
+ $f = $this->label_formfunc;
+ $this->maj_ticks_label[$i]=call_user_func($f,$nextMajor);
+ }
+ elseif( $this->label_logtype == 0 )
+ $this->maj_ticks_label[$i]=$nextMajor;
+ else
+ $this->maj_ticks_label[$i]='10^'.round(log10($nextMajor));
+ ++$i;
+ $nextMajor *= 10;
+ $step *= 10;
+ $count=1;
+ }
+ else {
+ if( $this->mincolor!="" ) $img->PushColor($this->mincolor);
+ $img->Line($pos,$ys,$a,$ys);
+ if( $this->mincolor!="" ) $img->PopCOlor();
+ }
+ }
+ }
+ else {
+ $a=$pos - $this->direction*$this->GetMinTickAbsSize();
+ $a2=$pos - $this->direction*$this->GetMajTickAbsSize();
+ $count=1;
+ $this->maj_ticks_pos[0]=$scale->Translate($start);
+ $this->maj_ticklabels_pos[0]=$scale->Translate($start);
+ if( $this->supress_first )
+ $this->maj_ticks_label[0]="";
+ else {
+ if( $this->label_formfunc != '' ) {
+ $f = $this->label_formfunc;
+ $this->maj_ticks_label[0]=call_user_func($f,$start);
+ }
+ elseif( $this->label_logtype == 0 )
+ $this->maj_ticks_label[0]=$start;
+ else
+ $this->maj_ticks_label[0]='10^'.round(log10($start));
+ }
+ $i=1;
+ for($x=$start; $x<=$limit; $x+=$step,++$count ) {
+ $xs=$scale->Translate($x);
+ $this->ticks_pos[]=$xs;
+ $this->ticklabels_pos[]=$xs;
+ if( $count % 10 == 0 ) {
+ $img->Line($xs,$pos,$xs,$a2);
+ $this->maj_ticks_pos[$i]=$xs;
+ $this->maj_ticklabels_pos[$i]=$xs;
+
+ if( $this->label_formfunc != '' ) {
+ $f = $this->label_formfunc;
+ $this->maj_ticks_label[$i]=call_user_func($f,$nextMajor);
+ }
+ elseif( $this->label_logtype == 0 )
+ $this->maj_ticks_label[$i]=$nextMajor;
+ else
+ $this->maj_ticks_label[$i]='10^'.round(log10($nextMajor));
+ ++$i;
+ $nextMajor *= 10;
+ $step *= 10;
+ $count=1;
+ }
+ else
+ $img->Line($xs,$pos,$xs,$a);
+ }
+ }
+ return true;
+ }
+} // Class
+/* EOF */
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_PIE.PHP
+// Description: Pie plot extension for JpGraph
+// Created: 2001-02-14
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_pie.php,v 1.49.2.8 2003/08/16 00:30:21 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+
+// Defines for PiePlot::SetLabelType()
+DEFINE("PIE_VALUE_ABS",1);
+DEFINE("PIE_VALUE_PER",0);
+DEFINE("PIE_VALUE_PERCENTAGE",0);
+DEFINE("PIE_VALUE_ADJPERCENTAGE",2);
+DEFINE("PIE_VALUE_ADJPER",2);
+
+//===================================================
+// CLASS PiePlot
+// Description: Draws a pie plot
+//===================================================
+class PiePlot {
+ var $posx=0.5,$posy=0.5;
+ var $radius=0.3;
+ var $explode_radius=array(),$explode_all=false,$explode_r=20;
+ var $labels=null, $legends=null;
+ var $csimtargets=null; // Array of targets for CSIM
+ var $csimareas=''; // Generated CSIM text
+ var $csimalts=null; // ALT tags for corresponding target
+ var $data=null;
+ var $title;
+ var $startangle=0;
+ var $weight=1, $color="black";
+ var $legend_margin=6,$show_labels=true;
+ var $themearr = array(
+ "earth" => array(136,34,40,45,46,62,63,134,74,10,120,136,141,168,180,77,209,218,346,395,89,430),
+ "pastel" => array(27,415,128,59,66,79,105,110,42,147,152,230,236,240,331,337,405,38),
+ "water" => array(8,370,24,40,335,56,213,237,268,14,326,387,10,388),
+ "sand" => array(27,168,34,170,19,50,65,72,131,209,46,393));
+ var $theme="earth";
+ var $setslicecolors=array();
+ var $labeltype=0; // Default to percentage
+ var $pie_border=true,$pie_interior_border=true;
+ var $value;
+ var $ishadowcolor='',$ishadowdrop=4;
+ var $ilabelposadj=1;
+ var $legendcsimtargets = array();
+ var $legendcsimalts = array();
+ var $adjusted_data = array();
+
+//---------------
+// CONSTRUCTOR
+ function PiePlot($data) {
+ $this->data = array_reverse($data);
+ $this->title = new Text("");
+ $this->title->SetFont(FF_FONT1,FS_BOLD);
+ $this->value = new DisplayValue();
+ $this->value->Show();
+ $this->value->SetFormat('%.1f%%');
+ }
+
+//---------------
+// PUBLIC METHODS
+ function SetCenter($x,$y=0.5) {
+ $this->posx = $x;
+ $this->posy = $y;
+ }
+
+ function SetColor($aColor) {
+ $this->color = $aColor;
+ }
+
+ function SetSliceColors($aColors) {
+ $this->setslicecolors = $aColors;
+ }
+
+ function SetShadow($aColor='darkgray',$aDropWidth=4) {
+ $this->ishadowcolor = $aColor;
+ $this->ishadowdrop = $aDropWidth;
+ }
+
+ function SetCSIMTargets($targets,$alts=null) {
+ $this->csimtargets=array_reverse($targets);
+ if( is_array($alts) )
+ $this->csimalts=array_reverse($alts);
+ }
+
+ function GetCSIMareas() {
+ return $this->csimareas;
+ }
+
+ function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) {
+ //Slice number, ellipse centre (x,y), height, width, start angle, end angle
+ while( $sa > 2*M_PI ) $sa = $sa - 2*M_PI;
+ while( $ea > 2*M_PI ) $ea = $ea - 2*M_PI;
+
+ $sa = 2*M_PI - $sa;
+ $ea = 2*M_PI - $ea;
+
+ //add coordinates of the centre to the map
+ $coords = "$xc, $yc";
+
+ //add coordinates of the first point on the arc to the map
+ $xp = floor(($radius*cos($ea))+$xc);
+ $yp = floor($yc-$radius*sin($ea));
+ $coords.= ", $xp, $yp";
+
+ //add coordinates every 0.2 radians
+ $a=$ea+0.2;
+ while ($a<$sa) {
+ $xp = floor($radius*cos($a)+$xc);
+ $yp = floor($yc-$radius*sin($a));
+ $coords.= ", $xp, $yp";
+ $a += 0.2;
+ }
+
+ //Add the last point on the arc
+ $xp = floor($radius*cos($sa)+$xc);
+ $yp = floor($yc-$radius*sin($sa));
+ $coords.= ", $xp, $yp";
+ if( !empty($this->csimtargets[$i]) ) {
+ $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
+ $this->csimtargets[$i]."\"";
+ if( !empty($this->csimalts[$i]) ) {
+ $tmp=sprintf($this->csimalts[$i],$this->data[$i]);
+ $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimareas .= ">\n";
+ }
+ }
+
+
+ function SetTheme($aTheme) {
+ if( in_array($aTheme,array_keys($this->themearr)) )
+ $this->theme = $aTheme;
+ else
+ JpGraphError::Raise("PiePLot::SetTheme() Unknown theme: $aTheme");
+ }
+
+ function ExplodeSlice($e,$radius=20) {
+ if( ! is_integer($e) )
+ JpGraphError::Raise('Argument to PiePlot::ExplodeSlice() must be an integer');
+ $this->explode_radius[$e]=$radius;
+ }
+
+ function ExplodeAll($radius=20) {
+ $this->explode_all=true;
+ $this->explode_r = $radius;
+ }
+
+ function Explode($aExplodeArr) {
+ if( !is_array($aExplodeArr) ) {
+ JpGraphError::Raise("Argument to PiePlot::Explode() must be an array with integer distances.");
+ }
+ $this->explode_radius = $aExplodeArr;
+ }
+
+ function SetStartAngle($aStart) {
+ if( $aStart < 0 || $aStart > 360 ) {
+ JpGraphError::Raise('Slice start angle must be between 0 and 360 degrees.');
+ }
+ $this->startangle = 360-$aStart;
+ $this->startangle *= M_PI/180;
+ }
+
+ function SetFont($family,$style=FS_NORMAL,$size=10) {
+ JpGraphError::Raise('PiePlot::SetFont() is deprecated. Use PiePlot->value->SetFont() instead.');
+ }
+
+ // Size in percentage
+ function SetSize($aSize) {
+ if( ($aSize>0 && $aSize<=0.5) || ($aSize>10 && $aSize<1000) )
+ $this->radius = $aSize;
+ else
+ JpGraphError::Raise("PiePlot::SetSize() Radius for pie must either be specified as a fraction
+ [0, 0.5] of the size of the image or as an absolute size in pixels
+ in the range [10, 1000]");
+ }
+
+ function SetFontColor($aColor) {
+ JpGraphError::Raise('PiePlot::SetFontColor() is deprecated. Use PiePlot->value->SetColor() instead.');
+ }
+
+ // Set label arrays
+ function SetLegends($aLegend) {
+ $this->legends = $aLegend;
+ }
+
+ // Set text labels for slices
+ function SetLabels($aLabels,$aLblPosAdj="auto") {
+ $this->labels = array_reverse($aLabels);
+ $this->ilabelposadj=$aLblPosAdj;
+ }
+
+ function SetLabelPos($aLblPosAdj) {
+ $this->ilabelposadj=$aLblPosAdj;
+ }
+
+ // Should we display actual value or percentage?
+ function SetLabelType($t) {
+ if( $t < 0 || $t > 2 )
+ JpGraphError::Raise("PiePlot::SetLabelType() Type for pie plots must be 0 or 1 (not $t).");
+ $this->labeltype=$t;
+ }
+
+ function SetValueType($aType) {
+ $this->SetLabelType($aType);
+ }
+
+ // Should the circle around a pie plot be displayed
+ function ShowBorder($exterior=true,$interior=true) {
+ $this->pie_border = $exterior;
+ $this->pie_interior_border = $interior;
+ }
+
+ // Setup the legends
+ function Legend(&$graph) {
+ $colors = array_keys($graph->img->rgb->rgb_table);
+ sort($colors);
+ $ta=$this->themearr[$this->theme];
+ $n = count($this->data);
+
+ if( $this->setslicecolors==null ) {
+ $numcolors=count($ta);
+ if( get_class($this)==='pieplot3d' ) {
+ $ta = array_reverse(array_slice($ta,0,$n));
+ }
+ }
+ else {
+ $this->setslicecolors = array_slice($this->setslicecolors,0,$n);
+ $numcolors=$n;
+ if( $graph->pieaa && get_class($this)==='pieplot' ) {
+ $this->setslicecolors = array_reverse($this->setslicecolors);
+ }
+ }
+
+ $sum=0;
+ for($i=0; $i < $n; ++$i)
+ $sum += $this->data[$i];
+
+ // Bail out with error if the sum is 0
+ if( $sum==0 )
+ JpGraphError::Raise("Illegal pie plot. Sum of all data is zero for Pie!");
+
+ // Make sure we don't plot more values than data points
+ // (in case the user added more legends than data points)
+ $n = min(count($this->legends),count($this->data));
+ if( $this->legends != "" )
+ $this->legends = array_reverse($this->legends);
+ for( $i=$n-1; $i >= 0; --$i ) {
+ $l = $this->legends[$i];
+ // Replace possible format with actual values
+ if( $this->labeltype==0 ) {
+ $l = sprintf($l,100*$this->data[$i]/$sum);
+ $alt = sprintf($this->csimalts[$i],$this->data[$i]);
+
+ }
+ elseif( $this->labeltype == 1) {
+ $l = sprintf($l,$this->data[$i]);
+ $alt = sprintf($this->csimalts[$i],$this->data[$i]);
+
+ }
+ else {
+ $l = sprintf($l,$this->adjusted_data[$i]);
+ $alt = sprintf($this->csimalts[$i],$this->adjusted_data[$i]);
+ }
+
+
+ if( $this->setslicecolors==null )
+ $graph->legend->Add($l,$colors[$ta[$i%$numcolors]],"",0,
+ $this->csimtargets[$i],$alt);
+ else
+ $graph->legend->Add($l,$this->setslicecolors[$i%$numcolors],"",0,
+ $this->csimtargets[$i],$alt);
+ }
+ }
+
+ // Adjust the rounded percetage value so that the sum of
+ // of the pie slices are always 100%
+ // Using the Hare/Niemeyer method
+ function AdjPercentage($aData,$aPrec=0) {
+ $mul=100;
+ if( $aPrec > 0 && $aPrec < 3 ) {
+ if( $aPrec == 1 )
+ $mul=1000;
+ else
+ $mul=10000;
+ }
+
+ $tmp = array();
+ $result = array();
+ $quote_sum=0;
+ $n = count($aData) ;
+ for( $i=0, $sum=0; $i < $n; ++$i )
+ $sum+=$aData[$i];
+ foreach($aData as $index => $value) {
+ $tmp_percentage=$value/$sum*$mul;
+ $result[$index]=floor($tmp_percentage);
+ $tmp[$index]=$tmp_percentage-$result[$index];
+ $quote_sum+=$result[$index];
+ }
+ if( $quote_sum == $mul) {
+ if( $mul > 100 ) {
+ $tmp = $mul / 100;
+ for( $i=0; $i < $n; ++$i ) {
+ $result[$i] /= $tmp ;
+ }
+ }
+ return $result;
+ }
+ arsort($tmp,SORT_NUMERIC);
+ reset($tmp);
+ for($i=0; $i < $mul-$quote_sum; $i++)
+ {
+ $result[key($tmp)]++;
+ next($tmp);
+ }
+ if( $mul > 100 ) {
+ $tmp = $mul / 100;
+ for( $i=0; $i < $n; ++$i ) {
+ $result[$i] /= $tmp ;
+ }
+ }
+ return $result;
+ }
+
+
+ function Stroke(&$img,$aaoption=0) {
+ // aaoption is used to handle antialias
+ // aaoption == 0 a normal pie
+ // aaoption == 1 just the body
+ // aaoption == 2 just the values
+
+ // Explode scaling. If anti anti alias we scale the image
+ // twice and we also need to scale the exploding distance
+ $expscale = $aaoption === 1 ? 2 : 1;
+
+ if( $this->labeltype == 2 ) {
+ // Adjust the data so that it will add up to 100%
+ $this->adjusted_data = $this->AdjPercentage($this->data);
+ }
+
+ $colors = array_keys($img->rgb->rgb_table);
+ sort($colors);
+ $ta=$this->themearr[$this->theme];
+ $n = count($this->data);
+
+ if( $this->setslicecolors==null ) {
+ $numcolors=count($ta);
+ }
+ else {
+ $this->setslicecolors = array_reverse(array_slice($this->setslicecolors,0,$n));
+ $numcolors=count($this->setslicecolors);
+ }
+
+ // Draw the slices
+ $sum=0;
+ for($i=0; $i < $n; ++$i)
+ $sum += $this->data[$i];
+
+ // Bail out with error if the sum is 0
+ if( $sum==0 )
+ JpGraphError::Raise("Sum of all data is 0 for Pie.");
+
+ // Set up the pie-circle
+ if( $this->radius <= 1 )
+ $radius = floor($this->radius*min($img->width,$img->height));
+ else {
+ $radius = $aaoption === 1 ? $this->radius*2 : $this->radius;
+ }
+
+ if( $this->posx <= 1 && $this->posx > 0 )
+ $xc = round($this->posx*$img->width);
+ else
+ $xc = $this->posx ;
+
+ if( $this->posy <= 1 && $this->posy > 0 )
+ $yc = round($this->posy*$img->height);
+ else
+ $yc = $this->posy ;
+
+ $n = count($this->data);
+
+ if( $this->explode_all )
+ for($i=0; $i < $n; ++$i)
+ $this->explode_radius[$i]=$this->explode_r;
+
+ if( $this->ishadowcolor != "" && $aaoption !== 2) {
+ $accsum=0;
+ $angle2 = $this->startangle;
+ $img->SetColor($this->ishadowcolor);
+ for($i=0; $sum > 0 && $i < $n; ++$i) {
+ $j = $n-$i-1;
+ $d = $this->data[$i];
+ $angle1 = $angle2;
+ $accsum += $d;
+ $angle2 = $this->startangle+2*M_PI*$accsum/$sum;
+ if( empty($this->explode_radius[$j]) )
+ $this->explode_radius[$j]=0;
+
+ $la = 2*M_PI - (abs($angle2-$angle1)/2.0+$angle1);
+
+ $xcm = $xc + $this->explode_radius[$j]*cos($la)*$expscale;
+ $ycm = $yc - $this->explode_radius[$j]*sin($la)*$expscale;
+
+ $xcm += $this->ishadowdrop*$expscale;
+ $ycm += $this->ishadowdrop*$expscale;
+
+ $img->CakeSlice($xcm,$ycm,$radius,$radius,
+ $angle1*180/M_PI,$angle2*180/M_PI,$this->ishadowcolor);
+
+ }
+ }
+
+ $accsum=0;
+ $angle2 = $this->startangle;
+ $img->SetColor($this->color);
+ for($i=0; $sum>0 && $i < $n; ++$i) {
+ $j = $n-$i-1;
+ if( empty($this->explode_radius[$j]) )
+ $this->explode_radius[$j]=0;
+ $d = $this->data[$i];
+ $angle1 = $angle2;
+ $accsum += $d;
+ $angle2 = $this->startangle+2*M_PI*$accsum/$sum;
+ $this->la[$i] = 2*M_PI - (abs($angle2-$angle1)/2.0+$angle1);
+
+ if( $d == 0 ) continue;
+
+ if( $this->setslicecolors==null )
+ $slicecolor=$colors[$ta[$i%$numcolors]];
+ else
+ $slicecolor=$this->setslicecolors[$i%$numcolors];
+
+ if( $this->pie_interior_border && $aaoption===0 )
+ $img->SetColor($this->color);
+ else
+ $img->SetColor($slicecolor);
+
+ $arccolor = $this->pie_border && $aaoption===0 ? $this->color : "";
+
+ $xcm = $xc + $this->explode_radius[$j]*cos($this->la[$i])*$expscale;
+ $ycm = $yc - $this->explode_radius[$j]*sin($this->la[$i])*$expscale;
+
+ if( $aaoption !== 2 ) {
+ $img->CakeSlice($xcm,$ycm,$radius-1,$radius-1,
+ $angle1*180/M_PI,$angle2*180/M_PI,$slicecolor,$arccolor);
+ }
+
+ if( $this->csimtargets && $aaoption !== 1 )
+ $this->AddSliceToCSIM($i,$xcm,$ycm,$radius,$angle1,$angle2);
+ }
+
+ // Format the titles for each slice
+ for( $i=0; $i < $n; ++$i) {
+ if( $this->labeltype==0 ) {
+ if( $sum != 0 )
+ $l = 100.0*$this->data[$i]/$sum;
+ else
+ $l = 0.0;
+ }
+ elseif( $this->labeltype==1 ) {
+ $l = $this->data[$i]*1.0;
+ }
+ else {
+ $l = $this->adjusted_data[$i];
+ }
+ if( isset($this->labels[$i]) && is_string($this->labels[$i]) )
+ $this->labels[$i]=sprintf($this->labels[$i],$l);
+ else
+ $this->labels[$i]=$l;
+ }
+
+ if( $this->value->show && $aaoption !== 1 ) {
+ $this->StrokeAllLabels($img,$xc,$yc,$radius);
+ }
+
+ // Adjust title position
+ if( $aaoption !== 1 ) {
+ $this->title->Pos($xc,
+ $yc-$this->title->GetFontHeight($img)-$radius-$this->title->margin,
+ "center","bottom");
+ $this->title->Stroke($img);
+ }
+
+ }
+
+//---------------
+// PRIVATE METHODS
+
+ function StrokeAllLabels($img,$xc,$yc,$radius) {
+ $n = count($this->labels);
+ for($i=0; $i < $n; ++$i) {
+ $this->StrokeLabel($this->labels[$i],$img,$xc,$yc,
+ $this->la[$i],
+ $radius + $this->explode_radius[$n-$i-1]);
+ }
+ }
+
+ // Position the labels of each slice
+ function StrokeLabel($label,$img,$xc,$yc,$a,$r) {
+
+ // Default value
+ if( $this->ilabelposadj === 'auto' )
+ $this->ilabelposadj = 0.65;
+
+ // We position the values diferently depending on if they are inside
+ // or outside the pie
+ if( $this->ilabelposadj < 1.0 ) {
+
+ $this->value->SetAlign('center','center');
+ $this->value->margin = 0;
+
+ $xt=round($this->ilabelposadj*$r*cos($a)+$xc);
+ $yt=round($yc-$this->ilabelposadj*$r*sin($a));
+
+ $this->value->Stroke($img,$label,$xt,$yt);
+ }
+ else {
+
+ $this->value->halign = "left";
+ $this->value->valign = "top";
+ $this->value->margin = 0;
+
+
+ // Position the axis title.
+ // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
+ // that intersects with the extension of the corresponding axis. The code looks a little
+ // bit messy but this is really the only way of having a reasonable position of the
+ // axis titles.
+ $img->SetFont($this->value->ff,$this->value->fs,$this->value->fsize);
+ $h=$img->GetTextHeight($label);
+ // For numeric values the format of the display value
+ // must be taken into account
+ if( is_numeric($label) ) {
+ if( $label > 0 )
+ $w=$img->GetTextWidth(sprintf($this->value->format,$label));
+ else
+ $w=$img->GetTextWidth(sprintf($this->value->negformat,$label));
+ }
+ else
+ $w=$img->GetTextWidth($label);
+
+ if( $this->ilabelposadj > 1.0 && $this->ilabelposadj < 5.0) {
+ $r *= $this->ilabelposadj;
+ }
+
+ $r += $img->GetFontHeight()/1.5;
+ $xt=round($r*cos($a)+$xc);
+ $yt=round($yc-$r*sin($a));
+
+ while( $a < 0 ) $a += 2*M_PI;
+ while( $a > 2*M_PI ) $a -= 2*M_PI;
+
+ if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0;
+ if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI;
+ if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1;
+ if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI);
+
+ if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI;
+ if( $a<=M_PI/4 ) $dy=(1-$a*2/M_PI);
+ if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1;
+ if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI);
+ if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0;
+
+ $this->value->Stroke($img,$label,$xt-$dx*$w,$yt-$dy*$h);
+ }
+ }
+} // Class
+
+
+//===================================================
+// CLASS PiePlotC
+// Description: Same as a normal pie plot but with a
+// filled circle in the center
+//===================================================
+class PiePlotC extends PiePlot {
+ var $imidsize=0.5; // Fraction of total width
+ var $imidcolor='white';
+ var $midtitle='';
+ var $middlecsimtarget="",$middlecsimalt="";
+
+ function PiePlotC($data,$aCenterTitle='') {
+ parent::PiePlot($data);
+ $this->midtitle = new Text();
+ $this->midtitle->ParagraphAlign('center');
+ }
+
+ function SetMid($aTitle,$aColor='white',$aSize=0.5) {
+ $this->midtitle->Set($aTitle);
+ $this->imidsize = $aSize ;
+ $this->imidcolor = $aColor ;
+ }
+
+ function SetMidTitle($aTitle) {
+ $this->midtitle->Set($aTitle);
+ }
+
+ function SetMidSize($aSize) {
+ $this->imidsize = $aSize ;
+ }
+
+ function SetMidColor($aColor) {
+ $this->imidcolor = $aColor ;
+ }
+
+ function SetMidCSIM($aTarget,$aAlt) {
+ $this->middlecsimtarget = $aTarget;
+ $this->middlecsimalt = $aAlt;
+ }
+
+ function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) {
+ //Slice number, ellipse centre (x,y), radius, start angle, end angle
+ while( $sa > 2*M_PI ) $sa = $sa - 2*M_PI;
+ while( $ea > 2*M_PI ) $ea = $ea - 2*M_PI;
+
+ $sa = 2*M_PI - $sa;
+ $ea = 2*M_PI - $ea;
+
+ // Add inner circle first point
+ $xp = floor(($this->imidsize*$radius*cos($ea))+$xc);
+ $yp = floor($yc-($this->imidsize*$radius*sin($ea)));
+ $coords = "$xp, $yp";
+
+ //add coordinates every 0.25 radians
+ $a=$ea+0.25;
+ while ($a < $sa) {
+ $xp = floor(($this->imidsize*$radius*cos($a)+$xc));
+ $yp = floor($yc-($this->imidsize*$radius*sin($a)));
+ $coords.= ", $xp, $yp";
+ $a += 0.25;
+ }
+
+ // Make sure we end at the last point
+ $xp = floor(($this->imidsize*$radius*cos($sa)+$xc));
+ $yp = floor($yc-($this->imidsize*$radius*sin($sa)));
+ $coords.= ", $xp, $yp";
+
+ // Straight line to outer circle
+ $xp = floor($radius*cos($sa)+$xc);
+ $yp = floor($yc-$radius*sin($sa));
+ $coords.= ", $xp, $yp";
+
+ //add coordinates every 0.25 radians
+ $a=$sa - 0.25;
+ while ($a > $ea) {
+ $xp = floor($radius*cos($a)+$xc);
+ $yp = floor($yc-$radius*sin($a));
+ $coords.= ", $xp, $yp";
+ $a -= 0.25;
+ }
+
+ //Add the last point on the arc
+ $xp = floor($radius*cos($ea)+$xc);
+ $yp = floor($yc-$radius*sin($ea));
+ $coords.= ", $xp, $yp";
+
+ // Close the arc
+ $xp = floor(($this->imidsize*$radius*cos($ea))+$xc);
+ $yp = floor($yc-($this->imidsize*$radius*sin($ea)));
+ $coords .= ", $xp, $yp";
+
+ if( !empty($this->csimtargets[$i]) ) {
+ $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
+ $this->csimtargets[$i]."\"";
+ if( !empty($this->csimalts[$i]) ) {
+ $tmp=sprintf($this->csimalts[$i],$this->data[$i]);
+ $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimareas .= ">\n";
+ }
+ }
+
+
+ function Stroke($img,$aaoption=0) {
+
+ // Stroke the pie but don't stroke values
+ $tmp = $this->value->show;
+ $this->value->show = false;
+ parent::Stroke($img,$aaoption);
+ $this->value->show = $tmp;
+
+ $xc = round($this->posx*$img->width);
+ $yc = round($this->posy*$img->height);
+
+ $radius = floor($this->radius * min($img->width,$img->height)) ;
+
+
+ if( $this->imidsize > 0 && $aaoption !== 2 ) {
+
+ if( $this->ishadowcolor != "" ) {
+ $img->SetColor($this->ishadowcolor);
+ $img->FilledCircle($xc+$this->ishadowdrop,$yc+$this->ishadowdrop,
+ round($radius*$this->imidsize));
+ }
+
+ $img->SetColor($this->imidcolor);
+ $img->FilledCircle($xc,$yc,round($radius*$this->imidsize));
+
+ if( $this->pie_border && $aaoption === 0 ) {
+ $img->SetColor($this->color);
+ $img->Circle($xc,$yc,round($radius*$this->imidsize));
+ }
+
+ if( !empty($this->middlecsimtarget) )
+ $this->AddMiddleCSIM($xc,$yc,round($radius*$this->imidsize));
+
+ }
+
+ if( $this->value->show && $aaoption !== 1) {
+ $this->StrokeAllLabels($img,$xc,$yc,$radius);
+ $this->midtitle->Pos($xc,$yc,'center','center');
+ $this->midtitle->Stroke($img);
+ }
+
+ }
+
+ function AddMiddleCSIM($xc,$yc,$r) {
+ $this->csimareas .= "<area shape=\"circle\" coords=\"$xc,$yc,$r\" href=\"".
+ $this->middlecsimtarget."\"";
+ if( !empty($this->middlecsimalt) ) {
+ $tmp = $this->middlecsimalt;
+ $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimareas .= ">\n";
+ }
+
+ function StrokeLabel($label,$img,$xc,$yc,$a,$r) {
+
+ if( $this->ilabelposadj === 'auto' )
+ $this->ilabelposadj = (1-$this->imidsize)/2+$this->imidsize;
+
+ parent::StrokeLabel($label,$img,$xc,$yc,$a,$r);
+
+ }
+
+}
+
+
+//===================================================
+// CLASS PieGraph
+// Description:
+//===================================================
+class PieGraph extends Graph {
+ var $posx, $posy, $radius;
+ var $legends=array();
+ var $plots=array();
+ var $pieaa = false ;
+//---------------
+// CONSTRUCTOR
+ function PieGraph($width=300,$height=200,$cachedName="",$timeout=0,$inline=1) {
+ $this->Graph($width,$height,$cachedName,$timeout,$inline);
+ $this->posx=$width/2;
+ $this->posy=$height/2;
+ $this->SetColor(array(255,255,255));
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Add($aObj) {
+
+ if( is_array($aObj) && count($aObj) > 0 )
+ $cl = get_class($aObj[0]);
+ else
+ $cl = get_class($aObj);
+
+ if( $cl == 'text' )
+ $this->AddText($aObj);
+ else
+ $this->plots[] = $aObj;
+ }
+
+ function SetAntiAliasing($aFlg=true) {
+ $this->pieaa = $aFlg;
+ }
+
+ function SetColor($c) {
+ $this->SetMarginColor($c);
+ }
+
+
+ function DisplayCSIMAreas() {
+ $csim="";
+ foreach($this->plots as $p ) {
+ $csim .= $p->GetCSIMareas();
+ }
+ //$csim.= $this->legend->GetCSIMareas();
+ if (preg_match_all("/area shape=\"(\w+)\" coords=\"([0-9\, ]+)\"/", $csim, $coords)) {
+ $this->img->SetColor($this->csimcolor);
+ for ($i=0; $i<count($coords[0]); $i++) {
+ if ($coords[1][$i]=="poly") {
+ preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/',$coords[2][$i],$pts);
+ $this->img->SetStartPoint($pts[1][count($pts[0])-1],$pts[2][count($pts[0])-1]);
+ for ($j=0; $j<count($pts[0]); $j++) {
+ $this->img->LineTo($pts[1][$j],$pts[2][$j]);
+ }
+ } else if ($coords[1][$i]=="rect") {
+ $pts = preg_split('/,/', $coords[2][$i]);
+ $this->img->SetStartPoint($pts[0],$pts[1]);
+ $this->img->LineTo($pts[2],$pts[1]);
+ $this->img->LineTo($pts[2],$pts[3]);
+ $this->img->LineTo($pts[0],$pts[3]);
+ $this->img->LineTo($pts[0],$pts[1]);
+
+ }
+ }
+ }
+ }
+
+ // Method description
+ function Stroke($aStrokeFileName="") {
+ // If the filename is the predefined value = '_csim_special_'
+ // we assume that the call to stroke only needs to do enough
+ // to correctly generate the CSIM maps.
+ // We use this variable to skip things we don't strictly need
+ // to do to generate the image map to improve performance
+ // a best we can. Therefor you will see a lot of tests !$_csim in the
+ // code below.
+ $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
+
+ // We need to know if we have stroked the plot in the
+ // GetCSIMareas. Otherwise the CSIM hasn't been generated
+ // and in the case of GetCSIM called before stroke to generate
+ // CSIM without storing an image to disk GetCSIM must call Stroke.
+ $this->iHasStroked = true;
+
+
+
+ $n = count($this->plots);
+
+ if( $this->pieaa ) {
+
+ if( !$_csim ) {
+ if( $this->background_image != "" ) {
+ $this->StrokeFrameBackground();
+ }
+ else {
+ $this->StrokeFrame();
+ }
+ }
+
+
+ $w = $this->img->width;
+ $h = $this->img->height;
+ $oldimg = $this->img->img;
+
+ $this->img->CreateImgCanvas(2*$w,2*$h);
+
+ $this->img->SetColor( $this->margin_color );
+ $this->img->FilledRectangle(0,0,2*$w-1,2*$h-1);
+
+ for($i=0; $i < $n; ++$i) {
+ if( $this->plots[$i]->posx > 1 )
+ $this->plots[$i]->posx *= 2 ;
+ if( $this->plots[$i]->posy > 1 )
+ $this->plots[$i]->posy *= 2 ;
+
+ $this->plots[$i]->Stroke($this->img,1);
+
+ if( $this->plots[$i]->posx > 1 )
+ $this->plots[$i]->posx /= 2 ;
+ if( $this->plots[$i]->posy > 1 )
+ $this->plots[$i]->posy /= 2 ;
+ }
+
+ $indent = $this->doframe ? ($this->frame_weight + ($this->doshadow ? $this->shadow_width : 0 )) : 0 ;
+ $indent += $this->framebevel ? $this->framebeveldepth + 1 : 0 ;
+ $this->img->CopyCanvasH($oldimg,$this->img->img,$indent,$indent,$indent,$indent,
+ $w-2*$indent,$h-2*$indent,2*($w-$indent),2*($h-$indent));
+
+ $this->img->img = $oldimg ;
+ $this->img->width = $w ;
+ $this->img->height = $h ;
+
+ for($i=0; $i < $n; ++$i) {
+ $this->plots[$i]->Stroke($this->img,2);
+ $this->plots[$i]->Legend($this);
+ }
+
+ }
+ else {
+
+ if( !$_csim ) {
+ if( $this->background_image != "" ) {
+ $this->StrokeFrameBackground();
+ }
+ else {
+ $this->StrokeFrame();
+ }
+ }
+
+ for($i=0; $i < $n; ++$i) {
+ $this->plots[$i]->Stroke($this->img);
+ $this->plots[$i]->Legend($this);
+ }
+ }
+
+
+ $this->legend->Stroke($this->img);
+ $this->footer->Stroke($this->img);
+
+ if( !$_csim ) {
+ $this->StrokeTitles();
+
+ // Stroke texts
+ if( $this->texts != null ) {
+ $n = count($this->texts);
+ for($i=0; $i < $n; ++$i ) {
+ $this->texts[$i]->Stroke($this->img);
+ }
+ }
+
+ if( JPG_DEBUG ) {
+ $this->DisplayCSIMAreas();
+ }
+ // If the filename is given as the special "__handle"
+ // then the image handler is returned and the image is NOT
+ // streamed back
+ if( $aStrokeFileName == _IMG_HANDLER ) {
+ return $this->img->img;
+ }
+ else {
+ // Finally stream the generated picture
+ $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
+ $aStrokeFileName);
+ }
+ }
+ }
+} // Class
+
+/* EOF */
+?>
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_PIE3D.PHP
+// Description: 3D Pie plot extension for JpGraph
+// Created: 2001-03-24
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_pie3d.php,v 1.46.2.3 2003/08/15 11:07:07 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+//===================================================
+// CLASS PiePlot3D
+// Description: Plots a 3D pie with a specified projection
+// angle between 20 and 70 degrees.
+//===================================================
+class PiePlot3D extends PiePlot {
+ var $labelhintcolor="red",$showlabelhint=true,$labelmargin=0.30;
+ var $angle=50;
+ var $edgecolor="", $edgeweight=1;
+ var $iThickness=false;
+
+//---------------
+// CONSTRUCTOR
+ function PiePlot3d(&$data) {
+ $this->radius = 0.5;
+ $this->data = $data;
+ $this->title = new Text("");
+ $this->title->SetFont(FF_FONT1,FS_BOLD);
+ $this->value = new DisplayValue();
+ $this->value->Show();
+ $this->value->SetFormat('%.0f%%');
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ // Set label arrays
+ function SetLegends($aLegend) {
+ $this->legends = array_reverse($aLegend);
+ }
+
+ function SetSliceColors($aColors) {
+ $this->setslicecolors = $aColors;
+ }
+
+ function Legend(&$aGraph) {
+ parent::Legend($aGraph);
+ $aGraph->legend->txtcol = array_reverse($aGraph->legend->txtcol);
+ }
+
+ function SetCSIMTargets($targets,$alts=null) {
+ $this->csimtargets = $targets;
+ $this->csimalts = $alts;
+ }
+
+ // Should the slices be separated by a line? If color is specified as "" no line
+ // will be used to separate pie slices.
+ function SetEdge($aColor,$aWeight=1) {
+ $this->edgecolor = $aColor;
+ $this->edgeweight = $aWeight;
+ }
+
+ // Specify projection angle for 3D in degrees
+ // Must be between 20 and 70 degrees
+ function SetAngle($a) {
+ if( $a<5 || $a>90 )
+ JpGraphError::Raise("PiePlot3D::SetAngle() 3D Pie projection angle must be between 5 and 85 degrees.");
+ else
+ $this->angle = $a;
+ }
+
+ function AddSliceToCSIM($i,$xc,$yc,$height,$width,$thick,$sa,$ea) { //Slice number, ellipse centre (x,y), height, width, start angle, end angle
+
+ $sa *= M_PI/180;
+ $ea *= M_PI/180;
+
+ //add coordinates of the centre to the map
+ $coords = "$xc, $yc";
+
+ //add coordinates of the first point on the arc to the map
+ $xp = floor($width*cos($sa)/2+$xc);
+ $yp = floor($yc-$height*sin($sa)/2);
+ $coords.= ", $xp, $yp";
+
+ //If on the front half, add the thickness offset
+ if ($sa >= M_PI && $sa <= 2*M_PI*1.01) {
+ $yp = floor($yp+$thick);
+ $coords.= ", $xp, $yp";
+ }
+
+ //add coordinates every 0.2 radians
+ $a=$sa+0.2;
+ while ($a<$ea) {
+ $xp = floor($width*cos($a)/2+$xc);
+ if ($a >= M_PI && $a <= 2*M_PI*1.01) {
+ $yp = floor($yc-($height*sin($a)/2)+$thick);
+ } else {
+ $yp = floor($yc-$height*sin($a)/2);
+ }
+ $coords.= ", $xp, $yp";
+ $a += 0.2;
+ }
+
+ //Add the last point on the arc
+ $xp = floor($width*cos($ea)/2+$xc);
+ $yp = floor($yc-$height*sin($ea)/2);
+
+
+ if ($ea >= M_PI && $ea <= 2*M_PI*1.01) {
+ $coords.= ", $xp, ".floor($yp+$thick);
+ }
+ $coords.= ", $xp, $yp";
+ $alt='';
+ if( !empty($this->csimalts[$i]) ) {
+ $tmp=sprintf($this->csimalts[$i],$this->data[$i]);
+ $alt="alt=\"$tmp\" title=\"$tmp\"";
+ }
+ if( !empty($this->csimtargets[$i]) )
+ $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->csimtargets[$i]."\" $alt>\n";
+ }
+
+ function SetLabels($aLabels,$aLblPosAdj="auto") {
+ $this->labels = $aLabels;
+ $this->ilabelposadj=$aLblPosAdj;
+ }
+
+
+ // Distance from the pie to the labels
+ function SetLabelMargin($m) {
+ assert($m>0 && $m<1);
+ $this->labelmargin=$m;
+ }
+
+ // Show a thin line from the pie to the label for a specific slice
+ function ShowLabelHint($f=true) {
+ $this->showlabelhint=$f;
+ }
+
+ // Set color of hint line to label for each slice
+ function SetLabelHintColor($c) {
+ $this->labelhintcolor=$c;
+ }
+
+ function SetHeight($aHeight) {
+ $this->iThickness = $aHeight;
+ }
+
+
+// Normalize Angle between 0-360
+ function NormAngle($a) {
+ // Normalize anle to 0 to 2M_PI
+ //
+ if( $a > 0 ) {
+ while($a > 360) $a -= 360;
+ }
+ else {
+ while($a < 0) $a += 360;
+ }
+ if( $a < 0 )
+ $a = 360 + $a;
+
+ if( $a == 360 ) $a=0;
+ return $a;
+ }
+
+
+
+// Draw one 3D pie slice at position ($xc,$yc) with height $z
+ function Pie3DSlice($img,$xc,$yc,$w,$h,$sa,$ea,$z,$fillcolor,$shadow=0.65) {
+
+ // Due to the way the 3D Pie algorithm works we are
+ // guaranteed that any slice we get into this method
+ // belongs to either the left or right side of the
+ // pie ellipse. Hence, no slice will cross 90 or 270
+ // point.
+ if( ($sa < 90 && $ea > 90) || ( ($sa > 90 && $sa < 270) && $ea > 270) ) {
+ JpGraphError::Raise('Internal assertion failed. Pie3D::Pie3DSlice');
+ exit(1);
+ }
+
+ $p[] = array();
+
+ // Setup pre-calculated values
+ $rsa = $sa/180*M_PI; // to Rad
+ $rea = $ea/180*M_PI; // to Rad
+ $sinsa = sin($rsa);
+ $cossa = cos($rsa);
+ $sinea = sin($rea);
+ $cosea = cos($rea);
+
+ // p[] is the points for the overall slice and
+ // pt[] is the points for the top pie
+
+ // Angular step when approximating the arc with a polygon train.
+ $step = 0.05;
+
+ if( $sa >= 270 ) {
+ if( $ea > 360 || ($ea > 0 && $ea <= 90) ) {
+ if( $ea > 0 && $ea <= 90 ) {
+ // Adjust angle to simplify conditions in loops
+ $rea += 2*M_PI;
+ }
+
+ $p = array($xc,$yc,$xc,$yc+$z,
+ $xc+$w*$cossa,$z+$yc-$h*$sinsa);
+ $pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa);
+
+ for( $a=$rsa; $a < 2*M_PI; $a += $step ) {
+ $tca = cos($a);
+ $tsa = sin($a);
+ $p[] = $xc+$w*$tca;
+ $p[] = $z+$yc-$h*$tsa;
+ $pt[] = $xc+$w*$tca;
+ $pt[] = $yc-$h*$tsa;
+ }
+
+ $pt[] = $xc+$w;
+ $pt[] = $yc;
+
+ $p[] = $xc+$w;
+ $p[] = $z+$yc;
+ $p[] = $xc+$w;
+ $p[] = $yc;
+ $p[] = $xc;
+ $p[] = $yc;
+
+ for( $a=2*M_PI+$step; $a < $rea; $a += $step ) {
+ $pt[] = $xc + $w*cos($a);
+ $pt[] = $yc - $h*sin($a);
+ }
+
+ $pt[] = $xc+$w*$cosea;
+ $pt[] = $yc-$h*$sinea;
+ $pt[] = $xc;
+ $pt[] = $yc;
+
+ }
+ else {
+ $p = array($xc,$yc,$xc,$yc+$z,
+ $xc+$w*$cossa,$z+$yc-$h*$sinsa);
+ $pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa);
+
+ $rea = $rea == 0.0 ? 2*M_PI : $rea;
+ for( $a=$rsa; $a < $rea; $a += $step ) {
+ $tca = cos($a);
+ $tsa = sin($a);
+ $p[] = $xc+$w*$tca;
+ $p[] = $z+$yc-$h*$tsa;
+ $pt[] = $xc+$w*$tca;
+ $pt[] = $yc-$h*$tsa;
+ }
+
+ $pt[] = $xc+$w*$cosea;
+ $pt[] = $yc-$h*$sinea;
+ $pt[] = $xc;
+ $pt[] = $yc;
+
+ $p[] = $xc+$w*$cosea;
+ $p[] = $z+$yc-$h*$sinea;
+ $p[] = $xc+$w*$cosea;
+ $p[] = $yc-$h*$sinea;
+ $p[] = $xc;
+ $p[] = $yc;
+ }
+ }
+ elseif( $sa >= 180 ) {
+ $p = array($xc,$yc,$xc,$yc+$z,$xc+$w*$cosea,$z+$yc-$h*$sinea);
+ $pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea);
+
+ for( $a=$rea; $a>$rsa; $a -= $step ) {
+ $tca = cos($a);
+ $tsa = sin($a);
+ $p[] = $xc+$w*$tca;
+ $p[] = $z+$yc-$h*$tsa;
+ $pt[] = $xc+$w*$tca;
+ $pt[] = $yc-$h*$tsa;
+ }
+
+ $pt[] = $xc+$w*$cossa;
+ $pt[] = $yc-$h*$sinsa;
+ $pt[] = $xc;
+ $pt[] = $yc;
+
+ $p[] = $xc+$w*$cossa;
+ $p[] = $z+$yc-$h*$sinsa;
+ $p[] = $xc+$w*$cossa;
+ $p[] = $yc-$h*$sinsa;
+ $p[] = $xc;
+ $p[] = $yc;
+
+ }
+ elseif( $sa >= 90 ) {
+ if( $ea > 180 ) {
+ $p = array($xc,$yc,$xc,$yc+$z,$xc+$w*$cosea,$z+$yc-$h*$sinea);
+ $pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea);
+
+ for( $a=$rea; $a > M_PI; $a -= $step ) {
+ $tca = cos($a);
+ $tsa = sin($a);
+ $p[] = $xc+$w*$tca;
+ $p[] = $z + $yc - $h*$tsa;
+ $pt[] = $xc+$w*$tca;
+ $pt[] = $yc-$h*$tsa;
+ }
+
+ $p[] = $xc-$w;
+ $p[] = $z+$yc;
+ $p[] = $xc-$w;
+ $p[] = $yc;
+ $p[] = $xc;
+ $p[] = $yc;
+
+ $pt[] = $xc-$w;
+ $pt[] = $z+$yc;
+ $pt[] = $xc-$w;
+ $pt[] = $yc;
+
+ for( $a=M_PI-$step; $a > $rsa; $a -= $step ) {
+ $pt[] = $xc + $w*cos($a);
+ $pt[] = $yc - $h*sin($a);
+ }
+
+ $pt[] = $xc+$w*$cossa;
+ $pt[] = $yc-$h*$sinsa;
+ $pt[] = $xc;
+ $pt[] = $yc;
+
+ }
+ else { // $sa >= 90 && $ea <= 180
+ $p = array($xc,$yc,$xc,$yc+$z,
+ $xc+$w*$cosea,$z+$yc-$h*$sinea,
+ $xc+$w*$cosea,$yc-$h*$sinea,
+ $xc,$yc);
+
+ $pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea);
+
+ for( $a=$rea; $a>$rsa; $a -= $step ) {
+ $pt[] = $xc + $w*cos($a);
+ $pt[] = $yc - $h*sin($a);
+ }
+
+ $pt[] = $xc+$w*$cossa;
+ $pt[] = $yc-$h*$sinsa;
+ $pt[] = $xc;
+ $pt[] = $yc;
+
+ }
+ }
+ else { // sa > 0 && ea < 90
+
+ $p = array($xc,$yc,$xc,$yc+$z,
+ $xc+$w*$cossa,$z+$yc-$h*$sinsa,
+ $xc+$w*$cossa,$yc-$h*$sinsa,
+ $xc,$yc);
+
+ $pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa);
+
+ for( $a=$rsa; $a < $rea; $a += $step ) {
+ $pt[] = $xc + $w*cos($a);
+ $pt[] = $yc - $h*sin($a);
+ }
+
+ $pt[] = $xc+$w*$cosea;
+ $pt[] = $yc-$h*$sinea;
+ $pt[] = $xc;
+ $pt[] = $yc;
+ }
+
+ $img->PushColor($fillcolor.":".$shadow);
+ $img->FilledPolygon($p);
+ $img->PopColor();
+
+ $img->PushColor($fillcolor);
+ $img->FilledPolygon($pt);
+ $img->PopColor();
+ }
+
+// Draw a 3D Pie
+ function Pie3D($aaoption,$img,$data,$colors,$xc,$yc,$d,$angle,$z,
+ $shadow=0.65,$startangle=0,$edgecolor="",$edgeweight=1) {
+
+ //---------------------------------------------------------------------------
+ // As usual the algorithm get more complicated than I originally
+ // envisioned. I believe that this is as simple as it is possible
+ // to do it with the features I want. It's a good exercise to start
+ // thinking on how to do this to convince your self that all this
+ // is really needed for the general case.
+ //
+ // The algorithm two draw 3D pies without "real 3D" is done in
+ // two steps.
+ // First imagine the pie cut in half through a thought line between
+ // 12'a clock and 6'a clock. It now easy to imagine that we can plot
+ // the individual slices for each half by starting with the topmost
+ // pie slice and continue down to 6'a clock.
+ //
+ // In the algortithm this is done in three principal steps
+ // Step 1. Do the knife cut to ensure by splitting slices that extends
+ // over the cut line. This is done by splitting the original slices into
+ // upto 3 subslices.
+ // Step 2. Find the top slice for each half
+ // Step 3. Draw the slices from top to bottom
+ //
+ // The thing that slightly complicates this scheme with all the
+ // angle comparisons below is that we can have an arbitrary start
+ // angle so we must take into account the different equivalence classes.
+ // For the same reason we must walk through the angle array in a
+ // modulo fashion.
+ //
+ // Limitations of algorithm:
+ // * A small exploded slice which crosses the 270 degree point
+ // will get slightly nagged close to the center due to the fact that
+ // we print the slices in Z-order and that the slice left part
+ // get printed first and might get slightly nagged by a larger
+ // slice on the right side just before the right part of the small
+ // slice. Not a major problem though.
+ //---------------------------------------------------------------------------
+
+
+ // Determine the height of the ellippse which gives an
+ // indication of the inclination angle
+ $h = ($angle/90.0)*$d;
+ $sum = 0;
+ for($i=0; $i<count($data); ++$i ) {
+ $sum += $data[$i];
+ }
+
+ // Special optimization
+ if( $sum==0 ) return;
+
+ if( $this->labeltype == 2 ) {
+ $this->adjusted_data = $this->AdjPercentage($data);
+ }
+
+ // Setup the start
+ $accsum = 0;
+ $a = $startangle;
+ $a = $this->NormAngle($a);
+
+ //
+ // Step 1 . Split all slices that crosses 90 or 270
+ //
+ $idx=0;
+ $adjexplode=array();
+ $numcolors = count($colors);
+ for($i=0; $i<count($data); ++$i, ++$idx ) {
+ $da = $data[$i]/$sum * 360;
+
+ if( empty($this->explode_radius[$i]) )
+ $this->explode_radius[$i]=0;
+
+ $expscale=1;
+ if( $aaoption == 1 )
+ $expscale=2;
+
+ $la = $a + $da/2;
+ $explode = array( $xc + $this->explode_radius[$i]*cos($la*M_PI/180)*$expscale,
+ $yc - $this->explode_radius[$i]*sin($la*M_PI/180) * ($h/$d) *$expscale );
+ $adjexplode[$idx] = $explode;
+ $labeldata[$i] = array($la,$explode[0],$explode[1]);
+ $originalangles[$i] = array($a,$a+$da);
+
+ $ne = $this->NormAngle($a+$da);
+ if( $da <= 180 ) {
+ // If the slice size is <= 90 it can at maximum cut across
+ // one boundary (either 90 or 270) where it needs to be split
+ $split=-1; // no split
+ if( ($da<=90 && ($a <= 90 && $ne > 90)) ||
+ (($da <= 180 && $da >90) && (($a < 90 || $a >= 270) && $ne > 90)) ) {
+ $split = 90;
+ }
+ elseif( ($da<=90 && ($a <= 270 && $ne > 270)) ||
+ (($da<=180 && $da>90) && ($a >= 90 && $a < 270 && ($a+$da) > 270 )) ) {
+ $split = 270;
+ }
+ if( $split > 0 ) { // split in two
+ $angles[$idx] = array($a,$split);
+ $adjcolors[$idx] = $colors[$i % $numcolors];
+ $adjexplode[$idx] = $explode;
+ $angles[++$idx] = array($split,$ne);
+ $adjcolors[$idx] = $colors[$i % $numcolors];
+ $adjexplode[$idx] = $explode;
+ }
+ else { // no split
+ $angles[$idx] = array($a,$ne);
+ $adjcolors[$idx] = $colors[$i % $numcolors];
+ $adjexplode[$idx] = $explode;
+ }
+ }
+ else {
+ // da>180
+ // Slice may, depending on position, cross one or two
+ // bonudaries
+
+ if( $a < 90 )
+ $split = 90;
+ elseif( $a <= 270 )
+ $split = 270;
+ else
+ $split = 90;
+
+ $angles[$idx] = array($a,$split);
+ $adjcolors[$idx] = $colors[$i % $numcolors];
+ $adjexplode[$idx] = $explode;
+ //if( $a+$da > 360-$split ) {
+ // For slices larger than 270 degrees we might cross
+ // another boundary as well. This means that we must
+ // split the slice further. The comparison gets a little
+ // bit complicated since we must take into accound that
+ // a pie might have a startangle >0 and hence a slice might
+ // wrap around the 0 angle.
+ // Three cases:
+ // a) Slice starts before 90 and hence gets a split=90, but
+ // we must also check if we need to split at 270
+ // b) Slice starts after 90 but before 270 and slices
+ // crosses 90 (after a wrap around of 0)
+ // c) If start is > 270 (hence the firstr split is at 90)
+ // and the slice is so large that it goes all the way
+ // around 270.
+ if( ($a < 90 && ($a+$da > 270)) ||
+ ($a > 90 && $a<=270 && ($a+$da>360+90) ) ||
+ ($a > 270 && $this->NormAngle($a+$da)>270) ) {
+ $angles[++$idx] = array($split,360-$split);
+ $adjcolors[$idx] = $colors[$i % $numcolors];
+ $adjexplode[$idx] = $explode;
+ $angles[++$idx] = array(360-$split,$ne);
+ $adjcolors[$idx] = $colors[$i % $numcolors];
+ $adjexplode[$idx] = $explode;
+ }
+ else {
+ // Just a simple split to the previous decided
+ // angle.
+ $angles[++$idx] = array($split,$ne);
+ $adjcolors[$idx] = $colors[$i % $numcolors];
+ $adjexplode[$idx] = $explode;
+ }
+ }
+ $a += $da;
+ $a = $this->NormAngle($a);
+ }
+
+ // Total number of slices
+ $n = count($angles);
+
+ for($i=0; $i<$n; ++$i) {
+ list($dbgs,$dbge) = $angles[$i];
+ }
+
+ //
+ // Step 2. Find start index (first pie that starts in upper left quadrant)
+ //
+ $minval = $angles[0][0];
+ $min = 0;
+ for( $i=0; $i<$n; ++$i ) {
+ if( $angles[$i][0] < $minval ) {
+ $minval = $angles[$i][0];
+ $min = $i;
+ }
+ }
+ $j = $min;
+ $cnt = 0;
+ while( $angles[$j][1] <= 90 ) {
+ $j++;
+ if( $j>=$n) {
+ $j=0;
+ }
+ if( $cnt > $n ) {
+ JpGraphError::Raise("Pie3D Internal error (#1). Trying to wrap twice when looking for start index");
+ }
+ ++$cnt;
+ }
+ $start = $j;
+
+ //
+ // Step 3. Print slices in z-order
+ //
+ $cnt = 0;
+
+ // First stroke all the slices between 90 and 270 (left half circle)
+ // counterclockwise
+
+ while( $angles[$j][0] < 270 && $aaoption !== 2 ) {
+
+ list($x,$y) = $adjexplode[$j];
+
+ $this->Pie3DSlice($img,$x,$y,$d,$h,$angles[$j][0],$angles[$j][1],
+ $z,$adjcolors[$j],$shadow);
+
+ $last = array($x,$y,$j);
+
+ $j++;
+ if( $j >= $n ) $j=0;
+ if( $cnt > $n ) {
+ JpGraphError::Raise("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking.");
+ }
+ ++$cnt;
+ }
+
+ $slice_left = $n-$cnt;
+ $j=$start-1;
+ if($j<0) $j=$n-1;
+ $cnt = 0;
+
+ // The stroke all slices from 90 to -90 (right half circle)
+ // clockwise
+ while( $cnt < $slice_left && $aaoption !== 2 ) {
+
+ list($x,$y) = $adjexplode[$j];
+
+ $this->Pie3DSlice($img,$x,$y,$d,$h,$angles[$j][0],$angles[$j][1],
+ $z,$adjcolors[$j],$shadow);
+ $j--;
+ if( $cnt > $n ) {
+ JpGraphError::Raise("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking.");
+ }
+ if($j<0) $j=$n-1;
+ $cnt++;
+ }
+
+ // Now do a special thing. Stroke the last slice on the left
+ // halfcircle one more time. This is needed in the case where
+ // the slice close to 270 have been exploded. In that case the
+ // part of the slice close to the center of the pie might be
+ // slightly nagged.
+ if( $aaoption !== 2 )
+ $this->Pie3DSlice($img,$last[0],$last[1],$d,$h,$angles[$last[2]][0],
+ $angles[$last[2]][1],$z,$adjcolors[$last[2]],$shadow);
+
+
+ if( $aaoption !== 1 ) {
+ // Now print possible labels and add csim
+ $img->SetFont($this->value->ff,$this->value->fs);
+ $margin = $img->GetFontHeight()/2;
+ for($i=0; $i < count($data); ++$i ) {
+ $la = $labeldata[$i][0];
+ $x = $labeldata[$i][1] + cos($la*M_PI/180)*($d+$margin);
+ $y = $labeldata[$i][2] - sin($la*M_PI/180)*($h+$margin);
+ if( $la > 180 && $la < 360 ) $y += $z;
+ if( $this->labeltype == 0 ) {
+ if( $sum > 0 )
+ $l = 100*$data[$i]/$sum;
+ else
+ $l = 0;
+ }
+ elseif( $this->labeltype == 1 ) {
+ $l = $data[$i];
+ }
+ else {
+ $l = $this->adjusted_data[$i];
+ }
+ if( isset($this->labels[$i]) && is_string($this->labels[$i]) )
+ $l=sprintf($this->labels[$i],$l);
+
+ $this->StrokeLabels($l,$img,$labeldata[$i][0]*M_PI/180,$x,$y,$z);
+
+ $this->AddSliceToCSIM($i,$labeldata[$i][1],$labeldata[$i][2],$h*2,$d*2,$z,
+ $originalangles[$i][0],$originalangles[$i][1]);
+ }
+ }
+
+ //
+ // Finally add potential lines in pie
+ //
+
+ if( $edgecolor=="" || $aaoption !== 0 ) return;
+
+ $accsum = 0;
+ $a = $startangle;
+ $a = $this->NormAngle($a);
+
+ $a *= M_PI/180.0;
+
+ $idx=0;
+ $img->PushColor($edgecolor);
+ $img->SetLineWeight($edgeweight);
+
+ $fulledge = true;
+ for($i=0; $i < count($data) && $fulledge; ++$i ) {
+ if( empty($this->explode_radius[$i]) )
+ $this->explode_radius[$i]=0;
+ if( $this->explode_radius[$i] > 0 ) {
+ $fulledge = false;
+ }
+ }
+
+
+ for($i=0; $i < count($data); ++$i, ++$idx ) {
+
+ $da = $data[$i]/$sum * 2*M_PI;
+ $this->StrokeFullSliceFrame($img,$xc,$yc,$a,$a+$da,$d,$h,$z,$edgecolor,
+ $this->explode_radius[$i],$fulledge);
+ $a += $da;
+ }
+ $img->PopColor();
+ }
+
+ function StrokeFullSliceFrame($img,$xc,$yc,$sa,$ea,$w,$h,$z,$edgecolor,$exploderadius,$fulledge) {
+ $step = 0.02;
+
+ if( $exploderadius > 0 ) {
+ $la = ($sa+$ea)/2;
+ $xc += $exploderadius*cos($la);
+ $yc -= $exploderadius*sin($la) * ($h/$w) ;
+
+ }
+
+ $p = array($xc,$yc,$xc+$w*cos($sa),$yc-$h*sin($sa));
+
+ for($a=$sa; $a < $ea; $a += $step ) {
+ $p[] = $xc + $w*cos($a);
+ $p[] = $yc - $h*sin($a);
+ }
+
+ $p[] = $xc+$w*cos($ea);
+ $p[] = $yc-$h*sin($ea);
+ $p[] = $xc;
+ $p[] = $yc;
+
+ $img->SetColor($edgecolor);
+ $img->Polygon($p);
+
+ // Unfortunately we can't really draw the full edge around the whole of
+ // of the slice if any of the slices are exploded. The reason is that
+ // this algorithm is to simply. There are cases where the edges will
+ // "overwrite" other slices when they have been exploded.
+ // Doing the full, proper 3D hidden lines stiff is actually quite
+ // tricky. So for exploded pies we only draw the top edge. Not perfect
+ // but the "real" solution is much more complicated.
+ if( $fulledge && !( $sa > 0 && $sa < M_PI && $ea < M_PI) ) {
+
+ if($sa < M_PI && $ea > M_PI)
+ $sa = M_PI;
+
+ if($sa < 2*M_PI && (($ea >= 2*M_PI) || ($ea > 0 && $ea < $sa ) ) )
+ $ea = 2*M_PI;
+
+ if( $sa >= M_PI && $ea <= 2*M_PI ) {
+ $p = array($xc + $w*cos($sa),$yc - $h*sin($sa),
+ $xc + $w*cos($sa),$z + $yc - $h*sin($sa));
+
+ for($a=$sa+$step; $a < $ea; $a += $step ) {
+ $p[] = $xc + $w*cos($a);
+ $p[] = $z + $yc - $h*sin($a);
+ }
+ $p[] = $xc + $w*cos($ea);
+ $p[] = $z + $yc - $h*sin($ea);
+ $p[] = $xc + $w*cos($ea);
+ $p[] = $yc - $h*sin($ea);
+ $img->SetColor($edgecolor);
+ $img->Polygon($p);
+ }
+ }
+ }
+
+ function Stroke($img,$aaoption=0) {
+ $n = count($this->data);
+
+ // If user hasn't set the colors use the theme array
+ if( $this->setslicecolors==null ) {
+ $colors = array_keys($img->rgb->rgb_table);
+ sort($colors);
+ $idx_a=$this->themearr[$this->theme];
+ $ca = array();
+ $m = count($idx_a);
+ for($i=0; $i < $m; ++$i)
+ $ca[$i] = $colors[$idx_a[$i]];
+ $ca = array_reverse(array_slice($ca,0,$n));
+ }
+ else {
+ $ca = $this->setslicecolors;
+ }
+
+
+ if( $this->posx <= 1 && $this->posx > 0 )
+ $xc = round($this->posx*$img->width);
+ else
+ $xc = $this->posx ;
+
+ if( $this->posy <= 1 && $this->posy > 0 )
+ $yc = round($this->posy*$img->height);
+ else
+ $yc = $this->posy ;
+
+ if( $this->radius <= 1 ) {
+ $width = floor($this->radius*min($img->width,$img->height));
+ // Make sure that the pie doesn't overflow the image border
+ // The 0.9 factor is simply an extra margin to leave some space
+ // between the pie an the border of the image.
+ $width = min($width,min($xc*0.9,($yc*90/$this->angle-$width/4)*0.9));
+ }
+ else {
+ $width = $this->radius * ($aaoption === 1 ? 2 : 1 ) ;
+ }
+
+ // Add a sanity check for width
+ if( $width < 1 ) {
+ JpGraphError::Raise("Width for 3D Pie is 0. Specify a size > 0");
+ exit();
+ }
+
+ // Establish a thickness. By default the thickness is a fifth of the
+ // pie slice width (=pie radius) but since the perspective depends
+ // on the inclination angle we use some heuristics to make the edge
+ // slightly thicker the less the angle.
+
+ // Has user specified an absolute thickness? In that case use
+ // that instead
+
+ if( $this->iThickness ) {
+ $thick = $this->iThickness;
+ $thick *= ($aaoption === 1 ? 2 : 1 );
+ }
+ else
+ $thick = $width/12;
+ $a = $this->angle;
+ if( $a <= 30 ) $thick *= 1.6;
+ elseif( $a <= 40 ) $thick *= 1.4;
+ elseif( $a <= 50 ) $thick *= 1.2;
+ elseif( $a <= 60 ) $thick *= 1.0;
+ elseif( $a <= 70 ) $thick *= 0.8;
+ elseif( $a <= 80 ) $thick *= 0.7;
+ else $thick *= 0.6;
+
+ $thick = floor($thick);
+
+ if( $this->explode_all )
+ for($i=0; $i < $n; ++$i)
+ $this->explode_radius[$i]=$this->explode_r;
+
+ $this->Pie3D($aaoption,$img,$this->data, $ca, $xc, $yc, $width, $this->angle,
+ $thick, 0.65, $this->startangle, $this->edgecolor, $this->edgeweight);
+
+ // Adjust title position
+ if( $aaoption != 1 ) {
+ $this->title->Pos($xc,$yc-$this->title->GetFontHeight($img)-$width/2-$this->title->margin, "center","bottom");
+ $this->title->Stroke($img);
+ }
+ }
+
+//---------------
+// PRIVATE METHODS
+
+ // Position the labels of each slice
+ function StrokeLabels($label,$img,$a,$xp,$yp,$z) {
+ $this->value->halign="left";
+ $this->value->valign="top";
+ $this->value->margin=0;
+
+ // Position the axis title.
+ // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
+ // that intersects with the extension of the corresponding axis. The code looks a little
+ // bit messy but this is really the only way of having a reasonable position of the
+ // axis titles.
+ $img->SetFont($this->value->ff,$this->value->fs,$this->value->fsize);
+ $h=$img->GetTextHeight($label);
+ // For numeric values the format of the display value
+ // must be taken into account
+ if( is_numeric($label) ) {
+ if( $label >= 0 )
+ $w=$img->GetTextWidth(sprintf($this->value->format,$label));
+ else
+ $w=$img->GetTextWidth(sprintf($this->value->negformat,$label));
+ }
+ else
+ $w=$img->GetTextWidth($label);
+ while( $a > 2*M_PI ) $a -= 2*M_PI;
+ if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0;
+ if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI;
+ if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1;
+ if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI);
+
+ if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI;
+ if( $a<=M_PI/4 ) $dy=(1-$a*2/M_PI);
+ if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1;
+ if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI);
+ if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0;
+
+ $x = round($xp-$dx*$w);
+ $y = round($yp-$dy*$h);
+
+ /*
+ // Mark anchor point for debugging
+ $img->SetColor('red');
+ $img->Line($xp-10,$yp,$xp+10,$yp);
+ $img->Line($xp,$yp-10,$xp,$yp+10);
+ */
+
+ $this->value->Stroke($img,$label,$x,$y);
+ }
+} // Class
+
+/* EOF */
+?>
--- /dev/null
+<?php
+//=======================================================================
+// File: JPGRAPH_PLOTMARK.PHP
+// Description: Class file. Handles plotmarks
+// Created: 2003-03-21
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_plotmark.inc,v 1.9.2.4 2003/08/16 00:31:06 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+
+//========================================================================
+// CLASS ImgData
+// Description: Base class for all image data classes that contains the
+// real image data.
+//========================================================================
+class ImgData {
+ var $name = ''; // Each subclass gives a name
+ var $an = array(); // Data array names
+ var $colors = array(); // Available colors
+ var $index = array(); // Index for colors
+ var $maxidx = 0 ; // Max color index
+ var $anchor_x=0.5, $anchor_y=0.5 ; // Where is the center of the image
+ // Create a GD image from the data and return a GD handle
+ function GetImg($aMark,$aIdx) {
+ $n = $this->an[$aMark];
+ if( is_string($aIdx) ) {
+ if( !in_array($aIdx,$this->colors) ) {
+ JpGraphError::Raise('This marker "'.($this->name).'" does not exist in color: '.$aIdx);
+ die();
+ }
+ $idx = $this->index[$aIdx];
+ }
+ elseif( !is_integer($aIdx) ||
+ (is_integer($aIdx) && $aIdx > $this->maxidx ) ) {
+ JpGraphError::Raise('Mark color index too large for marker "'.($this->name).'"');
+ }
+ else
+ $idx = $aIdx ;
+ return Image::CreateFromString(base64_decode($this->{$n}[$idx][1]));
+ }
+ function GetAnchor() {
+ return array($this->anchor_x,$this->anchor_y);
+ }
+}
+
+
+// Keep a global flag cache to reduce memory usage
+$_gFlagCache=array(
+ 1 => null,
+ 2 => null,
+ 3 => null,
+ 4 => null,
+);
+// Only supposed to b called as statics
+class FlagCache {
+ function GetFlagImgByName($aSize,$aName) {
+ global $_gFlagCache;
+ require_once('jpgraph_flags.php');
+ if( $_gFlagCache[$aSize] === null ) {
+ $_gFlagCache[$aSize] =& new FlagImages($aSize);
+ }
+ $f =& $_gFlagCache[$aSize];
+ $idx = $f->GetIdxByName($aName,$aFullName);
+ return $f->GetImgByIdx($idx);
+ }
+}
+
+//===================================================
+// CLASS PlotMark
+// Description: Handles the plot marks in graphs
+//===================================================
+class PlotMark {
+ var $title, $show=true;
+ var $type,$weight=1;
+ var $color="black", $width=4, $fill_color="blue";
+ var $yvalue,$xvalue='',$csimtarget,$csimalt,$csimareas;
+ var $iFormatCallback="";
+ var $iFormatCallback2="";
+ var $markimg='',$iScale=1.0;
+ var $oldfilename='',$iFileName='';
+ var $imgdata_balls = null;
+ var $imgdata_diamonds = null;
+ var $imgdata_squares = null;
+ var $imgdata_bevels = null;
+ var $imgdata_stars = null;
+ var $imgdata_pushpins = null;
+
+//--------------
+// CONSTRUCTOR
+ function PlotMark() {
+ $this->title = new Text();
+ $this->title->Hide();
+ $this->csimareas = '';
+ $this->csimalt = '';
+ $this->type=-1;
+ }
+//---------------
+// PUBLIC METHODS
+ function SetType($aType,$aFileName='',$aScale=1.0) {
+ $this->type = $aType;
+ if( $aType == MARK_IMG && $aFileName=='' ) {
+ JpGraphError::Raise('A filename must be specified if you set the mark type to MARK_IMG.');
+ }
+ $this->iFileName = $aFileName;
+ $this->iScale = $aScale;
+ }
+
+ function SetCallback($aFunc) {
+ $this->iFormatCallback = $aFunc;
+ }
+
+ function SetCallbackYX($aFunc) {
+ $this->iFormatCallback2 = $aFunc;
+ }
+
+ function GetType() {
+ return $this->type;
+ }
+
+ function SetColor($aColor) {
+ $this->color=$aColor;
+ }
+
+ function SetFillColor($aFillColor) {
+ $this->fill_color = $aFillColor;
+ }
+
+ function SetWeight($aWeight) {
+ $this->weight = $aWeight;
+ }
+
+ // Synonym for SetWidth()
+ function SetSize($aWidth) {
+ $this->width=$aWidth;
+ }
+
+ function SetWidth($aWidth) {
+ $this->width=$aWidth;
+ }
+
+ function SetDefaultWidth() {
+ switch( $this->type ) {
+ case MARK_CIRCLE:
+ case MARK_FILLEDCIRCLE:
+ $this->width=4;
+ break;
+ default:
+ $this->width=7;
+ }
+ }
+
+ function GetWidth() {
+ return $this->width;
+ }
+
+ function Hide($aHide=true) {
+ $this->show = !$aHide;
+ }
+
+ function Show($aShow=true) {
+ $this->show = $aShow;
+ }
+
+ function SetCSIMAltVal($aY,$aX='') {
+ $this->yvalue=$aY;
+ $this->xvalue=$aX;
+ }
+
+ function SetCSIMTarget($aTarget) {
+ $this->csimtarget=$aTarget;
+ }
+
+ function SetCSIMAlt($aAlt) {
+ $this->csimalt=$aAlt;
+ }
+
+ function GetCSIMAreas(){
+ return $this->csimareas;
+ }
+
+ function AddCSIMPoly($aPts) {
+ $coords = round($aPts[0]).", ".round($aPts[1]);
+ $n = count($aPts)/2;
+ for( $i=1; $i < $n; ++$i){
+ $coords .= ", ".round($aPts[2*$i]).", ".round($aPts[2*$i+1]);
+ }
+ $this->csimareas="";
+ if( !empty($this->csimtarget) ) {
+ $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->csimtarget."\"";
+ if( !empty($this->csimalt) ) {
+ $tmp=sprintf($this->csimalt,$this->yvalue,$this->xvalue);
+ $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimareas .= ">\n";
+ }
+ }
+
+ function AddCSIMCircle($x,$y,$r) {
+ $x = round($x); $y=round($y); $r=round($r);
+ $this->csimareas="";
+ if( !empty($this->csimtarget) ) {
+ $this->csimareas .= "<area shape=\"circle\" coords=\"$x,$y,$r\" href=\"".$this->csimtarget."\"";
+ if( !empty($this->csimalt) ) {
+ $tmp=sprintf($this->csimalt,$this->yvalue,$this->xvalue);
+ $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimareas .= ">\n";
+ }
+ }
+
+ function Stroke($img,$x,$y) {
+ if( !$this->show ) return;
+
+ if( $this->iFormatCallback != '' || $this->iFormatCallback2 != '' ) {
+
+ if( $this->iFormatCallback != '' ) {
+ $f = $this->iFormatCallback;
+ list($width,$color,$fcolor) = call_user_func($f,$this->yvalue);
+ $filename = $this->iFileName;
+ $imgscale = $this->iScale;
+ }
+ else {
+ $f = $this->iFormatCallback2;
+ list($width,$color,$fcolor,$filename,$imgscale) = call_user_func($f,$this->yvalue,$this->xvalue);
+ if( $filename=="" ) $filename = $this->iFileName;
+ if( $imgscale=="" ) $imgscale = $this->iScale;
+ }
+
+ if( $width=="" ) $width = $this->width;
+ if( $color=="" ) $color = $this->color;
+ if( $fcolor=="" ) $fcolor = $this->fill_color;
+
+ }
+ else {
+ $fcolor = $this->fill_color;
+ $color = $this->color;
+ $width = $this->width;
+ $filename = $this->iFileName;
+ $imgscale = $this->iScale;
+ }
+
+ if( $this->type == MARK_IMG ||
+ ($this->type >= MARK_FLAG1 && $this->type <= MARK_FLAG4 ) ||
+ $this->type >= MARK_IMG_PUSHPIN ) {
+
+ // Note: For the builtin images we use the "filename" parameter
+ // to denote the color
+ $anchor_x = 0.5;
+ $anchor_y = 0.5;
+ switch( $this->type ) {
+ case MARK_FLAG1:
+ case MARK_FLAG2:
+ case MARK_FLAG3:
+ case MARK_FLAG4:
+ $this->markimg = FlagCache::GetFlagImgByName($this->type-MARK_FLAG1+1,$filename);
+ break;
+
+ case MARK_IMG :
+ // Load an image and use that as a marker
+ // Small optimization, if we have already read an image don't
+ // waste time reading it again.
+ if( $this->markimg == '' || !($this->oldfilename === $filename) ) {
+ $this->markimg = Graph::LoadBkgImage('',$filename);
+ $this->oldfilename = $filename ;
+ }
+ break;
+
+ case MARK_IMG_PUSHPIN:
+ case MARK_IMG_SPUSHPIN:
+ case MARK_IMG_LPUSHPIN:
+ if( $this->imgdata_pushpins == null ) {
+ require_once 'imgdata_pushpins.inc';
+ $this->imgdata_pushpins = new ImgData_PushPins();
+ }
+ $this->markimg = $this->imgdata_pushpins->GetImg($this->type,$filename);
+ list($anchor_x,$anchor_y) = $this->imgdata_pushpins->GetAnchor();
+ break;
+
+ case MARK_IMG_SQUARE:
+ if( $this->imgdata_squares == null ) {
+ require_once 'imgdata_squares.inc';
+ $this->imgdata_squares = new ImgData_Squares();
+ }
+ $this->markimg = $this->imgdata_squares->GetImg($this->type,$filename);
+ list($anchor_x,$anchor_y) = $this->imgdata_squares->GetAnchor();
+ break;
+
+ case MARK_IMG_STAR:
+ if( $this->imgdata_stars == null ) {
+ require_once 'imgdata_stars.inc';
+ $this->imgdata_stars = new ImgData_Stars();
+ }
+ $this->markimg = $this->imgdata_stars->GetImg($this->type,$filename);
+ list($anchor_x,$anchor_y) = $this->imgdata_stars->GetAnchor();
+ break;
+
+ case MARK_IMG_BEVEL:
+ if( $this->imgdata_bevels == null ) {
+ require_once 'imgdata_bevels.inc';
+ $this->imgdata_bevels = new ImgData_Bevels();
+ }
+ $this->markimg = $this->imgdata_bevels->GetImg($this->type,$filename);
+ list($anchor_x,$anchor_y) = $this->imgdata_bevels->GetAnchor();
+ break;
+
+ case MARK_IMG_DIAMOND:
+ if( $this->imgdata_diamonds == null ) {
+ require_once 'imgdata_diamonds.inc';
+ $this->imgdata_diamonds = new ImgData_Diamonds();
+ }
+ $this->markimg = $this->imgdata_diamonds->GetImg($this->type,$filename);
+ list($anchor_x,$anchor_y) = $this->imgdata_diamonds->GetAnchor();
+ break;
+
+ case MARK_IMG_BALL:
+ case MARK_IMG_SBALL:
+ case MARK_IMG_MBALL:
+ case MARK_IMG_LBALL:
+ if( $this->imgdata_balls == null ) {
+ require_once 'imgdata_balls.inc';
+ $this->imgdata_balls = new ImgData_Balls();
+ }
+ $this->markimg = $this->imgdata_balls->GetImg($this->type,$filename);
+ list($anchor_x,$anchor_y) = $this->imgdata_balls->GetAnchor();
+ break;
+ }
+
+ $w = $img->GetWidth($this->markimg);
+ $h = $img->GetHeight($this->markimg);
+
+ $dw = round($imgscale * $w );
+ $dh = round($imgscale * $h );
+
+ $dx = round($x-$dw*$anchor_x);
+ $dy = round($y-$dh*$anchor_y);
+
+ $this->width = max($dx,$dy);
+
+ $cp = $GLOBALS['copyfunc'] ;
+ $cp($img->img,$this->markimg,$dx,$dy,0,0,$dw,$dh,$w,$h);
+ if( !empty($this->csimtarget) ) {
+ $this->csimareas = "<area shape=\"rect\" coords=\"".
+ $dx.','.$dy.','.round($dx+$dw).','.round($dy+$dh).'" '.
+ "href=\"".$this->csimtarget."\"";
+ if( !empty($this->csimalt) ) {
+ $tmp=sprintf($this->csimalt,$this->yvalue,$this->xvalue);
+ $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimareas .= ">\n";
+ }
+
+ // Stroke title
+ $this->title->Align("center","top");
+ $this->title->Stroke($img,$x,$y+round($dh/2));
+ return;
+ }
+
+ $weight = $this->weight;
+ $dx=round($width/2,0);
+ $dy=round($width/2,0);
+ $pts=0;
+
+ switch( $this->type ) {
+ case MARK_SQUARE:
+ $c[]=$x-$dx;$c[]=$y-$dy;
+ $c[]=$x+$dx;$c[]=$y-$dy;
+ $c[]=$x+$dx;$c[]=$y+$dy;
+ $c[]=$x-$dx;$c[]=$y+$dy;
+ $c[]=$x-$dx;$c[]=$y-$dy;
+ $pts=5;
+ break;
+ case MARK_UTRIANGLE:
+ ++$dx;++$dy;
+ $c[]=$x-$dx;$c[]=$y+0.87*$dy; // tan(60)/2*$dx
+ $c[]=$x;$c[]=$y-0.87*$dy;
+ $c[]=$x+$dx;$c[]=$y+0.87*$dy;
+ $c[]=$x-$dx;$c[]=$y+0.87*$dy; // tan(60)/2*$dx
+ $pts=4;
+ break;
+ case MARK_DTRIANGLE:
+ ++$dx;++$dy;
+ $c[]=$x;$c[]=$y+0.87*$dy; // tan(60)/2*$dx
+ $c[]=$x-$dx;$c[]=$y-0.87*$dy;
+ $c[]=$x+$dx;$c[]=$y-0.87*$dy;
+ $c[]=$x;$c[]=$y+0.87*$dy; // tan(60)/2*$dx
+ $pts=4;
+ break;
+ case MARK_DIAMOND:
+ $c[]=$x;$c[]=$y+$dy;
+ $c[]=$x-$dx;$c[]=$y;
+ $c[]=$x;$c[]=$y-$dy;
+ $c[]=$x+$dx;$c[]=$y;
+ $c[]=$x;$c[]=$y+$dy;
+ $pts=5;
+ break;
+ case MARK_LEFTTRIANGLE:
+ $c[]=$x;$c[]=$y;
+ $c[]=$x;$c[]=$y+2*$dy;
+ $c[]=$x+$dx*2;$c[]=$y;
+ $c[]=$x;$c[]=$y;
+ $pts=4;
+ break;
+ case MARK_RIGHTTRIANGLE:
+ $c[]=$x-$dx*2;$c[]=$y;
+ $c[]=$x;$c[]=$y+2*$dy;
+ $c[]=$x;$c[]=$y;
+ $c[]=$x-$dx*2;$c[]=$y;
+ $pts=4;
+ break;
+ case MARK_FLASH:
+ $dy *= 2;
+ $c[]=$x+$dx/2; $c[]=$y-$dy;
+ $c[]=$x-$dx+$dx/2; $c[]=$y+$dy*0.7-$dy;
+ $c[]=$x+$dx/2; $c[]=$y+$dy*1.3-$dy;
+ $c[]=$x-$dx+$dx/2; $c[]=$y+2*$dy-$dy;
+ $img->SetLineWeight($weight);
+ $img->SetColor($color);
+ $img->Polygon($c);
+ $img->SetLineWeight(1);
+ $this->AddCSIMPoly($c);
+ break;
+ }
+
+ if( $pts>0 ) {
+ $this->AddCSIMPoly($c);
+ $img->SetLineWeight($weight);
+ $img->SetColor($fcolor);
+ $img->FilledPolygon($c);
+ $img->SetColor($color);
+ $img->Polygon($c);
+ $img->SetLineWeight(1);
+ }
+ elseif( $this->type==MARK_CIRCLE ) {
+ $img->SetColor($color);
+ $img->Circle($x,$y,$width);
+ $this->AddCSIMCircle($x,$y,$width);
+ }
+ elseif( $this->type==MARK_FILLEDCIRCLE ) {
+ $img->SetColor($fcolor);
+ $img->FilledCircle($x,$y,$width);
+ $img->SetColor($color);
+ $img->Circle($x,$y,$width);
+ $this->AddCSIMCircle($x,$y,$width);
+ }
+ elseif( $this->type==MARK_CROSS ) {
+ // Oversize by a pixel to match the X
+ $img->SetColor($color);
+ $img->SetLineWeight($weight);
+ $img->Line($x,$y+$dy+1,$x,$y-$dy-1);
+ $img->Line($x-$dx-1,$y,$x+$dx+1,$y);
+ $this->AddCSIMCircle($x,$y,$dx);
+ }
+ elseif( $this->type==MARK_X ) {
+ $img->SetColor($color);
+ $img->SetLineWeight($weight);
+ $img->Line($x+$dx,$y+$dy,$x-$dx,$y-$dy);
+ $img->Line($x-$dx,$y+$dy,$x+$dx,$y-$dy);
+ $this->AddCSIMCircle($x,$y,$dx+$dy);
+ }
+ elseif( $this->type==MARK_STAR ) {
+ $img->SetColor($color);
+ $img->SetLineWeight($weight);
+ $img->Line($x+$dx,$y+$dy,$x-$dx,$y-$dy);
+ $img->Line($x-$dx,$y+$dy,$x+$dx,$y-$dy);
+ // Oversize by a pixel to match the X
+ $img->Line($x,$y+$dy+1,$x,$y-$dy-1);
+ $img->Line($x-$dx-1,$y,$x+$dx+1,$y);
+ $this->AddCSIMCircle($x,$y,$dx+$dy);
+ }
+
+ // Stroke title
+ $this->title->Align("center","center");
+ $this->title->Stroke($img,$x,$y);
+ }
+} // Class
+
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_POLAR.PHP
+// Description: Polar plot extension for JpGraph
+// Created: 2003-02-02
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_polar.php,v 1.2.2.1 2003/08/07 19:31:34 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+*/
+require_once "jpgraph_log.php";
+
+
+DEFINE('POLAR_360',1);
+DEFINE('POLAR_180',2);
+
+//
+// Note. Don't attempt to make sense of this code.
+// In order not to have to be able to inherit the scaling code
+// from the main graph package we have had to make some "tricks" since
+// the original scaling and axis was not designed to do what is
+// required here.
+// There were two option. 1: Re-implement everything and get a clean design
+// and 2: do some "small" trickery and be able to inherit most of
+// the functionlity from the main graph package.
+// We choose 2: here in order to save some time.
+//
+
+//--------------------------------------------------------------------------
+// class PolarPlot
+//--------------------------------------------------------------------------
+class PolarPlot {
+ var $numpoints=0;
+ var $iColor='navy',$iFillColor='';
+ var $iLineWeight=1;
+ var $coord=null;
+ var $legendcsimtarget='';
+ var $legendcsimalt='';
+ var $legend="";
+ var $csimtargets=array(); // Array of targets for CSIM
+ var $csimareas=""; // Resultant CSIM area tags
+ var $csimalts=null; // ALT:s for corresponding target
+ var $line_style='solid',$mark;
+
+ function PolarPlot($aData) {
+ $n = count($aData);
+ if( $n & 1 ) {
+ JpGraphError::Raise('Polar plots must have an even number of data point. Each data point is a tuple (angle,radius).');
+ }
+ $this->numpoints = $n/2;
+ $this->coord = $aData;
+ $this->mark = new PlotMark();
+ }
+
+ function SetWeight($aWeight) {
+ $this->iLineWeight = $aWeight;
+ }
+
+ function SetColor($aColor){
+ $this->iColor = $aColor;
+ }
+
+ function SetFillColor($aColor){
+ $this->iFillColor = $aColor;
+ }
+
+ function Max() {
+ $m = $this->coord[1];
+ $i=1;
+ while( $i < $this->numpoints ) {
+ $m = max($m,$this->coord[2*$i+1]);
+ ++$i;
+ }
+ return $m;
+ }
+ // Set href targets for CSIM
+ function SetCSIMTargets($aTargets,$aAlts=null) {
+ $this->csimtargets=$aTargets;
+ $this->csimalts=$aAlts;
+ }
+
+ // Get all created areas
+ function GetCSIMareas() {
+ return $this->csimareas;
+ }
+
+ function SetLegend($aLegend,$aCSIM="",$aCSIMAlt="") {
+ $this->legend = $aLegend;
+ $this->legendcsimtarget = $aCSIM;
+ $this->legendcsimalt = $aCSIMAlt;
+ }
+
+ // Private methods
+
+ function Legend(&$aGraph) {
+ $color = $this->iColor ;
+ if( $this->legend != "" ) {
+ if( $this->iFillColor!='' ) {
+ $color = $this->iFillColor;
+ $aGraph->legend->Add($this->legend,$color,$this->mark,0,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ }
+ else {
+ $aGraph->legend->Add($this->legend,$color,$this->mark,$this->line_style,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ }
+ }
+ }
+
+ function Stroke($img,$scale) {
+
+ $i=0;
+ $p=array();
+ $this->csimareas='';
+ while($i < $this->numpoints) {
+ list($x1,$y1) = $scale->PTranslate($this->coord[2*$i],$this->coord[2*$i+1]);
+ $p[2*$i] = $x1;
+ $p[2*$i+1] = $y1;
+
+ if( isset($this->csimtargets[$i]) ) {
+ $this->mark->SetCSIMTarget($this->csimtargets[$i]);
+ $this->mark->SetCSIMAlt($this->csimalts[$i]);
+ $this->mark->SetCSIMAltVal($this->coord[2*$i], $this->coord[2*$i+1]);
+ $this->mark->Stroke($img,$x1,$y1);
+ $this->csimareas .= $this->mark->GetCSIMAreas();
+ }
+ else
+ $this->mark->Stroke($img,$x1,$y1);
+
+ ++$i;
+ }
+
+ if( $this->iFillColor != '' ) {
+ $img->SetColor($this->iFillColor);
+ $img->FilledPolygon($p);
+ }
+ $img->SetLineWeight($this->iLineWeight);
+ $img->SetColor($this->iColor);
+ $img->Polygon($p,$this->iFillColor!='');
+ }
+}
+
+//--------------------------------------------------------------------------
+// class PolarAxis
+//--------------------------------------------------------------------------
+class PolarAxis extends Axis {
+ var $angle_step=15,$angle_color='lightgray',$angle_label_color='black';
+ var $angle_fontfam=FF_FONT1,$angle_fontstyle=FS_NORMAL,$angle_fontsize=10;
+ var $angle_fontcolor = 'navy';
+ var $gridminor_color='lightgray',$gridmajor_color='lightgray';
+ var $show_minor_grid = false, $show_major_grid = true ;
+ var $show_angle_mark=true, $show_angle_grid=true, $show_angle_label=true;
+ var $angle_tick_len=3, $angle_tick_len2=3, $angle_tick_color='black';
+ var $show_angle_tick=true;
+ var $radius_tick_color='black';
+
+ function PolarAxis(&$img,&$aScale) {
+ parent::Axis($img,$aScale);
+ }
+
+ function ShowAngleDegreeMark($aFlg=true) {
+ $this->show_angle_mark = $aFlg;
+ }
+
+ function SetAngleStep($aStep) {
+ $this->angle_step=$aStep;
+ }
+
+ function HideTicks($aFlg=true,$aAngleFlg=true) {
+ parent::HideTicks($aFlg,$aFlg);
+ $this->show_angle_tick = !$aAngleFlg;
+ }
+
+ function ShowAngleLabel($aFlg=true) {
+ $this->show_angle_label = $aFlg;
+ }
+
+ function ShowGrid($aMajor=true,$aMinor=false,$aAngle=true) {
+ $this->show_minor_grid = $aMinor;
+ $this->show_major_grid = $aMajor;
+ $this->show_angle_grid = $aAngle ;
+ }
+
+ function SetAngleFont($aFontFam,$aFontStyle=FS_NORMAL,$aFontSize=10) {
+ $this->angle_fontfam = $aFontFam;
+ $this->angle_fontstyle = $aFontStyle;
+ $this->angle_fontsize = $aFontSize;
+ }
+
+ function SetColor($aColor,$aRadColor='',$aAngleColor='') {
+ if( $aAngleColor == '' )
+ $aAngleColor=$aColor;
+ parent::SetColor($aColor,$aRadColor);
+ $this->angle_fontcolor = $aAngleColor;
+ }
+
+ function SetGridColor($aMajorColor,$aMinorColor='',$aAngleColor='') {
+ if( $aMinorColor == '' )
+ $aMinorColor = $aMajorColor;
+ if( $aAngleColor == '' )
+ $aAngleColor = $aMajorColor;
+
+ $this->gridminor_color = $aMinorColor;
+ $this->gridmajor_color = $aMajorColor;
+ $this->angle_color = $aAngleColor;
+ }
+
+ function SetTickColors($aRadColor,$aAngleColor='') {
+ $this->radius_tick_color = $aRadColor;
+ $this->angle_tick_color = $aAngleColor;
+ }
+
+ // Private methods
+ function StrokeGrid($pos) {
+ $x = round($this->img->left_margin + $this->img->plotwidth/2);
+ $this->scale->ticks->Stroke($this->img,$this->scale,$pos);
+
+ // Stroke the minor arcs
+ $pmin = array();
+ $p = $this->scale->ticks->ticks_pos;
+ $n = count($p);
+ $i = 0;
+ $this->img->SetColor($this->gridminor_color);
+ while( $i < $n ) {
+ $r = $p[$i]-$x+1;
+ $pmin[]=$r;
+ if( $this->show_minor_grid ) {
+ $this->img->Circle($x,$pos,$r);
+ }
+ $i++;
+ }
+
+ $limit = max($this->img->plotwidth,$this->img->plotheight)*1.4 ;
+ while( $r < $limit ) {
+ $off = $r;
+ $i=1;
+ $r = $off + round($p[$i]-$x+1);
+ while( $r < $limit && $i < $n ) {
+ $r = $off+$p[$i]-$x;
+ $pmin[]=$r;
+ if( $this->show_minor_grid ) {
+ $this->img->Circle($x,$pos,$r);
+ }
+ $i++;
+ }
+ }
+
+ // Stroke the major arcs
+ if( $this->show_major_grid ) {
+ // First determine how many minor step on
+ // every major step. We have recorded the minor radius
+ // in pmin and use these values. This is done in order
+ // to avoid rounding errors if we were to recalculate the
+ // different major radius.
+ $pmaj = $this->scale->ticks->maj_ticks_pos;
+ $p = $this->scale->ticks->ticks_pos;
+ if( $this->scale->name == 'lin' ) {
+ $step=round(($pmaj[1] - $pmaj[0])/($p[1] - $p[0]));
+ }
+ else {
+ $step=9;
+ }
+ $n = round(count($pmin)/$step);
+ $i = 0;
+ $this->img->SetColor($this->gridmajor_color);
+ $limit = max($this->img->plotwidth,$this->img->plotheight)*1.4 ;
+ $off = $r;
+ $i=0;
+ $r = $pmin[$i*$step];
+ while( $r < $limit && $i < $n ) {
+ $r = $pmin[$i*$step];
+ $this->img->Circle($x,$pos,$r);
+ $i++;
+ }
+ }
+
+ // Draw angles
+ if( $this->show_angle_grid ) {
+ $this->img->SetColor($this->angle_color);
+ $d = max($this->img->plotheight,$this->img->plotwidth)*1.4 ;
+ $a = 0;
+ $p = $this->scale->ticks->ticks_pos;
+ $start_radius = $p[1]-$x;
+ while( $a < 360 ) {
+ if( $a == 90 || $a == 270 ) {
+ // Make sure there are no rounding problem with
+ // exactly vertical lines
+ $this->img->Line($x+$start_radius*cos($a/180*M_PI)+1,
+ $pos-$start_radius*sin($a/180*M_PI),
+ $x+$start_radius*cos($a/180*M_PI)+1,
+ $pos-$d*sin($a/180*M_PI));
+
+ }
+ else {
+ $this->img->Line($x+$start_radius*cos($a/180*M_PI)+1,
+ $pos-$start_radius*sin($a/180*M_PI),
+ $x+$d*cos($a/180*M_PI),
+ $pos-$d*sin($a/180*M_PI));
+ }
+ $a += $this->angle_step;
+ }
+ }
+ }
+
+ function StrokeAngleLabels($pos,$type) {
+
+ if( !$this->show_angle_label )
+ return;
+
+ $x0 = round($this->img->left_margin+$this->img->plotwidth/2)+1;
+
+ $d = max($this->img->plotwidth,$this->img->plotheight)*1.42;
+ $a = $this->angle_step;
+ $t = new Text();
+ $t->SetColor($this->angle_fontcolor);
+ $t->SetFont($this->angle_fontfam,$this->angle_fontstyle,$this->angle_fontsize);
+ $xright = $this->img->width - $this->img->right_margin;
+ $ytop = $this->img->top_margin;
+ $xleft = $this->img->left_margin;
+ $ybottom = $this->img->height - $this->img->bottom_margin;
+ $ha = 'left';
+ $va = 'center';
+ $w = $this->img->plotwidth/2;
+ $h = $this->img->plotheight/2;
+ $xt = $x0; $yt = $pos;
+ $margin=5;
+
+ $tl = $this->angle_tick_len ; // Outer len
+ $tl2 = $this->angle_tick_len2 ; // Interior len
+
+ $this->img->SetColor($this->angle_tick_color);
+ $rot90 = $this->img->a == 90 ;
+
+ if( $type == POLAR_360 ) {
+ $ca1 = atan($h/$w)/M_PI*180;
+ $ca2 = 180-$ca1;
+ $ca3 = $ca1+180;
+ $ca4 = 360-$ca1;
+ $end = 360;
+ while( $a < $end ) {
+ $ca = cos($a/180*M_PI);
+ $sa = sin($a/180*M_PI);
+ $x = $d*$ca;
+ $y = $d*$sa;
+ $xt=1000;$yt=1000;
+ if( $a <= $ca1 || $a >= $ca4 ) {
+ $yt = $pos - $w * $y/$x;
+ $xt = $xright + $margin;
+ if( $rot90 ) {
+ $ha = 'center';
+ $va = 'top';
+ }
+ else {
+ $ha = 'left';
+ $va = 'center';
+ }
+ $x1=$xright-$tl2; $x2=$xright+$tl;
+ $y1=$y2=$yt;
+ }
+ elseif( $a > $ca1 && $a < $ca2 ) {
+ $xt = $x0 + $h * $x/$y;
+ $yt = $ytop - $margin;
+ if( $rot90 ) {
+ $ha = 'left';
+ $va = 'center';
+ }
+ else {
+ $ha = 'center';
+ $va = 'bottom';
+ }
+ $y1=$ytop+$tl2;$y2=$ytop-$tl;
+ $x1=$x2=$xt;
+ }
+ elseif( $a >= $ca2 && $a <= $ca3 ) {
+ $yt = $pos + $w * $y/$x;
+ $xt = $xleft - $margin;
+ if( $rot90 ) {
+ $ha = 'center';
+ $va = 'bottom';
+ }
+ else {
+ $ha = 'right';
+ $va = 'center';
+ }
+ $x1=$xleft+$tl2;$x2=$xleft-$tl;
+ $y1=$y2=$yt;
+ }
+ else {
+ $xt = $x0 - $h * $x/$y;
+ $yt = $ybottom + $margin;
+ if( $rot90 ) {
+ $ha = 'right';
+ $va = 'center';
+ }
+ else {
+ $ha = 'center';
+ $va = 'top';
+ }
+ $y1=$ybottom-$tl2;$y2=$ybottom+$tl;
+ $x1=$x2=$xt;
+ }
+ if( $a != 0 && $a != 180 ) {
+ $t->Align($ha,$va);
+ if( $this->show_angle_mark )
+ $a .= '°';
+ $t->Set($a);
+ $t->Stroke($this->img,$xt,$yt);
+ if( $this->show_angle_tick )
+ $this->img->Line($x1,$y1,$x2,$y2);
+ }
+ $a += $this->angle_step;
+ }
+ }
+ else {
+ // POLAR_HALF
+ $ca1 = atan($h/$w*2)/M_PI*180;
+ $ca2 = 180-$ca1;
+ $end = 180;
+ while( $a < $end ) {
+ $ca = cos($a/180*M_PI);
+ $sa = sin($a/180*M_PI);
+ $x = $d*$ca;
+ $y = $d*$sa;
+ if( $a <= $ca1 ) {
+ $yt = $pos - $w * $y/$x;
+ $xt = $xright + $margin;
+ if( $rot90 ) {
+ $ha = 'center';
+ $va = 'top';
+ }
+ else {
+ $ha = 'left';
+ $va = 'center';
+ }
+ $x1=$xright-$tl2; $x2=$xright+$tl;
+ $y1=$y2=$yt;
+ }
+ elseif( $a > $ca1 && $a < $ca2 ) {
+ $xt = $x0 + 2*$h * $x/$y;
+ $yt = $ytop - $margin;
+ if( $rot90 ) {
+ $ha = 'left';
+ $va = 'center';
+ }
+ else {
+ $ha = 'center';
+ $va = 'bottom';
+ }
+ $y1=$ytop+$tl2;$y2=$ytop-$tl;
+ $x1=$x2=$xt;
+ }
+ elseif( $a >= $ca2 ) {
+ $yt = $pos + $w * $y/$x;
+ $xt = $xleft - $margin;
+ if( $rot90 ) {
+ $ha = 'center';
+ $va = 'bottom';
+ }
+ else {
+ $ha = 'right';
+ $va = 'center';
+ }
+ $x1=$xleft+$tl2;$x2=$xleft-$tl;
+ $y1=$y2=$yt;
+ }
+ $t->Align($ha,$va);
+ if( $this->show_angle_mark )
+ $a .= '°';
+ $t->Set($a);
+ $t->Stroke($this->img,$xt,$yt);
+ if( $this->show_angle_tick )
+ $this->img->Line($x1,$y1,$x2,$y2);
+ $a += $this->angle_step;
+ }
+ }
+ }
+
+ function Stroke($pos) {
+
+ $this->img->SetLineWeight($this->weight);
+ $this->img->SetColor($this->color);
+ $this->img->SetFont($this->font_family,$this->font_style,$this->font_size);
+ if( !$this->hide_line )
+ $this->img->FilledRectangle($this->img->left_margin,$pos,
+ $this->img->width-$this->img->right_margin,$pos+$this->weight-1);
+ $y=$pos+$this->img->GetFontHeight()+$this->title_margin+$this->title->margin;
+ if( $this->title_adjust=="high" )
+ $this->title->Pos($this->img->width-$this->img->right_margin,$y,"right","top");
+ elseif( $this->title_adjust=="middle" || $this->title_adjust=="center" )
+ $this->title->Pos(($this->img->width-$this->img->left_margin-
+ $this->img->right_margin)/2+$this->img->left_margin,
+ $y,"center","top");
+ elseif($this->title_adjust=="low")
+ $this->title->Pos($this->img->left_margin,$y,"left","top");
+ else {
+ JpGraphError::Raise('Unknown alignment specified for X-axis title. ('.
+ $this->title_adjust.')');
+ }
+
+ $this->StrokeLabels($pos,false);
+ $this->img->SetColor($this->radius_tick_color);
+ $this->scale->ticks->Stroke($this->img,$this->scale,$pos);
+
+ //
+ // Mirror the positions for the left side of the scale
+ //
+ $mid = 2*($this->img->left_margin+$this->img->plotwidth/2);
+ $n = count($this->scale->ticks->ticks_pos);
+ $i=0;
+ while( $i < $n ) {
+ $this->scale->ticks->ticks_pos[$i] =
+ $mid-$this->scale->ticks->ticks_pos[$i] ;
+ ++$i;
+ }
+
+ $n = count($this->scale->ticks->maj_ticks_pos);
+ $i=0;
+ while( $i < $n ) {
+ $this->scale->ticks->maj_ticks_pos[$i] =
+ $mid-$this->scale->ticks->maj_ticks_pos[$i] ;
+ ++$i;
+ }
+
+ $n = count($this->scale->ticks->maj_ticklabels_pos);
+ $i=1;
+ while( $i < $n ) {
+ $this->scale->ticks->maj_ticklabels_pos[$i] =
+ $mid-$this->scale->ticks->maj_ticklabels_pos[$i] ;
+ ++$i;
+ }
+
+ // Draw the left side of the scale
+ $n = count($this->scale->ticks->ticks_pos);
+ $yu = $pos - $this->scale->ticks->direction*$this->scale->ticks->GetMinTickAbsSize();
+
+
+ // Minor ticks
+ $i=1;
+ while( $i < $n/2 ) {
+ $x = round($this->scale->ticks->ticks_pos[$i]) ;
+ $this->img->Line($x,$pos,$x,$yu);
+ ++$i;
+ }
+
+ $n = count($this->scale->ticks->maj_ticks_pos);
+ $yu = $pos - $this->scale->ticks->direction*$this->scale->ticks->GetMajTickAbsSize();
+
+
+ // Major ticks
+ $i=1;
+ while( $i < $n/2 ) {
+ $x = round($this->scale->ticks->maj_ticks_pos[$i]) ;
+ $this->img->Line($x,$pos,$x,$yu);
+ ++$i;
+ }
+
+ $this->StrokeLabels($pos,false);
+ $this->title->Stroke($this->img);
+ }
+}
+
+class PolarScale extends LinearScale {
+ var $graph;
+ function PolarScale($aMax=0,&$graph) {
+ parent::LinearScale(0,$aMax,'x');
+ $this->graph = &$graph;
+ }
+
+ function _Translate($v) {
+ return parent::Translate($v);
+ }
+
+ function PTranslate($aAngle,$aRad) {
+
+ $m = $this->scale[1];
+ $w = $this->graph->img->plotwidth/2;
+ $aRad = $aRad/$m*$w;
+
+ $x = cos( $aAngle/180 * M_PI ) * $aRad;
+ $y = sin( $aAngle/180 * M_PI ) * $aRad;
+
+ $x += $this->_Translate(0);
+
+ if( $this->graph->iType == POLAR_360 ) {
+ $y = ($this->graph->img->top_margin + $this->graph->img->plotheight/2) - $y;
+ }
+ else {
+ $y = ($this->graph->img->top_margin + $this->graph->img->plotheight) - $y;
+ }
+ return array($x,$y);
+ }
+}
+
+class PolarLogScale extends LogScale {
+ var $graph;
+ function PolarLogScale($aMax=1,&$graph) {
+ parent::LogScale(0,$aMax,'x');
+ $this->graph = &$graph;
+ $this->ticks->SetLabelLogType(LOGLABELS_MAGNITUDE);
+
+ }
+
+ function PTranslate($aAngle,$aRad) {
+
+ if( $aRad == 0 )
+ $aRad = 1;
+ $aRad = log10($aRad);
+ $m = $this->scale[1];
+ $w = $this->graph->img->plotwidth/2;
+ $aRad = $aRad/$m*$w;
+
+ $x = cos( $aAngle/180 * M_PI ) * $aRad;
+ $y = sin( $aAngle/180 * M_PI ) * $aRad;
+
+ $x += $w+$this->graph->img->left_margin;//$this->_Translate(0);
+ if( $this->graph->iType == POLAR_360 ) {
+ $y = ($this->graph->img->top_margin + $this->graph->img->plotheight/2) - $y;
+ }
+ else {
+ $y = ($this->graph->img->top_margin + $this->graph->img->plotheight) - $y;
+ }
+ return array($x,$y);
+ }
+}
+
+class PolarGraph extends Graph {
+ var $scale;
+ var $iType=POLAR_360;
+ var $axis;
+
+ function PolarGraph($aWidth=300,$aHeight=200,$aCachedName="",$aTimeOut=0,$aInline=true) {
+ parent::Graph($aWidth,$aHeight,$aCachedName,$aTimeOut,$aInline) ;
+ $this->SetDensity(TICKD_DENSE);
+ $this->SetBox();
+ $this->SetMarginColor('white');
+ }
+
+ function SetDensity($aDense) {
+ $this->SetTickDensity(TICKD_NORMAL,$aDense);
+ }
+
+ function Set90AndMargin($lm=0,$rm=0,$tm=0,$bm=0) {
+ $adj = ($this->img->height - $this->img->width)/2;
+ $this->SetAngle(90);
+ $this->img->SetMargin($lm-$adj,$rm-$adj,$tm+$adj,$bm+$adj);
+ $this->img->SetCenter(floor($this->img->width/2),floor($this->img->height/2));
+ $this->axis->SetLabelAlign('right','center');
+ //JpGraphError::Raise('Set90AndMargin() is not supported for polar graphs.');
+ }
+
+ function SetScale($aScale,$rmax=0) {
+ if( $aScale == 'lin' )
+ $this->scale = new PolarScale($rmax,$this);
+ elseif( $aScale == 'log' ) {
+ $this->scale = new PolarLogScale($rmax,$this);
+ }
+ else {
+ JpGraphError::Raise('Unknown scale type for polar graph. Must be "lin" or "log"');
+ }
+
+ $this->scale->Init($this->img);
+ $this->axis = new PolarAxis($this->img,$this->scale);
+ $this->SetMargin(40,40,50,40);
+ }
+
+ function SetType($aType) {
+ $this->iType = $aType;
+ }
+
+ function SetPlotSize($w,$h) {
+ $this->SetMargin(($this->img->width-$w)/2,($this->img->width-$w)/2,
+ ($this->img->height-$h)/2,($this->img->height-$h)/2);
+ }
+
+ // Private methods
+ function GetPlotsMax() {
+ $n = count($this->plots);
+ $m = $this->plots[0]->Max();
+ $i=1;
+ while($i < $n) {
+ $m = max($this->plots[$i]->Max());
+ ++$i;
+ }
+ return $m;
+ }
+
+ function Stroke($aStrokeFileName="") {
+
+ // Start by adjusting the margin so that potential titles will fit.
+ $this->AdjustMarginsForTitles();
+
+ // If the filename is the predefined value = '_csim_special_'
+ // we assume that the call to stroke only needs to do enough
+ // to correctly generate the CSIM maps.
+ // We use this variable to skip things we don't strictly need
+ // to do to generate the image map to improve performance
+ // a best we can. Therefor you will see a lot of tests !$_csim in the
+ // code below.
+ $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
+
+ // We need to know if we have stroked the plot in the
+ // GetCSIMareas. Otherwise the CSIM hasn't been generated
+ // and in the case of GetCSIM called before stroke to generate
+ // CSIM without storing an image to disk GetCSIM must call Stroke.
+ $this->iHasStroked = true;
+
+ //Check if we should autoscale axis
+ if( !$this->scale->IsSpecified() && count($this->plots)>0 ) {
+ $max = $this->GetPlotsMax();
+ $t1 = $this->img->plotwidth;
+ $this->img->plotwidth /= 2;
+ $t2 = $this->img->left_margin;
+ $this->img->left_margin += $this->img->plotwidth+1;
+ $this->scale->AutoScale($this->img,0,$max,
+ $this->img->plotwidth/$this->xtick_factor/2);
+ $this->img->plotwidth = $t1;
+ $this->img->left_margin = $t2;
+ }
+ else {
+ // The tick calculation will use the user suplied min/max values to determine
+ // the ticks. If auto_ticks is false the exact user specifed min and max
+ // values will be used for the scale.
+ // If auto_ticks is true then the scale might be slightly adjusted
+ // so that the min and max values falls on an even major step.
+ //$min = 0;
+ $max = $this->scale->scale[1];
+ $t1 = $this->img->plotwidth;
+ $this->img->plotwidth /= 2;
+ $t2 = $this->img->left_margin;
+ $this->img->left_margin += $this->img->plotwidth+1;
+ $this->scale->AutoScale($this->img,0,$max,
+ $this->img->plotwidth/$this->xtick_factor/2);
+ $this->img->plotwidth = $t1;
+ $this->img->left_margin = $t2;
+ }
+
+ if( $this->iType == POLAR_180 )
+ $pos = $this->img->height - $this->img->bottom_margin;
+ else
+ $pos = $this->img->plotheight/2 + $this->img->top_margin;
+
+
+ if( !$_csim ) {
+ $this->StrokePlotArea();
+ }
+
+ $this->iDoClipping = true;
+
+ if( $this->iDoClipping ) {
+ $oldimage = $this->img->CloneCanvasH();
+ }
+
+ if( !$_csim ) {
+ $this->axis->StrokeGrid($pos);
+ }
+
+ // Stroke all plots for Y1 axis
+ for($i=0; $i < count($this->plots); ++$i) {
+ $this->plots[$i]->Stroke($this->img,$this->scale);
+ }
+
+
+ if( $this->iDoClipping ) {
+ // Clipping only supports graphs at 0 and 90 degrees
+ if( $this->img->a == 0 ) {
+ $this->img->CopyCanvasH($oldimage,$this->img->img,
+ $this->img->left_margin,$this->img->top_margin,
+ $this->img->left_margin,$this->img->top_margin,
+ $this->img->plotwidth+1,$this->img->plotheight+1);
+ }
+ elseif( $this->img->a == 90 ) {
+ $adj = round(($this->img->height - $this->img->width)/2);
+ $this->img->CopyCanvasH($oldimage,$this->img->img,
+ $this->img->bottom_margin-$adj,$this->img->left_margin+$adj,
+ $this->img->bottom_margin-$adj,$this->img->left_margin+$adj,
+ $this->img->plotheight,$this->img->plotwidth);
+ }
+ $this->img->Destroy();
+ $this->img->SetCanvasH($oldimage);
+ }
+
+ if( !$_csim ) {
+ $this->axis->Stroke($pos);
+ $this->axis->StrokeAngleLabels($pos,$this->iType);
+ }
+
+ if( !$_csim ) {
+ $this->StrokePlotBox();
+ $this->footer->Stroke($this->img);
+
+ // The titles and legends never gets rotated so make sure
+ // that the angle is 0 before stroking them
+ $aa = $this->img->SetAngle(0);
+ $this->StrokeTitles();
+ }
+
+ for($i=0; $i < count($this->plots) ; ++$i ) {
+ $this->plots[$i]->Legend($this);
+ }
+
+ $this->legend->Stroke($this->img);
+
+ if( !$_csim ) {
+
+ $this->StrokeTexts();
+ $this->img->SetAngle($aa);
+
+ // Draw an outline around the image map
+ if(JPG_DEBUG)
+ $this->DisplayClientSideaImageMapAreas();
+
+ // Adjust the appearance of the image
+ $this->AdjustSaturationBrightnessContrast();
+
+ // If the filename is given as the special "__handle"
+ // then the image handler is returned and the image is NOT
+ // streamed back
+ if( $aStrokeFileName == _IMG_HANDLER ) {
+ return $this->img->img;
+ }
+ else {
+ // Finally stream the generated picture
+ $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
+ $aStrokeFileName);
+ }
+ }
+ }
+}
+
+
+
+?>
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_RADAR.PHP
+// Description: Radar plot extension for JpGraph
+// Created: 2001-02-04
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_radar.php,v 1.7 2003/03/20 19:57:22 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+
+class RadarLogTicks extends Ticks {
+//---------------
+// CONSTRUCTOR
+ function RadarLogTicks() {
+ }
+//---------------
+// PUBLIC METHODS
+
+ // TODO: Add Argument grid
+ function Stroke(&$aImg,&$grid,$aPos,$aAxisAngle,&$aScale,&$aMajPos,&$aMajLabel) {
+ $start = $aScale->GetMinVal();
+ $limit = $aScale->GetMaxVal();
+ $nextMajor = 10*$start;
+ $step = $nextMajor / 10.0;
+ $count=1;
+
+ $ticklen_maj=5;
+ $dx_maj=round(sin($aAxisAngle)*$ticklen_maj);
+ $dy_maj=round(cos($aAxisAngle)*$ticklen_maj);
+ $ticklen_min=3;
+ $dx_min=round(sin($aAxisAngle)*$ticklen_min);
+ $dy_min=round(cos($aAxisAngle)*$ticklen_min);
+
+ $aMajPos=array();
+ $aMajLabel=array();
+
+ if( $this->supress_first )
+ $aMajLabel[]="";
+ else
+ $aMajLabel[]=$start;
+ $yr=$aScale->RelTranslate($start);
+ $xt=round($yr*cos($aAxisAngle))+$aScale->scale_abs[0];
+ $yt=$aPos-round($yr*sin($aAxisAngle));
+ $aMajPos[]=$xt+2*$dx_maj;
+ $aMajPos[]=$yt-$aImg->GetFontheight()/2;
+ $grid[]=$xt;
+ $grid[]=$yt;
+
+ $aImg->SetLineWeight($this->weight);
+
+ for($y=$start; $y<=$limit; $y+=$step,++$count ) {
+ $yr=$aScale->RelTranslate($y);
+ $xt=round($yr*cos($aAxisAngle))+$aScale->scale_abs[0];
+ $yt=$aPos-round($yr*sin($aAxisAngle));
+ if( $count % 10 == 0 ) {
+ $grid[]=$xt;
+ $grid[]=$yt;
+ $aMajPos[]=$xt+2*$dx_maj;
+ $aMajPos[]=$yt-$aImg->GetFontheight()/2;
+ if( !$this->supress_tickmarks ) {
+ if( $this->majcolor!="" ) $aImg->PushColor($this->majcolor);
+ $aImg->Line($xt+$dx_maj,$yt+$dy_maj,$xt-$dx_maj,$yt-$dy_maj);
+ if( $this->majcolor!="" ) $aImg->PopColor();
+ }
+ $aMajLabel[]=$nextMajor;
+ $nextMajor *= 10;
+ $step *= 10;
+ $count=1;
+ }
+ else
+ if( !$this->supress_minor_tickmarks ) {
+ if( $this->mincolor!="" ) $aImg->PushColor($this->mincolor);
+ $aImg->Line($xt+$dx_min,$yt+$dy_min,$xt-$dx_min,$yt-$dy_min);
+ if( $this->mincolor!="" ) $aImg->PopColor();
+ }
+ }
+ }
+}
+
+class RadarLinearTicks extends LinearTicks {
+//---------------
+// CONSTRUCTOR
+ function RadarLinearTicks() {
+ // Empty
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ // TODO: Add argument grid
+ function Stroke(&$aImg,&$grid,$aPos,$aAxisAngle,&$aScale,&$aMajPos,&$aMajLabel) {
+ // Prepare to draw linear ticks
+ $maj_step_abs = abs($aScale->scale_factor*$this->major_step);
+ $min_step_abs = abs($aScale->scale_factor*$this->minor_step);
+ $nbrmaj = floor(($aScale->world_abs_size)/$maj_step_abs);
+ $nbrmin = floor(($aScale->world_abs_size)/$min_step_abs);
+ $skip = round($nbrmin/$nbrmaj); // Don't draw minor ontop of major
+
+ // Draw major ticks
+ $ticklen2=$this->major_abs_size;
+ $dx=round(sin($aAxisAngle)*$ticklen2);
+ $dy=round(cos($aAxisAngle)*$ticklen2);
+ $label=$aScale->scale[0]+$this->major_step;
+
+ $aImg->SetLineWeight($this->weight);
+
+ for($i=1; $i<=$nbrmaj; ++$i) {
+ $xt=round($i*$maj_step_abs*cos($aAxisAngle))+$aScale->scale_abs[0];
+ $yt=$aPos-round($i*$maj_step_abs*sin($aAxisAngle));
+ $aMajLabel[]=$label;
+ $label += $this->major_step;
+ $grid[]=$xt;
+ $grid[]=$yt;
+ $aMajPos[($i-1)*2]=$xt+2*$dx;
+ $aMajPos[($i-1)*2+1]=$yt-$aImg->GetFontheight()/2;
+ if( !$this->supress_tickmarks ) {
+ if( $this->majcolor!="" ) $aImg->PushColor($this->majcolor);
+ $aImg->Line($xt+$dx,$yt+$dy,$xt-$dx,$yt-$dy);
+ if( $this->majcolor!="" ) $aImg->PopColor();
+ }
+ }
+
+ // Draw minor ticks
+ $ticklen2=$this->minor_abs_size;
+ $dx=round(sin($aAxisAngle)*$ticklen2);
+ $dy=round(cos($aAxisAngle)*$ticklen2);
+ if( !$this->supress_tickmarks && !$this->supress_minor_tickmarks) {
+ if( $this->mincolor!="" ) $aImg->PushColor($this->mincolor);
+ for($i=1; $i<=$nbrmin; ++$i) {
+ if( ($i % $skip) == 0 ) continue;
+ $xt=round($i*$min_step_abs*cos($aAxisAngle))+$aScale->scale_abs[0];
+ $yt=$aPos-round($i*$min_step_abs*sin($aAxisAngle));
+ $aImg->Line($xt+$dx,$yt+$dy,$xt-$dx,$yt-$dy);
+ }
+ if( $this->mincolor!="" ) $aImg->PopColor();
+ }
+ }
+}
+
+
+
+//===================================================
+// CLASS RadarAxis
+// Description: Implements axis for the spider graph
+//===================================================
+class RadarAxis extends Axis {
+ var $title_color="navy";
+ var $title=null;
+//---------------
+// CONSTRUCTOR
+ function RadarAxis(&$img,&$aScale,$color=array(0,0,0)) {
+ parent::Axis($img,$aScale,$color);
+ $this->len=$img->plotheight;
+ $this->title = new Text();
+ $this->title->SetFont(FF_FONT1,FS_BOLD);
+ $this->color = array(0,0,0);
+ }
+//---------------
+// PUBLIC METHODS
+ function SetTickLabels($l) {
+ $this->ticks_label = $l;
+ }
+
+
+ // Stroke the axis
+ // $pos = Vertical position of axis
+ // $aAxisAngle = Axis angle
+ // $grid = Returns an array with positions used to draw the grid
+ // $lf = Label flag, TRUE if the axis should have labels
+ function Stroke($pos,$aAxisAngle,&$grid,$title,$lf) {
+ $this->img->SetColor($this->color);
+
+ // Determine end points for the axis
+ $x=round($this->scale->world_abs_size*cos($aAxisAngle)+$this->scale->scale_abs[0]);
+ $y=round($pos-$this->scale->world_abs_size*sin($aAxisAngle));
+
+ // Draw axis
+ $this->img->SetColor($this->color);
+ $this->img->SetLineWeight($this->weight);
+ if( !$this->hide )
+ $this->img->Line($this->scale->scale_abs[0],$pos,$x,$y);
+
+ $this->scale->ticks->Stroke($this->img,$grid,$pos,$aAxisAngle,$this->scale,$majpos,$majlabel);
+
+ // Draw labels
+ if( $lf && !$this->hide ) {
+ $this->img->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $this->img->SetTextAlign("left","top");
+ $this->img->SetColor($this->color);
+
+ // majpos contsins (x,y) coordinates for labels
+ for($i=0; $i<count($majpos)/2; ++$i) {
+ if( $this->ticks_label != null )
+ $this->img->StrokeText($majpos[$i*2],$majpos[$i*2+1],$this->ticks_label[$i]);
+ else
+ $this->img->StrokeText($majpos[$i*2],$majpos[$i*2+1],$majlabel[$i]);
+ }
+ }
+ $this->_StrokeAxisTitle($pos,$aAxisAngle,$title);
+ }
+//---------------
+// PRIVATE METHODS
+
+ function _StrokeAxisTitle($pos,$aAxisAngle,$title) {
+ $this->title->Set($title);
+ $marg=6+$this->title->margin;
+ $xt=round(($this->scale->world_abs_size+$marg)*cos($aAxisAngle)+$this->scale->scale_abs[0]);
+ $yt=round($pos-($this->scale->world_abs_size+$marg)*sin($aAxisAngle));
+
+ // Position the axis title.
+ // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
+ // that intersects with the extension of the corresponding axis. The code looks a little
+ // bit messy but this is really the only way of having a reasonable position of the
+ // axis titles.
+ $h=$this->img->GetFontHeight();
+ $w=$this->img->GetTextWidth($title);
+ while( $aAxisAngle > 2*M_PI ) $aAxisAngle -= 2*M_PI;
+ if( $aAxisAngle>=7*M_PI/4 || $aAxisAngle <= M_PI/4 ) $dx=0;
+ if( $aAxisAngle>=M_PI/4 && $aAxisAngle <= 3*M_PI/4 ) $dx=($aAxisAngle-M_PI/4)*2/M_PI;
+ if( $aAxisAngle>=3*M_PI/4 && $aAxisAngle <= 5*M_PI/4 ) $dx=1;
+ if( $aAxisAngle>=5*M_PI/4 && $aAxisAngle <= 7*M_PI/4 ) $dx=(1-($aAxisAngle-M_PI*5/4)*2/M_PI);
+
+ if( $aAxisAngle>=7*M_PI/4 ) $dy=(($aAxisAngle-M_PI)-3*M_PI/4)*2/M_PI;
+ if( $aAxisAngle<=M_PI/4 ) $dy=(1-$aAxisAngle*2/M_PI);
+ if( $aAxisAngle>=M_PI/4 && $aAxisAngle <= 3*M_PI/4 ) $dy=1;
+ if( $aAxisAngle>=3*M_PI/4 && $aAxisAngle <= 5*M_PI/4 ) $dy=(1-($aAxisAngle-3*M_PI/4)*2/M_PI);
+ if( $aAxisAngle>=5*M_PI/4 && $aAxisAngle <= 7*M_PI/4 ) $dy=0;
+
+ if( !$this->hide )
+ $this->title->Stroke($this->img,$xt-$dx*$w,$yt-$dy*$h,$title);
+ }
+
+
+} // Class
+
+
+//===================================================
+// CLASS RadarGrid
+// Description: Draws grid for the spider graph
+//===================================================
+class RadarGrid extends Grid {
+//------------
+// CONSTRUCTOR
+ function RadarGrid() {
+ }
+
+//----------------
+// PRIVATE METHODS
+ function Stroke(&$img,&$grid) {
+ if( !$this->show ) return;
+ $nbrticks = count($grid[0])/2;
+ $nbrpnts = count($grid);
+ $img->SetColor($this->grid_color);
+ $img->SetLineWeight($this->weight);
+ for($i=0; $i<$nbrticks; ++$i) {
+ for($j=0; $j<$nbrpnts; ++$j) {
+ $pnts[$j*2]=$grid[$j][$i*2];
+ $pnts[$j*2+1]=$grid[$j][$i*2+1];
+ }
+ for($k=0; $k<$nbrpnts; ++$k ){
+ $l=($k+1)%$nbrpnts;
+ if( $this->type == "solid" )
+ $img->Line($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1]);
+ elseif( $this->type == "dotted" )
+ $img->DashedLine($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1],1,6);
+ elseif( $this->type == "dashed" )
+ $img->DashedLine($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1],2,4);
+ elseif( $this->type == "longdashed" )
+ $img->DashedLine($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1],8,6);
+ }
+ $pnts=array();
+ }
+ }
+} // Class
+
+
+//===================================================
+// CLASS RadarPlot
+// Description: Plot a spiderplot
+//===================================================
+class RadarPlot {
+ var $data=array();
+ var $fill=false, $fill_color=array(200,170,180);
+ var $color=array(0,0,0);
+ var $legend="";
+ var $weight=1;
+//---------------
+// CONSTRUCTOR
+ function RadarPlot($data) {
+ $this->data = $data;
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Min() {
+ return Min($this->data);
+ }
+
+ function Max() {
+ return Max($this->data);
+ }
+
+ function SetLegend($legend) {
+ $this->legend=$legend;
+ }
+
+
+ function SetLineWeight($w) {
+ $this->weight=$w;
+ }
+
+ function SetFillColor($aColor) {
+ $this->fill_color = $aColor;
+ $this->fill = true;
+ }
+
+ function SetFill($f=true) {
+ $this->fill = $f;
+ }
+
+ function SetColor($aColor,$aFillColor=false) {
+ $this->color = $aColor;
+ if( $aFillColor ) {
+ $this->SetFillColor($aFillColor);
+ $this->fill = true;
+ }
+ }
+
+ function GetCSIMareas() {
+ JpGraphError::Raise("Client side image maps not supported for RadarPlots.");
+ }
+
+ function Stroke(&$img, $pos, &$scale, $startangle) {
+ $nbrpnts = count($this->data);
+ $astep=2*M_PI/$nbrpnts;
+ $a=$startangle;
+
+ // Rotate each point to the correct axis-angle
+ // TODO: Update for LogScale
+ for($i=0; $i<$nbrpnts; ++$i) {
+ //$c=$this->data[$i];
+ $cs=$scale->RelTranslate($this->data[$i]);
+ $x=round($cs*cos($a)+$scale->scale_abs[0]);
+ $y=round($pos-$cs*sin($a));
+ /*
+ $c=log10($c);
+ $x=round(($c-$scale->scale[0])*$scale->scale_factor*cos($a)+$scale->scale_abs[0]);
+ $y=round($pos-($c-$scale->scale[0])*$scale->scale_factor*sin($a));
+ */
+ $pnts[$i*2]=$x;
+ $pnts[$i*2+1]=$y;
+ $a += $astep;
+ }
+ if( $this->fill ) {
+ $img->SetColor($this->fill_color);
+ $img->FilledPolygon($pnts);
+ }
+ $img->SetLineWeight($this->weight);
+ $img->SetColor($this->color);
+ $pnts[]=$pnts[0];
+ $pnts[]=$pnts[1];
+ $img->Polygon($pnts);
+ }
+
+//---------------
+// PRIVATE METHODS
+ function GetCount() {
+ return count($this->data);
+ }
+
+ function Legend(&$graph) {
+ if( $this->legend=="" ) return;
+ if( $this->fill )
+ $graph->legend->Add($this->legend,$this->fill_color);
+ else
+ $graph->legend->Add($this->legend,$this->color);
+ }
+
+} // Class
+
+//===================================================
+// CLASS RadarGraph
+// Description: Main container for a spider graph
+//===================================================
+class RadarGraph extends Graph {
+ var $posx;
+ var $posy;
+ var $len;
+ var $plots=null, $axis_title=null;
+ var $grid,$axis=null;
+//---------------
+// CONSTRUCTOR
+ function RadarGraph($width=300,$height=200,$cachedName="",$timeout=0,$inline=1) {
+ $this->Graph($width,$height,$cachedName,$timeout,$inline);
+ $this->posx=$width/2;
+ $this->posy=$height/2;
+ $this->len=min($width,$height)*0.35;
+ $this->SetColor(array(255,255,255));
+ $this->SetTickDensity(TICKD_NORMAL);
+ $this->SetScale("lin");
+ }
+
+//---------------
+// PUBLIC METHODS
+ function SupressTickMarks($f=true) {
+ if( ERR_DEPRECATED )
+ JpGraphError::Raise('RadarGraph::SupressTickMarks() is deprecated. Use HideTickMarks() instead.');
+ $this->axis->scale->ticks->SupressTickMarks($f);
+ }
+
+ function HideTickMarks($aFlag=true) {
+ $this->axis->scale->ticks->SupressTickMarks($aFlag);
+ }
+
+ function ShowMinorTickmarks($aFlag=true) {
+ $this->yscale->ticks->SupressMinorTickMarks(!$aFlag);
+ }
+
+ function SetScale($axtype,$ymin=1,$ymax=1) {
+ if( $axtype != "lin" && $axtype != "log" ) {
+ JpGraphError::Raise("Illegal scale for spiderplot ($axtype). Must be \"lin\" or \"log\"");
+ }
+ if( $axtype=="lin" ) {
+ $this->yscale = & new LinearScale($ymin,$ymax);
+ $this->yscale->ticks = & new RadarLinearTicks();
+ $this->yscale->ticks->SupressMinorTickMarks();
+ }
+ elseif( $axtype=="log" ) {
+ $this->yscale = & new LogScale($ymin,$ymax);
+ $this->yscale->ticks = & new RadarLogTicks();
+ }
+
+ $this->axis = & new RadarAxis($this->img,$this->yscale);
+ $this->grid = & new RadarGrid();
+ }
+
+ function SetSize($aSize) {
+ if( $aSize<0.1 || $aSize>1 )
+ JpGraphError::Raise("Radar Plot size must be between 0.1 and 1. (Your value=$s)");
+ $this->len=min($this->img->width,$this->img->height)*$aSize/2;
+ }
+
+ function SetPlotSize($aSize) {
+ $this->SetSize($aSize);
+ }
+
+ function SetTickDensity($densy=TICKD_NORMAL) {
+ $this->ytick_factor=25;
+ switch( $densy ) {
+ case TICKD_DENSE:
+ $this->ytick_factor=12;
+ break;
+ case TICKD_NORMAL:
+ $this->ytick_factor=25;
+ break;
+ case TICKD_SPARSE:
+ $this->ytick_factor=40;
+ break;
+ case TICKD_VERYSPARSE:
+ $this->ytick_factor=70;
+ break;
+ default:
+ JpGraphError::Raise("RadarPlot Unsupported Tick density: $densy");
+ }
+ }
+
+ function SetPos($px,$py=0.5) {
+ $this->SetCenter($px,$py);
+ }
+
+ function SetCenter($px,$py=0.5) {
+ assert($px > 0 && $py > 0 );
+ $this->posx=$this->img->width*$px;
+ $this->posy=$this->img->height*$py;
+ }
+
+ function SetColor($c) {
+ $this->SetMarginColor($c);
+ }
+
+ function SetTitles($title) {
+ $this->axis_title = $title;
+ }
+
+ function Add(&$splot) {
+ $this->plots[]=$splot;
+ }
+
+ function GetPlotsYMinMax() {
+ $min=$this->plots[0]->Min();
+ $max=$this->plots[0]->Max();
+ foreach( $this->plots as $p ) {
+ $max=max($max,$p->Max());
+ $min=min($min,$p->Min());
+ }
+ if( $min < 0 )
+ JpGraphError::Raise("Minimum data $min (Radar plots only makes sence to use when all data points > 0)");
+ return array($min,$max);
+ }
+
+ // Stroke the Radar graph
+ function Stroke($aStrokeFileName="") {
+ // Set Y-scale
+ if( !$this->yscale->IsSpecified() && count($this->plots)>0 ) {
+ list($min,$max) = $this->GetPlotsYMinMax();
+ $this->yscale->AutoScale($this->img,0,$max,$this->len/$this->ytick_factor);
+ }
+ // Set start position end length of scale (in absolute pixels)
+ $this->yscale->SetConstants($this->posx,$this->len);
+
+ // We need as many axis as there are data points
+ $nbrpnts=$this->plots[0]->GetCount();
+
+ // If we have no titles just number the axis 1,2,3,...
+ if( $this->axis_title==null ) {
+ for($i=0; $i<$nbrpnts; ++$i )
+ $this->axis_title[$i] = $i+1;
+ }
+ elseif(count($this->axis_title)<$nbrpnts)
+ JpGraphError::Raise("Number of titles does not match number of points in plot.");
+ for($i=0; $i<count($this->plots); ++$i )
+ if( $nbrpnts != $this->plots[$i]->GetCount() )
+ JpGraphError::Raise("Each spider plot must have the same number of data points.");
+
+ if( $this->background_image != "" ) {
+ $this->StrokeFrameBackground();
+ }
+ else {
+ $this->StrokeFrame();
+ }
+ $astep=2*M_PI/$nbrpnts;
+
+ // Prepare legends
+ for($i=0; $i<count($this->plots); ++$i)
+ $this->plots[$i]->Legend($this);
+ $this->legend->Stroke($this->img);
+ $this->footer->Stroke($this->img);
+
+ // Plot points
+ $a=M_PI/2;
+ for($i=0; $i<count($this->plots); ++$i )
+ $this->plots[$i]->Stroke($this->img, $this->posy, $this->yscale, $a);
+
+ // Draw axis and grid
+ for( $i=0,$a=M_PI/2; $i<$nbrpnts; ++$i, $a+=$astep ) {
+ $this->axis->Stroke($this->posy,$a,$grid[$i],$this->axis_title[$i],$i==0);
+ }
+ $this->grid->Stroke($this->img,$grid);
+ $this->StrokeTitles();
+
+ // Stroke texts
+ if( $this->texts != null )
+ foreach( $this->texts as $t)
+ $t->Stroke($this->img);
+
+
+ // If the filename is given as the special "__handle"
+ // then the image handler is returned and the image is NOT
+ // streamed back
+ if( $aStrokeFileName == _IMG_HANDLER ) {
+ return $this->img->img;
+ }
+ else {
+ // Finally stream the generated picture
+ $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
+ $aStrokeFileName);
+ }
+ }
+} // Class
+
+/* EOF */
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_REGSTAT.PHP
+// Description: Regression and statistical analysis helper classes
+// Created: 2002-12-01
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_regstat.php,v 1.2 2003/03/08 11:29:21 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2002 Johan Persson
+//========================================================================
+*/
+
+//------------------------------------------------------------------------
+// CLASS Spline
+// Create a new data array from an existing data array but with more points.
+// The new points are interpolated using a cubic spline algorithm
+//------------------------------------------------------------------------
+class Spline {
+ // 3:rd degree polynom approximation
+
+ var $xdata,$ydata; // Data vectors
+ var $y2; // 2:nd derivate of ydata
+ var $n=0;
+
+ function Spline($xdata,$ydata) {
+ $this->y2 = array();
+ $this->xdata = $xdata;
+ $this->ydata = $ydata;
+
+ $n = count($ydata);
+ $this->n = $n;
+
+ // Natural spline 2:derivate == 0 at endpoints
+ $this->y2[0] = 0.0;
+ $this->y2[$n-1] = 0.0;
+ $delta[0] = 0.0;
+
+ // Calculate 2:nd derivate
+ for($i=1; $i < $n-1; ++$i) {
+ $d = ($xdata[$i+1]-$xdata[$i-1]);
+ if( $d == 0 ) {
+ JpGraphError::Raise('Invalid input data for spline. Two or more consecutive input X-values are equal. Each input X-value must differ since from a mathematical point of view it must be a one-to-one mapping, i.e. each X-value must correspond to exactly one Y-value.');
+ }
+ $s = ($xdata[$i]-$xdata[$i-1])/$d;
+ $p = $s*$this->y2[$i-1]+2.0;
+ $this->y2[$i] = ($s-1.0)/$p;
+ $delta[$i] = ($ydata[$i+1]-$ydata[$i])/($xdata[$i+1]-$xdata[$i]) -
+ ($ydata[$i]-$ydata[$i-1])/($xdata[$i]-$xdata[$i-1]);
+ $delta[$i] = (6.0*$delta[$i]/($xdata[$i+1]-$xdata[$i-1])-$s*$delta[$i-1])/$p;
+ }
+
+ // Backward substitution
+ for( $j=$n-2; $j >= 0; --$j ) {
+ $this->y2[$j] = $this->y2[$j]*$this->y2[$j+1] + $delta[$j];
+ }
+ }
+
+ // Return the two new data vectors
+ function Get($num=50) {
+ $n = $this->n ;
+ $step = ($this->xdata[$n-1]-$this->xdata[0]) / ($num-1);
+ $xnew=array();
+ $ynew=array();
+ $xnew[0] = $this->xdata[0];
+ $ynew[0] = $this->ydata[0];
+ for( $j=1; $j < $num; ++$j ) {
+ $xnew[$j] = $xnew[0]+$j*$step;
+ $ynew[$j] = $this->Interpolate($xnew[$j]);
+ }
+ return array($xnew,$ynew);
+ }
+
+ // Return a single interpolated Y-value from an x value
+ function Interpolate($xpoint) {
+
+ $max = $this->n-1;
+ $min = 0;
+
+ // Binary search to find interval
+ while( $max-$min > 1 ) {
+ $k = ($max+$min) / 2;
+ if( $this->xdata[$k] > $xpoint )
+ $max=$k;
+ else
+ $min=$k;
+ }
+
+ // Each interval is interpolated by a 3:degree polynom function
+ $h = $this->xdata[$max]-$this->xdata[$min];
+
+ if( $h == 0 ) {
+ JpGraphError::Raise('Invalid input data for spline. Two or more consecutive input X-values are equal. Each input X-value must differ since from a mathematical point of view it must be a one-to-one mapping, i.e. each X-value must correspond to exactly one Y-value.');
+ }
+
+
+ $a = ($this->xdata[$max]-$xpoint)/$h;
+ $b = ($xpoint-$this->xdata[$min])/$h;
+ return $a*$this->ydata[$min]+$b*$this->ydata[$max]+
+ (($a*$a*$a-$a)*$this->y2[$min]+($b*$b*$b-$b)*$this->y2[$max])*($h*$h)/6.0;
+ }
+}
+
+// EOF
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_SCATTER.PHP
+// Description: Scatter (and impuls) plot extension for JpGraph
+// Created: 2001-02-11
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_scatter.php,v 1.31.2.1 2003/08/15 15:27:40 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+
+//===================================================
+// CLASS FieldArrow
+// Description: Draw an arrow at (x,y) with angle a
+//===================================================
+class FieldArrow {
+ var $iSize=10; // Length in pixels for arrow
+ var $iArrowSize = 2;
+ var $iColor='black';
+ var $isizespec = array(
+ array(2,1),array(3,2),array(4,3),array(6,4),array(7,4),array(8,5),array(10,6),array(12,7),array(16,8),array(20,10));
+ function FieldArrow() {
+ }
+
+ function SetSize($aSize,$aArrowSize=2) {
+ $this->iSize = $aSize;
+ $this->iArrowSize = $aArrowSize;
+ }
+
+ function SetColor($aColor) {
+ $this->iColor = $aColor;
+ }
+
+ function Stroke($aImg,$x,$y,$a) {
+ $old_origin = $aImg->SetCenter($x,$y);
+ $old_a = $aImg->SetAngle(-$a);
+
+ $dx = round($this->iSize/2);
+ $c = array($x-$dx,$y,$x+$dx,$y);
+ $x += $dx;
+
+ list($dx,$dy) = $this->isizespec[$this->iArrowSize];
+ $ca = array($x,$y,$x-$dx,$y-$dy,$x-$dx,$y+$dy,$x,$y);
+
+ $aImg->SetColor($this->iColor);
+ $aImg->Polygon($c);
+ $aImg->FilledPolygon($ca);
+
+ $aImg->SetCenter($old_origin[0],$old_origin[1]);
+ $aImg->SetAngle($old_a);
+ }
+}
+
+//===================================================
+// CLASS FieldPlot
+// Description: Render a field plot
+//===================================================
+class FieldPlot extends Plot {
+ var $iAngles;
+ var $iCallback='';
+ function FieldPlot($datay,$datax,$angles) {
+ if( (count($datax) != count($datay)) )
+ JpGraphError::Raise("Fieldplots must have equal number of X and Y points.");
+ if( (count($datax) != count($angles)) )
+ JpGraphError::Raise("Fieldplots must have an angle specified for each X and Y points.");
+
+ $this->iAngles = $angles;
+
+ $this->Plot($datay,$datax);
+ $this->value->SetAlign('center','center');
+ $this->value->SetMargin(15);
+
+ $this->arrow = new FieldArrow();
+ }
+
+ function SetCallback($aFunc) {
+ $this->iCallback = $aFunc;
+ }
+
+ function Stroke(&$img,&$xscale,&$yscale) {
+
+ // Remeber base color and size
+ $bc = $this->arrow->iColor;
+ $bs = $this->arrow->iSize;
+ $bas = $this->arrow->iArrowSize;
+
+ for( $i=0; $i<$this->numpoints; ++$i ) {
+ // Skip null values
+ if( $this->coords[0][$i]==="" )
+ continue;
+
+ $f = $this->iCallback;
+ if( $f != "" ) {
+ list($cc,$cs,$cas) = call_user_func($f,$this->coords[1][$i],$this->coords[0][$i],$this->iAngles[$i]);
+ // Fall back on global data if the callback isn't set
+ if( $cc == "" ) $cc = $bc;
+ if( $cs == "" ) $cs = $bs;
+ if( $cas == "" ) $cas = $bas;
+ //echo "f=$f, cc=$cc, cs=$cs, cas=$cas<br>";
+ $this->arrow->SetColor($cc);
+ $this->arrow->SetSize($cs,$cas);
+ }
+
+ $xt = $xscale->Translate($this->coords[1][$i]);
+ $yt = $yscale->Translate($this->coords[0][$i]);
+
+ $this->arrow->Stroke($img,$xt,$yt,$this->iAngles[$i]);
+ $this->value->Stroke($img,$this->coords[0][$i],$xt,$yt);
+ }
+ }
+
+ // Framework function
+ function Legend(&$aGraph) {
+ if( $this->legend != "" ) {
+ $aGraph->legend->Add($this->legend,$this->mark->fill_color,$this->mark,0,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ }
+ }
+}
+
+//===================================================
+// CLASS ScatterPlot
+// Description: Render X and Y plots
+//===================================================
+class ScatterPlot extends Plot {
+ var $impuls = false;
+ var $linkpoints = false, $linkpointweight=1, $linkpointcolor="black";
+//---------------
+// CONSTRUCTOR
+ function ScatterPlot($datay,$datax=false) {
+ if( (count($datax) != count($datay)) && is_array($datax))
+ JpGraphError::Raise("Scatterplot must have equal number of X and Y points.");
+ $this->Plot($datay,$datax);
+ $this->mark = new PlotMark();
+ $this->mark->SetType(MARK_SQUARE);
+ $this->mark->SetColor($this->color);
+ $this->value->SetAlign('center','center');
+ $this->value->SetMargin(0);
+ }
+
+//---------------
+// PUBLIC METHODS
+ function SetImpuls($f=true) {
+ $this->impuls = $f;
+ }
+
+ // Combine the scatter plot points with a line
+ function SetLinkPoints($aFlag=true,$aColor="black",$aWeight=1) {
+ $this->linkpoints=$aFlag;
+ $this->linkpointcolor=$aColor;
+ $this->linkpointweight=$aWeight;
+ }
+
+ function Stroke(&$img,&$xscale,&$yscale) {
+
+ $ymin=$yscale->scale_abs[0];
+ if( $yscale->scale[0] < 0 )
+ $yzero=$yscale->Translate(0);
+ else
+ $yzero=$yscale->scale_abs[0];
+
+ $this->csimareas = '';
+ for( $i=0; $i<$this->numpoints; ++$i ) {
+
+ // Skip null values
+ if( $this->coords[0][$i]==="" )
+ continue;
+
+ if( isset($this->coords[1]) )
+ $xt = $xscale->Translate($this->coords[1][$i]);
+ else
+ $xt = $xscale->Translate($i);
+ $yt = $yscale->Translate($this->coords[0][$i]);
+
+
+ if( $this->linkpoints && isset($yt_old) ) {
+ $img->SetColor($this->linkpointcolor);
+ $img->SetLineWeight($this->linkpointweight);
+ $img->Line($xt_old,$yt_old,$xt,$yt);
+ }
+
+ if( $this->impuls ) {
+ $img->SetColor($this->color);
+ $img->SetLineWeight($this->weight);
+ $img->Line($xt,$yzero,$xt,$yt);
+ }
+
+ if( !empty($this->csimtargets[$i]) ) {
+ $this->mark->SetCSIMTarget($this->csimtargets[$i]);
+ $this->mark->SetCSIMAlt($this->csimalts[$i]);
+ }
+
+ if( isset($this->coords[1]) ) {
+ $this->mark->SetCSIMAltVal($this->coords[0][$i],$this->coords[1][$i]);
+ }
+ else {
+ $this->mark->SetCSIMAltVal($this->coords[0][$i],$i);
+ }
+
+ $this->mark->Stroke($img,$xt,$yt);
+
+ $this->csimareas .= $this->mark->GetCSIMAreas();
+ $this->value->Stroke($img,$this->coords[0][$i],$xt,$yt);
+
+ $xt_old = $xt;
+ $yt_old = $yt;
+ }
+ }
+
+ // Framework function
+ function Legend(&$aGraph) {
+ if( $this->legend != "" ) {
+ $aGraph->legend->Add($this->legend,$this->mark->fill_color,$this->mark,0,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ }
+ }
+} // Class
+/* EOF */
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/*=======================================================================
+// File: JPGRAPH_STOCK.PHP
+// Description: Stock plot extension for JpGraph
+// Created: 2003-01-27
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_stock.php,v 1.5 2003/02/10 16:38:04 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+*/
+
+//===================================================
+// CLASS StockPlot
+//===================================================
+class StockPlot extends Plot {
+ var $iWidth=9;
+ var $iEndLines=1;
+ var $iStockColor1='white',$iStockColor2='darkred',$iStockColor3='darkred';
+ var $iTupleSize = 4;
+//---------------
+// CONSTRUCTOR
+ function StockPlot(&$datay,$datax=false) {
+ if( count($datay) % $this->iTupleSize ) {
+ JpGraphError::Raise('Data values for Stock charts must contain an even multiple of '.$this->iTupleSize.' data points.');
+ }
+ $this->Plot($datay,$datax);
+ $this->numpoints /= $this->iTupleSize;
+ }
+//---------------
+// PUBLIC METHODS
+
+ function SetColor($aColor,$aColor1='white',$aColor2='darkred',$aColor3='darkred') {
+ $this->color = $aColor;
+ $this->iStockColor1 = $aColor1;
+ $this->iStockColor2 = $aColor2;
+ $this->iStockColor3 = $aColor3;
+ }
+
+ function SetWidth($aWidth) {
+ // Make sure it's odd
+ $this->iWidth = 2*floor($aWidth/2)+1;
+ }
+
+ function HideEndLines($aHide=true) {
+ $this->iEndLines = !$aHide;
+ }
+
+ // Gets called before any axis are stroked
+ function PreStrokeAdjust(&$graph) {
+ if( $this->center ) {
+ $a=0.5; $b=0.5;
+ $this->numpoints++;
+ } else {
+ $a=0; $b=0;
+ }
+ $graph->xaxis->scale->ticks->SetXLabelOffset($a);
+ $graph->SetTextScaleOff($b);
+ }
+
+ // Method description
+ function Stroke($img,$xscale,$yscale) {
+ $n=$this->numpoints;
+ if( $this->center ) $n--;
+ if( isset($this->coords[1]) ) {
+ if( count($this->coords[1])!=$n )
+ JpGraphError::Raise("Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])." Number of Y-points:$numpoints");
+ else
+ $exist_x = true;
+ }
+ else
+ $exist_x = false;
+
+ if( $exist_x )
+ $xs=$this->coords[1][0];
+ else
+ $xs=0;
+
+ $ts = $this->iTupleSize;
+ $this->csimareas = '';
+ for( $i=0; $i<$n; ++$i) {
+
+ //If value is NULL, then don't draw a bar at all
+ if ($this->coords[0][$i] === null) continue;
+
+ if( $exist_x ) $x=$this->coords[1][$i];
+ else $x=$i;
+ $xt = $xscale->Translate($x);
+
+ $neg = $this->coords[0][$i*4] > $this->coords[0][$i*$ts+1] ;
+ $yopen = $yscale->Translate($this->coords[0][$i*$ts]);
+ $yclose = $yscale->Translate($this->coords[0][$i*$ts+1]);
+ $ymin = $yscale->Translate($this->coords[0][$i*$ts+2]);
+ $ymax = $yscale->Translate($this->coords[0][$i*$ts+3]);
+
+ $dx = floor($this->iWidth/2);
+ $xl = $xt - $dx;
+ $xr = $xt + $dx;
+
+ if( $neg )
+ $img->SetColor($this->iStockColor3);
+ else
+ $img->SetColor($this->iStockColor1);
+ $img->FilledRectangle($xl,$yopen,$xr,$yclose);
+ $img->SetLineWeight($this->weight);
+ if( $neg )
+ $img->SetColor($this->iStockColor2);
+ else
+ $img->SetColor($this->color);
+
+ $img->Rectangle($xl,$yopen,$xr,$yclose);
+
+ if( $yopen < $yclose ) {
+ $ytop = $yopen ;
+ $ybottom = $yclose ;
+ }
+ else {
+ $ytop = $yclose ;
+ $ybottom = $yopen ;
+ }
+ $img->SetColor($this->color);
+ $img->Line($xt,$ytop,$xt,$ymax);
+ $img->Line($xt,$ybottom,$xt,$ymin);
+
+ if( $this->iEndLines ) {
+ $img->Line($xl,$ymax,$xr,$ymax);
+ $img->Line($xl,$ymin,$xr,$ymin);
+ }
+
+ // A chance for subclasses to add things to the bar
+ // for data point i
+ $this->ModBox($img,$xscale,$yscale,$i,$xl,$xr,$neg);
+
+ // Setup image maps
+ if( !empty($this->csimtargets[$i]) ) {
+ $this->csimareas.= '<area shape="rect" coords="'.
+ round($xl).','.round($ytop).','.
+ round($xr).','.round($ybottom).'" ';
+ $this->csimareas .= ' href="'.$this->csimtargets[$i].'"';
+ if( !empty($this->csimalts[$i]) ) {
+ $sval=$this->csimalts[$i];
+ $this->csimareas .= " alt=\"$sval\" title=\"$sval\" ";
+ }
+ $this->csimareas.= ">\n";
+ }
+ }
+ return true;
+ }
+
+ // A hook for subclasses to modify the plot
+ function ModBox($img,$xscale,$yscale,$i,$xl,$xr,$neg) {}
+
+} // Class
+
+//===================================================
+// CLASS BoxPlot
+//===================================================
+class BoxPlot extends StockPlot {
+ var $iPColor='black',$iNColor='white';
+ function BoxPlot($datay,$datax=false) {
+ $this->iTupleSize=5;
+ parent::StockPlot($datay,$datax);
+ }
+
+ function SetMedianColor($aPos,$aNeg) {
+ $this->iPColor = $aPos;
+ $this->iNColor = $aNeg;
+ }
+
+ function ModBox($img,$xscale,$yscale,$i,$xl,$xr,$neg) {
+ if( $neg )
+ $img->SetColor($this->iNColor);
+ else
+ $img->SetColor($this->iPColor);
+
+ $y = $yscale->Translate($this->coords[0][$i*5+4]);
+ $img->Line($xl,$y,$xr,$y);
+ }
+}
+
+/* EOF */
+?>
\ No newline at end of file
--- /dev/null
+Ver: $Id: Readme,v 1.7 2003/05/30 20:06:06 aditus Exp $
+
+Utility directory with a few _COMPLETELY_ UNSUPPORTED utilities.
+They are only made available due to some requests.
+
+Summary
+=======
+Most of the file in this directory form the basis of the JpGraph
+internal documentation system as used to generate the class reference.
+It works very well for us in our environment but it might completely
+fail in your setup.
+
+Directory ./misc
+----------------
+
+adjimg.php
+ Lets you adjust the contrast, brightness and saturation
+ in a image.
+ Usage:adjimg.php?file=name&[b=value][&c=value][&s=scale][&sat=saturation]
+
+gencolorchart.php
+ Generates a color chart with all colors available in
+ JpGraph
+
+mkgrad.php
+ A tool to easily generate gradient images for use as backgounds
+
+imgdbschema.inc
+ A set of utility classes used to easy generate DB schema reading the
+ data straight from the DB.
+
+
+Directory ./jpdocgen
+--------------------
+This directory contains all the scripts that make up the DDDA
+documentation architecture that is used to generate the database
+based documentation architecture used for JpGraph. This is completely
+generic and could be used for any PHP project which needs to combine
+both automtic and manual documentation.
+
+The main entry point is jpdocedit.php
+
+
+jpdb.php
+ An OO interface to a MySQL database.
+
+jplintphp.php
+ A very rudimentary "lint" and parser for PHP which parses
+ a valid PHP file and extracts funtions, variables and classes.
+ It also checks for unused class variables and missing
+ '$this->' Used as a base library for extracting class and method
+ information in jpgendb.php. This contains generic classes which
+ are meant to be overridden.
+
+jplintdriver.php
+ A simple driver for the parser in jplintphp.php you invoke
+ this with a URL variable 'target' which is set to the file
+ you would like to parse. This could be considered as a test
+ harness for jplintphp.php
+
+jpgendoc.php
+ A "fancy" version of jplintdriver.php which applies some
+ formatting to generate a nice template to do reference
+ documentation for a PHP script file. Again, this could be
+ considered a proof of concept of the basic design of the
+ jplintphp.php
+
+jpgendb.php
+ Read one or several PHP file and add classes and methods
+ to database. The classes to populate the DB from source
+ are normally instantiated from jpgendoc.php and this
+ script is not meant to be called directly.
+
+jpdbdelclass.php
+ Safely delete a class from the database. Some extra safety
+ features built in to avoid greaf.
+
+jpclassref.php
+ Basic framework to read information from DB and generate
+ indexed documentation. This is a framework which needs
+ a "formatter" plugin which does the actual formatting
+ of the data, see jpgenhtmldoc.php for a simple implementation
+ of a HTML formatter plugin.
+
+jpgenhtmldoc.php
+ Basic formatter plugin + driver to generate a HTML based
+ documentation from the information stored in the DB. The
+ generation of documentation is normally initialted from
+ jpdocgen.php
+
+de_utils.php
+ A number of utility classes which are used throughout the
+ DDDA architecture.
+
+jpd_editclass.php
+ Main form to edit DB data for a class
+
+jpd_editmethod.php
+ Main form to edit DB data for methods
+
+jpd_editproject.php
+ Main form to edit project meta data
+
+jpdocedit.php
+ Main entry point for the DDDA architecture. Opens the main
+ meny and lets the user work and update different projects as
+ well as presenting statictics on the various projects.
+
+<EOF>
+
+
+
+
--- /dev/null
+$Id: README,v 1.4 2002/08/06 08:42:19 aditus Exp $
+
+DDDA Public Release
+===================
+
+This directory contains all the files necessary to
+run the DDDA (Database Driven Documentation Architecture).
+
+1. Introduction
+---------------
+The DDDA system lets you parse a project which may consist
+of multiple PHP source file and record all classes, methods
+and global functions in a database.
+
+When this is done you can add comments and examples which
+is also stored in the database. All this is done through a
+GUI provided in this application.
+
+Whenever the source is changed you just update the DB
+and the new methods and classes will be added while keeping
+any old comments and description.
+
+It will automatically calculate a percentage figure for
+each class and method that indicates how well it is documented.
+
+When you are happy with the result you can automatically
+generate reference documentation from this DB.
+
+2. Installation
+---------------
+1. Copy all the files to a directory of your choice.
+2. Edit 'de_utils.php' to match your MySQL DB server
+settings (user and pwd)
+3. Point your browser to jpdocedit.php. The first time
+this script is run it will setup a few DB tables.
+
+3. Usage
+--------
+Most of the use should be self explaining.
+
+The basic usage cycle (after the initial setup) is:
+
+1. Parse files after code changes
+2. Add documentation to classes and methods
+3. Generate documentation
+
+Go back to 1.
+
+In the opening project window you have options to
+setup and edit one or several projects.
+
+The first thing you must do is to create a project
+and define a number of files belonging to this project.
+After that you need to update the DB by first going
+into the project view and then pressing the 'Update DB'
+button. Note that the update function is smart enough
+not to parse files which are up to date.
+(By clicking the 'Force' checkbox you can force all files
+in the project to be parsed)
+
+Then click on the methods or class names where you
+want to add documentation. To help keep track of your
+documentation effort there is a weighted tracking system
+built in which will give you, as a percentage, the
+documentation coverage. This will help direct your efforts
+to the classes most in need of more documentation.
+
+When you like to generate the documentation you just need
+to press the "Generate docs" button and the HTML files
+will be written to the output directory you have specified.
+
+Some notes:
+The button "Check DB" performs a consistency check on
+the DB.
+The button "Show documentation" opens the generated
+documentations.
+The button "Update DB" runs the parser of any files that
+has been modified since the last time parsing was done.
+
+
+4. Browser compatibility
+------------------------
+I have only tested this with Opera 6.x and at the moment
+it will not work with MS IE due to (what I consider) bugs
+in IE.
+
+When I get some time I will add some workarounds for the
+bugs in IE.
+
+Appendix
+A. Release history
+----------------------------------------------------------
+Date Version Comment
+----------------------------------------------------------
+15 July 2002 1.3 Added global project directory
+4 July 2002 1.2 First public release
+
+[EOF]
+
+
--- /dev/null
+<?php
+/*=======================================================================
+// File: DBSCHEMA_DDDA.PHP
+// Description: Draw a DB schema of the DDDA architecture
+// Created: 2002-08-25
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: dbschema_ddda.php,v 1.1 2002/08/25 22:05:18 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+// Note: The actual drawing of the tables are semi-automatically
+// but you can easily adjust the individual tables position
+// with the 'tblposadj' array.
+//
+//========================================================================
+*/
+include "../../jpgraph.php";
+include "../../jpgraph_canvas.php";
+include "../../jpgraph_canvtools.php";
+include "../misc/imgdbschema.inc";
+include "jpdb.php";
+
+
+// Global callback to format the table header names
+function FormatTblName($aName) {
+ // We want to replace any specifi references to the
+ // 'JpGraph' project with the generic '<project>'
+ return str_replace('JpGraph','<project>', $aName);
+}
+
+// Global callback to format each field name in the table
+function FormatFldName($aName,$aTable) {
+ return $aName;
+}
+
+
+class Driver {
+
+ var $ig, $img, $iscale, $ishape;
+ var $iymax,$ixmax;
+ var $iwidth,$iheight;
+
+ function Driver() {
+
+ // Define Image size and coordinate grid space to work within
+ $this->iwidth = 600;
+ $this->iheight= 750;
+ $this->iymax = 50;
+ $this->ixmax = 55;
+
+ // Setup a basic canvas
+ $this->ig = new CanvasGraph($this->iwidth,$this->iheight);
+ $this->img = $this->ig->img;
+
+ // Define the scale to be used
+ $this->iscale = new CanvasScale($this->ig);
+ $this->iscale->Set(0,$this->ixmax,0,$this->iymax);
+ $this->ishape = new Shape($this->ig,$this->iscale);
+
+ // A small frame around the canvas
+ $this->ig->SetMargin(2,3,2,3);
+ $this->ig->SetMarginColor("teal");
+ $this->ig->InitFrame();
+
+ }
+
+ function Run() {
+
+ $leftm=1.5; // Left margin (for table schemes)
+ $topm=5; // Top margin (for table schemes)
+ $tblwidth=15; // Individual table width
+ $tlo=1; // Offset for top line
+
+ // Add the background color for the project specific tables
+ $this->ishape->IndentedRectangle($leftm,$topm-1,3*$tblwidth+$tlo+6,45,
+ $tlo+2*$tblwidth+2,30,CORNER_BOTTOMLEFT,
+ 'lightblue');
+
+ // Stroke the tables (series of x,y offsets, If =-1 then use the
+ // automtic positioning
+ $tblposadj=array($tlo,0,$tblwidth+$tlo+2,0,2*$tblwidth+$tlo+4,
+ 0,-1,16,-1,16);
+ $dbschema = new ImgDBSchema('jpgraph_doc','FormatTblName','FormatFldName');
+ $dbschema->SetMargin($leftm,$topm);
+ $dbschema->SetTableWidth($tblwidth);
+ $dbschema->Stroke($this->img,$this->iscale,$tblposadj);
+
+ $tt = new CanvasRectangleText();
+ $tt->SetFillColor('');
+ $tt->SetColor('');
+ $tt->SetFontColor('navy');
+
+ // Add explanation
+ $tt->SetFont(FF_ARIAL,FS_NORMAL,12);
+ $tt->Set('Project specific tables',$tblwidth+$leftm+3,16,15);
+ $tt->Stroke($this->img,$this->iscale);
+
+ // Add title
+ $tt->SetColor('');
+ $tt->SetFont(FF_VERDANA,FS_BOLD,26);
+ $tt->Set('DDDA - DB Schema',9,0.5,30);
+ $tt->Stroke($this->img,$this->iscale);
+
+ // Add a version and date
+ $tt->SetFillColor('yellow');
+ $tt->SetFont(FF_FONT1,FS_NORMAL,10);
+ $tt->Set("Generated: ".date("ymd H:m",time()),1,$this->iymax*0.96,15);
+ $tt->Stroke($this->img,$this->iscale);
+
+ $this->ig->Stroke();
+ }
+}
+
+$driver = new Driver();
+$driver->Run();
+
+?>
+
--- /dev/null
+<?php
+//=======================================================================
+// File: DDDA_CHKDB.PHP
+// Description: Check database integrity for project
+// Created: 2002-07-01
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: ddda_chkdb.php,v 1.4 2002/07/03 23:31:01 aditus Exp $
+//
+// License: QPL 1.0
+// Copyright (C) 2002 Johan Persson
+//=======================================================================
+
+include "jpdb.php";
+include "de_utils.php";
+
+//--------------------------------------------------
+// Consistency check of database for project.
+class CheckProjectDB {
+ var $iDB;
+ var $iMethods=array();
+ var $iMethodsbyclass=array();
+ var $iMethodsbyidx=array();
+ var $iErrCnt=0;
+ var $iClasses=array();
+ var $iClassesbyname=array();
+ var $iClassesbyidx=array();
+ var $iProject='';
+ var $nc=0, $nm=0;
+
+ function CheckProjectDB($aDB) {
+ $this->iDB = $aDB;
+ }
+
+ function ErrorReport($aStr) {
+ ++$this->iErrCnt;
+ echo '<b><font color=red>Error '.$this->iErrCnt.':</font></b> '.$aStr."<br>\n\r";
+ }
+
+ function ReadMethods() {
+ // Read all methods
+ $q = 'SELECT fld_key, fld_name, fld_classname, fld_classidx, fld_methref1, fld_methref2, fld_methref3, fld_methref4, fld_methref5 FROM tbl_method_'.$this->iProject.' ORDER BY fld_classname, fld_name ';
+ $res=$this->iDB->Query($q);
+ $nm = $res->NumRows();
+ if( $nm==0 ) {
+ $this->ErrorReport('No methods in method table for project: '.$project);
+ die();
+ }
+ for( $i=0; $i<$nm; ++$i) {
+ $row = $res->Fetch();
+ $this->iMethods[$i]=$row;
+ $this->iMethodsbyclass[$row['fld_classname']][] = $row;
+ $idx = $row['fld_key'];
+ if( isset($this->iMethodsbyidx[$idx]) ) {
+ $this->ErrorReport('Duplicate key : '.$idx.' in method table.');
+ }
+ else $this->iMethodsbyidx[$idx] = $row['fld_name'];
+ }
+
+ return $nm;
+ }
+
+ function ReadClasses() {
+ // Read all Classes
+ $q = 'SELECT fld_key,fld_name,fld_parentname,fld_numfuncs FROM tbl_class_'.$this->iProject.' ORDER BY fld_key, fld_name ';
+ $res=$this->iDB->Query($q);
+ $nc = $res->NumRows();
+ if( $nc==0 ) {
+ $this->ErrorReport('No $this->iClasses for project: '.$project);
+ die();
+ }
+ for( $i=0; $i<$nc; ++$i) {
+ $this->iClasses[$i]=$res->Fetch();
+
+ $name = $this->iClasses[$i]['fld_name'];
+ if( isset($this->iClassesbyname[$name]) ) {
+ $this->ErrorReport('Duplicate class name $name: '.$name);
+ die();
+ }
+ $this->iClassesbyname[$name] = $this->iClasses[$i]['fld_key'];
+
+ $idx = $this->iClasses[$i]['fld_key'];
+ if( isset($this->iClassesbyidx[$idx]) ) {
+ $this->ErrorReport('Duplicate class idx: '.$idx);
+ die();
+ }
+ $this->iClassesbyidx[$idx] = $this->iClasses[$i]['fld_name'];
+ }
+
+ return $nc;
+ }
+
+ function ChkMethods() {
+ // Check methods for inconsistency
+ for( $i=0; $i<$this->nm; ++$i) {
+
+ // Check for duplicates
+ if( $i<$this->nm-1 && $this->iMethods[$i][1] == $this->iMethods[$i+1][1] && $this->iMethods[$i][2] == $this->iMethods[$i+1][2] ) {
+ $this->ErrorReport('Duplicate entry for method <b>'.$this->iMethods[$i][1].
+ '</b> in class <b>'.$this->iMethods[$i+1][2].'</b> with keys '.$this->iMethods[$i][0].' and '.$this->iMethods[$i+1][0]);
+ }
+
+ // Check that the existing references point to valid methods
+ for( $j=1; $j<=5; ++$j ) {
+ $ref = @$this->iMethods[$i]['fld_methref'.$j];
+ if( !empty($ref) ) {
+ if( $ref > -1 && empty($this->iMethodsbyidx[$ref]) ) {
+ $this->ErrorReport('Method <b>'.$this->iMethods[$i]['fld_classname'].'::'.$this->iMethods[$i]['fld_name'].'</b> has invalid reference fld_methref'.$j.'='.$ref);
+ }
+ }
+ }
+
+
+ // Check that class idx exists
+ $idx = $this->iMethods[$i][3];
+ if( $idx != 0 && empty($this->iClassesbyidx[$idx]) ) {
+ $this->ErrorReport('Classidx <b>'.$this->iMethods[$i][3].'</b> in method <b>'.$this->iMethods[$i][1].'</b> does not exist in class table.');
+ }
+
+ // Check that class name exists
+ if( $idx != 0 && empty($this->iClassesbyname[$this->iMethods[$i][2]]) ) {
+ $this->ErrorReport('Classname <b>'.$this->iMethods[$i][2].'</b> in method <b>'.$this->iMethods[$i][1].'</b> does not exist in class table.');
+ }
+
+ }
+ }
+
+ function ChkClasses() {
+ // Check classes for inconsistency
+ for( $i=0; $i<$this->nc; ++$i) {
+
+ // Check that parent class exist if specified
+ $parent = $this->iClasses[$i]['fld_parentname'];
+ if( !empty($parent) ) {
+ if( empty($this->iClassesbyname[$parent]) ) {
+ $this->ErrorReport('Parent class for <b>'.$this->iClasses[$i]['fld_name'].'</b> named <i><b>'.$parent.'</b></i> does not exist.');
+ }
+ }
+
+ // Check that this class have some methods
+ if( empty($this->iMethodsbyclass[$this->iClasses[$i]['fld_name']]) ) {
+ $this->ErrorReport('Class <b>'.$this->iClasses[$i]['fld_name'].'</b> does not have any methods ');
+ }
+ else {
+ // Check that number of methods matchup
+ $tmp = count($this->iMethodsbyclass[$this->iClasses[$i]['fld_name']]);
+ if( $tmp != $this->iClasses[$i]['fld_numfuncs'] ) {
+ $this->ErrorReport('Method count for Class <b>'.$this->iClasses[$i]['fld_name'].'</b> does not match. Count=<b>'.$this->iClasses[$i]['fld_numfuncs'].'</b> vs actual <b>'.$tmp.'</b>' );
+ }
+ }
+ }
+ }
+
+
+ function Run($aProject) {
+ $this->iProject = $aProject;
+ $timer = new JpgTimer();
+
+ echo "<h3>DB Consistency check for project: <font color=blue>$aProject</font></h3><hr>";
+ $timer->Push();
+
+ //echo HTMLGenerator::CloseWinButton();
+
+ echo "<i>This will verify the integrity of the keys in the database as well as foreign key references. ";
+ echo "It will also perform various consistency check within the methods and classes.</i>";
+
+ echo '<hr>';
+
+ echo "<h4>Reading project information ... </h4>\n";flush();
+ $this->nm = $this->ReadMethods();
+ $this->nc = $this->ReadClasses();
+
+ echo "Classes: $this->nc,   Methods: $this->nm <br>";
+
+ echo "<h4>Checking methods ... </h4>\n";flush();
+ $this->ChkMethods();
+
+ echo "<h4>Checking classes ... </h4>\n";flush();
+ $this->ChkClasses();
+
+ $t = round($timer->Pop()/1000,2);
+ $t = "$t".'s';
+
+ if( $this->iErrCnt > 0 )
+ $color='red';
+ else
+ $color='blue';
+ echo " <p> <font color=$color><b>Found ".$this->iErrCnt." errors.</b></font>";
+
+ echo '<hr> ';
+ echo "\n<table width=100%><tr><td>Time: $t </td><td align=right> <form><input type=button value='Close Windows' onclick='window.close();'></form></td></tr></table>";
+ }
+
+
+}
+
+class DBCheckDriver {
+ var $iProjidx;
+ var $iDB,$iDBUtils;
+
+ function DBCheckDriver($aProjidx) {
+ $this->iDB = DBFactory::InitDB();
+ $this->iDBUtils = new DBUtils($this->iDB);
+ $this->iProjidx = $aProjidx;
+ }
+
+ function GetListOfProjects() {
+ $list=array();
+ $this->iDBUtils->GetProjects($list);
+ $n = count($list)/2;
+ $t = '<table border=0>';
+ for( $i=0; $i<$n; ++$i ) {
+ $t .= '<tr><td> <a href="ddda_chkdb.php?idx='.$list[$i*2+1].'">'.$list[$i*2].'</a> </td></tr>';
+ }
+ $t .= '</table>';
+ return $t;
+ }
+
+ function Run() {
+ if( isset($this->iProjidx) && $this->iProjidx > 0 ) {
+ $dbchecker = new CheckProjectDB($this->iDB);
+ $projname = $this->iDBUtils->GetProjNameForKey($this->iProjidx);
+ if( empty($projname) ) {
+ die("<font color=red><b>Error: Invalid project index [$this->iProjidx].<b></font>");
+ }
+ HTMLGenerator::DocHeader('DB Consistency check: '.$projname);
+ $dbchecker->Run($projname);
+ }
+ else {
+ echo "<h2>DB Consistency check</h2><hr>";
+ echo "Select project to check. <p>";
+ echo $this->GetListOfProjects();
+ //echo "<b><font color=red>Error: No project index specified. </font></b><br>Usage: ddda_chkdb.php?idx=<i>nn</i>";
+ }
+ }
+}
+
+$idx = @$HTTP_GET_VARS['idx'] ;
+if( !isset($idx) )
+ $idx=0;
+
+$driver = new DbCheckDriver($idx);
+$driver->Run();
+
+?>
\ No newline at end of file
--- /dev/null
+
+body {
+ margin-top : 10px;
+ margin-left : 10px;
+ margin-right : 10px;
+ //background-color: #D8F8FF;
+ background-color: #F0F4F8;
+ color : Black;
+ font-family : Arial,sans-serif;
+}
+
+table.mainmeny {
+ background-color:lightblue;
+ border:navy solid 2pt;
+}
+
+span.menytitle {
+ font-weight : bold;
+ font-size : 120%;
+ color : white;
+}
+
+table.projectindex {
+ background-color:lightyellow;
+ border:navy solid 2pt;
+}
+
+td.projindexheader {
+ background-color:navy;
+ color : white;
+ font-family : Arial, Helvetica, sans-serif;
+ font-size : 1.3em;
+ font-weight : bold;
+ text-align : center;
+}
+
+
+A.mainmeny:link {
+ font-family : Arial, Helvetica, sans-serif;
+ text-decoration : none;
+ color : black;
+}
+
+A.mainmeny:visited {
+ font-family : Arial, Helvetica, sans-serif;
+ text-decoration : none;
+ color : black;
+}
+
+A.mainmeny:hover {
+ font-family : Arial, Helvetica, sans-serif;
+ text-decoration : none;
+ color : black;
+ background-color : Yellow;
+}
+
+h1.mainmeny {
+ font-family : Arial, Helvetica, sans-serif;
+ font-size : 1.1em;
+ text-decoration : none;
+ color : white;
+ //background-color : blue;
+}
+
+td.mainmeny {
+ text-align : center;
+ background-color : navy;
+ border-bottom : black solid 2pt;
+}
+
+
+table.stdinputform {
+ background-color : lightblue;
+ border:navy solid 2pt;
+}
+
+td.stdinputtitle {
+ font-family : Arial, Helvetica, sans-serif;
+ font-size : 1em;
+ color : white;
+ text-align : center;
+ background-color : navy;
+ border-bottom : black solid 2pt;
+}
+
+
+A:link {
+ font-family : Arial, Helvetica, sans-serif;
+ text-decoration : none;
+ color : blue;
+}
+
+A:visited {
+ font-family : Arial, Helvetica, sans-serif;
+ text-decoration : none;
+ color : blue;
+}
+
+A:hover {
+ font-family : Arial, Helvetica, sans-serif;
+ text-decoration : none;
+ color : blue;
+ background-color : Yellow;
+}
+*/
+
+/* Normal headlines should almost never be used. Use named headers below insted*/
+h1,h2,h3,h4,h5 {
+ font-family : Arial, Helvetica, serif;
+ font-size: 100%;
+ margin-bottom:0;
+}
+
+h1 strong, h2 strong, h3 strong, h4 strong {
+ color:#cc0000;
+}
+
+
+.title h1 {
+ font-family : Arial, Helvetica, sans-serif;
+ background : #eeeeee;
+ font-size : 1.5em;
+ margin:0;
+ font-weight : bold;
+ text-align : center;
+ color:darkblue;
+ border-top: black solid 2pt;
+ border-bottom: black solid 2pt;
+}
+
+.subtitle {
+ text-align :center;
+ margin-left: 2em;
+ margin-right: 2em;
+ margin-bottom:0.5em;
+ font-family : Times Roman, Helvetica, sans-serif;
+ font-weight : normal;
+ font-style:italic;
+ font-size :1.0em;
+ color : Navy;
+}
+
+
+.normfldlabel {
+ font-family : Arial, Helvetica, serif;
+ font-size : 100%;
+ font-weight : bold;
+ color : navy;
+}
+
+.errfldlabel {
+ font-family : Arial, Helvetica, serif;
+ font-size : 100%;
+ font-weight : bold;
+ color : red;
+}
+
+.error {
+ font-family : Arial, Helvetica, serif;
+ font-weight : bold;
+ color : darkred;
+}
+
+.note {
+ font-family : Arial, Helvetica, serif;
+ font-size : 0.8em;
+ font-weight : normal;
+ color : black;
+}
+
+.note strong {
+ color : #990000;
+ font-weight : bold;
+}
+
+input.submitbutton {
+ font-size : 90%;
+ font-weight : bold;
+}
+
+.footer {
+ font-size : 0.65em;
+}
+
+
+table.datainput {
+ background : LightBlue;
+ color : Black;
+ //font-size:1em;
+ border: black solid 2pt;
+}
+
+td.datainput {
+ font-size : 90%;
+}
+
+input {
+ font-size:90%;
+}
+
+textarea {
+ font-size:90%;
+}
+
+
\ No newline at end of file
--- /dev/null
+<?php
+//=======================================================================
+// File: DE_UTILS.PHP
+// Description: Doc Edit Utilities
+// Created: 2002-04-15
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: de_utils.php,v 1.27.2.1 2003/08/11 12:03:43 aditus Exp $
+//
+// License: QPL 1.0
+// Copyright (C) 2002 Johan Persson
+//=======================================================================
+
+$yesno_array=array(" ","","  Ja  ","1","  Nej  ","2");
+
+// --------------------------------------------------------------
+// MySQL setup. You need to change these defines to suit your
+// own setup
+// --------------------------------------------------------------
+
+DEFINE( 'DBSERVER', 'localhost' );
+DEFINE( 'DBUSER', 'root' );
+DEFINE( 'DBPWD', '' );
+DEFINE( 'DBNAME', 'jpgraph_doc' );
+
+//===================================================
+// CLASS JpgTimer
+// Description: General timing utility class to handle
+// timne measurement of generating graphs. Multiple
+// timers can be started by pushing new on a stack.
+//===================================================
+class JpgTimer {
+ var $start;
+ var $idx;
+//---------------
+// CONSTRUCTOR
+ function JpgTimer() {
+ $this->idx=0;
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ // Push a new timer start on stack
+ function Push() {
+ list($ms,$s)=explode(" ",microtime());
+ $this->start[$this->idx++]=floor($ms*1000) + 1000*$s;
+ }
+
+ // Pop the latest timer start and return the diff with the
+ // current time
+ function Pop() {
+ assert($this->idx>0);
+ list($ms,$s)=explode(" ",microtime());
+ $etime=floor($ms*1000) + (1000*$s);
+ $this->idx--;
+ return $etime-$this->start[$this->idx];
+ }
+} // Class
+
+
+class HTMLGenerator {
+
+function DocHeader($title, $desc='' , $keywords='', $stylesheet='de_normal.css') {
+
+
+ echo "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">
+ <HTML><HEAD>
+ <META name=\"description\" content=\"$desc\">
+ <META name=\"keywords\" content=\"$keywords\">
+ <META HTTP-EQUIV=\"Expires\" CONTENT=\"Fri Jun 12 1981 08:20:00 GMT\">
+ <META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">
+ <META HTTP-EQUIV=\"Cache-control\" CONTENT=\"no-cache\">
+ <LINK REL=STYLESHEET TYPE=\"text/css\" HREF=\"". $stylesheet ."\">
+ <title>". $title ."</title></head>";
+ }
+
+ function DocPreamble() {
+ echo "\n<body>\n";
+ }
+
+ function DocPostamble() {
+ echo "\n</body></html>\n";
+ }
+
+ function CloseWinButton($aAlign='right') {
+ echo "<span align=$aAlign><form><input type=button value='Close Windows' onclick='window.close();'></form></span>";
+ }
+
+ //-------------------------------------------------------------
+ // SelectCode() - Where value is different from displayed label
+ //-------------------------------------------------------------
+ function SelectCode($name,$option,$selected="",$size=0,$js="",$cssclass="") {
+ $txt="<select name=$name";
+ if( $size > 0 )
+ $txt .= " size=$size ";
+ if( $cssclass != "" )
+ $txt .= " class=$cssclass>\n ";
+ else
+ $txt .= " $js>";
+ for($i=0; $i<count($option); $i += 2) {
+ if( $selected==$option[($i+1)] )
+ $txt=$txt."<option selected value=".$option[($i+1)].">$option[$i]</option>\n";
+ else
+ $txt=$txt."<option value=\"".$option[($i+1)]."\">$option[$i]</option>\n";
+ }
+ return $txt."</select>\n";
+ }
+
+ //-------------------------------------------------------------
+ // Select() - Where value is same as displayed value
+ //-------------------------------------------------------------
+ function Select($name,$option,$selected="",$size=0,$js="",$cssclass="") {
+ $txt="<select name=$name";
+ if( $size > 0 )
+ $txt .= " size=$size ";
+ if( $cssclass != "" )
+ $txt .= " class=$cssclass>\n ";
+ else
+ $txt .= " $js>";
+ for($i=0; $i<count($option); $i++) {
+ if( $selected==$option[$i] )
+ $txt=$txt."<option selected value=\"$option[$i]\">$option[$i]</option>\n";
+ else
+ $txt=$txt."<option value=\"".$option[$i]."\">$option[$i]</option>\n";
+ }
+ return $txt."</select>\n";
+ }
+
+ //-------------------------------------------------------------
+ // TransCodetable()
+ //-------------------------------------------------------------
+ function TransCodetable($code,$table) {
+ for($i=0; $i<count($table); $i += 2) {
+ if( $code==$table[($i+1)] ) return $table[$i];
+ }
+ return "[UNKNOWN CODE $code]";
+ }
+
+ function CheckBox($name,$value,$ismarked=false) {
+ $c = $ismarked ? ' CHECKED ' : '';
+ return "<input type=checkbox name=$name value=$value $c>";
+ }
+
+ function Radio($name,$options,$selected='',$maxperrow=0) {
+ $n = count($options);
+ $t = '';
+ $rc = 0;
+ $t .= '<table width="100%" cellspacing=2 ><tr>';
+ for($i=0; $i < $n; ++$i ) {
+ $s = '';
+ if( $selected == $options[$i] )
+ $s = ' CHECKED ';
+ $t .= "<td> <input type=radio name=$name $s value=\"$options[$i]\">$options[$i] </td>\n";
+ ++$rc;
+ if( $maxperrow>0 && $rc>=$maxperrow ) {
+ $t .= "</tr>\n<tr>\n";
+ $rc = 0;
+ }
+ }
+ return $t.'</tr></table>';
+ }
+
+ function RadioCode($name,$options,$selected='',$maxperrow=0) {
+ $n = count($options);
+ $t = '';
+ $rc=0;
+ $t .= '<table width="100%" cellspacing=5 ><tr>';
+ for($i=0; $i < $n; $i+=2 ) {
+ $s = '';
+ if( $selected == $options[$i+1] )
+ $s = ' CHECKED ';
+ $t .= "<td> <input type=radio name=$name $s value=\"".$options[$i+1]."\">$options[$i] </td>\n";
+ ++$rc;
+ if( $maxperrow>0 && $rc>=$maxperrow ) {
+ $t .= "</tr>\n<tr>\n";
+ $rc = 0;
+ }
+
+ }
+ return $t.'</tr></table>';
+ }
+
+ function Reset($name,$val,$enabled=true) {
+ $t = $enabled ? '' : 'DISABLED';
+ return "<INPUT TYPE=reset name=$name $t value=\"$val\">";
+ }
+
+ function InputText($name,$val,$len,$maxlen=100) {
+ return '<INPUT TYPE=TEXT NAME='.$name.' SIZE='.$len.' MAXLENGTH='.$maxlen.'>';
+ }
+
+ function Button($name,$val='',$onclick='',$enabled=true ) {
+ if( $val == '' )
+ $val = ' Ok ';
+ $t = $enabled ? '' : 'DISABLED';
+ $o = $onclick != '' ? "onclick=\"$onclick\" " : '';
+ return "<INPUT TYPE=button name=$name $t value=\"$val\" $o>";
+ }
+
+ function Submit($name,$val='',$onclick='',$enabled=true) {
+ if( $val == '' )
+ $val = ' Ok ';
+ $t = $enabled ? '' : 'DISABLED';
+ $o = $onclick != '' ? "onclick=\"$onclick\" " : '';
+ return "<INPUT class=submitbutton TYPE=submit name=$name $t value=\"$val\" $o>";
+ }
+
+ function FileInput($name,$val,$enabled=true) {
+ $t = $enabled ? '' : 'DISABLED';
+ return "<INPUT TYPE=file name=$name $t value=\"$val\">";
+ }
+
+}
+
+
+class DBUtils {
+ var $iDBServer;
+ var $iProject='';
+
+
+ var $sql_create_table_project =
+ "CREATE TABLE tbl_projects (
+ fld_key int(11) NOT NULL auto_increment,
+ fld_name varchar(80) NOT NULL default '',
+ fld_docdir varchar(255) NOT NULL default '',
+ fld_projdir varchar(255) NOT NULL default '',
+ fld_doctype tinyint(4) NOT NULL default '0',
+ fld_showprivate tinyint(4) NOT NULL default '0',
+ fld_showglobfuncs tinyint(4) NOT NULL default '0',
+ fld_lang varchar(10) NOT NULL default '',
+ fld_desc text NOT NULL,
+ fld_timestamp timestamp(14) NOT NULL,
+ PRIMARY KEY (fld_key)
+ ) TYPE=MyISAM;" ;
+
+ var $sql_create_table_projfiles =
+ "CREATE TABLE tbl_projfiles (
+ fld_key int(11) NOT NULL auto_increment,
+ fld_name varchar(255) NOT NULL default '',
+ fld_desc text,
+ fld_projidx int(11) NOT NULL default '0',
+ fld_dbupdtime datetime default NULL,
+ fld_timestamp timestamp(14) NOT NULL,
+ PRIMARY KEY (fld_key)
+ ) TYPE=MyISAM;" ;
+
+ var $sql_create_table_class =
+ " CREATE TABLE tbl_class_%s (
+ fld_key int(11) NOT NULL auto_increment,
+ fld_name varchar(40) NOT NULL default '',
+ fld_public tinyint(4) NOT NULL default '1',
+ fld_parentname varchar(40) default NULL,
+ fld_file varchar(255) NOT NULL default '',
+ fld_linenbr int(11) NOT NULL default '0',
+ fld_numfuncs int(11) NOT NULL default '0',
+ fld_desc text,
+ fld_ref1 varchar(40) default NULL,
+ fld_ref2 varchar(40) default NULL,
+ fld_ref3 varchar(40) default NULL,
+ fld_ref4 varchar(40) default NULL,
+ fld_timestamp timestamp(14) NOT NULL,
+ PRIMARY KEY (fld_key)
+ ) TYPE=MyISAM;" ;
+
+ var $sql_create_table_method =
+ " CREATE TABLE tbl_method_%s (
+ fld_key int(11) NOT NULL auto_increment,
+ fld_name varchar(40) NOT NULL default '',
+ fld_public tinyint(4) NOT NULL default '1',
+ fld_linenbr int(11) NOT NULL default '0',
+ fld_file varchar(255) NOT NULL default '',
+ fld_classidx int(11) default NULL,
+ fld_classname varchar(50) NOT NULL default '',
+ fld_shortdesc tinytext,
+ fld_desc text,
+ fld_return varchar(200) NOT NULL default '',
+ fld_example text,
+ fld_methref1 int(11) default NULL,
+ fld_methref2 int(11) default NULL,
+ fld_methref3 int(11) default NULL,
+ fld_methref4 int(11) default NULL,
+ fld_methref5 int(11) default NULL,
+ fld_numargs tinyint(4) default NULL,
+ fld_arg1 varchar(40) default NULL,
+ fld_arg2 varchar(40) default NULL,
+ fld_arg3 varchar(40) default NULL,
+ fld_arg4 varchar(40) default NULL,
+ fld_arg5 varchar(40) default NULL,
+ fld_arg6 varchar(40) default NULL,
+ fld_arg7 varchar(40) default NULL,
+ fld_arg8 varchar(40) default NULL,
+ fld_arg9 varchar(40) default NULL,
+ fld_arg10 varchar(40) default NULL,
+ fld_argdes1 varchar(80) default NULL,
+ fld_argdes2 varchar(80) default NULL,
+ fld_argdes3 varchar(80) default NULL,
+ fld_argdes4 varchar(80) default NULL,
+ fld_argdes5 varchar(80) default NULL,
+ fld_argdes6 varchar(80) default NULL,
+ fld_argdes7 varchar(80) default NULL,
+ fld_argdes8 varchar(80) default NULL,
+ fld_argdes9 varchar(80) default NULL,
+ fld_argdes10 varchar(80) default NULL,
+ fld_argval1 varchar(30) NOT NULL default '',
+ fld_argval2 varchar(30) NOT NULL default '',
+ fld_argval3 varchar(30) NOT NULL default '',
+ fld_argval4 varchar(30) NOT NULL default '',
+ fld_argval5 varchar(30) NOT NULL default '',
+ fld_argval6 varchar(30) NOT NULL default '',
+ fld_argval7 varchar(30) NOT NULL default '',
+ fld_argval8 varchar(30) NOT NULL default '',
+ fld_argval9 varchar(30) NOT NULL default '',
+ fld_argval10 varchar(30) NOT NULL default '',
+ fld_timestamp timestamp(14) NOT NULL,
+ PRIMARY KEY (fld_key)
+ ) TYPE=MyISAM;";
+
+ var $sql_create_table_classvars =
+ " CREATE TABLE tbl_classvars_%s (
+ fld_key int(11) NOT NULL auto_increment,
+ fld_name varchar(30) NOT NULL default '',
+ fld_public tinyint(4) NOT NULL default '1',
+ fld_default varchar(30) default NULL,
+ fld_desc tinytext,
+ fld_classidx int(11) NOT NULL default '0',
+ fld_timestamp timestamp(14) NOT NULL,
+ PRIMARY KEY (fld_key)
+ ) TYPE=MyISAM;";
+
+
+ function DBUtils($aDBServer) {
+ $this->iDBServer = $aDBServer;
+ }
+
+ function SetProject($aProj) {
+ if( trim($aProj) != '' ) {
+ $this->iProject = '_'.$aProj;
+ }
+ else
+ $this->iProject = '';
+ }
+
+ function ExistTableProjects() {
+ $res = $this->iDBServer->Query("SELECT * FROM tbl_projects",true);
+ if( $res == false ) return false;
+ else return true;
+ }
+
+ function SetupNewDB() {
+ $this->iDBServer->Query($this->sql_create_table_project);
+ $this->iDBServer->Query($this->sql_create_table_projfiles);
+ }
+
+ function CreateNewTablesForProject($aProjName) {
+ $this->iDBServer->Query(sprintf($this->sql_create_table_class,$aProjName));
+ $this->iDBServer->Query(sprintf($this->sql_create_table_classvars,$aProjName));
+ $this->iDBServer->Query(sprintf($this->sql_create_table_method,$aProjName));
+ }
+
+ function GetList(&$aList,$aFlds, $aTable, $aWhere='') {
+ $q = "SELECT ".$aFlds[0];
+ $nf = count($aFlds);
+ $i = 1;
+ while( $i < $nf ) {
+ $q .= ",".$aFlds[$i];
+ ++$i;
+ }
+ $q .= " FROM $aTable ";
+ if( $aWhere != '' )
+ $q .= ' WHERE '.$aWhere;
+ $q .= " ORDER BY fld_name ";
+ $res = $this->iDBServer->Query($q);
+ $n = $res->NumRows();
+ $i = 0;
+ while( $i < $n ) {
+ $row = $res->Fetch();
+ for( $j=0; $j< $nf; ++$j )
+ $aList[] = $row[$j];//$row[$aFlds[$j]];
+ ++$i;
+ }
+ }
+
+ function GetClassListNameKeyPublic(&$aList) {
+ $aList = array(" ",-1,0);
+ $this->GetList($aList,array('fld_name','fld_key','fld_public'),'tbl_class'.$this->iProject);
+ }
+
+ function GetClassList(&$aList) {
+ $aList = array(" ");
+ $this->GetList($aList,array('fld_name'),'tbl_class'.$this->iProject);
+ }
+
+ function GetClassKey($aKey) {
+ $q = "SELECT * FROM tbl_class".$this->iProject." WHERE fld_key='".$aKey."'";
+ $res = $this->iDBServer->Query($q);
+ return $res->Fetch();
+ }
+
+ function DropProjTables($aKey) {
+ $projname=$this->GetProjNameForKey($aKey);
+ $q = 'DROP TABLE tbl_class_'.$projname.', tbl_method_'.$projname.', tbl_classvars_'.$projname;
+ $this->iDBServer->Query($q);
+ }
+
+ function GetMethodList(&$aList) {
+ $aList = array(" ",-1);
+ $this->GetList($aList,
+ array("CONCAT(fld_classname,'::',fld_name) as fld_name", 'fld_key'),'tbl_method'.$this->iProject);
+ }
+
+ function GetMethodKey($aKey) {
+ $q = "SELECT * FROM tbl_method".$this->iProject." WHERE fld_key=".$aKey;
+ $res = $this->iDBServer->Query($q);
+ return $res->Fetch();
+ }
+
+ function GetNumMethods($aKey) {
+ $q = "SELECT COUNT(*) FROM tbl_method".$this->iProject." WHERE fld_classidx=$aKey";
+ $res = $this->iDBServer->Query($q);
+ $r = $res->Fetch();
+ return $r[0];
+ }
+
+ function GetMethodsForClassKeyR($aKey) {
+ $q = "SELECT * FROM tbl_method".$this->iProject." WHERE fld_classidx=$aKey ORDER BY fld_name";
+ $res = $this->iDBServer->Query($q);
+ return $res;
+ }
+
+
+ function GetMethodListForClassKey(&$aList,$aClassIdx) {
+ $this->GetList($aList,
+ array("CONCAT(fld_classname,'::',fld_name) as fld_name", 'fld_key') ,
+ 'tbl_method'.$this->iProject,"fld_classidx=$aClassIdx");
+ }
+
+ function GetProject($aName='') {
+ if( $aName == '' )
+ $aName = substr($this->iProject,1);
+ $q = 'SELECT * FROM tbl_projects WHERE fld_name='."'$aName'";
+ $res = $this->iDBServer->Query($q);
+ $r = $res->Fetch();
+ return $r;
+ }
+
+ function GetProjects(&$aList,$aFill=0) {
+ $this->GetList($aList,array('fld_name', 'fld_key'),'tbl_projects');
+ $this->PadDisplayName($aList,$aFill);
+ }
+
+ function GetProjectFiles(&$aList,$aProjIdx,$aFill=0) {
+ $this->GetList($aList,
+ array('fld_name', 'fld_key'),'tbl_projfiles',' fld_projidx='.$aProjIdx);
+ $this->PadDisplayName($aList,$aFill);
+ }
+
+ function GetProjDir($aName) {
+ $q = "SELECT fld_docdir FROM tbl_projects WHERE fld_name='".$aName."'";
+ $res = $this->iDBServer->Query($q);
+ $r = $res->Fetch();
+ return $r['fld_docdir'];
+ }
+
+ function GetShowPrivate($aName) {
+ $q = "SELECT fld_showprivate FROM tbl_projects WHERE fld_name='".$aName."'";
+ $res = $this->iDBServer->Query($q);
+ $r = $res->Fetch();
+ return $r['fld_showprivate'];
+ }
+
+ function GetProjNameForKey($aKey) {
+ $q = 'SELECT fld_name FROM tbl_projects WHERE fld_key='.$aKey;
+ $res = $this->iDBServer->Query($q);
+ $r = $res->Fetch();
+ return $r['fld_name'];
+ }
+
+ function PadDisplayName(&$aList,$aFill) {
+ if( $aFill > 0 ) {
+ $n = count($aList);
+ for($i=0; $i < $n; $i += 2 ) {
+ $len = strlen($aList[$i]);
+ if( $len < $aFill ) {
+ $ps = '';
+ while($len++ < $aFill)
+ $ps .= ' ';
+ $aList[$i] .= $ps;
+ }
+ }
+ }
+ }
+}
+
+
+// Must have a global comparison method for usort()
+function _cmp($a,$b) {
+ if ($a[1] == $b[1]) {
+ if( $a[2] == $b[2] ) return 0;
+ return ($a[2] > $b[2]) ? 1 : -1;
+ }
+ return ($a[1] > $b[1]) ? 1 : -1;
+}
+
+DEFINE('FLDTYPE_TEXTINPUT',1);
+DEFINE('FLDTYPE_TEXTAREA',2);
+DEFINE('FLDTYPE_DROPDOWN',3);
+DEFINE('FLDTYPE_DROPDOWNCODE',4);
+DEFINE('FLDTYPE_STATICTEXT',5);
+DEFINE('FLDTYPE_STATICTEXTCODE',6);
+DEFINE('FLDTYPE_NONDBTEXT',7);
+DEFINE('FLDTYPE_TIMESTAMP',8);
+DEFINE('FLDTYPE_RADIO',9);
+DEFINE('FLDTYPE_RADIOCODE',10);
+DEFINE('FLDTYPE_CHECKBOX',11);
+DEFINE('FLDTYPE_FILEINPUT',12);
+DEFINE('FLDTYPE_PWD',13);
+
+DEFINE('FLDTYPE_SUBMIT',20);
+DEFINE('FLDTYPE_SAVE',21);
+DEFINE('FLDTYPE_CLEARSAVE',22);
+DEFINE('FLDTYPE_DELETESAVE',23);
+DEFINE('FLDTYPE_DELETECLEARSAVE',24);
+DEFINE('FLDTYPE_BUTTON',25);
+
+DEFINE('FLDTYPE_HRULE',40);
+
+
+DEFINE('MODIFY_HOOK',30);
+
+DEFINE('LBLPOS_TOP',1);
+DEFINE('LBLPOS_LEFT',2);
+DEFINE('LBLPOS_RIGHT',3);
+
+DEFINE('FLDPOS_LEFT',1);
+DEFINE('FLDPOS_RIGHT',2);
+
+class FormLayout {
+
+// Parameters
+//fldname,row,col,span,label,labelpos,fldpos,fldtype,targ1,targ2
+
+ // sort on row & col
+ var $iForm=array(),$iRows,$iHidden=array();
+ var $iFormClass, $iCellClass;
+ var $iAction;
+ var $iSaveButtonLabel = ' Save ';
+ var $iClearButtonLabel = ' New ';
+ var $iDeleteButtonLabel = ' Delete ';
+ var $iShowDelete = false, $iShowClear = false;
+ var $iBorder = 1, $iCellSpacing = 0, $iCellPadding = 5;
+ var $iHTMLGen;
+ var $iErrLabelClass = "errfldlabel";
+ var $iNormLabelClass = "normfldlabel";
+ var $iShowSave = true;
+ var $iReallyDeleteTxt = 'Really delete?';
+ var $iFormName, $iFormClass='stdinputform';
+ var $iAlign='center',$iTitle;
+
+ function FormLayout($aFormName,$aAction,$aTitle='') {
+ $this->iAction = $aAction;
+ $this->iHTMLGen = new HTMLGenerator();
+ $this->iFormName = $aFormName;
+ $this->iTitle = $aTitle;
+ }
+
+ function SetTitle($aTitle) {
+ $this->iTitle = $aTitle;
+ }
+
+ function SetCSSClass($aFormClass="",$aCellClass="") {
+ $this->iFormClass = $aFormClass;
+ $this->iCellClass = $aCellClass;
+ }
+
+ function SetButtonLabels($aSave,$aDelete='',$aNew='') {
+ $this->iSaveButtonLabel = $aSave;
+ if( $aNew!='' ) $this->iClearButtonLabel = $aNew;
+ if( $aDelete!='' ) $this->iDeleteButtonLabel = $aDelete;
+ }
+
+ function ShowDelete($aFlg = true ) {
+ $this->iShowDelete = $aFlg;
+ }
+
+ function SetBorder($aBorder) {
+ $this->iBorder = $aBorder;
+ }
+
+ function ShowClear($aFlg = true ) {
+ $this->iShowClear = $aFlg;
+ }
+
+ function SetFormClass($aClass) {
+ $this->iFormClass = $aClass;
+ }
+
+ function Stroke($aForm, $aHidden, $aValues, $aNonDBTexts=array(), $aErrFld=array() ) {
+ $this->iForm = $aForm;
+ $this->iHidden = $aHidden;
+ $this->iRows = count($aForm);
+
+ // Find out number of table columns
+ $max = 0;
+ for($i=0; $i < $this->iRows; $i++) {
+ $tmp = $this->iForm[$i][2] + $this->iForm[$i][3] - 1;
+ if( $tmp > $max ) $max = $tmp;
+ }
+ $ncols = $max;
+
+ if( empty($aValues['key']) )
+ $aValues['key'] = '';
+
+
+ $t = '<form name="'.$this->iFormName.'" action="'.$this->iAction."\" method=post>\n";
+ $t .= "<input type=hidden name=key value='".$aValues['key']."'>\n";
+ $t .= "<input type=hidden name=_x_formname value='".$this->iFormName."'>\n";
+ $nh = count($this->iHidden);
+ foreach( $this->iHidden as $h )
+ $t .= "<input type=hidden name=$h value='".$aValues[$h]."'>\n";
+
+ $t .= "<div align=$this->iAlign><table cellspacing=$this->iCellSpacing cellpadding=$this->iCellPadding class=$this->iFormClass>\n";
+
+ if( $this->iTitle != '' )
+ $t .= "<tr><td class=stdinputtitle colspan=$ncols>$this->iTitle</td></tr>";
+
+ usort($this->iForm,'_cmp');
+ $newrow = true;
+ $crow = 0;
+ for($i=0; $i < $this->iRows; $i++) {
+
+ if( $crow == 0 ) {
+ while( $crow < $this->iForm[$i][1] - 1 ) {
+ $t.= "\n<tr><td class=datainput colspan=$ncols> </td></tr>\n";
+ ++$crow;
+ }
+ ++$crow;
+ $t .= "<tr>\n";
+ }
+ elseif( $crow < ($this->iForm[$i][1] - 1) ) {
+ $t .= "</tr>\n";
+ while( $crow < $this->iForm[$i][1] - 1 ) {
+ $t .= "\n</tr><td class=datainput colspan=$ncols> </td></tr>\n";
+ ++$crow;
+ }
+ $t .= "<tr>\n";
+ ++$crow;
+ }
+ elseif( $crow == $this->iForm[$i][1]-1 ) {
+ $t .= "\n</tr>\n<tr>\n";
+ ++$crow;
+ }
+
+ if( $i > 0 ) {
+ if( ($this->iForm[$i-1][1]==$this->iForm[$i][1]) &&
+ ($this->iForm[$i-1][2]+$this->iForm[$i-1][3] > $this->iForm[$i][2]) )
+ die("<b>Form layout error</b><br>In row $crow Columns <b>".$this->iForm[$i-1][0]."</b> and <b>".$this->iForm[$i][0]."</b> overlaps. Please fix!");
+
+ if( $this->iForm[$i-1][1] == $crow ) {
+ $d = $this->iForm[$i][2] - ($this->iForm[$i-1][2] + $this->iForm[$i-1][3]);
+ for( $j=0; $j<$d; ++$j )
+ $t .= '<td class=datainput> </td>';
+ }
+ else {
+ for( $j=1; $j<$this->iForm[$i][2]; ++$j )
+ $t .= '<td class=datainput> </td>';
+ }
+ }
+
+ $lblclass = @$aErrFld[$this->iForm[$i][0]] ? $this->iErrLabelClass : $this->iNormLabelClass;
+ $cellalign = $this->iForm[$i][6] == FLDPOS_LEFT ? '' : ' align=right ';
+ $tst = $this->iForm[$i][7] == FLDTYPE_SUBMIT || $this->iForm[$i][7] == FLDTYPE_SAVE ;
+ $cellvalign = $tst ? 'valign=bottom' : '';
+
+ if( $this->iForm[$i][5] == LBLPOS_TOP || $this->iForm[$i][5] == LBLPOS_LEFT ) {
+ $t .= "<td class=datainput $cellalign $cellvalign colspan=".
+ $this->iForm[$i][3].'><span class="'.$lblclass.'">'.$this->iForm[$i][4].'</span>';
+
+ if( $this->iForm[$i][5] == LBLPOS_TOP )
+ $t .= "<br>";
+ else
+ $t .= ' ';
+ }
+ else {
+ $t .= "<td class=datainput $cellalign $cellvalign colspan=".$this->iForm[$i][3].'>';
+ }
+
+
+ $val = @$aValues[$this->iForm[$i][0]];
+ switch( $this->iForm[$i][7] ) {
+ case FLDTYPE_HRULE :
+ $t .= "<hr>";
+ break;
+ case FLDTYPE_FILEINPUT :
+ //echo "val=$val ";
+ $t .= '<input type=file name='.$this->iForm[$i][0].' value="'.$val.'" ';
+ $t .= 'size='.$this->iForm[$i][8].' maxlength='.$this->iForm[$i][9].'>';
+ break;
+ case FLDTYPE_TEXTINPUT :
+ //<input type=text name=modell value=\"$flds[fld_modell]\" size=15 maxlength=20>
+ $t .= '<input type=text name='.$this->iForm[$i][0].' value="'.$val.'" ';
+ $t .= 'size='.$this->iForm[$i][8].' maxlength='.$this->iForm[$i][9];
+ if( !empty($this->iForm[$i][10]) )
+ $t .= ' readonly style="font-family:courier new;font-weight:bold;">';
+ else
+ $t .= '>';
+ break;
+
+ case FLDTYPE_DROPDOWNCODE :
+ if( !empty($this->iForm[$i][9]) )
+ $size = $this->iForm[$i][9];
+ else
+ $size = 0;
+ $t .= $this->iHTMLGen->SelectCode($this->iForm[$i][0],$this->iForm[$i][8],$val,$size);
+ break;
+ case FLDTYPE_DROPDOWN :
+ if( !empty($this->iForm[$i][9]) )
+ $size = $this->iForm[$i][9];
+ else
+ $size = 0;
+ $t .= $this->iHTMLGen->Select($this->iForm[$i][0],$this->iForm[$i][8],$val,$size);
+ break;
+ case FLDTYPE_TEXTAREA :
+ $t .= "<textarea cols=".$this->iForm[$i][8]." rows=".$this->iForm[$i][9]." name=\"".$this->iForm[$i][0]."\">".$val."</textarea>";
+ break;
+ case FLDTYPE_STATICTEXTCODE :
+ $val = $this->iHTMLGen->TransCodetable($val,$this->iForm[$i][8]);
+ case FLDTYPE_STATICTEXT :
+ $tmp = $val;
+ if( $tmp == '' ) $tmp =' ';
+ $t .= $tmp;
+ $t .= "<input type=hidden name=".$this->iForm[$i][0]." value='".$val."'>";
+ break;
+ case FLDTYPE_NONDBTEXT :
+ if( !empty($this->iForm[$i][8]) )
+ $tmp = $this->iForm[$i][8];
+ else
+ $tmp = @$aNonDBTexts[$this->iForm[$i][0]];
+ if( $tmp == '' ) $tmp =' ';
+ $t .= $tmp;
+ break;
+
+ case FLDTYPE_TIMESTAMP :
+ if( $val == '' )
+ $t .= ' ';
+ else
+ $t .= $val = substr($val,0,4)."-".substr($val,4,2)."-".substr($val,6,2)." ".substr($val,8,2).":".substr($val,10,2);
+ break;
+
+ case FLDTYPE_CHECKBOX :
+ // name,value,checked
+ if( !empty($this->iForm[$i][8]) )
+ $v = $this->iForm[$i][8];
+ else
+ $v = 1;
+ $t .= $this->iHTMLGen->CheckBox($this->iForm[$i][0],$v,$val);
+ break;
+
+ case FLDTYPE_RADIO :
+ if( !empty($this->iForm[$i][9]) )
+ $cols = $this->iForm[$i][9];
+ else
+ $cols = 0;
+ $t .= $this->iHTMLGen->Radio($this->iForm[$i][0],$this->iForm[$i][8],$val,$cols);
+ break;
+
+ case FLDTYPE_RADIOCODE :
+ if( empty($this->iForm[$i][9]) )
+ $this->iForm[$i][9] = 0;
+ $t .= $this->iHTMLGen->RadioCode($this->iForm[$i][0],$this->iForm[$i][8],$val,$this->iForm[$i][9]);
+ break;
+
+ case FLDTYPE_DELETECLEARSAVE :
+ if( $aValues['key'] != '' )
+ $t .= $this->iHTMLGen->Submit('button_delete',$this->iDeleteButtonLabel,"return confirm('".$this->iReallyDeleteTxt."')");
+ $this->iShowDelete = false;
+ // No break here, fall through to next case !
+
+ case FLDTYPE_CLEARSAVE :
+ $t .= ' '.$this->iHTMLGen->Submit('button_clear',$this->iClearButtonLabel);
+ $this->iShowClear = false;
+ // No break here, fall through to next case !
+
+ case FLDTYPE_SAVE :
+ if( empty($this->iForm[$i][8]) )
+ $val = $this->iSaveButtonLabel;
+ else
+ $val = $this->iForm[$i][8];
+ $t .= ' '.$this->iHTMLGen->Submit('button_save',$this->iSaveButtonLabel);
+ $this->iShowSave = false;
+ break;
+
+ case FLDTYPE_SUBMIT :
+ if( empty($this->iForm[$i][8]) )
+ $val = ' : Ok ';
+ else
+ $val = $this->iForm[$i][8];
+ $t .= $this->iHTMLGen->Submit($this->iForm[$i][0],$val);
+ $this->iShowSave = false;
+ break;
+
+ case FLDTYPE_BUTTON :
+ if( empty($this->iForm[$i][8]) )
+ $val = ' : Ok ';
+ else
+ $val = $this->iForm[$i][8];
+ $t .= $this->iHTMLGen->Submit($this->iForm[$i][0],$val,$this->iForm[$i][9]);
+ break;
+
+ default:
+ die("Unknown field type.");
+ break;
+ }
+ if( $this->iForm[$i][5] == LBLPOS_RIGHT ) {
+ $t .= '<span class="'.$lblclass.'">'.$this->iForm[$i][4].'</span>';
+ }
+
+ $t .= "</td>\n";
+
+ // Is the next one at same row then find out how many empty
+ // columns is in between
+ if( $i < $this->iRows-1 ) {
+ if( $this->iForm[$i+1][1] != $crow ) {
+ for( $j=$this->iForm[$i][2]+$this->iForm[$i][3] - 1; $j<$ncols; ++$j )
+ $t .= '<td> </td>';
+ }
+ }
+ else {
+ // fill out the last row
+ for( $j=$this->iForm[$i][2]+$this->iForm[$i][3] - 1; $j<$ncols; ++$j )
+ $t .= '<td> </td>';
+ }
+ }
+ $t .= "\n</tr>\n";
+
+ // Setup automatic submit buttons
+ if( $this->iShowClear || $this->iShowDelete || $this->iShowSave ) {
+ $t .= "<tr>";
+ $t .= "<td class=datainputbutton align=right colspan=$ncols>";
+ $t .= $this->iShowClear ? "<input type=submit name=button_clear value=\"$this->iClearButtonLabel\">" : ' ';
+ $t .= $this->iShowDelete ? "\n<input type=submit name=button_delete onclick=\"return confirm('Really delete?')\" value=\"$this->iDeleteButtonLabel\" >" : ' ';
+ $t .= $this->iShowSave ? "\n<input class=submit type=submit name=button_save value=\"$this->iSaveButtonLabel\">\n" : ' ';
+ $t .= "</td></tr>";
+ }
+ $t .= "\n</table></div></form>";
+ return $t;
+ }
+}
+
+class DBTableEdit {
+ var $iDBUtils;
+ var $iDB;
+ var $iTableName = "";
+ var $iFormSpec=array() , $iHidden=array(), $iAction;
+ var $iFormLayout;
+
+ function DBTableEdit($aFormName,$aDBUtils,$aAction="") {
+ $this->iDBUtils = $aDBUtils;
+ $this->iDB = $aDBUtils->iDBServer;
+ $this->iAction = $aAction;
+ $this->iFormLayout = new FormLayout($aFormName,$this->iAction);
+ }
+
+ function ReportError($aErrFld) {
+ $nbr=1;
+ $t = "<b>The following fields have illegal values : </b><br>";
+ while ( list ($field, $errmsg) = each ($aErrFld) ) {
+ $t .= "<b><font color=red>$nbr. $errmsg</font></b><br>\n";
+ ++$nbr;
+ }
+ echo $t;
+ }
+
+
+ function Validate($aValues,&$aErrFld) {
+ // Must be implemented by subclasses
+ return true;
+ }
+
+ function DeleteRow($aKey) {
+ $q="DELETE FROM $this->iTableName WHERE fld_key=$aKey";
+ $this->iDB->Query($q);
+ }
+
+ function GetRow($aKey) {
+ $q = "SELECT * FROM $this->iTableName WHERE fld_key=$aKey";
+ $res = $this->iDB->Query($q);
+ if( $res->NumRows() > 0 )
+ return $res->Fetch();
+ else
+ return false;
+ }
+
+ function Set($aTableName,$aFormSpec,$aHidden=array()) {
+ $this->iTableName = $aTableName;
+ $this->iFormSpec = $aFormSpec;
+ $this->iHidden = $aHidden;
+ }
+
+ function ModifyValuesHook(&$aValues) {
+ // Virtual function to be used by subclass as a hook into
+ // the disply engined
+ }
+
+ function ModifyArrayHook($aName,&$aPar1) {
+ // Virtual function to be used by subclass as a hook into
+ // the disply engined
+ }
+
+ function NewPostHook($aValues) {
+ // Gets called right after a new post has been inserted
+ }
+
+ function PreDeleteHook($aKey) {
+ return false;
+ }
+
+ function PostDeleteHook($aKey) {
+ return true;
+ }
+
+ function Run($aValues,$aNonDBTexts=array()) {
+
+ $aErrFld = array();
+
+ if( isset($aValues['button_delete']) && !empty($aValues['key']) ) {
+ $t = $this->PreDeleteHook($aValues['key']);
+ if( $t ) {
+ $this->DeleteRow($aValues['key']);
+ $this->PostDeleteHook($aValues['key']);
+ $aValues = array();
+ }
+ }
+ elseif( isset($aValues['button_clear']) ) {
+ $aValues = array();
+ unset($aValues['key']);
+ }
+ elseif( isset($aValues['button_save']) ) {
+ if( $this->Validate($aValues,$aErrFld) ) {
+ $q = "REPLACE $this->iTableName SET ";
+ while (list ($key, $val) = each ($aValues)) {
+ if( !strstr($key,'button') && !strstr($key,'_x_') )
+ $q .= "fld_$key='".$val."', ";
+ }
+ $q .= "fld_timestamp=now()";
+ $this->iDB->Query($q);
+ if( empty($aValues['key']) ) {
+ $aValues['key'] = mysql_insert_id();
+ $this->NewPostHook($aValues);
+ }
+ }
+ else {
+ $this->ReportError($aErrFld);
+ }
+ }
+
+ $n = count($this->iFormSpec);
+ for( $i=0; $i < $n; ++$i ) {
+ if( ($this->iFormSpec[$i][7] == FLDTYPE_DROPDOWN ||
+ $this->iFormSpec[$i][7] == FLDTYPE_DROPDOWNCODE) &&
+ !is_array($this->iFormSpec[$i][8])) {
+
+ if( $this->iFormSpec[$i][8] == MODIFY_HOOK ){
+ $this->iFormSpec[$i][8] = array();
+ $this->ModifyArrayHook($this->iFormSpec[$i][0],$this->iFormSpec[$i][8]);
+ }
+ }
+ }
+
+ $this->ModifyValuesHook($aValues);
+
+ if( !empty($aValues['key']) ) {
+ $aRow = $this->GetRow($aValues['key']);
+ while (list ($key, $val) = each ($aRow)) {
+ if( !is_integer($key) ) {
+ $aValues[substr($key,4)] = htmlentities($val);
+ }
+ }
+ }
+ echo $this->iFormLayout->Stroke($this->iFormSpec, $this->iHidden, $aValues,$aNonDBTexts,$aErrFld);
+ }
+}
+
+
+class DocStat {
+ var $iDB,$iDBUtils;
+
+ function DocStat($aDBUtils) {
+ $this->iDBUtils = $aDBUtils;
+ $this->iDB = $aDBUtils->iDBServer;
+ }
+
+ // Find out summary statistics for a project
+ function ProjStat($aProjName) {
+ $cl=array();
+ // Get all classes in the project
+ $this->iDBUtils->GetClassListNameKeyPublic($cl);
+ $nc = (count($cl)-3)/3;
+ if( $nc == 1 )
+ return array(0,0,0); // No classes
+ $sum = 0;
+ $totnm = 0;
+ for( $i=1; $i <= $nc; ++$i ) {
+ list($cname,$nm,$ma,$tp,$ap) = $this->ClassStat($cl[3*$i+1]);
+ if( $tp==0 )
+ die( "PANIC: Total points for Class $cname is 0 actual points=$ap, nm=".(count($ma))."<br>\n" );
+ $sum += (1.0 * ($ap/$tp));
+ $totnm += $nm;
+ }
+ $avg = $sum / ($nc);
+ return array($avg,$nc,$totnm);
+ }
+
+ function ClassStat($aKey) {
+ // Find out how many methods in this class
+ $nm = $this->iDBUtils->GetNumMethods($aKey);
+
+ // Calculate points for each class
+ // Method description is weighted 5 to 1
+ $res = $this->iDBUtils->GetMethodsForClassKeyR($aKey);
+ $n = $res->NumRows();
+ $points = 0;
+ $tot = 0;
+ $ma = array();
+ for( $i=0; $i < $n; ++$i ) {
+ $meth = $res->Fetch();
+ $na = $meth['fld_numargs'];
+ $argdescnt=0;
+ for( $j=1; $j <= $na; ++$j ) {
+ if( !empty($meth['fld_argdes'.$j]) && trim($meth['fld_argdes'.$j]) != '' ) {
+ ++$argdescnt;
+ }
+ }
+ $sdes = (!empty($meth['fld_shortdesc']) && trim($meth['fld_shortdesc'])!='' ? 3 : 0);
+ $des = (!empty($meth['fld_desc']) && trim($meth['fld_desc'])!='' ? 5 : 0);
+
+ // For private methods we don't require the example to be filled in
+ $ex = $meth['fld_public']==0 || (!empty($meth['fld_example']) && trim($meth['fld_example'])!='') ? 2 : 0;
+ $mp = round(($argdescnt+$des+$sdes+$ex)/($na+10)*100);
+ $ma[] = array($meth['fld_public'],$meth['fld_name'],$meth['fld_key'],$mp);
+
+ $tot += ($na + 10);
+ $points += ($argdescnt + $des + $sdes + $ex);
+ }
+
+ // Don't do this for the pseudoclass for global functions which has index=0
+ if( $aKey > 0 ) {
+ // Calculate score for class
+ // Class description is weighted 5 to 1
+ $r = $this->iDBUtils->GetClassKey($aKey);
+ $classpoints = !empty($r['fld_desc']) && trim($r['fld_desc'])!='' ? 5 : 0;
+ $tot += 5;
+ $points += $classpoints;
+ // number of methds, Total possioble points, Actual points
+ return array($r['fld_name'],$nm,$ma,$tot,$points);
+ }
+ else
+ return array('Global functions',$nm,$ma,$tot,$points);
+ }
+}
+
+
+
+class GenJavascript {
+
+ function Stroke() {
+
+ $js = "<script language='JavaScript'>
+ function setprojcookie() {
+ opt=document.mainform.projidx.options[document.mainform.projidx.selectedIndex];
+//alert(\"opt=\"+opt.value);return;
+ projidx=opt.value;
+ if( projidx <= 0 ) {
+ projname='';
+ projidx = '';
+ document.cookie = 'ddda_project=';
+ }
+ else {
+ projname=opt.text;
+ projcookie = 'ddda_project='+projname+':'+projidx;
+ document.cookie= projcookie;
+ }
+ }
+
+ function openPopup(url,width,height,name) {
+ window.open(url,name,'width='+width+', height='+height+',left=50,top=50,resizable=yes,scrollbars=yes');
+ }
+
+ </script>\n";
+
+ echo $js;
+ }
+}
+
+
+class Utils {
+
+ function HighlightCodeSnippet($t,$bg=true,$span=true) {
+ $t = "<?$t?>";
+ $t = highlight_string($t,true);
+ $t=str_replace('<?','',$t);
+ $t=str_replace('?>','',$t);
+ $t=str_replace('<code>','',$t);
+ $t=str_replace('</code>','',$t);
+
+ if( $bg ) {
+ //$t = "<div style=\"background-color:#E1E1E1;font-family:courier new;font-size:90%;font-weight:bold;\"><b>$t</b></div>";
+ $t = "<div style=\"border-top:solid black 2pt;background-color:lightblue;font-family:courier new;font-size:90%;font-weight:bold;\"><b>$t</b></div>";
+ }
+ elseif( $span ) {
+ $t = "<span style=\"font-family:courier;font-size:85%;\">$t</span>";
+ }
+ return $t;
+ }
+
+ function Error($aMsg) {
+ die( "<font color=red><b>Error:</b></font> $aMsg" );
+ }
+
+ function Warning($aMsg) {
+ echo "<font color=red><b>Warning:</b></font> $aMsg";
+ }
+
+ function DBTimeStampFormat($a) {
+ return substr($a,0,4).'-'.substr($a,4,2).'-'.substr($a,6,2).' '.substr($a,8,2).':'.substr($a,10,2);
+ }
+}
+
+
+
+class DocEditDriver {
+ var $iDB;
+ var $iDBUtils;
+ var $iProjname='',$iProjidx=-1;
+
+ function DocEditDriver() {
+ global $HTTP_COOKIE_VARS;
+
+ $this->iDB = DBFactory::InitDB();
+ $this->iDBUtils = new DBUtils($this->iDB);
+
+ $this->iProjname = strtok(@$HTTP_COOKIE_VARS['ddda_project'],':');
+
+ //echo "cookie=$this->iProjname <p>";
+
+ if( $this->iProjname != '' ) {
+ $this->iProjidx = strtok(':');
+ $this->iDBUtils->SetProject($this->iProjname);
+ }
+ }
+
+ function Run() {
+ // Must be implemented by subclass
+ }
+
+ function Close() {
+ HTMLGenerator::DocPostamble();
+ }
+}
+
+class DBFactory {
+
+ // DBFactory::InitDB()
+ function InitDB() {
+ $db = new DBServer(DBUSER,DBPWD,DBSERVER);
+ if( !$db->SetDB(DBNAME,true) ) {
+ $db->Create(DBNAME);
+ echo "Created new database : ".DBNAME."<p>";
+ $db->SetDB(DBNAME);
+ }
+
+ return $db;
+ }
+}
+
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//==============================================================================
+// Name: JPCLASSREF.PHP
+// Description: Basic framework to extract information about class hierarchi
+// and structure from DB. To do specific output this framework
+// expects a "formatter" plugin which handles the actual
+// layout and formatting of class, functions and variables.
+// See jpgenhtmldoc.php for example on how to write a simple
+// HTML formatter.
+// Created: 2002-04-12
+// Author: johanp@aditus.nu
+// Version: $Id: jpclassref.php,v 1.15 2002/07/10 23:57:40 aditus Exp $
+//
+// License: QPL 1.0
+// Copyright (C) 2001,2002 Johan Persson
+//
+//==============================================================================
+include_once("jpdb.php");
+include_once("de_utils.php");
+
+DEFINE("MAX_METHODREF",5);
+DEFINE("MAX_METHODARGS",10);
+
+DEFINE("FMT_CLASSVARS",1);
+
+DEFINE('GLOBFUNCCLASS','GLOBALFUNCS');
+
+class DBCache {
+ var $iDB;
+ var $iDBUtils;
+ var $iClasses=array(),
+ $iClassesByName=array(),
+ $iClassMethods=array(),
+ $iClassMethodsByName=array(),
+ $iAllMethods=array();
+ var $iMethods;
+ var $iProjName;
+ var $iShowPrivate;
+
+ function DBCache($aDB,$aProjName) {
+ $this->iProjName = '_'.$aProjName;
+ $this->iDB = $aDB;
+ $this->iDBUtils = new DBUtils($aDB);
+ $this->iShowPrivate = $this->iDBUtils->GetShowPrivate($aProjName);
+ }
+
+ function RefreshMethods() {
+ $q = 'SELECT * FROM tbl_method'.$this->iProjName.' ORDER BY fld_name';
+ $res = $this->iDB->query($q);
+ $n = $res->NumRows();
+ $this->iMethods = array();
+ $this->iClassMethods = array();
+ $this->iClassMethodsByName = array();
+ for( $i=0; $i < $n; ++$i ) {
+ $row = $res->Fetch();
+ if( empty($row['fld_classidx']) || $row['fld_classidx']===0 )
+ $classname = GLOBFUNCCLASS;
+ else
+ $classname = $this->iClasses[$row['fld_classidx']][0];
+ $this->iMethods[$row["fld_key"]] = array($classname,$row['fld_name'],$row['fld_public']);
+ $this->iClassMethods[$classname][] = array('fld_name' => $row['fld_name'],
+ 'fld_public' => $row['fld_public'],
+ 'fld_file' => $row['fld_file'],
+ 'fld_linenbr' => $row['fld_linenbr']);
+ $this->iClassMethodsByName[$classname][$row['fld_name']] = array($row['fld_name'],$row['fld_public']);
+ }
+ }
+
+ function RefreshClasses() {
+ $q = 'SELECT * FROM tbl_class'.$this->iProjName.' ORDER BY fld_name';
+ $res = $this->iDB->query($q);
+ $n = $res->NumRows();
+ $this->iMethods = array();
+ for( $i=0; $i < $n; ++$i ) {
+ $row = $res->Fetch();
+ $this->iClasses[$row["fld_key"]] = array($row["fld_name"],$row["fld_parentname"],$row["fld_public"]);
+ $this->iClassesByName[$row["fld_name"]] = array($row["fld_key"],$row["fld_parentname"],$row["fld_public"]);
+ }
+ }
+
+}
+
+
+class ClassFormatter {
+ var $iDBCache;
+ var $iFlags;
+ var $iDirectory = '';
+ var $iProjName;
+ var $iProjDesc;
+ var $iShowPrivate;
+ var $iShowGlobFuncs;
+ var $iDocType = 0; // 0=Framed document, each class one file. 1=Everything in one file
+ var $iStatAverage = 0;
+ var $iStatNumClasses = 0;
+ var $iStatNumMethods = 0;
+
+ function ClassFormatter($aDBCache,$aFlags="") {
+ $this->iDBCache = $aDBCache;
+ $this->iFlags = $aFlags;
+ }
+
+ // Empty stubs ("virtual functions")
+ // A subclass needs to override this methods to actual achieve the
+ // desired formatting. The framework will call these formatting
+ // functions at the appropriate time.
+
+ function FmtClassHierarchySetup($aHier,$aNbr) {}
+ function FmtClassHierarchyExit($aHier,$aNbr) {}
+ function FmtClassHierarchyHeaders($aHier,$aNbr) {}
+ function FmtClassHierarchyColumnSetup($aClassName,$aColNbr) {}
+ function FmtClassHierarchyColumnExit($aClassName,$aColNbr) {}
+ function FmtClassHierarchyRow($aClassName,$aMethodName,$aOverridden,$aPublic) {}
+ function FmtClassSetup($aClassInfo) {}
+ function FmtClassOverview($aClassInfo) {}
+ function FmtClassVars($aVars) {}
+ function FmtClassRefs($aClassInfo) {}
+ function FmtFuncReturn($aFunc) {}
+ function FmtFuncPrototype($aClassName,$aFunc,$aShowFile=false) {}
+ function FmtFuncArgs($aFunc) {}
+ function FmtFuncDesc($aFunc) {}
+ function FmtFuncRef($aRef) {}
+ function FmtFuncExample($aFunc) {}
+ function FmtIndexSetup() {}
+ function FmtIndexClass($aClassName) {}
+ function FmtIndexMethod($aClassName,$aMethodName) {}
+ function FmtIndexExit() {}
+
+ // Called before/after global funcs
+ function GlobalFuncEntry($aNumFuncs) {}
+ function GlobalFuncExit() {}
+
+ // Called before/after any new class.
+ function ClassExit() {}
+ function ClassEntry($aClassName) {}
+
+ // Called before and after all formatting is done
+ function Start() {}
+ function End() {}
+
+
+ // ------- END OF STUBS -----------
+
+ function Init($aProjName,$aProjDesc,$aDocType,$aDocDir,$aShowPrivate,$aShowGlobFuncs) {
+
+ $this->iDirectory = $aDocDir;
+ $this->iProjName = $aProjName;
+ $this->iProjDesc = $aProjDesc;
+ $this->iShowPrivate = $aShowPrivate;
+ $this->iDocType = $aDocType;
+ $this->iShowGlobFuncs = $aShowGlobFuncs;
+
+ }
+
+ function InitStat($aAverage,$aNumClasses,$aNumMethods) {
+ $this->iStatAverage = $aAverage;
+ $this->iStatNumClasses = $aNumClasses;
+ $this->iStatNumMethods = $aNumMethods;
+ }
+
+ function ClassHierarchy($aHier) {
+ $n = count($aHier);
+ $this->FmtClassHierarchySetup($aHier,$n);
+ $this->FmtClassHierarchyHeaders($aHier,$n);
+
+ for( $i=0; $i<$n; ++$i ) {
+ $this->FmtClassHierarchyColumnSetup($aHier[$i],$i);
+
+ if( empty($this->iDBCache->iClassMethods[$aHier[$i]]) ) {
+ $this->FmtClassHierarchyRow($aHier[$i],"",false,false);
+ }
+ else {
+ $methods = $this->iDBCache->iClassMethods[$aHier[$i]];
+ $m = count($methods);
+ for( $j=0; $j<$m; ++$j ) {
+ $overridden = false;
+ if( $i > 0 ) {
+ if( !empty($supermethods[$methods[$j]['fld_name']]) )
+ $overridden = true;
+ }
+ $this->FmtClassHierarchyRow($aHier[$i],$methods[$j]['fld_name'],$overridden,$methods[$j]['fld_public']);
+ }
+ }
+ if( !empty($this->iDBCache->iClassMethodsByName[$aHier[$i]]) )
+ $supermethods = $this->iDBCache->iClassMethodsByName[$aHier[$i]];
+ else
+ $supermethods = array();
+ $this->FmtClassHierarchyColumnExit($aHier[$i],$i);
+ }
+ $this->FmtClassHierarchyExit($aHier,$n);
+ }
+
+
+ function DoClass($aClass,$aHier) {
+ $this->FmtClassSetup($aClass);
+ $this->ClassHierarchy($aHier);
+ $this->FmtClassOverview($aClass);
+ $this->FmtClassRefs($aClass);
+ $this->FmtClassOverviewExit($aClass);
+ }
+
+ function DoVars($aVars) {
+ if( !($this->iFlags & FMT_CLASSVARS) )
+ return;
+ $this->FmtClassVars($aVars);
+ }
+
+ function ResolvMethRef($aRef) {
+ if( empty( $this->iDBCache->iMethods[$aRef] ) )
+ Utils::Error("Unknown method reference=$aRef");
+ else return $this->iDBCache->iMethods[$aRef];
+ }
+
+ function DoFuncs($aFuncs,$aClassName,$aShowFile=false) {
+ $n = count($aFuncs);
+ for( $i=0; $i < $n; ++$i ) {
+
+ if( $aClassName == GLOBFUNCCLASS ) {
+ echo '<br>'.($i+1).' : <font color=blue>'.$aFuncs[$i]['fld_name']."()</font>...\n";
+ }
+
+ if( $aFuncs[$i]['fld_public'] == 0 && !$this->iDBCache->iShowPrivate )
+ continue;
+ $this->FmtFuncPrototype($aClassName,$aFuncs[$i],$aShowFile);
+ $this->FmtFuncArgs($aFuncs[$i]);
+ $this->FmtFuncDesc($aFuncs[$i]);
+ $this->FmtFuncReturn($aFuncs[$i]);
+
+ $j = 1;
+ $ref=array();
+ while( $j <= MAX_METHODREF ) {
+ if( !empty($aFuncs[$i]["fld_methref$j"]) && $aFuncs[$i]["fld_methref$j"] > 0)
+ $ref[]=$aFuncs[$i]["fld_methref$j"];
+ ++$j;
+ }
+ $m = count($ref);
+ if( $m > 0 ) {
+ $refarr=array();
+ for( $j=0; $j < $m; ++$j ) {
+ if( empty( $this->iDBCache->iMethods[$ref[$j]] ) )
+ Utils::Error("Unknown method reference key=$ref[$j] in method : $aClassName::".$aFuncs[$i]['fld_name']);
+ else $refarr[] = $this->iDBCache->iMethods[$ref[$j]];
+ }
+ $this->FmtFuncRef($refarr);
+ }
+ $this->FmtFuncExample($aFuncs[$i]);
+ }
+ }
+}
+
+
+class ClassRef {
+ var $iIdx, $iRow, $iVars, $iFuncs, $iDBCache, $iHierarchy;
+ function ClassRef($aRow,$aHierarchy,$aVars,$aFuncs,$aIdx,$aDBCache) {
+ $this->iIdx = $aIdx;
+ $this->iRow = $aRow;
+ $this->iVars = $aVars;
+ $this->iFuncs = $aFuncs;
+ $this->iDBCache = $aDBCache;
+ $this->iHierarchy = $aHierarchy;
+ }
+
+ function Stroke(&$aFormatter) {
+ $aFormatter->ClassEntry($this->iRow["fld_name"]);
+ $aFormatter->DoClass($this->iRow,$this->iHierarchy);
+ $aFormatter->DoVars($this->iVars);
+ $aFormatter->DoFuncs($this->iFuncs,$this->iRow["fld_name"]);
+ $aFormatter->ClassExit();
+ }
+}
+
+class ClassReader {
+ var $iDB, $iDBCache, $iFlags;
+ var $iFormatter;
+ var $iProjname;
+ var $iNumIndexCols = 3;
+
+ function ClassReader($aFormatter,$aDBCache,$aFlags="") {
+ $this->iDB = $aDBCache->iDB;
+ $this->iDBCache = $aDBCache;
+ $this->iFlags = $aFlags;
+ $this->iFormatter = $aFormatter;
+ $this->iProjName = $aDBCache->iProjName;
+ }
+
+ function SetNumIndexCols($aNum) {
+ $this->iNumIndexCols = $aNum;
+ }
+
+ function GetHierarchy($aClassName) {
+ $h = array($aClassName);
+ $parent = $this->iDBCache->iClassesByName[$aClassName][1];
+ while( $parent != "" ) {
+ $h[] = $parent;
+ if( empty($this->iDBCache->iClassesByName[$parent][1]) ) {
+ break;
+ }
+ else
+ $parent = $this->iDBCache->iClassesByName[$parent][1];
+ }
+ return $h;
+ }
+
+ function GenClassIndex() {
+ $this->iFormatter->FmtIndexSetup(count($this->iDBCache->iClasses),count($this->iDBCache->iMethods),$this->iNumIndexCols);
+ foreach( $this->iDBCache->iClasses as $c ) {
+ if( $c[2] == 0 && !$this->iDBCache->iShowPrivate )
+ continue;
+
+ $this->iFormatter->FmtIndexClass($c[0]);
+ if( !empty($this->iDBCache->iClassMethods[$c[0]]) && count($this->iDBCache->iClassMethods[$c[0]]) > 0 ) {
+ foreach( $this->iDBCache->iClassMethods[$c[0]] as $m ) {
+ if( $m['fld_public'] == 0 && !$this->iDBCache->iShowPrivate )
+ continue;
+ $this->iFormatter->FmtIndexMethod($c[0], $m['fld_name']);
+ }
+ }
+ }
+ $this->iFormatter->FmtIndexExit();
+ }
+
+
+ function Run($aClass) {
+
+ $q = 'SELECT * FROM tbl_class'.$this->iProjName.' ORDER BY fld_name ';
+ if( $aClass != "" )
+ $q .= " WHERE fld_name='".$aClass."'";
+ $classres = $this->iDB->query($q);
+ $n = $classres->NumRows();
+
+
+ $this->GenClassIndex();
+
+ for( $i=0; $i < $n; ++$i ) {
+ $row = $classres->Fetch();
+
+ if( $row['fld_public'] == 0 && !$this->iDBCache->iShowPrivate )
+ continue;
+
+ $hier = $this->GetHierarchy($row["fld_name"]);
+
+ $q = 'SELECT * FROM tbl_classvars'.$this->iProjName.' WHERE fld_classidx='.$row['fld_key'].' ORDER BY fld_name';
+ $varres = $this->iDB->query($q);
+ $nn = $varres->NumRows();
+ $vars = array();
+ for( $j=0; $j < $nn; ++$j ) {
+ $vars[] = $varres->Fetch();
+ }
+
+ $q = 'SELECT * FROM tbl_method'.$this->iProjName.' WHERE fld_classidx='.$row['fld_key'].' ORDER BY fld_name';
+ $funcres = $this->iDB->query($q);
+ $nn = $funcres->NumRows();
+ $funcs = array();
+ for( $j=0; $j < $nn; ++$j ) {
+ $funcs[] = $funcres->Fetch();
+ }
+
+ $c = new ClassRef($row,$hier,$vars,$funcs,$i,$this->iDBCache);
+ $c->Stroke($this->iFormatter);
+ }
+ }
+}
+
+// Read all global functions and format them
+class GlobalFuncReader {
+ var $iDB,$iDBCache,$iFlags;
+ var $iFormatter;
+ var $iProjname;
+
+ function GlobalFuncReader($aFormatter,$aDBCache,$aFlags="") {
+ $this->iDBCache = $aDBCache;
+ $this->iDB = $aDBCache->iDB;
+ $this->iFlags = $aFlags;
+ $this->iFormatter = $aFormatter;
+ $this->iProjName = $aDBCache->iProjName;
+ }
+
+ function Run() {
+ $q = 'SELECT * FROM tbl_method'.$this->iProjName.' WHERE fld_classidx=0 ORDER BY fld_name';
+ $res = $this->iDB->query($q);
+ $n = $res->NumRows();
+ $funcs=array();
+ for($i=0; $i < $n; ++$i ) {
+ $funcs[] = $res->Fetch();
+ }
+ $this->iFormatter->GlobalFuncEntry($n);
+ $this->iFormatter->DoFuncs($funcs,GLOBFUNCCLASS,true);
+ $this->iFormatter->GlobalFuncExit();
+ }
+}
+
+
+// Driver
+class ClassRefDriver {
+ var $iDB,$iDBCache;
+ var $iProjName, $iProjIdx;
+ var $iNumIndexCols=3;
+
+ function NewClassFormatter($aDBCache,$aFlags) {
+ Utils::Error("ERROR: NewClassFormatter must be overridden to provide the actual formatter");
+ }
+
+ function SetNumIndexCols($aNum) {
+ $this->iNumIndexCols = $aNum;
+ }
+
+ function ClassRefDriver() {
+ global $HTTP_COOKIE_VARS;
+
+ $this->iDB = DBFactory::InitDB();
+
+ $this->iProjname = strtok(@$HTTP_COOKIE_VARS['ddda_project'],':');
+ if( $this->iProjname != '' ) {
+ $this->iProjidx = strtok(':');
+ $this->iDBCache = new DBCache($this->iDB,$this->iProjname);
+ }
+ else
+ die('No, no project specified.');
+ }
+
+ function Run($aClass,$aFlags="") {
+
+ $dbutils = $this->iDBCache->iDBUtils;
+ $dbutils->SetProject($this->iProjname);
+ $proj = $dbutils->GetProject();
+ $docdir = $proj['fld_docdir'];
+ $showPrivate = $proj['fld_showprivate'];
+ $docType = $proj['fld_doctype'];
+ $showGlobFuncs = $proj['fld_showglobfuncs'];
+
+ $this->iDBCache->RefreshClasses();
+ $this->iDBCache->RefreshMethods();
+
+ $fmt = $this->NewClassFormatter($this->iDBCache,$aFlags);
+ $fmt->Init($this->iProjname,$proj['fld_desc'],$docType,$docdir,$showPrivate,$showGlobFuncs);
+ $ds = new DocStat($dbutils);
+ list($avg,$nc,$nm) = $ds->ProjStat($this->iProjname);
+ $fmt->InitStat($avg,$nc,$nm);
+ $fmt->Start();
+
+
+ // Format all the classes and their methods
+ $cr = new ClassReader($fmt,$this->iDBCache,$aFlags);
+ $cr->SetNumIndexCols($this->iNumIndexCols);
+ $cr->Run($aClass);
+
+ // Format all global functions
+ if( $showGlobFuncs ) {
+ $gf = new GlobalFuncReader($fmt,$this->iDBCache,$aFlags);
+ $gf->Run();
+ }
+
+ $fmt->End();
+
+ }
+}
+
+?>
--- /dev/null
+<?php
+//=======================================================================
+// File: JPD_EDITCLASS.PHP
+// Description: Edit class information
+// Created: 2001-04-15
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpd_editclass.php,v 1.3 2002/07/03 23:32:12 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2001,2002 Johan Persson
+//=======================================================================
+
+include "jpdb.php";
+include "de_utils.php";
+
+class EditClassTable extends DBTableEdit {
+ function EditClassTable($aDBUtils,$aTitle) {
+ parent::DBTableEdit('editclass',$aDBUtils);
+
+ $aDBUtils->GetClassList($cl);
+ $yn = array(" Private ",0," Public ",1);
+ $formSpec = array(
+ array('file',1,1,2,'File:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_STATICTEXT),
+ array('linenbr',1,3,1,'Line:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_STATICTEXT),
+ array('desc',2,1,3,'Description:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TEXTAREA,60,20),
+ array('ref1',3,1,1,'See also:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_DROPDOWN,$cl),
+ array('ref2',3,2,1,' ',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_DROPDOWN,$cl),
+ array('ref3',4,1,1,'',LBLPOS_LEFT,FLDPOS_LEFT,FLDTYPE_DROPDOWN,$cl),
+ array('ref4',4,2,1,'',LBLPOS_LEFT,FLDPOS_LEFT,FLDTYPE_DROPDOWN,$cl),
+ array('public',5,1,1,'Scope:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_DROPDOWNCODE,$yn),
+ array('timestamp',5,2,1,'Last edit:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TIMESTAMP),
+ array('_x_save',5,3,1,'',LBLPOS_LEFT,FLDPOS_RIGHT,FLDTYPE_SAVE),
+ );
+
+ $hidden = array('name','parentname','numfuncs');
+ $this->Set('tbl_class'.$aDBUtils->iProject,$formSpec,$hidden);
+ $this->iFormLayout->SetTitle($aTitle);
+ }
+
+ function Verify($aValues,&$aErrFld) {
+ return true;
+ }
+}
+
+class ClassEdDriver extends DocEditDriver {
+ function ClassEdDriver() {
+ $this->DocEditDriver();
+ }
+ function Run($aKey='') {
+ global $HTTP_POST_VARS;
+
+ HTMLGenerator::DocHeader('Edit DDDA Class','Modify or create existing class');
+ HTMLGenerator::DocPreamble();
+
+ $HTTP_POST_VARS['key'] = $aKey;
+ $r = $this->iDBUtils->GetClassKey($aKey);
+
+ if( !empty($r['fld_parentname']) )
+ $ext = ' <font color=lightgrey>extends '.$r['fld_parentname'].'</font>';
+ else
+ $ext = '';
+
+ $e = new EditClassTable($this->iDBUtils,$r['fld_name'].$ext);
+ $e->Run($HTTP_POST_VARS);
+ }
+}
+
+$d = new ClassEdDriver();
+$key = @$HTTP_GET_VARS['key']+0;
+$d->Run($key);
+$d->Close();
+
+?>
--- /dev/null
+<?php
+//=======================================================================
+// File: JPD_EDITMETHOD.PHP
+// Description: Edit method information
+// Created: 2001-04-15
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpd_editmethod.php,v 1.5 2003/01/30 15:02:03 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+
+include "jpdb.php";
+include "de_utils.php";
+
+class EditMethodTable extends DBTableEdit {
+ function EditMethodTable($aDBUtils,$aNumArgs=0,$aTitle='Edit method') {
+ parent::DBTableEdit('editmethod',$aDBUtils);
+
+ $aDBUtils->GetMethodList($ml);
+ $yn = array(" Private ",0," Public ",1);
+ // (fldname,row,col,span,label,labelpos,fldpos,fldtype,targ1,targ2)
+ $formSpec = array(
+ array('shortdesc',1,1,3,'Summary:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TEXTINPUT,55,200),
+ array('return',$aNumArgs+2,1,3,'Returns:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TEXTINPUT,55,200),
+ array('desc',$aNumArgs+3,1,3,'Description:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TEXTAREA,60,6),
+ array('methref1',$aNumArgs+4,1,3,'See also:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_DROPDOWNCODE,$ml),
+ array('methref2',$aNumArgs+5,1,3,'',LBLPOS_LEFT,FLDPOS_LEFT,FLDTYPE_DROPDOWNCODE,$ml),
+ array('methref3',$aNumArgs+6,1,3,'',LBLPOS_LEFT,FLDPOS_LEFT,FLDTYPE_DROPDOWNCODE,$ml),
+ array('methref4',$aNumArgs+7,1,3,'',LBLPOS_LEFT,FLDPOS_LEFT,FLDTYPE_DROPDOWNCODE,$ml),
+ array('example',$aNumArgs+8,1,3,'Example:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TEXTAREA,60,4),
+ array('public',$aNumArgs+9,1,1,'Scope:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_DROPDOWNCODE,$yn),
+ array('timestamp',$aNumArgs+9,2,1,'Last edit:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TIMESTAMP),
+ array('_x_save',$aNumArgs+9,3,1,'',LBLPOS_LEFT,FLDPOS_RIGHT,FLDTYPE_SAVE),
+ );
+ if( $aNumArgs > 0 ) {
+ $r = 2;
+ $formSpec[] = array('arg1', $r,1,1,'Argument:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TEXTINPUT,20,80,true);
+ $formSpec[] = array('argdes1', $r,2,2,'Description:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TEXTINPUT,30,80);
+ ++$r;
+ for( $i=2; $i <= $aNumArgs; ++$i ) {
+ $formSpec[] = array('arg'.$i, $r,1,1,'',LBLPOS_LEFT,FLDPOS_LEFT,FLDTYPE_TEXTINPUT,20,80,true);
+ $formSpec[] = array('argdes'.$i, $r,2,2,'',LBLPOS_LEFT,FLDPOS_LEFT,FLDTYPE_TEXTINPUT,30,80);
+ ++$r;
+ }
+ }
+ $hidden = array('file','linenbr','classidx','numargs','name','classname');
+ $this->Set('tbl_method'.$aDBUtils->iProject,$formSpec,$hidden);
+ $this->iFormLayout->SetTitle($aTitle);
+ }
+
+ function Verify($aValues,&$aErrFld) {
+ return true;
+ }
+}
+
+class MethEdDriver extends DocEditDriver {
+ function MethEdDriver() {
+ $this->DocEditDriver();
+ }
+ function Run($aKey='') {
+ global $HTTP_POST_VARS;
+
+ HTMLGenerator::DocHeader('Edit DDDA Class','Modify or create existing class');
+ HTMLGenerator::DocPreamble();
+
+ $HTTP_POST_VARS['key'] = $aKey;
+ $r = $this->iDBUtils->GetMethodKey($aKey);
+ $title = '<b><font face=arial>'.$r['fld_classname'].'::'.$r['fld_name'].'()</b></font>';
+ $e = new EditMethodTable($this->iDBUtils,$r['fld_numargs'],$title);
+ $e->Run($HTTP_POST_VARS,array('name'=>'<b><font face=arial>'.$r['fld_classname'].'::'.$r['fld_name'].'()</b></font>'));
+ }
+}
+
+$d = new MethEdDriver();
+$key = @$HTTP_GET_VARS['key']+0;
+$d->Run($key);
+$d->Close();
+
+?>
--- /dev/null
+<?php
+//=======================================================================
+// File: jpd_editproject.php
+// Description: Form for edit a specified project
+// Created: 2002-05-03
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpd_editproject.php,v 1.12.2.1 2003/08/11 12:03:43 aditus Exp $
+//
+// License: QPL 1.0
+// Copyright (C) 2002 Johan Persson
+//=======================================================================
+
+include "jpdb.php";
+include "de_utils.php";
+
+
+class EditProjects extends DBTableEdit {
+ var $iLabelShow = ' Show ';
+ var $iLabelShowFiles = ' Project files... ';
+ function EditProjects($aDBUtils) {
+ parent::DBTableEdit('projects',$aDBUtils,'jpd_editproject.php');
+ $doctype = array(" HTML:Separate files ",0," HTML:Single file ",1);
+ $formSpec = array(
+ array('name',1,1,1,'Project name:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TEXTINPUT,38,40),
+ //array('lang',1,2,1,'Language:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TEXTINPUT,10,20),
+ array('timestamp',1,2,1,'Last edit:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TIMESTAMP),
+ array('projdir',2,1,2,'Project directory:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TEXTINPUT,50,80),
+ array('desc',3,1,2,'Description:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TEXTAREA,50,5),
+ array('docdir',4,1,1,'Output directory:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TEXTINPUT,38,80),
+ array('doctype',4,2,1,'Output format:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_DROPDOWNCODE,$doctype),
+ array('showprivate',5,1,2,'Include private methods & classes',LBLPOS_RIGHT,FLDPOS_LEFT,FLDTYPE_CHECKBOX),
+ array('showglobfuncs',6,1,2,'Include global functions',LBLPOS_RIGHT,FLDPOS_LEFT,FLDTYPE_CHECKBOX),
+ array('',7,2,1,'',LBLPOS_LEFT,FLDPOS_RIGHT,FLDTYPE_DELETECLEARSAVE),
+ array('show_files',7,1,1,'',LBLPOS_LEFT,FLDPOS_LEFT,FLDTYPE_SUBMIT,$this->iLabelShowFiles),
+ array('_x_allprojects',9,1,2,'Existing projects:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_DROPDOWNCODE,MODIFY_HOOK,3),
+ array('show_project',10,1,1,'',LBLPOS_LEFT,FLDPOS_LEFT,FLDTYPE_SUBMIT,$this->iLabelShow),
+ array('',8,1,2,'',LBLPOS_LEFT,FLDPOS_RIGHT,FLDTYPE_HRULE)
+ );
+ $this->Set('tbl_projects',$formSpec);
+ $this->iFormLayout->SetTitle('Edit projects');
+ $this->iFormLayout->SetButtonLabels(' Save ',' Delete ',' Clear ');
+ }
+
+
+ function PreDeleteHook($aKey) {
+ $this->iDBUtils->DropProjTables($aKey);
+ return true;
+ }
+
+ function PostDeleteHook($aKey) {
+ return true;
+ }
+
+
+ function Validate($aValues,&$aErrFld) {
+ $r = true;
+
+ $pos = strpos($aValues['name'], ' ');
+ if( trim($aValues['name']) == '' || is_integer($pos) ) {
+ $aErrFld['name'] = 'Project name: Name must not contain spaces.';
+ $r = false;
+ }
+
+ if( strlen(trim($aValues['name'])) < 4 ) {
+ $aErrFld['name'] = 'Project name: Name must be at least be 4 characters.';
+ $r = false;
+ }
+
+
+ if( substr(trim($aValues['docdir']),-1) !='/' ) {
+ $aErrFld['docdir'] = 'Output directory: Must end with a "/" charcter.';
+ $r = false;
+ }
+
+ if( @is_dir($aValues['docdir'])==false ) {
+ $aErrFld['docdir'] = 'Output directory does not exist or is not a directory.';
+ $r = false;
+ }
+
+ if( trim($aValues['desc']) == '' ) {
+ $aErrFld['desc'] = 'Description: You must enter a description.';
+ $r = false;
+ }
+
+ if( !$r ) return false;
+
+ if( $aValues['key'] == '' ) { // Must be a new post
+ $this->iDBUtils->CreateNewTablesForProject($aValues['name']);
+ }
+
+ return true;
+ }
+
+ function ModifyArrayHook($aName,&$aArr) {
+ // We only have one hook so er don't bother to check that
+ // it really is _x_allfiles
+ $this->iDBUtils->GetProjects($aArr,90);
+ }
+
+ function ModifyValuesHook(&$aValues) {
+ // Get's called just before the new post is read
+ if( !empty($aValues['show_project']) &&
+ $aValues['show_project'] == $this->iLabelShow &&
+ !empty($aValues['_x_allprojects']) ) {
+ $aValues['key'] = $aValues['_x_allprojects'];
+ }
+ }
+
+}
+
+
+class EditProjectFiles extends DBTableEdit {
+ var $iLabelShow = ' Show ';
+ var $iLabelBackToProjects = 'Back To Project';
+ var $iProjIdx;
+ function EditProjectFiles($aDBUtils,$aProjIdx) {
+ parent::DBTableEdit('projfiles',$aDBUtils,'jpd_editproject.php');
+ $this->iProjIdx = $aProjIdx;
+ // Note: The prefix '_x_' indicates a field NOT in the DB
+ $formSpec = array(
+ array('projectname',1,1,2,'Project name:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_NONDBTEXT),
+ array('name',2,1,1,'Filename:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TEXTINPUT,30,255),
+ array('desc',3,1,2,'Description:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_TEXTAREA,50,2),
+ array('dbupdtime',2,2,1,'DB Updated:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_STATICTEXT),
+ array('timestamp',4,1,1,'Last edit:',LBLPOS_LEFT,FLDPOS_LEFT,FLDTYPE_TIMESTAMP),
+ array('',5,2,1,'',LBLPOS_LEFT,FLDPOS_RIGHT,FLDTYPE_DELETECLEARSAVE),
+ array('show_projects',5,1,1,'',LBLPOS_LEFT,FLDPOS_LEFT,FLDTYPE_SUBMIT,$this->iLabelBackToProjects),
+ array('_x_allfiles',7,1,2,'Existing files in project:',LBLPOS_TOP,FLDPOS_LEFT,FLDTYPE_DROPDOWNCODE,MODIFY_HOOK,10),
+ array('show_file',8,1,1,'',LBLPOS_LEFT,FLDPOS_LEFT,FLDTYPE_SUBMIT,$this->iLabelShow),
+ array('',6,1,2,'',LBLPOS_LEFT,FLDPOS_RIGHT,FLDTYPE_HRULE),
+ );
+
+ $hidden = array('projidx');
+ $this->Set('tbl_projfiles',$formSpec,$hidden);
+ $this->iFormLayout->SetTitle('Project files');
+ $this->iFormLayout->SetButtonLabels(' Save ',' Delete ',' Clear ');
+ }
+
+ function Validate($aValues,&$aErrFld) {
+
+ $pos = strpos($aValues['name'], ' ');
+ if( trim($aValues['name']) == '' || is_integer($pos) ) {
+ $aErrFld['name'] = 'Name must not contain spaces.';
+ return false;
+ }
+ else
+ return true;
+ }
+
+ function PreDeleteHook($aKey) {
+ return true;
+ }
+
+
+ function ModifyArrayHook($aName,&$aArr) {
+ // We only have one hook so er don't bother to check that
+ // it really is _x_allfiles
+ $this->iDBUtils->GetProjectFiles($aArr,$this->iProjIdx,75);
+ }
+
+ function ModifyValuesHook(&$aValues) {
+ // Get's called just before the new post is read
+ if( !empty($aValues['show_file']) &&
+ $aValues['show_file'] == $this->iLabelShow &&
+ !empty($aValues['_x_allfiles']) ) {
+ $aValues['key'] = $aValues['_x_allfiles'];
+ }
+
+ // After a clear (or delete) all values are cleared
+ // so we need to restore the project index
+ $aValues['projidx'] = $this->iProjIdx ;
+ }
+}
+
+
+class ProjectDriver extends DocEditDriver {
+ function Run() {
+ global $HTTP_POST_VARS;
+ global $HTTP_GET_VARS;
+
+ // We should go back to the project file form if we
+ // a) either came from that form with a save/delete
+ // b) we are going to that form from the project form
+ if( empty($HTTP_POST_VARS['show_projects']) && ((!empty($HTTP_POST_VARS['show_files']) && !empty($HTTP_POST_VARS['key']) &&
+ $HTTP_POST_VARS['key'] > 0 && $HTTP_POST_VARS['_x_formname']=='projects')
+ || @$HTTP_POST_VARS['_x_formname']=='projfiles') ) {
+ if( @$HTTP_POST_VARS['_x_formname']=='projfiles' )
+ $projkey = $HTTP_POST_VARS['projidx'];
+ else {
+ if( !empty($HTTP_POST_VARS['_x_allprojects']) )
+ $projkey = $HTTP_POST_VARS['_x_allprojects'];
+ else
+ $projkey = $HTTP_POST_VARS['key'];
+ $HTTP_POST_VARS = array();
+ }
+ HTMLGenerator::DocHeader('Edit DDDA Project Files','Modify or Add Files To Project');
+ HTMLGenerator::DocPreamble();
+ GenJavascript::Stroke();
+
+ $e = new EditProjectFiles($this->iDBUtils,$projkey);
+ $pname = $this->iDBUtils->GetProjNameForKey($projkey);
+ $e->Run($HTTP_POST_VARS,array('projectname' => '<b>'.$pname.'</b>'));
+ HTMLGenerator::CloseWinButton();
+ }
+ else {
+ HTMLGenerator::DocHeader('Edit DDDA Project','Modify or create new DDDA projects');
+ HTMLGenerator::DocPreamble();
+ GenJavascript::Stroke();
+
+ // For the case when we return from prtojfiles we set the current project key
+ // so we get back to the same project
+ if( @$HTTP_POST_VARS['_x_formname']=='projfiles' ) {
+ $key = @$HTTP_POST_VARS['projidx'];
+ $HTTP_POST_VARS = array();
+ $HTTP_POST_VARS['key'] = $key;
+ $HTTP_POST_VARS['_x_allprojects'] = $key;
+ }
+
+ // Special case if we open the window to create a new project (start with empty form)
+ if( empty($HTTP_GET_VARS['new']) && strlen(trim($this->iProjname)) > 0 && count($HTTP_POST_VARS)==0 ) {
+ $r = $this->iDBUtils->GetProject($this->iProjname);
+ $HTTP_POST_VARS['key'] = $r['fld_key'];
+ }
+
+ $e = new EditProjects($this->iDBUtils);
+ $e->Run($HTTP_POST_VARS);
+
+ HTMLGenerator::CloseWinButton();
+ }
+ }
+}
+
+$driver = new ProjectDriver();
+if( !empty($HTTP_GET_VARS['projkey']) )
+ $key = $HTTP_POST_VARS['projkey'] = $HTTP_GET_VARS['projkey'];
+$driver->Run();
+$driver->Close();
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//==============================================================================
+// Name: JPDB.PHP
+// Description: OO DB interface. Currently geared towards mysql
+// Created: 2002-03-17
+// Author: johanp@aditus.nu
+// Version: $Id: jpdb.php,v 1.3 2002/08/25 22:03:51 aditus Exp $
+//
+// License: QPL 1.0
+// Copyright (C) 2001,2002 Johan Persson
+//==============================================================================
+
+
+// SQL Result object. Returned after a query.
+class DBResult {
+ var $iDB;
+ var $iRes;
+ var $iDBRow,$iDBObj;
+ var $iNumRows = -1;
+ var $iNumFields = -1;
+
+ function DBResult($aDB,$aRes) {
+ $this->iDB = $aDB;
+ $this->iRes = $aRes;
+ }
+
+ function Fetch() {
+ $this->iDBRow = mysql_fetch_array($this->iRes);
+ return $this->iDBRow;
+ }
+
+ function FetchObj() {
+ $this->iDBObj = mysql_fetch_object($this->iRes);
+ return $this->iDBObj;
+ }
+
+ function NumRows() {
+ if( $this->iNumRows == -1 )
+ $this->iNumRows = mysql_num_rows($this->iRes);
+ return $this->iNumRows;
+ }
+
+ function NumFields() {
+ if( $this->iNumFields == -1 )
+ $this->iNumFields = mysql_num_fields($this->iRes);
+ return $this->iNumFields;
+ }
+
+ function GetFieldNames() {
+ $nbr = $this->NumFields();
+ $flds = array();
+ while( $nbr > 0 ) {
+ $meta = mysql_fetch_field($this->iRes);
+ $flds[] = $meta->name;
+ --$nbr;
+ }
+ }
+}
+
+
+// Abstraction for a DB server
+class DBServer {
+
+ var $iDBName;
+ var $iLastErr;
+
+ var $iUserID ;
+ var $iUserPWD ;
+ var $iServer ;
+ var $iLink;
+ var $iDie = true;
+
+ // If DryRun then no action on DB will be taken and all
+ // DB calls will succeed
+ var $iDryRun = false ;
+
+ function DBServer($aUser,$aPWD,$aServer="localhost") {
+ if( $this->iDryRun ) {
+ $this->iUserID = "DryRunUser";
+ $this->iUserPWD = "DryRunPWD";
+ $this->iServer = "DryRunServer";
+ return true;
+ }
+ $this->iLink = @mysql_connect($aServer,$aUser,$aPWD);
+ if( $this->iLink == false ) {
+ $this->SetError("Can't connect to server $aServer as $aUser");
+ return false;
+ }
+ $this->iUserID = $aUser;
+ $this->iUserPWD = $aPWD;
+ $this->iServer = $aServer;
+ return true;
+ }
+
+ function SetDB($aDBName,$aIgnoreError=false) {
+ if( $this->iDryRun ) {
+ $this->iDBName = "DryRunDBName";
+ return true;
+ }
+ $this->iDBName = $aDBName;
+ if( @mysql_select_db($aDBName) )
+ return true;
+ else {
+ if( !$aIgnoreError )
+ $this->SetError("Can't select database $aDBName.");
+ return false;
+ }
+ }
+
+ function SetDryRun($aFlg=true) {
+ $this->iDryRun = $aFlg;
+ }
+
+ function SetError($aMsg) {
+ if( $this->iDryRun ) {
+ return;
+ }
+ if( $this->iLink )
+ $err = mysql_error($this->iLink);
+ else
+ $err="";
+ $this->iLastErr = "<b>DB ERROR:</b>".$aMsg."<br>MySQL Error:".$err;
+ if( $this->iDie )
+ die($this->iLastErr);
+ }
+
+
+ function Query($aQuery,$aIgnoreError=false) {
+ if( $this->iDryRun ) {
+ return true;
+ }
+ $res=@mysql_query($aQuery,$this->iLink);
+ if( !$res && !$aIgnoreError ) {
+ $this->SetError("Error in query:<br> $aQuery<P>");
+ return false;
+ }
+ if( $res > 0 )
+ return new DBResult($this,$res);
+ return false;
+ }
+
+ function LastIdx() {
+ if( $this->iDryRun ) {
+ return 0;
+ }
+ return mysql_insert_id($this->iLink);
+ }
+
+ function Create($aDBName) {
+ mysql_create_db($aDBName,$this->iLink);
+ }
+
+ function Close() {
+ if( $this->iDryRun ) {
+ return;
+ }
+ mysql_close($this->iLink);
+ }
+
+ function GetTables() {
+ $r = mysql_list_tables($this->iDBName,$this->iLink);
+ $n = mysql_num_rows($r);
+ $tn = array();
+ for( $i=0; $i < $n; ++$i ) {
+ $tn[$i] = mysql_tablename($r,$i);
+ }
+ return $tn;
+ }
+
+ function GetFields($aTbl) {
+ $r = mysql_list_fields($this->iDBName,$aTbl,$this->iLink);
+ $n = mysql_num_fields($r);
+ $fn = array();
+ for( $i=0; $i < $n; ++$i ) {
+ $fn[$i] = mysql_field_name($r,$i).' : '. mysql_field_type($r,$i);
+ }
+ return $fn;
+ }
+
+ function GetTablesFields($aFlgPrint=false) {
+ $tblnames = $this->GetTables();
+ $n = count($tblnames);
+ $res = array();
+ for( $i=0; $i < $n; ++$i ) {
+ $fn = $this->GetFields($tblnames[$i]);
+ if( $aFlgPrint ) {
+ echo '<b>'.$tblnames[$i].'</b><br>';
+ $nn = count($fn);
+ for( $j=0; $j < $nn; ++$j ) {
+ echo $fn[$j]; //[0]. ': <font color=blue>' . $fn[$j][1].'</font><br>';
+ }
+ echo '<p>';
+ }
+ $res[$i] = array($tblnames[$i],$fn);
+ }
+ return $res;
+ }
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//==============================================================================
+// Name: JPDBDELCLASS.PHP
+// Description: Deletes a class from the documentation DB
+// Created: 2002-04-14
+// Author: johanp@aditus.nu
+// Version: $Id: jpdbdelclass.php,v 1.3 2002/06/05 22:39:14 aditus Exp $
+//
+// License: QPL 1.0
+// Copyright (C) 2002 Johan Persson
+//
+//==============================================================================
+
+include ("jpdb.php");
+include ("de_utils.php");
+
+DEFINE("DELDB_OVERRIDE_REFCHK",1);
+DEFINE("DELDB_OVERRIDE_EXTCHK",2);
+
+class RemoveDBClass {
+ var $iDB;
+ function RemoveDBCLass($aDB) {
+ $this->iDB = $aDB;
+ }
+
+ function Run($aProjname,$aClass,$aFlags=0) {
+
+ // Get some info about this class
+ $q = 'SELECT * FROM tbl_class_'.$aProjname.' WHERE fld_name='."'$aClass'";
+ $res = $this->iDB->Query($q);
+ if( $res->NumRows() == 0 ) {
+ Utils::Error("Can't find class '$aClass' in DB.");
+ return false;
+ }
+
+ $class = $res->Fetch();
+
+ // As a safety check we don't allow deleting classes who's
+ // methods are referenced by the method references in other classes.
+
+ $q = "SELECT DISTINCT CONCAT(c.fld_name,'::<b>',b.fld_name,'</b>()') as fld_name
+ FROM tbl_method_".$aProjname." as a,tbl_method_".$aProjname." as b,tbl_class_".$aProjname." as c
+ WHERE a.fld_classidx=".$class['fld_key']." AND
+ c.fld_key = b.fld_classidx AND
+ (b.fld_methref1=a.fld_key OR
+ b.fld_methref2=a.fld_key OR
+ b.fld_methref3=a.fld_key OR
+ b.fld_methref4=a.fld_key OR
+ b.fld_methref5=a.fld_key)
+ ";
+ $res = $this->iDB->Query($q);
+ $n = $res->NumRows();
+ if( $n > 0 && !($aFlags & DELDB_OVERRIDE_REFCHK)) {
+ $s = '';
+ while( $n > 0 ) {
+ $row = $res->Fetch();
+ $s .= $row['fld_name'];
+ if( $n > 1 ) $s .= ",<br> ";
+ --$n;
+ }
+ Utils::Error("Can't delete class '$aClass' since it's methods are referenced by:<br>$s<p>");
+ return false;
+ }
+
+ // As another safety check we don't allow deleting classes which
+ // are superclasses to some other classes in the DB
+
+ $q = 'SELECT fld_name FROM tbl_class_'.$aProjname.' WHERE fld_parentname='."'$aClass'";
+ $res = $this->iDB->Query($q);
+ $n = $res->NumRows();
+ if( $n > 0 && !($aFlags & DELDB_OVERRIDE_EXTCHK) ) {
+ $s = '';
+ while( $n > 0 ) {
+ $row = $res->Fetch();
+ $s .= "CLASS ".$row['fld_name'];
+ if( $n > 1 ) $s .= ",<br> ";
+ --$n;
+ }
+ Utils::Error("Can't delete class '$aClass' since it is inherited by:<br>$s<p>");
+ return false;
+ }
+
+ // Checks are done. Point of no return. Now really delete class
+
+ // Start by deleting all methods in this class
+ $q = "DELETE FROM tbl_method_".$aProjname." WHERE fld_classidx=".$class['fld_key'];
+ $this->iDB->Query($q);
+
+ // .. and all instance variables
+ $q = "DELETE FROM tbl_classvars_".$aProjname." WHERE fld_classidx=".$class['fld_key'];
+ $this->iDB->Query($q);
+
+ // ... and the class
+ $q = "DELETE FROM tbl_class_".$aProjname." WHERE fld_name='".$aClass."'";
+ $this->iDB->Query($q);
+
+ return true;
+ }
+}
+
+// Driver
+class Driver {
+ var $iDB;
+ var $iProjname,$iProjidx;
+
+ function Driver() {
+ global $HTTP_COOKIE_VARS;
+ $this->iDB = DBFactory::InitDB();
+
+ $this->iProjname = strtok(@$HTTP_COOKIE_VARS['ddda_project'],':');
+ if( $this->iProjname != '' ) {
+ $this->iProjidx = strtok(':');
+ }
+ else
+ die('No project specified.');
+ }
+
+ function Run($aClass,$aFlags=0) {
+ $dbremove = new RemoveDBClass($this->iDB);
+ if( $dbremove->Run($this->iProjname,$aClass,$aFlags) )
+ echo "Class <b>$aClass</b> has been removed from JpGraph Database.";
+ else
+ Utils::Error("Can't remove class <b>$aClass</b> from JpGRaph Database.");
+ }
+}
+
+
+//==========================================================================
+// Script entry point
+// Read URL argument and create Driver
+//==========================================================================
+if( !isset($HTTP_GET_VARS['class']) )
+ Utils::Error("Must specify class to remove from db. Use syntax class=<class-name>");
+else
+ $class = urldecode($HTTP_GET_VARS['class']);
+
+$driver = new Driver();
+$driver->Run($class);
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//=======================================================================
+// File: JPDOCEDIT.PHP
+// Description: Main entry for editing information in the doc DB
+// Created: 2002-04-15
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpdocedit.php,v 1.22.2.2 2003/08/11 12:12:18 aditus Exp $
+//
+// License: QPL 1.0
+// Copyright (C) 2002 Johan Persson
+//=======================================================================
+
+include_once "jpdb.php";
+include "de_utils.php";
+include 'jpgendb.php';
+
+class IndexPage {
+ var $iDBUtils;
+
+ function IndexPage($aDBUtils) {
+ $this->iDBUtils = $aDBUtils;
+ }
+
+ function Run($aProjidx,$aExpandMethods=false,$aCols=3,$aProjName='') {
+ $cl = array();
+ $this->iDBUtils->GetClassListNameKeyPublic($cl);
+ $nc = (count($cl)-3)/3;
+ $ds = new DocStat($this->iDBUtils);
+ $total = 0;
+ $ct = array();
+ $npercol = round($nc / $aCols);
+ $limit = 0;
+ $start = 1;
+ for( $c=0; $c < $aCols; ++$c ) {
+ $limit = $c==($aCols-1) ? $nc : $limit+$npercol;
+ $t = '';
+ $marker1 = '<span style="font-family:arial;font-size:110%;font-weight:bold;color:darkblue;">[</span>';
+ $marker2 = '<span style="font-family:arial;font-size:110%;font-weight:bold;color:darkblue;">]</span>';
+
+ for( $i = $start; $i <= $limit; ++$i ) {
+ list($cname,$nm,$ma,$tp,$ap) = $ds->ClassStat($cl[3*$i+1]);
+ $classpublic = $cl[3*$i+2];
+ $mk1=''; $mk2='';
+ if( $classpublic == 0 ) {
+ $mk1 = $marker1;
+ $mk2 = $marker2;
+ }
+
+ $pcolor = round(100*$ap/$tp,1) == 100 ? 'darkgreen' : 'red';
+ $p = round(100*$ap/$tp);
+ $total += $p;
+ $t .= "<tr><td style=\"font-family:arial;font-size:80%;\"><b>$i. <font color=".
+ $pcolor."><b>[".sprintf("%3d",$p)."%]</b> </font>$mk1<a href=\"javascript:openPopup('jpd_editclass.php?key=".$cl[3*$i+1]."',550,590);\">$cname</a>$mk2</b></td></tr>\n";
+ if( $aExpandMethods ) {
+ for( $j=0; $j < $nm; ++$j ) {
+ $mk1=''; $mk2='';
+ if( $ma[$j][0]==0 ) {
+ $mk1 = $marker1;
+ $mk2 = $marker2;
+ }
+ // Adjust the title a little bit to make the columns nicer
+ $l = $j+1 < 10 ? ($j+1).' ' : ''.$j+1;
+ $pcolor = round($ma[$j][3]) == 100 ? 'darkgreen' : 'red';
+ $t .= "<tr><td style=\"font-family:arial;font-size:80%;\"> $i.".$l." <font face=courier color=$pcolor>[".sprintf("%3d",$ma[$j][3])."%]</font> $mk1<a href=\"javascript:openPopup('jpd_editmethod.php?key=".$ma[$j][2]."',560,670);\">".$ma[$j][1]."$mk2</a></td></tr>";
+ }
+ }
+ }
+ $start += $npercol;
+ $ct[] = "<table width=\"100%\" border=0 cellpadding=4>\n$t</table>" ;
+ }
+
+ // Get all global functions
+ list($cname,$nm,$ma,$tp,$ap) = $ds->ClassStat(0);
+ $gfuncs=array();
+ $res=$this->iDBUtils->GetMethodListForClassKey($gfuncs,0);
+ $n = count($gfuncs)/2;
+ $gf='';
+ if( $n > 0 ) {
+ $gf='<p> <table border=0><tr><td style="font-family:arial;font-weight:bold;font-size:90%;">Global functions</td></tr>';
+ for( $i=0; $i<$n; ++$i ) {
+ $mk1=''; $mk2='';
+ if( $ma[$i][0]==0 ) {
+ $mk1 = $marker1;
+ $mk2 = $marker2;
+ }
+
+ $gf .= "<tr><td style=\"font-family:arial;font-size:80%;\"> ".($i+1).". <a href=\"javascript:openPopup('jpd_editmethod.php?key=".$ma[$i][2]."',560,670);\"><font face=courier color=darkred>[".sprintf("%3d",$ma[$i][3]).'%] </font>'.$mk1.$ma[$i][1]."$mk2</a></td></tr>";
+ }
+ $gf .= '</table>';
+ }
+
+
+ if( $nc > 0 )
+ $avg = round($total/$nc);
+ else
+ $avg = 0;
+ $w = round(100/$aCols);
+
+
+ $t = "<form name='mainform' method=post action=''>";
+ $t .= "<table border=0 cellspacing=0 width=100%><tr><td style='border-bottom:solid black 1pt;' valign=top>";
+ $projdoc = $this->iDBUtils->GetProjDir($aProjName).'index.html';
+
+ $t .= "\n<input name=\"button_gendoc\" type=button value=' Create doc ' onclick=\"openPopup('jpgenhtmldoc.php',400,500,'Update docs');\"> ";
+ $t .= "\n<input name=\"button_opendoc\" type=button value=\" Open doc \" onclick=\"openPopup('$projdoc',800,500,'Documentation:$aProjName');\">";
+ $t .= "</td><td style='border-bottom:solid black 1pt;'>\n<input name=\"button_regen\" type=button value=\" Update DB \" onclick=\"openPopup('jpgendbdriver.php?force='+mainform.chkbox_timestamp.checked,350,450,'Update DB');\"> ";
+ $t .= "\n<input name=\"chkbox_timestamp\" type=checkbox value=1> Force ";
+
+ $t .= "\n</td><td style='border-bottom:solid black 1pt;' valign=top align=right>";
+ $t .= "<input type=button value=' Close ' onclick='window.close();'>";
+ $t .= "</td></tr></table></form>";
+ $t .= "\n<table width=100% cellpadding=5 cellspacing=0 class=projectindex>\n" ;
+ $t .= "<tr><td colspan=$aCols class=projindexheader>$aProjName</td></tr>";
+ $t .= "<tr><td colspan=$aCols style=\"background:lightgrey;font-family:arial;color:#400080;\">";
+ $t .= "<b>Documentation status: <span style=\"color:#B01400;\">$avg %</span></b> ($nc classes)</td></tr>\n";
+ $tt = '<tr>';
+ $tt .= "<td width=".$w."% valign=top >$ct[0]</td>";
+
+ for( $i=1; $i < $aCols; ++$i ) {
+ $tt .= "<td width=".$w."% valign=top style='border-left: black solid 0.5pt;' >$ct[$i]";
+ if( $i < $aCols-1 )
+ $tt .= "</td>";
+ else
+ $tt .= "$gf</td>";
+ }
+ return $t.$tt.'</table>';
+ }
+}
+
+
+class DocEditMainEntry {
+ var $iDBUtils;
+
+ function DocEditMainEntry($aDBUtils) {
+ $this->iDBUtils = $aDBUtils;
+ }
+
+ function StrokeMeny($aProjname,$aProjidx) {
+
+ if( !$this->iDBUtils->ExistTableProjects() ) {
+
+ echo( "First time for this database. Initializing... " );
+ $this->iDBUtils->SetupNewDB();
+ echo( "done.");
+ }
+
+ $pl = array(' -- Select project -- ',-1);
+ $this->iDBUtils->GetProjects($pl);
+ $aHTML = new HTMLGenerator();
+
+ $t = "<form name='mainform' method=POST action=''>";
+ $t .= "<div align=center><table width=100% border=0 cellspacing=0 cellpadding=5 class='mainmeny'>";
+ $t .= '<tr><td colspan=2 class=mainmeny><span class=menytitle>Project meny</span></td></tr>';
+ $t .= "<tr><td>".$aHTML->SelectCode('projidx',$pl,$aProjidx,1,'onchange="setprojcookie();"');
+ $t .= "</td><td align=right><input name=\"button_showidx\" type=button value=\"Show\" onclick=\"openPopup('jpdocedit.php?button_showidx=1&showmethod=1',700,450,'DDDA Project:');\"> ";
+ $t .= "<input type=button value=\"Edit project\" onclick=\"openPopup('jpd_editproject.php',520,540,'Edit current project');\"></form>";
+ $t .= "</td></tr>";
+ $t .= "<tr><td><form name='newmainform' method=POST action=''><input type=button value=\"New project\" onclick=\"openPopup('jpd_editproject.php?new=1',470,540,'New project');\">";
+ $t .= "</td><td align=right><input type=button value=\"DB Check\" onclick=\"openPopup('ddda_chkdb.php?idx='+mainform.projidx.value,450,450,'DB Check');\">";
+ $t .= "</form></td></tr>";
+ $t .= "</table></div>";
+
+ echo $t;
+ }
+}
+
+
+class DocEdDriver extends DocEditDriver {
+
+ function PreAmble($aTitle,$aDesc) {
+ HTMLGenerator::DocHeader($aTitle,$aDesc);
+ HTMLGenerator::DocPreamble();
+ GenJavascript::Stroke();
+ }
+
+ function Run() {
+ global $HTTP_POST_VARS;
+ global $HTTP_GET_VARS;
+
+ $button_toggle = @$HTTP_POST_VARS['button_toggle'];
+ $button_showidx = @$HTTP_GET_VARS['button_showidx'];
+ $button_regen = @$HTTP_POST_VARS['button_regen'];
+ $regen_projidx = @$HTTP_POST_VARS['regen_projidx'];
+ $showmethod = @$HTTP_GET_VARS['showmethod'] ;
+
+ if((!empty($button_toggle) || !empty($button_showidx)) && !empty($this->iProjidx) && $this->iProjidx > 0) {
+ $this->Preamble('DDDA Project: '.$this->iProjname,'DDDA Project: '.$this->iProjname);
+ $ip = new IndexPage($this->iDBUtils);
+ $txt = $ip->Run($this->iProjidx,$showmethod,3,$this->iProjname);
+ echo $txt;
+ }
+ else {
+ $this->Preamble(' DDDA Main Meny ',' DDDA Main Meny '.$this->iProjname);
+ $m = new DocEditMainEntry($this->iDBUtils);
+ $m->StrokeMeny($this->iProjname,$this->iProjidx);
+ }
+ }
+}
+
+$driver = new DocEdDriver();
+$driver->Run();
+$driver->Close();
+
+?>
--- /dev/null
+<?php
+//==============================================================================
+// Name: JPGENDB.PHP
+// Description: Use parsing classes to generate a documentation skeleton
+// Created: 01/12/03
+// Author: johanp@aditus.nu
+// Version: $Id: jpgendb.php,v 1.18 2002/08/29 10:12:43 aditus Exp $
+//
+// License: QPL 1.0
+// Copyright (C) 2002 Johan Persson
+//
+//==============================================================================
+include_once("jplintphp.php");
+include_once("jpdb.php");
+include_once("de_utils.php");
+
+class Log {
+ var $iDisplayLog=false;
+
+ // Used to log action
+ function ToScreen($aStr,$aLineBreak=true) {
+ if( $this->iDisplayLog ) {
+ echo "<font color=#FF0000>";
+ echo $aStr;
+ echo "</font>";
+ if( $aLineBreak )
+ echo "<br>\n";
+ }
+ }
+}
+
+$gLogger = new Log();
+
+class DBFuncProp extends FuncProp {
+ var $iKey="";
+ var $iDesc="";
+ var $iClassIdx="";
+ var $iMethodRef=array();
+ var $iArgsDes=array();
+ var $iExample="";
+ var $iProjname;
+ var $iPublic;
+
+ function DBFuncProp($aProjname,$aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortDesc="",$aFileName="") {
+ parent::FuncProp($aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortDesc,$aFileName);
+ $this->iProjname = $aProjname;
+ $this->iPublic = 1; // Default to public
+ }
+
+ function Internalize($aDBRow) {
+ }
+
+ function Externalize($aDB,$aClassIdx="",$aKey="") {
+ $this->iClassIdx = $aClassIdx;
+ $q = "REPLACE INTO tbl_method_".$this->iProjname." SET ";
+ $q .= "fld_key='".$aKey."',";
+ $q .= "fld_name='".$this->iName."',";
+ $q .= "fld_public=".$this->iPublic.",";
+ $q .= "fld_linenbr='".$this->iLineNbr."',";
+ $q .= "fld_file='".mysql_escape_string(basename($this->iFileName))."',";
+ $q .= "fld_shortdesc='".mysql_escape_string($this->iShortComment)."',";
+ $q .= "fld_classidx='".$aClassIdx."',";
+ $q .= "fld_classname='".$this->iClassName."',";
+ $q .= "fld_desc='".mysql_escape_string($this->iDesc)."',";
+ $q .= "fld_example='".mysql_escape_string($this->iExample)."',";
+
+ for( $i=1; $i<=4; ++$i ) {
+ $mref = '';
+ if( !empty($this->iMethodRef[$i-1]) )
+ $mref = $this->iMethodRef[$i-1];
+ $q .= "fld_methref$i='".$mref."',";
+ }
+ $q .= "fld_numargs=".$this->iNumArgs;
+ for( $i=1; $i<=$this->iNumArgs; ++$i ) {
+ $q .= ",fld_arg$i='".$this->iArgs[$i-1]."'";
+ }
+ for( $i=1; $i<=$this->iNumArgs; ++$i ) {
+ if( empty($this->iArgsDes[$i-1]) )
+ $argdes="";
+ else
+ $argdes = $this->iArgsDes[$i-1];
+ $q .= ",fld_argdes$i='".mysql_escape_string($argdes)."'";
+ }
+
+ for( $i=1; $i<=$this->iNumArgs; ++$i ) {
+ if( !isset($this->iArgsVal[$i-1]) )
+ $argval='';
+ else
+ $argval = $this->iArgsVal[$i-1];
+ $q .= ",fld_argval$i='".mysql_escape_string($argval)."'";
+ }
+
+ $aDB->query($q);
+ $this->iKey = $aDB->LastIdx();
+ return $this->iKey;
+ }
+
+ function UpdateFromExisting($aOldFunc) {
+ // Sanity check
+ if( $this->iName != $aOldFunc["fld_name"] )
+ die("PANIC: UpdateArguments() Function name different.".$this->iName." != ". $aOldFunc['fld_name'] );
+
+ $numoldargs=$aOldFunc["fld_numargs"];
+ $numnewargs=$this->iNumArgs;
+
+ // Get the old descriptions and references
+
+ $this->iDesc = empty($aOldFunc['fld_desc']) ? '' : $aOldFunc['fld_desc'];
+ $this->iShortComment = empty($aOldFunc["fld_shortdesc"]) ? '' : $aOldFunc['fld_shortdesc'];
+ $this->iExample = empty($aOldFunc['fld_example']) ? '' : $aOldFunc['fld_example'];
+ $this->iPublic = $aOldFunc['fld_public'];
+
+ for( $i=0; $i < 4 ; ++$i) {
+ $this->iMethodRef[$i] = @$aOldFunc["fld_methref".($i+1)];
+ }
+
+ // DB Optimization. If old args are the same as new then don't
+ // bother touching DB.
+ for($i=0; $i<$numoldargs; ++$i) {
+ $exists[$i] = false;
+ }
+
+ for($i=1; $i<=$numoldargs; ++$i) {
+ $arg = $aOldFunc["fld_arg$i"];
+ for( $j=0; $j<$numnewargs; ++$j) {
+ if( $this->iArgs[$j] == $arg ) {
+ $exists[$i-1] = true;
+ $this->iArgsDes[$j] = $aOldFunc["fld_argdes$i"];
+ }
+ }
+ }
+
+ if( $numoldargs == $numnewargs ) {
+ $allsame=true;
+ for( $i=0; $i<$numoldargs; ++$i) {
+ if( !$exists[$i] )
+ $allsame=false;
+ }
+
+ // Check if any default value have changed
+ if( $allsame ) {
+ for( $i=1; $i<=$numoldargs && $allsame; ++$i) {
+ if( isset($this->iArgsVal[$i-1]) && $this->iArgsVal[$i-1] != $aOldFunc["fld_argval$i"] )
+ $allsame = false;
+ }
+ }
+
+ if( $allsame && ($aOldFunc["fld_linenbr"]==$this->iLineNbr) ) {
+ return false;
+ }
+ }
+
+ // Create the new set of arguments by combining the old existing
+ // one that is the same with the new one.
+ for( $i=0; $i<$numnewargs; ++$i ) {
+ $newarg = $this->iArgs[$i];
+ $this->iArgsDes[$i] = '';
+ for( $j=0; $j<$numoldargs; ++$j ) {
+ $oldarg = $aOldFunc['fld_arg'.($j+1)];
+ if( empty($aOldFunc['fld_argdes'.($j+1)]) )
+ $oldargdes = '';
+ else
+ $oldargdes = $aOldFunc['fld_argdes'.($j+1)];
+ if( $newarg==$oldarg ) {
+ $this->iArgsDes[$i] = $oldargdes;
+ }
+ }
+ }
+
+ return true;
+ }
+}
+
+class DBClassProp extends ClassProp {
+ var $iKey="";
+ var $iDesc="";
+ var $iProjname;
+ var $iPublic=1;
+ var $iRef=array();
+
+ function DBClassProp($aProjname,$aParent,$aName,$aLineNbr,$aFile) {
+ parent::ClassProp($aParent,$aName,$aLineNbr,$aFile);
+ $this->iProjname = $aProjname;
+ for($i=0; $i < 4; ++$i)
+ $this->iRef[$i]='';
+ }
+
+ function Internalize($aDBRow) {
+ }
+
+ function Externalize($aDB) {
+ // Check if this class already exist in the DB
+ $q = "SELECT * FROM tbl_class_".$this->iProjname." WHERE ";
+ $q .= "fld_name='".$this->iName."'";
+ $res = $aDB->query($q);
+ if( $res->NumRows() > 0 ) {
+ $GLOBALS["gLogger"]->ToScreen( "Class '".$this->iName."' at line #".$this->iLineNbr." is already in database<br>" );
+ $row = $res->Fetch();
+
+ // Update properties in this class with the DB proeprties
+ $this->iDesc = '';
+ $this->iPublic = 1;
+
+ if( !empty($row["fld_desc"]) )
+ $this->iDesc = $row["fld_desc"];
+ if( !is_null($row["fld_public"]) )
+ $this->iPublic = $row["fld_public"];
+
+ for( $i=1; $i<=4; ++$i ) {
+ if( !empty($row['fld_ref'.$i]) && (int)$row['fld_ref'.$i]!='-1' )
+ $this->iRef[$i-1] = $row['fld_ref'.$i];
+ else
+ $this->iRef[$i-1] = '';
+ }
+
+ $idx = $row['fld_key'];
+
+ // A sanity check of DB
+ if( is_null($row['fld_numfuncs']) ) {
+ echo "PANIC: DB Corruption. fld_numfuncs in DB is NULL for class ".$row['fld_name'].".($row[fld_numfuncs])<br>";
+ exit();
+ }
+ $this->ExternalizeUpdateMethods($aDB,$idx);
+ $this->ExternalizeClass($aDB,$idx);
+ }
+ else {
+ $GLOBALS["gLogger"]->ToScreen( "Adding class ".$this->iName );
+ $idx=$this->ExternalizeClass($aDB);
+ $this->ExternalizeMethods($aDB,$idx);
+ }
+ $this->ExternalizeVars($aDB,$idx);
+ return $idx;
+ }
+
+ function ExternalizeClass($aDB,$aKey="") {
+
+ $q = "REPLACE INTO tbl_class_".$this->iProjname." SET ";
+ $q .= "fld_key='".$aKey."',";
+ $q .= "fld_name='".$this->iName."',";
+ $q .= "fld_public=".$this->iPublic.',';
+ $q .= "fld_ref1='".$this->iRef[0]."',";
+ $q .= "fld_ref2='".$this->iRef[1]."',";
+ $q .= "fld_ref3='".$this->iRef[2]."',";
+ $q .= "fld_ref4='".$this->iRef[3]."',";
+ $q .= "fld_parentname='".$this->iParent."',";
+ $q .= "fld_file='".mysql_escape_string(basename($this->iFileName))."',";
+ $q .= "fld_numfuncs=".$this->iFuncNbr.",";
+ $q .= "fld_desc='".mysql_escape_string($this->iDesc)."',";
+ $q .= "fld_linenbr=".$this->iLineNbr." ";
+ $aDB->query($q);
+ $this->iKey = $aDB->LastIdx();
+
+ return $this->iKey;
+ }
+
+ function ExternalizeMethods($aDB,$aClassIdx) {
+ for( $m=0; $m<$this->iFuncNbr; ++$m) {
+ $func = $this->iFuncs[$m];
+ $func->Externalize($aDB,$aClassIdx);
+ }
+ }
+
+ function ExternalizeUpdateMethods($aDB,$aClassIdx) {
+ // Now get all the methods that is registred for this class
+
+ $q = "SELECT * FROM tbl_method_".$this->iProjname." WHERE fld_classidx=$aClassIdx";
+ $methres = $aDB->query($q);
+ $nbrmeth = $methres->NumRows();
+
+ if( $nbrmeth > 0 ) {
+ // Read all existing methods into 'oldfuncs'
+ $oldfuncs = array();
+ for($i=0; $i<$nbrmeth; ++$i) {
+ $oldfuncs[$i]=$methres->Fetch();
+ $exists[$i] = false;
+ }
+ $nold = count($oldfuncs);
+
+ // Sanity check of Database
+ for($i=0; $i<$nold; ++$i ) {
+ for($j=$i+1; $j<$nold; ++$j ) {
+ if( $oldfuncs[$i]['fld_name'] == $oldfuncs[$j]['fld_name'] ) {
+ echo "PANIC: Corruption in database. Function ".$oldfuncs[$j]['fld_name']." is double defined for classidx $aClassIdx<p>";
+ exit();
+ }
+ }
+ }
+
+ // Walk through all the existing methods
+ for($i=0; $i<$this->iFuncNbr ; ++$i) {
+ $found = false;
+ $func = $this->iFuncs[$i];
+
+ // This unfortunately have to be an O(n^2) mathching ...
+ for($j=0; ($j < $nold) && !$found ; ++$j) {
+ if( $oldfuncs[$j]["fld_name"] == $func->iName ) {
+ $found = true;
+ $exists[$j] = true;
+ $oldfunc = $oldfuncs[$j];
+ }
+ }
+
+ $GLOBALS["gLogger"]->ToScreen( "Checking if method :$func->iName exists..." );
+ if( !$found ) {
+ $exists[$j] = true;
+ $GLOBALS["gLogger"]->ToScreen( " no. Adding it to DB." );
+ $func->Externalize($aDB,$aClassIdx);
+ }
+ else {
+ $GLOBALS["gLogger"]->ToScreen( " yes." );
+
+ // Now update the newly parsed method with any exsiting descriptions
+ // in the database
+ $changed = $func->UpdateFromExisting($oldfunc);
+ if( $changed )
+ $func->Externalize($aDB,$aClassIdx,$oldfunc["fld_key"]);
+ }
+ } // for
+
+ // Delete no longer existing methods
+ for( $i=0; $i<$nold; ++$i ) {
+ if( !$exists[$i] ) {
+ $GLOBALS["gLogger"]->ToScreen( "Deleting method ".$this->iName."::".$oldfuncs[$i]["fld_name"]."()" );
+ $q = "DELETE FROM tbl_method_".$this->iProjname." WHERE fld_key=".$oldfuncs[$i]["fld_key"];
+ $res = $aDB->query($q);
+ if( $res->iRes==1 ) {
+ $q = "UPDATE tbl_class_".$this->iProjname." SET fld_numfuncs=fld_numfuncs-1 WHERE fld_key=$aClassIdx";
+ $aDB->query($q);
+ }
+ else {
+ echo "PANIC: DB Corruption. Can't delete function ".$oldfuncs[$i]["fld_name"]."<br>$q<p>\n";
+ exit();
+ }
+ }
+ }
+ }
+ else {
+ // No existing methods in the DB so just store the new ones
+ $this->ExternalizeMethods($aDB,$aClassIdx);
+ }
+ }
+
+ function ExternalizeVars($aDB,$aClassIdx) {
+ // We just delete all exiting variables for this class and then
+ // add the new ones. This could be DB optimized to only
+ // delete variables that doesn't exist any more and just adding
+ // the new one but this is simpler!
+ $q = "DELETE FROM tbl_classvars_".$this->iProjname." WHERE fld_classidx=".$aClassIdx;
+ $res = $aDB->query($q);
+
+ for( $i=0; $i < $this->iVarNbr; ++$i ) {
+ $q = "INSERT INTO tbl_classvars_".$this->iProjname." SET fld_key='',";
+ $q .= "fld_name='".mysql_escape_string($this->iVars[$i][0])."',";
+ $q .= "fld_default='".mysql_escape_string($this->iVars[$i][1])."',";
+ $q .= "fld_classidx=".$aClassIdx;
+ $aDB->query($q);
+ }
+ }
+}
+
+class DBParser extends Parser {
+ var $iLogNbr;
+ var $iDB;
+ var $iClassIdx=array();
+ var $iProjName;
+ var $iPrettyPrint=false;
+
+ function DBParser($aProjname,$aFile,$aDB) {
+ $this->iDB = $aDB;
+ $this->iLogNbr = 0;
+ $this->iProjName = $aProjname;
+ parent::Parser($aFile);
+ }
+
+ function PrettyPrint($aFlg) {
+ $this->iPrettyPrint = $aFlg;
+ }
+
+ function LineIndicatorMinor($aLineNbr) {
+ }
+
+ function LineIndicatorMajor($aLineNbr) {
+ }
+
+ function StartIndicator($aFilename) {
+ }
+
+ // Override Factory function for classes
+ function &NewClassProp($aParent,$aName,$aLineNbr,$aFileName) {
+ return new DBClassProp($this->iProjName,$aParent,$aName,$aLineNbr,$aFileName);
+ }
+
+ // Override Factory function for methods
+ function &NewFuncProp($aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortComment) {
+ return new DBFuncProp($this->iProjName,$aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortComment,$this->iCurrFileName);
+ }
+
+ // Map function for global functions
+ function MapGlobalFunc($aFunc) {
+
+ if( $this->iPrettyPrint )
+ parent::MapGlobalFunc($aFunc);
+
+ // Check if this function already exists
+ $q = "SELECT * FROM tbl_method_".$this->iProjName." WHERE fld_name='".$aFunc->iName."' AND fld_file='".mysql_escape_string(basename($this->iCurrFileName))."'";
+ //$q = "SELECT * FROM tbl_method_".$this->iProjName." WHERE fld_name='".$aFunc->iName."' ORDER BY fld_name";
+ $res = $this->iDB->Query($q);
+ $n=$res->NumRows();
+ if( $n===1 ) {
+ $oldfunc = $res->Fetch();
+ $aFunc->UpdateFromExisting($oldfunc);
+ $oldidx = $oldfunc['fld_key'];
+ $aFunc->Externalize($this->iDB,0,$oldidx);
+ }
+ elseif( $n > 1 ) {
+ /*
+ $rows = array();
+ for( $i=0; $i<$n; ++$n ) {
+ $rows[] = $res->Fetch();
+ }
+ for( $i=0; $i<$n; ++$n ) {
+ if( $rows[$i]['fld_name'] == $this->iCurrFileName &&
+ basename($rows[$i]['fld_file']) == basename($rows[$i+1]['fld_file']) ) {
+ die('PANIC : Database corrupt. There are multiple versions of GLOBAL function : '.$aFunc->iName);
+ }
+ else {
+ // Same name but in different files
+
+ }
+ }
+ */
+ die('PANIC : Database corrupt. There are multiple versions of GLOBAL function : '.$aFunc->iName);
+
+ }
+ else
+ $aFunc->Externalize($this->iDB);
+ }
+
+ // map function for classes
+ function MapClass($aClass) {
+ if( $this->iPrettyPrint ) {
+ $GLOBALS["gLogger"]->ToScreen( "<p>Mapping class '".$aClass->iName );
+ parent::MapClass($aClass);
+ }
+ $this->iClassIdx[$aClass->iName] = $aClass->Externalize($this->iDB);
+ }
+}
+
+
+class DBDriver extends LintDriver {
+ var $iDB;
+ var $iProjname;
+ var $iPrintFile = false;
+
+ function DBDriver($aProjname,$aFile,$aDB) {
+ $this->iDB = $aDB;
+ $this->iProjname = $aProjname;
+ parent::Driver($aFile);
+ }
+
+ function NewParser($aFile) {
+ return new DBParser($this->iProjname,$aFile,$this->iDB);
+ }
+
+ function PostProcessing() {
+ parent::PostProcessing();
+ }
+}
+
+
+// Test Driver
+class DBTestDriver extends LintDriver {
+ var $iDB;
+ var $iProjname;
+
+ function DBDriver($aProjname,$aFile) {
+ $this->iDB = DBFactory::InitDB();
+ $this->iProjname = $aProjname;
+ parent::Driver($aFile);
+ }
+
+ function NewParser($aFile) {
+ return new DBParser($this->iProjname,$aFile,$this->iDB);
+ }
+
+ function PostProcessing() {
+ parent::PostProcessing();
+ $res = $this->iParser->GetUnusedClassVariables();
+ if( trim($res!="") )
+ echo "<hr><h3>SUMMARY of unused instance variables</h3>$res";
+ $res = $this->iParser->GetWarnings();
+ if( trim($res!="") )
+ echo "<hr><h3>SUMMARY of warnings</h3>$res";
+ $this->iDB->close();
+ }
+}
+
+
+//==========================================================================
+// Script entry point for test harness
+// Read URL argument and create Driver
+//==========================================================================
+//if( !isset($HTTP_GET_VARS['target']) )
+//die("<b>No file specified.</b> Use 'mylintphp.php?target=file_name'" );
+//$file = urldecode($HTTP_GET_VARS['target']);
+//$driver = new DBTestDriver($file);
+//$driver->Run();
+
+
+
+
+?>
+
+
+
+
+
--- /dev/null
+<?php
+//==============================================================================
+// Name: JPGENDBDRIVER.PHP
+// Description: Driver for scanning project files to update the DB
+// Created: 2002-06-06 22:50
+// Author: johanp@aditus.nu
+// Version: $Id: jpgendbdriver.php,v 1.10 2002/09/02 14:26:27 aditus Exp $
+//
+// License: QPL 1.0
+// Copyright (C) 2002 Johan Persson
+//
+//==============================================================================
+
+include 'de_utils.php';
+include 'jpgendb.php';
+
+class ScanProjFiles {
+ var $iProjname;
+ var $iDBUtils;
+ var $iDB;
+
+ function ScanProjFiles($aDBUtils) {
+ $this->iDBUtils = $aDBUtils;
+ $this->iDB = $aDBUtils->iDBServer;
+ }
+
+ function Run($aProjname,$aForceUpdate=false) {
+ $this->iProjname = $aProjname;
+
+ HTMLGenerator::CloseWinButton();
+
+ echo "<b>Scanning files for project '$aProjname'</b><br>";
+
+ // Find full filename of all project files in the project
+ $proj = $this->iDBUtils->GetProject($aProjname);
+ $projidx = $proj['fld_key'];
+
+ echo "<i>($proj[fld_projdir])</i><p>\n";
+
+ $q = "SELECT * FROM tbl_projfiles WHERE fld_projidx=$projidx";
+ $res = $this->iDB->Query($q);
+ $n = $res->NumRows();
+ $ptimer = new JpgTimer();
+ while( $n-- > 0 ) {
+ $r = $res->Fetch();
+ $fname = $proj['fld_projdir'].'/'.$r['fld_name'];
+ $modtime=@filemtime($fname);
+ if( $modtime == false ) {
+ die("Can't access file: $fname");
+ }
+
+ $dbtime = strtotime($r['fld_dbupdtime']);
+ if( $aForceUpdate || $modtime > $dbtime ) {
+
+ echo "Parsing '".basename($fname)."'...\n";flush();
+ $dbdriver = new DBDriver($aProjname,$fname,$this->iDB);
+ $ptimer->Push();
+ $dbdriver->Run();
+ $t = round($ptimer->Pop()/1000,2);
+ $q = "UPDATE tbl_projfiles SET fld_dbupdtime=now() WHERE fld_key=".$r['fld_key'];
+ $this->iDB->Query($q);
+ echo "[in $t s]<br>\n";
+ }
+ else {
+ echo "DB is up to date for '".basename($fname)."'<br>\n";
+ }
+ }
+ echo "<p><h3>Done.</h3>";
+ HTMLGenerator::CloseWinButton();
+ }
+}
+
+class DbGenDriver extends DocEditDriver {
+ function Run($aForceUpdate=false) {
+ if( !empty($this->iProjidx) && $this->iProjidx > 0 ) {
+ $scan = new ScanProjFiles($this->iDBUtils);
+ $projname = $this->iDBUtils->GetProjNameForKey($this->iProjidx);//$regen_projidx);
+ $scan->Run($projname,$aForceUpdate);
+ }
+ else echo "No project index";
+ }
+}
+
+$force = @$HTTP_GET_VARS['force'] ;
+
+if( isset($force) && $force=='true' )
+ $force=1;
+else
+ $force=0;
+
+$driver = new DbGenDriver();
+$driver->Run($force);
+$driver->Close();
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//==============================================================================
+// Name: JPGENDOC.PHP
+// Description: Use parsing classes to generate a documentation skeleton
+// Created: 01/12/03
+// Author: johanp@aditus.nu
+// Version: $Id: jpgendoc.php,v 1.2 2003/01/30 14:55:57 aditus Exp $
+//
+// License: QPL 1.0
+// Copyright (C) 2001,2002 Johan Persson
+//==============================================================================
+include("jplintphp.php");
+
+// Utility function to highlight a snippet of PHP code
+function HighlightCodeSnippet($t,$bg=true) {
+ $t = "<?$t?>";
+ $t = highlight_string($t,true);
+ $t=str_replace('<?','',$t);
+ $t=str_replace('?>','',$t);
+ if( $bg ) {
+ $t = "<div style=\"background-color:#E6E6E6;font-family:courier new;font-size:85%;font-weight:bold;\"><b>$t</b></div>";
+ }
+ else {
+ $t = "<span style=\"font-family:courier;font-size:85%;font-weight:bold;\">$t</span>";
+ }
+ return $t;
+}
+
+
+// Formatting class for Classes
+class DocClassProp extends ClassProp {
+ var $aB,$aC;
+
+ function DocClassProp($aParent,$aName,$aLineNbr,$aFile) {
+ parent::ClassProp($aParent,$aName,$aLineNbr,$aFile);
+ }
+
+ function FormatVar($aVar) {
+ //return $aVar;
+ return HighlightCodeSnippet($aVar,false)."; ";
+ }
+
+ function FormatClass($aClass,$aParent) {
+ $res = "<div style=\"background-color:yellow;font-family:courier new;\">";
+ $res .= "CLASS <b>".$aClass."</b>";
+ if( $aParent != "" )
+ $res .= " EXTENDS <b><i>$aParent</i></b>";
+ $res .= "</div>\n";
+ return $res;
+ }
+
+ function PrettyPrintVars() {
+ $res = "<table border=0>\n";
+ for($i=0; $i<count($this->iVars); ++$i) {
+ $res .= "<tr><td valign=top>";
+ // highlight_string is buggy so we add ';' to be able to parse a
+ // single variable.
+ $t = $this->iVars[$i];
+ $t = "<?php $t?>";
+ ob_start();
+ highlight_string($t);
+ $t = ob_get_contents();
+ ob_end_clean();
+ $t=str_replace('<?php ','',$t);
+ $t=str_replace('?>','',$t);
+ $res .= "<span style=\"font-family:times;font-size:85%;font-weight:bold;\">$t</span>\n";
+ //$res .= "</td><td valign=top> </td><td>".$this->[$i+1];
+ $res .= "</td></tr>\n";
+ }
+ $res .= "</table>\n";
+ return $res;
+ }
+}
+
+// Formatting class for Methods
+class DocFuncProp extends FuncProp {
+ function DocFuncProp($aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortComment) {
+ parent::FuncProp($aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortComment);
+ }
+
+ function ToString() {
+ $res = $this->PrettyPrintFunc();
+ $res .= $this->PrettyPrintArgs();
+ $res .= "<b>Returns:</b>\n";
+ $res .= "<br><b>Description:</b><br>\n".str_replace('//','',$this->iShortComment);
+ $res .= "<br><b>Also see:</b>\n";
+ $res .= "<br><b>Example:</b><br> \n";
+ return $res;
+ }
+
+ function PrettyPrintFunc() {
+ $t="function ".$this->GetName()."(";
+ for($i=0; $i<count($this->iArgs); ++$i) {
+ if( $i != 0 ) $t .= ",";
+ $t .= $this->iArgs[$i];
+ }
+ $t .= ")";
+ return HighlightCodeSnippet($t);
+ }
+
+ function PrettyPrintArgs() {
+ if( count($this->iArgs) == 0 )
+ return "<br>\n";
+ $res = "<table border=0>\n";
+ for($i=0; $i<count($this->iArgs); ++$i) {
+ $res .= "<tr><td valign=top>";
+ // highlight_string is buggy so we add ';' to be able to parse a
+ // single variable.
+ $t = $this->iArgs[$i];
+ $t = "<?php $t?>";
+ ob_start();
+ highlight_string($t);
+ $t = ob_get_contents();
+ ob_end_clean();
+ $t=str_replace('<?php ','',$t);
+ $t=str_replace('?>','',$t);
+ $res .= "<span style=\"font-family:times;font-size:85%;font-weight:bold;\">$t</span>\n";
+ $res .= "</td><td valign=top> </td><td>XXXX</td></tr>\n";
+ }
+ $res .= "</table>\n";
+ return $res;
+ }
+}
+
+// Parser
+class DocParser extends Parser {
+ function DocParser($aFile) {
+ parent::Parser($aFile);
+ }
+ // Factory function for classes
+ function NewClassProp($aParent,$aName,$aLineNbr,$aFileName) {
+ return new DocClassProp($aParent,$aName,$aLineNbr,$aFileName);
+ }
+ // Factory function for methods
+ function NewFuncProp($aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortComment) {
+ return new DocFuncProp($aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortComment);
+ }
+ // Map function for methods
+ function MapFunc(&$aFunc) {
+ parent::MapFunc($aFunc);
+ }
+ // map function for classes
+ function MapClass(&$aClass) {
+ parent::MapClass($aClass);
+ }
+}
+
+
+// Driver
+class DocDriver extends Driver {
+ function DocDriver($aFile) {
+ parent::Driver($aFile);
+ }
+
+ function NewParser($aFile) {
+ return new DocParser($aFile);
+ }
+
+ function PostProcessing() {
+ parent::PostProcessing();
+ $res = $this->iParser->GetUnusedClassVariables();
+ if( trim($res!="") )
+ echo "<hr><h3>SUMMARY of unused instance variables</h3>$res";
+ $res = $this->iParser->GetWarnings();
+ if( trim($res!="") )
+ echo "<hr><h3>SUMMARY of warnings</h3>$res";
+ }
+}
+
+
+//==========================================================================
+// Script entry point
+// Read URL argument and create Driver
+//==========================================================================
+if( !isset($HTTP_GET_VARS['target']) )
+die("<b>No file specified.</b> Use 'mylintphp.php?target=file_name'" );
+$file = urldecode($HTTP_GET_VARS['target']);
+$driver = new DocDriver($file);
+$driver->Run();
+
+
+
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//==============================================================================
+// Name: JPGENHTMLDOC.PHP
+// Description: Implements a HTML plugin for the reference framework as
+// specified in jpclassref.php
+// Created: 2002-04-14
+// Author: johanp@aditus.nu
+// Version: $Id: jpgenhtmldoc.php,v 1.18 2003/01/30 14:55:57 aditus Exp $
+//
+// License: QPL 1.0
+// Copyright (C) 2002 Johan Persson
+//
+//==============================================================================
+
+include "jpclassref.php" ;
+
+DEFINE('GLOBALFUNCS_FILENAME','global_funcs.html');
+
+class FileWriter {
+ var $iFP=0;
+ var $iFileName;
+
+ function Open($aFileName) {
+ if( ($this->iFP = @fopen($aFileName,'w')) == false )
+ die("<b>File open error:</b> Can't open file '$aFileName'. Check that file and directory exists.");
+ $this->iFileName = $aFileName;
+ }
+
+ function Close() {
+ if( $this->iFP )
+ fclose($this->iFP);
+ }
+
+ function W($aStr) {
+ if( fwrite($this->iFP,$aStr) == -1 )
+ die("Can't write to file : ".$this->iFileName);
+ fflush($this->iFP);
+ }
+
+}
+
+
+// Basic HTML Class formatter
+class ClassHTMLFormatter extends ClassFormatter {
+ var $iNumClasses,$iColumnClassCnt,$iGlobalClassCnt,$iNumMethods,$iColumnMethCnt,$iClassMethodCnt;
+ var $iCol;
+ var $iWriter;
+ var $iListCnt=0;
+ var $iCSS =
+ '<style type="text/css">
+ <!--
+ A:link {font-family: helvetica, arial, geneva, sans-serif; font-size: x-small; text-decoration: none; color: #0000ff}
+ A:visited {font-family: helvetica, arial, geneva, sans-serif; font-size: x-small; text-decoration: none; color: #0000ff}
+ A:hover {font-family: helvetica, arial, geneva, sans-serif; font-size: x-small; text-decoration: underline; color: #FF0000}
+ th {font-family: helvetica, arial; color : blue; font-size:85%; background : lightgrey; border-right:black solid 1pt; border-bottom:black solid 1pt;}
+ //-->
+ </style>';
+
+ var $iIndexFramePage =
+ '<!doctype html public "-//W3C//DTD HTML 4.0 Frameset//EN">
+ <HTML><HEAD>
+ <LINK REL=STYLESHEET TYPE="text/css" HREF="de_normal.css">
+ <title>Project documentation</title>
+ </head>
+ <frameset cols="270,*">
+ <frame src=class_toc.html name=toc>;
+ <frame src=projinfo.html name=classdetail>
+ </frameset>
+ </html>';
+
+ var $iIndexPage =
+ '<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
+ <HTML><HEAD>
+ <LINK REL=STYLESHEET TYPE="text/css" HREF="de_normal.css">
+ <title>Project documentation</title>
+ </head>';
+
+ function ClassHTMLFormatter($aDBCache,$aFlags="") {
+ parent::ClassFormatter($aDBCache,$aFlags="");
+ $this->iWriter = new FileWriter();
+ }
+
+ function Start() {
+
+ $t = '<table width=100% border=1 style="background-color:lightblue;"><tr><td align=center><span style="font-size:20pt;font-family:arial;font-weight:bold;">'.$this->iProjName."</span></td></tr></table>\n";
+ $t .= '<div align=center><h3 style="color:darkred;font-family:arial;">Documentation status: '.round($this->iStatAverage*100).'%</h3>';
+ $t .= '<span style="font-family:arial;">Total number of Classes: '.$this->iStatNumClasses.', Methods: '.$this->iStatNumMethods."</span><p>\n";
+ if( $this->iShowPrivate )
+ $t .= '<i>This version <b>includes</b> private methods & classes</i><p>';
+ else
+ $t .= '<i>This version does <b>not</b> include private methods & classes</i><p>';
+ $t .= '<p><i>Generated at '.strftime('%d %b %Y at %H:%M')."</i><br>\n";
+ $t .="</div><hr>";
+ $t .= "<p>".$this->iProjDesc; //$proj['fld_desc'];
+
+ if( $this->iDocType == 0 ) {
+ $dt = 'HTML: Multiple files.';
+ $this->iWriter->Open($this->iDirectory.'projinfo.html');
+ $this->iWriter->W( $t );
+ $this->iWriter->Close();
+
+ $this->iWriter->Open($this->iDirectory.'index.html');
+ $this->iWriter->W( $this->iIndexFramePage );
+ $this->iWriter->Close();
+ }
+ else {
+ $dt = 'HTML: Single file.';
+ $this->iWriter->Open($this->iDirectory.'index.html');
+ $this->iWriter->W( $this->iIndexPage );
+ $this->iWriter->W( $this->iCSS );
+ $this->iWriter->W( $t );
+ }
+
+ HTMLGenerator::CloseWinButton('left');
+ echo "<hr>";
+ echo "<font face=arial><b>Generating reference for project : <font color=blue>$this->iProjName</font></b></font><br>";
+ echo "Output directory: <b><font color=blue>".$this->iDirectory.'</font></b><br>';
+ echo "Output format: <b><font color=blue>$dt</font></b> <br>\n";
+ echo "Including private methods and classes: <b><font color=blue>".($this->iShowPrivate ? 'Yes' : 'No')."</font></b><br>";
+ echo "Including global functions: <b><font color=blue>".($this->iShowGlobFuncs ? 'Yes' : 'No')."</font></b><br>";
+ echo "<hr>";
+ }
+
+ function End() {
+ echo "<h3>Successfully generated all documentation.</h3>";
+ echo "<hr>";
+ HTMLGenerator::CloseWinButton('left');
+ }
+
+ function ClassEntry($aClassName) {
+ ++$this->iListCnt;
+ echo "$this->iListCnt. Writing class <b><font color=blue>$aClassName</font></b>...";flush();
+ if( $this->iDocType == 0 ) { // Split output
+ $this->iWriter->Open($this->iDirectory.$aClassName.'.html');
+ $this->iWriter->W( $this->iCSS );
+ }
+ }
+
+ function ClassExit() {
+ echo " done.<br>\n";flush();
+ $this->iWriter->W( '<p> <hr> <p>' );
+ if( $this->iDocType == 0 ) { // Split output
+ $this->iWriter->Close();
+ }
+ }
+
+ function GlobalFuncEntry($aNumFuncs) {
+ echo "<p>Writing <b><font color=blue> $aNumFuncs global functions</font></b>...\n";flush();
+ if( $this->iDocType == 0 ) { // Split output
+ $this->iWriter->Open($this->iDirectory.GLOBALFUNCS_FILENAME);
+ $this->iWriter->W( $this->iCSS );
+ }
+ $this->iWriter->W('<div style="background-color:yellow;font-family:arial;font-weight:bold;font-size:150%;">Global functions</div>');
+ }
+
+ function GlobalFuncExit() {
+ echo "<br> done.<br>\n";
+ $this->iWriter->W( '<p> <hr> <p>' );
+ if( $this->iDocType == 0 ) { // Split output
+ $this->iWriter->Close();
+ }
+ }
+
+ function FmtClassHierarchySetup($aHier,$aNbr) {
+ $this->iWriter->W( "<table border=1>" );
+ }
+
+ function FmtClassHierarchyExit($aHier,$aNbr) {
+ $this->iWriter->W( "</tr></table>" );
+ }
+
+ function FmtClassHierarchyHeaders($aHier,$aNbr) {
+ $this->iWriter->W( "<tr>" );
+ for( $i=0; $i<$aNbr; ++$i ) {
+ $this->iWriter->W( '<td> <a href="'.$aHier[$i].'.html" style="font-family:arial;font-weight:bold;color:darkblue;">'.$aHier[$i]."</a> </td>" );
+ }
+ $this->iWriter->W( "</tr><tr>" );
+ }
+
+ function FmtClassHierarchyColumnSetup($aClassName,$aColNbr) {
+ $this->iWriter->W( "<td valign=top>" );
+ }
+
+ function FmtClassHierarchyColumnExit($aClassName,$aColNbr) {
+ $this->iWriter->W( "</td>" );
+ }
+
+ function FmtClassHierarchyRow($aClassName,$aMethodName,$aOverridden,$aPublic) {
+ if( $aMethodName == "" )
+ $this->iWriter->W( " " );
+ else {
+ // Markup private methods with a '*'
+ if( !$this->iShowPrivate && !$aPublic )
+ return;
+
+ if( $aPublic )
+ $mark='';
+ else
+ $mark='*';
+ if($aOverridden) {
+ $this->iWriter->W( "<a href=\"$aClassName.html#".
+ strtoupper("_".$aClassName."_".$aMethodName).
+ "\" style=\"color:darkgrey;\">$mark".$aMethodName."() </a><br>\n" );
+ }
+ else {
+ $this->iWriter->W( " ".
+ "<a href=\"$aClassName.html#".strtoupper("_".$aClassName."_".$aMethodName)."\">$mark".
+ $aMethodName."()</a> <br>\n" );
+ }
+ }
+ }
+
+ function FmtClassSetup($aClassInfo) {
+
+ $res = "<hr><a name=\"".strtoupper("_C_".$aClassInfo["fld_name"])."\"><div style=\"background-color:yellow;font-family:courier new;\"></a>";
+ $res .= "CLASS <b>".$aClassInfo["fld_name"]."</b>";
+ if( $aClassInfo["fld_parentname"] != "" )
+ $res .= " EXTENDS <a href=\"".$aClassInfo["fld_parentname"].".html#".strtoupper("_C_".$aClassInfo["fld_parentname"])."\" style=\"font-face:arial;font-weight:bold;\">".$aClassInfo["fld_parentname"]."</a>";
+ $res .= "</div>\n";
+ $res .= "<i>(Defined in: ".$aClassInfo['fld_file']." : $aClassInfo[fld_linenbr])</i>";
+ $this->iWriter->W( $res );
+ }
+
+ function FmtClassOverview($aClassInfo) {
+ $res = " <p><div style=\"font-weight:bold;font-family:arial;font-size:100%;\">Class usage and Overview</div>".$aClassInfo["fld_desc"]." <p> \n";
+ $this->iWriter->W( $res );
+ }
+
+ function FmtClassOverviewExit($aClassInfo) {
+ $this->iWriter->W( '<hr><span style="font-family:arial;font-size:120%;font-weight:bold;">Class Methods</span><hr>' );
+ }
+
+ function FmtClassRefs($aClassInfo) {
+ $MAXREFS = 4;
+ $refs = array();
+ for( $i=1; $i <= $MAXREFS; ++$i ) {
+ $cname = @$aClassInfo['fld_ref'.$i];
+ if( !empty($cname) && trim($cname) != '' ) {
+ $refs[] = $cname;
+ }
+ }
+ $n = count($refs);
+ if( $n==0 )
+ return;
+ $this->iWriter->W("<div style=\"font-weight:bold;font-family:arial;font-size:85%;\">See also related classes:</div>" ) ;
+ for( $i=0; $i < $n; ++$i ) {
+ $this->iWriter->W( '<a href="'.$refs[$i].'.html">'.$refs[$i].'</a>' );
+ if( $i < $n-2 )
+ $this->iWriter->W( ", " );
+ elseif( $i==$n-2 )
+ $this->iWriter->W( " and " );
+ }
+ $this->iWriter->W( ' <p> ' );
+ }
+
+ function FmtClassVars($aVars) {
+ $res = "<table border=0>\n";
+ for($i=0; $i<count($aVars); ++$i) {
+ $res .= "<tr><td valign=top>";
+ // highlight_string is buggy so we add ';' to be able to parse a
+ // single variable.
+ $t = $aVars[$i]["fld_name"];
+ $t = Utils::HighlightCodeSnippet($t);
+ $res .= "<span style=\"font-family:times;font-size:85%;font-weight:bold;\">$t</span>\n";
+ $res .= "</td></tr>\n";
+ }
+ $res .= "</table>\n";
+ $this->iWriter->W( $res );
+ }
+
+ function FmtFuncPrototype($aClassName,$aFunc,$aShowFile=false) {
+ $file = '';
+ if( $aShowFile ) {
+ $file = "<i>($aFunc[fld_file]:$aFunc[fld_linenbr])</i><br>\n";
+ }
+ $t = "function ".$aFunc["fld_name"]."(";
+ for($i=0; $i<$aFunc["fld_numargs"]; ++$i) {
+ if( $i != 0 ) $t .= ",";
+ $t .= $aFunc["fld_arg".($i+1)];
+ }
+ $t .= ")";
+ $t = Utils::HighlightCodeSnippet($t);
+ $t = "<p> <p> <span style='font-size:110%;'><a name=\"".strtoupper("_".$aClassName."_".$aFunc["fld_name"])."\">".$t."</a></span>\n";
+ $t .= "$file\n<span style='font-family:arial;font-size:90%;'><i>".$aFunc["fld_shortdesc"]."</i></span><p>\n";
+ $this->iWriter->W( "<p>\n".$t );
+ }
+
+ function FmtFuncReturn($aFunc) {
+ $r = $aFunc["fld_return"];
+ if( trim($r) != '' ) {
+ $res = "<br>\n<div style=\"font-weight:bold;font-family:arial;font-size:85%;\">Returns</div>$r<br>\n";
+ $this->iWriter->W( $res );
+ }
+ }
+
+ function FmtFuncArgs($aFunc) {
+ if( $aFunc["fld_numargs"] == 0 ) {
+ $this->iWriter->W( "<br>\n" );
+ return;
+ }
+
+ $res = "\n<table cellspacing=0 style='border:black solid 1pt;' width=100%>\n";
+ $res .= '<tr><th width=25%>Argument</th><th width=15%>Default</th><th width=60%>Description</th></tr>';
+ for($i=0; $i<$aFunc["fld_numargs"]; ++$i) {
+ $res .= "\n<tr><td style='border-right:black solid 1pt;font-family:courier;font-size:90%;font-weight:bold;'>";
+ // highlight_string is buggy so we add ';' to be able to parse a
+ // single variable.
+ $t = $aFunc["fld_arg".($i+1)];
+ $t = Utils::HighlightCodeSnippet($t,false,false);
+ $res .= "$t\n";
+ $res .= "</td><td style='border-right:black solid 1pt;font-family:courier;font-size:90%;font-weight:bold;'>";
+ $val = $aFunc["fld_argval".($i+1)];
+ if( !isset($val) || strlen(trim($val)) == 0 )
+ $val = ' ';
+ else
+ $val = Utils::HighlightCodeSnippet($val,false,false);
+ $res .= $val.'</td><td>';
+ $des = $aFunc["fld_argdes".($i+1)];
+ if( empty($des) || strlen(trim($des)) == 0 )
+ $des = "No description available";
+ $res .= "$des</td>";
+ $res .= "</tr>\n";
+ }
+ $res .= "</table>\n";
+ $this->iWriter->W( $res );
+ }
+
+ function FmtFuncDesc($aFunc) {
+ $res = "\n<div style=\"font-weight:bold;font-family:arial;font-size:85%;\">Description</div>";
+
+ if( strlen(trim($aFunc['fld_desc'])) == 0 )
+ $res .= "No description available.";
+ else
+ $res .= $aFunc['fld_desc']." <br>\n";
+
+ $this->iWriter->W( $res );
+ }
+
+ function FmtFuncRef($aRef) {
+ $this->iWriter->W(" <div style=\"font-weight:bold;font-family:arial;font-size:85%;\">See also</div>" ) ;
+ $m = count($aRef);
+ for( $i=0; $i < $m; ++$i ) {
+ list($cname,$mname) = $aRef[$i];
+ $this->iWriter->W( "<a href=\"$cname.html#".strtoupper("_".$cname."_".$mname)."\">".$cname."::".$mname."</a>" );
+ if( $i < $m-2 )
+ $this->iWriter->W( ", " );
+ elseif( $i==$m-2 )
+ $this->iWriter->W( " and " );
+ }
+ }
+
+ function FmtFuncExample($aFunc) {
+ if( $aFunc["fld_example"] != "" ) {
+ $this->iWriter->W( "\n<div style=\"font-weight:bold;font-family:arial;font-size:85%;\"><p>Example</div>" );
+ $this->iWriter->W( Utils::HighlightCodeSnippet($aFunc["fld_example"],false)."<br>\n" );
+ }
+ }
+
+ function FmtIndexSetup($aNumClasses,$aNumMethods, $aNumColumns=3) {
+
+ // Note: In this formatter we ignore the outside specification of
+ // columns because we always use 1 column for frames formatting
+ // and 3 columns for a single file formatting.
+
+ if( $this->iDocType == 0 ) {
+ $aNumColumns=1;
+ $class_toc_name = 'class_toc.html';
+ $this->iWriter->Open( $this->iDirectory.$class_toc_name );
+ }
+ else {
+ $class_toc_name = 'index.html';
+ $aNumColumns=3;
+ }
+
+ $this->iNumIndexColumns = $aNumColumns;
+ //echo "<h4>Creating index (".$this->iDirectory.$class_toc_name.") with $aNumColumns columns...</h4>";
+ $this->iWriter->W( $this->iCSS );
+ $this->iWriter->W( "<a href=projinfo.html target=classdetail style='font-size:120%;'>$this->iProjName</a><p>\n" );
+
+ if( $this->iShowGlobFuncs ) {
+ $this->iWriter->W( "<a href=".GLOBALFUNCS_FILENAME." target=classdetail style='font-size:100%;'>Global functions</a><p>\n" );
+ }
+
+ $this->iNumClasses = $aNumClasses;
+ $this->iNumMethods = $aNumMethods;
+ $this->iCol = 1;
+ $this->iColumnClassCnt = 0;
+ $this->iGlobalClassCnt = 0;
+ $this->iColumnMethCnt = 0;
+ $this->iWriter->W( "<table width=100%><tr><td valign=top width=".round(100/$aNumColumns,0)."%>" );
+ }
+
+ function FmtIndexExit() {
+ $this->iWriter->W( "</td></tr></table><p>" );
+ if( $this->iDocType == 0 ) {
+ $this->iWriter->Close();
+ }
+ }
+
+ function FmtIndexClass($aClassName) {
+ ++$this->iColumnClassCnt;
+ ++$this->iGlobalClassCnt;
+ $this->iClassMethodCnt=0;
+ if( ($this->iColumnClassCnt > floor($this->iNumClasses/$this->iNumIndexColumns)) ) {
+ if( $this->iCol < $this->iNumIndexColumns ) {
+ $this->iWriter->W( "</td><td valign=top width=".round(100/$this->iNumIndexColumns,0)."%>" );
+ $this->iColumnClassCnt = 0;
+ $this->iColumnMethCnt = 0;
+ }
+ }
+ $target = ' target=classdetail';
+ $this->iWriter->W( "<b><font face='arial'>$this->iGlobalClassCnt.</font> <a href=\"$aClassName.html#".strtoupper("_C_$aClassName")."\" $target>$aClassName</a></b><br>\n" );
+ }
+
+ function FmtIndexMethod($aClassName,$aMethodName) {
+ ++$this->iColumnMethCnt;
+ ++$this->iClassMethodCnt;
+ $target = ' target=classdetail';
+ $this->iWriter->W( " <span style='font-family:arial;font-size:x-small;'> $this->iGlobalClassCnt.$this->iClassMethodCnt</span> <a href=\"$aClassName.html#".strtoupper("_".$aClassName."_".$aMethodName)."\" $target>$aMethodName</a><br>\n" );
+ }
+}
+
+
+class HTMLDriver extends ClassRefDriver {
+ // Factory function
+ function NewClassFormatter($aDBCache,$aFlags) {
+ return new ClassHTMLFormatter($aDBCache,$aFlags);
+ }
+}
+
+
+//==========================================================================
+// Script entry point
+// Read URL argument and create Driver
+//==========================================================================
+if( !isset($HTTP_GET_VARS['class']) )
+$class = "";
+else
+$class = urldecode($HTTP_GET_VARS['class']);
+
+$driver = new HTMLDriver();
+$driver->Run($class,FMT_CLASSVARS);
+exit();
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//==============================================================================
+// Name: JPLINTDRIVER.PHP
+// Description: Driver for PHP "lint"
+// Created: 01/12/03
+// Author: johanp@aditus.nu
+// Version: $Id: jplintdriver.php,v 1.1 2002/04/20 19:31:32 aditus Exp $
+//
+// License: QPL 1.0
+// Copyright (C) 2001,2002 Johan Persson
+//==============================================================================
+
+include("jplintphp.php");
+
+//==========================================================================
+// Script entry point
+// Read URL argument and create Driver
+//==========================================================================
+if( !isset($HTTP_GET_VARS['target']) )
+ die("<b>No file specified.</b> Use 'mylintphp.php?target=file_name'" );
+$file = urldecode($HTTP_GET_VARS['target']);
+$driver = new Driver($file);
+$driver->Run();
+
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//==============================================================================
+// Name: JPLINTPHP.PHP
+// Description: Simple static analysis of a PHP file.
+// Created: 01/12/03
+// Author: johanp@aditus.nu
+// Version: $Id: jplintphp.php,v 1.14 2003/03/01 21:51:18 aditus Exp $
+//
+// License: QPL 1.0
+//
+// Copyright (C) 2001,2002 Johan Persson
+//
+// Long Description:
+// Parses a correct PHP file for classes and methods and does some rudimentary
+// checks and warns for:
+// 1) ... unused instance variables exists
+// 2) ... possible forgotten $this-> qualifier for access to instance variables
+//
+// Please note that the PHP file MUST be syntactically correct since
+// the parsing is very simple and can't cope with recovery after syntax errors.
+//==============================================================================
+
+set_time_limit(0);
+
+//-------------------------------------------------------------------
+// Some testcode to get the ereg expressions correct. Why does this
+// always has to be a bloody pain... :-)
+//------------------------------------------------------------------------
+//$aLine = 'var $txt1 = array(), $txt2 = "" , $txt3 = "kalle" ;';
+//$pattern='/^var\s+(\$\w+)?\s*=?(array\(\)|\d+\.\d+|\d+|"\w*")?(,\s*(\$\w+)?\s*=?(array\(\)|\d+\.\d+|\d+|"\w*")?)*/';
+//$vdec = '(\$\w+)?\s*=?\s*(array\(\)|\d+\.\d+|\d+|"\w*")?';
+//$vlist = "\s*$vdec\s*,?";
+//$pattern = "/^var $vlist$vlist$vlist$vlist$vlist;/";
+//echo "pattern=$pattern<p>";
+
+
+if( false ) {
+// $aLine = 'function GanttVLine($aDate,$aTitle="",$aColor="black",$aWeight="_{33}*45/12",$aStyle="dashed")';
+ $aLine = 'function SetFrameBevel($aDepth=3,$aBorder=false,$aBorderColor=\'black@0.4\',$aColor1=\'white\',$aColor2=\'darkgray\',$aFlg=true)';
+
+ $quotchars = "[\w|£|#|$|%|&|\.|@\[\]|+|*|\/|-|\{|\}]+";
+ $argdef = '\s*(\&?\$\w+)*=?(""|'."''".'|'."'".$quotchars."'".'|\d+|\d+\.\d+|\w+|"'.$quotchars.'"|array\(\d*,?\d*,?\d*,?\))?\s*,?';
+
+ $pattern = '/^function\s+(\w+)\s*\(\s*'.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.'\s*\)/i';
+
+ $flg=preg_match($pattern,trim($aLine),$matches);
+ if( $flg )
+ {
+
+ $numArgs = ceil((count($matches)-2)/2);
+ $fname = $matches[1];
+ $args=array();
+ $argsval=array();
+ for($i=0; $i<$numArgs; ++$i) {
+ $args[$i]=$matches[2+$i*2];
+ if( isset($matches[3+$i*2]) )
+ $argsval[$i]=$matches[3+$i*2];
+ }
+
+ echo "Number of args: ".ceil((count($matches)-1)/2)."<br>";
+ for($i=0;$i<count($matches); ++$i)
+ echo "<pre>$i:$matches[$i]</pre>";
+ }
+ else
+ echo "No match found.<br>";
+ exit();
+}
+
+
+
+// Base class for PHP class properties (Class, methods)
+class Prop {
+ var $iName;
+ function Prop($aName) {
+ $this->iName = $aName;
+ }
+ function GetName() {
+ return $this->iName;
+ }
+}
+
+// Stores properties for a class definition, name, methods, file etc
+class ClassProp extends Prop {
+ var $iParent;
+ var $iFileName;
+ var $iLineNbr;
+ var $iFuncs,$iFuncNbr=0;
+ var $iVars=array(),$iVarNbr=0,$iUsed=array();
+
+ function ClassProp($aParent,$aName,$aLineNbr,$aFile) {
+ $this->iName = $aName;
+ $this->iParent = $aParent;
+ $this->iLineNbr = $aLineNbr;
+ $this->iFileName = $aFile;
+ $this->iFuncs=array();
+ $this->iVars=array();
+ $this->iUsed=array();
+ $this->iFuncNbr = 0;
+ }
+
+ function AddVar($aVar,$aVal="") {
+ $this->iVars[$this->iVarNbr] = array($aVar,$aVal);
+ $this->iUsed[$this->iVarNbr] = false;
+ $this->iVarNbr++;
+ }
+
+ function IaAllVarsUsed() {
+ for($i=0; count($this->iVars); ++$i)
+ if( !$this->iUsed[$i] )
+ return false;
+ return true;
+ }
+
+ function AddFunc($aFunc) {
+
+ // Sanity check. Make sure that a function with this name isn't
+ // alrey defined in this class
+ $found = false;
+ for($i=0; $i<$this->iFuncNbr && !$found; ++$i) {
+ $found = ($aFunc->iName == $this->iFuncs[$i]->iName);
+ }
+ if( $found ) {
+ echo "<br><font color=red><b>Semantic error in PHP file:</font></b> Function <b>$aFunc->iName</b> is multiple defined in class <b>$this->iName</b>. Skipping.<br>\n";
+ return;
+ }
+
+
+ $this->iFuncs[$this->iFuncNbr]=$aFunc;
+ $this->iFuncNbr++;
+ }
+
+ function GetFileName() {
+ return $this->iFileName;
+ }
+
+ function FormatVar($aVar) {
+ return "<i>".$aVar."</i>";
+ }
+
+ function FormatClass($aClass,$aParent) {
+ $res = "<hr>CLASS <b>".$aClass."</b>";
+ if( $aParent != "" )
+ $res .= " INHERITS <b>".$aParent."</b>";
+ return $res;
+ }
+
+ // Some Java style ToString() methods
+ function ToString() {
+ $res = $this->FormatClass($this->iName,$this->iParent);
+ $res .= "(Defined in: ".$this->iFileName.":".$this->iLineNbr.")<br>" ;
+ $res .= "<br><b>VARS</b>";
+ for( $i=0; $i<count($this->iVars); ++$i) {
+ $res .= "<br> ".$this->FormatVar($this->iVars[$i][0]);
+ if($this->iVars[$i][1] != "")
+ $res .= " = ".$this->FormatVar($this->iVars[$i][1]);
+ if( !$this->iUsed[$i] )
+ $res .= "<font color=\"red\"> ** NOT USED **</font>";
+ }
+ $res .= "<p><b>METHODS</b><br>";
+ for( $i=0; $i<count($this->iFuncs); ++$i) {
+ $res .= $this->iFuncs[$i]->ToString();
+ }
+ return $res;
+ }
+
+}
+
+// Stores properties for a class method
+class FuncProp extends Prop {
+ var $iNumArgs;
+ var $iArgs=array(),$iArgsVal=array(), $iArgsDes=array();
+ var $iClassName;
+ var $iLineNbr;
+ var $iFileName;
+ var $iShortComment;
+
+ function FuncProp($aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortComment="",$aFileName="") {
+ $this->iName = $aName;
+ $this->iClassName = $aClassName;
+ $this->iNumArgs = count($aArgs);
+ $this->iArgs = $aArgs;
+ $this->iArgsVal = $aArgsVal;
+ $this->iLineNbr = $aLineNbr;
+ $this->iShortComment = $aShortComment;
+ $this->iFileName = $aFileName;
+ }
+
+// Some Java style ToString() methods
+ function ToString() {
+ $res = $this->iClassName."::<b>".$this->iName."</b>";
+
+ if( $this->iNumArgs > 0 ) {
+ $res .= "(";
+ for($i=0; $i<$this->iNumArgs; ++$i) {
+ if($i!=0) $res .= ", ";
+ $res .= "<i>".$this->iArgs[$i];
+ if( isset($this->iArgsVal[$i]) && $this->iArgsVal[$i]!="" )
+ $res .= " = ".$this->iArgsVal[$i];
+ $res .= "</i>";
+ }
+ $res .= ")";
+ }
+ else
+ $res .= "()";
+ return $res."<br>";
+ }
+}
+
+// The actual parser class. very simple. Read all the line
+// for a given file and try to figure out if there is a function or
+// class definiton on that line.
+class Parser {
+ var $iClasses=null,$iClassCnt;
+ var $iCurrClass=null;
+ var $iGlobalFuncs=null;
+ var $iBraceCnt=0;
+ var $iInComment=0,$iHyphenMarks=0,$iQuoteMarks=0,$iInString=0;
+ var $iCurrFileName;
+ var $iFp=null, $iCurrFile="";
+ var $iPrevLine,$iNextLine;
+ var $iWarnings=null;
+ var $iCommentBreak=true,$iLastComment="";
+
+ function Parser($aFile) {
+ $this->iClasses=array();
+ $this->iWarnings = array();
+ $this->iClassCnt=0;
+ $this->iGlobalFuncs = array();
+ $this->iCurrFileName=$aFile;
+ $fp = @fopen($aFile,"r");
+ if( !$fp ) {
+ die("Parser: Can't open file $aFile");
+ }
+ $this->iFp = $fp;
+ $this->iCurrFile=$aFile;
+ }
+
+ function MapClass($aClass) {
+ echo $aClass->ToString().'<p>';
+ }
+
+ function MapGlobalFunc($aFunc) {
+ echo $aFunc->ToString().'<p>';
+ }
+
+
+ function DoMapClasses() {
+ for($i=0; $i<count($this->iClasses); ++$i) {
+ $this->MapClass($this->iClasses[$i]);
+ }
+ }
+
+ function DoMapGlobalFuncs() {
+ $n = count($this->iGlobalFuncs);
+ for( $i=0; $i<$n; ++$i ) {
+ $this->MapGlobalFunc($this->iGlobalFuncs[$i]);
+ }
+ }
+
+ function StartIndicator($aFilename) {
+ echo "<h2>File: $aFilename </h2>\n";
+ flush();
+ }
+
+ function Start() {
+ // Read line by line to find each class and all methods
+ // defined within that class
+ $lnbr=1;
+ $this->iPrevLine = "";
+ $this->iNextLine = fgets($this->iFp,256);
+ $this->StartIndicator($this->iCurrFileName);
+ while( !feof($this->iFp) ) {
+ $buffer = $this->iNextLine;
+ $this->iNextLine = fgets($this->iFp,256);
+ $this->ParseLine($buffer,$lnbr);
+ $this->iPrevLine = $buffer;
+ ++$lnbr;
+ }
+ $buffer = $this->iNextLine;
+ $this->iNextLine="";
+ $this->ParseLine($buffer,$lnbr);
+ }
+
+ function End() {
+ fclose($this->iFp);
+ }
+
+ function GetWarnings() {
+ $res="";
+ for($i=0; $i<count($this->iWarnings); ++$i)
+ $res .= $this->iWarnings[$i]."<br>";
+ return $res;
+ }
+
+ function GetUnusedClassVariables() {
+ $res = "";
+ for($i=0; $i<count($this->iClasses); ++$i) {
+ $var = "";
+ for($j=0; $j<count($this->iClasses[$i]->iVars); ++$j) {
+ if( !$this->iClasses[$i]->iUsed[$j] ) {
+ if( $var != "" ) $var .= ", ";
+ $var .= "<i>".$this->iClasses[$i]->iVars[$j][0]."</i>";
+ }
+ }
+ if( $var != "" )
+ $res .= "<b>Warning:</b>Unused variables in Class ".$this->iClasses[$i]->GetName()." (".$var.")<br>";
+ }
+ return $res;
+ }
+
+ function CheckUsedVars($aLine,$aLineNbr) {
+ $n = count($this->iCurrClass->iVars);
+ if( $n==0 ) return;
+ $ret=false;
+ for( $i=0; $i<$n; ++$i) {
+ $pattern = "/this->".substr($this->iCurrClass->iVars[$i][0],1)."/";
+ $isVarUsed=preg_match($pattern,trim($aLine));
+
+ $pattern = "/[^>\w]".trim(substr($this->iCurrClass->iVars[$i][0],1))."[^\w]/";
+ $isVarUsedWithoutThis=preg_match($pattern,trim($aLine));
+
+ if( $isVarUsed ) {
+ $ret=true;
+ $this->iCurrClass->iUsed[$i]=true;
+ }
+ elseif( $isVarUsedWithoutThis ) {
+ $this->iWarnings[] = "<b>Warning:</b> Possible use of <b>".$this->iCurrClass->iVars[$i][0]."</b> (Class ".$this->iCurrClass->GetName().") in ".
+ $this->iCurrFileName.":".$aLineNbr." without a '\$this' qualifier.";
+ }
+ }
+ return $ret;
+ }
+
+ function ParseClassVars($aLine) {
+ // Instance variables in $matches[$i], $i=1,2,3,...
+ $vdec = '(\$\w+)?\s*=?\s*(array\(\)|\d+\.\d+|\d+|".*"'."|'.*'".')?';
+ $vlist = "\s*$vdec\s*,?";
+ $pattern = "/^var $vlist$vlist$vlist$vlist$vlist;/";
+
+
+ $isVar=preg_match($pattern,trim($aLine),$matches);
+ if( !$isVar ) return false;
+ $n = ceil((count($matches)-1)/2);
+ for($i=0; $i<ceil((count($matches)-1)/2); ++$i) {
+ if( !isset($matches[2+$i*2]) )
+ $matches[2+$i*2]="";
+ if( trim($matches[1+$i*2]) == "" ) {
+ echo "****DEBUG #$i: line=$aLine<br>m1=".$matches[$i*2]."m2=".$matches[1+$i*2]."m3=".$matches[2+$i*2]."<p>";
+ }
+ else
+ $this->iCurrClass->AddVar($matches[1+$i*2],$matches[2+$i*2]);
+ }
+ return true;
+ }
+
+ // Factory function for classes
+ function &NewClassProp($aParent,$aName,$aLineNbr,$aFileName) {
+ return new ClassProp($aParent,$aName,$aLineNbr,$aFileName);
+ }
+
+ // Factory function for methods
+ function &NewFuncProp($aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortComment) {
+ return new FuncProp($aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortComment,$this->iCurrFileName);
+ }
+
+ function LineIndicatorMinor($aLineNbr) {
+ echo "..$aLineNbr..";
+ flush();
+ }
+
+ function LineIndicatorMajor($aLineNbr) {
+ echo "<br>\n";
+ }
+
+ // Maintain brace count ignoring braces within strings and comments.
+ function BraceCount($aLine) {
+ $n = strlen($aLine);
+ $done = false;
+ $prevc='';
+ for( $i=0; $i<$n && !$done; ++$i ) {
+ $cc = substr($aLine,$i,2);
+ $c = substr($cc,0,1);
+ if( $prevc != '\\' && $c=='"' && !$this->iHyphenMarks )
+ $this->iQuoteMarks = $this->iQuoteMarks ? 0 : 1;
+
+ if( $prevc != '\\' && $c=="'" && !$this->iQuoteMarks )
+ $this->iHyphenMarks = $this->iHyphenMarks ? 0 : 1;
+
+ $this->iInString = $this->iHyphenMarks || $this->iQuoteMarks ? 1 : 0;
+
+ if( ($cc == '//' || $cc == '#') && !$this->iInString )
+ $done=true;
+ else {
+ if( $cc == '/*' && !$this->iInString ) $this->iInComment = true;
+ elseif( $cc == '*/' && !$this->iInString ) $this->iInComment = false;
+ elseif( $c == '{' && !$this->iInComment && !$this->iInString ) ++$this->iBraceCnt;
+ elseif( $c == '}' && !$this->iInComment && !$this->iInString ) --$this->iBraceCnt;
+ }
+ $prevc = $c;
+ }
+ //echo " $this->iBraceCnt ($this->iInComment, $this->iInString) : ".htmlentities($aLine)."<br>\n";
+ }
+
+ function ParseLine($aLine,$aLineNbr) {
+
+
+ if( $aLineNbr % 50 == 0 ) {
+ $this->LineIndicatorMinor($aLineNbr);
+ if( $aLineNbr % 500 == 0 )
+ $this->LineIndicatorMajor($aLineNbr);
+ }
+
+
+ $aLine = trim($aLine);
+ if( $aLine=='' ) return;
+
+ $pattern = '/^\s*\/\//';
+ if( !$this->iInString && preg_match($pattern,$aLine) ) {
+ if( $this->iCommentBreak ) {
+ $this->iLastComment = trim($aLine);
+ $this->iCommentBreak = false;
+ }
+ else
+ $this->iLastComment .= $aLine;
+ return;
+ }
+ else
+ $this->iCommentBreak = true;
+
+ if( $this->iBraceCnt < 0 )
+ die("Syntax error in PHP file. Unmatched braces on line $aLineNbr");
+
+ if( $this->iBraceCnt > 0 ) {
+ if( $this->ParseClassVars($aLine) ) {
+ return;
+ }
+ $this->CheckUsedVars($aLine,$aLineNbr);
+ }
+
+ // Is this a class definition of the form
+ // class classname {extends parentclass} \{
+ $pattern="/^(class)\s+(\w+)\s*(extends\s+(\w+\s*))?/i";
+ //$isClass=preg_match($pattern,trim($aLine),$matches);
+ if( !$this->iInString && preg_match($pattern,$aLine,$matches) ) {
+ $name = $matches[2];
+ if( isset($matches[4]) ) // Inheritance?
+ $parent = $matches[4];
+ else
+ $parent = "";
+ $this->iClasses[$this->iClassCnt] = $this->NewClassProp($parent,$name,$aLineNbr,$this->iCurrFileName);
+ $this->iCurrClass = &$this->iClasses[$this->iClassCnt];
+ $this->iClassCnt++;
+ }
+ else {
+ // Look for a function definition with arguments which may have default
+ // values. The pattern below works for up to 10 arguments
+ // $matches[1]=function name
+ // $matches[2+($i)*2]=argument $i name [i=0,1,2,...]
+ // $matches[3+($i)*2]=argument $i value
+ // Number of arguments=ceil((count($matches)-2)/2)
+ // Note: We must use ceil() since if the last argument has no initialization
+ // the last two entries wont exist and we will get a floating point
+ // number back.
+
+ $quotchars = "[\w|£|#|$|%|&|\.|@\[\]|+|*|\/|-|\{|\}]+";
+ $argdef = '\s*(\&?\$\w+)*=?(""|'."''".'|'."'".$quotchars."'".'|-?\d+|-?\d+\.\d+|\w+|"'.$quotchars.'"|array\(-?\d*,?-?\d*,?-?\d*,?\))?\s*,?';
+
+ $pattern = '/^function\s+(\w+)\s*\(\s*'.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.'\s*\)/i';
+
+ //$isFunction=preg_match($pattern,trim($aLine),$matches);
+ if( !$this->iInString && preg_match($pattern,$aLine,$matches) ) {
+ $numArgs = ceil((count($matches)-2)/2);
+ $fname = $matches[1];
+ $args=array();
+ $argsval=array();
+ for($i=0; $i<$numArgs; ++$i) {
+ $args[$i]=$matches[2+$i*2];
+ if( isset($matches[3+$i*2]) )
+ $argsval[$i]=$matches[3+$i*2];
+ }
+ if( isset($this->iCurrClass) && $this->iCurrClass!=null && $this->iBraceCnt==1 )
+ $this->iCurrClass->AddFunc($this->NewFuncProp($this->iCurrClass->GetName(),$fname,$aLineNbr,$args,$argsval,$this->iLastComment));
+ elseif( $this->iBraceCnt==0 ) {
+ // Add a global function
+ //$aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortComment,$aFileName=""
+ $this->iGlobalFuncs[] = $this->NewFuncProp('',$fname,$aLineNbr,$args,$argsval,$this->iPrevLine);
+ }
+ else
+ die("Syntax error in PHP file. Function definition within function. (".$this->iBraceCnt.")");
+
+ // Clear comment once we used it
+ $this->iLastComment = "" ;
+ }
+ }
+ $this->BraceCount($aLine);
+ }
+}
+
+
+// Class Driver
+// Parse a file and get all the functions and classed defined in that
+// file. The methods and classes are stored in the properties
+// iClasses and iFuncs and are each instances of ClassProp and FuncProp respectively
+// To use this class just inherit the class and implement
+// your own overloaded version of PostProcessing() (currently it just prints out the
+// found methods)
+class LintDriver {
+ var $iParser,$aFileName;
+
+ function Driver($aFile) {
+ $this->iParser = $this->NewParser($aFile);
+ }
+
+ function NewParser($aFile) {
+ return new Parser($aFile);
+ }
+
+ function Run() {
+ $this->iParser->Start();
+ $this->iParser->End();
+ $this->PostProcessing();
+ }
+
+ function PostProcessing() {
+ $this->iParser->DoMapClasses();
+ $this->iParser->DoMapGlobalFuncs();
+ }
+}
+
+// EOF
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//=========================================================================
+// Name: adjimg.php
+// Written by: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: adjimg.php,v 1.1 2002/04/20 19:31:33 aditus Exp $
+//
+// Description:
+// (Unsupported) Utility to take an image and adjust it's brightness,
+// contrast and saturation. The modified image is the displayed.
+// The original file is untouched.
+//
+// Usage: adjimg.php?file=name&b=value&c=value&s=scale&sat=saturation
+//
+// License: QPL 1.0
+// Copyright (C) 2001,2002 Johan Persson
+//=========================================================================
+
+function LoadImage($filename,$format="png") {
+ $f = "imagecreatefrom".$format;
+ $img = @$f($filename);
+ if( !$img ) {
+ die("Error: Can't read image file: $filename");
+ }
+ return $img;
+}
+
+function AdjSat($img,$sat) {
+ $nbr = imagecolorstotal ($img);
+ for( $i=0; $i<$nbr; ++$i ) {
+ $colarr = imagecolorsforindex ($img,$i);
+ $rgb[0]=$colarr["red"];
+ $rgb[1]=$colarr["green"];
+ $rgb[2]=$colarr["blue"];
+ $rgb = AdjRGBSat($rgb,$sat);
+ imagecolorset ($img, $i, $rgb[0], $rgb[1], $rgb[2]);
+ }
+}
+
+function AdjBrightContrast($img,$bright,$contr) {
+ $nbr = imagecolorstotal ($img);
+ for( $i=0; $i<$nbr; ++$i ) {
+ $colarr = imagecolorsforindex ($img,$i);
+ $r = AdjRGBBrightContrast($colarr["red"],$bright,$contr);
+ $g = AdjRGBBrightContrast($colarr["green"],$bright,$contr);
+ $b = AdjRGBBrightContrast($colarr["blue"],$bright,$contr);
+ imagecolorset ($img, $i, $r, $g, $b);
+ }
+}
+
+function AdjRGBBrightContrast($rgb,$bright,$contr) {
+ // First handle contrast, i.e change the dynamic range around grey
+ if( $contr <= 0 ) {
+ // Decrease contrast
+ $adj = abs($rgb-128) * (-$contr);
+ if( $rgb < 128 )
+ $rgb += $adj;
+ else
+ $rgb -= $adj;
+ }
+ else { // $contr > 0
+ // Increase contrast
+ if( $rgb < 128 )
+ $rgb = $rgb - ($rgb * $contr);
+ else
+ $rgb = $rgb + ((255-$rgb) * $contr);
+ }
+
+ // Add (or remove) various amount of white
+ $rgb += $bright*255;
+ $rgb=min($rgb,255);
+ $rgb=max($rgb,0);
+ return $rgb;
+}
+
+
+
+// Adjust saturation for RGB array $u. $sat is a value between -1 and 1
+// Note: Due to GD inability to handle true color the RGB values are only between
+// 8 bit. This makes saturation quite sensitive for small increases in parameter sat.
+//
+// Tip: To get a grayscale picture set sat=-100, values <-100 changes the colors
+// to the complementary colors.
+//
+// Implementation note: The saturation is implemented directly in the RGB space
+// by adjusting the perpendicular distance between the RGB point and the "grey"
+// line (1,1,1). Setting $sat>0 moves the point away from the line along the perp.
+// distance and a negative value moves the point closer to the line.
+// The values are truncated when the color point hits the bounding box along the
+// RGB axis.
+// DISCLAIMER: I'm not 100% sure this is he correct way to imeplemen a saturation
+// function in RGB space.
+function sign($a) {if( $a>=0) return 1; else return -1;}
+function AdjRGBSat($rgb,$sat) {
+ // Gray vector
+ $v=array(1,1,1);
+
+ // Dot product
+ $dot = $rgb[0]*$v[0]+$rgb[1]*$v[1]+$rgb[2]*$v[2];
+
+ // Normalize dot product
+ $normdot = $dot/3;
+
+ // Direction vector between $u and its projection onto $v
+ for($i=0; $i<3; ++$i)
+ $r[$i] = $rgb[$i] - $normdot*$v[$i];
+
+ // Adjustment factor so that sat==1 sets the highest RGB value to 255
+ if( $sat > 0 ) {
+ $m=0;
+ for( $i=0; $i<3; ++$i) {
+ if( sign($r[$i]) == 1 && $r[$i]>0)
+ $m=max($m,(255-$rgb[$i])/$r[$i]);
+ }
+ $tadj=$m;
+ }
+ else
+ $tadj=1;
+
+ $tadj = $tadj*$sat;
+ for($i=0; $i<3; ++$i) {
+ $un[$i] = round($rgb[$i] + $tadj*$r[$i]);
+
+ // Truncate color when they reach 0
+ if( $un[$i]<0 ) $un[$i]=0;
+
+ // Avoid potential rounding error
+ if( $un[$i]>255 ) $un[$i]=255;
+ }
+ return $un;
+}
+
+
+
+if( empty($file) )
+ die("<b>Usage:</b><br>r.php?file=name&[b=value][&c=value][&s=scale][&sat=saturation]<p>
+ file= Filename, must end with the image format, i.e. .png, .jpg, .gif<br>
+ b = Brightness value [-1, 1]<br>
+ c = Contrast value [-1, 1]<br>
+ s = Scale<br>
+ sat= Color saturation value.<br>");
+
+if(strstr($file,"png"))
+ $bkg = LoadImage($file);
+elseif( strstr($file,"jpg"))
+ $bkg = LoadImage($file,"jpeg");
+elseif( strstr($file,"gif"))
+ $bkg = LoadImage($file,"gif");
+
+if( empty($b) ) $b=0;
+if( empty($c) ) $c=0;
+if( empty($s) ) $s=1;
+if( empty($sat) ) $sat=0;
+
+// Adjust contrast and brightness of background color
+if( $b || $c )
+ AdjBrightContrast($bkg,$b,$c);
+
+// Adjust color saturation
+if( $sat )
+ AdjSat($bkg,$sat);
+
+// Get width & height
+$bw = ImageSX($bkg);
+$bh = ImageSY($bkg);
+
+// Scale image
+$w=$bw*$s;
+$h=$bh*$s;
+
+$img = imagecreate($w,$h);
+imagecopyresized($img,$bkg,0,0,0,0,$w,$h,$bw,$bh);
+$background_color = ImageColorAllocate ($img, 255, 255, 255);
+
+if(strstr($file,"png")) {
+ header ("Content-type: image/png");
+ imagepng($img);
+}
+elseif( strstr($file,"jpg")) {
+ header ("Content-type: image/jpeg");
+ imagejpeg($img);
+}
+elseif( strstr($file,"gif")) {
+ header ("Content-type: image/gif");
+ imagegif($img);
+}
+else
+ die("Unknown graphic format in file $file");
+?>
--- /dev/null
+<?php
+//=======================================================================
+// File: CHKGD.PHP
+// Description: Check which version of GD is installed
+// Created: 2002-10-13
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: chkgd.php,v 1.1 2002/10/13 22:17:45 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+
+
+function CheckGDVersion($aInfo=true) {
+ ob_start();
+ phpinfo(8); // Just get the modules loaded
+ $a = ob_get_contents();
+ ob_end_clean();
+ if( preg_match('/.*GD Version.*(1[0-9|\.]+).*/',$a,$m) ) {
+ $r=1;$v=$m[1];
+ }
+ elseif( preg_match('/.*GD Version.*(2[0-9|\.]+).*/',$a,$m) ) {
+ $r=2;$v=$m[1];
+ }
+ else {
+ $r=0;$v=$m[1];
+ }
+
+ if( $aInfo ) {
+ if( $r==1 )
+ echo "You have GD 1 installed. Version: $v (or higher)\n";
+ elseif( $r==2 )
+ echo "You have GD 2 installed. Version: $v (or higher)\n";
+ else
+ echo "You don't seem to have any GD support in your PHP installation";
+ }
+
+ return array($r,$v);
+}
+
+echo "<b>Checking GD version ...</b>\n<p>";
+
+CheckGDVersion();
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/*=======================================================================
+// File: GENCOLORCHART.PHP
+// Description: Automatically generates an indexpage of all named colors
+// Created: 2001-02-28
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: gencolorchart.php,v 1.2 2002/08/25 22:06:22 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+include "../../jpgraph.php";
+include "../../jpgraph_canvas.php";
+
+// Height and width of each color sample
+DEFINE("WIDTH",30);
+DEFINE("HEIGHT",30);
+
+function ColorBox($g,$x,$y,$nbr,&$colors) {
+ $g->img->SetColor($colors[$nbr]);
+ $g->img->FilledRectangle($x,$y,$x+WIDTH,$y+HEIGHT);
+ $g->img->SetColor("black");
+ $g->img->Rectangle($x,$y,$x+WIDTH,$y+HEIGHT);
+ $g->img->SetTextAlign("center","top");
+ $g->img->StrokeText($x+WIDTH/2,$y+HEIGHT+2,"$nbr:".$colors[$nbr]);
+}
+
+function GenColIndex() {
+ $dummy=0;
+ $rgb = new RGB($dummy);
+ $colors = array_keys($rgb->rgb_table);
+ sort($colors);
+
+ $n=1;
+ $c=0;
+ echo "<ol>";
+ while( $c < count($colors) ) {
+ $g = new CanvasGraph(700,1240);
+ $filename=CACHE_DIR."color_chart%02d.".$g->img->img_format;
+ $y=8;
+ $i=0;
+ while( $c<count($colors) && $i<112 ) {
+ $x=40; $j=0;
+ while( $c<count($colors) && $j<6 ) {
+ ColorBox($g,$x,$y,$c,$colors);
+ $x += 85+WIDTH;
+ ++$i; ++$j; ++$c;
+ }
+ $y += 35+HEIGHT;
+ }
+ $f = sprintf($filename,$n);
+ echo "<li>$f<br>\n";
+ flush();
+ $g->img->Stream($f);
+ $g->img->Destroy();
+ ++$n;
+ $allfiles[]=$f;
+ }
+ echo "</ol>";
+ echo "<p>Generating color chart index page.";
+ flush();
+
+ $frm = "<img src=%s border=0>\n";
+ $buf = "<h3>Color chart for JpGraph containing $c named colors</h3>\n";
+ foreach( $allfiles as $f ) {
+ $buf .= sprintf($frm,basename($f));
+ }
+ $fp = fopen(CACHE_DIR."colorchart.html","w");
+ if( !$fp )
+ die("Can't create index file.");
+ fwrite($fp,$buf);
+ fclose($fp);
+ echo "<br>";
+}
+
+function GenThemes($themes) {
+
+ $dummy=0;
+ $rgb = new RGB($dummy);
+ $colors = array_keys($rgb->rgb_table);
+ sort($colors);
+
+ $names = array_keys($themes);
+
+ $n=1;
+ echo "<ol>";
+ foreach($themes as $ta) {
+ $g = new CanvasGraph(700,(HEIGHT+35)*ceil(count($ta)/5)+30);
+ $filename=CACHE_DIR."theme%02d.".$g->img->img_format;
+ $y=8;
+ $nbr=0;
+ while( $nbr < count($ta) ) {
+ $x=40; $j=0;
+ while( $nbr<count($ta) && $j<5 ) {
+ ColorBox($g,$x,$y,$ta[$nbr],$colors);
+ $x += 85+WIDTH;
+ ++$j; ++$nbr;
+ }
+ $y += 35+HEIGHT;
+ }
+ $f = sprintf($filename,$n);
+ $allfiles[]=$f;
+ echo "<li>$f [$nbr colors in theme '".$names[$n-1]."']\n";
+ flush();
+ $g->img->Stream($f);
+ $g->img->Destroy();
+ ++$n;
+ }
+ echo "</ol>";
+
+ echo "<p>Generating theme index page.";
+ flush();
+
+ $frm = "<img src=%s border=0>\n";
+ --$n;
+ $buf = "<h3>Theme chart for JpGraph containing $n themes</h3>\n";
+ $i=1;
+ foreach( $allfiles as $f ) {
+ $buf .= "<h4>Theme $i (<i>".$names[$i-1].")</i></h4>\n";
+ $buf .= sprintf($frm,basename($f));
+ ++$i;
+ }
+ $fp = fopen(CACHE_DIR."themes.html","w");
+ if( !$fp )
+ die("Can't create index file.");
+ fwrite($fp,$buf);
+ fclose($fp);
+ echo "<br>";
+}
+
+$th=array(
+ "earth" => array(22,424,10,34,40,45,49,62,63,74,77,119,120,134,136,141,168,180,209,218,346,395,89,430),
+ "pastel" => array(22,424,27,38,42,58,66,79,105,110,128,147,152,230,240,331,337,405,415),
+ "water" => array(22,424,8,10,14,24,56,213,237,268,326,335,370,387,388),
+ "sand" => array(22,424,19,34,50,65,72,82,131,168,209)
+);
+
+echo "<h2>JpGraph color chart</h2>";
+echo "Generating color chart images ...<br>\n";
+flush();
+$timer = new JpgTimer();
+$timer->Push();
+GenColIndex();
+
+echo "<p>Generating themes...";
+GenThemes($th);
+
+$t=$timer->Pop()/1000;
+$t=sprintf("<p>Work done in: %0.2f seconds.",round($t,2));
+echo "$t<p>See <a href=\"".CACHE_DIR."colorchart.html\">Colorchart</a>\n";
+echo "<br>See <a href=\"".CACHE_DIR."themes.html\">Index of themes</a>\n";
+?>
+
+
--- /dev/null
+<?php
+/*=======================================================================
+// File: IMGDBSCHEMA.INC
+// Description: Classes to help drawing a DB schema semi-automtically
+// See dbschema_ddda.php for example of use.
+// Created: 2002-08-25
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: imgdbschema.inc,v 1.2 2002/08/29 10:11:22 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+
+//===================================================
+// CLASS ImgDBTable
+// Description: Utility class for drawing an graphical
+// illustration of a DB table
+//===================================================
+class ImgDBTable {
+ var $ix,$iy;
+ var $iheader_color='white',$iheader_fillcolor='navy';
+ var $ibody_color='black', $ibody_fillcolor='lightyellow';
+ var $iheader_txt,$ibody_txt;
+ var $iw,$ih,$ihh=0;
+ var $iheader_font=FF_FONT1,$iheader_style=FS_BOLD,$iheader_fontsize=14;
+ var $ibody_font=FF_FONT1,$ibody_style=FS_NORMAL,$ibody_fontsize=12;
+
+ function ImgDBTable($w=0,$h=0) {
+ $this->iw = $w;
+ $this->ih = $h;
+ }
+
+ function SetPos($x,$y) {
+ $this->ix = $x;
+ $this->iy = $y;
+ }
+
+ function HeaderColor($aFontColor,$aFillColor) {
+ $this->iheader_color=$aFontColor;
+ $this->iheader_fillcolor=$aFillColor;
+ }
+
+ function BodyColor($aFontColor,$aFillColor) {
+ $this->ibody_color=$aFontColor;
+ $this->ibody_fillcolor=$aFillColor;
+ }
+
+ function Set($aHeader,$aBody,$aWidth=0,$aHeadHeight=0,$aBodyHeight=0) {
+ $this->iw = $aWidth;
+ $this->ih = $aBodyHeight;
+ $this->SetHeader($aHeader,$aHeadHeight);
+ $this->SetBody($aBody);
+ }
+
+ function Setheader($aTxt,$aHeight=0) {
+ $this->iheader_txt = $aTxt;
+ $this->ihh = $aHeight;
+ }
+
+ function SetBody($aTxtArr) {
+ $n = count($aTxtArr);
+ $t = '';
+ for( $i=0; $i < $n; ++$i ) {
+ if( $i > 0 ) $t .= "\n";
+ $t .= $aTxtArr[$i];
+ }
+ $this->ibody_txt = $t;
+ }
+
+ function SetHeaderFont($aFam,$aSty,$aSize) {
+ $this->iheader_font = $aFam;
+ $this->iheader_style = $aSty;
+ $this->iheader_fontsize = $aSize;
+ }
+
+ function SetBodyFont($aFam,$aSty,$aSize) {
+ $this->ibody_font = $aFam;
+ $this->ibody_style = $aSty;
+ $this->ibody_fontsize = $aSize;
+ }
+
+ function StrokeAll($aImg,$aScale,$aTables) {
+ $n = count($aTables);
+ for($i=0; $i < $n; ++$i) {
+ $this->Set($aTables[$i][3], $aTables[$i][4], $aTables[$i][2]);
+ $this->Stroke($aImg,$aScale,$aTables[$i][0], $aTables[$i][1]);
+ }
+ }
+
+ function Stroke($aImg,$aScale,$ax=0,$ay=0) {
+ if( $ax != 0 )
+ $this->ix = $ax;
+ if( $ay != 0 )
+ $this->iy = $ay;
+
+ $t = new CanvasRectangleText();
+ $t->SetShadow();
+
+ $t->SetFont($this->iheader_font,$this->iheader_style,$this->iheader_fontsize);
+ $t->SetFillColor($this->iheader_fillcolor);
+ $t->SetFontColor($this->iheader_color);
+ $t->SetAutoMargin(7);
+ $t->ParagraphAlign('center');
+ $t->Set($this->iheader_txt,$this->ix,$this->iy,$this->iw,$this->ihh);
+
+ // In case of "auto-heighting" we need to find out the actual height
+ list($dummy,$this->ihh) = $t->Stroke($aImg,$aScale);
+ $this->iy = $aScale->TranslateY($this->iy);
+
+ // Use absolute coordinate (this is indicated by negative values)
+ $this->iy = -( $this->iy + $this->ihh );
+
+ $t->SetFont($this->ibody_font,$this->ibody_style,$this->ibody_fontsize);
+ $t->SetFillColor($this->ibody_fillcolor);
+ $t->SetFontColor($this->ibody_color);
+ $t->SetAutoMargin(8);
+ $t->ParagraphAlign('left');
+ $t->Set($this->ibody_txt,$this->ix,$this->iy,$this->iw,$this->ih);
+ $t->Stroke($aImg,$aScale);
+
+ }
+}
+
+
+
+//============================================================================
+// Class: ImgDBSchema
+// Generate an image of all tables in a specific DB
+//============================================================================
+class ImgDBSchema {
+ var $iDBSrv;
+ var $iTitle;
+ var $iFormTblName = '';
+ var $iFormFldName = '';
+ var $iLeftMarg=2,$iTopMarg=4;
+ var $iTableWidth=13,$iTableAutoHeight=20;
+
+
+ // Initialize with DB name as well as possible callback methods for
+ // formatting the table header and fields
+ function ImgDBSchema($aDBName,$aTblNameFormat='',$aFldNameFormat='') {
+ $this->iFormTblName = $aTblNameFormat;
+ $this->iFormFldName = $aFldNameFormat;
+ $this->iDBSrv = new DBServer('root','');
+ $this->iTitle = new CanvasRectangleText();
+ $this->iDBSrv->SetDB($aDBName);
+ }
+
+ function SetMargin($aLeft,$aTop) {
+ $this->iLeftMarg = $aLeft;
+ $this->iTopMarg = $aTop;
+ }
+
+ function SetTableWIdth($aWidth) {
+ $this->iTableWidth=$aWidth;
+ }
+
+ // Stroke tables using a specific image context and scale.
+ // It is possible to manually set the position of each table if
+ // you specify the TblPos for each DB table.
+ function Stroke($aImg,$aScale,$aTblPos=null) {
+
+ $tables = $this->SetupFormatTables($aTblPos);
+
+ // Setup the DB table raster class
+ $dbi = new ImgDBTable();
+
+ // Stroke all tables
+ $dbi->StrokeAll($aImg,$aScale,$tables);
+
+ }
+
+
+ // Get the information from the Database and setup
+ // the formatting array to help positioning and stroking the
+ // tables to the canvas
+ function SetupFormatTables($aTblPos) {
+
+ $tblflds = $this->iDBSrv->GetTablesFields();
+
+ $nt = count($tblflds);
+ $tables = array();
+
+ $x = $this->iLeftMarg;
+ $y = $this->iTopMarg;
+
+ for( $i=0; $i < $nt; ++$i ) {
+
+ if( !isset($aTblPos[2*$i]) ) $aTblPos[2*$i]=-1;
+ if( !isset($aTblPos[2*$i+1]) ) $aTblPos[2*$i+1]=-1;
+
+ if( $this->iFormTblName != '' ) {
+ $f = $this->iFormTblName;
+ $tn = $f($tblflds[$i][0]);
+ }
+ else
+ $tn = $tblflds[$i][0];
+
+ $flds = $tblflds[$i][1];
+ if( $this->iFormFldName != '' ) {
+ $f = $this->iFormFldName;
+ $n = count($flds);
+ for( $j=0; $j< $n; ++$j ) {
+ $flds[$j] = $f($flds[$j],$tblflds[$i][0]);
+ }
+ }
+
+ $tables[] = array($aTblPos[2*$i] >= 0 ? $this->iLeftMarg+$aTblPos[2*$i] : $x,
+ $aTblPos[2*$i+1] >= 0 ? $this->iTopMarg +$aTblPos[2*$i+1]: $y,
+ $this->iTableWidth,$tn,$flds);
+ $x += $this->iTableWidth + 1;
+ if( $x>35 ) {
+ $x = $this->iLeftMarg;
+ $y += $this->iTableAutoHeight;
+ }
+ }
+ return $tables;
+ }
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//=======================================================================
+// File: MKGRAD.PHP
+// Description: Simple tool to create a gradient background
+// Created: 2002-08-09
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: mkgrad.php,v 1.1 2002/08/09 23:09:12 aditus Exp $
+//
+// License: QPL 1.0
+// Copyright (C) 2002 Johan Persson
+//=======================================================================
+
+
+// This includes a lot of unecessary things as well...
+include "../../jpgraph.php";
+include "../../jpgraph_bar.php";
+include "../../jpgraph_canvas.php";
+
+
+// Must have a global comparison method for usort()
+function _cmp($a,$b) {
+ return strcmp($a,$b);
+}
+
+
+// Generate the input form
+class Form {
+ var $iColors;
+ var $iGradstyles;
+ function Form() {
+
+ $rgb = new RGB();
+ $this->iColors = array_keys($rgb->rgb_table);
+ usort($this->iColors,'_cmp');
+
+ $this->iGradstyles = array(
+ "Vertical",2,
+ "Horizontal",1,
+ "Vertical from middle",3,
+ "Horizontal from middle",4,
+ "Horizontal wider middle",6,
+ "Vertical wider middle",7,
+ "Rectangle",5 );
+ }
+
+ function Run() {
+
+ echo '<h3>Generate gradient background</h3>';
+ echo '<form METHOD=POST action=""><table style="border:blue solid 1;">';
+ echo '<tr><td>Width:<br>'.$this->GenHTMLInput('w',8,4,300).'</td>';
+ echo "\n";
+ echo '<td>Height:<br>'.$this->GenHTMLInput('h',8,4,300).'</td></tr>';
+ echo "\n";
+ echo '<tr><td>From Color:<br>';
+ echo $this->GenHTMLSelect('fc',$this->iColors);
+ echo '</td><td>To Color:<br>';
+ echo $this->GenHTMLSelect('tc',$this->iColors);
+ echo '</td></tr>';
+ echo '<tr><td colspan=2>Gradient style:<br>';
+ echo $this->GenHTMLSelectCode('s',$this->iGradstyles);
+ echo '</td></tr>';
+ echo '<tr><td colspan=2>Filename: (empty to stream)<br>';
+ echo $this->GenHTMLInput('fn',55,100);
+ echo '</td></tr>';
+ echo '<tr><td colspan=2 align=right>'.$this->GenHTMLSubmit('submit').'</td></tr>';
+ echo '</table>';
+ echo '</form>';
+
+ }
+
+ function GenHTMLSubmit($name) {
+ return '<INPUT TYPE=submit name="ok" value=" Ok " >';
+ }
+
+
+ function GenHTMLInput($name,$len,$maxlen=100,$val='') {
+ return '<INPUT TYPE=TEXT NAME='.$name.' VALUE="'.$val.'" SIZE='.$len.' MAXLENGTH='.$maxlen.'>';
+ }
+
+ function GenHTMLSelect($name,$option,$selected="",$size=0) {
+ $txt="<select name=$name";
+ if( $size > 0 )
+ $txt .= " size=$size >";
+ else
+ $txt .= ">";
+ for($i=0; $i<count($option); $i++) {
+ if( $selected==$option[$i] )
+ $txt=$txt."<option selected value=\"$option[$i]\">$option[$i]</option>\n";
+ else
+ $txt=$txt."<option value=\"".$option[$i]."\">$option[$i]</option>\n";
+ }
+ return $txt."</select>\n";
+ }
+
+ function GenHTMLSelectCode($name,$option,$selected="",$size=0) {
+ $txt="<select name=$name";
+ if( $size > 0 )
+ $txt .= " size=$size >";
+ else
+ $txt .= ">";
+ for($i=0; $i<count($option); $i += 2) {
+ if( $selected==$option[($i+1)] )
+ $txt=$txt."<option selected value=".$option[($i+1)].">$option[$i]</option>\n";
+ else
+ $txt=$txt."<option value=\"".$option[($i+1)]."\">$option[$i]</option>\n";
+ }
+ return $txt."</select>\n";
+ }
+
+}
+
+// Basic application driver
+
+class Driver {
+ var $iGraph, $iGrad;
+ var $iWidth,$iHeight;
+ var $iFromColor, $iToColor;
+ var $iStyle;
+ var $iForm;
+
+ function Driver() {
+ $this->iForm = new Form();
+ }
+
+ function GenGradImage() {
+
+ global $HTTP_POST_VARS;
+
+ $aWidth = (int)@$HTTP_POST_VARS['w'];
+ $aHeight = (int)@$HTTP_POST_VARS['h'];
+ $aFrom = @$HTTP_POST_VARS['fc'];
+ $aTo = @$HTTP_POST_VARS['tc'];
+ $aStyle = @$HTTP_POST_VARS['s'];
+ $aFileName = @$HTTP_POST_VARS['fn'];
+
+
+ if( $aFrom=='' || $aTo=='' || $aWidth < 1 || $aHeight < 1 )
+ exit("Syntax: mkgrad?w=nnn&h=nnn&fc=fromColor&tc=toColor&s=n");
+
+ $this->iWidth = $aWidth;
+ $this->iHeight = $aHeight;
+ $this->iFromColor = $aFrom;
+ $this->iToColor = $aTo;
+ $this->iStyle = $aStyle;
+
+ $this->graph = new CanvasGraph($aWidth,$aHeight);
+ $this->grad = new Gradient($this->graph->img);
+ $this->grad->FilledRectangle(0,0,
+ $this->iWidth,$this->iHeight,
+ $this->iFromColor,
+ $this->iToColor,
+ $this->iStyle);
+
+ if( $aFileName != "" ) {
+ $this->graph->Stroke($aFileName);
+ echo "Image file '$aFileName' created.";
+ }
+ else
+ $this->graph->Stroke();
+ }
+
+
+ function Run() {
+
+ global $HTTP_POST_VARS;
+
+ // Two modes:
+ // 1) If the script is called with no posted arguments
+ // we show the input form.
+ // 2) If we have posted arguments we naivly assume that
+ // we are called to do the image.
+
+ if( @$HTTP_POST_VARS['ok']===' Ok ' ) {
+ $this->GenGradImage();
+ }
+ else
+ $this->iForm->Run();
+ }
+}
+
+$driver = new Driver();
+$driver->Run();
+
+?>
\ No newline at end of file