From 4db6f8ad9d03e152ab344dcaeb3c5e6d333a36df Mon Sep 17 00:00:00 2001 From: Davide Franco Date: Wed, 1 Dec 2010 13:15:04 +0100 Subject: [PATCH] bacula-web: Upgraded phplot to version 5.2.0 --- .../external_packages/phplot/COPYING | 504 ++ .../external_packages/phplot/ChangeLog | 2849 +++---- .../external_packages/phplot/LICENSE.GPL | 340 - .../external_packages/phplot/LICENSE.PHP_3_0 | 68 - .../external_packages/phplot/NEWS.txt | 975 +++ .../external_packages/phplot/README | 45 - .../external_packages/phplot/README.txt | 163 + .../phplot/contrib/README.txt | 31 + .../phplot/contrib/color_range.example.php | 35 + .../phplot/contrib/color_range.php | 103 + .../phplot/contrib/color_range.test1.php | 55 + .../phplot/contrib/color_range.test2.php | 73 + .../phplot/contrib/prune_labels.example.php | 31 + .../phplot/contrib/prune_labels.php | 36 + .../phplot/contrib/prune_labels.test.php | 41 + .../phplot/doc/imgs/graph1.png | Bin 35562 -> 0 bytes .../phplot/doc/imgs/graph2.png | Bin 16749 -> 0 bytes .../phplot/doc/imgs/graph3.png | Bin 11392 -> 0 bytes .../phplot/doc/imgs/phplot-dia.png | Bin 47604 -> 0 bytes .../phplot/doc/imgs/qstart_fig1.png | Bin 1800 -> 0 bytes .../phplot/doc/imgs/qstart_fig2.png | Bin 2023 -> 0 bytes .../phplot/doc/imgs/qstart_fig3.png | Bin 2630 -> 0 bytes .../phplot/doc/imgs/qstart_fig4.png | Bin 2602 -> 0 bytes .../external_packages/phplot/doc/index.php | 152 - .../phplot/doc/internal_functions.html | 269 - .../phplot/doc/quickstart.html | 455 - .../external_packages/phplot/doc/schema.html | 35 - .../external_packages/phplot/doc/style.css | 42 - .../phplot/doc/user_functions.html | 316 - .../phplot/doc/user_internal_functions.html | 62 - .../phplot/examples/benjamingothic.ttf | Bin 19093 -> 0 bytes .../phplot/examples/create_chart.php | 167 - .../phplot/examples/data.php | 27 - .../phplot/examples/data_date.php | 267 - .../phplot/examples/data_date2.php | 267 - .../phplot/examples/data_sample1.php | 58 - .../phplot/examples/data_sample2.php | 52 - .../phplot/examples/data_sample3.php | 58 - .../phplot/examples/data_sample4.php | 24 - .../phplot/examples/data_sample5.php | 31 - .../phplot/examples/example1.php | 16 - .../phplot/examples/example2.php | 22 - .../phplot/examples/example3.php | 32 - .../phplot/examples/example4.php | 80 - .../phplot/examples/example6.php | 19 - .../phplot/examples/example7.php | 33 - .../phplot/examples/example8.php | 61 - .../phplot/examples/example9.php | 35 - .../phplot/examples/format_chart.php | 345 - .../phplot/examples/inline_image.php | 28 - .../phplot/examples/test_setup.php | 67 - .../external_packages/phplot/phplot.php | 7347 ++++++++++------- .../external_packages/phplot/phplot_data.php | 258 - .../external_packages/phplot/rgb.inc.php | 16 +- 54 files changed, 7888 insertions(+), 8102 deletions(-) create mode 100644 gui/bacula-web/external_packages/phplot/COPYING delete mode 100644 gui/bacula-web/external_packages/phplot/LICENSE.GPL delete mode 100644 gui/bacula-web/external_packages/phplot/LICENSE.PHP_3_0 create mode 100644 gui/bacula-web/external_packages/phplot/NEWS.txt delete mode 100644 gui/bacula-web/external_packages/phplot/README create mode 100644 gui/bacula-web/external_packages/phplot/README.txt create mode 100644 gui/bacula-web/external_packages/phplot/contrib/README.txt create mode 100644 gui/bacula-web/external_packages/phplot/contrib/color_range.example.php create mode 100755 gui/bacula-web/external_packages/phplot/contrib/color_range.php create mode 100644 gui/bacula-web/external_packages/phplot/contrib/color_range.test1.php create mode 100644 gui/bacula-web/external_packages/phplot/contrib/color_range.test2.php create mode 100644 gui/bacula-web/external_packages/phplot/contrib/prune_labels.example.php create mode 100644 gui/bacula-web/external_packages/phplot/contrib/prune_labels.php create mode 100644 gui/bacula-web/external_packages/phplot/contrib/prune_labels.test.php delete mode 100644 gui/bacula-web/external_packages/phplot/doc/imgs/graph1.png delete mode 100644 gui/bacula-web/external_packages/phplot/doc/imgs/graph2.png delete mode 100644 gui/bacula-web/external_packages/phplot/doc/imgs/graph3.png delete mode 100644 gui/bacula-web/external_packages/phplot/doc/imgs/phplot-dia.png delete mode 100644 gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig1.png delete mode 100644 gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig2.png delete mode 100644 gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig3.png delete mode 100644 gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig4.png delete mode 100644 gui/bacula-web/external_packages/phplot/doc/index.php delete mode 100644 gui/bacula-web/external_packages/phplot/doc/internal_functions.html delete mode 100644 gui/bacula-web/external_packages/phplot/doc/quickstart.html delete mode 100644 gui/bacula-web/external_packages/phplot/doc/schema.html delete mode 100644 gui/bacula-web/external_packages/phplot/doc/style.css delete mode 100644 gui/bacula-web/external_packages/phplot/doc/user_functions.html delete mode 100644 gui/bacula-web/external_packages/phplot/doc/user_internal_functions.html delete mode 100644 gui/bacula-web/external_packages/phplot/examples/benjamingothic.ttf delete mode 100644 gui/bacula-web/external_packages/phplot/examples/create_chart.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/data.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/data_date.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/data_date2.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/data_sample1.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/data_sample2.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/data_sample3.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/data_sample4.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/data_sample5.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/example1.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/example2.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/example3.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/example4.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/example6.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/example7.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/example8.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/example9.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/format_chart.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/inline_image.php delete mode 100644 gui/bacula-web/external_packages/phplot/examples/test_setup.php delete mode 100644 gui/bacula-web/external_packages/phplot/phplot_data.php diff --git a/gui/bacula-web/external_packages/phplot/COPYING b/gui/bacula-web/external_packages/phplot/COPYING new file mode 100644 index 0000000000..602bfc9463 --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/COPYING @@ -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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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 + + 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. + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/gui/bacula-web/external_packages/phplot/ChangeLog b/gui/bacula-web/external_packages/phplot/ChangeLog index b1c6d91afb..ace1b3bfc7 100644 --- a/gui/bacula-web/external_packages/phplot/ChangeLog +++ b/gui/bacula-web/external_packages/phplot/ChangeLog @@ -1,1610 +1,1245 @@ -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 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 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 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 index 5b6e7c66c2..0000000000 --- a/gui/bacula-web/external_packages/phplot/LICENSE.GPL +++ /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. - - 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.) - -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. - - 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. - - 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 - - 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. - - - Copyright (C) - - 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. - - , 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 index ffc1ab71f3..0000000000 --- a/gui/bacula-web/external_packages/phplot/LICENSE.PHP_3_0 +++ /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 - ". - -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 . - -This product includes the Zend Engine, freely available at -. diff --git a/gui/bacula-web/external_packages/phplot/NEWS.txt b/gui/bacula-web/external_packages/phplot/NEWS.txt new file mode 100644 index 0000000000..0efb1bc5c9 --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/NEWS.txt @@ -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 index b571f13ee3..0000000000 --- a/gui/bacula-web/external_packages/phplot/README +++ /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("") where is png, gif, jpeg, ... - - or edit the file phplot.php and change the line - - var $file_format = ""; - -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 index 0000000000..7485226510 --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/README.txt @@ -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 ---------------------------- + + +Hello, PHPlot! + + +

PHPlot Test

+ + + +--------------------------------------------------------- + +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 ----------------------------- +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 index 0000000000..3c63225200 --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/contrib/README.txt @@ -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 index 0000000000..d29978b770 --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/contrib/color_range.example.php @@ -0,0 +1,35 @@ +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 index 0000000000..6ff4d6d0ab --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/contrib/color_range.php @@ -0,0 +1,103 @@ + + "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 index 0000000000..661f591ea7 --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/contrib/color_range.test1.php @@ -0,0 +1,55 @@ +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 index 0000000000..5f272e52cd --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/contrib/prune_labels.php @@ -0,0 +1,36 @@ + 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 index 0000000000..6d3bbb2635 --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/contrib/prune_labels.test.php @@ -0,0 +1,41 @@ + $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 index 3b5a1c205aa4cfb7ff9a734e74092979f0ef67c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35562 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 Mp00i_>zopr0Q7F+k^lez 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 index ff0e80beb8f5e02f40509a98bffddce7a242cf4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16749 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
vTW1%`_{R+t}$pPrZ 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 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 index e437c8789ed60654fcdf70cb986e431afc500172..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11392 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 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 index eac14eb27d151cc9e1b242b46681e27d5c411591..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47604 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
M9n5j1t8zbN64hmb(Q41KgPG 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 lF~jTJq{p@S;hX|8HI z2tuE7cJziI6ke9EDQ=Y!&(3}#3r9$9zQp$S_QJwKsZ=U6xLj^#uh{=qD#O+^7{H(5KKA*$!DK(wD-MQ_Mu!{`qSQ>#HpN)^~aK%1I?O@ z-(HgTG%T+bcqHuRTUOnL*89?`R*Q6bZX^weg8B!#0h5Q#2vfI*i~mvi|B^=*0^iN= zz*uA`K|DnRHyS7?XM%V!EPi2b76TR6p;k>A^3i>85;-7`&hplx$WYINH^d zDW})kim-DWSV+c+I1V*1sRm(gjbl)+)c4Q_zZty&%)S!P<>+PD+CLl0BQGT+#)CkW zmqaG(e3^0WYKcuKPC&1WO`ICHRvsQ@uB{8dp> zF2OrH-y-I)cXuv0nsdB!jFT<^R+%mQ>44!~1i?hYz@V!*;GjBQFzqCas*Mr_z+s0s zSmi$yqc&XNy2K2=xj4ZjxiObcSwUNwbf{qk=0VRx$ez2e0Jnpthl?p?G?$^6K=??9 zt{0F*KRL9%p-;f<_b?q%F;Aaea?otp@3DDg$C*a;OSp*O>*rpAU72fcO9_UAlkxEf zwHl@|xAyCDrgI%-jgP`scRbAe@W22apvjk%_NW4Zyp)wD(y6fKNB`hS+cT=0-Guzw zd|@N;{nre+-Yc1|%VS^W-N#QUq(Aknh@($#STtJqNK!oX$Q*kkwI*bwCw#;w=)1Q< zKON7G(R+r$+DZ@k68Y+L7hx1D|EOF0pM~mg-kAZ1)2POwU)V*9J@h3T(dV7&q`XX| z{y{WV`wK6YH-+$;P|LYq_$E|K&PbU?#bdrtY`ftaduDz7R+n|%DEp#55?n0?+0OZc z2QF*Q?DZEuJ%n}$b~iH>GFZ0kl3WV0^$va;krsPkn2(OL{296GpM4aa2I!18;ba)c zGG;K|z5{`RhRR~9-&{Tbr-OYXF-!(x;jhN$$56eNQ-OJKIr*}7Nj|yL+oGBULhEpL zcm+EI|CV>RUpI8iWmYYFEtyicFm4?^-(P>ucub;drvQtOUDa2({fgyb(4g#Is!-46 zO||k1sg2JHP^Yu(OS*)(2t|K7jg;4J%dv&eKiu`ZrkfDU3d^_5tqnI)?x%KRx&~K( ze6*iZr(v5Dyt%+R;KZEdeg;kMyzY9{9gOZN<>i_RQnsu0+LnG9iXH+>(MfkAxM9tX z1HC-Z*`mHl`b8zyd13tRrsiGMHz55Jmhxz4^t|?ofgVYVv*J(Ib8$X90*)Z6@~{ZMe1)#vBhjlUDohzm`5;OgXD)7x-|C5;m#QT_ zD{Y}P=RMCd#Ty4&>|6>gn3RgXy+BEMwl*<-{}{Tw*-P|9kH6(dJoK+fO}xh_AMPWE z*gg)c{FsW3;7npw6~Si*Z6>kr8x6#Rl|w*TwqzSmbXJ5)1rS#0-{1(l_3IRbCfnKE z1|cL39u1)-#hB&1DA+eYkvdA%SNMAzp=h1?_&%GBooVVeZ|>U)xQRN++LsRd2&-yH z?0!F50-RIhv`8jfn(YnZOUrW5s&Gw}8wD5s!V;Ng-n0G?<$0nN)98;kg&(IX&C%tK g|8>Ftj(I(|@xYQQqreo|%Ysf3JsepMXRo3E2l@_6-~a#s 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 index 5deafe4b04b33a5cef9e73b2b49663cd4930c33b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2023 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 - - - - Welcome to PHPlot - - - - -

Welcome to PHPlot 5.0

- - - - - -
-

- PHPlot is a PHP4 class for on the fly graphs - generation. It was started by - Afan Ottenheimer in 2000 as an - opensource project, and is now co-developed with - Miguel de Benito thanks to - sourceforge. It is distributed under - the terms of the - GNU General Public License, - and the PHP license. You can always - obtain the latest source from the - sourceforge project page, please do also check CVS, we try to have it always working - there. -

-

For further information, please check - our website -

-
- - - -
Example line graph with labels, legend - and left and lower axis titles.
-
- - -

Features

- - - - - -
- - - -
Example 3d pie chart.
-
-

- Here goes a (incomplete) list, in no particular order.: -

    -
  • Several different graph types: lines, bars, stacked bars, points, areas, pie, squared.
  • -
  • text-data, data only and data-error data types accepted.
  • -
  • 3D shading for pie and bar graphs.
  • -
  • Different line types: solid and wholly customizable dashed ones.
  • -
  • Can draw error margins along y-axis when supplied in data.
  • -
  • Highly customizable canvas: titles, labels and ticks can be - placed anywhere, with any color and everything gets automagically placed without overlapping.
  • -
  • Vertical and horizontal grids.
  • -
  • Legend. Different types on the works.
  • -
  • TrueType font support.
  • -
  • Linear and logaritmic scales.
  • -
  • Several output formats: jpeg, png, gif, wbmp (those supported by your GD)
  • -
- And here a short to-do/whishlist: -
    -
  • Horizontal bars.
  • -
  • Simple isometric 3D plots.
  • -
  • Automatic placement of several plots in one image.
  • -
  • Better or automatic management of many drawing options (ticks, labels, etc.)
  • -
  • Subclassing for optimisation: move features into subclasses for optional use - and leave a fast core.
  • -
-

-
- - -

Requirements

-

-We are not sure about exact requirements, but at least PHP 4.2.0 and -GD Lib 2 are necessary. Feedback is welcome. -

- - -

Quick start

-

You can rush for a quick start here.

- - -

Tests and examples

-

-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: -

-

- - -

Internals

-

-Description of the use and inner workings of PHPlot: -

-

- -

The Authors

-

-

-

- -

$Id$

- - 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 index 90895c0d7c..0000000000 --- a/gui/bacula-web/external_packages/phplot/doc/internal_functions.html +++ /dev/null @@ -1,269 +0,0 @@ - - -PHPlot Internal Functions - - - -

PHPlot Internal Functions

- -

DrawArea() -
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.
Colors and border colors for each of the y1, y2, ... is set -by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("area") and SetDataType("data-data") - - -

DrawAreaSeries() -
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.
Colors and border colors for each of the y1, y2, ... is set -by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("area") and SetDataType("text-data") - -

DrawBackground() -
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 SetBackgroundColor and SetImageArea for setting -parameters. - -

DrawBars() -
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.
Colors and border colors for each of the y1, y2, ... is set -by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("bars") and SetDataType("text-data") - -

DrawDashedLine($x1,$y1,$x2,$y2,$dashed, $space, $color) -
Internal Function: Called instead of ImageLine when line_style = 'dashed' -line_style is set by SetLineStyles All variables passed to DrawDashedLine -are in pixel coordinates. - -

DrawDots() -
Internal Function: Draw Dots as defined by SetPointType 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.
Colors and border colors for each -y1, y2, , ... is set by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("dots") and SetDataType("data-data") - -

DrawDotSeries() -
Internal Function: Draw Dots as defined by SetPointType 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.
-Colors and border colors -for each y1, y2, , ... is set by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("dots") and SetDataType("text-data") - -

DrawDotsError() -
Internal Function: Draw Dots and Error Bars as defined by SetPointType 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 SetDataColors($which_data,$which_border) -The shape of the error bars is defined by SetErrorBarShape($which_ebs) -and SetErrorBarSize($which_ebs)
-This function is called if -you use SetPlotType("dots") and SetDataType("data-data-error") - -

DrawError($error_message) -
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. - -

DrawGraph() -
Internal Function: This is th last function called after you have set up -all the parameters of the graph see Set.... functions below. - -

DrawHorizontalTicks() -
Internal Function: Draw the ticks on the X axis. -The distance between ticks can be defined in a number of ways. Note: -that for text-data data its best to let the program handle the -distance between ticks - the default is 1. (Text-data data is data where the data is evenly spaced over -the x axis, no x-value is entered) - -

DrawImageBorder() -
Internal Function: Draw the border around the entire image. Currently this -generates the raised border look around the image. - -

DrawLabels() -
Internal Function: Draw the Title, X-axis label, and the Y-axis label. This -really just calls DrawTitle, DrawXLabel, and DrawYLabel. - -

DrawLegend(x,y,type) -
Internal Function: Draw the Legend. See SetLegendPixels() - -

DrawLines() -
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 SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("lines") and SetDataType("data-data") -
- -

DrawLineSeries() -
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.
Colors and border colors for each of the y1, y2, ... is set -by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("lines") and SetDataType("text-data") - -

DrawLinesError() -
Internal Function: Lines and Error Bars as defined by SetPointType 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 SetDataColors($which_data,$which_border) -The shape of the error bars is defined by SetErrorBarShape($which_ebs) -and SetErrorBarSize($which_ebs) -This function is called if -you use SetPlotType("lines") and SetDataType("data-data-error") - -

DrawPieChart() -
Internal Function: Draw pie chart. Data is in text-data format. $data[] must -already been defined as $data[] = array("label",y1,y2,y3,...)
-Colors and border colors for each of the y1, y2, ... is set -by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("pie") and SetDataType("text-data") - -

DrawPlotAreaBackground() -
Internal Function: Draw the Background - in the color as defined by X -and in an area as defined by X - -

DrawPlotBorder() -
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 -DrawVerticalTicks() and DrawXAxis() - -

DrawTitle() -
Internal Function: This will draw the title as set by -SetTitle and -SetTitleColor. This is also affected by SetUseTTF -to show how the title appears. - - -

DrawVerticalTicks() -
Internal Function: Called by DrawPlotBorder. The spacing between the ticks -is automatically set unless you have previously set the increment -by - SetNumVertTicks($which_nt) -or - SetVertTickIncrements but not both. - -

DrawXAxis() -
Internal Function: Draw the horizontal X axis at the world position X=0. - -

DrawXLabel() -
Internal Function: Called from DrawLabels. Draws the label of the X-Axis. - -

DrawYErrorBar($x_world,$y_world,$error_height,$error_bar_type,$color) -
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). - -

DrawYLabel() -
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. - -

FindDataLimits() -
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. - -

InitImage() -
Internal Function: An internal function called to set the -image pointer for GD. - -

PrintError($error_message) -
Internal Function: Prints the given error message to stdout. The function -is used for fatal errors that do not allow for creating an image. - -

PrintImage() -
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 SetFileFormat() - -

SetColor($color_asked) -
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).
- -

SetDefaultColors() -
Internal Function: Sets the default colors when first defining an image. -Overridden by functions like SetPlotBgColor, - SetBackgroundColor, - SetTextColor, - SetGridColor, - SetLightGridColor, - SetTickColor, and - SetTitleColor - -

SetDrawYGrid($which_dyg) -
Internal Function: 1 = Draw the Y grid. Anything else, don't draw the Y grid. - -

SetEqualXCoord() -
Internal Function: For text-data graphs set the spacing between -data points on the x-axis. - -

SetMargins() -
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(). - -

SetTranslation() -
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. - -

SetXLabelHeight() -
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. - - -

SetYLabelWidth() -
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. - - -

TTFBBoxSize($size, $angle, $font, $string) -
Internal Function: Calculate the size of the box which encloses the -text string $string using font $font, -angle $angle and size $size. This is -an internal function which is not called unless use_ttf is -set to 1. - -

xtr($x_world) -
Internal Function: Translate into x-pixels coordinates from x world coordinates. - -

ytr($y_world) -
Internal Function: Translate into y-pixels coordinates from y world coordinates. - - - - - -
- - - 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 index 6f738c6c9c..0000000000 --- a/gui/bacula-web/external_packages/phplot/doc/quickstart.html +++ /dev/null @@ -1,455 +0,0 @@ - - - - - PHPLOT Quick Start and Examples - - - - - - - - - - - - - - -

PHPlot Quick Start and Examples

- -

Afan Ottenheimer, January 7, 2001

-

Miguel de Benito, January 21, 2004

-

Contents

- - -

Introduction

- -

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. -

- -

First, lets discuss how PHPlot works in general with some -terminology. A PHPlot image can consist of several graphs , -each graph consisting of several elements. -You define an object (e.g. a variable like $graph), -select the properties of the element that compose the graph and "Draw" -what you want into the object. Tipically by selecting the plot type with -SetPlotType and at the end calling DrawGraph. You -can also directly invoke PrintImage, which either inserts the image -into the data streaming to the client or writes it to disk. -

-

In PHPlot there are World coordinates, which are the XY coordinates -relative to the axis origin, in the units of the data set; and -device (pixel) coordinates which in GD are relative to the -origin at the upper left side of the image. -

-

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. -

-

Since PHP is a server scripted language you have several options -for how you can "Print" the image. You can:

-
    -
  1. Write the image as a file on the server. (You specify a file - name and can specify caching as well)
  2. -
  3. Have the raw data stream out within an HTML file, as <IMG - SRC="my_PHPlot_code.php">
  4. -
  5. 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 .
  6. -
-

This document explains how to create plots using PHPlot from a -PHP script. Information on PHP can be found at www.php.net. -Information about the GD library which PHP uses to create images can -be found at www.boutell.com. -More information about PHPlot can be found at www.PHPlot.com.

- -

Creating the Object

- -

You create a PHPlot object by first including the code to be used -and then defining the variable:

-
-
<?php
-include('./phplot.php');  // here we include the PHPlot code 
-$graph =& new PHPlot();   // here we define the variable graph
-
-//Rest of code goes below
-?>
-
- -

The above code assigns the PHPlot object to the variable $graph. Please, -use that '&' to create a reference, it is needed by phplot's approximation of a destructor, -a facility unavailable in PHP4.

- -

Back to top

- - -

Real World Examples

-

Case 1: A simple graph

- -

We will start with a simple line graph.

-
-
<?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->SetDataValues($example_data);
-
-//Draw it
-$graph->DrawGraph(); // remember, since in this example we have one graph, PHPlot
-                        // does the PrintImage part for you
-?>
-
- -

And that's it! What we get is the following graph:

- -
- figure 1 -
Figure 1 -
- -

That's a great start, but now we'd like to specify the width and height -of the image.

- - -

Case 1a: Different Size Images and Titles

-

-Lets say we want it to have a width of 300 and a height of 250 pixels. -So instead of having the line

- $graph =& new PHPlot();

-we replace it with

- $graph =& new PHPlot(300,250);

-and you have specified the size in pixels of the image to be created. -A couple of things to note: -

-
    -
  • The default is not to use TTF fonts.
  • -
  • Since there was only one graph on the image we didn't have to - specify PrintImage, DrawGraph took care of it for us. -
  • -
  • - We did not specify the data type. If you do not specify the data - type PHPlot assumes text-data. -
  • -
  • - We did not specify the file type (gif, png, jpg, ...) . - PHPlot 5.0 assumes PNG image formats. -
  • -
  • - The data is passed in as an array of arrays. This may seem awkward - now, but as we add functionality this will be beneficial. -
  • -
-

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 $graph before printing the image. We achieve this with: -

-
-
<?php
-include ('./phplot.php');
-
-//create an graph object 300x250 pixels
-$graph =& new PHPlot(300,250);
-//Set titles
-$graph->SetTitle("Title\n\rSubtitle");
-$graph->SetXTitle('X data');
-$graph->SetYTitle('Y data');
-
-//...rest of the code
-
-
-?>
-
-
-
- Figure 2 -
- -

Note that in order for the "\n" and "\r " to be interpreted as -new line/new return characters for SetTitle you have to -enclose the string in double quotes.

- -

Case 2: Multiple Lines per Graph

-

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', y1, y2, y3, -...) This is very convenient when working with rows of data from databases. -
-Now our data will have three Y values for each position on the X axis -

-
-
<?php
-//Include the code
-include('./phplot.php');
-
-//Define the object
-$graph =& new PHPlot(300,250);
-
-//Set titles
-$graph->SetTitle("Title\n\rSubtitle");
-$graph->SetXTitle('X data');
-$graph->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->SetDataValues($example_data);
-
-//Draw it
-$graph->DrawGraph();
-?>
-
-

Which gives us:

-
- -
Figure 3 -
- -

Notice that each set of Y data gets a different color. -Also the missing data point is skipped, this behaviour can be adjusted with -SetDrawBrokenLines(TRUE); -

-

-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. -

-

Back to top

- - -

Customization

-

Valid types of plots (as of PHPlot 5.0): -

    -
  • bars (with optional shadows)
  • -
  • lines
  • -
  • linepoints (a faster way of plotting when - you want both points and lines)
  • -
  • area
  • -
  • points (lots of point types here)
  • -
  • pie (2D or 3D)
  • -
  • thinbarline (sometimes also called impulse)
  • -
  • error bar (which can also be used for stock market data graphs)
  • -
  • squared (for binary data)
  • -
-

-

You specify which type with the SetPlotType function. -We'll look at that function with bars and lines in the next example when we look at -multiple graphs per image. -

-

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): -

    -
  • SetXTickPos()
  • -
  • SetYTickPos()
  • -
  • SetXTickLength()
  • -
  • SetYTickLength()
  • -
  • SetXTickCrossing()
  • -
  • SetYTickCrossing()
  • -
  • SetXTickIncrement()
  • -
  • SetYTickIncrement()
  • -
  • SetNumXTicks()
  • -
  • SetNumYticks()
  • -
  • SetSkipTopTick()
  • -
  • SetSkipBottomTick()
  • - -
  • SetDrawXGrid()
  • -
  • SetDrawYGrid()
  • -
  • SetDrawDashedGrid()
  • -
  • SetDrawXDataLabelLines()
  • -
  • SetDrawYDataLabelLines() (not yet implemented)
  • -
  • SetXDataLabelPos()
  • -
  • SetYDataLabelPos()
  • -
  • SetXLabelAngle()
  • -
  • SetYLabelAngle()
  • -
  • SetXLabelType()
  • -
  • SetYLabelType()
  • -
-As we go further we will introduce some of these features of PHPlot. -For more specialized examples, please go back to the index and -look in the examples section. -

- -

Back to top

- -

Case 3: Multiple Graphs per Image

- -

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 -not print the image at the same time as the draw command. Now -we want it to wait for the explicit PrintImage function call. -To tell PHPlot this is the way we want to work, we use the -SetPrintImage function. -SetPrintImage(TRUE) is the default, and tells to draw the image -when DrawGraph is called. To turn this -off we use SetPrintImage(FALSE).

-

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. -

-

To specify in pixels the placement of each graph we use -SetNewPlotAreaPixels. The format is -SetNewPlotAreaPixels(upper_left_x, upper_left_y, lower_right_x, -lower_right_y) . Again we are using the GD coordinates where 0,0 -is the upper left corner of the image. -

-

In more detail:

-
-
<?php
-include('./PHPlot.php');  // here we include the PHPlot code
-$graph =& new PHPlot(400,250);   // here we define the variable $graph
-$graph->SetPrintImage(0); //Don't draw the image yet
-
-//....Data and Values for first graph here .....
-
-$graph->SetNewPlotAreaPixels(70,10,375,100);  // where to place it
-
-$graph->DrawGraph();   //Draw the first graph to the image.
-
-//....Data and Values for second graph here .....
-
-$graph->SetNewPlotAreaPixels(70,120,375,220); //where to place graph 2
-$graph->DrawGraph();  //Draw the second graph to the image
-
-//Print the image with both graphs
-$graph->PrintImage();
-?>
-
- -

Lets now create an image with 2 graphs on it with some example data.

- -
-
<?php
-//Include the code
-include('./phplot.php');
-
-//Define the object
-$graph =& new PHPlot(400,250);
-
-$graph->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->SetDataType("text-data");  //Must be called before SetDataValues
-
-$graph->SetDataValues($example_data);
-$graph->SetYTickIncrement(2);  //a smaller graph now - so we set a new tick increment
-
-$graph->SetXLabelAngle(90);
-$graph->SetXTitle("");
-$graph->SetYTitle("Price");
-$graph->SetPlotType("lines");
-$graph->SetLineWidth(1);
-
-$graph->SetNewPlotAreaPixels(70,10,375,100);  // where do we want the graph to go
-$graph->DrawGraph(); // remember, since we said not to draw yet, PHPlot
-                     // still needs a PrintImage 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->SetDataType("text-data");  //Must be called before SetDataValues
-
-$graph->SetDataValues($example_data);
-
-$graph->SetXTitle("");
-$graph->SetYTitle("Verbal Cues");
-$graph->SetYTickIncrement(10);
-$graph->SetPlotType("bars");
-$graph->SetXLabelAngle(0);  //have to re-set as defined above
-
-$graph->SetNewPlotAreaPixels(70,120,375,220);
-$graph->SetPlotAreaWorld(0,0,7,80);
-$graph->DrawGraph();
-
-//Print the image
-$graph->PrintImage();
-?>
-
- -



Which gives us:

- -
-
- Figure 4
-
- -

-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. -

- - -
$Id$
- - - 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 index 458ede2772..0000000000 --- a/gui/bacula-web/external_packages/phplot/doc/schema.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - PHPlot schema - - - - - - -

The Drawing

-

-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 (Miguel) -hope it will help somebody as it did with me when I first began browsing -through the 3000 lines of code Afan had written. -

-

If you are curious, for the drawing I used -Dia a gtk+ diagram creation -sofware which I find easy and suited for my purposes. -

schema
-

- -

The words

-

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 -quickstart. -

- -

$Id$

- - 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 index ff700e0f5e..0000000000 --- a/gui/bacula-web/external_packages/phplot/doc/style.css +++ /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 index 9b529019f3..0000000000 --- a/gui/bacula-web/external_packages/phplot/doc/user_functions.html +++ /dev/null @@ -1,316 +0,0 @@ - - -PHPlot User Functions - - - -

PHPlot User Functions

- - - - -
- -

Important functions

- -

SetDataType($which_dt) -
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. -

-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.

- -Colors and border colors are set by SetDataColors. 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.

- -So in PHPLOT the datalabel,x-value,y-value and error are -grouped together as a value array. Then the -entire set of points to be plotted is passed in as a data array -E.g. Data_array = array(value_array_1,value_array_2,....)

-See below for examples of the various data types:
- -

  • 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
    -
    -$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)
    -);
    -
    -Which will display data points at -(1,1.1), (1,2), (1,3), (1,4), (2,2), (2,3)....
    - -
  • -
  • 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 -
    -$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)
    -);
    -
    -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)... -
  • -
  • 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) -
  • - -

    SetDataValues($which_dv) -
    User Function: Passes the raw data values into the class variable -$this->data_values. This function needs to be called before any -image can be drawn. - -

    SetPlotType($which_pt) -
    User Function: Can be: bars, lines, linepoints, area, points, and pie - -

    SetErrorBarLineWidth($width) -
    Width of the Error Bars in Pixels. If not set then -uses "line_width" to set the width of the error_bar lines. - -

    SetFileFormat($which_file_format) -
    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. - -

    SetUseTTF($which_ttf) -
    User Function: Call this as SetUseTTF(1) when you have TTF compiled into PHP -otherwise call this as SetUseTTF(0) - - -
    - -

    Appearance functions

    - - -

    SetErrorBarShape($which_ebs) -
    User Function: Can be "tee" or "line." If it is tee, then -the half-width of the tee is set by SetErrorBarSize - -

    SetErrorBarSize($which_ebs) -
    User Function: Size in pixels of the Tee shape of the error bar. - -

    SetHorizTickIncrement($which_ti) -
    User Function: Set where to place the X-tick marks. - -

    SetNumHorizTicks($which_nt) -
    User Function: Use this or SetHorizTickIncrements but not both. - -

    SetNumVertTicks($which_nt) -
    User Function: Use this or SetVertTickIncrements but not both. - -

    SetPlotAreaPixels($x1,$y1,$x2,$y2) -
    User Function: You can use this to set the actual size in -pixels of the plot area on the image. - -

    SetPointShape($which_pt) -
    User Function: Can be: rect,circle,diamond,triangle,dot,line,halfline - -

    SetPointSize($which_ps) -
    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. - -

    SetPrecisionX($which_prec) -
    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. - -

    SetPrecisionY($which_prec) -
    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. - -

    SetShading($which_s) -
    User Function: Set the length of the shadows for shading bar charts -The color used is the LightGridColor See SetLightGridColor.
    - -

    SetTickLength($which_tl) -
    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).
    - - -

    SetTitle($title) -
    User Function: Set the title of the graph. Printed at the top -middle of the graph. - -

    SetHorizTickIncrement($which_ti) -
    User Function: Set the distance between tick marks on the X axis. $which_ti -is in world coordinates. - -

    SetVertTickIncrement($which_ti) -
    User Function: Set the distance between tick marks on the Y axis. $which_ti -is in world coordinates. - -

    SetXDatalabelMaxlength($which_xdlm) -
    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. - -

    - -

    SetXGridLabelType($which_xtf) -
    User Function: Can be "time", "title", "none", "default" or "data".
    -time - label is set by the the php command strftime()
    -title - label is treated as text, the first element of the data array.
    -data - label is formateed using php command number_format
    -none - no labelss are printed.
    -default - prints as it is entered.
    -

    - - -

    SetXScaleType($which_xct) -
    Can be "log" or "linear". - -

    SetXTimeFormat($which_xtf) -
    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. -

    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. -

    -The php strtotime() function also comes in handy turning dates into timestamps, especially for parameters to SetPlotAreaWorld(). -

    -Example:
    -$graph->SetPlotAreaWorld(strtotime("October 1"), 0,strtotime("December 15"),10);
    -$graph->SetXGridLabelType("time") ;
    -$graph->SetXTimeFormat("%b %d") ;
    - -

    SetXLabel($xlbl) -
    User Function: Set the label for the X axis. - -

    SetYLabel($ylbl) -
    User Function: Set the label for the Y axis. - -

    SetYScaleType($which_xct) -
    Can be "log" or "linear". - - -
    - -

    Color functions

    - -

    SetBackgroundColor($which_color) -
    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).
    It defaults to array(222,222,222) if -not defined.
    - -

    SetGridColor ($which_color) -
    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).
    - -

    SetLegend($which_legend) -
    $which_legend is an array of text for display in a small box -on the image. If you do not set the position with SetLegendPixels() -then it puts it in the upper right hand side. - -

    SetLegendPixels($which_x,$which_y,$which_type) -
    Pick the upper left corner of the legend box with -$which_x and $which_y in pixels. $which_type is reserved -for future use. - -

    SetLegendWorld($which_x,$which_y,$which_type) -
    Untested and documentation not written. Have Fun! - -

    SetLightGridColor ($which_color) -
    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).
    It defaults to array(222,222,222) if -not defined.
    - -

    SetLineWidth($which_lt) -
    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. - -

    SetLineStyles($which_sls) -
    User Function: Set style of the line to be printed. -This is an array. Currently only 'dashed' and 'solid' are -supported. - -

    SetPlotBgColor($which_color) -
    User Function: Set the Background color of the area on which -the plot is defined. Called from PlotAreaBackground
    -$which_color can be either a name like "black" or an rgb color array -array(int,int,int).
    It defaults to array(222,222,222) if -not defined. - -

    SetTextColor ($which_color) -
    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).
    - -

    SetTickColor ($which_color) -
    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).
    - -

    SetTitleColor($which_color) -
    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).
    - -
    - -

    Data manipulation functions

    - -

    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.

    - -

    DoScaleData($even, $show_in_legend) -
    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.
    - -

    DoMovingAverage($datarow, $interval, $show_in_legend) -
    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. -
    - -
    - -

    not implemented

    - -

    SetCharacterHeight() -
    User Function: Not yet implemented - - - 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 index bb2346322d..0000000000 --- a/gui/bacula-web/external_packages/phplot/doc/user_internal_functions.html +++ /dev/null @@ -1,62 +0,0 @@ - - -PHPlot User / Internal Functions - - - -

    PHPlot User / Internal Functions

    - - -

    DrawDataLabel($lab,$x_world,$y_world) -
    User Function and Internal Function: Draw a label at the world coordinates of x_world and y_world. - - -

    DrawDot($x_world,$y_world,$dot_type,$color) -
    User Function and Internal Function: Draw one dot at $x_world and $y_world where the world -coordinates (not pixel coordinates). - - -

    DrawXDataLabel($xlab,$xpos) -
    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. - - -

    SetDataColors($which_data,$which_border) -
    If called with $which_data="" it defaults to - $which_data = array("blue","bisque",array(0,176,0)); - $which_border = array("black"); - - -

    SetErrorBarColors($which_data) -
    If not called then the colors are the same as the -colors of the data as set by SetDataColors() -otherwise it sets the colors of the error bars. - -

    SetLabels($xlbl,$ylbl,$title) -
    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 SetXLabel. - - -

    SetNewPlotAreaPixels($x1,$y1,$x2,$y2) -
    Reserved - not used yet. - - - -

    SetPlotAreaWorld($xmin,$ymin,$xmax,$ymax) -
    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.
    You can call this function -as SetPlotAreaWorld("","","","") and it will set the -plot area based on max/min values of data. - - - - - - - - - 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 index 61058a6e21337af011930e5a27d7c9c29b38b78b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19093 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 file is intended to be called from format_chart.php, the example-o-matic.". - "Please click here to try it.

    "; - 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 index b1d1314db9..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data.php +++ /dev/null @@ -1,27 +0,0 @@ - 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 index 4a14d7b5cf..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data_date.php +++ /dev/null @@ -1,267 +0,0 @@ - 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 index 37152c7c6f..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data_date2.php +++ /dev/null @@ -1,267 +0,0 @@ - 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 index 11b8117c1e..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data_sample1.php +++ /dev/null @@ -1,58 +0,0 @@ -

    -Data type: (Text-data)
    -

    - - - - - - - - - - - - - -
    Title (x axis)Y data 1Y data 2Y data 3 Y data 4
    - - - - - - - - - -
    - -

    -Graph type: - -

    -
    -Please note when writing your application that the graph
    -types marked with an asterisk only support the data
    -type for this form, "text-data". -
    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 index e754a039d3..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data_sample2.php +++ /dev/null @@ -1,52 +0,0 @@ - -

    -Data type: (data-data)
    - - - - - - - - - - - - - - -
    Title (data label)X dataY data 1Y data 2Y data 3
    - - - - - - - - - -
    - -

    -Graph type: - -

    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 index 46f774b56d..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data_sample3.php +++ /dev/null @@ -1,58 +0,0 @@ -

    -Data set as X, Y, E+, E-, Y2, E2+, E2-,...
    - -Data type: (data-data-error) -

    - - - - - - - - -
    Title (data label)X dataY data 1Error +Error -Y data 2Error +Error -
    - - "; - - for ($i = 0; $i < $num_rows; $i++) { - // The label input element must be bigger. - $lines[0] = "\n"; - - // Show s for the rest of the columns - for ($j=1; $j<8; $j++) - $lines[$j] = "\n"; - $groups[$i] = join('', $lines); - } - echo join("
    \n", $groups); - ?> - -
    - -

    -Graph type: - -     -Error bar type: - -

    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 index 5c4d14c16f..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data_sample4.php +++ /dev/null @@ -1,24 +0,0 @@ -

    -Data type: data-data calculated from a function. -

    -
    -	$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)); 	
    -	}
    -
    -

    -Chart type: - - -

    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 index b8859b6345..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data_sample5.php +++ /dev/null @@ -1,31 +0,0 @@ -

    -Data type: data-data-error calculated from a function. -

    -
    -    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);
    -    }
    -
    -

    -Chart type: - -     -Error bar type: - - -

    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 index a134097785..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example1.php +++ /dev/null @@ -1,16 +0,0 @@ -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 index 8f06a20dba..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example2.php +++ /dev/null @@ -1,22 +0,0 @@ -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 index a518e1315e..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example3.php +++ /dev/null @@ -1,32 +0,0 @@ -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 index 360d2df8aa..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example4.php +++ /dev/null @@ -1,80 +0,0 @@ -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 index 405943b297..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example6.php +++ /dev/null @@ -1,19 +0,0 @@ -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 index 4b93ee8df2..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example7.php +++ /dev/null @@ -1,33 +0,0 @@ -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 index d60c053957..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example8.php +++ /dev/null @@ -1,61 +0,0 @@ -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 index 32865c1526..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example9.php +++ /dev/null @@ -1,35 +0,0 @@ -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 index 5e2f4b899c..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/format_chart.php +++ /dev/null @@ -1,345 +0,0 @@ - - - - Example-o-matic - - - - - - -

    PHPlot test graph form

    - -

    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. -

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Data Settings
    -

    - Data type: [ - text-data | - data-data | - function | - data-data-error | - randfunction ] -

    - -
    -
    -

    Optional values

    -
    -
    Sizes
    Width of graph in pixels:
    Height of graph in pixels:
    Maximum height of graph in y axis units:
    Minimum height of graph in y axis units:
    Titles and data labels
    Title:
    Y axis title:
    Y axis title position: - -
    Y axis data labels position: - -
    Y axis labels angle:
    X axis title:
    X axis title position: - -
    X axis data labels position: - -
    X axis labels angle:
    Grid and ticks
    Grid drawn: - -
    Dashed grid? - -
    X axis ticks length:
    X axis ticks crossing:
    X axis ticks position: - -
    X axis tick labels position: - -
    Y axis ticks length:
    Y axis ticks crossing:
    Y axis ticks position: - -
    Y axis tick labels position: - -
    X tick increment:
    Y tick increment:
    Other
    X axis position:
    Y axis position:
    Plot Border: - -
    Shade height (0 for none):
    Plot line width:
    Error bar line width:
    Point Type: - -
    Point Size:
    Draw broken lines with missing Y data: - -
    Use TrueType font: - -
    File format: - -
    -
    -
    - -

    -Please visit PHPlot's site, the -sourceforge project page, -or see more php code and examples -by Afan Ottenheimer of jeonet. -

    - -

    $Id$

    - - 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 index 16a5a352c5..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/inline_image.php +++ /dev/null @@ -1,28 +0,0 @@ - - ************************************************* - * This file is meant to be called only from the * - * test page * - * It will fail if called by itself. * - ************************************************* - -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 index b8860ca978..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/test_setup.php +++ /dev/null @@ -1,67 +0,0 @@ - - - - - PHPlot graphic formats test - - - - - - -

    PHPlot test graph form

    - -

    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 -

    PHPlot graphic formats test

    - -

    -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. -

    - -
    - - - - - - - - - -
    PNG graphics
    -"; - -?> -
    JPEG graphics
    -"; -?> -
    GIF graphics
    -"; -?> -
    BMP graphics
    -"; -?> -
    -
    - - - diff --git a/gui/bacula-web/external_packages/phplot/phplot.php b/gui/bacula-web/external_packages/phplot/phplot.php index 5ded19e5c3..c7f323c38d 100644 --- a/gui/bacula-web/external_packages/phplot/phplot.php +++ b/gui/bacula-web/external_packages/phplot/phplot.php @@ -1,462 +1,471 @@ + * 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 + * + * Maintainer (2006-present) + * + * + * 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 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 "

    Fatal error: $error_message

    "; - 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 wy1ytr($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 wx1xtr($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 index 37351504c1..0000000000 --- a/gui/bacula-web/external_packages/phplot/phplot_data.php +++ /dev/null @@ -1,258 +0,0 @@ -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 -?> diff --git a/gui/bacula-web/external_packages/phplot/rgb.inc.php b/gui/bacula-web/external_packages/phplot/rgb.inc.php index 3353e28e50..41256ee25d 100644 --- a/gui/bacula-web/external_packages/phplot/rgb.inc.php +++ b/gui/bacula-web/external_packages/phplot/rgb.inc.php @@ -1,7 +1,16 @@ 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) ); -?> -- 2.39.5