]> git.sur5r.net Git - bacula/bacula/commitdiff
bacula-web: Upgraded phplot to version 5.2.0
authorDavide Franco <bacula-dev@dflc.ch>
Wed, 1 Dec 2010 12:15:04 +0000 (13:15 +0100)
committerKern Sibbald <kern@sibbald.com>
Sat, 20 Apr 2013 12:39:42 +0000 (14:39 +0200)
54 files changed:
gui/bacula-web/external_packages/phplot/COPYING [new file with mode: 0644]
gui/bacula-web/external_packages/phplot/ChangeLog
gui/bacula-web/external_packages/phplot/LICENSE.GPL [deleted file]
gui/bacula-web/external_packages/phplot/LICENSE.PHP_3_0 [deleted file]
gui/bacula-web/external_packages/phplot/NEWS.txt [new file with mode: 0644]
gui/bacula-web/external_packages/phplot/README [deleted file]
gui/bacula-web/external_packages/phplot/README.txt [new file with mode: 0644]
gui/bacula-web/external_packages/phplot/contrib/README.txt [new file with mode: 0644]
gui/bacula-web/external_packages/phplot/contrib/color_range.example.php [new file with mode: 0644]
gui/bacula-web/external_packages/phplot/contrib/color_range.php [new file with mode: 0755]
gui/bacula-web/external_packages/phplot/contrib/color_range.test1.php [new file with mode: 0644]
gui/bacula-web/external_packages/phplot/contrib/color_range.test2.php [new file with mode: 0644]
gui/bacula-web/external_packages/phplot/contrib/prune_labels.example.php [new file with mode: 0644]
gui/bacula-web/external_packages/phplot/contrib/prune_labels.php [new file with mode: 0644]
gui/bacula-web/external_packages/phplot/contrib/prune_labels.test.php [new file with mode: 0644]
gui/bacula-web/external_packages/phplot/doc/imgs/graph1.png [deleted file]
gui/bacula-web/external_packages/phplot/doc/imgs/graph2.png [deleted file]
gui/bacula-web/external_packages/phplot/doc/imgs/graph3.png [deleted file]
gui/bacula-web/external_packages/phplot/doc/imgs/phplot-dia.png [deleted file]
gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig1.png [deleted file]
gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig2.png [deleted file]
gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig3.png [deleted file]
gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig4.png [deleted file]
gui/bacula-web/external_packages/phplot/doc/index.php [deleted file]
gui/bacula-web/external_packages/phplot/doc/internal_functions.html [deleted file]
gui/bacula-web/external_packages/phplot/doc/quickstart.html [deleted file]
gui/bacula-web/external_packages/phplot/doc/schema.html [deleted file]
gui/bacula-web/external_packages/phplot/doc/style.css [deleted file]
gui/bacula-web/external_packages/phplot/doc/user_functions.html [deleted file]
gui/bacula-web/external_packages/phplot/doc/user_internal_functions.html [deleted file]
gui/bacula-web/external_packages/phplot/examples/benjamingothic.ttf [deleted file]
gui/bacula-web/external_packages/phplot/examples/create_chart.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/data.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/data_date.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/data_date2.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/data_sample1.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/data_sample2.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/data_sample3.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/data_sample4.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/data_sample5.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/example1.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/example2.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/example3.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/example4.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/example6.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/example7.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/example8.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/example9.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/format_chart.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/inline_image.php [deleted file]
gui/bacula-web/external_packages/phplot/examples/test_setup.php [deleted file]
gui/bacula-web/external_packages/phplot/phplot.php
gui/bacula-web/external_packages/phplot/phplot_data.php [deleted file]
gui/bacula-web/external_packages/phplot/rgb.inc.php

diff --git a/gui/bacula-web/external_packages/phplot/COPYING b/gui/bacula-web/external_packages/phplot/COPYING
new file mode 100644 (file)
index 0000000..602bfc9
--- /dev/null
@@ -0,0 +1,504 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
index b1c6d91afb42818db008f67c7716016a8cf96008..ace1b3bfc7ab5faf5b1d4964287518f7c7bf91ff 100644 (file)
-2004-10-24 18:40  migueldb
+This is the Change Log for PHPlot.
+The project home page is http://sourceforge.net/projects/phplot/
+-----------------------------------------------------------------------------
+
+2010-10-03 (lbayuk)     ===== Released as 5.2.0 =====
+    * phplot.php: Updated version
+    * README.txt: Updated for new release
+    * NEWS.txt: Add text for new release
+    * Makefile: Removed HorizontalBars.txt from release. This is now all
+          documented in the reference manual.
+
+2010-10-01
+    * Rewrite color handling. Default and specified colors are now validated,
+      converted to RGBA arrays, and saved. The colors indexes are not
+      allocated until graph drawing time. Unneeded colors are not allocated.
+
+      Fix bug 3045131 "SetTransparentColor problems": Transparency setup is
+      deferred to graph drawing time, after most other colors are allocated.
+      Fixes SetTransparentColor order dependency, and works with data colors.
+
+      Fix bug 3049726 "Optimize color allocation": Colors allocation is
+      deferred to graph drawing time, and colors for most elements are only
+      allocated if needed. In particular, the data colors and dark colors,
+      which used 32 slots, now only get defined as needed.
+
+      Feature request (partial) 3077554 "Finer control over plot element
+      colors": The colors of the main, X, and Y titles can now be controlled
+      separately.
+
+      Change details:
+      New internal functions:
+          GetColorIndex(), GetDarkColorIndex()
+              Allocate pre-parsed colors for elements.
+          SetColorIndexes()
+              Does the deferred color allocation and defaults.
+          NeedDataDarkColors(), NeedErrorBarColors()
+              Called by graph drawing functions if those colors are needed.
+          GetDataColor(), GetDataErrorColors(), GetBarColors()
+              Custom or standard data color assignment for most plot types.
+          truncate_array()
+             Shorten an array. Used to limit data color allocation.
+
+      Removed internal functions:
+          SetIndexColor(), SetIndexDarkColor()
+              Unneeded. Color handling is now in two separate phases.
+
+      Removed internal member variable:
+          data_colors_alpha
+              PHPlot no longer calls SetDataColors before plotting, so this
+              variable is not needed to preserve the default alpha.
+
+      New internal member variables:
+          transparent_color
+              Saves color value from SetTransparentColor. For bug 3045131.
+          x_title_color, y_title_color, ndx_x_title_color, ndx_y_title_color
+              For title colors. DrawXTitle and DrawYTitle use them now.
+
+      Changed internal member variables:
+          i_border, plot_bg_color, bg_color, label_color, text_color, etc.
+              All variables for colors now always hold a parsed 4-component
+              (RGBA) color specification as an array, and are no longer
+              statically initialized to a color name.
+
+      New public functions:
+          SetXTitleColor(), SetYTitleColor()
+              These can be used to make the 3 titles have different colors.
+
+2010-09-26
+    * Don't let SetXDataLabelPos() accept 'xaxis' or 'all' as valid.
+      Don't let SetXTickLabelPos(), SetYTickLabelPos() accept 'all' as valid.
+      These were never implemented and never documented.
+
+
+    * Feature request #3073679 "Stacked bar plots with negative values":
+      Allow stacked bar plots (vertical and horizontal) to work with negative
+      values.  Changed FindDataLimits() to not take abs() values for
+      stackedbar when calculating the data range.  Changed DrawStackedBars()
+      and DrawHorizStackedBars() to handle negative values, including proper
+      label positions.  The first non-zero value in a row sets the direction
+      of the bar. Mixing positive and negative values within a row works but
+      the results are not useful.
+
+    * New internal DrawBar() moves common code from 4 bars and stackedbars
+      drawing functions and makes it easier to deal with shading vs bar
+      direction.
+
+2010-09-24
+    * Fix bug 3074402 "Fix Y axis default for horizontal plots":
+      Make the default axis positions symmetrical for vertical plots
+      (X and Y) and horizontal plots (Y and X). This fixes the problem
+      with horizontal bar/thinbarline plots that have negative data,
+      where the Y axis stayed on the left and the bars went rightward.
+
+2010-09-01
+    * Fix bug 3056991 "Internal methods should be 'protected'":
+      Changed CheckDataArray(), number_format(), FindDataLimits(), and
+      DrawBackground() to be 'protected'. The test suite no longer calls
+      these directly. For SetIndexColor(), SetRGBColor(), DrawText(),
+      SizeText(), xtr(), and ytr(), left these as public with a comment
+      indicating they should be treated as protected.
+
+    * Fix bug 3057000 "Review 'deprecated' methods":
+      + Changed deprecated method SetNewPlotAreaPixels() to just call
+      SetPlotAreaPixels(). It was suspicious as coded, and was found
+      to be equivalent to SetPlotAreaPixels().
+      +  Removed SetColor(), which didn't do anything. It was calling
+      SetRGBColor and discarding the result. After reviewing the
+      history of this in all CVS versions, it does not seem to have
+      ever been coded correctly, so there is no harm in removing it.
+      + All other deprecated methods seem OK and are left as is.
+
+2010-08-30 (lbayuk)     ===== Released as 5.1.3 =====
+    * phplot.php: Updated version
+    * README.txt: Updated for new release
+    * NEWS.txt: Add text for new release
+
+2010-08-27
+    * Fix bug 3051832 "Let PHP/GD find the font file":
+      Instead of checking for file existence, PHPlot now uses a non-drawing GD
+      operation to validate a TrueType font in SetFontTTF(). This allows GD to
+      use its internal search path, making it likely that fonts can be found
+      without needing a platform-dependent path in your script.  Full paths
+      will still work, so there is no compatibility issue.
+
+    * Fix bug 3051906 "Better choice for default TT font":
+      Removed "benjamingothic.ttf" as the default TT font setting. This has
+      not been included in PHPlot since 2004. Changed SetFontTTF() to call
+      new GetDefaultFontTTF(), which will try a number of sans-serif font
+      names the first time it needs a default TT font. Considering the above
+      fix to finding fonts, this has a good chance of finding a working
+      font. It is known to work on Windows and some Linux distributions.
+
+2010-08-19
+    * Makefile: Removed phplot_data.php from list of files to release.
+      Reference bug report 3048267. This file has not been maintained or
+      tested, and has bugs.  It will remain in CVS, but not be included
+      in PHPlot releases.
+
+2010-08-17
+    * Change new DrawLinePoints(). It does not have to check and
+      handle error bar plots, as DrawDots and DrawLines will do that.
+
+2010-08-16
+    * Rewrote DecodeDataType(). Previous implementation was hard to
+      extend and inefficient. Now it uses new class variables (not a
+      returned array) and is only called once, by DrawGraph. Changed all
+      users of data_type to use the new variables.
+
+      In CheckDataArray(), calculate data_columns properly for
+      text-data-single data type (pie chart) too. Simplify DrawPie
+      to use this, and merge 2 of the 3 cases.
+
+      Have a single function handle each plot type, so it can properly
+      check the data type and report a correct error message showing
+      all supported types. For example, DrawBars is now the entry point
+      for both bars and horizontal bars; DrawGraph does not directly
+      call DrawHorizBars. Similar for DrawStackedBars and
+      DrawHorizStackedBars.  Lines, Points, and Linepoints also now
+      have a single function each, dispatching to others as needed.
+      (These changes were split off from an upcoming, larger change.)
+
+2010-08-14
+    * Fixes to SetDataValues: need to clear out the 2 arrays before
+      setting values in a loop, else if the function is used more than
+      once then old values will remain.
+      Move records_per_group calculation outside the loop.
+      (These were split off from upcoming, larger changes.)
+
+    * Part 4 of horizontal plots: implement horizontal thinbarline plots.
+      Added new data type 'data-data-yx' (label, Y, X1, X2, ...).
+      DrawThinBarLines() now draws either vertical or horizontal plots, and
+      supports data types text-data, data-data, text-data-yx, data-data-yx.
+
+      Fixed DecodeDataType() to handle text-data-single too, for completeness.
+
+    * Fix for over-padding style and color arrays. These were padded to
+      records_per_group, but that is the maximum data array row size.
+      The number of actual data sets is less, and depends on the data type.
+      Calculate the correct number as data_columns at the top of DrawGraph,
+      and use that to pad the arrays. Also moved early error checking in
+      DrawGraph() into new CheckDataArray().
+
+2010-08-09
+    * Code cleanup. This is a large change to unify the comment and
+      coding stye used in PHPlot. The PEAR style guide is being used
+      as a reference, but PHPlot will never be 100% compliant.
+      This patch introduces no functional changes at all.
+        - Limit line length to 110.
+        - Remove obsolete, partially implemented Doxygen comments.
+        - Add descriptive comment above functions where missing.
+        - Use consistent comment markers and control structure spacing.
+        - Remove ctags-confusing end of function comments.
+        - Rewrote a few if/else blocks for cleaner logic.
+        - Re-order some functions for consistency (X, then Y).
+
+2010-08-04
+    * Implemented horizontal stacked bar plots:
+      Use the same data type introduced for horizontal bars, 'text-data-yx',
+      and the same plot type 'stackedbars', to make a horizontal stacked bar
+      plot.  Data value labels, both at the ends of the bars and within the
+      bars ('plotstack' labels) are available with horizontal stacked bars.
+
+    * Internal function DrawDataLabel() is replaced by DrawDataValueLabel(),
+      with different usage. The new function can also check to see if a label
+      fits in the allocated space.
+
+    * Fixed the text size check in stacked bar plots for data value labels
+      within the bars. The check only worked correctly for 0 degree labels. It
+      now works with any text angle. It suppresses the label if it is too high
+      (for vertical stacked bar plots) or too wide (for horizontal stacked bar
+      plots) to fit in the bar segment to which it belongs. Note that it only
+      checks in the bar direction. If the text is too wide (for vertical bars),
+      or too high (for horizontal bars), it will still be plotted, but will
+      cross the sides of the bar.
+
+2010-07-28
+    * Allow callbacks to return a value (to support new data_color callback).
+    * Feature request 3034164 "Extended control of data colors":
+      Define new callback 'data_color' for picking the data color.
+      Changed internal plot drawing functions DrawDots, DrawLines, DrawSquared,
+      DrawBars, DrawStackedBars, DrawDotsError, DrawThinBarLines,
+      DrawLinesError, and DrawHorizBars to use the data_color callback
+      (if defined) to select the data colors.
+    * SetRGBArray code cleanup (no functional changes).
+
+2010-07-27
+    * Fixes for error bars:
+      Code cleanup in DrawYErrorBar(), with no functional change.
+      Suppress duplicate drawing of error bars with 'linepoints' error plots.
+      This was already fixed for data labels. Now error bars will only be
+      drawn by the 'points' part, not the 'lines' part. There should be no
+      visible changes to plots. (This is needed for a future change).
+
+2010-07-26
+    * Horizontal bar charts (still an experimental feature) can now have data
+      value labels.
+    * HorizontalBars.txt: Fix incorrect description of bar direction. Add
+      text explaining the new data value labels.
+
+2010-06-29 (lbayuk)     ===== Released as 5.1.2 =====
+    * phplot.php: Updated version
+    * README.txt: Updated for new release
+    * NEWS.txt: Add text for new release
+
+2010-06-26
+   * Feature request 2885930 "Horizontal Bars":
+     Horizontal bar charts are implemented, as an experimental feature.
+     A new data type 'text-data-yx' was added, which works with
+     'bars' plot type to produce a horizontal bar chart from a data
+     array with X values for each Y value. Changes were made to
+     FindDataLimits, CalcMargins, CalcPlotAreaWorld, CalcBarWidths,
+     and CalcMaxDataLabelSize to handle the new data type. Other
+     changes were made to handle label position defaults and grid
+     defaults. New drawing functions were added for horizontal bars.
+
+   * HorizontalBars.txt: new documentation file for experimental feature.
+   * Makefile: List new documentation file.
+
+2010-06-25
+   * Each plot-type drawing function now checks that it is getting a data
+     type that it knows how to handle. A new internal function unifies the
+     checking and error message. (This is associated with an upcoming,
+     bigger change.)
+
+     Compatibility: If you were using an invalid data type for a plot type
+     whose function did not check, will now get an error.
+     
+   * Removed some dubious code from DrawLines() and DrawSquared() and
+     rewrote comments there. The code initialized lastx[0] and lasty[0],
+     but lasty was mapped using the X (rather than Y) function.  This was
+     obviously wrong, but closer inspection showed that the values were
+     never, used so the code was removed.
+
+2010-06-13
+   * Truecolor.txt: removed
+   * Makefile, README.txt: Removed reference to Truecolor.txt. Full
+     documentation for truecolor images is now in the Reference Manual.
+     
+2010-06-02
+   * Fix bug 3010116 "Bad rendering of title in multi-plot image
+     when using TTF":
+     Make sure the main title is drawn only once. (If drawn multiple
+     times with TrueType font text, the anti-aliasing effects result
+     in poor quality text.)
+
+2010-05-31
+   * Improvements to truecolor support (from feature request 2947679):
+     Truecolor support is now better integrated. The derived class only
+     has the constructor now, and the base class itself provides the alpha
+     color component support through the internal functions SetIndexColor(),
+     SetIndexDarkColor(), and SetRGBColor(). This means alpha channel
+     works with palette images too (in so far as GD supports this).
+
+   * Truecolor.txt: Updated per changes to truecolor support.
+
+   * Image tiling with mode 'scale' in tile_img(), used with image and
+     plot area backgrounds, now uses imagecopyresampled() rather than
+     imagecopyresized(). They are the same with palette images, but the
+     resampled copy gets better results with truecolor images.
+
+2010-05-29
+    * Feature request 3002606 "Add to plot and image border options":
+        Added options 'right', 'top', and 'bottom' to SetPlotBorderType()
+      (existing options are 'left', 'sides', 'none', and 'full'). This
+      now also accepts an array of the above options, giving complete
+      control over which sides to draw.
+        Added option 'solid' to SetImageBorderType() to use the actual
+      color set with SetImageBorderColor(), rather than the darker
+      shade as type 'plain' does (for some reason).
+        New function SetImageBorderWidth() sets the width of the image
+      border. The image border width is now accounted for in margin
+      calculations, although existing plots will not change.
+
+2010-04-04 (lbayuk)     ===== Released as 5.1.1 =====
+    * phplot.php: Updated version
+    * README.txt: Updated for new release
+    * NEWS.txt: Add text for new release
+
+2010-04-01
+    * Remove & from argument in SetDataValues(). The data array is not
+      modified and does not need to be passed by reference. (There is
+      no performance advantage, either.)
+
+2010-03-29
+    * Feature request 2947679 "Support for alpha blending/Truecolor":
+      Implemented truecolor image support with a new class
+      PHPlot_truecolor, extended color specifications to allow
+      specification of an alpha value, and added a new optional parameter
+      to SetDataColors for a default alpha value for all data colors.
+      This feature is *EXPERIMENTAL* (see next item).
+
+    * Truecolor.txt: New file, documentation for the new truecolor capability.
+      (The Truecolor feature is experimental, which means it is subject to
+      change in incompatible ways and the documentation has not yet been
+      incorporated into the PHPlot Reference Manual.)
+
+    * Makefile: Include new documentation file in release.
+
+2010-03-26
+    Fixed bug 2976735 "Improvements and fixes for 'area' plots":
+    Rewrote DrawArea() function which handles 'area' plot.
+        Part 1: This is related to feature request 2947679, Truecolor support
+    with transparency. The area plot function was filling each area from the X
+    axis up to the Y value, resulting in area overlaps. This wasn't a problem
+    with opaque colors, but with transparency, the overlapping areas resulted
+    in changed colors. The rewritten function fills the area between each line
+    instead of from each line down to the X axis. Plots with opaque colors
+    will not change.
+        Part 2: Area plots now work when the X axis is moved up with
+    SetXAxisPosition().
+        Part 3: Fixed FindDataLimits() for area (and stackedbars too) to
+    take absolute values of Y values. The drawing function was doing this,
+    but not FindDataLimits, resulting in incorrect limits if any Y<0.
+        Part 4: The rewritten DrawArea() also handles a new plot type
+    'stackedarea'.  This is an area plot where the Y values are stacked,
+    similar to 'stackedbars'.
+        Note: As part of the changes, it is now an error to try an area plot
+    with an unequal number of Y points for each X.
+
+2010-03-23
+    * Feature request 2973995 "Add y-Data to Stackedbars":
+      Implemented Y Data Labels for Stacked Bar charts (stackedbars).
+      The labels are enabled with SetYDataLabelPos, same as with bar charts.
+      There are two types of labels: above the stack with the total, and
+      within the bars at each segment. 'plotin' turns on the upper ones, and
+      'plotstack' turns both on.
+
+    * Other changes:
+      + Removed unimplemented second argument to SetYDataLabelPos.
+      + Fixed questionable logic in SetYDataLabelPos when given an argument
+        that belongs with SetYTickLabelPos.
+      + Fix comments at top of plot-type Draw functions.
+
+    * Fix for bug 2974639 "Stacked bars plot breaks with X axis != 0":
+      Stacked bar plots with non-zero X axis position no longer break apart
+      into segments with gaps. The bars are drawn up from the X axis, and
+      any segments or partial segments below the X axis are not drawn.
+
+2010-03-22
+    * Change related to feature request 2947679 - Fix 'dot' point shape:
+      Use imagefilledellipse(), not imagefilledarc(), when drawing the 'dot'
+      point shape. The fix was needed for future support of truecolor images
+      with transparency, but filled dots from imagefilledellipse() look
+      better (rounder) with regular images and opaque colors.
+      Credit to mvaldez for identifying the problem and providing the fix.
+
+2010-03-04
+    * Fix for bug 2963757 "point_counts undefined error in 5.1.0":
+      Fixed CheckPointParams so it sets point_counts even when the point shape
+      and point size arrays are already the same size and do not need padding.
+
+2010-01-26
+    * Fix for bug 2938219 "Bars go in wrong direction":
+      Fixed CalcAxisPositions() to be consistent in positioning the X axis.
+      When all Y values are <0 and the Y=0 line is not part of the plot range,
+      PHPlot will now default the X axis to the top of the plot, not the
+      bottom.  This fixes the problem with bars to negative Y values being
+      drawn downward if Y=0 is visible, but upward if Y=0 is not visible.
+      This also affects thinbarline plots.
+      Credit to lauryn1298 for finding the bug.
+
+2009-12-24 (lbayuk)     ===== Released as 5.1.0 =====
+
+2009-12-18
+    * Change for bug 1795971 "Fix default data colors":
+      The default Data Color and Error Bar Color arrays now have 16
+      different colors, no duplicates, and nothing so light that it
+      is invisible.
+      Using '' or False as the argument to SetDataColors, SetErrorBarColors,
+      and SetDataBorderColors now re-initializes the map to the defaults.
+      This was previously undocumented, and in some cases set the map to
+      something different from the default.
+
+2009-12-15
+    * Cleanup: Remove DrawAxisLegend() - empty function marked TODO,
+      not really clear what it was meant to do.
+
+2009-12-14
+    * Fix for bug 2914403 "Pie + X/Y titles: Undefined property error":
+      In DrawGraph(), don't try to draw X or Y titles for pie charts.
+
+    * Feature request 2899921: "allow different format for data and tick
+      labels"; Bug 2906436: "Fixes for X Tick Labels vs X Data Labels",
+      and partial implementation of changes from user 'adoll' regarding
+      tick vs data labels:
+
+      New public functions:
+      + SetXDataLabelType() : Sets formatting for X Data Labels
+      + SetYDataLabelType() : Sets formatting for Y Data Labels (bar charts)
+      + SetXDataLabelAngle() : Sets text angle for X Data Labels
+      + SetYDataLabelAngle() : Sets text angle for Y Data Label (bar charts)
+      The defaults for these are set up to be fully backward compatible
+      with previous releases of PHPlot (except see the next item).
+
+      Re-used function name SetXDataLabelAngle():
+      + This has been deprecated and undocumented since 2003-12-07, and
+        used to just call SetXLabelAngle(). For new behavior, see above.
+
+      Changes to public functions:
+      + SetXDataLabelPos() and SetXTickLabelPos() no longer cancel each
+        other out (set the other control variable to 'none'). Instead,
+        they are both considered before plot drawing.
+
+      Changes to internal functions:
+      + DrawDataLabel() now uses the font, angle, and color arguments as
+        provided, and does not substitute values if they are empty.
+      + SetLabelType() now takes mode='xd' and 'yd' for X Data and Y Data
+        label formatting; 'x' and 'y' are for tick labels only now.
+      + Functions that work on Data labels now call FormatLabel() with the
+        new mode parameter value 'xd' or 'yd, and use the new
+        data_label_angle variables.
+      + New CheckLabels(), used by DrawGraph to process label parameters.
+      + CalcMargins() - Rewritten to handle changes to Tick and Data labels.
+
+      Changes to internal class variables:
+      + New: x_data_label_angle, y_data_label_angle
+      + Do not initialize x_tick_label_pos or x_data_label_pos, so that
+        CheckLabels() can tell if they were set or not and apply defaults.
+      + Initialize y_data_label_pos to 'none', not 'plotleft'.
+      + Add 2 more indexes to label_format[] array: 'xd' and 'yd'.
+
+    * Cleanup:
+      + Delete unused internal class variable: draw_y_data_label_lines
+      + Delete unused function SetDrawYDataLabelLines()
+
+2009-12-07
+    * Fix bug 1795972 "Fix default point shapes":
+      +  Added 10 new point shapes to the existing 10 shapes.
+      +  Changed the default point shape from all 'diamond' to a
+         selection of up to 10 different shapes.
+      +  Fixed bug in the code that tried to set the point shapes
+         and sizes arrays to be the same size. This was not working,
+         resulting in unexpected point sizes.
+      +  Changed default point size to 6 for all shapes. It was trying
+         to be "5, 5, 3" but due to several bugs this was not working.
+      +  Do not adjust shape sizes to even numbers (was done for only two
+         shapes).  Instead, consistently truncate size/2 when needed.
+      NOTE: These may change the look of 'points' and 'linepoints' plots.
+
+    * Changed startup initialization code:
+      +  SetDefaultStyles() was doing some odd things using a variable
+         called "session_set", with comments referring to non-existent
+         session support code. This has been removed. There should be
+         no visible changes from this. PHPlot does not use PHP sessions.
+
+2009-12-04
+    * Fix for bug 2908256, errors in pie charts with bad data array:
+      (From a Drupal contrib module report by thekevinday.)
+      With pie charts only, a data array with no valid Y values resulted
+      in PHP error messages. All other plot types handle this by producing
+      an image without a graph.
+      Fixed DrawPieChart to behave this way too. If there are no valid Y
+      values, or if the sum of all Y values is 0, do not error out, but
+      don't draw a pie chart either.
+      Also, pie charts now ignore non-numeric Y values, like other plot types.
+
+2009-11-20 (lbayuk)
+    * Fix for bug 2900914 "Problem with display of 0 on Y axis":
+      Changed how X and Y values are stepped by tick intervals, to avoid
+      cumulative round-off error. This fixes the problem when Y crosses 0 with
+      a tick step such as 0.1 resulting in a long label for a very small but
+      non-zero number. Fixed DrawXTicks, DrawYTicks, and CalcMaxTickLabelSize.
+      (Originally reported by cncnet)
+
+2009-11-19 (lbayuk)
+    * Improve support for using callbacks to annotate plots:
+      Added new callback 'draw_all', called after all drawing.
+      Supply plot_area[] as argument to some drawing callbacks.
+      Added new method GetDeviceXY() to translate from world coordinates.
+      Allow NULL or '' for $font in DrawText() internal method, meaning to
+          use the generic font. If callbacks want to use DrawText, this
+          avoids them having to reference the internal fonts[] array.
+
+2009-11-01 (lbayuk)
+    * Address bug report 2886365 "Declare all functions and variables in
+      PHP5 style"
+      PHP5 deprecates the use of 'var' to declare a class member variable.
+      All initialized class member variables are now declared 'public'.
+      (It was tempting to make most or all 'protected' or 'private', but
+      that would likely break too much, including the PHPlot Test Suite.)
+
+      Most class member functions which are meant for internal use only are
+      now declared 'protected', so they cannot be called from scripts
+      (except in child classes). (Note PHP5 does not deprecate the use of
+      just 'function' to mean public, so public functions were not changed.)
+      Internal functions are those documented in the manual under Developer's
+      Guide, Internal Functions. If your code breaks because you are using
+      a method which is now protected, please post the details on the help
+      forum.
+
+      Some member variables which were set in the constructor are now
+      initialized with the class instead. (No impact.)
+
+      Removed commented-out, FIXME-noted code for interim labels.
+
+2009-10-12 (lbayuk)
+    * Bug report 2839547, allow SetImageBorderType('none') to reset the image
+      border type.  Also checked for other cases where there is no reset;
+      found one that exists (Set[XY]LabelType) but needs to be documented.
+
+2009-07-09 (lbayuk)
+    * Added a hook $plot->locale_override which can be set to True to prevent
+      PHPlot from loading locale settings from the environment with
+      setlocale(LC_ALL, ''). This is necessary for testing PHPlot on Windows,
+      where you cannot force a locale with an environment variable. It might
+      also be needed for people who want PHPlot's locale to differ from the
+      web server's locale.
+
+2009-06-12 (lbayuk)     ===== Released as 5.0.7 =====
+
+2009-06-11 (lbayuk)
+    * Change PHPlot license to LGPL, per Afan.
+        phplot.php, phplot_data.php - Change license notice.
+        rgb.inc.php - Change top comments and remove bottom marker.
+        COPYING - new file, text of LGPL.
+        LICENSE.* - removed files - old licenses.
+        Makefile - change list of distributed files.
+
+    * Fix for bug 2803900: SetRGBArray('large') does not work. The include
+      file defined a different array name than the main script expected.
+      (This bug seems to have happened over 8 years ago.) Fixed the array
+      names to match. Also removed the ./ prefix from the included filename
+      so it will be found if on the include path but not in the script
+      directory.  Also added error check if the rgb.inc.php include file
+      is needed and not found.
+
+2009-05-25 (lbayuk)
+    * Added new feature to allow partial margin or plot area specification.
+      You can omit, or specify as NULL, any of the 4 arguments to
+      SetMarginsPixels() or SetPlotAreaPixels(), and this means PHPlot
+      should use the automatically calculated margin on that side.
+      Credit to adoll for this feature.
+
+2009-05-17 (lbayuk)
+    * Fix for bug 2791502 "Error plots treat missing Y values as 0":
+      Plots with data type data-data-error now support missing Y values,
+      instead of treating them as 0. This works with lines, points,
+      and linepoints plot types, and also honors SetDrawBrokenLines.
+
+
+    * Fix for bug 2792860 "Wrong DataLabelLines with missing Y":
+      Do not draw X Data Label Lines at points with missing Y values.
+
+
+    * Fix for bug 2786350 "Missing Y data results in bad auto-range":
+      Rewrote FindDataLimits to ignore missing Y values, rather than
+      treating them as if 0, for calculating range.
+      Bug report and analysis by mrten.
+
+    * Fix for bug 2786354 "Incorrect auto-range for data-data-error":
+      For data-data-error data type, apply the positive and negative error
+      amounts for each Y point to that point only, rather than applying the
+      largest errors to the overall minimum and maximum Y value for the row.
+
+    Note: The two fixes above can change existing plots which rely on
+      automatic Y range calculation. The first fix affects plots with
+      missing Y values and min(Y)>0. The second fix can affect plots using
+      data-data-error data type and different error values for different
+      points. In both cases the new Y range can be smaller than before.
+
+2009-01-20 (lbayuk)     ===== Released as 5.0.6 =====
+
+2009-01-18 (lbayuk)
+    * Fix for bug 1891636 "Misaligned TTF X Labels":
+         PHPlot was using the actual bounding box of each line of text
+      to allocate space and set the text positioning, but was ignoring the
+      fact that the text baseline is not the same as the bottom of the
+      bounding box. This resulted in uneven alignment of the X labels if
+      they had different heights (for example, month names Jul and Aug).
+
+      PHPlot now calculates the size of text for allocation (SizeText) using
+      the descenders on the last line, and calculates the size for drawing
+      (DrawText) only to the baseline. PHPlot also now uses a fixed line
+      spacing for each line of text in a font, rather than the actual text
+      height. This allows separately drawn multi-line labels to align.
+
+    * Changes to line spacing when using multi-line labels:
+         PHPlot was using the class variable line_spacing to mean the
+      number of pixels between lines of multi-line labels. This made the
+      spacing too small for larger fonts, and it was not possible to adjust
+      line spacing for different types of text.
+
+         PHPlot now interprets line_spacing as the number of pixels only
+      for GD text, and as a scale factor for the font's built-in line
+      spacing for TrueType text. In addition, a new optional argument is
+      added to SetFont, SetFontGD, and SetFontTTF to set a line spacing
+      specific to that type of text.
+
+    * Changes had to be made to the legend drawing code to accommodate the
+      changes to font handling.
+
+      Note: The line spacing change results in slightly looser spacing on
+      multi-line TrueType text labels, and slightly taller legends, compared
+      to version 5.0.5.
+
+2008-09-21 (lbayuk)
+    * Interim fix for bug 1932571 "Data-Data Plot fails with same X values".
+      PHPlot will no longer hang when the range of X values is 0 (that is, when
+      x_min == x_max). It will arbitrarily set an X range of 1, so the
+      calculated tick step is not 0. This is a temporary fix. Work on a smarter
+      X and Y range calculation is in progress, which will handle edge cases
+      like this better, but it isn't ready and this bug has been open too long.
+      Credit to andyl for finding the bug.
+
+    * Fix font path: Use DIRECTORY_SEPARATOR constant not '/'.
+
+    Extended the label formatting capabilities, adding 'printf' and 'custom'
+    types, added a prefix and suffix for 'data' type, and allow format controls
+    to be included in SetXLabelType and SetYLabelType.
+
+    External changes:
+    * Added 'printf' label type. The caller specifies the print format as the
+      2nd argument to SetXLabelType or SetYLabelType (default '%e').
+             $plot->SetXLabelType('printf', '%5.2f');
+
+    * Added 'custom' label type. The caller supplies a callback (typically a
+      function name) and optional pass-through argument as the 2nd and 3rd
+      arguments to Set[XY]LabelType.  The function is called as $f($value, $arg)
+      to return the formatted $value.
+             $plot->SetXLabelType('custom', 'myfunction', $arg_value);
+
+    * In addition to Set[XY]TimeFormat, the format string for type 'time' can
+      now be set as the 2nd argument to Set[XY]LabelType.
+             $plot->SetXLabelType('time', '%H:%M');
+
+    * In addition to SetPrecision[XY], the precision for type 'data' can now be
+      set as the 2nd argument to Set[XY]LabelType. A 3rd and 4th argument
+      can supply a prefix and suffix for 'data' formatting. (All optional)
+             $plot->SetXLabelType('data', 2, '$', 'US');
+
+    Internal changes:
+    * Class variables x_precision, y_precision, x_label_type, y_label_type,
+      x_time_format, and y_time_format have been removed.
+
+    * New class array variable label_format[], with elements 'x' and 'y' which
+      are arrays for label formatting. Elements in the sub-arrays are not
+      initialized until needed.
+
+    * New function SetLabelType, which implements Set[XY]LabelType now.
+
+    * FormatLabel() was rewritten to support the new label formatting.
+
+    Compatibility:
+    * Any code that directly references class variables related to label
+      formatting will break, except for data_units_text. Use the documented
+      function methods instead.  Setting data_units_text as a suffix is
+      deprecated but still works.
+
+    * The 'data' type precision for 'Y' is still used for pie chart labels.
+
+2008-07-12 (lbayuk)
+    Multiple comment spelling error fixes. No functional changes.
+
+2008-07-06 (lbayuk)
+    Changes to allow mixing GD fixed-font text and TrueType Font (TTF) text
+    on the same plot.
+    (This change came from work done trying to fix TTF text positioning,
+    where it looks like additional information needs to be stored for TrueType
+    fonts. The old font data structure was awkward to extend, and allowing
+    mixed GD/TTF text was on the to-do list anyway.)
+
+    External changes:
+    * SetFontGD(), SetFontTTF(): New functions to set a font, with type.
+    * SetFont(): Now calls SetFontGD or SetFontTTF depending on $use_ttf.
+    These changes should be fully compatible with existing programs.
+
+    Internal changes:
+    * Updated comments explaining SetUseTTF() now sets the default type
+      (not the only type) of text used.
+    * Put all the font data into a class array. (Replaces $this->generic_font
+      with $this->fonts['generic'], etc.)
+    * ProcessTextGD() and ProcessTextTTF() now take the font array as one
+      argument, rather than separate arguments for font path and size.
+
+2008-01-13 (lbayuk)     ===== Released as 5.0.5 =====
+    * phplot.php: Updated version
+    * README.txt: Updated for new release
+    * NEWS.txt: Add text for new release
+    * Makefile: Remove 'Callbacks' from release target, as this material is
+      now in the reference manual.
+
+2008-01-07 (lbayuk)
+    Copyright updated to 2008 and PHP4 no longer listed as supported.
+
+    Major rewrite of the margin calculation functions to address multiple
+    problems.  Fixes for bugs 1856207 "Margin error with 'xaxis'/'yaxis'
+    position, 1843012 "Make margins, drawing consistent", and 945439
+    "x_tick_label_height not set correctly".
+
+    Note: These changes are inter-dependent and cannot be split up.
+
+    * Defer all calculations to DrawGraph time, to eliminate order dependencies.
+      These functions now just store their arguments in the object, and all
+      calculations happen later:
+    + SetXAxisPosition, SetYAxisPosition
+    + SetMarginsPixels
+    + SetPlotAreaPixels (Stores margins, not area, now.)
+    + SetPlotAreaWorld
+    + SetXTickIncrement, SetYTickIncrement
+
+    * A new callback 'debug_scale' was added to trace the margin and scale
+      calculations.
+
+    * CalcMargins was rewritten. Actual sizes of tick and data labels are now
+      used, rather than guesses like "use size of biggest Y value". A minimum
+      value (3 x safe_margin, or 15 pixels) applies to each margin.
+
+    * FindDataLimits no longer needs to find the longest data label, since
+      CalcMargins now does that more precisely.
+
+    * DrawXTitle and DrawYTitle now use position offsets calculated by
+      CalcMargins. Note: These titles are now offset from the plot area,
+      not the image area. The titles will move if you had set the plot area
+      or margins.
+
+    * DrawYTick, DrawXTick rewritten to use pre-calculated offsets, and common
+      code moved to new CalcTicks().
+
+    * DrawXDataLabel: Use pre-calculated offsets for text.
+
+    * DrawGraph: Rewrote top section (before drawing anything) to do the
+      calculations in the proper order, unconditionally.
+
+    * Class variables removed:
+      x_label_inc, y_label_inc, _x_label_cnt : These were never used.
+      title_height, x_title_height, y_title_width : Now internal to CalcMargins.
+      data_limits_done : No more need to remember if FindDataLimits called.
+
+    * New class variables added:
+      plot_margins_set : Keeps track of user-set plot area or automatic.
+      x_label_top_offset, x_label_bot_offset, x_offset_axis_offset,
+          y_label_left_offset, y_label_right_offset, y_label_axis_offset,
+          x_title_top_offset, x_title_bot_offset,
+          y_title_left_offset, y_title_left_offset : Label offsets
+      
+    * New internal functions:
+      CalcPlotAreaPixels : Deferred calculations taken out of SetPlotAreaPixels
+        and SetMarginsPixels.
+      CalcPlotAreaWorld : Deferred calculations taken out of SetPlotAreaWorld.
+      CalcAxisPositions : Calculate axis positions, moved from CalcTranslation.
+      CalcTicks : Calculate X and Y tick interval. This still uses the
+        same simple method (basically range/10), but now we could drop in a new
+        algorithm much more easily. This is now also used by CalcMargins.
+        Code taken out of DrawXTicks and DrawYTicks.
+      CalcMaxTickLabelSize : So CalcMargins can use the exact tick label sizes.
+      CalcMaxDataLabelSize : So CalcMargins can use the exact data label sizes.
+      DrawXTick : Code split out from DrawXTicks for symmetry with DrawYTick.
+
+
+2007-12-13 (lbayuk)
+    * Changed ProcessTextTTF() so SizeText() will return integers. It rounds
+      the calculated values up, so the bounding box really contains the text.
+      This also prevents unneeded float calculations in derived values.
+
+2007-12-09 (lbayuk)
+    Major rewrite of the text drawing functions to address multiple problems.
+    Note: These changes are inter-dependent and cannot be split up.
+
+    * Fixed bug 1813070 "Bad position for multi-line TrueType text":
+      TTF text is now drawn line-by-line, not as a block, for proper
+      alignment and positioning.
+
+    * Fixed bug 1813071 "Wrong title height for multi-line TTF text":
+      Corrected miscalculation of overall height of multi-line TTF titles.
+      This bug resulted in over-sized margins.
+      The height is now computed line-by-line, including the inter-line spacing.
+
+    * Fixed bug 1813474 "DrawText alignment arguments wrong":
+      Corrected meaning of 'top' vs 'bottom' alignment. PHPlot now follows
+      the usual conventions: 'top' alignment means top of text to reference.
+      DrawText default for vertical alignment is still 'bottom', but the
+      meaning was corrected.  All callers of DrawText were fixed.
+
+    * Fixed bug 1816844 "Fix order dependency for setting titles":
+      Defer processing titles strings until DrawGraph(), so there is no
+      more order dependency (no need to set font before setting title strings).
+
+    * Fixed bug 1819668 "Horiz. align multi-line text: GD vs TTF":
+      The new text routines draw TTF text line-by-line and correctly do
+      right-, center-, and left- alignment of each line within a text block.
+
+    * Fixed bug 1826513 "FIXME in DrawLegend: Max label length":
+      Use actual width of widest legend line to calculate legend box size.
+
+    * Partial fix for bug 945439 "x_tick_label_height not set correctly":
+      In FindDataLimits(), save the longest data label, not just its length,
+      and use the actual rendered size of that string in CalcMargins() for
+      the margin calculations.
+      Also take into account which of the tick or data labels are visible.
+      This is not a complete fix, but is a significant improvement.
+
+    The following changes were made related to the above fixes:
+
+    + Replaced internal function TTFBBoxSize(), which didn't work right, with
+      SizeText(). It returns the orthogonal bounding box of a block of text,
+      and works with both GD and TTF text.
+
+    + DrawText() and SizeText() call a single function ProcessText(), which is
+      the only place GD text and TTF text are distinguished. (So eventually
+      we will be able to mix GD and TTF text on a plot.)
+
+    + New internal functions ProcessTextGD() and ProcessTextTTF() draw (or size)
+      GD and TTF text respectively. These are only called by ProcessText().
+      These are re-implementations which properly position and align text.
+
+    + Removed class variables title_angle, x_title_angle, and y_title_angle. The
+      titles only work at their fixed angles anyway (0, 0, and 90 respectively).
+
+    + Line spacing set with SetLineSpacing() now affects TTF text as well as
+      GD text. Previously, it only affected GD text. The default line spacing
+      happens to be usable for TTF text.
+
+    + Added new callback hook 'debug_textbox' for developing, testing, and
+      documenting. It provides access to the text area bounding box.
+
+    + Removed unneeded class variables x_tick_label_height, y_tick_label_width,
+      x_tot_margin, y_tot_margin.
+
+2007-11-25
+    * Improve error handling:
+      Internal functions PrintError() and DrawError() are now the same. Both
+      will draw the error message into the image and output it, and then
+      trigger a user-level error. If no error handler has been set, it will
+      exit, as before. But now the error message should also get logged, or
+      written to the standard error stream, depending on the SAPI in use.
+      You can now establish an error handler to catch most PHPlot errors and
+      do some cleanup before exit.
+
+      This fix also covers bug #1823774 "Default Font Path and Error Message
+      Output".
+
+      Fixed the return value of most PHPlot functions, to return False on
+      error, else True. Since uncaught errors are fatal anyway, this only
+      affects code with an error handler that returns, which is not
+      recommended and unsupported at this time. These changes are for
+      possible future error handling options.
+
+2007-11-22
+    * Fix bug 1836528 "Insufficient checking of parameter values":
+      Rewrote CheckOption to correctly validate option choices.
+      (It previously accepted substrings and other incorrect values.)
+      PHPlot methods that use CheckOption now must be called with valid option
+      values. Empty strings are also no longer accepted.
+
+2007-11-17 (lbayuk)
+    * Change to callbacks to support extra arguments.
+      The PHPlot class can now pass extra arguments to a callback function.
+      Callback functions now take the following form:
+          my_callback($img, $passthru_arg, ...)
+      Where '...' is zero or more additional arguments supplied by PHPlot to
+      the callback. Each implemented callback reason will define any
+      additional arguments it uses. The existing defined callbacks have not
+      changed and do not currently pass any extra arguments.
+
+2007-11-10 (lbayuk)
+    * Fix bug 1827263 "Spoiled up pie-chart if $val is close to zero":
+      Skip pie slices which would result in an integer angle of zero
+      degrees, because the GD arc filling function will draw a complete
+      circle for that case.
+      Credit to Viacheslav <webdeveloper.ua at gmail.com> for finding this.
+
+    * Removed 8 of the functions (class methods) marked 'deprecated'.  Only
+      deprecated functions which seem to have been for internal use have
+      been removed. Even old scripts shouldn't be using them, and they are
+      becoming a problem to maintain.
+      Removed: SetImageArea() DrawDotSeries() DrawLineSeries() CalcXHeights()
+      CalcYWidths() DrawLabels() InitImage() DrawDashedLine().
+
+2007-10-20 (lbayuk)     ===== Released as 5.0.4 =====
+    * phplot.php: Updated copyright, version, and authors comments at top.
+    * README.txt: Updated for new release
+    * NEWS.txt: Add text for new release
+
+2007-10-18 (lbayuk)
+    * Add callbacks - experimental feature:
+      New functions SetCallback, GetCallback, RemoveCallback.
+      New internal function DoCallback.
+      Added callback hooks to DrawGraph.
+
+      Re-arranged code in DrawGraph to bring pie chart drawing into the main
+      switch on plot type, rather than a special case in its own block. This
+      makes it easier to follow and easier to add callback hooks.
+
+    * Callbacks: New file, documentation for the new callbacks feature.
+      (This won't be in the manual while it is an experimental feature.)
+
+2007-10-15 (lbayuk)
+    * Fix for bug 1813021: Miss-positioned right-justified vertical GD text.
+      Fixed DrawText() to correctly position 90 degree right-justified text
+      drawn in a fixed GD font. This could be seen with 90 degree Y tick
+      labels. (Found by accident while working on TrueType text problems.)
+      Also some code cleanup in DrawText: use elseif where appropriate.
+
+2007-10-09 (lbayuk)
+    * Code cleanup: Simplify SetIndexColor() and SetIndexDarkColor().
+      There is no need to first try ImageColorExact, then ImageColorResolve
+      if that fails. ImageColorResolve does all that for us.
+
+      Code cleanup: Rewrite SetRGBColor(). It now detects if an unrecognized
+      color name or color value form is used, and draws an error message.
+      Before this it would get a PHP index error and "headers already sent"
+      condition.
+
+    * Code cleanup: Remove duplicated code for loading image files.
+      Added new class-private function GetImage() which loads an image based
+      on the image type, and also returns the image size. This replaces
+      duplicated code in tile_img() and SetInputFile().
+      Also fixed comment at top of SetImageFile which said it was deprecated.
+      It isn't - it is used by the constructor. Moved the function out of the
+      'deprecated' area up to below where it is used.
+
+    * Code cleanup: PHPlot should not define or affect anything outside its
+      own class.
+      - Removed the check for __FUNCTION__ (PHP 4.3 and up). This is obsolete.
+      - Do not set error_reporting to E_ALL. Although it is recommended that
+      scripts do this, it is not the place of loaded classes to do it.
+      - Remove unused global constant TOTY.
+      - Removed constants MAXY and MINY. Global constants like this are bad.
+      These were used as magic index values into data[] to hold min and max Y
+      values for the row. Instead, put them in separate arrays which are
+      named data_miny[] and data_maxy[]. (This seems to be only used by the
+      data line drawing function.)
+
+      Comment cleanup: Remove one commented-out partial function DrawPlotLabel,
+      and fix another commented-out code fragment in DrawYErrorBar. Both of
+      these had unmatched braces in them which caused a balance-braces check
+      to fail.
+
+    * Code cleanup, array padding: Get rid of functions outside the class
+      and remove the interim fix for PHP 5 (which changed the behavior of
+      array_merge).  Rewrote external function array_pad_array() as a new
+      class function pad_array().  It does not need access to the class,
+      but I don't think PHPlot should add to the global namespace more
+      than necessary.  The third argument (array to use for padding) was
+      never used, so it was removed. It always pads the array with itself.
+      It now only works on 'usual integer indexed' arrays (0-based
+      sequential integer index). The was previously required but
+      undocumented for some of the arrays (like line_widths); now it is
+      required for all style arrays and will be documented. Now we can pad
+      the array to the required length, not just N times its previous
+      length, and we don't need array_merge.  Deleted external function
+      array_merge_php4() as it is no longer used.
+
+      Deleted PHP end marker ?>. You don't need this and it can cause
+      problems with extra whitespace in your output.
+
+2007-09-24 (lbayuk)
+    * Code cleanup: Fix ternary operator misuse.  This doesn't change
+      behavior, but it was annoying me so I fixed it.
+      Replaced all cases of code like this: $a = ($a > $b) ? $b : $a
+      With just: if ($a > $b) $a = $b
+
+    * Fix Makefile 'release' target to set owner/group when creating
+      the tar file. This avoids having to run it as root, but it needs
+      GNU tar to work.
+
+2007-09-08 (lbayuk)
+    * Fix for bug 1790441: Removed the PHPlot quasi-destructor function and
+      the register_shutdown_function() call which arranged for it to be used.
+      This was preventing release of memory when a PHPlot object was unset,
+      because the registered shutdown function held a reference to it.
+      So rather than improving memory use, it had the opposite effect.
+        Note: It is no longer necessary or recommended to use reference
+        assignment ($plot =& new PHPlot) for PHPlot object creation.
+      Thanks to annajilly for the thorough analysis, bug report, and fix.
+
+2007-09-05 (lbayuk)
+    * Rewrote FormatLabel() to ignore blank label values. Adapted from a
+      patch and feature request submitted by Gerhard Reithofer (exgerhardr).
+      Blank labels used to produce an error if the LabelType was set to
+      'time', and zero if set to 'data'. Now they are just ignored. This
+      provides a simple way to have labels only at selected intervals when
+      using time or data formats. For example, you can have a date/time
+      label at every 10th data point by setting the labels for the other 9
+      to be empty strings. Also: Removed $which_pos values 'plotx' and
+      'ploty'.  These were unused by PHPlot and this is an internal-only
+      function so there is no compatibility issue. Removed error checking on
+      $which_pos for the same reason; the error message used an undefined
+      variable anyway so it wouldn't have worked.
+
+2007-08-26 (lbayuk)
+    * Allow SetLegendStyle colorbox_align argument to be 'none', to suppress
+      the colorboxes in the legend.
+
+      Fix comment on $legend_text_align: empty means right, not left.
+
+      Rewrote DrawLegend layout code to make it easier to understand. The
+      result should be within 1 or 2 pixels of the previous size and position.
+
+    * Fixes for bug 1779115: SetLegendWorld() fails on undefined vars
+      Store the given coordinates and remember that they need to be converted
+      from world to pixel coordinates, but defer trying to actually convert
+      them until it is time to draw the legend. This way, there are no
+      problems with the scale having to being set up first (which is nearly
+      impossible to do).  Made the following changes:
+
+        Changed legend class variables to be uninitialized, and unset (rather
+        than empty string) means use the defaults. Added a new variable:
+        $legend_xy_world. If it is set, (legend_x_pos, legend_y_pos) need to
+        be converted to pixel coords. If it is unset, they are already pixel
+        coords (or undefined, meaning defaults).
+
+        Changed usage of internal function DrawLegend(): removed all arguments.
+        X and Y were always the class variables anyway, and now it needs to
+        also use the new flag to tell it if X and Y are world or pixel coords.
+        The third argument was unused.
+
+        Removed third, unused, default NULL argument from SetLegendPixels and
+        SetLegendWorld.
+        Changes to DrawLegend to convert x, y coords to pixel coordinates
+        if they came from SetLegendWorld. Also account for new usage of
+        the class variables: Test for unset to mean use default.
+
+2007-08-04 (lbayuk)
+    * New feature: control legend text and color box alignment.
+      Adds a new function SetLegendStyle to adjust the alignment of the
+      text and the color boxes inside the legend.
+      Based on part of bug 1208054, contributed by David Hernández Sanz.
+
+2006-12-02 (lbayuk)
+    * Fixes for bug 1605555: Y Data Labels use wrong font and not formatted.
+      Use y_label_font (not x_label_font) for Y Data Labels.
+      Use the formatted value for the label, not the original text.
+      (This applies to bar charts only, with the new Y data labels.)
+
+    * One fix for bug 1208054: Localization of number format.
+      If number formatting is enabled with 'data' format type, PHPlot previously
+      used dot for decimal point and comma for thousands separator, and there
+      was no way to change it.
+      
+      This fix adds a new function:
+               SetNumberFormat($decimal_point, $thousands_separator)
+      to set the separators.  In addition, if that function is not used,
+      PHPlot will now try to use locale-dependent separators. If locale
+      information is not available, it will fall back to the old defaults
+      of dot and comma.
+
+      Note: This change may have some negative effects. 1) If your locale is
+      "C" or "Posix", you might not get a thousands separator now by default.
+      You should be using a more specific locale. 2) If your PHP script is
+      forcing a specific locale with setlocale(), PHPlot will probably undo
+      that because it uses setlocale(LC_ALL, '') to import locale information
+      from the environment.  We have to do that, or a locale set through
+      the environment is ignored. But it will override a manually set locale.
+
+    * Fix for bug 937944: X/Y Tick Counts
+      PHPlot could draw one too few Y tick marks, and one too many X tick marks.
+
+      Changed the code to stop drawing X (Y) tick marks when the current X (Y)
+      value exceeds the maximum X (Y) value plus a small fudge factor. The fudge
+      factor accounts for cumulative error when repeatedly adding a delta to
+      the X (Y) value.
+
+      Notes: The bug report was writing about Y tick counts only, but X tick
+      counts can also be wrong. The proposed fix in the bug report does not
+      work in all cases.
+
+      This fix changes the appearance of many plots which were missing the
+      top-most Y tick mark. The extra X-tick mark problem is less common.
+
+===== Released as 5.0rc3 =====
+
+2006-11-13 (lbayuk)
+    * Fix for bug 1437912: x-axis label misalignment [bar charts]
+      The calculations were redone from scratch.
+      New control variable 'bar_extra_space', which works in addition to
+      'group_frac_width' to control how much extra space is around the bars.
+      Made bar widths match for 'stackedbars' and 1-bar-per-group 'bars'.
+
+      NOTE: This changes the appearance of charts. bars in 'stackedbars'
+      will now be thinner, and bars in 'bars' graphs will be thicker. I
+      saw no reason for them being different before.
+
+      This fix required fixing the positioning on the new bar data labels,
+      which was off before.  The bar data labels will now be centered.
+      Additional fixes to bar chart data labels:
+         For negative values, the label will center under the bar.
+         Fixed X-adjustment to account for shading.
+         Fixed to not suppress the data label if the value is 0.
+
+
+2006-11-10 (lbayuk)
+    * Fix for bug 1594457: DrawError text wrap and background fix
+      Do error image white background correctly, and word-wrap the text.
+
+    * Fix for bug 1594458: Suppress lines or points in 'linepoints' 
+      Don't draw X data labels twice for 'linepoints'.
+      Allow SetPointShapes value 'none' to suppress points, and allow
+      SetLineStyles value 'none' to suppress lines. This allows a 'linepoints'
+      graph to mix lines only, points only, and both on the same graph.
+
+
+2006-11-09 (lbayuk)
+    * Fixes for bug 1446523:
+      + Wrong variable name in deprecated SetAxisFontSize()
+      + Fails to properly handle error if SetDataValues() was never
+        called, or not called with a data array.
+
+    * Fix for bug 1117122: Pie Chart ignores SetPlotAreaPixels
+      Don't let DrawGraph recalculate the plot area for pie charts if the
+      user already set it with SetPlotAreaPixels.
+
+      NOTE: This fix may slightly change the appearance of some pie charts,
+      whether or not they use SetPlotAreaPixels.
+
+    * Fix for bug 1103992: Wrong max Y calculated for stackedbars
+      Changes FindDataLimits to calculate max Y correctly. It was counting
+      the first Y value in each record twice, which is always wrong but
+      only affected stackedbars because the Y values are summed.
+
+    * Fix for bug 1096199: Wrong error bar colors in DrawDotsError.
+      Rewrites DrawDotsError to make it work like DrawLinesError to
+      correctly increment the record and color indexes.
+      Also fixes uninitialized x_now_pixels.
+
+    * Fix for bug 1096197: No borders on unshaded Draw[Stacked]Bars
+      Unshaded Bars and StackedBars covered the border with the rectangle.
+      The fix is to draw the rectangle, then the border.
+
+      NOTE: This fix changes chart appearance. Bars and Stacked Bars
+      will now get a black border around each bar by default, if you
+      turn off the 3D-shading. If you want borderless, unshaded bars
+      you need to use SetDataBorderColors to set the data border colors
+      to be the same as the data colors.
+
+    * Fix for bug 1333164: Negative data values, if string variables, result
+      in unfilled bars. The problem was a string-to-string compare of a
+      negative number with the empty string x_axis_position. Fixed by
+      initializing x_axis_y_pixels to 0 if SetXAxisPosition was not used.
+
+
+2005-04-17 (afan)
+    * Fix for bug [ 1161072 ] SetInputFile warning, background overwrite
+
+    * Bug 1182672 fixed
+
+2005-04-15 (afan)
+    * fix for bug: [ 1182666 ] Y Auto-scale rounds in wrong direction
+
+    * Fix for bugs 1144644 TrueType font path problems and 1106328 TTF
+      path/filename inconsistency
+
+    * Fix Bug: [ 1117120 ] X Title sizing uses Y Title font height
+
+2005-04-13 (afan)
+    * Error in SetLineStyles() - does not accept an array argument
+
+
+2005-03-29 (afan)
+    * Small typo fixed in SetYDataLabelPos
+
+    * Update SetDataLabelPos: For past compatibility we accept plotleft,
+      ...but pass it to SetTickLabelPos
+
+2005-03-26 (afan)
+    * Change to line 3802: data lables now work with multiple bars with *$idx
+
+2005-03-25 (afan)
+    * Added Function DrawDataLabels to put data labels in world coords,
+      added call from DrawBars and modified SetYDataLabelPos to flag
+      whether or not to call DrawDataLabels.
+
+2005-01-20 (migueldb)
+    * Many bugfixes reported and solved by L. J. Bayuk. Thanks!
+      + fixed bug #1096190
+      + FindDataLimits(): fixed bug #1096192
+      + CalcTranslation(): fixed bug #1101317
+      + DrawImageBorder(): fixed bug 1096200
+      + DrawXDataLabel(): fixed bug 1099879
+      + DrawDots(): fixed bug #1096194
+
+===== Released as 5.0rc2 =====
+
+2004-10-24 (migueldb)
+    * array_merge_php4(): added to cope with the bug introduced by
+        the change in array_merge() from PHP4 to PHP5 (I haven't verified this)
+    * Fixed some divisions by zero, thanks to an old bug report.
 
-       * phplot.php:
+2004-09-09 (migueldb)
+    * SetPointSize(): deprecated
+    * SetPointSizes(): added as replacement for SetPointSize().
+      Now able to set point sizes on a per line basis.
+    * SetPointShape(): deprecated.
+    * SetPointShapes(): added as replacement for SetPointShape().
+      Now able to set point shape on a per line basis.
+    * DrawDot(): now needs record number to decide which dot shape and
+      size to draw.
+    * CalcMargins(): dirty fix for x data label placing.
+    * tile_img(): fixed tile placement.
 
-         + array_merge_php4(): added to cope with the bug introduced by
-         the change in array_merge() from PHP4 to PHP5 (I haven't verified
-         this)
-
-         + Fixed some divisions by zero, thanks to an old bug report.
-
-2004-10-24 17:44  migueldb
-
-       * README.txt, doc/quickstart.html, examples/create_chart.php,
-         examples/format_chart.php, examples/inline_image.php:
-
-         + Updated to the latest changes in phplot
-
-2004-10-24 17:40  migueldb
-
-       * doc/index.php:
-
-         + Minimal change
-
-2004-09-09 20:27  migueldb
-
-       * phplot.php:
-
-         + SetPointSize(): deprecated
-
-         + SetPointSizes(): added as replacement for SetPointSize().Now
-         able to set point sizes on a per line basis.
-
-         + SetPointShape(): deprecated.
-
-         + SetPointShapes(): added as replacement for SetPointShape(). Now
-         able to set point shape on a per line basis.
-
-         + DrawDot(): now needs record number to decide which dot shape
-         and size to draw.
-
-         + CalcMargins(): dirty fix for x data label placing.
-
-         + tile_img(): fixed tile placement.
-
-2004-06-14 14:19  migueldb
-
-       * phplot.php:
-
-         + SetXTickLabelPos() and others: more on the bug reported by Jo
-         Demol.
-
-2004-06-14 11:35  migueldb
-
-       * phplot.php:
-
-         + Fixed bug reported by Jo Demol.
-
-2004-05-11 14:14  migueldb
-
-       * phplot.php:
-
-         + SetBgImage(): added.
-
-         + SetPlotAreaBgImage(): added.
-
-         + SetInputFile(): deprecated.
-
-         + DrawBackground(): now accepts images as backgrounds.
-
-         + DrawPlotAreaBackground(): now accepts images as backgrounds.
-
-         + tile_img(): internal method added.
-
-2004-04-14 13:26  migueldb
-
-       * phplot.php:
-
-         + DrawXAxis(): No more horizontal tick nor label at X-axis'
-         sides.
-
-2004-03-21 18:01  migueldb
-
-       * phplot.php:
-
-         + x/y_label_type automaticaally set to 'data' when setting label
-         precision.
-
-         + minor corrections.
-
-2004-03-03 08:17  afan
-
-       * phplot.php: PlotAreaWorld - last fix
-
-2004-03-03 02:40  migueldb
-
-       * phplot.php:
-
-         + SetPlotAreaWorld(): fixed.
-
-2004-03-01 21:14  afan
-
-       * phplot.php: Needed one more = in  ($ymin === NULL)
-
-2004-02-29 11:21  afan
-
-       * phplot.php: phplot.php SetPlotAreaWorld - changed ($!ymin) to
-         ($ymin == NULL) for cases where $ymin = 0. (also done for ymax,
-         xmin, xmax) afan
-
-2004-02-23 10:34  migueldb
-
-       * phplot.php:
-
-         + SetPlotAreaWorld(): Fixed the calculation of max and min y.
-
-2004-02-14 12:29  migueldb
-
-       * phplot.php:
-
-         + SetPlotAreaWorld(): Fine grained control over which values are
-         auto-calculated. Should fix some trouble with y-scaling.
-
-         + DrawGraph(): yet another fix to drawing order...
-
-         + CheckOption(): fixes problems with mixedcase arguments.
-
-         + SetFileFormat(), FormatLabel(): typos fixed.
-
-2004-01-30 12:56  migueldb
-
-       * examples/data_sample1.php:
-
-         + Added "stacked bars" plot type.
-
-2004-01-30 12:50  migueldb
-
-       * phplot.php:
-
-         + DrawStackedBars(): added plot type.
-
-         + SetEqualXCoord(): renamed to more intuitive CalcBarWidths().
-         Modified for stacked bars.
-
-         + Changed graphing order. The grids are again at the background,
-         as they should. Added var $grid_at_foreground (bool) to alter
-         this.
-
-         + text-data-pie renamed to text-data-single.
-
-         + SetPlotAreaWorld(): fixed min_y bug.
-
-2004-01-29 17:10  migueldb
-
-       * phplot.php:
-
-         + SetPlotAreaWorld(): default behaviour is to adjust Y axis to Y
-         min/max.
-
-         + SetSkipLeftTick(), SetSkipRightTick(): added.
-
-         + SetFileFormat(): fixed silly bug that prevented selections from
-         being made.
-
-2004-01-28 20:00  migueldb
-
-       * phplot.php, doc/quickstart.html:
-
-         + Merged final changes to rel-5-0 into main trunk.
-
-2004-01-28 19:12  migueldb
-
-       * phplot.php:
-
-         + DrawGraph(): Fixed graph drawing order for default plots
-         (bars). Removed some redundat lines of code.
-
-2004-01-28 18:58  migueldb
-
-       * doc/quickstart.html:
-
-         + More stuff...
-
-2004-01-28 18:49  migueldb
-
-       * phplot.php:
-
-         + DrawPieChart(): fixed use of old variable.
-
-         + First steps for data labels autoadjustement and skipping.
-
-2004-01-27 12:12  migueldb
-
-       * phplot.php:
-
-         + Added missing SetSkipTopTick()
-
-         + Some comments.
-
-         + Fixed option checking for a couple of functions. Added some
-         more.
-
-2004-01-27 00:03  migueldb
-
-       * doc/index.php: [no log message]
-
-2004-01-25 20:28  migueldb
-
-       * doc/index.php:
-
-         + Fixed typo.
-
-2004-01-25 19:57  migueldb
-
-       * doc/index.php:
-
-         + Commit for 5.0rc1.
-
-2004-01-25 19:50  migueldb
-
-       * README.txt, doc/index.php, doc/quickstart.html, doc/schema.html:
-
-         + Commit for 5.0rc1.
-
-2004-01-25 19:44  migueldb
-
-       * phplot.php:
-
-         + Added missing SetDrawXDataLabelLines() and
-         SetDrawYDataLabelLines().
-
-         + Added some parameter checking with CheckOption().
-
-         + Added some comments.
-
-2004-01-25 19:32  migueldb
-
-       * examples/: create_chart.php, example3.php, format_chart.php:
-
-         + Commiting for 5.0rc1
-
-2004-01-25 18:21  migueldb
-
-       * examples/example8.php: [no log message]
-
-2004-01-25 18:20  migueldb
-
-       * doc/style.css:
-
-         + Added 'box' style.
-
-2004-01-25 17:11  migueldb
-
-       * phplot.php:
-
-         + Set*Colors(): should be faster when using default values.
-
-         + array_pad_array() non class-member function added.
-
-         + PadArrays(): now pads arrays with themselves, to mimic previous
-         behaviour, uses array_pad_array()
-
-         + DrawAxisLegend(): skeleton added. To do.
-
-2004-01-24 23:18  migueldb
-
-       * phplot_data.php:
-
-         + DoMovingAverage(): some corrections. The legend isn't correctly
-         updated yet.
-
-2004-01-24 23:16  migueldb
-
-       * phplot.php:
-
-         + PadArrays(): added. Formerly in SetDataValues(), now called
-         from DrawGraph(). Fixes SetLineWidths() buggy behaviour.
-
-         + Added MINY and MAXY constants, for DrawXDataLine().
-
-         + DrawGraph(): changed order when drawing axis, to avoid
-         overwriting.
-
-2004-01-21 20:08  migueldb
-
-       * doc/quickstart.html:
-
-         + More corrections
-
-         + Some links
-
-         + $Id$ tag
-
-2004-01-21 19:55  migueldb
-
-       * doc/quickstart.html:
-
-         + HTML heavily cleaned
-
-         + Some corrections
-
-2004-01-21 18:47  migueldb
-
-       * doc/index.php:
-
-         + Added quickstart.
-
-         + More things in whishlist.
-
-2004-01-21 18:46  migueldb
-
-       * doc/quickstart.html:
-
-         + Doc by Afan. With slight corrections.
-
-2004-01-21 18:44  migueldb
-
-       * examples/rgb.inc.php, doc/index.html: [no log message]
-
-2004-01-21 18:43  migueldb
-
-       * phplot.php:
-
-         + Small changes to functions' documentation.
-
-2004-01-21 18:06  migueldb
-
-       * phplot_data.php:
-
-         + renamed tedious data_values to data
-
-         + work still in progress
-
-2004-01-21 18:03  migueldb
-
-       * phplot.php:
-
-         + Added check for __FUNCTION__ for old PHP versions.
-
-         + DrawPieChart(): 'data-data' support added.
-
-         + DrawGraph(): added check to avoid calling FindDataLimits()
-         twice.
-
-         + DrawXDataLine(): Added.
-
-         + DrawXDataLabels(): Added support for vertical lines to data
-         points via DrawXDataLine()
-
-         + FindDataLimits(): Added calculation of per-row min_y and max_y,
-         for DrawDataLine()
-
-2003-12-30 13:26  migueldb
-
-       * phplot.php:
-
-         + SetDataValues(). Now reads a reference and copies it to
-         $this->data, a numeric array. All indices are converted to
-         numeric. num_recs[] holds the number of records per data row.
-
-         + FindDataLimits(). Works with new $this->data
-
-         + The following functions now work with the new data set and use
-         faster loops:
-
-         + DrawDots()
-
-         + DrawDotsError()
-
-         + DrawThinBarLines()
-
-         + DrawPieChart(). Minimum changes.
-
-         + DrawLines()
-
-         + DrawArea(). Other optimizations too.
-
-         + DrawBars(). Other things too.
-
-2003-12-30 00:01  migueldb
-
-       * phplot_data.php:
-
-         + Fixed constructor. Added parameters to fit PHPlot()'s
-
-         + Fixed many "undefined index" errors.
-
-         + DoMovingAverage(): puts data in a new row, and sets colors and
-         legend. Won't work for the moment.
-
-2003-12-29 21:58  migueldb
-
-       * phplot_data.php:
-
-         + Formatted after phplot.php style (PEAR)
-
-         + Added Doxygen comments.
-
-         + DoScaleData(): Optimized with for loops. Needs testing.
-
-         + DoMovingAverage(): Optimized. Changed behaviour with first
-         elements in dataset. Needs testing.
-
-         + DoExponentialMovingAverage(): added. Needs testing.
-
-2003-12-27 14:53  migueldb
-
-       * benjamingothic.ttf:
-
-         + The TTFont is only needed in 'examples/'.
-
-2003-12-27 14:52  migueldb
-
-       * phplot.php:
-
-         + Removed some (done) TODO marks.
-
-2003-12-27 14:06  migueldb
-
-       * examples/test_setup.php: [no log message]
-
-2003-12-27 14:06  migueldb
-
-       * examples/nav.html:
-
-         + This was unnecessary.
-
-2003-12-27 14:01  migueldb
-
-       * README.txt, LICENSE.GPL, LICENSE.PHP_3_0: [no log message]
-
-2003-12-27 13:46  migueldb
-
-       * doc/index.php:
-
-         + Added all the examples.
-
-         + Added 5.0 version number.
-
-         + Added myself as author.
-
-2003-12-27 13:44  migueldb
-
-       * examples/example9.php:
-
-         + Changed to follow phplot function renaming (again!)
-
-2003-12-24 13:39  migueldb
-
-       * examples/: create_chart.php, format_chart.php:
-
-         + New data type: randfunction, using data-data-error.
-
-2003-12-24 13:38  migueldb
-
-       * examples/: data_sample1.php, data_sample2.php, data_sample3.php,
-         data_sample4.php:
-
-         + Moved data type selection links to format_chart.php
-
-2003-12-24 13:36  migueldb
-
-       * phplot.php:
-
-         + Fixed spurious ticks bug.
-
-         + Corrected some comments.
-
-2003-12-24 12:52  migueldb
-
-       * examples/example5.php:
-
-         + Now in format_chart.php
-
-2003-12-24 12:47  migueldb
-
-       * examples/example4.php: Ooops! I deleted the wrong file!
-
-2003-12-24 12:39  migueldb
-
-       * examples/data_sample5.php:
-
-         + New 'randfunction' data type in example-o-matic. Taken from
-         deleted example4.php and test1.php.
-
-2003-12-24 12:37  migueldb
-
-       * examples/example4.php:
-
-         + Inserted example in example-o-matic as 'randfunction'
-
-2003-12-24 12:35  migueldb
-
-       * examples/test1.php: inserted example in example-o-matic as
-         'randfunction'
-
-2003-12-24 10:38  migueldb
-
-       * phplot.php:
-
-         + Fixed silly typo.
-
-2003-12-23 17:40  migueldb
-
-       * phplot.php:
-
-         + All renaming with leading underscores undone for compatibility.
-         It was a mess.
-
-         + Fixed a couple function call typos.
-
-         + Fixed axis position calculation.
-
-2003-12-17 16:28  migueldb
-
-       * examples/create_chart.php:
-
-         + Fixed to work with phplot.php v1.69
-
-2003-12-17 16:28  migueldb
-
-       * phplot.php:
-
-         + Added skip_left_tick and skip_right tick for x axis.
-
-         + Added y_label_angle (last commit)
-
-         + Fixed automatic axis positioning for plots with negative values
-         or log scales.
-
-         + Deleted messy SetGridParams(), SetTickParams() and
-         SetDataLabelParams(). I realized they were a bad idea of mine...
-         :(
-
-         + Some more code grouping and reorganisation.
-
-2003-12-15 16:52  migueldb
-
-       * examples/: create_chart.php, format_chart.php:
-
-         + Added x/y tick crossing lenghts.
-
-         + Added x axis and y axis positioning.
-
-         + Corrected some label placement options.
-
-2003-12-15 16:50  migueldb
-
-       * examples/: example4.php, example5.php, example9.php, test1.php:
-
-         + Updated examples to work with new PHPlot.
-
-         + Fixed some things.
-
-2003-12-15 16:48  migueldb
-
-       * phplot.php:
-
-         + x_tick_pos: added support for 'xaxis' position.
-
-         + x_tick_label_pos: added support for 'xaxis' position.
-
-         + DrawYTick(): fixed 'yaxis'
-
-         + Set[X|Y]TickCrossing(): added. Draw ticks crossing axis by a
-         specified length in pixels.
-
-         + DrawText(): improved vertical centering for TTF.
-
-         + SetDrawBrokenLines(): fixed silly thing.
-
-         + _DrawPlotBorder(): new option 'right' and 'sides'
-
-         + DrawLinesError(): removed some 'ifs' for speed.
-
-         + DrawDotsError(): removed some 'ifs' for speed.
-
-         + Minor corrections here and there.
-
-2003-12-13 04:58  migueldb
-
-       * phplot.php:
-
-         + DrawGraph(): Fixed drawing order error.
-
-2003-12-13 04:44  migueldb
-
-       * phplot.php:
-
-         + _SetIndexColors(): removed. When sessions were not set, indexes
-         were being calculated twice
-
-         + Centralized color and style defaults in _SetDefaultStyles()
-         (former _SetDefaultColors())
-
-         + SetLineWidth(): superseded by new SetLineWidths(). Now using an
-         array of values for per-line setting.
-
-         + DrawDashedLine(): added again for backward compatibility.
-
-         + DrawBars(): Faster shading.
-
-         + DrawYTicks(): slightly improved, DrawYTick() modified too.
-
-         + DrawGraph(): plot borders now drawn after plots.
-
-         + Added color and style variable declarations for easier
-         modification.
-
-         + More formatting. Some renaming undone for backwards
-         compatibility.
-
-2003-12-10 04:04  migueldb
-
-       * examples/: data_sample1.php, data_sample2.php:
-
-         + New plot type 'squared' added.
-
-2003-12-10 04:03  migueldb
-
-       * examples/: create_chart.php, format_chart.php:
-
-         + New option 'Draw broken lines' added.
-
-2003-12-10 04:00  migueldb
-
-       * phplot.php:
-
-         + Better available graph room usage.
-
-         + More renaming and formatting...
-
-         + Parameter validation with _CheckOption(), but I might drop it
-         if it slows everything down.
-
-         + DrawBinary() is again DrawSquared()... ooops. :) It is at least
-         now working.
-
-         + DrawSquared() done, quite silly thing, though.
-
-         + SetBrokenLines() added. Tells whether to draw lines for missing
-         Y data.
-
-2003-12-10 01:32  migueldb
-
-       * doc/schema.html:
-
-         + Simple [go to index] link.
-
-         + Typos fixed.
-
-2003-12-10 00:56  migueldb
-
-       * phplot.php:
-
-         + Reworking PEAR coding standards conformance, I had quite
-         screwed it up. Vim regexes are proving veeeery useful ;)
-
-         + Important comment about the destructor and class instantiation.
-
-         + Internal methods will now have a prepended underscore. I'm
-         renaming them little by little.
-
-         + SetDefaultDashedStyle(): now accepts any string as style.
-
-         + DrawSquared() renamed to DrawBinary()
-
-         + _CalcMargins(): corrected [x|y]_tick_label_width calculation.
-
-2003-12-07 18:58  migueldb
-
-       * examples/: example6.php, example7.php:
-
-         + SetDrawXDataLabels(false);
-
-2003-12-07 18:30  migueldb
-
-       * phplot.php:
-
-         + DrawDotsError(): doesn't fail anymore if data type isn't
-         data-data-error (might include text-data-error in the future).
-
-         + More variables changed to bool.
-
-         + SetNewPlotAreaPixels(): removed, SetPlotAreaPixels() now does
-         the same work.
-
-         + SetMarginsPixels(): now updates margin variables.
-
-         + SetTitle(): fixed wrong height calc for empty title.
-
-         + Removed many internal variable declarations. See the beggining
-         of the class declaration for comments on this. This might be a
-         bit stupid... :-?
-
-         + DrawSquared(): plot type on the works, addressing Feature
-         Request [558302]. Just the skeleton for now.
-
-2003-12-07 02:12  migueldb
-
-       * phplot.php:
-
-         + SetInputFile(): deallocates previously allocated 'img'
-
-         + Added 'line_spacing' and SetLineSpacing()
-
-         + InitImage(): deleted, moved into the constructor
-
-         + DrawError(): shows plain text message if 'img' unavailable.
-
-         + SetXTitle(): Fixed x_title_height calculation for TTF.
-
-         + More code reorganisation. As I seem to be alone in this, that
-         should be no problem...
-
-2003-12-07 01:12  migueldb
-
-       * phplot.php:
-
-         + TTF font provided with package now works (it wasn't being
-         found).
-
-         + TTF placement a bit adjusted.
-
-         + SetTitle(): Fixed wrong title height calculation for TTFonts.
-
-         + DrawLegend(): TTF support added.
-
-         + DrawDot(): renamed dot type 'crosshair' to 'plus'. Added
-         'cross' and 'trianglemid'.
-
-         + FormatTickLabel(): now used for tick and data labels. Renamed
-         to FormatLabel().
-
-         + draw_x_data_label: variable removed, now using
-         [x/y]_data_label_pos for both x and y data labels.
-
-         + draw_x_data_label_lines: parameter added.
-
-         + line_width: minor usage fixes.
-
-         + SetXDataLabelAngle() renamed to SetXLabelAngle(). Old function
-         kept in 'deprecated' section.
-
-         + Changed some options to bool format.
-
-         + Tried to make better decisions on Set*LabelParams() regarding
-         placement.
-
-         + Removed superfluous DrawLabels() function.
-
-         + Moved CalcXHeights() and CalcYWidths() into CalcMargins() for
-         speed (unperceptible I must admit) and clarity.
-
-         + Other changes here and there...
-
-2003-12-07 01:06  migueldb
-
-       * examples/data_sample2.php:
-
-         + Added missing data.
-
-2003-12-07 01:05  migueldb
-
-       * examples/: create_chart.php, data_sample1.php: [no log message]
-
-2003-12-07 01:04  migueldb
-
-       * examples/format_chart.php:
-
-         + Added a check to see if we are being called from the right
-         place.
-
-         + New data label angle option.
-
-         + New line and error bar line width options.
-
-         + New data label options.
-
-         + New point types.
-
-         + Some corrections.
-
-2003-12-06 21:12  migueldb
-
-       * examples/nav.html: [no log message]
-
-2003-12-06 21:09  migueldb
-
-       * examples/test_setup.php:
-
-         + Added nav. bar
-
-2003-12-06 21:08  migueldb
-
-       * examples/nav.html:
-
-         + Simple navigation bar, not very useful yet.
-
-2003-12-06 20:43  migueldb
-
-       * doc/style.css:
-
-         + New "nav" class.
-
-2003-12-06 20:42  migueldb
-
-       * doc/index.php:
-
-         + New index, now calls some php.
-
-2003-12-06 20:41  migueldb
-
-       * doc/php_test.php:
-
-         + Added the test for php functionality in index.php, so this file
-         is no longer necessary.
-
-2003-12-05 19:13  migueldb
-
-       * examples/inline_image.php:
-
-         + Added warning message when called on its own.
-
-2003-11-28 01:21  migueldb
-
-       * phplot.php:
-
-         + Default title position='none' for better positioning of
-         elements.
-
-         + xtr(), yrt(): return value round()ed
-
-         + DrawLines(): better (?) management of incomplete data sets. No
-         begin or end points forced. No more "undefined offset x in ..."
-
-         + DrawArea(): more modifications when working with incomplete
-         data sets. No extra beginning or ending points.
-
-2003-11-26 17:52  migueldb
-
-       * phplot.php: [no log message]
-
-2003-11-26 17:16  migueldb
-
-       * examples/: create_chart.php, format_chart.php:
-
-         + Added shading option
-
-2003-11-26 16:43  migueldb
-
-       * phplot.php:
-
-         + Added spaces after every comma, around every equal sign for
-         prettier code. :)
-
-         + DrawXTitle(): Another fix. Removed the check for tick_pos I
-         just introduced. Works better now.
-
-         + _PHPlot(): destructor added.
-
-         + Comments, indenting, and a couple of minor corrections here and
-         there.
-
-2003-11-26 12:19  migueldb
-
-       * phplot.php: l
-
-         + DrawXTitle(): check for tick_label_pos when calculating title
-         position (before the title would be misplaced under certain
-         circumstances)
-
-         + Added SetIndexDarkColor() and $ndx_data_dark_color[] calculated
-         from $data_colors[],  for shadows.
-
-         + New data type (one value per data row) 'text-data-once' for pie
-         charts
-
-         + DrawPieChart(): some optimisations. Shading added.
-
-2003-11-25 17:30  migueldb
-
-       * examples/create_chart.php:
-
-         + Now accepts point size from format_chart.php.
-
-2003-11-25 17:29  migueldb
-
-       * examples/format_chart.php:
-
-         + New point type 'crosshair' added to the options.
-
-         + Link to the documentation index at the bottom of the page.
-
-2003-11-25 17:28  migueldb
-
-       * examples/data_sample1.php:
-
-         + Fixed wrong comment.
-
-2003-11-25 17:28  migueldb
-
-       * phplot.php:
-
-         + DrawDot(): added new variables to avoid calls to ytr() and
-         xtr(), replaced imagefilledrectangle() with imageline() in
-         "halfline" and "line" modes.
-
-         + DrawDot(): added point type 'crosshair'
-
-         + DrawArea(): modified to follow the structure of the other plot
-         drawing methods. Removed obsolete DrawAreaSeries()
-
-         + DrawLines(): same modifications as with other plot drawing
-         methods.
-
-         + Removed dummy DrawLineSeries()
-
-         + More use of imagesetthickness()
-
-2003-11-25 01:34  migueldb
-
-       * phplot.php: Right now working on richer data label placement, but
-         in the meantime:
-
-         + Removed unnecessary (and wrong) right alignment option in
-         FormatTickLabel()
-
-         + Removed unused (thought it better :) DrawPlotLabel
-
-         + DrawYErrorBar(): uses imagesetthickness()
-
-         + DrawDots() optimised and cleaned.
-
-         + DrawThinBarLines() optimised and cleaned.
-
-         + DrawDotsError() optimised (I hope!)
-
-         + DrawLinesError() optimised. Fixed a bug with multiple lines
-
-         + Other small things, as usual...
-
-2003-11-25 01:27  migueldb
-
-       * examples/data_sample2.php:
-
-         + Removed bogus <td> tag.
-
-2003-11-25 00:49  migueldb
-
-       * examples/data_sample4.php:
-
-         + Added 'thinbarline' data plot option.
-
-2003-11-25 00:48  migueldb
-
-       * examples/data_sample1.php:
-
-         + Added thinbarline plot mode.
-
-         + Added comment on 'text-data' data type.
-
-2003-11-24 23:32  migueldb
-
-       * examples/: data_sample1.php, data_sample2.php, data_sample4.php,
-         format_chart.php:
-
-         + Replaced old data type "linear" with new "data" everywhere.
-
-2003-11-24 23:32  migueldb
-
-       * examples/create_chart.php:
-
-         + Accepts new $data_row format from data_sample3.php
-         (data-data-error format)
-
-         + Replaced old data type "linear" with new "data".
-
-2003-11-24 23:30  migueldb
-
-       * examples/data_sample3.php:
-
-         + Replaced old "linear" with new "data" everywhere.
-
-         + Modified code and $data_row organization to allow for easier
-         changes.
-
-         + Added another set of values (to have two lines in the example)
-
-2003-11-24 21:06  migueldb
-
-       * examples/data_sample3.php:
-
-         + Removed bogus <td> tag
-
-         + Added slashes to input tags end
-
-2003-11-24 17:44  migueldb
-
-       * phplot.php:
-
-         + Fixed a typo.
-
-2003-11-24 17:03  migueldb
-
-       * doc/: examples.html, function_reference.html, quick_start.php:
-
-         + Inserted into index.html (some time ago)
-
-2003-11-24 17:00  migueldb
-
-       * doc/style.css:
-
-         + Added new classes for the docs.
-
-2003-11-24 16:37  migueldb
-
-       * phplot.php:
-
-         + Added SetDefaultDashedStyle()
-
-         + Replaced outdated call to imagesetstyle() with SetDashedStyle()
-
-         + Moved the check for dashed_grid from constructor (where it
-         could lead to strange behaviour) into DrawXTicks() and
-         DrawYticks() (BTW, shouldn't these be renamed?)
-
-2003-11-24 16:12  migueldb
-
-       * examples/: data_sample1.php, data_sample2.php, data_sample3.php,
-         data_sample4.php:
-
-         + HTML tags to lowercase.
-
-         + Added missing closing tags.
-
-         + Indented code.
-
-2003-11-24 16:05  migueldb
-
-       * examples/create_chart.php:
-
-         + Modified to work with the new format_chart.php
-
-2003-11-24 16:04  migueldb
-
-       * examples/format_chart.php:
-
-         + New layout
-
-         + Many new options
-
-         + Some introductory words
-
-         + Removed "you have to reload" warning (the default behaviour for
-         PHPlot now is to send no-cache header)
-
-         + Small corrections
-
-2003-11-24 14:46  migueldb
-
-       * phplot.php:
-
-         + Added checks for tick placement in CalcMargins()
-
-         + Uninportant renaming of some internal variables.
-
-2003-11-24 14:25  migueldb
-
-       * phplot.php:
-
-         + Some more renaming for consistency: vtick, vert_tick, etc are
-         now y_tick_
-
-         + Same for htick -> x_tick_
-
-         + + Draw*Ticks() renamed following the same scheme
-
-         + Grouped tick label parameter setting functions in
-         SetTickLabelParams(). Left original ones in 'deprecated'
-
-         + Grouped grid parameter setting functions in SetGridParams().
-         Left original ones in 'deprecated'
-
-         + Grouped Title setting and positioning in Set*Title(). Left
-         original ones in 'deprecated'
-
-         + Slight modifications to data label placement
-
-2003-11-24 11:30  migueldb
-
-       * doc/index.html:
-
-         + More reorganisation
-
-2003-11-23 23:53  migueldb
-
-       * phplot.php:
-
-         + Corrected a typo.
-
-2003-11-23 23:39  migueldb
-
-       * phplot.php:
-
-         + Restructured internal font management: font variables are now
-         hashes with all associated info. DrawText() now only needs that
-         and chooses whether to draw TrueType or not. Almost every "if
-         ($this->use_ttf)" check removed as a result.
-
-         + Added SetDefaultTTFont()
-
-         + When drawing pie charts (no axis titles nor labels), maximize
-         plot area.
-
-2003-11-23 21:48  migueldb
-
-       * doc/index.html:
-
-         + Included function_reference.html links here.
-
-2003-11-23 21:42  migueldb
-
-       * doc/: index.html, schema.html: [no log message]
-
-2003-11-23 21:39  migueldb
-
-       * doc/index.html:
-
-         + New welcome page, some text.
-
-         + Some features listed.
-
-         + Uses stylesheet
-
-2003-11-23 21:37  migueldb
-
-       * doc/: style.css, php_test.php:
-
-         + First commit
-
-2003-11-23 21:36  migueldb
-
-       * examples/test_setup.php:
-
-         + Added stylesheet
-
-         + Silly change
-
-2003-11-23 21:35  migueldb
-
-       * examples/inline_image.php:
-
-         + Silly change
-
-2003-11-23 21:34  migueldb
-
-       * examples/create_chart.php:
-
-         + Renamed SetYLabel() and SetXLabel() to SetYTitle() and
-         SetXTitle()
-
-2003-11-23 21:33  migueldb
-
-       * examples/format_chart.php:
-
-         + Added file format option
-
-         + Added stylesheet
-
-2003-11-23 20:00  migueldb
-
-       * phplot.php:
-
-         + DrawText(): horizontal (left, center and right) and vertical
-         (top, center, bottom) alignment finished.
-
-         + Replaced all outdated ocurrences of ImageString() with calls to
-         DrawText().
-
-         + Removed redundant text placement calculations.
-
-         + DrawLegend() now draws the legend box more accurately (final
-         solution to bug #527867)
-
-2003-11-23 02:02  migueldb
-
-       * phplot.php:
-
-         + tick_length and tick_length2 now are htick_length and
-         vtick_length.
-
-         + Included SetHTickLength() and SetVTickLength()
-
-         + Fixed silly bug with SetDefaultFonts()
-
-         + SetUseTTF() now resets the fonts.
-
-         + More room for title
-
-2003-11-22 23:40  migueldb
-
-       * phplot.php:
-
-         + Somewhat clearer naming convention for labels, ticks, titles
-         and fonts.
-
-         + X tick labels and X Title can be: plotdow, plotup, both, none
-
-         + Y tick labels and Y Title can be: plotleft, plotright, both,
-         none
-
-         + Renamed some Set*() internal functions to Calc*() (Set
-
-         + is to be left for "public" methods)
-
-         + More doxygen comments
-
-         + Optimised FindDataLimits()
-
-         + A few bugfixes
-
-2003-11-22 18:04  migueldb
-
-       * examples/test_setup.php:
-
-         + Added wbmp format
-
-         + Now using imagetypes() instead of function_exists()
-
-2003-11-22 13:57  migueldb
-
-       * examples/test_setup.php:
-
-         + Graphic formats availability checks now use function_exists()
-
-         + HTML code restructured.
-
-2003-11-22 13:55  migueldb
-
-       * examples/create_chart.php:
-
-         + Using $_GET and $_POST
-
-         + SetImageFormat() option added
-
-2003-11-22 13:54  migueldb
-
-       * examples/format_chart.php:
-
-         + HTML code rewritten, better layout
-
-         + Image format option
-
-2003-11-22 03:02  migueldb
-
-       * phplot.php:
-
-         + Improved datalabel_font
-
-         + + Added SetDatalabelFontSize()
-
-         + Added many more doxygen comments.
-
-         + Other micro-changes.
-
-2003-11-22 02:06  migueldb
-
-       * examples/inline_image.php:
-
-         + register_globals default (PHP4) setting taken into account
-
-2003-11-22 01:46  migueldb
-
-       * phplot.php:
-
-         + Bug #790745 fixed. Thx. to the poster.
-
-         + DrawError() now accepts position and centers text
-
-         + '[423202] Catch error on empty arrays' solved. No more 'divide
-         by zero's
-
-         + Some (random) doxygen comments inserted. It might be a good
-         idea for auto-docs.
-
-2003-11-21 22:27  migueldb
-
-       * phplot.php: My first commit, here are the changes (that I
-         remember)
-
-         + Y axis tick and label options now work
-
-         + X axis tick and label options: plotdown,plotup,both,none
-
-         + Implemented tick_length2 for rightmost/upper side
-
-         + More default data colors assigned to data_color and error_color
-         arrays
-
-         + Deprecated draw_vert_ticks and SetDrawVertTicks() in favor of
-         'none' value in vert_tick_position
-
-         + Better? upper,lower, rightmost margin calculation
-
-         + Dashed grid lines
-
-         + Added FormatTickLabel(). Removed redundant code in
-         DrawHorizontalTicks()
-
-         + Removed all calls left to DrawDataLabel(), moved to
-         "deprecated"...
-
-         + Moved some code and functions around.
-
-         + Attempted correction for Bug [440065] "Pie labels not centered
-         correctly"
-
-         + Corrected '$which_pt not an acceptable plot type' typo [682068]
-
-         + Now using SetFileFormat() for PHP4
-
-         + Bug [484235] might be solved by now (not sure!)
-
-2003-11-21 20:42  migueldb
-
-       * examples/create_chart.php: Now works with register_globals off
-
-2002-06-21 01:35  afan
-
-       * examples/test_setup.php: Testing update: (Moved GIF to last
-         check)
-
-2002-04-30 15:25  afan
-
-       * phplot.php: Fixed error in pie graphs with data=0.  Changed
-         functions and tabs to PEAR standards
-
-         Afan
-
-2002-02-22 03:15  afan
-
-       * phplot.php: No changes - just settting up new server
-
-2001-04-19 03:03  afan
-
-       * phplot.php, examples/inline_image.php: fix error on function
-
-2001-04-19 00:18  mdj_guardian
-
-       * phplot.php: Fixed problem with example 8.  Also modified the
-         default y_padding value so the examples with zero on the bottom
-         turn out that way.
-
-2001-04-18 07:18  mdj_guardian
-
-       * phplot.php: Made the following changes:
-
-         + Fixed the case of built in functions to match PHP documentation
-
-         + Modified SetFileFormat to work in PHP3 & 4 with no changes
-
-         + Improved auto-scaling signifcantly  (SetVertPadding, y_padding,
-         SetPlotAreaWorld)   (needs docs and examples)
-
-         + Fixed a problem in DrawDashedLine when the line segments were
-         zero length
-
-         + Modified DrawLegend to fix positioning of lower right corner of
-         box.  Was cutting through text
-
-         + Removed a duplicated line in FindDataLimits
-
-2001-04-08 15:58  afan
-
-       * phplot.php: Minor change to formatting
-
-2001-03-27 07:42  afan
-
-       * phplot.php, phplot_data.php, examples/data.php,
-         examples/data_sample1.php, examples/data_sample3.php,
-         examples/example1.php, examples/example2.php,
-         examples/example3.php, examples/example4.php,
-         examples/example6.php, examples/example7.php,
-         examples/example8.php, examples/example9.php,
-         examples/format_chart.php, examples/inline_image.php,
-         examples/test_setup.php: Committing 4.4.6
-
-2001-03-19 16:08  afan
-
-       * phplot.php, doc/user_functions.html: Cleaned up Shading Function
-         for Bars Bug Fix in Placement of Datalabels.
-
-2001-03-17 16:40  afan
-
-       * examples/: data.php, data_date.php, data_date2.php,
-         data_sample1.php, data_sample2.php, data_sample3.php,
-         example1.php, example2.php, example3.php, example6.php,
-         example7.php, example8.php, example9.php, format_chart.php,
-         inline_image.php, test_setup.php: changed examples from ? to ?php
-         format.
-
-2001-03-16 13:59  afan
-
-       * phplot.php: Error in line 361 corrected.
-
-2001-03-16 03:10  afan
-
-       * phplot.php: Major updates to DrawDots and DrawBars to make it
-         easy to put labels and dots overwriting bars. Code optimizations
-         and checks to make sure you don't need to set error_reporting(0).
-
-2001-03-09 19:58  afan
-
-       * phplot.php: Minor bug fixes
-
-2001-03-01 17:47  afan
-
-       * README.txt, phplot.php, rgb_small.inc.php, examples/example8.php:
-         Bug on line 164 corrected Removed rgb_small.inc.php
-
-2001-02-27 17:49  afan
-
-       * phplot.php: Added ability to use background Images with plots
-         Changed Image Color Allocation to work even with background
-         Images Note: this change requires usage of PHPLOT 3.0.2 or later.
-
-2001-02-23 21:22  afan
-
-       * phplot.php, phplot_data.php, examples/example3.php,
-         examples/example6.php, examples/example9.php: DrawText,
-         SetRGBColor, Sessions fixes
-
-2001-02-16 16:40  afan
-
-       * phplot.php, examples/example1.php: Added output_file and
-         input_file Added Constructor Added check to see if an image index
-         has already been added for smaller files
-
-2001-02-14 21:06  afan
-
-       * phplot.php, examples/example1.php: Added Function SetColorIndex
-         for operating on image color indexes directly Also checks to see
-         if the color has already been defined for image size
-         optimization.
-
-2001-02-13 20:50  afan
-
-       * phplot.php, examples/example1.php: Minor Bugfix
-
-2001-02-13 18:16  afan
-
-       * phplot.php, examples/data.php, examples/example2.php,
-         examples/example3.php, examples/example4.php: Title
-         modifications, added movable Y axis
-
-2001-02-12 14:54  afan
-
-       * examples/example8.php: Adding example8: two plots on one image
-
-2001-02-09 18:56  afan
-
-       * phplot.php: Added SetYAxisPosition and changed DrawYAxisCode
-
-2001-02-09 10:14  afan
-
-       * phplot.php: Added Title return lines
-
-2001-02-08 05:35  afan
-
-       * README.txt, phplot.php, doc/user_functions.html,
-         doc/user_internal_functions.html, examples/data.php,
-         examples/example1.php, examples/example4.php: Added Error Bar
-         Widths, Missing Data Handling, Minor bugfix, More docs
-
-2001-01-29 05:51  afan
-
-       * phplot.php, examples/create_chart.php, examples/example7.php:
-         Added NumHorizTicks, fixed bug in example 7
-
-2001-01-23 05:09  afan
-
-       * doc/user_functions.html: Doc Fix
-
-2001-01-23 05:04  afan
-
-       * phplot.php: Minor Changes
-
-2001-01-23 05:03  afan
-
-       * phplot.php, doc/internal_functions.html, doc/user_functions.html:
-         Updated Documentation
-
-2001-01-23 04:36  afan
-
-       * phplot.php, examples/example3.php: Minor bug fixes.
-
-2001-01-23 01:33  afan
-
-       * phplot.php: Minor fix to phplot after major update
-
-2001-01-23 01:06  afan
-
-       * README.txt, phplot.php, phplot_data.php,
-         examples/create_chart.php, examples/data.php,
-         examples/data_date.php, examples/data_date2.php,
-         examples/example2.php, examples/example4.php,
-         examples/example6.php, examples/example7.php, examples/test1.php:
-         Lots changed: easier to make multiple images, logs, etc.
-
-2001-01-17 04:05  afan
-
-       * phplot.php: [no log message]
-
-2001-01-17 03:42  afan
-
-       * phplot.php: Implemented Log Plots. Very little error checking
-
-2001-01-17 03:41  afan
-
-       * phplot.php: Log plots implemented. Very little data error
-         checking.
-
-2001-01-14 03:02  extensive
-
-       * phplot.php, phplot_data.php, doc/internal_functions.html,
-         doc/user_functions.html: Changed ReportError to DrawError
-         respectively PrintError, added new functionality to PHPlot_data
-         and added some documentation of PHPlot_data
-
-2001-01-11 18:43  afan
-
-       * phplot.php, doc/internal_functions.html: Added documentation.
-
-2001-01-11 17:07  afan
-
-       * create_chart.php, data_sample1.php, data_sample2.php,
-         data_sample3.php, data_sample4.php, doc.htm, format_chart.php,
-         phplot.php, doc/examples.html, doc/function_reference.html,
-         doc/index.html, doc/internal_functions.html, doc/quick_start.php,
-         doc/user_functions.html, doc/user_internal_functions.html,
-         examples/benjamingothic.ttf, examples/create_chart.php,
-         examples/data_sample1.php, examples/data_sample2.php,
-         examples/data_sample3.php, examples/data_sample4.php,
-         examples/format_chart.php, examples/inline_image.php,
-         examples/test_setup.php: Major change to structure of PHPLOT. Doc
-         directory, moved examples to examples directory.
-
-2001-01-11 16:25  extensive
-
-       * phplot.php: just added ReportError() and changed all DrawError()
-         calls to use ReportError().  phplot seems to be broken on my
-         system but I don't think it is my fault (this was the first time
-         I was testing a 3.7.x version), let's address that later. Is it
-         broken on your system, too, Afan?
-
-2001-01-11 02:14  extensive
-
-       * phplot.php, phplot_data.php: I just corrected some typos (mostly
-         my name being misspelled ;-)
-
-2001-01-08 00:53  afan
-
-       * doc.htm, phplot.php, phplot_data.php: Added Plot Border types
-
-2001-01-07 00:35  afan
-
-       * doc.htm, phplot_data.php: Adding phplot_data for release 3.7.0
-
-2001-01-07 00:30  afan
-
-       * examples/: data.php, example1.php, example2.php, example3.php,
-         example4.php, example5.php, rgb.inc.php, test1.php: Adding
-         PHPLOT_DATA routines. Defining the first sub-class. Added
-         examples: Afan
-
-2001-01-07 00:23  afan
-
-       * README.txt, create_chart.php, data_sample3.php, doc.htm,
-         phplot.php, stocks.php, stocks1.php, test1.php: 3.6.4 Committed
-
-2000-12-13 22:02  afan
-
-       * phplot.php: Allowing data in time_format but printed using
-         strftime
-
-2000-12-13 04:47  afan
-
-       * doc.htm, phplot.php, stocks.php: ver 3.2.1 and New Docs
-
-2000-12-11 18:03  afan
-
-       * create_chart.php, data_sample2.php, format_chart.php, phplot.php:
-         Release version 3.2.0
-
-2000-11-29 17:12  afan
-
-       * README.txt, benjamingothic.ttf, create_chart.php,
-         data_sample1.php, data_sample2.php, data_sample3.php,
-         data_sample4.php, doc.htm, format_chart.php, phplot.php,
-         rgb.inc.php, rgb_small.inc.php, stocks.php, stocks1.php,
-         test1.php: Initial revision
-
-2000-11-29 17:12  afan
-
-       * README.txt, benjamingothic.ttf, create_chart.php,
-         data_sample1.php, data_sample2.php, data_sample3.php,
-         data_sample4.php, doc.htm, format_chart.php, phplot.php,
-         rgb.inc.php, rgb_small.inc.php, stocks.php, stocks1.php,
-         test1.php: The first upload!  Afan Ottenheimer
+2004-06-14 (migueldb)
+    * SetXTickLabelPos() and others: more on the bug reported by Jo Demol.
+    * Fixed bug reported by Jo Demol.
+
+2004-05-11 (migueldb)
+    * SetBgImage(): added.
+    * SetPlotAreaBgImage(): added.
+    * SetInputFile(): deprecated.
+    * DrawBackground(): now accepts images as backgrounds.
+    * DrawPlotAreaBackground(): now accepts images as backgrounds.
+    * tile_img(): internal method added.
 
+..........
+Editor's Note: For older changes to PHPlot, please see the CVS logs.
diff --git a/gui/bacula-web/external_packages/phplot/LICENSE.GPL b/gui/bacula-web/external_packages/phplot/LICENSE.GPL
deleted file mode 100644 (file)
index 5b6e7c6..0000000
+++ /dev/null
@@ -1,340 +0,0 @@
-                   GNU GENERAL PUBLIC LICENSE
-                      Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                           Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-\f
-                   GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-\f
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-\f
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-\f
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-                           NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-                    END OF TERMS AND CONDITIONS
-\f
-           How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program 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.
-
-    This program 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
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Library General
-Public License instead of this License.
diff --git a/gui/bacula-web/external_packages/phplot/LICENSE.PHP_3_0 b/gui/bacula-web/external_packages/phplot/LICENSE.PHP_3_0
deleted file mode 100644 (file)
index ffc1ab7..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
--------------------------------------------------------------------- 
-                  The PHP License, version 3.0
-Copyright (c) 1999 - 2003 The PHP Group. All rights reserved.
--------------------------------------------------------------------- 
-
-Redistribution and use in source and binary forms, with or without
-modification, is permitted provided that the following conditions
-are met:
-
-  1. Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-  2. Redistributions in binary form must reproduce the above copyright
-     notice, this list of conditions and the following disclaimer in
-     the documentation and/or other materials provided with the
-     distribution.
-  3. The name "PHP" must not be used to endorse or promote products
-     derived from this software without prior written permission. For
-     written permission, please contact group@php.net.
-  
-  4. Products derived from this software may not be called "PHP", nor
-     may "PHP" appear in their name, without prior written permission
-     from group@php.net.  You may indicate that your software works in
-     conjunction with PHP by saying "Foo for PHP" instead of calling
-     it "PHP Foo" or "phpfoo"
-  5. The PHP Group may publish revised and/or new versions of the
-     license from time to time. Each version will be given a
-     distinguishing version number.
-     Once covered code has been published under a particular version
-     of the license, you may always continue to use it under the terms
-     of that version. You may also choose to use such covered code
-     under the terms of any subsequent version of the license
-     published by the PHP Group. No one other than the PHP Group has
-     the right to modify the terms applicable to covered code created
-     under this License.
-
-  6. Redistributions of any form whatsoever must retain the following
-     acknowledgment:
-     "This product includes PHP, freely available from
-     <http://www.php.net/>".
-
-THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND 
-ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
-PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE PHP
-DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
-OF THE POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------- 
-
-This software consists of voluntary contributions made by many
-individuals on behalf of the PHP Group.
-
-The PHP Group can be contacted via Email at group@php.net.
-
-For more information on the PHP Group and the PHP project, 
-please see <http://www.php.net>.
-
-This product includes the Zend Engine, freely available at
-<http://www.zend.com>.
diff --git a/gui/bacula-web/external_packages/phplot/NEWS.txt b/gui/bacula-web/external_packages/phplot/NEWS.txt
new file mode 100644 (file)
index 0000000..0efb1bc
--- /dev/null
@@ -0,0 +1,975 @@
+This is the NEWS file for PHPlot, with release documentation.
+The project web site is http://sourceforge.net/projects/phplot/
+The project home page is http://phplot.sourceforge.net/
+Refer the the ChangeLog file for detailed source changes.
+-----------------------------------------------------------------------------
+
+2010-10-03 Release 5.2.0
+
+Overview:
+
+This is the current stable release of PHPlot. This release includes some
+bug fixes and new features, described below, and a reimplementation of
+internal processing of colors.
+
+The PHPlot reference manual has been updated to match this release.
+Horizontal plots are now documented in the manual, and the interim
+documentation in the source release (HorizontalBars.txt) has been removed.
+The manual is available in HTML format as a separate download from
+Sourceforge.  The manual is also available for on-line viewing from the
+project home page.  Starting with this release, the manual is also
+available in PDF format from the project home page.
+
+
+Cautions, Important Notes, and Compatibility Issues:
+
+Due to changes in color allocation (see bug #3049726 below), image files
+produced with PHPlot-5.2.0 will differ when compared byte-for-byte with
+those created by earlier releases, even when the images are identical (as
+they nearly always are).
+
+If you are creating a horizontal plot with any negative X values, and not
+setting the Y axis position, your plot will change (see bug #3074402 below)
+because PHPlot no longer leaves the Y axis on the left for horizontal plots.
+
+Some internal methods that used to have 'public' visibility are now protected.
+This will generally prevent you from using them. The list of changed functions
+can be found in the release ChangeLog. Avoid using any internal methods.
+
+If you are making a stackedbar plot with any negative values, PHPlot-5.1.3
+and earlier took the absolute value of each data value (which was not
+documented), but PHPlot-5.2.0 does not. See Feature Request #3073679 below.
+
+If your plot fills the 256 available color map slots in a palette image,
+your image may look different with PHPlot-5.2.0 due to changes in color
+allocation order.  This was seen on two of the tests in the PHPlot test
+suite. These tests tiled a JPEG image into the plot area or image
+background.  Being a truecolor image, the JPEG had a huge number of colors,
+which filled all available slots in the color map. Additional colors for
+plot elements had to be approximated, and different colors resulted with
+PHPlot-5.2.0 versus previous releases. To avoid this problem, either use a
+truecolor PHPlot object (PHPlot_truecolor constructor), or reduce the
+number of colors in the background image and convert it to PNG or GIF.
+
+This release contains significant changes to PHPlot internals. In particular,
+variables and functions related to element colors and color handling have
+changed. Refer to the PHPlot release ChangeLog for more details.  Remember,
+if you rely on accessing any member variable, or on using any non-public
+function or any function not documented in the reference section of the manual,
+your code is at risk of breaking with each new release.
+
+
+New features in 5.2.0:
+
+#3077554 "Finer control over plot element colors" (partial):
+  The X, Y, and main titles can now have different colors. See the PHPlot
+  Reference Manual entries for SetXTitleColor and SetYTitleColor.
+
+#3073679 "Stacked bar plots with negative values":
+  A stacked bar plot can now include negative values, and stacks of negative
+  values will be drawn downwards (or leftwards). See the PHPlot Reference
+  Manual under "Plot Type: stackedbars" for details.
+
+
+Bugs Fixed in 5.2.0:
+
+#3045131 "SetTransparentColor problems":
+  Setting a transparent color now works whether before or after setting
+  the background color (for example), and also now works with a data color.
+
+#3049726 "Optimize color allocation"
+  PHPlot now defers allocating colors until drawing time, and tries to allocate
+  only the colors which are actually needed. For palette images, this results
+  in use of fewer color slots and slightly smaller image files.
+
+#3074402 "Fix Y axis default for horizontal plots":
+  When horizontal plots were introduced, an asymmetry with the X and Y axis
+  position defaults was known but left. This behavior was later determined
+  to be unhelpful. So now the Y axis on horizontal plots will default to X=0,
+  or the X value closest to zero within the plot area range. This is the same
+  behavior as for the X axis in vertical plots.
+
+#3056991 "Internal methods should be 'protected'":
+  More of the internal PHPlot functions were changed to 'protected' visibility,
+  as the test suite was fixed to not call them directly.
+
+#3057000 "Review 'deprecated' methods":
+  One broken deprecated method (SetColor) was removed and one changed.
+  Note that deprecated methods are not documented and not tested.
+
+-----------------------------------------------------------------------------
+
+2010-08-30 Release 5.1.3
+
+Overview:
+
+This is the current stable release of PHPlot. Additional horizontal plot
+types and features have been added, however horizontal plots are still
+considered 'experimental'. A new callback has been added which allows
+greater control over the data colors. An improved method for accessing
+TrueType Font (TTF) files means that on many systems TTF text can be used
+without specifying font paths.
+
+
+Cautions and Important Notes:
+
+Since the previous PHPlot release, PHP-5.3.3 and PHP-5.2.14 have been
+released, and these include a fix for the TrueType Font (TTF) rendering
+problem. Use of these releases is now recommended.
+
+There has been extensive cleanup of the PHPlot code. If you have a
+customized version of PHPlot, you may find it difficult to update.
+
+The "additional data support" script phplot_data.php has been removed from
+this release. The script has not been developed or tested in a long time,
+and was found to have numerous problems. The script can still be found in
+the CVS repository.
+
+The changes in this release (horizontal plots, custom data color callback,
+and TrueType Font handling) should not result in any compatibility issues.
+
+
+New features in 5.1.3:
+
+#3049703 "Additional horizontal plots and features":
+  + Implemented data values labels in horizontal bar charts.
+    Use: SetXDataLabelPos('plotin').
+  + Add horizontal stacked bar charts, with data value labels.
+  + Add horizontal thinbarline plots.
+  Horizontal plots are still considered 'experimental', and documentation is
+  in the HorizontalBars.txt text file rather than the PHPlot Reference Manual.
+
+#3034164 "Extended control of data colors":
+  New callback 'data_color' can be used to customize selection of the color
+  of each bar, line segment, point marker, etc. This is documented in the
+  PHPlot Reference Manual section "Custom Data Color Selection", with new
+  examples in the Examples chapter.
+
+
+Bugs Fixed in 5.1.3:
+
+#3051906 "Better choice for default TT font":
+  Rather than always using the unlikely 'benjamingothic.ttf' as its default
+  TrueType font name, PHPlot now has a short list of sans-serif fonts, and
+  tries to find one that works if a default TT font is needed.  On many
+  systems, this will provide a high-quality default font without help.
+
+#3051832 "Let PHP/GD find the font file":
+  Instead of using file existence to validate a TT font file, PHPlot now
+  just tries to use the font. This allows PHP/GD to use its own rules to
+  try to find the font, without needing a pathname.  This works on Windows
+  and at least some Linux systems.
+
+#3048267 "phplot_data add-on is still broken"
+  Not fixed. phplot_data.php has been removed from the release.
+
+-----------------------------------------------------------------------------
+
+2010-06-29 Release 5.1.2
+
+Overview:
+
+This is the current stable release of PHPlot. Truecolor image support is no
+longer considered 'experimental', and is now documented in the reference
+manual. There is a new experimental feature for horizontal bar charts. This
+release also contains a bug fix and new feature.
+
+
+Cautions and Important Notes:
+
+The advisory against using PHP-5.3.2 or PHP-5.2.13 with PHPlot if you use
+TrueType fonts (TTF) continues. See the item below for PHPlot-5.1.1. The
+good news is that this has been fixed by the PHP Team and will be in the
+next releases.
+
+Compatibility of data type and plot type are now checked completely.  If
+you used an incorrect data type with certain plot types, your script may no
+longer work until you fix the data type. Specifically, the area, squared,
+and thinbarline plot types failed to check the data type they received, and
+treated anything other than 'data-data' as 'text-data'.  If you have a
+squared plot with data type 'data-data-error', for example (which is not
+supposed to work), it did produce a plot, but will now result in an error.
+
+The addition of horizontal bar charts should not impact any existing plot,
+with one small exception. The function SetYDataLabelPos() used to accept
+some additional, undocumented options (plotleft, plotright, both, yaxis)
+and pass these through to SetYTickLabelPos() "for compatibility". It no
+longer does so, as some of those are now used for horizontal bar chart
+labels. To position Y tick labels, use only SetYTickLabelPos().
+
+
+New features in 5.1.2:
+
+#3002606 "Add to plot and image border options":
+  SetPlotBorderType() now accepts 'right', 'top', and 'bottom', as well
+      as an array of options. So you can now control exactly which of the 4
+      border sides will be drawn.
+  SetImageBorderType() now accepts 'solid' as a choice. This will use the
+      actual color set with SetImageBorderColor(), rather than the darker
+      shade as type 'plain' does (which may have been a bug).
+  SetImageBorderWidth() is a new function that sets the width of the image
+      border. The defaults are the same as the fixed values used before: 1
+      pixel for plain, 2 pixels for raised.  The image border width is now
+      accounted for in margin calculations, if it is greater than 2 (to make
+      sure existing plots will not change).
+
+#2885930 "Horizontal Bars":
+  Horizontal bar charts are implemented, as an experimental feature.
+  'Experimental' means they are not yet documented in the reference manual,
+  and subject to change or removal.
+  Refer to the text file HorizontalBars.txt for details.
+
+#2947679 (follow-up) "Support for alpha blending/Truecolor":
+  Truecolor support is now documented in the Reference Manual. The interim
+  documentation file Truecolor.txt has been removed. Alpha channel
+  specification now works with both constructors and both image types.  This
+  fixes an issue if the base constructor was used with a truecolor background
+  image. (In PHPlot-5.1.1, the result would be a truecolor image, but the
+  alpha channel features were not available.)
+
+
+Bug Fixed in 5.1.2:
+
+#3010116 "Bad rendering of title in multi-plot image when using TTF":
+  Make sure the main title is drawn only once, to avoid bad rendering of
+  TTF titles with multiple plots due to anti-aliasing.
+
+-----------------------------------------------------------------------------
+
+2010-04-04 Release 5.1.1
+
+Overview:
+
+This is the current stable release of PHPlot. This release adds truecolor
+image support as an experimental feature, fixes a number of bugs and adds
+a few new features.
+
+The PHPlot reference manual has been updated to match this release.  The
+manual is available as a separate download from Sourceforge. The manual is
+also available for on-line viewing from the project home page.
+
+See the ChangeLog file in the release for more about changes and bug fixes.
+
+
+Cautions and Important Notes:
+
+Avoid using PHP-5.3.2 or PHP-5.2.13 with PHPlot if you use TrueType fonts
+(TTF). Some new bugs were introduced in those releases that adversely
+affects accurate positioning and rendering of TrueType font text.
+
+
+New features in 5.1.1:
+
+#2947679 "Support for alpha blending/Truecolor":
+  PHPlot can now produce truecolor images, with alpha blending of colors and
+  other effects. This is considered an experimental feature, meaning it is
+  not yet documented in the PHPlot Reference Manual, and subject to change.
+  Refer to the text file Truecolor.txt included in the PHPlot release for
+  information on using truecolor.
+  Two drawing changes were made to improve plot appearance with Truecolor:
+  + Filled dots (in points & linepoints plots) are now drawn better. This
+    also makes them look rounder with regular (non-Truecolor) plots.
+  + Area plots have the areas filled without overlapping each area down to
+    the Y axis. This was needed to fix problems with alpha blending, and
+    should have no effect on non-Truecolor plots.
+
+#2973995 "Add y-Data to Stackedbars":
+  You can now have Y Data Labels with 'stackedbars' plots. These label the Y
+  values (incremental and total) for each bar. Refer to the reference manual
+  page for SetYDataLabelPos().
+
+
+Bug Fixes in 5.1.1:
+
+#2976735 "Improvements and fixes for 'area' plots":
+  Moving X axis works; handle Y<0 better; new 'stackedarea' plot type is a
+  variation on 'area' with the data represented differently.
+
+#2974639 "Stacked bars plot breaks with X axis != 0":
+  Moving X axis works.
+
+#2963757 "point_counts undefined error in 5.1.0":
+  Fixed an error introduced in PHPlot-5.1.0 when point size and shape arrays
+  were set to the same size.
+
+#2938219 "Bars go in wrong direction":
+  For bar charts with all Y<0, bars will still be drawn down even if Y=0 is
+  not in range.
+
+-----------------------------------------------------------------------------
+
+2009-12-24 Release 5.1.0
+
+Overview:
+
+This is the current stable release of PHPlot. This release fixes a number of
+bugs and adds some new features. Some of the changes in this release can
+alter the appearance of plots, so be sure to review the information in this
+NEWS file and test this release with your application.
+
+The PHPlot reference manual has been updated to match this release.  The
+manual is available as a separate download from Sourceforge. The manual is
+also available for on-line viewing from the project home page.
+
+See the ChangeLog file in the release for more about changes and bug fixes.
+
+
+New features in 5.1.0:
+
++ A new "contrib" directory has been added for useful add-ons.
+  This currently contains:
+    * prune_labels : Control data label density on X axis.
+    * color_range : Define a gradient map for data colors.
+
++ Feature Request 2899921 "Allow different format for data and tick labels"
+  Text angle and format can now be controlled separately for data labels.
+
++ Locale loading override
+  New variable locale_override stops PHPlot from getting locale from system.
+
++ Translating Coordinates
+  New function GetDeviceXY() to translate world to device coordinates.
+
++ New drawing callback
+  New callback 'draw_all', called after all drawing is done.
+  The manual now contains an example of using this new callback and
+  the new GetDeviceXY() function to annotate a plot.
+
+
+Bug Fixes in 5.1.0:
+
+#2914403 "Pie + X/Y titles: Undefined property error"
+  X/Y titles are now properly ignored for pie charts.
+
+#2908256 "Error: array_sum() should be an array" (drupal)
+#2916864 "Should at least print legend on pie charts with empty data"
+  Pie charts with invalid data (no Y values > 0) now make an empty plot.
+
+#2906436 "Fixes for X Tick Labels vs X Data Labels"
+  Smarter determination of whether to do Tick labels, Data labels, or both.
+
+#2900914 "Problem with display of 0 on Y axis"
+  Fixed rounding error that could produce something like Y=8.12345E-16.
+
+#2886365 "PHP 5 patch" (Declare all functions and variables in PHP5 style)
+  Most internal PHPlot member functions now have "protected" visibility.
+
+#2839547 "SetImageBorderType('none')
+  You can use SetImageBorderType('none') to turn the image border back off.
+
+#1795972 "Fix default point shapes"
+  We now have 20 (vs 10) point shapes, with 10 (vs 1) used by default.
+
+#1795971 "Fix default data colors"
+  We now have 16 (vs 8) default data colors, no duplicates, all visible.
+
+
+Visible Changes and Possible Incompatibilities:
+
++ PHP5 visibility changes (Bug #2886365)
+Details: Most internal PHPlot member functions now have visibility
+    'protected', rather than all being public. All member variables are
+    still 'public'.
+
+Reason for the change: Use the recommended PHP5 syntax, better OO style.
+
+Compatibility: If you were calling a PHPlot internal function that got
+    changed to 'protected', this will break. Please report this.
+
+
++ Fix default point shapes (Bug 1795972)
+Details: We now have 20 (vs 10) point shapes available, and by default we
+    have 10 (vs 1) different shapes in use. The default size is now 6 pixels
+    for all point shapes.
+
+Reason for the changes: Using different shapes helps distinguish the data
+    sets. The existing 10 defined shapes were not enough, since some of them
+    are not centered over the points, too small, or otherwise hard to see.
+    The code to synchronize the point shape and size arrays was broken, and
+    some dubious code to adjust sizes to even numbers needed to be fixed.
+
+Compatibility (1): If you have a points or linepoints plot with more than
+    one dataset, and you did not use SetPointShapes() to configure the
+    shapes, them your plot will change from using a diamond for all data
+    sets to using different shapes for up to 10 data sets.
+
+Compatibility (2): Fixing the point size/point shape array size bug may
+    slightly change the size of some shapes, but it now works the way it
+    was documented and supposed to work.
+
++ Fix default data colors (Bug 1795971)
+Details: Defined a new set of 16 default data colors. The colors are
+    different and contrast well against the default white background.
+    The first 4 colors were not changed.
+
+Reason for the change: The default 8 data colors included two instances
+    of orange, and one color which was invisible on a white background.
+
+Compatibility: Colors will change on any plot with more than 4 data sets
+    where you did not use SetDataColors() to set your own data colors.
+
++ Re-used old function SetXDataLabelAngle()
+Details: SetXDataLabelAngle() now does something different.
+
+Reason for the change: This name was needed for a new function, to set the
+    angle for the X Data Labels. The old use of this function was not
+    documented, and marked "deprecated" in the code since around 2003-12-07.
+
+Compatibility: If you are still using SetXDataLabelAngle() to set both Tick
+    and Data label angles, you need to use SetXLabelAngle() instead.
+
++ Separate controls for tick and data labels (Feature Request 2899921)
+Details: New functions SetXDataLabelAngle(), SetYDataLabelAngle(),
+    SetXDataLabelType(), and SetYDataLabelType() to allow separate control
+    over the angle and format of data labels, versus tick labels.
+
+Reason for the change: Allow Data Labels to use different formatting and
+    angle compared to Tick Labels.
+
+Compatibility: The default behavior has been set up such that there should
+    be no compatibility issues. For example:
+    Old behavior: SetXLabelType() sets the type for both tick and data labels.
+    New behavior: SetXLabelType() sets the type for tick labels and the
+        default type for data labels.  SetXDataLabelType() sets the type for
+        data labels (overrides SetXLabelType).
+
++ X Tick Labels vs X Data Labels (Bug 2906436)
+Details: Regarding SetXTickLabelPos() and SetXDataLabelPos(): If only one
+    of them is called, the behavior is unchanged (only that label type will
+    be displayed). If both are called: Do exactly what was requested. If
+    neither was called: display only data labels if any data labels are
+    non-empty, else display only tick labels.
+
+Reason for the change: 1) Fix the long-standing problem behavior that by
+    default PHPlot overlays tick and data labels below the X axis. 2) Fix
+    order dependency between setting the position of tick and data labels.
+    3) Prepare for future extension of data labels, and allow both tick
+    and data labels to be on if the programmer enables both.
+
+Compatibility: There are some cases where your plot will change.
+    (a) Calls neither SetXDataLabelPos() nor SetXTickLabelPos():
+      Old behavior: Both tick and data labels displayed, possibly overlaid.
+      New behavior: If there are any non-blank data labels, then show only
+          the data labels, not the tick labels. Otherwise, show tick labels.
+
+   (b) Calls both SetXDataLabelPos() and SetXTickLabelPos(), with other than
+       'none' for each position:
+     Old behavior: The latter call was effective; earlier one ignored.
+     New behavior: Independent of order, both calls are effective.
+
+-----------------------------------------------------------------------------
+
+2009-06-14 Release 5.0.7
+
+Overview:
+
+This is the current stable release of PHPlot. The release adds one new
+feature, fixes a few bugs, and changes the license under which PHPlot
+is released.
+
+The PHPlot reference manual has been updated to match this release.  The
+manual is available as a separate download from Sourceforge. The manual is
+also now available for on-line viewing at http://phplot.sourceforge.net
+
+See the ChangeLog file for more about changes and bug fixes.
+
+
+Licensing:
+
+PHPlot is now released on the terms of the GNU Lesser General Public
+License, version 2.1. (Previous versions of PHPlot were released under
+a dual "PHP/GPL" license.) The licensing change was authorized by the
+original author and copyright holder of PHPlot.
+
+
+New feature in 5.0.7:
+
++ Plot area margins can now be partially specified, using either
+  SetMarginsPixels or SetPlotAreaPixels. In previous releases of
+  PHPlot you had to either specify all 4 margins or none.
+  Credit to adoll for this feature.
+
+
+Visible Changes and Possible Incompatibilities:
+
++ Y data range can change:
+  As a result of the bug fixes in this release, automatically-calculated
+  Y data ranges can change. If you have missing Y values in your data,
+  and you let PHPlot calculate the Y data range (that is, you do not
+  call SetPlotAreaWorld with a Ymin value), then the lower limit for Y
+  can change. If you have a plot with data-data-error data type, different
+  error values for different points, and let PHPlot calculate the Y data
+  range, then either Y limit can change.
+
+
+Bug Fixes in 5.0.7:
+
+
++ Fix for bug 2803900: SetRGBArray('large') does not work:
+  Corrected an array name usage problem. You can now select the large
+  color map. Also PHPlot no longer overrides use of the PHP include
+  path when loading the large color map, and now reports an error if the
+  file is needed and not found.
+
++ Fix for bug 2791502 "Error plots treat missing Y values as 0":
+  Missing Y values now with with data-data-error plots.
+
++ Fix for bug 2792860 "Wrong DataLabelLines with missing Y":
+  Data label lines are now suppressed at missing Y values.
+
++ Fix for bug 2786350 "Missing Y data results in bad auto-range":
+  Missing Y values are now ignored when calculating the Y data range.
+  Bug report and analysis by mrten.
+
++ Fix for bug 2786354 "Incorrect auto-range for data-data-error":
+  The Y data range is now correctly calculated for data-data-error plots
+  when the error values differ from point to point.
+
+
+-----------------------------------------------------------------------------
+
+2009-01-20 Release 5.0.6
+
+Overview:
+
+This is the current stable release of PHPlot. The purpose of this release
+is to fix additional problems with text spacing and positioning, and
+introduce some minor new features.
+
+The PHPlot reference manual has been updated to match this release.  The
+manual is available as a separate download from Sourceforge. The manual is
+also now available for on-line viewing at http://phplot.sourceforge.net
+
+
+New features in 5.0.6:
+
++ Allow mixing GD and TrueType font text on the same plot
+  You can use the new method functions SetFontGD() and SetFontTTF() to
+  select a font and font type for text element (labels, titles, etc.) For
+  example, you can have TrueType plot titles, and GD-fixed font labels.
+  SetUseTTF() now sets the default text type, TTF or GD. This is fully
+  backward compatible.
+
++ Extended label formatting
+  See the reference manual for more information on these.
+
+  New label formatting types are added: 'printf' (using a user-defined
+  format), and 'custom' (using a callback function).
+
+  For 'data' type formatting, a prefix and suffix can be added. (PHPlot
+  previously had an undocumented suffix for 'data' type, which still
+  works.)
+
+  For 'time' formatting, the format can now be specified in the same function
+  call rather than using SetXTimeFormat and SetYTimeFormat.
+
+  For 'data' formatting, the precision can now be specified in the same
+  function call, rather than using SetPrecisionX and SetPrecisionY.
+
++ Better control over line spacing in multi-line labels
+
+  Line spacing can now be set separately for each text element using an
+  additional argument to SetFont, SetFontGD, and SetFontTTF.  The overall
+  SetLineSpacing() value is the default for each text element that does not
+  have a specific line spacing set.
+
+  PHPlot now interprets the value set for line spacing as the number of
+  pixels only for GD text. For TrueType text, it is a scale factor for the
+  font's built-in line spacing for TrueType text. The equation used is:
+      interline_spacing = line_spacing * font_natural_spacing / 6
+  where line_spacing is either the global value set with SetLineSpacing
+  or a more specific value set with SetFont(), and font_natural_spacing
+  is the amount of space between lines built-in to the TrueType font. The
+  factor 6 should really be 4 (since PHPlot always used 4 as the default
+  line_spacing, this would give the natural font spacing by default). But
+  the text is too widely spaced with this value, and 6 was chosen to be
+  more compatible for typical font sizes.
+
+Visible Changes and Possible Incompatibilities:
+
++ Line spacing
+  Multi-line TrueType titles and labels will have different inter-line
+  spacing. Since the text size affects the margin and plot area sizes,
+  this results in slightly different sized features on any plot with
+  multi-line TrueType text.
+  Previous versions of PHPlot used a default 4 pixels for inter-line
+  spacing of multi-line TrueType text, regardless of the font size.
+  PHPlot now uses the 'natural' font inter-line spacing, adjusted by a line
+  spacing parameter (per text type, with a global default).
+
+  The same change can also increase the size of the legend box slightly.
+
++ Internal changes were made to the way font information is stored. Anything
+  that directly references PHPlot internals regarding fonts will break. Usage
+  also changed for the internal functions to size and draw text (ProcessText*,
+  SizeText*) due to font data storage changes.
+
++ Changes were made to internal class variables used to store label
+  formatting information. Anything relying on these internals may break.
+
+
+Bug Fixes in 5.0.6:
+
+#1932571: Data-Data Plot fails with same X values
+  PHPlot will no longer hang if all X values are the same. But this is
+  interim fix to force the X range to 1 to prevent the hang. Eventually,
+  smarter automatic range code will handle this better.
+  Credit to andyl for finding this.
+
+#1891636: Misaligned TTF X Labels
+  PHPlot will now correctly line-up TrueType labels along the X axis. There
+  were small but very noticeable errors before, when the text had descenders
+  or lines with all short letters.
+
+
+-----------------------------------------------------------------------------
+
+2008-01-13 Released 5.0.5
+
+Overview:
+
+This is the current stable release of PHPlot. The emphasis of this release
+is to improve text positioning, margin calculation, and error handling.
+
+Although this is considered a stable release, it has a large amount
+of changed code compared to the previous release 5.0.4. Two of the more
+complex components of PHPlot - text and margin calculations - were mostly
+re-written in this release. You are advised to carefully test your own
+applications with PHPlot-5.0.5 to see how your plots look. Refer to the
+README.txt file included in the release for information on reporting problems.
+
+Starting with this release, PHPlot no longer supports PHP4, since the PHP
+group officially declared end-of-life for PHP4 as of 31 December 2007.
+PHPlot-5.0.5 was tested only with PHP-5.2.5 and we are unlikely to address
+any issues using PHPlot with older versions of PHP.
+
+The PHPlot reference manual has been updated to match this release.  The
+manual is available as a separate download from Sourceforge. The manual is
+now also now available for on-line viewing at http://phplot.sourceforge.net
+
+The callback feature added in 5.0.4 is now documented in the reference
+manual. It is still considered experimental and subject to change, however.
+
+
+
+Visible Changes and Possible Incompatibilities:
+
++ Dropped support for PHP4.
+
++ Eliminated remaining order-dependent behavior related to margins and
+text. PHPlot should now do nothing at all, except record parameters, until
+you draw the graph with DrawGraph. I believe this was always the intended
+behavior of PHPlot, but over time perhaps various pre-calculations and
+dependencies crept in. Fixing this simplifies processing and should lead to
+more consistent behavior.
+
++ The rewritten margin calculation code now uses actual sizes of all tick
+and data labels and tick marks, rather than guesses. Margins collapse to
+remove unused elements, but a minimum margin (currently fixed at 15 pixels)
+is applied so the plot edges don't get to close to the image edges. The
+result is that most graphs with auto-calculated margins will change in
+appearance. It most cases, the margins get slightly smaller. In other
+cases, earlier releases mis-calculated the margins, so this release will
+produce much neater margins.
+
++ The X and Y titles are now offset out from the plot area, not in from the
+image area.  For auto-calculated margins this should not make any
+difference, but if you use SetMarginsPixels or SetPlotAreaPixels to set
+larger margins, the axis titles will move in closer to the plot with this
+release.
+
++ Changes were made to PHPlot internals, including removal of some class
+variables and functions, and addition of new variables and functions.
+These are documented in the ChangeLog. Relying on any internal variables
+or functions in an application using PHPlot is unwise. The following
+internal functions were removed:
+     SetImageArea() DrawDotSeries() DrawLineSeries() CalcXHeights()
+     CalcYWidths() DrawLabels() InitImage() DrawDashedLine()
+         These were marked 'deprecated', were undocumented and unmaintained.
+     TTFBBoxSize()
+         This was replaced with SizeText().
+
++ Line spacing set with SetLineSpacing() now affects TTF text as well as
+GD text. Previously, it only affected GD text. The default line spacing
+happens to be usable for TTF text.
+
++ Changes were made to error handling. PHPlot will now trigger a user-level
+error after producing an error image, instead of exiting.  If no error
+handler has been set, it will exit, as before. But now the error message
+should also get logged, or written to the standard error stream, depending
+on the SAPI in use.  You can now establish an error handler to catch most
+PHPlot errors and do some cleanup before exit.
+
++ PHPlot no longer accepts some invalid option values (such as a substring
+of a valid value, or empty strings) passed to functions. If your
+application aborts in CheckOption with PHPlot-5.0.5 but 'worked' with
+previous releases, them you were probably using an invalid option value.
+
+
+
+Bug Fixes in 5.0.5:
+
+#945439: x_tick_label_height not set correctly
+  Exact sizes of labels are now used to calculate margins.
+
+#1813070: Bad position for multi-line TrueType text
+  Fixed as part of text functions rewrite. Use correct basepoint
+  (lower left of each line) when positioning text lines.
+
+#1813071: Wrong title height for multi-line TTF text
+  Fixed as part of text functions rewrite: calculate height of
+  multi-line text correctly. Also now uses the line-spacing setting.
+
+#1813474: DrawText alignment arguments wrong
+  Fixed so 'top' and 'bottom' now have the usual meaning: top means
+  align top of text with reference, bottom means align bottom of text.
+  This was switched before. Changed every internal caller to compensate.
+
+#1816844: Fix order dependency for setting titles
+  Defer processing of title strings until DrawGraph(),
+  so it doesn't matter if fonts, etc. are set before or after.
+
+#1819668: Horiz. align multi-line text: GD vs TTF
+  The text functions were rewritten to draw TTF text line-by-line,
+  like GD text, and correctly align each line.
+
+#1823774: Default Font Path and Error Message
+  Error handling has been improved to make sure a message is logged, in
+  addition to the error image, and use error_trigger rather than exit.
+
+#1826513: FIXME in DrawLegend: Max label length
+  The actual size needed for legend text is now used.
+
+#1827263: Spoiled up pie-chart if $val is close to zero
+  Fixed by skipping over any segment that rounds to 0 degrees of
+  arc. (The GD function uses integer angles only, and 0 degrees
+  means draw a complete circle.)
+
+#1836528: Insufficient checking of parameter values
+  Rewrote validator function to reject improper parameter values.
+
+#1843012: Make margins, drawing consistent
+  Margin code logic was rewritten and checked for consistency.
+
+#1856207: Margin error with 'xaxis'/'yaxis' position
+  Margin space is now allocated for ticks and labels if their position
+  is 'xaxis' or 'yaxis' and the axis is at the plot edge. This is not
+  a perfect fix (the axis could be close but not at the edge).
+
+
+-----------------------------------------------------------------------------
+
+2007-10-20 Released 5.0.4
+
+Overview:
+
+This is the latest stable release of PHPlot. We are abandoning the 'rc'
+version naming style, because we don't consider these last releases
+'release candidate' versions. As we continue to make changes to PHPlot,
+we are not converging toward a final "5.0" release, however we do consider
+these releases stable and complete enough for production use.
+
+This release fixes a number of problems and introduces a few new features.
+
+The PHPlot reference manual has also been updated to match this release.
+New material has been added documenting some of the PHPlot internals.
+The manual is available as a separate download from Sourceforge.
+
+
+Code Cleanup:
+
+Some code cleanup is going in to this release. It is hoped that these
+changes will not impact any existing scripts using PHPlot, but will make
+the PHPlot code itself easier to understand and maintain.
+
+PHPlot now avoids making changes outside its own class definition. There
+are no longer any functions defined outside the class, nor any constants.
+Three constants (MINY MAXY TOTY) were removed, and 2 functions were removed
+(see Visible Changes below).  Also PHPlot no longer sets the PHP error
+reporting level to E_ALL. Although we highly recommend setting error
+reporting to E_ALL in your php.ini file or scripts, it is not right for
+PHPlot to assume that you want it.
+
+
+Visible Changes and Possible Incompatibilities:
+
+Arrays containing color and style information are used with several PHPlot
+functions to control the plot style array. These functions are:
+    SetPointShapes, SetPointSizes, SetLineWidths, SetLineStyles,
+    SetDataColors, SetDataBorderColors, and SetErrorBarColors.
+The arrays passed to these functions MUST used sequential integer 0-based
+indexes. This is what the PHP manual calls "Usual integer indices (starting
+from zero, increasing by one)". This is the type of array you get in PHP by
+default if you use array() without specifying key values, or use the
+empty-bracket assignment operator to add values onto an array.  In previous
+versions of PHPlot, some of these functions would also work with
+string-indexed or non-sequentially-indexed arrays, but this was not clearly
+defined. Starting with PHPlot-5.0.4, only arrays with "usual integer
+indices" work, and other array indexes will cause errors.
+
+Some internal-use-only functions have had their usage changed or been removed.
+If you are using functions that are not documented in the PHPlot Function
+Reference in the manual, your code may have to be changed.
+
+As part of the code cleanup, two functions which were defined outside the
+PHPlot class were removed: array_pad_array(), and array_merge_php4().
+If your code used these, you need to fix your code.
+
+The routines which accept a color name, value, or array now check for a valid
+color name. If you specify a color name which is not in your current color
+table, PHPlot will draw an error and exit. Previously, PHP would report an
+index error, continue, and get a 'headers already sent' message.
+
+
+Bug Fixes in 5.0.4:
+
+#1813021: Miss-positioned right-justified vertical GD text.
+  Fixed DrawText() to correctly position 90 degree right-justified text
+  drawn in a fixed GD font. This could be seen with 90 degree Y tick labels.
+
+#1790441 Removed destructor/shutdown function, and no longer recommend
+  using reference assignment when creating a PHPlot object. This was
+  interfering with memory usage.
+  Credit to annajilly for analysis.
+
+#1779115 SetLegendWorld() failed because of undefined variables. The
+  required order dependency was too hard to meet. This is now fixed.
+  You can now use SetLegendWorld anywhere before DrawGraph.
+
+#1726810 (feature request, but actually a bug fix) Ignore empty strings
+  as data labels when doing time or data label formatting. These would
+  previously produce errors or bad formatting. Now you can omit labels
+  as needed even with time and data formatting.
+  Credit to exgerhardr for finding this.
+
+#1605555 Y data labels used wrong font and not formatted (bar charts only).
+
+#1208054 Localization of number formatting in 'data' format type. PHPlot
+  will attempt to format the numbers in a way appropriate to your locale.
+  You can also force the formatting with the new function SetNumberFormat.
+  Credit to David Hernández Sanz.
+
+#937944 X/Y Tick counts: PHPlot could draw one two few Y tick counts, and
+  one too many X tick counts. This is not a perfect fix, and more work is
+  needed here, but this fixes an error case in both X and Y values.
+
+
+New Features in 5.0.4:
+
+New function SetLegendStyle allows control of the alignment of text and
+  color boxes within the legend.  Also allows removing the color boxes.
+  Based on bug #1208054.
+  Credit to David Hernández Sanz.
+
+New function SetNumberFormat. See bug report #1208054 above.
+
+Callbacks are added. PHPlot can call back your functions while generating the
+  plot. This is experimental, and documented only in the file "Callbacks".
+  Credit to annajilly for the idea and design.
+
+-----------------------------------------------------------------------------
+
+2006-11-13 Released 5.0rc3
+
+Overview:
+
+This is an interim release. It has been a long time since the previous
+release 5.0rc2, and there have been a lot of changes. There are still more
+changes likely to go in before we have "5.0", but there are enough for now.
+
+The PHPlot Reference Manual has also been released, and is available as a
+separate download from Sourceforge. PHPlot users and developers are
+strongly encouraged to read the manual.
+
+This release does not include the "doc/" and "examples/" directories of
+previous releases. The Reference Manual contains more complete and
+up-to-date information and examples, and I am unable to maintain the doc/
+and examples/ files while also maintaining the Reference Manual. If you
+need those files, they can be accessed with the Sourceforge web CVS
+browser.
+
+
+New Features:
+
+The emphasis for this release is bug fixing, so there are few new features.
+
++ You can now suppress lines or points on individual plots in a linepoints
+  graph. This feature was added because I needed a graph with several
+  linepoints lines, but also with a solid line showing an "80% goal".
+  Use SetPointShapes with the value 'none' in the array to suppress the
+    point markers for that plot (and only draw the line).
+  Use SetLineStyles with the value 'none' in the array to suppress the
+    line for that plot (and only draw the point markers).
+  [Bug # 1594458]
+
++ Bar charts can have data labels above the bar with the value. Turn
+  these on with SetYDataLabelPos('plotin'). This is somewhat experimental,
+  since there isn't a lot of room for labels on top of the bars and you
+  may find the results are not useful.
+
+
+Visible Changes:
+
+Here are the more significant changes in this release. These are changes
+which may affect existing scripts and output from PHPlot.  See the
+ChangeLog file for information about all changes and bug fixes.
+
++ A bug fix on bar chart bar borders results in black borders around the
+  bars if shading is turned off. The border was previously covered up,
+  but was supposed to be there. If you need borderless, unshaded bars,
+  you need to use SetDataBorderColors to make the borders the same colors
+  as the bars. [Bug # 1096197]
+
++ TrueType font pathname handling was fixed. You no longer need to use
+  SetUseTTF(True). You can either use full paths to the font files with
+  SetDefaultTTFont() and SetFont(), or you can call SetTTFPath() to point
+  to a directory of font files, and then use simple font filenames without
+  paths in SetDefaultTTFont() and SetFont().
+  [Bug # 1144644 plus several others]
+
++ There have been several fixes regarding automatically calculated ranges
+  and scales. The result is that you may see less extra space and fewer
+  tick marks in some cases.
+
++ A fix was made to bar and stackedbar graph bar widths in order to get
+  the X axis labels to properly center. As part of the fix, the bar widths
+  now match between the two graph types. (Before this fix, the bars were
+  narrower in bar graphs compared to the same data plotted as a stacked
+  bar.) As a result, bar graph bars will now be drawn with wider bars, and
+  stackedbar graph bars will be narrower. You can adjust this with the new
+  class variable bar_extra_space.     [Bug # 1437912]
+
++ Dot shapes and sizes were off by 1 or 2 slots in the array of shapes or
+  sizes. After the fix, you may get different dot shapes or sizes per
+  plot line. [Bug # 1096194]
+
+
+Testing:
+Since its output is visual (graphics), and it has so many interconnected
+modes and options, PHPlot is difficult to test. But at least we are now
+trying. I have a collection of PHPlot scripts (currently about 60) and a
+script to run through them. The script automatically checks that:
+    1) Nothing was written to the standard error stream;
+    2) An image file of size greater than 0 was written;
+    3) Neither the test script nor PHPlot did exit(). This catches cases
+       where PHPlot aborts with DrawError().
+
+The automated test is an easy way to check for serious regression, but you
+really need to inspect the output files to validate PHPlot. This takes a
+little time, and it is easy to overlook problems.
+
+The real issue is test coverage. Just as we can be sure that future
+PHPlot releases will pass the test collection, we can also be sure that
+future bug reports will be written against untested cases.
+
+--------------------
+
+2006-11-08 PHPlot on Sourceforge has a new maintainer: lbayuk
+
+--------------------
+
+2004-10-24 Released 5.0rc2
+
+--------------------
+
diff --git a/gui/bacula-web/external_packages/phplot/README b/gui/bacula-web/external_packages/phplot/README
deleted file mode 100644 (file)
index b571f13..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-This is a class for creating scientific and business charts. 
-To start extract the files with
-
-       tar -zxvf phplot-5.0rc1.tar.gz 
-    
-and then point your browser to 
-
-       doc/index.php.
-
-There are some configuration settings that you will need to make 
-based on your setup. 
-
-1. File Type: Depending on the version of GD you are using, 
-   you may or may not have GIF or PNG file ability. That is 
-   set with the function. 
-   
-        SetFileFormat("<filetype>") where <filetype> is png, gif, jpeg, ...
-        
-   or edit the file phplot.php and change the line
-   
-        var $file_format = "<filetype>";
-
-2. TTF: If you have TTF installed then use (and read the docs)
-
-        SetUseTTF(TRUE);
-        
-   otherwise use
-   
-       SetUseTTF(FALSE);
-
-Everything else should be independent of what version you are using.
-This has been tested with PHP3, PHP4, GD1.2 and GD 3.8. 
-
-To start please see doc/index.php. There you'll find examples, tests and
-some introductory documents.
-
---------------------------
-
-This is distributed with NO WARRANTY and under the terms of the GNU GPL
-and PHP licenses. If you use it - a cookie or some credit would be nice.
-
-You can get a copy of the GNU GPL at http://www.gnu.org/copyleft/gpl.html
-You can get a copy of the PHP License at http://www.php.net/license.html
-
-See http://www.sourceforge.net/projects/phplot/ for the latest changes.
diff --git a/gui/bacula-web/external_packages/phplot/README.txt b/gui/bacula-web/external_packages/phplot/README.txt
new file mode 100644 (file)
index 0000000..7485226
--- /dev/null
@@ -0,0 +1,163 @@
+This is the README file for PHPlot
+Last updated for PHPlot-5.2.0 on 2010-10-03
+The project web site is http://sourceforge.net/projects/phplot/
+The project home page is http://phplot.sourceforge.net/
+-----------------------------------------------------------------------------
+
+OVERVIEW:
+
+PHPlot is a PHP class for creating scientific and business charts.
+
+The release documentation contains only summary information. For more
+complete information, download the PHPlot Reference Manual from the
+Sourceforge project web site. You can also view the manual online at
+http://phplot.sourceforge.net
+
+For important changes in this release, see the NEWS.txt file.
+
+
+CONTENTS:
+
+   COPYING  . . . . . . . . . . . . LGPL 2.1 License file
+   ChangeLog  . . . . . . . . . . . Lists changes to the sources
+   NEWS.txt . . . . . . . . . . . . Highlights changes in releases
+   README.txt   . . . . . . . . . . This file
+   contrib  . . . . . . . . . . . . "Contributed" directory, add-ons
+   phplot.php   . . . . . . . . . . The main PHPlot source file
+   rgb.inc.php  . . . . . . . . . . Optional extended color table
+
+
+REQUIREMENTS:
+
+You need a recent version of PHP5, and you are advised to use the latest
+stable release.  This version of PHPlot has been tested with PHP-5.3.3 and
+PHP-5.2.14 on Linux, and with PHP-5.3.3 on Windows/XP.
+
+Use of PHP-5.3.2 or PHP-5.2.13 is not recommended, if you are using
+TrueType Font (TTF) text. A bug with TTF rendering in those versions
+affects PHPlot images. This was fixed in PHP-5.3.3 and PHP-5.2.14.
+
+You need the GD extension to PHP either built in to PHP or loaded as a
+module. Refer to the PHP documentation for more information - see the
+Image Functions chapter in the PHP Manual. We test PHPlot only with the
+PHP-supported, bundled GD library.
+
+If you want to display PHPlot charts on a web site, you need a PHP-enabled
+web server. You can also use the PHP CLI interface without a web server.
+
+PHPlot supports TrueType fonts, but does not include any TrueType font
+files.  If you want to use TrueType fonts on your charts, you need to have
+TrueType support in GD, and some TrueType font files.  By default, PHPlot
+uses a simple font which is built in to the GD library.
+
+
+INSTALLATION:
+
+Unpack the distribution. (If you are reading this file, you have probably
+already done that.)
+
+Installation of PHPlot simply involves copying two script files somewhere
+your PHP application scripts will be able to find them. The scripts are:
+     phplot.php   - The main script file
+     rgb.inc.php  - Optional large color table
+Make sure the permissions on these files allow the web server to read them.
+
+The ideal place is a directory outside your web server document area,
+and on your PHP include path. You can add to the include path in the PHP
+configuration file; consult the PHP manual for details.
+
+
+KNOWN ISSUES:
+
+Here are some of the problems we know about in PHPlot. See the bug tracker
+on the PHPlot project web site for more information.
+
+#1795969 The automatic range calculation for Y values needs to be rewritten.  
+  This is especially a problem with small offset ranges (e.g. Y=[999:1001]).
+  You can use SetPlotAreaWorld to set a specific range instead.
+
+#1605558 Wide/Custom dashed lines don't work well
+  This is partially a GD issue, partially PHPlot's fault.
+
+#2919086 Improve tick interval calculations
+  Tick interval calculations should try for intervals of 1, 2, or 5 times
+  a power of 10.
+
+PHP Issues:
+
+  PHP has many build-time and configuration options, and these can affect
+the operation of PHPlot (as well as any other application or library). Here
+are some known issues:
+  + Slackware Linux includes a version of PHP built with --enable-gd-jis-conv
+(JIS-mapped Japanese font support). This prevents the usual UTF-8 encoding
+of characters from working in TrueType Font (TTF) text strings.
+  + The Ubuntu Linux PHP GD package (php5-gd) was built to use the external
+shared GD library, not the one bundled with PHP. This can result in small
+differences in images, and some unsupported features (such as advanced
+truecolor image operations). Also, although this Ubuntu GD library was
+built with fontconfig support, PHP does not use it, so you still need to
+specify TrueType fonts with their actual file names.
+  + Some PHP installations may have a memory limit set too low to support
+large images, especially truecolor images.
+
+
+If you think you found a problem with PHPlot, or want to ask questions or
+provide feedback, please use the Help and Discussion forum at
+     http://sourceforge.net/projects/phplot/
+If you are sure you have found a bug, you can report it on the Bug tracker
+at the same web site. There is also a Features Request tracker.
+
+
+TESTING:
+
+You can test your installation by creating the following two files somewhere
+in your web document area. First, the HTML file:
+
+------------ simpleplot.html ----------------------------
+<html>
+<head>
+<title>Hello, PHPlot!</title>
+</head>
+<body>
+<h1>PHPlot Test</h1>
+<img src="simpleplot.php">
+</body>
+</html>
+---------------------------------------------------------
+
+Second, in the same directory, the image file producing PHP script file.
+Depending on where you installed phplot.php, you may need to specify a path
+in the 'require' line below.
+
+------------ simpleplot.php -----------------------------
+<?php
+require 'phplot.php';
+$plot = new PHPlot();
+$data = array(array('', 0, 0), array('', 1, 9));
+$plot->SetDataValues($data);
+$plot->SetDataType('data-data');
+$plot->DrawGraph();
+---------------------------------------------------------
+
+Access the URL to 'simpleplot.html' in your web browser. If you see a
+simple graph, you have successfully installed PHPlot. If you see no
+graph, check your web server error log for more information.
+
+
+COPYRIGHT and LICENSE:
+
+PHPlot is Copyright (C) 1998-2010 Afan Ottenheimer
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation;
+version 2.1 of the License.
+
+This software is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this software; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
diff --git a/gui/bacula-web/external_packages/phplot/contrib/README.txt b/gui/bacula-web/external_packages/phplot/contrib/README.txt
new file mode 100644 (file)
index 0000000..3c63225
--- /dev/null
@@ -0,0 +1,31 @@
+This is the README for PHPlot Contributed Code
+The project web site is http://sourceforge.net/projects/phplot/
+Last updated on 2009-12-08
+-----------------------------------------------------------------------------
+
+The PHPlot Contributed Code directory contains code you might find useful
+with PHPlot, but that doesn't quite belong as part of PHPlot itself.
+
+You will have to read the comments in the code files, and see the example
+files, to determine what these do and if they are useful to you. None of
+these functions is documented in the PHPlot Reference Manual.
+
+You may include or paste these functions into your own scripts. Check the
+files for details, but some of these are considered "public domain" with no
+usage or license restrictions.
+
+-----------------------------------------------------------------------------
+Contents:
+
+prune_labels: Reduce the number of labels along the X axis
+    prune_labels.php . . . . . . . . . . . . Code
+    prune_labels.example.php . . . . . . . . Example
+    prune_labels.test.php  . . . . . . . . . Test
+
+color_range: Create a gradient color map for data colors
+    color_range.php  . . . . . . . . . . . . Code
+    color_range.example.php  . . . . . . . . Example
+    color_range.test1.php  . . . . . . . . . Image creation test
+    color_range.test2.php  . . . . . . . . . Unit test
+
+-----------------------------------------------------------------------------
diff --git a/gui/bacula-web/external_packages/phplot/contrib/color_range.example.php b/gui/bacula-web/external_packages/phplot/contrib/color_range.example.php
new file mode 100644 (file)
index 0000000..d29978b
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+# PHPlot / contrib / color_range : Example
+# $Id: color_range.example.php,v 1.1 2009/12/09 03:45:45 lbayuk Exp $
+# This is a bar chart with a color gradient for the bars in each group.
+
+require_once 'phplot.php';
+require_once 'color_range.php';
+
+$bars_per_group = 10;
+$x_values = 4;
+
+mt_srand(1);
+$data = array();
+for ($i = 0; $i < $x_values; $i++) {
+    $row = array($i);
+    for ($j = 0; $j < $bars_per_group; $j++) $row[] = mt_rand(0, 100);
+    $data[] = $row;
+}
+
+$p = new PHPlot(800, 600);
+$p->SetTitle('Example - Bar Chart with gradient colors');
+$p->SetDataType('text-data');
+$p->SetDataValues($data);
+$p->SetPlotAreaWorld(0, 0, $x_values, 100);
+
+# This isn't necessary, as we do know how many data sets (bars_per_group):
+$n_data = count_data_sets($data, 'text-data');
+# Make a gradient color map:
+$colors = color_range($p->SetRGBColor('SkyBlue'),
+                      $p->SetRGBColor('DarkGreen'), $n_data);
+$p->SetDataColors($colors);
+$p->SetXTickLabelPos('none');
+$p->SetXTickPos('none');
+$p->SetPlotType('bars');
+$p->DrawGraph();
diff --git a/gui/bacula-web/external_packages/phplot/contrib/color_range.php b/gui/bacula-web/external_packages/phplot/contrib/color_range.php
new file mode 100755 (executable)
index 0000000..6ff4d6d
--- /dev/null
@@ -0,0 +1,103 @@
+<?php
+/*
+   PHPlot / contrib / color_range
+   $Id: color_range.php,v 1.2 2010/10/03 21:57:15 lbayuk Exp $
+   PHPlot contrib code - public domain - no copyright - use as you wish
+
+   Original contribution from: Josep Sanz <josep dot sans at w3 dot es>
+      "I wrote this code to calculate the range of colors between 2 colors
+      to plot using the range from color_a to color_b..."
+
+   I have changed the code and repackaged it, but the idea is the same.
+   Given 2 colors and number of data sets, computes an array of colors
+   that make up a gradient between the two provided colors, for use
+   with SetDataColors().
+
+   Provides the following functions:
+       $colors = color_range($color_a, $color_b, $n_intervals)
+              Returns a color array for SetDataColors.
+
+       $n = count_data_sets($data, $data_type)
+              Counts the number of data sets in a data array.
+              This can be used to provide $n_intervals in color_range().
+*/
+
+
+
+/*
+   Fill a color map with a gradient step between two colors.
+  Arguments:
+    $color_a : Starting color for the gradient. Array of (r, g, b)
+    $color_b : Ending color for the gradient. Array of (r, g, b)
+    $n_steps : Total number of color steps, including color_a and color_b.
+
+  Returns: A color map array with n_steps colors in the form
+           $colors[i][3], suitable for SetDataColors().
+
+  Notes:
+    You may use the PHPlot internal function $plot->SetRGBColor($color)
+    to convert a color name or #rrggbb notation into the required array
+    of 3 values (r, g, b) for color_a and color_b.
+
+    Newer versions of PHPlot use 4 components (r, g, b, a) arrays for color.
+    This script ignores the alpha component in those arrays.
+
+*/
+function color_range($color_a, $color_b, $n_steps)
+{
+    if ($n_steps < 2) $n_steps = 2;
+    $nc = $n_steps - 1;
+    # Note: $delta[] and $current[] are kept as floats. $colors is integers.
+    for ($i = 0; $i < 3; $i++)
+        $delta[$i] = ($color_b[$i] - $color_a[$i]) / $nc;
+    $current = $color_a;
+    for ($col = 0; $col < $nc; $col++) {
+        for ($i = 0; $i < 3; $i++) {
+            $colors[$col][$i] = (int)$current[$i];
+            $current[$i] += $delta[$i];
+        }
+    }
+    $colors[$nc] = $color_b;  # Make sure the last color is exact.
+    return $colors;
+}
+
+
+/*
+    Determine the number of data sets (plot lines, bars per group, pie
+    segments, etc.) contained in a data array.
+    This can be used to determine n_steps for $color_range.
+
+  Arguments:
+    $data : PHPlot data array
+    $data_type : PHPlot data type, describing $data. (e.g. 'data-data')
+  Returns: The number of data sets in the data array.
+  Notes:
+    This has to scan the entire data array. Don't use this unless you
+    really don't have a better way to determine the number of data sets.
+
+    This does NOT require that the data array be integer indexed.
+
+*/
+function count_data_sets($data, $data_type)
+{
+
+    if ($data_type == 'text-data-single')
+        return count($data); # Pie chart, 1 segment per record
+
+    # Get the longest data record:
+    $max_row = 0;
+    foreach ($data as $row)
+        if (($n = count($row)) > $max_row) $max_row = $n;
+
+   if ($data_type == 'text-data' || $data_type == 'text-data-yx')
+      return ($max_row - 1);  # Each record is (label Y1 Y2...)
+
+   if ($data_type == 'data-data' || $data_type == 'data-data-yx')
+      return ($max_row - 2); # Each record is (label X Y1 Y2...)
+
+   if ($data_type == 'data-data-error')
+      return (($max_row - 2) / 3); # Each record is (label X Y1 Y1+ Y1-...)
+
+   # Not a recognized data type... Just return something sane.
+   return $max_row;
+}
diff --git a/gui/bacula-web/external_packages/phplot/contrib/color_range.test1.php b/gui/bacula-web/external_packages/phplot/contrib/color_range.test1.php
new file mode 100644 (file)
index 0000000..661f591
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+# PHPlot / contrib / color_range : Test 1, make a picture
+# $Id: color_range.test1.php,v 1.1 2009/12/09 03:45:49 lbayuk Exp $
+# This creates a PNG file on output with a color gradient.
+
+require_once 'color_range.php';
+
+function usage()
+{
+   fwrite(STDERR, "Usage: color_range.test1.php color1 color2 number_of_colors
+Each color is of the form rrggbb with 2 digit hex color components.
+");
+   exit(1);
+}
+
+# Split color "rrggbb" into separate components. Code is from PHPlot.
+function rgb($color)
+{
+  return array(hexdec(substr($color, 1, 2)),
+               hexdec(substr($color, 3, 2)),
+               hexdec(substr($color, 5, 2)));
+}
+
+if ($_SERVER['argc'] != 4) usage();
+
+$color1 = rgb($_SERVER['argv'][1]);
+$color2 = rgb($_SERVER['argv'][2]);
+$n_col = (int)$_SERVER['argv'][3];
+if ($n_col < 2) usage();
+
+# Build a color map from colors[0]=color1 to colors[$n_col-1]=color2.
+$colors = color_range($color1, $color2, $n_col);
+
+# Make a picture:
+$w = 800;
+$h = 800;
+$im = imagecreate($w, $h);
+$background = imagecolorresolve($im, 0, 0, 0);
+for ($col = 0; $col < $n_col; $col++) {
+    list($r, $g, $b) = $colors[$col];
+    $colmap[$col] = imagecolorresolve($im, $r, $g, $b);
+}
+
+$margin = 20;
+$bar_width = (int)(($w - 2 * $margin) / $n_col);
+$x1 = $margin;
+$x2 = $x1 + $bar_width;
+$y1 = $margin;
+$y2 = $h - $margin;
+for ($col = 0; $col < $n_col; $col++) {
+    imagefilledrectangle($im, $x1, $y1, $x2, $y2, $colmap[$col]);
+    $x1 = $x2;
+    $x2 += $bar_width;
+}
+imagepng($im);
diff --git a/gui/bacula-web/external_packages/phplot/contrib/color_range.test2.php b/gui/bacula-web/external_packages/phplot/contrib/color_range.test2.php
new file mode 100644 (file)
index 0000000..bf6ff6c
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+/*
+  PHPlot / contrib / color_range : Unit tests
+  $Id: color_range.test2.php,v 1.1 2009/12/09 03:45:51 lbayuk Exp $
+
+  Tests color.range.php functions:
+      color_range($color_a, $color_b, $n_steps)
+      count_data_sets($data, $data_type)
+
+*/
+require_once 'color_range.php';
+
+
+# Testing count_data_sets()
+function test_count_data_sets($data, $data_type, $expected)
+{
+  $n = count_data_sets($data, $data_type);
+  if ($n == $expected) $result = "Pass";
+  else $result = "FAIL: Expected $expected but got";
+  echo "$result: $n data sets, $data_type with " . count($data) . " records.\n";
+}
+
+function test_driver_count_data_sets()
+{
+    echo "\nTesting count_data_sets():\n";
+    $data1 = array(array('a', 1, 2, 3), array('b', 2, 2, 3));
+    $data2 = array(array('a', 1, 2, 3, 4, 5, 6, 7), array('b', 2, 4, 5, 6));
+    $data3 = array(array('', 1), array('', 2), array('', 3), array('', 4));
+    $data4 = array(array('', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13),
+                   array('', 1, 2, 3, 4, 5, 6, 7),
+                   array('', 1, 2, 3, 4));
+
+    test_count_data_sets($data1, 'text-data', 3);
+    test_count_data_sets($data1, 'data-data', 2);
+    test_count_data_sets($data2, 'data-data-error', 2);
+    test_count_data_sets($data3, 'text-data-single', 4);
+
+    test_count_data_sets($data4, 'text-data', 13);
+    test_count_data_sets($data4, 'data-data', 12);
+    test_count_data_sets($data4, 'data-data-error', 4);
+    test_count_data_sets($data4, 'text-data-single', 3);
+}
+
+# Testing color_range()
+function test_color_range($color1, $color2, $nsteps, $expected)
+{
+    $info = 'From (' . implode(', ', $color1)
+       . ') To (' . implode(', ', $color2) . ") with $nsteps steps";
+
+    $colors = color_range($color1, $color2, $nsteps);
+    if ($colors == $expected) echo "Pass: $info\n";
+    else echo "FAIL: $info\n" . print_r($colors, True) . "\n";
+    if (($n = count($colors)) != $nsteps)
+        echo "FAIL: Bad count $n expecting $nsteps\n";
+}
+
+function test_driver_color_range()
+{
+    echo "\nTesting color_range():\n";
+    test_color_range(array(0,0,0), array(255,255,255), 3,
+        array(array(0,0,0), array(127,127,127), array(255,255,255)));
+
+    test_color_range(array(255,0,0), array(0,255,0), 2,
+        array(array(255,0,0), array(0,255,0)));
+
+    test_color_range(array(100,0,100), array(0,100,0), 6,
+        array(array(100,0,100), array(80,20,80), array(60,40,60),
+              array(40,60,40), array(20,80,20), array(0,100,0)));
+}
+
+
+test_driver_count_data_sets();
+test_driver_color_range();
diff --git a/gui/bacula-web/external_packages/phplot/contrib/prune_labels.example.php b/gui/bacula-web/external_packages/phplot/contrib/prune_labels.example.php
new file mode 100644 (file)
index 0000000..ae05bbf
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+# PHPlot / contrib / prune_labels : Example
+# $Id: prune_labels.example.php,v 1.1 2009/12/09 03:45:53 lbayuk Exp $
+# This produces 250 data points with date-formatted labels, and sets
+# a max of 20 labels to display.
+
+require_once 'phplot.php';
+require_once 'prune_labels.php';
+
+$base = mktime(12, 0, 0, 1, 1, 2000);
+$data = array();
+for ($i = 0; $i < 250; $i++) {
+    $data[] = array(86400 * $i + $base, $i, $i * 0.20);
+}
+
+# Show no more than 20 labels:
+prune_labels($data, 20);
+
+$p = new PHPlot(800, 600);
+$p->SetTitle('Example - pruned data labels');
+$p->SetDataType('data-data');
+$p->SetDataValues($data);
+$p->SetXLabelType('time', '%Y-%m-%d');
+$p->SetXLabelAngle(90);
+$p->SetXDataLabelPos('plotdown');
+$p->SetXTickLabelPos('none');
+$p->SetXTickPos('none');
+$p->SetDrawXGrid(False);
+$p->SetDrawYGrid(False);
+$p->SetPlotType('lines');
+$p->DrawGraph();
diff --git a/gui/bacula-web/external_packages/phplot/contrib/prune_labels.php b/gui/bacula-web/external_packages/phplot/contrib/prune_labels.php
new file mode 100644 (file)
index 0000000..5f272e5
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/*
+   PHPlot / contrib / prune_labels
+   $Id: prune_labels.php,v 1.1 2009/12/09 03:45:55 lbayuk Exp $
+   PHPlot contrib code - public domain - no copyright - use as you wish
+
+Reduce the number of data labels along the X axis,  when the density is too
+high.  This simply blanks out M-1 of every M labels in the data array.
+There are other ways to do this, but we need to keep the labels uniformly
+spaced.  You select the target maximum label count (maxlabels), and you will
+get no more than maxlabels data labels.
+
+  Arguments:
+     $data  - The PHPlot data array (reference variable)
+     $maxlabels - The maximum number of data labels you are willing to have,
+  Returns: Nothing
+     Modifies the $data array in place to remove some of the labels.
+
+   Notes:
+     The data array and its rows must be 0-based integer indexed arrays.
+*/
+function prune_labels(&$data, $maxlabels)
+{
+    # Do nothing if there are not already too many labels:
+    if (($n = count($data)) <= $maxlabels) return;
+
+    # Compute how many labels to erase. Keep 1 of every $m labels.
+    $m = (int)ceil($n / $maxlabels);
+
+    # Process the data array, zapping M-1 of every M labels:
+    $k = 0;
+    for ($i = 0; $i < $n; $i++) {
+       if ($k > 0) $data[$i][0] = '';
+       if (++$k >= $m) $k = 0;
+    }
+}
diff --git a/gui/bacula-web/external_packages/phplot/contrib/prune_labels.test.php b/gui/bacula-web/external_packages/phplot/contrib/prune_labels.test.php
new file mode 100644 (file)
index 0000000..6d3bbb2
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+# PHPlot / contrib / prune_labels : Test
+# $Id: prune_labels.test.php,v 1.1 2009/12/09 03:45:57 lbayuk Exp $
+# Test driver for contrib / prune_labels
+
+require_once 'prune_labels.php';
+
+/* Testing the prune_labels function: */
+function test($count, $maxlabels)
+{
+    # Make an array of count records, like PHPlot uses, with labels:
+    $data = array();
+    for ($i = 0; $i < $count; $i++) {
+        $data[] = array("Row $i", $i, 100, 200, 300);
+    }
+
+    prune_labels($data, $maxlabels);
+
+    # See how many labels are non-blank now:
+    $line = '';
+    $non_blank = 0;
+    for ($i = 0; $i < $count; $i++) {
+        if (!empty($data[$i][0])) {
+            $non_blank++;
+            $line .= '*';
+        } else {
+            $line .= '_';
+        }
+    }
+    $status = ($non_blank <= $maxlabels) ? 'PASS' : 'FAIL';
+    echo "$status: $count rows, maxlabels=$maxlabels => $non_blank labels\n";
+    echo substr($line, 0, 80) . "\n";  # Only show first 80 chars.
+}
+
+/* Test cases for prune_labels */
+for ($n = 7; $n <= 1000; $n *= 2) test($n, 10);
+for ($g = 5; $g <= 40; $g++) test(72, $g);
+# Edge cases
+test(80, 41);
+test(80, 40);
+test(80, 39);
diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/graph1.png b/gui/bacula-web/external_packages/phplot/doc/imgs/graph1.png
deleted file mode 100644 (file)
index 3b5a1c2..0000000
Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/graph1.png and /dev/null differ
diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/graph2.png b/gui/bacula-web/external_packages/phplot/doc/imgs/graph2.png
deleted file mode 100644 (file)
index ff0e80b..0000000
Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/graph2.png and /dev/null differ
diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/graph3.png b/gui/bacula-web/external_packages/phplot/doc/imgs/graph3.png
deleted file mode 100644 (file)
index e437c87..0000000
Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/graph3.png and /dev/null differ
diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/phplot-dia.png b/gui/bacula-web/external_packages/phplot/doc/imgs/phplot-dia.png
deleted file mode 100644 (file)
index eac14eb..0000000
Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/phplot-dia.png and /dev/null differ
diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig1.png b/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig1.png
deleted file mode 100644 (file)
index 08fc68b..0000000
Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig1.png and /dev/null differ
diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig2.png b/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig2.png
deleted file mode 100644 (file)
index 5deafe4..0000000
Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig2.png and /dev/null differ
diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig3.png b/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig3.png
deleted file mode 100644 (file)
index c98f692..0000000
Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig3.png and /dev/null differ
diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig4.png b/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig4.png
deleted file mode 100644 (file)
index f6c6872..0000000
Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig4.png and /dev/null differ
diff --git a/gui/bacula-web/external_packages/phplot/doc/index.php b/gui/bacula-web/external_packages/phplot/doc/index.php
deleted file mode 100644 (file)
index a6482ec..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<!-- $Id$ -->
-<html>
-<head>
-    <title>Welcome to PHPlot</title>
-    <link type="text/css" rel="stylesheet" href="style.css" />
-</head>
-<body>
-
-<h2>Welcome to PHPlot 5.0</h2>
-<table border="0">
- <tr>
-  <td valign="top">
-   <p>
-   PHPlot is a <a href="http://www.php.net">PHP4</a> class for on the fly graphs
-   generation. It was started by
-   <a href="mailto:afan AT jeo DOT net">Afan Ottenheimer</a> in 2000 as an
-   opensource project, and is now co-developed with 
-   <a href="mailto:nonick AT 8027 DOT org">Miguel de Benito</a> thanks to 
-   <a href="http://sourceforge.net">sourceforge</a>. It is distributed under
-   the terms of the 
-   <a href="http://www.gnu.org/copyleft/gpl.html"> GNU General Public License</a>,
-   and the <a href="http://www.php.net/license.html">PHP license</a>. You can always
-   obtain the latest source from the <a href="http://sourceforge.net/projects/phplot/">
-  sourceforge project page</a>, please do also check CVS, we try to have it always working
-  there.
-  </p>
-  <p>For further information, please check <a href="http://www.phplot.com">
-  our website</a>
-  </p>
-  </td>
-  <td>
-   <table border="0">
-   <tr><td><img src="imgs/graph1.png" /></td></tr>
-   <tr><td class="imgfoot">Example line graph with labels, legend
-       and left and lower axis titles.</td></tr>
-   </table>
-  </td>
- </tr>
-</table>
-
-
-<h3>Features</h3>
-<table border="0">
- <tr>
-  <td>
-   <table border="0">
-   <tr><td><img src="imgs/graph3.png" /></td></tr>
-   <tr><td class="imgfoot">Example 3d pie chart.</td></tr>
-  </table>
-  </td>
-  <td>
-   <p>
-   Here goes a (incomplete) list, in no particular order.:
-   <ul>
-    <li>Several different graph types: lines, bars, stacked bars, points, areas, pie, squared.</li>
-    <li>text-data, data only and data-error data types accepted.</li>
-    <li>3D shading for pie and bar graphs.</li>
-    <li>Different line types: solid and wholly customizable dashed ones.</li>
-    <li>Can draw error margins along y-axis when supplied in data. </li>
-    <li>Highly customizable canvas: titles, labels and ticks can be 
-        placed anywhere, with any color and everything gets automagically placed without overlapping.</li>
-    <li>Vertical and horizontal grids.</li>
-    <li>Legend. Different types on the works. </li>
-    <li>TrueType font support.</li>
-    <li>Linear and logaritmic scales.</li>
-    <li>Several output formats: jpeg, png, gif, wbmp (those supported by your GD)</li>
-   </ul>
-   And here a short to-do/whishlist:
-   <ul>
-    <li>Horizontal bars.</li>
-    <li>Simple isometric 3D plots.</li>
-    <li>Automatic placement of several plots in one image.</li>
-    <li>Better or automatic management of many drawing options (ticks, labels, etc.)</li>
-    <li>Subclassing for optimisation: move features into subclasses for optional use 
-        and leave a fast core.</li>
-   </ul>
-   </p>
-  </td>
- </tr>
-</table> 
-
-
-<h3>Requirements</h3>
-<p>
-We are not sure about exact requirements, but at least PHP 4.2.0 and 
-GD Lib 2 are necessary. Feedback is welcome.
-</p>
-
-
-<h3>Quick start</h3>
-<p>You can rush for a quick start <a href="quickstart.html">here</a>.</p>
-
-
-<h3>Tests and examples</h3>
-<p>
-These examples make use of many, but not all, of the features present in PHPlot.
-The best one is the example-o-matic, where you can alter many parameters. Please proceed
-to any of them:
-<ul>
-    <li><a href="../examples/test_setup.php">GD setup test</a></li>
-    <li>Examples:</li>
-        <ul>
-          <li><a href="../examples/format_chart.php">Example-o-matic</a>. 
-            Create most of the plot types with all data types and tweak most of the parameters.</li>
-          <li><a href="../examples/example1.php">Simple lines chart</a></li>
-          <li><a href="../examples/example2.php">Another one</a></li>
-          <li><a href="../examples/example3.php">Scaled data (with phplot_data)</a></li>
-          <li><a href="../examples/example4.php">Stock Chart (log scale)</a></li>
-          <li><a href="../examples/example6.php">Thin bar lines</a></li>
-          <li><a href="../examples/example7.php">Log chart with errors</a></li>
-          <li><a href="../examples/example8.php">Two plots in one image</a></li>
-          <li><a href="../examples/example9.php">Chart with some lines in it</a></li>
-        </ul>
-    <li><b>NOTE:</b>If the examples don't seem to work for you, it may be that you
-        don't have PHP set up correctly. If you do you should see some bold text here:
-        <?php echo "<b>OK!</b>\n"; ?> <br />
-        If you see no text then you should contact your system
-        administrator or your webserver documentation as to how to configure this.
-    </li>
-</ul>
-</p>
-
-
-<h3>Internals</h3>
-<p>
-Description of the use and inner workings of PHPlot:
-<ul>
-    <li><a href="schema.html">PHPlot canvas' elements drawing</a></li>
-    <li>Function reference: (<b>Very outdated</b>)
-        <ul>
-            <li><a href="user_functions.html">User functions</a></li>
-            <li><a href="user_internal_functions.html">User/Internal functions</a></li>
-            <li><a href="internal_functions.html">Internal functions (only of interest 
-                for developers)</a></li>
-        </ul>
-    </li>    
-</ul>
-</p>
-
-<h3>The Authors</h3>
-<p>
-<ul>
-    <li>Original work by <a href="mailto:afan AT jeo DOT net">Afan Ottenheimer</a>.</li>
-    <li>Recent work by <a href="mailto:nonick AT 8027 DOT org">Miguel de Benito</a>.</li>
-    <li>Contributions by Thiemo Nagel, Marlin Viss and Remi Ricard.</li>
-</ul>   
-</p>
-
-<p class="foot">$Id$</p>
-</body>
-</html>
diff --git a/gui/bacula-web/external_packages/phplot/doc/internal_functions.html b/gui/bacula-web/external_packages/phplot/doc/internal_functions.html
deleted file mode 100644 (file)
index 90895c0..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<HTML><HEAD>
-<TITLE>PHPlot Internal Functions</TITLE>
-</HEAD>
-<BODY>
-
-<h2>PHPlot Internal Functions</h2>
-
-<p><DT><B>DrawArea()</B>
-<DD>Internal Function: Draw an Area Chart. $data[] must already be defined as data-data
-using $data[] = array("title",x,y1,y2,...) Points are placed at (x,y1), (x,y2), ...
-one x-point per $data[] element. <BR>Colors and border colors for each of the y1, y2, ... is set
-by <B>SetDataColors($which_data,$which_border)</B>
-This function is called if
-you use SetPlotType("area") and SetDataType("data-data")
-
-
-<p><DT><B>DrawAreaSeries()</B>
-<DD>Internal Function: Draw an Area Chart. $data[] must already be defined as $data[] =
-array("title",y1,y2,y3,...) The x axis is divided evenly, one x-point per
-$data[] element. <BR>Colors and border colors for each of the y1, y2, ... is set
-by <B>SetDataColors($which_data,$which_border)</B>
-This function is called if
-you use SetPlotType("area") and SetDataType("text-data")
-
-<p><DT><B>DrawBackground()</B>
-<DD>Internal Function: Draw the full background of the image on image pointer. Should be used
-BEFORE other Draw functions unless you really want to overwrite what you've done
-before. See <B>SetBackgroundColor</B> and <B>SetImageArea</B> for setting
-parameters.
-
-<p><DT><B>DrawBars()</B>
-<DD>Internal Function: Draw vertical bars. $data[] must already be defined as $data[] =
-array("title",y1,y2,y3,...) The x axis is divided evenly, one x-point per
-$data[] element. <BR>Colors and border colors for each of the y1, y2, ... is set
-by <B>SetDataColors($which_data,$which_border)</B>
-This function is called if
-you use SetPlotType("bars") and SetDataType("text-data")
-
-<p><DT><B>DrawDashedLine($x1,$y1,$x2,$y2,$dashed, $space, $color)</B>
-<DD>Internal Function: Called instead of ImageLine when line_style = 'dashed'
-line_style is set by <b>SetLineStyles</b> All variables passed to DrawDashedLine
-are in pixel coordinates.
-
-<p><DT><B>DrawDots()</B>
-<DD>Internal Function: Draw Dots as defined by <B>SetPointType</B> and data in x,y format. $data[]
-must already be defined as $data[] = array("title",x,y1,y2,...) Where the point
-occurs depends on both the X and Y values. <BR>Colors and border colors for each
-y1, y2, , ... is set by <B>SetDataColors($which_data,$which_border)</B>
-This function is called if
-you use SetPlotType("dots") and SetDataType("data-data")
-
-<p><DT><B>DrawDotSeries()</B>
-<DD>Internal Function: Draw Dots as defined by <B>SetPointType</B> and data in text-data format.
-$data[] must already be defined as $data[] = array("title",y1,y2,...)
-The x axis is divided evenly, one x-point per $data[] element. <BR>
-Colors and border colors
-for each y1, y2, , ... is set by <B>SetDataColors($which_data,$which_border)</B>
-This function is called if
-you use SetPlotType("dots") and SetDataType("text-data")
-
-<p><DT><B>DrawDotsError()</B>
-<DD>Internal Function: Draw Dots and Error Bars as defined by <B>SetPointType</B> and
-data in data-data-error format. $data[] must already be defined
-as $data[] = array("label",x,y,error+,error-)
-Colors and border colors
-for each y1, y2, , ... is set by <B>SetDataColors($which_data,$which_border)</B>
-The shape of the error bars is defined by <B>SetErrorBarShape($which_ebs)</B>
-and <B>SetErrorBarSize($which_ebs)</B> <br>
-This function is called if
-you use SetPlotType("dots") and SetDataType("data-data-error")
-
-<p><DT><B>DrawError($error_message)</B>
-<DD>Internal Function: Draw an internal error message printed on the Image directly.
-This way - if there is an error and the output is an image you
-don't get the PHP "Header already sent" messages.
-
-<p><DT><B>DrawGraph()</B>
-<DD>Internal Function: This is th last function called after you have set up
-all the parameters of the graph see <b>Set....</b> functions below.
-
-<p><DT><B>DrawHorizontalTicks()</B>
-<DD>Internal Function:  Draw the ticks on the X axis.
-The distance between ticks can be defined in a number of ways. <i>Note:
-that for text-data data its best to let the program handle the
-distance between ticks - the default is  1.</i>  (Text-data data is data where the data is evenly spaced over
-the x axis, no x-value is entered)
-
-<p><DT><B>DrawImageBorder()</B>
-<DD>Internal Function: Draw the border around the entire image. Currently this
-generates the raised border look around the image.
-
-<p><DT><B>DrawLabels()</B>
-<DD>Internal Function: Draw the Title, X-axis label, and the Y-axis label. This
-really just calls DrawTitle, DrawXLabel, and DrawYLabel.
-
-<p><DT><B>DrawLegend(x,y,type)</B>
-<DD>Internal Function: Draw the Legend. See <b>SetLegendPixels()</b>
-
-<p><DT><B>DrawLines()</B>
-<DD>Internal Function: Draw lines in data-data format.
-$data[] must already be defined
-as $data[] = array("label",x,y1,y2,....)
-Colors and border colors
-for each y1, y2, , ... is set by <B>SetDataColors($which_data,$which_border)</B>
-This function is called if
-you use SetPlotType("lines") and SetDataType("data-data")
-<br>
-
-<p><DT><B>DrawLineSeries()</B>
-<DD>Internal Function: Draw lines in text-data format.
-$data[] must already be defined
-as $data[] = array("label",y1,y2,y3,....)
-The x axis is divided evenly, one x-point per
-$data[] element. <BR>Colors and border colors for each of the y1, y2, ... is set
-by <B>SetDataColors($which_data,$which_border)</B>
-This function is called if
-you use SetPlotType("lines") and SetDataType("text-data")
-
-<p><DT><B>DrawLinesError()</B>
-<DD>Internal Function: Lines and Error Bars as defined by <B>SetPointType</B> and
-data in data-data-error format. $data[] must already be defined
-as $data[] = array("label",x,y,error+,error-)
-Colors and border colors
-for each y1, y2, , ... is set by <B>SetDataColors($which_data,$which_border)</B>
-The shape of the error bars is defined by <B>SetErrorBarShape($which_ebs)</B>
-and <B>SetErrorBarSize($which_ebs)</B>
-This function is called if
-you use SetPlotType("lines") and SetDataType("data-data-error")
-
-<p><DT><B>DrawPieChart()</B>
-<DD>Internal Function: Draw pie chart. Data is in text-data format. $data[] must
-already been defined as $data[] = array("label",y1,y2,y3,...)<BR>
-Colors and border colors for each of the y1, y2, ... is set
-by <B>SetDataColors($which_data,$which_border)</B>
-This function is called if
-you use SetPlotType("pie") and SetDataType("text-data")
-
-<p><DT><B>DrawPlotAreaBackground()</B>
-<DD>Internal Function: Draw the Background - in the color as defined by <b>X</b>
-and in an area as defined by <b>X</b>
-
-<p><DT><B>DrawPlotBorder()</B>
-<DD>Internal Function: Draw the border around the
-plot area - (Note: this is NOT the full image).  This draws
-the rectangle as well as calling the functions
-<b>DrawVerticalTicks()</b> and <b>DrawXAxis()</b>
-
-<p><DT><B>DrawTitle()</B>
-<DD>Internal Function: This will draw the title as set by
-<b>SetTitle</b> and
-<b>SetTitleColor</b>. This is also affected by SetUseTTF
-to show how the title appears.
-
-
-<p><DT><B>DrawVerticalTicks()</B>
-<DD>Internal Function: Called by DrawPlotBorder. The spacing between the ticks
-is automatically set unless you have previously set the increment
-by
-       <B>SetNumVertTicks($which_nt)</B>
-or
-       <b>SetVertTickIncrements</b> but not both.
-
-<p><DT><B>DrawXAxis()</B>
-<DD>Internal Function: Draw the horizontal X axis at the world position X=0.
-
-<p><DT><B>DrawXLabel()</B>
-<DD>Internal Function: Called from DrawLabels. Draws the label of the X-Axis.
-
-<p><DT><B>DrawYErrorBar($x_world,$y_world,$error_height,$error_bar_type,$color)</B>
-<DD>Internal Function: This is similar to DrawDot, but it draws error bars.
-Draw one set of error bars at $x_world and $y_world where the
-data is defined in world coordinates (not pixel coordinates).
-
-<p><DT><B>DrawYLabel()</B>
-<DD>Internal Function: Called from DrawLabels. Draws the label of the Y-Axis. Its
-position is defined by the size of the font. Typically there is a
-two character height width around the plot area for drawing the
-labels.
-
-<p><DT><B>FindDataLimits()</B>
-<DD>Internal Function: This must be called AFTER SetDataType.  It finds
-the maxima and minima for setting the scaling to be
-able to convert from world to pixel coordinates.
-
-<p><DT><B>InitImage()</B>
-<DD>Internal Function: An internal function called to set the
-image pointer for GD.
-
-<p><DT><B>PrintError($error_message)</B>
-<DD>Internal Function: Prints the given error message to stdout. The function
-is used for fatal errors that do not allow for creating an image.
-
-<p><DT><B>PrintImage()</B>
-<DD>Internal Function: An internal function which prints the image using the PHP
-functions Header("Content-type:...") and ImageGIF(), ImageJPEG(),
-or ImagePNG() depending on what the setting is of $this->file_format.
-This is set by <b>SetFileFormat()</b>
-
-<p><DT><B>SetColor($color_asked)</B>
-<DD>Internal Function:  Translate from $color_asked to the index
-name color used in GD.
-$color_asked can be either a name like "black" or an rgb color array
-array(int,int,int). <br>
-
-<p><DT><B>SetDefaultColors()</B>
-<DD>Internal Function: Sets the default colors when first defining an image.
-Overridden by functions like <b>SetPlotBgColor</b>,
-        <b>SetBackgroundColor</b>,
-        <b>SetTextColor</b>,
-        <b>SetGridColor</b>,
-        <b>SetLightGridColor</b>,
-        <b>SetTickColor</b>, and
-        <b>SetTitleColor</b>
-
-<p><DT><B>SetDrawYGrid($which_dyg)</B>
-<DD>Internal Function: 1 = Draw the Y grid. Anything else, don't draw the Y grid.
-
-<p><DT><B>SetEqualXCoord()</B>
-<DD>Internal Function: For text-data graphs set the spacing between
-data points on the x-axis.
-
-<p><DT><B>SetMargins()</B>
-<DD>Internal Function: When the image is first created - set the margins
-    as the full area of the view surface (or panel),
-    less a margin of 4 character heights all round for labelling.
-    It thus depends on the current character size, set by SetCharacterHeight().
-
-<p><DT><B>SetTranslation()</B>
-<DD>Internal Function: Calculate the scale and origin for translating from
-world to pixel coordinates. This is an internal function,
-not one that you'd use normally, but if you are going to
-use it make sure you do it after SetDataType.
-
-<p><DT><B>SetXLabelHeight()</B>
-<DD>Internal Function: This is an internal function that sets the
-height of the X labels based on the maximum string
-length of the X data. It is used for positioning
-where the labels go relative to the x-axis.
-
-
-<p><DT><B>SetYLabelWidth()</B>
-<DD>Internal Function: Set the
-width of the Y Label based on the maximum string
-length of the Y data. It is used for positioning
-where the labels go relative to the y-axis.
-
-
-<p><DT><B>TTFBBoxSize($size, $angle, $font, $string)</B>
-<DD>Internal Function: Calculate the size of the box which encloses the
-text string <b>$string</b> using font <b>$font</b>,
-angle <b>$angle</b> and size <b>$size</b>. This is
-an internal function which is not called unless use_ttf is
-set to 1.
-
-<p><DT><B>xtr($x_world)</B>
-<DD>Internal Function: Translate into x-pixels coordinates from x world coordinates.
-
-<p><DT><B>ytr($y_world)</B>
-<DD>Internal Function: Translate into y-pixels coordinates from y world coordinates.
-
-
-
-
-
-</dl>
-
-</body>
-</html>
diff --git a/gui/bacula-web/external_packages/phplot/doc/quickstart.html b/gui/bacula-web/external_packages/phplot/doc/quickstart.html
deleted file mode 100644 (file)
index 6f738c6..0000000
+++ /dev/null
@@ -1,455 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<!-- $Id$ -->
-<html>
-<head>
-       <title>PHPLOT Quick Start and Examples</title>
-       <meta http-equiv="content-type" content="text/html; charset=utf-8" />
-       <meta name="author" content="Afan Ottenheimer" />
-    <meta name="author" content="Miguel de Benito" />
-       <meta name="created" content="20010302;20371700" />
-       <meta name="changed" content="20040121;19341300" />
-       <meta name="description" content="phplot quick start and examples" />
-       <meta name="keywords" content="phplot, graphs, images, php" />
-       <meta name="robots" content="all" />
-       <meta name="locale" content="en-us" />
-    <link type="text/css" rel="stylesheet" href="style.css" />
-</head>
-
-<body lang="en-us" bgcolor="#ffffff">
-
-<h1>PHPlot Quick Start and Examples</h1>
-
-<p>Afan Ottenheimer, January 7, 2001</p>
-<p>Miguel de Benito, January 21, 2004</p>
-<p><b>Contents</b></p>
-<ul>
-  <li><a href="#intro">Introduction</a></li>
-  <li><a href="#first">First steps</a></li>
-  <li><a href="#lines">Multiples lines per graph</a></li>
-  <li><a href="#multiple">Multiple graphs per image</a></li>
-  <br />
-</ul>
-
-<h2><a name="intro"></a>Introduction</h2>
-
-<p>Many web sites need to create real-time or dynamic charts and graphs.
-from live data sets. Many users have found PHP a great way for this dynamic
-creation of images using the GD library and there have been several
-good articles describing this [] [] []. The advantage of using the server
-to create an image (server side scripting) is that one does not have to 
-worry about browser compatibility or client operating system compatibility issues.
-The PHP image generating library uses the GD library to create elementary
-shapes (elipse, line, rectangle, ...).  
-PHPlot is a graphics library which provides a means by which you can have your
-(PHP enabled) web server create and manipulate graphs as objects and
-display the completed graph as an image. Data sets passed to PHPlot use a very 
-convenient way for database driven sites, in rows with y coordinate data.
-</p>
-
-<p>First, lets discuss how PHPlot works in general with some
-terminology. A PHPlot <i>image </i>can consist of several <i>graphs </i>,
-each graph consisting of several <i>elements</i>.
-You define an object (e.g. a variable like <code>$graph</code>),
-select the properties of the element that compose the graph and "<i>Draw</i>"
-what you want into the object. Tipically by selecting the plot type with
-<code>SetPlotType</code> and at the end calling <code>DrawGraph</code>. You
-can also directly invoke <code>PrintImage</code>, which either inserts the image
-into the data streaming to the client or writes it to disk. 
-</p>
-<p>In PHPlot there are <em>World coordinates</em>, which are the XY coordinates 
-relative to the axis origin, in the units of the data set; and 
-<em>device (pixel)</em> coordinates which in GD are relative to the 
-origin at the upper left side of the image. 
-</p>
-<p>You can think of the "Draw" functions as shaping the
-image object and the "Print" function as the method of
-finally creating the digital image. PHPlot is smart enough that if
-you only have one graph on an image, then the "Print" is
-done for you automatically. If you do have multiple graphs per image
-then you'll need to use both the "Draw" and "Print"
-functions. We'll talk about that a bit later. 
-</p>
-<p>Since PHP is a server scripted language you have several options
-for how you can "Print" the image. You can:</p>
-<ol>
-       <li>Write the image as a file on the server. (You specify a file
-       name and can specify caching as well)</li>
-       <li>Have the raw data stream out within an HTML file, as &lt;IMG
-       SRC="my_PHPlot_code.php"&gt;</li>
-       <li>Precede the raw data with html image headers and call the
-       script directly (showing up as an image) e.g.
-       http://somewhere/my_PHPlot_code2.php .</li>
-</ol>
-<p>This document explains how to create plots using PHPlot from a
-PHP script. Information on PHP can be found at <a href="http://www.php.net/">www.php.net</a>.
-Information about the GD library which PHP uses to create images can
-be found at <a href="http://www.boutell.com/">www.boutell.com</a>.
-More information about PHPlot can be found at <a href="www.phplot.com">www.PHPlot.com</a>.</p>
-
-<h2>Creating the Object</h2>
-
-<p>You create a PHPlot object by first including the code to be used
-and then defining the variable:</p>
-<div class="box">
-<pre>&lt;?php
-include('./phplot.php');  // here we include the PHPlot code 
-$graph =& new PHPlot();   // here we define the variable graph
-
-//Rest of code goes below
-?&gt;</pre>
-</div>
-
-<p>The above code assigns the PHPlot object to the variable <code>$graph</code>. Please,
-use that '&' to create a reference, it is needed by phplot's approximation of a destructor,
-a facility unavailable in PHP4.</p>
-
-<p> <a href="#top">Back to top</a> </p>
-
-
-<h2>Real World Examples</h2>
-<h3><a name="first"></a>Case 1: A simple graph</h3>
-
-<p>We will start with a simple line graph. </p>
-<div class="box">
-<pre>&lt;?php
-//Include the code
-include('./PHPlot.php');
-
-//Define the object
-$graph =& new PHPlot();
-
-//Define some data
-$example_data = array(
-     array('a',3),
-     array('b',5),
-     array('c',7),
-     array('d',8),
-     array('e',2),
-     array('f',6),
-     array('g',7)
-);
-$graph-&gt;SetDataValues($example_data);
-
-//Draw it
-$graph-&gt;DrawGraph(); // remember, since in this example we have one graph, PHPlot
-                        // does the <i>PrintImage </i>part for you
-?&gt;</pre>
-</div>
-
-<p> And that's it! What we get is the following graph: </p>
-
-<div align=center>
-  <img src="imgs/qstart_fig1.png" name="fig1" alt="figure 1" align="bottom" border="0" />
-  <br />Figure 1
-</div>
-
-<p>That's a great start, but now we'd like to specify the width and height
-of the image.</p>
-
-
-<h3>Case 1a: Different Size Images and Titles</h3>
-<p>
-Lets say we want it to have a width of 300 and a height of 250 pixels.
-So instead of having the line <br /><br />
-    <code>$graph =& new PHPlot();</code> <br /><br />
-we replace it with <br /><br />
-    <code>$graph =& new PHPlot(300,250);</code><br /><br />
-and you have specified the size in pixels of the image to be created.
-A couple of things to note:
-</p>
-<ul>
-  <li>The default is <em>not</em> to use TTF fonts. </li>
-  <li>Since there was only one graph on the image we didn't have to
-       specify PrintImage, DrawGraph took care of it for us.
-  </li>
-  <li>
-       We did not specify the data type. If you do not specify the data
-       type PHPlot assumes <code>text-data</code>.
-  </li>
-  <li>
-       We did not specify the file type (gif, png, jpg, ...) .
-       PHPlot 5.0 assumes PNG image formats.
-  </li>
-  <li>
-       The data is passed in as an array of arrays. This may seem awkward
-       now, but as we add functionality this will be beneficial.
-  </li>
-</ul>
-<p>Ok, now we're ready to add some customization to the plot. Let's change
-the size, the title and the x/y axis labels. All we need to do is modify
-the variable <code>$graph</code> before printing the image. We achieve this with:
-</p>
-<div class="box">
-<pre>&lt;?php
-include ('./phplot.php');
-
-//create an graph object 300x250 pixels
-$graph =& new PHPlot(300,250);
-//Set titles
-$graph-&gt;SetTitle("Title\n\rSubtitle");
-$graph-&gt;SetXTitle('X data');
-$graph-&gt;SetYTitle('Y data');
-
-//...rest of the code
-
-
-?&gt;</pre>
-</div>
-<div align=center>
- <img src="imgs/qstart_fig2.png" name="graphic1" align="bottom" border="0"><br />
- Figure 2
-</div>
-
-<p>Note that in order for the "\n" and "\r " to be interpreted as
-new line/new return characters for <code>SetTitle </code>you have to
-enclose the string in <b>double </b>quotes.</p>
-
-<h3><a name="lines"></a>Case 2: Multiple Lines per Graph </h3>
-<p>Lets say we want to plot not just one
-dataset but several y values for each x position. With PHPlot it is
-easy to specify the multiple data lines by just passing in all the Y
-values for a given X value at once. So instead of array('label', y)
-we specify array('label', y<sub>1</sub>, y<sub>2</sub>, y<sub>3</sub>,
-...) This is very convenient when working with rows of data from databases.
-<br />
-Now our data will have three Y values for each position on the X axis
-</p>
-<div class="box">
-<pre>&lt;?php
-//Include the code
-include('./phplot.php');
-
-//Define the object
-$graph =& new PHPlot(300,250);
-
-//Set titles
-$graph-&gt;SetTitle("Title\n\rSubtitle");
-$graph-&gt;SetXTitle('X data');
-$graph-&gt;SetYTitle('Y data');
-
-
-//Define some data
-$example_data = array(
-     array('a',3,4,2),
-     array('b',5,'',1),  // here we have a missing data point, that's ok
-     array('c',7,2,6),
-     array('d',8,1,4),
-     array('e',2,4,6),
-     array('f',6,4,5),
-     array('g',7,2,3)
-);
-$graph-&gt;SetDataValues($example_data);
-
-//Draw it
-$graph-&gt;DrawGraph();
-?&gt;</pre>
-</div>
-<p>Which gives us: </p>
-<div align="center">
-  <img src="imgs/qstart_fig3.png" name="graphic2" align=bottom border="0" />
-  <br />Figure 3
-</div>
-
-<p>Notice that each set of Y data gets a different color.
-Also the missing data point is skipped, this behaviour can be adjusted with
-<code>SetDrawBrokenLines(TRUE);</code>
-</p>
-<p>
-This gives you the basics of how to create a graph in PHPlot.
-A nice start, but now we'd like to add some customization, namely different
-fonts, margins and types of graphs.
-</p>
-<p><a href="#top">Back to top</a> </p>
-
-
-<h3>Customization</h3>
-<p>Valid types of plots (as of PHPlot 5.0):
-<ul>
-       <li><code>bars</code> (with optional shadows)</li>
-       <li><code>lines</code></li>
-       <li><code>linepoints</code> (a faster way of plotting when
-        you want both points and lines)</li>
-       <li><code>area</code></li>
-       <li><code>points</code> (lots of point types here)</li>
-       <li><code>pie</code> (2D or 3D)</li>
-       <li><code>thinbarline</code> (sometimes also called impulse) </li>
-       <li><code>error bar</code> (which can also be used for stock market data graphs)</li>
-    <li><code>squared</code> (for binary data) </li>
-</ul>
-</p>
-<p>You specify which type with the <code>SetPlotType</code> function.
-We'll look at that function with bars and lines in the next example when we look at
-multiple graphs per image.
-</p>
-<p>As we discussed before, there are several ways we can manipulate
-the look/feel of the graph object. Almost every parameter of ticks, grids and data labels
-can be adjusted via (among many others):
-<ul>
-  <li><code>SetXTickPos()</code></li>
-  <li><code>SetYTickPos()</code></li>
-  <li><code>SetXTickLength()</code></li>
-  <li><code>SetYTickLength()</code></li>
-  <li><code>SetXTickCrossing()</code></li>
-  <li><code>SetYTickCrossing()</code></li>
-  <li><code>SetXTickIncrement()</code></li>
-  <li><code>SetYTickIncrement()</code></li>
-  <li><code>SetNumXTicks()</code></li>
-  <li><code>SetNumYticks()</code></li>
-  <li><code>SetSkipTopTick()</code></li>
-  <li><code>SetSkipBottomTick()</code></li>
-
-  <li><code>SetDrawXGrid()</code></li>
-  <li><code>SetDrawYGrid()</code></li>
-  <li><code>SetDrawDashedGrid()</code></li>
-  <li><code>SetDrawXDataLabelLines()</code></li>
-  <li><code>SetDrawYDataLabelLines()</code> (not yet implemented)</li>
-  <li><code>SetXDataLabelPos()</code></li>
-  <li><code>SetYDataLabelPos()</code></li>
-  <li><code>SetXLabelAngle()</code></li>
-  <li><code>SetYLabelAngle()</code></li>
-  <li><code>SetXLabelType()</code></li>
-  <li><code>SetYLabelType()</code></li>
-</ul>
-As we go further we will introduce some of these features of PHPlot.
-For more specialized examples, please go <a href="index.php">back to the index</a> and
-look in the examples section.
-</p>
-
-<p> <a href="#top">Back to top</a> </p>
-
-<h3><a name="multiple"></a> <b>Case 3: Multiple Graphs per Image </b></h3>
-
-<p>To create an image with several separate graphs
-on it is a straightforward process. As in the previous examples we
-first have to create an object (e.g. variable) but now we tell it to
-<i>not</i> print the image at the same time as the draw command. Now
-we want it to wait for the explicit <code>PrintImage</code> function call.
-To tell PHPlot this is the way we want to work, we use the
-<code>SetPrintImage</code> function.
-<code>SetPrintImage(TRUE)</code> is the default, and tells to draw the image
-when <code>DrawGraph</code> is called. To turn this
-off we use <code>SetPrintImage(FALSE)</code>.</p>
-<p>Now we will draw several images entirely within one object. That
-means that if we set a value for one graph, there will be a couple of
-other commands we will need.
-</p>
-<p>To specify in pixels the placement of each graph we use
-<code>SetNewPlotAreaPixels</code>. The format is
-<code>SetNewPlotAreaPixels(upper_left_x, upper_left_y, lower_right_x,
-lower_right_y)</code> . Again we are using the GD coordinates where 0,0
-is the upper left corner of the image.
-</p>
-<p>In more detail:</p>
-<div class="box">
-<pre>&lt;?php
-include('./PHPlot.php');  // here we include the PHPlot code
-$graph =& new PHPlot(400,250);   // here we define the variable $graph
-$graph-&gt;SetPrintImage(0); //Don't draw the image yet
-
-//....Data and Values for first graph here .....
-
-$graph-&gt;SetNewPlotAreaPixels(70,10,375,100);  // where to place it
-
-$graph-&gt;DrawGraph();   //Draw the first graph to the image.
-
-//....Data and Values for second graph here .....
-
-$graph-&gt;SetNewPlotAreaPixels(70,120,375,220); //where to place graph 2
-$graph-&gt;DrawGraph();  //Draw the second graph to the image
-
-//Print the image with both graphs
-$graph-&gt;PrintImage();
-?&gt;</pre>
-</div>
-
-<p>Lets now create an image with 2 graphs on it with some example data.  </p>
-
-<div class="box">
-<pre>&lt;?php
-//Include the code
-include('./phplot.php');
-
-//Define the object
-$graph =& new PHPlot(400,250);
-
-$graph-&gt;SetPrintImage(0); //Don't draw the image until specified explicitly
-
-$example_data = array(
-     array('a',3),
-     array('b',5),
-     array('c',7),
-     array('d',8),
-     array('e',2),
-     array('f',6),
-     array('g',7)
-);
-
-$graph-&gt;SetDataType("text-data");  //Must be called before SetDataValues
-
-$graph-&gt;SetDataValues($example_data);
-$graph-&gt;SetYTickIncrement(2);  //a smaller graph now - so we set a new tick increment
-
-$graph-&gt;SetXLabelAngle(90);
-$graph-&gt;SetXTitle("");
-$graph-&gt;SetYTitle("Price");
-$graph-&gt;SetPlotType("lines");
-$graph-&gt;SetLineWidth(1);
-
-$graph-&gt;SetNewPlotAreaPixels(70,10,375,100);  // where do we want the graph to go
-$graph-&gt;DrawGraph(); // remember, since we said not to draw yet, PHPlot
-                     // still needs a <i>PrintImage </i>command to write an image.
-
-
-//Now do the second chart on the same image
-unset($example_data);  //we are re-using $example_data (to save memory), but you don't have to
-$example_data = array(
-     array('a',30,40,20),
-     array('b',50,'',10),  // here we have a missing data point, that's ok
-     array('c',70,20,60),
-     array('d',80,10,40),
-     array('e',20,40,60),
-     array('f',60,40,50),
-     array('g',70,20,30)
-);
-
-$graph-&gt;SetDataType("text-data");  //Must be called before SetDataValues
-
-$graph-&gt;SetDataValues($example_data);
-
-$graph-&gt;SetXTitle("");
-$graph-&gt;SetYTitle("Verbal Cues");
-$graph-&gt;SetYTickIncrement(10);
-$graph-&gt;SetPlotType("bars");
-$graph-&gt;SetXLabelAngle(0);  //have to re-set as defined above
-
-$graph-&gt;SetNewPlotAreaPixels(70,120,375,220);
-$graph-&gt;SetPlotAreaWorld(0,0,7,80);
-$graph-&gt;DrawGraph();
-
-//Print the image
-$graph-&gt;PrintImage();
-?&gt;</pre>
-</div>
-
-<p> <br /> <br /> Which gives us: </p>
-
-<div align="center">
-  <img src="imgs/qstart_fig4.png" name="graphic3" align="top" border="0" /> <br />
-  Figure 4 <br />
-</div>
-
-<p>
-You must remember that world Coordinates are the XY coordinates relative to the
-axis origin that can be drawn. Not the device (pixel) coordinates
-which in GD are relative to the origin at the upper left
-side of the image.
-</p>
-
-<div style="background:#6699cc; color:#ffffff;">
-  <a href="#top">Back to top</a> <br />
-  <a href="index.php">Back to the index</a> <br />
-</div>
-<div class="foot">$Id$</div>
-
-</body>
-</html>
diff --git a/gui/bacula-web/external_packages/phplot/doc/schema.html b/gui/bacula-web/external_packages/phplot/doc/schema.html
deleted file mode 100644 (file)
index 458ede2..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
-<html>
-<head>
-    <title>PHPlot schema</title>
-    <link type="text/css" rel="stylesheet" href="style.css" />
-</head>
-<body>
-
-<div class="nav"> [ <a href="../doc/index.html">Go to the index</a> ] </div>
-
-<h2>The Drawing</h2>
-<p>
-The drawing you can see here is an attempt to illustrate the positioning
-of elements on the canvas of PHPlot graphs. I'm not specially good at it
-so please don't complain. I (<a href="mailto:nonick AT 8027 DOT org">Miguel</a>)
-hope it will help somebody as it did with me when I first began browsing
-through the 3000 lines of code Afan had written.
-</p>
-<p>If you are curious, for the drawing I used 
-<a href="http://www.lysator.liu.se/~alla/dia/">Dia</a> a gtk+ diagram creation
-sofware which I find easy and suited for my purposes.
-<center><img src="imgs/phplot-dia.png" alt="schema" /></center>
-</p>
-
-<h2>The words</h2>
-<p> Creating a graph with PHPlot is easy, you just create an instance of
-the class, set up a few options, push in the data and smile at your
-new cute graphic. However here we'll proceed a little more step by step so
-to show you how to get the most of PHPlot. Please go to <a href="quickstart.html">
-quickstart</a>.
-</p>
-
-<p class="foot">$Id$</p>
-</body>
-</html>
diff --git a/gui/bacula-web/external_packages/phplot/doc/style.css b/gui/bacula-web/external_packages/phplot/doc/style.css
deleted file mode 100644 (file)
index ff700e0..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/* $Id$
- * Style sheet for PHPlot 
- */
-
-   
-.foot { 
-    text-align:right; 
-    font-style:italic; 
-    font-size:smaller;
-    width:100%;
-    background:#CCCCCC;
-    color:#000000;
-    }
-
-    
-.nav {
-    text-align:center;
-    color:#DDDDDD;
-    background:#FFFFFF;
-    font-size:7px;
-    }
-.imgfoot { text-align:center; font-size:small; font-style:italic; }  
-.hdr { text-align:center; background: #DDDDDD; color:#000000; }
-.bighdr { text-align:center; font-weight: bold; }
-
-h2 { border-bottom: 1px dashed; } 
-h3 { border-bottom: 1px dotted; margin-left:1em;}
-
-img { padding: 0.5em; }
-
-.box { 
-    background-color: #cccccc; 
-    color: #000000; 
-    border:1px solid; 
-    width:75%; 
-    margin:2em;
-    font-size:smaller;
-    }
-    
-pre { font-family: monospace, "white-space" }
-        
diff --git a/gui/bacula-web/external_packages/phplot/doc/user_functions.html b/gui/bacula-web/external_packages/phplot/doc/user_functions.html
deleted file mode 100644 (file)
index 9b52901..0000000
+++ /dev/null
@@ -1,316 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<HTML><HEAD>
-<TITLE>PHPlot User Functions</TITLE>
-</HEAD>
-<BODY>
-
-<h2>PHPlot User Functions</h2>
-
-<ul>
-<li><a href="#important">Important functions</a></li>
-<li><a href="#appearance">Appearance functions</a></li>
-<li><a href="#color">Color functions</a></li>
-<li><a href="#manipulation">Data manipulation functions</a></li>
-</ul>
-
-
-<hr>
-
-<h3><a name="important">Important functions</a></h3>
-
-<p><DT><B>SetDataType($which_dt)</B>
-<DD>User Function: PHPLOT can accept data in a number of different
-formats. One main difference between PHPLOT and PGPLOT
-is that the x-y or x-y-error data should be passed in
-with the data grouped together in an array.
-<p>
-Asside: Why the author chose this data format: In PGPLOT
-you would have the X-values in one array and the Y-values
-in a second. This would sometimes lead to really strange
-graphs if one array was accidentally one data element
-short. Plus most of the data the author was using came in
-from a database using a _get_row or _get_array (in php)
-and one can just pass it straight in to PHPLOT. <p>
-
-Colors and border colors are set by <b>SetDataColors</b>. Why
-not have colors as part of the same array?  Because most
-data applications are used to putting out raw data and not
-raw data + color information. <p>
-
-So in PHPLOT the datalabel,x-value,y-value and error are
-grouped together as a <b>value array</b>. Then the
-entire set of points to be plotted is passed in as a <b>data array</b>
-E.g. Data_array = array(value_array_1,value_array_2,....) <p>
-See below for examples of the various data types: <br>
-
-<LI>text-data: Data is displayed at equal spacing along the x-axis. Data
-is passed in as a data array of value arrays. Each element of the data
-array is a position on the x-axis. The first element of the value array is
-the label for that x-axis position, every subsequent element of the value
-array is a y-value.
-Example data<br>
-<pre>
-$data = array(
-       array("label 1",1.1,2,3,4),
-       array("label 2",2,3,4,5),
-       array("label 3",5,6,7,8),
-       array("label 4",10,12,13,14)
-);
-</pre>
-Which will display data points at
-(1,1.1), (1,2), (1,3), (1,4), (2,2), (2,3)....<br>
-<IMG SRC="something.png">
-</LI>
-<LI>data-data:
-        X-Y data is passed in like text-data except
-that the second element of the value array is the position along the
-x axis of the data point(s). One data element would be $data[] = (title,x,y1,y2,y3,y4,...)
-or
-<pre>
-$data = array(
-       array("label 1",1.1,2,3,4),
-       array("label 2",2,3,4,5),
-       array("label 3",5,6,7,8),
-       array("label 4",10,12,13,14)
-);
-</pre>
-Notice that this is the same as in the previous example except that
-the  x,y data points are at (1.1,2), (1.1,3), (1.1,4), (2,3)...
-</LI>
-<LI>data-data-error:
-Again X-Y data is passed in as an array with each value
-array being (data_label,x_position,y_position,error_plus,error_minus)
-</LI>
-
-<p><DT><B>SetDataValues($which_dv)</B>
-<DD>User Function: Passes the raw data values into the class variable
-$this->data_values. This function <b>needs</b> to be called before any
-image can be drawn.
-
-<p><DT><B>SetPlotType($which_pt)</B>
-<DD>User Function: Can be:  bars, lines, linepoints, area, points, and pie
-
-<p><DT><B>SetErrorBarLineWidth($width)</B>
-<DD>Width of the Error Bars in Pixels. If not set then 
-uses "line_width" to set the width of the error_bar lines.
-
-<p><DT><B>SetFileFormat($which_file_format)</B>
-<DD>User Function: Set the format of the output graph. Supported formats
-are GIF, JPEG, and PNG.  You can only use those formats
-that are supported by your version of GD. For example,
-if you use GD-1.8.3 you can not use GIF images. If you
-use GD-1.2 you can not use PNG or JPEG images.
-
-<p><DT><B>SetUseTTF($which_ttf)</B>
-<DD>User Function: Call this as SetUseTTF(1) when you have TTF compiled into PHP
-otherwise call this as SetUseTTF(0)
-
-
-<hr>
-
-<h3><a name="appearance">Appearance functions</a></h3>
-
-
-<p><DT><B>SetErrorBarShape($which_ebs)</B>
-<DD>User Function: Can be "tee" or "line." If it is tee, then
-the half-width of the tee is set by <b>SetErrorBarSize</b>
-
-<p><DT><B>SetErrorBarSize($which_ebs)</B>
-<DD>User Function: Size in pixels of the Tee shape of the error bar.
-
-<p><DT><B>SetHorizTickIncrement($which_ti)</B>
-<DD>User Function: Set where to place the X-tick marks.
-
-<p><DT><B>SetNumHorizTicks($which_nt)</B>
-<DD>User Function: Use this or SetHorizTickIncrements but not both.
-
-<p><DT><B>SetNumVertTicks($which_nt)</B>
-<DD>User Function: Use this or SetVertTickIncrements but not both.
-
-<p><DT><B>SetPlotAreaPixels($x1,$y1,$x2,$y2)</B>
-<DD>User Function: You can use this to set the actual size in
-pixels of the plot area on the image.
-
-<p><DT><B>SetPointShape($which_pt)</B>
-<DD>User Function: Can be: rect,circle,diamond,triangle,dot,line,halfline
-
-<p><DT><B>SetPointSize($which_ps)</B>
-<DD>User Function: Set the width of the displayed data point for
-plot_type's that have data points. E.g. dots, linepoints
-This is an integer. For diamonds, values that are even
-make the diamond look better.
-
-<p><DT><B>SetPrecisionX($which_prec)</B>
-<DD>User Function: Set the precision of the data written to the X axis.
-$which_prec is an integer which represents the number of digits
-displayed to the right of the decimal. This uses the number_format
-command in PHP.
-
-<p><DT><B>SetPrecisionY($which_prec)</B>
-<DD>User Function: Set the precision of the data written to the Y axis.
-$which_prec is an integer which represents the number of digits
-displayed to the right of the decimal. This uses the number_format
-command in PHP.
-
-<p><DT><B>SetShading($which_s)</B>
-<DD>User Function: Set the length of the shadows for shading bar charts
-The color used is the LightGridColor See <b>SetLightGridColor</b>. <br>
-
-<p><DT><B>SetTickLength($which_tl)</B>
-<DD>User Function: Set the length of the tick in pixels for the x axis and y axis.
-$which_color can be either a name like "black" or an rgb color array
-array(int,int,int). <br>
-
-
-<p><DT><B>SetTitle($title)</B>
-<DD>User Function: Set the title of the graph. Printed at the top
-middle of the graph.
-
-<p><DT><B>SetHorizTickIncrement($which_ti)</B>
-<DD>User Function: Set the distance between tick marks on the X axis. $which_ti
-is in world coordinates.
-
-<p><DT><B>SetVertTickIncrement($which_ti)</B>
-<DD>User Function: Set the distance between tick marks on the Y axis. $which_ti
-is in world coordinates.
-
-<p><DT><b>SetXDatalabelMaxlength($which_xdlm)</b>
-<DD>User Function: Sets the maximum length of the datalabels on the x-axis. 
-This adjusts the margins if the angle of the labels is not 0. 
-
-<p>
-
-<p><DT><b>SetXGridLabelType($which_xtf)</b>
-<DD>User Function: Can be "time", "title", "none", "default" or "data". <br>
-time - label is set by the the php command strftime()<br>
-title - label is treated as text, the first element of the data array.<br>
-data - label is formateed using php command number_format<br>
-none - no labelss are printed.<br>
-default - prints as it is entered. <br>
-<p>
-
-
-<p><DT><b>SetXScaleType($which_xct)</b>
-<DD>Can be "log" or "linear".
-
-<p><DT><b>SetXTimeFormat($which_xtf)</b>
-<DD>User Function: Used for x_grid_label_type="time".
-For the format see the php manual for strftime().  It assumes the x-data values are unix timestamps, and displays them according to the passed format string.
-<p>Example: If x values are seconds offset from the beginning of the day.  This coresponds to unix timestamps on January 1, 1970, so all I had to do was $graph->SetXGridLabelType("%H:%M") to display the time properly.
- <p>
-The php strtotime() function also comes in handy turning dates into timestamps, especially for parameters to SetPlotAreaWorld().
-<p>
-Example:<br>
-$graph->SetPlotAreaWorld(strtotime("October 1"), 0,strtotime("December 15"),10);<br>
-$graph->SetXGridLabelType("time") ;<br>
-$graph->SetXTimeFormat("%b %d") ;<br>
-
-<p><DT><B>SetXLabel($xlbl)</B>
-<DD>User Function: Set the label for the X axis.
-
-<p><DT><B>SetYLabel($ylbl)</B>
-<DD>User Function: Set the label for the Y axis.
-
-<p><DT><b>SetYScaleType($which_xct)</b>
-<DD>Can be "log" or "linear".
-
-
-<hr>
-
-<h3><a name="color">Color functions</a></h3>
-
-<p><DT><B>SetBackgroundColor($which_color)</B>
-<DD>User Function: Set the color of the background of the entire image.
-$which_color can be either a name like "black" or an rgb color array
-array(int,int,int). <br> It defaults to array(222,222,222) if
-not defined.<br>
-
-<p><DT><B>SetGridColor ($which_color)</B>
-<DD>User Function: Set the color of the grid. Defaults to "black"
-$which_color can be either a name like "black" or an rgb color array
-array(int,int,int). <br>
-
-<p><DT><B>SetLegend($which_legend)</B>
-<DD>$which_legend is an array of text for display in a small box
-on the image. If you do not set the position with <b>SetLegendPixels()</b>
-then it puts it in the upper right hand side. 
-
-<p><DT><b>SetLegendPixels($which_x,$which_y,$which_type)</b>
-<DD>Pick the upper left corner of the legend box with
-$which_x and $which_y in pixels. $which_type is reserved
-for future use. 
-
-<p><DT><b>SetLegendWorld($which_x,$which_y,$which_type)</b>
-<DD>Untested and documentation not written. Have Fun!
-
-<p><DT><B>SetLightGridColor ($which_color)</B>
-<DD>User Function: There are two grid colors, this sets the
-light grid color.
-$which_color can be either a name like "black" or an rgb color array
-array(int,int,int). <br> It defaults to array(222,222,222) if
-not defined.<br>
-
-<p><DT><B>SetLineWidth($which_lt)</B>
-<DD>User Function: Set the width of lines in pixels for graphs that use
-lines (linepoints and lines). Affects the size of the
-error bar lines as well.
-
-<p><DT><B>SetLineStyles($which_sls)</B>
-<DD>User Function: Set style of the line to be printed.
-This is an array. Currently only 'dashed' and 'solid' are
-supported.
-
-<p><DT><B>SetPlotBgColor($which_color)</B>
-<DD>User Function: Set the Background color of the area on which
-the plot is defined. Called from <b>PlotAreaBackground</b><br>
-$which_color can be either a name like "black" or an rgb color array
-array(int,int,int). <br> It defaults to array(222,222,222) if
-not defined.
-
-<p><DT><B>SetTextColor ($which_color)</B>
-<DD>User Function: Set the color of text written. It defaults to black if not called.
-You can call this function multiple times - each time it changes the
-color of text written.
-$which_color can be either a name like "black" or an rgb color array
-array(int,int,int). <br>
-
-<p><DT><B>SetTickColor ($which_color)</B>
-<DD>User Function: Set the color of the ticks on the axes
-$which_color can be either a name like "black" or an rgb color array
-array(int,int,int). <br>
-
-<p><DT><B>SetTitleColor($which_color)</B>
-<DD>User Function: Set the color of the title.
-$which_color can be either a name like "black" or an rgb color array
-array(int,int,int). <br>
-
-<hr>
-
-<h3><a name="manipulation">Data manipulation functions</a></h3>
-
-<p>These functions only are availabe in the class PHPlot_data that extends PHPlot.
-Since all functions of that section do calculations on the data, it is neccessary
-that data is supplied to the class by the SetDataValues() function before calling
-any of the functions.</p>
-
-<p><DT><B>DoScaleData($even, $show_in_legend)</B>
-<DD>User Function: Scales the data so that graphs with widely different amplitudes
-can be plotted into one image. If $show_in_legend is true, the amplification factor
-that is applied to each for the datasets is appended to the legend of the graph. If
-$even is true, the function multiplies only by 10^x,2*10^x or 5*10^x.<br>
-
-<p><DT><B>DoMovingAverage($datarow, $interval, $show_in_legend)</B>
-<DD>User Function: Computes a moving average over an amount of $interval units
-on the data row that is indexed by $datarow. If $show_in_legend is true, a notice
-that a data row is averaged and the length of the interval are appended to the legend.
-<br>
-
-<hr>
-
-<h3>not implemented</h3>
-
-<p><DT><B>SetCharacterHeight()</B>
-<DD>User Function: Not yet implemented
-
-</body>
-</html>
diff --git a/gui/bacula-web/external_packages/phplot/doc/user_internal_functions.html b/gui/bacula-web/external_packages/phplot/doc/user_internal_functions.html
deleted file mode 100644 (file)
index bb23463..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<HTML><HEAD>
-<TITLE>PHPlot User / Internal Functions</TITLE>
-</HEAD>
-<BODY>
-
-<h2>PHPlot User / Internal Functions</h2>
-
-
-<p><DT><B>DrawDataLabel($lab,$x_world,$y_world)</B>
-<DD>User Function and Internal Function: Draw a label at the world coordinates of x_world and y_world.
-
-
-<p><DT><B>DrawDot($x_world,$y_world,$dot_type,$color)</B>
-<DD>User Function and Internal Function: Draw one dot at $x_world and $y_world where the world
-coordinates (not pixel coordinates).
-
-
-<p><DT><B>DrawXDataLabel($xlab,$xpos)</B>
-<DD>User Function and Internal Function: Draw a data label on the x axis at world
-position $xpos. The position below
-the x axis is set based on the size of the font, and (if
-using TTF) the maximum string length of the labels, and
-the angle at which the labels are printed.
-
-
-<p><DT><B>SetDataColors($which_data,$which_border)</B>
-<DD> If called with $which_data="" it defaults to
-           $which_data = array("blue","bisque",array(0,176,0));
-            $which_border = array("black");
-
-
-<p><DT><B>SetErrorBarColors($which_data)</B>
-<DD> If not called then the colors are the same as the 
-colors of the data as set by <b>SetDataColors()</b>
-otherwise it sets the colors of the error bars. 
-
-<p><DT><B>SetLabels($xlbl,$ylbl,$title)</B>
-<DD>Internal Function and User Function: Set the text of the X, Y and Title labels.
-If you want to just set the X-axis label you can use <b>SetXLabel</b>.
-
-
-<p><DT><B>SetNewPlotAreaPixels($x1,$y1,$x2,$y2)</B>
-<DD>Reserved - not used yet.
-
-
-
-<p><DT><B>SetPlotAreaWorld($xmin,$ymin,$xmax,$ymax)</B>
-<DD>User Function and Internal Function: You do not have to call this function as the
-program will set the plot area automatically based on
-max values of X and Y. <br> You can call this function
-as SetPlotAreaWorld("","","","") and it will set the
-plot area based on max/min values of data.
-
-
-
-
-
-</dl>
-
-</body>
-</html>
diff --git a/gui/bacula-web/external_packages/phplot/examples/benjamingothic.ttf b/gui/bacula-web/external_packages/phplot/examples/benjamingothic.ttf
deleted file mode 100644 (file)
index 61058a6..0000000
Binary files a/gui/bacula-web/external_packages/phplot/examples/benjamingothic.ttf and /dev/null differ
diff --git a/gui/bacula-web/external_packages/phplot/examples/create_chart.php b/gui/bacula-web/external_packages/phplot/examples/create_chart.php
deleted file mode 100644 (file)
index 37e43cb..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-<?php    
-/* $Id$ */
-
-error_reporting(E_ALL);
-
-/*
-// Check if we are processing the form
-if (! array_key_exists('submit', $_POST)) {
-    echo "<p>This file is intended to be called from format_chart.php, the example-o-matic.".
-         "Please <a href=\"format_chart.php\">click here</a> to try it.</p>";
-    exit;
-}    
-*/
-
-extract ($_GET, EXTR_OVERWRITE);
-extract ($_POST, EXTR_OVERWRITE);
-
-
-//Sample functions
-
-// data-data as a function
-if ($which_data_type == 'function') { 
-       //Put function here
-       $dx = ".3";
-       $max = 6.4;
-       $maxi = $max/$dx;
-       for ($i=0; $i<$maxi; $i++) {
-               $a = 4;
-               $x = $dx*$i;
-               $data[$i] = array("", $x, $a*sin($x),$a*cos($x),$a*cos($x+1));  
-       }
-       $which_data_type = "data-data";
-}
-// data-data-error as a random function
-else if ($which_data_type == 'randfunction') {
-    srand ((double) microtime() * 1000000);
-    $a = 9.62;
-    $label[0] = "October"; $label[5] = "Day 5"; $label[10] = "Day 10";
-    $label[15] = "Day 15"; $label[20] = "Day 20"; $label[25] = "Day 25";
-    
-    for ($i = 0; $i <= 30; $i++) {
-        $a += rand(-1, 2);
-        $b = rand(0,1);
-        $c = rand(0,1);
-        $data[] = @ array($label[$i],$i+1,$a,$b,$c);
-    }
-    $which_data_type = 'data-data-error';
-}
-// MBD, this is for data_sample3.php, $num_data_rows is set there
-else if ($which_data_type == 'data-data-error') {
-    for ($i = 0; $i < $num_data_rows; $i++) {
-        eval ("\$data[\$i] = \$data_row$i; ");
-    }      
-} else { 
-    foreach($data_row0 as $key=>$val) {
-               $data[$key] = array($data_row0[$key],$data_row1[$key],$data_row2[$key],$data_row3[$key],$data_row4[$key]);
-       }
-}
-
-
-////////////////////////////////////////////////
-
-//Required Settings 
-       include("../phplot.php");
-       $graph = new PHPlot($xsize_in, $ysize_in);
-       $graph->SetDataType($which_data_type);  // Must be first thing
-    
-       $graph->SetDataValues($data);
-
-//Optional Settings (Don't need them) 
-
-//     $graph->SetTitle("This is a\n\rmultiple line title\n\rspanning three lines.");
-    $graph->SetTitle($title);
-       $graph->SetXTitle($xlbl, $which_xtitle_pos);
-       $graph->SetYTitle($ylbl, $which_ytitle_pos);
-       $graph->SetLegend(array("A","Bee","Cee","Dee"));
-    
-       $graph->SetFileFormat($which_fileformat);
-       $graph->SetPlotType($which_plot_type);
-    
-       $graph->SetUseTTF($which_use_ttf);
-    
-       $graph->SetYTickIncrement($which_yti);
-       $graph->SetXTickIncrement($which_xti);
-    $graph->SetXTickLength($which_xtl);
-    $graph->SetYTickLength($which_ytl);
-    $graph->SetXTickCrossing($which_xtc);
-    $graph->SetYTickCrossing($which_ytc);
-    $graph->SetXTickPos($which_xtick_pos);
-    $graph->SetYTickPos($which_ytick_pos);
-
-    
-    $graph->SetShading($which_shading);
-    $graph->SetLineWidth($which_line_width);
-    $graph->SetErrorBarLineWidth($which_errorbar_line_width);
-
-    $graph->SetDrawDashedGrid($which_dashed_grid);
-    switch($which_draw_grid) {
-    case 'x':
-        $graph->SetDrawXGrid(TRUE);
-        $graph->SetDrawYGrid(FALSE);
-        break;
-    case 'y':
-        $graph->SetDrawXGrid(FALSE);
-        $graph->SetDrawYGrid(TRUE);
-        break;
-    case 'both':
-        $graph->SetDrawXGrid(TRUE);
-        $graph->SetDrawYGrid(TRUE);
-        break;
-    case 'none':
-        $graph->SetDrawXGrid(FALSE);
-        $graph->SetDrawYGrid(FALSE);
-    }    
-    
-    $graph->SetXTickLabelPos($which_xtick_label_pos);
-    $graph->SetYTickLabelPos($which_ytick_label_pos);
-    $graph->SetXDataLabelPos($which_xdata_label_pos);
-    $graph->SetYDataLabelPos($which_ydata_label_pos);
-    
-    // Please remember that angles other than 90 are taken as 0 when working fith fixed fonts.
-    $graph->SetXLabelAngle($which_xlabel_angle);
-    $graph->SetYLabelAngle($which_ylabel_angle);
-
-       //$graph->SetLineStyles(array("dashed","dashed","solid","solid"));
-       $graph->SetPointShape($which_point);
-    $graph->SetPointSize($which_point_size);
-    $graph->SetDrawBrokenLines($which_broken);
-
-    // Some forms in format_chart.php don't set this variable, suppress errors.
-       @ $graph->SetErrorBarShape($which_error_type);
-    
-       $graph->SetXAxisPosition($which_xap);
-    $graph->SetYAxisPosition($which_yap);
-    $graph->SetPlotBorderType($which_btype);
-    
-       if ($maxy_in) { 
-               if ($which_data_type = "text-data") { 
-                       $graph->SetPlotAreaWorld(0,$miny_in,count($data),$maxy_in);
-               }
-       }
-
-/*
-//Even more settings
-
-       $graph->SetPlotAreaWorld(0,100,5.5,1000);
-       $graph->SetPlotAreaWorld(0,-10,6,35);
-       $graph->SetPlotAreaPixels(150,50,600,400);
-
-    $graph->SetDataColors(
-               array("blue","green","yellow","red"),  //Data Colors
-            array("black")                                                     //Border Colors
-    );  
-
-    $graph->SetPlotBgColor(array(222,222,222));
-    $graph->SetBackgroundColor(array(200,222,222)); //can use rgb values or "name" values
-    $graph->SetTextColor("black");
-    $graph->SetGridColor("black");
-    $graph->SetLightGridColor(array(175,175,175));
-    $graph->SetTickColor("black");
-    $graph->SetTitleColor(array(0,0,0)); // Can be array or name
-*/
-
-//      $graph->SetPrintImage(false);
-      $graph->DrawGraph();
-//      xdebug_dump_function_profile(XDEBUG_PROFILER_FS_SUM);
-?>
diff --git a/gui/bacula-web/external_packages/phplot/examples/data.php b/gui/bacula-web/external_packages/phplot/examples/data.php
deleted file mode 100644 (file)
index b1d1314..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-$example_data = array(
-       array("__A__",0.0,20,4,5,6),
-       array("__B__",2.0,30,5,6,7),
-       array("__C__",3.0,40,5,7,8),
-       array("__D__",4.0,50,3,6,3),
-       array("__E__",4.4,40,3,6,5),
-       array("__F__",5.4,40,5,6,5),
-       array("__G__",5.5,40,7,6,5),
-       array("__H__",7,35,0.0,0.0,""),
-       array("__I__",7.4,40,14,16,25),
-       array("__J__",7.6,40,6,6,5),
-       array("__K__",8.2,40,3,6,5),
-       array("__L__",8.5,40,8,6,9),
-       array("__M__",9.3,40,5,6,5),
-       array("__N__",9.6,40,9,6,7),
-       array("__O__",9.9,40,2,6,5),
-       array("__P__",10.0,40,3,6,8),
-       array("__Q__",10.4,40,3,6,5),
-       array("__R__",10.5,40,3,6,5),
-       array("__S__",10.8,40,3,6,5),
-       array("__T__",11.4,40,3,6,5),
-       array("__U__",12.0,40,3,7,5),
-       array("__V__",13.4,40,3,5,3),
-       array("__W__",14.0,30,3,5,6)
-);
-?>
diff --git a/gui/bacula-web/external_packages/phplot/examples/data_date.php b/gui/bacula-web/external_packages/phplot/examples/data_date.php
deleted file mode 100644 (file)
index 4a14d7b..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-<?php
-//Title,unixtime_date,value
-
-$example_data = array(
-       array("Jan",883634400,10),
-       array("",883720800,20),
-       array("",883807200,20),
-       array("",883893600,22),
-       array("",883980000,33),
-       array("",884239200,30),
-       array("",884325600,20),
-       array("",884412000,10),
-       array("",884498400,11),
-       array("",884584800,42),
-       array("",884844000,21),
-       array("",884930400,42),
-       array("",885016800,43),
-       array("",885103200,24),
-       array("",885189600,55),
-       array("",885448800,28),
-       array("",885535200,39),
-       array("",885621600,20),
-       array("",885708000,41),
-       array("",885967200,34),
-       array("",886053600,45),
-       array("",886140000,56),
-       array("",886226400,27),
-       array("Feb",886312800,28),
-       array("",886572000,31),
-       array("",886658400,32),
-       array("",886744800,33),
-       array("",886831200,34),
-       array("",886917600,35),
-       array("",887176800,38),
-       array("",887263200,39),
-       array("",887349600,40),
-       array("",887436000,41),
-       array("",887522400,42),
-       array("",887781600,45),
-       array("",887868000,46),
-       array("",887954400,47),
-       array("",888040800,48),
-       array("",888127200,49),
-       array("",888386400,52),
-       array("",888472800,53),
-       array("",888559200,54),
-       array("",888645600,55),
-       array("Mar",888732000,56),
-       array("",888991200,59),
-       array("",889077600,60),
-       array("",889164000,61),
-       array("",889250400,62),
-       array("",889336800,63),
-       array("",889596000,66),
-       array("",889682400,67),
-       array("",889768800,68),
-       array("",889855200,69),
-       array("",889941600,70),
-       array("",890200800,33),
-       array("",890287200,34),
-       array("",890373600,35),
-       array("",890460000,36),
-       array("",890546400,37),
-       array("",890805600,30),
-       array("",890892000,31),
-       array("",890978400,32),
-       array("",891064800,33),
-       array("",891151200,34),
-       array("",891410400,37),
-       array("",891496800,38),
-       array("",891583200,10),
-       array("",891669600,12),
-       array("",891756000,14),
-       array("",892015200,20),
-       array("",892101600,22),
-       array("",892188000,24),
-       array("",892274400,26),
-       array("",892360800,28),
-       array("",892620000,34),
-       array("",892706400,36),
-       array("",892792800,38),
-       array("",892879200,40),
-       array("",892965600,42),
-       array("",893224800,48),
-       array("",893311200,50),
-       array("",893397600,52),
-       array("",893484000,54),
-       array("",893570400,56),
-       array("",893829600,62),
-       array("",893916000,64),
-       array("",894002400,66),
-       array("",894088800,68),
-       array("",894175200,60),
-       array("",894434400,66),
-       array("",894520800,68),
-       array("",894607200,60),
-       array("",894693600,62),
-       array("",894780000,64),
-       array("",895039200,60),
-       array("",895125600,62),
-       array("",895212000,64),
-       array("",895298400,66),
-       array("",895384800,68),
-       array("",895644000,10),
-       array("",895730400,06),
-       array("",895816800,08),
-       array("",895903200,11),
-       array("",895989600,11),
-       array("",896248800,18),
-       array("",896335200,20),
-       array("",896421600,12),
-       array("",896508000,12),
-       array("",896594400,10),
-       array("",896853600,13),
-       array("",896940000,14),
-       array("",897026400,15),
-       array("",897112800,16),
-       array("",897199200,17),
-       array("",897458400,20),
-       array("",897544800,21),
-       array("",897631200,22),
-       array("",897717600,23),
-       array("",897804000,24),
-       array("",898063200,27),
-       array("",898149600,28),
-       array("",898236000,29),
-       array("",898322400,30),
-       array("",898408800,31),
-       array("",898668000,34),
-       array("",898754400,35),
-       array("",898840800,36),
-       array("",898927200,37),
-       array("",899013600,38),
-       array("",899272800,41),
-       array("",899359200,42),
-       array("",899445600,43),
-       array("",899532000,44),
-       array("",899618400,45),
-       array("",899877600,48),
-       array("",899964000,49),
-       array("",900050400,50),
-       array("",900136800,21),
-       array("",900223200,62),
-       array("",900482400,35),
-       array("",900568800,76),
-       array("",900655200,37),
-       array("",900741600,78),
-       array("",900828000,49),
-       array("",901087200,82),
-       array("",901173600,23),
-       array("",901260000,44),
-       array("",901346400,55),
-       array("",901432800,26),
-       array("",901692000,49),
-       array("",901778400,10),
-       array("",901864800,51),
-       array("",901951200,52),
-       array("",902037600,53),
-       array("",902296800,46),
-       array("",902383200,47),
-       array("",902469600,68),
-       array("",902556000,69),
-       array("",902642400,40),
-       array("",902901600,73),
-       array("",902988000,84),
-       array("",903074400,85),
-       array("",903160800,86),
-       array("",903247200,87),
-       array("",903506400,40),
-       array("",903592800,21),
-       array("",903679200,52),
-       array("",903765600,83),
-       array("",903852000,24),
-       array("",904111200,27),
-       array("",904197600,68),
-       array("",904284000,49),
-       array("",904370400,10),
-       array("",904456800,01),
-       array("",904716000,14),
-       array("",904802400,10),
-       array("",904888800,16),
-       array("",904975200,07),
-       array("",905061600,18),
-       array("",905320800,12),
-       array("",905407200,14),
-       array("",905493600,16),
-       array("",905580000,18),
-       array("",905666400,20),
-       array("",905925600,26),
-       array("",906012000,28),
-       array("",906098400,30),
-       array("",906184800,32),
-       array("",906271200,34),
-       array("",906530400,40),
-       array("",906616800,42),
-       array("",906703200,44),
-       array("",906789600,46),
-       array("",906876000,48),
-       array("",907135200,54),
-       array("",907221600,56),
-       array("",907308000,58),
-       array("",907394400,60),
-       array("",907480800,62),
-       array("",907740000,68),
-       array("",907826400,70),
-       array("",907912800,72),
-       array("",907999200,74),
-       array("",908085600,76),
-       array("",908344800,32),
-       array("",908431200,44),
-       array("",908517600,46),
-       array("",908604000,28),
-       array("",908690400,60),
-       array("",908949600,36),
-       array("",909036000,58),
-       array("",909122400,70),
-       array("",909208800,22),
-       array("",909295200,54),
-       array("",909554400,30),
-       array("",909640800,52),
-       array("",909727200,34),
-       array("",909813600,56),
-       array("",909900000,38),
-       array("",909986400,60),
-       array("",910245600,42),
-       array("",910332000,53),
-       array("",910418400,54),
-       array("",910504800,35),
-       array("",910591200,46),
-       array("",910850400,49),
-       array("",910936800,40),
-       array("",911023200,32),
-       array("",911109600,22),
-       array("",911196000,23),
-       array("",911455200,26),
-       array("",911541600,27),
-       array("",911628000,28),
-       array("",911714400,29),
-       array("",911800800,30),
-       array("",912060000,33),
-       array("",912146400,34),
-       array("",912232800,35),
-       array("",912319200,36),
-       array("",912405600,37),
-       array("Dec",912664800,40),
-       array("",912751200,41),
-       array("",912837600,42),
-       array("",912924000,43),
-       array("",913010400,44),
-       array("",913269600,47),
-       array("",913356000,48),
-       array("",913442400,49),
-       array("",913528800,50),
-       array("",913615200,51),
-       array("",913874400,54),
-       array("",913960800,55),
-       array("",914047200,56),
-       array("",914133600,57),
-       array("",914220000,58),
-       array("",914479200,61),
-       array("",914565600,62),
-       array("",914652000,63),
-       array("",914738400,64),
-       array("",914824800,65),
-       array("",915084000,68)
-);
-?>
diff --git a/gui/bacula-web/external_packages/phplot/examples/data_date2.php b/gui/bacula-web/external_packages/phplot/examples/data_date2.php
deleted file mode 100644 (file)
index 37152c7..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-<?php
-//Title,unixtime_date,value,+error,-error
-
-$example_data = array(
-       array("Jan",883634400,4.62,0.462,0.462), 
-       array("",883720800,6.62,0.662,0), 
-       array("",883807200,5.62,0,0.562), 
-       array("",883893600,5.62,0.562,0), 
-       array("",883980000,4.62,0.462,0.462), 
-       array("",884239200,6.62,0.662,0), 
-       array("",884325600,8.62,0,0.862), 
-       array("",884412000,10.62,1.062,0), 
-       array("",884498400,11.62,1.162,0), 
-       array("",884584800,10.62,0,1.062), 
-       array("",884844000,10.62,0,0), 
-       array("",884930400,10.62,0,1.062), 
-       array("",885016800,10.62,0,1.062), 
-       array("",885103200,12.62,1.262,1.262), 
-       array("",885189600,12.62,1.262,1.262), 
-       array("",885448800,14.62,1.462,1.462), 
-       array("",885535200,14.62,1.462,1.462), 
-       array("",885621600,14.62,1.462,1.462), 
-       array("",885708000,13.62,1.362,0), 
-       array("",885967200,14.62,1.462,0), 
-       array("",886053600,15.62,1.562,0), 
-       array("",886140000,14.62,1.462,0), 
-       array("",886226400,16.62,1.662,0), 
-       array("Feb",886312800,15.62,1.562,0), 
-       array("",886572000,14.62,0,1.462), 
-       array("",886658400,13.62,1.362,0), 
-       array("",886744800,14.62,1.462,1.462), 
-       array("",886831200,13.62,1.362,0), 
-       array("",886917600,13.62,1.362,1.362), 
-       array("",887176800,14.62,0,1.462), 
-       array("",887263200,13.62,1.362,1.362), 
-       array("",887349600,13.62,0,1.362), 
-       array("",887436000,12.62,1.262,0), 
-       array("",887522400,12.62,1.262,1.262), 
-       array("",887781600,12.62,0,0), 
-       array("",887868000,11.62,1.162,1.162), 
-       array("",887954400,11.62,1.162,0), 
-       array("",888040800,12.62,0,1.262), 
-       array("",888127200,13.62,1.362,1.362), 
-       array("",888386400,15.62,1.562,1.562), 
-       array("",888472800,17.62,0,1.762), 
-       array("",888559200,18.62,0,1.862), 
-       array("",888645600,19.62,0,0), 
-       array("Mar",888732000,19.62,1.962,0), 
-       array("",888991200,18.62,0,0), 
-       array("",889077600,17.62,1.762,0), 
-       array("",889164000,16.62,1.662,0), 
-       array("",889250400,16.62,0,0), 
-       array("",889336800,17.62,0,0), 
-       array("",889596000,16.62,0,0), 
-       array("",889682400,16.62,1.662,0), 
-       array("",889768800,18.62,1.862,0), 
-       array("",889855200,19.62,0,1.962), 
-       array("",889941600,20.62,0,0), 
-       array("",890200800,20.62,2.062,0), 
-       array("",890287200,21.62,0,0), 
-       array("",890373600,23.62,0,2.362), 
-       array("",890460000,25.62,0,2.562), 
-       array("",890546400,26.62,2.662,2.662), 
-       array("",890805600,26.62,2.662,2.662), 
-       array("",890892000,25.62,0,0), 
-       array("",890978400,26.62,2.662,2.662), 
-       array("",891064800,27.62,2.762,0), 
-       array("",891151200,29.62,2.962,0), 
-       array("",891410400,30.62,0,3.062), 
-       array("",891496800,32.62,0,0), 
-       array("",891583200,33.62,3.362,0), 
-       array("",891669600,33.62,3.362,3.362), 
-       array("",891756000,33.62,3.362,0), 
-       array("",892015200,33.62,0,0), 
-       array("",892101600,34.62,0,0), 
-       array("",892188000,35.62,3.562,0), 
-       array("",892274400,36.62,3.662,3.662), 
-       array("",892360800,37.62,0,3.762), 
-       array("",892620000,36.62,3.662,0), 
-       array("",892706400,38.62,3.862,0), 
-       array("",892792800,37.62,3.762,0), 
-       array("",892879200,39.62,3.962,0), 
-       array("",892965600,39.62,3.962,0), 
-       array("",893224800,38.62,0,3.862), 
-       array("",893311200,40.62,4.062,0), 
-       array("",893397600,40.62,4.062,0), 
-       array("",893484000,41.62,0,0), 
-       array("",893570400,40.62,4.062,4.062), 
-       array("",893829600,39.62,0,0), 
-       array("",893916000,40.62,0,4.062), 
-       array("",894002400,40.62,0,4.062), 
-       array("",894088800,42.62,0,0), 
-       array("",894175200,44.62,0,0), 
-       array("",894434400,45.62,0,4.562), 
-       array("",894520800,44.62,4.462,4.462), 
-       array("",894607200,43.62,4.362,4.362), 
-       array("",894693600,45.62,0,0), 
-       array("",894780000,46.62,4.662,0), 
-       array("",895039200,48.62,0,0), 
-       array("",895125600,48.62,4.862,0), 
-       array("",895212000,50.62,0,0), 
-       array("",895298400,52.62,5.262,0), 
-       array("",895384800,54.62,0,5.462), 
-       array("",895644000,53.62,0,5.362), 
-       array("",895730400,54.62,5.462,0), 
-       array("",895816800,56.62,5.662,5.662), 
-       array("",895903200,56.62,0,5.662), 
-       array("",895989600,55.62,5.562,5.562), 
-       array("",896248800,54.62,0,5.462), 
-       array("",896335200,53.62,5.362,5.362), 
-       array("",896421600,54.62,5.462,5.462), 
-       array("",896508000,54.62,0,5.462), 
-       array("",896594400,54.62,0,5.462), 
-       array("",896853600,55.62,5.562,5.562), 
-       array("",896940000,56.62,0,5.662), 
-       array("",897026400,57.62,5.762,0), 
-       array("",897112800,57.62,0,5.762), 
-       array("",897199200,59.62,5.962,0), 
-       array("",897458400,61.62,6.162,0), 
-       array("",897544800,63.62,6.362,0), 
-       array("",897631200,64.62,0,6.462), 
-       array("",897717600,63.62,0,6.362), 
-       array("",897804000,63.62,0,6.362), 
-       array("",898063200,63.62,6.362,6.362), 
-       array("",898149600,63.62,6.362,6.362), 
-       array("",898236000,63.62,0,0), 
-       array("",898322400,65.62,6.562,0), 
-       array("",898408800,67.62,6.762,0), 
-       array("",898668000,68.62,6.862,6.862), 
-       array("",898754400,69.62,0,6.962), 
-       array("",898840800,68.62,6.862,6.862), 
-       array("",898927200,68.62,6.862,6.862), 
-       array("",899013600,69.62,6.962,6.962), 
-       array("",899272800,69.62,0,6.962), 
-       array("",899359200,71.62,0,0), 
-       array("",899445600,71.62,7.162,7.162), 
-       array("",899532000,72.62,0,7.262), 
-       array("",899618400,72.62,0,7.262), 
-       array("",899877600,71.62,7.162,7.162), 
-       array("",899964000,72.62,7.262,0), 
-       array("",900050400,74.62,0,7.462), 
-       array("",900136800,74.62,0,7.462), 
-       array("",900223200,75.62,0,7.562), 
-       array("",900482400,77.62,0,7.762), 
-       array("",900568800,79.62,0,7.962), 
-       array("",900655200,78.62,7.862,0), 
-       array("",900741600,78.62,0,0), 
-       array("",900828000,77.62,0,0), 
-       array("",901087200,78.62,0,7.862), 
-       array("",901173600,77.62,7.762,7.762), 
-       array("",901260000,79.62,0,7.962), 
-       array("",901346400,80.62,0,8.062), 
-       array("",901432800,80.62,0,0), 
-       array("",901692000,79.62,7.962,0), 
-       array("",901778400,81.62,8.162,8.162), 
-       array("",901864800,80.62,8.062,0), 
-       array("",901951200,79.62,0,7.962), 
-       array("",902037600,80.62,0,8.062), 
-       array("",902296800,82.62,0,0), 
-       array("",902383200,83.62,8.362,8.362), 
-       array("",902469600,85.62,0,8.562), 
-       array("",902556000,84.62,0,0), 
-       array("",902642400,85.62,0,0), 
-       array("",902901600,85.62,8.562,8.562), 
-       array("",902988000,87.62,0,0), 
-       array("",903074400,88.62,8.862,0), 
-       array("",903160800,87.62,8.762,8.762), 
-       array("",903247200,88.62,8.862,8.862), 
-       array("",903506400,88.62,8.862,0), 
-       array("",903592800,88.62,0,0), 
-       array("",903679200,87.62,0,0), 
-       array("",903765600,87.62,8.762,8.762), 
-       array("",903852000,88.62,8.862,0), 
-       array("",904111200,87.62,8.762,8.762), 
-       array("",904197600,88.62,0,0), 
-       array("",904284000,89.62,8.962,8.962), 
-       array("",904370400,88.62,0,8.862), 
-       array("",904456800,89.62,8.962,8.962), 
-       array("",904716000,90.62,0,0), 
-       array("",904802400,92.62,9.262,9.262), 
-       array("",904888800,92.62,0,9.262), 
-       array("",904975200,94.62,9.462,0), 
-       array("",905061600,95.62,9.562,0), 
-       array("",905320800,94.62,9.462,9.462), 
-       array("",905407200,93.62,0,9.362), 
-       array("",905493600,95.62,9.562,9.562), 
-       array("",905580000,95.62,0,0), 
-       array("",905666400,94.62,0,9.462), 
-       array("",905925600,96.62,9.662,9.662), 
-       array("",906012000,98.62,9.862,0), 
-       array("",906098400,100.62,0,0), 
-       array("",906184800,102.62,0,0), 
-       array("",906271200,102.62,10.262,10.262), 
-       array("",906530400,102.62,0,0), 
-       array("",906616800,101.62,0,0), 
-       array("",906703200,101.62,10.162,10.162), 
-       array("",906789600,103.62,0,10.362), 
-       array("",906876000,102.62,10.262,0), 
-       array("",907135200,103.62,10.362,10.362), 
-       array("",907221600,103.62,10.362,0), 
-       array("",907308000,103.62,0,10.362), 
-       array("",907394400,104.62,0,0), 
-       array("",907480800,104.62,0,10.462), 
-       array("",907740000,103.62,0,0), 
-       array("",907826400,105.62,0,10.562), 
-       array("",907912800,104.62,0,0), 
-       array("",907999200,104.62,0,10.462), 
-       array("",908085600,103.62,0,0), 
-       array("",908344800,102.62,10.262,0), 
-       array("",908431200,102.62,0,10.262), 
-       array("",908517600,104.62,0,10.462), 
-       array("",908604000,104.62,0,0), 
-       array("",908690400,106.62,10.662,0), 
-       array("",908949600,107.62,0,10.762), 
-       array("",909036000,109.62,10.962,0), 
-       array("",909122400,109.62,10.962,10.962), 
-       array("",909208800,110.62,11.062,0), 
-       array("",909295200,109.62,0,10.962), 
-       array("",909554400,111.62,0,11.162), 
-       array("",909640800,111.62,11.162,0), 
-       array("",909727200,113.62,11.362,11.362), 
-       array("",909813600,115.62,0,11.562), 
-       array("",909900000,117.62,11.762,11.762), 
-       array("",909986400,117.62,0,11.762), 
-       array("",910245600,116.62,11.662,11.662), 
-       array("",910332000,118.62,11.862,11.862), 
-       array("",910418400,120.62,0,12.062), 
-       array("",910504800,121.62,12.162,0), 
-       array("",910591200,120.62,12.062,0), 
-       array("",910850400,122.62,12.262,0), 
-       array("",910936800,122.62,0,0), 
-       array("",911023200,123.62,0,0), 
-       array("",911109600,124.62,0,12.462), 
-       array("",911196000,125.62,12.562,0), 
-       array("",911455200,124.62,12.462,0), 
-       array("",911541600,123.62,12.362,0), 
-       array("",911628000,124.62,12.462,12.462), 
-       array("",911714400,126.62,0,0), 
-       array("",911800800,126.62,0,0), 
-       array("",912060000,127.62,0,12.762), 
-       array("",912146400,126.62,12.662,12.662), 
-       array("",912232800,127.62,0,12.762), 
-       array("",912319200,126.62,0,0), 
-       array("",912405600,125.62,0,12.562), 
-       array("Dec",912664800,126.62,0,0), 
-       array("",912751200,108.62,0,12.862), 
-       array("",912837600,108.62,0,12.862), 
-       array("",912924000,109.62,12.962,12.962), 
-       array("",913010400,100.62,0,0), 
-       array("",913269600,109.62,0,0), 
-       array("",913356000,108.62,0,0), 
-       array("",913442400,108.62,0,0), 
-       array("",913528800,108.62,0,12.862), 
-       array("",913615200,100.62,13.062,0), 
-       array("",913874400,109.62,12.962,0), 
-       array("",913960800,108.62,12.862,0), 
-       array("",914047200,108.62,12.862,12.862), 
-       array("",914133600,107.62,0,12.762), 
-       array("",914220000,108.62,0,12.862), 
-       array("",914479200,107.62,0,0), 
-       array("",914565600,107.62,12.762,12.762), 
-       array("",914652000,109.62,12.962,12.962), 
-       array("",914738400,101.62,0,0), 
-       array("",914824800,100.62,0,0), 
-       array("",915084000,109.62,0,12.962)
-);
-?>
diff --git a/gui/bacula-web/external_packages/phplot/examples/data_sample1.php b/gui/bacula-web/external_packages/phplot/examples/data_sample1.php
deleted file mode 100644 (file)
index 11b8117..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<p>
-Data type: (Text-data)<br />
-</p>
-<input type="hidden" name="which_data_type" value="text-data" />
-<table border=1>
- <tr>
-  <td>Title (x axis)</td><td>Y data 1</td><td>Y data 2</td>
-  <td>Y data 3</td> <td>Y data 4</td>
- </tr>
-<?php 
-       srand ((double) microtime() * 12341234);
-       $a = 25;
-       $b = 10;
-       $c = -5;
-       for ($i=0; $i<5; $i++) {
-               $a += rand(-2, 2);
-               $b += rand(-5, 5);
-               $c += rand(-2, 2);
-
-?>
- <tr>
-  <td>
-   <input type="text" name="data_row0[<?php echo $i?>]" value="Year <?php echo $i?>" />
-  </td><td>
-   <input type="text" name="data_row1[<?php echo $i?>]" value="<?php echo $a?>" size="3" />
-  </td><td>
-   <input type="text" name="data_row2[<?php echo $i?>]" value="<?php echo $b?>" size="3" />
-  </td><td>
-   <input type="text" name="data_row3[<?php echo $i?>]" value="<?php echo $c?>" size="3" />
-  </td><td>
-   <input type="text" name="data_row4[<?php echo $i?>]" value="<?php echo $c+1?>" size="3" />
-  </td>
- </tr>
-<?php 
-       }
-?>
-
-</table>
-
-<p>
-Graph type:
-<select name="which_plot_type">
-  <option value="bars">Bars (*)</option>
-  <option value="thinbarline">Thin bars</option>
-  <option value="lines">Lines</option>
-  <option value="squared">Squared lines</option>
-  <option value="pie">Pie (*)</option>
-  <option value="linepoints">Line and points</option>
-  <option value="points">Points</option>
-  <option value="area">Area</option>
-</select>
-</p>
-<div style="text-align:right; font-size: smaller;">
-Please note when writing your application that the graph <br />
-types marked with an asterisk only support the data <br />
-type for this form, "text-data".
-</div>
diff --git a/gui/bacula-web/external_packages/phplot/examples/data_sample2.php b/gui/bacula-web/external_packages/phplot/examples/data_sample2.php
deleted file mode 100644 (file)
index e754a03..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php 
-//data-data as just data
-               $data = array(
-                       array("label 0", 0,   2, 5, 3 ),        
-                       array("label 1", 2,   3, 4, 2 ),
-                       array("label 2", 3,   4, 3, 7 ),
-                       array("label 3", 4.5, 5, 2, 4 ),
-                       array("label 4", 5,   6, 1, 1 )
-               );
-?>
-<p>
-Data type: (data-data)<br />
-<input type="hidden" name="which_data_type" value="data-data" />
-<table border=1>
- <tr>
-  <td>Title (data label)</td><td>X data</td><td>Y data 1</td>
-  <td>Y data 2</td><td>Y data 3</td>
- </tr>
-  <?php 
-    for ($i=0; $i<5; $i++) {
-  ?>
- <tr>
-  <td>
-   <input type="text" name="data_row0[<?php echo $i?>]" value="<?php echo $data[$i][0]?>" />
-  </td><td>
-   <input type="text" name="data_row1[<?php echo $i?>]" value="<?php echo $data[$i][1]?>" size="3" />
-  </td><td>
-   <input type="text" name="data_row2[<?php echo $i?>]" value="<?php echo $data[$i][2]?>" size="3" />
-  </td><td>
-   <input type="text" name="data_row3[<?php echo $i?>]" value="<?php echo $data[$i][3]?>" size="3" />
-  </td><td>
-   <input type="text" name="data_row4[<?php echo $i?>]" value="<?php echo $data[$i][4]?>" size="3" />
-  </td>
- </tr>
-  <?php 
-    }
-  ?>
-
-</table>
-
-<p>
-Graph type:
-<select name="which_plot_type">
-  <option value="area">Area</option>
-  <option value="lines">Lines</option>
-  <option value="squared">Squared lines</option>
-  <option value="linepoints">Line and points</option>
-  <option value="points">Points</option>
-</select>
-</p>
diff --git a/gui/bacula-web/external_packages/phplot/examples/data_sample3.php b/gui/bacula-web/external_packages/phplot/examples/data_sample3.php
deleted file mode 100644 (file)
index 46f774b..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<p>
-Data set as X, Y, E+, E-, Y2, E2+, E2-,... <br />
-<?php
-//data-data-error
-        $num_rows = 6;
-               $data = array(
-                       array("label 0", 0, 1, .5, .1, 1,  .2, .1),     
-                       array("label 1", 2, 5, .5, .4, 2,  .1, .3),
-                       array("label 2", 3, 2, .1, .1, 3,  .3, .1),
-                       array("label 3", 4, 5, .5, .5, 3.5,.1, .2),
-                       array("label 4", 5, 1, .1, .1, 5,  .1, .1),
-            array("label 5", 6, 2, .1, .2, 0,  .2, .3)
-               );
-?>
-Data type: (data-data-error)
-</p>
-<input type="hidden" name="which_data_type" value="data-data-error" />
-<table border=1>
- <tr><td>Title (data label)</td><td>X data</td> 
-  <td>Y data 1</td><td>Error +</td><td>Error -</td><td>Y data 2</td><td>Error +</td><td>Error -</td>
- </tr>
- <tr>
-  <td>
-  
-   <?php
-    // MBD: All this is more complicated than before, but allows for easy adding of rows and columns
-    echo "<input type=\"hidden\" name=\"num_data_rows\" value=\"$num_rows\" />";
-    
-    for ($i = 0; $i < $num_rows; $i++) {
-        // The label input element must be bigger.
-        $lines[0] = "<input type=\"text\" name=\"data_row".$i."[0]\" value=\"".$data[$i][0]."\" size=\"10\" />\n";
-        
-        // Show <input>s for the rest of the columns
-        for ($j=1; $j<8; $j++)
-            $lines[$j] = "<input type=\"text\" name=\"data_row".$i."[$j]\" value=\"".$data[$i][$j]."\" size=\"3\" />\n";
-        $groups[$i] = join('</td><td>', $lines);
-    }
-    echo join("</tr><tr><td>\n", $groups);
-    ?>
-    
-  </td>
- </tr>
-</table>
-
-<p>
-Graph type: 
-<select name="which_plot_type">
- <option value="lines">lines</option>
- <option value="linepoints">line and points</option>
- <option value="points">points</option>
-</select>
-&nbsp; &nbsp;&nbsp;
-Error bar type:
-<select name="which_error_type"> 
- <option value="tee">tee</option>
- <option value="line">line</option>
-</select>
-</p>
diff --git a/gui/bacula-web/external_packages/phplot/examples/data_sample4.php b/gui/bacula-web/external_packages/phplot/examples/data_sample4.php
deleted file mode 100644 (file)
index 5c4d14c..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<p>
-Data type: <i>data-data</i> calculated from a function.
-</p>
-<pre>
-       $dx = ".3";
-       $max = 6.4;
-       $maxi = $max/$dx;
-       for ($i=0; $i<$maxi; $i++) {
-               $a = 4;
-               $x = $dx*$i;
-               $data[$i] = array("", $x, $a*sin($x), 
-                              $a*cos($x), $a*cos($x+1));       
-       }
-</pre>
-<p>
-Chart type: 
-<select name="which_plot_type">
-    <option value="lines">Lines</option>
-    <option value="linepoints">Lines and points</option>
-    <option value="points">Points</option>
-    <option value="thinbarline">Thin bars</option>
-</select>
-<input type="hidden" name="which_data_type" value="function" />
-</p>
diff --git a/gui/bacula-web/external_packages/phplot/examples/data_sample5.php b/gui/bacula-web/external_packages/phplot/examples/data_sample5.php
deleted file mode 100644 (file)
index b8859b6..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<p>
-Data type: <i>data-data-error</i> calculated from a function.
-</p>
-<pre>
-    srand ((double) microtime() * 1000000);
-    $a = 9.62;
-    $label[0] = "October"; $label[5] = "Day 5"; $label[10] = "Day 10";
-    $label[15] = "Day 15"; $label[20] = "Day 20"; $label[25] = "Day 25";
-
-    for ($i = 0; $i <= 30; $i++) {
-        $a += rand(-1, 2);
-        $b = rand(0,1);
-        $c = rand(0,1);
-        $data[] = @ array($label[$i],$i+1,$a,$b,$c);
-    }
-</pre>
-<p>
-Chart type: 
-<select name="which_plot_type">
-    <option value="lines">Lines</option>
-    <option value="linepoints">Lines and points</option>
-    <option value="points">Points</option>
-</select>
-&nbsp; &nbsp;&nbsp;
-Error bar type:
-<select name="which_error_type">
- <option value="tee">tee</option>
- <option value="line">line</option>
-</select>
-<input type="hidden" name="which_data_type" value="randfunction" />
-</p>
diff --git a/gui/bacula-web/external_packages/phplot/examples/example1.php b/gui/bacula-web/external_packages/phplot/examples/example1.php
deleted file mode 100644 (file)
index a134097..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-//Include the code
-include("../phplot.php");
-
-//Define the object
-$graph = new PHPlot;
-
-//Set some data
-include("./data.php");
-$graph->SetDataValues($example_data);
-
-
-//Draw it
-$graph->DrawGraph();
-
-?>
diff --git a/gui/bacula-web/external_packages/phplot/examples/example2.php b/gui/bacula-web/external_packages/phplot/examples/example2.php
deleted file mode 100644 (file)
index 8f06a20..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-//Include the code
-include("../phplot.php");
-
-//Define the Object
-$graph = new PHPlot;
-
-//Define some data
-include("./data.php");
-
-//Set the data type
-$graph->SetDataType("linear-linear");
-
-//Remove the X data labels
-//$graph->SetXGridLabelType("none");
-
-//Load the data into data array
-$graph->SetDataValues($example_data);
-
-//Draw the graph
-$graph->DrawGraph();
-?>
diff --git a/gui/bacula-web/external_packages/phplot/examples/example3.php b/gui/bacula-web/external_packages/phplot/examples/example3.php
deleted file mode 100644 (file)
index a518e13..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-//Include the code
-include("../phplot.php");
-
-//Include the code for manipulating data (scaling, moving averages, etc.)
-include("../phplot_data.php");
-
-//Define the object
-$graph = new PHPlot_Data();
-
-//Define some data
-include("./data.php");
-
-//Set the data type 
-$graph->SetDataType("data-data");
-
-//Load the data into the data array
-$graph->SetDataValues($example_data);
-$graph->DoMovingAverage(4,2,TRUE);
-
-//Call Scaling Function (in phplot_data.php)
-//$graph->DoScaleData(1,1);
-
-//Draw a Legend at pixel location 100,100
-$graph->SetLegendPixels(100,100,"");
-
-//have no labels on Y axis
-//$graph->SetYGridLabelType("none");
-
-//Print that puppy!
-$graph->DrawGraph();
-?>
diff --git a/gui/bacula-web/external_packages/phplot/examples/example4.php b/gui/bacula-web/external_packages/phplot/examples/example4.php
deleted file mode 100644 (file)
index 360d2df..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php    
-
-//Include the code
-include("../phplot.php");
-
-//Define the object
-$graph = new PHPlot;
-
-//Create some random data as ("label",X-position, Y-position, error+, error-)
-//linear-linear-error data 
-unset($data);
-$a = 4.62;
-srand ((double) microtime() * 1000);
-////////////////////////////////////////////////
-/////Note: for $label[5] to appear on the X axis,
-/////      there must be a horizontal tick mark at x=5, etc.
-/////////////////////////////////////////////
-$label[0] = "October";
-$label[5] = "Day 5";
-$label[10] = "Day 10";
-$label[15] = "Day 15";
-$label[20] = "Day 20";
-$label[25] = "Day 25";
-$label[30] = "Day 30";
-Error_Reporting(0);
-for ($i=0; $i<=50; $i++){ 
-       $a += rand(-1, 2);
-       $b = $a*rand(0,1)/10;
-       $c = $a*rand(0,1)/10;
-       $data[] = array("$label[$i]",$i+1,$a,$b,$c);
-       
-}
-
-
-//Define the data for error bars
-       $graph->SetDataType("linear-linear-error");  //Must be first thing
-
-//Set the Graph particulars
-       $graph->SetPrecisionX(0);
-       $graph->SetPrecisionY(0);
-       $graph->SetUseTTF("0");
-       $graph->SetDrawYGrid("1"); // 1 = true
-       $graph->SetDataValues($data);
-    $graph->SetImageArea(600, 400);
-       $graph->SetVertTickIncrement("");
-       $graph->SetHorizTickIncrement(1);
-    $graph->SetErrorBarLineWidth(1);
-       $graph->SetYScaleType("log");
-       $graph->SetPointShape("halfline");
-       $graph->SetErrorBarShape("line");
-       $graph->SetPlotType("points");
-       $graph->SetXGridLabelType("title");
-       $graph->SetXLabel("Day");
-       $graph->SetYLabel("Stock value");
-       $graph->SetTitle("Logarithmic scale\nexample 4");
-       //$graph->SetErrorBarColors(array("blue","red","green","black"));
-       $graph->SetDataColors(
-               array("blue","green","yellow","red"),  //Data Colors
-               array("black")                                                  //Border Colors
-       );  
-
-       $graph->SetPlotAreaWorld(0,0,32,50);
-       //$graph->SetPlotAreaPixels(150,50,600,400);
-
-/*
-//Other settings
-               $graph->SetPlotBgColor(array(222,222,222));
-               $graph->SetBackgroundColor(array(200,222,222)); //can use rgb values or "name" values
-               $graph->SetTextColor("black");
-               $graph->SetGridColor("black");
-               $graph->SetLightGridColor(array(175,175,175));
-               $graph->SetTickColor("black");
-               $graph->SetTitleColor(array(0,0,0)); // Can be array or name
-
-*/
-
-
-//Draw the graph
-    $graph->DrawGraph();
-?>
diff --git a/gui/bacula-web/external_packages/phplot/examples/example6.php b/gui/bacula-web/external_packages/phplot/examples/example6.php
deleted file mode 100644 (file)
index 405943b..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-include("../phplot.php");
-$graph = new PHPlot(600,200);
-include("./data_date.php");
-$graph->SetDataType("data-data");  //Must be called before SetDataValues
-
-$graph->SetXGridLabelType("time");
-$graph->SetXDataLabelAngle(90);
-$graph->SetXLabel("");
-$graph->SetYLabel("Volume");
-$graph->SetVertTickIncrement(30);
-$graph->SetXTimeFormat("%b %y");
-$graph->SetDataValues($example_data);
-$graph->SetHorizTickIncrement(2679000);
-$graph->SetPlotType("thinbarline");
-$graph->SetDrawXDataLabels(false);
-$graph->SetPlotAreaWorld(883634400,0,915095000,90);
-$graph->DrawGraph();
-?>
diff --git a/gui/bacula-web/external_packages/phplot/examples/example7.php b/gui/bacula-web/external_packages/phplot/examples/example7.php
deleted file mode 100644 (file)
index 4b93ee8..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-include("./data_date2.php");
-include("../phplot.php");
-$graph = new PHPlot;
-$graph->SetDataType("data-data-error");  //Must be called before SetDataValues
-
-$graph->SetImageArea(600,400);
-$graph->SetPrecisionY(0);
-$graph->SetXLabel("");
-$graph->SetYLabel("Volume");
-$graph->SetVertTickIncrement(20);
-$graph->SetXAxisPosition(1);
-//$graph->SetSkipBottomTick(1);
-
-//Set Unixtime Increment and X Axis Settings
-$graph->SetHorizTickIncrement(2679000);
-$graph->SetXGridLabelType("time");
-$graph->SetXTimeFormat("%b %y");
-$graph->SetXDataLabelAngle(90);
-
-$graph->SetDataValues($example_data);
-$graph->SetPlotType("lines");
-$graph->SetErrorBarShape("line");
-$graph->SetPointShape("halfline");
-$graph->SetYScaleType("log");
-$graph->SetLineWidth(1);
-$graph->SetDrawXDataLabels(false);
-
-//Since X axis is in Unixtime format we set the limits accordingly
-$graph->SetPlotAreaWorld(883634400,1,915095000,140);
-
-$graph->DrawGraph();
-?>
diff --git a/gui/bacula-web/external_packages/phplot/examples/example8.php b/gui/bacula-web/external_packages/phplot/examples/example8.php
deleted file mode 100644 (file)
index d60c053..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-include("./data_date2.php");
-include("../phplot.php");
-$graph = new PHPlot(600,400);
-$graph->SetPrintImage(0); //Don't draw the image yet
-
-$graph->SetDataType("data-data-error");  //Must be called before SetDataValues
-
-$graph->SetNewPlotAreaPixels(90,40,540,190);
-$graph->SetDataValues($example_data);
-
-$graph->SetXLabelType("time");
-$graph->SetXLabelAngle(90);
-$graph->SetXTitle("");
-$graph->SetYTitle("Price");
-$graph->SetYTickIncrement(20);
-$graph->SetXTickIncrement(2679000);
-$graph->SetXTimeFormat("%b %y");
-$graph->SetPlotType("lines");
-$graph->SetErrorBarShape("line");
-$graph->SetPointShape("halfline");
-$graph->SetYScaleType("log");
-$graph->SetLineWidths(array(1));
-$graph->SetPlotAreaWorld(883634400,1,915095000,140);
-$graph->SetXDataLabelPos('none');
-$graph->DrawGraph();
-
-//Now do the second chart on the image
-
-unset($example_data);
-
-$graph->SetYScaleType("linear");
-include("./data_date.php");
-
-$graph->SetDataType("data-data");  //Must be called before SetDataValues
-
-$graph->SetDataValues($example_data);
-$graph->SetNewPlotAreaPixels(90,260,540,350);
-$graph->SetDataValues($example_data);
-
-$graph->SetXLabelType("time");
-$graph->SetXLabelAngle(90);
-$graph->SetXTitle("");
-$graph->SetYTitle("Volume");
-$graph->SetYTickIncrement(30);
-$graph->SetPlotType("thinbarline");
-
-//Set how to display the x-axis ticks
-$graph->SetXTimeFormat("%b %y");
-$graph->SetXTickIncrement(2679000);
-$graph->SetXAxisPosition(0);  //Have to reset it after log plots
-
-//Set Plot to go from x = Jan 1 1998, to x = Dec 31 1998
-//     and from y = 0 to y = 90
-$graph->SetPlotAreaWorld(883634400,0,915095000,90);
-
-$graph->DrawGraph();
-
-//Print the image
-$graph->PrintImage();
-?>
diff --git a/gui/bacula-web/external_packages/phplot/examples/example9.php b/gui/bacula-web/external_packages/phplot/examples/example9.php
deleted file mode 100644 (file)
index 32865c1..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-//Include the code
-include("../phplot.php");
-
-//Define the object
-$graph = new PHPlot();
-
-//Define some data
-include("./data.php");
-$graph->SetDataValues($example_data);
-
-//Don't print until we say so
-$graph->SetPrintImage(0);
-
-//Draw it
-$graph->DrawGraph();
-
-//Define some colors
-$ndx_color = $graph->SetIndexColor("blue");
-$ndx_color1 = $graph->SetIndexColor("orange");
-
-//The image data colors are now ndx_data_color[]
-$graph->DrawDashedLine(250,$graph->plot_area[1],250,250,4,0,$graph->ndx_data_colors[0]);
-
-$graph->DrawDashedLine($graph->xtr(5),$graph->ytr(12),
-               $graph->xtr(20),$graph->ytr(42),5,3,$ndx_color);
-
-$graph->DrawDashedLine($graph->plot_area[0],250,$graph->plot_area[2],250,2,0,$ndx_color1);
-$graph->DrawDashedLine($graph->plot_area[0],251,$graph->plot_area[2],251,2,0,$ndx_color1);
-
-
-//Now print the image
-$graph->PrintImage();
-
-?>
diff --git a/gui/bacula-web/external_packages/phplot/examples/format_chart.php b/gui/bacula-web/external_packages/phplot/examples/format_chart.php
deleted file mode 100644 (file)
index 5e2f4b8..0000000
+++ /dev/null
@@ -1,345 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<html>
-<head>
-    <title>Example-o-matic</title>
-    <link type="text/css" rel="stylesheet" href="../doc/style.css" />
-</head>
-<body>
-
-<div class="nav"> [ <a href="../doc/index.php">Go to the index</a> ] </div>
-
-<h2>PHPlot test graph form</h2>
-
-<p>Use this form to test many different options of PHPlot. You can test
-every graph type supported for any of four different data types. You can
-tweak as you like or you can leave everything as is and press "Submit" for
-automatic values.
-</p>
-<form action="create_chart.php" method="post">
-<center>
-<table border="0">
-
-        <tr><td colspan="2" class="hdr">Data Settings</td></tr>
-        
-<tr>
-  <td colspan="2">
-    <p>
-      Data type: [
-        <a href="format_chart.php?which_data_type=text-data">text-data</a> |
-        <a href="format_chart.php?which_data_type=data-data">data-data</a> |
-        <a href="format_chart.php?which_data_type=function">function</a> |
-        <a href="format_chart.php?which_data_type=data-data-error">data-data-error</a> |
-        <a href="format_chart.php?which_data_type=randfunction">randfunction</a> ]
-    </p>
-    <?php
-        if ($_GET['which_data_type'] == 'text-data')
-            include('data_sample1.php'); 
-        elseif ($_GET['which_data_type'] == 'data-data')
-            include('data_sample2.php');
-        elseif ($_GET['which_data_type'] == 'data-data-error')
-            include('data_sample3.php');
-        elseif ($_GET['which_data_type'] == 'function') 
-            include('data_sample4.php');
-        elseif ($_GET['which_data_type'] == 'randfunction')
-            include('data_sample5.php');
-        else
-            include('data_sample1.php');
-    ?>
-  </td>
-</tr>
-        <tr><td colspan="2" class="hdr"><input name="submit" type="submit" /></td></tr>
-        <tr>
-          <td colspan="2">
-            <br />
-              <h3 style="text-align:center;margin:0;">Optional values</h3>
-            <br />
-          </td>
-        </tr>
-        <tr><td colspan="2" class="hdr">Sizes</td></tr>
-        
-<tr>
-  <td>Width of graph in pixels:</td>
-  <td><input type="text" name="xsize_in" value="600" size="4" /></td>
-</tr>
-<tr>
-  <td> Height of graph in pixels:</td>
-  <td><input type="text" name="ysize_in" value="400" size="4" /></td>
-</tr>
-<tr>  
-  <td>Maximum height of graph in y axis units:</td>
-  <td><input type="text" name="maxy_in" value="" size="4" /></td>
-</tr>
-<tr>
-  <td>Minimum height of graph in y axis units:</td>
-  <td><input type="text" name="miny_in" value="" size="4" /></td>
-</tr>
-
-            <tr><td colspan="2" class="hdr">Titles and data labels</td></tr>
-            
-<tr>
-  <td>Title:</td>
-  <td><input type="text" name="title" value="This is a title" /></td>
-</tr>
-<tr>
-  <td>Y axis title:</td>
-  <td><input type="text" name="ylbl" value="Revenue in millions" /></td>
-</tr>
-<tr>
-  <td>Y axis title position:</td>
-  <td>
-    <select name="which_ytitle_pos">
-      <option value="plotleft">Left of plot</option>
-      <option value="plotright">Right of plot</option>
-      <option value="both" selected>Both right and left</option>
-      <option value="none">No Y axis title</option>
-    </select>
-  </td>
-</tr>
-<tr>
-  <td>Y axis data labels position:</td>
-  <td>
-    <select name="which_ydata_label_pos">
-      <option value="plotleft">Left of plot</option>
-      <option value="plotright">Right of plot</option>
-      <option value="both">Both right and left</option>
-      <option value="plotin">In the plot (To Do)</option>
-      <option value="none" selected>No data labels</option>
-    </select>
-  </td>
-</tr>
-<tr>
-  <td>Y axis labels angle:</td>
-  <td><input name="which_ylabel_angle" value="0" size="3" /></td>
-</tr>
-
-
-<tr>
-  <td>X axis title:</td>
-  <td><input type="text" name="xlbl" value="years" /></td>
-</tr>
-<tr>
-  <td>X axis title position:</td>
-  <td>
-    <select name="which_xtitle_pos">
-      <option value="plotup">Up of plot</option>
-      <option value="plotdown">Down of plot</option>
-      <option value="both" selected>Both up and down</option>
-      <option value="none">No X axis title</option>
-    </select>
-  </td>
-</tr>
-<tr>
-  <td>X axis data labels position:</td>
-  <td>
-    <select name="which_xdata_label_pos">
-      <option value="plotup">Up of plot</option>
-      <option value="plotdown">Down of plot</option>
-      <option value="both" selected>Both up and down</option>
-      <option value="plotin">In the plot (To Do)</option>
-      <option value="none" selected>No data labels</option>
-    </select>
-  </td>
-</tr>
-<tr>
-  <td>X axis labels angle:</td>
-  <td><input name="which_xlabel_angle" value="0" size="3" /></td>
-</tr>
-
-            <tr><td colspan="2" class="hdr">Grid and ticks</td></tr>
-
-
-<tr>
-  <td>Grid drawn:</td>
-  <td>
-    <select name="which_draw_grid">
-      <option value="x">Vertical grid</option>
-      <option value="y">Horizontal grid</option>
-      <option value="both" selected>Both grids</option>
-      <option value="none">No grid</option>
-    </select>
-  </td>
-</tr>
-<tr>
-  <td>Dashed grid?</td>
-  <td>
-    <select name="which_dashed_grid">
-      <option value="1" selected>Yes</option>
-      <option value="0">No</option>
-    </select>
-  </td>
-</tr>
-<tr>
-  <td>X axis ticks length:</td>
-  <td><input type="text" name="which_xtl" value="5" size="3"/></td>
-</tr>
-<tr>
-  <td>X axis ticks crossing:</td>
-  <td><input type="text" name="which_xtc" value="3" size="3"/></td>
-</tr>
-<tr>
-  <td>X axis ticks position:</td>
-  <td>
-    <select name="which_xtick_pos">
-      <option value="plotup">Up of plot</option>
-      <option value="plotdown">Down of plot</option>
-      <option value="both" selected>Both up and down</option>
-      <option value="xaxis">At X axis</option>
-      <option value="none">No ticks</option>
-    </select>
-  </td>
-</tr>
-<tr>
-  <td>X axis tick labels position:</td>
-  <td>
-    <select name="which_xtick_label_pos">
-      <option value="plotup">Up of plot</option>
-      <option value="plotdown">Down of plot</option>
-      <option value="both" selected>Both up and down</option>
-      <option value="xaxis">Below X axis</option>
-      <option value="none">No tick labels</option>
-    </select>
-  </td>
-</tr>
-<tr>
-  <td>Y axis ticks length:</td>
-  <td><input type="text" name="which_ytl" value="5" size="3"/></td>
-</tr>
-<tr>
-  <td>Y axis ticks crossing:</td>
-  <td><input type="text" name="which_ytc" value="3" size="3"/></td>
-</tr>
-<tr>
-  <td>Y axis ticks position:</td>
-  <td>
-    <select name="which_ytick_pos">
-      <option value="plotleft">Left of plot</option>
-      <option value="plotright">Right of plot</option>
-      <option value="both" selected>Both right and left</option>
-      <option value="yaxis">At Y axis</option>
-      <option value="none">No ticks</option>
-    </select>
-  </td>
-</tr>
-<tr>
-  <td>Y axis tick labels position:</td>
-  <td>
-    <select name="which_ytick_label_pos">
-      <option value="plotleft">Left of plot</option>
-      <option value="plotright">Right of plot</option>
-      <option value="both" selected>Both right and left</option>
-      <option value="yaxis">Left of Y axis</option>
-      <option value="none">No tick labels</option>
-    </select>
-  </td>
-</tr>
-<tr>
-  <td>X tick increment:</td>
-  <td><input type="text" name="which_xti" value="1" /></td>
-</tr>
-<tr>
-  <td>Y tick increment:</td>
-  <td><input type="text" name="which_yti" value="" /></td>
-</tr>
-
-
-            <tr><td colspan="2" class="hdr">Other</td></tr>
-
-
-<tr>
-  <td>X axis position:</td>
-  <td><input type="text" name="which_xap" value="0" size="5"/></td>
-</tr>
-<tr>
-  <td>Y axis position:</td>
-  <td><input type="text" name="which_yap" value="0" size="5"/></td>
-</tr>
-<tr>
-  <td>Plot Border:</td>
-  <td>
-    <select name="which_btype">
-      <option value="plotleft">Left of plot</option>
-      <option value="plotright">Right of plot</option>
-      <option value="both">Both sides of plot</option>
-      <option value="full" selected>All four sides</option>
-      <option value="none">None</option>
-    </select> 
-  </td>
-</tr>
-
-
-<tr>
-  <td>Shade height (0 for none):</td>
-  <td><input type="text" name="which_shading" value="5" size="3"/></td>
-</tr>  
-<tr>
-  <td>Plot line width:</td>
-  <td><input name="which_line_width" value="1" size="3" /></td>
-</tr>
-<tr>
-  <td>Error bar line width:</td>
-  <td><input name="which_errorbar_line_width" value="1" size="3" /></td>
-</tr>
-<tr>
-  <td>Point Type:</td>
-  <td>
-    <select name="which_point">
-         <option value="diamond">Diamond</option>
-         <option value="rect">Square</option>
-         <option value="circle">Circle</option>
-         <option value="triangle">Triangle</option>
-         <option value="trianglemid">Centered triangle</option>
-      <option value="dot">Filled dot</option>
-         <option value="line">Line</option>
-         <option value="halfline">Half line</option>
-      <option value="cross" selected>Cross</option>
-      <option value="plus" selected>Plus sign</option>
-    </select>
-  </td>
-</tr>
-<tr>
-  <td>Point Size:</td>
-  <td><input name="which_point_size" value="4" size="3" /></td>
-</tr>
-<tr>
-  <td>Draw broken lines with missing Y data:</td>
-  <td>
-    <select name="which_broken">
-      <option value="0" selected>No</option>
-      <option value="1">Yes</option>
-    </select>
-<tr>
-  <td>Use TrueType font:</td>
-  <td>
-    <select name="which_use_ttf">
-      <option value="0" selected>No</option>
-      <option value="1">Yes</option>
-    </select>
-  </td>
-</tr>
-<tr>
-  <td>File format:</td>
-  <td>
-    <select name="which_fileformat">
-      <option value="png">png</option>
-      <option value="jpg">jpeg</option>
-      <option value="gif">gif</option>
-      <option value="wbmp">bmp</option>
-    </select>
-  </td>
-</tr>
-        <tr><td colspan="2" class="hdr"><input name="submit" type="submit" /></td></tr>
-
-</table>
-</center>
-</form>
-
-<p>
-Please visit <a href="http://phplot.sourceforge.net">PHPlot's site</a>, the
-<a href="http://sourceforge.net/projects/phplot/">sourceforge project page</a>,
-or see <a href="http://www.jeo.net/php/">more php code and examples</a> 
-by Afan Ottenheimer of jeonet.
-</p>
-
-<p class="foot">$Id$</p>
-</body>
-</html>
diff --git a/gui/bacula-web/external_packages/phplot/examples/inline_image.php b/gui/bacula-web/external_packages/phplot/examples/inline_image.php
deleted file mode 100644 (file)
index 16a5a35..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-/* $Id$ */
-
-if (! isset($_GET['which_title'])) {
-echo <<<EOF
-<pre>
-           *************************************************
-           * This file is meant to be called only from the *
-           *                   <a href="test_setup.php">test page</a>                   *
-           * It will fail if called by itself.             *
-           *************************************************
-</pre>
-EOF
-;
-exit;
-}
-
-// From PHP 4.?, register_globals is off, take it into account (MBD)
-
-include('../phplot.php');
-$graph = new PHPlot;
-include('./data.php');
-$graph->SetTitle("$_GET[which_title]");
-$graph->SetDataValues($example_data);
-$graph->SetIsInline('1');
-$graph->SetFileFormat("$_GET[which_format]");
-$graph->DrawGraph();
-?>
diff --git a/gui/bacula-web/external_packages/phplot/examples/test_setup.php b/gui/bacula-web/external_packages/phplot/examples/test_setup.php
deleted file mode 100644 (file)
index b8860ca..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<!-- $Id$ -->
-<html>
-<head>
-   <title>PHPlot graphic formats test</title>
-   <link type="text/css" rel="stylesheet" href="../doc/style.css" />
-</head>
-<body>
-
-<div class="nav"> [ <a href="../doc/index.html">Go to the index</a> ] </div>
-
-<h2>PHPlot test graph form</h2>
-
-<p>Use this form to test many different options of PHPlot. You can test
-every graph type supported for any of four different data types. You can
-tweak as you like or you can leave everything as is and press "Submit" for
-<h2>PHPlot graphic formats test</h2>
-
-<p>
-This page will test which graphic formats are supported by the version of GD 
-linked into PHP. You should see at least one of the four images below. 
-</p>
-
-<center>
-<table>
-<tr><td class="hdr">PNG graphics</td></tr>
-<tr><td>
-<?php
-    if (! imagetypes() & IMG_PNG )  
-       echo "PNG NOT ENABLED";
-    else
-           echo "<img src=\"inline_image.php?which_format=png&which_title=YES_PNG_IS_ENABLED\" />";
-    
-?>
-</td></tr>
-<tr><td class="hdr">JPEG graphics</td></tr>
-<tr><td>
-<?php
-    if (! imagetypes() & IMG_JPG )
-        echo "JPEG NOT ENABLED";
-    else
-           echo "<img src=\"inline_image.php?which_format=jpg&which_title=YES_JPG_IS_ENABLED\" />";
-?>
-</td></tr>
-<tr><td class="hdr">GIF graphics</td></tr>
-<tr><td>
-<?php
-    if (! imagetypes() & IMG_GIF)
-           echo "GIF NOT ENABLED";
-    else
-           echo "<img src=\"inline_image.php?which_format=gif&which_title=YES_GIF_IS_ENABLED\" />";
-?>
-</td></tr>
-<tr><td class="hdr">BMP graphics</td></tr>
-<tr><td>
-<?php
-    if (! imagetypes() & IMG_WBMP)
-           echo "BMP NOT ENABLED";
-    else
-           echo "<img src=\"inline_image.php?which_format=wbmp&which_title=YES_WBMP_IS_ENABLED\" />";
-?>
-</td></tr>
-</table>
-</center>
-
-</body>
-</html>
index 5ded19e5c310301f382d653b4a376f168f5d3a9f..c7f323c38db97808c0dbe78910879803ed9228f7 100644 (file)
 <?php
-
-/* $Id$ */
-
+/* $Id: phplot.php,v 1.201 2010/10/03 21:57:09 lbayuk Exp $ */
 /*
- * PHPLOT Version 5.0.rc1
- * Copyright (C) 1998, 1999, 2000, 2001 Afan Ottenheimer.  Released under
- * the GPL and PHP licenses as stated in the the README file which should
- * have been included with this document.
+ * PHPLOT Version 5.2.0
+ *
+ * A PHP class for creating scientific and business charts
+ * Visit http://sourceforge.net/projects/phplot/
+ * for PHPlot documentation, downloads, and discussions.
+ * ---------------------------------------------------------------------
+ * Copyright (C) 1998-2010 Afan Ottenheimer
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License.
  *
- * Recent (2003-2004) work by Miguel de Benito Delgado <nonick AT vodafone DOT es>
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
  *
- * Requires PHP 4.2.0 or later (CHECK THIS)
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * ---------------------------------------------------------------------
+ *
+ * Co-author and maintainer (2003-2005)
+ * Miguel de Benito Delgado <nonick AT vodafone DOT es>
+ *
+ * Maintainer (2006-present)
+ * <lbayuk AT users DOT sourceforge DOT net>
+ *
+ * Requires PHP 5.2.x or later. (PHP 4 is unsupported as of Jan 2008)
  */
 
-if (! defined(__FUNCTION__))
-    define(__FUNCTION__, '__FUNCTION__ Requires at least PHP 4.3.0.');
-
-define ('MINY', -1);        // Indexes in $data (for DrawXDataLine())
-define ('MAXY', -2);
-define ('TOTY', -3);
-
-error_reporting(E_ALL);
-
-class PHPlot {
-
-    /* I have removed internal variable declarations, some isset() checking was required,
-     * but now the variables left are those which can be tweaked by the user. This is intended to
-     * be the first step towards moving most of the Set...() methods into a subclass which will be
-     * used only when strictly necessary. Many users will be able to put default values here in the
-     * class and thus avoid memory overhead and reduce parsing times.
+class PHPlot
+{
+    /* Declare class variables which are initialized to static values. Many more class variables
+     * are used, defined as needed, but are unset by default.
+     * All these are declared as public. While it is tempting to make them private or protected, this
+     * is avoided for two reasons. First, it will break existing code, since all member variables
+     * were public in PHP4 and who knows what internal variables people used. Second, it makes
+     * testing harder and less effective. Nevertheless, your code should not modify these.
      */
-    //////////////// CONFIG PARAMETERS //////////////////////
 
-    var $is_inline = FALSE;             // FALSE = Sends headers, TRUE = sends just raw image data
-    var $browser_cache = FALSE;         // FALSE = Sends headers for browser to not cache the image,
-                                        // (only if is_inline = FALSE also)
+    public $is_inline = FALSE;             // FALSE = Sends headers, TRUE = sends just raw image data
+    public $browser_cache = FALSE;         // FALSE = Sends headers for browser to not cache the image,
+                                           // (only if is_inline = FALSE also)
+    public $print_image = TRUE;            // DrawGraph calls PrintImage. See SetPrintImage
+    public $background_done = FALSE;       // TRUE after background image is drawn once
 
-    var $safe_margin = 5;               // Extra margin used in several places. In pixels
+    public $safe_margin = 5;               // Extra margin used in several places, in pixels
 
-    var $x_axis_position = '';          // Where to draw both axis (world coordinates),
-    var $y_axis_position = '';          // leave blank for X axis at 0 and Y axis at left of plot.
+    public $x_axis_position = '';          // X axis position in Y world coordinates, blank for default.
+    public $y_axis_position = '';          // Y axis position in X world coordinates, blank for default.
 
-    var $xscale_type = 'linear';        // linear, log
-    var $yscale_type = 'linear';
+    public $xscale_type = 'linear';        // linear, log
+    public $yscale_type = 'linear';
 
 //Fonts
-    var $use_ttf  = FALSE;                  // Use True Type Fonts?
-    var $ttf_path = '.';                    // Default path to look in for TT Fonts.
-    var $default_ttfont = 'benjamingothic.ttf';
-    var $line_spacing = 4;                  // Pixels between lines.
-
-    // Font angles: 0 or 90 degrees for fixed fonts, any for TTF
-    var $x_label_angle = 0;                 // For labels on X axis (tick and data)
-    var $y_label_angle = 0;                 // For labels on Y axis (tick and data)
-    var $x_title_angle = 0;                 // Don't change this if you don't want to screw things up!
-    var $y_title_angle = 90;                // Nor this.
-    var $title_angle = 0;                   // Or this.
+    public $use_ttf  = FALSE;              // Use True Type Fonts by default?
+    public $ttf_path = '.';                // Default path to look in for TT Fonts.
+    // public $default_ttfont;             // Initialized in GetDefaultTTFont
+    public $line_spacing = 4;              // Controls line spacing of multi-line labels
+
+    // Label angles: 0 or 90 degrees for fixed fonts, any for TTF
+    public $x_label_angle = 0;             // For X tick labels
+    // public $x_data_label_angle;         // For X data labels; defaults to x_label_angle - see CheckLabels()
+    public $y_label_angle = 0;             // For Y tick labels
+    public $y_data_label_angle = 0;        // For Y data labels
 
 //Formats
-    var $file_format = 'png';
-    var $output_file = '';                  // For output to a file instead of stdout
+    public $file_format = 'png';
+    public $output_file = '';              // For output to a file instead of stdout
 
 //Data
-    var $data_type = 'text-data';           // text-data, data-data-error, data-data, text-data-single
-    var $plot_type= 'linepoints';           // bars, lines, linepoints, area, points, pie, thinbarline, squared
-
-    var $label_scale_position = 0.5;        // Shifts data labes in pie charts. 1 = top, 0 = bottom
-    var $group_frac_width = 0.7;            // value from 0 to 1 = width of bar groups
-    var $bar_width_adjust = 1;              // 1 = bars of normal width, must be > 0
+    public $data_type = 'text-data';       // Structure of the data array
+    public $plot_type= 'linepoints';       // bars, lines, linepoints, area, points, pie, thinbarline, squared
 
-    var $y_precision = 1;
-    var $x_precision = 1;
-
-    var $data_units_text = '';              // Units text for 'data' labels (i.e: '¤', '$', etc.)
+    public $label_scale_position = 0.5;    // Shifts data labels in pie charts. 1 = top, 0 = bottom
+    public $group_frac_width = 0.7;        // Bars use this fraction (0 to 1) of a group's space
+    public $bar_extra_space = 0.5;         // Number of extra bar's worth of space in a group
+    public $bar_width_adjust = 1;          // 1 = bars of normal width, must be > 0
 
 // Titles
-    var $title_txt = '';
-
-    var $x_title_txt = '';
-    var $x_title_pos = 'plotdown';          // plotdown, plotup, both, none
+    public $title_txt = '';
 
-    var $y_title_txt = '';
-    var $y_title_pos = 'plotleft';          // plotleft, plotright, both, none
+    public $x_title_txt = '';
+    public $x_title_pos = 'none';          // plotdown, plotup, both, none
 
+    public $y_title_txt = '';
+    public $y_title_pos = 'none';          // plotleft, plotright, both, none
 
 //Labels
     // There are two types of labels in PHPlot:
-    //    Tick labels: they follow the grid, next to ticks in axis.   (DONE)
-    //                 they are drawn at grid drawing time, by DrawXTicks() and DrawYTicks()
-    //    Data labels: they follow the data points, and can be placed on the axis or the plot (x/y)  (TODO)
-    //                 they are drawn at graph plotting time, by Draw*DataLabel(), called by DrawLines(), etc.
-    //                 Draw*DataLabel() also draws H/V lines to datapoints depending on draw_*_data_label_lines
-
+    //    Tick labels: Follow the grid, next to ticks in axis.
+    //                 Are drawn at grid drawing time, by DrawXTicks() and DrawYTicks()
+    //    Data labels: Follow the data points, and can be placed on the axis or the plot (x/y)
+    //                 Are drawn at graph plotting time, by Draw*DataLabel(), called by DrawLines(), etc.
+    //                 DrawXDataLabel() also draws vertical lines to data points, depending on
+    //                 draw_x_data_label_lines.
     // Tick Labels
-    var $x_tick_label_pos = 'plotdown';     // plotdown, plotup, both, xaxis, none
-    var $y_tick_label_pos = 'plotleft';     // plotleft, plotright, both, yaxis, none
-
-    // Data Labels:
-    var $x_data_label_pos = 'plotdown';     // plotdown, plotup, both, plot, all, none
-    var $y_data_label_pos = 'plotleft';     // plotleft, plotright, both, plot, all, none
-
-    var $draw_x_data_label_lines = FALSE;   // Draw a line from the data point to the axis?
-    var $draw_y_data_label_lines = FALSE;   // TODO
-
-    // Label types: (for tick, data and plot labels)
-    var $x_label_type = '';                 // data, time. Leave blank for no formatting.
-    var $y_label_type = '';                 // data, time. Leave blank for no formatting.
-    var $x_time_format = '%H:%m:%s';        // See http://www.php.net/manual/html/function.strftime.html
-    var $y_time_format = '%H:%m:%s';        // SetYTimeFormat() too...
-
-    // Skipping labels
-    var $x_label_inc = 1;                   // Draw a label every this many (1 = all) (TODO)
-    var $y_label_inc = 1;
-    var $_x_label_cnt = 0;                  // internal count FIXME: work in progress
-
-    // Legend
-    var $legend = '';                       // An array with legend titles
-    var $legend_x_pos = '';
-    var $legend_y_pos = '';
-
+    // Tick and Data label positions are not initialized, because PHPlot needs to tell if they
+    // defaulted or are set by the user. See CheckLabels() for details. The variables and
+    // effective defaults are shown here in comments (but CheckLabels adjusts the defaults).
+    // public $x_tick_label_pos = 'plotdown';     // X tick label position
+    // public $y_tick_label_pos = 'plotleft';     // Y tick label position
+    // public $x_data_label_pos = 'plotdown';     // X data label position
+    // public $y_data_label_pos = 'none';         // Y data label position
+
+    public $draw_x_data_label_lines = FALSE;   // Draw a line from the data point to the axis?
+
+    // Label format controls: (for tick, data and plot labels)
+    // Unset by default, these array members are used as needed for 'x' (x tick labels), 'xd' (x data
+    // labels), 'y' (y tick labels), and 'yd' (y data labels).
+    //    type, precision, prefix, suffix, time_format, printf_format, custom_callback, custom_arg.
+    // These replace the former: x_label_type, x_time_format, x_precision (similar for y), data_units_text.
+    public $label_format = array('x' => array(), 'xd' => array(), 'y' => array(), 'yd' => array());
+    // data_units_text is retained for backward compatibility, because there was never a function
+    // to set it. Use the 'suffix' argument to Set[XY]LabelType instead.
+    public $data_units_text = '';              // Units text for 'data' labels (i.e: '¤', '$', etc.)
+
+// Legend
+    public $legend = '';                       // An array with legend titles
+    // These variables are unset to take default values:
+    // public $legend_x_pos;                   // User-specified upper left coordinates of legend box
+    // public $legend_y_pos;
+    // public $legend_xy_world;                // If set, legend_x/y_pos are world coords, else pixel coords
+    // public $legend_text_align;              // left or right, Unset means right
+    // public $legend_colorbox_align;          // left, right, or none; Unset means same as text_align
 
 //Ticks
-    var $x_tick_length = 5;                 // tick length in pixels for upper/lower axis
-    var $y_tick_length = 5;                 // tick length in pixels for left/right axis
+    public $x_tick_length = 5;                 // tick length in pixels for upper/lower axis
+    public $y_tick_length = 5;                 // tick length in pixels for left/right axis
 
-    var $x_tick_cross = 3;                  // ticks cross x axis this many pixels
-    var $y_tick_cross = 3;                  // ticks cross y axis this many pixels
+    public $x_tick_cross = 3;                  // ticks cross x axis this many pixels
+    public $y_tick_cross = 3;                  // ticks cross y axis this many pixels
 
-    var $x_tick_pos = 'plotdown';           // plotdown, plotup, both, xaxis, none
-    var $y_tick_pos = 'plotleft';           // plotright, plotleft, both, yaxis, none
+    public $x_tick_pos = 'plotdown';           // plotdown, plotup, both, xaxis, none
+    public $y_tick_pos = 'plotleft';           // plotright, plotleft, both, yaxis, none
 
-    var $num_x_ticks = '';
-    var $num_y_ticks = '';
+    public $num_x_ticks = '';
+    public $num_y_ticks = '';
 
-    var $x_tick_inc = '';                   // Set num_x_ticks or x_tick_inc, not both.
-    var $y_tick_inc = '';                   // Set num_y_ticks or y_tick_inc, not both.
+    public $x_tick_inc = '';                   // Set num_x_ticks or x_tick_inc, not both.
+    public $y_tick_inc = '';                   // Set num_y_ticks or y_tick_inc, not both.
 
-    var $skip_top_tick = FALSE;
-    var $skip_bottom_tick = FALSE;
-    var $skip_left_tick = FALSE;
-    var $skip_right_tick = FALSE;
+    public $skip_top_tick = FALSE;
+    public $skip_bottom_tick = FALSE;
+    public $skip_left_tick = FALSE;
+    public $skip_right_tick = FALSE;
 
 //Grid Formatting
-    var $draw_x_grid = FALSE;
-    var $draw_y_grid = TRUE;
+    // public $draw_x_grid = FALSE;            // Default is False except for swapped data type
+    // public $draw_y_grid = TRUE;             // Default is True except for swapped data type
 
-    var $dashed_grid = TRUE;
-    var $grid_at_foreground = FALSE;        // Chooses whether to draw the grid below or above the graph
+    public $dashed_grid = TRUE;
+    public $grid_at_foreground = FALSE;        // Chooses whether to draw the grid below or above the graph
 
 //Colors and styles       (all colors can be array (R,G,B) or named color)
-    var $color_array = 'small';             // 'small', 'large' or array (define your own colors)
+    public $color_array = 'small';             // 'small', 'large' or array (define your own colors)
                                             // See rgb.inc.php and SetRGBArray()
-    var $i_border = array(194, 194, 194);
-    var $plot_bg_color = 'white';
-    var $bg_color = 'white';
-    var $label_color = 'black';
-    var $text_color = 'black';
-    var $grid_color = 'black';
-    var $light_grid_color = 'gray';
-    var $tick_color = 'black';
-    var $title_color = 'black';
-    var $data_colors = array('SkyBlue', 'green', 'orange', 'blue', 'orange', 'red', 'violet', 'azure1');
-    var $error_bar_colors = array('SkyBlue', 'green', 'orange', 'blue', 'orange', 'red', 'violet', 'azure1');
-    var $data_border_colors = array('black');
-
-    var $line_widths = array(1);           // single value or array
-    var $line_styles = array('solid', 'solid', 'dashed');   // single value or array
-    var $dashed_style = '2-4';              // colored dots-transparent dots
-
-    var $point_sizes = array(5,5,3);         // single value or array
-    var $point_shapes = array('diamond');   // rect, circle, diamond, triangle, dot, line, halfline, cross
-
-    var $error_bar_size = 5;                // right and left size of tee
-    var $error_bar_shape = 'tee';           // 'tee' or 'line'
-    var $error_bar_line_width = 1;          // single value (or array TODO)
-
-    var $plot_border_type = 'sides';        // left, sides, none, full
-    var $image_border_type = 'none';        // 'raised', 'plain', 'none'
-
-    var $shading = 5;                       // 0 for no shading, > 0 is size of shadows in pixels
-
-    var $draw_plot_area_background = FALSE;
-    var $draw_broken_lines = FALSE;          // Tells not to draw lines for missing Y data.
-
+    public $default_colors = array(       // The default colors for data and error bars
+        'SkyBlue', 'green', 'orange', 'blue', 'red', 'DarkGreen', 'purple', 'peru',
+        'cyan', 'salmon', 'SlateBlue', 'YellowGreen', 'magenta', 'aquamarine1', 'gold', 'violet');
+
+    // See SetDefaultStyles() for default colors for PHPlot elements.
+
+    public $line_widths = 1;                  // single value or array
+    public $line_styles = array('solid', 'solid', 'dashed');   // single value or array
+    public $dashed_style = '2-4';              // colored dots-transparent dots
+
+    public $point_sizes = array(6);            // Array of sizes for points. See CheckPointParams()
+    public $point_shapes = array(              // Array of point shapes. See SetPointShapes() and DrawDot()
+          'diamond', 'dot', 'delta', 'home', 'yield', 'box', 'circle', 'up', 'down', 'cross'
+       );
+
+    public $error_bar_size = 5;                // right and left size of tee
+    public $error_bar_shape = 'tee';           // 'tee' or 'line'
+    public $error_bar_line_width = 1;          // single value (or array TODO)
+
+    public $plot_border_type = 'sides';        // left, right, top, bottom, sides, none, full; or array
+    public $image_border_type = 'none';        // 'raised', 'plain', 'none'
+    // public $image_border_width;             // NULL, 0, or unset for default. Default depends on type.
+
+    public $shading = 5;                       // 0 for no shading, > 0 is size of shadows in pixels
+
+    public $draw_plot_area_background = FALSE;
+    public $draw_broken_lines = FALSE;          // Tells not to draw lines for missing Y data.
+
+//Miscellaneous
+    public $callbacks = array(                  // Valid callback reasons (see SetCallBack)
+        'draw_setup' => NULL,
+        'draw_image_background' => NULL,
+        'draw_plotarea_background' => NULL,
+        'draw_titles' => NULL,
+        'draw_axes' => NULL,
+        'draw_graph' => NULL,
+        'draw_border' => NULL,
+        'draw_legend' => NULL,
+        'draw_all' => NULL,
+        'data_color' => NULL,
+        'debug_textbox' => NULL,  // For testing/debugging text box alignment
+        'debug_scale' => NULL,    // For testing/debugging scale setup
+    );
 
 //////////////////////////////////////////////////////
 //BEGIN CODE
 //////////////////////////////////////////////////////
 
-    /*!
+    /*
      * Constructor: Setup img resource, colors and size of the image, and font sizes.
      *
-     * \param which_width       int    Image width in pixels.
-     * \param which_height      int    Image height in pixels.
-     * \param which_output_file string Filename for output.
-     * \param which_input_fule  string Path to a file to be used as background.
+     *   $which_width : Image width in pixels.
+     *   $which_height : Image height in pixels.
+     *   $which_output_file : Filename for output.
+     *   $which_input_file : Path to a file to be used as background.
      */
     function PHPlot($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL)
     {
-        /*
-         * Please see http://www.php.net/register_shutdown_function
-         * PLEASE NOTE: register_shutdown_function() will take a copy of the object rather than a reference
-         * so we put an ampersand. However, the function registered will work on the object as it
-         * was upon registration. To solve this, one of two methods can be used:
-         *      $obj = new object();
-         *      register_shutdown_function(array(&$obj,'shutdown'));
-         * OR
-         *      $obj = &new object();
-         * HOWEVER, as the second statement assigns $obj a reference to the current object, it might be that
-         * several instances mess things up... (CHECK THIS)
-         *
-         * AND
-         *    as $this->img is set upon construction of the object, problems will not arise for us (for the
-         *    moment maybe, so I put all this here just in case)
-         */
-        register_shutdown_function(array(&$this, '_PHPlot'));
-
         $this->SetRGBArray($this->color_array);
 
-        $this->background_done = FALSE;     // Set to TRUE after background image is drawn once
-
         if ($which_output_file)
             $this->SetOutputFile($which_output_file);
 
-        if ($which_input_file)
+        if ($which_input_file) {
             $this->SetInputFile($which_input_file);
-        else {
+        else {
             $this->image_width = $which_width;
             $this->image_height = $which_height;
 
             $this->img = ImageCreate($this->image_width, $this->image_height);
             if (! $this->img)
-                $this->PrintError('PHPlot(): Could not create image resource.');
-
+                return $this->PrintError('PHPlot(): Could not create image resource.');
         }
 
         $this->SetDefaultStyles();
         $this->SetDefaultFonts();
-
-        $this->SetTitle('');
-        $this->SetXTitle('');
-        $this->SetYTitle('');
-
-        $this->print_image = TRUE;      // Use for multiple plots per image (TODO: automatic)
     }
 
-    /*!
-     * Destructor. Image resources not deallocated can be memory hogs, I think
-     * it is safer to automatically call imagedestroy upon script termination than
-     * do it ourselves.
-     * See notes in the constructor code.
+    /*
+     * Reads an image file. Stores width and height, and returns the image
+     * resource. On error, calls PrintError and returns False.
+     * This is used by the constructor via SetInputFile, and by tile_img().
      */
-    function _PHPlot ()
+    protected function GetImage($image_filename, &$width, &$height)
     {
-        ImageDestroy($this->img);
-        return;
+        $error = '';
+        $size = getimagesize($image_filename);
+        if (!$size) {
+            $error = "Unable to query image file $image_filename";
+        } else {
+            $image_type = $size[2];
+            switch ($image_type) {
+            case IMAGETYPE_GIF:
+                $img = @ ImageCreateFromGIF ($image_filename);
+                break;
+            case IMAGETYPE_PNG:
+                $img = @ ImageCreateFromPNG ($image_filename);
+                break;
+            case IMAGETYPE_JPEG:
+                $img = @ ImageCreateFromJPEG ($image_filename);
+                break;
+            default:
+                $error = "Unknown image type ($image_type) for image file $image_filename";
+                break;
+            }
+        }
+        if (empty($error) && !$img) {
+            // getimagesize is OK, but GD won't read it. Maybe unsupported format.
+            $error = "Failed to read image file $image_filename";
+        }
+        if (!empty($error)) {
+            return $this->PrintError("GetImage(): $error");
+        }
+        $width = $size[0];
+        $height = $size[1];
+        return $img;
     }
 
+    /*
+     * Selects an input file to be used as background for the whole graph.
+     * This resets the graph size to the image's size.
+     * Note: This is used by the constructor. It is deprecated for direct use.
+     */
+    function SetInputFile($which_input_file)
+    {
+        $im = $this->GetImage($which_input_file, $this->image_width, $this->image_height);
+        if (!$im)
+            return FALSE;  // GetImage already produced an error message.
+
+        // Deallocate any resources previously allocated
+        if (isset($this->img))
+            imagedestroy($this->img);
+
+        $this->img = $im;
+
+        // Do not overwrite the input file with the background color.
+        $this->background_done = TRUE;
+
+        return TRUE;
+    }
 
 /////////////////////////////////////////////
 //////////////                         COLORS
 /////////////////////////////////////////////
 
-    /*!
-     * Returns an index to a color passed in as anything (string, hex, rgb)
-     *
-     * \param which_color * Color (can be '#AABBCC', 'Colorname', or array(r,g,b))
+    /*
+     * Allocate a GD color index for a color specified by a 4 component array.
+     * When a color is requested, it is parsed and checked by SetRGBColor, and then saved as an array
+     * of (R,G,B,A) components. At graph drawing time, this function is used to allocate the color.
+     *   $color : The color specification as a 4 component array: R, G, B, A.
+     * Returns: A GD color index that can be used when drawing.
      */
-    function SetIndexColor($which_color) 
+    protected function GetColorIndex($color)
     {
-        list ($r, $g, $b) = $this->SetRGBColor($which_color);  //Translate to RGB
-        $index = ImageColorExact($this->img, $r, $g, $b);
-        if ($index == -1) {
-            return ImageColorResolve($this->img, $r, $g, $b);
-        } else {
-            return $index;
-        }
+        list($r, $g, $b, $a) = $color;
+        return imagecolorresolvealpha($this->img, $r, $g, $b, $a);
     }
 
-
-    /*!
-     * Returns an index to a slightly darker color than the one requested. 
+    /*
+     * Allocate a GD color index for a darker shade of a color specified by a 4 component array.
+     * See notes for GetColorIndex() above.
+     *   $color : The color specification as a 4 component array: R, G, B, A.
+     * Returns: A GD color index that can be used when drawing.
      */
-    function SetIndexDarkColor($which_color) 
+    protected function GetDarkColorIndex($color)
     {
-        list ($r, $g, $b) = $this->SetRGBColor($which_color);
-
-        $r -= 0x30;     $r = ($r < 0) ? 0 : $r;
-        $g -= 0x30;     $g = ($g < 0) ? 0 : $g;
-        $b -= 0x30;     $b = ($b < 0) ? 0 : $b;
-
-        $index = ImageColorExact($this->img, $r, $g, $b);
-        if ($index == -1) {
-            return ImageColorResolve($this->img, $r, $g, $b);
-        } else {
-            return $index;
-        }
+        list ($r, $g, $b, $a) = $color;
+        $r = max(0, $r - 0x30);
+        $g = max(0, $g - 0x30);
+        $b = max(0, $b - 0x30);
+        return imagecolorresolvealpha($this->img, $r, $g, $b, $a);
     }
 
-    /*!
-     * Sets/reverts all colors and styles to their defaults. If session is set, then only updates indices,
-     * as they are lost with every script execution, else, sets the default colors by name or value and
-     * then updates indices too.
-     *
-     * FIXME Isn't this too slow?
-     *
-     */
-    function SetDefaultStyles()
-    {
-        /* Some of the Set*() functions use default values when they get no parameters. */
-
-        if (! isset($this->session_set)) {
-            // If sessions are enabled, this variable will be preserved, so upon future executions, we
-            // will have it set, as well as color names (though not color indices, that's why we
-            // need to rebuild them)
-            $this->session_set = TRUE;
-
-            // These only need to be set once
-            $this->SetLineWidths();
-            $this->SetLineStyles();
-            $this->SetDefaultDashedStyle($this->dashed_style);
-            $this->SetPointSizes($this->point_sizes);
-        }
-
-        $this->SetImageBorderColor($this->i_border);
-        $this->SetPlotBgColor($this->plot_bg_color);
-        $this->SetBackgroundColor($this->bg_color);
-        $this->SetLabelColor($this->label_color);
-        $this->SetTextColor($this->text_color);
-        $this->SetGridColor($this->grid_color);
-        $this->SetLightGridColor($this->light_grid_color);
-        $this->SetTickColor($this->tick_color);
-        $this->SetTitleColor($this->title_color);
+    /*
+     * Sets/reverts all colors and styles to their defaults.
+     */
+    protected function SetDefaultStyles()
+    {
+        $this->SetDefaultDashedStyle($this->dashed_style);
+        $this->SetImageBorderColor(array(194, 194, 194));
+        $this->SetPlotBgColor('white');
+        $this->SetBackgroundColor('white');
+        $this->SetLabelColor('black');
+        $this->SetTextColor('black');
+        $this->SetGridColor('black');
+        $this->SetLightGridColor('gray');
+        $this->SetTickColor('black');
+        $this->SetTitleColor('black');
+        // These functions set up the default colors when called without parameters
         $this->SetDataColors();
         $this->SetErrorBarColors();
         $this->SetDataBorderColors();
+        return TRUE;
     }
 
-
     /*
-     *
+     * Set the image background color to $which_color.
      */
     function SetBackgroundColor($which_color)
     {
-        $this->bg_color= $which_color;
-        $this->ndx_bg_color= $this->SetIndexColor($this->bg_color);
-        return TRUE;
+        return (bool)($this->bg_color = $this->SetRGBColor($which_color));
     }
 
     /*
-     *
+     * Set the plot area background color (if enabled) to $which_color.
      */
     function SetPlotBgColor($which_color)
     {
-        $this->plot_bg_color= $which_color;
-        $this->ndx_plot_bg_color= $this->SetIndexColor($this->plot_bg_color);
-        return TRUE;
+        return (bool)($this->plot_bg_color = $this->SetRGBColor($which_color));
     }
 
-   /*
-    *
-    */
-    function SetTitleColor($which_color) 
+    /*
+     * Set the color of the titles (main, X, and Y) to $which_color.
+     * See also SetXTitleColor and SetYTitleColor.
+     */
+    function SetTitleColor($which_color)
     {
-        $this->title_color= $which_color;
-        $this->ndx_title_color= $this->SetIndexColor($this->title_color);
-        return TRUE;
+        return (bool)($this->title_color = $this->SetRGBColor($which_color));
     }
 
     /*
-     *
+     * Set the color of the X title to $which_color.
+     * This overrides the color set with SetTitleColor.
      */
-    function SetTickColor ($which_color) 
+    function SetXTitleColor($which_color)
     {
-        $this->tick_color= $which_color;
-        $this->ndx_tick_color= $this->SetIndexColor($this->tick_color);
-        return TRUE;
+        return (bool)($this->x_title_color = $this->SetRGBColor($which_color));
     }
 
-    
     /*
-     *
+     * Set the color of the Y title to $which_color.
+     * This overrides the color set with SetTitleColor.
      */
-    function SetLabelColor ($which_color) 
+    function SetYTitleColor($which_color)
     {
-        $this->label_color = $which_color;
-        $this->ndx_title_color= $this->SetIndexColor($this->label_color);
-        return TRUE;
+        return (bool)($this->y_title_color = $this->SetRGBColor($which_color));
     }
 
-
     /*
-     *
+     * Set the color of the axis tick marks to $which_color.
      */
-    function SetTextColor ($which_color) 
+    function SetTickColor($which_color)
     {
-        $this->text_color= $which_color;
-        $this->ndx_text_color= $this->SetIndexColor($this->text_color);
-        return TRUE;
+        return (bool)($this->tick_color = $this->SetRGBColor($which_color));
     }
 
+    /*
+     * Do not use. Use SetTitleColor instead.
+     */
+    function SetLabelColor($which_color)
+    {
+        return $this->SetTitleColor($which_color);
+    }
 
     /*
-     *
+     * Set the general text color (tick and data labels, legend, etc) to $which_color.
      */
-    function SetLightGridColor ($which_color) 
+    function SetTextColor($which_color)
     {
-        $this->light_grid_color= $which_color;
-        $this->ndx_light_grid_color= $this->SetIndexColor($this->light_grid_color);
-        return TRUE;
+        return (bool)($this->text_color = $this->SetRGBColor($which_color));
     }
 
-    
     /*
-     *
+     * Set the X and Y grid colors to $which_color. Also sets the data label line color.
      */
-    function SetGridColor ($which_color) 
+    function SetLightGridColor($which_color)
     {
-        $this->grid_color = $which_color;
-        $this->ndx_grid_color= $this->SetIndexColor($this->grid_color);
-        return TRUE;
+        return (bool)($this->light_grid_color = $this->SetRGBColor($which_color));
     }
 
+    /*
+     * Set the color used for the X and Y axis, plot border, legend border to $which_color.
+     * Note: This has nothing to do with the grid, and we don't recall where this name came from.
+     */
+    function SetGridColor($which_color)
+    {
+        return (bool)($this->grid_color = $this->SetRGBColor($which_color));
+    }
 
     /*
-     *
+     * Set the color used for the image border to $which_color.
      */
     function SetImageBorderColor($which_color)
     {
-        $this->i_border = $which_color;
-        $this->ndx_i_border = $this->SetIndexColor($this->i_border);
-        $this->ndx_i_border_dark = $this->SetIndexDarkColor($this->i_border);
-        return TRUE;
+        return (bool)($this->i_border = $this->SetRGBColor($which_color));
     }
 
-
     /*
-     *
-     */   
+     * Designate color $which_color to be transparent, if supported by the image format.
+     */
     function SetTransparentColor($which_color)
-    { 
-        ImageColorTransparent($this->img, $this->SetIndexColor($which_color));
-        return TRUE;
+    {
+        return (bool)($this->transparent_color = $this->SetRGBColor($which_color));
     }
-    
 
-    /*!
-     * Sets the array of colors to be used. It can be user defined, a small predefined one 
+    /*
+     * Sets the array of colors to be used. It can be user defined, a small predefined one
      * or a large one included from 'rgb.inc.php'.
      *
-     * \param which_color_array If an array, the used as color array. If a string can 
-     *        be one of 'small' or 'large'.
+     *    $which_color_array : A color array, or 'small' or 'large'.
+     * Color arrays map color names into arrays of R, G, B and optionally A values.
      */
-    function SetRGBArray ($which_color_array) 
-    { 
-        if ( is_array($which_color_array) ) {           // User defined array
+    function SetRGBArray($which_color_array)
+    {
+        if (is_array($which_color_array)) {           // User defined array
             $this->rgb_array = $which_color_array;
-            return TRUE;
         } elseif ($which_color_array == 'small') {      // Small predefined color array
             $this->rgb_array = array(
                 'white'          => array(255, 255, 255),
@@ -496,10 +505,12 @@ class PHPlot {
                 'azure1'         => array(240, 255, 255),
                 'aquamarine1'    => array(127, 255, 212)
                 );
-            return TRUE;
-        } elseif ($which_color_array === 'large')  {    // Large color array
-            include("./rgb.inc.php");
-            $this->rgb_array = $RGBArray;
+        } elseif ($which_color_array == 'large')  {    // Large color array
+            if (!@include('rgb.inc.php')) {
+                return $this->PrintError("SetRGBArray(): Large color map could not be loaded\n"
+                                       . "from 'rgb.inc.php'.");
+            }
+            $this->rgb_array = $ColorArray;
         } else {                                        // Default to black and white only.
             $this->rgb_array = array('white' => array(255, 255, 255), 'black' => array(0, 0, 0));
         }
@@ -507,559 +518,953 @@ class PHPlot {
         return TRUE;
     }
 
-    /*!
-     * Returns an array in R, G, B format 0-255
+    /*
+     * Parse a color description and return the color component values.
+     * Arguments:
+     *   $color_asked : The desired color description, in one of these forms:
+     *       Component notation: array(R, G, B) or array(R, G, B, A) with each
+     *          in the range described below for the return value.
+     *          Examples: (255,255,0)  (204,0,0,30)
+     *       Hex notation: "#RRGGBB" or "#RRGGBBAA" where each pair is a 2 digit hex number.
+     *          Examples: #FF00FF (magenta)   #0000FF40 (Blue with alpha=64/127)
+     *       Named color in the current colormap, with optional suffix ":alpha" for alpha value.
+     *          Examples:  blue   red:60  yellow:20
+     *   $alpha : optional default alpha value. This is applied to the color if it doesn't
+     *       already have an alpha value. If not supplied, colors are opaque (alpha=0) by default.
      *
-     *  \param color_asked array(R,G,B) or string (named color or '#AABBCC')
+     * Returns an array describing a color as (R, G, B, Alpha).
+     * R, G, and B are integers 0-255, and Alpha is 0 (opaque) to 127 (transparent).
+     * Note: This function should be considered 'protected', and is not documented for public use.
      */
-    function SetRGBColor($color_asked
+    function SetRGBColor($color_asked, $alpha = 0)
     {
-        if ($color_asked == '') { $color_asked = array(0, 0, 0); };
+        if (empty($color_asked)) {
+            $ret_val = array(0, 0, 0);
 
-        if ( count($color_asked) == 3 ) {    // already array of 3 rgb
-               $ret_val =  $color_asked;
-        } else {                             // asking for a color by string
-            if(substr($color_asked, 0, 1) == '#') {         // asking in #FFFFFF format. 
-                $ret_val = array(hexdec(substr($color_asked, 1, 2)), hexdec(substr($color_asked, 3, 2)), 
-                                  hexdec(substr($color_asked, 5, 2)));
-            } else {                                        // asking by color name
-                $ret_val = $this->rgb_array[$color_asked];
-            }
+        } elseif (is_array($color_asked) && (($n = count($color_asked)) == 3 || $n == 4) ) {
+            // Already an array of 3 or 4 elements:
+            $ret_val = $color_asked;
+
+        } elseif (preg_match('/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i',
+                             $color_asked, $ss)) {
+            // #RRGGBB or #RRGGBBAA notation:
+            $ret_val = array(hexdec($ss[1]), hexdec($ss[2]), hexdec($ss[3]));
+            if (isset($ss[4])) $ret_val[] = hexdec($ss[4]);
+
+        } elseif (isset($this->rgb_array[$color_asked])) {
+            // Color by name:
+            $ret_val = $this->rgb_array[$color_asked];
+
+        } elseif (preg_match('/(.+):([\d]+)$/', $color_asked, $ss)
+                  && isset($this->rgb_array[$ss[1]])) {
+            // Color by name with ":alpha" suffix, alpha is a decimal number:
+            $ret_val = $this->rgb_array[$ss[1]];
+            $ret_val[3] = (int)$ss[2];
+
+        } else {
+            return $this->PrintError("SetRGBColor(): Color '$color_asked' is not valid.");
         }
+
+        // Append alpha if not already provided for:
+        if (count($ret_val) == 3)
+            $ret_val[] = $alpha;
         return $ret_val;
     }
 
-
-    /*!
-     * Sets the colors for the data.
-     */
-    function SetDataColors($which_data = NULL, $which_border = NULL) 
-    {
-        if (is_null($which_data) && is_array($this->data_colors)) {
-            // use already set data_colors
-        } else if (! is_array($which_data)) {
-            $this->data_colors = ($which_data) ? array($which_data) : array('blue', 'red', 'green', 'orange');
+    /*
+     * Sets the colors for the data, with optional default alpha value (for PHPlot_truecolor only)
+     * Cases are:
+     *    SetDataColors(array(...))  : Use the supplied array as the color map.
+     *    SetDataColors(colorname)   : Use an array of just colorname as the color map.
+     *    SetDataColors() or SetDataColors(NULL) : Load default color map if no color map is already set.
+     *    SetDataColors('') or SetDataColors(False) : Load default color map (even if one is already set).
+     *  $which_border is passed to SetDataBorderColors, for backward compatibility.
+     *  $alpha is a default Alpha to apply to all data colors that do not have alpha.
+     *    The default for this is NULL, not 0, so we can tell if it was defaulted. But the effective
+     *    default value is 0 (opaque).
+     */
+    function SetDataColors($which_data = NULL, $which_border = NULL, $alpha = NULL)
+    {
+        if (is_array($which_data)) {
+            $colors = $which_data;  // Use supplied array
+        } elseif (!empty($which_data)) {
+            $colors = array($which_data);  // Use supplied single color
+        } elseif (empty($this->data_colors) || !is_null($which_data)) {
+            $colors = $this->default_colors;  // Use default color array
         } else {
-            $this->data_colors = $which_data;
+            // which_data is NULL or missing and a color array is already set.
+            // The existing color array is left alone, except that if $alpha is
+            // given this will replace the alpha value of each existing color.
+            // This makes SetDataColors(NULL, NULL, $alpha) work.
+            if (isset($alpha)) {
+                $n_colors = count($this->data_colors);
+                for ($i = 0; $i < $n_colors; $i++) {
+                    $this->data_colors[$i][3] = $alpha; // Component 3 = alpha value
+                }
+            }
+            // No need to reparse the colors or anything else.
+            return TRUE;
         }
 
-        $i = 0;
-        foreach ($this->data_colors as $col) {
-            $this->ndx_data_colors[$i] = $this->SetIndexColor($col);
-            $this->ndx_data_dark_colors[$i] = $this->SetIndexDarkColor($col);
-            $i++;
+        if (!isset($alpha))
+            $alpha = 0; // Actual default is opaque colors.
+
+        // Check each color and convert to array (r,g,b,a) form.
+        // Use the $alpha argument as a default for the alpha value of each color.
+        $this->data_colors = array();
+        foreach ($colors as $color) {
+            $color_array = $this->SetRGBColor($color, $alpha);
+            if (!$color_array) return FALSE; // SetRGBColor already did an error message.
+            $this->data_colors[] = $color_array;
         }
 
         // For past compatibility:
-        $this->SetDataBorderColors($which_border);
-    } // function SetDataColors()
-
+        return $this->SetDataBorderColors($which_border);
+    }
 
-    /*!
-     *
+    /*
+     * Set the colors for the bars and stacked bars outlines.
+     * Argument usage is similar to SetDataColors(), except the default is just black.
      */
     function SetDataBorderColors($which_br = NULL)
     {
-        if (is_null($which_br) && is_array($this->data_border_colors)) {
-            // use already set data_border_colors
-        } else if (! is_array($which_br)) {
-            // Create new array with specified color
-            $this->data_border_colors = ($which_br) ? array($which_br) : array('black');
+        if (is_array($which_br)) {
+            $colors = $which_br; // Use supplied array
+        } elseif (!empty($which_br)) {
+            $colors = array($which_br);  // Use supplied single color
+        } elseif (empty($this->data_border_colors) || !is_null($which_br)) {
+            $colors = array('black'); // Use default
         } else {
-            $this->data_border_colors = $which_br;
+            return TRUE; // Do nothing: which_br is NULL or missing and a color array is already set.
         }
 
-        $i = 0;
-        foreach($this->data_border_colors as $col) {
-            $this->ndx_data_border_colors[$i] = $this->SetIndexColor($col);
-            $i++;
+        // Check each color and convert to array (r,g,b,a) form.
+        $this->data_border_colors = array();
+        foreach ($colors as $color) {
+            $color_array = $this->SetRGBColor($color);
+            if (!$color_array) return FALSE; // SetRGBColor already did an error message.
+            $this->data_border_colors[] = $color_array;
         }
-    } // function SetDataBorderColors()
-
+        return TRUE;
+    }
 
-    /*!
+    /*
      * Sets the colors for the data error bars.
+     * Argument usage is the same as SetDataColors().
      */
     function SetErrorBarColors($which_err = NULL)
     {
-        if (is_null($which_err) && is_array($this->error_bar_colors)) {
-            // use already set error_bar_colors
-        } else if (! is_array($which_err)) {
-            $this->error_bar_colors = ($which_err) ? array($which_err) : array('black');
+        if (is_array($which_err)) {
+            $colors = $which_err;  // Use supplied array
+        } elseif (!empty($which_err)) {
+            $colors = array($which_err);  // Use supplied single color
+        } elseif (empty($this->error_bar_colors) || !is_null($which_err)) {
+            $colors = $this->default_colors;  // Use default color array
         } else {
-            $this->error_bar_colors = $which_err;
+            return TRUE; // Do nothing: which_err is NULL or missing and a color array is already set.
         }
 
-        $i = 0;
-        foreach($this->error_bar_colors as $col) {
-            $this->ndx_error_bar_colors[$i] = $this->SetIndexColor($col);
-            $i++;
-        }       
+        // Check each color and convert to array (r,g,b,a) form.
+        $this->error_bar_colors = array();
+        foreach ($colors as $color) {
+            $color_array = $this->SetRGBColor($color);
+            if (!$color_array) return FALSE; // SetRGBColor already did an error message.
+            $this->error_bar_colors[] = $color_array;
+        }
         return TRUE;
+    }
 
-    } // function SetErrorBarColors()
-
-
-    /*!
-     * Sets the default dashed style.
-     *  \param which_style A string specifying order of colored and transparent dots, 
-     *         i.e: '4-3' means 4 colored, 3 transparent; 
-     *              '2-3-1-2' means 2 colored, 3 transparent, 1 colored, 2 transparent.
+    /*
+     * Sets the default dashed line style.
+     *   $which_style : A string specifying the dashed line style, as alternating numbers
+     *         of the length (in pixels) of lines and spaces, separated by dashes.
+     *   For example: '2-3-1-2' means 2 dots of color, 3 transparent, 1 color, then 2 transparent.
+     *   This builds a string which will evaluate to an array of integers. Each colored dot
+     *   is  '$which_ndxcol' and each transparent dot is 'IMG_COLOR_TRANSPARENT'. When SetDashedStyle()
+     *   eval's this with $which_ndxcol set, the result is a GD line style array.
      */
-    function SetDefaultDashedStyle($which_style) 
+    function SetDefaultDashedStyle($which_style)
     {
-        // String: "numcol-numtrans-numcol-numtrans..."
+        // Explode "numcol-numtrans-numcol-numtrans..." into segment counts:
         $asked = explode('-', $which_style);
 
         if (count($asked) < 2) {
-            $this->DrawError("SetDefaultDashedStyle(): Wrong parameter '$which_style'.");
-            return FALSE;
+            return $this->PrintError("SetDefaultDashedStyle(): Wrong parameter '$which_style'.");
         }
 
-        // Build the string to be eval()uated later by SetDashedStyle()
-        $this->default_dashed_style = 'array( ';
-
-        $t = 0;
-        foreach($asked as $s) {
-            if ($t % 2 == 0) {
-                $this->default_dashed_style .= str_repeat('$which_ndxcol,', $s);
-            } else {
-                $this->default_dashed_style .= str_repeat('IMG_COLOR_TRANSPARENT,', $s);
-            }
-            $t++;
+        // Build the string to be evaluated later by SetDashedStyle() with $which_ndxcolor set.
+        $result = '';
+        $vals = array('$which_ndxcol,', 'IMG_COLOR_TRANSPARENT,');
+        $index = 0;
+        foreach ($asked as $n) {
+            $result .= str_repeat($vals[$index], $n);
+            $index = 1 - $index;
         }
-        // Remove trailing comma and add closing parenthesis
-        $this->default_dashed_style = substr($this->default_dashed_style, 0, -1);
-        $this->default_dashed_style .= ')';
+        $this->default_dashed_style = "array($result)";
 
         return TRUE;
     }
 
-
-    /*!
+    /*
      * Sets the style before drawing a dashed line. Defaults to $this->default_dashed_style
-     *   \param which_ndxcol Color index to be used.
+     *    $which_ndxcol : Color index to be used.
      */
-    function SetDashedStyle($which_ndxcol)
+    protected function SetDashedStyle($which_ndxcol)
     {
         // See SetDefaultDashedStyle() to understand this.
         eval ("\$style = $this->default_dashed_style;");
         return imagesetstyle($this->img, $style);
     }
 
-
-    /*!
-     * Sets line widths on a per-line basis.
+    /*
+     * Set line widths for each data set.
+     *   $which_lw : Array of line widths in pixels, or a single value to use for all data sets.
      */
     function SetLineWidths($which_lw=NULL)
     {
-        if (is_null($which_lw)) {
-            // Do nothing, use default value.
-        } else if (is_array($which_lw)) {
-            // Did we get an array with line widths?
-            $this->line_widths = $which_lw;
-        } else {
-            $this->line_widths = array($which_lw);
+        if (is_array($which_lw)) {
+            $this->line_widths = $which_lw; // Use provided array
+        } elseif (!is_null($which_lw)) {
+            $this->line_widths = array($which_lw); // Convert value to array
         }
         return TRUE;
     }
 
-    /*!
-     *
+    /*
+     * Set line style ('solid' or 'dashed') for each data set.
+     *   $which_ls : Array of keywords, or a single keyword to use for all data sets.
      */
     function SetLineStyles($which_ls=NULL)
     {
-        if (is_null($which_ls)) {
-            // Do nothing, use default value.
-        } else if (! is_array($which_ls)) {
-            // Did we get an array with line styles?
-            $this->line_styles = $which_ls;
-        } else {
+        if (is_array($which_ls)) {
+            $this->line_styles = $which_ls; // Use provided array
+        } elseif (!is_null($which_ls)) {
             $this->line_styles = ($which_ls) ? array($which_ls) : array('solid');
         }
         return TRUE;
     }
 
-
 /////////////////////////////////////////////
-//////////////                          FONTS
+//////////////                 TEXT and FONTS
 /////////////////////////////////////////////
 
-
-    /*!
-     * Sets number of pixels between lines of the same text.
+    /*
+     * Controls the line spacing of multi-line labels.
+     *   $which_spc : Line spacing factor for text
+     * For GD text, this is the number of pixels between lines.
+     * For TTF text, it controls line spacing in proportion to the normal
+     * spacing defined by the font.
      */
     function SetLineSpacing($which_spc)
     {
         $this->line_spacing = $which_spc;
+        return TRUE;
     }
 
-
-    /*!
-     * Enables use of TrueType fonts in the graph. Font initialisation methods
-     * depend on this setting, so when called, SetUseTTF() resets the font
-     * settings
+    /*
+     * Select the default font type to use.
+     *   $which_ttf : True to default to TrueType, False to default to GD (fixed) fonts.
+     * This also resets all font settings to the defaults.
      */
-    function SetUseTTF($which_ttf) 
+    function SetUseTTF($which_ttf)
     {
         $this->use_ttf = $which_ttf;
-        if ($which_ttf)
-            $this->SetDefaultFonts();
-        return TRUE;
+        return $this->SetDefaultFonts();
     }
 
-    /*!
+    /*
      * Sets the directory name to look into for TrueType fonts.
      */
     function SetTTFPath($which_path)
     {
-        // Maybe someone needs really dynamic config. He'll need this:
-        // clearstatcache();
-
-        if (is_dir($which_path) && is_readable($which_path)) {
-            $this->ttf_path = $which_path;
-            return TRUE;
-        } else {
-            $this->PrintError("SetTTFPath(): $which_path is not a valid path.");
-            return FALSE;
+        if (!is_dir($which_path) || !is_readable($which_path)) {
+            return $this->PrintError("SetTTFPath(): $which_path is not a valid path.");
         }
+        $this->ttf_path = $which_path;
+        return TRUE;
     }
 
-    /*!
+    /*
      * Sets the default TrueType font and updates all fonts to that.
+     * The default font might be a full path, or relative to the TTFPath,
+     * so let SetFont check that it exists.
+     * Side effects: Enables use of TrueType fonts as the default font type,
+     * and resets all font settings.
      */
     function SetDefaultTTFont($which_font)
     {
-        if (is_file($which_font) && is_readable($which_font)) {
-            $this->default_ttfont = $which_font;
-            return $this->SetDefaultFonts();
-        } else {
-            $this->PrintError("SetDefaultTTFont(): $which_font is not a valid font file.");
-            return FALSE;
+        $this->default_ttfont = $which_font;
+        return $this->SetUseTTF(TRUE);
+    }
+
+    /*
+     * Return the default TrueType font name. If no default has been set,
+     * this tries some likely candidates for a font which can be loaded.
+     * If it finds one that works, that becomes the default TT font.
+     * If there is no default and it cannot find a working font, it falls
+     * back to the original PHPlot default (which will not likely work either).
+     */
+    protected function GetDefaultTTFont()
+    {
+        if (!isset($this->default_ttfont)) {
+            // No default font yet. Try some common sans-serif fonts.
+            $fonts = array('LiberationSans-Regular.ttf',  // For Linux with a correct GD font search path
+                           'Verdana.ttf', 'Arial.ttf', 'Helvetica.ttf', // For Windows, maybe others
+                           'ttf-liberation/LiberationSans-Regular.ttf', // For Debian, Ubuntu, and friends
+                           'benjamingothic.ttf',  // Original PHPlot default 
+                          );
+            foreach ($fonts as $font) {
+                // First try the font name alone, to see if GD can find and load it.
+                if (@imagettfbbox(10, 0, $font, "1") !== False)
+                    break;
+                // If the font wasn't found, try it with the default TTF path in front.
+                $font_with_path = $this->ttf_path . DIRECTORY_SEPARATOR . $font;
+                if (@imagettfbbox(10, 0, $font_with_path, "1") !== False) {
+                    $font = $font_with_path;
+                    break;
+                }
+            }
+            // We either have a working font, or are using the last one regardless.
+            $this->default_ttfont = $font;
         }
+        return $this->default_ttfont;
     }
 
-    /*!
+    /*
      * Sets fonts to their defaults
      */
-    function SetDefaultFonts()
+    protected function SetDefaultFonts()
     {
         // TTF:
         if ($this->use_ttf) {
-            //$this->SetTTFPath(dirname($_SERVER['PHP_SELF']));
-            $this->SetTTFPath(getcwd());
-            $this->SetFont('generic', $this->default_ttfont, 8);
-            $this->SetFont('title', $this->default_ttfont, 14);
-            $this->SetFont('legend', $this->default_ttfont, 8);
-            $this->SetFont('x_label', $this->default_ttfont, 6);
-            $this->SetFont('y_label', $this->default_ttfont, 6);
-            $this->SetFont('x_title', $this->default_ttfont, 10);
-            $this->SetFont('y_title', $this->default_ttfont, 10);
-        }
-        // Fixed:
-        else {
-            $this->SetFont('generic', 2);
-            $this->SetFont('title', 5);
-            $this->SetFont('legend', 2);
-            $this->SetFont('x_label', 1);
-            $this->SetFont('y_label', 1);           
-            $this->SetFont('x_title', 3);
-            $this->SetFont('y_title', 3);
+            return $this->SetFont('generic', '', 8)
+                && $this->SetFont('title', '', 14)
+                && $this->SetFont('legend', '', 8)
+                && $this->SetFont('x_label', '', 6)
+                && $this->SetFont('y_label', '', 6)
+                && $this->SetFont('x_title', '', 10)
+                && $this->SetFont('y_title', '', 10);
         }
-
-        return TRUE;
+        // Fixed GD Fonts:
+        return $this->SetFont('generic', 2)
+            && $this->SetFont('title', 5)
+            && $this->SetFont('legend', 2)
+            && $this->SetFont('x_label', 1)
+            && $this->SetFont('y_label', 1)
+            && $this->SetFont('x_title', 3)
+            && $this->SetFont('y_title', 3);
     }
 
-    /*!
-     * Sets Fixed/Truetype font parameters.
-     *  \param $which_elem Is the element whose font is to be changed.
-     *         It can be one of 'title', 'legend', 'generic',
-     *         'x_label', 'y_label', x_title' or 'y_title'
-     *  \param $which_font Can be a number (for fixed font sizes) or
-     *         a string with the filename when using TTFonts.
-     *  \param $which_size Point size (TTF only)
-     * Calculates and updates internal height and width variables.
+    /*
+     * Select a fixed (GD) font for an element.
+     * This allows using a fixed font, even with SetUseTTF(True).
+     *    $which_elem : The element whose font is to be changed.
+     *       One of: title legend generic x_label y_label x_title y_title
+     *    $which_font : A GD font number 1-5
+     *    $which_spacing (optional) : Line spacing factor
      */
-    function SetFont($which_elem, $which_font, $which_size = 12) 
+    function SetFontGD($which_elem, $which_font, $which_spacing = NULL)
     {
-        // TTF:
-        if ($this->use_ttf) {
-            $path = $this->ttf_path.'/'.$which_font;
-
-            if (! is_file($path) || ! is_readable($path) ) {
-                $this->DrawError("SetFont(): True Type font $path doesn't exist");
-                return FALSE;
-            }
-
-            switch ($which_elem) {
-            case 'generic':
-                $this->generic_font['font'] = $path;
-                $this->generic_font['size'] = $which_size;
-                break;
-            case 'title':
-                $this->title_font['font'] = $path;
-                $this->title_font['size'] = $which_size;
-                break;
-            case 'legend':
-                $this->legend_font['font'] = $path;
-                $this->legend_font['size'] = $which_size;
-                break;
-            case 'x_label':
-                $this->x_label_font['font'] = $path;
-                $this->x_label_font['size'] = $which_size;
-                break;
-            case 'y_label':
-                $this->y_label_font['font'] = $path;
-                $this->y_label_font['size'] = $which_size;
-                break;                   
-            case 'x_title':
-                $this->x_title_font['font'] = $path;
-                $this->x_title_font['size'] = $which_size;
-                break;
-            case 'y_title':
-                $this->y_title_font['font'] = $path;
-                $this->y_title_font['size'] = $which_size;
-                break;
-            default:
-                $this->DrawError("SetFont(): Unknown element '$which_elem' specified.");
-                return FALSE;
-            }
-            return TRUE;
+        if ($which_font < 1 || 5 < $which_font) {
+            return $this->PrintError(__FUNCTION__ . ': Font size must be 1, 2, 3, 4 or 5');
+        }
+        if (!$this->CheckOption($which_elem,
+                                'generic, title, legend, x_label, y_label, x_title, y_title',
+                                __FUNCTION__)) {
+            return FALSE;
+        }
 
-        } 
+        // Store the font parameters: name/size, char cell height and width.
+        $this->fonts[$which_elem] = array('ttf' => FALSE,
+                                          'font' => $which_font,
+                                          'height' => ImageFontHeight($which_font),
+                                          'width' => ImageFontWidth($which_font),
+                                          'line_spacing' => $which_spacing);
+        return TRUE;
+    }
 
-        // Fixed fonts:
-        if ($which_font > 5 || $which_font < 0) {
-            $this->DrawError('SetFont(): Non-TTF font size must be 1, 2, 3, 4 or 5');
+    /*
+     * Select a TrueType font for an element.
+     * This allows using a TrueType font, even with SetUseTTF(False).
+     *    $which_elem : The element whose font is to be changed.
+     *       One of: title legend generic x_label y_label x_title y_title
+     *    $which_font : A TrueType font filename or pathname.
+     *    $which_size : Font point size.
+     *    $which_spacing (optional) : Line spacing factor
+     */
+    function SetFontTTF($which_elem, $which_font, $which_size = 12, $which_spacing = NULL)
+    {
+        if (!$this->CheckOption($which_elem,
+                                'generic, title, legend, x_label, y_label, x_title, y_title',
+                                __FUNCTION__)) {
             return FALSE;
         }
 
-        switch ($which_elem) {
-        case 'generic':
-            $this->generic_font['font'] = $which_font;
-            $this->generic_font['height'] = ImageFontHeight($which_font);
-            $this->generic_font['width'] = ImageFontWidth($which_font);
-            break;
-        case 'title':
-           $this->title_font['font'] = $which_font;
-           $this->title_font['height'] = ImageFontHeight($which_font);
-           $this->title_font['width'] = ImageFontWidth($which_font);
-           break;
-        case 'legend':
-            $this->legend_font['font'] = $which_font;
-            $this->legend_font['height'] = ImageFontHeight($which_font);
-            $this->legend_font['width'] = ImageFontWidth($which_font);
-            break;
-        case 'x_label':
-            $this->x_label_font['font'] = $which_font;
-            $this->x_label_font['height'] = ImageFontHeight($which_font);
-            $this->x_label_font['width'] = ImageFontWidth($which_font);
-            break;
-        case 'y_label':
-            $this->y_label_font['font'] = $which_font;
-            $this->y_label_font['height'] = ImageFontHeight($which_font);
-            $this->y_label_font['width'] = ImageFontWidth($which_font);
-            break;               
-        case 'x_title':
-            $this->x_title_font['font'] = $which_font;
-            $this->x_title_font['height'] = ImageFontHeight($which_font);
-            $this->x_title_font['width'] = ImageFontWidth($which_font);
-            break;
-        case 'y_title':
-            $this->y_title_font['font'] = $which_font;
-            $this->y_title_font['height'] = ImageFontHeight($which_font);
-            $this->y_title_font['width'] = ImageFontWidth($which_font);
-            break;
-        default:
-            $this->DrawError("SetFont(): Unknown element '$which_elem' specified.");
-            return FALSE;
+        // Empty font name means use the default font.
+        if (empty($which_font))
+            $which_font = $this->GetDefaultTTFont();
+        $path = $which_font;
+
+        // First try the font name directly, if not then try with path.
+        // Use GD imagettfbbox() to determine if this is a valid font.
+        // The return $bbox is used below, if valid.
+        if (($bbox = @imagettfbbox($which_size, 0, $path, "E")) === False) {
+            $path = $this->ttf_path . DIRECTORY_SEPARATOR . $which_font;
+            if (($bbox = @imagettfbbox($which_size, 0, $path, "E")) === False) {
+                return $this->PrintError(__FUNCTION__ . ": Can't find TrueType font $which_font");
+            }
         }
+
+        // Calculate the font height and inherent line spacing. TrueType fonts have this information
+        // internally, but PHP/GD has no way to directly access it. So get the bounding box size of
+        // an upper-case character without descenders, and the baseline-to-baseline height.
+        // Note: In practice, $which_size = $height, maybe +/-1 . But which_size is in points,
+        // and height is in pixels, and someday GD may be able to tell the difference.
+        // The character width is saved too, but not used by the normal text drawing routines - it
+        // isn't necessarily a fixed-space font. It is used in DrawLegend.
+        $height = $bbox[1] - $bbox[5];
+        $width = $bbox[2] - $bbox[0];
+        $bbox = ImageTTFBBox($which_size, 0, $path, "E\nE");
+        $spacing = $bbox[1] - $bbox[5] - 2 * $height;
+
+        // Store the font parameters:
+        $this->fonts[$which_elem] = array('ttf' => TRUE,
+                                          'font' => $path,
+                                          'size' => $which_size,
+                                          'height' => $height,
+                                          'width' => $width,
+                                          'spacing' => $spacing,
+                                          'line_spacing' => $which_spacing);
         return TRUE;
     }
 
-
-    /*!
-     * Returns an array with the size of the bounding box of an
-     * arbitrarily placed (rotated) TrueType text string.
+    /*
+     * Select Fixed/TrueType font for an element. Which type of font is
+     * selected depends on the $use_ttf class variable (see SetUseTTF()).
+     * Before PHPlot supported mixing font types, only this function and
+     * SetUseTTF were available to select an overall font type, but now
+     * SetFontGD() and SetFontTTF() can be used for mixing font types.
+     *    $which_elem : The element whose font is to be changed.
+     *       One of: title legend generic x_label y_label x_title y_title
+     *    $which_font : A number 1-5 for fixed fonts, or a TrueType font.
+     *    $which_size : Ignored for Fixed fonts, point size for TrueType.
+     *    $which_spacing (optional) : Line spacing factor
      */
-    function TTFBBoxSize($size, $angle, $font, $string) 
+    function SetFont($which_elem, $which_font, $which_size = 12, $line_spacing = NULL)
     {
-        // First, assume angle < 90
-        $arr = ImageTTFBBox($size, 0, $font, $string);
-        $flat_width  = $arr[2] - $arr[0];
-        $flat_height = abs($arr[3] - $arr[5]);
+        if ($this->use_ttf)
+            return $this->SetFontTTF($which_elem, $which_font, $which_size, $line_spacing);
+        return $this->SetFontGD($which_elem, $which_font, $line_spacing);
+    }
 
-        // Now the bounding box
-        $angle = deg2rad($angle);
-        $width  = ceil(abs($flat_width*cos($angle) + $flat_height*sin($angle))); //Must be integer
-        $height = ceil(abs($flat_width*sin($angle) + $flat_height*cos($angle))); //Must be integer
+    /*
+     * Return the inter-line spacing for a font.
+     * This is an internal function, used by ProcessText* and DrawLegend.
+     *   $font : A font array variable.
+     * Returns: Spacing, in pixels, between text lines.
+     */
+    protected function GetLineSpacing($font)
+    {
+        // Use the per-font line spacing preference, if set, else the global value:
+        if (isset($font['line_spacing']))
+            $line_spacing = $font['line_spacing'];
+        else
+            $line_spacing = $this->line_spacing;
 
-        return array($width, $height);
+        // For GD fonts, that is the spacing in pixels.
+        // For TTF, adjust based on the 'natural' font spacing (see SetFontTTF):
+        if ($font['ttf']) {
+            $line_spacing = (int)($line_spacing * $font['spacing'] / 6.0);
+        }
+        return $line_spacing;
     }
 
-
-    /*!
-     * Draws a string of text. Horizontal and vertical alignment are relative to
-     * to the drawing. That is: vertical text (90 deg) gets centered along y-axis 
-     * with v_align = 'center', and adjusted to the left of x-axis with h_align = 'right',
+    /*
+     * Text drawing and sizing functions:
+     * ProcessText is meant for use only by DrawText and SizeText.
+     *    ProcessText(True, ...)  - Draw a block of text
+     *    ProcessText(False, ...) - Just return ($width, $height) of
+     *       the orthogonal bounding box containing the text.
+     * ProcessText is further split into separate functions for GD and TTF
+     * text, due to the size of the code.
+     *
+     * Horizontal and vertical alignment are relative to the drawing. That is:
+     * vertical text (90 deg) gets centered along Y position with
+     * v_align = 'center', and adjusted to the right of X position with
+     * h_align = 'right'.  Another way to look at this is to say
+     * that text rotation happens first, then alignment.
+     *
+     * Original multiple lines code submitted by Remi Ricard.
+     * Original vertical code submitted by Marlin Viss.
+     *
+     * Text routines rewritten by ljb to fix alignment and position problems.
+     * Here is my explanation and notes. More information and pictures will be
+     * placed in the PHPlot Reference Manual.
+     *
+     *    + Process TTF text one line at a time, not as a block. (See below)
+     *    + Flipped top vs bottom vertical alignment. The usual interpretation
+     *  is: bottom align means bottom of the text is at the specified Y
+     *  coordinate. For some reason, PHPlot did left/right the correct way,
+     *  but had top/bottom reversed. I fixed it, and left the default valign
+     *  argument as bottom, but the meaning of the default value changed.
+     *
+     *    For GD font text, only single-line text is handled by GD, and the
+     *  basepoint is the upper left corner of each text line.
+     *    For TTF text, multi-line text could be handled by GD, with the text
+     *  basepoint at the lower left corner of the first line of text.
+     *  (Behavior of TTF drawing routines on multi-line text is not documented.)
+     *  But you cannot do left/center/right alignment on each line that way,
+     *  or proper line spacing.
+     *    Therefore, for either text type, we have to break up the text into
+     *  lines and position each line independently.
+     *
+     *    There are 9 alignment modes: Horizontal = left, center, or right, and
+     *  Vertical = top, center, or bottom. Alignment is interpreted relative to
+     *  the image, not as the text is read. This makes sense when you consider
+     *  for example X axis labels. They need to be centered below the marks
+     *  (center, top alignment) regardless of the text angle.
+     *  'Bottom' alignment really means baseline alignment.
+     *
+     *    GD font text is supported (by libgd) at 0 degrees and 90 degrees only.
+     *  Multi-line or single line text works with any of the 9 alignment modes.
+     *
+     *    TTF text can be at any angle. The 9 alignment modes work for all angles,
+     *  but the results might not be what you expect for multi-line text. See
+     *  the PHPlot Reference Manual for pictures and details. In short, alignment
+     *  applies to the orthogonal (aligned with X and Y axes) bounding box that
+     *  contains the text, and to each line in the multi-line text box. Since
+     *  alignment is relative to the image, 45 degree multi-line text aligns
+     *  differently from 46 degree text.
+     *
+     *    Note that PHPlot allows multi-line text for the 3 titles, and they
+     *  are only drawn at 0 degrees (main and X titles) or 90 degrees (Y title).
+     *  Data labels can also be multi-line, and they can be drawn at any angle.
+     *  -ljb 2007-11-03
      *
-     * \note Original multiple lines code submitted by Remi Ricard.
-     * \note Original vertical code submitted by Marlin Viss.
      */
-    function DrawText($which_font, $which_angle, $which_xpos, $which_ypos, $which_color, $which_text,
-                      $which_halign = 'left', $which_valign = 'bottom') 
-    {
-        // TTF:
-        if ($this->use_ttf) {
-            $size = $this->TTFBBoxSize($which_font['size'], $which_angle, $which_font['font'], $which_text);
-            $rads = deg2rad($which_angle);
 
-            if ($which_valign == 'center')
-                $which_ypos += $size[1]/2;
+    /*
+     * ProcessTextGD() - Draw or size GD fixed-font text.
+     * This is intended for use only by ProcessText().
+     *    $draw_it : True to draw the text, False to just return the orthogonal width and height.
+     *    $font : PHPlot font array (with 'ttf' = False) - see SetFontGD()
+     *    $angle : Text angle in degrees. GD only supports 0 and 90. We treat >= 45 as 90, else 0.
+     *    $x, $y : Reference point for the text (ignored if !$draw_it)
+     *    $color : GD color index to use for drawing the text (ignored if !$draw_it)
+     *    $text : The text to draw or size. Put a newline between lines.
+     *    $h_factor : Horizontal alignment factor: 0(left), .5(center), or 1(right) (ignored if !$draw_it)
+     *    $v_factor : Vertical alignment factor: 0(top), .5(center), or 1(bottom) (ignored if !$draw_it)
+     * Returns: True, if drawing text, or an array of ($width, $height) if not.
+     */
+    protected function ProcessTextGD($draw_it, $font, $angle, $x, $y, $color, $text, $h_factor, $v_factor)
+    {
+        // Extract font parameters:
+        $font_number = $font['font'];
+        $font_width = $font['width'];
+        $font_height = $font['height'];
+        $line_spacing = $this->GetLineSpacing($font);
+
+        // Break up the text into lines, trim whitespace, find longest line.
+        // Save the lines and length for drawing below.
+        $longest = 0;
+        foreach (explode("\n", $text) as $each_line) {
+            $lines[] = $line = trim($each_line);
+            $line_lens[] = $line_len = strlen($line);
+            if ($line_len > $longest) $longest = $line_len;
+        }
+        $n_lines = count($lines);
+
+        // Width, height are based on font size and longest line, line count respectively.
+        // These are relative to the text angle.
+        $total_width = $longest * $font_width;
+        $total_height = $n_lines * $font_height + ($n_lines - 1) * $line_spacing;
+
+        if (!$draw_it) {
+            if ($angle < 45) return array($total_width, $total_height);
+            return array($total_height, $total_width);
+        }
+
+        $interline_step = $font_height + $line_spacing; // Line-to-line step
 
-            if ($which_valign == 'bottom')
-                $which_ypos += $size[1];
+        if ($angle >= 45) {
+            // Vertical text (90 degrees):
+            // (Remember the alignment convention with vertical text)
+            // For 90 degree text, alignment factors change like this:
+            $temp = $v_factor;
+            $v_factor = $h_factor;
+            $h_factor = 1 - $temp;
 
-            if ($which_halign == 'center')
-                $which_xpos -= ($size[0]/2) * cos($rads);
+            $draw_func = 'ImageStringUp';
 
-            if ($which_halign == 'left')
-                $which_xpos += $size[0] * sin($rads);
+            // Rotation matrix "R" for 90 degrees (with Y pointing down):
+            $r00 = 0;  $r01 = 1;
+            $r10 = -1; $r11 = 0;
 
-            if ($which_halign == 'right')
-                $which_xpos -= $size[0] * cos($rads);
+        } else {
+            // Horizontal text (0 degrees):
+            $draw_func = 'ImageString';
 
-            ImageTTFText($this->img, $which_font['size'], $which_angle, 
-                         $which_xpos, $which_ypos, $which_color, $which_font['font'], $which_text);
+            // Rotation matrix "R" for 0 degrees:
+            $r00 = 1; $r01 = 0;
+            $r10 = 0; $r11 = 1;
         }
-        // Fixed fonts:
-        else {
-            // Split the text by its lines, and count them
-            $which_text = ereg_replace("\r", "", $which_text);
-            $str = split("\n", $which_text);
-            $nlines = count($str);
-            $spacing = $this->line_spacing * ($nlines - 1);
 
-            // Vertical text:
-            // (Remember the alignment convention with vertical text)
-            if ($which_angle == 90) {
-                // The text goes around $which_xpos.
-                if ($which_halign == 'center')
-                    $which_xpos -= ($nlines * ($which_font['height'] + $spacing))/2;
-
-                // Left alignment requires no modification to $xpos...
-                // Right-align it. $which_xpos designated the rightmost x coordinate.
-                else if ($which_halign == 'right')
-                    $which_xpos += ($nlines * ($which_font['height'] + $spacing));
-
-                $ypos = $which_ypos;
-                for($i = 0; $i < $nlines; $i++) { 
-                    // Center the text vertically around $which_ypos (each line)
-                    if ($which_valign == 'center')
-                        $ypos = $which_ypos + (strlen($str[$i]) * $which_font['width']) / 2;
-                    // Make the text finish (vertically) at $which_ypos
-                    if ($which_valign == 'bottom')
-                        $ypos = $which_ypos + strlen($str[$i]) * $which_font['width'];
-
-                    ImageStringUp($this->img, $which_font['font'],
-                                  $i * ($which_font['height'] + $spacing) + $which_xpos,
-                                  $ypos, $str[$i], $which_color);
-                } 
-            }
-            // Horizontal text:
-            else {
-                // The text goes above $which_ypos
-                if ($which_valign == 'top')
-                    $which_ypos -= $nlines * ($which_font['height'] + $spacing);
-                // The text is centered around $which_ypos
-                if ($which_valign == 'center')
-                    $which_ypos -= ($nlines * ($which_font['height'] + $spacing))/2;
-                // valign = 'bottom' requires no modification
-
-                $xpos = $which_xpos;
-                for($i = 0; $i < $nlines; $i++) {
-                    // center the text around $which_xpos
-                    if ($which_halign == 'center')
-                        $xpos = $which_xpos - (strlen($str[$i]) * $which_font['width'])/2;
-                    // make the text finish at $which_xpos
-                    if ($which_halign == 'right')
-                        $xpos = $which_xpos - strlen($str[$i]) * $which_font['width'];
-
-                    ImageString($this->img, $which_font['font'], $xpos, 
-                                $i * ($which_font['height'] + $spacing) + $which_ypos,
-                                $str[$i], $which_color);
-                }                 
+        // Adjust for vertical alignment (horizontal text) or horizontal alignment (vertical text):
+        $factor = (int)($total_height * $v_factor);
+        $xpos = $x - $r01 * $factor;
+        $ypos = $y - $r11 * $factor;
+
+        // Debug callback provides the bounding box:
+        if ($this->GetCallback('debug_textbox')) {
+            if ($angle >= 45) {
+                $bbox_width  = $total_height;
+                $bbox_height = $total_width;
+                $px = $xpos;
+                $py = $ypos - (1 - $h_factor) * $total_width;
+            } else {
+                $bbox_width  = $total_width;
+                $bbox_height = $total_height;
+                $px = $xpos - $h_factor * $total_width;
+                $py = $ypos;
             }
-        } 
-        return TRUE;
-    } // function DrawText()
+            $this->DoCallback('debug_textbox', $px, $py, $bbox_width, $bbox_height);
+        }
 
+        for ($i = 0; $i < $n_lines; $i++) {
 
-/////////////////////////////////////////////
-///////////            INPUT / OUTPUT CONTROL
-/////////////////////////////////////////////
+            // Adjust for alignment of this line within the text block:
+            $factor = (int)($line_lens[$i] * $font_width * $h_factor);
+            $x = $xpos - $r00 * $factor;
+            $y = $ypos - $r10 * $factor;
 
-    /*!
-     * Sets output file format.
+            // Call ImageString or ImageStringUp:
+            $draw_func($this->img, $font_number, $x, $y, $lines[$i], $color);
+
+            // Step to the next line of text. This is a rotation of (x=0, y=interline_spacing)
+            $xpos += $r01 * $interline_step;
+            $ypos += $r11 * $interline_step;
+        }
+        return TRUE;
+    }
+
+    /*
+     * ProcessTextTTF() - Draw or size TTF text.
+     * This is intended for use only by ProcessText().
+     *    $draw_it : True to draw the text, False to just return the orthogonal width and height.
+     *    $font : PHPlot font array (with 'ttf' = True) - see SetFontTTF()
+     *    $angle : Text angle in degrees.
+     *    $x, $y : Reference point for the text (ignored if !$draw_it)
+     *    $color : GD color index to use for drawing the text (ignored if !$draw_it)
+     *    $text : The text to draw or size. Put a newline between lines.
+     *    $h_factor : Horizontal alignment factor: 0(left), .5(center), or 1(right) (ignored if !$draw_it)
+     *    $v_factor : Vertical alignment factor: 0(top), .5(center), or 1(bottom) (ignored if !$draw_it)
+     * Returns: True, if drawing text, or an array of ($width, $height) if not.
+     */
+    protected function ProcessTextTTF($draw_it, $font, $angle, $x, $y, $color, $text, $h_factor, $v_factor)
+    {
+        // Extract font parameters (see SetFontTTF):
+        $font_file = $font['font'];
+        $font_size = $font['size'];
+        $font_height = $font['height'];
+        $line_spacing = $this->GetLineSpacing($font);
+
+        // Break up the text into lines, trim whitespace.
+        // Calculate the total width and height of the text box at 0 degrees.
+        // Save the trimmed lines and their widths for later when drawing.
+        // To get uniform spacing, don't use the actual line heights.
+        // Total height = Font-specific line heights plus inter-line spacing.
+        // Total width = width of widest line.
+        // Last Line Descent is the offset from the bottom to the text baseline.
+        // Note: For some reason, ImageTTFBBox uses (-1,-1) as the reference point.
+        //   So 1+bbox[1] is the baseline to bottom distance.
+        $total_width = 0;
+        $lastline_descent = 0;
+        foreach (explode("\n", $text) as $each_line) {
+            $lines[] = $line = trim($each_line);
+            $bbox = ImageTTFBBox($font_size, 0, $font_file, $line);
+            $line_widths[] = $width = $bbox[2] - $bbox[0];
+            if ($width > $total_width) $total_width = $width;
+            $lastline_descent = 1 + $bbox[1];
+        }
+        $n_lines = count($lines);
+        $total_height = $n_lines * $font_height + ($n_lines - 1) * $line_spacing;
+
+        // Calculate the rotation matrix for the text's angle. Remember that GD points Y down,
+        // so the sin() terms change sign.
+        $theta = deg2rad($angle);
+        $cos_t = cos($theta);
+        $sin_t = sin($theta);
+        $r00 = $cos_t;    $r01 = $sin_t;
+        $r10 = -$sin_t;   $r11 = $cos_t;
+
+        // Make a bounding box of the right size, with upper left corner at (0,0).
+        // By convention, the point order is: LL, LR, UR, UL.
+        // Note this is still working with the text at 0 degrees.
+        // When sizing text (SizeText), use the overall size with descenders.
+        //   This tells the caller how much room to leave for the text.
+        // When drawing text (DrawText), use the size without descenders - that
+        //   is, down to the baseline. This is for accurate positioning.
+        $b[0] = 0;
+        if ($draw_it) {
+            $b[1] = $total_height;
+        } else {
+            $b[1] = $total_height + $lastline_descent;
+        }
+        $b[2] = $total_width;  $b[3] = $b[1];
+        $b[4] = $total_width;  $b[5] = 0;
+        $b[6] = 0;             $b[7] = 0;
+
+        // Rotate the bounding box, then offset to the reference point:
+        for ($i = 0; $i < 8; $i += 2) {
+            $x_b = $b[$i];
+            $y_b = $b[$i+1];
+            $c[$i]   = $x + $r00 * $x_b + $r01 * $y_b;
+            $c[$i+1] = $y + $r10 * $x_b + $r11 * $y_b;
+        }
+
+        // Get an orthogonal (aligned with X and Y axes) bounding box around it, by
+        // finding the min and max X and Y:
+        $bbox_ref_x = $bbox_max_x = $c[0];
+        $bbox_ref_y = $bbox_max_y = $c[1];
+        for ($i = 2; $i < 8; $i += 2) {
+            $x_b = $c[$i];
+            if ($x_b < $bbox_ref_x) $bbox_ref_x = $x_b;
+            elseif ($bbox_max_x < $x_b) $bbox_max_x = $x_b;
+            $y_b = $c[$i+1];
+            if ($y_b < $bbox_ref_y) $bbox_ref_y = $y_b;
+            elseif ($bbox_max_y < $y_b) $bbox_max_y = $y_b;
+        }
+        $bbox_width = $bbox_max_x - $bbox_ref_x;
+        $bbox_height = $bbox_max_y - $bbox_ref_y;
+
+        if (!$draw_it) {
+            // Return the bounding box, rounded up (so it always contains the text):
+            return array((int)ceil($bbox_width), (int)ceil($bbox_height));
+        }
+
+        $interline_step = $font_height + $line_spacing; // Line-to-line step
+
+        // Calculate the offsets from the supplied reference point to the
+        // upper-left corner of the text.
+        // Start at the reference point at the upper left corner of the bounding
+        // box (bbox_ref_x, bbox_ref_y) then adjust it for the 9 point alignment.
+        // h,v_factor are 0,0 for top,left, .5,.5 for center,center, 1,1 for bottom,right.
+        //    $off_x = $bbox_ref_x + $bbox_width * $h_factor - $x;
+        //    $off_y = $bbox_ref_y + $bbox_height * $v_factor - $y;
+        // Then use that offset to calculate back to the supplied reference point x, y
+        // to get the text base point.
+        //    $qx = $x - $off_x;
+        //    $qy = $y - $off_y;
+        // Reduces to:
+        $qx = 2 * $x - $bbox_ref_x - $bbox_width * $h_factor;
+        $qy = 2 * $y - $bbox_ref_y - $bbox_height * $v_factor;
+
+        // Check for debug callback. Don't calculate bounding box unless it is wanted.
+        if ($this->GetCallback('debug_textbox')) {
+            // Calculate the orthogonal bounding box coordinates for debug testing.
+
+            // qx, qy is upper left corner relative to the text.
+            // Calculate px,py: upper left corner (absolute) of the bounding box.
+            // There are 4 equation sets for this, depending on the quadrant:
+            if ($sin_t > 0) {
+                if ($cos_t > 0) {
+                    // Quadrant: 0d - 90d:
+                    $px = $qx; $py = $qy - $total_width * $sin_t;
+                } else {
+                    // Quadrant: 90d - 180d:
+                   $px = $qx + $total_width * $cos_t; $py = $qy - $bbox_height;
+                }
+            } else {
+                if ($cos_t < 0) {
+                    // Quadrant: 180d - 270d:
+                    $px = $qx - $bbox_width; $py = $qy + $total_height * $cos_t;
+                } else {
+                    // Quadrant: 270d - 360d:
+                    $px = $qx + $total_height * $sin_t; $py = $qy;
+                }
+            }
+            $this->DoCallback('debug_textbox', $px, $py, $bbox_width, $bbox_height);
+        }
+
+        // Since alignment is applied after rotation, which parameter is used
+        // to control alignment of each line within the text box varies with
+        // the angle.
+        //   Angle (degrees):       Line alignment controlled by:
+        //  -45 < angle <= 45          h_align
+        //   45 < angle <= 135         reversed v_align
+        //  135 < angle <= 225         reversed h_align
+        //  225 < angle <= 315         v_align
+        if ($cos_t >= $sin_t) {
+            if ($cos_t >= -$sin_t) $line_align_factor = $h_factor;
+            else $line_align_factor = $v_factor;
+        } else {
+            if ($cos_t >= -$sin_t) $line_align_factor = 1-$v_factor;
+            else $line_align_factor = 1-$h_factor;
+        }
+
+        // Now we have the start point, spacing and in-line alignment factor.
+        // We are finally ready to start drawing the text, line by line.
+        for ($i = 0; $i < $n_lines; $i++) {
+
+            // For drawing TTF text, the reference point is the left edge of the
+            // text baseline (not the lower left corner of the bounding box).
+            // The following also adjusts for horizontal (relative to
+            // the text) alignment of the current line within the box.
+            // What is happening is rotation of this vector by the text angle:
+            //    (x = (total_width - line_width) * factor, y = font_height)
+
+            $width_factor = ($total_width - $line_widths[$i]) * $line_align_factor;
+            $rx = $qx + $r00 * $width_factor + $r01 * $font_height;
+            $ry = $qy + $r10 * $width_factor + $r11 * $font_height;
+
+            // Finally, draw the text:
+            ImageTTFText($this->img, $font_size, $angle, $rx, $ry, $color, $font_file, $lines[$i]);
+
+            // Step to position of next line.
+            // This is a rotation of (x=0,y=height+line_spacing) by $angle:
+            $qx += $r01 * $interline_step;
+            $qy += $r11 * $interline_step;
+        }
+        return TRUE;
+    }
+
+    /*
+     * ProcessText() - Wrapper for ProcessTextTTF() and ProcessTextGD(). See notes above.
+     * This is intended for use from within PHPlot only, and only by DrawText() and SizeText().
+     *    $draw_it : True to draw the text, False to just return the orthogonal width and height.
+     *    $font : PHPlot font array, or NULL or empty string to use 'generic'
+     *    $angle : Text angle in degrees
+     *    $x, $y : Reference point for the text (ignored if !$draw_it)
+     *    $color : GD color index to use for drawing the text (ignored if !$draw_it)
+     *    $text : The text to draw or size. Put a newline between lines.
+     *    $halign : Horizontal alignment: left, center, or right (ignored if !$draw_it)
+     *    $valign : Vertical alignment: top, center, or bottom (ignored if !$draw_it)
+     *      Note: Alignment is relative to the image, not the text.
+     * Returns: True, if drawing text, or an array of ($width, $height) if not.
+     */
+    protected function ProcessText($draw_it, $font, $angle, $x, $y, $color, $text, $halign, $valign)
+    {
+        // Empty text case:
+        if ($text === '') {
+            if ($draw_it) return TRUE;
+            return array(0, 0);
+        }
+
+        // Calculate width and height offset factors using the alignment args:
+        if ($valign == 'top') $v_factor = 0;
+        elseif ($valign == 'center') $v_factor = 0.5;
+        else $v_factor = 1.0; // 'bottom'
+        if ($halign == 'left') $h_factor = 0;
+        elseif ($halign == 'center') $h_factor = 0.5;
+        else $h_factor = 1.0; // 'right'
+
+        // Apply a default font. This is mostly for external (callback) users.
+        if (empty($font)) $font = $this->fonts['generic'];
+
+        if ($font['ttf']) {
+            return $this->ProcessTextTTF($draw_it, $font, $angle, $x, $y, $color, $text,
+                                         $h_factor, $v_factor);
+        }
+        return $this->ProcessTextGD($draw_it, $font, $angle, $x, $y, $color, $text, $h_factor, $v_factor);
+    }
+
+    /*
+     * Draws a block of text. See comments above before ProcessText().
+     *    $which_font : PHPlot font array, or NULL or empty string to use 'generic'
+     *    $which_angle : Text angle in degrees
+     *    $which_xpos, $which_ypos: Reference point for the text
+     *    $which_color : GD color index to use for drawing the text
+     *    $which_text :  The text to draw, with newlines (\n) between lines.
+     *    $which_halign : Horizontal (relative to the image) alignment: left, center, or right.
+     *    $which_valign : Vertical (relative to the image) alignment: top, center, or bottom.
+     * Note: This function should be considered 'protected', and is not documented for public use.
+     */
+    function DrawText($which_font, $which_angle, $which_xpos, $which_ypos, $which_color, $which_text,
+                      $which_halign = 'left', $which_valign = 'bottom')
+    {
+        return $this->ProcessText(TRUE,
+                           $which_font, $which_angle, $which_xpos, $which_ypos,
+                           $which_color, $which_text, $which_halign, $which_valign);
+    }
+
+    /*
+     * Returns the size of block of text. This is the orthogonal width and height of a bounding
+     * box aligned with the X and Y axes of the text. Only for angle=0 is this the actual
+     * width and height of the text block, but for any angle it is the amount of space needed
+     * to contain the text.
+     *    $which_font : PHPlot font array, or NULL or empty string to use 'generic'
+     *    $which_angle : Text angle in degrees
+     *    $which_text :  The text to draw, with newlines (\n) between lines.
+     * Returns a two element array with: $width, $height.
+     * This is just a wrapper for ProcessText() - see above.
+     * Note: This function should be considered 'protected', and is not documented for public use.
+     */
+    function SizeText($which_font, $which_angle, $which_text)
+    {
+        // Color, position, and alignment are not used when calculating the size.
+        return $this->ProcessText(FALSE,
+                           $which_font, $which_angle, 0, 0, 1, $which_text, '', '');
+    }
+
+/////////////////////////////////////////////
+///////////            INPUT / OUTPUT CONTROL
+/////////////////////////////////////////////
+
+    /*
+     * Sets output file format to $format (jpg, png, ...)
      */
     function SetFileFormat($format)
     {
         $asked = $this->CheckOption($format, 'jpg, png, gif, wbmp', __FUNCTION__);
-
+        if (!$asked) return FALSE;
         switch ($asked) {
         case 'jpg':
-            if (imagetypes() & IMG_JPG)
-                $this->file_format = 'jpg';
-                return TRUE;
+            $format_test = IMG_JPG;
             break;
         case 'png':
-            if (imagetypes() & IMG_PNG)
-                $this->file_format = 'png';
-                return TRUE;
+            $format_test = IMG_PNG;
             break;
         case 'gif':
-            if (imagetypes() & IMG_GIF)
-                $this->file_format = 'gif';
-                return TRUE;
+            $format_test = IMG_GIF;
             break;
         case 'wbmp':
-            if (imagetypes() & IMG_WBMP)
-                $this->file_format = 'wbmp';
-                return TRUE;
+            $format_test = IMG_WBMP;
             break;
-        default:
-            $this->PrintError("SetFileFormat():File format '$format' not supported");
-            return FALSE;
         }
+        if (!(imagetypes() & $format_test)) {
+            return $this->PrintError("SetFileFormat(): File format '$format' not supported");
+        }
+        $this->file_format = $asked;
+        return TRUE;
     }
 
-
-    /*!
+    /*
      * Selects an input file to be used as graph background and scales or tiles this image
      * to fit the sizes.
-     *  \param input_file string Path to the file to be used (jpeg, png and gif accepted)
-     *  \param mode       string 'centeredtile', 'tile', 'scale' (the image to the graph's size)
+     *   $input_file : Path to the file to be used (jpeg, png and gif accepted)
+     *   $mode : 'centeredtile', 'tile', or 'scale' (the image to the graph's size)
      */
     function SetBgImage($input_file, $mode='centeredtile')
     {
         $this->bgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__);
         $this->bgimg  = $input_file;
+        return (boolean)$this->bgmode;
     }
 
-    /*!
+    /*
      * Selects an input file to be used as plot area background and scales or tiles this image
      * to fit the sizes.
-     *  \param input_file string Path to the file to be used (jpeg, png and gif accepted)
-     *  \param mode       string 'centeredtile', 'tile', 'scale' (the image to the graph's size)
+     *   $input_file : Path to the file to be used (jpeg, png and gif accepted)
+     *   $mode : 'centeredtile', 'tile', or 'scale' (the image to the graph's size)
      */
     function SetPlotAreaBgImage($input_file, $mode='tile')
     {
         $this->plotbgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__);
         $this->plotbgimg  = $input_file;
+        return (boolean)$this->plotbgmode;
     }
 
-
-    /*!
+    /*
      * Sets the name of the file to be used as output file.
      */
     function SetOutputFile($which_output_file)
@@ -1068,7 +1473,7 @@ class PHPlot {
         return TRUE;
     }
 
-    /*!
+    /*
      * Sets the output image as 'inline', that is: no Content-Type headers are sent
      * to the browser. Needed if you want to embed the images.
      */
@@ -1078,10 +1483,8 @@ class PHPlot {
         return TRUE;
     }
 
-
-    /*!
-     * Performs the actual outputting of the generated graph, and
-     * destroys the image resource.
+    /*
+     * Performs the actual outputting of the generated graph.
      */
     function PrintImage()
     {
@@ -1093,224 +1496,476 @@ class PHPlot {
             header('Pragma: no-cache');
         }
 
-        switch($this->file_format) {
+        switch ($this->file_format) {
         case 'png':
-            if (! $this->is_inline) {
-                Header('Content-type: image/png');
-            }
-            if ($this->is_inline && $this->output_file != '') {
-                ImagePng($this->img, $this->output_file);
-            } else {
-                ImagePng($this->img);
-            }
+            $mime_type = 'image/png';
+            $output_f = 'imagepng';
             break;
         case 'jpg':
-            if (! $this->is_inline) {
-                Header('Content-type: image/jpeg');
-            }
-            if ($this->is_inline && $this->output_file != '') {
-                ImageJPEG($this->img, $this->output_file);
-            } else {
-                ImageJPEG($this->img);
-            }
+            $mime_type = 'image/jpeg';
+            $output_f = 'imagejpeg';
             break;
         case 'gif':
-            if (! $this->is_inline) {
-                Header('Content-type: image/gif');
-            }
-            if ($this->is_inline && $this->output_file != '') {
-                ImageGIF($this->img, $this->output_file);
-            } else {
-                ImageGIF($this->img);
-            }
-
+            $mime_type = 'image/gif';
+            $output_f = 'imagegif';
             break;
-        case 'wbmp':        // wireless bitmap, 2 bit.
-            if (! $this->is_inline) {
-                Header('Content-type: image/wbmp');
-            }
-            if ($this->is_inline && $this->output_file != '') {
-                ImageWBMP($this->img, $this->output_file);
-            } else {
-                ImageWBMP($this->img);
-            }
-
+        case 'wbmp':
+            $mime_type = 'image/wbmp';
+            $output_f = 'imagewbmp';
             break;
         default:
-            $this->PrintError('PrintImage(): Please select an image type!');
-            break;
+            return $this->PrintError('PrintImage(): Please select an image type!');
+        }
+        if (!$this->is_inline) {
+            Header("Content-type: $mime_type");
+        }
+        if ($this->is_inline && $this->output_file != '') {
+            $output_f($this->img, $this->output_file);
+        } else {
+            $output_f($this->img);
         }
         return TRUE;
     }
 
-    /*! 
-     * Prints an error message to stdout and dies 
+    /*
+     *  Error handling for 'fatal' errors:
+     *   $error_message       Text of the error message
+     *  Standard output from PHPlot is expected to be an image file, such as
+     *  when handling an <img> tag browser request. So it is not permitted to
+     *  output text to standard output. (You should have display_errors=off)
+     *  Here is how PHPlot handles fatal errors:
+     *    + Write the error message into an image, and output the image.
+     *    + If no image can be output, write nothing and produce an HTTP
+     *      error header.
+     *    + Trigger a user-level error containing the error message.
+     *      If no error handler was set up, the script will log the
+     *      error and exit with non-zero status.
+     *
+     *  PrintError() and DrawError() are now equivalent. Both are provided for
+     *  compatibility. (In earlier releases, PrintError sent the message to
+     *  stdout only, and DrawError sent it in an image only.)
+     *
+     *  This function does not return, unless the calling script has set up
+     *  an error handler which does not exit. In that case, PrintError will
+     *  return False. But not all of PHPlot will handle this correctly, so
+     *  it is probably a bad idea for an error handler to return.
      */
-    function PrintError($error_message) 
+    protected function PrintError($error_message)
     {
-        echo "<p><b>Fatal error</b>: $error_message<p>";
-        die;
-    }
+        // Be sure not to loop recursively, e.g. PrintError - PrintImage - PrintError.
+        if (isset($this->in_error)) return FALSE;
+        $this->in_error = TRUE;
 
-    /*!
-     * Prints an error message inline into the generated image and draws it centered
-     * around the given coordinates (defaults to center of the image)
-     *   \param error_message Message to be drawn
-     *   \param where_x       X coordinate
-     *   \param where_y       Y coordinate
-     */
-    function DrawError($error_message, $where_x = NULL, $where_y = NULL) 
-    {
-        if (! $this->img)
-            $this->PrintError('_DrawError(): Warning, no image resource allocated. '.
-                              'The message to be written was: '.$error_message);
+        // Output an image containing the error message:
+        if (!empty($this->img)) {
+            $ypos = $this->image_height/2;
+            $xpos = $this->image_width/2;
+            $bgcolor = ImageColorResolve($this->img, 255, 255, 255);
+            $fgcolor = ImageColorResolve($this->img, 0, 0, 0);
+            ImageFilledRectangle($this->img, 0, 0, $this->image_width, $this->image_height, $bgcolor);
 
-        $ypos = (! $where_y) ? $this->image_height/2 : $where_y;
-        $xpos = (! $where_x) ? $this->image_width/2 : $where_x;
-        ImageRectangle($this->img, 0, 0, $this->image_width, $this->image_height,
-                       ImageColorAllocate($this->img, 255, 255, 255));
+            // Switch to built-in fonts, in case of error with TrueType fonts:
+            $this->SetUseTTF(FALSE);
 
-        $this->DrawText($this->generic_font, 0, $xpos, $ypos, ImageColorAllocate($this->img, 0, 0, 0),
-                        $error_message, 'center', 'center');
+            $this->DrawText($this->fonts['generic'], 0, $xpos, $ypos, $fgcolor,
+                            wordwrap($error_message), 'center', 'center');
+
+            $this->PrintImage();
+        } elseif (! $this->is_inline) {
+            Header('HTTP/1.0 500 Internal Server Error');
+        }
+        trigger_error($error_message, E_USER_ERROR);
+        unset($this->in_error);
+        return FALSE;  // In case error handler returns, rather than doing exit().
+    }
 
-        $this->PrintImage();
-        exit;
-//        return TRUE;
+    /*
+     * Display an error message and exit.
+     * This is provided for backward compatibility only. Use PrintError() instead.
+     *   $error_message       Text of the error message
+     *   $where_x, $where_y   Ignored, provided for compatibility.
+     */
+    protected function DrawError($error_message, $where_x = NULL, $where_y = NULL)
+    {
+        return $this->PrintError($error_message);
     }
 
 /////////////////////////////////////////////
 ///////////                            LABELS
 /////////////////////////////////////////////
 
-
-    /*!
-     * Sets position for X labels following data points.
+    /*
+     * Sets position for X data labels. For most plot types, these are
+     * labels along the X axis (but different from X tick labels).
+     *    Accepted positions are: plotdown, plotup, both, none.
+     * For horizontal bar charts, these are the labels right (or left) of the bars.
+     *    Accepted positions are: plotin, plotstack, none.
      */
     function SetXDataLabelPos($which_xdlp)
     {
-        $this->x_data_label_pos = $this->CheckOption($which_xdlp, 'plotdown, plotup, both, xaxis, all, none',
-                                                      __FUNCTION__);
-        if ($which_xdlp != 'none')
-            $this->x_tick_label_pos = 'none';
+        $which_xdlp = $this->CheckOption($which_xdlp, 'plotdown, plotup, both, none, plotin, plotstack',
+                                         __FUNCTION__);
+        if (!$which_xdlp) return FALSE;
+        $this->x_data_label_pos = $which_xdlp;
 
         return TRUE;
     }
 
-    /*!
-     * Sets position for Y labels following data points.
+    /*
+     * Sets position for Y data labels.
+     * For bars and stackedbars, these are labels above the bars with the Y values.
+     *    Accepted positions are: plotin, plotstack, none.
+     * For horizontal bar charts, these are the labels along the Y axis.
+     *    Accepted positions are: plotleft, plotright, both, none.
      */
-    function SetYDataLabelPos($which_ydlp) 
+    function SetYDataLabelPos($which_ydlp)
     {
-        $this->y_data_label_pos = $this->CheckOption($which_ydlp, 'plotleft, plotright, both, yaxis, all, none',
-                                                      __FUNCTION__);
-        if ($which_ydlp != 'none')
-            $this->y_tick_label_pos = 'none';
+        $which_ydlp = $this->CheckOption($which_ydlp, 'plotleft, plotright, both, none, plotin, plotstack',
+                                          __FUNCTION__);
+        if (!$which_ydlp) return FALSE;
+        $this->y_data_label_pos = $which_ydlp;
 
         return TRUE;
     }
 
-
-    /*!
-     * Sets position for X labels following ticks (hence grid lines)
+    /*
+     * Set position for X tick labels.
      */
-    function SetXTickLabelPos($which_xtlp) 
+    function SetXTickLabelPos($which_xtlp)
     {
-        $this->x_tick_label_pos = $this->CheckOption($which_xtlp, 'plotdown, plotup, both, xaxis, all, none',
-                                                      __FUNCTION__);
-        if ($which_xtlp != 'none')
-            $this->x_data_label_pos = 'none';
+        $which_xtlp = $this->CheckOption($which_xtlp, 'plotdown, plotup, both, xaxis, none',
+                                         __FUNCTION__);
+        if (!$which_xtlp) return FALSE;
+        $this->x_tick_label_pos = $which_xtlp;
 
         return TRUE;
     }
 
-    /*!
-     * Sets position for Y labels following ticks (hence grid lines)
+    /*
+     * Set position for Y tick labels.
      */
-    function SetYTickLabelPos($which_ytlp) 
+    function SetYTickLabelPos($which_ytlp)
     {
-        $this->y_tick_label_pos = $this->CheckOption($which_ytlp, 'plotleft, plotright, both, yaxis, all, none',
-                                                      __FUNCTION__);
-        if ($which_ytlp != 'none')
-            $this->y_data_label_pos = 'none';
+        $which_ytlp = $this->CheckOption($which_ytlp, 'plotleft, plotright, both, yaxis, none',
+                                         __FUNCTION__);
+        if (!$which_ytlp) return FALSE;
+        $this->y_tick_label_pos = $which_ytlp;
 
         return TRUE;
     }
 
-    /*!
-     * Sets type for tick and data labels on X axis.
-     * \note 'title' type left for backwards compatibility.
+    /*
+     * Set formatting type for tick and data labels on X or Y axis.
+     * This implements the 4 functions Set[XY]LabelType() and Set[XY]DataLabelType().
+     *    $mode  : 'x', 'y', 'xd', or 'yd' - which type of label to configure.
+     *        'x' and 'y' set the type for tick labels, and the default type for data labels
+     *        if they are not separately configured. 'xd' and 'yd' set the type for data labels.
+     *    $args  : Variable arguments, passed as an array.
+     *       [0] = $type (required) : Label type. 'data', 'time', 'printf', or 'custom'.
+     *     For type 'data':
+     *       [1] = $precision (optional). Numeric precision. Can also be set by SetPrecision[XY]().
+     *       [2] = $prefix (optional) - prefix string for labels.
+     *       [3] = $suffix (optional) - suffix string for labels. This replaces data_units_text.
+     *     For type 'time':
+     *       [1] = $format for strftime (optional). Can also be set by Set[XY]TimeFormat().
+     *     For type 'printf':
+     *       [1] = $format (optional) for sprintf.
+     *     For type 'custom':
+     *       [1] = $callback (required) - Custom function or array of (instance,method) to call.
+     *       [2] = $argument (optional) - Pass-through argument for the formatting function.
+     */
+    protected function SetLabelType($mode, $args)
+    {
+        if (!$this->CheckOption($mode, 'x, y, xd, yd', __FUNCTION__))
+            return FALSE;
+
+        $type = isset($args[0]) ? $args[0] : '';
+        $format =& $this->label_format[$mode];  // Shorthand reference to format storage variables
+        switch ($type) {
+        case 'data':
+            if (isset($args[1]))
+                $format['precision'] = $args[1];
+            elseif (!isset($format['precision']))
+                $format['precision'] = 1;
+            $format['prefix'] = isset($args[2]) ? $args[2] : '';
+            $format['suffix'] = isset($args[3]) ? $args[3] : '';
+            break;
+
+        case 'time':
+            if (isset($args[1]))
+                $format['time_format'] = $args[1];
+            elseif (!isset($format['time_format']))
+                $format['time_format'] = '%H:%M:%S';
+            break;
+
+        case 'printf':
+            if (isset($args[1]))
+                $format['printf_format'] = $args[1];
+            elseif (!isset($format['printf_format']))
+                $format['printf_format'] = '%e';
+            break;
+
+        case 'custom':
+            if (isset($args[1])) {
+                $format['custom_callback'] = $args[1];
+                $format['custom_arg'] = isset($args[2]) ? $args[2] : NULL;
+            } else {
+                $type = ''; // Error, 'custom' without a function, set to no-format mode.
+            }
+            break;
+
+        case '':
+        case 'title':   // Retained for backwards compatibility?
+            break;
+
+        default:
+            $this->CheckOption($type, 'data, time, printf, custom', __FUNCTION__);
+            $type = '';
+        }
+        $format['type'] = $type;
+        return (boolean)$type;
+    }
+
+    /*
+     * Select label formating for X tick labels, and for X data labels
+     * (unless SetXDataLabelType was called).
+     * See SetLabelType() for details.
      */
-    function SetXLabelType($which_xlt) 
+    function SetXLabelType()  // Variable arguments: $type, ...
     {
-        $this->x_label_type = $this->CheckOption($which_xlt, 'data, time, title', __FUNCTION__);
-        return TRUE;
+        $args = func_get_args();
+        return $this->SetLabelType('x', $args);
     }
 
-    /*!
-     * Sets type for tick and data labels on Y axis.
+    /*
+     * Select label formatting for X data labels, overriding SetXLabelType.
+     */
+    function SetXDataLabelType()  // Variable arguments: $type, ...
+    {
+        $args = func_get_args();
+        return $this->SetLabelType('xd', $args);
+    }
+
+    /*
+     * Select label formating for Y tick labels, and for Y data labels
+     * (unless SetYDataLabelType was called).
+     * See SetLabelType() for details.
+     */
+    function SetYLabelType()  // Variable arguments: $type, ...
+    {
+        $args = func_get_args();
+        return $this->SetLabelType('y', $args);
+    }
+
+    /*
+     * Select label formatting for Y data labels, overriding SetYLabelType.
+     */
+    function SetYDataLabelType()  // Variable arguments: $type, ...
+    {
+        $args = func_get_args();
+        return $this->SetLabelType('yd', $args);
+    }
+
+    /*
+     * Set the date/time format code for X labels.
+     * Note: Use of SetXLabelType('time', $which_xtf) is preferred, because
+     * SetXTimeFormat does not also enable date/time formatting.
      */
-    function SetYLabelType($which_ylt) 
+    function SetXTimeFormat($which_xtf)
     {
-        $this->y_label_type = $this->CheckOption($which_ylt, 'data, time', __FUNCTION__);
+        $this->label_format['x']['time_format'] = $which_xtf;
         return TRUE;
     }
 
-    function SetXTimeFormat($which_xtf) 
+    /*
+     * Set the date/time format code for Y labels.
+     * Note: Use of SetYLabelType('time', $which_ytf) is preferred, because
+     * SetYTimeFormat does not also enable date/time formatting.
+     */
+    function SetYTimeFormat($which_ytf)
     {
-        $this->x_time_format = $which_xtf;
+        $this->label_format['y']['time_format'] = $which_ytf;
         return TRUE;
     }
-    function SetYTimeFormat($which_ytf) 
+
+    /*
+     * Set number format parameters (decimal point and thousands separator) for
+     * 'data' mode label formatting, overriding the locale-defaults.
+     */
+    function SetNumberFormat($decimal_point, $thousands_sep)
     {
-        $this->y_time_format = $which_ytf;
+        $this->decimal_point = $decimal_point;
+        $this->thousands_sep = $thousands_sep;
         return TRUE;
     }
 
-    function SetXLabelAngle($which_xla) 
+    /*
+     * Set the text angle for X labels to $which_xla degrees.
+     */
+    function SetXLabelAngle($which_xla)
     {
         $this->x_label_angle = $which_xla;
         return TRUE;
     }
 
+    /*
+     * Set the text angle for Y labels to $which_xla degrees.
+     */
     function SetYLabelAngle($which_yla)
     {
         $this->y_label_angle = $which_yla;
         return TRUE;
     }
 
+    /*
+     * Set the angle for X Data Labels to $which_xdla degrees.
+     * If not used, this defaults to the value set with SetXLabelAngle.
+     */
+    function SetXDataLabelAngle($which_xdla)
+    {
+        $this->x_data_label_angle = $which_xdla;
+        return TRUE;
+    }
+
+    /*
+     * Set the angle for Y Data Labels to $which_ydla degrees.
+     * If not used, this defaults to zero (unlike X data labels).
+     */
+    function SetYDataLabelAngle($which_ydla)
+    {
+        $this->y_data_label_angle = $which_ydla;
+        return TRUE;
+    }
+
 /////////////////////////////////////////////
 ///////////                              MISC
 /////////////////////////////////////////////
 
-    /*!
-     * Checks the valididy of an option.
-     *  \param which_opt  String to check.
-     *  \param which_acc  String of accepted choices.
-     *  \param which_func Name of the calling function, for error messages.
-     *  \note If checking everywhere for correctness slows things down, we could provide a
-     *        child class overriding every Set...() method which uses CheckOption(). Those new
-     *        methods could proceed in the unsafe but faster way.
+    /*
+     * Checks the validity of an option.
+     *   $which_opt  String to check, such as the provided value of a function argument.
+     *   $which_acc  String of accepted choices. Must be lower-case, and separated
+     *               by exactly ', ' (comma, space).
+     *   $which_func Name of the calling function, for error messages.
+     * Returns the supplied option value, downcased and trimmed, if it is valid.
+     * Reports an error if the supplied option is not valid.
      */
-    function CheckOption($which_opt, $which_acc, $which_func)
+    protected function CheckOption($which_opt, $which_acc, $which_func)
     {
-        $asked = trim($which_opt);
-
-        // FIXME: this for backward compatibility, as eregi() fails with empty strings.
-        if ($asked == '')
-            return '';
+        $asked = strtolower(trim($which_opt));
 
-        $asked = strtolower($asked);
-        if (@ eregi($asked, $which_acc)) {
+        // Look for the supplied value in a comma/space separated list.
+        if (strpos(", $which_acc,", ", $asked,") !== FALSE)
             return $asked;
-        } else {
-            $this->DrawError("$which_func(): '$which_opt' not in available choices: '$which_acc'.");
-            return NULL;
+
+        $this->PrintError("$which_func(): '$which_opt' not in available choices: '$which_acc'.");
+        return NULL;
+    }
+
+    /*
+     * Checks the validity of an array of options.
+     *   $opt  Array or string to check.
+     *   $acc  String of accepted choices. Must be lower-case, and separated
+     *               by exactly ', ' (comma, space).
+     *   $func Name of the calling function, for error messages.
+     * Returns a array option value(s), downcased and trimmed, if all entries in $opt are valid.
+     * Reports an error if any supplied option is not valid. Returns NULL if the error handler returns.
+     */
+    protected function CheckOptionArray($opt, $acc, $func)
+    {
+        $opt_array = (array)$opt;
+        $result = array();
+        foreach ($opt_array as $option) {
+            $choice = $this->CheckOption($option, $acc, $func);
+            if (is_null($choice)) return NULL; // In case CheckOption error handler returns
+            $result[] = $choice;
         }
+        return $result;
+    }
+
+    /*
+     * Check compatibility of a plot type and data type.
+     * This is called by the plot-type-specific drawing functions.
+     *   $valid_types  String of supported data types. Multiple values must be
+     *      separated by exactly ', ' (comma, space).
+     * Returns True if the type is valid for this plot.
+     * Reports an error if the data type is not value. If the error is handled and
+     *   the handler returns, this returns False.
+     */
+    protected function CheckDataType($valid_types)
+    {
+        if (strpos(", $valid_types,", ", $this->data_type,") !== FALSE)
+            return TRUE;
+
+        $this->PrintError("Data type '$this->data_type' is not valid for '$this->plot_type' plots."
+               . " Supported data type(s): '$valid_types'");
+        return FALSE;
+    }
+
+    /*
+     * Decode the data type into variables used to determine how to process a data array.
+     * The goal is minimize which functions understand the actual data type values.
+     * This sets the datatype_* variables for use by other member functions.
+     *   datatype_implied : Implicit independent variable (e.g. text-data vs data-data)
+     *   datatype_swapped_xy : Swapped X/Y (horizontal plot)
+     *   datatype_error_bars : Data array has error bar data
+     *   datatype_pie_single : Data array is for a pie chart with one row per slice
+     */
+    protected function DecodeDataType()
+    {
+        $dt = $this->data_type;
+
+        $this->datatype_implied = ($dt == 'text-data' || $dt == 'text-data-single'
+                                || $dt == 'text-data-yx');
+        $this->datatype_swapped_xy = ($dt == 'text-data-yx' || $dt == 'data-data-yx');
+        $this->datatype_error_bars = ($dt == 'data-data-error');
+        $this->datatype_pie_single = ($dt == 'text-data-single');
     }
 
+    /*
+     * Make sure the data array is populated, and calculate the number of columns.
+     * This is called from DrawGraph. Calculates data_columns, which is the
+     * maximum number of dependent variable values (usually Y) in the data array rows.
+     * (For pie charts, this is the number of slices.)
+     * This depends on the data_type, unlike records_per_group (which was
+     * previously used to pad style arrays, but is not accurate).
+     * Returns True if the data array is OK, else reports an error (and may return False).
+     * Note error messages refer to the caller, the public DrawGraph().
+     */
+    protected function CheckDataArray()
+    {
+        // Test for missing image, which really should never happen.
+        if (!$this->img) {
+            return $this->PrintError('DrawGraph(): No image resource allocated');
+        }
+
+        // Test for missing or empty data array:
+        if (empty($this->data) || !is_array($this->data)) {
+            return $this->PrintError("DrawGraph(): No data array");
+        }
+        if ($this->total_records == 0) {
+            return $this->PrintError('DrawGraph(): Empty data set');
+        }
+
+        // Decode the data type into functional flags.
+        $this->DecodeDataType();
 
-    /*!
-     *  \note Submitted by Thiemo Nagel
+        // Calculate the maximum number of dependent values per independent value
+        // (e.g. Y for each X), or the number of pie slices.
+        if ($this->datatype_pie_single) {
+            $this->data_columns = $this->num_data_rows; // Special case for 1 type of pie chart.
+        } else {
+            $skip = $this->datatype_implied ? 1 : 2; // Skip data label and independent variable if used
+            $this->data_columns = $this->records_per_group - $skip;
+            if ($this->datatype_error_bars) // Each Y has +err and -err along with it
+                $this->data_columns = (int)($this->data_columns / 3);
+        }
+        return TRUE;
+    }
+
+    /*
+     * Control headers for browser-side image caching.
+     *   $which_browser_cache : True to allow browsers to cache the image.
      */
     function SetBrowserCache($which_browser_cache)
     {
@@ -1318,8 +1973,9 @@ class PHPlot {
         return TRUE;
     }
 
-    /*!
-     * Whether to show the final image or not
+    /*
+     * Set whether DrawGraph automatically outputs the image too.
+     *   $which_pi : True to have DrawGraph call PrintImage at the end.
      */
     function SetPrintImage($which_pi)
     {
@@ -1327,110 +1983,140 @@ class PHPlot {
         return TRUE;
     }
 
-    /*!
-     * Sets the graph's legend. If argument is not an array, appends it to the legend.
+    /*
+     * Set text to display in the graph's legend.
+     *   $which_leg : Array of strings for the complete legend, or a single string
+     *                to be appended to the legend.
      */
     function SetLegend($which_leg)
     {
         if (is_array($which_leg)) {             // use array
             $this->legend = $which_leg;
-            return TRUE;
-        } else if (! is_null($which_leg)) {     // append string
+        } elseif (! is_null($which_leg)) {     // append string
             $this->legend[] = $which_leg;
-            return TRUE;
         } else {
-            $this->DrawError("SetLegend(): argument must not be null.");
-            return FALSE;
+            return $this->PrintError("SetLegend(): argument must not be null.");
         }
+        return TRUE;
     }
 
-    /*!
-     * Specifies the absolute (relative to image's up/left corner) position
-     * of the legend's upper/leftmost corner.
-     *  $which_type not yet used (TODO)
+    /*
+     * Specifies the position of the legend's upper/leftmost corner,
+     * in pixel (device) coordinates.
      */
-    function SetLegendPixels($which_x, $which_y, $which_type=NULL) 
-    { 
+    function SetLegendPixels($which_x, $which_y)
+    {
         $this->legend_x_pos = $which_x;
         $this->legend_y_pos = $which_y;
+        // Make sure this is unset, meaning we have pixel coords:
+        unset($this->legend_xy_world);
 
         return TRUE;
     }
 
-    /*!
-     * Specifies the relative (to graph's origin) position of the legend's
-     * upper/leftmost corner. MUST be called after scales are set up.
-     *   $which_type not yet used (TODO)
+    /*
+     * Specifies the position of the legend's upper/leftmost corner,
+     * in world (data space) coordinates.
      */
-    function SetLegendWorld($which_x, $which_y, $which_type=NULL) 
-    { 
-        if (! isset($this->scale_is_set))
-            $this->CalcTranslation();
-
-        $this->legend_x_pos = $this->xtr($which_x);
-        $this->legend_y_pos = $this->ytr($which_y);
+    function SetLegendWorld($which_x, $which_y)
+    {
+        // Since conversion from world to pixel coordinates is not yet available, just
+        // remember the coordinates and set a flag to indicate conversion is needed.
+        $this->legend_x_pos = $which_x;
+        $this->legend_y_pos = $which_y;
+        $this->legend_xy_world = TRUE;
 
         return TRUE;
     }
 
-    /*!
-     * Accepted values are: left, sides, none, full
+    /*
+     * Set legend text alignment, color box alignment, and style options.
+     *   $text_align : Alignment of the text, 'left' or 'right'.
+     *   $colorbox_align : Alignment of the color boxes, 'left', 'right', 'none', or missing/empty.
+     *       If missing or empty, the same alignment as $text_align is used. Color box is positioned first.
+     *   $style : reserved for future use.
+     */
+    function SetLegendStyle($text_align, $colorbox_align = '', $style = '')
+    {
+        $this->legend_text_align = $this->CheckOption($text_align, 'left, right', __FUNCTION__);
+        if (empty($colorbox_align))
+            $this->legend_colorbox_align = $this->legend_text_align;
+        else
+            $this->legend_colorbox_align = $this->CheckOption($colorbox_align, 'left, right, none',
+                                                              __FUNCTION__);
+        return ((boolean)$this->legend_text_align && (boolean)$this->legend_colorbox_align);
+    }
+
+    /*
+     * Set border for the plot area.
+     * Accepted values are: left, right, top, bottom, sides, none, full or an array of those.
      */
     function SetPlotBorderType($pbt)
     {
-        $this->plot_border_type = $this->CheckOption($pbt, 'left, sides, none, full', __FUNCTION__);
+        $this->plot_border_type = $this->CheckOptionArray($pbt, 'left, right, top, bottom, sides, none, full',
+                                                          __FUNCTION__);
+        return !empty($this->plot_border_type);
     }
 
-    /*!
-     * Accepted values are: raised, plain
+    /*
+     * Set border style for the image.
+     * Accepted values are: raised, plain, solid, none
+     *  'solid' is the same as 'plain' except it fixes the color (see DrawImageBorder)
      */
-    function SetImageBorderType($sibt) 
+    function SetImageBorderType($sibt)
     {
-        $this->image_border_type = $this->CheckOption($sibt, 'raised, plain', __FUNCTION__);
+        $this->image_border_type = $this->CheckOption($sibt, 'raised, plain, solid, none', __FUNCTION__);
+        return (boolean)$this->image_border_type;
     }
 
+    /*
+     * Set border width for the image to $width in pixels.
+     */
+    function SetImageBorderWidth($width)
+    {
+        $this->image_border_width = $width;
+        return TRUE;
+    }
 
-    /*!
-     * \param dpab bool
+    /*
+     * Enable or disable drawing of the plot area background color.
      */
     function SetDrawPlotAreaBackground($dpab)
     {
         $this->draw_plot_area_background = (bool)$dpab;
+        return TRUE;
     }
 
-
-    /*!
-     * \param dyg bool 
+    /*
+     * Enable or disable drawing of the X grid lines.
      */
-    function SetDrawYGrid($dyg) 
+    function SetDrawXGrid($dxg)
     {
-        $this->draw_y_grid = (bool)$dyg;
+        $this->draw_x_grid = (bool)$dxg;
         return TRUE;
     }
 
-
-    /*!
-     * \param dxg bool
+    /*
+     * Enable or disable drawing of the Y grid lines.
      */
-    function SetDrawXGrid($dxg) 
+    function SetDrawYGrid($dyg)
     {
-        $this->draw_x_grid = (bool)$dxg;
+        $this->draw_y_grid = (bool)$dyg;
         return TRUE;
     }
 
-
-    /*!
-     * \param ddg bool 
+    /*
+     * Select dashed or solid grid lines.
+     *   $ddg : True for dashed grid lines, false for solid grid lines.
      */
-    function SetDrawDashedGrid($ddg) 
+    function SetDrawDashedGrid($ddg)
     {
         $this->dashed_grid = (bool)$ddg;
         return TRUE;
     }
 
-
-    /*!
-     * \param dxdl bool
+    /*
+     * Enable or disable drawing of X Data Label Lines.
      */
     function SetDrawXDataLabelLines($dxdl)
     {
@@ -1438,748 +2124,1265 @@ class PHPlot {
         return TRUE;
     }
 
-    
-    /*!
-     * TODO: draw_y_data_label_lines not implemented.
-     * \param dydl bool
-     */
-    function SetDrawYDataLabelLines($dydl)
-    {
-        $this->draw_y_data_label_lines = $dydl;
-        return TRUE;
-    }
-    
-    /*!
-     * Sets the graph's title.
-     * TODO: add parameter to choose title placement: left, right, centered=
+    /*
+     * Set the main title text for the plot.
      */
-    function SetTitle($which_title) 
+    function SetTitle($which_title)
     {
         $this->title_txt = $which_title;
-
-        if ($which_title == '') {
-            $this->title_height = 0;
-            return TRUE;
-        }            
-
-        $str = split("\n", $which_title);
-        $lines = count($str);
-        $spacing = $this->line_spacing * ($lines - 1);
-
-        if ($this->use_ttf) {
-            $size = $this->TTFBBoxSize($this->title_font['size'], 0, $this->title_font['font'], $which_title);
-            $this->title_height = $size[1] * $lines;
-        } else {
-            $this->title_height = ($this->title_font['height'] + $spacing) * $lines;
-        }   
         return TRUE;
     }
 
-    /*!
-     * Sets the X axis title and position.
+    /*
+     * Set the X axis title and position.
      */
-    function SetXTitle($which_xtitle, $which_xpos = 'plotdown') 
+    function SetXTitle($which_xtitle, $which_xpos = 'plotdown')
     {
         if ($which_xtitle == '')
             $which_xpos = 'none';
 
         $this->x_title_pos = $this->CheckOption($which_xpos, 'plotdown, plotup, both, none', __FUNCTION__);
-
+        if (!$this->x_title_pos) return FALSE;
         $this->x_title_txt = $which_xtitle;
+        return TRUE;
+    }
 
-        $str = split("\n", $which_xtitle);
-        $lines = count($str);
-        $spacing = $this->line_spacing * ($lines - 1);
-
-        if ($this->use_ttf) {
-            $size = $this->TTFBBoxSize($this->x_title_font['size'], 0, $this->x_title_font['font'], $which_xtitle);
-            $this->x_title_height = $size[1] * $lines;
-        } else {
-            $this->x_title_height = ($this->y_title_font['height'] + $spacing) * $lines;
-        }
-
-        return TRUE;
-    }
-
-
-    /*!
-     * Sets the Y axis title and position.
+    /*
+     * Set the Y axis title and position.
      */
-    function SetYTitle($which_ytitle, $which_ypos = 'plotleft') 
+    function SetYTitle($which_ytitle, $which_ypos = 'plotleft')
     {
         if ($which_ytitle == '')
             $which_ypos = 'none';
 
         $this->y_title_pos = $this->CheckOption($which_ypos, 'plotleft, plotright, both, none', __FUNCTION__);
-
+        if (!$this->y_title_pos) return FALSE;
         $this->y_title_txt = $which_ytitle;
-
-        $str = split("\n", $which_ytitle);
-        $lines = count($str);
-        $spacing = $this->line_spacing * ($lines - 1);
-
-        if ($this->use_ttf) {
-            $size = $this->TTFBBoxSize($this->y_title_font['size'], 90, $this->y_title_font['font'], 
-                                       $which_ytitle);
-            $this->y_title_width = $size[0] * $lines;
-        } else {
-            $this->y_title_width = ($this->y_title_font['height'] + $spacing) * $lines;
-        }
-
         return TRUE;
     }
 
-    /*!
-     * Sets the size of the drop shadow for bar and pie charts.
-     * \param which_s int Size in pixels.
+    /*
+     * Set the size of the drop shadow for bar and pie charts.
+     *   $which_s : Size of the drop shadow in pixels.
      */
-    function SetShading($which_s) 
-    { 
+    function SetShading($which_s)
+    {
         $this->shading = (int)$which_s;
         return TRUE;
     }
 
-    function SetPlotType($which_pt) 
+    /*
+     * Set the plot type (bars, points, ...)
+     */
+    function SetPlotType($which_pt)
     {
-        $this->plot_type = $this->CheckOption($which_pt, 
-                           'bars, stackedbars, lines, linepoints, area, points, pie, thinbarline, squared', 
+        $this->plot_type = $this->CheckOption($which_pt, 'bars, stackedbars, lines, linepoints,'
+                            . ' area, points, pie, thinbarline, squared, stackedarea',
                             __FUNCTION__);
+        return (boolean)$this->plot_type;
     }
 
-    /*!
-     * Sets the position of Y axis.
-     * \param pos int Position in world coordinates.
+    /*
+     * Set the position of the X axis.
+     *  $pos : Axis position in world coordinates (as an integer).
      */
-    function SetYAxisPosition($pos)
+    function SetXAxisPosition($pos)
     {
-        $this->y_axis_position = (int)$pos;
-        if (isset($this->scale_is_set)) {
-            $this->CalcTranslation();
-        }
+        $this->x_axis_position = (int)$pos;
         return TRUE;
     }
-    
-    /*!
-     * Sets the position of X axis.
-     * \param pos int Position in world coordinates. 
+
+    /*
+     * Set the position of the Y axis.
+     *  $pos : Axis position in world coordinates (as an integer).
      */
-    function SetXAxisPosition($pos)
+    function SetYAxisPosition($pos)
     {
-        $this->x_axis_position = (int)$pos;
-        if (isset($this->scale_is_set)) {
-            $this->CalcTranslation();
-        }
+        $this->y_axis_position = (int)$pos;
         return TRUE;
     }
 
-
+    /*
+     * Select linear or log scale for the X axis.
+     */
     function SetXScaleType($which_xst)
     {
         $this->xscale_type = $this->CheckOption($which_xst, 'linear, log', __FUNCTION__);
-        return TRUE;
+        return (boolean)$this->xscale_type;
     }
 
+    /*
+     * Select linear or log scale for the Y axis.
+     */
     function SetYScaleType($which_yst)
     {
         $this->yscale_type = $this->CheckOption($which_yst, 'linear, log',  __FUNCTION__);
-        return TRUE;
+        return (boolean)$this->yscale_type;
     }
 
+    /*
+     * Set the precision for numerically formatted X labels.
+     *   $which_prec : Number of digits to display.
+     * Note: This is equivalent to: SetXLabelType('data', $which_prec)
+     */
     function SetPrecisionX($which_prec)
     {
-        $this->x_precision = $which_prec;
-        $this->SetXLabelType('data');
-        return TRUE;
+        return $this->SetXLabelType('data', $which_prec);
     }
 
+    /*
+     * Set the precision for numerically formatted Y labels.
+     *   $which_prec : Number of digits to display.
+     * Note: This is equivalent to: SetYLabelType('data', $which_prec)
+     */
     function SetPrecisionY($which_prec)
     {
-        $this->y_precision = $which_prec;
-        $this->SetYLabelType('data');
-        return TRUE;
+        return $this->SetYLabelType('data', $which_prec);
     }
 
+    /*
+     * Set the line width (in pixels) for error bars.
+     */
     function SetErrorBarLineWidth($which_seblw)
     {
         $this->error_bar_line_width = $which_seblw;
         return TRUE;
     }
 
+    /*
+     * Set the position for pie chart percentage labels.
+     *   $which_blb : Real number between 0 and 1.
+     *      Smaller values move the labels in towards the center.
+     */
     function SetLabelScalePosition($which_blp)
     {
-        //0 to 1
         $this->label_scale_position = $which_blp;
         return TRUE;
     }
 
+    /*
+     * Set the size (in pixels) of the "T" in error bars.
+     */
     function SetErrorBarSize($which_ebs)
     {
-        //in pixels
         $this->error_bar_size = $which_ebs;
         return TRUE;
     }
 
-    /*!
-     * Can be one of: 'tee', 'line'
+    /*
+     * Set the shape of the in error bars.
+     *   $which_ebs : Error bar shape, 'tee' or 'line'.
      */
     function SetErrorBarShape($which_ebs)
     {
         $this->error_bar_shape = $this->CheckOption($which_ebs, 'tee, line', __FUNCTION__);
+        return (boolean)$this->error_bar_shape;
     }
 
-    /*!
-     * Sets point shape for each data set via an array.
-     * Shape can be one of: 'halfline', 'line', 'plus', 'cross', 'rect', 'circle', 'dot',
-     * 'diamond', 'triangle', 'trianglemid'
+    /*
+     * Synchronize the point shape and point size arrays.
+     * This is called just before drawing any plot that needs 'points'.
+     */
+    protected function CheckPointParams()
+    {
+        // Make both point_shapes and point_sizes the same size, by padding the smaller.
+        $ps = count($this->point_sizes);
+        $pt = count($this->point_shapes);
+
+        if ($ps < $pt) {
+            $this->pad_array($this->point_sizes, $pt);
+            $this->point_counts = $pt;
+        } elseif ($ps > $pt) {
+            $this->pad_array($this->point_shapes, $ps);
+            $this->point_counts = $ps;
+        } else {
+            $this->point_counts = $ps;
+        }
+
+        // Note: PHPlot used to check and adjust point_sizes to be an even number here,
+        // for all 'diamond' and 'triangle' shapes. The reason for this having been
+        // lost, and the current maintainer seeing no sense it doing this for only
+        // some shapes, the code has been removed. But see what DrawDot() does.
+    }
+
+    /*
+     * Set the point shape for each data set.
+     *   $which_pt : Array (or single value) of valid point shapes.
+     * The point shape and point sizes arrays are synchronized before drawing a graph
+     * that uses points. See CheckPointParams()
      */
     function SetPointShapes($which_pt)
     {
-        if (is_null($which_pt)) {
-            // Do nothing, use default value.
-        } else if (is_array($which_pt)) {
-            // Did we get an array with point shapes?
+        if (is_array($which_pt)) {
+            // Use provided array:
             $this->point_shapes = $which_pt;
-        } else {
-            // Single value into array
+        } elseif (!is_null($which_pt)) {
+            // Make the single value into an array:
             $this->point_shapes = array($which_pt);
         }
 
+        // Validate all the shapes. This list must agree with DrawDot().
         foreach ($this->point_shapes as $shape)
         {
-            // TODO, better check, per element rectification
-            $this->CheckOption($shape,
-               'halfline, line, plus, cross, rect, circle, dot, diamond, triangle, trianglemid',
-                __FUNCTION__);
-        }
-
-        // Make both point_shapes and point_sizes same size.
-        $ps = count($this->point_sizes);
-        $pt = count($this->point_shapes);
-
-        if ($ps < $pt) {
-            array_pad_array($this->point_sizes, $pt);
-        } else if ($pt > $ps) {
-            array_pad_array($this->point_shapes, $ps);
+            if (!$this->CheckOption($shape, 'halfline, line, plus, cross, rect, circle, dot,'
+                       . ' diamond, triangle, trianglemid, delta, yield, star, hourglass,'
+                       . ' bowtie, target, box, home, up, down, none', __FUNCTION__))
+                return FALSE;
         }
         return TRUE;
     }
 
-    /*!
-     * Sets the point size for point plots.
-     * \param ps int Size in pixels.
-     * \note Test this more extensively
+    /*
+     * Set the point size for point plots.
+     *   $which_ps : Array (or single value) of point sizes in pixels.
+     * The point shape and point sizes arrays are synchronized before drawing a graph
+     * that uses points. See CheckPointParams()
      */
     function SetPointSizes($which_ps)
     {
-        if (is_null($which_ps)) {
-            // Do nothing, use default value.
-        } else if (is_array($which_ps)) {
-            // Did we get an array with point sizes?
+        if (is_array($which_ps)) {
+            // Use provided array:
             $this->point_sizes = $which_ps;
-        } else {
-            // Single value into array
+        } elseif (!is_null($which_ps)) {
+            // Make the single value into an array:
             $this->point_sizes = array($which_ps);
         }
-
-        // Make both point_shapes and point_sizes same size.
-        $ps = count($this->point_sizes);
-        $pt = count($this->point_shapes);
-
-        if ($ps < $pt) {
-            array_pad_array($this->point_sizes, $pt);
-        } else if ($pt > $ps) {
-            array_pad_array($this->point_shapes, $ps);
-        }
-
-        // Fix odd point sizes for point shapes which need it
-        for ($i = 0; $i < $pt; $i++) {
-            if ($this->point_shapes[$i] == 'diamond' or $this->point_shapes[$i] == 'triangle') {
-                if ($this->point_sizes[$i] % 2 != 0) {
-                    $this->point_sizes[$i]++;
-                }
-            }
-        }
         return TRUE;
     }
 
-
-    /*!
-     * Tells not to draw lines for missing Y data. Only works with 'lines' and 'squared' plots.
-     * \param bl bool
+    /*
+     * Sets whether lines should be broken at missing data.
+     *   $bl : True to break the lines, false to connect around missing data.
+     * This only works with 'lines' and 'squared' plots.
      */
     function SetDrawBrokenLines($bl)
     {
         $this->draw_broken_lines = (bool)$bl;
+        return TRUE;
     }
 
-
-    /*!
+    /*
+     * Set the data type, which defines the structure of the data array
      *  text-data: ('label', y1, y2, y3, ...)
      *  text-data-single: ('label', data), for some pie charts.
      *  data-data: ('label', x, y1, y2, y3, ...)
      *  data-data-error: ('label', x1, y1, e1+, e2-, y2, e2+, e2-, y3, e3+, e3-, ...)
+     *  data-data-yx: ('label', y, x1, x2, x3, ..)
+     *  text-data-yx: ('label', x1, x2, x3, ...)
      */
     function SetDataType($which_dt)
     {
         //The next four lines are for past compatibility.
-        if ($which_dt == 'text-linear') { $which_dt = 'text-data'; };
-        if ($which_dt == 'linear-linear') { $which_dt = 'data-data'; };
-        if ($which_dt == 'linear-linear-error') { $which_dt = 'data-data-error'; };
-        if ($which_dt == 'text-data-pie') { $which_dt = 'text-data-single'; }
-
+        if ($which_dt == 'text-linear') $which_dt = 'text-data';
+        elseif ($which_dt == 'linear-linear') $which_dt = 'data-data';
+        elseif ($which_dt == 'linear-linear-error') $which_dt = 'data-data-error';
+        elseif ($which_dt == 'text-data-pie') $which_dt = 'text-data-single';
 
         $this->data_type = $this->CheckOption($which_dt, 'text-data, text-data-single, '.
-                                                         'data-data, data-data-error', __FUNCTION__);
-        return TRUE;
+                                                         'data-data, data-data-error, '.
+                                                         'data-data-yx, text-data-yx',
+                                                         __FUNCTION__);
+        return (boolean)$this->data_type;
     }
 
-    /*!
+    /*
      * Copy the array passed as data values. We convert to numerical indexes, for its
      * use for (or while) loops, which sometimes are faster. Performance improvements
      * vary from 28% in DrawLines() to 49% in DrawArea() for plot drawing functions.
      */
-    function SetDataValues(&$which_dv)
+    function SetDataValues($which_dv)
     {
-        unset ($this->data_limits_done);        // Reset this for every new data_set
         $this->num_data_rows = count($which_dv);
-        $this->total_records = 0;               // Perform some useful calculations.
-        $this->records_per_group = 1;           
-        for ($i = 0, $recs = 0; $i < $this->num_data_rows; $i++) {
-            // Copy
+        $this->total_records = 0;
+        $this->data = array();
+        $this->num_recs = array();
+        for ($i = 0; $i < $this->num_data_rows; $i++) {
             $this->data[$i] = array_values($which_dv[$i]);   // convert to numerical indices.
 
-            // Compute some values
-            $recs = count($this->data[$i]); 
+            // Count size of each row, and total for the array.
+            $recs = count($this->data[$i]);
             $this->total_records += $recs;
-
-            if ($recs > $this->records_per_group)
-                $this->records_per_group = $recs;
-
             $this->num_recs[$i] = $recs;
         }
+        // This is the size of the widest row in the data array
+        // Note records_per_group isn't used much anymore. See data_columns in CheckDataArray()
+        $this->records_per_group = max($this->num_recs);
+        return TRUE;
     }
 
-    /*!
+    /*
      * Pad styles arrays for later use by plot drawing functions:
      * This removes the need for $max_data_colors, etc. and $color_index = $color_index % $max_data_colors
      * in DrawBars(), DrawLines(), etc.
+     * The arrays are padded to data_columns which is the maximum number of data sets.
+     * See CheckDataArray() for the calculation.
      */
-    function PadArrays()
+    protected function PadArrays()
     {
-        array_pad_array($this->line_widths, $this->records_per_group);
-        array_pad_array($this->line_styles, $this->records_per_group);
+        $this->pad_array($this->line_widths, $this->data_columns);
+        $this->pad_array($this->line_styles, $this->data_columns);
+        $this->pad_array($this->ndx_data_colors, $this->data_columns);
+        $this->pad_array($this->ndx_data_border_colors, $this->data_columns);
+        // Other data color arrays are handled in the Need*Colors() functions.
 
-        array_pad_array($this->data_colors, $this->records_per_group);
-        array_pad_array($this->data_border_colors, $this->records_per_group);
-        array_pad_array($this->error_bar_colors, $this->records_per_group);
+        return TRUE;
+    }
 
-        $this->SetDataColors();
-        $this->SetDataBorderColors();
-        $this->SetErrorBarColors();
+    /*
+     * Pads an array with itself. This only works on 0-based sequential integer indexed arrays.
+     *    $arr : The array (or scalar) to pad. This argument is modified.
+     *    $size : Minimum size of the resulting array.
+     * If $arr is a scalar, it will be converted first to a single element array.
+     * If $arr has at least $size elements, it is unchanged.
+     * Otherwise, append elements of $arr to itself until it reaches $size elements.
+     */
+    protected function pad_array(&$arr, $size)
+    {
+        if (! is_array($arr)) {
+            $arr = array($arr);
+        }
+        $n = count($arr);
+        $base = 0;
+        while ($n < $size) $arr[$n++] = $arr[$base++];
+    }
+
+    /*
+     * Truncate an array to a maximum size.
+     * This only works on 0-based sequential integer indexed arrays.
+     *    $arr : The array to truncate.
+     *    $size : Maximum size of the resulting array.
+     */
+    protected function truncate_array(&$arr, $size)
+    {
+        for ($n = count($arr) - 1; $n >= $size; $n--) unset($arr[$n]);
+    }
+
+    /*
+     * Format a floating-point number.
+     *   $number : A floating point number to format
+     *   $decimals : Number of decimal places in the result
+     *   Returns the formatted result.
+     * This is like PHP's number_format, but uses class variables for separators.
+     * The separators will default to locale-specific values, if available.
+     */
+    protected function number_format($number, $decimals=0)
+    {
+        if (!isset($this->decimal_point) || !isset($this->thousands_sep)) {
+            // Load locale-specific values from environment, unless disabled:
+            if (empty($this->locale_override))
+                @setlocale(LC_ALL, '');
+            // Fetch locale settings:
+            $locale = @localeconv();
+            if (!empty($locale) && isset($locale['decimal_point']) &&
+                    isset($locale['thousands_sep'])) {
+              $this->decimal_point = $locale['decimal_point'];
+              $this->thousands_sep = $locale['thousands_sep'];
+            } else {
+              // Locale information not available.
+              $this->decimal_point = '.';
+              $this->thousands_sep = ',';
+            }
+        }
+        return number_format($number, $decimals, $this->decimal_point, $this->thousands_sep);
+    }
 
+    /*
+     * Register a callback (hook) function
+     *   $reason : A pre-defined name where a callback can be defined.
+     *   $function : The name of a function to register for callback, or an instance/method
+     *      pair in an array (see 'callbacks' in the PHP reference manual).
+     *   $arg : Optional argument to supply to the callback function when it is triggered.
+     *      (Often called "clientData")
+     *   Returns True if the callback reason is valid, else False.
+     */
+    function SetCallback($reason, $function, $arg = NULL)
+    {
+        // Use array_key_exists because valid reason keys have NULL as value.
+        if (!array_key_exists($reason, $this->callbacks))
+            return FALSE;
+        $this->callbacks[$reason] = array($function, $arg);
         return TRUE;
     }
 
+    /*
+     * Return the name of a function registered for callback. See SetCallBack.
+     *   $reason - A pre-defined name where a callback can be defined.
+     *   Returns the current callback function (name or array) for the given reason,
+     *   or False if there was no active callback or the reason is not valid.
+     * Note you can safely test the return value with a simple 'if', as
+     * no valid function name evaluates to false.
+     */
+    function GetCallback($reason)
+    {
+        if (isset($this->callbacks[$reason]))
+            return $this->callbacks[$reason][0];
+        return FALSE;
+    }
+
+    /*
+     * Un-register (remove) a function registered for callback.
+     *   $reason - A pre-defined name where a callback can be defined.
+     *   Returns: True if it was a valid callback reason, else False.
+     * Note: Returns True whether or not there was a callback registered.
+     */
+    function RemoveCallback($reason)
+    {
+        if (!array_key_exists($reason, $this->callbacks))
+            return FALSE;
+        $this->callbacks[$reason] = NULL;
+        return TRUE;
+    }
+
+    /*
+     * Invoke a callback, if one is registered.
+     * Accepts a variable number of arguments >= 1:
+     *   $reason : A string naming the callback.
+     *   ... : Zero or more additional arguments to be passed to the
+     *         callback function, after the passthru argument:
+     *           callback_function($image, $passthru, ...)
+     *   Returns: whatever value (if any) was returned by the callback.
+     */
+    protected function DoCallback() // Note: Variable arguments
+    {
+        $args = func_get_args();
+        $reason = $args[0];
+        if (!isset($this->callbacks[$reason]))
+            return;
+        list($function, $args[0]) = $this->callbacks[$reason];
+        array_unshift($args, $this->img);
+        // Now args[] looks like: img, passthru, extra args...
+        return call_user_func_array($function, $args);
+    }
+
+    /*
+     * Allocate colors for the plot.
+     * This is called by DrawGraph to allocate the colors needed for the plot.  Each selectable
+     * color has already been validated, parsed into an array (r,g,b,a), and stored into a member
+     * variable. Now the GD color indexes are assigned and stored into the ndx_*_color variables.
+     * This is deferred here to avoid allocating unneeded colors and to avoid order dependencies,
+     * especially with the transparent color.
+     *
+     * For drawing data elements, only the main data colors and border colors are allocated here.
+     * Dark colors and error bar colors are allocated by Need*Color() functions.
+     * (Data border colors default to just black, so there is no cost to always allocating.)
+     *
+     * Data color allocation works as follows. If there is a data_color callback, then allocate all
+     * defined data colors (because the callback can use them however it wants). Otherwise, truncate
+     * the array to the number of colors that will be used. This is the larger of the number of data
+     * sets and the number of legend lines.
+     */
+    protected function SetColorIndexes()
+    {
+        $this->ndx_bg_color         = $this->GetColorIndex($this->bg_color); // Background first
+        $this->ndx_plot_bg_color    = $this->GetColorIndex($this->plot_bg_color);
+        if ($this->image_border_type != 'none') {
+            $this->ndx_i_border         = $this->GetColorIndex($this->i_border);
+            $this->ndx_i_border_dark    = $this->GetDarkColorIndex($this->i_border);
+        }
+
+        // Handle defaults for X and Y title colors.
+        $this->ndx_title_color      = $this->GetColorIndex($this->title_color);
+        if (empty($this->x_title_color)) {
+            $this->ndx_x_title_color = $this->ndx_title_color;
+        } else {
+            $this->ndx_x_title_color = $this->GetColorIndex($this->x_title_color);
+        }
+        if (empty($this->y_title_color)) {
+            $this->ndx_y_title_color = $this->ndx_title_color;
+        } else {
+            $this->ndx_y_title_color = $this->GetColorIndex($this->y_title_color);
+        }
+
+        $this->ndx_text_color       = $this->GetColorIndex($this->text_color);
+        $this->ndx_grid_color       = $this->GetColorIndex($this->grid_color);
+        $this->ndx_light_grid_color = $this->GetColorIndex($this->light_grid_color);
+        $this->ndx_tick_color       = $this->GetColorIndex($this->tick_color);
+
+        // If no data_color callback is being used, only allocate needed colors.
+        if (!$this->GetCallback('data_color')) {
+            $data_colors_needed = max($this->data_columns, empty($this->legend) ? 0 : count($this->legend));
+            $this->truncate_array($this->data_colors, $data_colors_needed);
+            $this->truncate_array($this->data_border_colors, $data_colors_needed);
+            $this->truncate_array($this->error_bar_colors, $data_colors_needed);
+        }
+
+        // Allocate main data colors. For other colors used for data, see the functions which follow.
+        $getcolor_cb = array($this, 'GetColorIndex');
+        $this->ndx_data_colors = array_map($getcolor_cb, $this->data_colors);
+        $this->ndx_data_border_colors = array_map($getcolor_cb, $this->data_border_colors);
+
+        // Set up a color as transparent, if SetTransparentColor was used.
+        if (!empty($this->transparent_color)) {
+            imagecolortransparent($this->img, $this->GetColorIndex($this->transparent_color));
+        }
+    }
+
+    /*
+     * Allocate dark-shade data colors. Called if needed by graph drawing functions.
+     */
+    protected function NeedDataDarkColors()
+    {
+        $getdarkcolor_cb = array($this, 'GetDarkColorIndex');
+        $this->ndx_data_dark_colors = array_map($getdarkcolor_cb, $this->data_colors);
+        $this->pad_array($this->ndx_data_dark_colors, $this->data_columns);
+    }
+
+    /*
+     * Allocate error bar colors. Called if needed by graph drawing functions.
+     */
+    protected function NeedErrorBarColors()
+    {
+        $getcolor_cb = array($this, 'GetColorIndex');
+        $this->ndx_error_bar_colors = array_map($getcolor_cb, $this->error_bar_colors);
+        $this->pad_array($this->ndx_error_bar_colors, $this->data_columns);
+    }
 
 //////////////////////////////////////////////////////////
 ///////////         DATA ANALYSIS, SCALING AND TRANSLATION
 //////////////////////////////////////////////////////////
 
-    /*!
-     * Analizes data and sets up internal maxima and minima
-     * Needed by: CalcMargins(), ...
-     *   Text-Data is different than data-data graphs. For them what
-     *   we have, instead of X values, is # of records equally spaced on data.
-     *   text-data is passed in as $data[] = (title, y1, y2, y3, y4, ...)
-     *   data-data is passed in as $data[] = (title, x, y1, y2, y3, y4, ...)
-     */
-    function FindDataLimits()
-    {
-        // Set some default min and max values before running through the data
-        switch ($this->data_type) {
-        case 'text-data':
-            $minx = 0;
-            $maxx = $this->num_data_rows - 1 ;
-            $miny = $this->data[0][1];
-            $maxy = $miny;
-            break;
-        default:  //Everything else: data-data, etc, take first value
-            $minx = $this->data[0][1];
-            $maxx = $minx;
-            $miny = $this->data[0][2];
-            $maxy = $miny;
-            break;
-        }
-
-        $mine = 0;  // Maximum value for the -error bar (assume error bars always > 0)
-        $maxe = 0;  // Maximum value for the +error bar (assume error bars always > 0)
-        $maxt = 0;  // Maximum number of characters in text labels
+    /*
+     * Analyzes the data array and calculates the minimum and maximum values.
+     * In this function, IV refers to the independent variable, and DV the dependent variable.
+     * For most plots, IV is X and DV is Y. For swapped X/Y plots, IV is Y and DV is X.
+     * At the end of the function, IV and DV ranges get assigned into X or Y.
+     *
+     * This has to know how certain plot types use the data. 'area' and 'pie' use absolute
+     * values, 'stackedbars' sums values, and 'stackedarea' sums absolute values.
+     *
+     * This calculates min_x, max_x, min_y, and max_y. It also calculates two arrays
+     * data_min[] and data_max[] with per-row min and max values. These are used for
+     * data label lines. For normal (unswapped) data, these are the Y range for each X.
+     * For swapped X/Y data, they are the X range for each Y.
+     */
+    protected function FindDataLimits()
+    {
+        // Special case processing for certain plot types:
+        $sum_abs = ($this->plot_type == 'stackedarea'); // Sum of absolute values
+        $sum_val = ($this->plot_type == 'stackedbars'); // Sum of values
+        $abs_val = ($this->plot_type == 'area' || $this->plot_type == 'pie'); // Absolute values
 
-        $minminy = $miny;
-        $maxmaxy = $maxy;
+        // These need to be initialized in case there are multiple plots and missing data points.
+        $this->data_min = array();
+        $this->data_max = array();
 
-        if ($this->plot_type == 'stackedbars') { $maxmaxy = $minminy = 0; }
+        // Independent values are in the data array or assumed?
+        if ($this->datatype_implied) {
+            $all_iv = array(0, $this->num_data_rows - 1);
+        } else {
+            $all_iv = array();
+        }
 
-        // Process each row of data
-        for ($i=0; $i < $this->num_data_rows; $i++) {
-            $j=0;
-            // Extract maximum text label length
-            $val = @ strlen($this->data[$i][$j++]);
-            $maxt = ($val > $maxt) ? $val : $maxt;
+        // Process all rows of data:
+        for ($i = 0; $i < $this->num_data_rows; $i++) {
+            $n_vals = $this->num_recs[$i];
+            $j = 1; // Skips label at [0]
 
+            if (!$this->datatype_implied) {
+                $all_iv[] = (double)$this->data[$i][$j++];
+            }
 
-            if ($this->plot_type == 'stackedbars') { $maxy = $miny = 0; }
+            if ($sum_abs || $sum_val) {
+                $all_dv = array(0, 0); // One limit is 0, other calculated below
+            } else {
+                $all_dv = array();
+            }
+            while ($j < $n_vals) {
+                if (is_numeric($this->data[$i][$j])) {
+                    $val = (double)$this->data[$i][$j++];
 
-            switch ($this->data_type) {
-            case 'text-data':           // Data is passed in as (title, y1, y2, y3, ...)
-            case 'text-data-single':    // This one is for some pie charts
-                // $numrecs = @ count($this->data[$i]);
-                $miny = $maxy = (double)$this->data[$i][$j];
-                for (; $j < $this->num_recs[$i]; $j++) {
-                    $val = (double)$this->data[$i][$j];
-                    if ($this->plot_type == 'stackedbars') {
-                        $maxy += abs($val);      // only positive values for the moment
+                    if ($this->datatype_error_bars) {
+                        $all_dv[] = $val + (double)$this->data[$i][$j++];
+                        $all_dv[] = $val - (double)$this->data[$i][$j++];
+                    } elseif ($sum_abs) {
+                        $all_dv[1] += abs($val); // Sum of absolute values
+                    } elseif ($sum_val) {
+                        $all_dv[1] += $val;  // Sum of values
+                    } elseif ($abs_val) {
+                        $all_dv[] = abs($val); // List of all absolute values
                     } else {
-                        $maxy = ($val > $maxy) ? $val : $maxy;
-                        $miny = ($val < $miny) ? $val : $miny;
+                        $all_dv[] = $val; // List of all values
                     }
+                } else {    // Missing DV value
+                  $j++;
+                  if ($this->datatype_error_bars) $j += 2;
                 }
-                break;
-            case 'data-data':           // Data is passed in as (title, x, y, y2, y3, ...)
-                // X value:
-                $val = (double)$this->data[$i][$j++];
-                $maxx = ($val > $maxx) ? $val : $maxx;
-                $minx = ($val < $minx) ? $val : $minx;
-
-                $miny = $maxy = (double)$this->data[$i][$j];
-                // $numrecs = @ count($this->data[$i]);
-                for (; $j < $this->num_recs[$i]; $j++) {
-                    $val = (double)$this->data[$i][$j];
-                    $maxy = ($val > $maxy) ? $val : $maxy;
-                    $miny = ($val < $miny) ? $val : $miny;
-                }
-                break;
-            case 'data-data-error':     // Data is passed in as (title, x, y, err+, err-, y2, err2+, err2-,...)
-                // X value:
-                $val = (double)$this->data[$i][$j++];
-                $maxx = ($val > $maxx) ? $val : $maxx;
-                $minx = ($val < $minx) ? $val : $minx;
-
-                $miny = $maxy = (double)$this->data[$i][$j];
-                // $numrecs = @ count($this->data[$i]);
-                for (; $j < $this->num_recs[$i];) {
-                    // Y value:
-                    $val = (double)$this->data[$i][$j++];
-                    $maxy = ($val > $maxy) ? $val : $maxy;
-                    $miny = ($val < $miny) ? $val : $miny;
-                    // Error +:
-                    $val = (double)$this->data[$i][$j++];
-                    $maxe = ($val > $maxe) ? $val : $maxe;
-                    // Error -:
-                    $val = (double)$this->data[$i][$j++];
-                    $mine = ($val > $mine) ? $val : $mine;
-                }
-                $maxy = $maxy + $maxe;
-                $miny = $miny - $mine;      // assume error bars are always > 0
-                break;
-            default:
-                $this->PrintError("FindDataLimits(): Unknown data type '$data_type'.");
-            break;
             }
-            $this->data[$i][MINY] = $miny;      // This row's min Y, for DrawXDataLine()
-            $this->data[$i][MAXY] = $maxy;      // This row's max Y, for DrawXDataLine()
-
-            $minminy = ($miny < $minminy) ? $miny : $minminy;   // global min
-            $maxmaxy = ($maxy > $maxmaxy) ? $maxy : $maxmaxy;   // global max
+            if (!empty($all_dv)) {
+                $this->data_min[$i] = min($all_dv);  // Store per-row DV range
+                $this->data_max[$i] = max($all_dv);
+            }
         }
 
-        $this->min_x = $minx;
-        $this->max_x = $maxx;
-        $this->min_y = $minminy;
-        $this->max_y = $maxmaxy;
-        $this->max_t = $maxt;
-
-        $this->data_limits_done = TRUE;
+        if ($this->datatype_swapped_xy) {
+            // Assign min and max for swapped X/Y plots: IV=Y and DV=X
+            $this->min_y = min($all_iv);
+            $this->max_y = max($all_iv);
+            if (empty($this->data_min)) { // Guard against regressive case: No X at all
+                $this->min_x = 0;
+                $this->max_x = 0;
+            } else {
+                $this->min_x = min($this->data_min);  // Store global X range
+                $this->max_x = max($this->data_max);
+            }
+        } else {
+            // Assign min and max for normal plots: IV=X and DV=Y
+            $this->min_x = min($all_iv);
+            $this->max_x = max($all_iv);
+            if (empty($this->data_min)) { // Guard against regressive case: No Y at all
+                $this->min_y = 0;
+                $this->max_y = 0;
+            } else {
+                $this->min_y = min($this->data_min);  // Store global Y range
+                $this->max_y = max($this->data_max);
+            }
+        }
 
+        if ($this->GetCallback('debug_scale')) {
+            $this->DoCallback('debug_scale', __FUNCTION__, array(
+                'min_x' => $this->min_x, 'min_y' => $this->min_y,
+                'max_x' => $this->max_x, 'max_y' => $this->max_y));
+        }
         return TRUE;
     }
 
-
-    /*!
+    /*
      * Calculates image margins on the fly from title positions and sizes,
      * and tick labels positions and sizes.
      *
-     * FIXME: fix x_data_label_pos behaviour. Now we are leaving room for it AND x_tick_label_pos
-     *        maybe it shouldn't be so...
+     * A picture of the locations of elements and spacing can be found in the
+     * PHPlot Reference Manual.
      *
-     * FIXME: y_data_label_pos is not yet used...
+     * Calculates the following (class variables unless noted):
      *
-     * TODO: add x_tick_label_width and y_tick_label_height and use them to calculate
-     *       max_x_labels and max_y_labels, to be used by drawing functions to avoid overlapping.
-     */
-    function CalcMargins()
-    {
-        // Temporary variables for label size calculation
-        $xlab = $this->FormatLabel('x', $this->max_x);
-        $ylab = $this->FormatLabel('y', $this->max_y);
+     * Plot area margins (see note below):
+     *     y_top_margin
+     *     y_bot_margin
+     *     x_left_margin
+     *     x_right_margin
+     *
+     * Title sizes (these are now local, not class variables, since they are not used elsewhere):
+     *     title_height : Height of main title
+     *     x_title_height : Height of X axis title, 0 if no X title
+     *     y_title_width : Width of Y axis title, 0 if no Y title
+     *
+     * Tick/Data label offsets, relative to plot_area:
+     *     x_label_top_offset, x_label_bot_offset, x_label_axis_offset
+     *     y_label_left_offset, y_label_right_offset, y_label_axis_offset
+     *
+     * Title offsets, relative to plot area:
+     *     x_title_top_offset, x_title_bot_offset
+     *     y_title_left_offset, y_title_left_offset
+     *     title_offset (for main title, relative to image edge)
+     *
+     *  Note: The margins are calculated, but not stored, if margins or plot area were
+     *  set by the user with SetPlotAreaPixels or SetMarginsPixels. The margin
+     *  calculation is mixed in with the offset variables, so it doesn't seem worth the
+     *  trouble to separate them.
+     *
+     * If the $maximize argument is true, we use the full image size, minus safe_margin
+     * and main title, for the plot. This is for pie charts which have no axes or X/Y titles.
+     */
+    protected function CalcMargins($maximize)
+    {
+        // This is the line-to-line or line-to-text spacing:
+        $gap = $this->safe_margin;
+        // Initial margin on each side takes into account a possible image border.
+        // For compatibility, if border is 1 or 2, don't increase the margins.
+        $base_margin = max($gap, $this->GetImageBorderWidth() + 3);
+        $this->title_offset = $base_margin;  // For use in DrawTitle
+
+        // Minimum margin on each side. This reduces the chance that the
+        // right-most tick label (for example) will run off the image edge
+        // if there are no titles on that side.
+        $min_margin = 2 * $gap + $base_margin;
+
+        // Calculate the title sizes:
+        list($unused, $title_height) = $this->SizeText($this->fonts['title'], 0, $this->title_txt);
+        list($unused, $x_title_height) = $this->SizeText($this->fonts['x_title'], 0, $this->x_title_txt);
+        list($y_title_width, $unused) = $this->SizeText($this->fonts['y_title'], 90, $this->y_title_txt);
+
+        // Special case for maximum area usage with no X/Y titles or labels, only main title:
+        if ($maximize) {
+            if (!isset($this->x_left_margin))
+                $this->x_left_margin = $base_margin;
+            if (!isset($this->x_right_margin))
+                $this->x_right_margin = $base_margin;
+            if (!isset($this->y_top_margin)) {
+                $this->y_top_margin = $base_margin;
+                if ($title_height > 0)
+                    $this->y_top_margin += $title_height + $gap;
+            }
+            if (!isset($this->y_bot_margin))
+                $this->y_bot_margin = $base_margin;
 
-        // dirty fix:
-        // max_t is the maximum data label length (from column 0 of each data row).
-        if ($this->max_t > strlen ($xlab))
-            $xlab = sprintf ("%{$this->max_t}s","_");
+            return TRUE;
+        }
 
-        //////// Calculate maximum X/Y axis label height and width:
+        // Make local variables for these. (They get used a lot and I'm tired of this, this, this.)
+        $x_tick_label_pos = $this->x_tick_label_pos;
+        $x_data_label_pos = $this->x_data_label_pos;
+        $x_tick_pos       = $this->x_tick_pos;
+        $x_tick_len       = $this->x_tick_length;
+        $y_tick_label_pos = $this->y_tick_label_pos;
+        $y_tick_pos       = $this->y_tick_pos;
+        $y_tick_len       = $this->y_tick_length;
+        $y_data_label_pos = $this->y_data_label_pos;
+
+        // For X/Y tick and label position of 'xaxis' or 'yaxis', determine if the axis happens to be
+        // on an edge of a plot. If it is, we need to account for the margins there.
+        if ($this->x_axis_position <= $this->plot_min_y)
+            $x_axis_pos = 'bottom';
+        elseif ($this->x_axis_position >= $this->plot_max_y)
+            $x_axis_pos = 'top';
+        else
+            $x_axis_pos = 'none';
+        if ($this->y_axis_position <= $this->plot_min_x)
+            $y_axis_pos = 'left';
+        elseif ($this->y_axis_position >= $this->plot_max_x)
+            $y_axis_pos = 'right';
+        else
+            $y_axis_pos = 'none';
+
+        // Calculate the heights for X tick and data labels, and the max (used if they are overlaid):
+        $x_data_label_height = ($x_data_label_pos == 'none') ? 0 : $this->CalcMaxDataLabelSize('x');
+        $x_tick_label_height = ($x_tick_label_pos == 'none') ? 0 : $this->CalcMaxTickLabelSize('x');
+        $x_max_label_height = max($x_data_label_height, $x_tick_label_height);
+
+        // Calculate the space needed above and below the plot for X tick and X data labels:
+
+        // Above the plot:
+        $tick_labels_above = ($x_tick_label_pos == 'plotup' || $x_tick_label_pos == 'both'
+                          || ($x_tick_label_pos == 'xaxis' && $x_axis_pos == 'top'));
+        $data_labels_above = ($x_data_label_pos == 'plotup' || $x_data_label_pos == 'both');
+        if ($tick_labels_above) {
+            if ($data_labels_above) {
+                $label_height_above = $x_max_label_height;
+            } else {
+                $label_height_above = $x_tick_label_height;
+            }
+        } elseif ($data_labels_above) {
+            $label_height_above = $x_data_label_height;
+        } else {
+            $label_height_above = 0;
+        }
 
-        // TTFonts:
-        if ($this->use_ttf) {
-            // Maximum X axis label height
-            $size = $this->TTFBBoxSize($this->x_label_font['size'], $this->x_label_angle,
-                                       $this->x_label_font['font'], $xlab);
-            $this->x_tick_label_height = $size[1];
-
-            // Maximum Y axis label width
-            $size = $this->TTFBBoxSize($this->y_label_font['size'], $this->y_label_angle,
-                                        $this->y_label_font['font'], $ylab);
-            $this->y_tick_label_width = $size[0];
-        }
-        // Fixed fonts:
-        else {
-            // Maximum X axis label height
-            if ($this->x_label_angle == 90)
-                $this->x_tick_label_height = strlen($xlab) * $this->x_label_font['width'];
-            else
-                $this->x_tick_label_height = $this->x_label_font['height'];
+        // Below the plot:
+        $tick_labels_below = ($x_tick_label_pos == 'plotdown' || $x_tick_label_pos == 'both'
+                          || ($x_tick_label_pos == 'xaxis' && $x_axis_pos == 'bottom'));
+        $data_labels_below = ($x_data_label_pos == 'plotdown' || $x_data_label_pos == 'both');
+        if ($tick_labels_below) {
+            if ($data_labels_below) {
+                $label_height_below = $x_max_label_height;
+            } else {
+                $label_height_below = $x_tick_label_height;
+            }
+        } elseif ($data_labels_below) {
+            $label_height_below = $x_data_label_height;
+        } else {
+            $label_height_below = 0;
+        }
 
-            // Maximum Y axis label width
-            $this->y_tick_label_width = strlen($ylab) * $this->y_label_font['width'];
+        // Calculate the width for Y tick and data labels, if on, and the max:
+        // Note CalcMaxDataLabelSize('y') returns 0 except for swapped X/Y plots.
+        $y_data_label_width = ($y_data_label_pos == 'none') ? 0 : $this->CalcMaxDataLabelSize('y');
+        $y_tick_label_width = ($y_tick_label_pos == 'none') ? 0 : $this->CalcMaxTickLabelSize('y');
+        $y_max_label_width = max($y_data_label_width, $y_tick_label_width);
+
+        // Calculate the space needed left and right of the plot for Y tick and Y data labels:
+        // (Y data labels here are for swapped X/Y plots such has horizontal bars)
+
+        // Left of the plot:
+        $tick_labels_left = ($y_tick_label_pos == 'plotleft' || $y_tick_label_pos == 'both'
+                         || ($y_tick_label_pos == 'yaxis' && $y_axis_pos == 'left'));
+        $data_labels_left = ($y_data_label_pos == 'plotleft' || $y_data_label_pos == 'both');
+        if ($tick_labels_left) {
+            if ($data_labels_left) {
+                $label_width_left = $y_max_label_width;
+            } else {
+                $label_width_left = $y_tick_label_width;
+            }
+        } elseif ($data_labels_left) {
+            $label_width_left = $y_data_label_width;
+        } else {
+            $label_width_left = 0;
         }
 
+        // Right of the plot:
+        $tick_labels_right = ($y_tick_label_pos == 'plotright' || $y_tick_label_pos == 'both'
+                          || ($y_tick_label_pos == 'yaxis' && $y_axis_pos == 'right'));
+        $data_labels_right = ($y_data_label_pos == 'plotright' || $y_data_label_pos == 'both');
+        if ($tick_labels_right) {
+            if ($data_labels_right) {
+                $label_width_right = $y_max_label_width;
+            } else {
+                $label_width_right = $y_tick_label_width;
+            }
+        } elseif ($data_labels_right) {
+            $label_width_right = $y_data_label_width;
+        } else {
+            $label_width_right = 0;
+        }
 
         ///////// Calculate margins:
 
-        // Upper title, ticks and tick labels, and data labels:
-        $this->y_top_margin = $this->title_height + $this->safe_margin * 2;
-
-        if ($this->x_title_pos == 'plotup' || $this->x_title_pos == 'both')
-            $this->y_top_margin += $this->x_title_height + $this->safe_margin;
-
-        if ($this->x_tick_label_pos == 'plotup' || $this->x_tick_label_pos == 'both')
-            $this->y_top_margin += $this->x_tick_label_height;
-
-        if ($this->x_tick_pos == 'plotup' || $this->x_tick_pos == 'both')
-            $this->y_top_margin += $this->x_tick_length * 2;
-
-        if ($this->x_data_label_pos == 'plotup' || $this->x_data_label_pos == 'both')
-            $this->y_top_margin += $this->x_tick_label_height;
-
-        // Lower title, ticks and tick labels, and data labels:
-        $this->y_bot_margin = $this->safe_margin * 2;
-
-        if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both')
-            $this->y_bot_margin += $this->x_title_height;
-
-        if ($this->x_tick_pos == 'plotdown' || $this->x_tick_pos == 'both')
-            $this->y_bot_margin += $this->x_tick_length * 2;
-
-        if ($this->x_tick_pos == 'xaxis' && ($this->x_axis_position == '' || $this->x_axis_position == 0))
-            $this->y_bot_margin += $this->x_tick_length * 2;
+        // Calculating Top and Bottom margins:
+        // y_top_margin: Main title, Upper X title, X ticks and tick labels, and X data labels:
+        // y_bot_margin: Lower title, ticks and tick labels, and data labels:
+        $top_margin = $base_margin;
+        $bot_margin = $base_margin;
+        $this->x_title_top_offset = $gap;
+        $this->x_title_bot_offset = $gap;
+
+        // Space for main title?
+        if ($title_height > 0)
+            $top_margin += $title_height + $gap;
+
+        // Space for X Title?
+        if ($x_title_height > 0) {
+            $pos = $this->x_title_pos;
+            if ($pos == 'plotup' || $pos == 'both')
+                $top_margin += $x_title_height + $gap;
+            if ($pos == 'plotdown' || $pos == 'both')
+                $bot_margin += $x_title_height + $gap;
+        }
 
-        if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both')            
-            $this->y_bot_margin += $this->x_tick_label_height;
+        // Space for X Labels above the plot?
+        if ($label_height_above > 0) {
+            $top_margin += $label_height_above + $gap;
+            $this->x_title_top_offset += $label_height_above + $gap;
+        }
 
-        if ($this->x_tick_label_pos == 'xaxis' && ($this->x_axis_position == '' || $this->x_axis_position == 0))
-            $this->y_bot_margin += $this->x_tick_label_height;
+        // Space for X Labels below the plot?
+        if ($label_height_below > 0) {
+            $bot_margin += $label_height_below + $gap;
+            $this->x_title_bot_offset += $label_height_below + $gap;
+        }
 
-        if ($this->x_data_label_pos == 'plotdown' || $this->x_data_label_pos == 'both')
-            $this->y_bot_margin += $this->x_tick_label_height;
+        // Space for X Ticks above the plot?
+        if ($x_tick_pos == 'plotup' || $x_tick_pos == 'both'
+           || ($x_tick_pos == 'xaxis' && $x_axis_pos == 'top')) {
+            $top_margin += $x_tick_len;
+            $this->x_label_top_offset = $x_tick_len + $gap;
+            $this->x_title_top_offset += $x_tick_len;
+        } else {
+            // No X Ticks above the plot:
+            $this->x_label_top_offset = $gap;
+        }
 
-        // Left title, ticks and tick labels:
-        $this->x_left_margin = $this->safe_margin * 2;
+        // Space for X Ticks below the plot?
+        if ($x_tick_pos == 'plotdown' || $x_tick_pos == 'both'
+           || ($x_tick_pos == 'xaxis' && $x_axis_pos == 'bottom')) {
+            $bot_margin += $x_tick_len;
+            $this->x_label_bot_offset = $x_tick_len + $gap;
+            $this->x_title_bot_offset += $x_tick_len;
+        } else {
+            // No X Ticks below the plot:
+            $this->x_label_bot_offset = $gap;
+        }
+        // Label offsets for on-axis ticks:
+        if ($x_tick_pos == 'xaxis') {
+            $this->x_label_axis_offset = $x_tick_len + $gap;
+        } else {
+            $this->x_label_axis_offset = $gap;
+        }
 
-        if ($this->y_title_pos == 'plotleft' || $this->y_title_pos == 'both')
-            $this->x_left_margin += $this->y_title_width + $this->safe_margin;
+        // Calculating Left and Right margins:
+        // x_left_margin: Left Y title, Y ticks and tick labels:
+        // x_right_margin: Right Y title, Y ticks and tick labels:
+        $left_margin = $base_margin;
+        $right_margin = $base_margin;
+        $this->y_title_left_offset = $gap;
+        $this->y_title_right_offset = $gap;
+
+        // Space for Y Title?
+        if ($y_title_width > 0) {
+            $pos = $this->y_title_pos;
+            if ($pos == 'plotleft' || $pos == 'both')
+                $left_margin += $y_title_width + $gap;
+            if ($pos == 'plotright' || $pos == 'both')
+                $right_margin += $y_title_width + $gap;
+        }
 
-        if ($this->y_tick_label_pos == 'plotleft' || $this->y_tick_label_pos == 'both')
-            $this->x_left_margin += $this->y_tick_label_width;
+        // Space for Y Labels left of the plot?
+        if ($label_width_left > 0) {
+            $left_margin += $label_width_left + $gap;
+            $this->y_title_left_offset += $label_width_left + $gap;
+        }
 
-        if ($this->y_tick_pos == 'plotleft' || $this->y_tick_pos == 'both')
-            $this->x_left_margin += $this->y_tick_length * 2 ;
+        // Space for Y Labels right of the plot?
+        if ($label_width_right > 0) {
+            $right_margin += $label_width_right + $gap;
+            $this->y_title_right_offset += $label_width_right + $gap;
+        }
 
-        // Right title, ticks and tick labels:
-        $this->x_right_margin = $this->safe_margin * 2;
+        // Space for Y Ticks left of plot?
+        if ($y_tick_pos == 'plotleft' || $y_tick_pos == 'both'
+           || ($y_tick_pos == 'yaxis' && $y_axis_pos == 'left')) {
+            $left_margin += $y_tick_len;
+            $this->y_label_left_offset = $y_tick_len + $gap;
+            $this->y_title_left_offset += $y_tick_len;
+        } else {
+            // No Y Ticks left of plot:
+            $this->y_label_left_offset = $gap;
+        }
 
-        if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both')
-            $this->x_right_margin += $this->y_title_width + $this->safe_margin;
+        // Space for Y Ticks right of plot?
+        if ($y_tick_pos == 'plotright' || $y_tick_pos == 'both'
+           || ($y_tick_pos == 'yaxis' && $y_axis_pos == 'right')) {
+            $right_margin += $y_tick_len;
+            $this->y_label_right_offset = $y_tick_len + $gap;
+            $this->y_title_right_offset += $y_tick_len;
+        } else {
+            // No Y Ticks right of plot:
+            $this->y_label_right_offset = $gap;
+        }
 
-        if ($this->y_tick_label_pos == 'plotright' || $this->y_tick_label_pos == 'both')
-            $this->x_right_margin += $this->y_tick_label_width;
+        // Label offsets for on-axis ticks:
+        if ($x_tick_pos == 'yaxis') {
+            $this->y_label_axis_offset = $y_tick_len + $gap;
+        } else {
+            $this->y_label_axis_offset = $gap;
+        }
 
-        if ($this->y_tick_pos == 'plotright' || $this->y_tick_pos == 'both')
-            $this->x_right_margin += $this->y_tick_length * 2;
+        // Apply the minimum margins and store in the object.
+        // Do not set margins which were user-defined (see note at top of function).
+        if (!isset($this->y_top_margin))
+            $this->y_top_margin = max($min_margin, $top_margin);
+        if (!isset($this->y_bot_margin))
+            $this->y_bot_margin = max($min_margin, $bot_margin);
+        if (!isset($this->x_left_margin))
+            $this->x_left_margin = max($min_margin, $left_margin);
+        if (!isset($this->x_right_margin))
+            $this->x_right_margin = max($min_margin, $right_margin);
+
+        if ($this->GetCallback('debug_scale')) {
+            // (Too bad compact() doesn't work on class member variables...)
+            $this->DoCallback('debug_scale', __FUNCTION__, array(
+                'label_height_above' => $label_height_above,
+                'label_height_below' => $label_height_below,
+                'label_width_left' => $label_width_left,
+                'label_width_right' => $label_width_right,
+                'x_tick_len' => $x_tick_len,
+                'y_tick_len' => $y_tick_len,
+                'x_left_margin' => $this->x_left_margin,
+                'x_right_margin' => $this->x_right_margin,
+                'y_top_margin' => $this->y_top_margin,
+                'y_bot_margin' => $this->y_bot_margin,
+                'x_label_top_offset' => $this->x_label_top_offset,
+                'x_label_bot_offset' => $this->x_label_bot_offset,
+                'y_label_left_offset' => $this->y_label_left_offset,
+                'y_label_right_offset' => $this->y_label_right_offset,
+                'x_title_top_offset' => $this->x_title_top_offset,
+                'x_title_bot_offset' => $this->x_title_bot_offset,
+                'y_title_left_offset' => $this->y_title_left_offset,
+                'y_title_right_offset' => $this->y_title_right_offset));
+        }
 
+        return TRUE;
+    }
 
-        $this->x_tot_margin = $this->x_left_margin + $this->x_right_margin;
-        $this->y_tot_margin = $this->y_top_margin + $this->y_bot_margin;
+    /*
+     * Calculate the plot area (device coordinates) from the margins.
+     * (This used to be part of SetPlotAreaPixels.)
+     * The margins might come from SetMarginsPixels, SetPlotAreaPixels,
+     * or CalcMargins.
+     */
+    protected function CalcPlotAreaPixels()
+    {
+        $this->plot_area = array($this->x_left_margin, $this->y_top_margin,
+                                 $this->image_width - $this->x_right_margin,
+                                 $this->image_height - $this->y_bot_margin);
+        $this->plot_area_width = $this->plot_area[2] - $this->plot_area[0];
+        $this->plot_area_height = $this->plot_area[3] - $this->plot_area[1];
 
-        return;
+        $this->DoCallback('debug_scale', __FUNCTION__, $this->plot_area);
+        return TRUE;
     }
 
-
-    /*!
+    /*
      * Set the margins in pixels (left, right, top, bottom)
+     * This determines the plot area, equivalent to SetPlotAreaPixels().
+     * Deferred calculations now occur in CalcPlotAreaPixels().
      */
-    function SetMarginsPixels($which_lm, $which_rm, $which_tm, $which_bm)
-    { 
-
+    function SetMarginsPixels($which_lm = NULL, $which_rm = NULL, $which_tm = NULL, $which_bm = NULL)
+    {
         $this->x_left_margin = $which_lm;
         $this->x_right_margin = $which_rm;
-        $this->x_tot_margin = $which_lm + $which_rm;
-
         $this->y_top_margin = $which_tm;
         $this->y_bot_margin = $which_bm;
-        $this->y_tot_margin = $which_tm + $which_bm;
-
-        $this->SetPlotAreaPixels();
 
-        return;
+        return TRUE;
     }
 
-
-    /*!
-     * Sets the limits for the plot area. If no arguments are supplied, uses
-     * values calculated from CalcMargins();
-     * Like in GD, (0,0) is upper left
-     *
-     * This resets the scale if SetPlotAreaWorld() was already called
-     */
-    function SetPlotAreaPixels($x1=NULL, $y1=NULL, $x2=NULL, $y2=NULL) 
-    {
-        if ($x2 && $y2) {
-            $this->plot_area = array($x1, $y1, $x2, $y2);
-        } else {
-            if (! isset($this->x_tot_margin))
-                $this->CalcMargins();
-
-            $this->plot_area = array($this->x_left_margin, $this->y_top_margin,
-                                     $this->image_width - $this->x_right_margin,
-                                     $this->image_height - $this->y_bot_margin);
-        }
-        $this->plot_area_width = $this->plot_area[2] - $this->plot_area[0];
-        $this->plot_area_height = $this->plot_area[3] - $this->plot_area[1];
-
-        // Reset the scale with the new plot area.
-        if (isset($this->plot_max_x))
-            $this->CalcTranslation();
+    /*
+     * Sets the limits for the plot area.
+     * This stores the margins, not the area. That may seem odd, but
+     * the idea is to make SetPlotAreaPixels and SetMarginsPixels two
+     * ways to accomplish the same thing, and the deferred calculations
+     * in CalcMargins and CalcPlotAreaPixels don't need to know which
+     * was used.
+     *   (x1, y1) - Upper left corner of the plot area
+     *   (x2, y2) - Lower right corner of the plot area
+     */
+    function SetPlotAreaPixels($x1 = NULL, $y1 = NULL, $x2 = NULL, $y2 = NULL)
+    {
+        $this->x_left_margin = $x1;
+        if (isset($x2)) $this->x_right_margin = $this->image_width - $x2;
+        else unset($this->x_right_margin);
+        $this->y_top_margin = $y1;
+        if (isset($y2)) $this->y_bot_margin = $this->image_height - $y2;
+        else unset($this->y_bot_margin);
 
         return TRUE;
-
     }
 
-
-    /*!
-     * Sets minimum and maximum x and y values in the plot using FindDataLimits()
-     * or from the supplied parameters, if any.
+    /*
+     * Calculate the World Coordinate limits of the plot area.
+     * This goes with SetPlotAreaWorld, but the calculations are
+     * deferred until the graph is being drawn.
+     * Uses and sets: plot_min_x, plot_max_x, plot_min_y, plot_max_y
+     * These can be user-supplied or NULL to auto-calculate.
+     * Pre-requisites: FindDataLimits() calculates min_x, max_x, min_y, max_y
+     * which are the limits of the data to be plotted.
      *
-     * This resets the scale if SetPlotAreaPixels() was already called
-     */
-    function SetPlotAreaWorld($xmin=NULL, $ymin=NULL, $xmax=NULL, $ymax=NULL) 
-    {
-        if (! isset($this->data_limits_done)) { // For automatic setting of data we need data limits
-            $this->FindDataLimits() ;
-        }
-        if ($xmin === NULL || $xmin === '') {
-            if ($this->data_type == 'text-data')  // Valid for data without X values only.
-                $xmin = 0;
-            else
-                $xmin = $this->min_x;
-        }
-        if ($xmax === NULL || $xmax === '') {
-            if ($this->data_type == 'text-data')  // Valid for data without X values only.
-                $xmax = $this->max_x + 1;
-            else
-                $xmax = $this->max_x;
-        }
+     * Note: $implied_y and $swapped_xy are currently equivalent, but in the
+     * future there may be a data type with swapped X/Y and explicit Y values.
+     * The 4 code blocks below for plot_min_x, plot_max_x, plot_min_y, and
+     * plot_max_y already contain logic for this case.
+     * The general method is this:
+     *   If any part of the range is user-defined (via SetPlotAreaWorld),
+     *      use the user-defined value.
+     *   Else, if this is an implicitly-defined independent variable,
+     *      use the fixed range of 0 to (max+1).
+     *   Else, if this is an explicitly-defined independent variable,
+     *      use the exact data range (min to max).
+     *   Else, this is the dependent variable, so define a range which
+     *      includes and exceeds the data range by a bit.
+     */
+    protected function CalcPlotAreaWorld()
+    {
+        // Data array omits X or Y?
+        $implied_x = $this->datatype_implied && !$this->datatype_swapped_xy;
+        $implied_y = $this->datatype_implied && $this->datatype_swapped_xy;
+
+        if (isset($this->plot_min_x) && $this->plot_min_x !== '')
+            $xmin = $this->plot_min_x; // Use user-provided value
+        elseif ($implied_x)
+            $xmin = 0;          // Implied X starts at zero
+        elseif ($this->datatype_swapped_xy)
+            // If X is the dependent variable, leave some room below.
+            $xmin = floor($this->min_x - abs($this->min_x) * 0.1);
+        else
+            $xmin = $this->min_x;  // Otherwise just start at the min data X
+
+        if (isset($this->plot_max_x) && $this->plot_max_x !== '')
+            $xmax = $this->plot_max_x; // Use user-provided value
+        elseif ($implied_x)
+            $xmax = $this->max_x + 1; // Implied X ends after last value
+        elseif ($this->datatype_swapped_xy)
+            // If X is the dependent variable, leave some room above.
+            $xmax = ceil($this->max_x + abs($this->max_x) * 0.1);
+        else
+            $xmax = $this->max_x; // Otherwise just end at the max data X
+
+        if (isset($this->plot_min_y) && $this->plot_min_y !== '')
+            $ymin = $this->plot_min_y;  // Use user-provided value
+        elseif ($implied_y)
+            $ymin = 0;    // Implied Y starts at zero
+        elseif ($this->datatype_swapped_xy)
+            $ymin = $this->min_y; // Start at min data Y
+        else
+            // If Y is the dependent variable, leave some room below.
+            $ymin = floor($this->min_y - abs($this->min_y) * 0.1);
+
+        if (isset($this->plot_max_y) && $this->plot_max_y !== '')
+            $ymax = $this->plot_max_y; // Use user-provided value
+        elseif ($implied_y)
+            $ymax = $this->max_y + 1; // Implied Y ends after last value
+        elseif ($this->datatype_swapped_xy)
+            $ymax = $this->max_y;  // End at max data Y
+        else
+            // If Y is the dependent variable, leave some room above.
+            $ymax = ceil($this->max_y + abs($this->max_y) * 0.1);
 
-        // Leave room above and below the highest and lowest data points.
-        
-        if ($ymin === NULL || $ymin === '') {
-            if ($this->min_y < 0)
-                $ymin = ceil($this->min_y * 1.1);
-            else
-                $ymin = floor($this->min_y * 0.9);
-        }    
-        if ($ymax === NULL || $ymax === '') {
-            if ($this->max_y < 0)
-                $ymax = floor($this->max_y * 0.9);
-            else
-                $ymax = ceil($this->max_y * 1.1);
-        }
-        
         // Error checking
-        
-        if ($ymin == $ymax)     // Minimum height
-            $ymax += 1;
+
+        if ($ymin == $ymax)
+            $ymax++;
+        if ($xmin == $xmax)
+            $xmax++;
 
         if ($this->yscale_type == 'log') {
-            if ($ymin <= 0) { 
+            if ($ymin <= 0) {
                 $ymin = 1;
             }
             if ($ymax <= 0) {
-                $this->PrintError('SetPlotAreaWorld(): Log plots need data greater than 0');
-                return FALSE;
+                // Note: Error messages reference the user function, not this function.
+                return $this->PrintError('SetPlotAreaWorld(): Log plots need data greater than 0');
             }
         }
-        
+
         if ($ymax <= $ymin) {
-            $this->DrawError('SetPlotAreaWorld(): Error in data - max not greater than min');
-            return FALSE;
+            return $this->PrintError('SetPlotAreaWorld(): Error in data - max not greater than min');
         }
-       
-      
-        // Reset (if it was already set) the scale with the new maxs and mins
-      
+
         $this->plot_min_x = $xmin;
         $this->plot_max_x = $xmax;
         $this->plot_min_y = $ymin;
         $this->plot_max_y = $ymax;
-
-        if (isset($this->plot_area_width)) {
-            $this->CalcTranslation();
+        if ($this->GetCallback('debug_scale')) {
+            $this->DoCallback('debug_scale', __FUNCTION__, array(
+                'plot_min_x' => $this->plot_min_x, 'plot_min_y' => $this->plot_min_y,
+                'plot_max_x' => $this->plot_max_x, 'plot_max_y' => $this->plot_max_y));
         }
 
         return TRUE;
-    } //function SetPlotAreaWorld
-
+    }
 
-    /*!
-     * For bar plots, which have equally spaced x variables.
+    /*
+     * Stores the desired World Coordinate range of the plot.
+     * The user calls this to force one or more of the range limits to
+     * specific values. Anything not set will be calculated in CalcPlotAreaWorld().
      */
-    function CalcBarWidths() 
+    function SetPlotAreaWorld($xmin=NULL, $ymin=NULL, $xmax=NULL, $ymax=NULL)
     {
-        $group_width = ($this->plot_area[2] - $this->plot_area[0]) /
-                      $this->num_data_rows * $this->group_frac_width;
-        if ($this->plot_type == 'bars') {
-            $this->record_bar_width = $group_width / $this->records_per_group;
-        } else if ($this->plot_type == 'stackedbars') {
-            $this->record_bar_width = $group_width;
-        }            
-        $this->data_group_space = $group_width / 2;
+        $this->plot_min_x = $xmin;
+        $this->plot_max_x = $xmax;
+        $this->plot_min_y = $ymin;
+        $this->plot_max_y = $ymax;
+        return TRUE;
+    }
+
+    /*
+     * Calculate the width (or height) of bars for bar plots.
+     * This calculates:
+     *    record_bar_width : Allocated width for each bar (including gaps)
+     *    actual_bar_width : Actual drawn width of each bar
+     *    bar_adjust_gap  : Gap on each side of each bar (0 if they touch)
+     * For the case $verticals=False, horizontal bars are being drawn,
+     * but the same variable names are used. Think of "bar_width" as being
+     * the width if you are standing on the Y axis looking towards positive X.
+     */
+    protected function CalcBarWidths($verticals = TRUE)
+    {
+        // group_width is the width of a group, including padding
+        if ($verticals) {
+            $group_width = $this->plot_area_width / $this->num_data_rows;
+        } else {
+            $group_width = $this->plot_area_height / $this->num_data_rows;
+        }
+
+        // Actual number of bar spaces in the group. This includes the drawn bars, and
+        // 'bar_extra_space'-worth of extra bars.
+        if ($this->plot_type == 'stackedbars') {
+          $num_spots = 1 + $this->bar_extra_space;
+        } else {
+          $num_spots = $this->data_columns + $this->bar_extra_space;
+        }
+
+        // record_bar_width is the width of each bar's allocated area.
+        // If bar_width_adjust=1 this is the width of the bar, otherwise
+        // the bar is centered inside record_bar_width.
+        // The equation is:
+        //   group_frac_width * group_width = record_bar_width * num_spots
+        $this->record_bar_width = $this->group_frac_width * $group_width / $num_spots;
+
+        // Note that the extra space due to group_frac_width and bar_extra_space will be
+        // evenly divided on each side of the group: the drawn bars are centered in the group.
+
+        // Within each bar's allocated space, if bar_width_adjust=1 the bar fills the
+        // space, otherwise it is centered.
+        // This is the actual drawn bar width:
+        $this->actual_bar_width = $this->record_bar_width * $this->bar_width_adjust;
+        // This is the gap on each side of the bar (0 if bar_width_adjust=1):
+        $this->bar_adjust_gap = ($this->record_bar_width - $this->actual_bar_width) / 2;
+
+        if ($this->GetCallback('debug_scale')) {
+            $this->DoCallback('debug_scale', __FUNCTION__, array(
+                'record_bar_width' => $this->record_bar_width,
+                'actual_bar_width' => $this->actual_bar_width,
+                'bar_adjust_gap' => $this->bar_adjust_gap));
+        }
+        return TRUE;
+    }
+
+    /*
+     * Calculate X and Y Axis Positions, world coordinates.
+     * This needs the min/max x/y range set by CalcPlotAreaWorld.
+     * It adjusts or sets x_axis_position and y_axis_position per the data.
+     * Empty string means the values need to be calculated; otherwise they
+     * are supplied but need to be validated against the World area.
+     *
+     * Note: This used to be in CalcTranslation, but CalcMargins needs it too.
+     * This does not calculate the pixel values of the axes. That happens in
+     * CalcTranslation, after scaling is set up (which has to happen after
+     * margins are set up).
+     *
+     * For vertical plots, the X axis defaults to Y=0 if that is inside the plot range, else whichever
+     * of the top or bottom that has the smallest absolute value (that is, the value closest to 0).
+     * The Y axis defaults to the left edge. For horizontal plots, the axis roles and defaults are switched.
+     */
+    protected function CalcAxisPositions()
+    {
+        // Validate user-provided X axis position, or calculate a default if not provided:
+        if ($this->x_axis_position !== '') {
+            // Force user-provided X axis position to be within the plot range:
+            $this->x_axis_position = min(max($this->plot_min_y, $this->x_axis_position), $this->plot_max_y);
+        } elseif ($this->yscale_type == 'log') {
+            // Always use 1 for X axis position on log scale plots.
+            $this->x_axis_position = 1;
+        } elseif ($this->datatype_swapped_xy || $this->plot_min_y > 0) {
+            // Horizontal plot, or Vertical Plot with all Y > 0: Place X axis on the bottom.
+            $this->x_axis_position = $this->plot_min_y;
+        } elseif ($this->plot_max_y < 0) {
+            // Vertical plot with all Y < 0, so place the X axis at the top.
+            $this->x_axis_position = $this->plot_max_y;
+        } else {
+            // Vertical plot range includes Y=0, so place X axis at 0.
+            $this->x_axis_position = 0;
+        }
+
+        // Validate user-provided Y axis position, or calculate a default if not provided:
+        if ($this->y_axis_position !== '') {
+            // Force user-provided Y axis position to be within the plot range:
+            $this->y_axis_position = min(max($this->plot_min_x, $this->y_axis_position), $this->plot_max_x);
+        } elseif ($this->xscale_type == 'log') {
+            // Always use 1 for Y axis position on log scale plots.
+            $this->y_axis_position = 1;
+        } elseif (!$this->datatype_swapped_xy || $this->plot_min_x > 0) {
+            // Vertical plot, or Horizontal Plot with all X > 0: Place Y axis on left side.
+            $this->y_axis_position = $this->plot_min_x;
+        } elseif ($this->plot_max_x < 0) {
+            // Horizontal plot with all X < 0, so place the Y axis on the right side.
+            $this->y_axis_position = $this->plot_max_x;
+        } else {
+            // Horizontal plot range includes X=0: place Y axis at 0.
+            $this->y_axis_position = 0;
+        }
+
+        if ($this->GetCallback('debug_scale')) {
+            $this->DoCallback('debug_scale', __FUNCTION__, array(
+                'x_axis_position' => $this->x_axis_position,
+                'y_axis_position' => $this->y_axis_position));
+        }
+
         return TRUE;
     }
 
-    /*!
+    /*
      * Calculates scaling stuff...
      */
-    function CalcTranslation()
+    protected function CalcTranslation()
     {
         if ($this->plot_max_x - $this->plot_min_x == 0) { // Check for div by 0
             $this->xscale = 0;
         } else {
             if ($this->xscale_type == 'log') {
-                $this->xscale = ($this->plot_area_width)/(log10($this->plot_max_x) - log10($this->plot_min_x));
+                $this->xscale = $this->plot_area_width /
+                                (log10($this->plot_max_x) - log10($this->plot_min_x));
             } else {
-                $this->xscale = ($this->plot_area_width)/($this->plot_max_x - $this->plot_min_x);
+                $this->xscale = $this->plot_area_width / ($this->plot_max_x - $this->plot_min_x);
             }
         }
 
@@ -2187,9 +3390,10 @@ class PHPlot {
             $this->yscale = 0;
         } else {
             if ($this->yscale_type == 'log') {
-                $this->yscale = ($this->plot_area_height)/(log10($this->plot_max_y) - log10($this->plot_min_y));
+                $this->yscale = $this->plot_area_height /
+                                (log10($this->plot_max_y) - log10($this->plot_min_y));
             } else {
-                $this->yscale = ($this->plot_area_height)/($this->plot_max_y - $this->plot_min_y);
+                $this->yscale = $this->plot_area_height / ($this->plot_max_y - $this->plot_min_y);
             }
         }
         // GD defines x = 0 at left and y = 0 at TOP so -/+ respectively
@@ -2200,233 +3404,497 @@ class PHPlot {
         }
         if ($this->yscale_type == 'log') {
             $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * log10($this->plot_min_y));
-        } else { 
+        } else {
             $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * $this->plot_min_y);
         }
 
-        $this->scale_is_set = TRUE;
+        // Convert axis positions to device coordinates:
+        $this->y_axis_x_pixels = $this->xtr($this->y_axis_position);
+        $this->x_axis_y_pixels = $this->ytr($this->x_axis_position);
 
-        /************** FIXME?? *************/
-        // There should be a better place for this.
-
-        // User provided y axis position?
-        if ($this->y_axis_position != '') {
-            // Make sure we draw our axis inside the plot
-            $this->y_axis_position = ($this->y_axis_position < $this->plot_min_x)
-                                     ? $this->plot_min_x : $this->y_axis_position;
-            $this->y_axis_position = ($this->y_axis_position > $this->plot_max_x)
-                                     ? $this->plot_max_x : $this->y_axis_position;
-            $this->y_axis_x_pixels = $this->xtr($this->y_axis_position);
-        } else {
-            // Default to left axis
-            $this->y_axis_x_pixels = $this->xtr($this->plot_min_x);
-        }
-        // User provided x axis position?
-        if ($this->x_axis_position != '') {
-            // Make sure we draw our axis inside the plot
-            $this->x_axis_position = ($this->x_axis_position < $this->plot_min_y)
-                                     ? $this->plot_min_y : $this->x_axis_position;
-            $this->x_axis_position = ($this->x_axis_position > $this->plot_max_y)
-                                     ? $this->plot_max_y : $this->x_axis_position;
-            $this->x_axis_y_pixels = $this->ytr($this->x_axis_position);
-        } else { 
-            if ($this->yscale_type == 'log')
-                $this->x_axis_y_pixels = $this->ytr(1);
-            else
-                // Default to axis at 0 or plot_min_y (should be 0 anyway, from SetPlotAreaWorld())
-                $this->x_axis_y_pixels = ($this->plot_min_y <= 0) && (0 <= $this->plot_max_y) 
-                                         ? $this->ytr(0) : $this->ytr($this->plot_min_y);
+        if ($this->GetCallback('debug_scale')) {
+            $this->DoCallback('debug_scale', __FUNCTION__, array(
+                'xscale' => $this->xscale, 'yscale' => $this->yscale,
+                'plot_origin_x' => $this->plot_origin_x, 'plot_origin_y' => $this->plot_origin_y,
+                'y_axis_x_pixels' => $this->y_axis_x_pixels,
+                'x_axis_y_pixels' => $this->x_axis_y_pixels));
         }
 
-    } // function CalcTranslation()
-
+        return TRUE;
+    }
 
-    /*!
+    /*
      * Translate X world coordinate into pixel coordinate
-     * Needs values calculated by _CalcTranslation()
+     * See CalcTranslation() for calculation of xscale.
+     * Note: This function should be 'protected', but is left public for historical reasons.
+     * See GetDeviceXY() for a preferred public method.
      */
-    function xtr($x_world) 
+    function xtr($x_world)
     {
-        //$x_pixels =  $this->x_left_margin + ($this->image_width - $this->x_tot_margin)*
-        //      (($x_world - $this->plot_min_x) / ($this->plot_max_x - $this->plot_min_x)) ;
-        //which with a little bit of math reduces to ...
-        if ($this->xscale_type == 'log') { 
+        if ($this->xscale_type == 'log') {
             $x_pixels = $this->plot_origin_x + log10($x_world) * $this->xscale ;
-        } else { 
+        } else {
             $x_pixels = $this->plot_origin_x + $x_world * $this->xscale ;
         }
         return round($x_pixels);
     }
 
-
-    /*!
+    /*
      * Translate Y world coordinate into pixel coordinate.
-     * Needs values calculated by _CalcTranslation()
+     * See CalcTranslation() for calculation of yscale.
+     * Note: This function should be 'protected', but is left public for historical reasons.
+     * See GetDeviceXY() for a preferred public method.
      */
-    function ytr($y_world) 
+    function ytr($y_world)
     {
         if ($this->yscale_type == 'log') {
             //minus because GD defines y = 0 at top. doh!
             $y_pixels =  $this->plot_origin_y - log10($y_world) * $this->yscale ;
-        } else { 
-            $y_pixels =  $this->plot_origin_y - $y_world * $this->yscale ;  
+        } else {
+            $y_pixels =  $this->plot_origin_y - $y_world * $this->yscale ;
         }
         return round($y_pixels);
     }
 
-    /*!
-     * Formats a tick or data label.
+    /* A public interface to xtr and ytr. Translates (x,y) in world coordinates
+     * to (x,y) in device coordinates and returns them as an array.
+     * Usage is: list($x_pixel, $y_pixel) = $plot->GetDeviceXY($x_world, $y_world)
+     */
+    function GetDeviceXY($x_world, $y_world)
+    {
+        if (!isset($this->xscale)) {
+            return $this->PrintError("GetDeviceXY() was called before translation factors were calculated");
+        }
+        return array($this->xtr($x_world), $this->ytr($y_world));
+    }
+
+    /*
+     * Calculate tick parameters: Start, end, and delta values. This is used
+     * by both DrawXTicks() and DrawYTicks().
+     * This currently uses the same simplistic method previously used by
+     * PHPlot (basically just range/10), but splitting this out into its
+     * own function is the first step in replacing the method.
+     * This is also used by CalcMaxTickSize() for CalcMargins().
+     *
+     *   $which : 'x' or 'y' : Which tick parameters to calculate
      *
-     * \note Time formatting suggested by Marlin Viss
+     * Returns an array of 3 elements: tick_start, tick_end, tick_step
+     */
+    protected function CalcTicks($which)
+    {
+        if ($which == 'x') {
+            $num_ticks = $this->num_x_ticks;
+            $tick_inc = $this->x_tick_inc;
+            $data_max = $this->plot_max_x;
+            $data_min = $this->plot_min_x;
+            $skip_lo = $this->skip_left_tick;
+            $skip_hi = $this->skip_right_tick;
+        } elseif ($which == 'y') {
+            $num_ticks = $this->num_y_ticks;
+            $tick_inc = $this->y_tick_inc;
+            $data_max = $this->plot_max_y;
+            $data_min = $this->plot_min_y;
+            $skip_lo = $this->skip_bottom_tick;
+            $skip_hi = $this->skip_top_tick;
+        } else {
+          return $this->PrintError("CalcTicks: Invalid usage ($which)");
+        }
+
+        if (!empty($tick_inc)) {
+            $tick_step = $tick_inc;
+        } elseif (!empty($num_ticks)) {
+            $tick_step = ($data_max - $data_min) / $num_ticks;
+        } else {
+            $tick_step = ($data_max - $data_min) / 10;
+        }
+
+        // NOTE: When working with floats, because of approximations when adding $tick_step,
+        // the value may not quite reach the end, or may exceed it very slightly.
+        // So apply a "fudge" factor.
+        $tick_start = (double)$data_min;
+        $tick_end = (double)$data_max + ($data_max - $data_min) / 10000.0;
+
+        if ($skip_lo)
+            $tick_start += $tick_step;
+
+        if ($skip_hi)
+            $tick_end -= $tick_step;
+
+        return array($tick_start, $tick_end, $tick_step);
+    }
+
+    /*
+     * Calculate the size of the biggest tick label. This is used by CalcMargins().
+     * For 'x' ticks, it returns the height . For 'y' ticks, it returns the width.
+     * This means height along Y, or width along X - not relative to the text angle.
+     * That is what we need to calculate the needed margin space.
+     * (Previous versions of PHPlot estimated this, using the maximum X or Y value,
+     * or maybe the longest string. That doesn't work. -10 is longer than 9, etc.
+     * So this gets the actual size of each label, slow as that may be.
+     */
+    protected function CalcMaxTickLabelSize($which)
+    {
+        list($tick_start, $tick_end, $tick_step) = $this->CalcTicks($which);
+
+        if ($which == 'x') {
+          $font = $this->fonts['x_label'];
+          $angle = $this->x_label_angle;
+        } elseif ($which == 'y') {
+          $font = $this->fonts['y_label'];
+          $angle = $this->y_label_angle;
+        } else {
+          return $this->PrintError("CalcMaxTickLabelSize: Invalid usage ($which)");
+        }
+
+        $max_width = 0;
+        $max_height = 0;
+
+        // Loop over ticks, same as DrawXTicks and DrawYTicks:
+        // Avoid cumulative round-off errors from $val += $delta
+        $n = 0;
+        $tick_val = $tick_start;
+        while ($tick_val <= $tick_end) {
+            $tick_label = $this->FormatLabel($which, $tick_val);
+            list($width, $height) = $this->SizeText($font, $angle, $tick_label);
+            if ($width > $max_width) $max_width = $width;
+            if ($height > $max_height) $max_height = $height;
+            $tick_val = $tick_start + ++$n * $tick_step;
+        }
+        if ($this->GetCallback('debug_scale')) {
+            $this->DoCallback('debug_scale', __FUNCTION__, array(
+                'which' => $which, 'height' => $max_height, 'width' => $max_width));
+        }
+
+        if ($which == 'x')
+            return $max_height;
+        return $max_width;
+    }
+
+    /*
+     * Calculate the size of the biggest data label. This is used by CalcMargins().
+     * For $which='x', it returns the height of labels along the top or bottom.
+     * For $which='y', it returns the width of labels along the left or right sides.
+     * There is only one set of data labels (the first position in each data record).
+     * They normally go along the top or bottom (or both). If the data type indicates
+     * X/Y swapping (which is used for horizontal bar charts), the data labels go
+     * along the sides instead. So CalcMaxDataLabelSize('x') returns 0 if the
+     * data is X/Y swapped, and CalcMaxDataLabelSize('y') returns 0 if the data is
+     * is not X/Y swapped.
+     */
+    protected function CalcMaxDataLabelSize($which = 'x')
+    {
+        if ($which == 'x') {
+          if ($this->datatype_swapped_xy)
+              return 0; // Shortcut: labels aren't on top/bottom.
+          $font = $this->fonts['x_label'];
+          $angle = $this->x_data_label_angle;
+          $format_code = 'xd';
+        } elseif ($which == 'y') {
+          if (!$this->datatype_swapped_xy)
+              return 0; // Shortcut: labels aren't on left/right.
+          $font = $this->fonts['y_label'];
+          $angle = $this->y_data_label_angle;
+          $format_code = 'yd';
+        } else {
+          return $this->PrintError("CalcMaxDataLabelSize: Invalid usage ($which)");
+        }
+        $max_width = 0;
+        $max_height = 0;
+
+        // Loop over all data labels and find the biggest:
+        for ($i = 0; $i < $this->num_data_rows; $i++) {
+            $label = $this->FormatLabel($format_code, $this->data[$i][0]);
+            list($width, $height) = $this->SizeText($font, $angle, $label);
+            if ($width > $max_width) $max_width = $width;
+            if ($height > $max_height) $max_height = $height;
+        }
+        if ($this->GetCallback('debug_scale')) {
+            $this->DoCallback('debug_scale', __FUNCTION__, array(
+                'height' => $max_height, 'width' => $max_width));
+        }
+
+        if ($this->datatype_swapped_xy)
+            return $max_width;
+        return $max_height;
+    }
+
+    /*
+     * Set grid control defaults.
+     * X grid defaults off, Y grid defaults on, except the reverse is true
+     * with swapped graphs such as horizontal bars.
      */
-    function FormatLabel($which_pos, $which_lab)
+    protected function CalcGridSettings()
     {
-        switch ($which_pos) {
-        case 'x':
-        case 'plotx':
-            switch ($this->x_label_type) {
-            case 'title':
-                $lab = @ $this->data[$which_lab][0];
+        if (!isset($this->draw_x_grid))
+            $this->draw_x_grid = $this->datatype_swapped_xy;
+        if (!isset($this->draw_y_grid))
+            $this->draw_y_grid = !$this->datatype_swapped_xy;
+    }
+
+    /*
+     * Helper for CheckLabels() - determine if there are any non-empty labels.
+     * Returns True if all data labels are empty, else False.
+     */
+    protected function CheckLabelsAllEmpty()
+    {
+        for ($i = 0; $i < $this->num_data_rows; $i++)
+            if ($this->data[$i][0] !== '') return FALSE;
+        return TRUE;
+    }
+
+    /*
+     * Check and set label parameters. This handles deferred processing for label
+     * positioning and other label-related parameters.
+     *   Copy label_format from 'x' to 'xd', and 'y' to 'yd', if not already set.
+     *   Set x_data_label_angle from x_label_angle, if not already set.
+     *   Apply defaults to X and Y tick and data label positions.
+     * Note: the label strings in the data array are used as X data labels in
+     * the normal case, but as Y data labels in the swapped X/Y case.
+     */
+    protected function CheckLabels()
+    {
+        // The X and Y data labels are formatted the same as X and Y tick labels,
+        // unless overridden. Check and apply defaults for FormatLabel here:
+        if (empty($this->label_format['xd']) && !empty($this->label_format['x']))
+            $this->label_format['xd'] = $this->label_format['x'];
+        if (empty($this->label_format['yd']) && !empty($this->label_format['y']))
+            $this->label_format['yd'] = $this->label_format['y'];
+
+        // The X tick label angle setting controls X data label angles too,
+        // unless overridden. Check and apply the default here:
+        if (!isset($this->x_data_label_angle))
+            $this->x_data_label_angle = $this->x_label_angle;
+        // Note: Y data label angle defaults to zero, unlike X,
+        // for compatibility with older releases.
+
+        // X Label position fixups, for x_data_label_pos and x_tick_label_pos:
+        if ($this->datatype_swapped_xy) {
+            // Just apply defaults - there is no position conflict for X labels.
+            if (!isset($this->x_tick_label_pos))
+                $this->x_tick_label_pos = 'plotdown';
+            if (!isset($this->x_data_label_pos))
+                $this->x_data_label_pos = 'none';
+        } else {
+            // Apply defaults but do not allow conflict between tick and data labels.
+            if (isset($this->x_data_label_pos)) {
+                if (!isset($this->x_tick_label_pos)) {
+                    // Case: data_label_pos is set, tick_label_pos needs a default:
+                    if ($this->x_data_label_pos == 'none')
+                        $this->x_tick_label_pos = 'plotdown';
+                    else
+                        $this->x_tick_label_pos = 'none';
+                }
+            } elseif (isset($this->x_tick_label_pos)) {
+                // Case: tick_label_pos is set, data_label_pos needs a default:
+                if ($this->x_tick_label_pos == 'none')
+                    $this->x_data_label_pos = 'plotdown';
+                else
+                    $this->x_data_label_pos = 'none';
+            } else {
+                // Case: Neither tick_label_pos nor data_label_pos is set.
+                // We do not want them to be both on (as PHPlot used to do in this case).
+                // Turn on data labels if any were supplied, else tick labels.
+                if ($this->CheckLabelsAllEmpty()) {
+                    $this->x_data_label_pos = 'none';
+                    $this->x_tick_label_pos = 'plotdown';
+                } else {
+                    $this->x_data_label_pos = 'plotdown';
+                    $this->x_tick_label_pos = 'none';
+                }
+            }
+        }
+
+        // Y Label position fixups, for y_data_label_pos and y_tick_label_pos:
+        if (!$this->datatype_swapped_xy) {
+            // Just apply defaults - there is no position conflict.
+            if (!isset($this->y_tick_label_pos))
+                $this->y_tick_label_pos = 'plotleft';
+            if (!isset($this->y_data_label_pos))
+                $this->y_data_label_pos = 'none';
+        } else {
+            // Apply defaults but do not allow conflict between tick and data labels.
+            if (isset($this->y_data_label_pos)) {
+                if (!isset($this->y_tick_label_pos)) {
+                    // Case: data_label_pos is set, tick_label_pos needs a default:
+                    if ($this->y_data_label_pos == 'none')
+                        $this->y_tick_label_pos = 'plotleft';
+                    else
+                        $this->y_tick_label_pos = 'none';
+                }
+            } elseif (isset($this->y_tick_label_pos)) {
+                // Case: tick_label_pos is set, data_label_pos needs a default:
+                if ($this->y_tick_label_pos == 'none')
+                    $this->y_data_label_pos = 'plotleft';
+                else
+                    $this->y_data_label_pos = 'none';
+            } else {
+                // Case: Neither tick_label_pos nor data_label_pos is set.
+                // Turn on data labels if any were supplied, else tick labels.
+                if ($this->CheckLabelsAllEmpty()) {
+                    $this->y_data_label_pos = 'none';
+                    $this->y_tick_label_pos = 'plotleft';
+                } else {
+                    $this->y_data_label_pos = 'plotleft';
+                    $this->y_tick_label_pos = 'none';
+                }
+            }
+        }
+        return TRUE;
+    }
+
+    /*
+     * Formats a tick or data label.
+     *    which_pos - 'x', 'xd', 'y', or 'yd', selects formatting controls.
+     *        x, y are for tick labels; xd, yd are for data labels.
+     *    which_lab - String to format as a label.
+     * Credits: Time formatting suggested by Marlin Viss
+     *          Custom formatting suggested by zer0x333
+     * Notes:
+     *   Type 'title' is obsolete and retained for compatibility.
+     *   Class variable 'data_units_text' is retained as a suffix for 'data' type formatting for
+     *      backward compatibility. Since there was never a function/method to set it, there
+     *      could be somebody out there who sets it directly in the object.
+     */
+    protected function FormatLabel($which_pos, $which_lab)
+    {
+        // Assign a reference shortcut to the label format controls.
+        // Note CheckLabels() made sure the 'xd' and 'yd' arrays are set.
+        $format =& $this->label_format[$which_pos];
+
+        // Don't format empty strings (especially as time or numbers), or if no type was set.
+        if ($which_lab !== '' && !empty($format['type'])) {
+            switch ($format['type']) {
+            case 'title':  // Note: This is obsolete
+                $which_lab = @ $this->data[$which_lab][0];
                 break;
             case 'data':
-                $lab = number_format($which_lab, $this->x_precision, '.', ',').$this->data_units_text;
+                $which_lab = $format['prefix']
+                           . $this->number_format($which_lab, $format['precision'])
+                           . $this->data_units_text  // Obsolete
+                           . $format['suffix'];
                 break;
             case 'time':
-                $lab = strftime($this->x_time_format, $which_lab);
+                $which_lab = strftime($format['time_format'], $which_lab);
                 break;
-            default:
-                // Unchanged from whatever format it is passed in
-                $lab = $which_lab;
-            break;
-            }
-            break;
-        case 'y':
-        case 'ploty':
-            switch ($this->y_label_type) {
-            case 'data':
-                $lab = number_format($which_lab, $this->y_precision, '.', ',').$this->data_units_text;
+            case 'printf':
+                $which_lab = sprintf($format['printf_format'], $which_lab);
                 break;
-            case 'time':
-                $lab = strftime($this->y_time_format, $which_lab);
-                break;
-            default:
-                // Unchanged from whatever format it is passed in
-                $lab = $which_lab;
+            case 'custom':
+                $which_lab = call_user_func($format['custom_callback'], $which_lab, $format['custom_arg']);
                 break;
+
             }
-            break;
-        default:
-            $this->PrintError("FormatLabel(): Unknown label type $which_type");
-            return NULL;
         }
+        return $which_lab;
+    }
 
-        return $lab;
-    } //function FormatLabel
-
-
-
-/////////////////////////////////////////////    
+/////////////////////////////////////////////
 ///////////////                         TICKS
-/////////////////////////////////////////////    
+/////////////////////////////////////////////
 
-    /*!
-     * Use either this or SetNumXTicks() to set where to place x tick marks
+    /*
+     * Set the step (interval) between X ticks.
+     * Use either this or SetNumXTicks(), not both, to control the X tick marks.
      */
-    function SetXTickIncrement($which_ti=NULL)
+    function SetXTickIncrement($which_ti='')
     {
-        if ($which_ti) {
-            $this->x_tick_inc = $which_ti;  //world coordinates
-        } else {
-            if (! isset($this->data_limits_done)) {
-                $this->FindDataLimits();  //Get maxima and minima for scaling
-            }
-            $this->x_tick_inc =  ($this->plot_max_x  - $this->plot_min_x  )/10;
+        $this->x_tick_inc = $which_ti;
+        if (!empty($which_ti)) {
+            $this->num_x_ticks = '';
         }
-        $this->num_x_ticks = ''; //either use num_y_ticks or y_tick_inc, not both
         return TRUE;
     }
 
-    /*!
-     * Use either this or SetNumYTicks() to set where to place y tick marks
+    /*
+     * Set the step (interval) between Y ticks.
+     * Use either this or SetNumYTicks(), not both, to control the Y tick marks.
      */
-    function SetYTickIncrement($which_ti=NULL)
+    function SetYTickIncrement($which_ti='')
     {
-        if ($which_ti) {
-            $this->y_tick_inc = $which_ti;  //world coordinates
-        } else {
-            if (! isset($this->data_limits_done)) {
-                $this->FindDataLimits();  //Get maxima and minima for scaling
-            }
-            if (! isset($this->plot_max_y))
-                $this->SetPlotAreaWorld();
-
-            $this->y_tick_inc =  ($this->plot_max_y  - $this->plot_min_y  )/10;
+        $this->y_tick_inc = $which_ti;
+        if (!empty($which_ti)) {
+            $this->num_y_ticks = '';
         }
-        $this->num_y_ticks = ''; //either use num_y_ticks or y_tick_inc, not both
         return TRUE;
     }
 
-
-    function SetNumXTicks($which_nt) 
+    /*
+     * Set the number of X tick marks.
+     * Use either this or SetXTickIncrement(), not both, to control the X tick marks.
+     */
+    function SetNumXTicks($which_nt)
     {
         $this->num_x_ticks = $which_nt;
-        $this->x_tick_inc = '';  //either use num_x_ticks or x_tick_inc, not both
+        if (!empty($which_nt)) {
+            $this->x_tick_inc = '';
+        }
         return TRUE;
     }
 
-    function SetNumYTicks($which_nt) 
+    /*
+     * Set the number of Y tick marks.
+     * Use either this or SetYTickIncrement(), not both, to control the Y tick marks.
+     */
+    function SetNumYTicks($which_nt)
     {
         $this->num_y_ticks = $which_nt;
-        $this->y_tick_inc = '';  //either use num_y_ticks or y_tick_inc, not both
+        if (!empty($which_nt)) {
+            $this->y_tick_inc = '';  //either use num_y_ticks or y_tick_inc, not both
+        }
         return TRUE;
     }
 
-    /*!
-     *
+    /*
+     * Set the position for the X tick marks.
+     * These can be above the plot, below, both positions, at the X axis, or suppressed.
      */
-    function SetYTickPos($which_tp) 
-    { 
-        $this->y_tick_pos = $this->CheckOption($which_tp, 'plotleft, plotright, both, yaxis, none', __FUNCTION__);
-        return TRUE;
+    function SetXTickPos($which_tp)
+    {
+        $this->x_tick_pos = $this->CheckOption($which_tp, 'plotdown, plotup, both, xaxis, none',
+                                               __FUNCTION__);
+        return (boolean)$this->x_tick_pos;
     }
-    /*!
-     *
+
+    /*
+     * Set the position for the Y tick marks.
+     * These can be left of the plot, right, both positions, at the Y axis, or suppressed.
      */
-    function SetXTickPos($which_tp)
-    { 
-        $this->x_tick_pos = $this->CheckOption($which_tp, 'plotdown, plotup, both, xaxis, none', __FUNCTION__);
-        return TRUE;
+    function SetYTickPos($which_tp)
+    {
+        $this->y_tick_pos = $this->CheckOption($which_tp, 'plotleft, plotright, both, yaxis, none',
+                                              __FUNCTION__);
+        return (boolean)$this->y_tick_pos;
     }
 
-    /*!
-     * \param skip bool
-     */ 
+    /*
+     * Skip the top-most Y axis tick mark and label if $skip is true.
+     */
     function SetSkipTopTick($skip)
     {
         $this->skip_top_tick = (bool)$skip;
         return TRUE;
     }
 
-    /*!
-     * \param skip bool
+    /*
+     * Skip the bottom-most Y axis tick mark and label if $skip is true.
      */
-    function SetSkipBottomTick($skip) 
+    function SetSkipBottomTick($skip)
     {
         $this->skip_bottom_tick = (bool)$skip;
         return TRUE;
     }
 
-    /*!
-     * \param skip bool
-     */ 
+    /*
+     * Skip the left-most X axis tick mark and label if $skip is true.
+     */
     function SetSkipLeftTick($skip)
     {
         $this->skip_left_tick = (bool)$skip;
         return TRUE;
     }
 
-    /*!
-     * \param skip bool
+    /*
+     * Skip the right-most X axis tick mark and label if $skip is true.
      */
     function SetSkipRightTick($skip)
     {
@@ -2434,39 +3902,54 @@ class PHPlot {
         return TRUE;
     }
 
+    /*
+     * Set the outer length of X tick marks to $which_xln pixels.
+     * This is the part of the tick mark that sticks out from the plot area.
+     */
     function SetXTickLength($which_xln)
     {
         $this->x_tick_length = $which_xln;
         return TRUE;
     }
 
+    /*
+     * Set the outer length of Y tick marks to $which_yln pixels.
+     * This is the part of the tick mark that sticks out from the plot area.
+     */
     function SetYTickLength($which_yln)
     {
         $this->y_tick_length = $which_yln;
         return TRUE;
     }
 
+    /*
+     * Set the crossing length of X tick marks to $which_xc pixels.
+     * This is the part of the tick mark that sticks into the plot area.
+     */
     function SetXTickCrossing($which_xc)
     {
         $this->x_tick_cross = $which_xc;
         return TRUE;
     }
 
+    /*
+     * Set the crossing length of Y tick marks to $which_yc pixels.
+     * This is the part of the tick mark that sticks into the plot area.
+     */
     function SetYTickCrossing($which_yc)
     {
         $this->y_tick_cross = $which_yc;
         return TRUE;
     }
 
-
 /////////////////////////////////////////////
 ////////////////////          GENERIC DRAWING
 /////////////////////////////////////////////
 
-    /*!
-     * Fills the background.
+    /*
+     * Fill the image background, with a tiled image file or solid color.
      */
-    function DrawBackground()
+    protected function DrawBackground()
     {
         // Don't draw this twice if drawing two plots on one image
         if (! $this->background_done) {
@@ -2477,99 +3960,64 @@ class PHPlot {
                                      $this->ndx_bg_color);
             }
             $this->background_done = TRUE;
-            return TRUE;        // Done
         }
-        return FALSE;           // Nothing done
+        return TRUE;
     }
 
-
-    /*!
-     * Fills the plot area background.
+    /*
+     * Fill the plot area background, with a tiled image file or solid color.
      */
-    function DrawPlotAreaBackground()
+    protected function DrawPlotAreaBackground()
     {
         if (isset($this->plotbgimg)) {
             $this->tile_img($this->plotbgimg, $this->plot_area[0], $this->plot_area[1],
                             $this->plot_area_width, $this->plot_area_height, $this->plotbgmode);
+        } elseif ($this->draw_plot_area_background) {
+            ImageFilledRectangle($this->img, $this->plot_area[0], $this->plot_area[1],
+                                 $this->plot_area[2], $this->plot_area[3], $this->ndx_plot_bg_color);
         }
-        else {
-            if ($this->draw_plot_area_background) {
-                ImageFilledRectangle($this->img, $this->plot_area[0], $this->plot_area[1],
-                                     $this->plot_area[2], $this->plot_area[3], $this->ndx_plot_bg_color);
-            }
-        }
-
         return TRUE;
     }
 
-
-    /*!
+    /*
      * Tiles an image at some given coordinates.
-     *
-     * \param $file   string Filename of the picture to be used as tile.
-     * \param $xorig  int    X coordinate of the plot where the tile is to begin.
-     * \param $yorig  int    Y coordinate of the plot where the tile is to begin.
-     * \param $width  int    Width of the area to be tiled.
-     * \param $height int    Height of the area to be tiled.
-     * \param $mode   string One of 'centeredtile', 'tile', 'scale'.
-     */
-    function tile_img($file, $xorig, $yorig, $width, $height, $mode)
-    {
-        $size = getimagesize($file);
-        $input_format = $size[2];
-
-        switch($input_format) {
-        case 1:
-            $im = @ imagecreatefromGIF ($file);
-            if (! $im) {
-                $this->PrintError("tile_img:() Unable to open $file as a GIF.");
-                return FALSE;
-            }
-            break;
-        case 2:
-            $im = @ imagecreatefromJPEG ($file);
-            if (! $im) {
-                $this->PrintError("tile_img(): Unable to open $file as a JPG.");
-                return FALSE;
-            }
-            break;
-        case 3:
-            $im = @ imagecreatefromPNG ($file);
-            if (! $im) {
-                $this->PrintError("tile_img(): Unable to open $file as a PNG.");
-                return FALSE;
-            }
-            break;
-        default:
-            $this->PrintError('tile_img(): Please select a gif, jpg, or png image.');
-            return FALSE;
-            break;
-        }
-
+     *   $file : Filename of the picture to be used as tile.
+     *   $xorig : X device coordinate of where the tile is to begin.
+     *   $yorig : Y device coordinate of where the tile is to begin.
+     *   $width : Width of the area to be tiled.
+     *   $height : Height of the area to be tiled.
+     *   $mode : Tiling mode. One of 'centeredtile', 'tile', 'scale'.
+     */
+    protected function tile_img($file, $xorig, $yorig, $width, $height, $mode)
+    {
+        $im = $this->GetImage($file, $tile_width, $tile_height);
+        if (!$im)
+            return FALSE;  // GetImage already produced an error message.
 
         if ($mode == 'scale') {
-            imagecopyresized($this->img, $im, $xorig, $yorig, 0, 0, $width, $height, $size[0],$size[1]);
+            imagecopyresampled($this->img, $im, $xorig, $yorig, 0, 0, $width, $height,
+                               $tile_width, $tile_height);
             return TRUE;
-        } else if ($mode == 'centeredtile') {
-            $x0 = - floor($size[0]/2);   // Make the tile look better
-            $y0 = - floor($size[1]/2);
-        } else if ($mode = 'tile') {
+        }
+
+        if ($mode == 'centeredtile') {
+            $x0 = - floor($tile_width/2);   // Make the tile look better
+            $y0 = - floor($tile_height/2);
+        } else {      // Accept anything else as $mode == 'tile'
             $x0 = 0;
             $y0 = 0;
         }
 
-        // Actually draw the tile
-
-        // But first on a temporal image.
-        $tmp = ImageCreate($width, $height);
+        // Draw the tile onto a temporary image first.
+        $tmp = imagecreate($width, $height);
         if (! $tmp)
-            $this->PrintError('tile_img(): Could not create image resource.');
+            return $this->PrintError('tile_img(): Could not create image resource.');
 
-        for ($x = $x0; $x < $width; $x += $size[0])
-            for ($y = $y0; $y < $height; $y += $size[1])
-                imagecopy($tmp, $im, $x, $y, 0, 0, $size[0], $size[1]);
+        for ($x = $x0; $x < $width; $x += $tile_width)
+            for ($y = $y0; $y < $height; $y += $tile_height)
+                imagecopy($tmp, $im, $x, $y, 0, 0, $tile_width, $tile_height);
 
-        // Copy the temporal image onto the final one.
+        // Copy the temporary image onto the final one.
         imagecopy($this->img, $tmp, $xorig, $yorig, 0,0, $width, $height);
 
         // Free resources
@@ -2577,125 +4025,159 @@ class PHPlot {
         imagedestroy($im);
 
         return TRUE;
-    }  // function tile_img
-
+    }
 
-    /*!
-     * Draws a border around the final image.
+    /*
+     * Return the image border width.
+     * This is used by CalcMargins() and DrawImageBorder().
      */
-    function DrawImageBorder()
+    protected function GetImageBorderWidth()
     {
+        if ($this->image_border_type == 'none')
+            return 0; // No border
+        if (!empty($this->image_border_width))
+            return $this->image_border_width; // Specified border width
+        if ($this->image_border_type == 'raised')
+            return 2; // Default for raised border is 2 pixels.
+        return 1; // Default for other border types is 1 pixel.
+    }
+
+    /*
+     * Draws a border around the final image.
+     * Note: 'plain' draws a flat border using the dark shade of the border color.
+     * This probably should have been written to use the actual border color, but
+     * it is too late to fix it without changing plot appearances. Therefore a
+     * new type 'solid' was added to use the SetImageBorderColor color.
+     */
+    protected function DrawImageBorder()
+    {
+        if ($this->image_border_type == 'none')
+            return TRUE; // Early test for default case.
+        $width = $this->GetImageBorderWidth();
+        $color1 = $this->ndx_i_border;
+        $color2 = $this->ndx_i_border_dark;
+        $ex = $this->image_width - 1;
+        $ey = $this->image_height - 1;
         switch ($this->image_border_type) {
         case 'raised':
-            ImageLine($this->img, 0, 0, $this->image_width-1, 0, $this->ndx_i_border);
-            ImageLine($this->img, 1, 1, $this->image_width-2, 1, $this->ndx_i_border);
-            ImageLine($this->img, 0, 0, 0, $this->image_height-1, $this->ndx_i_border);
-            ImageLine($this->img, 1, 1, 1, $this->image_height-2, $this->ndx_i_border);
-            ImageLine($this->img, $this->image_width-1, 0, $this->image_width-1,
-                      $this->image_height-1, $this->ndx_i_border_dark);
-            ImageLine($this->img, 0, $this->image_height-1, $this->image_width-1,
-                      $this->image_height-1, $this->ndx_i_border_dark);
-            ImageLine($this->img, $this->image_width-2, 1, $this->image_width-2,
-                      $this->image_height-2, $this->ndx_i_border_dark);
-            ImageLine($this->img, 1, $this->image_height-2, $this->image_width-2,
-                      $this->image_height-2, $this->ndx_i_border_dark);
-            break;
-        case 'plain':
-            ImageLine($this->img, 0, 0, $this->image_width, 0, $this->ndx_i_border_dark);
-            ImageLine($this->img, $this->image_width-1, 0, $this->image_width-1,
-                      $this->image_height, $this->ndx_i_border_dark);
-            ImageLine($this->img, $this->image_width-1, $this->image_height-1, 0, $this->image_height-1,
-                      $this->ndx_i_border_dark);
-            ImageLine($this->img, 0, 0, 0, $this->image_height, $this->ndx_i_border_dark);
+            // Top and left lines use border color, right and bottom use the darker shade.
+            // Drawing order matters in the upper right and lower left corners.
+            for ($i = 0; $i < $width; $i++, $ex--, $ey--) {
+                imageline($this->img, $i,  $i,  $ex, $i,  $color1); // Top
+                imageline($this->img, $ex, $i,  $ex, $ey, $color2); // Right
+                imageline($this->img, $i,  $i,  $i,  $ey, $color1); // Left
+                imageline($this->img, $i,  $ey, $ex, $ey, $color2); // Bottom
+            }
             break;
-        case 'none':
+        case 'plain': // See note above re colors
+            $color1 = $color2;
+            // Fall through
+        case 'solid':
+            for ($i = 0; $i < $width; $i++, $ex--, $ey--) {
+                imagerectangle($this->img, $i, $i, $ex, $ey, $color1);
+            }
             break;
         default:
-            $this->DrawError("DrawImageBorder(): unknown image_border_type: '$this->image_border_type'");
-            return FALSE;
+            return $this->PrintError(
+                          "DrawImageBorder(): unknown image_border_type: '$this->image_border_type'");
         }
         return TRUE;
     }
 
-
-    /*!
-     * Adds the title to the graph.
+    /*
+     * Draws the main title on the graph.
+     * The title must not be drawn more than once (in the case of multiple plots
+     * on the image), because TTF text antialiasing makes it look bad.
      */
-    function DrawTitle() 
+    protected function DrawTitle()
     {
-        // Center of the plot area
-        //$xpos = ($this->plot_area[0] + $this->plot_area_width )/ 2;
+        if (isset($this->title_done) || $this->title_txt === '')
+            return TRUE;
 
         // Center of the image:
         $xpos = $this->image_width / 2;
 
         // Place it at almost at the top
-        $ypos = $this->safe_margin;
-
-        $this->DrawText($this->title_font, $this->title_angle, $xpos, $ypos,
-                        $this->ndx_title_color, $this->title_txt, 'center', 'bottom'); 
+        $ypos = $this->title_offset;
 
-        return TRUE; 
+        $this->DrawText($this->fonts['title'], 0, $xpos, $ypos,
+                        $this->ndx_title_color, $this->title_txt, 'center', 'top');
 
+        $this->title_done = TRUE;
+        return TRUE;
     }
 
-
-    /*!
+    /*
      * Draws the X-Axis Title
      */
-    function DrawXTitle()
+    protected function DrawXTitle()
     {
         if ($this->x_title_pos == 'none')
-            return;
+            return TRUE;
 
         // Center of the plot
         $xpos = ($this->plot_area[2] + $this->plot_area[0]) / 2;
 
         // Upper title
         if ($this->x_title_pos == 'plotup' || $this->x_title_pos == 'both') {
-            $ypos = $this->safe_margin + $this->title_height + $this->safe_margin;
-            $this->DrawText($this->x_title_font, $this->x_title_angle,
-                            $xpos, $ypos, $this->ndx_title_color, $this->x_title_txt, 'center');
+            $ypos = $this->plot_area[1] - $this->x_title_top_offset;
+            $this->DrawText($this->fonts['x_title'], 0, $xpos, $ypos, $this->ndx_x_title_color,
+                            $this->x_title_txt, 'center', 'bottom');
         }
         // Lower title
         if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both') {
-            $ypos = $this->image_height - $this->x_title_height - $this->safe_margin;
-            $this->DrawText($this->x_title_font, $this->x_title_angle,
-                            $xpos, $ypos, $this->ndx_title_color, $this->x_title_txt, 'center');
+            $ypos = $this->plot_area[3] + $this->x_title_bot_offset;
+            $this->DrawText($this->fonts['x_title'], 0, $xpos, $ypos, $this->ndx_x_title_color,
+                            $this->x_title_txt, 'center', 'top');
         }
         return TRUE;
     }
 
-    /*!
+    /*
      * Draws the Y-Axis Title
      */
-    function DrawYTitle()
+    protected function DrawYTitle()
     {
         if ($this->y_title_pos == 'none')
-            return;
+            return TRUE;
 
-        // Center the title vertically to the plot
+        // Center the title vertically to the plot area
         $ypos = ($this->plot_area[3] + $this->plot_area[1]) / 2;
 
         if ($this->y_title_pos == 'plotleft' || $this->y_title_pos == 'both') {
-            $xpos = $this->safe_margin;
-            $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color,
-                            $this->y_title_txt, 'left', 'center');
+            $xpos = $this->plot_area[0] - $this->y_title_left_offset;
+            $this->DrawText($this->fonts['y_title'], 90, $xpos, $ypos, $this->ndx_y_title_color,
+                            $this->y_title_txt, 'right', 'center');
         }
         if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both') {
-            $xpos = $this->image_width - $this->safe_margin - $this->y_title_width - $this->safe_margin;
-            $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color,
+            $xpos = $this->plot_area[2] + $this->y_title_right_offset;
+            $this->DrawText($this->fonts['y_title'], 90, $xpos, $ypos, $this->ndx_y_title_color,
                             $this->y_title_txt, 'left', 'center');
         }
 
         return TRUE;
     }
 
+    /*
+     * Draw the X axis, including ticks and labels, and X (vertical) grid lines.
+     */
+    protected function DrawXAxis()
+    {
+        // Draw ticks, labels and grid
+        $this->DrawXTicks();
+
+        //Draw X Axis at Y = x_axis_y_pixels
+        ImageLine($this->img, $this->plot_area[0]+1, $this->x_axis_y_pixels,
+                  $this->plot_area[2]-1, $this->x_axis_y_pixels, $this->ndx_grid_color);
+
+        return TRUE;
+    }
 
     /*
-     * \note Horizontal grid lines overwrite horizontal axis with y=0, so call this first, then DrawXAxis()
+     * Draw the Y axis, including ticks and labels, and Y (horizontal) grid lines.
+     * Horizontal grid lines overwrite horizontal axis with y=0, so call this first, then DrawXAxis()
      */
-    function DrawYAxis()
+    protected function DrawYAxis()
     {
         // Draw ticks, labels and grid, if any
         $this->DrawYTicks();
@@ -2708,85 +4190,108 @@ class PHPlot {
     }
 
     /*
-     *
+     * Draw one X tick mark and its tick label.
+     *   $which_xlab : Formatted X value for the label.
+     *   $which_xpix : X device coordinate for this tick mark.
      */
-    function DrawXAxis()
+    protected function DrawXTick($which_xlab, $which_xpix)
     {
-        // Draw ticks, labels and grid
-        $this->DrawXTicks();
+        // Ticks on X axis
+        if ($this->x_tick_pos == 'xaxis') {
+            ImageLine($this->img, $which_xpix, $this->x_axis_y_pixels - $this->x_tick_cross,
+                      $which_xpix, $this->x_axis_y_pixels + $this->x_tick_length, $this->ndx_tick_color);
+        }
 
-        /* This tick and label tend to overlap with regular Y Axis labels,
-         * as Mike Pullen pointed out.
-         *
-        //Draw Tick and Label for X axis
-        if (! $this->skip_bottom_tick) {
-            $ylab =$this->FormatLabel('y', $this->x_axis_position);
-            $this->DrawYTick($ylab, $this->x_axis_y_pixels);
+        // Ticks on top of the Plot Area
+        if ($this->x_tick_pos == 'plotup' || $this->x_tick_pos == 'both') {
+            ImageLine($this->img, $which_xpix, $this->plot_area[1] - $this->x_tick_length,
+                      $which_xpix, $this->plot_area[1] + $this->x_tick_cross, $this->ndx_tick_color);
         }
-        */
-        //Draw X Axis at Y = x_axis_y_pixels
-        ImageLine($this->img, $this->plot_area[0]+1, $this->x_axis_y_pixels,
-                  $this->plot_area[2]-1, $this->x_axis_y_pixels, $this->ndx_grid_color);
 
+        // Ticks on bottom of Plot Area
+        if ($this->x_tick_pos == 'plotdown' || $this->x_tick_pos == 'both') {
+            ImageLine($this->img, $which_xpix, $this->plot_area[3] + $this->x_tick_length,
+                      $which_xpix, $this->plot_area[3] - $this->x_tick_cross, $this->ndx_tick_color);
+        }
+
+        // Label on X axis
+        if ($this->x_tick_label_pos == 'xaxis') {
+            $this->DrawText($this->fonts['x_label'], $this->x_label_angle,
+                            $which_xpix, $this->x_axis_y_pixels + $this->x_label_axis_offset,
+                            $this->ndx_text_color, $which_xlab, 'center', 'top');
+        }
+
+        // Label on top of the Plot Area
+        if ($this->x_tick_label_pos == 'plotup' || $this->x_tick_label_pos == 'both') {
+            $this->DrawText($this->fonts['x_label'], $this->x_label_angle,
+                            $which_xpix, $this->plot_area[1] - $this->x_label_top_offset,
+                            $this->ndx_text_color, $which_xlab, 'center', 'bottom');
+        }
+
+        // Label on bottom of the Plot Area
+        if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both') {
+            $this->DrawText($this->fonts['x_label'], $this->x_label_angle,
+                            $which_xpix, $this->plot_area[3] + $this->x_label_bot_offset,
+                            $this->ndx_text_color, $which_xlab, 'center', 'top');
+        }
         return TRUE;
     }
 
-    /*!
-     * Draw Just one Tick, called from DrawYTicks() and DrawXAxis()
-     * TODO? Move this inside DrawYTicks() and Modify DrawXAxis() ?
+    /*
+     * Draw one Y tick mark and its tick label. Called from DrawYTicks() and DrawXAxis()
+     *   $which_ylab : Formatted Y value for the label.
+     *   $which_ypix : Y device coordinate for this tick mark.
      */
-    function DrawYTick($which_ylab, $which_ypix)
+    protected function DrawYTick($which_ylab, $which_ypix)
     {
         // Ticks on Y axis
         if ($this->y_tick_pos == 'yaxis') {
             ImageLine($this->img, $this->y_axis_x_pixels - $this->y_tick_length, $which_ypix,
-                      $this->y_axis_x_pixels + $this->y_tick_cross, $which_ypix,
-                      $this->ndx_tick_color);
-        }
-
-        // Labels on Y axis
-        if ($this->y_tick_label_pos == 'yaxis') {
-            $this->DrawText($this->y_label_font, $this->y_label_angle,
-                            $this->y_axis_x_pixels - $this->y_tick_length * 1.5, $which_ypix,
-                            $this->ndx_text_color, $which_ylab, 'right', 'center');
+                      $this->y_axis_x_pixels + $this->y_tick_cross, $which_ypix, $this->ndx_tick_color);
         }
 
         // Ticks to the left of the Plot Area
         if (($this->y_tick_pos == 'plotleft') || ($this->y_tick_pos == 'both') ) {
-            ImageLine($this->img, $this->plot_area[0] - $this->y_tick_length,
-                      $which_ypix, $this->plot_area[0] + $this->y_tick_cross,
-                      $which_ypix, $this->ndx_tick_color);
+            ImageLine($this->img, $this->plot_area[0] - $this->y_tick_length, $which_ypix,
+                      $this->plot_area[0] + $this->y_tick_cross, $which_ypix, $this->ndx_tick_color);
         }
 
         // Ticks to the right of the Plot Area
         if (($this->y_tick_pos == 'plotright') || ($this->y_tick_pos == 'both') ) {
-            ImageLine($this->img, ($this->plot_area[2] + $this->y_tick_length),
-                      $which_ypix, $this->plot_area[2] - $this->y_tick_cross,
-                      $which_ypix, $this->ndx_tick_color);
+            ImageLine($this->img, $this->plot_area[2] + $this->y_tick_length, $which_ypix,
+                      $this->plot_area[2] - $this->y_tick_cross, $which_ypix, $this->ndx_tick_color);
+        }
+
+        // Labels on Y axis
+        if ($this->y_tick_label_pos == 'yaxis') {
+            $this->DrawText($this->fonts['y_label'], $this->y_label_angle,
+                            $this->y_axis_x_pixels - $this->y_label_axis_offset, $which_ypix,
+                            $this->ndx_text_color, $which_ylab, 'right', 'center');
         }
 
         // Labels to the left of the plot area
         if ($this->y_tick_label_pos == 'plotleft' || $this->y_tick_label_pos == 'both') {
-            $this->DrawText($this->y_label_font, $this->y_label_angle,
-                            $this->plot_area[0] - $this->y_tick_length * 1.5, $which_ypix,
+            $this->DrawText($this->fonts['y_label'], $this->y_label_angle,
+                            $this->plot_area[0] - $this->y_label_left_offset, $which_ypix,
                             $this->ndx_text_color, $which_ylab, 'right', 'center');
         }
         // Labels to the right of the plot area
         if ($this->y_tick_label_pos == 'plotright' || $this->y_tick_label_pos == 'both') {
-            $this->DrawText($this->y_label_font, $this->y_label_angle,
-                            $this->plot_area[2] + $this->y_tick_length * 1.5, $which_ypix,
+            $this->DrawText($this->fonts['y_label'], $this->y_label_angle,
+                            $this->plot_area[2] + $this->y_label_right_offset, $which_ypix,
                             $this->ndx_text_color, $which_ylab, 'left', 'center');
         }
-   } // Function DrawYTick()
-
+        return TRUE;
+    }
 
-    /*!
-     * Draws Grid, Ticks and Tick Labels along Y-Axis
-     * Ticks and ticklabels can be left of plot only, right of plot only,
-     * both on the left and right of plot, or crossing a user defined Y-axis
-     * TODO: marks at whole numbers (-10, 10, 20, 30 ...) no matter where the plot begins (-3, 4.7, etc.)
+    /*
+     * Draws Grid, Ticks and Tick Labels along X-Axis
+     * Ticks and tick labels can be down of plot only, up of plot only,
+     * both on up and down of plot, or crossing a user defined X-axis
+     *
+     * Original vertical code submitted by Marlin Viss
      */
-    function DrawYTicks()
+    protected function DrawXTicks()
     {
         // Sets the line style for IMG_COLOR_STYLED lines (grid)
         if ($this->dashed_grid) {
@@ -2796,51 +4301,36 @@ class PHPlot {
             $style = $this->ndx_light_grid_color;
         }
 
-        // maxy is always > miny so delta_y is always positive
-        if ($this->y_tick_inc) {
-            $delta_y = $this->y_tick_inc;
-        } elseif ($this->num_y_ticks) {
-            $delta_y = ($this->plot_max_y - $this->plot_min_y) / $this->num_y_ticks;
-        } else {
-            $delta_y = ($this->plot_max_y - $this->plot_min_y) / 10 ;
-        }
-
-        // NOTE: When working with floats, because of approximations when adding $delta_y,
-        // $y_tmp never equals $y_end  at the for loop, so one spurious line would  get drawn where
-        // not for the substraction to $y_end here.
-        $y_tmp = (double)$this->plot_min_y;
-        $y_end = (double)$this->plot_max_y - ($delta_y/2);
-
-        if ($this->skip_bottom_tick)
-            $y_tmp += $delta_y;
+        // Calculate the tick start, end, and step:
+        list($x_start, $x_end, $delta_x) = $this->CalcTicks('x');
 
-        if ($this->skip_top_tick)
-            $y_end -= $delta_y;
-
-        for (;$y_tmp < $y_end; $y_tmp += $delta_y) {
-            $ylab = $this->FormatLabel('y', $y_tmp);
-            $y_pixels = $this->ytr($y_tmp);
+        // Loop, avoiding cumulative round-off errors from $x_tmp += $delta_x
+        $n = 0;
+        $x_tmp = $x_start;
+        while ($x_tmp <= $x_end) {
+            $xlab = $this->FormatLabel('x', $x_tmp);
+            $x_pixels = $this->xtr($x_tmp);
 
-            // Horizontal grid line
-            if ($this->draw_y_grid) {
-                ImageLine($this->img, $this->plot_area[0]+1, $y_pixels, $this->plot_area[2]-1, $y_pixels, $style);
+            // Vertical grid lines
+            if ($this->draw_x_grid) {
+                ImageLine($this->img, $x_pixels, $this->plot_area[1], $x_pixels, $this->plot_area[3], $style);
             }
 
-            // Draw ticks
-            $this->DrawYTick($ylab, $y_pixels);
+            // Draw tick mark(s)
+            $this->DrawXTick($xlab, $x_pixels);
+
+            // Step to next X, without accumulating error
+            $x_tmp = $x_start + ++$n * $delta_x;
         }
         return TRUE;
-    } // function DrawYTicks
-
+    }
 
-    /*!
-     * Draws Grid, Ticks and Tick Labels along X-Axis
-     * Ticks and tick labels can be down of plot only, up of plot only,
-     * both on up and down of plot, or crossing a user defined X-axis 
-     *
-     * \note Original vertical code submitted by Marlin Viss
+    /*
+     * Draw the grid, ticks, and tick labels along the Y axis.
+     * Ticks and tick labels can be left of plot only, right of plot only,
+     * both on the left and right of plot, or crossing a user defined Y-axis
      */
-    function DrawXTicks() 
+    protected function DrawYTicks()
     {
         // Sets the line style for IMG_COLOR_STYLED lines (grid)
         if ($this->dashed_grid) {
@@ -2850,118 +4340,106 @@ class PHPlot {
             $style = $this->ndx_light_grid_color;
         }
 
-        // Calculate x increment between ticks
-        if ($this->x_tick_inc) {
-            $delta_x = $this->x_tick_inc;
-        } elseif ($this->num_x_ticks) {
-            $delta_x = ($this->plot_max_x - $this->plot_min_x) / $this->num_x_ticks;
-        } else {
-            $delta_x =($this->plot_max_x - $this->plot_min_x) / 10 ;
-        }
-
-        // NOTE: When working with decimals, because of approximations when adding $delta_x,
-        // $x_tmp never equals $x_end  at the for loop, so one spurious line would  get drawn where
-        // not for the substraction to $x_end here.
-        $x_tmp = (double)$this->plot_min_x;
-        $x_end = (double)$this->plot_max_x - ($delta_x/2);
-
-        // Should the leftmost tick be drawn?
-        if ($this->skip_left_tick)
-            $x_tmp += $delta_x;
-
-        // And the rightmost?
-        if (! $this->skip_right_tick)
-            $x_end += $delta_x;
-
-        for (;$x_tmp < $x_end; $x_tmp += $delta_x) {
-            $xlab = $this->FormatLabel('x', $x_tmp);
-            $x_pixels = $this->xtr($x_tmp);
-
-            // Vertical grid lines
-            if ($this->draw_x_grid) {
-                ImageLine($this->img, $x_pixels, $this->plot_area[1], $x_pixels, $this->plot_area[3], $style);
-            }
-
-            // Tick on X Axis
-            if ($this->x_tick_pos == 'xaxis') {
+        // Calculate the tick start, end, and step:
+        list($y_start, $y_end, $delta_y) = $this->CalcTicks('y');
 
-                ImageLine($this->img, $x_pixels, $this->x_axis_y_pixels - $this->x_tick_cross,
-                          $x_pixels, $this->x_axis_y_pixels + $this->x_tick_length, $this->ndx_tick_color);
-            }
+        // Loop, avoiding cumulative round-off errors from $y_tmp += $delta_y
+        $n = 0;
+        $y_tmp = $y_start;
+        while ($y_tmp <= $y_end) {
+            $ylab = $this->FormatLabel('y', $y_tmp);
+            $y_pixels = $this->ytr($y_tmp);
 
-            // Label on X axis
-            if ($this->x_tick_label_pos == 'xaxis') {
-                 $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels,
-                                $this->x_axis_y_pixels + $this->x_tick_length*1.5, $this->ndx_text_color, 
-                                $xlab, 'center', 'bottom');
-            }              
-
-            // Top of the plot area tick
-            if ($this->x_tick_pos == 'plotup' || $this->x_tick_pos == 'both') {
-                ImageLine($this->img, $x_pixels, $this->plot_area[1] - $this->x_tick_length,
-                          $x_pixels, $this->plot_area[1] + $this->x_tick_cross, $this->ndx_tick_color);
-            }
-            // Bottom of the plot area tick
-            if ($this->x_tick_pos == 'plotdown' || $this->x_tick_pos == 'both') {
-                ImageLine($this->img, $x_pixels, $this->plot_area[3] + $this->x_tick_length,
-                          $x_pixels, $this->plot_area[3] - $this->x_tick_cross, $this->ndx_tick_color);
+            // Horizontal grid line
+            if ($this->draw_y_grid) {
+                ImageLine($this->img, $this->plot_area[0]+1, $y_pixels, $this->plot_area[2]-1,
+                          $y_pixels, $style);
             }
 
-            // Top of the plot area tick label
-            if ($this->x_tick_label_pos == 'plotup' || $this->x_tick_label_pos == 'both') {
-                $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels, 
-                                $this->plot_area[1] - $this->x_tick_length*1.5, $this->ndx_text_color, 
-                                $xlab, 'center', 'top');
-            }
+            // Draw tick mark(s)
+            $this->DrawYTick($ylab, $y_pixels);
 
-            // Bottom of the plot area tick label
-            if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both') {
-                $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels,
-                                $this->plot_area[3] + $this->x_tick_length*1.5, $this->ndx_text_color,
-                                $xlab, 'center', 'bottom');
-            }
+            // Step to next Y, without accumulating error
+            $y_tmp = $y_start + ++$n * $delta_y;
         }
-        return;
-    } // function DrawXTicks
+        return TRUE;
+    }
 
+    /*
+     *  Draw a border around the plot area. See SetPlotBorderType.
+     *  Note: SetPlotBorderType sets plot_border_type to an array, but
+     *  it won't be an array if it defaults or is set directly (backward compatibility).
+     */
+    protected function DrawPlotBorder()
+    {
+        $pbt = (array)$this->plot_border_type;
+        $sides = 0;  // Bitmap: 1=left 2=top 4=right 8=bottom
+        $map = array('left' => 1, 'plotleft' => 1, 'right' => 4, 'plotright' => 4, 'top' => 2,
+                      'bottom' => 8, 'both' => 5, 'sides' => 5, 'full' => 15, 'none' => 0);
+        foreach ($pbt as $option) $sides |= $map[$option];
+        if ($sides == 15) { // Border on all 4 sides
+            imagerectangle($this->img, $this->plot_area[0], $this->plot_area[1],
+                           $this->plot_area[2], $this->plot_area[3], $this->ndx_grid_color);
+        } else {
+            if ($sides & 1) // Left
+                imageline($this->img, $this->plot_area[0], $this->plot_area[1],
+                                      $this->plot_area[0], $this->plot_area[3], $this->ndx_grid_color);
+            if ($sides & 2) // Top
+                imageline($this->img, $this->plot_area[0], $this->plot_area[1],
+                                      $this->plot_area[2], $this->plot_area[1], $this->ndx_grid_color);
+            if ($sides & 4) // Right
+                imageline($this->img, $this->plot_area[2], $this->plot_area[1],
+                                      $this->plot_area[2], $this->plot_area[3], $this->ndx_grid_color);
+            if ($sides & 8) // Bottom
+                imageline($this->img, $this->plot_area[0], $this->plot_area[3],
+                                      $this->plot_area[2], $this->plot_area[3], $this->ndx_grid_color);
+        }
+        return TRUE;
+    }
 
-    /*!
-     * 
+    /*
+     * Draw the data value label associated with a point in the plot.
+     * This is used for bar and stacked bar charts. These are the labels above,
+     * to the right, or within the bars, not the axis labels.
+     *
+     *    $x_or_y : Specify 'x' or 'y' labels. This selects font, angle, and formatting.
+     *    $x_world, $y_world : World coordinates of the text (see also x/y_adjustment).
+     *    $text : The text to draw, after formatting with FormatLabel().
+     *    $halign, $valign : Selects from 9-point text alignment.
+     *    $x_adjustment, $y_adjustment : Text position offsets, in device coordinates.
+     *    $min_width, $min_height : If supplied, suppress the text if it will not fit.
+     * Returns True, if the text was drawn, or False, if it will not fit.
+     *
      */
-    function DrawPlotBorder()
-    {
-        switch ($this->plot_border_type) {
-        case 'left':    // for past compatibility
-        case 'plotleft':
-            ImageLine($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y),
-                      $this->plot_area[0], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
-            break;
-        case 'right':
-        case 'plotright':
-            ImageLine($this->img, $this->plot_area[2], $this->ytr($this->plot_min_y),
-                      $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
-            break;
-        case 'both':
-        case 'sides':
-             ImageLine($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y),
-                      $this->plot_area[0], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
-            ImageLine($this->img, $this->plot_area[2], $this->ytr($this->plot_min_y),
-                      $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
-            break;
-        case 'none':
-            //Draw No Border
-            break;
-        case 'full':
-        default:
-            ImageRectangle($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y),
-                           $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
-            break;
+    protected function DrawDataValueLabel($x_or_y, $x_world, $y_world, $text, $halign, $valign,
+                      $x_adjustment=0, $y_adjustment=0, $min_width=NULL, $min_height=NULL)
+    {
+        if ($x_or_y == 'x') {
+            $angle = $this->x_data_label_angle;
+            $font = $this->fonts['x_label'];
+            $formatted_text = $this->FormatLabel('xd', $text);
+        } else { // Assumed 'y'
+            $angle = $this->y_data_label_angle;
+            $font = $this->fonts['y_label'];
+            $formatted_text = $this->FormatLabel('yd', $text);
+        }
+        $color = $this->ndx_title_color; // Currently this is the same for X and Y labels
+
+        // Check to see if the text fits in the available space, if requested.
+        if (isset($min_width) || isset($min_height)) {
+            list($width, $height) = $this->SizeText($font, $angle, $formatted_text);
+            if ((isset($min_width) && ($min_width - $width)  < 2)
+                || (isset($min_height) && ($min_height - $height) < 2))
+                return FALSE;
         }
+
+        $this->DrawText($font, $angle, $this->xtr($x_world) + $x_adjustment,
+                        $this->ytr($y_world) + $y_adjustment,
+                        $color, $formatted_text, $halign, $valign);
         return TRUE;
     }
 
-
-    /*!
+    /*
      * Draws the data label associated with a point in the plot.
      * This is different from x_labels drawn by DrawXTicks() and care
      * should be taken not to draw both, as they'd probably overlap.
@@ -2969,221 +4447,265 @@ class PHPlot {
      * Leave the last parameter out, to avoid the drawing of vertical lines, no matter
      * what the setting is (for plots that need it, like DrawSquared())
      */
-    function DrawXDataLabel($xlab, $xpos, $row=FALSE)
+    protected function DrawXDataLabel($xlab, $xpos, $row=FALSE)
     {
-        // FIXME!! not working...
-        if (($this->_x_label_cnt++ % $this->x_label_inc) != 0)
-            return;
-
-        $xlab = $this->FormatLabel('x', $xlab);
+        $xlab = $this->FormatLabel('xd', $xlab);
 
         // Labels below the plot area
         if ($this->x_data_label_pos == 'plotdown' || $this->x_data_label_pos == 'both')
-            $this->DrawText($this->x_label_font, $this->x_label_angle, $xpos,
-                            $this->plot_area[3] + $this->x_tick_length,
-                            $this->ndx_text_color, $xlab, 'center', 'bottom');
+            $this->DrawText($this->fonts['x_label'], $this->x_data_label_angle,
+                            $xpos, $this->plot_area[3] + $this->x_label_bot_offset,
+                            $this->ndx_text_color, $xlab, 'center', 'top');
 
         // Labels above the plot area
         if ($this->x_data_label_pos == 'plotup' || $this->x_data_label_pos == 'both')
-            $this->DrawText($this->x_label_font, $this->x_label_angle, $xpos,
-                            $this->plot_area[1] - $this->x_tick_length ,
-                            $this->ndx_text_color, $xlab, 'center', 'top');
+            $this->DrawText($this->fonts['x_label'], $this->x_data_label_angle,
+                            $xpos, $this->plot_area[1] - $this->x_label_top_offset,
+                            $this->ndx_text_color, $xlab, 'center', 'bottom');
 
-        if ($row && $this->draw_x_data_label_lines)
+        // $row=0 means this is the first row. $row=FALSE means don't do any rows.
+        if ($row !== FALSE && $this->draw_x_data_label_lines)
             $this->DrawXDataLine($xpos, $row);
+        return TRUE;
+    }
+
+    /*
+     * Draw a data label along the Y axis or side.
+     * This is only used by horizontal bar charts.
+     */
+    protected function DrawYDataLabel($ylab, $ypos)
+    {
+        $ylab = $this->FormatLabel('yd', $ylab);
+
+        // Labels left of the plot area
+        if ($this->y_data_label_pos == 'plotleft' || $this->y_data_label_pos == 'both')
+            $this->DrawText($this->fonts['y_label'], $this->y_data_label_angle,
+                            $this->plot_area[0] - $this->y_label_left_offset, $ypos,
+                            $this->ndx_text_color, $ylab, 'right', 'center');
+
+        // Labels right of the plot area
+        if ($this->y_data_label_pos == 'plotright' || $this->y_data_label_pos == 'both')
+            $this->DrawText($this->fonts['y_label'], $this->y_data_label_angle,
+                            $this->plot_area[2] + $this->y_label_right_offset, $ypos,
+                            $this->ndx_text_color, $ylab, 'left', 'center');
+        return TRUE;
     }
 
-    /*!
+    /*
      * Draws Vertical lines from data points up and down.
      * Which lines are drawn depends on the value of x_data_label_pos,
      * and whether this is at all done or not, on draw_x_data_label_lines
      *
-     * \param xpos int position in pixels of the line.
-     * \param row int index of the data row being drawn.
+     *   $xpos : position in pixels of the line.
+     *   $row : index of the data row being drawn.
      */
-    function DrawXDataLine($xpos, $row)
+    protected function DrawXDataLine($xpos, $row)
     {
         // Sets the line style for IMG_COLOR_STYLED lines (grid)
-        if($this->dashed_grid) {
+        if ($this->dashed_grid) {
             $this->SetDashedStyle($this->ndx_light_grid_color);
             $style = IMG_COLOR_STYLED;
         } else {
             $style = $this->ndx_light_grid_color;
         }
 
-        // Lines from the bottom up
         if ($this->x_data_label_pos == 'both') {
+            // Lines from the bottom up
             ImageLine($this->img, $xpos, $this->plot_area[3], $xpos, $this->plot_area[1], $style);
-        }
-        // Lines coming from the bottom of the plot
-        else if ($this->x_data_label_pos == 'plotdown') {
-            // See FindDataLimits() to see why 'MAXY' index.
-            $ypos = $this->ytr($this->data[$row][MAXY]);
+        } elseif ($this->x_data_label_pos == 'plotdown' && isset($this->data_max[$row])) {
+            // Lines from the bottom of the plot up to the max Y value at this X:
+            $ypos = $this->ytr($this->data_max[$row]);
             ImageLine($this->img, $xpos, $ypos, $xpos, $this->plot_area[3], $style);
-        }
-        // Lines coming from the top of the plot
-        else if ($this->x_data_label_pos == 'plotup') {
-            // See FindDataLimits() to see why 'MINY' index.
-            $ypos = $this->ytr($this->data[$row][MINY]);
+        } elseif ($this->x_data_label_pos == 'plotup' && isset($this->data_min[$row])) {
+            // Lines from the top of the plot down to the min Y value at this X:
+            $ypos = $this->ytr($this->data_min[$row]);
             ImageLine($this->img, $xpos, $this->plot_area[1], $xpos, $ypos, $style);
         }
-    } 
-    
-/*    
-    function DrawPlotLabel($xlab, $xpos, $ypos) 
-    {
-        $this->DrawText($this->x_label_font, $this->x_label_angle, $xpos, $this
-*/
+        return TRUE;
+    }
 
-    /*!
+    /*
      * Draws the graph legend
      *
-     * \note Base code submitted by Marlin Viss
-     * FIXME: maximum label length should be calculated more accurately for TT fonts
-     *        Performing a BBox calculation for every legend element, for example.
+     * Base code submitted by Marlin Viss
      */
-    function DrawLegend($which_x1, $which_y1, $which_boxtype)
+    protected function DrawLegend()
     {
-        // Find maximum legend label length
-        $max_len = 0;
-        foreach ($this->legend as $leg) {
-            $len = strlen($leg);
-            $max_len = ($len > $max_len) ? $len : $max_len;
+        $font = &$this->fonts['legend'];
+
+        // Find maximum legend label line width.
+        $max_width = 0;
+        foreach ($this->legend as $line) {
+            list($width, $unused) = $this->SizeText($font, 0, $line);
+            if ($width > $max_width) $max_width = $width;
         }
-        $max_len += 5;          // Leave room for the boxes and margins
 
-        /////// Calculate legend labels sizes:  FIXME - dirty hack - FIXME
-        // TTF:
-        if ($this->use_ttf) {
-            $size = $this->TTFBBoxSize($this->legend_font['size'], 0,
-                                       $this->legend_font['font'], '_');
-            $char_w = $size[0];
-
-            $size = $this->TTFBBoxSize($this->legend_font['size'], 0,
-                                       $this->legend_font['font'], '|');
-            $char_h = $size[1];                                       
-        } 
-        // Fixed fonts:
-        else {
-            $char_w = $this->legend_font['width'];
-            $char_h = $this->legend_font['height'];
-        }
-
-        $v_margin = $char_h/2;                         // Between vertical borders and labels
-        $dot_height = $char_h + $this->line_spacing;   // Height of the small colored boxes
-        $width = $char_w * $max_len;
-
-        //////// Calculate box size
-        // upper Left
-        if ( (! $which_x1) || (! $which_y1) ) {
-            $box_start_x = $this->plot_area[2] - $width;
-            $box_start_y = $this->plot_area[1] + 5;
-        } else { 
-            $box_start_x = $which_x1;
-            $box_start_y = $which_y1;
+        // Use the font parameters to size the color boxes:
+        $char_w = $font['width'];
+        $char_h = $font['height'];
+        $line_spacing = $this->GetLineSpacing($font);
+
+        // Normalize text alignment and colorbox alignment variables:
+        $text_align = isset($this->legend_text_align) ? $this->legend_text_align : 'right';
+        $colorbox_align = isset($this->legend_colorbox_align) ? $this->legend_colorbox_align : 'right';
+
+        // Sizing parameters:
+        $v_margin = $char_h/2;                   // Between vertical borders and labels
+        $dot_height = $char_h + $line_spacing;   // Height of the small colored boxes
+        // Overall legend box width e.g.: | space colorbox space text space |
+        // where colorbox and each space are 1 char width.
+        if ($colorbox_align != 'none') {
+            $width = $max_width + 4 * $char_w;
+            $draw_colorbox = TRUE;
+        } else {
+            $width = $max_width + 2 * $char_w;
+            $draw_colorbox = FALSE;
         }
 
-        // Lower right corner
-        $box_end_y = $box_start_y + $dot_height*(count($this->legend)) + 2*$v_margin; 
-        $box_end_x = $box_start_x + $width - 5;
+        //////// Calculate box position
+        // User-defined position specified?
+        if ( !isset($this->legend_x_pos) || !isset($this->legend_y_pos)) {
+            // No, use default
+            $box_start_x = $this->plot_area[2] - $width - $this->safe_margin;
+            $box_start_y = $this->plot_area[1] + $this->safe_margin;
+        } elseif (isset($this->legend_xy_world)) {
+            // User-defined position in world-coordinates (See SetLegendWorld).
+            $box_start_x = $this->xtr($this->legend_x_pos);
+            $box_start_y = $this->ytr($this->legend_y_pos);
+            unset($this->legend_xy_world);
+        } else {
+            // User-defined position in pixel coordinates.
+            $box_start_x = $this->legend_x_pos;
+            $box_start_y = $this->legend_y_pos;
+        }
 
+        // Lower right corner
+        $box_end_y = $box_start_y + $dot_height*(count($this->legend)) + 2*$v_margin;
+        $box_end_x = $box_start_x + $width;
 
         // Draw outer box
-        ImageFilledRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y, $this->ndx_bg_color);
-        ImageRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y, $this->ndx_grid_color);
+        ImageFilledRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y,
+                             $this->ndx_bg_color);
+        ImageRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y,
+                       $this->ndx_grid_color);
 
         $color_index = 0;
         $max_color_index = count($this->ndx_data_colors) - 1;
 
-        $dot_left_x = $box_end_x - $char_w * 2;
-        $dot_right_x = $box_end_x - $char_w;
-        $y_pos = $box_start_y + $v_margin;
+        // Calculate color box and text horizontal positions.
+        if (!$draw_colorbox) {
+            if ($text_align == 'left')
+                $x_pos = $box_start_x + $char_w;
+            else
+                $x_pos = $box_end_x - $char_w;
+        } elseif ($colorbox_align == 'left') {
+            $dot_left_x = $box_start_x + $char_w;
+            $dot_right_x = $dot_left_x + $char_w;
+            if ($text_align == 'left')
+                $x_pos = $dot_left_x + 2 * $char_w;
+            else
+                $x_pos = $box_end_x - $char_w;
+        } else {
+            $dot_left_x = $box_end_x - 2 * $char_w;
+            $dot_right_x = $dot_left_x + $char_w;
+            if ($text_align == 'left')
+                $x_pos = $box_start_x + $char_w;
+            else
+                $x_pos = $dot_left_x - $char_w;
+        }
+
+        // Calculate starting position of first text line.  The bottom of each color box
+        // lines up with the bottom (baseline) of its text line.
+        $y_pos = $box_start_y + $v_margin + $dot_height;
 
         foreach ($this->legend as $leg) {
-            // Text right aligned to the little box
-            $this->DrawText($this->legend_font, 0, $dot_left_x - $char_w, $y_pos, 
-                            $this->ndx_text_color, $leg, 'right');
-            // Draw a box in the data color
-            ImageFilledRectangle($this->img, $dot_left_x, $y_pos + 1, $dot_right_x,
-                                 $y_pos + $dot_height-1, $this->ndx_data_colors[$color_index]);
-            // Draw a rectangle around the box
-            ImageRectangle($this->img, $dot_left_x, $y_pos + 1, $dot_right_x,
-                           $y_pos + $dot_height-1, $this->ndx_text_color);
-
-            $y_pos += $char_h + $this->line_spacing;
+            // Draw text with requested alignment:
+            $this->DrawText($font, 0, $x_pos, $y_pos, $this->ndx_text_color, $leg, $text_align, 'bottom');
+            if ($draw_colorbox) {
+                // Draw a box in the data color
+                $y1 = $y_pos - $dot_height + 1;
+                $y2 = $y_pos - 1;
+                ImageFilledRectangle($this->img, $dot_left_x, $y1, $dot_right_x, $y2,
+                                     $this->ndx_data_colors[$color_index]);
+                // Draw a rectangle around the box
+                ImageRectangle($this->img, $dot_left_x, $y1, $dot_right_x, $y2,
+                               $this->ndx_text_color);
+            }
+            $y_pos += $dot_height;
 
             $color_index++;
-            if ($color_index > $max_color_index) 
+            if ($color_index > $max_color_index)
                 $color_index = 0;
         }
-    } // Function DrawLegend()
-
-
-    /*!
-     * TODO Draws a legend over (or below) an axis of the plot.
-     */
-    function DrawAxisLegend()
-    {
-        // Calculate available room
-        // Calculate length of all items (boxes included)
-        // Calculate number of lines and room it would take. FIXME: this should be known in CalcMargins()
-        // Draw.
+        return TRUE;
     }
 
 /////////////////////////////////////////////
 ////////////////////             PLOT DRAWING
 /////////////////////////////////////////////
 
-
-    /*!
-     * Draws a pie chart. Data has to be 'text-data' type.
+    /*
+     * Draws a pie chart. Data is 'text-data', 'data-data', or 'text-data-single'.
+     *
+     *  For text-data-single, the data array contains records with an ignored label,
+     *  and one Y value. Each record defines a sector of the pie, as a portion of
+     *  the sum of all Y values.
+     *
+     *  For text-data and data-data, the data array contains records with an ignored label,
+     *  an ignored X value (for data-data only), and N (N>=1) Y values per record.
+     *  The pie chart will be produced with N segments. The relative size of the first
+     *  sector of the pie is the sum of the first Y data value in each record, etc.
+     *
+     *  Note: With text-data-single, the data labels could be used, but are not currently.
      *
-     *  This can work in two ways: the classical, with a column for each sector
-     *  (computes the column totals and draws the pie with that)
-     *  OR
-     *  Takes each row as a sector and uses it's first value. This has the added
-     *  advantage of using the labels provided, which is not the case with the
-     *  former method. This might prove useful for pie charts from GROUP BY sql queries
+     *  If there are no valid data points > 0 at all, just draw nothing. It may seem more correct to
+     *  raise an error, but all of the other plot types handle it this way implicitly. DrawGraph
+     *  checks for an empty data array, but this is different: a non-empty data array with no Y values,
+     *  or all Y=0.
      */
-    function DrawPieChart()
+    protected function DrawPieChart()
     {
+        if (!$this->CheckDataType('text-data, text-data-single, data-data'))
+            return FALSE;
+
+        // Allocate dark colors only if they will be used for shading.
+        if ($this->shading > 0)
+            $this->NeedDataDarkColors();
+
         $xpos = $this->plot_area[0] + $this->plot_area_width/2;
         $ypos = $this->plot_area[1] + $this->plot_area_height/2;
         $diameter = min($this->plot_area_width, $this->plot_area_height);
         $radius = $diameter/2;
 
-        // Get sum of each column? One pie slice per column
-        if ($this->data_type === 'text-data') {
-            for ($i = 0; $i < $this->num_data_rows; $i++) {
-                for ($j = 1; $j < $this->num_recs[$i]; $j++) {      // Label ($row[0]) unused in these pie charts
-                    @ $sumarr[$j] += abs($this->data[$i][$j]);      // NOTE!  sum > 0 to make pie charts
-                }
-            }
-        }
-        // Or only one column per row, one pie slice per row?
-        else if ($this->data_type == 'text-data-single') {
-            for ($i = 0; $i < $this->num_data_rows; $i++) {
-                $legend[$i] = $this->data[$i][0];                   // Set the legend to column labels
-                $sumarr[$i] = $this->data[$i][1];
+        $num_slices = $this->data_columns;  // See CheckDataArray which calculates this for us.
+        if ($num_slices < 1) return TRUE;   // Give up early if there is no data at all.
+        $sumarr = array_fill(0, $num_slices, 0);
+
+        if ($this->datatype_pie_single) {
+            // text-data-single: One data column per row, one pie slice per row.
+            for ($i = 0; $i < $num_slices; $i++) {
+                // $legend[$i] = $this->data[$i][0];                // Note: Labels are not used yet
+                if (is_numeric($this->data[$i][1]))
+                    $sumarr[$i] = abs($this->data[$i][1]);
             }
-        }
-        else if ($this->data_type == 'data-data') {
+        } else {
+            // text-data: Sum each column (skipping label), one pie slice per column.
+            // data-data: Sum each column (skipping X value and label), one pie slice per column.
+            $skip = ($this->datatype_implied) ? 1 : 2; // Leading values to skip in each row.
             for ($i = 0; $i < $this->num_data_rows; $i++) {
-                for ($j = 2; $j < $this->num_recs[$i]; $j++) {
-                    @ $sumarr[$j] += abs($this->data[$i][$j]);
+                for ($j = $skip; $j < $this->num_recs[$i]; $j++) {
+                    if (is_numeric($this->data[$i][$j]))
+                        $sumarr[$j-$skip] += abs($this->data[$i][$j]);
                 }
             }
         }
-        else {
-            $this->DrawError("DrawPieChart(): Data type '$this->data_type' not supported.");
-            return FALSE;
-        }
 
         $total = array_sum($sumarr);
 
         if ($total == 0) {
-            $this->DrawError('DrawPieChart(): Empty data set');
-            return FALSE;
+            // There are either no valid data points, or all are 0.
+            // See top comment about why not to make this an error.
+            return TRUE;
         }
 
         if ($this->shading) {
@@ -3191,214 +4713,334 @@ class PHPlot {
         } else {
             $diam2 = $diameter;
         }
-        $max_data_colors = count ($this->data_colors);
+        $max_data_colors = count($this->ndx_data_colors);
+
+        // Use the Y label format precision, with default value:
+        if (isset($this->label_format['y']['precision']))
+            $precision = $this->label_format['y']['precision'];
+        else
+            $precision = 1;
 
         for ($h = $this->shading; $h >= 0; $h--) {
             $color_index = 0;
             $start_angle = 0;
             $end_angle = 0;
-            foreach ($sumarr as $val) {
+            for ($j = 0; $j < $num_slices; $j++) {
+                $val = $sumarr[$j];
+
                 // For shaded pies: the last one (at the top of the "stack") has a brighter color:
                 if ($h == 0)
                     $slicecol = $this->ndx_data_colors[$color_index];
                 else
                     $slicecol = $this->ndx_data_dark_colors[$color_index];
 
-                $label_txt = number_format(($val / $total * 100), $this->y_precision, '.', ', ') . '%';
+                $label_txt = $this->number_format(($val / $total * 100), $precision) . '%';
                 $val = 360 * ($val / $total);
 
                 // NOTE that imagefilledarc measures angles CLOCKWISE (go figure why),
                 // so the pie chart would start clockwise from 3 o'clock, would it not be
                 // for the reversal of start and end angles in imagefilledarc()
+                // Also note ImageFilledArc only takes angles in integer degrees, and if the
+                // the start and end angles match then you get a full circle not a zero-width
+                // pie. This is bad. So skip any zero-size wedge. On the other hand, we cannot
+                // let cumulative error from rounding to integer result in missing wedges. So
+                // keep the running total as a float, and round the angles. It should not
+                // be necessary to check that the last wedge ends at 360 degrees.
                 $start_angle = $end_angle;
                 $end_angle += $val;
-                $mid_angle = deg2rad($end_angle - ($val / 2));
-
-                // Draw the slice
-                ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2,
-                               360-$end_angle, 360-$start_angle,
-                               $slicecol, IMG_ARC_PIE);
-
-                // Draw the labels only once
-                if ($h == 0) {
-                    // Draw the outline
-                    if (! $this->shading)
-                        ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2,
-                                       360-$end_angle, 360-$start_angle,
-                                       $this->ndx_grid_color, IMG_ARC_PIE | IMG_ARC_EDGED |IMG_ARC_NOFILL);
-
-
-                    // The '* 1.2' trick is to get labels out of the pie chart so there are more
-                    // chances they can be seen in small sectors.
-                    $label_x = $xpos + ($diameter * 1.2 * cos($mid_angle)) * $this->label_scale_position;
-                    $label_y = $ypos+$h - ($diam2 * 1.2 * sin($mid_angle)) * $this->label_scale_position;
-
-                    $this->DrawText($this->generic_font, 0, $label_x, $label_y, $this->ndx_grid_color,
-                                    $label_txt, 'center', 'center');
+                // This method of conversion to integer - truncate after reversing it - was
+                // chosen to match the implicit method of PHPlot<=5.0.4 to get the same slices.
+                $arc_start_angle = (int)(360 - $start_angle);
+                $arc_end_angle = (int)(360 - $end_angle);
+
+                if ($arc_start_angle > $arc_end_angle) {
+                    $mid_angle = deg2rad($end_angle - ($val / 2));
+
+                    // Draw the slice
+                    ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2,
+                                   $arc_end_angle, $arc_start_angle,
+                                   $slicecol, IMG_ARC_PIE);
+
+                    // Draw the labels only once
+                    if ($h == 0) {
+                        // Draw the outline
+                        if (! $this->shading)
+                            ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2,
+                                           $arc_end_angle, $arc_start_angle, $this->ndx_grid_color,
+                                           IMG_ARC_PIE | IMG_ARC_EDGED |IMG_ARC_NOFILL);
+
+                        // The '* 1.2' trick is to get labels out of the pie chart so there are more
+                        // chances they can be seen in small sectors.
+                        $label_x = $xpos + ($diameter * 1.2 * cos($mid_angle)) * $this->label_scale_position;
+                        $label_y = $ypos+$h - ($diam2 * 1.2 * sin($mid_angle)) * $this->label_scale_position;
+
+                        $this->DrawText($this->fonts['generic'], 0, $label_x, $label_y, $this->ndx_grid_color,
+                                        $label_txt, 'center', 'center');
+                    }
                 }
-                $color_index++;
-                $color_index = $color_index % $max_data_colors;
+                if (++$color_index >= $max_data_colors)
+                    $color_index = 0;
             }   // end for
         }   // end for
+        return TRUE;
+    }
+
+    /*
+     * Get data color to use for plotting.
+     *   $row, $idx : Index arguments for the current data point.
+     *   &$vars : Variable storage. Caller makes an empty array, and this function uses it.
+     *   &$data_color : Returned result - Color index for the data point.
+     *   $extra : Extra info flag passed through to data color callback.
+     */
+    protected function GetDataColor($row, $idx, &$vars, &$data_color, $extra = 0)
+    {
+        // Initialize or extract variables:
+        if (empty($vars)) {
+            $custom_color = (bool)$this->GetCallback('data_color');
+            $num_data_colors = count($this->ndx_data_colors);
+            $vars = compact('custom_color', 'num_data_colors');
+        } else {
+          extract($vars);
+        }
+
+        // Select the colors.
+        if ($custom_color) {
+            $col_i = $this->DoCallback('data_color', $row, $idx, $extra); // Custom color index
+            $data_color = $this->ndx_data_colors[$col_i % $num_data_colors];
+        } else {
+            $data_color = $this->ndx_data_colors[$idx];
+        }
     }
 
+    /*
+     * Get data color and error bar color to use for plotting.
+     *   $row, $idx : Index arguments for the current bar.
+     *   &$vars : Variable storage. Caller makes an empty array, and this function uses it.
+     *   &$data_color : Returned result - Color index for the data (bar fill)
+     *   &$error_color : Returned result - Color index for the error bars
+     *   $extra : Extra info flag passed through to data color callback.
+     */
+    protected function GetDataErrorColors($row, $idx, &$vars, &$data_color, &$error_color, $extra = 0)
+    {
+        // Initialize or extract variables:
+        if (empty($vars)) {
+            $this->NeedErrorBarColors();   // This plot needs error bar colors.
+            $custom_color = (bool)$this->GetCallback('data_color');
+            $num_data_colors = count($this->ndx_data_colors);
+            $num_error_colors = count($this->ndx_error_bar_colors);
+            $vars = compact('custom_color', 'num_data_colors', 'num_error_colors');
+        } else {
+          extract($vars);
+        }
+
+        // Select the colors.
+        if ($custom_color) {
+            $col_i = $this->DoCallback('data_color', $row, $idx, $extra); // Custom color index
+            $data_color = $this->ndx_data_colors[$col_i % $num_data_colors];
+            $error_color = $this->ndx_error_bar_colors[$col_i % $num_error_colors];
+        } else {
+            $data_color = $this->ndx_data_colors[$idx];
+            $error_color = $this->ndx_error_bar_colors[$idx];
+        }
+    }
 
-    /*!
-     * Supported data formats: data-data-error, text-data-error (doesn't exist yet)
-     * ( data comes in as array("title", x, y, error+, error-, y2, error2+, error2-, ...) )
+    /*
+     * Draw the points and errors bars for an error plot of types points and linepoints
+     * Supports only data-data-error format, with each row of the form
+     *   array("title", x, y1, error1+, error1-, y2, error2+, error2-, ...)
+     * This is called from DrawDots, with data type already checked.
+     *   $paired is true for linepoints error plots, to make sure elements are
+     *       only drawn once.  If true, data labels are drawn by DrawLinesError, and error
+     *       bars are drawn by DrawDotsError. (This choice is for backwards compatibility.)
      */
-    function DrawDotsError()
+    protected function DrawDotsError($paired = FALSE)
     {
-        $this->CheckOption($this->data_type, 'data-data-error', __FUNCTION__);
+        // Adjust the point shapes and point sizes arrays:
+        $this->CheckPointParams();
+
+        $gcvars = array(); // For GetDataErrorColors, which initializes and uses this.
+        // Special flag for data color callback to indicate the 'points' part of 'linepoints':
+        $alt_flag = $paired ? 1 : 0;
 
-        for($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
+        for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
             $record = 1;                                // Skip record #0 (title)
 
-            // Do we have a value for X?
-            if ($this->data_type == 'data-data-error')
-                $x_now = $this->data[$row][$record++];  // Read it, advance record index
-            else
-                $x_now = 0.5 + $cnt++;                  // Place text-data at X = 0.5, 1.5, 2.5, etc...
+            $x_now = $this->data[$row][$record++];  // Read it, advance record index
+
+            $x_now_pixels = $this->xtr($x_now);             // Absolute coordinates.
 
             // Draw X Data labels?
-            if ($this->x_data_label_pos != 'none')
+            if ($this->x_data_label_pos != 'none' && !$paired)
                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
 
-            while ($record < $this->num_recs[$row]) {
+            // Now go for Y, E+, E-
+            for ($idx = 0; $record < $this->num_recs[$row]; $idx++) {
+                if (is_numeric($this->data[$row][$record])) {         // Allow for missing Y data
+
+                    // Select the colors:
+                    $this->GetDataErrorColors($row, $idx, $gcvars, $data_color, $error_color, $alt_flag);
+
                     // Y:
-                    $y_now = $this->data[$row][$record];
-                    $this->DrawDot($x_now, $y_now, $record, $this->ndx_data_colors[$record++]);
+                    $y_now = $this->data[$row][$record++];
+                    $this->DrawDot($x_now, $y_now, $idx, $data_color);
 
                     // Error +
-                    $val = $this->data[$row][$record];
-                    $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape,
-                                         $this->ndx_error_bar_colors[$record++]);
+                    $val = $this->data[$row][$record++];
+                    $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape, $error_color);
                     // Error -
-                    $val = $this->data[$row][$record];
-                    $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape,
-                                         $this->ndx_error_bar_colors[$record++]);
+                    $val = $this->data[$row][$record++];
+                    $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape, $error_color);
+                } else {
+                    $record += 3;  // Skip over missing Y and its error values
+                }
             }
         }
-    } // function DrawDotsError()
-
+        return TRUE;
+    }
 
     /*
-     * Supported data types:
-     *  - data-data ("title", x, y1, y2, y3, ...)
-     *  - text-data ("title", y1, y2, y3, ...)
+     * Draw a points plot, or the points for a linepoints plot
+     * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...)
+     * Points plot with error bars (data-data-error format) is redirected to DrawDotsError.
+     *   $paired is true for linepoints plots, to make sure elements are only drawn once.
      */
-    function DrawDots()
+    protected function DrawDots($paired = FALSE)
     {
-        $this->CheckOption($this->data_type, 'text-data, data-data', __FUNCTION__);
+        if (!$this->CheckDataType('text-data, data-data, data-data-error'))
+            return FALSE;
+        if ($this->datatype_error_bars)
+            return $this->DrawDotsError($paired); // Redirect for points+errorbars plot
+
+        // Adjust the point shapes and point sizes arrays:
+        $this->CheckPointParams();
+
+        $gcvars = array(); // For GetDataColor, which initializes and uses this.
+        // Special flag for data color callback to indicate the 'points' part of 'linepoints':
+        $alt_flag = $paired ? 1 : 0;
 
         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
             $rec = 1;                    // Skip record #0 (data label)
 
-            // Do we have a value for X?
-            if ($this->data_type == 'data-data')
-                $x_now = $this->data[$row][$rec++];  // Read it, advance record index
+            if ($this->datatype_implied)                    // Implied X values?
+                $x_now = 0.5 + $cnt++;                      // Place text-data at X = 0.5, 1.5, 2.5, etc...
             else
-                $x_now = 0.5 + $cnt++;       // Place text-data at X = 0.5, 1.5, 2.5, etc...
+                $x_now = $this->data[$row][$rec++];         // Read it, advance record index
 
             $x_now_pixels = $this->xtr($x_now);
 
             // Draw X Data labels?
-            if ($this->x_data_label_pos != 'none')
+            if ($this->x_data_label_pos != 'none' && !$paired)
                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
 
             // Proceed with Y values
-            for($idx = 0;$rec < $this->num_recs[$row]; $rec++, $idx++) {
+            for ($idx = 0;$rec < $this->num_recs[$row]; $rec++, $idx++) {
                 if (is_numeric($this->data[$row][$rec])) {              // Allow for missing Y data
-                    $this->DrawDot($x_now, $this->data[$row][$rec],
-                                   $rec, $this->ndx_data_colors[$idx]);
+
+                    // Select the color:
+                    $this->GetDataColor($row, $idx, $gcvars, $data_color, $alt_flag);
+                    // Draw the marker:
+                    $this->DrawDot($x_now, $this->data[$row][$rec], $idx, $data_color);
                 }
             }
         }
-    } //function DrawDots
+        return TRUE;
+    }
 
+    /*
+     * Draw a Thin Bar Line plot, also known as an Impulse plot.
+     * A clean, fast routine for when you just want charts like stock volume charts.
+     * Supports data-data and text-data formats for vertical plots,
+     * and data-data-yx and text-data-yx for horizontal plots.
+     * Note that although this plot type supports multiple data sets, it rarely makes
+     * sense to have more than 1, because the lines will overlay.
+     * This one function does both vertical and horizontal plots. "iv" is used for the
+     * independent variable (X for vertical plots, Y for horizontal) and "dv" is used
+     * for the dependent variable (Y for vertical plots, X for horizontal).
+     */
+    protected function DrawThinBarLines()
+    {
+        if (!$this->CheckDataType('text-data, data-data, text-data-yx, data-data-yx'))
+            return FALSE;
 
-    /*!
-     * A clean, fast routine for when you just want charts like stock volume charts
-     */
-    function DrawThinBarLines()
-    {
-        $this->CheckOption($this->data_type, 'text-data, data-data', __FUNCTION__);
+        $gcvars = array(); // For GetDataColor, which initializes and uses this.
 
         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
             $rec = 1;                    // Skip record #0 (data label)
 
-            // Do we have a value for X?
-            if ($this->data_type == 'data-data')
-                $x_now = $this->data[$row][$rec++];  // Read it, advance record index
+            if ($this->datatype_implied)                    // Implied independent variable values?
+                $iv_now = 0.5 + $cnt++;                     // Place text-data at 0.5, 1.5, 2.5, etc...
             else
-                $x_now = 0.5 + $cnt++;       // Place text-data at X = 0.5, 1.5, 2.5, etc...
-
-            $x_now_pixels = $this->xtr($x_now);
+                $iv_now = $this->data[$row][$rec++];        // Read it, advance record index
 
-            // Draw X Data labels?
-            if ($this->x_data_label_pos != 'none')
-                $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
+            if ($this->datatype_swapped_xy) {
+                $y_now_pixels = $this->ytr($iv_now);
+                // Draw Y Data labels?
+                if ($this->y_data_label_pos != 'none')
+                    $this->DrawYDataLabel($this->data[$row][0], $y_now_pixels);
+            } else {
+                $x_now_pixels = $this->xtr($iv_now);
+                // Draw X Data labels?
+                if ($this->x_data_label_pos != 'none')
+                    $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
+            }
 
-            // Proceed with Y values
-            for($idx = 0;$rec < $this->num_recs[$row]; $rec++, $idx++) {
-                if (is_numeric($this->data[$row][$rec])) {              // Allow for missing Y data 
+            // Proceed with dependent values
+            for ($idx = 0; $rec < $this->num_recs[$row]; $rec++, $idx++) {
+                if (is_numeric($this->data[$row][$rec])) {              // Allow for missing data
+                    $dv = $this->data[$row][$rec];
                     ImageSetThickness($this->img, $this->line_widths[$idx]);
-                    // Draws a line from user defined x axis position up to ytr($val)
-                    ImageLine($this->img, $x_now_pixels, $this->x_axis_y_pixels, $x_now_pixels, 
-                              $this->ytr($this->data[$row][$rec]), $this->ndx_data_colors[$idx]);
+
+                    // Select the color:
+                    $this->GetDataColor($row, $idx, $gcvars, $data_color);
+
+                    if ($this->datatype_swapped_xy) {
+                        // Draw a line from user defined y axis position right (or left) to xtr($dv)
+                        ImageLine($this->img, $this->y_axis_x_pixels, $y_now_pixels,
+                                              $this->xtr($dv), $y_now_pixels, $data_color);
+                    } else {
+                        // Draw a line from user defined x axis position up (or down) to ytr($dv)
+                        ImageLine($this->img, $x_now_pixels, $this->x_axis_y_pixels,
+                                              $x_now_pixels, $this->ytr($dv), $data_color);
+                   }
                 }
             }
         }
 
         ImageSetThickness($this->img, 1);
-    }  //function DrawThinBarLines
+        return TRUE;
+    }
 
-    /*!
-     *
+    /*
+     *  Draw an Error Bar set. Used by DrawDotsError and DrawLinesError
      */
-    function DrawYErrorBar($x_world, $y_world, $error_height, $error_bar_type, $color)
+    protected function DrawYErrorBar($x_world, $y_world, $error_height, $error_bar_type, $color)
     {
-        /* 
-        // TODO: add a parameter to show datalabels next to error bars?
-        // something like this:
-        if ($this->x_data_label_pos == 'plot') {
-            $this->DrawText($this->error_font, 90, $x1, $y2, 
-                            $color, $label, 'center', 'top');
-        */
-
         $x1 = $this->xtr($x_world);
         $y1 = $this->ytr($y_world);
         $y2 = $this->ytr($y_world+$error_height) ;
 
         ImageSetThickness($this->img, $this->error_bar_line_width);
         ImageLine($this->img, $x1, $y1 , $x1, $y2, $color);
-
-        switch ($error_bar_type) {
-        case 'line':
-            break;
-        case 'tee':
+        if ($error_bar_type == 'tee') {
             ImageLine($this->img, $x1-$this->error_bar_size, $y2, $x1+$this->error_bar_size, $y2, $color);
-            break;
-        default:
-            ImageLine($this->img, $x1-$this->error_bar_size, $y2, $x1+$this->error_bar_size, $y2, $color);
-            break;
         }
-
         ImageSetThickness($this->img, 1);
         return TRUE;
     }
 
-    /*!
+    /*
      * Draws a styled dot. Uses world coordinates.
-     * Supported types: 'halfline', 'line', 'plus', 'cross', 'rect', 'circle', 'dot',
-     * 'diamond', 'triangle', 'trianglemid'
+     * The list of supported shapes can also be found in SetPointShapes().
+     * All shapes are drawn using a 3x3 grid, centered on the data point.
+     * The center is (x_mid, y_mid) and the corners are (x1, y1) and (x2, y2).
+     *   $record is the 0-based index that selects the shape and size.
      */
-    function DrawDot($x_world, $y_world, $record, $color)
+    protected function DrawDot($x_world, $y_world, $record, $color)
     {
-        // TODO: optimize, avoid counting every time we are called.
-        $record = $record % count ($this->point_shapes);
+        $index = $record % $this->point_counts;
+        $point_size = $this->point_sizes[$index];
 
-        $half_point = $this->point_sizes[$record] / 2;
+        $half_point = (int)($point_size / 2);
 
         $x_mid = $this->xtr($x_world);
         $y_mid = $this->ytr($y_world);
@@ -3408,7 +5050,7 @@ class PHPlot {
         $y1 = $y_mid - $half_point;
         $y2 = $y_mid + $half_point;
 
-        switch ($this->point_shapes[$record]) {
+        switch ($this->point_shapes[$index]) {
         case 'halfline':
             ImageLine($this->img, $x1, $y_mid, $x_mid, $y_mid, $color);
             break;
@@ -3423,15 +5065,11 @@ class PHPlot {
             ImageLine($this->img, $x1, $y1, $x2, $y2, $color);
             ImageLine($this->img, $x1, $y2, $x2, $y1, $color);
             break;
-        case 'rect':
-            ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $color);
-            break;
         case 'circle':
-            ImageArc($this->img, $x_mid, $y_mid, $this->point_sizes[$record], $this->point_sizes[$record], 0, 360, $color);
+            ImageArc($this->img, $x_mid, $y_mid, $point_size, $point_size, 0, 360, $color);
             break;
         case 'dot':
-            ImageFilledArc($this->img, $x_mid, $y_mid, $this->point_sizes[$record], $this->point_sizes[$record], 0, 360,
-                           $color, IMG_ARC_PIE);
+            ImageFilledEllipse($this->img, $x_mid, $y_mid, $point_size, $point_size, $color);
             break;
         case 'diamond':
             $arrpoints = array( $x1, $y_mid, $x_mid, $y1, $x2, $y_mid, $x_mid, $y2);
@@ -3445,108 +5083,174 @@ class PHPlot {
             $arrpoints = array( $x1, $y1, $x2, $y1, $x_mid, $y_mid);
             ImageFilledPolygon($this->img, $arrpoints, 3, $color);
             break;
-        default:
+        case 'yield':
+            $arrpoints = array( $x1, $y1, $x2, $y1, $x_mid, $y2);
+            ImageFilledPolygon($this->img, $arrpoints, 3, $color);
+            break;
+        case 'delta':
+            $arrpoints = array( $x1, $y2, $x2, $y2, $x_mid, $y1);
+            ImageFilledPolygon($this->img, $arrpoints, 3, $color);
+            break;
+        case 'star':
+            ImageLine($this->img, $x1, $y_mid, $x2, $y_mid, $color);
+            ImageLine($this->img, $x_mid, $y1, $x_mid, $y2, $color);
+            ImageLine($this->img, $x1, $y1, $x2, $y2, $color);
+            ImageLine($this->img, $x1, $y2, $x2, $y1, $color);
+            break;
+        case 'hourglass':
+            $arrpoints = array( $x1, $y1, $x2, $y1, $x1, $y2, $x2, $y2);
+            ImageFilledPolygon($this->img, $arrpoints, 4, $color);
+            break;
+        case 'bowtie':
+            $arrpoints = array( $x1, $y1, $x1, $y2, $x2, $y1, $x2, $y2);
+            ImageFilledPolygon($this->img, $arrpoints, 4, $color);
+            break;
+        case 'target':
+            ImageFilledRectangle($this->img, $x1, $y1, $x_mid, $y_mid, $color);
+            ImageFilledRectangle($this->img, $x_mid, $y_mid, $x2, $y2, $color);
+            ImageRectangle($this->img, $x1, $y1, $x2, $y2, $color);
+            break;
+        case 'box':
+            ImageRectangle($this->img, $x1, $y1, $x2, $y2, $color);
+            break;
+        case 'home': /* As in: "home plate" (baseball), also looks sort of like a house. */
+            $arrpoints = array( $x1, $y2, $x2, $y2, $x2, $y_mid, $x_mid, $y1, $x1, $y_mid);
+            ImageFilledPolygon($this->img, $arrpoints, 5, $color);
+            break;
+        case 'up':
+            ImagePolygon($this->img, array($x_mid, $y1, $x2, $y2, $x1, $y2), 3, $color);
+            break;
+        case 'down':
+            ImagePolygon($this->img, array($x_mid, $y2, $x1, $y1, $x2, $y1), 3, $color);
+            break;
+        case 'none': /* Special case, no point shape here */
+            break;
+        default: /* Also 'rect' */
             ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $color);
             break;
         }
         return TRUE;
     }
 
-    /*!
-     * Draw an area plot. Supported data types:
-     *      'text-data'
-     *      'data-data'
-     * NOTE: This function used to add first and last data values even on incomplete
-     *       sets. That is not the behaviour now. As for missing data in between,
-     *       there are two posibilities: replace the point with one on the X axis (previous
-     *       way), or forget about it and use the preceding and following ones to draw the polygon.
-     *       There is the possibility to use both, we just need to add the method to set
-     *       it. Something like SetMissingDataBehaviour(), for example.
-     */
-    function DrawArea()
-    {
-        $incomplete_data_defaults_to_x_axis = FALSE;        // TODO: make this configurable
+    /*
+     * Draw an 'area' or 'stacked area' plot.
+     * Both of these fill the area between lines, but in the stacked area graph the Y values
+     * are accumulated for each X, same as stacked bars. In the regular area graph, the areas
+     * are filled in order from the X axis up to each Y (so the Y values for each X need to be
+     * in decreasing order in this case).
+     * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...)
+     * Notes:
+     *   All Y values must be >= 0. (If any Y<0 the absolute value is used.)
+     *   Missing data points are NOT handled. (They are counted as 0.)
+     *   All rows must have the same number of Y points, or an error image will be produced.
+     */
+    protected function DrawArea($do_stacked = FALSE)
+    {
+        if (!$this->CheckDataType('text-data, data-data'))
+            return FALSE;
 
-        for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
+        $n = $this->num_data_rows;  // Number of X values
+
+        // These arrays store the device X and Y coordinates for all lines:
+        $xd = array();
+        $yd = array();
+
+        // Make sure each row has the same number of values. Note records_per_group is max(num_recs).
+        if ($this->records_per_group != min($this->num_recs)) {
+            return $this->PrintError("DrawArea(): Data array must contain the same number"
+                      . " of Y values for each X");
+        }
+
+        // Calculate the Y value for each X, and store the device
+        // coordinates into the xd and yd arrays.
+        // For stacked area plots, the Y values accumulate.
+        for ($row = 0; $row < $n; $row++) {
             $rec = 1;                                       // Skip record #0 (data label)
 
-            if ($this->data_type == 'data-data')            // Do we have a value for X?
-                $x_now = $this->data[$row][$rec++];         // Read it, advance record index
+            if ($this->datatype_implied)                    // Implied X values?
+                $x_now = 0.5 + $row;                        // Place text-data at X = 0.5, 1.5, 2.5, etc...
             else
-                $x_now = 0.5 + $cnt++;                      // Place text-data at X = 0.5, 1.5, 2.5, etc...
-
-            $x_now_pixels = $this->xtr($x_now);             // Absolute coordinates
+                $x_now = $this->data[$row][$rec++];         // Read it, advance record index
 
+            $x_now_pixels = $this->xtr($x_now);
 
             if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
 
-            // Proceed with Y values
-            // Create array of points for imagefilledpolygon()
-            for($idx = 0; $rec < $this->num_recs[$row]; $rec++, $idx++) {
-                if (is_numeric($this->data[$row][$rec])) {              // Allow for missing Y data 
-                    $y_now_pixels = $this->ytr($this->data[$row][$rec]);
-
-                    $posarr[$idx][] = $x_now_pixels;
-                    $posarr[$idx][] = $y_now_pixels;
-
-                    $num_points[$idx] = isset($num_points[$idx]) ? $num_points[$idx]+1 : 1;
-                }
-                // If there's missing data...
-                else {
-                    if (isset ($incomplete_data_defaults_to_x_axis)) {
-                        $posarr[$idx][] = $x_now_pixels;
-                        $posarr[$idx][] = $this->x_axis_y_pixels;
-                        $num_points[$idx] = isset($num_points[$idx]) ? $num_points[$idx]+1 : 1;
-                    }
+            // Store the X value.
+            // There is an artificial Y value at the axis. For 'area' it goes
+            // at the end; for stackedarea it goes before the start.
+            $xd[$row] = $x_now_pixels;
+            $yd[$row] = array();
+            if ($do_stacked)
+                $yd[$row][] = $this->x_axis_y_pixels;
+
+            // Store the Y values for this X.
+            // All Y values are clipped to the x axis which should be zero but can be moved.
+            $y = 0;
+            while ($rec < $this->records_per_group) {
+                if (is_numeric($this->data[$row][$rec])) {  // Treat missing values as 0.
+                    $y += abs($this->data[$row][$rec]);
                 }
+                $yd[$row][] = $this->ytr(max($this->x_axis_position, $y));
+                if (!$do_stacked) $y = 0;
+                $rec++;
             }
-        }   // end for
-
-        $end = count($posarr);
-        for ($i = 0; $i < $end; $i++) {
-            // Prepend initial points. X = first point's X, Y = x_axis_y_pixels
-            $x = $posarr[$i][0];
-            array_unshift($posarr[$i], $x, $this->x_axis_y_pixels);
-
-            // Append final points. X = last point's X, Y = x_axis_y_pixels
-            $x = $posarr[$i][count($posarr[$i])-2];
-            array_push($posarr[$i], $x, $this->x_axis_y_pixels);
-
-            $num_points[$i] += 2;
 
-            // Draw the poligon
-            ImageFilledPolygon($this->img, $posarr[$i], $num_points[$i], $this->ndx_data_colors[$i]);
+            if (!$do_stacked)
+                $yd[$row][] = $this->x_axis_y_pixels;
         }
 
-    } // function DrawArea()
+        // Now draw the filled polygons.
+        // Note data_columns is the number of Y points (columns excluding label and X), and the
+        // number of entries in the yd[] arrays is data_columns+1.
+        $prev_row = 0;
+        for ($row = 1; $row <= $this->data_columns; $row++) { // 1 extra for X axis artificial row
+            $pts = array();
+            // Previous data set forms top (for area) or bottom (for stackedarea):
+            for ($j = 0; $j < $n; $j++) {
+                $pts[] = $xd[$j];
+                $pts[] = $yd[$j][$prev_row];
+            }
+            // Current data set forms bottom (for area) or top (for stackedarea):
+            for ($j = $n- 1; $j >= 0; $j--) {
+                $pts[] = $xd[$j];
+                $pts[] = $yd[$j][$row];
+            }
+            // Draw it:
+            ImageFilledPolygon($this->img, $pts, $n * 2, $this->ndx_data_colors[$prev_row]);
 
+            $prev_row = $row;
+        }
+        return TRUE;
+    }
 
-    /*!
-     * Draw Lines. Supported data-types:
-     *      'data-data', 
-     *      'text-data'
-     * NOTE: Please see the note regarding incomplete data sets on DrawArea()
+    /*
+     * Draw a line plot, or the lines part of a linepoints plot
+     * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...)
+     * Line plot with error bars (data-data-error format) is redirected to DrawLinesError.
+     *   $paired is true for linepoints plots, to make sure elements are only drawn once.
      */
-    function DrawLines() 
+    protected function DrawLines($paired = FALSE)
     {
-        // This will tell us if lines have already begun to be drawn.
-        // It is an array to keep separate information for every line, with a single
-        // variable we would sometimes get "undefined offset" errors and no plot...
-        $start_lines = array_fill(0, $this->records_per_group, FALSE);
+        if (!$this->CheckDataType('text-data, data-data, data-data-error'))
+            return FALSE;
+        if ($this->datatype_error_bars)
+            return $this->DrawLinesError($paired); // Redirect for lines+errorbar plot
 
-        if ($this->data_type == 'text-data') { 
-            $lastx[0] = $this->xtr(0);
-            $lasty[0] = $this->xtr(0);
-        }
+        // Flag array telling if the current point is valid, one element per plot line.
+        // If start_lines[i] is true, then (lastx[i], lasty[i]) is the previous point.
+        $start_lines = array_fill(0, $this->data_columns, FALSE);
+
+        $gcvars = array(); // For GetDataColor, which initializes and uses this.
 
         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
             $record = 1;                                    // Skip record #0 (data label)
 
-            if ($this->data_type == 'data-data')            // Do we have a value for X?
-                $x_now = $this->data[$row][$record++];      // Read it, advance record index
-            else
+            if ($this->datatype_implied)                    // Implied X values?
                 $x_now = 0.5 + $cnt++;                      // Place text-data at X = 0.5, 1.5, 2.5, etc...
+            else
+                $x_now = $this->data[$row][$record++];      // Read it, advance record index
 
             $x_now_pixels = $this->xtr($x_now);             // Absolute coordinates
 
@@ -3554,50 +5258,53 @@ class PHPlot {
                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
 
             for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
-                if (is_numeric($this->data[$row][$record])) {           //Allow for missing Y data 
+                if (($line_style = $this->line_styles[$idx]) == 'none')
+                    continue; //Allow suppressing entire line, useful with linepoints
+                if (is_numeric($this->data[$row][$record])) {           //Allow for missing Y data
+
+                    // Select the color:
+                    $this->GetDataColor($row, $idx, $gcvars, $data_color);
+
                     $y_now_pixels = $this->ytr($this->data[$row][$record]);
 
-                    if ($start_lines[$idx] == TRUE) {
+                    if ($start_lines[$idx]) {
                         // Set line width, revert it to normal at the end
                         ImageSetThickness($this->img, $this->line_widths[$idx]);
 
-                        if ($this->line_styles[$idx] == 'dashed') {
-                            $this->SetDashedStyle($this->ndx_data_colors[$idx]);
-                            ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx], 
-                                      IMG_COLOR_STYLED);
-                        } else {
-                            ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx], 
-                                      $this->ndx_data_colors[$idx]);
+                        if ($line_style == 'dashed') {
+                            $this->SetDashedStyle($data_color);
+                            $data_color = IMG_COLOR_STYLED;
                         }
-
+                        ImageLine($this->img, $x_now_pixels, $y_now_pixels,
+                                  $lastx[$idx], $lasty[$idx], $data_color);
                     }
                     $lasty[$idx] = $y_now_pixels;
                     $lastx[$idx] = $x_now_pixels;
                     $start_lines[$idx] = TRUE;
-                } 
-                // Y data missing... should we leave a blank or not?
-                else if ($this->draw_broken_lines) {
+                } elseif ($this->draw_broken_lines) {  // Y data missing, leave a gap.
                     $start_lines[$idx] = FALSE;
                 }
             }   // end for
         }   // end for
 
-        ImageSetThickness($this->img, 1);       // Revert to original state for lines to be drawn later. 
-    } // function DrawLines()
-
+        ImageSetThickness($this->img, 1);       // Revert to original state for lines to be drawn later.
+        return TRUE;
+    }
 
-    /*!
-     * Draw lines with error bars - data comes in as 
-     *      array("label", x, y, error+, error-, y2, error2+, error2-, ...);
+    /*
+     * Draw lines with error bars for an error plot of types lines and linepoints
+     * Supports only data-data-error format, with each row of the form
+     *   array("title", x, y1, error1+, error1-, y2, error2+, error2-, ...)
+     * This is called from DrawLines, with data type already checked.
+     *   $paired is true for linepoints error plots, to make sure elements are
+     *       only drawn once.  If true, data labels are drawn by DrawLinesError, and error
+     *       bars are drawn by DrawDotsError. (This choice is for backwards compatibility.)
      */
-    function DrawLinesError() 
+    protected function DrawLinesError($paired = FALSE)
     {
-        if ($this->data_type != 'data-data-error') {
-            $this->DrawError("DrawLinesError(): Data type '$this->data_type' not supported.");
-            return FALSE;
-        }
+        $start_lines = array_fill(0, $this->data_columns, FALSE);
 
-        $start_lines = array_fill(0, $this->records_per_group, FALSE);
+        $gcvars = array(); // For GetDataErrorColors, which initializes and uses this.
 
         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
             $record = 1;                                    // Skip record #0 (data label)
@@ -3605,296 +5312,622 @@ class PHPlot {
             $x_now = $this->data[$row][$record++];          // Read X value, advance record index
 
             $x_now_pixels = $this->xtr($x_now);             // Absolute coordinates.
-            
 
             if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
 
             // Now go for Y, E+, E-
             for ($idx = 0; $record < $this->num_recs[$row]; $idx++) {
-                // Y
-                $y_now = $this->data[$row][$record++];
-                $y_now_pixels = $this->ytr($y_now);
+                if (($line_style = $this->line_styles[$idx]) == 'none')
+                    continue; //Allow suppressing entire line, useful with linepoints
+                if (is_numeric($this->data[$row][$record])) {    // Allow for missing Y data
 
-                if ($start_lines[$idx] == TRUE) {
-                    ImageSetThickness($this->img, $this->line_widths[$idx]);
+                    // Select the colors:
+                    $this->GetDataErrorColors($row, $idx, $gcvars, $data_color, $error_color);
 
-                    if ($this->line_styles[$idx] == 'dashed') {
-                        $this->SetDashedStyle($this->ndx_data_colors[$idx]);
-                        ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx], 
-                                  IMG_COLOR_STYLED);
-                    } else {
-                        ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx], 
-                                  $this->ndx_data_colors[$idx]);
+                    // Y
+                    $y_now = $this->data[$row][$record++];
+                    $y_now_pixels = $this->ytr($y_now);
+
+                    if ($start_lines[$idx]) {
+                        ImageSetThickness($this->img, $this->line_widths[$idx]);
+
+                        if ($line_style == 'dashed') {
+                            $this->SetDashedStyle($data_color);
+                            $data_color = IMG_COLOR_STYLED;
+                        }
+                        ImageLine($this->img, $x_now_pixels, $y_now_pixels,
+                                  $lastx[$idx], $lasty[$idx], $data_color);
                     }
-                }
 
-                // Error+
-                $val = $this->data[$row][$record++];
-                $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape, 
-                                     $this->ndx_error_bar_colors[$idx]);
+                    if ($paired) {
+                        $record += 2; // Skip error bars - done in the 'points' part of 'linepoints'.
+                    } else {
+                        // Error+
+                        $val = $this->data[$row][$record++];
+                        $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape, $error_color);
 
-                // Error-
-                $val = $this->data[$row][$record++];
-                $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape, 
-                                     $this->ndx_error_bar_colors[$idx]);
+                        // Error-
+                        $val = $this->data[$row][$record++];
+                        $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape, $error_color);
+                    }
 
-                // Update indexes:
-                $start_lines[$idx] = TRUE;   // Tells us if we already drew the first column of points, 
+                    // Update indexes:
+                    $start_lines[$idx] = TRUE;   // Tells us if we already drew the first column of points,
                                              // thus having $lastx and $lasty ready for the next column.
-                $lastx[$idx] = $x_now_pixels;
-                $lasty[$idx] = $y_now_pixels;
-            }   // end while
+                    $lastx[$idx] = $x_now_pixels;
+                    $lasty[$idx] = $y_now_pixels;
+
+                } else {
+                    $record += 3;  // Skip over missing Y and its error values
+                    if ($this->draw_broken_lines) {
+                        $start_lines[$idx] = FALSE;
+                    }
+                }
+            }   // end for
         }   // end for
 
         ImageSetThickness($this->img, 1);   // Revert to original state for lines to be drawn later.
-    }   // function DrawLinesError()
-
+        return TRUE;
+    }
 
+    /*
+     * Draw a Lines+Points plot (linepoints).
+     * This just uses DrawLines and DrawDots. They handle the error-bar case themselves.
+     */
+    protected function DrawLinePoints()
+    {
+        // This check is redundant, as DrawLines and DrawDots do it, but left here as insurance.
+        if (!$this->CheckDataType('text-data, data-data, data-data-error'))
+            return FALSE;
+        $this->DrawLines(TRUE);
+        $this->DrawDots(TRUE);
+        return TRUE;
+    }
 
-    /*!
-     * This is a mere copy of DrawLines() with one more line drawn for each point
+    /*
+     * Draw a Squared Line plot.
+     * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...)
+     * This is based on DrawLines(), with one more line drawn for each point.
      */
-    function DrawSquared() 
+    protected function DrawSquared()
     {
-        // This will tell us if lines have already begun to be drawn.
-        // It is an array to keep separate information for every line, for with a single
-        // variable we could sometimes get "undefined offset" errors and no plot...
-        $start_lines = array_fill(0, $this->records_per_group, FALSE);
+        if (!$this->CheckDataType('text-data, data-data'))
+            return FALSE;
+
+        // Flag array telling if the current point is valid, one element per plot line.
+        // If start_lines[i] is true, then (lastx[i], lasty[i]) is the previous point.
+        $start_lines = array_fill(0, $this->data_columns, FALSE);
+
+        $gcvars = array(); // For GetDataColor, which initializes and uses this.
 
-        if ($this->data_type == 'text-data') { 
-            $lastx[0] = $this->xtr(0);
-            $lasty[0] = $this->xtr(0);
-        }
-        
         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
             $record = 1;                                    // Skip record #0 (data label)
 
-            if ($this->data_type == 'data-data')            // Do we have a value for X?
-                $x_now = $this->data[$row][$record++];      // Read it, advance record index
-            else
+            if ($this->datatype_implied)                    // Implied X values?
                 $x_now = 0.5 + $cnt++;                      // Place text-data at X = 0.5, 1.5, 2.5, etc...
+            else
+                $x_now = $this->data[$row][$record++];      // Read it, advance record index
 
             $x_now_pixels = $this->xtr($x_now);             // Absolute coordinates
 
-            if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
-                $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); // notice there is no last param.
-                
-            // Draw Lines
+            if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
+                $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); // notice there is no last param.
+
+            // Draw Lines
+            for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
+                if (is_numeric($this->data[$row][$record])) {               // Allow for missing Y data
+                    $y_now_pixels = $this->ytr($this->data[$row][$record]);
+
+                    if ($start_lines[$idx]) {
+                        // Set line width, revert it to normal at the end
+                        ImageSetThickness($this->img, $this->line_widths[$idx]);
+
+                        // Select the color:
+                        $this->GetDataColor($row, $idx, $gcvars, $data_color);
+
+                        if ($this->line_styles[$idx] == 'dashed') {
+                            $this->SetDashedStyle($data_color);
+                            $data_color = IMG_COLOR_STYLED;
+                        }
+                        ImageLine($this->img, $lastx[$idx], $lasty[$idx],
+                                  $x_now_pixels, $lasty[$idx], $data_color);
+                        ImageLine($this->img, $x_now_pixels, $lasty[$idx],
+                                  $x_now_pixels, $y_now_pixels, $data_color);
+                    }
+                    $lastx[$idx] = $x_now_pixels;
+                    $lasty[$idx] = $y_now_pixels;
+                    $start_lines[$idx] = TRUE;
+                } elseif ($this->draw_broken_lines) {  // Y data missing, leave a gap.
+                    $start_lines[$idx] = FALSE;
+                }
+            }
+        }   // end while
+
+        ImageSetThickness($this->img, 1);
+        return TRUE;
+    }
+
+    /*
+     * Draw a bar (or segment of a bar), with optional shading or border.
+     * This is used by the bar and stackedbar plots, vertical and horizontal.
+     *   $x1, $y1 : One corner of the bar.
+     *   $x2, $y2 : Other corner of the bar.
+     *   $data_color : Color index to use for the bar fill.
+     *   $alt_color : Color index to use for the shading (if shading is on), else for the border.
+     *      Note the same color is NOT used for shading and border - just the same argument.
+     *      See GetBarColors() for where these arguments come from.
+     *   $shade_top : Shade the top? (Suppressed for downward stack segments except first.)
+     *   $shade_side : Shade the right side? (Suppressed for leftward stack segments except first.)
+     *      Only one of $shade_top or $shade_side can be FALSE. Both default to TRUE.
+     */
+    protected function DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color,
+            $shade_top = TRUE, $shade_side = TRUE)
+    {
+        // Sort the points so x1,y1 is upper left and x2,y2 is lower right. This
+        // is needed in order to get the shading right, and imagerectangle may require it.
+        if ($x1 > $x2) {
+            $t = $x1; $x1 = $x2; $x2 = $t;
+        }
+        if ($y1 > $y2) {
+            $t = $y1; $y1 = $y2; $y2 = $t;
+        }
+
+        // Draw the bar
+        ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $data_color);
+
+        // Draw a shade, or a border.
+        if (($shade = $this->shading) > 0) {
+            if ($shade_top && $shade_side) {
+                $npts = 6;
+                $pts = array($x1, $y1, $x1 + $shade, $y1 - $shade, $x2 + $shade, $y1 - $shade,
+                             $x2 + $shade, $y2 - $shade, $x2, $y2, $x2, $y1);
+            } else {
+                $npts = 4;
+                if ($shade_top) { // Suppress side shading
+                    $pts = array($x1, $y1, $x1 + $shade, $y1 - $shade, $x2 + $shade, $y1 - $shade, $x2, $y1);
+                } else { // Suppress top shading
+                    $pts = array($x2, $y2, $x2, $y1, $x2 + $shade, $y1 - $shade, $x2 + $shade, $y2 - $shade);
+                }
+            }
+            ImageFilledPolygon($this->img, $pts, $npts, $alt_color);
+        } else {
+            ImageRectangle($this->img, $x1, $y1, $x2,$y2, $alt_color);
+        }
+    }
+
+    /*
+     * Get colors to use for a bar chart. There is a data color, and either a border color
+     * or a shading color (data dark color).
+     *   $row, $idx : Index arguments for the current bar.
+     *   &$vars : Variable storage. Caller makes an empty array, and this function uses it.
+     *   &$data_color : Returned result - Color index for the data (bar fill).
+     *   &$alt_color : Returned result - Color index for the shading or outline.
+     */
+    protected function GetBarColors($row, $idx, &$vars, &$data_color, &$alt_color)
+    {
+        // Initialize or extract variables:
+        if (empty($vars)) {
+            if ($this->shading > 0)    // This plot needs dark colors if shading is on.
+                $this->NeedDataDarkColors();
+            $custom_color = (bool)$this->GetCallback('data_color');
+            $num_data_colors = count($this->ndx_data_colors);
+            $num_border_colors = count($this->ndx_data_border_colors);
+            $vars = compact('custom_color', 'num_data_colors', 'num_border_colors');
+        } else {
+          extract($vars);
+        }
+
+        // Select the colors.
+        if ($custom_color) {
+            $col_i = $this->DoCallback('data_color', $row, $idx); // Custom color index
+            $i_data = $col_i % $num_data_colors; // Index for data colors and dark colors
+            $i_border = $col_i % $num_border_colors; // Index for data borders (if used)
+        } else {
+            $i_data = $i_border = $idx;
+        }
+        $data_color = $this->ndx_data_colors[$i_data];
+        if ($this->shading > 0) {
+            $alt_color = $this->ndx_data_dark_colors[$i_data];
+        } else {
+            $alt_color = $this->ndx_data_border_colors[$i_border];
+        }
+    }
+
+    /*
+     * Draw a Bar chart
+     * Supports text-data format, with each row in the form array(label, y1, y2, y3, ...)
+     * Horizontal bars (text-data-yx format) are sent to DrawHorizBars() instead.
+     */
+    protected function DrawBars()
+    {
+        if (!$this->CheckDataType('text-data, text-data-yx'))
+            return FALSE;
+        if ($this->datatype_swapped_xy)
+            return $this->DrawHorizBars();
+        $this->CalcBarWidths();
+
+        // This is the X offset from the bar group's label center point to the left side of the first bar
+        // in the group. See also CalcBarWidths above.
+        $x_first_bar = ($this->data_columns * $this->record_bar_width) / 2 - $this->bar_adjust_gap;
+
+        $gcvars = array(); // For GetBarColors, which initializes and uses this.
+
+        for ($row = 0; $row < $this->num_data_rows; $row++) {
+            $record = 1;                                    // Skip record #0 (data label)
+
+            $x_now_pixels = $this->xtr(0.5 + $row);         // Place text-data at X = 0.5, 1.5, 2.5, etc...
+
+            if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
+                $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
+
+            // Lower left X of first bar in the group:
+            $x1 = $x_now_pixels - $x_first_bar;
+
+            // Draw the bars in the group:
+            for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
+                if (is_numeric($this->data[$row][$record])) {       // Allow for missing Y data
+                    $y = $this->data[$row][$record];
+                    $x2 = $x1 + $this->actual_bar_width;
+
+                    if (($upgoing_bar = $y >= $this->x_axis_position)) {
+                        $y1 = $this->ytr($y);
+                        $y2 = $this->x_axis_y_pixels;
+                    } else {
+                        $y1 = $this->x_axis_y_pixels;
+                        $y2 = $this->ytr($y);
+                    }
+
+                    // Select the colors:
+                    $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color);
+
+                    // Draw the bar, and the shade or border:
+                    $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color);
+
+                    // Draw optional data labels above the bars (or below, for negative values).
+                    if ( $this->y_data_label_pos == 'plotin') {
+                        if ($upgoing_bar) {
+                          $v_align = 'bottom';
+                          $y_offset = -5 - $this->shading;
+                        } else {
+                          $v_align = 'top';
+                          $y_offset = 2;
+                        }
+                        $this->DrawDataValueLabel('y', $row+0.5, $y, $y, 'center', $v_align,
+                                ($idx + 0.5) * $this->record_bar_width - $x_first_bar, $y_offset);
+                    }
+                }
+                // Step to next bar in group:
+                $x1 += $this->record_bar_width;
+            }   // end for
+        }   // end for
+        return TRUE;
+    }
+
+    /*
+     * Draw a Horizontal Bar chart
+     * Supports only text-data-yx format, with each row in the form array(label, x1, x2, x3, ...)
+     * Note that the data values are X not Y, and the bars are drawn horizontally.
+     * This is called from DrawBars, which has already checked the data type.
+     */
+    protected function DrawHorizBars()
+    {
+        $this->CalcBarWidths(FALSE); // Calculate bar sizes for horizontal plots
+
+        // This is the Y offset from the bar group's label center point to the bottom of the first bar
+        // in the group. See also CalcBarWidths above.
+        $y_first_bar = ($this->data_columns * $this->record_bar_width) / 2 - $this->bar_adjust_gap;
+
+        $gcvars = array(); // For GetBarColors, which initializes and uses this.
+
+        for ($row = 0; $row < $this->num_data_rows; $row++) {
+            $record = 1;                                    // Skip record #0 (data label)
+
+            $y_now_pixels = $this->ytr(0.5 + $row);         // Place bars at Y=0.5, 1.5, 2.5, etc...
+
+            if ($this->y_data_label_pos != 'none')          // Draw Y Data Labels?
+                $this->DrawYDataLabel($this->data[$row][0], $y_now_pixels);
+
+            // Lower left Y of first bar in the group:
+            $y1 = $y_now_pixels + $y_first_bar;
+
+            // Draw the bars in the group:
             for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
-                if (is_numeric($this->data[$row][$record])) {               // Allow for missing Y data 
-                    $y_now_pixels = $this->ytr($this->data[$row][$record]);
+                if (is_numeric($this->data[$row][$record])) {       // Allow for missing X data
+                    $x = $this->data[$row][$record];
+                    $y2 = $y1 - $this->actual_bar_width;
 
-                    if ($start_lines[$idx] == TRUE) {
-                        // Set line width, revert it to normal at the end
-                        ImageSetThickness($this->img, $this->line_widths[$idx]);
+                    if (($rightwards_bar = $x >= $this->y_axis_position)) {
+                        $x1 = $this->xtr($x);
+                        $x2 = $this->y_axis_x_pixels;
+                    } else {
+                        $x1 = $this->y_axis_x_pixels;
+                        $x2 = $this->xtr($x);
+                    }
 
-                        if ($this->line_styles[$idx] == 'dashed') {
-                            $this->SetDashedStyle($this->ndx_data_colors[$idx]);
-                            ImageLine($this->img, $lastx[$idx], $lasty[$idx], $x_now_pixels, $lasty[$idx],
-                                      IMG_COLOR_STYLED);
-                            ImageLine($this->img, $x_now_pixels, $lasty[$idx], $x_now_pixels, $y_now_pixels, 
-                                      IMG_COLOR_STYLED);
+                    // Select the colors:
+                    $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color);
+
+                    // Draw the bar, and the shade or border:
+                    $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color);
+
+                    // Draw optional data labels to the right of the bars (or left, if the bar
+                    // goes left of the Y axis line).
+                    if ($this->x_data_label_pos == 'plotin') {
+                        if ($rightwards_bar) {
+                          $h_align = 'left';
+                          $x_offset = 5 + $this->shading;
                         } else {
-                            ImageLine($this->img, $lastx[$idx], $lasty[$idx], $x_now_pixels, $lasty[$idx],
-                                      $this->ndx_data_colors[$idx]);
-                            ImageLine($this->img, $x_now_pixels, $lasty[$idx], $x_now_pixels, $y_now_pixels,
-                                      $this->ndx_data_colors[$idx]);
+                          $h_align = 'right';
+                          $x_offset = -2;
                         }
+                        $this->DrawDataValueLabel('x', $x, $row+0.5, $x, $h_align, 'center',
+                                $x_offset, $y_first_bar - ($idx + 0.5) * $this->record_bar_width);
                     }
-                    $lastx[$idx] = $x_now_pixels;
-                    $lasty[$idx] = $y_now_pixels;
-                    $start_lines[$idx] = TRUE;
-                } 
-                // Y data missing... should we leave a blank or not?
-                else if ($this->draw_broken_lines) {
-                    $start_lines[$idx] = FALSE;
-                } 
-            }
-        }   // end while
 
-        ImageSetThickness($this->img, 1); 
-    } // function DrawSquared()
+                }
+                // Step to next bar in group:
+                $y1 -= $this->record_bar_width;
+            }   // end for
+        }   // end for
 
+        return TRUE;
+    }
 
-    /*!    
-     * Data comes in as array("title", x, y, y2, y3, ...)
+    /*
+     * Draw a Stacked Bar chart
+     * Supports text-data format, with each row in the form array(label, y1, y2, y3, ...)
+     * Horizontal stacked bars (text-data-yx format) are sent to DrawHorizStackedBars() instead.
+     * Original stacked bars idea by Laurent Kruk < lolok at users.sourceforge.net >
      */
-    function DrawBars()
+    protected function DrawStackedBars()
     {
-        if ($this->data_type != 'text-data') {
-            $this->DrawError('DrawBars(): Bar plots must be text-data: use function SetDataType("text-data")');
+        if (!$this->CheckDataType('text-data, text-data-yx'))
             return FALSE;
-        }
+        if ($this->datatype_swapped_xy)
+            return $this->DrawHorizStackedBars();
+        $this->CalcBarWidths();
+
+        // This is the X offset from the bar's label center point to the left side of the bar.
+        $x_first_bar = $this->record_bar_width / 2 - $this->bar_adjust_gap;
+
+        $gcvars = array(); // For GetBarColors, which initializes and uses this.
+
+        // Determine if any data labels are on:
+        $data_labels_within = ($this->y_data_label_pos == 'plotstack');
+        $data_labels_end = $data_labels_within || ($this->y_data_label_pos == 'plotin');
+        $data_label_y_offset = -5 - $this->shading; // For upward labels only.
 
         for ($row = 0; $row < $this->num_data_rows; $row++) {
             $record = 1;                                    // Skip record #0 (data label)
 
             $x_now_pixels = $this->xtr(0.5 + $row);         // Place text-data at X = 0.5, 1.5, 2.5, etc...
 
-            if ($this->x_data_label_pos != 'none')          // Draw X Data labels? TODO:labels on top of bars.
+            if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
 
-            // Draw the bar
+            // Lower left and lower right X of the bars in this stack:
+            $x1 = $x_now_pixels - $x_first_bar;
+            $x2 = $x1 + $this->actual_bar_width;
+
+            // Draw the bar segments in this stack.
+            $wy1 = 0;                       // World coordinates Y1, current sum of values
+            $wy2 = $this->x_axis_position;  // World coordinates Y2, last drawn value
+            $first = TRUE;
+
             for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
-                if (is_numeric($this->data[$row][$record])) {       // Allow for missing Y data
-                    $x1 = $x_now_pixels - $this->data_group_space + ($idx * $this->record_bar_width);
-                    $x2 = $x1 + ($this->bar_width_adjust * $this->record_bar_width);
 
-                    if ($this->data[$row][$record] < $this->x_axis_position) {
-                        $y1 = $this->x_axis_y_pixels;
-                        $y2 = $this->ytr($this->data[$row][$record]);
-                    } else {
-                        $y1 = $this->ytr($this->data[$row][$record]);
-                        $y2 = $this->x_axis_y_pixels;
-                    }
+                // Skip missing Y values, and ignore Y=0 values.
+                if (is_numeric($this->data[$row][$record])
+                    && ($this_y = $this->data[$row][$record]) != 0) {
 
-                    if ($this->shading) {                           // Draw the shade?
-                        ImageFilledPolygon($this->img, array($x1, $y1,
-                                                       $x1 + $this->shading, $y1 - $this->shading,
-                                                       $x2 + $this->shading, $y1 - $this->shading,
-                                                       $x2 + $this->shading, $y2 - $this->shading,
-                                                       $x2, $y2,
-                                                       $x2, $y1),
-                                           6, $this->ndx_data_dark_colors[$idx]);
-                    }
-                    // Or draw a border?
-                    else {
-                        ImageRectangle($this->img, $x1, $y1, $x2,$y2, $this->ndx_data_border_colors[$idx]);
+                    // First non-zero value sets the direction, $upward. Note this compares to 0,
+                    // not the axis position. Segments are based at 0 but clip to the axis.
+                    if ($first)
+                        $upward = ($this_y > 0);
+
+                    $wy1 += $this_y;    // Keep the running total for this bar stack
+
+                    // Draw nothing if this segment would not increase the bar height.
+                    // Upward bars: draw if wy1>wy2.  Downward bars: Draw if wy1<wy2.
+                    if (($wy1 < $wy2) XOR $upward) {
+
+                        $y1 = $this->ytr($wy1); // Convert to device coordinates. $y1 is outermost value.
+                        $y2 = $this->ytr($wy2); // $y2 is innermost (closest to axis).
+
+                        // Select the colors:
+                        $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color);
+
+                        // Draw the bar, and the shade or border:
+                        $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color,
+                            // Only shade the top for upward bars, or the first segment of downward bars:
+                            $upward || $first, TRUE);
+
+                        // Draw optional data label for this bar segment just inside the end.
+                        // Text value is the current Y, but position is the cumulative Y.
+                        // The label is only drawn if it fits in the segment height |y2-y1|.
+                        if ($data_labels_within) {
+                            $this->DrawDataValueLabel('y', $row+0.5, $wy1, $this_y,
+                                                      'center', $upward ? 'top' : 'bottom',
+                                                      0, $upward ? 3 : -3, NULL, abs($y1 - $y2));
+                        }
+                        // Mark the new end of the bar, conditional on segment height > 0.
+                        $wy2 = $wy1;
                     }
-                    // Draw the bar
-                    ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_colors[$idx]);
+                    $first = FALSE;
                 }
             }   // end for
-        }   // end for
-    } //function DrawBars
 
+            // Draw optional data label above the bar with the total value.
+            // Value is wy1 (total value), but position is wy2 (end of the bar stack).
+            // These differ only with wrong-direction segments, or a stack completely clipped by the axis.
+            if ($data_labels_end) {
+                $this->DrawDataValueLabel('y', $row+0.5, $wy2, $wy1, 'center', $upward ? 'bottom' : 'top',
+                                          0, $upward ? $data_label_y_offset : 5);
+            }
+        }   // end for
+        return TRUE;
+    }
 
-    /*!
-     * Data comes in as array("title", x, y, y2, y3, ...)
-     * \note Original stacked bars idea by Laurent Kruk < lolok at users.sourceforge.net >
+    /*
+     * Draw a Horizontal Stacked Bar chart
+     * Supports only text-data-yx format, with each row in the form array(label, x1, x2, x3, ...)
+     * Note that the data values are X not Y, and the bars are drawn horizontally.
+     * This is called from DrawStackedBars, which has already checked the data type.
      */
-    function DrawStackedBars()
+    protected function DrawHorizStackedBars()
     {
-        if ($this->data_type != 'text-data') {
-            $this->DrawError('DrawStackedBars(): Bar plots must be text-data: use SetDataType("text-data")');
-            return FALSE;
-        }
+        $this->CalcBarWidths(FALSE); // Calculate bar sizes for horizontal plots
+
+        // This is the Y offset from the bar's label center point to the bottom of the bar
+        $y_first_bar = $this->record_bar_width / 2 - $this->bar_adjust_gap;
+
+        $gcvars = array(); // For GetBarColors, which initializes and uses this.
+
+        // Determine if any data labels are on:
+        $data_labels_within = ($this->x_data_label_pos == 'plotstack');
+        $data_labels_end = $data_labels_within || ($this->x_data_label_pos == 'plotin');
+        $data_label_x_offset = 5 + $this->shading; // For rightward labels only
 
         for ($row = 0; $row < $this->num_data_rows; $row++) {
             $record = 1;                                    // Skip record #0 (data label)
 
-            $x_now_pixels = $this->xtr(0.5 + $row);         // Place text-data at X = 0.5, 1.5, 2.5, etc...
+            $y_now_pixels = $this->ytr(0.5 + $row);         // Place bars at Y=0.5, 1.5, 2.5, etc...
 
-            if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
-                $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
+            if ($this->y_data_label_pos != 'none')          // Draw Y Data labels?
+                $this->DrawYDataLabel($this->data[$row][0], $y_now_pixels);
+
+            // Lower left and upper left Y of the bars in this stack:
+            $y1 = $y_now_pixels + $y_first_bar;
+            $y2 = $y1 - $this->actual_bar_width;
+
+            // Draw the bar segments in this stack:
+            $wx1 = 0;                       // World coordinates X1, current sum of values
+            $wx2 = $this->y_axis_position;  // World coordinates X2, last drawn value
+            $first = TRUE;
 
-            // Draw the bars
-            $oldv = 0;
             for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
-                if (is_numeric($this->data[$row][$record])) {       // Allow for missing Y data 
-                    $x1 = $x_now_pixels - $this->data_group_space;
-                    $x2 = $x_now_pixels + $this->data_group_space; 
-
-                    $y1 = $this->ytr(abs($this->data[$row][$record]) + $oldv);
-                    $y2 = $this->ytr($this->x_axis_position + $oldv);
-                    $oldv += abs($this->data[$row][$record]);
-
-                    if ($this->shading) {                           // Draw the shade?
-                        ImageFilledPolygon($this->img, array($x1, $y1, 
-                                                       $x1 + $this->shading, $y1 - $this->shading,
-                                                       $x2 + $this->shading, $y1 - $this->shading,
-                                                       $x2 + $this->shading, $y2 - $this->shading,
-                                                       $x2, $y2,
-                                                       $x2, $y1),
-                                           6, $this->ndx_data_dark_colors[$idx]);
-                    } 
-                    // Or draw a border?
-                    else {
-                        ImageRectangle($this->img, $x1, $y1, $x2,$y2, $this->ndx_data_border_colors[$idx]);
+
+                // Skip missing X values, and ignore X<0 values.
+                if (is_numeric($this->data[$row][$record])
+                    && ($this_x = $this->data[$row][$record]) != 0) {
+
+                    // First non-zero value sets the direction, $rightward. Note this compares to 0,
+                    // not the axis position. Segments are based at 0 but clip to the axis.
+                    if ($first)
+                        $rightward = ($this_x > 0);
+
+                    $wx1 += $this_x;  // Keep the running total for this bar stack
+
+                    // Draw nothing if this segment would not increase the bar length.
+                    // Rightward bars: draw if wx1>wx2. Leftward bars: Draw if wx1<wx2.
+                    if (($wx1 < $wx2) XOR $rightward) {
+
+                        $x1 = $this->xtr($wx1); // Convert to device coordinates. $x1 is outermost value.
+                        $x2 = $this->xtr($wx2); // $x2 is innermost (closest to axis).
+
+                        // Select the colors:
+                        $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color);
+
+                        // Draw the bar, and the shade or border:
+                        $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color,
+                            // Only shade the side for rightward bars, or the first segment of leftward bars:
+                            TRUE, $rightward || $first);
+                        // Draw optional data label for this bar segment just inside the end.
+                        // Text value is the current X, but position is the cumulative X.
+                        // The label is only drawn if it fits in the segment width |x2-x1|.
+                        if ($data_labels_within) {
+                            $this->DrawDataValueLabel('x', $wx1, $row+0.5, $this_x,
+                                                      $rightward ? 'right' : 'left', 'center',
+                                                      $rightward ? -3 : 3, 0, abs($x1 - $x2), NULL);
+                        }
+                        // Mark the new end of the bar, conditional on segment width > 0.
+                        $wx2 = $wx1;
                     }
-                    // Draw the bar
-                    ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_colors[$idx]);
-                    
-                } 
+                    $first = FALSE;
+                }
             }   // end for
+
+            // Draw optional data label right of the bar with the total value.
+            // Value is wx1 (total value), but position is wx2 (end of the bar stack).
+            // These differ only with wrong-direction segments, or a stack completely clipped by the axis.
+            if ($data_labels_end) {
+                $this->DrawDataValueLabel('x', $wx2, $row+0.5, $wx1, $rightward ? 'left' : 'right', 'center',
+                                          $rightward ? $data_label_x_offset : -5, 0);
+            }
         }   // end for
-    } //function DrawStackedBars 
+        return TRUE;
+    }
 
-    
-    /*!
-     *
+    /*
+     * Draw the graph.
+     * This is the function that performs the actual drawing, after all
+     * the parameters and data are set up.
+     * It also outputs the finished image, unless told not to.
+     * Note: It is possible for this to be called multiple times.
      */
     function DrawGraph()
     {
-        if (! $this->img) {
-            $this->DrawError('DrawGraph(): No image resource allocated');
+        // Test for missing image, missing data, empty data:
+        if (!$this->CheckDataArray())
+            return FALSE; // Error message already reported.
+
+        // Allocate colors for the plot:
+        $this->SetColorIndexes();
+
+        // For pie charts: don't draw grid or border or axes, and maximize area usage.
+        // These controls can be split up in the future if needed.
+        $draw_axes = ($this->plot_type != 'pie');
+
+        // Get maxima and minima for scaling:
+        if (!$this->FindDataLimits())
             return FALSE;
-        }
 
-        if (! is_array($this->data)) {
-            $this->DrawError("DrawGraph(): No array of data in \$data");
+        // Set plot area world values (plot_max_x, etc.):
+        if (!$this->CalcPlotAreaWorld())
             return FALSE;
-        }
 
-        if (! isset($this->data_limits_done))
-            $this->FindDataLimits();                // Get maxima and minima for scaling
+        // Calculate X and Y axis positions in World Coordinates:
+        $this->CalcAxisPositions();
 
-        if ($this->total_records == 0) {            // Check for empty data sets
-            $this->DrawError('Empty data set');
-            return FALSE;
-        }
+        // Process label-related parameters:
+        $this->CheckLabels();
 
-        $this->CalcMargins();                       // Calculate margins
+        // Apply grid defaults:
+        $this->CalcGridSettings();
 
-        if (! isset($this->plot_area_width))        // Set plot area pixel values (plot_area[])
-            $this->SetPlotAreaPixels();
+        // Calculate the plot margins, if needed.
+        // For pie charts, set the $maximize argument to maximize space usage.
+        $this->CalcMargins(!$draw_axes);
 
-        if (! isset($this->plot_max_y))             // Set plot area world values (plot_max_x, etc.)
-            $this->SetPlotAreaWorld();
+        // Calculate the actual plot area in device coordinates:
+        $this->CalcPlotAreaPixels();
 
-        if ($this->plot_type == 'bars' || $this->plot_type == 'stackedbars') // Calculate bar widths
-            $this->CalcBarWidths();
-/* FIXME!!  this sort of thing should not be done without user's consent
-        if ($this->x_data_label_pos != 'none') {    // Default: do not draw tick stuff if
-            $this->x_tick_label_pos = 'none';       // there are data labels.
-            $this->x_tick_pos = 'none';
-        }
-*/
-        $this->PadArrays();                         // Pad color and style arrays to fit records per group.
+        // Calculate the mapping between world and device coordinates:
+        $this->CalcTranslation();
 
-        $this->DrawBackground();
+        // Pad color and style arrays to fit records per group:
+        $this->PadArrays();
+        $this->DoCallback('draw_setup');
 
+        $this->DrawBackground();
         $this->DrawImageBorder();
+        $this->DoCallback('draw_image_background');
 
         $this->DrawPlotAreaBackground();
+        $this->DoCallback('draw_plotarea_background', $this->plot_area);
 
         $this->DrawTitle();
-        $this->DrawXTitle();
-        $this->DrawYTitle();
-
-        // Pie charts are drawn differently, handle them first
-        if ($this->plot_type == 'pie') {
-            // Pie charts can maximize image space usage.
-            $this->SetPlotAreaPixels($this->safe_margin, $this->title_height,
-                                     $this->image_width - $this->safe_margin,
-                                     $this->image_height - $this->safe_margin);
-            $this->DrawPieChart();
-
-            if ($this->legend)
-                $this->DrawLegend($this->legend_x_pos, $this->legend_y_pos, '');
-
-            if ($this->print_image)
-                $this->PrintImage();
-                
-            return;
+        if ($draw_axes) {  // If no axes (pie chart), no axis titles either
+            $this->DrawXTitle();
+            $this->DrawYTitle();
         }
+        $this->DoCallback('draw_titles');
 
-        ////// All other chart types:
-        
-        if (! $this->grid_at_foreground) {         // Usually one wants grids to go back, but...
-            $this->DrawYAxis();     // Y axis must be drawn before X axis (see DrawYAxis()) 
+        if ($draw_axes && ! $this->grid_at_foreground) {   // Usually one wants grids to go back, but...
+            $this->DrawYAxis();     // Y axis must be drawn before X axis (see DrawYAxis())
             $this->DrawXAxis();
+            $this->DoCallback('draw_axes');
         }
 
         switch ($this->plot_type) {
@@ -3908,277 +5941,249 @@ class PHPlot {
             $this->DrawSquared();
             break;
         case 'lines':
-            if ( $this->data_type == 'data-data-error') {
-                $this->DrawLinesError();
-            } else {
-                $this->DrawLines();
-            }
+            $this->DrawLines();
             break;
-        case 'linepoints':          // FIXME !!! DrawXDataLabel gets called in DrawLines() and DrawDots()
-            if ( $this->data_type == 'data-data-error') {
-                $this->DrawLinesError();
-                $this->DrawDotsError();
-            } else {
-                $this->DrawLines();
-                $this->DrawDots();
-            }
+        case 'linepoints':
+            $this->DrawLinePoints();
             break;
         case 'points';
-            if ( $this->data_type == 'data-data-error') {
-                $this->DrawDotsError();
-            } else {
-                $this->DrawDots();
-            }
+            $this->DrawDots();
+            break;
+        case 'pie':
+            $this->DrawPieChart();
             break;
         case 'stackedbars':
             $this->DrawStackedBars();
-            break; 
-        case 'bars':
-            $this->DrawBars();
             break;
+        case 'stackedarea':
+            $this->DrawArea(TRUE);
+            break;
+        // case 'bars':
         default:
-            $this->plot_type = 'bars';  // Set it if it wasn't already set.
             $this->DrawBars();
             break;
         }   // end switch
+        $this->DoCallback('draw_graph', $this->plot_area);
 
-        if ($this->grid_at_foreground) {         // Usually one wants grids to go back, but...
-            $this->DrawYAxis();     // Y axis must be drawn before X axis (see DrawYAxis()) 
+        if ($draw_axes && $this->grid_at_foreground) {   // Usually one wants grids to go back, but...
+            $this->DrawYAxis();     // Y axis must be drawn before X axis (see DrawYAxis())
             $this->DrawXAxis();
+            $this->DoCallback('draw_axes');
         }
 
-        $this->DrawPlotBorder();
-        
-        if ($this->legend)
-            $this->DrawLegend($this->legend_x_pos, $this->legend_y_pos, '');
+        if ($draw_axes) {
+            $this->DrawPlotBorder();
+            $this->DoCallback('draw_border');
+        }
 
-        if ($this->print_image)
-            $this->PrintImage();
+        if ($this->legend) {
+            $this->DrawLegend();
+            $this->DoCallback('draw_legend');
+        }
+        $this->DoCallback('draw_all', $this->plot_area);
+
+        if ($this->print_image && !$this->PrintImage())
+            return FALSE;
 
-    } //function DrawGraph()
+        return TRUE;
+    }
 
 /////////////////////////////////////////////
 //////////////////         DEPRECATED METHODS
 /////////////////////////////////////////////
 
-    /*!
+    /*
+     * Note on deprecated methods - as these pre-date the PHPlot Reference
+     * Manual, and there is minimal documentation about them, I have neither
+     * removed them nor changed them. They are not tested or documented, and
+     * should not be used.
+     */
+
+    /*
      * Deprecated, use SetYTickPos()
      */
-    function SetDrawVertTicks($which_dvt) 
+    function SetDrawVertTicks($which_dvt)
     {
         if ($which_dvt != 1)
             $this->SetYTickPos('none');
         return TRUE;
-    } 
+    }
 
-    /*!
+    /*
      * Deprecated, use SetXTickPos()
      */
-    function SetDrawHorizTicks($which_dht) 
+    function SetDrawHorizTicks($which_dht)
     {
         if ($which_dht != 1)
            $this->SetXTickPos('none');
         return TRUE;
     }
 
-    /*!
-     * \deprecated Use SetNumXTicks()
+    /*
+     * Deprecated - use SetNumXTicks()
      */
-    function SetNumHorizTicks($n) 
+    function SetNumHorizTicks($n)
     {
         return $this->SetNumXTicks($n);
     }
 
-    /*!
-     * \deprecated Use SetNumYTicks()
+    /*
+     * Deprecated - use SetNumYTicks()
      */
-    function SetNumVertTicks($n) 
+    function SetNumVertTicks($n)
     {
         return $this->SetNumYTicks($n);
     }
 
-    /*!
-     * \deprecated Use SetXTickIncrement()
+    /*
+     * Deprecated - use SetXTickIncrement()
      */
-    function SetHorizTickIncrement($inc) 
+    function SetHorizTickIncrement($inc)
     {
         return $this->SetXTickIncrement($inc);
     }
 
-
-    /*!
-     * \deprecated Use SetYTickIncrement()
+    /*
+     * Deprecated - use SetYTickIncrement()
      */
-    function SetVertTickIncrement($inc) 
+    function SetVertTickIncrement($inc)
     {
         return $this->SetYTickIncrement($inc);
     }
 
-    /*!
-     * \deprecated Use SetYTickPos()
+    /*
+     * Deprecated - use SetYTickPos()
      */
-    function SetVertTickPosition($which_tp) 
-    { 
-        return $this->SetYTickPos($which_tp); 
+    function SetVertTickPosition($which_tp)
+    {
+        return $this->SetYTickPos($which_tp);
     }
 
-    /*!
-     * \deprecated Use SetXTickPos()
+    /*
+     * Deprecated - use SetXTickPos()
      */
-    function SetHorizTickPosition($which_tp) 
-    { 
+    function SetHorizTickPosition($which_tp)
+    {
         return $this->SetXTickPos($which_tp);
     }
 
-    /*!
-     * \deprecated Use SetFont()
+    /*
+     * Deprecated - use SetFont()
      */
-    function SetTitleFontSize($which_size) 
+    function SetTitleFontSize($which_size)
     {
         return $this->SetFont('title', $which_size);
     }
 
-    /*!
-     * \deprecated Use SetFont()
+    /*
+     * Deprecated - use SetFont()
      */
-    function SetAxisFontSize($which_size) 
+    function SetAxisFontSize($which_size)
     {
         $this->SetFont('x_label', $which_size);
-        $this->SetFont('y_label', $whic_size);
+        $this->SetFont('y_label', $which_size);
     }
 
-    /*!
-     * \deprecated Use SetFont()
+    /*
+     * Deprecated - use SetFont()
      */
-    function SetSmallFontSize($which_size) 
+    function SetSmallFontSize($which_size)
     {
         return $this->SetFont('generic', $which_size);
     }
 
-    /*!
-     * \deprecated Use SetFont()
+    /*
+     * Deprecated - use SetFont()
      */
     function SetXLabelFontSize($which_size)
     {
         return $this->SetFont('x_title', $which_size);
     }
 
-    /*!
-     * \deprecated Use SetFont()
+    /*
+     * Deprecated - use SetFont()
      */
-    function SetYLabelFontSize($which_size) 
+    function SetYLabelFontSize($which_size)
     {
         return $this->SetFont('y_title', $which_size);
     }
 
-    /*!
-     * \deprecated Use SetXTitle()
+    /*
+     * Deprecated - use SetXTitle()
      */
-    function SetXLabel($which_xlab) 
+    function SetXLabel($which_xlab)
     {
         return $this->SetXTitle($which_xlab);
     }
 
-    /*!
-     * \deprecated Use SetYTitle()
-     */ 
-    function SetYLabel($which_ylab) 
-    {
-        return $this->SetYTitle($which_ylab);
-    }   
-
-    /*!
-     * \deprecated This is now an Internal function - please set width and 
-     *             height via PHPlot() upon object construction
+    /*
+     * Deprecated - use SetYTitle()
      */
-    function SetImageArea($which_iw, $which_ih) 
+    function SetYLabel($which_ylab)
     {
-        $this->image_width = $which_iw;
-        $this->image_height = $which_ih;
-
-        return TRUE;
+        return $this->SetYTitle($which_ylab);
     }
 
-    /*!
-     * \deprecated Use SetXTickLength() and SetYTickLength() instead.
+    /*
+     * Deprecated - use SetXTickLength() and SetYTickLength() instead.
      */
-    function SetTickLength($which_tl) 
+    function SetTickLength($which_tl)
     {
         $this->SetXTickLength($which_tl);
         $this->SetYTickLength($which_tl);
         return TRUE;
     }
 
-    /*!
-     * \deprecated  Use SetYLabelType()
+    /*
+     * Deprecated - use SetYLabelType()
      */
-    function SetYGridLabelType($which_yglt) 
+    function SetYGridLabelType($which_yglt)
     {
         return $this->SetYLabelType($which_yglt);
     }
 
-    /*!
-     * \deprecated  Use SetXLabelType()
+    /*
+     * Deprecated - use SetXLabelType()
      */
-    function SetXGridLabelType($which_xglt) 
+    function SetXGridLabelType($which_xglt)
     {
         return $this->SetXLabelType($which_xglt);
     }
-    /*!
-     * \deprecated Use SetYTickLabelPos()
+    /*
+     * Deprecated - use SetYTickLabelPos()
      */
-    function SetYGridLabelPos($which_yglp) 
+    function SetYGridLabelPos($which_yglp)
     {
         return $this->SetYTickLabelPos($which_yglp);
     }
-    /*!
-     * \deprecated Use SetXTickLabelPos()
+    /*
+     * Deprecated - use SetXTickLabelPos()
      */
-    function SetXGridLabelPos($which_xglp) 
+    function SetXGridLabelPos($which_xglp)
     {
         return $this->SetXTickLabelPos($which_xglp);
     }
 
-
-    /*!
-     * \deprecated Use SetXtitle()
+    /*
+     * Deprecated - use SetXtitle()
      */
-    function SetXTitlePos($xpos) 
+    function SetXTitlePos($xpos)
     {
         $this->x_title_pos = $xpos;
         return TRUE;
     }
 
-    /*!
-     * \deprecated Use SetYTitle()
+    /*
+     * Deprecated - use SetYTitle()
      */
-    function SetYTitlePos($xpos) 
+    function SetYTitlePos($xpos)
     {
         $this->y_title_pos = $xpos;
         return TRUE;
     }
 
-    /*!
-     * \deprecated  Use DrawDots()
-     */
-    function DrawDotSeries() 
-    {
-        $this->DrawDots();
-    }
-
-    /*!
-     * \deprecated Use SetXLabelAngle()
-     */
-    function SetXDataLabelAngle($which_xdla)
-    {
-        return $this->SetXLabelAngle($which_xdla);
-    }
-
-    /*!
-     * Draw Labels (not grid labels) on X Axis, following data points. Default position is 
-     * down of plot. Care must be taken not to draw these and x_tick_labels as they'd probably overlap.
-     *
-     * \deprecated Use SetXDataLabelPos()
+    /*
+     * Deprecated - use SetXDataLabelPos()
      */
     function SetDrawXDataLabels($which_dxdl)
     {
@@ -4188,114 +6193,16 @@ class PHPlot {
             $this->SetXDataLabelPos('none');
     }
 
-    /*!
-     * \deprecated This method was intended to improve performance by being specially 
-     * written for 'data-data'. However, the improvement didn't pay. Use DrawLines() instead
-     */
-    function DrawLineSeries() 
-    {
-        return $this->DrawLines();
-    }
-
-    /*!
-     * \deprecated Calculates maximum X-Axis label height. Now inside CalcMargins()
-     */
-    function CalcXHeights() 
-    {
-        // TTF
-        if ($this->use_ttf) {
-            $xstr = str_repeat('.', $this->max_t);
-            $size = $this->TTFBBoxSize($this->x_label_font['size'], $this->x_label_angle,
-                                       $this->x_label_font['font'], $xstr);
-            $this->x_tick_label_height = $size[1];
-        } 
-        // Fixed font
-        else { // For Non-TTF fonts we can have only angles 0 or 90
-            if ($this->x_label_angle == 90)
-                $this->x_tick_label_height = $this->max_t * $this->x_label_font['width'];
-            else
-                $this->x_tick_label_height = $this->x_label_font['height'];
-        }
-
-        return TRUE;
-    }
-
-
-    /*!
-     * \deprecated Calculates Maximum Y-Axis tick label width. Now inside CalcMargins()
-     */
-    function CalcYWidths() 
-    {
-        //the "." is for space. It isn't actually printed
-        $ylab = number_format($this->max_y, $this->y_precision, '.', ', ') . $this->data_units_text . '.';
-
-        // TTF
-        if ($this->use_ttf) {
-            // Maximum Y tick label width
-            $size = $this->TTFBBoxSize($this->y_label_font['size'], 0, $this->y_label_font['font'], $ylab);
-            $this->y_tick_label_width = $size[0];
-
-        } 
-        // Fixed font
-        else {
-            // Y axis title width
-            $this->y_tick_label_width = strlen($ylab) * $this->y_label_font['width'];
-        }
-
-        return TRUE;
-    }
-
-    /*!
-     * \deprecated Superfluous.
-     */
-    function DrawLabels() 
-    {
-        $this->DrawTitle();
-        $this->DrawXTitle();
-        $this->DrawYTitle();
-    }
-
-    /*! 
-     * Set up the image resource 'img'
-     * \deprecated The constructor should init 'img'
-     */
-    function InitImage()
-    {
-        $this->img = ImageCreate($this->image_width, $this->image_height);
-
-        if (! $this->img)
-            $this->PrintError('InitImage(): Could not create image resource');
-        return TRUE;
-    }
-
-    /*!
-     * \deprecated
-     */
-    function SetNewPlotAreaPixels($x1, $y1, $x2, $y2) 
-    {
-        //Like in GD 0, 0 is upper left set via pixel Coordinates
-        $this->plot_area = array($x1, $y1, $x2, $y2);
-        $this->plot_area_width = $this->plot_area[2] - $this->plot_area[0];
-        $this->plot_area_height = $this->plot_area[3] - $this->plot_area[1];
-        $this->y_top_margin = $this->plot_area[1];
-
-        if (isset($this->plot_max_x))
-            $this->CalcTranslation();
-
-        return TRUE;
-    }
-
-    /*!
-     * \deprecated Use _SetRGBColor()
+    /*
+     * Deprecated - use SetPlotAreaPixels()
      */
-    function SetColor($which_color)
+    function SetNewPlotAreaPixels($x1, $y1, $x2, $y2)
     {
-        $this->SetRGBColor($which_color);
-        return TRUE;
+        return $this->SetPlotAreaPixels($x1, $y1, $x2, $y2);
     }
 
     /*
-     * \deprecated Use SetLineWidths().
+     * Deprecated - use SetLineWidths().
      */
     function SetLineWidth($which_lw)
     {
@@ -4308,79 +6215,8 @@ class PHPlot {
         return TRUE;
     }
 
-    /*!
-     * \deprecated
-     */
-    function DrawDashedLine($x1, $y1, $x2, $y2 , $dash_length, $dash_space, $color)
-    {
-        if ($dash_length)
-            $dashes = array_fill(0, $dash_length, $color);
-        else
-            $dashes = array();
-        if ($dash_space)
-            $spaces = array_fill(0, $dash_space, IMG_COLOR_TRANSPARENT);
-        else
-            $spaces = array();
-
-        $style = array_merge($dashes, $spaces);
-        ImageSetStyle($this->img, $style);
-        ImageLine($this->img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED);
-    }
-
-    /*!
-     * \deprecated Selects an input file to be used as background for the whole graph.
-     * This resizes the graph to the image's size.
-     */
-    function SetInputFile($which_input_file)
-    {
-        $size = GetImageSize($which_input_file);
-        $input_type = $size[2];
-
-        switch($input_type) {
-        case 1:
-            $im = @ ImageCreateFromGIF ($which_input_file);
-            if (!$im) { // See if it failed
-                $this->PrintError("Unable to open $which_input_file as a GIF");
-                return FALSE;
-            }
-        break;
-        case 3:
-            $im = @ ImageCreateFromPNG ($which_input_file);
-            if (!$im) { // See if it failed
-                $this->PrintError("Unable to open $which_input_file as a PNG");
-                return FALSE;
-            }
-        break;
-        case 2:
-            $im = @ ImageCreateFromJPEG ($which_input_file);
-            if (!$im) { // See if it failed
-                $this->PrintError("Unable to open $which_input_file as a JPG");
-                return FALSE;
-            }
-        break;
-        default:
-            $this->PrintError('SetInputFile(): Please select gif, jpg, or png for image type!');
-            return FALSE;
-        break;
-        }
-
-        // Set Width and Height of Image
-        $this->image_width = $size[0];
-        $this->image_height = $size[1];
-
-        // Deallocate any resources previously allocated
-        if ($this->img)
-            imagedestroy($this->img);
-
-        $this->img = $im;
-
-        return TRUE;
-
-    }
-
-
     /*
-     * \deprecated Use SetPointShapes().
+     * Deprecated - use SetPointShapes().
      */
     function SetPointShape($which_pt)
     {
@@ -4389,60 +6225,51 @@ class PHPlot {
     }
 
     /*
-     * \deprecated Use SetPointSizes().
+     * Deprecated - use SetPointSizes().
      */
     function SetPointSize($which_ps)
     {
         $this->SetPointSizes($which_ps);
         return TRUE;
     }
-}  // class PHPlot
-
-
-
-////////////////////////
-
-
-/*!
- * Pads an array with another or with itself.
- *  \param arr array  Original array (reference)
- *  \param size int   Size of the resulting array.
- *  \param arr2 array If specified, array to use for padding. If unspecified, pad with $arr.
- */
-function array_pad_array(&$arr, $size, $arr2=NULL)
-{
-    if (! is_array($arr2)) {
-        $arr2 = $arr;                           // copy the original array
-    }
-    while (count($arr) < $size)
-        $arr = array_merge_php4($arr, $arr2);        // append until done
 }
 
-/*!
- * Fixes problem with array_merge() in PHP5.
- * \note I simply copied this from a bug report. I am not running php5 yet, so
- *       I cannot reproduce it, which is why I trust the reporter.
+/*
+ * The PHPlot_truecolor class extends PHPlot to use GD truecolor images.
  */
-function array_merge_php4($array1,$array2)
+
+class PHPlot_truecolor extends PHPlot
 {
-    $return=array();
+    /*
+     * PHPlot Truecolor variation constructor: Create a PHPlot_truecolor object and initialize it.
+     * Note this does NOT call the parent (PHPlot) constructor. It duplicates the code here.
+     * Everything is the same as the PHPlot constructor except for imagecreatetruecolor.
+     *
+     * Parameters are the same as PHPlot:
+     *   $which_width : Image width in pixels.
+     *   $which_height : Image height in pixels.
+     *   $which_output_file : Filename for output.
+     *   $which_input_file : Path to a file to be used as background.
+     */
+    function __construct($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL)
+    {
+        $this->SetRGBArray($this->color_array);
 
-    foreach(func_get_args() as $arg){
-        if(!is_array($arg)){
-        $arg=array($arg);
-        }
-        foreach($arg as $key=>$val){
-            if(!is_int($key)){
-                $return[$key]=$val;
-            }else{
-                $return[]=$val;
-            }
-        }
-    }
-    return $return;
- }
+        if ($which_output_file)
+            $this->SetOutputFile($which_output_file);
+
+        if ($which_input_file) {
+            $this->SetInputFile($which_input_file);
+        } else {
+            $this->image_width = $which_width;
+            $this->image_height = $which_height;
 
+            $this->img = imagecreatetruecolor($this->image_width, $this->image_height);
+            if (! $this->img)
+                return $this->PrintError('PHPlot_truecolor(): Could not create image resource.');
+        }
 
-?>
+        $this->SetDefaultStyles();
+        $this->SetDefaultFonts();
+    }
+}
diff --git a/gui/bacula-web/external_packages/phplot/phplot_data.php b/gui/bacula-web/external_packages/phplot/phplot_data.php
deleted file mode 100644 (file)
index 3735150..0000000
+++ /dev/null
@@ -1,258 +0,0 @@
-<?php
-/* $Id$
- * 
- * Copyright (C) 2000 Afan Ottenheimer.  Released under
- * the GPL and PHP licenses as stated in the the README file which
- * should have been included with this document.
-
- * This is an subclass for phplot.php and should only be
- * called after phplot.ini has been called. This extends
- * phplot by adding additional routines that can be used
- * to modify the data arrays.
- *
- * Data must be a *numerical* array, this is enforced in SetDataValues() 
- */
-
-require_once("phplot.php");
-
-class PHPlot_Data extends PHPlot 
-{
-    /*!
-     * Constructor
-     */
-    function PHPlot_Data($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL)
-    { 
-        if (! isset($this->img)) { 
-            $this->PHPlot($which_width, $which_height, $which_output_file, $which_input_file);
-        }
-    }
-    
-    /*!
-     * Will scale all data rows
-     * Maybe later I will do a function that only scales some rows
-     * if $even is TRUE, data will be scaled with "even" factors. 
-     * \note Original code by Thiemo Nagel
-     */
-    function DoScaleData($even, $show_in_legend) 
-    {
-        $offset = 0;        // We use this not to read labels in text-data
-            
-        if ($this->data_type == 'text-data') {
-            $offset = 1;
-        } elseif ($this->data_type != 'data-data') {
-            $this->DrawError('wrong data type!!');
-            return FALSE;
-        }
-
-        // Determine maxima for each data row in array $max
-        // Put maximum of the maxima in $maxmax
-        $maxmax = 0;
-        for($i=0; $i < $this->num_data_rows; $i++) {
-            $rowsize = count($this->data[$i]);
-            for ($j=$offset; $j < $rowsize; $j++) {
-                if ($this->data[$i][$j] > @ $max[$j])
-                    $max[$j] = $this->data[$i][$j];
-                if (@ $max[$j] > $maxmax) 
-                    $maxmax = $max[$j];
-            }
-        }
-        
-        // determine amplification factor $amplify
-        $end = count($max) + $offset;
-        for ($i=$offset; $i < $end; $i++) {
-            if ($max[$i] == 0 || $max[$i] == $maxmax) {
-                $amplify[$i] = 1;  // no divide by zero
-            } else {
-                if ($even) {
-                    $amp = pow(10,round(log10($maxmax / $max[$i]))-1);
-                    if ($amp * $max[$i] * 5 < $maxmax) {
-                        $amp *= 5;
-                    } elseif ($amp * $max[$i] * 2 < $maxmax) {
-                        $amp *= 2;
-                    }
-                } else {
-                    $amp = $maxmax / $max[$i];
-                    $digits = floor(log10($amp));
-                    $amp = round($amp/pow(10,$digits-1))*pow(10,$digits-1);
-                }
-                $amplify[$i] = $amp;
-            }
-            if ($amplify[$i] != 1 && $show_in_legend) 
-                @ $this->legend[$i] .= "*$amplify[$i]";
-        }
-
-        // Amplify data
-        // On my machine, running 1000 iterations over 1000 rows of 12 elements each,
-        // the for loops were 43.2% faster (MBD)
-        for ($i = 0; $i < $this->num_data_rows; $i++) {
-            $rowsize = count($this->data[$i]);
-            for ($j=$offset; $j < $rowsize; $j++) {
-                $this->data[$i][$j] *= $amplify[$j];
-            }
-        }
-
-        //Re-Scale Vertical Ticks if not already set
-        if ( ! $this->y_tick_increment) {
-            $this->SetYTickIncrement() ;
-        }
-
-        return TRUE;
-    } //function DoScaleData
-
-
-    /*!
-     * Computes a moving average of strength $interval for
-     * data row number $datarow, where 0 denotes the first
-     * row of y-data. 
-     *
-     *  \param int    datarow  Index of the row whereupon to make calculations
-     *  \param int    interval Number of elements to use in average ("strength")
-     *  \param bool   show     Whether to tell about the moving average in the legend.
-     *  \param string color    Color for the line to be drawn. This color is darkened. 
-     *                         Can be named or #RRGGBB.
-     *  \param int    width    Width of the line to be drawn.
-     *
-     *  \note Original idea by Theimo Nagel
-     */
-    function DoMovingAverage($datarow, $interval, $show=TRUE, $color=NULL, $width=NULL)
-    {
-        $off = 1;               // Skip record #0 (data label) 
-        
-        $this->PadArrays();
-        
-        if ($interval == 0) {
-            $this->DrawError('DoMovingAverage(): interval can\'t be 0');
-            return FALSE;
-        }
-
-        if ($datarow >= $this->records_per_group) {
-            $this->DrawError("DoMovingAverage(): Data row out of bounds ($datarow >= $this->records_per_group)");
-            return FALSE;
-        }
-        
-        if ($this->data_type == 'text-data') {
-            // Ok. No need to set the offset to skip more records.
-        } elseif ($this->data_type == 'data-data') {
-            $off++;             // first Y value at $data[][2]
-        } else {
-            $this->DrawError('DoMovingAverage(): wrong data type!!');
-            return FALSE;
-        }
-        
-        // Set color:
-        if ($color) {
-            array_push($this->ndx_data_colors, $this->SetIndexDarkColor($color));
-        } else {
-            array_push($this->ndx_data_colors, $this->SetIndexDarkColor($this->data_colors[$datarow]));
-        }
-        // Set line width:
-        if ($width) {
-            array_push($this->line_widths, $width);
-        } else {    
-            array_push($this->line_widths,  $this->line_widths[$datarow] * 2);
-        }
-        // Show in legend?
-        if ($show) {
-            $this->legend[$this->records_per_group-1] = "(MA[$datarow]:$interval)";
-        }
-
-        $datarow += $off;
-        for ($i = 0; $i < $this->num_data_rows; $i++) {
-            $storage[$i % $interval] = @ $this->data[$i][$datarow];
-            $ma = array_sum($storage);
-            $ma /= count($storage);
-            array_push($this->data[$i], $ma);   // Push the data onto the array
-            $this->num_recs[$i]++;              // Tell the drawing functions it is there
-        }
-        $this->records_per_group++;
-//        $this->FindDataLimits();
-        return TRUE;
-    } //function DoMovingAverage()
-
-
-    /**
-     * Computes an exponentially smoothed moving average.
-     * @param int perc "smoothing percentage"
-     * FIXME!!! I haven't checked this.
-     */
-    function DoExponentialMovingAverage($datarow, $perc, $show_in_legend)
-    {
-        if ($this->data_type == 'text-data') {
-            $datarow++;
-        } elseif ($this->data_type != 'data-data') {
-            $this->DrawError('DoWeightedMovingAverage(): wrong data type!!');
-            return FALSE;
-        }
-        
-        if ($show_in_legend) {
-            $this->legend[$datarow] .= " (MA: $interval)";
-        }
-
-        $storage[0] = $this->data[0][$datarow];
-        for ($i=1;$i < $this->num_data_rows; $i++) {
-            $storage[$i] = @ $storage[$i-1] + $perc * ($this->data[$i][$datarow] - $storage[$i-1]);
-            $ma = array_sum($storage);
-            $ma /= count($storage);
-            $this->data[$i][$datarow] = $ma;
-        }
-        return TRUE;
-    } // function DoExponentialMovingAverage()
-
-    
-    /*!
-     * Removes the DataSet of number $index
-     */
-    function DoRemoveDataSet($index) 
-    {
-        $offset = 1;
-        if ($this->data_type == 'data-data') {
-            $offset++;
-        } elseif ($this->data_type != 'text-data') {
-            $this->DrawError('wrong data type!!');
-            return FALSE;
-        }
-    
-        $index += $offset;
-        foreach ($this->data as $key=>$val) {
-            foreach ($val as $key2=>$val2) {
-                if ($key2 >= $index) {
-                    if (isset($this->data[$key][$key2+1])) {
-                        $this->data[$key][$key2] = $this->data[$key][$key2+1];
-                    } else {
-                        unset($this->data[$key][$key2]);
-                    }
-                }
-            }
-        }
-    } // function DoRemoveDataSet
-    
-    
-    /*!
-     * Computes row x divided by row y, stores the result in row x
-     * and deletes row y
-     */
-    function DoDivision($x,$y) 
-    {
-        $offset = 1;
-        if ($this->data_type == 'data-data') {
-            $offset++;
-        } elseif ($this->data_type != 'text-data') {
-            $this->DrawError('wrong data type!!');
-            return FALSE;
-        }
-    
-        $x += $offset; $y += $offset;
-        reset($this->data);
-        while (list($key, $val) = each($this->data)) {
-            if ($this->data[$key][$y] == 0) {
-                $this->data[$key][$x] = 0;
-            } else {
-                $this->data[$key][$x] /= $this->data[$key][$y];
-            }
-        }
-    
-        $this->DoRemoveDataSet($y-$offset);
-    } // function DoDivision
-
-} // class PHPlot_Data extends PHPlot
-?>
index 3353e28e50e43b8fb975f46c8da042bcb177007f..41256ee25dfb6be108af1bc2ca8a7b6dea2f3027 100644 (file)
@@ -1,7 +1,16 @@
 <?php
-//Looking forward to the day when memory is so
-//plentiful to be able to set gigantic arrays,
-//we offer this taken from PHPLOT. 
+/* $Id: rgb.inc.php,v 1.2 2009/06/12 01:58:35 lbayuk Exp $ */
+/*
+ * This is a large color map which can be used by PHPlot via
+ *     $plot->SetRGBArray('large')
+ * For more information on PHPlot see http://sourceforge.net/projects/phplot/
+ *
+ * rgb.inc.php comes with PHPlot but is derived from the X11 rgb.txt color
+ * database file, which contains no specific copyright notice. It may be
+ * covered by X.Org, XFree86, or MIT/X11 copyright and license, all of which
+ * allow redistribution on terms which are less strict than the LGPL which
+ * covers PHPlot.
+ */
 $ColorArray = array(
        "snow"                  => array(255, 250, 250),
        "ghost white"   => array(248, 248, 255),
@@ -741,4 +750,3 @@ $ColorArray = array(
        "grey99"                        => array(252, 252, 252),
        "gray100"                       => array(255, 255, 255)
 );
-?>